Compare commits
38 Commits
v9.5.0
...
v9.6.0-rc.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 93f6bc3780 | |||
| dde8f23cc3 | |||
| 7cfbd0da95 | |||
| a27ddfaaaf | |||
| b53f616015 | |||
| 22dc175879 | |||
| 5f23e4699c | |||
| a1bd258a7b | |||
| 39a9c54589 | |||
| 5f68370e07 | |||
| dae2de703d | |||
| fa19c40868 | |||
| 1df69d259a | |||
| 90dda0ca68 | |||
| e2d138cac6 | |||
| 15f968d5f8 | |||
| f6aec7f763 | |||
| 212b6c3a0f | |||
| 820256d451 | |||
| 3aba538db3 | |||
| 4820cf8cac | |||
| c289effba0 | |||
| 3edccf496a | |||
| 97b4171b3e | |||
| 4a073a7ba5 | |||
| 9f275d57a9 | |||
| d23bbaeb06 | |||
| c64f7a9ec4 | |||
| 214a9df382 | |||
| 90f6620f1e | |||
| f6e8048d9e | |||
| 5afca17d27 | |||
| 2d7f5ae279 | |||
| 458384d658 | |||
| 159b98132d | |||
| f6d3b50b08 | |||
| 5b1fdb7b37 | |||
| f8f76f6806 |
@@ -35,6 +35,8 @@ module.exports = {
|
||||
"files": ["src/**/*.ts"],
|
||||
"extends": ["matrix-org/ts"],
|
||||
"rules": {
|
||||
// We're okay being explicit at the moment
|
||||
"@typescript-eslint/no-empty-interface": "off",
|
||||
// While we're converting to ts we make heavy use of this
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"quotes": "off",
|
||||
|
||||
@@ -1,3 +1,35 @@
|
||||
Changes in [9.6.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.6.0-rc.1) (2021-01-29)
|
||||
==========================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.5.1...v9.6.0-rc.1)
|
||||
|
||||
* Add support for getting call stats
|
||||
[\#1584](https://github.com/matrix-org/matrix-js-sdk/pull/1584)
|
||||
* Fix compatibility with v0 calls
|
||||
[\#1583](https://github.com/matrix-org/matrix-js-sdk/pull/1583)
|
||||
* Upgrade deps 2021-01
|
||||
[\#1582](https://github.com/matrix-org/matrix-js-sdk/pull/1582)
|
||||
* Log the call ID when logging that we've received VoIP events
|
||||
[\#1581](https://github.com/matrix-org/matrix-js-sdk/pull/1581)
|
||||
* Fix extra negotiate message in Firefox
|
||||
[\#1579](https://github.com/matrix-org/matrix-js-sdk/pull/1579)
|
||||
* Add debug logs to encryption prep
|
||||
[\#1580](https://github.com/matrix-org/matrix-js-sdk/pull/1580)
|
||||
* Expose getPresence endpoint
|
||||
[\#1578](https://github.com/matrix-org/matrix-js-sdk/pull/1578)
|
||||
* Queue keys for backup even if backup isn't enabled yet
|
||||
[\#1577](https://github.com/matrix-org/matrix-js-sdk/pull/1577)
|
||||
* Stop retrying TURN access when forbidden
|
||||
[\#1576](https://github.com/matrix-org/matrix-js-sdk/pull/1576)
|
||||
* Add DTMF sending support
|
||||
[\#1573](https://github.com/matrix-org/matrix-js-sdk/pull/1573)
|
||||
|
||||
Changes in [9.5.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.5.1) (2021-01-26)
|
||||
================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.5.0...v9.5.1)
|
||||
|
||||
* [Release] Fix compatibility with v0 calls
|
||||
[\#1585](https://github.com/matrix-org/matrix-js-sdk/pull/1585)
|
||||
|
||||
Changes in [9.5.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.5.0) (2021-01-18)
|
||||
================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.5.0-rc.1...v9.5.0)
|
||||
|
||||
+13
-13
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "matrix-js-sdk",
|
||||
"version": "9.5.0",
|
||||
"version": "9.6.0-rc.1",
|
||||
"description": "Matrix Client-Server SDK for Javascript",
|
||||
"scripts": {
|
||||
"prepublishOnly": "yarn build",
|
||||
@@ -54,22 +54,22 @@
|
||||
"bs58": "^4.0.1",
|
||||
"content-type": "^1.0.4",
|
||||
"loglevel": "^1.7.1",
|
||||
"qs": "^6.9.4",
|
||||
"qs": "^6.9.6",
|
||||
"request": "^2.88.2",
|
||||
"unhomoglyph": "^1.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.12.8",
|
||||
"@babel/core": "^7.12.9",
|
||||
"@babel/cli": "^7.12.10",
|
||||
"@babel/core": "^7.12.10",
|
||||
"@babel/plugin-proposal-class-properties": "^7.12.1",
|
||||
"@babel/plugin-proposal-numeric-separator": "^7.12.7",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.12.1",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/plugin-transform-runtime": "^7.12.1",
|
||||
"@babel/preset-env": "^7.12.7",
|
||||
"@babel/plugin-transform-runtime": "^7.12.10",
|
||||
"@babel/preset-env": "^7.12.11",
|
||||
"@babel/preset-typescript": "^7.12.7",
|
||||
"@babel/register": "^7.12.1",
|
||||
"@types/jest": "^26.0.15",
|
||||
"@babel/register": "^7.12.10",
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/node": "12",
|
||||
"@types/request": "^2.48.5",
|
||||
"babel-eslint": "^10.1.0",
|
||||
@@ -78,20 +78,20 @@
|
||||
"better-docs": "^2.3.2",
|
||||
"browserify": "^17.0.0",
|
||||
"docdash": "^1.2.0",
|
||||
"eslint": "7.14.0",
|
||||
"eslint-config-matrix-org": "^0.1.2",
|
||||
"eslint": "7.18.0",
|
||||
"eslint-config-matrix-org": "^0.2.0",
|
||||
"eslint-plugin-babel": "^5.3.1",
|
||||
"exorcist": "^1.0.1",
|
||||
"fake-indexeddb": "^3.1.2",
|
||||
"jest": "^26.6.3",
|
||||
"jest-localstorage-mock": "^2.4.3",
|
||||
"jest-localstorage-mock": "^2.4.6",
|
||||
"jsdoc": "^3.6.6",
|
||||
"matrix-mock-request": "^1.2.3",
|
||||
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
|
||||
"rimraf": "^3.0.2",
|
||||
"terser": "^5.5.0",
|
||||
"terser": "^5.5.1",
|
||||
"tsify": "^5.0.2",
|
||||
"typescript": "^4.1.2"
|
||||
"typescript": "^4.1.3"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "node"
|
||||
|
||||
@@ -521,4 +521,19 @@ describe("MatrixClient", function() {
|
||||
xit("should be able to peek into a room using peekInRoom", function(done) {
|
||||
});
|
||||
});
|
||||
|
||||
describe("getPresence", function() {
|
||||
it("should send a presence HTTP GET", function() {
|
||||
httpLookups = [{
|
||||
method: "GET",
|
||||
path: `/presence/${encodeURIComponent(userId)}/status`,
|
||||
data: {
|
||||
"presence": "unavailable",
|
||||
"last_active_ago": 420845,
|
||||
},
|
||||
}];
|
||||
client.getPresence(userId);
|
||||
expect(httpLookups.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3841,6 +3841,19 @@ MatrixClient.prototype.setPresence = function(opts, callback) {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} userId The user to get presence for
|
||||
* @param {module:client.callback} callback Optional.
|
||||
* @return {Promise} Resolves: The presence state for this user.
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixClient.prototype.getPresence = function(userId, callback) {
|
||||
const path = utils.encodeUri("/presence/$userId/status", {
|
||||
$userId: userId,
|
||||
});
|
||||
|
||||
return this._http.authedRequest(callback, "GET", path, undefined, undefined);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve older messages from the given room and put them in the timeline.
|
||||
@@ -5425,6 +5438,11 @@ function checkTurnServers(client) {
|
||||
}
|
||||
}, function(err) {
|
||||
logger.error("Failed to get TURN URIs");
|
||||
// If we get a 403, there's no point in looping forever.
|
||||
if (err.httpStatus === 403) {
|
||||
logger.info("TURN access unavailable for this account");
|
||||
return;
|
||||
}
|
||||
client._checkTurnServersTimeoutID = setTimeout(function() {
|
||||
checkTurnServers(client);
|
||||
}, 60000);
|
||||
|
||||
@@ -264,11 +264,14 @@ MegolmEncryption.prototype._ensureOutboundSession = async function(
|
||||
await Promise.all([
|
||||
(async () => {
|
||||
// share keys with devices that we already have a session for
|
||||
logger.debug(`Sharing keys with existing Olm sessions in ${this._roomId}`);
|
||||
await this._shareKeyWithOlmSessions(
|
||||
session, key, payload, olmSessions,
|
||||
);
|
||||
logger.debug(`Shared keys with existing Olm sessions in ${this._roomId}`);
|
||||
})(),
|
||||
(async () => {
|
||||
logger.debug(`Sharing keys with new Olm sessions in ${this._roomId}`);
|
||||
const errorDevices = [];
|
||||
|
||||
// meanwhile, establish olm sessions for devices that we don't
|
||||
@@ -319,8 +322,10 @@ MegolmEncryption.prototype._ensureOutboundSession = async function(
|
||||
} else {
|
||||
await this._notifyFailedOlmDevices(session, key, errorDevices);
|
||||
}
|
||||
logger.debug(`Shared keys with new Olm sessions in ${this._roomId}`);
|
||||
})(),
|
||||
(async () => {
|
||||
logger.debug(`Notifying blocked devices in ${this._roomId}`);
|
||||
// also, notify blocked devices that they're blocked
|
||||
const blockedMap = {};
|
||||
for (const [userId, userBlockedDevices] of Object.entries(blocked)) {
|
||||
@@ -336,6 +341,7 @@ MegolmEncryption.prototype._ensureOutboundSession = async function(
|
||||
}
|
||||
|
||||
await this._notifyBlockedDevices(session, blockedMap);
|
||||
logger.debug(`Notified blocked devices in ${this._roomId}`);
|
||||
})(),
|
||||
]);
|
||||
};
|
||||
@@ -348,6 +354,11 @@ MegolmEncryption.prototype._ensureOutboundSession = async function(
|
||||
// first wait for the previous share to complete
|
||||
const prom = this._setupPromise.then(prepareSession);
|
||||
|
||||
// Ensure any failures are logged for debugging
|
||||
prom.catch(e => {
|
||||
logger.error(`Failed to ensure outbound session in ${this._roomId}`, e);
|
||||
});
|
||||
|
||||
// _setupPromise resolves to `session` whether or not the share succeeds
|
||||
this._setupPromise = prom.then(returnSession, returnSession);
|
||||
|
||||
@@ -369,17 +380,11 @@ MegolmEncryption.prototype._prepareNewSession = async function() {
|
||||
key.key, {ed25519: this._olmDevice.deviceEd25519Key},
|
||||
);
|
||||
|
||||
if (this._crypto.backupInfo) {
|
||||
// don't wait for it to complete
|
||||
this._crypto.backupGroupSession(
|
||||
this._roomId, this._olmDevice.deviceCurve25519Key, [],
|
||||
sessionId, key.key,
|
||||
).catch((e) => {
|
||||
// This throws if the upload failed, but this is fine
|
||||
// since it will have written it to the db and will retry.
|
||||
logger.log("Failed to back up megolm session", e);
|
||||
});
|
||||
}
|
||||
// don't wait for it to complete
|
||||
this._crypto.backupGroupSession(
|
||||
this._roomId, this._olmDevice.deviceCurve25519Key, [],
|
||||
sessionId, key.key,
|
||||
);
|
||||
|
||||
return new OutboundSessionInfo(sessionId);
|
||||
};
|
||||
@@ -846,24 +851,41 @@ MegolmEncryption.prototype.prepareToEncrypt = function(room) {
|
||||
// We're already preparing something, so don't do anything else.
|
||||
// FIXME: check if we need to restart
|
||||
// (https://github.com/matrix-org/matrix-js-sdk/issues/1255)
|
||||
const elapsedTime = Date.now() - this.encryptionPreparationMetadata.startTime;
|
||||
logger.debug(
|
||||
`Already started preparing to encrypt for ${this._roomId} ` +
|
||||
`${elapsedTime} ms ago, skipping`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug(`Preparing to encrypt events for ${this._roomId}`);
|
||||
|
||||
this.encryptionPreparationMetadata = {
|
||||
startTime: Date.now(),
|
||||
};
|
||||
this.encryptionPreparation = (async () => {
|
||||
const [devicesInRoom, blocked] = await this._getDevicesInRoom(room);
|
||||
try {
|
||||
logger.debug(`Getting devices in ${this._roomId}`);
|
||||
const [devicesInRoom, blocked] = await this._getDevicesInRoom(room);
|
||||
|
||||
if (this._crypto.getGlobalErrorOnUnknownDevices()) {
|
||||
// Drop unknown devices for now. When the message gets sent, we'll
|
||||
// throw an error, but we'll still be prepared to send to the known
|
||||
// devices.
|
||||
this._removeUnknownDevices(devicesInRoom);
|
||||
if (this._crypto.getGlobalErrorOnUnknownDevices()) {
|
||||
// Drop unknown devices for now. When the message gets sent, we'll
|
||||
// throw an error, but we'll still be prepared to send to the known
|
||||
// devices.
|
||||
this._removeUnknownDevices(devicesInRoom);
|
||||
}
|
||||
|
||||
logger.debug(`Ensuring outbound session in ${this._roomId}`);
|
||||
await this._ensureOutboundSession(devicesInRoom, blocked, true);
|
||||
|
||||
logger.debug(`Ready to encrypt events for ${this._roomId}`);
|
||||
} catch (e) {
|
||||
logger.error(`Failed to prepare to encrypt events for ${this._roomId}`, e);
|
||||
} finally {
|
||||
delete this.encryptionPreparationMetadata;
|
||||
delete this.encryptionPreparation;
|
||||
}
|
||||
|
||||
await this._ensureOutboundSession(devicesInRoom, blocked, true);
|
||||
|
||||
delete this.encryptionPreparation;
|
||||
})();
|
||||
};
|
||||
|
||||
@@ -1347,18 +1369,12 @@ MegolmDecryption.prototype.onRoomKeyEvent = function(event) {
|
||||
}
|
||||
});
|
||||
}).then(() => {
|
||||
if (this._crypto.backupInfo) {
|
||||
// don't wait for the keys to be backed up for the server
|
||||
this._crypto.backupGroupSession(
|
||||
content.room_id, senderKey, forwardingKeyChain,
|
||||
content.session_id, content.session_key, keysClaimed,
|
||||
exportFormat,
|
||||
).catch((e) => {
|
||||
// This throws if the upload failed, but this is fine
|
||||
// since it will have written it to the db and will retry.
|
||||
logger.log("Failed to back up megolm session", e);
|
||||
});
|
||||
}
|
||||
// don't wait for the keys to be backed up for the server
|
||||
this._crypto.backupGroupSession(
|
||||
content.room_id, senderKey, forwardingKeyChain,
|
||||
content.session_id, content.session_key, keysClaimed,
|
||||
exportFormat,
|
||||
);
|
||||
}).catch((e) => {
|
||||
logger.error(`Error handling m.room_key_event: ${e}`);
|
||||
});
|
||||
@@ -1564,7 +1580,7 @@ MegolmDecryption.prototype.importRoomKey = function(session, opts = {}) {
|
||||
true,
|
||||
opts.untrusted ? { untrusted: opts.untrusted } : {},
|
||||
).then(() => {
|
||||
if (this._crypto.backupInfo && opts.source !== "backup") {
|
||||
if (opts.source !== "backup") {
|
||||
// don't wait for it to complete
|
||||
this._crypto.backupGroupSession(
|
||||
session.room_id,
|
||||
|
||||
+7
-7
@@ -2884,18 +2884,18 @@ Crypto.prototype.backupGroupSession = async function(
|
||||
sessionId, sessionKey, keysClaimed,
|
||||
exportFormat,
|
||||
) {
|
||||
if (!this.backupInfo) {
|
||||
throw new Error("Key backups are not enabled");
|
||||
}
|
||||
|
||||
await this._cryptoStore.markSessionsNeedingBackup([{
|
||||
senderKey: senderKey,
|
||||
sessionId: sessionId,
|
||||
}]);
|
||||
|
||||
// don't wait for this to complete: it will delay so
|
||||
// happens in the background
|
||||
this.scheduleKeyBackupSend();
|
||||
if (this.backupInfo) {
|
||||
// don't wait for this to complete: it will delay so
|
||||
// happens in the background
|
||||
this.scheduleKeyBackupSend();
|
||||
}
|
||||
// if this.backupInfo is not set, then the keys will be backed up when
|
||||
// client.enableKeyBackup is called
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+71
-14
@@ -260,6 +260,11 @@ export class MatrixCall extends EventEmitter {
|
||||
private micMuted;
|
||||
private vidMuted;
|
||||
|
||||
// the stats for the call at the point it ended. We can't get these after we
|
||||
// tear the call down, so we just grab a snapshot before we stop the call.
|
||||
// The typescript definitions have this type as 'any' :(
|
||||
private callStatsAtEnd: any[];
|
||||
|
||||
// Perfect negotiation state: https://www.w3.org/TR/webrtc/#perfect-negotiation-example
|
||||
private makingOffer: boolean;
|
||||
private ignoreOffer: boolean;
|
||||
@@ -271,6 +276,9 @@ export class MatrixCall extends EventEmitter {
|
||||
this.type = null;
|
||||
this.forceTURN = opts.forceTURN;
|
||||
this.ourPartyId = this.client.deviceId;
|
||||
// We compare this to null to checks the presence of a party ID:
|
||||
// make sure it's null, not undefined
|
||||
this.opponentPartyId = null;
|
||||
// Array of Objects with urls, username, credential keys
|
||||
this.turnServers = opts.turnServers || [];
|
||||
if (this.turnServers.length === 0 && this.client.isFallbackICEServerAllowed()) {
|
||||
@@ -361,11 +369,11 @@ export class MatrixCall extends EventEmitter {
|
||||
this.type = CallType.Video;
|
||||
}
|
||||
|
||||
getOpponentMember() {
|
||||
public getOpponentMember() {
|
||||
return this.opponentMember;
|
||||
}
|
||||
|
||||
opponentCanBeTransferred() {
|
||||
public opponentCanBeTransferred() {
|
||||
return Boolean(this.opponentCaps && this.opponentCaps["m.call.transferee"]);
|
||||
}
|
||||
|
||||
@@ -373,7 +381,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* Retrieve the local <code><video></code> DOM element.
|
||||
* @return {Element} The dom element
|
||||
*/
|
||||
getLocalVideoElement(): HTMLVideoElement {
|
||||
public getLocalVideoElement(): HTMLVideoElement {
|
||||
return this.localVideoElement;
|
||||
}
|
||||
|
||||
@@ -382,7 +390,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* used for playing back video capable streams.
|
||||
* @return {Element} The dom element
|
||||
*/
|
||||
getRemoteVideoElement(): HTMLVideoElement {
|
||||
public getRemoteVideoElement(): HTMLVideoElement {
|
||||
return this.remoteVideoElement;
|
||||
}
|
||||
|
||||
@@ -391,7 +399,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* used for playing back audio only streams.
|
||||
* @return {Element} The dom element
|
||||
*/
|
||||
getRemoteAudioElement(): HTMLAudioElement {
|
||||
public getRemoteAudioElement(): HTMLAudioElement {
|
||||
return this.remoteAudioElement;
|
||||
}
|
||||
|
||||
@@ -400,7 +408,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* video will be rendered to it immediately.
|
||||
* @param {Element} element The <code><video></code> DOM element.
|
||||
*/
|
||||
async setLocalVideoElement(element : HTMLVideoElement) {
|
||||
public async setLocalVideoElement(element : HTMLVideoElement) {
|
||||
this.localVideoElement = element;
|
||||
|
||||
if (element && this.localAVStream && this.type === CallType.Video) {
|
||||
@@ -421,7 +429,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* the first received video-capable stream will be rendered to it immediately.
|
||||
* @param {Element} element The <code><video></code> DOM element.
|
||||
*/
|
||||
setRemoteVideoElement(element : HTMLVideoElement) {
|
||||
public setRemoteVideoElement(element : HTMLVideoElement) {
|
||||
if (element === this.remoteVideoElement) return;
|
||||
|
||||
element.autoplay = true;
|
||||
@@ -443,7 +451,7 @@ export class MatrixCall extends EventEmitter {
|
||||
* The audio will *not* be rendered from the remoteVideoElement.
|
||||
* @param {Element} element The <code><video></code> DOM element.
|
||||
*/
|
||||
async setRemoteAudioElement(element: HTMLAudioElement) {
|
||||
public async setRemoteAudioElement(element: HTMLAudioElement) {
|
||||
if (element === this.remoteAudioElement) return;
|
||||
|
||||
this.remoteAudioElement = element;
|
||||
@@ -451,6 +459,25 @@ export class MatrixCall extends EventEmitter {
|
||||
if (this.remoteStream) this.playRemoteAudio();
|
||||
}
|
||||
|
||||
// The typescript definitions have this type as 'any' :(
|
||||
public async getCurrentCallStats(): Promise<any[]> {
|
||||
if (this.callHasEnded()) {
|
||||
return this.callStatsAtEnd;
|
||||
}
|
||||
|
||||
return this.collectCallStats();
|
||||
}
|
||||
|
||||
private async collectCallStats(): Promise<any[]> {
|
||||
const statsReport = await this.peerConn.getStats();
|
||||
const stats = [];
|
||||
for (const item of statsReport) {
|
||||
stats.push(item[1]);
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure this call from an invite event. Used by MatrixClient.
|
||||
* @param {MatrixEvent} event The m.call.invite event
|
||||
@@ -709,6 +736,21 @@ export class MatrixCall extends EventEmitter {
|
||||
return callOnHold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a DTMF digit to the other party
|
||||
* @param digit The digit (nb. string - '#' and '*' are dtmf too)
|
||||
*/
|
||||
sendDtmfDigit(digit: string) {
|
||||
for (const sender of this.peerConn.getSenders()) {
|
||||
if (sender.track.kind === 'audio' && sender.dtmf) {
|
||||
sender.dtmf.insertDTMF(digit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("Unable to find a track to send DTMF on");
|
||||
}
|
||||
|
||||
private updateMuteStatus() {
|
||||
if (!this.localAVStream) {
|
||||
return;
|
||||
@@ -972,7 +1014,7 @@ export class MatrixCall extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.opponentPartyId !== undefined) {
|
||||
if (this.opponentPartyId !== null) {
|
||||
logger.info(
|
||||
`Ignoring answer from party ID ${event.getContent().party_id}: ` +
|
||||
`we already have an answer/reject from ${this.opponentPartyId}`,
|
||||
@@ -1069,8 +1111,21 @@ export class MatrixCall extends EventEmitter {
|
||||
await this.peerConn.setRemoteDescription(description);
|
||||
|
||||
if (description.type === 'offer') {
|
||||
// First we sent the direction of the tranciever to what we'd like it to be,
|
||||
// irresepective of whether the other side has us on hold - so just whether we
|
||||
// want the call to be on hold or not. This is necessary because in a few lines,
|
||||
// we'll adjust the direction and unless we do this too, we'll never come off hold.
|
||||
for (const tranceiver of this.peerConn.getTransceivers()) {
|
||||
tranceiver.direction = this.isRemoteOnHold() ? 'inactive' : 'sendrecv';
|
||||
}
|
||||
const localDescription = await this.peerConn.createAnswer();
|
||||
await this.peerConn.setLocalDescription(localDescription);
|
||||
// Now we've got our answer, set the direction to the outcome of the negotiation.
|
||||
// We need to do this otherwise Firefox will notice that the direction is not the
|
||||
// currentDirection and try to negotiate itself off hold again.
|
||||
for (const tranceiver of this.peerConn.getTransceivers()) {
|
||||
tranceiver.direction = tranceiver.currentDirection;
|
||||
}
|
||||
|
||||
this.sendVoipEvent(EventType.CallNegotiate, {
|
||||
description: this.peerConn.localDescription,
|
||||
@@ -1214,7 +1269,7 @@ export class MatrixCall extends EventEmitter {
|
||||
return; // because ICE can still complete as we're ending the call
|
||||
}
|
||||
logger.debug(
|
||||
"ICE connection state changed to: " + this.peerConn.iceConnectionState,
|
||||
"Call ID " + this.callId + ": ICE connection state changed to: " + this.peerConn.iceConnectionState,
|
||||
);
|
||||
// ideally we'd consider the call to be connected when we get media but
|
||||
// chrome doesn't implement any of the 'onstarted' events yet
|
||||
@@ -1332,11 +1387,11 @@ export class MatrixCall extends EventEmitter {
|
||||
}
|
||||
|
||||
onHangupReceived = (msg) => {
|
||||
logger.debug("Hangup received");
|
||||
logger.debug("Hangup received for call ID " + + this.callId);
|
||||
|
||||
// party ID must match (our chosen partner hanging up the call) or be undefined (we haven't chosen
|
||||
// a partner yet but we're treating the hangup as a reject as per VoIP v0)
|
||||
if (this.partyIdMatches(msg) || this.opponentPartyId === undefined || this.state === CallState.Ringing) {
|
||||
if (this.partyIdMatches(msg) || this.state === CallState.Ringing) {
|
||||
// default reason is user_hangup
|
||||
this.terminate(CallParty.Remote, msg.reason || CallErrorCode.UserHangup, true);
|
||||
} else {
|
||||
@@ -1345,7 +1400,7 @@ export class MatrixCall extends EventEmitter {
|
||||
};
|
||||
|
||||
onRejectReceived = (msg) => {
|
||||
logger.debug("Reject received");
|
||||
logger.debug("Reject received for call ID " + this.callId);
|
||||
|
||||
// No need to check party_id for reject because if we'd received either
|
||||
// an answer or reject, we wouldn't be in state InviteSent
|
||||
@@ -1367,7 +1422,7 @@ export class MatrixCall extends EventEmitter {
|
||||
};
|
||||
|
||||
onAnsweredElsewhere = (msg) => {
|
||||
logger.debug("Answered elsewhere");
|
||||
logger.debug("Call ID " + this.callId + " answered elsewhere");
|
||||
this.terminate(CallParty.Remote, CallErrorCode.AnsweredElsewhere, true);
|
||||
};
|
||||
|
||||
@@ -1440,6 +1495,8 @@ export class MatrixCall extends EventEmitter {
|
||||
private async terminate(hangupParty: CallParty, hangupReason: CallErrorCode, shouldEmit: boolean) {
|
||||
if (this.callHasEnded()) return;
|
||||
|
||||
this.callStatsAtEnd = await this.collectCallStats();
|
||||
|
||||
if (this.inviteTimeout) {
|
||||
clearTimeout(this.inviteTimeout);
|
||||
this.inviteTimeout = null;
|
||||
|
||||
Reference in New Issue
Block a user