Download as pdf or txt
Download as pdf or txt
You are on page 1of 16

8/14/23, 11:56 AM How to Use Node.

js Workers for Video Encoding | by Patrick Kalkman | Better Programming

Open in app

This member-only story is on us. Upgrade to access all of Medium.

Member-only story

How to Use Node.js Workers for Video


Encoding
Video encoding is very CPU-intensive, but let’s look at how to do this anyway

Patrick Kalkman · Follow


Published in Better Programming
5 min read · Feb 17, 2020

Listen Share More

Photo by sol on Unsplash

https://betterprogramming.pub/how-to-use-node-js-workers-for-video-encoding-f1379d3933d0 1/16
8/14/23, 11:56 AM How to Use Node.js Workers for Video Encoding | by Patrick Kalkman | Better Programming

Last night, I again worked on my side project, Mini Video Encoder. I added a new
service for encoding video. The Workflow Encoder, as I call the service, uses
Node.js. You may know that video encoding is very CPU-intensive.

In the past, Node.js has never been a good candidate for performing CPU-intensive
tasks. The reason for this is Node.js uses a single thread to execute JavaScript code.
But since the release of 11.7.0, Node.js has a new module called worker_threads .

The Node.js team describes Workers like this:

“Workers (threads) are useful for performing CPU-intensive JavaScript operations. They
will not help much with I/O-intensive work. Node.js’s built-in asynchronous I/O
operations are more efficient than Workers can be.”

Because of this, I implemented the Workflow Encoder using Workers. In this piece,
I describe the implementation.

Encoding Video
Before describing the implementation, I need to talk a bit about why encoding is
necessary for streaming video.

The Workflow Encoder is part of a larger project called Mini Video Encoder (MVE).
MVE is an open-source platform for converting videos. After conversion, a regular
HTTP server can deliver the videos using adaptive streaming.

What is adaptive streaming?


Adaptive streaming changes the bit rate and resolution of a video during playback. A
video player continuously measures the bandwidth of the connection and increases
or decreases the quality of the video.

To make this possible, Workflow Encoder creates many versions of the same video.
Each version has a different bit rate and resolution. This list of bit rates and
resolutions is called an encoding ladder.

Apple recommends the following encoding ladder for a 1080p video. If you encode
your videos, use this ladder — the video will play correctly on Apple devices.

Search this file


https://betterprogramming.pub/how-to-use-node-js-workers-for-video-encoding-f1379d3933d0 2/16
8/14/23, 11:56 AM How to Use Node.js Workers for Video Encoding | by Patrick Kalkman | Better Programming
Search this file…
Apple’s recommended encoding ladder for HLS x264
1 Resolution Bitrate Framerate
2 416 x 234 145 ≤ 30 fps
Using this
3 640 ladder means the Workflow
x 360 365
Encoder has≤to create nine different
30 fps
encodings.
4 768 x 432So you understand why
730we need to use the most efficient method for
≤ 30 fps
5 768 x 432
video encoding. 1100 ≤ 30 fps
6 960 x 540 2000 same as source
71280 x 720 3000 4.2.2. FFmpeg
same as source
The Workflow Encoder uses FFmpeg is an open-source video and
8 1280 x 720 4500 same as source
audio
9
encoder. It also uses the Fluent ffmpeg-API to make interacting with FFmpeg
1920 x 1080 6000 same as source
easier.
10 1920 x 1080 7800 same as source

For testing, I used the 1080p version of Caminandes 3: Llamigos. Caminandes 3 is a


hls_x264_encoding_ladder.csv hosted with ❤ by GitHub view raw
funny little open-source animation video of 2.5 minutes from the Blender Institute.

Caminandes 3: Llamigos

Caminandes 3, the video used for testing video encoding

https://betterprogramming.pub/how-to-use-node-js-workers-for-video-encoding-f1379d3933d0 3/16
8/14/23, 11:56 AM How to Use Node.js Workers for Video Encoding | by Patrick Kalkman | Better Programming

Implementing the Workflow Encoder Using Workers


When the Workflow Engine receives a video job, it first splits the job. For each bit-
rate and resolution combination from the encoding ladder, the Workflow Engine
creates a task.

The Workflow Encoder communicates with the Workflow engine and asks if there’s
a task to perform. The Workflow Encoder uses the REST API of the Workflow engine
for communication.

Creating and starting a Worker


If there’s a task to perform, the Workflow Encoder calls the function startEncoder .

The startEncoder function creates and starts the Worker. It creates the Worker

object by calling the constructor and passing a relative path to a JavaScript file. This
file contains the function that must be executed on a different thread.

1 /*
2 * Encoder Engine
3 */
4
5 // Dependencies

