简体   繁体   中英

Subscription to RxSwift Observable<VideoCaptureOutput> not triggering onNext()

I'd like to use RxSwift to process video frames captured from the iPhone camera. I'm using a community maintained project, https://github.com/RxSwiftCommunity/RxAVFoundation , which bridges AVFoundation (used to capture camera output) and RxSwift.

I'm trying to just print a dummy log statement whenever new video frames get written to the output buffer. The following is my ViewController. I configure the AVCaptureSession, set up the Rx chain, then start the session. However, the print statement in the .next case is never triggered. I reached out to the project owner. Is the below code correct? Here's the Reactive extension for the AVCaptureSession class from the community maintained project: https://github.com/RxSwiftCommunity/RxAVFoundation/blob/master/RxAVFoundation/AVCaptureSession%2BRx.swift

//  ViewController.swift
import UIKit
import AVFoundation
import RxSwift

class ViewController: UIViewController {

    // capture session
    private let session = AVCaptureSession()
    private var videoDevice: AVCaptureDevice!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
    
        session
            .rx
            .configure(captureDevice: videoDevice)
    
        let disposeBag = DisposeBag()
        
        let videoSettings = [(kCVPixelBufferPixelFormatTypeKey as NSString) : NSNumber(value: kCVPixelFormatType_32BGRA)] as [String : Any]
        session
            .rx
            .videoCaptureOutput(settings: videoSettings)
            .observeOn(MainScheduler.instance)
            .subscribe { [unowned self] (event) in
                switch event {
                    case .next(let captureOutput):
                        print("got a frame")
                    case .error(let error):
                        print("error: %@", "\(error)")
                    case .completed:
                        break // never happens
                }
            }
            .disposed(by: disposeBag)
        session
            .rx
            .startRunning()
    }
}

Because you've defined your DisposeBag locally inside viewDidLoad as soon as viewDidLoad finishes all the subscriptions added to the bag are disposed.

Declare your DisposeBag as an instance variable of the ViewController to fix:

...
// capture session
private let session = AVCaptureSession()
private var videoDevice: AVCaptureDevice!

private let disposeBag = DisposeBag()

override func viewDidLoad() {
    super.viewDidLoad()
    ...

Using .debug() is a great way to catch this kind of thing as it will print all events including disposal, eg:

session
    .rx
    .videoCaptureOutput(settings: videoSettings)
    .observeOn(MainScheduler.instance)
    .debug("Video Capture Output Observable:")
    .subscribe { [unowned self] (event) in
        switch event {
            case .next(let captureOutput):
                print("got a frame")
            case .error(let error):
                print("error: %@", "\(error)")
            case .completed:
                break // never happens
        }
    }
    .disposed(by: disposeBag)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM