]> git.sesse.net Git - vlc/blobdiff - modules/stream_out/transcode.c
* modules/stream_out/transcode.c: bug fix for subtitles transcoding.
[vlc] / modules / stream_out / transcode.c
index d96da236d7c3de4652870ae8731a75ec75053f99..9316d66af3a8a4cc6900bf17c8532d2bb4867e96 100644 (file)
 #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_( \
@@ -173,6 +178,8 @@ vlc_module_begin();
                 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_integer( SOUT_CFG_PREFIX "threads", 0, NULL, THREADS_TEXT,
                  THREADS_LONGTEXT, VLC_TRUE );
@@ -185,7 +192,7 @@ 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",
+    "senc", "scodec", "soverlay", "sfilter",
     "audio-sync", NULL
 };
 
@@ -210,17 +217,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 );
 
@@ -280,16 +288,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;
     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 +312,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 ) );
 
@@ -417,9 +430,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 +446,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] = "    ";
@@ -446,12 +461,16 @@ static int Open( vlc_object_t *p_this )
 
     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;
@@ -517,15 +536,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 +560,6 @@ struct sout_stream_id_t
 
     /* Sync */
     date_t          interpolated_pts;
-    mtime_t         i_initial_pts;
 };
 
 
@@ -818,17 +828,30 @@ 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;
@@ -872,10 +895,16 @@ static int transcode_audio_new( sout_stream_t *p_stream,
     /* 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];
+               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 );
@@ -883,10 +912,11 @@ static int transcode_audio_new( sout_stream_t *p_stream,
     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_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,6 +930,8 @@ 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 !=
@@ -918,7 +950,9 @@ static int transcode_audio_new( sout_stream_t *p_stream,
         if( id->pp_filter[0]->p_module ) id->i_filter++;
         else
         {
-            msg_Dbg( p_stream, "no audio filter found" );
+            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 );
@@ -928,6 +962,9 @@ static int transcode_audio_new( sout_stream_t *p_stream,
             return VLC_EGENERIC;
         }
 
+        id->pp_filter[0]->fmt_out.audio.i_bitspersample = 
+            audio_BitsPerSample( id->pp_filter[0]->fmt_out.i_codec );
+
         /* Try a 2 stage conversion */
         if( id->pp_filter[0]->fmt_out.i_codec !=
             id->p_encoder->fmt_in.i_codec )
@@ -946,7 +983,9 @@ static int transcode_audio_new( sout_stream_t *p_stream,
                 id->pp_filter[1]->fmt_out.i_codec !=
                   id->p_encoder->fmt_in.i_codec )
             {
-                msg_Dbg( p_stream, "no audio filter found" );
+                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] );
@@ -1061,7 +1100,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,6 +1138,7 @@ 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
@@ -1106,10 +1146,13 @@ static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id )
 
     /* Initialization of decoder structures */
     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 +1229,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 +1346,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 +1363,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 +1385,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] );
     }
 }
@@ -1344,6 +1411,7 @@ static int transcode_video_process( sout_stream_t *p_stream,
     while( (p_pic = id->p_decoder->pf_decode_video( id->p_decoder, &in )) )
     {
         subpicture_t *p_subpic = 0;
+        mtime_t i_pic_date = p_pic->date;
 
         if( p_sys->b_audio_sync )
         {
@@ -1351,12 +1419,10 @@ static int transcode_video_process( sout_stream_t *p_stream,
             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,18 +1434,20 @@ 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) );
+#endif
                 return VLC_EGENERIC;
             }
             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 )
@@ -1388,6 +1456,7 @@ static int transcode_video_process( sout_stream_t *p_stream,
             {
                 transcode_video_close( p_stream, id );
                 id->b_transcode = VLC_FALSE;
+                return VLC_EGENERIC;
             }
 
             /* Deinterlace */
@@ -1398,16 +1467,24 @@ 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_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++;
+                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" );
@@ -1431,21 +1508,33 @@ 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_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] );
+
+                    transcode_video_close( p_stream, id );
+                    id->b_transcode = VLC_FALSE;
+                    return VLC_EGENERIC;
                 }
             }
         }
@@ -1461,75 +1550,42 @@ 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, i_pic_date );
             /* TODO: get another pic */
         }
 
         /* Overlay subpicture */
         if( p_subpic )
         {
-            int i_width, i_height;
+            int i_scale_width, i_scale_height;
+            video_format_t *p_fmt;
 
-            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');
+            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;
 
-            i_width = id->p_encoder->fmt_in.video.i_width;
-            i_height = id->p_encoder->fmt_in.video.i_height;
-
-            while( p_subpic != NULL )
+            if( p_pic->i_refcount && !id->i_filter )
             {
-                subpicture_region_t *p_region = p_subpic->p_region;
-
-                while( p_region && p_sys->p_filter_blend &&
-                       p_sys->p_filter_blend->pf_video_blend )
+                /* We can't modify the picture, we need to duplicate it */
+                picture_t *p_tmp = video_new_buffer_decoder( id->p_decoder );
+                if( p_tmp )
                 {
-                    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;
+                    vout_CopyPicture( p_stream, p_tmp, p_pic );
+                    p_pic->pf_release( p_pic );
+                    p_pic = p_tmp;
                 }
-
-                p_subpic = p_subpic->p_next;
             }
+
+            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;
+
+            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 )
@@ -1548,6 +1604,9 @@ 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 )
+                date_Increment( &id->interpolated_pts, 1 );
+
             if( p_sys->b_audio_sync && i_duplicate > 1 )
             {
                 mtime_t i_pts = date_Get( &id->interpolated_pts ) + 1;
@@ -1621,22 +1680,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,
@@ -1652,24 +1741,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 )
 {
-    if( p_pic && p_pic->p_data ) free( p_pic->p_data );
+    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_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 );
 }
@@ -1691,6 +1807,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 =
@@ -1724,17 +1841,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;
@@ -1742,9 +1853,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 );
@@ -1752,15 +1860,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,
@@ -1774,24 +1873,10 @@ static int transcode_spu_process( sout_stream_t *p_stream,
     p_subpic = id->p_decoder->pf_decode_sub( id->p_decoder, &in );
     if( p_subpic && p_sys->b_soverlay )
     {
-        int i;
-
-        /* 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 );
-        }
+        spu_DisplaySubpicture( p_sys->p_spu, p_subpic );
     }
 
-    if(  p_subpic && !p_sys->b_soverlay )
+    if( p_subpic && !p_sys->b_soverlay )
     {
         block_t *p_block;
 
@@ -1808,78 +1893,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 );
 }