SCALE_SIZE
};
+#define SCALE_UNIT (1000)
+
static void FilterRelease( filter_t *p_filter )
{
if( p_filter->p_module )
memset( p_subpic, 0, sizeof(subpicture_t) );
p_subpic->i_status = RESERVED_SUBPICTURE;
p_subpic->b_absolute = true;
- p_subpic->b_pausable = false;
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_region->i_align |= SUBPICTURE_RENDERED;
}
+/**
+ * Place a region
+ */
+static void SpuRegionPlace( int *pi_x, int *pi_y,
+ const video_format_t *p_fmt,
+ const subpicture_t *p_subpic,
+ const subpicture_region_t *p_region,
+ int i_inv_scale_x, int i_inv_scale_y )
+{
+ int i_delta_x = p_region->i_x * i_inv_scale_x / SCALE_UNIT;
+ int i_delta_y = p_region->i_y * i_inv_scale_y / SCALE_UNIT;
+ int i_x, i_y;
+
+ if( p_region->i_align & SUBPICTURE_ALIGN_TOP )
+ {
+ i_y = i_delta_y;
+ }
+ else if( p_region->i_align & SUBPICTURE_ALIGN_BOTTOM )
+ {
+ i_y = p_fmt->i_height - p_region->fmt.i_height - i_delta_y;
+ }
+ else
+ {
+ i_y = p_fmt->i_height / 2 - p_region->fmt.i_height / 2;
+ }
+
+ if( p_region->i_align & SUBPICTURE_ALIGN_LEFT )
+ {
+ i_x = i_delta_x;
+ }
+ else if( p_region->i_align & SUBPICTURE_ALIGN_RIGHT )
+ {
+ i_x = p_fmt->i_width - p_region->fmt.i_width - i_delta_x;
+ }
+ else
+ {
+ i_x = p_fmt->i_width / 2 - p_region->fmt.i_width / 2;
+ }
+
+ if( p_subpic->b_absolute )
+ {
+ i_x = i_delta_x;
+ i_y = i_delta_y;
+ }
+
+ *pi_x = __MAX( i_x, 0 );
+ *pi_y = __MAX( i_y, 0 );
+}
+
static void SpuRenderRegion( spu_t *p_spu,
- picture_t *p_pic_dst, picture_t *p_pic_src,
+ picture_t *p_pic_dst,
subpicture_t *p_subpic, subpicture_region_t *p_region,
const int i_scale_width_orig, const int i_scale_height_orig,
- const int pi_subpic_x[SCALE_SIZE],
const int pi_scale_width[SCALE_SIZE],
const int pi_scale_height[SCALE_SIZE],
const video_format_t *p_fmt )
else
{
i_scale_idx = SCALE_DEFAULT;
- i_inv_scale_x = 1000;
- i_inv_scale_y = 1000;
+ i_inv_scale_x = SCALE_UNIT;
+ i_inv_scale_y = SCALE_UNIT;
}
- i_x_offset = (p_region->i_x + pi_subpic_x[ i_scale_idx ]) * i_inv_scale_x / 1000;
- i_y_offset = (p_region->i_y + p_subpic->i_y) * i_inv_scale_y / 1000;
-
/* Force palette if requested
* FIXME b_force_palette and b_force_crop are applied to all subpictures using palette
* instead of only the right one (being the dvd spu).
p_scale = p_spu->p_scale;
if( p_scale &&
- ( ( pi_scale_width[i_scale_idx] > 0 && pi_scale_width[i_scale_idx] != 1000 ) ||
- ( pi_scale_height[i_scale_idx] > 0 && pi_scale_height[i_scale_idx] != 1000 ) ||
+ ( ( pi_scale_width[i_scale_idx] > 0 && pi_scale_width[i_scale_idx] != SCALE_UNIT ) ||
+ ( pi_scale_height[i_scale_idx] > 0 && pi_scale_height[i_scale_idx] != SCALE_UNIT ) ||
( b_force_palette ) ) )
{
- const unsigned i_dst_width = p_region->fmt.i_width * pi_scale_width[i_scale_idx] / 1000;
- const unsigned i_dst_height = p_region->fmt.i_height * pi_scale_height[i_scale_idx] / 1000;
+ const unsigned i_dst_width = p_region->fmt.i_width * pi_scale_width[i_scale_idx] / SCALE_UNIT;
+ const unsigned i_dst_height = p_region->fmt.i_height * pi_scale_height[i_scale_idx] / SCALE_UNIT;
/* Destroy if cache is unusable */
if( p_region->p_cache )
p_scale->fmt_out.video.i_height = i_dst_height;
p_scale->fmt_out.video.i_visible_width =
- p_region->fmt.i_visible_width * pi_scale_width[ i_scale_idx ] / 1000;
+ p_region->fmt.i_visible_width * pi_scale_width[ i_scale_idx ] / SCALE_UNIT;
p_scale->fmt_out.video.i_visible_height =
- p_region->fmt.i_visible_height * pi_scale_height[ i_scale_idx ] / 1000;
+ p_region->fmt.i_visible_height * pi_scale_height[ i_scale_idx ] / SCALE_UNIT;
p_region->p_cache->fmt = p_scale->fmt_out.video;
- p_region->p_cache->i_x = p_region->i_x * pi_scale_width[ i_scale_idx ] / 1000;
- p_region->p_cache->i_y = p_region->i_y * pi_scale_height[ i_scale_idx ] / 1000;
+ p_region->p_cache->i_x = p_region->i_x * pi_scale_width[ i_scale_idx ] / SCALE_UNIT;
+ p_region->p_cache->i_y = p_region->i_y * pi_scale_height[ i_scale_idx ] / SCALE_UNIT;
p_region->p_cache->i_align = p_region->i_align;
p_region->p_cache->i_alpha = p_region->i_alpha;
p_pic = p_scale->pf_video_filter( p_scale, &p_region->p_cache->picture );
else
msg_Err( p_spu, "scaling failed (module not loaded)" );
+
if( p_pic )
{
p_region->p_cache->picture = *p_pic;
free( p_pic );
}
+ else
+ {
+ p_subpic->pf_destroy_region( VLC_OBJECT(p_spu),
+ p_region->p_cache );
+ p_region->p_cache = NULL;
+ }
}
/* And use the scaled picture */
}
}
- 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_region->i_y) * i_inv_scale_y / 1000;
- }
- 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_region->i_align & SUBPICTURE_ALIGN_RIGHT )
- {
- i_x_offset = p_fmt->i_width - p_region->fmt.i_width -
- (pi_subpic_x[ i_scale_idx ] + p_region->i_x)
- * i_inv_scale_x / 1000;
- }
- else if ( !(p_region->i_align & 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 +
- pi_subpic_x[ i_scale_idx ] *
- pi_scale_width[ i_scale_idx ] / 1000)
- * i_inv_scale_x / 1000;
- i_y_offset = (p_region->i_y +
- p_subpic->i_y * pi_scale_height[ i_scale_idx ] / 1000)
- * i_inv_scale_y / 1000;
-
- }
-
- i_x_offset = __MAX( i_x_offset, 0 );
- i_y_offset = __MAX( i_y_offset, 0 );
+ /* */
+ SpuRegionPlace( &i_x_offset, &i_y_offset,
+ p_fmt, p_subpic, p_region,
+ i_inv_scale_x, i_inv_scale_y );
if( p_spu->i_margin != 0 && !b_force_crop )
{
int i_diff = 0;
- int i_low = (i_y_offset - p_spu->i_margin) * i_inv_scale_y / 1000;
+ int i_low = (i_y_offset - p_spu->i_margin) * i_inv_scale_y / SCALE_UNIT;
int i_high = i_low + p_region->fmt.i_height;
/* crop extra margin to keep within bounds */
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_inv_scale_y / 1000 + i_diff );
+ i_y_offset -= ( p_spu->i_margin * i_inv_scale_y / SCALE_UNIT + i_diff );
}
/* Force cropping if requested */
if( b_force_crop )
{
video_format_t *p_fmt = &p_region->fmt;
- int i_crop_x = p_spu->i_crop_x * pi_scale_width[ i_scale_idx ] / 1000
- * i_inv_scale_x / 1000;
- int i_crop_y = p_spu->i_crop_y * pi_scale_height[ i_scale_idx ] / 1000
- * i_inv_scale_y / 1000;
- int i_crop_width = p_spu->i_crop_width * pi_scale_width[ i_scale_idx ] / 1000
- * i_inv_scale_x / 1000;
- int i_crop_height = p_spu->i_crop_height * pi_scale_height[ i_scale_idx ] / 1000
- * i_inv_scale_y / 1000;
+ int i_crop_x = p_spu->i_crop_x * pi_scale_width[ i_scale_idx ] / SCALE_UNIT
+ * i_inv_scale_x / SCALE_UNIT;
+ int i_crop_y = p_spu->i_crop_y * pi_scale_height[ i_scale_idx ] / SCALE_UNIT
+ * i_inv_scale_y / SCALE_UNIT;
+ int i_crop_width = p_spu->i_crop_width * pi_scale_width[ i_scale_idx ] / SCALE_UNIT
+ * i_inv_scale_x / SCALE_UNIT;
+ int i_crop_height = p_spu->i_crop_height * pi_scale_height[ i_scale_idx ] / SCALE_UNIT
+ * i_inv_scale_y / SCALE_UNIT;
/* Find the intersection */
if( i_crop_x + i_crop_width <= i_x_offset ||
if( p_spu->p_blend->p_module )
{
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,
+ &p_region->picture, i_x_offset, i_y_offset,
i_fade_alpha * p_subpic->i_alpha * p_region->i_alpha / 65025 );
}
else
}
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,
+ picture_t *p_pic_dst,
+ subpicture_t *p_subpic_list,
int i_scale_width_orig, int i_scale_height_orig )
{
+ const mtime_t i_current_date = mdate();
int i_source_video_width;
int i_source_video_height;
- subpicture_t *p_subpic_v;
+ subpicture_t *p_subpic;
/* Get lock */
vlc_mutex_lock( &p_spu->subpicture_lock );
- for( p_subpic_v = p_subpic;
- p_subpic_v != NULL && p_subpic_v->i_status != FREE_SUBPICTURE;
- p_subpic_v = p_subpic_v->p_next )
- {
- if( p_subpic_v->pf_pre_render )
- {
- p_subpic_v->pf_pre_render( p_fmt, p_spu, p_subpic_v, mdate() );
- }
- }
-
if( i_scale_width_orig <= 0 )
- i_scale_width_orig = 1000;
+ i_scale_width_orig = SCALE_UNIT;
if( i_scale_height_orig <= 0 )
- i_scale_height_orig = 1000;
+ i_scale_height_orig = SCALE_UNIT;
- i_source_video_width = p_fmt->i_width * 1000 / i_scale_width_orig;
- i_source_video_height = p_fmt->i_height * 1000 / i_scale_height_orig;
+ i_source_video_width = p_fmt->i_width * SCALE_UNIT / i_scale_width_orig;
+ i_source_video_height = p_fmt->i_height * SCALE_UNIT / i_scale_height_orig;
/* Check i_status again to make sure spudec hasn't destroyed the subpic */
- for( ; ( p_subpic != NULL ) && ( p_subpic->i_status != FREE_SUBPICTURE ); p_subpic = p_subpic->p_next )
+ for( p_subpic = p_subpic_list;
+ p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE;
+ p_subpic = p_subpic->p_next )
{
- subpicture_region_t *p_region;
- int pi_scale_width[ SCALE_SIZE ];
- int pi_scale_height[ SCALE_SIZE ];
- int pi_subpic_x[ SCALE_SIZE ];
- int k;
-
/* If the source video and subtitles stream agree on the size of
* the video then disregard all further references to the subtitle
* stream.
p_subpic->i_original_picture_width = 0;
}
- for( k = 0; k < SCALE_SIZE ; k++ )
- pi_subpic_x[ k ] = p_subpic->i_x;
+ /* */
+ if( p_subpic->pf_pre_render )
+ p_subpic->pf_pre_render( p_fmt, p_spu, p_subpic );
if( p_subpic->pf_update_regions )
{
- /* FIXME that part look like crap too if there is more than 1 region */
-
- if( p_subpic->p_region )
- spu_DestroyRegion( p_spu, p_subpic->p_region );
-
/* TODO do not reverse the scaling that was done before calling
* spu_RenderSubpictures, just pass it along (or do it inside
* spu_RenderSubpictures) */
fmt_org.i_height =
fmt_org.i_visible_height = i_source_video_height;
- p_subpic->p_region = p_subpic->pf_update_regions( &fmt_org, p_spu, p_subpic, mdate() );
+ p_subpic->pf_update_regions( &fmt_org, p_spu, p_subpic, i_current_date );
}
+ }
- /* */
- p_region = p_subpic->p_region;
- if( !p_region )
- continue;
-
- /* Create the blending module */
- if( !p_spu->p_blend )
- SpuRenderCreateBlend( p_spu, p_fmt->i_chroma, p_fmt->i_aspect );
+ /* Create the blending module */
+ if( !p_spu->p_blend )
+ SpuRenderCreateBlend( p_spu, p_fmt->i_chroma, p_fmt->i_aspect );
+
+ /* Load the scaling module */
+ if( !p_spu->p_scale && !p_spu->p_scale_yuvp )
+ SpuRenderCreateAndLoadScale( p_spu );
+
+ /* Load the text rendering module; it is possible there is a
+ * text region somewhere in the subpicture other than the first
+ * element in the region list, so just load it anyway as we'll
+ * probably want it sooner or later. */
+ if( !p_spu->p_text )
+ SpuRenderCreateAndLoadText( p_spu, p_fmt->i_width, p_fmt->i_height );
+
+ /* Process all subpictures and regions */
+ for( p_subpic = p_subpic_list;
+ p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE;
+ p_subpic = p_subpic->p_next )
+ {
+ subpicture_region_t *p_region;
+ int pi_scale_width[ SCALE_SIZE ];
+ int pi_scale_height[ SCALE_SIZE ];
+ int k;
- /* Load the text rendering module; it is possible there is a
- * text region somewhere in the subpicture other than the first
- * element in the region list, so just load it anyway as we'll
- * probably want it sooner or later. */
- if( !p_spu->p_text )
- SpuRenderCreateAndLoadText( p_spu, p_fmt->i_width, p_fmt->i_height );
+ if( !p_subpic->p_region )
+ continue;
+ /* */
if( p_spu->p_text )
{
subpicture_region_t *p_text_region = p_subpic->p_region;
/* XXX for text:
* scale[] allows to pass from rendered size (by text module) to video output size */
- pi_scale_width[SCALE_TEXT] = p_fmt->i_width * 1000 /
+ pi_scale_width[SCALE_TEXT] = p_fmt->i_width * SCALE_UNIT /
p_spu->p_text->fmt_out.video.i_width;
- pi_scale_height[SCALE_TEXT]= p_fmt->i_height * 1000 /
+ pi_scale_height[SCALE_TEXT]= p_fmt->i_height * SCALE_UNIT /
p_spu->p_text->fmt_out.video.i_height;
}
else
{
/* Just set a value to avoid using invalid memory while looping over the array */
pi_scale_width[SCALE_TEXT] =
- pi_scale_height[SCALE_TEXT]= 1000;
+ pi_scale_height[SCALE_TEXT]= SCALE_UNIT;
}
/* XXX for default:
* scale[] allows to pass from native (either video or original) size to output size */
-
if( p_subpic->i_original_picture_height > 0 &&
p_subpic->i_original_picture_width > 0 )
{
- pi_scale_width[SCALE_DEFAULT] = p_fmt->i_width * 1000 / p_subpic->i_original_picture_width;
- pi_scale_height[SCALE_DEFAULT] = p_fmt->i_height * 1000 / p_subpic->i_original_picture_height;
+ pi_scale_width[SCALE_DEFAULT] = p_fmt->i_width * SCALE_UNIT / p_subpic->i_original_picture_width;
+ pi_scale_height[SCALE_DEFAULT] = p_fmt->i_height * SCALE_UNIT / p_subpic->i_original_picture_height;
}
else
{
}
}
- /* Set default subpicture aspect ratio */
+ /* 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 )
if( ( p_region->fmt.i_sar_num * p_fmt->i_sar_den ) !=
( p_region->fmt.i_sar_den * p_fmt->i_sar_num ) )
{
+ /* FIXME FIXME what about region->i_x/i_y ? */
for( k = 0; k < SCALE_SIZE; k++ )
{
- pi_scale_width[k] = pi_scale_width[ k ] *
+ pi_scale_width[k] = pi_scale_width[k] *
(int64_t)p_region->fmt.i_sar_num * p_fmt->i_sar_den /
p_region->fmt.i_sar_den / p_fmt->i_sar_num;
-
- pi_subpic_x[k] = p_subpic->i_x * pi_scale_width[ k ] / 1000;
}
}
- /* Load the scaling module when needed */
- if( !p_spu->p_scale )
- {
- bool b_scale_used = false;
-
- for( k = 0; k < SCALE_SIZE; k++ )
- {
- const int i_scale_w = pi_scale_width[k];
- const int i_scale_h = pi_scale_height[k];
- if( ( i_scale_w > 0 && i_scale_w != 1000 ) || ( i_scale_h > 0 && i_scale_h != 1000 ) )
- b_scale_used = true;
- }
-
- if( b_scale_used )
- SpuRenderCreateAndLoadScale( p_spu );
- }
-
+ /* Render all regions */
for( ; p_region != NULL; p_region = p_region->p_next )
- SpuRenderRegion( p_spu, p_pic_dst, p_pic_src,
+ SpuRenderRegion( p_spu, p_pic_dst,
p_subpic, p_region, i_scale_width_orig, i_scale_height_orig,
- pi_subpic_x, pi_scale_width, pi_scale_height,
+ pi_scale_width, pi_scale_height,
p_fmt );
}
* 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_paused, bool b_subtitle_only )
{
- int i_index, i_channel;
+ int i_channel;
subpicture_t *p_subpic = NULL;
- subpicture_t *p_ephemer;
- mtime_t ephemer_date;
/* Run subpicture filters */
filter_chain_SubFilter( p_spu->p_chain, display_date );
* ends with NULL since p_subpic was initialized to NULL. */
for( i_channel = 0; i_channel < p_spu->i_channel; i_channel++ )
{
- p_ephemer = 0;
- ephemer_date = 0;
+ subpicture_t *p_ephemer = NULL;
+ mtime_t ephemer_date = 0;
+ int i_index;
for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
{
- if( p_spu->p_subpicture[i_index].i_channel != i_channel ||
- p_spu->p_subpicture[i_index].i_status != READY_SUBPICTURE )
+ subpicture_t *p_current = &p_spu->p_subpicture[i_index];
+
+ if( p_current->i_channel != i_channel ||
+ p_current->i_status != READY_SUBPICTURE ||
+ ( b_subtitle_only && !p_current->b_subtitle ) )
{
continue;
}
if( display_date &&
- display_date < p_spu->p_subpicture[i_index].i_start )
+ display_date < p_current->i_start )
{
/* Too early, come back next monday */
continue;
}
- if( p_spu->p_subpicture[i_index].i_start > ephemer_date )
- ephemer_date = p_spu->p_subpicture[i_index].i_start;
+ if( p_current->i_start > ephemer_date )
+ ephemer_date = p_current->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 ) )
+ if( display_date > p_current->i_stop &&
+ ( !p_current->b_ephemer || p_current->i_stop > p_current->i_start ) &&
+ !( p_current->b_subtitle && b_paused ) ) /* XXX Assume that subtitle are pausable */
{
/* Too late, destroy the subpic */
spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] );
}
/* If this is an ephemer subpic, add it to our list */
- if( p_spu->p_subpicture[i_index].b_ephemer )
+ if( p_current->b_ephemer )
{
- p_spu->p_subpicture[i_index].p_next = p_ephemer;
- p_ephemer = &p_spu->p_subpicture[i_index];
+ p_current->p_next = p_ephemer;
+ p_ephemer = p_current;
continue;
}
- p_spu->p_subpicture[i_index].p_next = p_subpic;
- p_subpic = &p_spu->p_subpicture[i_index];
+ p_current->p_next = p_subpic;
+ p_subpic = p_current;
}
/* If we found ephemer subpictures, check if they have to be
{
case SPU_CHANNEL_REGISTER:
pi = (int *)va_arg( args, int * );
- if( pi ) *pi = p_spu->i_channel++;
+ vlc_mutex_lock( &p_spu->subpicture_lock );
+ if( pi )
+ *pi = p_spu->i_channel++;
+ vlc_mutex_unlock( &p_spu->subpicture_lock );
break;
case SPU_CHANNEL_CLEAR: