Why is AVCaptureSession method canAddOutput returning false? - ios

I'm trying to build a camera app, and I'm trying to set up my capture session within viewDidLoad() in my main view controller. For some reason, whenever I run the app on my phone, AVCaptureSession method canAddOutput is evaluated as false:
var captureSession: AVCaptureSession!
var photoOutput: AVCapturePhotoOutput!
var previewLayer : AVCaptureVideoPreviewLayer!
//MARK: Outlets
#IBOutlet weak var previewView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
captureSession = AVCaptureSession()
captureSession.sessionPreset = AVCaptureSessionPresetPhoto
//Ask permission to camera
let device = AVCaptureDevice.defaultDevice(withDeviceType: .builtInWideAngleCamera, mediaType: AVMediaTypeVideo, position: .back)
AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { (granted: Bool) in
if granted {
print("granted")
//Set up session
if let input = try? AVCaptureDeviceInput(device: device) {
print("Input = device")
if (self.captureSession.canAddInput(input)) {
self.captureSession.addInput(input)
print("Input added to capture session")
if (self.captureSession.canAddOutput(self.photoOutput)) {
print("Output added to capture session")
self.captureSession.addOutput(self.photoOutput)
self.previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
self.previewLayer.frame = self.previewView.bounds
self.previewView.layer.addSublayer(self.previewLayer!)
self.captureSession.startRunning()
print("Session is running")
}
}
}
}
else {
print("Goodbye")
}
})
}
Unfortunately, I can only get it to print up until "Input added to capture session". Any suggestions would help - thanks!

You have to remove previous outputs added in session. you can use for loop for that.
for outputs in captureSession.outputs{ captureSession.removeOutput(outputs) }
then try to add new out put

Related

Camera preview layer makes button invisible

I'm trying to add a button over a camera preview but it doesn't show up when I run the program (I have constraints). I looked into the code and tried to debug but I'm new to swift and Xcode and I'm new to debugging in general. I saw that when I commented out the camera preview layer the button showed up. Thanks!
import UIKit
import AVFoundation
import QuartzCore
class View1: UIViewController , AVCaptureVideoDataOutputSampleBufferDelegate{
let captureSession = AVCaptureSession()
var previewLayer:CALayer!
var captureDevice:AVCaptureDevice!
#IBOutlet weak var cameraView:UIView!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
prepareCamera()
}
func prepareCamera() {
captureSession.sessionPreset = AVCaptureSessionPreset1920x1080
if let availableDevices = AVCaptureDeviceDiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: .back).devices {
captureDevice = availableDevices.first
beginSession()
}
}
func beginSession() {
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: captureDevice)
captureSession.addInput(captureDeviceInput)
} catch {
print(error.localizedDescription)
//Figure out what to do here
}
if let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) {
self.previewLayer = previewLayer
self.view.layer.addSublayer(self.previewLayer)
self.previewLayer.frame = self.view.layer.frame
captureSession.startRunning()
let dataOutput = AVCaptureVideoDataOutput()
dataOutput.videoSettings = [(kCVPixelBufferPixelFormatTypeKey as NSString): NSNumber(value: kCVPixelFormatType_32BGRA)]
dataOutput.alwaysDiscardsLateVideoFrames = true
if captureSession.canAddOutput(dataOutput){
captureSession.addOutput(dataOutput)
captureSession.commitConfiguration()
}
let queue = DispatchQueue(label: "com.PhotoAllergy.captureQueue")
dataOutput.setSampleBufferDelegate(self, queue: queue)
}
}
}
Maybe you could try setting the zPosition of the button to 1 or higher. YourButtonName.layer.zPostion = 2
Apple Documentation on ZPosition
You just have to add button as subview to view that is working as a preview for your avcamera.
class RecordVC {
#IBOutlet weak var vwRecordVideo : UIView!
#IBOutlet weak var btnGallary : UIButton!
override func viewDidLoad() {
super.viewDidLoad()
vwRecordVideo.addSubview(cameraButton)
vwRecordVideo.addSubview(btnGallary)
}
}

