init
This commit is contained in:
191
node_modules/matrix-js-sdk/lib/webrtc/stats/callStatsReportGatherer.js
generated
vendored
Normal file
191
node_modules/matrix-js-sdk/lib/webrtc/stats/callStatsReportGatherer.js
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
import _objectSpread from "@babel/runtime/helpers/objectSpread2";
|
||||
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
import { ConnectionStats } from "./connectionStats.js";
|
||||
import { ConnectionStatsBuilder } from "./connectionStatsBuilder.js";
|
||||
import { TransportStatsBuilder } from "./transportStatsBuilder.js";
|
||||
import { MediaSsrcHandler } from "./media/mediaSsrcHandler.js";
|
||||
import { MediaTrackHandler } from "./media/mediaTrackHandler.js";
|
||||
import { MediaTrackStatsHandler } from "./media/mediaTrackStatsHandler.js";
|
||||
import { TrackStatsBuilder } from "./trackStatsBuilder.js";
|
||||
import { ConnectionStatsReportBuilder } from "./connectionStatsReportBuilder.js";
|
||||
import { ValueFormatter } from "./valueFormatter.js";
|
||||
import { logger } from "../../logger.js";
|
||||
import { CallFeedStatsReporter } from "./callFeedStatsReporter.js";
|
||||
export class CallStatsReportGatherer {
|
||||
constructor(callId, opponentMemberId, pc, emitter, isFocus = true) {
|
||||
_defineProperty(this, "isActive", true);
|
||||
_defineProperty(this, "previousStatsReport", void 0);
|
||||
_defineProperty(this, "currentStatsReport", void 0);
|
||||
_defineProperty(this, "connectionStats", new ConnectionStats());
|
||||
_defineProperty(this, "trackStats", void 0);
|
||||
this.callId = callId;
|
||||
this.opponentMemberId = opponentMemberId;
|
||||
this.pc = pc;
|
||||
this.emitter = emitter;
|
||||
this.isFocus = isFocus;
|
||||
pc.addEventListener("signalingstatechange", this.onSignalStateChange.bind(this));
|
||||
this.trackStats = new MediaTrackStatsHandler(new MediaSsrcHandler(), new MediaTrackHandler(pc));
|
||||
}
|
||||
async processStats(groupCallId, localUserId) {
|
||||
const summary = {
|
||||
isFirstCollection: this.previousStatsReport === undefined,
|
||||
receivedMedia: 0,
|
||||
receivedAudioMedia: 0,
|
||||
receivedVideoMedia: 0,
|
||||
audioTrackSummary: {
|
||||
count: 0,
|
||||
muted: 0,
|
||||
maxPacketLoss: 0,
|
||||
maxJitter: 0,
|
||||
concealedAudio: 0,
|
||||
totalAudio: 0
|
||||
},
|
||||
videoTrackSummary: {
|
||||
count: 0,
|
||||
muted: 0,
|
||||
maxPacketLoss: 0,
|
||||
maxJitter: 0,
|
||||
concealedAudio: 0,
|
||||
totalAudio: 0
|
||||
}
|
||||
};
|
||||
if (this.isActive) {
|
||||
const statsPromise = this.pc.getStats();
|
||||
if (typeof statsPromise?.then === "function") {
|
||||
return statsPromise.then(report => {
|
||||
// @ts-ignore
|
||||
this.currentStatsReport = typeof report?.result === "function" ? report.result() : report;
|
||||
try {
|
||||
this.processStatsReport(groupCallId, localUserId);
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
return summary;
|
||||
}
|
||||
this.previousStatsReport = this.currentStatsReport;
|
||||
summary.receivedMedia = this.connectionStats.bitrate.download;
|
||||
summary.receivedAudioMedia = this.connectionStats.bitrate.audio?.download || 0;
|
||||
summary.receivedVideoMedia = this.connectionStats.bitrate.video?.download || 0;
|
||||
const trackSummary = TrackStatsBuilder.buildTrackSummary(Array.from(this.trackStats.getTrack2stats().values()));
|
||||
return _objectSpread(_objectSpread({}, summary), {}, {
|
||||
audioTrackSummary: trackSummary.audioTrackSummary,
|
||||
videoTrackSummary: trackSummary.videoTrackSummary
|
||||
});
|
||||
}).catch(error => {
|
||||
this.handleError(error);
|
||||
return summary;
|
||||
});
|
||||
}
|
||||
this.isActive = false;
|
||||
}
|
||||
return Promise.resolve(summary);
|
||||
}
|
||||
processStatsReport(groupCallId, localUserId) {
|
||||
const byteSentStatsReport = new Map();
|
||||
byteSentStatsReport.callId = this.callId;
|
||||
byteSentStatsReport.opponentMemberId = this.opponentMemberId;
|
||||
this.currentStatsReport?.forEach(now => {
|
||||
const before = this.previousStatsReport ? this.previousStatsReport.get(now.id) : null;
|
||||
// RTCIceCandidatePairStats - https://w3c.github.io/webrtc-stats/#candidatepair-dict*
|
||||
if (now.type === "candidate-pair" && now.nominated && now.state === "succeeded") {
|
||||
this.connectionStats.bandwidth = ConnectionStatsBuilder.buildBandwidthReport(now);
|
||||
this.connectionStats.transport = TransportStatsBuilder.buildReport(this.currentStatsReport, now, this.connectionStats.transport, this.isFocus);
|
||||
|
||||
// RTCReceivedRtpStreamStats
|
||||
// https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*
|
||||
// RTCSentRtpStreamStats
|
||||
// https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*
|
||||
} else if (now.type === "inbound-rtp" || now.type === "outbound-rtp") {
|
||||
const trackStats = this.trackStats.findTrack2Stats(now, now.type === "inbound-rtp" ? "remote" : "local");
|
||||
if (!trackStats) {
|
||||
return;
|
||||
}
|
||||
if (before) {
|
||||
TrackStatsBuilder.buildPacketsLost(trackStats, now, before);
|
||||
}
|
||||
|
||||
// Get the resolution and framerate for only remote video sources here. For the local video sources,
|
||||
// 'track' stats will be used since they have the updated resolution based on the simulcast streams
|
||||
// currently being sent. Promise based getStats reports three 'outbound-rtp' streams and there will be
|
||||
// more calculations needed to determine what is the highest resolution stream sent by the client if the
|
||||
// 'outbound-rtp' stats are used.
|
||||
if (now.type === "inbound-rtp") {
|
||||
TrackStatsBuilder.buildFramerateResolution(trackStats, now);
|
||||
if (before) {
|
||||
TrackStatsBuilder.buildBitrateReceived(trackStats, now, before);
|
||||
}
|
||||
const ts = this.trackStats.findTransceiverByTrackId(trackStats.trackId);
|
||||
TrackStatsBuilder.setTrackStatsState(trackStats, ts);
|
||||
TrackStatsBuilder.buildJitter(trackStats, now);
|
||||
TrackStatsBuilder.buildAudioConcealment(trackStats, now);
|
||||
} else if (before) {
|
||||
byteSentStatsReport.set(trackStats.trackId, ValueFormatter.getNonNegativeValue(now.bytesSent));
|
||||
TrackStatsBuilder.buildBitrateSend(trackStats, now, before);
|
||||
}
|
||||
TrackStatsBuilder.buildCodec(this.currentStatsReport, trackStats, now);
|
||||
} else if (now.type === "track" && now.kind === "video" && !now.remoteSource) {
|
||||
const trackStats = this.trackStats.findLocalVideoTrackStats(now);
|
||||
if (!trackStats) {
|
||||
return;
|
||||
}
|
||||
TrackStatsBuilder.buildFramerateResolution(trackStats, now);
|
||||
TrackStatsBuilder.calculateSimulcastFramerate(trackStats, now, before, this.trackStats.mediaTrackHandler.getActiveSimulcastStreams());
|
||||
}
|
||||
});
|
||||
this.emitter.emitByteSendReport(byteSentStatsReport);
|
||||
this.emitter.emitCallFeedReport(CallFeedStatsReporter.buildCallFeedReport(this.callId, this.opponentMemberId, this.pc));
|
||||
this.processAndEmitConnectionStatsReport();
|
||||
}
|
||||
setActive(isActive) {
|
||||
this.isActive = isActive;
|
||||
}
|
||||
getActive() {
|
||||
return this.isActive;
|
||||
}
|
||||
handleError(error) {
|
||||
this.isActive = false;
|
||||
logger.warn(`CallStatsReportGatherer ${this.callId} processStatsReport fails and set to inactive ${error}`);
|
||||
}
|
||||
processAndEmitConnectionStatsReport() {
|
||||
const report = ConnectionStatsReportBuilder.build(this.trackStats.getTrack2stats());
|
||||
report.callId = this.callId;
|
||||
report.opponentMemberId = this.opponentMemberId;
|
||||
this.connectionStats.bandwidth = report.bandwidth;
|
||||
this.connectionStats.bitrate = report.bitrate;
|
||||
this.connectionStats.packetLoss = report.packetLoss;
|
||||
this.emitter.emitConnectionStatsReport(_objectSpread(_objectSpread({}, report), {}, {
|
||||
transport: this.connectionStats.transport
|
||||
}));
|
||||
this.connectionStats.transport = [];
|
||||
}
|
||||
stopProcessingStats() {}
|
||||
onSignalStateChange() {
|
||||
if (this.pc.signalingState === "stable") {
|
||||
if (this.pc.currentRemoteDescription) {
|
||||
this.trackStats.mediaSsrcHandler.parse(this.pc.currentRemoteDescription.sdp, "remote");
|
||||
}
|
||||
if (this.pc.currentLocalDescription) {
|
||||
this.trackStats.mediaSsrcHandler.parse(this.pc.currentLocalDescription.sdp, "local");
|
||||
}
|
||||
}
|
||||
}
|
||||
setOpponentMemberId(id) {
|
||||
this.opponentMemberId = id;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=callStatsReportGatherer.js.map
|
||||
Reference in New Issue
Block a user