Compare commits

...

43 Commits

Author SHA1 Message Date
RiotRobot f7753f8be3 v9.4.0-rc.1 2020-12-16 14:17:25 +00:00
RiotRobot d2dfa29556 Prepare changelog for v9.4.0-rc.1 2020-12-16 14:17:25 +00:00
David Baker 035cb9fe08 Merge pull request #1553 from matrix-org/dbkr/line_1_2
Fixes to support line 1 / 2
2020-12-11 10:26:23 +00:00
David Baker 58d0018174 Merge remote-tracking branch 'origin/develop' into dbkr/line_1_2 2020-12-10 14:00:47 +00:00
David Baker 52ed0f8615 Merge pull request #1549 from matrix-org/dbkr/hold_ui
Add API for listening to remote hold status, advertise VoIP V1
2020-12-10 12:30:46 +00:00
David Baker 2a46513dfd remove outdated comment
(it did work - I just was not checking the flag in the right place)
2020-12-09 11:21:23 +00:00
David Baker 907567182d Mute speaker when putting a call on hold 2020-12-08 19:45:11 +00:00
David Baker 46cebcd1ca Merge pull request #1555 from matrix-org/dbkr/fix_hangup_from_other_client
A hangup from another client is still valid
2020-12-08 19:02:00 +00:00
David Baker 40198f95dc A hangup from another client is still valid
if we're in the ringing state

Fixes https://github.com/vector-im/element-web/issues/15933
2020-12-08 18:53:11 +00:00
David Baker 736b934b18 Merge remote-tracking branch 'origin/develop' into dbkr/hold_ui 2020-12-08 11:59:32 +00:00
J. Ryan Stinnett b009811ac9 Merge pull request #1554 from matrix-org/jryans/rm-temp-test-build
Remove temporary build step for tests
2020-12-08 10:48:56 +00:00
RiotRobot ff6612f9d0 Merge branch 'master' into develop 2020-12-07 12:10:31 +00:00
RiotRobot 565d446b1d v9.3.0 2020-12-07 12:07:32 +00:00
RiotRobot 044d334398 Prepare changelog for v9.3.0 2020-12-07 12:07:31 +00:00
J. Ryan Stinnett e31ef2dfb5 Remove temporary build step for tests
Now that the pipeline has been updated to build for tests, we can remove this
temporary build in the test script.

Related to https://github.com/matrix-org/pipelines/pull/113
2020-12-07 10:21:26 +00:00
J. Ryan Stinnett c20566083a Merge pull request #1552 from matrix-org/jryans/browser-prepublishonly
Move browser build steps to prepublish only
2020-12-07 10:19:07 +00:00
David Baker 9845553a5f playRemoteVideo should be with it's friend, playRemoteAudio 2020-12-04 20:03:58 +00:00
David Baker 03737546fe Remove the media operation queues
As per the comment on playRemoteVideo()
2020-12-04 20:03:11 +00:00
J. Ryan Stinnett 579cb00c98 Temporarily build browser mode for tests 2020-12-04 14:19:46 +00:00
David Baker a307950213 Merge remote-tracking branch 'origin/develop' into dbkr/hold_ui 2020-12-03 19:26:36 +00:00
David Baker 0f5c469be6 Merge remote-tracking branch 'origin/develop' into dbkr/line_1_2 2020-12-03 17:55:59 +00:00
David Baker 6f7e409e9a Some little fixes to support line 1 / 2
* Resume playing audio at the appropriate time
 * Re-emit call events (they were the exception before - all other events
   were re-emitted through the MatrixClient)
 * Fix an audio/video typo
