From c8bcd8b26c90ce3a348d696a46b89890422b3413 Mon Sep 17 00:00:00 2001 From: evdokimovs <49490279+evdokimovs@users.noreply.github.com> Date: Thu, 30 May 2024 19:49:21 +0200 Subject: [PATCH] Implement `PeerConnection.getStats()` for iOS (#161) --- .../medea_flutter_webrtc/model/RtcStats.kt | 3 ++- example/integration_test/webrtc_test.dart | 4 --- .../controller/PeerConnectionController.swift | 9 +++++++ ios/Classes/model/RtcStats.swift | 27 +++++++++++++++++++ ios/Classes/proxy/PeerConnectionProxy.swift | 13 +++++++++ lib/src/model/stats.dart | 3 +-- 6 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 ios/Classes/model/RtcStats.swift diff --git a/android/src/main/kotlin/com/instrumentisto/medea_flutter_webrtc/model/RtcStats.kt b/android/src/main/kotlin/com/instrumentisto/medea_flutter_webrtc/model/RtcStats.kt index 92f8ac26ed..b704b42231 100644 --- a/android/src/main/kotlin/com/instrumentisto/medea_flutter_webrtc/model/RtcStats.kt +++ b/android/src/main/kotlin/com/instrumentisto/medea_flutter_webrtc/model/RtcStats.kt @@ -28,6 +28,7 @@ data class RtcStats( /** Converts these [RtcStats] into a [Map] which can be returned to the Flutter side. */ fun asFlutterResult(): Map { - return mapOf("id" to id, "timestampUs" to timestampUs, "kind" to kind, "type" to type) + var report = mapOf("id" to id, "timestampUs" to timestampUs, "type" to type) + return report + kind } } diff --git a/example/integration_test/webrtc_test.dart b/example/integration_test/webrtc_test.dart index d10f35c89e..7a9a83e4be 100644 --- a/example/integration_test/webrtc_test.dart +++ b/example/integration_test/webrtc_test.dart @@ -1186,10 +1186,6 @@ void main() { }); testWidgets('Peer connection get stats.', (WidgetTester tester) async { - // TODO: Support stats for iOS platform. - if (Platform.isIOS) { - return; - } var pc1 = await PeerConnection.create(IceTransportType.all, []); var pc2 = await PeerConnection.create(IceTransportType.all, []); diff --git a/ios/Classes/controller/PeerConnectionController.swift b/ios/Classes/controller/PeerConnectionController.swift index d53f03c5c5..7cc9a569e4 100644 --- a/ios/Classes/controller/PeerConnectionController.swift +++ b/ios/Classes/controller/PeerConnectionController.swift @@ -139,6 +139,15 @@ class PeerConnectionController { self.sendResultFromTask(result, getFlutterError(error)) } } + case "getStats": + Task { + do { + let report = try await self.peer.getStats() + self.sendResultFromTask(result, report.asFlutterResult()) + } catch { + self.sendResultFromTask(result, getFlutterError(error)) + } + } case "addTransceiver": let mediaType = argsMap!["mediaType"] as? Int let initArgs = argsMap!["init"] as? [String: Any] diff --git a/ios/Classes/model/RtcStats.swift b/ios/Classes/model/RtcStats.swift new file mode 100644 index 0000000000..4037fdb5b2 --- /dev/null +++ b/ios/Classes/model/RtcStats.swift @@ -0,0 +1,27 @@ +/// Representation of an `RTCStatisticsReport`. +class RtcStats { + /// List of all RTC stats reports converted to flat `Map`. + var statsList: [[String: Any]] = [] + + /// Converts the provided `RTCStatisticsReport` into `RtcStats`. + init(report: RTCStatisticsReport) { + for (_, stats) in report.statistics { + var statDetails: [String: Any] = [:] + statDetails["id"] = stats.id + statDetails["type"] = stats.type + statDetails["timestampUs"] = Int(stats.timestamp_us) + + for (statName, statValue) in stats.values { + statDetails[statName] = statValue + } + + self.statsList.append(statDetails) + } + } + + /// Converts these `RtcStats` into a `Map` which can be returned to the + /// Flutter side. + func asFlutterResult() -> [[String: Any]] { + return self.statsList + } +} diff --git a/ios/Classes/proxy/PeerConnectionProxy.swift b/ios/Classes/proxy/PeerConnectionProxy.swift index 3c9543bbf2..11ed285b1a 100644 --- a/ios/Classes/proxy/PeerConnectionProxy.swift +++ b/ios/Classes/proxy/PeerConnectionProxy.swift @@ -39,6 +39,19 @@ class PeerConnectionProxy { self.id } + /// Returns `RtcStats` of this `PeerConnectionProxy`. + func getStats() async throws -> RtcStats { + return try await withCheckedThrowingContinuation { continuation in + do { + try self.peer.statistics { report in + continuation.resume(returning: RtcStats(report: report)) + } + } catch { + continuation.resume(throwing: error) + } + } + } + /// Synchronizes and returns all the `RtpTransceiverProxy`s of this /// `PeerConnectionProxy`. func getTransceivers() -> [RtpTransceiverProxy] { diff --git a/lib/src/model/stats.dart b/lib/src/model/stats.dart index 00b4d7152f..e5ee248399 100644 --- a/lib/src/model/stats.dart +++ b/lib/src/model/stats.dart @@ -54,8 +54,7 @@ class RtcStats { /// Creates [RTCStats] basing on the [Map] received from the native side. static RtcStats? fromMap(dynamic stats) { - stats['kind']['type'] = stats['type']; - var kind = RtcStatsType.fromMap(stats['kind']); + var kind = RtcStatsType.fromMap(stats); if (kind == null) { return null; } else {