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