https://betterprogramming.pub/how-to-use-node-js-workers-for-video-encoding-f1379d3933d0 4/16
8/14/23, 11:56 AM How to Use Node.js Workers for Video Encoding | by Patrick Kalkman | Better Programming
6 const workerThreads = require('worker_threads');
Starting the encoder by creating a Worker
7 const superagent = require('superagent');
8
The
9
second argument of the Worker constructor is an options object. I use this to set
const log = require('./log');
workerData
10 to the encodingInstructions
const constants = require('./constants');. This assignment clones
11 and makes it available
const config = require('./config');
encodingInstructions in the Worker function.
12
13 const encoderEngine = {};
The actual function performing the work is encode in encoder.js . The file contains
14
a 15
single function that the Worker
encoderEngine.startEncoder executes.
= function I left out most to focus only
startEncoder(encodingInstructions, cb) { on the
essential
16 part.
log.info('Starting Worker....');
17
18 const worker = new workerThreads.Worker('./lib/encoder/encoder.js', {
1 const { parentPort, workerData } = require("worker_threads");
19 workerData: encodingInstructions,
2
20 });
3 async function encode() {
21 };
4
encoderEngine.js
5 hosted with ❤ by GitHub
const encodingInstructions = workerData; view raw

6
7 ...
8
9 }
10
11 encode();

encoder.js hosted with ❤ by GitHub view raw

The encoder function in encoder.js that’s the Worker thread

On line 5, I get the encodingInstructions by reading workerData . At the start of the


file, I also require parentPort , which the function uses to perform communication
between the main thread and the Worker thread.

How to communicate from the Worker to the main thread


The Worker module allows bidirectional communication between the main and
worker thread. I’d like to get feedback from the Worker thread on the main thread
about the progress.

1 const message = {};


2 message.type = constants.WORKER_MESSAGE_TYPES.PROGRESS;
3 message.message = `Encoding: ${Math.round(info.percent)}%`;
4 parentPort.postMessage(message);

encoder.js hosted with ❤ by GitHub view raw

Send a message from the Worker thread to the main thread

https://betterprogramming.pub/how-to-use-node-js-workers-for-video-encoding-f1379d3933d0 5/16
8/14/23, 11:56 AM How to Use Node.js Workers for Video Encoding | by Patrick Kalkman | Better Programming

Most examples use postMessage to send a string. Instead, I communicate an object. I


want to send different messages and be able to distinguish them in the main thread.

The main thread, on the other end, receives these messages through events. On line
8, worker.on defines the function that receives the message events.

1 encoderEngine.startEncoder = function startEncoder(encodingInstructions, cb) {


2 log.info('Starting Worker....');
3
4 const worker = new workerThreads.Worker('./lib/encoder/encoder.js', {
5 workerData: encodingInstructions,
6 });
7
8 worker.on('message', (message) => {
9 if (message != null && typeof message === 'object') {
10 if (message.type === constants.WORKER_MESSAGE_TYPES.PROGRESS) {
11 log.info(message.message);
12 } else if (message.type === constants.WORKER_MESSAGE_TYPES.ERROR) {
13 cb(new Error(message.message));
14 } else if (message.type === constants.WORKER_MESSAGE_TYPES.DONE) {
15 log.info(message.message);
16 cb(null);
17 }
18 }
19 });
20
21 worker.on('error', (err) => {
22 cb(new Error(`An error occurred while encoding. ${err}`));
23 });
24
25 worker.on('exit', (code) => {
26 if (code !== 0) {
27 cb(new Error(`Worker stopped with exit code ${code}`));
28 } else {
29 cb(null);
30 }
31 });
32 };

encoderEngine.js hosted with ❤ by GitHub view raw

Starting the Worker and receiving message events

Depending on the type of message, the function performs a specific action. In the
case of the PROGRESS message, it logs the message using the log object. This way, we
see the progress of the worker in the main thread.
https://betterprogramming.pub/how-to-use-node-js-workers-for-video-encoding-f1379d3933d0 6/16
8/14/23, 11:56 AM How to Use Node.js Workers for Video Encoding | by Patrick Kalkman | Better Programming

The Worker reports the progress to the main thread

How to communicate from the main thread to the Worker


We also want to be able to communicate the other way around, from the main
thread to the Worker — for example, when we want to stop a running encoding task.

The mechanism is almost the same as communicating from the Worker to the main
thread. Here, we use the postMessage method on the worker object.

1 encoderEngine.stopEncoder = function stopEncoder(worker) {


2
3 const message = {};
4 message.type = constants.WORKER_MESSAGE_TYPES.STOP_ENCODING;
5 worker.postMessage(message);
6
7 }

encoderEngine.js hosted with ❤ by GitHub view raw

Sending a message from the main thread to the Worker thread

The Worker uses the parentPort to create an event handler for receiving messages.

1 parentPort.on('message', (message) => {


2 if (message.type === constants.WORKER_MESSAGE_TYPES.STOP_ENCODING) {
3 // Main thread asks to kill this thread.
4 log.info(`Main thread asked to stop this thread`);
5 ffmpegCommand.kill();
6 }
7 });

encoder.js hosted with ❤ by GitHub view raw

Receiving messages from the main thread on the Worker thread

https://betterprogramming.pub/how-to-use-node-js-workers-for-video-encoding-f1379d3933d0 7/16
8/14/23, 11:56 AM How to Use Node.js Workers for Video Encoding | by Patrick Kalkman | Better Programming

When the Worker receives STOP_ENCODING , it stops the running encoding task. It
stops the task by calling ffmpegCommand.kill() . This will SIGKILL to the FFmpeg
process and stop it.

