mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-14 00:06:17 -08:00
docs(site): [SITE-04] document Canvas and Mediabunny proxies
This commit is contained in:
1 parent
e2bd7c9742
commit
4db9789014
110 files changed
+4876
-2204
No files matched your search
@@ -28,6 +28,8 @@ not a VitePress page. Keep both URL surfaces compatible.
|
||||
| `docs/plugin/chromecast.md`, `docs/en/plugin/chromecast.md` | SDK loading, callbacks, shared sessions, receiver limitations and runtime types | `/document/plugin/chromecast.html`, `/document/en/plugin/chromecast.html` |
|
||||
| `docs/tool/iframe.md`, `docs/en/tool/iframe.md` | Parent/child setup, serialized requests, navigation cancellation, trust and type views | `/document/tool/iframe.html`, `/document/en/tool/iframe.html` |
|
||||
| `docs/tool/thumbnail.md`, `docs/en/tool/thumbnail.md` | Local file extraction, approved defaults, PNG sheets, events and URL ownership | `/document/tool/thumbnail.html`, `/document/en/tool/thumbnail.html` |
|
||||
| `docs/proxy/canvas.md`, `docs/en/proxy/canvas.md` | Backing video, draw callbacks/events, Canvas priority, subtitles and compatible types | `/document/proxy/canvas.html`, `/document/en/proxy/canvas.html` |
|
||||
| `docs/proxy/mediabunny.md`, `docs/en/proxy/mediabunny.md` | Input, HLS pairing, synthetic media state, lifecycle and explicit type views | `/document/proxy/mediabunny.html`, `/document/en/proxy/mediabunny.html` |
|
||||
| `docs/.vitepress/config.js` | Navigation, base URL, output path, page head | Repository `docs/document/` |
|
||||
| `docs/vite.config.ts` | Search plugin configuration | VitePress/Vite integration; real search acceptance remains pending |
|
||||
| `docs/public/main.js` | Run Code links and first-visit language redirect | Copied into the built site |
|
||||
@@ -41,9 +43,9 @@ and run `node refactor/scripts/demos.mjs --check`. Preserve the frozen BASE-04
|
||||
inventory. Also refresh and check the current site inventory; the two inventories
|
||||
serve different purposes. Neither path check replaces browser navigation tests.
|
||||
|
||||
The generated site lives in `docs/document/`. The current inventory contains 62 Markdown pages: 31 Chinese
|
||||
and 31 English. All 16 plugins and both tools have dedicated guides and navigation in both languages.
|
||||
The two proxies still need dedicated bilingual guides. Core member
|
||||
The generated site lives in `docs/document/`. The current inventory contains 66 Markdown pages: 33 Chinese
|
||||
and 33 English. All 16 plugins, both tools and both proxies have dedicated guides and navigation in both languages.
|
||||
Core member
|
||||
mapping and the current cross-check of earlier guides remain in SITE-04;
|
||||
navigation links alone are not complete API documentation.
|
||||
|
||||
@@ -55,6 +57,12 @@ Blob URLs. Original examples are retained; their navigation is not extraction or
|
||||
cross-window player acceptance. The thumbnail package's stale default-behavior
|
||||
sentence was corrected alongside these guides, requiring its candidate refresh.
|
||||
|
||||
Canvas documents its real backing video and synchronous post-draw callback;
|
||||
Mediabunny documents SDK input, pairable HLS selections and synthetic ranges/frame
|
||||
metadata. Native Canvas members take precedence in both proxies. An Auto menu or
|
||||
canPlayType result is not an adaptive-bitrate or decoder-capability guarantee.
|
||||
Proxy guide navigation does not establish media playback or device acceptance.
|
||||
|
||||
JASSUB documents actual resize argument order, query callbacks and resource
|
||||
ownership separately from preserved historical declarations. Danmuku Mask keeps
|
||||
both plugin dependencies in its Run Code link and explains initialization versus
|
||||
|
||||
@@ -107,8 +107,8 @@ const config = {
|
||||
text: '代理',
|
||||
collapsible: true,
|
||||
items: [
|
||||
{ text: 'Canvas', link: 'https://artplayer.org/?libs=./uncompiled/artplayer-proxy-canvas/index.js&example=canvas' },
|
||||
{ text: 'Mediabunny', link: 'https://artplayer.org/?libs=./uncompiled/artplayer-proxy-mediabunny/index.js&example=mediabunny' },
|
||||
{ text: 'Canvas', link: '/proxy/canvas' },
|
||||
{ text: 'Mediabunny', link: '/proxy/mediabunny' },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -189,8 +189,8 @@ const config = {
|
||||
text: 'Proxys',
|
||||
collapsible: true,
|
||||
items: [
|
||||
{ text: 'Canvas', link: 'https://artplayer.org/?libs=./uncompiled/artplayer-proxy-canvas/index.js&example=canvas' },
|
||||
{ text: 'Mediabunny', link: 'https://artplayer.org/?libs=./uncompiled/artplayer-proxy-mediabunny/index.js&example=mediabunny' },
|
||||
{ text: 'Canvas', link: '/en/proxy/canvas' },
|
||||
{ text: 'Mediabunny', link: '/en/proxy/mediabunny' },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
# Canvas video proxy
|
||||
|
||||
[简体中文](../../proxy/canvas.md)
|
||||
|
||||
Decode audio/video with a real video element, draw its picture to Canvas and optionally post-process each draw. Install through ArtPlayer's proxy option, not its plugins array. This page describes the unreleased branch; decoding, pixel access and device limitations still apply.
|
||||
|
||||
## Install and example
|
||||
|
||||
```sh
|
||||
yarn add artplayer artplayer-proxy-canvas
|
||||
```
|
||||
|
||||
ESM uses `import canvas from 'artplayer-proxy-canvas'`. Scripts load `dist/artplayer-proxy-canvas.js`, exposing `artplayerProxyCanvas`. The [original example](https://artplayer.org/?libs=./uncompiled/artplayer-proxy-canvas/index.js&example=canvas) is retained below:
|
||||
|
||||
<div className="run-code" data-libs="./uncompiled/artplayer-proxy-canvas/index.js">▶ Run Code</div>
|
||||
|
||||
```js
|
||||
// npm i artplayer-proxy-canvas
|
||||
// import artplayerProxyCanvas from 'artplayer-proxy-canvas';
|
||||
|
||||
const art = new Artplayer({
|
||||
container: '.artplayer-app',
|
||||
url: '/assets/sample/video.mp4',
|
||||
poster: '/assets/sample/poster.jpg',
|
||||
volume: 0.5,
|
||||
autoplay: false,
|
||||
autoSize: false,
|
||||
screenshot: true,
|
||||
setting: true,
|
||||
loop: true,
|
||||
flip: true,
|
||||
pip: true,
|
||||
playbackRate: true,
|
||||
aspectRatio: true,
|
||||
fullscreen: true,
|
||||
fullscreenWeb: true,
|
||||
miniProgressBar: true,
|
||||
autoPlayback: true,
|
||||
autoOrientation: true,
|
||||
subtitle: {
|
||||
url: '/assets/sample/subtitle.srt',
|
||||
},
|
||||
thumbnails: {
|
||||
url: '/assets/sample/thumbnails.png',
|
||||
number: 60,
|
||||
column: 10,
|
||||
scale: 0.85,
|
||||
},
|
||||
proxy: artplayerProxyCanvas(),
|
||||
})
|
||||
```
|
||||
|
||||
The optional argument is a drawing callback, not an options object:
|
||||
|
||||
```js
|
||||
proxy: artplayerProxyCanvas((context, video) => {
|
||||
context.fillStyle = 'rgba(0, 0, 0, 0.4)';
|
||||
context.fillRect(0, 0, context.canvas.width, 32);
|
||||
context.fillStyle = '#fff';
|
||||
context.fillText(video.currentTime.toFixed(1), 10, 22);
|
||||
})
|
||||
```
|
||||
|
||||
It receives the actual CanvasRenderingContext2D and backing HTMLVideoElement. The base picture is drawn and any acquired ImageBitmap closed before the synchronous callback. Return values are unused; an async callback's Promise is not awaited. Set required Canvas state yourself; changing dimensions resets drawing state.
|
||||
|
||||
## Returned element and media surface
|
||||
|
||||
Initialization synchronously returns an actual HTMLCanvasElement for ArtPlayer's media position. The backing video is connected to the player before playback, transparent and unfocusable, without another visible player.
|
||||
|
||||
Native Canvas members win: width/height, DOM events, getContext and toDataURL remain Canvas operations. Only enumerated video members absent from Canvas are forwarded; this is not a complete runtime copy of HTMLVideoElement. Access the real video through the draw callback when needed.
|
||||
|
||||
Backing media events are forwarded as ArtPlayer `video:<type>` events with the original Event. Canvas addEventListener('play', ...) is not a backing-video listener; use art.on('video:play', ...). Assigning media src/srcObject or invoking load invalidates old draws.
|
||||
|
||||
Normal ArtPlayer subtitle options remain supported. The active proxy's appendChild specially routes HTML tracks to the video; ordinary nodes still belong to Canvas. Core owns subtitle track replacement and URLs. The initial empty metadata track is removed before the first real subtitle is attached. Its final parent is VIDEO.
|
||||
|
||||
## Drawing, dimensions and events
|
||||
|
||||
| Trigger | Behavior |
|
||||
| --- | --- |
|
||||
| Play | One RAF drawing chain without overlapping asynchronous acquisitions |
|
||||
| Pause/source emptying | Cancel pending draws, keeping the displayed picture |
|
||||
| Paused seek | Request one fresh picture |
|
||||
| Resize | Coalesce requests and invalidate old asynchronous results |
|
||||
| loadedmetadata | Set Canvas dimensions when intrinsic video size is valid |
|
||||
| autoSize=false | Fit the video aspect ratio into the container with centering padding on resize |
|
||||
| autoSize=true | Skip that proxy fit; let the player handle automatic sizing |
|
||||
|
||||
Drawing needs readyState>=2, no seek in progress, and valid video/Canvas dimensions. It uses createImageBitmap when available, otherwise drawImage(video). This does not guarantee a callback for every decoded frame. Cross-origin CORS settings/responses still determine screenshot and pixel-read access.
|
||||
|
||||
| Custom event | Arguments and order |
|
||||
| --- | --- |
|
||||
| `artplayerProxyCanvas:draw` | Context and backing video, after a successful callback |
|
||||
| `artplayerProxyCanvas:error` | Original failure value from drawing, callback or initialization |
|
||||
|
||||
A missing2D context reports an error. The specific first-frame createImageBitmap InvalidStateError before decoding waits for another draw request; callback/drawImage errors are not broadly treated as first-frame delays. Destroying the player in the callback prevents a subsequent draw event or restarted loop.
|
||||
|
||||
## Cleanup and capabilities
|
||||
|
||||
Player destruction cancels RAF/deferred setup, removes internal subscriptions, pauses/unloads the video, clears srcObject and releases Canvas buffers. Late bitmaps are still closed. Clearing the stream reference does not stop caller-owned media tracks. Cleanup attempts all resources before throwing its first error. Escaped media methods/forwarded setters cannot restart playback or source loading; native Canvas methods still act on the same element.
|
||||
|
||||
There is no separate public start/stop/destroy control object; use ArtPlayer lifecycle. The original example's PiP/fullscreen/screenshot options remain subject to actual browser capabilities. Their presence is not proof of universal support, and Windows results do not establish Safari/iPhone/Android acceptance.
|
||||
|
||||
## TypeScript and historical types
|
||||
|
||||
Root and `/legacy` retain published1.1.0's optional callback, plain factory and exact HTMLCanvasElement result. Ordinary replacement functions remain assignable. The conflicting1.0 export= required-callback shape follows the approved latest-root policy. Use `/runtime` for accurate ESM/older CommonJS invocation and self-alias types with the same implementation:
|
||||
|
||||
```ts
|
||||
import Artplayer from 'artplayer';
|
||||
import canvas from 'artplayer-proxy-canvas/runtime';
|
||||
import type { MediaCanvas, Option } from 'artplayer-proxy-canvas/runtime';
|
||||
|
||||
const overlay: Option = (context, video) => {
|
||||
context.fillText(video.currentTime.toFixed(1), 10, 22);
|
||||
};
|
||||
const art = new Artplayer({ container: '#player', url: '/movie.mp4', proxy: canvas(overlay) });
|
||||
const media = art.template.$video as MediaCanvas;
|
||||
function play(): Promise<void> { return media.play(); }
|
||||
```
|
||||
|
||||
Public types are Option, Result, Factory, Callable, MediaCanvas and RuntimeFactory. MediaCanvas is an explicit view preserving native Canvas collisions; it adds no runtime capabilities and does not narrow factory return inference. The root's historical NodeNext ESM namespace remains; use runtime for accurate default calls. JavaScript root and `.default` reference the same factory; runtime types describe the alias as readonly. CommonJS can require the function directly, and older TS import=require callers can choose runtime.
|
||||
@@ -0,0 +1,168 @@
|
||||
# Mediabunny video proxy
|
||||
|
||||
[简体中文](../../proxy/mediabunny.md)
|
||||
|
||||
Read media through Mediabunny and expose a video-like surface backed by Canvas and an audio pipeline, including HLS quality/audio selection. Install through ArtPlayer's proxy option. This describes the unreleased branch; it does not supply missing decoders or every HTMLVideoElement capability.
|
||||
|
||||
## Install and example
|
||||
|
||||
```sh
|
||||
yarn add artplayer artplayer-proxy-mediabunny
|
||||
```
|
||||
|
||||
ESM uses `import mediabunny from 'artplayer-proxy-mediabunny'`. Scripts load `dist/artplayer-proxy-mediabunny.js`, exposing `artplayerProxyMediabunny`. This is the [original HLS example](https://artplayer.org/?libs=./uncompiled/artplayer-proxy-mediabunny/index.js&example=mediabunny); its remote stream is not an offline fixture:
|
||||
|
||||
<div className="run-code" data-libs="./uncompiled/artplayer-proxy-mediabunny/index.js">▶ Run Code</div>
|
||||
|
||||
```js
|
||||
// npm i artplayer-proxy-mediabunny
|
||||
// import artplayerProxyMediabunny from 'artplayer-proxy-mediabunny';
|
||||
|
||||
const art = new Artplayer({
|
||||
container: '.artplayer-app',
|
||||
url: 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8',
|
||||
autoSize: true,
|
||||
setting: true,
|
||||
loop: true,
|
||||
flip: true,
|
||||
playbackRate: true,
|
||||
fullscreen: true,
|
||||
fullscreenWeb: true,
|
||||
miniProgressBar: true,
|
||||
autoPlayback: true,
|
||||
autoOrientation: true,
|
||||
proxy: artplayerProxyMediabunny({
|
||||
m3u8: {
|
||||
quality: {
|
||||
control: true,
|
||||
setting: true,
|
||||
getName: level => level.height ? `${level.height}P` : level.name,
|
||||
title: 'Quality',
|
||||
auto: 'Auto',
|
||||
},
|
||||
audio: {
|
||||
control: true,
|
||||
setting: true,
|
||||
getName: track => track.name || track.language,
|
||||
title: 'Audio',
|
||||
auto: 'Auto',
|
||||
},
|
||||
},
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
The optional-options factory synchronously initializes an actual HTMLCanvasElement and installs art.mediabunny. Native Canvas members win collisions: DOM event/attribute methods do not become shim methods. Use ArtPlayer video:* events or the explicit shim for media operations.
|
||||
|
||||
## Inputs and options
|
||||
|
||||
| Option | Default or behavior |
|
||||
| --- | --- |
|
||||
| `source` | Initial source; a truthy value precedes art.option.url, but later host assignments can replace it |
|
||||
| `loadTimeout` | 0, no deadline; a finite positive value limits loading in milliseconds |
|
||||
| `timeupdateInterval` | 250ms renderer update interval, not an exact clock guarantee |
|
||||
| `avSyncTolerance` | 0.12 seconds, used in the late-frame threshold when dropping is enabled |
|
||||
| `dropLateFrames` | false; enable to skip sufficiently late pictures |
|
||||
| `poster` | Empty by default; captured by the video engine at initialization |
|
||||
| `preflightRange` | false; optional HEAD check for ordinary URLs |
|
||||
| `volume` / `muted` | Initial shim values0.7 / false; later player settings may override them |
|
||||
| `autoplay` / `loop` | Compatibility readback values defaulting false, not standalone autoplay/loop execution |
|
||||
| `crossOrigin` | Compatibility readback defaulting empty; does not configure SDK fetch credentials/CORS |
|
||||
| `m3u8` | Optional quality/audio menus, below |
|
||||
|
||||
Use ArtPlayer's own autoplay/loop configuration for player behavior, subject to browser playback policy. Shim autoplay/loop/crossOrigin setters remain inert. Changing shim.poster updates its option readback, not necessarily the poster already captured by the video engine.
|
||||
|
||||
The source declaration accepts URL strings, Blobs and byte ReadableStreams. Runtime also passes SDK Source/SourceRef values to SDK validation. Case-insensitive `.m3u8` suffixes, optionally followed by query/hash, select HLS_FORMATS; other sources use ALL_FORMATS. A URL without that suffix does not enable this proxy's HLS menu state merely from response content. Random access/reloading a stream depends on its source capabilities.
|
||||
|
||||
When enabled, preflight sends HEAD only for non-HLS strings. Missing accept-ranges or a value of none emits error with a RangeNotSupported Event detail and stops that preflight path, without setting shim.error. Network failures warn and continue; HLS/non-string sources skip HEAD. This is not a complete Range GET or codec-support check. Media responses must still be readable with suitable CORS.
|
||||
|
||||
## HLS menus and selection
|
||||
|
||||
Both m3u8.quality and m3u8.audio accept control, setting, title, auto and getName. No menu appears unless control or setting is enabled; setting also needs the player's settings panel.
|
||||
|
||||
| Setting | Default |
|
||||
| --- | --- |
|
||||
| `quality.title` / `audio.title` | Quality / Audio |
|
||||
| Either `auto` | Auto |
|
||||
| `quality.getName(level)` | level.name, otherwise height plus P |
|
||||
| `audio.getName(track)` | First available name, lang or language |
|
||||
|
||||
Levels provide id, index, name, height and bitrate; audio entries provide id, index, name, lang, language and bitrate. Full state also includes SDK track objects. Name can be null; return a displayable string from getName.
|
||||
|
||||
Quality needs video levels; audio needs at least two pairable tracks. Quality sorts by descending height. Equal labels are merged, favoring the selected item. Highlight/button text follows the actual current track, so auto mode may still display its chosen track rather than Auto.
|
||||
|
||||
```js
|
||||
const shim = art.mediabunny;
|
||||
const state = await shim.getM3u8State();
|
||||
if (state?.levels.length) {
|
||||
await shim.switchM3u8Quality(state.levels[0].id);
|
||||
}
|
||||
await shim.switchM3u8Audio('auto');
|
||||
```
|
||||
|
||||
Pass a numeric state id or literal 'auto', not an index, label or numeric string. Auto selects the SDK primary track; the proxy does not thereby measure bandwidth and continuously adapt bitrate. Quality changes retain pairable audio when possible, otherwise choose primary pairable audio; audio selection also maintains video pairing. Unknown IDs retain the current track. Methods are no-ops without an active HLS source.
|
||||
|
||||
Menu names are mediabunny-quality and mediabunny-audio. Metadata/restart refreshes them; loadstart/error/destruction clears them. A source without a capability removes its old UI. Stale menu callbacks and superseded choices cannot replace current state. Active asynchronous selection failures can reject.
|
||||
|
||||
## Shim members
|
||||
|
||||
| Member | Behavior |
|
||||
| --- | --- |
|
||||
| `canvas` | Native output element |
|
||||
| `src` / `currentSrc` | Source value; truthy src assignments load, empty assignments do not unload |
|
||||
| `play()` / `pause()` / `load()` | Promise playback, synchronous pause, reload current src; load has no completion Promise |
|
||||
| `currentTime` | Seconds; assignment starts an asynchronous seek without an awaitable setter |
|
||||
| `duration` | Seconds, possibly NaN when unknown or Infinity for live input |
|
||||
| `volume` / `muted` | Volume is numerically coerced/clamped0–1 and unmutes; muted is boolean-coerced |
|
||||
| `playbackRate` | Defaults1; accepts coerced positive non-NaN values and emits ratechange |
|
||||
| `paused` / `playing` / `ended` / `seeking` | Coordinator states; playing does not prove a real frame is displayed |
|
||||
| `readyState` / `networkState` / `error` | Compatibility state and `{code, message}` or null |
|
||||
| `videoWidth` / `videoHeight` | Video-engine dimensions |
|
||||
| `buffered` / `played` / `seekable` | Synthetic0-to-duration/current-time/duration ranges, not measured network buffering |
|
||||
| `createTimeRanges(start, end)` | Same synthetic helper, length0 or1; does not implement native index-range exceptions |
|
||||
| `canPlayType(type)` | Always maybe, without probing support |
|
||||
| `getM3u8State()` | Promise of state, or null for non-HLS/obsolete state |
|
||||
| `switchM3u8Quality(value)` / `switchM3u8Audio(value)` | Promise-based track selection |
|
||||
| `addEventListener` / `removeEventListener` | Shim media events, not Canvas DOM listeners |
|
||||
| `getBoundingClientRect()` | Delegate to Canvas |
|
||||
| `setAttribute(name, value)` | Shim handles src/muted, uses inert autoplay/loop setters, otherwise delegates to Canvas |
|
||||
| `destroy()` | Synchronous terminal shim/engine cleanup, idempotent |
|
||||
|
||||
Poster/autoplay/loop/crossOrigin follow the rules above. Controls=false, playsInline=true, preload='auto', defaultMuted=false and defaultPlaybackRate=1 have inert setters. Direct canvas.setAttribute is native Canvas behavior, unlike shim.setAttribute. Normally use art.destroy to also release host menus, alias and subscriptions.
|
||||
|
||||
HlsState contains levels, audios, currentLevel, currentAudio, videoMode and audioMode. Current entries may be null; modes are auto/manual. Audios are pairable with the selected video, so audio-only input does not guarantee menu entries. Narrow unknown track objects with the SDK types used by your application.
|
||||
|
||||
## Events, frame callbacks and cleanup
|
||||
|
||||
Shim events forward to ArtPlayer video:* as Event plus detail. Ordinary listeners retain duplicates, first-match removal, live-array iteration and exception propagation. This is not the complete native EventTarget API and does not accept native listener options.
|
||||
|
||||
Successful loading publishes loadedmetadata/durationchange/progress after participants prepare, then loadeddata/canplay/canplaythrough/progress. Waiting/loadstart remain deferred notifications. If neither selected track can decode, report code4 without success readiness; one usable track can support partial playback. Seek emits seeking/waiting, then seeked; track replacement also refreshes metadata/readiness. Each step checks for newer operations/destruction.
|
||||
|
||||
requestVideoFrameCallback(callback) returns a cancellable RAF ID for one synthetic notification, not a newly decoded-frame guarantee; it may run while paused. Callback now/captureTime/receiveTime use RAF time, expectedDisplayTime estimates now+16.6, and presentationTime/mediaTime are media seconds. Width/height come from the engine; presentedFrames, processingDuration and rtpTimestamp are always0. Do not use these values as decoder-throughput or network-latency measurements.
|
||||
|
||||
Source changes, cancellation and destruction invalidate old asynchronous work. Pause cancels pending playback and prevents old seeks from resuming it. Superseded operations may resolve normally; resolution does not prove their original intent ran. Active play/selection errors can reject. Src/load errors report through media events, normally code4; Range preflight is the separate path described above.
|
||||
|
||||
Destruction cancels input/preflight, frame callbacks, timers, audio nodes/context and decoder resources. Host cleanup also removes owned UI/subscriptions and deletes art.mediabunny only if it still belongs to this proxy. Late results cannot reactivate it. Cleanup is not a Promise of physical GPU/browser reclamation; long-duration and real-device acceptance remain separate.
|
||||
|
||||
## TypeScript
|
||||
|
||||
Root and `/legacy` preserve optional options and exact HTMLCanvasElement return inference. There is no `/runtime` subpath. Use explicit views for media capabilities:
|
||||
|
||||
```ts
|
||||
import Artplayer from 'artplayer';
|
||||
import mediabunny from 'artplayer-proxy-mediabunny';
|
||||
import type { MediaBunnyPlayer } from 'artplayer-proxy-mediabunny';
|
||||
|
||||
const art: MediaBunnyPlayer = new Artplayer({
|
||||
container: '#player', url: '/movie.m3u8',
|
||||
proxy: mediabunny({ m3u8: { quality: { control: true } } }),
|
||||
});
|
||||
async function selectFirstLevel(): Promise<void> {
|
||||
const shim = art.mediabunny;
|
||||
if (!shim) return;
|
||||
const state = await shim.getM3u8State();
|
||||
if (state?.levels.length) await shim.switchM3u8Quality(state.levels[0].id);
|
||||
}
|
||||
```
|
||||
|
||||
Public types are Option, Result, HlsLevel, HlsAudio, HlsState, MediaBunnyCanvas, MediaBunnyPlayer, MediaBunnyShim, MediaListener, SyntheticFrameCallback and SyntheticFrameMetadata. The Canvas view preserves native collisions; the optional player alias is removed on host destruction. CommonJS runtime supports direct and historical .default calls while the factory type remains plain callable. The legacy build does not polyfill WebCodecs, Web Audio or other browser capabilities.
|
||||
@@ -0,0 +1,120 @@
|
||||
# Canvas 视频代理
|
||||
|
||||
[English](../en/proxy/canvas.md)
|
||||
|
||||
使用真实 video 解码音视频,把画面绘制到 Canvas,并允许逐帧后处理。通过 ArtPlayer 的 proxy 配置安装,不放进 plugins 数组。本页描述未发布分支;Canvas 输出仍受浏览器解码、像素访问及设备能力限制。
|
||||
|
||||
## 安装与示例
|
||||
|
||||
```sh
|
||||
yarn add artplayer artplayer-proxy-canvas
|
||||
```
|
||||
|
||||
ESM 使用 `import canvas from 'artplayer-proxy-canvas'`。script 加载 `dist/artplayer-proxy-canvas.js`,全局名为 `artplayerProxyCanvas`。以下保留[原始示例](https://artplayer.org/?libs=./uncompiled/artplayer-proxy-canvas/index.js&example=canvas):
|
||||
|
||||
<div className="run-code" data-libs="./uncompiled/artplayer-proxy-canvas/index.js">▶ Run Code</div>
|
||||
|
||||
```js
|
||||
// npm i artplayer-proxy-canvas
|
||||
// import artplayerProxyCanvas from 'artplayer-proxy-canvas';
|
||||
|
||||
const art = new Artplayer({
|
||||
container: '.artplayer-app',
|
||||
url: '/assets/sample/video.mp4',
|
||||
poster: '/assets/sample/poster.jpg',
|
||||
volume: 0.5,
|
||||
autoplay: false,
|
||||
autoSize: false,
|
||||
screenshot: true,
|
||||
setting: true,
|
||||
loop: true,
|
||||
flip: true,
|
||||
pip: true,
|
||||
playbackRate: true,
|
||||
aspectRatio: true,
|
||||
fullscreen: true,
|
||||
fullscreenWeb: true,
|
||||
miniProgressBar: true,
|
||||
autoPlayback: true,
|
||||
autoOrientation: true,
|
||||
subtitle: {
|
||||
url: '/assets/sample/subtitle.srt',
|
||||
},
|
||||
thumbnails: {
|
||||
url: '/assets/sample/thumbnails.png',
|
||||
number: 60,
|
||||
column: 10,
|
||||
scale: 0.85,
|
||||
},
|
||||
proxy: artplayerProxyCanvas(),
|
||||
})
|
||||
```
|
||||
|
||||
可选参数是一个绘制回调,不是配置对象:
|
||||
|
||||
```js
|
||||
proxy: artplayerProxyCanvas((context, video) => {
|
||||
context.fillStyle = 'rgba(0, 0, 0, 0.4)';
|
||||
context.fillRect(0, 0, context.canvas.width, 32);
|
||||
context.fillStyle = '#fff';
|
||||
context.fillText(video.currentTime.toFixed(1), 10, 22);
|
||||
})
|
||||
```
|
||||
|
||||
回调收到真实 CanvasRenderingContext2D 和内部 HTMLVideoElement。基础画面先绘制,已取得的 ImageBitmap 先关闭,再同步调用回调;返回值不参与渲染,异步回调的 Promise 不被等待。每帧需要的 Canvas 状态应自行设置,尺寸改变会重置绘制状态。
|
||||
|
||||
## 返回元素与媒体接口
|
||||
|
||||
初始化同步返回实际 HTMLCanvasElement,ArtPlayer 将其作为视频位置的元素。内部 video 在播放前连接到播放器容器,透明且不可聚焦,不创建第二个可见播放器。
|
||||
|
||||
Canvas 原有成员优先:width/height、DOM 事件、getContext、toDataURL 等仍属于 Canvas。只有遍历到且 Canvas 上不存在的 video 成员才转发给内部 video;这不是完整 HTMLVideoElement 的运行时复制。需要底层 video 时使用回调参数。
|
||||
|
||||
媒体事件从内部 video 转成 ArtPlayer 的 `video:<type>`,传递原 Event。不要把 `canvas.addEventListener('play', ...)` 当作内部 video 监听;使用 `art.on('video:play', ...)`。媒体 src/srcObject 写入及 load 会使旧绘制失效。
|
||||
|
||||
subtitle 仍使用 ArtPlayer 的正常配置。活动代理的 appendChild 对 HTML track 有专门转发,使实际字幕 track 挂到内部 video;普通子节点继续挂在 Canvas。核心负责字幕 track 替换和 URL,代理最初的空 metadata track 会在第一条真实字幕插入前移除。字幕 DOM 的最终父节点是 VIDEO。
|
||||
|
||||
## 绘制、尺寸和事件
|
||||
|
||||
| 行为 | 实际处理 |
|
||||
| --- | --- |
|
||||
| 播放 | 开启单条 RAF 绘制链,不重叠执行多次异步取帧 |
|
||||
| 暂停、清空来源 | 取消待处理绘制,保留已显示画面 |
|
||||
| 暂停后 seek | 请求一次新画面 |
|
||||
| resize | 合并绘制请求,旧异步结果失效 |
|
||||
| loadedmetadata | 有有效原始尺寸时设置 Canvas width/height |
|
||||
| autoSize 为 false | resize 时按视频比例适配容器,并用 padding 居中 |
|
||||
| autoSize 为 true | 代理不执行上述容器适配,交由播放器处理自动尺寸 |
|
||||
|
||||
帧需要 readyState 至少2、非 seeking、有效视频和画布尺寸。可用时使用 createImageBitmap,否则直接 drawImage(video);这不是保证每个解码帧都有回调。跨域视频的 CORS 配置和响应仍决定截图/像素处理是否可用。
|
||||
|
||||
| 自定义事件 | 参数与顺序 |
|
||||
| --- | --- |
|
||||
| `artplayerProxyCanvas:draw` | context、真实 video;成功回调之后发出 |
|
||||
| `artplayerProxyCanvas:error` | 原始失败值;绘制、回调或初始化失败时报告 |
|
||||
|
||||
缺少2D context 会报告错误。首帧 createImageBitmap 在特定未解码状态下的 InvalidStateError 会等下一次绘制请求;不会把回调或 drawImage 的失败都当成“等首帧”。回调中销毁播放器后不再发 draw 或重启循环。
|
||||
|
||||
## 清理与能力边界
|
||||
|
||||
播放器销毁后取消 RAF/延迟初始化,移除内部监听器,暂停并卸载 video、清空 srcObject 和 Canvas 缓冲区。晚到位图仍会关闭。释放流引用不会 stop 调用方拥有的媒体 tracks;资源清理尽量全部执行后才抛首个错误。逃逸的媒体方法和转发 setter 不再启动播放或重新设源,原生 Canvas 方法仍操作原元素。
|
||||
|
||||
代理没有独立的公开 start/stop/destroy 控制对象;使用 ArtPlayer 的生命周期。原示例打开的 PiP、全屏、截图等选项仍取决于实际底层能力,不能从配置存在推断所有浏览器都支持。Windows 浏览器结果不能替代 Safari/iPhone/Android 真机验证。
|
||||
|
||||
## TypeScript 与旧类型
|
||||
|
||||
根和 `/legacy` 保留最新已发布1.1.0 的可选回调、纯工厂及精确 HTMLCanvasElement 返回类型,普通替换函数仍可赋值。1.0 的 export= 必填回调形状与之冲突,已批准选择最新根形状;需要准确 ESM/旧式 CommonJS 调用和自引用描述时用同实现的 `/runtime`:
|
||||
|
||||
```ts
|
||||
import Artplayer from 'artplayer';
|
||||
import canvas from 'artplayer-proxy-canvas/runtime';
|
||||
import type { MediaCanvas, Option } from 'artplayer-proxy-canvas/runtime';
|
||||
|
||||
const overlay: Option = (context, video) => {
|
||||
context.fillText(video.currentTime.toFixed(1), 10, 22);
|
||||
};
|
||||
const art = new Artplayer({ container: '#player', url: '/movie.mp4', proxy: canvas(overlay) });
|
||||
const media = art.template.$video as MediaCanvas;
|
||||
function play(): Promise<void> { return media.play(); }
|
||||
```
|
||||
|
||||
公开类型为 Option、Result、Factory、Callable、MediaCanvas、RuntimeFactory。MediaCanvas 是显式类型视图,保留 Canvas 同名成员,不增加实际能力或收紧工厂的返回推导。根 NodeNext ESM 的历史命名空间类型仍保留,准确默认调用使用 runtime。JavaScript 根工厂和 `.default` 指向同一函数;runtime 类型将该别名描述为 readonly。CommonJS 可直接 require,旧 TS 的 `import = require` 可选择 runtime 入口。
|
||||
@@ -0,0 +1,168 @@
|
||||
# Mediabunny 视频代理
|
||||
|
||||
[English](../en/proxy/mediabunny.md)
|
||||
|
||||
通过 Mediabunny 读取媒体,由 Canvas 与音频管线提供 video-like 接口,并支持 HLS 音轨/清晰度选择。安装位置是 ArtPlayer 的 proxy 选项。本页描述未发布分支;它不补齐浏览器缺少的解码器或所有 HTMLVideoElement 能力。
|
||||
|
||||
## 安装和示例
|
||||
|
||||
```sh
|
||||
yarn add artplayer artplayer-proxy-mediabunny
|
||||
```
|
||||
|
||||
ESM 使用 `import mediabunny from 'artplayer-proxy-mediabunny'`。script 加载 `dist/artplayer-proxy-mediabunny.js`,全局为 `artplayerProxyMediabunny`。下面保留[原始 HLS 示例](https://artplayer.org/?libs=./uncompiled/artplayer-proxy-mediabunny/index.js&example=mediabunny),其远端流不是离线夹具:
|
||||
|
||||
<div className="run-code" data-libs="./uncompiled/artplayer-proxy-mediabunny/index.js">▶ Run Code</div>
|
||||
|
||||
```js
|
||||
// npm i artplayer-proxy-mediabunny
|
||||
// import artplayerProxyMediabunny from 'artplayer-proxy-mediabunny';
|
||||
|
||||
const art = new Artplayer({
|
||||
container: '.artplayer-app',
|
||||
url: 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8',
|
||||
autoSize: true,
|
||||
setting: true,
|
||||
loop: true,
|
||||
flip: true,
|
||||
playbackRate: true,
|
||||
fullscreen: true,
|
||||
fullscreenWeb: true,
|
||||
miniProgressBar: true,
|
||||
autoPlayback: true,
|
||||
autoOrientation: true,
|
||||
proxy: artplayerProxyMediabunny({
|
||||
m3u8: {
|
||||
quality: {
|
||||
control: true,
|
||||
setting: true,
|
||||
getName: level => level.height ? `${level.height}P` : level.name,
|
||||
title: 'Quality',
|
||||
auto: 'Auto',
|
||||
},
|
||||
audio: {
|
||||
control: true,
|
||||
setting: true,
|
||||
getName: track => track.name || track.language,
|
||||
title: 'Audio',
|
||||
auto: 'Auto',
|
||||
},
|
||||
},
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
工厂接受可选配置,初始化同步返回真实 HTMLCanvasElement,同时设置 `art.mediabunny`。Canvas 的同名原生成员优先:DOM 事件/属性方法并不自动变成 shim 方法。用 ArtPlayer 的 video:* 事件或显式 shim 访问媒体能力。
|
||||
|
||||
## 输入和配置
|
||||
|
||||
| 选项 | 默认或作用 |
|
||||
| --- | --- |
|
||||
| `source` | 初始来源;非空值优先于 art.option.url,之后宿主赋值仍可替换 |
|
||||
| `loadTimeout` | 0,不设置加载期限;有限正数按毫秒限制本次加载 |
|
||||
| `timeupdateInterval` | 250ms,渲染器的更新间隔,不是精确计时承诺 |
|
||||
| `avSyncTolerance` | 0.12秒,参与开启丢帧时的迟到阈值计算 |
|
||||
| `dropLateFrames` | false;启用后允许跳过超过容差的迟到画面 |
|
||||
| `poster` | 默认空字符串;视频引擎初始化时捕获的海报地址 |
|
||||
| `preflightRange` | false;可选的普通 URL HEAD 检查 |
|
||||
| `volume` / `muted` | 0.7 / false 的初始 shim 状态,之后可被播放器设置覆盖 |
|
||||
| `autoplay` / `loop` | 默认 false 的兼容读取值;不会仅凭代理配置自行启动/循环 |
|
||||
| `crossOrigin` | 默认空字符串的兼容读取值;不配置 SDK fetch 的凭据或 CORS |
|
||||
| `m3u8` | 可选清晰度/音轨菜单,见下节 |
|
||||
|
||||
自动播放、循环等播放器行为使用 ArtPlayer 自身配置,并受浏览器播放策略限制。代理 shim 的 autoplay/loop/crossOrigin setter 保留为空操作,不能当作原生 video 设置使用。改变 shim.poster 更新其配置读取值,不保证重绘视频引擎已捕获的海报。
|
||||
|
||||
source 声明接受 URL 字符串、Blob 或 ReadableStream 字节流;运行时也把 SDK Source/SourceRef 交给 SDK 验证。字符串按 `.m3u8` 后缀(忽略大小写,允许 query/hash)识别 HLS,使用 HLS_FORMATS;其他输入使用 ALL_FORMATS。无此后缀的地址不会仅凭返回内容自动启用本代理的 HLS 菜单状态。可读流能否随机访问/重复加载仍取决于其来源能力。
|
||||
|
||||
Range 检查仅对启用检查的非 HLS 字符串请求 HEAD。没有 accept-ranges 或值为 none 时发出 detail 为 RangeNotSupported Event 的 error,停止该次加载前置过程,但不会设置 shim.error。网络请求异常会警告并继续;HLS 和非字符串跳过 HEAD。它不是完整 Range GET 或编码支持验证。媒体服务器仍需要实际可读响应及 CORS。
|
||||
|
||||
## HLS 菜单与选择
|
||||
|
||||
m3u8.quality 和 m3u8.audio 都可配置 control、setting、title、auto、getName。control/setting 都不启用时不显示对应菜单;使用 setting 还需播放器设置面板。
|
||||
|
||||
| 配置 | 默认 |
|
||||
| --- | --- |
|
||||
| `quality.title` / `audio.title` | Quality / Audio |
|
||||
| 两者 `auto` | Auto |
|
||||
| `quality.getName(level)` | level.name,否则高度加 P |
|
||||
| `audio.getName(track)` | track.name、lang、language 顺序取值 |
|
||||
|
||||
level 包含 id、index、name、height、bitrate;audio 包含 id、index、name、lang、language、bitrate。完整状态还带 SDK track 对象。name 可为 null,getName 应返回可显示字符串。
|
||||
|
||||
有视频级别才显示清晰度;可配对音轨至少两条才显示音轨菜单。清晰度按高度降序,相同显示名称会合并,优先保留当前选中项。高亮和按钮文本依据实际当前 track;即使 mode 是 auto,也可能显示实际选中的清晰度/音轨而非 Auto。
|
||||
|
||||
```js
|
||||
const shim = art.mediabunny;
|
||||
const state = await shim.getM3u8State();
|
||||
if (state?.levels.length) {
|
||||
await shim.switchM3u8Quality(state.levels[0].id);
|
||||
}
|
||||
await shim.switchM3u8Audio('auto');
|
||||
```
|
||||
|
||||
用状态中的数值 id 或字面量 `'auto'`,不要把 index、显示名或数字字符串当成 id。Auto 重新选择 SDK primary track,不代表代理会测带宽并持续自动切码率。切清晰度时尽量保留可配对音轨,否则重新选 primary pairable audio;音轨变化同样维护视频配对。未知 id 沿用当前轨道;无 HLS 来源时这些方法无操作。
|
||||
|
||||
菜单标识为 mediabunny-quality / mediabunny-audio。metadata/restart 刷新,loadstart/error/销毁清理。新来源缺少对应能力时移除旧 UI,旧菜单回调及过期选择不能覆盖新状态。异步切换失败仍可能拒绝,应处理 Promise。
|
||||
|
||||
## shim 成员
|
||||
|
||||
| 成员 | 行为 |
|
||||
| --- | --- |
|
||||
| `canvas` | 原生输出元素 |
|
||||
| `src` / `currentSrc` | 来源值;非空 src 赋值发起加载,空值不等同于卸载 |
|
||||
| `play()` / `pause()` / `load()` | Promise 播放、同步暂停、按当前 src 重新加载;load 不返回完成 Promise |
|
||||
| `currentTime` | 秒;写入启动异步 seek,setter 本身不能等待完成 |
|
||||
| `duration` | 秒;未知可为 NaN,直播可为 Infinity |
|
||||
| `volume` / `muted` | volume 数值转换并钳制0–1,写 volume 同时取消静音;写 muted 布尔化 |
|
||||
| `playbackRate` | 默认1,接受转换后大于0且非NaN的值;设置发 ratechange |
|
||||
| `paused` / `playing` / `ended` / `seeking` | 播放协调器状态,playing 不是已显示真实帧的证明 |
|
||||
| `readyState` / `networkState` / `error` | 兼容状态及 `{code, message}` 或 null |
|
||||
| `videoWidth` / `videoHeight` | 视频引擎尺寸 |
|
||||
| `buffered` / `played` / `seekable` | 合成范围:0到时长、0到当前时间、0到时长,不是实际网络缓冲测量 |
|
||||
| `createTimeRanges(start, end)` | 同一合成范围工具,空范围返回长度0;非空长度1,未实现原生索引越界异常 |
|
||||
| `canPlayType(type)` | 固定返回 maybe,不检测支持能力 |
|
||||
| `getM3u8State()` | Promise,非HLS/失效状态为 null |
|
||||
| `switchM3u8Quality(value)` / `switchM3u8Audio(value)` | Promise 轨道选择 |
|
||||
| `addEventListener` / `removeEventListener` | shim 媒体事件,不是 Canvas DOM 监听器 |
|
||||
| `getBoundingClientRect()` | 委托 Canvas |
|
||||
| `setAttribute(name, value)` | shim 方法特殊处理 src/muted,autoplay/loop沿用空setter;其他委托Canvas |
|
||||
| `destroy()` | 同步终止 shim 及其引擎,重复调用无操作 |
|
||||
|
||||
兼容字段 poster、autoplay、loop、crossOrigin 如上。controls=false、playsInline=true、preload='auto'、defaultMuted=false、defaultPlaybackRate=1;这些字段的 setter 无操作。直接 canvas.setAttribute 使用原生 Canvas 方法,与 shim.setAttribute 不同。正常宿主清理应使用 art.destroy,以同时释放 UI/别名/订阅。
|
||||
|
||||
HlsState 包含 levels、audios、currentLevel、currentAudio、videoMode、audioMode;当前项可为 null,模式为 auto/manual。audios 是与当前视频可配对的音轨,纯音频状态不保证有菜单项。track 类型为 unknown,按应用使用的 SDK 类型缩窄。
|
||||
|
||||
## 事件、帧回调与清理
|
||||
|
||||
shim 事件转发到 ArtPlayer 的 video:*,监听参数为 Event 加 detail。普通监听保留重复注册、移除首个匹配项和活数组遍历;异常可传播,不提供原生 EventTarget 的 options 参数或完整语义。
|
||||
|
||||
成功加载在音视频准备后先发布 loadedmetadata/durationchange/progress,再发布 loadeddata/canplay/canplaythrough/progress;加载的 waiting/loadstart 保留延迟通知。两个已选轨道都无法解码时报告 code4,不发布成功就绪序列;仍有可用轨道时可部分播放。seek 发 seeking/waiting,完成后 seeked;轨道替换还会更新 metadata/ready 事件。每一步检查是否已被新操作或销毁取代。
|
||||
|
||||
requestVideoFrameCallback(callback) 返回可取消的 RAF 编号,是一次性模拟通知,不保证解码新帧;暂停时也可能回调。callback(now, metadata) 的 now/captureTime/receiveTime 为 RAF 时间,expectedDisplayTime 估计为 now+16.6;presentationTime 和 mediaTime 是媒体秒数。width/height 来自引擎,presentedFrames、processingDuration、rtpTimestamp 恒为0。不要将这些值用于真实解码吞吐或网络延迟统计。
|
||||
|
||||
切源、取消与销毁使旧异步任务失效;暂停取消待开始播放并阻止旧 seek 完成后自行恢复。被替代操作可能正常结算,不能据此判定原意图已执行。活动播放/轨道错误仍可拒绝;src/load 的加载错误通过媒体事件报告,常规错误为 code4,Range 前置检查是上述独立路径。
|
||||
|
||||
销毁取消输入和预检查、帧回调、定时器、音频节点/AudioContext 及解码资源。宿主销毁还移除自身菜单、订阅,并只删除仍由该代理持有的 art.mediabunny。晚到结果不能重新激活实例。清理调用不是 GPU/浏览器全部底层资源已物理回收的 Promise;长期播放和真机验证保持独立。
|
||||
|
||||
## TypeScript
|
||||
|
||||
根和 `/legacy` 保留可选配置及精确 HTMLCanvasElement 返回推导,无 `/runtime` 子路径。需要媒体能力时使用显式类型视图:
|
||||
|
||||
```ts
|
||||
import Artplayer from 'artplayer';
|
||||
import mediabunny from 'artplayer-proxy-mediabunny';
|
||||
import type { MediaBunnyPlayer } from 'artplayer-proxy-mediabunny';
|
||||
|
||||
const art: MediaBunnyPlayer = new Artplayer({
|
||||
container: '#player', url: '/movie.m3u8',
|
||||
proxy: mediabunny({ m3u8: { quality: { control: true } } }),
|
||||
});
|
||||
async function selectFirstLevel(): Promise<void> {
|
||||
const shim = art.mediabunny;
|
||||
if (!shim) return;
|
||||
const state = await shim.getM3u8State();
|
||||
if (state?.levels.length) await shim.switchM3u8Quality(state.levels[0].id);
|
||||
}
|
||||
```
|
||||
|
||||
公开类型为 Option、Result、HlsLevel、HlsAudio、HlsState、MediaBunnyCanvas、MediaBunnyPlayer、MediaBunnyShim、MediaListener、SyntheticFrameCallback、SyntheticFrameMetadata。Canvas 视图保留同名原生成员;Player 别名可缺省且销毁后移除。CommonJS 运行时支持直接工厂和历史 `.default`,默认工厂类型仍为纯可调用签名。legacy 构建不提供 WebCodecs、Web Audio 或其他浏览器能力的 polyfill。
|
||||
@@ -50,5 +50,5 @@
|
||||
}
|
||||
|
||||
// <stdin>
|
||||
installNavigation(window, ["advanced/built-in.html", "advanced/built-in", "advanced/class.html", "advanced/class", "advanced/event.html", "advanced/event", "advanced/global.html", "advanced/global", "advanced/plugin.html", "advanced/plugin", "advanced/property.html", "advanced/property", "component/contextmenu.html", "component/contextmenu", "component/controls.html", "component/controls", "component/layers.html", "component/layers", "component/setting.html", "component/setting", "", "index.html", "plugin/ads.html", "plugin/ads", "plugin/ambilight.html", "plugin/ambilight", "plugin/asr.html", "plugin/asr", "plugin/audio-track.html", "plugin/audio-track", "plugin/auto-thumbnail.html", "plugin/auto-thumbnail", "plugin/chapter.html", "plugin/chapter", "plugin/chromecast.html", "plugin/chromecast", "plugin/danmuku-mask.html", "plugin/danmuku-mask", "plugin/danmuku.html", "plugin/danmuku", "plugin/dash-control.html", "plugin/dash-control", "plugin/document-pip.html", "plugin/document-pip", "plugin/hls-control.html", "plugin/hls-control", "plugin/jassub.html", "plugin/jassub", "plugin/multiple-subtitles.html", "plugin/multiple-subtitles", "plugin/vast.html", "plugin/vast", "plugin/vtt-thumbnail.html", "plugin/vtt-thumbnail", "start/i18n.html", "start/i18n", "start/option.html", "start/option", "tool/iframe.html", "tool/iframe", "tool/thumbnail.html", "tool/thumbnail"]);
|
||||
installNavigation(window, ["advanced/built-in.html", "advanced/built-in", "advanced/class.html", "advanced/class", "advanced/event.html", "advanced/event", "advanced/global.html", "advanced/global", "advanced/plugin.html", "advanced/plugin", "advanced/property.html", "advanced/property", "component/contextmenu.html", "component/contextmenu", "component/controls.html", "component/controls", "component/layers.html", "component/layers", "component/setting.html", "component/setting", "", "index.html", "plugin/ads.html", "plugin/ads", "plugin/ambilight.html", "plugin/ambilight", "plugin/asr.html", "plugin/asr", "plugin/audio-track.html", "plugin/audio-track", "plugin/auto-thumbnail.html", "plugin/auto-thumbnail", "plugin/chapter.html", "plugin/chapter", "plugin/chromecast.html", "plugin/chromecast", "plugin/danmuku-mask.html", "plugin/danmuku-mask", "plugin/danmuku.html", "plugin/danmuku", "plugin/dash-control.html", "plugin/dash-control", "plugin/document-pip.html", "plugin/document-pip", "plugin/hls-control.html", "plugin/hls-control", "plugin/jassub.html", "plugin/jassub", "plugin/multiple-subtitles.html", "plugin/multiple-subtitles", "plugin/vast.html", "plugin/vast", "plugin/vtt-thumbnail.html", "plugin/vtt-thumbnail", "proxy/canvas.html", "proxy/canvas", "proxy/mediabunny.html", "proxy/mediabunny", "start/i18n.html", "start/i18n", "start/option.html", "start/option", "tool/iframe.html", "tool/iframe", "tool/thumbnail.html", "tool/thumbnail"]);
|
||||
})();
|
||||
Reference in new issue
Block a user