4

We are a team of 5 developers working on a video rendering implementation. This implementation consists out of two parts.

  1. A live video preview in the browser using angular + konva.
  2. A node.js (node 14) serverless (AWS lambda container) implementation using konva-node that pipes frames to ffmpeg for rendering a mp4 video in higher quality for later download.

Both ways are working for us. Now we extracted the parts of the animation that are the same for frontend and backend implementation to an internal library. We imported them in BE and FE. That also works nicely for most parts.

We noticed here that konva-node is deprecated since a short time. Documentation says to use canvas + konva instead on node.js. But this just doesn't work. If we don't use konva-node we cannot create a stage without a 'container' value. Also we cannot create a raw image buffer anymore, because stage.toCanvas() actually returns a HTMLCanvas, which does not have this functionality.

  • So what does konva-node actually do to konva API?
  • Is node.js still supported after deprecation of konva-node?
  • How can we get toBuffer() and new Stage() functionality without konva-node in node.js?

backend (konva-node)

import konvaNode = require('konva-node');  
this.stage = new konvaNode.Stage({
     width: stageSize.width,
     height: stageSize.height
});

// [draw stuff on stage here]

// create raw frames to pipe to ffmpeg
const frame = await this.stage.toCanvas();
const buffer: Buffer = frame.toBuffer('raw');

frontend (konva)

import Konva from 'konva';
this.stage = new Konva.Stage({
     width: stageSize.width,
     height: stageSize.height,
     // connect stage to html element in browser
     container: 'container'
});

// [draw stuff on stage here]

Finally in an ideal world (if we could just Konva in frontend and backend without konva-node the following should be possible for a shared code.

loading images

public static loadKonvaImage(element, canvas): Promise<any> {
    return new Promise(resolve => {
        let image;
        if (canvas) {
            // node.js canvas image
            image = new canvas.Image();
        } else {
            // html browser image
            image = new Image();
        }
        image.src = element.url;
        image.onload = function () {
            const konvaImage = new Konva.Image(
            {image, element.width, element.height});
            konvaImage.cache();
            resolve(konvaImage);
        };
    });
}

Many props to the developer for the good work. We would look forward to use the library for a long time, but how can we if some core functionality that we rely on is outdated shortly after we started the project?

Another stack overflow answer mentioned Konva.isBrowser = false;. Maybe this is used to differentiate between a browser and a node canvas?

flohall
  • 967
  • 10
  • 19

2 Answers2

1

So what does konva-node actually do to konva API?

It slightly patches Konva code to use canvas nodejs library to use 2d canvas API. So, Konva will not use browser DOM API.

Is node.js still supported after deprecation of konva-node?

Yes. https://github.com/konvajs/konva#4-nodejs-env

How can we get toBuffer() and new Stage() functionality without konva-node in node.js?

You can try to use this:

const canvas = layer.getNativeCanvasElement();
const buffer = canvas.toBuffer();
lavrton
  • 18,973
  • 4
  • 30
  • 63
  • Thanks for your reply. Luckily we already found another way of doing so and I was missing a hint on how to create the `Konva.Stage` from your side. I hope the answer given by myself can inspire you for improvements in the interface type specifications. Anyway we are super happy now with Konva and its awesome speed. – flohall Oct 01 '21 at 16:25
0

We have solved the problems we had the following way:

create stage (shared between Be+FE)

public static createStage(stageWidth: number, stageHeight: number, canvas?: any): Konva.Stage {
    const stage = new Konva.Stage({
        width: stageWidth,
        height: stageHeight,
        container: canvas ? canvas : 'container'
    });
    return stage;
}

create raw image buffer (BE)

const frame: any = await this.stage.toCanvas();
const buffer: Buffer = frame.toBuffer('raw');

loading images (shared between Be+FE)

public static loadKonvaImage(element, canvas?: any): Promise<Konva.Image> {
   return new Promise(resolve => {
       const image = canvas ? new canvas.Image() : new Image();
       image.src = element.url;
       image.onload = function () {
           const konvaImage = new Konva.Image(
           {image, element.width, element.height});
           konvaImage.cache();
           resolve(konvaImage);
       };
   });
}

Two things we had to do.

  1. We have rewritten our whole backend and library code to use ESM modules and we got rid of konva-node and konva 7 in general.
  2. We defined the node module canvas in all places as any. It seems like Konva accepts more inputs than expected and like specified in the type interfaces of the classes. canvas is only installed in the backend and inserted in some library methods like shown above.

@lavrton nice to hear from you. Your answer might also work for getting the Buffer, but you didn't answer on how to create the stage. Luckily we found a solution for both issues.

flohall
  • 967
  • 10
  • 19
  • is there a way to render video stream into Image in node env? – Denis Ivanov Dec 17 '21 at 05:29
  • @DenisIvanov - I assume that there are multiple ways to do it - my personal recommendation would be to use ffmpeg - see examples here: https://stackoverflow.com/questions/40088222/ffmpeg-convert-video-to-images. Also I would prefer to use "child_process" to execute ffmpeg from node.js instead of other wrapper libs. This way you can use all ffmpeg commands like they are. Ffmpeg can be used in AWS Lambda, if thats what you want. Either build a container or use a lambda layer. – flohall Dec 28 '21 at 16:32