diff options
Diffstat (limited to 'comm/chat/protocols/matrix/lib/matrix-sdk/webrtc/stats/media')
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 |