ios 音频单元渲染错误kAudioUnitErr_CannotDoInCurrentContext仅适用于iPhone 14

jljoyd4f  于 2022-12-15  发布在  iOS
关注(0)|答案(1)|浏览(303)

我们有一个通信应用程序,已经超过8年了,现在在IOS平台上,最近我们遇到了一个问题,在新的iPhone 14。
我们将音频会话类别AVAudioSessionCategoryPlayAndRecord与AVAudioSessionMode语音聊天配合使用。我们还将使用kAudioUnitSubType_VoiceProcessingIO组件。
CoreAudio设置的一部分将回调大小设置如下:

NSTimeInterval desiredBufferDuration = .02;

BOOL prefResult = [session setPreferredIOBufferDuration:desiredBufferDuration error:&nsError];

然后,当使用返回请求缓冲持续时间时

NSTimeInterval actualBufferDuration = [session IOBufferDuration];


我们得到预期的.0213333,即48 kHz时的1024个样本。
在音频回调中,我们总是收到1024个样本。在回调中,我们只记录提供的样本数,如下所示:

static OSStatus inputAudioCallback (void *inRefCon,
                                    AudioUnitRenderActionFlags *ioActionFlags,
                                    const AudioTimeStamp *inTimeStamp,
                                    UInt32 inBusNumber,
                                    UInt32 inNumberFrames,
                                    AudioBufferList *ioData)
{
    NSLog (@"A %d", inNumberFrames);
}

代码被简化了,但是你明白了。
在iPhone 14以外的任何硬件设备上,我们每次从这个日志语句中得到1024。
在iPhone14上,我们得到如下序列:
A 960(6次)
阿1440
A 960(7次)
阿1440
它会重复。这本身并不会引起任何问题,但是我们也会在回调过程中拉入麦克风。下面是音频回调中的简化代码:

renderErr = AudioUnitRender(ioUnit, ioActionFlags, inTimeStamp, 1, inNumberFrames, myIOData);

简单调用,但通常在960/1440转换时,AudioUnitRender会为最后一个960和1440回调返回kAudioUnitErr_CannotDoInCurrentContext。
这会导致麦克风数据丢失,从而导致音频中出现爆音/毛刺。
如果我们切换到使用kAudioUnitSubType_RemoteIO子类型,我们每次回调都能可靠地获得1024个样本,并且AudioUnitRender函数每次都能正确工作。问题是我们无法消除回声,因此在此模式下使用手持设备毫无价值。
因此,问题是,iPhone 14是否发生了严重变化,在使用kAudioUnitySubType_VoiceProcessingIO时,在音频回调期间调用AudioUnitRender?这绝对不是IOS 16错误,因为iPhone 13或支持IOS 16的早期型号没有任何问题。
事实上,我们没有得到1024个样本在一个时间种告诉我们的东西是真的错了,但这段代码已经正确地工作了多年,现在是真正的行为在iPhone 14上时髦。

ovfsdjhp

ovfsdjhp1#

我们这边也遇到了同样的问题。我们认为这是Apple的问题,因为正如您所说,据我们所知,此API没有任何变化。它应该是与kAudioUnitSubType_VoiceProcessingIO音频单元交互的有效方式。
作为一种解决方案,我们决定尝试使用kAudioOutputUnitProperty_SetInputCallback属性注册另一个回调以进行捕捉,该属性与我们使用kAudioUnitProperty_SetRenderCallback设置的属性(之前将同时处理渲染和捕捉)放在一起。

AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = captureCallback;
callbackStruct.inputProcRefCon = this;
OSStatus result = AudioUnitSetProperty(audio_unit,
                                       kAudioOutputUnitProperty_SetInputCallback,
                                       kAudioUnitScope_Global,
                                       1, // input bus
                                       &callbackStruct,
                                       sizeof(callbackStruct));

然后,captureCallback调用AudioUnitRender并将数据复制到缓冲区中:

OSStatus IosAudioUnit::captureCallback(AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* /*ioData*/)
{
    const int currBuffSize = inNumberFrames * sizeof(float);
    bufferList.mData[0].mDataByteSize = currBuffSize;
    if (currBuffSize > MAX_BUF_SIZE) {
        printf("ERROR: currBufSize %d > MAX_BUF_SIZE %d\n");
        return kAudio_ParamError;
    }
    AudioUnitRender(audio_unit, ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, &bufferList);
    // Copy the data in the bufferList into our buffers
    // ... 
}

下面是我们如何在IosAudioUnit构造函数中设置bufferList:

#define MAX_BUF_SIZE = 23040; // 60 ms of frames in bytes at 48000Hz

IosAudioUnit::IosAudioUnit()
{
    AudioBufferList bufferList;
    bufferList.mNumberBuffers = 1;
    bufferList.mBuffers[0].mData = malloc(MAX_BUF_SIZE);
    bufferList.mBuffers[0].mDataByteSize = MAX_BUF_SIZE;
    bufferList.mBuffers[0].mNumberChannels = 2;
}

渲染回调和新的捕捉回调在iPhone 14上仍然是以960和1440的样本大小调用的,但AudioUnit似乎处理得更好,音频中不再有爆裂/小故障!
希望这有帮助!

相关问题