Skip to content
FrameworkStyle

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.

  • liveEdgeStart is the playback time where the live edge begins. Playback counts as live when currentTime reaches this value.
  • targetLiveWindow describes the kind of live window. Despite its name, it does not report a number of seconds: 0 means a sliding live window, Infinity means a live event with playback history, and NaN means 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';

The liveAudioFeatures and liveVideoFeatures feature bundles include this feature.

API Reference

State

PropertyTypeDetails
liveEdgeStartnumber
targetLiveWindownumber

Selector

Pass selectLive to usePlayer 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>;
}

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>;
}

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 targetLiveWindow from selectLive to check that the source is live.
  • Read the newest available time from the last range returned by selectBuffer.
  • Call the seek() action returned by selectTime with 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>
  );
}