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

使用CVPixelBuffer录制视频保存后画面旋转,修正尝试无效

CVPixelBuffer录制视频画面旋转修正后黑屏问题解决

我使用CVPixelBuffer录制竖屏视频,录制过程正常,但保存后画面出现旋转。尝试通过以下代码修正像素缓冲区方向后,导出的视频变为黑屏:

func rotatePixelBuffer(_ pixelBuffer: CVPixelBuffer, degrees: Int) -> CVPixelBuffer? {
        let image = CIImage(cvPixelBuffer: pixelBuffer)
        let rotationTransform = CGAffineTransform(rotationAngle: CGFloat(degrees) * CGFloat.pi / 180)
        let rotatedImage = image.transformed(by: rotationTransform)

        return createPixelBuffer(from: rotatedImage)
    }
    

    func createPixelBuffer(from ciImage: CIImage) -> CVPixelBuffer? {
        let attributes: [String: Any] = [
            kCVPixelBufferCGImageCompatibilityKey as String: true,
            kCVPixelBufferCGBitmapContextCompatibilityKey as String: true
        ]

        var pixelBuffer: CVPixelBuffer?
        let status = CVPixelBufferCreate(
            kCFAllocatorDefault,
            Int(ciImage.extent.size.height),
            Int(ciImage.extent.size.width),
            kCVPixelFormatType_32BGRA,
            attributes as CFDictionary,
            &pixelBuffer
        )

        guard status == kCVReturnSuccess, let unwrappedPixelBuffer = pixelBuffer else {
            return nil
        }

        CVPixelBufferLockBaseAddress(unwrappedPixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
        let context = CIContext()
        context.render(ciImage, to: unwrappedPixelBuffer)

        CVPixelBufferUnlockBaseAddress(unwrappedPixelBuffer, CVPixelBufferLockFlags(rawValue: 0))

        return unwrappedPixelBuffer
    }

问题分析

  1. 旋转后图像偏移:直接调用transformed(by:)旋转CIImage后,图像的extent会发生偏移,导致渲染到新像素缓冲区时画面不在可视范围内,出现黑屏。
  2. 像素缓冲区尺寸错误:创建新像素缓冲区时直接互换原图像的宽高,未考虑旋转后extent的实际尺寸。
  3. CIContext重复创建:每次渲染都新建CIContext,不仅性能低下,还可能引发渲染异常。

修正方案

复用CIContext,并在旋转时修正图像偏移,确保画面处于像素缓冲区的可视范围内:

// 复用CIContext,建议定义在类级别或全局
let ciContext = CIContext(options: [
    .useSoftwareRenderer: false,
    .priorityRequestLow: true
])

func rotatePixelBuffer(_ pixelBuffer: CVPixelBuffer, degrees: Int) -> CVPixelBuffer? {
    let image = CIImage(cvPixelBuffer: pixelBuffer)
    let rotationAngle = CGFloat(degrees) * .pi / 180
    
    // 计算旋转后的图像范围,修正偏移使图像居中
    var transform = CGAffineTransform(rotationAngle: rotationAngle)
    let rotatedExtent = image.extent.applying(transform)
    transform = transform.translatedBy(
        x: rotatedExtent.midX - image.extent.midX,
        y: rotatedExtent.midY - image.extent.midY
    )
    
    let rotatedImage = image.transformed(by: transform)
    return createPixelBuffer(from: rotatedImage)
}

func createPixelBuffer(from ciImage: CIImage) -> CVPixelBuffer? {
    let extent = ciImage.extent
    let width = Int(extent.width)
    let height = Int(extent.height)
    
    let attributes: [String: Any] = [
        kCVPixelBufferCGImageCompatibilityKey as String: true,
        kCVPixelBufferCGBitmapContextCompatibilityKey as String: true,
        kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA,
        kCVPixelBufferWidthKey as String: width,
        kCVPixelBufferHeightKey as String: height
    ]
    
    var pixelBuffer: CVPixelBuffer?
    let status = CVPixelBufferCreate(
        kCFAllocatorDefault,
        width,
        height,
        kCVPixelFormatType_32BGRA,
        attributes as CFDictionary,
        &pixelBuffer
    )
    
    guard status == kCVReturnSuccess, let unwrappedPixelBuffer = pixelBuffer else {
        return nil
    }
    
    CVPixelBufferLockBaseAddress(unwrappedPixelBuffer, .init(rawValue: 0))
    ciContext.render(ciImage, to: unwrappedPixelBuffer)
    CVPixelBufferUnlockBaseAddress(unwrappedPixelBuffer, .init(rawValue: 0))
    
    return unwrappedPixelBuffer
}

额外说明

  • 若使用前置摄像头录制,需添加镜像翻转:在旋转transform后追加transform = transform.scaledBy(x: -1, y: 1)。
  • 确保录制时的像素格式与旋转后保持一致(示例中为kCVPixelFormatType_32BGRA),避免格式不兼容导致的异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 18:18:36