]> git.sesse.net Git - vlc/blobdiff - modules/stream_out/transcode.c
* Fix some configuration options declerations and strings
[vlc] / modules / stream_out / transcode.c
index 55707a21ef6039440efb8bd72429b2d55d5fe820..da2219e289ca67d1233312ab2cd0506b62e797ce 100644 (file)
@@ -27,6 +27,7 @@
  *****************************************************************************/
 #include <stdlib.h>
 #include <string.h>
+#include <math.h>
 
 #include <vlc/vlc.h>
 #include <vlc/input.h>
 #define DEINTERLACE_TEXT N_("Deinterlace video")
 #define DEINTERLACE_LONGTEXT N_( \
     "Allows you to deinterlace the video before encoding." )
+#define DEINTERLACE_MODULE_TEXT N_("Deinterlace module")
+#define DEINTERLACE_MODULE_LONGTEXT N_( \
+    "Specifies the deinterlace module to use." )
 #define WIDTH_TEXT N_("Video width")
 #define WIDTH_LONGTEXT N_( \
     "Allows you to specify the output video width." )
 #define HEIGHT_TEXT N_("Video height")
 #define HEIGHT_LONGTEXT N_( \
     "Allows you to specify the output video height." )
+#define VFILTER_TEXT N_("Video filter")
+#define VFILTER_LONGTEXT N_( \
+    "Allows you to specify video filters used after the video " \
+    "transcoding and subpictures overlaying." )
 
 #define CROPTOP_TEXT N_("Video crop top")
 #define CROPTOP_LONGTEXT N_( \
 #define THREADS_TEXT N_("Number of threads")
 #define THREADS_LONGTEXT N_( \
     "Allows you to specify the number of threads used for the transcoding." )
+#define HP_TEXT N_("High priority")
+#define HP_LONGTEXT N_( \
+    "Runs the optional encoder thread at the OUTPUT priority instead of " \
+    "VIDEO." )
 
 #define ASYNC_TEXT N_("Synchronise on audio track")
 #define ASYNC_LONGTEXT N_( \
     "This option will drop/duplicate video frames to synchronise the video " \
     "track on the audio track." )
 
+#define HURRYUP_TEXT N_( "Hurry up" )
+#define HURRYUP_LONGTEXT N_( "Allows you to specify if the transcoder " \
+  "should drop frames if your CPU can't keep up with the encoding rate." )
+
+static char *ppsz_deinterlace_type[] =
+{
+    "deinterlace", "ffmpeg-deinterlace"
+};
+
 static int  Open ( vlc_object_t * );
 static void Close( vlc_object_t * );
 
 #define SOUT_CFG_PREFIX "sout-transcode-"
 
 vlc_module_begin();
+    set_shortname( _("Transcode"));
     set_description( _("Transcode stream output") );
     set_capability( "sout stream", 50 );
     add_shortcut( "transcode" );
     set_callbacks( Open, Close );
     set_category( CAT_SOUT );
     set_subcategory( SUBCAT_SOUT_STREAM );
-
+    set_section( N_("Video"), NULL );
     add_string( SOUT_CFG_PREFIX "venc", NULL, NULL, VENC_TEXT,
                 VENC_LONGTEXT, VLC_FALSE );
     add_string( SOUT_CFG_PREFIX "vcodec", NULL, NULL, VCODEC_TEXT,
@@ -147,12 +169,21 @@ vlc_module_begin();
                SCALE_LONGTEXT, VLC_FALSE );
     add_float( SOUT_CFG_PREFIX "fps", 0, NULL, FPS_TEXT,
                FPS_LONGTEXT, VLC_FALSE );
+    add_bool( SOUT_CFG_PREFIX "hurry-up", VLC_TRUE, NULL, HURRYUP_TEXT,
+               HURRYUP_LONGTEXT, VLC_FALSE );
     add_bool( SOUT_CFG_PREFIX "deinterlace", 0, NULL, DEINTERLACE_TEXT,
               DEINTERLACE_LONGTEXT, VLC_FALSE );
+    add_string( SOUT_CFG_PREFIX "deinterlace-module", "deinterlace", NULL,
+                DEINTERLACE_MODULE_TEXT, DEINTERLACE_MODULE_LONGTEXT,
+                VLC_FALSE );
+        change_string_list( ppsz_deinterlace_type, 0, 0 );
     add_integer( SOUT_CFG_PREFIX "width", 0, NULL, WIDTH_TEXT,
                  WIDTH_LONGTEXT, VLC_TRUE );
     add_integer( SOUT_CFG_PREFIX "height", 0, NULL, HEIGHT_TEXT,
                  HEIGHT_LONGTEXT, VLC_TRUE );
+    add_module_list_cat( SOUT_CFG_PREFIX "vfilter", SUBCAT_VIDEO_VFILTER,
+                     NULL, NULL,
+                     VFILTER_TEXT, VFILTER_LONGTEXT, VLC_FALSE );
 
     add_integer( SOUT_CFG_PREFIX "croptop", 0, NULL, CROPTOP_TEXT,
                  CROPTOP_LONGTEXT, VLC_TRUE );
@@ -163,6 +194,7 @@ vlc_module_begin();
     add_integer( SOUT_CFG_PREFIX "cropright", 0, NULL, CROPRIGHT_TEXT,
                  CROPRIGHT_LONGTEXT, VLC_TRUE );
 
