@livekit/track-processors 0.5.4 → 0.5.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 +202 -154
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +202 -154
- package/dist/index.mjs.map +1 -1
- package/dist/src/transformers/BackgroundTransformer.d.ts +3 -1
- package/dist/src/webgl/index.d.ts +2 -2
- package/dist/src/webgl/shader-programs/downSampler.d.ts +12 -0
- package/dist/src/webgl/utils.d.ts +2 -0
- package/package.json +1 -1
- package/src/transformers/BackgroundTransformer.ts +42 -28
- package/src/webgl/index.ts +98 -43
- package/src/webgl/shader-programs/blurShader.ts +5 -37
- package/src/webgl/shader-programs/boxBlurShader.ts +4 -42
- package/src/webgl/shader-programs/compositeShader.ts +5 -43
- package/src/webgl/shader-programs/downSampler.ts +94 -0
- package/src/webgl/utils.ts +32 -0
package/dist/index.js
CHANGED
|
@@ -264,6 +264,28 @@ function initTexture(gl, texIndex) {
|
|
|
264
264
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
265
265
|
return texture;
|
|
266
266
|
}
|
|
267
|
+
function createShader(gl, type, source) {
|
|
268
|
+
const shader = gl.createShader(type);
|
|
269
|
+
gl.shaderSource(shader, source);
|
|
270
|
+
gl.compileShader(shader);
|
|
271
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
272
|
+
console.error("Shader compile failed:", gl.getShaderInfoLog(shader));
|
|
273
|
+
gl.deleteShader(shader);
|
|
274
|
+
throw new Error("Shader compile failed");
|
|
275
|
+
}
|
|
276
|
+
return shader;
|
|
277
|
+
}
|
|
278
|
+
function createProgram(gl, vs, fs) {
|
|
279
|
+
const program = gl.createProgram();
|
|
280
|
+
gl.attachShader(program, vs);
|
|
281
|
+
gl.attachShader(program, fs);
|
|
282
|
+
gl.linkProgram(program);
|
|
283
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
284
|
+
console.error("Program link failed:", gl.getProgramInfoLog(program));
|
|
285
|
+
throw new Error("Program link failed");
|
|
286
|
+
}
|
|
287
|
+
return program;
|
|
288
|
+
}
|
|
267
289
|
function createFramebuffer(gl, texture, width, height) {
|
|
268
290
|
const framebuffer = gl.createFramebuffer();
|
|
269
291
|
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
@@ -328,7 +350,7 @@ var vertexShaderSource = (flipY = true) => `#version 300 es
|
|
|
328
350
|
|
|
329
351
|
// src/webgl/shader-programs/blurShader.ts
|
|
330
352
|
var blurFragmentShader = glsl`#version 300 es
|
|
331
|
-
precision
|
|
353
|
+
precision mediump float;
|
|
332
354
|
in vec2 texCoords;
|
|
333
355
|
uniform sampler2D u_texture;
|
|
334
356
|
uniform vec2 u_texelSize;
|
|
@@ -357,33 +379,9 @@ var blurFragmentShader = glsl`#version 300 es
|
|
|
357
379
|
}
|
|
358
380
|
`;
|
|
359
381
|
function createBlurProgram(gl) {
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
}
|
|
364
|
-
gl.shaderSource(blurFrag, blurFragmentShader);
|
|
365
|
-
gl.compileShader(blurFrag);
|
|
366
|
-
if (!gl.getShaderParameter(blurFrag, gl.COMPILE_STATUS)) {
|
|
367
|
-
const info = gl.getShaderInfoLog(blurFrag);
|
|
368
|
-
throw Error(`Failed to compile blur shader: ${info}`);
|
|
369
|
-
}
|
|
370
|
-
const blurVertexShader = gl.createShader(gl.VERTEX_SHADER);
|
|
371
|
-
if (!blurVertexShader) {
|
|
372
|
-
throw Error("cannot create blur vertex shader");
|
|
373
|
-
}
|
|
374
|
-
gl.shaderSource(blurVertexShader, vertexShaderSource());
|
|
375
|
-
gl.compileShader(blurVertexShader);
|
|
376
|
-
const blurProgram = gl.createProgram();
|
|
377
|
-
if (!blurProgram) {
|
|
378
|
-
throw Error("cannot create blur program");
|
|
379
|
-
}
|
|
380
|
-
gl.attachShader(blurProgram, blurVertexShader);
|
|
381
|
-
gl.attachShader(blurProgram, blurFrag);
|
|
382
|
-
gl.linkProgram(blurProgram);
|
|
383
|
-
if (!gl.getProgramParameter(blurProgram, gl.LINK_STATUS)) {
|
|
384
|
-
const info = gl.getProgramInfoLog(blurProgram);
|
|
385
|
-
throw Error(`Failed to link blur program: ${info}`);
|
|
386
|
-
}
|
|
382
|
+
const blurVertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource());
|
|
383
|
+
const blurFrag = createShader(gl, gl.FRAGMENT_SHADER, blurFragmentShader);
|
|
384
|
+
const blurProgram = createProgram(gl, blurVertexShader, blurFrag);
|
|
387
385
|
const blurUniforms = {
|
|
388
386
|
position: gl.getAttribLocation(blurProgram, "position"),
|
|
389
387
|
texture: gl.getUniformLocation(blurProgram, "u_texture"),
|
|
@@ -458,37 +456,9 @@ void main() {
|
|
|
458
456
|
}
|
|
459
457
|
`;
|
|
460
458
|
function createBoxBlurProgram(gl) {
|
|
461
|
-
const vertexShader =
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
}
|
|
465
|
-
gl.shaderSource(vertexShader, vertexShaderSource());
|
|
466
|
-
gl.compileShader(vertexShader);
|
|
467
|
-
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
|
|
468
|
-
const info = gl.getShaderInfoLog(vertexShader);
|
|
469
|
-
throw Error(`Failed to compile vertex shader: ${info}`);
|
|
470
|
-
}
|
|
471
|
-
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
|
|
472
|
-
if (!fragmentShader) {
|
|
473
|
-
throw Error("cannot create fragment shader");
|
|
474
|
-
}
|
|
475
|
-
gl.shaderSource(fragmentShader, boxBlurFragmentShader);
|
|
476
|
-
gl.compileShader(fragmentShader);
|
|
477
|
-
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
|
|
478
|
-
const info = gl.getShaderInfoLog(fragmentShader);
|
|
479
|
-
throw Error(`Failed to compile box blur shader: ${info}`);
|
|
480
|
-
}
|
|
481
|
-
const program = gl.createProgram();
|
|
482
|
-
if (!program) {
|
|
483
|
-
throw Error("cannot create box blur program");
|
|
484
|
-
}
|
|
485
|
-
gl.attachShader(program, vertexShader);
|
|
486
|
-
gl.attachShader(program, fragmentShader);
|
|
487
|
-
gl.linkProgram(program);
|
|
488
|
-
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
489
|
-
const info = gl.getProgramInfoLog(program);
|
|
490
|
-
throw Error(`Failed to link box blur program: ${info}`);
|
|
491
|
-
}
|
|
459
|
+
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource());
|
|
460
|
+
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, boxBlurFragmentShader);
|
|
461
|
+
const program = createProgram(gl, vertexShader, fragmentShader);
|
|
492
462
|
const uniforms = {
|
|
493
463
|
position: gl.getAttribLocation(program, "position"),
|
|
494
464
|
texture: gl.getUniformLocation(program, "u_texture"),
|
|
@@ -506,7 +476,7 @@ function createBoxBlurProgram(gl) {
|
|
|
506
476
|
|
|
507
477
|
// src/webgl/shader-programs/compositeShader.ts
|
|
508
478
|
var compositeFragmentShader = glsl`#version 300 es
|
|
509
|
-
precision
|
|
479
|
+
precision mediump float;
|
|
510
480
|
in vec2 texCoords;
|
|
511
481
|
uniform sampler2D background;
|
|
512
482
|
uniform sampler2D frame;
|
|
@@ -536,37 +506,9 @@ var compositeFragmentShader = glsl`#version 300 es
|
|
|
536
506
|
}
|
|
537
507
|
`;
|
|
538
508
|
function createCompositeProgram(gl) {
|
|
539
|
-
const vertexShader =
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
}
|
|
543
|
-
gl.shaderSource(vertexShader, vertexShaderSource());
|
|
544
|
-
gl.compileShader(vertexShader);
|
|
545
|
-
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
|
|
546
|
-
const info = gl.getShaderInfoLog(vertexShader);
|
|
547
|
-
throw Error(`Failed to compile vertex shader: ${info}`);
|
|
548
|
-
}
|
|
549
|
-
const compositeShader = gl.createShader(gl.FRAGMENT_SHADER);
|
|
550
|
-
if (!compositeShader) {
|
|
551
|
-
throw Error("cannot create fragment shader");
|
|
552
|
-
}
|
|
553
|
-
gl.shaderSource(compositeShader, compositeFragmentShader);
|
|
554
|
-
gl.compileShader(compositeShader);
|
|
555
|
-
if (!gl.getShaderParameter(compositeShader, gl.COMPILE_STATUS)) {
|
|
556
|
-
const info = gl.getShaderInfoLog(compositeShader);
|
|
557
|
-
throw Error(`Failed to compile composite shader: ${info}`);
|
|
558
|
-
}
|
|
559
|
-
const compositeProgram = gl.createProgram();
|
|
560
|
-
if (!compositeProgram) {
|
|
561
|
-
throw Error("cannot create composite program");
|
|
562
|
-
}
|
|
563
|
-
gl.attachShader(compositeProgram, vertexShader);
|
|
564
|
-
gl.attachShader(compositeProgram, compositeShader);
|
|
565
|
-
gl.linkProgram(compositeProgram);
|
|
566
|
-
if (!gl.getProgramParameter(compositeProgram, gl.LINK_STATUS)) {
|
|
567
|
-
const info = gl.getProgramInfoLog(compositeProgram);
|
|
568
|
-
throw Error(`Failed to link composite program: ${info}`);
|
|
569
|
-
}
|
|
509
|
+
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource());
|
|
510
|
+
const compositeShader = createShader(gl, gl.FRAGMENT_SHADER, compositeFragmentShader);
|
|
511
|
+
const compositeProgram = createProgram(gl, vertexShader, compositeShader);
|
|
570
512
|
const attribLocations = {
|
|
571
513
|
position: gl.getAttribLocation(compositeProgram, "position")
|
|
572
514
|
};
|
|
@@ -585,6 +527,63 @@ function createCompositeProgram(gl) {
|
|
|
585
527
|
};
|
|
586
528
|
}
|
|
587
529
|
|
|
530
|
+
// src/webgl/shader-programs/downSampler.ts
|
|
531
|
+
function createDownSampler(gl, width, height) {
|
|
532
|
+
const texture = gl.createTexture();
|
|
533
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
534
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
535
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
536
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
537
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
538
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
539
|
+
const framebuffer = gl.createFramebuffer();
|
|
540
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
541
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
|
542
|
+
const vertexSource = `
|
|
543
|
+
attribute vec2 position;
|
|
544
|
+
varying vec2 v_uv;
|
|
545
|
+
void main() {
|
|
546
|
+
v_uv = (position + 1.0) * 0.5;
|
|
547
|
+
gl_Position = vec4(position, 0.0, 1.0);
|
|
548
|
+
}
|
|
549
|
+
`;
|
|
550
|
+
const fragmentSource = `
|
|
551
|
+
precision mediump float;
|
|
552
|
+
varying vec2 v_uv;
|
|
553
|
+
uniform sampler2D u_texture;
|
|
554
|
+
void main() {
|
|
555
|
+
gl_FragColor = texture2D(u_texture, v_uv);
|
|
556
|
+
}
|
|
557
|
+
`;
|
|
558
|
+
const vertShader = createShader(gl, gl.VERTEX_SHADER, vertexSource);
|
|
559
|
+
const fragShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
560
|
+
const program = createProgram(gl, vertShader, fragShader);
|
|
561
|
+
const uniforms = {
|
|
562
|
+
texture: gl.getUniformLocation(program, "u_texture"),
|
|
563
|
+
position: gl.getAttribLocation(program, "position")
|
|
564
|
+
};
|
|
565
|
+
return {
|
|
566
|
+
framebuffer,
|
|
567
|
+
texture,
|
|
568
|
+
program,
|
|
569
|
+
uniforms
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
function applyDownsampling(gl, inputTexture, downSampler, vertexBuffer, width, height) {
|
|
573
|
+
gl.useProgram(downSampler.program);
|
|
574
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, downSampler.framebuffer);
|
|
575
|
+
gl.viewport(0, 0, width, height);
|
|
576
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
|
|
577
|
+
gl.enableVertexAttribArray(downSampler.uniforms.position);
|
|
578
|
+
gl.vertexAttribPointer(downSampler.uniforms.position, 2, gl.FLOAT, false, 0, 0);
|
|
579
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
580
|
+
gl.bindTexture(gl.TEXTURE_2D, inputTexture);
|
|
581
|
+
gl.uniform1i(downSampler.uniforms.texture, 0);
|
|
582
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
583
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
584
|
+
return downSampler.texture;
|
|
585
|
+
}
|
|
586
|
+
|
|
588
587
|
// src/webgl/index.ts
|
|
589
588
|
var setupWebGL = (canvas) => {
|
|
590
589
|
const gl = canvas.getContext("webgl2", {
|
|
@@ -592,6 +591,8 @@ var setupWebGL = (canvas) => {
|
|
|
592
591
|
premultipliedAlpha: true
|
|
593
592
|
});
|
|
594
593
|
let blurRadius = null;
|
|
594
|
+
let maskBlurRadius = 8;
|
|
595
|
+
const downsampleFactor = 4;
|
|
595
596
|
if (!gl) {
|
|
596
597
|
console.error("Failed to create WebGL context");
|
|
597
598
|
return void 0;
|
|
@@ -620,27 +621,36 @@ var setupWebGL = (canvas) => {
|
|
|
620
621
|
}
|
|
621
622
|
let bgBlurTextures = [];
|
|
622
623
|
let bgBlurFrameBuffers = [];
|
|
623
|
-
let
|
|
624
|
-
let
|
|
624
|
+
let blurredMaskTexture = null;
|
|
625
|
+
let finalMaskTextures = [];
|
|
626
|
+
let readMaskIndex = 0;
|
|
627
|
+
let writeMaskIndex = 1;
|
|
625
628
|
bgBlurTextures.push(initTexture(gl, 3));
|
|
626
629
|
bgBlurTextures.push(initTexture(gl, 4));
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
createFramebuffer(gl, maskBlurTextures[0], canvas.width, canvas.height)
|
|
630
|
+
const bgBlurTextureWidth = Math.floor(canvas.width / downsampleFactor);
|
|
631
|
+
const bgBlurTextureHeight = Math.floor(canvas.height / downsampleFactor);
|
|
632
|
+
const downSampler = createDownSampler(gl, bgBlurTextureWidth, bgBlurTextureHeight);
|
|
633
|
+
bgBlurFrameBuffers.push(
|
|
634
|
+
createFramebuffer(gl, bgBlurTextures[0], bgBlurTextureWidth, bgBlurTextureHeight)
|
|
633
635
|
);
|
|
634
|
-
|
|
635
|
-
createFramebuffer(gl,
|
|
636
|
+
bgBlurFrameBuffers.push(
|
|
637
|
+
createFramebuffer(gl, bgBlurTextures[1], bgBlurTextureWidth, bgBlurTextureHeight)
|
|
636
638
|
);
|
|
639
|
+
const tempMaskTexture = initTexture(gl, 5);
|
|
640
|
+
const tempMaskFrameBuffer = createFramebuffer(gl, tempMaskTexture, canvas.width, canvas.height);
|
|
641
|
+
finalMaskTextures.push(initTexture(gl, 6));
|
|
642
|
+
finalMaskTextures.push(initTexture(gl, 7));
|
|
643
|
+
const finalMaskFrameBuffers = [
|
|
644
|
+
createFramebuffer(gl, finalMaskTextures[0], canvas.width, canvas.height),
|
|
645
|
+
createFramebuffer(gl, finalMaskTextures[1], canvas.width, canvas.height)
|
|
646
|
+
];
|
|
637
647
|
gl.useProgram(compositeProgram);
|
|
638
648
|
gl.uniform1i(bgTextureLocation, 0);
|
|
639
649
|
gl.uniform1i(frameTextureLocation, 1);
|
|
640
650
|
gl.uniform1i(maskTextureLocation, 2);
|
|
641
651
|
let customBackgroundImage = emptyImageData;
|
|
642
|
-
function
|
|
643
|
-
if (frame.codedWidth === 0 ||
|
|
652
|
+
function renderFrame(frame) {
|
|
653
|
+
if (frame.codedWidth === 0 || finalMaskTextures.length === 0) {
|
|
644
654
|
return;
|
|
645
655
|
}
|
|
646
656
|
const width = frame.displayWidth;
|
|
@@ -650,11 +660,19 @@ var setupWebGL = (canvas) => {
|
|
|
650
660
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, frame);
|
|
651
661
|
let backgroundTexture = bgTexture;
|
|
652
662
|
if (blurRadius) {
|
|
653
|
-
|
|
663
|
+
const downSampledFrameTexture = applyDownsampling(
|
|
654
664
|
gl,
|
|
655
665
|
frameTexture,
|
|
656
|
-
|
|
657
|
-
|
|
666
|
+
downSampler,
|
|
667
|
+
vertexBuffer,
|
|
668
|
+
bgBlurTextureWidth,
|
|
669
|
+
bgBlurTextureHeight
|
|
670
|
+
);
|
|
671
|
+
backgroundTexture = applyBlur(
|
|
672
|
+
gl,
|
|
673
|
+
downSampledFrameTexture,
|
|
674
|
+
bgBlurTextureWidth,
|
|
675
|
+
bgBlurTextureHeight,
|
|
658
676
|
blurRadius,
|
|
659
677
|
blurProgram,
|
|
660
678
|
blurUniforms,
|
|
@@ -668,19 +686,6 @@ var setupWebGL = (canvas) => {
|
|
|
668
686
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, customBackgroundImage);
|
|
669
687
|
backgroundTexture = bgTexture;
|
|
670
688
|
}
|
|
671
|
-
const blurredMaskTexture = applyBlur(
|
|
672
|
-
gl,
|
|
673
|
-
mask.getAsWebGLTexture(),
|
|
674
|
-
width,
|
|
675
|
-
height,
|
|
676
|
-
blurRadius || 1,
|
|
677
|
-
// Use a default blur radius if not set
|
|
678
|
-
boxBlurProgram,
|
|
679
|
-
boxBlurUniforms,
|
|
680
|
-
vertexBuffer,
|
|
681
|
-
maskBlurFrameBuffers,
|
|
682
|
-
maskBlurTextures
|
|
683
|
-
);
|
|
684
689
|
gl.viewport(0, 0, width, height);
|
|
685
690
|
gl.clearColor(1, 1, 1, 1);
|
|
686
691
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
@@ -695,10 +700,9 @@ var setupWebGL = (canvas) => {
|
|
|
695
700
|
gl.bindTexture(gl.TEXTURE_2D, frameTexture);
|
|
696
701
|
gl.uniform1i(frameTextureLocation, 1);
|
|
697
702
|
gl.activeTexture(gl.TEXTURE2);
|
|
698
|
-
gl.bindTexture(gl.TEXTURE_2D,
|
|
703
|
+
gl.bindTexture(gl.TEXTURE_2D, finalMaskTextures[readMaskIndex]);
|
|
699
704
|
gl.uniform1i(maskTextureLocation, 2);
|
|
700
705
|
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
701
|
-
mask.close();
|
|
702
706
|
}
|
|
703
707
|
async function setBackgroundImage(image) {
|
|
704
708
|
customBackgroundImage = emptyImageData;
|
|
@@ -718,28 +722,56 @@ var setupWebGL = (canvas) => {
|
|
|
718
722
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, customBackgroundImage);
|
|
719
723
|
}
|
|
720
724
|
function setBlurRadius(radius) {
|
|
721
|
-
blurRadius = radius;
|
|
725
|
+
blurRadius = radius ? Math.max(1, Math.floor(radius / downsampleFactor)) : null;
|
|
722
726
|
setBackgroundImage(null);
|
|
723
727
|
}
|
|
728
|
+
function updateMask(mask) {
|
|
729
|
+
const tempFramebuffers = [tempMaskFrameBuffer, finalMaskFrameBuffers[writeMaskIndex]];
|
|
730
|
+
const tempTextures = [tempMaskTexture, finalMaskTextures[writeMaskIndex]];
|
|
731
|
+
applyBlur(
|
|
732
|
+
gl,
|
|
733
|
+
mask,
|
|
734
|
+
canvas.width,
|
|
735
|
+
canvas.height,
|
|
736
|
+
maskBlurRadius || 1,
|
|
737
|
+
boxBlurProgram,
|
|
738
|
+
boxBlurUniforms,
|
|
739
|
+
vertexBuffer,
|
|
740
|
+
tempFramebuffers,
|
|
741
|
+
tempTextures
|
|
742
|
+
);
|
|
743
|
+
readMaskIndex = writeMaskIndex;
|
|
744
|
+
writeMaskIndex = 1 - writeMaskIndex;
|
|
745
|
+
}
|
|
724
746
|
function cleanup() {
|
|
725
747
|
gl.deleteProgram(compositeProgram);
|
|
726
748
|
gl.deleteProgram(blurProgram);
|
|
727
749
|
gl.deleteProgram(boxBlurProgram);
|
|
728
750
|
gl.deleteTexture(bgTexture);
|
|
729
751
|
gl.deleteTexture(frameTexture);
|
|
752
|
+
gl.deleteTexture(tempMaskTexture);
|
|
753
|
+
gl.deleteFramebuffer(tempMaskFrameBuffer);
|
|
730
754
|
for (const texture of bgBlurTextures) {
|
|
731
755
|
gl.deleteTexture(texture);
|
|
732
756
|
}
|
|
733
757
|
for (const framebuffer of bgBlurFrameBuffers) {
|
|
734
758
|
gl.deleteFramebuffer(framebuffer);
|
|
735
759
|
}
|
|
736
|
-
for (const texture of
|
|
760
|
+
for (const texture of finalMaskTextures) {
|
|
737
761
|
gl.deleteTexture(texture);
|
|
738
762
|
}
|
|
739
|
-
for (const framebuffer of
|
|
763
|
+
for (const framebuffer of finalMaskFrameBuffers) {
|
|
740
764
|
gl.deleteFramebuffer(framebuffer);
|
|
741
765
|
}
|
|
742
766
|
gl.deleteBuffer(vertexBuffer);
|
|
767
|
+
if (blurredMaskTexture) {
|
|
768
|
+
gl.deleteTexture(blurredMaskTexture);
|
|
769
|
+
}
|
|
770
|
+
if (downSampler) {
|
|
771
|
+
gl.deleteTexture(downSampler.texture);
|
|
772
|
+
gl.deleteFramebuffer(downSampler.framebuffer);
|
|
773
|
+
gl.deleteProgram(downSampler.program);
|
|
774
|
+
}
|
|
743
775
|
if (customBackgroundImage) {
|
|
744
776
|
if (customBackgroundImage instanceof ImageBitmap) {
|
|
745
777
|
customBackgroundImage.close();
|
|
@@ -748,10 +780,9 @@ var setupWebGL = (canvas) => {
|
|
|
748
780
|
}
|
|
749
781
|
bgBlurTextures = [];
|
|
750
782
|
bgBlurFrameBuffers = [];
|
|
751
|
-
|
|
752
|
-
maskBlurFrameBuffers = [];
|
|
783
|
+
finalMaskTextures = [];
|
|
753
784
|
}
|
|
754
|
-
return {
|
|
785
|
+
return { renderFrame, updateMask, setBackgroundImage, setBlurRadius, cleanup };
|
|
755
786
|
};
|
|
756
787
|
|
|
757
788
|
// src/transformers/VideoTransformer.ts
|
|
@@ -802,6 +833,7 @@ var BackgroundProcessor = class extends VideoTransformer {
|
|
|
802
833
|
constructor(opts) {
|
|
803
834
|
super();
|
|
804
835
|
this.backgroundImage = null;
|
|
836
|
+
this.segmentationTimeMs = 0;
|
|
805
837
|
this.options = opts;
|
|
806
838
|
this.update(opts);
|
|
807
839
|
}
|
|
@@ -853,7 +885,7 @@ var BackgroundProcessor = class extends VideoTransformer {
|
|
|
853
885
|
(_a = this.gl) == null ? void 0 : _a.setBackgroundImage(imageData);
|
|
854
886
|
}
|
|
855
887
|
async transform(frame, controller) {
|
|
856
|
-
var _a;
|
|
888
|
+
var _a, _b;
|
|
857
889
|
try {
|
|
858
890
|
if (!(frame instanceof VideoFrame) || frame.codedWidth === 0 || frame.codedHeight === 0) {
|
|
859
891
|
console.debug("empty frame detected, ignoring");
|
|
@@ -863,37 +895,49 @@ var BackgroundProcessor = class extends VideoTransformer {
|
|
|
863
895
|
controller.enqueue(frame);
|
|
864
896
|
return;
|
|
865
897
|
}
|
|
898
|
+
const frameTimeMs = Date.now();
|
|
866
899
|
if (!this.canvas) {
|
|
867
900
|
throw TypeError("Canvas needs to be initialized first");
|
|
868
901
|
}
|
|
869
902
|
this.canvas.width = frame.displayWidth;
|
|
870
903
|
this.canvas.height = frame.displayHeight;
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
904
|
+
const segmentationPromise = new Promise((resolve, reject) => {
|
|
905
|
+
var _a2;
|
|
906
|
+
try {
|
|
907
|
+
let segmentationStartTimeMs = performance.now();
|
|
908
|
+
(_a2 = this.imageSegmenter) == null ? void 0 : _a2.segmentForVideo(frame, segmentationStartTimeMs, (result) => {
|
|
909
|
+
this.segmentationTimeMs = performance.now() - segmentationStartTimeMs;
|
|
910
|
+
this.segmentationResults = result;
|
|
911
|
+
this.updateMask(result.categoryMask);
|
|
912
|
+
result.close();
|
|
913
|
+
resolve();
|
|
880
914
|
});
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
processingTimeMs: performance.now() - startTimeMs,
|
|
884
|
-
segmentationTimeMs,
|
|
885
|
-
filterTimeMs
|
|
886
|
-
};
|
|
887
|
-
(_b = (_a2 = this.options).onFrameProcessed) == null ? void 0 : _b.call(_a2, stats);
|
|
888
|
-
controller.enqueue(newFrame);
|
|
889
|
-
} else {
|
|
890
|
-
controller.enqueue(frame);
|
|
915
|
+
} catch (e) {
|
|
916
|
+
reject(e);
|
|
891
917
|
}
|
|
892
|
-
frame.close();
|
|
893
918
|
});
|
|
919
|
+
const filterStartTimeMs = performance.now();
|
|
920
|
+
this.drawFrame(frame);
|
|
921
|
+
if (this.canvas && this.canvas.width > 0 && this.canvas.height > 0) {
|
|
922
|
+
const newFrame = new VideoFrame(this.canvas, {
|
|
923
|
+
timestamp: frame.timestamp || frameTimeMs
|
|
924
|
+
});
|
|
925
|
+
controller.enqueue(newFrame);
|
|
926
|
+
const filterTimeMs = performance.now() - filterStartTimeMs;
|
|
927
|
+
const stats = {
|
|
928
|
+
processingTimeMs: this.segmentationTimeMs + filterTimeMs,
|
|
929
|
+
segmentationTimeMs: this.segmentationTimeMs,
|
|
930
|
+
filterTimeMs
|
|
931
|
+
};
|
|
932
|
+
(_b = (_a = this.options).onFrameProcessed) == null ? void 0 : _b.call(_a, stats);
|
|
933
|
+
} else {
|
|
934
|
+
controller.enqueue(frame);
|
|
935
|
+
}
|
|
936
|
+
await segmentationPromise;
|
|
894
937
|
} catch (e) {
|
|
895
938
|
console.error("Error while processing frame: ", e);
|
|
896
|
-
|
|
939
|
+
} finally {
|
|
940
|
+
frame.close();
|
|
897
941
|
}
|
|
898
942
|
}
|
|
899
943
|
async update(opts) {
|
|
@@ -906,12 +950,16 @@ var BackgroundProcessor = class extends VideoTransformer {
|
|
|
906
950
|
}
|
|
907
951
|
}
|
|
908
952
|
async drawFrame(frame) {
|
|
909
|
-
|
|
953
|
+
var _a;
|
|
954
|
+
if (!this.gl)
|
|
910
955
|
return;
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
956
|
+
(_a = this.gl) == null ? void 0 : _a.renderFrame(frame);
|
|
957
|
+
}
|
|
958
|
+
async updateMask(mask) {
|
|
959
|
+
var _a;
|
|
960
|
+
if (!mask)
|
|
961
|
+
return;
|
|
962
|
+
(_a = this.gl) == null ? void 0 : _a.updateMask(mask.getAsWebGLTexture());
|
|
915
963
|
}
|
|
916
964
|
};
|
|
917
965
|
|