#include <vlc_filter.h>
#include <vlc_osd.h>
#include "../libvlc.h"
+#include "vout_internal.h"
#include <assert.h>
#include <limits.h>
/*****************************************************************************
* Local prototypes
*****************************************************************************/
+/* Number of simultaneous subpictures */
+#define VOUT_MAX_SUBPICTURES (VOUT_MAX_PICTURES)
+
#define VLC_FOURCC_YUVP VLC_FOURCC('Y','U','V','P')
#define VLC_FOURCC_YUVA VLC_FOURCC('Y','U','V','A')
#define VLC_FOURCC_RGBA VLC_FOURCC('R','G','B','A')
bool b_force_palette; /**< force palette of subpicture */
uint8_t palette[4][4]; /**< forced palette */
- /* Supciture filters */
+ /* Subpiture filters */
filter_chain_t *p_chain;
+
+ /* */
+ mtime_t i_last_sort_date;
};
/* */
static int SpuControl( spu_t *, int, va_list );
-static void SpuClearChannel( spu_t *p_spu, int i_channel, bool b_locked );
+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 * );
{
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" );
SpuRenderCreateAndLoadText( p_spu );
SpuRenderCreateAndLoadScale( p_spu );
+ /* */
+ p_sys->i_last_sort_date = -1;
+
return p_spu;
}
/* DEFAULT_CHAN always reset itself */
if( p_subpic->i_channel == DEFAULT_CHAN )
- SpuClearChannel( p_spu, DEFAULT_CHAN, false );
+ 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 )
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 )
+ const video_format_t *p_fmt_src, bool b_paused )
{
spu_private_t *p_sys = p_spu->p;
p_subpic = p_subpic->p_next )
{
/* */
- if( p_subpic->pf_pre_render )
+ if( !b_paused && p_subpic->pf_pre_render )
p_subpic->pf_pre_render( p_spu, p_subpic, p_fmt_dst );
- if( p_subpic->pf_update_regions )
+ if( !b_paused && p_subpic->pf_update_regions )
{
video_format_t fmt_org = *p_fmt_dst;
fmt_org.i_width =
* 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_ephemer = NULL;
+ 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_current->i_start > ephemer_date )
ephemer_date = p_current->i_start;
- 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 */
- {
- SpuHeapDeleteAt( &p_sys->heap, i_index );
- }
- else if( p_current->b_ephemer )
- {
- SubpictureChain( &p_ephemer, p_current );
- }
- else
- {
- SubpictureChain( &p_subpic, p_current );
- }
+ 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++;
}
- /* If we found ephemer subpictures, check if they have to be
- * displayed or destroyed */
- while( p_ephemer != NULL )
+ /* 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_tmp = p_ephemer;
- p_ephemer = p_ephemer->p_next;
+ subpicture_t *p_current = p_available_subpic[i_index];
+ bool b_late = pb_available_late[i_index];
- if( p_tmp->i_start < ephemer_date )
+ 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 ) )
{
- SpuHeapDeleteSubpicture( &p_sys->heap, p_tmp );
+ /* Destroy late and obsolete ephemer subpictures */
+ SpuHeapDeleteSubpicture( &p_sys->heap, p_current );
}
else
{
- SubpictureChain( &p_subpic, p_tmp );
+ 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
*****************************************************************************/
/* */
if( !p_blend->p_module )
- p_blend->p_module = module_need( p_blend, "video blending", 0, 0 );
+ p_blend->p_module = module_need( p_blend, "video blending", NULL, false );
}
static void SpuRenderCreateAndLoadText( spu_t *p_spu )
{
p_scale->pf_vout_buffer_del = spu_del_video_buffer;
vlc_object_attach( p_scale, p_obj );
- p_scale->p_module = module_need( p_scale, "video filter2", 0, 0 );
+ p_scale->p_module = module_need( p_scale, "video filter2", NULL, false );
return p_scale;
}
}
/* Scale if needed into cache */
- if( !p_region->p_private )
+ if( !p_region->p_private && i_dst_width > 0 && i_dst_height > 0 )
{
filter_t *p_scale = p_sys->p_scale;
* pre-rendered state, so the next time through everything is
* calculated again.
*/
- picture_Release( p_region->p_picture );
- p_region->p_picture = NULL;
+ if( p_region->p_picture )
+ {
+ picture_Release( p_region->p_picture );
+ p_region->p_picture = NULL;
+ }
if( p_region->p_private )
{
SpuRegionPrivateDelete( p_region->p_private );
return i0 < i1 ? -1 : i0 > i1 ? 1 : 0;
}
/**
- * This function compares 2 subpictures using the following properties
+ * This function compares 2 subpictures using the following properties
* (ordered by priority)
* 1. absolute positionning
* 2. start time
* This function destroys the subpictures which belong to the spu channel
* corresponding to i_channel_id.
*****************************************************************************/
-static void SpuClearChannel( spu_t *p_spu, int i_channel, bool b_locked )
+static void SpuClearChannel( spu_t *p_spu, int i_channel )
{
spu_private_t *p_sys = p_spu->p;
int i_subpic; /* subpicture index */
- if( !b_locked )
- vlc_mutex_lock( &p_sys->lock );
-
- vlc_assert_locked( &p_sys->lock );
+ vlc_mutex_lock( &p_sys->lock );
for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
{
p_entry->b_reject = true;
}
- if( !b_locked )
- vlc_mutex_unlock( &p_sys->lock );
+ vlc_mutex_unlock( &p_sys->lock );
}
/*****************************************************************************
case SPU_CHANNEL_CLEAR:
i = (int)va_arg( args, int );
- SpuClearChannel( p_spu, i, false );
+ SpuClearChannel( p_spu, i );
break;
default:
{
filter_owner_sys_t *p_sys = p_filter->p_owner;
- SpuClearChannel( p_sys->p_spu, p_sys->i_channel, true );
+ SpuClearChannel( p_sys->p_spu, p_sys->i_channel );
free( p_filter->p_owner );
}