[vlc-devel] [PATCH 6/6] aout: make the drain implementation asynchronous

Thomas Guillem thomas at gllm.fr
Tue Mar 12 16:23:54 CET 2019


Yes, only the waveout plugin can do an asynchronous drain (for now).

Only the plugin implementation is asynchronous (for now), aout_DecDrain() is
still synchronous since it will wait for drain end from this call. The goal is
to make aout plugins non-blocking so that we can easily change the aout core
implementation in the future.
---
 include/vlc_aout.h               | 16 +++++++++++++++-
 modules/audio_output/waveout.c   | 23 ++++++++++++++++++-----
 src/audio_output/aout_internal.h |  2 ++
 src/audio_output/dec.c           |  9 +++++++++
 src/audio_output/output.c        |  3 +++
 5 files changed, 47 insertions(+), 6 deletions(-)

diff --git a/include/vlc_aout.h b/include/vlc_aout.h
index d25abd874b..4441a42a82 100644
--- a/include/vlc_aout.h
+++ b/include/vlc_aout.h
@@ -112,6 +112,7 @@
 #include <vlc_block.h>
 
 struct vlc_audio_output_events {
+    void (*drained_report)(audio_output_t *);
     void (*timing_report)(audio_output_t *, vlc_tick_t system_now, vlc_tick_t pts);
     void (*volume_report)(audio_output_t *, float);
     void (*mute_report)(audio_output_t *, bool);
@@ -221,7 +222,7 @@ struct audio_output
       * \note This callback cannot be called in stopped state.
       */
     void (*drain)(audio_output_t *);
-    /**< Drain the playback buffers (can be NULL).
+    /**< Drain the playback buffers asynchronously (can be NULL).
       *
       * The implementation can invalidate the stream since this is generally
       * the last call before a stop(). However, a flush() could also be called
@@ -230,6 +231,9 @@ struct audio_output
       *
       * If NULL, the caller will wait for the delay returned by time_get before
       * calling stop().
+      *
+      * \note The implementation must call aout_DrainedReport() from or after
+      * this callback.
       */
 
     int (*volume_set)(audio_output_t *, float volume);
@@ -446,6 +450,16 @@ static inline void aout_RestartRequest(audio_output_t *aout, unsigned mode)
     aout->events->restart_request(aout, mode);
 }
 
+/**
+ * Report that the stream is drained
+ *
+ * This function can only be called one time after or from aout->drain().
+ */
+static inline void aout_DrainedReport(audio_output_t *aout)
+{
+    aout->events->drained_report(aout);
+}
+
 /**
  * Default implementation for audio_output_t.time_get
  */
diff --git a/modules/audio_output/waveout.c b/modules/audio_output/waveout.c
index 3c9884ce01..495fe519bc 100644
--- a/modules/audio_output/waveout.c
+++ b/modules/audio_output/waveout.c
@@ -119,6 +119,8 @@ struct aout_sys_t
     bool b_soft;                            /* Use software gain */
     uint8_t chans_to_reorder;              /* do we need channel reordering */
 
+    bool b_draining;
+
     uint8_t chan_table[AOUT_CHAN_MAX];
     vlc_fourcc_t format;
 
@@ -332,6 +334,7 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
     sys->i_frames = 0;
     sys->i_played_length = 0;
     sys->p_free_list = NULL;
+    sys->b_draining = false;
 
     fmt->channel_type = AUDIO_CHANNEL_TYPE_BITMAP;
 
@@ -395,7 +398,11 @@ static void Stop( audio_output_t *p_aout )
     MMRESULT result = waveOutReset( p_sys->h_waveout );
 
     /* wait for the frames to be queued in cleaning list */
-    WaveOutDrain( p_aout );
+    vlc_mutex_lock( &p_sys->lock );
+    while( p_sys->i_frames )
+        vlc_cond_wait( &p_sys->cond, &p_sys->lock );
+    vlc_mutex_unlock( &p_sys->lock );
+
     WaveOutClean( p_sys );
 
     /* now we can Close the device */
