Commit e5ed591a authored by Laurent Aimar's avatar Laurent Aimar

Implemented buffering manipulation at the es_out level.

It allows faster seek and start up time when the access pace can be controlled.
parent 2b9d0814
......@@ -361,6 +361,42 @@ int input_clock_GetRate( input_clock_t *cl )
return i_rate;
}
int input_clock_GetState( input_clock_t *cl,
mtime_t *pi_stream_start, mtime_t *pi_system_start,
mtime_t *pi_stream_duration, mtime_t *pi_system_duration )
{
vlc_mutex_lock( &cl->lock );
if( !cl->b_has_reference )
{
vlc_mutex_unlock( &cl->lock );
return VLC_EGENERIC;
}
*pi_stream_start = cl->ref.i_stream;
*pi_system_start = cl->ref.i_system;
*pi_stream_duration = cl->last.i_stream - cl->ref.i_stream;
*pi_system_duration = cl->last.i_system - cl->ref.i_system;
vlc_mutex_unlock( &cl->lock );
return VLC_SUCCESS;
}
void input_clock_ChangeSystemOrigin( input_clock_t *cl, mtime_t i_system )
{
vlc_mutex_lock( &cl->lock );
assert( cl->b_has_reference );
const mtime_t i_offset = i_system - cl->ref.i_system;
cl->ref.i_system += i_offset;
cl->last.i_system += i_offset;
vlc_mutex_unlock( &cl->lock );
}
/*****************************************************************************
* ClockStreamToSystem: converts a movie clock to system date
*****************************************************************************/
......
......@@ -56,6 +56,8 @@ static void *DecoderThread( vlc_object_t * );
static int DecoderProcess( decoder_t *, block_t * );
static void DecoderOutputChangePause( decoder_t *, bool b_paused, mtime_t i_date );
static void DecoderFlush( decoder_t * );
static void DecoderSignalBuffering( decoder_t *, bool );
static void DecoderFlushBuffering( decoder_t * );
static void DecoderUnsupportedCodec( decoder_t *, vlc_fourcc_t );
......@@ -152,6 +154,10 @@ struct decoder_owner_sys_t
mtime_t i_ts_delay;
};
#define DECODER_MAX_BUFFERING_COUNT (4)
#define DECODER_MAX_BUFFERING_AUDIO_DURATION (AOUT_MAX_PREPARE_TIME)
#define DECODER_MAX_BUFFERING_VIDEO_DURATION (1*CLOCK_FREQ)
/*****************************************************************************
* Public functions
*****************************************************************************/
......@@ -388,6 +394,8 @@ void input_DecoderDecode( decoder_t * p_dec, block_t *p_block )
bool input_DecoderIsEmpty( decoder_t * p_dec )
{
/* FIXME it is buggy if the decoder is buffering FIXME
* -> "deadlock" */
if( p_dec->p_owner->b_own_thread &&
block_FifoCount( p_dec->p_owner->p_fifo ) > 0 )
{
......@@ -548,6 +556,21 @@ void input_DecoderStopBuffering( decoder_t *p_dec )
vlc_mutex_unlock( &p_owner->lock );
}
void input_DecoderWaitBuffering( decoder_t *p_dec )
{
decoder_owner_sys_t *p_owner = p_dec->p_owner;
vlc_mutex_lock( &p_owner->lock );
while( vlc_object_alive( p_dec ) && p_owner->b_buffering && !p_owner->buffer.b_full )
{
block_FifoWake( p_owner->p_fifo );
vlc_cond_wait( &p_owner->wait, &p_owner->lock );
}
vlc_mutex_unlock( &p_owner->lock );
}
/*****************************************************************************
* Internal functions
*****************************************************************************/
......@@ -735,26 +758,30 @@ static void *DecoderThread( vlc_object_t *p_this )
decoder_t *p_dec = (decoder_t *)p_this;
decoder_owner_sys_t *p_owner = p_dec->p_owner;
block_t *p_block;
int canc = vlc_savecancel();
/* The decoder's main loop */
while( vlc_object_alive( p_dec ) && !p_dec->b_error )
{
if( ( p_block = block_FifoGet( p_owner->p_fifo ) ) == NULL )
continue;
block_t *p_block = block_FifoGet( p_owner->p_fifo );
if( DecoderProcess( p_dec, p_block ) != VLC_SUCCESS )
DecoderSignalBuffering( p_dec, p_block == NULL );
if( p_block && DecoderProcess( p_dec, p_block ) != VLC_SUCCESS )
break;
}
while( vlc_object_alive( p_dec ) )
{
block_t *p_block = block_FifoGet( p_owner->p_fifo );
DecoderSignalBuffering( p_dec, p_block == NULL );
/* Trash all received PES packets */
p_block = block_FifoGet( p_owner->p_fifo );
if( p_block )
block_Release( p_block );
}
DecoderSignalBuffering( p_dec, true );
/* We do it here because of the dll loader that wants close() in the
* same thread than open()/decode() */
......@@ -815,6 +842,22 @@ static void DecoderSignalFlushed( decoder_t *p_dec )
vlc_mutex_unlock( &p_owner->lock );
}
static void DecoderSignalBuffering( decoder_t *p_dec, bool b_full )
{
decoder_owner_sys_t *p_owner = p_dec->p_owner;
vlc_mutex_lock( &p_owner->lock );
if( p_owner->b_buffering )
{
if( b_full )
p_owner->buffer.b_full = true;
vlc_cond_signal( &p_owner->wait );
}
vlc_mutex_unlock( &p_owner->lock );
}
static bool DecoderIsFlushing( decoder_t *p_dec )
{
decoder_owner_sys_t *p_owner = p_dec->p_owner;
......@@ -943,58 +986,111 @@ static void DecoderFixTs( decoder_t *p_dec, mtime_t *pi_ts0, mtime_t *pi_ts1,
}
}
static void DecoderPlayAudio( decoder_t *p_dec, aout_buffer_t *p_audio, int i_block_rate,
static void DecoderPlayAudio( decoder_t *p_dec, aout_buffer_t *p_audio,
int *pi_played_sum, int *pi_lost_sum )
{
decoder_owner_sys_t *p_owner = p_dec->p_owner;
aout_instance_t *p_aout = p_owner->p_aout;
aout_input_t *p_aout_input = p_owner->p_aout_input;
/* */
if( p_audio->start_date <= 0 )
{
msg_Warn( p_dec, "non-dated audio buffer received" );
*pi_lost_sum += 1;
aout_BufferFree( p_audio );
return;
}
/* */
vlc_mutex_lock( &p_owner->lock );
bool b_reject;
DecoderWaitUnblock( p_dec, &b_reject );
if( p_owner->b_buffering || p_owner->buffer.p_audio )
{
p_audio->p_next = NULL;
int i_rate = i_block_rate > 0 ? i_block_rate : INPUT_RATE_DEFAULT;
mtime_t i_delay;
*p_owner->buffer.pp_audio_next = p_audio;
p_owner->buffer.pp_audio_next = &p_audio->p_next;
DecoderFixTs( p_dec, &p_audio->start_date, &p_audio->end_date, NULL,
&i_rate, &i_delay, false );
p_owner->buffer.i_count++;
if( p_owner->buffer.i_count > DECODER_MAX_BUFFERING_COUNT ||
p_audio->start_date - p_owner->buffer.p_audio->start_date > DECODER_MAX_BUFFERING_AUDIO_DURATION )
{
p_owner->buffer.b_full = true;
vlc_cond_signal( &p_owner->wait );
}
}
vlc_mutex_unlock( &p_owner->lock );
for( ;; )
{
bool b_has_more = false;
bool b_reject;
DecoderWaitUnblock( p_dec, &b_reject );
/* */
const mtime_t i_max_date = mdate() + i_delay + AOUT_MAX_ADVANCE_TIME;
if( p_owner->b_buffering )
{
vlc_mutex_unlock( &p_owner->lock );
return;
}
if( !p_aout || !p_aout_input ||
p_audio->start_date <= 0 || p_audio->start_date > i_max_date ||
i_rate < INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE ||
i_rate > INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE )
b_reject = true;
/* */
if( p_owner->buffer.p_audio )
{
p_audio = p_owner->buffer.p_audio;
if( !b_reject )
{
/* Wait if we are too early
* FIXME that's plain ugly to do it here */
mwait( p_audio->start_date - AOUT_MAX_PREPARE_TIME );
p_owner->buffer.p_audio = p_audio->p_next;
if( !aout_DecPlay( p_aout, p_aout_input, p_audio, i_rate ) )
*pi_played_sum += 1;
*pi_lost_sum += aout_DecGetResetLost( p_aout, p_aout_input );
}
else
{
if( p_audio->start_date <= 0 )
b_has_more = p_owner->buffer.p_audio != NULL;
if( !b_has_more )
p_owner->buffer.pp_audio_next = &p_owner->buffer.p_audio;
}
/* */
int i_rate = INPUT_RATE_DEFAULT;
mtime_t i_delay;
DecoderFixTs( p_dec, &p_audio->start_date, &p_audio->end_date, NULL,
&i_rate, &i_delay, false );
vlc_mutex_unlock( &p_owner->lock );
/* */
const mtime_t i_max_date = mdate() + i_delay + AOUT_MAX_ADVANCE_TIME;
if( !p_aout || !p_aout_input ||
p_audio->start_date <= 0 || p_audio->start_date > i_max_date ||
i_rate < INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE ||
i_rate > INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE )
b_reject = true;
if( !b_reject )
{
msg_Warn( p_dec, "non-dated audio buffer received" );
/* Wait if we are too early
* FIXME that's plain ugly to do it here */
mwait( p_audio->start_date - AOUT_MAX_PREPARE_TIME );
if( !aout_DecPlay( p_aout, p_aout_input, p_audio, i_rate ) )
*pi_played_sum += 1;
*pi_lost_sum += aout_DecGetResetLost( p_aout, p_aout_input );
}
else if( p_audio->start_date > i_max_date )
else
{
msg_Warn( p_aout, "received buffer in the future (%"PRId64")",
p_audio->start_date - mdate() );
if( p_audio->start_date <= 0 )
{
msg_Warn( p_dec, "non-dated audio buffer received" );
}
else if( p_audio->start_date > i_max_date )
{
msg_Warn( p_aout, "received buffer in the future (%"PRId64")",
p_audio->start_date - mdate() );
}
*pi_lost_sum += 1;
aout_BufferFree( p_audio );
}
*pi_lost_sum += 1;
aout_BufferFree( p_audio );
if( !b_has_more )
break;
vlc_mutex_lock( &p_owner->lock );
}
}
......@@ -1039,8 +1135,7 @@ static void DecoderDecodeAudio( decoder_t *p_dec, block_t *p_block )
p_owner->i_preroll_end = -1;
}
DecoderPlayAudio( p_dec, p_aout_buf, p_block ? p_block->i_rate : 0,
&i_played, &i_lost );
DecoderPlayAudio( p_dec, p_aout_buf, &i_played, &i_lost );
}
/* Update ugly stat */
......@@ -1145,58 +1240,111 @@ static void DecoderPlayVideo( decoder_t *p_dec, picture_t *p_picture,
decoder_owner_sys_t *p_owner = p_dec->p_owner;
vout_thread_t *p_vout = p_owner->p_vout;
if( p_picture->date <= 0 )
{
msg_Warn( p_vout, "non-dated video buffer received" );
*pi_lost_sum += 1;
VoutDisplayedPicture( p_vout, p_picture );
return;
}
/* */
vlc_mutex_lock( &p_owner->lock );
bool b_reject;
DecoderWaitUnblock( p_dec, &b_reject );
if( p_owner->b_buffering || p_owner->buffer.p_picture )
{
p_picture->p_next = NULL;
int i_rate = INPUT_RATE_DEFAULT;
mtime_t i_delay;
*p_owner->buffer.pp_picture_next = p_picture;
p_owner->buffer.pp_picture_next = &p_picture->p_next;
DecoderFixTs( p_dec, &p_picture->date, NULL, NULL,
&i_rate, &i_delay, false );
p_owner->buffer.i_count++;
if( p_owner->buffer.i_count > DECODER_MAX_BUFFERING_COUNT ||
p_picture->date - p_owner->buffer.p_picture->date > DECODER_MAX_BUFFERING_VIDEO_DURATION )
{
p_owner->buffer.b_full = true;
vlc_cond_signal( &p_owner->wait );
}
}
vlc_mutex_unlock( &p_owner->lock );
for( ;; )
{
bool b_has_more = false;
/* */
const mtime_t i_max_date = mdate() + i_delay + VOUT_BOGUS_DELAY;
bool b_reject;
DecoderWaitUnblock( p_dec, &b_reject );
if( p_picture->date <= 0 || p_picture->date >= i_max_date )
b_reject = true;
if( p_owner->b_buffering )
{
vlc_mutex_unlock( &p_owner->lock );
return;
}
if( !b_reject )
{
if( i_rate != p_owner->i_last_rate )
/* */
if( p_owner->buffer.p_picture )
{
/* Be sure to not display old picture after our own */
VoutFlushPicture( p_vout, p_picture->date );
p_owner->i_last_rate = i_rate;
p_picture = p_owner->buffer.p_picture;
p_owner->buffer.p_picture = p_picture->p_next;
b_has_more = p_owner->buffer.p_picture != NULL;
if( !b_has_more )
p_owner->buffer.pp_picture_next = &p_owner->buffer.p_picture;
}
vout_DatePicture( p_vout, p_picture, p_picture->date );
/* */
int i_rate = INPUT_RATE_DEFAULT;
mtime_t i_delay;
vout_DisplayPicture( p_vout, p_picture );
}
else
{
if( p_picture->date <= 0 )
DecoderFixTs( p_dec, &p_picture->date, NULL, NULL,
&i_rate, &i_delay, false );
vlc_mutex_unlock( &p_owner->lock );
/* */
const mtime_t i_max_date = mdate() + i_delay + VOUT_BOGUS_DELAY;
if( p_picture->date <= 0 || p_picture->date >= i_max_date )
b_reject = true;
if( !b_reject )
{
msg_Warn( p_vout, "non-dated video buffer received" );
if( i_rate != p_owner->i_last_rate )
{
/* Be sure to not display old picture after our own */
VoutFlushPicture( p_vout, p_picture->date );
p_owner->i_last_rate = i_rate;
}
vout_DatePicture( p_vout, p_picture, p_picture->date );
vout_DisplayPicture( p_vout, p_picture );
}
else
{
msg_Warn( p_vout, "early picture skipped (%"PRId64")",
p_picture->date - mdate() );
if( p_picture->date <= 0 )
{
msg_Warn( p_vout, "non-dated video buffer received" );
}
else
{
msg_Warn( p_vout, "early picture skipped (%"PRId64")",
p_picture->date - mdate() );
}
*pi_lost_sum += 1;
VoutDisplayedPicture( p_vout, p_picture );
}
*pi_lost_sum += 1;
VoutDisplayedPicture( p_vout, p_picture );
}
int i_tmp_display;
int i_tmp_lost;
vout_GetResetStatistic( p_vout, &i_tmp_display, &i_tmp_lost );
int i_tmp_display;
int i_tmp_lost;
vout_GetResetStatistic( p_vout, &i_tmp_display, &i_tmp_lost );
*pi_played_sum += i_tmp_display;
*pi_lost_sum += i_tmp_lost;
*pi_played_sum += i_tmp_display;
*pi_lost_sum += i_tmp_lost;
if( !b_has_more )
break;
vlc_mutex_lock( &p_owner->lock );
}
}
static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block )
......@@ -1265,21 +1413,72 @@ static void DecoderPlaySpu( decoder_t *p_dec, subpicture_t *p_subpic,
decoder_owner_sys_t *p_owner = p_dec->p_owner;
vout_thread_t *p_vout = p_owner->p_spu_vout;
/* Preroll does not work very well with subtitle */
/* */
if( p_subpic->i_start <= 0 )
{
msg_Warn( p_dec, "non-dated spu buffer received" );
subpicture_Delete( p_subpic );
return;
}
/* */
vlc_mutex_lock( &p_owner->lock );
bool b_reject;
DecoderWaitUnblock( p_dec, &b_reject );
if( p_owner->b_buffering || p_owner->buffer.p_subpic )
{
p_subpic->p_next = NULL;
DecoderFixTs( p_dec, &p_subpic->i_start, &p_subpic->i_stop, NULL,
NULL, NULL, b_telx );
*p_owner->buffer.pp_subpic_next = p_subpic;
p_owner->buffer.pp_subpic_next = &p_subpic->p_next;
vlc_mutex_unlock( &p_owner->lock );
p_owner->buffer.i_count++;
/* XXX it is important to be full after the first one */
if( p_owner->buffer.i_count > 0 )
{
p_owner->buffer.b_full = true;
vlc_cond_signal( &p_owner->wait );
}
}
if( !b_reject )
spu_DisplaySubpicture( p_vout->p_spu, p_subpic );
else
subpicture_Delete( p_subpic );
for( ;; )
{
bool b_has_more = false;
bool b_reject;
DecoderWaitUnblock( p_dec, &b_reject );
if( p_owner->b_buffering )
{
vlc_mutex_unlock( &p_owner->lock );
return;
}
/* */
if( p_owner->buffer.p_subpic )
{
p_subpic = p_owner->buffer.p_subpic;
p_owner->buffer.p_subpic = p_subpic->p_next;
b_has_more = p_owner->buffer.p_subpic != NULL;
if( !b_has_more )
p_owner->buffer.pp_subpic_next = &p_owner->buffer.p_subpic;
}
/* */
DecoderFixTs( p_dec, &p_subpic->i_start, &p_subpic->i_stop, NULL,
NULL, NULL, b_telx );
vlc_mutex_unlock( &p_owner->lock );
if( !b_reject )
spu_DisplaySubpicture( p_vout->p_spu, p_subpic );
else
subpicture_Delete( p_subpic );
if( !b_has_more )
break;
vlc_mutex_lock( &p_owner->lock );
}
}
static void DecoderPlaySout( decoder_t *p_dec, block_t *p_sout_block,
......@@ -1302,6 +1501,52 @@ static void DecoderPlaySout( decoder_t *p_dec, block_t *p_sout_block,
sout_InputSendBuffer( p_owner->p_sout_input, p_sout_block );
}
/* */
static void DecoderFlushBuffering( decoder_t *p_dec )
{
decoder_owner_sys_t *p_owner = p_dec->p_owner;
vlc_assert_locked( &p_owner->lock );
while( p_owner->buffer.p_picture )
{
picture_t *p_picture = p_owner->buffer.p_picture;
p_owner->buffer.p_picture = p_picture->p_next;
p_owner->buffer.i_count--;
if( p_owner->p_vout )
VoutDisplayedPicture( p_owner->p_vout, p_picture );
if( !p_owner->buffer.p_picture )
p_owner->buffer.pp_picture_next = &p_owner->buffer.p_picture;
}
while( p_owner->buffer.p_audio )
{
aout_buffer_t *p_audio = p_owner->buffer.p_audio;
p_owner->buffer.p_audio = p_audio->p_next;
p_owner->buffer.i_count--;
aout_BufferFree( p_audio );
if( !p_owner->buffer.p_audio )
p_owner->buffer.pp_audio_next = &p_owner->buffer.p_audio;
}
while( p_owner->buffer.p_subpic )
{
subpicture_t *p_subpic = p_owner->buffer.p_subpic;
p_owner->buffer.p_subpic = p_subpic->p_next;
p_owner->buffer.i_count--;
subpicture_Delete( p_subpic );
if( !p_owner->buffer.p_subpic )
p_owner->buffer.pp_subpic_next = &p_owner->buffer.p_subpic;
}
}
/* This function process a block for sout
*/
static void DecoderProcessSout( decoder_t *p_dec, block_t *p_block )
......@@ -1467,18 +1712,6 @@ static void DecoderProcessSpu( decoder_t *p_dec, block_t *p_block, bool b_flush
vout_thread_t *p_vout;
subpicture_t *p_spu;
if( b_flush && p_owner->p_spu_vout )
{
p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
if( p_vout && p_owner->p_spu_vout == p_vout )
spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR,
p_owner->i_spu_channel );
if( p_vout )
vlc_object_release( p_vout );
}
while( (p_spu = p_dec->pf_decode_sub( p_dec, p_block ? &p_block : NULL ) ) )
{
vlc_mutex_lock( &p_input->p->counters.counters_lock );
......@@ -1507,6 +1740,18 @@ static void DecoderProcessSpu( decoder_t *p_dec, block_t *p_block, bool b_flush
if( p_vout )
vlc_object_release( p_vout );
}
if( b_flush && p_owner->p_spu_vout )
{
p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
if( p_vout && p_owner->p_spu_vout == p_vout )
spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR,
p_owner->i_spu_channel );
if( p_vout )
vlc_object_release( p_vout );
}
}
/**
......@@ -1572,7 +1817,13 @@ static int DecoderProcess( decoder_t *p_dec, block_t *p_block )
/* */
if( b_flush_request )
{
vlc_mutex_lock( &p_owner->lock );
DecoderFlushBuffering( p_dec );
vlc_mutex_unlock( &p_owner->lock );
DecoderSignalFlushed( p_dec );
}
return p_dec->b_error ? VLC_EGENERIC : VLC_SUCCESS;
}
......@@ -1595,6 +1846,11 @@ static void DeleteDecoder( decoder_t * p_dec )
block_FifoEmpty( p_owner->p_fifo );
block_FifoRelease( p_owner->p_fifo );
/* */
vlc_mutex_lock( &p_owner->lock );
DecoderFlushBuffering( p_dec );
vlc_mutex_unlock( &p_owner->lock );
/* Cleanup */
if( p_owner->p_aout_input )
aout_DecDelete( p_owner->p_aout, p_owner->p_aout_input );
......@@ -1605,19 +1861,16 @@ static void DeleteDecoder( decoder_t * p_dec )
}
if( p_owner->p_vout )
{
int i_pic;
#define p_pic p_owner->p_vout->render.pp_picture[i_pic]
/* Hack to make sure all the the pictures are freed by the decoder */
for( i_pic = 0; i_pic < p_owner->p_vout->render.i_pictures;
i_pic++ )
for( int i_pic = 0; i_pic < p_owner->p_vout->render.i_pictures; i_pic++ )
{
picture_t *p_pic = p_owner->p_vout->render.pp_picture[i_pic];
if( p_pic->i_status == RESERVED_PICTURE )
vout_DestroyPicture( p_owner->p_vout, p_pic );
if( p_pic->i_refcount > 0 )
vout_UnlinkPicture( p_owner->p_vout, p_pic );
}
#undef p_pic
/* We are about to die. Reattach video output to p_vlc. */
vout_Request( p_dec, p_owner->p_vout, NULL );
......@@ -1686,6 +1939,8 @@ static aout_buffer_t *aout_new_buffer( decoder_t *p_dec, int i_samples )
/* Parameters changed, restart the aout */
vlc_mutex_lock( &p_owner->lock );
DecoderFlushBuffering( p_dec );
p_owner->p_aout_input = NULL;
aout_DecDelete( p_owner->p_aout, p_aout_input );
......@@ -1820,6 +2075,9 @@ static picture_t *vout_new_buffer( decoder_t *p_dec )
p_owner->video = p_dec->fmt_out.video;
vlc_mutex_lock( &p_owner->lock );
DecoderFlushBuffering( p_dec );
p_vout = p_owner->p_vout;
p_owner->p_vout = NULL;
vlc_mutex_unlock( &p_owner->lock );
......@@ -1871,6 +2129,9 @@ static picture_t *vout_new_buffer( decoder_t *p_dec )
if( DecoderIsFlushing( p_dec ) )
return NULL;
/* */
DecoderSignalBuffering( p_dec, true );
/* Check the decoder doesn't leak pictures */
for( i_pic = 0, i_ready_pic = 0; i_pic < p_owner->p_vout->render.i_pictures; i_pic++ )
{
......@@ -1960,6 +2221,12 @@ static subpicture_t *spu_new_buffer( decoder_t *p_dec )
if( p_owner->p_spu_vout != p_vout )
{
vlc_mutex_lock( &p_owner->lock );
DecoderFlushBuffering( p_dec );
vlc_mutex_unlock( &p_owner->lock );
spu_Control( p_vout->p_spu, SPU_CHANNEL_REGISTER,
&p_owner->i_spu_channel );
p_owner->i_spu_order = 0;
......
......@@ -140,6 +140,9 @@ struct es_out_sys_t
/* Rate used for clock */
int i_rate;
/* Used for buffering */
bool b_buffering;
/* Record */
sout_instance_t *p_sout_record;
};
......@@ -155,6 +158,10 @@ static void EsOutAddInfo( es_out_t *, es_out_id_t *es );
static bool EsIsSelected( es_out_id_t *es );
static void EsSelect( es_out_t *out, es_out_id_t *es );
static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update );
static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es );
static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_date );
static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date );
static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced );
static char *LanguageGetName( const char *psz_code );
static char *LanguageGetCode( const char *psz_lang );
static char **LanguageSplit( const char *psz_langs );
......@@ -274,6 +281,8 @@ es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate )
p_sys->i_rate = i_rate;
p_sys->b_buffering = true;
p_sys->p_sout_record = NULL;
return out;
......@@ -360,59 +369,17 @@ mtime_t input_EsOutGetWakeup( es_out_t *out )
return 0;
/* We do not have a wake up date if the input cannot have its speed
* controlled or sout is imposing its own */
if( !p_input->b_can_pace_control || p_input->p->b_out_pace_control )
* controlled or sout is imposing its own or while buffering
*
* FIXME for !p_input->b_can_pace_control a wkeup time is still needed to avoid too strong buffering */
if( !p_input->b_can_pace_control ||
p_input->p->b_out_pace_control ||
p_sys->b_buffering )
return 0;
return input_clock_GetWakeup( p_sys->p_pgrm->p_clock );
}
static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
{
es_out_sys_t *p_sys = out->p_sys;
/* Pause decoders first */
for( int i = 0; i < p_sys->i_es; i++ )
{
es_out_id_t *es = p_sys->es[i];
/* Send a dummy block to let decoder know that
* there is a discontinuity */
if( es->p_dec )
{
input_DecoderChangePause( es->p_dec, b_paused, i_date );
if( es->p_dec_record )
input_DecoderChangePause( es->p_dec_record, b_paused, i_date );
}
}
}
static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
{
es_out_sys_t *p_sys = out->p_sys;
for( int i = 0; i < p_sys->i_pgrm; i++ )
input_clock_ChangePause( p_sys->pgrm[i]->p_clock, b_paused, i_date );
}
static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es )
{
es_out_sys_t *p_sys = out->p_sys;
mtime_t i_delay = 0;
if( p_es->fmt.i_cat == AUDIO_ES )
i_delay = p_sys->i_audio_delay;
else if( p_es->fmt.i_cat == SPU_ES )
i_delay = p_sys->i_spu_delay;
if( i_delay != 0 )
{
if( p_es->p_dec )
input_DecoderChangeDelay( p_es->p_dec, i_delay );
if( p_es->p_dec_record )
input_DecoderChangeDelay( p_es->p_dec, i_delay );
}
}
void input_EsOutChangeRate( es_out_t *out, int i_rate )
{
es_out_sys_t *p_sys = out->p_sys;
......@@ -473,6 +440,8 @@ int input_EsOutSetRecord( es_out_t *out, bool b_record )
continue;
p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record );
if( p_es->p_dec_record && p_sys->b_buffering )
input_DecoderStartBuffering( p_es->p_dec_record );
}
}
else
......@@ -527,23 +496,21 @@ void input_EsOutChangePosition( es_out_t *out )
for( int i = 0; i < p_sys->i_es; i++ )
{
es_out_id_t *es = p_sys->es[i];
es_out_id_t *p_es = p_sys->es[i];
/* Send a dummy block to let decoder know that
* there is a discontinuity */
if( es->p_dec )
{
input_DecoderStartBuffering( es->p_dec );
input_DecoderStopBuffering( es->p_dec );
if( es->p_dec_record )
{
input_DecoderStartBuffering( es->p_dec_record );
input_DecoderStopBuffering( es->p_dec_record );
}
}
if( !p_es->p_dec )
continue;
input_DecoderStartBuffering( p_es->p_dec );
if( p_es->p_dec_record )
input_DecoderStartBuffering( p_es->p_dec );
}
es_out_Control( out, ES_OUT_RESET_PCR );
for( int i = 0; i < p_sys->i_pgrm; i++ )
input_clock_Reset( p_sys->pgrm[i]->p_clock );
p_sys->b_buffering = true;
}
bool input_EsOutDecodersEmpty( es_out_t *out )
......@@ -551,6 +518,13 @@ bool input_EsOutDecodersEmpty( es_out_t *out )
es_out_sys_t *p_sys = out->p_sys;
int i;
if( p_sys->b_buffering && p_sys->p_pgrm )
{
EsOutDecodersStopBuffering( out, true );
if( p_sys->b_buffering )
return true;
}
for( i = 0; i < p_sys->i_es; i++ )
{
es_out_id_t *es = p_sys->es[i];
......@@ -566,6 +540,111 @@ bool input_EsOutDecodersEmpty( es_out_t *out )
/*****************************************************************************
*
*****************************************************************************/
static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced )
{
es_out_sys_t *p_sys = out->p_sys;
int i_ret;
mtime_t i_stream_start;
mtime_t i_system_start;
mtime_t i_stream_duration;
mtime_t i_system_duration;
i_ret = input_clock_GetState( p_sys->p_pgrm->p_clock,
&i_stream_start, &i_system_start,
&i_stream_duration, &i_system_duration );
assert( !i_ret || b_forced );
if( i_ret )
return;
if( i_stream_duration <= p_sys->p_input->i_pts_delay && !b_forced )
{
msg_Dbg( p_sys->p_input, "Buffering %d%%", (int)(100 * i_stream_duration / p_sys->p_input->i_pts_delay) );
return;
}
msg_Dbg( p_sys->p_input, "Stream buffering done (%d ms in %d ms)",
(int)(i_stream_duration/1000), (int)(i_system_duration/1000) );
p_sys->b_buffering = false;
const mtime_t i_decoder_buffering_start = mdate();
for( int i = 0; i < p_sys->i_es; i++ )
{
es_out_id_t *p_es = p_sys->es[i];
if( !p_es->p_dec )
continue;
input_DecoderWaitBuffering( p_es->p_dec );
if( p_es->p_dec_record )
input_DecoderWaitBuffering( p_es->p_dec_record );
}
msg_Dbg( p_sys->p_input, "Decoder buffering done in %d ms",
(int)(mdate() - i_decoder_buffering_start)/1000 );
const mtime_t i_ts_delay = 10*1000 + /* FIXME CLEANUP thread wake up time*/
mdate();
//msg_Dbg( p_sys->p_input, "==> %lld", i_ts_delay - p_sys->p_input->i_pts_delay );
input_clock_ChangeSystemOrigin( p_sys->p_pgrm->p_clock, i_ts_delay - p_sys->p_input->i_pts_delay );
for( int i = 0; i < p_sys->i_es; i++ )
{
es_out_id_t *p_es = p_sys->es[i];
if( !p_es->p_dec )
continue;
input_DecoderStopBuffering( p_es->p_dec );
if( p_es->p_dec_record )
input_DecoderStopBuffering( p_es->p_dec );
}
}
static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
{
es_out_sys_t *p_sys = out->p_sys;
/* Pause decoders first */
for( int i = 0; i < p_sys->i_es; i++ )
{
es_out_id_t *es = p_sys->es[i];
/* Send a dummy block to let decoder know that
* there is a discontinuity */
if( es->p_dec )
{
input_DecoderChangePause( es->p_dec, b_paused, i_date );
if( es->p_dec_record )
input_DecoderChangePause( es->p_dec_record, b_paused, i_date );
}
}
}
static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
{
es_out_sys_t *p_sys = out->p_sys;
for( int i = 0; i < p_sys->i_pgrm; i++ )
input_clock_ChangePause( p_sys->pgrm[i]->p_clock, b_paused, i_date );
}
static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es )
{
es_out_sys_t *p_sys = out->p_sys;
mtime_t i_delay = 0;
if( p_es->fmt.i_cat == AUDIO_ES )
i_delay = p_sys->i_audio_delay;
else if( p_es->fmt.i_cat == SPU_ES )
i_delay = p_sys->i_spu_delay;
if( i_delay != 0 )
{
if( p_es->p_dec )
input_DecoderChangeDelay( p_es->p_dec, i_delay );
if( p_es->p_dec_record )
input_DecoderChangeDelay( p_es->p_dec, i_delay );
}
}
static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, es_format_t *fmt, const char *psz_language,
bool b_delete )
{
......@@ -1205,8 +1284,18 @@ static void EsCreateDecoder( es_out_t *out, es_out_id_t *p_es )
input_thread_t *p_input = p_sys->p_input;
p_es->p_dec = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_input->p->p_sout );
if( p_es->p_dec && !p_es->p_master && p_sys->p_sout_record )
p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record );
if( p_es->p_dec )
{
if( p_sys->b_buffering )
input_DecoderStartBuffering( p_es->p_dec );
if( !p_es->p_master && p_sys->p_sout_record )
{
p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record );
if( p_es->p_dec_record && p_sys->b_buffering )
input_DecoderStartBuffering( p_es->p_dec_record );
}
}
EsOutDecoderChangeDelay( out, p_es );
}
......@@ -1917,12 +2006,16 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args )
* TODO do not use mdate() but proper stream acquisition date */
input_clock_Update( p_pgrm->p_clock, VLC_OBJECT(p_sys->p_input),
p_sys->p_input->b_can_pace_control, i_pcr, mdate() );
/* Check buffering state on master clock update */
if( p_sys->b_buffering && p_pgrm == p_sys->p_pgrm )
EsOutDecodersStopBuffering( out, false );
return VLC_SUCCESS;
}
case ES_OUT_RESET_PCR:
for( i = 0; i < p_sys->i_pgrm; i++ )
input_clock_Reset( p_sys->pgrm[i]->p_clock );
msg_Err( p_sys->p_input, "ES_OUT_RESET_PCR called" );
input_EsOutChangePosition( out );
return VLC_SUCCESS;
case ES_OUT_GET_TS:
......
......@@ -75,6 +75,13 @@ void input_clock_ChangeRate( input_clock_t *, int i_rate );
*/
void input_clock_ChangePause( input_clock_t *, bool b_paused, mtime_t i_date );
/**
* This function allows to rebase the original system value date.
* It can be called only imediatly after a input_clock_Update call.
* FIXME ugly
*/
void input_clock_ChangeSystemOrigin( input_clock_t *, mtime_t i_system );
/**
* This function converts a timestamp from stream clock to system clock.
*
......@@ -88,5 +95,13 @@ mtime_t input_clock_GetTS( input_clock_t *, int *pi_rate, mtime_t i_pts_delay, m
*/
int input_clock_GetRate( input_clock_t * );
/**
* This function returns current clock state or VLC_EGENERIC if there is not a
* reference point.
*/
int input_clock_GetState( input_clock_t *,
mtime_t *pi_stream_start, mtime_t *pi_system_start,
mtime_t *pi_stream_duration, mtime_t *pi_system_duration );
#endif
......@@ -51,6 +51,11 @@ void input_DecoderChangeDelay( decoder_t *, mtime_t i_delay );
*/
void input_DecoderStartBuffering( decoder_t * );
/**
* This function waits for the decoder to have buffered sufficient data.
*/
void input_DecoderWaitBuffering( decoder_t * );
/**
* This function stops the buffering mode.
*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment