twilio-video 2.26.2 → 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 CHANGED
@@ -2,6 +2,43 @@ 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
+
5
42
  2.26.2 (February 21, 2023)
6
43
  ==========================
7
44
 
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.26.2/twilio-video.min.js"></script>
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:
@@ -1,4 +1,4 @@
1
- /*! twilio-video.js 2.26.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.
@@ -3873,8 +3873,10 @@ function restartWhenInadvertentlyStopped(localMediaTrack) {
3873
3873
  }).catch(function (error) {
3874
3874
  log.warn('Failed to detect silence:', error);
3875
3875
  }).finally(function () {
3876
- // Pause the dummy element again.
3877
- el.pause();
3876
+ // Pause the dummy element again, if there is no processed track.
3877
+ if (!localMediaTrack.processedTrack) {
3878
+ el.pause();
3879
+ }
3878
3880
  });
3879
3881
  }
3880
3882
  function shouldReacquireTrack() {
@@ -4232,8 +4234,8 @@ var LocalVideoTrack = /** @class */ (function (_super) {
4232
4234
  };
4233
4235
  /**
4234
4236
  * Add a {@link VideoProcessor} to allow for custom processing of video frames belonging to a VideoTrack.
4235
- * Only Chrome supports this as of now. Calling this API from a non-supported browser will result in a log warning.
4236
4237
  * @param {VideoProcessor} processor - The {@link VideoProcessor} to use.
4238
+ * @param {AddProcessorOptions} [options] - {@link AddProcessorOptions} to provide.
4237
4239
  * @returns {this}
4238
4240
  * @example
4239
4241
  * class GrayScaleProcessor {
@@ -4433,9 +4435,10 @@ function workaroundSilentLocalVideo(localVideoTrack, doc) {
4433
4435
  }).catch(function (error) {
4434
4436
  log.warn('Failed to detect silence and restart:', error);
4435
4437
  }).finally(function () {
4436
- // 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.
4437
4440
  el = localVideoTrack._dummyEl;
4438
- if (el && !el.paused) {
4441
+ if (el && !el.paused && !localVideoTrack.processedTrack) {
4439
4442
  el.pause();
4440
4443
  }
4441
4444
  // Reset the unmute handler.
@@ -6351,8 +6354,8 @@ var RemoteVideoTrack = /** @class */ (function (_super) {
6351
6354
  * Add a {@link VideoProcessor} to allow for custom processing of video frames belonging to a VideoTrack.
6352
6355
  * When a Participant un-publishes and re-publishes a VideoTrack, a new RemoteVideoTrack is created and
6353
6356
  * any VideoProcessors attached to the previous RemoteVideoTrack would have to be re-added again.
6354
- * Only Chrome supports this as of now. Calling this API from a non-supported browser will result in a log warning.
6355
6357
  * @param {VideoProcessor} processor - The {@link VideoProcessor} to use.
6358
+ * @param {AddProcessorOptions} [options] - {@link AddProcessorOptions} to provide.
6356
6359
  * @returns {this}
6357
6360
  * @example
6358
6361
  * class GrayScaleProcessor {
@@ -7019,10 +7022,10 @@ var VideoProcessorEventObserver = /** @class */ (function (_super) {
7019
7022
  if (!this._processorInfo) {
7020
7023
  return {};
7021
7024
  }
7022
- var _a = this._processorInfo, processor = _a.processor, captureHeight = _a.captureHeight, captureWidth = _a.captureWidth, inputFrameRate = _a.inputFrameRate, isRemoteVideoTrack = _a.isRemoteVideoTrack;
7023
- 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 };
7024
7027
  data.name = processor._name || 'VideoProcessor';
7025
- ['assetsPath', 'blurFilterRadius', 'fitType', 'isSimdEnabled', 'maskBlurRadius', 'version'].forEach(function (prop) {
7028
+ ['assetsPath', 'blurFilterRadius', 'debounce', 'fitType', 'isSimdEnabled', 'maskBlurRadius', 'pipeline', 'version'].forEach(function (prop) {
7026
7029
  var val = processor["_" + prop];
7027
7030
  if (typeof val !== 'undefined') {
7028
7031
  data[prop] = val;
@@ -7160,6 +7163,10 @@ var VideoTrack = /** @class */ (function (_super) {
7160
7163
  value: null,
7161
7164
  writable: true,
7162
7165
  },
7166
+ _processorOptions: {
7167
+ value: {},
7168
+ writable: true,
7169
+ },
7163
7170
  _unmuteHandler: {
7164
7171
  value: null,
7165
7172
  writable: true
@@ -7251,18 +7258,21 @@ var VideoTrack = /** @class */ (function (_super) {
7251
7258
  var _a = _this.mediaStreamTrack.getSettings(), _b = _a.width, width = _b === void 0 ? 0 : _b, _c = _a.height, height = _c === void 0 ? 0 : _c;
7252
7259
  // Setting the canvas' dimension triggers a redraw.
7253
7260
  // Only set it if it has changed.
7254
- if (_this._inputFrame.width !== width) {
7255
- _this._inputFrame.width = width;
7256
- _this._inputFrame.height = height;
7257
- if (_this._outputFrame) {
7258
- _this._outputFrame.width = width;
7259
- _this._outputFrame.height = height;
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;
7260
7269
  }
7270
+ _this._inputFrame.getContext('2d').drawImage(_this._dummyEl, 0, 0, width, height);
7261
7271
  }
7262
- _this._inputFrame.getContext('2d').drawImage(_this._dummyEl, 0, 0, width, height);
7263
7272
  var result = null;
7264
7273
  try {
7265
- result = _this.processor.processFrame(_this._inputFrame, _this._outputFrame);
7274
+ var input = _this._processorOptions.inputFrameBufferType === 'video' ? _this._dummyEl : _this._inputFrame;
7275
+ result = _this.processor.processFrame(input, _this._outputFrame);
7266
7276
  }
7267
7277
  catch (ex) {
7268
7278
  _this._log.debug('Exception detected after calling processFrame.', ex);
@@ -7270,7 +7280,9 @@ var VideoTrack = /** @class */ (function (_super) {
7270
7280
  ((result instanceof Promise) ? result : Promise.resolve(result))
7271
7281
  .then(function () {
7272
7282
  if (_this._outputFrame) {
7273
- _this.processedTrack.requestFrame();
7283
+ if (typeof _this.processedTrack.requestFrame === 'function') {
7284
+ _this.processedTrack.requestFrame();
7285
+ }
7274
7286
  _this._processorEventObserver.emit('stats');
7275
7287
  }
7276
7288
  })
@@ -7313,8 +7325,9 @@ var VideoTrack = /** @class */ (function (_super) {
7313
7325
  VideoTrack.prototype._restartProcessor = function () {
7314
7326
  var processor = this.processor;
7315
7327
  if (processor) {
7328
+ var processorOptions = Object.assign({}, this._processorOptions);
7316
7329
  this.removeProcessor(processor);
7317
- this.addProcessor(processor);
7330
+ this.addProcessor(processor, processorOptions);
7318
7331
  }
7319
7332
  };
7320
7333
  /**
@@ -7329,8 +7342,8 @@ var VideoTrack = /** @class */ (function (_super) {
7329
7342
  };
7330
7343
  /**
7331
7344
  * Add a {@link VideoProcessor} to allow for custom processing of video frames belonging to a VideoTrack.
7332
- * Only Chrome supports this as of now. Calling this API from a non-supported browser will result in a log warning.
7333
7345
  * @param {VideoProcessor} processor - The {@link VideoProcessor} to use.
7346
+ * @param {AddProcessorOptions} [options] - {@link AddProcessorOptions} to provide.
7334
7347
  * @returns {this}
7335
7348
  * @example
7336
7349
  * class GrayScaleProcessor {
@@ -7348,11 +7361,8 @@ var VideoTrack = /** @class */ (function (_super) {
7348
7361
  * videoTrack.addProcessor(new GrayScaleProcessor(100));
7349
7362
  * });
7350
7363
  */
7351
- VideoTrack.prototype.addProcessor = function (processor) {
7364
+ VideoTrack.prototype.addProcessor = function (processor, options) {
7352
7365
  var _this = this;
7353
- if (typeof OffscreenCanvas !== 'function') {
7354
- return this._log.warn('Adding a VideoProcessor is not supported in this browser.');
7355
- }
7356
7366
  if (!processor || typeof processor.processFrame !== 'function') {
7357
7367
  throw new Error('Received an invalid VideoProcessor from addProcessor.');
7358
7368
  }
@@ -7376,12 +7386,46 @@ var VideoTrack = /** @class */ (function (_super) {
7376
7386
  };
7377
7387
  this.mediaStreamTrack.addEventListener('unmute', this._unmuteHandler);
7378
7388
  }
7379
- var _a = this.mediaStreamTrack.getSettings(), _b = _a.width, width = _b === void 0 ? 0 : _b, _c = _a.height, height = _c === void 0 ? 0 : _c, _d = _a.frameRate, frameRate = _d === void 0 ? DEFAULT_FRAME_RATE : _d;
7380
- this._inputFrame = new OffscreenCanvas(width, height);
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
+ }
7381
7411
  this._outputFrame = document.createElement('canvas');
7382
7412
  this._outputFrame.width = width;
7383
7413
  this._outputFrame.height = height;
7384
- this.processedTrack = this._outputFrame.captureStream(0).getTracks()[0];
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];
7385
7429
  this.processedTrack.enabled = this.mediaStreamTrack.enabled;
7386
7430
  this.processor = processor;
7387
7431
  this._processorEventObserver.emit('add', {
@@ -7389,7 +7433,9 @@ var VideoTrack = /** @class */ (function (_super) {
7389
7433
  captureHeight: height,
7390
7434
  captureWidth: width,
7391
7435
  inputFrameRate: frameRate,
7392
- isRemoteVideoTrack: this.toString().includes('RemoteVideoTrack')
7436
+ isRemoteVideoTrack: this.toString().includes('RemoteVideoTrack'),
7437
+ inputFrameBufferType: inputFrameBufferType,
7438
+ outputFrameBufferContextType: outputFrameBufferContextType
7393
7439
  });
7394
7440
  this._updateElementsMediaStreamTrack();
7395
7441
  this._captureFrames();
@@ -7526,12 +7572,11 @@ var VideoTrack = /** @class */ (function (_super) {
7526
7572
  this._log.debug('Removing VideoProcessor from the VideoTrack', processor);
7527
7573
  clearTimeout(this._captureTimeoutId);
7528
7574
  this.mediaStreamTrack.removeEventListener('unmute', this._unmuteHandler);
7575
+ this._processorOptions = {};
7529
7576
  this._unmuteHandler = null;
7530
7577
  this._isCapturing = false;
7531
7578
  this.processor = null;
7532
7579
  this.processedTrack = null;
7533
- this._inputFrame.getContext('2d').clearRect(0, 0, this._inputFrame.width, this._inputFrame.height);
7534
- this._outputFrame.getContext('2d').clearRect(0, 0, this._outputFrame.width, this._outputFrame.height);
7535
7580
  this._inputFrame = null;
7536
7581
  this._outputFrame = null;
7537
7582
  this._updateElementsMediaStreamTrack();
@@ -7565,7 +7610,7 @@ function dimensionsChanged(track, elem) {
7565
7610
  * Any exception raised (either synchronously or asynchronously) in `processFrame` will result in the frame being dropped.
7566
7611
  * This callback has the following signature:<br/><br/>
7567
7612
  * <code>processFrame(</code><br/>
7568
- * &nbsp;&nbsp;<code>inputFrameBuffer: OffscreenCanvas,</code><br/>
7613
+ * &nbsp;&nbsp;<code>inputFrameBuffer: OffscreenCanvas | HTMLCanvasElement | HTMLVideoElement,</code><br/>
7569
7614
  * &nbsp;&nbsp;<code>outputFrameBuffer: HTMLCanvasElement</code><br/>
7570
7615
  * <code>): Promise&lt;void&gt; | void;</code>
7571
7616
  *
@@ -7581,6 +7626,30 @@ function dimensionsChanged(track, elem) {
7581
7626
  * }
7582
7627
  * }
7583
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
+ */
7584
7653
  /**
7585
7654
  * The {@link VideoTrack}'s dimensions changed.
7586
7655
  * @param {VideoTrack} track - The {@link VideoTrack} whose dimensions changed
@@ -30083,7 +30152,7 @@ module.exports={
30083
30152
  "name": "twilio-video",
30084
30153
  "title": "Twilio Video",
30085
30154
  "description": "Twilio Video JavaScript Library",
30086
- "version": "2.26.2",
30155
+ "version": "2.27.0",
30087
30156
  "homepage": "https://twilio.com",
30088
30157
  "author": "Mark Andrus Roberts <mroberts@twilio.com>",
30089
30158
  "contributors": [