1 /*
2 * This file is part of mpv.
3 *
4 * mpv is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * mpv is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <CoreAudio/HostTime.h>
19
20 #include "config.h"
21 #include "ao.h"
22 #include "internal.h"
23 #include "audio/format.h"
24 #include "osdep/timer.h"
25 #include "options/m_option.h"
26 #include "common/msg.h"
27 #include "ao_coreaudio_chmap.h"
28 #include "ao_coreaudio_properties.h"
29 #include "ao_coreaudio_utils.h"
30
31 struct priv {
32 AudioDeviceID device;
33 AudioUnit audio_unit;
34
35 uint64_t hw_latency_us;
36
37 AudioStreamBasicDescription original_asbd;
38 AudioStreamID original_asbd_stream;
39
40 int change_physical_format;
41 };
42
ca_get_hardware_latency(struct ao * ao)43 static int64_t ca_get_hardware_latency(struct ao *ao) {
44 struct priv *p = ao->priv;
45
46 double audiounit_latency_sec = 0.0;
47 uint32_t size = sizeof(audiounit_latency_sec);
48 OSStatus err = AudioUnitGetProperty(
49 p->audio_unit,
50 kAudioUnitProperty_Latency,
51 kAudioUnitScope_Global,
52 0,
53 &audiounit_latency_sec,
54 &size);
55 CHECK_CA_ERROR("cannot get audio unit latency");
56
57 uint64_t audiounit_latency_us = audiounit_latency_sec * 1e6;
58 uint64_t device_latency_us = ca_get_device_latency_us(ao, p->device);
59
60 MP_VERBOSE(ao, "audiounit latency [us]: %lld\n", audiounit_latency_us);
61 MP_VERBOSE(ao, "device latency [us]: %lld\n", device_latency_us);
62
63 return audiounit_latency_us + device_latency_us;
64
65 coreaudio_error:
66 return 0;
67 }
68
render_cb_lpcm(void * ctx,AudioUnitRenderActionFlags * aflags,const AudioTimeStamp * ts,UInt32 bus,UInt32 frames,AudioBufferList * buffer_list)69 static OSStatus render_cb_lpcm(void *ctx, AudioUnitRenderActionFlags *aflags,
70 const AudioTimeStamp *ts, UInt32 bus,
71 UInt32 frames, AudioBufferList *buffer_list)
72 {
73 struct ao *ao = ctx;
74 struct priv *p = ao->priv;
75 void *planes[MP_NUM_CHANNELS] = {0};
76
77 for (int n = 0; n < ao->num_planes; n++)
78 planes[n] = buffer_list->mBuffers[n].mData;
79
80 int64_t end = mp_time_us();
81 end += p->hw_latency_us + ca_get_latency(ts) + ca_frames_to_us(ao, frames);
82 ao_read_data(ao, planes, frames, end);
83 return noErr;
84 }
85
get_volume(struct ao * ao,struct ao_control_vol * vol)86 static int get_volume(struct ao *ao, struct ao_control_vol *vol) {
87 struct priv *p = ao->priv;
88 float auvol;
89 OSStatus err =
90 AudioUnitGetParameter(p->audio_unit, kHALOutputParam_Volume,
91 kAudioUnitScope_Global, 0, &auvol);
92
93 CHECK_CA_ERROR("could not get HAL output volume");
94 vol->left = vol->right = auvol * 100.0;
95 return CONTROL_TRUE;
96 coreaudio_error:
97 return CONTROL_ERROR;
98 }
99
set_volume(struct ao * ao,struct ao_control_vol * vol)100 static int set_volume(struct ao *ao, struct ao_control_vol *vol) {
101 struct priv *p = ao->priv;
102 float auvol = (vol->left + vol->right) / 200.0;
103 OSStatus err =
104 AudioUnitSetParameter(p->audio_unit, kHALOutputParam_Volume,
105 kAudioUnitScope_Global, 0, auvol, 0);
106 CHECK_CA_ERROR("could not set HAL output volume");
107 return CONTROL_TRUE;
108 coreaudio_error:
109 return CONTROL_ERROR;
110 }
111
control(struct ao * ao,enum aocontrol cmd,void * arg)112 static int control(struct ao *ao, enum aocontrol cmd, void *arg)
113 {
114 switch (cmd) {
115 case AOCONTROL_GET_VOLUME:
116 return get_volume(ao, arg);
117 case AOCONTROL_SET_VOLUME:
118 return set_volume(ao, arg);
119 }
120 return CONTROL_UNKNOWN;
121 }
122
123 static bool init_audiounit(struct ao *ao, AudioStreamBasicDescription asbd);
124 static void init_physical_format(struct ao *ao);
125
reinit_device(struct ao * ao)126 static bool reinit_device(struct ao *ao) {
127 struct priv *p = ao->priv;
128
129 OSStatus err = ca_select_device(ao, ao->device, &p->device);
130 CHECK_CA_ERROR("failed to select device");
131
132 return true;
133
134 coreaudio_error:
135 return false;
136 }
137
init(struct ao * ao)138 static int init(struct ao *ao)
139 {
140 struct priv *p = ao->priv;
141
142 if (!af_fmt_is_pcm(ao->format) || (ao->init_flags & AO_INIT_EXCLUSIVE)) {
143 MP_VERBOSE(ao, "redirecting to coreaudio_exclusive\n");
144 ao->redirect = "coreaudio_exclusive";
145 return CONTROL_ERROR;
146 }
147
148 if (!reinit_device(ao))
149 goto coreaudio_error;
150
151 if (p->change_physical_format)
152 init_physical_format(ao);
153
154 if (!ca_init_chmap(ao, p->device))
155 goto coreaudio_error;
156
157 AudioStreamBasicDescription asbd;
158 ca_fill_asbd(ao, &asbd);
159
160 if (!init_audiounit(ao, asbd))
161 goto coreaudio_error;
162
163 return CONTROL_OK;
164
165 coreaudio_error:
166 return CONTROL_ERROR;
167 }
168
init_physical_format(struct ao * ao)169 static void init_physical_format(struct ao *ao)
170 {
171 struct priv *p = ao->priv;
172 OSErr err;
173
174 void *tmp = talloc_new(NULL);
175
176 AudioStreamBasicDescription asbd;
177 ca_fill_asbd(ao, &asbd);
178
179 AudioStreamID *streams;
180 size_t n_streams;
181
182 err = CA_GET_ARY_O(p->device, kAudioDevicePropertyStreams,
183 &streams, &n_streams);
184 CHECK_CA_ERROR("could not get number of streams");
185
186 talloc_steal(tmp, streams);
187
188 MP_VERBOSE(ao, "Found %zd substream(s).\n", n_streams);
189
190 for (int i = 0; i < n_streams; i++) {
191 AudioStreamRangedDescription *formats;
192 size_t n_formats;
193
194 MP_VERBOSE(ao, "Looking at formats in substream %d...\n", i);
195
196 err = CA_GET_ARY(streams[i], kAudioStreamPropertyAvailablePhysicalFormats,
197 &formats, &n_formats);
198
199 if (!CHECK_CA_WARN("could not get number of stream formats"))
200 continue; // try next one
201
202 talloc_steal(tmp, formats);
203
204 uint32_t direction;
205 err = CA_GET(streams[i], kAudioStreamPropertyDirection, &direction);
206 CHECK_CA_ERROR("could not get stream direction");
207 if (direction != 0) {
208 MP_VERBOSE(ao, "Not an output stream.\n");
209 continue;
210 }
211
212 AudioStreamBasicDescription best_asbd = {0};
213
214 for (int j = 0; j < n_formats; j++) {
215 AudioStreamBasicDescription *stream_asbd = &formats[j].mFormat;
216
217 ca_print_asbd(ao, "- ", stream_asbd);
218
219 if (!best_asbd.mFormatID || ca_asbd_is_better(&asbd, &best_asbd,
220 stream_asbd))
221 best_asbd = *stream_asbd;
222 }
223
224 if (best_asbd.mFormatID) {
225 p->original_asbd_stream = streams[i];
226 err = CA_GET(p->original_asbd_stream,
227 kAudioStreamPropertyPhysicalFormat,
228 &p->original_asbd);
229 CHECK_CA_WARN("could not get current physical stream format");
230
231 if (ca_asbd_equals(&p->original_asbd, &best_asbd)) {
232 MP_VERBOSE(ao, "Requested format already set, not changing.\n");
233 p->original_asbd.mFormatID = 0;
234 break;
235 }
236
237 if (!ca_change_physical_format_sync(ao, streams[i], best_asbd))
238 p->original_asbd = (AudioStreamBasicDescription){0};
239 break;
240 }
241 }
242
243 coreaudio_error:
244 talloc_free(tmp);
245 return;
246 }
247
init_audiounit(struct ao * ao,AudioStreamBasicDescription asbd)248 static bool init_audiounit(struct ao *ao, AudioStreamBasicDescription asbd)
249 {
250 OSStatus err;
251 uint32_t size;
252 struct priv *p = ao->priv;
253
254 AudioComponentDescription desc = (AudioComponentDescription) {
255 .componentType = kAudioUnitType_Output,
256 .componentSubType = (ao->device) ?
257 kAudioUnitSubType_HALOutput :
258 kAudioUnitSubType_DefaultOutput,
259 .componentManufacturer = kAudioUnitManufacturer_Apple,
260 .componentFlags = 0,
261 .componentFlagsMask = 0,
262 };
263
264 AudioComponent comp = AudioComponentFindNext(NULL, &desc);
265 if (comp == NULL) {
266 MP_ERR(ao, "unable to find audio component\n");
267 goto coreaudio_error;
268 }
269
270 err = AudioComponentInstanceNew(comp, &(p->audio_unit));
271 CHECK_CA_ERROR("unable to open audio component");
272
273 err = AudioUnitInitialize(p->audio_unit);
274 CHECK_CA_ERROR_L(coreaudio_error_component,
275 "unable to initialize audio unit");
276
277 size = sizeof(AudioStreamBasicDescription);
278 err = AudioUnitSetProperty(p->audio_unit,
279 kAudioUnitProperty_StreamFormat,
280 kAudioUnitScope_Input, 0, &asbd, size);
281
282 CHECK_CA_ERROR_L(coreaudio_error_audiounit,
283 "unable to set the input format on the audio unit");
284
285 err = AudioUnitSetProperty(p->audio_unit,
286 kAudioOutputUnitProperty_CurrentDevice,
287 kAudioUnitScope_Global, 0, &p->device,
288 sizeof(p->device));
289 CHECK_CA_ERROR_L(coreaudio_error_audiounit,
290 "can't link audio unit to selected device");
291
292 p->hw_latency_us = ca_get_hardware_latency(ao);
293
294 AURenderCallbackStruct render_cb = (AURenderCallbackStruct) {
295 .inputProc = render_cb_lpcm,
296 .inputProcRefCon = ao,
297 };
298
299 err = AudioUnitSetProperty(p->audio_unit,
300 kAudioUnitProperty_SetRenderCallback,
301 kAudioUnitScope_Input, 0, &render_cb,
302 sizeof(AURenderCallbackStruct));
303
304 CHECK_CA_ERROR_L(coreaudio_error_audiounit,
305 "unable to set render callback on audio unit");
306
307 return true;
308
309 coreaudio_error_audiounit:
310 AudioUnitUninitialize(p->audio_unit);
311 coreaudio_error_component:
312 AudioComponentInstanceDispose(p->audio_unit);
313 coreaudio_error:
314 return false;
315 }
316
stop(struct ao * ao)317 static void stop(struct ao *ao)
318 {
319 struct priv *p = ao->priv;
320 OSStatus err = AudioOutputUnitStop(p->audio_unit);
321 CHECK_CA_WARN("can't stop audio unit");
322 }
323
start(struct ao * ao)324 static void start(struct ao *ao)
325 {
326 struct priv *p = ao->priv;
327 OSStatus err = AudioOutputUnitStart(p->audio_unit);
328 CHECK_CA_WARN("can't start audio unit");
329 }
330
331
uninit(struct ao * ao)332 static void uninit(struct ao *ao)
333 {
334 struct priv *p = ao->priv;
335 AudioOutputUnitStop(p->audio_unit);
336 AudioUnitUninitialize(p->audio_unit);
337 AudioComponentInstanceDispose(p->audio_unit);
338
339 if (p->original_asbd.mFormatID) {
340 OSStatus err = CA_SET(p->original_asbd_stream,
341 kAudioStreamPropertyPhysicalFormat,
342 &p->original_asbd);
343 CHECK_CA_WARN("could not restore physical stream format");
344 }
345 }
346
hotplug_cb(AudioObjectID id,UInt32 naddr,const AudioObjectPropertyAddress addr[],void * ctx)347 static OSStatus hotplug_cb(AudioObjectID id, UInt32 naddr,
348 const AudioObjectPropertyAddress addr[],
349 void *ctx)
350 {
351 struct ao *ao = ctx;
352 MP_VERBOSE(ao, "Handling potential hotplug event...\n");
353 reinit_device(ao);
354 ao_hotplug_event(ao);
355 return noErr;
356 }
357
358 static uint32_t hotplug_properties[] = {
359 kAudioHardwarePropertyDevices,
360 kAudioHardwarePropertyDefaultOutputDevice
361 };
362
hotplug_init(struct ao * ao)363 static int hotplug_init(struct ao *ao)
364 {
365 if (!reinit_device(ao))
366 goto coreaudio_error;
367
368 OSStatus err = noErr;
369 for (int i = 0; i < MP_ARRAY_SIZE(hotplug_properties); i++) {
370 AudioObjectPropertyAddress addr = {
371 hotplug_properties[i],
372 kAudioObjectPropertyScopeGlobal,
373 kAudioObjectPropertyElementMaster
374 };
375 err = AudioObjectAddPropertyListener(
376 kAudioObjectSystemObject, &addr, hotplug_cb, (void *)ao);
377 if (err != noErr) {
378 char *c1 = mp_tag_str(hotplug_properties[i]);
379 char *c2 = mp_tag_str(err);
380 MP_ERR(ao, "failed to set device listener %s (%s)", c1, c2);
381 goto coreaudio_error;
382 }
383 }
384
385 return 0;
386
387 coreaudio_error:
388 return -1;
389 }
390
hotplug_uninit(struct ao * ao)391 static void hotplug_uninit(struct ao *ao)
392 {
393 OSStatus err = noErr;
394 for (int i = 0; i < MP_ARRAY_SIZE(hotplug_properties); i++) {
395 AudioObjectPropertyAddress addr = {
396 hotplug_properties[i],
397 kAudioObjectPropertyScopeGlobal,
398 kAudioObjectPropertyElementMaster
399 };
400 err = AudioObjectRemovePropertyListener(
401 kAudioObjectSystemObject, &addr, hotplug_cb, (void *)ao);
402 if (err != noErr) {
403 char *c1 = mp_tag_str(hotplug_properties[i]);
404 char *c2 = mp_tag_str(err);
405 MP_ERR(ao, "failed to set device listener %s (%s)", c1, c2);
406 }
407 }
408 }
409
410 #define OPT_BASE_STRUCT struct priv
411
412 const struct ao_driver audio_out_coreaudio = {
413 .description = "CoreAudio AudioUnit",
414 .name = "coreaudio",
415 .uninit = uninit,
416 .init = init,
417 .control = control,
418 .reset = stop,
419 .start = start,
420 .hotplug_init = hotplug_init,
421 .hotplug_uninit = hotplug_uninit,
422 .list_devs = ca_get_device_list,
423 .priv_size = sizeof(struct priv),
424 .options = (const struct m_option[]){
425 {"change-physical-format", OPT_FLAG(change_physical_format)},
426 {0}
427 },
428 .options_prefix = "coreaudio",
429 };
430