Capturing a picture in Landscape mode with AVCapture - ios

Currently I have my ios app set up to be in landscape mode when it is run. The camera preview in the view is also in landscape, however when I take a photo it is taking them in portrait mode. I am wondering how I can capture the image in landscape mode only. below is my code for the Camera Preview and the TakePhoto functions.
override func viewWillAppear(animated: Bool) {
let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
for device in devices {
if device.position == AVCaptureDevicePosition.Back {
do {
let input = try AVCaptureDeviceInput(device: device as! AVCaptureDevice)
if captureSession.canAddInput(input){
captureSession.addInput(input)
sessionOutput.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
if captureSession.canAddOutput(sessionOutput){
captureSession.addOutput(sessionOutput)
captureSession.startRunning()
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.LandscapeRight
CameraView.layer.addSublayer(previewLayer)
previewLayer.position = CGPoint(x: self.CameraView.frame.width / 2, y: self.CameraView.frame.height / 2)
previewLayer.bounds = CameraView.frame
}
}
}
catch{
print("ERror")
}
}
}
}
#IBAction func TakePhoto(sender: UIButton) {
tags = tags + 1
TagsFired.text = "Tags Fired \(tags)"
if let videoConnection = sessionOutput.connectionWithMediaType(AVMediaTypeVideo){
sessionOutput.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: {
buffer, error in
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer)
UIImageWriteToSavedPhotosAlbum(UIImage(data: imageData)!, nil, nil, nil)
})
}
}

was able to find a fix for this via Rotating UIImage in Swift
Needed to convert UIImage to CGImage
http://wiki.hawkguide.com/wiki/Swift:_Convert_between_CGImage,_CIImage_and_UIImage

Related

How can I take picture when the camera is already running

I have an app that has a snapchat type camera where the UIView displays the back camera. I have a button on top and when I click that button I would like to take a picture. Right now when I click that button it simply opens up another camera.
This is the code for the button click:
#IBAction func takePhoto(_ sender: UIButton) {
imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .camera
present(imagePicker, animated: true, completion: nil)
}
However, as stated above, that is redundant since my ViewController displays a camera on ViewDidAppear.
override func viewDidAppear(_ animated: Bool) {
self.ShowCamera(self.frontCamera)
fullView.isHidden = false
}
func ShowCamera(_ front: Bool) {
self.captureSession.sessionPreset = AVCaptureSession.Preset.photo
if let availableDevices = AVCaptureDevice.DiscoverySession(deviceTypes: [ .builtInWideAngleCamera,.builtInMicrophone],
mediaType: AVMediaType.video, position: .back).devices.first {
self.captureDevice = availableDevices
if captureSession.isRunning != true {
self.beginSession()
}
}
if self.captureDevice == nil {
print("capture device is nil")
return
}
do {
try self.captureSession.removeInput(AVCaptureDeviceInput(device: self.captureDevice!))
} catch let error as NSError {
print(error)
}
}
func beginSession() {
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: captureDevice)
captureSession.addInput(captureDeviceInput)
} catch {
print(error.localizedDescription)
}
captureSession.startRunning()
let preview = AVCaptureVideoPreviewLayer(session: captureSession)
self.previewLayer = preview
preview.videoGravity = AVLayerVideoGravity.resizeAspectFill
CameraView.layer.insertSublayer(self.previewLayer, at: 0)
self.previewLayer.frame = self.CameraView.layer.frame
let dataOutput = AVCaptureVideoDataOutput()
dataOutput.videoSettings = [(kCVPixelBufferPixelFormatTypeKey as NSString) : NSNumber(value: kCVPixelFormatType_32BGRA)] as [String : Any]
dataOutput.alwaysDiscardsLateVideoFrames = true
if captureSession.canAddOutput(dataOutput)
{
captureSession.addOutput(dataOutput)
}
captureSession.commitConfiguration()
}
All the code above simply gets the UIView and shows the camera. The button for TakePhoto is a sublayer that shows on top of the camera image. When I click that button I want to use whatever image is displaying on my camera.
The command to capture a photo from the running session is
guard let output = captureSession.outputs[0] as? AVCapturePhotoOutput
else {return}
output.capturePhoto(with: settings, delegate: self)
Here, self, is a AVCapturePhotoCaptureDelegate. You then receive the photo thru the delegate messages and extract and save it.

AVFoundation camera previewlayer doesn't match horizontal orientation (Swift 4)

