Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for simulcast #2983

Merged
merged 19 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion spec/test-utils/webrtc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@ export class MockRTCRtpSender {
public replaceTrack(track: MockMediaStreamTrack) {
this.track = track;
}

public getParameters() {}
public setParameters() {}
}

export class MockRTCRtpReceiver {
Expand All @@ -292,7 +295,7 @@ export class MockMediaStreamTrack {

public listeners: [string, (...args: any[]) => any][] = [];
public isStopped = false;
public settings?: MediaTrackSettings;
public settings: MediaTrackSettings = {};

public getSettings(): MediaTrackSettings {
return this.settings!;
Expand Down Expand Up @@ -592,6 +595,7 @@ export class MockCallFeed {
export function installWebRTCMocks() {
global.navigator = {
mediaDevices: new MockMediaDevices().typed(),
userAgent: "This is definitely a user agent string",
} as unknown as Navigator;

global.window = {
Expand Down
85 changes: 43 additions & 42 deletions spec/unit/webrtc/call.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import {
import {
MCallAnswer,
MCallHangupReject,
SDPStreamMetadata,
SDPStreamMetadataKey,
SDPStreamMetadataPurpose,
} from "../../../src/webrtc/callEventTypes";
Expand Down Expand Up @@ -83,6 +82,7 @@ const fakeIncomingCall = async (client: TestClient, call: MatrixCall, version: s
client: client.client,
userId: "remote_user_id",
deviceId: undefined,
feedId: "remote_stream_id",
stream: new MockMediaStream("remote_stream_id", [
new MockMediaStreamTrack("remote_tack_id", "audio"),
]) as unknown as MediaStream,
Expand Down Expand Up @@ -316,13 +316,13 @@ describe("Call", function () {
}),
);

(call as any).pushRemoteFeed(
(call as any).pushRemoteStream(
new MockMediaStream("remote_stream", [
new MockMediaStreamTrack("remote_audio_track", "audio"),
new MockMediaStreamTrack("remote_video_track", "video"),
]),
);
const feed = call.getFeeds().find((feed) => feed.stream.id === "remote_stream");
const feed = call.getFeeds().find((feed) => feed.stream?.id === "remote_stream");
expect(feed?.purpose).toBe(SDPStreamMetadataPurpose.Usermedia);
expect(feed?.isAudioMuted()).toBeTruthy();
expect(feed?.isVideoMuted()).not.toBeTruthy();
Expand Down Expand Up @@ -439,8 +439,8 @@ describe("Call", function () {
video_muted: false,
},
});
(call as any).pushRemoteFeed(new MockMediaStream("remote_stream", []));
const feed = call.getFeeds().find((feed) => feed.stream.id === "remote_stream");
(call as any).pushRemoteStream(new MockMediaStream("remote_stream", []));
const feed = call.getFeeds().find((feed) => feed.stream?.id === "remote_stream");

call.onSDPStreamMetadataChangedReceived(
makeMockEvent("@test:foo", {
Expand Down Expand Up @@ -520,14 +520,14 @@ describe("Call", function () {
it("if no video", async () => {
call.getOpponentMember = jest.fn().mockReturnValue({ userId: "@bob:bar.uk" });

(call as any).pushRemoteFeed(new MockMediaStream("remote_stream1", []));
(call as any).pushRemoteStream(new MockMediaStream("remote_stream1", []));
expect(call.type).toBe(CallType.Voice);
});

it("if remote video", async () => {
call.getOpponentMember = jest.fn().mockReturnValue({ userId: "@bob:bar.uk" });

(call as any).pushRemoteFeed(
(call as any).pushRemoteStream(
new MockMediaStream("remote_stream1", [new MockMediaStreamTrack("track_id", "video")]),
);
expect(call.type).toBe(CallType.Video);
Expand Down Expand Up @@ -555,6 +555,7 @@ describe("Call", function () {
roomId: call.roomId,
userId: client.getUserId(),
deviceId: undefined,
feedId: "local_stream1",
purpose: SDPStreamMetadataPurpose.Usermedia,
audioMuted: false,
videoMuted: false,
Expand Down Expand Up @@ -597,6 +598,7 @@ describe("Call", function () {
client: client.client,
userId: client.getUserId(),
deviceId: undefined,
feedId: localUsermediaStream.id,
stream: localUsermediaStream as unknown as MediaStream,
purpose: SDPStreamMetadataPurpose.Usermedia,
audioMuted: false,
Expand All @@ -606,6 +608,7 @@ describe("Call", function () {
client: client.client,
userId: client.getUserId(),
deviceId: undefined,
feedId: localScreensharingStream.id,
stream: localScreensharingStream as unknown as MediaStream,
purpose: SDPStreamMetadataPurpose.Screenshare,
audioMuted: false,
Expand All @@ -629,8 +632,8 @@ describe("Call", function () {
video_muted: false,
},
});
(call as any).pushRemoteFeed(remoteUsermediaStream);
(call as any).pushRemoteFeed(remoteScreensharingStream);
(call as any).pushRemoteStream(remoteUsermediaStream);
(call as any).pushRemoteStream(remoteScreensharingStream);

expect(call.localUsermediaFeed!.stream).toBe(localUsermediaStream);
expect(call.localUsermediaStream).toBe(localUsermediaStream);
Expand Down Expand Up @@ -762,7 +765,7 @@ describe("Call", function () {
call.off(CallEvent.FeedsChanged, FEEDS_CHANGED_CALLBACK);
});

it("should ignore stream passed to pushRemoteFeed()", async () => {
it("should ignore stream passed to pushRemoteStream()", async () => {
await call.onAnswerReceived(
makeMockEvent("@test:foo", {
version: 1,
Expand All @@ -779,16 +782,16 @@ describe("Call", function () {
}),
);

(call as any).pushRemoteFeed(new MockMediaStream(STREAM_ID));
(call as any).pushRemoteFeed(new MockMediaStream(STREAM_ID));
(call as any).pushRemoteStream(new MockMediaStream(STREAM_ID));
(call as any).pushRemoteStream(new MockMediaStream(STREAM_ID));

expect(call.getRemoteFeeds().length).toBe(1);
expect(FEEDS_CHANGED_CALLBACK).toHaveBeenCalledTimes(1);
});

it("should ignore stream passed to pushRemoteFeedWithoutMetadata()", async () => {
(call as any).pushRemoteFeedWithoutMetadata(new MockMediaStream(STREAM_ID));
(call as any).pushRemoteFeedWithoutMetadata(new MockMediaStream(STREAM_ID));
it("should ignore stream passed to pushRemoteStreamWithoutMetadata()", async () => {
(call as any).pushRemoteStreamWithoutMetadata(new MockMediaStream(STREAM_ID));
(call as any).pushRemoteStreamWithoutMetadata(new MockMediaStream(STREAM_ID));

expect(call.getRemoteFeeds().length).toBe(1);
expect(FEEDS_CHANGED_CALLBACK).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -858,41 +861,39 @@ describe("Call", function () {
});

describe("receiving sdp_stream_metadata_changed events", () => {
const setupCall = (audio: boolean, video: boolean): SDPStreamMetadata => {
const metadata = {
stream: {
user_id: "user",
device_id: "device",
purpose: SDPStreamMetadataPurpose.Usermedia,
audio_muted: audio,
video_muted: video,
tracks: {},
},
};
(call as any).pushRemoteFeed(
const setupCall = (audio: boolean, video: boolean): void => {
(call as any).pushRemoteStream(
new MockMediaStream("stream", [
new MockMediaStreamTrack("track1", "audio"),
new MockMediaStreamTrack("track1", "video"),
]),
);
call.onSDPStreamMetadataChangedReceived({
getContent: () => ({
[SDPStreamMetadataKey]: metadata,
[SDPStreamMetadataKey]: {
stream: {
user_id: "user",
device_id: "device",
purpose: SDPStreamMetadataPurpose.Usermedia,
audio_muted: audio,
video_muted: video,
tracks: {},
},
},
}),
} as MatrixEvent);
return metadata;
};

it("should handle incoming sdp_stream_metadata_changed with audio muted", async () => {
const metadata = setupCall(true, false);
expect((call as any).remoteSDPStreamMetadata).toStrictEqual(metadata);
setupCall(true, false);
expect(call.opponentSupportsSDPStreamMetadata()).toBe(true);
expect(call.getRemoteFeeds()[0].isAudioMuted()).toBe(true);
expect(call.getRemoteFeeds()[0].isVideoMuted()).toBe(false);
});

it("should handle incoming sdp_stream_metadata_changed with video muted", async () => {
const metadata = setupCall(false, true);
expect((call as any).remoteSDPStreamMetadata).toStrictEqual(metadata);
setupCall(false, true);
expect(call.opponentSupportsSDPStreamMetadata()).toBe(true);
expect(call.getRemoteFeeds()[0].isAudioMuted()).toBe(false);
expect(call.getRemoteFeeds()[0].isVideoMuted()).toBe(true);
});
Expand Down Expand Up @@ -1394,8 +1395,8 @@ describe("Call", function () {

describe("onTrack", () => {
it("ignores streamless track", async () => {
// @ts-ignore Mock pushRemoteFeed() is private
jest.spyOn(call, "pushRemoteFeed");
// @ts-ignore Mock pushRemoteStream() is private
jest.spyOn(call, "pushRemoteStream");

await call.placeVoiceCall();

Expand All @@ -1404,13 +1405,13 @@ describe("Call", function () {
track: new MockMediaStreamTrack("track_ev", "audio"),
} as unknown as RTCTrackEvent);

// @ts-ignore Mock pushRemoteFeed() is private
expect(call.pushRemoteFeed).not.toHaveBeenCalled();
// @ts-ignore Mock pushRemoteStream() is private
expect(call.pushRemoteStream).not.toHaveBeenCalled();
});

it("correctly pushes", async () => {
// @ts-ignore Mock pushRemoteFeed() is private
jest.spyOn(call, "pushRemoteFeed");
// @ts-ignore Mock pushRemoteStream() is private
jest.spyOn(call, "pushRemoteStream");

await call.placeVoiceCall();
await call.onAnswerReceived(
Expand All @@ -1430,9 +1431,9 @@ describe("Call", function () {
track: stream.getAudioTracks()[0],
} as unknown as RTCTrackEvent);

// @ts-ignore Mock pushRemoteFeed() is private
expect(call.pushRemoteFeed).toHaveBeenCalledWith(stream);
// @ts-ignore Mock pushRemoteFeed() is private
// @ts-ignore Mock pushRemoteStream() is private
expect(call.pushRemoteStream).toHaveBeenCalledWith(stream);
// @ts-ignore Mock pushRemoteStream() is private
expect(call.removeTrackListeners.has(stream)).toBe(true);
});
});
Expand Down
2 changes: 2 additions & 0 deletions spec/unit/webrtc/callFeed.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ describe("CallFeed", () => {
[CallState.Connected, true],
[CallState.Connecting, false],
])("should react to call state, when !isLocal()", (state: CallState, expected: Boolean) => {
feed.stream?.addTrack(new MockMediaStreamTrack("track1", "video").typed());
call.state = state;
call.emit(CallEvent.State, state);

expect(feed.connected).toBe(expected);
Expand Down
15 changes: 8 additions & 7 deletions spec/unit/webrtc/groupCall.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,7 @@ describe("Group Call", function () {

await groupCall.setMicrophoneMuted(true);

groupCall.localCallFeed!.stream.getAudioTracks().forEach((track) => expect(track.enabled).toBe(false));
groupCall.localCallFeed!.stream!.getAudioTracks().forEach((track) => expect(track.enabled).toBe(false));
expect(groupCall.localCallFeed!.setAudioVideoMuted).toHaveBeenCalledWith(true, null);
setAVMutedArray.forEach((f) => expect(f).toHaveBeenCalledWith(true, null));
tracksArray.forEach((track) => expect(track.enabled).toBe(false));
Expand Down Expand Up @@ -835,9 +835,8 @@ describe("Group Call", function () {

await groupCall.setLocalVideoMuted(true);

groupCall.localCallFeed!.stream.getVideoTracks().forEach((track) => expect(track.enabled).toBe(false));
expect(mockClient.getMediaHandler().getUserMediaStream).toHaveBeenCalledWith(true, false);
expect(groupCall.updateLocalUsermediaStream).toHaveBeenCalled();
groupCall.localCallFeed!.stream!.getVideoTracks().forEach((track) => expect(track.enabled).toBe(false));
expect(groupCall.localCallFeed!.setAudioVideoMuted).toHaveBeenCalledWith(null, true);
setAVMutedArray.forEach((f) => expect(f).toHaveBeenCalledWith(null, true));
tracksArray.forEach((track) => expect(track.enabled).toBe(false));
sendMetadataUpdateArray.forEach((f) => expect(f).toHaveBeenCalled());
Expand Down Expand Up @@ -872,7 +871,7 @@ describe("Group Call", function () {
call.getOpponentMember = () => ({ userId: call.invitee } as RoomMember);
call.onSDPStreamMetadataChangedReceived(metadataEvent);
// @ts-ignore Mock
call.pushRemoteFeed(
call.pushRemoteStream(
// @ts-ignore Mock
new MockMediaStream("stream", [
new MockMediaStreamTrack("audio_track", "audio"),
Expand All @@ -899,7 +898,7 @@ describe("Group Call", function () {
call.getOpponentMember = () => ({ userId: call.invitee } as RoomMember);
call.onSDPStreamMetadataChangedReceived(metadataEvent);
// @ts-ignore Mock
call.pushRemoteFeed(
call.pushRemoteStream(
// @ts-ignore Mock
new MockMediaStream("stream", [
new MockMediaStreamTrack("audio_track", "audio"),
Expand Down Expand Up @@ -1157,7 +1156,7 @@ describe("Group Call", function () {
}),
} as MatrixEvent);
// @ts-ignore Mock
call.pushRemoteFeed(
call.pushRemoteStream(
// @ts-ignore Mock
new MockMediaStream("screensharing_stream", [new MockMediaStreamTrack("video_track", "video")]),
);
Expand Down Expand Up @@ -1211,6 +1210,7 @@ describe("Group Call", function () {
roomId: FAKE_ROOM_ID,
userId: FAKE_USER_ID_2,
deviceId: FAKE_DEVICE_ID_1,
feedId: "foo",
stream: new MockMediaStream("foo", []).typed(),
purpose: SDPStreamMetadataPurpose.Usermedia,
audioMuted: false,
Expand All @@ -1223,6 +1223,7 @@ describe("Group Call", function () {
roomId: FAKE_ROOM_ID,
userId: FAKE_USER_ID_3,
deviceId: FAKE_DEVICE_ID_1,
feedId: "foo",
stream: new MockMediaStream("foo", []).typed(),
purpose: SDPStreamMetadataPurpose.Usermedia,
audioMuted: false,
Expand Down
Loading