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