Commit 7d589f53 by Christoph

Fix for Safari / Firefox connection issues by buffering ice candidates until…

Fix for Safari / Firefox connection issues by buffering ice candidates until setRemoteDescription has been called successfully Updated third party components
parent 3b2391cd
...@@ -23,21 +23,21 @@ ...@@ -23,21 +23,21 @@
"url": "https://github.com/because-why-not/awrtc_browser" "url": "https://github.com/because-why-not/awrtc_browser"
}, },
"devDependencies": { "devDependencies": {
"@types/jasmine": "^2.8.16", "@types/jasmine": "^2.8.17",
"jasmine": "^2.99.0", "jasmine": "^2.99.0",
"jasmine-core": "^3.5.0", "jasmine-core": "^3.6.0",
"karma": "^5.0.5", "karma": "^5.2.3",
"karma-chrome-launcher": "^2.2.0", "karma-chrome-launcher": "^2.2.0",
"karma-firefox-launcher": "^1.3.0", "karma-firefox-launcher": "^1.3.0",
"karma-jasmine": "^2.0.1", "karma-jasmine": "^2.0.1",
"shx": "^0.3.2", "shx": "^0.3.2",
"source-map-loader": "^0.2.4", "source-map-loader": "^0.2.4",
"ts-loader": "^5.4.5", "ts-loader": "^5.4.5",
"tsconfig-paths-webpack-plugin": "^3.2.0", "tsconfig-paths-webpack-plugin": "^3.3.0",
"typescript": "^3.8.3", "typescript": "^3.9.7",
"uglify-js": "^2.8.29", "uglify-js": "^2.8.29",
"webpack": "^4.43.0", "webpack": "^4.44.2",
"webpack-cli": "^3.3.11", "webpack-cli": "^3.3.12",
"webrtc-adapter": "^6.4.8" "webrtc-adapter": "^7.x"
} }
} }
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//obsolete. not needed for unity build anymore
//special entry point only needed for backwards compatibility
//it will merge awrtc namespace into window so old code still works
//that accesses objects directly instead using the global awrtc object
//the index will include all external modules
import * as awrtc from "./index"
//we merge awrtc into the global namespace
Object.assign(window, awrtc);
//for less risky global access
(window as any).awrtc = awrtc;
console.debug("loading awrtc modules completed");
\ No newline at end of file
...@@ -121,6 +121,8 @@ export abstract class AWebRtcPeer { ...@@ -121,6 +121,8 @@ export abstract class AWebRtcPeer {
protected mOfferOptions: RTCOfferOptions = { "offerToReceiveAudio": false, "offerToReceiveVideo": false }; protected mOfferOptions: RTCOfferOptions = { "offerToReceiveAudio": false, "offerToReceiveVideo": false };
private mReadyForIce = false;
private mBufferedIceCandidates : RTCIceCandidate[] = [];
constructor(rtcConfig: RTCConfiguration) { constructor(rtcConfig: RTCConfiguration) {
...@@ -140,9 +142,9 @@ export abstract class AWebRtcPeer { ...@@ -140,9 +142,9 @@ export abstract class AWebRtcPeer {
this.mPeer = new RTCPeerConnection(rtcConfig); this.mPeer = new RTCPeerConnection(rtcConfig);
this.mPeer.onicecandidate = this.OnIceCandidate; this.mPeer.onicecandidate = this.OnIceCandidate;
this.mPeer.oniceconnectionstatechange = this.OnIceConnectionStateChange; this.mPeer.oniceconnectionstatechange = this.OnIceConnectionStateChange;
this.mPeer.onconnectionstatechange = this.OnConnectionStateChange;
this.mPeer.onicegatheringstatechange = this.OnIceGatheringStateChange; this.mPeer.onicegatheringstatechange = this.OnIceGatheringStateChange;
this.mPeer.onnegotiationneeded = this.OnRenegotiationNeeded; this.mPeer.onnegotiationneeded = this.OnRenegotiationNeeded;
this.mPeer.onconnectionstatechange = this.OnConnectionStateChange;
this.mPeer.onsignalingstatechange = this.OnSignalingChange; this.mPeer.onsignalingstatechange = this.OnSignalingChange;
} }
...@@ -210,6 +212,44 @@ export abstract class AWebRtcPeer { ...@@ -210,6 +212,44 @@ export abstract class AWebRtcPeer {
} }
} }
private BufferIceCandidate(ice: RTCIceCandidate){
this.mBufferedIceCandidates.push(ice);
}
/**Called after setRemoteDescription succeeded.
* After this call we accept ice candidates and add all buffered ice candidates we received
* until then.
*
* This is a workaround for problems between Safari & Firefox. Safari sometimes sends ice candidates before
* it sends an answer causing an error in firefox.
*/
private StartIce(){
Debug.Log("accepting ice candidates");
this.mReadyForIce = true;
if(this.mBufferedIceCandidates.length > 0)
{
Debug.Log("adding locally buffered ice candidates");
//signaling active. Forward ice candidates we received so far
const candidates = this.mBufferedIceCandidates;
this.mBufferedIceCandidates = [];
for (var candidate of candidates) {
this.AddIceCandidate(candidate);
}
}
}
private AddIceCandidate(ice: RTCIceCandidate){
//based on the shim internals there is a risk it triggers errors outside of the promise
try{
let promise = this.mPeer.addIceCandidate(ice);
promise.then(() => {/*success*/ });
promise.catch((error: DOMError) => { Debug.LogError(error); });
}catch(error){
Debug.LogError(error);
}
}
public HandleIncomingSignaling(): void { public HandleIncomingSignaling(): void {
//handle the incoming messages all at once //handle the incoming messages all at once
...@@ -260,10 +300,18 @@ export abstract class AWebRtcPeer { ...@@ -260,10 +300,18 @@ export abstract class AWebRtcPeer {
} else { } else {
let ice: RTCIceCandidate = new RTCIceCandidate(msg); let ice: RTCIceCandidate = new RTCIceCandidate(msg);
if (ice != null) { if (ice != null) {
let promise = this.mPeer.addIceCandidate(ice);
promise.then(() => {/*success*/ }); if(this.mReadyForIce)
promise.catch((error: DOMError) => { Debug.LogError(error); }); {
//expected normal behaviour
this.AddIceCandidate(ice);
}else{
//Safari sometimes sends ice candidates before the answer message
//causing firefox to trigger an error
//buffer and reemit once setRemoteCandidate has been called
this.BufferIceCandidate(ice);
}
} }
} }
} }
...@@ -345,6 +393,7 @@ export abstract class AWebRtcPeer { ...@@ -345,6 +393,7 @@ export abstract class AWebRtcPeer {
Debug.Log("CreateAnswer"); Debug.Log("CreateAnswer");
let remoteDescPromise = this.mPeer.setRemoteDescription(offer); let remoteDescPromise = this.mPeer.setRemoteDescription(offer);
remoteDescPromise.then(() => { remoteDescPromise.then(() => {
this.StartIce();
let createAnswerPromise = this.mPeer.createAnswer(); let createAnswerPromise = this.mPeer.createAnswer();
createAnswerPromise.then((desc: RTCSessionDescription) => { createAnswerPromise.then((desc: RTCSessionDescription) => {
let msg: string = JSON.stringify(desc); let msg: string = JSON.stringify(desc);
...@@ -378,6 +427,7 @@ export abstract class AWebRtcPeer { ...@@ -378,6 +427,7 @@ export abstract class AWebRtcPeer {
let remoteDescPromise = this.mPeer.setRemoteDescription(answer); let remoteDescPromise = this.mPeer.setRemoteDescription(answer);
remoteDescPromise.then(() => { remoteDescPromise.then(() => {
//all done //all done
this.StartIce();
}); });
remoteDescPromise.catch((error: DOMError) => { remoteDescPromise.catch((error: DOMError) => {
Debug.LogError(error); Debug.LogError(error);
...@@ -422,7 +472,7 @@ export abstract class AWebRtcPeer { ...@@ -422,7 +472,7 @@ export abstract class AWebRtcPeer {
private OnIceConnectionStateChange = (ev: Event): void => private OnIceConnectionStateChange = (ev: Event): void =>
{ {
Debug.Log("on ice connection state: " + this.mPeer.iceConnectionState); Debug.Log("oniceconnectionstatechange: " + this.mPeer.iceConnectionState);
//Chrome stopped emitting "failed" events. We have to react to disconnected events now //Chrome stopped emitting "failed" events. We have to react to disconnected events now
if (this.mPeer.iceConnectionState == "failed" || this.mPeer.iceConnectionState == "disconnected") if (this.mPeer.iceConnectionState == "failed" || this.mPeer.iceConnectionState == "disconnected")
{ {
...@@ -442,21 +492,24 @@ export abstract class AWebRtcPeer { ...@@ -442,21 +492,24 @@ export abstract class AWebRtcPeer {
*/ */
private OnConnectionStateChange = (ev:Event): void => private OnConnectionStateChange = (ev:Event): void =>
{ {
//Debug.Log("on connection state change: " + this.mPeer.iceConnectionState); Debug.Log("onconnectionstatechange: " + this.mPeer.iceConnectionState);
} }
private OnIceGatheringStateChange = (ev:Event): void => private OnIceGatheringStateChange = (ev:Event): void =>
{ {
//Debug.Log("ice gathering change: " + this.mPeer.iceGatheringState); Debug.Log("onicegatheringstatechange: " + this.mPeer.iceGatheringState);
} }
private OnRenegotiationNeeded = (ev:Event): void => private OnRenegotiationNeeded = (ev:Event): void =>
{ } {
}
//broken in chrome. won't switch to closed anymore //broken in chrome. won't switch to closed anymore
private OnSignalingChange = (ev:Event): void => private OnSignalingChange = (ev:Event): void =>
{ {
Debug.Log("on signaling change:" + this.mPeer.signalingState); Debug.Log("onsignalingstatechange:" + this.mPeer.signalingState);
//obsolete
if (this.mPeer.signalingState == "closed") { if (this.mPeer.signalingState == "closed") {
this.RtcSetClosed(); this.RtcSetClosed();
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment