UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
coreaudio.m
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2011-2023 CESNET, z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#ifdef HAVE_CONFIG_H
39#include "config.h"
40#include "config_unix.h"
41#endif // HAVE_CONFIG_H
42
43#include <AVFoundation/AVFoundation.h>
44#include <AudioUnit/AudioUnit.h>
45#include <Availability.h>
46#include <CoreAudio/AudioHardware.h>
47#include <pthread.h>
48#include <stdlib.h>
49#include <string.h>
50
51#include "audio/audio_capture.h"
53#include "audio/types.h"
54#include "audio/utils.h"
55#include "debug.h"
56#include "host.h"
57#include "lib_common.h"
58#include "utils/color_out.h"
59#include "utils/macos.h"
60#include "utils/macros.h"
61#include "utils/ring_buffer.h"
62
63#define MOD_NAME "[CoreAudio cap.] "
64
65enum {
66 CA_DIR = -1,
67};
68
70 AudioComponentInstance auHALComponentInstance;
72 char *tmp;
75 AudioBufferList *theBufferList;
76
77 pthread_mutex_t lock;
78 pthread_cond_t cv;
79 volatile int boss_waiting;
80 volatile int data_ready;
81};
82
83static OSStatus InputProc(void *inRefCon,
84 AudioUnitRenderActionFlags *ioActionFlags,
85 const AudioTimeStamp *inTimeStamp,
86 UInt32 inBusNumber,
87 UInt32 inNumberFrames,
88 AudioBufferList * ioData);
89static AudioBufferList *AllocateAudioBufferList(UInt32 numChannels, UInt32 size);
90static void DestroyAudioBufferList(AudioBufferList* list);
91
92// Convenience function to allocate our audio buffers
93static AudioBufferList *AllocateAudioBufferList(UInt32 numChannels, UInt32 size)
94{
95 AudioBufferList* list;
96 UInt32 i;
97
98 list = (AudioBufferList*)calloc(1, sizeof(AudioBufferList) + numChannels * sizeof(AudioBuffer));
99 if(list == NULL)
100 return NULL;
101
102 list->mNumberBuffers = numChannels;
103 for(i = 0; i < numChannels; ++i) {
104 list->mBuffers[i].mNumberChannels = 1;
105 list->mBuffers[i].mDataByteSize = size;
106 list->mBuffers[i].mData = malloc(size);
107 if(list->mBuffers[i].mData == NULL) {
109 return NULL;
110 }
111 }
112 return list;
113}
114
115// Convenience function to dispose of our audio buffers
116static void DestroyAudioBufferList(AudioBufferList* list)
117{
118 UInt32 i;
119
120 if(list) {
121 for(i = 0; i < list->mNumberBuffers; i++) {
122 if(list->mBuffers[i].mData)
123 free(list->mBuffers[i].mData);
124 }
125 free(list);
126 }
127}
128
129static OSStatus InputProc(void *inRefCon,
130 AudioUnitRenderActionFlags *ioActionFlags,
131 const AudioTimeStamp *inTimeStamp,
132 UInt32 inBusNumber,
133 UInt32 inNumberFrames,
134 AudioBufferList * ioData)
135{
136 UNUSED(ioData);
137 struct state_ca_capture * s = (struct state_ca_capture *) inRefCon;
138
139 OSStatus err =noErr;
140
141 err= AudioUnitRender(s->auHALComponentInstance, ioActionFlags, inTimeStamp, inBusNumber, //will be '1' for input data
142 inNumberFrames, //# of frames requested
143 s->theBufferList);
144
145 if(err == noErr) {
146 int i;
147 int len = inNumberFrames * s->audio_packet_size;
148 for(i = 0; i < s->frame.ch_count; ++i)
149 mux_channel(s->tmp, s->theBufferList->mBuffers[i].mData, s->frame.bps, len, s->frame.ch_count, i, 1.0);
150 uint32_t write_bytes = len * s->frame.ch_count;
151
152 pthread_mutex_lock(&s->lock);
153 ring_buffer_write(s->buffer, s->tmp, write_bytes);
154 s->data_ready = TRUE;
155 if(s->boss_waiting)
156 pthread_cond_signal(&s->cv);
157 pthread_mutex_unlock(&s->lock);
158 } else {
159 log_msg(LOG_LEVEL_ERROR, MOD_NAME "writing buffer caused error %s.\n", get_osstatus_str(err));
160 }
161
162 return err;
163}
164
165static void audio_cap_ca_probe(struct device_info **available_devices, int *count, void (**deleter)(void *))
166{
167 *deleter = free;
168 audio_ca_probe(available_devices, count, -1);
169}
170
171static void audio_cap_ca_help()
172{
173 struct device_info *available_devices;
174 int count;
175 void (*deleter)(void *) = NULL;
176 audio_cap_ca_probe(&available_devices, &count, &deleter);
177
178 for (int i = 0; i < count; ++i) {
179 color_printf("\t" TBOLD("coreaudio%-4s") ": %s\n", available_devices[i].dev, available_devices[i].name);
180 }
181 deleter ? deleter(available_devices) : free(available_devices);
182}
183
184#define CA_STRINGIFY(A) #A
185
186#define CHECK_OK(cmd, msg, action_failed) do { OSStatus ret = cmd; if (ret != noErr) {\
187 log_msg(strlen(CA_STRINGIFY(action_failed)) == 0 ? LOG_LEVEL_WARNING : LOG_LEVEL_ERROR, MOD_NAME "%s: %s\n", (msg), get_osstatus_str(ret));\
188 action_failed;\
189}\
190} while(0)
191#define NOOP
192
193#ifdef __MAC_10_14
194// http://anasambri.com/ios/accessing-camera-and-photos-in-ios.html
195static void (^cb)(BOOL) = ^void(BOOL granted) {
196 if (!granted) {
197 dispatch_async(dispatch_get_main_queue(), ^{
198 //show alert
199 });
200 }
201};
202#endif // defined __MAC_10_14
203
204
205
206static void * audio_cap_ca_init(struct module *parent, const char *cfg)
207{
208 UNUSED(parent);
209 if (strcmp(cfg, "help") == 0) {
210 printf("Core Audio capture usage:\n");
211 color_printf(TBOLD(TRED("\t-s coreaudio") "[:<index>|:<name>]") "\n");
212 color_printf("where\n\t" TBOLD("<name>") " - device name substring (case sensitive)\n\n");
213 printf("Available Core Audio capture devices:\n");
215 return INIT_NOERR;
216 }
217 OSStatus ret = noErr;
218 AudioComponentDescription desc;
219 AudioDeviceID device;
220 UInt32 size = sizeof device;
221
222 if (strlen(cfg) > 0) {
223 char *endptr = NULL;
224 device = strtol(cfg, &endptr, 0);
225 if (*endptr != '\0') {
226 device = audio_ca_get_device_by_name(cfg, CA_DIR);
227 if (device == UINT_MAX) {
228 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Wrong device index or unrecognized name \"%s\"!\n", cfg);
229 return NULL;
230 }
231 }
232 } else {
233 AudioObjectPropertyAddress propertyAddress;
234 propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
235 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
236 propertyAddress.mElement = kAudioObjectPropertyElementMain;
237 if ((ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &device)) != 0) {
238 log_msg(LOG_LEVEL_ERROR, "Error finding default input device: %s.\n", get_osstatus_str(ret));
239 return NULL;
240 }
241 }
242 char device_name[128];
243 audio_ca_get_device_name(device, sizeof device_name, device_name);
244 log_msg(LOG_LEVEL_INFO, MOD_NAME "Using device: %s\n", device_name);
245
246#ifdef __MAC_10_14
247 AVAuthorizationStatus authorization_status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
248 if (authorization_status == AVAuthorizationStatusRestricted ||
249 authorization_status == AVAuthorizationStatusDenied) {
250 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Application is not authorized to capture audio input!\n");
251 return NULL;
252 }
253 if (authorization_status == AVAuthorizationStatusNotDetermined) {
254 [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:cb];
255 }
256#endif // defined __MAC_10_14
257
258 double rate = 0.0;
259 size = sizeof(double);
260 AudioObjectPropertyAddress propertyAddress;
261 propertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
262 propertyAddress.mScope = kAudioDevicePropertyScopeInput;
263 propertyAddress.mElement = kAudioObjectPropertyElementMain;
264 ret = AudioObjectGetPropertyData(device, &propertyAddress, 0, NULL, &size, &rate);
265 if (ret != noErr || rate == 0.0) {
266 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to get sample rate: %s. Wrong device index?\n", get_osstatus_str(ret));
267 return NULL;
268 }
270 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Requested sample rate %u, got %lf!\n", audio_capture_sample_rate, rate);
271 }
272
273 struct state_ca_capture *s = (struct state_ca_capture *) calloc(1, sizeof(struct state_ca_capture));
274 pthread_mutex_init(&s->lock, NULL);
275 pthread_cond_init(&s->cv, NULL);
276 s->boss_waiting = FALSE;
277 s->data_ready = FALSE;
280
281 s->frame.sample_rate = rate;
283 int nonres_channel_size = s->frame.bps * s->frame.sample_rate;
284
285 s->theBufferList = AllocateAudioBufferList(s->frame.ch_count, nonres_channel_size);
286 s->tmp = (char *) malloc(nonres_channel_size * s->frame.ch_count);
287
289 s->frame.data = (char *) malloc(s->frame.max_size);
290
291 //There are several different types of Audio Units.
292 //Some audio units serve as Outputs, Mixers, or DSP
293 //units. See AUComponent.h for listing
294 desc.componentType = kAudioUnitType_Output;
295
296 //Every Component has a subType, which will give a clearer picture
297 //of what this components function will be.
298 //desc.componentSubType = kAudioUnitSubType_DefaultOutput;
299 desc.componentSubType = kAudioUnitSubType_HALOutput;
300
301 //all Audio Units in AUComponent.h must use
302 //"kAudioUnitManufacturer_Apple" as the Manufacturer
303 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
304 desc.componentFlags = 0;
305 desc.componentFlagsMask = 0;
306
307 bool failed = true;
308 do {
309 AudioComponent comp = AudioComponentFindNext(NULL, &desc);
310 if(!comp) {
311 fprintf(stderr, "Error finding AUHAL component.\n");
312 break;
313 }
314 CHECK_OK(AudioComponentInstanceNew(comp, &s->auHALComponentInstance),
315 "Error opening AUHAL component",
316 break);
317
318 //When using AudioUnitSetProperty the 4th parameter in the method
319 //refer to an AudioUnitElement. When using an AudioOutputUnit
320 //the input element will be '1' and the output element will be '0'.
321
322 UInt32 enableIO = 1;
323 CHECK_OK(AudioUnitSetProperty(s->auHALComponentInstance,
324 kAudioOutputUnitProperty_EnableIO,
325 kAudioUnitScope_Input,
326 1, // input element
327 &enableIO,
328 sizeof(enableIO)),
329 "Error enabling input on AUHAL", break);
330
331 enableIO = 0;
332 CHECK_OK(AudioUnitSetProperty(s->auHALComponentInstance,
333 kAudioOutputUnitProperty_EnableIO,
334 kAudioUnitScope_Output,
335 0, //output element
336 &enableIO,
337 sizeof(enableIO)),
338 "Error disabling output on AUHAL", break);
339
340 size=sizeof(device);
341 CHECK_OK(AudioUnitSetProperty(s->auHALComponentInstance,
342 kAudioOutputUnitProperty_CurrentDevice,
343 kAudioUnitScope_Global,
344 0,
345 &device,
346 sizeof(device)),
347 "Error setting device to AUHAL instance", break);
348
349 AudioStreamBasicDescription desc;
350
351 size = sizeof(desc);
352 CHECK_OK(AudioUnitGetProperty(s->auHALComponentInstance, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
353 1, &desc, &size), "Error getting default device properties", break);
354
355 desc.mChannelsPerFrame = s->frame.ch_count;
356 desc.mSampleRate = (double) s->frame.sample_rate;
357 desc.mFormatID = kAudioFormatLinearPCM;
358 desc.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved;
359 if (desc.mFormatID == kAudioFormatLinearPCM && s->frame.ch_count == 1)
360 desc.mFormatFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
361
362/*#if __BIG_ENDIAN__
363 desc.mFormatFlags |= kAudioFormatFlagIsBigEndian;
364#endif */
365 desc.mBitsPerChannel = s->frame.bps * 8;
366 desc.mBytesPerFrame = desc.mBitsPerChannel / 8;
367 desc.mFramesPerPacket = 1;
368 desc.mBytesPerPacket = desc.mBytesPerFrame;
369 s->audio_packet_size = desc.mBytesPerPacket;
370
371 CHECK_OK(AudioUnitSetProperty(s->auHALComponentInstance, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
372 1, &desc, sizeof(desc)), "Error setting device properties", break);
373
374 AURenderCallbackStruct input;
375 input.inputProc = InputProc;
376 input.inputProcRefCon = s;
377 CHECK_OK(AudioUnitSetProperty(s->auHALComponentInstance, kAudioOutputUnitProperty_SetInputCallback,
378 kAudioUnitScope_Global, 0, &input, sizeof(input)),
379 "Error setting input callback", break);
380 uint32_t numFrames = 128;
381 if (get_commandline_param("audio-cap-frames")) {
382 numFrames = atoi(get_commandline_param("audio-cap-frames"));
383 }
384 CHECK_OK(AudioUnitSetProperty(s->auHALComponentInstance, kAudioDevicePropertyBufferFrameSize,
385 kAudioUnitScope_Global, 0, &numFrames, sizeof(numFrames)),
386 "Error setting frames", NOOP);
387
388 CHECK_OK(AudioUnitInitialize(s->auHALComponentInstance), "Error initializing device", break);
389 CHECK_OK(AudioOutputUnitStart(s->auHALComponentInstance), "Error starting device", break);
390 failed = false;
391 } while(0);
392
393 if (!failed) {
394 return s;
395 }
396
397 // error accured
398 pthread_mutex_destroy(&s->lock);
399 pthread_cond_destroy(&s->cv);
401 free(s->frame.data);
403 free(s->tmp);
404 free(s);
405 return NULL;
406}
407
409{
410 struct state_ca_capture *s = (struct state_ca_capture *) state;
411 int ret = FALSE;
412
413 pthread_mutex_lock(&s->lock);
415 if(!ret) {
416 s->boss_waiting = TRUE;
417 while(!s->data_ready) {
418 pthread_cond_wait(&s->cv, &s->lock);
419 }
420 s->boss_waiting = FALSE;
422 s->data_ready = FALSE;
423 }
424 pthread_mutex_unlock(&s->lock);
425
426 s->frame.data_len = ret;
427
428 return &s->frame;
429}
430
431static void audio_cap_ca_done(void *state)
432{
433 struct state_ca_capture *s = (struct state_ca_capture *) state;
434
435 if(!s)
436 return;
437
438 pthread_mutex_destroy(&s->lock);
439 pthread_cond_destroy(&s->cv);
441 free(s->frame.data);
443 free(s->tmp);
444
445 free(s);
446}
447
454
456
#define UNUSED(x)
void ** state
struct video_desc desc
int void size_t * len
int void(** deleter)(void *))
This file contains definition of some common audio types.
#define AUDIO_CAPTURE_ABI_VERSION
int color_printf(const char *format,...)
#define TRED(x)
Definition color_out.h:75
#define TBOLD(x)
Definition color_out.h:70
#define TRUE
#define FALSE
void audio_ca_probe(struct device_info **available_devices, int *count, int dir)
AudioDeviceID audio_ca_get_device_by_name(const char *name, int dir)
void audio_ca_get_device_name(AudioDeviceID dev_id, size_t namebuf_len, char *namebuf)
#define CHECK_OK(cmd, msg, action_failed)
Definition coreaudio.m:186
static const struct audio_capture_info acap_coreaudio_info
Definition coreaudio.m:448
static void audio_cap_ca_help()
Definition coreaudio.m:171
static void DestroyAudioBufferList(AudioBufferList *list)
Definition coreaudio.m:116
static void audio_cap_ca_probe(struct device_info **available_devices, int *count, void(**deleter)(void *))
Definition coreaudio.m:165
static AudioBufferList * AllocateAudioBufferList(UInt32 numChannels, UInt32 size)
Definition coreaudio.m:93
static void audio_cap_ca_done(void *state)
Definition coreaudio.m:431
static void * audio_cap_ca_init(struct module *parent, const char *cfg)
Definition coreaudio.m:206
#define NOOP
Definition coreaudio.m:191
static struct audio_frame * audio_cap_ca_read(void *state)
Definition coreaudio.m:408
#define MOD_NAME
Definition coreaudio.m:63
@ CA_DIR
Definition coreaudio.m:66
static OSStatus InputProc(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
Definition coreaudio.m:129
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
int count
Definition hd-rum.c:44
unsigned int audio_capture_bps
Definition host.cpp:164
const char * get_commandline_param(const char *key)
Definition host.cpp:894
unsigned int audio_capture_sample_rate
Definition host.cpp:165
unsigned int audio_capture_channels
Definition host.cpp:163
#define DEFAULT_AUDIO_CAPTURE_CHANNELS
Definition host.h:89
#define INIT_NOERR
Definition host.h:73
@ LIBRARY_CLASS_AUDIO_CAPTURE
Definition lib_common.h:76
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
#define kAudioObjectPropertyElementMain
Definition macos.h:50
const char * get_osstatus_str(OSStatus err)
Definition macos.m:106
void ring_buffer_destroy(struct ring_buffer *ring)
struct ring_buffer * ring_buffer_init(int size)
int ring_buffer_read(struct ring_buffer *ring, char *out, int max_len)
void ring_buffer_write(struct ring_buffer *ring, const char *in, int len)
const char * parent
Definition settings.cpp:198
const char * name
Definition settings.cpp:193
struct used to comunicate with audio capture/playback device
Definition types.h:99
int sample_rate
Definition types.h:101
char * data
Definition types.h:102
int ch_count
Definition types.h:104
int data_len
Definition types.h:103
int bps
Definition types.h:100
int max_size
Definition types.h:105
char dev[1024]
Definition types.h:383
volatile int boss_waiting
Definition coreaudio.m:79
pthread_cond_t cv
Definition coreaudio.m:78
struct ring_buffer * buffer
Definition coreaudio.m:73
volatile int data_ready
Definition coreaudio.m:80
struct audio_frame frame
Definition coreaudio.m:71
pthread_mutex_t lock
Definition coreaudio.m:77
AudioBufferList * theBufferList
Definition coreaudio.m:75
AudioComponentInstance auHALComponentInstance
Definition coreaudio.m:70
void mux_channel(char *out, const char *in, int bps, int in_len, int out_stream_channels, int pos_in_stream, double scale)
Definition utils.cpp:371