mirror of https://github.com/mpv-player/mpv
ao_avfoundation: initial avfoundation ao support
This commit is contained in:
parent
7ab1080749
commit
1ed8607292
|
@ -138,11 +138,21 @@ Available audio output drivers are:
|
||||||
passthrough (even if the device reports it as supported). Use with
|
passthrough (even if the device reports it as supported). Use with
|
||||||
extreme care.
|
extreme care.
|
||||||
|
|
||||||
|
|
||||||
``coreaudio_exclusive`` (macOS only)
|
``coreaudio_exclusive`` (macOS only)
|
||||||
Native macOS audio output driver using direct device access and
|
Native macOS audio output driver using direct device access and
|
||||||
exclusive mode (bypasses the sound server).
|
exclusive mode (bypasses the sound server).
|
||||||
|
|
||||||
|
``avfoundation`` (macOS only)
|
||||||
|
Native macOS audio output driver using ``AVSampleBufferAudioRenderer``
|
||||||
|
in AVFoundation, which supports `spatial audio
|
||||||
|
<https://support.apple.com/en-us/HT211775>`_.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
Turning on spatial audio may hang the playback
|
||||||
|
if mpv is not started out of the bundle,
|
||||||
|
though playback with spatial audio off always works.
|
||||||
|
|
||||||
``openal``
|
``openal``
|
||||||
OpenAL audio output driver.
|
OpenAL audio output driver.
|
||||||
|
|
||||||
|
|
|
@ -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_audiounit;
|
||||||
extern const struct ao_driver audio_out_coreaudio;
|
extern const struct ao_driver audio_out_coreaudio;
|
||||||
extern const struct ao_driver audio_out_coreaudio_exclusive;
|
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_rsound;
|
||||||
extern const struct ao_driver audio_out_pipewire;
|
extern const struct ao_driver audio_out_pipewire;
|
||||||
extern const struct ao_driver audio_out_sndio;
|
extern const struct ao_driver audio_out_sndio;
|
||||||
|
@ -65,6 +66,9 @@ static const struct ao_driver * const audio_out_drivers[] = {
|
||||||
#if HAVE_COREAUDIO
|
#if HAVE_COREAUDIO
|
||||||
&audio_out_coreaudio,
|
&audio_out_coreaudio,
|
||||||
#endif
|
#endif
|
||||||
|
#if HAVE_AVFOUNDATION
|
||||||
|
&audio_out_avfoundation,
|
||||||
|
#endif
|
||||||
#if HAVE_PIPEWIRE
|
#if HAVE_PIPEWIRE
|
||||||
&audio_out_pipewire,
|
&audio_out_pipewire,
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -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),
|
||||||
|
};
|
|
@ -22,6 +22,7 @@
|
||||||
#include "ao_coreaudio_utils.h"
|
#include "ao_coreaudio_utils.h"
|
||||||
|
|
||||||
#include "ao_coreaudio_chmap.h"
|
#include "ao_coreaudio_chmap.h"
|
||||||
|
#include <CoreAudioTypes/CoreAudioTypes.h>
|
||||||
|
|
||||||
static const int speaker_map[][2] = {
|
static const int speaker_map[][2] = {
|
||||||
{ kAudioChannelLabel_Left, MP_SPEAKER_ID_FL },
|
{ kAudioChannelLabel_Left, MP_SPEAKER_ID_FL },
|
||||||
|
@ -65,6 +66,119 @@ static const int speaker_map[][2] = {
|
||||||
{ 0, -1 },
|
{ 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)
|
int ca_label_to_mp_speaker_id(AudioChannelLabel label)
|
||||||
{
|
{
|
||||||
for (int i = 0; speaker_map[i][1] >= 0; i++)
|
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;
|
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
|
#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))
|
if (!mp_msg_test(ao->log, l))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AudioChannelDescription *descs = layout->mChannelDescriptions;
|
AudioChannelLayoutTag tag = layout->mChannelLayoutTag;
|
||||||
|
mp_msg(ao->log, l, "audio channel layout: tag: <%u>", tag);
|
||||||
|
|
||||||
mp_msg(ao->log, l, "layout: tag: <%u>, bitmap: <%u>, "
|
if (tag == kAudioChannelLayoutTag_UseChannelDescriptions) {
|
||||||
"descriptions <%u>\n",
|
AudioChannelDescription *descs = layout->mChannelDescriptions;
|
||||||
(unsigned) layout->mChannelLayoutTag,
|
mp_msg(ao->log, l, ", descriptions <%u>\n",
|
||||||
(unsigned) layout->mChannelBitmap,
|
(unsigned) layout->mNumberChannelDescriptions);
|
||||||
(unsigned) layout->mNumberChannelDescriptions);
|
|
||||||
|
|
||||||
for (int i = 0; i < layout->mNumberChannelDescriptions; i++) {
|
for (int i = 0; i < layout->mNumberChannelDescriptions; i++) {
|
||||||
AudioChannelDescription d = descs[i];
|
AudioChannelDescription d = descs[i];
|
||||||
mp_msg(ao->log, l, " - description %d: label <%u, %u>, "
|
mp_msg(ao->log, l, " - description %d: label <%u, %u>, flags: <%u>",
|
||||||
" flags: <%u>, coords: <%f, %f, %f>\n", i,
|
i,
|
||||||
(unsigned) d.mChannelLabel,
|
(unsigned) d.mChannelLabel,
|
||||||
(unsigned) ca_label_to_mp_speaker_id(d.mChannelLabel),
|
(unsigned) ca_label_to_mp_speaker_id(d.mChannelLabel),
|
||||||
(unsigned) d.mChannelFlags,
|
(unsigned) d.mChannelFlags);
|
||||||
d.mCoordinates[0],
|
if (d.mChannelFlags != kAudioChannelFlags_AllOff) {
|
||||||
d.mCoordinates[1],
|
mp_msg(ao->log, l, ", coords: <%f, %f, %f>\n",
|
||||||
d.mCoordinates[2]);
|
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,
|
kAudioFormatProperty_ChannelLayoutForBitmap,
|
||||||
sizeof(uint32_t), &l->mChannelBitmap, &psize);
|
sizeof(uint32_t), &l->mChannelBitmap, &psize);
|
||||||
CHECK_CA_ERROR("failed to convert channel bitmap to descriptions (info)");
|
CHECK_CA_ERROR("failed to convert channel bitmap to descriptions (info)");
|
||||||
r = talloc_size(NULL, psize);
|
r = talloc_size(talloc_ctx, psize);
|
||||||
err = AudioFormatGetProperty(
|
err = AudioFormatGetProperty(
|
||||||
kAudioFormatProperty_ChannelLayoutForBitmap,
|
kAudioFormatProperty_ChannelLayoutForBitmap,
|
||||||
sizeof(uint32_t), &l->mChannelBitmap, &psize, r);
|
sizeof(uint32_t), &l->mChannelBitmap, &psize, r);
|
||||||
|
@ -127,7 +259,7 @@ static AudioChannelLayout *ca_layout_to_custom_layout(struct ao *ao,
|
||||||
err = AudioFormatGetPropertyInfo(
|
err = AudioFormatGetPropertyInfo(
|
||||||
kAudioFormatProperty_ChannelLayoutForTag,
|
kAudioFormatProperty_ChannelLayoutForTag,
|
||||||
sizeof(AudioChannelLayoutTag), &l->mChannelLayoutTag, &psize);
|
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)");
|
CHECK_CA_ERROR("failed to convert channel tag to descriptions (info)");
|
||||||
err = AudioFormatGetProperty(
|
err = AudioFormatGetProperty(
|
||||||
kAudioFormatProperty_ChannelLayoutForTag,
|
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)");
|
CHECK_CA_ERROR("failed to convert channel tag to descriptions (get)");
|
||||||
}
|
}
|
||||||
|
|
||||||
MP_VERBOSE(ao, "converted input channel layout:\n");
|
if (ao) {
|
||||||
ca_log_layout(ao, MSGL_V, l);
|
MP_VERBOSE(ao, "converted input channel layout:\n");
|
||||||
|
ca_log_layout(ao, MSGL_V, l);
|
||||||
|
}
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
coreaudio_error:
|
coreaudio_error:
|
||||||
return NULL;
|
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__)
|
#define CHMAP(n, ...) &(struct mp_chmap) MP_CONCAT(MP_CHMAP, n) (__VA_ARGS__)
|
||||||
|
|
||||||
|
|
|
@ -18,15 +18,22 @@
|
||||||
#ifndef MPV_COREAUDIO_CHMAP_H
|
#ifndef MPV_COREAUDIO_CHMAP_H
|
||||||
#define MPV_COREAUDIO_CHMAP_H
|
#define MPV_COREAUDIO_CHMAP_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
#include <AudioToolbox/AudioToolbox.h>
|
#include <AudioToolbox/AudioToolbox.h>
|
||||||
|
|
||||||
#include "config.h"
|
#if HAVE_AVFOUNDATION || HAVE_AUDIOUNIT
|
||||||
|
#undef HAVE_COREAUDIO
|
||||||
|
#define HAVE_COREAUDIO 1
|
||||||
|
#endif
|
||||||
|
|
||||||
struct mp_chmap;
|
struct mp_chmap;
|
||||||
|
|
||||||
int ca_label_to_mp_speaker_id(AudioChannelLabel label);
|
int ca_label_to_mp_speaker_id(AudioChannelLabel label);
|
||||||
|
AudioChannelLabel mp_speaker_id_to_ca_label(int speaker_id);
|
||||||
|
|
||||||
#if HAVE_COREAUDIO
|
#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);
|
bool ca_init_chmap(struct ao *ao, AudioDeviceID device);
|
||||||
void ca_get_active_chmap(struct ao *ao, AudioDeviceID device, int channel_count,
|
void ca_get_active_chmap(struct ao *ao, AudioDeviceID device, int channel_count,
|
||||||
struct mp_chmap *out_map);
|
struct mp_chmap *out_map);
|
||||||
|
|
|
@ -23,6 +23,11 @@
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
|
#if HAVE_AVFOUNDATION || HAVE_AUDIOUNIT
|
||||||
|
#undef HAVE_COREAUDIO
|
||||||
|
#define HAVE_COREAUDIO 1
|
||||||
|
#endif
|
||||||
|
|
||||||
// CoreAudio names are way too verbose
|
// CoreAudio names are way too verbose
|
||||||
#define ca_sel AudioObjectPropertySelector
|
#define ca_sel AudioObjectPropertySelector
|
||||||
#define ca_scope AudioObjectPropertyScope
|
#define ca_scope AudioObjectPropertyScope
|
||||||
|
|
|
@ -138,7 +138,8 @@ bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message)
|
||||||
{
|
{
|
||||||
if (code == noErr) return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,11 @@
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "osdep/utils-mac.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);
|
bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message);
|
||||||
|
|
||||||
#define CHECK_CA_ERROR_L(label, message) \
|
#define CHECK_CA_ERROR_L(label, message) \
|
||||||
|
|
13
meson.build
13
meson.build
|
@ -799,6 +799,14 @@ if features['audiounit']
|
||||||
sources += files('audio/out/ao_audiounit.m')
|
sources += files('audio/out/ao_audiounit.m')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
avfoundation = dependency('appleframeworks', modules: ['CoreMedia', 'AVFoundation'],
|
||||||
|
required: get_option('avfoundation'))
|
||||||
|
features += {'avfoundation': avfoundation.found()}
|
||||||
|
if features['avfoundation']
|
||||||
|
dependencies += avfoundation
|
||||||
|
sources += files('audio/out/ao_avfoundation.m')
|
||||||
|
endif
|
||||||
|
|
||||||
coreaudio = dependency('appleframeworks', modules: ['CoreFoundation', 'CoreAudio',
|
coreaudio = dependency('appleframeworks', modules: ['CoreFoundation', 'CoreAudio',
|
||||||
'AudioUnit', 'AudioToolbox'], required: get_option('coreaudio'))
|
'AudioUnit', 'AudioToolbox'], required: get_option('coreaudio'))
|
||||||
features += {'coreaudio': coreaudio.found()}
|
features += {'coreaudio': coreaudio.found()}
|
||||||
|
@ -809,9 +817,10 @@ if features['coreaudio']
|
||||||
'audio/out/ao_coreaudio_properties.c')
|
'audio/out/ao_coreaudio_properties.c')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if features['audiounit'] or features['coreaudio']
|
if features['audiounit'] or features['coreaudio'] or features['avfoundation']
|
||||||
sources += files('audio/out/ao_coreaudio_chmap.c',
|
sources += files('audio/out/ao_coreaudio_chmap.c',
|
||||||
'audio/out/ao_coreaudio_utils.c')
|
'audio/out/ao_coreaudio_utils.c',
|
||||||
|
'audio/out/ao_coreaudio_properties.c')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
jack_opt = get_option('jack').require(
|
jack_opt = get_option('jack').require(
|
||||||
|
|
|
@ -43,6 +43,7 @@ option('zlib', type: 'feature', value: 'auto', description: 'zlib')
|
||||||
option('alsa', type: 'feature', value: 'auto', description: 'ALSA audio output')
|
option('alsa', type: 'feature', value: 'auto', description: 'ALSA audio output')
|
||||||
option('audiounit', type: 'feature', value: 'auto', description: 'AudioUnit output (iOS)')
|
option('audiounit', type: 'feature', value: 'auto', description: 'AudioUnit output (iOS)')
|
||||||
option('coreaudio', type: 'feature', value: 'auto', description: 'CoreAudio audio output')
|
option('coreaudio', type: 'feature', value: 'auto', description: 'CoreAudio audio output')
|
||||||
|
option('avfoundation', type: 'feature', value: 'auto', description: 'AVFoundation audio output')
|
||||||
option('jack', type: 'feature', value: 'auto', description: 'JACK audio output')
|
option('jack', type: 'feature', value: 'auto', description: 'JACK audio output')
|
||||||
option('openal', type: 'feature', value: 'disabled', description: 'OpenAL audio output')
|
option('openal', type: 'feature', value: 'disabled', description: 'OpenAL audio output')
|
||||||
option('opensles', type: 'feature', value: 'auto', description: 'OpenSL ES audio output')
|
option('opensles', type: 'feature', value: 'auto', description: 'OpenSL ES audio output')
|
||||||
|
|
Loading…
Reference in New Issue