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

H264编码CMSampleBuffer触发EXC_BAD_ACCESS问题排查求助

排查EXC_BAD_ACCESS:CMSampleBuffer转H264编码的常见坑

看起来你这个频繁触发的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:29:03