diff --git a/DOCS/client-api-changes.rst b/DOCS/client-api-changes.rst
index 2a4838ead5abe..c82874544d388 100644
--- a/DOCS/client-api-changes.rst
+++ b/DOCS/client-api-changes.rst
@@ -32,6 +32,8 @@ API changes
::
+ --- mpv 0.41.0 ---
+ 2.6 - add mpv_set_audio_callback()
--- mpv 0.40.0 ---
2.5 - Deprecate MPV_RENDER_PARAM_AMBIENT_LIGHT. no replacement.
--- mpv 0.39.0 ---
diff --git a/audio/out/ao.c b/audio/out/ao.c
index ee76b702ea580..0eb427f832e02 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -51,6 +51,7 @@ extern const struct ao_driver audio_out_opensles;
extern const struct ao_driver audio_out_null;
extern const struct ao_driver audio_out_alsa;
extern const struct ao_driver audio_out_wasapi;
+extern const struct ao_driver audio_out_libmpv;
extern const struct ao_driver audio_out_pcm;
extern const struct ao_driver audio_out_lavc;
extern const struct ao_driver audio_out_sdl;
@@ -104,6 +105,7 @@ static const struct ao_driver * const audio_out_drivers[] = {
#if HAVE_COREAUDIO
&audio_out_coreaudio_exclusive,
#endif
+ &audio_out_libmpv,
&audio_out_pcm,
&audio_out_lavc,
};
diff --git a/audio/out/ao_libmpv.c b/audio/out/ao_libmpv.c
new file mode 100644
index 0000000000000..6fc88986a4090
--- /dev/null
+++ b/audio/out/ao_libmpv.c
@@ -0,0 +1,107 @@
+/*
+ * libmpv 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 .
+ */
+
+#include "ao.h"
+#include "audio/format.h"
+#include "ao_libmpv.h"
+#include "internal.h"
+#include "common/msg.h"
+
+struct priv {
+ void (*write_cb)(void *userdata, const void *data, int bytes);
+ void *userdata;
+};
+
+void ao_libmpv_set_cb(struct ao *ao, void (*cb)(void *userdata, const void *data, int bytes), void *userdata)
+{
+ struct priv *p = ao->priv;
+ p->write_cb = cb;
+ p->userdata = userdata;
+}
+
+static int init(struct ao *ao)
+{
+ ao->format = af_fmt_from_planar(ao->format);
+
+ struct mp_chmap_sel sel = {0};
+ mp_chmap_sel_add_waveext(&sel);
+ if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
+ return -1;
+
+ ao->bps = ao->channels.num * (int64_t)ao->samplerate * af_fmt_to_bytes(ao->format);
+
+ MP_INFO(ao, "libmpv: Samplerate: %d Hz Channels: %d Format: %s\n",
+ ao->samplerate, ao->channels.num, af_fmt_to_str(ao->format));
+
+ ao->untimed = true;
+ ao->device_buffer = 1 << 16;
+
+ return 0;
+}
+
+static void uninit(struct ao *ao)
+{
+}
+
+static bool audio_write(struct ao *ao, void **data, int samples)
+{
+ struct priv *priv = ao->priv;
+ const int len = samples * ao->sstride;
+
+ if (priv->write_cb)
+ priv->write_cb(priv->userdata, data[0], len);
+
+ return true;
+}
+
+static void get_state(struct ao *ao, struct mp_pcm_state *state)
+{
+ state->free_samples = ao->device_buffer;
+ state->queued_samples = 0;
+ state->delay = 0;
+}
+
+static bool set_pause(struct ao *ao, bool paused)
+{
+ return true; // signal support so common code doesn't write silence
+}
+
+static void start(struct ao *ao)
+{
+ // we use data immediately
+}
+
+static void reset(struct ao *ao)
+{
+}
+
+#define OPT_BASE_STRUCT struct priv
+
+const struct ao_driver audio_out_libmpv = {
+ .description = "libmpv audio output with a callback",
+ .name = "libmpv",
+ .init = init,
+ .uninit = uninit,
+ .get_state = get_state,
+ .set_pause = set_pause,
+ .write = audio_write,
+ .start = start,
+ .reset = reset,
+ .priv_size = sizeof(struct priv),
+};
diff --git a/audio/out/ao_libmpv.h b/audio/out/ao_libmpv.h
new file mode 100644
index 0000000000000..2adb446c61081
--- /dev/null
+++ b/audio/out/ao_libmpv.h
@@ -0,0 +1,24 @@
+/*
+ * libmpv 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 .
+ */
+
+#pragma once
+
+#include "ao.h"
+
+void ao_libmpv_set_cb(struct ao *ao, void (*cb)(void *userdata, const void *data, int bytes), void *userdata);
diff --git a/include/mpv/client.h b/include/mpv/client.h
index 85cff63bd5d20..8261dde61816e 100644
--- a/include/mpv/client.h
+++ b/include/mpv/client.h
@@ -248,7 +248,7 @@ extern "C" {
* relational operators (<, >, <=, >=).
*/
#define MPV_MAKE_VERSION(major, minor) (((major) << 16) | (minor) | 0UL)
-#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(2, 5)
+#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(2, 6)
/**
* The API user is allowed to "#define MPV_ENABLE_DEPRECATED 0" before
@@ -1768,6 +1768,18 @@ MPV_EXPORT void mpv_wakeup(mpv_handle *ctx);
*/
MPV_EXPORT void mpv_set_wakeup_callback(mpv_handle *ctx, void (*cb)(void *d), void *d);
+ /**
+ * Set a custom function that should be called on new audio data.
+ * Raw PCM is passed in as an argument to the callback, the format should be set before init using appropriate options.
+ *
+ * This can only be used with ao_libmpv and must be called before the ao is initialized (so before playback starts).
+ * Only one audio callback can be set per instance.
+ *
+ * @param cb function that should be called on audio data
+ * @param d arbitrary userdata passed to cb
+ */
+MPV_EXPORT void mpv_set_audio_callback(mpv_handle *ctx, void (*cb)(void *d, const void *data, int bytes), void *d);
+
/**
* Block until all asynchronous requests are done. This affects functions like
* mpv_command_async(), which return immediately and return their result as
diff --git a/meson.build b/meson.build
index 94ee12feee4f1..45b293522c42b 100644
--- a/meson.build
+++ b/meson.build
@@ -72,6 +72,7 @@ sources = files(
'audio/out/ao.c',
'audio/out/ao_lavc.c',
'audio/out/ao_null.c',
+ 'audio/out/ao_libmpv.c',
'audio/out/ao_pcm.c',
'audio/out/buffer.c',
diff --git a/player/audio.c b/player/audio.c
index b3e975dd2eda3..38870f10f9588 100644
--- a/player/audio.c
+++ b/player/audio.c
@@ -21,6 +21,8 @@
#include
#include
#include
+#include