Integrating Custom Camera View AVCaptureDevice

I'm trying to integrate a custom camera view and following some slightly outdated code whilst doing so. I've had several errors but believed I've fixed them bar 2.
Here is the current code so far:
import Foundation
import AVFoundation
import UIKit
class setupView : UIViewController {
#IBOutlet var cameraView: UIView!
#IBOutlet var nameTextField: UITextField!
var captureSession = AVCaptureSession()
var stillImageOutput = AVCapturePhotoOutput()
var previewLayer = AVCaptureVideoPreviewLayer()
override func viewDidLoad() {
let session = AVCaptureDeviceDiscoverySession.init(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: .back)
if let device = session?.devices[0] {
if device.position == AVCaptureDevicePosition.back {
do {
let input = try AVCaptureDeviceInput(device: device )
if captureSession.canAddInput(input){
captureSession.addInput(input)
stillImageOutput.outputSettings = [AVVideoCodecKey : AVVideoCodecJPEG]
if captureSession.canAddOutput(stillImageOutput) {
captureSession.addOutput(stillImageOutput)
captureSession.startRunning()
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.AVLayerVideoGravityResizeAspectFill
previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.portrait
cameraView.layer.addSublayer(previewLayer)
previewLayer.bounds = cameraView.frame
previewLayer.position = CGPoint(x: cameraView.frame.width / 2, y:cameraView.frame.height / 2)
}
}
} catch {
}
}
}
}
#IBAction func takePhoto(_ sender: Any) {
}
#IBAction func submitAction(_ sender: Any) {
}
}
I'm currently getting 2 errors:
"Value of type AVCapturePhotoOutput" has no member "outputSettings"
"Value of type "AVCaptureVideoPreviewLayer" has no member
"AVLayerVideoGravityResizeAspectFill"
You are almost there. The problem is some of the AVFoundation classes are deprecated and there is more than one way to take a photo now. Here is the issues with your code.
"Value of type AVCapturePhotoOutput" has no member "outputSettings"
It is because actually AVCapturePhotoOutput don't have any member defined as outputSettings. Check out full documentation of AVCapturePhotoOutput
Actually outputSettings is member of AVCaptureStillImageOutput and the same is deprecated from iOS 10.0
"Value of type "AVCaptureVideoPreviewLayer" has no member
"AVLayerVideoGravityResizeAspectFill"
Again the same mistake, as per your code there is no member for AVCaptureVideoPreviewLayer. In case if you want to set the video preview layer set it like below.
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
As like you mentioned the code is outdated and its using deprecated AVCaptureStillImageOutput
If really want to use AVCapturePhotoOutput then you should follow the below steps.
These are the steps to capture a photo.
Create an AVCapturePhotoOutput object. Use its properties to determine supported capture settings and to enable certain features (for example, whether to capture Live Photos).
Create and configure an AVCapturePhotoSettings object to choose features and settings for a specific capture (for example, whether to enable image stabilization or flash).
Capture an image by passing your photo settings object to the capturePhoto(with:delegate:) method along with a delegate object implementing the AVCapturePhotoCaptureDelegate protocol. The photo capture output then calls your delegate to notify you of significant events during the capture process.
have this below code on your clickCapture method and don't forgot to confirm and implement to delegate in your class.
let settings = AVCapturePhotoSettings()
let previewPixelType = settings.availablePreviewPhotoPixelFormatTypes.first!
let previewFormat = [kCVPixelBufferPixelFormatTypeKey as String: previewPixelType,
kCVPixelBufferWidthKey as String: 160,
kCVPixelBufferHeightKey as String: 160,
]
settings.previewPhotoFormat = previewFormat
self.cameraOutput.capturePhoto(with: settings, delegate: self)
if you would like to know the different way to capturing photo from avfoundation check out my previous SO answer
Also Apple documentation explains very clear for How to use AVCapturePhotoOutput
import AVFoundation
import Foundation
#IBOutlet weak var mainimage: UIImageView!
let captureSession = AVCaptureSession()
let stillImageOutput = AVCaptureStillImageOutput()
var previewLayer : AVCaptureVideoPreviewLayer?
var captureDevice : AVCaptureDevice?
override func viewDidLoad() {
super.viewDidLoad()
captureSession.sessionPreset = AVCaptureSessionPresetHigh
if let devices = AVCaptureDevice.devices() as? [AVCaptureDevice] {
// Loop through all the capture devices on this phone
for device in devices {
// Make sure this particular device supports video
if (device.hasMediaType(AVMediaTypeVideo)) {
// Finally check the position and confirm we've got the back camera
if(device.position == AVCaptureDevicePosition.front) {
captureDevice = device
if captureDevice != nil {
print("Capture device found")
beginSession()
}
}
}
}
}
}
func beginSession() {
do {
try captureSession.addInput(AVCaptureDeviceInput(device: captureDevice))
stillImageOutput.outputSettings = [AVVideoCodecKey:AVVideoCodecJPEG]
if captureSession.canAddOutput(stillImageOutput) {
captureSession.addOutput(stillImageOutput)
}
}
catch {
print("error: \(error.localizedDescription)")
}
guard let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) else {
print("no preview layer")
return
}
self.view.layer.addSublayer(previewLayer)
previewLayer.frame = self.view.layer.frame
captureSession.startRunning()
self.view.addSubview(mainimage)
}
This code is working in my app

