twilio-video 2.26.1 → 2.27.0
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 +54 -1
- package/COMMON_ISSUES.md +3 -3
- package/README.md +1 -1
- package/dist/twilio-video.js +197 -55
- package/dist/twilio-video.min.js +21 -21
- package/es5/connect.js +6 -1
- package/es5/connect.js.map +1 -1
- package/es5/media/track/localmediatrack.js +4 -2
- package/es5/media/track/localmediatrack.js.map +1 -1
- package/es5/media/track/localvideotrack.js +4 -3
- package/es5/media/track/localvideotrack.js.map +1 -1
- package/es5/media/track/remotevideotrack.js +1 -1
- package/es5/media/track/videoprocessoreventobserver.js +3 -3
- package/es5/media/track/videoprocessoreventobserver.js.map +1 -1
- package/es5/media/track/videotrack.js +88 -22
- package/es5/media/track/videotrack.js.map +1 -1
- package/es5/webrtc/getstats.js +28 -21
- 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/media/track/localmediatrack.js +4 -2
- package/lib/media/track/localvideotrack.js +4 -3
- package/lib/media/track/remotevideotrack.js +1 -1
- package/lib/media/track/videoprocessoreventobserver.js +11 -3
- package/lib/media/track/videotrack.js +92 -22
- package/lib/webrtc/getstats.js +25 -20
- package/lib/webrtc/util/index.js +63 -0
- package/package.json +1 -1
- package/tsdef/VideoProcessor.d.ts +4 -1
- package/tsdef/VideoTrack.d.ts +2 -1
- package/tsdef/index.d.ts +1 -0
- package/tsdef/types.d.ts +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,13 +2,66 @@ The Twilio Programmable Video SDKs use [Semantic Versioning](http://www.semver.o
|
|
|
2
2
|
|
|
3
3
|
**Version 1.x reached End of Life on September 8th, 2021.** See the changelog entry [here](https://www.twilio.com/changelog/end-of-life-complete-for-unsupported-versions-of-the-programmable-video-sdk). Support for the 1.x version ended on December 4th, 2020.
|
|
4
4
|
|
|
5
|
+
2.27.0 (March 21, 2023)
|
|
6
|
+
=======================
|
|
7
|
+
|
|
8
|
+
Changes
|
|
9
|
+
-------
|
|
10
|
+
|
|
11
|
+
`VideoTrack.addProcessor` now works on browsers that support `OffscreenCanvas` as well as `HTMLCanvasElement`. When used with
|
|
12
|
+
[@twilio/video-processors v2.0.0](https://github.com/twilio/twilio-video-processors.js/blob/2.0.0/CHANGELOG.md), the Virtual
|
|
13
|
+
Background feature will work on browsers that supports [WebGL2](https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext).
|
|
14
|
+
See [VideoTrack.addProcessor](https://sdk.twilio.com/js/video/releases/2.27.0/docs/VideoTrack.html#addProcessor__anchor) and
|
|
15
|
+
[@twilio/video-processors v2.0.0](https://github.com/twilio/twilio-video-processors.js/blob/2.0.0/CHANGELOG.md) for details.
|
|
16
|
+
|
|
17
|
+
### Example
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { createLocalVideoTrack } from 'twilio-video';
|
|
21
|
+
import { Pipeline, VirtualBackgroundProcessor } from '@twilio/video-processors';
|
|
22
|
+
|
|
23
|
+
const virtualBackgroundProcessor = new VirtualBackgroundProcessor({
|
|
24
|
+
pipeline: Pipeline.WebGL2,
|
|
25
|
+
// ...otherOptions
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
await virtualBackgroundProcessor.loadModel();
|
|
29
|
+
|
|
30
|
+
const videoTrack = await createLocalVideoTrack({
|
|
31
|
+
width: 640,
|
|
32
|
+
height: 480,
|
|
33
|
+
frameRate: 24
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
videoTrack.addProcessor(processor, {
|
|
37
|
+
inputFrameBufferType: 'video',
|
|
38
|
+
outputFrameBufferContextType: 'webgl2',
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
2.26.2 (February 21, 2023)
|
|
43
|
+
==========================
|
|
44
|
+
|
|
45
|
+
Changes
|
|
46
|
+
-------
|
|
47
|
+
|
|
48
|
+
- Starting from version 110, Chrome will no longer support the iSAC audio codec. The SDK will now log a warning to the console
|
|
49
|
+
whenever an audio or a video codec that is specified in `ConnectOptions.preferredAudioCodecs` and `ConnectOptions.preferredVideoCodecs`
|
|
50
|
+
is not supported by the browser. (VIDEO-12494)
|
|
51
|
+
|
|
52
|
+
Bug Fixes
|
|
53
|
+
---------
|
|
54
|
+
|
|
55
|
+
- Fixed a bug on Chrome versions 112+ where `Room.getStats()` did not reject the returned Promise when an exception was
|
|
56
|
+
raised while accessing WebRTC stats that due to a TypeError caused by trying to read from the now-removed `RTCMediaStreamTrackStats`. (VIDEO-12534)
|
|
57
|
+
|
|
5
58
|
2.26.1 (January 31, 2023)
|
|
6
59
|
=========================
|
|
7
60
|
|
|
8
61
|
Bug Fixes
|
|
9
62
|
---------
|
|
10
63
|
|
|
11
|
-
- Fixed a bug that manifests on Chrome versions 112+ where `Room.getStats()`
|
|
64
|
+
- Fixed a bug that manifests on Chrome versions 112+ where `Room.getStats()` raises an unhandled exception due to a
|
|
12
65
|
TypeError caused by trying to read from the now-removed `RTCMediaStreamTrackStats`. Instead, the SDK now reads from
|
|
13
66
|
the `RTCMediaSourceStats`. (VIDEO-12411)
|
|
14
67
|
- Fixed an error in the type definition for the `attach()` method of `AudioTrack` and `VideoTrack`. (VIDEO-12242)
|
package/COMMON_ISSUES.md
CHANGED
|
@@ -200,10 +200,10 @@ transform: scaleX(-1)
|
|
|
200
200
|
</p>
|
|
201
201
|
</details>
|
|
202
202
|
<details>
|
|
203
|
-
<summary>iOS
|
|
203
|
+
<summary>iOS 16: Local Media Tracks are lost in a video call after an interruption with a phone call</summary>
|
|
204
204
|
<p>
|
|
205
205
|
|
|
206
|
-
Due to a
|
|
206
|
+
Due to a bug on Safari on iOS 16, an incoming call causes local and sometimes remote media playback to stop after the **second** incoming call. You can find more details in this [WebKit bug](https://bugs.webkit.org/show_bug.cgi?id=240651). As a workaround, you can call the [Room.refreshInactiveMedia](https://sdk.twilio.com/js/video/releases/2.26.0/docs/Room.html#refreshInactiveMedia__anchor) method in your application in order to restart the muted local media tracks and resume playback of the paused remote media tracks.
|
|
207
207
|
</p>
|
|
208
208
|
</details>
|
|
209
209
|
<details>
|
|
@@ -348,7 +348,7 @@ trouble with twilio-video.js, ensure these are not running.
|
|
|
348
348
|
<summary>setSinkId method is not implemented in all browsers</summary>
|
|
349
349
|
<p>
|
|
350
350
|
|
|
351
|
-
The `audioElement.setSinkId()` method, which is used to change the audio output device for a given HTML audio element, is only implemented in Desktop Chrome and Desktop Edge. Therefore, it is not possible for users to change their audio output device in other browsers. Users will have to use their operating system settings to change their audio output device instead.
|
|
351
|
+
The `audioElement.setSinkId()` method, which is used to change the audio output device for a given HTML audio element, is only implemented in Desktop Chrome and Desktop Edge. Therefore, it is not possible for users to change their audio output device in other browsers. Users will have to use their operating system settings to change their audio output device instead.
|
|
352
352
|
|
|
353
353
|
More information about this method (including browser compatibility) is available [here](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/setSinkId).
|
|
354
354
|
</p>
|
package/README.md
CHANGED
|
@@ -74,7 +74,7 @@ Releases of twilio-video.js are hosted on a CDN, and you can include these
|
|
|
74
74
|
directly in your web app using a <script> tag.
|
|
75
75
|
|
|
76
76
|
```html
|
|
77
|
-
<script src="//sdk.twilio.com/js/video/releases/2.
|
|
77
|
+
<script src="//sdk.twilio.com/js/video/releases/2.27.0/twilio-video.min.js"></script>
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
Using this method, twilio-video.js will set a browser global:
|
package/dist/twilio-video.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! twilio-video.js 2.
|
|
1
|
+
/*! twilio-video.js 2.27.0
|
|
2
2
|
|
|
3
3
|
The following license applies to all parts of this software except as
|
|
4
4
|
documented below.
|
|
@@ -86,7 +86,7 @@ module.exports = createCancelableRoomPromise;
|
|
|
86
86
|
},{"./util/cancelablepromise":124}],2:[function(require,module,exports){
|
|
87
87
|
'use strict';
|
|
88
88
|
var MediaStreamTrack = require('./webrtc').MediaStreamTrack;
|
|
89
|
-
var _a = require('./webrtc/util'), guessBrowser = _a.guessBrowser, guessBrowserVersion = _a.guessBrowserVersion;
|
|
89
|
+
var _a = require('./webrtc/util'), guessBrowser = _a.guessBrowser, guessBrowserVersion = _a.guessBrowserVersion, isCodecSupported = _a.isCodecSupported;
|
|
90
90
|
var createCancelableRoomPromise = require('./cancelableroompromise');
|
|
91
91
|
var EncodingParametersImpl = require('./encodingparameters');
|
|
92
92
|
var LocalParticipant = require('./localparticipant');
|
|
@@ -407,6 +407,11 @@ function connect(token, options) {
|
|
|
407
407
|
video: options.preferredVideoCodecs.map(normalizeCodecSettings)
|
|
408
408
|
};
|
|
409
409
|
var networkQualityConfiguration = new NetworkQualityConfigurationImpl(isNonArrayObject(options.networkQuality) ? options.networkQuality : {});
|
|
410
|
+
// Log warnings for any unsupported preferred codecs.
|
|
411
|
+
['audio', 'video'].forEach(function (kind) { return preferredCodecs[kind].forEach(function (_a) {
|
|
412
|
+
var codec = _a.codec;
|
|
413
|
+
return isCodecSupported(codec, kind).then(function (isSupported) { return !isSupported && log.warn("The preferred " + kind + " codec \"" + codec + "\" will be ignored as it is not supported by the browser."); });
|
|
414
|
+
}); });
|
|
410
415
|
// Create a CancelableRoomPromise<Room> that resolves after these steps:
|
|
411
416
|
// 1 - Get the LocalTracks.
|
|
412
417
|
// 2 - Create the LocalParticipant using options.tracks.
|
|
@@ -3868,8 +3873,10 @@ function restartWhenInadvertentlyStopped(localMediaTrack) {
|
|
|
3868
3873
|
}).catch(function (error) {
|
|
3869
3874
|
log.warn('Failed to detect silence:', error);
|
|
3870
3875
|
}).finally(function () {
|
|
3871
|
-
// Pause the dummy element again.
|
|
3872
|
-
|
|
3876
|
+
// Pause the dummy element again, if there is no processed track.
|
|
3877
|
+
if (!localMediaTrack.processedTrack) {
|
|
3878
|
+
el.pause();
|
|
3879
|
+
}
|
|
3873
3880
|
});
|
|
3874
3881
|
}
|
|
3875
3882
|
function shouldReacquireTrack() {
|
|
@@ -4227,8 +4234,8 @@ var LocalVideoTrack = /** @class */ (function (_super) {
|
|
|
4227
4234
|
};
|
|
4228
4235
|
/**
|
|
4229
4236
|
* Add a {@link VideoProcessor} to allow for custom processing of video frames belonging to a VideoTrack.
|
|
4230
|
-
* Only Chrome supports this as of now. Calling this API from a non-supported browser will result in a log warning.
|
|
4231
4237
|
* @param {VideoProcessor} processor - The {@link VideoProcessor} to use.
|
|
4238
|
+
* @param {AddProcessorOptions} [options] - {@link AddProcessorOptions} to provide.
|
|
4232
4239
|
* @returns {this}
|
|
4233
4240
|
* @example
|
|
4234
4241
|
* class GrayScaleProcessor {
|
|
@@ -4428,9 +4435,10 @@ function workaroundSilentLocalVideo(localVideoTrack, doc) {
|
|
|
4428
4435
|
}).catch(function (error) {
|
|
4429
4436
|
log.warn('Failed to detect silence and restart:', error);
|
|
4430
4437
|
}).finally(function () {
|
|
4431
|
-
// If silent frames were not detected, then pause the dummy element again
|
|
4438
|
+
// If silent frames were not detected, then pause the dummy element again,
|
|
4439
|
+
// if there is no processed track.
|
|
4432
4440
|
el = localVideoTrack._dummyEl;
|
|
4433
|
-
if (el && !el.paused) {
|
|
4441
|
+
if (el && !el.paused && !localVideoTrack.processedTrack) {
|
|
4434
4442
|
el.pause();
|
|
4435
4443
|
}
|
|
4436
4444
|
// Reset the unmute handler.
|
|
@@ -6346,8 +6354,8 @@ var RemoteVideoTrack = /** @class */ (function (_super) {
|
|
|
6346
6354
|
* Add a {@link VideoProcessor} to allow for custom processing of video frames belonging to a VideoTrack.
|
|
6347
6355
|
* When a Participant un-publishes and re-publishes a VideoTrack, a new RemoteVideoTrack is created and
|
|
6348
6356
|
* any VideoProcessors attached to the previous RemoteVideoTrack would have to be re-added again.
|
|
6349
|
-
* Only Chrome supports this as of now. Calling this API from a non-supported browser will result in a log warning.
|
|
6350
6357
|
* @param {VideoProcessor} processor - The {@link VideoProcessor} to use.
|
|
6358
|
+
* @param {AddProcessorOptions} [options] - {@link AddProcessorOptions} to provide.
|
|
6351
6359
|
* @returns {this}
|
|
6352
6360
|
* @example
|
|
6353
6361
|
* class GrayScaleProcessor {
|
|
@@ -7014,10 +7022,10 @@ var VideoProcessorEventObserver = /** @class */ (function (_super) {
|
|
|
7014
7022
|
if (!this._processorInfo) {
|
|
7015
7023
|
return {};
|
|
7016
7024
|
}
|
|
7017
|
-
var _a = this._processorInfo, processor = _a.processor, captureHeight = _a.captureHeight, captureWidth = _a.captureWidth, inputFrameRate = _a.inputFrameRate, isRemoteVideoTrack = _a.isRemoteVideoTrack;
|
|
7018
|
-
var data = { captureHeight: captureHeight, captureWidth: captureWidth, inputFrameRate: inputFrameRate, isRemoteVideoTrack: isRemoteVideoTrack };
|
|
7025
|
+
var _a = this._processorInfo, processor = _a.processor, captureHeight = _a.captureHeight, captureWidth = _a.captureWidth, inputFrameRate = _a.inputFrameRate, isRemoteVideoTrack = _a.isRemoteVideoTrack, inputFrameBufferType = _a.inputFrameBufferType, outputFrameBufferContextType = _a.outputFrameBufferContextType;
|
|
7026
|
+
var data = { captureHeight: captureHeight, captureWidth: captureWidth, inputFrameRate: inputFrameRate, isRemoteVideoTrack: isRemoteVideoTrack, inputFrameBufferType: inputFrameBufferType, outputFrameBufferContextType: outputFrameBufferContextType };
|
|
7019
7027
|
data.name = processor._name || 'VideoProcessor';
|
|
7020
|
-
['assetsPath', 'blurFilterRadius', 'fitType', 'isSimdEnabled', 'maskBlurRadius', 'version'].forEach(function (prop) {
|
|
7028
|
+
['assetsPath', 'blurFilterRadius', 'debounce', 'fitType', 'isSimdEnabled', 'maskBlurRadius', 'pipeline', 'version'].forEach(function (prop) {
|
|
7021
7029
|
var val = processor["_" + prop];
|
|
7022
7030
|
if (typeof val !== 'undefined') {
|
|
7023
7031
|
data[prop] = val;
|
|
@@ -7155,6 +7163,10 @@ var VideoTrack = /** @class */ (function (_super) {
|
|
|
7155
7163
|
value: null,
|
|
7156
7164
|
writable: true,
|
|
7157
7165
|
},
|
|
7166
|
+
_processorOptions: {
|
|
7167
|
+
value: {},
|
|
7168
|
+
writable: true,
|
|
7169
|
+
},
|
|
7158
7170
|
_unmuteHandler: {
|
|
7159
7171
|
value: null,
|
|
7160
7172
|
writable: true
|
|
@@ -7246,18 +7258,21 @@ var VideoTrack = /** @class */ (function (_super) {
|
|
|
7246
7258
|
var _a = _this.mediaStreamTrack.getSettings(), _b = _a.width, width = _b === void 0 ? 0 : _b, _c = _a.height, height = _c === void 0 ? 0 : _c;
|
|
7247
7259
|
// Setting the canvas' dimension triggers a redraw.
|
|
7248
7260
|
// Only set it if it has changed.
|
|
7249
|
-
if (_this.
|
|
7250
|
-
_this.
|
|
7251
|
-
_this.
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7261
|
+
if (_this._outputFrame && _this._outputFrame.width !== width) {
|
|
7262
|
+
_this._outputFrame.width = width;
|
|
7263
|
+
_this._outputFrame.height = height;
|
|
7264
|
+
}
|
|
7265
|
+
if (_this._inputFrame) {
|
|
7266
|
+
if (_this._inputFrame.width !== width) {
|
|
7267
|
+
_this._inputFrame.width = width;
|
|
7268
|
+
_this._inputFrame.height = height;
|
|
7255
7269
|
}
|
|
7270
|
+
_this._inputFrame.getContext('2d').drawImage(_this._dummyEl, 0, 0, width, height);
|
|
7256
7271
|
}
|
|
7257
|
-
_this._inputFrame.getContext('2d').drawImage(_this._dummyEl, 0, 0, width, height);
|
|
7258
7272
|
var result = null;
|
|
7259
7273
|
try {
|
|
7260
|
-
|
|
7274
|
+
var input = _this._processorOptions.inputFrameBufferType === 'video' ? _this._dummyEl : _this._inputFrame;
|
|
7275
|
+
result = _this.processor.processFrame(input, _this._outputFrame);
|
|
7261
7276
|
}
|
|
7262
7277
|
catch (ex) {
|
|
7263
7278
|
_this._log.debug('Exception detected after calling processFrame.', ex);
|
|
@@ -7265,7 +7280,9 @@ var VideoTrack = /** @class */ (function (_super) {
|
|
|
7265
7280
|
((result instanceof Promise) ? result : Promise.resolve(result))
|
|
7266
7281
|
.then(function () {
|
|
7267
7282
|
if (_this._outputFrame) {
|
|
7268
|
-
_this.processedTrack.requestFrame
|
|
7283
|
+
if (typeof _this.processedTrack.requestFrame === 'function') {
|
|
7284
|
+
_this.processedTrack.requestFrame();
|
|
7285
|
+
}
|
|
7269
7286
|
_this._processorEventObserver.emit('stats');
|
|
7270
7287
|
}
|
|
7271
7288
|
})
|
|
@@ -7308,8 +7325,9 @@ var VideoTrack = /** @class */ (function (_super) {
|
|
|
7308
7325
|
VideoTrack.prototype._restartProcessor = function () {
|
|
7309
7326
|
var processor = this.processor;
|
|
7310
7327
|
if (processor) {
|
|
7328
|
+
var processorOptions = Object.assign({}, this._processorOptions);
|
|
7311
7329
|
this.removeProcessor(processor);
|
|
7312
|
-
this.addProcessor(processor);
|
|
7330
|
+
this.addProcessor(processor, processorOptions);
|
|
7313
7331
|
}
|
|
7314
7332
|
};
|
|
7315
7333
|
/**
|
|
@@ -7324,8 +7342,8 @@ var VideoTrack = /** @class */ (function (_super) {
|
|
|
7324
7342
|
};
|
|
7325
7343
|
/**
|
|
7326
7344
|
* Add a {@link VideoProcessor} to allow for custom processing of video frames belonging to a VideoTrack.
|
|
7327
|
-
* Only Chrome supports this as of now. Calling this API from a non-supported browser will result in a log warning.
|
|
7328
7345
|
* @param {VideoProcessor} processor - The {@link VideoProcessor} to use.
|
|
7346
|
+
* @param {AddProcessorOptions} [options] - {@link AddProcessorOptions} to provide.
|
|
7329
7347
|
* @returns {this}
|
|
7330
7348
|
* @example
|
|
7331
7349
|
* class GrayScaleProcessor {
|
|
@@ -7343,11 +7361,8 @@ var VideoTrack = /** @class */ (function (_super) {
|
|
|
7343
7361
|
* videoTrack.addProcessor(new GrayScaleProcessor(100));
|
|
7344
7362
|
* });
|
|
7345
7363
|
*/
|
|
7346
|
-
VideoTrack.prototype.addProcessor = function (processor) {
|
|
7364
|
+
VideoTrack.prototype.addProcessor = function (processor, options) {
|
|
7347
7365
|
var _this = this;
|
|
7348
|
-
if (typeof OffscreenCanvas !== 'function') {
|
|
7349
|
-
return this._log.warn('Adding a VideoProcessor is not supported in this browser.');
|
|
7350
|
-
}
|
|
7351
7366
|
if (!processor || typeof processor.processFrame !== 'function') {
|
|
7352
7367
|
throw new Error('Received an invalid VideoProcessor from addProcessor.');
|
|
7353
7368
|
}
|
|
@@ -7371,12 +7386,46 @@ var VideoTrack = /** @class */ (function (_super) {
|
|
|
7371
7386
|
};
|
|
7372
7387
|
this.mediaStreamTrack.addEventListener('unmute', this._unmuteHandler);
|
|
7373
7388
|
}
|
|
7374
|
-
|
|
7375
|
-
this.
|
|
7389
|
+
this._processorOptions = options || {};
|
|
7390
|
+
var _a = this._processorOptions, inputFrameBufferType = _a.inputFrameBufferType, outputFrameBufferContextType = _a.outputFrameBufferContextType;
|
|
7391
|
+
if (typeof OffscreenCanvas === 'undefined' && inputFrameBufferType === 'offscreencanvas') {
|
|
7392
|
+
throw new Error('OffscreenCanvas is not supported by this browser.');
|
|
7393
|
+
}
|
|
7394
|
+
if (inputFrameBufferType && inputFrameBufferType !== 'video' && inputFrameBufferType !== 'canvas' && inputFrameBufferType !== 'offscreencanvas') {
|
|
7395
|
+
throw new Error("Invalid inputFrameBufferType of " + inputFrameBufferType);
|
|
7396
|
+
}
|
|
7397
|
+
if (!inputFrameBufferType) {
|
|
7398
|
+
inputFrameBufferType = typeof OffscreenCanvas === 'undefined' ? 'canvas' : 'offscreencanvas';
|
|
7399
|
+
}
|
|
7400
|
+
var _b = this.mediaStreamTrack.getSettings(), _c = _b.width, width = _c === void 0 ? 0 : _c, _d = _b.height, height = _d === void 0 ? 0 : _d, _e = _b.frameRate, frameRate = _e === void 0 ? DEFAULT_FRAME_RATE : _e;
|
|
7401
|
+
if (inputFrameBufferType === 'offscreencanvas') {
|
|
7402
|
+
this._inputFrame = new OffscreenCanvas(width, height);
|
|
7403
|
+
}
|
|
7404
|
+
if (inputFrameBufferType === 'canvas') {
|
|
7405
|
+
this._inputFrame = document.createElement('canvas');
|
|
7406
|
+
}
|
|
7407
|
+
if (this._inputFrame) {
|
|
7408
|
+
this._inputFrame.width = width;
|
|
7409
|
+
this._inputFrame.height = height;
|
|
7410
|
+
}
|
|
7376
7411
|
this._outputFrame = document.createElement('canvas');
|
|
7377
7412
|
this._outputFrame.width = width;
|
|
7378
7413
|
this._outputFrame.height = height;
|
|
7379
|
-
|
|
7414
|
+
// NOTE(csantos): Initialize the rendering context for future renders. This also ensures
|
|
7415
|
+
// that the correct type is used and on Firefox, it throws an exception if you try to capture
|
|
7416
|
+
// frames prior calling getContext https://bugzilla.mozilla.org/show_bug.cgi?id=1572422
|
|
7417
|
+
outputFrameBufferContextType = outputFrameBufferContextType || '2d';
|
|
7418
|
+
var ctx = this._outputFrame.getContext(outputFrameBufferContextType);
|
|
7419
|
+
if (!ctx) {
|
|
7420
|
+
throw new Error("Cannot get outputFrameBufferContextType: " + outputFrameBufferContextType + ".");
|
|
7421
|
+
}
|
|
7422
|
+
// NOTE(csantos): Zero FPS means we can control when to render the next frame by calling requestFrame.
|
|
7423
|
+
// Some browsers such as Firefox doesn't support requestFrame so we will use default, which is an undefined value.
|
|
7424
|
+
// This means, the browser will use the highest FPS available.
|
|
7425
|
+
var targetFps = typeof CanvasCaptureMediaStreamTrack !== 'undefined' && CanvasCaptureMediaStreamTrack.prototype &&
|
|
7426
|
+
// eslint-disable-next-line
|
|
7427
|
+
typeof CanvasCaptureMediaStreamTrack.prototype.requestFrame === 'function' ? 0 : undefined;
|
|
7428
|
+
this.processedTrack = this._outputFrame.captureStream(targetFps).getTracks()[0];
|
|
7380
7429
|
this.processedTrack.enabled = this.mediaStreamTrack.enabled;
|
|
7381
7430
|
this.processor = processor;
|
|
7382
7431
|
this._processorEventObserver.emit('add', {
|
|
@@ -7384,7 +7433,9 @@ var VideoTrack = /** @class */ (function (_super) {
|
|
|
7384
7433
|
captureHeight: height,
|
|
7385
7434
|
captureWidth: width,
|
|
7386
7435
|
inputFrameRate: frameRate,
|
|
7387
|
-
isRemoteVideoTrack: this.toString().includes('RemoteVideoTrack')
|
|
7436
|
+
isRemoteVideoTrack: this.toString().includes('RemoteVideoTrack'),
|
|
7437
|
+
inputFrameBufferType: inputFrameBufferType,
|
|
7438
|
+
outputFrameBufferContextType: outputFrameBufferContextType
|
|
7388
7439
|
});
|
|
7389
7440
|
this._updateElementsMediaStreamTrack();
|
|
7390
7441
|
this._captureFrames();
|
|
@@ -7521,12 +7572,11 @@ var VideoTrack = /** @class */ (function (_super) {
|
|
|
7521
7572
|
this._log.debug('Removing VideoProcessor from the VideoTrack', processor);
|
|
7522
7573
|
clearTimeout(this._captureTimeoutId);
|
|
7523
7574
|
this.mediaStreamTrack.removeEventListener('unmute', this._unmuteHandler);
|
|
7575
|
+
this._processorOptions = {};
|
|
7524
7576
|
this._unmuteHandler = null;
|
|
7525
7577
|
this._isCapturing = false;
|
|
7526
7578
|
this.processor = null;
|
|
7527
7579
|
this.processedTrack = null;
|
|
7528
|
-
this._inputFrame.getContext('2d').clearRect(0, 0, this._inputFrame.width, this._inputFrame.height);
|
|
7529
|
-
this._outputFrame.getContext('2d').clearRect(0, 0, this._outputFrame.width, this._outputFrame.height);
|
|
7530
7580
|
this._inputFrame = null;
|
|
7531
7581
|
this._outputFrame = null;
|
|
7532
7582
|
this._updateElementsMediaStreamTrack();
|
|
@@ -7560,7 +7610,7 @@ function dimensionsChanged(track, elem) {
|
|
|
7560
7610
|
* Any exception raised (either synchronously or asynchronously) in `processFrame` will result in the frame being dropped.
|
|
7561
7611
|
* This callback has the following signature:<br/><br/>
|
|
7562
7612
|
* <code>processFrame(</code><br/>
|
|
7563
|
-
* <code>inputFrameBuffer: OffscreenCanvas,</code><br/>
|
|
7613
|
+
* <code>inputFrameBuffer: OffscreenCanvas | HTMLCanvasElement | HTMLVideoElement,</code><br/>
|
|
7564
7614
|
* <code>outputFrameBuffer: HTMLCanvasElement</code><br/>
|
|
7565
7615
|
* <code>): Promise<void> | void;</code>
|
|
7566
7616
|
*
|
|
@@ -7576,6 +7626,30 @@ function dimensionsChanged(track, elem) {
|
|
|
7576
7626
|
* }
|
|
7577
7627
|
* }
|
|
7578
7628
|
*/
|
|
7629
|
+
/**
|
|
7630
|
+
* Possible options to provide to {@link LocalVideoTrack#addProcessor} and {@link RemoteVideoTrack#addProcessor}.
|
|
7631
|
+
* @typedef {object} AddProcessorOptions
|
|
7632
|
+
* @property {string} [inputFrameBufferType="offscreencanvas"] - This option allows you to specify what kind of input you want to receive in your
|
|
7633
|
+
* Video Processor. The default is `offscreencanvas` and will fallback to a regular `canvas` if the browser does not support it.
|
|
7634
|
+
* Possible values include the following.
|
|
7635
|
+
* <br/>
|
|
7636
|
+
* <br/>
|
|
7637
|
+
* `offscreencanvas` - Your Video Processor will receive an [OffscreenCanvas](https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas)
|
|
7638
|
+
* which is good for canvas-related processing that can be rendered off screen.
|
|
7639
|
+
* <br/>
|
|
7640
|
+
* <br/>
|
|
7641
|
+
* `canvas` - Your Video Processor will receive an [HTMLCanvasElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement).
|
|
7642
|
+
* This is recommended on browsers that doesn't support `OffscreenCanvas`, or if you need to render the frame on the screen.
|
|
7643
|
+
* <br/>
|
|
7644
|
+
* <br/>
|
|
7645
|
+
* `video` - Your Video Processor will receive an [HTMLVideoElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement).
|
|
7646
|
+
* Use this option if you are processing the frame using WebGL or if you only need to [draw](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage)
|
|
7647
|
+
* the frame directly to your output canvas.
|
|
7648
|
+
* @property {string} [outputFrameBufferContextType="2d"] - The SDK needs the [context type](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext)
|
|
7649
|
+
* that your Video Processor uses in order to properly generate the processed track. For example, if your Video Processor uses WebGL2 (`canvas.getContext('webgl2')`),
|
|
7650
|
+
* you should set `outputFrameBufferContextType` to `webgl2`. Or if you're using Canvas 2D processing (`canvas.getContext('2d')`),
|
|
7651
|
+
* you should set `outputFrameBufferContextType` to `2d`.
|
|
7652
|
+
*/
|
|
7579
7653
|
/**
|
|
7580
7654
|
* The {@link VideoTrack}'s dimensions changed.
|
|
7581
7655
|
* @param {VideoTrack} track - The {@link VideoTrack} whose dimensions changed
|
|
@@ -26761,14 +26835,14 @@ function getTracks(peerConnection, kind, localOrRemote) {
|
|
|
26761
26835
|
function getTrackStats(peerConnection, track, options) {
|
|
26762
26836
|
if (options === void 0) { options = {}; }
|
|
26763
26837
|
if (typeof options.testForChrome !== 'undefined' || isChrome) {
|
|
26764
|
-
return chromeOrSafariGetTrackStats(peerConnection, track);
|
|
26838
|
+
return chromeOrSafariGetTrackStats(peerConnection, track, options);
|
|
26765
26839
|
}
|
|
26766
26840
|
if (typeof options.testForFirefox !== 'undefined' || isFirefox) {
|
|
26767
|
-
return firefoxGetTrackStats(peerConnection, track, options
|
|
26841
|
+
return firefoxGetTrackStats(peerConnection, track, options);
|
|
26768
26842
|
}
|
|
26769
26843
|
if (typeof options.testForSafari !== 'undefined' || isSafari) {
|
|
26770
26844
|
if (typeof options.testForSafari !== 'undefined' || getSdpFormat() === 'unified') {
|
|
26771
|
-
return chromeOrSafariGetTrackStats(peerConnection, track);
|
|
26845
|
+
return chromeOrSafariGetTrackStats(peerConnection, track, options);
|
|
26772
26846
|
}
|
|
26773
26847
|
// NOTE(syerrapragada): getStats() is not supported on
|
|
26774
26848
|
// Safari versions where plan-b is the SDP format
|
|
@@ -26784,33 +26858,31 @@ function getTrackStats(peerConnection, track, options) {
|
|
|
26784
26858
|
* Get the standardized statistics for a particular MediaStreamTrack in Chrome or Safari.
|
|
26785
26859
|
* @param {RTCPeerConnection} peerConnection
|
|
26786
26860
|
* @param {MediaStreamTrack} track
|
|
26861
|
+
* @param {object} options - Used for testing
|
|
26787
26862
|
* @returns {Promise.<Array<StandardizedTrackStatsReport>>}
|
|
26788
26863
|
*/
|
|
26789
|
-
function chromeOrSafariGetTrackStats(peerConnection, track) {
|
|
26790
|
-
|
|
26791
|
-
|
|
26864
|
+
function chromeOrSafariGetTrackStats(peerConnection, track, options) {
|
|
26865
|
+
if (chromeMajorVersion && chromeMajorVersion < 67) {
|
|
26866
|
+
return new Promise(function (resolve, reject) {
|
|
26792
26867
|
peerConnection.getStats(function (response) {
|
|
26793
26868
|
resolve([standardizeChromeLegacyStats(response, track)]);
|
|
26794
26869
|
}, null, reject);
|
|
26795
|
-
|
|
26796
|
-
|
|
26797
|
-
|
|
26798
|
-
|
|
26799
|
-
}, reject);
|
|
26870
|
+
});
|
|
26871
|
+
}
|
|
26872
|
+
return peerConnection.getStats(track).then(function (response) {
|
|
26873
|
+
return standardizeChromeOrSafariStats(response, options);
|
|
26800
26874
|
});
|
|
26801
26875
|
}
|
|
26802
26876
|
/**
|
|
26803
26877
|
* Get the standardized statistics for a particular MediaStreamTrack in Firefox.
|
|
26804
26878
|
* @param {RTCPeerConnection} peerConnection
|
|
26805
26879
|
* @param {MediaStreamTrack} track
|
|
26806
|
-
* @param {
|
|
26880
|
+
* @param {object} options
|
|
26807
26881
|
* @returns {Promise.<Array<StandardizedTrackStatsReport>>}
|
|
26808
26882
|
*/
|
|
26809
|
-
function firefoxGetTrackStats(peerConnection, track,
|
|
26810
|
-
return
|
|
26811
|
-
|
|
26812
|
-
resolve([standardizeFirefoxStats(response, isRemote)]);
|
|
26813
|
-
}, reject);
|
|
26883
|
+
function firefoxGetTrackStats(peerConnection, track, options) {
|
|
26884
|
+
return peerConnection.getStats(track).then(function (response) {
|
|
26885
|
+
return [standardizeFirefoxStats(response, options)];
|
|
26814
26886
|
});
|
|
26815
26887
|
}
|
|
26816
26888
|
/**
|
|
@@ -26885,9 +26957,14 @@ function standardizeChromeLegacyStats(response, track) {
|
|
|
26885
26957
|
/**
|
|
26886
26958
|
* Standardize the MediaStreamTrack's statistics in Chrome or Safari.
|
|
26887
26959
|
* @param {RTCStatsResponse} response
|
|
26960
|
+
* @param {object} options - Used for testing
|
|
26888
26961
|
* @returns {Array<StandardizedTrackStatsReport>}
|
|
26889
26962
|
*/
|
|
26890
|
-
function standardizeChromeOrSafariStats(response) {
|
|
26963
|
+
function standardizeChromeOrSafariStats(response, _a) {
|
|
26964
|
+
var _b = _a.simulateExceptionWhileStandardizingStats, simulateExceptionWhileStandardizingStats = _b === void 0 ? false : _b;
|
|
26965
|
+
if (simulateExceptionWhileStandardizingStats) {
|
|
26966
|
+
throw new Error('Error while gathering stats');
|
|
26967
|
+
}
|
|
26891
26968
|
var inbound = null;
|
|
26892
26969
|
// NOTE(mpatwardhan): We should expect more than one "outbound-rtp" stats for a
|
|
26893
26970
|
// VP8 simulcast MediaStreamTrack.
|
|
@@ -27054,17 +27131,21 @@ function standardizeChromeOrSafariStats(response) {
|
|
|
27054
27131
|
/**
|
|
27055
27132
|
* Standardize the MediaStreamTrack's statistics in Firefox.
|
|
27056
27133
|
* @param {RTCStatsReport} response
|
|
27057
|
-
* @param {
|
|
27134
|
+
* @param {object} options - Used for testing
|
|
27058
27135
|
* @returns {StandardizedTrackStatsReport}
|
|
27059
27136
|
*/
|
|
27060
|
-
function standardizeFirefoxStats(response,
|
|
27137
|
+
function standardizeFirefoxStats(response, _a) {
|
|
27138
|
+
if (response === void 0) { response = new Map(); }
|
|
27139
|
+
var isRemote = _a.isRemote, _b = _a.simulateExceptionWhileStandardizingStats, simulateExceptionWhileStandardizingStats = _b === void 0 ? false : _b;
|
|
27140
|
+
if (simulateExceptionWhileStandardizingStats) {
|
|
27141
|
+
throw new Error('Error while gathering stats');
|
|
27142
|
+
}
|
|
27061
27143
|
// NOTE(mroberts): If getStats is called on a closed RTCPeerConnection,
|
|
27062
27144
|
// Firefox returns undefined instead of an RTCStatsReport. We workaround this
|
|
27063
27145
|
// here. See the following bug for more details:
|
|
27064
27146
|
//
|
|
27065
27147
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1377225
|
|
27066
27148
|
//
|
|
27067
|
-
if (response === void 0) { response = new Map(); }
|
|
27068
27149
|
var inbound = null;
|
|
27069
27150
|
var outbound = null;
|
|
27070
27151
|
// NOTE(mmalavalli): Starting from Firefox 63, RTC{Inbound, Outbound}RTPStreamStats.isRemote
|
|
@@ -29093,18 +29174,79 @@ function support() {
|
|
|
29093
29174
|
&& typeof navigator.mediaDevices.getUserMedia === 'function'
|
|
29094
29175
|
&& typeof RTCPeerConnection === 'function';
|
|
29095
29176
|
}
|
|
29177
|
+
/**
|
|
29178
|
+
* Create a Set of supported codecs for a certain kind of media.
|
|
29179
|
+
* @param {'audio'|'video'} kind
|
|
29180
|
+
* @returns {Promise<Set<AudioCodec|VideoCodec>>}
|
|
29181
|
+
*/
|
|
29182
|
+
function createSupportedCodecsSet(kind) {
|
|
29183
|
+
if (typeof RTCRtpSender !== 'undefined'
|
|
29184
|
+
&& typeof RTCRtpSender.getCapabilities === 'function') {
|
|
29185
|
+
return Promise.resolve(new Set(RTCRtpSender
|
|
29186
|
+
.getCapabilities(kind)
|
|
29187
|
+
.codecs
|
|
29188
|
+
.map(function (_a) {
|
|
29189
|
+
var mimeType = _a.mimeType;
|
|
29190
|
+
return mimeType.split('/')[1].toLowerCase();
|
|
29191
|
+
})));
|
|
29192
|
+
}
|
|
29193
|
+
if (typeof RTCPeerConnection === 'undefined'
|
|
29194
|
+
|| typeof RTCPeerConnection.prototype === 'undefined'
|
|
29195
|
+
|| typeof RTCPeerConnection.prototype.addTransceiver !== 'function'
|
|
29196
|
+
|| typeof RTCPeerConnection.prototype.close !== 'function'
|
|
29197
|
+
|| typeof RTCPeerConnection.prototype.createOffer !== 'function') {
|
|
29198
|
+
return Promise.resolve(new Set());
|
|
29199
|
+
}
|
|
29200
|
+
var pc = new RTCPeerConnection();
|
|
29201
|
+
pc.addTransceiver(kind);
|
|
29202
|
+
return pc.createOffer().then(function (_a) {
|
|
29203
|
+
var sdp = _a.sdp;
|
|
29204
|
+
pc.close();
|
|
29205
|
+
return new Set((sdp.match(/^a=rtpmap:.+$/gm) || [])
|
|
29206
|
+
.map(function (line) { return line.match(/^a=rtpmap:.+ ([^/]+)/)[1].toLowerCase(); }));
|
|
29207
|
+
}, function () {
|
|
29208
|
+
pc.close();
|
|
29209
|
+
return new Set();
|
|
29210
|
+
});
|
|
29211
|
+
}
|
|
29212
|
+
// NOTE(mmalavalli): Cache the supported audio and video codecs here.
|
|
29213
|
+
var supportedCodecs = new Map();
|
|
29214
|
+
/**
|
|
29215
|
+
* Check whether a given codec for a certain kind of media is supported.
|
|
29216
|
+
* @param {AudioCodec|VideoCodec} codec
|
|
29217
|
+
* @param {'audio'|'video'} kind
|
|
29218
|
+
* @returns {Promise<boolean>}
|
|
29219
|
+
*/
|
|
29220
|
+
function isCodecSupported(codec, kind) {
|
|
29221
|
+
var codecs = supportedCodecs.get(kind);
|
|
29222
|
+
if (codecs) {
|
|
29223
|
+
return Promise.resolve(codecs.has(codec.toLowerCase()));
|
|
29224
|
+
}
|
|
29225
|
+
return createSupportedCodecsSet(kind).then(function (codecs) {
|
|
29226
|
+
supportedCodecs.set(kind, codecs);
|
|
29227
|
+
return codecs.has(codec.toLowerCase());
|
|
29228
|
+
});
|
|
29229
|
+
}
|
|
29230
|
+
/**
|
|
29231
|
+
* Clear cached supported codecs (unit tests only).
|
|
29232
|
+
*/
|
|
29233
|
+
function clearCachedSupportedCodecs() {
|
|
29234
|
+
supportedCodecs.clear();
|
|
29235
|
+
}
|
|
29096
29236
|
/**
|
|
29097
29237
|
* @typedef {object} Deferred
|
|
29098
29238
|
* @property {Promise} promise
|
|
29099
29239
|
* @property {function} reject
|
|
29100
29240
|
* @property {function} resolve
|
|
29101
29241
|
*/
|
|
29242
|
+
exports.clearCachedSupportedCodecs = clearCachedSupportedCodecs;
|
|
29102
29243
|
exports.defer = defer;
|
|
29103
29244
|
exports.delegateMethods = delegateMethods;
|
|
29104
29245
|
exports.difference = difference;
|
|
29105
29246
|
exports.flatMap = flatMap;
|
|
29106
29247
|
exports.guessBrowser = guessBrowser;
|
|
29107
29248
|
exports.guessBrowserVersion = guessBrowserVersion;
|
|
29249
|
+
exports.isCodecSupported = isCodecSupported;
|
|
29108
29250
|
exports.isIOSChrome = isIOSChrome;
|
|
29109
29251
|
exports.interceptEvent = interceptEvent;
|
|
29110
29252
|
exports.legacyPromise = legacyPromise;
|
|
@@ -30010,7 +30152,7 @@ module.exports={
|
|
|
30010
30152
|
"name": "twilio-video",
|
|
30011
30153
|
"title": "Twilio Video",
|
|
30012
30154
|
"description": "Twilio Video JavaScript Library",
|
|
30013
|
-
"version": "2.
|
|
30155
|
+
"version": "2.27.0",
|
|
30014
30156
|
"homepage": "https://twilio.com",
|
|
30015
30157
|
"author": "Mark Andrus Roberts <mroberts@twilio.com>",
|
|
30016
30158
|
"contributors": [
|