]> git.sesse.net Git - vlc/blobdiff - modules/stream_out/transcode.c
* modules/codec/ffmpeg/encoder.c, modules/stream_out/transcode.c: backport of 11318...
[vlc] / modules / stream_out / transcode.c
index 8841c1acb7a6998152c97ff24990ec68a87a18d4..fc3d3ba0c5d6fe195a4683bd9d6c673095877812 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 (ffmpeg-deinterlace or " \
+    "deinterlace)." )
 #define WIDTH_TEXT N_("Video width")
 #define WIDTH_LONGTEXT N_( \
     "Allows you to specify the output video width." )
 #define SCODEC_LONGTEXT N_( \
     "Allows you to specify the destination subtitles codec used for the " \
     "streaming output." )
+#define SFILTER_TEXT N_("Subpictures filter")
+#define SFILTER_LONGTEXT N_( \
+    "Allows you to specify subpictures filters used during the video " \
+    "transcoding. The subpictures produced by the filters will be overlayed " \
+    "directly onto the video." )
 
 #define THREADS_TEXT N_("Number of threads")
 #define THREADS_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 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,
@@ -140,8 +157,13 @@ 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 );
     add_integer( SOUT_CFG_PREFIX "width", 0, NULL, WIDTH_TEXT,
                  WIDTH_LONGTEXT, VLC_TRUE );
     add_integer( SOUT_CFG_PREFIX "height", 0, NULL, HEIGHT_TEXT,
@@ -156,6 +178,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,
@@ -166,26 +189,31 @@ 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_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 "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",
+    "scale", "fps", "width", "height", "deinterlace", "deinterlace-module",
+    "threads", "hurry-up", "aenc", "acodec", "ab", "samplerate", "channels",
+    "senc", "scodec", "soverlay", "sfilter",
     "audio-sync", NULL
 };
 
@@ -210,17 +238,18 @@ static int  transcode_video_encoder_open( sout_stream_t *, sout_stream_id_t *);
 static int  transcode_video_process( sout_stream_t *, sout_stream_id_t *,
                                      block_t *, block_t ** );
 
-static picture_t *video_new_buffer( decoder_t * );
-static void video_del_buffer( decoder_t *, picture_t * );
-static void video_link_picture( decoder_t *, picture_t * );
-static void video_unlink_picture( decoder_t *, picture_t * );
+static void video_del_buffer( vlc_object_t *, picture_t * );
+static picture_t *video_new_buffer_decoder( decoder_t * );
+static void video_del_buffer_decoder( decoder_t *, picture_t * );
+static void video_link_picture_decoder( decoder_t *, picture_t * );
+static void video_unlink_picture_decoder( decoder_t *, picture_t * );
+static picture_t *video_new_buffer_filter( filter_t * );
+static void video_del_buffer_filter( filter_t *, picture_t * );
 
 static int  transcode_spu_new    ( sout_stream_t *, sout_stream_id_t * );
 static void transcode_spu_close  ( sout_stream_t *, sout_stream_id_t * );
 static int  transcode_spu_process( sout_stream_t *, sout_stream_id_t *,
                                    block_t *, block_t ** );
-static subpicture_t *transcode_spu_get( sout_stream_t *, sout_stream_id_t *,
-                                        mtime_t );
 
 static int  EncoderThread( struct sout_stream_sys_t * p_sys );
 
@@ -268,7 +297,10 @@ 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_hurry_up;
 
     int             i_crop_top;
     int             i_crop_bottom;
@@ -280,16 +312,22 @@ struct sout_stream_sys_t
     char            *psz_senc;
     vlc_bool_t      b_soverlay;
     sout_cfg_t      *p_spu_cfg;
-    subpicture_t    *pp_subpics[SUBPICTURE_RING_SIZE];
-
-    /* Filters */
-    filter_t        *p_filter_blend;
+    spu_t           *p_spu;
 
     /* Sync */
-    vlc_bool_t      b_audio_sync;
+    vlc_bool_t      b_master_sync;
     mtime_t         i_master_drift;
 };
 
+struct decoder_owner_sys_t
+{
+    picture_t *pp_pics[PICTURE_RING_SIZE];
+};
+struct filter_owner_sys_t
+{
+    picture_t *pp_pics[PICTURE_RING_SIZE];
+};
+
 /*****************************************************************************
  * Open:
  *****************************************************************************/
@@ -298,7 +336,6 @@ static int Open( vlc_object_t *p_this )
     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
     sout_stream_sys_t *p_sys;
     vlc_value_t       val;
-    int               i;
 
     p_sys = vlc_object_create( p_this, sizeof( sout_stream_sys_t ) );
 
@@ -306,7 +343,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;
     }
 
@@ -388,6 +425,9 @@ 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;
 
