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