Skip to content

Commit

Permalink
Improve statistics collection (#281)
Browse files Browse the repository at this point in the history
  • Loading branch information
ipavlidakis authored Feb 2, 2024
1 parent 4cd59c8 commit de11e14
Show file tree
Hide file tree
Showing 11 changed files with 1,042 additions and 333 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,16 @@ final class ParticipantStatsViewModel: ObservableObject {
private func handleStatsReport(_ report: CallStatsReport?) {
guard let report,
let trackId = participant.track?.trackId,
let participantStats = report.participantsStats.report[trackId] else {
let participantsStatsArray = report.participantsStats.report[trackId],
!participantsStatsArray.isEmpty
else {
return
}

let participantStats = participantsStatsArray.sorted { lhs, rhs in
lhs.frameWidth >= rhs.frameWidth && lhs.frameHeight >= rhs.frameHeight
}[0]

let isLocalUser = participant.sessionId == call.state.sessionId
let datacenter = StatsEntry(
title: "Region",
Expand Down
33 changes: 20 additions & 13 deletions Sources/StreamVideo/Models/CallStatsReport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,20 @@ public struct CallStatsReport: Sendable {
}

/// A struct representing statistics for participants in the call.
public struct ParticipantsStats: Sendable {
public struct ParticipantsStats: Sendable, Equatable {
/// The report containing statistics for individual participants.
public let report: [String: BaseStats]
public let report: [String: [BaseStats]]

public static func + (
lhs: ParticipantsStats,
rhs: ParticipantsStats
) -> ParticipantsStats {
ParticipantsStats(report: lhs.report.merging(rhs.report) { $1 })
}
}

/// A struct representing basic statistics for a participant in the call.
public struct BaseStats: Sendable {
public struct BaseStats: Sendable, Equatable {
/// The total bytes sent by the participant.
public let bytesSent: Int
/// The total bytes received by the participant.
Expand Down Expand Up @@ -60,23 +67,23 @@ public struct BaseStats: Sendable {
}

/// A struct representing an aggregated stats report for the call.
public struct AggregatedStatsReport: Sendable {
public struct AggregatedStatsReport: Sendable, Equatable {
/// The total bytes sent by all participants.
public let totalBytesSent: Int
public internal(set) var totalBytesSent: Int
/// The total bytes received by all participants.
public let totalBytesReceived: Int
public internal(set) var totalBytesReceived: Int
/// The average jitter across all participants in milliseconds.
public let averageJitterInMs: Double
public internal(set) var averageJitterInMs: Double
/// The average round-trip time across all participants in milliseconds.
public let averageRoundTripTimeInMs: Double
public internal(set) var averageRoundTripTimeInMs: Double
/// The reasons for quality limitations in the call.
public let qualityLimitationReasons: String
public internal(set) var qualityLimitationReasons: String
/// The highest frame width among all video streams.
public let highestFrameWidth: Int
public internal(set) var highestFrameWidth: Int
/// The highest frame height among all video streams.
public let highestFrameHeight: Int
public internal(set) var highestFrameHeight: Int
/// The highest frames per second among all video streams.
public let highestFramesPerSecond: Int
public internal(set) var highestFramesPerSecond: Int
/// The timestamp when the aggregated stats report was generated.
public let timestamp: Double
public internal(set) var timestamp: Double
}
88 changes: 88 additions & 0 deletions Sources/StreamVideo/WebRTC/Statistics/Statistics+Convenience.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//
// Copyright © 2024 Stream.io Inc. All rights reserved.
//

import Foundation
import StreamWebRTC

/// A wrapper around RTCStatistics that can be used to easily access its properties.
@dynamicMemberLookup
struct StreamRTCStatistics {
/// A structure providing a type-safe way to access values from RTCStatistics' values dictionary.
@objcMembers final class CodingKeys: NSObject {
var trackIdentifier: String = ""
var codecId: String = ""
var mimeType: String = ""
var bytesSent: Int = 0
var bytesReceived: Int = 0
var jitter: Double = 0
var currentRoundTripTime: Double = 0
var qualityLimitationReason: String = ""
var frameWidth: Int = 0
var frameHeight: Int = 0
var framesPerSecond: Int = 0
var transportId: String = ""
var dtlsState: String = ""
var selectedCandidatePairId: String = ""
var kind: String = ""
var rid: String = ""
var ssrc: Int = 0
}

private let source: StreamStatisticsProtocol?

/// The of the `source` or empty.
var type: String { source?.type ?? "" }

/// The id of the `source` or empty.
var id: String { source?.id ?? "" }

init?(_ source: StreamStatisticsProtocol?) {
guard let source else { return nil }
self.source = source
}

/// A handy way to access values from the source's `values` storage.
subscript<T>(dynamicMember keyPath: KeyPath<CodingKeys, T>) -> T? {
/// Implement logic to dynamically access the members of RTCStatisticsReport
let value = NSExpression(forKeyPath: keyPath).keyPath
return source?.values[value] as? T
}
}

/// A wrapper around RTCStatisticsReport that can be used to easily access its properties.
struct StreamRTCStatisticsReport {
var statistics: [StreamRTCStatistics]
var timestamp: TimeInterval
var source: RTCStatisticsReport?

init(_ source: RTCStatisticsReport?) {
self.init(
statistics: source?.statistics.compactMap { StreamRTCStatistics($0.value) } ?? [],
timestamp: source?.timestamp_us ?? Date().timeIntervalSince1970,
source: source
)
}

init(
statistics: [StreamRTCStatistics],
timestamp: TimeInterval,
source: RTCStatisticsReport?
) {
self.statistics = statistics
self.timestamp = timestamp
self.source = source
}
}

/// Describes and object that can be queried to get information about a RTCStatistics source.
protocol StreamStatisticsProtocol {

var type: String { get }

var id: String { get }

var values: [String: NSObject] { get }
}

extension RTCStatistics: StreamStatisticsProtocol {}
Loading

0 comments on commit de11e14

Please sign in to comment.