Browse Source

aout: make drain asynchronous

aout_DecDrain() is now asynchronous, except for modules still implementing
aout->drain (only during the transition).

Add aout->drain_async(). Aout modules should implement drain_async
instead of drain, and call aout_DrainedReport() to report that the
stream is drained.

Aout modules not implementing any drain functions will still rely on
aout_TimeGet() to detect the end of the stream.
pull/134/head
Thomas Guillem 5 years ago
committed by Rémi Denis-Courmont
parent
commit
0dd8096f46
  1. 23
      include/vlc_aout.h
  2. 7
      src/audio_output/aout_internal.h
  3. 60
      src/audio_output/dec.c
  4. 10
      src/audio_output/output.c
  5. 9
      src/input/decoder.c

23
include/vlc_aout.h

@ -126,6 +126,7 @@
struct vlc_audio_output_events {
void (*timing_report)(audio_output_t *, vlc_tick_t system_now, vlc_tick_t pts);
void (*drained_report)(audio_output_t *);
void (*volume_report)(audio_output_t *, float);
void (*mute_report)(audio_output_t *, bool);
void (*policy_report)(audio_output_t *, bool);
@ -250,6 +251,20 @@ struct audio_output
* calling stop().
*/
void (*drain_async)(audio_output_t *);
/**< Drain the playback buffers asynchronously (can be NULL).
*
* A drain operation can be cancelled by aout->flush() or aout->stop().
*
* It is legal to continue playback after a drain_async, if flush() is
* called before the next play().
*
* Call aout_DrainedReport() to notify that the stream is drained.
*
* If NULL, the caller will wait for the delay returned by time_get before
* calling stop().
*/
int (*volume_set)(audio_output_t *, float volume);
/**< Changes playback volume (optional, may be NULL).
*
@ -294,6 +309,14 @@ static inline int aout_TimeGet(audio_output_t *aout, vlc_tick_t *delay)
return aout->time_get(aout, delay);
}
/**
* Report than the stream is drained (after a call to aout->drain_async)
*/
static inline void aout_DrainedReport(audio_output_t *aout)
{
aout->events->drained_report(aout);
}
/**
* Report change of configured audio volume to the core and UI.
*/

7
src/audio_output/aout_internal.h

@ -50,6 +50,9 @@ typedef struct
aout_volume_t *volume;
bool bitexact;
atomic_bool drained;
_Atomic vlc_tick_t drain_deadline;
struct
{
vlc_mutex_t lock;
@ -166,6 +169,10 @@ void aout_DecChangeRate(audio_output_t *aout, float rate);
void aout_DecChangeDelay(audio_output_t *aout, vlc_tick_t delay);
void aout_DecFlush(audio_output_t *);
void aout_DecDrain(audio_output_t *);
/* Contrary to other aout_Dec*() functions, this function can be called from
* any threads */
bool aout_DecIsDrained(audio_output_t *);
void aout_RequestRestart (audio_output_t *, unsigned);
void aout_RequestRetiming(audio_output_t *aout, vlc_tick_t system_ts,
vlc_tick_t audio_ts);

60
src/audio_output/dec.c

@ -38,18 +38,6 @@
#include "clock/clock.h"
#include "libvlc.h"
static void aout_Drain(audio_output_t *aout)
{
if (aout->drain)
aout->drain(aout);
else
{
vlc_tick_t delay;
if (aout_TimeGet(aout, &delay) == 0)
vlc_tick_sleep(delay);
}
}
/**
* Creates an audio output
*/
@ -586,10 +574,30 @@ void aout_DecFlush(audio_output_t *aout)
owner->sync.delay = 0;
}
}
atomic_store_explicit(&owner->drained, false, memory_order_relaxed);
atomic_store_explicit(&owner->drain_deadline, VLC_TICK_INVALID,
memory_order_relaxed);
owner->sync.discontinuity = true;
owner->original_pts = VLC_TICK_INVALID;
}
bool aout_DecIsDrained(audio_output_t *aout)
{
aout_owner_t *owner = aout_owner (aout);
if (aout->drain_async == NULL)
{
vlc_tick_t drain_deadline =
atomic_load_explicit(&owner->drain_deadline, memory_order_relaxed);
return drain_deadline != VLC_TICK_INVALID
&& vlc_tick_now() >= drain_deadline;
}
else
return atomic_load_explicit(&owner->drained, memory_order_relaxed);
}
void aout_DecDrain(audio_output_t *aout)
{
aout_owner_t *owner = aout_owner (aout);
@ -604,7 +612,33 @@ void aout_DecDrain(audio_output_t *aout)
aout->play(aout, block, vlc_tick_now());
}
aout_Drain(aout);
if (aout->drain)
{
aout->drain(aout);
aout_DrainedReport(aout);
}
else if (aout->drain_async)
{
assert(!atomic_load_explicit(&owner->drained, memory_order_relaxed));
aout->drain_async(aout);
}
else
{
assert(atomic_load_explicit(&owner->drain_deadline,
memory_order_relaxed) == VLC_TICK_INVALID);
vlc_tick_t drain_deadline = vlc_tick_now();
vlc_tick_t delay;
if (aout_TimeGet(aout, &delay) == 0)
drain_deadline += delay;
/* else the deadline is now, and aout_DecIsDrained() will return true
* on the first call. */
atomic_store_explicit(&owner->drain_deadline, drain_deadline,
memory_order_relaxed);
}
vlc_clock_Reset(owner->sync.clock);
if (owner->filters)

