Skip to content

Commit a9a57d4

Browse files
authored
[Fix]improve slow network behaviour (#852)
1 parent d834172 commit a9a57d4

File tree

19 files changed

+143
-234
lines changed

19 files changed

+143
-234
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
44

55
# Upcoming
66

7+
### 🔄 Changed
8+
- Improved behavior in bad-network conditions. [#852](https://github.com/GetStream/stream-video-swift/pull/852)
9+
710
### 🐞 Fixed
811
- CallKit ending 1:1 calls prematurely. [#850](https://github.com/GetStream/stream-video-swift/pull/850)
12+
- Fixed an issue that was causing confusion to the shared AudioSession object when multiple Call instances are in memory. [#852](https://github.com/GetStream/stream-video-swift/pull/852)
913

1014
# [1.25.0](https://github.com/GetStream/stream-video-swift/releases/tag/1.25.0)
1115
_June 16, 2025_

DemoApp/Sources/Views/CallView/DemoCallView.swift

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ struct DemoCallView<ViewFactory: DemoAppViewFactory>: View {
3939
var body: some View {
4040
viewFactory
4141
.makeInnerCallView(viewModel: viewModel)
42-
.onReceive(viewModel.callSettingsPublisher) { _ in
43-
Task { await updateMicrophoneChecker() }
44-
}
4542
.onReceive(microphoneChecker.decibelsPublisher, perform: { values in
4643
guard !viewModel.callSettings.audioOn else { return }
4744
for value in values {
@@ -81,12 +78,4 @@ struct DemoCallView<ViewFactory: DemoAppViewFactory>: View {
8178
.chat(viewModel: viewModel, chatViewModel: chatViewModel)
8279
.toastView(toast: $snapshotViewModel.toast)
8380
}
84-
85-
private func updateMicrophoneChecker() async {
86-
if viewModel.call != nil, viewModel.callSettings.audioOn {
87-
await microphoneChecker.startListening()
88-
} else if viewModel.call != nil, !viewModel.callSettings.audioOn {
89-
await microphoneChecker.stopListening()
90-
}
91-
}
9281
}

DemoApp/Sources/Views/CustomCallView.swift

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ struct CustomCallView<Factory: ViewFactory>: View {
1818

1919
var body: some View {
2020
StreamVideoSwiftUI.CallView(viewFactory: viewFactory, viewModel: viewModel)
21-
.onReceive(viewModel.$callSettings) { _ in
22-
Task { await updateMicrophoneChecker() }
23-
}
2421
.onReceive(microphoneChecker.$audioLevels, perform: { values in
2522
guard !viewModel.callSettings.audioOn else { return }
2623
for value in values {
@@ -47,12 +44,4 @@ struct CustomCallView<Factory: ViewFactory>: View {
4744
: nil
4845
)
4946
}
50-
51-
private func updateMicrophoneChecker() async {
52-
if !viewModel.callSettings.audioOn {
53-
await microphoneChecker.startListening()
54-
} else {
55-
await microphoneChecker.stopListening()
56-
}
57-
}
5847
}

Sources/StreamVideo/CallKit/CallKitService.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,12 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
199199
log.error(
200200
"""
201201
Failed to report incoming call with
202-
callId:\(callId)
203-
callType:\(callType)
202+
cid: \(cid)
203+
localizedCallerName: \(localizedCallerName)
204+
hasVideo: \(hasVideo)
204205
""",
205-
subsystems: .callKit
206+
subsystems: .callKit,
207+
error: error
206208
)
207209
callEnded(cid, ringingTimedOut: false)
208210
}

Sources/StreamVideo/WebRTC/v2/PeerConnection/MediaAdapters/LocalMediaAdapters/LocalAudioMediaAdapter.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ final class LocalAudioMediaAdapter: LocalMediaAdapting, @unchecked Sendable {
180180
transceiverStorage
181181
.forEach { $0.value.track.isEnabled = false }
182182

183+
await audioRecorder.stopRecording()
184+
183185
log.debug(
184186
"""
185187
Local audio tracks are now unpublished:

Sources/StreamVideo/WebRTC/v2/SFU/SFUEventAdapter.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,8 @@ final class SFUEventAdapter: @unchecked Sendable {
427427

428428
return updatedParticipants
429429
}
430+
431+
await stateAdapter.updateCallSettings(from: event)
430432
}
431433

432434
/// Handles a PinsChanged event.

Sources/StreamVideo/WebRTC/v2/WebRTCConfiguration.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ enum WebRTCConfiguration {
1616

1717
/// Timeout for authentication in production environment.
1818
static let production = Timeout(
19-
authenticate: 10,
20-
connect: 10,
21-
join: 10,
19+
authenticate: 30,
20+
connect: 30,
21+
join: 30,
2222
migrationCompletion: 10,
2323
publisherSetUpBeforeNegotiation: 2
2424
)

Sources/StreamVideo/WebRTC/v2/WebRTCStateAdapter.swift

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,6 @@ actor WebRTCStateAdapter: ObservableObject, StreamAudioSessionAdapterDelegate {
119119
self.rtcPeerConnectionCoordinatorFactory = rtcPeerConnectionCoordinatorFactory
120120
self.videoCaptureSessionProvider = videoCaptureSessionProvider
121121
self.screenShareSessionProvider = screenShareSessionProvider
122-
123-
Task {
124-
await configureAudioSession()
125-
}
126122
}
127123

128124
deinit {
@@ -135,7 +131,28 @@ actor WebRTCStateAdapter: ObservableObject, StreamAudioSessionAdapterDelegate {
135131
}
136132

137133
/// Sets the call settings.
138-
func set(callSettings value: CallSettings) { self.callSettings = value }
134+
func set(
135+
callSettings value: CallSettings,
136+
file: StaticString = #file,
137+
function: StaticString = #function,
138+
line: UInt = #line
139+
) {
140+
guard value != callSettings else {
141+
return
142+
}
143+
log.debug(
144+
"""
145+
Updating CallSettings
146+
From: \(callSettings)
147+
To: \(value)
148+
""",
149+
subsystems: .webRTC,
150+
functionName: function,
151+
fileName: file,
152+
lineNumber: line
153+
)
154+
self.callSettings = value
155+
}
139156

140157
/// Sets the initial call settings.
141158
func set(initialCallSettings value: CallSettings?) { self.initialCallSettings = value }
@@ -225,7 +242,6 @@ actor WebRTCStateAdapter: ObservableObject, StreamAudioSessionAdapterDelegate {
225242
subsystems: .webRTC
226243
)
227244

228-
peerConnectionsDisposableBag.removeAll()
229245
let publisher = rtcPeerConnectionCoordinatorFactory.buildCoordinator(
230246
sessionId: sessionID,
231247
peerType: .publisher,
@@ -278,6 +294,8 @@ actor WebRTCStateAdapter: ObservableObject, StreamAudioSessionAdapterDelegate {
278294
}
279295
.store(in: peerConnectionsDisposableBag)
280296

297+
configureAudioSession()
298+
281299
/// We setUp and restoreScreenSharing on the publisher in order to prepare all required tracks
282300
/// for publication. In that way, negotiation will wait until ``completeSetUp`` has been called.
283301
/// Then, with all the tracks prepared, will continue the negotiation flow.
@@ -308,6 +326,7 @@ actor WebRTCStateAdapter: ObservableObject, StreamAudioSessionAdapterDelegate {
308326
screenShareSessionProvider.activeSession = nil
309327
videoCaptureSessionProvider.activeSession = nil
310328
peerConnectionsDisposableBag.removeAll()
329+
disposableBag.removeAll()
311330
await publisher?.close()
312331
await subscriber?.close()
313332
self.publisher = nil
@@ -336,6 +355,7 @@ actor WebRTCStateAdapter: ObservableObject, StreamAudioSessionAdapterDelegate {
336355
)
337356

338357
peerConnectionsDisposableBag.removeAll()
358+
disposableBag.removeAll()
339359
await publisher?.prepareForClosing()
340360
await subscriber?.prepareForClosing()
341361
publisher = nil
@@ -452,8 +472,6 @@ actor WebRTCStateAdapter: ObservableObject, StreamAudioSessionAdapterDelegate {
452472
let updated = assignTracks(on: next)
453473
/// Sends the updated participants to observers while helping publishing streamlined updates.
454474
set(participants: updated)
455-
/// Updates the call settings from the participants update.
456-
updateCallSettingsFromParticipants(Array(updated.values))
457475

458476
/// Logs the completion of the participant operation.
459477
log.debug(
@@ -496,28 +514,37 @@ actor WebRTCStateAdapter: ObservableObject, StreamAudioSessionAdapterDelegate {
496514
}
497515
}
498516

499-
/// Updates the call settings from the participants update.
500-
/// - Parameter participants: The participants to update the call settings from.
501-
/// - Note: This is used when the localParticipant gets muted remotely by someone else.
502-
func updateCallSettingsFromParticipants(_ participants: [CallParticipant]) {
517+
func updateCallSettings(
518+
from event: Stream_Video_Sfu_Event_TrackUnpublished
519+
) {
503520
guard
504-
let localParticipant = participants.first(where: { $0.sessionId == sessionID }),
505-
/// Skip updates for the initial period while the connection is established.
506-
Date().timeIntervalSince(localParticipant.joinedAt) > 5.0
521+
event.participant.sessionID == sessionID,
522+
event.type == .audio || event.type == .video
507523
else {
508524
return
509525
}
510526

527+
let participant = event.participant.toCallParticipant()
528+
511529
let currentCallSettings = self.callSettings
512-
let participantCallSettings = currentCallSettings
513-
.withUpdatedAudioState(localParticipant.hasAudio)
514-
.withUpdatedVideoState(localParticipant.hasVideo)
530+
let possibleNewCallSettings = {
531+
switch event.type {
532+
case .audio:
533+
return currentCallSettings.withUpdatedAudioState(false)
534+
case .video:
535+
return currentCallSettings.withUpdatedVideoState(false)
536+
default:
537+
return currentCallSettings
538+
}
539+
}()
515540

516-
guard participantCallSettings != currentCallSettings else {
541+
guard
542+
currentCallSettings != possibleNewCallSettings
543+
else {
517544
return
518545
}
519546

520-
self.set(callSettings: participantCallSettings)
547+
set(callSettings: possibleNewCallSettings)
521548
}
522549

523550
// MARK: - Private Helpers

Sources/StreamVideoSwiftUI/CallView/LocalParticipantViewModifier.swift

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,6 @@ public struct LocalParticipantViewModifier: ViewModifier {
3030
_callSettings = callSettings
3131
self.showAllInfo = showAllInfo
3232
self.decorations = .init(decorations)
33-
34-
Task {
35-
callSettings.wrappedValue.audioOn
36-
? await microphoneCheckerInstance.startListening()
37-
: await microphoneCheckerInstance.stopListening()
38-
}
3933
}
4034

4135
public func body(content: Content) -> some View {
@@ -58,13 +52,6 @@ public struct LocalParticipantViewModifier: ViewModifier {
5852
}
5953
}
6054
}
61-
.onChange(of: callSettings, perform: { newValue in
62-
Task {
63-
newValue.audioOn
64-
? await microphoneChecker.startListening()
65-
: await microphoneChecker.stopListening()
66-
}
67-
})
6855
)
6956
.applyDecorationModifierIfRequired(
7057
VideoCallParticipantOptionsModifier(participant: localParticipant, call: call),
@@ -133,8 +120,6 @@ public struct LocalParticipantViewModifier_iOS13: ViewModifier {
133120
.padding(.bottom, 2)
134121
}
135122
.padding(.all, showAllInfo ? 16 : 8)
136-
.onAppear { Task { await microphoneChecker.startListening() } }
137-
.onDisappear { Task { await microphoneChecker.stopListening() } }
138123
)
139124
.applyDecorationModifierIfRequired(
140125
VideoCallParticipantOptionsModifier(participant: localParticipant, call: call),

Sources/StreamVideoSwiftUI/CallingViews/LobbyViewModel.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import SwiftUI
99

1010
@MainActor
1111
public class LobbyViewModel: ObservableObject, @unchecked Sendable {
12+
@Injected(\.callAudioRecorder) private var callAudioRecorder
13+
1214
private let camera: Any
1315
private var imagesTask: Task<Void, Never>?
1416
private let disposableBag = DisposableBag()
@@ -71,8 +73,19 @@ public class LobbyViewModel: ObservableObject, @unchecked Sendable {
7173

7274
public func cleanUp() {
7375
disposableBag.removeAll()
76+
Task {
77+
await callAudioRecorder.stopRecording()
78+
}
7479
}
75-
80+
81+
public func didUpdate(callSettings: CallSettings) async {
82+
if callSettings.audioOn {
83+
await callAudioRecorder.startRecording(ignoreActiveCall: true)
84+
} else {
85+
await callAudioRecorder.stopRecording()
86+
}
87+
}
88+
7689
// MARK: - private
7790

7891
private func loadCurrentMembers() {

0 commit comments

Comments
 (0)