nimio-player
v1.9.0
Published
Nimio SLDP 2.0 Player — lightweight JS player built on WebCodecs
Downloads
409
Maintainers
Readme
SLDP ecosystem
SLDP (Softvelum Low Delay Protocol) is a part of Larix product family, which provides a complete solution for mobile contribution and low-latency playback.
- Nimio — lightweight JavaScript SLDP player for the web built on WebCodecs.
- Larix Player — playback including SLDP (iOS / Apple TV / Android)
- Larix Broadcaster — mobile contribution; supports SLDP talkback (iOS / Android)
Get Larix apps
Larix Player
Larix Broadcaster
Overview
Nimio is a lightweight, flexible JavaScript player for the SLDP 2.0 protocol built on the WebCodecs API. It offers low-latency streaming, full control over audio/video pipelines, and easy integration into modern web apps.
Demo
https://nimio.pages.dev/demo - Latest
https://softvelum.com/nimio/demo/ - Stable
Player highlights
- Ultra-low latency SLDP 2.0 over WebSockets with WebCodecs; supports H.264/H.265/AV1/VP8/VP9 plus AAC/MP3/Opus.
- Adaptive streaming
- Integrated player UI
- Latency and sync control: target + tolerance, fast-forward/seek correction, audio gap smoothing, timestamp recovery.
- Audio-only/video-only modes
- Full screen playback
- VU meter
- EventBus hooks for custom UI/analytics
SLDP & WebCodecs Features
- SLDP Protocol (Softvelum Low Delay Protocol) - WebSocket‑based streaming with sub-100ms latency, supporting H.264, H.265/HEVC, AV1, VP8, VP9, AAC, MP3, Opus, and more.
- WebCodecs Integration - Fine‑grained control over decoding, synchronization, jitter buffering, playback speed, and debugging at each stage of the pipeline.
- Available on all web platforms - desktop, Android and recent iOS/macOS.
Read the first beta release announcement with the list of current features.
Installation
Nimio can be used either as an npm package in a bundler-based project, or as a standalone build loaded directly in the browser.
Option A. npm package (bundler: Vite, webpack, Rollup, Parcel, etc.)
npm install nimio-playerimport Nimio from "nimio-player";
import "nimio-player/style.css";
const nimio = new Nimio({
streamUrl: "wss://example.com/stream",
container: "#player",
});Web Workers and the AudioWorklet processor used by Nimio are emitted as separate chunks next to the main file. Modern bundlers pick them up automatically via new Worker(new URL(...), import.meta.url) — no extra configuration is required.
Option B. Standalone build (no bundler)
Download the latest dist.tar.gz from the Releases page and host its contents on your server, or build it yourself:
git clone https://github.com/Softvelum/nimio.git
cd nimio
npm install
npm run buildThe build produces versioned files in dist/: nimio-<version>.js, nimio-<version>.css and a ready-to-use demo.html.
Quick Start
<link rel="stylesheet" href="/path/to/nimio.css" />
<script type="module" src="/path/to/nimio.js"></script>
<div id="player"></div>
<script type="module">
const nimio = new Nimio({
streamUrl: "wss://example.com/stream",
container: "#player",
});
</script>Full Configuration Example
nimio = new Nimio({
streamUrl: "wss://example.com/stream", //SLDP stream URL
container: "#player", // CSS selector or HTMLElement
//optional parameters:
width: 476,
height: 268,
latency: 600, // Target latency in ms
startOffset: 1000, // Startup offset in ms
pauseTimeout: 3000, // ms until auto-stop when paused
metricsOverlay: true, // Show overlay with performance metrics
logLevel: "warn", // Logging verbosity
autoplay: true, // Start playback automatically
videoOnly: false, // Video only playback
audioOnly: false, // Audio only playback
muted: true, // Player is muted on start
hardwareAcceleration: false, // Request hardware decoder; falls back to software if unsupported
adaptiveBitrate: {
initialRendition: "480p", // Default rendition which the player will set on start
maxRendition: "1080p", // Maximum rendition that the player will set automatically
sizeConstrained: true, // Player won't automatically switch to renditions which dimensions exceed the actual player size more then 5%
},
vuMeter: {
// a volume unit (VU) meter settings object
api: "AudioWorklet", // audio processing interface used by VU meter ("AudioWorklet" or "ScriptProcessor")
container: "vu-meter", // ID of the VU meter container. VU meter UI occupies the whole container's size. Vertical orientation (container's height >= width), horizontal orientation (container's width > height)
mode: "peak", // the way which audio level values are calculated ("peak", "avg", "rms")
type: "input", // VU meter readings type ("input" or "output")
rate: 6, // audio level update frequency limit (from 0.001 to 50)
dbRange: 100, // decibel scale range which is plotted in the VU meter UI
},
vod: {
// video on demand (VOD) playback settings. VOD feature provides playback of recorded live stream (DVR). VOD settings can be defined as an object, or simply with true or false values. true value has the same meaning as an empty object, false means the feature is switched off. If not defined, VOD functionality is not enabled by default. VOD feature is backed by open source HLS.js library.
// Please refer to https://softvelum.com/2024/06/dvr-sldp-html5-player/ article for setup and usage details.
url: "https://myserver.com:8081/live/stream/playlist_dvr.m3u8", // (optional) URL to HLS stream that provides recorded content. If not defined, SLDP Player will use default Nimble Streamer DVR path according to the live stream URL.
startupVodFailover: true, // (optional) specifies whether the player should automatically switch to VOD on startup if live stream playback fails. Enabled by default.
liveFailover: true, // (optional) specifies whether the player should automatically switch to Live if VOD stream playback fails. Enabled by default.
thumbnails: true, // (optional) enables video thumbnails above the progress bar of the player while hovering or dragging it. It works only if Nimble Streamer is configured with the dvr_hls_add_program_date_time parameter enabled.
hlsjs: {
source: "https://cdn.jsdelivr.net/npm/hls.js@1",
}, // (optional) HLS.js library settings. It can be either object or string. Currently 2 string values are available: 1. ‘local’ – HLS.js is already included in the web application, so Nimio doesn’t load anything. 2. ‘cdn’ – (default) Nimio will automatically load HLS.js library from the official CDN URL. If the hlsjs parameter is defined as object, it can contain the following fields (currently only one parameter is supported): ‘source‘ – a URL to HLS.js library if a specific version is required.
},
captions: {
// CEA-608 closed captions settings object. Can be set to {} or true to enable captions without specific settings. Each caption track is represented by ID: capObj pair, where the ID is one of the following: ‘CC1’, ‘CC2’, ‘CC3’, ‘CC4’. The capObj is an object, that defines the following settings:
CC1: {
name: "First track", // (optional) track name that is shown in caption selection menu. If it’s not set, then corresponding ID will be shown instead.
lang: "English", // (optional) track language that is shown in parentheses in caption selection menu after the track name.
default: true, // (optional) defines whether given track should be enabled by default. If this parameter isn’t specified for any of the tracks, then captions won’t be shown on the player start.
},
CC2: {
name: "Second track", // (optional) track name
lang: "French", // (optional) track language
},
},
timecodes: true, // enables handling of SEI picture timing (H264) and SEI time code (H265) messages
});
nimio.play();Multiple players
Mosaic demo (multiple players on one page): docs/mosaic-demo.md
Cross‑Origin Isolation
Nimio tries to use SharedArrayBuffer for zero‑copy state/audio exchange, but now falls back to a message‑based path when it is unavailable.
For best latency you should still enable a fully isolated browsing context by sending both the Cross‑Origin‑Opener‑Policy and Cross‑Origin‑Embedder‑Policy headers on any page or asset that loads the player.
Add these two headers:
add_header Cross-Origin-Opener-Policy same-origin always;
add_header Cross-Origin-Embedder-Policy require-corp always;Example: Nginx Configuration
server {
listen 443 ssl;
server_name example.com;
# Serve the Nimio demo with COOP/COEP
location ^~ /nimio/ {
alias /usr/share/nginx/example.com/nimio/;
index index.html;
# Enable cross‑origin isolation
add_header Cross-Origin-Opener-Policy same-origin always;
add_header Cross-Origin-Embedder-Policy require-corp always;
}
}Methods
Instance Methods
These methods are available on every Nimio player instance.
play()
Start playback.pause()
Pause playback.stop()
Stop and reset the player.destroy()
Destroy the player instance and release all memory.version()
Return the current version string of the player instance.seekVod(position)
Updates current VOD playback position with the specified value. If the player is currently in the live mode, it's switched to the VOD mode.
Return value: boolean status (true- position update is performed successfully,false- playback position update failed).
Parameters: position - playback position in seconds from the start. Possible values are in range [0, duration]seekLive(buffer)
Updates current live playback position (buffering) with the specified value. If the player is currently in VOD mode, it will be switched to Live mode. The latency value in the player's settings is updated with the value of the buffer parameter. If the parameter is omitted, the current buffering setting is used.
Return value: boolean status (true- position update is performed successfully,false- playback position update failed).
Parameters: buffer - (optional) buffer size in seconds that the player pertains during live playback.getCaptionTracks()
Returns a map of the CEA-608 caption tracks currently available in the stream.
Return value: object, containing ID: CAP_OBJ pairs, where the ID is one of the following: "CC1", "CC2", "CC3", "CC4". The CAP_OBJ is and object with the following fields:- title - caption track title that is shown in caption selection dialog.
- name - (optional) caption track name defined in caption settings.
- lang - (optional) caption track language defined in caption settings.
getCurrentCaptionTrack()
Returns currently selected CEA-608 caption track.
Return value: object, containing single ID: CAP_OBJ pair, with the ID of currently selected caption track. If no caption track is selected, then an empty object will be returned.setCaptionTrack(ID)
Sets current CEA-608 caption track with the ID specified. To turn off captions, "OFF" value should be specified as ID.
Return value: boolean status (true- track is set successfully,false- specified track can't be set).
Parameters:
ID - either caption track ID value, which can be one of the following: "CC1", "CC2", "CC3", "CC4" or "OFF" to turn off captions.getCurrentStreamBandwidth()
Returns stream's bandwidth in bits per second.getVodThumbnailUrl(time)
Returns the URL to the thumbnail of the current VOD stream at the given time in seconds. More specifically, the thumbnail is the keyframe of the HLS segment that covers the given time.
IMPORTANT: This functionality only works if Nimble Streamer is configured with thedvr_hls_add_program_date_timeparameter enabled. If there is no thumbnail at the specified time or if Nimble Streamer isn't configured with the above parameter, the result is undefined.
Return value: string with the requested thumbnail URL.
Parameters:
time - time of the thumbnail in seconds from 0 to the length of the DVR archivegetStreamEncodedFramerate()
Returns framerate value calculated from the SPS parameters. Applicable only when the "timecodes" init parameter is enabled. If there are no relevant parameters in the SPS, the method will return undefined. The method should be run after the player started to receive video frames. E.g. it can be run on the first "nimio:sei-timecode" event emission.
Return value: integer value of the current stream's framerate, encoded in the SPS.
Static Methods
These methods are available directly on the Nimio class.
Nimio.version()
Return the current version string (identical toinstance.version()).
Events
Nimio player uses events to interact with its UI. It allows to create custom UI easily.
Events sent from UI to player
These events are used to send commands and data from UI to Nimio player.
ui:play-pause-click
Start/pause playback control invoked.
Parameters:
isPlayClicked: Boolean;
mode: "live" | "vod"; // playback modeui:volume-change
Set audio volume.
Parameters:
volume: Number; // Current volume as integer value in the range from 0 to 100.ui:mute-unmute-click
Mute/unmute audio.
Parameters:
mute: Boolean;ui:rendition-select
A specific rendition is selected from the list received from the nimio:rendition-list event.
Parameters:
rend: {
id: Number, // An integer number with unique rendition ID.
name: String // Rendition name.
},
mode: "live" | "vod"; // playback modeEvents sent from player to UI
These events are used to send data from Nimio player to UI.
nimio:play
Playback started. Parameters:
mode: "live" | "vod"; // playback modenimio:pause
Playback paused.
Parameters:
mode: "live" | "vod"; // playback modenimio:playback-startPlayback has started and first frame is rendered.
Parameters:
mode: "live" | "vod"; // playback modenimio:playback-end
Playback reached the end of the media.
Parameters:
mode: "live" | "vod"; // playback modenimio:volume-set
Audio volume level set.
Parameters:
volume: Number; // Current volume integer value in the range from 0 to 100.nimio:muted
Audio muted/unmuted.
Parameters:
muted: Boolean;nimio:abr
Adaptive bitrate (ABR) mode enabled/disabled.
Parameters:
enabled: Boolean;nimio:rendition-list
List of available renditions.
Parameters:
renditions: Array<{
id: Number, // An integer number with unique rendition ID.
name: String // Rendition name.
}>;nimio:rendition-set
Active video/audio rendition selected manually or programmatically.
Parameters:
rendition: {
id: Number, // An integer number with unique rendition ID.
name: String // Rendition name.
}Playback events
nimio:connection-started
Invoked when player starts connection to media server.
Parameters:
url - stream URL the player connects to, e.g. "wss://exampl.com/live/stream"nimio:connection-established
Emitted when connection to media server is established and a list of available streams is received. The list of renditions available for user is composed depending on those streams and browser capabilities.
Parameters:
streams: Array<{
name: String, // application and stream name, e.g. 'live/stream'
width: Number, // stream width in pixels if video is present
height: Number, // stream height in pixels if video is present
vcodec: String, // stream video codec if present
video: String, // either 'supported' or 'not supported' depending on browser capabilities
acodec: String, // stream audio codec if present
audio: String, // either 'supported' or 'not supported' depending on browser capabilities
bandwidth: Number, // stream bandwidth expressed in bits per second
}>;nimio:vod-progress
Invoked when the current VOD playback state is changed. The VOD playback status change during live playback is usually related to the VOD duration change.
Parameters:
status: {
position: Number, // VOD playback position in seconds from the start
duration: Number, // total VOD duration in seconds
}nimio:live-progress
Invoked when the current live playback state is changed.
Parameters:
status: {
buffer: Number, // current buffer size in seconds
}Other events that can be handled by the caller
nimio:captions-arrived
Emitted each time a new set of captions is ready to be displayed on the player's screen. All previous captions should be replaced by new ones. CEA-608 caption format implies that player's screen is divided into 15 rows and 32 columns. Each cell contains one symbol.
Parameters:
captions: Array<{
time: Number, // caption display start time in microseconds
x: Number, // caption block's horizontal position counted from the left. Can be in range of 0 - 31.
y1: Number, //caption block's top line position counted from the top. Can be in range of 0 - 14.
y2: Number, // caption block's bottom line position counted from the top. Can be in range of 0 - 14.
regions: Array<{
spans: Array<{
row: Number, // row number starting from the top. Can be in range of 0 - 14.
content: String, // caption span text string
style: { // caption span style object
foreground: String, // text font color
background: String, // background color
italics: Boolean, // text should be italic if true
underline: Boolean, // text should be underlined if true
flash: Boolean, // span should be blinking if true
},
}>
}> // If the regions array is empty, it means that the given caption block is used to clear all previous captions from the given time onwards
}>,
currentTime: Number, // current player time in microsecondsnimio:sei-timecode
Emitted each time a new SEI picture timing or SEI time code message is parsed by the player.
Parameters:
frameTs: Number, // timestamp of a frame to which the given SEI picture timing message is attached. The timestamp is defined in microseconds
clockTs: Number, // clockTimestamp from the SEI message which is calculated according to the Rec. ITU-T H.264 document.
stringTs: String, // timecode from the SEI message in the form "hh:mm:ss.n_frames time_offset"
mode: String, // "live" - the timecode is retrieved from a Live stream, "vod" - the timecode is retrieved from a VOD stream;Roadmap
The following features are planned for upcoming releases:
- Automatic aspect ratio detection
- Picture-in-Picture (PiP)
- WebTransport protocol
- Screenshot capture
- Extended Player API
- OffscreenCanvas rendering
- Resume from pause in DVR mode (no auto-jump to live)
Development
Common scripts:
npm run dev— start the Vite dev server with the demo page.npm run build— produce the standalone build indist/(versioned file names +demo.html).npm run build:pkg— produce the npm package layout inpkg/(stablenimio.js/nimio.css+ worker chunks inpkg/assets/).npm run build:all— run both builds.npm run test:pkg:vite— build the npm package, install it into the Vite smoke-test app from a localnpm packtarball, and run the app production build.npm run dev:pkg:vite— build and install the local package tarball, then start the Vite smoke-test app for manual browser testing.npm run test:pkg:webpack— build the npm package, install it into the webpack smoke-test app from a localnpm packtarball, and run the app production build.npm run dev:pkg:webpack— build and install the local package tarball, then start the webpack smoke-test app for manual browser testing.npm run tar— packdist/intodist.tar.gzfor standalone distribution.npm run test/npm run test:ci— run the test suite.
npm package smoke test
The examples/vite-package and examples/webpack-package projects verify that
the npm package works when consumed by real bundler apps. They import
nimio-player and nimio-player/style.css from a local tarball generated with
npm pack, so the tests cover the published package shape (files, exports,
CSS, worker chunks, and AudioWorklet assets), not just the source tree.
Run the build smoke test from the repository root:
npm run test:pkg:vite
npm run test:pkg:webpackFor manual playback testing in a browser:
npm run dev:pkg:vite
npm run dev:pkg:webpackContributing
Contributions are welcome! Please open an issue for discussion or submit a pull request.
License
Nimio released under MIT License.
