]> git.sesse.net Git - vlc/blob - src/video_output/vout_subpictures.c
More clean up on subtitle.
[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, const video_format_t *p_vfmt )
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_vfmt->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_vfmt;
508
509     /* */
510     if( !p_blend->p_module )
511         p_blend->p_module = module_Need( p_blend, "video blending", 0, 0 );
512 }
513 static void SpuRenderCreateAndLoadText( spu_t *p_spu, int i_width, int i_height )
514 {
515     filter_t *p_text;
516
517     assert( !p_spu->p_text );
518
519     p_spu->p_text =
520     p_text        = vlc_custom_create( p_spu, sizeof(filter_t),
521                                        VLC_OBJECT_GENERIC, "spu text" );
522     if( !p_text )
523         return;
524
525     es_format_Init( &p_text->fmt_in, VIDEO_ES, 0 );
526
527     es_format_Init( &p_text->fmt_out, VIDEO_ES, 0 );
528     p_text->fmt_out.video.i_width =
529     p_text->fmt_out.video.i_visible_width = i_width;
530     p_text->fmt_out.video.i_height =
531     p_text->fmt_out.video.i_visible_height = i_height;
532
533     p_text->pf_sub_buffer_new = spu_new_buffer;
534     p_text->pf_sub_buffer_del = spu_del_buffer;
535
536     vlc_object_attach( p_text, p_spu );
537
538     /* FIXME TOCHECK shouldn't module_Need( , , psz_modulename, false ) do the
539      * same than these 2 calls ? */
540     char *psz_modulename = var_CreateGetString( p_spu, "text-renderer" );
541     if( psz_modulename && *psz_modulename )
542     {
543         p_text->p_module = module_Need( p_text, "text renderer",
544                                         psz_modulename, true );
545     }
546     free( psz_modulename );
547
548     if( !p_text->p_module )
549         p_text->p_module = module_Need( p_text, "text renderer", NULL, false );
550 }
551
552 static void SpuRenderCreateAndLoadScale( spu_t *p_spu )
553 {
554     /* FIXME: We'll also be using it for YUVA and RGBA blending ... */
555     const vlc_fourcc_t i_dummy_chroma = VLC_FOURCC('Y','U','V','P');
556
557     filter_t *p_scale;
558
559     assert( !p_spu->p_scale );
560
561     p_spu->p_scale =
562     p_scale        = vlc_custom_create( p_spu, sizeof(filter_t),
563                                         VLC_OBJECT_GENERIC, "scale" );
564     if( !p_scale )
565         return;
566
567     es_format_Init( &p_scale->fmt_in, VIDEO_ES, 0 );
568     p_scale->fmt_in.video.i_chroma = i_dummy_chroma;
569     p_scale->fmt_in.video.i_width =
570     p_scale->fmt_in.video.i_height = 32;
571
572     es_format_Init( &p_scale->fmt_out, VIDEO_ES, 0 );
573     p_scale->fmt_out.video.i_chroma = i_dummy_chroma;
574     p_scale->fmt_out.video.i_width =
575     p_scale->fmt_out.video.i_height = 16;
576
577     p_scale->pf_vout_buffer_new = spu_new_video_buffer;
578     p_scale->pf_vout_buffer_del = spu_del_video_buffer;
579
580     vlc_object_attach( p_scale, p_spu );
581     p_scale->p_module = module_Need( p_spu->p_scale, "video filter2", 0, 0 );
582 }
583
584 static void SpuRenderRegion( spu_t *p_spu,
585                              picture_t *p_pic_dst, picture_t *p_pic_src,
586                              subpicture_t *p_subpic, subpicture_region_t *p_region,
587                              const int i_scale_width_orig, const int i_scale_height_orig,
588                              const int pi_subpic_x[SCALE_SIZE],
589                              const int pi_scale_width[SCALE_SIZE],
590                              const int pi_scale_height[SCALE_SIZE],
591                              const video_format_t *p_fmt )
592 {
593     video_format_t orig_fmt = p_region->fmt;
594     bool b_rerender_text = false;
595     int i_fade_alpha = 255;
596     int i_x_offset;
597     int i_y_offset;
598     int i_scale_idx;
599     int i_inv_scale_x;
600     int i_inv_scale_y;
601
602     if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') )
603     {
604         if( p_spu->p_text && p_spu->p_text->p_module )
605         {
606             vlc_value_t  val;
607
608             /* Setup 3 variables which can be used to render
609              * time-dependent text (and effects). The first indicates
610              * the total amount of time the text will be on screen,
611              * the second the amount of time it has already been on
612              * screen (can be a negative value as text is layed out
613              * before it is rendered) and the third is a feedback
614              * variable from the renderer - if the renderer sets it
615              * then this particular text is time-dependent, eg. the
616              * visual progress bar inside the text in karaoke and the
617              * text needs to be rendered multiple times in order for
618              * the effect to work - we therefore need to return the
619              * region to its original state at the end of the loop,
620              * instead of leaving it in YUVA or YUVP.
621              * Any renderer which is unaware of how to render
622              * time-dependent text can happily ignore the variables
623              * and render the text the same as usual - it should at
624              * least show up on screen, but the effect won't change
625              * the text over time.
626              */
627
628             var_Create( p_spu->p_text, "spu-duration", VLC_VAR_TIME );
629             var_SetTime( p_spu->p_text, "spu-duration", p_subpic->i_stop - p_subpic->i_start );
630
631             var_Create( p_spu->p_text, "spu-elapsed", VLC_VAR_TIME );
632             var_SetTime( p_spu->p_text, "spu-elapsed", mdate() - p_subpic->i_start );
633
634             var_Create( p_spu->p_text, "text-rerender", VLC_VAR_BOOL );
635             var_SetBool( p_spu->p_text, "text-rerender", false );
636
637             var_Create( p_spu->p_text, "scale", VLC_VAR_INTEGER );
638             var_SetInteger( p_spu->p_text, "scale",
639                             __MIN(i_scale_width_orig, i_scale_height_orig) );
640
641             if( p_spu->p_text->pf_render_html && p_region->psz_html )
642             {
643                 p_spu->p_text->pf_render_html( p_spu->p_text,
644                                                p_region, p_region );
645             }
646             else if( p_spu->p_text->pf_render_text )
647             {
648                 p_spu->p_text->pf_render_text( p_spu->p_text,
649                                                p_region, p_region );
650             }
651             b_rerender_text = var_GetBool( p_spu->p_text, "text-rerender" );
652
653             var_Destroy( p_spu->p_text, "spu-duration" );
654             var_Destroy( p_spu->p_text, "spu-elapsed" );
655             var_Destroy( p_spu->p_text, "text-rerender" );
656             var_Destroy( p_spu->p_text, "scale" );
657         }
658         p_region->i_align |= SUBPICTURE_RENDERED;
659     }
660
661     /* From now on, we can only process non text data */
662     if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') )
663         goto exit;
664
665     if( p_region->i_align & SUBPICTURE_RENDERED )
666     {
667         /* We are using a region which come from rendered text */
668         i_scale_idx   = SCALE_TEXT;
669         i_inv_scale_x = i_scale_width_orig;
670         i_inv_scale_y = i_scale_height_orig;
671     }
672     else
673     {
674         i_scale_idx   = SCALE_DEFAULT;
675         i_inv_scale_x = 1000;
676         i_inv_scale_y = 1000;
677     }
678
679     i_x_offset = (p_region->i_x + pi_subpic_x[ i_scale_idx ]) * i_inv_scale_x / 1000;
680     i_y_offset = (p_region->i_y + p_subpic->i_y) * i_inv_scale_y / 1000;
681
682     /* Force palette if requested */
683     if( p_spu->b_force_palette &&
684         ( VLC_FOURCC('Y','U','V','P') == p_region->fmt.i_chroma ) )
685     {
686         /* It look so wrong I won't comment
687          * p_palette->palette is [256][4] with a int i_entries
688          * p_spu->palette is [4][4]
689          * */
690         memcpy( p_region->fmt.p_palette->palette, p_spu->palette, 16 );
691     }
692
693     /* Scale SPU if necessary */
694     if( p_region->p_cache )
695     {
696         if( pi_scale_width[ i_scale_idx ] * p_region->fmt.i_width / 1000 !=
697             p_region->p_cache->fmt.i_width ||
698             pi_scale_height[ i_scale_idx ] * p_region->fmt.i_height / 1000 !=
699             p_region->p_cache->fmt.i_height )
700         {
701             p_subpic->pf_destroy_region( VLC_OBJECT(p_spu),
702                                          p_region->p_cache );
703             p_region->p_cache = 0;
704         }
705     }
706
707     if( ( ( pi_scale_width[ i_scale_idx ] != 1000 ) ||
708           ( pi_scale_height[ i_scale_idx ] != 1000 ) ) &&
709         ( ( pi_scale_width[ i_scale_idx ] > 0 ) ||
710           ( pi_scale_height[ i_scale_idx ] > 0 ) ) &&
711         p_spu->p_scale && !p_region->p_cache )
712     {
713         picture_t *p_pic;
714
715         p_spu->p_scale->fmt_in.video = p_region->fmt;
716         p_spu->p_scale->fmt_out.video = p_region->fmt;
717
718         p_region->p_cache =
719             p_subpic->pf_create_region( VLC_OBJECT(p_spu),
720                 &p_spu->p_scale->fmt_out.video );
721         if( p_spu->p_scale->fmt_out.video.p_palette )
722             *p_spu->p_scale->fmt_out.video.p_palette =
723                 *p_region->fmt.p_palette;
724         p_region->p_cache->p_next = p_region->p_next;
725
726         vout_CopyPicture( p_spu, &p_region->p_cache->picture,
727                           &p_region->picture );
728
729         p_spu->p_scale->fmt_out.video.i_width =
730             p_region->fmt.i_width * pi_scale_width[ i_scale_idx ] / 1000;
731         p_spu->p_scale->fmt_out.video.i_visible_width =
732             p_region->fmt.i_visible_width * pi_scale_width[ i_scale_idx ] / 1000;
733         p_spu->p_scale->fmt_out.video.i_height =
734             p_region->fmt.i_height * pi_scale_height[ i_scale_idx ] / 1000;
735         p_spu->p_scale->fmt_out.video.i_visible_height =
736             p_region->fmt.i_visible_height * pi_scale_height[ i_scale_idx ] / 1000;
737         p_region->p_cache->fmt = p_spu->p_scale->fmt_out.video;
738         p_region->p_cache->i_x = p_region->i_x * pi_scale_width[ i_scale_idx ] / 1000;
739         p_region->p_cache->i_y = p_region->i_y * pi_scale_height[ i_scale_idx ] / 1000;
740         p_region->p_cache->i_align = p_region->i_align;
741         p_region->p_cache->i_alpha = p_region->i_alpha;
742
743         p_pic = p_spu->p_scale->pf_video_filter(
744                          p_spu->p_scale, &p_region->p_cache->picture );
745         if( p_pic )
746         {
747             picture_t p_pic_tmp = p_region->p_cache->picture;
748             p_region->p_cache->picture = *p_pic;
749             *p_pic = p_pic_tmp;
750             free( p_pic );
751         }
752     }
753
754     if( ( ( pi_scale_width[ i_scale_idx ] != 1000 ) ||
755           ( pi_scale_height[ i_scale_idx ] != 1000 ) ) &&
756         ( ( pi_scale_width[ i_scale_idx ] > 0 ) ||
757           ( pi_scale_height[ i_scale_idx ] > 0 ) ) &&
758         p_spu->p_scale && p_region->p_cache )
759     {
760         p_region = p_region->p_cache;
761     }
762
763     if( p_region->i_align & SUBPICTURE_ALIGN_BOTTOM )
764     {
765         i_y_offset = p_fmt->i_height - p_region->fmt.i_height -
766             (p_subpic->i_y + p_region->i_y) * i_inv_scale_y / 1000;
767     }
768     else if ( !(p_region->i_align & SUBPICTURE_ALIGN_TOP) )
769     {
770         i_y_offset = p_fmt->i_height / 2 - p_region->fmt.i_height / 2;
771     }
772
773     if( p_region->i_align & SUBPICTURE_ALIGN_RIGHT )
774     {
775         i_x_offset = p_fmt->i_width - p_region->fmt.i_width -
776             (pi_subpic_x[ i_scale_idx ] + p_region->i_x)
777             * i_inv_scale_x / 1000;
778     }
779     else if ( !(p_region->i_align & SUBPICTURE_ALIGN_LEFT) )
780     {
781         i_x_offset = p_fmt->i_width / 2 - p_region->fmt.i_width / 2;
782     }
783
784     if( p_subpic->b_absolute )
785     {
786         i_x_offset = (p_region->i_x +
787             pi_subpic_x[ i_scale_idx ] *
788                              pi_scale_width[ i_scale_idx ] / 1000)
789             * i_inv_scale_x / 1000;
790         i_y_offset = (p_region->i_y +
791             p_subpic->i_y * pi_scale_height[ i_scale_idx ] / 1000)
792             * i_inv_scale_y / 1000;
793
794     }
795
796     i_x_offset = __MAX( i_x_offset, 0 );
797     i_y_offset = __MAX( i_y_offset, 0 );
798
799     if( ( p_spu->i_margin != 0 ) &&
800         ( p_spu->b_force_crop == false ) )
801     {
802         int i_diff = 0;
803         int i_low = (i_y_offset - p_spu->i_margin) * i_inv_scale_y / 1000;
804         int i_high = i_low + p_region->fmt.i_height;
805
806         /* crop extra margin to keep within bounds */
807         if( i_low < 0 )
808             i_diff = i_low;
809         if( i_high > (int)p_fmt->i_height )
810             i_diff = i_high - p_fmt->i_height;
811         i_y_offset -= ( p_spu->i_margin * i_inv_scale_y / 1000 + i_diff );
812     }
813
814     if( p_subpic->b_fade )
815     {
816         mtime_t i_fade_start = ( p_subpic->i_stop +
817                                  p_subpic->i_start ) / 2;
818         mtime_t i_now = mdate();
819         if( i_now >= i_fade_start && p_subpic->i_stop > i_fade_start )
820         {
821             i_fade_alpha = 255 * ( p_subpic->i_stop - i_now ) /
822                            ( p_subpic->i_stop - i_fade_start );
823         }
824     }
825
826     /* */
827     SpuRenderUpdateBlend( p_spu, &p_region->fmt );
828
829     /* Force cropping if requested */
830     if( p_spu->b_force_crop )
831     {
832         video_format_t *p_fmt = &p_spu->p_blend->fmt_in.video;
833         int i_crop_x = p_spu->i_crop_x * pi_scale_width[ i_scale_idx ] / 1000
834                             * i_inv_scale_x / 1000;
835         int i_crop_y = p_spu->i_crop_y * pi_scale_height[ i_scale_idx ] / 1000
836                             * i_inv_scale_y / 1000;
837         int i_crop_width = p_spu->i_crop_width * pi_scale_width[ i_scale_idx ] / 1000
838                             * i_inv_scale_x / 1000;
839         int i_crop_height = p_spu->i_crop_height * pi_scale_height[ i_scale_idx ] / 1000
840                             * i_inv_scale_y / 1000;
841
842         /* Find the intersection */
843         if( i_crop_x + i_crop_width <= i_x_offset ||
844             i_x_offset + (int)p_fmt->i_visible_width < i_crop_x ||
845             i_crop_y + i_crop_height <= i_y_offset ||
846             i_y_offset + (int)p_fmt->i_visible_height < i_crop_y )
847         {
848             /* No intersection */
849             p_fmt->i_visible_width = p_fmt->i_visible_height = 0;
850         }
851         else
852         {
853             int i_x, i_y, i_x_end, i_y_end;
854             i_x = __MAX( i_crop_x, i_x_offset );
855             i_y = __MAX( i_crop_y, i_y_offset );
856             i_x_end = __MIN( i_crop_x + i_crop_width,
857                            i_x_offset + (int)p_fmt->i_visible_width );
858             i_y_end = __MIN( i_crop_y + i_crop_height,
859                            i_y_offset + (int)p_fmt->i_visible_height );
860
861             p_fmt->i_x_offset = i_x - i_x_offset;
862             p_fmt->i_y_offset = i_y - i_y_offset;
863             p_fmt->i_visible_width = i_x_end - i_x;
864             p_fmt->i_visible_height = i_y_end - i_y;
865
866             i_x_offset = i_x;
867             i_y_offset = i_y;
868         }
869     }
870
871     i_x_offset = __MAX( i_x_offset, 0 );
872     i_y_offset = __MAX( i_y_offset, 0 );
873
874     /* Update the output picture size */
875     p_spu->p_blend->fmt_out.video.i_width =
876         p_spu->p_blend->fmt_out.video.i_visible_width =
877             p_fmt->i_width;
878     p_spu->p_blend->fmt_out.video.i_height =
879         p_spu->p_blend->fmt_out.video.i_visible_height =
880             p_fmt->i_height;
881
882     if( p_spu->p_blend->p_module )
883     {
884         p_spu->p_blend->pf_video_blend( p_spu->p_blend, p_pic_dst,
885             p_pic_src, &p_region->picture, i_x_offset, i_y_offset,
886             i_fade_alpha * p_subpic->i_alpha * p_region->i_alpha / 65025 );
887     }
888     else
889     {
890         msg_Err( p_spu, "blending %4.4s to %4.4s failed",
891                  (char *)&p_spu->p_blend->fmt_out.video.i_chroma,
892                  (char *)&p_spu->p_blend->fmt_out.video.i_chroma );
893     }
894
895 exit:
896     if( b_rerender_text )
897     {
898         /* Some forms of subtitles need to be re-rendered more than
899          * once, eg. karaoke. We therefore restore the region to its
900          * pre-rendered state, so the next time through everything is
901          * calculated again.
902          */
903         p_region->picture.pf_release( &p_region->picture );
904         memset( &p_region->picture, 0, sizeof( picture_t ) );
905         p_region->fmt = orig_fmt;
906         p_region->i_align &= ~SUBPICTURE_RENDERED;
907     }
908 }
909
910 void spu_RenderSubpictures( spu_t *p_spu, video_format_t *p_fmt,
911                             picture_t *p_pic_dst, picture_t *p_pic_src,
912                             subpicture_t *p_subpic,
913                             int i_scale_width_orig, int i_scale_height_orig )
914 {
915     int i_source_video_width;
916     int i_source_video_height;
917     subpicture_t *p_subpic_v;
918
919     /* Get lock */
920     vlc_mutex_lock( &p_spu->subpicture_lock );
921
922     for( p_subpic_v = p_subpic;
923             p_subpic_v != NULL && p_subpic_v->i_status != FREE_SUBPICTURE;
924             p_subpic_v = p_subpic_v->p_next )
925     {
926         if( p_subpic_v->pf_pre_render )
927         {
928             p_subpic_v->pf_pre_render( p_fmt, p_spu, p_subpic_v, mdate() );
929         }
930     }
931
932     if( i_scale_width_orig <= 0 )
933         i_scale_width_orig = 1000;
934     if( i_scale_height_orig <= 0 )
935         i_scale_height_orig = 1000;
936
937     i_source_video_width  = p_fmt->i_width  * 1000 / i_scale_width_orig;
938     i_source_video_height = p_fmt->i_height * 1000 / i_scale_height_orig;
939
940     /* Check i_status again to make sure spudec hasn't destroyed the subpic */
941     for( ; ( p_subpic != NULL ) && ( p_subpic->i_status != FREE_SUBPICTURE ); p_subpic = p_subpic->p_next )
942     {
943         subpicture_region_t *p_region;
944         int pi_scale_width[ SCALE_SIZE ];
945         int pi_scale_height[ SCALE_SIZE ];
946         int pi_subpic_x[ SCALE_SIZE ];
947         int k;
948
949         /* If the source video and subtitles stream agree on the size of
950          * the video then disregard all further references to the subtitle
951          * stream.
952          */
953         if( ( i_source_video_height == p_subpic->i_original_picture_height ) &&
954             ( i_source_video_width  == p_subpic->i_original_picture_width ) )
955         {
956             p_subpic->i_original_picture_height = 0;
957             p_subpic->i_original_picture_width = 0;
958         }
959
960         for( k = 0; k < SCALE_SIZE ; k++ )
961             pi_subpic_x[ k ] = p_subpic->i_x;
962
963         if( p_subpic->pf_update_regions )
964         {
965             /* FIXME that part look like crap too if there is more than 1 region */
966
967             if( p_subpic->p_region )
968                 spu_DestroyRegion( p_spu, p_subpic->p_region );
969
970             /* TODO do not reverse the scaling that was done before calling
971              * spu_RenderSubpictures, just pass it along (or do it inside
972              * spu_RenderSubpictures) */
973             video_format_t fmt_org = *p_fmt;
974             fmt_org.i_width =
975             fmt_org.i_visible_width = i_source_video_width;
976             fmt_org.i_height =
977             fmt_org.i_visible_height = i_source_video_height;
978
979             p_subpic->p_region = p_subpic->pf_update_regions( &fmt_org, p_spu, p_subpic, mdate() );
980         }
981
982         /* */
983         p_region = p_subpic->p_region;
984         if( !p_region )
985             continue;
986
987         /* Create the blending module */
988         if( !p_spu->p_blend )
989             SpuRenderCreateBlend( p_spu, p_fmt->i_chroma, p_fmt->i_aspect );
990
991         /* Load the text rendering module; it is possible there is a
992          * text region somewhere in the subpicture other than the first
993          * element in the region list, so just load it anyway as we'll
994          * probably want it sooner or later. */
995         if( !p_spu->p_text )
996             SpuRenderCreateAndLoadText( p_spu, p_fmt->i_width, p_fmt->i_height );
997
998         if( p_spu->p_text )
999         {
1000             subpicture_region_t *p_text_region = p_subpic->p_region;
1001
1002             /* Only overwrite the size fields if the region is still in
1003              * pre-rendered TEXT format. We have to traverse the subregion
1004              * list because if more than one subregion is present, the text
1005              * region isn't guarentteed to be the first in the list, and
1006              * only text regions use this flag. All of this effort assists
1007              * with the rescaling of text that has been rendered at native
1008              * resolution, rather than video resolution.
1009              */
1010             while( p_text_region &&
1011                    p_text_region->fmt.i_chroma != VLC_FOURCC('T','E','X','T') )
1012             {
1013                 p_text_region = p_text_region->p_next;
1014             }
1015
1016             if( p_text_region &&
1017                 ( ( p_text_region->i_align & SUBPICTURE_RENDERED ) == 0 ) )
1018             {
1019                 if( (p_subpic->i_original_picture_height > 0) &&
1020                     (p_subpic->i_original_picture_width  > 0) )
1021                 {
1022                     p_spu->p_text->fmt_out.video.i_width =
1023                         p_spu->p_text->fmt_out.video.i_visible_width =
1024                         p_subpic->i_original_picture_width;
1025                     p_spu->p_text->fmt_out.video.i_height =
1026                         p_spu->p_text->fmt_out.video.i_visible_height =
1027                         p_subpic->i_original_picture_height;
1028                 }
1029                 else
1030                 {
1031                     p_spu->p_text->fmt_out.video.i_width =
1032                         p_spu->p_text->fmt_out.video.i_visible_width =
1033                         p_fmt->i_width;
1034                     p_spu->p_text->fmt_out.video.i_height =
1035                         p_spu->p_text->fmt_out.video.i_visible_height =
1036                         p_fmt->i_height;
1037                 }
1038             }
1039         }
1040
1041         pi_scale_width[ SCALE_DEFAULT ]  = i_scale_width_orig;
1042         pi_scale_height[ SCALE_DEFAULT ] = i_scale_height_orig;
1043
1044         if( p_spu->p_text )
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         /* If we have an explicit size plane to render to, then turn off
1052          * the fontsize rescaling.
1053          */
1054         if( (p_subpic->i_original_picture_height > 0) &&
1055             (p_subpic->i_original_picture_width  > 0) )
1056         {
1057 #if 1
1058             /* FIXME That seems so wrong */
1059             i_scale_width_orig  = 1000;
1060             i_scale_height_orig = 1000;
1061 #else
1062             /* It is probably that :*/
1063             pi_scale_width[ SCALE_DEFAULT ]  = i_scale_width_orig * i_source_video_width / p_subpic->i_original_picture_width;
1064             pi_scale_height[ SCALE_DEFAULT ] = i_scale_height_orig * i_source_video_height / p_subpic->i_original_picture_height;
1065 #endif
1066         }
1067
1068         for( k = 0; k < SCALE_SIZE ; k++ )
1069         {
1070             /* Case of both width and height being specified has been dealt
1071              * with above by instead rendering to an output pane of the
1072              * explicit dimensions specified - we don't need to scale it.
1073              */
1074             if( (p_subpic->i_original_picture_height > 0) &&
1075                 (p_subpic->i_original_picture_width <= 0) )
1076             {
1077                 pi_scale_height[ k ] = pi_scale_height[ k ] * i_source_video_height /
1078                                  p_subpic->i_original_picture_height;
1079                 pi_scale_width[ k ]  = pi_scale_width[ k ]  * i_source_video_height /
1080                                  p_subpic->i_original_picture_height;
1081             }
1082         }
1083
1084         /* Set default subpicture aspect ratio */
1085         if( p_region->fmt.i_aspect && ( !p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den ) )
1086         {
1087             p_region->fmt.i_sar_den = p_region->fmt.i_aspect;
1088             p_region->fmt.i_sar_num = VOUT_ASPECT_FACTOR;
1089         }
1090         if( !p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den )
1091         {
1092             p_region->fmt.i_sar_den = p_fmt->i_sar_den;
1093             p_region->fmt.i_sar_num = p_fmt->i_sar_num;
1094         }
1095
1096         /* Take care of the aspect ratio */
1097         if( ( p_region->fmt.i_sar_num * p_fmt->i_sar_den ) !=
1098             ( p_region->fmt.i_sar_den * p_fmt->i_sar_num ) )
1099         {
1100             for( k = 0; k < SCALE_SIZE ; k++ )
1101             {
1102                 pi_scale_width[ k ] = pi_scale_width[ k ] *
1103                     (int64_t)p_region->fmt.i_sar_num * p_fmt->i_sar_den /
1104                     p_region->fmt.i_sar_den / p_fmt->i_sar_num;
1105                 pi_subpic_x[ k ] = p_subpic->i_x * pi_scale_width[ k ] / 1000;
1106             }
1107         }
1108
1109         /* Load the scaling module when needed */
1110         if( !p_spu->p_scale )
1111         {
1112             bool b_scale_used = false;
1113
1114             for( k = 0; k < SCALE_SIZE ; k++ )
1115             {
1116                 const int i_scale_w = pi_scale_width[k];
1117                 const int i_scale_h = pi_scale_height[k];
1118                 if( ( i_scale_w > 0 && i_scale_w != 1000 ) || ( i_scale_h > 0 && i_scale_h != 1000 ) )
1119                     b_scale_used = true;
1120             }
1121
1122             if( b_scale_used )
1123                 SpuRenderCreateAndLoadScale( p_spu );
1124         }
1125
1126         for( ; p_region != NULL; p_region = p_region->p_next )
1127             SpuRenderRegion( p_spu, p_pic_dst, p_pic_src,
1128                              p_subpic, p_region, i_scale_width_orig, i_scale_height_orig,
1129                              pi_subpic_x, pi_scale_width, pi_scale_height,
1130                              p_fmt );
1131     }
1132
1133     vlc_mutex_unlock( &p_spu->subpicture_lock );
1134 }
1135
1136 /*****************************************************************************
1137  * spu_SortSubpictures: find the subpictures to display
1138  *****************************************************************************
1139  * This function parses all subpictures and decides which ones need to be
1140  * displayed. This operation does not need lock, since only READY_SUBPICTURE
1141  * are handled. If no picture has been selected, display_date will depend on
1142  * the subpicture.
1143  * We also check for ephemer DVD subpictures (subpictures that have
1144  * to be removed if a newer one is available), which makes it a lot
1145  * more difficult to guess if a subpicture has to be rendered or not.
1146  *****************************************************************************/
1147 subpicture_t *spu_SortSubpictures( spu_t *p_spu, mtime_t display_date,
1148                                    bool b_paused )
1149 {
1150     int i_index, i_channel;
1151     subpicture_t *p_subpic = NULL;
1152     subpicture_t *p_ephemer;
1153     mtime_t      ephemer_date;
1154
1155     /* Run subpicture filters */
1156     filter_chain_SubFilter( p_spu->p_chain, display_date );
1157
1158     /* We get an easily parsable chained list of subpictures which
1159      * ends with NULL since p_subpic was initialized to NULL. */
1160     for( i_channel = 0; i_channel < p_spu->i_channel; i_channel++ )
1161     {
1162         p_ephemer = 0;
1163         ephemer_date = 0;
1164
1165         for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
1166         {
1167             if( p_spu->p_subpicture[i_index].i_channel != i_channel ||
1168                 p_spu->p_subpicture[i_index].i_status != READY_SUBPICTURE )
1169             {
1170                 continue;
1171             }
1172             if( display_date &&
1173                 display_date < p_spu->p_subpicture[i_index].i_start )
1174             {
1175                 /* Too early, come back next monday */
1176                 continue;
1177             }
1178
1179             if( p_spu->p_subpicture[i_index].i_start > ephemer_date )
1180                 ephemer_date = p_spu->p_subpicture[i_index].i_start;
1181
1182             if( display_date > p_spu->p_subpicture[i_index].i_stop &&
1183                 ( !p_spu->p_subpicture[i_index].b_ephemer ||
1184                   p_spu->p_subpicture[i_index].i_stop >
1185                   p_spu->p_subpicture[i_index].i_start ) &&
1186                 !( p_spu->p_subpicture[i_index].b_pausable &&
1187                    b_paused ) )
1188             {
1189                 /* Too late, destroy the subpic */
1190                 spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] );
1191                 continue;
1192             }
1193
1194             /* If this is an ephemer subpic, add it to our list */
1195             if( p_spu->p_subpicture[i_index].b_ephemer )
1196             {
1197                 p_spu->p_subpicture[i_index].p_next = p_ephemer;
1198                 p_ephemer = &p_spu->p_subpicture[i_index];
1199
1200                 continue;
1201             }
1202
1203             p_spu->p_subpicture[i_index].p_next = p_subpic;
1204             p_subpic = &p_spu->p_subpicture[i_index];
1205         }
1206
1207         /* If we found ephemer subpictures, check if they have to be
1208          * displayed or destroyed */
1209         while( p_ephemer != NULL )
1210         {
1211             subpicture_t *p_tmp = p_ephemer;
1212             p_ephemer = p_ephemer->p_next;
1213
1214             if( p_tmp->i_start < ephemer_date )
1215             {
1216                 /* Ephemer subpicture has lived too long */
1217                 spu_DestroySubpicture( p_spu, p_tmp );
1218             }
1219             else
1220             {
1221                 /* Ephemer subpicture can still live a bit */
1222                 p_tmp->p_next = p_subpic;
1223                 p_subpic = p_tmp;
1224             }
1225         }
1226     }
1227
1228     return p_subpic;
1229 }
1230
1231 /*****************************************************************************
1232  * SpuClearChannel: clear an spu channel
1233  *****************************************************************************
1234  * This function destroys the subpictures which belong to the spu channel
1235  * corresponding to i_channel_id.
1236  *****************************************************************************/
1237 static void SpuClearChannel( spu_t *p_spu, int i_channel, bool b_locked )
1238 {
1239     int          i_subpic;                               /* subpicture index */
1240     subpicture_t *p_subpic = NULL;                  /* first free subpicture */
1241
1242     if( !b_locked )
1243         vlc_mutex_lock( &p_spu->subpicture_lock );
1244
1245     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
1246     {
1247         p_subpic = &p_spu->p_subpicture[i_subpic];
1248         if( p_subpic->i_status == FREE_SUBPICTURE
1249             || ( p_subpic->i_status != RESERVED_SUBPICTURE
1250                  && p_subpic->i_status != READY_SUBPICTURE ) )
1251         {
1252             continue;
1253         }
1254
1255         if( p_subpic->i_channel == i_channel )
1256         {
1257             while( p_subpic->p_region )
1258             {
1259                 subpicture_region_t *p_region = p_subpic->p_region;
1260                 p_subpic->p_region = p_region->p_next;
1261                 spu_DestroyRegion( p_spu, p_region );
1262             }
1263
1264             if( p_subpic->pf_destroy ) p_subpic->pf_destroy( p_subpic );
1265             p_subpic->i_status = FREE_SUBPICTURE;
1266         }
1267     }
1268
1269     if( !b_locked )
1270         vlc_mutex_unlock( &p_spu->subpicture_lock );
1271 }
1272
1273 /*****************************************************************************
1274  * spu_ControlDefault: default methods for the subpicture unit control.
1275  *****************************************************************************/
1276 static int spu_vaControlDefault( spu_t *p_spu, int i_query, va_list args )
1277 {
1278     int *pi, i;
1279
1280     switch( i_query )
1281     {
1282     case SPU_CHANNEL_REGISTER:
1283         pi = (int *)va_arg( args, int * );
1284         if( pi ) *pi = p_spu->i_channel++;
1285         break;
1286
1287     case SPU_CHANNEL_CLEAR:
1288         i = (int)va_arg( args, int );
1289         SpuClearChannel( p_spu, i, false );
1290         break;
1291
1292     default:
1293         msg_Dbg( p_spu, "control query not supported" );
1294         return VLC_EGENERIC;
1295     }
1296
1297     return VLC_SUCCESS;
1298 }
1299
1300 /*****************************************************************************
1301  * Object variables callbacks
1302  *****************************************************************************/
1303
1304 /*****************************************************************************
1305  * UpdateSPU: update subpicture settings
1306  *****************************************************************************
1307  * This function is called from CropCallback and at initialization time, to
1308  * retrieve crop information from the input.
1309  *****************************************************************************/
1310 static void UpdateSPU( spu_t *p_spu, vlc_object_t *p_object )
1311 {
1312     vlc_value_t val;
1313
1314     p_spu->b_force_palette = false;
1315     p_spu->b_force_crop = false;
1316
1317     if( var_Get( p_object, "highlight", &val ) || !val.b_bool ) return;
1318
1319     p_spu->b_force_crop = true;
1320     var_Get( p_object, "x-start", &val );
1321     p_spu->i_crop_x = val.i_int;
1322     var_Get( p_object, "y-start", &val );
1323     p_spu->i_crop_y = val.i_int;
1324     var_Get( p_object, "x-end", &val );
1325     p_spu->i_crop_width = val.i_int - p_spu->i_crop_x;
1326     var_Get( p_object, "y-end", &val );
1327     p_spu->i_crop_height = val.i_int - p_spu->i_crop_y;
1328
1329     if( var_Get( p_object, "menu-palette", &val ) == VLC_SUCCESS )
1330     {
1331         memcpy( p_spu->palette, val.p_address, 16 );
1332         p_spu->b_force_palette = true;
1333     }
1334
1335     msg_Dbg( p_object, "crop: %i,%i,%i,%i, palette forced: %i",
1336              p_spu->i_crop_x, p_spu->i_crop_y,
1337              p_spu->i_crop_width, p_spu->i_crop_height,
1338              p_spu->b_force_palette );
1339 }
1340
1341 /*****************************************************************************
1342  * CropCallback: called when the highlight properties are changed
1343  *****************************************************************************
1344  * This callback is called from the input thread when we need cropping
1345  *****************************************************************************/
1346 static int CropCallback( vlc_object_t *p_object, char const *psz_var,
1347                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1348 {
1349     (void)psz_var; (void)oldval; (void)newval;
1350     UpdateSPU( (spu_t *)p_data, p_object );
1351     return VLC_SUCCESS;
1352 }
1353
1354 /*****************************************************************************
1355  * Buffers allocation callbacks for the filters
1356  *****************************************************************************/
1357 static subpicture_t *sub_new_buffer( filter_t *p_filter )
1358 {
1359     filter_owner_sys_t *p_sys = p_filter->p_owner;
1360     subpicture_t *p_subpicture = spu_CreateSubpicture( p_sys->p_spu );
1361     if( p_subpicture ) p_subpicture->i_channel = p_sys->i_channel;
1362     return p_subpicture;
1363 }
1364
1365 static void sub_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1366 {
1367     filter_owner_sys_t *p_sys = p_filter->p_owner;
1368     spu_DestroySubpicture( p_sys->p_spu, p_subpic );
1369 }
1370
1371 static subpicture_t *spu_new_buffer( filter_t *p_filter )
1372 {
1373     (void)p_filter;
1374     subpicture_t *p_subpic = (subpicture_t *)malloc(sizeof(subpicture_t));
1375     if( !p_subpic ) return NULL;
1376     memset( p_subpic, 0, sizeof(subpicture_t) );
1377     p_subpic->b_absolute = true;
1378
1379     p_subpic->pf_create_region = __spu_CreateRegion;
1380     p_subpic->pf_make_region = __spu_MakeRegion;
1381     p_subpic->pf_destroy_region = __spu_DestroyRegion;
1382
1383     return p_subpic;
1384 }
1385
1386 static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1387 {
1388     while( p_subpic->p_region )
1389     {
1390         subpicture_region_t *p_region = p_subpic->p_region;
1391         p_subpic->p_region = p_region->p_next;
1392         p_subpic->pf_destroy_region( VLC_OBJECT(p_filter), p_region );
1393     }
1394
1395     free( p_subpic );
1396 }
1397
1398 static picture_t *spu_new_video_buffer( filter_t *p_filter )
1399 {
1400     picture_t *p_picture = malloc( sizeof(picture_t) );
1401     if( !p_picture ) return NULL;
1402     if( vout_AllocatePicture( p_filter, p_picture,
1403                               p_filter->fmt_out.video.i_chroma,
1404                               p_filter->fmt_out.video.i_width,
1405                               p_filter->fmt_out.video.i_height,
1406                               p_filter->fmt_out.video.i_aspect )
1407         != VLC_SUCCESS )
1408     {
1409         free( p_picture );
1410         return NULL;
1411     }
1412
1413     p_picture->pf_release = RegionPictureRelease;
1414
1415     return p_picture;
1416 }
1417
1418 static void spu_del_video_buffer( filter_t *p_filter, picture_t *p_pic )
1419 {
1420     (void)p_filter;
1421     if( p_pic )
1422     {
1423         free( p_pic->p_data_orig );
1424         free( p_pic );
1425     }
1426 }
1427
1428 static int SubFilterCallback( vlc_object_t *p_object, char const *psz_var,
1429                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
1430 {
1431     VLC_UNUSED(p_object); VLC_UNUSED(oldval);
1432     VLC_UNUSED(newval); VLC_UNUSED(psz_var);
1433
1434     spu_t *p_spu = (spu_t *)p_data;
1435     vlc_mutex_lock( &p_spu->subpicture_lock );
1436     filter_chain_Reset( p_spu->p_chain, NULL, NULL );
1437     spu_ParseChain( p_spu );
1438     vlc_mutex_unlock( &p_spu->subpicture_lock );
1439     return VLC_SUCCESS;
1440 }
1441
1442 static int sub_filter_allocation_init( filter_t *p_filter, void *p_data )
1443 {
1444     spu_t *p_spu = (spu_t *)p_data;
1445
1446     p_filter->pf_sub_buffer_new = sub_new_buffer;
1447     p_filter->pf_sub_buffer_del = sub_del_buffer;
1448
1449     filter_owner_sys_t *p_sys = malloc( sizeof(filter_owner_sys_t) );
1450     if( !p_sys ) return VLC_EGENERIC;
1451
1452     p_filter->p_owner = p_sys;
1453     spu_Control( p_spu, SPU_CHANNEL_REGISTER, &p_sys->i_channel );
1454     p_sys->p_spu = p_spu;
1455
1456     return VLC_SUCCESS;
1457 }
1458
1459 static void sub_filter_allocation_clear( filter_t *p_filter )
1460 {
1461     filter_owner_sys_t *p_sys = p_filter->p_owner;
1462     SpuClearChannel( p_sys->p_spu, p_sys->i_channel, true );
1463     free( p_filter->p_owner );
1464 }