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
相关产品推荐
相关产品推荐

