]> git.sesse.net Git - vlc/blob - src/video_output/vout_subpictures.c
Missing include (vout).
[vlc] / src / video_output / vout_subpictures.c
1 /*****************************************************************************
2  * vout_subpictures.c : subpicture management functions
3  *****************************************************************************
4  * Copyright (C) 2000-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Vincent Seguin <seguin@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *          Gildas Bazin <gbazin@videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <assert.h>
34 #include <limits.h>
35
36 #include <vlc_common.h>
37 #include <vlc_vout.h>
38 #include <vlc_block.h>
39 #include <vlc_filter.h>
40 #include <vlc_osd.h>
41 #include "../libvlc.h"
42 #include "vout_internal.h"
43 #include <vlc_image.h>
44
45 /*****************************************************************************
46  * Local prototypes
47  *****************************************************************************/
48
49 /* Number of simultaneous subpictures */
50 #define VOUT_MAX_SUBPICTURES (__MAX(VOUT_MAX_PICTURES, SPU_MAX_PREPARE_TIME/5000))
51
52 /* */
53 typedef struct
54 {
55     subpicture_t *p_subpicture;
56     bool          b_reject;
57 } spu_heap_entry_t;
58
59 typedef struct
60 {
61     spu_heap_entry_t p_entry[VOUT_MAX_SUBPICTURES];
62
63 } spu_heap_t;
64
65 static void SpuHeapInit( spu_heap_t * );
66 static int  SpuHeapPush( spu_heap_t *, subpicture_t * );
67 static void SpuHeapDeleteAt( spu_heap_t *, int i_index );
68 static int  SpuHeapDeleteSubpicture( spu_heap_t *, subpicture_t * );
69 static void SpuHeapClean( spu_heap_t *p_heap );
70
71 struct spu_private_t
72 {
73     vlc_mutex_t lock;   /* lock to protect all followings fields */
74
75     spu_heap_t heap;
76
77     int i_channel;             /**< number of subpicture channels registered */
78     filter_t *p_blend;                            /**< alpha blending module */
79     filter_t *p_text;                              /**< text renderer module */
80     filter_t *p_scale_yuvp;                     /**< scaling module for YUVP */
81     filter_t *p_scale;                    /**< scaling module (all but YUVP) */
82     bool b_force_crop;                     /**< force cropping of subpicture */
83     int i_crop_x, i_crop_y, i_crop_width, i_crop_height;       /**< cropping */
84
85     int i_margin;                        /**< force position of a subpicture */
86     bool b_force_palette;             /**< force palette of subpicture */
87     uint8_t palette[4][4];                               /**< forced palette */
88
89     /* Subpiture filters */
90     char           *psz_chain_update;
91     vlc_mutex_t    chain_lock;
92     filter_chain_t *p_chain;
93
94     /* */
95     mtime_t i_last_sort_date;
96 };
97
98 /* */
99 struct subpicture_region_private_t
100 {
101     video_format_t fmt;
102     picture_t      *p_picture;
103 };
104 static subpicture_region_private_t *SpuRegionPrivateNew( video_format_t * );
105 static void SpuRegionPrivateDelete( subpicture_region_private_t * );
106
107 /* */
108 typedef struct
109 {
110     int w;
111     int h;
112 } spu_scale_t;
113 static spu_scale_t spu_scale_create( int w, int h );
114 static spu_scale_t spu_scale_unit(void );
115 static spu_scale_t spu_scale_createq( int wn, int wd, int hn, int hd );
116 static int spu_scale_w( int v, const spu_scale_t s );
117 static int spu_scale_h( int v, const spu_scale_t s );
118 static int spu_invscale_w( int v, const spu_scale_t s );
119 static int spu_invscale_h( int v, const spu_scale_t s );
120
121 typedef struct
122 {
123     int i_x;
124     int i_y;
125     int i_width;
126     int i_height;
127
128     spu_scale_t scale;
129 } spu_area_t;
130
131 static spu_area_t spu_area_create( int x, int y, int w, int h, spu_scale_t );
132 static spu_area_t spu_area_scaled( spu_area_t );
133 static spu_area_t spu_area_unscaled( spu_area_t, spu_scale_t );
134 static bool spu_area_overlap( spu_area_t, spu_area_t );
135
136
137 /* Subpicture rendered flag
138  * FIXME ? it could be moved to private ? */
139 #define SUBPICTURE_RENDERED  (0x1000)
140 #if SUBPICTURE_RENDERED < SUBPICTURE_ALIGN_MASK
141 #   error SUBPICTURE_RENDERED too low
142 #endif
143
144 #define SCALE_UNIT (1000)
145
146 static void SubpictureUpdate( subpicture_t *,
147                               const video_format_t *p_fmt_src,
148                               const video_format_t *p_fmt_dst,
149                               mtime_t i_ts );
150 static void SubpictureChain( subpicture_t **pp_head, subpicture_t *p_subpic );
151 static int SubpictureCmp( const void *s0, const void *s1 );
152
153 static void SpuRenderRegion( spu_t *,
154                              picture_t *p_pic_dst, spu_area_t *,
155                              subpicture_t *, subpicture_region_t *,
156                              const spu_scale_t scale_size,
157                              const video_format_t *p_fmt,
158                              const spu_area_t *p_subtitle_area, int i_subtitle_area,
159                              mtime_t render_date );
160
161 static void UpdateSPU   ( spu_t *, vlc_object_t * );
162 static int  CropCallback( vlc_object_t *, char const *,
163                           vlc_value_t, vlc_value_t, void * );
164 static int MarginCallback( vlc_object_t *, char const *,
165                            vlc_value_t, vlc_value_t, void * );
166
167 static int SpuControl( spu_t *, int, va_list );
168
169 static void SpuClearChannel( spu_t *p_spu, int i_channel );
170
171 /* Buffer allocation for SPU filter (blend, scale, ...) */
172 static subpicture_t *spu_new_buffer( filter_t * );
173 static void spu_del_buffer( filter_t *, subpicture_t * );
174 static picture_t *spu_new_video_buffer( filter_t * );
175 static void spu_del_video_buffer( filter_t *, picture_t * );
176
177 /* Buffer aloccation fir SUB filter */
178 static int SubFilterCallback( vlc_object_t *, char const *,
179                               vlc_value_t, vlc_value_t, void * );
180
181 static int SubFilterAllocationInit( filter_t *, void * );
182 static void SubFilterAllocationClean( filter_t * );
183
184 /* */
185 static void SpuRenderCreateAndLoadText( spu_t * );
186 static void SpuRenderCreateAndLoadScale( spu_t * );
187 static void FilterRelease( filter_t *p_filter );
188
189 /*****************************************************************************
190  * Public API
191  *****************************************************************************/
192
193 #undef spu_Create
194 /**
195  * Creates the subpicture unit
196  *
197  * \param p_this the parent object which creates the subpicture unit
198  */
199 spu_t *spu_Create( vlc_object_t *p_this )
200 {
201     spu_t *p_spu;
202     spu_private_t *p_sys;
203
204     p_spu = vlc_custom_create( p_this, sizeof(spu_t) + sizeof(spu_private_t),
205                                VLC_OBJECT_GENERIC, "subpicture" );
206     if( !p_spu )
207         return NULL;
208     vlc_object_attach( p_spu, p_this );
209
210     /* Initialize spu fields */
211     p_spu->pf_control = SpuControl;
212     p_spu->p = p_sys = (spu_private_t*)&p_spu[1];
213
214     /* Initialize private fields */
215     vlc_mutex_init( &p_sys->lock );
216
217     SpuHeapInit( &p_sys->heap );
218
219     p_sys->p_blend = NULL;
220     p_sys->p_text = NULL;
221     p_sys->p_scale = NULL;
222     p_sys->p_scale_yuvp = NULL;
223
224     p_sys->i_margin = var_InheritInteger( p_spu, "sub-margin" );
225
226     /* Register the default subpicture channel */
227     p_sys->i_channel = 2;
228
229     p_sys->psz_chain_update = NULL;
230     vlc_mutex_init( &p_sys->chain_lock );
231     p_sys->p_chain = filter_chain_New( p_spu, "sub filter", false,
232                                        SubFilterAllocationInit,
233                                        SubFilterAllocationClean,
234                                        p_spu );
235
236     /* Load text and scale module */
237     SpuRenderCreateAndLoadText( p_spu );
238     SpuRenderCreateAndLoadScale( p_spu );
239
240     /* */
241     p_sys->i_last_sort_date = -1;
242
243     return p_spu;
244 }
245
246 /**
247  * Initialise the subpicture unit
248  *
249  * \param p_spu the subpicture unit object
250  */
251 int spu_Init( spu_t *p_spu )
252 {
253     var_Create( p_spu, "sub-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
254     var_AddCallback( p_spu, "sub-filter", SubFilterCallback, p_spu );
255     var_TriggerCallback( p_spu, "sub-filter" );
256
257     return VLC_SUCCESS;
258 }
259
260 /**
261  * Destroy the subpicture unit
262  *
263  * \param p_this the parent object which destroys the subpicture unit
264  */
265 void spu_Destroy( spu_t *p_spu )
266 {
267     spu_private_t *p_sys = p_spu->p;
268
269     var_DelCallback( p_spu, "sub-filter", SubFilterCallback, p_spu );
270
271     if( p_sys->p_blend )
272         filter_DeleteBlend( p_sys->p_blend );
273
274     if( p_sys->p_text )
275         FilterRelease( p_sys->p_text );
276
277     if( p_sys->p_scale_yuvp )
278         FilterRelease( p_sys->p_scale_yuvp );
279
280     if( p_sys->p_scale )
281         FilterRelease( p_sys->p_scale );
282
283     filter_chain_Delete( p_sys->p_chain );
284     vlc_mutex_destroy( &p_sys->chain_lock );
285     free( p_sys->psz_chain_update );
286
287     /* Destroy all remaining subpictures */
288     SpuHeapClean( &p_sys->heap );
289
290     vlc_mutex_destroy( &p_sys->lock );
291
292     vlc_object_release( p_spu );
293 }
294
295 /**
296  * Attach/Detach the SPU from any input
297  *
298  * \param p_this the object in which to destroy the subpicture unit
299  * \param b_attach to select attach or detach
300  */
301 void spu_Attach( spu_t *p_spu, vlc_object_t *p_this, bool b_attach )
302 {
303     vlc_object_t *p_input;
304
305     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
306     if( !p_input )
307         return;
308
309     if( b_attach )
310     {
311         UpdateSPU( p_spu, VLC_OBJECT(p_input) );
312         var_Create( p_input, "highlight", VLC_VAR_BOOL );
313         var_AddCallback( p_input, "highlight", CropCallback, p_spu );
314         var_AddCallback( p_input, "sub-margin", MarginCallback, p_spu->p );
315
316         vlc_mutex_lock( &p_spu->p->lock );
317         p_spu->p->i_margin = var_GetInteger( p_input, "sub-margin" );
318         vlc_mutex_unlock( &p_spu->p->lock );
319
320         vlc_object_release( p_input );
321     }
322     else
323     {
324         /* Delete callbacks */
325         var_DelCallback( p_input, "sub-margin", MarginCallback, p_spu->p );
326         var_DelCallback( p_input, "highlight", CropCallback, p_spu );
327         var_Destroy( p_input, "highlight" );
328         vlc_object_release( p_input );
329     }
330 }
331
332 /**
333  * Inform the SPU filters of mouse event
334  */
335 int spu_ProcessMouse( spu_t *p_spu,
336                       const vlc_mouse_t *p_mouse,
337                       const video_format_t *p_fmt )
338 {
339     spu_private_t *p_sys = p_spu->p;
340
341     vlc_mutex_lock( &p_sys->chain_lock );
342     filter_chain_MouseEvent( p_sys->p_chain, p_mouse, p_fmt );
343     vlc_mutex_unlock( &p_sys->chain_lock );
344
345     return VLC_SUCCESS;
346 }
347
348 /**
349  * Display a subpicture
350  *
351  * Remove the reservation flag of a subpicture, which will cause it to be
352  * ready for display.
353  * \param p_spu the subpicture unit object
354  * \param p_subpic the subpicture to display
355  */
356 void spu_DisplaySubpicture( spu_t *p_spu, subpicture_t *p_subpic )
357 {
358     spu_private_t *p_sys = p_spu->p;
359
360     /* DEFAULT_CHAN always reset itself */
361     if( p_subpic->i_channel == DEFAULT_CHAN )
362         SpuClearChannel( p_spu, DEFAULT_CHAN );
363
364     /* p_private is for spu only and cannot be non NULL here */
365     for( subpicture_region_t *r = p_subpic->p_region; r != NULL; r = r->p_next )
366         assert( r->p_private == NULL );
367
368     /* */
369     vlc_mutex_lock( &p_sys->lock );
370     if( SpuHeapPush( &p_sys->heap, p_subpic ) )
371     {
372         vlc_mutex_unlock( &p_sys->lock );
373         msg_Err( p_spu, "subpicture heap full" );
374         subpicture_Delete( p_subpic );
375         return;
376     }
377     vlc_mutex_unlock( &p_sys->lock );
378 }
379
380 /**
381  * This function renders all sub picture units in the list.
382  */
383 void spu_RenderSubpictures( spu_t *p_spu,
384                             picture_t *p_pic_dst, const video_format_t *p_fmt_dst,
385                             subpicture_t *p_subpic_list,
386                             const video_format_t *p_fmt_src,
387                             mtime_t render_subtitle_date )
388 {
389     spu_private_t *p_sys = p_spu->p;
390
391     const mtime_t render_osd_date = mdate();
392
393     const int i_source_video_width  = p_fmt_src->i_width;
394     const int i_source_video_height = p_fmt_src->i_height;
395
396     unsigned int i_subpicture;
397     subpicture_t *pp_subpicture[VOUT_MAX_SUBPICTURES];
398
399     unsigned int i_subtitle_region_count;
400     spu_area_t p_subtitle_area_buffer[VOUT_MAX_SUBPICTURES];
401     spu_area_t *p_subtitle_area;
402     int i_subtitle_area;
403
404     vlc_mutex_lock( &p_sys->lock );
405
406     /* Preprocess subpictures */
407     i_subpicture = 0;
408     i_subtitle_region_count = 0;
409     for( subpicture_t * p_subpic = p_subpic_list;
410             p_subpic != NULL;
411                 p_subpic = p_subpic->p_next )
412     {
413         SubpictureUpdate( p_subpic,
414                           p_fmt_src, p_fmt_dst,
415                           p_subpic->b_subtitle ? render_subtitle_date : render_osd_date );
416
417         /* */
418         if( p_subpic->b_subtitle )
419         {
420             for( subpicture_region_t *r = p_subpic->p_region; r != NULL; r = r->p_next )
421                 i_subtitle_region_count++;
422         }
423
424         /* */
425         pp_subpicture[i_subpicture++] = p_subpic;
426     }
427
428     /* Be sure we have at least 1 picture to process */
429     if( i_subpicture <= 0 )
430     {
431         vlc_mutex_unlock( &p_sys->lock );
432         return;
433     }
434
435     /* Now order subpicture array
436      * XXX The order is *really* important for overlap subtitles positionning */
437     qsort( pp_subpicture, i_subpicture, sizeof(*pp_subpicture), SubpictureCmp );
438
439     /* Allocate area array for subtitle overlap */
440     i_subtitle_area = 0;
441     p_subtitle_area = p_subtitle_area_buffer;
442     if( i_subtitle_region_count > sizeof(p_subtitle_area_buffer)/sizeof(*p_subtitle_area_buffer) )
443         p_subtitle_area = calloc( i_subtitle_region_count, sizeof(*p_subtitle_area) );
444
445     /* Create the blending module */
446     if( !p_sys->p_blend )
447         p_spu->p->p_blend = filter_NewBlend( VLC_OBJECT(p_spu), p_fmt_dst );
448
449     /* Process all subpictures and regions (in the right order) */
450     for( unsigned int i_index = 0; i_index < i_subpicture; i_index++ )
451     {
452         subpicture_t *p_subpic = pp_subpicture[i_index];
453         subpicture_region_t *p_region;
454
455         if( !p_subpic->p_region )
456             continue;
457
458         /* FIXME when possible use a better rendering size than source size
459          * (max of display size and source size for example) FIXME */
460         int i_render_width  = p_subpic->i_original_picture_width;
461         int i_render_height = p_subpic->i_original_picture_height;
462         if( !i_render_width || !i_render_height )
463         {
464             if( i_render_width != 0 || i_render_height != 0 )
465                 msg_Err( p_spu, "unsupported original picture size %dx%d",
466                          i_render_width, i_render_height );
467
468             p_subpic->i_original_picture_width  = i_render_width = i_source_video_width;
469             p_subpic->i_original_picture_height = i_render_height = i_source_video_height;
470         }
471
472         if( p_sys->p_text )
473         {
474             p_sys->p_text->fmt_out.video.i_width          =
475             p_sys->p_text->fmt_out.video.i_visible_width  = i_render_width;
476
477             p_sys->p_text->fmt_out.video.i_height         =
478             p_sys->p_text->fmt_out.video.i_visible_height = i_render_height;
479         }
480
481         /* Compute scaling from picture to source size */
482         spu_scale_t scale = spu_scale_createq( i_source_video_width,  i_render_width,
483                                                i_source_video_height, i_render_height );
484
485         /* Update scaling from source size to display size(p_fmt_dst) */
486         scale.w = scale.w * p_fmt_dst->i_width  / i_source_video_width;
487         scale.h = scale.h * p_fmt_dst->i_height / i_source_video_height;
488
489         /* Set default subpicture aspect ratio
490          * FIXME if we only handle 1 aspect ratio per picture, why is it set per
491          * region ? */
492         p_region = p_subpic->p_region;
493         if( !p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den )
494         {
495             p_region->fmt.i_sar_den = p_fmt_dst->i_sar_den;
496             p_region->fmt.i_sar_num = p_fmt_dst->i_sar_num;
497         }
498
499         /* Take care of the aspect ratio */
500         if( p_region->fmt.i_sar_num * p_fmt_dst->i_sar_den !=
501             p_region->fmt.i_sar_den * p_fmt_dst->i_sar_num )
502         {
503             /* FIXME FIXME what about region->i_x/i_y ? */
504             scale.w = scale.w *
505                 (int64_t)p_region->fmt.i_sar_num * p_fmt_dst->i_sar_den /
506                 p_region->fmt.i_sar_den / p_fmt_dst->i_sar_num;
507         }
508
509         /* Render all regions
510          * We always transform non absolute subtitle into absolute one on the
511          * first rendering to allow good subtitle overlap support.
512          */
513         for( p_region = p_subpic->p_region; p_region != NULL; p_region = p_region->p_next )
514         {
515             spu_area_t area;
516
517             /* Check scale validity */
518             if( scale.w <= 0 || scale.h <= 0 )
519                 continue;
520
521             /* */
522             SpuRenderRegion( p_spu, p_pic_dst, &area,
523                              p_subpic, p_region, scale, p_fmt_dst,
524                              p_subtitle_area, i_subtitle_area,
525                              p_subpic->b_subtitle ? render_subtitle_date : render_osd_date );
526
527             if( p_subpic->b_subtitle )
528             {
529                 area = spu_area_unscaled( area, scale );
530                 if( !p_subpic->b_absolute && area.i_width > 0 && area.i_height > 0 )
531                 {
532                     p_region->i_x = area.i_x;
533                     p_region->i_y = area.i_y;
534                 }
535                 if( p_subtitle_area )
536                     p_subtitle_area[i_subtitle_area++] = area;
537             }
538         }
539         if( p_subpic->b_subtitle )
540             p_subpic->b_absolute = true;
541     }
542
543     /* */
544     if( p_subtitle_area != p_subtitle_area_buffer )
545         free( p_subtitle_area );
546
547     vlc_mutex_unlock( &p_sys->lock );
548 }
549
550 /*****************************************************************************
551  * spu_SortSubpictures: find the subpictures to display
552  *****************************************************************************
553  * This function parses all subpictures and decides which ones need to be
554  * displayed. If no picture has been selected, display_date will depend on
555  * the subpicture.
556  * We also check for ephemer DVD subpictures (subpictures that have
557  * to be removed if a newer one is available), which makes it a lot
558  * more difficult to guess if a subpicture has to be rendered or not.
559  *****************************************************************************/
560 subpicture_t *spu_SortSubpictures( spu_t *p_spu, mtime_t render_subtitle_date,
561                                    bool b_subtitle_only )
562 {
563     spu_private_t *p_sys = p_spu->p;
564     int i_channel;
565     subpicture_t *p_subpic = NULL;
566     const mtime_t render_osd_date = mdate();
567
568     /* Update sub-filter chain */
569     vlc_mutex_lock( &p_sys->lock );
570     char *psz_chain_update = p_sys->psz_chain_update;
571     p_sys->psz_chain_update = NULL;
572     vlc_mutex_unlock( &p_sys->lock );
573
574     vlc_mutex_lock( &p_sys->chain_lock );
575     if( psz_chain_update )
576     {
577         filter_chain_Reset( p_sys->p_chain, NULL, NULL );
578
579         filter_chain_AppendFromString( p_spu->p->p_chain, psz_chain_update );
580
581         free( psz_chain_update );
582     }
583     /* Run subpicture filters */
584     filter_chain_SubFilter( p_sys->p_chain, render_osd_date );
585     vlc_mutex_unlock( &p_sys->chain_lock );
586
587     vlc_mutex_lock( &p_sys->lock );
588
589     /* We get an easily parsable chained list of subpictures which
590      * ends with NULL since p_subpic was initialized to NULL. */
591     for( i_channel = 0; i_channel < p_sys->i_channel; i_channel++ )
592     {
593         subpicture_t *p_available_subpic[VOUT_MAX_SUBPICTURES];
594         bool         pb_available_late[VOUT_MAX_SUBPICTURES];
595         int          i_available = 0;
596
597         mtime_t      start_date = render_subtitle_date;
598         mtime_t      ephemer_subtitle_date = 0;
599         mtime_t      ephemer_osd_date = 0;
600         int64_t      i_ephemer_subtitle_order = INT64_MIN;
601         int64_t      i_ephemer_system_order = INT64_MIN;
602         int i_index;
603
604         /* Select available pictures */
605         for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
606         {
607             spu_heap_entry_t *p_entry = &p_sys->heap.p_entry[i_index];
608             subpicture_t *p_current = p_entry->p_subpicture;
609             bool b_stop_valid;
610             bool b_late;
611
612             if( !p_current || p_entry->b_reject )
613             {
614                 if( p_entry->b_reject )
615                     SpuHeapDeleteAt( &p_sys->heap, i_index );
616                 continue;
617             }
618
619             if( p_current->i_channel != i_channel ||
620                 ( b_subtitle_only && !p_current->b_subtitle ) )
621             {
622                 continue;
623             }
624             const mtime_t render_date = p_current->b_subtitle ? render_subtitle_date : render_osd_date;
625             if( render_date &&
626                 render_date < p_current->i_start )
627             {
628                 /* Too early, come back next monday */
629                 continue;
630             }
631
632             mtime_t *pi_ephemer_date  = p_current->b_subtitle ? &ephemer_subtitle_date : &ephemer_osd_date;
633             int64_t *pi_ephemer_order = p_current->b_subtitle ? &i_ephemer_subtitle_order : &i_ephemer_system_order;
634             if( p_current->i_start >= *pi_ephemer_date )
635             {
636                 *pi_ephemer_date = p_current->i_start;
637                 if( p_current->i_order > *pi_ephemer_order )
638                     *pi_ephemer_order = p_current->i_order;
639             }
640
641             b_stop_valid = !p_current->b_ephemer || p_current->i_stop > p_current->i_start;
642
643             b_late = b_stop_valid && p_current->i_stop <= render_date;
644
645             /* start_date will be used for correct automatic overlap support
646              * in case picture that should not be displayed anymore (display_time)
647              * overlap with a picture to be displayed (p_current->i_start)  */
648             if( p_current->b_subtitle && !b_late && !p_current->b_ephemer )
649                 start_date = p_current->i_start;
650
651             /* */
652             p_available_subpic[i_available] = p_current;
653             pb_available_late[i_available] = b_late;
654             i_available++;
655         }
656
657         /* Only forced old picture display at the transition */
658         if( start_date < p_sys->i_last_sort_date )
659             start_date = p_sys->i_last_sort_date;
660         if( start_date <= 0 )
661             start_date = INT64_MAX;
662
663         /* Select pictures to be displayed */
664         for( i_index = 0; i_index < i_available; i_index++ )
665         {
666             subpicture_t *p_current = p_available_subpic[i_index];
667             bool b_late = pb_available_late[i_index];
668
669             const mtime_t stop_date = p_current->b_subtitle ? __MAX( start_date, p_sys->i_last_sort_date ) : render_osd_date;
670             const mtime_t ephemer_date = p_current->b_subtitle ? ephemer_subtitle_date : ephemer_osd_date;
671             const int64_t i_ephemer_order = p_current->b_subtitle ? i_ephemer_subtitle_order : i_ephemer_system_order;
672
673             /* Destroy late and obsolete ephemer subpictures */
674             bool b_rejet = b_late && p_current->i_stop <= stop_date;
675             if( p_current->b_ephemer )
676             {
677                 if( p_current->i_start < ephemer_date )
678                     b_rejet = true;
679                 else if( p_current->i_start == ephemer_date &&
680                          p_current->i_order < i_ephemer_order )
681                     b_rejet = true;
682             }
683
684             if( b_rejet )
685                 SpuHeapDeleteSubpicture( &p_sys->heap, p_current );
686             else
687                 SubpictureChain( &p_subpic, p_current );
688         }
689     }
690
691     p_sys->i_last_sort_date = render_subtitle_date;
692     vlc_mutex_unlock( &p_sys->lock );
693
694     return p_subpic;
695 }
696
697 void spu_OffsetSubtitleDate( spu_t *p_spu, mtime_t i_duration )
698 {
699     spu_private_t *p_sys = p_spu->p;
700
701     vlc_mutex_lock( &p_sys->lock );
702     for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
703     {
704         spu_heap_entry_t *p_entry = &p_sys->heap.p_entry[i];
705         subpicture_t *p_current = p_entry->p_subpicture;
706
707         if( p_current && p_current->b_subtitle )
708         {
709             if( p_current->i_start > 0 )
710                 p_current->i_start += i_duration;
711             if( p_current->i_stop > 0 )
712                 p_current->i_stop += i_duration;
713         }
714     }
715     vlc_mutex_unlock( &p_sys->lock );
716 }
717
718 /*****************************************************************************
719  * subpicture_t allocation
720  *****************************************************************************/
721 struct subpicture_private_t
722 {
723     video_format_t src;
724     video_format_t dst;
725 };
726
727 subpicture_t *subpicture_New( const subpicture_updater_t *p_upd )
728 {
729     subpicture_t *p_subpic = calloc( 1, sizeof(*p_subpic) );
730     if( !p_subpic )
731         return NULL;
732
733     p_subpic->i_order    = 0;
734     p_subpic->b_absolute = true;
735     p_subpic->b_fade     = false;
736     p_subpic->b_subtitle = false;
737     p_subpic->i_alpha    = 0xFF;
738     p_subpic->p_region   = NULL;
739
740     if( p_upd )
741     {
742         subpicture_private_t *p_private = malloc( sizeof(*p_private) );
743         if( !p_private )
744         {
745             free( p_subpic );
746             return NULL;
747         }
748         video_format_Init( &p_private->src, 0 );
749         video_format_Init( &p_private->dst, 0 );
750
751         p_subpic->updater   = *p_upd;
752         p_subpic->p_private = p_private;
753     }
754     else
755     {
756         p_subpic->p_private = NULL;
757
758         p_subpic->updater.pf_validate = NULL;
759         p_subpic->updater.pf_update   = NULL;
760         p_subpic->updater.pf_destroy  = NULL;
761         p_subpic->updater.p_sys       = NULL;
762     }
763     return p_subpic;
764 }
765
766 void subpicture_Delete( subpicture_t *p_subpic )
767 {
768     subpicture_region_ChainDelete( p_subpic->p_region );
769     p_subpic->p_region = NULL;
770
771     if( p_subpic->updater.pf_destroy )
772         p_subpic->updater.pf_destroy( p_subpic );
773
774     free( p_subpic->p_private );
775     free( p_subpic );
776 }
777
778 static void SubpictureChain( subpicture_t **pp_head, subpicture_t *p_subpic )
779 {
780     p_subpic->p_next = *pp_head;
781
782     *pp_head = p_subpic;
783 }
784
785 subpicture_t *subpicture_NewFromPicture( vlc_object_t *p_obj,
786                                          picture_t *p_picture, vlc_fourcc_t i_chroma )
787 {
788     /* */
789     video_format_t fmt_in = p_picture->format;
790
791     /* */
792     video_format_t fmt_out;
793     fmt_out = fmt_in;
794     fmt_out.i_chroma = i_chroma;
795
796     /* */
797     image_handler_t *p_image = image_HandlerCreate( p_obj );
798     if( !p_image )
799         return NULL;
800
801     picture_t *p_pip = image_Convert( p_image, p_picture, &fmt_in, &fmt_out );
802
803     image_HandlerDelete( p_image );
804
805     if( !p_pip )
806         return NULL;
807
808     subpicture_t *p_subpic = subpicture_New( NULL );
809     if( !p_subpic )
810     {
811          picture_Release( p_pip );
812          return NULL;
813     }
814
815     p_subpic->i_original_picture_width  = fmt_out.i_width;
816     p_subpic->i_original_picture_height = fmt_out.i_height;
817
818     fmt_out.i_sar_num =
819     fmt_out.i_sar_den = 0;
820
821     p_subpic->p_region = subpicture_region_New( &fmt_out );
822     if( p_subpic->p_region )
823     {
824         picture_Release( p_subpic->p_region->p_picture );
825         p_subpic->p_region->p_picture = p_pip;
826     }
827     else
828     {
829         picture_Release( p_pip );
830     }
831     return p_subpic;
832 }
833
834 static void SubpictureUpdate( subpicture_t *p_subpicture,
835                               const video_format_t *p_fmt_src,
836                               const video_format_t *p_fmt_dst,
837                               mtime_t i_ts )
838 {
839     subpicture_updater_t *p_upd = &p_subpicture->updater;
840     subpicture_private_t *p_private = p_subpicture->p_private;
841
842     if( !p_upd->pf_validate )
843         return;
844     if( !p_upd->pf_validate( p_subpicture,
845                           !video_format_IsSimilar( p_fmt_src,
846                                                    &p_private->src ), p_fmt_src,
847                           !video_format_IsSimilar( p_fmt_dst,
848                                                    &p_private->dst ), p_fmt_dst,
849                           i_ts ) )
850         return;
851
852     subpicture_region_ChainDelete( p_subpicture->p_region );
853     p_subpicture->p_region = NULL;
854
855     p_upd->pf_update( p_subpicture, p_fmt_src, p_fmt_dst, i_ts );
856
857     video_format_Clean( &p_private->src );
858     video_format_Clean( &p_private->dst );
859
860     video_format_Copy( &p_private->src, p_fmt_src );
861     video_format_Copy( &p_private->dst, p_fmt_dst );
862 }
863
864 /*****************************************************************************
865  * subpicture_region_t allocation
866  *****************************************************************************/
867 subpicture_region_t *subpicture_region_New( const video_format_t *p_fmt )
868 {
869     subpicture_region_t *p_region = calloc( 1, sizeof(*p_region ) );
870     if( !p_region )
871         return NULL;
872
873     p_region->fmt = *p_fmt;
874     p_region->fmt.p_palette = NULL;
875     if( p_fmt->i_chroma == VLC_CODEC_YUVP )
876     {
877         p_region->fmt.p_palette = calloc( 1, sizeof(*p_region->fmt.p_palette) );
878         if( p_fmt->p_palette )
879             *p_region->fmt.p_palette = *p_fmt->p_palette;
880     }
881     p_region->i_alpha = 0xff;
882     p_region->p_next = NULL;
883     p_region->p_private = NULL;
884     p_region->psz_text = NULL;
885     p_region->p_style = NULL;
886     p_region->p_picture = NULL;
887
888     if( p_fmt->i_chroma == VLC_CODEC_TEXT )
889         return p_region;
890
891     p_region->p_picture = picture_NewFromFormat( p_fmt );
892     if( !p_region->p_picture )
893     {
894         free( p_region->fmt.p_palette );
895         free( p_region );
896         return NULL;
897     }
898
899     return p_region;
900 }
901
902 /* */
903 void subpicture_region_Delete( subpicture_region_t *p_region )
904 {
905     if( !p_region )
906         return;
907
908     if( p_region->p_private )
909         SpuRegionPrivateDelete( p_region->p_private );
910
911     if( p_region->p_picture )
912         picture_Release( p_region->p_picture );
913
914     free( p_region->fmt.p_palette );
915
916     free( p_region->psz_text );
917     free( p_region->psz_html );
918     if( p_region->p_style )
919         text_style_Delete( p_region->p_style );
920     free( p_region );
921 }
922
923 /* */
924 void subpicture_region_ChainDelete( subpicture_region_t *p_head )
925 {
926     while( p_head )
927     {
928         subpicture_region_t *p_next = p_head->p_next;
929
930         subpicture_region_Delete( p_head );
931
932         p_head = p_next;
933     }
934 }
935
936
937
938 /*****************************************************************************
939  * heap managment
940  *****************************************************************************/
941 static void SpuHeapInit( spu_heap_t *p_heap )
942 {
943     for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
944     {
945         spu_heap_entry_t *e = &p_heap->p_entry[i];
946
947         e->p_subpicture = NULL;
948         e->b_reject = false;
949     }
950 }
951
952 static int SpuHeapPush( spu_heap_t *p_heap, subpicture_t *p_subpic )
953 {
954     for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
955     {
956         spu_heap_entry_t *e = &p_heap->p_entry[i];
957
958         if( e->p_subpicture )
959             continue;
960
961         e->p_subpicture = p_subpic;
962         e->b_reject = false;
963         return VLC_SUCCESS;
964     }
965     return VLC_EGENERIC;
966 }
967
968 static void SpuHeapDeleteAt( spu_heap_t *p_heap, int i_index )
969 {
970     spu_heap_entry_t *e = &p_heap->p_entry[i_index];
971
972     if( e->p_subpicture )
973         subpicture_Delete( e->p_subpicture );
974
975     e->p_subpicture = NULL;
976 }
977
978 static int SpuHeapDeleteSubpicture( spu_heap_t *p_heap, subpicture_t *p_subpic )
979 {
980     for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
981     {
982         spu_heap_entry_t *e = &p_heap->p_entry[i];
983
984         if( e->p_subpicture != p_subpic )
985             continue;
986
987         SpuHeapDeleteAt( p_heap, i );
988         return VLC_SUCCESS;
989     }
990     return VLC_EGENERIC;
991 }
992
993 static void SpuHeapClean( spu_heap_t *p_heap )
994 {
995     for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
996     {
997         spu_heap_entry_t *e = &p_heap->p_entry[i];
998         if( e->p_subpicture )
999             subpicture_Delete( e->p_subpicture );
1000     }
1001 }
1002
1003 static subpicture_region_private_t *SpuRegionPrivateNew( video_format_t *p_fmt )
1004 {
1005     subpicture_region_private_t *p_private = malloc( sizeof(*p_private) );
1006
1007     if( !p_private )
1008         return NULL;
1009
1010     p_private->fmt = *p_fmt;
1011     if( p_fmt->p_palette )
1012     {
1013         p_private->fmt.p_palette = malloc( sizeof(*p_private->fmt.p_palette) );
1014         if( p_private->fmt.p_palette )
1015             *p_private->fmt.p_palette = *p_fmt->p_palette;
1016     }
1017     p_private->p_picture = NULL;
1018
1019     return p_private;
1020 }
1021 static void SpuRegionPrivateDelete( subpicture_region_private_t *p_private )
1022 {
1023     if( p_private->p_picture )
1024         picture_Release( p_private->p_picture );
1025     free( p_private->fmt.p_palette );
1026     free( p_private );
1027 }
1028
1029 static void FilterRelease( filter_t *p_filter )
1030 {
1031     if( p_filter->p_module )
1032         module_unneed( p_filter, p_filter->p_module );
1033
1034     vlc_object_release( p_filter );
1035 }
1036
1037 static void SpuRenderCreateAndLoadText( spu_t *p_spu )
1038 {
1039     filter_t *p_text;
1040
1041     assert( !p_spu->p->p_text );
1042
1043     p_spu->p->p_text =
1044     p_text        = vlc_custom_create( p_spu, sizeof(filter_t),
1045                                        VLC_OBJECT_GENERIC, "spu text" );
1046     if( !p_text )
1047         return;
1048
1049     es_format_Init( &p_text->fmt_in, VIDEO_ES, 0 );
1050
1051     es_format_Init( &p_text->fmt_out, VIDEO_ES, 0 );
1052     p_text->fmt_out.video.i_width =
1053     p_text->fmt_out.video.i_visible_width = 32;
1054     p_text->fmt_out.video.i_height =
1055     p_text->fmt_out.video.i_visible_height = 32;
1056
1057     p_text->pf_sub_buffer_new = spu_new_buffer;
1058     p_text->pf_sub_buffer_del = spu_del_buffer;
1059
1060     vlc_object_attach( p_text, p_spu );
1061
1062     /* FIXME TOCHECK shouldn't module_need( , , psz_modulename, false ) do the
1063      * same than these 2 calls ? */
1064     char *psz_modulename = var_CreateGetString( p_spu, "text-renderer" );
1065     if( psz_modulename && *psz_modulename )
1066     {
1067         p_text->p_module = module_need( p_text, "text renderer",
1068                                         psz_modulename, true );
1069     }
1070     free( psz_modulename );
1071
1072     if( !p_text->p_module )
1073         p_text->p_module = module_need( p_text, "text renderer", NULL, false );
1074
1075     /* Create a few variables used for enhanced text rendering */
1076     var_Create( p_text, "spu-duration", VLC_VAR_TIME );
1077     var_Create( p_text, "spu-elapsed", VLC_VAR_TIME );
1078     var_Create( p_text, "text-rerender", VLC_VAR_BOOL );
1079     var_Create( p_text, "scale", VLC_VAR_INTEGER );
1080 }
1081
1082 static filter_t *CreateAndLoadScale( vlc_object_t *p_obj,
1083                                      vlc_fourcc_t i_src_chroma, vlc_fourcc_t i_dst_chroma,
1084                                      bool b_resize )
1085 {
1086     filter_t *p_scale;
1087
1088     p_scale = vlc_custom_create( p_obj, sizeof(filter_t),
1089                                  VLC_OBJECT_GENERIC, "scale" );
1090     if( !p_scale )
1091         return NULL;
1092
1093     es_format_Init( &p_scale->fmt_in, VIDEO_ES, 0 );
1094     p_scale->fmt_in.video.i_chroma = i_src_chroma;
1095     p_scale->fmt_in.video.i_width =
1096     p_scale->fmt_in.video.i_height = 32;
1097
1098     es_format_Init( &p_scale->fmt_out, VIDEO_ES, 0 );
1099     p_scale->fmt_out.video.i_chroma = i_dst_chroma;
1100     p_scale->fmt_out.video.i_width =
1101     p_scale->fmt_out.video.i_height = b_resize ? 16 : 32;
1102
1103     p_scale->pf_video_buffer_new = spu_new_video_buffer;
1104     p_scale->pf_video_buffer_del = spu_del_video_buffer;
1105
1106     vlc_object_attach( p_scale, p_obj );
1107     p_scale->p_module = module_need( p_scale, "video filter2", NULL, false );
1108
1109     return p_scale;
1110 }
1111 static void SpuRenderCreateAndLoadScale( spu_t *p_spu )
1112 {
1113     assert( !p_spu->p->p_scale );
1114     assert( !p_spu->p->p_scale_yuvp );
1115     /* XXX p_spu->p_scale is used for all conversion/scaling except yuvp to
1116      * yuva/rgba */
1117     p_spu->p->p_scale = CreateAndLoadScale( VLC_OBJECT(p_spu),
1118                                             VLC_CODEC_YUVA, VLC_CODEC_YUVA, true );
1119     /* This one is used for YUVP to YUVA/RGBA without scaling
1120      * FIXME rename it */
1121     p_spu->p->p_scale_yuvp = CreateAndLoadScale( VLC_OBJECT(p_spu),
1122                                                  VLC_CODEC_YUVP, VLC_CODEC_YUVA, false );
1123 }
1124
1125 static void SpuRenderText( spu_t *p_spu, bool *pb_rerender_text,
1126                            subpicture_t *p_subpic, subpicture_region_t *p_region,
1127                            int i_min_scale_ratio, mtime_t render_date )
1128 {
1129     filter_t *p_text = p_spu->p->p_text;
1130
1131     assert( p_region->fmt.i_chroma == VLC_CODEC_TEXT );
1132
1133     if( !p_text || !p_text->p_module )
1134         goto exit;
1135
1136     /* Setup 3 variables which can be used to render
1137      * time-dependent text (and effects). The first indicates
1138      * the total amount of time the text will be on screen,
1139      * the second the amount of time it has already been on
1140      * screen (can be a negative value as text is layed out
1141      * before it is rendered) and the third is a feedback
1142      * variable from the renderer - if the renderer sets it
1143      * then this particular text is time-dependent, eg. the
1144      * visual progress bar inside the text in karaoke and the
1145      * text needs to be rendered multiple times in order for
1146      * the effect to work - we therefore need to return the
1147      * region to its original state at the end of the loop,
1148      * instead of leaving it in YUVA or YUVP.
1149      * Any renderer which is unaware of how to render
1150      * time-dependent text can happily ignore the variables
1151      * and render the text the same as usual - it should at
1152      * least show up on screen, but the effect won't change
1153      * the text over time.
1154      */
1155     var_SetTime( p_text, "spu-duration", p_subpic->i_stop - p_subpic->i_start );
1156     var_SetTime( p_text, "spu-elapsed", render_date );
1157     var_SetBool( p_text, "text-rerender", false );
1158     var_SetInteger( p_text, "scale", i_min_scale_ratio );
1159
1160     if( p_text->pf_render_html && p_region->psz_html )
1161     {
1162         p_text->pf_render_html( p_text, p_region, p_region );
1163     }
1164     else if( p_text->pf_render_text )
1165     {
1166         p_text->pf_render_text( p_text, p_region, p_region );
1167     }
1168     *pb_rerender_text = var_GetBool( p_text, "text-rerender" );
1169
1170 exit:
1171     p_region->i_align |= SUBPICTURE_RENDERED;
1172 }
1173
1174 /**
1175  * A few scale functions helpers.
1176  */
1177 static spu_scale_t spu_scale_create( int w, int h )
1178 {
1179     spu_scale_t s = { .w = w, .h = h };
1180     if( s.w <= 0 )
1181         s.w = SCALE_UNIT;
1182     if( s.h <= 0 )
1183         s.h = SCALE_UNIT;
1184     return s;
1185 }
1186 static spu_scale_t spu_scale_unit( void )
1187 {
1188     return spu_scale_create( SCALE_UNIT, SCALE_UNIT );
1189 }
1190 static spu_scale_t spu_scale_createq( int wn, int wd, int hn, int hd )
1191 {
1192     return spu_scale_create( wn * SCALE_UNIT / wd,
1193                              hn * SCALE_UNIT / hd );
1194 }
1195 static int spu_scale_w( int v, const spu_scale_t s )
1196 {
1197     return v * s.w / SCALE_UNIT;
1198 }
1199 static int spu_scale_h( int v, const spu_scale_t s )
1200 {
1201     return v * s.h / SCALE_UNIT;
1202 }
1203 static int spu_invscale_w( int v, const spu_scale_t s )
1204 {
1205     return v * SCALE_UNIT / s.w;
1206 }
1207 static int spu_invscale_h( int v, const spu_scale_t s )
1208 {
1209     return v * SCALE_UNIT / s.h;
1210 }
1211
1212 /**
1213  * A few area functions helpers
1214  */
1215 static spu_area_t spu_area_create( int x, int y, int w, int h, spu_scale_t s )
1216 {
1217     spu_area_t a = { .i_x = x, .i_y = y, .i_width = w, .i_height = h, .scale = s };
1218     return a;
1219 }
1220 static spu_area_t spu_area_scaled( spu_area_t a )
1221 {
1222     if( a.scale.w == SCALE_UNIT && a.scale.h == SCALE_UNIT )
1223         return a;
1224
1225     a.i_x = spu_scale_w( a.i_x, a.scale );
1226     a.i_y = spu_scale_h( a.i_y, a.scale );
1227
1228     a.i_width  = spu_scale_w( a.i_width,  a.scale );
1229     a.i_height = spu_scale_h( a.i_height, a.scale );
1230
1231     a.scale = spu_scale_unit();
1232     return a;
1233 }
1234 static spu_area_t spu_area_unscaled( spu_area_t a, spu_scale_t s )
1235 {
1236     if( a.scale.w == s.w && a.scale.h == s.h )
1237         return a;
1238
1239     a = spu_area_scaled( a );
1240
1241     a.i_x = spu_invscale_w( a.i_x, s );
1242     a.i_y = spu_invscale_h( a.i_y, s );
1243
1244     a.i_width  = spu_invscale_w( a.i_width, s );
1245     a.i_height = spu_invscale_h( a.i_height, s );
1246
1247     a.scale = s;
1248     return a;
1249 }
1250 static bool spu_area_overlap( spu_area_t a, spu_area_t b )
1251 {
1252     const int i_dx = 0;
1253     const int i_dy = 0;
1254
1255     a = spu_area_scaled( a );
1256     b = spu_area_scaled( b );
1257
1258     return  __MAX( a.i_x-i_dx, b.i_x ) < __MIN( a.i_x+a.i_width +i_dx, b.i_x+b.i_width  ) &&
1259             __MAX( a.i_y-i_dy, b.i_y ) < __MIN( a.i_y+a.i_height+i_dy, b.i_y+b.i_height );
1260 }
1261
1262 /**
1263  * Avoid area overlapping
1264  */
1265 static void SpuAreaFixOverlap( spu_area_t *p_dst,
1266                                const spu_area_t *p_sub, int i_sub, int i_align )
1267 {
1268     spu_area_t a = spu_area_scaled( *p_dst );
1269     bool b_moved = false;
1270     bool b_ok;
1271
1272     /* Check for overlap
1273      * XXX It is not fast O(n^2) but we should not have a lot of region */
1274     do
1275     {
1276         b_ok = true;
1277         for( int i = 0; i < i_sub; i++ )
1278         {
1279             spu_area_t sub = spu_area_scaled( p_sub[i] );
1280
1281             if( !spu_area_overlap( a, sub ) )
1282                 continue;
1283
1284             if( i_align & SUBPICTURE_ALIGN_TOP )
1285             {
1286                 /* We go down */
1287                 int i_y = sub.i_y + sub.i_height;
1288                 a.i_y = i_y;
1289                 b_moved = true;
1290             }
1291             else if( i_align & SUBPICTURE_ALIGN_BOTTOM )
1292             {
1293                 /* We go up */
1294                 int i_y = sub.i_y - a.i_height;
1295                 a.i_y = i_y;
1296                 b_moved = true;
1297             }
1298             else
1299             {
1300                 /* TODO what to do in this case? */
1301                 //fprintf( stderr, "Overlap with unsupported alignment\n" );
1302                 break;
1303             }
1304
1305             b_ok = false;
1306             break;
1307         }
1308     } while( !b_ok );
1309
1310     if( b_moved )
1311         *p_dst = spu_area_unscaled( a, p_dst->scale );
1312 }
1313
1314
1315 static void SpuAreaFitInside( spu_area_t *p_area, const spu_area_t *p_boundary )
1316 {
1317   spu_area_t a = spu_area_scaled( *p_area );
1318
1319   const int i_error_x = (a.i_x + a.i_width) - p_boundary->i_width;
1320   if( i_error_x > 0 )
1321       a.i_x -= i_error_x;
1322   if( a.i_x < 0 )
1323       a.i_x = 0;
1324
1325   const int i_error_y = (a.i_y + a.i_height) - p_boundary->i_height;
1326   if( i_error_y > 0 )
1327       a.i_y -= i_error_y;
1328   if( a.i_y < 0 )
1329       a.i_y = 0;
1330
1331   *p_area = spu_area_unscaled( a, p_area->scale );
1332 }
1333
1334 /**
1335  * Place a region
1336  */
1337 static void SpuRegionPlace( int *pi_x, int *pi_y,
1338                             const subpicture_t *p_subpic,
1339                             const subpicture_region_t *p_region )
1340 {
1341     const int i_delta_x = p_region->i_x;
1342     const int i_delta_y = p_region->i_y;
1343     int i_x, i_y;
1344
1345     assert( p_region->i_x != INT_MAX && p_region->i_y != INT_MAX );
1346     if( p_region->i_align & SUBPICTURE_ALIGN_TOP )
1347     {
1348         i_y = i_delta_y;
1349     }
1350     else if( p_region->i_align & SUBPICTURE_ALIGN_BOTTOM )
1351     {
1352         i_y = p_subpic->i_original_picture_height - p_region->fmt.i_height - i_delta_y;
1353     }
1354     else
1355     {
1356         i_y = p_subpic->i_original_picture_height / 2 - p_region->fmt.i_height / 2;
1357     }
1358
1359     if( p_region->i_align & SUBPICTURE_ALIGN_LEFT )
1360     {
1361         i_x = i_delta_x;
1362     }
1363     else if( p_region->i_align & SUBPICTURE_ALIGN_RIGHT )
1364     {
1365         i_x = p_subpic->i_original_picture_width - p_region->fmt.i_width - i_delta_x;
1366     }
1367     else
1368     {
1369         i_x = p_subpic->i_original_picture_width / 2 - p_region->fmt.i_width / 2;
1370     }
1371
1372     if( p_subpic->b_absolute )
1373     {
1374         i_x = i_delta_x;
1375         i_y = i_delta_y;
1376     }
1377
1378     /* Margin shifts all subpictures */
1379     /* NOTE We have margin only for subtitles, so we don't really need this here
1380     if( i_margin_y != 0 )
1381         i_y -= i_margin_y;*/
1382
1383     /* Clamp offset to not go out of the screen (when possible) */
1384     /* NOTE Again, useful only for subtitles, otherwise goes against the alignment logic above
1385     const int i_error_x = (i_x + p_region->fmt.i_width) - p_subpic->i_original_picture_width;
1386     if( i_error_x > 0 )
1387         i_x -= i_error_x;
1388     if( i_x < 0 )
1389         i_x = 0;
1390
1391     const int i_error_y = (i_y + p_region->fmt.i_height) - p_subpic->i_original_picture_height;
1392     if( i_error_y > 0 )
1393         i_y -= i_error_y;
1394     if( i_y < 0 )
1395         i_y = 0;*/
1396
1397     *pi_x = i_x;
1398     *pi_y = i_y;
1399 }
1400
1401 /**
1402  * This function computes the current alpha value for a given region.
1403  */
1404 static int SpuRegionAlpha( subpicture_t *p_subpic, subpicture_region_t *p_region )
1405 {
1406     /* Compute alpha blend value */
1407     int i_fade_alpha = 255;
1408     if( p_subpic->b_fade )
1409     {
1410         mtime_t i_fade_start = ( p_subpic->i_stop +
1411                                  p_subpic->i_start ) / 2;
1412         mtime_t i_now = mdate();
1413
1414         if( i_now >= i_fade_start && p_subpic->i_stop > i_fade_start )
1415         {
1416             i_fade_alpha = 255 * ( p_subpic->i_stop - i_now ) /
1417                            ( p_subpic->i_stop - i_fade_start );
1418         }
1419     }
1420     return i_fade_alpha * p_subpic->i_alpha * p_region->i_alpha / 65025;
1421 }
1422
1423 /**
1424  * It will render the provided region onto p_pic_dst.
1425  */
1426
1427 static void SpuRenderRegion( spu_t *p_spu,
1428                              picture_t *p_pic_dst, spu_area_t *p_area,
1429                              subpicture_t *p_subpic, subpicture_region_t *p_region,
1430                              const spu_scale_t scale_size,
1431                              const video_format_t *p_fmt,
1432                              const spu_area_t *p_subtitle_area, int i_subtitle_area,
1433                              mtime_t render_date )
1434 {
1435     spu_private_t *p_sys = p_spu->p;
1436
1437     video_format_t fmt_original = p_region->fmt;
1438     bool b_rerender_text = false;
1439     bool b_restore_format = false;
1440     int i_x_offset;
1441     int i_y_offset;
1442
1443     video_format_t region_fmt;
1444     picture_t *p_region_picture;
1445
1446     /* Invalidate area by default */
1447     *p_area = spu_area_create( 0,0, 0,0, scale_size );
1448
1449     /* Render text region */
1450     if( p_region->fmt.i_chroma == VLC_CODEC_TEXT )
1451     {
1452         const int i_min_scale_ratio = SCALE_UNIT; /* FIXME what is the right value? (scale_size is not) */
1453         SpuRenderText( p_spu, &b_rerender_text, p_subpic, p_region,
1454                        i_min_scale_ratio, render_date );
1455         b_restore_format = b_rerender_text;
1456
1457         /* Check if the rendering has failed ... */
1458         if( p_region->fmt.i_chroma == VLC_CODEC_TEXT )
1459             goto exit;
1460     }
1461
1462     /* Force palette if requested
1463      * FIXME b_force_palette and b_force_crop are applied to all subpictures using palette
1464      * instead of only the right one (being the dvd spu).
1465      */
1466     const bool b_using_palette = p_region->fmt.i_chroma == VLC_CODEC_YUVP;
1467     const bool b_force_palette = b_using_palette && p_sys->b_force_palette;
1468     const bool b_force_crop    = b_force_palette && p_sys->b_force_crop;
1469     bool b_changed_palette     = false;
1470
1471
1472     /* Compute the margin which is expressed in destination pixel unit
1473      * The margin is applied only to subtitle and when no forced crop is
1474      * requested (dvd menu) */
1475     int i_margin_y = 0;
1476     if( !b_force_crop && p_subpic->b_subtitle )
1477         i_margin_y = spu_invscale_h( p_sys->i_margin, scale_size );
1478
1479     /* Place the picture
1480      * We compute the position in the rendered size */
1481     SpuRegionPlace( &i_x_offset, &i_y_offset,
1482                     p_subpic, p_region );
1483
1484     /* Save this position for subtitle overlap support
1485      * it is really important that there are given without scale_size applied */
1486     *p_area = spu_area_create( i_x_offset, i_y_offset,
1487                                p_region->fmt.i_width, p_region->fmt.i_height,
1488                                scale_size );
1489
1490     /* Handle overlapping subtitles when possible */
1491     if( p_subpic->b_subtitle && !p_subpic->b_absolute )
1492     {
1493         SpuAreaFixOverlap( p_area, p_subtitle_area, i_subtitle_area,
1494                            p_region->i_align );
1495     }
1496
1497     /* we copy the area: for the subtitle overlap support we want
1498      * to only save the area without margin applied */
1499     spu_area_t restrained = *p_area;
1500
1501     /* apply margin to subtitles and correct if they go over the picture edge */
1502     if( p_subpic->b_subtitle )
1503     {
1504         restrained.i_y -= i_margin_y;
1505         spu_area_t display = spu_area_create( 0, 0, p_fmt->i_width, p_fmt->i_height,
1506                                               spu_scale_unit() );
1507         SpuAreaFitInside( &restrained, &display );
1508     }
1509
1510     /* Fix the position for the current scale_size */
1511     i_x_offset = spu_scale_w( restrained.i_x, restrained.scale );
1512     i_y_offset = spu_scale_h( restrained.i_y, restrained.scale );
1513
1514     /* */
1515     if( b_force_palette )
1516     {
1517         video_palette_t *p_palette = p_region->fmt.p_palette;
1518         video_palette_t palette;
1519
1520         /* We suppose DVD palette here */
1521         palette.i_entries = 4;
1522         for( int i = 0; i < 4; i++ )
1523             for( int j = 0; j < 4; j++ )
1524                 palette.palette[i][j] = p_sys->palette[i][j];
1525
1526         if( p_palette->i_entries == palette.i_entries )
1527         {
1528             for( int i = 0; i < p_palette->i_entries; i++ )
1529                 for( int j = 0; j < 4; j++ )
1530                     b_changed_palette |= p_palette->palette[i][j] != palette.palette[i][j];
1531         }
1532         else
1533         {
1534             b_changed_palette = true;
1535         }
1536         *p_palette = palette;
1537     }
1538
1539     /* */
1540     region_fmt = p_region->fmt;
1541     p_region_picture = p_region->p_picture;
1542
1543
1544     /* Scale from rendered size to destination size */
1545     if( p_sys->p_scale && p_sys->p_scale->p_module &&
1546         ( !b_using_palette || ( p_sys->p_scale_yuvp && p_sys->p_scale_yuvp->p_module ) ) &&
1547         ( scale_size.w != SCALE_UNIT || scale_size.h != SCALE_UNIT || b_using_palette ) )
1548     {
1549         const unsigned i_dst_width  = spu_scale_w( p_region->fmt.i_width, scale_size );
1550         const unsigned i_dst_height = spu_scale_h( p_region->fmt.i_height, scale_size );
1551
1552         /* Destroy the cache if unusable */
1553         if( p_region->p_private )
1554         {
1555             subpicture_region_private_t *p_private = p_region->p_private;
1556             bool b_changed = false;
1557
1558             /* Check resize changes */
1559             if( i_dst_width  != p_private->fmt.i_width ||
1560                 i_dst_height != p_private->fmt.i_height )
1561                 b_changed = true;
1562
1563             /* Check forced palette changes */
1564             if( b_changed_palette )
1565                 b_changed = true;
1566
1567             if( b_changed )
1568             {
1569                 SpuRegionPrivateDelete( p_private );
1570                 p_region->p_private = NULL;
1571             }
1572         }
1573
1574         /* Scale if needed into cache */
1575         if( !p_region->p_private && i_dst_width > 0 && i_dst_height > 0 )
1576         {
1577             filter_t *p_scale = p_sys->p_scale;
1578
1579             picture_t *p_picture = p_region->p_picture;
1580             picture_Hold( p_picture );
1581
1582             /* Convert YUVP to YUVA/RGBA first for better scaling quality */
1583             if( b_using_palette )
1584             {
1585                 filter_t *p_scale_yuvp = p_sys->p_scale_yuvp;
1586
1587                 p_scale_yuvp->fmt_in.video = p_region->fmt;
1588
1589                 /* TODO converting to RGBA for RGB video output is better */
1590                 p_scale_yuvp->fmt_out.video = p_region->fmt;
1591                 p_scale_yuvp->fmt_out.video.i_chroma = VLC_CODEC_YUVA;
1592
1593                 p_picture = p_scale_yuvp->pf_video_filter( p_scale_yuvp, p_picture );
1594                 if( !p_picture )
1595                 {
1596                     /* Well we will try conversion+scaling */
1597                     msg_Warn( p_spu, "%4.4s to %4.4s conversion failed",
1598                              (const char*)&p_scale_yuvp->fmt_in.video.i_chroma,
1599                              (const char*)&p_scale_yuvp->fmt_out.video.i_chroma );
1600                 }
1601             }
1602
1603             /* Conversion(except from YUVP)/Scaling */
1604             if( p_picture &&
1605                 ( p_picture->format.i_width != i_dst_width ||
1606                   p_picture->format.i_height != i_dst_height ) )
1607             {
1608                 p_scale->fmt_in.video = p_picture->format;
1609                 p_scale->fmt_out.video = p_picture->format;
1610
1611                 p_scale->fmt_out.video.i_width = i_dst_width;
1612                 p_scale->fmt_out.video.i_height = i_dst_height;
1613
1614                 p_scale->fmt_out.video.i_visible_width =
1615                     spu_scale_w( p_region->fmt.i_visible_width, scale_size );
1616                 p_scale->fmt_out.video.i_visible_height =
1617                     spu_scale_h( p_region->fmt.i_visible_height, scale_size );
1618
1619                 p_picture = p_scale->pf_video_filter( p_scale, p_picture );
1620                 if( !p_picture )
1621                     msg_Err( p_spu, "scaling failed" );
1622             }
1623
1624             /* */
1625             if( p_picture )
1626             {
1627                 p_region->p_private = SpuRegionPrivateNew( &p_picture->format );
1628                 if( p_region->p_private )
1629                 {
1630                     p_region->p_private->p_picture = p_picture;
1631                     if( !p_region->p_private->p_picture )
1632                     {
1633                         SpuRegionPrivateDelete( p_region->p_private );
1634                         p_region->p_private = NULL;
1635                     }
1636                 }
1637                 else
1638                 {
1639                     picture_Release( p_picture );
1640                 }
1641             }
1642         }
1643
1644         /* And use the scaled picture */
1645         if( p_region->p_private )
1646         {
1647             region_fmt = p_region->p_private->fmt;
1648             p_region_picture = p_region->p_private->p_picture;
1649         }
1650     }
1651
1652     /* Force cropping if requested */
1653     if( b_force_crop )
1654     {
1655         int i_crop_x = spu_scale_w( p_sys->i_crop_x, scale_size );
1656         int i_crop_y = spu_scale_h( p_sys->i_crop_y, scale_size );
1657         int i_crop_width = spu_scale_w( p_sys->i_crop_width, scale_size );
1658         int i_crop_height= spu_scale_h( p_sys->i_crop_height,scale_size );
1659
1660         /* Find the intersection */
1661         if( i_crop_x + i_crop_width <= i_x_offset ||
1662             i_x_offset + (int)region_fmt.i_visible_width < i_crop_x ||
1663             i_crop_y + i_crop_height <= i_y_offset ||
1664             i_y_offset + (int)region_fmt.i_visible_height < i_crop_y )
1665         {
1666             /* No intersection */
1667             region_fmt.i_visible_width =
1668             region_fmt.i_visible_height = 0;
1669         }
1670         else
1671         {
1672             int i_x, i_y, i_x_end, i_y_end;
1673             i_x = __MAX( i_crop_x, i_x_offset );
1674             i_y = __MAX( i_crop_y, i_y_offset );
1675             i_x_end = __MIN( i_crop_x + i_crop_width,
1676                            i_x_offset + (int)region_fmt.i_visible_width );
1677             i_y_end = __MIN( i_crop_y + i_crop_height,
1678                            i_y_offset + (int)region_fmt.i_visible_height );
1679
1680             region_fmt.i_x_offset = i_x - i_x_offset;
1681             region_fmt.i_y_offset = i_y - i_y_offset;
1682             region_fmt.i_visible_width = i_x_end - i_x;
1683             region_fmt.i_visible_height = i_y_end - i_y;
1684
1685             i_x_offset = __MAX( i_x, 0 );
1686             i_y_offset = __MAX( i_y, 0 );
1687         }
1688     }
1689
1690     /* Update the blender */
1691     if( filter_ConfigureBlend( p_spu->p->p_blend,
1692                                p_fmt->i_width, p_fmt->i_height,
1693                                &region_fmt ) ||
1694         filter_Blend( p_spu->p->p_blend,
1695                       p_pic_dst, i_x_offset, i_y_offset,
1696                       p_region_picture, SpuRegionAlpha( p_subpic, p_region ) ) )
1697     {
1698         msg_Err( p_spu, "blending %4.4s to %4.4s failed",
1699                  (char *)&p_sys->p_blend->fmt_in.video.i_chroma,
1700                  (char *)&p_sys->p_blend->fmt_out.video.i_chroma );
1701     }
1702
1703 exit:
1704     if( b_rerender_text )
1705     {
1706         /* Some forms of subtitles need to be re-rendered more than
1707          * once, eg. karaoke. We therefore restore the region to its
1708          * pre-rendered state, so the next time through everything is
1709          * calculated again.
1710          */
1711         if( p_region->p_picture )
1712         {
1713             picture_Release( p_region->p_picture );
1714             p_region->p_picture = NULL;
1715         }
1716         if( p_region->p_private )
1717         {
1718             SpuRegionPrivateDelete( p_region->p_private );
1719             p_region->p_private = NULL;
1720         }
1721         p_region->i_align &= ~SUBPICTURE_RENDERED;
1722     }
1723     if( b_restore_format )
1724         p_region->fmt = fmt_original;
1725 }
1726
1727 /**
1728  * This function compares two 64 bits integers.
1729  * It can be used by qsort.
1730  */
1731 static int IntegerCmp( int64_t i0, int64_t i1 )
1732 {
1733     return i0 < i1 ? -1 : i0 > i1 ? 1 : 0;
1734 }
1735 /**
1736  * This function compares 2 subpictures using the following properties
1737  * (ordered by priority)
1738  * 1. absolute positionning
1739  * 2. start time
1740  * 3. creation order (per channel)
1741  *
1742  * It can be used by qsort.
1743  *
1744  * XXX spu_RenderSubpictures depends heavily on this order.
1745  */
1746 static int SubpictureCmp( const void *s0, const void *s1 )
1747 {
1748     subpicture_t *p_subpic0 = *(subpicture_t**)s0;
1749     subpicture_t *p_subpic1 = *(subpicture_t**)s1;
1750     int r;
1751
1752     r = IntegerCmp( !p_subpic0->b_absolute, !p_subpic1->b_absolute );
1753     if( !r )
1754         r = IntegerCmp( p_subpic0->i_start, p_subpic1->i_start );
1755     if( !r )
1756         r = IntegerCmp( p_subpic0->i_channel, p_subpic1->i_channel );
1757     if( !r )
1758         r = IntegerCmp( p_subpic0->i_order, p_subpic1->i_order );
1759     return r;
1760 }
1761
1762 /*****************************************************************************
1763  * SpuClearChannel: clear an spu channel
1764  *****************************************************************************
1765  * This function destroys the subpictures which belong to the spu channel
1766  * corresponding to i_channel_id.
1767  *****************************************************************************/
1768 static void SpuClearChannel( spu_t *p_spu, int i_channel )
1769 {
1770     spu_private_t *p_sys = p_spu->p;
1771     int          i_subpic;                               /* subpicture index */
1772
1773     vlc_mutex_lock( &p_sys->lock );
1774
1775     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
1776     {
1777         spu_heap_entry_t *p_entry = &p_sys->heap.p_entry[i_subpic];
1778         subpicture_t *p_subpic = p_entry->p_subpicture;
1779
1780         if( !p_subpic )
1781             continue;
1782         if( p_subpic->i_channel != i_channel && ( i_channel != -1 || p_subpic->i_channel == DEFAULT_CHAN ) )
1783             continue;
1784
1785         /* You cannot delete subpicture outside of spu_SortSubpictures */
1786         p_entry->b_reject = true;
1787     }
1788
1789     vlc_mutex_unlock( &p_sys->lock );
1790 }
1791
1792 /*****************************************************************************
1793  * spu_ControlDefault: default methods for the subpicture unit control.
1794  *****************************************************************************/
1795 static int SpuControl( spu_t *p_spu, int i_query, va_list args )
1796 {
1797     spu_private_t *p_sys = p_spu->p;
1798     int *pi, i;
1799
1800     switch( i_query )
1801     {
1802     case SPU_CHANNEL_REGISTER:
1803         pi = (int *)va_arg( args, int * );
1804         vlc_mutex_lock( &p_sys->lock );
1805         if( pi )
1806             *pi = p_sys->i_channel++;
1807         vlc_mutex_unlock( &p_sys->lock );
1808         break;
1809
1810     case SPU_CHANNEL_CLEAR:
1811         i = (int)va_arg( args, int );
1812         SpuClearChannel( p_spu, i );
1813         break;
1814
1815     default:
1816         msg_Dbg( p_spu, "control query not supported" );
1817         return VLC_EGENERIC;
1818     }
1819
1820     return VLC_SUCCESS;
1821 }
1822
1823 /*****************************************************************************
1824  * Object variables callbacks
1825  *****************************************************************************/
1826
1827 /*****************************************************************************
1828  * UpdateSPU: update subpicture settings
1829  *****************************************************************************
1830  * This function is called from CropCallback and at initialization time, to
1831  * retrieve crop information from the input.
1832  *****************************************************************************/
1833 static void UpdateSPU( spu_t *p_spu, vlc_object_t *p_object )
1834 {
1835     spu_private_t *p_sys = p_spu->p;
1836     vlc_value_t val;
1837
1838     vlc_mutex_lock( &p_sys->lock );
1839
1840     p_sys->b_force_palette = false;
1841     p_sys->b_force_crop = false;
1842
1843     if( var_Get( p_object, "highlight", &val ) || !val.b_bool )
1844     {
1845         vlc_mutex_unlock( &p_sys->lock );
1846         return;
1847     }
1848
1849     p_sys->b_force_crop = true;
1850     p_sys->i_crop_x = var_GetInteger( p_object, "x-start" );
1851     p_sys->i_crop_y = var_GetInteger( p_object, "y-start" );
1852     p_sys->i_crop_width  = var_GetInteger( p_object, "x-end" ) - p_sys->i_crop_x;
1853     p_sys->i_crop_height = var_GetInteger( p_object, "y-end" ) - p_sys->i_crop_y;
1854
1855     if( var_Get( p_object, "menu-palette", &val ) == VLC_SUCCESS )
1856     {
1857         memcpy( p_sys->palette, val.p_address, 16 );
1858         p_sys->b_force_palette = true;
1859     }
1860     vlc_mutex_unlock( &p_sys->lock );
1861
1862     msg_Dbg( p_object, "crop: %i,%i,%i,%i, palette forced: %i",
1863              p_sys->i_crop_x, p_sys->i_crop_y,
1864              p_sys->i_crop_width, p_sys->i_crop_height,
1865              p_sys->b_force_palette );
1866 }
1867
1868 /*****************************************************************************
1869  * CropCallback: called when the highlight properties are changed
1870  *****************************************************************************
1871  * This callback is called from the input thread when we need cropping
1872  *****************************************************************************/
1873 static int CropCallback( vlc_object_t *p_object, char const *psz_var,
1874                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1875 {
1876     VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(psz_var);
1877
1878     UpdateSPU( (spu_t *)p_data, p_object );
1879     return VLC_SUCCESS;
1880 }
1881
1882 /*****************************************************************************
1883  * MarginCallback: called when requested subtitle position has changed         *
1884  *****************************************************************************/
1885
1886 static int MarginCallback( vlc_object_t *p_object, char const *psz_var,
1887                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1888 {
1889     VLC_UNUSED( psz_var ); VLC_UNUSED( oldval ); VLC_UNUSED( p_object );
1890     spu_private_t *p_sys = ( spu_private_t* ) p_data;
1891
1892     vlc_mutex_lock( &p_sys->lock );
1893     p_sys->i_margin = newval.i_int;
1894     vlc_mutex_unlock( &p_sys->lock );
1895     return VLC_SUCCESS;
1896 }
1897
1898 /*****************************************************************************
1899  * Buffers allocation callbacks for the filters
1900  *****************************************************************************/
1901 struct filter_owner_sys_t
1902 {
1903     spu_t *p_spu;
1904     int i_channel;
1905 };
1906
1907 static subpicture_t *sub_new_buffer( filter_t *p_filter )
1908 {
1909     filter_owner_sys_t *p_sys = p_filter->p_owner;
1910
1911     subpicture_t *p_subpicture = subpicture_New( NULL );
1912     if( p_subpicture )
1913         p_subpicture->i_channel = p_sys->i_channel;
1914     return p_subpicture;
1915 }
1916 static void sub_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1917 {
1918     VLC_UNUSED( p_filter );
1919     subpicture_Delete( p_subpic );
1920 }
1921
1922 static subpicture_t *spu_new_buffer( filter_t *p_filter )
1923 {
1924     VLC_UNUSED(p_filter);
1925     return subpicture_New( NULL );
1926 }
1927 static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1928 {
1929     VLC_UNUSED(p_filter);
1930     subpicture_Delete( p_subpic );
1931 }
1932
1933 static picture_t *spu_new_video_buffer( filter_t *p_filter )
1934 {
1935     const video_format_t *p_fmt = &p_filter->fmt_out.video;
1936
1937     VLC_UNUSED(p_filter);
1938     return picture_NewFromFormat( p_fmt );
1939 }
1940 static void spu_del_video_buffer( filter_t *p_filter, picture_t *p_picture )
1941 {
1942     VLC_UNUSED(p_filter);
1943     picture_Release( p_picture );
1944 }
1945
1946 static int SubFilterAllocationInit( filter_t *p_filter, void *p_data )
1947 {
1948     spu_t *p_spu = p_data;
1949
1950     filter_owner_sys_t *p_sys = malloc( sizeof(filter_owner_sys_t) );
1951     if( !p_sys )
1952         return VLC_EGENERIC;
1953
1954     p_filter->pf_sub_buffer_new = sub_new_buffer;
1955     p_filter->pf_sub_buffer_del = sub_del_buffer;
1956
1957     p_filter->p_owner = p_sys;
1958     spu_Control( p_spu, SPU_CHANNEL_REGISTER, &p_sys->i_channel );
1959     p_sys->p_spu = p_spu;
1960
1961     return VLC_SUCCESS;
1962 }
1963
1964 static void SubFilterAllocationClean( filter_t *p_filter )
1965 {
1966     filter_owner_sys_t *p_sys = p_filter->p_owner;
1967
1968     SpuClearChannel( p_sys->p_spu, p_sys->i_channel );
1969     free( p_filter->p_owner );
1970 }
1971
1972 static int SubFilterCallback( vlc_object_t *p_object, char const *psz_var,
1973                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1974 {
1975     spu_t *p_spu = p_data;
1976     spu_private_t *p_sys = p_spu->p;
1977
1978     VLC_UNUSED(p_object); VLC_UNUSED(oldval); VLC_UNUSED(psz_var);
1979
1980     vlc_mutex_lock( &p_sys->lock );
1981
1982     free( p_sys->psz_chain_update );
1983     p_sys->psz_chain_update = strdup( newval.psz_string );
1984
1985     vlc_mutex_unlock( &p_sys->lock );
1986     return VLC_SUCCESS;
1987 }
1988