]> git.sesse.net Git - vlc/blobdiff - src/video_output/vout_subpictures.c
Revert "vimeo: use avformat demuxer"
[vlc] / src / video_output / vout_subpictures.c
index fd255f5f6e65bf16a06ad8054f690fd92be21bda..dc21fa78f26c5d4c63b90133568914add9a1f055 100644 (file)
@@ -91,13 +91,6 @@ struct spu_private_t
     mtime_t i_last_sort_date;
 };
 
-/* Subpicture rendered flag
- * FIXME ? it could be moved to private ? */
-#define SUBPICTURE_RENDERED  (0x1000)
-#if SUBPICTURE_RENDERED < SUBPICTURE_ALIGN_MASK
-#   error SUBPICTURE_RENDERED too low
-#endif
-
 /*****************************************************************************
  * heap managment
  *****************************************************************************/
@@ -206,17 +199,12 @@ static int spu_get_attachments( filter_t *p_filter,
     return i_ret;
 }
 
-static void SpuRenderCreateAndLoadText( spu_t *p_spu )
+static filter_t *SpuRenderCreateAndLoadText( spu_t *p_spu )
 {
-    filter_t *p_text;
-
-    assert( !p_spu->p->p_text );
-
-    p_spu->p->p_text =
-    p_text        = vlc_custom_create( p_spu, sizeof(filter_t),
-                                       VLC_OBJECT_GENERIC, "spu text" );
+    filter_t *p_text = vlc_custom_create( p_spu, sizeof(*p_text),
+                                          VLC_OBJECT_GENERIC, "spu text" );
     if( !p_text )
-        return;
+        return NULL;
 
     p_text->p_owner = xmalloc( sizeof(*p_text->p_owner) );
     p_text->p_owner->p_spu = p_spu;
@@ -232,35 +220,22 @@ static void SpuRenderCreateAndLoadText( spu_t *p_spu )
     p_text->pf_get_attachments = spu_get_attachments;
 
     vlc_object_attach( p_text, p_spu );
-
-    /* FIXME TOCHECK shouldn't module_need( , , psz_modulename, false ) do the
-     * same than these 2 calls ? */
-    char *psz_modulename = var_CreateGetString( p_spu, "text-renderer" );
-    if( psz_modulename && *psz_modulename )
-    {
-        p_text->p_module = module_need( p_text, "text renderer",
-                                        psz_modulename, true );
-    }
-    free( psz_modulename );
-
-    if( !p_text->p_module )
-        p_text->p_module = module_need( p_text, "text renderer", NULL, false );
+    p_text->p_module = module_need( p_text, "text renderer", "$text-renderer", false );
 
     /* Create a few variables used for enhanced text rendering */
-    var_Create( p_text, "spu-duration", VLC_VAR_TIME );
     var_Create( p_text, "spu-elapsed", VLC_VAR_TIME );
     var_Create( p_text, "text-rerender", VLC_VAR_BOOL );
     var_Create( p_text, "scale", VLC_VAR_INTEGER );
+
+    return p_text;
 }
 
