From c98cf8256deb1e2a5e691b2c5d7aa4b5709f2bdb Mon Sep 17 00:00:00 2001 From: Andrew Walz Date: Wed, 6 Sep 2017 13:43:57 -0600 Subject: [PATCH] Fixed issue with audio going over bluetooth --- .../DemoSwiftyCam.xcodeproj/project.pbxproj | 21 ++- .../xcschemes/SwiftyCam-iOS.xcscheme | 4 +- .../xcschemes/DemoSwiftyCam.xcscheme | 4 +- DemoSwiftyCam/DemoSwiftyCam/Info.plist | 2 + .../DemoSwiftyCam/PhotoViewController.swift | 2 +- .../DemoSwiftyCam/VideoViewController.swift | 16 ++- Source/PreviewView.swift | 6 +- Source/SwiftyCamViewController.swift | 124 +++++++++--------- 8 files changed, 106 insertions(+), 73 deletions(-) diff --git a/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.pbxproj b/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.pbxproj index b8b883f..fed99d5 100644 --- a/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.pbxproj +++ b/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.pbxproj @@ -175,7 +175,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0810; - LastUpgradeCheck = 0820; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = Cappsule; TargetAttributes = { 05D2A9B31E80BE9700B479E9 = { @@ -185,6 +185,7 @@ 1675A9711E00A68300B80903 = { CreatedOnToolsVersion = 8.1; DevelopmentTeam = G8E5P2X66G; + LastSwiftMigration = 0900; ProvisioningStyle = Automatic; }; }; @@ -333,7 +334,9 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -341,7 +344,11 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVES = YES; CLANG_WARN_UNREACHABLE_CODE = YES; @@ -383,7 +390,9 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -391,7 +400,11 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVES = YES; CLANG_WARN_UNREACHABLE_CODE = YES; @@ -427,7 +440,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.Walzy.DemoSwiftyCam1; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -442,7 +456,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.Walzy.DemoSwiftyCam1; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; diff --git a/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/xcshareddata/xcschemes/SwiftyCam-iOS.xcscheme b/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/xcshareddata/xcschemes/SwiftyCam-iOS.xcscheme index 95275f3..b6c9ea0 100644 --- a/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/xcshareddata/xcschemes/SwiftyCam-iOS.xcscheme +++ b/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/xcshareddata/xcschemes/SwiftyCam-iOS.xcscheme @@ -1,6 +1,6 @@ @@ -36,6 +37,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/xcuserdata/DARKPR0.xcuserdatad/xcschemes/DemoSwiftyCam.xcscheme b/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/xcuserdata/DARKPR0.xcuserdatad/xcschemes/DemoSwiftyCam.xcscheme index 9a743aa..0e5254a 100644 --- a/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/xcuserdata/DARKPR0.xcuserdatad/xcschemes/DemoSwiftyCam.xcscheme +++ b/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/xcuserdata/DARKPR0.xcuserdatad/xcschemes/DemoSwiftyCam.xcscheme @@ -1,6 +1,6 @@ @@ -45,6 +46,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/DemoSwiftyCam/DemoSwiftyCam/Info.plist b/DemoSwiftyCam/DemoSwiftyCam/Info.plist index e3b8756..e3fcada 100644 --- a/DemoSwiftyCam/DemoSwiftyCam/Info.plist +++ b/DemoSwiftyCam/DemoSwiftyCam/Info.plist @@ -2,6 +2,8 @@ + NSPhotoLibraryUsageDescription + To save videos CFBundleDevelopmentRegion en CFBundleExecutable diff --git a/DemoSwiftyCam/DemoSwiftyCam/PhotoViewController.swift b/DemoSwiftyCam/DemoSwiftyCam/PhotoViewController.swift index 89dc74a..4333a1f 100644 --- a/DemoSwiftyCam/DemoSwiftyCam/PhotoViewController.swift +++ b/DemoSwiftyCam/DemoSwiftyCam/PhotoViewController.swift @@ -45,7 +45,7 @@ class PhotoViewController: UIViewController { view.addSubview(cancelButton) } - func cancel() { + @objc func cancel() { dismiss(animated: true, completion: nil) } } diff --git a/DemoSwiftyCam/DemoSwiftyCam/VideoViewController.swift b/DemoSwiftyCam/DemoSwiftyCam/VideoViewController.swift index ce55aa9..0423a71 100644 --- a/DemoSwiftyCam/DemoSwiftyCam/VideoViewController.swift +++ b/DemoSwiftyCam/DemoSwiftyCam/VideoViewController.swift @@ -58,6 +58,20 @@ class VideoViewController: UIViewController { cancelButton.setImage(#imageLiteral(resourceName: "cancel"), for: UIControlState()) cancelButton.addTarget(self, action: #selector(cancel), for: .touchUpInside) view.addSubview(cancelButton) + + + // Allow background audio to continue to play + do { + try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient) + } catch let error as NSError { + print(error) + } + + do { + try AVAudioSession.sharedInstance().setActive(true) + } catch let error as NSError { + print(error) + } } override func viewDidAppear(_ animated: Bool) { @@ -65,7 +79,7 @@ class VideoViewController: UIViewController { player?.play() } - func cancel() { + @objc func cancel() { dismiss(animated: true, completion: nil) } diff --git a/Source/PreviewView.swift b/Source/PreviewView.swift index f555d97..2b62c26 100644 --- a/Source/PreviewView.swift +++ b/Source/PreviewView.swift @@ -55,11 +55,11 @@ class PreviewView: UIView { let previewlayer = layer as! AVCaptureVideoPreviewLayer switch gravity { case .resize: - previewlayer.videoGravity = AVLayerVideoGravityResize + previewlayer.videoGravity = AVLayerVideoGravity.resize case .resizeAspect: - previewlayer.videoGravity = AVLayerVideoGravityResizeAspect + previewlayer.videoGravity = AVLayerVideoGravity.resizeAspect case .resizeAspectFill: - previewlayer.videoGravity = AVLayerVideoGravityResizeAspectFill + previewlayer.videoGravity = AVLayerVideoGravity.resizeAspectFill } return previewlayer } diff --git a/Source/SwiftyCamViewController.swift b/Source/SwiftyCamViewController.swift index 7d337de..4a7e743 100644 --- a/Source/SwiftyCamViewController.swift +++ b/Source/SwiftyCamViewController.swift @@ -275,7 +275,7 @@ open class SwiftyCamViewController: UIViewController { // Test authorization status for Camera and Micophone - switch AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo) { + switch AVCaptureDevice.authorizationStatus(for: AVMediaType.video) { case .authorized: // already authorized @@ -284,7 +284,7 @@ open class SwiftyCamViewController: UIViewController { // not yet determined sessionQueue.suspend() - AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { [unowned self] granted in + AVCaptureDevice.requestAccess(for: AVMediaType.video, completionHandler: { [unowned self] granted in if !granted { self.setupResult = .notAuthorized } @@ -511,7 +511,7 @@ open class SwiftyCamViewController: UIViewController { } // Update the orientation on the movie file output video connection before starting recording. - let movieFileOutputConnection = self.movieFileOutput?.connection(withMediaType: AVMediaTypeVideo) + let movieFileOutputConnection = self.movieFileOutput?.connection(with: AVMediaType.video) //flip video output if front facing camera is selected @@ -524,7 +524,7 @@ open class SwiftyCamViewController: UIViewController { // Start recording to a temporary file. let outputFileName = UUID().uuidString let outputFilePath = (NSTemporaryDirectory() as NSString).appendingPathComponent((outputFileName as NSString).appendingPathExtension("mov")!) - movieFileOutput.startRecording(toOutputFileURL: URL(fileURLWithPath: outputFilePath), recordingDelegate: self) + movieFileOutput.startRecording(to: URL(fileURLWithPath: outputFilePath), recordingDelegate: self) self.isVideoRecording = true DispatchQueue.main.async { self.cameraDelegate?.swiftyCam(self, didBeginRecordingVideo: self.currentCamera) @@ -599,7 +599,7 @@ open class SwiftyCamViewController: UIViewController { // remove and re-add inputs and outputs for input in self.session.inputs { - self.session.removeInput(input as! AVCaptureInput) + self.session.removeInput(input ) } self.addInputs() @@ -656,12 +656,12 @@ open class SwiftyCamViewController: UIViewController { fileprivate func configureVideoPreset() { if currentCamera == .front { - session.sessionPreset = videoInputPresetFromVideoQuality(quality: .high) + session.sessionPreset = AVCaptureSession.Preset(rawValue: videoInputPresetFromVideoQuality(quality: .high)) } else { - if session.canSetSessionPreset(videoInputPresetFromVideoQuality(quality: videoQuality)) { - session.sessionPreset = videoInputPresetFromVideoQuality(quality: videoQuality) + if session.canSetSessionPreset(AVCaptureSession.Preset(rawValue: videoInputPresetFromVideoQuality(quality: videoQuality))) { + session.sessionPreset = AVCaptureSession.Preset(rawValue: videoInputPresetFromVideoQuality(quality: videoQuality)) } else { - session.sessionPreset = videoInputPresetFromVideoQuality(quality: .high) + session.sessionPreset = AVCaptureSession.Preset(rawValue: videoInputPresetFromVideoQuality(quality: .high)) } } } @@ -671,9 +671,9 @@ open class SwiftyCamViewController: UIViewController { fileprivate func addVideoInput() { switch currentCamera { case .front: - videoDevice = SwiftyCamViewController.deviceWithMediaType(AVMediaTypeVideo, preferringPosition: .front) + videoDevice = SwiftyCamViewController.deviceWithMediaType(AVMediaType.video.rawValue, preferringPosition: .front) case .rear: - videoDevice = SwiftyCamViewController.deviceWithMediaType(AVMediaTypeVideo, preferringPosition: .back) + videoDevice = SwiftyCamViewController.deviceWithMediaType(AVMediaType.video.rawValue, preferringPosition: .back) } if let device = videoDevice { @@ -705,14 +705,14 @@ open class SwiftyCamViewController: UIViewController { } do { - let videoDeviceInput = try AVCaptureDeviceInput(device: videoDevice) + let videoDeviceInput = try AVCaptureDeviceInput(device: videoDevice!) if session.canAddInput(videoDeviceInput) { session.addInput(videoDeviceInput) self.videoDeviceInput = videoDeviceInput } else { print("[SwiftyCam]: Could not add video device input to the session") - print(session.canSetSessionPreset(videoInputPresetFromVideoQuality(quality: videoQuality))) + print(session.canSetSessionPreset(AVCaptureSession.Preset(rawValue: videoInputPresetFromVideoQuality(quality: videoQuality)))) setupResult = .configurationFailed session.commitConfiguration() return @@ -731,8 +731,8 @@ open class SwiftyCamViewController: UIViewController { return } do { - let audioDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeAudio) - let audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice) + let audioDevice = AVCaptureDevice.default(for: AVMediaType.audio) + let audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice!) if session.canAddInput(audioDeviceInput) { session.addInput(audioDeviceInput) @@ -753,7 +753,7 @@ open class SwiftyCamViewController: UIViewController { if self.session.canAddOutput(movieFileOutput) { self.session.addOutput(movieFileOutput) - if let connection = movieFileOutput.connection(withMediaType: AVMediaTypeVideo) { + if let connection = movieFileOutput.connection(with: AVMediaType.video) { if connection.isVideoStabilizationSupported { connection.preferredVideoStabilizationMode = .auto } @@ -807,7 +807,7 @@ open class SwiftyCamViewController: UIViewController { } fileprivate func getVideoOrientation() -> AVCaptureVideoOrientation { - guard shouldUseDeviceOrientation, let deviceOrientation = self.deviceOrientation else { return previewLayer!.videoPreviewLayer.connection.videoOrientation } + guard shouldUseDeviceOrientation, let deviceOrientation = self.deviceOrientation else { return previewLayer!.videoPreviewLayer.connection!.videoOrientation } switch deviceOrientation { case .landscapeLeft: @@ -863,11 +863,11 @@ open class SwiftyCamViewController: UIViewController { return } - if let videoConnection = photoFileOutput?.connection(withMediaType: AVMediaTypeVideo) { + if let videoConnection = photoFileOutput?.connection(with: AVMediaType.video) { photoFileOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler: {(sampleBuffer, error) in if (sampleBuffer != nil) { - let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer) + let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer!) let image = self.processPhoto(imageData!) // Call delegate and return new image @@ -916,38 +916,35 @@ open class SwiftyCamViewController: UIViewController { fileprivate func videoInputPresetFromVideoQuality(quality: VideoQuality) -> String { switch quality { - case .high: return AVCaptureSessionPresetHigh - case .medium: return AVCaptureSessionPresetMedium - case .low: return AVCaptureSessionPresetLow - case .resolution352x288: return AVCaptureSessionPreset352x288 - case .resolution640x480: return AVCaptureSessionPreset640x480 - case .resolution1280x720: return AVCaptureSessionPreset1280x720 - case .resolution1920x1080: return AVCaptureSessionPreset1920x1080 - case .iframe960x540: return AVCaptureSessionPresetiFrame960x540 - case .iframe1280x720: return AVCaptureSessionPresetiFrame1280x720 + case .high: return AVCaptureSession.Preset.high.rawValue + case .medium: return AVCaptureSession.Preset.medium.rawValue + case .low: return AVCaptureSession.Preset.low.rawValue + case .resolution352x288: return AVCaptureSession.Preset.cif352x288.rawValue + case .resolution640x480: return AVCaptureSession.Preset.vga640x480.rawValue + case .resolution1280x720: return AVCaptureSession.Preset.hd1280x720.rawValue + case .resolution1920x1080: return AVCaptureSession.Preset.hd1920x1080.rawValue + case .iframe960x540: return AVCaptureSession.Preset.iFrame960x540.rawValue + case .iframe1280x720: return AVCaptureSession.Preset.iFrame1280x720.rawValue case .resolution3840x2160: if #available(iOS 9.0, *) { - return AVCaptureSessionPreset3840x2160 + return AVCaptureSession.Preset.hd4K3840x2160.rawValue } else { print("[SwiftyCam]: Resolution 3840x2160 not supported") - return AVCaptureSessionPresetHigh + return AVCaptureSession.Preset.high.rawValue } } } /// Get Devices - fileprivate class func deviceWithMediaType(_ mediaType: String, preferringPosition position: AVCaptureDevicePosition) -> AVCaptureDevice? { - if let devices = AVCaptureDevice.devices(withMediaType: mediaType) as? [AVCaptureDevice] { - return devices.filter({ $0.position == position }).first - } - return nil + fileprivate class func deviceWithMediaType(_ mediaType: String, preferringPosition position: AVCaptureDevice.Position) -> AVCaptureDevice? { + return AVCaptureDevice.devices(for: AVMediaType(rawValue: mediaType)).first } /// Enable or disable flash for photo - fileprivate func changeFlashSettings(device: AVCaptureDevice, mode: AVCaptureFlashMode) { + fileprivate func changeFlashSettings(device: AVCaptureDevice, mode: AVCaptureDevice.FlashMode) { do { try device.lockForConfiguration() device.flashMode = mode @@ -981,17 +978,17 @@ open class SwiftyCamViewController: UIViewController { return } - let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) + let device = AVCaptureDevice.default(for: AVMediaType.video) // Check if device has a flash if (device?.hasTorch)! { do { try device?.lockForConfiguration() - if (device?.torchMode == AVCaptureTorchMode.on) { - device?.torchMode = AVCaptureTorchMode.off + if (device?.torchMode == AVCaptureDevice.TorchMode.on) { + device?.torchMode = AVCaptureDevice.TorchMode.off self.isCameraTorchOn = false } else { do { - try device?.setTorchModeOnWithLevel(1.0) + try device?.setTorchModeOn(level: 1.0) self.isCameraTorchOn = true } catch { print("[SwiftyCam]: \(error)") @@ -1089,27 +1086,28 @@ extension SwiftyCamViewController : SwiftyCamButtonDelegate { extension SwiftyCamViewController : AVCaptureFileOutputRecordingDelegate { /// Process newly captured video and write it to temporary directory - - public func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) { - if let currentBackgroundRecordingID = backgroundRecordingID { - backgroundRecordingID = UIBackgroundTaskInvalid - - if currentBackgroundRecordingID != UIBackgroundTaskInvalid { - UIApplication.shared.endBackgroundTask(currentBackgroundRecordingID) - } - } - if error != nil { - print("[SwiftyCam]: Movie file finishing error: \(error)") - DispatchQueue.main.async { - self.cameraDelegate?.swiftyCam(self, didFailToRecordVideo: error) + + public func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) { + if let currentBackgroundRecordingID = backgroundRecordingID { + backgroundRecordingID = UIBackgroundTaskInvalid + + if currentBackgroundRecordingID != UIBackgroundTaskInvalid { + UIApplication.shared.endBackgroundTask(currentBackgroundRecordingID) } - } else { - //Call delegate function with the URL of the outputfile - DispatchQueue.main.async { - self.cameraDelegate?.swiftyCam(self, didFinishProcessVideoAt: outputFileURL) - } - } - } + } + + if let currentError = error { + print("[SwiftyCam]: Movie file finishing error: \(currentError)") + DispatchQueue.main.async { + self.cameraDelegate?.swiftyCam(self, didFailToRecordVideo: currentError) + } + } else { + //Call delegate function with the URL of the outputfile + DispatchQueue.main.async { + self.cameraDelegate?.swiftyCam(self, didFinishProcessVideoAt: outputFileURL) + } + } + } } // Mark: UIGestureRecognizer Declarations @@ -1124,7 +1122,7 @@ extension SwiftyCamViewController { return } do { - let captureDevice = AVCaptureDevice.devices().first as? AVCaptureDevice + let captureDevice = AVCaptureDevice.devices().first try captureDevice?.lockForConfiguration() zoomScale = min(maxZoomScale, max(1.0, min(beginZoomScale * pinch.scale, captureDevice!.activeFormat.videoMaxZoomFactor))) @@ -1166,7 +1164,7 @@ extension SwiftyCamViewController { device.focusMode = .autoFocus } device.exposurePointOfInterest = focusPoint - device.exposureMode = AVCaptureExposureMode.continuousAutoExposure + device.exposureMode = AVCaptureDevice.ExposureMode.continuousAutoExposure device.unlockForConfiguration() //Call delegate function and pass in the location of the touch @@ -1199,7 +1197,7 @@ extension SwiftyCamViewController { let translationDifference = currentTranslation - previousPanTranslation do { - let captureDevice = AVCaptureDevice.devices().first as? AVCaptureDevice + let captureDevice = AVCaptureDevice.devices().first try captureDevice?.lockForConfiguration() let currentZoom = captureDevice?.videoZoomFactor ?? 0.0