summaryrefslogtreecommitdiffstats
path: root/audio/out/ao_null.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:36:56 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:36:56 +0000
commit51de1d8436100f725f3576aefa24a2bd2057bc28 (patch)
treec6d1d5264b6d40a8d7ca34129f36b7d61e188af3 /audio/out/ao_null.c
parentInitial commit. (diff)
downloadmpv-51de1d8436100f725f3576aefa24a2bd2057bc28.tar.xz
mpv-51de1d8436100f725f3576aefa24a2bd2057bc28.zip
Adding upstream version 0.37.0.upstream/0.37.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'audio/out/ao_null.c')
-rw-r--r--audio/out/ao_null.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/audio/out/ao_null.c b/audio/out/ao_null.c
new file mode 100644
index 0000000..fcb61d2
--- /dev/null
+++ b/audio/out/ao_null.c
@@ -0,0 +1,230 @@
+/*
+ * null audio output driver
+ *
+ * This file is part of mpv.
+ *
+ * mpv 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.
+ *
+ * mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Note: this does much more than just ignoring audio output. It simulates
+ * (to some degree) an ideal AO.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "mpv_talloc.h"
+
+#include "osdep/timer.h"
+#include "options/m_option.h"
+#include "common/common.h"
+#include "common/msg.h"
+#include "audio/format.h"
+#include "ao.h"
+#include "internal.h"
+
+struct priv {
+ bool paused;
+ double last_time;
+ float buffered; // samples
+ int buffersize; // samples
+ bool playing;
+
+ bool untimed;
+ float bufferlen; // seconds
+ float speed; // multiplier
+ float latency_sec; // seconds
+ float latency; // samples
+ bool broken_eof;
+ bool broken_delay;
+
+ // Minimal unit of audio samples that can be written at once. If play() is
+ // called with sizes not aligned to this, a rounded size will be returned.
+ // (This is not needed by the AO API, but many AOs behave this way.)
+ int outburst; // samples
+
+ struct m_channels channel_layouts;
+ int format;
+};
+
+static void drain(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+
+ if (ao->untimed) {
+ priv->buffered = 0;
+ return;
+ }
+
+ if (priv->paused)
+ return;
+
+ double now = mp_time_sec();
+ if (priv->buffered > 0) {
+ priv->buffered -= (now - priv->last_time) * ao->samplerate * priv->speed;
+ if (priv->buffered < 0)
+ priv->buffered = 0;
+ }
+ priv->last_time = now;
+}
+
+static int init(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+
+ if (priv->format)
+ ao->format = priv->format;
+
+ ao->untimed = priv->untimed;
+
+ struct mp_chmap_sel sel = {.tmp = ao};
+ if (priv->channel_layouts.num_chmaps) {
+ for (int n = 0; n < priv->channel_layouts.num_chmaps; n++)
+ mp_chmap_sel_add_map(&sel, &priv->channel_layouts.chmaps[n]);
+ } else {
+ mp_chmap_sel_add_any(&sel);
+ }
+ if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
+ mp_chmap_from_channels(&ao->channels, 2);
+
+ priv->latency = priv->latency_sec * ao->samplerate;
+
+ // A "buffer" for this many seconds of audio
+ int bursts = (int)(ao->samplerate * priv->bufferlen + 1) / priv->outburst;
+ ao->device_buffer = priv->outburst * bursts + priv->latency;
+
+ priv->last_time = mp_time_sec();
+
+ return 0;
+}
+
+// close audio device
+static void uninit(struct ao *ao)
+{
+}
+
+// stop playing and empty buffers (for seeking/pause)
+static void reset(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+ priv->buffered = 0;
+ priv->playing = false;
+}
+
+static void start(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+
+ if (priv->paused)
+ MP_ERR(ao, "illegal state: start() while paused\n");
+
+ drain(ao);
+ priv->paused = false;
+ priv->last_time = mp_time_sec();
+ priv->playing = true;
+}
+
+static bool set_pause(struct ao *ao, bool paused)
+{
+ struct priv *priv = ao->priv;
+
+ if (!priv->playing)
+ MP_ERR(ao, "illegal state: set_pause() while not playing\n");
+
+ if (priv->paused != paused) {
+
+ drain(ao);
+ priv->paused = paused;
+ if (!priv->paused)
+ priv->last_time = mp_time_sec();
+ }
+
+ return true;
+}
+
+static bool audio_write(struct ao *ao, void **data, int samples)
+{
+ struct priv *priv = ao->priv;
+
+ if (priv->buffered <= 0)
+ priv->buffered = priv->latency; // emulate fixed latency
+
+ priv->buffered += samples;
+ return true;
+}
+
+static void get_state(struct ao *ao, struct mp_pcm_state *state)
+{
+ struct priv *priv = ao->priv;
+
+ drain(ao);
+
+ state->free_samples = ao->device_buffer - priv->latency - priv->buffered;
+ state->free_samples = state->free_samples / priv->outburst * priv->outburst;
+ state->queued_samples = priv->buffered;
+
+ // Note how get_state returns the delay in audio device time (instead of
+ // adjusting for speed), since most AOs seem to also do that.
+ state->delay = priv->buffered;
+
+ // Drivers with broken EOF handling usually always report the same device-
+ // level delay that is additional to the buffer time.
+ if (priv->broken_eof && priv->buffered < priv->latency)
+ state->delay = priv->latency;
+
+ state->delay /= ao->samplerate;
+
+ if (priv->broken_delay) { // Report only multiples of outburst
+ double q = priv->outburst / (double)ao->samplerate;
+ if (state->delay > 0)
+ state->delay = (int)(state->delay / q) * q;
+ }
+
+ state->playing = priv->playing && priv->buffered > 0;
+}
+
+#define OPT_BASE_STRUCT struct priv
+
+const struct ao_driver audio_out_null = {
+ .description = "Null audio output",
+ .name = "null",
+ .init = init,
+ .uninit = uninit,
+ .reset = reset,
+ .get_state = get_state,
+ .set_pause = set_pause,
+ .write = audio_write,
+ .start = start,
+ .priv_size = sizeof(struct priv),
+ .priv_defaults = &(const struct priv) {
+ .bufferlen = 0.2,
+ .outburst = 256,
+ .speed = 1,
+ },
+ .options = (const struct m_option[]) {
+ {"untimed", OPT_BOOL(untimed)},
+ {"buffer", OPT_FLOAT(bufferlen), M_RANGE(0, 100)},
+ {"outburst", OPT_INT(outburst), M_RANGE(1, 100000)},
+ {"speed", OPT_FLOAT(speed), M_RANGE(0, 10000)},
+ {"latency", OPT_FLOAT(latency_sec), M_RANGE(0, 100)},
+ {"broken-eof", OPT_BOOL(broken_eof)},
+ {"broken-delay", OPT_BOOL(broken_delay)},
+ {"channel-layouts", OPT_CHANNELS(channel_layouts)},
+ {"format", OPT_AUDIOFORMAT(format)},
+ {0}
+ },
+ .options_prefix = "ao-null",
+};