1 /*
2 * AudioToolbox output device
3 * Copyright (c) 2020 Thilo Borgmann <thilo.borgmann@mail.de>
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * @file
24 * AudioToolbox output device
25 * @author Thilo Borgmann <thilo.borgmann@mail.de>
26 */
27
28 #import <AudioToolbox/AudioToolbox.h>
29 #include <pthread.h>
30
37
39 {
41
46
49
51
53 {
56 return 1;
57 } else {
59 return 0;
60 }
61 }
62
64 AudioQueueBufferRef inBuffer)
65 {
66 // unlock the buffer that has just been consumed
68 for (
int i = 0;
i < 2;
i++) {
69 if (inBuffer ==
ctx->buffer[
i]) {
71 }
72 }
73 }
74
76 {
78 OSStatus err = noErr;
79 CFStringRef device_UID =
NULL;
80 AudioDeviceID *devices;
81 int num_devices;
82
83
84 // get devices
85 UInt32 data_size = 0;
86 AudioObjectPropertyAddress prop;
87 prop.mSelector = kAudioHardwarePropertyDevices;
88 prop.mScope = kAudioObjectPropertyScopeGlobal;
89 #if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 120000
90 prop.mElement = kAudioObjectPropertyElementMain;
91 #else
92 prop.mElement = kAudioObjectPropertyElementMaster;
93 #endif
94 err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0,
NULL, &data_size);
95 if (
check_status(avctx, &err,
"AudioObjectGetPropertyDataSize devices"))
97
98 num_devices = data_size / sizeof(AudioDeviceID);
99
100 devices = (AudioDeviceID*)(
av_malloc(data_size));
101 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0,
NULL, &data_size, devices);
102 if (
check_status(avctx, &err,
"AudioObjectGetPropertyData devices")) {
105 }
106
107 // list devices
108 if (
ctx->list_devices) {
109 CFStringRef device_name =
NULL;
110 prop.mScope = kAudioDevicePropertyScopeInput;
111
113 for(UInt32
i = 0;
i < num_devices; ++
i) {
114 // UID
115 data_size = sizeof(device_UID);
116 prop.mSelector = kAudioDevicePropertyDeviceUID;
117 err = AudioObjectGetPropertyData(devices[
i], &prop, 0,
NULL, &data_size, &device_UID);
118 if (
check_status(avctx, &err,
"AudioObjectGetPropertyData UID"))
119 continue;
120
121 // name
122 data_size = sizeof(device_name);
123 prop.mSelector = kAudioDevicePropertyDeviceNameCFString;
124 err = AudioObjectGetPropertyData(devices[
i], &prop, 0,
NULL, &data_size, &device_name);
125 if (
check_status(avctx, &err,
"AudioObjecTGetPropertyData name"))
126 continue;
127
129 CFStringGetCStringPtr(device_name, kCFStringEncodingMacRoman),
130 CFStringGetCStringPtr(device_UID, kCFStringEncodingMacRoman));
131 }
132 }
133
134 // get user-defined device UID or use default device
135 // -audio_device_index overrides any URL given
136 const char *stream_name = avctx->
url;
137 if (stream_name &&
ctx->audio_device_index == -1) {
138 sscanf(stream_name,
"%d", &
ctx->audio_device_index);
139 }
140
141 if (
ctx->audio_device_index >= 0) {
142 // get UID of selected device
143 data_size = sizeof(device_UID);
144 prop.mSelector = kAudioDevicePropertyDeviceUID;
145 err = AudioObjectGetPropertyData(devices[
ctx->audio_device_index], &prop, 0,
NULL, &data_size, &device_UID);
146 if (
check_status(avctx, &err,
"AudioObjecTGetPropertyData UID")) {
149 }
150 } else {
151 // use default device
153 }
154
157 av_log(
ctx,
AV_LOG_DEBUG,
"UID: %s\n", CFStringGetCStringPtr(device_UID, kCFStringEncodingMacRoman));
158
159 // check input stream
163 }
164
167
168 // audio format
169 AudioStreamBasicDescription device_format = {0};
171 device_format.mFormatID = kAudioFormatLinearPCM;
184 device_format.mBytesPerFrame = (device_format.mBitsPerChannel >> 3) * device_format.mChannelsPerFrame;
185 device_format.mFramesPerPacket = 1;
186 device_format.mBytesPerPacket = device_format.mBytesPerFrame * device_format.mFramesPerPacket;
187 device_format.mReserved = 0;
188
208
209 // create new output queue for the device
211 NULL, kCFRunLoopCommonModes,
214 if (err == kAudioFormatUnsupportedDataFormatError)
217 }
218
219 // set user-defined device or leave untouched for default
220 if (device_UID !=
NULL) {
221 err = AudioQueueSetProperty(
ctx->queue, kAudioQueueProperty_CurrentDevice, &device_UID,
sizeof(device_UID));
222 if (
check_status(avctx, &err,
"AudioQueueSetProperty output UID"))
224 }
225
226 // start the queue
227 err = AudioQueueStart(
ctx->queue,
NULL);
230
231 // init the mutexes for double-buffering
234
235 return 0;
236 }
237
239 {
241 OSStatus err = noErr;
242
243 // use the other buffer
244 ctx->cur_buf = !
ctx->cur_buf;
245
246 // lock for writing or wait for the buffer to be available
247 // will be unlocked by queue callback
249
250 // (re-)allocate the buffer if not existent or of different size
251 if (!
ctx->buffer[
ctx->cur_buf] ||
ctx->buffer[
ctx->cur_buf]->mAudioDataBytesCapacity !=
pkt->
size) {
252 err = AudioQueueAllocateBuffer(
ctx->queue,
pkt->
size, &
ctx->buffer[
ctx->cur_buf]);
253 if (
check_status(avctx, &err,
"AudioQueueAllocateBuffer")) {
256 }
257 }
258
259 AudioQueueBufferRef buf =
ctx->buffer[ctx->
cur_buf];
260
261 // copy audio data into buffer and enqueue the buffer
262 memcpy(buf->mAudioData,
pkt->
data, buf->mAudioDataBytesCapacity);
263 buf->mAudioDataByteSize = buf->mAudioDataBytesCapacity;
264 err = AudioQueueEnqueueBuffer(
ctx->queue, buf, 0,
NULL);
265 if (
check_status(avctx, &err,
"AudioQueueEnqueueBuffer")) {
268 }
269
270 return 0;
271 }
272
274 {
276 OSStatus err = noErr;
277
280
281 err = AudioQueueFlush(
ctx->queue);
283 err = AudioQueueDispose(
ctx->queue,
true);
285
286 return 0;
287 }
288
293 };
294
301 };
302
304 .
p.
name =
"audiotoolbox",
314 };