Commit 27e11c4d by Daniel Dahan

added adjustment method for CaptureSession, to correctly adjust the underlying…

added adjustment method for CaptureSession, to correctly adjust the underlying image captured based on orientation
parent dd92bc32
...@@ -36,7 +36,7 @@ flow of your application. ...@@ -36,7 +36,7 @@ flow of your application.
import UIKit import UIKit
import Material import Material
class AppMenuController: MenuController, MenuViewDelegate { class AppMenuController: MenuController {
/// MenuView diameter. /// MenuView diameter.
private let baseSize: CGSize = CGSizeMake(56, 56) private let baseSize: CGSize = CGSizeMake(56, 56)
...@@ -74,11 +74,6 @@ class AppMenuController: MenuController, MenuViewDelegate { ...@@ -74,11 +74,6 @@ class AppMenuController: MenuController, MenuViewDelegate {
(menuView.menu.views?.first as? MaterialButton)?.animate(MaterialAnimation.rotate(angle: 0)) (menuView.menu.views?.first as? MaterialButton)?.animate(MaterialAnimation.rotate(angle: 0))
} }
// Handles touch outside
func menuViewDidTapOutside(menuView: MenuView) {
closeMenu()
}
/// Handler for blue button. /// Handler for blue button.
func handleBlueButton() { func handleBlueButton() {
closeMenu() closeMenu()
...@@ -103,7 +98,6 @@ class AppMenuController: MenuController, MenuViewDelegate { ...@@ -103,7 +98,6 @@ class AppMenuController: MenuController, MenuViewDelegate {
} }
} }
/// Prepares the menuView. /// Prepares the menuView.
private func prepareMenuView() { private func prepareMenuView() {
var image: UIImage? = MaterialIcon.cm.add var image: UIImage? = MaterialIcon.cm.add
...@@ -159,3 +153,10 @@ class AppMenuController: MenuController, MenuViewDelegate { ...@@ -159,3 +153,10 @@ class AppMenuController: MenuController, MenuViewDelegate {
} }
} }
/// MenuViewDelegate.
extension AppMenuController: MenuViewDelegate {
func menuViewDidTapOutside(menuView: MenuView) {
closeMenu()
}
}
...@@ -237,21 +237,13 @@ class ViewController: UIViewController, CaptureViewDelegate, CaptureSessionDeleg ...@@ -237,21 +237,13 @@ class ViewController: UIViewController, CaptureViewDelegate, CaptureSessionDeleg
toolbar.depth = .None toolbar.depth = .None
// Title label. // Title label.
let titleLabel: UILabel = UILabel() toolbar.titleLabel.hidden = true
titleLabel.hidden = true toolbar.titleLabel.textColor = MaterialColor.white
titleLabel.textAlignment = .Center
titleLabel.textColor = MaterialColor.white
titleLabel.font = RobotoFont.regular
toolbar.titleLabel = titleLabel
// Detail label. // Detail label.
let detailLabel: UILabel = UILabel() toolbar.detail = "Recording"
detailLabel.hidden = true toolbar.detailLabel.hidden = true
detailLabel.text = "Recording" toolbar.detailLabel.textColor = MaterialColor.red.accent1
detailLabel.textAlignment = .Center
detailLabel.textColor = MaterialColor.red.accent1
detailLabel.font = RobotoFont.regular
toolbar.detailLabel = detailLabel
toolbar.leftControls = [switchCamerasButton] toolbar.leftControls = [switchCamerasButton]
toolbar.rightControls = [flashButton] toolbar.rightControls = [flashButton]
......
...@@ -36,7 +36,7 @@ flow of your application. ...@@ -36,7 +36,7 @@ flow of your application.
import UIKit import UIKit
import Material import Material
class AppMenuController: MenuController, MenuViewDelegate { class AppMenuController: MenuController {
/// MenuView diameter. /// MenuView diameter.
private let baseSize: CGSize = CGSizeMake(56, 56) private let baseSize: CGSize = CGSizeMake(56, 56)
...@@ -105,11 +105,7 @@ class AppMenuController: MenuController, MenuViewDelegate { ...@@ -105,11 +105,7 @@ class AppMenuController: MenuController, MenuViewDelegate {
view.backgroundColor = MaterialColor.black view.backgroundColor = MaterialColor.black
prepareMenuView() prepareMenuView()
} }

func menuViewDidTapOutside(menuView: MenuView) {
closeMenu()
}
/// Prepares the add button. /// Prepares the add button.
private func prepareMenuView() { private func prepareMenuView() {
var image: UIImage? = MaterialIcon.cm.add var image: UIImage? = MaterialIcon.cm.add
...@@ -158,3 +154,10 @@ class AppMenuController: MenuController, MenuViewDelegate { ...@@ -158,3 +154,10 @@ class AppMenuController: MenuController, MenuViewDelegate {
} }
} }
/// MenuViewDelegate.
extension AppMenuController: MenuViewDelegate {
func menuViewDidTapOutside(menuView: MenuView) {
closeMenu()
}
}
...@@ -279,7 +279,7 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate { ...@@ -279,7 +279,7 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate {
var userInfo: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>() var userInfo: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>()
userInfo[NSLocalizedDescriptionKey] = "[Material Error: Unsupported focusMode.]" userInfo[NSLocalizedDescriptionKey] = "[Material Error: Unsupported focusMode.]"
userInfo[NSLocalizedFailureReasonErrorKey] = "[Material Error: Unsupported focusMode.]" userInfo[NSLocalizedFailureReasonErrorKey] = "[Material Error: Unsupported focusMode.]"
error = NSError(domain: "io.material.CaptureView", code: 0001, userInfo: userInfo) error = NSError(domain: "io.cosmicmind.Material.CaptureView", code: 0001, userInfo: userInfo)
userInfo[NSUnderlyingErrorKey] = error userInfo[NSUnderlyingErrorKey] = error
} }
if let e: NSError = error { if let e: NSError = error {
...@@ -310,7 +310,7 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate { ...@@ -310,7 +310,7 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate {
var userInfo: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>() var userInfo: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>()
userInfo[NSLocalizedDescriptionKey] = "[Material Error: Unsupported flashMode.]" userInfo[NSLocalizedDescriptionKey] = "[Material Error: Unsupported flashMode.]"
userInfo[NSLocalizedFailureReasonErrorKey] = "[Material Error: Unsupported flashMode.]" userInfo[NSLocalizedFailureReasonErrorKey] = "[Material Error: Unsupported flashMode.]"
error = NSError(domain: "io.material.CaptureView", code: 0002, userInfo: userInfo) error = NSError(domain: "io.cosmicmind.Material.CaptureView", code: 0002, userInfo: userInfo)
userInfo[NSUnderlyingErrorKey] = error userInfo[NSUnderlyingErrorKey] = error
} }
if let e: NSError = error { if let e: NSError = error {
...@@ -341,7 +341,7 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate { ...@@ -341,7 +341,7 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate {
var userInfo: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>() var userInfo: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>()
userInfo[NSLocalizedDescriptionKey] = "[Material Error: Unsupported torchMode.]" userInfo[NSLocalizedDescriptionKey] = "[Material Error: Unsupported torchMode.]"
userInfo[NSLocalizedFailureReasonErrorKey] = "[Material Error: Unsupported torchMode.]" userInfo[NSLocalizedFailureReasonErrorKey] = "[Material Error: Unsupported torchMode.]"
error = NSError(domain: "io.material.CaptureView", code: 0003, userInfo: userInfo) error = NSError(domain: "io.cosmicmind.Material.CaptureView", code: 0003, userInfo: userInfo)
userInfo[NSUnderlyingErrorKey] = error userInfo[NSUnderlyingErrorKey] = error
} }
if let e: NSError = error { if let e: NSError = error {
...@@ -350,92 +350,78 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate { ...@@ -350,92 +350,78 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate {
} }
} }
/** /// The session quality preset.
:name: sessionPreset
*/
public var sessionPreset: CaptureSessionPreset { public var sessionPreset: CaptureSessionPreset {
didSet { didSet {
session.sessionPreset = CaptureSessionPresetToString(sessionPreset) session.sessionPreset = CaptureSessionPresetToString(sessionPreset)
} }
} }
/** /// The capture video orientation.
:name: sessionPreset public var videoOrientation: AVCaptureVideoOrientation {
*/ var o: AVCaptureVideoOrientation
public var currentVideoOrientation: AVCaptureVideoOrientation {
var orientation: AVCaptureVideoOrientation
switch UIDevice.currentDevice().orientation { switch UIDevice.currentDevice().orientation {
case .Portrait: case .Portrait:
orientation = .Portrait o = .Portrait
case .LandscapeRight: case .LandscapeRight:
orientation = .LandscapeLeft o = .LandscapeLeft
case .PortraitUpsideDown: case .PortraitUpsideDown:
orientation = .PortraitUpsideDown o = .PortraitUpsideDown
default: default:
orientation = .LandscapeRight o = .LandscapeRight
} }
return orientation return o
} }
/** /// A delegation property for CaptureSessionDelegate.
:name: delegate
*/
public weak var delegate: CaptureSessionDelegate? public weak var delegate: CaptureSessionDelegate?
/** /// Initializer.
:name: init
*/
public override init() { public override init() {
sessionPreset = .PresetHigh sessionPreset = .PresetHigh
super.init() super.init()
prepareSession() prepareSession()
} }
/** /// Starts the session.
:name: startSession
*/
public func startSession() { public func startSession() {
if !isRunning { if !isRunning {
dispatch_async(sessionQueue) { dispatch_async(sessionQueue) { [weak self] in
self.session.startRunning() self?.session.startRunning()
} }
} }
} }
/** /// Stops the session.
:name: startSession
*/
public func stopSession() { public func stopSession() {
if isRunning { if isRunning {
dispatch_async(sessionQueue) { dispatch_async(sessionQueue) { [weak self] in
self.session.stopRunning() self?.session.stopRunning()
} }
} }
} }
/** /// Switches the camera if possible.
:name: switchCameras
*/
public func switchCameras() { public func switchCameras() {
if canSwitchCameras { if canSwitchCameras {
do { do {
if let v: AVCaptureDevicePosition = self.cameraPosition { if let v: AVCaptureDevicePosition = cameraPosition {
self.delegate?.captureSessionWillSwitchCameras?(self, position: v) delegate?.captureSessionWillSwitchCameras?(self, position: v)
let videoInput: AVCaptureDeviceInput? = try AVCaptureDeviceInput(device: self.inactiveCamera!) let videoInput: AVCaptureDeviceInput? = try AVCaptureDeviceInput(device: inactiveCamera!)
self.session.beginConfiguration() session.beginConfiguration()
self.session.removeInput(self.activeVideoInput) session.removeInput(activeVideoInput)
if self.session.canAddInput(videoInput) { if session.canAddInput(videoInput) {
self.session.addInput(videoInput) session.addInput(videoInput)
self.activeVideoInput = videoInput activeVideoInput = videoInput
} else { } else {
self.session.addInput(self.activeVideoInput) session.addInput(activeVideoInput)
} }
self.session.commitConfiguration() session.commitConfiguration()
self.delegate?.captureSessionDidSwitchCameras?(self, position: self.cameraPosition!) delegate?.captureSessionDidSwitchCameras?(self, position: cameraPosition!)
} }
} catch let e as NSError { } catch let e as NSError {
self.delegate?.captureSessionFailedWithError?(self, error: e) delegate?.captureSessionFailedWithError?(self, error: e)
} }
} }
} }
...@@ -487,7 +473,7 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate { ...@@ -487,7 +473,7 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate {
var userInfo: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>() var userInfo: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>()
userInfo[NSLocalizedDescriptionKey] = "[Material Error: Unsupported focusAtPoint.]" userInfo[NSLocalizedDescriptionKey] = "[Material Error: Unsupported focusAtPoint.]"
userInfo[NSLocalizedFailureReasonErrorKey] = "[Material Error: Unsupported focusAtPoint.]" userInfo[NSLocalizedFailureReasonErrorKey] = "[Material Error: Unsupported focusAtPoint.]"
error = NSError(domain: "io.material.CaptureView", code: 0004, userInfo: userInfo) error = NSError(domain: "io.cosmicmind.Material.CaptureView", code: 0004, userInfo: userInfo)
userInfo[NSUnderlyingErrorKey] = error userInfo[NSUnderlyingErrorKey] = error
} }
if let e: NSError = error { if let e: NSError = error {
...@@ -517,7 +503,7 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate { ...@@ -517,7 +503,7 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate {
var userInfo: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>() var userInfo: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>()
userInfo[NSLocalizedDescriptionKey] = "[Material Error: Unsupported exposeAtPoint.]" userInfo[NSLocalizedDescriptionKey] = "[Material Error: Unsupported exposeAtPoint.]"
userInfo[NSLocalizedFailureReasonErrorKey] = "[Material Error: Unsupported exposeAtPoint.]" userInfo[NSLocalizedFailureReasonErrorKey] = "[Material Error: Unsupported exposeAtPoint.]"
error = NSError(domain: "io.material.CaptureView", code: 0005, userInfo: userInfo) error = NSError(domain: "io.cosmicmind.Material.CaptureView", code: 0005, userInfo: userInfo)
userInfo[NSUnderlyingErrorKey] = error userInfo[NSUnderlyingErrorKey] = error
} }
if let e: NSError = error { if let e: NSError = error {
...@@ -576,16 +562,37 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate { ...@@ -576,16 +562,37 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate {
:name: captureStillImage :name: captureStillImage
*/ */
public func captureStillImage() { public func captureStillImage() {
dispatch_async(sessionQueue) { dispatch_async(sessionQueue) { [weak self] in
if let v: AVCaptureConnection = self.imageOutput.connectionWithMediaType(AVMediaTypeVideo) { if let s: CaptureSession = self {
v.videoOrientation = self.currentVideoOrientation if let v: AVCaptureConnection = s.imageOutput.connectionWithMediaType(AVMediaTypeVideo) {
self.imageOutput.captureStillImageAsynchronouslyFromConnection(v) { [weak self] (sampleBuffer: CMSampleBuffer!, error: NSError!) -> Void in v.videoOrientation = s.videoOrientation
if let s: CaptureSession = self { s.imageOutput.captureStillImageAsynchronouslyFromConnection(v) { [weak self] (sampleBuffer: CMSampleBuffer!, error: NSError!) -> Void in
if nil == error { if let s: CaptureSession = self {
let data: NSData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer) var captureError: NSError? = error
s.delegate?.captureStillImageAsynchronously?(s, image: UIImage(data: data)!) if nil == captureError {
} else { let data: NSData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
s.delegate?.captureStillImageAsynchronouslyFailedWithError?(s, error: error!) if let image1: UIImage = UIImage(data: data) {
if let image2: UIImage = s.adjustOrientationForImage(image1) {
s.delegate?.captureStillImageAsynchronously?(s, image: image2)
} else {
var userInfo: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>()
userInfo[NSLocalizedDescriptionKey] = "[Material Error: Cannot fix image orientation.]"
userInfo[NSLocalizedFailureReasonErrorKey] = "[Material Error: Cannot fix image orientation.]"
captureError = NSError(domain: "io.cosmicmind.Material.CaptureView", code: 0006, userInfo: userInfo)
userInfo[NSUnderlyingErrorKey] = error
}
} else {
var userInfo: Dictionary<String, AnyObject> = Dictionary<String, AnyObject>()
userInfo[NSLocalizedDescriptionKey] = "[Material Error: Cannot capture image from data.]"
userInfo[NSLocalizedFailureReasonErrorKey] = "[Material Error: Cannot capture image from data.]"
captureError = NSError(domain: "io.cosmicmind.Material.CaptureView", code: 0007, userInfo: userInfo)
userInfo[NSUnderlyingErrorKey] = error
}
}
if let e: NSError = captureError {
s.delegate?.captureStillImageAsynchronouslyFailedWithError?(s, error: e)
}
} }
} }
} }
...@@ -598,25 +605,27 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate { ...@@ -598,25 +605,27 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate {
*/ */
public func startRecording() { public func startRecording() {
if !isRecording { if !isRecording {
dispatch_async(sessionQueue) { dispatch_async(sessionQueue) { [weak self] in
if let v: AVCaptureConnection = self.movieOutput.connectionWithMediaType(AVMediaTypeVideo) { if let s: CaptureSession = self {
v.videoOrientation = self.currentVideoOrientation if let v: AVCaptureConnection = s.movieOutput.connectionWithMediaType(AVMediaTypeVideo) {
v.preferredVideoStabilizationMode = .Auto v.videoOrientation = s.videoOrientation
} v.preferredVideoStabilizationMode = .Auto
if let v: AVCaptureDevice = self.activeCamera {
if v.smoothAutoFocusSupported {
do {
try v.lockForConfiguration()
v.smoothAutoFocusEnabled = true
v.unlockForConfiguration()
} catch let e as NSError {
self.delegate?.captureSessionFailedWithError?(self, error: e)
}
} }
if let v: AVCaptureDevice = s.activeCamera {
self.movieOutputURL = self.uniqueURL() if v.smoothAutoFocusSupported {
if let v: NSURL = self.movieOutputURL { do {
self.movieOutput.startRecordingToOutputFileURL(v, recordingDelegate: self) try v.lockForConfiguration()
v.smoothAutoFocusEnabled = true
v.unlockForConfiguration()
} catch let e as NSError {
s.delegate?.captureSessionFailedWithError?(s, error: e)
}
}
s.movieOutputURL = s.uniqueURL()
if let v: NSURL = s.movieOutputURL {
s.movieOutput.startRecordingToOutputFileURL(v, recordingDelegate: s)
}
} }
} }
} }
...@@ -733,4 +742,64 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate { ...@@ -733,4 +742,64 @@ public class CaptureSession : NSObject, AVCaptureFileOutputRecordingDelegate {
} }
return nil return nil
} }
/**
Adjusts the orientation of the image from the capture orientation.
This is an issue when taking images, the capture orientation is not set correctly
when using Portrait.
- Parameter image: A UIImage to adjust.
- Returns: An optional UIImage if successful.
*/
private func adjustOrientationForImage(image: UIImage) -> UIImage? {
guard .Up != image.imageOrientation else {
return image
}
var transform: CGAffineTransform = CGAffineTransformIdentity
// Rotate if Left, Right, or Down.
switch image.imageOrientation {
case .Down, .DownMirrored:
transform = CGAffineTransformTranslate(transform, image.size.width, image.size.height)
transform = CGAffineTransformRotate(transform, CGFloat(M_PI))
case .Left, .LeftMirrored:
transform = CGAffineTransformTranslate(transform, image.size.width, 0)
transform = CGAffineTransformRotate(transform, CGFloat(M_PI_2))
case .Right, .RightMirrored:
transform = CGAffineTransformTranslate(transform, 0, image.size.height)
transform = CGAffineTransformRotate(transform, -CGFloat(M_PI_2))
default:break
}
// Flip if mirrored.
switch image.imageOrientation {
case .UpMirrored, .DownMirrored:
transform = CGAffineTransformTranslate(transform, image.size.width, 0)
transform = CGAffineTransformScale(transform, -1, 1)
case .LeftMirrored, .RightMirrored:
transform = CGAffineTransformTranslate(transform, image.size.height, 0)
transform = CGAffineTransformScale(transform, -1, 1)
default:break
}
// Draw the underlying CGImage with the calculated transform.
guard let context = CGBitmapContextCreate(nil, Int(image.size.width), Int(image.size.height), CGImageGetBitsPerComponent(image.CGImage), 0, CGImageGetColorSpace(image.CGImage), CGImageGetBitmapInfo(image.CGImage).rawValue) else {
return nil
}
CGContextConcatCTM(context, transform)
switch image.imageOrientation {
case .Left, .LeftMirrored, .Right, .RightMirrored:
CGContextDrawImage(context, CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width), image.CGImage)
default:
CGContextDrawImage(context, CGRect(origin: .zero, size: image.size), image.CGImage)
}
guard let CGImage = CGBitmapContextCreateImage(context) else {
return nil
}
return UIImage(CGImage: CGImage)
}
} }
...@@ -311,7 +311,7 @@ public class CaptureView : MaterialView, UIGestureRecognizerDelegate { ...@@ -311,7 +311,7 @@ public class CaptureView : MaterialView, UIGestureRecognizerDelegate {
v.frame.origin.x = bounds.width - v.bounds.width - contentInset.right v.frame.origin.x = bounds.width - v.bounds.width - contentInset.right
} }
if let v: AVCaptureConnection = (previewView.layer as! AVCaptureVideoPreviewLayer).connection { if let v: AVCaptureConnection = (previewView.layer as! AVCaptureVideoPreviewLayer).connection {
v.videoOrientation = captureSession.currentVideoOrientation v.videoOrientation = captureSession.videoOrientation
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment