A live stream reaches its audience one of two ways, and your app doesn't have to care which:
- Real time (
RTC_ONLY). Viewers join over WebRTC, roughly a quarter of a second behind the host. With edge scaling, viewers past the stream's first room are seated on edge rooms on other media nodes, so one stream isn't capped by one node. - CDN (
BROADCAST). Viewers watch HLS: an adaptive 720p/480p/240p ladder in 2-second segments, a few seconds behind, at CDN cost. The SDKs play it for you.
Where this fits: you create streams and mint viewer tokens from your backend (Viewers). Edge scaling is one flag at creation. HLS playback is one flag on the viewer-token call.
Edge scaling
Create the stream with edgeScaling: true:
const stream = await livqeno.liveStreams.create({
title: 'Product launch',
hostIdentity: 'alice',
edgeScaling: true,
});from livqeno import CreateLiveStreamParams
stream = livqeno.live_streams.create(
CreateLiveStreamParams(title="Product launch", host_identity="alice", edge_scaling=True)
)Then mint viewer tokens exactly as before. What changes is where the viewer is seated:
- The first viewers join the stream's own room, called the origin.
- Once the origin has its share, new viewers go to edge room 1, then edge room 2, and so on. Edges are filled one at a time, and a spare is kept warm, so nobody waits for a room to come up.
- Each edge carries the same hosts and tracks as the origin. Hosts publish once and never see the edges. An edge that empties out is closed after a couple of minutes.
- For very large audiences the edges form a tree. The origin feeds up to 8 edges directly, and each further edge is fed by an existing edge instead of the origin. Two levels give 64 edges, each holding a full room of viewers. The extra hop adds roughly a network round trip, not seconds.
A cascaded viewer's credential names its edge in rtcRoom
(stream_…__e2). @livqeno/live, React Native and Flutter join
whatever rtcRoom says, so your client code doesn't change. Forward the
credential as-is.
Viewer quality
Edges forward every simulcast layer, so each viewer gets the quality its connection and screen can take, the same as on the origin. To let viewers choose for themselves:
live.setPreferredQuality('low'); // 'auto' | 'high' | 'medium' | 'low'auto (the default) sizes video to the element and the network. The other
values pin a layer, including for hosts who join later. On HLS the same
call pins a rendition in hls.js. Safari's native player always chooses for
itself.
HLS for broadcast streams
A BROADCAST stream's viewers don't join a room. Ask for a playback
credential by passing acceptPlayback, and you get chat plus an HLS URL
instead of a 409:
const credential = await livqeno.liveStreams.createViewerToken(stream.id, 'viewer-42', {
acceptPlayback: true,
});
// credential.playback.url → https://cdn…/stream_…/master.m3u8credential = livqeno.live_streams.create_viewer_token(stream["id"], "viewer-42", accept_playback=True)On the device, join with the credential either way and play it if it's HLS:
import { LiveStream } from '@livqeno/live';
const live = await LiveStream.join(credential);
if (live.delivery === 'hls') {
await live.attachPlayer(document.querySelector('video')!);
} else {
live.room.on('trackSubscribed', (track) => {
/* render the host's tracks */
});
}- Web: Safari plays HLS natively. Other browsers need
hls.jsinstalled (npm install hls.js), which is loaded only when a viewer actually gets an HLS credential. - React Native: render
<LiveStreamPlayer url={stream.playbackUrl!} />. It needsreact-native-video. - Flutter: render
LivqenoLivePlayer(url: stream.playbackUrl!).
Players start near the live edge and keep retrying while the stream is still
starting. playback.url can exist a few seconds before the first segment
does.
Low-latency HLS
When the deployment serves it, the credential also carries
playback.llhlsUrl: Low-Latency HLS with 0.5-second parts and blocking
playlist reloads, targeting about 2 seconds behind the host instead of
5–7. The SDK players use it automatically and fall back to playback.url
if it fails:
- Web:
attachPlayer()plays it with hls.js in low-latency mode, or with Safari's native player. - React Native:
<LiveStreamPlayer url={stream.playbackUrl!} lowLatencyUrl={stream.lowLatencyPlaybackUrl} /> - Flutter:
LivqenoLivePlayer(url: stream.playbackUrl!, lowLatencyUrl: stream.lowLatencyPlaybackUrl)
Recordings always come from the standard HLS copy.
Choosing
Real time (RTC_ONLY) | Real time + edgeScaling | CDN (BROADCAST) | |
|---|---|---|---|
| Delay behind the host | ~0.3 s | ~0.3 s | ~2 s (LL-HLS) / ~5–7 s (HLS) |
| Audience | one media node | many nodes | CDN |
| Viewers can go on stage | yes | yes | yes (Stage) |
| Best for | calls with an audience, small shows | live auctions, interactive shows, classes | webinars, sports, very large audiences |