1 /*****************************************************************************
2 * vout_subpictures.c : subpicture management functions
3 *****************************************************************************
4 * Copyright (C) 2000-2007 the VideoLAN team
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
33 #include <vlc_common.h>
35 #include <vlc_block.h>
36 #include <vlc_filter.h>
38 #include "../libvlc.h"
40 /*****************************************************************************
42 *****************************************************************************/
43 static void UpdateSPU ( spu_t *, vlc_object_t * );
44 static int CropCallback( vlc_object_t *, char const *,
45 vlc_value_t, vlc_value_t, void * );
47 static int spu_vaControlDefault( spu_t *, int, va_list );
49 static subpicture_t *sub_new_buffer( filter_t * );
50 static void sub_del_buffer( filter_t *, subpicture_t * );
51 static subpicture_t *spu_new_buffer( filter_t * );
52 static void spu_del_buffer( filter_t *, subpicture_t * );
53 static picture_t *spu_new_video_buffer( filter_t * );
54 static void spu_del_video_buffer( filter_t *, picture_t * );
56 static int spu_ParseChain( spu_t * );
57 static int SubFilterCallback( vlc_object_t *, char const *,
58 vlc_value_t, vlc_value_t, void * );
60 static int sub_filter_allocation_init( filter_t *, void * );
61 static void sub_filter_allocation_clear( filter_t * );
62 struct filter_owner_sys_t
75 * Creates the subpicture unit
77 * \param p_this the parent object which creates the subpicture unit
79 spu_t *__spu_Create( vlc_object_t *p_this )
82 spu_t *p_spu = vlc_custom_create( p_this, sizeof( spu_t ),
83 VLC_OBJECT_GENERIC, "subpicture" );
85 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
87 p_spu->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
90 p_spu->p_blend = NULL;
92 p_spu->p_scale = NULL;
93 p_spu->pf_control = spu_vaControlDefault;
95 /* Register the default subpicture channel */
98 vlc_mutex_init( &p_spu->subpicture_lock );
100 vlc_object_attach( p_spu, p_this );
102 p_spu->p_chain = filter_chain_New( p_spu, "sub filter", false,
103 sub_filter_allocation_init,
104 sub_filter_allocation_clear,
110 * Initialise the subpicture unit
112 * \param p_spu the subpicture unit object
114 int spu_Init( spu_t *p_spu )
118 /* If the user requested a sub margin, we force the position. */
119 var_Create( p_spu, "sub-margin", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
120 var_Get( p_spu, "sub-margin", &val );
121 p_spu->i_margin = val.i_int;
123 var_Create( p_spu, "sub-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
124 var_AddCallback( p_spu, "sub-filter", SubFilterCallback, p_spu );
126 spu_ParseChain( p_spu );
131 int spu_ParseChain( spu_t *p_spu )
133 char *psz_parser = var_GetString( p_spu, "sub-filter" );
134 if( filter_chain_AppendFromString( p_spu->p_chain, psz_parser ) < 0 )
145 * Destroy the subpicture unit
147 * \param p_this the parent object which destroys the subpicture unit
149 void spu_Destroy( spu_t *p_spu )
153 /* Destroy all remaining subpictures */
154 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
156 if( p_spu->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
158 spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] );
164 if( p_spu->p_blend->p_module )
165 module_Unneed( p_spu->p_blend, p_spu->p_blend->p_module );
167 vlc_object_detach( p_spu->p_blend );
168 vlc_object_release( p_spu->p_blend );
173 if( p_spu->p_text->p_module )
174 module_Unneed( p_spu->p_text, p_spu->p_text->p_module );
176 vlc_object_detach( p_spu->p_text );
177 vlc_object_release( p_spu->p_text );
182 if( p_spu->p_scale->p_module )
183 module_Unneed( p_spu->p_scale, p_spu->p_scale->p_module );
185 vlc_object_detach( p_spu->p_scale );
186 vlc_object_release( p_spu->p_scale );
189 filter_chain_Delete( p_spu->p_chain );
191 vlc_mutex_destroy( &p_spu->subpicture_lock );
192 vlc_object_release( p_spu );
196 * Attach/Detach the SPU from any input
198 * \param p_this the object in which to destroy the subpicture unit
199 * \param b_attach to select attach or detach
201 void spu_Attach( spu_t *p_spu, vlc_object_t *p_this, bool b_attach )
203 vlc_object_t *p_input;
205 p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
206 if( !p_input ) return;
210 UpdateSPU( p_spu, VLC_OBJECT(p_input) );
211 var_AddCallback( p_input, "highlight", CropCallback, p_spu );
212 vlc_object_release( p_input );
216 /* Delete callback */
217 var_DelCallback( p_input, "highlight", CropCallback, p_spu );
218 vlc_object_release( p_input );
223 * Create a subpicture region
225 * \param p_this vlc_object_t
226 * \param p_fmt the format that this subpicture region should have
228 static void RegionPictureRelease( picture_t *p_pic )
230 free( p_pic->p_data_orig );
232 subpicture_region_t *__spu_CreateRegion( vlc_object_t *p_this,
233 video_format_t *p_fmt )
235 subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
236 if( !p_region ) return NULL;
238 memset( p_region, 0, sizeof(subpicture_region_t) );
239 p_region->i_alpha = 0xff;
240 p_region->p_next = NULL;
241 p_region->p_cache = NULL;
242 p_region->fmt = *p_fmt;
243 p_region->psz_text = NULL;
244 p_region->p_style = NULL;
246 if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') )
247 p_fmt->p_palette = p_region->fmt.p_palette =
248 malloc( sizeof(video_palette_t) );
249 else p_fmt->p_palette = p_region->fmt.p_palette = NULL;
251 p_region->picture.p_data_orig = NULL;
253 if( p_fmt->i_chroma == VLC_FOURCC('T','E','X','T') ) return p_region;
255 vout_AllocatePicture( p_this, &p_region->picture, p_fmt->i_chroma,
256 p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect );
258 if( !p_region->picture.i_planes )
261 free( p_fmt->p_palette );
265 p_region->picture.pf_release = RegionPictureRelease;
271 * Make a subpicture region from an existing picture_t
273 * \param p_this vlc_object_t
274 * \param p_fmt the format that this subpicture region should have
275 * \param p_pic a pointer to the picture creating the region (not freed)
277 subpicture_region_t *__spu_MakeRegion( vlc_object_t *p_this,
278 video_format_t *p_fmt,
281 subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
283 if( !p_region ) return NULL;
284 memset( p_region, 0, sizeof(subpicture_region_t) );
285 p_region->i_alpha = 0xff;
286 p_region->p_next = 0;
287 p_region->p_cache = 0;
288 p_region->fmt = *p_fmt;
289 p_region->psz_text = 0;
290 p_region->p_style = NULL;
292 if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') )
293 p_fmt->p_palette = p_region->fmt.p_palette =
294 malloc( sizeof(video_palette_t) );
295 else p_fmt->p_palette = p_region->fmt.p_palette = NULL;
297 memcpy( &p_region->picture, p_pic, sizeof(picture_t) );
298 p_region->picture.pf_release = RegionPictureRelease;
304 * Destroy a subpicture region
306 * \param p_this vlc_object_t
307 * \param p_region the subpicture region to destroy
309 void __spu_DestroyRegion( vlc_object_t *p_this, subpicture_region_t *p_region )
311 if( !p_region ) return;
312 if( p_region->picture.pf_release )
313 p_region->picture.pf_release( &p_region->picture );
314 free( p_region->fmt.p_palette );
315 if( p_region->p_cache ) __spu_DestroyRegion( p_this, p_region->p_cache );
317 free( p_region->psz_text );
318 free( p_region->psz_html );
319 //free( p_region->p_style ); FIXME --fenrir plugin does not allocate the memory for it. I think it might lead to segfault, video renderer can live longer than the decoder
324 * Display a subpicture
326 * Remove the reservation flag of a subpicture, which will cause it to be
328 * \param p_spu the subpicture unit object
329 * \param p_subpic the subpicture to display
331 void spu_DisplaySubpicture( spu_t *p_spu, subpicture_t *p_subpic )
333 /* Check if status is valid */
334 if( p_subpic->i_status != RESERVED_SUBPICTURE )
336 msg_Err( p_spu, "subpicture %p has invalid status #%d",
337 p_subpic, p_subpic->i_status );
340 /* Remove reservation flag */
341 p_subpic->i_status = READY_SUBPICTURE;
343 if( p_subpic->i_channel == DEFAULT_CHAN )
345 p_subpic->i_channel = 0xFFFF;
346 spu_Control( p_spu, SPU_CHANNEL_CLEAR, DEFAULT_CHAN );
347 p_subpic->i_channel = DEFAULT_CHAN;
352 * Allocate a subpicture in the spu heap.
354 * This function create a reserved subpicture in the spu heap.
355 * A null pointer is returned if the function fails. This method provides an
356 * already allocated zone of memory in the spu data fields. It needs locking
357 * since several pictures can be created by several producers threads.
358 * \param p_spu the subpicture unit in which to create the subpicture
359 * \return NULL on error, a reserved subpicture otherwise
361 subpicture_t *spu_CreateSubpicture( spu_t *p_spu )
363 int i_subpic; /* subpicture index */
364 subpicture_t * p_subpic = NULL; /* first free subpicture */
367 vlc_mutex_lock( &p_spu->subpicture_lock );
370 * Look for an empty place
373 for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
375 if( p_spu->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE )
377 /* Subpicture is empty and ready for allocation */
378 p_subpic = &p_spu->p_subpicture[i_subpic];
379 p_spu->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
384 /* If no free subpicture could be found */
385 if( p_subpic == NULL )
387 msg_Err( p_spu, "subpicture heap is full" );
388 vlc_mutex_unlock( &p_spu->subpicture_lock );
392 /* Copy subpicture information, set some default values */
393 memset( p_subpic, 0, sizeof(subpicture_t) );
394 p_subpic->i_status = RESERVED_SUBPICTURE;
395 p_subpic->b_absolute = true;
396 p_subpic->b_pausable = false;
397 p_subpic->b_fade = false;
398 p_subpic->i_alpha = 0xFF;
399 p_subpic->p_region = NULL;
400 p_subpic->pf_render = NULL;
401 p_subpic->pf_destroy = NULL;
402 p_subpic->p_sys = NULL;
403 vlc_mutex_unlock( &p_spu->subpicture_lock );
405 p_subpic->pf_create_region = __spu_CreateRegion;
406 p_subpic->pf_make_region = __spu_MakeRegion;
407 p_subpic->pf_destroy_region = __spu_DestroyRegion;
413 * Remove a subpicture from the heap
415 * This function frees a previously reserved subpicture.
416 * It is meant to be used when the construction of a picture aborted.
417 * This function does not need locking since reserved subpictures are ignored
420 void spu_DestroySubpicture( spu_t *p_spu, subpicture_t *p_subpic )
423 vlc_mutex_lock( &p_spu->subpicture_lock );
425 /* There can be race conditions so we need to check the status */
426 if( p_subpic->i_status == FREE_SUBPICTURE )
428 vlc_mutex_unlock( &p_spu->subpicture_lock );
432 /* Check if status is valid */
433 if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
434 && ( p_subpic->i_status != READY_SUBPICTURE ) )
436 msg_Err( p_spu, "subpicture %p has invalid status %d",
437 p_subpic, p_subpic->i_status );
440 while( p_subpic->p_region )
442 subpicture_region_t *p_region = p_subpic->p_region;
443 p_subpic->p_region = p_region->p_next;
444 spu_DestroyRegion( p_spu, p_region );
447 if( p_subpic->pf_destroy )
449 p_subpic->pf_destroy( p_subpic );
452 p_subpic->i_status = FREE_SUBPICTURE;
454 vlc_mutex_unlock( &p_spu->subpicture_lock );
457 /*****************************************************************************
458 * spu_RenderSubpictures: render a subpicture list
459 *****************************************************************************
460 * This function renders all sub picture units in the list.
461 *****************************************************************************/
462 void spu_RenderSubpictures( spu_t *p_spu, video_format_t *p_fmt,
463 picture_t *p_pic_dst, picture_t *p_pic_src,
464 subpicture_t *p_subpic,
465 int i_scale_width_orig, int i_scale_height_orig )
467 int i_source_video_width;
468 int i_source_video_height;
469 subpicture_t *p_subpic_v = p_subpic;
472 vlc_mutex_lock( &p_spu->subpicture_lock );
474 for( p_subpic_v = p_subpic;
475 p_subpic_v != NULL && p_subpic_v->i_status != FREE_SUBPICTURE;
476 p_subpic_v = p_subpic_v->p_next )
478 if( p_subpic_v->pf_pre_render )
480 p_subpic_v->pf_pre_render( p_fmt, p_spu, p_subpic_v, mdate() );
484 if( i_scale_width_orig <= 0 )
485 i_scale_width_orig = 1;
486 if( i_scale_height_orig <= 0 )
487 i_scale_height_orig = 1;
489 i_source_video_width = p_fmt->i_width * 1000 / i_scale_width_orig;
490 i_source_video_height = p_fmt->i_height * 1000 / i_scale_height_orig;
492 /* Check i_status again to make sure spudec hasn't destroyed the subpic */
493 while( ( p_subpic != NULL ) && ( p_subpic->i_status != FREE_SUBPICTURE ) )
495 subpicture_region_t *p_region;
496 int pi_scale_width[ SCALE_SIZE ];
497 int pi_scale_height[ SCALE_SIZE ];
498 int pi_subpic_x[ SCALE_SIZE ];
501 /* If the source video and subtitles stream agree on the size of
502 * the video then disregard all further references to the subtitle
505 if( ( i_source_video_height == p_subpic->i_original_picture_height ) &&
506 ( i_source_video_width == p_subpic->i_original_picture_width ) )
508 p_subpic->i_original_picture_height = 0;
509 p_subpic->i_original_picture_width = 0;
512 for( k = 0; k < SCALE_SIZE ; k++ )
513 pi_subpic_x[ k ] = p_subpic->i_x;
515 if( p_subpic->pf_update_regions )
517 if ( p_subpic->p_region ) {
518 spu_DestroyRegion( p_spu, p_subpic->p_region );
521 /* TODO do not reverse the scaling that was done before calling
522 * spu_RenderSubpictures, just pass it along (or do it inside
523 * spu_RenderSubpictures) */
524 video_format_t fmt_org = *p_fmt;
526 fmt_org.i_visible_width = i_source_video_width;
528 fmt_org.i_visible_height = i_source_video_height;
530 p_subpic->p_region = p_region = p_subpic->pf_update_regions( &fmt_org, p_spu, p_subpic, mdate() );
534 p_region = p_subpic->p_region;
537 /* Load the blending module */
538 if( !p_spu->p_blend && p_region )
540 static const char typename[] = "blend";
542 vlc_custom_create( p_spu, sizeof(filter_t), VLC_OBJECT_GENERIC,
544 vlc_object_attach( p_spu->p_blend, p_spu );
545 p_spu->p_blend->fmt_out.video.i_x_offset =
546 p_spu->p_blend->fmt_out.video.i_y_offset = 0;
547 p_spu->p_blend->fmt_out.video.i_aspect = p_fmt->i_aspect;
548 p_spu->p_blend->fmt_out.video.i_chroma = p_fmt->i_chroma;
550 /* The blend module will be loaded when needed with the real
552 memset( &p_spu->p_blend->fmt_in, 0, sizeof(p_spu->p_blend->fmt_in) );
553 p_spu->p_blend->p_module = NULL;
556 /* Load the text rendering module; it is possible there is a
557 * text region somewhere in the subpicture other than the first
558 * element in the region list, so just load it anyway as we'll
559 * probably want it sooner or later. */
560 if( !p_spu->p_text && p_region )
562 static const char typename[] = "spu text";
563 char *psz_modulename = NULL;
566 vlc_custom_create( p_spu, sizeof(filter_t), VLC_OBJECT_GENERIC,
568 vlc_object_attach( p_spu->p_text, p_spu );
570 p_spu->p_text->fmt_out.video.i_width =
571 p_spu->p_text->fmt_out.video.i_visible_width =
573 p_spu->p_text->fmt_out.video.i_height =
574 p_spu->p_text->fmt_out.video.i_visible_height =
577 p_spu->p_text->pf_sub_buffer_new = spu_new_buffer;
578 p_spu->p_text->pf_sub_buffer_del = spu_del_buffer;
580 psz_modulename = var_CreateGetString( p_spu, "text-renderer" );
581 if( psz_modulename && *psz_modulename )
583 p_spu->p_text->p_module =
584 module_Need( p_spu->p_text, "text renderer",
585 psz_modulename, true );
587 if( !p_spu->p_text->p_module )
589 p_spu->p_text->p_module =
590 module_Need( p_spu->p_text, "text renderer", 0, 0 );
592 free( psz_modulename );
597 subpicture_region_t *p_text_region = p_subpic->p_region;
599 /* Only overwrite the size fields if the region is still in
600 * pre-rendered TEXT format. We have to traverse the subregion
601 * list because if more than one subregion is present, the text
602 * region isn't guarentteed to be the first in the list, and
603 * only text regions use this flag. All of this effort assists
604 * with the rescaling of text that has been rendered at native
605 * resolution, rather than video resolution.
607 while( p_text_region &&
608 ( p_text_region->fmt.i_chroma != VLC_FOURCC('T','E','X','T') ) )
610 p_text_region = p_text_region->p_next;
614 ( ( p_text_region->i_align & SUBPICTURE_RENDERED ) == 0 ) )
616 if( (p_subpic->i_original_picture_height > 0) &&
617 (p_subpic->i_original_picture_width > 0) )
619 p_spu->p_text->fmt_out.video.i_width =
620 p_spu->p_text->fmt_out.video.i_visible_width =
621 p_subpic->i_original_picture_width;
622 p_spu->p_text->fmt_out.video.i_height =
623 p_spu->p_text->fmt_out.video.i_visible_height =
624 p_subpic->i_original_picture_height;
628 p_spu->p_text->fmt_out.video.i_width =
629 p_spu->p_text->fmt_out.video.i_visible_width =
631 p_spu->p_text->fmt_out.video.i_height =
632 p_spu->p_text->fmt_out.video.i_visible_height =
638 pi_scale_width[ SCALE_DEFAULT ] = i_scale_width_orig;
639 pi_scale_height[ SCALE_DEFAULT ] = i_scale_height_orig;
643 pi_scale_width[ SCALE_TEXT ] = ( p_fmt->i_width * 1000 ) /
644 p_spu->p_text->fmt_out.video.i_width;
645 pi_scale_height[ SCALE_TEXT ] = ( p_fmt->i_height * 1000 ) /
646 p_spu->p_text->fmt_out.video.i_height;
648 /* If we have an explicit size plane to render to, then turn off
649 * the fontsize rescaling.
651 if( (p_subpic->i_original_picture_height > 0) &&
652 (p_subpic->i_original_picture_width > 0) )
654 i_scale_width_orig = 1000;
655 i_scale_height_orig = 1000;
658 for( k = 0; k < SCALE_SIZE ; k++ )
660 /* Case of both width and height being specified has been dealt
661 * with above by instead rendering to an output pane of the
662 * explicit dimensions specified - we don't need to scale it.
664 if( (p_subpic->i_original_picture_height > 0) &&
665 (p_subpic->i_original_picture_width <= 0) )
667 pi_scale_height[ k ] = pi_scale_height[ k ] * i_source_video_height /
668 p_subpic->i_original_picture_height;
669 pi_scale_width[ k ] = pi_scale_width[ k ] * i_source_video_height /
670 p_subpic->i_original_picture_height;
674 /* Set default subpicture aspect ratio */
675 if( p_region && p_region->fmt.i_aspect &&
676 ( !p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den ) )
678 p_region->fmt.i_sar_den = p_region->fmt.i_aspect;
679 p_region->fmt.i_sar_num = VOUT_ASPECT_FACTOR;
682 ( !p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den ) )
684 p_region->fmt.i_sar_den = p_fmt->i_sar_den;
685 p_region->fmt.i_sar_num = p_fmt->i_sar_num;
688 /* Take care of the aspect ratio */
690 ( ( p_region->fmt.i_sar_num * p_fmt->i_sar_den ) !=
691 ( p_region->fmt.i_sar_den * p_fmt->i_sar_num ) ) )
693 for( k = 0; k < SCALE_SIZE ; k++ )
695 pi_scale_width[ k ] = pi_scale_width[ k ] *
696 (int64_t)p_region->fmt.i_sar_num * p_fmt->i_sar_den /
697 p_region->fmt.i_sar_den / p_fmt->i_sar_num;
698 pi_subpic_x[ k ] = p_subpic->i_x * pi_scale_width[ k ] / 1000;
702 /* Load the scaling module */
703 if( !p_spu->p_scale &&
704 ((((pi_scale_width[ SCALE_TEXT ] > 0) || (pi_scale_height[ SCALE_TEXT ] > 0)) &&
705 ((pi_scale_width[ SCALE_TEXT ] != 1000) || (pi_scale_height[ SCALE_TEXT ] != 1000))) ||
706 (((pi_scale_width[ SCALE_DEFAULT ] > 0) || (pi_scale_height[ SCALE_DEFAULT ] > 0)) &&
707 ((pi_scale_width[ SCALE_DEFAULT ] != 1000) || (pi_scale_height[ SCALE_DEFAULT ] != 1000)))) )
709 static const char typename[] = "scale";
711 vlc_custom_create( p_spu, sizeof(filter_t), VLC_OBJECT_GENERIC,
713 vlc_object_attach( p_spu->p_scale, p_spu );
714 p_spu->p_scale->fmt_out.video.i_chroma =
715 p_spu->p_scale->fmt_in.video.i_chroma =
716 VLC_FOURCC('Y','U','V','P');
717 /* FIXME: We'll also be using it for YUVA and RGBA blending ... */
719 p_spu->p_scale->fmt_in.video.i_width =
720 p_spu->p_scale->fmt_in.video.i_height = 32;
721 p_spu->p_scale->fmt_out.video.i_width =
722 p_spu->p_scale->fmt_out.video.i_height = 16;
724 p_spu->p_scale->pf_vout_buffer_new = spu_new_video_buffer;
725 p_spu->p_scale->pf_vout_buffer_del = spu_del_video_buffer;
726 p_spu->p_scale->p_module =
727 module_Need( p_spu->p_scale, "video filter2", 0, 0 );
732 video_format_t orig_fmt = p_region->fmt;
733 bool b_rerender_text = false;
734 int i_fade_alpha = 255;
737 int i_scale_idx = SCALE_DEFAULT;
738 int i_inv_scale_x = 1000;
739 int i_inv_scale_y = 1000;
741 if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') )
743 if( p_spu->p_text && p_spu->p_text->p_module )
747 /* Setup 3 variables which can be used to render
748 * time-dependent text (and effects). The first indicates
749 * the total amount of time the text will be on screen,
750 * the second the amount of time it has already been on
751 * screen (can be a negative value as text is layed out
752 * before it is rendered) and the third is a feedback
753 * variable from the renderer - if the renderer sets it
754 * then this particular text is time-dependent, eg. the
755 * visual progress bar inside the text in karaoke and the
756 * text needs to be rendered multiple times in order for
757 * the effect to work - we therefore need to return the
758 * region to its original state at the end of the loop,
759 * instead of leaving it in YUVA or YUVP.
760 * Any renderer which is unaware of how to render
761 * time-dependent text can happily ignore the variables
762 * and render the text the same as usual - it should at
763 * least show up on screen, but the effect won't change
764 * the text over time.
767 var_Create( p_spu->p_text, "spu-duration", VLC_VAR_TIME );
768 val.i_time = p_subpic->i_stop - p_subpic->i_start;
769 var_Set( p_spu->p_text, "spu-duration", val );
771 var_Create( p_spu->p_text, "spu-elapsed", VLC_VAR_TIME );
772 val.i_time = mdate() - p_subpic->i_start;
773 var_Set( p_spu->p_text, "spu-elapsed", val );
775 var_Create( p_spu->p_text, "text-rerender", VLC_VAR_BOOL );
776 var_SetBool( p_spu->p_text, "text-rerender", false );
778 var_Create( p_spu->p_text, "scale", VLC_VAR_INTEGER );
779 var_SetInteger( p_spu->p_text, "scale",
780 __MIN(i_scale_width_orig, i_scale_height_orig) );
782 if( p_spu->p_text->pf_render_html && p_region->psz_html )
784 p_spu->p_text->pf_render_html( p_spu->p_text,
785 p_region, p_region );
787 else if( p_spu->p_text->pf_render_text )
789 p_spu->p_text->pf_render_text( p_spu->p_text,
790 p_region, p_region );
792 b_rerender_text = var_GetBool( p_spu->p_text, "text-rerender" );
794 var_Destroy( p_spu->p_text, "spu-duration" );
795 var_Destroy( p_spu->p_text, "spu-elapsed" );
796 var_Destroy( p_spu->p_text, "text-rerender" );
797 var_Destroy( p_spu->p_text, "scale" );
799 p_region->i_align |= SUBPICTURE_RENDERED;
802 if( p_region->i_align & SUBPICTURE_RENDERED )
804 i_scale_idx = SCALE_TEXT;
805 i_inv_scale_x = i_scale_width_orig;
806 i_inv_scale_y = i_scale_height_orig;
809 i_x_offset = (p_region->i_x + pi_subpic_x[ i_scale_idx ]) * i_inv_scale_x / 1000;
810 i_y_offset = (p_region->i_y + p_subpic->i_y) * i_inv_scale_y / 1000;
812 /* Force palette if requested */
813 if( p_spu->b_force_palette &&
814 ( VLC_FOURCC('Y','U','V','P') == p_region->fmt.i_chroma ) )
816 memcpy( p_region->fmt.p_palette->palette,
817 p_spu->palette, 16 );
820 /* Scale SPU if necessary */
821 if( p_region->p_cache &&
822 ( p_region->fmt.i_chroma != VLC_FOURCC('T','E','X','T') ) )
824 if( pi_scale_width[ i_scale_idx ] * p_region->fmt.i_width / 1000 !=
825 p_region->p_cache->fmt.i_width ||
826 pi_scale_height[ i_scale_idx ] * p_region->fmt.i_height / 1000 !=
827 p_region->p_cache->fmt.i_height )
829 p_subpic->pf_destroy_region( VLC_OBJECT(p_spu),
831 p_region->p_cache = 0;
835 if( ( ( pi_scale_width[ i_scale_idx ] != 1000 ) ||
836 ( pi_scale_height[ i_scale_idx ] != 1000 ) ) &&
837 ( ( pi_scale_width[ i_scale_idx ] > 0 ) ||
838 ( pi_scale_height[ i_scale_idx ] > 0 ) ) &&
839 p_spu->p_scale && !p_region->p_cache &&
840 ( p_region->fmt.i_chroma != VLC_FOURCC('T','E','X','T') ) )
844 p_spu->p_scale->fmt_in.video = p_region->fmt;
845 p_spu->p_scale->fmt_out.video = p_region->fmt;
848 p_subpic->pf_create_region( VLC_OBJECT(p_spu),
849 &p_spu->p_scale->fmt_out.video );
850 if( p_spu->p_scale->fmt_out.video.p_palette )
851 *p_spu->p_scale->fmt_out.video.p_palette =
852 *p_region->fmt.p_palette;
853 p_region->p_cache->p_next = p_region->p_next;
855 vout_CopyPicture( p_spu, &p_region->p_cache->picture,
856 &p_region->picture );
858 p_spu->p_scale->fmt_out.video.i_width =
859 p_region->fmt.i_width * pi_scale_width[ i_scale_idx ] / 1000;
860 p_spu->p_scale->fmt_out.video.i_visible_width =
861 p_region->fmt.i_visible_width * pi_scale_width[ i_scale_idx ] / 1000;
862 p_spu->p_scale->fmt_out.video.i_height =
863 p_region->fmt.i_height * pi_scale_height[ i_scale_idx ] / 1000;
864 p_spu->p_scale->fmt_out.video.i_visible_height =
865 p_region->fmt.i_visible_height * pi_scale_height[ i_scale_idx ] / 1000;
866 p_region->p_cache->fmt = p_spu->p_scale->fmt_out.video;
867 p_region->p_cache->i_x = p_region->i_x * pi_scale_width[ i_scale_idx ] / 1000;
868 p_region->p_cache->i_y = p_region->i_y * pi_scale_height[ i_scale_idx ] / 1000;
869 p_region->p_cache->i_align = p_region->i_align;
870 p_region->p_cache->i_alpha = p_region->i_alpha;
872 p_pic = p_spu->p_scale->pf_video_filter(
873 p_spu->p_scale, &p_region->p_cache->picture );
876 picture_t p_pic_tmp = p_region->p_cache->picture;
877 p_region->p_cache->picture = *p_pic;
883 if( ( ( pi_scale_width[ i_scale_idx ] != 1000 ) ||
884 ( pi_scale_height[ i_scale_idx ] != 1000 ) ) &&
885 ( ( pi_scale_width[ i_scale_idx ] > 0 ) ||
886 ( pi_scale_height[ i_scale_idx ] > 0 ) ) &&
887 p_spu->p_scale && p_region->p_cache &&
888 ( p_region->fmt.i_chroma != VLC_FOURCC('T','E','X','T') ) )
890 p_region = p_region->p_cache;
893 if( p_region->i_align & SUBPICTURE_ALIGN_BOTTOM )
895 i_y_offset = p_fmt->i_height - p_region->fmt.i_height -
896 (p_subpic->i_y + p_region->i_y) * i_inv_scale_y / 1000;
898 else if ( !(p_region->i_align & SUBPICTURE_ALIGN_TOP) )
900 i_y_offset = p_fmt->i_height / 2 - p_region->fmt.i_height / 2;
903 if( p_region->i_align & SUBPICTURE_ALIGN_RIGHT )
905 i_x_offset = p_fmt->i_width - p_region->fmt.i_width -
906 (pi_subpic_x[ i_scale_idx ] + p_region->i_x)
907 * i_inv_scale_x / 1000;
909 else if ( !(p_region->i_align & SUBPICTURE_ALIGN_LEFT) )
911 i_x_offset = p_fmt->i_width / 2 - p_region->fmt.i_width / 2;
914 if( p_subpic->b_absolute )
916 i_x_offset = (p_region->i_x +
917 pi_subpic_x[ i_scale_idx ] *
918 pi_scale_width[ i_scale_idx ] / 1000)
919 * i_inv_scale_x / 1000;
920 i_y_offset = (p_region->i_y +
921 p_subpic->i_y * pi_scale_height[ i_scale_idx ] / 1000)
922 * i_inv_scale_y / 1000;
926 i_x_offset = __MAX( i_x_offset, 0 );
927 i_y_offset = __MAX( i_y_offset, 0 );
929 if( ( p_spu->i_margin != 0 ) &&
930 ( p_spu->b_force_crop == false ) )
933 int i_low = (i_y_offset - p_spu->i_margin) * i_inv_scale_y / 1000;
934 int i_high = i_low + p_region->fmt.i_height;
936 /* crop extra margin to keep within bounds */
939 if( i_high > (int)p_fmt->i_height )
940 i_diff = i_high - p_fmt->i_height;
941 i_y_offset -= ( p_spu->i_margin * i_inv_scale_y / 1000 + i_diff );
944 if( p_subpic->b_fade )
946 mtime_t i_fade_start = ( p_subpic->i_stop +
947 p_subpic->i_start ) / 2;
948 mtime_t i_now = mdate();
949 if( i_now >= i_fade_start && p_subpic->i_stop > i_fade_start )
951 i_fade_alpha = 255 * ( p_subpic->i_stop - i_now ) /
952 ( p_subpic->i_stop - i_fade_start );
956 if( p_region->fmt.i_chroma != VLC_FOURCC('T','E','X','T') )
958 if( p_spu->p_blend->fmt_in.video.i_chroma != p_region->fmt.i_chroma )
960 /* The chroma is not the same, we need to reload the blend module
961 * XXX to match the old behaviour just test !p_spu->p_blend->fmt_in.video.i_chroma */
962 if( p_spu->p_blend->p_module )
963 module_Unneed( p_spu->p_blend, p_spu->p_blend->p_module );
965 p_spu->p_blend->fmt_in.video = p_region->fmt;
966 p_spu->p_blend->p_module = module_Need( p_spu->p_blend, "video blending", 0, 0 );
970 p_spu->p_blend->fmt_in.video = p_region->fmt;
973 /* Force cropping if requested */
974 if( p_spu->b_force_crop )
976 video_format_t *p_fmt = &p_spu->p_blend->fmt_in.video;
977 int i_crop_x = p_spu->i_crop_x * pi_scale_width[ i_scale_idx ] / 1000
978 * i_inv_scale_x / 1000;
979 int i_crop_y = p_spu->i_crop_y * pi_scale_height[ i_scale_idx ] / 1000
980 * i_inv_scale_y / 1000;
981 int i_crop_width = p_spu->i_crop_width * pi_scale_width[ i_scale_idx ] / 1000
982 * i_inv_scale_x / 1000;
983 int i_crop_height = p_spu->i_crop_height * pi_scale_height[ i_scale_idx ] / 1000
984 * i_inv_scale_y / 1000;
986 /* Find the intersection */
987 if( i_crop_x + i_crop_width <= i_x_offset ||
988 i_x_offset + (int)p_fmt->i_visible_width < i_crop_x ||
989 i_crop_y + i_crop_height <= i_y_offset ||
990 i_y_offset + (int)p_fmt->i_visible_height < i_crop_y )
992 /* No intersection */
993 p_fmt->i_visible_width = p_fmt->i_visible_height = 0;
997 int i_x, i_y, i_x_end, i_y_end;
998 i_x = __MAX( i_crop_x, i_x_offset );
999 i_y = __MAX( i_crop_y, i_y_offset );
1000 i_x_end = __MIN( i_crop_x + i_crop_width,
1001 i_x_offset + (int)p_fmt->i_visible_width );
1002 i_y_end = __MIN( i_crop_y + i_crop_height,
1003 i_y_offset + (int)p_fmt->i_visible_height );
1005 p_fmt->i_x_offset = i_x - i_x_offset;
1006 p_fmt->i_y_offset = i_y - i_y_offset;
1007 p_fmt->i_visible_width = i_x_end - i_x;
1008 p_fmt->i_visible_height = i_y_end - i_y;
1015 i_x_offset = __MAX( i_x_offset, 0 );
1016 i_y_offset = __MAX( i_y_offset, 0 );
1018 /* Update the output picture size */
1019 p_spu->p_blend->fmt_out.video.i_width =
1020 p_spu->p_blend->fmt_out.video.i_visible_width =
1022 p_spu->p_blend->fmt_out.video.i_height =
1023 p_spu->p_blend->fmt_out.video.i_visible_height =
1026 if( p_spu->p_blend->p_module )
1028 p_spu->p_blend->pf_video_blend( p_spu->p_blend, p_pic_dst,
1029 p_pic_src, &p_region->picture, i_x_offset, i_y_offset,
1030 i_fade_alpha * p_subpic->i_alpha * p_region->i_alpha / 65025 );
1034 msg_Err( p_spu, "blending %4.4s to %4.4s failed",
1035 (char *)&p_spu->p_blend->fmt_out.video.i_chroma,
1036 (char *)&p_spu->p_blend->fmt_out.video.i_chroma );
1040 if( b_rerender_text )
1042 /* Some forms of subtitles need to be re-rendered more than
1043 * once, eg. karaoke. We therefore restore the region to its
1044 * pre-rendered state, so the next time through everything is
1047 p_region->picture.pf_release( &p_region->picture );
1048 memset( &p_region->picture, 0, sizeof( picture_t ) );
1049 p_region->fmt = orig_fmt;
1050 p_region->i_align &= ~SUBPICTURE_RENDERED;
1052 p_region = p_region->p_next;
1055 p_subpic = p_subpic->p_next;
1058 vlc_mutex_unlock( &p_spu->subpicture_lock );
1061 /*****************************************************************************
1062 * spu_SortSubpictures: find the subpictures to display
1063 *****************************************************************************
1064 * This function parses all subpictures and decides which ones need to be
1065 * displayed. This operation does not need lock, since only READY_SUBPICTURE
1066 * are handled. If no picture has been selected, display_date will depend on
1068 * We also check for ephemer DVD subpictures (subpictures that have
1069 * to be removed if a newer one is available), which makes it a lot
1070 * more difficult to guess if a subpicture has to be rendered or not.
1071 *****************************************************************************/
1072 subpicture_t *spu_SortSubpictures( spu_t *p_spu, mtime_t display_date,
1075 int i_index, i_channel;
1076 subpicture_t *p_subpic = NULL;
1077 subpicture_t *p_ephemer;
1078 mtime_t ephemer_date;
1080 /* Run subpicture filters */
1081 filter_chain_SubFilter( p_spu->p_chain, display_date );
1083 /* We get an easily parsable chained list of subpictures which
1084 * ends with NULL since p_subpic was initialized to NULL. */
1085 for( i_channel = 0; i_channel < p_spu->i_channel; i_channel++ )
1090 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
1092 if( p_spu->p_subpicture[i_index].i_channel != i_channel ||
1093 p_spu->p_subpicture[i_index].i_status != READY_SUBPICTURE )
1098 display_date < p_spu->p_subpicture[i_index].i_start )
1100 /* Too early, come back next monday */
1104 if( p_spu->p_subpicture[i_index].i_start > ephemer_date )
1105 ephemer_date = p_spu->p_subpicture[i_index].i_start;
1107 if( display_date > p_spu->p_subpicture[i_index].i_stop &&
1108 ( !p_spu->p_subpicture[i_index].b_ephemer ||
1109 p_spu->p_subpicture[i_index].i_stop >
1110 p_spu->p_subpicture[i_index].i_start ) &&
1111 !( p_spu->p_subpicture[i_index].b_pausable &&
1114 /* Too late, destroy the subpic */
1115 spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] );
1119 /* If this is an ephemer subpic, add it to our list */
1120 if( p_spu->p_subpicture[i_index].b_ephemer )
1122 p_spu->p_subpicture[i_index].p_next = p_ephemer;
1123 p_ephemer = &p_spu->p_subpicture[i_index];
1128 p_spu->p_subpicture[i_index].p_next = p_subpic;
1129 p_subpic = &p_spu->p_subpicture[i_index];
1132 /* If we found ephemer subpictures, check if they have to be
1133 * displayed or destroyed */
1134 while( p_ephemer != NULL )
1136 subpicture_t *p_tmp = p_ephemer;
1137 p_ephemer = p_ephemer->p_next;
1139 if( p_tmp->i_start < ephemer_date )
1141 /* Ephemer subpicture has lived too long */
1142 spu_DestroySubpicture( p_spu, p_tmp );
1146 /* Ephemer subpicture can still live a bit */
1147 p_tmp->p_next = p_subpic;
1156 /*****************************************************************************
1157 * SpuClearChannel: clear an spu channel
1158 *****************************************************************************
1159 * This function destroys the subpictures which belong to the spu channel
1160 * corresponding to i_channel_id.
1161 *****************************************************************************/
1162 static void SpuClearChannel( spu_t *p_spu, int i_channel, bool b_locked )
1164 int i_subpic; /* subpicture index */
1165 subpicture_t *p_subpic = NULL; /* first free subpicture */
1168 vlc_mutex_lock( &p_spu->subpicture_lock );
1170 for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
1172 p_subpic = &p_spu->p_subpicture[i_subpic];
1173 if( p_subpic->i_status == FREE_SUBPICTURE
1174 || ( p_subpic->i_status != RESERVED_SUBPICTURE
1175 && p_subpic->i_status != READY_SUBPICTURE ) )
1180 if( p_subpic->i_channel == i_channel )
1182 while( p_subpic->p_region )
1184 subpicture_region_t *p_region = p_subpic->p_region;
1185 p_subpic->p_region = p_region->p_next;
1186 spu_DestroyRegion( p_spu, p_region );
1189 if( p_subpic->pf_destroy ) p_subpic->pf_destroy( p_subpic );
1190 p_subpic->i_status = FREE_SUBPICTURE;
1195 vlc_mutex_unlock( &p_spu->subpicture_lock );
1198 /*****************************************************************************
1199 * spu_ControlDefault: default methods for the subpicture unit control.
1200 *****************************************************************************/
1201 static int spu_vaControlDefault( spu_t *p_spu, int i_query, va_list args )
1207 case SPU_CHANNEL_REGISTER:
1208 pi = (int *)va_arg( args, int * );
1209 if( pi ) *pi = p_spu->i_channel++;
1212 case SPU_CHANNEL_CLEAR:
1213 i = (int)va_arg( args, int );
1214 SpuClearChannel( p_spu, i, false );
1218 msg_Dbg( p_spu, "control query not supported" );
1219 return VLC_EGENERIC;
1225 /*****************************************************************************
1226 * Object variables callbacks
1227 *****************************************************************************/
1229 /*****************************************************************************
1230 * UpdateSPU: update subpicture settings
1231 *****************************************************************************
1232 * This function is called from CropCallback and at initialization time, to
1233 * retrieve crop information from the input.
1234 *****************************************************************************/
1235 static void UpdateSPU( spu_t *p_spu, vlc_object_t *p_object )
1239 p_spu->b_force_palette = false;
1240 p_spu->b_force_crop = false;
1242 if( var_Get( p_object, "highlight", &val ) || !val.b_bool ) return;
1244 p_spu->b_force_crop = true;
1245 var_Get( p_object, "x-start", &val );
1246 p_spu->i_crop_x = val.i_int;
1247 var_Get( p_object, "y-start", &val );
1248 p_spu->i_crop_y = val.i_int;
1249 var_Get( p_object, "x-end", &val );
1250 p_spu->i_crop_width = val.i_int - p_spu->i_crop_x;
1251 var_Get( p_object, "y-end", &val );
1252 p_spu->i_crop_height = val.i_int - p_spu->i_crop_y;
1254 if( var_Get( p_object, "menu-palette", &val ) == VLC_SUCCESS )
1256 memcpy( p_spu->palette, val.p_address, 16 );
1257 p_spu->b_force_palette = true;
1260 msg_Dbg( p_object, "crop: %i,%i,%i,%i, palette forced: %i",
1261 p_spu->i_crop_x, p_spu->i_crop_y,
1262 p_spu->i_crop_width, p_spu->i_crop_height,
1263 p_spu->b_force_palette );
1266 /*****************************************************************************
1267 * CropCallback: called when the highlight properties are changed
1268 *****************************************************************************
1269 * This callback is called from the input thread when we need cropping
1270 *****************************************************************************/
1271 static int CropCallback( vlc_object_t *p_object, char const *psz_var,
1272 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1274 (void)psz_var; (void)oldval; (void)newval;
1275 UpdateSPU( (spu_t *)p_data, p_object );
1279 /*****************************************************************************
1280 * Buffers allocation callbacks for the filters
1281 *****************************************************************************/
1282 static subpicture_t *sub_new_buffer( filter_t *p_filter )
1284 filter_owner_sys_t *p_sys = p_filter->p_owner;
1285 subpicture_t *p_subpicture = spu_CreateSubpicture( p_sys->p_spu );
1286 if( p_subpicture ) p_subpicture->i_channel = p_sys->i_channel;
1287 return p_subpicture;
1290 static void sub_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1292 filter_owner_sys_t *p_sys = p_filter->p_owner;
1293 spu_DestroySubpicture( p_sys->p_spu, p_subpic );
1296 static subpicture_t *spu_new_buffer( filter_t *p_filter )
1299 subpicture_t *p_subpic = (subpicture_t *)malloc(sizeof(subpicture_t));
1300 if( !p_subpic ) return NULL;
1301 memset( p_subpic, 0, sizeof(subpicture_t) );
1302 p_subpic->b_absolute = true;
1304 p_subpic->pf_create_region = __spu_CreateRegion;
1305 p_subpic->pf_make_region = __spu_MakeRegion;
1306 p_subpic->pf_destroy_region = __spu_DestroyRegion;
1311 static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
1313 while( p_subpic->p_region )
1315 subpicture_region_t *p_region = p_subpic->p_region;
1316 p_subpic->p_region = p_region->p_next;
1317 p_subpic->pf_destroy_region( VLC_OBJECT(p_filter), p_region );
1323 static picture_t *spu_new_video_buffer( filter_t *p_filter )
1325 picture_t *p_picture = malloc( sizeof(picture_t) );
1326 if( !p_picture ) return NULL;
1327 if( vout_AllocatePicture( p_filter, p_picture,
1328 p_filter->fmt_out.video.i_chroma,
1329 p_filter->fmt_out.video.i_width,
1330 p_filter->fmt_out.video.i_height,
1331 p_filter->fmt_out.video.i_aspect )
1338 p_picture->pf_release = RegionPictureRelease;
1343 static void spu_del_video_buffer( filter_t *p_filter, picture_t *p_pic )
1348 free( p_pic->p_data_orig );
1353 static int SubFilterCallback( vlc_object_t *p_object, char const *psz_var,
1354 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1356 VLC_UNUSED(p_object); VLC_UNUSED(oldval);
1357 VLC_UNUSED(newval); VLC_UNUSED(psz_var);
1359 spu_t *p_spu = (spu_t *)p_data;
1360 vlc_mutex_lock( &p_spu->subpicture_lock );
1361 filter_chain_Reset( p_spu->p_chain, NULL, NULL );
1362 spu_ParseChain( p_spu );
1363 vlc_mutex_unlock( &p_spu->subpicture_lock );
1367 static int sub_filter_allocation_init( filter_t *p_filter, void *p_data )
1369 spu_t *p_spu = (spu_t *)p_data;
1371 p_filter->pf_sub_buffer_new = sub_new_buffer;
1372 p_filter->pf_sub_buffer_del = sub_del_buffer;
1374 filter_owner_sys_t *p_sys = malloc( sizeof(filter_owner_sys_t) );
1375 if( !p_sys ) return VLC_EGENERIC;
1377 p_filter->p_owner = p_sys;
1378 spu_Control( p_spu, SPU_CHANNEL_REGISTER, &p_sys->i_channel );
1379 p_sys->p_spu = p_spu;
1384 static void sub_filter_allocation_clear( filter_t *p_filter )
1386 filter_owner_sys_t *p_sys = p_filter->p_owner;
1387 SpuClearChannel( p_sys->p_spu, p_sys->i_channel, true );
1388 free( p_filter->p_owner );