10
src/audio_output/output.c

@ -69,6 +69,12 @@ static void aout_TimingNotify(audio_output_t *aout, vlc_tick_t system_ts,
aout_RequestRetiming(aout, system_ts, audio_ts);
}
static void aout_DrainedNotify(audio_output_t *aout)
{
aout_owner_t *owner = aout_owner (aout);
atomic_store_explicit(&owner->drained, true, memory_order_relaxed);
}
/**
* Supply or update the current custom ("hardware") volume.
* @param volume current custom volume
@ -160,6 +166,7 @@ static int aout_GainNotify (audio_output_t *aout, float gain)
static const struct vlc_audio_output_events aout_events = {
aout_TimingNotify,
aout_DrainedNotify,
aout_VolumeNotify,
aout_MuteNotify,
aout_PolicyNotify,
@ -243,6 +250,9 @@ audio_output_t *aout_New (vlc_object_t *parent)
vlc_atomic_rc_init(&owner->rc);
vlc_audio_meter_Init(&owner->meter, aout);
atomic_init(&owner->drained, false);
atomic_init(&owner->drain_deadline, VLC_TICK_INVALID);
/* Audio output module callbacks */
var_Create (aout, "volume", VLC_VAR_FLOAT);
var_AddCallback (aout, "volume", var_Copy, parent);

9
src/input/decoder.c

@ -158,7 +158,6 @@ struct vlc_input_decoder_t
/* Flushing */
bool flushing;
bool b_draining;
atomic_bool drained;
bool b_idle;
bool aborting;
@ -1748,10 +1747,7 @@ static void *DecoderThread( void *p_data )
vlc_mutex_lock( &p_owner->lock );
vlc_fifo_Lock( p_owner->p_fifo );
if( p_owner->b_draining && (frame == NULL) )
{
p_owner->b_draining = false;
p_owner->drained = true;
}
vlc_cond_signal( &p_owner->wait_acknowledge );
vlc_mutex_unlock( &p_owner->lock );
}
@ -1857,7 +1853,6 @@ CreateDecoder( vlc_object_t *p_parent, const es_format_t *fmt,
p_owner->flushing = false;
p_owner->b_draining = false;
p_owner->drained = false;
atomic_init( &p_owner->reload, RELOAD_NO_REQUEST );
p_owner->b_idle = false;
@ -2289,8 +2284,8 @@ bool vlc_input_decoder_IsEmpty( vlc_input_decoder_t * p_owner )
#endif
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_draining || p_owner->drained;
else if( p_owner->fmt.i_cat == AUDIO_ES && p_owner->p_aout != NULL )
b_empty = aout_DecIsDrained( p_owner->p_aout );
else
b_empty = true; /* TODO subtitles support */
vlc_mutex_unlock( &p_owner->lock );

Loading…
Cancel
Save