This commit is contained in:
Evgeny Poberezkin committed 2026-09-13 20:10:41 +01:00
1 parent 36d571c84a
commit 2a046564ac
1 file changed
+54 -99
@@ -54,7 +54,6 @@ let inactiveCallMediaSources = {
let activeCall;
let notConnectedCall;
let answerTimeout = 30000;
// How long a call is allowed to stay disconnected before it is ended
let reconnectTimeout = 30000;
var useWorker = false;
var isDesktop = false;
@@ -208,8 +207,7 @@ const processCommand = (function () {
localOrPeerMediaSourcesChanged(call);
await setupMediaStreams(call);
let connectionTimeout = setTimeout(connectionHandler, answerTimeout);
// disconnect call if it does not reconnect in time
let disconnectedTimeout;
let reconnectingTimeout;
let wasConnected = false;
if (pc.connectionState) {
pc.addEventListener("connectionstatechange", connectionStateChange);
@@ -221,44 +219,65 @@ const processCommand = (function () {
async function connectionStateChange() {
// "failed" means the second party did not answer in time (15 sec timeout in Chrome WebView)
// See https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/p2p/base/p2p_constants.cc;l=70)
// Once the call is connected failed needs to work otherwise the call would go on forever
if (pc.connectionState !== "failed" || wasConnected)
connectionHandler();
}
async function connectionHandler() {
if (reconnecting()) {
// The call was connected and lost connection should be reconnected without any action as long as connection issue is fixed
// call only ends if it doesn't reconnect within reconnectTimeout timeframe.
// "reconnecting" is reported instead of "disconnected"/"failed"
if (disconnectedTimeout === undefined) {
sendConnectionState("reconnecting");
disconnectedTimeout = setTimeout(() => {
disconnectedTimeout = undefined;
if (activeCall === call && !connected()) {
// call ends because call did not reconnect
// makes sure to set call to disconnected
sendConnectionState("disconnected");
terminateCall();
}
}, reconnectTimeout);
async function connectionHandler(reconnectTimedOut = false) {
var _a;
let state = (_a = pc.connectionState) !== null && _a !== void 0 ? _a : (pc.iceConnectionState != "completed" && pc.iceConnectionState != "checking"
? pc.iceConnectionState
: pc.iceConnectionState == "completed"
? "connected"
: "connecting"); /* webView 69-70 doesn't have connectionState yet */
if (wasConnected) {
if (reconnectTimedOut) {
reconnectingTimeout = undefined;
if (activeCall !== call || state == "connected")
return;
state = "disconnected";
}
return;
}
if (wasConnected && connecting()) {
return;
}
if (connected()) {
const restored = wasConnected;
wasConnected = true;
clearDisconnectedTimeout();
clearConnectionTimeout();
// reconnecting turns back to connected
// client keeps original call duration
if (restored) {
sendConnectionState();
else if (state == "disconnected" || state == "failed") {
if (reconnectingTimeout !== undefined)
return;
reconnectingTimeout = setTimeout(() => connectionHandler(true), reconnectTimeout);
state = "reconnecting";
}
else if (state == "connecting" || state == "new") {
return;
}
sendConnectionState();
}
sendMessageToNative({
resp: {
type: "connection",
state: {
connectionState: state,
iceConnectionState: pc.iceConnectionState,
iceGatheringState: pc.iceGatheringState,
signalingState: pc.signalingState,
},
},
});
if (state == "disconnected" || state == "failed") {
clearConnectionTimeout();
clearTimeout(reconnectingTimeout);
if (pc.connectionState) {
pc.removeEventListener("connectionstatechange", connectionStateChange);
}
else {
pc.removeEventListener("iceconnectionstatechange", connectionStateChange);
}
if (activeCall) {
setTimeout(() => sendMessageToNative({ resp: { type: "ended" } }), 0);
}
endCall();
}
else if (state == "connected") {
clearConnectionTimeout();
clearTimeout(reconnectingTimeout);
reconnectingTimeout = undefined;
if (wasConnected)
return;
wasConnected = true;
const stats = (await pc.getStats());
for (const stat of stats.values()) {
const { type, state } = stat;
@@ -276,65 +295,7 @@ const processCommand = (function () {
break;
}
}
return;
}
sendConnectionState();
if (disconnected() || failed()) {
// the call was not connected
terminateCall();
}
}
function sendConnectionState(state) {
var _a;
sendMessageToNative({
resp: {
type: "connection",
state: {
connectionState: (_a = state !== null && state !== void 0 ? state : pc.connectionState) !== null && _a !== void 0 ? _a : (pc.iceConnectionState != "completed" && pc.iceConnectionState != "checking"
? pc.iceConnectionState
: pc.iceConnectionState == "completed"
? "connected"
: "connecting") /* webView 69-70 doesn't have connectionState yet */,
iceConnectionState: pc.iceConnectionState,
iceGatheringState: pc.iceGatheringState,
signalingState: pc.signalingState,
},
},
});
}
// webView 69-70 has no connectionState, and it reports a connected call as "completed" as well
function connected() {
return (pc.connectionState == "connected" ||
(!pc.connectionState && (pc.iceConnectionState == "connected" || pc.iceConnectionState == "completed")));
}
function connecting() {
return (pc.connectionState == "connecting" ||
pc.connectionState == "new" ||
(!pc.connectionState && (pc.iceConnectionState == "checking" || pc.iceConnectionState == "new")));
}
function disconnected() {
return pc.connectionState == "disconnected" || (!pc.connectionState && pc.iceConnectionState == "disconnected");
}
function failed() {
return pc.connectionState == "failed" || (!pc.connectionState && pc.iceConnectionState == "failed");
}
// the connected call lost connection and could reconnect
function reconnecting() {
return wasConnected && (disconnected() || failed());
}
function terminateCall() {
clearConnectionTimeout();
clearDisconnectedTimeout();
if (pc.connectionState) {
pc.removeEventListener("connectionstatechange", connectionStateChange);
}
else {
pc.removeEventListener("iceconnectionstatechange", connectionStateChange);
}
if (activeCall) {
setTimeout(() => sendMessageToNative({ resp: { type: "ended" } }), 0);
}
endCall();
}
function clearConnectionTimeout() {
if (connectionTimeout) {
@@ -342,12 +303,6 @@ const processCommand = (function () {
connectionTimeout = undefined;
}
}
function clearDisconnectedTimeout() {
if (disconnectedTimeout !== undefined) {
clearTimeout(disconnectedTimeout);
disconnectedTimeout = undefined;
}
}
}
function serialize(x) {
return LZString.compressToBase64(JSON.stringify(x));