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