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