]> git.sesse.net Git - vlc/blob - src/video_output/vout_subpictures.c
Refactor: rename "sub-filter" to "sub-source" this includes capability, functions...
[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_modules.h>
38 #include <vlc_input.h>
39 #include <vlc_vout.h>
40 #include <vlc_filter.h>
41 #include <vlc_spu.h>
42
43 #include "../libvlc.h"
44 #include "vout_internal.h"
45 #include "../misc/subpicture.h"
46
47 /*****************************************************************************
48  * Local prototypes
49  *****************************************************************************/
50
51 /* Number of simultaneous subpictures */
52 #define VOUT_MAX_SUBPICTURES (__MAX(VOUT_MAX_PICTURES, SPU_MAX_PREPARE_TIME/5000))
53
54 /* */
55 typedef struct
56 {
57     subpicture_t *p_subpicture;
58     bool          b_reject;
59 } spu_heap_entry_t;
60
61 typedef struct
62 {
63     spu_heap_entry_t p_entry[VOUT_MAX_SUBPICTURES];
64
65 } spu_heap_t;
66
67 struct spu_private_t
68 {
69     vlc_mutex_t lock;   /* lock to protect all followings fields */
70     vlc_object_t *p_input;
71
72     spu_heap_t heap;
73
74     int i_channel;             /**< number of subpicture channels registered */
75     filter_t *p_text;                              /**< text renderer module */
76     filter_t *p_scale_yuvp;                     /**< scaling module for YUVP */
77     filter_t *p_scale;                    /**< scaling module (all but YUVP) */
78     bool b_force_crop;                     /**< force cropping of subpicture */
79     int i_crop_x, i_crop_y, i_crop_width, i_crop_height;       /**< cropping */
80
81     int i_margin;                        /**< force position of a subpicture */
82     bool b_force_palette;             /**< force palette of subpicture */
83     uint8_t palette[4][4];                               /**< forced palette */
84
85     /* Subpiture filters */
86     char           *psz_chain_update;
87     vlc_mutex_t    chain_lock;
88     filter_chain_t *p_chain;
89
90     /* */
91     mtime_t i_last_sort_date;
92 };
93
94 /*****************************************************************************
95  * heap managment
96  *****************************************************************************/
97 static void SpuHeapInit( spu_heap_t *p_heap )
98 {
99     for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
100     {
101         spu_heap_entry_t *e = &p_heap->p_entry[i];
102
103         e->p_subpicture = NULL;
104         e->b_reject = false;
105     }
106 }
107
108 static int SpuHeapPush( spu_heap_t *p_heap, subpicture_t *p_subpic )
109 {
110     for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
111     {
112         spu_heap_entry_t *e = &p_heap->p_entry[i];
113
114         if( e->p_subpicture )
115             continue;
116
117         e->p_subpicture = p_subpic;
118         e->b_reject = false;
119         return VLC_SUCCESS;
120     }
121     return VLC_EGENERIC;
122 }
123
124 static void SpuHeapDeleteAt( spu_heap_t *p_heap, int i_index )
125 {
126     spu_heap_entry_t *e = &p_heap->p_entry[i_index];
127
128     if( e->p_subpicture )
129         subpicture_Delete( e->p_subpicture );
130
131     e->p_subpicture = NULL;
132 }
133
134 static int SpuHeapDeleteSubpicture( spu_heap_t *p_heap, subpicture_t *p_subpic )
135 {
136     for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
137     {
138         spu_heap_entry_t *e = &p_heap->p_entry[i];
139
140         if( e->p_subpicture != p_subpic )
141             continue;
142
143         SpuHeapDeleteAt( p_heap, i );
144         return VLC_SUCCESS;
145     }
146     return VLC_EGENERIC;
147 }
148
149 static void SpuHeapClean( spu_heap_t *p_heap )
150 {
151     for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
152     {
153         spu_heap_entry_t *e = &p_heap->p_entry[i];
154         if( e->p_subpicture )
155             subpicture_Delete( e->p_subpicture );
156     }
157 }
158
159 struct filter_owner_sys_t
160 {
161     spu_t *p_spu;
162     int i_channel;
163 };
164
165 static void FilterRelease( filter_t *p_filter )
166 {
167     if( p_filter->p_module )
168         module_unneed( p_filter, p_filter->p_module );
169     if( p_filter->p_owner )
170         free( p_filter->p_owner );
171
172     vlc_object_release( p_filter );
173 }
174
175 static picture_t *spu_new_video_buffer( filter_t *p_filter )
176 {
177     const video_format_t *p_fmt = &p_filter->fmt_out.video;
178
179     VLC_UNUSED(p_filter);
180     return picture_NewFromFormat( p_fmt );
181 }
182 static void spu_del_video_buffer( filter_t *p_filter, picture_t *p_picture )
183 {
184     VLC_UNUSED(p_filter);
185     picture_Release( p_picture );
186 }
187
188 static int spu_get_attachments( filter_t *p_filter,
189                                 input_attachment_t ***ppp_attachment,
190                                 int *pi_attachment )
191 {
192     spu_t *p_spu = p_filter->p_owner->p_spu;
193
194     int i_ret = VLC_EGENERIC;
195     if( p_spu->p->p_input )
196         i_ret = input_Control( (input_thread_t*)p_spu->p->p_input,
197                                INPUT_GET_ATTACHMENTS,
198                                ppp_attachment, pi_attachment );
199     return i_ret;
200 }
201
202 static filter_t *SpuRenderCreateAndLoadText( spu_t *p_spu )
203 {
204     filter_t *p_text = vlc_custom_create( p_spu, sizeof(*p_text),
205                                           VLC_OBJECT_GENERIC, "spu text" );
206     if( !p_text )
207         return NULL;
208
209     p_text->p_owner = xmalloc( sizeof(*p_text->p_owner) );
210     p_text->p_owner->p_spu = p_spu;
211
212     es_format_Init( &p_text->fmt_in, VIDEO_ES, 0 );
213
214     es_format_Init( &p_text->fmt_out, VIDEO_ES, 0 );
215     p_text->fmt_out.video.i_width =
216     p_text->fmt_out.video.i_visible_width = 32;
217     p_text->fmt_out.video.i_height =
218     p_text->fmt_out.video.i_visible_height = 32;
219
220     p_text->pf_get_attachments = spu_get_attachments;
221
222     vlc_object_attach( p_text, p_spu );
223     p_text->p_module = module_need( p_text, "text renderer", "$text-renderer", false );
224
225     /* Create a few variables used for enhanced text rendering */
226     var_Create( p_text, "spu-elapsed", VLC_VAR_TIME );
227     var_Create( p_text, "text-rerender", VLC_VAR_BOOL );
228     var_Create( p_text, "scale", VLC_VAR_INTEGER );
229
230     return p_text;
231 }
232
233 static filter_t *SpuRenderCreateAndLoadScale( vlc_object_t *p_obj,
234                                               vlc_fourcc_t i_src_chroma, vlc_fourcc_t i_dst_chroma,
235                                               bool b_resize )
236 {
237     filter_t *p_scale = vlc_custom_create( p_obj, sizeof(*p_scale),
238                                            VLC_OBJECT_GENERIC, "scale" );
239     if( !p_scale )
240         return NULL;
241
242     es_format_Init( &p_scale->fmt_in, VIDEO_ES, 0 );
243     p_scale->fmt_in.video.i_chroma = i_src_chroma;
244     p_scale->fmt_in.video.i_width =
245     p_scale->fmt_in.video.i_height = 32;
246
247     es_format_Init( &p_scale->fmt_out, VIDEO_ES, 0 );
248     p_scale->fmt_out.video.i_chroma = i_dst_chroma;
249     p_scale->fmt_out.video.i_width =
250     p_scale->fmt_out.video.i_height = b_resize ? 16 : 32;
251
252     p_scale->pf_video_buffer_new = spu_new_video_buffer;
253     p_scale->pf_video_buffer_del = spu_del_video_buffer;
254
255     vlc_object_attach( p_scale, p_obj );
256     p_scale->p_module = module_need( p_scale, "video filter2", NULL, false );
257
258     return p_scale;
259 }
260
261 static void SpuRenderText( spu_t *p_spu, bool *pb_rerender_text,
262                            subpicture_region_t *p_region,
263                            mtime_t render_date )
264 {
265     filter_t *p_text = p_spu->p->p_text;
266
267     assert( p_region->fmt.i_chroma == VLC_CODEC_TEXT );
268
269     if( !p_text || !p_text->p_module )
270         return;
271
272     /* Setup 3 variables which can be used to render
273      * time-dependent text (and effects). The first indicates
274      * the total amount of time the text will be on screen,
275      * the second the amount of time it has already been on
276      * screen (can be a negative value as text is layed out
277      * before it is rendered) and the third is a feedback
278      * variable from the renderer - if the renderer sets it
279      * then this particular text is time-dependent, eg. the
280      * visual progress bar inside the text in karaoke and the
281      * text needs to be rendered multiple times in order for
282      * the effect to work - we therefore need to return the
283      * region to its original state at the end of the loop,
284      * instead of leaving it in YUVA or YUVP.
285      * Any renderer which is unaware of how to render
286      * time-dependent text can happily ignore the variables
287      * and render the text the same as usual - it should at
288      * least show up on screen, but the effect won't change
289      * the text over time.
290      */
291     var_SetTime( p_text, "spu-elapsed", render_date );
292     var_SetBool( p_text, "text-rerender", false );
293
294     if( p_text->pf_render_html && p_region->psz_html )
295     {
296         p_text->pf_render_html( p_text, p_region, p_region );
297     }
298     else if( p_text->pf_render_text )
299     {
300         p_text->pf_render_text( p_text, p_region, p_region );
301     }
302     *pb_rerender_text = var_GetBool( p_text, "text-rerender" );
303 }
304
305 /**
306  * A few scale functions helpers.
307  */
308
309 #define SCALE_UNIT (1000)
310 typedef struct
311 {
312     int w;
313     int h;
314 } spu_scale_t;
315
316 static spu_scale_t spu_scale_create( int w, int h )
317 {
318     spu_scale_t s = { .w = w, .h = h };
319     if( s.w <= 0 )
320         s.w = SCALE_UNIT;
321     if( s.h <= 0 )
322         s.h = SCALE_UNIT;
323     return s;
324 }
325 static spu_scale_t spu_scale_unit( void )
326 {
327     return spu_scale_create( SCALE_UNIT, SCALE_UNIT );
328 }
329 static spu_scale_t spu_scale_createq( int64_t wn, int64_t wd, int64_t hn, int64_t hd )
330 {
331     return spu_scale_create( wn * SCALE_UNIT / wd,
332                              hn * SCALE_UNIT / hd );
333 }
334 static int spu_scale_w( int v, const spu_scale_t s )
335 {
336     return v * s.w / SCALE_UNIT;
337 }
338 static int spu_scale_h( int v, const spu_scale_t s )
339 {
340     return v * s.h / SCALE_UNIT;
341 }
342 static int spu_invscale_w( int v, const spu_scale_t s )
343 {
344     return v * SCALE_UNIT / s.w;
345 }
346 static int spu_invscale_h( int v, const spu_scale_t s )
347 {
348     return v * SCALE_UNIT / s.h;
349 }
350
351 /**
352  * A few area functions helpers
353  */
354 typedef struct
355 {
356     int i_x;
357     int i_y;
358     int i_width;
359     int i_height;
360
361     spu_scale_t scale;
362 } spu_area_t;
363
364 static spu_area_t spu_area_create( int x, int y, int w, int h, spu_scale_t s )
365 {
366     spu_area_t a = { .i_x = x, .i_y = y, .i_width = w, .i_height = h, .scale = s };
367     return a;
368 }
369 static spu_area_t spu_area_scaled( spu_area_t a )
370 {
371     if( a.scale.w == SCALE_UNIT && a.scale.h == SCALE_UNIT )
372         return a;
373
374     a.i_x = spu_scale_w( a.i_x, a.scale );
375     a.i_y = spu_scale_h( a.i_y, a.scale );
376
377     a.i_width  = spu_scale_w( a.i_width,  a.scale );
378     a.i_height = spu_scale_h( a.i_height, a.scale );
379
380     a.scale = spu_scale_unit();
381     return a;
382 }
383 static spu_area_t spu_area_unscaled( spu_area_t a, spu_scale_t s )
384 {
385     if( a.scale.w == s.w && a.scale.h == s.h )
386         return a;
387
388     a = spu_area_scaled( a );
389
390     a.i_x = spu_invscale_w( a.i_x, s );
391     a.i_y = spu_invscale_h( a.i_y, s );
392
393     a.i_width  = spu_invscale_w( a.i_width, s );
394     a.i_height = spu_invscale_h( a.i_height, s );
395
396     a.scale = s;
397     return a;
398 }
399 static bool spu_area_overlap( spu_area_t a, spu_area_t b )
400 {
401     const int i_dx = 0;
402     const int i_dy = 0;
403
404     a = spu_area_scaled( a );
405     b = spu_area_scaled( b );
406
407     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  ) &&
408             __MAX( a.i_y-i_dy, b.i_y ) < __MIN( a.i_y+a.i_height+i_dy, b.i_y+b.i_height );
409 }
410
411 /**
412  * Avoid area overlapping
413  */
414 static void SpuAreaFixOverlap( spu_area_t *p_dst,
415                                const spu_area_t *p_sub, int i_sub, int i_align )
416 {
417     spu_area_t a = spu_area_scaled( *p_dst );
418     bool b_moved = false;
419     bool b_ok;
420
421     /* Check for overlap
422      * XXX It is not fast O(n^2) but we should not have a lot of region */
423     do
424     {
425         b_ok = true;
426         for( int i = 0; i < i_sub; i++ )
427         {
428             spu_area_t sub = spu_area_scaled( p_sub[i] );
429
430             if( !spu_area_overlap( a, sub ) )
431                 continue;
432
433             if( i_align & SUBPICTURE_ALIGN_TOP )
434             {
435                 /* We go down */
436                 int i_y = sub.i_y + sub.i_height;
437                 a.i_y = i_y;
438                 b_moved = true;
439             }
440             else if( i_align & SUBPICTURE_ALIGN_BOTTOM )
441             {
442                 /* We go up */
443                 int i_y = sub.i_y - a.i_height;
444                 a.i_y = i_y;
445                 b_moved = true;
446             }
447             else
448             {
449                 /* TODO what to do in this case? */
450                 //fprintf( stderr, "Overlap with unsupported alignment\n" );
451                 break;
452             }
453
454             b_ok = false;
455             break;
456         }
457     } while( !b_ok );
458
459     if( b_moved )
460         *p_dst = spu_area_unscaled( a, p_dst->scale );
461 }
462
463
464 static void SpuAreaFitInside( spu_area_t *p_area, const spu_area_t *p_boundary )
465 {
466   spu_area_t a = spu_area_scaled( *p_area );
467
468   const int i_error_x = (a.i_x + a.i_width) - p_boundary->i_width;
469   if( i_error_x > 0 )
470       a.i_x -= i_error_x;
471   if( a.i_x < 0 )
472       a.i_x = 0;
473
474   const int i_error_y = (a.i_y + a.i_height) - p_boundary->i_height;
475   if( i_error_y > 0 )
476       a.i_y -= i_error_y;
477   if( a.i_y < 0 )
478       a.i_y = 0;
479
480   *p_area = spu_area_unscaled( a, p_area->scale );
481 }
482
483 /**
484  * Place a region
485  */
486 static void SpuRegionPlace( int *pi_x, int *pi_y,
487                             const subpicture_t *p_subpic,
488                             const subpicture_region_t *p_region )
489 {
490     assert( p_region->i_x != INT_MAX && p_region->i_y != INT_MAX );
491     if( p_subpic->b_absolute )
492     {
493         *pi_x = p_region->i_x;
494         *pi_y = p_region->i_y;
495     }
496     else
497     {
498         if( p_region->i_align & SUBPICTURE_ALIGN_TOP )
499         {
500             *pi_y = p_region->i_y;
501         }
502         else if( p_region->i_align & SUBPICTURE_ALIGN_BOTTOM )
503         {
504             *pi_y = p_subpic->i_original_picture_height - p_region->fmt.i_height - p_region->i_y;
505         }
506         else
507         {
508             *pi_y = p_subpic->i_original_picture_height / 2 - p_region->fmt.i_height / 2;
509         }
510
511         if( p_region->i_align & SUBPICTURE_ALIGN_LEFT )
512         {
513             *pi_x = p_region->i_x;
514         }
515         else if( p_region->i_align & SUBPICTURE_ALIGN_RIGHT )
516         {
517             *pi_x = p_subpic->i_original_picture_width - p_region->fmt.i_width - p_region->i_x;
518         }
519         else
520         {
521             *pi_x = p_subpic->i_original_picture_width / 2 - p_region->fmt.i_width / 2;
522         }
523     }
524 }
525
526 /**
527  * This function compares two 64 bits integers.
528  * It can be used by qsort.
529  */
530 static int IntegerCmp( int64_t i0, int64_t i1 )
531 {
532     return i0 < i1 ? -1 : i0 > i1 ? 1 : 0;
533 }
534 /**
535  * This function compares 2 subpictures using the following properties
536  * (ordered by priority)
537  * 1. absolute positionning
538  * 2. start time
539  * 3. creation order (per channel)
540  *
541  * It can be used by qsort.
542  *
543  * XXX spu_RenderSubpictures depends heavily on this order.
544  */
545 static int SubpictureCmp( const void *s0, const void *s1 )
546 {
547     subpicture_t *p_subpic0 = *(subpicture_t**)s0;
548     subpicture_t *p_subpic1 = *(subpicture_t**)s1;
549     int r;
550
551     r = IntegerCmp( !p_subpic0->b_absolute, !p_subpic1->b_absolute );
552     if( !r )
553         r = IntegerCmp( p_subpic0->i_start, p_subpic1->i_start );
554     if( !r )
555         r = IntegerCmp( p_subpic0->i_channel, p_subpic1->i_channel );
556     if( !r )
557         r = IntegerCmp( p_subpic0->i_order, p_subpic1->i_order );
558     return r;
559 }
560
561 /*****************************************************************************
562  * SpuSelectSubpictures: find the subpictures to display
563  *****************************************************************************
564  * This function parses all subpictures and decides which ones need to be
565  * displayed. If no picture has been selected, display_date will depend on
566  * the subpicture.
567  * We also check for ephemer DVD subpictures (subpictures that have
568  * to be removed if a newer one is available), which makes it a lot
569  * more difficult to guess if a subpicture has to be rendered or not.
570  *****************************************************************************/
571 static void SpuSelectSubpictures( spu_t *p_spu,
572                                   unsigned int *pi_subpicture,
573                                   subpicture_t **pp_subpicture,
574                                   mtime_t render_subtitle_date,
575                                   mtime_t render_osd_date,
576                                   bool b_subtitle_only )
577 {
578     spu_private_t *p_sys = p_spu->p;
579
580     /* */
581     *pi_subpicture = 0;
582
583     /* Create a list of channels */
584     int pi_channel[VOUT_MAX_SUBPICTURES];
585     int i_channel_count = 0;
586
587     for( int i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
588     {
589         spu_heap_entry_t *p_entry = &p_sys->heap.p_entry[i_index];
590         if( !p_entry->p_subpicture || p_entry->b_reject )
591             continue;
592         const int i_channel = p_entry->p_subpicture->i_channel;
593         int i;
594         for( i = 0; i < i_channel_count; i++ )
595         {
596             if( pi_channel[i] == i_channel )
597                 break;
598         }
599         if( i_channel_count <= i )
600             pi_channel[i_channel_count++] = i_channel;
601     }
602
603     /* Fill up the pp_subpicture arrays with relevent pictures */
604     for( int i = 0; i < i_channel_count; i++ )
605     {
606         const int i_channel = pi_channel[i];
607         subpicture_t *p_available_subpic[VOUT_MAX_SUBPICTURES];
608         bool         pb_available_late[VOUT_MAX_SUBPICTURES];
609         int          i_available = 0;
610
611         mtime_t      start_date = render_subtitle_date;
612         mtime_t      ephemer_subtitle_date = 0;
613         mtime_t      ephemer_osd_date = 0;
614         int64_t      i_ephemer_subtitle_order = INT64_MIN;
615         int64_t      i_ephemer_system_order = INT64_MIN;
616         int i_index;
617
618         /* Select available pictures */
619         for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
620         {
621             spu_heap_entry_t *p_entry = &p_sys->heap.p_entry[i_index];
622             subpicture_t *p_current = p_entry->p_subpicture;
623             bool b_stop_valid;
624             bool b_late;
625
626             if( !p_current || p_entry->b_reject )
627             {
628                 if( p_entry->b_reject )
629                     SpuHeapDeleteAt( &p_sys->heap, i_index );
630                 continue;
631             }
632
633             if( p_current->i_channel != i_channel ||
634                 ( b_subtitle_only && !p_current->b_subtitle ) )
635             {
636                 continue;
637             }
638             const mtime_t render_date = p_current->b_subtitle ? render_subtitle_date : render_osd_date;
639             if( render_date &&
640                 render_date < p_current->i_start )
641             {
642                 /* Too early, come back next monday */
643                 continue;
644             }
645
646             mtime_t *pi_ephemer_date  = p_current->b_subtitle ? &ephemer_subtitle_date : &ephemer_osd_date;
647             int64_t *pi_ephemer_order = p_current->b_subtitle ? &i_ephemer_subtitle_order : &i_ephemer_system_order;
648             if( p_current->i_start >= *pi_ephemer_date )
649             {
650                 *pi_ephemer_date = p_current->i_start;
651                 if( p_current->i_order > *pi_ephemer_order )
652                     *pi_ephemer_order = p_current->i_order;
653             }
654
655             b_stop_valid = !p_current->b_ephemer || p_current->i_stop > p_current->i_start;
656
657             b_late = b_stop_valid && p_current->i_stop <= render_date;
658
659             /* start_date will be used for correct automatic overlap support
660              * in case picture that should not be displayed anymore (display_time)
661              * overlap with a picture to be displayed (p_current->i_start)  */
662             if( p_current->b_subtitle && !b_late && !p_current->b_ephemer )
663                 start_date = p_current->i_start;
664
665             /* */
666             p_available_subpic[i_available] = p_current;
667             pb_available_late[i_available] = b_late;
668             i_available++;
669         }
670
671         /* Only forced old picture display at the transition */
672         if( start_date < p_sys->i_last_sort_date )
673             start_date = p_sys->i_last_sort_date;
674         if( start_date <= 0 )
675             start_date = INT64_MAX;
676
677         /* Select pictures to be displayed */
678         for( i_index = 0; i_index < i_available; i_index++ )
679         {
680             subpicture_t *p_current = p_available_subpic[i_index];
681             bool b_late = pb_available_late[i_index];
682
683             const mtime_t stop_date = p_current->b_subtitle ? __MAX( start_date, p_sys->i_last_sort_date ) : render_osd_date;
684             const mtime_t ephemer_date = p_current->b_subtitle ? ephemer_subtitle_date : ephemer_osd_date;
685             const int64_t i_ephemer_order = p_current->b_subtitle ? i_ephemer_subtitle_order : i_ephemer_system_order;
686
687             /* Destroy late and obsolete ephemer subpictures */
688             bool b_rejet = b_late && p_current->i_stop <= stop_date;
689             if( p_current->b_ephemer )
690             {
691                 if( p_current->i_start < ephemer_date )
692                     b_rejet = true;
693                 else if( p_current->i_start == ephemer_date &&
694                          p_current->i_order < i_ephemer_order )
695                     b_rejet = true;
696             }
697
698             if( b_rejet )
699                 SpuHeapDeleteSubpicture( &p_sys->heap, p_current );
700             else
701                 pp_subpicture[(*pi_subpicture)++] = p_current;
702         }
703     }
704
705     p_sys->i_last_sort_date = render_subtitle_date;
706 }
707
708
709
710 /**
711  * It will transform the provided region into another region suitable for rendering.
712  */
713
714 static void SpuRenderRegion( spu_t *p_spu,
715                              subpicture_region_t **pp_dst, spu_area_t *p_dst_area,
716                              subpicture_t *p_subpic, subpicture_region_t *p_region,
717                              const spu_scale_t scale_size,
718                              const vlc_fourcc_t *p_chroma_list,
719                              const video_format_t *p_fmt,
720                              const spu_area_t *p_subtitle_area, int i_subtitle_area,
721                              mtime_t render_date )
722 {
723     spu_private_t *p_sys = p_spu->p;
724
725     video_format_t fmt_original = p_region->fmt;
726     bool b_restore_text = false;
727     int i_x_offset;
728     int i_y_offset;
729
730     video_format_t region_fmt;
731     picture_t *p_region_picture;
732
733     /* Invalidate area by default */
734     *p_dst_area = spu_area_create( 0,0, 0,0, scale_size );
735     *pp_dst     = NULL;
736
737     /* Render text region */
738     if( p_region->fmt.i_chroma == VLC_CODEC_TEXT )
739     {
740         SpuRenderText( p_spu, &b_restore_text, p_region,
741                        render_date );
742
743         /* Check if the rendering has failed ... */
744         if( p_region->fmt.i_chroma == VLC_CODEC_TEXT )
745             goto exit;
746     }
747
748     /* Force palette if requested
749      * FIXME b_force_palette and b_force_crop are applied to all subpictures using palette
750      * instead of only the right one (being the dvd spu).
751      */
752     const bool b_using_palette = p_region->fmt.i_chroma == VLC_CODEC_YUVP;
753     const bool b_force_palette = b_using_palette && p_sys->b_force_palette;
754     const bool b_force_crop    = b_force_palette && p_sys->b_force_crop;
755     bool b_changed_palette     = false;
756
757     /* Compute the margin which is expressed in destination pixel unit
758      * The margin is applied only to subtitle and when no forced crop is
759      * requested (dvd menu) */
760     int i_margin_y = 0;
761     if( !b_force_crop && p_subpic->b_subtitle )
762         i_margin_y = spu_invscale_h( p_sys->i_margin, scale_size );
763
764     /* Place the picture
765      * We compute the position in the rendered size */
766     SpuRegionPlace( &i_x_offset, &i_y_offset,
767                     p_subpic, p_region );
768
769     /* Save this position for subtitle overlap support
770      * it is really important that there are given without scale_size applied */
771     *p_dst_area = spu_area_create( i_x_offset, i_y_offset,
772                                p_region->fmt.i_width, p_region->fmt.i_height,
773                                scale_size );
774
775     /* Handle overlapping subtitles when possible */
776     if( p_subpic->b_subtitle && !p_subpic->b_absolute )
777     {
778         SpuAreaFixOverlap( p_dst_area, p_subtitle_area, i_subtitle_area,
779                            p_region->i_align );
780     }
781
782     /* we copy the area: for the subtitle overlap support we want
783      * to only save the area without margin applied */
784     spu_area_t restrained = *p_dst_area;
785
786     /* apply margin to subtitles and correct if they go over the picture edge */
787     if( p_subpic->b_subtitle )
788         restrained.i_y -= i_margin_y;
789
790     spu_area_t display = spu_area_create( 0, 0, p_fmt->i_width, p_fmt->i_height,
791                                           spu_scale_unit() );
792     SpuAreaFitInside( &restrained, &display );
793
794     /* Fix the position for the current scale_size */
795     i_x_offset = spu_scale_w( restrained.i_x, restrained.scale );
796     i_y_offset = spu_scale_h( restrained.i_y, restrained.scale );
797
798     /* */
799     if( b_force_palette )
800     {
801         video_palette_t *p_palette = p_region->fmt.p_palette;
802         video_palette_t palette;
803
804         /* We suppose DVD palette here */
805         palette.i_entries = 4;
806         for( int i = 0; i < 4; i++ )
807             for( int j = 0; j < 4; j++ )
808                 palette.palette[i][j] = p_sys->palette[i][j];
809
810         if( p_palette->i_entries == palette.i_entries )
811         {
812             for( int i = 0; i < p_palette->i_entries; i++ )
813                 for( int j = 0; j < 4; j++ )
814                     b_changed_palette |= p_palette->palette[i][j] != palette.palette[i][j];
815         }
816         else
817         {
818             b_changed_palette = true;
819         }
820         *p_palette = palette;
821     }
822
823     /* */
824     region_fmt = p_region->fmt;
825     p_region_picture = p_region->p_picture;
826
827     bool b_convert_chroma = true;
828     for( int i = 0; p_chroma_list[i] && b_convert_chroma; i++ )
829     {
830         if( region_fmt.i_chroma == p_chroma_list[i] )
831             b_convert_chroma = false;
832     }
833
834     /* Scale from rendered size to destination size */
835     if( p_sys->p_scale && p_sys->p_scale->p_module &&
836         ( !b_using_palette || ( p_sys->p_scale_yuvp && p_sys->p_scale_yuvp->p_module ) ) &&
837         ( scale_size.w != SCALE_UNIT || scale_size.h != SCALE_UNIT ||
838           b_using_palette || b_convert_chroma) )
839     {
840         const unsigned i_dst_width  = spu_scale_w( p_region->fmt.i_width, scale_size );
841         const unsigned i_dst_height = spu_scale_h( p_region->fmt.i_height, scale_size );
842
843         /* Destroy the cache if unusable */
844         if( p_region->p_private )
845         {
846             subpicture_region_private_t *p_private = p_region->p_private;
847             bool b_changed = false;
848
849             /* Check resize changes */
850             if( i_dst_width  != p_private->fmt.i_width ||
851                 i_dst_height != p_private->fmt.i_height )
852                 b_changed = true;
853
854             /* Check forced palette changes */
855             if( b_changed_palette )
856                 b_changed = true;
857
858             if( b_convert_chroma && p_private->fmt.i_chroma != p_chroma_list[0] )
859                 b_changed = true;
860
861             if( b_changed )
862             {
863                 subpicture_region_private_Delete( p_private );
864                 p_region->p_private = NULL;
865             }
866         }
867
868         /* Scale if needed into cache */
869         if( !p_region->p_private && i_dst_width > 0 && i_dst_height > 0 )
870         {
871             filter_t *p_scale = p_sys->p_scale;
872
873             picture_t *p_picture = p_region->p_picture;
874             picture_Hold( p_picture );
875
876             /* Convert YUVP to YUVA/RGBA first for better scaling quality */
877             if( b_using_palette )
878             {
879                 filter_t *p_scale_yuvp = p_sys->p_scale_yuvp;
880
881                 p_scale_yuvp->fmt_in.video = p_region->fmt;
882
883                 p_scale_yuvp->fmt_out.video = p_region->fmt;
884                 p_scale_yuvp->fmt_out.video.i_chroma = p_chroma_list[0];
885
886                 p_picture = p_scale_yuvp->pf_video_filter( p_scale_yuvp, p_picture );
887                 if( !p_picture )
888                 {
889                     /* Well we will try conversion+scaling */
890                     msg_Warn( p_spu, "%4.4s to %4.4s conversion failed",
891                              (const char*)&p_scale_yuvp->fmt_in.video.i_chroma,
892                              (const char*)&p_scale_yuvp->fmt_out.video.i_chroma );
893                 }
894             }
895
896             /* Conversion(except from YUVP)/Scaling */
897             if( p_picture &&
898                 ( p_picture->format.i_width != i_dst_width ||
899                   p_picture->format.i_height != i_dst_height ||
900                   ( b_convert_chroma && !b_using_palette ) ) )
901             {
902                 p_scale->fmt_in.video = p_picture->format;
903                 p_scale->fmt_out.video = p_picture->format;
904                 if( b_convert_chroma )
905                     p_scale->fmt_out.i_codec        =
906                     p_scale->fmt_out.video.i_chroma = p_chroma_list[0];
907
908                 p_scale->fmt_out.video.i_width = i_dst_width;
909                 p_scale->fmt_out.video.i_height = i_dst_height;
910
911                 p_scale->fmt_out.video.i_visible_width =
912                     spu_scale_w( p_region->fmt.i_visible_width, scale_size );
913                 p_scale->fmt_out.video.i_visible_height =
914                     spu_scale_h( p_region->fmt.i_visible_height, scale_size );
915
916                 p_picture = p_scale->pf_video_filter( p_scale, p_picture );
917                 if( !p_picture )
918                     msg_Err( p_spu, "scaling failed" );
919             }
920
921             /* */
922             if( p_picture )
923             {
924                 p_region->p_private = subpicture_region_private_New( &p_picture->format );
925                 if( p_region->p_private )
926                 {
927                     p_region->p_private->p_picture = p_picture;
928                     if( !p_region->p_private->p_picture )
929                     {
930                         subpicture_region_private_Delete( p_region->p_private );
931                         p_region->p_private = NULL;
932                     }
933                 }
934                 else
935                 {
936                     picture_Release( p_picture );
937                 }
938             }
939         }
940
941         /* And use the scaled picture */
942         if( p_region->p_private )
943         {
944             region_fmt = p_region->p_private->fmt;
945             p_region_picture = p_region->p_private->p_picture;
946         }
947     }
948
949     /* Force cropping if requested */
950     if( b_force_crop )
951     {
952         int i_crop_x = spu_scale_w( p_sys->i_crop_x, scale_size );
953         int i_crop_y = spu_scale_h( p_sys->i_crop_y, scale_size );
954         int i_crop_width = spu_scale_w( p_sys->i_crop_width, scale_size );
955         int i_crop_height= spu_scale_h( p_sys->i_crop_height,scale_size );
956
957         /* Find the intersection */
958         if( i_crop_x + i_crop_width <= i_x_offset ||
959             i_x_offset + (int)region_fmt.i_visible_width < i_crop_x ||
960             i_crop_y + i_crop_height <= i_y_offset ||
961             i_y_offset + (int)region_fmt.i_visible_height < i_crop_y )
962         {
963             /* No intersection */
964             region_fmt.i_visible_width =
965             region_fmt.i_visible_height = 0;
966         }
967         else
968         {
969             int i_x, i_y, i_x_end, i_y_end;
970             i_x = __MAX( i_crop_x, i_x_offset );
971             i_y = __MAX( i_crop_y, i_y_offset );
972             i_x_end = __MIN( i_crop_x + i_crop_width,
973                            i_x_offset + (int)region_fmt.i_visible_width );
974             i_y_end = __MIN( i_crop_y + i_crop_height,
975                            i_y_offset + (int)region_fmt.i_visible_height );
976
977             region_fmt.i_x_offset = i_x - i_x_offset;
978             region_fmt.i_y_offset = i_y - i_y_offset;
979             region_fmt.i_visible_width = i_x_end - i_x;
980             region_fmt.i_visible_height = i_y_end - i_y;
981
982             i_x_offset = __MAX( i_x, 0 );
983             i_y_offset = __MAX( i_y, 0 );
984         }
985     }
986
987     subpicture_region_t *p_dst = *pp_dst = subpicture_region_New( &region_fmt );
988     if( p_dst )
989     {
990         p_dst->i_x       = i_x_offset;
991         p_dst->i_y       = i_y_offset;
992         p_dst->i_align   = 0;
993         if( p_dst->p_picture )
994             picture_Release( p_dst->p_picture );
995         p_dst->p_picture = picture_Hold( p_region_picture );
996         int i_fade_alpha = 255;
997         if( p_subpic->b_fade )
998         {
999             mtime_t fade_start = ( p_subpic->i_stop +
1000                                    p_subpic->i_start ) / 2;
1001
1002             if( fade_start <= render_date && fade_start < p_subpic->i_stop )
1003                 i_fade_alpha = 255 * ( p_subpic->i_stop - render_date ) /
1004                                      ( p_subpic->i_stop - fade_start );
1005         }
1006         p_dst->i_alpha   = i_fade_alpha * p_subpic->i_alpha * p_region->i_alpha / 65025;
1007     }
1008
1009 exit:
1010     if( b_restore_text )
1011     {
1012         /* Some forms of subtitles need to be re-rendered more than
1013          * once, eg. karaoke. We therefore restore the region to its
1014          * pre-rendered state, so the next time through everything is
1015          * calculated again.
1016          */
1017         if( p_region->p_picture )
1018         {
1019             picture_Release( p_region->p_picture );
1020             p_region->p_picture = NULL;
1021         }
1022         if( p_region->p_private )
1023         {
1024             subpicture_region_private_Delete( p_region->p_private );
1025             p_region->p_private = NULL;
1026         }
1027         p_region->fmt = fmt_original;
1028     }
1029 }
1030
1031 /**
1032  * This function renders all sub picture units in the list.
1033  */
1034 static subpicture_t *SpuRenderSubpictures( spu_t *p_spu,
1035                                            unsigned int i_subpicture,
1036                                            subpicture_t **pp_subpicture,
1037                                            const vlc_fourcc_t *p_chroma_list,
1038                                            const video_format_t *p_fmt_dst,
1039                                            const video_format_t *p_fmt_src,
1040                                            mtime_t render_subtitle_date,
1041                                            mtime_t render_osd_date )
1042 {
1043     spu_private_t *p_sys = p_spu->p;
1044
1045     /* Count the number of regions and subtitle regions */
1046     unsigned int i_subtitle_region_count = 0;
1047     unsigned int i_region_count          = 0;
1048     for( unsigned i = 0; i < i_subpicture; i++ )
1049     {
1050         const subpicture_t *p_subpic = pp_subpicture[i];
1051
1052         unsigned i_count = 0;
1053         for( subpicture_region_t *r = p_subpic->p_region; r != NULL; r = r->p_next )
1054             i_count++;
1055
1056         if( p_subpic->b_subtitle )
1057             i_subtitle_region_count += i_count;
1058         i_region_count += i_count;
1059     }
1060     if( i_region_count <= 0 )
1061         return NULL;
1062
1063     /* Create the output subpicture */
1064     subpicture_t *p_output = subpicture_New( NULL );
1065     if( !p_output )
1066         return NULL;
1067     p_output->i_original_picture_width  = p_fmt_dst->i_width;
1068     p_output->i_original_picture_height = p_fmt_dst->i_height;
1069     subpicture_region_t **pp_output_last = &p_output->p_region;
1070
1071     /* Allocate area array for subtitle overlap */
1072     spu_area_t p_subtitle_area_buffer[VOUT_MAX_SUBPICTURES];
1073     spu_area_t *p_subtitle_area;
1074     int i_subtitle_area;
1075
1076     i_subtitle_area = 0;
1077     p_subtitle_area = p_subtitle_area_buffer;
1078     if( i_subtitle_region_count > sizeof(p_subtitle_area_buffer)/sizeof(*p_subtitle_area_buffer) )
1079         p_subtitle_area = calloc( i_subtitle_region_count, sizeof(*p_subtitle_area) );
1080
1081     /* Process all subpictures and regions (in the right order) */
1082     for( unsigned int i_index = 0; i_index < i_subpicture; i_index++ )
1083     {
1084         subpicture_t *p_subpic = pp_subpicture[i_index];
1085         subpicture_region_t *p_region;
1086
1087         if( !p_subpic->p_region )
1088             continue;
1089
1090         if( p_subpic->i_original_picture_width  <= 0 ||
1091             p_subpic->i_original_picture_height <= 0 )
1092         {
1093             if( p_subpic->i_original_picture_width  > 0 ||
1094                 p_subpic->i_original_picture_height > 0 )
1095                 msg_Err( p_spu, "original picture size %dx%d is unsupported",
1096                          p_subpic->i_original_picture_width,
1097                          p_subpic->i_original_picture_height );
1098             else
1099                 msg_Warn( p_spu, "original picture size is undefined" );
1100
1101             p_subpic->i_original_picture_width  = p_fmt_src->i_width;
1102             p_subpic->i_original_picture_height = p_fmt_src->i_height;
1103         }
1104
1105         if( p_sys->p_text )
1106         {
1107             /* FIXME aspect ratio ? */
1108             p_sys->p_text->fmt_out.video.i_width          =
1109             p_sys->p_text->fmt_out.video.i_visible_width  = p_subpic->i_original_picture_width;
1110
1111             p_sys->p_text->fmt_out.video.i_height         =
1112             p_sys->p_text->fmt_out.video.i_visible_height = p_subpic->i_original_picture_height;
1113
1114             var_SetInteger( p_sys->p_text, "scale", SCALE_UNIT );
1115         }
1116
1117         /* Render all regions
1118          * We always transform non absolute subtitle into absolute one on the
1119          * first rendering to allow good subtitle overlap support.
1120          */
1121         for( p_region = p_subpic->p_region; p_region != NULL; p_region = p_region->p_next )
1122         {
1123             spu_area_t area;
1124
1125             /* Compute region scale AR */
1126             video_format_t region_fmt =p_region->fmt;
1127             if( region_fmt.i_sar_num <= 0 || region_fmt.i_sar_den <= 0 )
1128             {
1129                 region_fmt.i_sar_num = p_fmt_src->i_sar_num;
1130                 region_fmt.i_sar_den = p_fmt_src->i_sar_den;
1131             }
1132
1133             /* Compute scaling from original size to destination size
1134              * FIXME The current scaling ensure that the heights match, the width being
1135              * cropped.
1136              */
1137             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,
1138                                                   (int64_t)p_subpic->i_original_picture_height * p_fmt_dst->i_sar_num * region_fmt.i_sar_den,
1139                                                   p_fmt_dst->i_height,
1140                                                   p_subpic->i_original_picture_height );
1141
1142             /* Check scale validity */
1143             if( scale.w <= 0 || scale.h <= 0 )
1144                 continue;
1145
1146             /* */
1147             SpuRenderRegion( p_spu, pp_output_last, &area,
1148                              p_subpic, p_region, scale,
1149                              p_chroma_list, p_fmt_dst,
1150                              p_subtitle_area, i_subtitle_area,
1151                              p_subpic->b_subtitle ? render_subtitle_date : render_osd_date );
1152             if( *pp_output_last )
1153                 pp_output_last = &(*pp_output_last)->p_next;
1154
1155             if( p_subpic->b_subtitle )
1156             {
1157                 area = spu_area_unscaled( area, scale );
1158                 if( !p_subpic->b_absolute && area.i_width > 0 && area.i_height > 0 )
1159                 {
1160                     p_region->i_x = area.i_x;
1161                     p_region->i_y = area.i_y;
1162                 }
1163                 if( p_subtitle_area )
1164                     p_subtitle_area[i_subtitle_area++] = area;
1165             }
1166         }
1167         if( p_subpic->b_subtitle && p_subpic->p_region )
1168             p_subpic->b_absolute = true;
1169     }
1170
1171     /* */
1172     if( p_subtitle_area != p_subtitle_area_buffer )
1173         free( p_subtitle_area );
1174
1175     return p_output;
1176 }
1177
1178 /*****************************************************************************
1179  * Object variables callbacks
1180  *****************************************************************************/
1181
1182 /*****************************************************************************
1183  * UpdateSPU: update subpicture settings
1184  *****************************************************************************
1185  * This function is called from CropCallback and at initialization time, to
1186  * retrieve crop information from the input.
1187  *****************************************************************************/
1188 static void UpdateSPU( spu_t *p_spu, vlc_object_t *p_object )
1189 {
1190     spu_private_t *p_sys = p_spu->p;
1191     vlc_value_t val;
1192
1193     vlc_mutex_lock( &p_sys->lock );
1194
1195     p_sys->b_force_palette = false;
1196     p_sys->b_force_crop = false;
1197
1198     if( var_Get( p_object, "highlight", &val ) || !val.b_bool )
1199     {
1200         vlc_mutex_unlock( &p_sys->lock );
1201         return;
1202     }
1203
1204     p_sys->b_force_crop = true;
1205     p_sys->i_crop_x = var_GetInteger( p_object, "x-start" );
1206     p_sys->i_crop_y = var_GetInteger( p_object, "y-start" );
1207     p_sys->i_crop_width  = var_GetInteger( p_object, "x-end" ) - p_sys->i_crop_x;
1208     p_sys->i_crop_height = var_GetInteger( p_object, "y-end" ) - p_sys->i_crop_y;
1209
1210     if( var_Get( p_object, "menu-palette", &val ) == VLC_SUCCESS )
1211     {
1212         memcpy( p_sys->palette, val.p_address, 16 );
1213         p_sys->b_force_palette = true;
1214     }
1215     vlc_mutex_unlock( &p_sys->lock );
1216
1217     msg_Dbg( p_object, "crop: %i,%i,%i,%i, palette forced: %i",
1218              p_sys->i_crop_x, p_sys->i_crop_y,
1219              p_sys->i_crop_width, p_sys->i_crop_height,
1220              p_sys->b_force_palette );
1221 }
1222
1223 /*****************************************************************************
1224  * CropCallback: called when the highlight properties are changed
1225  *****************************************************************************
1226  * This callback is called from the input thread when we need cropping
1227  *****************************************************************************/
1228 static int CropCallback( vlc_object_t *p_object, char const *psz_var,
1229                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1230 {
1231     VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(psz_var);
1232
1233     UpdateSPU( (spu_t *)p_data, p_object );
1234     return VLC_SUCCESS;
1235 }
1236
1237 /*****************************************************************************
1238  * Buffers allocation callbacks for the filters
1239  *****************************************************************************/
1240
1241 static subpicture_t *sub_new_buffer( filter_t *p_filter )
1242 {
1243     filter_owner_sys_t *p_sys = p_filter->p_owner;
1244
1245     subpicture_t *p_subpicture = subpicture_New( NULL );
1246     if( p_subpicture )
1247         p_subpicture->i_channel = p_sys->i_channel;
1248     return p_subpicture;
1249 }
1250 static void sub_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1251 {
1252     VLC_UNUSED( p_filter );
1253     subpicture_Delete( p_subpic );
1254 }
1255
1256 static int SubSourceAllocationInit( filter_t *p_filter, void *p_data )
1257 {
1258     spu_t *p_spu = p_data;
1259
1260     filter_owner_sys_t *p_sys = malloc( sizeof(filter_owner_sys_t) );
1261     if( !p_sys )
1262         return VLC_EGENERIC;
1263
1264     p_filter->pf_sub_buffer_new = sub_new_buffer;
1265     p_filter->pf_sub_buffer_del = sub_del_buffer;
1266
1267     p_filter->p_owner = p_sys;
1268     p_sys->i_channel = spu_RegisterChannel( p_spu );
1269     p_sys->p_spu = p_spu;
1270
1271     return VLC_SUCCESS;
1272 }
1273
1274 static void SubSourceAllocationClean( filter_t *p_filter )
1275 {
1276     filter_owner_sys_t *p_sys = p_filter->p_owner;
1277
1278     spu_ClearChannel( p_sys->p_spu, p_sys->i_channel );
1279     free( p_filter->p_owner );
1280 }
1281
1282 /*****************************************************************************
1283  * Public API
1284  *****************************************************************************/
1285
1286 #undef spu_Create
1287 /**
1288  * Creates the subpicture unit
1289  *
1290  * \param p_this the parent object which creates the subpicture unit
1291  */
1292 spu_t *spu_Create( vlc_object_t *p_this )
1293 {
1294     spu_t *p_spu;
1295     spu_private_t *p_sys;
1296
1297     p_spu = vlc_custom_create( p_this, sizeof(spu_t) + sizeof(spu_private_t),
1298                                VLC_OBJECT_GENERIC, "subpicture" );
1299     if( !p_spu )
1300         return NULL;
1301     vlc_object_attach( p_spu, p_this );
1302
1303     /* Initialize spu fields */
1304     p_spu->p = p_sys = (spu_private_t*)&p_spu[1];
1305
1306     /* Initialize private fields */
1307     vlc_mutex_init( &p_sys->lock );
1308
1309     SpuHeapInit( &p_sys->heap );
1310
1311     p_sys->p_text = NULL;
1312     p_sys->p_scale = NULL;
1313     p_sys->p_scale_yuvp = NULL;
1314
1315     p_sys->i_margin = var_InheritInteger( p_spu, "sub-margin" );
1316
1317     /* Register the default subpicture channel */
1318     p_sys->i_channel = SPU_DEFAULT_CHANNEL + 1;
1319
1320     p_sys->psz_chain_update = NULL;
1321     vlc_mutex_init( &p_sys->chain_lock );
1322     p_sys->p_chain = filter_chain_New( p_spu, "sub source", false,
1323                                        SubSourceAllocationInit,
1324                                        SubSourceAllocationClean,
1325                                        p_spu );
1326
1327     /* Load text and scale module */
1328     p_sys->p_text = SpuRenderCreateAndLoadText( p_spu );
1329
1330     /* XXX p_spu->p_scale is used for all conversion/scaling except yuvp to
1331      * yuva/rgba */
1332     p_sys->p_scale = SpuRenderCreateAndLoadScale( VLC_OBJECT(p_spu),
1333                                                   VLC_CODEC_YUVA, VLC_CODEC_RGBA, true );
1334
1335     /* This one is used for YUVP to YUVA/RGBA without scaling
1336      * FIXME rename it */
1337     p_sys->p_scale_yuvp = SpuRenderCreateAndLoadScale( VLC_OBJECT(p_spu),
1338                                                        VLC_CODEC_YUVP, VLC_CODEC_YUVA, false );
1339
1340     /* */
1341     p_sys->i_last_sort_date = -1;
1342
1343     return p_spu;
1344 }
1345
1346 /**
1347  * Destroy the subpicture unit
1348  *
1349  * \param p_this the parent object which destroys the subpicture unit
1350  */
1351 void spu_Destroy( spu_t *p_spu )
1352 {
1353     spu_private_t *p_sys = p_spu->p;
1354
1355     if( p_sys->p_text )
1356         FilterRelease( p_sys->p_text );
1357
1358     if( p_sys->p_scale_yuvp )
1359         FilterRelease( p_sys->p_scale_yuvp );
1360
1361     if( p_sys->p_scale )
1362         FilterRelease( p_sys->p_scale );
1363
1364     filter_chain_Delete( p_sys->p_chain );
1365     vlc_mutex_destroy( &p_sys->chain_lock );
1366     free( p_sys->psz_chain_update );
1367
1368     /* Destroy all remaining subpictures */
1369     SpuHeapClean( &p_sys->heap );
1370
1371     vlc_mutex_destroy( &p_sys->lock );
1372
1373     vlc_object_release( p_spu );
1374 }
1375
1376 /**
1377  * Attach/Detach the SPU from any input
1378  *
1379  * \param p_this the object in which to destroy the subpicture unit
1380  * \param b_attach to select attach or detach
1381  */
1382 void spu_Attach( spu_t *p_spu, vlc_object_t *p_input, bool b_attach )
1383 {
1384     if( b_attach )
1385     {
1386         UpdateSPU( p_spu, p_input );
1387         var_Create( p_input, "highlight", VLC_VAR_BOOL );
1388         var_AddCallback( p_input, "highlight", CropCallback, p_spu );
1389
1390         vlc_mutex_lock( &p_spu->p->lock );
1391         p_spu->p->p_input = p_input;
1392
1393         if( p_spu->p->p_text )
1394             FilterRelease( p_spu->p->p_text );
1395         p_spu->p->p_text = SpuRenderCreateAndLoadText( p_spu );
1396
1397         vlc_mutex_unlock( &p_spu->p->lock );
1398     }
1399     else
1400     {
1401         vlc_mutex_lock( &p_spu->p->lock );
1402         p_spu->p->p_input = NULL;
1403         vlc_mutex_unlock( &p_spu->p->lock );
1404
1405         /* Delete callbacks */
1406         var_DelCallback( p_input, "highlight", CropCallback, p_spu );
1407         var_Destroy( p_input, "highlight" );
1408     }
1409 }
1410
1411 /**
1412  * Inform the SPU filters of mouse event
1413  */
1414 int spu_ProcessMouse( spu_t *p_spu,
1415                       const vlc_mouse_t *p_mouse,
1416                       const video_format_t *p_fmt )
1417 {
1418     spu_private_t *p_sys = p_spu->p;
1419
1420     vlc_mutex_lock( &p_sys->chain_lock );
1421     filter_chain_MouseEvent( p_sys->p_chain, p_mouse, p_fmt );
1422     vlc_mutex_unlock( &p_sys->chain_lock );
1423
1424     return VLC_SUCCESS;
1425 }
1426
1427 /**
1428  * Display a subpicture
1429  *
1430  * Remove the reservation flag of a subpicture, which will cause it to be
1431  * ready for display.
1432  * \param p_spu the subpicture unit object
1433  * \param p_subpic the subpicture to display
1434  */
1435 void spu_PutSubpicture( spu_t *p_spu, subpicture_t *p_subpic )
1436 {
1437     spu_private_t *p_sys = p_spu->p;
1438
1439     /* SPU_DEFAULT_CHANNEL always reset itself */
1440     if( p_subpic->i_channel == SPU_DEFAULT_CHANNEL )
1441         spu_ClearChannel( p_spu, SPU_DEFAULT_CHANNEL );
1442
1443     /* p_private is for spu only and cannot be non NULL here */
1444     for( subpicture_region_t *r = p_subpic->p_region; r != NULL; r = r->p_next )
1445         assert( r->p_private == NULL );
1446
1447     /* */
1448     vlc_mutex_lock( &p_sys->lock );
1449     if( SpuHeapPush( &p_sys->heap, p_subpic ) )
1450     {
1451         vlc_mutex_unlock( &p_sys->lock );
1452         msg_Err( p_spu, "subpicture heap full" );
1453         subpicture_Delete( p_subpic );
1454         return;
1455     }
1456     vlc_mutex_unlock( &p_sys->lock );
1457 }
1458
1459 subpicture_t *spu_Render( spu_t *p_spu,
1460                           const vlc_fourcc_t *p_chroma_list,
1461                           const video_format_t *p_fmt_dst,
1462                           const video_format_t *p_fmt_src,
1463                           mtime_t render_subtitle_date,
1464                           mtime_t render_osd_date,
1465                           bool b_subtitle_only )
1466 {
1467     spu_private_t *p_sys = p_spu->p;
1468
1469     /* Update sub-source chain */
1470     vlc_mutex_lock( &p_sys->lock );
1471     char *psz_chain_update = p_sys->psz_chain_update;
1472     p_sys->psz_chain_update = NULL;
1473     vlc_mutex_unlock( &p_sys->lock );
1474
1475     vlc_mutex_lock( &p_sys->chain_lock );
1476     if( psz_chain_update )
1477     {
1478         filter_chain_Reset( p_sys->p_chain, NULL, NULL );
1479
1480         filter_chain_AppendFromString( p_spu->p->p_chain, psz_chain_update );
1481
1482         free( psz_chain_update );
1483     }
1484     /* Run subpicture sources */
1485     filter_chain_SubSource( p_sys->p_chain, render_osd_date );
1486     vlc_mutex_unlock( &p_sys->chain_lock );
1487
1488     static const vlc_fourcc_t p_chroma_list_default_yuv[] = {
1489         VLC_CODEC_YUVA,
1490         VLC_CODEC_RGBA,
1491         VLC_CODEC_YUVP,
1492         0,
1493     };
1494     static const vlc_fourcc_t p_chroma_list_default_rgb[] = {
1495         VLC_CODEC_RGBA,
1496         VLC_CODEC_YUVA,
1497         VLC_CODEC_YUVP,
1498         0,
1499     };
1500
1501     if( !p_chroma_list || *p_chroma_list == 0 )
1502         p_chroma_list = vlc_fourcc_IsYUV(p_fmt_dst->i_chroma) ? p_chroma_list_default_yuv
1503                                                               : p_chroma_list_default_rgb;
1504
1505     vlc_mutex_lock( &p_sys->lock );
1506
1507     unsigned int i_subpicture;
1508     subpicture_t *pp_subpicture[VOUT_MAX_SUBPICTURES];
1509
1510     /* Get an array of subpictures to render */
1511     SpuSelectSubpictures( p_spu, &i_subpicture, pp_subpicture,
1512                           render_subtitle_date, render_osd_date, b_subtitle_only );
1513     if( i_subpicture <= 0 )
1514     {
1515         vlc_mutex_unlock( &p_sys->lock );
1516         return NULL;
1517     }
1518
1519     /* Updates the subpictures */
1520     for( unsigned i = 0; i < i_subpicture; i++ )
1521     {
1522         subpicture_t *p_subpic = pp_subpicture[i];
1523         subpicture_Update( p_subpic,
1524                            p_fmt_src, p_fmt_dst,
1525                            p_subpic->b_subtitle ? render_subtitle_date : render_osd_date );
1526     }
1527
1528     /* Now order the subpicture array
1529      * XXX The order is *really* important for overlap subtitles positionning */
1530     qsort( pp_subpicture, i_subpicture, sizeof(*pp_subpicture), SubpictureCmp );
1531
1532     /* Render the subpictures */
1533     subpicture_t *p_render = SpuRenderSubpictures( p_spu,
1534                                                    i_subpicture, pp_subpicture,
1535                                                    p_chroma_list,
1536                                                    p_fmt_dst,
1537                                                    p_fmt_src,
1538                                                    render_subtitle_date,
1539                                                    render_osd_date );
1540     vlc_mutex_unlock( &p_sys->lock );
1541
1542     return p_render;
1543 }
1544
1545 void spu_OffsetSubtitleDate( spu_t *p_spu, mtime_t i_duration )
1546 {
1547     spu_private_t *p_sys = p_spu->p;
1548
1549     vlc_mutex_lock( &p_sys->lock );
1550     for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
1551     {
1552         spu_heap_entry_t *p_entry = &p_sys->heap.p_entry[i];
1553         subpicture_t *p_current = p_entry->p_subpicture;
1554
1555         if( p_current && p_current->b_subtitle )
1556         {
1557             if( p_current->i_start > 0 )
1558                 p_current->i_start += i_duration;
1559             if( p_current->i_stop > 0 )
1560                 p_current->i_stop += i_duration;
1561         }
1562     }
1563     vlc_mutex_unlock( &p_sys->lock );
1564 }
1565
1566 int spu_RegisterChannel( spu_t *p_spu )
1567 {
1568     spu_private_t *p_sys = p_spu->p;
1569
1570     vlc_mutex_lock( &p_sys->lock );
1571     int i_channel = p_sys->i_channel++;
1572     vlc_mutex_unlock( &p_sys->lock );
1573
1574     return i_channel;
1575 }
1576
1577 void spu_ClearChannel( spu_t *p_spu, int i_channel )
1578 {
1579     spu_private_t *p_sys = p_spu->p;
1580
1581     vlc_mutex_lock( &p_sys->lock );
1582
1583     for( int i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
1584     {
1585         spu_heap_entry_t *p_entry = &p_sys->heap.p_entry[i_subpic];
1586         subpicture_t *p_subpic = p_entry->p_subpicture;
1587
1588         if( !p_subpic )
1589             continue;
1590         if( p_subpic->i_channel != i_channel && ( i_channel != -1 || p_subpic->i_channel == SPU_DEFAULT_CHANNEL ) )
1591             continue;
1592
1593         /* You cannot delete subpicture outside of spu_SortSubpictures */
1594         p_entry->b_reject = true;
1595     }
1596
1597     vlc_mutex_unlock( &p_sys->lock );
1598 }
1599
1600 void spu_ChangeSources( spu_t *p_spu, const char *psz_filters )
1601 {
1602     spu_private_t *p_sys = p_spu->p;
1603
1604     vlc_mutex_lock( &p_sys->lock );
1605
1606     free( p_sys->psz_chain_update );
1607     p_sys->psz_chain_update = strdup( psz_filters );
1608
1609     vlc_mutex_unlock( &p_sys->lock );
1610 }
1611
1612 void spu_ChangeMargin( spu_t *p_spu, int i_margin )
1613 {
1614     spu_private_t *p_sys = p_spu->p;
1615
1616     vlc_mutex_lock( &p_sys->lock );
1617     p_sys->i_margin = i_margin;
1618     vlc_mutex_unlock( &p_sys->lock );
1619 }
1620