Skip to content

Commit 04c5398

Browse files
committed
audio/out: add libmpv callback driver
This adds an ao driver that can be used to register a callback with libmpv to get raw PCM data. Useful for embedding mpv in applications such as game engines, which need the audio to be played in a 3D world and spatialized. Signed-off-by: Orion Moonclaw <webmaster@fawx.news>
1 parent 77dee9b commit 04c5398

File tree

7 files changed

+175
-1
lines changed

7 files changed

+175
-1
lines changed

DOCS/client-api-changes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ API changes
3232

3333
::
3434

35+
--- mpv 0.41.0 ---
36+
2.6 - add mpv_set_audio_callback()
3537
--- mpv 0.40.0 ---
3638
2.5 - Deprecate MPV_RENDER_PARAM_AMBIENT_LIGHT. no replacement.
3739
--- mpv 0.39.0 ---

audio/out/ao.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ extern const struct ao_driver audio_out_opensles;
5151
extern const struct ao_driver audio_out_null;
5252
extern const struct ao_driver audio_out_alsa;
5353
extern const struct ao_driver audio_out_wasapi;
54+
extern const struct ao_driver audio_out_libmpv;
5455
extern const struct ao_driver audio_out_pcm;
5556
extern const struct ao_driver audio_out_lavc;
5657
extern const struct ao_driver audio_out_sdl;
@@ -104,6 +105,7 @@ static const struct ao_driver * const audio_out_drivers[] = {
104105
#if HAVE_COREAUDIO
105106
&audio_out_coreaudio_exclusive,
106107
#endif
108+
&audio_out_libmpv,
107109
&audio_out_pcm,
108110
&audio_out_lavc,
109111
};

audio/out/ao_libmpv.c

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* libmpv audio output driver
3+
*
4+
* This file is part of mpv.
5+
*
6+
* mpv is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 2.1 of the License, or (at your option) any later version.
10+
*
11+
* mpv is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public
17+
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#include "ao.h"
21+
#include "audio/format.h"
22+
#include "ao_libmpv.h"
23+
#include "internal.h"
24+
#include "common/msg.h"
25+
26+
struct priv {
27+
void (*write_cb)(void *userdata, const void *data, int bytes);
28+
void *userdata;
29+
uint64_t data_length;
30+
};
31+
32+
void ao_libmpv_set_cb(struct ao *ao, void (*cb)(void *userdata, const void *data, int bytes), void *userdata)
33+
{
34+
struct priv *p = ao->priv;
35+
p->write_cb = cb;
36+
p->userdata = userdata;
37+
}
38+
39+
static int init(struct ao *ao)
40+
{
41+
ao->format = af_fmt_from_planar(ao->format);
42+
43+
struct mp_chmap_sel sel = {0};
44+
mp_chmap_sel_add_waveext(&sel);
45+
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
46+
return -1;
47+
48+
ao->bps = ao->channels.num * (int64_t)ao->samplerate * af_fmt_to_bytes(ao->format);
49+
50+
MP_INFO(ao, "libmpv: Samplerate: %d Hz Channels: %d Format: %s\n",
51+
ao->samplerate, ao->channels.num, af_fmt_to_str(ao->format));
52+
53+
ao->untimed = true;
54+
ao->device_buffer = 1 << 16;
55+
56+
return 0;
57+
}
58+
59+
static void uninit(struct ao *ao)
60+
{
61+
}
62+
63+
static bool audio_write(struct ao *ao, void **data, int samples)
64+
{
65+
struct priv *priv = ao->priv;
66+
int len = samples * ao->sstride;
67+
68+
if (priv->write_cb)
69+
priv->write_cb(priv->userdata, data[0], len);
70+
priv->data_length += len;
71+
72+
return true;
73+
}
74+
75+
static void get_state(struct ao *ao, struct mp_pcm_state *state)
76+
{
77+
state->free_samples = ao->device_buffer;
78+
state->queued_samples = 0;
79+
state->delay = 0;
80+
}
81+
82+
static bool set_pause(struct ao *ao, bool paused)
83+
{
84+
return true; // signal support so common code doesn't write silence
85+
}
86+
87+
static void start(struct ao *ao)
88+
{
89+
// we use data immediately
90+
}
91+
92+
static void reset(struct ao *ao)
93+
{
94+
}
95+
96+
#define OPT_BASE_STRUCT struct priv
97+
98+
const struct ao_driver audio_out_libmpv = {
99+
.description = "libmpv audio output with a callback",
100+
.name = "libmpv",
101+
.init = init,
102+
.uninit = uninit,
103+
.get_state = get_state,
104+
.set_pause = set_pause,
105+
.write = audio_write,
106+
.start = start,
107+
.reset = reset,
108+
.priv_size = sizeof(struct priv),
109+
.priv_defaults = &(const struct priv) {},
110+
};

