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 now so the surface can appear even if playback never produces
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
- func activate() {
91
- try? session.setActive(true)
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
- try? session.setActive(false, options: .notifyOthersOnDeactivation)
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: activating while staying paused would hold a
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
- try? session.setActive(true)
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-audio-browser",
3
- "version": "0.2.0-next.30",
3
+ "version": "0.2.0-next.31",
4
4
  "description": "react-native-audio-browser is a react native package built with Nitro",
5
5
  "keywords": [
6
6
  "react-native",