Using this tutorial here: http://www.musicalgeometry.com/?p=1297 I have created a custom overlay and image capture with AVCaptureSession
.
I am attempting to allow the user to switch between the front and back camera. Here is my code in CaptureSessionManager
to switch cameras:
- (void)addVideoInputFrontCamera:(BOOL)front { NSArray *devices = [AVCaptureDevice devices]; AVCaptureDevice *frontCamera; AVCaptureDevice *backCamera; for (AVCaptureDevice *device in devices) { //NSLog(@"Device name: %@", [device localizedName]); if ([device hasMediaType:AVMediaTypeVideo]) { if ([device position] == AVCaptureDevicePositionBack) { //NSLog(@"Device position : back"); backCamera = device; } else { //NSLog(@"Device position : front"); frontCamera = device; } } } NSError *error = nil; if (front) { AVCaptureDeviceInput *frontFacingCameraDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:frontCamera error:&error]; if (!error) { if ([[self captureSession] canAddInput:frontFacingCameraDeviceInput]) { [[self captureSession] addInput:frontFacingCameraDeviceInput]; } else { NSLog(@"Couldn't add front facing video input"); } } } else { AVCaptureDeviceInput *backFacingCameraDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:backCamera error:&error]; if (!error) { if ([[self captureSession] canAddInput:backFacingCameraDeviceInput]) { [[self captureSession] addInput:backFacingCameraDeviceInput]; } else { NSLog(@"Couldn't add back facing video input"); } } } }
Now in my custom overlay controller I initialize everything like so in viewDidLoad
:
[self setCaptureManager:[[CaptureSessionManager alloc] init]]; [[self captureManager] addVideoInputFrontCamera:NO]; // set to YES for Front Camera, No for Back camera [[self captureManager] addStillImageOutput]; [[self captureManager] addVideoPreviewLayer]; CGRect layerRect = [[[self view] layer] bounds]; [[[self captureManager] previewLayer] setBounds:layerRect]; [[[self captureManager] previewLayer] setPosition:CGPointMake(CGRectGetMidX(layerRect),CGRectGetMidY(layerRect))]; [[[self view] layer] addSublayer:[[self captureManager] previewLayer]]; [[_captureManager captureSession] startRunning];
The switch camera button is connected to a method called switchCamera
. I have tried this:
- (void)switchCameraView:(id)sender { [[self captureManager] addVideoInputFrontCamera:YES]; // set to YES for Front Camera, No for Back camera }
When calling this, I get the error NSLog
from the CaptureSessionManager
and I cannot figure out why. In viewDidLoad
, if I set the fontCamera
to YES
, it shows the front camera but cannot switch to back, and vice versa.
Any ideas on how to get it to switch properly?
You first need to remove the existing AVCameraInput from the AVCaptureSession and then add a new AVCameraInput to the AVCaptureSession. The following works for me (under ARC):
-(IBAction)switchCameraTapped:(id)sender { //Change camera source if(_captureSession) { //Indicate that some changes will be made to the session [_captureSession beginConfiguration]; //Remove existing input AVCaptureInput* currentCameraInput = [_captureSession.inputs objectAtIndex:0]; [_captureSession removeInput:currentCameraInput]; //Get new input AVCaptureDevice *newCamera = nil; if(((AVCaptureDeviceInput*)currentCameraInput).device.position == AVCaptureDevicePositionBack) { newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront]; } else { newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack]; } //Add input to session NSError *err = nil; AVCaptureDeviceInput *newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:newCamera error:&err]; if(!newVideoInput || err) { NSLog(@"Error creating capture device input: %@", err.localizedDescription); } else { [_captureSession addInput:newVideoInput]; } //Commit all the configuration changes at once [_captureSession commitConfiguration]; } } // Find a camera with the specified AVCaptureDevicePosition, returning nil if one is not found - (AVCaptureDevice *) cameraWithPosition:(AVCaptureDevicePosition) position { NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; for (AVCaptureDevice *device in devices) { if ([device position] == position) return device; } return nil; }
Swift 4/5
@IBAction func switchCameraTapped(sender: Any) { //Change camera source if let session = captureSession { //Remove existing input guard let currentCameraInput: AVCaptureInput = session.inputs.first else { return } //Indicate that some changes will be made to the session session.beginConfiguration() session.removeInput(currentCameraInput) //Get new input var newCamera: AVCaptureDevice! = nil if let input = currentCameraInput as? AVCaptureDeviceInput { if (input.device.position == .back) { newCamera = cameraWithPosition(position: .front) } else { newCamera = cameraWithPosition(position: .back) } } //Add input to session var err: NSError? var newVideoInput: AVCaptureDeviceInput! do { newVideoInput = try AVCaptureDeviceInput(device: newCamera) } catch let err1 as NSError { err = err1 newVideoInput = nil } if newVideoInput == nil || err != nil { print("Error creating capture device input: \(err?.localizedDescription)") } else { session.addInput(newVideoInput) } //Commit all the configuration changes at once session.commitConfiguration() } } // Find a camera with the specified AVCaptureDevicePosition, returning nil if one is not found func cameraWithPosition(position: AVCaptureDevice.Position) -> AVCaptureDevice? { let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .unspecified) for device in discoverySession.devices { if device.position == position { return device } } return nil }
Swift 3 Edit (Combined with François-Julien Alcaraz answer):
@IBAction func switchCameraTapped(sender: Any) { //Change camera source if let session = captureSession { //Indicate that some changes will be made to the session session.beginConfiguration() //Remove existing input guard let currentCameraInput: AVCaptureInput = session.inputs.first as? AVCaptureInput else { return } session.removeInput(currentCameraInput) //Get new input var newCamera: AVCaptureDevice! = nil if let input = currentCameraInput as? AVCaptureDeviceInput { if (input.device.position == .back) { newCamera = cameraWithPosition(position: .front) } else { newCamera = cameraWithPosition(position: .back) } } //Add input to session var err: NSError? var newVideoInput: AVCaptureDeviceInput! do { newVideoInput = try AVCaptureDeviceInput(device: newCamera) } catch let err1 as NSError { err = err1 newVideoInput = nil } if newVideoInput == nil || err != nil { print("Error creating capture device input: \(err?.localizedDescription)") } else { session.addInput(newVideoInput) } //Commit all the configuration changes at once session.commitConfiguration() } } // Find a camera with the specified AVCaptureDevicePosition, returning nil if one is not found func cameraWithPosition(position: AVCaptureDevicePosition) -> AVCaptureDevice? { if let discoverySession = AVCaptureDeviceDiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: .unspecified) { for device in discoverySession.devices { if device.position == position { return device } } } return nil }
Swift version to @NES_4Life's answer:
@IBAction func switchCameraTapped(sender: AnyObject) { //Change camera source if let session = captureSession { //Indicate that some changes will be made to the session session.beginConfiguration() //Remove existing input let currentCameraInput:AVCaptureInput = session.inputs.first as! AVCaptureInput session.removeInput(currentCameraInput) //Get new input var newCamera:AVCaptureDevice! = nil if let input = currentCameraInput as? AVCaptureDeviceInput { if (input.device.position == .Back) { newCamera = cameraWithPosition(.Front) } else { newCamera = cameraWithPosition(.Back) } } //Add input to session var err: NSError? var newVideoInput: AVCaptureDeviceInput! do { newVideoInput = try AVCaptureDeviceInput(device: newCamera) } catch let err1 as NSError { err = err1 newVideoInput = nil } if(newVideoInput == nil || err != nil) { print("Error creating capture device input: \(err!.localizedDescription)") } else { session.addInput(newVideoInput) } //Commit all the configuration changes at once session.commitConfiguration() } } // Find a camera with the specified AVCaptureDevicePosition, returning nil if one is not found func cameraWithPosition(position: AVCaptureDevicePosition) -> AVCaptureDevice? { let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo) for device in devices { let device = device as! AVCaptureDevice if device.position == position { return device } } return nil }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With