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