@@ -397,6 +437,19 @@ static int Open( vlc_object_t *p_this )
     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 );
@@ -417,9 +470,12 @@ static int Open( vlc_object_t *p_this )
     }
 
     /* Subpictures transcoding parameters */
-    var_Get( p_stream, SOUT_CFG_PREFIX "senc", &val );
+    p_sys->p_spu = 0;
     p_sys->psz_senc = NULL;
     p_sys->p_spu_cfg = NULL;
+    p_sys->i_scodec = 0;
+
+    var_Get( p_stream, SOUT_CFG_PREFIX "senc", &val );
     if( val.psz_string && *val.psz_string )
     {
         char *psz_next;
@@ -430,7 +486,6 @@ static int Open( vlc_object_t *p_this )
     if( val.psz_string ) free( val.psz_string );
 
     var_Get( p_stream, SOUT_CFG_PREFIX "scodec", &val );
-    p_sys->i_scodec = 0;
     if( val.psz_string && *val.psz_string )
     {
         char fcc[4] = "    ";
@@ -441,21 +496,25 @@ 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 );
     p_sys->b_soverlay = val.b_bool;
-    p_sys->p_filter_blend = 0;
 
-    for( i = 0; i < SUBPICTURE_RING_SIZE; i++ )
+    var_Get( p_stream, SOUT_CFG_PREFIX "sfilter", &val );
+    if( val.psz_string && *val.psz_string )
     {
-        p_sys->pp_subpics[i] = 0;
+        p_sys->p_spu = spu_Create( p_stream );
+        var_Create( p_sys->p_spu, "sub-filter", VLC_VAR_STRING );
+        var_Set( p_sys->p_spu, "sub-filter", val );
+        spu_Init( p_sys->p_spu );
     }
+    if( val.psz_string ) free( val.psz_string );
 
     var_Get( p_stream, SOUT_CFG_PREFIX "audio-sync", &val );
-    p_sys->b_audio_sync = val.b_bool;
-    if( p_sys->f_fps > 0 ) p_sys->b_audio_sync = VLC_TRUE;
+    p_sys->b_master_sync = val.b_bool;
+    if( p_sys->f_fps > 0 ) p_sys->b_master_sync = VLC_TRUE;
 
     p_stream->pf_add    = Add;
     p_stream->pf_del    = Del;
@@ -503,6 +562,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;
@@ -517,15 +590,7 @@ static void Close( vlc_object_t * p_this )
     }
     if( p_sys->psz_senc ) free( p_sys->psz_senc );
 
-    if( p_sys->p_filter_blend )
-    {
-        if( p_sys->p_filter_blend->p_module )
-            module_Unneed( p_sys->p_filter_blend,
-                           p_sys->p_filter_blend->p_module );
-
-        vlc_object_detach( p_sys->p_filter_blend );
-        vlc_object_destroy( p_sys->p_filter_blend );
-    }
+    if( p_sys->p_spu ) spu_Destroy( p_sys->p_spu );
 
     vlc_object_destroy( p_sys );
 }
@@ -549,7 +614,6 @@ struct sout_stream_id_t
 
     /* Sync */
     date_t          interpolated_pts;
-    mtime_t         i_initial_pts;
 };
 
 
@@ -575,9 +639,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 */
@@ -607,11 +668,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 ) )
@@ -654,8 +732,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) )
@@ -770,6 +849,28 @@ static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
 
     if( !id->b_transcode && id->id )
     {
+        if( p_sys->b_master_sync && p_sys->i_master_drift )
+        {
+            if( p_buffer->i_dts > 0 )
+            {
+                p_buffer->i_dts -= p_sys->i_master_drift;
+                if( p_buffer->i_dts < 0 )
+                {
+                    block_Release( p_buffer );
+                    return VLC_EGENERIC;
+                }
+            }
+            if( p_buffer->i_pts > 0 )
+            {
+                p_buffer->i_pts -= p_sys->i_master_drift;
+                if( p_buffer->i_pts < 0 )
+                {
+                    block_Release( p_buffer );
+                    return VLC_EGENERIC;
+                }
+            }
+        }
+
         return p_sys->p_out->pf_send( p_sys->p_out, id->id, p_buffer );
     }
     else if( !id->b_transcode )
