react-native-audio-browser 0.2.0-next.30 → 0.2.0-next.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -52,12 +52,11 @@ extension HybridAudioBrowser: PlayerCallbacks, AVPlayerEngineCallbacks {
|
|
|
52
52
|
// can be set at setup (before any track) while output only begins here. Idempotent:
|
|
53
53
|
// `setActive(true)` is a no-op when the session is already active.
|
|
54
54
|
if state.playing || state.buffering {
|
|
55
|
-
audioSession.activate()
|
|
56
55
|
// The now-playing session's activation request at link time predates this
|
|
57
56
|
// (the audio session was still inactive, and the system may have declined);
|
|
58
|
-
// re-request
|
|
59
|
-
// audio (first-ever load failing on a dead stream).
|
|
60
|
-
engine?.reactivateNowPlayingSession()
|
|
57
|
+
// re-request once the audio session is active, so the surface can appear even if
|
|
58
|
+
// playback never produces audio (first-ever load failing on a dead stream).
|
|
59
|
+
audioSession.activate { [weak self] in self?.engine?.reactivateNowPlayingSession() }
|
|
61
60
|
}
|
|
62
61
|
// A stall starting or recovering may not change the Player state.
|
|
63
62
|
engine?.nowPlayingUpdater.render()
|
|
@@ -35,6 +35,11 @@ final class AudioSessionController {
|
|
|
35
35
|
|
|
36
36
|
private var releaseWork: DispatchWorkItem?
|
|
37
37
|
|
|
38
|
+
/// Where `setActive` runs: it blocks until the system has changed the session, which can take
|
|
39
|
+
/// long enough to stall the main thread. Serial, so a release and a quick re-activation keep
|
|
40
|
+
/// their order.
|
|
41
|
+
private let activationQueue = DispatchQueue(label: "com.audiobrowser.audio-session", qos: .userInitiated)
|
|
42
|
+
|
|
38
43
|
/// Touched only by `init` and `deinit`.
|
|
39
44
|
private nonisolated(unsafe) var observers: [NSObjectProtocol] = []
|
|
40
45
|
private var volumeObservation: NSKeyValueObservation?
|
|
@@ -86,10 +91,14 @@ final class AudioSessionController {
|
|
|
86
91
|
applyCategory()
|
|
87
92
|
}
|
|
88
93
|
|
|
89
|
-
/// Activates the session for output and cancels a pending release.
|
|
90
|
-
|
|
91
|
-
|
|
94
|
+
/// Activates the session for output and cancels a pending release. `then` runs on the main
|
|
95
|
+
/// actor once the activation has run, whether or not it succeeded.
|
|
96
|
+
func activate(then: (@MainActor @Sendable () -> Void)? = nil) {
|
|
92
97
|
cancelRelease()
|
|
98
|
+
activationQueue.async {
|
|
99
|
+
try? AVAudioSession.sharedInstance().setActive(true)
|
|
100
|
+
if let then { Task { @MainActor in then() } }
|
|
101
|
+
}
|
|
93
102
|
}
|
|
94
103
|
|
|
95
104
|
/// Releases the session after 3 s unless playback resumes, so other apps' audio can come back.
|
|
@@ -100,7 +109,9 @@ final class AudioSessionController {
|
|
|
100
109
|
let release = DispatchWorkItem { [weak self] in
|
|
101
110
|
guard let self, player?.playWhenReady == false else { return }
|
|
102
111
|
releaseWork = nil
|
|
103
|
-
|
|
112
|
+
activationQueue.async {
|
|
113
|
+
try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
|
|
114
|
+
}
|
|
104
115
|
}
|
|
105
116
|
releaseWork = release
|
|
106
117
|
DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: release)
|
|
@@ -155,12 +166,13 @@ final class AudioSessionController {
|
|
|
155
166
|
player?.handleInterruptionBegan()
|
|
156
167
|
case .ended:
|
|
157
168
|
let shouldResume = options.map { AVAudioSession.InterruptionOptions(rawValue: $0).contains(.shouldResume) } ?? false
|
|
158
|
-
// Reactivate only when a resume follows
|
|
159
|
-
// non-mixable session with nothing to release it.
|
|
169
|
+
// Reactivate only when a resume follows, and resume once it is active: activating while
|
|
170
|
+
// staying paused would hold a non-mixable session with nothing to release it.
|
|
160
171
|
if shouldResume, player?.willResumeAfterInterruption == true {
|
|
161
|
-
|
|
172
|
+
activate { [weak self] in self?.player?.handleInterruptionEnded(shouldResume: true) }
|
|
173
|
+
} else {
|
|
174
|
+
player?.handleInterruptionEnded(shouldResume: shouldResume)
|
|
162
175
|
}
|
|
163
|
-
player?.handleInterruptionEnded(shouldResume: shouldResume)
|
|
164
176
|
@unknown default:
|
|
165
177
|
break
|
|
166
178
|
}
|