Thanks for reading.
I am creating a custom camera using AVFoundation.
When I maximize the wide angle with the ultra wide angle camera (when videoZoomFactor is minimized), the wide angle field of view is narrower compared to the apple default camera.
Looking at the metadata from the album, the focal length is 13mm for the apple default camera, while it is 16mm for the one I created. Below is an excerpt of the code.
Camera Settings
if let captureDevice = AVCaptureDevice.default(
.builtInTripleCamera,
for: .video,
position: .back
) {
self.captureDevice = captureDevice
} else if let captureDevice = AVCaptureDevice.default(
.builtInDualWideCamera,
for: .video,
position: .back
) {
self.captureDevice = captureDevice
} else if let captureDevice = AVCaptureDevice.default(
.builtInWideAngleCamera,
for: .video,
position: .back
) {
self.captureDevice = captureDevice
}
do {
let input = try AVCaptureDeviceInput(device: captureDevice)
let videoDataOutput = AVCaptureVideoDataOutput()
// Omitted
photoOutput = AVCapturePhotoOutput()
guard let photoOutput = photoOutput else { return }
photoOutput.isHighResolutionCaptureEnabled = true
session.sessionPreset = .photo
// Omitted
} catch {
}
for connection in session.connections {
connection.preferredVideoStabilizationMode = .cinematicExtended
}
zoom function
func zoom(zoomFactor: CGFloat, ramping: Bool = false) {
do {
try captureDevice?.lockForConfiguration()
self.zoomFactor = zoomFactor
if ramping {
captureDevice?.ramp(toVideoZoomFactor: zoomFactor, withRate: 10.0)
} else {
captureDevice?.videoZoomFactor = zoomFactor
}
captureDevice?.unlockForConfiguration()
} catch {
errorReportingService.reportError(error: error)
}
}
Test devices: iPhone 11, 12mini
Thanks for reading this far. I want to make it as wide angle as the apple default camera!
This app allows for a wider angle than the one I created. So I believe there is a way.
I'm running into an issue where isDepthDataDeliveryEnabled is false for me on an iPhone X and using .builtInDualCamera.
The weird thing is that this is only happening on one specific iPhone X. I've tested on other iPhone Xs and iPhone 11s, and everything works as expected. All devices I've tested on have been running iOS 13.3.
Just wanted to see if there might be any device-specific reason why capturing depth data might not be enabled on a device.
lazy var frontCamera: AVCaptureDevice? = {
let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes:
[.builtInDualCamera, .builtInDualWideCamera, .builtInUltraWideCamera, .builtInTelephotoCamera, .builtInWideAngleCamera, .builtInTrueDepthCamera, .builtInTripleCamera],
mediaType: .video, position: .back)
let devices = discoverySession.devices
for device in devices {
print("\(device) supports \(device.activeFormat.supportedDepthDataFormats)")
}
let depthSupportedDevices = devices.filter({ return $0.activeFormat.supportedDepthDataFormats.count != 0 })
print("depth supported devices are: \(depthSupportedDevices)")
return depthSupportedDevices.first
}()
func sessionPrepare() {
guard let captureDevice = frontCamera else { return }
do {
let deviceInput = try AVCaptureDeviceInput(device: captureDevice)
session.beginConfiguration()
session.sessionPreset = .photo
if session.canAddInput(deviceInput) {
session.addInput(deviceInput)
}
if session.canAddOutput(output) {
session.addOutput(output)
output.isDepthDataDeliveryEnabled = output.isDepthDataDeliverySupported
output.isPortraitEffectsMatteDeliveryEnabled = output.isPortraitEffectsMatteDeliverySupported
}
session.commitConfiguration()
} catch {
self.dismiss(animated: true, completion: nil)
}
}
We have barcode scanning functionality in our iOS app and we give the customer the ability to toggle the torch on and off as needed. On the iPhone X (and only on the iPhone X) when the AvCaptureSession is running and the torch is enabled, the video capture on the screen freezes. As soon as the torch is turned off again the video capture starts again. Has anyone run into this? I can't seem to find anything that points to a work around. Wondering if this a iPhone X bug?
I ran into this issue. After some experimentation, it turned out that obtaining the device to configure the torch must be done in the exact same way that you obtain the device when you configure your AVCaptureSession. E.G.:
let captureSession = AVCaptureSession()
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera], mediaType: AVMediaType.video, position: .back)
guard let captureDevice = deviceDiscoverySession.devices.first else {
print("Couldn't get a camera")
return
}
do {
let input = try AVCaptureDeviceInput(device: captureDevice)
captureSession!.addInput(input)
} catch {
print(error)
return
}
Use that exact method for obtaining the device when toggling the torch (flashlight) on and off. In this case, the lines:
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera], mediaType: AVMediaType.video, position: .back)
guard let device = deviceDiscoverySession.devices.first
Example:
func toggleTorch() {
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera], mediaType: AVMediaType.video, position: .back)
guard let device = deviceDiscoverySession.devices.first
else {return}
if device.hasTorch {
do {
try device.lockForConfiguration()
let on = device.isTorchActive
if on != true && device.isTorchModeSupported(.on) {
try device.setTorchModeOn(level: 1.0)
} else if device.isTorchModeSupported(.off){
device.torchMode = .off
} else {
print("Torch mode is not supported")
}
device.unlockForConfiguration()
} catch {
print("Torch could not be used")
}
} else {
print("Torch is not available")
}
}
I realize some code may be superfluous in the toggleTorch function, but I'm leaving it. Hope this helps.
Since AVCaptureDevice.devices is depreciated in iOS 10, I am trying to adjust this example code to AVCaptureDeviceDiscoverySession.
var error: NSError?
var captureSession: AVCaptureSession?
var backVideoDevice: AVCaptureDevice?
//let videoDevices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) // .devices DEPRECIATED
//iOS 10
let videoDevices = AVCaptureDeviceDiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: .back)
// Get back video device
if let videoDevices = videoDevices
{
for captureDevice in videoDevices
{
if (captureDevice as AnyObject).position == AVCaptureDevicePosition.back
{
backVideoDevice = captureDevice as? AVCaptureDevice
break
}
}
}
And here I am stuck, a error comes up at this line
for captureDevice in videoDevices
at the point videoDevices and says: Type 'AVCaptureDeviceDiscoverySession' does not conform to protocol 'Sequence'.
Where or what do I miss or oversee? Thx.
The function returns an object of type AVCaptureDeviceDiscoverySession, and you have to access the devices property of that to get the array you were expecting:
let session = AVCaptureDeviceDiscoverySession.init(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: .back)
if let device = session?.devices[0] {
backVideoDevice = device
}
Note that you no longer need to loop over all devices as the AVCaptureDeviceDiscoverySession is only returning devices with a position of .back in the first place. Since there will be only one of those, you'll find it at devices[0].
Before iOS 10 came out I was using the following code to get the video and audio capture for my video recorder:
for device in AVCaptureDevice.devices()
{
if (device as AnyObject).hasMediaType( AVMediaTypeAudio )
{
self.audioCapture = device as? AVCaptureDevice
}
else if (device as AnyObject).hasMediaType( AVMediaTypeVideo )
{
if (device as AnyObject).position == AVCaptureDevicePosition.back
{
self.backCameraVideoCapture = device as? AVCaptureDevice
}
else
{
self.frontCameraVideoCapture = device as? AVCaptureDevice
}
}
}
When iOS 10 finally came out, I received the following warning when I was running my code. Note that my video recorder was still working smoothly for about 2 weeks.
'devices()' was deprecated in iOS 10.0: Use AVCaptureDeviceDiscoverySession instead.
As I was running my code this morning, my video recorder stopped working. xCode8 does not give me any errors but the previewLayer for the camera capture is completely white. When I then start recording I receive the following error:
Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSLocalizedDescription=The operation could not be completed, NSUnderlyingError=0x17554440 {Error Domain=NSOSStatusErrorDomain Code=-12780 "(null)"}, NSLocalizedFailureReason=An unknown error occurred (-12780)}
I believe that has something to do with the fact that I am using the deprecated approach AVCaptureDevice.devices(). Hence, I was wondering how to use AVCaptureDeviceDiscoverySession instead?
Thank you for your help in advance!
You can get the front camera with the following:
AVCaptureDevice.default(.builtInWideAngleCamera, for: AVMediaType.video, position: .front)
The back camera:
AVCaptureDevice.default(.builtInWideAngleCamera, for: AVMediaType.video, position: .back)
And the microphone:
AVCaptureDevice.default(.builtInMicrophone, for: AVMediaType.audio, position: .unspecified)
Here's my code (Swift 3) to get camera position :
// Find a camera with the specified AVCaptureDevicePosition, returning nil if one is not found
func cameraWithPosition(_ position: AVCaptureDevicePosition) -> AVCaptureDevice?
{
if let deviceDescoverySession = AVCaptureDeviceDiscoverySession.init(deviceTypes: [AVCaptureDeviceType.builtInWideAngleCamera],
mediaType: AVMediaTypeVideo,
position: AVCaptureDevicePosition.unspecified) {
for device in deviceDescoverySession.devices {
if device.position == position {
return device
}
}
}
return nil
}
If you want, you can also get the new devicesTypes from iPhone 7+ (dual camera) by changing the deviceTypes array.
Here's a good read : https://forums.developer.apple.com/thread/63347
Swift 4, iOS 10+ and Xcode 10.1 replaces
if let cameraID = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front)?.localizedName {
//cameraID = "Front Camera"
}
with AVCaptureDevice.DiscoverySession implementation
if let cameraID = AVCaptureDevice.DiscoverySession.init(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera], mediaType: .video, position: .front).devices.first?.localizedName{
//cameraID = "Front Camera"
}
Need to wrap it with #available(iOS 10,*) check.
It works on Xcode 9.2 and Swift 4
AVCaptureDevice.default(.builtInWideAngleCamera, for: AVMediaType.video, position: .back)
https://developer.apple.com/documentation/avfoundation/avcapturedevice/2361508-default
Swift 3
For selecting the back camera:(also you can change .back as needed)
For selecting another deviceType simple add it inside the [ ] (i.e:
[deviceTypeCamera, AVCaptureDeviceType.builtInMicrophone]
(or create a private let... like I did in the code with the back camera)
private let position = AVCaptureDevicePosition.back
private let deviceTypeBackCamera = AVCaptureDeviceType.builtInWideAngleCamera
private func selectCaptureDevice() -> AVCaptureDevice? {
return AVCaptureDeviceDiscoverySession(deviceTypes: [deviceTypeBackCamera], mediaType: AVMediaTypeVideo, position: position).devices.first
}
example: iOS 11 Swift 4
override func viewDidLoad() {
super.viewDidLoad()
// Get the back-facing camera for capturing videos
// AVCaptureDevice.default(.builtInWideAngleCamera, for: AVMediaType.video, position: .back)
let deviceDiscoverySession = AVCaptureDevice.default(.builtInWideAngleCamera, for: AVMediaType.video, position: .back)
guard let captureDevice = deviceDiscoverySession 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)
} catch {
// If any error occurs, simply print it out and don't continue any more.
print(error)
return
}
// 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()
Try below code to get camera ID:
NSString *cameraID = nil;
NSArray *captureDeviceType = #[AVCaptureDeviceTypeBuiltInWideAngleCamera];
AVCaptureDeviceDiscoverySession *captureDevice =
[AVCaptureDeviceDiscoverySession
discoverySessionWithDeviceTypes:captureDeviceType
mediaType:AVMediaTypeVideo
position:AVCaptureDevicePositionUnspecified];
cameraID = [captureDevice.devices.lastObject localizedName];
Swift 4 (xCode 10.1)
This is what worked for me in the latest version of Swift. I did not see this answer, and it took me a while to suss out so here is how to get the front facing camera.
if let device = AVCaptureDevice.defaultDevice(withDeviceType: .builtInWideAngleCamera , mediaType: AVMediaTypeVideo, position: .front) {
//Do the camera thing here..
}
Simplified:
func getCamera(with position: AVCaptureDevice.Position) -> AVCaptureDevice? {
let deviceDescoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera], mediaType: .video, position: .unspecified)
return deviceDescoverySession.devices.first(where: { $0.position == position })
}
// and you can use like that:
guard let device = getCamera(with: .back) else { return }
For my video capture app I'm using the following code to get the mic, front and rear camera and I've tested this code from iOS 7 to 10.0.2.
var frontCamera : AVCaptureDevice?
var rearCamera : AVCaptureDevice?
captureSession = AVCaptureSession()
let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
let audioDevices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeAudio)
for mic in audioDevices {
audioDevice = mic as? AVCaptureDevice
audioCapturePossible = true
}
for device in devices {
if device.position == AVCaptureDevicePosition.Front {
frontCamera = device as? AVCaptureDevice
hasFrontCamera = true
}
else if device.position == AVCaptureDevicePosition.Back {
rearCamera = device as? AVCaptureDevice
hasRearCamera = true
}
}
In Swift 5 it is more easy
AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .front)
05/2019 :
//video
self.session = AVCaptureSession()
guard
let videoDeviceInput = try? AVCaptureDeviceInput(device: device!),
self.session!.canAddInput(videoDeviceInput)
else { return }
self.session!.addInput(videoDeviceInput)
//audio
guard
let audioDeviceInput = try? AVCaptureDeviceInput(device: mic!),
self.session!.canAddInput(audioDeviceInput)
else { return }
self.session!.addInput(audioDeviceInput)