Skip to content

Commit 9da99e5

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 9da99e5

File tree

9 files changed

+172
-1
lines changed

9 files changed

+172
-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: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
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+
};
30+
31+
void ao_libmpv_set_cb(struct ao *ao, void (*cb)(void *userdata, const void *data, int bytes), void *userdata)
32+
{
33+
struct priv *p = ao->priv;
34+
p->write_cb = cb;
35+
p->userdata = userdata;
36+
}
37+
38+
static int init(struct ao *ao)
39+
{
40+
ao->format = af_fmt_from_planar(ao->format);
41+
42+
struct mp_chmap_sel sel = {0};
43+
mp_chmap_sel_add_waveext(&sel);
44+
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
45+
return -1;
46+
47+
ao->bps = ao->channels.num * (int64_t)ao->samplerate * af_fmt_to_bytes(ao->format);
48+
49+
MP_INFO(ao, "libmpv: Samplerate: %d Hz Channels: %d Format: %s\n",
50+
ao->samplerate, ao->channels.num, af_fmt_to_str(ao->format));
51+
52+
ao->untimed = true;
53+
ao->device_buffer = 1 << 16;
54+
55+
return 0;
56+
}
57+
58+
static void uninit(struct ao *ao)
59+
{
60+
}
61+
62+
static bool audio_write(struct ao *ao, void **data, int samples)
63+
{
64+
struct priv *priv = ao->priv;
65+
const int len = samples * ao->sstride;
66+
67+
if (priv->write_cb)
68+
priv->write_cb(priv->userdata, data[0], len);
69+
70+
return true;
71+
}
72+
73+
static void get_state(struct ao *ao, struct mp_pcm_state *state)
74+
{
75+
state->free_samples = ao->device_buffer;
76+
state->queued_samples = 0;
77+
state->delay = 0;
78+
}
79+
80+
static bool set_pause(struct ao *ao, bool paused)
81+
{
82+
return true; // signal support so common code doesn't write silence
83+
}
84+
85+
static void start(struct ao *ao)
86+
{
87+
// we use data immediately
88+
}
89+
90+
static void reset(struct ao *ao)
91+
{
92+
}
93+
94+
#define OPT_BASE_STRUCT struct priv
95+
96+
const struct ao_driver audio_out_libmpv = {
97+
.description = "libmpv audio output with a callback",
98+
.name = "libmpv",
99+
.init = init,
100+
.uninit = uninit,
101+
.get_state = get_state,
102+
.set_pause = set_pause,
103+
.write = audio_write,
104+
.start = start,
105+
.reset = reset,
106+
.priv_size = sizeof(struct priv),
107+
};

audio/out/ao_libmpv.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
#pragma once
21+
22+
#include "ao.h"
23+
24+
void ao_libmpv_set_cb(struct ao *ao, void (*cb)(void *userdata, const void *data, int bytes), void *userdata);

include/mpv/client.h

Lines changed: 13 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,18 @@ 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.
1773+
* Raw PCM is passed in as an argument to the callback, the format should be set before init using appropriate options.
1774+
*
1775+
* This can only be used with ao_libmpv and must be called before the ao is initialized (so before playback starts).
1776+
* Only one audio callback can be set per instance.
1777+
*
1778+
* @param cb function that should be called on audio data
1779+
* @param d arbitrary userdata passed to cb
1780+
*/
1781+
MPV_EXPORT int mpv_set_audio_callback(mpv_handle *ctx, void (*cb)(void *d, const void *data, int bytes), void *d);
1782+
17711783
/**
17721784
* Block until all asynchronous requests are done. This affects functions like
17731785
* 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/audio.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include <limits.h>
2222
#include <math.h>
2323
#include <assert.h>
24+
#include <audio/out/ao_libmpv.h>
25+
#include <audio/out/internal.h>
2426

2527
#include "mpv_talloc.h"
2628

@@ -447,6 +449,9 @@ static int reinit_audio_filters_and_output(struct MPContext *mpctx)
447449
mpctx, mpctx->encode_lavc_ctx, out_rate,
448450
out_format, out_channels);
449451

452+
if (strcmp(mpctx->ao->driver->name, "libmpv") == 0)
453+
ao_libmpv_set_cb(mpctx->ao, mpctx->ao_libmpv_cb, mpctx->ao_libmpv_userdata);
454+
450455
int ao_rate = 0;
451456
int ao_format = 0;
452457
struct mp_chmap ao_channels = {0};

player/client.c

Lines changed: 14 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
*
@@ -2245,3 +2248,14 @@ bool mp_streamcb_lookup(struct mpv_global *g, const char *protocol,
22452248
mp_mutex_unlock(&clients->lock);
22462249
return found;
22472250
}
2251+
2252+
int mpv_set_audio_callback(mpv_handle *ctx, void (*cb)(void *userdata, const void *data, int bytes), void *userdata)
2253+
{
2254+
lock_core(ctx);
2255+
2256+
ctx->mpctx->ao_libmpv_cb = cb;
2257+
ctx->mpctx->ao_libmpv_userdata = userdata;
2258+
2259+
unlock_core(ctx);
2260+
return MPV_ERROR_SUCCESS;
2261+
}

player/core.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,10 @@ typedef struct MPContext {
324324
struct mp_aframe *ao_filter_fmt; // for weak gapless audio check
325325
struct ao_chain *ao_chain;
326326

327+
// Callback that will be set for ao_libmpv
328+
void (*ao_libmpv_cb)(void *userdata, const void *data, int bytes);
329+
void *ao_libmpv_userdata;
330+
327331
struct vo_chain *vo_chain;
328332

329333
struct vo *video_out;

0 commit comments

Comments
 (0)