mirror of
https://github.com/mpv-player/mpv
synced 2025-02-20 23:07:02 +00:00
ao_coreaudio: use description-based channel layouts
CoreAudio supports 3 kinds of layouts: bitmap based, tag based, and speaker description based (using either channel labels or positional data). Previously we tried to convert everything to bitmap based channel layouts, but it turns out description based ones are the most generic and there are built-in CoreAudio APIs to perform the conversion in this direction. Moreover description based layouts support waveext extensions (like SDL and SDR), and are easier to map to mp_chmaps.
This commit is contained in:
parent
e2f26f01fe
commit
b46ffaec7c
@ -303,23 +303,16 @@ static int init(struct ao *ao)
|
||||
&layouts, &n_layouts);
|
||||
CHECK_CA_ERROR("could not get audio device prefered layouts");
|
||||
|
||||
uint32_t *bitmaps;
|
||||
size_t n_bitmaps;
|
||||
|
||||
ca_bitmaps_from_layouts(ao, layouts, n_layouts, &bitmaps, &n_bitmaps);
|
||||
talloc_free(layouts);
|
||||
|
||||
struct mp_chmap_sel chmap_sel = {0};
|
||||
|
||||
for (int i=0; i < n_bitmaps; i++) {
|
||||
for (int i = 0; i < n_layouts; i++) {
|
||||
struct mp_chmap chmap = {0};
|
||||
mp_chmap_from_lavc(&chmap, bitmaps[i]);
|
||||
mp_chmap_sel_add_map(&chmap_sel, &chmap);
|
||||
if (ca_layout_to_mp_chmap(ao, &layouts[i], &chmap))
|
||||
mp_chmap_sel_add_map(&chmap_sel, &chmap);
|
||||
}
|
||||
|
||||
talloc_free(bitmaps);
|
||||
talloc_free(layouts);
|
||||
|
||||
if (ao->channels.num < 3 || n_bitmaps < 1)
|
||||
if (ao->channels.num < 3)
|
||||
// If the input is not surround or we could not get any usable
|
||||
// bitmap from the hardware, default to waveext...
|
||||
mp_chmap_sel_add_waveext(&chmap_sel);
|
||||
|
@ -377,60 +377,6 @@ static int ca_label_to_mp_speaker_id(AudioChannelLabel label)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool ca_bitmap_from_ch_desc(struct ao *ao, AudioChannelLayout *layout,
|
||||
uint32_t *bitmap)
|
||||
{
|
||||
// If the channel layout uses channel descriptions, from my
|
||||
// exepriments there are there three possibile cases:
|
||||
// * The description has a label kAudioChannelLabel_Unknown:
|
||||
// Can't do anything about this (looks like non surround
|
||||
// layouts are like this).
|
||||
// * The description uses positional information: this in
|
||||
// theory could be used but one would have to map spatial
|
||||
// positions to labels which is not really feasible.
|
||||
// * The description has a well known label which can be mapped
|
||||
// to the waveextensible definition: this is the kind of
|
||||
// descriptions we process here.
|
||||
size_t ch_num = layout->mNumberChannelDescriptions;
|
||||
bool all_channels_valid = true;
|
||||
|
||||
for (int j=0; j < ch_num && all_channels_valid; j++) {
|
||||
AudioChannelLabel label = layout->mChannelDescriptions[j].mChannelLabel;
|
||||
const int mp_speaker_id = ca_label_to_mp_speaker_id(label);
|
||||
if (mp_speaker_id < 0) {
|
||||
MP_VERBOSE(ao, "channel label=%d unusable to build channel "
|
||||
"bitmap, skipping layout\n", label);
|
||||
all_channels_valid = false;
|
||||
} else {
|
||||
*bitmap |= 1ULL << mp_speaker_id;
|
||||
}
|
||||
}
|
||||
|
||||
return all_channels_valid;
|
||||
}
|
||||
|
||||
static bool ca_bitmap_from_ch_tag(struct ao *ao, AudioChannelLayout *layout,
|
||||
uint32_t *bitmap)
|
||||
{
|
||||
// This layout is defined exclusively by it's tag. Use the Audio
|
||||
// Format Services API to try and convert it to a bitmap that
|
||||
// mpv can use.
|
||||
uint32_t bitmap_size = sizeof(uint32_t);
|
||||
|
||||
AudioChannelLayoutTag tag = layout->mChannelLayoutTag;
|
||||
OSStatus err = AudioFormatGetProperty(
|
||||
kAudioFormatProperty_BitmapForLayoutTag,
|
||||
sizeof(AudioChannelLayoutTag), &tag,
|
||||
&bitmap_size, bitmap);
|
||||
if (err != noErr) {
|
||||
MP_VERBOSE(ao, "channel layout tag=%d unusable to build channel "
|
||||
"bitmap, skipping layout\n", tag);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void ca_log_layout(struct ao *ao, AudioChannelLayout *layout)
|
||||
{
|
||||
if (!mp_msg_test(ao->log, MSGL_V))
|
||||
@ -457,30 +403,57 @@ static void ca_log_layout(struct ao *ao, AudioChannelLayout *layout)
|
||||
}
|
||||
}
|
||||
|
||||
void ca_bitmaps_from_layouts(struct ao *ao,
|
||||
AudioChannelLayout *layouts, size_t n_layouts,
|
||||
uint32_t **bitmaps, size_t *n_bitmaps)
|
||||
bool ca_layout_to_mp_chmap(struct ao *ao, AudioChannelLayout *layout,
|
||||
struct mp_chmap *chmap)
|
||||
{
|
||||
*n_bitmaps = 0;
|
||||
*bitmaps = talloc_array_size(NULL, sizeof(uint32_t), n_layouts);
|
||||
AudioChannelLayoutTag tag = layout->mChannelLayoutTag;
|
||||
uint32_t layout_size = sizeof(layout);
|
||||
OSStatus err;
|
||||
|
||||
for (int i=0; i < n_layouts; i++) {
|
||||
uint32_t bitmap = 0;
|
||||
ca_log_layout(ao, &layouts[i]);
|
||||
if (tag == kAudioChannelLayoutTag_UseChannelBitmap) {
|
||||
err = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForBitmap,
|
||||
sizeof(uint32_t),
|
||||
&layout->mChannelBitmap,
|
||||
&layout_size,
|
||||
layout);
|
||||
CHECK_CA_ERROR("failed to convert channel bitmap to descriptions");
|
||||
} else if (tag != kAudioChannelLayoutTag_UseChannelDescriptions) {
|
||||
err = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForTag,
|
||||
sizeof(AudioChannelLayoutTag),
|
||||
&layout->mChannelLayoutTag,
|
||||
&layout_size,
|
||||
layout);
|
||||
CHECK_CA_ERROR("failed to convert channel tag to descriptions");
|
||||
}
|
||||
|
||||
switch (layouts[i].mChannelLayoutTag) {
|
||||
case kAudioChannelLayoutTag_UseChannelBitmap:
|
||||
(*bitmaps)[(*n_bitmaps)++] = layouts[i].mChannelBitmap;
|
||||
break;
|
||||
ca_log_layout(ao, layout);
|
||||
|
||||
case kAudioChannelLayoutTag_UseChannelDescriptions:
|
||||
if (ca_bitmap_from_ch_desc(ao, &layouts[i], &bitmap))
|
||||
(*bitmaps)[(*n_bitmaps)++] = bitmap;
|
||||
break;
|
||||
// If the channel layout uses channel descriptions, from my
|
||||
// experiments there are there three possibile cases:
|
||||
// * The description has a label kAudioChannelLabel_Unknown:
|
||||
// Can't do anything about this (looks like non surround
|
||||
// layouts are like this).
|
||||
// * The description uses positional information: this in
|
||||
// theory could be used but one would have to map spatial
|
||||
// positions to labels which is not really feasible.
|
||||
// * The description has a well known label which can be mapped
|
||||
// to the waveextensible definition: this is the kind of
|
||||
// descriptions we process here.
|
||||
|
||||
default:
|
||||
if (ca_bitmap_from_ch_tag(ao, &layouts[i], &bitmap))
|
||||
(*bitmaps)[(*n_bitmaps)++] = bitmap;
|
||||
for (int n = 0; n < layout->mNumberChannelDescriptions; n++) {
|
||||
AudioChannelLabel label = layout->mChannelDescriptions[n].mChannelLabel;
|
||||
uint8_t speaker = ca_label_to_mp_speaker_id(label);
|
||||
if (speaker < 0) {
|
||||
MP_VERBOSE(ao, "channel label=%d unusable to build channel "
|
||||
"bitmap, skipping layout\n", label);
|
||||
} else {
|
||||
chmap->speaker[n] = speaker;
|
||||
chmap->num = n + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return chmap->num > 0;
|
||||
coreaudio_error:
|
||||
ca_log_layout(ao, layout);
|
||||
return false;
|
||||
}
|
||||
|
@ -77,8 +77,7 @@ OSStatus ca_disable_device_listener(AudioDeviceID device, void *flag);
|
||||
bool ca_change_format(struct ao *ao, AudioStreamID stream,
|
||||
AudioStreamBasicDescription change_format);
|
||||
|
||||
void ca_bitmaps_from_layouts(struct ao *ao,
|
||||
AudioChannelLayout *layouts, size_t n_layouts,
|
||||
uint32_t **bitmaps, size_t *n_bitmaps);
|
||||
bool ca_layout_to_mp_chmap(struct ao *ao, AudioChannelLayout *layout,
|
||||
struct mp_chmap *chmap);
|
||||
|
||||
#endif /* MPV_COREAUDIO_UTILS_H */
|
||||
|
Loading…
Reference in New Issue
Block a user