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 +37 -0
- package/README.md +1 -1
- package/dist/twilio-video.js +102 -33
- package/dist/twilio-video.min.js +19 -19
- 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/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/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
|
@@ -54,6 +54,10 @@ class VideoTrack extends MediaTrack {
|
|
|
54
54
|
value: null,
|
|
55
55
|
writable: true,
|
|
56
56
|
},
|
|
57
|
+
_processorOptions: {
|
|
58
|
+
value: {},
|
|
59
|
+
writable: true,
|
|
60
|
+
},
|
|
57
61
|
_unmuteHandler: {
|
|
58
62
|
value: null,
|
|
59
63
|
writable: true
|
|
@@ -152,27 +156,31 @@ class VideoTrack extends MediaTrack {
|
|
|
152
156
|
const { width = 0, height = 0 } = this.mediaStreamTrack.getSettings();
|
|
153
157
|
// Setting the canvas' dimension triggers a redraw.
|
|
154
158
|
// Only set it if it has changed.
|
|
155
|
-
if (this.
|
|
156
|
-
this.
|
|
157
|
-
this.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
this.
|
|
159
|
+
if (this._outputFrame && this._outputFrame.width !== width) {
|
|
160
|
+
this._outputFrame.width = width;
|
|
161
|
+
this._outputFrame.height = height;
|
|
162
|
+
}
|
|
163
|
+
if (this._inputFrame) {
|
|
164
|
+
if (this._inputFrame.width !== width) {
|
|
165
|
+
this._inputFrame.width = width;
|
|
166
|
+
this._inputFrame.height = height;
|
|
162
167
|
}
|
|
168
|
+
this._inputFrame.getContext('2d').drawImage(this._dummyEl, 0, 0, width, height);
|
|
163
169
|
}
|
|
164
|
-
this._inputFrame.getContext('2d').drawImage(this._dummyEl, 0, 0, width, height);
|
|
165
170
|
|
|
166
171
|
let result = null;
|
|
167
172
|
try {
|
|
168
|
-
|
|
173
|
+
const input = this._processorOptions.inputFrameBufferType === 'video' ? this._dummyEl : this._inputFrame;
|
|
174
|
+
result = this.processor.processFrame(input, this._outputFrame);
|
|
169
175
|
} catch (ex) {
|
|
170
176
|
this._log.debug('Exception detected after calling processFrame.', ex);
|
|
171
177
|
}
|
|
172
178
|
((result instanceof Promise) ? result : Promise.resolve(result))
|
|
173
179
|
.then(() => {
|
|
174
180
|
if (this._outputFrame) {
|
|
175
|
-
this.processedTrack.requestFrame
|
|
181
|
+
if (typeof this.processedTrack.requestFrame === 'function') {
|
|
182
|
+
this.processedTrack.requestFrame();
|
|
183
|
+
}
|
|
176
184
|
this._processorEventObserver.emit('stats');
|
|
177
185
|
}
|
|
178
186
|
})
|
|
@@ -216,8 +224,9 @@ class VideoTrack extends MediaTrack {
|
|
|
216
224
|
_restartProcessor() {
|
|
217
225
|
const processor = this.processor;
|
|
218
226
|
if (processor) {
|
|
227
|
+
const processorOptions = Object.assign({}, this._processorOptions);
|
|
219
228
|
this.removeProcessor(processor);
|
|
220
|
-
this.addProcessor(processor);
|
|
229
|
+
this.addProcessor(processor, processorOptions);
|
|
221
230
|
}
|
|
222
231
|
}
|
|
223
232
|
|
|
@@ -235,8 +244,8 @@ class VideoTrack extends MediaTrack {
|
|
|
235
244
|
|
|
236
245
|
/**
|
|
237
246
|
* Add a {@link VideoProcessor} to allow for custom processing of video frames belonging to a VideoTrack.
|
|
238
|
-
* Only Chrome supports this as of now. Calling this API from a non-supported browser will result in a log warning.
|
|
239
247
|
* @param {VideoProcessor} processor - The {@link VideoProcessor} to use.
|
|
248
|
+
* @param {AddProcessorOptions} [options] - {@link AddProcessorOptions} to provide.
|
|
240
249
|
* @returns {this}
|
|
241
250
|
* @example
|
|
242
251
|
* class GrayScaleProcessor {
|
|
@@ -254,10 +263,7 @@ class VideoTrack extends MediaTrack {
|
|
|
254
263
|
* videoTrack.addProcessor(new GrayScaleProcessor(100));
|
|
255
264
|
* });
|
|
256
265
|
*/
|
|
257
|
-
addProcessor(processor) {
|
|
258
|
-
if (typeof OffscreenCanvas !== 'function') {
|
|
259
|
-
return this._log.warn('Adding a VideoProcessor is not supported in this browser.');
|
|
260
|
-
}
|
|
266
|
+
addProcessor(processor, options) {
|
|
261
267
|
if (!processor || typeof processor.processFrame !== 'function') {
|
|
262
268
|
throw new Error('Received an invalid VideoProcessor from addProcessor.');
|
|
263
269
|
}
|
|
@@ -284,13 +290,51 @@ class VideoTrack extends MediaTrack {
|
|
|
284
290
|
this.mediaStreamTrack.addEventListener('unmute', this._unmuteHandler);
|
|
285
291
|
}
|
|
286
292
|
|
|
293
|
+
this._processorOptions = options || {};
|
|
294
|
+
let { inputFrameBufferType, outputFrameBufferContextType } = this._processorOptions;
|
|
295
|
+
if (typeof OffscreenCanvas === 'undefined' && inputFrameBufferType === 'offscreencanvas') {
|
|
296
|
+
throw new Error('OffscreenCanvas is not supported by this browser.');
|
|
297
|
+
}
|
|
298
|
+
if (inputFrameBufferType && inputFrameBufferType !== 'video' && inputFrameBufferType !== 'canvas' && inputFrameBufferType !== 'offscreencanvas') {
|
|
299
|
+
throw new Error(`Invalid inputFrameBufferType of ${inputFrameBufferType}`);
|
|
300
|
+
}
|
|
301
|
+
if (!inputFrameBufferType) {
|
|
302
|
+
inputFrameBufferType = typeof OffscreenCanvas === 'undefined' ? 'canvas' : 'offscreencanvas';
|
|
303
|
+
}
|
|
304
|
+
|
|
287
305
|
const { width = 0, height = 0, frameRate = DEFAULT_FRAME_RATE } = this.mediaStreamTrack.getSettings();
|
|
288
|
-
|
|
306
|
+
if (inputFrameBufferType === 'offscreencanvas') {
|
|
307
|
+
this._inputFrame = new OffscreenCanvas(width, height);
|
|
308
|
+
}
|
|
309
|
+
if (inputFrameBufferType === 'canvas') {
|
|
310
|
+
this._inputFrame = document.createElement('canvas');
|
|
311
|
+
}
|
|
312
|
+
if (this._inputFrame) {
|
|
313
|
+
this._inputFrame.width = width;
|
|
314
|
+
this._inputFrame.height = height;
|
|
315
|
+
}
|
|
316
|
+
|
|
289
317
|
this._outputFrame = document.createElement('canvas');
|
|
290
318
|
this._outputFrame.width = width;
|
|
291
319
|
this._outputFrame.height = height;
|
|
292
320
|
|
|
293
|
-
|
|
321
|
+
// NOTE(csantos): Initialize the rendering context for future renders. This also ensures
|
|
322
|
+
// that the correct type is used and on Firefox, it throws an exception if you try to capture
|
|
323
|
+
// frames prior calling getContext https://bugzilla.mozilla.org/show_bug.cgi?id=1572422
|
|
324
|
+
outputFrameBufferContextType = outputFrameBufferContextType || '2d';
|
|
325
|
+
const ctx = this._outputFrame.getContext(outputFrameBufferContextType);
|
|
326
|
+
if (!ctx) {
|
|
327
|
+
throw new Error(`Cannot get outputFrameBufferContextType: ${outputFrameBufferContextType}.`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// NOTE(csantos): Zero FPS means we can control when to render the next frame by calling requestFrame.
|
|
331
|
+
// Some browsers such as Firefox doesn't support requestFrame so we will use default, which is an undefined value.
|
|
332
|
+
// This means, the browser will use the highest FPS available.
|
|
333
|
+
const targetFps = typeof CanvasCaptureMediaStreamTrack !== 'undefined' && CanvasCaptureMediaStreamTrack.prototype &&
|
|
334
|
+
// eslint-disable-next-line
|
|
335
|
+
typeof CanvasCaptureMediaStreamTrack.prototype.requestFrame === 'function' ? 0 : undefined;
|
|
336
|
+
|
|
337
|
+
this.processedTrack = this._outputFrame.captureStream(targetFps).getTracks()[0];
|
|
294
338
|
this.processedTrack.enabled = this.mediaStreamTrack.enabled;
|
|
295
339
|
this.processor = processor;
|
|
296
340
|
|
|
@@ -299,7 +343,9 @@ class VideoTrack extends MediaTrack {
|
|
|
299
343
|
captureHeight: height,
|
|
300
344
|
captureWidth: width,
|
|
301
345
|
inputFrameRate: frameRate,
|
|
302
|
-
isRemoteVideoTrack: this.toString().includes('RemoteVideoTrack')
|
|
346
|
+
isRemoteVideoTrack: this.toString().includes('RemoteVideoTrack'),
|
|
347
|
+
inputFrameBufferType,
|
|
348
|
+
outputFrameBufferContextType
|
|
303
349
|
});
|
|
304
350
|
this._updateElementsMediaStreamTrack();
|
|
305
351
|
this._captureFrames();
|
|
@@ -440,13 +486,12 @@ class VideoTrack extends MediaTrack {
|
|
|
440
486
|
this._log.debug('Removing VideoProcessor from the VideoTrack', processor);
|
|
441
487
|
clearTimeout(this._captureTimeoutId);
|
|
442
488
|
this.mediaStreamTrack.removeEventListener('unmute', this._unmuteHandler);
|
|
489
|
+
this._processorOptions = {};
|
|
443
490
|
this._unmuteHandler = null;
|
|
444
491
|
this._isCapturing = false;
|
|
445
492
|
|
|
446
493
|
this.processor = null;
|
|
447
494
|
this.processedTrack = null;
|
|
448
|
-
this._inputFrame.getContext('2d').clearRect(0, 0, this._inputFrame.width, this._inputFrame.height);
|
|
449
|
-
this._outputFrame.getContext('2d').clearRect(0, 0, this._outputFrame.width, this._outputFrame.height);
|
|
450
495
|
this._inputFrame = null;
|
|
451
496
|
this._outputFrame = null;
|
|
452
497
|
|
|
@@ -484,7 +529,7 @@ function dimensionsChanged(track, elem) {
|
|
|
484
529
|
* Any exception raised (either synchronously or asynchronously) in `processFrame` will result in the frame being dropped.
|
|
485
530
|
* This callback has the following signature:<br/><br/>
|
|
486
531
|
* <code>processFrame(</code><br/>
|
|
487
|
-
* <code>inputFrameBuffer: OffscreenCanvas,</code><br/>
|
|
532
|
+
* <code>inputFrameBuffer: OffscreenCanvas | HTMLCanvasElement | HTMLVideoElement,</code><br/>
|
|
488
533
|
* <code>outputFrameBuffer: HTMLCanvasElement</code><br/>
|
|
489
534
|
* <code>): Promise<void> | void;</code>
|
|
490
535
|
*
|
|
@@ -501,6 +546,31 @@ function dimensionsChanged(track, elem) {
|
|
|
501
546
|
* }
|
|
502
547
|
*/
|
|
503
548
|
|
|
549
|
+
/**
|
|
550
|
+
* Possible options to provide to {@link LocalVideoTrack#addProcessor} and {@link RemoteVideoTrack#addProcessor}.
|
|
551
|
+
* @typedef {object} AddProcessorOptions
|
|
552
|
+
* @property {string} [inputFrameBufferType="offscreencanvas"] - This option allows you to specify what kind of input you want to receive in your
|
|
553
|
+
* Video Processor. The default is `offscreencanvas` and will fallback to a regular `canvas` if the browser does not support it.
|
|
554
|
+
* Possible values include the following.
|
|
555
|
+
* <br/>
|
|
556
|
+
* <br/>
|
|
557
|
+
* `offscreencanvas` - Your Video Processor will receive an [OffscreenCanvas](https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas)
|
|
558
|
+
* which is good for canvas-related processing that can be rendered off screen.
|
|
559
|
+
* <br/>
|
|
560
|
+
* <br/>
|
|
561
|
+
* `canvas` - Your Video Processor will receive an [HTMLCanvasElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement).
|
|
562
|
+
* This is recommended on browsers that doesn't support `OffscreenCanvas`, or if you need to render the frame on the screen.
|
|
563
|
+
* <br/>
|
|
564
|
+
* <br/>
|
|
565
|
+
* `video` - Your Video Processor will receive an [HTMLVideoElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement).
|
|
566
|
+
* 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)
|
|
567
|
+
* the frame directly to your output canvas.
|
|
568
|
+
* @property {string} [outputFrameBufferContextType="2d"] - The SDK needs the [context type](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext)
|
|
569
|
+
* that your Video Processor uses in order to properly generate the processed track. For example, if your Video Processor uses WebGL2 (`canvas.getContext('webgl2')`),
|
|
570
|
+
* you should set `outputFrameBufferContextType` to `webgl2`. Or if you're using Canvas 2D processing (`canvas.getContext('2d')`),
|
|
571
|
+
* you should set `outputFrameBufferContextType` to `2d`.
|
|
572
|
+
*/
|
|
573
|
+
|
|
504
574
|
/**
|
|
505
575
|
* The {@link VideoTrack}'s dimensions changed.
|
|
506
576
|
* @param {VideoTrack} track - The {@link VideoTrack} whose dimensions changed
|
package/package.json
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export class VideoProcessor {
|
|
2
|
-
processFrame(
|
|
2
|
+
processFrame(
|
|
3
|
+
inputFrameBuffer: OffscreenCanvas | HTMLCanvasElement | HTMLVideoElement,
|
|
4
|
+
outputFrameBuffer: HTMLCanvasElement
|
|
5
|
+
): Promise<void> | void;
|
|
3
6
|
}
|
package/tsdef/VideoTrack.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AddProcessorOptions } from './types';
|
|
1
2
|
import { MediaTrack } from './MediaTrack';
|
|
2
3
|
import { VideoProcessor } from './VideoProcessor';
|
|
3
4
|
|
|
@@ -14,6 +15,6 @@ export class VideoTrack extends MediaTrack {
|
|
|
14
15
|
processor: VideoProcessor | null;
|
|
15
16
|
processedTrack: MediaStreamTrack | null;
|
|
16
17
|
|
|
17
|
-
addProcessor(processor: VideoProcessor): this;
|
|
18
|
+
addProcessor(processor: VideoProcessor, options?: AddProcessorOptions): this;
|
|
18
19
|
removeProcessor(processor: VideoProcessor): this;
|
|
19
20
|
}
|
package/tsdef/index.d.ts
CHANGED
package/tsdef/types.d.ts
CHANGED
|
@@ -21,6 +21,11 @@ export type AudioTrackPublication = LocalAudioTrackPublication | RemoteAudioTrac
|
|
|
21
21
|
export type DataTrackPublication = LocalDataTrackPublication | RemoteDataTrackPublication;
|
|
22
22
|
export type VideoTrackPublication = LocalVideoTrackPublication | RemoteVideoTrackPublication;
|
|
23
23
|
|
|
24
|
+
export interface AddProcessorOptions {
|
|
25
|
+
inputFrameBufferType?: 'video' | 'canvas' | 'offscreencanvas';
|
|
26
|
+
outputFrameBufferContextType?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
24
29
|
export interface EncodingParameters {
|
|
25
30
|
maxAudioBitrate?: number | null;
|
|
26
31
|
maxVideoBitrate?: number | null;
|