I've been taking bits and pieces of code from the internet and stackoverflow to get a simple camera app working.
However, I've noticed that if I flip my phone to the sideway position,
I see two problems:
1)the
camera preview layer only takes up half the screen
2)and the camera's orientation doesn't seem to be changing; it stays fixed in the vertical position
My constraints seem to be fine, and if I look at various simulators' UIimageView(Camera's preview layer) in the hortionzal position, the UImage is strecthed properly. So not sure why the camera preview layer is only stretching to half the screen.
(ImagePreview = Camera preview layer)
As for the orientation problem, this seems to be a coding problem?
I looked up some posts on stackoverflow, but I didn't see anything for Swift 4.
Not sure if these is an easy way to do this in Swift 4.
iPhone AVFoundation camera orientation
Here is some of the code from my camera app:
import Foundation
import AVFoundation
import UIKit
class CameraSetup{
var captureSession = AVCaptureSession()
var frontCam : AVCaptureDevice?
var backCam : AVCaptureDevice?
var currentCam: AVCaptureDevice?
var captureInput: AVCaptureDeviceInput?
var captureOutput: AVCapturePhotoOutput?
var cameraPreviewLayer: AVCaptureVideoPreviewLayer?
func captureDevice()
{
let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .unspecified)
for device in discoverySession.devices{
if device.position == .front{
frontCam = device
}
else if device.position == .back{
backCam = device}
do {
try backCam?.lockForConfiguration()
backCam?.focusMode = .autoFocus
backCam?.exposureMode = .autoExpose
backCam?.unlockForConfiguration()
}
catch let error{
print(error)}
}
}
func configureCaptureInput(){
currentCam = backCam!
do{
captureInput = try AVCaptureDeviceInput(device: currentCam!)
if captureSession.canAddInput(captureInput!){
captureSession.addInput(captureInput!)
}
}
catch let error{
print(error)
}
}
func configureCaptureOutput(){
captureOutput = AVCapturePhotoOutput()
captureOutput!.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey : AVVideoCodecType.jpeg])], completionHandler: nil)
if captureSession.canAddOutput(captureOutput!){
captureSession.addOutput(captureOutput!)
}
captureSession.startRunning()
}
Here is the PreviewLayer function:
func configurePreviewLayer(view: UIView){
cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resize;
cameraPreviewLayer?.zPosition = -1;
view.layer.insertSublayer(cameraPreviewLayer!, at: 0)
cameraPreviewLayer?.frame = view.bounds
}
EDIT:
As suggested I made the move the view.bounds line one line above:
func configurePreviewLayer(view: UIView){
cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resize;
cameraPreviewLayer?.zPosition = -1;
cameraPreviewLayer?.frame = view.bounds
view.layer.insertSublayer(cameraPreviewLayer!, at: 0)
However, the problem still persists:
Here is the horizontal view:
I think you should user UIView instead of UIImageView.. and try this :
cameraPreviewlayer?.videoGravity = AVLayerVideoGravityResize;
cameraPreviewlayer?.zPosition = -1;
cameraPreviewlayer?.frame = self.imagePreview.bounds

Toggle front/back cameras

Following my previous question, I've made a lot of changes to my code which has bought up a new set of questions which is why I decided to create a new question instead of following the previous.
I'm currently playing around with Swift development where I'm trying to make a basic application which displays the camera within a UIImageview.
I am using the AVFoundation framework.
So far, I've been able to set up the front facing camera on load. My question is, how would I go about implemented the ability to toggle the camera upon a button click?
I firstly inititialise instances:
var captureSession = AVCaptureSession()
var sessionOutput = AVCapturePhotoOutput()
var sessionOutputSetting = AVCapturePhotoSettings(format: [AVVideoCodecKey:AVVideoCodecJPEG])
var previewLayer = AVCaptureVideoPreviewLayer()
I also created a toggle bool:
// Bool to manage camera toggle. False = front-face (default)
var toggle = false
In the viewWillApear I call pickCamera function which checks the value of toggle and creates a device descovery session:
func pickCamera(which: Bool) {
if (which == true) {
let deviceDescovery = AVCaptureDeviceDiscoverySession(deviceTypes: [AVCaptureDeviceType.builtInDualCamera, AVCaptureDeviceType.builtInTelephotoCamera, AVCaptureDeviceType.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: AVCaptureDevicePosition.back)
startCamera(deviceDesc: deviceDescovery!)
toggle = true;
} else if (which == false) {
let deviceDescovery = AVCaptureDeviceDiscoverySession(deviceTypes: [AVCaptureDeviceType.builtInDualCamera, AVCaptureDeviceType.builtInTelephotoCamera, AVCaptureDeviceType.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: AVCaptureDevicePosition.front)
startCamera(deviceDesc: deviceDescovery!)
toggle = false;
}
}
The startCamera function then creates and sets the captureSession and adds to the parent layer to display:
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(sessionOutput)) {
captureSession.addOutput(sessionOutput)
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.portrait
captureSession.startRunning()
cameraView.layer.addSublayer(previewLayer)
}
}
}
catch {
print("Exception")
}
} else if (device.position == AVCaptureDevicePosition.front) {
do {
let input = try AVCaptureDeviceInput(device: device)
if (captureSession.canAddInput(input)) {
captureSession.addInput(input)
if (captureSession.canAddOutput(sessionOutput)) {
captureSession.addOutput(sessionOutput)
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.portrait
captureSession.startRunning()
cameraView.layer.addSublayer(previewLayer)
}
}
}
catch {
print("Exception")
}
}
}
}
I've also added a button with an action:
#IBAction func toggleCamera(_ sender: Any) {
if (toggle == false) {
print("Changing to back camera")
previewLayer.removeFromSuperlayer()
toggle = true;
pickCamera(which: true)
} else if (toggle == true) {
print("Changing to front camera")
previewLayer.removeFromSuperlayer()
toggle = false;
pickCamera(which: false)
}
}
The toggle method is supposed to clear the current view and call the pickCamera method which should create a new instance of the alternative camera.
Although for some reason, this is not working. I'm guessing it's something to do with not properly clearing the previous view/adding the new view correctly but again i'm unsure.
Thank you for taking the time to look at my problem and please ask if i'm missing information or have not explained myself properly.
Update
Finally fixed. The problem lied with not stopping the current captureSession before creating a new captureSession.
To fix this, I updated the toggleCamera function to include:
let currentCameraInput: AVCaptureInput = captureSession.inputs[0] as! AVCaptureInput
For anyone interested in the code, look here

