Skip to content

Live Transcoding

For media servers that transcode video on demand rather than serving pre-encoded HLS. LiveTranscodingPlugin manages the negotiation with a NoMercy media server endpoint, monitors transcode progress, and exposes quality switching that works alongside the server’s encoding pipeline.

Prerequisites: Video Quick Start. Your server must expose a compatible /transcode endpoint.

Minimal setup

TypeScript
import { nmplayer } from '@nomercy-entertainment/nomercy-video-player';
import { DesktopUiPlugin } from '@nomercy-entertainment/nomercy-video-player/plugins';
import { LiveTranscodingPlugin } from '@nomercy-entertainment/nomercy-video-player/plugins';

const player = nmplayer('main')
.addPlugin(DesktopUiPlugin)
.addPlugin(LiveTranscodingPlugin, {
controlUrl: 'wss://media.your-server.com/transcode/movie-1/ws',
seekTimeoutMs: 15_000,
})
.setup({
baseUrl: 'https://media.your-server.com',
auth: { bearerToken: () => myAuth.getToken() },
playlist: [
{
id: 'movie-1',
title: 'My Movie',
url: '/api/transcode/movie-1/stream.m3u8',
duration: 7200,
},
],
});

player.on('ready', () => {
player.item(0, { autoplay: true });
});

The plugin opens a control WebSocket to controlUrl and listens for transcode-progress messages from the server. When the server is still encoding, it gates beforeLoad and beforeSeek until the transcoder has reached the requested position, so playback never outruns the encode.

Monitoring transcode progress

TypeScript
const transcodingPlugin = player.getPlugin(LiveTranscodingPlugin);

// Fires on each progress update from the server:
player.on('plugin:live-transcoding:job:progress', ({ transcodedSeconds, totalSeconds }) => {
if (totalSeconds) {
progressBar.value = transcodedSeconds / totalSeconds;
}
progressLabel.textContent = `Encoding: ${Math.floor(transcodedSeconds)}s`;
});

// Fires when the full transcode job completes:
player.on('plugin:live-transcoding:job:complete', ({ jobId }) => {
progressBar.hidden = true;
});

// Fires if the server reports an error:
player.on('plugin:live-transcoding:job:error', ({ error }) => {
showError(error.message);
});

Quality hint

Pass preferredHeight to request a target output height from the transcoding server:

TypeScript
player.addPlugin(LiveTranscodingPlugin, {
controlUrl: 'wss://media.your-server.com/transcode/movie-1/ws',
preferredHeight: 1080,
});

When the server is encoding multiple quality variants in parallel, the player’s standard player.quality(idx) call controls which HLS level hls.js selects, and standard ABR applies once variants are available.

Session cleanup

The transcode session on the server holds resources (CPU, disk). Disposing the player closes the plugin’s control WebSocket (the channel auto-closes on dispose), which is the server’s signal to tear the session down:

TypeScript
// On page unload or player unmount:
player.dispose(); // closes the control WebSocket; the server tears down the session on disconnect

For single-page apps, wire disposal to your router’s leave hook:

TypeScript
// Vue Router:
onBeforeRouteLeave(() => {
player.dispose();
});

// React Router:
useEffect(() => {
return () => player.dispose();
}, []);