Skip to contentSkip to navigationSkip to topbar
Page tools
Useful for sharing or LLM
Accelerate development with AI

On this page
Looking for more inspiration?Visit the

Video Media SDK for Node.js quickstart


(new)

Public Beta

The Video Media SDK for Node.js is currently available as a Public Beta product and the information contained in this document is subject to change. This means that some features are not yet implemented and others may be changed before the product is declared as Generally Available. Public Beta products are not covered by the Twilio Support Terms or Twilio Service Level Agreement(link takes you to an external page).

The Video Media SDK for Node.js is not a US Health Insurance Portability and Accountability Act (HIPAA)(link takes you to an external page) Eligible Service or Payment Card Industry Data Security Standard (PCI DSS)(link takes you to an external page) compliant and should not be enabled in workflows that are subject to HIPAA or PCI.

This quickstart shows how to connect to a Video Room from a Node.js server, publish a video track by pushing raw frames, and receive decoded frames from remote Participants. To learn what the SDK is and how it differs from the client-side SDKs, see the Overview.


Prerequisites

prerequisites page anchor

To use the Video Media SDK, you need the following prerequisites:

  • Create a Twilio account(link takes you to an external page).

  • Install Node.js version 24.0.0 or later on Linux x86-64, or on macOS x86-64 for local development. On an Apple Silicon Mac, you need an x64 build of Node.js. To learn more, see system requirements.

  • Create an API key SID and secret.

  • To run the virtual_camera.js(link takes you to an external page) example, install FFmpeg(link takes you to an external page) and make sure ffmpeg is on your PATH. On macOS, run brew install ffmpeg. On Debian or Ubuntu, run sudo apt install ffmpeg.


  1. Install the SDK and the Twilio helper library:
    1
    npm install twilio
    2
    npm install @twilio/video-node-sdk
    You use the twilio helper library to create Access Tokens.
  2. In your app, add the import statement for the SDK:
    const { connect, createLocalVideoTrack } = require('@twilio/video-node-sdk');

The connect() function takes a standard Twilio Video Access Token with a VideoGrant, the same token format the JavaScript SDK uses. Generate an Access Token on your server with the twilio helper library(link takes you to an external page).

Create an access token example

create-an-access-token-example page anchor
1
const twilio = require('twilio');
2
3
function generateToken(identity, roomName) {
4
const token = new twilio.jwt.AccessToken(
5
process.env.TWILIO_ACCOUNT_SID,
6
process.env.TWILIO_API_KEY,
7
process.env.TWILIO_API_SECRET,
8
{ identity, ttl: 3600 },
9
);
10
token.addGrant(new twilio.jwt.AccessToken.VideoGrant({ room: roomName }));
11
return token.toJwt();
12
}
(warning)

Treat an API Key like a password

Keep your API key secret on the server. Never ship Twilio credentials in client-side code or commit the controls to source control.


Create a local video track, and then pass it to connect(). The returned promise resolves after the Room connects. Because connect() returns a promise, call it from an async function. The code in the following sections runs inside that function.

Connect to a Room example

connect-to-a-room-example page anchor
1
const { connect, createLocalVideoTrack } = require('@twilio/video-node-sdk');
2
3
async function main() {
4
const videoTrack = createLocalVideoTrack('virtual-camera');
5
6
const room = await connect(generateToken('node-participant', 'my-room'), {
7
name: 'my-room',
8
videoTracks: [videoTrack],
9
});
10
11
console.log('Connected to Room:', room.name, room.sid);
12
13
// Add the code from the following sections here.
14
}
15
16
main().catch(err => {
17
console.error('Error:', err);
18
process.exit(1);
19
});

Unlike the client-side SDKs, a local track lacks a camera. To supply raw I420 video frames, call the write() method on the track. Each call takes the frame dimensions and the y, u, and v planes. Each plane object contains its data as a Buffer, its stride, and its own width and height.

1
videoTrack.write({
2
format: 'I420',
3
width: 1280,
4
height: 720,
5
y: { data: yPlane, stride: 1280, width: 1280, height: 720 },
6
u: { data: uPlane, stride: 640, width: 640, height: 360 },
7
v: { data: vPlane, stride: 640, width: 640, height: 360 },
8
});
(information)

Connect before you send

Wait for connect() to resolve, and then send frames. The SDK drops any video frames that you write before then. Start your send loop after the await returns.

To learn about I420 video planes and strides and PCM audio, see Work with media frames.


Receive media from remote Participants

receive-media-from-remote-participants page anchor

Remote media arrives as raw decoded frames. Listen for trackSubscribed, then read frames from each video or audio track with the frames() async iterator.

1
async function trackSubscribed(track) {
2
if (track.kind !== 'video') return;
3
try {
4
// Frames that arrive while you process one are queued, and the SDK drops
5
// them if the queue fills. The loop ends by itself when the track is
6
// unsubscribed or the Room disconnects.
7
for await (const frame of track.frames()) {
8
console.log(`${frame.width}x${frame.height} @ ${frame.timestamp}us`);
9
frame.close?.();
10
}
11
} catch (err) {
12
// Nothing awaits this function, so an error that escapes here becomes an
13
// unhandled rejection and stops the process.
14
console.error('Frame loop failed:', err);
15
}
16
}
17
18
function participantConnected(participant) {
19
participant.on('trackSubscribed', trackSubscribed);
20
21
participant.tracks.forEach(publication => {
22
if (publication.isSubscribed) {
23
trackSubscribed(publication.track);
24
}
25
});
26
}
27
28
// participantConnected doesn't fire for Participants already in the Room, and a
29
// track can finish subscribing before the trackSubscribed listener is attached.
30
// Call participantConnected for each Participant in room.participants, and check
31
// isSubscribed on each publication.
32
room.participants.forEach(participantConnected);
33
room.on('participantConnected', participantConnected);
34
35
room.on('disconnected', () => {
36
room.dispose();
37
});

When you finish with a Room, call room.dispose(). Until you do, the Node.js process doesn't exit. Call it with the disconnected event, as in the preceding example.


The SDK repository(link takes you to an external page) includes runnable examples. The virtual_camera.js(link takes you to an external page) example decodes an MP4 with ffmpeg and sends its frames into a Room.

1
git clone https://github.com/twilio/twilio-video-node.git
2
cd twilio-video-node
3
npm install --prefix examples
4
cp .env.example .env
5
# Edit .env and set TWILIO_ACCOUNT_SID, TWILIO_API_KEY, and TWILIO_API_SECRET.
6
node examples/virtual_camera.js my-room

To review every example, see the examples directory(link takes you to an external page).