Front facing camera on iPhone preview mirrored - ios

I am using working with swift on xcode 9.2 to code up a quick preview app for my ios 11. My issue is that when I deploy the following code snippet to start a video preview using my front camera the preview is mirrored. Is there a way to avoid this mirroring? Or maybe mirror it again so it normalizes?
guard let captureDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: AVMediaType.video, position: .front) else {
print ("Error: no video devices available")
return false
}

Related

How to know does an iphone device support triple-cameras system?

In my project, I am trying to support triple cameras system. But I don't know how to check whether a device has triple cameras?
you can do this with following code either device has builtin dual back camera or wide angle back camera
var currentDevice:AVCaptureDevice?
if let device = AVCaptureDevice.default(.builtInDualCamera, for: AVMediaType.video, position: .back)
{
currentDevice = device
}
else if let device = AVCaptureDevice.default(.builtInTripleCamera, for: .video, position: .back)
{
currentDevice = device
}
else if let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: AVMediaType.video, position: .back)
{
currentDevice = device
}
else
{
print("Error: no camera available")
}
Hope this will helps you 😊
In the AVFoundation kit you have an AVCaptureDevice, which have an DeviceType which you can put as the default.
if let device = AVCaptureDevice.default(.builtInTripleCamera, for: .video, position: .back) {
}

iOS, How to do face tracking using the rear camera?

I'm planning to use ARKit's camera feed as input into Apple's Vision API so I can recognize people's faces in screen-space, no depth information required. To simplify the process, I'm attempting to modify Apple's face tracking over frames example here: Tracking the User’s Face in Real Time
I thought that I could simply change the function here:
fileprivate func configureFrontCamera(for captureSession: AVCaptureSession) throws -> (device: AVCaptureDevice, resolution: CGSize) {
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: .front)
if let device = deviceDiscoverySession.devices.first {
if let deviceInput = try? AVCaptureDeviceInput(device: device) {
if captureSession.canAddInput(deviceInput) {
captureSession.addInput(deviceInput)
}
if let highestResolution = self.highestResolution420Format(for: device) {
try device.lockForConfiguration()
device.activeFormat = highestResolution.format
device.unlockForConfiguration()
return (device, highestResolution.resolution)
}
}
}
throw NSError(domain: "ViewController", code: 1, userInfo: nil)
}
In the first line of the function, one of the arguments is .front for front-facing camera. I changed this to .back. This successfully gives me the rear-facing camera. However, the recognition region seems a little bit choppy, and as soon as it fixates on a face in the image, Xcode reports the error:
VisionFaceTrack[877:54517] [ServicesDaemonManager] interruptionHandler is called. -[FontServicesDaemonManager connection]_block_invoke
Message from debugger: Terminated due to memory issue
In other words, the program crashes when a face is recognized, it seems. Clearly there is more to this than simply changing the constant used. Perhaps there is a buffer somewhere with the wrong size, or a wrong resolution. May I have help figuring out what may be wrong here?
A better solution would also include information about how to achieve this with arkit's camera feed, but I'm pretty sure it's the same idea with the CVPixelBuffer.
How would I adapt this example to use the rear camera?
EDIT: I think the issue is that my device has too little memory to support the algorithm using the back camera, as the back camera has a higher resolution.
However, even on another higher performance device, the tracking quality is pretty bad. --yet the vision algorithm only needs raw images, doesn't it? In that case, shouldn't this work? I can't find any examples online of using the back camera for face tracking.
Here's how I adapted the sample to make it work on my iPad Pro.
1) Download the sample project from here: Tracking the User’s Face in Real Time.
2) Change the function which loads the front facing camera to use back facing. Rename it to configureBackCamera and call this method setupAVCaptureSession:
fileprivate func configureBackCamera(for captureSession: AVCaptureSession) throws -> (device: AVCaptureDevice, resolution: CGSize) {
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: .back)
if let device = deviceDiscoverySession.devices.first {
if let deviceInput = try? AVCaptureDeviceInput(device: device) {
if captureSession.canAddInput(deviceInput) {
captureSession.addInput(deviceInput)
}
if let highestResolution = self.highestResolution420Format(for: device) {
try device.lockForConfiguration()
device.activeFormat = highestResolution.format
device.unlockForConfiguration()
return (device, highestResolution.resolution)
}
}
}
throw NSError(domain: "ViewController", code: 1, userInfo: nil)
}
3) Change the implementation of the method highestResolution420Format. The problem is, now that the back-facing camera is used, you have access to formats with much higher resolution than with front-facing camera, which can impact the performance of the tracking. You need to adapt to your use case, but here's an example of limiting the resolution to 1080p.
fileprivate func highestResolution420Format(for device: AVCaptureDevice) -> (format: AVCaptureDevice.Format, resolution: CGSize)? {
var highestResolutionFormat: AVCaptureDevice.Format? = nil
var highestResolutionDimensions = CMVideoDimensions(width: 0, height: 0)
for format in device.formats {
let deviceFormat = format as AVCaptureDevice.Format
let deviceFormatDescription = deviceFormat.formatDescription
if CMFormatDescriptionGetMediaSubType(deviceFormatDescription) == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange {
let candidateDimensions = CMVideoFormatDescriptionGetDimensions(deviceFormatDescription)
if (candidateDimensions.height > 1080) {
continue
}
if (highestResolutionFormat == nil) || (candidateDimensions.width > highestResolutionDimensions.width) {
highestResolutionFormat = deviceFormat
highestResolutionDimensions = candidateDimensions
}
}
}
if highestResolutionFormat != nil {
let resolution = CGSize(width: CGFloat(highestResolutionDimensions.width), height: CGFloat(highestResolutionDimensions.height))
return (highestResolutionFormat!, resolution)
}
return nil
}
4) Now the tracking will work, but the face positions will not be correct. The reason is that UI presentation is wrong, because original sample was designed for front facing cameras with mirrored display, while the back facing camera doesn't need mirroring.
In order to tweak for this, simply change updateLayerGeometry() method. Specifically, you need to change this:
// Scale and mirror the image to ensure upright presentation.
let affineTransform = CGAffineTransform(rotationAngle: radiansForDegrees(rotation))
.scaledBy(x: scaleX, y: -scaleY)
overlayLayer.setAffineTransform(affineTransform)
into this:
// Scale the image to ensure upright presentation.
let affineTransform = CGAffineTransform(rotationAngle: radiansForDegrees(rotation))
.scaledBy(x: -scaleX, y: -scaleY)
overlayLayer.setAffineTransform(affineTransform)
After this, the tracking should work and the results should be correct.

