1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-08-06 12:02:40 +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:
Enrico Schwendig
2023-05-26 09:56:49 +02:00
committed by GitHub
parent b5414ea914
commit 7ade461a4c
17 changed files with 160 additions and 119 deletions

View File

@@ -14,21 +14,22 @@ See the License for the specific language governing permissions and
limitations under the License. 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 { StatsReportEmitter } from "../../../../src/webrtc/stats/statsReportEmitter";
import { MediaSsrcHandler } from "../../../../src/webrtc/stats/media/mediaSsrcHandler";
const CALL_ID = "CALL_ID"; const CALL_ID = "CALL_ID";
const USER_ID = "USER_ID"; const USER_ID = "USER_ID";
describe("StatsReportGatherer", () => { describe("CallStatsReportGatherer", () => {
let collector: StatsReportGatherer; let collector: CallStatsReportGatherer;
let rtcSpy: RTCPeerConnection; let rtcSpy: RTCPeerConnection;
let emitter: StatsReportEmitter; let emitter: StatsReportEmitter;
beforeEach(() => { beforeEach(() => {
rtcSpy = { getStats: () => new Promise<RTCStatsReport>(() => null) } as RTCPeerConnection; rtcSpy = { getStats: () => new Promise<RTCStatsReport>(() => null) } as RTCPeerConnection;
rtcSpy.addEventListener = jest.fn(); rtcSpy.addEventListener = jest.fn();
emitter = new StatsReportEmitter(); 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", () => { describe("on process stats", () => {
@@ -145,4 +146,44 @@ describe("StatsReportGatherer", () => {
expect(collector.getActive()).toBeTruthy(); 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");
});
});
}); });

View File

@@ -16,7 +16,7 @@ limitations under the License.
import { TrackID } from "../../../../src/webrtc/stats/statsReport"; import { TrackID } from "../../../../src/webrtc/stats/statsReport";
import { MediaTrackStats } from "../../../../src/webrtc/stats/media/mediaTrackStats"; import { MediaTrackStats } from "../../../../src/webrtc/stats/media/mediaTrackStats";
import { StatsReportBuilder } from "../../../../src/webrtc/stats/statsReportBuilder"; import { ConnectionStatsReportBuilder } from "../../../../src/webrtc/stats/connectionStatsReportBuilder";
describe("StatsReportBuilder", () => { describe("StatsReportBuilder", () => {
const LOCAL_VIDEO_TRACK_ID = "LOCAL_VIDEO_TRACK_ID"; const LOCAL_VIDEO_TRACK_ID = "LOCAL_VIDEO_TRACK_ID";
@@ -39,7 +39,7 @@ describe("StatsReportBuilder", () => {
describe("should build stats", () => { describe("should build stats", () => {
it("by media track stats.", async () => { it("by media track stats.", async () => {
expect(StatsReportBuilder.build(stats)).toEqual({ expect(ConnectionStatsReportBuilder.build(stats)).toEqual({
bitrate: { bitrate: {
audio: { audio: {
download: 4000, download: 4000,

View File

@@ -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 See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { ConnectionStatsReporter } from "../../../../src/webrtc/stats/connectionStatsReporter"; import { ConnectionStatsBuilder } from "../../../../src/webrtc/stats/connectionStatsBuilder";
describe("ConnectionStatsReporter", () => { describe("ConnectionStatsReporter", () => {
describe("should on bandwidth stats", () => { describe("should on bandwidth stats", () => {
@@ -22,11 +22,11 @@ describe("ConnectionStatsReporter", () => {
availableIncomingBitrate: 1000, availableIncomingBitrate: 1000,
availableOutgoingBitrate: 2000, availableOutgoingBitrate: 2000,
} as RTCIceCandidatePairStats; } 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", () => { it("build empty bandwidth report if chromium starts attributes not available", () => {
const stats = {} as RTCIceCandidatePairStats; 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, availableIncomingBitrate: 1000,
availableOutgoingBitrate: 2000, availableOutgoingBitrate: 2000,
} as RTCIceCandidatePairStats; } 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", () => { it("build empty bandwidth report if chromium starts attributes not available", () => {
const stats = {} as RTCIceCandidatePairStats; const stats = {} as RTCIceCandidatePairStats;
expect(ConnectionStatsReporter.buildBandwidthReport(stats)).toEqual({ download: 0, upload: 0 }); expect(ConnectionStatsBuilder.buildBandwidthReport(stats)).toEqual({ download: 0, upload: 0 });
}); });
}); });
}); });

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { GroupCallStats } from "../../../../src/webrtc/stats/groupCallStats"; 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 GROUP_CALL_ID = "GROUP_ID";
const LOCAL_USER_ID = "LOCAL_USER_ID"; const LOCAL_USER_ID = "LOCAL_USER_ID";
@@ -112,7 +112,7 @@ describe("GroupCallStats", () => {
concealedAudio: 0, concealedAudio: 0,
totalAudio: 0, totalAudio: 0,
}, },
} as SummaryStats; } as CallStatsReportSummary;
let processStatsSpy; let processStatsSpy;
if (collector) { if (collector) {
processStatsSpy = jest.spyOn(collector, "processStats").mockResolvedValue(summaryStats); processStatsSpy = jest.spyOn(collector, "processStats").mockResolvedValue(summaryStats);

View File

@@ -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 See the License for the specific language governing permissions and
limitations under the License. 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"; import { StatsReportEmitter } from "../../../../src/webrtc/stats/statsReportEmitter";
describe("SummaryStatsReporter", () => { describe("SummaryStatsReportGatherer", () => {
let reporter: SummaryStatsReporter; let reporter: SummaryStatsReportGatherer;
let emitter: StatsReportEmitter; let emitter: StatsReportEmitter;
beforeEach(() => { beforeEach(() => {
emitter = new StatsReportEmitter(); emitter = new StatsReportEmitter();
emitter.emitSummaryStatsReport = jest.fn(); emitter.emitSummaryStatsReport = jest.fn();
reporter = new SummaryStatsReporter(emitter); reporter = new SummaryStatsReportGatherer(emitter);
}); });
describe("build Summary Stats Report", () => { describe("build Summary Stats Report", () => {

View File

@@ -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 See the License for the specific language governing permissions and
limitations under the License. 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"; import { MediaTrackStats } from "../../../../src/webrtc/stats/media/mediaTrackStats";
describe("TrackStatsReporter", () => { describe("TrackStatsBuilder", () => {
describe("should on frame and resolution stats", () => { describe("should on frame and resolution stats", () => {
it("creating empty frame and resolution report, if no data available.", async () => { it("creating empty frame and resolution report, if no data available.", async () => {
const trackStats = new MediaTrackStats("1", "local", "video"); const trackStats = new MediaTrackStats("1", "local", "video");
TrackStatsReporter.buildFramerateResolution(trackStats, {}); TrackStatsBuilder.buildFramerateResolution(trackStats, {});
expect(trackStats.getFramerate()).toEqual(0); expect(trackStats.getFramerate()).toEqual(0);
expect(trackStats.getResolution()).toEqual({ width: -1, height: -1 }); expect(trackStats.getResolution()).toEqual({ width: -1, height: -1 });
}); });
it("creating empty frame and resolution report.", async () => { it("creating empty frame and resolution report.", async () => {
const trackStats = new MediaTrackStats("1", "remote", "video"); const trackStats = new MediaTrackStats("1", "remote", "video");
TrackStatsReporter.buildFramerateResolution(trackStats, { TrackStatsBuilder.buildFramerateResolution(trackStats, {
framesPerSecond: 22.2, framesPerSecond: 22.2,
frameHeight: 180, frameHeight: 180,
frameWidth: 360, frameWidth: 360,
@@ -39,7 +39,7 @@ describe("TrackStatsReporter", () => {
describe("should on simulcast", () => { describe("should on simulcast", () => {
it("creating simulcast framerate.", async () => { it("creating simulcast framerate.", async () => {
const trackStats = new MediaTrackStats("1", "local", "video"); const trackStats = new MediaTrackStats("1", "local", "video");
TrackStatsReporter.calculateSimulcastFramerate( TrackStatsBuilder.calculateSimulcastFramerate(
trackStats, trackStats,
{ {
framesSent: 100, framesSent: 100,
@@ -58,7 +58,7 @@ describe("TrackStatsReporter", () => {
describe("should on bytes received stats", () => { describe("should on bytes received stats", () => {
it("creating build bitrate received report.", async () => { it("creating build bitrate received report.", async () => {
const trackStats = new MediaTrackStats("1", "remote", "video"); const trackStats = new MediaTrackStats("1", "remote", "video");
TrackStatsReporter.buildBitrateReceived( TrackStatsBuilder.buildBitrateReceived(
trackStats, trackStats,
{ {
bytesReceived: 2001000, bytesReceived: 2001000,
@@ -73,7 +73,7 @@ describe("TrackStatsReporter", () => {
describe("should on bytes send stats", () => { describe("should on bytes send stats", () => {
it("creating build bitrate send report.", async () => { it("creating build bitrate send report.", async () => {
const trackStats = new MediaTrackStats("1", "local", "video"); const trackStats = new MediaTrackStats("1", "local", "video");
TrackStatsReporter.buildBitrateSend( TrackStatsBuilder.buildBitrateSend(
trackStats, trackStats,
{ {
bytesSent: 2001000, bytesSent: 2001000,
@@ -90,7 +90,7 @@ describe("TrackStatsReporter", () => {
const trackStats = new MediaTrackStats("1", "remote", "video"); const trackStats = new MediaTrackStats("1", "remote", "video");
const remote = {} as RTCStatsReport; const remote = {} as RTCStatsReport;
remote.get = jest.fn().mockReturnValue({ mimeType: "video/v8" }); 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"); expect(trackStats.getCodec()).toEqual("v8");
}); });
}); });
@@ -98,7 +98,7 @@ describe("TrackStatsReporter", () => {
describe("should on package lost stats", () => { describe("should on package lost stats", () => {
it("creating build package lost on send report.", async () => { it("creating build package lost on send report.", async () => {
const trackStats = new MediaTrackStats("1", "local", "video"); const trackStats = new MediaTrackStats("1", "local", "video");
TrackStatsReporter.buildPacketsLost( TrackStatsBuilder.buildPacketsLost(
trackStats, trackStats,
{ {
type: "outbound-rtp", type: "outbound-rtp",
@@ -114,7 +114,7 @@ describe("TrackStatsReporter", () => {
}); });
it("creating build package lost on received report.", async () => { it("creating build package lost on received report.", async () => {
const trackStats = new MediaTrackStats("1", "remote", "video"); const trackStats = new MediaTrackStats("1", "remote", "video");
TrackStatsReporter.buildPacketsLost( TrackStatsBuilder.buildPacketsLost(
trackStats, trackStats,
{ {
type: "inbound-rtp", type: "inbound-rtp",
@@ -133,7 +133,7 @@ describe("TrackStatsReporter", () => {
describe("should set state of a TrackStats", () => { describe("should set state of a TrackStats", () => {
it("to not alive if Transceiver undefined", async () => { it("to not alive if Transceiver undefined", async () => {
const trackStats = new MediaTrackStats("1", "remote", "video"); const trackStats = new MediaTrackStats("1", "remote", "video");
TrackStatsReporter.setTrackStatsState(trackStats, undefined); TrackStatsBuilder.setTrackStatsState(trackStats, undefined);
expect(trackStats.alive).toBeFalsy(); expect(trackStats.alive).toBeFalsy();
}); });
@@ -145,7 +145,7 @@ describe("TrackStatsReporter", () => {
} as RTCRtpSender, } as RTCRtpSender,
} as RTCRtpTransceiver; } as RTCRtpTransceiver;
TrackStatsReporter.setTrackStatsState(trackStats, ts); TrackStatsBuilder.setTrackStatsState(trackStats, ts);
expect(trackStats.alive).toBeFalsy(); expect(trackStats.alive).toBeFalsy();
}); });
@@ -162,7 +162,7 @@ describe("TrackStatsReporter", () => {
} as RTCRtpReceiver, } as RTCRtpReceiver,
} as RTCRtpTransceiver; } as RTCRtpTransceiver;
TrackStatsReporter.setTrackStatsState(trackStats, ts); TrackStatsBuilder.setTrackStatsState(trackStats, ts);
expect(trackStats.alive).toBeTruthy(); expect(trackStats.alive).toBeTruthy();
}); });
@@ -179,7 +179,7 @@ describe("TrackStatsReporter", () => {
} as RTCRtpSender, } as RTCRtpSender,
} as RTCRtpTransceiver; } as RTCRtpTransceiver;
TrackStatsReporter.setTrackStatsState(trackStats, ts); TrackStatsBuilder.setTrackStatsState(trackStats, ts);
expect(trackStats.alive).toBeTruthy(); expect(trackStats.alive).toBeTruthy();
}); });
@@ -195,7 +195,7 @@ describe("TrackStatsReporter", () => {
} as RTCRtpReceiver, } as RTCRtpReceiver,
} as RTCRtpTransceiver; } as RTCRtpTransceiver;
TrackStatsReporter.setTrackStatsState(trackStats, ts); TrackStatsBuilder.setTrackStatsState(trackStats, ts);
expect(trackStats.alive).toBeFalsy(); expect(trackStats.alive).toBeFalsy();
}); });
@@ -211,7 +211,7 @@ describe("TrackStatsReporter", () => {
} as RTCRtpReceiver, } as RTCRtpReceiver,
} as RTCRtpTransceiver; } as RTCRtpTransceiver;
TrackStatsReporter.setTrackStatsState(trackStats, ts); TrackStatsBuilder.setTrackStatsState(trackStats, ts);
expect(trackStats.alive).toBeTruthy(); expect(trackStats.alive).toBeTruthy();
expect(trackStats.muted).toBeTruthy(); expect(trackStats.muted).toBeTruthy();
}); });
@@ -219,7 +219,7 @@ describe("TrackStatsReporter", () => {
describe("should build Track Summary", () => { describe("should build Track Summary", () => {
it("and returns empty summary if stats list empty", async () => { it("and returns empty summary if stats list empty", async () => {
const summary = TrackStatsReporter.buildTrackSummary([]); const summary = TrackStatsBuilder.buildTrackSummary([]);
expect(summary).toEqual({ expect(summary).toEqual({
audioTrackSummary: { audioTrackSummary: {
count: 0, count: 0,
@@ -242,7 +242,7 @@ describe("TrackStatsReporter", () => {
it("and returns summary if stats list not empty and ignore local summery", async () => { it("and returns summary if stats list not empty and ignore local summery", async () => {
const trackStatsList = buildMockTrackStatsList(); const trackStatsList = buildMockTrackStatsList();
const summary = TrackStatsReporter.buildTrackSummary(trackStatsList); const summary = TrackStatsBuilder.buildTrackSummary(trackStatsList);
expect(summary).toEqual({ expect(summary).toEqual({
audioTrackSummary: { audioTrackSummary: {
count: 2, count: 2,
@@ -267,7 +267,7 @@ describe("TrackStatsReporter", () => {
const trackStatsList = buildMockTrackStatsList(); const trackStatsList = buildMockTrackStatsList();
trackStatsList[1].muted = true; trackStatsList[1].muted = true;
trackStatsList[5].muted = true; trackStatsList[5].muted = true;
const summary = TrackStatsReporter.buildTrackSummary(trackStatsList); const summary = TrackStatsBuilder.buildTrackSummary(trackStatsList);
expect(summary).toEqual({ expect(summary).toEqual({
audioTrackSummary: { audioTrackSummary: {
count: 2, count: 2,
@@ -292,7 +292,7 @@ describe("TrackStatsReporter", () => {
const trackStatsList = buildMockTrackStatsList(); const trackStatsList = buildMockTrackStatsList();
trackStatsList[1].muted = true; trackStatsList[1].muted = true;
trackStatsList[1].alive = false; trackStatsList[1].alive = false;
const summary = TrackStatsReporter.buildTrackSummary(trackStatsList); const summary = TrackStatsBuilder.buildTrackSummary(trackStatsList);
expect(summary).toEqual({ expect(summary).toEqual({
audioTrackSummary: { audioTrackSummary: {
count: 2, count: 2,
@@ -330,7 +330,7 @@ describe("TrackStatsReporter", () => {
trackStatsList[2].setAudioConcealment(220, 2000); trackStatsList[2].setAudioConcealment(220, 2000);
trackStatsList[5].setAudioConcealment(180, 2000); trackStatsList[5].setAudioConcealment(180, 2000);
const summary = TrackStatsReporter.buildTrackSummary(trackStatsList); const summary = TrackStatsBuilder.buildTrackSummary(trackStatsList);
expect(summary).toEqual({ expect(summary).toEqual({
audioTrackSummary: { audioTrackSummary: {
count: 2, count: 2,
@@ -355,25 +355,25 @@ describe("TrackStatsReporter", () => {
describe("should build jitter value in Track Stats", () => { describe("should build jitter value in Track Stats", () => {
it("and returns track stats without jitter if report not 'inbound-rtp'", async () => { it("and returns track stats without jitter if report not 'inbound-rtp'", async () => {
const trackStats = new MediaTrackStats("1", "remote", "video"); const trackStats = new MediaTrackStats("1", "remote", "video");
TrackStatsReporter.buildJitter(trackStats, { jitter: 0.01 }); TrackStatsBuilder.buildJitter(trackStats, { jitter: 0.01 });
expect(trackStats.getJitter()).toEqual(0); expect(trackStats.getJitter()).toEqual(0);
}); });
it("and returns track stats with jitter", async () => { it("and returns track stats with jitter", async () => {
const trackStats = new MediaTrackStats("1", "remote", "video"); 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); expect(trackStats.getJitter()).toEqual(10);
}); });
it("and returns negative jitter if stats has no jitter value", async () => { it("and returns negative jitter if stats has no jitter value", async () => {
const trackStats = new MediaTrackStats("1", "remote", "video"); const trackStats = new MediaTrackStats("1", "remote", "video");
TrackStatsReporter.buildJitter(trackStats, { type: "inbound-rtp" }); TrackStatsBuilder.buildJitter(trackStats, { type: "inbound-rtp" });
expect(trackStats.getJitter()).toEqual(-1); expect(trackStats.getJitter()).toEqual(-1);
}); });
it("and returns jitter as number", async () => { it("and returns jitter as number", async () => {
const trackStats = new MediaTrackStats("1", "remote", "video"); 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); expect(trackStats.getJitter()).toEqual(500);
}); });
}); });

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. 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"; import { TransportStats } from "../../../../src/webrtc/stats/transportStats";
describe("TransportStatsReporter", () => { describe("TransportStatsReporter", () => {
@@ -35,7 +35,7 @@ describe("TransportStatsReporter", () => {
it("build new transport stats if all properties there", () => { it("build new transport stats if all properties there", () => {
const { report, stats } = mockStatsReport(isFocus, 0); const { report, stats } = mockStatsReport(isFocus, 0);
const conferenceStatsTransport: TransportStats[] = []; const conferenceStatsTransport: TransportStats[] = [];
const transportStats = TransportStatsReporter.buildReport(report, stats, conferenceStatsTransport, isFocus); const transportStats = TransportStatsBuilder.buildReport(report, stats, conferenceStatsTransport, isFocus);
expect(transportStats).toEqual([ expect(transportStats).toEqual([
{ {
ip: `${remoteIC.ip + 0}:${remoteIC.port}`, ip: `${remoteIC.ip + 0}:${remoteIC.port}`,
@@ -54,8 +54,8 @@ describe("TransportStatsReporter", () => {
const mock1 = mockStatsReport(isFocus, 0); const mock1 = mockStatsReport(isFocus, 0);
const mock2 = mockStatsReport(isFocus, 1); const mock2 = mockStatsReport(isFocus, 1);
let transportStats: TransportStats[] = []; let transportStats: TransportStats[] = [];
transportStats = TransportStatsReporter.buildReport(mock1.report, mock1.stats, transportStats, isFocus); transportStats = TransportStatsBuilder.buildReport(mock1.report, mock1.stats, transportStats, isFocus);
transportStats = TransportStatsReporter.buildReport(mock2.report, mock2.stats, transportStats, isFocus); transportStats = TransportStatsBuilder.buildReport(mock2.report, mock2.stats, transportStats, isFocus);
expect(transportStats).toEqual([ expect(transportStats).toEqual([
{ {
ip: `${remoteIC.ip + 0}:${remoteIC.port}`, ip: `${remoteIC.ip + 0}:${remoteIC.port}`,
@@ -84,8 +84,8 @@ describe("TransportStatsReporter", () => {
const mock1 = mockStatsReport(isFocus, 0); const mock1 = mockStatsReport(isFocus, 0);
const mock2 = mockStatsReport(isFocus, 0); const mock2 = mockStatsReport(isFocus, 0);
let transportStats: TransportStats[] = []; let transportStats: TransportStats[] = [];
transportStats = TransportStatsReporter.buildReport(mock1.report, mock1.stats, transportStats, isFocus); transportStats = TransportStatsBuilder.buildReport(mock1.report, mock1.stats, transportStats, isFocus);
transportStats = TransportStatsReporter.buildReport(mock2.report, mock2.stats, transportStats, isFocus); transportStats = TransportStatsBuilder.buildReport(mock2.report, mock2.stats, transportStats, isFocus);
expect(transportStats).toEqual([ expect(transportStats).toEqual([
{ {
ip: `${remoteIC.ip + 0}:${remoteIC.port}`, ip: `${remoteIC.ip + 0}:${remoteIC.port}`,

View File

@@ -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 See the License for the specific language governing permissions and
limitations under the License. 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", () => { describe("on get non negative values", () => {
it("formatter shod return number", async () => { it("formatter shod return number", async () => {
expect(StatsValueFormatter.getNonNegativeValue("2")).toEqual(2); expect(ValueFormatter.getNonNegativeValue("2")).toEqual(2);
expect(StatsValueFormatter.getNonNegativeValue(0)).toEqual(0); expect(ValueFormatter.getNonNegativeValue(0)).toEqual(0);
expect(StatsValueFormatter.getNonNegativeValue("-2")).toEqual(0); expect(ValueFormatter.getNonNegativeValue("-2")).toEqual(0);
expect(StatsValueFormatter.getNonNegativeValue("")).toEqual(0); expect(ValueFormatter.getNonNegativeValue("")).toEqual(0);
expect(StatsValueFormatter.getNonNegativeValue(NaN)).toEqual(0); expect(ValueFormatter.getNonNegativeValue(NaN)).toEqual(0);
}); });
}); });
}); });

View File

@@ -17,17 +17,17 @@ limitations under the License.
import { ConnectionStats } from "./connectionStats"; import { ConnectionStats } from "./connectionStats";
import { StatsReportEmitter } from "./statsReportEmitter"; import { StatsReportEmitter } from "./statsReportEmitter";
import { ByteSend, ByteSentStatsReport, TrackID } from "./statsReport"; import { ByteSend, ByteSentStatsReport, TrackID } from "./statsReport";
import { ConnectionStatsReporter } from "./connectionStatsReporter"; import { ConnectionStatsBuilder } from "./connectionStatsBuilder";
import { TransportStatsReporter } from "./transportStatsReporter"; import { TransportStatsBuilder } from "./transportStatsBuilder";
import { MediaSsrcHandler } from "./media/mediaSsrcHandler"; import { MediaSsrcHandler } from "./media/mediaSsrcHandler";
import { MediaTrackHandler } from "./media/mediaTrackHandler"; import { MediaTrackHandler } from "./media/mediaTrackHandler";
import { MediaTrackStatsHandler } from "./media/mediaTrackStatsHandler"; import { MediaTrackStatsHandler } from "./media/mediaTrackStatsHandler";
import { TrackStatsReporter } from "./trackStatsReporter"; import { TrackStatsBuilder } from "./trackStatsBuilder";
import { StatsReportBuilder } from "./statsReportBuilder"; import { ConnectionStatsReportBuilder } from "./connectionStatsReportBuilder";
import { StatsValueFormatter } from "./statsValueFormatter"; import { ValueFormatter } from "./valueFormatter";
import { SummaryStats } from "./summaryStats"; import { CallStatsReportSummary } from "./callStatsReportSummary";
export class StatsReportGatherer { export class CallStatsReportGatherer {
private isActive = true; private isActive = true;
private previousStatsReport: RTCStatsReport | undefined; private previousStatsReport: RTCStatsReport | undefined;
private currentStatsReport: RTCStatsReport | undefined; private currentStatsReport: RTCStatsReport | undefined;
@@ -46,7 +46,7 @@ export class StatsReportGatherer {
this.trackStats = new MediaTrackStatsHandler(new MediaSsrcHandler(), new MediaTrackHandler(pc)); 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 = { const summary = {
isFirstCollection: this.previousStatsReport === undefined, isFirstCollection: this.previousStatsReport === undefined,
receivedMedia: 0, receivedMedia: 0,
@@ -54,7 +54,7 @@ export class StatsReportGatherer {
receivedVideoMedia: 0, receivedVideoMedia: 0,
audioTrackSummary: { count: 0, muted: 0, maxPacketLoss: 0, maxJitter: 0, concealedAudio: 0, totalAudio: 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 }, videoTrackSummary: { count: 0, muted: 0, maxPacketLoss: 0, maxJitter: 0, concealedAudio: 0, totalAudio: 0 },
} as SummaryStats; } as CallStatsReportSummary;
if (this.isActive) { if (this.isActive) {
const statsPromise = this.pc.getStats(); const statsPromise = this.pc.getStats();
if (typeof statsPromise?.then === "function") { if (typeof statsPromise?.then === "function") {
@@ -73,7 +73,7 @@ export class StatsReportGatherer {
summary.receivedMedia = this.connectionStats.bitrate.download; summary.receivedMedia = this.connectionStats.bitrate.download;
summary.receivedAudioMedia = this.connectionStats.bitrate.audio?.download || 0; summary.receivedAudioMedia = this.connectionStats.bitrate.audio?.download || 0;
summary.receivedVideoMedia = this.connectionStats.bitrate.video?.download || 0; summary.receivedVideoMedia = this.connectionStats.bitrate.video?.download || 0;
const trackSummary = TrackStatsReporter.buildTrackSummary( const trackSummary = TrackStatsBuilder.buildTrackSummary(
Array.from(this.trackStats.getTrack2stats().values()), Array.from(this.trackStats.getTrack2stats().values()),
); );
return { return {
@@ -93,14 +93,14 @@ export class StatsReportGatherer {
} }
private processStatsReport(groupCallId: string, localUserId: string): void { 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) => { this.currentStatsReport?.forEach((now) => {
const before = this.previousStatsReport ? this.previousStatsReport.get(now.id) : null; const before = this.previousStatsReport ? this.previousStatsReport.get(now.id) : null;
// RTCIceCandidatePairStats - https://w3c.github.io/webrtc-stats/#candidatepair-dict* // RTCIceCandidatePairStats - https://w3c.github.io/webrtc-stats/#candidatepair-dict*
if (now.type === "candidate-pair" && now.nominated && now.state === "succeeded") { if (now.type === "candidate-pair" && now.nominated && now.state === "succeeded") {
this.connectionStats.bandwidth = ConnectionStatsReporter.buildBandwidthReport(now); this.connectionStats.bandwidth = ConnectionStatsBuilder.buildBandwidthReport(now);
this.connectionStats.transport = TransportStatsReporter.buildReport( this.connectionStats.transport = TransportStatsBuilder.buildReport(
this.currentStatsReport, this.currentStatsReport,
now, now,
this.connectionStats.transport, this.connectionStats.transport,
@@ -121,7 +121,7 @@ export class StatsReportGatherer {
} }
if (before) { 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, // 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 // more calculations needed to determine what is the highest resolution stream sent by the client if the
// 'outbound-rtp' stats are used. // 'outbound-rtp' stats are used.
if (now.type === "inbound-rtp") { if (now.type === "inbound-rtp") {
TrackStatsReporter.buildFramerateResolution(trackStats, now); TrackStatsBuilder.buildFramerateResolution(trackStats, now);
if (before) { if (before) {
TrackStatsReporter.buildBitrateReceived(trackStats, now, before); TrackStatsBuilder.buildBitrateReceived(trackStats, now, before);
} }
const ts = this.trackStats.findTransceiverByTrackId(trackStats.trackId); const ts = this.trackStats.findTransceiverByTrackId(trackStats.trackId);
TrackStatsReporter.setTrackStatsState(trackStats, ts); TrackStatsBuilder.setTrackStatsState(trackStats, ts);
TrackStatsReporter.buildJitter(trackStats, now); TrackStatsBuilder.buildJitter(trackStats, now);
TrackStatsReporter.buildAudioConcealment(trackStats, now); TrackStatsBuilder.buildAudioConcealment(trackStats, now);
} else if (before) { } else if (before) {
byteSentStats.set(trackStats.trackId, StatsValueFormatter.getNonNegativeValue(now.bytesSent)); byteSentStatsReport.set(trackStats.trackId, ValueFormatter.getNonNegativeValue(now.bytesSent));
TrackStatsReporter.buildBitrateSend(trackStats, now, before); 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) { } else if (now.type === "track" && now.kind === "video" && !now.remoteSource) {
const trackStats = this.trackStats.findLocalVideoTrackStats(now); const trackStats = this.trackStats.findLocalVideoTrackStats(now);
if (!trackStats) { if (!trackStats) {
return; return;
} }
TrackStatsReporter.buildFramerateResolution(trackStats, now); TrackStatsBuilder.buildFramerateResolution(trackStats, now);
TrackStatsReporter.calculateSimulcastFramerate( TrackStatsBuilder.calculateSimulcastFramerate(
trackStats, trackStats,
now, now,
before, before,
@@ -158,8 +158,8 @@ export class StatsReportGatherer {
} }
}); });
this.emitter.emitByteSendReport(byteSentStats); this.emitter.emitByteSendReport(byteSentStatsReport);
this.processAndEmitReport(); this.processAndEmitConnectionStatsReport();
} }
public setActive(isActive: boolean): void { public setActive(isActive: boolean): void {
@@ -174,8 +174,8 @@ export class StatsReportGatherer {
this.isActive = false; this.isActive = false;
} }
private processAndEmitReport(): void { private processAndEmitConnectionStatsReport(): void {
const report = StatsReportBuilder.build(this.trackStats.getTrack2stats()); const report = ConnectionStatsReportBuilder.build(this.trackStats.getTrack2stats());
this.connectionStats.bandwidth = report.bandwidth; this.connectionStats.bandwidth = report.bandwidth;
this.connectionStats.bitrate = report.bitrate; this.connectionStats.bitrate = report.bitrate;

View File

@@ -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 See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
export interface SummaryStats { export interface CallStatsReportSummary {
receivedMedia: number; receivedMedia: number;
receivedAudioMedia: number; receivedAudioMedia: number;
receivedVideoMedia: number; receivedVideoMedia: number;

View File

@@ -15,7 +15,7 @@ limitations under the License.
*/ */
import { Bitrate } from "./media/mediaTrackStats"; import { Bitrate } from "./media/mediaTrackStats";
export class ConnectionStatsReporter { export class ConnectionStatsBuilder {
public static buildBandwidthReport(now: RTCIceCandidatePairStats): Bitrate { public static buildBandwidthReport(now: RTCIceCandidatePairStats): Bitrate {
const availableIncomingBitrate = now.availableIncomingBitrate; const availableIncomingBitrate = now.availableIncomingBitrate;
const availableOutgoingBitrate = now.availableOutgoingBitrate; const availableOutgoingBitrate = now.availableOutgoingBitrate;

View File

@@ -16,7 +16,7 @@ limitations under the License.
import { AudioConcealment, CodecMap, ConnectionStatsReport, FramerateMap, ResolutionMap, TrackID } from "./statsReport"; import { AudioConcealment, CodecMap, ConnectionStatsReport, FramerateMap, ResolutionMap, TrackID } from "./statsReport";
import { MediaTrackStats, Resolution } from "./media/mediaTrackStats"; import { MediaTrackStats, Resolution } from "./media/mediaTrackStats";
export class StatsReportBuilder { export class ConnectionStatsReportBuilder {
public static build(stats: Map<TrackID, MediaTrackStats>): ConnectionStatsReport { public static build(stats: Map<TrackID, MediaTrackStats>): ConnectionStatsReport {
const report = {} as ConnectionStatsReport; const report = {} as ConnectionStatsReport;
@@ -103,12 +103,12 @@ export class StatsReportBuilder {
}; };
report.packetLoss = { report.packetLoss = {
total: StatsReportBuilder.calculatePacketLoss( total: ConnectionStatsReportBuilder.calculatePacketLoss(
lostPackets.download + lostPackets.upload, lostPackets.download + lostPackets.upload,
totalPackets.download + totalPackets.upload, totalPackets.download + totalPackets.upload,
), ),
download: StatsReportBuilder.calculatePacketLoss(lostPackets.download, totalPackets.download), download: ConnectionStatsReportBuilder.calculatePacketLoss(lostPackets.download, totalPackets.download),
upload: StatsReportBuilder.calculatePacketLoss(lostPackets.upload, totalPackets.upload), upload: ConnectionStatsReportBuilder.calculatePacketLoss(lostPackets.upload, totalPackets.upload),
}; };
report.audioConcealment = audioConcealment; report.audioConcealment = audioConcealment;
report.totalAudioConcealment = { report.totalAudioConcealment = {

View File

@@ -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 See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { StatsReportGatherer } from "./statsReportGatherer"; import { CallStatsReportGatherer } from "./callStatsReportGatherer";
import { StatsReportEmitter } from "./statsReportEmitter"; import { StatsReportEmitter } from "./statsReportEmitter";
import { SummaryStats } from "./summaryStats"; import { CallStatsReportSummary } from "./callStatsReportSummary";
import { SummaryStatsReporter } from "./summaryStatsReporter"; import { SummaryStatsReportGatherer } from "./summaryStatsReportGatherer";
export class GroupCallStats { export class GroupCallStats {
private timer: undefined | ReturnType<typeof setTimeout>; 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(); 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) {} public constructor(private groupCallId: string, private userId: string, private interval: number = 10000) {}
@@ -49,7 +49,7 @@ export class GroupCallStats {
if (this.hasStatsReportGatherer(callId)) { if (this.hasStatsReportGatherer(callId)) {
return false; 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; return true;
} }
@@ -57,17 +57,17 @@ export class GroupCallStats {
return this.gatherers.delete(callId); 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; return this.hasStatsReportGatherer(callId) ? this.gatherers.get(callId) : undefined;
} }
private processStats(): void { private processStats(): void {
const summary: Promise<SummaryStats>[] = []; const summary: Promise<CallStatsReportSummary>[] = [];
this.gatherers.forEach((c) => { this.gatherers.forEach((c) => {
summary.push(c.processStats(this.groupCallId, this.userId)); 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 { public setInterval(interval: number): void {

View File

@@ -11,10 +11,10 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { StatsReportEmitter } from "./statsReportEmitter"; import { StatsReportEmitter } from "./statsReportEmitter";
import { SummaryStats } from "./summaryStats"; import { CallStatsReportSummary } from "./callStatsReportSummary";
import { SummaryStatsReport } from "./statsReport"; import { SummaryStatsReport } from "./statsReport";
interface SummaryCounter { interface CallStatsReportSummaryCounter {
receivedAudio: number; receivedAudio: number;
receivedVideo: number; receivedVideo: number;
receivedMedia: number; receivedMedia: number;
@@ -22,10 +22,10 @@ interface SummaryCounter {
totalAudio: number; totalAudio: number;
} }
export class SummaryStatsReporter { export class SummaryStatsReportGatherer {
public constructor(private emitter: StatsReportEmitter) {} 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. // 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 // 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. // 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) { if (summaryTotalCount === 0) {
return; return;
} }
const summaryCounter: SummaryCounter = { const summaryCounter: CallStatsReportSummaryCounter = {
receivedAudio: 0, receivedAudio: 0,
receivedVideo: 0, receivedVideo: 0,
receivedMedia: 0, receivedMedia: 0,
@@ -70,7 +70,7 @@ export class SummaryStatsReporter {
this.emitter.emitSummaryStatsReport(report); this.emitter.emitSummaryStatsReport(report);
} }
private countTrackListReceivedMedia(counter: SummaryCounter, stats: SummaryStats): void { private countTrackListReceivedMedia(counter: CallStatsReportSummaryCounter, stats: CallStatsReportSummary): void {
let hasReceivedAudio = false; let hasReceivedAudio = false;
let hasReceivedVideo = false; let hasReceivedVideo = false;
if (stats.receivedAudioMedia > 0 || stats.audioTrackSummary.count === 0) { 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) { if (maxJitter < stats.videoTrackSummary.maxJitter) {
maxJitter = stats.videoTrackSummary.maxJitter; maxJitter = stats.videoTrackSummary.maxJitter;
} }
@@ -103,7 +103,7 @@ export class SummaryStatsReporter {
return maxJitter; return maxJitter;
} }
private buildMaxPacketLoss(maxPacketLoss: number, stats: SummaryStats): number { private buildMaxPacketLoss(maxPacketLoss: number, stats: CallStatsReportSummary): number {
if (maxPacketLoss < stats.videoTrackSummary.maxPacketLoss) { if (maxPacketLoss < stats.videoTrackSummary.maxPacketLoss) {
maxPacketLoss = stats.videoTrackSummary.maxPacketLoss; maxPacketLoss = stats.videoTrackSummary.maxPacketLoss;
} }
@@ -114,7 +114,7 @@ export class SummaryStatsReporter {
return maxPacketLoss; return maxPacketLoss;
} }
private countConcealedAudio(summaryCounter: SummaryCounter, stats: SummaryStats): void { private countConcealedAudio(summaryCounter: CallStatsReportSummaryCounter, stats: CallStatsReportSummary): void {
summaryCounter.concealedAudio += stats.audioTrackSummary.concealedAudio; summaryCounter.concealedAudio += stats.audioTrackSummary.concealedAudio;
summaryCounter.totalAudio += stats.audioTrackSummary.totalAudio; summaryCounter.totalAudio += stats.audioTrackSummary.totalAudio;
} }

View File

@@ -1,8 +1,8 @@
import { MediaTrackStats } from "./media/mediaTrackStats"; import { MediaTrackStats } from "./media/mediaTrackStats";
import { StatsValueFormatter } from "./statsValueFormatter"; import { ValueFormatter } from "./valueFormatter";
import { TrackSummary } from "./summaryStats"; import { TrackSummary } from "./callStatsReportSummary";
export class TrackStatsReporter { export class TrackStatsBuilder {
public static buildFramerateResolution(trackStats: MediaTrackStats, now: any): void { public static buildFramerateResolution(trackStats: MediaTrackStats, now: any): void {
const resolution = { const resolution = {
height: now.frameHeight, height: now.frameHeight,
@@ -56,7 +56,7 @@ export class TrackStatsReporter {
public static buildBitrateReceived(trackStats: MediaTrackStats, now: any, before: any): void { public static buildBitrateReceived(trackStats: MediaTrackStats, now: any, before: any): void {
trackStats.setBitrate({ trackStats.setBitrate({
download: TrackStatsReporter.calculateBitrate( download: TrackStatsBuilder.calculateBitrate(
now.bytesReceived, now.bytesReceived,
before.bytesReceived, before.bytesReceived,
now.timestamp, now.timestamp,
@@ -81,11 +81,11 @@ export class TrackStatsReporter {
packetsNow = 0; packetsNow = 0;
} }
const packetsBefore = StatsValueFormatter.getNonNegativeValue(before[key]); const packetsBefore = ValueFormatter.getNonNegativeValue(before[key]);
const packetsDiff = Math.max(0, packetsNow - packetsBefore); const packetsDiff = Math.max(0, packetsNow - packetsBefore);
const packetsLostNow = StatsValueFormatter.getNonNegativeValue(now.packetsLost); const packetsLostNow = ValueFormatter.getNonNegativeValue(now.packetsLost);
const packetsLostBefore = StatsValueFormatter.getNonNegativeValue(before.packetsLost); const packetsLostBefore = ValueFormatter.getNonNegativeValue(before.packetsLost);
const packetsLostDiff = Math.max(0, packetsLostNow - packetsLostBefore); const packetsLostDiff = Math.max(0, packetsLostNow - packetsLostBefore);
trackStats.setLoss({ trackStats.setLoss({
@@ -101,8 +101,8 @@ export class TrackStatsReporter {
nowTimestamp: number, nowTimestamp: number,
beforeTimestamp: number, beforeTimestamp: number,
): number { ): number {
const bytesNow = StatsValueFormatter.getNonNegativeValue(bytesNowAny); const bytesNow = ValueFormatter.getNonNegativeValue(bytesNowAny);
const bytesBefore = StatsValueFormatter.getNonNegativeValue(bytesBeforeAny); const bytesBefore = ValueFormatter.getNonNegativeValue(bytesBeforeAny);
const bytesProcessed = Math.max(0, bytesNow - bytesBefore); const bytesProcessed = Math.max(0, bytesNow - bytesBefore);
const timeMs = nowTimestamp - beforeTimestamp; const timeMs = nowTimestamp - beforeTimestamp;
@@ -188,7 +188,7 @@ export class TrackStatsReporter {
const jitterStr = statsReport?.jitter; const jitterStr = statsReport?.jitter;
if (jitterStr !== undefined) { if (jitterStr !== undefined) {
const jitter = StatsValueFormatter.getNonNegativeValue(jitterStr); const jitter = ValueFormatter.getNonNegativeValue(jitterStr);
trackStats.setJitter(Math.round(jitter * 1000)); trackStats.setJitter(Math.round(jitter * 1000));
} else { } else {
trackStats.setJitter(-1); trackStats.setJitter(-1);

View File

@@ -1,6 +1,6 @@
import { TransportStats } from "./transportStats"; import { TransportStats } from "./transportStats";
export class TransportStatsReporter { export class TransportStatsBuilder {
public static buildReport( public static buildReport(
report: RTCStatsReport | undefined, report: RTCStatsReport | undefined,
now: RTCIceCandidatePairStats, now: RTCIceCandidatePairStats,

View File

@@ -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 See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
export class StatsValueFormatter { export class ValueFormatter {
public static getNonNegativeValue(imput: any): number { public static getNonNegativeValue(imput: any): number {
let value = imput; let value = imput;