]> git.sesse.net Git - vlc/blob - src/video_output/vout_subpictures.c
+ vout_subpictures.c: don't check only the first subpicture_t to see
[vlc] / src / video_output / vout_subpictures.c
1 /*****************************************************************************
2  * vout_subpictures.c : subpicture management functions
3  *****************************************************************************
4  * Copyright (C) 2000-2004 VideoLAN
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>                                                /* free() */
30 #include <stdio.h>                                              /* sprintf() */
31 #include <string.h>                                            /* strerror() */
32
33 #include <vlc/vlc.h>
34
35 #include "vlc_block.h"
36 #include "vlc_video.h"
37 #include "video_output.h"
38 #include "vlc_filter.h"
39 #include "osd.h"
40
41 static void UpdateSPU        ( vout_thread_t *, vlc_object_t * );
42 static int  CropCallback     ( vlc_object_t *, char const *,
43                                vlc_value_t, vlc_value_t, void * );
44
45 static subpicture_t *spu_new_buffer( filter_t * );
46 static void spu_del_buffer( filter_t *, subpicture_t * );
47
48 /**
49  * Initialise the subpicture decoder unit
50  *
51  * \param p_vout the vout in which to create the subpicture unit
52  */
53 void vout_InitSPU( vout_thread_t *p_vout )
54 {
55     int i_index;
56
57     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
58     {
59         p_vout->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
60         p_vout->p_subpicture[i_index].i_type   = EMPTY_SUBPICTURE;
61     }
62
63     p_vout->p_blend = NULL;
64     p_vout->p_text = NULL;
65
66     p_vout->i_crop_x = p_vout->i_crop_y =
67         p_vout->i_crop_width = p_vout->i_crop_height = 0;
68     p_vout->b_force_alpha = VLC_FALSE;
69     p_vout->b_force_crop = VLC_FALSE;
70
71     vout_AttachSPU( p_vout, VLC_TRUE );
72 }
73
74 /**
75  * Destroy the subpicture decoder unit
76  *
77  * \param p_vout the vout in which to destroy the subpicture unit
78  */
79 void vout_DestroySPU( vout_thread_t *p_vout )
80 {
81     int i_index;
82
83     /* Destroy all remaining subpictures */
84     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
85     {
86         if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
87         {
88             vout_DestroySubPicture( p_vout,
89                                     &p_vout->p_subpicture[i_index] );
90         }
91     }
92
93     if( p_vout->p_blend )
94     {
95         if( p_vout->p_blend->p_module )
96             module_Unneed( p_vout->p_blend, p_vout->p_blend->p_module );
97
98         vlc_object_detach( p_vout->p_blend );
99         vlc_object_destroy( p_vout->p_blend );
100     }
101
102     if( p_vout->p_text )
103     {
104         if( p_vout->p_text->p_module )
105             module_Unneed( p_vout->p_text, p_vout->p_text->p_module );
106
107         vlc_object_detach( p_vout->p_text );
108         vlc_object_destroy( p_vout->p_text );
109     }
110
111     vout_AttachSPU( p_vout, VLC_FALSE );
112 }
113
114 /**
115  * Attach/Detach the SPU from any input
116  *
117  * \param p_vout the vout in which to destroy the subpicture unit
118  * \param b_attach to select attach or detach
119  */
120 void vout_AttachSPU( vout_thread_t *p_vout, vlc_bool_t b_attach )
121 {
122     vlc_object_t *p_input;
123
124     p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
125     if( !p_input ) return;
126
127     if( b_attach )
128     {
129         UpdateSPU( p_vout, VLC_OBJECT(p_input) );
130         var_AddCallback( p_input, "highlight", CropCallback, p_vout );
131         vlc_object_release( p_input );
132     }
133     else
134     {
135         /* Delete callback */
136         var_DelCallback( p_input, "highlight", CropCallback, p_vout );
137         vlc_object_release( p_input );
138     }
139
140 }
141
142 /**
143  * Create a subpicture region
144  *
145  * \param p_this vlc_object_t
146  * \param p_fmt the format that this subpicture region should have
147  */
148 subpicture_region_t *__spu_CreateRegion( vlc_object_t *p_this,
149                                          video_format_t *p_fmt )
150 {
151     subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
152     memset( p_region, 0, sizeof(subpicture_region_t) );
153     p_region->p_next = 0;
154     p_region->fmt = *p_fmt;
155     p_region->psz_text = 0;
156
157     if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') )
158         p_fmt->p_palette = p_region->fmt.p_palette =
159             malloc( sizeof(video_palette_t) );
160     else p_fmt->p_palette = p_region->fmt.p_palette = NULL;
161
162     p_region->picture.p_data_orig = 0;
163
164     if( p_fmt->i_chroma == VLC_FOURCC('T','E','X','T') ) return p_region;
165
166     vout_AllocatePicture( p_this, &p_region->picture, p_fmt->i_chroma,
167                           p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect );
168
169     if( !p_region->picture.i_planes )
170     {
171         free( p_region );
172         free( p_fmt->p_palette );
173         return NULL;
174     }
175
176     return p_region;
177 }
178
179 /**
180  * Destroy a subpicture region
181  *
182  * \param p_this vlc_object_t
183  * \param p_region the subpicture region to destroy
184  */
185 void __spu_DestroyRegion( vlc_object_t *p_this, subpicture_region_t *p_region )
186 {
187     if( !p_region ) return;
188     if( p_region->picture.p_data_orig ) free( p_region->picture.p_data_orig );
189     if( p_region->fmt.p_palette ) free( p_region->fmt.p_palette );
190     if( p_region->psz_text ) free( p_region->psz_text );
191     free( p_region );
192 }
193
194 /**
195  * Display a subpicture unit
196  *
197  * Remove the reservation flag of a subpicture, which will cause it to be
198  * ready for display.
199  * \param p_vout the video output this subpicture should be displayed on
200  * \param p_subpic the subpicture to display
201  */
202 void vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
203 {
204     int         i_margin;
205
206     /* Check if status is valid */
207     if( p_subpic->i_status != RESERVED_SUBPICTURE )
208     {
209         msg_Err( p_vout, "subpicture %p has invalid status #%d",
210                          p_subpic, p_subpic->i_status );
211     }
212
213     /* If the user requested an SPU margin, we force the position after
214      * having checked that it was a valid value. */
215     i_margin = config_GetInt( p_vout, "spumargin" );
216
217     if( i_margin >= 0 )
218     {
219         if( p_subpic->i_height + (unsigned int)i_margin
220                                                  <= p_vout->output.i_height )
221         {
222             p_subpic->i_y = p_vout->output.i_height
223                              - i_margin - p_subpic->i_height;
224         }
225     }
226
227     /* Remove reservation flag */
228     p_subpic->i_status = READY_SUBPICTURE;
229 }
230
231 /**
232  * Allocate a subpicture in the video output heap.
233  *
234  * This function create a reserved subpicture in the video output heap.
235  * A null pointer is returned if the function fails. This method provides an
236  * already allocated zone of memory in the spu data fields. It needs locking
237  * since several pictures can be created by several producers threads.
238  * \param p_vout the vout in which to create the subpicture
239  * \param i_channel the channel this subpicture should belong to
240  * \param i_type the type of the subpicture
241  * \return NULL on error, a reserved subpicture otherwise
242  */
243 subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_channel,
244                                      int i_type )
245 {
246     int                 i_subpic;                        /* subpicture index */
247     subpicture_t *      p_subpic = NULL;            /* first free subpicture */
248
249     /* Clear the default channel before writing into it */
250     if( i_channel == DEFAULT_CHAN )
251     {
252         vout_ClearOSDChannel( p_vout, DEFAULT_CHAN );
253     }
254
255     /* Get lock */
256     vlc_mutex_lock( &p_vout->subpicture_lock );
257
258     /*
259      * Look for an empty place
260      */
261     p_subpic = NULL;
262     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
263     {
264         if( p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE )
265         {
266             /* Subpicture is empty and ready for allocation */
267             p_subpic = &p_vout->p_subpicture[i_subpic];
268             p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
269             break;
270         }
271     }
272
273     /* If no free subpicture could be found */
274     if( p_subpic == NULL )
275     {
276         msg_Err( p_vout, "subpicture heap is full" );
277         vlc_mutex_unlock( &p_vout->subpicture_lock );
278         return NULL;
279     }
280
281     /* Copy subpicture information, set some default values */
282     p_subpic->i_channel = i_channel;
283     p_subpic->i_type    = i_type;
284     p_subpic->i_status  = RESERVED_SUBPICTURE;
285
286     p_subpic->i_start   = 0;
287     p_subpic->i_stop    = 0;
288     p_subpic->b_ephemer = VLC_FALSE;
289
290     p_subpic->i_x       = 0;
291     p_subpic->i_y       = 0;
292     p_subpic->i_width   = 0;
293     p_subpic->i_height  = 0;
294     p_subpic->b_absolute= VLC_TRUE;
295     p_subpic->i_flags   = 0;
296     p_subpic->pf_render = 0;
297     p_subpic->pf_destroy= 0;
298     p_subpic->p_sys     = 0;
299
300     /* Remain last subpicture displayed in DEFAULT_CHAN */
301     if( i_channel == DEFAULT_CHAN )
302     {
303         p_vout->p_default_channel = p_subpic;
304     }
305
306     vlc_mutex_unlock( &p_vout->subpicture_lock );
307
308     p_subpic->pf_create_region = __spu_CreateRegion;
309     p_subpic->pf_destroy_region = __spu_DestroyRegion;
310
311     return p_subpic;
312 }
313
314 /**
315  * Remove a subpicture from the heap
316  *
317  * This function frees a previously reserved subpicture.
318  * It is meant to be used when the construction of a picture aborted.
319  * This function does not need locking since reserved subpictures are ignored
320  * by the output thread.
321  */
322 void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
323 {
324     /* Get lock */
325     vlc_mutex_lock( &p_vout->subpicture_lock );
326
327     /* There can be race conditions so we need to check the status */
328     if( p_subpic->i_status == FREE_SUBPICTURE )
329     {
330         vlc_mutex_unlock( &p_vout->subpicture_lock );
331         return;
332     }
333
334     /* Check if status is valid */
335     if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
336            && ( p_subpic->i_status != READY_SUBPICTURE ) )
337     {
338         msg_Err( p_vout, "subpicture %p has invalid status %d",
339                          p_subpic, p_subpic->i_status );
340     }
341
342     while( p_subpic->p_region )
343     {
344         subpicture_region_t *p_region = p_subpic->p_region;
345         p_subpic->p_region = p_region->p_next;
346         spu_DestroyRegion( p_vout, p_region );
347     }
348
349     if( p_subpic->pf_destroy )
350     {
351         p_subpic->pf_destroy( p_subpic );
352     }
353
354     p_subpic->i_status = FREE_SUBPICTURE;
355
356     vlc_mutex_unlock( &p_vout->subpicture_lock );
357 }
358
359 /*****************************************************************************
360  * vout_RenderSubPictures: render a subpicture list
361  *****************************************************************************
362  * This function renders all sub picture units in the list.
363  *****************************************************************************/
364 void vout_RenderSubPictures( vout_thread_t *p_vout, picture_t *p_pic_dst,
365                              picture_t *p_pic_src, subpicture_t *p_subpic )
366 {
367     /* Get lock */
368     vlc_mutex_lock( &p_vout->subpicture_lock );
369
370     /* Check i_status again to make sure spudec hasn't destroyed the subpic */
371     while( p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE )
372     {
373         subpicture_region_t *p_region = p_subpic->p_region;
374
375         /* Load the blending module */
376         if( !p_vout->p_blend && p_region )
377         {
378             p_vout->p_blend = vlc_object_create( p_vout, sizeof(filter_t) );
379             vlc_object_attach( p_vout->p_blend, p_vout );
380             p_vout->p_blend->fmt_out.video.i_x_offset =
381                 p_vout->p_blend->fmt_out.video.i_y_offset = 0;
382             p_vout->p_blend->fmt_out.video.i_aspect =
383                 p_vout->render.i_aspect;
384             p_vout->p_blend->fmt_out.video.i_chroma =
385                 p_vout->output.i_chroma;
386     
387             p_vout->p_blend->fmt_in.video.i_chroma = VLC_FOURCC('Y','U','V','P');
388     
389             p_vout->p_blend->p_module =
390                 module_Need( p_vout->p_blend, "video blending", 0, 0 );
391         }
392     
393         /* Load the text rendering module */
394         if( !p_vout->p_text && p_region )
395         {
396             p_vout->p_text = vlc_object_create( p_vout, sizeof(filter_t) );
397             vlc_object_attach( p_vout->p_text, p_vout );
398     
399             p_vout->p_text->fmt_out.video.i_width =
400                 p_vout->p_text->fmt_out.video.i_visible_width =
401                     p_vout->output.i_width;
402             p_vout->p_text->fmt_out.video.i_height =
403                 p_vout->p_text->fmt_out.video.i_visible_height =
404                     p_vout->output.i_height;
405     
406             p_vout->p_text->pf_spu_buffer_new = spu_new_buffer;
407             p_vout->p_text->pf_spu_buffer_del = spu_del_buffer;
408     
409             p_vout->p_text->p_module =
410                 module_Need( p_vout->p_text, "text renderer", 0, 0 );
411         }
412
413         if( p_subpic->pf_render )
414         {
415             p_subpic->pf_render( p_vout, p_pic_dst, p_subpic );
416         }
417         else while( p_region && p_vout->p_blend &&
418                     p_vout->p_blend->pf_video_blend )
419         {
420             int i_x_offset = p_region->i_x + p_subpic->i_x;
421             int i_y_offset = p_region->i_y + p_subpic->i_y;
422
423             if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') )
424             {
425                 if( p_vout->p_text && p_vout->p_text->p_module &&
426                     p_vout->p_text->pf_render_string )
427                 {
428                     /* TODO: do it in a less hacky way
429                      * (modify text renderer API) */
430                     subpicture_t *p_spu;
431                     subpicture_region_t tmp_region;
432                     block_t *p_new_block =
433                         block_New( p_vout, strlen(p_region->psz_text) + 1 );
434
435                     if( p_new_block )
436                     {
437                         memcpy( p_new_block->p_buffer, p_region->psz_text,
438                                 p_new_block->i_buffer );
439                         p_new_block->i_pts = p_new_block->i_dts =
440                             p_subpic->i_start;
441                         p_new_block->i_length =
442                             p_subpic->i_start - p_subpic->i_stop;
443                         p_spu = p_vout->p_text->pf_render_string(
444                             p_vout->p_text, p_new_block );
445
446                         if( p_spu )
447                         {
448                             tmp_region = *p_region;
449                             *p_region = *p_spu->p_region;
450                             p_region->p_next = tmp_region.p_next;
451                             *p_spu->p_region = tmp_region;
452                             p_vout->p_text->pf_spu_buffer_del( p_vout->p_text,
453                                                                p_spu );
454                         }
455                     }
456                 }
457             }
458
459             if( p_subpic->i_flags & OSD_ALIGN_BOTTOM )
460             {
461                 i_y_offset = p_vout->output.i_height - p_region->fmt.i_height -
462                     p_subpic->i_y;
463             }
464             else if ( !(p_subpic->i_flags & OSD_ALIGN_TOP) )
465             {
466                 i_y_offset = p_vout->output.i_height / 2 -
467                     p_region->fmt.i_height / 2;
468             }
469
470             if( p_subpic->i_flags & OSD_ALIGN_RIGHT )
471             {
472                 i_x_offset = p_vout->output.i_width - p_region->fmt.i_width -
473                     p_subpic->i_x;
474             }
475             else if ( !(p_subpic->i_flags & OSD_ALIGN_LEFT) )
476             {
477                 i_x_offset = p_vout->output.i_width / 2 -
478                     p_region->fmt.i_width / 2;
479             }
480
481             if( p_subpic->b_absolute )
482             {
483                 i_x_offset = p_region->i_x + p_subpic->i_x;
484                 i_y_offset = p_region->i_y + p_subpic->i_y;
485             }
486
487             p_vout->p_blend->fmt_in.video = p_region->fmt;
488
489             /* Force cropping if requested */
490             if( p_vout->b_force_crop )
491             {
492                 video_format_t *p_fmt = &p_vout->p_blend->fmt_in.video;
493
494                 /* Find the intersection */
495                 if( p_vout->i_crop_x + p_vout->i_crop_width <= i_x_offset ||
496                     i_x_offset + (int)p_fmt->i_visible_width <
497                         p_vout->i_crop_x ||
498                     p_vout->i_crop_y + p_vout->i_crop_height <= i_y_offset ||
499                     i_y_offset + (int)p_fmt->i_visible_height <
500                         p_vout->i_crop_y )
501                 {
502                     /* No intersection */
503                     p_fmt->i_visible_width = p_fmt->i_visible_height = 0;
504                 }
505                 else
506                 {
507                     int i_x, i_y, i_x_end, i_y_end;
508                     i_x = __MAX( p_vout->i_crop_x, i_x_offset );
509                     i_y = __MAX( p_vout->i_crop_y, i_y_offset );
510                     i_x_end = __MIN( p_vout->i_crop_x + p_vout->i_crop_width,
511                                    i_x_offset + (int)p_fmt->i_visible_width );
512                     i_y_end = __MIN( p_vout->i_crop_y + p_vout->i_crop_height,
513                                    i_y_offset + (int)p_fmt->i_visible_height );
514
515                     p_fmt->i_x_offset = i_x - i_x_offset;
516                     p_fmt->i_y_offset = i_y - i_y_offset;
517                     p_fmt->i_visible_width = i_x_end - i_x;
518                     p_fmt->i_visible_height = i_y_end - i_y;
519
520                     i_x_offset = i_x;
521                     i_y_offset = i_y;
522                 }
523             }
524
525             /* Force palette if requested */
526             if( p_vout->b_force_alpha && VLC_FOURCC('Y','U','V','P') ==
527                 p_vout->p_blend->fmt_in.video.i_chroma )
528             {
529                 p_vout->p_blend->fmt_in.video.p_palette->palette[0][3] =
530                     p_vout->pi_alpha[0];
531                 p_vout->p_blend->fmt_in.video.p_palette->palette[1][3] =
532                     p_vout->pi_alpha[1];
533                 p_vout->p_blend->fmt_in.video.p_palette->palette[2][3] =
534                     p_vout->pi_alpha[2];
535                 p_vout->p_blend->fmt_in.video.p_palette->palette[3][3] =
536                     p_vout->pi_alpha[3];
537             }
538
539             /* Update the output picture size */
540             p_vout->p_blend->fmt_out.video.i_width =
541                 p_vout->p_blend->fmt_out.video.i_visible_width =
542                     p_vout->output.i_width;
543             p_vout->p_blend->fmt_out.video.i_height =
544                 p_vout->p_blend->fmt_out.video.i_visible_height =
545                     p_vout->output.i_height;
546
547            p_vout->p_blend->pf_video_blend( p_vout->p_blend, p_pic_dst,
548                 p_pic_src, &p_region->picture, i_x_offset, i_y_offset );
549
550             p_region = p_region->p_next;
551         }
552
553         p_subpic = p_subpic->p_next;
554     }
555
556     vlc_mutex_unlock( &p_vout->subpicture_lock );
557 }
558
559 /*****************************************************************************
560  * vout_SortSubPictures: find the subpictures to display
561  *****************************************************************************
562  * This function parses all subpictures and decides which ones need to be
563  * displayed. This operation does not need lock, since only READY_SUBPICTURE
564  * are handled. If no picture has been selected, display_date will depend on
565  * the subpicture.
566  * We also check for ephemer DVD subpictures (subpictures that have
567  * to be removed if a newer one is available), which makes it a lot
568  * more difficult to guess if a subpicture has to be rendered or not.
569  *****************************************************************************/
570 subpicture_t *vout_SortSubPictures( vout_thread_t *p_vout,
571                                     mtime_t display_date )
572 {
573     int i_index;
574     subpicture_t *p_subpic     = NULL;
575     subpicture_t *p_ephemer    = NULL;
576     mtime_t       ephemer_date = 0;
577
578     /* We get an easily parsable chained list of subpictures which
579      * ends with NULL since p_subpic was initialized to NULL. */
580     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
581     {
582         if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE )
583         {
584             /* If it is a DVD subpicture, check its date */
585             if( p_vout->p_subpicture[i_index].i_type == MEMORY_SUBPICTURE )
586             {
587                 if( !p_vout->p_subpicture[i_index].b_ephemer
588                      && display_date > p_vout->p_subpicture[i_index].i_stop )
589                 {
590                     /* Too late, destroy the subpic */
591                     vout_DestroySubPicture( p_vout,
592                                     &p_vout->p_subpicture[i_index] );
593                     continue;
594                 }
595
596                 if( display_date
597                      && display_date < p_vout->p_subpicture[i_index].i_start )
598                 {
599                     /* Too early, come back next monday */
600                     continue;
601                 }
602
603                 /* If this is an ephemer subpic, see if it's the
604                  * youngest we have */
605                 if( p_vout->p_subpicture[i_index].b_ephemer )
606                 {
607                     if( p_ephemer == NULL )
608                     {
609                         p_ephemer = &p_vout->p_subpicture[i_index];
610                         continue;
611                     }
612
613                     if( p_vout->p_subpicture[i_index].i_start
614                                                      < p_ephemer->i_start )
615                     {
616                         /* Link the previous ephemer subpicture and
617                          * replace it with the current one */
618                         p_ephemer->p_next = p_subpic;
619                         p_subpic = p_ephemer;
620                         p_ephemer = &p_vout->p_subpicture[i_index];
621
622                         /* If it's the 2nd youngest subpicture,
623                          * register its date */
624                         if( !ephemer_date
625                               || ephemer_date > p_subpic->i_start )
626                         {
627                             ephemer_date = p_subpic->i_start;
628                         }
629
630                         continue;
631                     }
632                 }
633
634                 p_vout->p_subpicture[i_index].p_next = p_subpic;
635                 p_subpic = &p_vout->p_subpicture[i_index];
636
637                 /* If it's the 2nd youngest subpicture, register its date */
638                 if( !ephemer_date || ephemer_date > p_subpic->i_start )
639                 {
640                     ephemer_date = p_subpic->i_start;
641                 }
642             }
643             /* If it's not a DVD subpicture, just register it */
644             else
645             {
646                 p_vout->p_subpicture[i_index].p_next = p_subpic;
647                 p_subpic = &p_vout->p_subpicture[i_index];
648             }
649         }
650     }
651
652     /* If we found an ephemer subpicture, check if it has to be
653      * displayed */
654     if( p_ephemer != NULL )
655     {
656         if( p_ephemer->i_start <= ephemer_date )
657         {
658             /* Ephemer subpicture has lived too long */
659             vout_DestroySubPicture( p_vout, p_ephemer );
660         }
661         else
662         {
663             /* Ephemer subpicture can still live a bit */
664             p_ephemer->p_next = p_subpic;
665             return p_ephemer;
666         }
667     }
668
669     return p_subpic;
670 }
671
672 /*****************************************************************************
673  * vout_RegisterOSDChannel: register an OSD channel
674  *****************************************************************************
675  * This function affects an ID to an OSD channel
676  *****************************************************************************/
677 int vout_RegisterOSDChannel( vout_thread_t *p_vout )
678 {
679     msg_Dbg( p_vout, "Registering OSD channel, ID: %i",
680              p_vout->i_channel_count + 1 );
681     return ++p_vout->i_channel_count;
682 }
683
684 /*****************************************************************************
685  * vout_ClearOSDChannel: clear an OSD channel
686  *****************************************************************************
687  * This function destroys the subpictures which belong to the OSD channel
688  * corresponding to i_channel_id.
689  *****************************************************************************/
690 void vout_ClearOSDChannel( vout_thread_t *p_vout, int i_channel )
691 {
692     int                 i_subpic;                        /* subpicture index */
693     subpicture_t *      p_subpic = NULL;            /* first free subpicture */
694
695     if( i_channel == DEFAULT_CHAN )
696     {
697         if( p_vout->p_default_channel != NULL )
698         {
699             vout_DestroySubPicture( p_vout, p_vout->p_default_channel );
700         }
701         p_vout->p_default_channel = NULL;
702         return;
703     }
704
705     vlc_mutex_lock( &p_vout->subpicture_lock );
706
707     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
708     {
709         p_subpic = &p_vout->p_subpicture[i_subpic];
710         if( p_subpic->i_status == FREE_SUBPICTURE
711             || ( p_subpic->i_status != RESERVED_SUBPICTURE
712                  && p_subpic->i_status != READY_SUBPICTURE ) )
713         {
714             continue;
715         }
716
717         if( p_subpic->i_channel == i_channel )
718         {
719             while( p_subpic->p_region )
720             {
721                 subpicture_region_t *p_region = p_subpic->p_region;
722                 p_subpic->p_region = p_region->p_next;
723                 spu_DestroyRegion( p_vout, p_region );
724             }
725
726             if( p_subpic->pf_destroy )
727             {
728                 p_subpic->pf_destroy( p_subpic );
729             }
730             p_subpic->i_status = FREE_SUBPICTURE;
731         }
732     }
733
734     vlc_mutex_unlock( &p_vout->subpicture_lock );
735 }
736
737 /*****************************************************************************
738  * UpdateSPU: update subpicture settings
739  *****************************************************************************
740  * This function is called from CropCallback and at initialization time, to
741  * retrieve crop information from the input.
742  *****************************************************************************/
743 static void UpdateSPU( vout_thread_t *p_vout, vlc_object_t *p_object )
744 {
745     vlc_value_t val;
746
747     p_vout->b_force_alpha = VLC_FALSE;
748     p_vout->b_force_crop = VLC_FALSE;
749
750     if( var_Get( p_object, "highlight", &val ) || !val.b_bool ) return;
751
752     p_vout->b_force_crop = VLC_TRUE;
753     var_Get( p_object, "x-start", &val );
754     p_vout->i_crop_x = val.i_int;
755     var_Get( p_object, "y-start", &val );
756     p_vout->i_crop_y = val.i_int;
757     var_Get( p_object, "x-end", &val );
758     p_vout->i_crop_width = val.i_int - p_vout->i_crop_x;
759     var_Get( p_object, "y-end", &val );
760     p_vout->i_crop_height = val.i_int - p_vout->i_crop_y;
761
762 #if 0
763     if( var_Get( p_object, "color", &val ) == VLC_SUCCESS )
764     {
765         int i;
766         for( i = 0; i < 4; i++ )
767         {
768             p_vout->pi_color[i] = ((uint8_t *)val.p_address)[i];
769         }
770     }
771 #endif
772
773     if( var_Get( p_object, "contrast", &val ) == VLC_SUCCESS )
774     {
775         int i;
776         for( i = 0; i < 4; i++ )
777         {
778             p_vout->pi_alpha[i] = ((uint8_t *)val.p_address)[i];
779             p_vout->pi_alpha[i] = p_vout->pi_alpha[i] == 0xf ?
780                 0xff : p_vout->pi_alpha[i] << 4;
781         }
782         p_vout->b_force_alpha = VLC_TRUE;
783     }
784
785     msg_Dbg( p_vout, "crop: %i,%i,%i,%i, alpha: %i",
786              p_vout->i_crop_x, p_vout->i_crop_y,
787              p_vout->i_crop_width, p_vout->i_crop_height,
788              p_vout->b_force_alpha );
789 }
790
791 /*****************************************************************************
792  * CropCallback: called when the highlight properties are changed
793  *****************************************************************************
794  * This callback is called from the input thread when we need cropping
795  *****************************************************************************/
796 static int CropCallback( vlc_object_t *p_object, char const *psz_var,
797                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
798 {
799     UpdateSPU( (vout_thread_t *)p_data, p_object );
800     return VLC_SUCCESS;
801 }
802
803 /*****************************************************************************
804  * Buffers allocation callbacks for the filters
805  *****************************************************************************/
806 static subpicture_t *spu_new_buffer( filter_t *p_filter )
807 {
808     subpicture_t *p_subpic = (subpicture_t *)malloc(sizeof(subpicture_t));
809     memset( p_subpic, 0, sizeof(subpicture_t) );
810     p_subpic->b_absolute = VLC_TRUE;
811
812     p_subpic->pf_create_region = __spu_CreateRegion;
813     p_subpic->pf_destroy_region = __spu_DestroyRegion;
814
815     return p_subpic;
816 }
817
818 static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
819 {
820     while( p_subpic->p_region )
821     {
822         subpicture_region_t *p_region = p_subpic->p_region;
823         p_subpic->p_region = p_region->p_next;
824         p_subpic->pf_destroy_region( VLC_OBJECT(p_filter), p_region );
825     }
826
827     free( p_subpic );
828 }