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