Skip to main content

View a Stream

Play video streams using manifest URLs.

What is a Manifest Player?

A Manifest Player plays video streams using manifest URLs (HLS .m3u8, FLV .flv, or DASH .mpd) rather than connecting directly to a WebRTC call. This is commonly used for:

  • Transcoded broadcasts with multiple quality levels
  • Archived/recorded streams (VOD - Video on Demand)
  • CDN-delivered content
  • Scenarios where ultra-low latency isn't required

A Manifest Player plays video streams using a manifest URL (HLS .m3u8, FLV .flv, or DASH .mpd) rather than connecting directly to a WebRTC call. This is commonly used for:

  • Transcoded broadcasts with multiple quality levels
  • Archived/recorded streams (VOD)
  • CDN-delivered content
  • Scenarios where ultra-low latency isn't required

Codec Support and the Snapshot Fallback

All live browser formats are encoded as H.264 video with AAC audio — there are no VP9 or AV1 live renditions. Every mainstream browser plays H.264, so this rarely matters in practice, but on a browser or device without H.264 support the player has no playable format.

When that happens the player degrades gracefully and automatically: it emits noPlayers: true, and shows the stream's live snapshot image as a poster, refreshed on the manifest polling interval — so the viewer still sees a periodically-updating picture of the live stream. This requires nothing from your client code:

  • The manifest carries a jpeg format (formats.jpeg.encodings[] at several resolutions) and a previewImg URL; the player picks the best fit and sets it as the video poster (displayPoster option, on by default).
  • The images come from the platform's snapshot service, so they are only available while the stream is live and producing snapshots.

Subscribe to noPlayers and the manifest event (see Player Events & Liveness) if you want to detect this state and adjust your UI.

Player Component

The <Player/> component is a reusable video preview component that displays what your camera sees and provides standard device controls. This reusable component will be used as a building block for other documentation examples:

Key Points

  • The Player component uses useRequestPlayer internally to load the manifest
  • All control components must be children of the Player to access the player context
  • Controls automatically sync with player state
  • You can add, remove, or rearrange controls as needed

Imports

You'll need to import the following namespaces and constructors from the @video/video-client-react package:

/**
* IMPORT VIDEO CLIENT COMPONENTS AND HOOKS
*
* - components: Pre-built UI components (Video element wrapper)
* - context: React Context providers and hooks (PlayerAPIProvider)
* - hooks: Custom hooks for video functionality (useRequestPlayer)
*/
import { components, context, hooks } from "@video/video-client-react";

/**
* EXTRACT REQUIRED EXPORTS
*
* - PlayerAPIProvider: Context provider that makes player instance available to children
* - Video: Video element wrapper with built-in player integration
* - useRequestPlayer: Hook that requests and initializes a player from a manifest URL
*/
const { PlayerAPIProvider, usePeerAPI } = context;
const { Video } = components;
const { useRequestPlayer } = hooks;

useRequestPlayer

The useRequestPlayer hook takes a media source and returns a PlayerAPI instance.

  • Handles attaching and dettaching events to the player
/**
* REQUEST AND INITIALIZE PLAYER
*
* The useRequestPlayer hook is the core of this component. It:
* 1. Takes the manifest URL (source) and loads it
* 2. Determines the appropriate player technology (HLS.js, FLV.js, native)
* 3. Creates a player instance configured for that technology
* 4. Handles errors and loading states automatically
* 5. Returns the player instance (or null if still loading/failed)
*
* The hook also handles cleanup when the component unmounts, ensuring
* the player is properly disposed and resources are freed.
*
* PARAMETERS:
* - source: The manifest URL to load (required)
* - requestPlayerOptions: Optional configuration for the player
* - eventsMap: Optional event listeners to attach to the player
*/
const player = useRequestPlayer({ source, requestPlayerOptions, eventsMap });

Render UI

Construct your UI using Context Providers to share player instances with child components:

  • PlayerAPIProvider: Makes player control available to children
  • children: Allows re-usability and customization of component, which may vary throughout your application
/**
* LOADING STATE CHECK
*
* If the player hasn't been created yet (still loading or failed),
* return null to render nothing.
*
* In a production app, you might want to show:
* - A loading spinner while player is initializing
* - An error message if loading failed
* - A placeholder image or poster frame
*/
if (player == null) return null;

