Buffer management is the core of WebRTS’s low-latency strategy. Because live streams have no replay opportunity, every decision about when to speed up, slow down, skip, or adapt quality must be made in real time based on how much media is buffered ahead of the playback head. TheDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/CeeblueTV/wrts-client/llms.txt
Use this file to discover all available pages before exploring further.
Player class continuously measures this buffer amount in milliseconds and classifies it into one of four states that drive all adaptation logic cooperatively.
Buffer State Machine
TheBufferState enum defines four possible states for the player’s buffer:
BufferState.NONE — Stopped or Loading
BufferState.NONE — Stopped or Loading
The buffer has no meaningful state. This is the initial state when the player is stopped, and also the state during the first buffering phase before playback begins. Congestion-detection and adaptation algorithms are paused while in
NONE.BufferState.LOW — Below 150 ms (default)
BufferState.LOW — Below 150 ms (default)
The buffer is critically low, indicating network congestion or a delay in receiving data. This state triggers recovery mechanisms: the player switches to a lower video quality rendition, reduces
playbackRate, and (when player.reliable = false) aborts in-flight downloads to skip to the live edge.BufferState.OK — Within acceptable range
BufferState.OK — Within acceptable range
The buffer is healthy. Playback proceeds at
playbackRate = 1.0. No bitrate changes or frame skipping occur unless a transition out of OK is detected.BufferState.HIGH — Above 550 ms (default)
BufferState.HIGH — Above 550 ms (default)
The buffer has grown too large relative to the configured latency target, meaning the client is receiving data faster than it is consuming it. This state triggers live-sync: the player increases
playbackRate and may switch to a higher video quality rendition.Configuring Buffer Thresholds
The default thresholds are 150 ms (low) and 550 ms (high), but these can be tuned for your network conditions:bufferLimitMiddle is computed automatically as bufferLimitLow + Math.round((bufferLimitHigh - bufferLimitLow) / 2) whenever either threshold is set.
Hysteresis
State transitions include deliberate hysteresis to prevent rapid oscillation between states:- LOW → OK requires the buffer to rise above
bufferLimitMiddle(not just abovebufferLimitLow) - OK → LOW requires the buffer to fall below
bufferLimitLow - OK → HIGH requires the buffer to exceed
bufferLimitHigh - HIGH → OK requires the buffer to drop below
bufferLimitMiddle(not merely belowbufferLimitHigh)
OK band acts as a stable operating region, and the player must cross the midpoint before recovering from either a LOW or a HIGH state.
How Buffer Amount Is Measured
The player computesbufferAmount from the HTML MediaSource buffered time ranges:
endTime is the furthest buffered media position and currentTime is the current playback position (clamped to startTime if playback hasn’t advanced yet). The result is always non-negative.
onBufferChange() fires whenever bufferAmount changes by at least 50 ms (the BUFFER_CHANGE_STEP constant), keeping event frequency manageable while still reacting promptly to buffer shifts.
Playback Rate Adaptation
To gently approach the live edge without jarring jumps, the player adjusts<video>.playbackRate continuously based on buffer state. The default range is 0.84×–1.16×.
HIGH State — Speeding Up
WhenbufferState === HIGH and maxRate > 100, the rate increases linearly from a floor of 1.08× up to 1.16× (by default) based on how far the buffer exceeds bufferLimitHigh:
PLAYBACK_RATE_MAX_FLOOR = 108). This floor prevents a too-small increase that would be ineffective at draining the buffer.
LOW State — Slowing Down
WhenbufferState === LOW and minRate < 100, the rate decreases linearly from 0.92× down to 0.84× (by default) based on how far the buffer falls below bufferLimitMiddle:
PLAYBACK_RATE_MIN_CEIL = 92). Decreases smaller than 8% are considered too timid to be helpful.
OK State
WhenbufferState === OK, playbackRate is reset to exactly 1.0.
Playback Rate Constants Summary
| Constant | Value | Meaning |
|---|---|---|
PLAYBACK_RATE_MIN | 84 | Default minimum rate (0.84×) |
PLAYBACK_RATE_MIN_CEIL | 92 | Forced ceiling for minimum rate (0.92×) |
PLAYBACK_RATE_MAX_FLOOR | 108 | Forced floor for maximum rate (1.08×) |
PLAYBACK_RATE_MAX | 116 | Default maximum rate (1.16×) |
Customizing Playback Rate Range
You can narrow the range per platform or use case by overridingonBufferChange:
On iOS / Safari,
ManagedMediaSource is used instead of MediaSource. Playback rate changes on this path can produce audible audio glitches during live streaming. The default behavior still adapts the rate because the latency and stall-prevention benefits outweigh the cost for most use cases. If smooth audio matters more for your application, disable rate adaptation when ManagedMediaSource is detected:Adaptive Bitrate Interaction
When the stream provides multiple video quality renditions (Multi-Bitrate / MBR), buffer state drives track selection automatically.- Buffer LOW + stall or aborted download — the player switches to a lower video track to match available bandwidth. The current download is aborted immediately to free the connection for the lower-quality request.
- Buffer back above
bufferLimitMiddle+ bandwidth estimate allows — the player may switch to a higher video track. A “bandwidth emulation” fetch is used to probe whether the higher rendition fits within available throughput before committing. - Manual track selection — if you set
player.videoTrackto a specific track index, MBR is disabled and the player stays on that track. Setplayer.videoTrack = undefinedto re-enable automatic selection.
Frame Skipping (Unreliable Mode)
Frame skipping is the most aggressive live-edge recovery mechanism and is active only whenplayer.reliable = false (the default). It is managed by HTTPAdaptiveSource.
Sequence Skipping
When the buffer is critically low and the player is in the buffering state (recovering from a stall),HTTPAdaptiveSource calculates the delay to the live edge:
delay > maxSequenceDuration, the source increments the sequence number to skip that media segment entirely. This repeats until the delay fits within one sequence duration.
Example: If the player is 2.5 seconds behind and sequences are 1 second long, it skips sequences n and n+1 and downloads n+2 directly.
First-Frame-Only Download
For the lowest-quality video rendition (one with no lower fallback), when the buffer entersLOW state during normal playback (not a stall), HTTPAdaptiveSource performs a HEAD request to obtain the first-sample-length response header. If available, it downloads only the key frame of the segment using a byte-range request. This provides a visual update for the viewer while consuming minimal bandwidth.
When the downloaded key frame is reached, or if the buffer drops to zero mid-download, the video transfer is aborted and the remaining duration is padded to cover the full segment’s worth of time.
Audio Priority
In severe congestion scenarios, audio continuity is always preserved. Audio tracks use theskippable controller (sequence-level skipping), while video tracks in the last rendition use the alterable controller (frame-level dropping). This means audio remains continuous even when video frames are skipped, which is less jarring to the viewer than broken audio.
The Stall Recovery Flow
Stall Detected
The
HTMLVideoElement fires a waiting event. The player checks whether bufferAmount <= bufferLimitLow to confirm this is a genuine stall (not just a programmatic seek).Player Enters Buffering State
player.buffering becomes true. BufferState is forced to LOW. The video is paused. A data-timeout timer is started (default ~14 seconds) to detect complete network failure. onStall() is fired.Source Aborts In-Flight Downloads
HTTPAdaptiveSource receives the Stall event and immediately aborts all alterable and skippable controllers, freeing the HTTP/2 connection for new requests.Sequence Skipping
The source calculates the delay to the live edge. If the delay exceeds
maxSequenceDuration, it advances the sequence number to skip the gap. It verifies via a HEAD request that the target sequence exists before committing to the skip.Buffering Until Middle
The player resumes downloading from the new sequence position. It remains in the buffering state until
bufferAmount >= bufferLimitMiddle, ensuring a stable cushion before resuming playback.Buffer Events
ThePlayer class emits three buffer-related events that you can override:
| Event | When it fires | Typical use |
|---|---|---|
onBufferState(oldState) | When bufferState transitions to a new value | Logging, UI indicator updates |
onBufferChange() | When bufferAmount changes by ≥ 50 ms | Driving adjustPlaybackRate() (default behavior) |
onStall() | When a stall is detected (before recovery begins) | Logging, analytics |
Configuring Network Profiles
Buffer thresholds should be tuned based on the network conditions between the streamer and viewer. The following profiles are example configurations taken from the example player — they are not library defaults:| Profile | Description | bufferLimitLow | bufferLimitHigh | Expected latency ≈ |
|---|---|---|---|---|
| Ideal | Streamer and viewer are geographically close, minimal hops | 200 ms | 500 ms | 350 ms |
| Normal | Stable and reliable network, geographical distance negligible | 400 ms | 800 ms | 600 ms |
| Distant | Streamer and viewer are far apart or many intermediaries (VPNs, CDN) | 1000 ms | 2000 ms | 1500 ms |
| Unstable | Maximum resilience for challenging networks; latency secondary | 2000 ms | 4000 ms | 3000 ms |