[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