async process()

in src/backgroundfilter/BackgroundFilterProcessor.ts [296:394]


  async process(buffers: VideoFrameBuffer[]): Promise<VideoFrameBuffer[]> {
    if (this.destroyed) {
      return buffers;
    }

    this.frameCounter.frameReceived(buffers[0].framerate);
    this.cpuMonitor.frameReceived();
    const inputCanvas = buffers[0].asCanvasElement() as HTMLCanvasElement;
    if (!inputCanvas) {
      return buffers;
    }

    if (!this.modelInitialized) {
      // return existing buffer, if any
      buffers[0] = this.canvasVideoFrameBuffer;
      return buffers;
    }

    const frameWidth = inputCanvas.width;
    const frameHeight = inputCanvas.height;
    if (frameWidth === 0 || frameHeight === 0) {
      return buffers;
    }

    // on first execution of process the source width will be zero
    if (this.sourceWidth === 0) {
      this.sourceWidth = frameWidth;
      this.sourceHeight = frameHeight;

      // update target canvas size to match the frame size
      this.targetCanvas.width = this.sourceWidth;
      this.targetCanvas.height = this.sourceHeight;

      this.logger.info(`${this.filterType} source width: ${this.sourceWidth}`);
      this.logger.info(`${this.filterType} source height: ${this.sourceHeight}`);

      this.initOnFirstExecution();
    }

    if (this.sourceWidth !== frameWidth || this.sourceHeight !== frameHeight) {
      this.sourceWidth = frameWidth;
      this.sourceHeight = frameHeight;

      // update target canvas size to match the frame size
      this.targetCanvas.width = this.sourceWidth;
      this.targetCanvas.height = this.sourceHeight;
    }

    try {
      this.frameCounter.filterSubmitted();
      let mask = this.mask$.value;

      const hscale = this.spec.model.input.width / inputCanvas.width;
      const vscale = this.spec.model.input.height / inputCanvas.height;

      if (this.scaledCanvas === undefined) {
        this.scaledCanvas = document.createElement('canvas');
        this.scaledCanvas.width = this.spec.model.input.width;
        this.scaledCanvas.height = this.spec.model.input.height;
      }

      const scaledCtx = this.scaledCanvas.getContext('2d');
      scaledCtx.save();
      scaledCtx.scale(hscale, vscale);
      scaledCtx.drawImage(inputCanvas, 0, 0);
      scaledCtx.restore();

      const imageData = scaledCtx.getImageData(
        0,
        0,
        this.scaledCanvas.width,
        this.scaledCanvas.height
      );

      // update the filter mask based on the filter update rate
      if (this.frameNumber % this.videoFramesPerFilterUpdate === 0) {
        // process frame...
        const maskPromise = this.mask$.whenNext();
        this.worker.postMessage({ msg: 'predict', payload: imageData }, [imageData.data.buffer]);
        mask = await maskPromise;
      }
      // It's possible that while waiting for the predict to complete the processor was destroyed.
      // adding a destroyed check here to ensure the implementation of drawImageWithMask does not throw
      // an error due to destroyed processor.
      if (!this.destroyed) {
        this.drawImageWithMask(inputCanvas, mask);
      }
    } catch (error) {
      this.logger.error(`could not process ${this.filterType} frame buffer due to ${error}`);
      return buffers;
    } finally {
      this.frameCounter.filterComplete();
      this.frameNumber++;
    }

    buffers[0] = this.canvasVideoFrameBuffer;

    return buffers;
  }