vlc_mutex_t lock;
vlc_cond_t wait_request;
vlc_cond_t wait_acknowledge;
+ vlc_cond_t wait_fifo; /* TODO: merge with wait_acknowledge */
/* -- These variables need locking on write(only) -- */
audio_output_t *p_aout;
} pause;
/* Waiting */
- bool b_woken;
bool b_waiting;
bool b_first;
bool b_has_data;
/* Flushing */
bool b_flushing;
+ bool b_draining;
+ bool b_drained;
+ bool b_idle;
/* CC */
struct
return b_flushing;
}
-static void DecoderSignalWait( decoder_t *p_dec )
-{
- decoder_owner_sys_t *p_owner = p_dec->p_owner;
-
- vlc_mutex_lock( &p_owner->lock );
-
- if( p_owner->b_waiting )
- {
- p_owner->b_has_data = true;
- vlc_cond_signal( &p_owner->wait_acknowledge );
- }
-
- vlc_mutex_unlock( &p_owner->lock );
-}
-
static block_t *DecoderBlockFlushNew()
{
block_t *p_null = block_Alloc( 128 );
return p_picture;
/* */
- DecoderSignalWait( p_dec );
+ vlc_mutex_lock( &p_owner->lock );
+ if( p_owner->b_waiting )
+ {
+ p_owner->b_has_data = true;
+ vlc_cond_signal( &p_owner->wait_acknowledge );
+ }
+ vlc_mutex_unlock( &p_owner->lock );
/* Check the decoder doesn't leak pictures */
vout_FixLeaks( p_owner->p_vout );
audio_output_t *p_aout = p_owner->p_aout;
/* */
- if( p_audio && p_audio->i_pts <= VLC_TS_INVALID ) // FIXME --VLC_TS_INVALID verify audio_output/*
+ if( p_audio->i_pts <= VLC_TS_INVALID ) // FIXME --VLC_TS_INVALID verify audio_output/*
{
msg_Warn( p_dec, "non-dated audio buffer received" );
*pi_lost_sum += 1;
/* */
vlc_mutex_lock( &p_owner->lock );
-
- if( p_audio && p_owner->b_waiting )
+race:
+ if( p_owner->b_waiting )
{
p_owner->b_has_data = true;
vlc_cond_signal( &p_owner->wait_acknowledge );
}
- for( ;; )
- {
- bool b_paused;
-
- bool b_reject = DecoderWaitUnblock( p_dec );
-
- b_paused = p_owner->b_paused;
-
- if (!p_audio)
- break;
-
- /* */
- int i_rate = INPUT_RATE_DEFAULT;
+ bool b_reject = DecoderWaitUnblock( p_dec );
+ bool b_paused = p_owner->b_paused;
- DecoderFixTs( p_dec, &p_audio->i_pts, NULL, &p_audio->i_length,
- &i_rate, AOUT_MAX_ADVANCE_TIME );
+ /* */
+ int i_rate = INPUT_RATE_DEFAULT;
- if( p_audio->i_pts <= VLC_TS_INVALID
- || i_rate < INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE
- || i_rate > INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE )
- b_reject = true;
+ DecoderFixTs( p_dec, &p_audio->i_pts, NULL, &p_audio->i_length,
+ &i_rate, AOUT_MAX_ADVANCE_TIME );
- DecoderWaitDate( p_dec, &b_reject,
- p_audio->i_pts - AOUT_MAX_PREPARE_TIME );
+ if( p_audio->i_pts <= VLC_TS_INVALID
+ || i_rate < INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE
+ || i_rate > INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE )
+ b_reject = true;
- if( unlikely(p_owner->b_paused != b_paused) )
- continue; /* race with input thread? retry... */
- if( p_aout == NULL )
- b_reject = true;
+ DecoderWaitDate( p_dec, &b_reject,
+ p_audio->i_pts - AOUT_MAX_PREPARE_TIME );
- if( !b_reject )
- {
- assert( !p_owner->b_paused );
- if( !aout_DecPlay( p_aout, p_audio, i_rate ) )
- *pi_played_sum += 1;
- *pi_lost_sum += aout_DecGetResetLost( p_aout );
- }
- else
- {
- msg_Dbg( p_dec, "discarded audio buffer" );
- *pi_lost_sum += 1;
- block_Release( p_audio );
- }
+ if( unlikely(p_owner->b_paused != b_paused) )
+ goto race; /* race with input thread? retry... */
+ if( p_aout == NULL )
+ b_reject = true;
- break;
+ if( !b_reject )
+ {
+ assert( !p_owner->b_paused );
+ if( !aout_DecPlay( p_aout, p_audio, i_rate ) )
+ *pi_played_sum += 1;
+ *pi_lost_sum += aout_DecGetResetLost( p_aout );
+ }
+ else
+ {
+ msg_Dbg( p_dec, "discarded audio buffer" );
+ *pi_lost_sum += 1;
+ block_Release( p_audio );
}
vlc_mutex_unlock( &p_owner->lock );
}
int i_lost = 0;
int i_played = 0;
- if (!p_block) {
- /* Play a NULL block to output buffered frames */
- DecoderPlayAudio( p_dec, NULL, &i_played, &i_lost );
- }
- else while( (p_aout_buf = p_dec->pf_decode_audio( p_dec, &p_block )) )
+ while( (p_aout_buf = p_dec->pf_decode_audio( p_dec, &p_block )) )
{
if( DecoderIsFlushing( p_dec ) )
{
{
msg_Dbg( p_dec, "End of audio preroll" );
if( p_owner->p_aout )
- aout_DecFlush( p_owner->p_aout );
+ aout_DecFlush( p_owner->p_aout, false );
/* */
p_owner->i_preroll_end = VLC_TS_INVALID;
}
}
if( b_flush && p_owner->p_aout )
- aout_DecFlush( p_owner->p_aout );
+ aout_DecFlush( p_owner->p_aout, false );
}
static void DecoderPlaySpu( decoder_t *p_dec, subpicture_t *p_subpic )
vlc_fifo_Lock( p_owner->p_fifo );
vlc_fifo_CleanupPush( p_owner->p_fifo );
+ vlc_cond_signal( &p_owner->wait_fifo );
+
while( vlc_fifo_IsEmpty( p_owner->p_fifo ) )
{
- if( p_owner->b_woken )
+ if( p_owner->b_draining )
+ { /* We have emptied the FIFO and there is a pending request to
+ * drain. Pass p_block = NULL to decoder just once. */
+ p_owner->b_draining = false;
break;
+ }
+
+ p_owner->b_idle = true;
vlc_fifo_Wait( p_owner->p_fifo );
/* Make sure there is no cancellation point other than this one^^.
* If you need one, be sure to push cleanup of p_block. */
+ p_owner->b_idle = false;
}
p_block = vlc_fifo_DequeueUnlocked( p_owner->p_fifo );
- if( p_block != NULL )
- vlc_cond_signal( &p_owner->wait_acknowledge );
-
- p_owner->b_woken = false;
vlc_cleanup_run();
- if( p_block == NULL || p_block->i_flags & BLOCK_FLAG_CORE_EOS )
- DecoderSignalWait( p_dec );
+ int canc = vlc_savecancel();
+ DecoderProcess( p_dec, p_block );
- if( p_block )
- {
- int canc = vlc_savecancel();
-
- if( p_block->i_flags & BLOCK_FLAG_CORE_EOS )
- {
- /* calling DecoderProcess() with NULL block will make
- * decoders/packetizers flush their buffers */
- block_Release( p_block );
- p_block = NULL;
- }
-
- DecoderProcess( p_dec, p_block );
-
- vlc_restorecancel( canc );
+ vlc_mutex_lock( &p_owner->lock );
+ if( p_block == NULL )
+ { /* Draining: the decoder is drained and all decoded buffers are
+ * queued to the output at this point. Now drain the output. */
+ if( p_owner->p_aout != NULL )
+ aout_DecFlush( p_owner->p_aout, true );
}
+ p_owner->b_drained = (p_block == NULL);
+
+ vlc_cond_signal( &p_owner->wait_acknowledge );
+ vlc_mutex_unlock( &p_owner->lock );
+ vlc_restorecancel( canc );
}
return NULL;
}
es_format_Init( &p_owner->fmt, UNKNOWN_ES, 0 );
/* decoder fifo */
- p_owner->b_woken = false;
p_owner->p_fifo = block_FifoNew();
if( unlikely(p_owner->p_fifo == NULL) )
{
vlc_mutex_init( &p_owner->lock );
vlc_cond_init( &p_owner->wait_request );
vlc_cond_init( &p_owner->wait_acknowledge );
+ vlc_cond_init( &p_owner->wait_fifo );
p_owner->b_fmt_description = false;
p_owner->p_description = NULL;
p_owner->b_has_data = false;
p_owner->b_flushing = false;
+ p_owner->b_draining = false;
+ p_owner->b_drained = false;
+ p_owner->b_idle = false;
/* */
p_owner->cc.b_supported = false;
if( p_owner->p_aout )
{
/* TODO: REVISIT gap-less audio */
- aout_DecFlush( p_owner->p_aout );
+ aout_DecFlush( p_owner->p_aout, false );
aout_DecDelete( p_owner->p_aout );
input_resource_PutAout( p_owner->p_resource, p_owner->p_aout );
if( p_owner->p_input != NULL )
vlc_object_release( p_owner->p_packetizer );
}
+ vlc_cond_destroy( &p_owner->wait_fifo );
vlc_cond_destroy( &p_owner->wait_acknowledge );
vlc_cond_destroy( &p_owner->wait_request );
vlc_mutex_destroy( &p_owner->lock );
* Locking is not necessary as b_waiting is only read, not written by
* the decoder thread. */
while( vlc_fifo_GetCount( p_owner->p_fifo ) >= 10 )
- vlc_fifo_WaitCond( p_owner->p_fifo, &p_owner->wait_acknowledge );
+ vlc_fifo_WaitCond( p_owner->p_fifo, &p_owner->wait_fifo );
}
vlc_fifo_QueueUnlocked( p_owner->p_fifo, p_block );
bool input_DecoderIsEmpty( decoder_t * p_dec )
{
decoder_owner_sys_t *p_owner = p_dec->p_owner;
+
assert( !p_owner->b_waiting );
- bool b_empty = block_FifoCount( p_dec->p_owner->p_fifo ) <= 0;
+ if( block_FifoCount( p_dec->p_owner->p_fifo ) > 0 )
+ return false;
+
+ bool b_empty;
+
+ vlc_mutex_lock( &p_owner->lock );
+ if( p_owner->fmt.i_cat == VIDEO_ES && p_owner->p_vout != NULL )
+ b_empty = vout_IsEmpty( p_owner->p_vout );
+ else if( p_owner->fmt.i_cat == AUDIO_ES )
+ b_empty = p_owner->b_drained;
+ else
+ b_empty = true; /* TODO subtitles support */
+ vlc_mutex_unlock( &p_owner->lock );
- if( b_empty )
- {
- vlc_mutex_lock( &p_owner->lock );
- /* TODO subtitles support */
- if( p_owner->fmt.i_cat == VIDEO_ES && p_owner->p_vout )
- b_empty = vout_IsEmpty( p_owner->p_vout );
- else if( p_owner->fmt.i_cat == AUDIO_ES && p_owner->p_aout )
- b_empty = aout_DecIsEmpty( p_owner->p_aout );
- vlc_mutex_unlock( &p_owner->lock );
- }
return b_empty;
}
*/
void input_DecoderDrain( decoder_t *p_dec )
{
- block_t *p_block = block_Alloc(0);
- if( unlikely(p_block == NULL) )
- return;
+ decoder_owner_sys_t *p_owner = p_dec->p_owner;
- p_block->i_flags |= BLOCK_FLAG_CORE_EOS;
- input_DecoderDecode( p_dec, p_block, false );
+ vlc_fifo_Lock( p_owner->p_fifo );
+ p_owner->b_draining = true;
+ vlc_fifo_Signal( p_owner->p_fifo );
+ vlc_fifo_Unlock( p_owner->p_fifo );
}
static void DecoderFlush( decoder_t *p_dec )
vlc_assert_locked( &p_owner->lock );
+ vlc_fifo_Lock( p_owner->p_fifo );
/* Empty the fifo */
- block_FifoEmpty( p_owner->p_fifo );
+ block_ChainRelease( vlc_fifo_DequeueAllUnlocked( p_owner->p_fifo ) );
+ p_owner->b_draining = false; /* flush supersedes drain */
+ vlc_fifo_Unlock( p_owner->p_fifo );
/* Monitor for flush end */
p_owner->b_flushing = true;
{
decoder_owner_sys_t *p_owner = p_dec->p_owner;
- vlc_mutex_lock( &p_owner->lock );
/* Normally, p_owner->b_paused != b_paused here. But if a track is added
* while the input is paused (e.g. add sub file), then b_paused is
- * (incorrectly) false. */
- if( likely(p_owner->b_paused != b_paused) ) {
- p_owner->b_paused = b_paused;
- p_owner->pause.i_date = i_date;
- p_owner->pause.i_ignore = 0;
- vlc_cond_signal( &p_owner->wait_request );
-
- /* XXX only audio and video output have to be paused.
- * - for sout it is useless
- * - for subs, it is done by the vout
- */
- if( p_owner->fmt.i_cat == AUDIO_ES )
- {
- if( p_owner->p_aout )
- aout_DecChangePause( p_owner->p_aout, b_paused, i_date );
- }
- else if( p_owner->fmt.i_cat == VIDEO_ES )
- {
- if( p_owner->p_vout )
- vout_ChangePause( p_owner->p_vout, b_paused, i_date );
- }
+ * (incorrectly) false. FIXME: This is a bug in the decoder owner. */
+ if( unlikely(p_owner->b_paused == b_paused) )
+ return;
+
+ vlc_mutex_lock( &p_owner->lock );
+ p_owner->b_paused = b_paused;
+ p_owner->pause.i_date = i_date;
+ p_owner->pause.i_ignore = 0;
+ vlc_cond_signal( &p_owner->wait_request );
+
+ /* XXX only audio and video output have to be paused.
+ * - for sout it is useless
+ * - for subs, it is done by the vout
+ */
+ if( p_owner->fmt.i_cat == AUDIO_ES )
+ {
+ if( p_owner->p_aout )
+ aout_DecChangePause( p_owner->p_aout, b_paused, i_date );
+ }
+ else if( p_owner->fmt.i_cat == VIDEO_ES )
+ {
+ if( p_owner->p_vout )
+ vout_ChangePause( p_owner->p_vout, b_paused, i_date );
}
vlc_mutex_unlock( &p_owner->lock );
}
assert( p_owner->b_waiting );
vlc_mutex_lock( &p_owner->lock );
+ vlc_fifo_Lock( p_owner->p_fifo );
while( !p_owner->b_has_data )
{
- vlc_fifo_Lock( p_owner->p_fifo );
- p_owner->b_woken = true;
+ if( p_owner->b_idle )
+ {
+ assert( vlc_fifo_IsEmpty( p_owner->p_fifo ) );
+ msg_Warn( p_dec, "can't wait without data to decode" );
+ break;
+ }
vlc_fifo_Signal( p_owner->p_fifo );
vlc_fifo_Unlock( p_owner->p_fifo );
vlc_cond_wait( &p_owner->wait_acknowledge, &p_owner->lock );
+ vlc_fifo_Lock( p_owner->p_fifo );
}
+ vlc_fifo_Unlock( p_owner->p_fifo );
vlc_mutex_unlock( &p_owner->lock );
}