]> git.sesse.net Git - vlc/blob - src/video_output/vout_subpictures.c
Changed subpicture_region_t->picture into a picture_t *
[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 *p_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     p_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 if cache is unusable
1012          * FIXME do not always destroy the region it can sometimes be reused 
1013          * if same size and same palette if present */
1014         if( p_region->p_private )
1015         {
1016             SpuRegionPrivateDestroy( p_region->p_private );
1017             p_region->p_private = NULL;
1018         }
1019
1020         /* Scale if needed into cache */
1021         if( !p_region->p_private )
1022         {
1023             picture_t *p_pic;
1024
1025             p_scale->fmt_in.video = p_region->fmt;
1026             p_scale->fmt_out.video = p_region->fmt;
1027
1028             p_scale->fmt_out.video.i_width = i_dst_width;
1029             p_scale->fmt_out.video.i_height = i_dst_height;
1030
1031             p_scale->fmt_out.video.i_visible_width =
1032                 spu_scale_w( p_region->fmt.i_visible_width, scale_size );
1033             p_scale->fmt_out.video.i_visible_height =
1034                 spu_scale_h( p_region->fmt.i_visible_height, scale_size );
1035
1036             p_region->p_private = SpuRegionPrivateCreate( &p_scale->fmt_out.video );
1037
1038             if( p_scale->p_module )
1039             {
1040                 picture_Yield( p_region->p_picture );
1041                 p_region->p_private->p_picture = p_scale->pf_video_filter( p_scale, p_region->p_picture );
1042             }
1043             if( !p_region->p_private->p_picture )
1044             {
1045                 msg_Err( p_spu, "scaling failed (module not loaded)" );
1046                 SpuRegionPrivateDestroy( p_region->p_private );
1047                 p_region->p_private = NULL;
1048             }
1049         }
1050
1051         /* And use the scaled picture */
1052         if( p_region->p_private )
1053         {
1054             p_region_fmt = &p_region->p_private->fmt;
1055             p_region_picture = p_region->p_private->p_picture;
1056         }
1057     }
1058
1059     /* Force cropping if requested */
1060     if( b_force_crop )
1061     {
1062         video_format_t *p_fmt = p_region_fmt;
1063         int i_crop_x = spu_scale_w( p_spu->i_crop_x, scale_size );
1064         int i_crop_y = spu_scale_h( p_spu->i_crop_y, scale_size );
1065         int i_crop_width = spu_scale_w( p_spu->i_crop_width, scale_size );
1066         int i_crop_height= spu_scale_h( p_spu->i_crop_height,scale_size );
1067
1068         /* Find the intersection */
1069         if( i_crop_x + i_crop_width <= i_x_offset ||
1070             i_x_offset + (int)p_fmt->i_visible_width < i_crop_x ||
1071             i_crop_y + i_crop_height <= i_y_offset ||
1072             i_y_offset + (int)p_fmt->i_visible_height < i_crop_y )
1073         {
1074             /* No intersection */
1075             p_fmt->i_visible_width = p_fmt->i_visible_height = 0;
1076         }
1077         else
1078         {
1079             int i_x, i_y, i_x_end, i_y_end;
1080             i_x = __MAX( i_crop_x, i_x_offset );
1081             i_y = __MAX( i_crop_y, i_y_offset );
1082             i_x_end = __MIN( i_crop_x + i_crop_width,
1083                            i_x_offset + (int)p_fmt->i_visible_width );
1084             i_y_end = __MIN( i_crop_y + i_crop_height,
1085                            i_y_offset + (int)p_fmt->i_visible_height );
1086
1087             p_fmt->i_x_offset = i_x - i_x_offset;
1088             p_fmt->i_y_offset = i_y - i_y_offset;
1089             p_fmt->i_visible_width = i_x_end - i_x;
1090             p_fmt->i_visible_height = i_y_end - i_y;
1091
1092             i_x_offset = __MAX( i_x, 0 );
1093             i_y_offset = __MAX( i_y, 0 );
1094         }
1095         b_restore_format = true;
1096     }
1097
1098     /* Update the blender */
1099     SpuRenderUpdateBlend( p_spu, p_fmt->i_width, p_fmt->i_height, p_region_fmt );
1100
1101     if( p_spu->p_blend->p_module )
1102     {
1103         const int i_alpha = SpuRegionAlpha( p_subpic, p_region );
1104
1105         p_spu->p_blend->pf_video_blend( p_spu->p_blend, p_pic_dst,
1106             p_region_picture, i_x_offset, i_y_offset, i_alpha );
1107     }
1108     else
1109     {
1110         msg_Err( p_spu, "blending %4.4s to %4.4s failed",
1111                  (char *)&p_spu->p_blend->fmt_out.video.i_chroma,
1112                  (char *)&p_spu->p_blend->fmt_out.video.i_chroma );
1113     }
1114
1115 exit:
1116     if( b_rerender_text )
1117     {
1118         /* Some forms of subtitles need to be re-rendered more than
1119          * once, eg. karaoke. We therefore restore the region to its
1120          * pre-rendered state, so the next time through everything is
1121          * calculated again.
1122          */
1123         picture_Release( p_region->p_picture );
1124         p_region->p_picture = NULL;
1125         if( p_region->p_private )
1126         {
1127             SpuRegionPrivateDestroy( p_region->p_private );
1128             p_region->p_private = NULL;
1129         }
1130         p_region->i_align &= ~SUBPICTURE_RENDERED;
1131     }
1132     if( b_restore_format )
1133         p_region->fmt = fmt_original;
1134 }
1135
1136 /**
1137  * This function compares two 64 bits integers.
1138  * It can be used by qsort.
1139  */
1140 static int IntegerCmp( int64_t i0, int64_t i1 )
1141 {
1142     return i0 < i1 ? -1 : i0 > i1 ? 1 : 0;
1143 }
1144 /**
1145  * This function compares 2 subpictures using the following properties 
1146  * (ordered by priority)
1147  * 1. absolute positionning
1148  * 2. start time
1149  * 3. creation order
1150  *
1151  * It can be used by qsort.
1152  *
1153  * XXX spu_RenderSubpictures depends heavily on this order.
1154  */
1155 static int SubpictureCmp( const void *s0, const void *s1 )
1156 {
1157     subpicture_t *p_subpic0 = *(subpicture_t**)s0;
1158     subpicture_t *p_subpic1 = *(subpicture_t**)s1;
1159     int r;
1160
1161     r = IntegerCmp( !p_subpic0->b_absolute, !p_subpic1->b_absolute );
1162     if( !r )
1163         r = IntegerCmp( p_subpic0->i_start, p_subpic1->i_start );
1164     if( !r )
1165         r = IntegerCmp( p_subpic0->i_order, p_subpic1->i_order );
1166     return r;
1167 }
1168 /**
1169  * This function renders all sub picture units in the list.
1170  */
1171 void spu_RenderSubpictures( spu_t *p_spu,
1172                             picture_t *p_pic_dst, const video_format_t *p_fmt_dst,
1173                             subpicture_t *p_subpic_list,
1174                             const video_format_t *p_fmt_src )
1175 {
1176     const int i_source_video_width  = p_fmt_src->i_width;
1177     const int i_source_video_height = p_fmt_src->i_height;
1178     const mtime_t i_current_date = mdate();
1179
1180     unsigned int i_subpicture;
1181     subpicture_t *pp_subpicture[VOUT_MAX_SUBPICTURES];
1182
1183     unsigned int i_subtitle_region_count;
1184     spu_area_t p_subtitle_area_buffer[VOUT_MAX_SUBPICTURES];
1185     spu_area_t *p_subtitle_area;
1186     int i_subtitle_area;
1187
1188     /* Get lock */
1189     vlc_mutex_lock( &p_spu->subpicture_lock );
1190
1191     /* Preprocess subpictures */
1192     i_subpicture = 0;
1193     i_subtitle_region_count = 0;
1194     for( subpicture_t * p_subpic = p_subpic_list;
1195             p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE; /* Check again status (as we where unlocked) */
1196                 p_subpic = p_subpic->p_next )
1197     {
1198         /* */
1199         if( p_subpic->pf_pre_render )
1200             p_subpic->pf_pre_render( p_spu, p_subpic, p_fmt_dst );
1201
1202         if( p_subpic->pf_update_regions )
1203         {
1204             video_format_t fmt_org = *p_fmt_dst;
1205             fmt_org.i_width =
1206             fmt_org.i_visible_width = i_source_video_width;
1207             fmt_org.i_height =
1208             fmt_org.i_visible_height = i_source_video_height;
1209
1210             p_subpic->pf_update_regions( p_spu, p_subpic, &fmt_org, i_current_date );
1211         }
1212
1213         /* */
1214         if( p_subpic->b_subtitle )
1215         {
1216             for( subpicture_region_t *r = p_subpic->p_region; r != NULL; r = r->p_next )
1217                 i_subtitle_region_count++;
1218         }
1219
1220         /* */
1221         pp_subpicture[i_subpicture++] = p_subpic;
1222     }
1223
1224     /* Be sure we have at least 1 picture to process */
1225     if( i_subpicture <= 0 )
1226     {
1227         vlc_mutex_unlock( &p_spu->subpicture_lock );
1228         return;
1229     }
1230
1231     /* Now order subpicture array
1232      * XXX The order is *really* important for overlap subtitles positionning */
1233     qsort( pp_subpicture, i_subpicture, sizeof(*pp_subpicture), SubpictureCmp );
1234
1235     /* Allocate area array for subtitle overlap */
1236     i_subtitle_area = 0;
1237     p_subtitle_area = p_subtitle_area_buffer;
1238     if( i_subtitle_region_count > sizeof(p_subtitle_area_buffer)/sizeof(*p_subtitle_area_buffer) )
1239         p_subtitle_area = calloc( i_subtitle_region_count, sizeof(*p_subtitle_area) );
1240
1241     /* Create the blending module */
1242     if( !p_spu->p_blend )
1243         SpuRenderCreateBlend( p_spu, p_fmt_dst->i_chroma, p_fmt_dst->i_aspect );
1244
1245     /* Process all subpictures and regions (in the right order) */
1246     for( unsigned int i_index = 0; i_index < i_subpicture; i_index++ )
1247     {
1248         subpicture_t *p_subpic = pp_subpicture[i_index];
1249         subpicture_region_t *p_region;
1250
1251         if( !p_subpic->p_region )
1252             continue;
1253
1254         /* FIXME when possible use a better rendering size than source size
1255          * (max of display size and source size for example) FIXME */
1256         int i_render_width  = p_subpic->i_original_picture_width;
1257         int i_render_height = p_subpic->i_original_picture_height;
1258         if( !i_render_width || !i_render_height )
1259         {
1260             if( i_render_width != 0 || i_render_height != 0 )
1261                 msg_Err( p_spu, "unsupported original picture size %dx%d",
1262                          i_render_width, i_render_height );
1263
1264             p_subpic->i_original_picture_width  = i_render_width = i_source_video_width;
1265             p_subpic->i_original_picture_height = i_render_height = i_source_video_height;
1266         }
1267
1268         if( p_spu->p_text )
1269         {
1270             p_spu->p_text->fmt_out.video.i_width          =
1271             p_spu->p_text->fmt_out.video.i_visible_width  = i_render_width;
1272
1273             p_spu->p_text->fmt_out.video.i_height         =
1274             p_spu->p_text->fmt_out.video.i_visible_height = i_render_height;
1275         }
1276
1277         /* Compute scaling from picture to source size */
1278         spu_scale_t scale = spu_scale_createq( i_source_video_width,  i_render_width,
1279                                                i_source_video_height, i_render_height );
1280
1281         /* Update scaling from source size to display size(p_fmt_dst) */
1282         scale.w = scale.w * p_fmt_dst->i_width  / i_source_video_width;
1283         scale.h = scale.h * p_fmt_dst->i_height / i_source_video_height;
1284
1285         /* Set default subpicture aspect ratio
1286          * FIXME if we only handle 1 aspect ratio per picture, why is it set per
1287          * region ? */
1288         p_region = p_subpic->p_region;
1289         if( !p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den )
1290         {
1291             if( p_region->fmt.i_aspect != 0 )
1292             {
1293                 p_region->fmt.i_sar_den = p_region->fmt.i_aspect;
1294                 p_region->fmt.i_sar_num = VOUT_ASPECT_FACTOR;
1295             }
1296             else
1297             {
1298                 p_region->fmt.i_sar_den = p_fmt_dst->i_sar_den;
1299                 p_region->fmt.i_sar_num = p_fmt_dst->i_sar_num;
1300             }
1301         }
1302
1303         /* Take care of the aspect ratio */
1304         if( p_region->fmt.i_sar_num * p_fmt_dst->i_sar_den !=
1305             p_region->fmt.i_sar_den * p_fmt_dst->i_sar_num )
1306         {
1307             /* FIXME FIXME what about region->i_x/i_y ? */
1308             scale.w = scale.w *
1309                 (int64_t)p_region->fmt.i_sar_num * p_fmt_dst->i_sar_den /
1310                 p_region->fmt.i_sar_den / p_fmt_dst->i_sar_num;
1311         }
1312
1313         /* Render all regions
1314          * We always transform non absolute subtitle into absolute one on the
1315          * first rendering to allow good subtitle overlap support.
1316          */
1317         for( p_region = p_subpic->p_region; p_region != NULL; p_region = p_region->p_next )
1318         {
1319             spu_area_t area;
1320
1321             /* Check scale validity */
1322             if( scale.w <= 0 || scale.h <= 0 )
1323                 continue;
1324
1325             /* */
1326             SpuRenderRegion( p_spu, p_pic_dst, &area,
1327                              p_subpic, p_region, scale, p_fmt_dst,
1328                              p_subtitle_area, i_subtitle_area );
1329
1330             if( p_subpic->b_subtitle )
1331             {
1332                 area = spu_area_unscaled( area, scale );
1333                 if( !p_subpic->b_absolute && area.i_width > 0 && area.i_height > 0 )
1334                 {
1335                     p_region->i_x = area.i_x;
1336                     p_region->i_y = area.i_y;
1337                 }
1338                 if( p_subtitle_area )
1339                     p_subtitle_area[i_subtitle_area++] = area;
1340             }
1341         }
1342         if( p_subpic->b_subtitle )
1343             p_subpic->b_absolute = true;
1344     }
1345
1346     /* */
1347     if( p_subtitle_area != p_subtitle_area_buffer )
1348         free( p_subtitle_area );
1349
1350     vlc_mutex_unlock( &p_spu->subpicture_lock );
1351 }
1352
1353 /*****************************************************************************
1354  * spu_SortSubpictures: find the subpictures to display
1355  *****************************************************************************
1356  * This function parses all subpictures and decides which ones need to be
1357  * displayed. This operation does not need lock, since only READY_SUBPICTURE
1358  * are handled. If no picture has been selected, display_date will depend on
1359  * the subpicture.
1360  * We also check for ephemer DVD subpictures (subpictures that have
1361  * to be removed if a newer one is available), which makes it a lot
1362  * more difficult to guess if a subpicture has to be rendered or not.
1363  *****************************************************************************/
1364 subpicture_t *spu_SortSubpictures( spu_t *p_spu, mtime_t display_date,
1365                                    bool b_paused, bool b_subtitle_only )
1366 {
1367     int i_channel;
1368     subpicture_t *p_subpic = NULL;
1369
1370     /* Run subpicture filters */
1371     filter_chain_SubFilter( p_spu->p_chain, display_date );
1372
1373     /* We get an easily parsable chained list of subpictures which
1374      * ends with NULL since p_subpic was initialized to NULL. */
1375     for( i_channel = 0; i_channel < p_spu->i_channel; i_channel++ )
1376     {
1377         subpicture_t *p_ephemer = NULL;
1378         mtime_t      ephemer_date = 0;
1379         int i_index;
1380
1381         for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
1382         {
1383             subpicture_t *p_current = &p_spu->p_subpicture[i_index];
1384
1385             if( p_current->i_channel != i_channel ||
1386                 p_current->i_status != READY_SUBPICTURE ||
1387                 ( b_subtitle_only && !p_current->b_subtitle ) )
1388             {
1389                 continue;
1390             }
1391             if( display_date &&
1392                 display_date < p_current->i_start )
1393             {
1394                 /* Too early, come back next monday */
1395                 continue;
1396             }
1397
1398             if( p_current->i_start > ephemer_date )
1399                 ephemer_date = p_current->i_start;
1400
1401             if( display_date > p_current->i_stop &&
1402                 ( !p_current->b_ephemer || p_current->i_stop > p_current->i_start ) &&
1403                 !( p_current->b_subtitle && b_paused ) ) /* XXX Assume that subtitle are pausable */
1404             {
1405                 /* Too late, destroy the subpic */
1406                 spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] );
1407                 continue;
1408             }
1409
1410             /* If this is an ephemer subpic, add it to our list */
1411             if( p_current->b_ephemer )
1412             {
1413                 p_current->p_next = p_ephemer;
1414                 p_ephemer = p_current;
1415
1416                 continue;
1417             }
1418
1419             p_current->p_next = p_subpic;
1420             p_subpic = p_current;
1421         }
1422
1423         /* If we found ephemer subpictures, check if they have to be
1424          * displayed or destroyed */
1425         while( p_ephemer != NULL )
1426         {
1427             subpicture_t *p_tmp = p_ephemer;
1428             p_ephemer = p_ephemer->p_next;
1429
1430             if( p_tmp->i_start < ephemer_date )
1431             {
1432                 /* Ephemer subpicture has lived too long */
1433                 spu_DestroySubpicture( p_spu, p_tmp );
1434             }
1435             else
1436             {
1437                 /* Ephemer subpicture can still live a bit */
1438                 p_tmp->p_next = p_subpic;
1439                 p_subpic = p_tmp;
1440             }
1441         }
1442     }
1443
1444     return p_subpic;
1445 }
1446
1447 /*****************************************************************************
1448  * SpuClearChannel: clear an spu channel
1449  *****************************************************************************
1450  * This function destroys the subpictures which belong to the spu channel
1451  * corresponding to i_channel_id.
1452  *****************************************************************************/
1453 static void SpuClearChannel( spu_t *p_spu, int i_channel, bool b_locked )
1454 {
1455     int          i_subpic;                               /* subpicture index */
1456     subpicture_t *p_subpic = NULL;                  /* first free subpicture */
1457
1458     if( !b_locked )
1459         vlc_mutex_lock( &p_spu->subpicture_lock );
1460
1461     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
1462     {
1463         p_subpic = &p_spu->p_subpicture[i_subpic];
1464         if( p_subpic->i_status == FREE_SUBPICTURE
1465             || ( p_subpic->i_status != RESERVED_SUBPICTURE
1466                  && p_subpic->i_status != READY_SUBPICTURE ) )
1467         {
1468             continue;
1469         }
1470
1471         if( p_subpic->i_channel == i_channel )
1472         {
1473             while( p_subpic->p_region )
1474             {
1475                 subpicture_region_t *p_region = p_subpic->p_region;
1476                 p_subpic->p_region = p_region->p_next;
1477                 spu_DestroyRegion( p_spu, p_region );
1478             }
1479
1480             if( p_subpic->pf_destroy ) p_subpic->pf_destroy( p_subpic );
1481             p_subpic->i_status = FREE_SUBPICTURE;
1482         }
1483     }
1484
1485     if( !b_locked )
1486         vlc_mutex_unlock( &p_spu->subpicture_lock );
1487 }
1488
1489 /*****************************************************************************
1490  * spu_ControlDefault: default methods for the subpicture unit control.
1491  *****************************************************************************/
1492 static int spu_vaControlDefault( spu_t *p_spu, int i_query, va_list args )
1493 {
1494     int *pi, i;
1495
1496     switch( i_query )
1497     {
1498     case SPU_CHANNEL_REGISTER:
1499         pi = (int *)va_arg( args, int * );
1500         vlc_mutex_lock( &p_spu->subpicture_lock );
1501         if( pi )
1502             *pi = p_spu->i_channel++;
1503         vlc_mutex_unlock( &p_spu->subpicture_lock );
1504         break;
1505
1506     case SPU_CHANNEL_CLEAR:
1507         i = (int)va_arg( args, int );
1508         SpuClearChannel( p_spu, i, false );
1509         break;
1510
1511     default:
1512         msg_Dbg( p_spu, "control query not supported" );
1513         return VLC_EGENERIC;
1514     }
1515
1516     return VLC_SUCCESS;
1517 }
1518
1519 /*****************************************************************************
1520  * Object variables callbacks
1521  *****************************************************************************/
1522
1523 /*****************************************************************************
1524  * UpdateSPU: update subpicture settings
1525  *****************************************************************************
1526  * This function is called from CropCallback and at initialization time, to
1527  * retrieve crop information from the input.
1528  *****************************************************************************/
1529 static void UpdateSPU( spu_t *p_spu, vlc_object_t *p_object )
1530 {
1531     vlc_value_t val;
1532
1533     vlc_mutex_lock( &p_spu->subpicture_lock );
1534
1535     p_spu->b_force_palette = false;
1536     p_spu->b_force_crop = false;
1537
1538     if( var_Get( p_object, "highlight", &val ) || !val.b_bool )
1539     {
1540         vlc_mutex_unlock( &p_spu->subpicture_lock );
1541         return;
1542     }
1543
1544     p_spu->b_force_crop = true;
1545     var_Get( p_object, "x-start", &val );
1546     p_spu->i_crop_x = val.i_int;
1547     var_Get( p_object, "y-start", &val );
1548     p_spu->i_crop_y = val.i_int;
1549     var_Get( p_object, "x-end", &val );
1550     p_spu->i_crop_width = val.i_int - p_spu->i_crop_x;
1551     var_Get( p_object, "y-end", &val );
1552     p_spu->i_crop_height = val.i_int - p_spu->i_crop_y;
1553
1554     if( var_Get( p_object, "menu-palette", &val ) == VLC_SUCCESS )
1555     {
1556         memcpy( p_spu->palette, val.p_address, 16 );
1557         p_spu->b_force_palette = true;
1558     }
1559     vlc_mutex_unlock( &p_spu->subpicture_lock );
1560
1561     msg_Dbg( p_object, "crop: %i,%i,%i,%i, palette forced: %i",
1562              p_spu->i_crop_x, p_spu->i_crop_y,
1563              p_spu->i_crop_width, p_spu->i_crop_height,
1564              p_spu->b_force_palette );
1565 }
1566
1567 /*****************************************************************************
1568  * CropCallback: called when the highlight properties are changed
1569  *****************************************************************************
1570  * This callback is called from the input thread when we need cropping
1571  *****************************************************************************/
1572 static int CropCallback( vlc_object_t *p_object, char const *psz_var,
1573                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1574 {
1575     (void)psz_var; (void)oldval; (void)newval;
1576     UpdateSPU( (spu_t *)p_data, p_object );
1577     return VLC_SUCCESS;
1578 }
1579
1580 /*****************************************************************************
1581  * Buffers allocation callbacks for the filters
1582  *****************************************************************************/
1583 static subpicture_t *sub_new_buffer( filter_t *p_filter )
1584 {
1585     filter_owner_sys_t *p_sys = p_filter->p_owner;
1586     subpicture_t *p_subpicture = spu_CreateSubpicture( p_sys->p_spu );
1587     if( p_subpicture ) p_subpicture->i_channel = p_sys->i_channel;
1588     return p_subpicture;
1589 }
1590
1591 static void sub_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1592 {
1593     filter_owner_sys_t *p_sys = p_filter->p_owner;
1594     spu_DestroySubpicture( p_sys->p_spu, p_subpic );
1595 }
1596
1597 static subpicture_t *spu_new_buffer( filter_t *p_filter )
1598 {
1599     subpicture_t *p_subpic = calloc( 1, sizeof(subpicture_t) );
1600     if( !p_subpic )
1601         return NULL;
1602
1603     p_subpic->b_absolute = true;
1604     p_subpic->i_alpha    = 0xFF;
1605
1606     p_subpic->pf_create_region = __spu_CreateRegion;
1607     p_subpic->pf_destroy_region = __spu_DestroyRegion;
1608
1609     VLC_UNUSED(p_filter);
1610     return p_subpic;
1611 }
1612
1613 static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1614 {
1615     while( p_subpic->p_region )
1616     {
1617         subpicture_region_t *p_region = p_subpic->p_region;
1618         p_subpic->p_region = p_region->p_next;
1619         p_subpic->pf_destroy_region( VLC_OBJECT(p_filter), p_region );
1620     }
1621
1622     free( p_subpic );
1623 }
1624
1625 static picture_t *spu_new_video_buffer( filter_t *p_filter )
1626 {
1627     const video_format_t *p_fmt = &p_filter->fmt_out.video;
1628
1629     VLC_UNUSED(p_filter);
1630     return picture_New( p_fmt->i_chroma,
1631                         p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect );
1632 }
1633
1634 static void spu_del_video_buffer( filter_t *p_filter, picture_t *p_picture )
1635 {
1636     VLC_UNUSED(p_filter);
1637     picture_Release( p_picture );
1638 }
1639
1640 static int SubFilterCallback( vlc_object_t *p_object, char const *psz_var,
1641                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1642 {
1643     spu_t *p_spu = p_data;
1644
1645     VLC_UNUSED(p_object); VLC_UNUSED(oldval);
1646     VLC_UNUSED(newval); VLC_UNUSED(psz_var);
1647
1648     vlc_mutex_lock( &p_spu->subpicture_lock );
1649     filter_chain_Reset( p_spu->p_chain, NULL, NULL );
1650     spu_ParseChain( p_spu );
1651     vlc_mutex_unlock( &p_spu->subpicture_lock );
1652     return VLC_SUCCESS;
1653 }
1654
1655 static int SubFilterAllocationInit( filter_t *p_filter, void *p_data )
1656 {
1657     spu_t *p_spu = p_data;
1658
1659     filter_owner_sys_t *p_sys = malloc( sizeof(filter_owner_sys_t) );
1660     if( !p_sys )
1661         return VLC_EGENERIC;
1662
1663     p_filter->pf_sub_buffer_new = sub_new_buffer;
1664     p_filter->pf_sub_buffer_del = sub_del_buffer;
1665
1666     p_filter->p_owner = p_sys;
1667     spu_Control( p_spu, SPU_CHANNEL_REGISTER, &p_sys->i_channel );
1668     p_sys->p_spu = p_spu;
1669
1670     return VLC_SUCCESS;
1671 }
1672
1673 static void SubFilterAllocationClean( filter_t *p_filter )
1674 {
1675     filter_owner_sys_t *p_sys = p_filter->p_owner;
1676
1677     SpuClearChannel( p_sys->p_spu, p_sys->i_channel, true );
1678     free( p_filter->p_owner );
1679 }
1680