Node.js Media Server
Node-Media-Server is an open-source, high-performance, and low-latency live streaming server built with Node.js. The current stable version is 4.2.4. It focuses on providing a robust platform for RTMP (Real-Time Messaging Protocol), HTTP-FLV, and WS-FLV streaming, with enhanced support for modern codecs like HEVC, VP9, and AV1 in its v4 release. This version introduced a comprehensive REST API for management, JWT-based authentication, real-time monitoring, and session management, making it suitable for modern streaming applications. It differentiates itself from alternatives like Nginx RTMP or enterprise solutions by being a Node.js-native, extensible, and lightweight solution ideal for rapid development and integration into JavaScript ecosystems. While it doesn't specify a fixed release cadence, the project shows active development and regular updates.
Common errors
-
Error: listen EADDRINUSE: address already in use :::1935
cause Another process is already using the RTMP port (default 1935) or HTTP port (default 8000), preventing Node-Media-Server from binding to it.fixChange the `rtmp.port` or `http.port` in your `config` object to an unused port, or terminate the process currently occupying the desired port. Use `lsof -i :1935` (Linux/macOS) or `netstat -ano | findstr :1935` (Windows) to identify the conflicting process. -
TypeError: NodeMediaServer is not a constructor
cause This typically occurs in a CommonJS (`require`) environment when attempting to import `NodeMediaServer` as a named export (`{ NodeMediaServer }`) or in an ESM environment where a default import is expected for a CJS module.fixFor CommonJS, use `const NodeMediaServer = require('node-media-server');`. For ESM, use `import NodeMediaServer from 'node-media-server';` as `node-media-server` is primarily a CommonJS module with a default export. -
HTTP 401 Unauthorized for API endpoints
cause You are attempting to access a REST API endpoint without a valid JWT token, or the token is expired/malformed, or the configured API username/password for JWT generation is incorrect.fixFirst, ensure `http.api: true` is set in your configuration. Authenticate by making a `POST /api/v1/login` request with correct `username` and `password` to obtain a JWT token. Include this token in subsequent API requests via the `Authorization: Bearer YOUR_JWT_TOKEN` header. Verify that the `auth.jwt.users` credentials in your server config match your login attempt. -
Cannot read properties of undefined (reading 'on') for nms.on()
cause This error specifically occurs in early v4.x versions (e.g., v4.0.7) where the `nms.on` event listener method was temporarily unavailable or broken, unlike in v2.x.fixUpdate your `node-media-server` package to the latest stable `4.x` version (e.g., `4.2.4`) where this issue is generally resolved. If the problem persists, consult the project's GitHub issues for any specific guidance related to your patch version.
Warnings
- breaking Node-Media-Server v4 is a major breaking change and is incompatible with v2. Direct upgrades between these major versions are not supported, and configurations, particularly for extensions, must be rewritten.
- breaking Version 4 of Node-Media-Server is no longer compatible with the `cn_cdn` extension ID `flv_265` standard. Projects relying on this specific extension will require significant adjustments or alternative solutions.
- breaking Node-Media-Server v4 has removed compatibility with Flash Player's RTMP protocol. Legacy Flash-based clients will no longer be able to connect and stream.
- gotcha The `nms.on()` event handling method, which was standard in v2.x, appears to be non-existent or broken in early v4.x versions. This can affect custom logic tied to server events.
- gotcha The default JWT `secret` (`nodemedia2017privatekey`) and `admin` API credentials are weak and should be changed immediately in production environments to prevent unauthorized access.
- gotcha Node-Media-Server requires Node.js version 18.0.0 or higher. Running it on older Node.js versions may lead to unexpected errors or instability.
Install
-
npm install node-media-server -
yarn add node-media-server -
pnpm add node-media-server
Imports
- NodeMediaServer
import { NodeMediaServer } from 'node-media-server';import NodeMediaServer from 'node-media-server';
- Config
import { Config } from 'node-media-server';import type { Config } from 'node-media-server'; - NmsEvent
import { NmsEvent } from 'node-media-server';import type { NmsEvent } from 'node-media-server';
Quickstart
import NodeMediaServer from 'node-media-server';
import * as fs from 'fs';
// Ensure media and html directories exist for recording and static files
const mediaRoot = './media';
const recordPath = `${mediaRoot}/record`;
const htmlRoot = './html';
if (!fs.existsSync(mediaRoot)) fs.mkdirSync(mediaRoot);
if (!fs.existsSync(recordPath)) fs.mkdirSync(recordPath, { recursive: true });
if (!fs.existsSync(htmlRoot)) fs.mkdirSync(htmlRoot);
// Create a dummy index.html for static server to function
if (!fs.existsSync(`${htmlRoot}/index.html`)) {
fs.writeFileSync(`${htmlRoot}/index.html`, '<h1>Node-Media-Server Static Content</h1><p>This is a placeholder page.</p>');
}
const config = {
rtmp: {
port: 1935,
chunk_size: 60000,
gop_cache: true,
ping: 30,
ping_timeout: 60
},
http: {
port: 8000,
mediaroot: mediaRoot, // Directory for recorded files or static content
allow_origin: '*',
api: true, // Enable REST API
api_user: process.env.NMS_API_USER ?? 'admin',
api_pass: process.env.NMS_API_PASS ?? 's3cr3tP@ssw0rd'
},
auth: {
play: false,
publish: false,
secret: process.env.NMS_JWT_SECRET ?? 'superSecretKey!HighlySecure!ChangeMe!',
jwt: {
expiresIn: '1h',
algorithm: 'HS256',
users: [
{
username: process.env.NMS_ADMIN_USERNAME ?? 'admin',
password: process.env.NMS_ADMIN_PASSWORD ?? 'p@ssw0rdF0rAdm1n' // IMPORTANT: Hash this in production!
}
]
}
},
record: {
path: recordPath,
append_file: true // Append to existing file if stream restarts
},
static: {
router: '/static',
root: htmlRoot
}
};
const nms = new NodeMediaServer(config);
nms.on('postPublish', (id, StreamPath, args) => {
console.log(`[NodeEvent on postPublish] Stream started: ${StreamPath}`);
// Example: Log stream details or integrate with other services
});
nms.on('doneConnect', (id, args) => {
console.log(`[NodeEvent on doneConnect] Client disconnected: ${id}`);
});
try {
nms.run();
console.log('Node-Media-Server is running.');
console.log(`RTMP server listening on port ${config.rtmp.port}`);
console.log(`HTTP server with API listening on port ${config.http.port}`);
console.log('Access static files at /static');
console.log('Publish stream via RTMP, e.g., rtmp://localhost:1935/live/streamkey');
console.log('Play stream via HTTP-FLV, e.g., http://localhost:8000/live/streamkey.flv');
} catch (error) {
console.error('Failed to start Node-Media-Server:', error);
}