1 /*****************************************************************************
2 * vout_subpictures.c : subpicture management functions
3 *****************************************************************************
4 * Copyright (C) 2000-2004 VideoLAN
7 * Authors: Vincent Seguin <seguin@via.ecp.fr>
8 * Samuel Hocevar <sam@zoy.org>
9 * Gildas Bazin <gbazin@videolan.org>
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.
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.
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 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
29 #include <stdlib.h> /* free() */
30 #include <stdio.h> /* sprintf() */
31 #include <string.h> /* strerror() */
35 #include "vlc_video.h"
36 #include "video_output.h"
37 #include "vlc_filter.h"
40 static void UpdateSPU ( vout_thread_t *, vlc_object_t * );
41 static int CropCallback ( vlc_object_t *, char const *,
42 vlc_value_t, vlc_value_t, void * );
44 static subpicture_t *spu_new_buffer( filter_t * );
45 static void spu_del_buffer( filter_t *, subpicture_t * );
48 * Initialise the subpicture decoder unit
50 * \param p_vout the vout in which to create the subpicture unit
52 void vout_InitSPU( vout_thread_t *p_vout )
54 vlc_object_t *p_input;
57 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
59 p_vout->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
60 p_vout->p_subpicture[i_index].i_type = EMPTY_SUBPICTURE;
63 /* Load the text rendering module */
64 p_vout->p_text = vlc_object_create( p_vout, sizeof(filter_t) );
65 p_vout->p_text->pf_spu_buffer_new = spu_new_buffer;
66 p_vout->p_text->pf_spu_buffer_del = spu_del_buffer;
67 p_vout->p_text->p_owner = (filter_owner_sys_t *)p_vout;
68 vlc_object_attach( p_vout->p_text, p_vout );
69 p_vout->p_text->p_module =
70 module_Need( p_vout->p_text, "text renderer", 0, 0 );
71 if( p_vout->p_text->p_module == NULL )
73 msg_Warn( p_vout, "no suitable text renderer module" );
74 vlc_object_detach( p_vout->p_text );
75 vlc_object_destroy( p_vout->p_text );
76 p_vout->p_text = NULL;
79 p_vout->p_blend = NULL;
81 p_vout->i_crop_x = p_vout->i_crop_y =
82 p_vout->i_crop_width = p_vout->i_crop_height = 0;
83 p_vout->b_force_alpha = VLC_FALSE;
84 p_vout->b_force_crop = VLC_FALSE;
87 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
90 UpdateSPU( p_vout, VLC_OBJECT(p_input) );
91 var_AddCallback( p_input, "highlight", CropCallback, p_vout );
92 vlc_object_release( p_input );
97 * Destroy the subpicture decoder unit
99 * \param p_vout the vout in which to destroy the subpicture unit
101 void vout_DestroySPU( vout_thread_t *p_vout )
103 vlc_object_t *p_input;
106 /* Destroy all remaining subpictures */
107 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
109 if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
111 vout_DestroySubPicture( p_vout,
112 &p_vout->p_subpicture[i_index] );
118 if( p_vout->p_text->p_module )
119 module_Unneed( p_vout->p_text, p_vout->p_text->p_module );
121 vlc_object_detach( p_vout->p_text );
122 vlc_object_destroy( p_vout->p_text );
125 if( p_vout->p_blend )
127 if( p_vout->p_blend->p_module )
128 module_Unneed( p_vout->p_blend, p_vout->p_blend->p_module );
130 vlc_object_detach( p_vout->p_blend );
131 vlc_object_destroy( p_vout->p_blend );
134 /* Delete callback */
135 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
138 var_DelCallback( p_input, "highlight", CropCallback, p_vout );
139 vlc_object_release( p_input );
144 * Create a subpicture region
146 * \param p_this vlc_object_t
147 * \param p_fmt the format that this subpicture region should have
149 subpicture_region_t *__spu_CreateRegion( vlc_object_t *p_this,
150 video_format_t *p_fmt )
152 subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
153 memset( p_region, 0, sizeof(subpicture_region_t) );
154 p_region->p_next = 0;
155 p_region->fmt = *p_fmt;
157 vout_AllocatePicture( p_this, &p_region->picture, p_fmt->i_chroma,
158 p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect );
160 if( !p_region->picture.i_planes )
166 if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') )
167 p_fmt->p_palette = p_region->fmt.p_palette =
168 malloc( sizeof(video_palette_t) );
169 else p_fmt->p_palette = p_region->fmt.p_palette = NULL;
175 * Destroy a subpicture region
177 * \param p_this vlc_object_t
178 * \param p_region the subpicture region to destroy
180 void __spu_DestroyRegion( vlc_object_t *p_this, subpicture_region_t *p_region )
182 if( !p_region ) return;
183 if( p_region->picture.p_data_orig ) free( p_region->picture.p_data_orig );
184 if( p_region->fmt.p_palette ) free( p_region->fmt.p_palette );
189 * Display a subpicture unit
191 * Remove the reservation flag of a subpicture, which will cause it to be
193 * \param p_vout the video output this subpicture should be displayed on
194 * \param p_subpic the subpicture to display
196 void vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
200 /* Check if status is valid */
201 if( p_subpic->i_status != RESERVED_SUBPICTURE )
203 msg_Err( p_vout, "subpicture %p has invalid status #%d",
204 p_subpic, p_subpic->i_status );
207 /* If the user requested an SPU margin, we force the position after
208 * having checked that it was a valid value. */
209 i_margin = config_GetInt( p_vout, "spumargin" );
213 if( p_subpic->i_height + (unsigned int)i_margin
214 <= p_vout->output.i_height )
216 p_subpic->i_y = p_vout->output.i_height
217 - i_margin - p_subpic->i_height;
221 /* Remove reservation flag */
222 p_subpic->i_status = READY_SUBPICTURE;
226 * Allocate a subpicture in the video output heap.
228 * This function create a reserved subpicture in the video output heap.
229 * A null pointer is returned if the function fails. This method provides an
230 * already allocated zone of memory in the spu data fields. It needs locking
231 * since several pictures can be created by several producers threads.
232 * \param p_vout the vout in which to create the subpicture
233 * \param i_channel the channel this subpicture should belong to
234 * \param i_type the type of the subpicture
235 * \return NULL on error, a reserved subpicture otherwise
237 subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_channel,
240 int i_subpic; /* subpicture index */
241 subpicture_t * p_subpic = NULL; /* first free subpicture */
243 /* Clear the default channel before writing into it */
244 if( i_channel == DEFAULT_CHAN )
246 vout_ClearOSDChannel( p_vout, DEFAULT_CHAN );
250 vlc_mutex_lock( &p_vout->subpicture_lock );
253 * Look for an empty place
256 for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
258 if( p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE )
260 /* Subpicture is empty and ready for allocation */
261 p_subpic = &p_vout->p_subpicture[i_subpic];
262 p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
267 /* If no free subpicture could be found */
268 if( p_subpic == NULL )
270 msg_Err( p_vout, "subpicture heap is full" );
271 vlc_mutex_unlock( &p_vout->subpicture_lock );
275 /* Copy subpicture information, set some default values */
276 p_subpic->i_channel = i_channel;
277 p_subpic->i_type = i_type;
278 p_subpic->i_status = RESERVED_SUBPICTURE;
280 p_subpic->i_start = 0;
281 p_subpic->i_stop = 0;
282 p_subpic->b_ephemer = VLC_FALSE;
286 p_subpic->i_width = 0;
287 p_subpic->i_height = 0;
288 p_subpic->b_absolute= VLC_TRUE;
289 p_subpic->i_flags = 0;
290 p_subpic->pf_render = 0;
291 p_subpic->pf_destroy= 0;
294 /* Remain last subpicture displayed in DEFAULT_CHAN */
295 if( i_channel == DEFAULT_CHAN )
297 p_vout->p_default_channel = p_subpic;
300 vlc_mutex_unlock( &p_vout->subpicture_lock );
302 p_subpic->pf_create_region = __spu_CreateRegion;
303 p_subpic->pf_destroy_region = __spu_DestroyRegion;
309 * Remove a subpicture from the heap
311 * This function frees a previously reserved subpicture.
312 * It is meant to be used when the construction of a picture aborted.
313 * This function does not need locking since reserved subpictures are ignored
314 * by the output thread.
316 void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
319 vlc_mutex_lock( &p_vout->subpicture_lock );
321 /* There can be race conditions so we need to check the status */
322 if( p_subpic->i_status == FREE_SUBPICTURE )
324 vlc_mutex_unlock( &p_vout->subpicture_lock );
328 /* Check if status is valid */
329 if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
330 && ( p_subpic->i_status != READY_SUBPICTURE ) )
332 msg_Err( p_vout, "subpicture %p has invalid status %d",
333 p_subpic, p_subpic->i_status );
336 while( p_subpic->p_region )
338 subpicture_region_t *p_region = p_subpic->p_region;
339 p_subpic->p_region = p_region->p_next;
340 spu_DestroyRegion( p_vout, p_region );
343 if( p_subpic->pf_destroy )
345 p_subpic->pf_destroy( p_subpic );
348 p_subpic->i_status = FREE_SUBPICTURE;
350 vlc_mutex_unlock( &p_vout->subpicture_lock );
353 /*****************************************************************************
354 * vout_RenderSubPictures: render a subpicture list
355 *****************************************************************************
356 * This function renders all sub picture units in the list.
357 *****************************************************************************/
358 void vout_RenderSubPictures( vout_thread_t *p_vout, picture_t *p_pic_dst,
359 picture_t *p_pic_src, subpicture_t *p_subpic )
361 /* Load the blending module */
362 if( !p_vout->p_blend && p_subpic && p_subpic->p_region )
364 p_vout->p_blend = vlc_object_create( p_vout, sizeof(filter_t) );
365 vlc_object_attach( p_vout->p_blend, p_vout );
366 p_vout->p_blend->fmt_out.video.i_width =
367 p_vout->p_blend->fmt_out.video.i_visible_width =
368 p_vout->render.i_width;
369 p_vout->p_blend->fmt_out.video.i_height =
370 p_vout->p_blend->fmt_out.video.i_visible_height =
371 p_vout->render.i_height;
372 p_vout->p_blend->fmt_out.video.i_x_offset =
373 p_vout->p_blend->fmt_out.video.i_y_offset = 0;
374 p_vout->p_blend->fmt_out.video.i_aspect =
375 p_vout->render.i_aspect;
376 p_vout->p_blend->fmt_out.video.i_chroma =
377 p_vout->output.i_chroma;
379 p_vout->p_blend->fmt_in.video = p_subpic->p_region->fmt;
381 p_vout->p_blend->p_module =
382 module_Need( p_vout->p_blend, "video blending", 0, 0 );
386 vlc_mutex_lock( &p_vout->subpicture_lock );
388 /* Check i_status again to make sure spudec hasn't destroyed the subpic */
389 while( p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE )
391 subpicture_region_t *p_region = p_subpic->p_region;
393 if( p_subpic->pf_render )
395 p_subpic->pf_render( p_vout, p_pic_dst, p_subpic );
397 else while( p_region && p_vout->p_blend &&
398 p_vout->p_blend->pf_video_blend )
400 int i_x_offset = p_region->i_x + p_subpic->i_x;
401 int i_y_offset = p_region->i_y + p_subpic->i_y;
403 if( p_subpic->i_flags & OSD_ALIGN_BOTTOM )
405 i_y_offset = p_vout->output.i_height - p_region->fmt.i_height -
408 else if ( !(p_subpic->i_flags & OSD_ALIGN_TOP) )
410 i_y_offset = p_vout->output.i_height / 2 -
411 p_region->fmt.i_height / 2;
414 if( p_subpic->i_flags & OSD_ALIGN_RIGHT )
416 i_x_offset = p_vout->output.i_width - p_region->fmt.i_width -
419 else if ( !(p_subpic->i_flags & OSD_ALIGN_LEFT) )
421 i_x_offset = p_vout->output.i_width / 2 -
422 p_region->fmt.i_width / 2;
425 if( p_subpic->b_absolute )
427 i_x_offset = p_region->i_x + p_subpic->i_x;
428 i_y_offset = p_region->i_y + p_subpic->i_y;
431 p_vout->p_blend->fmt_in.video = p_region->fmt;
433 /* Force cropping if requested */
434 if( p_vout->b_force_crop )
436 video_format_t *p_fmt = &p_vout->p_blend->fmt_in.video;
438 /* Find the intersection */
439 if( p_vout->i_crop_x + p_vout->i_crop_width <= i_x_offset ||
440 i_x_offset + (int)p_fmt->i_visible_width <
442 p_vout->i_crop_y + p_vout->i_crop_height <= i_y_offset ||
443 i_y_offset + (int)p_fmt->i_visible_height <
446 /* No intersection */
447 p_fmt->i_visible_width = p_fmt->i_visible_height = 0;
451 int i_x, i_y, i_x_end, i_y_end;
452 i_x = __MAX( p_vout->i_crop_x, i_x_offset );
453 i_y = __MAX( p_vout->i_crop_y, i_y_offset );
454 i_x_end = __MIN( p_vout->i_crop_x + p_vout->i_crop_width,
455 i_x_offset + (int)p_fmt->i_visible_width );
456 i_y_end = __MIN( p_vout->i_crop_y + p_vout->i_crop_height,
457 i_y_offset + (int)p_fmt->i_visible_height );
459 p_fmt->i_x_offset = i_x - i_x_offset;
460 p_fmt->i_y_offset = i_y - i_y_offset;
461 p_fmt->i_visible_width = i_x_end - i_x;
462 p_fmt->i_visible_height = i_y_end - i_y;
469 /* Force palette if requested */
470 if( p_vout->b_force_alpha && VLC_FOURCC('Y','U','V','P') ==
471 p_vout->p_blend->fmt_in.video.i_chroma )
473 p_vout->p_blend->fmt_in.video.p_palette->palette[0][3] =
475 p_vout->p_blend->fmt_in.video.p_palette->palette[1][3] =
477 p_vout->p_blend->fmt_in.video.p_palette->palette[2][3] =
479 p_vout->p_blend->fmt_in.video.p_palette->palette[3][3] =
483 p_vout->p_blend->pf_video_blend( p_vout->p_blend, p_pic_dst,
484 p_pic_src, &p_region->picture, i_x_offset, i_y_offset );
486 p_region = p_region->p_next;
489 p_subpic = p_subpic->p_next;
492 vlc_mutex_unlock( &p_vout->subpicture_lock );
495 /*****************************************************************************
496 * vout_SortSubPictures: find the subpictures to display
497 *****************************************************************************
498 * This function parses all subpictures and decides which ones need to be
499 * displayed. This operation does not need lock, since only READY_SUBPICTURE
500 * are handled. If no picture has been selected, display_date will depend on
502 * We also check for ephemer DVD subpictures (subpictures that have
503 * to be removed if a newer one is available), which makes it a lot
504 * more difficult to guess if a subpicture has to be rendered or not.
505 *****************************************************************************/
506 subpicture_t *vout_SortSubPictures( vout_thread_t *p_vout,
507 mtime_t display_date )
510 subpicture_t *p_subpic = NULL;
511 subpicture_t *p_ephemer = NULL;
512 mtime_t ephemer_date = 0;
514 /* We get an easily parsable chained list of subpictures which
515 * ends with NULL since p_subpic was initialized to NULL. */
516 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
518 if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE )
520 /* If it is a DVD subpicture, check its date */
521 if( p_vout->p_subpicture[i_index].i_type == MEMORY_SUBPICTURE )
523 if( !p_vout->p_subpicture[i_index].b_ephemer
524 && display_date > p_vout->p_subpicture[i_index].i_stop )
526 /* Too late, destroy the subpic */
527 vout_DestroySubPicture( p_vout,
528 &p_vout->p_subpicture[i_index] );
533 && display_date < p_vout->p_subpicture[i_index].i_start )
535 /* Too early, come back next monday */
539 /* If this is an ephemer subpic, see if it's the
540 * youngest we have */
541 if( p_vout->p_subpicture[i_index].b_ephemer )
543 if( p_ephemer == NULL )
545 p_ephemer = &p_vout->p_subpicture[i_index];
549 if( p_vout->p_subpicture[i_index].i_start
550 < p_ephemer->i_start )
552 /* Link the previous ephemer subpicture and
553 * replace it with the current one */
554 p_ephemer->p_next = p_subpic;
555 p_subpic = p_ephemer;
556 p_ephemer = &p_vout->p_subpicture[i_index];
558 /* If it's the 2nd youngest subpicture,
559 * register its date */
561 || ephemer_date > p_subpic->i_start )
563 ephemer_date = p_subpic->i_start;
570 p_vout->p_subpicture[i_index].p_next = p_subpic;
571 p_subpic = &p_vout->p_subpicture[i_index];
573 /* If it's the 2nd youngest subpicture, register its date */
574 if( !ephemer_date || ephemer_date > p_subpic->i_start )
576 ephemer_date = p_subpic->i_start;
579 /* If it's not a DVD subpicture, just register it */
582 p_vout->p_subpicture[i_index].p_next = p_subpic;
583 p_subpic = &p_vout->p_subpicture[i_index];
588 /* If we found an ephemer subpicture, check if it has to be
590 if( p_ephemer != NULL )
592 if( p_ephemer->i_start < ephemer_date )
594 /* Ephemer subpicture has lived too long */
595 vout_DestroySubPicture( p_vout, p_ephemer );
599 /* Ephemer subpicture can still live a bit */
600 p_ephemer->p_next = p_subpic;
608 /*****************************************************************************
609 * vout_RegisterOSDChannel: register an OSD channel
610 *****************************************************************************
611 * This function affects an ID to an OSD channel
612 *****************************************************************************/
613 int vout_RegisterOSDChannel( vout_thread_t *p_vout )
615 msg_Dbg( p_vout, "Registering OSD channel, ID: %i",
616 p_vout->i_channel_count + 1 );
617 return ++p_vout->i_channel_count;
620 /*****************************************************************************
621 * vout_ClearOSDChannel: clear an OSD channel
622 *****************************************************************************
623 * This function destroys the subpictures which belong to the OSD channel
624 * corresponding to i_channel_id.
625 *****************************************************************************/
626 void vout_ClearOSDChannel( vout_thread_t *p_vout, int i_channel )
628 int i_subpic; /* subpicture index */
629 subpicture_t * p_subpic = NULL; /* first free subpicture */
631 if( i_channel == DEFAULT_CHAN )
633 if( p_vout->p_default_channel != NULL )
635 vout_DestroySubPicture( p_vout, p_vout->p_default_channel );
637 p_vout->p_default_channel = NULL;
641 vlc_mutex_lock( &p_vout->subpicture_lock );
643 for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
645 p_subpic = &p_vout->p_subpicture[i_subpic];
646 if( p_subpic->i_status == FREE_SUBPICTURE
647 || ( p_subpic->i_status != RESERVED_SUBPICTURE
648 && p_subpic->i_status != READY_SUBPICTURE ) )
653 if( p_subpic->i_channel == i_channel )
655 while( p_subpic->p_region )
657 subpicture_region_t *p_region = p_subpic->p_region;
658 p_subpic->p_region = p_region->p_next;
659 spu_DestroyRegion( p_vout, p_region );
662 if( p_subpic->pf_destroy )
664 p_subpic->pf_destroy( p_subpic );
666 p_subpic->i_status = FREE_SUBPICTURE;
670 vlc_mutex_unlock( &p_vout->subpicture_lock );
673 /*****************************************************************************
674 * UpdateSPU: update subpicture settings
675 *****************************************************************************
676 * This function is called from CropCallback and at initialization time, to
677 * retrieve crop information from the input.
678 *****************************************************************************/
679 static void UpdateSPU( vout_thread_t *p_vout, vlc_object_t *p_object )
683 p_vout->b_force_alpha = VLC_FALSE;
684 p_vout->b_force_crop = VLC_FALSE;
686 if( var_Get( p_object, "highlight", &val ) || !val.b_bool ) return;
688 p_vout->b_force_crop = VLC_TRUE;
689 var_Get( p_object, "x-start", &val );
690 p_vout->i_crop_x = val.i_int;
691 var_Get( p_object, "y-start", &val );
692 p_vout->i_crop_y = val.i_int;
693 var_Get( p_object, "x-end", &val );
694 p_vout->i_crop_width = val.i_int - p_vout->i_crop_x;
695 var_Get( p_object, "y-end", &val );
696 p_vout->i_crop_height = val.i_int - p_vout->i_crop_y;
699 if( var_Get( p_object, "color", &val ) == VLC_SUCCESS )
702 for( i = 0; i < 4; i++ )
704 p_vout->pi_color[i] = ((uint8_t *)val.p_address)[i];
709 if( var_Get( p_object, "contrast", &val ) == VLC_SUCCESS )
712 for( i = 0; i < 4; i++ )
714 p_vout->pi_alpha[i] = ((uint8_t *)val.p_address)[i];
715 p_vout->pi_alpha[i] = p_vout->pi_alpha[i] == 0xf ?
716 0xff : p_vout->pi_alpha[i] << 4;
718 p_vout->b_force_alpha = VLC_TRUE;
721 msg_Dbg( p_vout, "crop: %i,%i,%i,%i, alpha: %i",
722 p_vout->i_crop_x, p_vout->i_crop_y,
723 p_vout->i_crop_width, p_vout->i_crop_height,
724 p_vout->b_force_alpha );
727 /*****************************************************************************
728 * CropCallback: called when the highlight properties are changed
729 *****************************************************************************
730 * This callback is called from the input thread when we need cropping
731 *****************************************************************************/
732 static int CropCallback( vlc_object_t *p_object, char const *psz_var,
733 vlc_value_t oldval, vlc_value_t newval, void *p_data )
735 UpdateSPU( (vout_thread_t *)p_data, p_object );
739 /*****************************************************************************
740 * Buffers allocation callbacks for the filters
741 *****************************************************************************/
742 static subpicture_t *spu_new_buffer( filter_t *p_filter )
744 vout_thread_t *p_vout = (vout_thread_t *)p_filter->p_owner;
747 p_spu = vout_CreateSubPicture( p_vout, !DEFAULT_CHAN, MEMORY_SUBPICTURE );
751 static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_spu )
753 vout_thread_t *p_vout = (vout_thread_t *)p_filter->p_owner;
754 vout_DestroySubPicture( p_vout, p_spu );