You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-07-31 15:24:23 +03:00
Refactor naming of webrtc stats reporter (#3404)
* Refactor names in webrtc stats * Refactor summary stats reporter to gatherer * Add test for signal change * rename test
This commit is contained in:
@ -14,21 +14,22 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { StatsReportGatherer } from "../../../../src/webrtc/stats/statsReportGatherer";
|
||||
import { CallStatsReportGatherer } from "../../../../src/webrtc/stats/callStatsReportGatherer";
|
||||
import { StatsReportEmitter } from "../../../../src/webrtc/stats/statsReportEmitter";
|
||||
import { MediaSsrcHandler } from "../../../../src/webrtc/stats/media/mediaSsrcHandler";
|
||||
|
||||
const CALL_ID = "CALL_ID";
|
||||
const USER_ID = "USER_ID";
|
||||
|
||||
describe("StatsReportGatherer", () => {
|
||||
let collector: StatsReportGatherer;
|
||||
describe("CallStatsReportGatherer", () => {
|
||||
let collector: CallStatsReportGatherer;
|
||||
let rtcSpy: RTCPeerConnection;
|
||||
let emitter: StatsReportEmitter;
|
||||
beforeEach(() => {
|
||||
rtcSpy = { getStats: () => new Promise<RTCStatsReport>(() => null) } as RTCPeerConnection;
|
||||
rtcSpy.addEventListener = jest.fn();
|
||||
emitter = new StatsReportEmitter();
|
||||
collector = new StatsReportGatherer(CALL_ID, USER_ID, rtcSpy, emitter);
|
||||
collector = new CallStatsReportGatherer(CALL_ID, USER_ID, rtcSpy, emitter);
|
||||
});
|
||||
|
||||
describe("on process stats", () => {
|
||||
@ -145,4 +146,44 @@ describe("StatsReportGatherer", () => {
|
||||
expect(collector.getActive()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("on signal state change event", () => {
|
||||
let events: { [key: string]: any };
|
||||
beforeEach(() => {
|
||||
events = [];
|
||||
// Define the addEventListener method with a Jest mock function
|
||||
rtcSpy.addEventListener = jest.fn((event: any, callback: any) => {
|
||||
events[event] = callback;
|
||||
});
|
||||
|
||||
collector = new CallStatsReportGatherer(CALL_ID, USER_ID, rtcSpy, emitter);
|
||||
});
|
||||
it("in case of stable, parse remote and local description", async () => {
|
||||
// @ts-ignore
|
||||
const mediaSsrcHandler = {
|
||||
parse: jest.fn(),
|
||||
ssrcToMid: jest.fn(),
|
||||
findMidBySsrc: jest.fn(),
|
||||
getSsrcToMidMap: jest.fn(),
|
||||
} as MediaSsrcHandler;
|
||||
|
||||
const remoteSDP = "sdp";
|
||||
const localSDP = "sdp";
|
||||
|
||||
// @ts-ignore
|
||||
rtcSpy.signalingState = "stable";
|
||||
|
||||
// @ts-ignore
|
||||
rtcSpy.currentRemoteDescription = <RTCSessionDescription>{ sdp: remoteSDP };
|
||||
// @ts-ignore
|
||||
rtcSpy.currentLocalDescription = <RTCSessionDescription>{ sdp: localSDP };
|
||||
|
||||
// @ts-ignore
|
||||
collector.trackStats.mediaSsrcHandler = mediaSsrcHandler;
|
||||
|
||||
events["signalingstatechange"]();
|
||||
expect(mediaSsrcHandler.parse).toHaveBeenCalledWith(remoteSDP, "remote");
|
||||
expect(mediaSsrcHandler.parse).toHaveBeenCalledWith(localSDP, "local");
|
||||
});
|
||||
});
|
||||
});
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||
|
||||
import { TrackID } from "../../../../src/webrtc/stats/statsReport";
|
||||
import { MediaTrackStats } from "../../../../src/webrtc/stats/media/mediaTrackStats";
|
||||
import { StatsReportBuilder } from "../../../../src/webrtc/stats/statsReportBuilder";
|
||||
import { ConnectionStatsReportBuilder } from "../../../../src/webrtc/stats/connectionStatsReportBuilder";
|
||||
|
||||
describe("StatsReportBuilder", () => {
|
||||
const LOCAL_VIDEO_TRACK_ID = "LOCAL_VIDEO_TRACK_ID";
|
||||
@ -39,7 +39,7 @@ describe("StatsReportBuilder", () => {
|
||||
|
||||
describe("should build stats", () => {
|
||||
it("by media track stats.", async () => {
|
||||
expect(StatsReportBuilder.build(stats)).toEqual({
|
||||
expect(ConnectionStatsReportBuilder.build(stats)).toEqual({
|
||||
bitrate: {
|
||||
audio: {
|
||||
download: 4000,
|
@ -13,7 +13,7 @@ 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 { ConnectionStatsReporter } from "../../../../src/webrtc/stats/connectionStatsReporter";
|
||||
import { ConnectionStatsBuilder } from "../../../../src/webrtc/stats/connectionStatsBuilder";
|
||||
|
||||
describe("ConnectionStatsReporter", () => {
|
||||
describe("should on bandwidth stats", () => {
|
||||
@ -22,11 +22,11 @@ describe("ConnectionStatsReporter", () => {
|
||||
availableIncomingBitrate: 1000,
|
||||
availableOutgoingBitrate: 2000,
|
||||
} as RTCIceCandidatePairStats;
|
||||
expect(ConnectionStatsReporter.buildBandwidthReport(stats)).toEqual({ download: 1, upload: 2 });
|
||||
expect(ConnectionStatsBuilder.buildBandwidthReport(stats)).toEqual({ download: 1, upload: 2 });
|
||||
});
|
||||
it("build empty bandwidth report if chromium starts attributes not available", () => {
|
||||
const stats = {} as RTCIceCandidatePairStats;
|
||||
expect(ConnectionStatsReporter.buildBandwidthReport(stats)).toEqual({ download: 0, upload: 0 });
|
||||
expect(ConnectionStatsBuilder.buildBandwidthReport(stats)).toEqual({ download: 0, upload: 0 });
|
||||
});
|
||||
});
|
||||
|
||||
@ -36,11 +36,11 @@ describe("ConnectionStatsReporter", () => {
|
||||
availableIncomingBitrate: 1000,
|
||||
availableOutgoingBitrate: 2000,
|
||||
} as RTCIceCandidatePairStats;
|
||||
expect(ConnectionStatsReporter.buildBandwidthReport(stats)).toEqual({ download: 1, upload: 2 });
|
||||
expect(ConnectionStatsBuilder.buildBandwidthReport(stats)).toEqual({ download: 1, upload: 2 });
|
||||
});
|
||||
it("build empty bandwidth report if chromium starts attributes not available", () => {
|
||||
const stats = {} as RTCIceCandidatePairStats;
|
||||
expect(ConnectionStatsReporter.buildBandwidthReport(stats)).toEqual({ download: 0, upload: 0 });
|
||||
expect(ConnectionStatsBuilder.buildBandwidthReport(stats)).toEqual({ download: 0, upload: 0 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
import { GroupCallStats } from "../../../../src/webrtc/stats/groupCallStats";
|
||||
import { SummaryStats } from "../../../../src/webrtc/stats/summaryStats";
|
||||
import { CallStatsReportSummary } from "../../../../src/webrtc/stats/callStatsReportSummary";
|
||||
|
||||
const GROUP_CALL_ID = "GROUP_ID";
|
||||
const LOCAL_USER_ID = "LOCAL_USER_ID";
|
||||
@ -112,7 +112,7 @@ describe("GroupCallStats", () => {
|
||||
concealedAudio: 0,
|
||||
totalAudio: 0,
|
||||
},
|
||||
} as SummaryStats;
|
||||
} as CallStatsReportSummary;
|
||||
let processStatsSpy;
|
||||
if (collector) {
|
||||
processStatsSpy = jest.spyOn(collector, "processStats").mockResolvedValue(summaryStats);
|
||||
|
@ -13,16 +13,16 @@ 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 { SummaryStatsReporter } from "../../../../src/webrtc/stats/summaryStatsReporter";
|
||||
import { SummaryStatsReportGatherer } from "../../../../src/webrtc/stats/summaryStatsReportGatherer";
|
||||
import { StatsReportEmitter } from "../../../../src/webrtc/stats/statsReportEmitter";
|
||||
|
||||
describe("SummaryStatsReporter", () => {
|
||||
let reporter: SummaryStatsReporter;
|
||||
describe("SummaryStatsReportGatherer", () => {
|
||||
let reporter: SummaryStatsReportGatherer;
|
||||
let emitter: StatsReportEmitter;
|
||||
beforeEach(() => {
|
||||
emitter = new StatsReportEmitter();
|
||||
emitter.emitSummaryStatsReport = jest.fn();
|
||||
reporter = new SummaryStatsReporter(emitter);
|
||||
reporter = new SummaryStatsReportGatherer(emitter);
|
||||
});
|
||||
|
||||
describe("build Summary Stats Report", () => {
|
@ -13,20 +13,20 @@ 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 { TrackStatsReporter } from "../../../../src/webrtc/stats/trackStatsReporter";
|
||||
import { TrackStatsBuilder } from "../../../../src/webrtc/stats/trackStatsBuilder";
|
||||
import { MediaTrackStats } from "../../../../src/webrtc/stats/media/mediaTrackStats";
|
||||
|
||||
describe("TrackStatsReporter", () => {
|
||||
describe("TrackStatsBuilder", () => {
|
||||
describe("should on frame and resolution stats", () => {
|
||||
it("creating empty frame and resolution report, if no data available.", async () => {
|
||||
const trackStats = new MediaTrackStats("1", "local", "video");
|
||||
TrackStatsReporter.buildFramerateResolution(trackStats, {});
|
||||
TrackStatsBuilder.buildFramerateResolution(trackStats, {});
|
||||
expect(trackStats.getFramerate()).toEqual(0);
|
||||
expect(trackStats.getResolution()).toEqual({ width: -1, height: -1 });
|
||||
});
|
||||
it("creating empty frame and resolution report.", async () => {
|
||||
const trackStats = new MediaTrackStats("1", "remote", "video");
|
||||
TrackStatsReporter.buildFramerateResolution(trackStats, {
|
||||
TrackStatsBuilder.buildFramerateResolution(trackStats, {
|
||||
framesPerSecond: 22.2,
|
||||
frameHeight: 180,
|
||||
frameWidth: 360,
|
||||
@ -39,7 +39,7 @@ describe("TrackStatsReporter", () => {
|
||||
describe("should on simulcast", () => {
|
||||
it("creating simulcast framerate.", async () => {
|
||||
const trackStats = new MediaTrackStats("1", "local", "video");
|
||||
TrackStatsReporter.calculateSimulcastFramerate(
|
||||
TrackStatsBuilder.calculateSimulcastFramerate(
|
||||
trackStats,
|
||||
{
|
||||
framesSent: 100,
|
||||
@ -58,7 +58,7 @@ describe("TrackStatsReporter", () => {
|
||||
describe("should on bytes received stats", () => {
|
||||
it("creating build bitrate received report.", async () => {
|
||||
const trackStats = new MediaTrackStats("1", "remote", "video");
|
||||
TrackStatsReporter.buildBitrateReceived(
|
||||
TrackStatsBuilder.buildBitrateReceived(
|
||||
trackStats,
|
||||
{
|
||||
bytesReceived: 2001000,
|
||||
@ -73,7 +73,7 @@ describe("TrackStatsReporter", () => {
|
||||
describe("should on bytes send stats", () => {
|
||||
it("creating build bitrate send report.", async () => {
|
||||
const trackStats = new MediaTrackStats("1", "local", "video");
|
||||
TrackStatsReporter.buildBitrateSend(
|
||||
TrackStatsBuilder.buildBitrateSend(
|
||||
trackStats,
|
||||
{
|
||||
bytesSent: 2001000,
|
||||
@ -90,7 +90,7 @@ describe("TrackStatsReporter", () => {
|
||||
const trackStats = new MediaTrackStats("1", "remote", "video");
|
||||
const remote = {} as RTCStatsReport;
|
||||
remote.get = jest.fn().mockReturnValue({ mimeType: "video/v8" });
|
||||
TrackStatsReporter.buildCodec(remote, trackStats, { codecId: "codecID" });
|
||||
TrackStatsBuilder.buildCodec(remote, trackStats, { codecId: "codecID" });
|
||||
expect(trackStats.getCodec()).toEqual("v8");
|
||||
});
|
||||
});
|
||||
@ -98,7 +98,7 @@ describe("TrackStatsReporter", () => {
|
||||
describe("should on package lost stats", () => {
|
||||
it("creating build package lost on send report.", async () => {
|
||||
const trackStats = new MediaTrackStats("1", "local", "video");
|
||||
TrackStatsReporter.buildPacketsLost(
|
||||
TrackStatsBuilder.buildPacketsLost(
|
||||
trackStats,
|
||||
{
|
||||
type: "outbound-rtp",
|
||||
@ -114,7 +114,7 @@ describe("TrackStatsReporter", () => {
|
||||
});
|
||||
it("creating build package lost on received report.", async () => {
|
||||
const trackStats = new MediaTrackStats("1", "remote", "video");
|
||||
TrackStatsReporter.buildPacketsLost(
|
||||
TrackStatsBuilder.buildPacketsLost(
|
||||
trackStats,
|
||||
{
|
||||
type: "inbound-rtp",
|
||||
@ -133,7 +133,7 @@ describe("TrackStatsReporter", () => {
|
||||
describe("should set state of a TrackStats", () => {
|
||||
it("to not alive if Transceiver undefined", async () => {
|
||||
const trackStats = new MediaTrackStats("1", "remote", "video");
|
||||
TrackStatsReporter.setTrackStatsState(trackStats, undefined);
|
||||
TrackStatsBuilder.setTrackStatsState(trackStats, undefined);
|
||||
expect(trackStats.alive).toBeFalsy();
|
||||
});
|
||||
|
||||
@ -145,7 +145,7 @@ describe("TrackStatsReporter", () => {
|
||||
} as RTCRtpSender,
|
||||
} as RTCRtpTransceiver;
|
||||
|
||||
TrackStatsReporter.setTrackStatsState(trackStats, ts);
|
||||
TrackStatsBuilder.setTrackStatsState(trackStats, ts);
|
||||
expect(trackStats.alive).toBeFalsy();
|
||||
});
|
||||
|
||||
@ -162,7 +162,7 @@ describe("TrackStatsReporter", () => {
|
||||
} as RTCRtpReceiver,
|
||||
} as RTCRtpTransceiver;
|
||||
|
||||
TrackStatsReporter.setTrackStatsState(trackStats, ts);
|
||||
TrackStatsBuilder.setTrackStatsState(trackStats, ts);
|
||||
expect(trackStats.alive).toBeTruthy();
|
||||
});
|
||||
|
||||
@ -179,7 +179,7 @@ describe("TrackStatsReporter", () => {
|
||||
} as RTCRtpSender,
|
||||
} as RTCRtpTransceiver;
|
||||
|
||||
TrackStatsReporter.setTrackStatsState(trackStats, ts);
|
||||
TrackStatsBuilder.setTrackStatsState(trackStats, ts);
|
||||
expect(trackStats.alive).toBeTruthy();
|
||||
});
|
||||
|
||||
@ -195,7 +195,7 @@ describe("TrackStatsReporter", () => {
|
||||
} as RTCRtpReceiver,
|
||||
} as RTCRtpTransceiver;
|
||||
|
||||
TrackStatsReporter.setTrackStatsState(trackStats, ts);
|
||||
TrackStatsBuilder.setTrackStatsState(trackStats, ts);
|
||||
expect(trackStats.alive).toBeFalsy();
|
||||
});
|
||||
|
||||
@ -211,7 +211,7 @@ describe("TrackStatsReporter", () => {
|
||||
} as RTCRtpReceiver,
|
||||
} as RTCRtpTransceiver;
|
||||
|
||||
TrackStatsReporter.setTrackStatsState(trackStats, ts);
|
||||
TrackStatsBuilder.setTrackStatsState(trackStats, ts);
|
||||
expect(trackStats.alive).toBeTruthy();
|
||||
expect(trackStats.muted).toBeTruthy();
|
||||
});
|
||||
@ -219,7 +219,7 @@ describe("TrackStatsReporter", () => {
|
||||
|
||||
describe("should build Track Summary", () => {
|
||||
it("and returns empty summary if stats list empty", async () => {
|
||||
const summary = TrackStatsReporter.buildTrackSummary([]);
|
||||
const summary = TrackStatsBuilder.buildTrackSummary([]);
|
||||
expect(summary).toEqual({
|
||||
audioTrackSummary: {
|
||||
count: 0,
|
||||
@ -242,7 +242,7 @@ describe("TrackStatsReporter", () => {
|
||||
|
||||
it("and returns summary if stats list not empty and ignore local summery", async () => {
|
||||
const trackStatsList = buildMockTrackStatsList();
|
||||
const summary = TrackStatsReporter.buildTrackSummary(trackStatsList);
|
||||
const summary = TrackStatsBuilder.buildTrackSummary(trackStatsList);
|
||||
expect(summary).toEqual({
|
||||
audioTrackSummary: {
|
||||
count: 2,
|
||||
@ -267,7 +267,7 @@ describe("TrackStatsReporter", () => {
|
||||
const trackStatsList = buildMockTrackStatsList();
|
||||
trackStatsList[1].muted = true;
|
||||
trackStatsList[5].muted = true;
|
||||
const summary = TrackStatsReporter.buildTrackSummary(trackStatsList);
|
||||
const summary = TrackStatsBuilder.buildTrackSummary(trackStatsList);
|
||||
expect(summary).toEqual({
|
||||
audioTrackSummary: {
|
||||
count: 2,
|
||||
@ -292,7 +292,7 @@ describe("TrackStatsReporter", () => {
|
||||
const trackStatsList = buildMockTrackStatsList();
|
||||
trackStatsList[1].muted = true;
|
||||
trackStatsList[1].alive = false;
|
||||
const summary = TrackStatsReporter.buildTrackSummary(trackStatsList);
|
||||
const summary = TrackStatsBuilder.buildTrackSummary(trackStatsList);
|
||||
expect(summary).toEqual({
|
||||
audioTrackSummary: {
|
||||
count: 2,
|
||||
@ -330,7 +330,7 @@ describe("TrackStatsReporter", () => {
|
||||
trackStatsList[2].setAudioConcealment(220, 2000);
|
||||
trackStatsList[5].setAudioConcealment(180, 2000);
|
||||
|
||||
const summary = TrackStatsReporter.buildTrackSummary(trackStatsList);
|
||||
const summary = TrackStatsBuilder.buildTrackSummary(trackStatsList);
|
||||
expect(summary).toEqual({
|
||||
audioTrackSummary: {
|
||||
count: 2,
|
||||
@ -355,25 +355,25 @@ describe("TrackStatsReporter", () => {
|
||||
describe("should build jitter value in Track Stats", () => {
|
||||
it("and returns track stats without jitter if report not 'inbound-rtp'", async () => {
|
||||
const trackStats = new MediaTrackStats("1", "remote", "video");
|
||||
TrackStatsReporter.buildJitter(trackStats, { jitter: 0.01 });
|
||||
TrackStatsBuilder.buildJitter(trackStats, { jitter: 0.01 });
|
||||
expect(trackStats.getJitter()).toEqual(0);
|
||||
});
|
||||
|
||||
it("and returns track stats with jitter", async () => {
|
||||
const trackStats = new MediaTrackStats("1", "remote", "video");
|
||||
TrackStatsReporter.buildJitter(trackStats, { type: "inbound-rtp", jitter: 0.01 });
|
||||
TrackStatsBuilder.buildJitter(trackStats, { type: "inbound-rtp", jitter: 0.01 });
|
||||
expect(trackStats.getJitter()).toEqual(10);
|
||||
});
|
||||
|
||||
it("and returns negative jitter if stats has no jitter value", async () => {
|
||||
const trackStats = new MediaTrackStats("1", "remote", "video");
|
||||
TrackStatsReporter.buildJitter(trackStats, { type: "inbound-rtp" });
|
||||
TrackStatsBuilder.buildJitter(trackStats, { type: "inbound-rtp" });
|
||||
expect(trackStats.getJitter()).toEqual(-1);
|
||||
});
|
||||
|
||||
it("and returns jitter as number", async () => {
|
||||
const trackStats = new MediaTrackStats("1", "remote", "video");
|
||||
TrackStatsReporter.buildJitter(trackStats, { type: "inbound-rtp", jitter: "0.5" });
|
||||
TrackStatsBuilder.buildJitter(trackStats, { type: "inbound-rtp", jitter: "0.5" });
|
||||
expect(trackStats.getJitter()).toEqual(500);
|
||||
});
|
||||
});
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { TransportStatsReporter } from "../../../../src/webrtc/stats/transportStatsReporter";
|
||||
import { TransportStatsBuilder } from "../../../../src/webrtc/stats/transportStatsBuilder";
|
||||
import { TransportStats } from "../../../../src/webrtc/stats/transportStats";
|
||||
|
||||
describe("TransportStatsReporter", () => {
|
||||
@ -35,7 +35,7 @@ describe("TransportStatsReporter", () => {
|
||||
it("build new transport stats if all properties there", () => {
|
||||
const { report, stats } = mockStatsReport(isFocus, 0);
|
||||
const conferenceStatsTransport: TransportStats[] = [];
|
||||
const transportStats = TransportStatsReporter.buildReport(report, stats, conferenceStatsTransport, isFocus);
|
||||
const transportStats = TransportStatsBuilder.buildReport(report, stats, conferenceStatsTransport, isFocus);
|
||||
expect(transportStats).toEqual([
|
||||
{
|
||||
ip: `${remoteIC.ip + 0}:${remoteIC.port}`,
|
||||
@ -54,8 +54,8 @@ describe("TransportStatsReporter", () => {
|
||||
const mock1 = mockStatsReport(isFocus, 0);
|
||||
const mock2 = mockStatsReport(isFocus, 1);
|
||||
let transportStats: TransportStats[] = [];
|
||||
transportStats = TransportStatsReporter.buildReport(mock1.report, mock1.stats, transportStats, isFocus);
|
||||
transportStats = TransportStatsReporter.buildReport(mock2.report, mock2.stats, transportStats, isFocus);
|
||||
transportStats = TransportStatsBuilder.buildReport(mock1.report, mock1.stats, transportStats, isFocus);
|
||||
transportStats = TransportStatsBuilder.buildReport(mock2.report, mock2.stats, transportStats, isFocus);
|
||||
expect(transportStats).toEqual([
|
||||
{
|
||||
ip: `${remoteIC.ip + 0}:${remoteIC.port}`,
|
||||
@ -84,8 +84,8 @@ describe("TransportStatsReporter", () => {
|
||||
const mock1 = mockStatsReport(isFocus, 0);
|
||||
const mock2 = mockStatsReport(isFocus, 0);
|
||||
let transportStats: TransportStats[] = [];
|
||||
transportStats = TransportStatsReporter.buildReport(mock1.report, mock1.stats, transportStats, isFocus);
|
||||
transportStats = TransportStatsReporter.buildReport(mock2.report, mock2.stats, transportStats, isFocus);
|
||||
transportStats = TransportStatsBuilder.buildReport(mock1.report, mock1.stats, transportStats, isFocus);
|
||||
transportStats = TransportStatsBuilder.buildReport(mock2.report, mock2.stats, transportStats, isFocus);
|
||||
expect(transportStats).toEqual([
|
||||
{
|
||||
ip: `${remoteIC.ip + 0}:${remoteIC.port}`,
|
@ -13,16 +13,16 @@ 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 { StatsValueFormatter } from "../../../../src/webrtc/stats/statsValueFormatter";
|
||||
import { ValueFormatter } from "../../../../src/webrtc/stats/valueFormatter";
|
||||
|
||||
describe("StatsValueFormatter", () => {
|
||||
describe("ValueFormatter", () => {
|
||||
describe("on get non negative values", () => {
|
||||
it("formatter shod return number", async () => {
|
||||
expect(StatsValueFormatter.getNonNegativeValue("2")).toEqual(2);
|
||||
expect(StatsValueFormatter.getNonNegativeValue(0)).toEqual(0);
|
||||
expect(StatsValueFormatter.getNonNegativeValue("-2")).toEqual(0);
|
||||
expect(StatsValueFormatter.getNonNegativeValue("")).toEqual(0);
|
||||
expect(StatsValueFormatter.getNonNegativeValue(NaN)).toEqual(0);
|
||||
expect(ValueFormatter.getNonNegativeValue("2")).toEqual(2);
|
||||
expect(ValueFormatter.getNonNegativeValue(0)).toEqual(0);
|
||||
expect(ValueFormatter.getNonNegativeValue("-2")).toEqual(0);
|
||||
expect(ValueFormatter.getNonNegativeValue("")).toEqual(0);
|
||||
expect(ValueFormatter.getNonNegativeValue(NaN)).toEqual(0);
|
||||
});
|
||||
});
|
||||
});
|
@ -17,17 +17,17 @@ limitations under the License.
|
||||
import { ConnectionStats } from "./connectionStats";
|
||||
import { StatsReportEmitter } from "./statsReportEmitter";
|
||||
import { ByteSend, ByteSentStatsReport, TrackID } from "./statsReport";
|
||||
import { ConnectionStatsReporter } from "./connectionStatsReporter";
|
||||
import { TransportStatsReporter } from "./transportStatsReporter";
|
||||
import { ConnectionStatsBuilder } from "./connectionStatsBuilder";
|
||||
import { TransportStatsBuilder } from "./transportStatsBuilder";
|
||||
import { MediaSsrcHandler } from "./media/mediaSsrcHandler";
|
||||
import { MediaTrackHandler } from "./media/mediaTrackHandler";
|
||||
import { MediaTrackStatsHandler } from "./media/mediaTrackStatsHandler";
|
||||
import { TrackStatsReporter } from "./trackStatsReporter";
|
||||
import { StatsReportBuilder } from "./statsReportBuilder";
|
||||
import { StatsValueFormatter } from "./statsValueFormatter";
|
||||
import { SummaryStats } from "./summaryStats";
|
||||
import { TrackStatsBuilder } from "./trackStatsBuilder";
|
||||
import { ConnectionStatsReportBuilder } from "./connectionStatsReportBuilder";
|
||||
import { ValueFormatter } from "./valueFormatter";
|
||||
import { CallStatsReportSummary } from "./callStatsReportSummary";
|
||||
|
||||
export class StatsReportGatherer {
|
||||
export class CallStatsReportGatherer {
|
||||
private isActive = true;
|
||||
private previousStatsReport: RTCStatsReport | undefined;
|
||||
private currentStatsReport: RTCStatsReport | undefined;
|
||||
@ -46,7 +46,7 @@ export class StatsReportGatherer {
|
||||
this.trackStats = new MediaTrackStatsHandler(new MediaSsrcHandler(), new MediaTrackHandler(pc));
|
||||
}
|
||||
|
||||
public async processStats(groupCallId: string, localUserId: string): Promise<SummaryStats> {
|
||||
public async processStats(groupCallId: string, localUserId: string): Promise<CallStatsReportSummary> {
|
||||
const summary = {
|
||||
isFirstCollection: this.previousStatsReport === undefined,
|
||||
receivedMedia: 0,
|
||||
@ -54,7 +54,7 @@ export class StatsReportGatherer {
|
||||
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 },
|
||||
} as SummaryStats;
|
||||
} as CallStatsReportSummary;
|
||||
if (this.isActive) {
|
||||
const statsPromise = this.pc.getStats();
|
||||
if (typeof statsPromise?.then === "function") {
|
||||
@ -73,7 +73,7 @@ export class StatsReportGatherer {
|
||||
summary.receivedMedia = this.connectionStats.bitrate.download;
|
||||
summary.receivedAudioMedia = this.connectionStats.bitrate.audio?.download || 0;
|
||||
summary.receivedVideoMedia = this.connectionStats.bitrate.video?.download || 0;
|
||||
const trackSummary = TrackStatsReporter.buildTrackSummary(
|
||||
const trackSummary = TrackStatsBuilder.buildTrackSummary(
|
||||
Array.from(this.trackStats.getTrack2stats().values()),
|
||||
);
|
||||
return {
|
||||
@ -93,14 +93,14 @@ export class StatsReportGatherer {
|
||||
}
|
||||
|
||||
private processStatsReport(groupCallId: string, localUserId: string): void {
|
||||
const byteSentStats: ByteSentStatsReport = new Map<TrackID, ByteSend>();
|
||||
const byteSentStatsReport: ByteSentStatsReport = new Map<TrackID, ByteSend>();
|
||||
|
||||
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 = ConnectionStatsReporter.buildBandwidthReport(now);
|
||||
this.connectionStats.transport = TransportStatsReporter.buildReport(
|
||||
this.connectionStats.bandwidth = ConnectionStatsBuilder.buildBandwidthReport(now);
|
||||
this.connectionStats.transport = TransportStatsBuilder.buildReport(
|
||||
this.currentStatsReport,
|
||||
now,
|
||||
this.connectionStats.transport,
|
||||
@ -121,7 +121,7 @@ export class StatsReportGatherer {
|
||||
}
|
||||
|
||||
if (before) {
|
||||
TrackStatsReporter.buildPacketsLost(trackStats, now, before);
|
||||
TrackStatsBuilder.buildPacketsLost(trackStats, now, before);
|
||||
}
|
||||
|
||||
// Get the resolution and framerate for only remote video sources here. For the local video sources,
|
||||
@ -130,26 +130,26 @@ export class StatsReportGatherer {
|
||||
// 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") {
|
||||
TrackStatsReporter.buildFramerateResolution(trackStats, now);
|
||||
TrackStatsBuilder.buildFramerateResolution(trackStats, now);
|
||||
if (before) {
|
||||
TrackStatsReporter.buildBitrateReceived(trackStats, now, before);
|
||||
TrackStatsBuilder.buildBitrateReceived(trackStats, now, before);
|
||||
}
|
||||
const ts = this.trackStats.findTransceiverByTrackId(trackStats.trackId);
|
||||
TrackStatsReporter.setTrackStatsState(trackStats, ts);
|
||||
TrackStatsReporter.buildJitter(trackStats, now);
|
||||
TrackStatsReporter.buildAudioConcealment(trackStats, now);
|
||||
TrackStatsBuilder.setTrackStatsState(trackStats, ts);
|
||||
TrackStatsBuilder.buildJitter(trackStats, now);
|
||||
TrackStatsBuilder.buildAudioConcealment(trackStats, now);
|
||||
} else if (before) {
|
||||
byteSentStats.set(trackStats.trackId, StatsValueFormatter.getNonNegativeValue(now.bytesSent));
|
||||
TrackStatsReporter.buildBitrateSend(trackStats, now, before);
|
||||
byteSentStatsReport.set(trackStats.trackId, ValueFormatter.getNonNegativeValue(now.bytesSent));
|
||||
TrackStatsBuilder.buildBitrateSend(trackStats, now, before);
|
||||
}
|
||||
TrackStatsReporter.buildCodec(this.currentStatsReport, trackStats, now);
|
||||
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;
|
||||
}
|
||||
TrackStatsReporter.buildFramerateResolution(trackStats, now);
|
||||
TrackStatsReporter.calculateSimulcastFramerate(
|
||||
TrackStatsBuilder.buildFramerateResolution(trackStats, now);
|
||||
TrackStatsBuilder.calculateSimulcastFramerate(
|
||||
trackStats,
|
||||
now,
|
||||
before,
|
||||
@ -158,8 +158,8 @@ export class StatsReportGatherer {
|
||||
}
|
||||
});
|
||||
|
||||
this.emitter.emitByteSendReport(byteSentStats);
|
||||
this.processAndEmitReport();
|
||||
this.emitter.emitByteSendReport(byteSentStatsReport);
|
||||
this.processAndEmitConnectionStatsReport();
|
||||
}
|
||||
|
||||
public setActive(isActive: boolean): void {
|
||||
@ -174,8 +174,8 @@ export class StatsReportGatherer {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
private processAndEmitReport(): void {
|
||||
const report = StatsReportBuilder.build(this.trackStats.getTrack2stats());
|
||||
private processAndEmitConnectionStatsReport(): void {
|
||||
const report = ConnectionStatsReportBuilder.build(this.trackStats.getTrack2stats());
|
||||
|
||||
this.connectionStats.bandwidth = report.bandwidth;
|
||||
this.connectionStats.bitrate = report.bitrate;
|
@ -10,7 +10,7 @@ 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.
|
||||
*/
|
||||
export interface SummaryStats {
|
||||
export interface CallStatsReportSummary {
|
||||
receivedMedia: number;
|
||||
receivedAudioMedia: number;
|
||||
receivedVideoMedia: number;
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
import { Bitrate } from "./media/mediaTrackStats";
|
||||
|
||||
export class ConnectionStatsReporter {
|
||||
export class ConnectionStatsBuilder {
|
||||
public static buildBandwidthReport(now: RTCIceCandidatePairStats): Bitrate {
|
||||
const availableIncomingBitrate = now.availableIncomingBitrate;
|
||||
const availableOutgoingBitrate = now.availableOutgoingBitrate;
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||
import { AudioConcealment, CodecMap, ConnectionStatsReport, FramerateMap, ResolutionMap, TrackID } from "./statsReport";
|
||||
import { MediaTrackStats, Resolution } from "./media/mediaTrackStats";
|
||||
|
||||
export class StatsReportBuilder {
|
||||
export class ConnectionStatsReportBuilder {
|
||||
public static build(stats: Map<TrackID, MediaTrackStats>): ConnectionStatsReport {
|
||||
const report = {} as ConnectionStatsReport;
|
||||
|
||||
@ -103,12 +103,12 @@ export class StatsReportBuilder {
|
||||
};
|
||||
|
||||
report.packetLoss = {
|
||||
total: StatsReportBuilder.calculatePacketLoss(
|
||||
total: ConnectionStatsReportBuilder.calculatePacketLoss(
|
||||
lostPackets.download + lostPackets.upload,
|
||||
totalPackets.download + totalPackets.upload,
|
||||
),
|
||||
download: StatsReportBuilder.calculatePacketLoss(lostPackets.download, totalPackets.download),
|
||||
upload: StatsReportBuilder.calculatePacketLoss(lostPackets.upload, totalPackets.upload),
|
||||
download: ConnectionStatsReportBuilder.calculatePacketLoss(lostPackets.download, totalPackets.download),
|
||||
upload: ConnectionStatsReportBuilder.calculatePacketLoss(lostPackets.upload, totalPackets.upload),
|
||||
};
|
||||
report.audioConcealment = audioConcealment;
|
||||
report.totalAudioConcealment = {
|
@ -13,16 +13,16 @@ 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 { StatsReportGatherer } from "./statsReportGatherer";
|
||||
import { CallStatsReportGatherer } from "./callStatsReportGatherer";
|
||||
import { StatsReportEmitter } from "./statsReportEmitter";
|
||||
import { SummaryStats } from "./summaryStats";
|
||||
import { SummaryStatsReporter } from "./summaryStatsReporter";
|
||||
import { CallStatsReportSummary } from "./callStatsReportSummary";
|
||||
import { SummaryStatsReportGatherer } from "./summaryStatsReportGatherer";
|
||||
|
||||
export class GroupCallStats {
|
||||
private timer: undefined | ReturnType<typeof setTimeout>;
|
||||
private readonly gatherers: Map<string, StatsReportGatherer> = new Map<string, StatsReportGatherer>();
|
||||
private readonly gatherers: Map<string, CallStatsReportGatherer> = new Map<string, CallStatsReportGatherer>();
|
||||
public readonly reports = new StatsReportEmitter();
|
||||
private readonly summaryStatsReporter = new SummaryStatsReporter(this.reports);
|
||||
private readonly summaryStatsReportGatherer = new SummaryStatsReportGatherer(this.reports);
|
||||
|
||||
public constructor(private groupCallId: string, private userId: string, private interval: number = 10000) {}
|
||||
|
||||
@ -49,7 +49,7 @@ export class GroupCallStats {
|
||||
if (this.hasStatsReportGatherer(callId)) {
|
||||
return false;
|
||||
}
|
||||
this.gatherers.set(callId, new StatsReportGatherer(callId, userId, peerConnection, this.reports));
|
||||
this.gatherers.set(callId, new CallStatsReportGatherer(callId, userId, peerConnection, this.reports));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -57,17 +57,17 @@ export class GroupCallStats {
|
||||
return this.gatherers.delete(callId);
|
||||
}
|
||||
|
||||
public getStatsReportGatherer(callId: string): StatsReportGatherer | undefined {
|
||||
public getStatsReportGatherer(callId: string): CallStatsReportGatherer | undefined {
|
||||
return this.hasStatsReportGatherer(callId) ? this.gatherers.get(callId) : undefined;
|
||||
}
|
||||
|
||||
private processStats(): void {
|
||||
const summary: Promise<SummaryStats>[] = [];
|
||||
const summary: Promise<CallStatsReportSummary>[] = [];
|
||||
this.gatherers.forEach((c) => {
|
||||
summary.push(c.processStats(this.groupCallId, this.userId));
|
||||
});
|
||||
|
||||
Promise.all(summary).then((s: Awaited<SummaryStats>[]) => this.summaryStatsReporter.build(s));
|
||||
Promise.all(summary).then((s: Awaited<CallStatsReportSummary>[]) => this.summaryStatsReportGatherer.build(s));
|
||||
}
|
||||
|
||||
public setInterval(interval: number): void {
|
||||
|
@ -11,10 +11,10 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
import { StatsReportEmitter } from "./statsReportEmitter";
|
||||
import { SummaryStats } from "./summaryStats";
|
||||
import { CallStatsReportSummary } from "./callStatsReportSummary";
|
||||
import { SummaryStatsReport } from "./statsReport";
|
||||
|
||||
interface SummaryCounter {
|
||||
interface CallStatsReportSummaryCounter {
|
||||
receivedAudio: number;
|
||||
receivedVideo: number;
|
||||
receivedMedia: number;
|
||||
@ -22,10 +22,10 @@ interface SummaryCounter {
|
||||
totalAudio: number;
|
||||
}
|
||||
|
||||
export class SummaryStatsReporter {
|
||||
export class SummaryStatsReportGatherer {
|
||||
public constructor(private emitter: StatsReportEmitter) {}
|
||||
|
||||
public build(allSummary: SummaryStats[]): void {
|
||||
public build(allSummary: CallStatsReportSummary[]): void {
|
||||
// Filter all stats which collect the first time webrtc stats.
|
||||
// Because stats based on time interval and the first collection of a summery stats has no previous
|
||||
// webrtcStats as basement all the calculation are 0. We don't want track the 0 stats.
|
||||
@ -34,7 +34,7 @@ export class SummaryStatsReporter {
|
||||
if (summaryTotalCount === 0) {
|
||||
return;
|
||||
}
|
||||
const summaryCounter: SummaryCounter = {
|
||||
const summaryCounter: CallStatsReportSummaryCounter = {
|
||||
receivedAudio: 0,
|
||||
receivedVideo: 0,
|
||||
receivedMedia: 0,
|
||||
@ -70,7 +70,7 @@ export class SummaryStatsReporter {
|
||||
this.emitter.emitSummaryStatsReport(report);
|
||||
}
|
||||
|
||||
private countTrackListReceivedMedia(counter: SummaryCounter, stats: SummaryStats): void {
|
||||
private countTrackListReceivedMedia(counter: CallStatsReportSummaryCounter, stats: CallStatsReportSummary): void {
|
||||
let hasReceivedAudio = false;
|
||||
let hasReceivedVideo = false;
|
||||
if (stats.receivedAudioMedia > 0 || stats.audioTrackSummary.count === 0) {
|
||||
@ -92,7 +92,7 @@ export class SummaryStatsReporter {
|
||||
}
|
||||
}
|
||||
|
||||
private buildMaxJitter(maxJitter: number, stats: SummaryStats): number {
|
||||
private buildMaxJitter(maxJitter: number, stats: CallStatsReportSummary): number {
|
||||
if (maxJitter < stats.videoTrackSummary.maxJitter) {
|
||||
maxJitter = stats.videoTrackSummary.maxJitter;
|
||||
}
|
||||
@ -103,7 +103,7 @@ export class SummaryStatsReporter {
|
||||
return maxJitter;
|
||||
}
|
||||
|
||||
private buildMaxPacketLoss(maxPacketLoss: number, stats: SummaryStats): number {
|
||||
private buildMaxPacketLoss(maxPacketLoss: number, stats: CallStatsReportSummary): number {
|
||||
if (maxPacketLoss < stats.videoTrackSummary.maxPacketLoss) {
|
||||
maxPacketLoss = stats.videoTrackSummary.maxPacketLoss;
|
||||
}
|
||||
@ -114,7 +114,7 @@ export class SummaryStatsReporter {
|
||||
return maxPacketLoss;
|
||||
}
|
||||
|
||||
private countConcealedAudio(summaryCounter: SummaryCounter, stats: SummaryStats): void {
|
||||
private countConcealedAudio(summaryCounter: CallStatsReportSummaryCounter, stats: CallStatsReportSummary): void {
|
||||
summaryCounter.concealedAudio += stats.audioTrackSummary.concealedAudio;
|
||||
summaryCounter.totalAudio += stats.audioTrackSummary.totalAudio;
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
import { MediaTrackStats } from "./media/mediaTrackStats";
|
||||
import { StatsValueFormatter } from "./statsValueFormatter";
|
||||
import { TrackSummary } from "./summaryStats";
|
||||
import { ValueFormatter } from "./valueFormatter";
|
||||
import { TrackSummary } from "./callStatsReportSummary";
|
||||
|
||||
export class TrackStatsReporter {
|
||||
export class TrackStatsBuilder {
|
||||
public static buildFramerateResolution(trackStats: MediaTrackStats, now: any): void {
|
||||
const resolution = {
|
||||
height: now.frameHeight,
|
||||
@ -56,7 +56,7 @@ export class TrackStatsReporter {
|
||||
|
||||
public static buildBitrateReceived(trackStats: MediaTrackStats, now: any, before: any): void {
|
||||
trackStats.setBitrate({
|
||||
download: TrackStatsReporter.calculateBitrate(
|
||||
download: TrackStatsBuilder.calculateBitrate(
|
||||
now.bytesReceived,
|
||||
before.bytesReceived,
|
||||
now.timestamp,
|
||||
@ -81,11 +81,11 @@ export class TrackStatsReporter {
|
||||
packetsNow = 0;
|
||||
}
|
||||
|
||||
const packetsBefore = StatsValueFormatter.getNonNegativeValue(before[key]);
|
||||
const packetsBefore = ValueFormatter.getNonNegativeValue(before[key]);
|
||||
const packetsDiff = Math.max(0, packetsNow - packetsBefore);
|
||||
|
||||
const packetsLostNow = StatsValueFormatter.getNonNegativeValue(now.packetsLost);
|
||||
const packetsLostBefore = StatsValueFormatter.getNonNegativeValue(before.packetsLost);
|
||||
const packetsLostNow = ValueFormatter.getNonNegativeValue(now.packetsLost);
|
||||
const packetsLostBefore = ValueFormatter.getNonNegativeValue(before.packetsLost);
|
||||
const packetsLostDiff = Math.max(0, packetsLostNow - packetsLostBefore);
|
||||
|
||||
trackStats.setLoss({
|
||||
@ -101,8 +101,8 @@ export class TrackStatsReporter {
|
||||
nowTimestamp: number,
|
||||
beforeTimestamp: number,
|
||||
): number {
|
||||
const bytesNow = StatsValueFormatter.getNonNegativeValue(bytesNowAny);
|
||||
const bytesBefore = StatsValueFormatter.getNonNegativeValue(bytesBeforeAny);
|
||||
const bytesNow = ValueFormatter.getNonNegativeValue(bytesNowAny);
|
||||
const bytesBefore = ValueFormatter.getNonNegativeValue(bytesBeforeAny);
|
||||
const bytesProcessed = Math.max(0, bytesNow - bytesBefore);
|
||||
|
||||
const timeMs = nowTimestamp - beforeTimestamp;
|
||||
@ -188,7 +188,7 @@ export class TrackStatsReporter {
|
||||
|
||||
const jitterStr = statsReport?.jitter;
|
||||
if (jitterStr !== undefined) {
|
||||
const jitter = StatsValueFormatter.getNonNegativeValue(jitterStr);
|
||||
const jitter = ValueFormatter.getNonNegativeValue(jitterStr);
|
||||
trackStats.setJitter(Math.round(jitter * 1000));
|
||||
} else {
|
||||
trackStats.setJitter(-1);
|
@ -1,6 +1,6 @@
|
||||
import { TransportStats } from "./transportStats";
|
||||
|
||||
export class TransportStatsReporter {
|
||||
export class TransportStatsBuilder {
|
||||
public static buildReport(
|
||||
report: RTCStatsReport | undefined,
|
||||
now: RTCIceCandidatePairStats,
|
@ -10,7 +10,7 @@ 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.
|
||||
*/
|
||||
export class StatsValueFormatter {
|
||||
export class ValueFormatter {
|
||||
public static getNonNegativeValue(imput: any): number {
|
||||
let value = imput;
|
||||
|
Reference in New Issue
Block a user