/*****************************************************************************
* vout_subpictures.c : subpicture management functions
*****************************************************************************
- * Copyright (C) 2000-2005 the VideoLAN team
+ * Copyright (C) 2000-2007 the VideoLAN team
* $Id$
*
* Authors: Vincent Seguin <seguin@via.ecp.fr>
static picture_t *spu_new_video_buffer( filter_t * );
static void spu_del_video_buffer( filter_t *, picture_t * );
+static int spu_ParseChain( spu_t * );
+static void spu_DeleteChain( spu_t * );
+static int SubFilterCallback( vlc_object_t *, char const *,
+ vlc_value_t, vlc_value_t, void * );
+
struct filter_owner_sys_t
{
spu_t *p_spu;
*/
int spu_Init( spu_t *p_spu )
{
- char *psz_parser;
vlc_value_t val;
/* If the user requested a sub margin, we force the position. */
p_spu->i_margin = val.i_int;
var_Create( p_spu, "sub-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
- var_Get( p_spu, "sub-filter", &val );
+ var_AddCallback( p_spu, "sub-filter", SubFilterCallback, p_spu );
+ spu_ParseChain( p_spu );
+
+ return VLC_EGENERIC;
+}
+
+int spu_ParseChain( spu_t *p_spu )
+{
+ char *psz_parser;
+ vlc_value_t val;
+ var_Get( p_spu, "sub-filter", &val );
psz_parser = val.psz_string;
while( psz_parser && *psz_parser )
{
- config_chain_t *p_cfg; /* Do we ever need to free this ? */
+ config_chain_t *p_cfg;
char *psz_name;
psz_parser = config_ChainCreate( &psz_name, &p_cfg, psz_parser );
vlc_object_attach( p_spu->pp_filter[p_spu->i_filter], p_spu );
p_spu->pp_filter[p_spu->i_filter]->pf_sub_buffer_new = sub_new_buffer;
p_spu->pp_filter[p_spu->i_filter]->pf_sub_buffer_del = sub_del_buffer;
- p_spu->pp_filter[p_spu->i_filter]->p_cfg = p_cfg;;
+ p_spu->pp_filter[p_spu->i_filter]->p_cfg = p_cfg;
p_spu->pp_filter[p_spu->i_filter]->p_module =
module_Need( p_spu->pp_filter[p_spu->i_filter],
- "sub filter", psz_name, 0 );
+ "sub filter", psz_name, VLC_TRUE );
if( p_spu->pp_filter[p_spu->i_filter]->p_module )
{
filter_owner_sys_t *p_sys = malloc( sizeof(filter_owner_sys_t) );
else
{
msg_Dbg( p_spu, "no sub filter found" );
+ config_ChainDestroy( p_spu->pp_filter[p_spu->i_filter]->p_cfg );
vlc_object_detach( p_spu->pp_filter[p_spu->i_filter] );
vlc_object_destroy( p_spu->pp_filter[p_spu->i_filter] );
}
vlc_object_destroy( p_spu->p_scale );
}
+ spu_DeleteChain( p_spu );
+
+ vlc_mutex_destroy( &p_spu->subpicture_lock );
+ vlc_object_destroy( p_spu );
+}
+
+static void spu_DeleteChain( spu_t *p_spu )
+{
+ if( p_spu->i_filter )
while( p_spu->i_filter-- )
{
module_Unneed( p_spu->pp_filter[p_spu->i_filter],
p_spu->pp_filter[p_spu->i_filter]->p_module );
free( p_spu->pp_filter[p_spu->i_filter]->p_owner );
+ config_ChainDestroy( p_spu->pp_filter[p_spu->i_filter]->p_cfg );
vlc_object_detach( p_spu->pp_filter[p_spu->i_filter] );
vlc_object_destroy( p_spu->pp_filter[p_spu->i_filter] );
}
-
- vlc_mutex_destroy( &p_spu->subpicture_lock );
- vlc_object_destroy( p_spu );
}
/**
video_format_t *p_fmt )
{
subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
+ if( !p_region ) return NULL;
+
memset( p_region, 0, sizeof(subpicture_region_t) );
p_region->p_next = 0;
p_region->p_cache = 0;
if( !p_region->picture.i_planes )
{
free( p_region );
- free( p_fmt->p_palette );
+ if( p_fmt->p_palette ) free( p_fmt->p_palette );
return NULL;
}
picture_t *p_pic )
{
subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
+ (void)p_this;
memset( p_region, 0, sizeof(subpicture_region_t) );
p_region->p_next = 0;
p_region->p_cache = 0;
if( p_region->psz_text )
free( p_region->psz_text );
+ if( p_region->psz_html )
+ free( p_region->psz_html );
//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
free( p_region );
}
p_spu->p_blend->fmt_out.video.i_aspect = p_fmt->i_aspect;
p_spu->p_blend->fmt_out.video.i_chroma = p_fmt->i_chroma;
p_spu->p_blend->fmt_in.video.i_chroma = VLC_FOURCC('Y','U','V','P');
+ /* XXX: We'll also be using it for YUVA and RGBA blending ... */
p_spu->p_blend->p_module =
module_Need( p_spu->p_blend, "video blending", 0, 0 );
if( psz_modulename && *psz_modulename )
{
p_spu->p_text->p_module =
- module_Need( p_spu->p_text, "text renderer", psz_modulename, VLC_TRUE );
+ module_Need( p_spu->p_text, "text renderer",
+ psz_modulename, VLC_TRUE );
}
if( !p_spu->p_text->p_module )
{
}
if( p_spu->p_text )
{
- if( p_subpic->i_original_picture_height > 0 &&
- p_subpic->i_original_picture_width > 0 )
+ if( (p_subpic->i_original_picture_height > 0) &&
+ (p_subpic->i_original_picture_width > 0) )
{
p_spu->p_text->fmt_out.video.i_width =
p_spu->p_text->fmt_out.video.i_visible_width =
i_scale_width = i_scale_width_orig;
i_scale_height = i_scale_height_orig;
- if( p_subpic->i_original_picture_height > 0 &&
- p_subpic->i_original_picture_width > 0 )
+ if( (p_subpic->i_original_picture_height > 0) &&
+ (p_subpic->i_original_picture_width > 0) )
{
i_scale_width = i_scale_width * p_fmt->i_width /
p_subpic->i_original_picture_width;
{
i_scale_height = i_scale_height * p_fmt->i_height /
p_subpic->i_original_picture_height;
- i_scale_width = i_scale_height * i_scale_height / p_fmt->i_height;
+ i_scale_width = i_scale_height * i_scale_height / p_fmt->i_height;
}
/* Set default subpicture aspect ratio */
}
/* Load the scaling module */
- if( !p_spu->p_scale && (i_scale_width != 1000 ||
- i_scale_height != 1000) )
+ if( !p_spu->p_scale &&
+ ((i_scale_width > 0) || (i_scale_height > 0)) &&
+ ((i_scale_width != 1000) || (i_scale_height != 1000)) )
{
p_spu->p_scale = vlc_object_create( p_spu, VLC_OBJECT_FILTER );
vlc_object_attach( p_spu->p_scale, p_spu );
p_spu->p_scale->fmt_out.video.i_chroma =
p_spu->p_scale->fmt_in.video.i_chroma =
VLC_FOURCC('Y','U','V','P');
+ /* XXX: We'll also be using it for YUVA and RGBA blending ... */
p_spu->p_scale->fmt_in.video.i_width =
p_spu->p_scale->fmt_in.video.i_height = 32;
p_spu->p_scale->fmt_out.video.i_width =
while( p_region && p_spu->p_blend && p_spu->p_blend->pf_video_blend )
{
+ video_format_t orig_fmt = p_region->fmt;
+ vlc_bool_t b_rerender_text = VLC_FALSE;
int i_fade_alpha = 255;
int i_x_offset = p_region->i_x + i_subpic_x;
int i_y_offset = p_region->i_y + p_subpic->i_y;
{
if( p_spu->p_text && p_spu->p_text->p_module )
{
- p_region->i_align = p_subpic->i_flags;
+ vlc_value_t val;
+
+ /* Setup 3 variables which can be used to render time-dependent
+ * text (and effects). The first indicates the total amount of
+ * time the text will be on screen, the second the amount of time
+ * it has already been on screen (can be a negative value as text
+ * is layed out before it is rendered) and the third is a feedback
+ * variable from the renderer - if the renderer sets it then this
+ * particular text is time-dependent, eg. the visual progress bar
+ * inside the text in karaoke and the text needs to be rendered
+ * multiple times in order for the effect to work - we therefore
+ * need to return the region to its original state at the end of
+ * the loop, instead of leaving it in YUVA or YUVP
+ * Any renderer which is unaware of how to render time-dependent
+ * text can happily ignore the variables and render the text the
+ * same as usual - it should at least show up on screen, but the
+ * effect won't change the text over time.
+ */
+
+ var_Create( p_spu->p_text, "spu-duration", VLC_VAR_TIME );
+ val.i_time = p_subpic->i_stop - p_subpic->i_start;
+ var_Set( p_spu->p_text, "spu-duration", val );
+
+ var_Create( p_spu->p_text, "spu-elapsed", VLC_VAR_TIME );
+ val.i_time = mdate() - p_subpic->i_start;
+ var_Set( p_spu->p_text, "spu-elapsed", val );
+
+ var_Create( p_spu->p_text, "text-rerender", VLC_VAR_BOOL );
+ var_SetBool( p_spu->p_text, "text-rerender", VLC_FALSE );
if( p_spu->p_text->pf_render_html && p_region->psz_html )
{
else if( p_spu->p_text->pf_render_text )
{
p_spu->p_text->pf_render_text( p_spu->p_text,
- p_region, p_region );
+ p_region, p_region );
}
+ b_rerender_text = var_GetBool( p_spu->p_text, "text-rerender" );
+
+ var_Destroy( p_spu->p_text, "spu-duration" );
+ var_Destroy( p_spu->p_text, "spu-elapsed" );
+ var_Destroy( p_spu->p_text, "text-rerender" );
+
}
}
/* Force palette if requested */
- if( p_spu->b_force_palette && VLC_FOURCC('Y','U','V','P') ==
- p_region->fmt.i_chroma )
+ if( p_spu->b_force_palette &&
+ (VLC_FOURCC('Y','U','V','P') == p_region->fmt.i_chroma) )
{
memcpy( p_region->fmt.p_palette->palette,
p_spu->palette, 16 );
}
}
- if( (i_scale_width != 1000 || i_scale_height != 1000) &&
+ if( ((i_scale_width != 1000) || (i_scale_height != 1000)) &&
+ ((i_scale_width > 0) || (i_scale_height > 0)) &&
p_spu->p_scale && !p_region->p_cache )
{
picture_t *p_pic;
p_region->p_cache->fmt = p_spu->p_scale->fmt_out.video;
p_region->p_cache->i_x = p_region->i_x * i_scale_width / 1000;
p_region->p_cache->i_y = p_region->i_y * i_scale_height / 1000;
+ p_region->p_cache->i_align = p_region->i_align;
p_pic = p_spu->p_scale->pf_video_filter(
p_spu->p_scale, &p_region->p_cache->picture );
free( p_pic );
}
}
- if( (i_scale_width != 1000 || i_scale_height != 1000) &&
+ if( ((i_scale_width != 1000) || (i_scale_height != 1000)) &&
+ ((i_scale_width > 0) || (i_scale_height > 0)) &&
p_spu->p_scale && p_region->p_cache )
{
p_region = p_region->p_cache;
}
- if( p_subpic->i_flags & SUBPICTURE_ALIGN_BOTTOM )
+ if( p_region->i_align & SUBPICTURE_ALIGN_BOTTOM )
{
i_y_offset = p_fmt->i_height - p_region->fmt.i_height -
- p_subpic->i_y;
+ p_subpic->i_y - p_region->i_y;
}
- else if ( !(p_subpic->i_flags & SUBPICTURE_ALIGN_TOP) )
+ else if ( !(p_region->i_align & SUBPICTURE_ALIGN_TOP) )
{
i_y_offset = p_fmt->i_height / 2 - p_region->fmt.i_height / 2;
}
- if( p_subpic->i_flags & SUBPICTURE_ALIGN_RIGHT )
+ if( p_region->i_align & SUBPICTURE_ALIGN_RIGHT )
{
i_x_offset = p_fmt->i_width - p_region->fmt.i_width -
- i_subpic_x;
+ i_subpic_x - p_region->i_x;
}
- else if ( !(p_subpic->i_flags & SUBPICTURE_ALIGN_LEFT) )
+ else if ( !(p_region->i_align & SUBPICTURE_ALIGN_LEFT) )
{
i_x_offset = p_fmt->i_width / 2 - p_region->fmt.i_width / 2;
}
p_subpic->i_y * i_scale_height / 1000;
}
-
+
i_x_offset = __MAX( i_x_offset, 0 );
i_y_offset = __MAX( i_y_offset, 0 );
p_pic_src, &p_region->picture, i_x_offset, i_y_offset,
i_fade_alpha * p_subpic->i_alpha / 255 );
+ if( b_rerender_text )
+ {
+ /* Some forms of subtitles need to be re-rendered more than once,
+ * eg. karaoke. We therefore restore the region to its pre-rendered
+ * state, so the next time through everything is calculated again.
+ */
+ p_region->picture.pf_release( &p_region->picture );
+ memset( &p_region->picture, 0, sizeof( picture_t ) );
+ p_region->fmt = orig_fmt;
+ }
p_region = p_region->p_next;
}
static int CropCallback( vlc_object_t *p_object, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
+ (void)psz_var; (void)oldval; (void)newval;
UpdateSPU( (spu_t *)p_data, p_object );
return VLC_SUCCESS;
}
static subpicture_t *spu_new_buffer( filter_t *p_filter )
{
subpicture_t *p_subpic = (subpicture_t *)malloc(sizeof(subpicture_t));
+ (void)p_filter;
memset( p_subpic, 0, sizeof(subpicture_t) );
p_subpic->b_absolute = VLC_TRUE;
static void spu_del_video_buffer( filter_t *p_filter, picture_t *p_pic )
{
+ (void)p_filter;
if( p_pic && p_pic->p_data_orig ) free( p_pic->p_data_orig );
if( p_pic ) free( p_pic );
}
+
+static int SubFilterCallback( vlc_object_t *p_object, char const *psz_var,
+ vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+ (void)p_object; (void)oldval; (void)newval;
+
+ if( !strcmp( psz_var, "sub-filter" ) )
+ {
+ spu_t *p_spu = (spu_t *)p_data;
+ vlc_mutex_lock( &p_spu->subpicture_lock );
+ spu_DeleteChain( p_spu );
+ spu_ParseChain( p_spu );
+ vlc_mutex_unlock( &p_spu->subpicture_lock );
+ }
+ return VLC_SUCCESS;
+}