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.
@@ -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.isRemote);
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
- return new Promise((resolve, reject) => {
327
- if (chromeMajorVersion && chromeMajorVersion < 67) {
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
- return;
332
- }
333
- peerConnection.getStats(track).then(response => {
334
- resolve(standardizeChromeOrSafariStats(response));
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 {boolean} isRemote
341
+ * @param {object} options
344
342
  * @returns {Promise.<Array<StandardizedTrackStatsReport>>}
345
343
  */
346
- function firefoxGetTrackStats(peerConnection, track, isRemote) {
347
- return new Promise((resolve, reject) => {
348
- peerConnection.getStats(track).then(response => {
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 && track.remoteSource;
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 {boolean} isRemote
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:
@@ -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
@@ -2,7 +2,7 @@
2
2
  "name": "twilio-video",
3
3
  "title": "Twilio Video",
4
4
  "description": "Twilio Video JavaScript Library",
5
- "version": "2.26.0",
5
+ "version": "2.26.2",
6
6
  "homepage": "https://twilio.com",
7
7
  "author": "Mark Andrus Roberts <mroberts@twilio.com>",
8
8
  "contributors": [
@@ -1,16 +1,5 @@
1
- import { Track } from './Track';
1
+ import { MediaTrack } from './MediaTrack';
2
2
 
3
- export class AudioTrack extends Track {
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
+ }
@@ -1,4 +1,4 @@
1
- import { Track } from './Track';
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 Track {
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?: CreateLocalTracksOptions|CreateLocalAudioTrackOptions): Promise<LocalAudioTrack>;
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