Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * This file is part of mpv. |
3 | | * |
4 | | * mpv is free software; you can redistribute it and/or |
5 | | * modify it under the terms of the GNU Lesser General Public |
6 | | * License as published by the Free Software Foundation; either |
7 | | * version 2.1 of the License, or (at your option) any later version. |
8 | | * |
9 | | * mpv is distributed in the hope that it will be useful, |
10 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | * GNU Lesser General Public License for more details. |
13 | | * |
14 | | * You should have received a copy of the GNU Lesser General Public |
15 | | * License along with mpv. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | |
18 | | #include <stdio.h> |
19 | | #include <stdlib.h> |
20 | | #include <string.h> |
21 | | #include <math.h> |
22 | | #include <assert.h> |
23 | | |
24 | | #include "mpv_talloc.h" |
25 | | |
26 | | #include "config.h" |
27 | | #include "ao.h" |
28 | | #include "internal.h" |
29 | | #include "audio/format.h" |
30 | | |
31 | | #include "options/options.h" |
32 | | #include "options/m_config_frontend.h" |
33 | | #include "osdep/endian.h" |
34 | | #include "common/msg.h" |
35 | | #include "common/common.h" |
36 | | #include "common/global.h" |
37 | | |
38 | | extern const struct ao_driver audio_out_oss; |
39 | | extern const struct ao_driver audio_out_audiotrack; |
40 | | extern const struct ao_driver audio_out_audiounit; |
41 | | extern const struct ao_driver audio_out_coreaudio; |
42 | | extern const struct ao_driver audio_out_coreaudio_exclusive; |
43 | | extern const struct ao_driver audio_out_avfoundation; |
44 | | extern const struct ao_driver audio_out_rsound; |
45 | | extern const struct ao_driver audio_out_pipewire; |
46 | | extern const struct ao_driver audio_out_sndio; |
47 | | extern const struct ao_driver audio_out_pulse; |
48 | | extern const struct ao_driver audio_out_jack; |
49 | | extern const struct ao_driver audio_out_openal; |
50 | | extern const struct ao_driver audio_out_opensles; |
51 | | extern const struct ao_driver audio_out_null; |
52 | | extern const struct ao_driver audio_out_alsa; |
53 | | extern const struct ao_driver audio_out_wasapi; |
54 | | extern const struct ao_driver audio_out_pcm; |
55 | | extern const struct ao_driver audio_out_lavc; |
56 | | extern const struct ao_driver audio_out_sdl; |
57 | | |
58 | | static const struct ao_driver * const audio_out_drivers[] = { |
59 | | // native: |
60 | | #if HAVE_ANDROID |
61 | | &audio_out_audiotrack, |
62 | | #endif |
63 | | #if HAVE_AUDIOUNIT |
64 | | &audio_out_audiounit, |
65 | | #endif |
66 | | #if HAVE_COREAUDIO |
67 | | &audio_out_coreaudio, |
68 | | #endif |
69 | | #if HAVE_AVFOUNDATION |
70 | | &audio_out_avfoundation, |
71 | | #endif |
72 | | #if HAVE_PIPEWIRE |
73 | | &audio_out_pipewire, |
74 | | #endif |
75 | | #if HAVE_PULSE |
76 | | &audio_out_pulse, |
77 | | #endif |
78 | | #if HAVE_ALSA |
79 | | &audio_out_alsa, |
80 | | #endif |
81 | | #if HAVE_WASAPI |
82 | | &audio_out_wasapi, |
83 | | #endif |
84 | | #if HAVE_OSS_AUDIO |
85 | | &audio_out_oss, |
86 | | #endif |
87 | | // wrappers: |
88 | | #if HAVE_JACK |
89 | | &audio_out_jack, |
90 | | #endif |
91 | | #if HAVE_OPENAL |
92 | | &audio_out_openal, |
93 | | #endif |
94 | | #if HAVE_OPENSLES |
95 | | &audio_out_opensles, |
96 | | #endif |
97 | | #if HAVE_SDL2_AUDIO |
98 | | &audio_out_sdl, |
99 | | #endif |
100 | | #if HAVE_SNDIO |
101 | | &audio_out_sndio, |
102 | | #endif |
103 | | &audio_out_null, |
104 | | #if HAVE_COREAUDIO |
105 | | &audio_out_coreaudio_exclusive, |
106 | | #endif |
107 | | &audio_out_pcm, |
108 | | &audio_out_lavc, |
109 | | }; |
110 | | |
111 | | static bool get_desc(struct m_obj_desc *dst, int index) |
112 | 1.57M | { |
113 | 1.57M | if (index >= MP_ARRAY_SIZE(audio_out_drivers)) |
114 | 221k | return false; |
115 | 1.35M | const struct ao_driver *ao = audio_out_drivers[index]; |
116 | 1.35M | *dst = (struct m_obj_desc) { |
117 | 1.35M | .name = ao->name, |
118 | 1.35M | .description = ao->description, |
119 | 1.35M | .priv_size = ao->priv_size, |
120 | 1.35M | .priv_defaults = ao->priv_defaults, |
121 | 1.35M | .options = ao->options, |
122 | 1.35M | .options_prefix = ao->options_prefix, |
123 | 1.35M | .global_opts = ao->global_opts, |
124 | 1.35M | .hidden = ao->encode, |
125 | 1.35M | .p = ao, |
126 | 1.35M | }; |
127 | 1.35M | return true; |
128 | 1.57M | } |
129 | | |
130 | | // For the ao option |
131 | | static const struct m_obj_list ao_obj_list = { |
132 | | .get_desc = get_desc, |
133 | | .description = "audio outputs", |
134 | | .allow_trailer = true, |
135 | | .disallow_positional_parameters = true, |
136 | | .use_global_options = true, |
137 | | }; |
138 | | |
139 | | #define OPT_BASE_STRUCT struct ao_opts |
140 | | const struct m_sub_options ao_conf = { |
141 | | .opts = (const struct m_option[]) { |
142 | | {"ao", OPT_SETTINGSLIST(audio_driver_list, &ao_obj_list), |
143 | | .flags = UPDATE_AUDIO}, |
144 | | {"audio-device", OPT_STRING(audio_device), .flags = UPDATE_AUDIO}, |
145 | | {"audio-client-name", OPT_STRING(audio_client_name), .flags = UPDATE_AUDIO}, |
146 | | {"audio-buffer", OPT_DOUBLE(audio_buffer), |
147 | | .flags = UPDATE_AUDIO, M_RANGE(0, 10)}, |
148 | | {0} |
149 | | }, |
150 | | .size = sizeof(OPT_BASE_STRUCT), |
151 | | .defaults = &(const OPT_BASE_STRUCT){ |
152 | | .audio_buffer = 0.2, |
153 | | .audio_device = "auto", |
154 | | .audio_client_name = "mpv", |
155 | | }, |
156 | | }; |
157 | | |
158 | | static struct ao *ao_alloc(bool probing, struct mpv_global *global, |
159 | | void (*wakeup_cb)(void *ctx), void *wakeup_ctx, |
160 | | char *name) |
161 | 31.1k | { |
162 | 31.1k | mp_assert(wakeup_cb); |
163 | | |
164 | 31.1k | struct mp_log *log = mp_log_new(NULL, global->log, "ao"); |
165 | 31.1k | struct m_obj_desc desc; |
166 | 31.1k | if (!m_obj_list_find(&desc, &ao_obj_list, bstr0(name))) { |
167 | 833 | mp_msg(log, MSGL_ERR, "Audio output %s not found!\n", name); |
168 | 833 | talloc_free(log); |
169 | 833 | return NULL; |
170 | 30.3k | }; |
171 | 30.3k | struct ao_opts *opts = mp_get_config_group(NULL, global, &ao_conf); |
172 | 30.3k | struct ao *ao = talloc_ptrtype(NULL, ao); |
173 | 30.3k | talloc_steal(ao, log); |
174 | 30.3k | *ao = (struct ao) { |
175 | 30.3k | .driver = desc.p, |
176 | 30.3k | .probing = probing, |
177 | 30.3k | .global = global, |
178 | 30.3k | .wakeup_cb = wakeup_cb, |
179 | 30.3k | .wakeup_ctx = wakeup_ctx, |
180 | 30.3k | .log = mp_log_new(ao, log, name), |
181 | 30.3k | .def_buffer = opts->audio_buffer, |
182 | 30.3k | .client_name = talloc_strdup(ao, opts->audio_client_name), |
183 | 30.3k | }; |
184 | 30.3k | talloc_free(opts); |
185 | 30.3k | ao->priv = m_config_group_from_desc(ao, ao->log, global, &desc, name); |
186 | 30.3k | if (!ao->priv) |
187 | 0 | goto error; |
188 | 30.3k | ao_set_gain(ao, 1.0f); |
189 | 30.3k | return ao; |
190 | 0 | error: |
191 | 0 | talloc_free(ao); |
192 | 0 | return NULL; |
193 | 30.3k | } |
194 | | |
195 | | static struct ao *ao_init(bool probing, struct mpv_global *global, |
196 | | void (*wakeup_cb)(void *ctx), void *wakeup_ctx, |
197 | | struct encode_lavc_context *encode_lavc_ctx, int flags, |
198 | | int samplerate, int format, struct mp_chmap channels, |
199 | | char *dev, char *name) |
200 | 31.1k | { |
201 | 31.1k | struct ao *ao = ao_alloc(probing, global, wakeup_cb, wakeup_ctx, name); |
202 | 31.1k | if (!ao) |
203 | 833 | return NULL; |
204 | 30.3k | ao->samplerate = samplerate; |
205 | 30.3k | ao->channels = channels; |
206 | 30.3k | ao->format = format; |
207 | 30.3k | ao->encode_lavc_ctx = encode_lavc_ctx; |
208 | 30.3k | ao->init_flags = flags; |
209 | 30.3k | if (ao->driver->encode != !!ao->encode_lavc_ctx) |
210 | 36 | goto fail; |
211 | | |
212 | 30.2k | MP_VERBOSE(ao, "requested format: %d Hz, %s channels, %s\n", |
213 | 30.2k | ao->samplerate, mp_chmap_to_str(&ao->channels), |
214 | 30.2k | af_fmt_to_str(ao->format)); |
215 | | |
216 | 30.2k | ao->device = talloc_strdup(ao, dev); |
217 | 30.2k | ao->stream_silence = flags & AO_INIT_STREAM_SILENCE; |
218 | | |
219 | 30.2k | init_buffer_pre(ao); |
220 | | |
221 | 30.2k | int r = ao->driver->init(ao); |
222 | 30.2k | if (r < 0) { |
223 | | // Silly exception for coreaudio spdif redirection |
224 | 0 | if (ao->redirect) { |
225 | 0 | char redirect[80], rdevice[80]; |
226 | 0 | snprintf(redirect, sizeof(redirect), "%s", ao->redirect); |
227 | 0 | snprintf(rdevice, sizeof(rdevice), "%s", ao->device ? ao->device : ""); |
228 | 0 | ao_uninit(ao); |
229 | 0 | return ao_init(probing, global, wakeup_cb, wakeup_ctx, |
230 | 0 | encode_lavc_ctx, flags, samplerate, format, channels, |
231 | 0 | rdevice, redirect); |
232 | 0 | } |
233 | 0 | goto fail; |
234 | 0 | } |
235 | 30.2k | ao->driver_initialized = true; |
236 | | |
237 | 30.2k | ao->sstride = af_fmt_to_bytes(ao->format); |
238 | 30.2k | ao->num_planes = 1; |
239 | 30.2k | if (af_fmt_is_planar(ao->format)) { |
240 | 19.1k | ao->num_planes = ao->channels.num; |
241 | 19.1k | } else { |
242 | 11.1k | ao->sstride *= ao->channels.num; |
243 | 11.1k | } |
244 | 30.2k | ao->bps = (int64_t)ao->samplerate * ao->sstride; |
245 | | |
246 | 30.2k | if (ao->device_buffer <= 0 && ao->driver->write) { |
247 | 149 | MP_ERR(ao, "Device buffer size not set.\n"); |
248 | 149 | goto fail; |
249 | 149 | } |
250 | 30.1k | if (ao->device_buffer) |
251 | 30.1k | MP_VERBOSE(ao, "device buffer: %d samples.\n", ao->device_buffer); |
252 | 30.1k | ao->buffer = MPMAX(ao->device_buffer, ao->def_buffer * ao->samplerate); |
253 | 30.1k | ao->buffer = MPMAX(ao->buffer, 1); |
254 | | |
255 | 30.1k | int align = af_format_sample_alignment(ao->format); |
256 | 30.1k | ao->buffer = (ao->buffer + align - 1) / align * align; |
257 | 30.1k | MP_VERBOSE(ao, "using soft-buffer of %d samples.\n", ao->buffer); |
258 | | |
259 | 30.1k | if (!init_buffer_post(ao)) |
260 | 0 | goto fail; |
261 | 30.1k | return ao; |
262 | | |
263 | 185 | fail: |
264 | 185 | ao_uninit(ao); |
265 | 185 | return NULL; |
266 | 30.1k | } |
267 | | |
268 | | static void split_ao_device(void *tmp, char *opt, char **out_ao, char **out_dev) |
269 | 30.3k | { |
270 | 30.3k | *out_ao = NULL; |
271 | 30.3k | *out_dev = NULL; |
272 | 30.3k | if (!opt) |
273 | 0 | return; |
274 | 30.3k | if (!opt[0] || strcmp(opt, "auto") == 0) |
275 | 30.3k | return; |
276 | | // Split on "/". If "/" is the final character, or absent, out_dev is NULL. |
277 | 4 | bstr b_dev, b_ao; |
278 | 4 | bstr_split_tok(bstr0(opt), "/", &b_ao, &b_dev); |
279 | 4 | if (b_dev.len > 0) |
280 | 3 | *out_dev = bstrto0(tmp, b_dev); |
281 | 4 | *out_ao = bstrto0(tmp, b_ao); |
282 | 4 | } |
283 | | |
284 | | struct ao *ao_init_best(struct mpv_global *global, |
285 | | int init_flags, |
286 | | void (*wakeup_cb)(void *ctx), void *wakeup_ctx, |
287 | | struct encode_lavc_context *encode_lavc_ctx, |
288 | | int samplerate, int format, struct mp_chmap channels) |
289 | 30.3k | { |
290 | 30.3k | void *tmp = talloc_new(NULL); |
291 | 30.3k | struct ao_opts *opts = mp_get_config_group(tmp, global, &ao_conf); |
292 | 30.3k | struct mp_log *log = mp_log_new(tmp, global->log, "ao"); |
293 | 30.3k | struct ao *ao = NULL; |
294 | 30.3k | struct m_obj_settings *ao_list = NULL; |
295 | 30.3k | int ao_num = 0; |
296 | | |
297 | 61.4k | for (int n = 0; opts->audio_driver_list && opts->audio_driver_list[n].name; n++) |
298 | 31.1k | MP_TARRAY_APPEND(tmp, ao_list, ao_num, opts->audio_driver_list[n]); |
299 | | |
300 | 30.3k | bool forced_dev = false; |
301 | 30.3k | char *pref_ao, *pref_dev; |
302 | 30.3k | split_ao_device(tmp, opts->audio_device, &pref_ao, &pref_dev); |
303 | 30.3k | if (!ao_num && pref_ao) { |
304 | | // Reuse the autoselection code |
305 | 0 | MP_TARRAY_APPEND(tmp, ao_list, ao_num, |
306 | 0 | (struct m_obj_settings){.name = pref_ao}); |
307 | 0 | forced_dev = true; |
308 | 0 | } |
309 | | |
310 | 30.3k | bool autoprobe = ao_num == 0; |
311 | | |
312 | | // Something like "--ao=a,b," means do autoprobing after a and b fail. |
313 | 30.3k | if (ao_num && strlen(ao_list[ao_num - 1].name) == 0) { |
314 | 11 | ao_num -= 1; |
315 | 11 | autoprobe = true; |
316 | 11 | } |
317 | | |
318 | 30.3k | if (autoprobe) { |
319 | 11 | for (int n = 0; n < MP_ARRAY_SIZE(audio_out_drivers); n++) { |
320 | 11 | const struct ao_driver *driver = audio_out_drivers[n]; |
321 | 11 | if (driver == &audio_out_null) |
322 | 11 | break; |
323 | 0 | MP_TARRAY_APPEND(tmp, ao_list, ao_num, |
324 | 0 | (struct m_obj_settings){.name = (char *)driver->name}); |
325 | 0 | } |
326 | 11 | } |
327 | | |
328 | 30.3k | if (init_flags & AO_INIT_NULL_FALLBACK) { |
329 | 0 | MP_TARRAY_APPEND(tmp, ao_list, ao_num, |
330 | 0 | (struct m_obj_settings){.name = "null"}); |
331 | 0 | } |
332 | | |
333 | 31.3k | for (int n = 0; n < ao_num; n++) { |
334 | 31.1k | struct m_obj_settings *entry = &ao_list[n]; |
335 | 31.1k | bool probing = n + 1 != ao_num; |
336 | 31.1k | mp_verbose(log, "Trying audio driver '%s'\n", entry->name); |
337 | 31.1k | char *dev = NULL; |
338 | 31.1k | if (pref_ao && pref_dev && strcmp(entry->name, pref_ao) == 0) { |
339 | 0 | dev = pref_dev; |
340 | 0 | mp_verbose(log, "Using preferred device '%s'\n", dev); |
341 | 0 | } |
342 | 31.1k | ao = ao_init(probing, global, wakeup_cb, wakeup_ctx, encode_lavc_ctx, |
343 | 31.1k | init_flags, samplerate, format, channels, dev, entry->name); |
344 | 31.1k | if (ao) |
345 | 30.1k | break; |
346 | 1.01k | if (!probing) |
347 | 194 | mp_err(log, "Failed to initialize audio driver '%s'\n", entry->name); |
348 | 1.01k | if (dev && forced_dev) { |
349 | 0 | mp_err(log, "This audio driver/device was forced with the " |
350 | 0 | "--audio-device option.\nTry unsetting it.\n"); |
351 | 0 | } |
352 | 1.01k | } |
353 | | |
354 | 30.3k | talloc_free(tmp); |
355 | 30.3k | return ao; |
356 | 30.3k | } |
357 | | |
358 | | // Query the AO_EVENT_*s as requested by the events parameter, and return them. |
359 | | int ao_query_and_reset_events(struct ao *ao, int events) |
360 | 301k | { |
361 | 301k | return atomic_fetch_and(&ao->events_, ~(unsigned)events) & events; |
362 | 301k | } |
363 | | |
364 | | // Returns events that were set by this calls. |
365 | | static int ao_add_events(struct ao *ao, int events) |
366 | 0 | { |
367 | 0 | unsigned prev_events = atomic_fetch_or(&ao->events_, events); |
368 | 0 | unsigned new = events & ~prev_events; |
369 | 0 | if (new) |
370 | 0 | ao->wakeup_cb(ao->wakeup_ctx); |
371 | 0 | return new; |
372 | 0 | } |
373 | | |
374 | | // Request that the player core destroys and recreates the AO. Fully thread-safe. |
375 | | void ao_request_reload(struct ao *ao) |
376 | 0 | { |
377 | 0 | ao_add_events(ao, AO_EVENT_RELOAD); |
378 | 0 | } |
379 | | |
380 | | // Notify the player that the device list changed. Fully thread-safe. |
381 | | void ao_hotplug_event(struct ao *ao) |
382 | 0 | { |
383 | 0 | ao_add_events(ao, AO_EVENT_HOTPLUG); |
384 | 0 | } |
385 | | |
386 | | bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s, |
387 | | struct mp_chmap *map) |
388 | 30.2k | { |
389 | 30.2k | MP_VERBOSE(ao, "Channel layouts:\n"); |
390 | 30.2k | mp_chmal_sel_log(s, ao->log, MSGL_V); |
391 | 30.2k | bool r = mp_chmap_sel_adjust(s, map); |
392 | 30.2k | if (r) |
393 | 30.2k | MP_VERBOSE(ao, "result: %s\n", mp_chmap_to_str(map)); |
394 | 30.2k | return r; |
395 | 30.2k | } |
396 | | |
397 | | // safe_multichannel=true behaves like ao_chmap_sel_adjust. |
398 | | // safe_multichannel=false is a helper for callers which do not support safe |
399 | | // handling of arbitrary channel layouts. If the multichannel layouts are not |
400 | | // considered "always safe" (e.g. HDMI), then allow only stereo or mono, if |
401 | | // they are part of the list in *s. |
402 | | bool ao_chmap_sel_adjust2(struct ao *ao, const struct mp_chmap_sel *s, |
403 | | struct mp_chmap *map, bool safe_multichannel) |
404 | 0 | { |
405 | 0 | if (!safe_multichannel && (ao->init_flags & AO_INIT_SAFE_MULTICHANNEL_ONLY)) { |
406 | 0 | struct mp_chmap res = *map; |
407 | 0 | if (mp_chmap_sel_adjust(s, &res)) { |
408 | 0 | if (!mp_chmap_equals(&res, &(struct mp_chmap)MP_CHMAP_INIT_MONO) && |
409 | 0 | !mp_chmap_equals(&res, &(struct mp_chmap)MP_CHMAP_INIT_STEREO)) |
410 | 0 | { |
411 | 0 | MP_VERBOSE(ao, "Disabling multichannel output.\n"); |
412 | 0 | *map = (struct mp_chmap)MP_CHMAP_INIT_STEREO; |
413 | 0 | } |
414 | 0 | } |
415 | 0 | } |
416 | |
|
417 | 0 | return ao_chmap_sel_adjust(ao, s, map); |
418 | 0 | } |
419 | | |
420 | | bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s, |
421 | | struct mp_chmap *map, int num) |
422 | 0 | { |
423 | 0 | return mp_chmap_sel_get_def(s, map, num); |
424 | 0 | } |
425 | | |
426 | | // --- The following functions just return immutable information. |
427 | | |
428 | | void ao_get_format(struct ao *ao, |
429 | | int *samplerate, int *format, struct mp_chmap *channels) |
430 | 60.3k | { |
431 | 60.3k | *samplerate = ao->samplerate; |
432 | 60.3k | *format = ao->format; |
433 | 60.3k | *channels = ao->channels; |
434 | 60.3k | } |
435 | | |
436 | | const char *ao_get_name(struct ao *ao) |
437 | 30.1k | { |
438 | 30.1k | return ao->driver->name; |
439 | 30.1k | } |
440 | | |
441 | | const char *ao_get_description(struct ao *ao) |
442 | 30.1k | { |
443 | 30.1k | return ao->driver->description; |
444 | 30.1k | } |
445 | | |
446 | | bool ao_untimed(struct ao *ao) |
447 | 117k | { |
448 | 117k | return ao->untimed; |
449 | 117k | } |
450 | | |
451 | | // --- |
452 | | |
453 | | struct ao_hotplug { |
454 | | struct mpv_global *global; |
455 | | void (*wakeup_cb)(void *ctx); |
456 | | void *wakeup_ctx; |
457 | | // A single AO instance is used to listen to hotplug events. It wouldn't |
458 | | // make much sense to allow multiple AO drivers; all sane platforms have |
459 | | // a single audio API providing all events. |
460 | | // This is _not_ necessarily the same AO instance as used for playing |
461 | | // audio. |
462 | | struct ao *ao; |
463 | | // cached |
464 | | struct ao_device_list *list; |
465 | | bool needs_update; |
466 | | }; |
467 | | |
468 | | struct ao_hotplug *ao_hotplug_create(struct mpv_global *global, |
469 | | void (*wakeup_cb)(void *ctx), |
470 | | void *wakeup_ctx) |
471 | 68 | { |
472 | 68 | struct ao_hotplug *hp = talloc_ptrtype(NULL, hp); |
473 | 68 | *hp = (struct ao_hotplug){ |
474 | 68 | .global = global, |
475 | 68 | .wakeup_cb = wakeup_cb, |
476 | 68 | .wakeup_ctx = wakeup_ctx, |
477 | 68 | .needs_update = true, |
478 | 68 | }; |
479 | 68 | return hp; |
480 | 68 | } |
481 | | |
482 | | static void get_devices(struct ao *ao, struct ao_device_list *list) |
483 | 0 | { |
484 | 0 | if (ao->driver->list_devs) { |
485 | 0 | ao->driver->list_devs(ao, list); |
486 | 0 | } else { |
487 | 0 | ao_device_list_add(list, ao, &(struct ao_device_desc){"", ""}); |
488 | 0 | } |
489 | 0 | } |
490 | | |
491 | | bool ao_hotplug_check_update(struct ao_hotplug *hp) |
492 | 29 | { |
493 | 29 | if (hp->ao && ao_query_and_reset_events(hp->ao, AO_EVENT_HOTPLUG)) { |
494 | 0 | hp->needs_update = true; |
495 | 0 | return true; |
496 | 0 | } |
497 | 29 | return false; |
498 | 29 | } |
499 | | |
500 | | // The return value is valid until the next call to this API. |
501 | | struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp, |
502 | | struct ao *playback_ao) |
503 | 356 | { |
504 | 356 | if (hp->list && !hp->needs_update) |
505 | 288 | return hp->list; |
506 | | |
507 | 68 | talloc_free(hp->list); |
508 | 68 | struct ao_device_list *list = talloc_zero(hp, struct ao_device_list); |
509 | 68 | hp->list = list; |
510 | | |
511 | 68 | MP_TARRAY_APPEND(list, list->devices, list->num_devices, |
512 | 68 | (struct ao_device_desc){"auto", "Autoselect device"}); |
513 | | |
514 | | // Try to use the same AO for hotplug handling as for playback. |
515 | | // Different AOs may not agree and the playback one is the only one the |
516 | | // user knows about and may even have configured explicitly. |
517 | 68 | if (!hp->ao && playback_ao && playback_ao->driver->hotplug_init) { |
518 | 0 | struct ao *ao = ao_alloc(true, hp->global, hp->wakeup_cb, hp->wakeup_ctx, |
519 | 0 | (char *)playback_ao->driver->name); |
520 | 0 | if (playback_ao->driver->hotplug_init(ao) >= 0) { |
521 | 0 | hp->ao = ao; |
522 | 0 | } else { |
523 | 0 | talloc_free(ao); |
524 | 0 | } |
525 | 0 | } |
526 | | |
527 | 68 | for (int n = 0; n < MP_ARRAY_SIZE(audio_out_drivers); n++) { |
528 | 68 | const struct ao_driver *d = audio_out_drivers[n]; |
529 | 68 | if (d == &audio_out_null) |
530 | 68 | break; // don't add unsafe/special entries |
531 | | |
532 | 0 | struct ao *ao = ao_alloc(true, hp->global, hp->wakeup_cb, hp->wakeup_ctx, |
533 | 0 | (char *)d->name); |
534 | 0 | if (!ao) |
535 | 0 | continue; |
536 | | |
537 | 0 | if (ao->driver->hotplug_init) { |
538 | 0 | if (ao->driver->hotplug_init(ao) >= 0) { |
539 | 0 | get_devices(ao, list); |
540 | 0 | if (hp->ao) |
541 | 0 | ao->driver->hotplug_uninit(ao); |
542 | 0 | else |
543 | 0 | hp->ao = ao; // keep this one |
544 | 0 | } |
545 | 0 | } else { |
546 | 0 | get_devices(ao, list); |
547 | 0 | } |
548 | 0 | if (ao != hp->ao) |
549 | 0 | talloc_free(ao); |
550 | 0 | } |
551 | 68 | hp->needs_update = false; |
552 | 68 | return list; |
553 | 356 | } |
554 | | |
555 | | void ao_device_list_add(struct ao_device_list *list, struct ao *ao, |
556 | | struct ao_device_desc *e) |
557 | 0 | { |
558 | 0 | struct ao_device_desc c = *e; |
559 | 0 | const char *dname = ao->driver->name; |
560 | 0 | char buf[80]; |
561 | 0 | if (!c.desc || !c.desc[0]) { |
562 | 0 | if (c.name && c.name[0]) { |
563 | 0 | c.desc = c.name; |
564 | 0 | } else if (list->num_devices) { |
565 | | // Assume this is the default device. |
566 | 0 | snprintf(buf, sizeof(buf), "Default (%s)", dname); |
567 | 0 | c.desc = buf; |
568 | 0 | } else { |
569 | | // First default device (and maybe the only one). |
570 | 0 | c.desc = "Default"; |
571 | 0 | } |
572 | 0 | } |
573 | 0 | c.name = (c.name && c.name[0]) ? talloc_asprintf(list, "%s/%s", dname, c.name) |
574 | 0 | : talloc_strdup(list, dname); |
575 | 0 | c.desc = talloc_strdup(list, c.desc); |
576 | 0 | MP_TARRAY_APPEND(list, list->devices, list->num_devices, c); |
577 | 0 | } |
578 | | |
579 | | void ao_hotplug_destroy(struct ao_hotplug *hp) |
580 | 164k | { |
581 | 164k | if (!hp) |
582 | 164k | return; |
583 | 68 | if (hp->ao && hp->ao->driver->hotplug_uninit) |
584 | 0 | hp->ao->driver->hotplug_uninit(hp->ao); |
585 | 68 | talloc_free(hp->ao); |
586 | 68 | talloc_free(hp); |
587 | 68 | } |
588 | | |
589 | | static void dummy_wakeup(void *ctx) |
590 | 0 | { |
591 | 0 | } |
592 | | |
593 | | void ao_print_devices(struct mpv_global *global, struct mp_log *log, |
594 | | struct ao *playback_ao) |
595 | 0 | { |
596 | 0 | struct ao_hotplug *hp = ao_hotplug_create(global, dummy_wakeup, NULL); |
597 | 0 | struct ao_device_list *list = ao_hotplug_get_device_list(hp, playback_ao); |
598 | 0 | mp_info(log, "List of detected audio devices:\n"); |
599 | 0 | for (int n = 0; n < list->num_devices; n++) { |
600 | 0 | struct ao_device_desc *desc = &list->devices[n]; |
601 | 0 | mp_info(log, " '%s' (%s)\n", desc->name, desc->desc); |
602 | 0 | } |
603 | 0 | ao_hotplug_destroy(hp); |
604 | 0 | } |
605 | | |
606 | | void ao_set_gain(struct ao *ao, float gain) |
607 | 90.5k | { |
608 | 90.5k | atomic_store(&ao->gain, gain); |
609 | 90.5k | } |
610 | | |
611 | | #define MUL_GAIN_i(d, num_samples, gain, low, center, high) \ |
612 | 182k | for (int n = 0; n < (num_samples); n++) \ |
613 | 182k | (d)[n] = MPCLAMP( \ |
614 | 54 | ((((int64_t)((d)[n]) - (center)) * (gain) + 128) >> 8) + (center), \ |
615 | 54 | (low), (high)) |
616 | | |
617 | | #define MUL_GAIN_f(d, num_samples, gain) \ |
618 | 0 | for (int n = 0; n < (num_samples); n++) \ |
619 | 0 | (d)[n] = (d)[n] * (gain) |
620 | | |
621 | | static void process_plane(struct ao *ao, void *data, int num_samples) |
622 | 7.77M | { |
623 | 7.77M | float gain = atomic_load_explicit(&ao->gain, memory_order_relaxed); |
624 | 7.77M | int gi = lrint(256.0 * gain); |
625 | 7.77M | if (gi == 256) |
626 | 7.77M | return; |
627 | 54 | switch (af_fmt_from_planar(ao->format)) { |
628 | 0 | case AF_FORMAT_U8: |
629 | 0 | MUL_GAIN_i((uint8_t *)data, num_samples, gi, 0, 128, 255); |
630 | 0 | break; |
631 | 54 | case AF_FORMAT_S16: |
632 | 54 | MUL_GAIN_i((int16_t *)data, num_samples, gi, INT16_MIN, 0, INT16_MAX); |
633 | 54 | break; |
634 | 0 | case AF_FORMAT_S32: |
635 | 0 | MUL_GAIN_i((int32_t *)data, num_samples, gi, INT32_MIN, 0, INT32_MAX); |
636 | 0 | break; |
637 | 0 | case AF_FORMAT_FLOAT: |
638 | 0 | MUL_GAIN_f((float *)data, num_samples, gain); |
639 | 0 | break; |
640 | 0 | case AF_FORMAT_DOUBLE: |
641 | 0 | MUL_GAIN_f((double *)data, num_samples, gain); |
642 | 0 | break; |
643 | 0 | default:; |
644 | | // all other sample formats are simply not supported |
645 | 54 | } |
646 | 54 | } |
647 | | |
648 | | void ao_post_process_data(struct ao *ao, void **data, int num_samples) |
649 | 7.46M | { |
650 | 7.46M | bool planar = af_fmt_is_planar(ao->format); |
651 | 7.46M | int planes = planar ? ao->channels.num : 1; |
652 | 7.46M | int plane_samples = num_samples * (planar ? 1: ao->channels.num); |
653 | 15.2M | for (int n = 0; n < planes; n++) |
654 | 7.77M | process_plane(ao, data[n], plane_samples); |
655 | 7.46M | } |
656 | | |
657 | | static int get_conv_type(struct ao_convert_fmt *fmt) |
658 | 0 | { |
659 | 0 | if (af_fmt_to_bytes(fmt->src_fmt) * 8 == fmt->dst_bits && !fmt->pad_msb) |
660 | 0 | return 0; // passthrough |
661 | 0 | if (fmt->src_fmt == AF_FORMAT_S32 && fmt->dst_bits == 24 && !fmt->pad_msb) |
662 | 0 | return 1; // simple 32->24 bit conversion |
663 | 0 | if (fmt->src_fmt == AF_FORMAT_S32 && fmt->dst_bits == 32 && fmt->pad_msb == 8) |
664 | 0 | return 2; // simple 32->24 bit conversion, with MSB padding |
665 | 0 | return -1; // unsupported |
666 | 0 | } |
667 | | |
668 | | // Check whether ao_convert_inplace() can be called. As an exception, the |
669 | | // planar-ness of the sample format and the number of channels is ignored. |
670 | | // All other parameters must be as passed to ao_convert_inplace(). |
671 | | bool ao_can_convert_inplace(struct ao_convert_fmt *fmt) |
672 | 0 | { |
673 | 0 | return get_conv_type(fmt) >= 0; |
674 | 0 | } |
675 | | |
676 | | bool ao_need_conversion(struct ao_convert_fmt *fmt) |
677 | 0 | { |
678 | 0 | return get_conv_type(fmt) != 0; |
679 | 0 | } |
680 | | |
681 | | // The LSB is always ignored. |
682 | | #if BYTE_ORDER == BIG_ENDIAN |
683 | | #define SHIFT24(x) ((3-(x))*8) |
684 | | #else |
685 | 0 | #define SHIFT24(x) (((x)+1)*8) |
686 | | #endif |
687 | | |
688 | | static void convert_plane(int type, void *data, int num_samples) |
689 | 0 | { |
690 | 0 | switch (type) { |
691 | 0 | case 0: |
692 | 0 | break; |
693 | 0 | case 1: /* fall through */ |
694 | 0 | case 2: { |
695 | 0 | int bytes = type == 1 ? 3 : 4; |
696 | 0 | for (int s = 0; s < num_samples; s++) { |
697 | 0 | uint32_t val = *((uint32_t *)data + s); |
698 | 0 | uint8_t *ptr = (uint8_t *)data + s * bytes; |
699 | 0 | ptr[0] = val >> SHIFT24(0); |
700 | 0 | ptr[1] = val >> SHIFT24(1); |
701 | 0 | ptr[2] = val >> SHIFT24(2); |
702 | 0 | if (type == 2) |
703 | 0 | ptr[3] = 0; |
704 | 0 | } |
705 | 0 | break; |
706 | 0 | } |
707 | 0 | default: |
708 | 0 | MP_ASSERT_UNREACHABLE(); |
709 | 0 | } |
710 | 0 | } |
711 | | |
712 | | // data[n] contains the pointer to the first sample of the n-th plane, in the |
713 | | // format implied by fmt->src_fmt. src_fmt also controls whether the data is |
714 | | // all in one plane, or if there is a plane per channel. |
715 | | void ao_convert_inplace(struct ao_convert_fmt *fmt, void **data, int num_samples) |
716 | 0 | { |
717 | 0 | int type = get_conv_type(fmt); |
718 | 0 | bool planar = af_fmt_is_planar(fmt->src_fmt); |
719 | 0 | int planes = planar ? fmt->channels : 1; |
720 | 0 | int plane_samples = num_samples * (planar ? 1: fmt->channels); |
721 | 0 | for (int n = 0; n < planes; n++) |
722 | 0 | convert_plane(type, data[n], plane_samples); |
723 | 0 | } |