Worker thread stops when it receives a stop message from the main thread

Conclusion
I like how the Node.js team implemented threading using Workers. By having an
explicit communication channel between threads, they prevent synchronization
issues. Synchronization causes a lot of issues with other programming languages.

The current implementation of the Workflow Encoder uses Workers for encoding
video. You can find the source on GitHub. It’s still a work in progress.

Thank you for reading.

https://betterprogramming.pub/how-to-use-node-js-workers-for-video-encoding-f1379d3933d0 8/16
8/14/23, 11:56 AM How to Use Node.js Workers for Video Encoding | by Patrick Kalkman | Better Programming

Nodejs JavaScript Video Encoding Open Source Programming

Follow

Written by Patrick Kalkman


2.6K Followers · Writer for Better Programming

Dev & writer exploring open-source innovation & agile. Passionate about learning & teaching.
https://medium.com/@pkalkman/membership

More from Patrick Kalkman and Better Programming

Patrick Kalkman in ITNEXT

Dependency Injection in Python


Building flexible and testable architectures in Python

https://betterprogramming.pub/how-to-use-node-js-workers-for-video-encoding-f1379d3933d0 9/16
8/14/23, 11:56 AM How to Use Node.js Workers for Video Encoding | by Patrick Kalkman | Better Programming

· 13 min read · Apr 14

824 5

Sergei Savvov in Better Programming

Create a Clone of Yourself With a Fine-tuned LLM


Unleash your digital twin

11 min read · Jul 28

1.6K 13

https://betterprogramming.pub/how-to-use-node-js-workers-for-video-encoding-f1379d3933d0 10/16
8/14/23, 11:56 AM How to Use Node.js Workers for Video Encoding | by Patrick Kalkman | Better Programming

Dmitry Kruglov in Better Programming

The Architecture of a Modern Startup


Hype wave, pragmatic evidence vs the need to move fast

16 min read · Nov 7, 2022

4K 38

Patrick Kalkman in ITNEXT

How to Build a Document-Based Q&A System Using OpenAI and Python


https://betterprogramming.pub/how-to-use-node-js-workers-for-video-encoding-f1379d3933d0 11/16
8/14/23, 11:56 AM How to Use Node.js Workers for Video Encoding | by Patrick Kalkman | Better Programming

Leveraging the power of Large Language Models and the LangChain framework for an
innovative approach to document querying

· 14 min read · May 30

146 5

See all from Patrick Kalkman

See all from Better Programming

Recommended from Medium

Dinubhagya

How to set up a Node.js + TypeScript + Express project 🚀


In this article, we walk through the process of creating a node.js + express + typescript +
nodemon project from a scratch.

5 min read · Feb 24

https://betterprogramming.pub/how-to-use-node-js-workers-for-video-encoding-f1379d3933d0 12/16
8/14/23, 11:56 AM How to Use Node.js Workers for Video Encoding | by Patrick Kalkman | Better Programming

18

Fernando Doglio in Bits and Pieces

Demystifying JSX: building your own JSX parser from scratch


Because why not?!

7 min read · Dec 27, 2022

693 6

Lists

General Coding Knowledge


20 stories · 196 saves

It's never too late or early to start something


13 stories · 68 saves

Stories to Help You Grow as a Software Developer


19 stories · 265 saves

Coding & Development


11 stories · 101 saves

https://betterprogramming.pub/how-to-use-node-js-workers-for-video-encoding-f1379d3933d0 13/16
8/14/23, 11:56 AM How to Use Node.js Workers for Video Encoding | by Patrick Kalkman | Better Programming

Afaque Umer in Towards AI

From Experiments 🧪 to Deployment 🚀 : MLflow 101


Uplift Your MLOps Journey by crafting a Spam Filter using Streamlit and MLflow

11 min read · Aug 5

604 2

Dmitry Kruglov in Better Programming

The Architecture of a Modern Startup


https://betterprogramming.pub/how-to-use-node-js-workers-for-video-encoding-f1379d3933d0 14/16
8/14/23, 11:56 AM How to Use Node.js Workers for Video Encoding | by Patrick Kalkman | Better Programming

Hype wave, pragmatic evidence vs the need to move fast

16 min read · Nov 7, 2022

4K 38

Mikayel Dadayan in Level Up Coding

NodeJS runtime environment: Libuv Library (Event loop, Thread pool)


Node.js is an open-source, cross-platform JavaScript runtime environment that enables
developers to build server-side applications. It runs…

9 min read · Feb 14

75

https://betterprogramming.pub/how-to-use-node-js-workers-for-video-encoding-f1379d3933d0 15/16
8/14/23, 11:56 AM How to Use Node.js Workers for Video Encoding | by Patrick Kalkman | Better Programming

Alvaro Martinez Muñoz ✅

🔧 Nginx Proxy Manager, A Reverse Proxy Management System 🔧


In this post,

5 min read · Jun 23

34

See more recommendations

https://betterprogramming.pub/how-to-use-node-js-workers-for-video-encoding-f1379d3933d0 16/16

You might also like