From ee8a1b619f42e04c33789650043a4a232fbe9124 Mon Sep 17 00:00:00 2001 From: nelanelanela Date: Tue, 14 Oct 2014 10:28:41 +0100 Subject: [PATCH] Video recording option. With or without mic. --- .../UserInterfaceState.xcuserstate | Bin 43963 -> 43116 bytes camera/Base.lproj/Main.storyboard | 29 ++- camera/CameraManager.swift | 218 +++++++++++++++--- camera/ViewController.swift | 31 ++- 4 files changed, 232 insertions(+), 46 deletions(-) diff --git a/camera.xcodeproj/project.xcworkspace/xcuserdata/nataliaterlecka.xcuserdatad/UserInterfaceState.xcuserstate b/camera.xcodeproj/project.xcworkspace/xcuserdata/nataliaterlecka.xcuserdatad/UserInterfaceState.xcuserstate index ae167a42e7a2c2cfb655a664585201a976877aa1..3666d4bf9369a64232279b995702802a1d667c2a 100644 GIT binary patch delta 15753 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 W?vs6o0uc5+1%Z9fPk+0A{Qm&it3^-% delta 16819 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 + @@ -64,15 +83,14 @@ + + - - - @@ -80,11 +98,6 @@ - - - - - diff --git a/camera/CameraManager.swift b/camera/CameraManager.swift index eec5e01..afb1624 100644 --- a/camera/CameraManager.swift +++ b/camera/CameraManager.swift @@ -8,6 +8,7 @@ import UIKit import AVFoundation +import AssetsLibrary private let _singletonSharedInstance = CameraManager() @@ -16,13 +17,15 @@ enum CameraDevice { } enum CameraFlashMode: Int { - case Off - case On - case Auto + case Off, On, Auto +} + +enum CameraOutputMode { + case StillImage, VideoWithMic, VideoOnly } /// Class for handling iDevices custom camera usage -class CameraManager: NSObject { +class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate { /// The Bool property to determin if current device has front camera. var hasFrontCamera: Bool = { @@ -102,16 +105,94 @@ class CameraManager: NSObject { } } - private var captureSession: AVCaptureSession? + /// Property to change camera output. + var cameraOutputMode: CameraOutputMode { + get { + return self.currentCameraOutputMode + } + set(newCameraOutputMode) { + if newCameraOutputMode != self.currentCameraOutputMode { + self.captureSession?.beginConfiguration() + + // remove current setting + switch self.currentCameraOutputMode { + case .StillImage: + if let validStillImageOutput = self.stillImageOutput? { + self.captureSession?.removeOutput(validStillImageOutput) + } + case .VideoOnly, .VideoWithMic: + if let validMovieOutput = self.movieOutput? { + self.captureSession?.removeOutput(validMovieOutput) + } + if self.currentCameraOutputMode == .VideoWithMic { + if let validMic = self.mic? { + self.captureSession?.removeInput(validMic) + } + } + } + // configure new devices + switch newCameraOutputMode { + case .StillImage: + if (self.stillImageOutput == nil) { + self._setupStillImageOutput() + } + if let validStillImageOutput = self.stillImageOutput? { + self.captureSession?.addOutput(validStillImageOutput) + } + self.captureSession?.sessionPreset = AVCaptureSessionPresetPhoto + + case .VideoOnly, .VideoWithMic: + if (self.movieOutput == nil) { + self._setupMovieOutput() + } + if let validMovieOutput = self.movieOutput? { + self.captureSession?.addOutput(validMovieOutput) + } + if self.currentCameraOutputMode == .VideoWithMic { + if (self.mic == nil) { + self._setupMic() + } + if let validMic = self.mic? { + self.captureSession?.addInput(validMic) + } + } + self.captureSession?.sessionPreset = AVCaptureSessionPresetMedium + + } + self.captureSession?.commitConfiguration() + + self.currentCameraOutputMode = newCameraOutputMode + } + } + } + + /// Capture sessioc to customize camera settings. + var captureSession: AVCaptureSession? + + private weak var embedingView: UIView? + private var sessionQueue: dispatch_queue_t = dispatch_queue_create("CameraSessionQueue", DISPATCH_QUEUE_SERIAL) + private var frontCamera: AVCaptureInput? private var rearCamera: AVCaptureInput? + private var mic: AVCaptureDeviceInput? private var stillImageOutput: AVCaptureStillImageOutput? + private var movieOutput: AVCaptureMovieFileOutput? private var previewLayer: AVCaptureVideoPreviewLayer? + private var cameraIsSetup = false + private var currentCameraDevice = CameraDevice.Back private var currentFlashMode = CameraFlashMode.Off - private weak var embedingView: UIView? + private var currentCameraOutputMode = CameraOutputMode.StillImage + + private var tempFilePath: NSURL = { + let tempPath = NSTemporaryDirectory().stringByAppendingPathComponent("tempMovie").stringByAppendingPathExtension("mp4") + if NSFileManager.defaultManager().fileExistsAtPath(tempPath!) { + NSFileManager.defaultManager().removeItemAtPath(tempPath!, error: nil) + } + return NSURL(fileURLWithPath: tempPath!) + }() /// CameraManager singleton instance to use the camera. class var sharedInstance: CameraManager { @@ -127,8 +208,9 @@ class CameraManager: NSObject { Inits a capture session and adds a preview layer to the given view. Preview layer bounds will automaticaly be set to match given view. :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 */ - func addPreeviewLayerToView(view: UIView) + func addPreeviewLayerToView(view: UIView, cameraOutputMode: CameraOutputMode) { if let validEmbedingView = self.embedingView? { if let validPreviewLayer = self.previewLayer? { @@ -137,9 +219,11 @@ class CameraManager: NSObject { } if self.cameraIsSetup { self._addPreeviewLayerToView(view) + self.cameraOutputMode = cameraOutputMode } else { self._setupCamera({ Void -> Void in self._addPreeviewLayerToView(view) + self.cameraOutputMode = cameraOutputMode }) } } @@ -164,7 +248,9 @@ class CameraManager: NSObject { self.captureSession = nil self.frontCamera = nil self.rearCamera = nil + self.mic = nil self.stillImageOutput = nil + self.movieOutput = nil } /** @@ -175,27 +261,81 @@ class CameraManager: NSObject { func capturePictureWithCompletition(imageCompletition: UIImage -> Void) { if self.cameraIsSetup { - dispatch_async(self.sessionQueue, { - if let validStillImageOutput = self.stillImageOutput? { - validStillImageOutput.captureStillImageAsynchronouslyFromConnection(validStillImageOutput.connectionWithMediaType(AVMediaTypeVideo), completionHandler: { [weak self] (sample: CMSampleBuffer!, error: NSError!) -> Void in - if (error? != nil) { - dispatch_async(dispatch_get_main_queue(), { - if let weakSelf = self { - weakSelf._show("error", message: error.localizedDescription) - } - }) - } else { - let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sample) - imageCompletition(UIImage(data: imageData)) - } - }) - } - }) + if self.cameraOutputMode == .StillImage { + dispatch_async(self.sessionQueue, { + if let validStillImageOutput = self.stillImageOutput? { + validStillImageOutput.captureStillImageAsynchronouslyFromConnection(validStillImageOutput.connectionWithMediaType(AVMediaTypeVideo), completionHandler: { [weak self] (sample: CMSampleBuffer!, error: NSError!) -> Void in + if (error? != nil) { + dispatch_async(dispatch_get_main_queue(), { + if let weakSelf = self { + weakSelf._show("error", message: error.localizedDescription) + } + }) + } else { + let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sample) + imageCompletition(UIImage(data: imageData)) + } + }) + } + }) + } else { + self._show("Capture session output mode video", message: "I can't take any picture") + } } else { self._show("No capture session setup", message: "I can't take any picture") } } + /** + Starts recording a video with or without voice as in the session preset. + */ + func startRecordingVideo() + { + if self.cameraOutputMode != .StillImage { + self.movieOutput?.startRecordingToOutputFileURL(self.tempFilePath, recordingDelegate: self) + } else { + self._show("Capture session output still image", message: "I can only take pictures") + } + } + + /** + Stop recording a video. + */ + func stopRecordingVideo() + { + if let runningMovieOutput = self.movieOutput { + if runningMovieOutput.recording { + runningMovieOutput.stopRecording() + } + } + } + + + // PRAGMA MARK - AVCaptureFileOutputRecordingDelegate + + func captureOutput(captureOutput: AVCaptureFileOutput!, didStartRecordingToOutputFileAtURL fileURL: NSURL!, fromConnections connections: [AnyObject]!) + { + + } + + func captureOutput(captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAtURL outputFileURL: NSURL!, fromConnections connections: [AnyObject]!, error: NSError!) + { + if (error != nil) { + self._show("Unable to save video to the iPhone", message: error.localizedDescription) + } else { + let library = ALAssetsLibrary() + library.writeVideoAtPathToSavedPhotosAlbum(outputFileURL, completionBlock: { (assetURL: NSURL?, error: NSError?) -> Void in + + if (error != nil) { + self._show("Unable to save video to the iPhone.", message: error!.localizedDescription) + } + }) + } + } + + + // PRAGMA MARK - CameraManager() + func orientationChanged() { switch UIDevice.currentDevice().orientation { @@ -224,7 +364,10 @@ class CameraManager: NSObject { if let validCaptureSession = self.captureSession? { validCaptureSession.beginConfiguration() self._addVideoInput() - self._addStillImageOutput() + self._setupStillImageOutput() + if let validStillImageOutput = self.stillImageOutput? { + self.captureSession?.addOutput(self.stillImageOutput) + } self._setupPreviewLayer() validCaptureSession.commitConfiguration() validCaptureSession.startRunning() @@ -291,11 +434,30 @@ class CameraManager: NSObject { } } - private func _addStillImageOutput() + private func _setupMic() { - self.stillImageOutput = AVCaptureStillImageOutput() - if let validStillImageOutput = self.stillImageOutput? { - self.captureSession?.addOutput(self.stillImageOutput) + if (self.mic == nil) { + var error: NSError? + let micDevice:AVCaptureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeAudio); + self.mic = AVCaptureDeviceInput.deviceInputWithDevice(micDevice, error: &error) as? AVCaptureDeviceInput; + if let errorHappened = error { + self.mic = nil + self._show("Mic error", message: errorHappened.description) + } + } + } + + private func _setupStillImageOutput() + { + if (self.stillImageOutput == nil) { + self.stillImageOutput = AVCaptureStillImageOutput() + } + } + + private func _setupMovieOutput() + { + if (self.movieOutput == nil) { + self.movieOutput = AVCaptureMovieFileOutput() } } diff --git a/camera/ViewController.swift b/camera/ViewController.swift index 842dc2f..3a19b0e 100644 --- a/camera/ViewController.swift +++ b/camera/ViewController.swift @@ -18,7 +18,7 @@ class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - self.cameraManager.addPreeviewLayerToView(self.cameraView) + self.cameraManager.addPreeviewLayerToView(self.cameraView, cameraOutputMode: CameraOutputMode.VideoWithMic) self.imageView.hidden = true } @@ -27,19 +27,30 @@ class ViewController: UIViewController { self.cameraManager.flashMode = CameraFlashMode.fromRaw((self.cameraManager.flashMode.toRaw()+1)%3)! } - @IBAction func viewTapped(sender: UITapGestureRecognizer) + @IBAction func recordButtonTapped(sender: UIButton) { - if self.cameraView.hidden == true { - self.cameraView.hidden = false - self.imageView.hidden = true + sender.selected = !sender.selected + sender.backgroundColor = sender.selected ? UIColor.redColor() : UIColor.greenColor() + if sender.selected { + self.cameraManager.startRecordingVideo() } else { - self.cameraManager.capturePictureWithCompletition({ (image: UIImage) -> Void in - self.cameraView.hidden = true - self.imageView.hidden = false - self.imageView.image = image - }) + self.cameraManager.stopRecordingVideo() } } + +// @IBAction func viewTapped(sender: UITapGestureRecognizer) +// { +// if self.cameraView.hidden == true { +// self.cameraView.hidden = false +// self.imageView.hidden = true +// } else { +// self.cameraManager.capturePictureWithCompletition({ (image: UIImage) -> Void in +// self.cameraView.hidden = true +// self.imageView.hidden = false +// self.imageView.image = image +// }) +// } +// } @IBAction func changeCameraDevice(sender: UIButton) { self.cameraManager.cameraDevice = self.cameraManager.cameraDevice == CameraDevice.Front ? CameraDevice.Back : CameraDevice.Front