Skip to content

Commit 5689c66

Browse files
committed
Fix infinite loop in media data provider
1 parent 3225ca3 commit 5689c66

File tree

2 files changed

+37
-24
lines changed

2 files changed

+37
-24
lines changed

Sources/AudioBuffersQueue.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ final class AudioBuffersQueue {
77

88
private(set) var duration = CMTime.zero
99

10+
var isEmpty: Bool {
11+
buffers.withLock(\.isEmpty)
12+
}
13+
1014
init(audioDescription: AudioStreamBasicDescription) {
1115
self.audioDescription = audioDescription
1216
self.duration = CMTime(value: 0, timescale: Int32(audioDescription.mSampleRate))
@@ -27,13 +31,24 @@ final class AudioBuffersQueue {
2731
buffers.withLock { $0.append(buffer) }
2832
}
2933

34+
func peek() -> CMSampleBuffer? {
35+
buffers.withLock { $0.first }
36+
}
37+
3038
func dequeue() -> CMSampleBuffer? {
3139
buffers.withLock { buffers in
3240
if buffers.isEmpty { return nil }
3341
return buffers.removeFirst()
3442
}
3543
}
3644

45+
func removeFirst() {
46+
buffers.withLock { buffers in
47+
if buffers.isEmpty { return }
48+
buffers.removeFirst()
49+
}
50+
}
51+
3752
func removeAll() {
3853
buffers.withLock { $0.removeAll() }
3954
duration = .zero

Sources/AudioSynchronizer.swift

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ final class AudioSynchronizer {
1919
private let onPaused: PausedCallback
2020
private let timeUpdateInterval: CMTime
2121

22-
private var receiveComplete = false
22+
private var receiveComplete = false
2323
private var audioBuffersQueue: AudioBuffersQueue?
2424
private var audioFileStream: AudioFileStream?
2525
private var audioRenderer: AVSampleBufferAudioRenderer?
@@ -99,8 +99,10 @@ final class AudioSynchronizer {
9999
closeFileStream()
100100
cancelObservation()
101101
receiveComplete = false
102-
audioSynchronizer = nil
102+
audioRenderer.flatMap { audioSynchronizer?.removeRenderer($0, at: .zero) }
103+
audioRenderer?.stopRequestingMediaData()
103104
audioRenderer = nil
105+
audioSynchronizer = nil
104106
}
105107

106108
// MARK: - Private
@@ -140,28 +142,22 @@ final class AudioSynchronizer {
140142
) {
141143
observeRenderer(audioRenderer, synchronizer: audioSynchronizer)
142144
audioRenderer.requestMediaDataWhenReady(on: queue) { [weak self] in
143-
self?.provideMediaDataIfNeeded(
145+
self?.provideMediaDataIfNeeded()
146+
}
147+
}
148+
149+
private func provideMediaDataIfNeeded() {
150+
guard let audioRenderer, let audioSynchronizer, let audioBuffersQueue else { return }
151+
while let buffer = audioBuffersQueue.peek(), audioRenderer.isReadyForMoreMediaData {
152+
audioRenderer.enqueue(buffer)
153+
audioBuffersQueue.removeFirst()
154+
startPlaybackIfCan(
144155
audioRenderer: audioRenderer,
145156
audioSynchronizer: audioSynchronizer
146157
)
147158
}
148-
}
149-
150-
private func provideMediaDataIfNeeded(
151-
audioRenderer: AVSampleBufferAudioRenderer,
152-
audioSynchronizer: AVSampleBufferRenderSynchronizer
153-
) {
154-
while audioRenderer.isReadyForMoreMediaData {
155-
if let buffer = audioBuffersQueue?.dequeue() {
156-
audioRenderer.enqueue(buffer)
157-
startPlaybackIfCan(
158-
audioRenderer: audioRenderer,
159-
audioSynchronizer: audioSynchronizer
160-
)
161-
} else if receiveComplete {
162-
audioRenderer.stopRequestingMediaData()
163-
break
164-
}
159+
if audioBuffersQueue.isEmpty && receiveComplete {
160+
audioRenderer.stopRequestingMediaData()
165161
}
166162
}
167163

@@ -203,10 +199,11 @@ final class AudioSynchronizer {
203199
private func observeRate(_ audioSynchronizer: AVSampleBufferRenderSynchronizer) {
204200
cancelRateObservation()
205201
let name = AVSampleBufferRenderSynchronizer.rateDidChangeNotification
206-
audioRendererRateCancellable = NotificationCenter.default.publisher(for: name).sink { [weak self] _ in
207-
guard let self else { return }
208-
onRateChanged(audioSynchronizer.rate)
209-
}
202+
audioRendererRateCancellable = NotificationCenter.default
203+
.publisher(for: name).sink { [weak self, weak audioSynchronizer] _ in
204+
guard let self, let audioSynchronizer else { return }
205+
onRateChanged(audioSynchronizer.rate)
206+
}
210207
}
211208

212209
private func cancelRateObservation() {
@@ -226,6 +223,7 @@ final class AudioSynchronizer {
226223
audioSynchronizer.setRate(0.0, time: audioSynchronizer.currentTime())
227224
onRateChanged(0.0)
228225
onComplete()
226+
invalidate()
229227
} else {
230228
onTimeChanged(time)
231229
}

0 commit comments

Comments
 (0)