audiotoolbox.m 14 KB

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