Video Media SDK for Node.js quickstart
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.
The Video Media SDK for Node.js is not a US Health Insurance Portability and Accountability Act (HIPAA) Eligible Service or Payment Card Industry Data Security Standard (PCI DSS) 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.
To use the Video Media SDK, you need the following prerequisites:
-
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.
View how to create an API Key -
To run the
virtual_camera.jsexample, install FFmpeg and make sureffmpegis on yourPATH. On macOS, runbrew install ffmpeg. On Debian or Ubuntu, runsudo apt install ffmpeg.
- Install the SDK and the Twilio helper library:
You use the1npm install twilio2npm install @twilio/video-node-sdk
twiliohelper library to create Access Tokens. - 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.
1const twilio = require('twilio');23function generateToken(identity, roomName) {4const token = new twilio.jwt.AccessToken(5process.env.TWILIO_ACCOUNT_SID,6process.env.TWILIO_API_KEY,7process.env.TWILIO_API_SECRET,8{ identity, ttl: 3600 },9);10token.addGrant(new twilio.jwt.AccessToken.VideoGrant({ room: roomName }));11return token.toJwt();12}
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.
1const { connect, createLocalVideoTrack } = require('@twilio/video-node-sdk');23async function main() {4const videoTrack = createLocalVideoTrack('virtual-camera');56const room = await connect(generateToken('node-participant', 'my-room'), {7name: 'my-room',8videoTracks: [videoTrack],9});1011console.log('Connected to Room:', room.name, room.sid);1213// Add the code from the following sections here.14}1516main().catch(err => {17console.error('Error:', err);18process.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.
1videoTrack.write({2format: 'I420',3width: 1280,4height: 720,5y: { data: yPlane, stride: 1280, width: 1280, height: 720 },6u: { data: uPlane, stride: 640, width: 640, height: 360 },7v: { data: vPlane, stride: 640, width: 640, height: 360 },8});
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.
Remote media arrives as raw decoded frames. Listen for trackSubscribed, then read frames from each video or audio track with the frames() async iterator.
1async function trackSubscribed(track) {2if (track.kind !== 'video') return;3try {4// Frames that arrive while you process one are queued, and the SDK drops5// them if the queue fills. The loop ends by itself when the track is6// unsubscribed or the Room disconnects.7for await (const frame of track.frames()) {8console.log(`${frame.width}x${frame.height} @ ${frame.timestamp}us`);9frame.close?.();10}11} catch (err) {12// Nothing awaits this function, so an error that escapes here becomes an13// unhandled rejection and stops the process.14console.error('Frame loop failed:', err);15}16}1718function participantConnected(participant) {19participant.on('trackSubscribed', trackSubscribed);2021participant.tracks.forEach(publication => {22if (publication.isSubscribed) {23trackSubscribed(publication.track);24}25});26}2728// participantConnected doesn't fire for Participants already in the Room, and a29// track can finish subscribing before the trackSubscribed listener is attached.30// Call participantConnected for each Participant in room.participants, and check31// isSubscribed on each publication.32room.participants.forEach(participantConnected);33room.on('participantConnected', participantConnected);3435room.on('disconnected', () => {36room.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 includes runnable examples. The virtual_camera.js example decodes an MP4 with ffmpeg and sends its frames into a Room.
1git clone https://github.com/twilio/twilio-video-node.git2cd twilio-video-node3npm install --prefix examples4cp .env.example .env5# Edit .env and set TWILIO_ACCOUNT_SID, TWILIO_API_KEY, and TWILIO_API_SECRET.6node examples/virtual_camera.js my-room
To review every example, see the examples directory.
- Work with media frames: Learn the I420 video and PCM audio frame formats in depth.
- Differences from the JavaScript SDK: Map what you know from the browser SDK to the server.
- Best practices: Pace frames, manage resources, and troubleshoot common issues.