Playback
Every live stream is available two ways at once, from the same ingest. You do not choose at publish time.
| WebRTC (WHEP) | HLS | |
|---|---|---|
| Latency | Typically under a second | Several seconds |
| Reach | Modern browsers | Everything, including smart TVs |
| Best for | Auctions, betting, anything where the audience talks back | Scale, and long-tail devices |
The built-in player
Section titled “The built-in player”http://your-host:9001/watch/<application>/<stream>Public and unauthenticated, meant to be shared. It prefers WebRTC and falls back to HLS. If the broadcast ends, the page says so and reconnects on its own if it starts again.
HLS in your own player
Section titled “HLS in your own player”http://your-host:9001/live/<application>/<stream>/playlist.m3u8A standard media playlist. Segment length and how much of the live window is
retained are set in [hls].
Ad breaks appear as discontinuities, which conforming players handle without special support.
With adaptive bitrate on, the same URL serves a playlist of every rendition, and the player chooses between them as its connection changes. Nothing about the URL changes.
Viewer sessions
Section titled “Viewer sessions”Each player that opens playlist.m3u8 is redirected once to a playlist address
of its own:
/live/<application>/<stream>/s/<session>/playlist.m3u8This is how Jadarit counts HLS viewers, and where each viewer’s own ads will go. Every player in common use follows the redirect without being told to. Only the playlists are per viewer: the segments they list are the same files for everyone, so a CDN in front of the server caches each one once.
A viewer counts while their player keeps asking for the playlist, and stops
counting about 20 seconds after it stops. Query parameters on the first
request, such as playlist.m3u8?device=tv, are kept with the session.
To turn sessions off, for a player that cannot follow a redirect, set
sessions = false in [hls]. Playback
works the same, but HLS viewers are no longer counted.
Viewer counts
Section titled “Viewer counts”The dashboard shows how many people are watching each stream, and the API returns it with every stream:
"viewers": { "hls": 41, "webrtc": 12, "total": 53 }A WebRTC viewer counts from the moment they connect until the connection ends.
Behind a reverse proxy or CDN
Section titled “Behind a reverse proxy or CDN”Behind nginx, a load balancer or a CDN, every request reaches Jadarit from the
proxy, and the viewer’s own address arrives in the X-Forwarded-For header.
Jadarit believes that header only from the addresses listed in
trusted_proxies under [server]:
from anyone else, the header says whatever the sender chose.
The default trusts only the server itself, which covers nginx on the same machine and a Cloudflare Tunnel. If your CDN or load balancer connects from elsewhere, add its address ranges:
[server]trusted_proxies = ["127.0.0.1/32", "::1/128", "10.0.0.0/8"]Viewer addresses matter for ads. Ad servers choose and count ads by the viewer’s address, so a wrong one costs targeting, and a forged one would let anyone pass as any viewer.
WebRTC in your own player
Section titled “WebRTC in your own player”Jadarit speaks WHEP. POST an SDP offer and play the answer:
POST /api/v1/apps/<application>/streams/<stream>/whepContent-Type: application/sdpconst pc = new RTCPeerConnection();pc.addTransceiver('video', { direction: 'recvonly' });pc.addTransceiver('audio', { direction: 'recvonly' });
const offer = await pc.createOffer();await pc.setLocalDescription(offer);
const res = await fetch( `/api/v1/apps/${app}/streams/${stream}/whep`, { method: 'POST', headers: { 'Content-Type': 'application/sdp' }, body: offer.sdp },);await pc.setRemoteDescription({ type: 'answer', sdp: await res.text() });With adaptive bitrate on, the server moves each WebRTC viewer between
renditions by itself, and a data channel named quality lets your player offer
a quality menu. The protocol is in
Adaptive bitrate.
Serving WebRTC beyond the LAN
Section titled “Serving WebRTC beyond the LAN”WebRTC media is UDP and does not travel over an HTTP reverse proxy or a Cloudflare Tunnel. To reach viewers outside your network you need:
- a reachable address to advertise. The server discovers one by STUN at
startup, so this usually needs nothing from you. Set
public_iponly when discovery would get it wrong, such as behind a load balancer. - a fixed
udp_port_min/udp_port_maxrange, forwarded on your router. This one you do have to set: ephemeral ports cannot be forwarded.
Both are in [webrtc]. Leaving the
range at 0/0 uses ephemeral ports, which works on a LAN and nowhere else.
jadarit doctor checks all of this from the server and prints the exact ports
to forward, and to which address; see
Diagnostics.