summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
authorMisaki Kasumi <misakikasumi@outlook.com>2024-03-17 04:14:47 +0800
committerder richter <der.richter@gmx.de>2024-03-29 13:46:59 +0100
commit1ed8607292ae4f9eafd041d304ad68740ffd3fd7 (patch)
tree3d62798116b3bd358ee5b9ff6d2733a45f0cd132 /audio
parent7ab1080749b02e3e545166e67528c6a039e04969 (diff)
downloadmpv-1ed8607292ae4f9eafd041d304ad68740ffd3fd7.tar.bz2
mpv-1ed8607292ae4f9eafd041d304ad68740ffd3fd7.tar.xz
ao_avfoundation: initial avfoundation ao support
Diffstat (limited to 'audio')
-rw-r--r--audio/out/ao.c4
-rw-r--r--audio/out/ao_avfoundation.m358
-rw-r--r--audio/out/ao_coreaudio_chmap.c217
-rw-r--r--audio/out/ao_coreaudio_chmap.h9
-rw-r--r--audio/out/ao_coreaudio_properties.h5
-rw-r--r--audio/out/ao_coreaudio_utils.c3
-rw-r--r--audio/out/ao_coreaudio_utils.h5
7 files changed, 576 insertions, 25 deletions
diff --git a/audio/out/ao.c b/audio/out/ao.c
index 3c61ee786c..75fcbac6fa 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -40,6 +40,7 @@ extern const struct ao_driver audio_out_audiotrack;
extern const struct ao_driver audio_out_audiounit;
extern const struct ao_driver audio_out_coreaudio;
extern const struct ao_driver audio_out_coreaudio_exclusive;
+extern const struct ao_driver audio_out_avfoundation;
extern const struct ao_driver audio_out_rsound;
extern const struct ao_driver audio_out_pipewire;
extern const struct ao_driver audio_out_sndio;
@@ -65,6 +66,9 @@ static const struct ao_driver * const audio_out_drivers[] = {
#if HAVE_COREAUDIO
&audio_out_coreaudio,
#endif
+#if HAVE_AVFOUNDATION
+ &audio_out_avfoundation,
+#endif
#if HAVE_PIPEWIRE
&audio_out_pipewire,
#endif
diff --git a/audio/out/ao_avfoundation.m b/audio/out/ao_avfoundation.m
new file mode 100644
index 0000000000..3ec11c4fdf
--- /dev/null
+++ b/audio/out/ao_avfoundation.m
@@ -0,0 +1,358 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ao.h"
+#include "audio/format.h"
+#include "audio/out/ao_coreaudio_chmap.h"
+#include "audio/out/ao_coreaudio_utils.h"
+#include "common/common.h"
+#include "common/msg.h"
+#include "internal.h"
+#include "osdep/timer.h"
+#include "ta/ta_talloc.h"
+
+#import <AVFoundation/AVFoundation.h>
+#import <Foundation/Foundation.h>
+#import <CoreAudioTypes/CoreAudioTypes.h>
+#import <CoreFoundation/CoreFoundation.h>
+#import <CoreMedia/CoreMedia.h>
+
+
+@interface AVObserver : NSObject {
+ struct ao *ao;
+}
+- (void)handleRestartNotification:(NSNotification*)notification;
+@end
+
+struct priv {
+ AVSampleBufferAudioRenderer *renderer;
+ AVSampleBufferRenderSynchronizer *synchronizer;
+ dispatch_queue_t queue;
+ CMAudioFormatDescriptionRef format_description;
+ AVObserver *observer;
+ int64_t end_time_av;
+};
+
+static int64_t CMTimeGetNanoseconds(CMTime time)
+{
+ time = CMTimeConvertScale(time, 1000000000, kCMTimeRoundingMethod_Default);
+ return time.value;
+}
+
+static CMTime CMTimeFromNanoseconds(int64_t time)
+{
+ return CMTimeMake(time, 1000000000);
+}
+
+static void feed(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+ int samplerate = ao->samplerate;
+ int sstride = ao->sstride;
+
+ CMBlockBufferRef block_buffer = NULL;
+ CMSampleBufferRef sample_buffer = NULL;
+ OSStatus err;
+
+ int request_sample_count = samplerate / 10;
+ int buffer_size = request_sample_count * sstride;
+ void *data[] = {CFAllocatorAllocate(NULL, buffer_size, 0)};
+
+ int64_t cur_time_av = CMTimeGetNanoseconds([p->synchronizer currentTime]);
+ int64_t cur_time_mp = mp_time_ns();
+ int64_t end_time_av = MPMAX(p->end_time_av, cur_time_av);
+ int64_t time_delta = CMTimeGetNanoseconds(CMTimeMake(request_sample_count, samplerate));
+ int real_sample_count = ao_read_data_nonblocking(ao, data, request_sample_count, end_time_av - cur_time_av + cur_time_mp + time_delta);
+ if (real_sample_count == 0) {
+ // avoid spinning by blocking the thread
+ mp_sleep_ns(10000000);
+ goto finish;
+ }
+
+ if ((err = CMBlockBufferCreateWithMemoryBlock(
+ NULL,
+ data[0],
+ buffer_size,
+ NULL,
+ NULL,
+ 0,
+ real_sample_count * sstride,
+ 0,
+ &block_buffer
+ )) != noErr) {
+ MP_FATAL(ao, "failed to create block buffer\n");
+ MP_VERBOSE(ao, "CMBlockBufferCreateWithMemoryBlock returned %d\n", err);
+ goto error;
+ }
+ data[0] = NULL;
+
+ CMSampleTimingInfo sample_timing_into[] = {(CMSampleTimingInfo) {
+ .duration = CMTimeMake(1, samplerate),
+ .presentationTimeStamp = CMTimeFromNanoseconds(end_time_av),
+ .decodeTimeStamp = kCMTimeInvalid
+ }};
+ size_t sample_size_array[] = {sstride};
+ if ((err = CMSampleBufferCreateReady(
+ NULL,
+ block_buffer,
+ p->format_description,
+ real_sample_count,
+ 1,
+ sample_timing_into,
+ 1,
+ sample_size_array,
+ &sample_buffer
+ )) != noErr) {
+ MP_FATAL(ao, "failed to create sample buffer\n");
+ MP_VERBOSE(ao, "CMSampleBufferCreateReady returned %d\n", err);
+ goto error;
+ }
+
+ [p->renderer enqueueSampleBuffer:sample_buffer];
+
+ time_delta = CMTimeGetNanoseconds(CMTimeMake(real_sample_count, samplerate));
+ p->end_time_av = end_time_av + time_delta;
+
+ goto finish;
+
+error:
+ ao_request_reload(ao);
+finish:
+ if (data[0]) CFAllocatorDeallocate(NULL, data[0]);
+ if (block_buffer) CFRelease(block_buffer);
+ if (sample_buffer) CFRelease(sample_buffer);
+}
+
+static void start(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+
+ p->end_time_av = -1;
+ [p->synchronizer setRate:1];
+ [p->renderer requestMediaDataWhenReadyOnQueue:p->queue usingBlock:^{
+ feed(ao);
+ }];
+}
+
+static void stop(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+
+ dispatch_sync(p->queue, ^{
+ [p->renderer stopRequestingMediaData];
+ [p->renderer flush];
+ [p->synchronizer setRate:0];
+ });
+}
+
+static int control(struct ao *ao, enum aocontrol cmd, void *arg)
+{
+ struct priv *p = ao->priv;
+
+ switch (cmd) {
+ case AOCONTROL_GET_MUTE:
+ *(bool*)arg = [p->renderer isMuted];
+ return CONTROL_OK;
+ case AOCONTROL_GET_VOLUME:
+ *(float*)arg = [p->renderer volume] * 100;
+ return CONTROL_OK;
+ case AOCONTROL_SET_MUTE:
+ [p->renderer setMuted:*(bool*)arg];
+ return CONTROL_OK;
+ case AOCONTROL_SET_VOLUME:
+ [p->renderer setVolume:*(float*)arg / 100];
+ return CONTROL_OK;
+ default:
+ return CONTROL_UNKNOWN;
+ }
+}
+
+@implementation AVObserver
+- (instancetype)initWithAO:(struct ao*)_ao {
+ self = [super init];
+ if (self) {
+ ao = _ao;
+ }
+ return self;
+}
+- (void)handleRestartNotification:(NSNotification*)notification {
+ char *name = cfstr_get_cstr((CFStringRef)notification.name);
+ MP_WARN(ao, "restarting due to system notification; this will cause desync\n");
+ MP_VERBOSE(ao, "notification name: %s\n", name);
+ talloc_free(name);
+ stop(ao);
+ start(ao);
+}
+@end
+
+static int init(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+ AudioChannelLayout *layout = NULL;
+
+#if TARGET_OS_IPHONE
+ AVAudioSession *instance = AVAudioSession.sharedInstance;
+ NSInteger maxChannels = instance.maximumOutputNumberOfChannels;
+ NSInteger prefChannels = MIN(maxChannels, ao->channels.num);
+ [instance setCategory:AVAudioSessionCategoryPlayback error:nil];
+ [instance setMode:AVAudioSessionModeMoviePlayback error:nil];
+ [instance setActive:YES error:nil];
+ [instance setPreferredOutputNumberOfChannels:prefChannels error:nil];
+#endif
+
+ if ((p->renderer = [[AVSampleBufferAudioRenderer alloc] init]) == nil) {
+ MP_FATAL(ao, "failed to create audio renderer\n");
+ MP_VERBOSE(ao, "AVSampleBufferAudioRenderer failed to initialize\n");
+ goto error;
+ }
+ if ((p->synchronizer = [[AVSampleBufferRenderSynchronizer alloc] init]) == nil) {
+ MP_FATAL(ao, "failed to create rendering synchronizer\n");
+ MP_VERBOSE(ao, "AVSampleBufferRenderSynchronizer failed to initialize\n");
+ goto error;
+ }
+ if ((p->queue = dispatch_queue_create(
+ "avfoundation event",
+ dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INTERACTIVE, 0)
+ )) == NULL) {
+ MP_FATAL(ao, "failed to create dispatch queue\n");
+ MP_VERBOSE(ao, "dispatch_queue_create failed\n");
+ goto error;
+ }
+
+ if (ao->device && ao->device[0]) {
+ [p->renderer setAudioOutputDeviceUniqueID:(NSString*)cfstr_from_cstr(ao->device)];
+ }
+
+ [p->synchronizer addRenderer:p->renderer];
+ if (@available(tvOS 14.5, iOS 14.5, macOS 11.3, *)) {
+ [p->synchronizer setDelaysRateChangeUntilHasSufficientMediaData:NO];
+ }
+
+ if (af_fmt_is_spdif(ao->format)) {
+ MP_FATAL(ao, "avfoundation does not support SPDIF\n");
+#if HAVE_COREAUDIO
+ MP_FATAL(ao, "please use coreaudio_exclusive instead\n");
+#endif
+ goto error;
+ }
+
+ // AVSampleBufferAudioRenderer only supports interleaved formats
+ ao->format = af_fmt_from_planar(ao->format);
+ if (af_fmt_is_planar(ao->format)) {
+ MP_FATAL(ao, "planar audio formats are unsupported\n");
+ goto error;
+ }
+
+ AudioStreamBasicDescription asbd;
+ ca_fill_asbd(ao, &asbd);
+ size_t layout_size = sizeof(AudioChannelLayout)
+ + (ao->channels.num - 1) * sizeof(AudioChannelDescription);
+ layout = talloc_size(ao, layout_size);
+ layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
+ layout->mNumberChannelDescriptions = ao->channels.num;
+ for (int i = 0; i < ao->channels.num; ++i) {
+ AudioChannelDescription *desc = layout->mChannelDescriptions + i;
+ desc->mChannelFlags = kAudioChannelFlags_AllOff;
+ desc->mChannelLabel = mp_speaker_id_to_ca_label(ao->channels.speaker[i]);
+ }
+
+ void *talloc_ctx = talloc_new(NULL);
+ AudioChannelLayout *std_layout = ca_find_standard_layout(talloc_ctx, layout);
+ memmove(layout, std_layout, sizeof(AudioChannelLayout));
+ talloc_free(talloc_ctx);
+ ca_log_layout(ao, MSGL_V, layout);
+
+ OSStatus err;
+ if ((err = CMAudioFormatDescriptionCreate(
+ NULL,
+ &asbd,
+ layout_size,
+ layout,
+ 0,
+ NULL,
+ NULL,
+ &p->format_description
+ )) != noErr) {
+ MP_FATAL(ao, "failed to create audio format description\n");
+ MP_VERBOSE(ao, "CMAudioFormatDescriptionCreate returned %d\n", err);
+ goto error;
+ }
+ talloc_free(layout);
+ layout = NULL;
+
+ // AVSampleBufferAudioRenderer read ahead aggressively
+ ao->device_buffer = ao->samplerate * 2;
+
+ p->observer = [[AVObserver alloc] initWithAO:ao];
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ [center addObserver:p->observer selector:@selector(handleRestartNotification:) name:AVSampleBufferAudioRendererOutputConfigurationDidChangeNotification object:p->renderer];
+ [center addObserver:p->observer selector:@selector(handleRestartNotification:) name:AVSampleBufferAudioRendererWasFlushedAutomaticallyNotification object:p->renderer];
+
+ return CONTROL_OK;
+
+error:
+ talloc_free(layout);
+ if (p->renderer) [p->renderer release];
+ if (p->synchronizer) [p->synchronizer release];
+ if (p->queue) dispatch_release(p->queue);
+ if (p->format_description) CFRelease(p->format_description);
+
+#if TARGET_OS_IPHONE
+ [AVAudioSession.sharedInstance setActive:NO
+ withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
+ error:nil
+ ];
+#endif
+
+ return CONTROL_ERROR;
+}
+
+static void uninit(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+
+ stop(ao);
+
+ [p->renderer release];
+ [p->synchronizer release];
+ dispatch_release(p->queue);
+ CFRelease(p->format_description);
+
+ [[NSNotificationCenter defaultCenter] removeObserver:p->observer];
+ [p->observer release];
+
+#if TARGET_OS_IPHONE
+ [AVAudioSession.sharedInstance setActive:NO
+ withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
+ error:nil
+ ];
+#endif
+}
+
+#define OPT_BASE_STRUCT struct priv
+
+const struct ao_driver audio_out_avfoundation = {
+ .description = "AVFoundation AVSampleBufferAudioRenderer",
+ .name = "avfoundation",
+ .uninit = uninit,
+ .init = init,
+ .control = control,
+ .reset = stop,
+ .start = start,
+ .list_devs = ca_get_device_list,
+ .priv_size = sizeof(struct priv),
+};
diff --git a/audio/out/ao_coreaudio_chmap.c b/audio/out/ao_coreaudio_chmap.c
index fb7f577ea1..5a129c4e67 100644
--- a/audio/out/ao_coreaudio_chmap.c
+++ b/audio/out/ao_coreaudio_chmap.c
@@ -22,6 +22,7 @@
#include "ao_coreaudio_utils.h"
#include "ao_coreaudio_chmap.h"
+#include <CoreAudioTypes/CoreAudioTypes.h>
static const int speaker_map[][2] = {
{ kAudioChannelLabel_Left, MP_SPEAKER_ID_FL },
@@ -65,6 +66,119 @@ static const int speaker_map[][2] = {
{ 0, -1 },
};
+static const AudioChannelLayoutTag std_layouts[] = {
+ (100U<<16) | 1, // kAudioChannelLayoutTag_Mono
+ (101U<<16) | 2, // kAudioChannelLayoutTag_Stereo
+ (102U<<16) | 2, // kAudioChannelLayoutTag_StereoHeadphones
+ (103U<<16) | 2, // kAudioChannelLayoutTag_MatrixStereo
+ (104U<<16) | 2, // kAudioChannelLayoutTag_MidSide
+ (105U<<16) | 2, // kAudioChannelLayoutTag_XY
+ (106U<<16) | 2, // kAudioChannelLayoutTag_Binaural
+ (107U<<16) | 4, // kAudioChannelLayoutTag_Ambisonic_B_Format
+ (108U<<16) | 4, // kAudioChannelLayoutTag_Quadraphonic
+ (109U<<16) | 5, // kAudioChannelLayoutTag_Pentagonal
+ (110U<<16) | 6, // kAudioChannelLayoutTag_Hexagonal
+ (111U<<16) | 8, // kAudioChannelLayoutTag_Octagonal
+ (112U<<16) | 8, // kAudioChannelLayoutTag_Cube
+ (113U<<16) | 3, // kAudioChannelLayoutTag_MPEG_3_0_A
+ (114U<<16) | 3, // kAudioChannelLayoutTag_MPEG_3_0_B
+ (115U<<16) | 4, // kAudioChannelLayoutTag_MPEG_4_0_A
+ (116U<<16) | 4, // kAudioChannelLayoutTag_MPEG_4_0_B
+ (117U<<16) | 5, // kAudioChannelLayoutTag_MPEG_5_0_A
+ (118U<<16) | 5, // kAudioChannelLayoutTag_MPEG_5_0_B
+ (119U<<16) | 5, // kAudioChannelLayoutTag_MPEG_5_0_C
+ (120U<<16) | 5, // kAudioChannelLayoutTag_MPEG_5_0_D
+ (121U<<16) | 6, // kAudioChannelLayoutTag_MPEG_5_1_A
+ (122U<<16) | 6, // kAudioChannelLayoutTag_MPEG_5_1_B
+ (123U<<16) | 6, // kAudioChannelLayoutTag_MPEG_5_1_C
+ (124U<<16) | 6, // kAudioChannelLayoutTag_MPEG_5_1_D
+ (125U<<16) | 7, // kAudioChannelLayoutTag_MPEG_6_1_A
+ (126U<<16) | 8, // kAudioChannelLayoutTag_MPEG_7_1_A
+ (127U<<16) | 8, // kAudioChannelLayoutTag_MPEG_7_1_B
+ (128U<<16) | 8, // kAudioChannelLayoutTag_MPEG_7_1_C
+ (129U<<16) | 8, // kAudioChannelLayoutTag_Emagic_Default_7_1
+ (130U<<16) | 8, // kAudioChannelLayoutTag_SMPTE_DTV
+ (131U<<16) | 3, // kAudioChannelLayoutTag_ITU_2_1
+ (132U<<16) | 4, // kAudioChannelLayoutTag_ITU_2_2
+ (133U<<16) | 3, // kAudioChannelLayoutTag_DVD_4
+ (134U<<16) | 4, // kAudioChannelLayoutTag_DVD_5
+ (135U<<16) | 5, // kAudioChannelLayoutTag_DVD_6
+ (136U<<16) | 4, // kAudioChannelLayoutTag_DVD_10
+ (137U<<16) | 5, // kAudioChannelLayoutTag_DVD_11
+ (138U<<16) | 5, // kAudioChannelLayoutTag_DVD_18
+ (139U<<16) | 6, // kAudioChannelLayoutTag_AudioUnit_6_0
+ (140U<<16) | 7, // kAudioChannelLayoutTag_AudioUnit_7_0
+ (148U<<16) | 7, // kAudioChannelLayoutTag_AudioUnit_7_0_Front
+ (141U<<16) | 6, // kAudioChannelLayoutTag_AAC_6_0
+ (142U<<16) | 7, // kAudioChannelLayoutTag_AAC_6_1
+ (143U<<16) | 7, // kAudioChannelLayoutTag_AAC_7_0
+ (183U<<16) | 8, // kAudioChannelLayoutTag_AAC_7_1_B
+ (184U<<16) | 8, // kAudioChannelLayoutTag_AAC_7_1_C
+ (144U<<16) | 8, // kAudioChannelLayoutTag_AAC_Octagonal
+ (145U<<16) | 16, // kAudioChannelLayoutTag_TMH_10_2_std
+ (146U<<16) | 21, // kAudioChannelLayoutTag_TMH_10_2_full
+ (149U<<16) | 2, // kAudioChannelLayoutTag_AC3_1_0_1
+ (150U<<16) | 3, // kAudioChannelLayoutTag_AC3_3_0
+ (151U<<16) | 4, // kAudioChannelLayoutTag_AC3_3_1
+ (152U<<16) | 4, // kAudioChannelLayoutTag_AC3_3_0_1
+ (153U<<16) | 4, // kAudioChannelLayoutTag_AC3_2_1_1
+ (154U<<16) | 5, // kAudioChannelLayoutTag_AC3_3_1_1
+ (155U<<16) | 6, // kAudioChannelLayoutTag_EAC_6_0_A
+ (156U<<16) | 7, // kAudioChannelLayoutTag_EAC_7_0_A
+ (157U<<16) | 7, // kAudioChannelLayoutTag_EAC3_6_1_A
+ (158U<<16) | 7, // kAudioChannelLayoutTag_EAC3_6_1_B
+ (159U<<16) | 7, // kAudioChannelLayoutTag_EAC3_6_1_C
+ (160U<<16) | 8, // kAudioChannelLayoutTag_EAC3_7_1_A
+ (161U<<16) | 8, // kAudioChannelLayoutTag_EAC3_7_1_B
+ (162U<<16) | 8, // kAudioChannelLayoutTag_EAC3_7_1_C
+ (163U<<16) | 8, // kAudioChannelLayoutTag_EAC3_7_1_D
+ (164U<<16) | 8, // kAudioChannelLayoutTag_EAC3_7_1_E
+ (165U<<16) | 8, // kAudioChannelLayoutTag_EAC3_7_1_F
+ (166U<<16) | 8, // kAudioChannelLayoutTag_EAC3_7_1_G
+ (167U<<16) | 8, // kAudioChannelLayoutTag_EAC3_7_1_H
+ (168U<<16) | 4, // kAudioChannelLayoutTag_DTS_3_1
+ (169U<<16) | 5, // kAudioChannelLayoutTag_DTS_4_1
+ (170U<<16) | 6, // kAudioChannelLayoutTag_DTS_6_0_A
+ (171U<<16) | 6, // kAudioChannelLayoutTag_DTS_6_0_B
+ (172U<<16) | 6, // kAudioChannelLayoutTag_DTS_6_0_C
+ (173U<<16) | 7, // kAudioChannelLayoutTag_DTS_6_1_A
+ (174U<<16) | 7, // kAudioChannelLayoutTag_DTS_6_1_B
+ (175U<<16) | 7, // kAudioChannelLayoutTag_DTS_6_1_C
+ (176U<<16) | 7, // kAudioChannelLayoutTag_DTS_7_0
+ (177U<<16) | 8, // kAudioChannelLayoutTag_DTS_7_1
+ (178U<<16) | 8, // kAudioChannelLayoutTag_DTS_8_0_A
+ (179U<<16) | 8, // kAudioChannelLayoutTag_DTS_8_0_B
+ (180U<<16) | 9, // kAudioChannelLayoutTag_DTS_8_1_A
+ (181U<<16) | 9, // kAudioChannelLayoutTag_DTS_8_1_B
+ (182U<<16) | 7, // kAudioChannelLayoutTag_DTS_6_1_D
+ (185U<<16) | 4, // kAudioChannelLayoutTag_WAVE_4_0_B
+ (186U<<16) | 5, // kAudioChannelLayoutTag_WAVE_5_0_B
+ (187U<<16) | 6, // kAudioChannelLayoutTag_WAVE_5_1_B
+ (188U<<16) | 7, // kAudioChannelLayoutTag_WAVE_6_1
+ (189U<<16) | 8, // kAudioChannelLayoutTag_WAVE_7_1
+ (194U<<16) | 8, // kAudioChannelLayoutTag_Atmos_5_1_2
+ (195U<<16) | 10, // kAudioChannelLayoutTag_Atmos_5_1_4
+ (196U<<16) | 10, // kAudioChannelLayoutTag_Atmos_7_1_2
+ (192U<<16) | 12, // kAudioChannelLayoutTag_Atmos_7_1_4
+ (193U<<16) | 16, // kAudioChannelLayoutTag_Atmos_9_1_6
+ (197U<<16) | 4, // kAudioChannelLayoutTag_Logic_4_0_C
+ (198U<<16) | 6, // kAudioChannelLayoutTag_Logic_6_0_B
+ (199U<<16) | 7, // kAudioChannelLayoutTag_Logic_6_1_B
+ (200U<<16) | 7, // kAudioChannelLayoutTag_Logic_6_1_D
+ (201U<<16) | 8, // kAudioChannelLayoutTag_Logic_7_1_B
+ (202U<<16) | 12, // kAudioChannelLayoutTag_Logic_Atmos_7_1_4_B
+ (203U<<16) | 14, // kAudioChannelLayoutTag_Logic_Atmos_7_1_6
+ (204U<<16) | 24, // kAudioChannelLayoutTag_CICP_13
+ (205U<<16) | 8, // kAudioChannelLayoutTag_CICP_14
+ (206U<<16) | 12, // kAudioChannelLayoutTag_CICP_15
+ (207U<<16) | 10, // kAudioChannelLayoutTag_CICP_16
+ (208U<<16) | 12, // kAudioChannelLayoutTag_CICP_17
+ (209U<<16) | 14, // kAudioChannelLayoutTag_CICP_18
+ (210U<<16) | 12, // kAudioChannelLayoutTag_CICP_19
+ (211U<<16) | 14, // kAudioChannelLayoutTag_CICP_20
+ kAudioChannelLayoutTag_Unknown
+};
+
int ca_label_to_mp_speaker_id(AudioChannelLabel label)
{
for (int i = 0; speaker_map[i][1] >= 0; i++)
@@ -73,30 +187,48 @@ int ca_label_to_mp_speaker_id(AudioChannelLabel label)
return -1;
}
+AudioChannelLabel mp_speaker_id_to_ca_label(int speaker_id)
+{
+ for (int i = 0; speaker_map[i][1] >= 0; i++)
+ if (speaker_map[i][1] == speaker_id)
+ return speaker_map[i][0];
+ return -1; // kAudioChannelLabel_Unknown
+}
+
#if HAVE_COREAUDIO
-static void ca_log_layout(struct ao *ao, int l, AudioChannelLayout *layout)
+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]);
+ AudioChannelLayoutTag tag = layout->mChannelLayoutTag;
+ mp_msg(ao->log, l, "audio channel layout: tag: <%u>", tag);
+
+ if (tag == kAudioChannelLayoutTag_UseChannelDescriptions) {
+ AudioChannelDescription *descs = layout->mChannelDescriptions;
+ mp_msg(ao->log, l, ", descriptions <%u>\n",
+ (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>",
+ i,
+ (unsigned) d.mChannelLabel,
+ (unsigned) ca_label_to_mp_speaker_id(d.mChannelLabel),
+ (unsigned) d.mChannelFlags);
+ if (d.mChannelFlags != kAudioChannelFlags_AllOff) {
+ mp_msg(ao->log, l, ", coords: <%f, %f, %f>\n",
+ d.mCoordinates[0],
+ d.mCoordinates[1],
+ d.mCoordinates[2]);
+ } else {
+ mp_msg(ao->log, l, "\n");
+ }
+ }
+ } else if (tag == kAudioChannelLayoutTag_UseChannelBitmap) {
+ mp_msg(ao->log, l, ", bitmap <%u>\n", layout->mChannelBitmap);
+ } else {
+ mp_msg(ao->log, l, "\n");
}
}
@@ -117,7 +249,7 @@ static AudioChannelLayout *ca_layout_to_custom_layout(struct ao *ao,
kAudioFormatProperty_ChannelLayoutForBitmap,
sizeof(uint32_t), &l->mChannelBitmap, &psize);
CHECK_CA_ERROR("failed to convert channel bitmap to descriptions (info)");
- r = talloc_size(NULL, psize);
+ r = talloc_size(talloc_ctx, psize);
err = AudioFormatGetProperty(
kAudioFormatProperty_ChannelLayoutForBitmap,
sizeof(uint32_t), &l->mChannelBitmap, &psize, r);
@@ -127,7 +259,7 @@ static AudioChannelLayout *ca_layout_to_custom_layout(struct ao *ao,
err = AudioFormatGetPropertyInfo(
kAudioFormatProperty_ChannelLayoutForTag,
sizeof(AudioChannelLayoutTag), &l->mChannelLayoutTag, &psize);
- r = talloc_size(NULL, psize);
+ r = talloc_size(talloc_ctx, psize);
CHECK_CA_ERROR("failed to convert channel tag to descriptions (info)");
err = AudioFormatGetProperty(
kAudioFormatProperty_ChannelLayoutForTag,
@@ -135,14 +267,53 @@ static AudioChannelLayout *ca_layout_to_custom_layout(struct ao *ao,
CHECK_CA_ERROR("failed to convert channel tag to descriptions (get)");
}
- MP_VERBOSE(ao, "converted input channel layout:\n");
- ca_log_layout(ao, MSGL_V, l);
+ if (ao) {
+ MP_VERBOSE(ao, "converted input channel layout:\n");
+ ca_log_layout(ao, MSGL_V, l);
+ }
return r;
coreaudio_error:
return NULL;
}
+AudioChannelLayout *ca_find_standard_layout(void *talloc_ctx, AudioChannelLayout *l)
+{
+ if (l->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions)
+ return l;
+
+ AudioChannelLayout *s = talloc_size(talloc_ctx, sizeof(AudioChannelLayout));
+
+ for (int i = 0; ; ++i) {
+ if ((s->mChannelLayoutTag = std_layouts[i]) == kAudioChannelLayoutTag_Unknown) {
+ s = NULL;
+ break;
+ }
+
+ AudioChannelLayout *r = ca_layout_to_custom_layout(NULL, talloc_ctx, s);
+
+ if (!r)
+ goto mismatch;
+ if (l->mNumberChannelDescriptions != r->mNumberChannelDescriptions)
+ goto mismatch;
+
+ for (int i = 0; i < l->mNumberChannelDescriptions; ++i) {
+ AudioChannelDescription *ld = l->mChannelDescriptions + i;
+ AudioChannelDescription *rd = r->mChannelDescriptions + i;
+ if (ld->mChannelLabel == rd->mChannelLabel)
+ continue;
+ // XXX: we cannot handle channels with coordinates
+ goto mismatch;
+ }
+
+ break;
+
+mismatch:;
+ }
+
+ return s ? s : l;
+}
+
#define CHMAP(n, ...) &(struct mp_chmap) MP_CONCAT(MP_CHMAP, n) (__VA_ARGS__)
diff --git a/audio/out/ao_coreaudio_chmap.h b/audio/out/ao_coreaudio_chmap.h
index b6d160c546..0b21e8330c 100644
--- a/audio/out/ao_coreaudio_chmap.h
+++ b/audio/out/ao_coreaudio_chmap.h
@@ -18,15 +18,22 @@
#ifndef MPV_COREAUDIO_CHMAP_H
#define MPV_COREAUDIO_CHMAP_H
+#include "config.h"
#include <AudioToolbox/AudioToolbox.h>
-#include "config.h"
+#if HAVE_AVFOUNDATION || HAVE_AUDIOUNIT
+#undef HAVE_COREAUDIO
+#define HAVE_COREAUDIO 1
+#endif
struct mp_chmap;
int ca_label_to_mp_speaker_id(AudioChannelLabel label);
+AudioChannelLabel mp_speaker_id_to_ca_label(int speaker_id);
#if HAVE_COREAUDIO
+AudioChannelLayout *ca_find_standard_layout(void *talloc_ctx, AudioChannelLayout *l);
+void ca_log_layout(struct ao *ao, int l, AudioChannelLayout *layout);
bool ca_init_chmap(struct ao *ao, AudioDeviceID device);
void ca_get_active_chmap(struct ao *ao, AudioDeviceID device, int channel_count,
struct mp_chmap *out_map);
diff --git a/audio/out/ao_coreaudio_properties.h b/audio/out/ao_coreaudio_properties.h
index f29396883d..2c9c5657bb 100644
--- a/audio/out/ao_coreaudio_properties.h
+++ b/audio/out/ao_coreaudio_properties.h
@@ -23,6 +23,11 @@
#include "internal.h"
+#if HAVE_AVFOUNDATION || HAVE_AUDIOUNIT
+#undef HAVE_COREAUDIO
+#define HAVE_COREAUDIO 1
+#endif
+
// CoreAudio names are way too verbose
#define ca_sel AudioObjectPropertySelector
#define ca_scope AudioObjectPropertyScope
diff --git a/audio/out/ao_coreaudio_utils.c b/audio/out/ao_coreaudio_utils.c
index f730beca87..e74092a8d2 100644
--- a/audio/out/ao_coreaudio_utils.c
+++ b/audio/out/ao_coreaudio_utils.c
@@ -138,7 +138,8 @@ bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message)
{
if (code == noErr) return true;
- mp_msg(ao->log, level, "%s (%s/%d)\n", message, mp_tag_str(code), (int)code);
+ if (ao)
+ mp_msg(ao->log, level, "%s (%s/%d)\n", message, mp_tag_str(code), (int)code);
return false;
}
diff --git a/audio/out/ao_coreaudio_utils.h b/audio/out/ao_coreaudio_utils.h
index c6f1a5ec24..699ffde9a7 100644
--- a/audio/out/ao_coreaudio_utils.h
+++ b/audio/out/ao_coreaudio_utils.h
@@ -29,6 +29,11 @@
#include "internal.h"
#include "osdep/utils-mac.h"
+#if HAVE_AVFOUNDATION || HAVE_AUDIOUNIT
+#undef HAVE_COREAUDIO
+#define HAVE_COREAUDIO 1
+#endif
+
bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message);
#define CHECK_CA_ERROR_L(label, message) \