-static filter_t *CreateAndLoadScale( vlc_object_t *p_obj,
-                                     vlc_fourcc_t i_src_chroma, vlc_fourcc_t i_dst_chroma,
-                                     bool b_resize )
+static filter_t *SpuRenderCreateAndLoadScale( vlc_object_t *p_obj,
+                                              vlc_fourcc_t i_src_chroma, vlc_fourcc_t i_dst_chroma,
+                                              bool b_resize )
 {
-    filter_t *p_scale;
-
-    p_scale = vlc_custom_create( p_obj, sizeof(filter_t),
-                                 VLC_OBJECT_GENERIC, "scale" );
+    filter_t *p_scale = vlc_custom_create( p_obj, sizeof(*p_scale),
+                                           VLC_OBJECT_GENERIC, "scale" );
     if( !p_scale )
         return NULL;
 
@@ -282,30 +257,17 @@ static filter_t *CreateAndLoadScale( vlc_object_t *p_obj,
 
     return p_scale;
 }
-static void SpuRenderCreateAndLoadScale( spu_t *p_spu )
-{
-    assert( !p_spu->p->p_scale );
-    assert( !p_spu->p->p_scale_yuvp );
-    /* XXX p_spu->p_scale is used for all conversion/scaling except yuvp to
-     * yuva/rgba */
-    p_spu->p->p_scale = CreateAndLoadScale( VLC_OBJECT(p_spu),
-                                            VLC_CODEC_YUVA, VLC_CODEC_YUVA, true );
-    /* This one is used for YUVP to YUVA/RGBA without scaling
-     * FIXME rename it */
-    p_spu->p->p_scale_yuvp = CreateAndLoadScale( VLC_OBJECT(p_spu),
-                                                 VLC_CODEC_YUVP, VLC_CODEC_YUVA, false );
-}
 
 static void SpuRenderText( spu_t *p_spu, bool *pb_rerender_text,
-                           subpicture_t *p_subpic, subpicture_region_t *p_region,
-                           int i_min_scale_ratio, mtime_t render_date )
+                           subpicture_region_t *p_region,
+                           mtime_t render_date )
 {
     filter_t *p_text = p_spu->p->p_text;
 
     assert( p_region->fmt.i_chroma == VLC_CODEC_TEXT );
 
     if( !p_text || !p_text->p_module )
-        goto exit;
+        return;
 
     /* Setup 3 variables which can be used to render
      * time-dependent text (and effects). The first indicates
@@ -326,10 +288,8 @@ static void SpuRenderText( spu_t *p_spu, bool *pb_rerender_text,
      * least show up on screen, but the effect won't change
      * the text over time.
      */
-    var_SetTime( p_text, "spu-duration", p_subpic->i_stop - p_subpic->i_start );
     var_SetTime( p_text, "spu-elapsed", render_date );
     var_SetBool( p_text, "text-rerender", false );
-    var_SetInteger( p_text, "scale", i_min_scale_ratio );
 
     if( p_text->pf_render_html && p_region->psz_html )
     {
@@ -340,9 +300,6 @@ static void SpuRenderText( spu_t *p_spu, bool *pb_rerender_text,
         p_text->pf_render_text( p_text, p_region, p_region );
     }
     *pb_rerender_text = var_GetBool( p_text, "text-rerender" );
-
-exit:
-    p_region->i_align |= SUBPICTURE_RENDERED;
 }
 
 /**
@@ -369,7 +326,7 @@ static spu_scale_t spu_scale_unit( void )
 {
     return spu_scale_create( SCALE_UNIT, SCALE_UNIT );
 }
-static spu_scale_t spu_scale_createq( int wn, int wd, int hn, int hd )
+static spu_scale_t spu_scale_createq( int64_t wn, int64_t wd, int64_t hn, int64_t hd )
 {
     return spu_scale_create( wn * SCALE_UNIT / wd,
                              hn * SCALE_UNIT / hd );
@@ -530,45 +487,40 @@ static void SpuRegionPlace( int *pi_x, int *pi_y,
                             const subpicture_t *p_subpic,
                             const subpicture_region_t *p_region )
 {
-    const int i_delta_x = p_region->i_x;
-    const int i_delta_y = p_region->i_y;
-    int i_x, i_y;
-
     assert( p_region->i_x != INT_MAX && p_region->i_y != INT_MAX );
-    if( p_region->i_align & SUBPICTURE_ALIGN_TOP )
-    {
-        i_y = i_delta_y;
-    }
-    else if( p_region->i_align & SUBPICTURE_ALIGN_BOTTOM )
-    {
-        i_y = p_subpic->i_original_picture_height - p_region->fmt.i_height - i_delta_y;
-    }
-    else
-    {
-        i_y = p_subpic->i_original_picture_height / 2 - p_region->fmt.i_height / 2;
-    }
-
-    if( p_region->i_align & SUBPICTURE_ALIGN_LEFT )
-    {
-        i_x = i_delta_x;
-    }
-    else if( p_region->i_align & SUBPICTURE_ALIGN_RIGHT )
+    if( p_subpic->b_absolute )
     {
-        i_x = p_subpic->i_original_picture_width - p_region->fmt.i_width - i_delta_x;
+        *pi_x = p_region->i_x;
+        *pi_y = p_region->i_y;
     }
     else
     {
-        i_x = p_subpic->i_original_picture_width / 2 - p_region->fmt.i_width / 2;
-    }
+        if( p_region->i_align & SUBPICTURE_ALIGN_TOP )
+        {
+            *pi_y = p_region->i_y;
+        }
+        else if( p_region->i_align & SUBPICTURE_ALIGN_BOTTOM )
+        {
+            *pi_y = p_subpic->i_original_picture_height - p_region->fmt.i_height - p_region->i_y;
+        }
+        else
+        {
+            *pi_y = p_subpic->i_original_picture_height / 2 - p_region->fmt.i_height / 2;
+        }
 
-    if( p_subpic->b_absolute )
-    {
-        i_x = i_delta_x;
-        i_y = i_delta_y;
+        if( p_region->i_align & SUBPICTURE_ALIGN_LEFT )
+        {
+            *pi_x = p_region->i_x;
+        }
+        else if( p_region->i_align & SUBPICTURE_ALIGN_RIGHT )
+        {
+            *pi_x = p_subpic->i_original_picture_width - p_region->fmt.i_width - p_region->i_x;
+        }
+        else
+        {
+            *pi_x = p_subpic->i_original_picture_width / 2 - p_region->fmt.i_width / 2;
+        }
     }
-
-    *pi_x = i_x;
-    *pi_y = i_y;
 }
 
 /**
@@ -606,15 +558,8 @@ static int SubpictureCmp( const void *s0, const void *s1 )
     return r;
 }
 
-static void SubpictureChain( subpicture_t **pp_head, subpicture_t *p_subpic )
-{
-    p_subpic->p_next = *pp_head;
-
-    *pp_head = p_subpic;
-}
-
 /*****************************************************************************
- * SpuSortSubpictures: find the subpictures to display
+ * SpuSelectSubpictures: find the subpictures to display
  *****************************************************************************
  * This function parses all subpictures and decides which ones need to be
  * displayed. If no picture has been selected, display_date will depend on
@@ -623,13 +568,17 @@ static void SubpictureChain( subpicture_t **pp_head, subpicture_t *p_subpic )
  * to be removed if a newer one is available), which makes it a lot
  * more difficult to guess if a subpicture has to be rendered or not.
  *****************************************************************************/
-static subpicture_t *SpuSortSubpictures( spu_t *p_spu,
-                                         mtime_t render_subtitle_date,
-                                         mtime_t render_osd_date,
-                                         bool b_subtitle_only )
+static void SpuSelectSubpictures( spu_t *p_spu,
+                                  unsigned int *pi_subpicture,
+                                  subpicture_t **pp_subpicture,
+                                  mtime_t render_subtitle_date,
+                                  mtime_t render_osd_date,
+                                  bool b_subtitle_only )
 {
     spu_private_t *p_sys = p_spu->p;
-    subpicture_t *p_subpic = NULL;
+
+    /* */
+    *pi_subpicture = 0;
 
     /* Create a list of channels */
     int pi_channel[VOUT_MAX_SUBPICTURES];
@@ -651,8 +600,7 @@ static subpicture_t *SpuSortSubpictures( spu_t *p_spu,
             pi_channel[i_channel_count++] = i_channel;
     }
 
-    /* We get an easily parsable chained list of subpictures which
-     * ends with NULL since p_subpic was initialized to NULL. */
+    /* Fill up the pp_subpicture arrays with relevent pictures */
     for( int i = 0; i < i_channel_count; i++ )
     {
         const int i_channel = pi_channel[i];
@@ -750,13 +698,11 @@ static subpicture_t *SpuSortSubpictures( spu_t *p_spu,
             if( b_rejet )
                 SpuHeapDeleteSubpicture( &p_sys->heap, p_current );
             else
-                SubpictureChain( &p_subpic, p_current );
+                pp_subpicture[(*pi_subpicture)++] = p_current;
         }
     }
 
     p_sys->i_last_sort_date = render_subtitle_date;
-
-    return p_subpic;
 }
 
 
@@ -769,6 +715,7 @@ static void SpuRenderRegion( spu_t *p_spu,
                              subpicture_region_t **pp_dst, spu_area_t *p_dst_area,
                              subpicture_t *p_subpic, subpicture_region_t *p_region,
                              const spu_scale_t scale_size,
+                             const vlc_fourcc_t *p_chroma_list,
                              const video_format_t *p_fmt,
                              const spu_area_t *p_subtitle_area, int i_subtitle_area,
                              mtime_t render_date )
@@ -776,8 +723,7 @@ static void SpuRenderRegion( spu_t *p_spu,
     spu_private_t *p_sys = p_spu->p;
 
     video_format_t fmt_original = p_region->fmt;
-    bool b_rerender_text = false;
-    bool b_restore_format = false;
+    bool b_restore_text = false;
     int i_x_offset;
     int i_y_offset;
 
@@ -791,10 +737,8 @@ static void SpuRenderRegion( spu_t *p_spu,
     /* Render text region */
     if( p_region->fmt.i_chroma == VLC_CODEC_TEXT )
     {
-        const int i_min_scale_ratio = SCALE_UNIT; /* FIXME what is the right value? (scale_size is not) */
-        SpuRenderText( p_spu, &b_rerender_text, p_subpic, p_region,
-                       i_min_scale_ratio, render_date );
-        b_restore_format = b_rerender_text;
+        SpuRenderText( p_spu, &b_restore_text, p_region,
+                       render_date );
 
         /* Check if the rendering has failed ... */
         if( p_region->fmt.i_chroma == VLC_CODEC_TEXT )
@@ -810,7 +754,6 @@ static void SpuRenderRegion( spu_t *p_spu,
     const bool b_force_crop    = b_force_palette && p_sys->b_force_crop;
     bool b_changed_palette     = false;
 
-
     /* Compute the margin which is expressed in destination pixel unit
      * The margin is applied only to subtitle and when no forced crop is
      * requested (dvd menu) */
@@ -881,11 +824,18 @@ static void SpuRenderRegion( spu_t *p_spu,
     region_fmt = p_region->fmt;
     p_region_picture = p_region->p_picture;
 
+    bool b_convert_chroma = true;
+    for( int i = 0; p_chroma_list[i] && b_convert_chroma; i++ )
+    {
+        if( region_fmt.i_chroma == p_chroma_list[i] )
+            b_convert_chroma = false;
+    }
 
     /* Scale from rendered size to destination size */
     if( p_sys->p_scale && p_sys->p_scale->p_module &&
         ( !b_using_palette || ( p_sys->p_scale_yuvp && p_sys->p_scale_yuvp->p_module ) ) &&
-        ( scale_size.w != SCALE_UNIT || scale_size.h != SCALE_UNIT || b_using_palette ) )
+        ( scale_size.w != SCALE_UNIT || scale_size.h != SCALE_UNIT ||
+          b_using_palette || b_convert_chroma) )
     {
         const unsigned i_dst_width  = spu_scale_w( p_region->fmt.i_width, scale_size );
         const unsigned i_dst_height = spu_scale_h( p_region->fmt.i_height, scale_size );
@@ -905,6 +855,9 @@ static void SpuRenderRegion( spu_t *p_spu,
             if( b_changed_palette )
                 b_changed = true;
 
+            if( b_convert_chroma && p_private->fmt.i_chroma != p_chroma_list[0] )
+                b_changed = true;
+
             if( b_changed )
             {
                 subpicture_region_private_Delete( p_private );
@@ -927,9 +880,8 @@ static void SpuRenderRegion( spu_t *p_spu,
 
                 p_scale_yuvp->fmt_in.video = p_region->fmt;
 
-                /* TODO converting to RGBA for RGB video output is better */
                 p_scale_yuvp->fmt_out.video = p_region->fmt;
-                p_scale_yuvp->fmt_out.video.i_chroma = VLC_CODEC_YUVA;
+                p_scale_yuvp->fmt_out.video.i_chroma = p_chroma_list[0];
 
                 p_picture = p_scale_yuvp->pf_video_filter( p_scale_yuvp, p_picture );
                 if( !p_picture )
@@ -944,10 +896,14 @@ static void SpuRenderRegion( spu_t *p_spu,
             /* Conversion(except from YUVP)/Scaling */
             if( p_picture &&
                 ( p_picture->format.i_width != i_dst_width ||
-                  p_picture->format.i_height != i_dst_height ) )
+                  p_picture->format.i_height != i_dst_height ||
+                  ( b_convert_chroma && !b_using_palette ) ) )
             {
                 p_scale->fmt_in.video = p_picture->format;
                 p_scale->fmt_out.video = p_picture->format;
+                if( b_convert_chroma )
+                    p_scale->fmt_out.i_codec        =
+                    p_scale->fmt_out.video.i_chroma = p_chroma_list[0];
 
                 p_scale->fmt_out.video.i_width = i_dst_width;
                 p_scale->fmt_out.video.i_height = i_dst_height;
@@ -1034,6 +990,8 @@ static void SpuRenderRegion( spu_t *p_spu,
         p_dst->i_x       = i_x_offset;
         p_dst->i_y       = i_y_offset;
         p_dst->i_align   = 0;
+        if( p_dst->p_picture )
+            picture_Release( p_dst->p_picture );
         p_dst->p_picture = picture_Hold( p_region_picture );
         int i_fade_alpha = 255;
         if( p_subpic->b_fade )
@@ -1049,7 +1007,7 @@ static void SpuRenderRegion( spu_t *p_spu,
     }
 
 exit:
-    if( b_rerender_text )
+    if( b_restore_text )
     {
         /* Some forms of subtitles need to be re-rendered more than
          * once, eg. karaoke. We therefore restore the region to its
@@ -1066,68 +1024,53 @@ exit:
             subpicture_region_private_Delete( p_region->p_private );
             p_region->p_private = NULL;
         }
-        p_region->i_align &= ~SUBPICTURE_RENDERED;
-    }
-    if( b_restore_format )
         p_region->fmt = fmt_original;
+    }
 }
 
 /**
  * This function renders all sub picture units in the list.
  */
 static subpicture_t *SpuRenderSubpictures( spu_t *p_spu,
+                                           unsigned int i_subpicture,
+                                           subpicture_t **pp_subpicture,
+                                           const vlc_fourcc_t *p_chroma_list,
                                            const video_format_t *p_fmt_dst,
-                                           subpicture_t *p_subpic_list,
                                            const video_format_t *p_fmt_src,
                                            mtime_t render_subtitle_date,
                                            mtime_t render_osd_date )
 {
     spu_private_t *p_sys = p_spu->p;
 
-    const int i_source_video_width  = p_fmt_src->i_width;
-    const int i_source_video_height = p_fmt_src->i_height;
-
-    unsigned int i_subpicture;
-    subpicture_t *pp_subpicture[VOUT_MAX_SUBPICTURES];
-
-    unsigned int i_subtitle_region_count;
-    spu_area_t p_subtitle_area_buffer[VOUT_MAX_SUBPICTURES];
-    spu_area_t *p_subtitle_area;
-    int i_subtitle_area;
-
-    /* Preprocess subpictures */
-    i_subpicture = 0;
-    i_subtitle_region_count = 0;
-    for( subpicture_t * p_subpic = p_subpic_list;
-            p_subpic != NULL;
-                p_subpic = p_subpic->p_next )
+    /* Count the number of regions and subtitle regions */
+    unsigned int i_subtitle_region_count = 0;
+    unsigned int i_region_count          = 0;
+    for( unsigned i = 0; i < i_subpicture; i++ )
     {
-        subpicture_Update( p_subpic,
-                           p_fmt_src, p_fmt_dst,
-                           p_subpic->b_subtitle ? render_subtitle_date : render_osd_date );
+        const subpicture_t *p_subpic = pp_subpicture[i];
 
-        /* */
-        if( p_subpic->b_subtitle )
-        {
-            for( subpicture_region_t *r = p_subpic->p_region; r != NULL; r = r->p_next )
-                i_subtitle_region_count++;
-        }
+        unsigned i_count = 0;
+        for( subpicture_region_t *r = p_subpic->p_region; r != NULL; r = r->p_next )
+            i_count++;
 
-        /* */
-        pp_subpicture[i_subpicture++] = p_subpic;
+        if( p_subpic->b_subtitle )
+            i_subtitle_region_count += i_count;
+        i_region_count += i_count;
     }
-
-    /* Be sure we have at least 1 picture to process */
-    if( i_subpicture <= 0 )
+    if( i_region_count <= 0 )
         return NULL;
 
+    /* Create the output subpicture */
     subpicture_t *p_output = subpicture_New( NULL );
-
-    /* Now order subpicture array
-     * XXX The order is *really* important for overlap subtitles positionning */
-    qsort( pp_subpicture, i_subpicture, sizeof(*pp_subpicture), SubpictureCmp );
+    if( !p_output )
+        return NULL;
+    subpicture_region_t **pp_output_last = &p_output->p_region;
 
     /* Allocate area array for subtitle overlap */
+    spu_area_t p_subtitle_area_buffer[VOUT_MAX_SUBPICTURES];
+    spu_area_t *p_subtitle_area;
+    int i_subtitle_area;
+
     i_subtitle_area = 0;
     p_subtitle_area = p_subtitle_area_buffer;
     if( i_subtitle_region_count > sizeof(p_subtitle_area_buffer)/sizeof(*p_subtitle_area_buffer) )
@@ -1142,55 +1085,31 @@ static subpicture_t *SpuRenderSubpictures( spu_t *p_spu,
         if( !p_subpic->p_region )
             continue;
 
-        /* FIXME when possible use a better rendering size than source size
-         * (max of display size and source size for example) FIXME */
-        int i_render_width  = p_subpic->i_original_picture_width;
-        int i_render_height = p_subpic->i_original_picture_height;
-        if( !i_render_width || !i_render_height )
+        if( p_subpic->i_original_picture_width  <= 0 ||
+            p_subpic->i_original_picture_height <= 0 )
         {
-            if( i_render_width != 0 || i_render_height != 0 )
-                msg_Err( p_spu, "unsupported original picture size %dx%d",
-                         i_render_width, i_render_height );
+            if( p_subpic->i_original_picture_width  > 0 ||
+                p_subpic->i_original_picture_height > 0 )
+                msg_Err( p_spu, "original picture size %dx%d is unsupported",
+                         p_subpic->i_original_picture_width,
+                         p_subpic->i_original_picture_height );
+            else
+                msg_Warn( p_spu, "original picture size is undefined" );
 
-            p_subpic->i_original_picture_width  = i_render_width = i_source_video_width;
-            p_subpic->i_original_picture_height = i_render_height = i_source_video_height;
+            p_subpic->i_original_picture_width  = p_fmt_src->i_width;
+            p_subpic->i_original_picture_height = p_fmt_src->i_height;
         }
 
         if( p_sys->p_text )
         {
+            /* FIXME aspect ratio ? */
             p_sys->p_text->fmt_out.video.i_width          =
-            p_sys->p_text->fmt_out.video.i_visible_width  = i_render_width;
+            p_sys->p_text->fmt_out.video.i_visible_width  = p_subpic->i_original_picture_width;
 
             p_sys->p_text->fmt_out.video.i_height         =
-            p_sys->p_text->fmt_out.video.i_visible_height = i_render_height;
-        }
-
-        /* Compute scaling from picture to source size */
-        spu_scale_t scale = spu_scale_createq( i_source_video_width,  i_render_width,
-                                               i_source_video_height, i_render_height );
-
-        /* Update scaling from source size to display size(p_fmt_dst) */
-        scale.w = scale.w * p_fmt_dst->i_width  / i_source_video_width;
-        scale.h = scale.h * p_fmt_dst->i_height / i_source_video_height;
+            p_sys->p_text->fmt_out.video.i_visible_height = p_subpic->i_original_picture_height;
 
-        /* Set default subpicture aspect ratio
-         * FIXME if we only handle 1 aspect ratio per picture, why is it set per
-         * region ? */
-        p_region = p_subpic->p_region;
-        if( !p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den )
-        {
-            p_region->fmt.i_sar_den = p_fmt_dst->i_sar_den;
-            p_region->fmt.i_sar_num = p_fmt_dst->i_sar_num;
-        }
-
-        /* Take care of the aspect ratio */
-        if( p_region->fmt.i_sar_num * p_fmt_dst->i_sar_den !=
-            p_region->fmt.i_sar_den * p_fmt_dst->i_sar_num )
-        {
-            /* FIXME FIXME what about region->i_x/i_y ? */
-            scale.w = scale.w *
-                (int64_t)p_region->fmt.i_sar_num * p_fmt_dst->i_sar_den /
-                p_region->fmt.i_sar_den / p_fmt_dst->i_sar_num;
+            var_SetInteger( p_sys->p_text, "scale", SCALE_UNIT );
         }
 
         /* Render all regions
@@ -1201,23 +1120,35 @@ static subpicture_t *SpuRenderSubpictures( spu_t *p_spu,
         {
             spu_area_t area;
 
+            /* Compute region scale AR */
+            video_format_t region_fmt =p_region->fmt;
+            if( region_fmt.i_sar_num <= 0 || region_fmt.i_sar_den <= 0 )
+            {
+                region_fmt.i_sar_num = p_fmt_src->i_sar_num;
+                region_fmt.i_sar_den = p_fmt_src->i_sar_den;
+            }
+
+            /* Compute scaling from original size to destination size
+             * FIXME The current scaling ensure that the heights match, the width being
+             * cropped.
+             */
+            spu_scale_t scale= spu_scale_createq( (int64_t)p_fmt_dst->i_height                 * p_fmt_dst->i_sar_den * region_fmt.i_sar_num,
+                                                  (int64_t)p_subpic->i_original_picture_height * p_fmt_dst->i_sar_num * region_fmt.i_sar_den,
+                                                  p_fmt_dst->i_height,
+                                                  p_subpic->i_original_picture_height );
+
             /* Check scale validity */
             if( scale.w <= 0 || scale.h <= 0 )
                 continue;
 
             /* */
-            subpicture_region_t *p_render;
-            SpuRenderRegion( p_spu, &p_render, &area,
-                             p_subpic, p_region, scale, p_fmt_dst,
+            SpuRenderRegion( p_spu, pp_output_last, &area,
+                             p_subpic, p_region, scale,
+                             p_chroma_list, p_fmt_dst,
                              p_subtitle_area, i_subtitle_area,
                              p_subpic->b_subtitle ? render_subtitle_date : render_osd_date );
-            if( p_render )
-            {
-                subpicture_region_t **pp_last = &p_output->p_region;
-                while( *pp_last )
-                    pp_last = &(*pp_last)->p_next;
-                *pp_last = p_render;
-            }
+            if( *pp_output_last )
+                pp_output_last = &(*pp_output_last)->p_next;
 
             if( p_subpic->b_subtitle )
             {
@@ -1231,7 +1162,7 @@ static subpicture_t *SpuRenderSubpictures( spu_t *p_spu,
                     p_subtitle_area[i_subtitle_area++] = area;
             }
         }
-        if( p_subpic->b_subtitle )
+        if( p_subpic->b_subtitle && p_subpic->p_region )
             p_subpic->b_absolute = true;
     }
 
@@ -1392,8 +1323,17 @@ spu_t *spu_Create( vlc_object_t *p_this )
                                        p_spu );
 
     /* Load text and scale module */
-    SpuRenderCreateAndLoadText( p_spu );
-    SpuRenderCreateAndLoadScale( p_spu );
+    p_sys->p_text = SpuRenderCreateAndLoadText( p_spu );
+
+    /* XXX p_spu->p_scale is used for all conversion/scaling except yuvp to
+     * yuva/rgba */
+    p_sys->p_scale = SpuRenderCreateAndLoadScale( VLC_OBJECT(p_spu),
+                                                  VLC_CODEC_YUVA, VLC_CODEC_RGBA, true );
+
+    /* This one is used for YUVP to YUVA/RGBA without scaling
+     * FIXME rename it */
+    p_sys->p_scale_yuvp = SpuRenderCreateAndLoadScale( VLC_OBJECT(p_spu),
+                                                       VLC_CODEC_YUVP, VLC_CODEC_YUVA, false );
 
     /* */
     p_sys->i_last_sort_date = -1;
@@ -1448,9 +1388,9 @@ void spu_Attach( spu_t *p_spu, vlc_object_t *p_input, bool b_attach )
         vlc_mutex_lock( &p_spu->p->lock );
         p_spu->p->p_input = p_input;
 
-        FilterRelease( p_spu->p->p_text );
-        p_spu->p->p_text = NULL;
-        SpuRenderCreateAndLoadText( p_spu );
+        if( p_spu->p->p_text )
+            FilterRelease( p_spu->p->p_text );
+        p_spu->p->p_text = SpuRenderCreateAndLoadText( p_spu );
 
         vlc_mutex_unlock( &p_spu->p->lock );
     }
@@ -1515,6 +1455,7 @@ void spu_PutSubpicture( spu_t *p_spu, subpicture_t *p_subpic )
 }
 
 subpicture_t *spu_Render( spu_t *p_spu,
+                          const vlc_fourcc_t *p_chroma_list,
                           const video_format_t *p_fmt_dst,
                           const video_format_t *p_fmt_src,
                           mtime_t render_subtitle_date,
@@ -1542,23 +1483,55 @@ subpicture_t *spu_Render( spu_t *p_spu,
     filter_chain_SubFilter( p_sys->p_chain, render_osd_date );
     vlc_mutex_unlock( &p_sys->chain_lock );
 
-    /* Get the sorted list of subpicture to render */
+    static const vlc_fourcc_t p_chroma_list_default_yuv[] = {
+        VLC_CODEC_YUVA,
+        VLC_CODEC_RGBA,
+        VLC_CODEC_YUVP,
+        0,
+    };
+    static const vlc_fourcc_t p_chroma_list_default_rgb[] = {
+        VLC_CODEC_RGBA,
+        VLC_CODEC_YUVA,
+        VLC_CODEC_YUVP,
+        0,
+    };
+
+    if( !p_chroma_list || *p_chroma_list == 0 )
+        p_chroma_list = vlc_fourcc_IsYUV(p_fmt_dst->i_chroma) ? p_chroma_list_default_yuv
+                                                              : p_chroma_list_default_rgb;
+
     vlc_mutex_lock( &p_sys->lock );
 
-    subpicture_t *p_list = SpuSortSubpictures( p_spu,
-                                               render_subtitle_date,
-                                               render_osd_date,
-                                               b_subtitle_only );
-    if( !p_list )
+    unsigned int i_subpicture;
+    subpicture_t *pp_subpicture[VOUT_MAX_SUBPICTURES];
+
+    /* Get an array of subpictures to render */
+    SpuSelectSubpictures( p_spu, &i_subpicture, pp_subpicture,
+                          render_subtitle_date, render_osd_date, b_subtitle_only );
+    if( i_subpicture <= 0 )
     {
         vlc_mutex_unlock( &p_sys->lock );
         return NULL;
     }
 
-    /* Render the current list of subpictures */
+    /* Updates the subpictures */
+    for( unsigned i = 0; i < i_subpicture; i++ )
+    {
+        subpicture_t *p_subpic = pp_subpicture[i];
+        subpicture_Update( p_subpic,
+                           p_fmt_src, p_fmt_dst,
+                           p_subpic->b_subtitle ? render_subtitle_date : render_osd_date );
+    }
+
+    /* Now order the subpicture array
+     * XXX The order is *really* important for overlap subtitles positionning */
+    qsort( pp_subpicture, i_subpicture, sizeof(*pp_subpicture), SubpictureCmp );
+
+    /* Render the subpictures */
     subpicture_t *p_render = SpuRenderSubpictures( p_spu,
+                                                   i_subpicture, pp_subpicture,
+                                                   p_chroma_list,
                                                    p_fmt_dst,
-                                                   p_list,
                                                    p_fmt_src,
                                                    render_subtitle_date,
                                                    render_osd_date );