Coverage Report

Created: 2026-08-14 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/audio/chmap.c
Line
Count
Source
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 <stdlib.h>
19
#include <assert.h>
20
21
#include <libavutil/common.h>
22
23
#include "common/common.h"
24
#include "common/msg.h"
25
#include "chmap.h"
26
27
// Names taken from libavutil/channel_layout.c (Not accessible by API.)
28
// Use of these names is hard-coded in some places (e.g. ao_alsa.c)
29
static const char *const speaker_names[MP_SPEAKER_ID_COUNT][2] = {
30
    [MP_SPEAKER_ID_FL]          = {"fl",   "front left"},
31
    [MP_SPEAKER_ID_FR]          = {"fr",   "front right"},
32
    [MP_SPEAKER_ID_FC]          = {"fc",   "front center"},
33
    [MP_SPEAKER_ID_LFE]         = {"lfe",  "low frequency"},
34
    [MP_SPEAKER_ID_BL]          = {"bl",   "back left"},
35
    [MP_SPEAKER_ID_BR]          = {"br",   "back right"},
36
    [MP_SPEAKER_ID_FLC]         = {"flc",  "front left-of-center"},
37
    [MP_SPEAKER_ID_FRC]         = {"frc",  "front right-of-center"},
38
    [MP_SPEAKER_ID_BC]          = {"bc",   "back center"},
39
    [MP_SPEAKER_ID_SL]          = {"sl",   "side left"},
40
    [MP_SPEAKER_ID_SR]          = {"sr",   "side right"},
41
    [MP_SPEAKER_ID_TC]          = {"tc",   "top center"},
42
    [MP_SPEAKER_ID_TFL]         = {"tfl",  "top front left"},
43
    [MP_SPEAKER_ID_TFC]         = {"tfc",  "top front center"},
44
    [MP_SPEAKER_ID_TFR]         = {"tfr",  "top front right"},
45
    [MP_SPEAKER_ID_TBL]         = {"tbl",  "top back left"},
46
    [MP_SPEAKER_ID_TBC]         = {"tbc",  "top back center"},
47
    [MP_SPEAKER_ID_TBR]         = {"tbr",  "top back right"},
48
    [MP_SPEAKER_ID_DL]          = {"dl",   "downmix left"},
49
    [MP_SPEAKER_ID_DR]          = {"dr",   "downmix right"},
50
    [MP_SPEAKER_ID_WL]          = {"wl",   "wide left"},
51
    [MP_SPEAKER_ID_WR]          = {"wr",   "wide right"},
52
    [MP_SPEAKER_ID_SDL]         = {"sdl",  "surround direct left"},
53
    [MP_SPEAKER_ID_SDR]         = {"sdr",  "surround direct right"},
54
    [MP_SPEAKER_ID_LFE2]        = {"lfe2", "low frequency 2"},
55
    [MP_SPEAKER_ID_TSL]         = {"tsl",  "top side left"},
56
    [MP_SPEAKER_ID_TSR]         = {"tsr",  "top side right"},
57
    [MP_SPEAKER_ID_BFC]         = {"bfc",  "bottom front center"},
58
    [MP_SPEAKER_ID_BFL]         = {"bfl",  "bottom front left"},
59
    [MP_SPEAKER_ID_BFR]         = {"bfr",  "bottom front right"},
60
    [MP_SPEAKER_ID_NA]          = {"na",   "not available"},
61
};
62
63
// Names taken from libavutil/channel_layout.c (Not accessible by API.)
64
// Channel order corresponds to lavc/waveex, except for the alsa entries.
65
static const char *const std_layout_names[][2] = {
66
    {"empty",           ""}, // not in lavc
67
    {"mono",            "fc"},
68
    {"1.0",             "fc"}, // not in lavc
69
    {"stereo",          "fl-fr"},
70
    {"2.0",             "fl-fr"}, // not in lavc
71
    {"2.1",             "fl-fr-lfe"},
72
    {"3.0",             "fl-fr-fc"},
73
    {"3.0(back)",       "fl-fr-bc"},
74
    {"4.0",             "fl-fr-fc-bc"},
75
    {"quad",            "fl-fr-bl-br"},
76
    {"quad(side)",      "fl-fr-sl-sr"},
77
    {"3.1",             "fl-fr-fc-lfe"},
78
    {"3.1(back)",       "fl-fr-lfe-bc"}, // not in lavc
79
    {"5.0",             "fl-fr-fc-bl-br"},
80
    {"5.0(alsa)",       "fl-fr-bl-br-fc"}, // not in lavc
81
    {"5.0(side)",       "fl-fr-fc-sl-sr"},
82
    {"4.1",             "fl-fr-fc-lfe-bc"},
83
    {"4.1(alsa)",       "fl-fr-bl-br-lfe"}, // not in lavc
84
    {"5.1",             "fl-fr-fc-lfe-bl-br"},
85
    {"5.1(alsa)",       "fl-fr-bl-br-fc-lfe"}, // not in lavc
86
    {"5.1(side)",       "fl-fr-fc-lfe-sl-sr"},
87
    {"6.0",             "fl-fr-fc-bc-sl-sr"},
88
    {"6.0(front)",      "fl-fr-flc-frc-sl-sr"},
89
    {"hexagonal",       "fl-fr-fc-bl-br-bc"},
90
    {"6.1",             "fl-fr-fc-lfe-bc-sl-sr"},
91
    {"6.1(back)",       "fl-fr-fc-lfe-bl-br-bc"},
92
    {"6.1(top)",        "fl-fr-fc-lfe-bl-br-tc"}, // not in lavc
93
    {"6.1(front)",      "fl-fr-lfe-flc-frc-sl-sr"},
94
    {"7.0",             "fl-fr-fc-bl-br-sl-sr"},
95
    {"7.0(front)",      "fl-fr-fc-flc-frc-sl-sr"},
96
    {"7.0(rear)",       "fl-fr-fc-bl-br-sdl-sdr"}, // not in lavc
97
    {"7.1",             "fl-fr-fc-lfe-bl-br-sl-sr"},
98
    {"7.1(alsa)",       "fl-fr-bl-br-fc-lfe-sl-sr"}, // not in lavc
99
    {"7.1(wide)",       "fl-fr-fc-lfe-bl-br-flc-frc"},
100
    {"7.1(wide-side)",  "fl-fr-fc-lfe-flc-frc-sl-sr"},
101
    {"7.1(top)",        "fl-fr-fc-lfe-bl-br-tfl-tfr"},
102
    {"7.1(rear)",       "fl-fr-fc-lfe-bl-br-sdl-sdr"}, // not in lavc
103
    {"octagonal",       "fl-fr-fc-bl-br-bc-sl-sr"},
104
    {"cube",            "fl-fr-bl-br-tfl-tfr-tbl-tbr"},
105
    {"hexadecagonal",   "fl-fr-fc-bl-br-bc-sl-sr-tfc-tfl-tfr-tbl-tbc-tbr-wl-wr"},
106
    {"downmix",         "fl-fr"},
107
    {"22.2",            "fl-fr-fc-lfe-bl-br-flc-frc-bc-sl-sr-tc-tfl-tfc-tfr-tbl-tbc-tbr-lfe2-tsl-tsr-bfc-bfl-bfr"},
108
    {"auto",            ""}, // not in lavc
109
    {0}
110
};
111
112
static const struct mp_chmap default_layouts[] = {
113
    {0},                                        // empty
114
    MP_CHMAP_INIT_MONO,                         // mono
115
    MP_CHMAP2(FL, FR),                          // stereo
116
    MP_CHMAP3(FL, FR, LFE),                     // 2.1
117
    MP_CHMAP4(FL, FR, FC, BC),                  // 4.0
118
    MP_CHMAP5(FL, FR, FC, BL,  BR),             // 5.0
119
    MP_CHMAP6(FL, FR, FC, LFE, BL, BR),         // 5.1
120
    MP_CHMAP7(FL, FR, FC, LFE, BC, SL, SR),     // 6.1
121
    MP_CHMAP8(FL, FR, FC, LFE, BL, BR, SL, SR), // 7.1
122
};
123
124
// Returns true if speakers are mapped uniquely, and there's at least 1 channel.
125
bool mp_chmap_is_valid(const struct mp_chmap *src)
126
37.4M
{
127
37.4M
    bool mapped[MP_SPEAKER_ID_COUNT] = {0};
128
88.5M
    for (int n = 0; n < src->num; n++) {
129
51.0M
        int sp = src->speaker[n];
130
51.0M
        if (sp >= MP_SPEAKER_ID_COUNT || mapped[sp])
131
52
            return false;
132
51.0M
        if (sp != MP_SPEAKER_ID_NA)
133
46.7M
            mapped[sp] = true;
134
51.0M
    }
135
37.4M
    return src->num > 0;
136
37.4M
}
137
138
bool mp_chmap_is_empty(const struct mp_chmap *src)
139
0
{
140
0
    return src->num == 0;
141
0
}
142
143
// Return true if the channel map defines the number of the channels only, and
144
// the channels have to meaning associated with them.
145
bool mp_chmap_is_unknown(const struct mp_chmap *src)
146
2.38M
{
147
3.02M
    for (int n = 0; n < src->num; n++) {
148
2.95M
        if (src->speaker[n] != MP_SPEAKER_ID_NA)
149
2.30M
            return false;
150
2.95M
    }
151
74.2k
    return mp_chmap_is_valid(src);
152
2.38M
}
153
154
// Note: empty channel maps compare as equal. Invalid ones can equal too.
155
bool mp_chmap_equals(const struct mp_chmap *a, const struct mp_chmap *b)
156
21.1M
{
157
21.1M
    if (a->num != b->num)
158
734k
        return false;
159
46.2M
    for (int n = 0; n < a->num; n++) {
160
26.2M
        if (a->speaker[n] != b->speaker[n])
161
430k
            return false;
162
26.2M
    }
163
19.9M
    return true;
164
20.3M
}
165
166
// Whether they use the same speakers (even if in different order).
167
bool mp_chmap_equals_reordered(const struct mp_chmap *a, const struct mp_chmap *b)
168
803k
{
169
803k
    struct mp_chmap t1 = *a, t2 = *b;
170
803k
    mp_chmap_reorder_norm(&t1);
171
803k
    mp_chmap_reorder_norm(&t2);
172
803k
    return mp_chmap_equals(&t1, &t2);
173
803k
}
174
175
bool mp_chmap_is_stereo(const struct mp_chmap *src)
176
0
{
177
0
    static const struct mp_chmap stereo = MP_CHMAP_INIT_STEREO;
178
0
    return mp_chmap_equals(src, &stereo);
179
0
}
180
181
static int comp_uint8(const void *a, const void *b)
182
4.39M
{
183
4.39M
    return *(const uint8_t *)a - *(const uint8_t *)b;
184
4.39M
}
185
186
// Reorder channels to normal order, with monotonically increasing speaker IDs.
187
// We define this order as the same order used with waveex.
188
void mp_chmap_reorder_norm(struct mp_chmap *map)
189
1.60M
{
190
1.60M
    uint8_t *arr = &map->speaker[0];
191
1.60M
    qsort(arr, map->num, 1, comp_uint8);
192
1.60M
}
193
194
// Remove silent (NA) channels, if any.
195
void mp_chmap_remove_na(struct mp_chmap *map)
196
182k
{
197
182k
    struct mp_chmap new = {0};
198
706k
    for (int n = 0; n < map->num; n++) {
199
523k
        int sp = map->speaker[n];
200
523k
        if (sp != MP_SPEAKER_ID_NA)
201
304k
            new.speaker[new.num++] = map->speaker[n];
202
523k
    }
203
182k
    *map = new;
204
182k
}
205
206
// Add silent (NA) channels to map until map->num >= num.
207
void mp_chmap_fill_na(struct mp_chmap *map, int num)
208
5.47k
{
209
5.47k
    mp_assert(num <= MP_NUM_CHANNELS);
210
5.47k
    while (map->num < num)
211
0
        map->speaker[map->num++] = MP_SPEAKER_ID_NA;
212
5.47k
}
213
214
// Set *dst to a standard layout with the given number of channels.
215
// If the number of channels is invalid, an invalid map is set, and
216
// mp_chmap_is_valid(dst) will return false.
217
void mp_chmap_from_channels(struct mp_chmap *dst, int num_channels)
218
130k
{
219
130k
    *dst = (struct mp_chmap) {0};
220
130k
    if (num_channels >= 0 && num_channels < MP_ARRAY_SIZE(default_layouts))
221
91.2k
        *dst = default_layouts[num_channels];
222
130k
    if (!dst->num)
223
52.7k
        mp_chmap_set_unknown(dst, num_channels);
224
130k
}
225
226
// Set *dst to an unknown layout for the given numbers of channels.
227
// If the number of channels is invalid, an invalid map is set, and
228
// mp_chmap_is_valid(dst) will return false.
229
// A mp_chmap with all entries set to NA is treated specially in some
230
// contexts (watch out for mp_chmap_is_unknown()).
231
void mp_chmap_set_unknown(struct mp_chmap *dst, int num_channels)
232
78.8k
{
233
78.8k
    if (num_channels < 0 || num_channels > MP_NUM_CHANNELS) {
234
24.4k
        *dst = (struct mp_chmap) {0};
235
54.4k
    } else {
236
54.4k
        dst->num = num_channels;
237
542k
        for (int n = 0; n < dst->num; n++)
238
487k
            dst->speaker[n] = MP_SPEAKER_ID_NA;
239
54.4k
    }
240
78.8k
}
241
242
// Return the ffmpeg channel layout as in <libavutil/channel_layout.h>.
243
// Speakers not representable by ffmpeg are dropped.
244
// Warning: this ignores the order of the channels, and will return a channel
245
//          mask even if the order is different from libavcodec's.
246
//          Also, "unknown" channel maps are translated to non-sense channel
247
//          maps with the same number of channels.
248
uint64_t mp_chmap_to_lavc_unchecked(const struct mp_chmap *src)
249
624k
{
250
624k
    struct mp_chmap t = *src;
251
624k
    if (t.num > 64)
252
0
        return 0;
253
    // lavc has no concept for unknown layouts yet, so pick something that does
254
    // the job of signaling the number of channels, even if it makes no sense
255
    // as a proper layout.
256
624k
    if (mp_chmap_is_unknown(&t))
257
11.2k
        return t.num == 64 ? (uint64_t)-1 : (1ULL << t.num) - 1;
258
613k
    uint64_t mask = 0;
259
2.06M
    for (int n = 0; n < t.num; n++) {
260
1.44M
        if (t.speaker[n] < 64) // ignore MP_SPEAKER_ID_NA etc.
261
1.44M
            mask |= 1ULL << t.speaker[n];
262
1.44M
    }
263
613k
    return mask;
264
624k
}
265
266
// Return the ffmpeg channel layout as in <libavutil/channel_layout.h>.
267
// Returns 0 if the channel order doesn't match lavc's or if it's invalid.
268
uint64_t mp_chmap_to_lavc(const struct mp_chmap *src)
269
627k
{
270
627k
    if (!mp_chmap_is_lavc(src))
271
20.0k
        return 0;
272
607k
    return mp_chmap_to_lavc_unchecked(src);
273
627k
}
274
275
// Set channel map from the ffmpeg channel layout as in
276
// <libavutil/channel_layout.h>.
277
// If the number of channels exceed MP_NUM_CHANNELS, set dst to empty.
278
void mp_chmap_from_lavc(struct mp_chmap *dst, uint64_t src)
279
6.59M
{
280
6.59M
    dst->num = 0;
281
428M
    for (int n = 0; n < 64; n++) {
282
421M
        if (src & (1ULL << n)) {
283
7.31M
            if (dst->num >= MP_NUM_CHANNELS) {
284
0
                dst->num = 0;
285
0
                return;
286
0
            }
287
7.31M
            dst->speaker[dst->num] = n;
288
7.31M
            dst->num++;
289
7.31M
        }
290
421M
    }
291
6.59M
}
292
293
bool mp_chmap_is_lavc(const struct mp_chmap *src)
294
627k
{
295
627k
    if (!mp_chmap_is_valid(src))
296
20.0k
        return false;
297
607k
    if (mp_chmap_is_unknown(src))
298
0
        return true;
299
    // lavc's channel layout is a bit mask, and channels are always ordered
300
    // from LSB to MSB speaker bits, so speaker IDs have to increase.
301
607k
    mp_assert(src->num > 0);
302
1.41M
    for (int n = 1; n < src->num; n++) {
303
811k
        if (src->speaker[n - 1] >= src->speaker[n])
304
0
            return false;
305
811k
    }
306
2.02M
    for (int n = 0; n < src->num; n++) {
307
1.41M
        if (src->speaker[n] >= 64)
308
0
            return false;
309
1.41M
    }
310
607k
    return true;
311
607k
}
312
313
// Warning: for "unknown" channel maps, this returns something that may not
314
//          make sense. Invalid channel maps are not changed.
315
void mp_chmap_reorder_to_lavc(struct mp_chmap *map)
316
356
{
317
356
    if (!mp_chmap_is_valid(map))
318
0
        return;
319
356
    uint64_t mask = mp_chmap_to_lavc_unchecked(map);
320
356
    mp_chmap_from_lavc(map, mask);
321
356
}
322
323
// Get reordering array for from->to reordering. from->to must have the same set
324
// of speakers (i.e. same number and speaker IDs, just different order). Then,
325
// for each speaker n, src[n] will be set such that:
326
//      to->speaker[n] = from->speaker[src[n]]
327
// (src[n] gives the source channel for destination channel n)
328
// If *from and *to don't contain the same set of speakers, then the above
329
// invariant is not guaranteed. Instead, src[n] can be set to -1 if the channel
330
// at to->speaker[n] is unmapped.
331
void mp_chmap_get_reorder(int src[MP_NUM_CHANNELS], const struct mp_chmap *from,
332
                          const struct mp_chmap *to)
