[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