gpt4 book ai didi

ios - 如何使用 CoreAudio 的 AudioConverter 实时编码 AAC?

转载 作者:可可西里 更新时间:2023-11-01 04:15:26 27 4
gpt4 key购买 nike

我能找到的所有使用 AudioConverterRef 的示例代码都侧重于我预先拥有所有数据的用例(例如转换磁盘上的文件)。他们通常调用 AudioConverterFillComplexBuffer 将要转换的 PCM 作为 inInputDataProcUserData 并将其填入回调中。 (这真的应该如何使用吗?那为什么它需要回调?)对于我的用例,我正在尝试从麦克风流式传输 aac 音频,所以我没有文件,并且我的 PCM 缓冲区正在实时填写。

因为我没有预先获得所有数据,所以我尝试在输入数据输出后在回调中执行 *ioNumberDataPackets = 0,但这只会将 AudioConverter 置于需要 AudioConverterReset() 的死状态,我没有从中获取任何数据。

我在网上看到的一种方法是,如果我存储的数据太小,则从回调中返回一个错误,一旦我有更多数据就重试,但这似乎是一种资源浪费,我甚至都不敢尝试。

我真的需要“重试直到我的输入缓冲区足够大”,还是有更好的方法?

最佳答案

AudioConverterFillComplexBuffer 实际上并不意味着“用我在此处的输入缓冲区 填充编码器”。它的意思是“用来自编码器的编码数据填充此输出缓冲区”。从这个角度来看,回调突然变得有意义了——它用于获取源数据以满足“为我填充这个输出缓冲区”的请求。也许这对其他人来说是显而易见的,但我花了 很长 时间来理解这一点(并且从我看到的所有 AudioConverter 示例代码中,人们都在通过 inInputDataProcUserData 发送输入数据的地方徘徊,我猜我不是唯一一个)。

AudioConverterFillComplexBuffer 调用处于阻塞状态,并希望您从回调中同步向其传送数据。如果您实时编码,则需要在您自己设置的单独线程上调用 FillComplexBuffer。在回调中,您可以检查可用的输入数据,如果不可用,则需要阻塞信号量。使用 NSCondition,编码器线程看起来像这样:

- (void)startEncoder
{
OSStatus creationStatus = AudioConverterNew(&_fromFormat, &_toFormat, &_converter);

_running = YES;
_condition = [[NSCondition alloc] init];
[self performSelectorInBackground:@selector(_encoderThread) withObject:nil];
}

- (void)_encoderThread
{
while(_running) {
// Make quarter-second buffers.
size_t bufferSize = (_outputBitrate/8) * 0.25;
NSMutableData *outAudioBuffer = [NSMutableData dataWithLength:bufferSize];
AudioBufferList outAudioBufferList;
outAudioBufferList.mNumberBuffers = 1;
outAudioBufferList.mBuffers[0].mNumberChannels = _toFormat.mChannelsPerFrame;
outAudioBufferList.mBuffers[0].mDataByteSize = (UInt32)bufferSize;
outAudioBufferList.mBuffers[0].mData = [outAudioBuffer mutableBytes];

UInt32 ioOutputDataPacketSize = 1;

_currentPresentationTime = kCMTimeInvalid; // you need to fill this in during FillComplexBuffer
const OSStatus conversionResult = AudioConverterFillComplexBuffer(_converter, FillBufferTrampoline, (__bridge void*)self, &ioOutputDataPacketSize, &outAudioBufferList, NULL);

// here I convert the AudioBufferList into a CMSampleBuffer, which I've omitted for brevity.
// Ping me if you need it.
[self.delegate encoder:self encodedSampleBuffer:outSampleBuffer];
}
}

回调可能如下所示:(请注意,我通常使用此蹦床立即转发到我的实例上的一个方法(通过在 inUserData 中转发我的实例;为简洁起见省略此步骤) ):

static OSStatus FillBufferTrampoline(AudioConverterRef               inAudioConverter,
UInt32* ioNumberDataPackets,
AudioBufferList* ioData,
AudioStreamPacketDescription** outDataPacketDescription,
void* inUserData)
{
[_condition lock];

UInt32 countOfPacketsWritten = 0;

while (true) {
// If the condition fires and we have shut down the encoder, just pretend like we have written 0 bytes and are done.
if(!_running) break;

// Out of input data? Wait on the condition.
if(_inputBuffer.length == 0) {
[_condition wait];
continue;
}

// We have data! Fill ioData from your _inputBuffer here.
// Also save the input buffer's start presentationTime here.

// Exit out of the loop, since we're done waiting for data
break;
}

[_condition unlock];

// 2. Set ioNumberDataPackets to the amount of data remaining


// if running is false, this will be 0, indicating EndOfStream
*ioNumberDataPackets = countOfPacketsWritten;

return noErr;
}

为了完整起见,下面是您将如何向此编码器提供数据,以及如何正确关闭它:

- (void)appendSampleBuffer:(CMSampleBufferRef)sampleBuffer
{
[_condition lock];
// Convert sampleBuffer and put it into _inputBuffer here
[_condition broadcast];
[_condition unlock];
}

- (void)stopEncoding
{
[_condition lock];
_running = NO;
[_condition broadcast];
[_condition unlock];
}

关于ios - 如何使用 CoreAudio 的 AudioConverter 实时编码 AAC?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30271186/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com