From dbb76bfb8d123ce23f4658253273c9b312b8d0a1 Mon Sep 17 00:00:00 2001
From: Andrei Gavrilescu <51706180+andrei-gavrilescu@users.noreply.github.com>
Date: Thu, 3 Oct 2024 10:34:02 +0300
Subject: [PATCH] feat(statistics): add pre call test API

---
 JitsiMeetJS.ts                    | 11 +++++++++++
 modules/statistics/PreCallTest.ts | 30 ++++++++++++++++++++++++++++++
 package-lock.json                 | 11 +++++++++++
 package.json                      |  1 +
 webpack-shared-config.js          |  4 ++--
 5 files changed, 55 insertions(+), 2 deletions(-)
 create mode 100644 modules/statistics/PreCallTest.ts

diff --git a/JitsiMeetJS.ts b/JitsiMeetJS.ts
index fea313ce79..78b3c434ec 100644
--- a/JitsiMeetJS.ts
+++ b/JitsiMeetJS.ts
@@ -35,6 +35,7 @@ import * as E2ePingEvents from './service/e2eping/E2ePingEvents';
 import { createGetUserMediaEvent } from './service/statistics/AnalyticsEvents';
 import *  as RTCStatsEvents from './modules/RTCStats/RTCStatsEvents';
 import { VideoType } from './service/RTC/VideoType';
+import runPreCallTest, { IceServer, PreCallResult } from './modules/statistics/PreCallTest';
 
 const logger = Logger.getLogger(__filename);
 
@@ -477,6 +478,16 @@ export default {
         NetworkInfo.updateNetworkInfo({ isOnline });
     },
 
+    /**
+     * Run a pre-call test to check the network conditions.
+     * 
+     * @param {IceServer} iceServers  - The ICE servers to use for the test,
+     * @returns {Promise<PreCallResult | any>} - A Promise that resolves with the test results or rejects with an error message.
+     */
+    runPreCallTest(iceServers) {
+        return runPreCallTest(iceServers);
+    },
+
     /**
      * Represents a hub/namespace for utility functionality which may be of
      * interest to lib-jitsi-meet clients.
diff --git a/modules/statistics/PreCallTest.ts b/modules/statistics/PreCallTest.ts
new file mode 100644
index 0000000000..a1bc24ea01
--- /dev/null
+++ b/modules/statistics/PreCallTest.ts
@@ -0,0 +1,30 @@
+import PreCallTest from '@jitsi/precall-test';
+
+
+export interface PreCallResult {
+    throughput: number; // Maximum bandwidth reached in kbps  (kilo bits per second).
+    fractionalLoss: number; // Packet loss percentage over all the test traffic.
+    rtt: number;  // Round trip time in milliseconds.
+    jitter: number;  // Variation in packet arrival times during the transmission of media.
+    mediaConnectivity: boolean; // Whether the data channel was able to send data or not.
+}
+
+// Same interface as a PeerConnection configuration object.
+export interface IceServer {
+    urls: Array<string> | string;
+    username?: string;
+    credential?: string;
+}
+
+/**
+ * Run a pre-call test to check the network conditions. It uses a TURN server to establish
+ * a connection between two PeerConnections using the server as a relay. Afterwards it sends 
+ * some test traffic through a data channel to measure the network conditions, these are 
+ * recorded and returned through a Promise.
+ * 
+ * @param {Array<IceServer>} - The ICE servers to use for the test, these are passes to the PeerConnection constructor. 
+ * @returns {Promise<PreCallResult | any>} - A Promise that resolves with the test results or rejects with an error.
+ */
+export default async function runPreCallTest(iceServers: Array<IceServer>): Promise<PreCallResult | string> {
+    return new PreCallTest().start(iceServers);
+}
diff --git a/package-lock.json b/package-lock.json
index 57c948724e..62276958cd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
       "dependencies": {
         "@jitsi/js-utils": "2.2.1",
         "@jitsi/logger": "2.0.2",
+        "@jitsi/precall-test": "1.0.6",
         "@jitsi/rtcstats": "9.7.0",
         "@jitsi/sdp-interop": "git+https://github.com/jitsi/sdp-interop#3d49eb4aa26863a3f8d32d7581cdb4321244266b",
         "@testrtc/watchrtc-sdk": "1.38.2",
@@ -2023,6 +2024,11 @@
       "resolved": "https://registry.npmjs.org/@jitsi/logger/-/logger-2.0.2.tgz",
       "integrity": "sha512-qwbpRwuwkBFgh0F5jivq/5fAm46yVoXURc5LCklEs8lAShYVangFEXKW7RLpZuZ5nQnrHrlvU8MswQNREmvahg=="
     },
+    "node_modules/@jitsi/precall-test": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/@jitsi/precall-test/-/precall-test-1.0.6.tgz",
+      "integrity": "sha512-L/gnRjnFxpQqvfILXeFoVTHEtwL01zoSKAIqMVxMmaD0ahozML8uztlxRPlYSbKrT31j3Cm1ss9KdSNeO3f9FQ=="
+    },
     "node_modules/@jitsi/rtcstats": {
       "version": "9.7.0",
       "resolved": "https://registry.npmjs.org/@jitsi/rtcstats/-/rtcstats-9.7.0.tgz",
@@ -8685,6 +8691,11 @@
       "resolved": "https://registry.npmjs.org/@jitsi/logger/-/logger-2.0.2.tgz",
       "integrity": "sha512-qwbpRwuwkBFgh0F5jivq/5fAm46yVoXURc5LCklEs8lAShYVangFEXKW7RLpZuZ5nQnrHrlvU8MswQNREmvahg=="
     },
+    "@jitsi/precall-test": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/@jitsi/precall-test/-/precall-test-1.0.6.tgz",
+      "integrity": "sha512-L/gnRjnFxpQqvfILXeFoVTHEtwL01zoSKAIqMVxMmaD0ahozML8uztlxRPlYSbKrT31j3Cm1ss9KdSNeO3f9FQ=="
+    },
     "@jitsi/rtcstats": {
       "version": "9.7.0",
       "resolved": "https://registry.npmjs.org/@jitsi/rtcstats/-/rtcstats-9.7.0.tgz",
diff --git a/package.json b/package.json
index d84a2c7bf9..47c36a0dc3 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,7 @@
   "dependencies": {
     "@jitsi/js-utils": "2.2.1",
     "@jitsi/logger": "2.0.2",
+    "@jitsi/precall-test": "1.0.6",
     "@jitsi/rtcstats": "9.7.0",
     "@jitsi/sdp-interop": "git+https://github.com/jitsi/sdp-interop#3d49eb4aa26863a3f8d32d7581cdb4321244266b",
     "@testrtc/watchrtc-sdk": "1.38.2",
diff --git a/webpack-shared-config.js b/webpack-shared-config.js
index 764649e1fe..3ee7fcbcc1 100644
--- a/webpack-shared-config.js
+++ b/webpack-shared-config.js
@@ -80,8 +80,8 @@ module.exports = (minimize, analyzeBundle) => {
         },
         performance: {
             hints: minimize ? 'error' : false,
-            maxAssetSize: 1.08 * 1024 * 1024,
-            maxEntrypointSize: 1.08 * 1024 * 1024
+            maxAssetSize: 1.25 * 1024 * 1024,
+            maxEntrypointSize: 1.25 * 1024 * 1024
         },
         plugins: [
             new IgnorePlugin({ resourceRegExp: /^(@xmldom\/xmldom|ws)$/ }),