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