chromecast: only create the sub-sout when we know the ES in use
authorSteve Lhomme <robux4@videolabs.io>
Thu, 28 Apr 2016 12:20:56 +0000 (14:20 +0200)
committerThomas Guillem <thomas@gllm.fr>
Tue, 3 May 2016 11:22:20 +0000 (13:22 +0200)
we then decide whether we want to remux or transcode
use the Matroska container as it allows better streaming and more codec than
the Chromecast supports (VP8 / Vorbis)

Signed-off-by: Thomas Guillem <thomas@gllm.fr>
modules/stream_out/chromecast/cast.cpp

index 4a244f8..9a3c610 100644 (file)
 
 struct sout_stream_sys_t
 {
-    sout_stream_sys_t(intf_sys_t *intf, sout_stream_t *sout, bool has_video)
-        : p_out(sout)
+    sout_stream_sys_t(intf_sys_t * const intf, bool has_video, int port,
+                      const char *psz_default_muxer, const char *psz_default_mime)
+        : p_out(NULL)
+        , default_muxer(psz_default_muxer)
+        , default_mime(psz_default_mime)
         , p_intf(intf)
         , b_has_video(has_video)
+        , i_port(port)
+        , es_changed( true )
     {
         assert(p_intf != NULL);
     }
@@ -51,17 +56,34 @@ struct sout_stream_sys_t
         sout_StreamChainDelete(p_out, p_out);
     }
 
-    sout_stream_t * const p_out;
+    bool canDecodeVideo( const es_format_t *p_es ) const;
+    bool canDecodeAudio( const es_format_t *p_es ) const;
+
+    sout_stream_t     *p_out;
+    std::string        sout;
+    const std::string  default_muxer;
+    const std::string  default_mime;
+
     intf_sys_t * const p_intf;
     const bool b_has_video;
+    const int i_port;
 
     sout_stream_id_sys_t *GetSubId( sout_stream_t*, sout_stream_id_sys_t* );
 
+    bool                               es_changed;
     std::vector<sout_stream_id_sys_t*> streams;
+
+private:
+    int UpdateOutput( sout_stream_t * );
 };
 
 #define SOUT_CFG_PREFIX "sout-chromecast-"
 
+static const vlc_fourcc_t DEFAULT_TRANSCODE_AUDIO = VLC_CODEC_MP3;
+static const vlc_fourcc_t DEFAULT_TRANSCODE_VIDEO = VLC_CODEC_H264;
+static const char DEFAULT_MUXER[] = "avformat{mux=matroska}";
+
+
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
@@ -98,8 +120,8 @@ vlc_module_begin ()
 
     add_integer(SOUT_CFG_PREFIX "http-port", HTTP_PORT, HTTP_PORT_TEXT, HTTP_PORT_LONGTEXT, false)
     add_bool(SOUT_CFG_PREFIX "video", true, HAS_VIDEO_TEXT, HAS_VIDEO_LONGTEXT, false)
-    add_string(SOUT_CFG_PREFIX "mux", "mp4stream", MUX_TEXT, MUX_LONGTEXT, false)
-    add_string(SOUT_CFG_PREFIX "mime", "video/mp4", MIME_TEXT, MIME_LONGTEXT, false)
+    add_string(SOUT_CFG_PREFIX "mux", DEFAULT_MUXER, MUX_TEXT, MUX_LONGTEXT, false)
+    add_string(SOUT_CFG_PREFIX "mime", "video/x-matroska", MIME_TEXT, MIME_LONGTEXT, false)
 
 vlc_module_end ()
 
@@ -130,6 +152,7 @@ static sout_stream_id_sys_t *Add(sout_stream_t *p_stream, const es_format_t *p_f
         p_sys_id->p_sub_id = NULL;
 
         p_sys->streams.push_back( p_sys_id );
+        p_sys->es_changed = true;
     }
     return p_sys_id;
 }
@@ -149,9 +172,153 @@ static void Del(sout_stream_t *p_stream, sout_stream_id_sys_t *id)
             es_format_Clean( &p_sys->streams[i]->fmt );
             free( p_sys->streams[i] );
             p_sys->streams.erase( p_sys->streams.begin() +  i );
+            p_sys->es_changed = true;
             break;
         }
     }
