mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
docs(site): add Google Cast documentation (#1681)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
9bcdf5ffc2
commit
fa768da757
@@ -0,0 +1,9 @@
|
||||
---
|
||||
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
|
||||
import html from './BasicUsage.html?raw';
|
||||
---
|
||||
|
||||
<HtmlDemo html={html} />
|
||||
<script>
|
||||
import "./BasicUsage.ts";
|
||||
</script>
|
||||
@@ -0,0 +1,39 @@
|
||||
.video-player {
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.video-player video {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.media-cast-button {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
padding-block: 8px;
|
||||
padding-inline: 20px;
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 9999px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.media-cast-button[data-availability="unsupported"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.media-cast-button .connected {
|
||||
display: none;
|
||||
}
|
||||
.media-cast-button .disconnected {
|
||||
display: none;
|
||||
}
|
||||
.media-cast-button[data-cast-state="connected"] .connected {
|
||||
display: inline;
|
||||
}
|
||||
.media-cast-button:not([data-cast-state="connected"]) .disconnected {
|
||||
display: inline;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<video-player class="video-player">
|
||||
<media-container>
|
||||
<hls-video
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8"
|
||||
autoplay
|
||||
muted
|
||||
playsinline
|
||||
loop
|
||||
></hls-video>
|
||||
<media-cast-button class="media-cast-button">
|
||||
<span class="connected">Stop casting</span>
|
||||
<span class="disconnected">Start casting</span>
|
||||
</media-cast-button>
|
||||
</media-container>
|
||||
</video-player>
|
||||
@@ -0,0 +1,3 @@
|
||||
import '@videojs/html/video/player';
|
||||
import '@videojs/html/media/hls-video';
|
||||
import '@videojs/html/ui/cast-button';
|
||||
@@ -0,0 +1,25 @@
|
||||
.media-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.media-container video {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.media-cast-button {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
padding-block: 8px;
|
||||
padding-inline: 20px;
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 9999px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.media-cast-button[data-availability="unsupported"] {
|
||||
display: none;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { CastButton, createPlayer } from '@videojs/react';
|
||||
import { HlsVideo } from '@videojs/react/media/hls-video';
|
||||
import { videoFeatures } from '@videojs/react/video';
|
||||
|
||||
const Player = createPlayer({ features: videoFeatures });
|
||||
|
||||
export default function BasicUsage() {
|
||||
return (
|
||||
<Player.Provider>
|
||||
<Player.Container className="media-container">
|
||||
<HlsVideo
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8"
|
||||
autoPlay
|
||||
muted
|
||||
playsInline
|
||||
loop
|
||||
/>
|
||||
<CastButton
|
||||
className="media-cast-button"
|
||||
render={(props, state) => (
|
||||
<button {...props}>{state.castState === 'connected' ? 'Stop casting' : 'Start casting'}</button>
|
||||
)}
|
||||
/>
|
||||
</Player.Container>
|
||||
</Player.Provider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
---
|
||||
title: Google Cast
|
||||
description: How to add Chromecast support to your Video.js player — lazy SDK loading, session lifecycle, and configuration options
|
||||
---
|
||||
|
||||
import FrameworkCase from '@/components/docs/FrameworkCase.astro';
|
||||
import DocsLink from '@/components/docs/DocsLink.astro';
|
||||
import DocsLinkCard from '@/components/docs/DocsLinkCard.astro';
|
||||
import Aside from '@/components/Aside.astro';
|
||||
|
||||
Video.js has built-in Google Cast support for HLS and DASH media elements. When a Chromecast device is on the same network, a Cast button appears automatically in Chromium browsers. Tapping it starts a session that moves playback to the TV while the browser stays in control.
|
||||
|
||||
<Aside type="note">
|
||||
Cast is only available in Chromium-based browsers (Chrome, Edge, and others built on Blink). On other browsers, `remotePlaybackAvailability` is `'unsupported'`.
|
||||
</Aside>
|
||||
|
||||
## How Cast works
|
||||
|
||||
Cast uses a **sender / receiver** model:
|
||||
|
||||
- **Sender** — the browser tab. It controls playback commands (play, pause, seek, volume) and sends a load request to the receiver with the source URL and metadata.
|
||||
- **Receiver** — the Chromecast device running the default media receiver application (or a custom receiver you configure).
|
||||
|
||||
### Session lifecycle
|
||||
|
||||
A Cast session moves through three states, exposed via the <DocsLink slug="reference/feature-remote-playback">Remote Playback</DocsLink> feature:
|
||||
|
||||
| State | Meaning |
|
||||
|---|---|
|
||||
| `'disconnected'` | No active session |
|
||||
| `'connecting'` | User accepted; session negotiating |
|
||||
| `'connected'` | Receiver is playing |
|
||||
|
||||
While `connected`, the media element's `play`, `pause`, `currentTime`, `volume`, `muted`, and `playbackRate` all proxy to the Cast receiver. Local playback is suspended.
|
||||
|
||||
### The lazy-loaded SDK
|
||||
|
||||
The Cast SDK (`cast_sender.js`) is injected into the page as a `<script>` tag the first time a Cast-capable media element is created in a Chromium browser. No SDK cost is paid on browsers that don't support Cast.
|
||||
|
||||
Set `disableRemotePlayback` on the media element to opt out:
|
||||
|
||||
<FrameworkCase frameworks={["react"]}>
|
||||
```tsx
|
||||
<HlsVideo disableRemotePlayback />
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
<FrameworkCase frameworks={["html"]}>
|
||||
```html
|
||||
<hls-video disableremoteplayback></hls-video>
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
## Minimal setup
|
||||
|
||||
Cast works out of the box with the `videoFeatures` preset — no extra configuration needed. Add a `CastButton` to give users a way to start and stop a session:
|
||||
|
||||
<FrameworkCase frameworks={["react"]}>
|
||||
```tsx
|
||||
import { createPlayer, CastButton } from '@videojs/react';
|
||||
import { HlsVideo } from '@videojs/react/media/hls-video';
|
||||
import { videoFeatures } from '@videojs/react/video';
|
||||
|
||||
const Player = createPlayer({ features: videoFeatures });
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Player.Provider>
|
||||
<Player.Container>
|
||||
<HlsVideo src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8" autoPlay muted playsInline loop />
|
||||
<CastButton />
|
||||
</Player.Container>
|
||||
</Player.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
<FrameworkCase frameworks={["html"]}>
|
||||
```html
|
||||
<video-player>
|
||||
<media-container>
|
||||
<hls-video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8" autoplay muted playsinline loop></hls-video>
|
||||
<media-cast-button></media-cast-button>
|
||||
</media-container>
|
||||
</video-player>
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
## Configuring Cast
|
||||
|
||||
All Cast configuration is set via the `config.googleCast` property. In React, pass it as a declarative prop. In HTML, set it as a JavaScript property; HTML attributes are not supported.
|
||||
|
||||
### Custom receiver application ID
|
||||
|
||||
By default, Video.js uses Google's [Default Media Receiver](https://developers.google.com/cast/docs/web_sender/integrate#default_media_web_receiver) (`CC1AD845`). To use your own receiver app, set `receiver`:
|
||||
|
||||
<FrameworkCase frameworks={["react"]}>
|
||||
```tsx
|
||||
<HlsVideo src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8" config={{ googleCast: { receiver: 'YOUR_APP_ID' } }} />
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
<FrameworkCase frameworks={["html"]}>
|
||||
```ts
|
||||
const video = document.querySelector('hls-video');
|
||||
video.config = { googleCast: { receiver: 'YOUR_APP_ID' } };
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
### Custom data on load
|
||||
|
||||
Pass arbitrary data to the receiver with each load request via `customData`. The receiver app reads it from the `customData` field of the load request:
|
||||
|
||||
<FrameworkCase frameworks={["react"]}>
|
||||
```tsx
|
||||
<HlsVideo
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8"
|
||||
config={{ googleCast: { customData: { token: 'abc123', userId: 'u_789' } } }}
|
||||
/>
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
<FrameworkCase frameworks={["html"]}>
|
||||
```ts
|
||||
const video = document.querySelector('hls-video');
|
||||
video.config = { googleCast: { customData: { token: 'abc123', userId: 'u_789' } } };
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
### Cast source override
|
||||
|
||||
By default the receiver loads the same URL as the browser. Use `src` (and optionally `contentType`) to send a different URL, for example, a separate manifest or CDN-edge URL accessible from the TV network:
|
||||
|
||||
<FrameworkCase frameworks={["react"]}>
|
||||
```tsx
|
||||
<HlsVideo
|
||||
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
|
||||
config={{ googleCast: { src: 'https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8', contentType: 'application/x-mpegURL' } }}
|
||||
/>
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
<FrameworkCase frameworks={["html"]}>
|
||||
```ts
|
||||
const video = document.querySelector('hls-video');
|
||||
video.config = {
|
||||
googleCast: {
|
||||
src: 'https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8',
|
||||
contentType: 'application/x-mpegURL',
|
||||
},
|
||||
};
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
### HLS on the receiver
|
||||
|
||||
When the source (or `config.googleCast.src`) is an HLS playlist, Video.js automatically detects the segment format (TS or fMP4) by inspecting the playlist and sets `hlsSegmentFormat` / `hlsVideoSegmentFormat` on the Cast load request. The default media receiver handles HLS natively, so no extra configuration is needed.
|
||||
|
||||
Use `streamType` to tell the receiver whether the stream is `'on-demand'` or `'live'` (falls back to the player's `streamType` when unset):
|
||||
|
||||
<FrameworkCase frameworks={["react"]}>
|
||||
```tsx
|
||||
<HlsVideo src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM.m3u8" config={{ googleCast: { streamType: 'live' } }} />
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
<FrameworkCase frameworks={["html"]}>
|
||||
```ts
|
||||
const video = document.querySelector('hls-video');
|
||||
video.config = { googleCast: { streamType: 'live' } };
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
## Cast state and selectors
|
||||
|
||||
Cast session state lives in the <DocsLink slug="reference/feature-remote-playback">Remote Playback</DocsLink> feature slice. Use `selectRemotePlayback` to subscribe:
|
||||
|
||||
<FrameworkCase frameworks={["react"]}>
|
||||
```tsx
|
||||
import { selectRemotePlayback, usePlayer } from '@videojs/react';
|
||||
|
||||
function CastStatus() {
|
||||
const remotePlayback = usePlayer(selectRemotePlayback);
|
||||
if (!remotePlayback) return null;
|
||||
|
||||
return <span>{remotePlayback.remotePlaybackState}</span>;
|
||||
}
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
<FrameworkCase frameworks={["html"]}>
|
||||
```ts
|
||||
import { PlayerController, playerContext, selectRemotePlayback } from '@videojs/html';
|
||||
|
||||
class CastStatus extends HTMLElement {
|
||||
readonly #remotePlayback = new PlayerController(this, playerContext, selectRemotePlayback);
|
||||
}
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
## Browser availability
|
||||
|
||||
Cast availability is surfaced as `remotePlaybackAvailability` on the remote playback feature. Hide the button when unsupported:
|
||||
|
||||
<FrameworkCase frameworks={["html"]}>
|
||||
```css
|
||||
media-cast-button[data-availability="unsupported"] {
|
||||
display: none;
|
||||
}
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
<FrameworkCase frameworks={["react"]}>
|
||||
```css
|
||||
.cast-button[data-availability="unsupported"] {
|
||||
display: none;
|
||||
}
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
## See also
|
||||
|
||||
<DocsLinkCard slug="reference/cast-button">CastButton component reference</DocsLinkCard>
|
||||
<DocsLinkCard slug="reference/feature-remote-playback">Remote Playback feature reference</DocsLinkCard>
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
title: CastButton
|
||||
frameworkTitle:
|
||||
html: media-cast-button
|
||||
description: Accessible Cast toggle button with state reflection and keyboard support
|
||||
---
|
||||
|
||||
import ComponentReference from "@/components/docs/api-reference/ComponentReference.astro";
|
||||
import FrameworkCase from "@/components/docs/FrameworkCase.astro";
|
||||
import StyleCase from "@/components/docs/StyleCase.astro";
|
||||
import Demo from "@/components/docs/demos/Demo.astro";
|
||||
|
||||
{/* React demos */}
|
||||
import BasicUsageDemoReact from "@/components/docs/demos/cast-button/react/css/BasicUsage";
|
||||
import basicUsageReactTsx from "@/components/docs/demos/cast-button/react/css/BasicUsage.tsx?raw";
|
||||
import basicUsageReactCss from "@/components/docs/demos/cast-button/react/css/BasicUsage.css?raw";
|
||||
|
||||
{/* HTML demos */}
|
||||
import BasicUsageDemoHtml from "@/components/docs/demos/cast-button/html/css/BasicUsage.astro";
|
||||
import basicUsageHtml from "@/components/docs/demos/cast-button/html/css/BasicUsage.html?raw";
|
||||
import basicUsageHtmlCss from "@/components/docs/demos/cast-button/html/css/BasicUsage.css?raw";
|
||||
import basicUsageHtmlTs from "@/components/docs/demos/cast-button/html/css/BasicUsage.ts?raw";
|
||||
|
||||
## Anatomy
|
||||
|
||||
<FrameworkCase frameworks={["react"]}>
|
||||
```tsx
|
||||
<CastButton />
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
<FrameworkCase frameworks={["html"]}>
|
||||
```html
|
||||
<media-cast-button></media-cast-button>
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
## Behavior
|
||||
|
||||
Toggles a Google Cast session. Clicking the button while `disconnected` opens the Cast device picker; clicking while `connected` ends the session.
|
||||
|
||||
The button does nothing when `availability` is not `'available'` — for example, when no Chromecast is on the network, or on browsers that don't support Cast.
|
||||
|
||||
## Styling
|
||||
|
||||
Style based on cast state and availability:
|
||||
|
||||
<FrameworkCase frameworks={["html"]}>
|
||||
```css
|
||||
/* During an active session */
|
||||
media-cast-button[data-cast-state="connected"] {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
/* Hide when Cast is unsupported (non-Chromium browsers) */
|
||||
media-cast-button[data-availability="unsupported"] {
|
||||
display: none;
|
||||
}
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
<FrameworkCase frameworks={["react"]}>
|
||||
React renders a `<button>` element. Add a `className` to style it:
|
||||
|
||||
```css
|
||||
/* During an active session */
|
||||
.cast-button[data-cast-state="connected"] {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
/* Hide when Cast is unsupported (non-Chromium browsers) */
|
||||
.cast-button[data-availability="unsupported"] {
|
||||
display: none;
|
||||
}
|
||||
```
|
||||
</FrameworkCase>
|
||||
|
||||
## Accessibility
|
||||
|
||||
Renders a `<button>` with an automatic `aria-label`:
|
||||
|
||||
| Cast state | Default label |
|
||||
|---|---|
|
||||
| `'disconnected'` | "Start casting" |
|
||||
| `'connecting'` | "Connecting" |
|
||||
| `'connected'` | "Stop casting" |
|
||||
|
||||
Override with the `label` prop. Keyboard activation: <kbd>Enter</kbd> / <kbd>Space</kbd>.
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Usage
|
||||
|
||||
<FrameworkCase frameworks={["react"]}>
|
||||
<StyleCase styles={["css"]}>
|
||||
<Demo files={[
|
||||
{ title: "App.tsx", code: basicUsageReactTsx, lang: "tsx" },
|
||||
{ title: "App.css", code: basicUsageReactCss, lang: "css" },
|
||||
]}>
|
||||
<BasicUsageDemoReact client:idle />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
</FrameworkCase>
|
||||
|
||||
<FrameworkCase frameworks={["html"]}>
|
||||
<StyleCase styles={["css"]}>
|
||||
<Demo files={[
|
||||
{ title: "index.html", code: basicUsageHtml, lang: "html" },
|
||||
{ title: "index.css", code: basicUsageHtmlCss, lang: "css" },
|
||||
{ title: "index.ts", code: basicUsageHtmlTs, lang: "ts" },
|
||||
]}>
|
||||
<BasicUsageDemoHtml />
|
||||
</Demo>
|
||||
</StyleCase>
|
||||
</FrameworkCase>
|
||||
|
||||
<ComponentReference component="CastButton" />
|
||||
@@ -5,10 +5,13 @@ description: Remote playback state and actions for the player store
|
||||
|
||||
import FeatureReference from "@/components/docs/api-reference/FeatureReference.astro";
|
||||
import DocsLink from "@/components/docs/DocsLink.astro";
|
||||
import DocsLinkCard from "@/components/docs/DocsLinkCard.astro";
|
||||
import FrameworkCase from "@/components/docs/FrameworkCase.astro";
|
||||
|
||||
Controls remote playback to devices like Chromecast (Chromium) and AirPlay (Safari). Exits fullscreen before initiating a remote playback session.
|
||||
|
||||
<DocsLinkCard slug="concepts/cast">Google Cast concept guide — setup, configuration, and HLS</DocsLinkCard>
|
||||
|
||||
<FeatureReference feature="remotePlayback" />
|
||||
|
||||
### Selector
|
||||
|
||||
@@ -35,6 +35,7 @@ export const sidebar: Sidebar = [
|
||||
llmsDescription:
|
||||
'Understanding-oriented pages that explain how and why things work. Read these to build a mental model of the library.',
|
||||
contents: [
|
||||
{ slug: 'concepts/cast', sidebarLabel: 'Google Cast' },
|
||||
{ slug: 'concepts/features' },
|
||||
{ slug: 'concepts/skins' },
|
||||
{ slug: 'concepts/presets' },
|
||||
@@ -62,6 +63,7 @@ export const sidebar: Sidebar = [
|
||||
{ slug: 'reference/airplay-button' },
|
||||
{ slug: 'reference/buffering-indicator' },
|
||||
{ slug: 'reference/captions-button' },
|
||||
{ slug: 'reference/cast-button' },
|
||||
{ slug: 'reference/controls' },
|
||||
{ slug: 'reference/fullscreen-button' },
|
||||
{ slug: 'reference/mute-button' },
|
||||
|
||||
Reference in New Issue
Block a user