/**
* RENDER THE PLAYER UI
*
* STRUCTURE:
* PlayerAPIProvider (makes player available to children)
* └── Outer container (playerClassName)
* └── Video container (playerContainerClassName)
* ├── Video element (videoClassName) - displays the stream
* └── Children - typically control buttons and UI elements
*
* HOW IT WORKS:
* 1. PlayerAPIProvider shares the player instance via React Context
* 2. All child components can access the player using usePlayerAPI()
* 3. The Video component connects to the player and displays the video
* 4. Children (controls) automatically sync with player state
*
* IMPORTANT: The PlayerAPIProvider is crucial - without it, child
* components wouldn't be able to access the player instance.
*/
return (
<PlayerAPIProvider playerAPI={player}>
{/* Outer wrapper for styling */}
<div className={mergedClassNames.playerClassName ?? ""}>
{/* Container for video and overlays */}
<div className={mergedClassNames.playerContainerClassName ?? ""}>
{/* Video element - displays the actual stream */}
<Video id="player-video" ref={videoElement} className={mergedClassNames.videoClassName ?? ""}/>

{/* Child components - typically controls */}
{children}
</div>
</div>
</PlayerAPIProvider>
);

Full Component Code

// ManifestPlayer.tsx

/**
* ManifestPlayer Component
*
* OVERVIEW:
* This component demonstrates how to play a video stream using a manifest URL
* (HLS or FLV) instead of connecting to a live WebRTC call. This is commonly
* used for viewing transcoded streams, VOD content, or broadcasts that have
* been processed through a media server.
*
* WHAT YOU'LL LEARN:
* - How to play video streams using manifest URLs
* - How to use the Player component for manifest playback
* - How to add playback controls (play, pause, mute, volume, quality)
* - How to create custom player controls
* - The difference between manifest players and WebRTC call players
*
* KEY CONCEPTS:
* 1. MANIFEST PLAYER vs CALL PLAYER:
* - Manifest Player: Uses HLS/FLV URLs, typically for transcoded content
* - Call Player: Connects directly to WebRTC calls for low-latency streaming
*
* 2. PRE-BUILT COMPONENTS:
* - Player: Wrapper component that handles manifest loading and playback
* - Video controls: Pre-built UI components for common player operations
*
* USE CASES:
* - Viewing archived/recorded streams
* - Playing transcoded broadcasts with multiple quality levels
* - Streaming to viewers who don't need ultra-low latency
* - CDN-delivered video content
* - Mobile-optimized streaming (HLS is widely supported)
*
* @example
* ```tsx
* <ManifestPlayer />
* ```
*/

import React from "react";

/**
* IMPORT PRE-BUILT COMPONENTS
*
* The @video/video-client-react package provides ready-to-use UI components
* for common player operations. These components automatically connect to
* the player context and handle all the logic for you.
*/
import { components } from "@video/video-client-react";
/**
* EXTRACT PRE-BUILT CONTROL COMPONENTS
*
* These components provide standard video player controls:
* - VolumeRange: Slider for adjusting volume (0-100%)
* - QualitySelect: Dropdown for selecting video quality/bitrate
* - ToggleMuteButton: Button to mute/unmute audio
* - TogglePlayButton: Button to play/pause video
*
* All these components work automatically when placed inside a
* PlayerAPIProvider (which the Player component provides).
*/
const { VolumeRange, QualitySelect, ToggleMuteButton, TogglePlayButton } = components;

/**
* IMPORT CUSTOM COMPONENTS
*
* - CustomPlayButton: A custom play/pause button demonstrating how to
* create your own player controls
* - Player: A wrapper component that manages manifest loading, player
* initialization, and provides context to child components
*/
import Player from "../../components/Player";

/**
* IMPORT MANIFEST URL
*
* In this example, we import a static manifest URL for demonstration.
* In a real application, you would typically:
* - Receive the manifest URL from your backend API
* - Get it from a webhook/callback after broadcast transcoding starts
* - Fetch it from your video management system
*
* Common manifest formats:
* - HLS: .m3u8 URLs (works on iOS, Safari, and with hls.js on other browsers)
* - FLV: .flv URLs over HTTP (requires flv.js or similar)
*/
import { manifestUrl } from "../../utils";

/**
* STYLING CONFIGURATION
*
* Define CSS classes for the player and its container.
* The Player component accepts a classNames prop for customization.
*
*/
const classNames = {
playerClassNames: {
videoClassName: "md:w-1/2 object-cover overflow-hidden rounded-xl",
playerContainerClassName: "relative flex flex-row gap-2",
},
};

function ManifestPlayer(): React.ReactElement | null {
/**
* RENDER THE MANIFEST PLAYER
*
* COMPONENT STRUCTURE:
*
* Player (wrapper component)
* └── Handles manifest loading and player initialization
* └── Provides PlayerAPIProvider context to all children
* └── Renders the video element
* └── Children: Control components
* ├── QualitySelect: Choose video quality/bitrate
* ├── ToggleMuteButton: Mute/unmute audio
* ├── TogglePlayButton: Play/pause video
* ├── CustomPlayButton: Custom play/pause (demonstrates extensibility)
* └── VolumeRange: Adjust volume level
*
* HOW IT WORKS:
* 1. The Player component receives the manifest URL via the `source` prop
* 2. It uses the useRequestPlayer hook internally to load the manifest
* 3. It creates a player instance and provides it via PlayerAPIProvider
* 4. All child components can access the player through usePlayerAPI hook
* 5. Controls automatically sync with player state (paused, muted, volume, etc.)
*
* CUSTOMIZATION:
* - Add/remove control components as needed
* - Rearrange controls by changing their order
* - Use classNames prop to style the player and video
* - Create custom controls using usePlayerAPI and useEvent hooks
*
* IMPORTANT: All control components must be children of the Player component
* to access the PlayerAPIProvider context.
*/
return (
<Player source={manifestUrl} classNames={classNames.playerClassNames} >
{/* Container for all player controls */}
<div className="flex flex-col gap-2">
{/* Quality selector - shows available quality levels */}
<QualitySelect />

{/* Mute button - toggles audio on/off */}
<ToggleMuteButton />

{/* Pre-built play/pause button */}
<TogglePlayButton />

{/* Volume slider - adjusts volume from 0 to 100 */}
<VolumeRange />
</div>
</Player>
);
}

/**
* EXPORT THE COMPONENT
*
* This component is ready to use as-is, or can serve as a starting
* point for building your own manifest player with custom controls
* and styling.
*/
export default ManifestPlayer;

Manifest Player Component

Imports

Import the required components. We'll use our pre-built Player component as a wrapper to compose player controls.

import React from "react";

/**
* IMPORT PRE-BUILT COMPONENTS
*
* The @video/video-client-react package provides ready-to-use UI components
* for common player operations. These components automatically connect to
* the player context and handle all the logic for you.
*/
import { components } from "@video/video-client-react";
/**
* EXTRACT PRE-BUILT CONTROL COMPONENTS
*
* These components provide standard video player controls:
* - VolumeRange: Slider for adjusting volume (0-100%)
* - QualitySelect: Dropdown for selecting video quality/bitrate
* - ToggleMuteButton: Button to mute/unmute audio
* - TogglePlayButton: Button to play/pause video
*
* All these components work automatically when placed inside a
* PlayerAPIProvider (which the Player component provides).
*/
const { VolumeRange, QualitySelect, ToggleMuteButton, TogglePlayButton } = components;

/**
* IMPORT CUSTOM COMPONENTS
*
* - CustomPlayButton: A custom play/pause button demonstrating how to
* create your own player controls
* - Player: A wrapper component that manages manifest loading, player
* initialization, and provides context to child components
*/
import Player from "../../components/Player";

Media Source

The useRequestPlayer hook accepts a variety of Media Sources as it's source parameter, including manifest urls and MediaStreams.

In this example, we import a static manifest URL for demonstration. In a real application, you would typically:

  • Receive the manifest URL from your backend API
  • Get it from a webhook after broadcast transcoding starts
  • Fetch it from your video management system

For more info, see What are manifests?.

