-static subpicture_region_private_t *SpuRegionPrivateNew( video_format_t * );
-static void SpuRegionPrivateDelete( subpicture_region_private_t * );
-
-/* */
-typedef struct
-{
- int w;
- int h;
-} spu_scale_t;
-static spu_scale_t spu_scale_create( int w, int h );
-static spu_scale_t spu_scale_unit(void );
-static spu_scale_t spu_scale_createq( int wn, int wd, int hn, int hd );
-static int spu_scale_w( int v, const spu_scale_t s );
-static int spu_scale_h( int v, const spu_scale_t s );
-static int spu_invscale_w( int v, const spu_scale_t s );
-static int spu_invscale_h( int v, const spu_scale_t s );
-
-typedef struct
-{
- int i_x;
- int i_y;
- int i_width;
- int i_height;
-
- spu_scale_t scale;
-} spu_area_t;
-
-static spu_area_t spu_area_create( int x, int y, int w, int h, spu_scale_t );
-static spu_area_t spu_area_scaled( spu_area_t );
-static spu_area_t spu_area_unscaled( spu_area_t, spu_scale_t );
-static bool spu_area_overlap( spu_area_t, spu_area_t );
-
-
-/* Subpicture rendered flag
- * FIXME ? it could be moved to private ? */
-#define SUBPICTURE_RENDERED (0x1000)
-#if SUBPICTURE_RENDERED < SUBPICTURE_ALIGN_MASK
-# error SUBPICTURE_RENDERED too low
-#endif
-
-#define SCALE_UNIT (1000)
-
-static void SubpictureChain( subpicture_t **pp_head, subpicture_t *p_subpic );
-static int SubpictureCmp( const void *s0, const void *s1 );
-
-static void SpuRenderRegion( spu_t *,
- picture_t *p_pic_dst, spu_area_t *,
- subpicture_t *, subpicture_region_t *,
- const spu_scale_t scale_size,
- const video_format_t *p_fmt,
- const spu_area_t *p_subtitle_area, int i_subtitle_area );
-
-static void UpdateSPU ( spu_t *, vlc_object_t * );
-static int CropCallback( vlc_object_t *, char const *,
- vlc_value_t, vlc_value_t, void * );
-
-static int SpuControl( spu_t *, int, va_list );
-
-static void SpuClearChannel( spu_t *p_spu, int i_channel );
-
-/* Buffer allocation for SPU filter (blend, scale, ...) */
-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 * );
-
-/* Buffer aloccation fir SUB filter */
-static int SubFilterCallback( vlc_object_t *, char const *,
- vlc_value_t, vlc_value_t, void * );
-
-static int SubFilterAllocationInit( filter_t *, void * );
-static void SubFilterAllocationClean( filter_t * );
-
-/* */
-static void SpuRenderCreateAndLoadText( spu_t * );
-static void SpuRenderCreateAndLoadScale( spu_t * );
-static void SpuRenderCreateBlend( spu_t *, vlc_fourcc_t i_chroma, int i_aspect );
-static void FilterRelease( filter_t *p_filter );
-
-/*****************************************************************************
- * Public API
- *****************************************************************************/
-/**
- * Creates the subpicture unit
- *
- * \param p_this the parent object which creates the subpicture unit
- */
-spu_t *__spu_Create( vlc_object_t *p_this )
-{
- spu_t *p_spu;
- spu_private_t *p_sys;
-
- p_spu = vlc_custom_create( p_this, sizeof(spu_t) + sizeof(spu_private_t),
- VLC_OBJECT_GENERIC, "subpicture" );
-
- if( !p_spu )
- return NULL;
-
- /* Initialize spu fields */
- p_spu->pf_control = SpuControl;
- p_spu->p = p_sys = (spu_private_t*)&p_spu[1];
-
- /* Initialize private fields */
- vlc_mutex_init( &p_sys->lock );
-
- SpuHeapInit( &p_sys->heap );
-
- p_sys->p_blend = NULL;
- p_sys->p_text = NULL;
- p_sys->p_scale = NULL;
- p_sys->p_scale_yuvp = NULL;
-
- /* Register the default subpicture channel */
- p_sys->i_channel = 2;
-
- vlc_object_attach( p_spu, p_this );
-
- p_sys->psz_chain_update = NULL;
- p_sys->p_chain = filter_chain_New( p_spu, "sub filter", false,
- SubFilterAllocationInit,
- SubFilterAllocationClean,
- p_spu );
-
- /* Load text and scale module */
- SpuRenderCreateAndLoadText( p_spu );
- SpuRenderCreateAndLoadScale( p_spu );
-
- /* */
- p_sys->i_last_sort_date = -1;
-
- return p_spu;
-}
-
-/**
- * Initialise the subpicture unit
- *
- * \param p_spu the subpicture unit object
- */
-int spu_Init( spu_t *p_spu )
-{
- spu_private_t *p_sys = p_spu->p;
-
- /* If the user requested a sub margin, we force the position. */
- p_sys->i_margin = var_CreateGetInteger( p_spu, "sub-margin" );
-
- var_Create( p_spu, "sub-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
- var_AddCallback( p_spu, "sub-filter", SubFilterCallback, p_spu );
- var_TriggerCallback( p_spu, "sub-filter" );
-
- return VLC_SUCCESS;
-}
-
-/**
- * Destroy the subpicture unit
- *
- * \param p_this the parent object which destroys the subpicture unit
- */
-void spu_Destroy( spu_t *p_spu )
-{
- spu_private_t *p_sys = p_spu->p;
-
- var_DelCallback( p_spu, "sub-filter", SubFilterCallback, p_spu );
-
- if( p_sys->p_blend )
- FilterRelease( p_sys->p_blend );
-
- if( p_sys->p_text )
- FilterRelease( p_sys->p_text );
-
- if( p_sys->p_scale_yuvp )
- FilterRelease( p_sys->p_scale_yuvp );
-
- if( p_sys->p_scale )
- FilterRelease( p_sys->p_scale );
-
- filter_chain_Delete( p_sys->p_chain );
- free( p_sys->psz_chain_update );
-
- /* Destroy all remaining subpictures */
- SpuHeapClean( &p_sys->heap );
-
- vlc_mutex_destroy( &p_sys->lock );
-
- vlc_object_release( 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, bool 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 );
- }
-}
-
-/**
- * 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 )
-{
- spu_private_t *p_sys = p_spu->p;
-
- /* DEFAULT_CHAN always reset itself */
- if( p_subpic->i_channel == DEFAULT_CHAN )
- SpuClearChannel( p_spu, DEFAULT_CHAN );
-
- /* p_private is for spu only and cannot be non NULL here */
- for( subpicture_region_t *r = p_subpic->p_region; r != NULL; r = r->p_next )
- assert( r->p_private == NULL );
-
- /* */
- vlc_mutex_lock( &p_sys->lock );
- if( SpuHeapPush( &p_sys->heap, p_subpic ) )
- {
- vlc_mutex_unlock( &p_sys->lock );
- msg_Err( p_spu, "subpicture heap full" );
- subpicture_Delete( p_subpic );
- return;
- }
- vlc_mutex_unlock( &p_sys->lock );
-}
-
-/**
- * This function renders all sub picture units in the list.
- */
-void spu_RenderSubpictures( spu_t *p_spu,
- picture_t *p_pic_dst, const video_format_t *p_fmt_dst,
- subpicture_t *p_subpic_list,
- const video_format_t *p_fmt_src, bool b_paused )
-{
- spu_private_t *p_sys = p_spu->p;
-
- const int i_source_video_width = p_fmt_src->i_width;
- const int i_source_video_height = p_fmt_src->i_height;
- const mtime_t i_current_date = mdate();
-
- unsigned int i_subpicture;
- subpicture_t *pp_subpicture[VOUT_MAX_SUBPICTURES];
-
- unsigned int i_subtitle_region_count;
- spu_area_t p_subtitle_area_buffer[VOUT_MAX_SUBPICTURES];
- spu_area_t *p_subtitle_area;
- int i_subtitle_area;
-
- vlc_mutex_lock( &p_sys->lock );
-
- /* Preprocess subpictures */
- i_subpicture = 0;
- i_subtitle_region_count = 0;
- for( subpicture_t * p_subpic = p_subpic_list;
- p_subpic != NULL;
- p_subpic = p_subpic->p_next )
- {
- /* */
- if( !b_paused && p_subpic->pf_pre_render )
- p_subpic->pf_pre_render( p_spu, p_subpic, p_fmt_dst );
-
- if( !b_paused && p_subpic->pf_update_regions )
- {
- video_format_t fmt_org = *p_fmt_dst;
- fmt_org.i_width =
- fmt_org.i_visible_width = i_source_video_width;
- fmt_org.i_height =
- fmt_org.i_visible_height = i_source_video_height;
-
- p_subpic->pf_update_regions( p_spu, p_subpic, &fmt_org, i_current_date );
- }
-
- /* */
- if( p_subpic->b_subtitle )
- {
- for( subpicture_region_t *r = p_subpic->p_region; r != NULL; r = r->p_next )
- i_subtitle_region_count++;
- }
-
- /* */
- pp_subpicture[i_subpicture++] = p_subpic;
- }
-
- /* Be sure we have at least 1 picture to process */
- if( i_subpicture <= 0 )
- {
- vlc_mutex_unlock( &p_sys->lock );
- return;
- }
-
- /* Now order subpicture array
- * XXX The order is *really* important for overlap subtitles positionning */
- qsort( pp_subpicture, i_subpicture, sizeof(*pp_subpicture), SubpictureCmp );
-
- /* Allocate area array for subtitle overlap */
- i_subtitle_area = 0;
- p_subtitle_area = p_subtitle_area_buffer;
- if( i_subtitle_region_count > sizeof(p_subtitle_area_buffer)/sizeof(*p_subtitle_area_buffer) )
- p_subtitle_area = calloc( i_subtitle_region_count, sizeof(*p_subtitle_area) );
-
- /* Create the blending module */
- if( !p_sys->p_blend )
- SpuRenderCreateBlend( p_spu, p_fmt_dst->i_chroma, p_fmt_dst->i_aspect );
-
- /* Process all subpictures and regions (in the right order) */
- for( unsigned int i_index = 0; i_index < i_subpicture; i_index++ )
- {
- subpicture_t *p_subpic = pp_subpicture[i_index];
- subpicture_region_t *p_region;
-
- if( !p_subpic->p_region )
- continue;
-
- /* FIXME when possible use a better rendering size than source size
- * (max of display size and source size for example) FIXME */
- int i_render_width = p_subpic->i_original_picture_width;
- int i_render_height = p_subpic->i_original_picture_height;
- if( !i_render_width || !i_render_height )
- {
- if( i_render_width != 0 || i_render_height != 0 )
- msg_Err( p_spu, "unsupported original picture size %dx%d",
- i_render_width, i_render_height );
-
- p_subpic->i_original_picture_width = i_render_width = i_source_video_width;
- p_subpic->i_original_picture_height = i_render_height = i_source_video_height;
- }
-
- if( p_sys->p_text )
- {
- p_sys->p_text->fmt_out.video.i_width =
- p_sys->p_text->fmt_out.video.i_visible_width = i_render_width;
-
- p_sys->p_text->fmt_out.video.i_height =
- p_sys->p_text->fmt_out.video.i_visible_height = i_render_height;
- }
-
- /* Compute scaling from picture to source size */
- spu_scale_t scale = spu_scale_createq( i_source_video_width, i_render_width,
- i_source_video_height, i_render_height );
-
- /* Update scaling from source size to display size(p_fmt_dst) */
- scale.w = scale.w * p_fmt_dst->i_width / i_source_video_width;
- scale.h = scale.h * p_fmt_dst->i_height / i_source_video_height;
-
- /* Set default subpicture aspect ratio
- * FIXME if we only handle 1 aspect ratio per picture, why is it set per
- * region ? */
- p_region = p_subpic->p_region;
- if( !p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den )
- {
- if( p_region->fmt.i_aspect != 0 )
- {
- p_region->fmt.i_sar_den = p_region->fmt.i_aspect;
- p_region->fmt.i_sar_num = VOUT_ASPECT_FACTOR;
- }
- else
- {
- p_region->fmt.i_sar_den = p_fmt_dst->i_sar_den;
- p_region->fmt.i_sar_num = p_fmt_dst->i_sar_num;
- }
- }
-
- /* Take care of the aspect ratio */
- if( p_region->fmt.i_sar_num * p_fmt_dst->i_sar_den !=
- p_region->fmt.i_sar_den * p_fmt_dst->i_sar_num )
- {
- /* FIXME FIXME what about region->i_x/i_y ? */
- scale.w = scale.w *
- (int64_t)p_region->fmt.i_sar_num * p_fmt_dst->i_sar_den /
- p_region->fmt.i_sar_den / p_fmt_dst->i_sar_num;
- }
-
- /* Render all regions
- * We always transform non absolute subtitle into absolute one on the
- * first rendering to allow good subtitle overlap support.
- */
- for( p_region = p_subpic->p_region; p_region != NULL; p_region = p_region->p_next )
- {
- spu_area_t area;
-
- /* Check scale validity */
- if( scale.w <= 0 || scale.h <= 0 )
- continue;
-
- /* */
- SpuRenderRegion( p_spu, p_pic_dst, &area,
- p_subpic, p_region, scale, p_fmt_dst,
- p_subtitle_area, i_subtitle_area );
-
- if( p_subpic->b_subtitle )
- {
- area = spu_area_unscaled( area, scale );
- if( !p_subpic->b_absolute && area.i_width > 0 && area.i_height > 0 )
- {
- p_region->i_x = area.i_x;
- p_region->i_y = area.i_y;
- }
- if( p_subtitle_area )
- p_subtitle_area[i_subtitle_area++] = area;
- }
- }
- if( p_subpic->b_subtitle )
- p_subpic->b_absolute = true;
- }
-
- /* */
- if( p_subtitle_area != p_subtitle_area_buffer )
- free( p_subtitle_area );
-
- vlc_mutex_unlock( &p_sys->lock );
-}
-
-/*****************************************************************************
- * spu_SortSubpictures: find the subpictures to display
- *****************************************************************************
- * This function parses all subpictures and decides which ones need to be
- * displayed. If no picture has been selected, display_date will depend on
- * the subpicture.
- * We also check for ephemer DVD subpictures (subpictures that have
- * 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 *spu_SortSubpictures( spu_t *p_spu, mtime_t display_date,
- bool b_paused, bool b_subtitle_only )
-{
- spu_private_t *p_sys = p_spu->p;
- int i_channel;
- subpicture_t *p_subpic = NULL;
-
- /* Update sub-filter chain */
- vlc_mutex_lock( &p_sys->lock );
- char *psz_chain_update = p_sys->psz_chain_update;
- p_sys->psz_chain_update = NULL;
- vlc_mutex_unlock( &p_sys->lock );
-
- if( psz_chain_update )
- {
- filter_chain_Reset( p_sys->p_chain, NULL, NULL );
-
- filter_chain_AppendFromString( p_spu->p->p_chain, psz_chain_update );
-
- free( psz_chain_update );
- }
-
- /* Run subpicture filters */
- filter_chain_SubFilter( p_sys->p_chain, display_date );
-
- vlc_mutex_lock( &p_sys->lock );
-
- /* We get an easily parsable chained list of subpictures which
- * ends with NULL since p_subpic was initialized to NULL. */
- for( i_channel = 0; i_channel < p_sys->i_channel; i_channel++ )
- {
- subpicture_t *p_available_subpic[VOUT_MAX_SUBPICTURES];
- bool pb_available_late[VOUT_MAX_SUBPICTURES];
- int i_available = 0;
-
- mtime_t start_date = display_date;
- mtime_t ephemer_date = 0;
- int i_index;
-
- /* Select available pictures */
- for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
- {
- spu_heap_entry_t *p_entry = &p_sys->heap.p_entry[i_index];
- subpicture_t *p_current = p_entry->p_subpicture;
- bool b_stop_valid;
- bool b_late;
-
- if( !p_current || p_entry->b_reject )
- {
- if( p_entry->b_reject )
- SpuHeapDeleteAt( &p_sys->heap, i_index );
- continue;
- }
-
- if( p_current->i_channel != i_channel ||
- ( b_subtitle_only && !p_current->b_subtitle ) )
- {
- continue;
- }
- if( display_date &&
- display_date < p_current->i_start )
- {
- /* Too early, come back next monday */
- continue;
- }
-
- if( p_current->i_start > ephemer_date )
- ephemer_date = p_current->i_start;
-
- b_stop_valid = ( !p_current->b_ephemer || p_current->i_stop > p_current->i_start ) &&
- ( !p_current->b_subtitle || !b_paused ); /* XXX Assume that subtitle are pausable */
-
- b_late = b_stop_valid && p_current->i_stop <= display_date;
-
- /* start_date will be used for correct automatic overlap support
- * in case picture that should not be displayed anymore (display_time)
- * overlap with a picture to be displayed (p_current->i_start) */
- if( !b_late && !p_current->b_ephemer )
- start_date = p_current->i_start;
-
- /* */
- p_available_subpic[i_available] = p_current;
- pb_available_late[i_available] = b_late;
- i_available++;
- }
-
- /* Only forced old picture display at the transition */
- if( start_date < p_sys->i_last_sort_date )
- start_date = p_sys->i_last_sort_date;
- if( start_date <= 0 )
- start_date = INT64_MAX;
-
- /* Select pictures to be displayed */
- for( i_index = 0; i_index < i_available; i_index++ )
- {
- subpicture_t *p_current = p_available_subpic[i_index];
- bool b_late = pb_available_late[i_index];
-
- if( ( b_late && p_current->i_stop <= __MAX( start_date, p_sys->i_last_sort_date ) ) ||
- ( p_current->b_ephemer && p_current->i_start < ephemer_date ) )
- {
- /* Destroy late and obsolete ephemer subpictures */
- SpuHeapDeleteSubpicture( &p_sys->heap, p_current );
- }
- else
- {
- SubpictureChain( &p_subpic, p_current );
- }
- }
- }
-
- p_sys->i_last_sort_date = display_date;
- vlc_mutex_unlock( &p_sys->lock );
-
- return p_subpic;
-}
-
-void spu_OffsetSubtitleDate( spu_t *p_spu, mtime_t i_duration )
-{
- spu_private_t *p_sys = p_spu->p;
-
- vlc_mutex_lock( &p_sys->lock );
- for( int i = 0; i < VOUT_MAX_SUBPICTURES; i++ )
- {
- spu_heap_entry_t *p_entry = &p_sys->heap.p_entry[i];
- subpicture_t *p_current = p_entry->p_subpicture;
-
- if( p_current && p_current->b_subtitle )
- {
- if( p_current->i_start > 0 )
- p_current->i_start += i_duration;
- if( p_current->i_stop > 0 )
- p_current->i_stop += i_duration;
- }
- }
- vlc_mutex_unlock( &p_sys->lock );
-}
-
-/*****************************************************************************
- * subpicture_t allocation
- *****************************************************************************/
-subpicture_t *subpicture_New( void )
-{
- subpicture_t *p_subpic = calloc( 1, sizeof(*p_subpic) );
- if( !p_subpic )
- return NULL;
-
- p_subpic->i_order = 0;
- p_subpic->b_absolute = true;
- p_subpic->b_fade = false;
- p_subpic->b_subtitle = false;
- p_subpic->i_alpha = 0xFF;
- p_subpic->p_region = NULL;
- p_subpic->pf_render = NULL;
- p_subpic->pf_destroy = NULL;
- p_subpic->p_sys = NULL;
-
- return p_subpic;
-}
-
-void subpicture_Delete( subpicture_t *p_subpic )
-{
- subpicture_region_ChainDelete( p_subpic->p_region );
- p_subpic->p_region = NULL;
-
- if( p_subpic->pf_destroy )
- {
- p_subpic->pf_destroy( p_subpic );
- }
- free( p_subpic );
-}
-
-static void SubpictureChain( subpicture_t **pp_head, subpicture_t *p_subpic )
-{
- p_subpic->p_next = *pp_head;
-
- *pp_head = p_subpic;
-}
-
-/*****************************************************************************
- * subpicture_region_t allocation
- *****************************************************************************/
-subpicture_region_t *subpicture_region_New( const video_format_t *p_fmt )
-{
- subpicture_region_t *p_region = calloc( 1, sizeof(*p_region ) );
- if( !p_region )
- return NULL;
-
- p_region->fmt = *p_fmt;
- p_region->fmt.p_palette = NULL;
- if( p_fmt->i_chroma == VLC_CODEC_YUVP )
- {
- p_region->fmt.p_palette = calloc( 1, sizeof(*p_region->fmt.p_palette) );
- if( p_fmt->p_palette )
- *p_region->fmt.p_palette = *p_fmt->p_palette;
- }
- p_region->i_alpha = 0xff;
- p_region->p_next = NULL;
- p_region->p_private = NULL;
- p_region->psz_text = NULL;
- p_region->p_style = NULL;
- p_region->p_picture = NULL;
-
- if( p_fmt->i_chroma == VLC_CODEC_TEXT )
- return p_region;
-
- p_region->p_picture = picture_New( p_fmt->i_chroma, p_fmt->i_width, p_fmt->i_height,
- p_fmt->i_aspect );
- if( !p_region->p_picture )
- {
- free( p_fmt->p_palette );
- free( p_region );
- return NULL;
- }
-
- return p_region;
-}
-
-/* */
-void subpicture_region_Delete( subpicture_region_t *p_region )
-{
- if( !p_region )
- return;
-
- if( p_region->p_private )
- SpuRegionPrivateDelete( p_region->p_private );
-
- if( p_region->p_picture )
- picture_Release( p_region->p_picture );
-
- free( p_region->fmt.p_palette );
-
- free( p_region->psz_text );
- free( p_region->psz_html );
- if( p_region->p_style )
- text_style_Delete( p_region->p_style );
- free( p_region );
-}
-
-/* */
-void subpicture_region_ChainDelete( subpicture_region_t *p_head )
-{
- while( p_head )
- {
- subpicture_region_t *p_next = p_head->p_next;
-
- subpicture_region_Delete( p_head );
-
- p_head = p_next;
- }
-}
-
-