X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fvideo_output%2Fvout_subpictures.c;h=fb412a8ffc0200c977223bd0c907d22b7d689aa4;hb=0c9117de56d95e4b26c3bfd1de284a3d983a9a57;hp=56d8fb3f8960a5c28459de1b3839d4fbc959d5a2;hpb=ddec0b930efaef05ac51137841a7705f5ac75236;p=vlc diff --git a/src/video_output/vout_subpictures.c b/src/video_output/vout_subpictures.c index 56d8fb3f89..fb412a8ffc 100644 --- a/src/video_output/vout_subpictures.c +++ b/src/video_output/vout_subpictures.c @@ -1,11 +1,12 @@ /***************************************************************************** * vout_subpictures.c : subpicture management functions ***************************************************************************** - * Copyright (C) 2000 VideoLAN - * $Id: vout_subpictures.c,v 1.6 2002/01/21 00:52:07 sam Exp $ + * Copyright (C) 2000-2005 the VideoLAN team + * $Id$ * * Authors: Vincent Seguin * Samuel Hocevar + * Gildas Bazin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,300 +20,815 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ -#include /* ENOMEM */ #include /* free() */ #include /* sprintf() */ #include /* strerror() */ -#include - -#include "video.h" -#include "video_output.h" +#include +#include +#include +#include +#include /***************************************************************************** * Local prototypes *****************************************************************************/ -static void vout_RenderRGBSPU( const vout_thread_t *p_vout, picture_t *p_pic, - const subpicture_t *p_spu ); -static void vout_RenderYUVSPU( const vout_thread_t *p_vout, picture_t *p_pic, - const subpicture_t *p_spu ); +static void UpdateSPU ( spu_t *, vlc_object_t * ); +static int CropCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); -/* FIXME: fake palette - the real one has to be sought in the .IFO */ -static int p_palette[4] = { 0x0000, 0x0000, 0xffff, 0x8888 }; +static int spu_vaControlDefault( spu_t *, int, va_list ); -/***************************************************************************** - * vout_DisplaySubPicture: display a subpicture unit - ***************************************************************************** - * Remove the reservation flag of a subpicture, which will cause it to be ready - * for display. The picture does not need to be locked, since it is ignored by - * the output thread if is reserved. - *****************************************************************************/ -void vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic ) +static subpicture_t *sub_new_buffer( filter_t * ); +static void sub_del_buffer( filter_t *, subpicture_t * ); +static subpicture_t *spu_new_buffer( filter_t * ); +static void spu_del_buffer( filter_t *, subpicture_t * ); +static picture_t *spu_new_video_buffer( filter_t * ); +static void spu_del_video_buffer( filter_t *, picture_t * ); + +struct filter_owner_sys_t { -#ifdef TRACE_VOUT - char psz_start[ MSTRTIME_MAX_SIZE ]; /* buffer for date string */ - char psz_stop[ MSTRTIME_MAX_SIZE ]; /* buffer for date string */ -#endif - int i_margin; + spu_t *p_spu; + int i_channel; +}; -#ifdef DEBUG - /* Check if status is valid */ - if( p_subpic->i_status != RESERVED_SUBPICTURE ) +/** + * Creates the subpicture unit + * + * \param p_this the parent object which creates the subpicture unit + */ +spu_t *__spu_Create( vlc_object_t *p_this ) +{ + int i_index; + spu_t *p_spu = vlc_object_create( p_this, VLC_OBJECT_SPU ); + + for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++) + { + p_spu->p_subpicture[i_index].i_status = FREE_SUBPICTURE; + } + + p_spu->p_blend = NULL; + p_spu->p_text = NULL; + p_spu->p_scale = NULL; + p_spu->i_filter = 0; + p_spu->pf_control = spu_vaControlDefault; + + /* Register the default subpicture channel */ + p_spu->i_channel = 2; + + vlc_mutex_init( p_this, &p_spu->subpicture_lock ); + + vlc_object_attach( p_spu, p_this ); + + return p_spu; +} + +/** + * Initialise the subpicture unit + * + * \param p_spu the subpicture unit object + */ +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. */ + var_Create( p_spu, "sub-margin", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + var_Get( p_spu, "sub-margin", &val ); + 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 ); + + psz_parser = val.psz_string; + + while( psz_parser && *psz_parser ) { - intf_ErrMsg( "error: subpicture %p has invalid status #%d", - p_subpic, p_subpic->i_status ); + config_chain_t *p_cfg; /* Do we ever need to free this ? */ + char *psz_name; + + psz_parser = config_ChainCreate( &psz_name, &p_cfg, psz_parser ); + + msg_Dbg( p_spu, "adding sub-filter: %s", psz_name ); + + p_spu->pp_filter[p_spu->i_filter] = + vlc_object_create( p_spu, VLC_OBJECT_FILTER ); + 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_module = + module_Need( p_spu->pp_filter[p_spu->i_filter], + "sub filter", psz_name, 0 ); + if( p_spu->pp_filter[p_spu->i_filter]->p_module ) + { + filter_owner_sys_t *p_sys = malloc( sizeof(filter_owner_sys_t) ); + p_spu->pp_filter[p_spu->i_filter]->p_owner = p_sys; + spu_Control( p_spu, SPU_CHANNEL_REGISTER, &p_sys->i_channel ); + p_sys->p_spu = p_spu; + p_spu->i_filter++; + } + else + { + msg_Dbg( p_spu, "no sub filter found" ); + vlc_object_detach( p_spu->pp_filter[p_spu->i_filter] ); + vlc_object_destroy( p_spu->pp_filter[p_spu->i_filter] ); + } + + if( p_spu->i_filter >= 10 ) + { + msg_Dbg( p_spu, "can't add anymore filters" ); + } + + free( psz_name ); } -#endif + if( val.psz_string ) free( val.psz_string ); - /* If the user requested an SPU margin, we force the position after - * having checked that it was a valid value. */ - i_margin = main_GetIntVariable( VOUT_SPUMARGIN_VAR, - VOUT_SPUMARGIN_DEFAULT ); + return VLC_EGENERIC; +} - if( i_margin >= 0 ) +/** + * Destroy the subpicture unit + * + * \param p_this the parent object which destroys the subpicture unit + */ +void spu_Destroy( spu_t *p_spu ) +{ + int i_index; + + vlc_object_detach( p_spu ); + + /* Destroy all remaining subpictures */ + for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ ) { - if( p_subpic->i_height + i_margin <= p_vout->output.i_height ) + if( p_spu->p_subpicture[i_index].i_status != FREE_SUBPICTURE ) { - p_subpic->i_y = p_vout->output.i_height - - i_margin - p_subpic->i_height; + spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] ); } } + if( p_spu->p_blend ) + { + if( p_spu->p_blend->p_module ) + module_Unneed( p_spu->p_blend, p_spu->p_blend->p_module ); + + vlc_object_detach( p_spu->p_blend ); + vlc_object_destroy( p_spu->p_blend ); + } + + if( p_spu->p_text ) + { + if( p_spu->p_text->p_module ) + module_Unneed( p_spu->p_text, p_spu->p_text->p_module ); + + vlc_object_detach( p_spu->p_text ); + vlc_object_destroy( p_spu->p_text ); + } + + if( p_spu->p_scale ) + { + if( p_spu->p_scale->p_module ) + module_Unneed( p_spu->p_scale, p_spu->p_scale->p_module ); + + vlc_object_detach( p_spu->p_scale ); + vlc_object_destroy( p_spu->p_scale ); + } + + 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 ); + 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 ); +} + +/** + * Attach/Detach the SPU from any input + * + * \param p_this the object in which to destroy the subpicture unit + * \param b_attach to select attach or detach + */ +void spu_Attach( spu_t *p_spu, vlc_object_t *p_this, vlc_bool_t b_attach ) +{ + vlc_object_t *p_input; + + p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT ); + if( !p_input ) return; + + if( b_attach ) + { + UpdateSPU( p_spu, VLC_OBJECT(p_input) ); + var_AddCallback( p_input, "highlight", CropCallback, p_spu ); + vlc_object_release( p_input ); + } + else + { + /* Delete callback */ + var_DelCallback( p_input, "highlight", CropCallback, p_spu ); + vlc_object_release( p_input ); + } +} + +/** + * Create a subpicture region + * + * \param p_this vlc_object_t + * \param p_fmt the format that this subpicture region should have + */ +static void RegionPictureRelease( picture_t *p_pic ) +{ + if( p_pic->p_data_orig ) free( p_pic->p_data_orig ); +} +subpicture_region_t *__spu_CreateRegion( vlc_object_t *p_this, + video_format_t *p_fmt ) +{ + subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) ); + memset( p_region, 0, sizeof(subpicture_region_t) ); + p_region->p_next = 0; + p_region->p_cache = 0; + p_region->fmt = *p_fmt; + p_region->psz_text = 0; + p_region->p_style = NULL; + + if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') ) + p_fmt->p_palette = p_region->fmt.p_palette = + malloc( sizeof(video_palette_t) ); + else p_fmt->p_palette = p_region->fmt.p_palette = NULL; + + p_region->picture.p_data_orig = NULL; + + if( p_fmt->i_chroma == VLC_FOURCC('T','E','X','T') ) return p_region; + + vout_AllocatePicture( p_this, &p_region->picture, p_fmt->i_chroma, + p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect ); + + if( !p_region->picture.i_planes ) + { + free( p_region ); + free( p_fmt->p_palette ); + return NULL; + } + + p_region->picture.pf_release = RegionPictureRelease; + + return p_region; +} + +/** + * Make a subpicture region from an existing picture_t + * + * \param p_this vlc_object_t + * \param p_fmt the format that this subpicture region should have + * \param p_pic a pointer to the picture creating the region (not freed) + */ +subpicture_region_t *__spu_MakeRegion( vlc_object_t *p_this, + video_format_t *p_fmt, + picture_t *p_pic ) +{ + subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) ); + memset( p_region, 0, sizeof(subpicture_region_t) ); + p_region->p_next = 0; + p_region->p_cache = 0; + p_region->fmt = *p_fmt; + p_region->psz_text = 0; + p_region->p_style = NULL; + + if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') ) + p_fmt->p_palette = p_region->fmt.p_palette = + malloc( sizeof(video_palette_t) ); + else p_fmt->p_palette = p_region->fmt.p_palette = NULL; + + memcpy( &p_region->picture, p_pic, sizeof(picture_t) ); + p_region->picture.pf_release = RegionPictureRelease; + + return p_region; +} + +/** + * Destroy a subpicture region + * + * \param p_this vlc_object_t + * \param p_region the subpicture region to destroy + */ +void __spu_DestroyRegion( vlc_object_t *p_this, subpicture_region_t *p_region ) +{ + if( !p_region ) return; + if( p_region->picture.pf_release ) + p_region->picture.pf_release( &p_region->picture ); + if( p_region->fmt.p_palette ) free( p_region->fmt.p_palette ); + if( p_region->p_cache ) __spu_DestroyRegion( p_this, p_region->p_cache ); + + if( p_region->psz_text ) + free( p_region->psz_text ); + //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 ); +} + +/** + * Display a subpicture + * + * Remove the reservation flag of a subpicture, which will cause it to be + * ready for display. + * \param p_spu the subpicture unit object + * \param p_subpic the subpicture to display + */ +void spu_DisplaySubpicture( spu_t *p_spu, subpicture_t *p_subpic ) +{ + /* Check if status is valid */ + if( p_subpic->i_status != RESERVED_SUBPICTURE ) + { + msg_Err( p_spu, "subpicture %p has invalid status #%d", + p_subpic, p_subpic->i_status ); + } + /* Remove reservation flag */ p_subpic->i_status = READY_SUBPICTURE; -#ifdef TRACE_VOUT - /* Send subpicture information */ - intf_DbgMsg("subpicture %p: type=%d, begin date=%s, end date=%s", - p_subpic, p_subpic->i_type, - mstrtime( psz_start, p_subpic->i_start ), - mstrtime( psz_stop, p_subpic->i_stop ) ); -#endif + if( p_subpic->i_channel == DEFAULT_CHAN ) + { + p_subpic->i_channel = 0xFFFF; + spu_Control( p_spu, SPU_CHANNEL_CLEAR, DEFAULT_CHAN ); + p_subpic->i_channel = DEFAULT_CHAN; + } } -/***************************************************************************** - * vout_CreateSubPicture: allocate a subpicture in the video output heap. - ***************************************************************************** - * This function create a reserved subpicture in the video output heap. +/** + * Allocate a subpicture in the spu heap. + * + * This function create a reserved subpicture in the spu heap. * A null pointer is returned if the function fails. This method provides an * already allocated zone of memory in the spu data fields. It needs locking * since several pictures can be created by several producers threads. - *****************************************************************************/ -subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_type, - int i_size ) + * \param p_spu the subpicture unit in which to create the subpicture + * \return NULL on error, a reserved subpicture otherwise + */ +subpicture_t *spu_CreateSubpicture( spu_t *p_spu ) { int i_subpic; /* subpicture index */ - subpicture_t * p_free_subpic = NULL; /* first free subpicture */ - subpicture_t * p_destroyed_subpic = NULL; /* first destroyed subpic */ + subpicture_t * p_subpic = NULL; /* first free subpicture */ /* Get lock */ - vlc_mutex_lock( &p_vout->subpicture_lock ); + vlc_mutex_lock( &p_spu->subpicture_lock ); /* * Look for an empty place */ + p_subpic = NULL; for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ ) { - if( p_vout->p_subpicture[i_subpic].i_status == DESTROYED_SUBPICTURE ) - { - /* Subpicture is marked for destruction, but is still allocated */ - if( (p_vout->p_subpicture[i_subpic].i_type == i_type) && - (p_vout->p_subpicture[i_subpic].i_size >= i_size) ) - { - /* Memory size do match or is smaller : memory will not be - * reallocated, and function can end immediately - this is - * the best possible case, since no memory allocation needs - * to be done */ - p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE; -#ifdef TRACE_VOUT - intf_DbgMsg("subpicture %p (in destroyed subpicture slot)", - &p_vout->p_subpicture[i_subpic] ); -#endif - vlc_mutex_unlock( &p_vout->subpicture_lock ); - return( &p_vout->p_subpicture[i_subpic] ); - } - else if( p_destroyed_subpic == NULL ) - { - /* Memory size do not match, but subpicture index will be kept - * in case we find no other place */ - p_destroyed_subpic = &p_vout->p_subpicture[i_subpic]; - } - } - else if( (p_free_subpic == NULL) && - (p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE )) + if( p_spu->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE ) { /* Subpicture is empty and ready for allocation */ - p_free_subpic = &p_vout->p_subpicture[i_subpic]; + p_subpic = &p_spu->p_subpicture[i_subpic]; + p_spu->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE; + break; } } - /* If no free subpictures are available, use a destroyed subpicture */ - if( (p_free_subpic == NULL) && (p_destroyed_subpic != NULL ) ) + /* If no free subpicture could be found */ + if( p_subpic == NULL ) { - /* No free subpicture or matching destroyed subpictures have been - * found, but a destroyed subpicture is still avalaible */ - free( p_destroyed_subpic->p_data ); - p_free_subpic = p_destroyed_subpic; + msg_Err( p_spu, "subpicture heap is full" ); + vlc_mutex_unlock( &p_spu->subpicture_lock ); + return NULL; } - /* - * Prepare subpicture - */ - if( p_free_subpic != NULL ) - { - /* Allocate memory */ - switch( i_type ) - { - case TEXT_SUBPICTURE: /* text subpicture */ - p_free_subpic->p_data = memalign( 16, i_size + 1 ); - break; - case DVD_SUBPICTURE: /* DVD subpicture unit */ - p_free_subpic->p_data = memalign( 16, i_size ); - break; -#ifdef DEBUG - default: - intf_ErrMsg("error: unknown subpicture type %d", i_type ); - p_free_subpic->p_data = NULL; - break; -#endif - } + /* Copy subpicture information, set some default values */ + memset( p_subpic, 0, sizeof(subpicture_t) ); + p_subpic->i_status = RESERVED_SUBPICTURE; + p_subpic->b_absolute = VLC_TRUE; + p_subpic->b_pausable = VLC_FALSE; + p_subpic->b_fade = VLC_FALSE; + p_subpic->i_alpha = 0xFF; + p_subpic->p_region = 0; + p_subpic->pf_render = 0; + p_subpic->pf_destroy = 0; + p_subpic->p_sys = 0; + vlc_mutex_unlock( &p_spu->subpicture_lock ); + + p_subpic->pf_create_region = __spu_CreateRegion; + p_subpic->pf_make_region = __spu_MakeRegion; + p_subpic->pf_destroy_region = __spu_DestroyRegion; - if( p_free_subpic->p_data != NULL ) - { - /* Copy subpicture information, set some default values */ - p_free_subpic->i_type = i_type; - p_free_subpic->i_status = RESERVED_SUBPICTURE; - p_free_subpic->i_size = i_size; - p_free_subpic->i_x = 0; - p_free_subpic->i_y = 0; - p_free_subpic->i_width = 0; - p_free_subpic->i_height = 0; - p_free_subpic->i_horizontal_align = CENTER_RALIGN; - p_free_subpic->i_vertical_align = CENTER_RALIGN; - } - else - { - /* Memory allocation failed : set subpicture as empty */ - p_free_subpic->i_type = EMPTY_SUBPICTURE; - p_free_subpic->i_status = FREE_SUBPICTURE; - p_free_subpic = NULL; - intf_ErrMsg( "vout error: spu allocation returned %s", - strerror( ENOMEM ) ); - } - -#ifdef TRACE_VOUT - intf_DbgMsg("subpicture %p (in free subpicture slot)", p_free_subpic ); -#endif - vlc_mutex_unlock( &p_vout->subpicture_lock ); - return( p_free_subpic ); - } - - /* No free or destroyed subpicture could be found */ - intf_DbgMsg( "warning: subpicture heap is full" ); - vlc_mutex_unlock( &p_vout->subpicture_lock ); - return( NULL ); + return p_subpic; } -/***************************************************************************** - * vout_DestroySubPicture: remove a subpicture from the heap - ***************************************************************************** +/** + * Remove a subpicture from the heap + * * This function frees a previously reserved subpicture. * It is meant to be used when the construction of a picture aborted. * This function does not need locking since reserved subpictures are ignored - * by the output thread. - *****************************************************************************/ -void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic ) + * by the spu. + */ +void spu_DestroySubpicture( spu_t *p_spu, subpicture_t *p_subpic ) { -#ifdef DEBUG - /* Check if status is valid */ - if( ( p_subpic->i_status != RESERVED_SUBPICTURE ) - && ( p_subpic->i_status != READY_SUBPICTURE ) ) - { - intf_ErrMsg("error: subpicture %p has invalid status %d", - p_subpic, p_subpic->i_status ); - } -#endif - - p_subpic->i_status = DESTROYED_SUBPICTURE; - -#ifdef TRACE_VOUT - intf_DbgMsg("subpicture %p", p_subpic); -#endif + /* Get lock */ + vlc_mutex_lock( &p_spu->subpicture_lock ); + + /* There can be race conditions so we need to check the status */ + if( p_subpic->i_status == FREE_SUBPICTURE ) + { + vlc_mutex_unlock( &p_spu->subpicture_lock ); + return; + } + + /* Check if status is valid */ + if( ( p_subpic->i_status != RESERVED_SUBPICTURE ) + && ( p_subpic->i_status != READY_SUBPICTURE ) ) + { + msg_Err( p_spu, "subpicture %p has invalid status %d", + p_subpic, p_subpic->i_status ); + } + + while( p_subpic->p_region ) + { + subpicture_region_t *p_region = p_subpic->p_region; + p_subpic->p_region = p_region->p_next; + spu_DestroyRegion( p_spu, p_region ); + } + + if( p_subpic->pf_destroy ) + { + p_subpic->pf_destroy( p_subpic ); + } + + p_subpic->i_status = FREE_SUBPICTURE; + + vlc_mutex_unlock( &p_spu->subpicture_lock ); } /***************************************************************************** - * vout_RenderSubPictures: render a subpicture list + * spu_RenderSubpictures: render a subpicture list ***************************************************************************** - * This function renders a sub picture unit. + * This function renders all sub picture units in the list. *****************************************************************************/ -void vout_RenderSubPictures( vout_thread_t *p_vout, picture_t *p_pic, - subpicture_t *p_subpic ) +void spu_RenderSubpictures( spu_t *p_spu, video_format_t *p_fmt, + picture_t *p_pic_dst, picture_t *p_pic_src, + subpicture_t *p_subpic, + int i_scale_width_orig, int i_scale_height_orig ) { -#if 0 - p_vout_font_t p_font; /* text font */ - int i_width, i_height; /* subpicture dimensions */ -#endif + /* Get lock */ + vlc_mutex_lock( &p_spu->subpicture_lock ); - while( p_subpic != NULL ) + /* Check i_status again to make sure spudec hasn't destroyed the subpic */ + while( p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE ) { - switch( p_subpic->i_type ) + subpicture_region_t *p_region = p_subpic->p_region; + int i_scale_width, i_scale_height; + int i_subpic_x = p_subpic->i_x; + + /* Load the blending module */ + if( !p_spu->p_blend && p_region ) { - case DVD_SUBPICTURE: /* DVD subpicture unit */ - vout_RenderRGBSPU( p_vout, p_pic, p_subpic ); - vout_RenderYUVSPU( p_vout, p_pic, p_subpic ); - break; + p_spu->p_blend = vlc_object_create( p_spu, VLC_OBJECT_FILTER ); + vlc_object_attach( p_spu->p_blend, p_spu ); + p_spu->p_blend->fmt_out.video.i_x_offset = + p_spu->p_blend->fmt_out.video.i_y_offset = 0; + 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'); + + p_spu->p_blend->p_module = + module_Need( p_spu->p_blend, "video blending", 0, 0 ); + } + + /* Load the text rendering module */ + if( !p_spu->p_text && p_region && p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') ) + { + char *psz_modulename = NULL; + + p_spu->p_text = vlc_object_create( p_spu, VLC_OBJECT_FILTER ); + vlc_object_attach( p_spu->p_text, p_spu ); + + p_spu->p_text->fmt_out.video.i_width = + p_spu->p_text->fmt_out.video.i_visible_width = + p_fmt->i_width; + p_spu->p_text->fmt_out.video.i_height = + p_spu->p_text->fmt_out.video.i_visible_height = + p_fmt->i_height; + + p_spu->p_text->pf_sub_buffer_new = spu_new_buffer; + p_spu->p_text->pf_sub_buffer_del = spu_del_buffer; + + psz_modulename = var_CreateGetString( p_spu, "text-renderer" ); + if( psz_modulename && *psz_modulename ) + { + p_spu->p_text->p_module = + module_Need( p_spu->p_text, "text renderer", psz_modulename, VLC_TRUE ); + } + if( !p_spu->p_text->p_module ) + { + p_spu->p_text->p_module = + module_Need( p_spu->p_text, "text renderer", 0, 0 ); + } + if( psz_modulename ) free( psz_modulename ); + } + if( p_spu->p_text ) + { + 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 = + p_subpic->i_original_picture_width; + p_spu->p_text->fmt_out.video.i_height = + p_spu->p_text->fmt_out.video.i_visible_height = + p_subpic->i_original_picture_height; + } + else + { + p_spu->p_text->fmt_out.video.i_width = + p_spu->p_text->fmt_out.video.i_visible_width = + p_fmt->i_width; + p_spu->p_text->fmt_out.video.i_height = + p_spu->p_text->fmt_out.video.i_visible_height = + p_fmt->i_height; + } + } + + 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 ) + { + 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; + } + else if( p_subpic->i_original_picture_height > 0 ) + { + 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; + } + + /* Set default subpicture aspect ratio */ + if( p_region && p_region->fmt.i_aspect && + (!p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den) ) + { + p_region->fmt.i_sar_den = p_region->fmt.i_aspect; + p_region->fmt.i_sar_num = VOUT_ASPECT_FACTOR; + } + if( p_region && + (!p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den) ) + { + p_region->fmt.i_sar_den = p_fmt->i_sar_den; + p_region->fmt.i_sar_num = p_fmt->i_sar_num; + } + + /* Take care of the aspect ratio */ + if( p_region && p_region->fmt.i_sar_num * p_fmt->i_sar_den != + p_region->fmt.i_sar_den * p_fmt->i_sar_num ) + { + i_scale_width = i_scale_width * + (int64_t)p_region->fmt.i_sar_num * p_fmt->i_sar_den / + p_region->fmt.i_sar_den / p_fmt->i_sar_num; + i_subpic_x = p_subpic->i_x * i_scale_width / 1000; + } + + /* Load the scaling module */ + if( !p_spu->p_scale && (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'); + 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 = + p_spu->p_scale->fmt_out.video.i_height = 16; + + p_spu->p_scale->pf_vout_buffer_new = spu_new_video_buffer; + p_spu->p_scale->pf_vout_buffer_del = spu_del_video_buffer; + p_spu->p_scale->p_module = + module_Need( p_spu->p_scale, "video filter2", 0, 0 ); + } + + while( p_region && p_spu->p_blend && p_spu->p_blend->pf_video_blend ) + { + 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 0 - case TEXT_SUBPICTURE: /* single line text */ - /* Select default font if not specified */ - p_font = p_subpic->type.text.p_font; - if( p_font == NULL ) + if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') ) { - p_font = p_vout->p_default_font; + if( p_spu->p_text && p_spu->p_text->p_module && + p_spu->p_text->pf_render_text ) + { + p_region->i_align = p_subpic->i_flags; + p_spu->p_text->pf_render_text( p_spu->p_text, + p_region, p_region ); + } } - /* Compute text size (width and height fields are ignored) - * and print it */ - vout_TextSize( p_font, p_subpic->type.text.i_style, - p_subpic->p_data, &i_width, &i_height ); - if( !Align( p_vout, &p_subpic->i_x, &p_subpic->i_y, - i_width, i_height, p_subpic->i_horizontal_align, - p_subpic->i_vertical_align ) ) + /* Force palette if requested */ + if( p_spu->b_force_palette && VLC_FOURCC('Y','U','V','P') == + p_region->fmt.i_chroma ) { - vout_Print( p_font, - p_vout->p_buffer[ p_vout->i_buffer_index ].p_data + - p_subpic->i_x * p_vout->i_bytes_per_pixel + - p_subpic->i_y * p_vout->i_bytes_per_line, - p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line, p_subpic->type.text.i_char_color, - p_subpic->type.text.i_border_color, - p_subpic->type.text.i_bg_color, - p_subpic->type.text.i_style, p_subpic->p_data, 100 ); - SetBufferArea( p_vout, p_subpic->i_x, p_subpic->i_y, - i_width, i_height ); + memcpy( p_region->fmt.p_palette->palette, + p_spu->palette, 16 ); } - break; -#endif - default: -#ifdef DEBUG - intf_ErrMsg( "error: unknown subpicture %p type %d", - p_subpic, p_subpic->i_type ); -#endif - break; + /* Scale SPU if necessary */ + if( p_region->p_cache ) + { + if( i_scale_width * p_region->fmt.i_width / 1000 != + p_region->p_cache->fmt.i_width || + i_scale_height * p_region->fmt.i_height / 1000 != + p_region->p_cache->fmt.i_height ) + { + p_subpic->pf_destroy_region( VLC_OBJECT(p_spu), + p_region->p_cache ); + p_region->p_cache = 0; + } + } + + if( (i_scale_width != 1000 || i_scale_height != 1000) && + p_spu->p_scale && !p_region->p_cache ) + { + picture_t *p_pic; + + p_spu->p_scale->fmt_in.video = p_region->fmt; + p_spu->p_scale->fmt_out.video = p_region->fmt; + + p_region->p_cache = + p_subpic->pf_create_region( VLC_OBJECT(p_spu), + &p_spu->p_scale->fmt_out.video ); + if( p_spu->p_scale->fmt_out.video.p_palette ) + *p_spu->p_scale->fmt_out.video.p_palette = + *p_region->fmt.p_palette; + p_region->p_cache->p_next = p_region->p_next; + + vout_CopyPicture( p_spu, &p_region->p_cache->picture, + &p_region->picture ); + + p_spu->p_scale->fmt_out.video.i_width = + p_region->fmt.i_width * i_scale_width / 1000; + p_spu->p_scale->fmt_out.video.i_visible_width = + p_region->fmt.i_visible_width * i_scale_width / 1000; + p_spu->p_scale->fmt_out.video.i_height = + p_region->fmt.i_height * i_scale_height / 1000; + p_spu->p_scale->fmt_out.video.i_visible_height = + p_region->fmt.i_visible_height * i_scale_height / 1000; + 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_pic = p_spu->p_scale->pf_video_filter( + p_spu->p_scale, &p_region->p_cache->picture ); + if( p_pic ) + { + picture_t p_pic_tmp = p_region->p_cache->picture; + p_region->p_cache->picture = *p_pic; + *p_pic = p_pic_tmp; + free( p_pic ); + } + } + if( (i_scale_width != 1000 || i_scale_height != 1000) && + p_spu->p_scale && p_region->p_cache ) + { + p_region = p_region->p_cache; + } + + if( p_subpic->i_flags & SUBPICTURE_ALIGN_BOTTOM ) + { + i_y_offset = p_fmt->i_height - p_region->fmt.i_height - + p_subpic->i_y; + } + else if ( !(p_subpic->i_flags & 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 ) + { + i_x_offset = p_fmt->i_width - p_region->fmt.i_width - + i_subpic_x; + } + else if ( !(p_subpic->i_flags & SUBPICTURE_ALIGN_LEFT) ) + { + i_x_offset = p_fmt->i_width / 2 - p_region->fmt.i_width / 2; + } + + if( p_subpic->b_absolute ) + { + i_x_offset = p_region->i_x + + i_subpic_x * i_scale_width / 1000; + i_y_offset = p_region->i_y + + p_subpic->i_y * i_scale_height / 1000; + + } + + i_x_offset = __MAX( i_x_offset, 0 ); + i_y_offset = __MAX( i_y_offset, 0 ); + + if( p_spu->i_margin != 0 && p_spu->b_force_crop == VLC_FALSE ) + { + int i_diff = 0; + int i_low = i_y_offset - p_spu->i_margin; + int i_high = i_y_offset + p_region->fmt.i_height - p_spu->i_margin; + + /* crop extra margin to keep within bounds */ + if( i_low < 0 ) i_diff = i_low; + if( i_high > (int)p_fmt->i_height ) i_diff = i_high - p_fmt->i_height; + i_y_offset -= ( p_spu->i_margin + i_diff ); + } + + p_spu->p_blend->fmt_in.video = p_region->fmt; + + /* Force cropping if requested */ + if( p_spu->b_force_crop ) + { + video_format_t *p_fmt = &p_spu->p_blend->fmt_in.video; + int i_crop_x = p_spu->i_crop_x * i_scale_width / 1000; + int i_crop_y = p_spu->i_crop_y * i_scale_height / 1000; + int i_crop_width = p_spu->i_crop_width * i_scale_width / 1000; + int i_crop_height = p_spu->i_crop_height * i_scale_height/1000; + + /* Find the intersection */ + if( i_crop_x + i_crop_width <= i_x_offset || + i_x_offset + (int)p_fmt->i_visible_width < i_crop_x || + i_crop_y + i_crop_height <= i_y_offset || + i_y_offset + (int)p_fmt->i_visible_height < i_crop_y ) + { + /* No intersection */ + p_fmt->i_visible_width = p_fmt->i_visible_height = 0; + } + else + { + int i_x, i_y, i_x_end, i_y_end; + i_x = __MAX( i_crop_x, i_x_offset ); + i_y = __MAX( i_crop_y, i_y_offset ); + i_x_end = __MIN( i_crop_x + i_crop_width, + i_x_offset + (int)p_fmt->i_visible_width ); + i_y_end = __MIN( i_crop_y + i_crop_height, + i_y_offset + (int)p_fmt->i_visible_height ); + + p_fmt->i_x_offset = i_x - i_x_offset; + p_fmt->i_y_offset = i_y - i_y_offset; + p_fmt->i_visible_width = i_x_end - i_x; + p_fmt->i_visible_height = i_y_end - i_y; + + i_x_offset = i_x; + i_y_offset = i_y; + } + } + + /* Update the output picture size */ + p_spu->p_blend->fmt_out.video.i_width = + p_spu->p_blend->fmt_out.video.i_visible_width = + p_fmt->i_width; + p_spu->p_blend->fmt_out.video.i_height = + p_spu->p_blend->fmt_out.video.i_visible_height = + p_fmt->i_height; + + if( p_subpic->b_fade ) + { + mtime_t i_fade_start = ( p_subpic->i_stop + + p_subpic->i_start ) / 2; + mtime_t i_now = mdate(); + if( i_now >= i_fade_start && p_subpic->i_stop > i_fade_start ) + { + i_fade_alpha = 255 * ( p_subpic->i_stop - i_now ) / + ( p_subpic->i_stop - i_fade_start ); + } + } + + i_x_offset = __MAX( i_x_offset, 0 ); + i_y_offset = __MAX( i_y_offset, 0 ); + + p_spu->p_blend->pf_video_blend( p_spu->p_blend, p_pic_dst, + p_pic_src, &p_region->picture, i_x_offset, i_y_offset, + i_fade_alpha * p_subpic->i_alpha / 255 ); + + p_region = p_region->p_next; } p_subpic = p_subpic->p_next; } + + vlc_mutex_unlock( &p_spu->subpicture_lock ); } /***************************************************************************** - * vout_SortSubPictures: find the subpictures to display + * spu_SortSubpictures: find the subpictures to display ***************************************************************************** * This function parses all subpictures and decides which ones need to be * displayed. This operation does not need lock, since only READY_SUBPICTURE @@ -322,99 +838,93 @@ void vout_RenderSubPictures( vout_thread_t *p_vout, picture_t *p_pic, * to be removed if a newer one is available), which makes it a lot * more difficult to guess if a subpicture has to be rendered or not. *****************************************************************************/ -subpicture_t *vout_SortSubPictures( vout_thread_t *p_vout, - mtime_t display_date ) +subpicture_t *spu_SortSubpictures( spu_t *p_spu, mtime_t display_date, + vlc_bool_t b_paused ) { - int i_index; - subpicture_t *p_subpic = NULL; - subpicture_t *p_ephemer = NULL; - mtime_t ephemer_date = 0; + int i_index, i_channel; + subpicture_t *p_subpic = NULL; + subpicture_t *p_ephemer; + mtime_t ephemer_date; + + /* Run subpicture filters */ + for( i_index = 0; i_index < p_spu->i_filter; i_index++ ) + { + subpicture_t *p_subpic_filter; + p_subpic_filter = p_spu->pp_filter[i_index]-> + pf_sub_filter( p_spu->pp_filter[i_index], display_date ); + if( p_subpic_filter ) + { + spu_DisplaySubpicture( p_spu, p_subpic_filter ); + } + } /* We get an easily parsable chained list of subpictures which * ends with NULL since p_subpic was initialized to NULL. */ - for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ ) + for( i_channel = 0; i_channel < p_spu->i_channel; i_channel++ ) { - if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE ) + p_ephemer = 0; + ephemer_date = 0; + + for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ ) { - /* If it is a DVD subpicture, check its date */ - if( p_vout->p_subpicture[i_index].i_type == DVD_SUBPICTURE ) + if( p_spu->p_subpicture[i_index].i_channel != i_channel || + p_spu->p_subpicture[i_index].i_status != READY_SUBPICTURE ) { - if( display_date > p_vout->p_subpicture[i_index].i_stop ) - { - /* Too late, destroy the subpic */ - vout_DestroySubPicture( p_vout, - &p_vout->p_subpicture[i_index] ); - continue; - } - - if( display_date < p_vout->p_subpicture[i_index].i_start ) - { - /* Too early, come back next monday */ - continue; - } - - /* If this is an ephemer subpic, see if it's the - * youngest we have */ - if( p_vout->p_subpicture[i_index].b_ephemer ) - { - if( p_ephemer == NULL ) - { - p_ephemer = &p_vout->p_subpicture[i_index]; - continue; - } - - if( p_vout->p_subpicture[i_index].i_start - < p_ephemer->i_start ) - { - /* Link the previous ephemer subpicture and - * replace it with the current one */ - p_ephemer->p_next = p_subpic; - p_subpic = p_ephemer; - p_ephemer = &p_vout->p_subpicture[i_index]; - - /* If it's the 2nd youngest subpicture, - * register its date */ - if( !ephemer_date - || ephemer_date > p_subpic->i_start ) - { - ephemer_date = p_subpic->i_start; - } - - continue; - } - } + continue; + } + if( display_date && + display_date < p_spu->p_subpicture[i_index].i_start ) + { + /* Too early, come back next monday */ + continue; + } - p_vout->p_subpicture[i_index].p_next = p_subpic; - p_subpic = &p_vout->p_subpicture[i_index]; + if( p_spu->p_subpicture[i_index].i_start > ephemer_date ) + ephemer_date = p_spu->p_subpicture[i_index].i_start; - /* If it's the 2nd youngest subpicture, register its date */ if( !ephemer_date || ephemer_date > p_subpic->i_start ) - { - ephemer_date = p_subpic->i_start; - } + if( display_date > p_spu->p_subpicture[i_index].i_stop && + ( !p_spu->p_subpicture[i_index].b_ephemer || + p_spu->p_subpicture[i_index].i_stop > + p_spu->p_subpicture[i_index].i_start ) && + !( p_spu->p_subpicture[i_index].b_pausable && + b_paused ) ) + { + /* Too late, destroy the subpic */ + spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] ); + continue; } - /* If it's not a DVD subpicture, just register it */ - else + + /* If this is an ephemer subpic, add it to our list */ + if( p_spu->p_subpicture[i_index].b_ephemer ) { - p_vout->p_subpicture[i_index].p_next = p_subpic; - p_subpic = &p_vout->p_subpicture[i_index]; + p_spu->p_subpicture[i_index].p_next = p_ephemer; + p_ephemer = &p_spu->p_subpicture[i_index]; + + continue; } - } - } - /* If we found an ephemer subpicture, check if it has to be - * displayed */ - if( p_ephemer != NULL ) - { - if( p_ephemer->i_start < ephemer_date ) - { - /* Ephemer subpicture has lived too long */ - vout_DestroySubPicture( p_vout, p_ephemer ); + p_spu->p_subpicture[i_index].p_next = p_subpic; + p_subpic = &p_spu->p_subpicture[i_index]; } - else + + /* If we found ephemer subpictures, check if they have to be + * displayed or destroyed */ + while( p_ephemer != NULL ) { - /* Ephemer subpicture can still live a bit */ - p_ephemer->p_next = p_subpic; - return p_ephemer; + subpicture_t *p_tmp = p_ephemer; + p_ephemer = p_ephemer->p_next; + + if( p_tmp->i_start < ephemer_date ) + { + /* Ephemer subpicture has lived too long */ + spu_DestroySubpicture( p_spu, p_tmp ); + } + else + { + /* Ephemer subpicture can still live a bit */ + p_tmp->p_next = p_subpic; + p_subpic = p_tmp; + } } } @@ -422,157 +932,189 @@ subpicture_t *vout_SortSubPictures( vout_thread_t *p_vout, } /***************************************************************************** - * vout_RenderRGBSPU: draw an SPU on a picture + * SpuClearChannel: clear an spu channel ***************************************************************************** - * This is a fast implementation of the subpicture drawing code. The data - * has been preprocessed once in spu_decoder.c, so we don't need to parse the - * RLE buffer again and again. Most sanity checks are done in spu_decoder.c - * so that this routine can be as fast as possible. + * This function destroys the subpictures which belong to the spu channel + * corresponding to i_channel_id. *****************************************************************************/ -static void vout_RenderRGBSPU( const vout_thread_t *p_vout, picture_t *p_pic, - const subpicture_t *p_spu ) +static void SpuClearChannel( spu_t *p_spu, int i_channel ) { -#if 0 - int i_len, i_color; - u16 *p_source = (u16 *)p_spu->p_data; - - int i_xscale = ( p_buffer->i_pic_width << 6 ) / p_pic->i_width; - int i_yscale = ( p_buffer->i_pic_height << 6 ) / p_pic->i_height; + int i_subpic; /* subpicture index */ + subpicture_t *p_subpic = NULL; /* first free subpicture */ - int i_width = p_spu->i_width * i_xscale; - int i_height = p_spu->i_height * i_yscale; + vlc_mutex_lock( &p_spu->subpicture_lock ); - int i_x, i_y, i_ytmp, i_yreal, i_ynext; - - u8 *p_dest = p_buffer->p_data + ( i_width >> 6 ) * i_bytes_per_pixel - /* Add the picture coordinates and the SPU coordinates */ - + ( p_buffer->i_pic_x + ((p_spu->i_x * i_xscale) >> 6)) - * i_bytes_per_pixel - + ( p_buffer->i_pic_y + ((p_spu->i_y * i_yscale) >> 6)) - * i_bytes_per_line; - - /* Draw until we reach the bottom of the subtitle */ - i_y = 0; - - while( i_y < i_height ) + for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ ) { - i_ytmp = i_y >> 6; - i_y += i_yscale; + p_subpic = &p_spu->p_subpicture[i_subpic]; + if( p_subpic->i_status == FREE_SUBPICTURE + || ( p_subpic->i_status != RESERVED_SUBPICTURE + && p_subpic->i_status != READY_SUBPICTURE ) ) + { + continue; + } - /* Check whether we need to draw one line or more than one */ - if( i_ytmp + 1 >= ( i_y >> 6 ) ) + if( p_subpic->i_channel == i_channel ) { - /* Just one line : we precalculate i_y >> 6 */ - i_yreal = i_bytes_per_line * i_ytmp; + while( p_subpic->p_region ) + { + subpicture_region_t *p_region = p_subpic->p_region; + p_subpic->p_region = p_region->p_next; + spu_DestroyRegion( p_spu, p_region ); + } - /* Draw until we reach the end of the line */ - i_x = i_width; + if( p_subpic->pf_destroy ) p_subpic->pf_destroy( p_subpic ); + p_subpic->i_status = FREE_SUBPICTURE; + } + } - while( i_x ) - { - /* Get the RLE part */ - i_color = *p_source & 0x3; + vlc_mutex_unlock( &p_spu->subpicture_lock ); +} - /* Draw the line */ - if( i_color ) - { - i_len = i_xscale * ( *p_source++ >> 2 ); +/***************************************************************************** + * spu_ControlDefault: default methods for the subpicture unit control. + *****************************************************************************/ +static int spu_vaControlDefault( spu_t *p_spu, int i_query, va_list args ) +{ + int *pi, i; - memset( p_dest - i_bytes_per_pixel * ( i_x >> 6 ) - + i_yreal, - p_palette[ i_color ], - i_bytes_per_pixel * ( ( i_len >> 6 ) + 1 ) ); + switch( i_query ) + { + case SPU_CHANNEL_REGISTER: + pi = (int *)va_arg( args, int * ); + if( pi ) *pi = p_spu->i_channel++; + break; + + case SPU_CHANNEL_CLEAR: + i = (int)va_arg( args, int ); + SpuClearChannel( p_spu, i ); + break; + + default: + msg_Dbg( p_spu, "control query not supported" ); + return VLC_EGENERIC; + } - i_x -= i_len; - continue; - } + return VLC_SUCCESS; +} - i_x -= i_xscale * ( *p_source++ >> 2 ); - } - } - else - { - i_yreal = i_bytes_per_line * i_ytmp; - i_ynext = i_bytes_per_line * i_y >> 6; +/***************************************************************************** + * Object variables callbacks + *****************************************************************************/ - /* Draw until we reach the end of the line */ - i_x = i_width; +/***************************************************************************** + * UpdateSPU: update subpicture settings + ***************************************************************************** + * This function is called from CropCallback and at initialization time, to + * retrieve crop information from the input. + *****************************************************************************/ +static void UpdateSPU( spu_t *p_spu, vlc_object_t *p_object ) +{ + vlc_value_t val; - while( i_x ) - { - /* Get the RLE part */ - i_color = *p_source & 0x3; + p_spu->b_force_palette = VLC_FALSE; + p_spu->b_force_crop = VLC_FALSE; - /* Draw as many lines as needed */ - if( i_color ) - { - i_len = i_xscale * ( *p_source++ >> 2 ); - - for( i_ytmp = i_yreal ; - i_ytmp < i_ynext ; - i_ytmp += i_bytes_per_line ) - { - memset( p_dest - i_bytes_per_pixel * ( i_x >> 6 ) - + i_ytmp, - p_palette[ i_color ], - i_bytes_per_pixel * ( ( i_len >> 6 ) + 1 ) ); - } - - i_x -= i_len; - continue; - } + if( var_Get( p_object, "highlight", &val ) || !val.b_bool ) return; - i_x -= i_xscale * ( *p_source++ >> 2 ); - } - } + p_spu->b_force_crop = VLC_TRUE; + var_Get( p_object, "x-start", &val ); + p_spu->i_crop_x = val.i_int; + var_Get( p_object, "y-start", &val ); + p_spu->i_crop_y = val.i_int; + var_Get( p_object, "x-end", &val ); + p_spu->i_crop_width = val.i_int - p_spu->i_crop_x; + var_Get( p_object, "y-end", &val ); + p_spu->i_crop_height = val.i_int - p_spu->i_crop_y; + + if( var_Get( p_object, "menu-palette", &val ) == VLC_SUCCESS ) + { + memcpy( p_spu->palette, val.p_address, 16 ); + p_spu->b_force_palette = VLC_TRUE; } -#endif + + msg_Dbg( p_object, "crop: %i,%i,%i,%i, palette forced: %i", + p_spu->i_crop_x, p_spu->i_crop_y, + p_spu->i_crop_width, p_spu->i_crop_height, + p_spu->b_force_palette ); } /***************************************************************************** - * vout_RenderYUVSPU: draw an SPU on an YUV overlay + * CropCallback: called when the highlight properties are changed ***************************************************************************** - * This is a fast implementation of the subpicture drawing code. The data - * has been preprocessed once in spu_decoder.c, so we don't need to parse the - * RLE buffer again and again. Most sanity checks are done in spu_decoder.c - * so that this routine can be as fast as possible. + * This callback is called from the input thread when we need cropping *****************************************************************************/ -static void vout_RenderYUVSPU( const vout_thread_t *p_vout, picture_t *p_pic, - const subpicture_t *p_spu ) +static int CropCallback( vlc_object_t *p_object, char const *psz_var, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) { - int i_len, i_color; - u16 *p_source = (u16 *)p_spu->p_data; + UpdateSPU( (spu_t *)p_data, p_object ); + return VLC_SUCCESS; +} - int i_x, i_y; +/***************************************************************************** + * Buffers allocation callbacks for the filters + *****************************************************************************/ +static subpicture_t *sub_new_buffer( filter_t *p_filter ) +{ + filter_owner_sys_t *p_sys = p_filter->p_owner; + subpicture_t *p_subpicture = spu_CreateSubpicture( p_sys->p_spu ); + if( p_subpicture ) p_subpicture->i_channel = p_sys->i_channel; + return p_subpicture; +} - u8 *p_dest = p_pic->p->p_pixels + p_spu->i_x + p_spu->i_width - + p_vout->output.i_width * ( p_spu->i_y + p_spu->i_height ); +static void sub_del_buffer( filter_t *p_filter, subpicture_t *p_subpic ) +{ + filter_owner_sys_t *p_sys = p_filter->p_owner; + spu_DestroySubpicture( p_sys->p_spu, p_subpic ); +} - /* Draw until we reach the bottom of the subtitle */ - i_y = p_spu->i_height * p_vout->output.i_width; +static subpicture_t *spu_new_buffer( filter_t *p_filter ) +{ + subpicture_t *p_subpic = (subpicture_t *)malloc(sizeof(subpicture_t)); + memset( p_subpic, 0, sizeof(subpicture_t) ); + p_subpic->b_absolute = VLC_TRUE; - while( i_y ) - { - /* Draw until we reach the end of the line */ - i_x = p_spu->i_width; + p_subpic->pf_create_region = __spu_CreateRegion; + p_subpic->pf_make_region = __spu_MakeRegion; + p_subpic->pf_destroy_region = __spu_DestroyRegion; - while( i_x ) - { - /* Draw the line if needed */ - i_color = *p_source & 0x3; + return p_subpic; +} - if( i_color ) - { - i_len = *p_source++ >> 2; - memset( p_dest - i_x - i_y, p_palette[ i_color ], i_len ); - i_x -= i_len; - continue; - } +static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_subpic ) +{ + while( p_subpic->p_region ) + { + subpicture_region_t *p_region = p_subpic->p_region; + p_subpic->p_region = p_region->p_next; + p_subpic->pf_destroy_region( VLC_OBJECT(p_filter), p_region ); + } - i_x -= *p_source++ >> 2; - } + free( p_subpic ); +} - i_y -= p_vout->output.i_width; +static picture_t *spu_new_video_buffer( filter_t *p_filter ) +{ + picture_t *p_picture = malloc( sizeof(picture_t) ); + + if( vout_AllocatePicture( p_filter, p_picture, + p_filter->fmt_out.video.i_chroma, + p_filter->fmt_out.video.i_width, + p_filter->fmt_out.video.i_height, + p_filter->fmt_out.video.i_aspect ) + != VLC_SUCCESS ) + { + free( p_picture ); + return NULL; } + + p_picture->pf_release = RegionPictureRelease; + + return p_picture; } +static void spu_del_video_buffer( filter_t *p_filter, picture_t *p_pic ) +{ + if( p_pic && p_pic->p_data_orig ) free( p_pic->p_data_orig ); + if( p_pic ) free( p_pic ); +}