H264编码CMSampleBuffer触发EXC_BAD_ACCESS问题排查求助
看起来你这个频繁触发的EXC_BAD_ACCESS问题我之前做H264编码时也踩过,结合你的代码细节,我梳理几个核心排查方向和修复方案:
1. CVPixelBuffer生命周期是最大嫌疑
你用CMSampleBufferCreateReadyWithImageBuffer创建SampleBuffer时,默认情况下系统不会自动retain传入的CVPixelBuffer。如果你的pxBuffer在SampleBuffer被编码前就被释放(比如是自动释放池管理的对象,或者在其他线程提前释放),那么编码时访问的就是野内存,直接触发EXC_BAD_ACCESS。
修复方案:强制复制CVPixelBuffer
创建SampleBuffer时添加kCMSampleBufferOption_ImageBufferShouldBeCopied选项,让SampleBuffer内部持有独立的像素缓冲区副本:
let options = [kCMSampleBufferOption_ImageBufferShouldBeCopied as String: true] as CFDictionary let status = CMSampleBufferCreateReadyWithImageBuffer( allocator: kCFAllocatorDefault, imageBuffer: pxBuffer!, formatDescription: videoDesc!, sampleTiming: &timingInfo, options: options, sampleBufferOut: &sampleBfr )
2. VTCompressionSession的线程安全问题
你用了全局并发队列DISPATCH_QUEUE_PRIORITY_BACKGROUND来执行编码,但VTCompressionSession的操作不是线程安全的,并发调用VTCompressionSessionEncodeFrame或者在编码过程中销毁Session(比如你的错误处理逻辑),都会导致野指针访问。
修复方案:改用串行队列管理所有Session操作
把编码队列改成串行队列,确保所有对EncodingSession的操作(创建、编码、销毁)都在同一个串行队列执行,避免竞态:
// 替换原来的全局队列创建代码 aQueue = dispatch_queue_create("com.your.app.h264.encoder", DISPATCH_QUEUE_SERIAL);
同时,确保VTCompressionSessionInvalidate和EncodingSession = NULL的操作也在这个串行队列里执行,不要在其他线程直接修改Session指针。
3. frameCount的线程不安全自增
你在并发队列里直接用self->frameCount++,这个操作不是原子性的,多线程下会导致竞态条件,虽然不一定直接触发EXC_BAD_ACCESS,但可能引发后续时间戳混乱或其他内存异常。
修复方案:改用原子属性或串行队列保证原子性
要么把frameCount改成原子属性:
@property (atomic, assign) NSInteger frameCount; // 编码时使用属性访问,确保线程安全 self.frameCount++;
要么在串行队列里执行自增(因为已经改用串行队列,这一步天然安全)。
4. 确认CMSampleBuffer的复制是否正确
你提到尝试复制SampleBuffer但没用,可能是复制方式不对。正确的SampleBuffer复制应该用CMSampleBufferCreateCopy,确保复制后的Buffer完全独立:
- (void) encode:(CMSampleBufferRef )sampleBuffer { dispatch_sync(aQueue, ^{ CMSampleBufferRef copiedBuffer = NULL; OSStatus copyStatus = CMSampleBufferCreateCopy(kCFAllocatorDefault, sampleBuffer, &copiedBuffer); if (copyStatus != noErr) { // 处理复制失败 return; } self.frameCount++; CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(copiedBuffer); // ... 后续编码逻辑 ... CFRelease(copiedBuffer); // 记得释放复制的Buffer }); }
总结
优先排查CVPixelBuffer的生命周期和VTCompressionSession的线程安全这两个点,这是这类EXC_BAD_ACCESS最常见的根源。先把队列改成串行,再给SampleBuffer添加复制选项,应该能解决大部分问题。
内容的提问来源于stack exchange,提问作者Aravind G S

