@livekit/track-processors 0.1.4 → 0.1.5
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/dist/index.js +279 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +279 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +5 -3
- package/dist/package.json +0 -56
- package/dist/src/ProcessorPipeline.js +0 -41
- package/dist/src/ProcessorPipeline.js.map +0 -1
- package/dist/src/index.js +0 -28
- package/dist/src/index.js.map +0 -1
- package/dist/src/transformers/BackgroundTransformer.js +0 -142
- package/dist/src/transformers/BackgroundTransformer.js.map +0 -1
- package/dist/src/transformers/DummyTransformer.js +0 -10
- package/dist/src/transformers/DummyTransformer.js.map +0 -1
- package/dist/src/transformers/MediaPipeHolisticTrackerTransformer.js +0 -44
- package/dist/src/transformers/MediaPipeHolisticTrackerTransformer.js.map +0 -1
- package/dist/src/transformers/VideoTransformer.js +0 -26
- package/dist/src/transformers/VideoTransformer.js.map +0 -1
- package/dist/src/transformers/index.js +0 -3
- package/dist/src/transformers/index.js.map +0 -1
- package/dist/src/transformers/types.js +0 -2
- package/dist/src/transformers/types.js.map +0 -1
- package/dist/src/utils.js +0 -3
- package/dist/src/utils.js.map +0 -1
package/dist/index.js
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } }// src/ProcessorPipeline.ts
|
|
2
|
+
var ProcessorPipeline = class {
|
|
3
|
+
static get isSupported() {
|
|
4
|
+
return typeof MediaStreamTrackGenerator !== "undefined" && typeof MediaStreamTrackProcessor !== "undefined";
|
|
5
|
+
}
|
|
6
|
+
constructor(transformers, name) {
|
|
7
|
+
this.name = name;
|
|
8
|
+
this.transformers = transformers;
|
|
9
|
+
}
|
|
10
|
+
async init(opts) {
|
|
11
|
+
var _a, _b;
|
|
12
|
+
this.source = opts.track;
|
|
13
|
+
this.sourceSettings = this.source.getSettings();
|
|
14
|
+
this.sourceDummy = opts.element;
|
|
15
|
+
if (!(this.sourceDummy instanceof HTMLVideoElement)) {
|
|
16
|
+
throw TypeError("Currently only video transformers are supported");
|
|
17
|
+
}
|
|
18
|
+
this.processor = new MediaStreamTrackProcessor({ track: this.source });
|
|
19
|
+
this.trackGenerator = new MediaStreamTrackGenerator({ kind: "video" });
|
|
20
|
+
this.canvas = new OffscreenCanvas(
|
|
21
|
+
(_a = this.sourceSettings.width) != null ? _a : 300,
|
|
22
|
+
(_b = this.sourceSettings.height) != null ? _b : 300
|
|
23
|
+
);
|
|
24
|
+
let readableStream = this.processor.readable;
|
|
25
|
+
for (const transformer of this.transformers) {
|
|
26
|
+
transformer.init({
|
|
27
|
+
outputCanvas: this.canvas,
|
|
28
|
+
inputElement: this.sourceDummy
|
|
29
|
+
});
|
|
30
|
+
readableStream = readableStream.pipeThrough(transformer.transformer);
|
|
31
|
+
}
|
|
32
|
+
readableStream.pipeTo(this.trackGenerator.writable);
|
|
33
|
+
this.processedTrack = this.trackGenerator;
|
|
34
|
+
}
|
|
35
|
+
async destroy() {
|
|
36
|
+
var _a;
|
|
37
|
+
for (const transformer of this.transformers) {
|
|
38
|
+
await transformer.destroy();
|
|
39
|
+
}
|
|
40
|
+
(_a = this.trackGenerator) == null ? void 0 : _a.stop();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// src/transformers/BackgroundTransformer.ts
|
|
45
|
+
var _tasksvision = require('@mediapipe/tasks-vision'); var vision = _interopRequireWildcard(_tasksvision);
|
|
46
|
+
|
|
47
|
+
// package.json
|
|
48
|
+
var dependencies = {
|
|
49
|
+
"@mediapipe/holistic": "0.5.1675471629",
|
|
50
|
+
"@mediapipe/tasks-vision": "0.10.1"
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// src/transformers/VideoTransformer.ts
|
|
54
|
+
var VideoTransformer = class {
|
|
55
|
+
constructor() {
|
|
56
|
+
this.isDisabled = false;
|
|
57
|
+
}
|
|
58
|
+
init({ outputCanvas, inputElement: inputVideo }) {
|
|
59
|
+
var _a;
|
|
60
|
+
if (!(inputVideo instanceof HTMLVideoElement)) {
|
|
61
|
+
throw TypeError("Video transformer needs a HTMLVideoElement as input");
|
|
62
|
+
}
|
|
63
|
+
this.transformer = new TransformStream({
|
|
64
|
+
transform: (frame, controller) => this.transform(frame, controller)
|
|
65
|
+
});
|
|
66
|
+
this.canvas = outputCanvas || null;
|
|
67
|
+
if (outputCanvas) {
|
|
68
|
+
this.ctx = ((_a = this.canvas) == null ? void 0 : _a.getContext("2d", { readFrequently: true })) || void 0;
|
|
69
|
+
}
|
|
70
|
+
this.inputVideo = inputVideo;
|
|
71
|
+
this.isDisabled = false;
|
|
72
|
+
}
|
|
73
|
+
async destroy() {
|
|
74
|
+
this.isDisabled = true;
|
|
75
|
+
this.canvas = void 0;
|
|
76
|
+
this.ctx = void 0;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// src/transformers/BackgroundTransformer.ts
|
|
81
|
+
var BackgroundProcessor = class extends VideoTransformer {
|
|
82
|
+
constructor(opts) {
|
|
83
|
+
super();
|
|
84
|
+
this.backgroundImage = null;
|
|
85
|
+
if (opts.blurRadius) {
|
|
86
|
+
this.blurRadius = opts.blurRadius;
|
|
87
|
+
} else if (opts.imagePath) {
|
|
88
|
+
this.loadBackground(opts.imagePath);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
static get isSupported() {
|
|
92
|
+
return typeof OffscreenCanvas !== "undefined";
|
|
93
|
+
}
|
|
94
|
+
async init({ outputCanvas, inputElement: inputVideo }) {
|
|
95
|
+
super.init({ outputCanvas, inputElement: inputVideo });
|
|
96
|
+
const fileSet = await vision.FilesetResolver.forVisionTasks(
|
|
97
|
+
`https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@${dependencies["@mediapipe/tasks-vision"]}/wasm`
|
|
98
|
+
);
|
|
99
|
+
this.imageSegmenter = await vision.ImageSegmenter.createFromOptions(fileSet, {
|
|
100
|
+
baseOptions: {
|
|
101
|
+
modelAssetPath: "https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_segmenter/float16/latest/selfie_segmenter.tflite",
|
|
102
|
+
delegate: "CPU"
|
|
103
|
+
},
|
|
104
|
+
runningMode: "VIDEO",
|
|
105
|
+
outputCategoryMask: true,
|
|
106
|
+
outputConfidenceMasks: false
|
|
107
|
+
});
|
|
108
|
+
this.sendFramesContinuouslyForSegmentation(this.inputVideo);
|
|
109
|
+
}
|
|
110
|
+
async destroy() {
|
|
111
|
+
var _a;
|
|
112
|
+
await super.destroy();
|
|
113
|
+
await ((_a = this.imageSegmenter) == null ? void 0 : _a.close());
|
|
114
|
+
this.backgroundImage = null;
|
|
115
|
+
}
|
|
116
|
+
async sendFramesContinuouslyForSegmentation(videoEl) {
|
|
117
|
+
var _a;
|
|
118
|
+
if (!this.isDisabled) {
|
|
119
|
+
if (videoEl.videoWidth > 0 && videoEl.videoHeight > 0) {
|
|
120
|
+
let startTimeMs = performance.now();
|
|
121
|
+
(_a = this.imageSegmenter) == null ? void 0 : _a.segmentForVideo(
|
|
122
|
+
videoEl,
|
|
123
|
+
startTimeMs,
|
|
124
|
+
(result) => this.segmentationResults = result
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
videoEl.requestVideoFrameCallback(() => {
|
|
128
|
+
this.sendFramesContinuouslyForSegmentation(videoEl);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async loadBackground(path) {
|
|
133
|
+
const img = new Image();
|
|
134
|
+
await new Promise((resolve, reject) => {
|
|
135
|
+
img.crossOrigin = "Anonymous";
|
|
136
|
+
img.onload = () => resolve(img);
|
|
137
|
+
img.onerror = (err) => reject(err);
|
|
138
|
+
img.src = path;
|
|
139
|
+
});
|
|
140
|
+
const imageData = await createImageBitmap(img);
|
|
141
|
+
this.backgroundImage = imageData;
|
|
142
|
+
}
|
|
143
|
+
async transform(frame, controller) {
|
|
144
|
+
if (this.isDisabled) {
|
|
145
|
+
controller.enqueue(frame);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (!this.canvas) {
|
|
149
|
+
throw TypeError("Canvas needs to be initialized first");
|
|
150
|
+
}
|
|
151
|
+
if (this.blurRadius) {
|
|
152
|
+
await this.blurBackground(frame);
|
|
153
|
+
} else {
|
|
154
|
+
await this.drawVirtualBackground(frame);
|
|
155
|
+
}
|
|
156
|
+
const newFrame = new VideoFrame(this.canvas, {
|
|
157
|
+
timestamp: frame.timestamp || Date.now()
|
|
158
|
+
});
|
|
159
|
+
frame.close();
|
|
160
|
+
controller.enqueue(newFrame);
|
|
161
|
+
}
|
|
162
|
+
async drawVirtualBackground(frame) {
|
|
163
|
+
var _a;
|
|
164
|
+
if (!this.canvas || !this.ctx || !this.segmentationResults || !this.inputVideo)
|
|
165
|
+
return;
|
|
166
|
+
if ((_a = this.segmentationResults) == null ? void 0 : _a.categoryMask) {
|
|
167
|
+
this.ctx.filter = "blur(3px)";
|
|
168
|
+
this.ctx.globalCompositeOperation = "copy";
|
|
169
|
+
const bitmap = await maskToBitmap(
|
|
170
|
+
this.segmentationResults.categoryMask,
|
|
171
|
+
this.inputVideo.videoWidth,
|
|
172
|
+
this.inputVideo.videoHeight
|
|
173
|
+
);
|
|
174
|
+
this.ctx.drawImage(bitmap, 0, 0);
|
|
175
|
+
this.ctx.filter = "none";
|
|
176
|
+
this.ctx.globalCompositeOperation = "source-in";
|
|
177
|
+
if (this.backgroundImage) {
|
|
178
|
+
this.ctx.drawImage(
|
|
179
|
+
this.backgroundImage,
|
|
180
|
+
0,
|
|
181
|
+
0,
|
|
182
|
+
this.backgroundImage.width,
|
|
183
|
+
this.backgroundImage.height,
|
|
184
|
+
0,
|
|
185
|
+
0,
|
|
186
|
+
this.canvas.width,
|
|
187
|
+
this.canvas.height
|
|
188
|
+
);
|
|
189
|
+
} else {
|
|
190
|
+
this.ctx.fillStyle = "#00FF00";
|
|
191
|
+
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
192
|
+
}
|
|
193
|
+
this.ctx.globalCompositeOperation = "destination-over";
|
|
194
|
+
}
|
|
195
|
+
this.ctx.drawImage(frame, 0, 0, this.canvas.width, this.canvas.height);
|
|
196
|
+
}
|
|
197
|
+
async blurBackground(frame) {
|
|
198
|
+
var _a, _b;
|
|
199
|
+
if (!this.ctx || !this.canvas || !((_b = (_a = this.segmentationResults) == null ? void 0 : _a.categoryMask) == null ? void 0 : _b.canvas) || !this.inputVideo) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
this.ctx.save();
|
|
203
|
+
this.ctx.globalCompositeOperation = "copy";
|
|
204
|
+
const bitmap = await maskToBitmap(
|
|
205
|
+
this.segmentationResults.categoryMask,
|
|
206
|
+
this.inputVideo.videoWidth,
|
|
207
|
+
this.inputVideo.videoHeight
|
|
208
|
+
);
|
|
209
|
+
this.ctx.filter = "blur(3px)";
|
|
210
|
+
this.ctx.globalCompositeOperation = "copy";
|
|
211
|
+
this.ctx.drawImage(bitmap, 0, 0);
|
|
212
|
+
this.ctx.filter = "none";
|
|
213
|
+
this.ctx.globalCompositeOperation = "source-out";
|
|
214
|
+
this.ctx.drawImage(frame, 0, 0, this.canvas.width, this.canvas.height);
|
|
215
|
+
this.ctx.globalCompositeOperation = "destination-over";
|
|
216
|
+
this.ctx.filter = `blur(${this.blurRadius}px)`;
|
|
217
|
+
this.ctx.drawImage(frame, 0, 0);
|
|
218
|
+
this.ctx.restore();
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
function maskToBitmap(mask, videoWidth, videoHeight) {
|
|
222
|
+
const dataArray = new Uint8ClampedArray(videoWidth * videoHeight * 4);
|
|
223
|
+
const result = mask.getAsUint8Array();
|
|
224
|
+
for (let i = 0; i < result.length; i += 1) {
|
|
225
|
+
dataArray[i * 4] = result[i];
|
|
226
|
+
dataArray[i * 4 + 1] = result[i];
|
|
227
|
+
dataArray[i * 4 + 2] = result[i];
|
|
228
|
+
dataArray[i * 4 + 3] = result[i];
|
|
229
|
+
}
|
|
230
|
+
const dataNew = new ImageData(dataArray, videoWidth, videoHeight);
|
|
231
|
+
return createImageBitmap(dataNew);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// src/transformers/DummyTransformer.ts
|
|
235
|
+
var DummyTransformer = class extends VideoTransformer {
|
|
236
|
+
async transform(frame, controller) {
|
|
237
|
+
controller.enqueue(frame);
|
|
238
|
+
}
|
|
239
|
+
async destroy() {
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// src/index.ts
|
|
244
|
+
var BackgroundBlur = (blurRadius = 10) => {
|
|
245
|
+
const isPipelineSupported = ProcessorPipeline.isSupported && BackgroundProcessor.isSupported;
|
|
246
|
+
if (!isPipelineSupported) {
|
|
247
|
+
throw new Error("pipeline is not supported in this browser");
|
|
248
|
+
}
|
|
249
|
+
const pipeline = new ProcessorPipeline(
|
|
250
|
+
[new BackgroundProcessor({ blurRadius })],
|
|
251
|
+
"background-blur"
|
|
252
|
+
);
|
|
253
|
+
return pipeline;
|
|
254
|
+
};
|
|
255
|
+
var VirtualBackground = (imagePath) => {
|
|
256
|
+
const isPipelineSupported = ProcessorPipeline.isSupported && BackgroundProcessor.isSupported;
|
|
257
|
+
if (!isPipelineSupported) {
|
|
258
|
+
throw new Error("pipeline is not supported in this browser");
|
|
259
|
+
}
|
|
260
|
+
const pipeline = new ProcessorPipeline(
|
|
261
|
+
[new BackgroundProcessor({ imagePath })],
|
|
262
|
+
"virtual-background"
|
|
263
|
+
);
|
|
264
|
+
return pipeline;
|
|
265
|
+
};
|
|
266
|
+
var Dummy = () => {
|
|
267
|
+
const isPipelineSupported = ProcessorPipeline.isSupported && BackgroundProcessor.isSupported;
|
|
268
|
+
if (!isPipelineSupported) {
|
|
269
|
+
throw new Error("pipeline is not supported in this browser");
|
|
270
|
+
}
|
|
271
|
+
const pipeline = new ProcessorPipeline([new DummyTransformer()], "dummy");
|
|
272
|
+
return pipeline;
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
exports.BackgroundBlur = BackgroundBlur; exports.Dummy = Dummy; exports.VirtualBackground = VirtualBackground;
|
|
279
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ProcessorPipeline.ts","../src/transformers/BackgroundTransformer.ts","../package.json","../src/transformers/VideoTransformer.ts","../src/transformers/DummyTransformer.ts","../src/index.ts"],"names":[],"mappings":";AAGA,IAAqB,oBAArB,MAA6E;AAAA,EAC3E,WAAW,cAAc;AACvB,WACE,OAAO,8BAA8B,eACrC,OAAO,8BAA8B;AAAA,EAEzC;AAAA,EAoBA,YAAY,cAA4C,MAAc;AACpE,SAAK,OAAO;AACZ,SAAK,eAAe;AAAA,EACtB;AAAA,EAEA,MAAM,KAAK,MAAoC;AAlCjD;AAmCI,SAAK,SAAS,KAAK;AACnB,SAAK,iBAAiB,KAAK,OAAO,YAAY;AAC9C,SAAK,cAAc,KAAK;AACxB,QAAI,EAAE,KAAK,uBAAuB,mBAAmB;AACnD,YAAM,UAAU,iDAAiD;AAAA,IACnE;AAEA,SAAK,YAAY,IAAI,0BAA0B,EAAE,OAAO,KAAK,OAAO,CAAC;AACrE,SAAK,iBAAiB,IAAI,0BAA0B,EAAE,MAAM,QAAQ,CAAC;AAErE,SAAK,SAAS,IAAI;AAAA,OAChB,UAAK,eAAe,UAApB,YAA6B;AAAA,OAC7B,UAAK,eAAe,WAApB,YAA8B;AAAA,IAChC;AAEA,QAAI,iBAAiB,KAAK,UAAU;AACpC,eAAW,eAAe,KAAK,cAAc;AAC3C,kBAAY,KAAK;AAAA,QACf,cAAc,KAAK;AAAA,QACnB,cAAc,KAAK;AAAA,MACrB,CAAC;AACD,uBAAiB,eAAe,YAAY,YAAa,WAAY;AAAA,IACvE;AACA,mBAAe,OAAO,KAAK,eAAe,QAAQ;AAClD,SAAK,iBAAiB,KAAK;AAAA,EAC7B;AAAA,EAEA,MAAM,UAAU;AA9DlB;AA+DI,eAAW,eAAe,KAAK,cAAc;AAC3C,YAAM,YAAY,QAAQ;AAAA,IAC5B;AACA,eAAK,mBAAL,mBAAqB;AAAA,EACvB;AACF;;;ACpEA,YAAY,YAAY;;;ACuBtB,mBAAgB;AAAA,EACd,uBAAuB;AAAA,EACvB,2BAA2B;AAC7B;;;ACxBF,IAA8B,mBAA9B,MAAgF;AAAA,EAAhF;AASE,SAAU,aAAuB;AAAA;AAAA,EAEjC,KAAK,EAAE,cAAc,cAAc,WAAW,GAAsC;AAbtF;AAcI,QAAI,EAAE,sBAAsB,mBAAmB;AAC7C,YAAM,UAAU,qDAAqD;AAAA,IACvE;AACA,SAAK,cAAc,IAAI,gBAAgB;AAAA,MACrC,WAAW,CAAC,OAAO,eAAe,KAAK,UAAU,OAAO,UAAU;AAAA,IACpE,CAAC;AACD,SAAK,SAAS,gBAAgB;AAC9B,QAAI,cAAc;AAChB,WAAK,QAAM,UAAK,WAAL,mBAAa,WAAW,MAAM,EAAE,gBAAgB,KAAK,OAAM;AAAA,IACxE;AACA,SAAK,aAAa;AAClB,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,MAAM,UAAU;AACd,SAAK,aAAa;AAClB,SAAK,SAAS;AACd,SAAK,MAAM;AAAA,EACb;AAMF;;;AF5BA,IAAqB,sBAArB,cAAiD,iBAAiB;AAAA,EAahE,YAAY,MAAyB;AACnC,UAAM;AALR,2BAAsC;AAMpC,QAAI,KAAK,YAAY;AACnB,WAAK,aAAa,KAAK;AAAA,IACzB,WAAW,KAAK,WAAW;AACzB,WAAK,eAAe,KAAK,SAAS;AAAA,IACpC;AAAA,EACF;AAAA,EAnBA,WAAW,cAAc;AACvB,WAAO,OAAO,oBAAoB;AAAA,EACpC;AAAA,EAmBA,MAAM,KAAK,EAAE,cAAc,cAAc,WAAW,GAAgC;AAClF,UAAM,KAAK,EAAE,cAAc,cAAc,WAAW,CAAC;AAErD,UAAM,UAAU,MAAa,uBAAgB;AAAA,MAC3C,wDAAwD,aAAa,yBAAyB,CAAC;AAAA,IACjG;AAEA,SAAK,iBAAiB,MAAa,sBAAe,kBAAkB,SAAS;AAAA,MAC3E,aAAa;AAAA,QACX,gBACE;AAAA,QACF,UAAU;AAAA,MACZ;AAAA,MACA,aAAa;AAAA,MACb,oBAAoB;AAAA,MACpB,uBAAuB;AAAA,IACzB,CAAC;AAGD,SAAK,sCAAsC,KAAK,UAAW;AAAA,EAC7D;AAAA,EAEA,MAAM,UAAU;AAtDlB;AAuDI,UAAM,MAAM,QAAQ;AACpB,YAAM,UAAK,mBAAL,mBAAqB;AAC3B,SAAK,kBAAkB;AAAA,EACzB;AAAA,EAEA,MAAM,sCAAsC,SAA2B;AA5DzE;AA6DI,QAAI,CAAC,KAAK,YAAY;AACpB,UAAI,QAAQ,aAAa,KAAK,QAAQ,cAAc,GAAG;AACrD,YAAI,cAAc,YAAY,IAAI;AAClC,mBAAK,mBAAL,mBAAqB;AAAA,UACnB;AAAA,UACA;AAAA,UACA,CAAC,WAAY,KAAK,sBAAsB;AAAA;AAAA,MAE5C;AACA,cAAQ,0BAA0B,MAAM;AACtC,aAAK,sCAAsC,OAAO;AAAA,MACpD,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,eAAe,MAAc;AACjC,UAAM,MAAM,IAAI,MAAM;AAEtB,UAAM,IAAI,QAAQ,CAAC,SAAS,WAAW;AACrC,UAAI,cAAc;AAClB,UAAI,SAAS,MAAM,QAAQ,GAAG;AAC9B,UAAI,UAAU,CAAC,QAAQ,OAAO,GAAG;AACjC,UAAI,MAAM;AAAA,IACZ,CAAC;AACD,UAAM,YAAY,MAAM,kBAAkB,GAAG;AAC7C,SAAK,kBAAkB;AAAA,EACzB;AAAA,EAEA,MAAM,UAAU,OAAmB,YAA0D;AAC3F,QAAI,KAAK,YAAY;AACnB,iBAAW,QAAQ,KAAK;AACxB;AAAA,IACF;AACA,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,UAAU,sCAAsC;AAAA,IACxD;AACA,QAAI,KAAK,YAAY;AACnB,YAAM,KAAK,eAAe,KAAK;AAAA,IACjC,OAAO;AACL,YAAM,KAAK,sBAAsB,KAAK;AAAA,IACxC;AACA,UAAM,WAAW,IAAI,WAAW,KAAK,QAAQ;AAAA,MAC3C,WAAW,MAAM,aAAa,KAAK,IAAI;AAAA,IACzC,CAAC;AACD,UAAM,MAAM;AACZ,eAAW,QAAQ,QAAQ;AAAA,EAC7B;AAAA,EAEA,MAAM,sBAAsB,OAAmB;AA7GjD;AA8GI,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,OAAO,CAAC,KAAK,uBAAuB,CAAC,KAAK;AAAY;AAGhF,SAAI,UAAK,wBAAL,mBAA0B,cAAc;AAC1C,WAAK,IAAI,SAAS;AAClB,WAAK,IAAI,2BAA2B;AACpC,YAAM,SAAS,MAAM;AAAA,QACnB,KAAK,oBAAoB;AAAA,QACzB,KAAK,WAAW;AAAA,QAChB,KAAK,WAAW;AAAA,MAClB;AACA,WAAK,IAAI,UAAU,QAAQ,GAAG,CAAC;AAC/B,WAAK,IAAI,SAAS;AAClB,WAAK,IAAI,2BAA2B;AACpC,UAAI,KAAK,iBAAiB;AACxB,aAAK,IAAI;AAAA,UACP,KAAK;AAAA,UACL;AAAA,UACA;AAAA,UACA,KAAK,gBAAgB;AAAA,UACrB,KAAK,gBAAgB;AAAA,UACrB;AAAA,UACA;AAAA,UACA,KAAK,OAAO;AAAA,UACZ,KAAK,OAAO;AAAA,QACd;AAAA,MACF,OAAO;AACL,aAAK,IAAI,YAAY;AACrB,aAAK,IAAI,SAAS,GAAG,GAAG,KAAK,OAAO,OAAO,KAAK,OAAO,MAAM;AAAA,MAC/D;AAEA,WAAK,IAAI,2BAA2B;AAAA,IACtC;AACA,SAAK,IAAI,UAAU,OAAO,GAAG,GAAG,KAAK,OAAO,OAAO,KAAK,OAAO,MAAM;AAAA,EAEvE;AAAA,EAEA,MAAM,eAAe,OAAmB;AAnJ1C;AAoJI,QACE,CAAC,KAAK,OACN,CAAC,KAAK,UACN,GAAC,gBAAK,wBAAL,mBAA0B,iBAA1B,mBAAwC,WACzC,CAAC,KAAK,YACN;AACA;AAAA,IACF;AACA,SAAK,IAAI,KAAK;AACd,SAAK,IAAI,2BAA2B;AAEpC,UAAM,SAAS,MAAM;AAAA,MACnB,KAAK,oBAAoB;AAAA,MACzB,KAAK,WAAW;AAAA,MAChB,KAAK,WAAW;AAAA,IAClB;AACA,SAAK,IAAI,SAAS;AAClB,SAAK,IAAI,2BAA2B;AACpC,SAAK,IAAI,UAAU,QAAQ,GAAG,CAAC;AAC/B,SAAK,IAAI,SAAS;AAClB,SAAK,IAAI,2BAA2B;AACpC,SAAK,IAAI,UAAU,OAAO,GAAG,GAAG,KAAK,OAAO,OAAO,KAAK,OAAO,MAAM;AACrE,SAAK,IAAI,2BAA2B;AACpC,SAAK,IAAI,SAAS,QAAQ,KAAK,UAAU;AACzC,SAAK,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9B,SAAK,IAAI,QAAQ;AAAA,EACnB;AACF;AAEA,SAAS,aACP,MACA,YACA,aACsB;AACtB,QAAM,YAA+B,IAAI,kBAAkB,aAAa,cAAc,CAAC;AACvF,QAAM,SAAS,KAAK,gBAAgB;AACpC,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,GAAG;AACzC,cAAU,IAAI,CAAC,IAAI,OAAO,CAAC;AAC3B,cAAU,IAAI,IAAI,CAAC,IAAI,OAAO,CAAC;AAC/B,cAAU,IAAI,IAAI,CAAC,IAAI,OAAO,CAAC;AAC/B,cAAU,IAAI,IAAI,CAAC,IAAI,OAAO,CAAC;AAAA,EACjC;AACA,QAAM,UAAU,IAAI,UAAU,WAAW,YAAY,WAAW;AAEhE,SAAO,kBAAkB,OAAO;AAClC;;;AG/LA,IAAqB,mBAArB,cAA8C,iBAAiB;AAAA,EAC7D,MAAM,UAAU,OAAmB,YAA0D;AAC3F,eAAW,QAAQ,KAAK;AAAA,EAC1B;AAAA,EAEA,MAAM,UAAU;AAAA,EAEhB;AACF;;;ACNO,IAAM,iBAAiB,CAAC,aAAqB,OAAO;AACzD,QAAM,sBAAsB,kBAAkB,eAAe,oBAAsB;AACnF,MAAI,CAAC,qBAAqB;AACxB,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACA,QAAM,WAAW,IAAI;AAAA,IACnB,CAAC,IAAI,oBAAsB,EAAE,WAAW,CAAC,CAAC;AAAA,IAC1C;AAAA,EACF;AACA,SAAO;AACT;AAEO,IAAM,oBAAoB,CAAC,cAAsB;AACtD,QAAM,sBAAsB,kBAAkB,eAAe,oBAAsB;AACnF,MAAI,CAAC,qBAAqB;AACxB,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACA,QAAM,WAAW,IAAI;AAAA,IACnB,CAAC,IAAI,oBAAsB,EAAE,UAAU,CAAC,CAAC;AAAA,IACzC;AAAA,EACF;AACA,SAAO;AACT;AAEO,IAAM,QAAQ,MAAM;AACzB,QAAM,sBAAsB,kBAAkB,eAAe,oBAAsB;AACnF,MAAI,CAAC,qBAAqB;AACxB,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACA,QAAM,WAAW,IAAI,kBAAkB,CAAC,IAAI,iBAAiB,CAAC,GAAG,OAAO;AACxE,SAAO;AACT","sourcesContent":["import type { ProcessorOptions, Track, TrackProcessor } from 'livekit-client';\nimport { VideoTrackTransformer } from './transformers';\n\nexport default class ProcessorPipeline implements TrackProcessor<Track.Kind> {\n static get isSupported() {\n return (\n typeof MediaStreamTrackGenerator !== 'undefined' &&\n typeof MediaStreamTrackProcessor !== 'undefined'\n );\n }\n\n name: string;\n\n source?: MediaStreamVideoTrack;\n\n sourceSettings?: MediaTrackSettings;\n\n processor?: MediaStreamTrackProcessor<VideoFrame>;\n\n trackGenerator?: MediaStreamTrackGenerator<VideoFrame>;\n\n canvas?: OffscreenCanvas;\n\n sourceDummy?: HTMLMediaElement;\n\n processedTrack?: MediaStreamTrack;\n\n transformers: Array<VideoTrackTransformer>;\n\n constructor(transformers: Array<VideoTrackTransformer>, name: string) {\n this.name = name;\n this.transformers = transformers;\n }\n\n async init(opts: ProcessorOptions<Track.Kind>) {\n this.source = opts.track as MediaStreamVideoTrack;\n this.sourceSettings = this.source.getSettings();\n this.sourceDummy = opts.element;\n if (!(this.sourceDummy instanceof HTMLVideoElement)) {\n throw TypeError('Currently only video transformers are supported');\n }\n // TODO explore if we can do all the processing work in a webworker\n this.processor = new MediaStreamTrackProcessor({ track: this.source });\n this.trackGenerator = new MediaStreamTrackGenerator({ kind: 'video' });\n\n this.canvas = new OffscreenCanvas(\n this.sourceSettings.width ?? 300,\n this.sourceSettings.height ?? 300,\n );\n\n let readableStream = this.processor.readable;\n for (const transformer of this.transformers) {\n transformer.init({\n outputCanvas: this.canvas,\n inputElement: this.sourceDummy!,\n });\n readableStream = readableStream.pipeThrough(transformer!.transformer!);\n }\n readableStream.pipeTo(this.trackGenerator.writable);\n this.processedTrack = this.trackGenerator as MediaStreamVideoTrack;\n }\n\n async destroy() {\n for (const transformer of this.transformers) {\n await transformer.destroy();\n }\n this.trackGenerator?.stop();\n }\n}\n","import * as vision from '@mediapipe/tasks-vision';\nimport { dependencies } from '../../package.json';\nimport VideoTransformer from './VideoTransformer';\nimport { VideoTransformerInitOptions } from './types';\n\nexport type BackgroundOptions = {\n blurRadius?: number;\n imagePath?: string;\n};\n\nexport default class BackgroundProcessor extends VideoTransformer {\n static get isSupported() {\n return typeof OffscreenCanvas !== 'undefined';\n }\n\n imageSegmenter?: vision.ImageSegmenter;\n\n segmentationResults: vision.ImageSegmenterResult | undefined;\n\n backgroundImage: ImageBitmap | null = null;\n\n blurRadius?: number;\n\n constructor(opts: BackgroundOptions) {\n super();\n if (opts.blurRadius) {\n this.blurRadius = opts.blurRadius;\n } else if (opts.imagePath) {\n this.loadBackground(opts.imagePath);\n }\n }\n\n async init({ outputCanvas, inputElement: inputVideo }: VideoTransformerInitOptions) {\n super.init({ outputCanvas, inputElement: inputVideo });\n\n const fileSet = await vision.FilesetResolver.forVisionTasks(\n `https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@${dependencies['@mediapipe/tasks-vision']}/wasm`,\n );\n\n this.imageSegmenter = await vision.ImageSegmenter.createFromOptions(fileSet, {\n baseOptions: {\n modelAssetPath:\n 'https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_segmenter/float16/latest/selfie_segmenter.tflite',\n delegate: 'CPU',\n },\n runningMode: 'VIDEO',\n outputCategoryMask: true,\n outputConfidenceMasks: false,\n });\n\n // this.loadBackground(opts.backgroundUrl).catch((e) => console.error(e));\n this.sendFramesContinuouslyForSegmentation(this.inputVideo!);\n }\n\n async destroy() {\n await super.destroy();\n await this.imageSegmenter?.close();\n this.backgroundImage = null;\n }\n\n async sendFramesContinuouslyForSegmentation(videoEl: HTMLVideoElement) {\n if (!this.isDisabled) {\n if (videoEl.videoWidth > 0 && videoEl.videoHeight > 0) {\n let startTimeMs = performance.now();\n this.imageSegmenter?.segmentForVideo(\n videoEl,\n startTimeMs,\n (result) => (this.segmentationResults = result),\n );\n }\n videoEl.requestVideoFrameCallback(() => {\n this.sendFramesContinuouslyForSegmentation(videoEl);\n });\n }\n }\n\n async loadBackground(path: string) {\n const img = new Image();\n\n await new Promise((resolve, reject) => {\n img.crossOrigin = 'Anonymous';\n img.onload = () => resolve(img);\n img.onerror = (err) => reject(err);\n img.src = path;\n });\n const imageData = await createImageBitmap(img);\n this.backgroundImage = imageData;\n }\n\n async transform(frame: VideoFrame, controller: TransformStreamDefaultController<VideoFrame>) {\n if (this.isDisabled) {\n controller.enqueue(frame);\n return;\n }\n if (!this.canvas) {\n throw TypeError('Canvas needs to be initialized first');\n }\n if (this.blurRadius) {\n await this.blurBackground(frame);\n } else {\n await this.drawVirtualBackground(frame);\n }\n const newFrame = new VideoFrame(this.canvas, {\n timestamp: frame.timestamp || Date.now(),\n });\n frame.close();\n controller.enqueue(newFrame);\n }\n\n async drawVirtualBackground(frame: VideoFrame) {\n if (!this.canvas || !this.ctx || !this.segmentationResults || !this.inputVideo) return;\n // this.ctx.save();\n // this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n if (this.segmentationResults?.categoryMask) {\n this.ctx.filter = 'blur(3px)';\n this.ctx.globalCompositeOperation = 'copy';\n const bitmap = await maskToBitmap(\n this.segmentationResults.categoryMask,\n this.inputVideo.videoWidth,\n this.inputVideo.videoHeight,\n );\n this.ctx.drawImage(bitmap, 0, 0);\n this.ctx.filter = 'none';\n this.ctx.globalCompositeOperation = 'source-in';\n if (this.backgroundImage) {\n this.ctx.drawImage(\n this.backgroundImage,\n 0,\n 0,\n this.backgroundImage.width,\n this.backgroundImage.height,\n 0,\n 0,\n this.canvas.width,\n this.canvas.height,\n );\n } else {\n this.ctx.fillStyle = '#00FF00';\n this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);\n }\n\n this.ctx.globalCompositeOperation = 'destination-over';\n }\n this.ctx.drawImage(frame, 0, 0, this.canvas.width, this.canvas.height);\n // this.ctx.restore();\n }\n\n async blurBackground(frame: VideoFrame) {\n if (\n !this.ctx ||\n !this.canvas ||\n !this.segmentationResults?.categoryMask?.canvas ||\n !this.inputVideo\n ) {\n return;\n }\n this.ctx.save();\n this.ctx.globalCompositeOperation = 'copy';\n\n const bitmap = await maskToBitmap(\n this.segmentationResults.categoryMask,\n this.inputVideo.videoWidth,\n this.inputVideo.videoHeight,\n );\n this.ctx.filter = 'blur(3px)';\n this.ctx.globalCompositeOperation = 'copy';\n this.ctx.drawImage(bitmap, 0, 0);\n this.ctx.filter = 'none';\n this.ctx.globalCompositeOperation = 'source-out';\n this.ctx.drawImage(frame, 0, 0, this.canvas.width, this.canvas.height);\n this.ctx.globalCompositeOperation = 'destination-over';\n this.ctx.filter = `blur(${this.blurRadius}px)`;\n this.ctx.drawImage(frame, 0, 0);\n this.ctx.restore();\n }\n}\n\nfunction maskToBitmap(\n mask: vision.MPMask,\n videoWidth: number,\n videoHeight: number,\n): Promise<ImageBitmap> {\n const dataArray: Uint8ClampedArray = new Uint8ClampedArray(videoWidth * videoHeight * 4);\n const result = mask.getAsUint8Array();\n for (let i = 0; i < result.length; i += 1) {\n dataArray[i * 4] = result[i];\n dataArray[i * 4 + 1] = result[i];\n dataArray[i * 4 + 2] = result[i];\n dataArray[i * 4 + 3] = result[i];\n }\n const dataNew = new ImageData(dataArray, videoWidth, videoHeight);\n\n return createImageBitmap(dataNew);\n}\n","{\n \"name\": \"@livekit/track-processors\",\n \"version\": \"0.1.4\",\n \"description\": \"LiveKit track processors\",\n \"main\": \"dist/index.js\",\n \"module\": \"dist/index.mjs\",\n \"source\": \"src/index.ts\",\n \"types\": \"dist/src/index.d.ts\",\n \"repository\": \"git@github.com:livekit/livekit-track-processors.git\",\n \"author\": \"Lukas Seiler\",\n \"license\": \"Apache-2.0\",\n \"scripts\": {\n \"build\": \"tsup --onSuccess \\\"tsc --declaration --emitDeclarationOnly\\\"\",\n \"build-docs\": \"yarn exec typedoc\",\n \"build-sample\": \"cd example && webpack && cp styles.css index.html dist/\",\n \"lint\": \"eslint src\",\n \"test\": \"jest\",\n \"sample\": \"vite serve example --port 8080 --open\"\n },\n \"files\": [\n \"dist\",\n \"src\"\n ],\n \"dependencies\": {\n \"@mediapipe/holistic\": \"0.5.1675471629\",\n \"@mediapipe/tasks-vision\": \"0.10.1\"\n },\n \"peerDependencies\": {\n \"livekit-client\": \"^1.10.0\"\n },\n \"devDependencies\": {\n \"@trivago/prettier-plugin-sort-imports\": \"^4.1.1\",\n \"@types/dom-mediacapture-transform\": \"^0.1.5\",\n \"@types/jest\": \"^27.0.3\",\n \"@types/offscreencanvas\": \"^2019.7.0\",\n \"@typescript-eslint/eslint-plugin\": \"^4.31.2\",\n \"@webpack-cli/serve\": \"^1.5.2\",\n \"eslint\": \"8.39.0\",\n \"eslint-config-airbnb-typescript\": \"17.0.0\",\n \"eslint-config-prettier\": \"8.8.0\",\n \"eslint-plugin-ecmascript-compat\": \"^3.0.0\",\n \"eslint-plugin-import\": \"2.27.5\",\n \"jest\": \"^27.4.3\",\n \"livekit-client\": \"^0.16.6\",\n \"prettier\": \"^2.8.8\",\n \"ts-jest\": \"^27.0.7\",\n \"ts-loader\": \"^8.1.0\",\n \"ts-proto\": \"^1.85.0\",\n \"tsup\": \"^7.1.0\",\n \"typedoc\": \"^0.20.35\",\n \"typedoc-plugin-no-inherit\": \"1.3.0\",\n \"typescript\": \"^5.0.4\",\n \"vite\": \"^4.3.8\",\n \"webpack\": \"^5.53.0\",\n \"webpack-cli\": \"^4.8.0\",\n \"webpack-dev-server\": \"^4.2.1\"\n }\n}\n","import { VideoTrackTransformer, VideoTransformerInitOptions } from './types';\n\nexport default abstract class VideoTransformer implements VideoTrackTransformer {\n transformer?: TransformStream;\n\n canvas?: OffscreenCanvas;\n\n ctx?: OffscreenCanvasRenderingContext2D;\n\n inputVideo?: HTMLVideoElement;\n\n protected isDisabled?: Boolean = false;\n\n init({ outputCanvas, inputElement: inputVideo }: VideoTransformerInitOptions): void {\n if (!(inputVideo instanceof HTMLVideoElement)) {\n throw TypeError('Video transformer needs a HTMLVideoElement as input');\n }\n this.transformer = new TransformStream({\n transform: (frame, controller) => this.transform(frame, controller),\n });\n this.canvas = outputCanvas || null;\n if (outputCanvas) {\n this.ctx = this.canvas?.getContext('2d', { readFrequently: true }) || undefined;\n }\n this.inputVideo = inputVideo;\n this.isDisabled = false;\n }\n\n async destroy() {\n this.isDisabled = true;\n this.canvas = undefined;\n this.ctx = undefined;\n }\n\n abstract transform(\n frame: VideoFrame,\n controller: TransformStreamDefaultController<VideoFrame>,\n ): void;\n}\n","import VideoTransformer from './VideoTransformer';\n\nexport default class DummyTransformer extends VideoTransformer {\n async transform(frame: VideoFrame, controller: TransformStreamDefaultController<VideoFrame>) {\n controller.enqueue(frame);\n }\n\n async destroy() {\n // nothing to do\n }\n}\n","import ProcessorPipeline from './ProcessorPipeline';\nimport BackgroundTransformer from './transformers/BackgroundTransformer';\nimport DummyTransformer from './transformers/DummyTransformer';\n\nexport const BackgroundBlur = (blurRadius: number = 10) => {\n const isPipelineSupported = ProcessorPipeline.isSupported && BackgroundTransformer.isSupported;\n if (!isPipelineSupported) {\n throw new Error('pipeline is not supported in this browser');\n }\n const pipeline = new ProcessorPipeline(\n [new BackgroundTransformer({ blurRadius })],\n 'background-blur',\n );\n return pipeline;\n};\n\nexport const VirtualBackground = (imagePath: string) => {\n const isPipelineSupported = ProcessorPipeline.isSupported && BackgroundTransformer.isSupported;\n if (!isPipelineSupported) {\n throw new Error('pipeline is not supported in this browser');\n }\n const pipeline = new ProcessorPipeline(\n [new BackgroundTransformer({ imagePath })],\n 'virtual-background',\n );\n return pipeline;\n};\n\nexport const Dummy = () => {\n const isPipelineSupported = ProcessorPipeline.isSupported && BackgroundTransformer.isSupported;\n if (!isPipelineSupported) {\n throw new Error('pipeline is not supported in this browser');\n }\n const pipeline = new ProcessorPipeline([new DummyTransformer()], 'dummy');\n return pipeline;\n};\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
// src/ProcessorPipeline.ts
|
|
2
|
+
var ProcessorPipeline = class {
|
|
3
|
+
static get isSupported() {
|
|
4
|
+
return typeof MediaStreamTrackGenerator !== "undefined" && typeof MediaStreamTrackProcessor !== "undefined";
|
|
5
|
+
}
|
|
6
|
+
constructor(transformers, name) {
|
|
7
|
+
this.name = name;
|
|
8
|
+
this.transformers = transformers;
|
|
9
|
+
}
|
|
10
|
+
async init(opts) {
|
|
11
|
+
var _a, _b;
|
|
12
|
+
this.source = opts.track;
|
|
13
|
+
this.sourceSettings = this.source.getSettings();
|
|
14
|
+
this.sourceDummy = opts.element;
|
|
15
|
+
if (!(this.sourceDummy instanceof HTMLVideoElement)) {
|
|
16
|
+
throw TypeError("Currently only video transformers are supported");
|
|
17
|
+
}
|
|
18
|
+
this.processor = new MediaStreamTrackProcessor({ track: this.source });
|
|
19
|
+
this.trackGenerator = new MediaStreamTrackGenerator({ kind: "video" });
|
|
20
|
+
this.canvas = new OffscreenCanvas(
|
|
21
|
+
(_a = this.sourceSettings.width) != null ? _a : 300,
|
|
22
|
+
(_b = this.sourceSettings.height) != null ? _b : 300
|
|
23
|
+
);
|
|
24
|
+
let readableStream = this.processor.readable;
|
|
25
|
+
for (const transformer of this.transformers) {
|
|
26
|
+
transformer.init({
|
|
27
|
+
outputCanvas: this.canvas,
|
|
28
|
+
inputElement: this.sourceDummy
|
|
29
|
+
});
|
|
30
|
+
readableStream = readableStream.pipeThrough(transformer.transformer);
|
|
31
|
+
}
|
|
32
|
+
readableStream.pipeTo(this.trackGenerator.writable);
|
|
33
|
+
this.processedTrack = this.trackGenerator;
|
|
34
|
+
}
|
|
35
|
+
async destroy() {
|
|
36
|
+
var _a;
|
|
37
|
+
for (const transformer of this.transformers) {
|
|
38
|
+
await transformer.destroy();
|
|
39
|
+
}
|
|
40
|
+
(_a = this.trackGenerator) == null ? void 0 : _a.stop();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// src/transformers/BackgroundTransformer.ts
|
|
45
|
+
import * as vision from "@mediapipe/tasks-vision";
|
|
46
|
+
|
|
47
|
+
// package.json
|
|
48
|
+
var dependencies = {
|
|
49
|
+
"@mediapipe/holistic": "0.5.1675471629",
|
|
50
|
+
"@mediapipe/tasks-vision": "0.10.1"
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// src/transformers/VideoTransformer.ts
|
|
54
|
+
var VideoTransformer = class {
|
|
55
|
+
constructor() {
|
|
56
|
+
this.isDisabled = false;
|
|
57
|
+
}
|
|
58
|
+
init({ outputCanvas, inputElement: inputVideo }) {
|
|
59
|
+
var _a;
|
|
60
|
+
if (!(inputVideo instanceof HTMLVideoElement)) {
|
|
61
|
+
throw TypeError("Video transformer needs a HTMLVideoElement as input");
|
|
62
|
+
}
|
|
63
|
+
this.transformer = new TransformStream({
|
|
64
|
+
transform: (frame, controller) => this.transform(frame, controller)
|
|
65
|
+
});
|
|
66
|
+
this.canvas = outputCanvas || null;
|
|
67
|
+
if (outputCanvas) {
|
|
68
|
+
this.ctx = ((_a = this.canvas) == null ? void 0 : _a.getContext("2d", { readFrequently: true })) || void 0;
|
|
69
|
+
}
|
|
70
|
+
this.inputVideo = inputVideo;
|
|
71
|
+
this.isDisabled = false;
|
|
72
|
+
}
|
|
73
|
+
async destroy() {
|
|
74
|
+
this.isDisabled = true;
|
|
75
|
+
this.canvas = void 0;
|
|
76
|
+
this.ctx = void 0;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// src/transformers/BackgroundTransformer.ts
|
|
81
|
+
var BackgroundProcessor = class extends VideoTransformer {
|
|
82
|
+
constructor(opts) {
|
|
83
|
+
super();
|
|
84
|
+
this.backgroundImage = null;
|
|
85
|
+
if (opts.blurRadius) {
|
|
86
|
+
this.blurRadius = opts.blurRadius;
|
|
87
|
+
} else if (opts.imagePath) {
|
|
88
|
+
this.loadBackground(opts.imagePath);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
static get isSupported() {
|
|
92
|
+
return typeof OffscreenCanvas !== "undefined";
|
|
93
|
+
}
|
|
94
|
+
async init({ outputCanvas, inputElement: inputVideo }) {
|
|
95
|
+
super.init({ outputCanvas, inputElement: inputVideo });
|
|
96
|
+
const fileSet = await vision.FilesetResolver.forVisionTasks(
|
|
97
|
+
`https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@${dependencies["@mediapipe/tasks-vision"]}/wasm`
|
|
98
|
+
);
|
|
99
|
+
this.imageSegmenter = await vision.ImageSegmenter.createFromOptions(fileSet, {
|
|
100
|
+
baseOptions: {
|
|
101
|
+
modelAssetPath: "https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_segmenter/float16/latest/selfie_segmenter.tflite",
|
|
102
|
+
delegate: "CPU"
|
|
103
|
+
},
|
|
104
|
+
runningMode: "VIDEO",
|
|
105
|
+
outputCategoryMask: true,
|
|
106
|
+
outputConfidenceMasks: false
|
|
107
|
+
});
|
|
108
|
+
this.sendFramesContinuouslyForSegmentation(this.inputVideo);
|
|
109
|
+
}
|
|
110
|
+
async destroy() {
|
|
111
|
+
var _a;
|
|
112
|
+
await super.destroy();
|
|
113
|
+
await ((_a = this.imageSegmenter) == null ? void 0 : _a.close());
|
|
114
|
+
this.backgroundImage = null;
|
|
115
|
+
}
|
|
116
|
+
async sendFramesContinuouslyForSegmentation(videoEl) {
|
|
117
|
+
var _a;
|
|
118
|
+
if (!this.isDisabled) {
|
|
119
|
+
if (videoEl.videoWidth > 0 && videoEl.videoHeight > 0) {
|
|
120
|
+
let startTimeMs = performance.now();
|
|
121
|
+
(_a = this.imageSegmenter) == null ? void 0 : _a.segmentForVideo(
|
|
122
|
+
videoEl,
|
|
123
|
+
startTimeMs,
|
|
124
|
+
(result) => this.segmentationResults = result
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
videoEl.requestVideoFrameCallback(() => {
|
|
128
|
+
this.sendFramesContinuouslyForSegmentation(videoEl);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async loadBackground(path) {
|
|
133
|
+
const img = new Image();
|
|
134
|
+
await new Promise((resolve, reject) => {
|
|
135
|
+
img.crossOrigin = "Anonymous";
|
|
136
|
+
img.onload = () => resolve(img);
|
|
137
|
+
img.onerror = (err) => reject(err);
|
|
138
|
+
img.src = path;
|
|
139
|
+
});
|
|
140
|
+
const imageData = await createImageBitmap(img);
|
|
141
|
+
this.backgroundImage = imageData;
|
|
142
|
+
}
|
|
143
|
+
async transform(frame, controller) {
|
|
144
|
+
if (this.isDisabled) {
|
|
145
|
+
controller.enqueue(frame);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (!this.canvas) {
|
|
149
|
+
throw TypeError("Canvas needs to be initialized first");
|
|
150
|
+
}
|
|
151
|
+
if (this.blurRadius) {
|
|
152
|
+
await this.blurBackground(frame);
|
|
153
|
+
} else {
|
|
154
|
+
await this.drawVirtualBackground(frame);
|
|
155
|
+
}
|
|
156
|
+
const newFrame = new VideoFrame(this.canvas, {
|
|
157
|
+
timestamp: frame.timestamp || Date.now()
|
|
158
|
+
});
|
|
159
|
+
frame.close();
|
|
160
|
+
controller.enqueue(newFrame);
|
|
161
|
+
}
|
|
162
|
+
async drawVirtualBackground(frame) {
|
|
163
|
+
var _a;
|
|
164
|
+
if (!this.canvas || !this.ctx || !this.segmentationResults || !this.inputVideo)
|
|
165
|
+
return;
|
|
166
|
+
if ((_a = this.segmentationResults) == null ? void 0 : _a.categoryMask) {
|
|
167
|
+
this.ctx.filter = "blur(3px)";
|
|
168
|
+
this.ctx.globalCompositeOperation = "copy";
|
|
169
|
+
const bitmap = await maskToBitmap(
|
|
170
|
+
this.segmentationResults.categoryMask,
|
|
171
|
+
this.inputVideo.videoWidth,
|
|
172
|
+
this.inputVideo.videoHeight
|
|
173
|
+
);
|
|
174
|
+
this.ctx.drawImage(bitmap, 0, 0);
|
|
175
|
+
this.ctx.filter = "none";
|
|
176
|
+
this.ctx.globalCompositeOperation = "source-in";
|
|
177
|
+
if (this.backgroundImage) {
|
|
178
|
+
this.ctx.drawImage(
|
|
179
|
+
this.backgroundImage,
|
|
180
|
+
0,
|
|
181
|
+
0,
|
|
182
|
+
this.backgroundImage.width,
|
|
183
|
+
this.backgroundImage.height,
|
|
184
|
+
0,
|
|
185
|
+
0,
|
|
186
|
+
this.canvas.width,
|
|
187
|
+
this.canvas.height
|
|
188
|
+
);
|
|
189
|
+
} else {
|
|
190
|
+
this.ctx.fillStyle = "#00FF00";
|
|
191
|
+
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
192
|
+
}
|
|
193
|
+
this.ctx.globalCompositeOperation = "destination-over";
|
|
194
|
+
}
|
|
195
|
+
this.ctx.drawImage(frame, 0, 0, this.canvas.width, this.canvas.height);
|
|
196
|
+
}
|
|
197
|
+
async blurBackground(frame) {
|
|
198
|
+
var _a, _b;
|
|
199
|
+
if (!this.ctx || !this.canvas || !((_b = (_a = this.segmentationResults) == null ? void 0 : _a.categoryMask) == null ? void 0 : _b.canvas) || !this.inputVideo) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
this.ctx.save();
|
|
203
|
+
this.ctx.globalCompositeOperation = "copy";
|
|
204
|
+
const bitmap = await maskToBitmap(
|
|
205
|
+
this.segmentationResults.categoryMask,
|
|
206
|
+
this.inputVideo.videoWidth,
|
|
207
|
+
this.inputVideo.videoHeight
|
|
208
|
+
);
|
|
209
|
+
this.ctx.filter = "blur(3px)";
|
|
210
|
+
this.ctx.globalCompositeOperation = "copy";
|
|
211
|
+
this.ctx.drawImage(bitmap, 0, 0);
|
|
212
|
+
this.ctx.filter = "none";
|
|
213
|
+
this.ctx.globalCompositeOperation = "source-out";
|
|
214
|
+
this.ctx.drawImage(frame, 0, 0, this.canvas.width, this.canvas.height);
|
|
215
|
+
this.ctx.globalCompositeOperation = "destination-over";
|
|
216
|
+
this.ctx.filter = `blur(${this.blurRadius}px)`;
|
|
217
|
+
this.ctx.drawImage(frame, 0, 0);
|
|
218
|
+
this.ctx.restore();
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
function maskToBitmap(mask, videoWidth, videoHeight) {
|
|
222
|
+
const dataArray = new Uint8ClampedArray(videoWidth * videoHeight * 4);
|
|
223
|
+
const result = mask.getAsUint8Array();
|
|
224
|
+
for (let i = 0; i < result.length; i += 1) {
|
|
225
|
+
dataArray[i * 4] = result[i];
|
|
226
|
+
dataArray[i * 4 + 1] = result[i];
|
|
227
|
+
dataArray[i * 4 + 2] = result[i];
|
|
228
|
+
dataArray[i * 4 + 3] = result[i];
|
|
229
|
+
}
|
|
230
|
+
const dataNew = new ImageData(dataArray, videoWidth, videoHeight);
|
|
231
|
+
return createImageBitmap(dataNew);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// src/transformers/DummyTransformer.ts
|
|
235
|
+
var DummyTransformer = class extends VideoTransformer {
|
|
236
|
+
async transform(frame, controller) {
|
|
237
|
+
controller.enqueue(frame);
|
|
238
|
+
}
|
|
239
|
+
async destroy() {
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// src/index.ts
|
|
244
|
+
var BackgroundBlur = (blurRadius = 10) => {
|
|
245
|
+
const isPipelineSupported = ProcessorPipeline.isSupported && BackgroundProcessor.isSupported;
|
|
246
|
+
if (!isPipelineSupported) {
|
|
247
|
+
throw new Error("pipeline is not supported in this browser");
|
|
248
|
+
}
|
|
249
|
+
const pipeline = new ProcessorPipeline(
|
|
250
|
+
[new BackgroundProcessor({ blurRadius })],
|
|
251
|
+
"background-blur"
|
|
252
|
+
);
|
|
253
|
+
return pipeline;
|
|
254
|
+
};
|
|
255
|
+
var VirtualBackground = (imagePath) => {
|
|
256
|
+
const isPipelineSupported = ProcessorPipeline.isSupported && BackgroundProcessor.isSupported;
|
|
257
|
+
if (!isPipelineSupported) {
|
|
258
|
+
throw new Error("pipeline is not supported in this browser");
|
|
259
|
+
}
|
|
260
|
+
const pipeline = new ProcessorPipeline(
|
|
261
|
+
[new BackgroundProcessor({ imagePath })],
|
|
262
|
+
"virtual-background"
|
|
263
|
+
);
|
|
264
|
+
return pipeline;
|
|
265
|
+
};
|
|
266
|
+
var Dummy = () => {
|
|
267
|
+
const isPipelineSupported = ProcessorPipeline.isSupported && BackgroundProcessor.isSupported;
|
|
268
|
+
if (!isPipelineSupported) {
|
|
269
|
+
throw new Error("pipeline is not supported in this browser");
|
|
270
|
+
}
|
|
271
|
+
const pipeline = new ProcessorPipeline([new DummyTransformer()], "dummy");
|
|
272
|
+
return pipeline;
|
|
273
|
+
};
|
|
274
|
+
export {
|
|
275
|
+
BackgroundBlur,
|
|
276
|
+
Dummy,
|
|
277
|
+
VirtualBackground
|
|
278
|
+
};
|
|
279
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ProcessorPipeline.ts","../src/transformers/BackgroundTransformer.ts","../package.json","../src/transformers/VideoTransformer.ts","../src/transformers/DummyTransformer.ts","../src/index.ts"],"sourcesContent":["import type { ProcessorOptions, Track, TrackProcessor } from 'livekit-client';\nimport { VideoTrackTransformer } from './transformers';\n\nexport default class ProcessorPipeline implements TrackProcessor<Track.Kind> {\n static get isSupported() {\n return (\n typeof MediaStreamTrackGenerator !== 'undefined' &&\n typeof MediaStreamTrackProcessor !== 'undefined'\n );\n }\n\n name: string;\n\n source?: MediaStreamVideoTrack;\n\n sourceSettings?: MediaTrackSettings;\n\n processor?: MediaStreamTrackProcessor<VideoFrame>;\n\n trackGenerator?: MediaStreamTrackGenerator<VideoFrame>;\n\n canvas?: OffscreenCanvas;\n\n sourceDummy?: HTMLMediaElement;\n\n processedTrack?: MediaStreamTrack;\n\n transformers: Array<VideoTrackTransformer>;\n\n constructor(transformers: Array<VideoTrackTransformer>, name: string) {\n this.name = name;\n this.transformers = transformers;\n }\n\n async init(opts: ProcessorOptions<Track.Kind>) {\n this.source = opts.track as MediaStreamVideoTrack;\n this.sourceSettings = this.source.getSettings();\n this.sourceDummy = opts.element;\n if (!(this.sourceDummy instanceof HTMLVideoElement)) {\n throw TypeError('Currently only video transformers are supported');\n }\n // TODO explore if we can do all the processing work in a webworker\n this.processor = new MediaStreamTrackProcessor({ track: this.source });\n this.trackGenerator = new MediaStreamTrackGenerator({ kind: 'video' });\n\n this.canvas = new OffscreenCanvas(\n this.sourceSettings.width ?? 300,\n this.sourceSettings.height ?? 300,\n );\n\n let readableStream = this.processor.readable;\n for (const transformer of this.transformers) {\n transformer.init({\n outputCanvas: this.canvas,\n inputElement: this.sourceDummy!,\n });\n readableStream = readableStream.pipeThrough(transformer!.transformer!);\n }\n readableStream.pipeTo(this.trackGenerator.writable);\n this.processedTrack = this.trackGenerator as MediaStreamVideoTrack;\n }\n\n async destroy() {\n for (const transformer of this.transformers) {\n await transformer.destroy();\n }\n this.trackGenerator?.stop();\n }\n}\n","import * as vision from '@mediapipe/tasks-vision';\nimport { dependencies } from '../../package.json';\nimport VideoTransformer from './VideoTransformer';\nimport { VideoTransformerInitOptions } from './types';\n\nexport type BackgroundOptions = {\n blurRadius?: number;\n imagePath?: string;\n};\n\nexport default class BackgroundProcessor extends VideoTransformer {\n static get isSupported() {\n return typeof OffscreenCanvas !== 'undefined';\n }\n\n imageSegmenter?: vision.ImageSegmenter;\n\n segmentationResults: vision.ImageSegmenterResult | undefined;\n\n backgroundImage: ImageBitmap | null = null;\n\n blurRadius?: number;\n\n constructor(opts: BackgroundOptions) {\n super();\n if (opts.blurRadius) {\n this.blurRadius = opts.blurRadius;\n } else if (opts.imagePath) {\n this.loadBackground(opts.imagePath);\n }\n }\n\n async init({ outputCanvas, inputElement: inputVideo }: VideoTransformerInitOptions) {\n super.init({ outputCanvas, inputElement: inputVideo });\n\n const fileSet = await vision.FilesetResolver.forVisionTasks(\n `https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@${dependencies['@mediapipe/tasks-vision']}/wasm`,\n );\n\n this.imageSegmenter = await vision.ImageSegmenter.createFromOptions(fileSet, {\n baseOptions: {\n modelAssetPath:\n 'https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_segmenter/float16/latest/selfie_segmenter.tflite',\n delegate: 'CPU',\n },\n runningMode: 'VIDEO',\n outputCategoryMask: true,\n outputConfidenceMasks: false,\n });\n\n // this.loadBackground(opts.backgroundUrl).catch((e) => console.error(e));\n this.sendFramesContinuouslyForSegmentation(this.inputVideo!);\n }\n\n async destroy() {\n await super.destroy();\n await this.imageSegmenter?.close();\n this.backgroundImage = null;\n }\n\n async sendFramesContinuouslyForSegmentation(videoEl: HTMLVideoElement) {\n if (!this.isDisabled) {\n if (videoEl.videoWidth > 0 && videoEl.videoHeight > 0) {\n let startTimeMs = performance.now();\n this.imageSegmenter?.segmentForVideo(\n videoEl,\n startTimeMs,\n (result) => (this.segmentationResults = result),\n );\n }\n videoEl.requestVideoFrameCallback(() => {\n this.sendFramesContinuouslyForSegmentation(videoEl);\n });\n }\n }\n\n async loadBackground(path: string) {\n const img = new Image();\n\n await new Promise((resolve, reject) => {\n img.crossOrigin = 'Anonymous';\n img.onload = () => resolve(img);\n img.onerror = (err) => reject(err);\n img.src = path;\n });\n const imageData = await createImageBitmap(img);\n this.backgroundImage = imageData;\n }\n\n async transform(frame: VideoFrame, controller: TransformStreamDefaultController<VideoFrame>) {\n if (this.isDisabled) {\n controller.enqueue(frame);\n return;\n }\n if (!this.canvas) {\n throw TypeError('Canvas needs to be initialized first');\n }\n if (this.blurRadius) {\n await this.blurBackground(frame);\n } else {\n await this.drawVirtualBackground(frame);\n }\n const newFrame = new VideoFrame(this.canvas, {\n timestamp: frame.timestamp || Date.now(),\n });\n frame.close();\n controller.enqueue(newFrame);\n }\n\n async drawVirtualBackground(frame: VideoFrame) {\n if (!this.canvas || !this.ctx || !this.segmentationResults || !this.inputVideo) return;\n // this.ctx.save();\n // this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n if (this.segmentationResults?.categoryMask) {\n this.ctx.filter = 'blur(3px)';\n this.ctx.globalCompositeOperation = 'copy';\n const bitmap = await maskToBitmap(\n this.segmentationResults.categoryMask,\n this.inputVideo.videoWidth,\n this.inputVideo.videoHeight,\n );\n this.ctx.drawImage(bitmap, 0, 0);\n this.ctx.filter = 'none';\n this.ctx.globalCompositeOperation = 'source-in';\n if (this.backgroundImage) {\n this.ctx.drawImage(\n this.backgroundImage,\n 0,\n 0,\n this.backgroundImage.width,\n this.backgroundImage.height,\n 0,\n 0,\n this.canvas.width,\n this.canvas.height,\n );\n } else {\n this.ctx.fillStyle = '#00FF00';\n this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);\n }\n\n this.ctx.globalCompositeOperation = 'destination-over';\n }\n this.ctx.drawImage(frame, 0, 0, this.canvas.width, this.canvas.height);\n // this.ctx.restore();\n }\n\n async blurBackground(frame: VideoFrame) {\n if (\n !this.ctx ||\n !this.canvas ||\n !this.segmentationResults?.categoryMask?.canvas ||\n !this.inputVideo\n ) {\n return;\n }\n this.ctx.save();\n this.ctx.globalCompositeOperation = 'copy';\n\n const bitmap = await maskToBitmap(\n this.segmentationResults.categoryMask,\n this.inputVideo.videoWidth,\n this.inputVideo.videoHeight,\n );\n this.ctx.filter = 'blur(3px)';\n this.ctx.globalCompositeOperation = 'copy';\n this.ctx.drawImage(bitmap, 0, 0);\n this.ctx.filter = 'none';\n this.ctx.globalCompositeOperation = 'source-out';\n this.ctx.drawImage(frame, 0, 0, this.canvas.width, this.canvas.height);\n this.ctx.globalCompositeOperation = 'destination-over';\n this.ctx.filter = `blur(${this.blurRadius}px)`;\n this.ctx.drawImage(frame, 0, 0);\n this.ctx.restore();\n }\n}\n\nfunction maskToBitmap(\n mask: vision.MPMask,\n videoWidth: number,\n videoHeight: number,\n): Promise<ImageBitmap> {\n const dataArray: Uint8ClampedArray = new Uint8ClampedArray(videoWidth * videoHeight * 4);\n const result = mask.getAsUint8Array();\n for (let i = 0; i < result.length; i += 1) {\n dataArray[i * 4] = result[i];\n dataArray[i * 4 + 1] = result[i];\n dataArray[i * 4 + 2] = result[i];\n dataArray[i * 4 + 3] = result[i];\n }\n const dataNew = new ImageData(dataArray, videoWidth, videoHeight);\n\n return createImageBitmap(dataNew);\n}\n","{\n \"name\": \"@livekit/track-processors\",\n \"version\": \"0.1.4\",\n \"description\": \"LiveKit track processors\",\n \"main\": \"dist/index.js\",\n \"module\": \"dist/index.mjs\",\n \"source\": \"src/index.ts\",\n \"types\": \"dist/src/index.d.ts\",\n \"repository\": \"git@github.com:livekit/livekit-track-processors.git\",\n \"author\": \"Lukas Seiler\",\n \"license\": \"Apache-2.0\",\n \"scripts\": {\n \"build\": \"tsup --onSuccess \\\"tsc --declaration --emitDeclarationOnly\\\"\",\n \"build-docs\": \"yarn exec typedoc\",\n \"build-sample\": \"cd example && webpack && cp styles.css index.html dist/\",\n \"lint\": \"eslint src\",\n \"test\": \"jest\",\n \"sample\": \"vite serve example --port 8080 --open\"\n },\n \"files\": [\n \"dist\",\n \"src\"\n ],\n \"dependencies\": {\n \"@mediapipe/holistic\": \"0.5.1675471629\",\n \"@mediapipe/tasks-vision\": \"0.10.1\"\n },\n \"peerDependencies\": {\n \"livekit-client\": \"^1.10.0\"\n },\n \"devDependencies\": {\n \"@trivago/prettier-plugin-sort-imports\": \"^4.1.1\",\n \"@types/dom-mediacapture-transform\": \"^0.1.5\",\n \"@types/jest\": \"^27.0.3\",\n \"@types/offscreencanvas\": \"^2019.7.0\",\n \"@typescript-eslint/eslint-plugin\": \"^4.31.2\",\n \"@webpack-cli/serve\": \"^1.5.2\",\n \"eslint\": \"8.39.0\",\n \"eslint-config-airbnb-typescript\": \"17.0.0\",\n \"eslint-config-prettier\": \"8.8.0\",\n \"eslint-plugin-ecmascript-compat\": \"^3.0.0\",\n \"eslint-plugin-import\": \"2.27.5\",\n \"jest\": \"^27.4.3\",\n \"livekit-client\": \"^0.16.6\",\n \"prettier\": \"^2.8.8\",\n \"ts-jest\": \"^27.0.7\",\n \"ts-loader\": \"^8.1.0\",\n \"ts-proto\": \"^1.85.0\",\n \"tsup\": \"^7.1.0\",\n \"typedoc\": \"^0.20.35\",\n \"typedoc-plugin-no-inherit\": \"1.3.0\",\n \"typescript\": \"^5.0.4\",\n \"vite\": \"^4.3.8\",\n \"webpack\": \"^5.53.0\",\n \"webpack-cli\": \"^4.8.0\",\n \"webpack-dev-server\": \"^4.2.1\"\n }\n}\n","import { VideoTrackTransformer, VideoTransformerInitOptions } from './types';\n\nexport default abstract class VideoTransformer implements VideoTrackTransformer {\n transformer?: TransformStream;\n\n canvas?: OffscreenCanvas;\n\n ctx?: OffscreenCanvasRenderingContext2D;\n\n inputVideo?: HTMLVideoElement;\n\n protected isDisabled?: Boolean = false;\n\n init({ outputCanvas, inputElement: inputVideo }: VideoTransformerInitOptions): void {\n if (!(inputVideo instanceof HTMLVideoElement)) {\n throw TypeError('Video transformer needs a HTMLVideoElement as input');\n }\n this.transformer = new TransformStream({\n transform: (frame, controller) => this.transform(frame, controller),\n });\n this.canvas = outputCanvas || null;\n if (outputCanvas) {\n this.ctx = this.canvas?.getContext('2d', { readFrequently: true }) || undefined;\n }\n this.inputVideo = inputVideo;\n this.isDisabled = false;\n }\n\n async destroy() {\n this.isDisabled = true;\n this.canvas = undefined;\n this.ctx = undefined;\n }\n\n abstract transform(\n frame: VideoFrame,\n controller: TransformStreamDefaultController<VideoFrame>,\n ): void;\n}\n","import VideoTransformer from './VideoTransformer';\n\nexport default class DummyTransformer extends VideoTransformer {\n async transform(frame: VideoFrame, controller: TransformStreamDefaultController<VideoFrame>) {\n controller.enqueue(frame);\n }\n\n async destroy() {\n // nothing to do\n }\n}\n","import ProcessorPipeline from './ProcessorPipeline';\nimport BackgroundTransformer from './transformers/BackgroundTransformer';\nimport DummyTransformer from './transformers/DummyTransformer';\n\nexport const BackgroundBlur = (blurRadius: number = 10) => {\n const isPipelineSupported = ProcessorPipeline.isSupported && BackgroundTransformer.isSupported;\n if (!isPipelineSupported) {\n throw new Error('pipeline is not supported in this browser');\n }\n const pipeline = new ProcessorPipeline(\n [new BackgroundTransformer({ blurRadius })],\n 'background-blur',\n );\n return pipeline;\n};\n\nexport const VirtualBackground = (imagePath: string) => {\n const isPipelineSupported = ProcessorPipeline.isSupported && BackgroundTransformer.isSupported;\n if (!isPipelineSupported) {\n throw new Error('pipeline is not supported in this browser');\n }\n const pipeline = new ProcessorPipeline(\n [new BackgroundTransformer({ imagePath })],\n 'virtual-background',\n );\n return pipeline;\n};\n\nexport const Dummy = () => {\n const isPipelineSupported = ProcessorPipeline.isSupported && BackgroundTransformer.isSupported;\n if (!isPipelineSupported) {\n throw new Error('pipeline is not supported in this browser');\n }\n const pipeline = new ProcessorPipeline([new DummyTransformer()], 'dummy');\n return pipeline;\n};\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livekit/track-processors",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "LiveKit track processors",
|
|
5
|
-
"main": "dist/
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
6
7
|
"source": "src/index.ts",
|
|
7
8
|
"types": "dist/src/index.d.ts",
|
|
8
9
|
"repository": "git@github.com:livekit/livekit-track-processors.git",
|
|
9
10
|
"author": "Lukas Seiler",
|
|
10
11
|
"license": "Apache-2.0",
|
|
11
12
|
"scripts": {
|
|
12
|
-
"build": "
|
|
13
|
+
"build": "tsup --onSuccess \"tsc --declaration --emitDeclarationOnly\"",
|
|
13
14
|
"build-docs": "yarn exec typedoc",
|
|
14
15
|
"build-sample": "cd example && webpack && cp styles.css index.html dist/",
|
|
15
16
|
"lint": "eslint src",
|
|
@@ -45,6 +46,7 @@
|
|
|
45
46
|
"ts-jest": "^27.0.7",
|
|
46
47
|
"ts-loader": "^8.1.0",
|
|
47
48
|
"ts-proto": "^1.85.0",
|
|
49
|
+
"tsup": "^7.1.0",
|
|
48
50
|
"typedoc": "^0.20.35",
|
|
49
51
|
"typedoc-plugin-no-inherit": "1.3.0",
|
|
50
52
|
"typescript": "^5.0.4",
|
package/dist/package.json
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@livekit/track-processors",
|
|
3
|
-
"version": "0.1.3",
|
|
4
|
-
"description": "LiveKit track processors",
|
|
5
|
-
"main": "dist/src/index.js",
|
|
6
|
-
"source": "src/index.ts",
|
|
7
|
-
"types": "dist/src/index.d.ts",
|
|
8
|
-
"repository": "git@github.com:livekit/livekit-track-processors.git",
|
|
9
|
-
"author": "Lukas Seiler",
|
|
10
|
-
"license": "Apache-2.0",
|
|
11
|
-
"scripts": {
|
|
12
|
-
"build": "yarn exec tsc",
|
|
13
|
-
"build-docs": "yarn exec typedoc",
|
|
14
|
-
"build-sample": "cd example && webpack && cp styles.css index.html dist/",
|
|
15
|
-
"lint": "eslint src",
|
|
16
|
-
"test": "jest",
|
|
17
|
-
"sample": "vite serve example --port 8080 --open"
|
|
18
|
-
},
|
|
19
|
-
"files": [
|
|
20
|
-
"dist",
|
|
21
|
-
"src"
|
|
22
|
-
],
|
|
23
|
-
"dependencies": {
|
|
24
|
-
"@mediapipe/holistic": "0.5.1675471629",
|
|
25
|
-
"@mediapipe/tasks-vision": "0.10.1"
|
|
26
|
-
},
|
|
27
|
-
"peerDependencies": {
|
|
28
|
-
"livekit-client": "^1.10.0"
|
|
29
|
-
},
|
|
30
|
-
"devDependencies": {
|
|
31
|
-
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
|
|
32
|
-
"@types/dom-mediacapture-transform": "^0.1.5",
|
|
33
|
-
"@types/jest": "^27.0.3",
|
|
34
|
-
"@types/offscreencanvas": "^2019.7.0",
|
|
35
|
-
"@typescript-eslint/eslint-plugin": "^4.31.2",
|
|
36
|
-
"@webpack-cli/serve": "^1.5.2",
|
|
37
|
-
"eslint": "8.39.0",
|
|
38
|
-
"eslint-config-airbnb-typescript": "17.0.0",
|
|
39
|
-
"eslint-config-prettier": "8.8.0",
|
|
40
|
-
"eslint-plugin-ecmascript-compat": "^3.0.0",
|
|
41
|
-
"eslint-plugin-import": "2.27.5",
|
|
42
|
-
"jest": "^27.4.3",
|
|
43
|
-
"livekit-client": "^0.16.6",
|
|
44
|
-
"prettier": "^2.8.8",
|
|
45
|
-
"ts-jest": "^27.0.7",
|
|
46
|
-
"ts-loader": "^8.1.0",
|
|
47
|
-
"ts-proto": "^1.85.0",
|
|
48
|
-
"typedoc": "^0.20.35",
|
|
49
|
-
"typedoc-plugin-no-inherit": "1.3.0",
|
|
50
|
-
"typescript": "^5.0.4",
|
|
51
|
-
"vite": "^4.3.8",
|
|
52
|
-
"webpack": "^5.53.0",
|
|
53
|
-
"webpack-cli": "^4.8.0",
|
|
54
|
-
"webpack-dev-server": "^4.2.1"
|
|
55
|
-
}
|
|
56
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
export default class ProcessorPipeline {
|
|
2
|
-
static get isSupported() {
|
|
3
|
-
return (typeof MediaStreamTrackGenerator !== 'undefined' &&
|
|
4
|
-
typeof MediaStreamTrackProcessor !== 'undefined');
|
|
5
|
-
}
|
|
6
|
-
constructor(transformers, name) {
|
|
7
|
-
this.name = name;
|
|
8
|
-
this.transformers = transformers;
|
|
9
|
-
}
|
|
10
|
-
async init(opts) {
|
|
11
|
-
var _a, _b;
|
|
12
|
-
this.source = opts.track;
|
|
13
|
-
this.sourceSettings = this.source.getSettings();
|
|
14
|
-
this.sourceDummy = opts.element;
|
|
15
|
-
if (!(this.sourceDummy instanceof HTMLVideoElement)) {
|
|
16
|
-
throw TypeError('Currently only video transformers are supported');
|
|
17
|
-
}
|
|
18
|
-
// TODO explore if we can do all the processing work in a webworker
|
|
19
|
-
this.processor = new MediaStreamTrackProcessor({ track: this.source });
|
|
20
|
-
this.trackGenerator = new MediaStreamTrackGenerator({ kind: 'video' });
|
|
21
|
-
this.canvas = new OffscreenCanvas((_a = this.sourceSettings.width) !== null && _a !== void 0 ? _a : 300, (_b = this.sourceSettings.height) !== null && _b !== void 0 ? _b : 300);
|
|
22
|
-
let readableStream = this.processor.readable;
|
|
23
|
-
for (const transformer of this.transformers) {
|
|
24
|
-
transformer.init({
|
|
25
|
-
outputCanvas: this.canvas,
|
|
26
|
-
inputElement: this.sourceDummy,
|
|
27
|
-
});
|
|
28
|
-
readableStream = readableStream.pipeThrough(transformer.transformer);
|
|
29
|
-
}
|
|
30
|
-
readableStream.pipeTo(this.trackGenerator.writable);
|
|
31
|
-
this.processedTrack = this.trackGenerator;
|
|
32
|
-
}
|
|
33
|
-
async destroy() {
|
|
34
|
-
var _a;
|
|
35
|
-
for (const transformer of this.transformers) {
|
|
36
|
-
await transformer.destroy();
|
|
37
|
-
}
|
|
38
|
-
(_a = this.trackGenerator) === null || _a === void 0 ? void 0 : _a.stop();
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
//# sourceMappingURL=ProcessorPipeline.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ProcessorPipeline.js","sourceRoot":"","sources":["../../src/ProcessorPipeline.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACpC,MAAM,KAAK,WAAW;QACpB,OAAO,CACL,OAAO,yBAAyB,KAAK,WAAW;YAChD,OAAO,yBAAyB,KAAK,WAAW,CACjD,CAAC;IACJ,CAAC;IAoBD,YAAY,YAA0C,EAAE,IAAY;QAClE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAkC;;QAC3C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAA8B,CAAC;QAClD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QAChD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC;QAChC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,YAAY,gBAAgB,CAAC,EAAE;YACnD,MAAM,SAAS,CAAC,iDAAiD,CAAC,CAAC;SACpE;QACD,mEAAmE;QACnE,IAAI,CAAC,SAAS,GAAG,IAAI,yBAAyB,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACvE,IAAI,CAAC,cAAc,GAAG,IAAI,yBAAyB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAEvE,IAAI,CAAC,MAAM,GAAG,IAAI,eAAe,CAC/B,MAAA,IAAI,CAAC,cAAc,CAAC,KAAK,mCAAI,GAAG,EAChC,MAAA,IAAI,CAAC,cAAc,CAAC,MAAM,mCAAI,GAAG,CAClC,CAAC;QAEF,IAAI,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;QAC7C,KAAK,MAAM,WAAW,IAAI,IAAI,CAAC,YAAY,EAAE;YAC3C,WAAW,CAAC,IAAI,CAAC;gBACf,YAAY,EAAE,IAAI,CAAC,MAAM;gBACzB,YAAY,EAAE,IAAI,CAAC,WAAY;aAChC,CAAC,CAAC;YACH,cAAc,GAAG,cAAc,CAAC,WAAW,CAAC,WAAY,CAAC,WAAY,CAAC,CAAC;SACxE;QACD,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QACpD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAuC,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,OAAO;;QACX,KAAK,MAAM,WAAW,IAAI,IAAI,CAAC,YAAY,EAAE;YAC3C,MAAM,WAAW,CAAC,OAAO,EAAE,CAAC;SAC7B;QACD,MAAA,IAAI,CAAC,cAAc,0CAAE,IAAI,EAAE,CAAC;IAC9B,CAAC;CACF"}
|
package/dist/src/index.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import ProcessorPipeline from './ProcessorPipeline';
|
|
2
|
-
import BackgroundTransformer from './transformers/BackgroundTransformer';
|
|
3
|
-
import DummyTransformer from './transformers/DummyTransformer';
|
|
4
|
-
export const BackgroundBlur = (blurRadius = 10) => {
|
|
5
|
-
const isPipelineSupported = ProcessorPipeline.isSupported && BackgroundTransformer.isSupported;
|
|
6
|
-
if (!isPipelineSupported) {
|
|
7
|
-
throw new Error('pipeline is not supported in this browser');
|
|
8
|
-
}
|
|
9
|
-
const pipeline = new ProcessorPipeline([new BackgroundTransformer({ blurRadius })], 'background-blur');
|
|
10
|
-
return pipeline;
|
|
11
|
-
};
|
|
12
|
-
export const VirtualBackground = (imagePath) => {
|
|
13
|
-
const isPipelineSupported = ProcessorPipeline.isSupported && BackgroundTransformer.isSupported;
|
|
14
|
-
if (!isPipelineSupported) {
|
|
15
|
-
throw new Error('pipeline is not supported in this browser');
|
|
16
|
-
}
|
|
17
|
-
const pipeline = new ProcessorPipeline([new BackgroundTransformer({ imagePath })], 'virtual-background');
|
|
18
|
-
return pipeline;
|
|
19
|
-
};
|
|
20
|
-
export const Dummy = () => {
|
|
21
|
-
const isPipelineSupported = ProcessorPipeline.isSupported && BackgroundTransformer.isSupported;
|
|
22
|
-
if (!isPipelineSupported) {
|
|
23
|
-
throw new Error('pipeline is not supported in this browser');
|
|
24
|
-
}
|
|
25
|
-
const pipeline = new ProcessorPipeline([new DummyTransformer()], 'dummy');
|
|
26
|
-
return pipeline;
|
|
27
|
-
};
|
|
28
|
-
//# sourceMappingURL=index.js.map
|
package/dist/src/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AACpD,OAAO,qBAAqB,MAAM,sCAAsC,CAAC;AACzE,OAAO,gBAAgB,MAAM,iCAAiC,CAAC;AAE/D,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,aAAqB,EAAE,EAAE,EAAE;IACxD,MAAM,mBAAmB,GAAG,iBAAiB,CAAC,WAAW,IAAI,qBAAqB,CAAC,WAAW,CAAC;IAC/F,IAAI,CAAC,mBAAmB,EAAE;QACxB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;KAC9D;IACD,MAAM,QAAQ,GAAG,IAAI,iBAAiB,CACpC,CAAC,IAAI,qBAAqB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,EAC3C,iBAAiB,CAClB,CAAC;IACF,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,SAAiB,EAAE,EAAE;IACrD,MAAM,mBAAmB,GAAG,iBAAiB,CAAC,WAAW,IAAI,qBAAqB,CAAC,WAAW,CAAC;IAC/F,IAAI,CAAC,mBAAmB,EAAE;QACxB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;KAC9D;IACD,MAAM,QAAQ,GAAG,IAAI,iBAAiB,CACpC,CAAC,IAAI,qBAAqB,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,EAC1C,oBAAoB,CACrB,CAAC;IACF,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG,GAAG,EAAE;IACxB,MAAM,mBAAmB,GAAG,iBAAiB,CAAC,WAAW,IAAI,qBAAqB,CAAC,WAAW,CAAC;IAC/F,IAAI,CAAC,mBAAmB,EAAE;QACxB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;KAC9D;IACD,MAAM,QAAQ,GAAG,IAAI,iBAAiB,CAAC,CAAC,IAAI,gBAAgB,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;IAC1E,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC"}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import * as vision from '@mediapipe/tasks-vision';
|
|
2
|
-
import * as pkg from '../../package.json';
|
|
3
|
-
import VideoTransformer from './VideoTransformer';
|
|
4
|
-
export default class BackgroundProcessor extends VideoTransformer {
|
|
5
|
-
static get isSupported() {
|
|
6
|
-
return typeof OffscreenCanvas !== 'undefined';
|
|
7
|
-
}
|
|
8
|
-
constructor(opts) {
|
|
9
|
-
super();
|
|
10
|
-
this.backgroundImage = null;
|
|
11
|
-
if (opts.blurRadius) {
|
|
12
|
-
this.blurRadius = opts.blurRadius;
|
|
13
|
-
}
|
|
14
|
-
else if (opts.imagePath) {
|
|
15
|
-
this.loadBackground(opts.imagePath);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
async init({ outputCanvas, inputElement: inputVideo }) {
|
|
19
|
-
super.init({ outputCanvas, inputElement: inputVideo });
|
|
20
|
-
const fileSet = await vision.FilesetResolver.forVisionTasks(`https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@${pkg.dependencies['@mediapipe/tasks-vision']}/wasm`);
|
|
21
|
-
this.imageSegmenter = await vision.ImageSegmenter.createFromOptions(fileSet, {
|
|
22
|
-
baseOptions: {
|
|
23
|
-
modelAssetPath: 'https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_segmenter/float16/latest/selfie_segmenter.tflite',
|
|
24
|
-
delegate: 'CPU',
|
|
25
|
-
},
|
|
26
|
-
runningMode: 'VIDEO',
|
|
27
|
-
outputCategoryMask: true,
|
|
28
|
-
outputConfidenceMasks: false,
|
|
29
|
-
});
|
|
30
|
-
// this.loadBackground(opts.backgroundUrl).catch((e) => console.error(e));
|
|
31
|
-
this.sendFramesContinuouslyForSegmentation(this.inputVideo);
|
|
32
|
-
}
|
|
33
|
-
async destroy() {
|
|
34
|
-
var _a;
|
|
35
|
-
await super.destroy();
|
|
36
|
-
await ((_a = this.imageSegmenter) === null || _a === void 0 ? void 0 : _a.close());
|
|
37
|
-
this.backgroundImage = null;
|
|
38
|
-
}
|
|
39
|
-
async sendFramesContinuouslyForSegmentation(videoEl) {
|
|
40
|
-
var _a;
|
|
41
|
-
if (!this.isDisabled) {
|
|
42
|
-
if (videoEl.videoWidth > 0 && videoEl.videoHeight > 0) {
|
|
43
|
-
let startTimeMs = performance.now();
|
|
44
|
-
(_a = this.imageSegmenter) === null || _a === void 0 ? void 0 : _a.segmentForVideo(videoEl, startTimeMs, (result) => (this.segmentationResults = result));
|
|
45
|
-
}
|
|
46
|
-
videoEl.requestVideoFrameCallback(() => {
|
|
47
|
-
this.sendFramesContinuouslyForSegmentation(videoEl);
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
async loadBackground(path) {
|
|
52
|
-
const img = new Image();
|
|
53
|
-
await new Promise((resolve, reject) => {
|
|
54
|
-
img.crossOrigin = 'Anonymous';
|
|
55
|
-
img.onload = () => resolve(img);
|
|
56
|
-
img.onerror = (err) => reject(err);
|
|
57
|
-
img.src = path;
|
|
58
|
-
});
|
|
59
|
-
const imageData = await createImageBitmap(img);
|
|
60
|
-
this.backgroundImage = imageData;
|
|
61
|
-
}
|
|
62
|
-
async transform(frame, controller) {
|
|
63
|
-
if (this.isDisabled) {
|
|
64
|
-
controller.enqueue(frame);
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
if (!this.canvas) {
|
|
68
|
-
throw TypeError('Canvas needs to be initialized first');
|
|
69
|
-
}
|
|
70
|
-
if (this.blurRadius) {
|
|
71
|
-
await this.blurBackground(frame);
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
await this.drawVirtualBackground(frame);
|
|
75
|
-
}
|
|
76
|
-
const newFrame = new VideoFrame(this.canvas, {
|
|
77
|
-
timestamp: frame.timestamp || Date.now(),
|
|
78
|
-
});
|
|
79
|
-
frame.close();
|
|
80
|
-
controller.enqueue(newFrame);
|
|
81
|
-
}
|
|
82
|
-
async drawVirtualBackground(frame) {
|
|
83
|
-
var _a;
|
|
84
|
-
if (!this.canvas || !this.ctx || !this.segmentationResults || !this.inputVideo)
|
|
85
|
-
return;
|
|
86
|
-
// this.ctx.save();
|
|
87
|
-
// this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
88
|
-
if ((_a = this.segmentationResults) === null || _a === void 0 ? void 0 : _a.categoryMask) {
|
|
89
|
-
this.ctx.filter = 'blur(3px)';
|
|
90
|
-
this.ctx.globalCompositeOperation = 'copy';
|
|
91
|
-
const bitmap = await maskToBitmap(this.segmentationResults.categoryMask, this.inputVideo.videoWidth, this.inputVideo.videoHeight);
|
|
92
|
-
this.ctx.drawImage(bitmap, 0, 0);
|
|
93
|
-
this.ctx.filter = 'none';
|
|
94
|
-
this.ctx.globalCompositeOperation = 'source-in';
|
|
95
|
-
if (this.backgroundImage) {
|
|
96
|
-
this.ctx.drawImage(this.backgroundImage, 0, 0, this.backgroundImage.width, this.backgroundImage.height, 0, 0, this.canvas.width, this.canvas.height);
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
this.ctx.fillStyle = '#00FF00';
|
|
100
|
-
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
101
|
-
}
|
|
102
|
-
this.ctx.globalCompositeOperation = 'destination-over';
|
|
103
|
-
}
|
|
104
|
-
this.ctx.drawImage(frame, 0, 0, this.canvas.width, this.canvas.height);
|
|
105
|
-
// this.ctx.restore();
|
|
106
|
-
}
|
|
107
|
-
async blurBackground(frame) {
|
|
108
|
-
var _a, _b;
|
|
109
|
-
if (!this.ctx ||
|
|
110
|
-
!this.canvas ||
|
|
111
|
-
!((_b = (_a = this.segmentationResults) === null || _a === void 0 ? void 0 : _a.categoryMask) === null || _b === void 0 ? void 0 : _b.canvas) ||
|
|
112
|
-
!this.inputVideo) {
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
this.ctx.save();
|
|
116
|
-
this.ctx.globalCompositeOperation = 'copy';
|
|
117
|
-
const bitmap = await maskToBitmap(this.segmentationResults.categoryMask, this.inputVideo.videoWidth, this.inputVideo.videoHeight);
|
|
118
|
-
this.ctx.filter = 'blur(3px)';
|
|
119
|
-
this.ctx.globalCompositeOperation = 'copy';
|
|
120
|
-
this.ctx.drawImage(bitmap, 0, 0);
|
|
121
|
-
this.ctx.filter = 'none';
|
|
122
|
-
this.ctx.globalCompositeOperation = 'source-out';
|
|
123
|
-
this.ctx.drawImage(frame, 0, 0, this.canvas.width, this.canvas.height);
|
|
124
|
-
this.ctx.globalCompositeOperation = 'destination-over';
|
|
125
|
-
this.ctx.filter = `blur(${this.blurRadius}px)`;
|
|
126
|
-
this.ctx.drawImage(frame, 0, 0);
|
|
127
|
-
this.ctx.restore();
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
function maskToBitmap(mask, videoWidth, videoHeight) {
|
|
131
|
-
const dataArray = new Uint8ClampedArray(videoWidth * videoHeight * 4);
|
|
132
|
-
const result = mask.getAsUint8Array();
|
|
133
|
-
for (let i = 0; i < result.length; i += 1) {
|
|
134
|
-
dataArray[i * 4] = result[i];
|
|
135
|
-
dataArray[i * 4 + 1] = result[i];
|
|
136
|
-
dataArray[i * 4 + 2] = result[i];
|
|
137
|
-
dataArray[i * 4 + 3] = result[i];
|
|
138
|
-
}
|
|
139
|
-
const dataNew = new ImageData(dataArray, videoWidth, videoHeight);
|
|
140
|
-
return createImageBitmap(dataNew);
|
|
141
|
-
}
|
|
142
|
-
//# sourceMappingURL=BackgroundTransformer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"BackgroundTransformer.js","sourceRoot":"","sources":["../../../src/transformers/BackgroundTransformer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import VideoTransformer from './VideoTransformer';
|
|
2
|
-
export default class DummyTransformer extends VideoTransformer {
|
|
3
|
-
async transform(frame, controller) {
|
|
4
|
-
controller.enqueue(frame);
|
|
5
|
-
}
|
|
6
|
-
async destroy() {
|
|
7
|
-
// nothing to do
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
//# sourceMappingURL=DummyTransformer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DummyTransformer.js","sourceRoot":"","sources":["../../../src/transformers/DummyTransformer.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,MAAM,oBAAoB,CAAC;AAElD,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,gBAAgB;IAC5D,KAAK,CAAC,SAAS,CAAC,KAAiB,EAAE,UAAwD;QACzF,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,OAAO;QACX,gBAAgB;IAClB,CAAC;CACF"}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Holistic } from '@mediapipe/holistic';
|
|
2
|
-
import VideoTransformer from './VideoTransformer';
|
|
3
|
-
export default class MediaPipeHolisticTrackerTransformer extends VideoTransformer {
|
|
4
|
-
static get isSupported() {
|
|
5
|
-
return true;
|
|
6
|
-
}
|
|
7
|
-
constructor({ holisticOptions, callback }) {
|
|
8
|
-
super();
|
|
9
|
-
this.callback = callback || (() => null);
|
|
10
|
-
this.holisticOptions = holisticOptions || {};
|
|
11
|
-
}
|
|
12
|
-
init({ inputElement: inputVideo, outputCanvas }) {
|
|
13
|
-
super.init({ outputCanvas, inputElement: inputVideo });
|
|
14
|
-
this.holistic = new Holistic({
|
|
15
|
-
locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/holistic/${file}`,
|
|
16
|
-
});
|
|
17
|
-
this.holistic.setOptions(this.holisticOptions);
|
|
18
|
-
this.holistic.onResults((r) => {
|
|
19
|
-
this.callback(r);
|
|
20
|
-
});
|
|
21
|
-
this.sendFramesContinuouslyForTracking(this.inputVideo);
|
|
22
|
-
}
|
|
23
|
-
async destroy() {
|
|
24
|
-
var _a;
|
|
25
|
-
this.callback = () => null;
|
|
26
|
-
await super.destroy();
|
|
27
|
-
await ((_a = this.holistic) === null || _a === void 0 ? void 0 : _a.close());
|
|
28
|
-
}
|
|
29
|
-
async transform() {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
async sendFramesContinuouslyForTracking(videoEl) {
|
|
33
|
-
var _a;
|
|
34
|
-
if (!this.isDisabled) {
|
|
35
|
-
if (videoEl.videoWidth > 0 && videoEl.videoHeight > 0) {
|
|
36
|
-
await ((_a = this.holistic) === null || _a === void 0 ? void 0 : _a.send({ image: videoEl }));
|
|
37
|
-
}
|
|
38
|
-
videoEl.requestVideoFrameCallback(() => {
|
|
39
|
-
this.sendFramesContinuouslyForTracking(videoEl);
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
//# sourceMappingURL=MediaPipeHolisticTrackerTransformer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MediaPipeHolisticTrackerTransformer.js","sourceRoot":"","sources":["../../../src/transformers/MediaPipeHolisticTrackerTransformer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAoB,MAAM,qBAAqB,CAAC;AACjE,OAAO,gBAAgB,MAAM,oBAAoB,CAAC;AAQlD,MAAM,CAAC,OAAO,OAAO,mCAAoC,SAAQ,gBAAgB;IAKxE,MAAM,KAAK,WAAW;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,YAAY,EAAE,eAAe,EAAE,QAAQ,EAA8C;QACnF,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,QAAQ,GAAG,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,eAAe,GAAG,eAAe,IAAI,EAAE,CAAC;IAC/C,CAAC;IAED,IAAI,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAA+B;QAC1E,KAAK,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC,CAAC;QAEvD,IAAI,CAAC,QAAQ,GAAG,IAAI,QAAQ,CAAC;YAC3B,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,oDAAoD,IAAI,EAAE;SACjF,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC/C,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE;YAC5B,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,iCAAiC,CAAC,IAAI,CAAC,UAAW,CAAC,CAAC;IAC3D,CAAC;IAED,KAAK,CAAC,OAAO;;QACX,IAAI,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC;QAC3B,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC;QACtB,MAAM,CAAA,MAAA,IAAI,CAAC,QAAQ,0CAAE,KAAK,EAAE,CAAA,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,SAAS;QACb,OAAO;IACT,CAAC;IAED,KAAK,CAAC,iCAAiC,CAAC,OAAyB;;QAC/D,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE;YACpB,IAAI,OAAO,CAAC,UAAU,GAAG,CAAC,IAAI,OAAO,CAAC,WAAW,GAAG,CAAC,EAAE;gBACrD,MAAM,CAAA,MAAA,IAAI,CAAC,QAAQ,0CAAE,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA,CAAC;aAC/C;YAED,OAAO,CAAC,yBAAyB,CAAC,GAAG,EAAE;gBACrC,IAAI,CAAC,iCAAiC,CAAC,OAAO,CAAC,CAAC;YAClD,CAAC,CAAC,CAAC;SACJ;IACH,CAAC;CACF"}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
export default class VideoTransformer {
|
|
2
|
-
constructor() {
|
|
3
|
-
this.isDisabled = false;
|
|
4
|
-
}
|
|
5
|
-
init({ outputCanvas, inputElement: inputVideo }) {
|
|
6
|
-
var _a;
|
|
7
|
-
if (!(inputVideo instanceof HTMLVideoElement)) {
|
|
8
|
-
throw TypeError('Video transformer needs a HTMLVideoElement as input');
|
|
9
|
-
}
|
|
10
|
-
this.transformer = new TransformStream({
|
|
11
|
-
transform: (frame, controller) => this.transform(frame, controller),
|
|
12
|
-
});
|
|
13
|
-
this.canvas = outputCanvas || null;
|
|
14
|
-
if (outputCanvas) {
|
|
15
|
-
this.ctx = ((_a = this.canvas) === null || _a === void 0 ? void 0 : _a.getContext('2d', { readFrequently: true })) || undefined;
|
|
16
|
-
}
|
|
17
|
-
this.inputVideo = inputVideo;
|
|
18
|
-
this.isDisabled = false;
|
|
19
|
-
}
|
|
20
|
-
async destroy() {
|
|
21
|
-
this.isDisabled = true;
|
|
22
|
-
this.canvas = undefined;
|
|
23
|
-
this.ctx = undefined;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
//# sourceMappingURL=VideoTransformer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"VideoTransformer.js","sourceRoot":"","sources":["../../../src/transformers/VideoTransformer.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,OAAgB,gBAAgB;IAA9C;QASY,eAAU,GAAa,KAAK,CAAC;IA2BzC,CAAC;IAzBC,IAAI,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAA+B;;QAC1E,IAAI,CAAC,CAAC,UAAU,YAAY,gBAAgB,CAAC,EAAE;YAC7C,MAAM,SAAS,CAAC,qDAAqD,CAAC,CAAC;SACxE;QACD,IAAI,CAAC,WAAW,GAAG,IAAI,eAAe,CAAC;YACrC,SAAS,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,UAAU,CAAC;SACpE,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,GAAG,YAAY,IAAI,IAAI,CAAC;QACnC,IAAI,YAAY,EAAE;YAChB,IAAI,CAAC,GAAG,GAAG,CAAA,MAAA,IAAI,CAAC,MAAM,0CAAE,UAAU,CAAC,IAAI,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,KAAI,SAAS,CAAC;SACjF;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,IAAI,CAAC,GAAG,GAAG,SAAS,CAAC;IACvB,CAAC;CAMF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/transformers/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,SAAS,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/transformers/types.ts"],"names":[],"mappings":""}
|
package/dist/src/utils.js
DELETED
package/dist/src/utils.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,yBAAyB,KAAK,WAAW,CAAC;AAClF,MAAM,CAAC,MAAM,uBAAuB,GAAG,OAAO,eAAe,KAAK,WAAW,CAAC"}
|