Coverage Report

Created: 2025-08-11 06:44

/src/mpv/audio/out/ao.c
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
}