Merge pull request #5971 from MaximKhlobystov/ice-connection-state-errors

Better handling of video ice connection state errors.
This commit is contained in:
Anton Georgiev 2018-08-14 13:12:12 -04:00 committed by GitHub
commit f83374491b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 28 deletions

View File

@ -45,6 +45,10 @@ const intlClientErrors = defineMessages({
id: 'app.video.notReadableError',
description: 'error message When the webcam is being used by other software',
},
iceConnectionStateError: {
id: 'app.video.iceConnectionStateError',
description: 'Error message for ice connection state being failed',
},
});
const intlSFUErrors = defineMessages({
@ -62,7 +66,7 @@ const intlSFUErrors = defineMessages({
},
2003: {
id: 'app.sfu.mediaServerRequestTimeout2003',
description: "Error message fired when requests are timing out due to lack of resources",
description: 'Error message fired when requests are timing out due to lack of resources',
},
2021: {
id: 'app.sfu.serverIceGatheringFailed2021',
@ -79,7 +83,7 @@ const intlSFUErrors = defineMessages({
2203: {
id: 'app.sfu.noAvailableCodec2203',
description: 'Error message fired when the server has no available codec for the client',
}
},
});
const CAMERA_SHARE_FAILED_WAIT_TIME = 15000;
@ -123,10 +127,10 @@ class VideoProvider extends Component {
}
logger(type, message, options = {}) {
const {userId, userName} = this.props;
const { userId, userName } = this.props;
const topic = options.topic || 'video';
logger[type]({obj: Object.assign(options, {userId, userName, topic})}, `[${topic}] ${message}`);
logger[type]({ obj: Object.assign(options, { userId, userName, topic }) }, `[${topic}] ${message}`);
}
_sendPauseStream(id, role, state) {
@ -215,7 +219,7 @@ class VideoProvider extends Component {
}
onWsOpen() {
this.logger('debug', '------ Websocket connection opened.', {topic: 'ws'});
this.logger('debug', '------ Websocket connection opened.', { topic: 'ws' });
// -- Resend queued messages that happened when socket was not connected
while (this.wsQueue.length > 0) {
@ -228,7 +232,7 @@ class VideoProvider extends Component {
}
onWsClose(error) {
this.logger('debug', '------ Websocket connection closed.', {topic: 'ws'});
this.logger('debug', '------ Websocket connection closed.', { topic: 'ws' });
this.stopWebRTCPeer(this.props.userId);
clearInterval(this.pingInterval);
@ -246,7 +250,7 @@ class VideoProvider extends Component {
onWsMessage(msg) {
const parsedMessage = JSON.parse(msg.data);
this.logger('debug', `Received new message '${parsedMessage.id}'`, {topic: 'ws', message: parsedMessage});
this.logger('debug', `Received new message '${parsedMessage.id}'`, { topic: 'ws', message: parsedMessage });
switch (parsedMessage.id) {
case 'startResponse':
@ -266,7 +270,7 @@ class VideoProvider extends Component {
break;
case 'pong':
this.logger('debug', 'Received pong from server', {topic: 'ws'});
this.logger('debug', 'Received pong from server', { topic: 'ws' });
break;
case 'error':
@ -281,10 +285,10 @@ class VideoProvider extends Component {
if (this.connectedToMediaServer()) {
const jsonMessage = JSON.stringify(message);
this.logger('debug', `Sending message '${message.id}'`, {topic: 'ws', message});
this.logger('debug', `Sending message '${message.id}'`, { topic: 'ws', message });
ws.send(jsonMessage, (error) => {
if (error) {
this.logger(`client: Websocket error '${error}' on message '${message.id}'`, {topic: 'ws'});
this.logger(`client: Websocket error '${error}' on message '${message.id}'`, { topic: 'ws' });
}
});
} else {
@ -303,12 +307,12 @@ class VideoProvider extends Component {
const id = message.cameraId;
const peer = this.webRtcPeers[id];
this.logger('debug', 'SDP answer received from server. Processing ...', {cameraId: id, sdpAnswer: message.sdpAnswer});
this.logger('debug', 'SDP answer received from server. Processing ...', { cameraId: id, sdpAnswer: message.sdpAnswer });
if (peer) {
peer.processAnswer(message.sdpAnswer, (error) => {
if (error) {
return this.logger('debug', JSON.stringify(error), {cameraId: id});
return this.logger('debug', JSON.stringify(error), { cameraId: id });
}
});
} else {
@ -319,13 +323,13 @@ class VideoProvider extends Component {
handleIceCandidate(message) {
const webRtcPeer = this.webRtcPeers[message.cameraId];
this.logger('debug', 'Received remote ice candidate', {topic: 'ice', candidate: message.candidate})
this.logger('debug', 'Received remote ice candidate', { topic: 'ice', candidate: message.candidate });
if (webRtcPeer) {
if (webRtcPeer.didSDPAnswered) {
webRtcPeer.addIceCandidate(message.candidate, (err) => {
if (err) {
return this.logger('error', `Error adding candidate: ${err}`, {cameraId: message.cameraId});
return this.logger('error', `Error adding candidate: ${err}`, { cameraId: message.cameraId });
}
});
} else {
@ -335,15 +339,19 @@ class VideoProvider extends Component {
webRtcPeer.iceQueue.push(message.candidate);
}
} else {
this.logger('warn', ' [iceCandidate] Message arrived after the peer was already thrown out, discarding it...', {cameraId: message.cameraId});
this.logger('warn', ' [iceCandidate] Message arrived after the peer was already thrown out, discarding it...', { cameraId: message.cameraId });
}
}
stopWebRTCPeer(id) {
this.logger('info', 'Stopping webcam', {cameraId: id});
this.logger('info', 'Stopping webcam', { cameraId: id });
const { userId } = this.props;
const shareWebcam = id === userId;
// in this case, 'closed' state is not caused by an error;
// we stop listening to prevent this from being treated as an error
this.webRtcPeers[id].peerConnection.oniceconnectionstatechange = null;
if (shareWebcam) {
this.unshareWebcam();
}
@ -365,11 +373,11 @@ class VideoProvider extends Component {
destroyWebRTCPeer(id) {
const webRtcPeer = this.webRtcPeers[id];
if (webRtcPeer) {
this.logger('info', 'Stopping WebRTC peer', {cameraId: id});
this.logger('info', 'Stopping WebRTC peer', { cameraId: id });
webRtcPeer.dispose();
delete this.webRtcPeers[id];
} else {
this.logger('warn', 'No WebRTC peer to stop (not an error)', {cameraId: id});
this.logger('warn', 'No WebRTC peer to stop (not an error)', { cameraId: id });
}
}
@ -436,7 +444,7 @@ class VideoProvider extends Component {
return this._webRTCOnError(errorGenOffer, id, shareWebcam);
}
this.logger('debug', `Invoking SDP offer callback function ${location.host}`, {cameraId: id, offerSdp});
this.logger('debug', `Invoking SDP offer callback function ${location.host}`, { cameraId: id, offerSdp });
const message = {
type: 'video',
@ -453,6 +461,8 @@ class VideoProvider extends Component {
peer.didSDPAnswered = true;
});
});
this.webRtcPeers[id].peerConnection.oniceconnectionstatechange =
this._getOnIceConnectionStateChangeCallback(id);
}
}
@ -460,7 +470,7 @@ class VideoProvider extends Component {
const { intl } = this.props;
return () => {
this.logger('error', `Camera share has not suceeded in ${CAMERA_SHARE_FAILED_WAIT_TIME}`, {cameraId: id});
this.logger('error', `Camera share has not suceeded in ${CAMERA_SHARE_FAILED_WAIT_TIME}`, { cameraId: id });
if (this.props.userId === id) {
this.notifyError(intl.formatMessage(intlClientErrors.sharingError));
@ -489,7 +499,7 @@ class VideoProvider extends Component {
peer.addIceCandidate(candidate, (err) => {
if (err) {
this.notifyError(intl.formatMessage(intlClientErrors.iceCandidateError));
return this.logger('error', `Error adding candidate: ${err}`, {cameraId});
return this.logger('error', `Error adding candidate: ${err}`, { cameraId });
}
});
}
@ -510,7 +520,7 @@ class VideoProvider extends Component {
this.stopWebRTCPeer(id);
return this.logger('error', errorMessage, {cameraId: id});
return this.logger('error', errorMessage, { cameraId: id });
}
_getOnIceCandidateCallback(id, shareWebcam) {
@ -521,11 +531,11 @@ class VideoProvider extends Component {
if (!this.restartTimeout[id]) {
this.restartTimer[id] = this.restartTimer[id] || CAMERA_SHARE_FAILED_WAIT_TIME;
this.logger('debug', `Setting a camera connection restart in ${this.restartTimer[id]}`, {cameraId: id});
this.logger('debug', `Setting a camera connection restart in ${this.restartTimer[id]}`, { cameraId: id });
this.restartTimeout[id] = setTimeout(this._getWebRTCStartTimeout(id, shareWebcam, peer), this.restartTimer[id]);
}
this.logger('debug', 'Generated local ice candidate', {topic: 'ice', candidate})
this.logger('debug', 'Generated local ice candidate', { topic: 'ice', candidate });
const message = {
type: 'video',
@ -538,6 +548,24 @@ class VideoProvider extends Component {
};
}
_getOnIceConnectionStateChangeCallback(id) {
const { intl } = this.props;
const peer = this.webRtcPeers[id];
return (event) => {
const connectionState = peer.peerConnection.iceConnectionState;
if (connectionState === 'failed' || connectionState === 'closed') {
// prevent the same error from being detected multiple times
peer.peerConnection.oniceconnectionstatechange = null;
this.logger('error', 'ICE connection state', id);
this.stopWebRTCPeer(id);
this.notifyError(intl.formatMessage(intlClientErrors.iceConnectionStateError));
}
};
}
attachVideoStream(id) {
const video = this.videoTags[id];
if (video == null) {
@ -706,7 +734,7 @@ class VideoProvider extends Component {
monitorTrackStart(peer, track, local, callback) {
const that = this;
this.logger('info', 'Starting stats monitoring on', {cameraId: track.id});
this.logger('info', 'Starting stats monitoring on', { cameraId: track.id });
const getStatsInterval = 2000;
const callGetStats = () => {
@ -772,9 +800,9 @@ class VideoProvider extends Component {
}
handlePlayStop(message) {
const {cameraId} = message;
const { cameraId } = message;
this.logger('info', 'Handle play stop for camera', {cameraId});
this.logger('info', 'Handle play stop for camera', { cameraId });
this.stopWebRTCPeer(id);
}
@ -784,7 +812,7 @@ class VideoProvider extends Component {
const videoTag = this.videoTags[id];
if (peer) {
this.logger('info', 'Handle play start for camera', {cameraId: id});
this.logger('info', 'Handle play start for camera', { cameraId: id });
// Clear camera shared timeout when camera succesfully starts
clearTimeout(this.restartTimeout[id]);

View File

@ -322,6 +322,7 @@
"app.video.joinVideo": "Share Webcam",
"app.video.leaveVideo": "Unshare Webcam",
"app.video.iceCandidateError": "Error on adding ice candidate",
"app.video.iceConnectionStateError": "Error 1107: ICE negotiation failed",
"app.video.permissionError": "Error on sharing webcam. Please check permissions",
"app.video.sharingError": "Error on sharing webcam",
"app.video.notFoundError": "Could not find webcam. Please make sure it's connected",