iPhone 16 Pro Max上AVFoundation音频压缩失败问题求助
问题:AVFoundation压缩iPhone 16 Pro Max视频时音频压缩异常
在使用AVFoundation压缩iPhone 16 Pro Max录制的视频时,音频压缩环节出现异常。调用AVAssetWriterInput的appendSampleBuffer方法后,写入器状态变为失败,输出错误信息如下:
关键代码
self.audioSettings = @{ AVFormatIDKey: @(kAudioFormatMPEG4AAC), AVNumberOfChannelsKey: @2, AVSampleRateKey: @44100, AVEncoderBitRateKey: @128000, }; [self.audioInput requestMediaDataWhenReadyOnQueue:self.inputQueue usingBlock:^ { while (input.isReadyForMoreMediaData) { CMSampleBufferRef sampleBuffer = [output copyNextSampleBuffer]; if (sampleBuffer) { if ([input appendSampleBuffer:sampleBuffer]) { // Something went wrong here! } } } }];
错误信息
Error Domain=AVFoundationErrorDomain Code=-11800 "This operation cannot be completed" UserInfo={ NSLocalizedFailureReason=An unknown error occurred(-12780), NSLocalizedDescription=This operation cannot be completed, NSUnderlyingError=0x3023db8a0 { Error Domain=NSOSStatusErrorDomain Code=-12780 "(null)" } }
成因分析
- iPhone 16 Pro Max录制的原生音频格式大概率是**kAudioFormatAppleLossless(ALAC)**或高规格PCM,你硬编码的音频参数(44.1kHz、双声道)和原始音频的实际参数不匹配,编码器无法兼容处理。
- 错误码
-12780对应AVErrorInvalidSampleBuffer,说明传入的CMSampleBufferRef不符合AVAssetWriterInput的预期:要么是采样率、声道数、编码格式不匹配,要么是样本缓冲区的时间戳、配置信息存在异常。 - 代码未处理
sampleBuffer为空的场景,且写入失败后没有及时终止写入流程并释放资源,导致写入器陷入异常状态。
修复方案
1. 匹配原始音频参数
不要硬编码音频参数,先获取原始视频音频轨道的实际参数,再对应设置编码参数:
// 获取原始音频轨道 AVAssetTrack *audioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject]; // 获取音频格式描述 NSDictionary *audioFormatDict = [audioTrack formatDescriptions].firstObject; CMFormatDescriptionRef formatDesc = (__bridge CMFormatDescriptionRef)audioFormatDict; const AudioStreamBasicDescription *asbd = CMAudioFormatDescriptionGetStreamBasicDescription(formatDesc); // 基于原始参数生成编码设置 self.audioSettings = @{ AVFormatIDKey: @(kAudioFormatMPEG4AAC), AVNumberOfChannelsKey: @(asbd->mChannelsPerFrame), AVSampleRateKey: @(asbd->mSampleRate), AVEncoderBitRateKey: @128000, };
2. 处理音频格式转换
如果原始音频是ALAC等非PCM格式,需先转换为编码器支持的格式。可以在创建AVAssetReaderTrackOutput时指定输出格式:
AudioStreamBasicDescription outputASBD = {0}; outputASBD.mFormatID = kAudioFormatLinearPCM; // 匹配原始采样率 outputASBD.mSampleRate = asbd->mSampleRate; outputASBD.mChannelsPerFrame = asbd->mChannelsPerFrame; outputASBD.mBitsPerChannel = 16; outputASBD.mBytesPerPacket = outputASBD.mChannelsPerFrame * 2; outputASBD.mBytesPerFrame = outputASBD.mChannelsPerFrame * 2; outputASBD.mFramesPerPacket = 1; outputASBD.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; CMFormatDescriptionRef outputFormatDesc = CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &outputASBD, 0, NULL, NULL); AVAssetReaderTrackOutput *audioOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:audioTrack outputSettings:(__bridge NSDictionary *)outputFormatDesc]; CFRelease(outputFormatDesc);
3. 完善错误处理逻辑
写入失败时及时获取错误信息,终止写入流程并释放资源:
if (sampleBuffer) { if (![input appendSampleBuffer:sampleBuffer]) { NSError *writeError = self.assetWriter.error; NSLog(@"音频写入失败:%@", writeError.localizedDescription); // 终止写入 [self.assetWriter finishWritingWithCompletionHandler:^{ // 处理后续清理逻辑 }]; break; } // 释放样本缓冲区 CFRelease(sampleBuffer); } else { // 无更多样本,结束输入 [input markAsFinished]; break; }
4. 检查时间戳同步
确保音频样本的时间戳和视频样本在同一时间轴上,避免时间戳不连续或超出写入器时间范围:
if (sampleBuffer) { CMTime presentationTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); // 可添加时间戳合法性检查 if (CMTimeCompare(presentationTime, kCMTimeInvalid) == 0) { NSLog(@"无效的音频时间戳"); CFRelease(sampleBuffer); continue; } // 执行写入操作 }
内容的提问来源于stack exchange,提问作者Mokyz
相关产品推荐
相关产品推荐

