]> git.sesse.net Git - vlc/blob - src/video_output/vout_subpictures.c
vout: Given that 16:10 is the most common display ratio, put it first.
[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
42 /*****************************************************************************
43  * Local prototypes
44  *****************************************************************************/
45 static void UpdateSPU   ( spu_t *, vlc_object_t * );
46 static int  CropCallback( vlc_object_t *, char const *,
47                           vlc_value_t, vlc_value_t, void * );
48
49 static int spu_vaControlDefault( spu_t *, int, va_list );
50
51 static subpicture_t *sub_new_buffer( filter_t * );
52 static void sub_del_buffer( filter_t *, subpicture_t * );
53 static subpicture_t *spu_new_buffer( filter_t * );
54 static void spu_del_buffer( filter_t *, subpicture_t * );
55 static picture_t *spu_new_video_buffer( filter_t * );
56 static void spu_del_video_buffer( filter_t *, picture_t * );
57
58 static int spu_ParseChain( spu_t * );
59 static int SubFilterCallback( vlc_object_t *, char const *,
60                               vlc_value_t, vlc_value_t, void * );
61
62 static int sub_filter_allocation_init( filter_t *, void * );
63 static void sub_filter_allocation_clear( filter_t * );
64 struct filter_owner_sys_t
65 {
66     spu_t *p_spu;
67     int i_channel;
68 };
69
70 enum {
71     SCALE_DEFAULT,
72     SCALE_TEXT,
73     SCALE_SIZE
74 };
75
76 /**
77  * Creates the subpicture unit
78  *
79  * \param p_this the parent object which creates the subpicture unit
80  */
81 spu_t *__spu_Create( vlc_object_t *p_this )
82 {
83     int i_index;
84     spu_t *p_spu = vlc_custom_create( p_this, sizeof( spu_t ),
85                                       VLC_OBJECT_GENERIC, "subpicture" );
86
87     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
88     {
89         p_spu->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
90     }
91
92     p_spu->p_blend = NULL;
93     p_spu->p_text = NULL;
94     p_spu->p_scale = NULL;
95     p_spu->pf_control = spu_vaControlDefault;
96
97     /* Register the default subpicture channel */
98     p_spu->i_channel = 2;
99
100     vlc_mutex_init( &p_spu->subpicture_lock );
101
102     vlc_object_attach( p_spu, p_this );
103
104     p_spu->p_chain = filter_chain_New( p_spu, "sub filter", false,
105                                        sub_filter_allocation_init,
106                                        sub_filter_allocation_clear,
107                                        p_spu );
108     return p_spu;
109 }
110
111 /**
112  * Initialise the subpicture unit
113  *
114  * \param p_spu the subpicture unit object
115  */
116 int spu_Init( spu_t *p_spu )
117 {
118     vlc_value_t val;
119
120     /* If the user requested a sub margin, we force the position. */
121     var_Create( p_spu, "sub-margin", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
122     var_Get( p_spu, "sub-margin", &val );
123     p_spu->i_margin = val.i_int;
124
125     var_Create( p_spu, "sub-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
126     var_AddCallback( p_spu, "sub-filter", SubFilterCallback, p_spu );
127
128     spu_ParseChain( p_spu );
129
130     return VLC_SUCCESS;
131 }
132
133 int spu_ParseChain( spu_t *p_spu )
134 {
135     char *psz_parser = var_GetString( p_spu, "sub-filter" );
136     if( filter_chain_AppendFromString( p_spu->p_chain, psz_parser ) < 0 )
137     {
138         free( psz_parser );
139         return VLC_EGENERIC;
140     }
141
142     free( psz_parser );
143     return VLC_SUCCESS;
144 }
145
146 /**
147  * Destroy the subpicture unit
148  *
149  * \param p_this the parent object which destroys the subpicture unit
150  */
151 void spu_Destroy( spu_t *p_spu )
152 {
153     int i_index;
154
155     /* Destroy all remaining subpictures */
156     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
157     {
158         if( p_spu->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
159         {
160             spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] );
161         }
162     }
163
164     if( p_spu->p_blend )
165     {
166         if( p_spu->p_blend->p_module )
167             module_Unneed( p_spu->p_blend, p_spu->p_blend->p_module );
168
169         vlc_object_detach( p_spu->p_blend );
170         vlc_object_release( p_spu->p_blend );
171     }
172
173     if( p_spu->p_text )
174     {
175         if( p_spu->p_text->p_module )
176             module_Unneed( p_spu->p_text, p_spu->p_text->p_module );
177
178         vlc_object_detach( p_spu->p_text );
179         vlc_object_release( p_spu->p_text );
180     }
181
182     if( p_spu->p_scale )
183     {
184         if( p_spu->p_scale->p_module )
185             module_Unneed( p_spu->p_scale, p_spu->p_scale->p_module );
186
187         vlc_object_detach( p_spu->p_scale );
188         vlc_object_release( p_spu->p_scale );
189     }
190
191     filter_chain_Delete( p_spu->p_chain );
192
193     vlc_mutex_destroy( &p_spu->subpicture_lock );
194     vlc_object_release( p_spu );
195 }
196
197 /**
198  * Attach/Detach the SPU from any input
199  *
200  * \param p_this the object in which to destroy the subpicture unit
201  * \param b_attach to select attach or detach
202  */
203 void spu_Attach( spu_t *p_spu, vlc_object_t *p_this, bool b_attach )
204 {
205     vlc_object_t *p_input;
206
207     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
208     if( !p_input ) return;
209
210     if( b_attach )
211     {
212         UpdateSPU( p_spu, VLC_OBJECT(p_input) );
213         var_AddCallback( p_input, "highlight", CropCallback, p_spu );
214         vlc_object_release( p_input );
215     }
216     else
217     {
218         /* Delete callback */
219         var_DelCallback( p_input, "highlight", CropCallback, p_spu );
220         vlc_object_release( p_input );
221     }
222 }
223
224 /**
225  * Create a subpicture region
226  *
227  * \param p_this vlc_object_t
228  * \param p_fmt the format that this subpicture region should have
229  */
230 static void RegionPictureRelease( picture_t *p_pic )
231 {
232     free( p_pic->p_data_orig );
233 }
234 subpicture_region_t *__spu_CreateRegion( vlc_object_t *p_this,
235                                          video_format_t *p_fmt )
236 {
237     subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
238     if( !p_region ) return NULL;
239
240     memset( p_region, 0, sizeof(subpicture_region_t) );
241     p_region->i_alpha = 0xff;
242     p_region->p_next = NULL;
243     p_region->p_cache = NULL;
244     p_region->fmt = *p_fmt;
245     p_region->psz_text = NULL;
246     p_region->p_style = NULL;
247
248     if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') )
249         p_fmt->p_palette = p_region->fmt.p_palette =
250             malloc( sizeof(video_palette_t) );
251     else p_fmt->p_palette = p_region->fmt.p_palette = NULL;
252
253     p_region->picture.p_data_orig = NULL;
254
255     if( p_fmt->i_chroma == VLC_FOURCC('T','E','X','T') ) return p_region;
256
257     vout_AllocatePicture( p_this, &p_region->picture, p_fmt->i_chroma,
258                           p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect );
259
260     if( !p_region->picture.i_planes )
261     {
262         free( p_region );
263         free( p_fmt->p_palette );
264         return NULL;
265     }
266
267     p_region->picture.pf_release = RegionPictureRelease;
268
269     return p_region;
270 }
271
272 /**
273  * Make a subpicture region from an existing picture_t
274  *
275  * \param p_this vlc_object_t
276  * \param p_fmt the format that this subpicture region should have
277  * \param p_pic a pointer to the picture creating the region (not freed)
278  */
279 subpicture_region_t *__spu_MakeRegion( vlc_object_t *p_this,
280                                        video_format_t *p_fmt,
281                                        picture_t *p_pic )
282 {
283     subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
284     (void)p_this;
285     if( !p_region ) return NULL;
286     memset( p_region, 0, sizeof(subpicture_region_t) );
287     p_region->i_alpha = 0xff;
288     p_region->p_next = 0;
289     p_region->p_cache = 0;
290     p_region->fmt = *p_fmt;
291     p_region->psz_text = 0;
292     p_region->p_style = NULL;
293
294     if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') )
295         p_fmt->p_palette = p_region->fmt.p_palette =
296             malloc( sizeof(video_palette_t) );
297     else p_fmt->p_palette = p_region->fmt.p_palette = NULL;
298
299     memcpy( &p_region->picture, p_pic, sizeof(picture_t) );
300     p_region->picture.pf_release = RegionPictureRelease;
301
302     return p_region;
303 }
304
305 /**
306  * Destroy a subpicture region
307  *
308  * \param p_this vlc_object_t
309  * \param p_region the subpicture region to destroy
310  */
311 void __spu_DestroyRegion( vlc_object_t *p_this, subpicture_region_t *p_region )
312 {
313     if( !p_region ) return;
314     if( p_region->picture.pf_release )
315         p_region->picture.pf_release( &p_region->picture );
316     free( p_region->fmt.p_palette );
317     if( p_region->p_cache ) __spu_DestroyRegion( p_this, p_region->p_cache );
318
319     free( p_region->psz_text );
320     free( p_region->psz_html );
321     //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
322     free( p_region );
323 }
324
325 /**
326  * Display a subpicture
327  *
328  * Remove the reservation flag of a subpicture, which will cause it to be
329  * ready for display.
330  * \param p_spu the subpicture unit object
331  * \param p_subpic the subpicture to display
332  */
333 void spu_DisplaySubpicture( spu_t *p_spu, subpicture_t *p_subpic )
334 {
335     /* Check if status is valid */
336     if( p_subpic->i_status != RESERVED_SUBPICTURE )
337     {
338         msg_Err( p_spu, "subpicture %p has invalid status #%d",
339                  p_subpic, p_subpic->i_status );
340     }
341
342     /* Remove reservation flag */
343     p_subpic->i_status = READY_SUBPICTURE;
344
345     if( p_subpic->i_channel == DEFAULT_CHAN )
346     {
347         p_subpic->i_channel = 0xFFFF;
348         spu_Control( p_spu, SPU_CHANNEL_CLEAR, DEFAULT_CHAN );
349         p_subpic->i_channel = DEFAULT_CHAN;
350     }
351 }
352
353 /**
354  * Allocate a subpicture in the spu heap.
355  *
356  * This function create a reserved subpicture in the spu heap.
357  * A null pointer is returned if the function fails. This method provides an
358  * already allocated zone of memory in the spu data fields. It needs locking
359  * since several pictures can be created by several producers threads.
360  * \param p_spu the subpicture unit in which to create the subpicture
361  * \return NULL on error, a reserved subpicture otherwise
362  */
363 subpicture_t *spu_CreateSubpicture( spu_t *p_spu )
364 {
365     int                 i_subpic;                        /* subpicture index */
366     subpicture_t *      p_subpic = NULL;            /* first free subpicture */
367
368     /* Get lock */
369     vlc_mutex_lock( &p_spu->subpicture_lock );
370
371     /*
372      * Look for an empty place
373      */
374     p_subpic = NULL;
375     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
376     {
377         if( p_spu->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE )
378         {
379             /* Subpicture is empty and ready for allocation */
380             p_subpic = &p_spu->p_subpicture[i_subpic];
381             p_spu->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
382             break;
383         }
384     }
385
386     /* If no free subpicture could be found */
387     if( p_subpic == NULL )
388     {
389         msg_Err( p_spu, "subpicture heap is full" );
390         vlc_mutex_unlock( &p_spu->subpicture_lock );
391         return NULL;
392     }
393
394     /* Copy subpicture information, set some default values */
395     memset( p_subpic, 0, sizeof(subpicture_t) );
396     p_subpic->i_status   = RESERVED_SUBPICTURE;
397     p_subpic->b_absolute = true;
398     p_subpic->b_pausable = false;
399     p_subpic->b_fade     = false;
400     p_subpic->i_alpha    = 0xFF;
401     p_subpic->p_region   = NULL;
402     p_subpic->pf_render  = NULL;
403     p_subpic->pf_destroy = NULL;
404     p_subpic->p_sys      = NULL;
405     vlc_mutex_unlock( &p_spu->subpicture_lock );
406
407     p_subpic->pf_create_region = __spu_CreateRegion;
408     p_subpic->pf_make_region = __spu_MakeRegion;
409     p_subpic->pf_destroy_region = __spu_DestroyRegion;
410
411     return p_subpic;
412 }
413
414 /**
415  * Remove a subpicture from the heap
416  *
417  * This function frees a previously reserved subpicture.
418  * It is meant to be used when the construction of a picture aborted.
419  * This function does not need locking since reserved subpictures are ignored
420  * by the spu.
421  */
422 void spu_DestroySubpicture( spu_t *p_spu, subpicture_t *p_subpic )
423 {
424     /* Get lock */
425     vlc_mutex_lock( &p_spu->subpicture_lock );
426
427     /* There can be race conditions so we need to check the status */
428     if( p_subpic->i_status == FREE_SUBPICTURE )
429     {
430         vlc_mutex_unlock( &p_spu->subpicture_lock );
431         return;
432     }
433
434     /* Check if status is valid */
435     if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
436            && ( p_subpic->i_status != READY_SUBPICTURE ) )
437     {
438         msg_Err( p_spu, "subpicture %p has invalid status %d",
439                          p_subpic, p_subpic->i_status );
440     }
441
442     while( p_subpic->p_region )
443     {
444         subpicture_region_t *p_region = p_subpic->p_region;
445         p_subpic->p_region = p_region->p_next;
446         spu_DestroyRegion( p_spu, p_region );
447     }
448
449     if( p_subpic->pf_destroy )
450     {
451         p_subpic->pf_destroy( p_subpic );
452     }
453
454     p_subpic->i_status = FREE_SUBPICTURE;
455
456     vlc_mutex_unlock( &p_spu->subpicture_lock );
457 }
458
459 /*****************************************************************************
460  * spu_RenderSubpictures: render a subpicture list
461  *****************************************************************************
462  * This function renders all sub picture units in the list.
463  *****************************************************************************/
464 static void SpuRenderCreateBlend( spu_t *p_spu, vlc_fourcc_t i_chroma, int i_aspect )
465 {
466     filter_t *p_blend;
467
468     assert( !p_spu->p_blend );
469
470     p_spu->p_blend =
471     p_blend        = vlc_custom_create( p_spu, sizeof(filter_t),
472                                         VLC_OBJECT_GENERIC, "blend" );
473     if( !p_blend )
474         return;
475
476     es_format_Init( &p_blend->fmt_in, VIDEO_ES, 0 );
477
478     es_format_Init( &p_blend->fmt_out, VIDEO_ES, 0 );
479     p_blend->fmt_out.video.i_x_offset = 0;
480     p_blend->fmt_out.video.i_y_offset = 0;
481     p_blend->fmt_out.video.i_chroma = i_chroma;
482     p_blend->fmt_out.video.i_aspect = i_aspect;
483
484     /* The blend module will be loaded when needed with the real
485     * input format */
486     p_blend->p_module = NULL;
487
488     /* */
489     vlc_object_attach( p_blend, p_spu );
490 }
491 static void SpuRenderUpdateBlend( spu_t *p_spu, int i_out_width, int i_out_height, const video_format_t *p_in_fmt )
492 {
493     filter_t *p_blend = p_spu->p_blend;
494
495     assert( p_blend );
496
497     /* */
498     if( p_blend->p_module && p_blend->fmt_in.video.i_chroma != p_in_fmt->i_chroma )
499     {
500         /* The chroma is not the same, we need to reload the blend module
501          * XXX to match the old behaviour just test !p_blend->fmt_in.video.i_chroma */
502         module_Unneed( p_blend, p_blend->p_module );
503         p_blend->p_module = NULL;
504     }
505
506     /* */
507     p_blend->fmt_in.video = *p_in_fmt;
508
509     /* */
510     p_blend->fmt_out.video.i_width =
511     p_blend->fmt_out.video.i_visible_width = i_out_width;
512     p_blend->fmt_out.video.i_height =
513     p_blend->fmt_out.video.i_visible_height = i_out_height;
514
515     /* */
516     if( !p_blend->p_module )
517         p_blend->p_module = module_Need( p_blend, "video blending", 0, 0 );
518 }
519 static void SpuRenderCreateAndLoadText( spu_t *p_spu, int i_width, int i_height )
520 {
521     filter_t *p_text;
522
523     assert( !p_spu->p_text );
524
525     p_spu->p_text =
526     p_text        = vlc_custom_create( p_spu, sizeof(filter_t),
527                                        VLC_OBJECT_GENERIC, "spu text" );
528     if( !p_text )
529         return;
530
531     es_format_Init( &p_text->fmt_in, VIDEO_ES, 0 );
532
533     es_format_Init( &p_text->fmt_out, VIDEO_ES, 0 );
534     p_text->fmt_out.video.i_width =
535     p_text->fmt_out.video.i_visible_width = i_width;
536     p_text->fmt_out.video.i_height =
537     p_text->fmt_out.video.i_visible_height = i_height;
538
539     p_text->pf_sub_buffer_new = spu_new_buffer;
540     p_text->pf_sub_buffer_del = spu_del_buffer;
541
542     vlc_object_attach( p_text, p_spu );
543
544     /* FIXME TOCHECK shouldn't module_Need( , , psz_modulename, false ) do the
545      * same than these 2 calls ? */
546     char *psz_modulename = var_CreateGetString( p_spu, "text-renderer" );
547     if( psz_modulename && *psz_modulename )
548     {
549         p_text->p_module = module_Need( p_text, "text renderer",
550                                         psz_modulename, true );
551     }
552     free( psz_modulename );
553
554     if( !p_text->p_module )
555         p_text->p_module = module_Need( p_text, "text renderer", NULL, false );
556 }
557
558 static void SpuRenderCreateAndLoadScale( spu_t *p_spu )
559 {
560     /* FIXME: We'll also be using it for YUVA and RGBA blending ... */
561     const vlc_fourcc_t i_dummy_chroma = VLC_FOURCC('Y','U','V','P');
562
563     filter_t *p_scale;
564
565     assert( !p_spu->p_scale );
566
567     p_spu->p_scale =
568     p_scale        = vlc_custom_create( p_spu, sizeof(filter_t),
569                                         VLC_OBJECT_GENERIC, "scale" );
570     if( !p_scale )
571         return;
572
573     es_format_Init( &p_scale->fmt_in, VIDEO_ES, 0 );
574     p_scale->fmt_in.video.i_chroma = i_dummy_chroma;
575     p_scale->fmt_in.video.i_width =
576     p_scale->fmt_in.video.i_height = 32;
577
578     es_format_Init( &p_scale->fmt_out, VIDEO_ES, 0 );
579     p_scale->fmt_out.video.i_chroma = i_dummy_chroma;
580     p_scale->fmt_out.video.i_width =
581     p_scale->fmt_out.video.i_height = 16;
582
583     p_scale->pf_vout_buffer_new = spu_new_video_buffer;
584     p_scale->pf_vout_buffer_del = spu_del_video_buffer;
585
586     vlc_object_attach( p_scale, p_spu );
587     p_scale->p_module = module_Need( p_spu->p_scale, "video filter2", 0, 0 );
588 }
589
590 static void SpuRenderText( spu_t *p_spu, bool *pb_rerender_text,
591                            subpicture_t *p_subpic, subpicture_region_t *p_region, int i_min_scale_ratio )
592 {
593     assert( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') );
594
595     if( !p_spu->p_text || !p_spu->p_text->p_module )
596         goto exit;
597
598     /* Setup 3 variables which can be used to render
599      * time-dependent text (and effects). The first indicates
600      * the total amount of time the text will be on screen,
601      * the second the amount of time it has already been on
602      * screen (can be a negative value as text is layed out
603      * before it is rendered) and the third is a feedback
604      * variable from the renderer - if the renderer sets it
605      * then this particular text is time-dependent, eg. the
606      * visual progress bar inside the text in karaoke and the
607      * text needs to be rendered multiple times in order for
608      * the effect to work - we therefore need to return the
609      * region to its original state at the end of the loop,
610      * instead of leaving it in YUVA or YUVP.
611      * Any renderer which is unaware of how to render
612      * time-dependent text can happily ignore the variables
613      * and render the text the same as usual - it should at
614      * least show up on screen, but the effect won't change
615      * the text over time.
616      */
617
618     /* FIXME why these variables are recreated every time and not
619      * when text renderer module was created ? */
620     var_Create( p_spu->p_text, "spu-duration", VLC_VAR_TIME );
621     var_Create( p_spu->p_text, "spu-elapsed", VLC_VAR_TIME );
622     var_Create( p_spu->p_text, "text-rerender", VLC_VAR_BOOL );
623     var_Create( p_spu->p_text, "scale", VLC_VAR_INTEGER );
624
625     var_SetTime( p_spu->p_text, "spu-duration", p_subpic->i_stop - p_subpic->i_start );
626     var_SetTime( p_spu->p_text, "spu-elapsed", mdate() - p_subpic->i_start );
627     var_SetBool( p_spu->p_text, "text-rerender", false );
628     var_SetInteger( p_spu->p_text, "scale", i_min_scale_ratio );
629
630     if( p_spu->p_text->pf_render_html && p_region->psz_html )
631     {
632         p_spu->p_text->pf_render_html( p_spu->p_text,
633                                        p_region, p_region );
634     }
635     else if( p_spu->p_text->pf_render_text )
636     {
637         p_spu->p_text->pf_render_text( p_spu->p_text,
638                                        p_region, p_region );
639     }
640     *pb_rerender_text = var_GetBool( p_spu->p_text, "text-rerender" );
641
642     var_Destroy( p_spu->p_text, "spu-duration" );
643     var_Destroy( p_spu->p_text, "spu-elapsed" );
644     var_Destroy( p_spu->p_text, "text-rerender" );
645     var_Destroy( p_spu->p_text, "scale" );
646
647 exit:
648     p_region->i_align |= SUBPICTURE_RENDERED;
649 }
650
651 static void SpuRenderRegion( spu_t *p_spu,
652                              picture_t *p_pic_dst, picture_t *p_pic_src,
653                              subpicture_t *p_subpic, subpicture_region_t *p_region,
654                              const int i_scale_width_orig, const int i_scale_height_orig,
655                              const int pi_subpic_x[SCALE_SIZE],
656                              const int pi_scale_width[SCALE_SIZE],
657                              const int pi_scale_height[SCALE_SIZE],
658                              const video_format_t *p_fmt )
659 {
660     video_format_t orig_fmt;
661     bool b_rerender_text;
662     int i_fade_alpha;
663     int i_x_offset;
664     int i_y_offset;
665     int i_scale_idx;
666     int i_inv_scale_x;
667     int i_inv_scale_y;
668
669     orig_fmt = p_region->fmt;
670     b_rerender_text = false;
671     if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') )
672     {
673         SpuRenderText( p_spu, &b_rerender_text, p_subpic, p_region, __MIN(i_scale_width_orig, i_scale_height_orig) );
674
675         /* Check if the rendering has failed ... */
676         if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') )
677             goto exit;
678     }
679
680     if( p_region->i_align & SUBPICTURE_RENDERED )
681     {
682         /* We are using a region which come from rendered text */
683         i_scale_idx   = SCALE_TEXT;
684         i_inv_scale_x = i_scale_width_orig;
685         i_inv_scale_y = i_scale_height_orig;
686     }
687     else
688     {
689         i_scale_idx   = SCALE_DEFAULT;
690         i_inv_scale_x = 1000;
691         i_inv_scale_y = 1000;
692     }
693
694     i_x_offset = (p_region->i_x + pi_subpic_x[ i_scale_idx ]) * i_inv_scale_x / 1000;
695     i_y_offset = (p_region->i_y + p_subpic->i_y) * i_inv_scale_y / 1000;
696
697     /* Force palette if requested */
698     if( p_spu->b_force_palette &&
699         p_region->fmt.i_chroma == VLC_FOURCC('Y','U','V','P') )
700     {
701         /* It looks so wrong I won't comment
702          * p_palette->palette is [256][4] with a int i_entries
703          * p_spu->palette is [4][4]
704          * */
705         memcpy( p_region->fmt.p_palette->palette, p_spu->palette, 16 );
706     }
707
708     if( p_spu->p_scale &&
709         ( ( pi_scale_width[i_scale_idx]  > 0 && pi_scale_width[i_scale_idx]  != 1000 ) ||
710           ( pi_scale_height[i_scale_idx] > 0 && pi_scale_height[i_scale_idx] != 1000 ) ) )
711     {
712         const int i_dst_width  = p_region->fmt.i_width  * pi_scale_width[i_scale_idx] / 1000;
713         const int i_dst_height = p_region->fmt.i_height * pi_scale_height[i_scale_idx] / 1000;
714
715         /* Destroy if cache is unusable */
716         if( p_region->p_cache )
717         {
718             if( p_region->p_cache->fmt.i_width  != i_dst_width ||
719                 p_region->p_cache->fmt.i_height != i_dst_height )
720             {
721                 p_subpic->pf_destroy_region( VLC_OBJECT(p_spu),
722                                              p_region->p_cache );
723                 p_region->p_cache = NULL;
724             }
725         }
726
727         /* Scale if needed into cache */
728         if( !p_region->p_cache )
729         {
730             picture_t *p_pic;
731
732             p_spu->p_scale->fmt_in.video = p_region->fmt;
733             p_spu->p_scale->fmt_out.video = p_region->fmt;
734
735             p_region->p_cache =
736                 p_subpic->pf_create_region( VLC_OBJECT(p_spu),
737                                             &p_spu->p_scale->fmt_out.video );
738             p_region->p_cache->p_next = p_region->p_next;
739
740             if( p_spu->p_scale->fmt_out.video.p_palette )
741                 *p_spu->p_scale->fmt_out.video.p_palette =
742                     *p_region->fmt.p_palette;
743
744             vout_CopyPicture( p_spu, &p_region->p_cache->picture,
745                               &p_region->picture );
746
747             p_spu->p_scale->fmt_out.video.i_width = i_dst_width;
748             p_spu->p_scale->fmt_out.video.i_height = i_dst_height;
749
750             p_spu->p_scale->fmt_out.video.i_visible_width =
751                 p_region->fmt.i_visible_width * pi_scale_width[ i_scale_idx ] / 1000;
752             p_spu->p_scale->fmt_out.video.i_visible_height =
753                 p_region->fmt.i_visible_height * pi_scale_height[ i_scale_idx ] / 1000;
754
755             p_region->p_cache->fmt = p_spu->p_scale->fmt_out.video;
756             p_region->p_cache->i_x = p_region->i_x * pi_scale_width[ i_scale_idx ] / 1000;
757             p_region->p_cache->i_y = p_region->i_y * pi_scale_height[ i_scale_idx ] / 1000;
758             p_region->p_cache->i_align = p_region->i_align;
759             p_region->p_cache->i_alpha = p_region->i_alpha;
760
761             p_pic = p_spu->p_scale->pf_video_filter(
762                              p_spu->p_scale, &p_region->p_cache->picture );
763             if( p_pic )
764             {
765                 p_region->p_cache->picture = *p_pic;
766                 free( p_pic );
767             }
768         }
769
770         /* And use the scaled picture */
771         if( p_region->p_cache )
772             p_region = p_region->p_cache;
773     }
774
775     if( p_region->i_align & SUBPICTURE_ALIGN_BOTTOM )
776     {
777         i_y_offset = p_fmt->i_height - p_region->fmt.i_height -
778             (p_subpic->i_y + p_region->i_y) * i_inv_scale_y / 1000;
779     }
780     else if ( !(p_region->i_align & SUBPICTURE_ALIGN_TOP) )
781     {
782         i_y_offset = p_fmt->i_height / 2 - p_region->fmt.i_height / 2;
783     }
784
785     if( p_region->i_align & SUBPICTURE_ALIGN_RIGHT )
786     {
787         i_x_offset = p_fmt->i_width - p_region->fmt.i_width -
788             (pi_subpic_x[ i_scale_idx ] + p_region->i_x)
789             * i_inv_scale_x / 1000;
790     }
791     else if ( !(p_region->i_align & SUBPICTURE_ALIGN_LEFT) )
792     {
793         i_x_offset = p_fmt->i_width / 2 - p_region->fmt.i_width / 2;
794     }
795
796     if( p_subpic->b_absolute )
797     {
798         i_x_offset = (p_region->i_x +
799             pi_subpic_x[ i_scale_idx ] *
800                              pi_scale_width[ i_scale_idx ] / 1000)
801             * i_inv_scale_x / 1000;
802         i_y_offset = (p_region->i_y +
803             p_subpic->i_y * pi_scale_height[ i_scale_idx ] / 1000)
804             * i_inv_scale_y / 1000;
805
806     }
807
808     i_x_offset = __MAX( i_x_offset, 0 );
809     i_y_offset = __MAX( i_y_offset, 0 );
810
811     if( p_spu->i_margin != 0 && !p_spu->b_force_crop )
812     {
813         int i_diff = 0;
814         int i_low = (i_y_offset - p_spu->i_margin) * i_inv_scale_y / 1000;
815         int i_high = i_low + p_region->fmt.i_height;
816
817         /* crop extra margin to keep within bounds */
818         if( i_low < 0 )
819             i_diff = i_low;
820         if( i_high > (int)p_fmt->i_height )
821             i_diff = i_high - p_fmt->i_height;
822         i_y_offset -= ( p_spu->i_margin * i_inv_scale_y / 1000 + i_diff );
823     }
824
825     /* Force cropping if requested */
826     if( p_spu->b_force_crop )
827     {
828         video_format_t *p_fmt = &p_region->fmt;
829         int i_crop_x = p_spu->i_crop_x * pi_scale_width[ i_scale_idx ] / 1000
830                             * i_inv_scale_x / 1000;
831         int i_crop_y = p_spu->i_crop_y * pi_scale_height[ i_scale_idx ] / 1000
832                             * i_inv_scale_y / 1000;
833         int i_crop_width = p_spu->i_crop_width * pi_scale_width[ i_scale_idx ] / 1000
834                             * i_inv_scale_x / 1000;
835         int i_crop_height = p_spu->i_crop_height * pi_scale_height[ i_scale_idx ] / 1000
836                             * i_inv_scale_y / 1000;
837
838         /* Find the intersection */
839         if( i_crop_x + i_crop_width <= i_x_offset ||
840             i_x_offset + (int)p_fmt->i_visible_width < i_crop_x ||
841             i_crop_y + i_crop_height <= i_y_offset ||
842             i_y_offset + (int)p_fmt->i_visible_height < i_crop_y )
843         {
844             /* No intersection */
845             p_fmt->i_visible_width = p_fmt->i_visible_height = 0;
846         }
847         else
848         {
849             int i_x, i_y, i_x_end, i_y_end;
850             i_x = __MAX( i_crop_x, i_x_offset );
851             i_y = __MAX( i_crop_y, i_y_offset );
852             i_x_end = __MIN( i_crop_x + i_crop_width,
853                            i_x_offset + (int)p_fmt->i_visible_width );
854             i_y_end = __MIN( i_crop_y + i_crop_height,
855                            i_y_offset + (int)p_fmt->i_visible_height );
856
857             p_fmt->i_x_offset = i_x - i_x_offset;
858             p_fmt->i_y_offset = i_y - i_y_offset;
859             p_fmt->i_visible_width = i_x_end - i_x;
860             p_fmt->i_visible_height = i_y_end - i_y;
861
862             i_x_offset = i_x;
863             i_y_offset = i_y;
864         }
865     }
866
867     i_x_offset = __MAX( i_x_offset, 0 );
868     i_y_offset = __MAX( i_y_offset, 0 );
869
870     /* Compute alpha blend value */
871     i_fade_alpha = 255;
872     if( p_subpic->b_fade )
873     {
874         mtime_t i_fade_start = ( p_subpic->i_stop +
875                                  p_subpic->i_start ) / 2;
876         mtime_t i_now = mdate();
877         if( i_now >= i_fade_start && p_subpic->i_stop > i_fade_start )
878         {
879             i_fade_alpha = 255 * ( p_subpic->i_stop - i_now ) /
880                            ( p_subpic->i_stop - i_fade_start );
881         }
882     }
883
884     /* Update the blender */
885     SpuRenderUpdateBlend( p_spu, p_fmt->i_width, p_fmt->i_height, &p_region->fmt );
886
887     if( p_spu->p_blend->p_module )
888     {
889         p_spu->p_blend->pf_video_blend( p_spu->p_blend, p_pic_dst,
890             p_pic_src, &p_region->picture, i_x_offset, i_y_offset,
891             i_fade_alpha * p_subpic->i_alpha * p_region->i_alpha / 65025 );
892     }
893     else
894     {
895         msg_Err( p_spu, "blending %4.4s to %4.4s failed",
896                  (char *)&p_spu->p_blend->fmt_out.video.i_chroma,
897                  (char *)&p_spu->p_blend->fmt_out.video.i_chroma );
898     }
899
900 exit:
901     if( b_rerender_text )
902     {
903         /* Some forms of subtitles need to be re-rendered more than
904          * once, eg. karaoke. We therefore restore the region to its
905          * pre-rendered state, so the next time through everything is
906          * calculated again.
907          */
908         p_region->picture.pf_release( &p_region->picture );
909         memset( &p_region->picture, 0, sizeof( picture_t ) );
910         p_region->fmt = orig_fmt;
911         p_region->i_align &= ~SUBPICTURE_RENDERED;
912     }
913 }
914
915 void spu_RenderSubpictures( spu_t *p_spu, video_format_t *p_fmt,
916                             picture_t *p_pic_dst, picture_t *p_pic_src,
917                             subpicture_t *p_subpic,
918                             int i_scale_width_orig, int i_scale_height_orig )
919 {
920     int i_source_video_width;
921     int i_source_video_height;
922     subpicture_t *p_subpic_v;
923
924     /* Get lock */
925     vlc_mutex_lock( &p_spu->subpicture_lock );
926
927     for( p_subpic_v = p_subpic;
928             p_subpic_v != NULL && p_subpic_v->i_status != FREE_SUBPICTURE;
929             p_subpic_v = p_subpic_v->p_next )
930     {
931         if( p_subpic_v->pf_pre_render )
932         {
933             p_subpic_v->pf_pre_render( p_fmt, p_spu, p_subpic_v, mdate() );
934         }
935     }
936
937     if( i_scale_width_orig <= 0 )
938         i_scale_width_orig = 1000;
939     if( i_scale_height_orig <= 0 )
940         i_scale_height_orig = 1000;
941
942     i_source_video_width  = p_fmt->i_width  * 1000 / i_scale_width_orig;
943     i_source_video_height = p_fmt->i_height * 1000 / i_scale_height_orig;
944
945     /* Check i_status again to make sure spudec hasn't destroyed the subpic */
946     for( ; ( p_subpic != NULL ) && ( p_subpic->i_status != FREE_SUBPICTURE ); p_subpic = p_subpic->p_next )
947     {
948         subpicture_region_t *p_region;
949         int pi_scale_width[ SCALE_SIZE ];
950         int pi_scale_height[ SCALE_SIZE ];
951         int pi_subpic_x[ SCALE_SIZE ];
952         int k;
953
954         /* If the source video and subtitles stream agree on the size of
955          * the video then disregard all further references to the subtitle
956          * stream.
957          */
958         if( ( i_source_video_height == p_subpic->i_original_picture_height ) &&
959             ( i_source_video_width  == p_subpic->i_original_picture_width ) )
960         {
961             /* FIXME this looks wrong */
962             p_subpic->i_original_picture_height = 0;
963             p_subpic->i_original_picture_width = 0;
964         }
965
966         for( k = 0; k < SCALE_SIZE ; k++ )
967             pi_subpic_x[ k ] = p_subpic->i_x;
968
969         if( p_subpic->pf_update_regions )
970         {
971             /* FIXME that part look like crap too if there is more than 1 region */
972
973             if( p_subpic->p_region )
974                 spu_DestroyRegion( p_spu, p_subpic->p_region );
975
976             /* TODO do not reverse the scaling that was done before calling
977              * spu_RenderSubpictures, just pass it along (or do it inside
978              * spu_RenderSubpictures) */
979             video_format_t fmt_org = *p_fmt;
980             fmt_org.i_width =
981             fmt_org.i_visible_width = i_source_video_width;
982             fmt_org.i_height =
983             fmt_org.i_visible_height = i_source_video_height;
984
985             p_subpic->p_region = p_subpic->pf_update_regions( &fmt_org, p_spu, p_subpic, mdate() );
986         }
987
988         /* */
989         p_region = p_subpic->p_region;
990         if( !p_region )
991             continue;
992
993         /* Create the blending module */
994         if( !p_spu->p_blend )
995             SpuRenderCreateBlend( p_spu, p_fmt->i_chroma, p_fmt->i_aspect );
996
997         /* Load the text rendering module; it is possible there is a
998          * text region somewhere in the subpicture other than the first
999          * element in the region list, so just load it anyway as we'll
1000          * probably want it sooner or later. */
1001         if( !p_spu->p_text )
1002             SpuRenderCreateAndLoadText( p_spu, p_fmt->i_width, p_fmt->i_height );
1003
1004         if( p_spu->p_text )
1005         {
1006             subpicture_region_t *p_text_region = p_subpic->p_region;
1007
1008             /* Only overwrite the size fields if the region is still in
1009              * pre-rendered TEXT format. We have to traverse the subregion
1010              * list because if more than one subregion is present, the text
1011              * region isn't guarentteed to be the first in the list, and
1012              * only text regions use this flag. All of this effort assists
1013              * with the rescaling of text that has been rendered at native
1014              * resolution, rather than video resolution.
1015              */
1016             while( p_text_region &&
1017                    p_text_region->fmt.i_chroma != VLC_FOURCC('T','E','X','T') )
1018             {
1019                 p_text_region = p_text_region->p_next;
1020             }
1021
1022             if( p_text_region &&
1023                 ( ( p_text_region->i_align & SUBPICTURE_RENDERED ) == 0 ) )
1024             {
1025                 if( p_subpic->i_original_picture_height > 0 &&
1026                     p_subpic->i_original_picture_width  > 0 )
1027                 {
1028                     p_spu->p_text->fmt_out.video.i_width =
1029                     p_spu->p_text->fmt_out.video.i_visible_width =
1030                         p_subpic->i_original_picture_width;
1031                     p_spu->p_text->fmt_out.video.i_height =
1032                     p_spu->p_text->fmt_out.video.i_visible_height =
1033                         p_subpic->i_original_picture_height;
1034                 }
1035                 else
1036                 {
1037                     p_spu->p_text->fmt_out.video.i_width =
1038                     p_spu->p_text->fmt_out.video.i_visible_width =
1039                         p_fmt->i_width;
1040                     p_spu->p_text->fmt_out.video.i_height =
1041                     p_spu->p_text->fmt_out.video.i_visible_height =
1042                         p_fmt->i_height;
1043                 }
1044             }
1045
1046             /* XXX for text:
1047              *  scale[] allows to pass from rendered size (by text module) to video output size */
1048             pi_scale_width[SCALE_TEXT] = p_fmt->i_width * 1000 /
1049                                           p_spu->p_text->fmt_out.video.i_width;
1050             pi_scale_height[SCALE_TEXT]= p_fmt->i_height * 1000 /
1051                                           p_spu->p_text->fmt_out.video.i_height;
1052         }
1053         else
1054         {
1055             /* Just set a value to avoid using invalid memory while looping over the array */
1056             pi_scale_width[SCALE_TEXT] =
1057             pi_scale_height[SCALE_TEXT]= 1000;
1058         }
1059
1060         /* XXX for default:
1061          *  scale[] allows to pass from native (either video or original) size to output size */
1062
1063         if( p_subpic->i_original_picture_height > 0 &&
1064             p_subpic->i_original_picture_width  > 0 )
1065         {
1066             pi_scale_width[SCALE_DEFAULT]  = p_fmt->i_width  * 1000 / p_subpic->i_original_picture_width;
1067             pi_scale_height[SCALE_DEFAULT] = p_fmt->i_height * 1000 / p_subpic->i_original_picture_height;
1068         }
1069         else
1070         {
1071             pi_scale_width[ SCALE_DEFAULT ]  = i_scale_width_orig;
1072             pi_scale_height[ SCALE_DEFAULT ] = i_scale_height_orig;
1073         }
1074
1075         for( k = 0; k < SCALE_SIZE ; k++ )
1076         {
1077             /* Case of both width and height being specified has been dealt
1078              * with above by instead rendering to an output pane of the
1079              * explicit dimensions specified - we don't need to scale it.
1080              */
1081             if( p_subpic->i_original_picture_height > 0 &&
1082                 p_subpic->i_original_picture_width <= 0 )
1083             {
1084                 pi_scale_height[ k ] = pi_scale_height[ k ] * i_source_video_height /
1085                                  p_subpic->i_original_picture_height;
1086                 pi_scale_width[ k ]  = pi_scale_width[ k ]  * i_source_video_height /
1087                                  p_subpic->i_original_picture_height;
1088             }
1089         }
1090
1091         /* Set default subpicture aspect ratio */
1092         if( !p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den )
1093         {
1094             if( p_region->fmt.i_aspect != 0 )
1095             {
1096                 p_region->fmt.i_sar_den = p_region->fmt.i_aspect;
1097                 p_region->fmt.i_sar_num = VOUT_ASPECT_FACTOR;
1098             }
1099             else
1100             {
1101                 p_region->fmt.i_sar_den = p_fmt->i_sar_den;
1102                 p_region->fmt.i_sar_num = p_fmt->i_sar_num;
1103             }
1104         }
1105
1106         /* Take care of the aspect ratio */
1107         if( ( p_region->fmt.i_sar_num * p_fmt->i_sar_den ) !=
1108             ( p_region->fmt.i_sar_den * p_fmt->i_sar_num ) )
1109         {
1110             for( k = 0; k < SCALE_SIZE; k++ )
1111             {
1112                 pi_scale_width[k] = pi_scale_width[ k ] *
1113                     (int64_t)p_region->fmt.i_sar_num * p_fmt->i_sar_den /
1114                     p_region->fmt.i_sar_den / p_fmt->i_sar_num;
1115
1116                 pi_subpic_x[k] = p_subpic->i_x * pi_scale_width[ k ] / 1000;
1117             }
1118         }
1119
1120         /* Load the scaling module when needed */
1121         if( !p_spu->p_scale )
1122         {
1123             bool b_scale_used = false;
1124
1125             for( k = 0; k < SCALE_SIZE; k++ )
1126             {
1127                 const int i_scale_w = pi_scale_width[k];
1128                 const int i_scale_h = pi_scale_height[k];
1129                 if( ( i_scale_w > 0 && i_scale_w != 1000 ) || ( i_scale_h > 0 && i_scale_h != 1000 ) )
1130                     b_scale_used = true;
1131             }
1132
1133             if( b_scale_used )
1134                 SpuRenderCreateAndLoadScale( p_spu );
1135         }
1136
1137         for( ; p_region != NULL; p_region = p_region->p_next )
1138             SpuRenderRegion( p_spu, p_pic_dst, p_pic_src,
1139                              p_subpic, p_region, i_scale_width_orig, i_scale_height_orig,
1140                              pi_subpic_x, pi_scale_width, pi_scale_height,
1141                              p_fmt );
1142     }
1143
1144     vlc_mutex_unlock( &p_spu->subpicture_lock );
1145 }
1146
1147 /*****************************************************************************
1148  * spu_SortSubpictures: find the subpictures to display
1149  *****************************************************************************
1150  * This function parses all subpictures and decides which ones need to be
1151  * displayed. This operation does not need lock, since only READY_SUBPICTURE
1152  * are handled. If no picture has been selected, display_date will depend on
1153  * the subpicture.
1154  * We also check for ephemer DVD subpictures (subpictures that have
1155  * to be removed if a newer one is available), which makes it a lot
1156  * more difficult to guess if a subpicture has to be rendered or not.
1157  *****************************************************************************/
1158 subpicture_t *spu_SortSubpictures( spu_t *p_spu, mtime_t display_date,
1159                                    bool b_paused )
1160 {
1161     int i_index, i_channel;
1162     subpicture_t *p_subpic = NULL;
1163     subpicture_t *p_ephemer;
1164     mtime_t      ephemer_date;
1165
1166     /* Run subpicture filters */
1167     filter_chain_SubFilter( p_spu->p_chain, display_date );
1168
1169     /* We get an easily parsable chained list of subpictures which
1170      * ends with NULL since p_subpic was initialized to NULL. */
1171     for( i_channel = 0; i_channel < p_spu->i_channel; i_channel++ )
1172     {
1173         p_ephemer = 0;
1174         ephemer_date = 0;
1175
1176         for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
1177         {
1178             if( p_spu->p_subpicture[i_index].i_channel != i_channel ||
1179                 p_spu->p_subpicture[i_index].i_status != READY_SUBPICTURE )
1180             {
1181                 continue;
1182             }
1183             if( display_date &&
1184                 display_date < p_spu->p_subpicture[i_index].i_start )
1185             {
1186                 /* Too early, come back next monday */
1187                 continue;
1188             }
1189
1190             if( p_spu->p_subpicture[i_index].i_start > ephemer_date )
1191                 ephemer_date = p_spu->p_subpicture[i_index].i_start;
1192
1193             if( display_date > p_spu->p_subpicture[i_index].i_stop &&
1194                 ( !p_spu->p_subpicture[i_index].b_ephemer ||
1195                   p_spu->p_subpicture[i_index].i_stop >
1196                   p_spu->p_subpicture[i_index].i_start ) &&
1197                 !( p_spu->p_subpicture[i_index].b_pausable &&
1198                    b_paused ) )
1199             {
1200                 /* Too late, destroy the subpic */
1201                 spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] );
1202                 continue;
1203             }
1204
1205             /* If this is an ephemer subpic, add it to our list */
1206             if( p_spu->p_subpicture[i_index].b_ephemer )
1207             {
1208                 p_spu->p_subpicture[i_index].p_next = p_ephemer;
1209                 p_ephemer = &p_spu->p_subpicture[i_index];
1210
1211                 continue;
1212             }
1213
1214             p_spu->p_subpicture[i_index].p_next = p_subpic;
1215             p_subpic = &p_spu->p_subpicture[i_index];
1216         }
1217
1218         /* If we found ephemer subpictures, check if they have to be
1219          * displayed or destroyed */
1220         while( p_ephemer != NULL )
1221         {
1222             subpicture_t *p_tmp = p_ephemer;
1223             p_ephemer = p_ephemer->p_next;
1224
1225             if( p_tmp->i_start < ephemer_date )
1226             {
1227                 /* Ephemer subpicture has lived too long */
1228                 spu_DestroySubpicture( p_spu, p_tmp );
1229             }
1230             else
1231             {
1232                 /* Ephemer subpicture can still live a bit */
1233                 p_tmp->p_next = p_subpic;
1234                 p_subpic = p_tmp;
1235             }
1236         }
1237     }
1238
1239     return p_subpic;
1240 }
1241
1242 /*****************************************************************************
1243  * SpuClearChannel: clear an spu channel
1244  *****************************************************************************
1245  * This function destroys the subpictures which belong to the spu channel
1246  * corresponding to i_channel_id.
1247  *****************************************************************************/
1248 static void SpuClearChannel( spu_t *p_spu, int i_channel, bool b_locked )
1249 {
1250     int          i_subpic;                               /* subpicture index */
1251     subpicture_t *p_subpic = NULL;                  /* first free subpicture */
1252
1253     if( !b_locked )
1254         vlc_mutex_lock( &p_spu->subpicture_lock );
1255
1256     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
1257     {
1258         p_subpic = &p_spu->p_subpicture[i_subpic];
1259         if( p_subpic->i_status == FREE_SUBPICTURE
1260             || ( p_subpic->i_status != RESERVED_SUBPICTURE
1261                  && p_subpic->i_status != READY_SUBPICTURE ) )
1262         {
1263             continue;
1264         }
1265
1266         if( p_subpic->i_channel == i_channel )
1267         {
1268             while( p_subpic->p_region )
1269             {
1270                 subpicture_region_t *p_region = p_subpic->p_region;
1271                 p_subpic->p_region = p_region->p_next;
1272                 spu_DestroyRegion( p_spu, p_region );
1273             }
1274
1275             if( p_subpic->pf_destroy ) p_subpic->pf_destroy( p_subpic );
1276             p_subpic->i_status = FREE_SUBPICTURE;
1277         }
1278     }
1279
1280     if( !b_locked )
1281         vlc_mutex_unlock( &p_spu->subpicture_lock );
1282 }
1283
1284 /*****************************************************************************
1285  * spu_ControlDefault: default methods for the subpicture unit control.
1286  *****************************************************************************/
1287 static int spu_vaControlDefault( spu_t *p_spu, int i_query, va_list args )
1288 {
1289     int *pi, i;
1290
1291     switch( i_query )
1292     {
1293     case SPU_CHANNEL_REGISTER:
1294         pi = (int *)va_arg( args, int * );
1295         if( pi ) *pi = p_spu->i_channel++;
1296         break;
1297
1298     case SPU_CHANNEL_CLEAR:
1299         i = (int)va_arg( args, int );
1300         SpuClearChannel( p_spu, i, false );
1301         break;
1302
1303     default:
1304         msg_Dbg( p_spu, "control query not supported" );
1305         return VLC_EGENERIC;
1306     }
1307
1308     return VLC_SUCCESS;
1309 }
1310
1311 /*****************************************************************************
1312  * Object variables callbacks
1313  *****************************************************************************/
1314
1315 /*****************************************************************************
1316  * UpdateSPU: update subpicture settings
1317  *****************************************************************************
1318  * This function is called from CropCallback and at initialization time, to
1319  * retrieve crop information from the input.
1320  *****************************************************************************/
1321 static void UpdateSPU( spu_t *p_spu, vlc_object_t *p_object )
1322 {
1323     vlc_value_t val;
1324
1325     p_spu->b_force_palette = false;
1326     p_spu->b_force_crop = false;
1327
1328     if( var_Get( p_object, "highlight", &val ) || !val.b_bool ) return;
1329
1330     p_spu->b_force_crop = true;
1331     var_Get( p_object, "x-start", &val );
1332     p_spu->i_crop_x = val.i_int;
1333     var_Get( p_object, "y-start", &val );
1334     p_spu->i_crop_y = val.i_int;
1335     var_Get( p_object, "x-end", &val );
1336     p_spu->i_crop_width = val.i_int - p_spu->i_crop_x;
1337     var_Get( p_object, "y-end", &val );
1338     p_spu->i_crop_height = val.i_int - p_spu->i_crop_y;
1339
1340     if( var_Get( p_object, "menu-palette", &val ) == VLC_SUCCESS )
1341     {
1342         memcpy( p_spu->palette, val.p_address, 16 );
1343         p_spu->b_force_palette = true;
1344     }
1345
1346     msg_Dbg( p_object, "crop: %i,%i,%i,%i, palette forced: %i",
1347              p_spu->i_crop_x, p_spu->i_crop_y,
1348              p_spu->i_crop_width, p_spu->i_crop_height,
1349              p_spu->b_force_palette );
1350 }
1351
1352 /*****************************************************************************
1353  * CropCallback: called when the highlight properties are changed
1354  *****************************************************************************
1355  * This callback is called from the input thread when we need cropping
1356  *****************************************************************************/
1357 static int CropCallback( vlc_object_t *p_object, char const *psz_var,
1358                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1359 {
1360     (void)psz_var; (void)oldval; (void)newval;
1361     UpdateSPU( (spu_t *)p_data, p_object );
1362     return VLC_SUCCESS;
1363 }
1364
1365 /*****************************************************************************
1366  * Buffers allocation callbacks for the filters
1367  *****************************************************************************/
1368 static subpicture_t *sub_new_buffer( filter_t *p_filter )
1369 {
1370     filter_owner_sys_t *p_sys = p_filter->p_owner;
1371     subpicture_t *p_subpicture = spu_CreateSubpicture( p_sys->p_spu );
1372     if( p_subpicture ) p_subpicture->i_channel = p_sys->i_channel;
1373     return p_subpicture;
1374 }
1375
1376 static void sub_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1377 {
1378     filter_owner_sys_t *p_sys = p_filter->p_owner;
1379     spu_DestroySubpicture( p_sys->p_spu, p_subpic );
1380 }
1381
1382 static subpicture_t *spu_new_buffer( filter_t *p_filter )
1383 {
1384     (void)p_filter;
1385     subpicture_t *p_subpic = (subpicture_t *)malloc(sizeof(subpicture_t));
1386     if( !p_subpic ) return NULL;
1387     memset( p_subpic, 0, sizeof(subpicture_t) );
1388     p_subpic->b_absolute = true;
1389
1390     p_subpic->pf_create_region = __spu_CreateRegion;
1391     p_subpic->pf_make_region = __spu_MakeRegion;
1392     p_subpic->pf_destroy_region = __spu_DestroyRegion;
1393
1394     return p_subpic;
1395 }
1396
1397 static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1398 {
1399     while( p_subpic->p_region )
1400     {
1401         subpicture_region_t *p_region = p_subpic->p_region;
1402         p_subpic->p_region = p_region->p_next;
1403         p_subpic->pf_destroy_region( VLC_OBJECT(p_filter), p_region );
1404     }
1405
1406     free( p_subpic );
1407 }
1408
1409 static picture_t *spu_new_video_buffer( filter_t *p_filter )
1410 {
1411     picture_t *p_picture = malloc( sizeof(picture_t) );
1412     if( !p_picture ) return NULL;
1413     if( vout_AllocatePicture( p_filter, p_picture,
1414                               p_filter->fmt_out.video.i_chroma,
1415                               p_filter->fmt_out.video.i_width,
1416                               p_filter->fmt_out.video.i_height,
1417                               p_filter->fmt_out.video.i_aspect )
1418         != VLC_SUCCESS )
1419     {
1420         free( p_picture );
1421         return NULL;
1422     }
1423
1424     p_picture->pf_release = RegionPictureRelease;
1425
1426     return p_picture;
1427 }
1428
1429 static void spu_del_video_buffer( filter_t *p_filter, picture_t *p_pic )
1430 {
1431     (void)p_filter;
1432     if( p_pic )
1433     {
1434         free( p_pic->p_data_orig );
1435         free( p_pic );
1436     }
1437 }
1438
1439 static int SubFilterCallback( vlc_object_t *p_object, char const *psz_var,
1440                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1441 {
1442     VLC_UNUSED(p_object); VLC_UNUSED(oldval);
1443     VLC_UNUSED(newval); VLC_UNUSED(psz_var);
1444
1445     spu_t *p_spu = (spu_t *)p_data;
1446     vlc_mutex_lock( &p_spu->subpicture_lock );
1447     filter_chain_Reset( p_spu->p_chain, NULL, NULL );
1448     spu_ParseChain( p_spu );
1449     vlc_mutex_unlock( &p_spu->subpicture_lock );
1450     return VLC_SUCCESS;
1451 }
1452
1453 static int sub_filter_allocation_init( filter_t *p_filter, void *p_data )
1454 {
1455     spu_t *p_spu = (spu_t *)p_data;
1456
1457     p_filter->pf_sub_buffer_new = sub_new_buffer;
1458     p_filter->pf_sub_buffer_del = sub_del_buffer;
1459
1460     filter_owner_sys_t *p_sys = malloc( sizeof(filter_owner_sys_t) );
1461     if( !p_sys ) return VLC_EGENERIC;
1462
1463     p_filter->p_owner = p_sys;
1464     spu_Control( p_spu, SPU_CHANNEL_REGISTER, &p_sys->i_channel );
1465     p_sys->p_spu = p_spu;
1466
1467     return VLC_SUCCESS;
1468 }
1469
1470 static void sub_filter_allocation_clear( filter_t *p_filter )
1471 {
1472     filter_owner_sys_t *p_sys = p_filter->p_owner;
1473     SpuClearChannel( p_sys->p_spu, p_sys->i_channel, true );
1474     free( p_filter->p_owner );
1475 }