Live
Live edge state for the player store
Tracks when playback is at the live edge and what kind of live window the stream provides.
liveEdgeStartis the playback time where the live edge begins. Playback counts as live whencurrentTimereaches this value.targetLiveWindowdescribes the kind of live window. Despite its name, it does not report a number of seconds:0means a sliding live window,Infinitymeans a live event with playback history, andNaNmeans on-demand or not known yet.
Both values are NaN when the media does not provide live-edge information.
Use the buffer feature to find the times a viewer can seek to. Its seekable value contains [start, end] pairs. On a sliding live stream, the oldest and newest available times both move forward.
The live presets do not include the stream type feature, so selectStreamType returns undefined unless you add it to a custom player.
The time feature reports the end of the available live video as duration, even when the browser reports Infinity. To check for live playback, use !Number.isNaN(targetLiveWindow) instead of checking duration.
Import
import { liveFeature } from '@videojs/react';import { liveFeature } from '@videojs/html';The liveAudioFeatures and liveVideoFeatures feature bundles include this feature.
API Reference
State
| Property | Type | Details |
|---|---|---|
liveEdgeStart | number | |
| ||
targetLiveWindow | number | |
| ||
Selector
Pass selectLive to usePlayer to subscribe to live state. Returns undefined if the live feature is not configured.
Pass selectLive to PlayerController to subscribe to live state. Returns undefined if the live feature is not configured.
import { selectLive, selectTime, usePlayer } from '@videojs/react';
function LiveEdgeIndicator() {
const live = usePlayer(selectLive);
const time = usePlayer(selectTime);
if (!live || Number.isNaN(live.targetLiveWindow)) return null;
const atEdge = time != null && time.currentTime >= live.liveEdgeStart;
return <span className="live-indicator">{atEdge ? 'LIVE' : 'BEHIND LIVE'}</span>;
}import { createPlayer, UIElement, selectLive } from '@videojs/html';
import { liveVideoFeatures } from '@videojs/html/live-video';
const { PlayerController } = createPlayer({ features: liveVideoFeatures });
class LiveEdgeButton extends UIElement {
readonly #live = new PlayerController(this, selectLive);
}Jump to the live edge
Use the packaged live button for the usual jump-to-live behavior. It combines the live, time, and buffer features, then seeks to the end of the last seekable range. It also manages its accessible label and disabled state.
import { LiveButton } from '@videojs/react';
export function StationLiveButton() {
return <LiveButton className="station-live-button">On air</LiveButton>;
}<script type="module">
import '@videojs/html/ui/live-button';
</script>
<media-live-button class="station-live-button">On air</media-live-button>Put the button in your custom controls and style it there. Do not calculate the destination from duration or set currentTime to Infinity.
If you need different behavior, you can combine the same public features yourself:
- Read
targetLiveWindowfromselectLiveto check that the source is live. - Read the newest available time from the last range returned by
selectBuffer. - Call the
seek()action returned byselectTimewith that time.
import { selectBuffer, selectLive, selectTime, usePlayer } from '@videojs/react';
export function CustomLiveButton() {
const live = usePlayer(selectLive);
const time = usePlayer(selectTime);
const buffer = usePlayer(selectBuffer);
const newestTime = buffer?.seekable.at(-1)?.[1];
const jumpToLive = () => {
if (!time || newestTime === undefined || !Number.isFinite(newestTime)) return;
void time.seek(newestTime);
};
const disabled = !live || Number.isNaN(live.targetLiveWindow) || !Number.isFinite(newestTime);
return (
<button type="button" disabled={disabled} onClick={jumpToLive}>
Go live
</button>
);
}Read the same three selectors with PlayerController. Call selectTime’s seek() action with the last end time from selectBuffer when selectLive reports a live source.