AVCaptureDevice DiscoverySession for iOS 9.0 - ios

I'm developing a QR and Matrix Code reader app. I'm getting AVCaptureDeviceInput with AvCaptureDevice.DiscoverySession. My problem is that It is only available after iOS 10.0. How can I get it for fallback versions?
// Get the back-facing camera for capturing videos
if #available(iOS 10.0, *) {
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .back)
guard let captureDevice = deviceDiscoverySession.devices.first else {
print("Failed to get the camera device")
return
}
} else {
// Fallback on earlier versions
}
do {
// Get an instance of the AVCaptureDeviceInput class using the previous device object.
let input = try AVCaptureDeviceInput(device: captureDevice)
// Set the input device on the capture session.
captureSession.addInput(input)
// Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
let captureMetadataOutput = AVCaptureMetadataOutput()
captureSession.addOutput(captureMetadataOutput)
// Set delegate and use the default dispatch queue to execute the call back
captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
//TODO: Decide the data types!
captureMetadataOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.dataMatrix]
//captureMetadataOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.qr]
} catch {
// If any error occurs, simply print it out and don't continue any more.
print(error)
return
}

If you want to get list of AVCaptureDevice, you can use
let cameras = AVCaptureDevice.devices(for: AVMediaType.video)
in iOS 9

Related

Unable to remove audio input after recording video with camera Swift / AVFoundation

I'm currently trying to debug my video camera module that records video and audio with av foundation while mixing with background audio. It only adds the audio input when the user starts recording and should remove the audio input after the user stops recording. It successfully works the first time on app launch when the user starts recording but fails to remove the audio input after showing me the recorded preview with the following error:
2023-02-10 12:44:01.834776-0800 Camera[14061:986111] [avas] AVAudioSession_iOS.mm:1271 Deactivating an audio session that has running I/O. All I/O should be stopped or paused prior to deactivating the audio session.
Error occurred while removing audio device input: Error Domain=NSOSStatusErrorDomain Code=560030580 "(null)"
Here is my initial capture session code:
func setUp(){
do{
self.session.beginConfiguration()
let cameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front)
if cameraDevice != nil {
/* only add video in setup */
if self.session.canAddInput(videoInput) {
self.session.addInput(videoInput)
self.videoDeviceInput = videoInput
}
if self.session.canAddOutput(self.output){
self.session.addOutput(self.output)
}
if self.session.canAddOutput(self.photoOutput){
self.session.addOutput(self.photoOutput)
}
//for audio mixing, make sure this is default set to true
self.session.automaticallyConfiguresApplicationAudioSession = true
self.session.commitConfiguration()
}
}
catch{
print(error.localizedDescription)
}
}
Here is my start recording code that gets triggered when the users uses the camera module to start recording a video with audio that mixes with the background audio:
func startRecording() {
/* add audio input only when user starts recording */
do
{
try AVAudioSession.sharedInstance().setActive(false)
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.ambient)
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: AVAudioSession.CategoryOptions.mixWithOthers)
try AVAudioSession.sharedInstance().setMode(AVAudioSession.Mode.videoRecording)
try AVAudioSession.sharedInstance().setActive(true)
let audioDevice = AVCaptureDevice.default(for: .audio)
let audioInput = try AVCaptureDeviceInput(device: audioDevice!)
if self.session.canAddInput(audioInput){
self.session.automaticallyConfiguresApplicationAudioSession = false
self.session.addInput(audioInput)
}
} catch {
print("Can't Set Audio Session Category: \(error)")
}
// MARK: Temporary URL for recording Video
let tempURL = NSTemporaryDirectory() + "\(Date()).mov"
//Need to correct image orientation before moving further
if let videoOutputConnection = output.connection(with: .video) {
//For frontCamera settings to capture mirror image
if self.videoDeviceInput.device.position == .front {
videoOutputConnection.automaticallyAdjustsVideoMirroring = false
videoOutputConnection.isVideoMirrored = true
} else {
videoOutputConnection.automaticallyAdjustsVideoMirroring = true
}
}
output.startRecording(to: URL(fileURLWithPath: tempURL), recordingDelegate: self)
isRecording = true
}
Here is my stop recording method:
func stopRecording(){
output.stopRecording()
isRecording = false
self.flashOn = false
/* attempts to remove Audio input */
do{
try AVAudioSession.sharedInstance().setActive(false)
let audioDevice = AVCaptureDevice.default(for: .audio)
let audioInput = try AVCaptureDeviceInput(device: audioDevice!)
self.session.removeInput(audioInput)
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.ambient, mode: .default, options: [.mixWithOthers])
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print("Error occurred while removing audio device input: \(error)")
}
}
The stopRecording method outputs the error which makes the camera no longer attach the audio for future recordings but successfully works only for the first recording.
Basically, I feel like I'm really close but I've been splitting my hair trying to debug this for the past week with limited documents and solutions. It works for the first recording session with the audio and future recording sessions no longer have any audio attached to the video that the camera has recorded upon calling start recording again.

