summaryrefslogtreecommitdiffstats
path: root/comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media
diff options
context:
space:
mode:
Diffstat (limited to 'comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media')
-rw-r--r--comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media/mediaSsrcHandler.js62
-rw-r--r--comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media/mediaTrackHandler.js69
-rw-r--r--comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media/mediaTrackStats.js150
-rw-r--r--comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media/mediaTrackStatsHandler.js82
4 files changed, 363 insertions, 0 deletions
diff --git a/comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media/mediaSsrcHandler.js b/comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media/mediaSsrcHandler.js
new file mode 100644
index 0000000000..5e43415558
--- /dev/null
+++ b/comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media/mediaSsrcHandler.js
@@ -0,0 +1,62 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.MediaSsrcHandler = void 0;
+var _sdpTransform = require("sdp-transform");
+function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
+function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } /*
+ Copyright 2023 The Matrix.org Foundation C.I.C.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+class MediaSsrcHandler {
+ constructor() {
+ _defineProperty(this, "ssrcToMid", {
+ local: new Map(),
+ remote: new Map()
+ });
+ }
+ findMidBySsrc(ssrc, type) {
+ let mid;
+ this.ssrcToMid[type].forEach((ssrcs, m) => {
+ if (ssrcs.find(s => s == ssrc)) {
+ mid = m;
+ return;
+ }
+ });
+ return mid;
+ }
+ parse(description, type) {
+ const sdp = (0, _sdpTransform.parse)(description);
+ const ssrcToMid = new Map();
+ sdp.media.forEach(m => {
+ if (!!m.mid && m.type === "video" || m.type === "audio") {
+ const ssrcs = [];
+ m.ssrcs?.forEach(ssrc => {
+ if (ssrc.attribute === "cname") {
+ ssrcs.push(`${ssrc.id}`);
+ }
+ });
+ ssrcToMid.set(`${m.mid}`, ssrcs);
+ }
+ });
+ this.ssrcToMid[type] = ssrcToMid;
+ }
+ getSsrcToMidMap(type) {
+ return this.ssrcToMid[type];
+ }
+}
+exports.MediaSsrcHandler = MediaSsrcHandler; \ No newline at end of file
diff --git a/comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media/mediaTrackHandler.js b/comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media/mediaTrackHandler.js
new file mode 100644
index 0000000000..c4252a9cbd
--- /dev/null
+++ b/comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media/mediaTrackHandler.js
@@ -0,0 +1,69 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.MediaTrackHandler = void 0;
+/*
+Copyright 2023 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+class MediaTrackHandler {
+ constructor(pc) {
+ this.pc = pc;
+ }
+ getLocalTracks(kind) {
+ const isNotNullAndKind = track => {
+ return track !== null && track.kind === kind;
+ };
+ // @ts-ignore The linter don't get it
+ return this.pc.getTransceivers().filter(t => t.currentDirection === "sendonly" || t.currentDirection === "sendrecv").filter(t => t.sender !== null).map(t => t.sender).map(s => s.track).filter(isNotNullAndKind);
+ }
+ getTackById(trackId) {
+ return this.pc.getTransceivers().map(t => {
+ if (t?.sender.track !== null && t.sender.track.id === trackId) {
+ return t.sender.track;
+ }
+ if (t?.receiver.track !== null && t.receiver.track.id === trackId) {
+ return t.receiver.track;
+ }
+ return undefined;
+ }).find(t => t !== undefined);
+ }
+ getLocalTrackIdByMid(mid) {
+ const transceiver = this.pc.getTransceivers().find(t => t.mid === mid);
+ if (transceiver !== undefined && !!transceiver.sender && !!transceiver.sender.track) {
+ return transceiver.sender.track.id;
+ }
+ return undefined;
+ }
+ getRemoteTrackIdByMid(mid) {
+ const transceiver = this.pc.getTransceivers().find(t => t.mid === mid);
+ if (transceiver !== undefined && !!transceiver.receiver && !!transceiver.receiver.track) {
+ return transceiver.receiver.track.id;
+ }
+ return undefined;
+ }
+ getActiveSimulcastStreams() {
+ //@TODO implement this right.. Check how many layer configured
+ return 3;
+ }
+ getTransceiverByTrackId(trackId) {
+ return this.pc.getTransceivers().find(t => {
+ return t.receiver.track.id === trackId || t.sender.track !== null && t.sender.track.id === trackId;
+ });
+ }
+}
+exports.MediaTrackHandler = MediaTrackHandler; \ No newline at end of file
diff --git a/comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media/mediaTrackStats.js b/comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media/mediaTrackStats.js
new file mode 100644
index 0000000000..d5a7963c23
--- /dev/null
+++ b/comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media/mediaTrackStats.js
@@ -0,0 +1,150 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.MediaTrackStats = void 0;
+function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
+function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
+/*
+Copyright 2023 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+class MediaTrackStats {
+ constructor(trackId, type, kind) {
+ this.trackId = trackId;
+ this.type = type;
+ this.kind = kind;
+ _defineProperty(this, "loss", {
+ packetsTotal: 0,
+ packetsLost: 0,
+ isDownloadStream: false
+ });
+ _defineProperty(this, "bitrate", {
+ download: 0,
+ upload: 0
+ });
+ _defineProperty(this, "resolution", {
+ width: -1,
+ height: -1
+ });
+ _defineProperty(this, "audioConcealment", {
+ concealedAudio: 0,
+ totalAudioDuration: 0
+ });
+ _defineProperty(this, "framerate", 0);
+ _defineProperty(this, "jitter", 0);
+ _defineProperty(this, "codec", "");
+ _defineProperty(this, "isAlive", true);
+ _defineProperty(this, "isMuted", false);
+ _defineProperty(this, "isEnabled", true);
+ }
+ getType() {
+ return this.type;
+ }
+ setLoss(loss) {
+ this.loss = loss;
+ }
+ getLoss() {
+ return this.loss;
+ }
+ setResolution(resolution) {
+ this.resolution = resolution;
+ }
+ getResolution() {
+ return this.resolution;
+ }
+ setFramerate(framerate) {
+ this.framerate = framerate;
+ }
+ getFramerate() {
+ return this.framerate;
+ }
+ setBitrate(bitrate) {
+ this.bitrate = bitrate;
+ }
+ getBitrate() {
+ return this.bitrate;
+ }
+ setCodec(codecShortType) {
+ this.codec = codecShortType;
+ return true;
+ }
+ getCodec() {
+ return this.codec;
+ }
+ resetBitrate() {
+ this.bitrate = {
+ download: 0,
+ upload: 0
+ };
+ }
+ set alive(isAlive) {
+ this.isAlive = isAlive;
+ }
+
+ /**
+ * A MediaTrackState is alive if the corresponding MediaStreamTrack track bound to a transceiver and the
+ * MediaStreamTrack is in state MediaStreamTrack.readyState === live
+ */
+ get alive() {
+ return this.isAlive;
+ }
+ set muted(isMuted) {
+ this.isMuted = isMuted;
+ }
+
+ /**
+ * A MediaTrackState.isMuted corresponding to MediaStreamTrack.muted.
+ * But these values only match if MediaTrackState.isAlive.
+ */
+ get muted() {
+ return this.isMuted;
+ }
+ set enabled(isEnabled) {
+ this.isEnabled = isEnabled;
+ }
+
+ /**
+ * A MediaTrackState.isEnabled corresponding to MediaStreamTrack.enabled.
+ * But these values only match if MediaTrackState.isAlive.
+ */
+ get enabled() {
+ return this.isEnabled;
+ }
+ setJitter(jitter) {
+ this.jitter = jitter;
+ }
+
+ /**
+ * Jitter in milliseconds
+ */
+ getJitter() {
+ return this.jitter;
+ }
+
+ /**
+ * Audio concealment ration (conceled duration / total duration)
+ */
+ setAudioConcealment(concealedAudioDuration, totalAudioDuration) {
+ this.audioConcealment.concealedAudio = concealedAudioDuration;
+ this.audioConcealment.totalAudioDuration = totalAudioDuration;
+ }
+ getAudioConcealment() {
+ return this.audioConcealment;
+ }
+}
+exports.MediaTrackStats = MediaTrackStats; \ No newline at end of file
diff --git a/comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media/mediaTrackStatsHandler.js b/comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media/mediaTrackStatsHandler.js
new file mode 100644
index 0000000000..f72f644cb3
--- /dev/null
+++ b/comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media/mediaTrackStatsHandler.js
@@ -0,0 +1,82 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.MediaTrackStatsHandler = void 0;
+var _mediaTrackStats = require("./mediaTrackStats");
+function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
+function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } /*
+ Copyright 2023 The Matrix.org Foundation C.I.C.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+class MediaTrackStatsHandler {
+ constructor(mediaSsrcHandler, mediaTrackHandler) {
+ this.mediaSsrcHandler = mediaSsrcHandler;
+ this.mediaTrackHandler = mediaTrackHandler;
+ _defineProperty(this, "track2stats", new Map());
+ }
+
+ /**
+ * Find tracks by rtc stats
+ * Argument report is any because the stats api is not consistent:
+ * For example `trackIdentifier`, `mid` not existing in every implementations
+ * https://www.w3.org/TR/webrtc-stats/#dom-rtcinboundrtpstreamstats
+ * https://developer.mozilla.org/en-US/docs/Web/API/RTCInboundRtpStreamStats
+ */
+ findTrack2Stats(report, type) {
+ let trackID;
+ if (report.trackIdentifier) {
+ trackID = report.trackIdentifier;
+ } else if (report.mid) {
+ trackID = type === "remote" ? this.mediaTrackHandler.getRemoteTrackIdByMid(report.mid) : this.mediaTrackHandler.getLocalTrackIdByMid(report.mid);
+ } else if (report.ssrc) {
+ const mid = this.mediaSsrcHandler.findMidBySsrc(report.ssrc, type);
+ if (!mid) {
+ return undefined;
+ }
+ trackID = type === "remote" ? this.mediaTrackHandler.getRemoteTrackIdByMid(report.mid) : this.mediaTrackHandler.getLocalTrackIdByMid(report.mid);
+ }
+ if (!trackID) {
+ return undefined;
+ }
+ let trackStats = this.track2stats.get(trackID);
+ if (!trackStats) {
+ const track = this.mediaTrackHandler.getTackById(trackID);
+ if (track !== undefined) {
+ const kind = track.kind === "audio" ? track.kind : "video";
+ trackStats = new _mediaTrackStats.MediaTrackStats(trackID, type, kind);
+ this.track2stats.set(trackID, trackStats);
+ } else {
+ return undefined;
+ }
+ }
+ return trackStats;
+ }
+ findLocalVideoTrackStats(report) {
+ const localVideoTracks = this.mediaTrackHandler.getLocalTracks("video");
+ if (localVideoTracks.length === 0) {
+ return undefined;
+ }
+ return this.findTrack2Stats(report, "local");
+ }
+ getTrack2stats() {
+ return this.track2stats;
+ }
+ findTransceiverByTrackId(trackID) {
+ return this.mediaTrackHandler.getTransceiverByTrackId(trackID);
+ }
+}
+exports.MediaTrackStatsHandler = MediaTrackStatsHandler; \ No newline at end of file