在不超过广播扩展的内存限制的情况下缩放 CMSampleBuffer 大小
Posted
技术标签:
【中文标题】在不超过广播扩展的内存限制的情况下缩放 CMSampleBuffer 大小【英文标题】:Scale CMSampleBuffer Size Without Passing the Memory Limit Of Broadcast Extension 【发布时间】:2021-10-31 01:02:17 【问题描述】:我正在开发一个将像素缓冲区从广播上传扩展发送到 OpenTok 的应用程序。当我运行我的广播扩展时,它会在几秒钟内达到其内存限制。我一直在寻找减小 CMSampleBuffers 大小和规模的方法,并通过首先将它们转换为 CIImage,然后对其进行缩放,然后将它们转换为 CVPixelBuffers 以发送 OpenTok 服务器来结束该过程。不幸的是,即使我尝试减少像素缓冲区,扩展仍然崩溃。我的代码如下:
首先,我在 Sample Handler 的 processSampleBuffer 函数中将 CMSampleBuffer 转换为 CVPixelBuffer,然后将 CVPixelBuffer 和时间戳一起传递给我的函数。在这里,我将 CVPixelBuffer 转换为 cIImage 并使用 cIFilter(CILanczosScaleTransform) 对其进行缩放。之后,我使用 PixelBufferPool 和 cIContext 从 CIImage 生成像素缓冲区,然后使用 videoCaptureConsumer 将新缓冲区发送到 OpenTok 服务器。
func processPixelBuffer(pixelBuffer:CVPixelBuffer, timeStamp ts:CMTime)
guard let ciImage = self.scaleFilterImage(inputImage: pixelBuffer.cmIImage, withAspectRatio: 1.0, scale: CGFloat(kVideoFrameScaleFactor)) else return
if self.pixelBufferPool == nil ||
self.pixelBuffer?.size != pixelBuffer.size
self.destroyPixelBuffers()
self.updateBufferPool(newWidth: Int(ciImage.extent.size.width), newHeight: Int(ciImage.extent.size.height))
guard CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, self.pixelBufferPool, &self.pixelBuffer) == kCVReturnSuccess
else return
context?.render(ciImage, to:pixelBuffer)
self.videoCaptureConsumer?.consumeImageBuffer(pixelBuffer,
orientation:.up,
timestamp:ts,
metadata:nil)
如果 pixelBufferPool 为 nil 或 pixelBuffer 的大小发生变化,我会更新池。
private func updateBufferPool(newWidth: Int, newHeight: Int)
let pixelBufferAttributes: [String: Any] = [
kCVPixelBufferPixelFormatTypeKey as String: UInt(self.videoFormat),
kCVPixelBufferWidthKey as String: newWidth,
kCVPixelBufferHeightKey as String: newHeight,
kCVPixelBufferiosurfacePropertiesKey as String: [:]
]
CVPixelBufferPoolCreate(nil,nil, pixelBufferAttributes as NSDictionary?, &pixelBufferPool)
这是我用来缩放 cIImage 的函数:
func scaleFilterImage(inputImage:CIImage, withAspectRatio aspectRatio:CGFloat, scale:CGFloat) -> CIImage?
scaleFilter?.setValue(inputImage, forKey:kCIInputImageKey)
scaleFilter?.setValue(scale, forKey:kCIInputScaleKey)
scaleFilter?.setValue(aspectRatio, forKey:kCIInputAspectRatioKey)
return scaleFilter?.outputImage
我的问题是为什么它仍然不断崩溃,是否有另一种方法可以减少 CVPixelBuffer 大小而不会导致内存限制崩溃?
我将不胜感激这方面的任何帮助。 Swift 或 Objective - C,我愿意接受所有建议。
【问题讨论】:
【参考方案1】:我不知道Core Image 在从CVPixelBuffer
生成CIImage
时是否复制或引用数据。然而,Accelerate 的 vImage 库提供了一个可以解决这个问题的无拷贝解决方案。 vImageBuffer_InitForCopyFromCVPixelBuffer
函数(与kvImageNoAllocate
结合使用)初始化与CVPixelBuffer
共享数据的vImage_Buffer
。注意需要用CVPixelBufferLockBaseAddress
锁定Core Video像素缓冲区。
您可以通过使用以下方法初始化 vImage_Buffer
来采取更直接的路线:
let buffer = vImage_Buffer(data: data,
height: vImagePixelCount(height),
width: vImagePixelCount(width),
rowBytes: bytesPerRow)
请致电CVPixelBufferGetBaseAddress(_:)
获取data
、CVPixelBufferGet[Height|Width](_:)
获取尺寸以及CVPixelBufferGetBytesPerRow(_:)
获取bytesPerRow
。您仍然需要锁定CVPixelBuffer
。
您可以创建引用源和目标核心视频像素缓冲区的源和目标 vImage 缓冲区。
vImage 为不同的像素格式提供缩放功能:https://developer.apple.com/documentation/accelerate/vimage/vimage_operations/image_scaling。比例是源大小和目标大小之间的比率。
【讨论】:
谢谢,但我正在寻找 GPU 而不是 CPU 的解决方案。当我使用 Accelerate 时,它会导致高 CPU 消耗并且渲染缓冲区非常慢。你知道我如何在 Metal 或 VideoToolBox 中实现它吗? 这很奇怪,因为我使用 Accelerate 进行实时视频处理没有问题。您可以在此处或 Apple 开发者论坛中发布您的代码,以确保您以最佳方式使用 Accelerate。以上是关于在不超过广播扩展的内存限制的情况下缩放 CMSampleBuffer 大小的主要内容,如果未能解决你的问题,请参考以下文章
在不删除数据的情况下限制 ggplot2 轴(超出限制):缩放
在不超过 inode 或硬盘空间的情况下存储大量 jpeg 图像(Python)