error noCamerasAvailable, which is only on iPhone <8+ Swift

i have a strange issue which I can notice only on devices older than iPhone 8+,
I don't know how can I fix that but error message is very simple 'noCamerasAvailable,'
Everything with permissions should be okay cause it works on my iPhone XS Max and my friend's iPhone X. That is my simply code to display camera view
cameraController.prepare {(error) in
if let error = error {
print("Camera error:")
print(error)
}
try? self.cameraController.displayPreview(on: self.view)
}
Fixed I had to replace this
let session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera], mediaType: AVMediaType.video, position: .unspecified)
to this
let session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .unspecified)

I get this ridiculous value when reading IOS camera's lens position

Using this code with the BuiltInWideAngleCamera in Swift on an iPhone XS MAX running iOS 12.1.2:
let lensPos: Float = AVCaptureDevice.currentLensPosition;
lockCameraForSettings();
self.inputDevice?.setFocusModeLocked(lensPosition: LensPos, completionHandler: { (time) -> Void in})
unlockCameraForShooting();
results in a crash:
[AVCaptureDevice setFocusModeLockedWithLensPosition:completionHandler:] The passed lensPosition -340282346638528859811704183484516925440.000000 is out-of-range [0, 1]'
The camera is running and visibly in focus on the screen preview. How is it possible for it to be in this configuration?
Inserting a constant value between 0-1 works, at least in that it does not throw an error.
I believe you mean to use .lensPosition instead of .currentLensPosition which is a special constant representing the position of the lens. You can only access .lensPosition when you are referencing a instance of type AVCaptureDevice.
var captureDevice: AVCaptureDevice?
// Plus models and X's
if let device = AVCaptureDevice.default(.builtInDualCamera,
for: .video, position: .back) {
captureDevice = device
// Single Lens devices.
} else if let device = AVCaptureDevice.default(.builtInWideAngleCamera,
for: .video, position: .back) {
captureDevice = device
} else {
// No camera was found, is it broke?
print("Missing expected back camera device.")
}
if let device = captureDevice {
// We have a device, do something with it.
print(device.lensPosition)
}