Unsupported type found - AVMetadataObject

I'm trying to implement barcode scanning in my iOS app, but for some reason I can't open the camera. I'm using Apple's AVFoundation framework for scanning.
This is my code for launching the camera and implement the scanner:
// Get the back-facing camera for capturing videos
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .back)
guard let captureDevice = deviceDiscoverySession.devices.first else {
print("Failed to get the camera device")
return
}
do {
// Get an instance of the AVCaptureDeviceInput class using the previous device object.
let input = try AVCaptureDeviceInput(device: captureDevice)
// Set the input device on the capture session.
captureSession?.addInput(input)
// Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
let captureMetadataOutput = AVCaptureMetadataOutput()
captureSession?.addOutput(captureMetadataOutput)
// Set delegate and use the default dispatch queue to execute the call back
captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
captureMetadataOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.qr]
// Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer.
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
videoPreviewLayer?.frame = view.layer.bounds
view.layer.addSublayer(videoPreviewLayer!)
// Start video capture.
captureSession?.startRunning()
// Initialize QR Code Frame to highlight the QR code
qrCodeFrameView = UIView()
if let qrCodeFrameView = qrCodeFrameView {
qrCodeFrameView.layer.borderColor = UIColor.green.cgColor
qrCodeFrameView.layer.borderWidth = 2
view.addSubview(qrCodeFrameView)
view.bringSubview(toFront: qrCodeFrameView)
}
} catch {
// If any error occurs, simply print it out and don't continue any more.
print(error)
return
}
Immediately as the screen loads, I get the following message printed to the console:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[AVCaptureMetadataOutput setMetadataObjectTypes:] Unsupported type found - use -availableMetadataObjectTypes'
*** First throw call stack:
(0x1824d2d8c 0x18168c5ec 0x18803eb44 0x1013f035c 0x1013f12ec 0x18c23e64c 0x18c35f870 0x18c244700 0x18c37a1a8 0x18c2c19e0 0x18c2b6890 0x18c2b51d0 0x18ca96d1c 0x18ca992c8 0x18ca92368 0x18247b404 0x18247ac2c 0x18247879c 0x182398da8 0x18437d020 0x18c3b5758 0x1013da1d8 0x181e29fc0)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
I couldn't find any solution on Stack Overflow or on any other website.
Could you please help me fixing this?
So I had the same error and I solved it by making small changes. Hope this helps(if you haven't resolved it already!).
let input = try AVCaptureDeviceInput(device: captureDevice)
//Make sure you have initialized the captureSession object
captureSession = AVCaptureSession()
captureSession?.addInput(input)
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
videoPreviewLayer?.frame = view.layer.bounds
view.layer.addSublayer(videoPreviewLayer!)
let captureMetadataOutput = AVCaptureMetadataOutput()
captureSession?.addOutput(captureMetadataOutput)
// Main Change
captureMetadataOutput.metadataObjectTypes = captureMetadataOutput.availableMetadataObjectTypes
//[AVMetadataObject.ObjectType.qr]
captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
captureSession?.startRunning()

__availableRawPhotoPixelFormatTypes is empty on iPhone 7+ and iOS11

I'm trying to capture RAW files with AVFoundation. However I'm getting empty array in __availableRawPhotoPixelFormatTypes
Here is my snippet
if self._photoOutput == nil {
self._photoOutput = AVCapturePhotoOutput()
print(self._photoOutput!.__availableRawPhotoPixelFormatTypes)
}
And the output is empty array []
What may cause this?
Here are three things that will cause the availableRawPhotoPixelFormatTypes array to be empty:
You are reading the availableRawPhotoPixelFormatTypes property before adding your _photoOutput to an AVCaptureSession with a video source.
You are using the dual camera input. If so, you can't capture RAW images.
You are using the front camera. If so, you can't capture RAW images.
Here is some modified sample code from an excellent Apple guide (see link below). I have copied from several places and updated it slightly for brevity, simplicity and better overview:
let session = AVCaptureSession()
let photoOutput = AVCapturePhotoOutput()
private func configureSession() {
// Get camera device.
guard let videoCaptureDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else {
print("Unable to get camera device.")
return
}
// Create a capture input.
guard let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice) else {
print("Unable to obtain video input for default camera.")
return
}
// Make sure inputs and output can be added to session.
guard session.canAddInput(videoInput) else { return }
guard session.canAddOutput(photoOutput) else { return }
// Configure the session.
session.beginConfiguration()
session.sessionPreset = .photo
session.addInput(videoInput)
// availableRawPhotoPixelFormatTypes is empty.
session.addOutput(photoOutput)
// availableRawPhotoPixelFormatTypes should not be empty.
session.commitConfiguration()
}
private func capturePhoto() {
// Photo settings for RAW capture.
let rawFormatType = kCVPixelFormatType_14Bayer_RGGB
// At this point the array should not be empty (session has been configured).
guard photoOutput.availableRawPhotoPixelFormatTypes.contains(NSNumber(value: rawFormatType).uint32Value) else {
print("No available RAW pixel formats")
return
}
let photoSettings = AVCapturePhotoSettings(rawPixelFormatType: rawFormatType)
photoOutput.capturePhoto(with: photoSettings, delegate: self)
}
// MARK: - AVCapturePhotoCaptureDelegate methods
func photoOutput(_ output: AVCapturePhotoOutput,
didFinishProcessingRawPhoto rawSampleBuffer: CMSampleBuffer?,
previewPhoto previewPhotoSampleBuffer: CMSampleBuffer?,
resolvedSettings: AVCaptureResolvedPhotoSettings,
bracketSettings: AVCaptureBracketedStillImageSettings?,
error: Error?) {
guard error == nil, let rawSampleBuffer = rawSampleBuffer else {
print("Error capturing RAW photo:\(error)")
return
}
// Do something with the rawSampleBuffer.
}
Apple's Photo Capture Guide:
https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/PhotoCaptureGuide/index.html
The availableRawPhotoPixelFormatTypes property:
https://developer.apple.com/documentation/avfoundation/avcapturephotooutput/1778628-availablerawphotopixelformattype)
iPhone camera capabilities:
https://developer.apple.com/library/content/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Cameras/Cameras.html
One supplyment to #Thomas's answer:
If you change AVCaptureDevice.activeFormat, AVCaptureSession.sessionPreset will be set to inputPriority automatically. In such situation, availableRawPhotoPixelFormatTypes will be empty too.

