diff --git a/spec/unit/webrtc/groupCall.spec.ts b/spec/unit/webrtc/groupCall.spec.ts index 3d344266a..4d839827b 100644 --- a/spec/unit/webrtc/groupCall.spec.ts +++ b/spec/unit/webrtc/groupCall.spec.ts @@ -1629,4 +1629,77 @@ describe("Group Call", function () { expect(room.currentState.getStateEvents(EventType.GroupCallMemberPrefix, FAKE_USER_ID_2)).toBe(null); }); }); + + describe("collection stats", () => { + let groupCall: GroupCall; + + beforeAll(() => { + jest.useFakeTimers(); + jest.setSystemTime(0); + }); + + afterAll(() => jest.useRealTimers()); + + beforeEach(async () => { + const typedMockClient = new MockCallMatrixClient(FAKE_USER_ID_1, FAKE_DEVICE_ID_1, FAKE_SESSION_ID_1); + const mockClient = typedMockClient.typed(); + const room = new Room(FAKE_ROOM_ID, mockClient, FAKE_USER_ID_1); + groupCall = new GroupCall( + mockClient, + room, + GroupCallType.Video, + false, + GroupCallIntent.Prompt, + FAKE_CONF_ID, + ); + }); + it("should be undefined if not get stats", async () => { + // @ts-ignore + const stats = groupCall.stats; + expect(stats).toBeUndefined(); + }); + + it("should be defined after first access", async () => { + groupCall.getGroupCallStats(); + // @ts-ignore + const stats = groupCall.stats; + expect(stats).toBeDefined(); + }); + + it("with every number should do nothing if no stats exists.", async () => { + groupCall.setGroupCallStatsInterval(0); + // @ts-ignore + let stats = groupCall.stats; + expect(stats).toBeUndefined(); + + groupCall.setGroupCallStatsInterval(10000); + // @ts-ignore + stats = groupCall.stats; + expect(stats).toBeUndefined(); + }); + + it("with number should stop existing stats", async () => { + const stats = groupCall.getGroupCallStats(); + // @ts-ignore + const stop = jest.spyOn(stats, "stop"); + // @ts-ignore + const start = jest.spyOn(stats, "start"); + groupCall.setGroupCallStatsInterval(0); + + expect(stop).toHaveBeenCalled(); + expect(start).not.toHaveBeenCalled(); + }); + + it("with number should restart existing stats", async () => { + const stats = groupCall.getGroupCallStats(); + // @ts-ignore + const stop = jest.spyOn(stats, "stop"); + // @ts-ignore + const start = jest.spyOn(stats, "start"); + groupCall.setGroupCallStatsInterval(10000); + + expect(stop).toHaveBeenCalled(); + expect(start).toHaveBeenCalled(); + }); + }); }); diff --git a/spec/unit/webrtc/stats/groupCallStats.spec.ts b/spec/unit/webrtc/stats/groupCallStats.spec.ts index 297e49152..f0b6c4365 100644 --- a/spec/unit/webrtc/stats/groupCallStats.spec.ts +++ b/spec/unit/webrtc/stats/groupCallStats.spec.ts @@ -68,7 +68,7 @@ describe("GroupCallStats", () => { jest.useRealTimers(); }); - it("starting processing as well without stats collectors", async () => { + it("starting processing stats as well without stats collectors", async () => { // @ts-ignore stats.processStats = jest.fn(); stats.start(); @@ -77,6 +77,16 @@ describe("GroupCallStats", () => { expect(stats.processStats).toHaveBeenCalled(); }); + it("not starting processing stats if interval 0", async () => { + const statsDisabled = new GroupCallStats(GROUP_CALL_ID, LOCAL_USER_ID, 0); + // @ts-ignore + statsDisabled.processStats = jest.fn(); + statsDisabled.start(); + jest.advanceTimersByTime(TIME_INTERVAL); + // @ts-ignore + expect(statsDisabled.processStats).not.toHaveBeenCalled(); + }); + it("starting processing and calling the collectors", async () => { stats.addStatsReportGatherer("CALL_ID", "USER_ID", mockRTCPeerConnection()); const collector = stats.getStatsReportGatherer("CALL_ID"); diff --git a/src/webrtc/groupCall.ts b/src/webrtc/groupCall.ts index 0207cf1d5..a6a629576 100644 --- a/src/webrtc/groupCall.ts +++ b/src/webrtc/groupCall.ts @@ -236,7 +236,12 @@ export class GroupCall extends TypedEventEmitter< private initWithVideoMuted = false; private initCallFeedPromise?: Promise; - private readonly stats: GroupCallStats; + private stats: GroupCallStats | undefined; + /** + * Configure default webrtc stats collection interval in ms + * Disable collecting webrtc stats by setting interval to 0 + */ + private statsCollectIntervalTime = 0; public constructor( private client: MatrixClient, @@ -261,12 +266,6 @@ export class GroupCall extends TypedEventEmitter< this.on(GroupCallEvent.GroupCallStateChanged, this.onStateChanged); this.on(GroupCallEvent.LocalScreenshareStateChanged, this.onLocalFeedsChanged); this.allowCallWithoutVideoAndAudio = !!isCallWithoutVideoAndAudio; - - const userID = this.client.getUserId() || "unknown"; - this.stats = new GroupCallStats(this.groupCallId, userID); - this.stats.reports.on(StatsReport.CONNECTION_STATS, this.onConnectionStats); - this.stats.reports.on(StatsReport.BYTE_SENT_STATS, this.onByteSentStats); - this.stats.reports.on(StatsReport.SUMMARY_STATS, this.onSummaryStats); } private onConnectionStats = (report: ConnectionStatsReport): void => { @@ -553,7 +552,7 @@ export class GroupCall extends TypedEventEmitter< clearInterval(this.retryCallLoopInterval); this.client.removeListener(CallEventHandlerEvent.Incoming, this.onIncomingCall); - this.stats.stop(); + this.stats?.stop(); } public leave(): void { @@ -1087,7 +1086,7 @@ export class GroupCall extends TypedEventEmitter< this.reEmitter.reEmit(call, Object.values(CallEvent)); - call.initStats(this.stats); + call.initStats(this.getGroupCallStats()); onCallFeedsChanged(); } @@ -1609,6 +1608,24 @@ export class GroupCall extends TypedEventEmitter< }; public getGroupCallStats(): GroupCallStats { + if (this.stats === undefined) { + const userID = this.client.getUserId() || "unknown"; + this.stats = new GroupCallStats(this.groupCallId, userID, this.statsCollectIntervalTime); + this.stats.reports.on(StatsReport.CONNECTION_STATS, this.onConnectionStats); + this.stats.reports.on(StatsReport.BYTE_SENT_STATS, this.onByteSentStats); + this.stats.reports.on(StatsReport.SUMMARY_STATS, this.onSummaryStats); + } return this.stats; } + + public setGroupCallStatsInterval(interval: number): void { + this.statsCollectIntervalTime = interval; + if (this.stats !== undefined) { + this.stats.stop(); + this.stats.setInterval(interval); + if (interval > 0) { + this.stats.start(); + } + } + } } diff --git a/src/webrtc/stats/groupCallStats.ts b/src/webrtc/stats/groupCallStats.ts index d552ad589..898424846 100644 --- a/src/webrtc/stats/groupCallStats.ts +++ b/src/webrtc/stats/groupCallStats.ts @@ -27,7 +27,7 @@ export class GroupCallStats { public constructor(private groupCallId: string, private userId: string, private interval: number = 10000) {} public start(): void { - if (this.timer === undefined) { + if (this.timer === undefined && this.interval > 0) { this.timer = setInterval(() => { this.processStats(); }, this.interval); @@ -69,4 +69,8 @@ export class GroupCallStats { Promise.all(summary).then((s: Awaited[]) => this.summaryStatsReporter.build(s)); } + + public setInterval(interval: number): void { + this.interval = interval; + } }