summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2015-05-05 21:47:19 +0200
committerwm4 <wm4@nowhere>2015-05-05 21:47:19 +0200
commit934109a35b895c4237001522a49c5e196ad0e200 (patch)
tree5ea935818a0310da1af0e57a82085992ca3a6c27 /audio
parent399267393bb96710cde53c2fc7563f55cc32deb8 (diff)
downloadmpv-934109a35b895c4237001522a49c5e196ad0e200.tar.bz2
mpv-934109a35b895c4237001522a49c5e196ad0e200.tar.xz
ao_coreaudio: move channel mapping code to a separate file
Move all of the channel map retrieval/negotiation code to a separate file. This will (probably) be helpful when extending ao_coreaudio_exclusive.c. Nothing else changes, other than some minor cosmetics and renaming, and changing some details for decoupling it from the ao_coreaudio.c internals.
Diffstat (limited to 'audio')
-rw-r--r--audio/out/ao_coreaudio.c259
-rw-r--r--audio/out/ao_coreaudio_chmap.c268
-rw-r--r--audio/out/ao_coreaudio_chmap.h25
3 files changed, 297 insertions, 255 deletions
diff --git a/audio/out/ao_coreaudio.c b/audio/out/ao_coreaudio.c
index a91ba95c2f..09ca4fa8cd 100644
--- a/audio/out/ao_coreaudio.c
+++ b/audio/out/ao_coreaudio.c
@@ -25,8 +25,9 @@
#include "options/m_option.h"
#include "misc/ring.h"
#include "common/msg.h"
-#include "audio/out/ao_coreaudio_properties.h"
-#include "audio/out/ao_coreaudio_utils.h"
+#include "ao_coreaudio_chmap.h"
+#include "ao_coreaudio_properties.h"
+#include "ao_coreaudio_utils.h"
struct priv {
AudioDeviceID device;
@@ -37,9 +38,6 @@ struct priv {
int change_physical_format;
};
-bool ca_layout_to_mp_chmap(struct ao *ao, AudioChannelLayout *layout,
- struct mp_chmap *chmap);
-
static int64_t ca_get_hardware_latency(struct ao *ao) {
struct priv *p = ao->priv;
@@ -123,7 +121,6 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
return CONTROL_UNKNOWN;
}
-static bool init_chmap(struct ao *ao);
static bool init_audiounit(struct ao *ao, AudioStreamBasicDescription asbd);
static void init_physical_format(struct ao *ao);
@@ -160,7 +157,7 @@ static int init(struct ao *ao)
if (p->change_physical_format)
init_physical_format(ao);
- if (!init_chmap(ao))
+ if (!ca_init_chmap(ao, p->device))
goto coreaudio_error;
ao->format = af_fmt_from_planar(ao->format);
@@ -177,97 +174,6 @@ coreaudio_error:
return CONTROL_ERROR;
}
-static AudioChannelLayout* ca_query_layout(struct ao *ao, void *talloc_ctx)
-{
- struct priv *p = ao->priv;
- OSStatus err;
- uint32_t psize;
- AudioChannelLayout *r = NULL;
-
- AudioObjectPropertyAddress p_addr = (AudioObjectPropertyAddress) {
- .mSelector = kAudioDevicePropertyPreferredChannelLayout,
- .mScope = kAudioDevicePropertyScopeOutput,
- .mElement = kAudioObjectPropertyElementWildcard,
- };
-
- err = AudioObjectGetPropertyDataSize(p->device, &p_addr, 0, NULL, &psize);
- CHECK_CA_ERROR("could not get device preferred layout (size)");
-
- r = talloc_size(talloc_ctx, psize);
-
- err = AudioObjectGetPropertyData(p->device, &p_addr, 0, NULL, &psize, r);
- CHECK_CA_ERROR("could not get device preferred layout (get)");
-
-coreaudio_error:
- return r;
-}
-
-static AudioChannelLayout* ca_query_stereo_layout(struct ao *ao, void *talloc_ctx)
-{
- struct priv *p = ao->priv;
- OSStatus err;
- const int nch = 2;
- uint32_t channels[nch];
- AudioChannelLayout *r = NULL;
-
- AudioObjectPropertyAddress p_addr = (AudioObjectPropertyAddress) {
- .mSelector = kAudioDevicePropertyPreferredChannelsForStereo,
- .mScope = kAudioDevicePropertyScopeOutput,
- .mElement = kAudioObjectPropertyElementWildcard,
- };
-
- uint32_t psize = sizeof(channels);
- err = AudioObjectGetPropertyData(p->device, &p_addr, 0, NULL, &psize, channels);
- CHECK_CA_ERROR("could not get device preferred stereo layout");
-
- psize = sizeof(AudioChannelLayout) + nch * sizeof(AudioChannelDescription);
- r = talloc_zero_size(talloc_ctx, psize);
- r->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
- r->mNumberChannelDescriptions = nch;
-
- AudioChannelDescription desc = {0};
- desc.mChannelFlags = kAudioChannelFlags_AllOff;
-
- for(int i = 0; i < nch; i++) {
- desc.mChannelLabel = channels[i];
- r->mChannelDescriptions[i] = desc;
- }
-
-coreaudio_error:
- return r;
-}
-
-static bool init_chmap(struct ao *ao)
-{
- struct priv *p = ao->priv;
- void *ta_ctx = talloc_new(NULL);
-
- struct mp_chmap_sel chmap_sel = {.tmp = p};
- struct mp_chmap chmap = {0};
-
- AudioChannelLayout *ml = ca_query_layout(ao, ta_ctx);
- if (ml && ca_layout_to_mp_chmap(ao, ml, &chmap))
- mp_chmap_sel_add_map(&chmap_sel, &chmap);
-
- AudioChannelLayout *sl = ca_query_stereo_layout(ao, ta_ctx);
- if (sl && ca_layout_to_mp_chmap(ao, sl, &chmap))
- mp_chmap_sel_add_map(&chmap_sel, &chmap);
-
- talloc_free(ta_ctx);
-
- if (!ao_chmap_sel_adjust(ao, &chmap_sel, &ao->channels)) {
- MP_ERR(ao, "could not select a suitable channel map among the "
- "hardware supported ones. Make sure to configure your "
- "output device correctly in 'Audio MIDI Setup.app'\n");
- goto coreaudio_error;
- }
-
- return true;
-
-coreaudio_error:
- return false;
-}
-
static void init_physical_format(struct ao *ao)
{
struct priv *p = ao->priv;
@@ -469,163 +375,6 @@ static void hotplug_uninit(struct ao *ao)
}
}
-
-// Channel Mapping functions
-static const int speaker_map[][2] = {
- { kAudioChannelLabel_Left, MP_SPEAKER_ID_FL },
- { kAudioChannelLabel_Right, MP_SPEAKER_ID_FR },
- { kAudioChannelLabel_Center, MP_SPEAKER_ID_FC },
- { kAudioChannelLabel_LFEScreen, MP_SPEAKER_ID_LFE },
- { kAudioChannelLabel_LeftSurround, MP_SPEAKER_ID_BL },
- { kAudioChannelLabel_RightSurround, MP_SPEAKER_ID_BR },
- { kAudioChannelLabel_LeftCenter, MP_SPEAKER_ID_FLC },
- { kAudioChannelLabel_RightCenter, MP_SPEAKER_ID_FRC },
- { kAudioChannelLabel_CenterSurround, MP_SPEAKER_ID_BC },
- { kAudioChannelLabel_LeftSurroundDirect, MP_SPEAKER_ID_SL },
- { kAudioChannelLabel_RightSurroundDirect, MP_SPEAKER_ID_SR },
- { kAudioChannelLabel_TopCenterSurround, MP_SPEAKER_ID_TC },
- { kAudioChannelLabel_VerticalHeightLeft, MP_SPEAKER_ID_TFL },
- { kAudioChannelLabel_VerticalHeightCenter, MP_SPEAKER_ID_TFC },
- { kAudioChannelLabel_VerticalHeightRight, MP_SPEAKER_ID_TFR },
- { kAudioChannelLabel_TopBackLeft, MP_SPEAKER_ID_TBL },
- { kAudioChannelLabel_TopBackCenter, MP_SPEAKER_ID_TBC },
- { kAudioChannelLabel_TopBackRight, MP_SPEAKER_ID_TBR },
-
- // unofficial extensions
- { kAudioChannelLabel_RearSurroundLeft, MP_SPEAKER_ID_SDL },
- { kAudioChannelLabel_RearSurroundRight, MP_SPEAKER_ID_SDR },
- { kAudioChannelLabel_LeftWide, MP_SPEAKER_ID_WL },
- { kAudioChannelLabel_RightWide, MP_SPEAKER_ID_WR },
- { kAudioChannelLabel_LFE2, MP_SPEAKER_ID_LFE2 },
-
- { kAudioChannelLabel_HeadphonesLeft, MP_SPEAKER_ID_DL },
- { kAudioChannelLabel_HeadphonesRight, MP_SPEAKER_ID_DR },
-
- { kAudioChannelLabel_Unknown, MP_SPEAKER_ID_NA0 },
-
- { 0, -1 },
-};
-
-static int ca_label_to_mp_speaker_id(AudioChannelLabel label)
-{
- for (int i = 0; speaker_map[i][1] >= 0; i++)
- if (speaker_map[i][0] == label)
- return speaker_map[i][1];
- return -1;
-}
-
-static void ca_log_layout(struct ao *ao, int l, AudioChannelLayout *layout)
-{
- if (!mp_msg_test(ao->log, l))
- return;
-
- AudioChannelDescription *descs = layout->mChannelDescriptions;
-
- mp_msg(ao->log, l, "layout: tag: <%u>, bitmap: <%u>, "
- "descriptions <%u>\n",
- (unsigned) layout->mChannelLayoutTag,
- (unsigned) layout->mChannelBitmap,
- (unsigned) layout->mNumberChannelDescriptions);
-
- for (int i = 0; i < layout->mNumberChannelDescriptions; i++) {
- AudioChannelDescription d = descs[i];
- mp_msg(ao->log, l, " - description %d: label <%u, %u>, "
- " flags: <%u>, coords: <%f, %f, %f>\n", i,
- (unsigned) d.mChannelLabel,
- (unsigned) ca_label_to_mp_speaker_id(d.mChannelLabel),
- (unsigned) d.mChannelFlags,
- d.mCoordinates[0],
- d.mCoordinates[1],
- d.mCoordinates[2]);
- }
-}
-
-static AudioChannelLayout *ca_layout_to_custom_layout(
- struct ao *ao, void *talloc_ctx, AudioChannelLayout *l)
-{
- AudioChannelLayoutTag tag = l->mChannelLayoutTag;
- AudioChannelLayout *r;
- OSStatus err;
-
- if (tag == kAudioChannelLayoutTag_UseChannelBitmap) {
- uint32_t psize;
- err = AudioFormatGetPropertyInfo(
- kAudioFormatProperty_ChannelLayoutForBitmap,
- sizeof(uint32_t), &l->mChannelBitmap, &psize);
- CHECK_CA_ERROR("failed to convert channel bitmap to descriptions (info)");
- r = talloc_size(NULL, psize);
- err = AudioFormatGetProperty(
- kAudioFormatProperty_ChannelLayoutForBitmap,
- sizeof(uint32_t), &l->mChannelBitmap, &psize, r);
- CHECK_CA_ERROR("failed to convert channel bitmap to descriptions (get)");
- } else if (tag != kAudioChannelLayoutTag_UseChannelDescriptions) {
- uint32_t psize;
- err = AudioFormatGetPropertyInfo(
- kAudioFormatProperty_ChannelLayoutForTag,
- sizeof(AudioChannelLayoutTag), &l->mChannelLayoutTag, &psize);
- r = talloc_size(NULL, psize);
- CHECK_CA_ERROR("failed to convert channel tag to descriptions (info)");
- err = AudioFormatGetProperty(
- kAudioFormatProperty_ChannelLayoutForTag,
- sizeof(AudioChannelLayoutTag), &l->mChannelLayoutTag, &psize, r);
- CHECK_CA_ERROR("failed to convert channel tag to descriptions (get)");
- } else {
- r = l;
- }
-
- return r;
-coreaudio_error:
- return NULL;
-}
-
-bool ca_layout_to_mp_chmap(struct ao *ao, AudioChannelLayout *layout,
- struct mp_chmap *chmap)
-{
- void *talloc_ctx = talloc_new(NULL);
-
- MP_DBG(ao, "input channel layout:\n");
- ca_log_layout(ao, MSGL_DEBUG, layout);
-
- AudioChannelLayout *l = ca_layout_to_custom_layout(ao, talloc_ctx, layout);
- if (!l)
- goto coreaudio_error;
-
- MP_VERBOSE(ao, "converted input channel layout:\n");
- ca_log_layout(ao, MSGL_V, l);
-
- if (l->mNumberChannelDescriptions > MP_NUM_CHANNELS) {
- MP_VERBOSE(ao, "layout has too many descriptions (%u, max: %d)\n",
- (unsigned) l->mNumberChannelDescriptions, MP_NUM_CHANNELS);
- return false;
- }
-
- int next_na = MP_SPEAKER_ID_NA0;
- for (int n = 0; n < l->mNumberChannelDescriptions; n++) {
- AudioChannelLabel label = l->mChannelDescriptions[n].mChannelLabel;
- uint8_t speaker = ca_label_to_mp_speaker_id(label);
- if (speaker == MP_SPEAKER_ID_NA0 && next_na < MP_SPEAKER_ID_NA_LAST)
- {
- speaker = next_na++;
- }
- if (speaker < 0) {
- MP_VERBOSE(ao, "channel label=%u unusable to build channel "
- "bitmap, skipping layout\n", (unsigned) label);
- goto coreaudio_error;
- } else {
- chmap->speaker[n] = speaker;
- chmap->num = n + 1;
- }
- }
-
- talloc_free(talloc_ctx);
- return chmap->num > 0;
-coreaudio_error:
- MP_VERBOSE(ao, "converted input channel layout (failed):\n");
- ca_log_layout(ao, MSGL_V, layout);
- talloc_free(talloc_ctx);
- return false;
-}
-
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_coreaudio = {
diff --git a/audio/out/ao_coreaudio_chmap.c b/audio/out/ao_coreaudio_chmap.c
new file mode 100644
index 0000000000..f50bf4f3cc
--- /dev/null
+++ b/audio/out/ao_coreaudio_chmap.c
@@ -0,0 +1,268 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "common/common.h"
+
+#include "ao_coreaudio_utils.h"
+
+#include "ao_coreaudio_chmap.h"
+
+static const int speaker_map[][2] = {
+ { kAudioChannelLabel_Left, MP_SPEAKER_ID_FL },
+ { kAudioChannelLabel_Right, MP_SPEAKER_ID_FR },
+ { kAudioChannelLabel_Center, MP_SPEAKER_ID_FC },
+ { kAudioChannelLabel_LFEScreen, MP_SPEAKER_ID_LFE },
+ { kAudioChannelLabel_LeftSurround, MP_SPEAKER_ID_BL },
+ { kAudioChannelLabel_RightSurround, MP_SPEAKER_ID_BR },
+ { kAudioChannelLabel_LeftCenter, MP_SPEAKER_ID_FLC },
+ { kAudioChannelLabel_RightCenter, MP_SPEAKER_ID_FRC },
+ { kAudioChannelLabel_CenterSurround, MP_SPEAKER_ID_BC },
+ { kAudioChannelLabel_LeftSurroundDirect, MP_SPEAKER_ID_SL },
+ { kAudioChannelLabel_RightSurroundDirect, MP_SPEAKER_ID_SR },
+ { kAudioChannelLabel_TopCenterSurround, MP_SPEAKER_ID_TC },
+ { kAudioChannelLabel_VerticalHeightLeft, MP_SPEAKER_ID_TFL },
+ { kAudioChannelLabel_VerticalHeightCenter, MP_SPEAKER_ID_TFC },
+ { kAudioChannelLabel_VerticalHeightRight, MP_SPEAKER_ID_TFR },
+ { kAudioChannelLabel_TopBackLeft, MP_SPEAKER_ID_TBL },
+ { kAudioChannelLabel_TopBackCenter, MP_SPEAKER_ID_TBC },
+ { kAudioChannelLabel_TopBackRight, MP_SPEAKER_ID_TBR },
+
+ // unofficial extensions
+ { kAudioChannelLabel_RearSurroundLeft, MP_SPEAKER_ID_SDL },
+ { kAudioChannelLabel_RearSurroundRight, MP_SPEAKER_ID_SDR },
+ { kAudioChannelLabel_LeftWide, MP_SPEAKER_ID_WL },
+ { kAudioChannelLabel_RightWide, MP_SPEAKER_ID_WR },
+ { kAudioChannelLabel_LFE2, MP_SPEAKER_ID_LFE2 },
+
+ { kAudioChannelLabel_HeadphonesLeft, MP_SPEAKER_ID_DL },
+ { kAudioChannelLabel_HeadphonesRight, MP_SPEAKER_ID_DR },
+
+ { kAudioChannelLabel_Unknown, MP_SPEAKER_ID_NA0 },
+
+ { 0, -1 },
+};
+
+static int ca_label_to_mp_speaker_id(AudioChannelLabel label)
+{
+ for (int i = 0; speaker_map[i][1] >= 0; i++)
+ if (speaker_map[i][0] == label)
+ return speaker_map[i][1];
+ return -1;
+}
+
+static void ca_log_layout(struct ao *ao, int l, AudioChannelLayout *layout)
+{
+ if (!mp_msg_test(ao->log, l))
+ return;
+
+ AudioChannelDescription *descs = layout->mChannelDescriptions;
+
+ mp_msg(ao->log, l, "layout: tag: <%u>, bitmap: <%u>, "
+ "descriptions <%u>\n",
+ (unsigned) layout->mChannelLayoutTag,
+ (unsigned) layout->mChannelBitmap,
+ (unsigned) layout->mNumberChannelDescriptions);
+
+ for (int i = 0; i < layout->mNumberChannelDescriptions; i++) {
+ AudioChannelDescription d = descs[i];
+ mp_msg(ao->log, l, " - description %d: label <%u, %u>, "
+ " flags: <%u>, coords: <%f, %f, %f>\n", i,
+ (unsigned) d.mChannelLabel,
+ (unsigned) ca_label_to_mp_speaker_id(d.mChannelLabel),
+ (unsigned) d.mChannelFlags,
+ d.mCoordinates[0],
+ d.mCoordinates[1],
+ d.mCoordinates[2]);
+ }
+}
+
+static AudioChannelLayout *ca_layout_to_custom_layout(struct ao *ao,
+ void *talloc_ctx,
+ AudioChannelLayout *l)
+{
+ AudioChannelLayoutTag tag = l->mChannelLayoutTag;
+ AudioChannelLayout *r;
+ OSStatus err;
+
+ if (tag == kAudioChannelLayoutTag_UseChannelBitmap) {
+ uint32_t psize;
+ err = AudioFormatGetPropertyInfo(
+ kAudioFormatProperty_ChannelLayoutForBitmap,
+ sizeof(uint32_t), &l->mChannelBitmap, &psize);
+ CHECK_CA_ERROR("failed to convert channel bitmap to descriptions (info)");
+ r = talloc_size(NULL, psize);
+ err = AudioFormatGetProperty(
+ kAudioFormatProperty_ChannelLayoutForBitmap,
+ sizeof(uint32_t), &l->mChannelBitmap, &psize, r);
+ CHECK_CA_ERROR("failed to convert channel bitmap to descriptions (get)");
+ } else if (tag != kAudioChannelLayoutTag_UseChannelDescriptions) {
+ uint32_t psize;
+ err = AudioFormatGetPropertyInfo(
+ kAudioFormatProperty_ChannelLayoutForTag,
+ sizeof(AudioChannelLayoutTag), &l->mChannelLayoutTag, &psize);
+ r = talloc_size(NULL, psize);
+ CHECK_CA_ERROR("failed to convert channel tag to descriptions (info)");
+ err = AudioFormatGetProperty(
+ kAudioFormatProperty_ChannelLayoutForTag,
+ sizeof(AudioChannelLayoutTag), &l->mChannelLayoutTag, &psize, r);
+ CHECK_CA_ERROR("failed to convert channel tag to descriptions (get)");
+ } else {
+ r = l;
+ }
+
+ return r;
+coreaudio_error:
+ return NULL;
+}
+
+static bool ca_layout_to_mp_chmap(struct ao *ao, AudioChannelLayout *layout,
+ struct mp_chmap *chmap)
+{
+ void *talloc_ctx = talloc_new(NULL);
+
+ MP_DBG(ao, "input channel layout:\n");
+ ca_log_layout(ao, MSGL_DEBUG, layout);
+
+ AudioChannelLayout *l = ca_layout_to_custom_layout(ao, talloc_ctx, layout);
+ if (!l)
+ goto coreaudio_error;
+
+ MP_VERBOSE(ao, "converted input channel layout:\n");
+ ca_log_layout(ao, MSGL_V, l);
+
+ if (l->mNumberChannelDescriptions > MP_NUM_CHANNELS) {
+ MP_VERBOSE(ao, "layout has too many descriptions (%u, max: %d)\n",
+ (unsigned) l->mNumberChannelDescriptions, MP_NUM_CHANNELS);
+ return false;
+ }
+
+ int next_na = MP_SPEAKER_ID_NA0;
+ for (int n = 0; n < l->mNumberChannelDescriptions; n++) {
+ AudioChannelLabel label = l->mChannelDescriptions[n].mChannelLabel;
+ uint8_t speaker = ca_label_to_mp_speaker_id(label);
+ if (speaker == MP_SPEAKER_ID_NA0 && next_na < MP_SPEAKER_ID_NA_LAST)
+ speaker = next_na++;
+ if (speaker < 0) {
+ MP_VERBOSE(ao, "channel label=%u unusable to build channel "
+ "bitmap, skipping layout\n", (unsigned) label);
+ goto coreaudio_error;
+ } else {
+ chmap->speaker[n] = speaker;
+ chmap->num = n + 1;
+ }
+ }
+
+ talloc_free(talloc_ctx);
+ return chmap->num > 0;
+coreaudio_error:
+ MP_VERBOSE(ao, "converted input channel layout (failed):\n");
+ ca_log_layout(ao, MSGL_V, layout);
+ talloc_free(talloc_ctx);
+ return false;
+}
+
+static AudioChannelLayout* ca_query_layout(struct ao *ao,
+ AudioDeviceID device,
+ void *talloc_ctx)
+{
+ OSStatus err;
+ uint32_t psize;
+ AudioChannelLayout *r = NULL;
+
+ AudioObjectPropertyAddress p_addr = (AudioObjectPropertyAddress) {
+ .mSelector = kAudioDevicePropertyPreferredChannelLayout,
+ .mScope = kAudioDevicePropertyScopeOutput,
+ .mElement = kAudioObjectPropertyElementWildcard,
+ };
+
+ err = AudioObjectGetPropertyDataSize(device, &p_addr, 0, NULL, &psize);
+ CHECK_CA_ERROR("could not get device preferred layout (size)");
+
+ r = talloc_size(talloc_ctx, psize);
+
+ err = AudioObjectGetPropertyData(device, &p_addr, 0, NULL, &psize, r);
+ CHECK_CA_ERROR("could not get device preferred layout (get)");
+
+coreaudio_error:
+ return r;
+}
+
+static AudioChannelLayout* ca_query_stereo_layout(struct ao *ao,
+ AudioDeviceID device,
+ void *talloc_ctx)
+{
+ OSStatus err;
+ const int nch = 2;
+ uint32_t channels[nch];
+ AudioChannelLayout *r = NULL;
+
+ AudioObjectPropertyAddress p_addr = (AudioObjectPropertyAddress) {
+ .mSelector = kAudioDevicePropertyPreferredChannelsForStereo,
+ .mScope = kAudioDevicePropertyScopeOutput,
+ .mElement = kAudioObjectPropertyElementWildcard,
+ };
+
+ uint32_t psize = sizeof(channels);
+ err = AudioObjectGetPropertyData(device, &p_addr, 0, NULL, &psize, channels);
+ CHECK_CA_ERROR("could not get device preferred stereo layout");
+
+ psize = sizeof(AudioChannelLayout) + nch * sizeof(AudioChannelDescription);
+ r = talloc_zero_size(talloc_ctx, psize);
+ r->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
+ r->mNumberChannelDescriptions = nch;
+
+ AudioChannelDescription desc = {0};
+ desc.mChannelFlags = kAudioChannelFlags_AllOff;
+
+ for(int i = 0; i < nch; i++) {
+ desc.mChannelLabel = channels[i];
+ r->mChannelDescriptions[i] = desc;
+ }
+
+coreaudio_error:
+ return r;
+}
+
+bool ca_init_chmap(struct ao *ao, AudioDeviceID device)
+{
+ void *ta_ctx = talloc_new(NULL);
+
+ struct mp_chmap_sel chmap_sel = {.tmp = ta_ctx};
+ struct mp_chmap chmap = {0};
+
+ AudioChannelLayout *ml = ca_query_layout(ao, device, ta_ctx);
+ if (ml && ca_layout_to_mp_chmap(ao, ml, &chmap))
+ mp_chmap_sel_add_map(&chmap_sel, &chmap);
+
+ AudioChannelLayout *sl = ca_query_stereo_layout(ao, device, ta_ctx);
+ if (sl && ca_layout_to_mp_chmap(ao, sl, &chmap))
+ mp_chmap_sel_add_map(&chmap_sel, &chmap);
+
+ if (!ao_chmap_sel_adjust(ao, &chmap_sel, &ao->channels)) {
+ MP_ERR(ao, "could not select a suitable channel map among the "
+ "hardware supported ones. Make sure to configure your "
+ "output device correctly in 'Audio MIDI Setup.app'\n");
+ goto coreaudio_error;
+ }
+
+ talloc_free(ta_ctx);
+ return true;
+
+coreaudio_error:
+ talloc_free(ta_ctx);
+ return false;
+}
diff --git a/audio/out/ao_coreaudio_chmap.h b/audio/out/ao_coreaudio_chmap.h
new file mode 100644
index 0000000000..ce31975b6d
--- /dev/null
+++ b/audio/out/ao_coreaudio_chmap.h
@@ -0,0 +1,25 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MPV_COREAUDIO_CHMAP_H
+#define MPV_COREAUDIO_CHMAP_H
+
+#include <AudioToolbox/AudioToolbox.h>
+
+bool ca_init_chmap(struct ao *ao, AudioDeviceID device);
+
+#endif