twilio-video 2.26.0 → 2.26.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/COMMON_ISSUES.md +3 -3
- package/README.md +1 -1
- package/dist/twilio-video.js +104 -30
- package/dist/twilio-video.min.js +21 -21
- package/es5/connect.js +6 -1
- package/es5/connect.js.map +1 -1
- package/es5/twilioconnection.js +3 -0
- package/es5/twilioconnection.js.map +1 -1
- package/es5/webrtc/getstats.js +32 -27
- package/es5/webrtc/getstats.js.map +1 -1
- package/es5/webrtc/util/index.js +61 -0
- package/es5/webrtc/util/index.js.map +1 -1
- package/lib/connect.js +12 -1
- package/lib/twilioconnection.js +4 -0
- package/lib/webrtc/getstats.js +29 -26
- package/lib/webrtc/util/index.js +63 -0
- package/package.json +1 -1
- package/tsdef/AudioTrack.d.ts +2 -13
- package/tsdef/MediaTrack.d.ts +16 -0
- package/tsdef/VideoTrack.d.ts +2 -12
- package/tsdef/index.d.ts +4 -3
- package/tsdef/twilio-video-tests.ts +3 -0
package/lib/webrtc/getstats.js
CHANGED
|
@@ -53,9 +53,7 @@ function _getStats(peerConnection, options) {
|
|
|
53
53
|
[remoteVideoTracks, 'remoteVideoTrackStats', true]
|
|
54
54
|
], ([tracks, statsArrayName, isRemote]) => {
|
|
55
55
|
return tracks.map(track => {
|
|
56
|
-
return getTrackStats(peerConnection, track, Object.assign({
|
|
57
|
-
isRemote: isRemote
|
|
58
|
-
}, options)).then(trackStatsArray => {
|
|
56
|
+
return getTrackStats(peerConnection, track, Object.assign({ isRemote }, options)).then(trackStatsArray => {
|
|
59
57
|
trackStatsArray.forEach(trackStats => {
|
|
60
58
|
trackStats.trackId = track.id;
|
|
61
59
|
statsResponse[statsArrayName].push(trackStats);
|
|
@@ -296,14 +294,14 @@ function getTracks(peerConnection, kind, localOrRemote) {
|
|
|
296
294
|
*/
|
|
297
295
|
function getTrackStats(peerConnection, track, options = {}) {
|
|
298
296
|
if (typeof options.testForChrome !== 'undefined' || isChrome) {
|
|
299
|
-
return chromeOrSafariGetTrackStats(peerConnection, track);
|
|
297
|
+
return chromeOrSafariGetTrackStats(peerConnection, track, options);
|
|
300
298
|
}
|
|
301
299
|
if (typeof options.testForFirefox !== 'undefined' || isFirefox) {
|
|
302
|
-
return firefoxGetTrackStats(peerConnection, track, options
|
|
300
|
+
return firefoxGetTrackStats(peerConnection, track, options);
|
|
303
301
|
}
|
|
304
302
|
if (typeof options.testForSafari !== 'undefined' || isSafari) {
|
|
305
303
|
if (typeof options.testForSafari !== 'undefined' || getSdpFormat() === 'unified') {
|
|
306
|
-
return chromeOrSafariGetTrackStats(peerConnection, track);
|
|
304
|
+
return chromeOrSafariGetTrackStats(peerConnection, track, options);
|
|
307
305
|
}
|
|
308
306
|
// NOTE(syerrapragada): getStats() is not supported on
|
|
309
307
|
// Safari versions where plan-b is the SDP format
|
|
@@ -320,19 +318,19 @@ function getTrackStats(peerConnection, track, options = {}) {
|
|
|
320
318
|
* Get the standardized statistics for a particular MediaStreamTrack in Chrome or Safari.
|
|
321
319
|
* @param {RTCPeerConnection} peerConnection
|
|
322
320
|
* @param {MediaStreamTrack} track
|
|
321
|
+
* @param {object} options - Used for testing
|
|
323
322
|
* @returns {Promise.<Array<StandardizedTrackStatsReport>>}
|
|
324
323
|
*/
|
|
325
|
-
function chromeOrSafariGetTrackStats(peerConnection, track) {
|
|
326
|
-
|
|
327
|
-
|
|
324
|
+
function chromeOrSafariGetTrackStats(peerConnection, track, options) {
|
|
325
|
+
if (chromeMajorVersion && chromeMajorVersion < 67) {
|
|
326
|
+
return new Promise((resolve, reject) => {
|
|
328
327
|
peerConnection.getStats(response => {
|
|
329
328
|
resolve([standardizeChromeLegacyStats(response, track)]);
|
|
330
329
|
}, null, reject);
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}, reject);
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
return peerConnection.getStats(track).then(response => {
|
|
333
|
+
return standardizeChromeOrSafariStats(response, options);
|
|
336
334
|
});
|
|
337
335
|
}
|
|
338
336
|
|
|
@@ -340,14 +338,12 @@ function chromeOrSafariGetTrackStats(peerConnection, track) {
|
|
|
340
338
|
* Get the standardized statistics for a particular MediaStreamTrack in Firefox.
|
|
341
339
|
* @param {RTCPeerConnection} peerConnection
|
|
342
340
|
* @param {MediaStreamTrack} track
|
|
343
|
-
* @param {
|
|
341
|
+
* @param {object} options
|
|
344
342
|
* @returns {Promise.<Array<StandardizedTrackStatsReport>>}
|
|
345
343
|
*/
|
|
346
|
-
function firefoxGetTrackStats(peerConnection, track,
|
|
347
|
-
return
|
|
348
|
-
|
|
349
|
-
resolve([standardizeFirefoxStats(response, isRemote)]);
|
|
350
|
-
}, reject);
|
|
344
|
+
function firefoxGetTrackStats(peerConnection, track, options) {
|
|
345
|
+
return peerConnection.getStats(track).then(response => {
|
|
346
|
+
return [standardizeFirefoxStats(response, options)];
|
|
351
347
|
});
|
|
352
348
|
}
|
|
353
349
|
|
|
@@ -428,9 +424,13 @@ function standardizeChromeLegacyStats(response, track) {
|
|
|
428
424
|
/**
|
|
429
425
|
* Standardize the MediaStreamTrack's statistics in Chrome or Safari.
|
|
430
426
|
* @param {RTCStatsResponse} response
|
|
427
|
+
* @param {object} options - Used for testing
|
|
431
428
|
* @returns {Array<StandardizedTrackStatsReport>}
|
|
432
429
|
*/
|
|
433
|
-
function standardizeChromeOrSafariStats(response) {
|
|
430
|
+
function standardizeChromeOrSafariStats(response, { simulateExceptionWhileStandardizingStats = false }) {
|
|
431
|
+
if (simulateExceptionWhileStandardizingStats) {
|
|
432
|
+
throw new Error('Error while gathering stats');
|
|
433
|
+
}
|
|
434
434
|
let inbound = null;
|
|
435
435
|
|
|
436
436
|
// NOTE(mpatwardhan): We should expect more than one "outbound-rtp" stats for a
|
|
@@ -470,7 +470,7 @@ function standardizeChromeOrSafariStats(response) {
|
|
|
470
470
|
}
|
|
471
471
|
});
|
|
472
472
|
|
|
473
|
-
const isRemote = track
|
|
473
|
+
const isRemote = track ? track.remoteSource : !localMedia;
|
|
474
474
|
const mainSources = isRemote ? [inbound] : outbound;
|
|
475
475
|
const stats = [];
|
|
476
476
|
const remoteSource = isRemote ? remoteOutbound : remoteInbound; // remote rtp stats
|
|
@@ -523,7 +523,7 @@ function standardizeChromeOrSafariStats(response) {
|
|
|
523
523
|
standardizedStats.frameWidthReceived = frameWidth;
|
|
524
524
|
} else {
|
|
525
525
|
standardizedStats.frameWidthSent = frameWidth;
|
|
526
|
-
standardizedStats.frameWidthInput = track.frameWidth;
|
|
526
|
+
standardizedStats.frameWidthInput = track ? track.frameWidth : localMedia.width;
|
|
527
527
|
}
|
|
528
528
|
}
|
|
529
529
|
|
|
@@ -533,7 +533,7 @@ function standardizeChromeOrSafariStats(response) {
|
|
|
533
533
|
standardizedStats.frameHeightReceived = frameHeight;
|
|
534
534
|
} else {
|
|
535
535
|
standardizedStats.frameHeightSent = frameHeight;
|
|
536
|
-
standardizedStats.frameHeightInput = track.frameHeight;
|
|
536
|
+
standardizedStats.frameHeightInput = track ? track.frameHeight : localMedia.height;
|
|
537
537
|
}
|
|
538
538
|
}
|
|
539
539
|
|
|
@@ -626,10 +626,13 @@ function standardizeChromeOrSafariStats(response) {
|
|
|
626
626
|
/**
|
|
627
627
|
* Standardize the MediaStreamTrack's statistics in Firefox.
|
|
628
628
|
* @param {RTCStatsReport} response
|
|
629
|
-
* @param {
|
|
629
|
+
* @param {object} options - Used for testing
|
|
630
630
|
* @returns {StandardizedTrackStatsReport}
|
|
631
631
|
*/
|
|
632
|
-
function standardizeFirefoxStats(response = new Map(), isRemote) {
|
|
632
|
+
function standardizeFirefoxStats(response = new Map(), { isRemote, simulateExceptionWhileStandardizingStats = false }) {
|
|
633
|
+
if (simulateExceptionWhileStandardizingStats) {
|
|
634
|
+
throw new Error('Error while gathering stats');
|
|
635
|
+
}
|
|
633
636
|
// NOTE(mroberts): If getStats is called on a closed RTCPeerConnection,
|
|
634
637
|
// Firefox returns undefined instead of an RTCStatsReport. We workaround this
|
|
635
638
|
// here. See the following bug for more details:
|
package/lib/webrtc/util/index.js
CHANGED
|
@@ -307,6 +307,67 @@ function support() {
|
|
|
307
307
|
&& typeof RTCPeerConnection === 'function';
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
+
/**
|
|
311
|
+
* Create a Set of supported codecs for a certain kind of media.
|
|
312
|
+
* @param {'audio'|'video'} kind
|
|
313
|
+
* @returns {Promise<Set<AudioCodec|VideoCodec>>}
|
|
314
|
+
*/
|
|
315
|
+
function createSupportedCodecsSet(kind) {
|
|
316
|
+
if (typeof RTCRtpSender !== 'undefined'
|
|
317
|
+
&& typeof RTCRtpSender.getCapabilities === 'function') {
|
|
318
|
+
return Promise.resolve(new Set(
|
|
319
|
+
RTCRtpSender
|
|
320
|
+
.getCapabilities(kind)
|
|
321
|
+
.codecs
|
|
322
|
+
.map(({ mimeType }) => mimeType.split('/')[1].toLowerCase())
|
|
323
|
+
));
|
|
324
|
+
}
|
|
325
|
+
if (typeof RTCPeerConnection === 'undefined'
|
|
326
|
+
|| typeof RTCPeerConnection.prototype === 'undefined'
|
|
327
|
+
|| typeof RTCPeerConnection.prototype.addTransceiver !== 'function'
|
|
328
|
+
|| typeof RTCPeerConnection.prototype.close !== 'function'
|
|
329
|
+
|| typeof RTCPeerConnection.prototype.createOffer !== 'function') {
|
|
330
|
+
return Promise.resolve(new Set());
|
|
331
|
+
}
|
|
332
|
+
const pc = new RTCPeerConnection();
|
|
333
|
+
pc.addTransceiver(kind);
|
|
334
|
+
return pc.createOffer().then(({ sdp }) => {
|
|
335
|
+
pc.close();
|
|
336
|
+
return new Set((sdp.match(/^a=rtpmap:.+$/gm) || [])
|
|
337
|
+
.map(line => line.match(/^a=rtpmap:.+ ([^/]+)/)[1].toLowerCase()));
|
|
338
|
+
}, () => {
|
|
339
|
+
pc.close();
|
|
340
|
+
return new Set();
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// NOTE(mmalavalli): Cache the supported audio and video codecs here.
|
|
345
|
+
const supportedCodecs = new Map();
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Check whether a given codec for a certain kind of media is supported.
|
|
349
|
+
* @param {AudioCodec|VideoCodec} codec
|
|
350
|
+
* @param {'audio'|'video'} kind
|
|
351
|
+
* @returns {Promise<boolean>}
|
|
352
|
+
*/
|
|
353
|
+
function isCodecSupported(codec, kind) {
|
|
354
|
+
const codecs = supportedCodecs.get(kind);
|
|
355
|
+
if (codecs) {
|
|
356
|
+
return Promise.resolve(codecs.has(codec.toLowerCase()));
|
|
357
|
+
}
|
|
358
|
+
return createSupportedCodecsSet(kind).then(codecs => {
|
|
359
|
+
supportedCodecs.set(kind, codecs);
|
|
360
|
+
return codecs.has(codec.toLowerCase());
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Clear cached supported codecs (unit tests only).
|
|
366
|
+
*/
|
|
367
|
+
function clearCachedSupportedCodecs() {
|
|
368
|
+
supportedCodecs.clear();
|
|
369
|
+
}
|
|
370
|
+
|
|
310
371
|
/**
|
|
311
372
|
* @typedef {object} Deferred
|
|
312
373
|
* @property {Promise} promise
|
|
@@ -314,12 +375,14 @@ function support() {
|
|
|
314
375
|
* @property {function} resolve
|
|
315
376
|
*/
|
|
316
377
|
|
|
378
|
+
exports.clearCachedSupportedCodecs = clearCachedSupportedCodecs;
|
|
317
379
|
exports.defer = defer;
|
|
318
380
|
exports.delegateMethods = delegateMethods;
|
|
319
381
|
exports.difference = difference;
|
|
320
382
|
exports.flatMap = flatMap;
|
|
321
383
|
exports.guessBrowser = guessBrowser;
|
|
322
384
|
exports.guessBrowserVersion = guessBrowserVersion;
|
|
385
|
+
exports.isCodecSupported = isCodecSupported;
|
|
323
386
|
exports.isIOSChrome = isIOSChrome;
|
|
324
387
|
exports.interceptEvent = interceptEvent;
|
|
325
388
|
exports.legacyPromise = legacyPromise;
|
package/package.json
CHANGED
package/tsdef/AudioTrack.d.ts
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MediaTrack } from './MediaTrack';
|
|
2
2
|
|
|
3
|
-
export class AudioTrack extends
|
|
4
|
-
isStarted: boolean;
|
|
5
|
-
isEnabled: boolean;
|
|
3
|
+
export class AudioTrack extends MediaTrack {
|
|
6
4
|
kind: 'audio';
|
|
7
|
-
mediaStreamTrack: MediaStreamTrack;
|
|
8
|
-
|
|
9
|
-
attach(element?: HTMLMediaElement | string): HTMLMediaElement;
|
|
10
|
-
detach(element?: HTMLMediaElement | string): HTMLMediaElement[];
|
|
11
|
-
|
|
12
|
-
on(event: 'disabled', listener: (track: this) => void): this;
|
|
13
|
-
on(event: 'enabled', listener: (track: this) => void): this;
|
|
14
|
-
on(event: 'started', listener: (track: this) => void): this;
|
|
15
|
-
on(event: string, listener: (...args: any[]) => void): this;
|
|
16
5
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Track } from './Track';
|
|
2
|
+
|
|
3
|
+
export class MediaTrack extends Track {
|
|
4
|
+
isStarted: boolean;
|
|
5
|
+
isEnabled: boolean;
|
|
6
|
+
mediaStreamTrack: MediaStreamTrack;
|
|
7
|
+
|
|
8
|
+
attach(element?: HTMLMediaElement | string): HTMLMediaElement;
|
|
9
|
+
detach(element: HTMLMediaElement | string): HTMLMediaElement;
|
|
10
|
+
detach(): HTMLMediaElement[];
|
|
11
|
+
|
|
12
|
+
on(event: 'disabled', listener: (track: this) => void): this;
|
|
13
|
+
on(event: 'enabled', listener: (track: this) => void): this;
|
|
14
|
+
on(event: 'started', listener: (track: this) => void): this;
|
|
15
|
+
on(event: string, listener: (...args: any[]) => void): this;
|
|
16
|
+
}
|
package/tsdef/VideoTrack.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MediaTrack } from './MediaTrack';
|
|
2
2
|
import { VideoProcessor } from './VideoProcessor';
|
|
3
3
|
|
|
4
4
|
export namespace VideoTrack {
|
|
@@ -8,22 +8,12 @@ export namespace VideoTrack {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export class VideoTrack extends
|
|
12
|
-
isStarted: boolean;
|
|
13
|
-
isEnabled: boolean;
|
|
11
|
+
export class VideoTrack extends MediaTrack {
|
|
14
12
|
dimensions: VideoTrack.Dimensions;
|
|
15
13
|
kind: 'video';
|
|
16
14
|
processor: VideoProcessor | null;
|
|
17
|
-
mediaStreamTrack: MediaStreamTrack;
|
|
18
15
|
processedTrack: MediaStreamTrack | null;
|
|
19
16
|
|
|
20
17
|
addProcessor(processor: VideoProcessor): this;
|
|
21
18
|
removeProcessor(processor: VideoProcessor): this;
|
|
22
|
-
attach(element?: HTMLMediaElement | string): HTMLVideoElement;
|
|
23
|
-
detach(element?: HTMLMediaElement | string): HTMLVideoElement[];
|
|
24
|
-
|
|
25
|
-
on(event: 'disabled', listener: (track: this) => void): this;
|
|
26
|
-
on(event: 'enabled', listener: (track: this) => void): this;
|
|
27
|
-
on(event: 'started', listener: (track: this) => void): this;
|
|
28
|
-
on(event: string, listener: (...args: any[]) => void): this;
|
|
29
19
|
}
|
package/tsdef/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export const isSupported: boolean;
|
|
|
12
12
|
export const version:string;
|
|
13
13
|
export const Logger: Log.RootLogger;
|
|
14
14
|
export function connect(token: string, options?: ConnectOptions): CancelablePromise<Room>;
|
|
15
|
-
export function createLocalAudioTrack(options?:
|
|
15
|
+
export function createLocalAudioTrack(options?: CreateLocalTrackOptions|CreateLocalAudioTrackOptions): Promise<LocalAudioTrack>;
|
|
16
16
|
export function createLocalTracks(options?: CreateLocalTracksOptions): Promise<LocalTrack[]>;
|
|
17
17
|
export function createLocalVideoTrack(options?: CreateLocalTrackOptions): Promise<LocalVideoTrack>;
|
|
18
18
|
export function runPreflight(token: string, options?: PreflightOptions): PreflightTest;
|
|
@@ -43,6 +43,9 @@ export { Room } from './Room';
|
|
|
43
43
|
export { Track } from './Track';
|
|
44
44
|
export { TrackPublication } from './TrackPublication';
|
|
45
45
|
export { TwilioError } from './TwilioError';
|
|
46
|
+
export { VideoProcessor } from './VideoProcessor';
|
|
47
|
+
export { VideoTrack } from './VideoTrack';
|
|
48
|
+
|
|
46
49
|
export {
|
|
47
50
|
AudioCodec,
|
|
48
51
|
AudioCodecSettings,
|
|
@@ -93,5 +96,3 @@ export {
|
|
|
93
96
|
VideoTrackPublication,
|
|
94
97
|
VP8CodecSettings
|
|
95
98
|
} from './types';
|
|
96
|
-
export { VideoProcessor } from './VideoProcessor';
|
|
97
|
-
export { VideoTrack } from './VideoTrack';
|
|
@@ -346,6 +346,9 @@ function trackSubscribed(track: Video.VideoTrack | Video.AudioTrack) {
|
|
|
346
346
|
}
|
|
347
347
|
|
|
348
348
|
function trackUnsubscribed(track: Video.VideoTrack | Video.AudioTrack) {
|
|
349
|
+
const element = document.createElement(track.kind);
|
|
350
|
+
track.detach('#foo').remove();
|
|
351
|
+
track.detach(element).remove();
|
|
349
352
|
track.detach().forEach(element => element.remove());
|
|
350
353
|
}
|
|
351
354
|
|