|
|
|
@ -31,25 +31,25 @@ public enum CameraOutputQuality: Int {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Class for handling iDevices custom camera usage
|
|
|
|
/// Class for handling iDevices custom camera usage
|
|
|
|
public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGestureRecognizerDelegate {
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: - Public properties
|
|
|
|
// MARK: - Public properties
|
|
|
|
|
|
|
|
|
|
|
|
/// Capture session to customize camera settings.
|
|
|
|
/// Capture session to customize camera settings.
|
|
|
|
public var captureSession: AVCaptureSession?
|
|
|
|
public var captureSession: AVCaptureSession?
|
|
|
|
|
|
|
|
|
|
|
|
/// Property to determine if the manager should show the error for the user. If you want to show the errors yourself set this to false. If you want to add custom error UI set showErrorBlock property. Default value is false.
|
|
|
|
/// Property to determine if the manager should show the error for the user. If you want to show the errors yourself set this to false. If you want to add custom error UI set showErrorBlock property. Default value is false.
|
|
|
|
public var showErrorsToUsers = false
|
|
|
|
public var showErrorsToUsers = false
|
|
|
|
|
|
|
|
|
|
|
|
/// Property to determine if the manager should show the camera permission popup immediatly when it's needed or you want to show it manually. Default value is true. Be carful cause using the camera requires permission, if you set this value to false and don't ask manually you won't be able to use the camera.
|
|
|
|
/// Property to determine if the manager should show the camera permission popup immediatly when it's needed or you want to show it manually. Default value is true. Be carful cause using the camera requires permission, if you set this value to false and don't ask manually you won't be able to use the camera.
|
|
|
|
public var showAccessPermissionPopupAutomatically = true
|
|
|
|
public var showAccessPermissionPopupAutomatically = true
|
|
|
|
|
|
|
|
|
|
|
|
/// A block creating UI to present error message to the user. This can be customised to be presented on the Window root view controller, or to pass in the viewController which will present the UIAlertController, for example.
|
|
|
|
/// A block creating UI to present error message to the user. This can be customised to be presented on the Window root view controller, or to pass in the viewController which will present the UIAlertController, for example.
|
|
|
|
public var showErrorBlock:(erTitle: String, erMessage: String) -> Void = { (erTitle: String, erMessage: String) -> Void in
|
|
|
|
public var showErrorBlock:(erTitle: String, erMessage: String) -> Void = { (erTitle: String, erMessage: String) -> Void in
|
|
|
|
|
|
|
|
|
|
|
|
// var alertController = UIAlertController(title: erTitle, message: erMessage, preferredStyle: .Alert)
|
|
|
|
// var alertController = UIAlertController(title: erTitle, message: erMessage, preferredStyle: .Alert)
|
|
|
|
// alertController.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: { (alertAction) -> Void in }))
|
|
|
|
// alertController.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: { (alertAction) -> Void in }))
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// if let topController = UIApplication.sharedApplication().keyWindow?.rootViewController {
|
|
|
|
// if let topController = UIApplication.sharedApplication().keyWindow?.rootViewController {
|
|
|
|
// topController.presentViewController(alertController, animated: true, completion:nil)
|
|
|
|
// topController.presentViewController(alertController, animated: true, completion:nil)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
@ -58,6 +58,9 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
/// Property to determine if manager should write the resources to the phone library. Default value is true.
|
|
|
|
/// Property to determine if manager should write the resources to the phone library. Default value is true.
|
|
|
|
public var writeFilesToPhoneLibrary = true
|
|
|
|
public var writeFilesToPhoneLibrary = true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Property to determine if manager allows zoom. Default value is true.
|
|
|
|
|
|
|
|
public var enableZoom = true
|
|
|
|
|
|
|
|
|
|
|
|
/// Property to determine if manager should follow device orientation. Default value is true.
|
|
|
|
/// Property to determine if manager should follow device orientation. Default value is true.
|
|
|
|
public var shouldRespondToOrientationChanges = true {
|
|
|
|
public var shouldRespondToOrientationChanges = true {
|
|
|
|
didSet {
|
|
|
|
didSet {
|
|
|
|
@ -68,14 +71,14 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// The Bool property to determine if the camera is ready to use.
|
|
|
|
/// The Bool property to determine if the camera is ready to use.
|
|
|
|
public var cameraIsReady: Bool {
|
|
|
|
public var cameraIsReady: Bool {
|
|
|
|
get {
|
|
|
|
get {
|
|
|
|
return cameraIsSetup
|
|
|
|
return cameraIsSetup
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// The Bool property to determine if current device has front camera.
|
|
|
|
/// The Bool property to determine if current device has front camera.
|
|
|
|
public var hasFrontCamera: Bool = {
|
|
|
|
public var hasFrontCamera: Bool = {
|
|
|
|
let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
|
|
|
|
let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
|
|
|
|
@ -87,7 +90,7 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}()
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
/// The Bool property to determine if current device has flash.
|
|
|
|
/// The Bool property to determine if current device has flash.
|
|
|
|
public var hasFlash: Bool = {
|
|
|
|
public var hasFlash: Bool = {
|
|
|
|
let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
|
|
|
|
let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
|
|
|
|
@ -99,7 +102,7 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}()
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
/// Property to change camera device between front and back.
|
|
|
|
/// Property to change camera device between front and back.
|
|
|
|
public var cameraDevice = CameraDevice.Back {
|
|
|
|
public var cameraDevice = CameraDevice.Back {
|
|
|
|
didSet {
|
|
|
|
didSet {
|
|
|
|
@ -139,18 +142,19 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
if cameraIsSetup {
|
|
|
|
if cameraIsSetup {
|
|
|
|
if cameraOutputMode != oldValue {
|
|
|
|
if cameraOutputMode != oldValue {
|
|
|
|
_setupOutputMode(cameraOutputMode, oldCameraOutputMode: oldValue)
|
|
|
|
_setupOutputMode(cameraOutputMode, oldCameraOutputMode: oldValue)
|
|
|
|
|
|
|
|
_setupMaxZoom()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Property to check video recording duration when in progress
|
|
|
|
/// Property to check video recording duration when in progress
|
|
|
|
public var recordedDuration : CMTime { return movieOutput?.recordedDuration ?? kCMTimeZero }
|
|
|
|
public var recordedDuration : CMTime { return movieOutput?.recordedDuration ?? kCMTimeZero }
|
|
|
|
|
|
|
|
|
|
|
|
/// Property to check video recording file size when in progress
|
|
|
|
/// Property to check video recording file size when in progress
|
|
|
|
public var recordedFileSize : Int64 { return movieOutput?.recordedFileSize ?? 0 }
|
|
|
|
public var recordedFileSize : Int64 { return movieOutput?.recordedFileSize ?? 0 }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: - Private properties
|
|
|
|
// MARK: - Private properties
|
|
|
|
|
|
|
|
|
|
|
|
private weak var embedingView: UIView?
|
|
|
|
private weak var embedingView: UIView?
|
|
|
|
@ -162,16 +166,16 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo) as! [AVCaptureDevice]
|
|
|
|
let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo) as! [AVCaptureDevice]
|
|
|
|
return devices.filter{$0.position == .Front}.first
|
|
|
|
return devices.filter{$0.position == .Front}.first
|
|
|
|
}()
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
private lazy var backCameraDevice: AVCaptureDevice? = {
|
|
|
|
private lazy var backCameraDevice: AVCaptureDevice? = {
|
|
|
|
let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo) as! [AVCaptureDevice]
|
|
|
|
let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo) as! [AVCaptureDevice]
|
|
|
|
return devices.filter{$0.position == .Back}.first
|
|
|
|
return devices.filter{$0.position == .Back}.first
|
|
|
|
}()
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
private lazy var mic: AVCaptureDevice? = {
|
|
|
|
private lazy var mic: AVCaptureDevice? = {
|
|
|
|
return AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeAudio)
|
|
|
|
return AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeAudio)
|
|
|
|
}()
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
private var stillImageOutput: AVCaptureStillImageOutput?
|
|
|
|
private var stillImageOutput: AVCaptureStillImageOutput?
|
|
|
|
private var movieOutput: AVCaptureMovieFileOutput?
|
|
|
|
private var movieOutput: AVCaptureMovieFileOutput?
|
|
|
|
private var previewLayer: AVCaptureVideoPreviewLayer?
|
|
|
|
private var previewLayer: AVCaptureVideoPreviewLayer?
|
|
|
|
@ -188,9 +192,14 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
} catch { }
|
|
|
|
} catch { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NSURL(string: tempPath)!
|
|
|
|
return NSURL(string: tempPath)!
|
|
|
|
}()
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private var pinchToZoom: UIPinchGestureRecognizer?
|
|
|
|
|
|
|
|
private var zoomScale: CGFloat = 1.0
|
|
|
|
|
|
|
|
private var beginZoomScale: CGFloat = 1.0
|
|
|
|
|
|
|
|
private var maxZoomFactor: CGFloat = 1.0
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: - CameraManager
|
|
|
|
// MARK: - CameraManager
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
@ -199,7 +208,7 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
:param: view The view you want to add the preview layer to
|
|
|
|
:param: view The view you want to add the preview layer to
|
|
|
|
:param: cameraOutputMode The mode you want capturesession to run image / video / video and microphone
|
|
|
|
:param: cameraOutputMode The mode you want capturesession to run image / video / video and microphone
|
|
|
|
:param: completition Optional completition block
|
|
|
|
:param: completition Optional completition block
|
|
|
|
|
|
|
|
|
|
|
|
:returns: Current state of the camera: Ready / AccessDenied / NoDeviceFound / NotDetermined.
|
|
|
|
:returns: Current state of the camera: Ready / AccessDenied / NoDeviceFound / NotDetermined.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public func addPreviewLayerToView(view: UIView) -> CameraState {
|
|
|
|
public func addPreviewLayerToView(view: UIView) -> CameraState {
|
|
|
|
@ -236,7 +245,7 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
Asks the user for camera permissions. Only works if the permissions are not yet determined. Note that it'll also automaticaly ask about the microphone permissions if you selected VideoWithMic output.
|
|
|
|
Asks the user for camera permissions. Only works if the permissions are not yet determined. Note that it'll also automaticaly ask about the microphone permissions if you selected VideoWithMic output.
|
|
|
|
|
|
|
|
|
|
|
|
:param: completition Completition block with the result of permission request
|
|
|
|
:param: completition Completition block with the result of permission request
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public func askUserForCameraPermissions(completition: Bool -> Void) {
|
|
|
|
public func askUserForCameraPermissions(completition: Bool -> Void) {
|
|
|
|
@ -340,7 +349,7 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
imageCompletition(UIImage(data: imageData), nil)
|
|
|
|
imageCompletition(UIImage(data: imageData), nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
_show(NSLocalizedString("Capture session output mode video", comment:""), message: NSLocalizedString("I can't take any picture", comment:""))
|
|
|
|
_show(NSLocalizedString("Capture session output mode video", comment:""), message: NSLocalizedString("I can't take any picture", comment:""))
|
|
|
|
@ -375,33 +384,33 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
Current camera status.
|
|
|
|
Current camera status.
|
|
|
|
|
|
|
|
|
|
|
|
:returns: Current state of the camera: Ready / AccessDenied / NoDeviceFound / NotDetermined
|
|
|
|
:returns: Current state of the camera: Ready / AccessDenied / NoDeviceFound / NotDetermined
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public func currentCameraStatus() -> CameraState {
|
|
|
|
public func currentCameraStatus() -> CameraState {
|
|
|
|
return _checkIfCameraIsAvailable()
|
|
|
|
return _checkIfCameraIsAvailable()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
Change current flash mode to next value from available ones.
|
|
|
|
Change current flash mode to next value from available ones.
|
|
|
|
|
|
|
|
|
|
|
|
:returns: Current flash mode: Off / On / Auto
|
|
|
|
:returns: Current flash mode: Off / On / Auto
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public func changeFlashMode() -> CameraFlashMode {
|
|
|
|
public func changeFlashMode() -> CameraFlashMode {
|
|
|
|
flashMode = CameraFlashMode(rawValue: (flashMode.rawValue+1)%3)!
|
|
|
|
flashMode = CameraFlashMode(rawValue: (flashMode.rawValue+1)%3)!
|
|
|
|
return flashMode
|
|
|
|
return flashMode
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
Change current output quality mode to next value from available ones.
|
|
|
|
Change current output quality mode to next value from available ones.
|
|
|
|
|
|
|
|
|
|
|
|
:returns: Current quality mode: Low / Medium / High
|
|
|
|
:returns: Current quality mode: Low / Medium / High
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public func changeQualityMode() -> CameraOutputQuality {
|
|
|
|
public func changeQualityMode() -> CameraOutputQuality {
|
|
|
|
cameraOutputQuality = CameraOutputQuality(rawValue: (cameraOutputQuality.rawValue+1)%3)!
|
|
|
|
cameraOutputQuality = CameraOutputQuality(rawValue: (cameraOutputQuality.rawValue+1)%3)!
|
|
|
|
return cameraOutputQuality
|
|
|
|
return cameraOutputQuality
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: - AVCaptureFileOutputRecordingDelegate
|
|
|
|
// MARK: - AVCaptureFileOutputRecordingDelegate
|
|
|
|
|
|
|
|
|
|
|
|
public func captureOutput(captureOutput: AVCaptureFileOutput!, didStartRecordingToOutputFileAtURL fileURL: NSURL!, fromConnections connections: [AnyObject]!) {
|
|
|
|
public func captureOutput(captureOutput: AVCaptureFileOutput!, didStartRecordingToOutputFileAtURL fileURL: NSURL!, fromConnections connections: [AnyObject]!) {
|
|
|
|
@ -435,9 +444,48 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: - UIGestureRecognizerDelegate
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if gestureRecognizer.isKindOfClass(UIPinchGestureRecognizer) {
|
|
|
|
|
|
|
|
beginZoomScale = zoomScale;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _handlePinchGesture(pinchRecognizer: UIPinchGestureRecognizer) {
|
|
|
|
|
|
|
|
var allTouchesAreOnThePreviewLayer = true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let numTouches = pinchRecognizer.numberOfTouches();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for index in 0..<numTouches {
|
|
|
|
|
|
|
|
let location: CGPoint = pinchRecognizer.locationOfTouch(index, inView: embedingView)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let convertedLocation: CGPoint = (embedingView?.convertPoint(location, fromCoordinateSpace: (embedingView?.superview)!))!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((embedingView?.pointInside(convertedLocation, withEvent: nil)) == nil) {
|
|
|
|
|
|
|
|
allTouchesAreOnThePreviewLayer = false
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if allTouchesAreOnThePreviewLayer {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_updateZoomScale(pinchRecognizer.scale)
|
|
|
|
|
|
|
|
_applyZoomScale()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: - CameraManager()
|
|
|
|
// MARK: - CameraManager()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private func _updateZoomScale(scale: CGFloat) {
|
|
|
|
|
|
|
|
zoomScale = max(1.0, min(beginZoomScale * scale, maxZoomFactor))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func _updateTorch(flashMode: CameraFlashMode) {
|
|
|
|
private func _updateTorch(flashMode: CameraFlashMode) {
|
|
|
|
captureSession?.beginConfiguration()
|
|
|
|
captureSession?.beginConfiguration()
|
|
|
|
let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
|
|
|
|
let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
|
|
|
|
@ -458,7 +506,7 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
captureSession?.commitConfiguration()
|
|
|
|
captureSession?.commitConfiguration()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func _executeVideoCompletitionWithURL(url: NSURL?, error: NSError?) {
|
|
|
|
private func _executeVideoCompletitionWithURL(url: NSURL?, error: NSError?) {
|
|
|
|
if let validCompletition = videoCompletition {
|
|
|
|
if let validCompletition = videoCompletition {
|
|
|
|
validCompletition(videoURL: url, error: error)
|
|
|
|
validCompletition(videoURL: url, error: error)
|
|
|
|
@ -473,7 +521,7 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
shouldReinitializeMovieOutput = shouldReinitializeMovieOutput || !connection.active
|
|
|
|
shouldReinitializeMovieOutput = shouldReinitializeMovieOutput || !connection.active
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if shouldReinitializeMovieOutput {
|
|
|
|
if shouldReinitializeMovieOutput {
|
|
|
|
movieOutput = AVCaptureMovieFileOutput()
|
|
|
|
movieOutput = AVCaptureMovieFileOutput()
|
|
|
|
movieOutput!.movieFragmentInterval = kCMTimeInvalid
|
|
|
|
movieOutput!.movieFragmentInterval = kCMTimeInvalid
|
|
|
|
@ -484,7 +532,7 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return movieOutput!
|
|
|
|
return movieOutput!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func _getStillImageOutput() -> AVCaptureStillImageOutput {
|
|
|
|
private func _getStillImageOutput() -> AVCaptureStillImageOutput {
|
|
|
|
var shouldReinitializeStillImageOutput = stillImageOutput == nil
|
|
|
|
var shouldReinitializeStillImageOutput = stillImageOutput == nil
|
|
|
|
if !shouldReinitializeStillImageOutput {
|
|
|
|
if !shouldReinitializeStillImageOutput {
|
|
|
|
@ -494,19 +542,22 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if shouldReinitializeStillImageOutput {
|
|
|
|
if shouldReinitializeStillImageOutput {
|
|
|
|
stillImageOutput = AVCaptureStillImageOutput()
|
|
|
|
stillImageOutput = AVCaptureStillImageOutput()
|
|
|
|
|
|
|
|
|
|
|
|
captureSession?.beginConfiguration()
|
|
|
|
captureSession?.beginConfiguration()
|
|
|
|
captureSession?.addOutput(stillImageOutput)
|
|
|
|
captureSession?.addOutput(stillImageOutput)
|
|
|
|
captureSession?.commitConfiguration()
|
|
|
|
captureSession?.commitConfiguration()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stillImageOutput!.connectionWithMediaType(AVMediaTypeVideo)?.videoScaleAndCropFactor = zoomScale
|
|
|
|
|
|
|
|
|
|
|
|
return stillImageOutput!
|
|
|
|
return stillImageOutput!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@objc private func _orientationChanged() {
|
|
|
|
@objc private func _orientationChanged() {
|
|
|
|
var currentConnection: AVCaptureConnection?;
|
|
|
|
var currentConnection: AVCaptureConnection?;
|
|
|
|
switch cameraOutputMode {
|
|
|
|
switch cameraOutputMode {
|
|
|
|
case .StillImage:
|
|
|
|
case .StillImage:
|
|
|
|
currentConnection = stillImageOutput?.connectionWithMediaType(AVMediaTypeVideo)
|
|
|
|
currentConnection = _getStillImageOutput().connectionWithMediaType(AVMediaTypeVideo)
|
|
|
|
case .VideoOnly, .VideoWithMic:
|
|
|
|
case .VideoOnly, .VideoWithMic:
|
|
|
|
currentConnection = _getMovieOutput().connectionWithMediaType(AVMediaTypeVideo)
|
|
|
|
currentConnection = _getMovieOutput().connectionWithMediaType(AVMediaTypeVideo)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -547,12 +598,13 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
|
|
|
|
|
|
|
|
private func _setupCamera(completition: Void -> Void) {
|
|
|
|
private func _setupCamera(completition: Void -> Void) {
|
|
|
|
captureSession = AVCaptureSession()
|
|
|
|
captureSession = AVCaptureSession()
|
|
|
|
|
|
|
|
|
|
|
|
dispatch_async(sessionQueue, {
|
|
|
|
dispatch_async(sessionQueue, {
|
|
|
|
if let validCaptureSession = self.captureSession {
|
|
|
|
if let validCaptureSession = self.captureSession {
|
|
|
|
validCaptureSession.beginConfiguration()
|
|
|
|
validCaptureSession.beginConfiguration()
|
|
|
|
validCaptureSession.sessionPreset = AVCaptureSessionPresetHigh
|
|
|
|
validCaptureSession.sessionPreset = AVCaptureSessionPresetHigh
|
|
|
|
self._updateCameraDevice(self.cameraDevice)
|
|
|
|
self._updateCameraDevice(self.cameraDevice)
|
|
|
|
|
|
|
|
self._setupPinchToZoom()
|
|
|
|
self._setupOutputs()
|
|
|
|
self._setupOutputs()
|
|
|
|
self._setupOutputMode(self.cameraOutputMode, oldCameraOutputMode: nil)
|
|
|
|
self._setupOutputMode(self.cameraOutputMode, oldCameraOutputMode: nil)
|
|
|
|
self._setupPreviewLayer()
|
|
|
|
self._setupPreviewLayer()
|
|
|
|
@ -563,7 +615,7 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
self._startFollowingDeviceOrientation()
|
|
|
|
self._startFollowingDeviceOrientation()
|
|
|
|
self.cameraIsSetup = true
|
|
|
|
self.cameraIsSetup = true
|
|
|
|
self._orientationChanged()
|
|
|
|
self._orientationChanged()
|
|
|
|
|
|
|
|
|
|
|
|
completition()
|
|
|
|
completition()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
@ -585,6 +637,7 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
|
|
|
|
|
|
|
|
private func _addPreviewLayerToView(view: UIView) {
|
|
|
|
private func _addPreviewLayerToView(view: UIView) {
|
|
|
|
embedingView = view
|
|
|
|
embedingView = view
|
|
|
|
|
|
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), { () -> Void in
|
|
|
|
dispatch_async(dispatch_get_main_queue(), { () -> Void in
|
|
|
|
guard let _ = self.previewLayer else {
|
|
|
|
guard let _ = self.previewLayer else {
|
|
|
|
return
|
|
|
|
return
|
|
|
|
@ -614,15 +667,29 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private func _setupMaxZoom() {
|
|
|
|
|
|
|
|
if cameraDevice == .Back {
|
|
|
|
|
|
|
|
if let maxZoom = backCameraDevice?.activeFormat.videoMaxZoomFactor {
|
|
|
|
|
|
|
|
maxZoomFactor = maxZoom
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
if let maxZoom = frontCameraDevice?.activeFormat.videoMaxZoomFactor {
|
|
|
|
|
|
|
|
maxZoomFactor = maxZoom
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func _setupOutputMode(newCameraOutputMode: CameraOutputMode, oldCameraOutputMode: CameraOutputMode?) {
|
|
|
|
private func _setupOutputMode(newCameraOutputMode: CameraOutputMode, oldCameraOutputMode: CameraOutputMode?) {
|
|
|
|
captureSession?.beginConfiguration()
|
|
|
|
captureSession?.beginConfiguration()
|
|
|
|
|
|
|
|
|
|
|
|
if let cameraOutputToRemove = oldCameraOutputMode {
|
|
|
|
if let cameraOutputToRemove = oldCameraOutputMode {
|
|
|
|
// remove current setting
|
|
|
|
// remove current setting
|
|
|
|
switch cameraOutputToRemove {
|
|
|
|
switch cameraOutputToRemove {
|
|
|
|
case .StillImage:
|
|
|
|
case .StillImage:
|
|
|
|
if let validStillImageOutput = stillImageOutput {
|
|
|
|
if let validStillImageOutput = stillImageOutput {
|
|
|
|
captureSession?.removeOutput(validStillImageOutput)
|
|
|
|
captureSession?.removeOutput(validStillImageOutput)
|
|
|
|
|
|
|
|
_removePinchToZoom()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case .VideoOnly, .VideoWithMic:
|
|
|
|
case .VideoOnly, .VideoWithMic:
|
|
|
|
if let validMovieOutput = movieOutput {
|
|
|
|
if let validMovieOutput = movieOutput {
|
|
|
|
@ -633,7 +700,7 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// configure new devices
|
|
|
|
// configure new devices
|
|
|
|
switch newCameraOutputMode {
|
|
|
|
switch newCameraOutputMode {
|
|
|
|
case .StillImage:
|
|
|
|
case .StillImage:
|
|
|
|
@ -642,10 +709,11 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let validStillImageOutput = stillImageOutput {
|
|
|
|
if let validStillImageOutput = stillImageOutput {
|
|
|
|
captureSession?.addOutput(validStillImageOutput)
|
|
|
|
captureSession?.addOutput(validStillImageOutput)
|
|
|
|
|
|
|
|
_addPinchToZoom()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case .VideoOnly, .VideoWithMic:
|
|
|
|
case .VideoOnly, .VideoWithMic:
|
|
|
|
captureSession?.addOutput(_getMovieOutput())
|
|
|
|
captureSession?.addOutput(_getMovieOutput())
|
|
|
|
|
|
|
|
|
|
|
|
if newCameraOutputMode == .VideoWithMic {
|
|
|
|
if newCameraOutputMode == .VideoWithMic {
|
|
|
|
if let validMic = _deviceInputFromDevice(mic) {
|
|
|
|
if let validMic = _deviceInputFromDevice(mic) {
|
|
|
|
captureSession?.addInput(validMic)
|
|
|
|
captureSession?.addInput(validMic)
|
|
|
|
@ -656,7 +724,7 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
_updateCameraQualityMode(cameraOutputQuality)
|
|
|
|
_updateCameraQualityMode(cameraOutputQuality)
|
|
|
|
_orientationChanged()
|
|
|
|
_orientationChanged()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func _setupOutputs() {
|
|
|
|
private func _setupOutputs() {
|
|
|
|
if (stillImageOutput == nil) {
|
|
|
|
if (stillImageOutput == nil) {
|
|
|
|
stillImageOutput = AVCaptureStillImageOutput()
|
|
|
|
stillImageOutput = AVCaptureStillImageOutput()
|
|
|
|
@ -676,12 +744,12 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
|
|
|
|
previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func _updateCameraDevice(deviceType: CameraDevice) {
|
|
|
|
private func _updateCameraDevice(deviceType: CameraDevice) {
|
|
|
|
if let validCaptureSession = captureSession {
|
|
|
|
if let validCaptureSession = captureSession {
|
|
|
|
validCaptureSession.beginConfiguration()
|
|
|
|
validCaptureSession.beginConfiguration()
|
|
|
|
let inputs = validCaptureSession.inputs as! [AVCaptureInput]
|
|
|
|
let inputs = validCaptureSession.inputs as! [AVCaptureInput]
|
|
|
|
|
|
|
|
|
|
|
|
for input in inputs {
|
|
|
|
for input in inputs {
|
|
|
|
if let deviceInput = input as? AVCaptureDeviceInput {
|
|
|
|
if let deviceInput = input as? AVCaptureDeviceInput {
|
|
|
|
if deviceInput.device == backCameraDevice && cameraDevice == .Front {
|
|
|
|
if deviceInput.device == backCameraDevice && cameraDevice == .Front {
|
|
|
|
@ -712,7 +780,7 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
validCaptureSession.commitConfiguration()
|
|
|
|
validCaptureSession.commitConfiguration()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func _updateFlasMode(flashMode: CameraFlashMode) {
|
|
|
|
private func _updateFlasMode(flashMode: CameraFlashMode) {
|
|
|
|
captureSession?.beginConfiguration()
|
|
|
|
captureSession?.beginConfiguration()
|
|
|
|
let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
|
|
|
|
let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
|
|
|
|
@ -763,7 +831,7 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
|
|
|
|
|
|
|
|
private func _removeMicInput() {
|
|
|
|
private func _removeMicInput() {
|
|
|
|
guard let inputs = captureSession?.inputs as? [AVCaptureInput] else { return }
|
|
|
|
guard let inputs = captureSession?.inputs as? [AVCaptureInput] else { return }
|
|
|
|
|
|
|
|
|
|
|
|
for input in inputs {
|
|
|
|
for input in inputs {
|
|
|
|
if let deviceInput = input as? AVCaptureDeviceInput {
|
|
|
|
if let deviceInput = input as? AVCaptureDeviceInput {
|
|
|
|
if deviceInput.device == mic {
|
|
|
|
if deviceInput.device == mic {
|
|
|
|
@ -773,7 +841,7 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func _show(title: String, message: String) {
|
|
|
|
private func _show(title: String, message: String) {
|
|
|
|
if showErrorsToUsers {
|
|
|
|
if showErrorsToUsers {
|
|
|
|
dispatch_async(dispatch_get_main_queue(), { () -> Void in
|
|
|
|
dispatch_async(dispatch_get_main_queue(), { () -> Void in
|
|
|
|
@ -781,7 +849,7 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private func _deviceInputFromDevice(device: AVCaptureDevice?) -> AVCaptureDeviceInput? {
|
|
|
|
private func _deviceInputFromDevice(device: AVCaptureDevice?) -> AVCaptureDeviceInput? {
|
|
|
|
guard let validDevice = device else { return nil }
|
|
|
|
guard let validDevice = device else { return nil }
|
|
|
|
do {
|
|
|
|
do {
|
|
|
|
@ -791,6 +859,28 @@ public class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate {
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private func _setupPinchToZoom() {
|
|
|
|
|
|
|
|
pinchToZoom = UIPinchGestureRecognizer(target: self, action: "_handlePinchGesture:")
|
|
|
|
|
|
|
|
pinchToZoom!.delegate = self
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private func _addPinchToZoom() {
|
|
|
|
|
|
|
|
embedingView?.addGestureRecognizer(pinchToZoom!)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private func _removePinchToZoom() {
|
|
|
|
|
|
|
|
embedingView?.removeGestureRecognizer(pinchToZoom!)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private func _applyZoomScale() {
|
|
|
|
|
|
|
|
let affineTransform = CGAffineTransformMakeScale(zoomScale, zoomScale)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CATransaction.begin()
|
|
|
|
|
|
|
|
CATransaction.setAnimationDuration(0.025)
|
|
|
|
|
|
|
|
embedingView?.transform = affineTransform
|
|
|
|
|
|
|
|
CATransaction.commit()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
deinit {
|
|
|
|
deinit {
|
|
|
|
stopAndRemoveCaptureSession()
|
|
|
|
stopAndRemoveCaptureSession()
|
|
|
|
|