+    set_section( N_("Audio"), NULL );
     add_string( SOUT_CFG_PREFIX "aenc", NULL, NULL, AENC_TEXT,
                 AENC_LONGTEXT, VLC_FALSE );
     add_string( SOUT_CFG_PREFIX "acodec", NULL, NULL, ACODEC_TEXT,
@@ -173,29 +205,34 @@ vlc_module_begin();
                  ACHANS_LONGTEXT, VLC_FALSE );
     add_integer( SOUT_CFG_PREFIX "samplerate", 0, NULL, ARATE_TEXT,
                  ARATE_LONGTEXT, VLC_TRUE );
+    add_bool( SOUT_CFG_PREFIX "audio-sync", 0, NULL, ASYNC_TEXT,
+              ASYNC_LONGTEXT, VLC_FALSE );
 
+    set_section( N_("Overlays/Subtitles"), NULL );
     add_string( SOUT_CFG_PREFIX "senc", NULL, NULL, SENC_TEXT,
                 SENC_LONGTEXT, VLC_FALSE );
     add_string( SOUT_CFG_PREFIX "scodec", NULL, NULL, SCODEC_TEXT,
                 SCODEC_LONGTEXT, VLC_FALSE );
     add_bool( SOUT_CFG_PREFIX "soverlay", 0, NULL, SCODEC_TEXT,
                SCODEC_LONGTEXT, VLC_FALSE );
-    add_string( SOUT_CFG_PREFIX "sfilter", NULL, NULL, SFILTER_TEXT,
-                SFILTER_LONGTEXT, VLC_FALSE );
+    add_module_list_cat( SOUT_CFG_PREFIX "sfilter", SUBCAT_VIDEO_SUBPIC,
+                     NULL, NULL,
+                     SFILTER_TEXT, SFILTER_LONGTEXT, VLC_FALSE );
 
+    set_section( N_("Miscellaneous"), NULL );
     add_integer( SOUT_CFG_PREFIX "threads", 0, NULL, THREADS_TEXT,
                  THREADS_LONGTEXT, VLC_TRUE );
+    add_bool( SOUT_CFG_PREFIX "high-priority", 0, NULL, HP_TEXT, HP_LONGTEXT,
+              VLC_TRUE );
 
