Первая часть кода - это конфигурация камеры, а вторая часть - контроллер вида. Когда я смотрю на фотографию, которую я сохранил в своих альбомах, она показывает портретное видео, не зависящее от ориентации моей камеры. Если мое видео находится в портретном режиме, видео / фотография также будет в портретном режиме, чего я и хочу, однако, когда я записываю в альбомной ориентации, файл все еще находится в портретном режиме. Может ли кто-нибудь помочь мне, это будет высоко ценится. Заранее спасибо.
import Foundation
import AVFoundation
import UIKit
class CameraConfiguration: NSObject {
enum CameraControllerError: Swift.Error {
case captureSessionAlreadyRunning
case captureSessionIsMissing
case inputsAreInvalid
case invalidOperation
case noCamerasAvailable
case unknown
}
public enum CameraPosition {
case front
case rear
}
public enum OutputType {
case photo
case video
}
var captureSession: AVCaptureSession?
var frontCamera: AVCaptureDevice?
var rearCamera: AVCaptureDevice?
var audioDevice: AVCaptureDevice?
var currentCameraPosition: CameraPosition?
var frontCameraInput: AVCaptureDeviceInput?
var rearCameraInput: AVCaptureDeviceInput?
var photoOutput: AVCapturePhotoOutput?
var previewLayer: AVCaptureVideoPreviewLayer?
var flashMode: AVCaptureDevice.FlashMode = AVCaptureDevice.FlashMode.off
var photoCaptureCompletionBlock: ((UIImage?, Error?) -> Void)?
var videoRecordCompletionBlock: ((URL?, Error?) -> Void)?
var videoFileOutput : AVCaptureMovieFileOutput!
var videoOutput: AVCaptureMovieFileOutput?
var audioInput: AVCaptureDeviceInput?
var outputType: OutputType?
}
extension CameraConfiguration {
func setup(handler: @escaping (Error?)-> Void ) {
func createCaptureSession() {
self.captureSession = AVCaptureSession()
}
func configureCaptureDevices() throws {
let session = AVCaptureDevice.DiscoverySession.init(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: AVCaptureDevice.Position.unspecified)
let cameras = (session.devices.compactMap{$0})
for camera in cameras {
if camera.position == .front {
self.frontCamera = camera
}
if camera.position == .back {
self.rearCamera = camera
try camera.lockForConfiguration()
camera.focusMode = .continuousAutoFocus
camera.unlockForConfiguration()
}
}
self.audioDevice = AVCaptureDevice.default(for: AVMediaType.audio)
}
//Configure inputs with capture session
//only allows one camera-based input per capture session at a time.
func configureDeviceInputs() throws {
guard let captureSession = self.captureSession else {
throw CameraControllerError.captureSessionIsMissing
}
if let rearCamera = self.rearCamera {
self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera)
if captureSession.canAddInput(self.rearCameraInput!) {
captureSession.addInput(self.rearCameraInput!)
self.currentCameraPosition = .rear
} else {
throw CameraControllerError.inputsAreInvalid
}
}
else if let frontCamera = self.frontCamera {
self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)
if captureSession.canAddInput(self.frontCameraInput!) {
captureSession.addInput(self.frontCameraInput!)
self.currentCameraPosition = .front
} else {
throw CameraControllerError.inputsAreInvalid
}
}
else {
throw CameraControllerError.noCamerasAvailable
}
if let audioDevice = self.audioDevice {
self.audioInput = try AVCaptureDeviceInput(device: audioDevice)
if captureSession.canAddInput(self.audioInput!) {
captureSession.addInput(self.audioInput!)
} else {
throw CameraControllerError.inputsAreInvalid
}
}
}
//Configure outputs with capture session
func configurePhotoOutput() throws {
guard let captureSession = self.captureSession else {
throw CameraControllerError.captureSessionIsMissing
}
self.photoOutput = AVCapturePhotoOutput()
self.photoOutput?.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey : AVVideoCodecType.jpeg ])], completionHandler: nil)
if captureSession.canAddOutput(self.photoOutput!) {
captureSession.addOutput(self.photoOutput!)
}
self.outputType = .photo
captureSession.startRunning()
}
func configureVideoOutput() throws {
guard let captureSession = self.captureSession else {
throw CameraControllerError.captureSessionIsMissing
}
self.videoOutput = AVCaptureMovieFileOutput()
if captureSession.canAddOutput(self.videoOutput!) {
captureSession.addOutput(self.videoOutput!)
}
// let delayTime = dispatch_time(dispatch_time_t(DispatchTime.now()), Int64(5 * Double(NSEC_PER_SEC)))
// dispatch_after(delayTime, dispatch_get_main_queue()) {
// print("stopping")
// self.movieOutput.stopRecording()
// }
}
DispatchQueue(label: "setup").async {
do {
createCaptureSession()
try configureCaptureDevices()
try configureDeviceInputs()
try configurePhotoOutput()
try configureVideoOutput()
} catch {
DispatchQueue.main.async {
handler(error)
}
return
}
DispatchQueue.main.async {
handler(nil)
}
}
}
func displayPreview(_ view: UIView) throws {
guard let captureSession = self.captureSession, captureSession.isRunning else { throw CameraControllerError.captureSessionIsMissing }
self.previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
self.previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.previewLayer?.connection?.videoOrientation = .portrait
view.layer.insertSublayer(self.previewLayer!, at: 0)
self.previewLayer?.frame = CGRect(x: 0, y: 0, width: view.frame.width , height: view.frame.height)
}
func switchCameras() throws {
guard let currentCameraPosition = currentCameraPosition, let captureSession = self.captureSession, captureSession.isRunning else { throw CameraControllerError.captureSessionIsMissing }
captureSession.beginConfiguration()
let inputs = captureSession.inputs
func switchToFrontCamera() throws {
guard let rearCameraInput = self.rearCameraInput, inputs.contains(rearCameraInput),let frontCamera = self.frontCamera else { throw CameraControllerError.invalidOperation }
captureSession.removeInput(rearCameraInput)
self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)
if captureSession.canAddInput(self.frontCameraInput!) {
captureSession.addInput(self.frontCameraInput!)
self.currentCameraPosition = .front
}
else { throw CameraControllerError.invalidOperation }
}
func switchToRearCamera() throws {
guard let frontCameraInput = self.frontCameraInput, inputs.contains(frontCameraInput), let rearCamera = self.rearCamera else { throw CameraControllerError.invalidOperation }
captureSession.removeInput(frontCameraInput)
self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera)
if captureSession.canAddInput(rearCameraInput!) {
captureSession.addInput(rearCameraInput!)
self.currentCameraPosition = .rear
}
else { throw CameraControllerError.invalidOperation }
}
switch currentCameraPosition {
case .front:
try switchToRearCamera()
case .rear:
try switchToFrontCamera()
}
captureSession.commitConfiguration()
}
func captureImage(completion: @escaping (UIImage?, Error?) -> Void) {
guard let captureSession = self.captureSession, captureSession.isRunning else {
completion(nil, CameraControllerError.captureSessionIsMissing)
return
}
let settings = AVCapturePhotoSettings()
settings.flashMode = self.flashMode
self.photoOutput?.capturePhoto(with: settings, delegate: self)
self.photoCaptureCompletionBlock = completion
}
func recordVideo(completion: @escaping (URL?, Error?)-> Void) {
guard let captureSession = self.captureSession, captureSession.isRunning else {
completion(nil, CameraControllerError.captureSessionIsMissing)
return
}
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let fileUrl = paths[0].appendingPathComponent("output.mp4")
try? FileManager.default.removeItem(at: fileUrl)
videoOutput!.startRecording(to: fileUrl, recordingDelegate: self)
self.videoRecordCompletionBlock = completion
}
func stopRecording(completion: @escaping (Error?)->Void) {
guard let captureSession = self.captureSession, captureSession.isRunning else {
completion(CameraControllerError.captureSessionIsMissing)
return
}
self.videoOutput?.stopRecording()
}
}
extension CameraConfiguration: AVCapturePhotoCaptureDelegate {
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
if let error = error { self.photoCaptureCompletionBlock?(nil, error) }
if let data = photo.fileDataRepresentation() {
let image = UIImage(data: data)
self.photoCaptureCompletionBlock?(image, nil)
}
else {
self.photoCaptureCompletionBlock?(nil, CameraControllerError.unknown)
}
}
fileprivate func _imageOrientation(forDeviceOrientation deviceOrientation: UIDeviceOrientation, isMirrored: Bool) -> UIImage.Orientation {
switch deviceOrientation {
case .landscapeLeft:
return isMirrored ? .upMirrored : .up
case .landscapeRight:
return isMirrored ? .downMirrored : .down
default:
break
}
return isMirrored ? .leftMirrored : .right
}
func convert(cmage:CIImage) -> UIImage
{
let context:CIContext = CIContext.init(options: nil)
let cgImage:CGImage = context.createCGImage(cmage, from: cmage.extent)!
let image:UIImage = UIImage.init(cgImage: cgImage)
return image
}
}
extension CameraConfiguration: AVCaptureFileOutputRecordingDelegate {
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
if error == nil {
self.videoRecordCompletionBlock?(outputFileURL, nil)
} else {
self.videoRecordCompletionBlock?(nil, error)
}
}
}
extension CameraConfiguration: AVCaptureVideoDataOutputSampleBufferDelegate {
}
import UIKit
import Photos
class CameraViewController: UIViewController {
@IBOutlet weak var cameraButton: UIButton!
@IBOutlet weak var toggleCameraButton: UIButton!
@IBOutlet weak var toggleHelp: UIButton!
@IBOutlet weak var cameraSelectionButton: UIButton!
@IBOutlet weak var videoCameraButton: UIButton!
@IBOutlet weak var toggleFlashButton: UIButton!
var cameraConfig: CameraConfiguration!
let imagePickerController = UIImagePickerController()
var videoRecordingStarted: Bool = false
func checkPermission(completion: @escaping ()->Void) {
let photoAuthorizationStatus = PHPhotoLibrary.authorizationStatus()
switch photoAuthorizationStatus {
case .authorized:
print("Access is granted by user")
completion()
case .notDetermined:
PHPhotoLibrary.requestAuthorization({
(newStatus) in
print("status is \(newStatus)")
if newStatus == PHAuthorizationStatus.authorized {
/* do stuff here */
completion()
print("success")
}
})
print("It is not determined until now")
case .restricted:
// same same
print("User do not have access to photo album.")
case .denied:
// same same
print("User has denied the permission.")
default:
return
}
}
fileprivate func registerNotification() {
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: NSNotification.Name(rawValue: "App is going background"), object: nil)
notificationCenter.addObserver(self, selector: #selector(appCameToForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
}
@objc func appMovedToBackground() {
if videoRecordingStarted {
videoRecordingStarted = false
self.cameraConfig.stopRecording { (error) in
print(error ?? "Video recording error")
}
}
}
//@IBAction func gotoGallery(_ sender: Any) {
// checkPermission(completion: {
// self.imagePickerController.sourceType = .photoLibrary
// self.imagePickerController.delegate = self
// self.imagePickerController.mediaTypes = ["public.image", "public.movie"]
// self.present(self.imagePickerController, animated: true, completion: nil)
// })
//}
@objc func appCameToForeground() {
print("app enters foreground")
}
override func viewDidLoad() {
super.viewDidLoad()
self.cameraSelectionButton.tintColor = UIColor.blue
self.cameraConfig = CameraConfiguration()
cameraConfig.setup { (error) in
if error != nil {
print(error!.localizedDescription)
}
}
registerNotification()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.setNavigationBarHidden(false, animated: animated)
}
@IBAction func toggleFlash(_ sender: Any) {
if cameraConfig.flashMode == .on {
cameraConfig.flashMode = .off
self.toggleFlashButton.setImage(#imageLiteral(resourceName: "flash_off"), for: .normal)
} else if cameraConfig.flashMode == .off {
cameraConfig.flashMode = .on
self.toggleFlashButton.setImage(#imageLiteral(resourceName: "flash_on"), for: .normal)
} else {
cameraConfig.flashMode = .auto
self.toggleFlashButton.setImage(#imageLiteral(resourceName: "flash_auto"), for: .normal)
}
}
@objc fileprivate func showToastForSaved() {
showToast(message: "Saved!", fontSize: 12.0)
}
@objc fileprivate func showToastForRecordingStopped() {
showToast(message: "Recording Stopped", fontSize: 12.0)
}
@objc func image(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
if let error = error {
// we got back an error!
showToast(message: "Could not save!! \n\(error)", fontSize: 12)
} else {
showToast(message: "Saved", fontSize: 12.0)
}
}
@objc func video(_ video: String, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
if let error = error {
// we got back an error!
showToast(message: "Could not save!! \n\(error)", fontSize: 12)
} else {
showToast(message: "Saved", fontSize: 12.0)
}
print(video)
}
@IBAction func didTapOnTakePhotoButton(_ sender: Any) {
if self.cameraConfig.outputType == .photo {
self.cameraConfig.captureImage { (image, error) in
guard let image = image else {
print(error ?? "Image capture error")
return
}
// try? PHPhotoLibrary.shared().performChangesAndWait {
// PHAssetChangeRequest.creationRequestForAsset(from: image)
// }
UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.image(_:didFinishSavingWithError:contextInfo:)), nil)
}
} else {
if videoRecordingStarted {
videoRecordingStarted = false
toggleCameraButton.isHidden = false
cameraSelectionButton.isHidden = false
videoCameraButton.isHidden = false
toggleFlashButton.isHidden = false
toggleHelp.isHidden = false
self.cameraConfig.stopRecording { (error) in
print(error ?? "Video recording error")
}
} else if !videoRecordingStarted {
toggleCameraButton.isHidden = true
cameraSelectionButton.isHidden = true
videoCameraButton.isHidden = true
toggleFlashButton.isHidden = true
toggleHelp.isHidden = true
videoRecordingStarted = true
self.cameraConfig.recordVideo { (url, error) in
guard let url = url else {
print(error ?? "Video recording error")
return
}
UISaveVideoAtPathToSavedPhotosAlbum(url.path, self, #selector(self.video(_:didFinishSavingWithError:contextInfo:)), nil)
}
}
}
}
@IBAction func toggleCamera(_ sender: Any) {
do {
try cameraConfig.switchCameras()
} catch {
print(error.localizedDescription)
}
switch cameraConfig.currentCameraPosition {
case .some(.front):
self.toggleCameraButton.setImage(#imageLiteral(resourceName: "camera_front"), for: .normal)
case .some(.rear):
self.toggleCameraButton.setImage(#imageLiteral(resourceName: "camera_rear"), for: .normal)
default: return
}
}
@IBAction func selectVideoMode(_ sender: Any) {
self.cameraConfig.outputType = .video
self.cameraSelectionButton.tintColor = UIColor.lightGray
self.videoCameraButton.tintColor = UIColor.blue
}
@IBAction func selectCameraMode(_ sender: Any) {
self.cameraConfig.outputType = .photo
self.videoCameraButton.tintColor = UIColor.lightGray
self.cameraSelectionButton.tintColor = UIColor.blue
}
}
extension CameraViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
@objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as? URL {
print("videoURL:\(String(describing: videoURL))")
}
self.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
}