333
8.52k
{
334
553k
    for (int n = 0; n < MP_NUM_CHANNELS; n++)
335
545k
        src[n] = -1;
336
337
8.52k
    if (mp_chmap_is_unknown(from) || mp_chmap_is_unknown(to)) {
338
42.0k
        for (int n = 0; n < to->num; n++)
339
36.5k
            src[n] = n < from->num ? n : -1;
340
5.47k
        return;
341
5.47k
    }
342
343
17.1k
    for (int n = 0; n < to->num; n++) {
344
48.8k
        for (int i = 0; i < from->num; i++) {
345
48.8k
            if (to->speaker[n] == from->speaker[i]) {
346
14.0k
                src[n] = i;
347
14.0k
                break;
348
14.0k
            }
349
48.8k
        }
350
14.0k
    }
351
352
17.1k
    for (int n = 0; n < to->num; n++)
353
14.0k
        mp_assert(src[n] < 0 || (to->speaker[n] == from->speaker[src[n]]));
354
3.04k
}
355
356
// Return the number of channels only in a.
357
int mp_chmap_diffn(const struct mp_chmap *a, const struct mp_chmap *b)
358
0
{
359
0
    uint64_t a_mask = mp_chmap_to_lavc_unchecked(a);
360
0
    uint64_t b_mask = mp_chmap_to_lavc_unchecked(b);
361
0
    return av_popcount64((a_mask ^ b_mask) & a_mask);
362
0
}
363
364
// Returns something like "fl-fr-fc". If there's a standard layout in lavc
365
// order, return that, e.g. "3.0" instead of "fl-fr-fc".
366
// Unassigned but valid speakers get names like "sp28".
367
char *mp_chmap_to_str_buf(char *buf, size_t buf_size, const struct mp_chmap *src)
368
460k
{
369
460k
    buf[0] = '\0';
370
371
460k
    if (mp_chmap_is_unknown(src)) {
372
9.36k
        snprintf(buf, buf_size, "unknown%d", src->num);
373
9.36k
        return buf;
374
9.36k
    }
375
376
1.28M
    for (int n = 0; n < src->num; n++) {
377
829k
        int sp = src->speaker[n];
378
829k
        const char *s = sp < MP_SPEAKER_ID_COUNT ? speaker_names[sp][0] : NULL;
379
829k
        char sp_buf[10];
380
829k
        if (!s) {
381
14.3k
            snprintf(sp_buf, sizeof(sp_buf), "sp%d", sp);
382
14.3k
            s = sp_buf;
383
14.3k
        }
384
829k
        mp_snprintf_cat(buf, buf_size, "%s%s", n > 0 ? "-" : "", s);
385
829k
    }
386
387
    // To standard layout name
388
2.08M
    for (int n = 0; std_layout_names[n][0]; n++) {
389
2.07M
        if (strcmp(buf, std_layout_names[n][1]) == 0) {
390
445k
            snprintf(buf, buf_size, "%s", std_layout_names[n][0]);
391
445k
            break;
392
445k
        }
393
2.07M
    }
394
395
451k
    return buf;
396
460k
}
397
398
// If src can be parsed as channel map (as produced by mp_chmap_to_str()),
399
// return true and set *dst. Otherwise, return false and don't change *dst.
400
// Note: call mp_chmap_is_valid() to test whether the returned map is valid
401
//       the map could be empty, or contain multiply mapped channels
402
bool mp_chmap_from_str(struct mp_chmap *dst, bstr src)
403
822k
{
404
    // Single number corresponds to mp_chmap_from_channels()
405
822k
    if (src.len > 0) {
406
822k
        bstr t = src;
407
822k
        bool unknown = bstr_eatstart0(&t, "unknown");
408
822k
        bstr rest;
409
822k
        long long count = bstrtoll(t, &rest, 10);
410
822k
        if (rest.len == 0) {
411
15.1k
            struct mp_chmap res;
412
15.1k
            if (unknown) {
413
944
                mp_chmap_set_unknown(&res, count);
414
14.2k
            } else {
415
14.2k
                mp_chmap_from_channels(&res, count);
416
14.2k
            }
417
15.1k
            if (mp_chmap_is_valid(&res)) {
418
15.0k
                *dst = res;
419
15.0k
                return true;
420
15.0k
            }
421
15.1k
        }
422
822k
    }
423
424
    // From standard layout name
425
7.03M
    for (int n = 0; std_layout_names[n][0]; n++) {
426
6.99M
        if (bstr_equals0(src, std_layout_names[n][0])) {
427
764k
            src = bstr0(std_layout_names[n][1]);
428
764k
            break;
429
764k
        }
430
6.99M
    }
431
432
    // Explicit speaker list (separated by "-")
433
807k
    struct mp_chmap res = {0};
434
2.48M
    while (src.len) {
435
1.68M
        bstr s;
436
1.68M
        bstr_split_tok(src, "-", &s, &src);
437
1.68M
        int speaker = -1;
438
10.1M
        for (int n = 0; n < MP_SPEAKER_ID_COUNT; n++) {
439
10.1M
            const char *name = speaker_names[n][0];
440
10.1M
            if (name && bstr_equals0(s, name)) {
441
1.63M
                speaker = n;
442
1.63M
                break;
443
1.63M
            }
444
10.1M
        }
445
1.68M
        if (speaker < 0) {
446
43.2k
            if (bstr_eatstart0(&s, "sp")) {
447
42.9k
                long long sp = bstrtoll(s, &s, 0);
448
42.9k
                if (s.len == 0 && sp >= 0 && sp < MP_SPEAKER_ID_COUNT)
449
42.8k
                    speaker = sp;
450
42.9k
            }
451
43.2k
            if (speaker < 0)
452
400
                return false;
453
43.2k
        }
454
1.68M
        if (res.num >= MP_NUM_CHANNELS)
455
1
            return false;
456
1.68M
        res.speaker[res.num] = speaker;
457
1.68M
        res.num++;
458
1.68M
    }
459
460
807k
    *dst = res;
461
807k
    return true;
462
807k
}
463
464
// Output a human readable "canonical" channel map string. Converting this from
465
// a string back to a channel map can yield a different map, but the string
466
// looks nicer. E.g. "fc-fl-fr-na" becomes "3.0".
467
char *mp_chmap_to_str_hr_buf(char *buf, size_t buf_size, const struct mp_chmap *src)
468
182k
{
469
182k
    struct mp_chmap map = *src;
470
182k
    mp_chmap_remove_na(&map);
471
764k
    for (int n = 0; std_layout_names[n][0]; n++) {
472
763k
        struct mp_chmap s;
473
763k
        if (mp_chmap_from_str(&s, bstr0(std_layout_names[n][0])) &&
474
763k
            mp_chmap_equals_reordered(&s, &map))
475
181k
        {
476
181k
            map = s;
477
181k
            break;
478
181k
        }
479
763k
    }
480
182k
    return mp_chmap_to_str_buf(buf, buf_size, &map);
481
182k
}
482
483
mp_ch_layout_tuple *mp_iterate_builtin_layouts(void **opaque)
484
0
{
485
0
    uintptr_t i = (uintptr_t)*opaque;
486
487
0
    if (i >= MP_ARRAY_SIZE(std_layout_names) ||
488
0
        !std_layout_names[i][0])
489
0
        return NULL;
490
491
0
    *opaque = (void *)(i + 1);
492
493
0
    if (std_layout_names[i][1][0] == '\0') {
494
0
        return mp_iterate_builtin_layouts(opaque);
495
0
    }
496
497
0
    return &std_layout_names[i];
498
0
}
499
500
void mp_chmap_print_help(struct mp_log *log)
501
1
{
502
1
    mp_info(log, "Speakers:\n");
503
66
    for (int n = 0; n < MP_SPEAKER_ID_COUNT; n++) {
504
65
        if (speaker_names[n][0])
505
31
            mp_info(log, "    %-16s (%s)\n",
506
65
                    speaker_names[n][0], speaker_names[n][1]);
507
65
    }
508
1
    mp_info(log, "Standard layouts:\n");
509
44
    for (int n = 0; std_layout_names[n][0]; n++) {
510
43
        mp_info(log, "    %-16s (%s)\n",
511
43
                 std_layout_names[n][0], std_layout_names[n][1]);
512
43
    }
513
65
    for (int n = 0; n < MP_NUM_CHANNELS; n++)
514
64
        mp_info(log, "    unknown%d\n", n + 1);
515
1
}