I got this code off of GitHub as the most basic custom camera view controller. I have implemented a flash, which works if I test both front and back cameras. But how would I implement a button in which you could switch cameras while in the app? I have created a camera Button Pressed action which will run when the camera switch button is tapped.
class CameraViewController: UIViewController {
#IBOutlet weak var flashButton: UIButton!
#IBOutlet weak var cameraButton: UIButton!
var flashOn = false
#IBOutlet weak var previewView: PreviewView!
let captureSession = AVCaptureSession()
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
let capturePhotoOutput = AVCapturePhotoOutput()
let capturePhotoDelegate = CapturePhotoDelegate()
private var deviceInput: AVCaptureDeviceInput?
private var cameraPosition: CameraPosition = .back
enum CameraPosition {
case front
case back
}
override func viewDidLoad() {
super.viewDidLoad()
checkCameraUsagePermission()
flashButton.setTitle("OFF", for: .normal)
cameraButton.setTitle("BACK", for: .normal)
}
func initialiseCaptureSession() {
let captureDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .unspecified)
guard let input = try? AVCaptureDeviceInput(device: captureDevice!),
captureSession.canAddInput(input)
else { return }
captureSession.addInput(input)
self.previewView.videoPreviewLayer.session = self.captureSession
self.previewView.videoPreviewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
capturePhotoOutput.isHighResolutionCaptureEnabled = true
captureSession.addOutput(capturePhotoOutput)
captureSession.startRunning()
}
#IBAction func onTapTakePhoto(_ sender: UIButton) {
let photoSettings = AVCapturePhotoSettings()
photoSettings.isAutoStillImageStabilizationEnabled = true
photoSettings.isHighResolutionPhotoEnabled = true
photoSettings.flashMode = .auto
if flashOn == true {
photoSettings.flashMode = .on
} else if flashOn == false {
photoSettings.flashMode = .off
}
capturePhotoOutput.capturePhoto(with: photoSettings, delegate: capturePhotoDelegate)
}
func checkCameraUsagePermission() {
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized:
self.initialiseCaptureSession()
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { granted in
if granted {
self.initialiseCaptureSession()
}
}
case .denied:
return
case .restricted:
return
}
}
#IBAction func flashButtonPressed(_ sender: UIButton) {
if flashOn == false {
flashOn = true
flashButton.setTitle("ON", for: .normal)
} else {
flashOn = false
flashButton.setTitle("OFF", for: .normal)
}
}
func addVideoInput(position: AVCaptureDevice.Position) {
guard let device: AVCaptureDevice = AVCaptureDevice.default(.builtInWideAngleCamera,
for: .video, position: position) else { return }
if let currentInput = self.deviceInput {
self.captureSession.removeInput(currentInput)
self.deviceInput = nil
}
do {
let input = try AVCaptureDeviceInput(device: device)
if self.captureSession.canAddInput(input) {
self.captureSession.addInput(input)
self.deviceInput = input
}
} catch {
print(error)
}
}
#IBAction func cameraButtonPressed(_ sender: UIButton) {
switch self.cameraPosition {
case .front:
self.cameraPosition = .back
self.addVideoInput(position: .back)
case .back:
self.cameraPosition = .front
self.addVideoInput(position: .front)
}
//configure your session here
DispatchQueue.main.async {
self.captureSession.beginConfiguration()
if self.captureSession.canAddOutput(self.capturePhotoOutput) {
self.captureSession.addOutput(self.capturePhotoOutput)
}
self.captureSession.commitConfiguration()
}
}
}
First of all, store your AVCaptureDeviceInput in property
private var deviceInput: AVCaptureDeviceInput?
next create enum that will indicate which camera is active
enum CameraPosition {
case front
case back
}
and property for current camera mode
private var cameraPosition: CameraPosition = .back
Now create function that will be responsible for camera switching
func addVideoInput(position: AVCaptureDevice.Position) {
guard let device: AVCaptureDevice = AVCaptureDevice.default(.builtInWideAngleCamera,
for: .video, position: position) else { return }
if let currentInput = self.deviceInput {
self.captureSession.removeInput(currentInput)
self.deviceInput = nil
}
do {
let input = try AVCaptureDeviceInput(device: device)
if self.captureSession.canAddInput(input) {
self.captureSession.addInput(input)
self.deviceInput = input
}
} catch {
print(error)
}
}
and now in cameraButtonPressed method you can switch cameras
#IBAction func cameraButtonPressed(_ sender: UIButton) {
switch self.cameraPosition {
case .front:
self.cameraPosition = .back
self.addVideoInput(position: .back)
case .back:
self.cameraPosition = .front
self.addVideoInput(position: .front)
}
//configure your session here
DispatchQueue.main.async {
self.captureSession.beginConfiguration()
if self.captureSession.canAddOutput(self.capturePhotoOutput) {
self.captureSession.addOutput(self.capturePhotoOutput)
}
self.captureSession.commitConfiguration()
}
}
Related
I have implemented the preview camera using AVFoundation, its working fine. But I have a hard time to switch the camera back and front. I have added a switch button at the bottom bar. By default, its the back camera, I want to switch it to front. How can I do that?
class FifteenSecsViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {
#IBOutlet weak var camPreview: UIView!
let captureSession = AVCaptureSession()
let movieOutput = AVCaptureMovieFileOutput()
var previewLayer: AVCaptureVideoPreviewLayer!
var activeInput: AVCaptureDeviceInput!
var outputURL: URL!
override func viewDidLoad() {
super.viewDidLoad()
if setupSession() {
setupPreview()
startSession()
}
self.switchCameraButton.addTarget(self, action: #selector(switchButtonTapped), for: .touchUpInside)
}
func setupSession() -> Bool {
captureSession.sessionPreset = AVCaptureSession.Preset.high
// Setup Camera
let camera: AVCaptureDevice?
camera = AVCaptureDevice.default(for: .video)
do {
let input = try AVCaptureDeviceInput(device: camera!)
if captureSession.canAddInput(input) {
captureSession.addInput(input)
activeInput = input
}
} catch {
print("Error setting device video input: \(error)")
return false
}
// Setup Microphone
let microphone = AVCaptureDevice.default(for: .audio)
do {
let micInput = try AVCaptureDeviceInput(device: microphone!)
if captureSession.canAddInput(micInput) {
captureSession.addInput(micInput)
}
} catch {
print("Error setting device audio input: \(error)")
return false
}
// Movie output
let seconds : Int64 = 3
let maxDuration = CMTime(seconds: Double(seconds),
preferredTimescale: 1)
movieOutput.maxRecordedDuration = maxDuration
if captureSession.canAddOutput(movieOutput) {
captureSession.addOutput(movieOutput)
}
return true
}
func setupPreview() {
// Configure previewLayer
previewLayer = AVCaptureVideoPreviewLayer(session:
captureSession)
previewLayer.frame = camPreview.bounds
previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
camPreview.layer.addSublayer(previewLayer)
}
//MARK:- Camera Session
func startSession() {
if !captureSession.isRunning {
videoQueue().async {
self.captureSession.startRunning()
}
}
}
#objc func switchButtonTapped(){
// what to write here??
}
}
Function switchButtonTapped is an actionTarget of UIButton. If I add this code in this button:
#objc func switchButtonTapped(){
if setupSession() {
setupPreview()
startSession()
}
}
Camerapreview screen shows a white screen and got stuck.
Try this code:
func switchCamera() {
session?.beginConfiguration()
let currentInput = session?.inputs.first as? AVCaptureDeviceInput
session?.removeInput(currentInput!)
let newCameraDevice = currentInput?.device.position == .back ? getCamera(with: .front) : getCamera(with: .back)
let newVideoInput = try? AVCaptureDeviceInput(device: newCameraDevice!)
session?.addInput(newVideoInput!)
session?.commitConfiguration()
}
func getCamera(with position: AVCaptureDevice.Position) -> AVCaptureDevice? {
guard let devices = AVCaptureDevice.devices(for: AVMediaType.video) as? [AVCaptureDevice] else {
return nil
}
return devices.filter {
$0.position == position
}.first
}
To begin create a device input for the front camera:
let frontDevice: AVCaptureDevice? = {
for device in AVCaptureDevice.devices(for: AVMediaType.video) {
if device.position == .front {
return device
}
}
return nil
}()
lazy var frontDeviceInput: AVCaptureDeviceInput? = {
if let _frontDevice = self.frontDevice {
return try? AVCaptureDeviceInput(device: _frontDevice)
}
return nil
}()
Then in your switchButtonTapped, if there is a front camera you can do the switch between the front and the ones:
func switchButtonTapped() {
if let _frontDeviceInput = frontDeviceInput {
captureSession.beginConfiguration()
if let _currentInput = captureSession.inputs.first as? AVCaptureDeviceInput {
captureSession.removeInput(_currentInput)
let newDeviceInput = (_currentInput.device.position == .front) ? activeInput : _frontDeviceInput
captureSession.addInput(newDeviceInput!)
}
captureSession.commitConfiguration()
}
}
If you need more details, don't hesitate.
Below is the code for the camera section, I tried adding in a boolean to detect when the front camera is activated but I receive an error.
import UIKit
import AVFoundation
class MainCameraCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var myView: UIView!
var captureSession = AVCaptureSession()
var backCamera: AVCaptureDevice?
var frontCamera: AVCaptureDevice?
var currentCamera: AVCaptureDevice?
var photoOutPut: AVCapturePhotoOutput?
var cameraPreviewLayer: AVCaptureVideoPreviewLayer?
var image: UIImage?
var usingFrontCamera = false
override func awakeFromNib() {
super.awakeFromNib()
setupCaptureSession()
setupDevice()
setupInput()
setupPreviewLayer()
startRunningCaptureSession()
}
func setupCaptureSession(){
captureSession.sessionPreset = AVCaptureSession.Preset.photo
}
func setupDevice(usingFrontCamera:Bool = false){
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.position == AVCaptureDevice.Position.back {
backCamera = device
} else if device.position == AVCaptureDevice.Position.front {
frontCamera = device
}
}
if usingFrontCamera {
currentCamera = frontCamera
} else {
currentCamera = backCamera
}
}
func setupInput() {
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: currentCamera!)
captureSession.addInput(captureDeviceInput)
photoOutPut = AVCapturePhotoOutput()
photoOutPut?.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format:[AVVideoCodecKey: AVVideoCodecType.jpeg])], completionHandler: nil)
captureSession.addOutput(photoOutPut!)
} catch {
print(error)
}
}
func setupPreviewLayer(){
cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
cameraPreviewLayer?.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
self.layer.insertSublayer(cameraPreviewLayer!, at: 0)
}
func startRunningCaptureSession(){
captureSession.startRunning()
}
#IBAction func cameraButton_TouchUpInside(_ sender: Any) {
let settings = AVCapturePhotoSettings()
photoOutPut?.capturePhoto(with: settings, delegate: self as! AVCapturePhotoCaptureDelegate)
}
//Flip to front and back camera
#IBAction func FlipThe_camera(_ sender: UIButton) {
print("Flip touched")
if let inputs = captureSession.inputs as? [AVCaptureDeviceInput] {
for input in inputs {
captureSession.removeInput(input)
}
}
usingFrontCamera = !usingFrontCamera
setupCaptureSession()
setupDevice(usingFrontCamera:usingFrontCamera)
setupInput()
setupPreviewLayer()
startRunningCaptureSession()
}
}
collectionViewAttemptOfPagingAttempt002[2908:834004] * Terminating
app due to uncaught exception 'NSInvalidArgumentException', reason:
'* -[AVCaptureSession addOutput:] Cannot add output
to capture session
->
-> because more than one output of the same type is
unsupported'
Your problem is that you are not checking if you can add input and/or output before adding them to your capture session. Btw you don't need to setup the preview layer again when switching your camera and you need only one captureDevice:
if captureSession.canAddInput(captureDeviceInput) {
captureSession.addInput(captureDeviceInput)
}
and
if captureSession.canAddOutput(photoOutPut) {
captureSession.addOutput(photoOutPut)
}
Your CollectionViewCell code should look something like this:
class MainCameraCollectionViewCell: UICollectionViewCell, AVCapturePhotoCaptureDelegate {
private var captureSession = AVCaptureSession()
private var sessionQueue: DispatchQueue!
private var captureDevice: AVCaptureDevice!
private var photoOutPut: AVCapturePhotoOutput!
private var cameraPreviewLayer: AVCaptureVideoPreviewLayer!
var image: UIImage?
var usingFrontCamera = false
override func awakeFromNib() {
super.awakeFromNib()
setupCaptureSession()
setupDevice()
setupInput()
setupPreviewLayer()
startRunningCaptureSession()
}
func setupCaptureSession(){
captureSession.sessionPreset = .photo
sessionQueue = DispatchQueue(label: "session queue")
}
func setupDevice(usingFrontCamera: Bool = false){
sessionQueue.async {
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: .unspecified)
let devices = deviceDiscoverySession.devices
for device in devices {
if usingFrontCamera && device.position == .front {
self.captureDevice = device
} else if device.position == .back {
self.captureDevice = device
}
}
}
}
func setupInput() {
sessionQueue.async {
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: self.captureDevice)
if self.captureSession.canAddInput(captureDeviceInput) {
self.captureSession.addInput(captureDeviceInput)
}
self.photoOutPut = AVCapturePhotoOutput()
self.photoOutPut.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format:[AVVideoCodecKey: AVVideoCodecType.jpeg])], completionHandler: nil)
if self.captureSession.canAddOutput(self.photoOutPut) {
self.captureSession.addOutput(self.photoOutPut)
}
} catch {
print(error)
}
}
}
func setupPreviewLayer() {
cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
cameraPreviewLayer.videoGravity = .resizeAspectFill
cameraPreviewLayer.connection?.videoOrientation = .portrait
cameraPreviewLayer.frame = UIScreen.main.bounds
layer.insertSublayer(cameraPreviewLayer, at: 0)
}
func startRunningCaptureSession() {
captureSession.startRunning()
}
#IBAction func cameraButton_TouchUpInside(_ sender: Any) {
let settings = AVCapturePhotoSettings()
photoOutPut.capturePhoto(with: settings, delegate: self)
}
//Flip to front and back camera
#IBAction func FlipThe_camera(_ sender: UIButton) {
captureSession.beginConfiguration()
if let inputs = captureSession.inputs as? [AVCaptureDeviceInput] {
for input in inputs {
captureSession.removeInput(input)
}
}
usingFrontCamera = !usingFrontCamera
setupCaptureSession()
setupDevice(usingFrontCamera: usingFrontCamera)
setupInput()
captureSession.commitConfiguration()
startRunningCaptureSession()
}
}
Sample project
The app crashes with an error
No active and enabled video connection'
After taking a picture with the back camera segueing to another screen dismissing the screen and returning back to the camera screen flipping from the back camera to the front camera and taking another picture the app crashes below is the code for the camera screen.
import UIKit
import AVFoundation
protocol gestureDelegate{
func gestureDelegate()
}
protocol previewSegueDelegate {
func previewSegueDelegate(image:UIImage,device:AVCaptureDevice)
}
class MainCameraCollectionViewCell: UICollectionViewCell {
var gdelegate: gestureDelegate?
var pdelegate: previewSegueDelegate?
#IBOutlet weak var myView: UIView!
var captureSession = AVCaptureSession()
private var sessionQueue: DispatchQueue!
var captureConnection = AVCaptureConnection()
var backCamera: AVCaptureDevice?
var frontCamera: AVCaptureDevice?
var currentCamera: AVCaptureDevice?
var photoOutPut: AVCapturePhotoOutput?
var cameraPreviewLayer: AVCaptureVideoPreviewLayer?
var image: UIImage?
var usingFrontCamera = false
override func awakeFromNib() {
super.awakeFromNib()
setupCaptureSession()
setupDevice()
setupInput()
setupPreviewLayer()
startRunningCaptureSession()
print("Inside of camera cell")
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(MainCameraCollectionViewCell.tapEdit(sender:)))
addGestureRecognizer(pinchGesture)
}
#objc func tapEdit(sender: UIPinchGestureRecognizer){
gdelegate?.gestureDelegate()
guard let device = currentCamera else { return }
if sender.state == .changed {
let maxZoomFactor = device.activeFormat.videoMaxZoomFactor
let pinchVelocityDividerFactor: CGFloat = 5.0
do {
try device.lockForConfiguration()
defer { device.unlockForConfiguration() }
let desiredZoomFactor = device.videoZoomFactor + atan2(sender.velocity, pinchVelocityDividerFactor)
device.videoZoomFactor = max(1.0, min(desiredZoomFactor, maxZoomFactor))
} catch {
print(error)
}
}
}
func setupCaptureSession(){
captureSession.sessionPreset = AVCaptureSession.Preset.photo
sessionQueue = DispatchQueue(label: "session queue")
}
func setupDevice(usingFrontCamera:Bool = false){
sessionQueue.async {
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera], mediaType: AVMediaType.video, position: AVCaptureDevice.Position.unspecified)
let devices = deviceDiscoverySession.devices
for device in devices{
if usingFrontCamera && device.position == AVCaptureDevice.Position.front {
self.currentCamera = device
} else if device.position == AVCaptureDevice.Position.back {
self.currentCamera = device
}
}
}
}
func setupInput() {
sessionQueue.async {
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: self.currentCamera!)
if self.captureSession.canAddInput(captureDeviceInput) {
self.captureSession.addInput(captureDeviceInput)
}
self.photoOutPut = AVCapturePhotoOutput()
self.photoOutPut?.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format:[AVVideoCodecKey: AVVideoCodecType.jpeg])], completionHandler: nil)
if self.captureSession.canAddOutput(self.photoOutPut!) {
self.captureSession.addOutput(self.photoOutPut!)
}
} catch {
print(error)
}
}
}
func setupPreviewLayer(){
cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
cameraPreviewLayer?.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
self.layer.insertSublayer(cameraPreviewLayer!, at: 0)
}
func startRunningCaptureSession(){
captureSession.startRunning()
}
#IBAction func cameraButton_TouchUpInside(_ sender: Any) {
let settings = AVCapturePhotoSettings()
photoOutPut?.capturePhoto(with: settings, delegate: self as! AVCapturePhotoCaptureDelegate)
}
#IBAction func FlipThe_camera(_ sender: UIButton) {
print("Flip Touched")
captureSession.beginConfiguration()
if let inputs = captureSession.inputs as? [AVCaptureDeviceInput] {
for input in inputs {
captureSession.removeInput(input)
}
}
usingFrontCamera = !usingFrontCamera
setupCaptureSession()
setupDevice(usingFrontCamera: usingFrontCamera)
setupInput()
captureSession.commitConfiguration()
startRunningCaptureSession()
}
}
extension MainCameraCollectionViewCell: AVCapturePhotoCaptureDelegate{
func photoOutput(_ output: AVCapturePhotoOutput,
didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
if let imageData = photo.fileDataRepresentation(){
print(imageData)
image = UIImage(data: imageData)
// performSegue(withIdentifier: "showPhoto_segue", sender: nil)
if(self.image == nil){
print("The image is empty")
}
pdelegate?.previewSegueDelegate(image: self.image!, device: currentCamera!)
}
}
}
This is the code of my camera, change the front camera to the rear camera, if I take a picture with the back camera, the orientation of the photo is good (original), but if i take a photo with the front camera get the image with the bad orientation.
class TakeSelfieViewController: UIViewController, AVCapturePhotoCaptureDelegate {
var captureSession = AVCaptureSession()
var photoOutput = AVCapturePhotoOutput()
var previewLayer : AVCaptureVideoPreviewLayer?
var captureDevice : AVCaptureDevice?
var sessionOutputSetting = AVCapturePhotoSettings(format: [AVVideoCodecKey:AVVideoCodecJPEG])
var toggle = false
#IBOutlet weak var cameraView: UIView!
#IBOutlet weak var tempImageView: UIImageView!
#IBOutlet weak var adorButton: UIButton!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
previewLayer?.frame = cameraView.bounds
let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.light)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = adorButton.bounds
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
adorButton.addSubview(blurEffectView)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
pickCamera(which: toggle)
}
func pickCamera(which: Bool) {
if (which == true) {
let deviceDescovery = AVCaptureDeviceDiscoverySession(deviceTypes: [AVCaptureDeviceType.builtInDualCamera, AVCaptureDeviceType.builtInTelephotoCamera,AVCaptureDeviceType.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: AVCaptureDevicePosition.back)
print("back camera")
startCamera(deviceDesc: deviceDescovery!)
toggle = true
} else if (which == false) {
let deviceDescovery = AVCaptureDeviceDiscoverySession(deviceTypes: [AVCaptureDeviceType.builtInDualCamera, AVCaptureDeviceType.builtInTelephotoCamera,AVCaptureDeviceType.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: AVCaptureDevicePosition.front)
print("front camera")
startCamera(deviceDesc: deviceDescovery!)
toggle = false
}
}
func startCamera(deviceDesc: AVCaptureDeviceDiscoverySession!) {
for device in (deviceDesc.devices)! {
if device.position == AVCaptureDevicePosition.back {
do {
let input = try AVCaptureDeviceInput(device: device)
if captureSession.canAddInput(input) {
captureSession.addInput(input)
if captureSession.canAddOutput(photoOutput) {
captureSession.addOutput(photoOutput)
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
previewLayer?.connection.videoOrientation = AVCaptureVideoOrientation.portrait
cameraView.layer.addSublayer(previewLayer!)
captureSession.startRunning()
print("ADD Back")
} else { print("Cannot add input - back") }
}
} catch {
print("Error")
}
} else if (device.position == AVCaptureDevicePosition.front) {
do {
let input = try AVCaptureDeviceInput(device: device)
print(input)
if captureSession.canAddInput(input) {
captureSession.addInput(input)
if captureSession.canAddOutput(photoOutput) {
captureSession.addOutput(photoOutput)
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
previewLayer?.connection.videoOrientation = AVCaptureVideoOrientation.portrait
cameraView.layer.addSublayer(previewLayer!)
captureSession.startRunning()
print("ADD Front")
}
} else { print("Cannot add input - front") }
} catch {
print(error)
}
}
}
}
func didPressTakePhoto() {
if let videoConnection = photoOutput.connection(withMediaType: AVMediaTypeVideo) {
videoConnection.videoOrientation = AVCaptureVideoOrientation.portrait
let settings = AVCapturePhotoSettings(format: [AVVideoCodecKey : AVVideoCodecJPEG])
photoOutput.capturePhoto(with: settings, delegate: self)
}
}
func capture(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) {
let imageData = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: photoSampleBuffer!, previewPhotoSampleBuffer: previewPhotoSampleBuffer)
let dataProvider = CGDataProvider(data: imageData as! CFData)
let cgImageRef = CGImage(jpegDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)
let image = UIImage(cgImage: cgImageRef!, scale: 1.0, orientation: UIImageOrientation.right)
self.tempImageView.image = image
self.tempImageView.isHidden = false
self.yellowButton.isHidden = true
self.toggleAction.isHidden = true
self.adorButton.isHidden = true
print("Hola")
}
var didTakePhoto = Bool()
#IBOutlet weak var yellowButton: UIButton!
#IBOutlet weak var toggleAction: UIButton!
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if didTakePhoto {
tempImageView.isHidden = true
yellowButton.isHidden = false
toggleAction.isHidden = false
adorButton.isHidden = false
didTakePhoto = false
print("🍊")
}
}
#IBAction func yellowPressed(_ sender: UIButton) {
captureSession.startRunning()
didTakePhoto = true
didPressTakePhoto()
print("🐶")
}
#IBAction func toggleCamera(_ sender: Any) {
if (toggle == false) {
print("Changing to back camera")
let currentCameraInput: AVCaptureInput = captureSession.inputs[0] as! AVCaptureInput
captureSession.removeInput(currentCameraInput)
toggle = true
pickCamera(which: toggle)
} else if (toggle == true) {
print("Changing to front camera")
let currentCameraInput: AVCaptureInput = captureSession.inputs[0] as! AVCaptureInput
captureSession.removeInput(currentCameraInput)
toggle = false
pickCamera(which: toggle)
}
}
override var prefersStatusBarHidden: Bool {
return true
}
}
How do I solve this? 🤔
Images taken with front camera are mirrored, when you take a picture the image orientation is taken inside the EXIF dictionary of it, or passed inside a dictionary of metadata.
Most of the time when you pass it around as JPG or PNG this value is not taken into account if you don't deal directly with it.
You should experience a similar problem if you take a picture in landscape.
In your capture method, it seems that you are forcing orientation to a fixed value when you should take care of it.
So, lately I have been trying to implement the function of switching the camera view from back to front camera in Swift 3. However, with no luck.
Currently, my default view is from the back camera - I can take pictures with it and then retake. But can anyone help me and show how do I either double tap the screen to switch cameras or simply use the assigned button to switch them? Thank you!
import UIKit
import AVFoundation
import FirebaseDatabase
class CameraView: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
override var prefersStatusBarHidden: Bool {
return true
}
var captureSession : AVCaptureSession!
var stillImageOutput : AVCaptureStillImageOutput!
var previewLayer : AVCaptureVideoPreviewLayer!
#IBOutlet var cameraView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
previewLayer?.frame = cameraView.bounds
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
captureSession = AVCaptureSession()
captureSession?.sessionPreset = AVCaptureSessionPreset1920x1080
var backCamera = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
var error : NSError?
do {
var input = try! AVCaptureDeviceInput(device: backCamera)
if (error == nil && captureSession?.canAddInput(input) != nil) {
captureSession?.addInput(input)
stillImageOutput = AVCaptureStillImageOutput()
stillImageOutput.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
if (captureSession?.canAddOutput(stillImageOutput) != nil) {
captureSession?.addOutput(stillImageOutput)
previewLayer = AVCaptureVideoPreviewLayer (session: captureSession)
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.portrait
cameraView.layer.addSublayer(previewLayer)
captureSession?.startRunning() }
}
} catch {
}
}
#IBOutlet var tempImageView: UIImageView!
#IBAction func didPressTakePhoto(_ sender: UIButton) {
if let videoConnection = stillImageOutput.connection(withMediaType: AVMediaTypeVideo) {
videoConnection.videoOrientation = AVCaptureVideoOrientation.portrait
stillImageOutput.captureStillImageAsynchronously(from: videoConnection, completionHandler: {
(sampleBuffer, error) in
if sampleBuffer != nil {
var imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
var dataProvider = CGDataProvider.init(data: imageData as! CFData)
var cgImageRef = CGImage.init(jpegDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: true, intent: .defaultIntent)
var image = UIImage (cgImage: cgImageRef!, scale: 1.0, orientation: UIImageOrientation.right)
self.tempImageView.image = image
self.tempImageView.isHidden = false
}
})
}
}
var didTakePhoto = Bool()
#IBAction func didPressTakeAnother(_ sender: UIButton) {
if didTakePhoto == true {
tempImageView.isHidden = true
didTakePhoto = false
} else {
captureSession?.startRunning()
didTakePhoto = true
}
}
}
don't see here any problems - here is working solution:
import Foundation
import UIKit
import AVFoundation
class MainViewController: UIViewController {
var tempImage: UIImageView?
var captureSession: AVCaptureSession?
var stillImageOutput: AVCaptureStillImageOutput?
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
var currentCaptureDevice: AVCaptureDevice?
var usingFrontCamera = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
loadCamera()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
videoPreviewLayer!.frame = self.cameraPreviewSurface.bounds
}
#IBAction func switchButtonAction(_ sender: Any) {
usingFrontCamera = !usingFrontCamera
loadCamera()
}
func getFrontCamera() -> AVCaptureDevice?{
let videoDevices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo)
for device in videoDevices!{
let device = device as! AVCaptureDevice
if device.position == AVCaptureDevicePosition.front {
return device
}
}
return nil
}
func getBackCamera() -> AVCaptureDevice{
return AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
}
func loadCamera() {
if(captureSession == nil){
captureSession = AVCaptureSession()
captureSession!.sessionPreset = AVCaptureSessionPresetPhoto
}
var error: NSError?
var input: AVCaptureDeviceInput!
currentCaptureDevice = (usingFrontCamera ? getFrontCamera() : getBackCamera())
do {
input = try AVCaptureDeviceInput(device: currentCaptureDevice)
} catch let error1 as NSError {
error = error1
input = nil
print(error!.localizedDescription)
}
for i : AVCaptureDeviceInput in (self.captureSession?.inputs as! [AVCaptureDeviceInput]){
self.captureSession?.removeInput(i)
}
if error == nil && captureSession!.canAddInput(input) {
captureSession!.addInput(input)
stillImageOutput = AVCaptureStillImageOutput()
stillImageOutput?.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
if captureSession!.canAddOutput(stillImageOutput) {
captureSession!.addOutput(stillImageOutput)
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
videoPreviewLayer!.videoGravity = AVLayerVideoGravityResizeAspectFill
videoPreviewLayer!.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
//self.cameraPreviewSurface.layer.sublayers?.forEach { $0.removeFromSuperlayer() }
self.cameraPreviewSurface.layer.addSublayer(videoPreviewLayer!)
DispatchQueue.main.async {
self.captureSession!.startRunning()
}
}
}
}
}
some notes:
cameraPreviewSurface - this is your UIView where camera will show
don't reassign the session, don't just add input, but before add new - remove existing ones,
p.s. code done with swift 3.0.1 / xcode 8.1
Cheers )
Xcode Version : Version 10.1 (10B61)
Swift Version : Swift 4.2
Change AVCaptureSession Capture Source
Refers from Stepan Maksymov Solution
we can simplified by replacing captureSession.inputs
First Create an IBAction Outlet and Connect ViewController to Change Camera View
#IBAction private func changeCamera(_ cameraButton: UIButton) {
usingFrontCamera = !usingFrontCamera
do{
captureSession.removeInput(captureSession.inputs.first!)
if(usingFrontCamera){
captureDevice = getFrontCamera()
}else{
captureDevice = getBackCamera()
}
let captureDeviceInput1 = try AVCaptureDeviceInput(device: captureDevice)
captureSession.addInput(captureDeviceInput1)
}catch{
print(error.localizedDescription)
}
}
Second step Copy simplified AVCaptureDevice Setting || refer Stepan Maksymov
func getFrontCamera() -> AVCaptureDevice?{
return AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .front).devices.first
return nil
}
func getBackCamera() -> AVCaptureDevice?{
return AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .back).devices.first
return nil
}
You can replace #IBAction with function like so.
func changeCamera(){
usingFrontCamera = !usingFrontCamera
do{
captureSession.removeInput(captureSession.inputs.first!)
if(usingFrontCamera){
captureDevice = getFrontCamera()
}else{
captureDevice = getBackCamera()
}
let captureDeviceInput1 = try AVCaptureDeviceInput(device: captureDevice)
captureSession.addInput(captureDeviceInput1)
}catch{
print(error.localizedDescription)
}
}
In addition to Stepan Maksymov post, I sugest to add this function
func stopCaptureSession () {
self.captureSession.stopRunning()
if let inputs = captureSession.inputs as? [AVCaptureDeviceInput] {
for input in inputs {
self.captureSession.removeInput(input)
}
}
}
And call it instead of his post lines:
for i : AVCaptureDeviceInput in (self.captureSession?.inputs as! [AVCaptureDeviceInput]){
self.captureSession?.removeInput(i)
}
This way the cameras will change quicker.