Commit 036ee285 authored by Yuval Tze's avatar Yuval Tze Committed by Rémi Denis-Courmont

Subsdelay (new sub-filter)

sub-filter that enables extending subtitles delay
Signed-off-by: Rémi Denis-Courmont's avatarRémi Denis-Courmont <remi@remlab.net>
parent a498397a
......@@ -76,6 +76,7 @@ SOURCES_atmo = atmo/atmo.cpp \
atmo/FnordlichtConnection.cpp atmo/FnordlichtConnection.h \
atmo/AtmoPacketQueue.cpp atmo/AtmoPacketQueue.h
SOURCES_gradfun = gradfun.c gradfun.h
SOURCES_subsdelay = subsdelay.c
noinst_HEADERS = filter_picture.h
libvlc_LTLIBRARIES += \
......@@ -117,6 +118,7 @@ libvlc_LTLIBRARIES += \
libscene_plugin.la \
libsepia_plugin.la \
libsharpen_plugin.la \
libsubsdelay_plugin.la \
libtransform_plugin.la \
libwall_plugin.la \
libwave_plugin.la \
......
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_filter.h>
#include <vlc_subpicture.h>
#include <vlc_osd.h>
#include <vlc_es.h>
#include <stdlib.h>
/*****************************************************************************
* Local constants
*****************************************************************************/
/* descriptions */
#define SUBSDELAY_HELP N_("Change subtitles delay")
#define MODE_TEXT N_( "Delay calculation mode" )
#define MODE_LONGTEXT N_( \
"Absolute delay - add absolute delay to each subtitle. " \
"Relative to source delay - multiply subtitle delay. " \
"Relative to source content - determine subtitle delay from its content (text)." )
#define FACTOR_TEXT N_( "Calculation factor" )
#define FACTOR_LONGTEXT N_( "Calculation factor. " \
"In Absolute delay mode the factor represents seconds.")
#define OVERLAP_TEXT N_( "Maximum overlapping subtitles" )
#define OVERLAP_LONGTEXT N_( "Maximum number of subtitles allowed at the same time." )
#define MIN_ALPHA_TEXT N_( "Minimum alpha value" )
#define MIN_ALPHA_LONGTEXT N_( \
"Alpha value of the earliest subtitle, where 0 is fully transparent and 255 is fully opaque." )
#define MIN_STOPS_INTERVAL_TEXT N_( "Interval between two disappearances" )
#define MIN_STOPS_INTERVAL_LONGTEXT N_( \
"Minimum time (in milliseconds) that subtitle should stay after its predecessor has disappeared " \
"(subtitle delay will be extended to meet this requirement)." )
#define MIN_STOP_START_INTERVAL_TEXT N_( "Interval between disappearance and appearance" )
#define MIN_STOP_START_INTERVAL_LONGTEXT N_( \
"Minimum time (in milliseconds) between subtitle disappearance and newer subtitle appearance " \
"(earlier subtitle delay will be extended to fill the gap)." )
#define MIN_START_STOP_INTERVAL_TEXT N_( "Interval between appearance and disappearance" )
#define MIN_START_STOP_INTERVAL_LONGTEXT N_( \
"Minimum time (in milliseconds) that subtitle should stay after newer subtitle has appeared " \
"(earlier subtitle delay will be shortened to avoid the overlap)." )
static const int pi_mode_values[] = { 0, 1, 2 };
static const char * const ppsz_mode_descriptions[] = { N_( "Absolute delay" ), N_( "Relative to source delay" ), N_(
"Relative to source content" ) };
/* parameters */
#define CFG_PREFIX "subsdelay-"
#define CFG_MODE CFG_PREFIX "mode"
#define CFG_FACTOR CFG_PREFIX "factor"
#define CFG_OVERLAP CFG_PREFIX "overlap"
#define CFG_MIN_ALPHA CFG_PREFIX "min-alpha"
#define CFG_MIN_STOPS_INTERVAL CFG_PREFIX "min-stops"
#define CFG_MIN_STOP_START_INTERVAL CFG_PREFIX "min-stop-start"
#define CFG_MIN_START_STOP_INTERVAL CFG_PREFIX "min-start-stop"
/* max subtitles handled on the heap */
#define SUBSDELAY_MAX_ENTRIES 16
/* factor convert macros */
#define INT_FACTOR_BASE 1000
#define FLOAT_FACTOR_TO_INT_FACTOR( x ) (int)( ( x ) * INT_FACTOR_BASE )
#define INT_FACTOR_TO_MICROSEC( x ) ( ( x ) * ( 1000000 / INT_FACTOR_BASE ) )
#define INT_FACTOR_TO_RANK_FACTOR( x ) ( x )
#define MILLISEC_TO_MICROSEC( x ) ( ( x ) * 1000 )
#define SUBSDELAY_MODE_ABSOLUTE 0
#define SUBSDELAY_MODE_RELATIVE_SOURCE_DELAY 1
#define SUBSDELAY_MODE_RELATIVE_SOURCE_CONTENT 2
/*****************************************************************************
* subsdelay_heap_entry_t: Heap entry
*****************************************************************************/
typedef subpicture_updater_sys_t subsdelay_heap_entry_t;
struct subpicture_updater_sys_t
{
subpicture_t *p_subpic; /* local subtitle */
subpicture_t *p_source; /* subtitle source */
filter_t *p_filter; /* assigned subsdelay filter */
subsdelay_heap_entry_t *p_next; /* next entry */
bool b_update_stop; /* new stop value should be calculated */
bool b_update_ephemer; /* actual stop value is unknown */
bool b_update_position; /* subtitle position should be updated */
bool b_check_empty; /* subtitle content should be checked */
mtime_t i_new_stop; /* new stop value */
/* last region data*/
int i_last_region_x;
int i_last_region_y;
int i_last_region_align;
bool b_last_region_saved;
};
/*****************************************************************************
* subsdelay_heap_t: Heap
*****************************************************************************/
typedef struct
{
vlc_mutex_t lock; /* heap global lock */
subsdelay_heap_entry_t *p_list[SUBSDELAY_MAX_ENTRIES]; /* subtitles entries array */
subsdelay_heap_entry_t *p_head; /* subtitles entries linked list */
int i_count; /* subtitles count */
} subsdelay_heap_t;
/*****************************************************************************
* filter_sys_t: Subsdelay filter descriptor
*****************************************************************************/
struct filter_sys_t
{
int i_mode; /* delay calculation mode */
int i_factor; /* calculation factor */
int i_overlap; /* max overlap */
int i_min_alpha; /* oldest subtitle alpha value */
int64_t i_min_stops_interval;
int64_t i_min_stop_start_interval;
int64_t i_min_start_stop_interval;
subsdelay_heap_t heap; /* subpictures list */
};
/*****************************************************************************
* Filter functions
*****************************************************************************/
static int SubsdelayCreate( vlc_object_t * );
static void SubsdelayDestroy( vlc_object_t * );
static subpicture_t * SubsdelayFilter( filter_t *p_filter, subpicture_t* p_subpic );
static int SubsdelayCallback( vlc_object_t *p_this, char const *psz_var, vlc_value_t oldval, vlc_value_t newval,
void *p_data );
/*****************************************************************************
* Helper functions
*****************************************************************************/
static void SubsdelayEnforceDelayRules( filter_t *p_filter );
static int64_t SubsdelayEstimateDelay( filter_t *p_filter, subsdelay_heap_entry_t *p_entry );
static void SubsdelayRecalculateDelays( filter_t *p_filter );
static int SubsdelayCalculateAlpha( filter_t *p_filter, int i_overlapping, int i_source_alpha );
static int SubsdelayGetTextRank( char *psz_text );
static bool SubsdelayIsTextEmpty( char *psz_text );
/*****************************************************************************
* Subpicture functions
*****************************************************************************/
static int SubpicValidateWrapper( subpicture_t *p_subpic, bool has_src_changed, const video_format_t *p_fmt_src,
bool has_dst_changed, const video_format_t *p_fmt_dst, mtime_t i_ts );
static void SubpicUpdateWrapper( subpicture_t *p_subpic, const video_format_t *p_fmt_src,
const video_format_t *p_fmt_dst, mtime_t i_ts );
static void SubpicDestroyWrapper( subpicture_t *p_subpic );
static void SubpicLocalUpdate( subpicture_t* p_subpic, mtime_t i_ts );
static bool SubpicIsEmpty( subpicture_t* p_subpic );
static subpicture_t *SubpicClone( subpicture_t *p_source, subpicture_updater_t *updater );
static void SubpicDestroyClone( subpicture_t *p_subpic );
/*****************************************************************************
* Heap functions
*****************************************************************************/
static void SubsdelayHeapInit( subsdelay_heap_t *p_heap );
static void SubsdelayHeapDestroy( subsdelay_heap_t *p_heap );
static subsdelay_heap_entry_t *SubsdelayHeapPush( subsdelay_heap_t *p_heap, subpicture_t *p_subpic, filter_t *p_filter );
static void SubsdelayHeapRemove( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry );
static void SubsdelayRebuildList( subsdelay_heap_t *p_heap );
static void SubsdelayHeapLock( subsdelay_heap_t *p_heap );
static void SubsdelayHeapUnlock( subsdelay_heap_t *p_heap );
static subsdelay_heap_entry_t * SubsdelayEntryCreate( subpicture_t *p_subpic, filter_t *p_filter );
static void SubsdelayEntryDestroy( subsdelay_heap_entry_t *p_entry );
/* heap / entries special functionality */
static int SubsdelayHeapCountOverlap( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry, mtime_t i_date );
static void SubsdelayEntryNewStopValueUpdated( subsdelay_heap_entry_t *p_entry );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin()
set_shortname( _("Subsdelay") )
set_description( _("Subtitles delay") )
set_help( SUBSDELAY_HELP )
set_capability( "sub filter", 0 )
set_callbacks( SubsdelayCreate, SubsdelayDestroy )
set_category( CAT_VIDEO )
set_subcategory( SUBCAT_VIDEO_SUBPIC )
add_integer( CFG_MODE, 1, MODE_TEXT, MODE_LONGTEXT, false )
change_integer_list( pi_mode_values, ppsz_mode_descriptions )
add_float_with_range( CFG_FACTOR, 2, 0, 20, NULL, FACTOR_TEXT, FACTOR_LONGTEXT, false )
add_integer_with_range( CFG_OVERLAP, 3, 1, 4, NULL, OVERLAP_TEXT, OVERLAP_LONGTEXT, false )
add_integer_with_range( CFG_MIN_ALPHA, 125, 0, 255, NULL, MIN_ALPHA_TEXT, MIN_ALPHA_LONGTEXT, false )
set_section( N_("Overlap fix"), NULL )
add_integer( CFG_MIN_STOPS_INTERVAL, 1000, MIN_STOPS_INTERVAL_TEXT, MIN_STOPS_INTERVAL_LONGTEXT, false )
add_integer( CFG_MIN_START_STOP_INTERVAL, 1000, MIN_START_STOP_INTERVAL_TEXT,
MIN_START_STOP_INTERVAL_LONGTEXT, false )
add_integer( CFG_MIN_STOP_START_INTERVAL, 1000, MIN_STOP_START_INTERVAL_TEXT,
MIN_STOP_START_INTERVAL_LONGTEXT, false )
vlc_module_end ()
static const char * const ppsz_filter_options[] = { "mode", "factor", "overlap", NULL };
/*****************************************************************************
* SubsdelayCreate: Create subsdelay filter
*****************************************************************************/
static int SubsdelayCreate( vlc_object_t *p_this )
{
filter_t *p_filter = (filter_t *) p_this;
filter_sys_t *p_sys;
/* allocate structure */
p_sys = (filter_sys_t*) malloc( sizeof(filter_sys_t) );
if( !p_sys )
{
return VLC_ENOMEM;
}
/* init parameters */
p_sys->i_mode = var_CreateGetIntegerCommand( p_filter, CFG_MODE );
var_AddCallback( p_filter, CFG_MODE, SubsdelayCallback, p_sys );
p_sys->i_factor = FLOAT_FACTOR_TO_INT_FACTOR( var_CreateGetFloatCommand( p_filter, CFG_FACTOR ) );
var_AddCallback( p_filter, CFG_FACTOR, SubsdelayCallback, p_sys );
p_sys->i_overlap = var_CreateGetIntegerCommand( p_filter, CFG_OVERLAP );
var_AddCallback( p_filter, CFG_OVERLAP, SubsdelayCallback, p_sys );
p_sys->i_min_alpha = var_CreateGetIntegerCommand( p_filter, CFG_MIN_ALPHA );
var_AddCallback( p_filter, CFG_MIN_ALPHA, SubsdelayCallback, p_sys );
p_sys->i_min_stops_interval
= MILLISEC_TO_MICROSEC( var_CreateGetIntegerCommand( p_filter, CFG_MIN_STOPS_INTERVAL ) );
var_AddCallback( p_filter, CFG_MIN_STOPS_INTERVAL, SubsdelayCallback, p_sys );
p_sys->i_min_stop_start_interval
= MILLISEC_TO_MICROSEC( var_CreateGetIntegerCommand( p_filter, CFG_MIN_STOP_START_INTERVAL ) );
var_AddCallback( p_filter, CFG_MIN_STOP_START_INTERVAL, SubsdelayCallback, p_sys );
p_sys->i_min_start_stop_interval
= MILLISEC_TO_MICROSEC( var_CreateGetIntegerCommand( p_filter, CFG_MIN_START_STOP_INTERVAL ) );
var_AddCallback( p_filter, CFG_MIN_START_STOP_INTERVAL, SubsdelayCallback, p_sys );
p_filter->p_sys = p_sys;
p_filter->pf_sub_filter = SubsdelayFilter;
config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options, p_filter->p_cfg );
SubsdelayHeapInit( &p_sys->heap );
return VLC_SUCCESS;
}
/*****************************************************************************
* SubsdelayDestroy: Destroy subsdelay filter
*****************************************************************************/
static void SubsdelayDestroy( vlc_object_t *p_this )
{
filter_t *p_filter = (filter_t *) p_this;
filter_sys_t *p_sys = p_filter->p_sys;
SubsdelayHeapDestroy( &p_sys->heap );
/* destroy parameters */
var_DelCallback( p_filter, CFG_MODE, SubsdelayCallback, p_sys );
var_Destroy( p_filter, CFG_MODE );
var_DelCallback( p_filter, CFG_FACTOR, SubsdelayCallback, p_sys );
var_Destroy( p_filter, CFG_FACTOR );
var_DelCallback( p_filter, CFG_OVERLAP, SubsdelayCallback, p_sys );
var_Destroy( p_filter, CFG_OVERLAP );
var_DelCallback( p_filter, CFG_MIN_ALPHA, SubsdelayCallback, p_sys );
var_Destroy( p_filter, CFG_MIN_ALPHA );
var_DelCallback( p_filter, CFG_MIN_STOPS_INTERVAL, SubsdelayCallback, p_sys );
var_Destroy( p_filter, CFG_MIN_STOPS_INTERVAL );
var_DelCallback( p_filter, CFG_MIN_STOP_START_INTERVAL, SubsdelayCallback, p_sys );
var_Destroy( p_filter, CFG_MIN_STOP_START_INTERVAL );
var_DelCallback( p_filter, CFG_MIN_START_STOP_INTERVAL, SubsdelayCallback, p_sys );
var_Destroy( p_filter, CFG_MIN_START_STOP_INTERVAL );
free( p_sys );
}
/*****************************************************************************
* SubsdelayFilter: Filter new subpicture
*****************************************************************************/
static subpicture_t * SubsdelayFilter( filter_t *p_filter, subpicture_t* p_subpic )
{
subsdelay_heap_t *p_heap;
subsdelay_heap_entry_t *p_entry;
if( !p_subpic->b_subtitle )
{
return p_subpic;
}
if( SubpicIsEmpty( p_subpic ) )
{
/* empty subtitles usually helps terminate ephemer subtitles, but this filter calculates the stop value anyway,
so this subtitle can be dropped */
subpicture_Delete( p_subpic );
return NULL;
}
p_heap = &p_filter->p_sys->heap;
/* add subpicture to the heap */
SubsdelayHeapLock( p_heap );
p_entry = SubsdelayHeapPush( p_heap, p_subpic, p_filter );
if( !p_entry )
{
SubsdelayHeapUnlock( p_heap );
msg_Err(p_filter, "Can't add subpicture to the heap");
return p_subpic;
}
p_subpic = p_entry->p_subpic; /* get the local subpic */
if( p_subpic->b_ephemer )
{
/* set a relativly long delay in hope that the next subtitle
will arrive in this time and the real delay could be determined */
p_subpic->i_stop = p_subpic->i_start + 20000000; /* start + 20 sec */
p_subpic->b_ephemer = false;
}
SubsdelayEnforceDelayRules( p_filter );
SubsdelayHeapUnlock( p_heap );
return p_subpic;
}
/*****************************************************************************
* SubsdelayCallback: Subsdelay parameters callback
*****************************************************************************/
static int SubsdelayCallback( vlc_object_t *p_this, char const *psz_var, vlc_value_t oldval, vlc_value_t newval,
void *p_data )
{
filter_sys_t *p_sys = (filter_sys_t *) p_data;
VLC_UNUSED( oldval );
SubsdelayHeapLock( &p_sys->heap );
if( !strcmp( psz_var, CFG_MODE ) )
{
p_sys->i_mode = newval.i_int;
}
else if( !strcmp( psz_var, CFG_FACTOR ) )
{
p_sys->i_factor = FLOAT_FACTOR_TO_INT_FACTOR( newval.f_float );
}
else if( !strcmp( psz_var, CFG_OVERLAP ) )
{
p_sys->i_overlap = newval.i_int;
}
else if( !strcmp( psz_var, CFG_MIN_ALPHA ) )
{
p_sys->i_min_alpha = newval.i_int;
}
else if( !strcmp( psz_var, CFG_MIN_STOPS_INTERVAL ) )
{
p_sys->i_min_stops_interval = MILLISEC_TO_MICROSEC( newval.i_int );
}
else if( !strcmp( psz_var, CFG_MIN_STOP_START_INTERVAL ) )
{
p_sys->i_min_stop_start_interval = MILLISEC_TO_MICROSEC( newval.i_int );
}
else if( !strcmp( psz_var, CFG_MIN_START_STOP_INTERVAL ) )
{
p_sys->i_min_start_stop_interval = MILLISEC_TO_MICROSEC( newval.i_int );
}
else
{
SubsdelayHeapUnlock( &p_sys->heap );
return VLC_ENOVAR;
}
SubsdelayRecalculateDelays( (filter_t *) p_this );
SubsdelayHeapUnlock( &p_sys->heap );
return VLC_SUCCESS;
}
/*****************************************************************************
* SubsdelayHeapInit: Initialize heap
*****************************************************************************/
static void SubsdelayHeapInit( subsdelay_heap_t *p_heap )
{
p_heap->i_count = 0;
p_heap->p_head = NULL;
vlc_mutex_init( &p_heap->lock );
}
/*****************************************************************************
* SubsdelayHeapDestroy: Destroy the heap and remove its entries
*****************************************************************************/
static void SubsdelayHeapDestroy( subsdelay_heap_t *p_heap )
{
subsdelay_heap_entry_t *p_entry;
SubsdelayHeapLock( p_heap );
for( p_entry = p_heap->p_head; p_entry != NULL; p_entry = p_entry->p_next )
{
p_entry->p_subpic->i_stop = p_entry->p_source->i_stop;
p_entry->p_filter = NULL;
}
SubsdelayHeapUnlock( p_heap );
vlc_mutex_destroy( &p_heap->lock );
}
/*****************************************************************************
* SubsdelayHeapPush: Add new subpicture to the heap
*****************************************************************************/
static subsdelay_heap_entry_t *SubsdelayHeapPush( subsdelay_heap_t *p_heap, subpicture_t *p_subpic, filter_t *p_filter )
{
subsdelay_heap_entry_t *p_entry, *p_last, *p_new_entry;
if( p_heap->i_count >= SUBSDELAY_MAX_ENTRIES )
{
return NULL; /* the heap is full */
}
p_new_entry = SubsdelayEntryCreate( p_subpic, p_filter );
if( !p_new_entry )
{
return NULL;
}
p_last = NULL;
for( p_entry = p_heap->p_head; p_entry != NULL; p_entry = p_entry->p_next )
{
if( p_entry->p_source->i_start > p_subpic->i_start )
{
/* the new entry should be inserted before p_entry */
break;
}
p_last = p_entry;
}
if( p_last )
{
p_new_entry->p_next = p_last->p_next;
p_last->p_next = p_new_entry;
if( p_last->b_update_ephemer )
{
/* the correct stop value can be determined */
p_last->p_source->i_stop = p_new_entry->p_source->i_start;
p_last->b_update_ephemer = false;
}
}
else
{
p_new_entry->p_next = p_heap->p_head;
p_heap->p_head = p_new_entry;
}
/* rebuild list */
SubsdelayRebuildList( p_heap );
return p_new_entry;
}
/*****************************************************************************
* SubsdelayHeapRemove: Remove entry
*****************************************************************************/
static void SubsdelayHeapRemove( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry )
{
subsdelay_heap_entry_t *p_curr, *p_prev;
p_prev = NULL;
for( p_curr = p_heap->p_head; p_curr != NULL; p_curr = p_curr->p_next )
{
if( p_curr == p_entry )
{
break;
}
p_prev = p_curr;
}
if( p_prev )
{
p_prev->p_next = p_entry->p_next;
}
else
{
p_heap->p_head = p_entry->p_next;
}
p_entry->p_filter = NULL;
SubsdelayRebuildList( p_heap );
}
static void SubsdelayRebuildList( subsdelay_heap_t *p_heap )
{
subsdelay_heap_entry_t *p_curr;
int i_index;
i_index = 0;
for( p_curr = p_heap->p_head; p_curr != NULL; p_curr = p_curr->p_next )
{
p_heap->p_list[i_index] = p_curr;
i_index++;
}
p_heap->i_count = i_index;
}
/*****************************************************************************
* SubsdelayHeapLock: Lock the heap
*****************************************************************************/
static void SubsdelayHeapLock( subsdelay_heap_t *p_heap )
{
vlc_mutex_lock( &p_heap->lock );
}
/*****************************************************************************
* SubsdelayHeapUnlock: Unlock the heap
*****************************************************************************/
static void SubsdelayHeapUnlock( subsdelay_heap_t *p_heap )
{
vlc_mutex_unlock( &p_heap->lock );
}
/*****************************************************************************
* SubsdelayHeapCreateEntry: Create new entry
*****************************************************************************/
static subsdelay_heap_entry_t * SubsdelayEntryCreate( subpicture_t *p_source, filter_t *p_filter )
{
subsdelay_heap_entry_t *p_entry;
subpicture_t *p_new_subpic;
subpicture_updater_t updater;
/* allocate structure */
p_entry = (subsdelay_heap_entry_t *) malloc( sizeof( subsdelay_heap_entry_t ) );
if( !p_entry )
{
return NULL;
}
/* initialize local updater */
updater.p_sys = p_entry;
updater.pf_validate = SubpicValidateWrapper;
updater.pf_update = SubpicUpdateWrapper;
updater.pf_destroy = SubpicDestroyWrapper;
/* create new subpic */
p_new_subpic = SubpicClone( p_source, &updater );
if( !p_new_subpic )
{
free( p_entry );
return NULL;
}
/* initialize entry */
p_entry->p_subpic = p_new_subpic;
p_entry->p_source = p_source;
p_entry->p_filter = p_filter;
p_entry->p_next = NULL;
p_entry->b_update_stop = true;
p_entry->b_update_ephemer = p_source->b_ephemer;
p_entry->b_update_position = true;
p_entry->b_check_empty = true;
p_entry->i_new_stop = p_source->i_stop;
p_entry->b_last_region_saved = false;
p_entry->i_last_region_x = 0;
p_entry->i_last_region_y = 0;
p_entry->i_last_region_align = 0;
return p_entry;
}
/*****************************************************************************
* SubsdelayEntryDestroy: Destroy entry
*****************************************************************************/
static void SubsdelayEntryDestroy( subsdelay_heap_entry_t *p_entry )
{
SubpicDestroyClone( p_entry->p_source );
free( p_entry );
}
/*****************************************************************************
* SubsdelayHeapCountOverlap: Count overlapping subtitles at a given time
*****************************************************************************/
static int SubsdelayHeapCountOverlap( subsdelay_heap_t *p_heap, subsdelay_heap_entry_t *p_entry, mtime_t i_date )
{
subsdelay_heap_entry_t *p_curr;
int i_overlaps;
VLC_UNUSED( p_heap );
i_overlaps = 0;
for( p_curr = p_entry->p_next; p_curr != NULL; p_curr = p_curr->p_next )
{
if( p_curr->p_source->i_start > i_date )
{
break;
}
if( !p_curr->b_check_empty ) /* subtitle was checked, and it's not empty */
{
i_overlaps++;
}
}