2020-12-03 17:41:34 +00:00
J. Ryan Stinnett d707912d81 Move browser build steps to prepublish only
This speeds local development and CI runs by only running the browser build
steps at release time.
2020-12-03 14:58:28 +00:00
Michael Telatynski 97ab680f4e Merge pull request #1541 from matrix-org/t3chguy/socials
Extend getSsoLoginUrl for MSC2858
2020-12-02 15:48:40 +00:00
Michael Telatynski ea35a29bd8 Merge branch 'develop' of github.com:matrix-org/matrix-js-sdk into t3chguy/socials 2020-12-02 15:18:37 +00:00
RiotRobot 63c182bc3e v9.3.0-rc.1 2020-12-02 14:20:20 +00:00
RiotRobot b1a0a12a5f Prepare changelog for v9.3.0-rc.1 2020-12-02 14:20:19 +00:00
David Baker cc242230be Update comment 2020-11-27 14:42:18 +00:00
David Baker aef9211ea8 Merge pull request #1551 from matrix-org/dbkr/user_media_error
Export CallError
2020-11-27 14:40:59 +00:00
David Baker de5d557882 Export CallError
So the types can be typed
2020-11-27 12:57:40 +00:00
J. Ryan Stinnett fa9adf199d Merge pull request #1550 from matrix-org/jryans/upgrade-deps-2020-11-25
Upgrade dependencies
2020-11-26 15:50:28 +00:00
J. Ryan Stinnett 0807066f1f Nested upgrades via yarn upgrade 2020-11-26 15:06:30 +00:00
J. Ryan Stinnett 142e79941d Upgrade to latest major version of direct deps 2020-11-26 15:04:52 +00:00
David Baker 5e9ce38a24 Add API for listening to remote hold status
And avoid isLocalOnHold() returning true whilst the remote side
is un-held.
2020-11-26 14:32:47 +00:00
J. Ryan Stinnett f42e6373c4 Merge pull request #1547 from dalcde/error
Don't log error when environment does not support WebRTC
2020-11-26 12:13:49 +00:00
Dexter Chua bc46609caa Don't log error when WebRTC not supported
This function is *always* called when a MatrixClient is created, e.g. in
an appservice. If the environment does not support WebRTC, this is not
necessarily an error; it is expected in many situations.

Fix a small typo

Signed-off-by: Dexter Chua <dec41@srcf.net>
2020-11-26 13:55:36 +08:00
J. Ryan Stinnett cc44abe2d3 Nested upgrades via yarn upgrade 2020-11-25 17:22:20 +00:00
J. Ryan Stinnett db6398acdd Upgrade to latest minor version of direct deps 2020-11-25 17:16:22 +00:00
RiotRobot 6661bde608 Merge branch 'master' into develop 2020-11-23 16:23:01 +00:00
J. Ryan Stinnett 5993dd588c Merge pull request #1544 from matrix-org/jryans/fix-dehydration-method
Fix dehydration method name
2020-11-19 16:24:25 +00:00
J. Ryan Stinnett 3ef91e16d8 Fix dehydration method name
https://github.com/matrix-org/matrix-js-sdk/pull/1537 changed some dehydration
method names, but one call site was missed.
2020-11-19 16:16:40 +00:00
Michael Telatynski 99bff04ccc Update base-apis.js 2020-11-17 15:25:17 +00:00
Michael Telatynski bd906e619d Extend getSsoLoginUrl for MSC2858 2020-11-17 09:55:14 +00:00
5 changed files with 2395 additions and 2336 deletions
+36
View File
@@ -1,3 +1,39 @@
Changes in [9.4.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.4.0-rc.1) (2020-12-16)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.3.0...v9.4.0-rc.1)
* Fixes to support line 1 / 2
[\#1553](https://github.com/matrix-org/matrix-js-sdk/pull/1553)
* Add API for listening to remote hold status, advertise VoIP V1
[\#1549](https://github.com/matrix-org/matrix-js-sdk/pull/1549)
* A hangup from another client is still valid
[\#1555](https://github.com/matrix-org/matrix-js-sdk/pull/1555)
* Remove temporary build step for tests
[\#1554](https://github.com/matrix-org/matrix-js-sdk/pull/1554)
* Move browser build steps to prepublish only
[\#1552](https://github.com/matrix-org/matrix-js-sdk/pull/1552)
* Extend getSsoLoginUrl for MSC2858
[\#1541](https://github.com/matrix-org/matrix-js-sdk/pull/1541)
Changes in [9.3.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.3.0) (2020-12-07)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.3.0-rc.1...v9.3.0)
* No changes since rc.1
Changes in [9.3.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.3.0-rc.1) (2020-12-02)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.2.0...v9.3.0-rc.1)
* Export CallError
[\#1551](https://github.com/matrix-org/matrix-js-sdk/pull/1551)
* Upgrade dependencies
[\#1550](https://github.com/matrix-org/matrix-js-sdk/pull/1550)
* Don't log error when environment does not support WebRTC
[\#1547](https://github.com/matrix-org/matrix-js-sdk/pull/1547)
* Fix dehydration method name
[\#1544](https://github.com/matrix-org/matrix-js-sdk/pull/1544)
Changes in [9.2.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.2.0) (2020-11-23)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.2.0-rc.1...v9.2.0)
+24 -22
View File
@@ -1,13 +1,15 @@
{
"name": "matrix-js-sdk",
"version": "9.2.0",
"version": "9.4.0-rc.1",
"description": "Matrix Client-Server SDK for Javascript",
"scripts": {
"prepare": "yarn build",
"prepublishOnly": "yarn build",
"postinstall": "yarn build:dev",
"start": "echo THIS IS FOR LEGACY PURPOSES ONLY. && babel src -w -s -d lib --verbose --extensions \".ts,.js\"",
"dist": "echo 'This is for the release script so it can make assets (browser bundle).' && yarn build",
"clean": "rimraf lib dist",
"build": "yarn clean && git rev-parse HEAD > git-revision.txt && yarn build:compile && yarn build:compile-browser && yarn build:minify-browser && yarn build:types",
"build": "yarn build:dev && yarn build:compile-browser && yarn build:minify-browser",
"build:dev": "yarn clean && git rev-parse HEAD > git-revision.txt && yarn build:compile && yarn build:types",
"build:types": "tsc --emitDeclarationOnly",
"build:compile": "babel -d lib --verbose --extensions \".ts,.js\" src",
"build:compile-browser": "mkdirp dist && browserify -d src/browser-index.js -p [ tsify -p ./tsconfig.json ] -t [ babelify --sourceMaps=inline --presets [ @babel/preset-env @babel/preset-typescript ] ] | exorcist dist/browser-matrix.js.map > dist/browser-matrix.js",
@@ -46,50 +48,50 @@
"release.sh"
],
"dependencies": {
"@babel/runtime": "^7.11.2",
"@babel/runtime": "^7.12.5",
"another-json": "^0.2.0",
"browser-request": "^0.3.3",
"bs58": "^4.0.1",
"content-type": "^1.0.4",
"loglevel": "^1.7.0",
"loglevel": "^1.7.1",
"qs": "^6.9.4",
"request": "^2.88.2",
"unhomoglyph": "^1.0.6"
},
"devDependencies": {
"@babel/cli": "^7.11.6",
"@babel/core": "^7.11.6",
"@babel/plugin-proposal-class-properties": "^7.10.4",
"@babel/plugin-proposal-numeric-separator": "^7.10.4",
"@babel/plugin-proposal-object-rest-spread": "^7.11.0",
"@babel/cli": "^7.12.8",
"@babel/core": "^7.12.9",
"@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.11.5",
"@babel/preset-env": "^7.11.5",
"@babel/preset-typescript": "^7.10.4",
"@babel/register": "^7.11.5",
"@types/jest": "^26.0.14",
"@babel/plugin-transform-runtime": "^7.12.1",
"@babel/preset-env": "^7.12.7",
"@babel/preset-typescript": "^7.12.7",
"@babel/register": "^7.12.1",
"@types/jest": "^26.0.15",
"@types/node": "12",
"@types/request": "^2.48.5",
"babel-eslint": "^10.1.0",
"babel-jest": "^24.9.0",
"babel-jest": "^26.6.3",
"babelify": "^10.0.0",
"better-docs": "^2.3.2",
"browserify": "^16.5.2",
"browserify": "^17.0.0",
"docdash": "^1.2.0",
"eslint": "7.9.0",
"eslint": "7.14.0",
"eslint-config-matrix-org": "^0.1.2",
"eslint-plugin-babel": "^5.3.1",
"exorcist": "^1.0.1",
"fake-indexeddb": "^3.1.2",
"jest": "^24.9.0",
"jest": "^26.6.3",
"jest-localstorage-mock": "^2.4.3",
"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": "^4.8.0",
"tsify": "^4.0.2",
"typescript": "^3.9.7"
"terser": "^5.5.0",
"tsify": "^5.0.2",
"typescript": "^4.1.2"
},
"jest": {
"testEnvironment": "node"
+11 -4
View File
@@ -356,15 +356,22 @@ MatrixBaseApis.prototype.getCasLoginUrl = function(redirectUrl) {
* authenticates with the SSO.
* @param {string} loginType The type of SSO login we are doing (sso or cas).
* Defaults to 'sso'.
* @param {string} idpId The ID of the Identity Provider being targeted, optional.
* @return {string} The HS URL to hit to begin the SSO login process.
*/
MatrixBaseApis.prototype.getSsoLoginUrl = function(redirectUrl, loginType) {
MatrixBaseApis.prototype.getSsoLoginUrl = function(redirectUrl, loginType, idpId) {
if (loginType === undefined) {
loginType = "sso";
}
return this._http.getUrl("/login/"+loginType+"/redirect", {
"redirectUrl": redirectUrl,
}, PREFIX_R0);
let prefix = PREFIX_R0;
let url = "/login/" + loginType + "/redirect";
if (idpId) {
url += "/" + idpId;
prefix = "/_matrix/client/unstable/org.matrix.msc2858";
}
return this._http.getUrl(url, { redirectUrl }, prefix);
};
/**
+147 -116
View File
@@ -26,6 +26,7 @@ import {EventEmitter} from 'events';
import * as utils from '../utils';
import MatrixEvent from '../models/event';
import {EventType} from '../@types/event';
import { RoomMember } from '../models/room-member';
// events: hangup, error(err), replaced(call), state(state, oldState)
@@ -93,15 +94,13 @@ export enum CallEvent {
Replaced = 'replaced',
// The value of isLocalOnHold() has changed
LocalHoldUnhold = 'local_hold_unhold',
// The value of isRemoteOnHold() has changed
RemoteHoldUnhold = 'remote_hold_unhold',
// backwards compat alias for LocalHoldUnhold: remove in a major version bump
HoldUnhold = 'hold_unhold',
}
enum MediaQueueId {
RemoteVideo = 'remote_video',
RemoteAudio = 'remote_audio',
LocalVideo = 'local_video',
}
export enum CallErrorCode {
/** The user chose to end the call */
UserHangup = 'user_hangup',
@@ -175,11 +174,8 @@ export enum CallErrorCode {
/**
* The version field that we set in m.call.* events
* Once we are able to speak v1 VoIP sufficiently, this
* bumped to 1. While we partially speak v1 VoIP, it remains
* as 0.
*/
const VOIP_PROTO_VERSION = 0;
const VOIP_PROTO_VERSION = 1;
/** The fallback ICE server to use for STUN or TURN protocols. */
const FALLBACK_ICE_SERVER = 'stun:turn.matrix.org';
@@ -187,7 +183,7 @@ const FALLBACK_ICE_SERVER = 'stun:turn.matrix.org';
/** The length of time a call can be ringing for. */
const CALL_TIMEOUT_MS = 60000;
class CallError extends Error {
export class CallError extends Error {
code : string;
constructor(code : CallErrorCode, msg: string, err: Error) {
@@ -224,7 +220,6 @@ export class MatrixCall extends EventEmitter {
private turnServers: Array<TurnServer>;
private candidateSendQueue: Array<RTCIceCandidate>;
private candidateSendTries: number;
private mediaPromises: { [queueId: string]: Promise<void>; };
private sentEndOfCandidates: boolean;
private peerConn: RTCPeerConnection;
private localVideoElement: HTMLVideoElement;
@@ -240,6 +235,7 @@ export class MatrixCall extends EventEmitter {
// XXX: I don't know why this is called 'config'.
private config: MediaStreamConstraints;
private successor: MatrixCall;
private opponentMember: RoomMember;
private opponentVersion: number;
// The party ID of the other side: undefined if we haven't chosen a partner
// yet, null if we have but they didn't send a party ID.
@@ -249,6 +245,11 @@ export class MatrixCall extends EventEmitter {
// The logic of when & if a call is on hold is nontrivial and explained in is*OnHold
// This flag represents whether we want the other party to be on hold
private remoteOnHold;
// and this one we set when we're transitioning out of the hold state because we
// can't tell the difference between that and the other party holding us
private unholdingRemote;
private micMuted;
private vidMuted;
@@ -283,17 +284,12 @@ export class MatrixCall extends EventEmitter {
this.candidateSendQueue = [];
this.candidateSendTries = 0;
// Lookup from opaque queue ID to a promise for media element operations that
// need to be serialised into a given queue. Store this per-MatrixCall on the
// assumption that multiple matrix calls will never compete for control of the
// same DOM elements.
this.mediaPromises = Object.create(null);
this.sentEndOfCandidates = false;
this.inviteOrAnswerSent = false;
this.makingOffer = false;
this.remoteOnHold = false;
this.unholdingRemote = false;
this.micMuted = false;
this.vidMuted = false;
}
@@ -358,12 +354,8 @@ export class MatrixCall extends EventEmitter {
this.type = CallType.Video;
}
private queueMediaOperation(queueId: MediaQueueId, operation: () => any) {
if (this.mediaPromises[queueId] !== undefined) {
this.mediaPromises[queueId] = this.mediaPromises[queueId].then(operation, operation);
} else {
this.mediaPromises[queueId] = Promise.resolve(operation());
}
getOpponentMember() {
return this.opponentMember;
}
/**
@@ -397,17 +389,19 @@ export class MatrixCall extends EventEmitter {
* video will be rendered to it immediately.
* @param {Element} element The <code>&lt;video&gt;</code> DOM element.
*/
setLocalVideoElement(element : HTMLVideoElement) {
async setLocalVideoElement(element : HTMLVideoElement) {
this.localVideoElement = element;
if (element && this.localAVStream && this.type === CallType.Video) {
element.autoplay = true;
this.queueMediaOperation(MediaQueueId.LocalVideo, () => {
element.srcObject = this.localAVStream;
element.muted = true;
return element.play();
});
element.srcObject = this.localAVStream;
element.muted = true;
try {
await element.play();
} catch (e) {
logger.info("Failed to play local video element", e);
}
}
}
@@ -428,10 +422,7 @@ export class MatrixCall extends EventEmitter {
this.remoteVideoElement = element;
if (this.remoteStream) {
this.queueMediaOperation(MediaQueueId.RemoteVideo, () => {
element.srcObject = this.remoteStream;
return element.play();
});
this.playRemoteVideo();
}
}
@@ -444,9 +435,7 @@ export class MatrixCall extends EventEmitter {
async setRemoteAudioElement(element: HTMLAudioElement) {
if (element === this.remoteAudioElement) return;
if (this.remoteVideoElement) this.remoteVideoElement.muted = true;
this.remoteAudioElement = element;
this.remoteAudioElement.muted = false;
if (this.remoteStream) this.playRemoteAudio();
}
@@ -481,6 +470,7 @@ export class MatrixCall extends EventEmitter {
this.direction = CallDirection.Inbound;
this.opponentVersion = this.msg.version;
this.opponentPartyId = this.msg.party_id || null;
this.opponentMember = event.sender;
if (event.getLocalAge()) {
setTimeout(() => {
@@ -663,6 +653,7 @@ export class MatrixCall extends EventEmitter {
setRemoteOnHold(onHold: boolean) {
if (this.isRemoteOnHold() === onHold) return;
this.remoteOnHold = onHold;
if (!onHold) this.unholdingRemote = true;
for (const tranceiver of this.peerConn.getTransceivers()) {
// We set 'inactive' rather than 'sendonly' because we're not planning on
@@ -670,6 +661,12 @@ export class MatrixCall extends EventEmitter {
tranceiver.direction = onHold ? 'inactive' : 'sendrecv';
}
this.updateMuteStatus();
if (!onHold) {
this.playRemoteAudio();
}
this.emit(CallEvent.RemoteHoldUnhold, this.remoteOnHold);
}
/**
@@ -682,6 +679,7 @@ export class MatrixCall extends EventEmitter {
*/
isLocalOnHold(): boolean {
if (this.state !== CallState.Connected) return false;
if (this.unholdingRemote) return false;
let callOnHold = true;
@@ -706,6 +704,16 @@ export class MatrixCall extends EventEmitter {
const vidShouldBeMuted = this.vidMuted || this.remoteOnHold;
setTracksEnabled(this.localAVStream.getVideoTracks(), !vidShouldBeMuted);
if (this.remoteOnHold) {
if (this.remoteAudioElement && this.remoteAudioElement.srcObject === this.remoteStream) {
this.remoteAudioElement.muted = true;
} else if (this.remoteVideoElement && this.remoteVideoElement.srcObject === this.remoteStream) {
this.remoteVideoElement.muted = true;
}
} else {
this.playRemoteAudio();
}
}
/**
@@ -728,19 +736,21 @@ export class MatrixCall extends EventEmitter {
const videoEl = this.getLocalVideoElement();
if (videoEl && this.type === CallType.Video) {
this.queueMediaOperation(MediaQueueId.LocalVideo, () => {
videoEl.autoplay = true;
if (this.screenSharingStream) {
logger.debug(
"Setting screen sharing stream to the local video element",
);
videoEl.srcObject = this.screenSharingStream;
} else {
videoEl.srcObject = stream;
}
videoEl.muted = true;
return videoEl.play();
});
videoEl.autoplay = true;
if (this.screenSharingStream) {
logger.debug(
"Setting screen sharing stream to the local video element",
);
videoEl.srcObject = this.screenSharingStream;
} else {
videoEl.srcObject = stream;
}
videoEl.muted = true;
try {
await videoEl.play();
} catch (e) {
logger.info("Failed to play local video element", e);
}
}
this.localAVStream = stream;
@@ -805,13 +815,15 @@ export class MatrixCall extends EventEmitter {
const localVidEl = this.getLocalVideoElement();
if (localVidEl && this.type === CallType.Video) {
this.queueMediaOperation(MediaQueueId.LocalVideo, () => {
localVidEl.autoplay = true;
localVidEl.srcObject = stream;
localVidEl.autoplay = true;
localVidEl.srcObject = stream;
localVidEl.muted = true;
return localVidEl.play();
});
localVidEl.muted = true;
try {
await localVidEl.play();
} catch (e) {
logger.info("Failed to play local video element", e);
}
}
this.localAVStream = stream;
@@ -948,6 +960,7 @@ export class MatrixCall extends EventEmitter {
this.opponentVersion = event.getContent().version;
this.opponentPartyId = event.getContent().party_id || null;
this.opponentMember = event.sender;
this.setState(CallState.Connecting);
@@ -1009,7 +1022,7 @@ export class MatrixCall extends EventEmitter {
// Here we follow the perfect negotiation logic from
// https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Perfect_negotiation
const offerCollision = (
(description.type == 'offer') &&
(description.type === 'offer') &&
(this.makingOffer || this.peerConn.signalingState != 'stable')
);
@@ -1019,7 +1032,13 @@ export class MatrixCall extends EventEmitter {
return;
}
const prevOnHold = this.isLocalOnHold();
const prevLocalOnHold = this.isLocalOnHold();
if (description.type === 'answer') {
// whenever we get an answer back, clear the flag we set whilst trying to un-hold
// the other party: the state of the channels now reflects reality
this.unholdingRemote = false;
}
try {
await this.peerConn.setRemoteDescription(description);
@@ -1036,9 +1055,11 @@ export class MatrixCall extends EventEmitter {
logger.warn("Failed to complete negotiation", err);
}
const nowOnHold = this.isLocalOnHold();
if (prevOnHold !== nowOnHold) {
this.emit(CallEvent.HoldUnhold, nowOnHold);
const newLocalOnHold = this.isLocalOnHold();
if (prevLocalOnHold !== newLocalOnHold) {
this.emit(CallEvent.LocalHoldUnhold, newLocalOnHold);
// also this one for backwards compat
this.emit(CallEvent.HoldUnhold, newLocalOnHold);
}
}
@@ -1201,14 +1222,7 @@ export class MatrixCall extends EventEmitter {
if (ev.track.kind === 'video') {
if (this.remoteVideoElement) {
this.queueMediaOperation(MediaQueueId.RemoteVideo, async () => {
this.remoteVideoElement.srcObject = this.remoteStream;
try {
await this.remoteVideoElement.play();
} catch (e) {
logger.error("Failed to play remote video element", e);
}
});
this.playRemoteVideo();
}
} else {
if (this.remoteAudioElement) this.playRemoteAudio();
@@ -1235,30 +1249,50 @@ export class MatrixCall extends EventEmitter {
}
};
playRemoteAudio() {
this.queueMediaOperation(MediaQueueId.RemoteAudio, async () => {
this.remoteAudioElement.srcObject = this.remoteStream;
async playRemoteAudio() {
if (this.remoteVideoElement) this.remoteVideoElement.muted = true;
this.remoteAudioElement.muted = false;
// if audioOutput is non-default:
try {
if (audioOutput) {
// This seems quite unreliable in Chrome, although I haven't yet managed to make a jsfiddle where
// it fails.
// It seems reliable if you set the sink ID after setting the srcObject and then set the sink ID
// back to the default after the call is over
logger.info("Setting audio sink to " + audioOutput + ", was " + this.remoteAudioElement.sinkId);
await this.remoteAudioElement.setSinkId(audioOutput);
}
} catch (e) {
logger.warn("Couldn't set requested audio output device: using default", e);
}
this.remoteAudioElement.srcObject = this.remoteStream;
try {
await this.remoteAudioElement.play();
} catch (e) {
logger.error("Failed to play remote video element", e);
// if audioOutput is non-default:
try {
if (audioOutput) {
// This seems quite unreliable in Chrome, although I haven't yet managed to make a jsfiddle where
// it fails.
// It seems reliable if you set the sink ID after setting the srcObject and then set the sink ID
// back to the default after the call is over
logger.info("Setting audio sink to " + audioOutput + ", was " + this.remoteAudioElement.sinkId);
await this.remoteAudioElement.setSinkId(audioOutput);
}
});
} catch (e) {
logger.warn("Couldn't set requested audio output device: using default", e);
}
try {
await this.remoteAudioElement.play();
} catch (e) {
logger.error("Failed to play remote audio element", e);
}
}
private async playRemoteVideo() {
// A note on calling methods on media elements:
// We used to have queues per media element to serialise all calls on those elements.
// The reason given for this was that load() and play() were racing. However, we now
// never call load() explicitly so this seems unnecessary. However, serialising every
// operation was causing bugs where video would not resume because some play command
// had got stuck and all media operations were queued up behind it. If necessary, we
// should serialise the ones that need to be serialised but then be able to interrupt
// them with another load() which will cancel the pending one, but since we don't call
// load() explicitly, it shouldn't be a problem.
this.remoteVideoElement.srcObject = this.remoteStream;
logger.info("playing remote video. stream active? " + this.remoteStream.active);
try {
await this.remoteVideoElement.play();
} catch (e) {
logger.info("Failed to play remote video element", e);
}
}
onHangupReceived = (msg) => {
@@ -1266,13 +1300,12 @@ export class MatrixCall extends EventEmitter {
// 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) {
if (this.partyIdMatches(msg) || this.opponentPartyId === undefined || this.state === CallState.Ringing) {
// default reason is user_hangup
this.terminate(CallParty.Remote, msg.reason || CallErrorCode.UserHangup, true);
} else {
logger.info(`Ignoring message from party ID ${msg.party_id}: our partner is ${this.opponentPartyId}`);
return;
}
// default reason is user_hangup
this.terminate(CallParty.Remote, msg.reason || CallErrorCode.UserHangup, true);
};
onRejectReceived = (msg) => {
@@ -1337,7 +1370,7 @@ export class MatrixCall extends EventEmitter {
}
}
private terminate(hangupParty: CallParty, hangupReason: CallErrorCode, shouldEmit: boolean) {
private async terminate(hangupParty: CallParty, hangupReason: CallErrorCode, shouldEmit: boolean) {
if (this.callHasEnded()) return;
if (this.inviteTimeout) {
@@ -1350,29 +1383,23 @@ export class MatrixCall extends EventEmitter {
const localVid = this.getLocalVideoElement();
if (remoteVid) {
this.queueMediaOperation(MediaQueueId.RemoteVideo, () => {
remoteVid.pause();
remoteVid.srcObject = null;
});
remoteVid.pause();
remoteVid.srcObject = null;
}
if (remoteAud) {
this.queueMediaOperation(MediaQueueId.RemoteAudio, async () => {
remoteAud.pause();
remoteAud.srcObject = null;
try {
// As per comment in playRemoteAudio, setting the sink ID back to the default
// once the call is over makes setSinkId work reliably.
await this.remoteAudioElement.setSinkId('')
} catch (e) {
logger.warn("Failed to set sink ID back to default");
}
});
remoteAud.pause();
remoteAud.srcObject = null;
try {
// As per comment in playRemoteAudio, setting the sink ID back to the default
// once the call is over makes setSinkId work reliably.
await this.remoteAudioElement.setSinkId('')
} catch (e) {
logger.warn("Failed to set sink ID back to default");
}
}
if (localVid) {
this.queueMediaOperation(MediaQueueId.LocalVideo, () => {
localVid.pause();
localVid.srcObject = null;
});
localVid.pause();
localVid.srcObject = null;
}
this.hangupParty = hangupParty;
this.hangupReason = hangupReason;
@@ -1569,7 +1596,7 @@ export function setVideoInput(deviceId: string) { videoInput = deviceId; }
export function createNewMatrixCall(client: any, roomId: string, options?: CallOpts) {
// typeof prevents Node from erroring on an undefined reference
if (typeof(window) === 'undefined' || typeof(document) === 'undefined') {
logger.error("No window or document object: WebRTC is not supported in this environement");
logger.info("No window or document object: WebRTC is not supported in this environment");
return null;
}
@@ -1600,5 +1627,9 @@ export function createNewMatrixCall(client: any, roomId: string, options?: CallO
// call level options
forceTURN: client._forceTURN || optionsForceTURN,
};
return new MatrixCall(opts);
const call = new MatrixCall(opts);
client.reEmitter.reEmit(call, Object.values(CallEvent));
return call;
}
+2177 -2194
View File
File diff suppressed because it is too large Load Diff