Capturing image and setting it to UIImageView

I am trying to capture an Image and set it to the UIImageView, hence to create the camera I have the following code:
class HomeController: BaseController, UIImagePickerControllerDelegate {
var detector: AFDXDetector?
var captureSession : AVCaptureSession?
var stillImageOutput : AVCapturePhotoOutput?
var previewLayer : AVCaptureVideoPreviewLayer?
var camera : AVCaptureDevice!
#IBOutlet weak var cameraBtn: UIButton!
#IBOutlet weak var cameraView: UIView!
#IBOutlet weak var cameraImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
startCamera()
}
func startCamera() {
do {
captureSession = AVCaptureSession()
camera = AVCaptureDevice.defaultDevice(withDeviceType: .builtInWideAngleCamera, mediaType: AVMediaTypeVideo, position: .front)
captureSession?.sessionPreset = AVCaptureSessionPreset1280x720
let input = try AVCaptureDeviceInput(device: camera)
if (captureSession?.canAddInput(input))!{
captureSession?.addInput(input)
stillImageOutput = AVCapturePhotoOutput()
if (captureSession?.canAddOutput(stillImageOutput))!{
print("output added")
captureSession?.canAddOutput(stillImageOutput)
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer?.videoGravity = AVLayerVideoGravityResizeAspect
previewLayer?.connection.videoOrientation = AVCaptureVideoOrientation.portrait
cameraView.layer.addSublayer(previewLayer!)
captureSession?.startRunning()
}
}
} catch {
}
}
#IBAction func cameraBtnPressed(_ sender: Any) {
if (stillImageOutput?.connection(withMediaType: AVMediaTypeVideo)) != nil
{
print("video connection detected")
}
}
}
For some reason, the print statement "video connection detected" doesn't get called although the camera is working
Does anyone else know why?
Within the if statment of captureSession?.canAddOutput(stillImageOutput) change captureSession?.canAddOutput(stillImageOutput) to .addOutput

get a SubLayer with swift2 and Xcode 7

