diff --git a/src/domain/session/room/CallViewModel.ts b/src/domain/session/room/CallViewModel.ts index 5b6f05b4..020c9f17 100644 --- a/src/domain/session/room/CallViewModel.ts +++ b/src/domain/session/room/CallViewModel.ts @@ -109,6 +109,7 @@ export class CallViewModel extends ViewModel { // unmute but no track? if (muteSettings.microphone && !getStreamAudioTrack(localMedia.userMedia)) { const stream = await this.platform.mediaDevices.getMediaTracks(true, !muteSettings.camera); + console.log("got tracks", Array.from(stream.getTracks()).map((t: MediaStreamTrack) => { return {kind: t.kind, id: t.id};})) await this.call.setMedia(localMedia.withUserMedia(stream)); } else { await this.call.setMuted(muteSettings.toggleMicrophone()); diff --git a/src/matrix/calls/LocalMedia.ts b/src/matrix/calls/LocalMedia.ts index b79fe098..922e6861 100644 --- a/src/matrix/calls/LocalMedia.ts +++ b/src/matrix/calls/LocalMedia.ts @@ -45,13 +45,19 @@ export class LocalMedia { const cloneOrAdoptStream = (oldOriginalStream: Stream | undefined, oldCloneStream: Stream | undefined, newStream: Stream | undefined): Stream | undefined => { let stream; if (oldOriginalStream?.id === newStream?.id) { - stream = oldCloneStream; + return oldCloneStream; } else { - stream = newStream?.clone(); + const clonedStream = newStream?.clone(); + // clonedStream.addEventListener("removetrack", evt => { + // console.log(`removing track ${evt.track.id} (${evt.track.kind}) from clonedStream ${clonedStream.id}`); + // }); + // console.log("got cloned tracks", Array.from(clonedStream.getTracks()).map((t: MediaStreamTrack) => { return {kind: t.kind, id: t.id};}), "from new stream", Array.from(newStream.getTracks()).map((t: MediaStreamTrack) => { return {kind: t.kind, id: t.id};})); + // console.log("stopping old audio stream", getStreamAudioTrack(oldCloneStream)?.id); + // console.log("stopping old video stream", getStreamVideoTrack(oldCloneStream)?.id); getStreamAudioTrack(oldCloneStream)?.stop(); getStreamVideoTrack(oldCloneStream)?.stop(); + return clonedStream; } - return stream; } return new LocalMedia( cloneOrAdoptStream(oldOriginal?.userMedia, oldClone?.userMedia, this.userMedia), diff --git a/src/matrix/calls/PeerCall.ts b/src/matrix/calls/PeerCall.ts index 809decbf..1e22a2ff 100644 --- a/src/matrix/calls/PeerCall.ts +++ b/src/matrix/calls/PeerCall.ts @@ -383,8 +383,7 @@ export class PeerCall implements IDisposable { const offer = this.peerConnection.localDescription!; // Get rid of any candidates waiting to be sent: they'll be included in the local // description we just got and will send in the offer. - log.log(`Discarding ${ - this.candidateSendQueue.length} candidates that will be sent in offer`); + log.set("includedCandidates", this.candidateSendQueue.length); this.candidateSendQueue = []; // need to queue this @@ -414,16 +413,20 @@ export class PeerCall implements IDisposable { this.sendCandidateQueue(log); - await log.wrap("invite timeout", async log => { - if (this._state === CallState.InviteSent) { + if (this._state === CallState.InviteSent) { + const timeoutLog = this.logItem.child("invite timeout"); + log.refDetached(timeoutLog); + // don't await this, as it would block other negotationneeded events from being processed + // as they are processed serially + timeoutLog.run(async log => { try { await this.delay(CALL_TIMEOUT_MS); } catch (err) { return; } // @ts-ignore TS doesn't take the await above into account to know that the state could have changed in between if (this._state === CallState.InviteSent) { this._hangup(CallErrorCode.InviteTimeout, log); } - } - }); + }).catch(err => {}); // prevent error from being unhandled, it will be logged already by run above + } }; private async handleInviteGlare(content: MCallInvite, partyId: PartyId, log: ILogItem): Promise { diff --git a/src/matrix/calls/group/GroupCall.ts b/src/matrix/calls/group/GroupCall.ts index 19e40dbf..087dc1ff 100644 --- a/src/matrix/calls/group/GroupCall.ts +++ b/src/matrix/calls/group/GroupCall.ts @@ -149,11 +149,14 @@ export class GroupCall extends EventEmitter<{change: never}> { await joinedData.logItem.wrap("join", async log => { this._state = GroupCallState.Joining; this.emitChange(); - const memberContent = await this._createJoinPayload(); - // send m.call.member state event - const request = this.options.hsApi.sendState(this.roomId, EventType.GroupCallMember, this.options.ownUserId, memberContent, {log}); - await request.response(); - this.emitChange(); + await log.wrap("update member state", async log => { + const memberContent = await this._createJoinPayload(); + log.set("payload", memberContent); + // send m.call.member state event + const request = this.options.hsApi.sendState(this.roomId, EventType.GroupCallMember, this.options.ownUserId, memberContent, {log}); + await request.response(); + this.emitChange(); + }); // send invite to all members that are < my userId for (const [,member] of this._members) { this.connectToMember(member, joinedData, log); diff --git a/src/matrix/calls/group/Member.ts b/src/matrix/calls/group/Member.ts index cf6bb6f0..3003c3df 100644 --- a/src/matrix/calls/group/Member.ts +++ b/src/matrix/calls/group/Member.ts @@ -293,7 +293,7 @@ export class Member { async setMedia(localMedia: LocalMedia, previousMedia: LocalMedia): Promise { const {connection} = this; if (connection) { - connection.localMedia = connection.localMedia.replaceClone(connection.localMedia, previousMedia); + connection.localMedia = localMedia.replaceClone(connection.localMedia, previousMedia); await connection.peerCall?.setMedia(connection.localMedia, connection.logItem); } } diff --git a/src/platform/web/dom/MediaDevices.ts b/src/platform/web/dom/MediaDevices.ts index c34ab85b..d6439faa 100644 --- a/src/platform/web/dom/MediaDevices.ts +++ b/src/platform/web/dom/MediaDevices.ts @@ -30,6 +30,9 @@ export class MediaDevicesWrapper implements IMediaDevices { async getMediaTracks(audio: true | MediaDeviceInfo, video: boolean | MediaDeviceInfo): Promise { const stream = await this.mediaDevices.getUserMedia(this.getUserMediaContraints(audio, video)); + stream.addEventListener("removetrack", evt => { + console.log(`removing track ${evt.track.id} (${evt.track.kind}) from stream ${stream.id}`); + }); return stream as Stream; }