Manually setup AVAudioSession (allow background music playback)
This commit is contained in:
parent
bcdf30fa9d
commit
49e5dd67dc
@ -133,17 +133,25 @@ enum FormatError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SessionError: String {
|
enum SessionError {
|
||||||
case cameraNotReady = "camera-not-ready"
|
case cameraNotReady
|
||||||
|
case audioSessionSetupFailed(reason: String)
|
||||||
|
|
||||||
var code: String {
|
var code: String {
|
||||||
return rawValue
|
switch self {
|
||||||
|
case .cameraNotReady:
|
||||||
|
return "camera-not-ready"
|
||||||
|
case .audioSessionSetupFailed:
|
||||||
|
return "audio-session-setup-failed"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var message: String {
|
var message: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .cameraNotReady:
|
case .cameraNotReady:
|
||||||
return "The Camera is not ready yet! Wait for the onInitialized() callback!"
|
return "The Camera is not ready yet! Wait for the onInitialized() callback!"
|
||||||
|
case let .audioSessionSetupFailed(reason):
|
||||||
|
return "The audio session failed to setup! \(reason)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,12 +111,12 @@ final class CameraView: UIView {
|
|||||||
name: .AVCaptureSessionRuntimeError,
|
name: .AVCaptureSessionRuntimeError,
|
||||||
object: captureSession)
|
object: captureSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func removeFromSuperview() {
|
override func removeFromSuperview() {
|
||||||
captureSession.stopRunning()
|
captureSession.stopRunning()
|
||||||
super.removeFromSuperview()
|
super.removeFromSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
func sessionRuntimeError(notification: Notification) {
|
func sessionRuntimeError(notification: Notification) {
|
||||||
guard let error = notification.userInfo?[AVCaptureSessionErrorKey] as? AVError else {
|
guard let error = notification.userInfo?[AVCaptureSessionErrorKey] as? AVError else {
|
||||||
@ -136,19 +136,19 @@ final class CameraView: UIView {
|
|||||||
required init?(coder _: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) is not implemented.")
|
fatalError("init(coder:) is not implemented.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// pragma MARK: Props updating
|
// pragma MARK: Props updating
|
||||||
override final func didSetProps(_ changedProps: [String]!) {
|
override final func didSetProps(_ changedProps: [String]!) {
|
||||||
let shouldReconfigure = changedProps.contains { propsThatRequireReconfiguration.contains($0) }
|
let shouldReconfigure = changedProps.contains { propsThatRequireReconfiguration.contains($0) }
|
||||||
let shouldReconfigureFormat = shouldReconfigure || changedProps.contains("format")
|
let shouldReconfigureFormat = shouldReconfigure || changedProps.contains("format")
|
||||||
let shouldReconfigureDevice = shouldReconfigureFormat || changedProps.contains { propsThatRequireDeviceReconfiguration.contains($0) }
|
let shouldReconfigureDevice = shouldReconfigureFormat || changedProps.contains { propsThatRequireDeviceReconfiguration.contains($0) }
|
||||||
|
|
||||||
let willReconfigure = shouldReconfigure || shouldReconfigureFormat || shouldReconfigureDevice
|
let willReconfigure = shouldReconfigure || shouldReconfigureFormat || shouldReconfigureDevice
|
||||||
|
|
||||||
let shouldCheckActive = willReconfigure || changedProps.contains("isActive") || captureSession.isRunning != isActive
|
let shouldCheckActive = willReconfigure || changedProps.contains("isActive") || captureSession.isRunning != isActive
|
||||||
let shouldUpdateTorch = willReconfigure || changedProps.contains("torch") || shouldCheckActive
|
let shouldUpdateTorch = willReconfigure || changedProps.contains("torch") || shouldCheckActive
|
||||||
let shouldUpdateZoom = willReconfigure || changedProps.contains("zoom") || shouldCheckActive
|
let shouldUpdateZoom = willReconfigure || changedProps.contains("zoom") || shouldCheckActive
|
||||||
|
|
||||||
if shouldReconfigure || shouldCheckActive || shouldUpdateTorch || shouldUpdateZoom || shouldReconfigureFormat || shouldReconfigureDevice {
|
if shouldReconfigure || shouldCheckActive || shouldUpdateTorch || shouldUpdateZoom || shouldReconfigureFormat || shouldReconfigureDevice {
|
||||||
queue.async {
|
queue.async {
|
||||||
if shouldReconfigure {
|
if shouldReconfigure {
|
||||||
@ -160,14 +160,14 @@ final class CameraView: UIView {
|
|||||||
if shouldReconfigureDevice {
|
if shouldReconfigureDevice {
|
||||||
self.configureDevice()
|
self.configureDevice()
|
||||||
}
|
}
|
||||||
|
|
||||||
if shouldUpdateZoom {
|
if shouldUpdateZoom {
|
||||||
let zoomPercent = CGFloat(max(min(self.zoom.doubleValue, 1.0), 0.0))
|
let zoomPercent = CGFloat(max(min(self.zoom.doubleValue, 1.0), 0.0))
|
||||||
let zoomScaled = (zoomPercent * (self.maxAvailableZoom - self.minAvailableZoom)) + self.minAvailableZoom
|
let zoomScaled = (zoomPercent * (self.maxAvailableZoom - self.minAvailableZoom)) + self.minAvailableZoom
|
||||||
self.zoom(factor: zoomScaled, animated: false)
|
self.zoom(factor: zoomScaled, animated: false)
|
||||||
self.pinchScaleOffset = zoomScaled
|
self.pinchScaleOffset = zoomScaled
|
||||||
}
|
}
|
||||||
|
|
||||||
if shouldCheckActive && self.captureSession.isRunning != self.isActive {
|
if shouldCheckActive && self.captureSession.isRunning != self.isActive {
|
||||||
if self.isActive {
|
if self.isActive {
|
||||||
self.captureSession.startRunning()
|
self.captureSession.startRunning()
|
||||||
@ -175,7 +175,7 @@ final class CameraView: UIView {
|
|||||||
self.captureSession.stopRunning()
|
self.captureSession.stopRunning()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a wack workaround, but if I immediately set torch mode after `startRunning()`, the session isn't quite ready yet and will ignore torch.
|
// This is a wack workaround, but if I immediately set torch mode after `startRunning()`, the session isn't quite ready yet and will ignore torch.
|
||||||
self.queue.asyncAfter(deadline: .now() + 0.1) {
|
self.queue.asyncAfter(deadline: .now() + 0.1) {
|
||||||
if shouldUpdateTorch {
|
if shouldUpdateTorch {
|
||||||
@ -228,6 +228,16 @@ final class CameraView: UIView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// INPUTS
|
// INPUTS
|
||||||
|
// Audio Setup
|
||||||
|
do {
|
||||||
|
captureSession.automaticallyConfiguresApplicationAudioSession = false
|
||||||
|
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord, options: [.mixWithOthers, .allowBluetooth, .defaultToSpeaker])
|
||||||
|
try AVAudioSession.sharedInstance().setActive(true)
|
||||||
|
} catch let error as NSError {
|
||||||
|
// not critical, so don't return
|
||||||
|
invokeOnError(.session(.audioSessionSetupFailed(reason: error.description)))
|
||||||
|
}
|
||||||
|
|
||||||
// Video Input
|
// Video Input
|
||||||
do {
|
do {
|
||||||
if let videoDeviceInput = self.videoDeviceInput {
|
if let videoDeviceInput = self.videoDeviceInput {
|
||||||
|
@ -20,7 +20,7 @@ export type FormatError =
|
|||||||
| 'format/invalid-low-light-boost'
|
| 'format/invalid-low-light-boost'
|
||||||
| 'format/invalid-format'
|
| 'format/invalid-format'
|
||||||
| 'format/invalid-preset';
|
| 'format/invalid-preset';
|
||||||
export type SessionError = 'session/camera-not-ready';
|
export type SessionError = 'session/camera-not-ready' | 'session/audio-session-setup-failed';
|
||||||
export type CaptureError =
|
export type CaptureError =
|
||||||
| 'capture/invalid-photo-format'
|
| 'capture/invalid-photo-format'
|
||||||
| 'capture/encoder-error'
|
| 'capture/encoder-error'
|
||||||
|
Loading…
Reference in New Issue
Block a user