1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-26 17:03:12 +03:00

Remove the media operation queues

As per the comment on playRemoteVideo()
This commit is contained in:
David Baker
2020-12-04 20:03:11 +00:00
parent 0f5c469be6
commit 03737546fe

View File

@@ -101,12 +101,6 @@ export enum CallEvent {
HoldUnhold = 'hold_unhold', HoldUnhold = 'hold_unhold',
} }
enum MediaQueueId {
RemoteVideo = 'remote_video',
RemoteAudio = 'remote_audio',
LocalVideo = 'local_video',
}
export enum CallErrorCode { export enum CallErrorCode {
/** The user chose to end the call */ /** The user chose to end the call */
UserHangup = 'user_hangup', UserHangup = 'user_hangup',
@@ -226,7 +220,6 @@ export class MatrixCall extends EventEmitter {
private turnServers: Array<TurnServer>; private turnServers: Array<TurnServer>;
private candidateSendQueue: Array<RTCIceCandidate>; private candidateSendQueue: Array<RTCIceCandidate>;
private candidateSendTries: number; private candidateSendTries: number;
private mediaPromises: { [queueId: string]: Promise<void>; };
private sentEndOfCandidates: boolean; private sentEndOfCandidates: boolean;
private peerConn: RTCPeerConnection; private peerConn: RTCPeerConnection;
private localVideoElement: HTMLVideoElement; private localVideoElement: HTMLVideoElement;
@@ -291,12 +284,6 @@ export class MatrixCall extends EventEmitter {
this.candidateSendQueue = []; this.candidateSendQueue = [];
this.candidateSendTries = 0; this.candidateSendTries = 0;
// Lookup from opaque queue ID to a promise for media element operations that
// need to be serialised into a given queue. Store this per-MatrixCall on the
// assumption that multiple matrix calls will never compete for control of the
// same DOM elements.
this.mediaPromises = Object.create(null);
this.sentEndOfCandidates = false; this.sentEndOfCandidates = false;
this.inviteOrAnswerSent = false; this.inviteOrAnswerSent = false;
this.makingOffer = false; this.makingOffer = false;
@@ -371,14 +358,6 @@ export class MatrixCall extends EventEmitter {
return this.opponentMember; return this.opponentMember;
} }
private queueMediaOperation(queueId: MediaQueueId, operation: () => any) {
if (this.mediaPromises[queueId] !== undefined) {
this.mediaPromises[queueId] = this.mediaPromises[queueId].then(operation, operation);
} else {
this.mediaPromises[queueId] = Promise.resolve(operation());
}
}
/** /**
* Retrieve the local <code>&lt;video&gt;</code> DOM element. * Retrieve the local <code>&lt;video&gt;</code> DOM element.
* @return {Element} The dom element * @return {Element} The dom element
@@ -410,17 +389,19 @@ export class MatrixCall extends EventEmitter {
* video will be rendered to it immediately. * video will be rendered to it immediately.
* @param {Element} element The <code>&lt;video&gt;</code> DOM element. * @param {Element} element The <code>&lt;video&gt;</code> DOM element.
*/ */
setLocalVideoElement(element : HTMLVideoElement) { async setLocalVideoElement(element : HTMLVideoElement) {
this.localVideoElement = element; this.localVideoElement = element;
if (element && this.localAVStream && this.type === CallType.Video) { if (element && this.localAVStream && this.type === CallType.Video) {
element.autoplay = true; element.autoplay = true;
this.queueMediaOperation(MediaQueueId.LocalVideo, () => {
element.srcObject = this.localAVStream; element.srcObject = this.localAVStream;
element.muted = true; element.muted = true;
return element.play(); try {
}); await element.play();
} catch (e) {
logger.info("Failed to play local video element", e);
}
} }
} }
@@ -441,10 +422,7 @@ export class MatrixCall extends EventEmitter {
this.remoteVideoElement = element; this.remoteVideoElement = element;
if (this.remoteStream) { if (this.remoteStream) {
this.queueMediaOperation(MediaQueueId.RemoteVideo, () => { this.playRemoteVideo();
element.srcObject = this.remoteStream;
return element.play();
});
} }
} }
@@ -748,7 +726,6 @@ export class MatrixCall extends EventEmitter {
const videoEl = this.getLocalVideoElement(); const videoEl = this.getLocalVideoElement();
if (videoEl && this.type === CallType.Video) { if (videoEl && this.type === CallType.Video) {
this.queueMediaOperation(MediaQueueId.LocalVideo, () => {
videoEl.autoplay = true; videoEl.autoplay = true;
if (this.screenSharingStream) { if (this.screenSharingStream) {
logger.debug( logger.debug(
@@ -759,8 +736,11 @@ export class MatrixCall extends EventEmitter {
videoEl.srcObject = stream; videoEl.srcObject = stream;
} }
videoEl.muted = true; videoEl.muted = true;
return videoEl.play(); try {
}); await videoEl.play();
} catch (e) {
logger.info("Failed to play local video element", e);
}
} }
this.localAVStream = stream; this.localAVStream = stream;
@@ -825,13 +805,15 @@ export class MatrixCall extends EventEmitter {
const localVidEl = this.getLocalVideoElement(); const localVidEl = this.getLocalVideoElement();
if (localVidEl && this.type === CallType.Video) { if (localVidEl && this.type === CallType.Video) {
this.queueMediaOperation(MediaQueueId.LocalVideo, () => {
localVidEl.autoplay = true; localVidEl.autoplay = true;
localVidEl.srcObject = stream; localVidEl.srcObject = stream;
localVidEl.muted = true; localVidEl.muted = true;
return localVidEl.play(); try {
}); await localVidEl.play();
} catch (e) {
logger.info("Failed to play local video element", e);
}
} }
this.localAVStream = stream; this.localAVStream = stream;
@@ -1231,14 +1213,7 @@ export class MatrixCall extends EventEmitter {
if (ev.track.kind === 'video') { if (ev.track.kind === 'video') {
if (this.remoteVideoElement) { if (this.remoteVideoElement) {
this.queueMediaOperation(MediaQueueId.RemoteVideo, async () => { this.playRemoteVideo();
this.remoteVideoElement.srcObject = this.remoteStream;
try {
await this.remoteVideoElement.play();
} catch (e) {
logger.error("Failed to play remote video element", e);
}
});
} }
} else { } else {
if (this.remoteAudioElement) this.playRemoteAudio(); if (this.remoteAudioElement) this.playRemoteAudio();
@@ -1265,8 +1240,7 @@ export class MatrixCall extends EventEmitter {
} }
}; };
playRemoteAudio() { async playRemoteAudio() {
this.queueMediaOperation(MediaQueueId.RemoteAudio, async () => {
if (this.remoteVideoElement) this.remoteVideoElement.muted = true; if (this.remoteVideoElement) this.remoteVideoElement.muted = true;
this.remoteAudioElement.muted = false; this.remoteAudioElement.muted = false;
@@ -1291,7 +1265,6 @@ export class MatrixCall extends EventEmitter {
} catch (e) { } catch (e) {
logger.error("Failed to play remote audio element", e); logger.error("Failed to play remote audio element", e);
} }
});
} }
onHangupReceived = (msg) => { onHangupReceived = (msg) => {
@@ -1370,7 +1343,7 @@ export class MatrixCall extends EventEmitter {
} }
} }
private terminate(hangupParty: CallParty, hangupReason: CallErrorCode, shouldEmit: boolean) { private async terminate(hangupParty: CallParty, hangupReason: CallErrorCode, shouldEmit: boolean) {
if (this.callHasEnded()) return; if (this.callHasEnded()) return;
if (this.inviteTimeout) { if (this.inviteTimeout) {
@@ -1383,13 +1356,10 @@ export class MatrixCall extends EventEmitter {
const localVid = this.getLocalVideoElement(); const localVid = this.getLocalVideoElement();
if (remoteVid) { if (remoteVid) {
this.queueMediaOperation(MediaQueueId.RemoteVideo, () => {
remoteVid.pause(); remoteVid.pause();
remoteVid.srcObject = null; remoteVid.srcObject = null;
});
} }
if (remoteAud) { if (remoteAud) {
this.queueMediaOperation(MediaQueueId.RemoteAudio, async () => {
remoteAud.pause(); remoteAud.pause();
remoteAud.srcObject = null; remoteAud.srcObject = null;
try { try {
@@ -1399,13 +1369,10 @@ export class MatrixCall extends EventEmitter {
} catch (e) { } catch (e) {
logger.warn("Failed to set sink ID back to default"); logger.warn("Failed to set sink ID back to default");
} }
});
} }
if (localVid) { if (localVid) {
this.queueMediaOperation(MediaQueueId.LocalVideo, () => {
localVid.pause(); localVid.pause();
localVid.srcObject = null; localVid.srcObject = null;
});
} }
this.hangupParty = hangupParty; this.hangupParty = hangupParty;
this.hangupReason = hangupReason; this.hangupReason = hangupReason;
@@ -1528,6 +1495,25 @@ export class MatrixCall extends EventEmitter {
const msgPartyId = msg.party_id || null; const msgPartyId = msg.party_id || null;
return msgPartyId === this.opponentPartyId; return msgPartyId === this.opponentPartyId;
} }
private async playRemoteVideo() {
// A note on calling methods on media elements:
// We used to have queues per media element to serialise all calls on those elements.
// The reason given for this was that load() and play() were racing. However, we now
// never call load() explicitly so this seems unnecessary. However, serialising every
// operation was causing bugs where video would not resume because some play command
// had got stuck and all media operations were queued up behind it. If necessary, we
// should serialise the ones that need to be serialised but then be able to interrupt
// them with another load() which will cancel the pending one, but since we don't call
// load() explicitly, it shouldn't be a problem.
this.remoteVideoElement.srcObject = this.remoteStream;
logger.info("playing remote video. stream active? " + this.remoteStream.active);
try {
await this.remoteVideoElement.play();
} catch (e) {
logger.info("Failed to play remote video element", e);
}
}
} }
function setTracksEnabled(tracks: Array<MediaStreamTrack>, enabled: boolean) { function setTracksEnabled(tracks: Array<MediaStreamTrack>, enabled: boolean) {