6

我正在通过 iPhone 摄像头录制过滤后的视频,在录制时将 CIImage 实时转换为 UIImage 时,CPU 使用率会大幅增加。我制作 CVPixelBuffer 的缓冲区函数使用 UIImage,到目前为止,这需要我进行此转换。如果可能的话,我想创建一个使用 CIImage 的缓冲区函数,这样我就可以跳过从 UIImage 到 CIImage 的转换。我认为这将大大提高录制视频时的性能,因为 CPU 和 GPU 之间不会有任何切换。

这就是我现在所拥有的。在我的 captureOutput 函数中,我从 CIImage 创建了一个 UIImage,它是过滤后的图像。我使用 UIImage 从缓冲区函数创建一个 CVPixelBuffer,并将其附加到assetWriter 的pixelBufferInput:

let imageUI = UIImage(ciImage: ciImage)

let filteredBuffer:CVPixelBuffer? = buffer(from: imageUI)

let success = self.assetWriterPixelBufferInput?.append(filteredBuffer!, withPresentationTime: self.currentSampleTime!)

我使用 UIImage 的缓冲区函数:

func buffer(from image: UIImage) -> CVPixelBuffer? {
    let attrs = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue] as CFDictionary
    var pixelBuffer : CVPixelBuffer?
    let status = CVPixelBufferCreate(kCFAllocatorDefault, Int(image.size.width), Int(image.size.height), kCVPixelFormatType_32ARGB, attrs, &pixelBuffer)

    guard (status == kCVReturnSuccess) else {
        return nil
    }

    CVPixelBufferLockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))
    let pixelData = CVPixelBufferGetBaseAddress(pixelBuffer!)

    let videoRecContext = CGContext(data: pixelData,
                            width: Int(image.size.width),
                            height: Int(image.size.height),
                            bitsPerComponent: 8,
                            bytesPerRow: videoRecBytesPerRow,
                            space: (MTLCaptureView?.colorSpace)!, // It's getting the current colorspace from a MTKView
                            bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue)

    videoRecContext?.translateBy(x: 0, y: image.size.height)
    videoRecContext?.scaleBy(x: 1.0, y: -1.0)

    UIGraphicsPushContext(videoRecContext!)
    image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
    UIGraphicsPopContext()
    CVPixelBufferUnlockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))

    return pixelBuffer
}
4

3 回答 3

6

创建一个CIContext并使用它CIImage直接呈现给您的CVPixelBufferusing CIContext.render(_: CIImage, to buffer: CVPixelBuffer)

于 2019-01-24T19:57:51.060 回答
5

rob mayoff 的回答总结了这一点,但要记住一件非常非常非常重要的事情:

Core Image 延迟渲染,直到客户端请求访问帧缓冲区,即CVPixelBufferLockBaseAddress.

我从与 Apple 的技术支持工程师交谈中了解到这一点,但在任何文档中都找不到。我只在 macOS 上使用它,但想象它在 iOS 上不会有什么不同。

请记住,如果您在渲染之前锁定缓冲区,它仍然可以工作,但会在后面运行一帧,并且第一个渲染将为空。

最后,在 SO 甚至在这个线程中都不止一次提到它:避免CVPixelBuffer为每个渲染创建新的,因为每个缓冲区占用大量系统资源。这就是我们拥有它的原因CVPixelBufferPool——Apple在他们的框架中使用它,所以你可以实现更好的性能!✌️

于 2020-03-26T17:28:21.757 回答
1

为了扩展我从 rob mayoff 那里得到的答案,我将在下面展示我所做的更改:

在 captureOutput 函数中,我将代码更改为:

let filteredBuffer : CVPixelBuffer? = buffer(from: ciImage)

filterContext?.render(_:ciImage, to:filteredBuffer!)

let success = self.assetWriterPixelBufferInput?.append(filteredBuffer!, withPresentationTime: self.currentSampleTime!)

请注意,缓冲区函数传递了一个 ciImage。我格式化了我的缓冲区函数以传递 CIImage,并且能够摆脱里面的很多东西:

func buffer(from image: CIImage) -> CVPixelBuffer? {
    let attrs = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue] as CFDictionary
    var pixelBuffer : CVPixelBuffer?
    let status = CVPixelBufferCreate(kCFAllocatorDefault, Int(image.extent.width), Int(image.extent.height), kCVPixelFormatType_32ARGB, attrs, &pixelBuffer)

    guard (status == kCVReturnSuccess) else {
        return nil
    }

    return pixelBuffer
}
于 2019-01-24T20:50:33.260 回答