/**
* IMPORT MANIFEST URL
*
* In this example, we import a static manifest URL for demonstration.
* In a real application, you would typically:
* - Receive the manifest URL from your backend API
* - Get it from a webhook/callback after broadcast transcoding starts
* - Fetch it from your video management system
*
* Common manifest formats:
* - HLS: .m3u8 URLs (works on iOS, Safari, and with hls.js on other browsers)
* - FLV: .flv URLs over HTTP (requires flv.js or similar)
*/
import { manifestUrl } from "../../utils";

Render UI

Assemble your UI using modular components from the @video/video-client-react library.

/**
* RENDER THE MANIFEST PLAYER
*
* COMPONENT STRUCTURE:
*
* Player (wrapper component)
* └── Handles manifest loading and player initialization
* └── Provides PlayerAPIProvider context to all children
* └── Renders the video element
* └── Children: Control components
* ├── QualitySelect: Choose video quality/bitrate
* ├── ToggleMuteButton: Mute/unmute audio
* ├── TogglePlayButton: Play/pause video
* ├── CustomPlayButton: Custom play/pause (demonstrates extensibility)
* └── VolumeRange: Adjust volume level
*
* HOW IT WORKS:
* 1. The Player component receives the manifest URL via the `source` prop
* 2. It uses the useRequestPlayer hook internally to load the manifest
* 3. It creates a player instance and provides it via PlayerAPIProvider
* 4. All child components can access the player through usePlayerAPI hook
* 5. Controls automatically sync with player state (paused, muted, volume, etc.)
*
* CUSTOMIZATION:
* - Add/remove control components as needed
* - Rearrange controls by changing their order
* - Use classNames prop to style the player and video
* - Create custom controls using usePlayerAPI and useEvent hooks
*
* IMPORTANT: All control components must be children of the Player component
* to access the PlayerAPIProvider context.
*/
return (
<Player source={manifestUrl} classNames={classNames.playerClassNames} >
{/* Container for all player controls */}
<div className="flex flex-col gap-2">
{/* Quality selector - shows available quality levels */}
<QualitySelect />

{/* Mute button - toggles audio on/off */}
<ToggleMuteButton />

{/* Pre-built play/pause button */}
<TogglePlayButton />

{/* Volume slider - adjusts volume from 0 to 100 */}
<VolumeRange />
</div>
</Player>
);

Full Component Code

// ManifestPlayer.tsx

/**
* ManifestPlayer Component
*
* OVERVIEW:
* This component demonstrates how to play a video stream using a manifest URL
* (HLS or FLV) instead of connecting to a live WebRTC call. This is commonly
* used for viewing transcoded streams, VOD content, or broadcasts that have
* been processed through a media server.
*
* WHAT YOU'LL LEARN:
* - How to play video streams using manifest URLs
* - How to use the Player component for manifest playback
* - How to add playback controls (play, pause, mute, volume, quality)
* - How to create custom player controls
* - The difference between manifest players and WebRTC call players
*
* KEY CONCEPTS:
* 1. MANIFEST PLAYER vs CALL PLAYER:
* - Manifest Player: Uses HLS/FLV URLs, typically for transcoded content
* - Call Player: Connects directly to WebRTC calls for low-latency streaming
*
* 2. PRE-BUILT COMPONENTS:
* - Player: Wrapper component that handles manifest loading and playback
* - Video controls: Pre-built UI components for common player operations
*
* USE CASES:
* - Viewing archived/recorded streams
* - Playing transcoded broadcasts with multiple quality levels
* - Streaming to viewers who don't need ultra-low latency
* - CDN-delivered video content
* - Mobile-optimized streaming (HLS is widely supported)
*
* @example
* ```tsx
* <ManifestPlayer />
* ```
*/

import React from "react";

/**
* IMPORT PRE-BUILT COMPONENTS
*
* The @video/video-client-react package provides ready-to-use UI components
* for common player operations. These components automatically connect to
* the player context and handle all the logic for you.
*/
import { components } from "@video/video-client-react";
/**
* EXTRACT PRE-BUILT CONTROL COMPONENTS
*
* These components provide standard video player controls:
* - VolumeRange: Slider for adjusting volume (0-100%)
* - QualitySelect: Dropdown for selecting video quality/bitrate
* - ToggleMuteButton: Button to mute/unmute audio
* - TogglePlayButton: Button to play/pause video
*
* All these components work automatically when placed inside a
* PlayerAPIProvider (which the Player component provides).
*/
const { VolumeRange, QualitySelect, ToggleMuteButton, TogglePlayButton } = components;

