代码之家  ›  专栏  ›  技术社区  ›  TJ_SpectiveLabs

如何将UIViewRepresentable与AVSampleBufferDisplayLayer一起传递给SwiftUI

  •  1
  • TJ_SpectiveLabs  · 技术社区  · 8 月前

    当视图加载到设备上时,我只有一个黑屏。使用Storyboard实例和UIViewController时,我有非常相似的代码,但我需要摆脱它,因为我使用的是visionOS,我想将其抽象为使用RealityKit和VideoPlayerLayer,但为了原型设计和迭代其他任务,我希望这个AVSampleBufferDisplayLayer能够工作……最终我必须将AVSampleBufferVideoRenderer传递给VideoPlayerLayer,这样实现就可以帮助我深入了解RealityKit渲染。

    这是代码,为了便于调试和提问,我将所有内容都塞进了一个文件中。

    非常感谢。

    struct MirrorView: View {
        var body: some View {
            VStack {
                LayerView()
            }
        }
    }
    
    struct LayerView: UIViewRepresentable {
        func makeUIView(context: Context) -> UIView {
            print("LayerUIView is being created")
            return LayerUIView()
                
        }
        
        func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LayerView>) {
            print("LayerUIView is being updated")
        }
    }
    
    class LayerUIView: UIView {
        
        private let networking = Networking()
        private let displayLayer = AVSampleBufferDisplayLayer()
        private var subscriptions = Set<AnyCancellable>()
        private var sampleBufferTask: Task<Void, Never>?
        
        override init(frame: CGRect) {
            super.init(frame: frame)
            print("LayerUIView initialized")
            setupVideoLayer()
            setupNetworking()
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        override func layoutSubviews() {
            super.layoutSubviews()
            layer.frame = bounds
        }
        
        private func setupVideoLayer() {
            
            displayLayer.frame = bounds
            displayLayer.videoGravity = .resizeAspect
            
            layer.addSublayer(displayLayer)
            NotificationCenter.default.addObserver(
                    self,
                    selector: #selector(handleFailedToDecodeNotification(_:)),
                    name: .AVSampleBufferDisplayLayerFailedToDecode,
                    object: displayLayer
                )
            }
    
            @objc private func handleFailedToDecodeNotification(_ notification: Notification) {
                if let error = notification.userInfo?[AVSampleBufferDisplayLayerFailedToDecodeNotificationErrorKey] {
                    print("Failed to decode sample buffer. Error: \(error)")
                } else {
                    print("Failed to decode sample buffer. No error information available.")
                }
            }
        
        private func setupNetworking() {
            networking.startAdvertising()
            print("Networking is connected: \(networking.isConnected)")
            startSampleBufferTask()
        }
    
        // MARK: - Task Management
        private func startSampleBufferTask() {
            
            sampleBufferTask = Task {
                for await sampleBuffer in networking.sampleBuffers {
                    let formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer)
                    print("Format Description: \(String(describing: formatDescription))")
    
                    let presentationTimeStamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
                    print("Presentation Timestamp: \(presentationTimeStamp)")
    
                    let duration = CMSampleBufferGetDuration(sampleBuffer)
                    print("Duration: \(duration)")
                    
                    DispatchQueue.main.async {
                        self.displayLayer.sampleBufferRenderer.enqueue(sampleBuffer)
                    }
                }
            }
        }
    
        private func stopSampleBufferTask() {
            sampleBufferTask?.cancel()
            sampleBufferTask = nil
        }
    }
    
    #Preview {
        MirrorView()
    }
    

    我创建了一个UIKit版本,并成功地将其作为iOS设备加载到AVP中。

    下面是运行该代码的ViewController:

    
    class ViewController: UIViewController { //, VideoDecoderAnnexBAdaptorDelegate {
        
        // MARK: - Properties
        private let networking = Networking()
        private let displayLayer = AVSampleBufferDisplayLayer()
        private var subscriptions = Set<AnyCancellable>()
        private var sampleBufferTask: Task<Void, Never>?
        
        // MARK: - Lifecycle
        override func viewDidLoad() {
            super.viewDidLoad()
            
            setupVideoLayer()
            setupNetworking()
        }
        
        override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
            return .allButUpsideDown
        }
    
        override var shouldAutorotate: Bool {
            return true
        }
        
        override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
            super.viewWillTransition(to: size, with: coordinator)
            
            coordinator.animate(alongsideTransition: { _ in
                // Adjust layout for new orientation
                self.displayLayer.frame = self.view.bounds
            }, completion: nil)
        }
        
        // MARK: - Setup Methods
        private func setupVideoLayer() {
            displayLayer.frame = view.bounds
            displayLayer.videoGravity = .resizeAspect
            displayLayer.backgroundColor = UIColor.black.cgColor
            view.layer.addSublayer(displayLayer)
            NotificationCenter.default.addObserver(
                    self,
                    selector: #selector(handleFailedToDecodeNotification(_:)),
                    name: .AVSampleBufferDisplayLayerFailedToDecode,
                    object: displayLayer
                )
            }
    
            @objc private func handleFailedToDecodeNotification(_ notification: Notification) {
                if let error = notification.userInfo?[AVSampleBufferDisplayLayerFailedToDecodeNotificationErrorKey] {
                    print("Failed to decode sample buffer. Error: \(error)")
                } else {
                    print("Failed to decode sample buffer. No error information available.")
                }
            }
        
        private func setupNetworking() {
            networking.startAdvertising()
            print("Networking is connected: \(networking.isConnected)")
            startSampleBufferTask()
        }
    
        // MARK: - Task Management
        private func startSampleBufferTask() {
            
            sampleBufferTask = Task {
                for await sampleBuffer in networking.sampleBuffers {
                    let formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer)
                    print("Format Description: \(String(describing: formatDescription))")
    
                    let presentationTimeStamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
                    print("Presentation Timestamp: \(presentationTimeStamp)")
    
                    let duration = CMSampleBufferGetDuration(sampleBuffer)
                    print("Duration: \(duration)")
                    
                    DispatchQueue.main.async {
                        self.displayLayer.sampleBufferRenderer.enqueue(sampleBuffer)
                    }
                }
            }
        }
    
        private func stopSampleBufferTask() {
            sampleBufferTask?.cancel()
            sampleBufferTask = nil
        }
    }
    
    
    1 回复  |  直到 8 月前
        1
  •  0
  •   duckSern1108    8 月前

    在你的init中 LayerView 代码:

    func makeUIView(context: Context) -> UIView {
        print("LayerUIView is being created")
        return LayerUIView()         
    }
    

    初始 frame 对于 LayerUIView .zero 因此,你 displayLayer s 框架 最初是 .zero 因此,它不能在屏幕上正确显示,而且 LayerUIView 是init, displayeLayer s 框架 不再更新。

    因此,为了解决您的问题,您需要更新 框架 对于 显示层 什么时候 layoutSubviews 我也是。In LayerUIView ,而不是更新 框架 对于 layer :

    override func layoutSubviews() {
        super.layoutSubviews()
        layer.frame = bounds
    }
    

    请改用此代码:

    override func layoutSubviews() {
        super.layoutSubviews()
        displayLayer.frame = bounds
    }
    
    推荐文章