-    add_bool( SOUT_CFG_PREFIX "audio-sync", 0, NULL, ASYNC_TEXT,
-              ASYNC_LONGTEXT, VLC_FALSE );
 vlc_module_end();
 
 static const char *ppsz_sout_options[] = {
     "venc", "vcodec", "vb", "croptop", "cropbottom", "cropleft", "cropright",
-    "scale", "fps", "width", "height", "deinterlace", "threads",
-    "aenc", "acodec", "ab", "samplerate", "channels",
-    "senc", "scodec", "soverlay", "sfilter",
-    "audio-sync", NULL
+    "scale", "fps", "width", "height", "vfilter", "deinterlace",
+    "deinterlace-module", "threads", "hurry-up", "aenc", "acodec", "ab",
+    "samplerate", "channels", "senc", "scodec", "soverlay", "sfilter",
+    "audio-sync", "high-priority", NULL
 };
 
 /*****************************************************************************
@@ -278,7 +315,14 @@ struct sout_stream_sys_t
     int             i_width;
     int             i_height;
     vlc_bool_t      b_deinterlace;
+    char            *psz_deinterlace;
+    sout_cfg_t      *p_deinterlace_cfg;
     int             i_threads;
+    vlc_bool_t      b_high_priority;
+    vlc_bool_t      b_hurry_up;
+    char            *psz_vfilters[10];
+    sout_cfg_t      *p_vfilters_cfg[10];
+    int             i_vfilters;
 
     int             i_crop_top;
     int             i_crop_bottom;
@@ -300,10 +344,12 @@ struct sout_stream_sys_t
 struct decoder_owner_sys_t
 {
     picture_t *pp_pics[PICTURE_RING_SIZE];
+    sout_stream_sys_t *p_sys;
 };
 struct filter_owner_sys_t
 {
     picture_t *pp_pics[PICTURE_RING_SIZE];
+    sout_stream_sys_t *p_sys;
 };
 
 /*****************************************************************************
@@ -321,7 +367,7 @@ static int Open( vlc_object_t *p_this )
     if( !p_sys->p_out )
     {
         msg_Err( p_stream, "cannot create chain" );
-        free( p_sys );
+        vlc_object_destroy( p_sys );
         return VLC_EGENERIC;
     }
 
@@ -403,15 +449,51 @@ static int Open( vlc_object_t *p_this )
     var_Get( p_stream, SOUT_CFG_PREFIX "fps", &val );
     p_sys->f_fps = val.f_float;
 
+    var_Get( p_stream, SOUT_CFG_PREFIX "hurry-up", &val );
+    p_sys->b_hurry_up = val.b_bool;
+
     var_Get( p_stream, SOUT_CFG_PREFIX "width", &val );
     p_sys->i_width = val.i_int;
 
     var_Get( p_stream, SOUT_CFG_PREFIX "height", &val );
     p_sys->i_height = val.i_int;
 
+    var_Get( p_stream, SOUT_CFG_PREFIX "vfilter", &val );
+    p_sys->i_vfilters = 0;
+    if( val.psz_string && *val.psz_string )
+    {
+        char *psz_parser = val.psz_string;
+
+        while( psz_parser != NULL && *psz_parser != '\0' )
+        {
+            psz_parser = sout_CfgCreate(
+                                   &p_sys->psz_vfilters[p_sys->i_vfilters],
+                                   &p_sys->p_vfilters_cfg[p_sys->i_vfilters],
+                                   psz_parser );
+            p_sys->i_vfilters++;
+            if( psz_parser != NULL && *psz_parser != '\0' ) psz_parser++;
+        }
+    }
+    if( val.psz_string ) free( val.psz_string );
+    p_sys->psz_vfilters[p_sys->i_vfilters] = NULL;
+    p_sys->p_vfilters_cfg[p_sys->i_vfilters] = NULL;
+
     var_Get( p_stream, SOUT_CFG_PREFIX "deinterlace", &val );
     p_sys->b_deinterlace = val.b_bool;
 
+    var_Get( p_stream, SOUT_CFG_PREFIX "deinterlace-module", &val );
+    p_sys->psz_deinterlace = NULL;
+    p_sys->p_deinterlace_cfg = NULL;
+    if( val.psz_string && *val.psz_string )
+    {
+        char *psz_next;
+        psz_next = sout_CfgCreate( &p_sys->psz_deinterlace,
+                                   &p_sys->p_deinterlace_cfg,
+                                   val.psz_string );
+        if( psz_next ) free( psz_next );
+    }
+    if( val.psz_string ) free( val.psz_string );
+
     var_Get( p_stream, SOUT_CFG_PREFIX "croptop", &val );
     p_sys->i_crop_top = val.i_int;
     var_Get( p_stream, SOUT_CFG_PREFIX "cropbottom", &val );
@@ -423,6 +505,8 @@ static int Open( vlc_object_t *p_this )
 
     var_Get( p_stream, SOUT_CFG_PREFIX "threads", &val );
     p_sys->i_threads = val.i_int;
+    var_Get( p_stream, SOUT_CFG_PREFIX "high-priority", &val );
+    p_sys->b_high_priority = val.b_bool;
 
     if( p_sys->i_vcodec )
     {
@@ -458,7 +542,7 @@ static int Open( vlc_object_t *p_this )
 
     if( p_sys->i_scodec )
     {
-        msg_Dbg( p_stream, "codec spu=%4.4s", (char *)&p_sys->i_acodec );
+        msg_Dbg( p_stream, "codec spu=%4.4s", (char *)&p_sys->i_scodec );
     }
 
     var_Get( p_stream, SOUT_CFG_PREFIX "soverlay", &val );
@@ -524,6 +608,20 @@ static void Close( vlc_object_t * p_this )
     }
     if( p_sys->psz_venc ) free( p_sys->psz_venc );
 
+    while( p_sys->p_deinterlace_cfg != NULL )
+    {
+        sout_cfg_t *p_next = p_sys->p_deinterlace_cfg->p_next;
+
+        if( p_sys->p_deinterlace_cfg->psz_name )
+            free( p_sys->p_deinterlace_cfg->psz_name );
+        if( p_sys->p_deinterlace_cfg->psz_value )
+            free( p_sys->p_deinterlace_cfg->psz_value );
+        free( p_sys->p_deinterlace_cfg );
+
+        p_sys->p_deinterlace_cfg = p_next;
+    }
+    if( p_sys->psz_deinterlace ) free( p_sys->psz_deinterlace );
+
     while( p_sys->p_spu_cfg != NULL )
     {
         sout_cfg_t *p_next = p_sys->p_spu_cfg->p_next;
@@ -556,6 +654,8 @@ struct sout_stream_id_t
     /* Filters */
     filter_t        *pp_filter[10];
     int             i_filter;
+    filter_t        *pp_vfilter[10];
+    int             i_vfilter;
 
     /* Encoder */
     encoder_t       *p_encoder;
@@ -587,9 +687,6 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
     vlc_object_attach( id->p_decoder, p_stream );
     id->p_decoder->p_module = NULL;
     id->p_decoder->fmt_in = *p_fmt;
-    id->p_decoder->fmt_out = *p_fmt;
-    id->p_decoder->fmt_out.i_extra = 0;
-    id->p_decoder->fmt_out.p_extra = 0;
     id->p_decoder->b_pace_control = VLC_TRUE;
 
     /* Create encoder object */
@@ -619,11 +716,28 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
         id->p_encoder->fmt_out.i_codec = p_sys->i_acodec;
         id->p_encoder->fmt_out.audio.i_rate = p_sys->i_sample_rate > 0 ?
             p_sys->i_sample_rate : (int)p_fmt->audio.i_rate;
-        id->p_encoder->fmt_out.audio.i_channels = p_sys->i_channels > 0 ?
-            p_sys->i_channels : p_fmt->audio.i_channels;
         id->p_encoder->fmt_out.i_bitrate = p_sys->i_abitrate;
         id->p_encoder->fmt_out.audio.i_bitspersample =
             p_fmt->audio.i_bitspersample;