@@ -650,6 +657,11 @@ static void CALLBACK WaveOutCallback( HWAVEOUT h_waveout, UINT uMsg,
     p_waveheader->p_next = sys->p_free_list;
     sys->p_free_list = p_waveheader;
     sys->i_frames--;
+    if (sys->b_draining)
+    {
+        sys->b_draining = false;
+        aout_DrainedReport((audio_output_t *)_p_aout);
+    }
     vlc_cond_broadcast( &sys->cond );
     vlc_mutex_unlock( &sys->lock );
 }
@@ -863,11 +875,12 @@ static void WaveOutFlush( audio_output_t *p_aout)
 
 static void WaveOutDrain( audio_output_t *p_aout)
 {
+    aout_sys_t *sys = p_aout->sys;
     vlc_mutex_lock( &sys->lock );
-    while( sys->i_frames )
-    {
-        vlc_cond_wait( &sys->cond, &sys->lock );
-    }
+    if( sys->i_frames )
+        sys->b_draining = true;
+    else
+        aout_DrainedReport( p_aout );
     vlc_mutex_unlock( &sys->lock );
 }
 
diff --git a/src/audio_output/aout_internal.h b/src/audio_output/aout_internal.h
index ed72cff0f8..669c81b67f 100644
--- a/src/audio_output/aout_internal.h
+++ b/src/audio_output/aout_internal.h
@@ -85,6 +85,7 @@ typedef struct
     atomic_uint buffers_played;
     atomic_uchar restart;
 
+    vlc_sem_t drain_sem;
     bool drained;
 } aout_owner_t;
 
@@ -147,6 +148,7 @@ void aout_DecDrain(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);
+void aout_Drained(audio_output_t *);
 
 static inline void aout_InputRequestRestart(audio_output_t *aout)
 {
diff --git a/src/audio_output/dec.c b/src/audio_output/dec.c
index 683f181f9f..91bfb4f147 100644
--- a/src/audio_output/dec.c
+++ b/src/audio_output/dec.c
@@ -531,6 +531,12 @@ void aout_DecFlush(audio_output_t *aout)
     owner->drained = false;
 }
 
+void aout_Drained(audio_output_t *aout)
+{
+    aout_owner_t *owner = aout_owner (aout);
+    vlc_sem_post(&owner->drain_sem);
+}
+
 void aout_DecDrain(audio_output_t *aout)
 {
     aout_owner_t *owner = aout_owner (aout);
@@ -544,7 +550,10 @@ void aout_DecDrain(audio_output_t *aout)
         aout->play(aout, block, vlc_tick_now());
 
     if (aout->drain)
+    {
         aout->drain(aout);
+        vlc_sem_wait(&owner->drain_sem);
+    }
     else
     {
         vlc_tick_t delay;
diff --git a/src/audio_output/output.c b/src/audio_output/output.c
index cfc786f4a1..3f1cfbcbbc 100644
--- a/src/audio_output/output.c
+++ b/src/audio_output/output.c
@@ -165,6 +165,7 @@ static int aout_GainNotify (audio_output_t *aout, float gain)
 }
 
 static const struct vlc_audio_output_events aout_events = {
+    aout_Drained,
     aout_TimingNotify,
     aout_VolumeNotify,
     aout_MuteNotify,
@@ -242,6 +243,7 @@ audio_output_t *aout_New (vlc_object_t *parent)
     vlc_mutex_init (&owner->lock);
     vlc_mutex_init (&owner->dev.lock);
     vlc_mutex_init (&owner->vp.lock);
+    vlc_sem_init(&owner->drain_sem, 0);
     vlc_viewpoint_init (&owner->vp.value);
     atomic_init (&owner->vp.update, false);
 
@@ -397,6 +399,7 @@ static void aout_Destructor (vlc_object_t *obj)
     audio_output_t *aout = (audio_output_t *)obj;
     aout_owner_t *owner = aout_owner (aout);
 
+    vlc_sem_destroy(&owner->drain_sem);
     vlc_mutex_destroy (&owner->dev.lock);
     for (aout_dev_t *dev = owner->dev.list, *next; dev != NULL; dev = next)
     {
-- 
2.20.1



More information about the vlc-devel mailing list