]> git.sesse.net Git - vlc/blob - src/video_output/vout_subpictures.c
Clean up.
[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 the use the scale 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             p_subpic->i_original_picture_height = 0;
962             p_subpic->i_original_picture_width = 0;
963         }
964
965         for( k = 0; k < SCALE_SIZE ; k++ )
966             pi_subpic_x[ k ] = p_subpic->i_x;
967
968         if( p_subpic->pf_update_regions )
969         {
970             /* FIXME that part look like crap too if there is more than 1 region */
971
972             if( p_subpic->p_region )
973                 spu_DestroyRegion( p_spu, p_subpic->p_region );
974
975             /* TODO do not reverse the scaling that was done before calling
976              * spu_RenderSubpictures, just pass it along (or do it inside
977              * spu_RenderSubpictures) */
978             video_format_t fmt_org = *p_fmt;
979             fmt_org.i_width =
980             fmt_org.i_visible_width = i_source_video_width;
981             fmt_org.i_height =
982             fmt_org.i_visible_height = i_source_video_height;
983
984             p_subpic->p_region = p_subpic->pf_update_regions( &fmt_org, p_spu, p_subpic, mdate() );
985         }
986
987         /* */
988         p_region = p_subpic->p_region;
989         if( !p_region )
990             continue;
991
992         /* Create the blending module */
993         if( !p_spu->p_blend )
994             SpuRenderCreateBlend( p_spu, p_fmt->i_chroma, p_fmt->i_aspect );
995
996         /* Load the text rendering module; it is possible there is a
997          * text region somewhere in the subpicture other than the first
998          * element in the region list, so just load it anyway as we'll
999          * probably want it sooner or later. */
1000         if( !p_spu->p_text )
1001             SpuRenderCreateAndLoadText( p_spu, p_fmt->i_width, p_fmt->i_height );
1002
1003         if( p_spu->p_text )
1004         {
1005             subpicture_region_t *p_text_region = p_subpic->p_region;
1006
1007             /* Only overwrite the size fields if the region is still in
1008              * pre-rendered TEXT format. We have to traverse the subregion
1009              * list because if more than one subregion is present, the text
1010              * region isn't guarentteed to be the first in the list, and
1011              * only text regions use this flag. All of this effort assists
1012              * with the rescaling of text that has been rendered at native
1013              * resolution, rather than video resolution.
1014              */
1015             while( p_text_region &&
1016                    p_text_region->fmt.i_chroma != VLC_FOURCC('T','E','X','T') )
1017             {
1018                 p_text_region = p_text_region->p_next;
1019             }
1020
1021             if( p_text_region &&
1022                 ( ( p_text_region->i_align & SUBPICTURE_RENDERED ) == 0 ) )
1023             {
1024                 if( (p_subpic->i_original_picture_height > 0) &&
1025                     (p_subpic->i_original_picture_width  > 0) )
1026                 {
1027                     p_spu->p_text->fmt_out.video.i_width =
1028                         p_spu->p_text->fmt_out.video.i_visible_width =
1029                         p_subpic->i_original_picture_width;
1030                     p_spu->p_text->fmt_out.video.i_height =
1031                         p_spu->p_text->fmt_out.video.i_visible_height =
1032                         p_subpic->i_original_picture_height;
1033                 }
1034                 else
1035                 {
1036                     p_spu->p_text->fmt_out.video.i_width =
1037                         p_spu->p_text->fmt_out.video.i_visible_width =
1038                         p_fmt->i_width;
1039                     p_spu->p_text->fmt_out.video.i_height =
1040                         p_spu->p_text->fmt_out.video.i_visible_height =
1041                         p_fmt->i_height;
1042                 }
1043             }
1044
1045             /* */
1046             pi_scale_width[SCALE_TEXT] = p_fmt->i_width * 1000 /
1047                                           p_spu->p_text->fmt_out.video.i_width;
1048             pi_scale_height[SCALE_TEXT]= p_fmt->i_height * 1000 /
1049                                           p_spu->p_text->fmt_out.video.i_height;
1050         }
1051
1052         pi_scale_width[ SCALE_DEFAULT ]  = i_scale_width_orig;
1053         pi_scale_height[ SCALE_DEFAULT ] = i_scale_height_orig;
1054
1055         /* If we have an explicit size plane to render to, then turn off
1056          * the fontsize rescaling.
1057          */
1058         if( (p_subpic->i_original_picture_height > 0) &&
1059             (p_subpic->i_original_picture_width  > 0) )
1060         {
1061 #if 1
1062             /* FIXME That seems so wrong */
1063             i_scale_width_orig  = 1000;
1064             i_scale_height_orig = 1000;
1065 #else
1066             /* It is probably that :*/
1067             pi_scale_width[ SCALE_DEFAULT ]  = i_scale_width_orig * i_source_video_width / p_subpic->i_original_picture_width;
1068             pi_scale_height[ SCALE_DEFAULT ] = i_scale_height_orig * i_source_video_height / p_subpic->i_original_picture_height;
1069 #endif
1070         }
1071
1072         for( k = 0; k < SCALE_SIZE ; k++ )
1073         {
1074             /* Case of both width and height being specified has been dealt
1075              * with above by instead rendering to an output pane of the
1076              * explicit dimensions specified - we don't need to scale it.
1077              */
1078             if( (p_subpic->i_original_picture_height > 0) &&
1079                 (p_subpic->i_original_picture_width <= 0) )
1080             {
1081                 pi_scale_height[ k ] = pi_scale_height[ k ] * i_source_video_height /
1082                                  p_subpic->i_original_picture_height;
1083                 pi_scale_width[ k ]  = pi_scale_width[ k ]  * i_source_video_height /
1084                                  p_subpic->i_original_picture_height;
1085             }
1086         }
1087
1088         /* Set default subpicture aspect ratio */
1089         if( p_region->fmt.i_aspect && ( !p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den ) )
1090         {
1091             p_region->fmt.i_sar_den = p_region->fmt.i_aspect;
1092             p_region->fmt.i_sar_num = VOUT_ASPECT_FACTOR;
1093         }
1094         if( !p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den )
1095         {
1096             p_region->fmt.i_sar_den = p_fmt->i_sar_den;
1097             p_region->fmt.i_sar_num = p_fmt->i_sar_num;
1098         }
1099
1100         /* Take care of the aspect ratio */
1101         if( ( p_region->fmt.i_sar_num * p_fmt->i_sar_den ) !=
1102             ( p_region->fmt.i_sar_den * p_fmt->i_sar_num ) )
1103         {
1104             for( k = 0; k < SCALE_SIZE ; k++ )
1105             {
1106                 pi_scale_width[ k ] = pi_scale_width[ k ] *
1107                     (int64_t)p_region->fmt.i_sar_num * p_fmt->i_sar_den /
1108                     p_region->fmt.i_sar_den / p_fmt->i_sar_num;
1109                 pi_subpic_x[ k ] = p_subpic->i_x * pi_scale_width[ k ] / 1000;
1110             }
1111         }
1112
1113         /* Load the scaling module when needed */
1114         if( !p_spu->p_scale )
1115         {
1116             bool b_scale_used = false;
1117
1118             for( k = 0; k < SCALE_SIZE ; k++ )
1119             {
1120                 const int i_scale_w = pi_scale_width[k];
1121                 const int i_scale_h = pi_scale_height[k];
1122                 if( ( i_scale_w > 0 && i_scale_w != 1000 ) || ( i_scale_h > 0 && i_scale_h != 1000 ) )
1123                     b_scale_used = true;
1124             }
1125
1126             if( b_scale_used )
1127                 SpuRenderCreateAndLoadScale( p_spu );
1128         }
1129
1130         for( ; p_region != NULL; p_region = p_region->p_next )
1131             SpuRenderRegion( p_spu, p_pic_dst, p_pic_src,
1132                              p_subpic, p_region, i_scale_width_orig, i_scale_height_orig,
1133                              pi_subpic_x, pi_scale_width, pi_scale_height,
1134                              p_fmt );
1135     }
1136
1137     vlc_mutex_unlock( &p_spu->subpicture_lock );
1138 }
1139
1140 /*****************************************************************************
1141  * spu_SortSubpictures: find the subpictures to display
1142  *****************************************************************************
1143  * This function parses all subpictures and decides which ones need to be
1144  * displayed. This operation does not need lock, since only READY_SUBPICTURE
1145  * are handled. If no picture has been selected, display_date will depend on
1146  * the subpicture.
1147  * We also check for ephemer DVD subpictures (subpictures that have
1148  * to be removed if a newer one is available), which makes it a lot
1149  * more difficult to guess if a subpicture has to be rendered or not.
1150  *****************************************************************************/
1151 subpicture_t *spu_SortSubpictures( spu_t *p_spu, mtime_t display_date,
1152                                    bool b_paused )
1153 {
1154     int i_index, i_channel;
1155     subpicture_t *p_subpic = NULL;
1156     subpicture_t *p_ephemer;
1157     mtime_t      ephemer_date;
1158
1159     /* Run subpicture filters */
1160     filter_chain_SubFilter( p_spu->p_chain, display_date );
1161
1162     /* We get an easily parsable chained list of subpictures which
1163      * ends with NULL since p_subpic was initialized to NULL. */
1164     for( i_channel = 0; i_channel < p_spu->i_channel; i_channel++ )
1165     {
1166         p_ephemer = 0;
1167         ephemer_date = 0;
1168
1169         for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
1170         {
1171             if( p_spu->p_subpicture[i_index].i_channel != i_channel ||
1172                 p_spu->p_subpicture[i_index].i_status != READY_SUBPICTURE )
1173             {
1174                 continue;
1175             }
1176             if( display_date &&
1177                 display_date < p_spu->p_subpicture[i_index].i_start )
1178             {
1179                 /* Too early, come back next monday */
1180                 continue;
1181             }
1182
1183             if( p_spu->p_subpicture[i_index].i_start > ephemer_date )
1184                 ephemer_date = p_spu->p_subpicture[i_index].i_start;
1185
1186             if( display_date > p_spu->p_subpicture[i_index].i_stop &&
1187                 ( !p_spu->p_subpicture[i_index].b_ephemer ||
1188                   p_spu->p_subpicture[i_index].i_stop >
1189                   p_spu->p_subpicture[i_index].i_start ) &&
1190                 !( p_spu->p_subpicture[i_index].b_pausable &&
1191                    b_paused ) )
1192             {
1193                 /* Too late, destroy the subpic */
1194                 spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] );
1195                 continue;
1196             }
1197
1198             /* If this is an ephemer subpic, add it to our list */
1199             if( p_spu->p_subpicture[i_index].b_ephemer )
1200             {
1201                 p_spu->p_subpicture[i_index].p_next = p_ephemer;
1202                 p_ephemer = &p_spu->p_subpicture[i_index];
1203
1204                 continue;
1205             }
1206
1207             p_spu->p_subpicture[i_index].p_next = p_subpic;
1208             p_subpic = &p_spu->p_subpicture[i_index];
1209         }
1210
1211         /* If we found ephemer subpictures, check if they have to be
1212          * displayed or destroyed */
1213         while( p_ephemer != NULL )
1214         {
1215             subpicture_t *p_tmp = p_ephemer;
1216             p_ephemer = p_ephemer->p_next;
1217
1218             if( p_tmp->i_start < ephemer_date )
1219             {
1220                 /* Ephemer subpicture has lived too long */
1221                 spu_DestroySubpicture( p_spu, p_tmp );
1222             }
1223             else
1224             {
1225                 /* Ephemer subpicture can still live a bit */
1226                 p_tmp->p_next = p_subpic;
1227                 p_subpic = p_tmp;
1228             }
1229         }
1230     }
1231
1232     return p_subpic;
1233 }
1234
1235 /*****************************************************************************
1236  * SpuClearChannel: clear an spu channel
1237  *****************************************************************************
1238  * This function destroys the subpictures which belong to the spu channel
1239  * corresponding to i_channel_id.
1240  *****************************************************************************/
1241 static void SpuClearChannel( spu_t *p_spu, int i_channel, bool b_locked )
1242 {
1243     int          i_subpic;                               /* subpicture index */
1244     subpicture_t *p_subpic = NULL;                  /* first free subpicture */
1245
1246     if( !b_locked )
1247         vlc_mutex_lock( &p_spu->subpicture_lock );
1248
1249     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
1250     {
1251         p_subpic = &p_spu->p_subpicture[i_subpic];
1252         if( p_subpic->i_status == FREE_SUBPICTURE
1253             || ( p_subpic->i_status != RESERVED_SUBPICTURE
1254                  && p_subpic->i_status != READY_SUBPICTURE ) )
1255         {
1256             continue;
1257         }
1258
1259         if( p_subpic->i_channel == i_channel )
1260         {
1261             while( p_subpic->p_region )
1262             {
1263                 subpicture_region_t *p_region = p_subpic->p_region;
1264                 p_subpic->p_region = p_region->p_next;
1265                 spu_DestroyRegion( p_spu, p_region );
1266             }
1267
1268             if( p_subpic->pf_destroy ) p_subpic->pf_destroy( p_subpic );
1269             p_subpic->i_status = FREE_SUBPICTURE;
1270         }
1271     }
1272
1273     if( !b_locked )
1274         vlc_mutex_unlock( &p_spu->subpicture_lock );
1275 }
1276
1277 /*****************************************************************************
1278  * spu_ControlDefault: default methods for the subpicture unit control.
1279  *****************************************************************************/
1280 static int spu_vaControlDefault( spu_t *p_spu, int i_query, va_list args )
1281 {
1282     int *pi, i;
1283
1284     switch( i_query )
1285     {
1286     case SPU_CHANNEL_REGISTER:
1287         pi = (int *)va_arg( args, int * );
1288         if( pi ) *pi = p_spu->i_channel++;
1289         break;
1290
1291     case SPU_CHANNEL_CLEAR:
1292         i = (int)va_arg( args, int );
1293         SpuClearChannel( p_spu, i, false );
1294         break;
1295
1296     default:
1297         msg_Dbg( p_spu, "control query not supported" );
1298         return VLC_EGENERIC;
1299     }
1300
1301     return VLC_SUCCESS;
1302 }
1303
1304 /*****************************************************************************
1305  * Object variables callbacks
1306  *****************************************************************************/
1307
1308 /*****************************************************************************
1309  * UpdateSPU: update subpicture settings
1310  *****************************************************************************
1311  * This function is called from CropCallback and at initialization time, to
1312  * retrieve crop information from the input.
1313  *****************************************************************************/
1314 static void UpdateSPU( spu_t *p_spu, vlc_object_t *p_object )
1315 {
1316     vlc_value_t val;
1317
1318     p_spu->b_force_palette = false;
1319     p_spu->b_force_crop = false;
1320
1321     if( var_Get( p_object, "highlight", &val ) || !val.b_bool ) return;
1322
1323     p_spu->b_force_crop = true;
1324     var_Get( p_object, "x-start", &val );
1325     p_spu->i_crop_x = val.i_int;
1326     var_Get( p_object, "y-start", &val );
1327     p_spu->i_crop_y = val.i_int;
1328     var_Get( p_object, "x-end", &val );
1329     p_spu->i_crop_width = val.i_int - p_spu->i_crop_x;
1330     var_Get( p_object, "y-end", &val );
1331     p_spu->i_crop_height = val.i_int - p_spu->i_crop_y;
1332
1333     if( var_Get( p_object, "menu-palette", &val ) == VLC_SUCCESS )
1334     {
1335         memcpy( p_spu->palette, val.p_address, 16 );
1336         p_spu->b_force_palette = true;
1337     }
1338
1339     msg_Dbg( p_object, "crop: %i,%i,%i,%i, palette forced: %i",
1340              p_spu->i_crop_x, p_spu->i_crop_y,
1341              p_spu->i_crop_width, p_spu->i_crop_height,
1342              p_spu->b_force_palette );
1343 }
1344
1345 /*****************************************************************************
1346  * CropCallback: called when the highlight properties are changed
1347  *****************************************************************************
1348  * This callback is called from the input thread when we need cropping
1349  *****************************************************************************/
1350 static int CropCallback( vlc_object_t *p_object, char const *psz_var,
1351                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1352 {
1353     (void)psz_var; (void)oldval; (void)newval;
1354     UpdateSPU( (spu_t *)p_data, p_object );
1355     return VLC_SUCCESS;
1356 }
1357
1358 /*****************************************************************************
1359  * Buffers allocation callbacks for the filters
1360  *****************************************************************************/
1361 static subpicture_t *sub_new_buffer( filter_t *p_filter )
1362 {
1363     filter_owner_sys_t *p_sys = p_filter->p_owner;
1364     subpicture_t *p_subpicture = spu_CreateSubpicture( p_sys->p_spu );
1365     if( p_subpicture ) p_subpicture->i_channel = p_sys->i_channel;
1366     return p_subpicture;
1367 }
1368
1369 static void sub_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1370 {
1371     filter_owner_sys_t *p_sys = p_filter->p_owner;
1372     spu_DestroySubpicture( p_sys->p_spu, p_subpic );
1373 }
1374
1375 static subpicture_t *spu_new_buffer( filter_t *p_filter )
1376 {
1377     (void)p_filter;
1378     subpicture_t *p_subpic = (subpicture_t *)malloc(sizeof(subpicture_t));
1379     if( !p_subpic ) return NULL;
1380     memset( p_subpic, 0, sizeof(subpicture_t) );
1381     p_subpic->b_absolute = true;
1382
1383     p_subpic->pf_create_region = __spu_CreateRegion;
1384     p_subpic->pf_make_region = __spu_MakeRegion;
1385     p_subpic->pf_destroy_region = __spu_DestroyRegion;
1386
1387     return p_subpic;
1388 }
1389
1390 static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1391 {
1392     while( p_subpic->p_region )
1393     {
1394         subpicture_region_t *p_region = p_subpic->p_region;
1395         p_subpic->p_region = p_region->p_next;
1396         p_subpic->pf_destroy_region( VLC_OBJECT(p_filter), p_region );
1397     }
1398
1399     free( p_subpic );
1400 }
1401
1402 static picture_t *spu_new_video_buffer( filter_t *p_filter )
1403 {
1404     picture_t *p_picture = malloc( sizeof(picture_t) );
1405     if( !p_picture ) return NULL;
1406     if( vout_AllocatePicture( p_filter, p_picture,
1407                               p_filter->fmt_out.video.i_chroma,
1408                               p_filter->fmt_out.video.i_width,
1409                               p_filter->fmt_out.video.i_height,
1410                               p_filter->fmt_out.video.i_aspect )
1411         != VLC_SUCCESS )
1412     {
1413         free( p_picture );
1414         return NULL;
1415     }
1416
1417     p_picture->pf_release = RegionPictureRelease;
1418
1419     return p_picture;
1420 }
1421
1422 static void spu_del_video_buffer( filter_t *p_filter, picture_t *p_pic )
1423 {
1424     (void)p_filter;
1425     if( p_pic )
1426     {
1427         free( p_pic->p_data_orig );
1428         free( p_pic );
1429     }
1430 }
1431
1432 static int SubFilterCallback( vlc_object_t *p_object, char const *psz_var,
1433                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1434 {
1435     VLC_UNUSED(p_object); VLC_UNUSED(oldval);
1436     VLC_UNUSED(newval); VLC_UNUSED(psz_var);
1437
1438     spu_t *p_spu = (spu_t *)p_data;
1439     vlc_mutex_lock( &p_spu->subpicture_lock );
1440     filter_chain_Reset( p_spu->p_chain, NULL, NULL );
1441     spu_ParseChain( p_spu );
1442     vlc_mutex_unlock( &p_spu->subpicture_lock );
1443     return VLC_SUCCESS;
1444 }
1445
1446 static int sub_filter_allocation_init( filter_t *p_filter, void *p_data )
1447 {
1448     spu_t *p_spu = (spu_t *)p_data;
1449
1450     p_filter->pf_sub_buffer_new = sub_new_buffer;
1451     p_filter->pf_sub_buffer_del = sub_del_buffer;
1452
1453     filter_owner_sys_t *p_sys = malloc( sizeof(filter_owner_sys_t) );
1454     if( !p_sys ) return VLC_EGENERIC;
1455
1456     p_filter->p_owner = p_sys;
1457     spu_Control( p_spu, SPU_CHANNEL_REGISTER, &p_sys->i_channel );
1458     p_sys->p_spu = p_spu;
1459
1460     return VLC_SUCCESS;
1461 }
1462
1463 static void sub_filter_allocation_clear( filter_t *p_filter )
1464 {
1465     filter_owner_sys_t *p_sys = p_filter->p_owner;
1466     SpuClearChannel( p_sys->p_spu, p_sys->i_channel, true );
1467     free( p_filter->p_owner );
1468 }