@javascriptcommon/react-native-track-player 4.1.23 → 4.1.25

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.
@@ -41,55 +41,58 @@ open class AudioTap {
41
41
  extension AVPlayerWrapper {
42
42
  internal func attachTap(_ tap: AudioTap?, to item: AVPlayerItem) {
43
43
  guard let tap else { return }
44
- guard let track = item.asset.tracks(withMediaType: .audio).first else {
44
+
45
+ // Try sync first (works for local/progressive files)
46
+ if let track = item.asset.tracks(withMediaType: .audio).first {
47
+ applyTap(tap, to: item, track: track)
45
48
  return
46
49
  }
47
-
50
+
51
+ // Async fallback for HLS streams where tracks aren't immediately available
52
+ Task {
53
+ guard let tracks = try? await item.asset.loadTracks(withMediaType: .audio),
54
+ let track = tracks.first else { return }
55
+ await MainActor.run {
56
+ self.applyTap(tap, to: item, track: track)
57
+ }
58
+ }
59
+ }
60
+
61
+ private func applyTap(_ tap: AudioTap, to item: AVPlayerItem, track: AVAssetTrack) {
48
62
  let audioMix = AVMutableAudioMix()
49
63
  let params = AVMutableAudioMixInputParameters(track: track)
50
-
51
- // we need to retain this pointer so it doesn't disappear out from under us.
52
- // we'll then let it go after we finalize. If the tap changed upstream, we
53
- // aren't going to pick up the new one until after this player item goes away.
64
+
54
65
  let client = UnsafeMutableRawPointer(Unmanaged.passRetained(tap).toOpaque())
55
-
66
+
56
67
  var callbacks = MTAudioProcessingTapCallbacks(version: kMTAudioProcessingTapCallbacksVersion_0, clientInfo: client)
57
68
  { tapRef, clientInfo, tapStorageOut in
58
- // initial tap setup
59
69
  guard let clientInfo else { return }
60
70
  tapStorageOut.pointee = clientInfo
61
71
  let audioTap = Unmanaged<AudioTap>.fromOpaque(clientInfo).takeUnretainedValue()
62
72
  audioTap.initialize()
63
73
  } finalize: { tapRef in
64
- // clean up
65
74
  let audioTap = Unmanaged<AudioTap>.fromOpaque(MTAudioProcessingTapGetStorage(tapRef)).takeUnretainedValue()
66
75
  audioTap.finalize()
67
- // we're done, we can let go of the pointer we retained.
68
76
  Unmanaged.passUnretained(audioTap).release()
69
77
  } prepare: { tapRef, maxFrames, processingFormat in
70
- // allocate memory for sound processing
71
78
  let audioTap = Unmanaged<AudioTap>.fromOpaque(MTAudioProcessingTapGetStorage(tapRef)).takeUnretainedValue()
72
79
  audioTap.prepare(description: processingFormat.pointee)
73
80
  } unprepare: { tapRef in
74
- // deallocate memory for sound processing
75
81
  let audioTap = Unmanaged<AudioTap>.fromOpaque(MTAudioProcessingTapGetStorage(tapRef)).takeUnretainedValue()
76
82
  audioTap.unprepare()
77
83
  } process: { tapRef, numberFrames, flags, bufferListInOut, numberFramesOut, flagsOut in
78
84
  guard noErr == MTAudioProcessingTapGetSourceAudio(tapRef, numberFrames, bufferListInOut, flagsOut, nil, numberFramesOut) else {
79
85
  return
80
86
  }
81
-
82
- // process sound data
83
87
  let audioTap = Unmanaged<AudioTap>.fromOpaque(MTAudioProcessingTapGetStorage(tapRef)).takeUnretainedValue()
84
88
  audioTap.process(numberOfFrames: numberFrames, buffer: UnsafeMutableAudioBufferListPointer(bufferListInOut))
85
89
  }
86
-
90
+
87
91
  var tapRef: MTAudioProcessingTap?
88
92
  let error = MTAudioProcessingTapCreate(kCFAllocatorDefault, &callbacks, kMTAudioProcessingTapCreationFlag_PreEffects, &tapRef)
89
- assert(error == noErr)
90
-
93
+ if error != noErr { return }
94
+
91
95
  params.audioTapProcessor = tapRef
92
-
93
96
  audioMix.inputParameters = [params]
94
97
  item.audioMix = audioMix
95
98
  }