Merge pull request #86 from RyanRory/microphone-flash-fix

Fix Microphone Bug
pull/89/head
gmbnt 6 years ago committed by GitHub
commit e662a76b6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -34,11 +34,57 @@ class PhotoCapture: NSObject {
} }
private(set) var desiredPosition: AVCaptureDevice.Position = .back private(set) var desiredPosition: AVCaptureDevice.Position = .back
let recordingAudioActivity = AudioActivity(audioDescription: "PhotoCapture", behavior: .playAndRecord)
override init() { override init() {
self.session = AVCaptureSession() self.session = AVCaptureSession()
self.captureOutput = CaptureOutput() self.captureOutput = CaptureOutput()
} }
// MARK: - Dependencies
var audioSession: OWSAudioSession {
return Environment.shared.audioSession
}
// MARK: -
var audioDeviceInput: AVCaptureDeviceInput?
func startAudioCapture() throws {
assertIsOnSessionQueue()
guard audioSession.startAudioActivity(recordingAudioActivity) else {
throw PhotoCaptureError.assertionError(description: "unable to capture audio activity")
}
self.session.beginConfiguration()
defer { self.session.commitConfiguration() }
let audioDevice = AVCaptureDevice.default(for: .audio)
// verify works without audio permissions
let audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice!)
if session.canAddInput(audioDeviceInput) {
// self.session.addInputWithNoConnections(audioDeviceInput)
session.addInput(audioDeviceInput)
self.audioDeviceInput = audioDeviceInput
} else {
owsFailDebug("Could not add audio device input to the session")
}
}
func stopAudioCapture() {
assertIsOnSessionQueue()
self.session.beginConfiguration()
defer { self.session.commitConfiguration() }
guard let audioDeviceInput = self.audioDeviceInput else {
owsFailDebug("audioDevice was unexpectedly nil")
return
}
session.removeInput(audioDeviceInput)
self.audioDeviceInput = nil
audioSession.endAudioActivity(recordingAudioActivity)
}
func startCapture() -> Promise<Void> { func startCapture() -> Promise<Void> {
return sessionQueue.async(.promise) { [weak self] in return sessionQueue.async(.promise) { [weak self] in
guard let self = self else { return } guard let self = self else { return }
@ -48,15 +94,6 @@ class PhotoCapture: NSObject {
try self.updateCurrentInput(position: .back) try self.updateCurrentInput(position: .back)
let audioDevice = AVCaptureDevice.default(for: .audio)
// verify works without audio permissions
let audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice!)
if self.session.canAddInput(audioDeviceInput) {
self.session.addInput(audioDeviceInput)
} else {
owsFailDebug("Could not add audio device input to the session")
}
guard let photoOutput = self.captureOutput.photoOutput else { guard let photoOutput = self.captureOutput.photoOutput else {
throw PhotoCaptureError.initializationFailed throw PhotoCaptureError.initializationFailed
} }
@ -290,19 +327,21 @@ extension PhotoCapture: CaptureButtonDelegate {
AssertIsOnMainThread() AssertIsOnMainThread()
Logger.verbose("") Logger.verbose("")
sessionQueue.async { sessionQueue.async(.promise) {
try self.startAudioCapture()
self.captureOutput.beginVideo(delegate: self) self.captureOutput.beginVideo(delegate: self)
}.done {
DispatchQueue.main.async { self.delegate?.photoCaptureDidBeginVideo(self)
self.delegate?.photoCaptureDidBeginVideo(self) }.catch { error in
} self.delegate?.photoCapture(self, processingDidError: error)
} }.retainUntilComplete()
} }
func didCompleteLongPressCaptureButton(_ captureButton: CaptureButton) { func didCompleteLongPressCaptureButton(_ captureButton: CaptureButton) {
Logger.verbose("") Logger.verbose("")
sessionQueue.async { sessionQueue.async {
self.captureOutput.completeVideo(delegate: self) self.captureOutput.completeVideo(delegate: self)
self.stopAudioCapture()
} }
AssertIsOnMainThread() AssertIsOnMainThread()
// immediately inform UI that capture is stopping // immediately inform UI that capture is stopping
@ -312,6 +351,9 @@ extension PhotoCapture: CaptureButtonDelegate {
func didCancelLongPressCaptureButton(_ captureButton: CaptureButton) { func didCancelLongPressCaptureButton(_ captureButton: CaptureButton) {
Logger.verbose("") Logger.verbose("")
AssertIsOnMainThread() AssertIsOnMainThread()
sessionQueue.async {
self.stopAudioCapture()
}
delegate?.photoCaptureDidCancelVideo(self) delegate?.photoCaptureDidCancelVideo(self)
} }
@ -369,8 +411,11 @@ extension PhotoCapture: CaptureOutputDelegate {
AssertIsOnMainThread() AssertIsOnMainThread()
if let error = error { if let error = error {
delegate?.photoCapture(self, processingDidError: error) guard didSucceedDespiteError(error) else {
return delegate?.photoCapture(self, processingDidError: error)
return
}
Logger.info("Ignoring error, since capture succeeded.")
} }
let dataSource = DataSourcePath.dataSource(with: outputFileURL, shouldDeleteOnDeallocation: true) let dataSource = DataSourcePath.dataSource(with: outputFileURL, shouldDeleteOnDeallocation: true)
@ -378,6 +423,19 @@ extension PhotoCapture: CaptureOutputDelegate {
let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: kUTTypeMPEG4 as String) let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: kUTTypeMPEG4 as String)
delegate?.photoCapture(self, didFinishProcessingAttachment: attachment) delegate?.photoCapture(self, didFinishProcessingAttachment: attachment)
} }
/// The AVCaptureFileOutput can return an error even though recording succeeds.
/// I can't find useful documentation on this, but Apple's example AVCam app silently
/// discards these errors, so we do the same.
/// These spurious errors can be reproduced 1/3 of the time when making a series of short videos.
private func didSucceedDespiteError(_ error: Error) -> Bool {
let nsError = error as NSError
guard let successfullyFinished = nsError.userInfo[AVErrorRecordingSuccessfullyFinishedKey] as? Bool else {
return false
}
return successfullyFinished
}
} }
// MARK: - Capture Adapter // MARK: - Capture Adapter
@ -410,6 +468,10 @@ class CaptureOutput {
} }
movieOutput = AVCaptureMovieFileOutput() movieOutput = AVCaptureMovieFileOutput()
// disable movie fragment writing since it's not supported on mp4
// leaving it enabled causes all audio to be lost on videos longer
// than the default length (10s).
movieOutput.movieFragmentInterval = CMTime.invalid
} }
var photoOutput: AVCaptureOutput? { var photoOutput: AVCaptureOutput? {

@ -45,24 +45,11 @@ class PhotoCaptureViewController: OWSViewController {
} }
} }
// MARK: - Dependencies
var audioActivity: AudioActivity?
var audioSession: OWSAudioSession {
return Environment.shared.audioSession
}
// MARK: - Overrides // MARK: - Overrides
override func loadView() { override func loadView() {
self.view = UIView() self.view = UIView()
self.view.backgroundColor = Colors.navigationBarBackground self.view.backgroundColor = Colors.navigationBarBackground
let audioActivity = AudioActivity(audioDescription: "PhotoCaptureViewController", behavior: .playAndRecord)
self.audioActivity = audioActivity
if !self.audioSession.startAudioActivity(audioActivity) {
owsFailDebug("unexpectedly unable to start audio activity")
}
} }
override func viewDidLoad() { override func viewDidLoad() {

Loading…
Cancel
Save