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