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