+        id->p_encoder->fmt_out.audio.i_channels = p_sys->i_channels > 0 ?
+            p_sys->i_channels : p_fmt->audio.i_channels;
+        /* Sanity check for audio channels */
+        id->p_encoder->fmt_out.audio.i_channels =
+            __MIN( id->p_encoder->fmt_out.audio.i_channels,
+                   id->p_decoder->fmt_in.audio.i_channels );
+        id->p_encoder->fmt_out.audio.i_original_channels =
+            id->p_decoder->fmt_in.audio.i_physical_channels;
+        if( id->p_decoder->fmt_in.audio.i_channels ==
+            id->p_encoder->fmt_out.audio.i_channels )
+        {
+            id->p_encoder->fmt_out.audio.i_physical_channels =
+                id->p_decoder->fmt_in.audio.i_physical_channels;
+        }
+        else
+        {
+            id->p_encoder->fmt_out.audio.i_physical_channels =
+                pi_channels_maps[id->p_encoder->fmt_out.audio.i_channels];
+        }
 
         /* Build decoder -> filter -> encoder chain */
         if( transcode_audio_new( p_stream, id ) )
@@ -666,8 +780,9 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
 
         if( p_sys->f_fps > 0 )
         {
-            id->p_encoder->fmt_out.video.i_frame_rate = p_sys->f_fps * 1000;
-            id->p_encoder->fmt_out.video.i_frame_rate_base = 1000;
+            id->p_encoder->fmt_out.video.i_frame_rate =
+                (p_sys->f_fps * 1001) + 0.5;
+            id->p_encoder->fmt_out.video.i_frame_rate_base = 1001;
         }
     }
     else if( p_fmt->i_cat == SPU_ES && (p_sys->i_scodec || p_sys->psz_senc) )
@@ -881,16 +996,51 @@ int audio_BitsPerSample( vlc_fourcc_t i_format )
     return 0;
 }
 
