WebRTC streaming
without the boilerplate
A modern, TypeScript-first client library for the WHIP and WHEP WebRTC streaming protocols. Publish or subscribe to live streams in a few lines of code.
Installation
Install the package from npm:
The package ships three output formats to cover every environment:
| Format | File | Use case |
|---|---|---|
| ESM | dist/index.mjs | Bundlers — Vite, webpack, Rollup |
| CommonJS | dist/index.cjs | Node.js tooling |
| IIFE | dist/index.global.js | <script> tag, CDN |
CDN (no build step)
<script src="https://unpkg.com/whip-whep-client/dist/index.global.js"></script>
<script>
const { WHIPClient, WHEPClient } = WhipWhepClient;
</script>
Quick Start
Publish a stream (WHIP)
import { WHIPClient } from 'whip-whep-client';
const client = new WHIPClient({
endpoint: 'https://ingest.example.com/whip/live',
token: 'my-secret-token',
});
client.on('connected', () => console.log('Publishing'));
client.on('disconnected', () => console.log('Paused'));
client.on('failed', (err) => console.error('Error:', err));
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
await client.publish(stream);
// When done:
await client.stop();
Subscribe to a stream (WHEP)
import { WHEPClient } from 'whip-whep-client';
const client = new WHEPClient({
endpoint: 'https://cdn.example.com/whep/stream/abc123',
token: 'viewer-token',
});
client.on('stream', (stream) => {
document.querySelector('video').srcObject = stream;
});
await client.view();
// When done:
await client.stop();
WHIPClient — Constructor Options
All options for new WHIPClient(options: WHIPClientOptions).
| Option | Type | Default | Description |
|---|---|---|---|
endpoint | string | required | WHIP endpoint URL. |
token | string | — | Sent as Authorization: Bearer <token>. |
headers | Record<string, string> | — | Static custom headers appended to every HTTP request. |
getHeaders | () => Record | Promise | — | Async function resolved before each request. Overrides headers. |
iceServers | RTCIceServer[] | browser defaults | STUN / TURN server list. |
iceTransportPolicy | 'all' | 'relay' | 'all' | Set to 'relay' to force TURN-only connectivity. |
iceCandidatePoolSize | number | 0 | Number of ICE candidates to pre-gather before the offer is sent. |
timeout | number | 15000 | Maximum ms to wait for the SDP exchange to complete. |
iceConnectionTimeout | number | — | Maximum ms to wait for ICE to reach 'connected' after SDP exchange. |
autoReconnect | boolean | AutoReconnectOptions | — | Automatically retry on mid-session failure. See Auto-Reconnect. |
endpointRecovery | boolean | false | Attempt PATCH-based session recovery before full reconnect. v1.1 |
adaptiveQuality | boolean | AdaptiveQualityOptions | — | Auto-scale video bitrate based on connection quality. v1.1 |
simulcast | boolean | false | Enable simulcast with three quality layers (high / mid / low). |
audioCodec | string | — | Preferred audio codec name, e.g. 'opus'. |
videoCodec | string | — | Preferred video codec name, e.g. 'h264', 'vp8', 'av1'. |
audio | AudioEncodingOptions | — | Advanced Opus parameters. See AudioEncodingOptions below. |
video | VideoLayerOptions | VideoLayerOptions[] | — | Single-layer or per-simulcast-layer encoding parameters. |
maxBandwidth | number | — | Session bandwidth limit in kbps. Written as b=AS in the SDP offer. |
peerConnectionConfig | RTCConfiguration | — | Extra fields merged into the RTCPeerConnection config. |
logger | Logger | — | Any object with debug/info/warn/error methods (e.g. console). |
AudioEncodingOptions
| Option | Type | Default | Description |
|---|---|---|---|
maxBitrate | number | — | Maximum audio bitrate in bps. Applied via setParameters(). |
dtx | boolean | false | Discontinuous Transmission — reduces bitrate during silence. |
stereo | boolean | false | Force stereo encoding. |
fec | boolean | true | In-band Forward Error Correction for Opus. |
comfortNoise | boolean | false | Generate comfort noise during silence. |
contentHint | 'speech' | 'music' | '' | — | Encoder content hint passed to the browser. |
VideoLayerOptions
| Option | Type | Description |
|---|---|---|
rid | string | RID label for simulcast layers, e.g. 'high', 'mid', 'low'. |
maxBitrate | number | Maximum encoding bitrate in bps. |
maxFramerate | number | Maximum frames per second. |
scaleResolutionDownBy | number | Downscale factor. 1 = full, 2 = half, 4 = quarter. |
active | boolean | Whether this encoding layer is active. |
degradationPreference | RTCDegradationPreference | How to trade off quality when bandwidth is constrained. |
contentHint | 'motion' | 'detail' | 'text' | '' | Encoder hint for the content type. |
publish()
Starts publishing. Creates the RTCPeerConnection, adds media tracks, performs the SDP offer/answer exchange with the server, and waits for ICE to connect.
| Option | Type | Default | Description |
|---|---|---|---|
audio | boolean | true | Include the audio track from the stream. |
video | boolean | true | Include the video track from the stream. |
simulcast | boolean | from constructor | Override simulcast for this specific call. |
signal | AbortSignal | — | Cancel the publish operation. The in-flight HTTP request is aborted, the peer connection is closed, and a DOMException('AbortError') is thrown. v1.2 |
Cancelling with AbortSignal
const ac = new AbortController();
setTimeout(() => ac.abort(), 5_000); // cancel if publish takes > 5 s
try {
await client.publish(stream, { signal: ac.signal });
} catch (err) {
if (err instanceof DOMException && err.name === 'AbortError') {
console.log('Publish cancelled');
}
}
Throws InvalidStateError when called on a client that is not in the 'idle' state. Create a new instance or call reconnect() instead.
publishScreen() NEW in v1.2
Captures the screen (or window / browser tab) via getDisplayMedia and publishes it to the WHIP endpoint. Returns the captured MediaStream so the caller can stop individual tracks when sharing ends.
| Option | Type | Default | Description |
|---|---|---|---|
displayAudio | boolean | false | Request system / tab audio from getDisplayMedia. Browser support varies — Chrome only. |
micAudio | boolean | MediaTrackConstraints | false | Capture microphone audio via getUserMedia and use it as the published audio track. Takes precedence over displayAudio. |
videoConstraints | MediaTrackConstraints | — | Video constraints merged on top of the defaults (1920 × 1080 @ 30 fps). |
publishOptions | PublishOptions | — | Per-call overrides forwarded to the underlying publish() call (e.g. signal, simulcast). |
// Screen share with microphone audio
const stream = await client.publishScreen({ micAudio: true });
// Stop sharing when the user clicks a button
stopBtn.onclick = async () => {
for (const t of stream.getTracks()) t.stop();
await client.stop();
};
Listen to the ended event on the stream's video track to detect when the user clicks the browser's native "Stop sharing" button and call client.stop() accordingly.
stop()
Sends HTTP DELETE to the resource URL, closes the RTCPeerConnection, and removes all event listeners. The client transitions to 'closed' state. Safe to call multiple times.
reconnect()
Tears down the current session and re-runs publish() with the stream from the last call. Useful for manually handling a 'failed' event. When endpointRecovery is enabled, a PATCH-based recovery is attempted first.
Throws InvalidStateError if publish() has not been called at least once.
replaceTrack()
Replaces the active sender track without renegotiation. The swap takes effect immediately via RTCRtpSender.replaceTrack(). The stored stream used by reconnect() is updated automatically.
// Switch to screen share mid-stream
const screen = await navigator.mediaDevices.getDisplayMedia({ video: true });
await client.replaceTrack('video', screen.getVideoTracks()[0]);
// Switch back to camera
const cam = await navigator.mediaDevices.getUserMedia({ video: true });
await client.replaceTrack('video', cam.getVideoTracks()[0]);
muteTrack() / unmuteTrack() / isTrackMuted() NEW in v1.2
Mute or unmute the active sender track by toggling MediaStreamTrack.enabled. No renegotiation is required — a muted audio track sends silence, a muted video track sends a black frame. The sender remains active and can be toggled instantly.
// Mute microphone
client.muteTrack('audio');
// Check state
console.log(client.isTrackMuted('audio')); // true
// Unmute
client.unmuteTrack('audio');
Use muteTrack() to temporarily silence a sender. Use replaceTrack() when you need to swap to a completely different source (e.g. switching camera to screen share).
getStats()
Returns a normalised snapshot of the current session. Bitrate values are computed as a delta since the previous call — call periodically (e.g. every 2 s) for meaningful readings. See Connection Stats for the full type reference.
watchStats() NEW in v1.2
Polls getStats() on a fixed interval and invokes callback with each snapshot and a rolling StatsHistory window. Returns a zero-argument cleanup function that stops polling when called. The optional historySize parameter caps the window (default 10).
const stopWatching = client.watchStats(2_000, (stats, history) => {
console.log('quality', stats.quality);
console.log('bitrate', stats.video?.bitrate);
console.log('avg bitrate', history.avgVideoBitrate()); // rolling mean
const prev = history.prev;
if (prev?.video) {
const delta = (stats.video?.bitrate ?? 0) - prev.video.bitrate;
console.log('Δ bitrate', delta); // trend
}
});
// Stop when you're done
stopWatching();
StatsHistory
| Member | Type | Description |
|---|---|---|
snapshots | ReadonlyArray<StreamStats> | All snapshots in the window, oldest first. Capped at historySize. |
prev | StreamStats | null | Snapshot from the previous interval. null on the first call. |
avgVideoBitrate() | number | null | Mean video bitrate across the window in bps. |
avgAudioBitrate() | number | null | Mean audio bitrate across the window in bps. |
avgPacketLossRate() | number | null | Mean packet-loss rate (0–1) across audio and video tracks in the window. |
avgRoundTripTime() | number | null | Mean RTT in seconds across the window. Skips null entries. |
Audio Level Monitor NEW in v1.2
Starts polling the outgoing audio sender's amplitude via an AudioContext + AnalyserNode and emits 'audiolevel' events at the given interval. The emitted level is a normalised RMS value in [0, 1] — useful for building "speaking" indicators.
The monitor is stopped automatically by stop() and during reconnects. stopAudioLevelMonitor() also closes the underlying AudioContext.
await client.publish(stream);
client.startAudioLevelMonitor(100); // poll every 100 ms (default)
client.on('audiolevel', (level) => {
micIndicator.style.opacity = level; // 0 = silence, 1 = max
});
// Stop when you no longer need it
client.stopAudioLevelMonitor();
Call startAudioLevelMonitor() after publish() resolves — the audio sender must exist before monitoring can start.
WHIPClient — Events
| Event | Arguments | Description |
|---|---|---|
connected | — | ICE + DTLS fully established, media is flowing. |
disconnected | — | Connection temporarily lost. May recover without action. |
failed | error: Error | Connection has irrecoverably failed. |
reconnecting | attempt: number, delayMs: number | Auto-reconnect attempt is starting. |
reconnected | — | Auto-reconnect successfully restored the session. |
qualitychange | quality: ConnectionQuality | Adaptive quality changed the effective bitrate level. |
audiolevel | level: number | Normalised RMS audio amplitude [0–1]. Only emitted while startAudioLevelMonitor() is active. v1.2 |
connectionstatechange | state: RTCPeerConnectionState | Raw RTCPeerConnection state change. |
iceconnectionstatechange | state: RTCIceConnectionState | ICE connection state change. |
icegatheringstatechange | state: RTCIceGatheringState | ICE gathering state change. |
WHEPClient — Constructor Options
Shares all base options with WHIPClient (endpoint, token, headers, getHeaders, iceServers, iceTransportPolicy, iceCandidatePoolSize, timeout, iceConnectionTimeout, autoReconnect, logger, peerConnectionConfig), plus:
| Option | Type | Default | Description |
|---|---|---|---|
audioCodec | string | — | Preferred inbound audio codec. |
videoCodec | string | — | Preferred inbound video codec. |
maxBandwidth | number | — | Bandwidth hint to the server in kbps (b=AS in the SDP offer). |
view()
Opens the WHEP subscription. Returns a MediaStream populated as remote tracks arrive. The 'stream' event fires once all expected tracks are received.
| Option | Type | Default | Description |
|---|---|---|---|
audio | boolean | true | Add an audio receive transceiver. |
video | boolean | true | Add a video receive transceiver. |
stop() — WHEPClient
Stops all remote tracks, sends HTTP DELETE to the resource URL, and closes the peer connection. Safe to call multiple times.
reconnect() — WHEPClient
Tears down the current session and re-runs view() with the options from the last call. The new stream is delivered via the 'stream' event.
getStats() — WHEPClient
Same interface as WHIPClient.getStats(). Reads from inbound-rtp entries (receiver stats) and the active ICE candidate pair for RTT.
WHEPClient — Events
All WHIPClient base events plus:
| Event | Arguments | Description |
|---|---|---|
stream | stream: MediaStream | Remote stream is ready to attach to a <video> element. |
Authentication
Three mechanisms are available, merged in priority order (later entries override earlier ones):
- Built-in defaults —
Content-Type,Authorizationfromtoken - Static
headers - Dynamic
getHeaders()return value
Bearer token
const client = new WHIPClient({
endpoint: 'https://ingest.example.com/whip/live',
token: 'my-secret', // → Authorization: Bearer my-secret
});
Custom static headers
const client = new WHIPClient({
endpoint: 'https://ingest.example.com/whip/live',
headers: {
'Authorization': 'Token abc123',
'X-API-Key': 'my-key',
},
});
Dynamic headers (token refresh, HMAC)
const client = new WHIPClient({
endpoint: 'https://ingest.example.com/whip/live',
getHeaders: async () => ({
'Authorization': `Bearer ${await tokenStore.getValidToken()}`,
}),
});
Simulcast
Set simulcast: true to publish three quality layers simultaneously. An SFU can then forward the appropriate layer to each viewer based on their bandwidth.
const client = new WHIPClient({
endpoint: 'https://ingest.example.com/whip/live',
simulcast: true,
});
Custom layer configuration
Override the default layers by passing an array to video. Layers should be ordered high to low quality and each must have a unique rid.
const client = new WHIPClient({
endpoint: 'https://ingest.example.com/whip/live',
simulcast: true,
video: [
{ rid: 'high', maxBitrate: 2_500_000, scaleResolutionDownBy: 1 },
{ rid: 'mid', maxBitrate: 1_000_000, scaleResolutionDownBy: 2 },
{ rid: 'low', maxBitrate: 300_000, scaleResolutionDownBy: 4 },
],
});
Simulcast requires SFU-side support. Single-origin servers (e.g. Cloudflare Stream) do not forward simulcast layers to WHEP viewers.
Endpoint Recovery NEW in v1.1
When a connection drops, the default reconnect flow sends HTTP DELETE to release the server resource and then re-opens a brand-new session with a fresh POST. If the server is still holding the session — a brief network hiccup — this teardown is unnecessary overhead.
Enable endpointRecovery: true to attempt a lighter-weight recovery first, per RFC 9725 §4.3:
- The library stores the
ETagfrom each successful WHIP POST response. - On reconnect, it sends a PATCH to the resource URL with a new SDP offer and an
If-Match: <etag>header — without deleting the resource first. - If the server still holds the session it responds with
200 OKand a new SDP answer. - If the server rejects the PATCH (e.g.
404 Not Foundwhen the session has expired), the library automatically falls back to DELETE + POST.
const client = new WHIPClient({
endpoint: 'https://ingest.example.com/whip/live',
token: 'my-token',
autoReconnect: true,
endpointRecovery: true,
});
client.on('reconnecting', (attempt) => console.log(`Attempt ${attempt}`));
client.on('reconnected', () => console.log('Session restored'));
await client.publish(stream);
endpointRecovery is only useful when combined with autoReconnect or a manual reconnect() call. It only applies to WHIPClient.
Adaptive Quality NEW in v1.1
Enable adaptiveQuality to have the library automatically scale the video encoder bitrate in response to the measured ConnectionQuality. When quality degrades the bitrate is reduced; once conditions improve the bitrate is gradually restored.
const client = new WHIPClient({
endpoint: 'https://ingest.example.com/whip/live',
video: { maxBitrate: 2_500_000 }, // target at 'excellent'
adaptiveQuality: true,
});
client.on('qualitychange', (quality) => {
console.log('Bitrate adjusted for:', quality);
});
await client.publish(stream);
Bitrate scaling
| Quality | Bitrate factor | Example (2.5 Mbps target) |
|---|---|---|
excellent | 100 % | 2 500 kbps |
good | 75 % | 1 875 kbps |
fair | 50 % | 1 250 kbps |
poor | 25 % | 625 kbps |
Fine-tuning
const client = new WHIPClient({
endpoint: 'https://ingest.example.com/whip/live',
video: { maxBitrate: 3_000_000 },
adaptiveQuality: {
intervalMs: 3_000, // sample every 3 s (default: 5 000)
downgradeThreshold: 2, // 2 bad readings → scale down (default: 2)
upgradeThreshold: 4, // 4 good readings → scale up (default: 4)
minVideoBitrate: 200_000, // never below 200 kbps (default: 150 000)
},
});
AdaptiveQualityOptions
| Option | Type | Default | Description |
|---|---|---|---|
intervalMs | number | 5000 | Stats polling interval in milliseconds. |
downgradeThreshold | number | 2 | Consecutive degraded readings before scaling down. |
upgradeThreshold | number | 4 | Consecutive improved readings before scaling up. |
minVideoBitrate | number | 150 000 | Minimum video bitrate floor in bps. |
The target bitrate is taken from video.maxBitrate when set, or defaults to 2.5 Mbps. Adaptive quality adjusts the single-layer video sender and does not modify simulcast layer activity.
Auto-Reconnect
Pass autoReconnect: true to automatically retry when the connection fails after a previously successful session. It does not retry signalling errors (e.g. a 401 from the server).
const client = new WHIPClient({
endpoint: 'https://ingest.example.com/whip/live',
autoReconnect: true,
});
client.on('reconnecting', (attempt, delayMs) =>
console.log(`Attempt ${attempt} in ${delayMs}ms`)
);
client.on('reconnected', () => console.log('Session restored'));
AutoReconnectOptions
| Option | Type | Default | Description |
|---|---|---|---|
maxAttempts | number | 5 | Maximum number of reconnection attempts. |
initialDelayMs | number | 1000 | Delay before the second attempt. The first retry is immediate. |
maxDelayMs | number | 30 000 | Upper bound on the inter-attempt delay. |
backoff | 'exponential' | 'fixed' | exponential | Delay growth strategy. |
Connection Stats
Poll getStats() to monitor stream health, or use the watchStats() helper to avoid writing your own interval. Bitrate is computed as a delta between consecutive calls.
// One-shot
const stats = await client.getStats();
// Continuous polling via watchStats()
const stopWatching = client.watchStats(2_000, (stats, history) => {
console.log(stats.quality); // 'excellent' | 'good' | 'fair' | 'poor'
console.log(stats.roundTripTime); // seconds
console.log(stats.video?.bitrate); // bps (current)
console.log(history.avgVideoBitrate()); // bps (rolling mean)
console.log(history.avgPacketLossRate()); // 0–1 (rolling mean)
console.log(stats.video?.frameRate); // fps
});
StreamStats
| Field | Type | Description |
|---|---|---|
timestamp | number | Ms since epoch when the snapshot was taken. |
audio | AudioStats | null | Audio stats. null when no audio track is active. |
video | VideoStats | null | Video stats. null when no video track is active. |
roundTripTime | number | null | RTT in seconds from RTCP. null until the first report. |
quality | ConnectionQuality | Derived quality label. |
Quality thresholds
| Quality | Packet-loss rate | RTT |
|---|---|---|
excellent | < 1 % | < 50 ms |
good | < 3 % | < 150 ms |
fair | < 8 % | < 300 ms |
poor | ≥ 8 % | ≥ 300 ms |
SDP Utilities
Low-level SDP helpers exported for advanced use cases such as custom signalling layers or testing.
import {
preferCodec, setBandwidth, addSimulcast,
patchFmtp, listCodecs, extractSsrc, removeExtmap,
} from 'whip-whep-client';
// Prefer H.264 in the video section
const sdp = preferCodec(originalSdp, 'video', 'H264');
// Add a 3 Mbps bandwidth limit to the video section
const sdp = setBandwidth(sdp, 'video', 3_000);
// List codec names in the audio section
const codecs = listCodecs(sdp, 'audio');
// Patch Opus fmtp parameters
const sdp = patchFmtp(sdp, 'audio', 'opus', { usedtx: 1, stereo: 1 });
ICE Utilities
import { setupIceTrickle, waitForIceGathering } from 'whip-whep-client';
const cleanup = setupIceTrickle(pc, {
mode: 'end-of-candidates', // or 'immediate'
onCandidates: async (candidates) => {
await fetch(resourceUrl, {
method: 'PATCH',
body: candidates.map((c) => `a=${c.candidate}`).join('\r\n'),
});
},
});
cleanup(); // remove event listeners when done
Media Helpers
Convenience wrappers around getUserMedia and getDisplayMedia that set contentHint automatically.
getUserStream()
import { getUserStream } from 'whip-whep-client';
const stream = await getUserStream({
videoContentHint: 'motion', // default — optimises for camera movement
audioContentHint: 'speech', // default — optimises Opus for voice
});
await client.publish(stream);
getScreenStream()
import { getScreenStream } from 'whip-whep-client';
const screen = await getScreenStream({
audio: true,
videoConstraints: { frameRate: { ideal: 15 }, width: { max: 1920 } },
});
await client.publish(screen);
Preset — LiveKit
LiveKit is an open-source WebRTC SFU with managed cloud and self-hosted options. The preset enables simulcast and H.264 by default.
import { WHIPClient, WHEPClient, livekit } from 'whip-whep-client';
// Publish
const publisher = new WHIPClient({
endpoint: 'https://my-project.livekit.cloud/rtc/whip',
...livekit.whip(accessToken),
});
// View
const viewer = new WHEPClient({
endpoint: 'https://my-project.livekit.cloud/rtc/whep',
...livekit.whep(accessToken),
});
Preset — OvenMedia Engine
OvenMedia Engine is an open-source, real-time streaming server with native WHIP and WHEP support.
import { WHIPClient, WHEPClient, ovenmedia } from 'whip-whep-client';
// Publish (no auth)
const publisher = new WHIPClient({
endpoint: 'http://localhost:3333/live/stream1',
...ovenmedia.whip(),
});
// View
const viewer = new WHEPClient({
endpoint: 'http://localhost:3334/live/stream1',
...ovenmedia.whep(),
});
Preset — Cloudflare Stream
Cloudflare Stream is a managed video platform with WHIP ingest and WHEP egress. The preset enforces H.264 and sets a 128 kbps audio limit.
import { WHIPClient, WHEPClient, cloudflare } from 'whip-whep-client';
const uid = 'customer-xyz';
const liveInputKey = 'abc123...';
const publisher = new WHIPClient({
endpoint: `https://${uid}.cloudflarestream.com/${liveInputKey}/webrtc/publish`,
...cloudflare.whip(),
});
Preset — Ant Media Server
Ant Media Server supports WHIP and WHEP in both Community and Enterprise editions.
import { WHIPClient, WHEPClient, antmedia } from 'whip-whep-client';
// Community edition (no auth)
const publisher = new WHIPClient({
endpoint: 'http://localhost:5080/WebRTCAppEE/whip/stream1',
...antmedia.whip(),
});
// Enterprise edition (JWT)
const publisher = new WHIPClient({
endpoint: 'https://ams.example.com:5443/WebRTCAppEE/whip/stream1',
...antmedia.whip(jwtToken),
});
Preset — Millicast NEW in v1.2.1
Millicast (Dolby.io Real-time Streaming) is a managed WebRTC CDN that delivers sub-second latency at global scale. The preset enforces H.264 and enables stereo Opus with DTX and FEC. Tokens must be obtained from the Millicast Director API server-side — never embed long-lived API secrets in client code.
import { WHIPClient, WHEPClient, millicast } from 'whip-whep-client';
// Publish
const publisher = new WHIPClient({
endpoint: 'https://director.millicast.com/api/whip/my-stream',
...millicast.whip(publishToken),
});
// View
const viewer = new WHEPClient({
endpoint: 'https://director.millicast.com/api/whep/my-stream',
...millicast.whep(subscribeToken),
});
Preset — SRS NEW in v1.2.1
SRS (Simple Realtime Server) is a widely-used open-source media server with native WHIP and WHEP support. Authentication is optional — configure http_hooks or security in srs.conf if needed.
import { WHIPClient, WHEPClient, srs } from 'whip-whep-client';
// Publish (no auth)
const publisher = new WHIPClient({
endpoint: 'http://localhost:1985/rtc/v1/whip/?app=live&stream=stream1',
...srs.whip(),
});
// View
const viewer = new WHEPClient({
endpoint: 'http://localhost:1985/rtc/v1/whep/?app=live&stream=stream1',
...srs.whep(),
});
Preset — MediaMTX NEW in v1.2.1
MediaMTX (formerly rtsp-simple-server) is a lightweight, zero-dependency media server with built-in WHIP and WHEP support. Authentication is configured via mediamtx.yml and is optional.
import { WHIPClient, WHEPClient, mediamtx } from 'whip-whep-client';
// Publish (no auth)
const publisher = new WHIPClient({
endpoint: 'http://localhost:8889/stream1/whip',
...mediamtx.whip(),
});
// View
const viewer = new WHEPClient({
endpoint: 'http://localhost:8889/stream1/whep',
...mediamtx.whep(),
});
Error Handling
All errors extend WhipWhepError and carry an optional status property with the HTTP response code.
import { WHIPError, WHEPError, TimeoutError, InvalidStateError } from 'whip-whep-client';
try {
await client.publish(stream);
} catch (err) {
if (err instanceof TimeoutError) {
console.error('SDP exchange timed out');
} else if (err instanceof WHIPError && err.status === 401) {
console.error('Unauthorized — check your token');
} else if (err instanceof WHIPError && err.status === 503) {
console.error('Server at capacity');
} else {
throw err;
}
}
| Class | When thrown |
|---|---|
WHIPError | WHIPClient.publish() — server rejected the offer or network error. |
WHEPError | WHEPClient.view() — server rejected the offer or network error. |
TimeoutError | SDP exchange exceeded timeout, or ICE exceeded iceConnectionTimeout. |
InvalidStateError | Method called in the wrong lifecycle state. |
TypeScript
The library is written in TypeScript and ships full type declarations. All public types are exported directly from the package root.
import type {
// Client options
WHIPClientOptions,
WHEPClientOptions,
AudioEncodingOptions,
VideoLayerOptions,
PublishOptions,
PublishScreenOptions, // NEW in v1.2
ViewOptions,
// Reconnect & adaptive quality
AutoReconnectOptions,
AdaptiveQualityOptions, // NEW in v1.1
// Events
BaseClientEvents,
WHIPClientEvents,
WHEPClientEvents,
// Stats
StreamStats,
AudioStats,
VideoStats,
ConnectionQuality,
// Misc
ClientState,
Logger,
} from 'whip-whep-client';