在不超过广播扩展的内存限制的情况下缩放 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(_:) 获取dataCVPixelBufferGet[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)

如何在不实际缩放的情况下更改视图缩放值?

如何在不传输整个文件的情况下限制使用 JavaScript(或 Java)上传的文件的大小?

为啥 2d 画布在不模糊的情况下无法缩放?

如何在不更改用户设置的缩放比例的情况下更新地图标记?