Skip to content

Commit a8a59f3

Browse files
Make code for placing and answering calls more flexible
Signed-off-by: Šimon Brandner <[email protected]> Co-authored-by: Robert Long <[email protected]>
1 parent 6a0604b commit a8a59f3

File tree

1 file changed

+113
-50
lines changed

1 file changed

+113
-50
lines changed

src/webrtc/call.ts

Lines changed: 113 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -567,51 +567,69 @@ export class MatrixCall extends EventEmitter {
567567
logger.info(`Pushed remote stream (id="${stream.id}", active="${stream.active}")`);
568568
}
569569

570-
private pushLocalFeed(stream: MediaStream, purpose: SDPStreamMetadataPurpose, addToPeerConnection = true): void {
570+
private pushNewLocalFeed(stream: MediaStream, purpose: SDPStreamMetadataPurpose, addToPeerConnection = true): void {
571571
const userId = this.client.getUserId();
572572

573+
// TODO: Find out what is going on here
574+
// why do we enable audio (and only audio) tracks here? -- matthew
575+
setTracksEnabled(stream.getAudioTracks(), true);
576+
573577
// We try to replace an existing feed if there already is one with the same purpose
574578
const existingFeed = this.getLocalFeeds().find((feed) => feed.purpose === purpose);
575579
if (existingFeed) {
576580
existingFeed.setNewStream(stream);
577581
} else {
578-
this.feeds.push(new CallFeed({
579-
client: this.client,
580-
roomId: this.roomId,
581-
audioMuted: stream.getAudioTracks().length === 0,
582-
videoMuted: stream.getVideoTracks().length === 0,
583-
userId,
584-
stream,
585-
purpose,
586-
}));
582+
this.pushLocalFeed(
583+
new CallFeed({
584+
client: this.client,
585+
roomId: this.roomId,
586+
audioMuted: stream.getAudioTracks().length === 0,
587+
videoMuted: stream.getVideoTracks().length === 0,
588+
userId,
589+
stream,
590+
purpose,
591+
}),
592+
addToPeerConnection,
593+
);
587594
this.emit(CallEvent.FeedsChanged, this.feeds);
588595
}
596+
}
589597

590-
// TODO: Find out what is going on here
591-
// why do we enable audio (and only audio) tracks here? -- matthew
592-
setTracksEnabled(stream.getAudioTracks(), true);
598+
/**
599+
* Pushes supplied feed to the call
600+
* @param {CallFeed} callFeed to push
601+
* @param {boolean} addToPeerConnection whether to add the tracks to the peer connection
602+
*/
603+
public pushLocalFeed(callFeed: CallFeed, addToPeerConnection = true): void {
604+
this.feeds.push(callFeed);
593605

594606
if (addToPeerConnection) {
595-
const senderArray = purpose === SDPStreamMetadataPurpose.Usermedia ?
607+
const senderArray = callFeed.purpose === SDPStreamMetadataPurpose.Usermedia ?
596608
this.usermediaSenders : this.screensharingSenders;
597609
// Empty the array
598610
senderArray.splice(0, senderArray.length);
599611

600-
this.emit(CallEvent.FeedsChanged, this.feeds);
601-
for (const track of stream.getTracks()) {
612+
for (const track of callFeed.stream.getTracks()) {
602613
logger.info(
603614
`Adding track (` +
604615
`id="${track.id}", ` +
605616
`kind="${track.kind}", ` +
606-
`streamId="${stream.id}", ` +
607-
`streamPurpose="${purpose}"` +
617+
`streamId="${callFeed.stream}", ` +
618+
`streamPurpose="${callFeed.purpose}"` +
608619
`) to peer connection`,
609620
);
610-
senderArray.push(this.peerConn.addTrack(track, stream));
621+
senderArray.push(this.peerConn.addTrack(track, callFeed.stream));
611622
}
612623
}
613624

614-
logger.info(`Pushed local stream (id="${stream.id}", active="${stream.active}", purpose="${purpose}")`);
625+
logger.info(
626+
`Pushed local stream ` +
627+
`(id="${callFeed.stream.id}", ` +
628+
`active="${callFeed.stream.active}", ` +
629+
`purpose="${callFeed.purpose}")`,
630+
);
631+
632+
this.emit(CallEvent.FeedsChanged, this.feeds);
615633
}
616634

617635
/**
@@ -794,11 +812,27 @@ export class MatrixCall extends EventEmitter {
794812
this.waitForLocalAVStream = true;
795813

796814
try {
797-
const mediaStream = await this.client.getMediaHandler().getUserMediaStream(
815+
const stream = await this.client.getMediaHandler().getUserMediaStream(
798816
answerWithAudio, answerWithVideo,
799817
);
800818
this.waitForLocalAVStream = false;
801-
this.gotUserMediaForAnswer(mediaStream);
819+
const usermediaFeed = new CallFeed({
820+
client: this.client,
821+
roomId: this.roomId,
822+
userId: this.client.getUserId(),
823+
stream,
824+
purpose: SDPStreamMetadataPurpose.Usermedia,
825+
audioMuted: stream.getAudioTracks().length === 0,
826+
videoMuted: stream.getVideoTracks().length === 0,
827+
});
828+
829+
const feeds = [usermediaFeed];
830+
831+
if (this.localScreensharingFeed) {
832+
feeds.push(this.localScreensharingFeed);
833+
}
834+
835+
this.answerWithCallFeeds(feeds);
802836
} catch (e) {
803837
if (answerWithVideo) {
804838
// Try to answer without video
@@ -816,6 +850,14 @@ export class MatrixCall extends EventEmitter {
816850
}
817851
}
818852

853+
public answerWithCallFeeds(callFeeds: CallFeed[]): void {
854+
if (this.inviteOrAnswerSent) return;
855+
856+
logger.debug(`Answering call ${this.callId}`);
857+
858+
this.gotCallFeedsForAnswer(callFeeds);
859+
}
860+
819861
/**
820862
* Replace this call with a new call, e.g. for glare resolution. Used by
821863
* MatrixClient.
@@ -827,7 +869,7 @@ export class MatrixCall extends EventEmitter {
827869
newCall.waitForLocalAVStream = true;
828870
} else if ([CallState.CreateOffer, CallState.InviteSent].includes(this.state)) {
829871
logger.debug("Handing local stream to new call");
830-
newCall.gotUserMediaForAnswer(this.localUsermediaStream);
872+
newCall.gotCallFeedsForAnswer(this.getLocalFeeds());
831873
}
832874
this.successor = newCall;
833875
this.emit(CallEvent.Replaced, newCall);
@@ -899,7 +941,7 @@ export class MatrixCall extends EventEmitter {
899941
if (this.hasLocalUserMediaAudioTrack) return;
900942
if (this.hasLocalUserMediaVideoTrack) return;
901943

902-
this.pushLocalFeed(stream, SDPStreamMetadataPurpose.Usermedia);
944+
this.pushNewLocalFeed(stream, SDPStreamMetadataPurpose.Usermedia);
903945
} else if (upgradeAudio) {
904946
if (this.hasLocalUserMediaAudioTrack) return;
905947

@@ -965,7 +1007,7 @@ export class MatrixCall extends EventEmitter {
9651007
try {
9661008
const stream = await this.client.getMediaHandler().getScreensharingStream(desktopCapturerSourceId);
9671009
if (!stream) return false;
968-
this.pushLocalFeed(stream, SDPStreamMetadataPurpose.Screenshare);
1010+
this.pushNewLocalFeed(stream, SDPStreamMetadataPurpose.Screenshare);
9691011
return true;
9701012
} catch (err) {
9711013
this.emit(CallEvent.Error,
@@ -1007,7 +1049,7 @@ export class MatrixCall extends EventEmitter {
10071049
});
10081050
sender.replaceTrack(track);
10091051

1010-
this.pushLocalFeed(stream, SDPStreamMetadataPurpose.Screenshare, false);
1052+
this.pushNewLocalFeed(stream, SDPStreamMetadataPurpose.Screenshare, false);
10111053

10121054
return true;
10131055
} catch (err) {
@@ -1167,26 +1209,25 @@ export class MatrixCall extends EventEmitter {
11671209
setTracksEnabled(this.localUsermediaStream.getVideoTracks(), !vidShouldBeMuted);
11681210
}
11691211

1170-
/**
1171-
* Internal
1172-
* @param {Object} stream
1173-
*/
1174-
private gotUserMediaForInvite = async (stream: MediaStream): Promise<void> => {
1212+
private gotCallFeedsForInvite(callFeeds: CallFeed[], requestScreenshareFeed = false): void {
11751213
if (this.successor) {
1176-
this.successor.gotUserMediaForAnswer(stream);
1214+
this.successor.gotCallFeedsForAnswer(callFeeds);
11771215
return;
11781216
}
11791217
if (this.callHasEnded()) {
11801218
this.stopAllMedia();
11811219
return;
11821220
}
11831221

1184-
this.pushLocalFeed(stream, SDPStreamMetadataPurpose.Usermedia);
1222+
for (const feed of callFeeds) {
1223+
this.pushLocalFeed(feed);
1224+
}
1225+
11851226
this.setState(CallState.CreateOffer);
11861227

11871228
logger.debug("gotUserMediaForInvite");
11881229
// Now we wait for the negotiationneeded event
1189-
};
1230+
}
11901231

11911232
private async sendAnswer(): Promise<void> {
11921233
const answerContent = {
@@ -1235,12 +1276,15 @@ export class MatrixCall extends EventEmitter {
12351276
this.sendCandidateQueue();
12361277
}
12371278

1238-
private gotUserMediaForAnswer = async (stream: MediaStream): Promise<void> => {
1239-
if (this.callHasEnded()) {
1240-
return;
1279+
private async gotCallFeedsForAnswer(callFeeds: CallFeed[]): Promise<void> {
1280+
if (this.callHasEnded()) return;
1281+
1282+
this.waitForLocalAVStream = false;
1283+
1284+
for (const feed of callFeeds) {
1285+
this.pushLocalFeed(feed);
12411286
}
12421287

1243-
this.pushLocalFeed(stream, SDPStreamMetadataPurpose.Usermedia);
12441288
this.setState(CallState.CreateAnswer);
12451289

12461290
let myAnswer;
@@ -1268,7 +1312,7 @@ export class MatrixCall extends EventEmitter {
12681312
this.terminate(CallParty.Local, CallErrorCode.SetLocalDescription, true);
12691313
return;
12701314
}
1271-
};
1315+
}
12721316

12731317
/**
12741318
* Internal
@@ -2010,15 +2054,41 @@ export class MatrixCall extends EventEmitter {
20102054
* @throws if have passed audio=false.
20112055
*/
20122056
public async placeCall(audio: boolean, video: boolean): Promise<void> {
2013-
logger.debug(`placeCall audio=${audio} video=${video}`);
20142057
if (!audio) {
20152058
throw new Error("You CANNOT start a call without audio");
20162059
}
2060+
this.setState(CallState.WaitLocalMedia);
2061+
2062+
try {
2063+
const stream = await this.client.getMediaHandler().getUserMediaStream(audio, video);
2064+
const callFeed = new CallFeed({
2065+
client: this.client,
2066+
roomId: this.roomId,
2067+
userId: this.client.getUserId(),
2068+
stream,
2069+
purpose: SDPStreamMetadataPurpose.Usermedia,
2070+
audioMuted: stream.getAudioTracks().length === 0,
2071+
videoMuted: stream.getVideoTracks().length === 0,
2072+
});
2073+
await this.placeCallWithCallFeeds([callFeed]);
2074+
} catch (e) {
2075+
this.getUserMediaFailed(e);
2076+
return;
2077+
}
2078+
}
2079+
2080+
/**
2081+
* Place a call to this room with call feed.
2082+
* @param {CallFeed[]} callFeeds to use
2083+
* @throws if you have not specified a listener for 'error' events.
2084+
* @throws if have passed audio=false.
2085+
*/
2086+
public async placeCallWithCallFeeds(callFeeds: CallFeed[], requestScreenshareFeed = false): Promise<void> {
20172087
this.checkForErrorListener();
2088+
this.direction = CallDirection.Outbound;
2089+
20182090
// XXX Find a better way to do this
20192091
this.client.callEventHandler.calls.set(this.callId, this);
2020-
this.setState(CallState.WaitLocalMedia);
2021-
this.direction = CallDirection.Outbound;
20222092

20232093
// make sure we have valid turn creds. Unless something's gone wrong, it should
20242094
// poll and keep the credentials valid so this should be instant.
@@ -2030,14 +2100,7 @@ export class MatrixCall extends EventEmitter {
20302100
// create the peer connection now so it can be gathering candidates while we get user
20312101
// media (assuming a candidate pool size is configured)
20322102
this.peerConn = this.createPeerConnection();
2033-
2034-
try {
2035-
const mediaStream = await this.client.getMediaHandler().getUserMediaStream(audio, video);
2036-
this.gotUserMediaForInvite(mediaStream);
2037-
} catch (e) {
2038-
this.getUserMediaFailed(e);
2039-
return;
2040-
}
2103+
this.gotCallFeedsForInvite(callFeeds, requestScreenshareFeed);
20412104
}
20422105

20432106
private createPeerConnection(): RTCPeerConnection {

0 commit comments

Comments
 (0)