+static filter_t *transcode_audio_filter_new( sout_stream_t *p_stream,
+                                             sout_stream_id_t *id,
+                                             es_format_t *p_fmt_in,
+                                             es_format_t *p_fmt_out )
+{
+    filter_t *p_filter = vlc_object_create( p_stream, VLC_OBJECT_FILTER );
+
+    vlc_object_attach( p_filter, p_stream );
+    p_filter->pf_audio_buffer_new = __block_New;
+
+    p_filter->fmt_in = *p_fmt_in;
+    p_filter->fmt_out = *p_fmt_out;
+
+    p_filter->p_module = module_Need( p_filter, "audio filter2", 0, 0 );
+    if( p_filter->p_module )
+    {
+        p_filter->fmt_out.audio.i_bitspersample = 
+            audio_BitsPerSample( p_filter->fmt_out.i_codec );
+        *p_fmt_in = p_filter->fmt_out;
+    }
+    else
+    {
+        vlc_object_detach( p_filter );
+        vlc_object_destroy( p_filter );
+        p_filter = 0;
+    }
+
+    return p_filter;
+}
+
 static int transcode_audio_new( sout_stream_t *p_stream,
                                 sout_stream_id_t *id )
 {
     sout_stream_sys_t *p_sys = p_stream->p_sys;
+    es_format_t fmt_last;
+    int i_pass = 6;
 
     /*
      * Open decoder
      */
 
     /* Initialization of decoder structures */
+    id->p_decoder->fmt_out = id->p_decoder->fmt_in;
+    id->p_decoder->fmt_out.i_extra = 0;
+    id->p_decoder->fmt_out.p_extra = 0;
     id->p_decoder->pf_decode_audio = 0;
     id->p_decoder->pf_aout_buffer_new = audio_new_buffer;
     id->p_decoder->pf_aout_buffer_del = audio_del_buffer;
@@ -906,6 +1056,9 @@ static int transcode_audio_new( sout_stream_t *p_stream,
     }
     id->p_decoder->fmt_out.audio.i_bitspersample = 
         audio_BitsPerSample( id->p_decoder->fmt_out.i_codec );
+    fmt_last = id->p_decoder->fmt_out;
+    /* FIX decoders so we don't have to do this */
+    fmt_last.audio.i_rate = id->p_decoder->fmt_in.audio.i_rate;
 
     /*
      * Open encoder
@@ -916,27 +1069,14 @@ static int transcode_audio_new( sout_stream_t *p_stream,
                     id->p_decoder->fmt_out.i_codec );
     id->p_encoder->fmt_in.audio.i_format = id->p_decoder->fmt_out.i_codec;
 
-    /* Sanity check for audio channels */
-    id->p_encoder->fmt_out.audio.i_channels =
-        __MIN( id->p_encoder->fmt_out.audio.i_channels,
-               id->p_decoder->fmt_out.audio.i_channels );
-    if( id->p_decoder->fmt_out.audio.i_channels ==
-        id->p_encoder->fmt_out.audio.i_channels )
-        id->p_encoder->fmt_out.audio.i_physical_channels =
-            id->p_encoder->fmt_out.audio.i_original_channels =
-                id->p_decoder->fmt_out.audio.i_physical_channels;
-    else
-        id->p_encoder->fmt_out.audio.i_physical_channels =
-            id->p_encoder->fmt_out.audio.i_original_channels =
-                pi_channels_maps[id->p_encoder->fmt_out.audio.i_channels];
-
     /* Initialization of encoder format structures */
     es_format_Init( &id->p_encoder->fmt_in, AUDIO_ES, AOUT_FMT_S16_NE );
     id->p_encoder->fmt_in.audio.i_format = AOUT_FMT_S16_NE;
     id->p_encoder->fmt_in.audio.i_rate = id->p_encoder->fmt_out.audio.i_rate;
     id->p_encoder->fmt_in.audio.i_physical_channels =
-        id->p_encoder->fmt_in.audio.i_original_channels =
-            id->p_encoder->fmt_out.audio.i_physical_channels;
+        id->p_encoder->fmt_out.audio.i_physical_channels;
+    id->p_encoder->fmt_in.audio.i_original_channels =
+        id->p_encoder->fmt_out.audio.i_original_channels;
     id->p_encoder->fmt_in.audio.i_channels =
         id->p_encoder->fmt_out.audio.i_channels;
     id->p_encoder->fmt_in.audio.i_bitspersample =
@@ -957,74 +1097,81 @@ static int transcode_audio_new( sout_stream_t *p_stream,
     id->p_encoder->fmt_in.audio.i_bitspersample =
         audio_BitsPerSample( id->p_encoder->fmt_in.i_codec );
 
-    /* Check if we need a filter for chroma conversion or resizing */
-    if( id->p_decoder->fmt_out.i_codec !=
-        id->p_encoder->fmt_in.i_codec )
+    /* Load conversion filters */
+    if( fmt_last.audio.i_channels != id->p_encoder->fmt_in.audio.i_channels ||
+        fmt_last.audio.i_rate != id->p_encoder->fmt_in.audio.i_rate )
     {
-        id->pp_filter[0] =
-            vlc_object_create( p_stream, VLC_OBJECT_FILTER );
-        vlc_object_attach( id->pp_filter[0], p_stream );
+        /* We'll have to go through fl32 first */
+        es_format_t fmt_out = id->p_encoder->fmt_in;
+        fmt_out.i_codec = fmt_out.audio.i_format = VLC_FOURCC('f','l','3','2');
 
-        id->pp_filter[0]->pf_audio_buffer_new = __block_New;
+        id->pp_filter[id->i_filter] =
+            transcode_audio_filter_new( p_stream, id, &fmt_last, &fmt_out );
 
-        id->pp_filter[0]->fmt_in = id->p_decoder->fmt_out;
-        id->pp_filter[0]->fmt_out = id->p_encoder->fmt_in;
-        id->pp_filter[0]->p_module =
-            module_Need( id->pp_filter[0], "audio filter2", 0, 0 );
-        if( id->pp_filter[0]->p_module ) id->i_filter++;
-        else
+        if( id->pp_filter[id->i_filter] ) id->i_filter++;
+    }
+
+    while( i_pass-- )
+    {
+        if( fmt_last.audio.i_channels !=
+            id->p_encoder->fmt_in.audio.i_channels ||
+            fmt_last.audio.i_rate != id->p_encoder->fmt_in.audio.i_rate ||
+            fmt_last.i_codec != id->p_encoder->fmt_in.i_codec )
         {
-            msg_Dbg( p_stream, "no audio filter found (%4.4s->%4.4s)",
-                     (char *)&id->pp_filter[0]->fmt_in,
-                     (char *)&id->pp_filter[0]->fmt_out );
-            vlc_object_detach( id->pp_filter[0] );
-            vlc_object_destroy( id->pp_filter[0] );
-            module_Unneed( id->p_decoder, id->p_decoder->p_module );
-            id->p_decoder->p_module = 0;
-            module_Unneed( id->p_encoder, id->p_encoder->p_module );
-            id->p_encoder->p_module = 0;
-            return VLC_EGENERIC;
+            id->pp_filter[id->i_filter] =
+                transcode_audio_filter_new( p_stream, id, &fmt_last,
+                                            &id->p_encoder->fmt_in );
+
+            if( id->pp_filter[id->i_filter] ) id->i_filter++;
+            else break;
         }
+    }
 
-        id->pp_filter[0]->fmt_out.audio.i_bitspersample = 
-            audio_BitsPerSample( id->pp_filter[0]->fmt_out.i_codec );
+    /* Final checks to see if conversions were successful */
+    if( fmt_last.i_codec != id->p_encoder->fmt_in.i_codec )
+    {
+        msg_Err( p_stream, "no audio filter found (%4.4s->%4.4s)",
+                 (char *)&fmt_last.i_codec,
+                 (char *)&id->p_encoder->fmt_in.i_codec );
+        transcode_audio_close( p_stream, id );
+        return VLC_EGENERIC;
+    }
 
-        /* Try a 2 stage conversion */
-        if( id->pp_filter[0]->fmt_out.i_codec !=
-            id->p_encoder->fmt_in.i_codec )
-        {
-            id->pp_filter[1] =
-                vlc_object_create( p_stream, VLC_OBJECT_FILTER );
-            vlc_object_attach( id->pp_filter[1], p_stream );
-
-            id->pp_filter[1]->pf_audio_buffer_new = __block_New;
-
-            id->pp_filter[1]->fmt_in = id->pp_filter[0]->fmt_out;
-            id->pp_filter[1]->fmt_out = id->p_encoder->fmt_in;
-            id->pp_filter[1]->p_module =
-              module_Need( id->pp_filter[1], "audio filter2", 0, 0 );
-            if( !id->pp_filter[1]->p_module ||
-                id->pp_filter[1]->fmt_out.i_codec !=
-                  id->p_encoder->fmt_in.i_codec )
-            {
-                msg_Dbg( p_stream, "no audio filter found (%4.4s->%4.4s)",
-                         (char *)&id->pp_filter[1]->fmt_in,
-                         (char *)&id->pp_filter[1]->fmt_out );
-                module_Unneed( id->pp_filter[0], id->pp_filter[0]->p_module );
-                vlc_object_detach( id->pp_filter[0] );
-                vlc_object_destroy( id->pp_filter[0] );
-                if( id->pp_filter[1]->p_module )
-                module_Unneed( id->pp_filter[0], id->pp_filter[0]->p_module );
-                vlc_object_detach( id->pp_filter[1] );
-                vlc_object_destroy( id->pp_filter[1] );
-                module_Unneed( id->p_decoder, id->p_decoder->p_module );
-                id->p_decoder->p_module = 0;
-                module_Unneed( id->p_encoder, id->p_encoder->p_module );
-                id->p_encoder->p_module = 0;
-                return VLC_EGENERIC;
-            }
-            else id->i_filter++;
-        }
+    if( fmt_last.audio.i_channels != id->p_encoder->fmt_in.audio.i_channels )
+    {
+        msg_Err( p_stream, "no audio filter found for mixing from"
+                 " %i to %i channels", fmt_last.audio.i_channels,
+                 id->p_encoder->fmt_in.audio.i_channels );
+#if 0
+        /* FIXME : this might work, but only if the encoder is restarted */
+        id->p_encoder->fmt_in.audio.i_channels = fmt_last.audio.i_channels;
+        id->p_encoder->fmt_out.audio.i_channels = fmt_last.audio.i_channels;
+
+        id->p_encoder->fmt_in.audio.i_physical_channels =
+            id->p_encoder->fmt_in.audio.i_original_channels =
+                fmt_last.audio.i_physical_channels;
+        id->p_encoder->fmt_out.audio.i_physical_channels =
+            id->p_encoder->fmt_out.audio.i_original_channels =
+                fmt_last.audio.i_physical_channels;
+#else
+        transcode_audio_close( p_stream, id );
+        return VLC_EGENERIC;
+#endif
+    }
+
+    if( fmt_last.audio.i_rate != id->p_encoder->fmt_in.audio.i_rate )
+    {
+        msg_Err( p_stream, "no audio filter found for resampling from"
+                 " %iHz to %iHz", fmt_last.audio.i_rate,
+                 id->p_encoder->fmt_in.audio.i_rate );
+#if 0
+        /* FIXME : this might work, but only if the encoder is restarted */
+        id->p_encoder->fmt_in.audio.i_rate = fmt_last.audio.i_rate;
+        id->p_encoder->fmt_out.audio.i_rate = fmt_last.audio.i_rate;
+#else
+        transcode_audio_close( p_stream, id );
+        return VLC_EGENERIC;
+#endif
     }
 
     /* FIXME: Hack for mp3 transcoding support */
@@ -1042,10 +1189,12 @@ static void transcode_audio_close( sout_stream_t *p_stream,
     /* Close decoder */
     if( id->p_decoder->p_module )
         module_Unneed( id->p_decoder, id->p_decoder->p_module );
+    id->p_decoder->p_module = 0;
 
     /* Close encoder */
     if( id->p_encoder->p_module )
         module_Unneed( id->p_encoder, id->p_encoder->p_module );
+    id->p_encoder->p_module = 0;
 
     /* Close filters */
     for( i = 0; i < id->i_filter; i++ )
@@ -1169,6 +1318,9 @@ static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id )
      */
 
     /* Initialization of decoder structures */
+    id->p_decoder->fmt_out = id->p_decoder->fmt_in;
+    id->p_decoder->fmt_out.i_extra = 0;
+    id->p_decoder->fmt_out.p_extra = 0;
     id->p_decoder->pf_decode_video = 0;
     id->p_decoder->pf_vout_buffer_new = video_new_buffer_decoder;
     id->p_decoder->pf_vout_buffer_del = video_del_buffer_decoder;
@@ -1177,6 +1329,7 @@ static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id )
     id->p_decoder->p_owner = malloc( sizeof(decoder_owner_sys_t) );
     for( i = 0; i < PICTURE_RING_SIZE; i++ )
         id->p_decoder->p_owner->pp_pics[i] = 0;
+    id->p_decoder->p_owner->p_sys = p_sys;
     //id->p_decoder->p_cfg = p_sys->p_video_cfg;
 
     id->p_decoder->p_module =
@@ -1235,6 +1388,8 @@ static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id )
 
     if( p_sys->i_threads >= 1 )
     {
+        int i_priority = p_sys->b_high_priority ? VLC_THREAD_PRIORITY_OUTPUT :
+                           VLC_THREAD_PRIORITY_VIDEO;
         p_sys->id_video = id;
         vlc_mutex_init( p_stream, &p_sys->lock_out );
         vlc_cond_init( p_stream, &p_sys->cond );
@@ -1243,8 +1398,8 @@ static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id )
         p_sys->i_last_pic = 0;
         p_sys->p_buffers = NULL;
         p_sys->b_die = p_sys->b_error = 0;
