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