[vlc-devel] [RFC PATCH 4/6] aout: add drain cb and a default implementation
Thomas Guillem
thomas at gllm.fr
Thu Mar 16 16:43:23 CET 2017
If the aout doesn't implement the drain callback, there is a default
implementation based on the "loosy" mmdevice one. This default implementation
gets the aout delay via time_get() and schedule a timer that will set the
drained atomic to true after that delay.
pause(), flush() and stop() can be called while draining, but flush() and
stop() should reset the draining state.
--
Will be squashed with 3/6
---
include/vlc_aout.h | 8 ++++
src/audio_output/aout_internal.h | 7 ++++
src/audio_output/dec.c | 23 ++++++++++
src/audio_output/output.c | 91 ++++++++++++++++++++++++++++++++++++----
4 files changed, 121 insertions(+), 8 deletions(-)
diff --git a/include/vlc_aout.h b/include/vlc_aout.h
index 7fc04ff146..32be678a94 100644
--- a/include/vlc_aout.h
+++ b/include/vlc_aout.h
@@ -145,6 +145,13 @@ struct audio_output
/**< Flushes the playback buffers (mandatory, cannot be NULL).
* \note A stream must have been started when called.
*/
+ int (*drain)(audio_output_t *);
+ /**< Drains the playback buffers (optional, may be NULL).
+ * This function should not wait for the drain to finish. Call
+ * event.drain_report() to notify when the aout is drained. flush(),
+ * pause() or stop() can be called while the aout is draining. The
+ * draining state should be reseted after flush() and stop().
+ * \return 0 on success, non-zero on failure or lack of data
* \note A stream must have been started when called.
*/
int (*volume_set)(audio_output_t *, float volume);
@@ -170,6 +177,7 @@ struct audio_output
void (*policy_report)(audio_output_t *, bool);
void (*device_report)(audio_output_t *, const char *);
void (*hotplug_report)(audio_output_t *, const char *, const char *);
+ void (*drain_report)(audio_output_t *);
int (*gain_request)(audio_output_t *, float);
void (*restart_request)(audio_output_t *, unsigned);
} event;
diff --git a/src/audio_output/aout_internal.h b/src/audio_output/aout_internal.h
index c6a0f7f728..70dcc5f4b6 100644
--- a/src/audio_output/aout_internal.h
+++ b/src/audio_output/aout_internal.h
@@ -83,6 +83,10 @@ typedef struct
atomic_uint buffers_lost;
atomic_uint buffers_played;
atomic_uchar restart;
+
+ bool draining;
+ atomic_bool drained;
+ vlc_timer_t drain_timer;
} aout_owner_t;
typedef struct
@@ -119,6 +123,7 @@ int aout_OutputTimeGet(audio_output_t *, mtime_t *);
void aout_OutputPlay(audio_output_t *, block_t *);
void aout_OutputPause( audio_output_t * p_aout, bool, mtime_t );
void aout_OutputFlush( audio_output_t * p_aout );
+void aout_OutputDrain( audio_output_t * p_aout );
void aout_OutputDelete( audio_output_t * p_aout );
void aout_OutputLock(audio_output_t *);
void aout_OutputUnlock(audio_output_t *);
@@ -145,6 +150,8 @@ int aout_DecPlay(audio_output_t *, block_t *, int i_input_rate);
void aout_DecGetResetStats(audio_output_t *, unsigned *, unsigned *);
void aout_DecChangePause(audio_output_t *, bool b_paused, mtime_t i_date);
void aout_DecFlush(audio_output_t *);
+void aout_DecDrain(audio_output_t *);
+bool aout_DecDrained(audio_output_t *);
void aout_RequestRestart (audio_output_t *, unsigned);
static inline void aout_InputRequestRestart(audio_output_t *aout)
diff --git a/src/audio_output/dec.c b/src/audio_output/dec.c
index bf0027520e..2a09f29a40 100644
--- a/src/audio_output/dec.c
+++ b/src/audio_output/dec.c
@@ -80,6 +80,7 @@ int aout_DecNew( audio_output_t *p_aout,
owner->volume = aout_volume_New (p_aout, p_replay_gain);
atomic_store (&owner->restart, 0);
+ atomic_store (&owner->drained, false);
owner->input_format = *p_format;
owner->mixer_format = owner->input_format;
owner->request_vout = *p_request_vout;
@@ -439,3 +440,25 @@ void aout_DecFlush (audio_output_t *aout)
}
aout_OutputUnlock (aout);
}
+
+void aout_DecDrain (audio_output_t *aout)
+{
+ aout_owner_t *owner = aout_owner (aout);
+
+ aout_OutputLock (aout);
+ owner->sync.end = VLC_TS_INVALID;
+ if (owner->mixer_format.i_format)
+ {
+ block_t *block = aout_FiltersDrain (owner->filters);
+ if (block)
+ aout_OutputPlay (aout, block);
+ aout_OutputDrain (aout);
+ }
+ aout_OutputUnlock (aout);
+}
+
+bool aout_DecDrained(audio_output_t *aout)
+{
+ aout_owner_t *owner = aout_owner (aout);
+ return atomic_load (&owner->drained);
+}
diff --git a/src/audio_output/output.c b/src/audio_output/output.c
index 9dadcf8537..f16c68648a 100644
--- a/src/audio_output/output.c
+++ b/src/audio_output/output.c
@@ -162,6 +162,12 @@ static int aout_GainNotify (audio_output_t *aout, float gain)
return 0;
}
+static void aout_DrainNotify (audio_output_t *aout)
+{
+ aout_owner_t *owner = aout_owner (aout);
+ atomic_store( &owner->drained, true );
+}
+
static int FilterCallback (vlc_object_t *obj, const char *var,
vlc_value_t prev, vlc_value_t cur, void *data)
{
@@ -193,6 +199,10 @@ audio_output_t *aout_New (vlc_object_t *parent)
owner->req.volume = -1.f;
owner->req.mute = -1;
+ owner->draining = false;
+ atomic_init (&owner->drained, false);
+ owner->drain_timer = NULL;
+
vlc_object_set_destructor (aout, aout_Destructor);
/* Audio output module callbacks */
@@ -208,6 +218,7 @@ audio_output_t *aout_New (vlc_object_t *parent)
aout->event.policy_report = aout_PolicyNotify;
aout->event.device_report = aout_DeviceNotify;
aout->event.hotplug_report = aout_HotplugNotify;
+ aout->event.drain_report = aout_DrainNotify;
aout->event.gain_request = aout_GainNotify;
aout->event.restart_request = aout_RestartNotify;
@@ -362,6 +373,36 @@ static void aout_Destructor (vlc_object_t *obj)
vlc_mutex_destroy (&owner->lock);
}
+static void DrainTimerCb( void *data )
+{
+ aout_DrainNotify( data );
+}
+
+static int DrainTimerSchedule( audio_output_t *aout, mtime_t delay )
+{
+ aout_owner_t *owner = aout_owner (aout);
+ assert( owner->drain_timer == NULL);
+
+ if( vlc_timer_create( &owner->drain_timer, DrainTimerCb, aout ) == 0 )
+ {
+ vlc_timer_schedule( owner->drain_timer, false, delay, 0 );
+ return 0;
+ }
+ else
+ return -1;
+}
+
+static void DrainTimerReset( audio_output_t *aout )
+{
+ aout_owner_t *owner = aout_owner (aout);
+ if( owner->drain_timer != NULL )
+ {
+ vlc_timer_schedule( owner->drain_timer, false, 0, 0 );
+ vlc_timer_destroy( owner->drain_timer );
+ owner->drain_timer = NULL;
+ }
+}
+
/**
* Starts an audio output stream.
* \param fmt audio output stream format [IN/OUT]
@@ -457,11 +498,15 @@ int aout_OutputNew (audio_output_t *aout, audio_sample_format_t *restrict fmt)
*/
void aout_OutputDelete (audio_output_t *aout)
{
+ aout_owner_t *owner = aout_owner (aout);
aout_OutputAssertLocked (aout);
var_DelCallback (aout, "stereo-mode", aout_ChannelsRestart, NULL);
if (aout->stop != NULL)
aout->stop (aout);
+ DrainTimerReset (aout);
+ atomic_store (&owner->drained, false);
+ owner->draining = false;
}
int aout_OutputTimeGet (audio_output_t *aout, mtime_t *delay)
@@ -491,13 +536,6 @@ void aout_OutputPlay (audio_output_t *aout, block_t *block)
aout->play (aout, block);
}
-static void PauseDefault (audio_output_t *aout, bool pause, mtime_t date)
-{
- if (pause)
- aout_OutputFlush (aout, false);
- (void) date;
-}
-
/**
* Notifies the audio output (if any) of pause/resume events.
* This enables the output to expedite pause, instead of waiting for its
@@ -507,8 +545,19 @@ static void PauseDefault (audio_output_t *aout, bool pause, mtime_t date)
*/
void aout_OutputPause( audio_output_t *aout, bool pause, mtime_t date )
{
+ aout_owner_t *owner = aout_owner (aout);
aout_OutputAssertLocked (aout);
- ((aout->pause != NULL) ? aout->pause : PauseDefault) (aout, pause, date);
+
+ if( aout->pause != NULL )
+ {
+ aout->pause( aout, pause, date );
+ if( pause )
+ DrainTimerReset( aout );
+ else if( owner->draining )
+ aout_OutputDrain( aout );
+ }
+ else if( pause )
+ aout_OutputFlush (aout);
}
/**
@@ -519,8 +568,34 @@ void aout_OutputPause( audio_output_t *aout, bool pause, mtime_t date )
*/
void aout_OutputFlush( audio_output_t *aout )
{
+ aout_owner_t *owner = aout_owner (aout);
aout_OutputAssertLocked( aout );
+
aout->flush( aout );
+ DrainTimerReset( aout );
+ owner->draining = false;
+ atomic_store (&owner->drained, false);
+}
+
+/**
+ * Drains the audio output buffers.
+ * \note This can only be called after a successful aout_OutputNew().
+ * \warning The caller must hold the audio output lock.
+ */
+void aout_OutputDrain( audio_output_t *aout )
+{
+ aout_owner_t *owner = aout_owner (aout);
+ aout_OutputAssertLocked( aout );
+
+ owner->draining = true;
+ if( aout->drain == NULL || aout->drain( aout ) != VLC_SUCCESS )
+ {
+ /* Loosy drain emulation */
+ mtime_t delay;
+ if( aout_OutputTimeGet( aout, &delay ) != 0
+ || DrainTimerSchedule( aout, delay ) != 0)
+ aout_DrainNotify( aout );
+ }
}
static int aout_OutputVolumeSet (audio_output_t *aout, float vol)
--
2.11.0
More information about the vlc-devel
mailing list