iOS camera view opening up at black intermittently

I have a viewcontroller which has a button that calls a second viewcontroller, which adds a video sublayer and calls the camera.
The code has been working fine until I tried adding other things like another button to the second viewcontroller, then, it would sometimes work and sometimes not work.
By "not work" I mean it would open up a black screen with nothing at all. Does not respond to anything.
I've deleted the buttons / code etc but it hasn't fixed anything.
It seems it would sometimes just work. I.e. after it works I can add a button or change code and it would work and then it would show black screen again.
There are no build errors and trace and it basically is sitting there waiting for me to do something (like press the record button) but nothing is showing.
I've read that I should "bringsubviewtofront" but that doesn't seem to do anything.
Any suggestions?
Thanks in advance.
UPDATE: I think I found something related. I was trying to do a programmatically position a button on the screen using CGRect and part of that involved getting the text view's width and height.
I found that the code crashed with "expected to find optional value but found nil" message, i.e. I couldn't do anything like: textView.frame.width, textView.frame.height, textView.translatesAutoresizingMaskIntoConstraints = false etc.
At first I thought it was my code but after trying it on another VC using the same code, it suddenly started working again, i.e. I get values for textView.frame.width and textView.frame.height.
And my camera started showing preview!
So I reckon when the preview is black, then my buttons and text views have no values.
let captureSession = AVCaptureSession()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
captureSession.sessionPreset = AVCaptureSession.Preset.high
// loop through all devices looking for cameras
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera], mediaType: AVMediaType.video, position: AVCaptureDevice.Position.unspecified)
let devices = deviceDiscoverySession.devices
for device in devices {
if (device.hasMediaType(AVMediaType.video)) {
if device.position == AVCaptureDevice.Position.back {
backCamera = device
} else if device.position == AVCaptureDevice.Position.front {
frontCamera = device
}
}
}
currentDevice = frontCamera
// look through all devices looking for microphone
let audioDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [AVCaptureDevice.DeviceType.builtInMicrophone], mediaType: AVMediaType.audio, position: AVCaptureDevice.Position.unspecified)
let audioDevices = audioDiscoverySession.devices
for audioDevice in audioDevices {
if (audioDevice.hasMediaType(AVMediaType.audio)) {
audioCapture = audioDevice
}
}
// set up input output
do {
// setup camera input
let captureDeviceInput = try AVCaptureDeviceInput(device: currentDevice!)
captureSession.addInput(captureDeviceInput)
// setup audio input
let captureDeviceAudio = try AVCaptureDeviceInput(device: audioCapture!)
captureSession.addInput(captureDeviceAudio)
videoFileOutput = AVCaptureMovieFileOutput()
captureSession.addOutput(videoFileOutput!)
} catch {
print(error)
}
cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
cameraPreviewLayer?.connection?.videoOrientation = currentVideoOrientation()
cameraPreviewLayer?.frame = self.view.frame
self.view.layer.insertSublayer(cameraPreviewLayer!, at: 0)
captureSession.startRunning()
}
OK, I find out how to resolve it but I don't know why it's doing it, other than it's a bug in Xcode.
It seems the problem has nothing to do with the video sublayer and its code.
I have Text Views and buttons etc on this ViewController.
I found that if I change the size of a button or TextView, e.g. increase and decrease the size of a text view, then the problem goes away.
The problem comes back if you then change something, e.g. code or move buttons around etc. but if you go back to the Text View and change the size, then it'll work again.
That's the workaround but I don't know what triggers this problem.
Check if you have provided the code to ask for the permission to your app to access the camera. If you have added the code but not allowed the app to access camera while it was asking permission, then got to settings > app name > camera and switch on the access. If not add this:
//Permission for Camera
AVCaptureDevice.requestAccess(for: AVMediaType.video) { response in
if response {
//access granted
} else {
//Take required action
}
}

Resources