mirror of https://github.com/mpv-player/mpv
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.
This commit is contained in:
parent
399267393b
commit
934109a35b
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -132,6 +132,7 @@ def build(ctx):
|
|||
( "audio/out/ao.c" ),
|
||||
( "audio/out/ao_alsa.c", "alsa" ),
|
||||
( "audio/out/ao_coreaudio.c", "coreaudio" ),
|
||||
( "audio/out/ao_coreaudio_chmap.c", "coreaudio" ),
|
||||
( "audio/out/ao_coreaudio_exclusive.c", "coreaudio" ),
|
||||
( "audio/out/ao_coreaudio_properties.c", "coreaudio" ),
|
||||
( "audio/out/ao_coreaudio_utils.c", "coreaudio" ),
|
||||
|
|
Loading…
Reference in New Issue