You've already forked matrix-js-sdk
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:
@@ -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><video></code> DOM element.
|
* Retrieve the local <code><video></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><video></code> DOM element.
|
* @param {Element} element The <code><video></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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user