audio/out/ao_libmpv.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* libmpv audio output driver
3+
*
4+
* This file is part of mpv.
5+
*
6+
* mpv is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 2.1 of the License, or (at your option) any later version.
10+
*
11+
* mpv is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public
17+
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#ifndef MP_AO_LIBMPV_H_
21+
#define MP_AO_LIBMPV_H_
22+
23+
void ao_libmpv_set_cb(struct ao *ao, void (*cb)(void *userdata, const void *data, int bytes), void *userdata);
24+
25+
#endif

include/mpv/client.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ extern "C" {
248248
* relational operators (<, >, <=, >=).
249249
*/
250250
#define MPV_MAKE_VERSION(major, minor) (((major) << 16) | (minor) | 0UL)
251-
#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(2, 5)
251+
#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(2, 6)
252252

253253
/**
254254
* The API user is allowed to "#define MPV_ENABLE_DEPRECATED 0" before
@@ -1768,6 +1768,16 @@ MPV_EXPORT void mpv_wakeup(mpv_handle *ctx);
17681768
*/
17691769
MPV_EXPORT void mpv_set_wakeup_callback(mpv_handle *ctx, void (*cb)(void *d), void *d);
17701770

1771+
/**
1772+
* Set a custom function that should be called on new audio data. Raw PCM is passed in as an argument to the callback.
1773+
* This can only be used with ao set to libmpv and after the audio driver has been initialized.
1774+
* Only one audio callback can be set.
1775+
*
1776+
* @param cb function that should be called on audio data
1777+
* @param d arbitrary userdata passed to cb
1778+
*/
1779+
MPV_EXPORT int mpv_set_audio_callback(mpv_handle *ctx, void (*cb)(void *d, const void *data, int bytes), void *d);
1780+
17711781
/**
17721782
* Block until all asynchronous requests are done. This affects functions like
17731783
* mpv_command_async(), which return immediately and return their result as

meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ sources = files(
7272
'audio/out/ao.c',
7373
'audio/out/ao_lavc.c',
7474
'audio/out/ao_null.c',
75+
'audio/out/ao_libmpv.c',
7576
'audio/out/ao_pcm.c',
7677
'audio/out/buffer.c',
7778

player/client.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@
4747
#include "core.h"
4848
#include "client.h"
4949

50+
#include <audio/out/internal.h>
51+
#include <audio/out/ao_libmpv.h>
52+
5053
/*
5154
* Locking hierarchy:
5255
*
@@ -379,6 +382,27 @@ void mpv_set_wakeup_callback(mpv_handle *ctx, void (*cb)(void *d), void *d)
379382
mp_mutex_unlock(&ctx->wakeup_lock);
380383
}
381384

385+
int mpv_set_audio_callback(mpv_handle *ctx, void (*cb)(void *userdata, const void *data, int bytes), void *userdata)
386+
{
387+
mp_mutex_lock(&ctx->wakeup_lock);
388+
if (!ctx->mpctx->initialized || !ctx->mpctx->ao)
389+
{
390+
mp_mutex_unlock(&ctx->wakeup_lock);
391+
return MPV_ERROR_UNINITIALIZED;
392+
}
393+
394+
struct ao *ao = ctx->mpctx->ao;
395+
if (strcmp(ao->driver->name, "libmpv") != 0)
396+
{
397+
mp_mutex_unlock(&ctx->wakeup_lock);
398+
return MPV_ERROR_UNSUPPORTED;
399+
}
400+
401+
ao_libmpv_set_cb(ao, cb, userdata);
402+
mp_mutex_unlock(&ctx->wakeup_lock);
403+
return MPV_ERROR_SUCCESS;
404+
}
405+
382406
static void lock_core(mpv_handle *ctx)
383407
{
384408
mp_dispatch_lock(ctx->mpctx->dispatch);

0 commit comments

Comments
 (0)