/**
* IMPORT CUSTOM COMPONENTS
*
* - CustomPlayButton: A custom play/pause button demonstrating how to
* create your own player controls
* - Player: A wrapper component that manages manifest loading, player
* initialization, and provides context to child components
*/
import Player from "../../components/Player";

/**
* IMPORT MANIFEST URL
*
* In this example, we import a static manifest URL for demonstration.
* In a real application, you would typically:
* - Receive the manifest URL from your backend API
* - Get it from a webhook/callback after broadcast transcoding starts
* - Fetch it from your video management system
*
* Common manifest formats:
* - HLS: .m3u8 URLs (works on iOS, Safari, and with hls.js on other browsers)
* - FLV: .flv URLs over HTTP (requires flv.js or similar)
*/
import { manifestUrl } from "../../utils";

/**
* STYLING CONFIGURATION
*
* Define CSS classes for the player and its container.
* The Player component accepts a classNames prop for customization.
*
*/
const classNames = {
playerClassNames: {
videoClassName: "md:w-1/2 object-cover overflow-hidden rounded-xl",
playerContainerClassName: "relative flex flex-row gap-2",
},
};

function ManifestPlayer(): React.ReactElement | null {
/**
* RENDER THE MANIFEST PLAYER
*
* COMPONENT STRUCTURE:
*
* Player (wrapper component)
* └── Handles manifest loading and player initialization
* └── Provides PlayerAPIProvider context to all children
* └── Renders the video element
* └── Children: Control components
* ├── QualitySelect: Choose video quality/bitrate
* ├── ToggleMuteButton: Mute/unmute audio
* ├── TogglePlayButton: Play/pause video
* ├── CustomPlayButton: Custom play/pause (demonstrates extensibility)
* └── VolumeRange: Adjust volume level
*
* HOW IT WORKS:
* 1. The Player component receives the manifest URL via the `source` prop
* 2. It uses the useRequestPlayer hook internally to load the manifest
* 3. It creates a player instance and provides it via PlayerAPIProvider
* 4. All child components can access the player through usePlayerAPI hook
* 5. Controls automatically sync with player state (paused, muted, volume, etc.)
*
* CUSTOMIZATION:
* - Add/remove control components as needed
* - Rearrange controls by changing their order
* - Use classNames prop to style the player and video
* - Create custom controls using usePlayerAPI and useEvent hooks
*
* IMPORTANT: All control components must be children of the Player component
* to access the PlayerAPIProvider context.
*/
return (
<Player source={manifestUrl} classNames={classNames.playerClassNames} >
{/* Container for all player controls */}
<div className="flex flex-col gap-2">
{/* Quality selector - shows available quality levels */}
<QualitySelect />

{/* Mute button - toggles audio on/off */}
<ToggleMuteButton />

{/* Pre-built play/pause button */}
<TogglePlayButton />

{/* Volume slider - adjusts volume from 0 to 100 */}
<VolumeRange />
</div>
</Player>
);
}

/**
* EXPORT THE COMPONENT
*
* This component is ready to use as-is, or can serve as a starting
* point for building your own manifest player with custom controls
* and styling.
*/
export default ManifestPlayer;

Full Code

// Player.tsx

/**
* Player Component
*
* OVERVIEW:
* A reusable video player component that handles manifest loading, player
* initialization, and provides context for control components. This component
* abstracts away the complexity of requesting a player, managing its lifecycle,
* and providing it to child components.
*
* WHAT IT DOES:
* - Loads and plays video streams from manifest URLs (HLS, FLV, DASH)
* - Initializes the player using the useRequestPlayer hook
* - Provides PlayerAPIProvider context to all children
* - Renders the video element with customizable styling
* - Manages player lifecycle (creation and disposal)
*
* KEY FEATURES:
* - Fully self-contained: Handles all player setup automatically
* - Customizable styling via classNames prop
* - Supports children for adding custom controls
* - Works with any manifest format supported by the video client
* - Automatic error handling and loading states
*
* REQUIREMENTS:
* - A valid manifest URL (HLS .m3u8, FLV .flv, or DASH .mpd)
* - No additional context providers needed (self-contained)
*
* @example Basic usage:
* ```tsx
* <Player source="https://example.com/stream.m3u8">
* <TogglePlayButton />
* <ToggleMuteButton />
* </Player>
* ```
*
* @example With custom styling:
* ```tsx
* <Player
* source={manifestUrl}
* classNames={{
* videoClassName: "w-full h-auto",
* playerContainerClassName: "relative"
* }}
* >
* <YourCustomControls />
* </Player>
* ```
*/