@@ -818,32 +919,80 @@ int audio_BitsPerSample( vlc_fourcc_t i_format )
     {
     case VLC_FOURCC('u','8',' ',' '):
     case VLC_FOURCC('s','8',' ',' '):
-        return 1;
+        return 8;
 
     case VLC_FOURCC('u','1','6','l'):
     case VLC_FOURCC('s','1','6','l'):
     case VLC_FOURCC('u','1','6','b'):
     case VLC_FOURCC('s','1','6','b'):
-        return 2;
-
+        return 16;
+
+    case VLC_FOURCC('u','2','4','l'):
+    case VLC_FOURCC('s','2','4','l'):
+    case VLC_FOURCC('u','2','4','b'):
+    case VLC_FOURCC('s','2','4','b'):
+        return 24;
+
+    case VLC_FOURCC('u','3','2','l'):
+    case VLC_FOURCC('s','3','2','l'):
+    case VLC_FOURCC('u','3','2','b'):
+    case VLC_FOURCC('s','3','2','b'):
     case VLC_FOURCC('f','l','3','2'):
     case VLC_FOURCC('f','i','3','2'):
-        return 4;
+        return 32;
+
+    case VLC_FOURCC('f','l','6','4'):
+        return 64;
     }
 
     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;
@@ -859,6 +1008,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
@@ -869,24 +1021,18 @@ 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_in.audio.i_channels );
-    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 =
-            pi_channels_maps[id->p_encoder->fmt_out.audio.i_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 = 16;
+    id->p_encoder->fmt_in.audio.i_bitspersample =
+        audio_BitsPerSample( id->p_encoder->fmt_in.i_codec );
 
     id->p_encoder->p_cfg = p_stream->p_sys->p_audio_cfg;
 
@@ -900,70 +1046,86 @@ static int transcode_audio_new( sout_stream_t *p_stream,
         return VLC_EGENERIC;
     }
     id->p_encoder->fmt_in.audio.i_format = id->p_encoder->fmt_in.i_codec;
+    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
-        {
-            msg_Dbg( p_stream, "no audio filter found" );
-            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;
-        }
+        if( id->pp_filter[id->i_filter] ) id->i_filter++;
+    }
 
-        /* Try a 2 stage conversion */
-        if( id->pp_filter[0]->fmt_out.i_codec !=
-            id->p_encoder->fmt_in.i_codec )
+    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 )
         {
-            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" );
-                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++;
+            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;
         }
     }
 
+    /* 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;
+    }
+
+    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 */
     if( id->p_encoder->fmt_out.i_codec == VLC_FOURCC( 'm','p','3',' ' ) )
         id->p_encoder->fmt_out.i_codec = VLC_FOURCC( 'm','p','g','a' );
