Switch cameras with avcapturesession
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 foundfunc 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 foundfunc 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 foundfunc 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}
Based on previous answers I made my own version with some validations and one specific change, the current camera input might not be the first object of the capture session's inputs, so I changed this:
//Remove existing inputAVCaptureInput* currentCameraInput = [self.captureSession.inputs objectAtIndex:0];[self.captureSession removeInput:currentCameraInput];
To this (removing all video type inputs):
for (AVCaptureDeviceInput *input in self.captureSession.inputs) { if ([input.device hasMediaType:AVMediaTypeVideo]) { [self.captureSession removeInput:input]; break; }}
Here's the entire code:
if (!self.captureSession) return;[self.captureSession beginConfiguration];AVCaptureDeviceInput *currentCameraInput;// Remove current (video) inputfor (AVCaptureDeviceInput *input in self.captureSession.inputs) { if ([input.device hasMediaType:AVMediaTypeVideo]) { [self.captureSession removeInput:input]; currentCameraInput = input; break; }}if (!currentCameraInput) return;// Switch device positionAVCaptureDevicePosition captureDevicePosition = AVCaptureDevicePositionUnspecified;if (currentCameraInput.device.position == AVCaptureDevicePositionBack) { captureDevicePosition = AVCaptureDevicePositionFront;} else { captureDevicePosition = AVCaptureDevicePositionBack;}// Select new cameraAVCaptureDevice *newCamera;NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];for (AVCaptureDevice *captureDevice in devices) { if (captureDevice.position == captureDevicePosition) { newCamera = captureDevice; }}if (!newCamera) return;// Add new camera inputNSError *error;AVCaptureDeviceInput *newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:newCamera error:&error];if (!error && [self.captureSession canAddInput:newVideoInput]) { [self.captureSession addInput:newVideoInput];}[self.captureSession commitConfiguration];