From 1f88e8179471e67440061ed5073278af9a565266 Mon Sep 17 00:00:00 2001 From: Karol Sygiet Date: Thu, 27 Apr 2023 15:23:24 +0200 Subject: [PATCH] [MV-410] Add getStats function (#39) --- .../rtc/InternalMembraneRTC.kt | 5 ++ .../org/membraneframework/rtc/MembraneRTC.kt | 9 +++ .../rtc/PeerConnectionManager.kt | 55 +++++++++++++++++++ .../membraneframework/rtc/models/RTCStats.kt | 38 +++++++++++++ 4 files changed, 107 insertions(+) create mode 100644 MembraneRTC/src/main/java/org/membraneframework/rtc/models/RTCStats.kt diff --git a/MembraneRTC/src/main/java/org/membraneframework/rtc/InternalMembraneRTC.kt b/MembraneRTC/src/main/java/org/membraneframework/rtc/InternalMembraneRTC.kt index 8ab60ff..dff6c68 100644 --- a/MembraneRTC/src/main/java/org/membraneframework/rtc/InternalMembraneRTC.kt +++ b/MembraneRTC/src/main/java/org/membraneframework/rtc/InternalMembraneRTC.kt @@ -12,6 +12,7 @@ import org.membraneframework.rtc.events.OfferData import org.membraneframework.rtc.media.* import org.membraneframework.rtc.models.EncodingReason import org.membraneframework.rtc.models.Peer +import org.membraneframework.rtc.models.RTCStats import org.membraneframework.rtc.models.TrackContext import org.membraneframework.rtc.models.VadStatus import org.membraneframework.rtc.transport.EventTransportError @@ -491,4 +492,8 @@ constructor( listener.onTrackReady(trackContext) } + + fun getStats(): Map { + return peerConnectionManager.getStats() + } } diff --git a/MembraneRTC/src/main/java/org/membraneframework/rtc/MembraneRTC.kt b/MembraneRTC/src/main/java/org/membraneframework/rtc/MembraneRTC.kt index 1e91f07..feaacf2 100644 --- a/MembraneRTC/src/main/java/org/membraneframework/rtc/MembraneRTC.kt +++ b/MembraneRTC/src/main/java/org/membraneframework/rtc/MembraneRTC.kt @@ -5,6 +5,7 @@ import android.content.Intent import kotlinx.coroutines.Dispatchers import org.membraneframework.rtc.dagger.DaggerMembraneRTCComponent import org.membraneframework.rtc.media.* +import org.membraneframework.rtc.models.RTCStats import org.membraneframework.rtc.utils.Metadata import org.webrtc.Logging @@ -206,6 +207,14 @@ private constructor( Logging.enableLogToDebugOutput(severity) } + /** + * Returns current connection stats + * @return a map containing statistics + */ + fun getStats(): Map { + return client.getStats() + } + companion object { /** * Creates an instance of MembraneRTC client and starts the connecting process. diff --git a/MembraneRTC/src/main/java/org/membraneframework/rtc/PeerConnectionManager.kt b/MembraneRTC/src/main/java/org/membraneframework/rtc/PeerConnectionManager.kt index e6831a6..cbabcf0 100644 --- a/MembraneRTC/src/main/java/org/membraneframework/rtc/PeerConnectionManager.kt +++ b/MembraneRTC/src/main/java/org/membraneframework/rtc/PeerConnectionManager.kt @@ -13,9 +13,14 @@ import org.membraneframework.rtc.media.LocalScreencastTrack import org.membraneframework.rtc.media.LocalTrack import org.membraneframework.rtc.media.LocalVideoTrack import org.membraneframework.rtc.media.TrackBandwidthLimit +import org.membraneframework.rtc.models.QualityLimitationDurations +import org.membraneframework.rtc.models.RTCInboundStats +import org.membraneframework.rtc.models.RTCOutboundStats +import org.membraneframework.rtc.models.RTCStats import org.membraneframework.rtc.utils.* import org.webrtc.* import timber.log.Timber +import java.math.BigInteger import java.util.* import kotlin.math.pow @@ -35,6 +40,7 @@ internal class PeerConnectionManager private var peerConnection: PeerConnection? = null private val peerConnectionMutex = Mutex() + private val peerConnectionStats = mutableMapOf() private var iceServers: List? = null private var config: PeerConnection.RTCConfiguration? = null @@ -466,6 +472,55 @@ internal class PeerConnectionManager override fun onRenegotiationNeeded() { Timber.d("Renegotiation needed") } + + fun getStats(): Map { + peerConnection?.getStats { rtcStatsReport -> extractRelevantStats(rtcStatsReport) } + return peerConnectionStats.toMap() + } + + private fun extractRelevantStats(rp: RTCStatsReport) { + rp.statsMap.values.forEach { + if (it.type == "outbound-rtp") { + val durations = it.members["qualityLimitationDurations"] as? Map<*, *> + val qualityLimitation = QualityLimitationDurations( + durations?.get("bandwidth") as? Double ?: 0.0, + durations?.get("cpu") as? Double ?: 0.0, + durations?.get("none") as? Double ?: 0.0, + durations?.get("other") as? Double ?: 0.0 + ) + + val tmp = RTCOutboundStats( + it.members["kind"] as? String, + it.members["rid"] as? String, + it.members["bytesSent"] as? BigInteger, + it.members["targetBitrate"] as? Double, + it.members["packetsSent"] as? Long, + it.members["framesEncoded"] as? Long, + it.members["framesPerSecond"] as? Double, + it.members["frameWidth"] as? Long, + it.members["frameHeight"] as? Long, + qualityLimitation + ) + + peerConnectionStats[it.id as String] = tmp + } else if (it.type == "inbound-rtp") { + val tmp = RTCInboundStats( + it.members["kind"] as? String, + it.members["jitter"] as? Double, + it.members["packetsLost"] as? Int, + it.members["packetsReceived"] as? Long, + it.members["bytesReceived"] as? BigInteger, + it.members["framesReceived"] as? Int, + it.members["frameWidth"] as? Long, + it.members["frameHeight"] as? Long, + it.members["framesPerSecond"] as? Double, + it.members["framesDropped"] as? Long + ) + + peerConnectionStats[it.id as String] = tmp + } + } + } } /** diff --git a/MembraneRTC/src/main/java/org/membraneframework/rtc/models/RTCStats.kt b/MembraneRTC/src/main/java/org/membraneframework/rtc/models/RTCStats.kt new file mode 100644 index 0000000..91e64e4 --- /dev/null +++ b/MembraneRTC/src/main/java/org/membraneframework/rtc/models/RTCStats.kt @@ -0,0 +1,38 @@ +package org.membraneframework.rtc.models + +import java.math.BigInteger + +data class QualityLimitationDurations( + val bandwidth: Double, + val cpu: Double, + val none: Double, + val other: Double +) + +open class RTCStats + +data class RTCOutboundStats( + val kind: String? = "", + val rid: String? = "", + val bytesSent: BigInteger? = BigInteger("0"), + val targetBitrate: Double? = 0.0, + val packetsSent: Long? = 0, + val framesEncoded: Long? = 0, + val framesPerSecond: Double? = 0.0, + val frameWidth: Long? = 0, + val frameHeight: Long? = 0, + val qualityLimitationDurations: QualityLimitationDurations? +) : RTCStats() + +data class RTCInboundStats( + val kind: String? = "", + val jitter: Double? = 0.0, + val packetsLost: Int? = 0, + val packetsReceived: Long? = 0, + val bytesReceived: BigInteger? = BigInteger("0"), + val framesReceived: Int? = 0, + val frameWidth: Long? = 0, + val frameHeight: Long? = 0, + val framesPerSecond: Double? = 0.0, + val framesDropped: Long? = 0 +) : RTCStats()