From 1cb6affcfdd0595147c19e2dc2114cf6505ef707 Mon Sep 17 00:00:00 2001
From: Alexandre Janniaux <alexandre.janniaux@gmail.com>
Date: Wed, 28 Nov 2018 18:27:16 +0100
Subject: [PATCH 25/25] transcode: add support for mutliple venc parameters

Add support for multiple venc parameters for transcoding. Venc
parameters are tested in th given order and fallback if the encoder
can't be opened.

For example:
--sout#transcode{\
    venc={vcodec=h264,module=avcodec{...}},\
    venc={vcodec=VP80,module=vpx{...}}}
will first try avcodec module with h264 video format, then vpx module
with VP8 video format.
---
 modules/stream_out/transcode/transcode.c | 257 ++++++++++++++++++-----
 modules/stream_out/transcode/transcode.h |   2 +
 2 files changed, 204 insertions(+), 55 deletions(-)

diff --git a/modules/stream_out/transcode/transcode.c b/modules/stream_out/transcode/transcode.c
index c92dd4a974..9009f312bb 100644
--- a/modules/stream_out/transcode/transcode.c
+++ b/modules/stream_out/transcode/transcode.c
@@ -153,6 +153,9 @@ vlc_module_begin ()
     set_section( N_("Video"), NULL )
     add_module( SOUT_CFG_PREFIX "venc", "encoder", NULL, VENC_TEXT,
                 VENC_LONGTEXT, false )
+    /* XXX: VERY HACKY */
+    add_string( SOUT_CFG_PREFIX "module", NULL,
+                "define the venc module in venc{} parameters", "", true )
     add_string( SOUT_CFG_PREFIX "vcodec", NULL, VCODEC_TEXT,
                 VCODEC_LONGTEXT, false )
     add_integer( SOUT_CFG_PREFIX "vb", 0, VB_TEXT,
@@ -219,7 +222,7 @@ vlc_module_begin ()
 vlc_module_end ()
 
 static const char *const ppsz_sout_options[] = {
-    "venc", "vcodec", "vb",
+    "vcodec", "vb",
     "scale", "fps", "width", "height", "vfilter", "deinterlace",
     "deinterlace-module", "threads", "aenc", "acodec", "ab", "alang",
     "afilter", "samplerate", "channels", "senc", "scodec", "soverlay",
@@ -227,6 +230,10 @@ static const char *const ppsz_sout_options[] = {
     NULL
 };
 
+static const char *const ppsz_venc_options[] = {
+    "vcodec", "module", NULL
+};
+
 /*****************************************************************************
  * Exported prototypes
  *****************************************************************************/
@@ -241,6 +248,7 @@ static int Open( vlc_object_t *p_this )
 {
     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
     sout_stream_sys_t *p_sys;
+    config_chain_t    *p_cfg;
     char              *psz_string;
 
     if( !p_stream->p_next )
@@ -251,8 +259,17 @@ static int Open( vlc_object_t *p_this )
     p_sys = calloc( 1, sizeof( *p_sys ) );
     p_sys->i_master_drift = 0;
 
+    ARRAY_INIT( p_sys->pp_vencs );
+
+    for( p_cfg = p_stream->p_cfg; p_cfg; p_cfg = p_cfg->p_next )
+    {
+        if( !strcmp( p_cfg->psz_name, "venc" ) )
+            ARRAY_APPEND(p_sys->pp_vencs, p_cfg);
+    }
+
+    /* Parse the other option (except venc) */
     config_ChainParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options,
-                   p_stream->p_cfg );
+                       p_stream->p_cfg );
 
     /* Audio transcoding parameters */
     psz_string = var_GetString( p_stream, SOUT_CFG_PREFIX "aenc" );
@@ -310,29 +327,6 @@ static int Open( vlc_object_t *p_this )
     free( psz_string );
 
     /* Video transcoding parameters */
-    psz_string = var_GetString( p_stream, SOUT_CFG_PREFIX "venc" );
-    p_sys->psz_venc = NULL;
-    p_sys->p_video_cfg = NULL;
-    if( psz_string && *psz_string )
-    {
-        char *psz_next;
-        psz_next = config_ChainCreate( &p_sys->psz_venc, &p_sys->p_video_cfg,
-                                   psz_string );
-        free( psz_next );
-    }
-    free( psz_string );
-
-    psz_string = var_GetString( p_stream, SOUT_CFG_PREFIX "vcodec" );
-    p_sys->i_vcodec = 0;
-    if( psz_string && *psz_string )
-    {
-        char fcc[5] = "    \0";
-        memcpy( fcc, psz_string, __MIN( strlen( psz_string ), 4 ) );
-        p_sys->i_vcodec = vlc_fourcc_GetCodecFromString( VIDEO_ES, fcc );
-        msg_Dbg( p_stream, "Checking video codec mapping for %s got %4.4s ", fcc, (char*)&p_sys->i_vcodec);
-    }
-    free( psz_string );
-
     p_sys->i_vbitrate = var_GetInteger( p_stream, SOUT_CFG_PREFIX "vb" );
     if( p_sys->i_vbitrate < 16000 ) p_sys->i_vbitrate *= 1000;
 
@@ -435,6 +429,11 @@ static void Close( vlc_object_t * p_this )
     sout_stream_t       *p_stream = (sout_stream_t*)p_this;
     sout_stream_sys_t   *p_sys = p_stream->p_sys;
 
+    if( p_sys->p_venc_conf )
+        vlc_object_release( p_sys->p_venc_conf );
+
+    ARRAY_RESET(p_sys->pp_vencs);
+
     free( p_sys->psz_af );
 
     config_ChainDestroy( p_sys->p_audio_cfg );
@@ -481,6 +480,30 @@ static void DeleteSoutStreamID( sout_stream_id_sys_t *id )
     }
 }
 