import React, {useRef} from "react";
/**
* IMPORT VIDEO CLIENT COMPONENTS AND HOOKS
*
* - components: Pre-built UI components (Video element wrapper)
* - context: React Context providers and hooks (PlayerAPIProvider)
* - hooks: Custom hooks for video functionality (useRequestPlayer)
*/
import { components, context, hooks } from "@video/video-client-react";

/**
* EXTRACT REQUIRED EXPORTS
*
* - PlayerAPIProvider: Context provider that makes player instance available to children
* - Video: Video element wrapper with built-in player integration
* - useRequestPlayer: Hook that requests and initializes a player from a manifest URL
*/
const { PlayerAPIProvider, usePeerAPI } = context;
const { Video } = components;
const { useRequestPlayer } = hooks;

/**
* STYLING INTERFACE
*
* Defines the CSS classes that can be customized for different parts
* of the player component. All fields are optional.
*/
export interface PlayerClassNames {
playerClassName: string;
videoClassName: string;
displayNameClassName: string;
playerContainerClassName: string;
}

/**
* DEFAULT STYLING
*
* Default styling using Tailwind CSS classes.
*/
const defaultClassNames: PlayerClassNames = {
playerClassName: "bg-black-200",
videoClassName: "h-56 w-56 object-cover overflow-hidden rounded-xl",
displayNameClassName: "text-sm text-white absolute bottom-1 left-1 z-[200] bg-black/50 px-2 py-1 rounded-md",
playerContainerClassName: "relative h-56 w-56",
};

/**
* PROPS INTERFACE
*
* Extends UseRequestPlayerOptions to inherit all player configuration options
* like source, requestPlayerOptions, and eventsMap.
*/
interface PlayerProps extends hooks.UseRequestPlayerOptions {
/** Child components (typically player controls) */
children?: React.ReactNode;
/** Optional custom class names (partial override of defaults) */
classNames?: Partial<PlayerClassNames>;
}


function Player({source, children, classNames, requestPlayerOptions, eventsMap}: PlayerProps): JSX.Element | null {
/**
* MERGE CUSTOM AND DEFAULT CLASS NAMES
*
* Combines default styling with any custom overrides provided via props.
*/
const mergedClassNames = { ...defaultClassNames, ...classNames };

/**
* VIDEO ELEMENT REF
*
* Creates a reference to the HTML video element.
* While not strictly required for basic playback (the Video component handles
* this internally), having a ref available can be useful for:
* - Direct DOM manipulation if needed
* - Integration with third-party libraries
* - Advanced video element access
*/
const videoElement = useRef<HTMLVideoElement>(null);

/**
* REQUEST AND INITIALIZE PLAYER
*
* The useRequestPlayer hook is the core of this component. It:
* 1. Takes the manifest URL (source) and loads it
* 2. Determines the appropriate player technology (HLS.js, FLV.js, native)
* 3. Creates a player instance configured for that technology
* 4. Handles errors and loading states automatically
* 5. Returns the player instance (or null if still loading/failed)
*
* The hook also handles cleanup when the component unmounts, ensuring
* the player is properly disposed and resources are freed.
*
* PARAMETERS:
* - source: The manifest URL to load (required)
* - requestPlayerOptions: Optional configuration for the player
* - eventsMap: Optional event listeners to attach to the player
*/
const player = useRequestPlayer({ source, requestPlayerOptions, eventsMap });

/**
* LOADING STATE CHECK
*
* If the player hasn't been created yet (still loading or failed),
* return null to render nothing.
*
* In a production app, you might want to show:
* - A loading spinner while player is initializing
* - An error message if loading failed
* - A placeholder image or poster frame
*/
if (player == null) return null;

/**
* RENDER THE PLAYER UI
*
* STRUCTURE:
* PlayerAPIProvider (makes player available to children)
* └── Outer container (playerClassName)
* └── Video container (playerContainerClassName)
* ├── Video element (videoClassName) - displays the stream
* └── Children - typically control buttons and UI elements
*
* HOW IT WORKS:
* 1. PlayerAPIProvider shares the player instance via React Context
* 2. All child components can access the player using usePlayerAPI()
* 3. The Video component connects to the player and displays the video
* 4. Children (controls) automatically sync with player state
*
* IMPORTANT: The PlayerAPIProvider is crucial - without it, child
* components wouldn't be able to access the player instance.
*/
return (
<PlayerAPIProvider playerAPI={player}>
{/* Outer wrapper for styling */}
<div className={mergedClassNames.playerClassName ?? ""}>
{/* Container for video and overlays */}
<div className={mergedClassNames.playerContainerClassName ?? ""}>
{/* Video element - displays the actual stream */}
<Video id="player-video" ref={videoElement} className={mergedClassNames.videoClassName ?? ""}/>

{/* Child components - typically controls */}
{children}
</div>
</div>
</PlayerAPIProvider>
);
}

