diff --git a/MembraneRTC/Sources/MembraneRTC/MembraneRTC.swift b/MembraneRTC/Sources/MembraneRTC/MembraneRTC.swift index f8ae3112..d815a128 100644 --- a/MembraneRTC/Sources/MembraneRTC/MembraneRTC.swift +++ b/MembraneRTC/Sources/MembraneRTC/MembraneRTC.swift @@ -433,6 +433,15 @@ public class MembraneRTC: MulticastDelegate, ObservableObje peerConnectionManager.setTrackEncoding(trackId: trackId, encoding: encoding, enabled: enabled) } + /** + Returns current connection stats. + + - Returns: a map containing statistics + */ + public func getStats() -> [String: RTCStats] { + return peerConnectionManager.getStats() + } + /** Changes severity level of debug logs. diff --git a/MembraneRTC/Sources/MembraneRTC/PeerConnectionManager.swift b/MembraneRTC/Sources/MembraneRTC/PeerConnectionManager.swift index 74bad0d6..e8201d3e 100644 --- a/MembraneRTC/Sources/MembraneRTC/PeerConnectionManager.swift +++ b/MembraneRTC/Sources/MembraneRTC/PeerConnectionManager.swift @@ -26,6 +26,8 @@ internal class PeerConnectionManager: NSObject, RTCPeerConnectionDelegate { private let peerConnectionListener: PeerConnectionListener + private var peerConnectionStats: [String: RTCStats] = [:] + internal init( config: RTCConfiguration, peerConnectionFactory: PeerConnectionFactoryWrapper, peerConnectionListener: PeerConnectionListener @@ -274,6 +276,57 @@ internal class PeerConnectionManager: NSObject, RTCPeerConnectionDelegate { sender.parameters = params } + private func extractRelevantStats(rp: RTCStatisticsReport) { + rp.statistics.forEach { it1 in + let it = it1.value + if it.type == "outbound-rtp" { + let duration = it.values["qualityLimitationDurations"] as? [String: Double] + let qualityLimitation: QualityLimitationDurations = QualityLimitationDurations( + bandwidth: duration?["bandwidth"] ?? 0.0, + cpu: duration?["cpu"] ?? 0.0, none: duration?["none"] ?? 0.0, other: duration?["other"] ?? 0.0) + + let tmp = RTCOutboundStats( + kind: it.values["kind"] as? String ?? "", + rid: it.values["rid"] as? String ?? "", + bytesSent: it.values["bytesSent"] as? UInt ?? 0, + targetBitrate: it.values["targetBitrate"] as? Double ?? 0.0, + packetsSent: it.values["packetsSent"] as? UInt ?? 0, + framesEncoded: it.values["framesEncoded"] as? UInt ?? 0, + framesPerSecond: it.values["framesPerSecond"] as? Double ?? 0.0, + frameWidth: it.values["frameWidth"] as? UInt ?? 0, + frameHeight: it.values["frameHeight"] as? UInt ?? 0, + qualityLimitationDurations: qualityLimitation + ) + + peerConnectionStats[it.id as String] = tmp + } else if it.type == "inbound-rtp" { + let tmp = RTCInboundStats( + kind: it.values["kind"] as? String ?? "", + jitter: it.values["jitter"] as? Double ?? 0.0, + packetsLost: it.values["packetsLost"] as? UInt ?? 0, + packetsReceived: it.values["packetsReceived"] as? UInt ?? 0, + bytesReceived: it.values["bytesReceived"] as? UInt ?? 0, + framesReceived: it.values["framesReceived"] as? UInt ?? 0, + frameWidth: it.values["frameWidth"] as? UInt ?? 0, + frameHeight: it.values["frameHeight"] as? UInt ?? 0, + framesPerSecond: it.values["framesPerSecond"] as? Double ?? 0.0, + framesDropped: it.values["framesDropped"] as? UInt ?? 0 + ) + + peerConnectionStats[it.id as String] = tmp + } + } + } + + public func getStats() -> [String: RTCStats] { + if let connection = connection { + connection.statistics(completionHandler: { RTCStatisticsReport in + self.extractRelevantStats(rp: RTCStatisticsReport) + }) + } + return peerConnectionStats + } + private func applyBitrate(encodings: [RTCRtpEncodingParameters], maxBitrate: TrackBandwidthLimit) { switch maxBitrate { case .BandwidthLimit(let limit): diff --git a/MembraneRTC/Sources/MembraneRTC/Types/RTCStats.swift b/MembraneRTC/Sources/MembraneRTC/Types/RTCStats.swift new file mode 100644 index 00000000..c8f72559 --- /dev/null +++ b/MembraneRTC/Sources/MembraneRTC/Types/RTCStats.swift @@ -0,0 +1,76 @@ +public struct QualityLimitationDurations { + public let bandwidth: Double + public let cpu: Double + public let none: Double + public let other: Double + + public init(bandwidth: Double, cpu: Double, none: Double, other: Double) { + self.bandwidth = bandwidth + self.cpu = cpu + self.none = none + self.other = other + } +} + +public protocol RTCStats {} + +public struct RTCOutboundStats: RTCStats { + public let kind: String + public let rid: String + public let bytesSent: UInt + public let targetBitrate: Double + public let packetsSent: UInt + public let framesEncoded: UInt + public let framesPerSecond: Double + public let frameWidth: UInt + public let frameHeight: UInt + public let qualityLimitationDurations: QualityLimitationDurations? + + public init( + kind: String = "", rid: String = "", bytesSent: UInt = 0, targetBitrate: Double = 0.0, packetsSent: UInt = 0, + framesEncoded: UInt = 0, framesPerSecond: Double = 0.0, frameWidth: UInt = 0, frameHeight: UInt = 0, + qualityLimitationDurations: QualityLimitationDurations? = nil + ) { + self.kind = kind + self.rid = rid + self.bytesSent = bytesSent + self.targetBitrate = targetBitrate + self.packetsSent = packetsSent + self.framesEncoded = framesEncoded + self.framesPerSecond = framesPerSecond + self.frameWidth = frameWidth + self.frameHeight = frameHeight + self.qualityLimitationDurations = qualityLimitationDurations + } +} + +public struct RTCInboundStats: RTCStats { + public let kind: String + public let jitter: Double + public let packetsLost: UInt + public let packetsReceived: UInt + public let bytesReceived: UInt + public let framesReceived: UInt + public let frameWidth: UInt + public let frameHeight: UInt + public let framesPerSecond: Double + public let framesDropped: UInt + + public init( + kind: String = "", jitter: Double = 0.0, packetsLost: UInt = 0, packetsReceived: UInt = 0, + bytesReceived: UInt = 0, + framesReceived: UInt = 0, frameWidth: UInt = 0, frameHeight: UInt = 0, framesPerSecond: Double = 0.0, + framesDropped: UInt = 0 + ) { + self.kind = kind + self.jitter = jitter + self.packetsLost = packetsLost + self.packetsReceived = packetsReceived + self.bytesReceived = bytesReceived + self.framesReceived = framesReceived + self.frameWidth = frameWidth + self.frameHeight = frameHeight + self.framesPerSecond = framesPerSecond + self.framesDropped = framesDropped + } +}