Swift - AVFoundation on OS X adding sublayer issue

944 views Asked by At

I am trying to get a preview of my video device in a custom view. But all I get is an empty window. I see that I have no problem accessing my camera. As soon as the app fires up i see my logitech cameras led turn on. I assume my problem is adding the the preview layer as a sublayer.

Here is my simple code:

import Cocoa
import AVFoundation

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

@IBOutlet weak var video: NSView!
@IBOutlet weak var window: NSWindow!

let captureSession = AVCaptureSession()
var captureDevice : AVCaptureDevice?
var previewLayer : AVCaptureVideoPreviewLayer?

func applicationDidFinishLaunching(aNotification: NSNotification) {

    captureSession.sessionPreset = AVCaptureSessionPresetLow
    let devices = AVCaptureDevice.devices()
    for device in devices {

        if (device.hasMediaType(AVMediaTypeVideo)) {
            captureDevice = device as? AVCaptureDevice
            println(captureDevice)
        }
    }

    if captureDevice != nil {
        beginSession()
    }


}

func beginSession() {

    println("begin")
    var err : NSError? = nil
    captureSession.addInput(AVCaptureDeviceInput(device: captureDevice, error: &err))

    if err != nil {
        println("error: \(err?.localizedDescription)")
    }



    previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    previewLayer!.frame = self.video.bounds
    previewLayer!.videoGravity = AVLayerVideoGravityResizeAspectFill
    self.video.layer?.addSublayer(previewLayer)
    captureSession.startRunning()
}

func applicationWillTerminate(aNotification: NSNotification) {
    // Insert code here to tear down your application
}


}
3

There are 3 answers

0
Koray Birand On BEST ANSWER

Solved it.. At Interface Builder, I needed to add a custom view in core animation.

0
Erich Kuester On

In Swift 2 the error handling has changed. If you use Main.storyboard than the following approach should work:

class ViewController: NSViewController {

let captureSession = AVCaptureSession()
var captureDevice: AVCaptureDevice?
var previewLayer: AVCaptureVideoPreviewLayer?
var previewPanel: NSView!

override func viewDidLoad() {
    super.viewDidLoad()
    captureSession.sessionPreset = AVCaptureSessionPresetLow
    let devices = AVCaptureDevice.devices()
    for device in devices {
        if (device.hasMediaType(AVMediaTypeVideo)) {
            captureDevice = device as? AVCaptureDevice
            //Swift.print("device: \(captureDevice)")
        }
    }
    Swift.print("beginning")
    do {
        let deviceInput = try AVCaptureDeviceInput(device: captureDevice)
        captureSession.addInput(deviceInput)
        // get the CustomView as preview panel
        previewPanel = self.view.subviews.first
        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer!.frame = self.view.bounds
        // add layer to preview
        previewLayer!.videoGravity = AVLayerVideoGravityResizeAspectFill
        previewPanel.layer?.addSublayer(previewLayer!)
        captureSession.startRunning()
    } catch let error as NSError {
        Swift.print("Error: no valid camera input in \(error.domain)")
    }
}
override var representedObject: AnyObject? {
    didSet {
        // Update the view, if already loaded.
    }
}

Don't forget to add the CustomView.

0
gdh On

For my small swift-base AVFoundation grabber, this Q&A post helped me. I noticed that the following checks are important to make for the "Custom View" in the Interface builder:

  • Attribute Inspector > Check "Can Draw Concurrently":
  • View Effects Inspector > Check your custom view on the "Core Animation Layer list", (maybe even uncheck the main view controller)