[FFmpeg-devel] [PATCH 6/6] Add AudioToolbox audio input device.
toots at rastageeks.org
toots at rastageeks.org
Tue Mar 22 15:39:57 EET 2022
From: Romain Beauxis <toots at rastageeks.org>
diff --git a/configure b/configure
index a7953ffc16..37f9f7b80a 100755
--- a/configure
+++ b/configure
@@ -203,6 +203,7 @@ External library support:
--disable-avfoundation disable Apple AVFoundation framework [autodetect]
--enable-avisynth enable reading of AviSynth script files [no]
--disable-bzlib disable bzlib [autodetect]
+ --disable-coremedia disable Apple CoreMedia framework [autodetect]
--disable-coreimage disable Apple CoreImage framework [autodetect]
--enable-chromaprint enable audio fingerprinting with chromaprint [no]
--enable-frei0r enable frei0r video filtering [no]
@@ -1751,6 +1752,7 @@ EXTERNAL_AUTODETECT_LIBRARY_LIST="
appkit
avfoundation
bzlib
+ coremedia
coreimage
iconv
libxcb
@@ -3488,6 +3490,8 @@ alsa_outdev_deps="alsa"
avfoundation_indev_deps="avfoundation corevideo coremedia pthreads"
avfoundation_indev_suggest="coregraphics applicationservices"
avfoundation_indev_extralibs="-framework Foundation"
+audiotoolbox_indev_deps="coremedia audiotoolbox"
+audiotoolbox_indev_extralibs="-framework CoreMedia -framework AudioToolbox"
audiotoolbox_outdev_deps="audiotoolbox pthreads"
audiotoolbox_outdev_extralibs="-framework AudioToolbox -framework CoreAudio"
bktr_indev_deps_any="dev_bktr_ioctl_bt848_h machine_ioctl_bt848_h dev_video_bktr_ioctl_bt848_h dev_ic_bt8xx_h"
@@ -6330,6 +6334,7 @@ check_lib camera2ndk "stdbool.h stdint.h camera/NdkCameraManager.h" ACameraManag
enabled appkit && check_apple_framework AppKit
enabled audiotoolbox && check_apple_framework AudioToolbox
enabled avfoundation && check_apple_framework AVFoundation
+enabled coremedia && check_apple_framework CoreMedia
enabled coreimage && check_apple_framework CoreImage
enabled metal && check_apple_framework Metal
enabled videotoolbox && check_apple_framework VideoToolbox
diff --git a/doc/indevs.texi b/doc/indevs.texi
index 858c0fa4e4..8d57a26a5f 100644
--- a/doc/indevs.texi
+++ b/doc/indevs.texi
@@ -103,6 +103,41 @@ Set the maximum number of frames to buffer. Default is 5.
@end table
+ at section AudioToolbox
+
+AudioToolbox input device.
+
+Allows native input from CoreAudio devices on OSX.
+
+ at subsection Options
+
+AudioToolbox supports the following options:
+
+ at table @option
+
+ at item channels
+Set the number of channels. Default is device's default.
+
+ at item frames_queue_length
+Maximum of buffers in the input queue
+
+ at item buffer_frame_size
+Buffer frame size, gouverning internal latency
+
+ at item big_endian
+Return big endian samples
+
+ at item sample_format
+Sample format
+
+ at end table
+
+ at subsection Examples
+
+ at itemize
+
+ at end itemize
+
@section avfoundation
AVFoundation input device.
diff --git a/libavdevice/Makefile b/libavdevice/Makefile
index 99fea7133a..78d4168521 100644
--- a/libavdevice/Makefile
+++ b/libavdevice/Makefile
@@ -15,6 +15,7 @@ OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o
OBJS-$(CONFIG_ALSA_INDEV) += alsa_dec.o alsa.o timefilter.o
OBJS-$(CONFIG_ALSA_OUTDEV) += alsa_enc.o alsa.o
OBJS-$(CONFIG_ANDROID_CAMERA_INDEV) += android_camera.o
+OBJS-$(CONFIG_AUDIOTOOLBOX_INDEV) += audiotoolbox_dec.o
OBJS-$(CONFIG_AUDIOTOOLBOX_OUTDEV) += audiotoolbox.o
OBJS-$(CONFIG_AVFOUNDATION_INDEV) += avfoundation.o
OBJS-$(CONFIG_BKTR_INDEV) += bktr.o
diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c
index 22323a0a44..fbecdbb0b2 100644
--- a/libavdevice/alldevices.c
+++ b/libavdevice/alldevices.c
@@ -26,6 +26,7 @@ extern const AVInputFormat ff_alsa_demuxer;
extern const AVOutputFormat ff_alsa_muxer;
extern const AVInputFormat ff_android_camera_demuxer;
extern const AVOutputFormat ff_audiotoolbox_muxer;
+extern const AVInputFormat ff_audiotoolbox_demuxer;
extern const AVInputFormat ff_avfoundation_demuxer;
extern const AVInputFormat ff_bktr_demuxer;
extern const AVOutputFormat ff_caca_muxer;
diff --git a/libavdevice/audiotoolbox.m b/libavdevice/audiotoolbox.m
index 0cb97b5e46..3216f14607 100644
--- a/libavdevice/audiotoolbox.m
+++ b/libavdevice/audiotoolbox.m
@@ -84,7 +84,7 @@ static av_cold int at_write_header(AVFormatContext *avctx)
AudioObjectPropertyAddress prop;
prop.mSelector = kAudioHardwarePropertyDevices;
prop.mScope = kAudioObjectPropertyScopeGlobal;
- prop.mElement = kAudioObjectPropertyElementMaster;
+ prop.mElement = 0;
err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, NULL, &data_size);
if (check_status(avctx, &err, "AudioObjectGetPropertyDataSize devices"))
return AVERROR(EINVAL);
@@ -173,7 +173,7 @@ static av_cold int at_write_header(AVFormatContext *avctx)
device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_S16BE) ? kAudioFormatFlagIsBigEndian : 0;
device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_S24BE) ? kAudioFormatFlagIsBigEndian : 0;
device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_S32BE) ? kAudioFormatFlagIsBigEndian : 0;
- device_format.mChannelsPerFrame = codecpar->channels;
+ device_format.mChannelsPerFrame = codecpar->ch_layout.nb_channels;
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);
device_format.mBytesPerFrame = (device_format.mBitsPerChannel >> 3) * device_format.mChannelsPerFrame;
device_format.mFramesPerPacket = 1;
@@ -193,9 +193,9 @@ static av_cold int at_write_header(AVFormatContext *avctx)
av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_S24BE) ? "kAudioFormatFlagIsBigEndian" : "0");
av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_S32BE) ? "kAudioFormatFlagIsBigEndian" : "0");
av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags == %i\n", device_format.mFormatFlags);
- av_log(ctx, AV_LOG_DEBUG, "device_format.mChannelsPerFrame = %i\n", codecpar->channels);
+ av_log(ctx, AV_LOG_DEBUG, "device_format.mChannelsPerFrame = %i\n", codecpar->ch_layout.nb_channels);
av_log(ctx, AV_LOG_DEBUG, "device_format.mBitsPerChannel = %i\n", av_get_bytes_per_sample(codecpar->format) << 3);
- av_log(ctx, AV_LOG_DEBUG, "device_format.mBytesPerFrame = %i\n", (device_format.mBitsPerChannel >> 3) * codecpar->channels);
+ av_log(ctx, AV_LOG_DEBUG, "device_format.mBytesPerFrame = %i\n", (device_format.mBitsPerChannel >> 3) * codecpar->ch_layout.nb_channels);
av_log(ctx, AV_LOG_DEBUG, "device_format.mBytesPerPacket = %i\n", device_format.mBytesPerFrame);
av_log(ctx, AV_LOG_DEBUG, "device_format.mFramesPerPacket = %i\n", 1);
av_log(ctx, AV_LOG_DEBUG, "device_format.mReserved = %i\n", 0);
diff --git a/libavdevice/audiotoolbox_dec.m b/libavdevice/audiotoolbox_dec.m
new file mode 100644
index 0000000000..ae80c0e54a
--- /dev/null
+++ b/libavdevice/audiotoolbox_dec.m
@@ -0,0 +1,530 @@
+/*
+ * AudioToolbox input device
+ * Copyright (c) 2022 Romain Beauxis <toots at rastageeks.org>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * AudioToolbox input device
+ * @author Romain Beauxis <toots at rastageeks.org>
+ */
+
+#import <AudioToolbox/AudioToolbox.h>
+#import <CoreMedia/CoreMedia.h>
+
+#include "libavutil/channel_layout.h"
+#include "libavformat/internal.h"
+#include "avdevice.h"
+
+typedef struct {
+ void *data;
+ int size;
+} buffer_t;
+
+typedef struct {
+ AVClass *class;
+ CMSimpleQueueRef frames_queue;
+ AudioUnit audio_unit;
+ AudioStreamBasicDescription record_format;
+ uint64_t position;
+ int frames_queue_length;
+ int buffer_frame_size;
+ int stream_index;
+ int big_endian;
+ enum AVSampleFormat sample_format;
+ int channels;
+} ATContext;
+
+static int check_status(void *ctx, OSStatus status, const char *msg) {
+ if (status == noErr) {
+ av_log(ctx, AV_LOG_DEBUG, "OK: %s\n", msg);
+ return 0;
+ }
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
+ av_log(ctx, AV_LOG_ERROR, "Error: %s (%s)\n", msg, [[error localizedDescription] UTF8String]);
+ [pool release];
+ return 1;
+}
+
+static OSStatus input_callback(void *priv,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData) {
+ ATContext *ctx = (ATContext *)priv;
+ OSStatus err;
+
+ AudioBuffer audio_buffer;
+
+ audio_buffer.mNumberChannels = ctx->channels;
+ audio_buffer.mDataByteSize = inNumberFrames * ctx->record_format.mBytesPerFrame;
+
+ audio_buffer.mData = av_malloc(audio_buffer.mDataByteSize);
+ memset(audio_buffer.mData, 0, audio_buffer.mDataByteSize);
+
+ AudioBufferList bufferList;
+ bufferList.mNumberBuffers = 1;
+ bufferList.mBuffers[0] = audio_buffer;
+
+ err = AudioUnitRender(ctx->audio_unit,
+ ioActionFlags,
+ inTimeStamp,
+ inBusNumber,
+ inNumberFrames,
+ &bufferList);
+ if (check_status(ctx, err, "AudioUnitRender")) {
+ av_freep(&audio_buffer.mData);
+ return err;
+ }
+
+ buffer_t *buffer = av_malloc(sizeof(buffer_t));
+ buffer->data = audio_buffer.mData;
+ buffer->size = audio_buffer.mDataByteSize;
+ err = CMSimpleQueueEnqueue(ctx->frames_queue, buffer);
+
+ if (err != noErr) {
+ av_log(ctx, AV_LOG_DEBUG, "Could not enqueue audio frame!\n");
+ return err;
+ }
+
+ return noErr;
+}
+
+static av_cold int audiotoolbox_read_header(AVFormatContext *avctx) {
+ ATContext *ctx = (ATContext*)avctx->priv_data;
+ OSStatus err = noErr;
+ AudioChannelLayout *channel_layout = NULL;
+ AudioDeviceID device = kAudioObjectUnknown;
+ AudioObjectPropertyAddress prop;
+ UInt32 data_size;
+
+ enum AVCodecID codec_id = av_get_pcm_codec(ctx->sample_format, ctx->big_endian);
+
+ if (codec_id == AV_CODEC_ID_NONE) {
+ av_log(ctx, AV_LOG_ERROR, "Error: invalid sample format!\n");
+ return AVERROR(EINVAL);
+ }
+
+ prop.mScope = kAudioObjectPropertyScopeGlobal;
+ prop.mElement = 0;
+
+ if (!strcmp(avctx->url, "default")) {
+ prop.mSelector = kAudioHardwarePropertyDefaultInputDevice;
+ data_size = sizeof(AudioDeviceID);
+ err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &data_size, &device);
+ if (check_status(avctx, err, "AudioObjectGetPropertyData DefaultInputDevice"))
+ goto fail;
+ } else {
+ prop.mSelector = kAudioHardwarePropertyDeviceForUID;
+ CFStringRef deviceUID = CFStringCreateWithCStringNoCopy(NULL, avctx->url, kCFStringEncodingUTF8, kCFAllocatorNull);
+ AudioValueTranslation value;
+ value.mInputData = &deviceUID;
+ value.mInputDataSize = sizeof(deviceUID);;
+ value.mOutputData = &device;
+ value.mOutputDataSize = sizeof(device);
+ data_size = sizeof(value);
+
+ err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &data_size, &value);
+ CFRelease(deviceUID);
+
+ if (check_status(avctx, err, "AudioObjectGetPropertyData DeviceForUID"))
+ goto fail;
+ }
+
+ Float64 sample_rate;
+ prop.mSelector = kAudioDevicePropertyNominalSampleRate;
+ prop.mScope = kAudioObjectPropertyScopeInput;
+ data_size = sizeof(sample_rate);
+ err = AudioObjectGetPropertyData(device, &prop, 0, NULL, &data_size, &sample_rate);
+ if (check_status(avctx, err, "AudioObjectGetPropertyData SampleRate"))
+ goto fail;
+
+ if (!ctx->channels) {
+ prop.mSelector = kAudioDevicePropertyPreferredChannelLayout;
+ prop.mScope = kAudioObjectPropertyScopeInput;
+ UInt32 channel_layout_size;
+
+ err = AudioObjectGetPropertyDataSize(device, &prop, 0, NULL, &channel_layout_size);
+ if (check_status(avctx, err, "AudioObjectGetPropertyDataSize PreferredChannelLayout"))
+ goto fail;
+
+ channel_layout = av_malloc(channel_layout_size);
+ err = AudioObjectGetPropertyData(device, &prop, 0, NULL, &channel_layout_size, channel_layout);
+ if (check_status(avctx, err, "AudioObjectGetPropertyData PreferredChannelLayout"))
+ goto fail;
+
+ data_size = sizeof(ctx->channels);
+ err = AudioFormatGetProperty(kAudioFormatProperty_NumberOfChannelsForLayout, channel_layout_size, channel_layout, &data_size, &ctx->channels);
+ if (check_status(avctx, err, "AudioFormatGetProperty NumberOfChannelsForLayout"))
+ goto fail;
+ }
+
+ ctx->record_format.mFormatID = kAudioFormatLinearPCM;
+ ctx->record_format.mChannelsPerFrame = ctx->channels;
+ ctx->record_format.mFormatFlags = kAudioFormatFlagIsPacked;
+ ctx->record_format.mBitsPerChannel = av_get_bytes_per_sample(ctx->sample_format) << 3;
+
+ if (ctx->big_endian)
+ ctx->record_format.mFormatFlags |= kAudioFormatFlagIsBigEndian;
+
+ switch (ctx->sample_format) {
+ case AV_SAMPLE_FMT_S16:
+ case AV_SAMPLE_FMT_S32:
+ ctx->record_format.mFormatFlags |= kAudioFormatFlagIsSignedInteger;
+ break;
+ case AV_SAMPLE_FMT_FLT:
+ ctx->record_format.mFormatFlags |= kAudioFormatFlagIsFloat;
+ break;
+ default:
+ av_log(ctx, AV_LOG_ERROR, "Error: invalid sample format!\n");
+ goto fail;
+ }
+
+ av_log(ctx, AV_LOG_DEBUG, "Audio Input: %s\n", avctx->url);
+ av_log(ctx, AV_LOG_DEBUG, "samplerate: %d\n", (int)sample_rate);
+ av_log(ctx, AV_LOG_DEBUG, "channels: %d\n", ctx->channels);
+ av_log(ctx, AV_LOG_DEBUG, "Input format: %s\n", avcodec_get_name(codec_id));
+
+ data_size = sizeof(ctx->record_format);
+ err = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &data_size, &ctx->record_format);
+ if (check_status(avctx, err, "AudioFormatGetProperty FormatInfo"))
+ goto fail;
+
+ AudioComponentDescription desc;
+ AudioComponent comp;
+
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = kAudioUnitSubType_HALOutput;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+
+ comp = AudioComponentFindNext(NULL, &desc);
+ if (comp == NULL) {
+ av_log(ctx, AV_LOG_ERROR, "Error: AudioComponentFindNext\n");
+ goto fail;
+ }
+
+ err = AudioComponentInstanceNew(comp, &ctx->audio_unit);
+ if (check_status(avctx, err, "AudioComponentInstanceNew"))
+ goto fail;
+
+ UInt32 enableIO = 1;
+ err = AudioUnitSetProperty(ctx->audio_unit,
+ kAudioOutputUnitProperty_EnableIO,
+ kAudioUnitScope_Input,
+ 1,
+ &enableIO,
+ sizeof(enableIO));
+ if (check_status(avctx, err, "AudioUnitSetProperty EnableIO"))
+ goto fail;
+
+ enableIO = 0;
+ err = AudioUnitSetProperty(ctx->audio_unit,
+ kAudioOutputUnitProperty_EnableIO,
+ kAudioUnitScope_Output,
+ 0,
+ &enableIO,
+ sizeof(enableIO));
+ if (check_status(avctx, err, "AudioUnitSetProperty EnableIO"))
+ goto fail;
+
+ err = AudioUnitSetProperty(ctx->audio_unit,
+ kAudioOutputUnitProperty_CurrentDevice,
+ kAudioUnitScope_Global,
+ 0,
+ &device,
+ sizeof(device));
+ if (check_status(avctx, err, "AudioUnitSetProperty CurrentDevice"))
+ goto fail;
+
+ err = AudioUnitSetProperty(ctx->audio_unit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input,
+ 0,
+ &ctx->record_format,
+ sizeof(ctx->record_format));
+ if (check_status(avctx, err, "AudioUnitSetProperty StreamFormat"))
+ goto fail;
+
+ err = AudioUnitSetProperty(ctx->audio_unit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Output,
+ 1,
+ &ctx->record_format,
+ sizeof(ctx->record_format));
+ if (check_status(avctx, err, "AudioUnitSetProperty StreamFormat"))
+ goto fail;
+
+ err = AudioUnitSetProperty(ctx->audio_unit,
+ kAudioDevicePropertyBufferFrameSize,
+ kAudioUnitScope_Global,
+ 0,
+ &ctx->buffer_frame_size,
+ sizeof(ctx->buffer_frame_size));
+ if (check_status(avctx, err, "AudioUnitSetProperty BufferFrameSize"))
+ goto fail;
+
+ AURenderCallbackStruct callback = {0};
+ callback.inputProc = input_callback;
+ callback.inputProcRefCon = ctx;
+ err = AudioUnitSetProperty(ctx->audio_unit,
+ kAudioOutputUnitProperty_SetInputCallback,
+ kAudioUnitScope_Global,
+ 0,
+ &callback,
+ sizeof(callback));
+ if (check_status(avctx, err, "AudioUnitSetProperty SetInputCallback"))
+ goto fail;
+
+ err = AudioUnitInitialize(ctx->audio_unit);
+ if (check_status(avctx, err, "AudioUnitInitialize"))
+ goto fail;
+
+ err = CMSimpleQueueCreate(kCFAllocatorDefault, ctx->frames_queue_length, &ctx->frames_queue);
+ if (check_status(avctx, err, "CMSimpleQueueCreate"))
+ goto fail;
+
+ CFRetain(ctx->frames_queue);
+
+ err = AudioUnitInitialize(ctx->audio_unit);
+ if (check_status(avctx, err, "AudioUnitInitialize"))
+ goto fail;
+
+ err = AudioOutputUnitStart(ctx->audio_unit);
+ if (check_status(avctx, err, "AudioOutputUnitStart"))
+ goto fail;
+
+ AVStream* stream = avformat_new_stream(avctx, NULL);
+ stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ stream->codecpar->sample_rate = sample_rate;
+ stream->codecpar->channels = ctx->channels;
+ stream->codecpar->channel_layout = av_get_default_channel_layout(stream->codecpar->channels);
+ stream->codecpar->codec_id = codec_id;
+
+ avpriv_set_pts_info(stream, 64, 1, sample_rate);
+
+ ctx->stream_index = stream->index;
+ ctx->position = 0;
+
+ av_freep(&channel_layout);
+ return 0;
+
+fail:
+ av_freep(&channel_layout);
+ return AVERROR(EINVAL);
+}
+
+static int audiotoolbox_read_packet(AVFormatContext *avctx, AVPacket *pkt) {
+ ATContext *ctx = (ATContext*)avctx->priv_data;
+
+ if (CMSimpleQueueGetCount(ctx->frames_queue) < 1)
+ return AVERROR(EAGAIN);
+
+ buffer_t *buffer = (buffer_t *)CMSimpleQueueDequeue(ctx->frames_queue);
+
+ int status = av_packet_from_data(pkt, buffer->data, buffer->size);
+ if (status < 0) {
+ av_freep(&buffer->data);
+ av_freep(&buffer);
+ return status;
+ }
+
+ pkt->stream_index = ctx->stream_index;
+ pkt->flags |= AV_PKT_FLAG_KEY;
+ pkt->pts = pkt->dts = ctx->position;
+
+ ctx->position += pkt->size / (ctx->channels * av_get_bytes_per_sample(ctx->sample_format));
+
+ av_freep(&buffer);
+ return 0;
+}
+
+static av_cold int audiotoolbox_close(AVFormatContext *avctx) {
+ ATContext *ctx = (ATContext*)avctx->priv_data;
+
+ if (ctx->audio_unit) {
+ AudioOutputUnitStop(ctx->audio_unit);
+ AudioComponentInstanceDispose(ctx->audio_unit);
+ ctx->audio_unit = NULL;
+ }
+
+ if (ctx->frames_queue) {
+ buffer_t *buffer = (buffer_t *)CMSimpleQueueDequeue(ctx->frames_queue);
+
+ while (buffer) {
+ av_freep(&buffer->data);
+ av_freep(&buffer);
+ buffer = (buffer_t *)CMSimpleQueueDequeue(ctx->frames_queue);
+ }
+
+ CFRelease(ctx->frames_queue);
+ ctx->frames_queue = NULL;
+ }
+
+ return 0;
+}
+
+static int audiotoolbox_get_device_list(AVFormatContext *avctx, AVDeviceInfoList *device_list)
+{
+ OSStatus err = noErr;
+ CFStringRef device_UID = NULL;
+ CFStringRef device_name = NULL;
+ AudioDeviceID *devices = NULL;
+ AVDeviceInfo *avdevice_info = NULL;
+ int num_devices, ret;
+ UInt32 i;
+
+ avdevice_info = av_mallocz(sizeof(AVDeviceInfo));
+
+ if (!avdevice_info) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ avdevice_info->device_name = av_strdup("default");
+ avdevice_info->device_description = av_strdup("Default audio input device");
+ if (!avdevice_info->device_name || !avdevice_info->device_description) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ avdevice_info->media_types = av_malloc_array(1, sizeof(enum AVMediaType));
+ if (avdevice_info->media_types) {
+ avdevice_info->nb_media_types = 1;
+ avdevice_info->media_types[0] = AVMEDIA_TYPE_AUDIO;
+ }
+
+ if ((ret = av_dynarray_add_nofree(&device_list->devices,
+ &device_list->nb_devices, avdevice_info)) < 0)
+ goto fail;
+
+ // get devices
+ UInt32 data_size = 0;
+ AudioObjectPropertyAddress prop;
+ prop.mSelector = kAudioHardwarePropertyDevices;
+ prop.mScope = kAudioObjectPropertyScopeGlobal;
+ prop.mElement = 0;
+ err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, NULL, &data_size);
+ if (check_status(avctx, err, "AudioObjectGetPropertyDataSize devices"))
+ return AVERROR(EINVAL);
+
+ num_devices = data_size / sizeof(AudioDeviceID);
+ devices = av_malloc(data_size);
+
+ err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &data_size, devices);
+ if (check_status(avctx, err, "AudioObjectGetPropertyData devices")) {
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+
+ for(i = 0; i < num_devices; ++i) {
+ prop.mSelector = kAudioDevicePropertyStreams;
+ prop.mScope = kAudioDevicePropertyScopeInput;
+ data_size = 0;
+
+ err = AudioObjectGetPropertyDataSize(devices[i], &prop, 0, NULL, &data_size);
+ if (check_status(avctx, err, "AudioObjectGetPropertyData Streams"))
+ continue;
+
+ UInt32 streamCount = data_size / sizeof(AudioStreamID);
+
+ if (streamCount <= 0)
+ continue;
+
+ // UID
+ data_size = sizeof(device_UID);
+ prop.mSelector = kAudioDevicePropertyDeviceUID;
+ err = AudioObjectGetPropertyData(devices[i], &prop, 0, NULL, &data_size, &device_UID);
+ if (check_status(avctx, err, "AudioObjectGetPropertyData UID"))
+ continue;
+
+ // name
+ data_size = sizeof(device_name);
+ prop.mSelector = kAudioDevicePropertyDeviceNameCFString;
+ err = AudioObjectGetPropertyData(devices[i], &prop, 0, NULL, &data_size, &device_name);
+ if (check_status(avctx, err, "AudioObjectGetPropertyData name"))
+ continue;
+
+ avdevice_info = av_mallocz(sizeof(AVDeviceInfo));
+ if (!avdevice_info) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ avdevice_info->device_name = av_strdup(CFStringGetCStringPtr(device_UID, kCFStringEncodingUTF8));
+ avdevice_info->device_description = av_strdup(CFStringGetCStringPtr(device_name, kCFStringEncodingUTF8));
+ if (!avdevice_info->device_name || !avdevice_info->device_description) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ avdevice_info->media_types = av_malloc_array(1, sizeof(enum AVMediaType));
+ if (avdevice_info->media_types) {
+ avdevice_info->nb_media_types = 1;
+ avdevice_info->media_types[0] = AVMEDIA_TYPE_AUDIO;
+ }
+
+ if ((ret = av_dynarray_add_nofree(&device_list->devices,
+ &device_list->nb_devices, avdevice_info)) < 0)
+ goto fail;
+ }
+
+ av_freep(&devices);
+ return 0;
+
+fail:
+ av_freep(&devices);
+ if (avdevice_info) {
+ av_freep(&avdevice_info->device_name);
+ av_freep(&avdevice_info->device_description);
+ av_freep(&avdevice_info);
+ }
+
+ return ret;
+}
+
+static const AVOption options[] = {
+ { "channels", "number of audio channels", offsetof(ATContext, channels), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { "frames_queue_length", "maximum of buffers in the input queue", offsetof(ATContext, frames_queue_length), AV_OPT_TYPE_INT, {.i64=10}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { "buffer_frame_size", "buffer frame size, gouverning internal latency", offsetof(ATContext, buffer_frame_size), AV_OPT_TYPE_INT, {.i64=1024}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { "big_endian", "return big endian samples", offsetof(ATContext, big_endian), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
+ { "sample_format", "sample format", offsetof(ATContext, sample_format), AV_OPT_TYPE_INT, {.i64=AV_SAMPLE_FMT_S16}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { NULL },
+};
+
+static const AVClass audiotoolbox_class = {
+ .class_name = "AudioToolbox",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+ .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT,
+};
+
+const AVInputFormat ff_audiotoolbox_demuxer = {
+ .name = "audiotoolbox",
+ .long_name = NULL_IF_CONFIG_SMALL("AudioToolbox input device"),
+ .priv_data_size = sizeof(ATContext),
+ .read_header = audiotoolbox_read_header,
+ .read_packet = audiotoolbox_read_packet,
+ .read_close = audiotoolbox_close,
+ .get_device_list = audiotoolbox_get_device_list,
+ .flags = AVFMT_NOFILE,
+ .priv_class = &audiotoolbox_class,
+};
--
2.32.0 (Apple Git-132)
More information about the ffmpeg-devel
mailing list