+
+    if ( p_sys->streams.empty() )
+    {
+        sout_StreamChainDelete( p_sys->p_out, p_sys->p_out );
+        p_sys->p_out = NULL;
+        p_sys->sout = "";
+    }
+}
+
+
+bool sout_stream_sys_t::canDecodeVideo( const es_format_t *p_es ) const
+{
+    if (p_es->i_codec == VLC_CODEC_H264 || p_es->i_codec == VLC_CODEC_VP8)
+        return true;
+    return false;
+}
+
+bool sout_stream_sys_t::canDecodeAudio( const es_format_t *p_es ) const
+{
+    if (p_es->i_codec == VLC_CODEC_VORBIS ||
+        p_es->i_codec == VLC_CODEC_MP4A ||
+        p_es->i_codec == VLC_FOURCC('h', 'a', 'a', 'c') ||
+        p_es->i_codec == VLC_FOURCC('l', 'a', 'a', 'c') ||
+        p_es->i_codec == VLC_FOURCC('s', 'a', 'a', 'c') ||
+        p_es->i_codec == VLC_CODEC_MPGA ||
+        p_es->i_codec == VLC_CODEC_MP3 ||
+        p_es->i_codec == VLC_CODEC_A52 ||
+        p_es->i_codec == VLC_CODEC_EAC3)
+        return true;
+    return false;
+}
+
+int sout_stream_sys_t::UpdateOutput( sout_stream_t *p_stream )
+{
+    assert( p_stream->p_sys == this );
+
+    if ( es_changed )
+    {
+        es_changed = false;
+
+        bool canRemux = true;
+        vlc_fourcc_t i_codec_video = 0, i_codec_audio = 0;
+
+        for (std::vector<sout_stream_id_sys_t*>::iterator it = streams.begin(); it != streams.end(); ++it)
+        {
+            const es_format_t *p_es = &(*it)->fmt;
+            if (p_es->i_cat == AUDIO_ES)
+            {
+                if (!canDecodeAudio( p_es ))
+                {
+                    msg_Dbg( p_stream, "can't remux audio track %d codec %4.4s", p_es->i_id, (const char*)&p_es->i_codec );
+                    canRemux = false;
+                }
+                else if (i_codec_audio == 0)
+                    i_codec_audio = p_es->i_codec;
+            }
+            else if (b_has_video && p_es->i_cat == VIDEO_ES)
+            {
+                if (!canDecodeVideo( p_es ))
+                {
+                    msg_Dbg( p_stream, "can't remux video track %d codec %4.4s", p_es->i_id, (const char*)&p_es->i_codec );
+                    canRemux = false;
+                }
+                else if (i_codec_video == 0)
+                    i_codec_video = p_es->i_codec;
+            }
+        }
+
+        std::stringstream ssout;
+        if ( !canRemux )
+        {
+            if ( i_codec_audio == 0 )
+                i_codec_audio = DEFAULT_TRANSCODE_AUDIO;
+            /* avcodec AAC encoder is experimental */
+            if ( i_codec_audio == VLC_CODEC_MP4A ||
+                 i_codec_audio == VLC_FOURCC('h', 'a', 'a', 'c') ||
+                 i_codec_audio == VLC_FOURCC('l', 'a', 'a', 'c') ||
+                 i_codec_audio == VLC_FOURCC('s', 'a', 'a', 'c'))
+                i_codec_audio = DEFAULT_TRANSCODE_AUDIO;
+
+            if ( i_codec_video == 0 )
+                i_codec_video = DEFAULT_TRANSCODE_VIDEO;
+
+            /* TODO: provide audio samplerate and channels */
+            ssout << "transcode{acodec=";
+            char s_fourcc[5];
+            vlc_fourcc_to_char( i_codec_audio, s_fourcc );
+            s_fourcc[4] = '\0';
+            ssout << s_fourcc;
+            if ( b_has_video )
+            {
+                /* TODO: provide maxwidth,maxheight */
+                ssout << ",vcodec=";
+                vlc_fourcc_to_char( i_codec_video, s_fourcc );
+                s_fourcc[4] = '\0';
+                ssout << s_fourcc;
+            }
+            ssout << "}:";
+        }
+        std::string mime;
+        if ( !b_has_video && default_muxer == DEFAULT_MUXER )
+            mime = "audio/x-matroska";
+        else if ( i_codec_audio == VLC_CODEC_VORBIS &&
+                  i_codec_video == VLC_CODEC_VP8 &&
+                  default_muxer == DEFAULT_MUXER )
+            mime = "video/webm";
+        else
+            mime = default_mime;
+
+        ssout << "http{dst=:" << i_port << "/stream"
+              << ",mux=" << default_muxer
+              << ",access=http{mime=" << mime << "}}";
+
+        if ( sout != ssout.str() )
+        {
+            if ( unlikely( p_out != NULL ) )
+            {
+                sout_StreamChainDelete( p_out, p_out );
+                sout = "";
+            }
+
+            p_out = sout_StreamChainNew( p_stream->p_sout, ssout.str().c_str(), NULL, NULL);
+            if (p_out == NULL) {
+                msg_Dbg(p_stream, "could not create sout chain:%s", ssout.str().c_str());
+                return VLC_EGENERIC;
+            }
+            sout = ssout.str();
+        }
+
+        /* check the streams we can actually add */
+        for (std::vector<sout_stream_id_sys_t*>::iterator it = streams.begin(); it != streams.end(); ++it)
+        {
+            sout_stream_id_sys_t *p_sys_id = *it;
+            p_sys_id->p_sub_id = sout_StreamIdAdd( p_out, &p_sys_id->fmt );
+            if ( p_sys_id->p_sub_id == NULL )
+            {
+                msg_Err( p_stream, "can't handle a stream" );
+                streams.erase( it, it );
+            }
+        }
+    }
+
+    return VLC_SUCCESS;
 }
 
 sout_stream_id_sys_t *sout_stream_sys_t::GetSubId( sout_stream_t *p_stream,
@@ -161,6 +328,9 @@ sout_stream_id_sys_t *sout_stream_sys_t::GetSubId( sout_stream_t *p_stream,
 
     assert( p_stream->p_sys == this );
 
+    if ( UpdateOutput( p_stream ) != VLC_SUCCESS )
+        return NULL;
+
     for (i = 0; i < streams.size(); ++i)
     {
         if ( id == (sout_stream_id_sys_t*) streams[i] )
@@ -216,9 +386,11 @@ static int Open(vlc_object_t *p_this)
     char *psz_var_mime = NULL;
     sout_stream_t *p_sout = NULL;
     bool b_has_video = true;
+    int i_local_server_port;
     std::stringstream ss;
 
     config_ChainParse(p_stream, SOUT_CFG_PREFIX, ppsz_sout_options, p_stream->p_cfg);
+    i_local_server_port = var_InheritInteger(p_stream, SOUT_CFG_PREFIX "http-port");
 
     psz_mux = var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX "mux");
     if (psz_mux == NULL)
@@ -229,7 +401,8 @@ static int Open(vlc_object_t *p_this)
     if (psz_var_mime == NULL)
         goto error;
 
-    ss << "http{dst=:" << var_InheritInteger(p_stream, SOUT_CFG_PREFIX "http-port") << "/stream"
+    /* check if we can open the proper sout */
+    ss << "http{dst=:" << i_local_server_port << "/stream"
        << ",mux=" << psz_mux
        << ",access=http{mime=" << psz_var_mime << "}}";
 
@@ -238,10 +411,12 @@ static int Open(vlc_object_t *p_this)
         msg_Dbg(p_stream, "could not create sout chain:%s", ss.str().c_str());
         goto error;
     }
+    sout_StreamChainDelete( p_sout, p_sout );
 
     b_has_video = var_GetBool(p_stream, SOUT_CFG_PREFIX "video");
 
-    p_sys = new(std::nothrow) sout_stream_sys_t(p_intf, p_sout, b_has_video);
+    p_sys = new(std::nothrow) sout_stream_sys_t( p_intf, b_has_video, i_local_server_port,
+                                                 psz_mux, psz_var_mime );
     if (unlikely(p_sys == NULL))
         goto error;
 
@@ -258,7 +433,6 @@ static int Open(vlc_object_t *p_this)
     return VLC_SUCCESS;
 
 error:
-    sout_StreamChainDelete(p_sout, p_sout);
     free(psz_mux);
     free(psz_var_mime);
     delete p_sys;