/**
* EXPORT THE PLAYER COMPONENT
*
* This component is designed to be highly reusable. It can be used:
* - As-is for basic manifest playback
* - As a wrapper for custom player UIs
* - In galleries showing multiple streams
* - For VOD, live streams, or transcoded content
*/
export default Player;
// ManifestPlayer.tsx

/**
* ManifestPlayer Component
*
* OVERVIEW:
* This component demonstrates how to play a video stream using a manifest URL
* (HLS or FLV) instead of connecting to a live WebRTC call. This is commonly
* used for viewing transcoded streams, VOD content, or broadcasts that have
* been processed through a media server.
*
* WHAT YOU'LL LEARN:
* - How to play video streams using manifest URLs
* - How to use the Player component for manifest playback
* - How to add playback controls (play, pause, mute, volume, quality)
* - How to create custom player controls
* - The difference between manifest players and WebRTC call players
*
* KEY CONCEPTS:
* 1. MANIFEST PLAYER vs CALL PLAYER:
* - Manifest Player: Uses HLS/FLV URLs, typically for transcoded content
* - Call Player: Connects directly to WebRTC calls for low-latency streaming
*
* 2. PRE-BUILT COMPONENTS:
* - Player: Wrapper component that handles manifest loading and playback
* - Video controls: Pre-built UI components for common player operations
*
* USE CASES:
* - Viewing archived/recorded streams
* - Playing transcoded broadcasts with multiple quality levels
* - Streaming to viewers who don't need ultra-low latency
* - CDN-delivered video content
* - Mobile-optimized streaming (HLS is widely supported)
*
* @example
* ```tsx
* <ManifestPlayer />
* ```
*/

import React from "react";

/**
* IMPORT PRE-BUILT COMPONENTS
*
* The @video/video-client-react package provides ready-to-use UI components
* for common player operations. These components automatically connect to
* the player context and handle all the logic for you.
*/
import { components } from "@video/video-client-react";
/**
* EXTRACT PRE-BUILT CONTROL COMPONENTS
*
* These components provide standard video player controls:
* - VolumeRange: Slider for adjusting volume (0-100%)
* - QualitySelect: Dropdown for selecting video quality/bitrate
* - ToggleMuteButton: Button to mute/unmute audio
* - TogglePlayButton: Button to play/pause video
*
* All these components work automatically when placed inside a
* PlayerAPIProvider (which the Player component provides).
*/
const { VolumeRange, QualitySelect, ToggleMuteButton, TogglePlayButton } = components;

/**
* IMPORT CUSTOM COMPONENTS
*
* - CustomPlayButton: A custom play/pause button demonstrating how to
* create your own player controls
* - Player: A wrapper component that manages manifest loading, player
* initialization, and provides context to child components
*/
import Player from "../../components/Player";

