You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS 18下AudioUnit录制PCM转AAC崩溃问题排查与解决请求

问题:iOS 18下PCM转AAC崩溃排查与修复

原代码

AudioBufferList* convertPCMToAAC (XDXRecorder *recoder) {
UInt32   maxPacketSize    = 0;
UInt32   size             = sizeof(maxPacketSize);
OSStatus status;

status = AudioConverterGetProperty(_encodeConvertRef,
                                   kAudioConverterPropertyMaximumOutputPacketSize,
                                   &size,
                                   &maxPacketSize);
//    log4cplus_info("AudioConverter","kAudioConverterPropertyMaximumOutputPacketSize status:%d \n",(int)status);

AudioBufferList *bufferList             = (AudioBufferList *)malloc(sizeof(AudioBufferList));
bufferList->mNumberBuffers              = 1;
bufferList->mBuffers[0].mNumberChannels = _targetDes.mChannelsPerFrame;
bufferList->mBuffers[0].mData           = malloc(maxPacketSize);
bufferList->mBuffers[0].mDataByteSize   = kTVURecoderPCMMaxBuffSize;

AudioStreamPacketDescription outputPacketDescriptions;

UInt32 inNumPackets = 1;

pthread_mutex_lock(&pcmBufferMutex);

status = AudioConverterFillComplexBuffer(_encodeConvertRef,
                                         encodeConverterComplexInputDataProc,
                                         pcm_buffer,
                                         &inNumPackets,
                                         bufferList,
                                         &outputPacketDescriptions);
pthread_mutex_unlock(&pcmBufferMutex);
if(status != noErr){
    //        log4cplus_debug("Audio Recoder","set AudioConverterFillComplexBuffer status:%d inNumPackets:%d \n",(int)status, inNumPackets);
    free(bufferList->mBuffers[0].mData);
    free(bufferList);
    return NULL;
}

if (recoder.needsVoiceDemo) {
    OSStatus status = AudioFileWritePackets(recoder.mRecordFile,
                                            FALSE,
                                            bufferList->mBuffers[0].mDataByteSize,
                                            &outputPacketDescriptions,
                                            recoder.mRecordPacket,
                                            &inNumPackets,
                                            bufferList->mBuffers[0].mData);
    //        log4cplus_info("write file","write file status = %d",(int)status);
    if (status == noErr) {
        recoder.mRecordPacket += inNumPackets; 
    }
}

return bufferList;
}

这段代码在iOS 18以下版本运行正常,但在iOS 18中转换时会崩溃,控制台仅显示报错信息AURemoteIO::IOThread (14): EXC_BAD_ACCESS (code=1, address=0x0),崩溃发生在malloc(maxPacketSize)或AudioConverterFillComplexBuffer()调用处。


崩溃原因分析

  • maxPacketSize有效性未校验:iOS 18中AudioConverterGetProperty获取最大输出包大小可能失败,导致maxPacketSize为0,直接调用malloc(0)会分配极小内存块,后续写入时触发越界崩溃;原代码未检查该API的返回状态。
  • 输出缓冲区大小设置错误:原代码将AAC输出缓冲区的mDataByteSize设为PCM输入缓冲区的kTVURecoderPCMMaxBuffSize,iOS 18对缓冲区大小的校验更严格,不匹配会引发内存访问错误。
  • 外部变量未做有效性校验:_encodeConvertRef、_targetDes等全局/外部变量可能在iOS 18中存在生命周期问题(如已被释放),直接使用会导致空指针访问。
  • 内存分配未做失败处理:malloc返回NULL时未处理,后续操作空指针触发崩溃。

修复方案

1. 严格校验AudioConverterGetProperty结果

在获取最大包大小后,立即检查状态和值,避免无效内存分配:

status = AudioConverterGetProperty(_encodeConvertRef,
                                   kAudioConverterPropertyMaximumOutputPacketSize,
                                   &size,
                                   &maxPacketSize);
if (status != noErr || maxPacketSize == 0) {
    return NULL;
}

2. 修正输出缓冲区大小设置

将输出缓冲区的mDataByteSize设为maxPacketSize,而非PCM输入缓冲区大小:

bufferList->mBuffers[0].mDataByteSize = maxPacketSize;

3. 校验外部变量有效性

调用AudioConverter API前,先确认核心变量不为空:

if (!recoder || !_encodeConvertRef) {
    return NULL;
}

4. 处理内存分配失败情况

每次malloc后检查是否成功,避免空指针操作:

AudioBufferList *bufferList = (AudioBufferList *)malloc(sizeof(AudioBufferList));
if (!bufferList) {
    return NULL;
}
bufferList->mBuffers[0].mData = malloc(maxPacketSize);
if (!bufferList->mBuffers[0].mData) {
    free(bufferList);
    return NULL;
}

完整修复后的代码

AudioBufferList* convertPCMToAAC (XDXRecorder *recoder) {
    // 校验核心输入变量
    if (!recoder || !_encodeConvertRef) {
        return NULL;
    }

    UInt32   maxPacketSize    = 0;
    UInt32   size             = sizeof(maxPacketSize);
    OSStatus status;

    // 获取并校验最大输出包大小
    status = AudioConverterGetProperty(_encodeConvertRef,
                                       kAudioConverterPropertyMaximumOutputPacketSize,
                                       &size,
                                       &maxPacketSize);
    if (status != noErr || maxPacketSize == 0) {
        return NULL;
    }

    // 分配AudioBufferList并校验
    AudioBufferList *bufferList = (AudioBufferList *)malloc(sizeof(AudioBufferList));
    if (!bufferList) {
        return NULL;
    }
    bufferList->mNumberBuffers = 1;
    bufferList->mBuffers[0].mNumberChannels = _targetDes.mChannelsPerFrame;
    
    // 分配输出数据缓冲区并校验
    bufferList->mBuffers[0].mData = malloc(maxPacketSize);
    if (!bufferList->mBuffers[0].mData) {
        free(bufferList);
        return NULL;
    }
    // 设置正确的输出缓冲区大小
    bufferList->mBuffers[0].mDataByteSize = maxPacketSize;

    AudioStreamPacketDescription outputPacketDescriptions;
    UInt32 inNumPackets = 1;

    pthread_mutex_lock(&pcmBufferMutex);
    status = AudioConverterFillComplexBuffer(_encodeConvertRef,
                                             encodeConverterComplexInputDataProc,
                                             pcm_buffer,
                                             &inNumPackets,
                                             bufferList,
                                             &outputPacketDescriptions);
    pthread_mutex_unlock(&pcmBufferMutex);

    if(status != noErr){
        free(bufferList->mBuffers[0].mData);
        free(bufferList);
        return NULL;
    }

    if (recoder->needsVoiceDemo) {
        OSStatus writeStatus = AudioFileWritePackets(recoder->mRecordFile,
                                                FALSE,
                                                bufferList->mBuffers[0].mDataByteSize,
                                                &outputPacketDescriptions,
                                                recoder->mRecordPacket,
                                                &inNumPackets,
                                                bufferList->mBuffers[0].mData);
        if (writeStatus == noErr) {
            recoder->mRecordPacket += inNumPackets; 
        }
    }

    return bufferList;
}

内容的提问来源于stack exchange,提问作者Jiang.Miller

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 08:53:10