AVCaptureVideoPreviewLayer black screen

hello i had a working code with AVCaptureVideoPreviewLayer.. it's a part barcode reader:
let captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
do {
let input = try AVCaptureDeviceInput(device: captureDevice) as AVCaptureDeviceInput
session.addInput(input)
print("input done..")
} catch let error as NSError {
print(error)
}
let output = AVCaptureMetadataOutput()
output.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
session.addOutput(output)
output.metadataObjectTypes = output.availableMetadataObjectTypes
previewLayer = AVCaptureVideoPreviewLayer(session: session) as AVCaptureVideoPreviewLayer
previewLayer.frame = self.view.bounds
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
self.view.layer.addSublayer(previewLayer)
self.view.bringSubviewToFront(self.highlightView)
session.startRunning()
It was starting and running also, there wasn't any error messeage and i used on my old iPhone too and i could see the picture of the camera. But 2 days ago my iphone's been replaced, i didn't change anything in the code. Now the app starting but i can see only black screen.
Does anybody know what could cause this?
Thank you!
This is the code I used to test this out:
override func viewDidLoad() {
super.viewDidLoad()
var session = AVCaptureSession.new()
session.sessionPreset = AVCaptureSessionPresetMedium
let captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
let input = AVCaptureDeviceInput(device: captureDevice, error: nil) as AVCaptureDeviceInput
session.addInput(input)
print("input done..")
var previewLayer = AVCaptureVideoPreviewLayer(session: session) as AVCaptureVideoPreviewLayer
previewLayer.frame = self.view.bounds
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
self.view.layer.addSublayer(previewLayer)
session.startRunning()
}
I appears that the problem was that you did not pass in the NSError required by the AVCaptureDeviceInput. Xcode was not sure what the try statement was trying to do as well. I've simply passed in nil into the constructor for now, but you will want to make sure that you handle it. I also removed the part on setting up the output, as that was not relevant to my testing. Feel free to add that back in.
I tested this on my iPhone 4 running iOS 7.1 for reference.
From iOS 8 you need to ask for the permission, you can do something like this
if ([AVCaptureDevice respondsToSelector:#selector(requestAccessForMediaType: completionHandler:)]) {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted) {
// Got the permission
} else {
// Permission has been denied.
}
}];
} else {
// below ios 6
}
you can execute permission method in swifth
if AVCaptureDevice.respondsToSelector(Selector("requestAccessForMediaType"))
{
AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo, completionHandler: { (grandted:Bool) -> Void in
//Check if granted and if its true do camera work else permission denied
})
}
else
{
// Below iOS 6
}