I would save a sublayer. But I can not get a target.
I want to save the layer that is displayed on the screen that contains the view of the camera. When I want it back, I get a white image. I concluded that I targeted my male layer when I record in dernnière function.
Here is my code:
#IBOutlet weak var blutEffect: UIVisualEffectView!
#IBOutlet weak var background: UIView! // vu d'affichage
var previewLayer = AVCaptureVideoPreviewLayer()
var captureSession = AVCaptureSession()
override func viewDidLoad() {
super.viewDidLoad()
camera()
}
#IBAction func takePhoto(sender: AnyObject) {
captureSession.stopRunning()
}
func camera () {
captureSession = AVCaptureSession()
previewLayer = AVCaptureVideoPreviewLayer()
var captureDevice : AVCaptureDevice?
let devices = AVCaptureDevice.devices()
captureSession.sessionPreset = AVCaptureSessionPresetHigh
background.layer.sublayers?.removeAll()
for device in devices {
if (device.hasMediaType(AVMediaTypeVideo)) {
if(device.position == AVCaptureDevicePosition.Back) {
captureDevice = device as? AVCaptureDevice
if captureDevice != nil {
do {
try captureSession.addInput(AVCaptureDeviceInput(device: captureDevice))
} catch _ as NSError {
print("ERROR")
}
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
self.background.layer.addSublayer(previewLayer)
previewLayer.frame = self.background.layer.frame
captureSession.startRunning()
}
}
}
}
}
func screenShotMethod() {
UIGraphicsBeginImageContext(background.layer.frame.size)
background.layer.renderInContext(UIGraphicsGetCurrentContext())
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
//Save it to the camera roll
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}
Thanks for your help !

AVCapture Session To Capture Image SWIFT

I have created an AVCaptureSession to capture video output and display it to the user via UIView. Now I want to be able to click a button (takePhoto method) and display the image from the session in an UIImageView. I have tried to iterate through each devices connection and try to save the output but that hasnt worked. The code I have is below
let captureSession = AVCaptureSession()
var stillImageOutput: AVCaptureStillImageOutput!
#IBOutlet var imageView: UIImageView!
#IBOutlet var cameraView: UIView!
// If we find a device we'll store it here for later use
var captureDevice : AVCaptureDevice?
override func viewDidLoad() {
// Do any additional setup after loading the view, typically from a nib.
super.viewDidLoad()
println("I AM AT THE CAMERA")
captureSession.sessionPreset = AVCaptureSessionPresetLow
self.captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
if(captureDevice != nil){
beginSession()
}
}
func beginSession() {
self.stillImageOutput = AVCaptureStillImageOutput()
self.captureSession.addOutput(self.stillImageOutput)
var err : NSError? = nil
self.captureSession.addInput(AVCaptureDeviceInput(device: self.captureDevice, error: &err))
if err != nil {
println("error: \(err?.localizedDescription)")
}
var previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
self.cameraView.layer.addSublayer(previewLayer)
previewLayer?.frame = self.cameraView.layer.frame
captureSession.startRunning()
}
#IBAction func takePhoto(sender: UIButton) {
self.stillImageOutput.captureStillImageAsynchronouslyFromConnection(self.stillImageOutput.connectionWithMediaType(AVMediaTypeVideo)) { (buffer:CMSampleBuffer!, error:NSError!) -> Void in
var image = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer)
var data_image = UIImage(data: image)
self.imageView.image = data_image
}
}
}
You should try adding a new thread when adding input and outputs to the session before starting it. In Apple's documentation they state
Important: The startRunning method is a blocking call which can take some time, therefore you should perform session setup on a serial queue so that the main queue isn't blocked (which keeps the UI responsive). See AVCam for iOS for the canonical implementation example.
Try using a dispatch in the create session method such as below
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { // 1
self.captureSession.addOutput(self.stillImageOutput)
self.captureSession.addInput(AVCaptureDeviceInput(device: self.captureDevice, error: &err))
self.captureSession.sessionPreset = AVCaptureSessionPresetPhoto
if err != nil {
println("error: \(err?.localizedDescription)")
}
var previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
previewLayer?.frame = self.cameraView.layer.bounds
previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
dispatch_async(dispatch_get_main_queue(), { // 2
// 3
self.cameraView.layer.addSublayer(previewLayer)
self.captureSession.startRunning()
});
});

Resources