@@ -979,10 +1141,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++ )
@@ -1007,7 +1171,7 @@ static int transcode_audio_process( sout_stream_t *p_stream,
     while( (p_audio_buf = id->p_decoder->pf_decode_audio( id->p_decoder,
                                                           &in )) )
     {
-        if( p_sys->b_audio_sync )
+        if( p_sys->b_master_sync )
         {
             mtime_t i_dts = date_Get( &id->interpolated_pts ) + 1;
             p_sys->i_master_drift = p_audio_buf->start_date - i_dts;
@@ -1061,7 +1225,7 @@ static aout_buffer_t *audio_new_buffer( decoder_t *p_dec, int i_samples )
 
     if( p_dec->fmt_out.audio.i_bitspersample )
     {
-        i_size = i_samples * p_dec->fmt_out.audio.i_bitspersample *
+        i_size = i_samples * p_dec->fmt_out.audio.i_bitspersample / 8 *
             p_dec->fmt_out.audio.i_channels;
     }
     else if( p_dec->fmt_out.audio.i_bytes_per_frame &&
@@ -1099,17 +1263,24 @@ static void audio_del_buffer( decoder_t *p_dec, aout_buffer_t *p_buffer )
 static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id )
 {
     sout_stream_sys_t *p_sys = p_stream->p_sys;
+    int i;
 
     /*
      * 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_video = 0;
-    id->p_decoder->pf_vout_buffer_new = video_new_buffer;
-    id->p_decoder->pf_vout_buffer_del = video_del_buffer;
-    id->p_decoder->pf_picture_link    = video_link_picture;
-    id->p_decoder->pf_picture_unlink  = video_unlink_picture;
+    id->p_decoder->pf_vout_buffer_new = video_new_buffer_decoder;
+    id->p_decoder->pf_vout_buffer_del = video_del_buffer_decoder;
+    id->p_decoder->pf_picture_link    = video_link_picture_decoder;
+    id->p_decoder->pf_picture_unlink  = video_unlink_picture_decoder;
+    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_cfg = p_sys->p_video_cfg;
 
     id->p_decoder->p_module =
@@ -1186,6 +1357,8 @@ static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id )
         }
     }
 
+    date_Set( &id->interpolated_pts, 0 );
+
     return VLC_SUCCESS;
 }
 
@@ -1301,7 +1474,7 @@ static int transcode_video_encoder_open( sout_stream_t *p_stream,
 static void transcode_video_close( sout_stream_t *p_stream,
                                    sout_stream_id_t *id )
 {
-    int i;
+    int i, j;
 
     if( p_stream->p_sys->i_threads >= 1 )
     {
@@ -1318,6 +1491,18 @@ static void transcode_video_close( sout_stream_t *p_stream,
     if( id->p_decoder->p_module )
         module_Unneed( id->p_decoder, id->p_decoder->p_module );
 
+    if( id->p_decoder->p_owner )
+    {
+        /* Clean-up pictures ring buffer */
+        for( i = 0; i < PICTURE_RING_SIZE; i++ )
+        {
+            if( id->p_decoder->p_owner->pp_pics[i] )
+                video_del_buffer( VLC_OBJECT(id->p_decoder),
+                                  id->p_decoder->p_owner->pp_pics[i] );
+        }
+        free( id->p_decoder->p_owner );
+    }
+
     /* Close encoder */
     if( id->p_encoder->p_module )
         module_Unneed( id->p_encoder, id->p_encoder->p_module );
@@ -1328,6 +1513,16 @@ static void transcode_video_close( sout_stream_t *p_stream,
         vlc_object_detach( id->pp_filter[i] );
         if( id->pp_filter[i]->p_module )
             module_Unneed( id->pp_filter[i], id->pp_filter[i]->p_module );
+
+        /* Clean-up pictures ring buffer */
+        for( j = 0; j < PICTURE_RING_SIZE; j++ )
+        {
+            if( id->pp_filter[i]->p_owner->pp_pics[j] )
+                video_del_buffer( VLC_OBJECT(id->pp_filter[i]),
+                                  id->pp_filter[i]->p_owner->pp_pics[j] );
+        }
+        free( id->pp_filter[i]->p_owner );
+
         vlc_object_destroy( id->pp_filter[i] );
     }
 }
@@ -1345,18 +1540,28 @@ static int transcode_video_process( sout_stream_t *p_stream,
     {
         subpicture_t *p_subpic = 0;
 
-        if( p_sys->b_audio_sync )
+        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;
             mtime_t i_master_drift = p_sys->i_master_drift;
             mtime_t i_pts;
 
-            if( !id->i_initial_pts ) id->i_initial_pts = p_pic->date;
-
             if( !i_master_drift )
             {
                 /* No audio track ? */
-                i_master_drift = id->i_initial_pts;
+                p_sys->i_master_drift = i_master_drift = p_pic->date;
             }
 
             i_pts = date_Get( &id->interpolated_pts ) + 1;
@@ -1368,24 +1573,28 @@ static int transcode_video_process( sout_stream_t *p_stream,
 
             if( i_video_drift < i_master_drift - 50000 )
             {
+#if 0
                 msg_Dbg( p_stream, "dropping frame (%i)",
                          (int)(i_video_drift - i_master_drift) );
-                return VLC_EGENERIC;
+#endif
+                p_pic->pf_release( p_pic );
+                continue;
             }
             else if( i_video_drift > i_master_drift + 50000 )
             {
+#if 0
                 msg_Dbg( p_stream, "adding frame (%i)",
                          (int)(i_video_drift - i_master_drift) );
+#endif
                 i_duplicate = 2;
             }
-
-            date_Increment( &id->interpolated_pts, 1 );
         }
 
         if( !id->p_encoder->p_module )
         {
             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;
                 return VLC_EGENERIC;
@@ -1399,16 +1608,25 @@ static int transcode_video_process( sout_stream_t *p_stream,
                 vlc_object_attach( id->pp_filter[id->i_filter], p_stream );
 
                 id->pp_filter[id->i_filter]->pf_vout_buffer_new =
-                    video_new_buffer;
+                    video_new_buffer_filter;
                 id->pp_filter[id->i_filter]->pf_vout_buffer_del =
-                    video_del_buffer;
+                    video_del_buffer_filter;
 
                 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 );
-                if( id->pp_filter[id->i_filter]->p_module ) id->i_filter++;
+                                 "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->i_filter++;
+                }
                 else
                 {
                     msg_Dbg( p_stream, "no video filter found" );
@@ -1432,22 +1650,32 @@ static int transcode_video_process( sout_stream_t *p_stream,
                 vlc_object_attach( id->pp_filter[id->i_filter], p_stream );
 
                 id->pp_filter[id->i_filter]->pf_vout_buffer_new =
-                    video_new_buffer;
+                    video_new_buffer_filter;
                 id->pp_filter[id->i_filter]->pf_vout_buffer_del =
-                    video_del_buffer;
+                    video_del_buffer_filter;
 
                 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 );
-                if( id->pp_filter[id->i_filter]->p_module ) id->i_filter++;
+                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->i_filter++;
+                }
                 else
                 {
                     msg_Dbg( p_stream, "no video filter found" );
                     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;
                     return VLC_EGENERIC;
@@ -1466,104 +1694,47 @@ static int transcode_video_process( sout_stream_t *p_stream,
          */
 
         /* Check if we have a subpicture to overlay */
-        if( p_sys->p_filter_blend )
+        if( p_sys->p_spu )
         {
-            p_subpic = transcode_spu_get( p_stream, id, p_pic->date );
+            p_subpic = spu_SortSubpictures( p_sys->p_spu, p_pic->date );
             /* TODO: get another pic */
         }
 
         /* Overlay subpicture */
         if( p_subpic )
         {
-            int i_width, i_height;
-
-            p_sys->p_filter_blend->fmt_out = id->p_encoder->fmt_in;
-            p_sys->p_filter_blend->fmt_out.video.i_visible_width =
-                p_sys->p_filter_blend->fmt_out.video.i_width;
-            p_sys->p_filter_blend->fmt_out.video.i_visible_height =
-                p_sys->p_filter_blend->fmt_out.video.i_height;
-            p_sys->p_filter_blend->fmt_out.video.i_chroma =
-                VLC_FOURCC('I','4','2','0');
+            int i_scale_width, i_scale_height;
+            video_format_t *p_fmt;
 
-            i_width = id->p_encoder->fmt_in.video.i_width;
-            i_height = id->p_encoder->fmt_in.video.i_height;
+            i_scale_width = id->p_encoder->fmt_in.video.i_width * 1000 /
+                id->p_decoder->fmt_out.video.i_width;
+            i_scale_height = id->p_encoder->fmt_in.video.i_height * 1000 /
+                id->p_decoder->fmt_out.video.i_height;
 
-            if( p_pic->i_refcount )
+            if( p_pic->i_refcount && !id->i_filter )
             {
                 /* We can't modify the picture, we need to duplicate it */
-                picture_t *p_tmp =
-                    video_new_buffer( (decoder_t *)p_sys->p_filter_blend );
+                picture_t *p_tmp = video_new_buffer_decoder( id->p_decoder );
                 if( p_tmp )
                 {
-                    int i, j;
-                    for( i = 0; i < p_pic->i_planes; i++ )
-                    {
-                        for( j = 0; j < p_pic->p[i].i_visible_lines; j++ )
-                        {
-                            memcpy( p_tmp->p[i].p_pixels + j *
-                                    p_tmp->p[i].i_pitch,
-                                    p_pic->p[i].p_pixels + j *
-                                    p_pic->p[i].i_pitch,
-                                    p_tmp->p[i].i_visible_pitch );
-                        }
-                    }
-                    p_tmp->date = p_pic->date;
-                    p_tmp->b_force = p_pic->b_force;
-                    p_tmp->i_nb_fields = p_pic->i_nb_fields;
-                    p_tmp->b_progressive = p_pic->b_progressive;
-                    p_tmp->b_top_field_first = p_pic->b_top_field_first;
+                    vout_CopyPicture( p_stream, p_tmp, p_pic );
                     p_pic->pf_release( p_pic );
                     p_pic = p_tmp;
                 }
             }
 
-            while( p_subpic != NULL )
-            {
-                subpicture_region_t *p_region = p_subpic->p_region;
+            if( id->i_filter )
+                p_fmt = &id->pp_filter[id->i_filter -1]->fmt_out.video;
+            else
+                p_fmt = &id->p_decoder->fmt_out.video;
 
-                while( p_region && p_sys->p_filter_blend &&
-                       p_sys->p_filter_blend->pf_video_blend )
-                {
-                    int i_x_offset = p_region->i_x + p_subpic->i_x;
-                    int i_y_offset = p_region->i_y + p_subpic->i_y;
-
-                    if( p_subpic->i_flags & OSD_ALIGN_BOTTOM )
-                    {
-                        i_y_offset = i_height - p_region->fmt.i_height -
-                            p_subpic->i_y;
-                    }
-                    else if ( !(p_subpic->i_flags & OSD_ALIGN_TOP) )
-                    {
-                        i_y_offset = i_height / 2 - p_region->fmt.i_height / 2;
-                    }
-
-                    if( p_subpic->i_flags & OSD_ALIGN_RIGHT )
-                    {
-                        i_x_offset = i_width - p_region->fmt.i_width -
-                            p_subpic->i_x;
-                    }
-                    else if ( !(p_subpic->i_flags & OSD_ALIGN_LEFT) )
-                    {
-                        i_x_offset = i_width / 2 - p_region->fmt.i_width / 2;
-                    }
-
-                    if( p_subpic->b_absolute )
-                    {
-                        i_x_offset = p_region->i_x + p_subpic->i_x;
-                        i_y_offset = p_region->i_y + p_subpic->i_y;
-                    }
-
-                    p_sys->p_filter_blend->fmt_in.video = p_region->fmt;
-
-                    p_sys->p_filter_blend->pf_video_blend(
-                         p_sys->p_filter_blend, p_pic, p_pic,
-                         &p_region->picture, i_x_offset, i_y_offset );
-
-                    p_region = p_region->p_next;
-                }
+            /* FIXME (shouldn't have to be done here) */
+            p_fmt->i_sar_num = p_fmt->i_aspect *
+                p_fmt->i_height / p_fmt->i_width;
+            p_fmt->i_sar_den = VOUT_ASPECT_FACTOR;
 
-                p_subpic = p_subpic->p_next;
-            }
+            spu_RenderSubpictures( p_sys->p_spu, p_fmt, p_pic, p_pic, p_subpic,
+                                   i_scale_width, i_scale_height );
         }
 
         if( p_sys->i_threads >= 1 )
@@ -1582,7 +1753,10 @@ static int transcode_video_process( sout_stream_t *p_stream,
             p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic );
             block_ChainAppend( out, p_block );
 
-            if( p_sys->b_audio_sync && i_duplicate > 1 )
+            if( p_sys->b_master_sync )
+                date_Increment( &id->interpolated_pts, 1 );
+
+            if( p_sys->b_master_sync && i_duplicate > 1 )
             {
                 mtime_t i_pts = date_Get( &id->interpolated_pts ) + 1;
                 date_Increment( &id->interpolated_pts, 1 );
@@ -1602,7 +1776,6 @@ static int EncoderThread( sout_stream_sys_t *p_sys )
 {
     sout_stream_id_t *id = p_sys->id_video;
     picture_t *p_pic;
-    int i_plane;
 
     while( !p_sys->b_die && !p_sys->b_error )
     {
@@ -1627,13 +1800,23 @@ static int EncoderThread( sout_stream_sys_t *p_sys )
         p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic );
         vlc_mutex_lock( &p_sys->lock_out );
         block_ChainAppend( &p_sys->p_buffers, p_block );
-        vlc_mutex_unlock( &p_sys->lock_out );
 
-        for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ )
+        if( p_sys->b_master_sync )
+            date_Increment( &id->interpolated_pts, 1 );
+
+#if 0
+        if( p_sys->b_master_sync && i_duplicate > 1 )
         {
-            free( p_pic->p[i_plane].p_pixels );
+            mtime_t i_pts = date_Get( &id->interpolated_pts ) + 1;
+            date_Increment( &id->interpolated_pts, 1 );
+            p_pic->date = i_pts;
+            p_block = id->p_encoder->pf_encode_video(id->p_encoder, p_pic);
+            block_ChainAppend( &p_sys->p_buffers, p_block );
         }
-        free( p_pic );
+#endif
+        vlc_mutex_unlock( &p_sys->lock_out );
+
+        p_pic->pf_release( p_pic );
     }
 
     while( p_sys->i_last_pic != p_sys->i_first_pic )
@@ -1641,11 +1824,7 @@ static int EncoderThread( sout_stream_sys_t *p_sys )
         p_pic = p_sys->pp_pics[p_sys->i_first_pic++];
         p_sys->i_first_pic %= PICTURE_RING_SIZE;
 
-        for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ )
-        {
-            free( p_pic->p[i_plane].p_pixels );
-        }
-        free( p_pic );
+        p_pic->pf_release( p_pic );
     }
 
     block_ChainRelease( p_sys->p_buffers );
@@ -1655,22 +1834,52 @@ static int EncoderThread( sout_stream_sys_t *p_sys )
 
 struct picture_sys_t
 {
-    decoder_t *p_dec;
+    vlc_object_t *p_owner;
 };
 
 static void video_release_buffer( picture_t *p_pic )
 {
     if( p_pic && !p_pic->i_refcount && p_pic->pf_release && p_pic->p_sys )
     {
-        video_del_buffer( p_pic->p_sys->p_dec, p_pic );
+        video_del_buffer_decoder( (decoder_t *)p_pic->p_sys->p_owner, p_pic );
     }
     else if( p_pic && p_pic->i_refcount > 0 ) p_pic->i_refcount--;
 }
 
-static picture_t *video_new_buffer( decoder_t *p_dec )
+static picture_t *video_new_buffer( vlc_object_t *p_this, picture_t **pp_ring )
 {
-    picture_t *p_pic = malloc( sizeof(picture_t) );
+    decoder_t *p_dec = (decoder_t *)p_this;
+    picture_t *p_pic;
+    int i;
+
+    /* 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, "
+                 "resetting its ring buffer" );
+
+        for( i = 0; i < PICTURE_RING_SIZE; i++ )
+        {
+            pp_ring[i]->pf_release( pp_ring[i] );
+        }
+
+        i = 0;
+    }
 
+    p_pic = malloc( sizeof(picture_t) );
     p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec;
     vout_AllocatePicture( VLC_OBJECT(p_dec), p_pic,
                           p_dec->fmt_out.video.i_chroma,
@@ -1686,24 +1895,51 @@ static picture_t *video_new_buffer( decoder_t *p_dec )
 
     p_pic->pf_release = video_release_buffer;
     p_pic->p_sys = malloc( sizeof(picture_sys_t) );
-    p_pic->p_sys->p_dec = p_dec;
+    p_pic->p_sys->p_owner = p_this;
+    p_pic->i_status = RESERVED_PICTURE;
+
+    pp_ring[i] = p_pic;
 
     return p_pic;
 }
 
-static void video_del_buffer( decoder_t *p_dec, picture_t *p_pic )
+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 );
+}
+
+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 );
+}
+
+static void video_del_buffer( vlc_object_t *p_this, picture_t *p_pic )
 {
-    if( p_pic && p_pic->p_data ) free( p_pic->p_data );
+    if( p_pic && p_pic->p_data_orig ) free( p_pic->p_data_orig );
     if( p_pic && p_pic->p_sys ) free( p_pic->p_sys );
     if( p_pic ) free( p_pic );
 }
 
-static void video_link_picture( decoder_t *p_dec, picture_t *p_pic )
+static void video_del_buffer_decoder( decoder_t *p_decoder, picture_t *p_pic )
+{
+    p_pic->i_refcount = 0;
+    p_pic->i_status = DESTROYED_PICTURE;
+}
+
+static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
+{
+    p_pic->i_refcount = 0;
+    p_pic->i_status = DESTROYED_PICTURE;
+}
+
+static void video_link_picture_decoder( decoder_t *p_dec, picture_t *p_pic )
 {
     p_pic->i_refcount++;
 }
 
-static void video_unlink_picture( decoder_t *p_dec, picture_t *p_pic )
+static void video_unlink_picture_decoder( decoder_t *p_dec, picture_t *p_pic )
 {
     video_release_buffer( p_pic );
 }
@@ -1725,6 +1961,7 @@ static int transcode_spu_new( sout_stream_t *p_stream, sout_stream_id_t *id )
     /* Initialization of decoder structures */
     id->p_decoder->pf_spu_buffer_new = spu_new_buffer;
     id->p_decoder->pf_spu_buffer_del = spu_del_buffer;
+    id->p_decoder->p_owner = (decoder_owner_sys_t *)p_stream;
     //id->p_decoder->p_cfg = p_sys->p_spu_cfg;
 
     id->p_decoder->p_module =
@@ -1758,17 +1995,11 @@ static int transcode_spu_new( sout_stream_t *p_stream, sout_stream_id_t *id )
             return VLC_EGENERIC;
         }
     }
-    else
+
+    if( !p_sys->p_spu )
     {
-        p_sys->p_filter_blend =
-            vlc_object_create( p_stream, VLC_OBJECT_FILTER );
-        vlc_object_attach( p_sys->p_filter_blend, p_stream );
-        p_sys->p_filter_blend->fmt_out.video.i_chroma =
-            VLC_FOURCC('I','4','2','0');
-        p_sys->p_filter_blend->fmt_in.video.i_chroma =
-            VLC_FOURCC('Y','U','V','A');
-        p_sys->p_filter_blend->p_module =
-            module_Need( p_sys->p_filter_blend, "video blending", 0, 0 );
+        p_sys->p_spu = spu_Create( p_stream );
+        spu_Init( p_sys->p_spu );
     }
 
     return VLC_SUCCESS;
@@ -1776,9 +2007,6 @@ static int transcode_spu_new( sout_stream_t *p_stream, sout_stream_id_t *id )
 
 static void transcode_spu_close( sout_stream_t *p_stream, sout_stream_id_t *id)
 {
-    sout_stream_sys_t *p_sys = p_stream->p_sys;
-    int i;
-
     /* Close decoder */
     if( id->p_decoder->p_module )
         module_Unneed( id->p_decoder, id->p_decoder->p_module );
@@ -1786,15 +2014,6 @@ static void transcode_spu_close( sout_stream_t *p_stream, sout_stream_id_t *id)
     /* Close encoder */
     if( id->p_encoder->p_module )
         module_Unneed( id->p_encoder, id->p_encoder->p_module );
-
-    /* Free subpictures */
-    for( i = 0; i < SUBPICTURE_RING_SIZE; i++ )
-    {
-        if( !p_sys->pp_subpics[i] ) continue;
-
-        spu_del_buffer( id->p_decoder, p_sys->pp_subpics[i] );
-        p_sys->pp_subpics[i] = NULL;
-    }
 }
 
 static int transcode_spu_process( sout_stream_t *p_stream,
@@ -1806,26 +2025,19 @@ static int transcode_spu_process( sout_stream_t *p_stream,
     *out = NULL;
 
     p_subpic = id->p_decoder->pf_decode_sub( id->p_decoder, &in );
-    if( p_subpic && p_sys->b_soverlay )
-    {
-        int i;
+    if( !p_subpic ) return VLC_EGENERIC;
 
-        /* Find a free slot in our supictures ring buffer */
-        for( i = 0; i < SUBPICTURE_RING_SIZE; i++ )
-        {
-            if( !p_sys->pp_subpics[i] )
-            {
-                p_sys->pp_subpics[i] = p_subpic;
-                break;
-            }
-        }
-        if( i == SUBPICTURE_RING_SIZE )
-        {
-            spu_del_buffer( id->p_decoder, p_subpic );
-        }
+    if( p_sys->b_master_sync && p_sys->i_master_drift )
+    {
+        p_subpic->i_start -= p_sys->i_master_drift;
+        if( p_subpic->i_stop ) p_subpic->i_stop -= p_sys->i_master_drift;
     }
 
-    if(  p_subpic && !p_sys->b_soverlay )
+    if( p_sys->b_soverlay )
+    {
+        spu_DisplaySubpicture( p_sys->p_spu, p_subpic );
+    }
+    else
     {
         block_t *p_block;
 
@@ -1842,78 +2054,14 @@ static int transcode_spu_process( sout_stream_t *p_stream,
     return VLC_EGENERIC;
 }
 
-static subpicture_t *transcode_spu_get( sout_stream_t *p_stream,
-                                        sout_stream_id_t *id,
-                                        mtime_t display_date )
-{
-    sout_stream_sys_t *p_sys = p_stream->p_sys;
-    subpicture_t *p_subpic = 0;
-    subpicture_t *p_ephemer = 0;
-    subpicture_t **pp_subpic = &p_subpic;
-    int i;
-
-    /* Find current subpictures and remove old ones */
-    for( i = 0; i < SUBPICTURE_RING_SIZE; i++ )
-    {
-        if( !p_sys->pp_subpics[i] ) continue;
-
-        if( !p_sys->pp_subpics[i]->b_ephemer &&
-            p_sys->pp_subpics[i]->i_stop < display_date )
-        {
-            spu_del_buffer( id->p_decoder, p_sys->pp_subpics[i] );
-            p_sys->pp_subpics[i] = NULL;
-            continue;
-        }
-
-        if( p_sys->pp_subpics[i]->i_start > display_date ) continue;
-
-        if( p_sys->pp_subpics[i]->b_ephemer && !p_ephemer )
-        {
-            p_ephemer = p_sys->pp_subpics[i];
-        }
-        else if( p_sys->pp_subpics[i]->b_ephemer )
-        {
-            if( p_ephemer->i_start < p_sys->pp_subpics[i]->i_start )
-            {
-                subpicture_t tmp;
-                tmp = *p_ephemer;
-                *p_ephemer = *p_sys->pp_subpics[i];
-                *p_sys->pp_subpics[i] = tmp;
-            }
-
-            spu_del_buffer( id->p_decoder, p_sys->pp_subpics[i] );
-            p_sys->pp_subpics[i] = NULL;
-            continue;
-        }
-
-        /* Add subpicture to the list */
-        *pp_subpic = p_sys->pp_subpics[i];
-        pp_subpic = &p_sys->pp_subpics[i]->p_next;
-    }
-
-    return p_subpic;
-}
-
 static subpicture_t *spu_new_buffer( decoder_t *p_dec )
 {
-    subpicture_t *p_subpic = (subpicture_t *)malloc(sizeof(subpicture_t));
-    memset( p_subpic, 0, sizeof(subpicture_t) );
-    p_subpic->b_absolute = VLC_TRUE;
-
-    p_subpic->pf_create_region = __spu_CreateRegion;
-    p_subpic->pf_destroy_region = __spu_DestroyRegion;
-
-    return p_subpic;
+    sout_stream_t *p_stream = (sout_stream_t *)p_dec->p_owner;
+    return spu_CreateSubpicture( p_stream->p_sys->p_spu );
 }
 
 static void spu_del_buffer( decoder_t *p_dec, subpicture_t *p_subpic )
 {
-    while( p_subpic->p_region )
-    {
-        subpicture_region_t *p_region = p_subpic->p_region;
-        p_subpic->p_region = p_region->p_next;
-        p_subpic->pf_destroy_region( VLC_OBJECT(p_dec), p_region );
-    }
-
-    free( p_subpic );
+    sout_stream_t *p_stream = (sout_stream_t *)p_dec->p_owner;
+    spu_DestroySubpicture( p_stream->p_sys->p_spu, p_subpic );
 }