Unable to get devices using AVCaptureDevice

I've managed to find some code that would give me access to the devices of a phone (such as the camera). The issue is that when I compile the code (and I'm printing the different devices) using Xcode, I get an empty array.
Here is what I wrote:
import UIKit
import AVFoundation
class ViewController: UIViewController {
let captureSession = AVCaptureSession()
var previewLayer : AVCaptureVideoPreviewLayer?
// If we find a device we'll store it here for later us
var captureDevice : AVCaptureDevice?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
captureSession.sessionPreset = AVCaptureSessionPresetHigh
let devices = AVCaptureDevice.devices()
println(devices)
// 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.Back) {
captureDevice = device as? AVCaptureDevice
if captureDevice != nil {
println("Capture device found")
beginSession()
}
}
}
}
}
func beginSession() {
var err : NSError? = nil
captureSession.addInput(AVCaptureDeviceInput(device: captureDevice, error: &err))
if err != nil {
println("error: \(err?.localizedDescription)")
}
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
self.view.layer.addSublayer(previewLayer)
previewLayer?.frame = self.view.layer.frame
captureSession.startRunning()
}
}
Do you have any ideas as to why I am getting an empty array?
If you're only running it in the simulator, the array will always be empty because it has no physical hardware to choose from. In fact, if you try to access physical hardware inside the Simulator, it will crash. If you plug a device in and still get an empty array, let me know.
first check the current status of the authorization
AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo)
more detail you can read this article
Check the current Status of Authorization for Swift 4
AVCaptureDevice.authorizationStatus(for: AVMediaType.video)

Resources