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