audiotoolbox.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  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. * @file
  23. * AudioToolbox output device
  24. * @author Thilo Borgmann <thilo.borgmann@mail.de>
  25. */
  26. #import <AudioToolbox/AudioToolbox.h>
  27. #include <pthread.h>
  28. #include "libavutil/opt.h"
  29. #include "libavformat/internal.h"
  30. #include "libavformat/mux.h"
  31. #include "libavutil/internal.h"
  32. #include "avdevice.h"
  33. typedef struct
  34. {
  35. AVClass *class;
  36. AudioQueueBufferRef buffer[2];
  37. pthread_mutex_t buffer_lock[2];
  38. int cur_buf;
  39. AudioQueueRef queue;
  40. int list_devices;
  41. int audio_device_index;
  42. } ATContext;
  43. static int check_status(AVFormatContext *avctx, OSStatus *status, const char *msg)
  44. {
  45. if (*status != noErr) {
  46. av_log(avctx, AV_LOG_ERROR, "Error: %s (%i)\n", msg, *status);
  47. return 1;
  48. } else {
  49. av_log(avctx, AV_LOG_DEBUG, " OK : %s\n", msg);
  50. return 0;
  51. }
  52. }
  53. static void queue_callback(void* atctx, AudioQueueRef inAQ,
  54. AudioQueueBufferRef inBuffer)
  55. {
  56. // unlock the buffer that has just been consumed
  57. ATContext *ctx = (ATContext*)atctx;
  58. for (int i = 0; i < 2; i++) {
  59. if (inBuffer == ctx->buffer[i]) {
  60. pthread_mutex_unlock(&ctx->buffer_lock[i]);
  61. }
  62. }
  63. }
  64. static av_cold int at_write_header(AVFormatContext *avctx)
  65. {
  66. ATContext *ctx = (ATContext*)avctx->priv_data;
  67. OSStatus err = noErr;
  68. CFStringRef device_UID = NULL;
  69. AudioDeviceID *devices;
  70. int num_devices;
  71. // get devices
  72. UInt32 data_size = 0;
  73. AudioObjectPropertyAddress prop;
  74. prop.mSelector = kAudioHardwarePropertyDevices;
  75. prop.mScope = kAudioObjectPropertyScopeGlobal;
  76. #if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 120000
  77. prop.mElement = kAudioObjectPropertyElementMain;
  78. #else
  79. prop.mElement = kAudioObjectPropertyElementMaster;
  80. #endif
  81. err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, NULL, &data_size);
  82. if (check_status(avctx, &err, "AudioObjectGetPropertyDataSize devices"))
  83. return AVERROR(EINVAL);
  84. num_devices = data_size / sizeof(AudioDeviceID);
  85. devices = (AudioDeviceID*)(av_malloc(data_size));
  86. err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &data_size, devices);
  87. if (check_status(avctx, &err, "AudioObjectGetPropertyData devices")) {
  88. av_freep(&devices);
  89. return AVERROR(EINVAL);
  90. }
  91. // list devices
  92. if (ctx->list_devices) {
  93. CFStringRef device_name = NULL;
  94. prop.mScope = kAudioDevicePropertyScopeInput;
  95. av_log(ctx, AV_LOG_INFO, "CoreAudio devices:\n");
  96. for(UInt32 i = 0; i < num_devices; ++i) {
  97. // UID
  98. data_size = sizeof(device_UID);
  99. prop.mSelector = kAudioDevicePropertyDeviceUID;
  100. err = AudioObjectGetPropertyData(devices[i], &prop, 0, NULL, &data_size, &device_UID);
  101. if (check_status(avctx, &err, "AudioObjectGetPropertyData UID"))
  102. continue;
  103. // name
  104. data_size = sizeof(device_name);
  105. prop.mSelector = kAudioDevicePropertyDeviceNameCFString;
  106. err = AudioObjectGetPropertyData(devices[i], &prop, 0, NULL, &data_size, &device_name);
  107. if (check_status(avctx, &err, "AudioObjecTGetPropertyData name"))
  108. continue;
  109. av_log(ctx, AV_LOG_INFO, "[%d] %30s, %s\n", i,
  110. CFStringGetCStringPtr(device_name, kCFStringEncodingMacRoman),
  111. CFStringGetCStringPtr(device_UID, kCFStringEncodingMacRoman));
  112. }
  113. }
  114. // get user-defined device UID or use default device
  115. // -audio_device_index overrides any URL given
  116. const char *stream_name = avctx->url;
  117. if (stream_name && ctx->audio_device_index == -1) {
  118. sscanf(stream_name, "%d", &ctx->audio_device_index);
  119. }
  120. if (ctx->audio_device_index >= 0) {
  121. // get UID of selected device
  122. data_size = sizeof(device_UID);
  123. prop.mSelector = kAudioDevicePropertyDeviceUID;
  124. err = AudioObjectGetPropertyData(devices[ctx->audio_device_index], &prop, 0, NULL, &data_size, &device_UID);
  125. if (check_status(avctx, &err, "AudioObjecTGetPropertyData UID")) {
  126. av_freep(&devices);
  127. return AVERROR(EINVAL);
  128. }
  129. } else {
  130. // use default device
  131. device_UID = NULL;
  132. }
  133. av_log(ctx, AV_LOG_DEBUG, "stream_name: %s\n", stream_name);
  134. av_log(ctx, AV_LOG_DEBUG, "audio_device_idnex: %i\n", ctx->audio_device_index);
  135. av_log(ctx, AV_LOG_DEBUG, "UID: %s\n", CFStringGetCStringPtr(device_UID, kCFStringEncodingMacRoman));
  136. // check input stream
  137. if (avctx->nb_streams != 1 || avctx->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) {
  138. av_log(ctx, AV_LOG_ERROR, "Only a single audio stream is supported.\n");
  139. return AVERROR(EINVAL);
  140. }
  141. av_freep(&devices);
  142. AVCodecParameters *codecpar = avctx->streams[0]->codecpar;
  143. // audio format
  144. AudioStreamBasicDescription device_format = {0};
  145. device_format.mSampleRate = codecpar->sample_rate;
  146. device_format.mFormatID = kAudioFormatLinearPCM;
  147. device_format.mFormatFlags |= (codecpar->format == AV_SAMPLE_FMT_FLT) ? kLinearPCMFormatFlagIsFloat : 0;
  148. device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_S8) ? kLinearPCMFormatFlagIsSignedInteger : 0;
  149. device_format.mFormatFlags |= (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE)) ? kLinearPCMFormatFlagIsSignedInteger : 0;
  150. device_format.mFormatFlags |= (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S24BE, AV_CODEC_ID_PCM_S24LE)) ? kLinearPCMFormatFlagIsSignedInteger : 0;
  151. device_format.mFormatFlags |= (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S32BE, AV_CODEC_ID_PCM_S32LE)) ? kLinearPCMFormatFlagIsSignedInteger : 0;
  152. device_format.mFormatFlags |= (av_sample_fmt_is_planar(codecpar->format)) ? kAudioFormatFlagIsNonInterleaved : 0;
  153. device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_F32BE) ? kAudioFormatFlagIsBigEndian : 0;
  154. device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_S16BE) ? kAudioFormatFlagIsBigEndian : 0;
  155. device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_S24BE) ? kAudioFormatFlagIsBigEndian : 0;
  156. device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_S32BE) ? kAudioFormatFlagIsBigEndian : 0;
  157. device_format.mChannelsPerFrame = codecpar->ch_layout.nb_channels;
  158. device_format.mBitsPerChannel = (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S24BE, AV_CODEC_ID_PCM_S24LE)) ? 24 : (av_get_bytes_per_sample(codecpar->format) << 3);
  159. device_format.mBytesPerFrame = (device_format.mBitsPerChannel >> 3) * device_format.mChannelsPerFrame;
  160. device_format.mFramesPerPacket = 1;
  161. device_format.mBytesPerPacket = device_format.mBytesPerFrame * device_format.mFramesPerPacket;
  162. device_format.mReserved = 0;
  163. av_log(ctx, AV_LOG_DEBUG, "device_format.mSampleRate = %i\n", codecpar->sample_rate);
  164. av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatID = %s\n", "kAudioFormatLinearPCM");
  165. av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->format == AV_SAMPLE_FMT_FLT) ? "kLinearPCMFormatFlagIsFloat" : "0");
  166. av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_S8) ? "kLinearPCMFormatFlagIsSignedInteger" : "0");
  167. av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S32BE, AV_CODEC_ID_PCM_S32LE)) ? "kLinearPCMFormatFlagIsSignedInteger" : "0");
  168. av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE)) ? "kLinearPCMFormatFlagIsSignedInteger" : "0");
  169. av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S24BE, AV_CODEC_ID_PCM_S24LE)) ? "kLinearPCMFormatFlagIsSignedInteger" : "0");
  170. av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (av_sample_fmt_is_planar(codecpar->format)) ? "kAudioFormatFlagIsNonInterleaved" : "0");
  171. av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_F32BE) ? "kAudioFormatFlagIsBigEndian" : "0");
  172. av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_S16BE) ? "kAudioFormatFlagIsBigEndian" : "0");
  173. av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_S24BE) ? "kAudioFormatFlagIsBigEndian" : "0");
  174. av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_S32BE) ? "kAudioFormatFlagIsBigEndian" : "0");
  175. av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags == %i\n", device_format.mFormatFlags);
  176. av_log(ctx, AV_LOG_DEBUG, "device_format.mChannelsPerFrame = %i\n", codecpar->ch_layout.nb_channels);
  177. av_log(ctx, AV_LOG_DEBUG, "device_format.mBitsPerChannel = %i\n", av_get_bytes_per_sample(codecpar->format) << 3);
  178. av_log(ctx, AV_LOG_DEBUG, "device_format.mBytesPerFrame = %i\n", (device_format.mBitsPerChannel >> 3) * codecpar->ch_layout.nb_channels);
  179. av_log(ctx, AV_LOG_DEBUG, "device_format.mBytesPerPacket = %i\n", device_format.mBytesPerFrame);
  180. av_log(ctx, AV_LOG_DEBUG, "device_format.mFramesPerPacket = %i\n", 1);
  181. av_log(ctx, AV_LOG_DEBUG, "device_format.mReserved = %i\n", 0);
  182. // create new output queue for the device
  183. err = AudioQueueNewOutput(&device_format, queue_callback, ctx,
  184. NULL, kCFRunLoopCommonModes,
  185. 0, &ctx->queue);
  186. if (check_status(avctx, &err, "AudioQueueNewOutput")) {
  187. if (err == kAudioFormatUnsupportedDataFormatError)
  188. av_log(ctx, AV_LOG_ERROR, "Unsupported output format.\n");
  189. return AVERROR(EINVAL);
  190. }
  191. // set user-defined device or leave untouched for default
  192. if (device_UID != NULL) {
  193. err = AudioQueueSetProperty(ctx->queue, kAudioQueueProperty_CurrentDevice, &device_UID, sizeof(device_UID));
  194. if (check_status(avctx, &err, "AudioQueueSetProperty output UID"))
  195. return AVERROR(EINVAL);
  196. }
  197. // start the queue
  198. err = AudioQueueStart(ctx->queue, NULL);
  199. if (check_status(avctx, &err, "AudioQueueStart"))
  200. return AVERROR(EINVAL);
  201. // init the mutexes for double-buffering
  202. pthread_mutex_init(&ctx->buffer_lock[0], NULL);
  203. pthread_mutex_init(&ctx->buffer_lock[1], NULL);
  204. return 0;
  205. }
  206. static int at_write_packet(AVFormatContext *avctx, AVPacket *pkt)
  207. {
  208. ATContext *ctx = (ATContext*)avctx->priv_data;
  209. OSStatus err = noErr;
  210. // use the other buffer
  211. ctx->cur_buf = !ctx->cur_buf;
  212. // lock for writing or wait for the buffer to be available
  213. // will be unlocked by queue callback
  214. pthread_mutex_lock(&ctx->buffer_lock[ctx->cur_buf]);
  215. // (re-)allocate the buffer if not existant or of different size
  216. if (!ctx->buffer[ctx->cur_buf] || ctx->buffer[ctx->cur_buf]->mAudioDataBytesCapacity != pkt->size) {
  217. err = AudioQueueAllocateBuffer(ctx->queue, pkt->size, &ctx->buffer[ctx->cur_buf]);
  218. if (check_status(avctx, &err, "AudioQueueAllocateBuffer")) {
  219. pthread_mutex_unlock(&ctx->buffer_lock[ctx->cur_buf]);
  220. return AVERROR(ENOMEM);
  221. }
  222. }
  223. AudioQueueBufferRef buf = ctx->buffer[ctx->cur_buf];
  224. // copy audio data into buffer and enqueue the buffer
  225. memcpy(buf->mAudioData, pkt->data, buf->mAudioDataBytesCapacity);
  226. buf->mAudioDataByteSize = buf->mAudioDataBytesCapacity;
  227. err = AudioQueueEnqueueBuffer(ctx->queue, buf, 0, NULL);
  228. if (check_status(avctx, &err, "AudioQueueEnqueueBuffer")) {
  229. pthread_mutex_unlock(&ctx->buffer_lock[ctx->cur_buf]);
  230. return AVERROR(EINVAL);
  231. }
  232. return 0;
  233. }
  234. static av_cold int at_write_trailer(AVFormatContext *avctx)
  235. {
  236. ATContext *ctx = (ATContext*)avctx->priv_data;
  237. OSStatus err = noErr;
  238. pthread_mutex_destroy(&ctx->buffer_lock[0]);
  239. pthread_mutex_destroy(&ctx->buffer_lock[1]);
  240. err = AudioQueueFlush(ctx->queue);
  241. check_status(avctx, &err, "AudioQueueFlush");
  242. err = AudioQueueDispose(ctx->queue, true);
  243. check_status(avctx, &err, "AudioQueueDispose");
  244. return 0;
  245. }
  246. static const AVOption options[] = {
  247. { "list_devices", "list available audio devices", offsetof(ATContext, list_devices), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
  248. { "audio_device_index", "select audio device by index (starts at 0)", offsetof(ATContext, audio_device_index), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
  249. { NULL },
  250. };
  251. static const AVClass at_class = {
  252. .class_name = "AudioToolbox",
  253. .item_name = av_default_item_name,
  254. .option = options,
  255. .version = LIBAVUTIL_VERSION_INT,
  256. .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT,
  257. };
  258. const FFOutputFormat ff_audiotoolbox_muxer = {
  259. .p.name = "audiotoolbox",
  260. .p.long_name = NULL_IF_CONFIG_SMALL("AudioToolbox output device"),
  261. .priv_data_size = sizeof(ATContext),
  262. .p.audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE),
  263. .p.video_codec = AV_CODEC_ID_NONE,
  264. .write_header = at_write_header,
  265. .write_packet = at_write_packet,
  266. .write_trailer = at_write_trailer,
  267. .p.flags = AVFMT_NOFILE,
  268. .p.priv_class = &at_class,
  269. };