Skip to main content

Player Events & Liveness

The player event catalog, the manifest liveness signal, and how to tell when a stream is live.

The Native Frame web player is an event emitter. This page is the reference for the events it fires — including the manifest event, the player's live/offline signal — plus the polling behavior behind it and how to detect liveness from your backend.

Subscribing to player events

videoClient.requestPlayer(manifestUrl, options) returns a player you can subscribe to with player.on(event, handler):

import { VideoClient, types } from "@video/video-client-core";

const vc = new VideoClient(vcOptions);
const player = vc.requestPlayer(manifestUrl, playerOptions);

player.on("driverFailover", (failedOver) => {
console.log("driver failover:", failedOver);
});
Typing the manifest event

requestPlayer is typed to return PlayerAPI, but the object it returns is a manifest player, which emits the additional manifest event. To subscribe with full typing, cast to types.ManifestPlayer:

const player = vc.requestPlayer(manifestUrl, playerOptions) as types.ManifestPlayer;

player.on("manifest", ({ state, formats, viewCount }) => {
console.log("stream is", state, "with", viewCount, "viewers");
});

The manifest event — the live/offline signal

The player fetches and re-polls the stream's manifest, and emits manifest with the result. This is the signal to build live/offline UI on:

player.on("manifest", (m) => {
// m: { state: "online" | "offline" | "forbidden"; code?: number;
// formats: ManifestFormats; viewCount?: number }
});
stateMeaning
onlineThe stream is live — the manifest returned playable formats.
offlineThe stream is not live (the manifest returned 404 or no formats). The player keeps polling and transitions back to online when the stream starts.
forbiddenAccess was denied (401/403) and did not recover within the recovery window. This is terminal: the player disposes and raises an error.
  • code is the HTTP status of the manifest request.
  • formats lists the available delivery formats (mp4-hls, webrtc, jpeg, …).
  • viewCount is the stream's current viewer count — see Viewer presence.

Polling cadence

The player re-polls the manifest on an interval that depends on the last result. All four are configurable via requestPlayer options:

OptionDefaultApplies when
pollingInterval5000 msStream online
notFoundPollingInterval10000 msStream offline (last poll was 404)
unauthorizedPollingInterval2000 msLast poll was 401/403, still inside the recovery window
unauthorizedRecoveryDuration10000 msHow long 401/403 is retried before the player gives up with forbidden

So an offline stream is noticed within ~10 seconds of going live, and recorded (VOD) manifests are not polled at all.

Event catalog

Events on every player:

EventPayloadFires when
manifest{ state, code?, formats, viewCount? }Each manifest poll result — see above. (Manifest players only.)
driverstringThe active playback driver changed (e.g. "webrtc", "hlsjs").
driverFailoverbooleanThe active driver stalled and the player failed over to a backup driver.
restartDriver{ timeout: number }The player is restarting its driver.
availableQualitiesQuality[]The set of selectable quality levels changed.
currentQualityQuality | nullThe active quality level changed.
localVideoPausedbooleanPlayback was paused/resumed locally.
localAudioMutedbooleanAudio was muted/unmuted locally.
localAudioVolumenumberLocal volume changed.
forcedMutebooleanThe browser's autoplay policy forced the player to mute.
hostElementAttached{ el }The player attached to its <video> element.
videoFirstPlayFirst frame of playback.
timeupdate, progressStandard playback progress ticks.
consumerAudioEnabledbooleanThe remote peer's audio track became active/inactive (WebRTC).
consumerVideoEnabledbooleanThe remote peer's video track became active/inactive (WebRTC).
playerAccessDenied{ message: string }The access token was rejected — most commonly because a webhook token was reused (tokens must be unique per viewer).
peerAtCapacitybooleanA private stream is at its producer/peer capacity.
noPlayersbooleanNo compatible playback driver is available in this browser — see codec support.
errorErrorA player error. Always subscribe to this.
disposedThe player was disposed.
Tokens must be unique

playerAccessDenied typically means an access token was used more than once. Webhook-validated viewer tokens are single-use — issuing the same token to two viewers kicks the first one. See Webhook Authentication.

Detecting liveness from your backend

If you need liveness server-side (not in the player), poll the stream detail endpoint — but read its liveness fields carefully:

  • active is only present — as truewhile the stream is live. When the stream is offline, the field is omitted entirely; the API never returns active: false. Check for the presence of the field (data.active === true), and treat a missing field as offline. Code that waits for active === false will wait forever.
  • lastLiveAt (most recent time the stream was live) and currentLiveSince (start of the current broadcast) are likewise omitted when not applicable, and are only populated for v2-mode streams.
  • Alternatively, poll the stream's manifest URL with GET200 with formats means live, 404 means offline. (Use GET, not HEAD: the manifest endpoint returns 404 to HEAD requests even when the stream is live.)
  • For push-based liveness, the starting / closed events on the program-states webhook fire when a broadcast starts and ends.

Viewer presence

There is no push-based viewer roster. Your options:

  • Pull: the manifest document and the manifest player event both carry viewCount; there are also dedicated viewer count and stream state endpoints.
  • Push: derive joins and leaves from the joining / closed events on consuming tokens in the program-states webhook — see the presence recipe in the webhook stream-state guide.

See also