audiotoolbox.m 14 KB

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