使用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 }
问题分析
- 旋转后图像偏移:直接调用
transformed(by:)旋转CIImage后,图像的extent会发生偏移,导致渲染到新像素缓冲区时画面不在可视范围内,出现黑屏。 - 像素缓冲区尺寸错误:创建新像素缓冲区时直接互换原图像的宽高,未考虑旋转后
extent的实际尺寸。 - 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
相关产品推荐
相关产品推荐

