Skip to content

Commit c4b2817

Browse files
authored
[Fix]MicrophoneChecker occasional crash (#813)
1 parent b4f800f commit c4b2817

File tree

4 files changed

+16
-9
lines changed

4 files changed

+16
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1010

1111
### 🐞 Fixed
1212
- Fix a retain cycle that was causing StreamVideo to leak in projects using NoiseCancellation. [#814](https://github.com/GetStream/stream-video-swift/pull/814)
13+
- Fix occasional crash caused inside `MicrophoneChecker`. [#813](https://github.com/GetStream/stream-video-swift/pull/813)
1314

1415
# [1.22.2](https://github.com/GetStream/stream-video-swift/releases/tag/1.22.2)
1516
_May 13, 2025_

Sources/StreamVideo/WebRTC/v2/Extensions/Foundation/Publisher+TaskSink.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,13 @@ extension Publisher where Output: Sendable {
8787
// Skip processing if the queue is unavailable.
8888
return
8989
}
90+
let capturedInput = input
9091
// Schedule the task on the provided serial actor queue.
9192
queue.async {
9293
do {
9394
// Check for task cancellation and process the value.
9495
try Task.checkCancellation()
95-
try await receiveValue(input)
96+
try await receiveValue(capturedInput)
9697
} catch let error as Failure {
9798
// Handle specific failure cases.
9899
receiveCompletion(.failure(error))

Sources/StreamVideoSwiftUI/CallingViews/MicrophoneChecker.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ public final class MicrophoneChecker: ObservableObject {
5656
if updateMetersCancellable == nil {
5757
updateMetersCancellable = audioRecorder
5858
.metersPublisher
59-
.sinkTask(queue: serialQueue) { @MainActor [weak self] in
60-
self?.didReceiveUpdatedMeters($0)
61-
}
59+
.compactMap { [weak self] in self?.normaliseAndAppend($0) }
60+
.receive(on: DispatchQueue.main)
61+
.assign(to: \.audioLevels, onWeak: self)
6262
}
6363
}
6464
} catch {
@@ -91,14 +91,14 @@ public final class MicrophoneChecker: ObservableObject {
9191

9292
// MARK: - private
9393

94-
private func didReceiveUpdatedMeters(_ decibel: Float) {
94+
private func normaliseAndAppend(_ decibel: Float) -> [Float] {
9595
let normalisedAudioLevel = audioNormaliser.normalise(decibel)
9696
var temp = audioLevels
9797
temp.append(normalisedAudioLevel)
9898
if temp.count > valueLimit {
9999
temp = Array(temp.dropFirst())
100100
}
101-
audioLevels = temp
101+
return temp
102102
}
103103
}
104104

StreamVideoSwiftUITests/CallingViews/MicrophoneChecker_Tests.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import XCTest
1111

1212
final class MicrophoneChecker_Tests: XCTestCase, @unchecked Sendable {
1313

14+
private nonisolated(unsafe) static var originalCallAudioRecorder: StreamCallAudioRecorder! = StreamCallAudioRecorderKey.currentValue
1415
private lazy var mockStreamVideo: MockStreamVideo! = .init()
1516
private lazy var subject: MicrophoneChecker! = .init(valueLimit: 3)
1617
private lazy var mockAudioRecorder: MockStreamCallAudioRecorder! = MockStreamCallAudioRecorder(filename: "test.wav")
@@ -23,13 +24,18 @@ final class MicrophoneChecker_Tests: XCTestCase, @unchecked Sendable {
2324

2425
override func tearDown() async throws {
2526
await subject.stopListening()
26-
InjectedValues[\.callAudioRecorder] = StreamCallAudioRecorderKey.currentValue
27+
InjectedValues[\.callAudioRecorder] = Self.originalCallAudioRecorder
2728
mockAudioRecorder = nil
2829
mockStreamVideo = nil
2930
subject = nil
3031
try await super.tearDown()
3132
}
3233

34+
override class func tearDown() {
35+
Self.originalCallAudioRecorder = nil
36+
super.tearDown()
37+
}
38+
3339
// MARK: - init
3440

3541
func test_startListening_startListeningWasCalledOnAudioRecorder() async {
@@ -49,7 +55,6 @@ final class MicrophoneChecker_Tests: XCTestCase, @unchecked Sendable {
4955
// MARK: - audioLevels
5056

5157
func test_startListeningAndPostAudioLevels_microphoneCheckerHasExpectedValues() async throws {
52-
LogConfig.level = .debug
5358
await subject.startListening()
5459

5560
let inputs = [
@@ -61,7 +66,7 @@ final class MicrophoneChecker_Tests: XCTestCase, @unchecked Sendable {
6166

6267
for value in inputs {
6368
mockAudioRecorder.mockMetersPublisher.send(Float(value))
64-
try? await Task.sleep(nanoseconds: 100_000)
69+
await wait(for: 0.1)
6570
}
6671

6772
let values = try await subject

0 commit comments

Comments
 (0)