/**
* IMPORT MANIFEST URL
*
* In this example, we import a static manifest URL for demonstration.
* In a real application, you would typically:
* - Receive the manifest URL from your backend API
* - Get it from a webhook/callback after broadcast transcoding starts
* - Fetch it from your video management system
*
* Common manifest formats:
* - HLS: .m3u8 URLs (works on iOS, Safari, and with hls.js on other browsers)
* - FLV: .flv URLs over HTTP (requires flv.js or similar)
*/
import { manifestUrl } from "../../utils";

/**
* STYLING CONFIGURATION
*
* Define CSS classes for the player and its container.
* The Player component accepts a classNames prop for customization.
*
*/
const classNames = {
playerClassNames: {
videoClassName: "md:w-1/2 object-cover overflow-hidden rounded-xl",
playerContainerClassName: "relative flex flex-row gap-2",
},
};

function ManifestPlayer(): React.ReactElement | null {
/**
* RENDER THE MANIFEST PLAYER
*
* COMPONENT STRUCTURE:
*
* Player (wrapper component)
* └── Handles manifest loading and player initialization
* └── Provides PlayerAPIProvider context to all children
* └── Renders the video element
* └── Children: Control components
* ├── QualitySelect: Choose video quality/bitrate
* ├── ToggleMuteButton: Mute/unmute audio
* ├── TogglePlayButton: Play/pause video
* ├── CustomPlayButton: Custom play/pause (demonstrates extensibility)
* └── VolumeRange: Adjust volume level
*
* HOW IT WORKS:
* 1. The Player component receives the manifest URL via the `source` prop
* 2. It uses the useRequestPlayer hook internally to load the manifest
* 3. It creates a player instance and provides it via PlayerAPIProvider
* 4. All child components can access the player through usePlayerAPI hook
* 5. Controls automatically sync with player state (paused, muted, volume, etc.)
*
* CUSTOMIZATION:
* - Add/remove control components as needed
* - Rearrange controls by changing their order
* - Use classNames prop to style the player and video
* - Create custom controls using usePlayerAPI and useEvent hooks
*
* IMPORTANT: All control components must be children of the Player component
* to access the PlayerAPIProvider context.
*/
return (
<Player source={manifestUrl} classNames={classNames.playerClassNames} >
{/* Container for all player controls */}
<div className="flex flex-col gap-2">
{/* Quality selector - shows available quality levels */}
<QualitySelect />

{/* Mute button - toggles audio on/off */}
<ToggleMuteButton />

{/* Pre-built play/pause button */}
<TogglePlayButton />

{/* Volume slider - adjusts volume from 0 to 100 */}
<VolumeRange />
</div>
</Player>
);
}

/**
* EXPORT THE COMPONENT
*
* This component is ready to use as-is, or can serve as a starting
* point for building your own manifest player with custom controls
* and styling.
*/
export default ManifestPlayer;

Key Concepts

Player

The Player instance manages video playback:

  • Loads and parses manifest files
  • Handles adaptive bitrate streaming (automatically switches quality based on network conditions)
  • Provides playback control methods (play(), pause(), mute(), unmute())
  • Manages audio and video state

Manifest URL

A manifest file contains information about available video streams:

  • HLS (.m3u8): Apple's HTTP Live Streaming format, widely supported
  • DASH (.mpd): MPEG-DASH format, ISO standard for adaptive streaming
  • FLV (.flv): Flash Video format, used for low-latency streaming
  • JSON: Native Frame's multi-format manifest containing all available options

Adaptive Bitrate Streaming

The player automatically adjusts video quality based on:

  • Available bandwidth
  • Device capabilities
  • Buffer health
  • Network conditions

This ensures smooth playback with minimal buffering.

Player Properties

Key properties for controlling playback:

  • localVideoPaused: Check if video is paused
  • localAudioMuted: Check if audio is muted
  • localAudioVolume: Get/set volume (0-1 range)

Manifest Player vs WebRTC Player

FeatureManifest PlayerWebRTC Player
ConnectionHLS/FLV/DASH URLsDirect to live calls
AuthenticationNot requiredRequired
Adaptive BitrateSupportedN/A
LatencyHigher (3-30 seconds)Ultra-low (sub-second)
Best ForVOD, CDN-delivered contentReal-time interaction

Next Steps

To learn more about the video-client-core library and advanced features: