mirror of
https://github.com/zoriya/v10.git
synced 2026-08-14 18:04:49 +00:00
feat(spf): export ./live-hls + sandbox live-engine harness
Add the ./live-hls subpath export (build entry + package export) and a LiveHlsEngineSignals type. Add a bare sandbox template (live-hls-engine) that wires createLiveHlsEngine to a raw <video> — no player/skin — and logs playback state, for end-to-end live-playback verification. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
bbaeb52ed8
commit
4a58e85015
@@ -0,0 +1,27 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Live HLS Engine (SPF)</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; padding: 0; margin: 0; }
|
||||
body { padding: 16px; font-family: monospace; font-size: 13px; color: #e0e0e0; background: #1a1a1a; }
|
||||
h1 { margin-bottom: 10px; font-size: 15px; color: #fff; }
|
||||
video { display: block; width: 100%; max-width: 900px; aspect-ratio: 16/9; background: #000; }
|
||||
.state { display: grid; grid-template-columns: max-content 1fr; gap: 2px 16px; max-width: 900px; padding: 6px 8px; margin: 8px 0; background: #222; border-radius: 3px; }
|
||||
.state-key { color: #777; }
|
||||
.val { color: #6cf; }
|
||||
.log { max-width: 900px; height: 360px; padding: 6px 8px; overflow-y: auto; font-size: 11px; line-height: 1.6; background: #111; border-radius: 3px; }
|
||||
.err { color: #f66; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Live HLS Engine — <code>createLiveHlsEngine</code></h1>
|
||||
<video id="video" controls muted playsinline></video>
|
||||
<div class="state" id="state"></div>
|
||||
<h2>Log</h2>
|
||||
<div class="log" id="log"></div>
|
||||
<script type="module" src="./main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Bare harness for the experimental SPF live HLS engine — wires
|
||||
* `createLiveHlsEngine` to a raw <video> (no player/skin) and logs playback
|
||||
* state, to validate live CMAF/LL-HLS playback end-to-end.
|
||||
*/
|
||||
import { createLiveHlsEngine, type LiveHlsEngineSignals } from '@videojs/spf/live-hls';
|
||||
|
||||
const SRC = 'https://stream.mux.com/1DRguGQyA2K2TIelbV7rU7uePlZXHyYWR1LEMC8iTC4.m3u8';
|
||||
|
||||
const video = document.getElementById('video') as HTMLVideoElement;
|
||||
const logEl = document.getElementById('log')!;
|
||||
const stateEl = document.getElementById('state')!;
|
||||
|
||||
const log = (msg: string, cls = '') => {
|
||||
const line = document.createElement('div');
|
||||
if (cls) line.className = cls;
|
||||
line.textContent = `${new Date().toISOString().slice(11, 23)} ${msg}`;
|
||||
logEl.prepend(line);
|
||||
};
|
||||
|
||||
const buffered = () =>
|
||||
video.buffered.length
|
||||
? `${video.buffered.start(0).toFixed(2)}–${video.buffered.end(video.buffered.length - 1).toFixed(2)}`
|
||||
: 'none';
|
||||
|
||||
function renderState() {
|
||||
const rows: [string, string][] = [
|
||||
['paused', String(video.paused)],
|
||||
['currentTime', video.currentTime.toFixed(2)],
|
||||
['readyState', String(video.readyState)],
|
||||
['buffered', buffered()],
|
||||
['duration', String(video.duration)],
|
||||
['error', video.error ? `${video.error.code}` : 'none'],
|
||||
];
|
||||
stateEl.innerHTML = rows.map(([k, v]) => `<div class="state-key">${k}</div><div class="val">${v}</div>`).join('');
|
||||
}
|
||||
setInterval(renderState, 500);
|
||||
|
||||
for (const ev of ['loadedmetadata', 'durationchange', 'canplay', 'playing', 'waiting', 'stalled', 'seeked', 'ended']) {
|
||||
video.addEventListener(ev, () =>
|
||||
log(`${ev} t=${video.currentTime.toFixed(2)} buffered=${buffered()} ready=${video.readyState}`)
|
||||
);
|
||||
}
|
||||
video.addEventListener('error', () => log(`video error: ${video.error?.code}`, 'err'));
|
||||
|
||||
let signals: LiveHlsEngineSignals | undefined;
|
||||
const engine = createLiveHlsEngine({
|
||||
onSignalsReady: (refs) => {
|
||||
signals = refs;
|
||||
},
|
||||
});
|
||||
if (!signals) throw new Error('live engine signals not ready');
|
||||
|
||||
video.preload = 'auto';
|
||||
signals.context.mediaElement.set(video);
|
||||
signals.state.presentation.set({ url: SRC });
|
||||
log(`engine wired; presentation = ${SRC}`);
|
||||
|
||||
video.play().then(
|
||||
() => log('play() resolved'),
|
||||
(e) => log(`play() rejected: ${e}`, 'err')
|
||||
);
|
||||
|
||||
// Expose for ad-hoc debugging from the console / Playwright.
|
||||
Object.assign(window as unknown as Record<string, unknown>, { __live: { engine, signals, video } });
|
||||
@@ -33,6 +33,11 @@
|
||||
"development": "./dist/dev/hls.js",
|
||||
"default": "./dist/default/hls.js"
|
||||
},
|
||||
"./live-hls": {
|
||||
"types": "./dist/dev/live-hls.d.ts",
|
||||
"development": "./dist/dev/live-hls.js",
|
||||
"default": "./dist/default/live-hls.js"
|
||||
},
|
||||
"./background-video": {
|
||||
"types": "./dist/dev/background-video.d.ts",
|
||||
"development": "./dist/dev/background-video.js",
|
||||
|
||||
@@ -31,7 +31,12 @@ import { resolvePresentation } from '../../behaviors/resolve-presentation';
|
||||
import { setupFailoverMonitor } from '../../behaviors/setup-failover-monitor';
|
||||
import { syncPreload } from '../../behaviors/sync-preload';
|
||||
import { switchAudioTrack, switchVideoTrack } from '../../behaviors/track-switching';
|
||||
import type { SimpleHlsEngineConfig, SimpleHlsEngineContext, SimpleHlsEngineState } from '../hls/engine';
|
||||
import type {
|
||||
SimpleHlsEngineConfig,
|
||||
SimpleHlsEngineContext,
|
||||
SimpleHlsEngineSignals,
|
||||
SimpleHlsEngineState,
|
||||
} from '../hls/engine';
|
||||
|
||||
/** Config for the live HLS engine: the VoD config plus live-only options. */
|
||||
export interface LiveHlsEngineConfig extends SimpleHlsEngineConfig {
|
||||
@@ -44,6 +49,7 @@ export interface LiveHlsEngineConfig extends SimpleHlsEngineConfig {
|
||||
|
||||
export type LiveHlsEngineState = SimpleHlsEngineState;
|
||||
export type LiveHlsEngineContext = SimpleHlsEngineContext;
|
||||
export type LiveHlsEngineSignals = SimpleHlsEngineSignals;
|
||||
|
||||
const shareSignals = makeShareSignals<LiveHlsEngineState, LiveHlsEngineContext>([
|
||||
'userVideoTrackSelection',
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
export type { LiveHlsEngineConfig, LiveHlsEngineContext, LiveHlsEngineState } from './engine';
|
||||
export type {
|
||||
LiveHlsEngineConfig,
|
||||
LiveHlsEngineContext,
|
||||
LiveHlsEngineSignals,
|
||||
LiveHlsEngineState,
|
||||
} from './engine';
|
||||
export { createLiveHlsEngine } from './engine';
|
||||
|
||||
@@ -8,6 +8,7 @@ const createConfig = (mode: PackageBuildMode): UserConfig => ({
|
||||
index: 'src/index.ts',
|
||||
dom: 'src/dom.ts',
|
||||
hls: 'src/playback/engines/hls/index.ts',
|
||||
'live-hls': 'src/playback/engines/live-hls/index.ts',
|
||||
'background-video': 'src/playback/engines/background-video/index.ts',
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user