如何使用 CVPixelBuffer/IOSurface 更新 CALayer?

and*_*rew 8 calayer nsview swift cvpixelbuffer

我有一个 IOSurface 支持的 CVPixelBuffer,它正在以 30fps 的速度从外部源更新。我想在 NSView 中渲染图像数据的预览——对我来说最好的方法是什么?

我可以直接在视图上设置 CALayer 的 .contents ,但这只会在我的视图第一次更新时更新(或者,如果我调整了视图的大小)。我一直在研究文档,但我无法弄清楚在图层或视图上正确调用 NeedDisplay 以让视图基础结构知道自己刷新,尤其是当更新来自视图外部时。

理想情况下,我只是将 IOSurface 绑定到我的图层,我对它所做的任何更改都会被传播,但我不确定这是否可能。

class VideoPreviewController: NSViewController, VideoFeedConsumer {
    let customLayer : CALayer = CALayer()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do view setup here.
        print("Loaded our video preview")
        
        view.layer?.addSublayer(customLayer)
        customLayer.frame = view.frame
        
        // register our view with the browser service
        VideoFeedBrowser.instance.registerConsumer(self)
    }
    
    override func viewWillDisappear() {
        // deregister our view from the video feed
        VideoFeedBrowser.instance.deregisterConsumer(self)

        super.viewWillDisappear()
    }
    
    // This callback gets called at 30fps whenever the pixelbuffer is updated
    @objc func updateFrame(pixelBuffer: CVPixelBuffer) {

        guard let surface = CVPixelBufferGetIOSurface(pixelBuffer)?.takeUnretainedValue() else {
            print("pixelbuffer isn't IOsurface backed! noooooo!")
            return;
        }

        // Try and tell the view to redraw itself with new contents?
        // These methods don't work
        //self.view.setNeedsDisplay(self.view.visibleRect)
        //self.customLayer.setNeedsDisplay()
        self.customLayer.contents = surface

    }
    
}
Run Code Online (Sandbox Code Playgroud)

这是我对基于 NSView 而不是基于 NSViewController 的缩放版本的尝试,该版本也无法正确更新(或就此而言正确缩放):

class VideoPreviewThumbnail: NSView, VideoFeedConsumer {
   

    required init?(coder decoder: NSCoder) {
        super.init(coder: decoder)
        self.wantsLayer = true
        
        // register our view with the browser service
        VideoFeedBrowser.instance.registerConsumer(self)
    }
    
    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        self.wantsLayer = true
        
        // register our view with the browser service
        VideoFeedBrowser.instance.registerConsumer(self)
    }
    
    deinit{
        VideoFeedBrowser.instance.deregisterConsumer(self)
    }
    
    override func updateLayer() {
        // Do I need to put something here?
        print("update layer")
    }
    
    @objc
    func updateFrame(pixelBuffer: CVPixelBuffer) {
        guard let surface = CVPixelBufferGetIOSurface(pixelBuffer)?.takeUnretainedValue() else {
            print("pixelbuffer isn't IOsurface backed! noooooo!")
            return;
        }
        self.layer?.contents = surface
        self.layer?.transform = CATransform3DMakeScale(
            self.frame.width / CGFloat(CVPixelBufferGetWidth(pixelBuffer)),
            self.frame.height / CGFloat(CVPixelBufferGetHeight(pixelBuffer)),
            CGFloat(1))
    }

}
Run Code Online (Sandbox Code Playgroud)

我错过了什么?

ner*_*own 0

如果您有一些更新它的 IBAction,则使用该didSet块创建一个观察变量,并且每当触发 IBAction 时,更改其值。另请记住在该块中编写更新时要运行的代码。

我建议将变量设置为 an Int,将其默认值设置为0,并1在每次更新时添加到它。

您可以将其转换NSViewNSImageViewfor 部分,其中您要求在 上显示图像NSView数据,以便完成这项工作。