+static encoder_t *InitEncoder( vlc_object_t *p_parent,
+                               sout_stream_t *p_stream,
+                               const es_format_t *p_fmt )
+{
+    sout_stream_sys_t *p_sys = p_stream->p_sys;
+    encoder_t *p_encoder = sout_EncoderCreate( p_parent );
+    if( !p_encoder )
+        return NULL;
+    p_encoder->p_module = NULL;
+
+    /* Create destination format */
+    es_format_Init( &p_encoder->fmt_in, p_fmt->i_cat, 0 );
+    es_format_Init( &p_encoder->fmt_out, p_fmt->i_cat, 0 );
+    p_encoder->fmt_out.i_id    = p_fmt->i_id;
+    p_encoder->fmt_out.i_group = p_fmt->i_group;
+
+    if( p_sys->psz_alang )
+        p_encoder->fmt_out.psz_language = strdup( p_sys->psz_alang );
+    else if( p_fmt->psz_language )
+        p_encoder->fmt_out.psz_language = strdup( p_fmt->psz_language );
+
+    return p_encoder;
+}
+
 static sout_stream_id_sys_t *Add( sout_stream_t *p_stream,
                                   const es_format_t *p_fmt )
 {
@@ -499,49 +522,173 @@ static sout_stream_id_sys_t *Add( sout_stream_t *p_stream,
     /* Create decoder object */
     id->p_decoder = vlc_object_create( p_stream, sizeof( decoder_t ) );
     if( !id->p_decoder )
+    {
+        msg_Err( p_stream, "cannot create decoder" );
         goto error;
+    }
     id->p_decoder->p_module = NULL;
     es_format_Init( &id->p_decoder->fmt_out, p_fmt->i_cat, 0 );
     es_format_Copy( &id->p_decoder->fmt_in, p_fmt );
     id->p_decoder->b_frame_drop_allowed = false;
 
     /* Create encoder object */
-    id->p_encoder = sout_EncoderCreate( p_stream );
-    if( !id->p_encoder )
-        goto error;
-    id->p_encoder->p_module = NULL;
-
-    /* Create destination format */
-    es_format_Init( &id->p_encoder->fmt_in, p_fmt->i_cat, 0 );
-    es_format_Init( &id->p_encoder->fmt_out, p_fmt->i_cat, 0 );
-    id->p_encoder->fmt_out.i_id    = p_fmt->i_id;
-    id->p_encoder->fmt_out.i_group = p_fmt->i_group;
+    if( p_fmt->i_cat == VIDEO_ES ) //&& p_sys->i_vcodec )
+    {
+        for( int config_idx=0;
+             config_idx<p_sys->pp_vencs.i_size;
+             config_idx++ )
+        {
+            config_chain_t *p_enc_cfg = ARRAY_VAL( p_sys->pp_vencs, config_idx );
+
+            /* the encoder config will be hold by the encoder if it succeed */
+            p_sys->p_venc_conf =
+                vlc_object_create( p_stream, sizeof(vlc_object_t) );
+
+            msg_Dbg( p_stream, "trying configuration venc={%s}",
+                     p_enc_cfg->psz_value );
+
+            size_t length = strlen( p_enc_cfg->psz_value );
+            size_t first_equal = strcspn( p_enc_cfg->psz_value, "=" );
+            size_t first_brace = strcspn( p_enc_cfg->psz_value, "{" );
+            size_t first_comma = strcspn( p_enc_cfg->psz_value, "," );
+
+            config_ChainDestroy( p_sys->p_video_cfg );
+            free( p_sys->psz_venc );
+
+            p_sys->psz_venc = NULL;
+            p_sys->p_video_cfg = NULL;
+
+            if( first_comma == length
+             && first_equal > first_brace )
+            {
+                /* venc configuration is in the legacy form */
+                char *psz_next;
+                psz_next = config_ChainCreate( &p_sys->psz_venc,
+                                               &p_sys->p_video_cfg,
+                                               p_enc_cfg->psz_value );
+                free( psz_next );
+            }
+            else
+            {
+                /* venc configuration is in the new extended form */
+                config_chain_t *p_conf;
+                config_ChainParseOptions( &p_conf, p_enc_cfg->psz_value );
+                config_ChainParse( p_sys->p_venc_conf, SOUT_CFG_PREFIX,
+                                   ppsz_venc_options, p_conf );
+                config_ChainDestroy( p_conf );
+
+                p_sys->psz_venc = NULL;
+                p_sys->p_video_cfg = NULL;
+                char *psz_string = var_GetString( p_sys->p_venc_conf,
+                                                  SOUT_CFG_PREFIX "module" );
+
+
+                if( !psz_string )
+                {
+                    msg_Err( p_stream,
+                             "missing key \"encoder\" in extended venc option: %s",
+                             p_enc_cfg->psz_value );
+                    vlc_object_release( p_sys->p_venc_conf );
+                    p_sys->p_venc_conf = NULL;
+                    continue;
+                }
+
+
+                char *psz_next;
+                psz_next = config_ChainCreate( &p_sys->psz_venc,
+                                               &p_sys->p_video_cfg,
+                                               psz_string );
+                free( psz_next );
+                free( psz_string );
+            }
+
+            char *psz_vcodec = var_GetString( p_sys->p_venc_conf,
+                                              SOUT_CFG_PREFIX "vcodec" );
+            if( psz_vcodec == NULL )
+                psz_vcodec = var_InheritString( p_sys->p_venc_conf,
+                                                SOUT_CFG_PREFIX "vcodec" );
+            p_sys->i_vcodec = 0;
+            if( psz_vcodec && *psz_vcodec )
+            {
+                char fcc[5] = "    \0";
+                memcpy( fcc, psz_vcodec, __MIN( strlen( psz_vcodec ), 4 ) );
+                p_sys->i_vcodec = vlc_fourcc_GetCodecFromString( VIDEO_ES, fcc );
+                msg_Dbg( p_stream, "Checking video codec mapping for %s got %4.4s ", fcc, (char*)&p_sys->i_vcodec);
+            }
+            free( psz_vcodec );
+
+
+            id->p_encoder = InitEncoder( p_sys->p_venc_conf, p_stream, p_fmt );
+            if( !id->p_encoder )
+            {
+                msg_Warn( p_stream, "cannot initialize encoder" );
+                vlc_object_release( p_sys->p_venc_conf );
+                p_sys->p_venc_conf = NULL;
+                continue;
+            }
+
+            if( !p_sys->i_vcodec )
+            {
+                msg_Warn( p_stream,
+                          "cannot use config venc={%s}, no vcodec defined",
+                          p_enc_cfg->psz_value );
+
+                es_format_Clean( &id->p_encoder->fmt_out );
+                vlc_object_release( p_sys->p_venc_conf );
+                p_sys->p_venc_conf = NULL;
+
+                vlc_object_release( id->p_encoder );
+                id->p_encoder = NULL;
+                continue;
+            }
+
+            if( !transcode_video_add( p_stream, p_fmt, id ) )
+            {
+                msg_Warn( p_stream,
+                          "can't use transcode configuration with venc=%s",
+                          p_enc_cfg->psz_value );
+                es_format_Clean( &id->p_encoder->fmt_out );
+
+                vlc_object_release( p_sys->p_venc_conf );
+                p_sys->p_venc_conf = NULL;
+
+                vlc_object_release( id->p_encoder );
+                id->p_encoder = NULL;
+                continue;
+            }
+
+            msg_Info( p_stream, "using venc={%s} configuration for transcoding",
+                      p_enc_cfg->psz_value );
+            break;
+        }
+    }
+    else
+    {
+        id->p_encoder = InitEncoder( VLC_OBJECT(p_stream), p_stream, p_fmt );
 
-    if( p_sys->psz_alang )
-        id->p_encoder->fmt_out.psz_language = strdup( p_sys->psz_alang );
-    else if( p_fmt->psz_language )
-        id->p_encoder->fmt_out.psz_language = strdup( p_fmt->psz_language );
+        bool success;
 
-    bool success;
+        if( p_fmt->i_cat == AUDIO_ES && p_sys->i_acodec )
+            success = transcode_audio_add(p_stream, p_fmt, id);
+        else if( ( p_fmt->i_cat == SPU_ES ) &&
+                 ( p_sys->i_scodec || p_sys->b_soverlay ) )
+            success = transcode_spu_add(p_stream, p_fmt, id);
+        else
+        {
+            msg_Dbg( p_stream, "not transcoding a stream (fcc=`%4.4s')",
+                     (char*)&p_fmt->i_codec );
+            id->id = sout_StreamIdAdd( p_stream->p_next, p_fmt );
+            id->b_transcode = false;
 
-    if( p_fmt->i_cat == AUDIO_ES && p_sys->i_acodec )
-        success = transcode_audio_add(p_stream, p_fmt, id);
-    else if( p_fmt->i_cat == VIDEO_ES && p_sys->i_vcodec )
-        success = transcode_video_add(p_stream, p_fmt, id);
-    else if( ( p_fmt->i_cat == SPU_ES ) &&
-             ( p_sys->i_scodec || p_sys->b_soverlay ) )
-        success = transcode_spu_add(p_stream, p_fmt, id);
-    else
-    {
-        msg_Dbg( p_stream, "not transcoding a stream (fcc=`%4.4s')",
-                 (char*)&p_fmt->i_codec );
-        id->id = sout_StreamIdAdd( p_stream->p_next, p_fmt );
-        id->b_transcode = false;
+            success = id->id;
+        }
 
-        success = id->id;
+        if (!success)
+            goto error;
     }
 
-    if(!success)
+
+    if( !id->p_encoder )
         goto error;
 
     return id;
diff --git a/modules/stream_out/transcode/transcode.h b/modules/stream_out/transcode/transcode.h
index 1b5e886d94..e2ba426030 100644
--- a/modules/stream_out/transcode/transcode.h
+++ b/modules/stream_out/transcode/transcode.h
@@ -38,6 +38,8 @@ struct sout_stream_sys_t
     char            *psz_af;
 
     /* Video */
+    DECL_ARRAY(config_chain_t *) pp_vencs;
+    vlc_object_t    *p_venc_conf;
     vlc_fourcc_t    i_vcodec;   /* codec video (0 if not transcode) */
     char            *psz_venc;
     config_chain_t  *p_video_cfg;
-- 
2.20.1

