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

kill unhandled exceptions where loads and plays race by queuing them as promises

This commit is contained in:
Matthew Hodgson
2016-08-31 16:29:14 +01:00
parent d356e722da
commit b0782885d5

View File

@@ -74,6 +74,8 @@ MatrixCall.ERR_LOCAL_OFFER_FAILED = "local_offer_failed";
*/ */
MatrixCall.ERR_NO_USER_MEDIA = "no_user_media"; MatrixCall.ERR_NO_USER_MEDIA = "no_user_media";
MatrixCall.mediaPromises = {};
utils.inherits(MatrixCall, EventEmitter); utils.inherits(MatrixCall, EventEmitter);
/** /**
@@ -144,6 +146,64 @@ MatrixCall.prototype.placeScreenSharingCall =
_tryPlayRemoteStream(this); _tryPlayRemoteStream(this);
}; };
/**
* Play the given HTMLMediaElement, serialising the operation into a chain
* of promises to avoid racing access to the element
* @param {Element} HTMLMediaElement to play
* @param {string} Arbitrary ID to track the chain of promises to be used
*/
MatrixCall.prototype.playElement = function(element, queueId) {
if (MatrixCall.mediaPromises[queueId]) {
MatrixCall.mediaPromises[queueId] =
MatrixCall.mediaPromises[queueId].then(function() {
return element.play();
});
}
else {
MatrixCall.mediaPromises[queueId] = element.play();
}
};
/**
* Pause the given HTMLMediaElement, serialising the operation into a chain
* of promises to avoid racing access to the element
* @param {Element} HTMLMediaElement to pause
* @param {string} Arbitrary ID to track the chain of promises to be used
*/
MatrixCall.prototype.pauseElement = function(element, queueId) {
if (MatrixCall.mediaPromises[queueId]) {
MatrixCall.mediaPromises[queueId] =
MatrixCall.mediaPromises[queueId].then(function() {
return element.pause();
});
}
else {
// pause doesn't actually return a promise, but do this for symmetry
// and just in case it does in future.
MatrixCall.mediaPromises[queueId] = element.pause();
}
};
/**
* Assign the given HTMLMediaElement by setting the .src attribute on it,
* serialising the operation into a chain of promises to avoid racing access
* to the element
* @param {Element} HTMLMediaElement to pause
* @param {string} the src attribute value to assign to the element
* @param {string} Arbitrary ID to track the chain of promises to be used
*/
MatrixCall.prototype.assignElement = function(element, src, queueId) {
if (MatrixCall.mediaPromises[queueId]) {
MatrixCall.mediaPromises[queueId] =
MatrixCall.mediaPromises[queueId].then(function() {
element.src = src;
});
}
else {
element.src = src;
}
};
/** /**
* 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
@@ -180,13 +240,15 @@ MatrixCall.prototype.setLocalVideoElement = function(element) {
if (element && this.localAVStream && this.type === 'video') { if (element && this.localAVStream && this.type === 'video') {
element.autoplay = true; element.autoplay = true;
element.src = this.URL.createObjectURL(this.localAVStream); this.assignElement(element,
this.URL.createObjectURL(this.localAVStream),
"localVideo");
element.muted = true; element.muted = true;
var self = this; var self = this;
setTimeout(function() { setTimeout(function() {
var vel = self.getLocalVideoElement(); var vel = self.getLocalVideoElement();
if (vel.play) { if (vel.play) {
vel.play(); self.playElement(vel, "localVideo");
} }
}, 0); }, 0);
} }
@@ -414,16 +476,20 @@ MatrixCall.prototype._gotUserMediaForInvite = function(stream) {
videoEl.autoplay = true; videoEl.autoplay = true;
if (this.screenSharingStream) { if (this.screenSharingStream) {
debuglog("Setting screen sharing stream to the local video element"); debuglog("Setting screen sharing stream to the local video element");
videoEl.src = this.URL.createObjectURL(this.screenSharingStream); this.assignElement(videoEl,
this.URL.createObjectURL(this.screenSharingStream),
"localVideo");
} }
else { else {
videoEl.src = this.URL.createObjectURL(stream); this.assignElement(videoEl,
this.URL.createObjectURL(stream),
"localVideo");
} }
videoEl.muted = true; videoEl.muted = true;
setTimeout(function() { setTimeout(function() {
var vel = self.getLocalVideoElement(); var vel = self.getLocalVideoElement();
if (vel.play) { if (vel.play) {
vel.play(); self.playElement(vel, "localVideo");
} }
}, 0); }, 0);
} }
@@ -460,12 +526,14 @@ MatrixCall.prototype._gotUserMediaForAnswer = function(stream) {
if (localVidEl && self.type == 'video') { if (localVidEl && self.type == 'video') {
localVidEl.autoplay = true; localVidEl.autoplay = true;
localVidEl.src = self.URL.createObjectURL(stream); this.assignElement(localVidEl,
this.URL.createObjectURL(stream),
"localVideo");
localVidEl.muted = true; localVidEl.muted = true;
setTimeout(function() { setTimeout(function() {
var vel = self.getLocalVideoElement(); var vel = self.getLocalVideoElement();
if (vel.play) { if (vel.play) {
vel.play(); self.playElement(vel, "localVideo");
} }
}, 0); }, 0);
} }
@@ -712,7 +780,7 @@ MatrixCall.prototype._onAddStream = function(event) {
t.onstarted = hookCallback(self, self._onRemoteStreamTrackStarted); t.onstarted = hookCallback(self, self._onRemoteStreamTrackStarted);
}); });
event.stream.onended = hookCallback(self, self._onRemoteStreamEnded); event.stream.oninactive = hookCallback(self, self._onRemoteStreamEnded);
// not currently implemented in chrome // not currently implemented in chrome
event.stream.onstarted = hookCallback(self, self._onRemoteStreamStarted); event.stream.onstarted = hookCallback(self, self._onRemoteStreamStarted);
@@ -824,21 +892,21 @@ var sendCandidate = function(self, content) {
var terminate = function(self, hangupParty, hangupReason, shouldEmit) { var terminate = function(self, hangupParty, hangupReason, shouldEmit) {
if (self.getRemoteVideoElement()) { if (self.getRemoteVideoElement()) {
if (self.getRemoteVideoElement().pause) { if (self.getRemoteVideoElement().pause) {
self.getRemoteVideoElement().pause(); self.pauseElement(self.getRemoteVideoElement(), "remoteVideo");
} }
self.getRemoteVideoElement().src = ""; self.assignElement(self.getRemoteVideoElement(), "", "remoteVideo");
} }
if (self.getRemoteAudioElement()) { if (self.getRemoteAudioElement()) {
if (self.getRemoteAudioElement().pause) { if (self.getRemoteAudioElement().pause) {
self.getRemoteAudioElement().pause(); self.pauseElement(self.getRemoteAudioElement(), "remoteAudio");
} }
self.getRemoteAudioElement().src = ""; self.assignElement(self.getRemoteAudioElement(), "", "remoteAudio");
} }
if (self.getLocalVideoElement()) { if (self.getLocalVideoElement()) {
if (self.getLocalVideoElement().pause) { if (self.getLocalVideoElement().pause) {
self.getLocalVideoElement().pause(); self.pauseElement(self.getLocalVideoElement(), "localVideo");
} }
self.getLocalVideoElement().src = ""; self.assignElement(self.getLocalVideoElement(), "", "localVideo");
} }
self.hangupParty = hangupParty; self.hangupParty = hangupParty;
self.hangupReason = hangupReason; self.hangupReason = hangupReason;
@@ -896,11 +964,13 @@ var _tryPlayRemoteStream = function(self) {
if (self.getRemoteVideoElement() && self.remoteAVStream) { if (self.getRemoteVideoElement() && self.remoteAVStream) {
var player = self.getRemoteVideoElement(); var player = self.getRemoteVideoElement();
player.autoplay = true; player.autoplay = true;
player.src = self.URL.createObjectURL(self.remoteAVStream); self.assignElement(player,
self.URL.createObjectURL(self.remoteAVStream),
"remoteVideo");
setTimeout(function() { setTimeout(function() {
var vel = self.getRemoteVideoElement(); var vel = self.getRemoteVideoElement();
if (vel.play) { if (vel.play) {
vel.play(); self.playElement(vel, "remoteVideo");
} }
// OpenWebRTC does not support oniceconnectionstatechange yet // OpenWebRTC does not support oniceconnectionstatechange yet
if (self.webRtc.isOpenWebRTC()) { if (self.webRtc.isOpenWebRTC()) {
@@ -914,11 +984,13 @@ var _tryPlayRemoteAudioStream = function(self) {
if (self.getRemoteAudioElement() && self.remoteAStream) { if (self.getRemoteAudioElement() && self.remoteAStream) {
var player = self.getRemoteAudioElement(); var player = self.getRemoteAudioElement();
player.autoplay = true; player.autoplay = true;
player.src = self.URL.createObjectURL(self.remoteAStream); self.assignElement(player,
self.URL.createObjectURL(self.remoteAStream),
"remoteAudio");
setTimeout(function() { setTimeout(function() {
var ael = self.getRemoteAudioElement(); var ael = self.getRemoteAudioElement();
if (ael.play) { if (ael.play) {
ael.play(); self.playElement(vel, "remoteAudio");
} }
// OpenWebRTC does not support oniceconnectionstatechange yet // OpenWebRTC does not support oniceconnectionstatechange yet
if (self.webRtc.isOpenWebRTC()) { if (self.webRtc.isOpenWebRTC()) {
@@ -1102,6 +1174,7 @@ var forAllTracksOnStream = function(s, f) {
/** The MatrixCall class. */ /** The MatrixCall class. */
module.exports.MatrixCall = MatrixCall; module.exports.MatrixCall = MatrixCall;
/** /**
* Create a new Matrix call for the browser. * Create a new Matrix call for the browser.
* @param {MatrixClient} client The client instance to use. * @param {MatrixClient} client The client instance to use.