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
jpegformat (formats.jpeg.encodings[]at several resolutions) and apreviewImgURL; the player picks the best fit and sets it as the video poster (displayPosteroption, 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.
- React
- Vanilla JavaScript
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
useRequestPlayerinternally 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;
This guide demonstrates how to play a livestream using a manifest URL with the @video/video-client-core library and vanilla JavaScript (no framework required).
The manifest player application consists of three main files:
- manifest-player.html - HTML structure and script imports
- player-utils.js - Utility functions for creating player UI and handling controls
- manifest-player.js - Main application logic for loading and controlling the player
Prerequisites
Before you begin, you'll need:
- Manifest URL: A URL pointing to your stream manifest (HLS, FLV, DASH, or Native Frame JSON format)
In a production application, you would typically receive the manifest URL from your backend API or video management system.
For more information, see What are manifests?.
HTML
First, create an HTML file that loads the video-client-core library and your JavaScript modules.
videoContainer: Will be populated with video player and controls
<!--
Native Frame Manifest Player - Vanilla JavaScript Implementation
This HTML file demonstrates how to play a livestream using a manifest URL
with the @video/video-client-core library and vanilla JavaScript (no framework required).
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
The manifest player allows viewers to:
- Play/pause video
- Control volume and muting
- Automatically handle adaptive bitrate streaming
- Play recorded or live streams
Required Setup:
1. Obtain a manifest URL from your Native Frame backend or CDN
2. Load the video-client-core library via CDN
3. Include the necessary JavaScript modules
-->
<!doctype html>
<html>
<head>
<!--
Import Map Configuration
This configures module resolution for ES modules. The "vdc-cdn" alias
points to the video-client-core library hosted on the Native Frame CDN.
You can update the version number in the URL to use a different version
of the library. Check https://cdn.nativeframe.com/ for available versions.
-->
<script type="importmap">
{
"imports": {
"vdc-cdn": "https://cdn.nativeframe.com/video-client-core-13.2.0.js"
}
}
</script>
<!-- << imports -->
<!--
JavaScript Module Imports
These modules must be loaded in order:
1. player-utils.js - Provides helper functions for creating the player UI
2. manifest-player.js - Main application logic for loading and controlling the player
-->
<script type="module" src="/js/player-utils.js"></script>
<script type="module" src="/js/manifest-player.js"></script>
</head>
<body>
<!--
Player UI Structure
The page consists of a single container element:
- videoContainer: Will be populated with the video player and playback controls
The video element and controls are dynamically created and injected by
the player-utils.js module.
-->
<div>
<!-- Video player and controls will be injected here -->
<div id="videoContainer"></div>
</div>
</body>
</html>
Using JavaScript Modules
The import map tells the browser where to find the vdc-cdn module. You can update the version number in the URL to use different versions of the video-client-core library.
These modules must be loaded as ES modules (type="module") and in this order:
player-utils.js- Provides UI helper functionsmanifest-player.js- Contains main application logic
<!--
Import Map Configuration
This configures module resolution for ES modules. The "vdc-cdn" alias
points to the video-client-core library hosted on the Native Frame CDN.
You can update the version number in the URL to use a different version
of the library. Check https://cdn.nativeframe.com/ for available versions.
-->
<script type="importmap">
{
"imports": {
"vdc-cdn": "https://cdn.nativeframe.com/video-client-core-13.2.0.js"
}
}
</script>
<!--
JavaScript Module Imports
These modules must be loaded in order:
1. player-utils.js - Provides helper functions for creating the player UI
2. manifest-player.js - Main application logic for loading and controlling the player
-->
<script type="module" src="/js/player-utils.js"></script>
<script type="module" src="/js/manifest-player.js"></script>
JavaScript
Player Utils
This module provides utility functions for creating and managing the player UI.
/**
* Player Utilities Module
*
* This module provides utility functions for creating and managing manifest player UI.
* It handles:
* - Creating video elements and playback controls
* - Appending player UI to the DOM
* - Attaching event handlers for player controls
* - Removing event handlers on cleanup
*
* Key Concepts:
* - Player: The video player instance that manages playback of manifest streams
* - Video Element: The HTML <video> tag where the stream is displayed
* - Playback Controls: Buttons and inputs for play/pause, mute, and volume control
*/
/**
* Attach Event Handlers to Player Controls
*
* Sets up click and change event handlers for all player UI controls.
* This enables users to:
* - Play/pause video playback
* - Mute/unmute audio
* - Adjust volume level
*
* @param {Player} player - The player instance to control
* @param {string} id - Unique identifier for the video element and its controls
*
* The player object provides these key properties:
* - localVideoPaused: Boolean indicating if video is paused
* - localAudioMuted: Boolean indicating if audio is muted
* - localAudioVolume: Number (0-1) representing volume level
*/
function attachPlayerClickHandlers(player, id) {
/**
* Toggle Play/Pause
*
* Toggles between playing and pausing the video stream.
* The player.localVideoPaused property indicates current playback state.
*
* @param {Event} event - Click event from the play button
*/
async function togglePlay(event) {
if (player.localVideoPaused) {
// Resume playback
await player.play();
event.target.textContent = "Pause";
} else {
// Pause playback
await player.pause();
event.target.textContent = "Play";
}
}
/**
* Toggle Mute/Unmute
*
* Toggles audio muting on and off.
* When muted, the video continues playing but no audio is heard.
*
* @param {Event} event - Click event from the mute button
*/
async function toggleMute(event) {
if (player.localAudioMuted) {
// Unmute audio
await player.unmute();
event.target.textContent = "Mute";
} else {
// Mute audio
await player.mute();
event.target.textContent = "Unmute";
}
}
/**
* Handle Volume Change
*
* Adjusts the audio volume based on slider input.
* Volume is converted from 0-100 range to 0-1 range.
* Setting volume to 0 automatically mutes the player.
*
* @param {Event} ev - Change event from the volume slider
*/
function handleVolume(ev) {
const volumeValue = Number(ev.target.value);
if (volumeValue === 0) {
// Mute when volume is set to 0
player.localAudioMuted = true;
} else {
// Unmute when volume is above 0
player.localAudioMuted = false;
}
// Set volume (convert from 0-100 to 0-1)
player.localAudioVolume = volumeValue / 100;
}
/**
* Wire Up Event Listeners
*
* Connect the handler functions to the corresponding DOM elements.
* Sets initial button text to reflect starting player state.
*/
document.getElementById(`playBtn-${id}`).onclick = togglePlay;
document.getElementById(`playBtn-${id}`).textContent = "Pause"; // Default state is playing
document.getElementById(`muteBtn-${id}`).onclick = toggleMute;
document.getElementById(`muteBtn-${id}`).textContent = "Mute"; // Default state is unmuted
document.getElementById(`volume-${id}`).onchange = handleVolume;
}
/**
* Remove Event Listeners from Player Controls
*
* Cleans up event listeners when the player is disposed.
* This prevents memory leaks and ensures proper resource cleanup.
*
* @param {string} id - Unique identifier for the video element and its controls
*
* Important: Always call this function before disposing of a player to
* properly clean up event handlers and prevent memory leaks.
*/
function removePlayerClickHandlers(id) {
// Remove event listeners from all control elements
document.getElementById(`playBtn-${id}`).removeAllListeners();
document.getElementById(`muteBtn-${id}`).removeAllListeners();
document.getElementById(`volume-${id}`).removeAllListeners();
}
/**
* Generate HTML for Video Player and Controls
*
* Creates the HTML structure for the video element and playback controls.
* This includes:
* - Video element for displaying the stream
* - Play/pause toggle button
* - Mute/unmute toggle button
* - Volume slider control
*
* @param {string} id - Unique identifier for the video element and its controls
* @returns {string} HTML string containing the player UI structure
*
* The generated HTML will be injected into the videoContainer div.
* Each control element is given a unique ID based on the provided id parameter,
* allowing multiple players on the same page.
*/
function createVideoElement(id) {
return `
<div id="video-wrapper-${id}">
<!-- Video element where the stream will be displayed -->
<video
width="100%"
height="100%"
id="${id}"
>
</video>
<!-- Play/pause toggle button -->
<button id="playBtn-${id}"></button>
<!-- Mute/unmute toggle button -->
<button id="muteBtn-${id}"></button>
<!-- Volume control slider -->
<div class="volume-container">
<label for="volume-${id}">Volume: </label>
<input type="range" id="volume-${id}" min="0" max="100" value="50" />
</div>
</div>
`;
}
/**
* Append Video Player to DOM
*
* Injects the player UI into the page and returns a reference to the video element.
* This function:
* 1. Finds the video container div
* 2. Inserts the player HTML
* 3. Returns the video element for player attachment
*
* @param {string} id - Unique identifier for the video element
* @returns {HTMLVideoElement} Reference to the video element
*
* The video element is returned so the Player can be attached to it using
* player.attachTo(videoElement).
*/
function appendVideoElement(id) {
// Find the container where we'll inject the player UI
const videoContainer = document.getElementById("videoContainer");
// Insert the player HTML into the container
videoContainer.insertAdjacentHTML("beforeend", createVideoElement(id));
// Return reference to the video element
return document.getElementById(id);
}
/**
* Export Utility Functions
*
* These functions are used by the main manifest-player.js module to
* create and manage the player UI.
*/
export { attachPlayerClickHandlers, removePlayerClickHandlers, appendVideoElement };
Main Application Logic (manifest-player.js)
Manifest URL Configuration
The manifest URL points to the stream you want to play. This can be:
- HLS manifest (.m3u8) - Most common for live and VOD streaming
- FLV manifest (.flv) - Used for Flash-based streaming
- DASH manifest (.mpd) - Alternative adaptive streaming format
- Native Frame JSON manifest (.json) - Contains multiple format options
In a production application, you would typically:
- Fetch this URL from your backend API
- Receive it from a webhook when transcoding starts
- Get it from your video management system
- Pass it as a URL parameter or configuration
/**
* Native Frame Manifest Player - Main Application
*
* This is the main application file for the manifest player. It handles:
* - Loading and playing video streams from manifest URLs
* - Creating and managing the player UI
* - Handling playback controls
* - Resource cleanup
*
* Flow:
* 1. Page loads → init() is called
* 2. Player is requested with the manifest URL
* 3. Video element and controls are created and added to the DOM
* 4. Player is attached to the video element
* 5. User can control playback via UI buttons
* 6. Resources are cleaned up when page is hidden
*
* Manifest Player vs WebRTC Player:
* - Manifest players use HLS/FLV/DASH URLs for transcoded streams
* - WebRTC players connect directly to live calls for ultra-low latency
* - Manifest players don't require authentication
* - Manifest players support adaptive bitrate streaming
*/
import { requestPlayer } from 'vdc-cdn';
import { attachPlayerClickHandlers, removePlayerClickHandlers, appendVideoElement } from './player-utils.js';
/**
* Manifest URL Configuration
*
* The manifest URL points to the stream you want to play. This can be:
* - HLS manifest (.m3u8) - Most common for live and VOD streaming
* - FLV manifest (.flv) - Used for Flash-based streaming
* - DASH manifest (.mpd) - Alternative adaptive streaming format
* - Native Frame JSON manifest (.json) - Contains multiple format options
*
* In a production application, you would typically:
* - Fetch this URL from your backend API
* - Receive it from a webhook when transcoding starts
* - Get it from your video management system
* - Pass it as a URL parameter or configuration
*
* For more information, see:
* https://docs.nativeframe.com/concepts/manifests
*/
/**
* Application State
*
* The player variable maintains the player instance throughout the application lifecycle.
* It is initialized to null and populated during init().
*/
let player = null;
/**
* Initialize the Manifest Player
*
* This is the main initialization function that sets up the player.
* It runs when the page loads and performs these steps:
*
* 1. Requests a player instance with the manifest URL
* 2. Creates video element and controls in the DOM
* 3. Attaches the player to the video element
* 4. Sets up event handlers for playback controls
* 5. Registers cleanup handler for page visibility changes
*
* Player Options:
* - autoPlay: true - Automatically starts playing when loaded
* - muted: false - Audio is enabled by default (set to true for autoplay in some browsers)
*
* After init() completes, the video will start playing automatically and
* users can control playback via the play/pause, mute, and volume controls.
*/
async function init() {
// Request a player instance for the manifest URL
// The player automatically handles:
// - Loading the manifest
// - Selecting the appropriate playback technology (HLS.js, mpegts.js, native)
// - Adaptive bitrate streaming
// - Buffering and playback
player = await requestPlayer(manifestUrl, { autoPlay: true, muted: false });
// Create and append the video element with controls to the DOM
const video = appendVideoElement("manifest-player");
// Attach the player to the video element
// This connects the player's media stream to the video tag for display
player.attachTo(video);
// Set up event handlers for playback controls (play, mute, volume)
attachPlayerClickHandlers(player, "manifest-player");
// Register cleanup handler for when the page is hidden
document.addEventListener("visibilitychange", dispose);
}
/**
* Clean Up Player Resources
*
* Disposes of the player and removes event listeners when the page is hidden.
* This should be called to:
* - Release video resources
* - Stop network connections
* - Free up memory
*
* Important: Always dispose of player resources when done to prevent:
* - Memory leaks
* - Continued network usage
* - Background playback when page is not visible
*
* This function is triggered automatically by the visibilitychange event
* when the user navigates away or hides the page.
*/
function dispose() {
if (document.hidden) {
// Dispose of the player (stops playback and releases resources)
player?.dispose();
player = null;
// Remove event handlers from control elements
removePlayerClickHandlers("manifest-player");
// Remove the visibility change listener
document.removeEventListener("visibilitychange", dispose);
}
}
/**
* Application Entry Point
*
* This runs when the page finishes loading. It calls init() to set up
* the player and start playback.
*/
window.onload = async () => {
await init();
};
Full Code
<!--
Native Frame Manifest Player - Vanilla JavaScript Implementation
This HTML file demonstrates how to play a livestream using a manifest URL
with the @video/video-client-core library and vanilla JavaScript (no framework required).
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
The manifest player allows viewers to:
- Play/pause video
- Control volume and muting
- Automatically handle adaptive bitrate streaming
- Play recorded or live streams
Required Setup:
1. Obtain a manifest URL from your Native Frame backend or CDN
2. Load the video-client-core library via CDN
3. Include the necessary JavaScript modules
-->
<!doctype html>
<html>
<head>
<!--
Import Map Configuration
This configures module resolution for ES modules. The "vdc-cdn" alias
points to the video-client-core library hosted on the Native Frame CDN.
You can update the version number in the URL to use a different version
of the library. Check https://cdn.nativeframe.com/ for available versions.
-->
<script type="importmap">
{
"imports": {
"vdc-cdn": "https://cdn.nativeframe.com/video-client-core-13.2.0.js"
}
}
</script>
<!-- << imports -->
<!--
JavaScript Module Imports
These modules must be loaded in order:
1. player-utils.js - Provides helper functions for creating the player UI
2. manifest-player.js - Main application logic for loading and controlling the player
-->
<script type="module" src="/js/player-utils.js"></script>
<script type="module" src="/js/manifest-player.js"></script>
</head>
<body>
<!--
Player UI Structure
The page consists of a single container element:
- videoContainer: Will be populated with the video player and playback controls
The video element and controls are dynamically created and injected by
the player-utils.js module.
-->
<div>
<!-- Video player and controls will be injected here -->
<div id="videoContainer"></div>
</div>
</body>
</html>
/**
* Player Utilities Module
*
* This module provides utility functions for creating and managing manifest player UI.
* It handles:
* - Creating video elements and playback controls
* - Appending player UI to the DOM
* - Attaching event handlers for player controls
* - Removing event handlers on cleanup
*
* Key Concepts:
* - Player: The video player instance that manages playback of manifest streams
* - Video Element: The HTML <video> tag where the stream is displayed
* - Playback Controls: Buttons and inputs for play/pause, mute, and volume control
*/
/**
* Attach Event Handlers to Player Controls
*
* Sets up click and change event handlers for all player UI controls.
* This enables users to:
* - Play/pause video playback
* - Mute/unmute audio
* - Adjust volume level
*
* @param {Player} player - The player instance to control
* @param {string} id - Unique identifier for the video element and its controls
*
* The player object provides these key properties:
* - localVideoPaused: Boolean indicating if video is paused
* - localAudioMuted: Boolean indicating if audio is muted
* - localAudioVolume: Number (0-1) representing volume level
*/
function attachPlayerClickHandlers(player, id) {
/**
* Toggle Play/Pause
*
* Toggles between playing and pausing the video stream.
* The player.localVideoPaused property indicates current playback state.
*
* @param {Event} event - Click event from the play button
*/
async function togglePlay(event) {
if (player.localVideoPaused) {
// Resume playback
await player.play();
event.target.textContent = "Pause";
} else {
// Pause playback
await player.pause();
event.target.textContent = "Play";
}
}
/**
* Toggle Mute/Unmute
*
* Toggles audio muting on and off.
* When muted, the video continues playing but no audio is heard.
*
* @param {Event} event - Click event from the mute button
*/
async function toggleMute(event) {
if (player.localAudioMuted) {
// Unmute audio
await player.unmute();
event.target.textContent = "Mute";
} else {
// Mute audio
await player.mute();
event.target.textContent = "Unmute";
}
}
/**
* Handle Volume Change
*
* Adjusts the audio volume based on slider input.
* Volume is converted from 0-100 range to 0-1 range.
* Setting volume to 0 automatically mutes the player.
*
* @param {Event} ev - Change event from the volume slider
*/
function handleVolume(ev) {
const volumeValue = Number(ev.target.value);
if (volumeValue === 0) {
// Mute when volume is set to 0
player.localAudioMuted = true;
} else {
// Unmute when volume is above 0
player.localAudioMuted = false;
}
// Set volume (convert from 0-100 to 0-1)
player.localAudioVolume = volumeValue / 100;
}
/**
* Wire Up Event Listeners
*
* Connect the handler functions to the corresponding DOM elements.
* Sets initial button text to reflect starting player state.
*/
document.getElementById(`playBtn-${id}`).onclick = togglePlay;
document.getElementById(`playBtn-${id}`).textContent = "Pause"; // Default state is playing
document.getElementById(`muteBtn-${id}`).onclick = toggleMute;
document.getElementById(`muteBtn-${id}`).textContent = "Mute"; // Default state is unmuted
document.getElementById(`volume-${id}`).onchange = handleVolume;
}
/**
* Remove Event Listeners from Player Controls
*
* Cleans up event listeners when the player is disposed.
* This prevents memory leaks and ensures proper resource cleanup.
*
* @param {string} id - Unique identifier for the video element and its controls
*
* Important: Always call this function before disposing of a player to
* properly clean up event handlers and prevent memory leaks.
*/
function removePlayerClickHandlers(id) {
// Remove event listeners from all control elements
document.getElementById(`playBtn-${id}`).removeAllListeners();
document.getElementById(`muteBtn-${id}`).removeAllListeners();
document.getElementById(`volume-${id}`).removeAllListeners();
}
/**
* Generate HTML for Video Player and Controls
*
* Creates the HTML structure for the video element and playback controls.
* This includes:
* - Video element for displaying the stream
* - Play/pause toggle button
* - Mute/unmute toggle button
* - Volume slider control
*
* @param {string} id - Unique identifier for the video element and its controls
* @returns {string} HTML string containing the player UI structure
*
* The generated HTML will be injected into the videoContainer div.
* Each control element is given a unique ID based on the provided id parameter,
* allowing multiple players on the same page.
*/
function createVideoElement(id) {
return `
<div id="video-wrapper-${id}">
<!-- Video element where the stream will be displayed -->
<video
width="100%"
height="100%"
id="${id}"
>
</video>
<!-- Play/pause toggle button -->
<button id="playBtn-${id}"></button>
<!-- Mute/unmute toggle button -->
<button id="muteBtn-${id}"></button>
<!-- Volume control slider -->
<div class="volume-container">
<label for="volume-${id}">Volume: </label>
<input type="range" id="volume-${id}" min="0" max="100" value="50" />
</div>
</div>
`;
}
/**
* Append Video Player to DOM
*
* Injects the player UI into the page and returns a reference to the video element.
* This function:
* 1. Finds the video container div
* 2. Inserts the player HTML
* 3. Returns the video element for player attachment
*
* @param {string} id - Unique identifier for the video element
* @returns {HTMLVideoElement} Reference to the video element
*
* The video element is returned so the Player can be attached to it using
* player.attachTo(videoElement).
*/
function appendVideoElement(id) {
// Find the container where we'll inject the player UI
const videoContainer = document.getElementById("videoContainer");
// Insert the player HTML into the container
videoContainer.insertAdjacentHTML("beforeend", createVideoElement(id));
// Return reference to the video element
return document.getElementById(id);
}
/**
* Export Utility Functions
*
* These functions are used by the main manifest-player.js module to
* create and manage the player UI.
*/
export { attachPlayerClickHandlers, removePlayerClickHandlers, appendVideoElement };
/**
* Native Frame Manifest Player - Main Application
*
* This is the main application file for the manifest player. It handles:
* - Loading and playing video streams from manifest URLs
* - Creating and managing the player UI
* - Handling playback controls
* - Resource cleanup
*
* Flow:
* 1. Page loads → init() is called
* 2. Player is requested with the manifest URL
* 3. Video element and controls are created and added to the DOM
* 4. Player is attached to the video element
* 5. User can control playback via UI buttons
* 6. Resources are cleaned up when page is hidden
*
* Manifest Player vs WebRTC Player:
* - Manifest players use HLS/FLV/DASH URLs for transcoded streams
* - WebRTC players connect directly to live calls for ultra-low latency
* - Manifest players don't require authentication
* - Manifest players support adaptive bitrate streaming
*/
import { requestPlayer } from 'vdc-cdn';
import { attachPlayerClickHandlers, removePlayerClickHandlers, appendVideoElement } from './player-utils.js';
/**
* Manifest URL Configuration
*
* The manifest URL points to the stream you want to play. This can be:
* - HLS manifest (.m3u8) - Most common for live and VOD streaming
* - FLV manifest (.flv) - Used for Flash-based streaming
* - DASH manifest (.mpd) - Alternative adaptive streaming format
* - Native Frame JSON manifest (.json) - Contains multiple format options
*
* In a production application, you would typically:
* - Fetch this URL from your backend API
* - Receive it from a webhook when transcoding starts
* - Get it from your video management system
* - Pass it as a URL parameter or configuration
*
* For more information, see:
* https://docs.nativeframe.com/concepts/manifests
*/
/**
* Application State
*
* The player variable maintains the player instance throughout the application lifecycle.
* It is initialized to null and populated during init().
*/
let player = null;
/**
* Initialize the Manifest Player
*
* This is the main initialization function that sets up the player.
* It runs when the page loads and performs these steps:
*
* 1. Requests a player instance with the manifest URL
* 2. Creates video element and controls in the DOM
* 3. Attaches the player to the video element
* 4. Sets up event handlers for playback controls
* 5. Registers cleanup handler for page visibility changes
*
* Player Options:
* - autoPlay: true - Automatically starts playing when loaded
* - muted: false - Audio is enabled by default (set to true for autoplay in some browsers)
*
* After init() completes, the video will start playing automatically and
* users can control playback via the play/pause, mute, and volume controls.
*/
async function init() {
// Request a player instance for the manifest URL
// The player automatically handles:
// - Loading the manifest
// - Selecting the appropriate playback technology (HLS.js, mpegts.js, native)
// - Adaptive bitrate streaming
// - Buffering and playback
player = await requestPlayer(manifestUrl, { autoPlay: true, muted: false });
// Create and append the video element with controls to the DOM
const video = appendVideoElement("manifest-player");
// Attach the player to the video element
// This connects the player's media stream to the video tag for display
player.attachTo(video);
// Set up event handlers for playback controls (play, mute, volume)
attachPlayerClickHandlers(player, "manifest-player");
// Register cleanup handler for when the page is hidden
document.addEventListener("visibilitychange", dispose);
}
/**
* Clean Up Player Resources
*
* Disposes of the player and removes event listeners when the page is hidden.
* This should be called to:
* - Release video resources
* - Stop network connections
* - Free up memory
*
* Important: Always dispose of player resources when done to prevent:
* - Memory leaks
* - Continued network usage
* - Background playback when page is not visible
*
* This function is triggered automatically by the visibilitychange event
* when the user navigates away or hides the page.
*/
function dispose() {
if (document.hidden) {
// Dispose of the player (stops playback and releases resources)
player?.dispose();
player = null;
// Remove event handlers from control elements
removePlayerClickHandlers("manifest-player");
// Remove the visibility change listener
document.removeEventListener("visibilitychange", dispose);
}
}
/**
* Application Entry Point
*
* This runs when the page finishes loading. It calls init() to set up
* the player and start playback.
*/
window.onload = async () => {
await init();
};
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 pausedlocalAudioMuted: Check if audio is mutedlocalAudioVolume: Get/set volume (0-1 range)
Manifest Player vs WebRTC Player
| Feature | Manifest Player | WebRTC Player |
|---|---|---|
| Connection | HLS/FLV/DASH URLs | Direct to live calls |
| Authentication | Not required | Required |
| Adaptive Bitrate | Supported | N/A |
| Latency | Higher (3-30 seconds) | Ultra-low (sub-second) |
| Best For | VOD, CDN-delivered content | Real-time interaction |
Next Steps
To learn more about the video-client-core library and advanced features:
- Explore creating a call stream for low-latency viewing