1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * AudioToolbox output device 3cabdff1aSopenharmony_ci * Copyright (c) 2020 Thilo Borgmann <thilo.borgmann@mail.de> 4cabdff1aSopenharmony_ci * 5cabdff1aSopenharmony_ci * This file is part of FFmpeg. 6cabdff1aSopenharmony_ci * 7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 11cabdff1aSopenharmony_ci * 12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15cabdff1aSopenharmony_ci * Lesser General Public License for more details. 16cabdff1aSopenharmony_ci * 17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20cabdff1aSopenharmony_ci */ 21cabdff1aSopenharmony_ci 22cabdff1aSopenharmony_ci/** 23cabdff1aSopenharmony_ci * @file 24cabdff1aSopenharmony_ci * AudioToolbox output device 25cabdff1aSopenharmony_ci * @author Thilo Borgmann <thilo.borgmann@mail.de> 26cabdff1aSopenharmony_ci */ 27cabdff1aSopenharmony_ci 28cabdff1aSopenharmony_ci#import <AudioToolbox/AudioToolbox.h> 29cabdff1aSopenharmony_ci#include <pthread.h> 30cabdff1aSopenharmony_ci 31cabdff1aSopenharmony_ci#include "libavutil/opt.h" 32cabdff1aSopenharmony_ci#include "libavformat/internal.h" 33cabdff1aSopenharmony_ci#include "libavutil/internal.h" 34cabdff1aSopenharmony_ci#include "avdevice.h" 35cabdff1aSopenharmony_ci 36cabdff1aSopenharmony_citypedef struct 37cabdff1aSopenharmony_ci{ 38cabdff1aSopenharmony_ci AVClass *class; 39cabdff1aSopenharmony_ci 40cabdff1aSopenharmony_ci AudioQueueBufferRef buffer[2]; 41cabdff1aSopenharmony_ci pthread_mutex_t buffer_lock[2]; 42cabdff1aSopenharmony_ci int cur_buf; 43cabdff1aSopenharmony_ci AudioQueueRef queue; 44cabdff1aSopenharmony_ci 45cabdff1aSopenharmony_ci int list_devices; 46cabdff1aSopenharmony_ci int audio_device_index; 47cabdff1aSopenharmony_ci 48cabdff1aSopenharmony_ci} ATContext; 49cabdff1aSopenharmony_ci 50cabdff1aSopenharmony_cistatic int check_status(AVFormatContext *avctx, OSStatus *status, const char *msg) 51cabdff1aSopenharmony_ci{ 52cabdff1aSopenharmony_ci if (*status != noErr) { 53cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Error: %s (%i)\n", msg, *status); 54cabdff1aSopenharmony_ci return 1; 55cabdff1aSopenharmony_ci } else { 56cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_DEBUG, " OK : %s\n", msg); 57cabdff1aSopenharmony_ci return 0; 58cabdff1aSopenharmony_ci } 59cabdff1aSopenharmony_ci} 60cabdff1aSopenharmony_ci 61cabdff1aSopenharmony_cistatic void queue_callback(void* atctx, AudioQueueRef inAQ, 62cabdff1aSopenharmony_ci AudioQueueBufferRef inBuffer) 63cabdff1aSopenharmony_ci{ 64cabdff1aSopenharmony_ci // unlock the buffer that has just been consumed 65cabdff1aSopenharmony_ci ATContext *ctx = (ATContext*)atctx; 66cabdff1aSopenharmony_ci for (int i = 0; i < 2; i++) { 67cabdff1aSopenharmony_ci if (inBuffer == ctx->buffer[i]) { 68cabdff1aSopenharmony_ci pthread_mutex_unlock(&ctx->buffer_lock[i]); 69cabdff1aSopenharmony_ci } 70cabdff1aSopenharmony_ci } 71cabdff1aSopenharmony_ci} 72cabdff1aSopenharmony_ci 73cabdff1aSopenharmony_cistatic av_cold int at_write_header(AVFormatContext *avctx) 74cabdff1aSopenharmony_ci{ 75cabdff1aSopenharmony_ci ATContext *ctx = (ATContext*)avctx->priv_data; 76cabdff1aSopenharmony_ci OSStatus err = noErr; 77cabdff1aSopenharmony_ci CFStringRef device_UID = NULL; 78cabdff1aSopenharmony_ci AudioDeviceID *devices; 79cabdff1aSopenharmony_ci int num_devices; 80cabdff1aSopenharmony_ci 81cabdff1aSopenharmony_ci 82cabdff1aSopenharmony_ci // get devices 83cabdff1aSopenharmony_ci UInt32 data_size = 0; 84cabdff1aSopenharmony_ci AudioObjectPropertyAddress prop; 85cabdff1aSopenharmony_ci prop.mSelector = kAudioHardwarePropertyDevices; 86cabdff1aSopenharmony_ci prop.mScope = kAudioObjectPropertyScopeGlobal; 87cabdff1aSopenharmony_ci prop.mElement = kAudioObjectPropertyElementMaster; 88cabdff1aSopenharmony_ci err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, NULL, &data_size); 89cabdff1aSopenharmony_ci if (check_status(avctx, &err, "AudioObjectGetPropertyDataSize devices")) 90cabdff1aSopenharmony_ci return AVERROR(EINVAL); 91cabdff1aSopenharmony_ci 92cabdff1aSopenharmony_ci num_devices = data_size / sizeof(AudioDeviceID); 93cabdff1aSopenharmony_ci 94cabdff1aSopenharmony_ci devices = (AudioDeviceID*)(av_malloc(data_size)); 95cabdff1aSopenharmony_ci err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &data_size, devices); 96cabdff1aSopenharmony_ci if (check_status(avctx, &err, "AudioObjectGetPropertyData devices")) { 97cabdff1aSopenharmony_ci av_freep(&devices); 98cabdff1aSopenharmony_ci return AVERROR(EINVAL); 99cabdff1aSopenharmony_ci } 100cabdff1aSopenharmony_ci 101cabdff1aSopenharmony_ci // list devices 102cabdff1aSopenharmony_ci if (ctx->list_devices) { 103cabdff1aSopenharmony_ci CFStringRef device_name = NULL; 104cabdff1aSopenharmony_ci prop.mScope = kAudioDevicePropertyScopeInput; 105cabdff1aSopenharmony_ci 106cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_INFO, "CoreAudio devices:\n"); 107cabdff1aSopenharmony_ci for(UInt32 i = 0; i < num_devices; ++i) { 108cabdff1aSopenharmony_ci // UID 109cabdff1aSopenharmony_ci data_size = sizeof(device_UID); 110cabdff1aSopenharmony_ci prop.mSelector = kAudioDevicePropertyDeviceUID; 111cabdff1aSopenharmony_ci err = AudioObjectGetPropertyData(devices[i], &prop, 0, NULL, &data_size, &device_UID); 112cabdff1aSopenharmony_ci if (check_status(avctx, &err, "AudioObjectGetPropertyData UID")) 113cabdff1aSopenharmony_ci continue; 114cabdff1aSopenharmony_ci 115cabdff1aSopenharmony_ci // name 116cabdff1aSopenharmony_ci data_size = sizeof(device_name); 117cabdff1aSopenharmony_ci prop.mSelector = kAudioDevicePropertyDeviceNameCFString; 118cabdff1aSopenharmony_ci err = AudioObjectGetPropertyData(devices[i], &prop, 0, NULL, &data_size, &device_name); 119cabdff1aSopenharmony_ci if (check_status(avctx, &err, "AudioObjecTGetPropertyData name")) 120cabdff1aSopenharmony_ci continue; 121cabdff1aSopenharmony_ci 122cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_INFO, "[%d] %30s, %s\n", i, 123cabdff1aSopenharmony_ci CFStringGetCStringPtr(device_name, kCFStringEncodingMacRoman), 124cabdff1aSopenharmony_ci CFStringGetCStringPtr(device_UID, kCFStringEncodingMacRoman)); 125cabdff1aSopenharmony_ci } 126cabdff1aSopenharmony_ci } 127cabdff1aSopenharmony_ci 128cabdff1aSopenharmony_ci // get user-defined device UID or use default device 129cabdff1aSopenharmony_ci // -audio_device_index overrides any URL given 130cabdff1aSopenharmony_ci const char *stream_name = avctx->url; 131cabdff1aSopenharmony_ci if (stream_name && ctx->audio_device_index == -1) { 132cabdff1aSopenharmony_ci sscanf(stream_name, "%d", &ctx->audio_device_index); 133cabdff1aSopenharmony_ci } 134cabdff1aSopenharmony_ci 135cabdff1aSopenharmony_ci if (ctx->audio_device_index >= 0) { 136cabdff1aSopenharmony_ci // get UID of selected device 137cabdff1aSopenharmony_ci data_size = sizeof(device_UID); 138cabdff1aSopenharmony_ci prop.mSelector = kAudioDevicePropertyDeviceUID; 139cabdff1aSopenharmony_ci err = AudioObjectGetPropertyData(devices[ctx->audio_device_index], &prop, 0, NULL, &data_size, &device_UID); 140cabdff1aSopenharmony_ci if (check_status(avctx, &err, "AudioObjecTGetPropertyData UID")) { 141cabdff1aSopenharmony_ci av_freep(&devices); 142cabdff1aSopenharmony_ci return AVERROR(EINVAL); 143cabdff1aSopenharmony_ci } 144cabdff1aSopenharmony_ci } else { 145cabdff1aSopenharmony_ci // use default device 146cabdff1aSopenharmony_ci device_UID = NULL; 147cabdff1aSopenharmony_ci } 148cabdff1aSopenharmony_ci 149cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "stream_name: %s\n", stream_name); 150cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "audio_device_idnex: %i\n", ctx->audio_device_index); 151cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "UID: %s\n", CFStringGetCStringPtr(device_UID, kCFStringEncodingMacRoman)); 152cabdff1aSopenharmony_ci 153cabdff1aSopenharmony_ci // check input stream 154cabdff1aSopenharmony_ci if (avctx->nb_streams != 1 || avctx->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) { 155cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_ERROR, "Only a single audio stream is supported.\n"); 156cabdff1aSopenharmony_ci return AVERROR(EINVAL); 157cabdff1aSopenharmony_ci } 158cabdff1aSopenharmony_ci 159cabdff1aSopenharmony_ci av_freep(&devices); 160cabdff1aSopenharmony_ci AVCodecParameters *codecpar = avctx->streams[0]->codecpar; 161cabdff1aSopenharmony_ci 162cabdff1aSopenharmony_ci // audio format 163cabdff1aSopenharmony_ci AudioStreamBasicDescription device_format = {0}; 164cabdff1aSopenharmony_ci device_format.mSampleRate = codecpar->sample_rate; 165cabdff1aSopenharmony_ci device_format.mFormatID = kAudioFormatLinearPCM; 166cabdff1aSopenharmony_ci device_format.mFormatFlags |= (codecpar->format == AV_SAMPLE_FMT_FLT) ? kLinearPCMFormatFlagIsFloat : 0; 167cabdff1aSopenharmony_ci device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_S8) ? kLinearPCMFormatFlagIsSignedInteger : 0; 168cabdff1aSopenharmony_ci device_format.mFormatFlags |= (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE)) ? kLinearPCMFormatFlagIsSignedInteger : 0; 169cabdff1aSopenharmony_ci device_format.mFormatFlags |= (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S24BE, AV_CODEC_ID_PCM_S24LE)) ? kLinearPCMFormatFlagIsSignedInteger : 0; 170cabdff1aSopenharmony_ci device_format.mFormatFlags |= (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S32BE, AV_CODEC_ID_PCM_S32LE)) ? kLinearPCMFormatFlagIsSignedInteger : 0; 171cabdff1aSopenharmony_ci device_format.mFormatFlags |= (av_sample_fmt_is_planar(codecpar->format)) ? kAudioFormatFlagIsNonInterleaved : 0; 172cabdff1aSopenharmony_ci device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_F32BE) ? kAudioFormatFlagIsBigEndian : 0; 173cabdff1aSopenharmony_ci device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_S16BE) ? kAudioFormatFlagIsBigEndian : 0; 174cabdff1aSopenharmony_ci device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_S24BE) ? kAudioFormatFlagIsBigEndian : 0; 175cabdff1aSopenharmony_ci device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_S32BE) ? kAudioFormatFlagIsBigEndian : 0; 176cabdff1aSopenharmony_ci device_format.mChannelsPerFrame = codecpar->channels; 177cabdff1aSopenharmony_ci 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); 178cabdff1aSopenharmony_ci device_format.mBytesPerFrame = (device_format.mBitsPerChannel >> 3) * device_format.mChannelsPerFrame; 179cabdff1aSopenharmony_ci device_format.mFramesPerPacket = 1; 180cabdff1aSopenharmony_ci device_format.mBytesPerPacket = device_format.mBytesPerFrame * device_format.mFramesPerPacket; 181cabdff1aSopenharmony_ci device_format.mReserved = 0; 182cabdff1aSopenharmony_ci 183cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "device_format.mSampleRate = %i\n", codecpar->sample_rate); 184cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatID = %s\n", "kAudioFormatLinearPCM"); 185cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->format == AV_SAMPLE_FMT_FLT) ? "kLinearPCMFormatFlagIsFloat" : "0"); 186cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_S8) ? "kLinearPCMFormatFlagIsSignedInteger" : "0"); 187cabdff1aSopenharmony_ci 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"); 188cabdff1aSopenharmony_ci 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"); 189cabdff1aSopenharmony_ci 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"); 190cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (av_sample_fmt_is_planar(codecpar->format)) ? "kAudioFormatFlagIsNonInterleaved" : "0"); 191cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_F32BE) ? "kAudioFormatFlagIsBigEndian" : "0"); 192cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_S16BE) ? "kAudioFormatFlagIsBigEndian" : "0"); 193cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_S24BE) ? "kAudioFormatFlagIsBigEndian" : "0"); 194cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_S32BE) ? "kAudioFormatFlagIsBigEndian" : "0"); 195cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags == %i\n", device_format.mFormatFlags); 196cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "device_format.mChannelsPerFrame = %i\n", codecpar->channels); 197cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "device_format.mBitsPerChannel = %i\n", av_get_bytes_per_sample(codecpar->format) << 3); 198cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "device_format.mBytesPerFrame = %i\n", (device_format.mBitsPerChannel >> 3) * codecpar->channels); 199cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "device_format.mBytesPerPacket = %i\n", device_format.mBytesPerFrame); 200cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "device_format.mFramesPerPacket = %i\n", 1); 201cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_DEBUG, "device_format.mReserved = %i\n", 0); 202cabdff1aSopenharmony_ci 203cabdff1aSopenharmony_ci // create new output queue for the device 204cabdff1aSopenharmony_ci err = AudioQueueNewOutput(&device_format, queue_callback, ctx, 205cabdff1aSopenharmony_ci NULL, kCFRunLoopCommonModes, 206cabdff1aSopenharmony_ci 0, &ctx->queue); 207cabdff1aSopenharmony_ci if (check_status(avctx, &err, "AudioQueueNewOutput")) { 208cabdff1aSopenharmony_ci if (err == kAudioFormatUnsupportedDataFormatError) 209cabdff1aSopenharmony_ci av_log(ctx, AV_LOG_ERROR, "Unsupported output format.\n"); 210cabdff1aSopenharmony_ci return AVERROR(EINVAL); 211cabdff1aSopenharmony_ci } 212cabdff1aSopenharmony_ci 213cabdff1aSopenharmony_ci // set user-defined device or leave untouched for default 214cabdff1aSopenharmony_ci if (device_UID != NULL) { 215cabdff1aSopenharmony_ci err = AudioQueueSetProperty(ctx->queue, kAudioQueueProperty_CurrentDevice, &device_UID, sizeof(device_UID)); 216cabdff1aSopenharmony_ci if (check_status(avctx, &err, "AudioQueueSetProperty output UID")) 217cabdff1aSopenharmony_ci return AVERROR(EINVAL); 218cabdff1aSopenharmony_ci } 219cabdff1aSopenharmony_ci 220cabdff1aSopenharmony_ci // start the queue 221cabdff1aSopenharmony_ci err = AudioQueueStart(ctx->queue, NULL); 222cabdff1aSopenharmony_ci if (check_status(avctx, &err, "AudioQueueStart")) 223cabdff1aSopenharmony_ci return AVERROR(EINVAL); 224cabdff1aSopenharmony_ci 225cabdff1aSopenharmony_ci // init the mutexes for double-buffering 226cabdff1aSopenharmony_ci pthread_mutex_init(&ctx->buffer_lock[0], NULL); 227cabdff1aSopenharmony_ci pthread_mutex_init(&ctx->buffer_lock[1], NULL); 228cabdff1aSopenharmony_ci 229cabdff1aSopenharmony_ci return 0; 230cabdff1aSopenharmony_ci} 231cabdff1aSopenharmony_ci 232cabdff1aSopenharmony_cistatic int at_write_packet(AVFormatContext *avctx, AVPacket *pkt) 233cabdff1aSopenharmony_ci{ 234cabdff1aSopenharmony_ci ATContext *ctx = (ATContext*)avctx->priv_data; 235cabdff1aSopenharmony_ci OSStatus err = noErr; 236cabdff1aSopenharmony_ci 237cabdff1aSopenharmony_ci // use the other buffer 238cabdff1aSopenharmony_ci ctx->cur_buf = !ctx->cur_buf; 239cabdff1aSopenharmony_ci 240cabdff1aSopenharmony_ci // lock for writing or wait for the buffer to be available 241cabdff1aSopenharmony_ci // will be unlocked by queue callback 242cabdff1aSopenharmony_ci pthread_mutex_lock(&ctx->buffer_lock[ctx->cur_buf]); 243cabdff1aSopenharmony_ci 244cabdff1aSopenharmony_ci // (re-)allocate the buffer if not existant or of different size 245cabdff1aSopenharmony_ci if (!ctx->buffer[ctx->cur_buf] || ctx->buffer[ctx->cur_buf]->mAudioDataBytesCapacity != pkt->size) { 246cabdff1aSopenharmony_ci err = AudioQueueAllocateBuffer(ctx->queue, pkt->size, &ctx->buffer[ctx->cur_buf]); 247cabdff1aSopenharmony_ci if (check_status(avctx, &err, "AudioQueueAllocateBuffer")) { 248cabdff1aSopenharmony_ci pthread_mutex_unlock(&ctx->buffer_lock[ctx->cur_buf]); 249cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 250cabdff1aSopenharmony_ci } 251cabdff1aSopenharmony_ci } 252cabdff1aSopenharmony_ci 253cabdff1aSopenharmony_ci AudioQueueBufferRef buf = ctx->buffer[ctx->cur_buf]; 254cabdff1aSopenharmony_ci 255cabdff1aSopenharmony_ci // copy audio data into buffer and enqueue the buffer 256cabdff1aSopenharmony_ci memcpy(buf->mAudioData, pkt->data, buf->mAudioDataBytesCapacity); 257cabdff1aSopenharmony_ci buf->mAudioDataByteSize = buf->mAudioDataBytesCapacity; 258cabdff1aSopenharmony_ci err = AudioQueueEnqueueBuffer(ctx->queue, buf, 0, NULL); 259cabdff1aSopenharmony_ci if (check_status(avctx, &err, "AudioQueueEnqueueBuffer")) { 260cabdff1aSopenharmony_ci pthread_mutex_unlock(&ctx->buffer_lock[ctx->cur_buf]); 261cabdff1aSopenharmony_ci return AVERROR(EINVAL); 262cabdff1aSopenharmony_ci } 263cabdff1aSopenharmony_ci 264cabdff1aSopenharmony_ci return 0; 265cabdff1aSopenharmony_ci} 266cabdff1aSopenharmony_ci 267cabdff1aSopenharmony_cistatic av_cold int at_write_trailer(AVFormatContext *avctx) 268cabdff1aSopenharmony_ci{ 269cabdff1aSopenharmony_ci ATContext *ctx = (ATContext*)avctx->priv_data; 270cabdff1aSopenharmony_ci OSStatus err = noErr; 271cabdff1aSopenharmony_ci 272cabdff1aSopenharmony_ci pthread_mutex_destroy(&ctx->buffer_lock[0]); 273cabdff1aSopenharmony_ci pthread_mutex_destroy(&ctx->buffer_lock[1]); 274cabdff1aSopenharmony_ci 275cabdff1aSopenharmony_ci err = AudioQueueFlush(ctx->queue); 276cabdff1aSopenharmony_ci check_status(avctx, &err, "AudioQueueFlush"); 277cabdff1aSopenharmony_ci err = AudioQueueDispose(ctx->queue, true); 278cabdff1aSopenharmony_ci check_status(avctx, &err, "AudioQueueDispose"); 279cabdff1aSopenharmony_ci 280cabdff1aSopenharmony_ci return 0; 281cabdff1aSopenharmony_ci} 282cabdff1aSopenharmony_ci 283cabdff1aSopenharmony_cistatic const AVOption options[] = { 284cabdff1aSopenharmony_ci { "list_devices", "list available audio devices", offsetof(ATContext, list_devices), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, 285cabdff1aSopenharmony_ci { "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 }, 286cabdff1aSopenharmony_ci { NULL }, 287cabdff1aSopenharmony_ci}; 288cabdff1aSopenharmony_ci 289cabdff1aSopenharmony_cistatic const AVClass at_class = { 290cabdff1aSopenharmony_ci .class_name = "AudioToolbox", 291cabdff1aSopenharmony_ci .item_name = av_default_item_name, 292cabdff1aSopenharmony_ci .option = options, 293cabdff1aSopenharmony_ci .version = LIBAVUTIL_VERSION_INT, 294cabdff1aSopenharmony_ci .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT, 295cabdff1aSopenharmony_ci}; 296cabdff1aSopenharmony_ci 297cabdff1aSopenharmony_ciconst AVOutputFormat ff_audiotoolbox_muxer = { 298cabdff1aSopenharmony_ci .name = "audiotoolbox", 299cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("AudioToolbox output device"), 300cabdff1aSopenharmony_ci .priv_data_size = sizeof(ATContext), 301cabdff1aSopenharmony_ci .audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), 302cabdff1aSopenharmony_ci .video_codec = AV_CODEC_ID_NONE, 303cabdff1aSopenharmony_ci .write_header = at_write_header, 304cabdff1aSopenharmony_ci .write_packet = at_write_packet, 305cabdff1aSopenharmony_ci .write_trailer = at_write_trailer, 306cabdff1aSopenharmony_ci .flags = AVFMT_NOFILE, 307cabdff1aSopenharmony_ci .priv_class = &at_class, 308cabdff1aSopenharmony_ci}; 309