1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-08-09 10:22:46 +03:00

TS strict mode compliance in the call / groupcall code (#2805)

* TS strict mode compliance in the call / groupcall code

* Also the test

* Fix initOpponentCrypto

to not panic if it doesn't actually need to init crypto
This commit is contained in:
David Baker
2022-10-26 11:45:03 +01:00
committed by GitHub
parent 450ff00c3e
commit c374ba2367
9 changed files with 186 additions and 153 deletions

View File

@@ -99,7 +99,7 @@ describe("CallEventHandler", () => {
expect(callEventHandler.callEventBuffer.length).toBe(2);
expect(callEventHandler.nextSeqByCall.get("123")).toBe(2);
expect(callEventHandler.toDeviceEventBuffers.get("123").length).toBe(1);
expect(callEventHandler.toDeviceEventBuffers.get("123")?.length).toBe(1);
const event4 = new MatrixEvent({
type: EventType.CallCandidates,
@@ -112,7 +112,7 @@ describe("CallEventHandler", () => {
expect(callEventHandler.callEventBuffer.length).toBe(2);
expect(callEventHandler.nextSeqByCall.get("123")).toBe(2);
expect(callEventHandler.toDeviceEventBuffers.get("123").length).toBe(2);
expect(callEventHandler.toDeviceEventBuffers.get("123")?.length).toBe(2);
const event5 = new MatrixEvent({
type: EventType.CallCandidates,
@@ -125,7 +125,7 @@ describe("CallEventHandler", () => {
expect(callEventHandler.callEventBuffer.length).toBe(5);
expect(callEventHandler.nextSeqByCall.get("123")).toBe(5);
expect(callEventHandler.toDeviceEventBuffers.get("123").length).toBe(0);
expect(callEventHandler.toDeviceEventBuffers.get("123")?.length).toBe(0);
});
it("should ignore a call if invite & hangup come within a single sync", () => {
@@ -161,7 +161,7 @@ describe("CallEventHandler", () => {
it("should ignore non-call events", async () => {
// @ts-ignore Mock handleCallEvent is private
jest.spyOn(client.callEventHandler, "handleCallEvent");
jest.spyOn(client, "checkTurnServers").mockReturnValue(undefined);
jest.spyOn(client, "checkTurnServers").mockReturnValue(Promise.resolve(true));
const room = new Room("!room:id", client, "@user:id");
const timelineData: IRoomTimelineData = { timeline: new EventTimeline(new EventTimelineSet(room, {})) };
@@ -186,10 +186,10 @@ describe("CallEventHandler", () => {
let room: Room;
beforeEach(() => {
room = new Room("!room:id", client, client.getUserId());
room = new Room("!room:id", client, client.getUserId()!);
timelineData = { timeline: new EventTimeline(new EventTimelineSet(room, {})) };
jest.spyOn(client, "checkTurnServers").mockReturnValue(undefined);
jest.spyOn(client, "checkTurnServers").mockReturnValue(Promise.resolve(true));
jest.spyOn(client, "getRoom").mockReturnValue(room);
jest.spyOn(room, "getMember").mockReturnValue({ user_id: client.getUserId() } as unknown as RoomMember);
@@ -246,10 +246,10 @@ describe("CallEventHandler", () => {
await sync();
expect(incomingCallListener).toHaveBeenCalled();
expect(call.groupCallId).toBe(GROUP_CALL_ID);
expect(call!.groupCallId).toBe(GROUP_CALL_ID);
// @ts-ignore Mock opponentDeviceId is private
expect(call.opponentDeviceId).toBe(DEVICE_ID);
expect(call.getOpponentSessionId()).toBe(SESSION_ID);
expect(call!.getOpponentSessionId()).toBe(SESSION_ID);
// @ts-ignore Mock onIncomingCall is private
expect(groupCall.onIncomingCall).toHaveBeenCalledWith(call);

View File

@@ -116,8 +116,8 @@ class MockCall {
setAudioVideoMuted: jest.fn<void, [boolean, boolean]>(),
stream: new MockMediaStream("stream"),
};
public remoteUsermediaFeed: CallFeed;
public remoteScreensharingFeed: CallFeed;
public remoteUsermediaFeed?: CallFeed;
public remoteScreensharingFeed?: CallFeed;
public reject = jest.fn<void, []>();
public answerWithCallFeeds = jest.fn<void, [CallFeed[]]>();
@@ -128,7 +128,7 @@ class MockCall {
on = jest.fn();
removeListener = jest.fn();
getOpponentMember() {
getOpponentMember(): Partial<RoomMember> {
return {
userId: this.opponentUserId,
};
@@ -276,7 +276,7 @@ describe('Group Call', function() {
await groupCall.initLocalCallFeed();
const oldStream = groupCall.localCallFeed.stream as unknown as MockMediaStream;
const oldStream = groupCall.localCallFeed?.stream as unknown as MockMediaStream;
// arbitrary values, important part is that they're the same afterwards
await groupCall.setLocalVideoMuted(true);
@@ -286,7 +286,7 @@ describe('Group Call', function() {
groupCall.updateLocalUsermediaStream(newStream);
expect(groupCall.localCallFeed.stream).toBe(newStream);
expect(groupCall.localCallFeed?.stream).toBe(newStream);
expect(groupCall.isLocalVideoMuted()).toEqual(true);
expect(groupCall.isMicrophoneMuted()).toEqual(false);
@@ -474,7 +474,7 @@ describe('Group Call', function() {
// we should still be muted at this point because the metadata update hasn't sent
expect(groupCall.isMicrophoneMuted()).toEqual(true);
expect(mockCall.localUsermediaFeed.setAudioVideoMuted).not.toHaveBeenCalled();
metadataUpdateResolve();
metadataUpdateResolve!();
await mutePromise;
@@ -500,7 +500,7 @@ describe('Group Call', function() {
// we should be muted at this point, before the metadata update has been sent
expect(groupCall.isMicrophoneMuted()).toEqual(true);
expect(mockCall.localUsermediaFeed.setAudioVideoMuted).toHaveBeenCalled();
metadataUpdateResolve();
metadataUpdateResolve!();
await mutePromise;
@@ -550,7 +550,7 @@ describe('Group Call', function() {
groupCall1.onMemberStateChanged(fakeEvent);
groupCall2.onMemberStateChanged(fakeEvent);
}
return Promise.resolve(null);
return Promise.resolve({ "event_id": "foo" });
};
client1.sendStateEvent.mockImplementation(fakeSendStateEvents);
@@ -644,7 +644,7 @@ describe('Group Call', function() {
expect(client1.sendToDevice).toHaveBeenCalled();
const oldCall = groupCall1.getCallByUserId(client2.userId);
oldCall.emit(CallEvent.Hangup, oldCall);
oldCall!.emit(CallEvent.Hangup, oldCall!);
client1.sendToDevice.mockClear();
@@ -660,11 +660,11 @@ describe('Group Call', function() {
// when we placed the call, we could await on enter which waited for the call to
// be made. We don't have that luxury now, so first have to wait for the call
// to even be created...
let newCall: MatrixCall;
let newCall: MatrixCall | undefined;
while (
(newCall = groupCall1.getCallByUserId(client2.userId)) === undefined ||
newCall.peerConn === undefined ||
newCall.callId == oldCall.callId
newCall.callId == oldCall!.callId
) {
await flushPromises();
}
@@ -704,7 +704,7 @@ describe('Group Call', function() {
groupCall1.setMicrophoneMuted(false);
groupCall1.setLocalVideoMuted(false);
const call = groupCall1.getCallByUserId(client2.userId);
const call = groupCall1.getCallByUserId(client2.userId)!;
call.isMicrophoneMuted = jest.fn().mockReturnValue(true);
call.setMicrophoneMuted = jest.fn();
call.isLocalVideoMuted = jest.fn().mockReturnValue(true);
@@ -743,13 +743,13 @@ describe('Group Call', function() {
it("should mute local audio when calling setMicrophoneMuted()", async () => {
const groupCall = await createAndEnterGroupCall(mockClient, room);
groupCall.localCallFeed.setAudioVideoMuted = jest.fn();
groupCall.localCallFeed!.setAudioVideoMuted = jest.fn();
const setAVMutedArray = groupCall.calls.map(call => {
call.localUsermediaFeed.setAudioVideoMuted = jest.fn();
return call.localUsermediaFeed.setAudioVideoMuted;
call.localUsermediaFeed!.setAudioVideoMuted = jest.fn();
return call.localUsermediaFeed!.setAudioVideoMuted;
});
const tracksArray = groupCall.calls.reduce((acc, call) => {
acc.push(...call.localUsermediaStream.getAudioTracks());
const tracksArray = groupCall.calls.reduce((acc: MediaStreamTrack[], call: MatrixCall) => {
acc.push(...call.localUsermediaStream!.getAudioTracks());
return acc;
}, []);
const sendMetadataUpdateArray = groupCall.calls.map(call => {
@@ -759,8 +759,8 @@ describe('Group Call', function() {
await groupCall.setMicrophoneMuted(true);
groupCall.localCallFeed.stream.getAudioTracks().forEach(track => expect(track.enabled).toBe(false));
expect(groupCall.localCallFeed.setAudioVideoMuted).toHaveBeenCalledWith(true, null);
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));
sendMetadataUpdateArray.forEach(f => expect(f).toHaveBeenCalled());
@@ -771,14 +771,14 @@ describe('Group Call', function() {
it("should mute local video when calling setLocalVideoMuted()", async () => {
const groupCall = await createAndEnterGroupCall(mockClient, room);
groupCall.localCallFeed.setAudioVideoMuted = jest.fn();
groupCall.localCallFeed!.setAudioVideoMuted = jest.fn();
const setAVMutedArray = groupCall.calls.map(call => {
call.localUsermediaFeed.setAudioVideoMuted = jest.fn();
call.localUsermediaFeed.isVideoMuted = jest.fn().mockReturnValue(true);
return call.localUsermediaFeed.setAudioVideoMuted;
call.localUsermediaFeed!.setAudioVideoMuted = jest.fn();
call.localUsermediaFeed!.isVideoMuted = jest.fn().mockReturnValue(true);
return call.localUsermediaFeed!.setAudioVideoMuted;
});
const tracksArray = groupCall.calls.reduce((acc, call) => {
acc.push(...call.localUsermediaStream.getVideoTracks());
const tracksArray = groupCall.calls.reduce((acc: MediaStreamTrack[], call: MatrixCall) => {
acc.push(...call.localUsermediaStream!.getVideoTracks());
return acc;
}, []);
const sendMetadataUpdateArray = groupCall.calls.map(call => {
@@ -788,8 +788,8 @@ describe('Group Call', function() {
await groupCall.setLocalVideoMuted(true);
groupCall.localCallFeed.stream.getVideoTracks().forEach(track => expect(track.enabled).toBe(false));
expect(groupCall.localCallFeed.setAudioVideoMuted).toHaveBeenCalledWith(null, true);
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());
@@ -827,9 +827,9 @@ describe('Group Call', function() {
]));
call.onSDPStreamMetadataChangedReceived(metadataEvent);
const feed = groupCall.getUserMediaFeedByUserId(call.invitee);
expect(feed.isAudioMuted()).toBe(true);
expect(feed.isVideoMuted()).toBe(false);
const feed = groupCall.getUserMediaFeedByUserId(call.invitee!);
expect(feed!.isAudioMuted()).toBe(true);
expect(feed!.isVideoMuted()).toBe(false);
groupCall.terminate();
});
@@ -850,9 +850,9 @@ describe('Group Call', function() {
]));
call.onSDPStreamMetadataChangedReceived(metadataEvent);
const feed = groupCall.getUserMediaFeedByUserId(call.invitee);
expect(feed.isAudioMuted()).toBe(false);
expect(feed.isVideoMuted()).toBe(true);
const feed = groupCall.getUserMediaFeedByUserId(call.invitee!);
expect(feed!.isAudioMuted()).toBe(false);
expect(feed!.isVideoMuted()).toBe(true);
groupCall.terminate();
});

View File

@@ -38,7 +38,7 @@ export const acquireContext = (): AudioContext => {
export const releaseContext = () => {
refCount--;
if (refCount === 0) {
audioContext.close();
audioContext?.close();
audioContext = null;
}
};

View File

@@ -331,7 +331,7 @@ function getTransceiverKey(purpose: SDPStreamMetadataPurpose, kind: TransceiverK
* @param {MatrixClient} opts.client The Matrix Client instance to send events to.
*/
export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap> {
public roomId: string;
public roomId?: string;
public callId: string;
public invitee?: string;
public state = CallState.Fledgling;
@@ -361,15 +361,15 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
private transceivers = new Map<TransceiverKey, RTCRtpTransceiver>();
private inviteOrAnswerSent = false;
private waitForLocalAVStream: boolean;
private waitForLocalAVStream = false;
private successor?: MatrixCall;
private opponentMember?: RoomMember;
private opponentVersion?: number | string;
// The party ID of the other side: undefined if we haven't chosen a partner
// yet, null if we have but they didn't send a party ID.
private opponentPartyId: string | null;
private opponentCaps: CallCapabilities;
private iceDisconnectedTimeout: ReturnType<typeof setTimeout>;
private opponentPartyId: string | null | undefined;
private opponentCaps?: CallCapabilities;
private iceDisconnectedTimeout?: ReturnType<typeof setTimeout>;
private inviteTimeout?: ReturnType<typeof setTimeout>;
private readonly removeTrackListeners = new Map<MediaStream, () => void>();
@@ -384,7 +384,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
// Perfect negotiation state: https://www.w3.org/TR/webrtc/#perfect-negotiation-example
private makingOffer = false;
private ignoreOffer: boolean;
private ignoreOffer = false;
private responsePromiseChain?: Promise<void>;
@@ -399,17 +399,21 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
private callLengthInterval?: ReturnType<typeof setInterval>;
private callLength = 0;
private opponentDeviceId: string;
private opponentDeviceInfo: DeviceInfo;
private opponentSessionId: string;
public groupCallId: string;
private opponentDeviceId?: string;
private opponentDeviceInfo?: DeviceInfo;
private opponentSessionId?: string;
public groupCallId?: string;
constructor(opts: CallOpts) {
super();
this.roomId = opts.roomId;
this.invitee = opts.invitee;
this.client = opts.client;
this.forceTURN = opts.forceTURN;
if (!this.client.deviceId) throw new Error("Client must have a device ID to start calls");
this.forceTURN = opts.forceTURN ?? false;
this.ourPartyId = this.client.deviceId;
this.opponentDeviceId = opts.opponentDeviceId;
this.opponentSessionId = opts.opponentSessionId;
@@ -448,7 +452,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
* @param label A human readable label for this datachannel
* @param options An object providing configuration options for the data channel.
*/
public createDataChannel(label: string, options: RTCDataChannelInit) {
public createDataChannel(label: string, options: RTCDataChannelInit | undefined) {
const dataChannel = this.peerConn!.createDataChannel(label, options);
this.emit(CallEvent.DataChannel, dataChannel);
return dataChannel;
@@ -458,7 +462,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
return this.opponentMember;
}
public getOpponentSessionId(): string {
public getOpponentSessionId(): string | undefined {
return this.opponentSessionId;
}
@@ -570,8 +574,13 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
this.opponentDeviceInfo = new DeviceInfo(this.opponentDeviceId);
return;
}
// if we've got to this point, we do want to init crypto, so throw if we can't
if (!this.client.crypto) throw new Error("Crypto is not initialised.");
const userId = this.invitee || this.getOpponentMember()?.userId;
if (!userId) throw new Error("Couldn't find opponent user ID to init crypto");
const userId = this.invitee || this.getOpponentMember().userId;
const deviceInfoMap = await this.client.crypto.deviceList.downloadKeys([userId], false);
this.opponentDeviceInfo = deviceInfoMap[userId][this.opponentDeviceId];
if (this.opponentDeviceInfo === undefined) {
@@ -749,7 +758,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
// accumulate which makes the SDP very large very quickly: in fact it only takes
// about 6 video tracks to exceed the maximum size of an Olm-encrypted
// Matrix event.
const transceiver = this.transceivers.get(tKey);
const transceiver = this.transceivers.get(tKey)!;
// this is what would allow us to use addTransceiver(), but it's not available
// on Firefox yet. We call it anyway if we have it.
@@ -764,10 +773,15 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
// doesn't yet implement RTCRTPSender.setStreams()
// (https://bugzilla.mozilla.org/show_bug.cgi?id=1510802) so we'd have no way to group the
// two tracks together into a stream.
const newSender = this.peerConn.addTrack(track, callFeed.stream);
const newSender = this.peerConn!.addTrack(track, callFeed.stream);
// now go & fish for the new transceiver
this.transceivers.set(tKey, this.peerConn.getTransceivers().find(t => t.sender === newSender));
const newTransciever = this.peerConn!.getTransceivers().find(t => t.sender === newSender);
if (newTransciever) {
this.transceivers.set(tKey, newTransciever);
} else {
logger.warn("Didn't find a matching transceiver after adding track!");
}
}
}
}
@@ -797,8 +811,8 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
// There is no way to actually remove a transceiver, so this just sets it to inactive
// (or recvonly) and replaces the source with nothing.
if (this.transceivers.has(transceiverKey)) {
const transceiver = this.transceivers.get(transceiverKey);
if (transceiver.sender) this.peerConn.removeTrack(transceiver.sender);
const transceiver = this.transceivers.get(transceiverKey)!;
if (transceiver.sender) this.peerConn!.removeTrack(transceiver.sender);
}
}
@@ -850,7 +864,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
if (!this.peerConn) return;
const statsReport = await this.peerConn.getStats();
const stats = [];
const stats: any[] = [];
statsReport.forEach(item => {
stats.push(item);
});
@@ -917,8 +931,8 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
this.hangupParty = CallParty.Remote; // effectively
this.setState(CallState.Ended);
this.stopAllMedia();
if (this.peerConn.signalingState != 'closed') {
this.peerConn.close();
if (this.peerConn!.signalingState != 'closed') {
this.peerConn!.close();
}
this.emit(CallEvent.Hangup, this);
}
@@ -947,7 +961,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
private shouldAnswerWithMediaType(
wantedValue: boolean | undefined,
valueOfTheOtherSide: boolean | undefined,
valueOfTheOtherSide: boolean,
type: "audio" | "video",
): boolean {
if (wantedValue && !valueOfTheOtherSide) {
@@ -1186,7 +1200,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
for (const transceiver of [audioTransceiver, videoTransceiver]) {
// this is slightly mixing the track and transceiver API but is basically just shorthand
// for removing the sender.
if (transceiver && transceiver.sender) this.peerConn.removeTrack(transceiver.sender);
if (transceiver && transceiver.sender) this.peerConn!.removeTrack(transceiver.sender);
}
this.client.getMediaHandler().stopScreensharingStream(this.localScreensharingStream!);
@@ -1215,9 +1229,9 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
const sender = this.transceivers.get(getTransceiverKey(
SDPStreamMetadataPurpose.Usermedia, "video",
)).sender;
))?.sender;
sender?.replaceTrack(track);
sender?.replaceTrack(track ?? null);
this.pushNewLocalFeed(stream, SDPStreamMetadataPurpose.Screenshare, false);
@@ -1230,8 +1244,8 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
const track = this.localUsermediaStream?.getTracks().find((track) => track.kind === "video");
const sender = this.transceivers.get(getTransceiverKey(
SDPStreamMetadataPurpose.Usermedia, "video",
)).sender;
sender?.replaceTrack(track);
))?.sender;
sender?.replaceTrack(track ?? null);
this.client.getMediaHandler().stopScreensharingStream(this.localScreensharingStream!);
this.deleteFeedByStream(this.localScreensharingStream!);
@@ -1298,8 +1312,13 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
`) to peer connection`,
);
const newSender = this.peerConn!.addTrack(track, this.localUsermediaStream);
this.transceivers.set(tKey, this.peerConn.getTransceivers().find(t => t.sender === newSender));
const newSender = this.peerConn!.addTrack(track, this.localUsermediaStream!);
const newTransciever = this.peerConn!.getTransceivers().find(t => t.sender === newSender);
if (newTransciever) {
this.transceivers.set(tKey, newTransciever);
} else {
logger.warn("Couldn't find matching transceiver for newly added track!");
}
}
}
}
@@ -1436,7 +1455,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
const micShouldBeMuted = this.isMicrophoneMuted() || this.remoteOnHold;
const vidShouldBeMuted = this.isLocalVideoMuted() || this.remoteOnHold;
logger.log(`call ${this.callId} updateMuteStatus stream ${this.localUsermediaStream.id} micShouldBeMuted ${
logger.log(`call ${this.callId} updateMuteStatus stream ${this.localUsermediaStream!.id} micShouldBeMuted ${
micShouldBeMuted} vidShouldBeMuted ${vidShouldBeMuted}`);
setTracksEnabled(this.localUsermediaStream!.getAudioTracks(), !micShouldBeMuted);
setTracksEnabled(this.localUsermediaStream!.getVideoTracks(), !vidShouldBeMuted);
@@ -1463,7 +1482,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
}
if (requestScreenshareFeed) {
this.peerConn.addTransceiver("video", {
this.peerConn!.addTransceiver("video", {
direction: "recvonly",
});
}
@@ -1535,7 +1554,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
// bandwidth when transmitting silence
private mungeSdp(description: RTCSessionDescriptionInit, mods: CodecParamsMod[]): void {
// The only way to enable DTX at this time is through SDP munging
const sdp = parseSdp(description.sdp);
const sdp = parseSdp(description.sdp!);
sdp.media.forEach(media => {
const payloadTypeToCodecMap = new Map<number, string>();
@@ -1570,7 +1589,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
}
if (!found) {
media.fmtp.push({
payload: codecToPayloadTypeMap.get(mod.codec),
payload: codecToPayloadTypeMap.get(mod.codec)!,
config: extraconfig.join(";"),
});
}
@@ -1580,13 +1599,13 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
}
private async createOffer(): Promise<RTCSessionDescriptionInit> {
const offer = await this.peerConn.createOffer();
const offer = await this.peerConn!.createOffer();
this.mungeSdp(offer, getCodecParamMods(this.isPtt));
return offer;
}
private async createAnswer(): Promise<RTCSessionDescriptionInit> {
const answer = await this.peerConn.createAnswer();
const answer = await this.peerConn!.createAnswer();
this.mungeSdp(answer, getCodecParamMods(this.isPtt));
return answer;
}
@@ -1665,7 +1684,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
};
private onIceGatheringStateChange = (event: Event): void => {
logger.debug(`Call ${this.callId} ice gathering state changed to ${this.peerConn.iceGatheringState}`);
logger.debug(`Call ${this.callId} ice gathering state changed to ${this.peerConn!.iceGatheringState}`);
if (this.peerConn?.iceGatheringState === 'complete') {
this.queueCandidate(null);
}
@@ -1688,10 +1707,12 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
if (this.opponentPartyId === undefined) {
// we haven't picked an opponent yet so save the candidates
logger.info(`Call ${this.callId} Buffering ${candidates.length} candidates until we pick an opponent`);
const bufferedCandidates = this.remoteCandidateBuffer.get(fromPartyId) || [];
bufferedCandidates.push(...candidates);
this.remoteCandidateBuffer.set(fromPartyId, bufferedCandidates);
if (fromPartyId) {
logger.info(`Call ${this.callId} Buffering ${candidates.length} candidates until we pick an opponent`);
const bufferedCandidates = this.remoteCandidateBuffer.get(fromPartyId) || [];
bufferedCandidates.push(...candidates);
this.remoteCandidateBuffer.set(fromPartyId, bufferedCandidates);
}
return;
}
@@ -1905,7 +1926,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
try {
await this.gotLocalOffer();
} catch (e) {
this.getLocalOfferFailed(e);
this.getLocalOfferFailed(e as Error);
return;
} finally {
this.makingOffer = false;
@@ -1932,7 +1953,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
}
try {
await this.peerConn.setLocalDescription(offer);
await this.peerConn!.setLocalDescription(offer);
} catch (err) {
logger.debug(`Call ${this.callId} Error setting local description!`, err);
this.terminate(CallParty.Local, CallErrorCode.SetLocalDescription, true);
@@ -2057,7 +2078,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
// ideally we'd consider the call to be connected when we get media but
// chrome doesn't implement any of the 'onstarted' events yet
if (["connected", "completed"].includes(this.peerConn?.iceConnectionState)) {
if (["connected", "completed"].includes(this.peerConn?.iceConnectionState ?? '')) {
clearTimeout(this.iceDisconnectedTimeout);
this.setState(CallState.Connected);
@@ -2069,7 +2090,9 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
}
} else if (this.peerConn?.iceConnectionState == 'failed') {
// Firefox for Android does not yet have support for restartIce()
if (this.peerConn?.restartIce) {
// (the types say it's always defined though, so we have to cast
// to prevent typescript from warning).
if (this.peerConn?.restartIce as (() => void) | null) {
this.candidatesEnded = false;
this.peerConn!.restartIce();
} else {
@@ -2085,7 +2108,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
// the peer, since we don't want to block the line if they're not saying anything.
// Experimenting in Chrome, this happens after 5 or 6 seconds, which is probably
// fast enough.
if (this.isPtt && ["failed", "disconnected"].includes(this.peerConn.iceConnectionState)) {
if (this.isPtt && ["failed", "disconnected"].includes(this.peerConn!.iceConnectionState)) {
for (const feed of this.getRemoteFeeds()) {
feed.setAudioVideoMuted(true, true);
}
@@ -2243,16 +2266,16 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
this.emit(CallEvent.SendVoipEvent, {
type: "toDevice",
eventType,
userId: this.invitee || this.getOpponentMember().userId,
userId: this.invitee || this.getOpponentMember()?.userId,
opponentDeviceId: this.opponentDeviceId,
content,
});
const userId = this.invitee || this.getOpponentMember().userId;
const userId = this.invitee || this.getOpponentMember()!.userId;
if (this.client.getUseE2eForGroupCall()) {
await this.client.encryptAndSendToDevices([{
userId,
deviceInfo: this.opponentDeviceInfo,
deviceInfo: this.opponentDeviceInfo!,
}], {
type: eventType,
content,
@@ -2273,7 +2296,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
userId: this.invitee || this.getOpponentMember()?.userId,
});
await this.client.sendEvent(this.roomId, eventType, realContent);
await this.client.sendEvent(this.roomId!, eventType, realContent);
}
}
@@ -2322,7 +2345,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
// Call this method before sending an invite or answer message
private discardDuplicateCandidates(): number {
let discardCount = 0;
const newQueue = [];
const newQueue: RTCIceCandidate[] = [];
for (let i = 0; i < this.candidateSendQueue.length; i++) {
const candidate = this.candidateSendQueue[i];
@@ -2647,7 +2670,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
this.opponentPartyId = msg.party_id || null;
}
this.opponentCaps = msg.capabilities || {} as CallCapabilities;
this.opponentMember = this.client.getRoom(this.roomId).getMember(ev.getSender());
this.opponentMember = this.client.getRoom(this.roomId)!.getMember(ev.getSender()) ?? undefined;
}
private async addBufferedIceCandidates(): Promise<void> {

View File

@@ -16,7 +16,7 @@ limitations under the License.
import { MatrixEvent } from '../models/event';
import { logger } from '../logger';
import { CallDirection, CallErrorCode, CallState, createNewMatrixCall, MatrixCall } from './call';
import { CallDirection, CallError, CallErrorCode, CallState, createNewMatrixCall, MatrixCall } from './call';
import { EventType } from '../@types/event';
import { ClientEvent, MatrixClient } from '../client';
import { MCallAnswer, MCallHangupReject } from "./callEventTypes";
@@ -152,7 +152,7 @@ export class CallEventHandler {
this.toDeviceEventBuffers.set(content.call_id, []);
}
const buffer = this.toDeviceEventBuffers.get(content.call_id);
const buffer = this.toDeviceEventBuffers.get(content.call_id)!;
const index = buffer.findIndex((e) => e.getContent().seq > content.seq);
if (index === -1) {
@@ -172,7 +172,7 @@ export class CallEventHandler {
while (nextEvent && nextEvent.getContent().seq === this.nextSeqByCall.get(callId)) {
this.callEventBuffer.push(nextEvent);
this.nextSeqByCall.set(callId, nextEvent.getContent().seq + 1);
nextEvent = buffer.shift();
nextEvent = buffer!.shift();
}
}
};
@@ -194,7 +194,7 @@ export class CallEventHandler {
let opponentDeviceId: string | undefined;
let groupCall: GroupCall;
let groupCall: GroupCall | undefined;
if (groupCallId) {
groupCall = this.client.groupCallEventHandler.getGroupCallById(groupCallId);
@@ -241,7 +241,7 @@ export class CallEventHandler {
return; // This invite was meant for another user in the room
}
const timeUntilTurnCresExpire = this.client.getTurnServersExpiry() - Date.now();
const timeUntilTurnCresExpire = (this.client.getTurnServersExpiry() ?? 0) - Date.now();
logger.info("Current turn creds expire in " + timeUntilTurnCresExpire + " ms");
call = createNewMatrixCall(
this.client,
@@ -267,10 +267,12 @@ export class CallEventHandler {
try {
await call.initWithInvite(event);
} catch (e) {
if (e.code === GroupCallErrorCode.UnknownDevice) {
groupCall?.emit(GroupCallEvent.Error, e);
} else {
logger.error(e);
if (e instanceof CallError) {
if (e.code === GroupCallErrorCode.UnknownDevice) {
groupCall?.emit(GroupCallEvent.Error, e);
} else {
logger.error(e);
}
}
}
this.calls.set(call.callId, call);
@@ -292,7 +294,7 @@ export class CallEventHandler {
if (
call.roomId === thisCall.roomId &&
thisCall.direction === CallDirection.Outbound &&
call.getOpponentMember().userId === thisCall.invitee &&
call.getOpponentMember()?.userId === thisCall.invitee &&
isCalling
) {
existingCall = thisCall;

View File

@@ -27,7 +27,7 @@ const SPEAKING_SAMPLE_COUNT = 8; // samples
export interface ICallFeedOpts {
client: MatrixClient;
roomId: string;
roomId?: string;
userId: string;
stream: MediaStream;
purpose: SDPStreamMetadataPurpose;
@@ -67,7 +67,7 @@ export class CallFeed extends TypedEventEmitter<CallFeedEvent, EventHandlerMap>
public speakingVolumeSamples: number[];
private client: MatrixClient;
private roomId: string;
private roomId?: string;
private audioMuted: boolean;
private videoMuted: boolean;
private localVolume = 1;
@@ -295,8 +295,8 @@ export class CallFeed extends TypedEventEmitter<CallFeedEvent, EventHandlerMap>
clearTimeout(this.volumeLooperTimeout);
this.stream?.removeEventListener("addtrack", this.onAddTrack);
if (this.audioContext) {
this.audioContext = null;
this.analyser = null;
this.audioContext = undefined;
this.analyser = undefined;
releaseContext();
}
this._disposed = true;

View File

@@ -9,6 +9,7 @@ import { CallErrorCode,
MatrixCall,
setTracksEnabled,
createNewMatrixCall,
CallError,
} from "./call";
import { RoomMember } from "../models/room-member";
import { Room } from "../models/room";
@@ -56,7 +57,7 @@ export type GroupCallEventHandlerMap = {
[GroupCallEvent.UserMediaFeedsChanged]: (feeds: CallFeed[]) => void;
[GroupCallEvent.ScreenshareFeedsChanged]: (feeds: CallFeed[]) => void;
[GroupCallEvent.LocalScreenshareStateChanged]: (
isScreensharing: boolean, feed: CallFeed, sourceId: string,
isScreensharing: boolean, feed?: CallFeed, sourceId?: string,
) => void;
[GroupCallEvent.LocalMuteStateChanged]: (audioMuted: boolean, videoMuted: boolean) => void;
[GroupCallEvent.ParticipantsChanged]: (participants: RoomMember[]) => void;
@@ -136,7 +137,7 @@ export enum GroupCallState {
interface ICallHandlers {
onCallFeedsChanged: (feeds: CallFeed[]) => void;
onCallStateChanged: (state: CallState, oldState: CallState) => void;
onCallStateChanged: (state: CallState, oldState: CallState | undefined) => void;
onCallHangup: (call: MatrixCall) => void;
onCallReplaced: (newCall: MatrixCall) => void;
}
@@ -232,7 +233,7 @@ export class GroupCall extends TypedEventEmitter<
}
public getLocalFeeds(): CallFeed[] {
const feeds = [];
const feeds: CallFeed[] = [];
if (this.localCallFeed) feeds.push(this.localCallFeed);
if (this.localScreenshareFeed) feeds.push(this.localScreenshareFeed);
@@ -311,11 +312,11 @@ export class GroupCall extends TypedEventEmitter<
await this.initLocalCallFeed();
}
this.addParticipant(this.room.getMember(this.client.getUserId()));
this.addParticipant(this.room.getMember(this.client.getUserId()!)!);
await this.sendMemberStateEvent();
this.activeSpeaker = null;
this.activeSpeaker = undefined;
this.setState(GroupCallState.Entered);
@@ -343,7 +344,7 @@ export class GroupCall extends TypedEventEmitter<
private dispose() {
if (this.localCallFeed) {
this.removeUserMediaFeed(this.localCallFeed);
this.localCallFeed = null;
this.localCallFeed = undefined;
}
if (this.localScreenshareFeed) {
@@ -359,7 +360,7 @@ export class GroupCall extends TypedEventEmitter<
return;
}
this.removeParticipant(this.room.getMember(this.client.getUserId()));
this.removeParticipant(this.room.getMember(this.client.getUserId()!)!);
this.removeMemberStateEvent();
@@ -367,7 +368,7 @@ export class GroupCall extends TypedEventEmitter<
this.removeCall(this.calls[this.calls.length - 1], CallErrorCode.UserHangup);
}
this.activeSpeaker = null;
this.activeSpeaker = undefined;
clearTimeout(this.activeSpeakerLoopTimeout);
this.retryCallCounts.clear();
@@ -470,7 +471,7 @@ export class GroupCall extends TypedEventEmitter<
this.setMicrophoneMuted(true);
}, this.pttMaxTransmitTime);
} else if (muted && !this.isMicrophoneMuted()) {
clearTimeout(this.transmitTimer);
if (this.transmitTimer !== null) clearTimeout(this.transmitTimer);
this.transmitTimer = null;
}
}
@@ -502,7 +503,7 @@ export class GroupCall extends TypedEventEmitter<
}
for (const call of this.calls) {
setTracksEnabled(call.localUsermediaFeed.stream.getAudioTracks(), !muted);
setTracksEnabled(call.localUsermediaFeed!.stream.getAudioTracks(), !muted);
}
this.emit(GroupCallEvent.LocalMuteStateChanged, muted, this.isLocalVideoMuted());
@@ -576,7 +577,7 @@ export class GroupCall extends TypedEventEmitter<
this.localScreenshareFeed = new CallFeed({
client: this.client,
roomId: this.room.roomId,
userId: this.client.getUserId(),
userId: this.client.getUserId()!,
stream,
purpose: SDPStreamMetadataPurpose.Screenshare,
audioMuted: false,
@@ -593,7 +594,7 @@ export class GroupCall extends TypedEventEmitter<
// TODO: handle errors
await Promise.all(this.calls.map(call => call.pushLocalFeed(
this.localScreenshareFeed.clone(),
this.localScreenshareFeed!.clone(),
)));
await this.sendMemberStateEvent();
@@ -603,7 +604,10 @@ export class GroupCall extends TypedEventEmitter<
if (opts.throwOnFail) throw error;
logger.error("Enabling screensharing error", error);
this.emit(GroupCallEvent.Error,
new GroupCallError(GroupCallErrorCode.NoUserMedia, "Failed to get screen-sharing stream: ", error),
new GroupCallError(
GroupCallErrorCode.NoUserMedia,
"Failed to get screen-sharing stream: ", error as Error,
),
);
return false;
}
@@ -611,8 +615,8 @@ export class GroupCall extends TypedEventEmitter<
await Promise.all(this.calls.map(call => {
if (call.localScreensharingFeed) call.removeLocalFeed(call.localScreensharingFeed);
}));
this.client.getMediaHandler().stopScreensharingStream(this.localScreenshareFeed.stream);
this.removeScreenshareFeed(this.localScreenshareFeed);
this.client.getMediaHandler().stopScreensharingStream(this.localScreenshareFeed!.stream);
this.removeScreenshareFeed(this.localScreenshareFeed!);
this.localScreenshareFeed = undefined;
this.localDesktopCapturerSourceId = undefined;
await this.sendMemberStateEvent();
@@ -652,8 +656,8 @@ export class GroupCall extends TypedEventEmitter<
return;
}
const opponentMemberId = newCall.getOpponentMember().userId;
const existingCall = this.getCallByUserId(opponentMemberId);
const opponentMemberId = newCall.getOpponentMember()?.userId;
const existingCall = opponentMemberId ? this.getCallByUserId(opponentMemberId) : null;
if (existingCall && existingCall.callId === newCall.callId) {
return;
@@ -709,7 +713,7 @@ export class GroupCall extends TypedEventEmitter<
const res = await send();
// Clear the old interval first, so that it isn't forgot
clearInterval(this.resendMemberStateTimer);
if (this.resendMemberStateTimer !== null) clearInterval(this.resendMemberStateTimer);
// Resend the state event every so often so it doesn't become stale
this.resendMemberStateTimer = setInterval(async () => {
logger.log("Resending call member state");
@@ -720,13 +724,13 @@ export class GroupCall extends TypedEventEmitter<
}
private async removeMemberStateEvent(): Promise<ISendEventResponse> {
clearInterval(this.resendMemberStateTimer);
if (this.resendMemberStateTimer !== null) clearInterval(this.resendMemberStateTimer);
this.resendMemberStateTimer = null;
return await this.updateMemberCallState(undefined);
}
private async updateMemberCallState(memberCallState?: IGroupCallRoomMemberCallState): Promise<ISendEventResponse> {
const localUserId = this.client.getUserId();
const localUserId = this.client.getUserId()!;
const memberState = this.getMemberStateEvents(localUserId)?.getContent<IGroupCallRoomMemberState>();
@@ -766,7 +770,7 @@ export class GroupCall extends TypedEventEmitter<
// The member events may be received for another room, which we will ignore.
if (event.getRoomId() !== this.room.roomId) return;
const member = this.room.getMember(event.getStateKey());
const member = this.room.getMember(event.getStateKey()!);
if (!member) {
logger.warn(`Couldn't find room member for ${event.getStateKey()}: ignoring member state event!`);
return;
@@ -816,7 +820,7 @@ export class GroupCall extends TypedEventEmitter<
}, content["m.expires_ts"] - Date.now()));
// Don't process your own member.
const localUserId = this.client.getUserId();
const localUserId = this.client.getUserId()!;
if (member.userId === localUserId) {
return;
@@ -860,6 +864,11 @@ export class GroupCall extends TypedEventEmitter<
},
);
if (!newCall) {
logger.error("Failed to create call!");
return;
}
if (existingCall) {
logger.debug(`Replacing call ${existingCall.callId} to ${member.userId} with ${newCall.callId}`);
this.replaceCall(existingCall, newCall, CallErrorCode.NewSession);
@@ -884,7 +893,7 @@ export class GroupCall extends TypedEventEmitter<
);
} catch (e) {
logger.warn(`Failed to place call to ${member.userId}!`, e);
if (e.code === GroupCallErrorCode.UnknownDevice) {
if (e instanceof CallError && e.code === GroupCallErrorCode.UnknownDevice) {
this.emit(GroupCallEvent.Error, e);
} else {
this.emit(
@@ -904,7 +913,7 @@ export class GroupCall extends TypedEventEmitter<
}
};
public getDeviceForMember(userId: string): IGroupCallRoomMemberDevice {
public getDeviceForMember(userId: string): IGroupCallRoomMemberDevice | undefined {
const memberStateEvent = this.getMemberStateEvents(userId);
if (!memberStateEvent) {
@@ -931,7 +940,7 @@ export class GroupCall extends TypedEventEmitter<
private onRetryCallLoop = () => {
for (const event of this.getMemberStateEvents()) {
const memberId = event.getStateKey();
const memberId = event.getStateKey()!;
const existingCall = this.calls.find((call) => getCallUserId(call) === memberId);
const retryCallCount = this.retryCallCounts.get(memberId) || 0;
@@ -948,7 +957,7 @@ export class GroupCall extends TypedEventEmitter<
* Call Event Handlers
*/
public getCallByUserId(userId: string): MatrixCall {
public getCallByUserId(userId: string): MatrixCall | undefined {
return this.calls.find((call) => getCallUserId(call) === userId);
}
@@ -996,7 +1005,7 @@ export class GroupCall extends TypedEventEmitter<
const onCallFeedsChanged = () => this.onCallFeedsChanged(call);
const onCallStateChanged =
(state: CallState, oldState: CallState) => this.onCallStateChanged(call, state, oldState);
(state: CallState, oldState: CallState | undefined) => this.onCallStateChanged(call, state, oldState);
const onCallHangup = this.onCallHangup;
const onCallReplaced = (newCall: MatrixCall) => this.replaceCall(call, newCall);
@@ -1029,7 +1038,7 @@ export class GroupCall extends TypedEventEmitter<
onCallStateChanged,
onCallHangup,
onCallReplaced,
} = this.callHandlers.get(opponentMemberId);
} = this.callHandlers.get(opponentMemberId)!;
call.removeListener(CallEvent.FeedsChanged, onCallFeedsChanged);
call.removeListener(CallEvent.State, onCallStateChanged);
@@ -1095,8 +1104,8 @@ export class GroupCall extends TypedEventEmitter<
}
};
private onCallStateChanged = (call: MatrixCall, state: CallState, _oldState: CallState) => {
const audioMuted = this.localCallFeed.isAudioMuted();
private onCallStateChanged = (call: MatrixCall, state: CallState, _oldState: CallState | undefined) => {
const audioMuted = this.localCallFeed!.isAudioMuted();
if (
call.localUsermediaStream &&
@@ -1105,7 +1114,7 @@ export class GroupCall extends TypedEventEmitter<
call.setMicrophoneMuted(audioMuted);
}
const videoMuted = this.localCallFeed.isVideoMuted();
const videoMuted = this.localCallFeed!.isVideoMuted();
if (
call.localUsermediaStream &&
@@ -1115,7 +1124,7 @@ export class GroupCall extends TypedEventEmitter<
}
if (state === CallState.Connected) {
this.retryCallCounts.delete(getCallUserId(call));
this.retryCallCounts.delete(getCallUserId(call)!);
}
};
@@ -1177,8 +1186,8 @@ export class GroupCall extends TypedEventEmitter<
}
private onActiveSpeakerLoop = () => {
let topAvg: number;
let nextActiveSpeaker: string;
let topAvg: number | undefined = undefined;
let nextActiveSpeaker: string | undefined = undefined;
for (const callFeed of this.userMediaFeeds) {
if (callFeed.userId === this.client.getUserId() && this.userMediaFeeds.length > 1) {
@@ -1200,7 +1209,7 @@ export class GroupCall extends TypedEventEmitter<
}
}
if (nextActiveSpeaker && this.activeSpeaker !== nextActiveSpeaker && topAvg > SPEAKING_THRESHOLD) {
if (nextActiveSpeaker && this.activeSpeaker !== nextActiveSpeaker && topAvg && topAvg > SPEAKING_THRESHOLD) {
this.activeSpeaker = nextActiveSpeaker;
this.emit(GroupCallEvent.ActiveSpeakerChanged, this.activeSpeaker);
}

View File

@@ -91,7 +91,7 @@ export class GroupCallEventHandler {
}
private getRoomDeferred(roomId: string): RoomDeferred {
let deferred: RoomDeferred = this.roomDeferreds.get(roomId);
let deferred = this.roomDeferreds.get(roomId);
if (deferred === undefined) {
let resolveFunc: () => void;
deferred = {
@@ -99,7 +99,7 @@ export class GroupCallEventHandler {
resolveFunc = resolve;
}),
};
deferred.resolve = resolveFunc;
deferred.resolve = resolveFunc!;
this.roomDeferreds.set(roomId, deferred);
}
@@ -110,7 +110,7 @@ export class GroupCallEventHandler {
return this.getRoomDeferred(roomId).prom;
}
public getGroupCallById(groupCallId: string): GroupCall {
public getGroupCallById(groupCallId: string): GroupCall | undefined {
return [...this.groupCalls.values()].find((groupCall) => groupCall.groupCallId === groupCallId);
}
@@ -135,7 +135,7 @@ export class GroupCallEventHandler {
}
logger.info("Group call event handler processed room", room.roomId);
this.getRoomDeferred(room.roomId).resolve();
this.getRoomDeferred(room.roomId).resolve!();
}
private createGroupCallFromRoomStateEvent(event: MatrixEvent): GroupCall | undefined {

View File

@@ -228,8 +228,8 @@ export class MediaHandler extends TypedEventEmitter<
this.localUserMediaStream = stream;
}
} else {
stream = this.localUserMediaStream.clone();
logger.log(`mediaHandler clone userMediaStream ${this.localUserMediaStream.id} new stream ${
stream = this.localUserMediaStream!.clone();
logger.log(`mediaHandler clone userMediaStream ${this.localUserMediaStream?.id} new stream ${
stream.id} shouldRequestAudio ${shouldRequestAudio} shouldRequestVideo ${shouldRequestVideo}`);
if (!shouldRequestAudio) {
@@ -282,12 +282,11 @@ export class MediaHandler extends TypedEventEmitter<
* @param reusable is allowed to be reused by the MediaHandler
* @returns {MediaStream} based on passed parameters
*/
public async getScreensharingStream(opts: IScreensharingOpts = {}, reusable = true): Promise<MediaStream | null> {
public async getScreensharingStream(opts: IScreensharingOpts = {}, reusable = true): Promise<MediaStream> {
let stream: MediaStream;
if (this.screensharingStreams.length === 0) {
const screenshareConstraints = this.getScreenshareContraints(opts);
if (!screenshareConstraints) return null;
if (opts.desktopCapturerSourceId) {
// We are using Electron
@@ -385,7 +384,7 @@ export class MediaHandler extends TypedEventEmitter<
if (desktopCapturerSourceId) {
logger.debug("Using desktop capturer source", desktopCapturerSourceId);
return {
audio,
audio: audio ?? false,
video: {
mandatory: {
chromeMediaSource: "desktop",
@@ -396,7 +395,7 @@ export class MediaHandler extends TypedEventEmitter<
} else {
logger.debug("Not using desktop capturer source");
return {
audio,
audio: audio ?? false,
video: true,
};
}