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

使用vImageRotate90_ARGB8888旋转CMSampleBuffer时崩溃求助

解决vImageRotate90_ARGB8888崩溃问题

我仔细排查了你的代码,发现几个关键问题导致了崩溃,下面逐一说明并给出修复方案:

1. 像素格式不匹配

vImageRotate90_ARGB8888 仅支持 kCVPixelFormatType_32ARGB 格式的像素缓冲区,但你的代码直接复用了原 CVPixelBuffer 的像素格式。如果原采样缓冲区的格式不是32ARGB(比如常见的kCVPixelFormatType_420YpCbCr8BiPlanarFullRange),调用这个函数必然会崩溃。

修复:先检查原像素格式,若不是32ARGB,需要先转换格式;如果你的采集流程可以直接设置为32ARGB格式,那是最优解(比如在AVCaptureSession配置时指定)。

2. 目标缓冲区参数错误

旋转90度顺时针后,输出缓冲区的宽高应该和原缓冲区交换:

  • 原宽度width → 目标高度
  • 原高度height → 目标宽度

同时,目标缓冲区的bytesPerRow计算错误,ARGB8888每个像素占4字节,所以正确的行字节数应该是 4 * 目标宽度(也就是4 * height)。你之前的计算多乘了sizeof(unsigned char),这是冗余的(因为unsigned char本身就是1字节)。

3. 内存分配大小错误

你用bytesPerRow * height计算内存大小,但bytesPerRow可能因为内存对齐比4 * width大,而旋转后的缓冲区实际需要的内存是4 * 目标宽度 * 目标高度(也就是4 * height * width),用原行字节数计算会分配多余的内存,甚至可能因为对齐问题导致vImage处理出错。

4. 旋转参数定义问题

确认kRotate90DegreesClockwise的宏定义是否正确:vImageRotate90_ARGB8888的rotation参数接受0-3的整数,分别对应0°、90°、180°、270°顺时针旋转。如果你的宏定义是90而不是1,这会导致vImage读取越界,直接崩溃。

修正后的完整代码

typedef NS_ENUM(uint8_t, vImageRotation) {
    kRotate0DegreesClockwise = 0,
    kRotate90DegreesClockwise = 1,
    kRotate180DegreesClockwise = 2,
    kRotate270DegreesClockwise = 3
};

- (CVPixelBufferRef)rotateBuffer:(CMSampleBufferRef)sampleBuffer {
    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    if (!pixelBuffer) return NULL;
    
    // 锁定缓冲区基地址
    CVReturn lockStatus = CVPixelBufferLockBaseAddress(pixelBuffer, 0);
    if (lockStatus != kCVReturnSuccess) {
        NSLog(@"Failed to lock pixel buffer");
        return NULL;
    }
    
    size_t originalWidth = CVPixelBufferGetWidth(pixelBuffer);
    size_t originalHeight = CVPixelBufferGetHeight(pixelBuffer);
    OSType originalFormat = CVPixelBufferGetPixelFormatType(pixelBuffer);
    
    // 确保像素格式是ARGB8888,否则需要先转换
    if (originalFormat != kCVPixelFormatType_32ARGB) {
        NSLog(@"Unsupported pixel format for vImageRotate90_ARGB8888");
        CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
        // 这里可以添加格式转换逻辑,比如使用CIContext转换
        return NULL;
    }
    
    void *sourceBaseAddr = CVPixelBufferGetBaseAddress(pixelBuffer);
    size_t sourceBytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
    
    // 旋转90度后,宽高交换
    size_t destWidth = originalHeight;
    size_t destHeight = originalWidth;
    // ARGB8888每个像素4字节,行字节数按目标宽度计算
    size_t destBytesPerRow = 4 * destWidth;
    // 计算需要分配的内存大小
    size_t destBufferSize = destBytesPerRow * destHeight;
    
    unsigned char *destPixelData = (unsigned char *)malloc(destBufferSize);
    if (!destPixelData) {
        NSLog(@"Failed to allocate memory for destination buffer");
        CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
        return NULL;
    }
    
    // 初始化vImage缓冲区
    vImage_Buffer sourceBuffer = {sourceBaseAddr, originalHeight, originalWidth, sourceBytesPerRow};
    vImage_Buffer destinationBuffer = {destPixelData, destHeight, destWidth, destBytesPerRow};
    
    Pixel_8888 bgColor = {0, 0, 0, 0};
    // 使用正确的旋转参数(1代表90度顺时针)
    vImage_Error error = vImageRotate90_ARGB8888(&sourceBuffer, &destinationBuffer, kRotate90DegreesClockwise, bgColor, kvImageNoFlags);
    if (error != kvImageNoError) {
        NSLog(@"vImageRotate failed with error: %ld", error);
        free(destPixelData);
        CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
        return NULL;
    }
    
    // 创建旋转后的CVPixelBuffer,注意指定正确的像素格式
    CVPixelBufferRef rotatedBuffer = NULL;
    CVReturn createStatus = CVPixelBufferCreateWithBytes(kCFAllocatorDefault,
                                                        destWidth,
                                                        destHeight,
                                                        kCVPixelFormatType_32ARGB,
                                                        destPixelData,
                                                        destBytesPerRow,
                                                        freePixelBufferData,
                                                        NULL,
                                                        NULL,
                                                        &rotatedBuffer);
    
    if (createStatus != kCVReturnSuccess) {
        NSLog(@"Failed to create rotated pixel buffer");
        free(destPixelData);
        rotatedBuffer = NULL;
    }
    
    CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
    return rotatedBuffer;
}

void freePixelBufferData(void *releaseRefCon, const void *baseAddress) {
    free((void *)baseAddress);
}

额外建议

  • 如果你的原缓冲区格式不是32ARGB,推荐使用CIContext先转换格式,或者在AVCaptureDeviceInput配置时直接设置输出格式为kCVPixelFormatType_32ARGB,这样可以避免额外的格式转换开销。
  • 每次调用vImage函数后都检查错误码,这样能更快定位问题,而不是直接崩溃。
  • 确保所有内存分配都有对应的释放逻辑,避免内存泄漏。

内容的提问来源于stack exchange,提问作者anthonya1999

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:15:57