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, e.g.:
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)