[vlc-devel] [PATCH 2/8] aout: add Async API

Thomas Guillem thomas at gllm.fr
Fri Mar 8 17:24:40 CET 2019


This new API will be the entry point for the decoder.

All commands are executed asynchronously, one after the other, on a separate
thread with few exceptions.

aout_Start() and aout_Play() commands are blocking since we need to check the
return code. aout_Play() is also blocking in order to avoid to introduce a new
audio buffer. Indeed, audio buffering/delay must be handled by aout modules.

Some commands can be prepended or appended. This is the case for
aout_Restart(..., bool now). If 'now' is true, the command will be executed
now.
---
 src/Makefile.am                  |   1 +
 src/audio_output/aout_internal.h |  25 +++
 src/audio_output/async.c         | 299 +++++++++++++++++++++++++++++++
 src/audio_output/output.c        |   8 +
 4 files changed, 333 insertions(+)
 create mode 100644 src/audio_output/async.c

diff --git a/src/Makefile.am b/src/Makefile.am
index 7f6327c34e..542f8a850e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -302,6 +302,7 @@ libvlccore_la_SOURCES = \
 	input/thumbnailer.c \
 	input/var.c \
 	audio_output/aout_internal.h \
+	audio_output/async.c \
 	audio_output/common.c \
 	audio_output/dec.c \
 	audio_output/filters.c \
diff --git a/src/audio_output/aout_internal.h b/src/audio_output/aout_internal.h
index 1dd92e96aa..b221040597 100644
--- a/src/audio_output/aout_internal.h
+++ b/src/audio_output/aout_internal.h
@@ -26,6 +26,7 @@
 # include <stdatomic.h>
 
 # include <vlc_viewpoint.h>
+# include <vlc_list.h>
 # include "../clock/clock.h"
 
 /* Max input rate factor (1/4 -> 4) */
@@ -40,6 +41,15 @@ enum {
 typedef struct aout_volume aout_volume_t;
 typedef struct aout_dev aout_dev_t;
 
+struct aout_async
+{
+    vlc_mutex_t lock;
+    vlc_cond_t thread_wait;
+    vlc_cond_t client_wait;
+    vlc_thread_t thread;
+    struct vlc_list cmds;
+};
+
 typedef struct
 {
     vlc_mutex_t lock;
@@ -47,6 +57,8 @@ typedef struct
     aout_filters_t *filters;
     aout_volume_t *volume;
 
+    struct aout_async async;
+
     struct
     {
         vlc_mutex_t lock;
@@ -163,6 +175,19 @@ static inline void aout_SetWavePhysicalChannels(audio_sample_format_t *fmt)
     aout_FormatPrepare(fmt);
 }
 
+/* From async.c */
+int aout_AsyncInit(audio_output_t *);
+void aout_AsyncDestroy(audio_output_t *);
+int aout_Start(audio_output_t *, const audio_sample_format_t *,
+               struct vlc_clock_t *clock, const audio_replay_gain_t *);
+void aout_Stop(audio_output_t *);
+void aout_Restart(audio_output_t *, unsigned, bool now);
+int aout_Play(audio_output_t *, block_t *block);
+void aout_Flush(audio_output_t *, bool wait);
+void aout_ChangePause(audio_output_t *, bool paused, vlc_tick_t date);
+void aout_ChangeRate(audio_output_t *, float rate);
+void aout_ChangeDelay(audio_output_t *, vlc_tick_t delay);
+
 /* From filters.c */
 
 /* Extended version of aout_FiltersNew
diff --git a/src/audio_output/async.c b/src/audio_output/async.c
new file mode 100644
index 0000000000..513f5481c4
--- /dev/null
+++ b/src/audio_output/async.c
@@ -0,0 +1,299 @@
+/*****************************************************************************
+ * async.c : audio output async API
+ *****************************************************************************
+ * Copyright (C) 2019 VLC authors, VideoLAN and Videolabs SAS
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+
+#include <vlc_common.h>
+#include <vlc_aout.h>
+
+#include "aout_internal.h"
+
+static inline struct aout_async *aout_async (audio_output_t *aout)
+{
+    return &aout_owner(aout)->async;
+}
+
+enum aout_async_cmd_type
+{
+    AOUT_ASYNC_CMD_START,
+    AOUT_ASYNC_CMD_STOP,
+    AOUT_ASYNC_CMD_RESTART,
+    AOUT_ASYNC_CMD_PLAY,
+    AOUT_ASYNC_CMD_FLUSH,
+    AOUT_ASYNC_CMD_CHANGE_PAUSE,
+    AOUT_ASYNC_CMD_CHANGE_RATE,
+    AOUT_ASYNC_CMD_CHANGE_DELAY,
+    AOUT_ASYNC_CMD_CLOSE,
+};
+
+union aout_async_cmd_arg
+{
+    /* AOUT_ASYNC_CMD_START */
+    struct {
+        const audio_sample_format_t *fmt;
+        struct vlc_clock_t *clock;
+        const audio_replay_gain_t *replay_gain;
+    } start;
+
+    /* AOUT_ASYNC_CMD_RESTART */
+    unsigned restart_flag;
+
+    /* AOUT_ASYNC_CMD_PLAY */
+    block_t *block;
+
+    /* AOUT_ASYNC_CMD_FLUSH */
+    bool drain;
+
+    /* AOUT_ASYNC_CMD_CHANGE_PAUSE */
+    struct {
+        bool enabled;
+        vlc_tick_t date;
+    } pause;
+
+    /* AOUT_ASYNC_CMD_CHANGE_RATE */
+    float rate;
+
+    /* AOUT_ASYNC_CMD_CHANGE_DELAY */
+    vlc_tick_t delay;
+};
+
+struct aout_async_cmd
+{
+    enum aout_async_cmd_type type;
+    union aout_async_cmd_arg arg;
+    bool wait;
+    int ret;
+    struct vlc_list node;
+};
+
+static void *
+aout_Thread(void *data)
+{
+    audio_output_t *aout = data;
+    struct aout_async *async = aout_async(aout);
+
+    vlc_mutex_lock(&async->lock);
+    bool stop = false;
+    while (!stop)
+    {
+        while (vlc_list_is_empty(&async->cmds))
+            vlc_cond_wait(&async->thread_wait, &async->lock);
+
+        struct aout_async_cmd *cmd =
+            vlc_list_first_entry_or_null(&async->cmds,
+                                         struct aout_async_cmd, node);
+        assert(cmd);
+        const union aout_async_cmd_arg *arg = &cmd->arg;
+
+        vlc_mutex_unlock(&async->lock);
+
+        int ret = VLC_SUCCESS;
+        switch (cmd->type)
+        {
+            case AOUT_ASYNC_CMD_START:
+                assert(cmd->wait);
+                ret = aout_DecNew(aout, arg->start.fmt, arg->start.clock,
+                                  arg->start.replay_gain);
+                break;
+            case AOUT_ASYNC_CMD_STOP:
+                aout_DecDelete(aout);
+                break;
+            case AOUT_ASYNC_CMD_RESTART:
+                aout_RequestRestart(aout, arg->restart_flag);
+                break;
+            case AOUT_ASYNC_CMD_PLAY:
+                assert(cmd->wait);
+                ret = aout_DecPlay(aout, arg->block);
+                break;
+            case AOUT_ASYNC_CMD_FLUSH:
+                aout_DecFlush(aout, arg->drain);
+                break;
+            case AOUT_ASYNC_CMD_CHANGE_PAUSE:
+                aout_DecChangePause(aout, arg->pause.enabled, arg->pause.date);
+                break;
+            case AOUT_ASYNC_CMD_CHANGE_RATE:
+                aout_DecChangeRate(aout, arg->rate);
+                break;
+            case AOUT_ASYNC_CMD_CHANGE_DELAY:
+                aout_DecChangeDelay(aout, arg->delay);
+                break;
+            case AOUT_ASYNC_CMD_CLOSE:
+                stop = true;
+                break;
+        }
+
+        vlc_mutex_lock(&async->lock);
+        vlc_list_remove(&cmd->node);
+
+        if (cmd->wait)
+        {
+            cmd->wait = false;
+            cmd->ret = ret;
+            vlc_cond_broadcast(&async->client_wait);
+        }
+        else
+            free(cmd);
+    }
+
+    assert(vlc_list_is_empty(&async->cmds));
+    vlc_mutex_unlock(&async->lock);
+
+    return NULL;
+}
+
+static inline int
+aout_AsyncRun(audio_output_t *aout, enum aout_async_cmd_type type,
+              union aout_async_cmd_arg arg, bool wait, bool prepend)
+{
+    struct aout_async *async = aout_async(aout);
+
+    struct aout_async_cmd *cmd = malloc(sizeof(*cmd));
+    if (!cmd)
+        return VLC_ENOMEM;
+    cmd->type = type;
+    cmd->arg = arg;
+    cmd->wait = wait;
+    int ret = cmd->ret = VLC_SUCCESS;
+
+    vlc_mutex_lock(&async->lock);
+
+    if (prepend)
+        vlc_list_prepend(&cmd->node, &async->cmds);
+    else
+        vlc_list_append(&cmd->node, &async->cmds);
+    vlc_cond_signal(&async->thread_wait);
+    if (wait)
+    {
+        while (cmd->wait)
+            vlc_cond_wait(&async->client_wait, &async->lock);
+        ret = cmd->ret;
+        free(cmd);
+    }
+
+    vlc_mutex_unlock(&async->lock);
+    return ret;
+}
+
+int
+aout_Start(audio_output_t *aout, const audio_sample_format_t *fmt,
+           struct vlc_clock_t *clock, const audio_replay_gain_t *replay_gain)
+{
+    return aout_AsyncRun(aout, AOUT_ASYNC_CMD_START,
+        (union aout_async_cmd_arg) {
+            .start = { fmt, clock, replay_gain }
+        }, true, false);
+}
+
+void
+aout_Stop(audio_output_t *aout)
+{
+    aout_AsyncRun(aout, AOUT_ASYNC_CMD_STOP,
+                  (union aout_async_cmd_arg) { 0 }, false, false);
+}
+
+void aout_Restart(audio_output_t *aout, unsigned restart_flag, bool now)
+{
+    aout_AsyncRun(aout, AOUT_ASYNC_CMD_RESTART,
+        (union aout_async_cmd_arg) {
+            .restart_flag = restart_flag
+        }, false, now);
+}
+
+int
+aout_Play(audio_output_t *aout, block_t *block)
+{
+    return aout_AsyncRun(aout, AOUT_ASYNC_CMD_PLAY,
+        (union aout_async_cmd_arg) {
+            .block = block
+        }, true, false);
+}
+
+void
+aout_Flush(audio_output_t *aout, bool drain)
+{
+    aout_AsyncRun(aout, AOUT_ASYNC_CMD_FLUSH,
+        (union aout_async_cmd_arg) {
+            .drain = drain
+        }, false, false);
+}
+
+void
+aout_ChangePause(audio_output_t *aout, bool paused, vlc_tick_t date)
+{
+    aout_AsyncRun(aout, AOUT_ASYNC_CMD_CHANGE_PAUSE,
+        (union aout_async_cmd_arg) {
+            .pause = { paused, date }
+        }, false, false);
+}
+
+void
+aout_ChangeRate(audio_output_t *aout, float rate)
+{
+    aout_AsyncRun(aout, AOUT_ASYNC_CMD_CHANGE_RATE,
+        (union aout_async_cmd_arg) {
+            .rate = rate
+        }, false, false);
+}
+
+void
+aout_ChangeDelay(audio_output_t *aout, vlc_tick_t delay)
+{
+    aout_AsyncRun(aout, AOUT_ASYNC_CMD_CHANGE_DELAY,
+        (union aout_async_cmd_arg) {
+            .delay = delay
+        }, false, false);
+}
+
+int
+aout_AsyncInit(audio_output_t *aout)
+{
+    struct aout_async *async = aout_async(aout);
+
+    vlc_mutex_init(&async->lock);
+    vlc_cond_init(&async->thread_wait);
+    vlc_cond_init(&async->client_wait);
+    vlc_list_init(&async->cmds);
+
+    if (vlc_clone(&async->thread, aout_Thread, aout,
+                  VLC_THREAD_PRIORITY_AUDIO) != 0)
+    {
+        vlc_mutex_destroy(&async->lock);
+        return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+void
+aout_AsyncDestroy(audio_output_t *aout)
+{
+    struct aout_async *async = aout_async(aout);
+
+    aout_AsyncRun(aout, AOUT_ASYNC_CMD_CLOSE,
+                  (union aout_async_cmd_arg) { 0 }, false, false);
+
+    vlc_join(async->thread, NULL);
+    vlc_mutex_destroy(&async->lock);
+    vlc_cond_destroy(&async->thread_wait);
+    vlc_cond_destroy(&async->client_wait);
+}
diff --git a/src/audio_output/output.c b/src/audio_output/output.c
index f12121a704..5b3447fb9f 100644
--- a/src/audio_output/output.c
+++ b/src/audio_output/output.c
@@ -237,6 +237,12 @@ audio_output_t *aout_New (vlc_object_t *parent)
     if (unlikely(aout == NULL))
         return NULL;
 
+    if (aout_AsyncInit(aout) != VLC_SUCCESS)
+    {
+        vlc_object_delete(aout);
+        return NULL;
+    }
+
     aout_owner_t *owner = aout_owner (aout);
 
     vlc_mutex_init (&owner->lock);
@@ -378,6 +384,8 @@ void aout_Destroy (audio_output_t *aout)
     aout->device_select = NULL;
     aout_OutputUnlock (aout);
 
+    aout_AsyncDestroy(aout);
+
     var_DelCallback (aout, "viewpoint", ViewpointCallback, NULL);
     var_DelCallback (aout, "audio-filter", FilterCallback, NULL);
     var_DelCallback(aout, "device", var_CopyDevice, vlc_object_parent(aout));
-- 
2.20.1



More information about the vlc-devel mailing list