Skip to content

Commit e71a36f

Browse files
authored
[Enhancement]Improve tracing (#891)
1 parent f60e3bd commit e71a36f

29 files changed

+322
-63
lines changed

Sources/StreamVideo/Call.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,6 +1473,8 @@ public class Call: @unchecked Sendable, WSEventsSubscriber {
14731473
)
14741474
}
14751475

1476+
// MARK: - CallKit
1477+
14761478
/// Notifies the `Call` instance that CallKit has activated the system audio
14771479
/// session.
14781480
///
@@ -1487,6 +1489,12 @@ public class Call: @unchecked Sendable, WSEventsSubscriber {
14871489
try callController.callKitActivated(audioSession)
14881490
}
14891491

1492+
internal func didPerform(_ action: WebRTCTrace.CallKitAction) {
1493+
Task(disposableBag: disposableBag) { [weak callController] in
1494+
await callController?.didPerform(action)
1495+
}
1496+
}
1497+
14901498
// MARK: - private
14911499

14921500
private func updatePermissions(

Sources/StreamVideo/CallKit/CallKitService.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import AVFoundation
66
import CallKit
77
import Combine
88
import Foundation
9+
import StreamWebRTC
910

1011
/// `CallKitService` manages interactions with the CallKit framework,
1112
/// facilitating VoIP calls in an application.
@@ -368,6 +369,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
368369
log.debug("CXProvider didReset.", subsystems: .callKit)
369370
storageAccessQueue.sync {
370371
for (_, entry) in _storage {
372+
entry.call.didPerform(.didReset)
371373
entry.call.leave()
372374
}
373375
}
@@ -393,6 +395,8 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
393395
if
394396
let active,
395397
let call = callEntry(for: active)?.call {
398+
call.didPerform(.didActivateAudioSession)
399+
396400
do {
397401
try call.callKitActivated(audioSession)
398402
} catch {
@@ -417,6 +421,11 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
417421
""",
418422
subsystems: .callKit
419423
)
424+
if
425+
let active,
426+
let call = callEntry(for: active)?.call {
427+
call.didPerform(.didDeactivateAudioSession)
428+
}
420429
}
421430

422431
open func provider(
@@ -433,6 +442,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
433442
ringingTimerCancellable?.cancel()
434443
ringingTimerCancellable = nil
435444
active = action.callUUID
445+
callToJoinEntry.call.didPerform(.performAnswerCall)
436446

437447
Task(disposableBag: disposableBag) { @MainActor [weak self] in
438448
guard let self else {
@@ -510,6 +520,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
510520
subsystems: .callKit
511521
)
512522
if currentCallWasEnded {
523+
stackEntry.call.didPerform(.performEndCall)
513524
stackEntry.call.leave()
514525
} else {
515526
do {
@@ -527,6 +538,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
527538
""",
528539
subsystems: .callKit
529540
)
541+
stackEntry.call.didPerform(.performRejectCall)
530542
try await stackEntry.call.reject(reason: rejectionReason)
531543
} catch {
532544
log.error(error, subsystems: .callKit)
@@ -546,6 +558,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
546558
action.fail()
547559
return
548560
}
561+
stackEntry.call.didPerform(.performSetMutedCall)
549562
Task(disposableBag: disposableBag) {
550563
do {
551564
if action.isMuted {

Sources/StreamVideo/Controllers/CallController.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,12 @@ class CallController: @unchecked Sendable {
515515
await webRTCCoordinator.disableClientCapabilities(capabilities)
516516
}
517517

518+
// MARK: - CallKit tracing
519+
520+
func didPerform(_ action: WebRTCTrace.CallKitAction) async {
521+
await webRTCCoordinator.didPerform(action)
522+
}
523+
518524
// MARK: - private
519525

520526
private func handleParticipantsUpdated() {

Sources/StreamVideo/Utils/AudioSession/StreamAudioSession.swift

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ final class StreamAudioSession: @unchecked Sendable, ObservableObject {
5555
/// Retrieves the current audio route description.
5656
var currentRoute: AVAudioSessionRouteDescription { audioSession.currentRoute }
5757

58+
private let audioDeviceModule: RTCAudioDeviceModule
59+
5860
/// Initializes a new `StreamAudioSessionAdapter` instance, configuring
5961
/// the session with default settings and enabling manual audio control
6062
/// for WebRTC.
@@ -69,13 +71,15 @@ final class StreamAudioSession: @unchecked Sendable, ObservableObject {
6971
callSettings: CallSettings = .init(),
7072
ownCapabilities: Set<OwnCapability> = [],
7173
policy: AudioSessionPolicy = DefaultAudioSessionPolicy(),
72-
audioSession: AudioSessionProtocol = StreamRTCAudioSession()
74+
audioSession: AudioSessionProtocol = StreamRTCAudioSession(),
75+
audioDeviceModule: RTCAudioDeviceModule
7376
) {
7477
activeCallSettings = callSettings
7578
self.ownCapabilities = ownCapabilities
7679
self.policy = policy
7780
self.audioSession = audioSession
7881
category = audioSession.category
82+
self.audioDeviceModule = audioDeviceModule
7983

8084
/// Update the active call's `audioSession` to make available to
8185
/// other components.
@@ -413,6 +417,16 @@ extension StreamAudioSession: Encodable {
413417
// MARK: - Codable
414418

415419
enum CodingKeys: String, CodingKey {
420+
case isActive
421+
case isRecording
422+
case isAudioModuleRecording
423+
case isAudioModulePlaying
424+
case category
425+
case mode
426+
case overrideOutputPort
427+
case useManualAudio
428+
case isAudioEnabled
429+
case hasRecordPermission
416430
case speakerOn
417431
case device
418432
case deviceIsExternal = "device.isExternal"
@@ -422,11 +436,23 @@ extension StreamAudioSession: Encodable {
422436

423437
func encode(to encoder: Encoder) throws {
424438
var container = encoder.container(keyedBy: CodingKeys.self)
425-
try container.encode(activeCallSettings.speakerOn, forKey: .speakerOn)
439+
try container.encode(audioSession.isActive, forKey: .isActive)
440+
try container.encode(isRecording, forKey: .isRecording)
441+
try container.encode(audioDeviceModule.recording, forKey: .isAudioModuleRecording)
442+
try container.encode(audioDeviceModule.playing, forKey: .isAudioModulePlaying)
443+
try container.encode(audioSession.category.rawValue, forKey: .category)
444+
try container.encode(audioSession.mode.rawValue, forKey: .mode)
445+
try container.encode(audioSession.overrideOutputPort.stringValue, forKey: .overrideOutputPort)
446+
try container.encode(audioSession.hasRecordPermission, forKey: .hasRecordPermission)
426447
try container.encode("\(audioSession.currentRoute)", forKey: .device)
427448
try container.encode(audioSession.currentRoute.isExternal, forKey: .deviceIsExternal)
428449
try container.encode(audioSession.currentRoute.isSpeaker, forKey: .deviceIsSpeaker)
429450
try container.encode(audioSession.currentRoute.isReceiver, forKey: .deviceIsReceiver)
451+
452+
if let rtcAudioSession = audioSession as? StreamRTCAudioSession {
453+
try container.encode(rtcAudioSession.useManualAudio, forKey: .useManualAudio)
454+
try container.encode(rtcAudioSession.isAudioEnabled, forKey: .isAudioEnabled)
455+
}
430456
}
431457
}
432458

Sources/StreamVideo/Utils/AudioSession/StreamRTCAudioSession.swift

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,18 @@ protocol AudioSessionProtocol {
2222

2323
var category: AVAudioSession.Category { get }
2424

25+
var mode: AVAudioSession.Mode { get }
26+
27+
var overrideOutputPort: AVAudioSession.PortOverride { get }
28+
2529
/// A Boolean value indicating whether manual audio routing is used.
2630
var useManualAudio: Bool { get set }
2731

2832
/// A Boolean value indicating whether audio is enabled.
2933
var isAudioEnabled: Bool { get set }
3034

35+
var hasRecordPermission: Bool { get }
36+
3137
/// Configures the audio session category and options.
3238
/// - Parameters:
3339
/// - category: The audio category (e.g., `.playAndRecord`).
@@ -89,6 +95,10 @@ final class StreamRTCAudioSession: AudioSessionProtocol, @unchecked Sendable, Re
8995

9096
var category: AVAudioSession.Category { state.category }
9197

98+
var mode: AVAudioSession.Mode { state.mode }
99+
100+
var overrideOutputPort: AVAudioSession.PortOverride { state.overrideOutputPort }
101+
92102
/// A Boolean value indicating whether the audio session uses manual
93103
/// audio routing.
94104
var useManualAudio: Bool {
@@ -102,6 +112,8 @@ final class StreamRTCAudioSession: AudioSessionProtocol, @unchecked Sendable, Re
102112
get { source.isAudioEnabled }
103113
}
104114

115+
var hasRecordPermission: Bool { source.session.recordPermission == .granted }
116+
105117
// MARK: - Lifecycle
106118

107119
init() {
@@ -175,7 +187,7 @@ final class StreamRTCAudioSession: AudioSessionProtocol, @unchecked Sendable, Re
175187
_ isActive: Bool
176188
) async throws {
177189
try await performOperation { [weak self] in
178-
guard let self else {
190+
guard let self, source.isActive != isActive else {
179191
return
180192
}
181193

@@ -245,3 +257,16 @@ final class StreamRTCAudioSession: AudioSessionProtocol, @unchecked Sendable, Re
245257
RTCAudioSessionConfiguration.setWebRTC(webRTCConfiguration)
246258
}
247259
}
260+
261+
extension AVAudioSession.PortOverride {
262+
var stringValue: String {
263+
switch self {
264+
case .none:
265+
return "none"
266+
case .speaker:
267+
return "speaker"
268+
@unknown default:
269+
return "unknown"
270+
}
271+
}
272+
}

Sources/StreamVideo/Utils/StreamAppStateAdapter/StreamAppStateAdapter.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public protocol AppStateProviding: Sendable {
1515
}
1616

1717
/// Represents the app's state: foreground or background.
18-
public enum ApplicationState: Sendable, Equatable { case foreground, background }
18+
public enum ApplicationState: String, Sendable, Equatable { case foreground, background }
1919

2020
/// An adapter that observes the app's state and publishes changes.
2121
final class StreamAppStateAdapter: AppStateProviding, ObservableObject, @unchecked Sendable {

Sources/StreamVideo/WebRTC/PeerConnectionFactory.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ final class PeerConnectionFactory: @unchecked Sendable {
4242
var supportedVideoCodecDecoding: [RTCVideoCodecInfo] {
4343
defaultDecoder.supportedCodecs()
4444
}
45-
45+
46+
var audioDeviceModule: RTCAudioDeviceModule { factory.audioDeviceModule }
47+
4648
/// Creates or retrieves a PeerConnectionFactory instance for a given
4749
/// audio processing module.
4850
/// - Parameter audioProcessingModule: The RTCAudioProcessingModule to use.

Sources/StreamVideo/WebRTC/v2/StateMachine/Stages/WebRTCCoordinator+Joined.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,7 @@ extension WebRTCCoordinator.StateMachine.Stage {
490490
statsReporter.publisher = await stateAdapter.publisher
491491
statsReporter.subscriber = await stateAdapter.subscriber
492492
statsReporter.sfuAdapter = await stateAdapter.sfuAdapter
493+
statsReporter.reconnectAttempts = context.reconnectAttempts
493494

494495
/// Update the state adapter with the new stats reporter.
495496
await stateAdapter.set(statsAdapter: statsReporter)
@@ -500,6 +501,7 @@ extension WebRTCCoordinator.StateMachine.Stage {
500501
statsReporter?.publisher = await stateAdapter.publisher
501502
statsReporter?.subscriber = await stateAdapter.subscriber
502503
statsReporter?.sfuAdapter = await stateAdapter.sfuAdapter
504+
statsReporter?.reconnectAttempts = context.reconnectAttempts
503505
}
504506
}
505507

Sources/StreamVideo/WebRTC/v2/Stats/Models/WebRTCTrace.swift

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ extension WebRTCTrace {
103103
event: SFUAdapterEvent
104104
) {
105105
self.init(
106-
id: nil,
106+
id: "sfu",
107107
tag: event.traceTag,
108108
data: event.traceData
109109
)
@@ -119,7 +119,7 @@ extension WebRTCTrace {
119119
event: SelectiveEncodable
120120
) {
121121
self.init(
122-
id: nil,
122+
id: "sfu",
123123
tag: tag,
124124
data: .init(event)
125125
)
@@ -134,7 +134,6 @@ extension WebRTCTrace {
134134
/// - callSettings: The active call settings.
135135
/// - audioSession: The audio session state.
136136
init(
137-
callSettings: CallSettings,
138137
audioSession: StreamAudioSession
139138
) {
140139
self.init(
@@ -155,18 +154,66 @@ extension WebRTCTrace {
155154
init(
156155
status: InternetConnectionStatus
157156
) {
158-
let data = {
157+
let tag = {
159158
switch status {
160159
case .available:
161-
return "online"
160+
return "network.state.online"
162161
case .unavailable, .unknown:
163-
return "offline"
162+
return "network.state.offline"
164163
}
165164
}()
166165
self.init(
167166
id: nil,
168-
tag: "network.changed",
169-
data: .init(data)
167+
tag: tag,
168+
data: nil
169+
)
170+
}
171+
}
172+
173+
extension WebRTCTrace {
174+
enum CallKitAction: String {
175+
case didReset
176+
case didActivateAudioSession
177+
case didDeactivateAudioSession
178+
case performAnswerCall
179+
case performEndCall
180+
case performRejectCall
181+
case performSetMutedCall
182+
}
183+
184+
init(
185+
_ action: CallKitAction
186+
) {
187+
self.init(
188+
id: nil,
189+
tag: "callKit.\(action.rawValue)",
190+
data: nil
191+
)
192+
}
193+
}
194+
195+
extension WebRTCTrace {
196+
197+
init(
198+
applicationState: ApplicationState
199+
) {
200+
self.init(
201+
id: nil,
202+
tag: "application.state.\(applicationState.rawValue)",
203+
data: nil
204+
)
205+
}
206+
}
207+
208+
extension WebRTCTrace {
209+
210+
init(
211+
thermalState: ProcessInfo.ThermalState
212+
) {
213+
self.init(
214+
id: nil,
215+
tag: "device.thermal.state.\(thermalState)",
216+
data: nil
170217
)
171218
}
172219
}

0 commit comments

Comments
 (0)