AVFoundation to take video swift 3

I have been following this tutorial: https://www.youtube.com/watch?v=w0O3ZGUS3pk and managed to get to the point where I can take photos, and see the camera output on my UIView.
However I need to record video instead of taking photos. I have looked on stackOverflow and in the AVFoundation help, but couldn't get it to work. This is my code so far:
override func viewDidAppear(_ animated: Bool) {
let devices = AVCaptureDevice.devices()
for device in devices! {
if (device as AnyObject).position == AVCaptureDevicePosition.back {
do {
let input = try AVCaptureDeviceInput(device: device as! AVCaptureDevice)
if captureSession.canAddInput(input) {
captureSession.addInput(input)
if captureSession.canAddOutput(sessionOutput) {
captureSession.addOutput(sessionOutput)
captureSession.startRunning()
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.portrait
cameraView.layer.addSublayer(previewLayer)
previewLayer.position = CGPoint(x: self.cameraView.frame.width/2, y: self.cameraView.frame.height/2)
previewLayer.bounds = cameraView.bounds
}
}
}
catch {
print("error")
}
}
}
}
#IBAction func recordPress(_ sender: Any) {
if let videoConnection = sessionOutput.connection(withMediaType: AVMediaTypeVideo) {
}
}
I saw a question on here relating to this tutorial, but it didnt have the answer.
How would you use the AVFoundation to record video in this way?

Cannot get camera image

I created a simple app supposes to show a live image to a view from iPhone camera modifying this tutorial which is written in Swift2 into Swift3.
I use Xcode 8.1 and run the app on iPhone 6 with iOS version of 9.3.4
my Xcode deployment target setting is 9.1
When I run the app, the code complete without an error but camera image is not showing. The app doesn't even show permission alert for using camera.
What am I doing wrong?
Following is my complete code.
import UIKit
import AVFoundation
class ViewController: UIViewController {
// MARK: UI
let cameraView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
// MARK: System
var captureSession = AVCaptureSession()
var sessionOutput = AVCaptureStillImageOutput()
var previewLayer = AVCaptureVideoPreviewLayer()
override func viewDidLoad() {
super.viewDidLoad()
setupSubviews()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
cameraSetup()
}
func setupSubviews() {
view.addSubview( cameraView )
cameraView.backgroundColor = UIColor.orange
cameraView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
cameraView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
cameraView.widthAnchor.constraint(equalToConstant: 200).isActive = true
cameraView.heightAnchor.constraint(equalToConstant: 200).isActive = true
}
func cameraSetup() {
print("**** Start Camera Setup ****")
let devices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo)! as! [AVCaptureDevice]
for device in devices {
if device.position == .front {
print("Front")
do {
let input = try AVCaptureDeviceInput(device: device)
if captureSession.canAddInput(input) {
captureSession.addInput(input)
sessionOutput.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
print("Input added")
if captureSession.canAddOutput(sessionOutput) {
captureSession.addOutput(sessionOutput)
captureSession.startRunning()
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.portrait
cameraView.layer.addSublayer(previewLayer)
previewLayer.position = CGPoint(x: self.cameraView.frame.width / 2, y: self.cameraView.frame.height / 2)
previewLayer.bounds = cameraView.frame
print("Output added")
}
}
} catch {
print("Error")
}
}
}
print("**** Finish Camera Setup ****")
}
}

Resources