Переглянути джерело

libvlc: Add multiple venc support in transcode

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.

(cherry picked from commit 90e06eb191326c8abc35cda59d3572fab4ba385c)
Alexandre Janniaux 6 роки тому
батько
коміт
553a5da7fe

+ 394 - 0
Resources/MobileVLCKit/patches/0025-transcode-add-support-for-mutliple-venc-paramete.patch

@@ -0,0 +1,394 @@
+From 5715c84484346241bc934ff139c268a044e40853 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] 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/codec/avcodec/encoder.c          |   2 +
+ modules/stream_out/transcode/transcode.c | 257 ++++++++++++++++++-----
+ modules/stream_out/transcode/transcode.h |   2 +
+ 3 files changed, 206 insertions(+), 55 deletions(-)
+
+diff --git a/modules/codec/avcodec/encoder.c b/modules/codec/avcodec/encoder.c
+index c587e2b243..670d03d7ef 100644
+--- a/modules/codec/avcodec/encoder.c
++++ b/modules/codec/avcodec/encoder.c
+@@ -346,12 +346,14 @@ int InitVideoEnc( vlc_object_t *p_this )
+         if( !p_codec )
+         {
+             msg_Err( p_this, "Encoder `%s' not found", psz_encoder );
++            free( psz_encoder );
+             return VLC_EGENERIC;
+         }
+         else if( p_codec->id != i_codec_id )
+         {
+             msg_Err( p_this, "Encoder `%s' can't handle %4.4s",
+                     psz_encoder, (char*)&p_enc->fmt_out.i_codec );
++            free( psz_encoder );
+             return VLC_EGENERIC;
+         }
+     }
+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.17.1
+