-        if( vlc_thread_create( p_sys, "encoder", EncoderThread,
-                               VLC_THREAD_PRIORITY_VIDEO, VLC_FALSE ) )
+        if( vlc_thread_create( p_sys, "encoder", EncoderThread, i_priority,
+                               VLC_FALSE ) )
         {
             msg_Err( p_stream, "cannot spawn encoder thread" );
             module_Unneed( id->p_decoder, id->p_decoder->p_module );
@@ -1421,6 +1576,23 @@ static void transcode_video_close( sout_stream_t *p_stream,
 
         vlc_object_destroy( id->pp_filter[i] );
     }
+    for( i = 0; i < id->i_vfilter; i++ )
+    {
+        vlc_object_detach( id->pp_vfilter[i] );
+        if( id->pp_vfilter[i]->p_module )
+            module_Unneed( id->pp_vfilter[i], id->pp_vfilter[i]->p_module );
+
+        /* Clean-up pictures ring buffer */
+        for( j = 0; j < PICTURE_RING_SIZE; j++ )
+        {
+            if( id->pp_vfilter[i]->p_owner->pp_pics[j] )
+                video_del_buffer( VLC_OBJECT(id->pp_vfilter[i]),
+                                  id->pp_vfilter[i]->p_owner->pp_pics[j] );
+        }
+        free( id->pp_vfilter[i]->p_owner );
+
+        vlc_object_destroy( id->pp_vfilter[i] );
+    }
 }
 
 static int transcode_video_process( sout_stream_t *p_stream,
@@ -1436,6 +1608,18 @@ static int transcode_video_process( sout_stream_t *p_stream,
     {
         subpicture_t *p_subpic = 0;
 
+        if( p_stream->p_sout->i_out_pace_nocontrol && p_sys->b_hurry_up )
+        {
+            mtime_t current_date = mdate();
+            if( current_date + 50000 > p_pic->date )
+            {
+                msg_Dbg( p_stream, "late picture skipped ("I64Fd")",
+                         current_date + 50000 - p_pic->date );
+                p_pic->pf_release( p_pic );
+                continue;
+            }
+        }
+
         if( p_sys->b_master_sync )
         {
             mtime_t i_video_drift;
@@ -1462,7 +1646,7 @@ static int transcode_video_process( sout_stream_t *p_stream,
                          (int)(i_video_drift - i_master_drift) );
 #endif
                 p_pic->pf_release( p_pic );
-                return VLC_EGENERIC;
+                continue;
             }
             else if( i_video_drift > i_master_drift + 50000 )
             {
@@ -1478,9 +1662,9 @@ static int transcode_video_process( sout_stream_t *p_stream,
         {
             if( transcode_video_encoder_open( p_stream, id ) != VLC_SUCCESS )
             {
+                p_pic->pf_release( p_pic );
                 transcode_video_close( p_stream, id );
                 id->b_transcode = VLC_FALSE;
-                p_pic->pf_release( p_pic );
                 return VLC_EGENERIC;
             }
 
@@ -1498,15 +1682,17 @@ static int transcode_video_process( sout_stream_t *p_stream,
 
                 id->pp_filter[id->i_filter]->fmt_in = id->p_decoder->fmt_out;
                 id->pp_filter[id->i_filter]->fmt_out = id->p_decoder->fmt_out;
+                id->pp_filter[id->i_filter]->p_cfg = p_sys->p_deinterlace_cfg;
                 id->pp_filter[id->i_filter]->p_module =
                     module_Need( id->pp_filter[id->i_filter],
-                                 "video filter2", "deinterlace", 0 );
+                                 "video filter2", p_sys->psz_deinterlace, 0 );
                 if( id->pp_filter[id->i_filter]->p_module )
                 {
                     id->pp_filter[id->i_filter]->p_owner =
                         malloc( sizeof(filter_owner_sys_t) );
                     for( i = 0; i < PICTURE_RING_SIZE; i++ )
                         id->pp_filter[id->i_filter]->p_owner->pp_pics[i] = 0;
+                    id->pp_filter[id->i_filter]->p_owner->p_sys = p_sys;
 
                     id->i_filter++;
                 }
@@ -1539,6 +1725,7 @@ static int transcode_video_process( sout_stream_t *p_stream,
 
                 id->pp_filter[id->i_filter]->fmt_in = id->p_decoder->fmt_out;
                 id->pp_filter[id->i_filter]->fmt_out = id->p_encoder->fmt_in;
+                id->pp_filter[id->i_filter]->p_cfg = NULL;
                 id->pp_filter[id->i_filter]->p_module =
                     module_Need( id->pp_filter[id->i_filter],
                                  "video filter2", 0, 0 );
@@ -1548,6 +1735,7 @@ static int transcode_video_process( sout_stream_t *p_stream,
                         malloc( sizeof(filter_owner_sys_t) );
                     for( i = 0; i < PICTURE_RING_SIZE; i++ )
                         id->pp_filter[id->i_filter]->p_owner->pp_pics[i] = 0;
+                    id->pp_filter[id->i_filter]->p_owner->p_sys = p_sys;
 
                     id->i_filter++;
                 }
@@ -1557,12 +1745,47 @@ static int transcode_video_process( sout_stream_t *p_stream,
                     vlc_object_detach( id->pp_filter[id->i_filter] );
                     vlc_object_destroy( id->pp_filter[id->i_filter] );
 
+                    p_pic->pf_release( p_pic );
                     transcode_video_close( p_stream, id );
                     id->b_transcode = VLC_FALSE;
-                    p_pic->pf_release( p_pic );
                     return VLC_EGENERIC;
                 }
             }
+
+            for( i = 0; i < p_sys->i_vfilters; i++ )
+            {
+                id->pp_vfilter[id->i_vfilter] =
+                    vlc_object_create( p_stream, VLC_OBJECT_FILTER );
+                vlc_object_attach( id->pp_vfilter[id->i_vfilter], p_stream );
+
+                id->pp_vfilter[id->i_vfilter]->pf_vout_buffer_new =
+                    video_new_buffer_filter;
+                id->pp_vfilter[id->i_vfilter]->pf_vout_buffer_del =
+                    video_del_buffer_filter;
+
+                id->pp_vfilter[id->i_vfilter]->fmt_in = id->p_encoder->fmt_in;
+                id->pp_vfilter[id->i_vfilter]->fmt_out = id->p_encoder->fmt_in;
+                id->pp_vfilter[id->i_vfilter]->p_cfg = p_sys->p_vfilters_cfg[i];
+                id->pp_vfilter[id->i_vfilter]->p_module =
+                    module_Need( id->pp_vfilter[id->i_vfilter],
+                          "video filter2", p_sys->psz_vfilters[i], 0 );
+                if( id->pp_vfilter[id->i_vfilter]->p_module )
+                {
+                    id->pp_vfilter[id->i_vfilter]->p_owner =
+                        malloc( sizeof(filter_owner_sys_t) );
+                    for( i = 0; i < PICTURE_RING_SIZE; i++ )
+                        id->pp_vfilter[id->i_vfilter]->p_owner->pp_pics[i] = 0;
+                    id->pp_vfilter[id->i_vfilter]->p_owner->p_sys = p_sys;
+
+                    id->i_vfilter++;
+                }
+                else
+                {
+                    msg_Dbg( p_stream, "no video filter found" );
+                    vlc_object_detach( id->pp_vfilter[id->i_vfilter] );
+                    vlc_object_destroy( id->pp_vfilter[id->i_vfilter] );
+                }
+            }
         }
 
         /* Run filter chain */
@@ -1619,6 +1842,12 @@ static int transcode_video_process( sout_stream_t *p_stream,
                                    i_scale_width, i_scale_height );
         }
 
+        /* Run vfilter chain */
+        for( i = 0; i < id->i_vfilter; i++ )
+        {
+            p_pic = id->pp_vfilter[i]->pf_video_filter(id->pp_vfilter[i], p_pic);
+        }
+
         if( p_sys->i_threads >= 1 )
         {
             vlc_mutex_lock( &p_sys->lock_out );
@@ -1728,7 +1957,8 @@ static void video_release_buffer( picture_t *p_pic )
     else if( p_pic && p_pic->i_refcount > 0 ) p_pic->i_refcount--;
 }
 
-static picture_t *video_new_buffer( vlc_object_t *p_this, picture_t **pp_ring )
+static picture_t *video_new_buffer( vlc_object_t *p_this, picture_t **pp_ring,
+                                    sout_stream_sys_t *p_sys )
 {
     decoder_t *p_dec = (decoder_t *)p_this;
     picture_t *p_pic;
@@ -1748,6 +1978,32 @@ static picture_t *video_new_buffer( vlc_object_t *p_this, picture_t **pp_ring )
         if( pp_ring[i] == 0 ) break;
     }
 
+    if( i == PICTURE_RING_SIZE && p_sys->i_threads >= 1 )
+    {
+        int i_first_pic = p_sys->i_first_pic;
+
+        if( p_sys->i_first_pic != p_sys->i_last_pic )
+        {
+            /* Encoder still has stuff to encode, wait to clear-up the list */
+            while( p_sys->i_first_pic == i_first_pic )
+                msleep( 100000 );
+        }
+
+        /* Find an empty space in the picture ring buffer */
+        for( i = 0; i < PICTURE_RING_SIZE; i++ )
+        {
+            if( pp_ring[i] != 0 && pp_ring[i]->i_status == DESTROYED_PICTURE )
+            {
+                pp_ring[i]->i_status = RESERVED_PICTURE;
+                return pp_ring[i];
+            }
+        }
+        for( i = 0; i < PICTURE_RING_SIZE; i++ )
+        {
+            if( pp_ring[i] == 0 ) break;
+        }
+    }
+
     if( i == PICTURE_RING_SIZE )
     {
         msg_Err( p_this, "decoder/filter is leaking pictures, "
@@ -1788,13 +2044,14 @@ static picture_t *video_new_buffer( vlc_object_t *p_this, picture_t **pp_ring )
 static picture_t *video_new_buffer_decoder( decoder_t *p_dec )
 {
     return video_new_buffer( VLC_OBJECT(p_dec),
-                             p_dec->p_owner->pp_pics );
+                             p_dec->p_owner->pp_pics, p_dec->p_owner->p_sys );
 }
 
 static picture_t *video_new_buffer_filter( filter_t *p_filter )
 {
     return video_new_buffer( VLC_OBJECT(p_filter),
-                             p_filter->p_owner->pp_pics );
+                             p_filter->p_owner->pp_pics,
+                             p_filter->p_owner->p_sys );
 }
 
 static void video_del_buffer( vlc_object_t *p_this, picture_t *p_pic )