three-vr-player 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"file":"VideoSource-Cx7XPpvZ.js","sources":["../src/core/proxy.ts","../src/core/projections.ts","../src/ui/format.ts","../src/core/vr-panel-layout.ts","../src/core/VRControls.ts","../src/core/StereoScene.ts","../src/core/LookControls.ts","../src/core/VideoSource.ts"],"sourcesContent":["export type StreamFormat = 'hls' | 'dash' | 'progressive';\r\n\r\nexport interface ProxyConfig {\r\n url: string;\r\n apiPassword?: string;\r\n headers?: Record<string, string>;\r\n}\r\n\r\n/** Detect streaming format from a URL by extension. */\r\nexport function detectFormat(rawUrl: string): StreamFormat {\r\n let pathname = rawUrl;\r\n try { pathname = new URL(rawUrl).pathname; } catch { /* relative/opaque: use raw */ }\r\n const lower = pathname.toLowerCase();\r\n if (lower.endsWith('.m3u8')) return 'hls';\r\n if (lower.endsWith('.mpd')) return 'dash';\r\n return 'progressive';\r\n}\r\n\r\nconst ENDPOINTS: Record<StreamFormat, string> = {\r\n progressive: '/proxy/stream',\r\n hls: '/proxy/hls/manifest.m3u8',\r\n dash: '/proxy/mpd/manifest.m3u8',\r\n};\r\n\r\n/**\r\n * Build the URL the `<video>`/hls.js should load. When `proxy` is omitted the raw\r\n * URL is returned (it must then be CORS-clean to be used as a WebGL texture).\r\n */\r\nexport function buildProxyUrl(rawUrl: string, proxy?: ProxyConfig): { url: string; format: StreamFormat } {\r\n const format = detectFormat(rawUrl);\r\n if (!proxy || !proxy.url) return { url: rawUrl, format };\r\n const base = proxy.url.replace(/\\/+$/, '');\r\n const params = new URLSearchParams();\r\n params.set('d', rawUrl);\r\n if (proxy.apiPassword) params.set('api_password', proxy.apiPassword);\r\n for (const [name, value] of Object.entries(proxy.headers ?? {})) {\r\n if (value != null && value !== '') params.set(`h_${name}`, value);\r\n }\r\n return { url: `${base}${ENDPOINTS[format]}?${params.toString()}`, format };\r\n}\r\n","import type { Projection } from '../types.js';\r\n\r\nexport type Split = 'mono' | 'sbs' | 'tb';\r\nexport type GeomKind = 'sphere180' | 'sphere360' | 'plane';\r\n\r\nexport interface ModeConfig {\r\n geom: GeomKind;\r\n split: Split;\r\n stereo: boolean;\r\n flat?: boolean;\r\n /** plane only: 'full' = displayW/H, 'per-eye' = (W/2)/H */\r\n aspect?: 'full' | 'per-eye';\r\n}\r\n\r\nexport const MODES: Record<Projection, ModeConfig> = {\r\n '180-sbs': { geom: 'sphere180', split: 'sbs', stereo: true },\r\n '180-mono': { geom: 'sphere180', split: 'mono', stereo: false },\r\n '360-mono': { geom: 'sphere360', split: 'mono', stereo: false },\r\n '360-sbs': { geom: 'sphere360', split: 'sbs', stereo: true },\r\n '360-tb': { geom: 'sphere360', split: 'tb', stereo: true },\r\n 'flat-2d': { geom: 'plane', split: 'mono', stereo: false, flat: true, aspect: 'full' },\r\n 'flat-sbs-full': { geom: 'plane', split: 'sbs', stereo: true, flat: true, aspect: 'per-eye' },\r\n 'flat-sbs-half': { geom: 'plane', split: 'sbs', stereo: true, flat: true, aspect: 'full' },\r\n};\r\n\r\nexport const PROJECTIONS: { value: Projection; label: string }[] = [\r\n { value: '180-sbs', label: '180° SBS (VR180)' },\r\n { value: '180-mono', label: '180° Mono' },\r\n { value: '360-mono', label: '360° Mono' },\r\n { value: '360-sbs', label: '360° SBS' },\r\n { value: '360-tb', label: '360° Top-Bottom' },\r\n { value: 'flat-2d', label: 'Flat 2D' },\r\n { value: 'flat-sbs-full', label: 'Flat 3D — Full SBS' },\r\n { value: 'flat-sbs-half', label: 'Flat 3D — Half SBS' },\r\n];\r\n\r\nexport function isFlatMode(p: Projection): boolean {\r\n return !!MODES[p]?.flat;\r\n}\r\n\r\n/** Guess a projection from a URL / filename; null when nothing recognizable. */\r\nexport function detectProjection(url: string): Projection | null {\r\n const s = String(url).toLowerCase();\r\n const tb = /(^|[^a-z])(tb|ou|top.?bottom|over.?under)([^a-z]|$)/.test(s);\r\n const sbs = /(sbs|side.?by.?side)/.test(s);\r\n if (s.includes('360')) {\r\n if (tb) return '360-tb';\r\n if (sbs) return '360-sbs';\r\n return '360-mono';\r\n }\r\n if (s.includes('180') || s.includes('vr180')) {\r\n return s.includes('mono') ? '180-mono' : '180-sbs';\r\n }\r\n if (sbs) return s.includes('half') ? 'flat-sbs-half' : 'flat-sbs-full';\r\n return null;\r\n}\r\n","/** Format seconds as m:ss or h:mm:ss. */\r\nexport function formatTime(seconds: number): string {\r\n if (!Number.isFinite(seconds) || seconds < 0) return '0:00';\r\n const s = Math.floor(seconds % 60);\r\n const m = Math.floor((seconds / 60) % 60);\r\n const h = Math.floor(seconds / 3600);\r\n const ss = String(s).padStart(2, '0');\r\n return h > 0 ? `${h}:${String(m).padStart(2, '0')}:${ss}` : `${m}:${ss}`;\r\n}\r\n","/**\r\n * Geometry and hit-testing for the in-VR control panel, expressed purely in\r\n * canvas-pixel space — no three.js, no DOM. Both the painter (VRControls draws\r\n * to a 2D canvas) and the controller-ray hit-tester read the SAME layout, so a\r\n * button is never drawn in one place and clicked in another. Kept dependency-free\r\n * so it can be unit-tested.\r\n */\r\n\r\nexport const PANEL_W = 1024;\r\nexport const PANEL_H = 340;\r\n\r\nexport type VRRegion = 'play' | 'seek' | 'volume' | 'exit' | 'recenter';\r\n\r\nexport interface Rect { x: number; y: number; w: number; h: number; }\r\n\r\nexport interface PanelLayout {\r\n width: number; height: number;\r\n title: Rect;\r\n recenter: Rect;\r\n exit: Rect;\r\n play: Rect;\r\n volIcon: Rect;\r\n volBar: Rect;\r\n seekBar: Rect;\r\n timeCur: { x: number; y: number };\r\n timeDur: { x: number; y: number };\r\n}\r\n\r\n/** Where every control sits on the panel canvas. A single source of truth. */\r\nexport function panelLayout(): PanelLayout {\r\n const W = PANEL_W, H = PANEL_H, pad = 48;\r\n return {\r\n width: W, height: H,\r\n recenter: { x: 30, y: 26, w: 160, h: 52 },\r\n exit: { x: W - 190, y: 26, w: 160, h: 52 },\r\n title: { x: 0, y: 92, w: W, h: 44 },\r\n play: { x: W / 2 - 44, y: 150, w: 88, h: 88 },\r\n volIcon: { x: pad, y: 178, w: 44, h: 44 },\r\n volBar: { x: pad + 60, y: 192, w: 200, h: 16 },\r\n seekBar: { x: pad, y: 286, w: W - pad * 2, h: 14 },\r\n timeCur: { x: pad, y: 262 },\r\n timeDur: { x: W - pad, y: 262 },\r\n };\r\n}\r\n\r\nconst clamp01 = (v: number) => (v < 0 ? 0 : v > 1 ? 1 : v);\r\n\r\nfunction inRect(r: Rect, x: number, y: number, padX = 0, padY = 0): boolean {\r\n return x >= r.x - padX && x <= r.x + r.w + padX && y >= r.y - padY && y <= r.y + r.h + padY;\r\n}\r\n\r\nexport interface VRHit {\r\n region: VRRegion;\r\n /** For bars, the 0..1 fraction along the track. For the volume icon (mute\r\n * toggle) it is undefined — a bare `volume` hit means \"toggle mute\". */\r\n value?: number;\r\n}\r\n\r\n/**\r\n * Map a canvas-pixel point to the control it lands on, or null for empty space.\r\n * The thin bars get generous vertical padding so aiming with a jittery controller\r\n * ray is forgiving.\r\n */\r\nexport function hitTest(x: number, y: number, layout: PanelLayout = panelLayout()): VRHit | null {\r\n const BAR_PAD = 26;\r\n if (inRect(layout.recenter, x, y)) return { region: 'recenter' };\r\n if (inRect(layout.exit, x, y)) return { region: 'exit' };\r\n if (inRect(layout.play, x, y)) return { region: 'play' };\r\n if (inRect(layout.volIcon, x, y)) return { region: 'volume' }; // no value -> toggle mute\r\n if (inRect(layout.volBar, x, y, 12, BAR_PAD)) {\r\n return { region: 'volume', value: clamp01((x - layout.volBar.x) / layout.volBar.w) };\r\n }\r\n if (inRect(layout.seekBar, x, y, 12, BAR_PAD)) {\r\n return { region: 'seek', value: clamp01((x - layout.seekBar.x) / layout.seekBar.w) };\r\n }\r\n return null;\r\n}\r\n","import * as THREE from 'three';\r\nimport { formatTime } from '../ui/format.js';\r\nimport { PANEL_W, PANEL_H, panelLayout, hitTest, type PanelLayout, type VRRegion, type Rect } from './vr-panel-layout.js';\r\n\r\n/** The playback state + commands the panel needs. Kept small so the owner\r\n * (StereoScene) can wire it straight to the `<video>` element. */\r\nexport interface VRControlsActions {\r\n isPlaying(): boolean;\r\n currentTime(): number;\r\n duration(): number;\r\n volume(): number; // 0..1\r\n muted(): boolean;\r\n title(): string;\r\n togglePlay(): void;\r\n seekFraction(f: number): void;\r\n setVolume(v: number): void;\r\n toggleMute(): void;\r\n exitVR(): void;\r\n recenter(): void;\r\n}\r\n\r\nconst ACCENT = '#4f8cff';\r\nconst TEXT = '#e8eaed';\r\nconst MUTED_TEXT = '#aab2c0';\r\n// Quest Touch (xr-standard) face buttons: 4 = A/X (lower), 5 = B/Y (upper).\r\nconst TOGGLE_BUTTONS = [4, 5];\r\nconst IDLE_HIDE_MS = 8000;\r\n// Deliberate controller motion (per frame) that re-summons the panel; tunable.\r\nconst MOTION_ROT = 0.05; // radians\r\nconst MOTION_POS = 0.02; // metres\r\nconst REVEAL_COOLDOWN_MS = 1500; // don't re-summon on motion right after hiding\r\n\r\n/** A world-locked control panel for immersive VR, drawn to a canvas texture and\r\n * driven by the motion controllers' laser + trigger. Lives only while an XR\r\n * session is active; StereoScene builds it on sessionstart and disposes it on end. */\r\nexport class VRControls {\r\n private readonly renderer: THREE.WebGLRenderer;\r\n private readonly scene: THREE.Scene;\r\n private readonly actions: VRControlsActions;\r\n private readonly layout: PanelLayout = panelLayout();\r\n\r\n private readonly canvas: HTMLCanvasElement;\r\n private readonly ctx: CanvasRenderingContext2D;\r\n private readonly texture: THREE.CanvasTexture;\r\n private readonly panel: THREE.Mesh;\r\n private readonly cursor: THREE.Mesh; // white dot at the laser/panel intersection\r\n\r\n private readonly raycaster = new THREE.Raycaster();\r\n private readonly tmpMatrix = new THREE.Matrix4();\r\n private readonly tmpVec = new THREE.Vector3();\r\n private readonly tmpQuat = new THREE.Quaternion();\r\n private readonly prevQuat: THREE.Quaternion[] = []; // last frame's controller poses, for motion reveal\r\n private readonly prevPos: THREE.Vector3[] = [];\r\n private revealCooldownUntil = 0;\r\n private readonly controllers: THREE.Group[] = [];\r\n private readonly lasers: THREE.Line[] = [];\r\n private readonly connected: boolean[] = []; // per slot: is a real tracked-pointer bound?\r\n private readonly cleanups: (() => void)[] = [];\r\n\r\n private visible = false;\r\n private hover: VRRegion | null = null;\r\n private paintKey = '';\r\n private idleAt = 0;\r\n private togglePrev = false;\r\n private placeFrames = 0; // re-lock the panel to the head pose for a few frames after summon\r\n\r\n constructor(opts: { renderer: THREE.WebGLRenderer; scene: THREE.Scene; actions: VRControlsActions }) {\r\n this.renderer = opts.renderer;\r\n this.scene = opts.scene;\r\n this.actions = opts.actions;\r\n\r\n this.canvas = document.createElement('canvas');\r\n this.canvas.width = PANEL_W; this.canvas.height = PANEL_H;\r\n this.ctx = this.canvas.getContext('2d')!;\r\n\r\n this.texture = new THREE.CanvasTexture(this.canvas);\r\n this.texture.colorSpace = THREE.SRGBColorSpace;\r\n this.texture.minFilter = THREE.LinearFilter;\r\n\r\n const pw = 1.0, ph = (pw * PANEL_H) / PANEL_W; // ~3:1, ~35° wide at 1.6 m\r\n this.panel = new THREE.Mesh(\r\n new THREE.PlaneGeometry(pw, ph),\r\n new THREE.MeshBasicMaterial({ map: this.texture, transparent: true, depthTest: false, side: THREE.DoubleSide }),\r\n );\r\n this.panel.renderOrder = 10; // always drawn on top of the video sphere\r\n this.panel.frustumCulled = false;\r\n this.panel.visible = false;\r\n this.scene.add(this.panel);\r\n\r\n this.cursor = new THREE.Mesh(\r\n new THREE.SphereGeometry(0.007, 16, 12),\r\n new THREE.MeshBasicMaterial({ color: 0xffffff, depthTest: false }),\r\n );\r\n this.cursor.renderOrder = 11; // on top of the panel\r\n this.cursor.frustumCulled = false;\r\n this.cursor.visible = false;\r\n this.scene.add(this.cursor);\r\n\r\n // Seed connected state from the session's current input sources (the 'connected'\r\n // event may have already fired before this object existed), then keep it live\r\n // via connect/disconnect. Only a bound tracked-pointer gets a laser — otherwise\r\n // the empty controller slot sits at the origin and draws a fixed phantom line.\r\n const session = this.renderer.xr.getSession();\r\n const trackedCount = session ? Array.from(session.inputSources).filter((s) => s.targetRayMode === 'tracked-pointer').length : 0;\r\n for (let i = 0; i < 2; i++) {\r\n const c = this.renderer.xr.getController(i);\r\n const laser = this.makeLaser();\r\n laser.visible = false;\r\n c.add(laser);\r\n this.scene.add(c);\r\n this.controllers.push(c);\r\n this.lasers.push(laser);\r\n this.connected.push(i < trackedCount);\r\n\r\n const select = () => this.onSelect(i);\r\n const squeeze = () => this.toggle(); // grip summons/dismisses (+ recenters) the panel\r\n const connect = (e?: { data?: { targetRayMode?: string } }) => { this.connected[i] = !e?.data || e.data.targetRayMode === 'tracked-pointer'; };\r\n const disconnect = () => { this.connected[i] = false; };\r\n const ev = c as unknown as {\r\n addEventListener(t: string, l: (e?: { data?: { targetRayMode?: string } }) => void): void;\r\n removeEventListener(t: string, l: (e?: { data?: { targetRayMode?: string } }) => void): void;\r\n };\r\n ev.addEventListener('selectstart', select);\r\n ev.addEventListener('squeezestart', squeeze);\r\n ev.addEventListener('connected', connect);\r\n ev.addEventListener('disconnected', disconnect);\r\n this.cleanups.push(() => {\r\n ev.removeEventListener('selectstart', select);\r\n ev.removeEventListener('squeezestart', squeeze);\r\n ev.removeEventListener('connected', connect);\r\n ev.removeEventListener('disconnected', disconnect);\r\n });\r\n }\r\n\r\n this.paint(true);\r\n // Not shown on entry — summoned by the grip button or by moving a controller.\r\n }\r\n\r\n private makeLaser(): THREE.Line {\r\n const geo = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, -1)]);\r\n const mat = new THREE.LineBasicMaterial({ color: 0x4f8cff, transparent: true, opacity: 0.8 });\r\n return new THREE.Line(geo, mat);\r\n }\r\n\r\n /** Called once per frame from the render loop (before renderer.render). */\r\n update(time: number): void {\r\n if (!this.renderer.xr.getSession()) return;\r\n this.pollToggle();\r\n this.maybeRevealOnMotion(time);\r\n\r\n if (!this.visible) { for (const l of this.lasers) l.visible = false; this.cursor.visible = false; return; }\r\n\r\n // Re-lock to the head pose for a few frames after summon: the XR camera pose\r\n // isn't updated until renderer.render (which runs after this), so the pose read\r\n // on the very first frame is stale/identity. Settling over a few frames fixes it.\r\n if (this.placeFrames > 0) { this.place(); this.placeFrames--; }\r\n\r\n // Auto-hide after a spell of no aiming at the panel.\r\n if (this.idleAt && time > this.idleAt) { this.hide(); return; }\r\n\r\n // Raycast both controllers; the one pointing at the panel wins the hover.\r\n let hover: VRRegion | null = null;\r\n let cursorAt: THREE.Vector3 | null = null;\r\n for (let i = 0; i < this.controllers.length; i++) {\r\n const laser = this.lasers[i];\r\n const controller = this.controllers[i];\r\n // A controller slot with no bound input source is left at the world origin,\r\n // so its laser would draw as a fixed phantom line. Require both a live\r\n // connection and a real pose (off the origin) before drawing/raycasting it.\r\n const posed = this.tmpVec.setFromMatrixPosition(controller.matrixWorld).lengthSq() > 1e-6;\r\n if (!this.connected[i] || !posed) { laser.visible = false; continue; }\r\n const r = this.rayHit(controller);\r\n // Draw a laser only when it actually meets the panel. That's the aiming feedback\r\n // the user needs, and it means a controller that's set down (still a live input\r\n // source at its resting pose, but not aimed at the panel) never draws a phantom line.\r\n if (r.point) {\r\n laser.visible = true;\r\n laser.scale.z = r.distance;\r\n cursorAt = r.point; // laser is on the panel — show the dot there\r\n if (r.hit) hover = r.hit.region;\r\n } else {\r\n laser.visible = false;\r\n }\r\n }\r\n if (cursorAt) { this.cursor.position.copy(cursorAt); this.cursor.visible = true; }\r\n else this.cursor.visible = false;\r\n if (hover) this.idleAt = time + IDLE_HIDE_MS; // stay up while actively aimed at\r\n this.hover = hover;\r\n this.paint(false);\r\n }\r\n\r\n /** Ray from a controller against the panel: the region under it (if any) and\r\n * how far the laser reaches. */\r\n private rayHit(controller: THREE.Group): { hit: ReturnType<typeof hitTest>; distance: number; point: THREE.Vector3 | null } {\r\n this.tmpMatrix.identity().extractRotation(controller.matrixWorld);\r\n this.raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld);\r\n this.raycaster.ray.direction.set(0, 0, -1).applyMatrix4(this.tmpMatrix);\r\n const hits = this.raycaster.intersectObject(this.panel, false);\r\n if (!hits.length) return { hit: null, distance: 5, point: null };\r\n const h = hits[0];\r\n const hit = h.uv ? hitTest(h.uv.x * PANEL_W, (1 - h.uv.y) * PANEL_H, this.layout) : null;\r\n return { hit, distance: h.distance, point: h.point };\r\n }\r\n\r\n private onSelect(index: number): void {\r\n if (!this.visible || !this.connected[index]) return;\r\n const { hit } = this.rayHit(this.controllers[index]);\r\n if (!hit) return;\r\n this.idleAt = performanceNow() + IDLE_HIDE_MS;\r\n switch (hit.region) {\r\n case 'play': this.actions.togglePlay(); break;\r\n case 'exit': this.actions.exitVR(); break;\r\n case 'recenter': this.actions.recenter(); this.placeFrames = 12; break; // re-place the panel in the new frame\r\n\r\n case 'seek': if (hit.value !== undefined) this.actions.seekFraction(hit.value); break;\r\n case 'volume':\r\n if (hit.value !== undefined) this.actions.setVolume(hit.value);\r\n else this.actions.toggleMute();\r\n break;\r\n }\r\n this.paint(true);\r\n }\r\n\r\n /** Re-summon the panel when a connected controller is deliberately moved (and\r\n * we're past the post-hide cooldown). Tracks each controller's pose per frame. */\r\n private maybeRevealOnMotion(time: number): void {\r\n for (let i = 0; i < this.controllers.length; i++) {\r\n const c = this.controllers[i];\r\n const p = this.tmpVec.setFromMatrixPosition(c.matrixWorld);\r\n if (!this.connected[i] || p.lengthSq() <= 1e-6) continue; // skip unbound/unposed slots\r\n const q = this.tmpQuat.setFromRotationMatrix(c.matrixWorld);\r\n const prevQ = this.prevQuat[i], prevP = this.prevPos[i];\r\n if (prevQ && prevP && !this.visible && time > this.revealCooldownUntil\r\n && (q.angleTo(prevQ) > MOTION_ROT || p.distanceTo(prevP) > MOTION_POS)) {\r\n this.show();\r\n }\r\n if (!this.prevQuat[i]) this.prevQuat[i] = new THREE.Quaternion();\r\n if (!this.prevPos[i]) this.prevPos[i] = new THREE.Vector3();\r\n this.prevQuat[i].copy(q);\r\n this.prevPos[i].copy(p);\r\n }\r\n }\r\n\r\n private pollToggle(): void {\r\n const session = this.renderer.xr.getSession();\r\n let pressed = false;\r\n for (const src of session?.inputSources ?? []) {\r\n const g = src.gamepad;\r\n if (g && TOGGLE_BUTTONS.some((b) => g.buttons[b]?.pressed)) pressed = true;\r\n }\r\n if (pressed && !this.togglePrev) this.toggle();\r\n this.togglePrev = pressed;\r\n }\r\n\r\n private toggle(): void { this.visible ? this.hide() : this.show(); }\r\n\r\n private show(): void {\r\n this.place();\r\n this.placeFrames = 12; // settle onto the real head pose over the next frames\r\n this.visible = true;\r\n this.panel.visible = true;\r\n this.idleAt = performanceNow() + IDLE_HIDE_MS;\r\n this.paint(true);\r\n }\r\n\r\n private hide(): void {\r\n this.visible = false;\r\n this.panel.visible = false;\r\n this.cursor.visible = false;\r\n for (const l of this.lasers) l.visible = false;\r\n this.revealCooldownUntil = performanceNow() + REVEAL_COOLDOWN_MS;\r\n }\r\n\r\n /** World-lock the panel in front of the current head pose (the \"recenter\"). Uses\r\n * yaw only (so head pitch/roll at summon doesn't skew it), drops it into the lower\r\n * field of view, and tilts its face up toward the viewer. Re-run on every summon. */\r\n private place(): void {\r\n const cam = this.renderer.xr.getCamera();\r\n const pos = cam.getWorldPosition(new THREE.Vector3());\r\n const quat = cam.getWorldQuaternion(new THREE.Quaternion());\r\n const yaw = new THREE.Euler().setFromQuaternion(quat, 'YXZ').y;\r\n const yawQuat = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, yaw, 0, 'YXZ'));\r\n const forward = new THREE.Vector3(0, 0, -1).applyQuaternion(yawQuat);\r\n this.panel.position.copy(pos).addScaledVector(forward, 1.5);\r\n this.panel.position.y -= 0.55; // near the lower field of view\r\n this.panel.quaternion.copy(yawQuat); // plane front (+Z) faces back toward the viewer\r\n this.panel.rotateX(-0.3); // sitting low, angle the face up toward the eyes\r\n }\r\n\r\n /** Trim text with a trailing ellipsis to fit maxWidth. Assumes ctx.font is set. */\r\n private ellipsize(text: string, maxWidth: number): string {\r\n const c = this.ctx;\r\n if (c.measureText(text).width <= maxWidth) return text;\r\n let t = text;\r\n while (t.length > 1 && c.measureText(t + '…').width > maxWidth) t = t.slice(0, -1);\r\n return t.replace(/\\s+$/, '') + '…';\r\n }\r\n\r\n private roundRect(x: number, y: number, w: number, h: number, r: number): void {\r\n const c = this.ctx;\r\n c.beginPath();\r\n c.moveTo(x + r, y);\r\n c.arcTo(x + w, y, x + w, y + h, r);\r\n c.arcTo(x + w, y + h, x, y + h, r);\r\n c.arcTo(x, y + h, x, y, r);\r\n c.arcTo(x, y, x + w, y, r);\r\n c.closePath();\r\n }\r\n\r\n /** Repaint the panel only when something visible changed (state or hover). */\r\n private paint(force: boolean): void {\r\n const cur = this.actions.currentTime(), dur = this.actions.duration();\r\n const vol = this.actions.volume(), muted = this.actions.muted();\r\n const key = [this.actions.isPlaying(), Math.floor(cur), Math.floor(dur), vol.toFixed(2), muted, this.hover, this.actions.title()].join('|');\r\n if (!force && key === this.paintKey) return;\r\n this.paintKey = key;\r\n\r\n const c = this.ctx, L = this.layout;\r\n c.clearRect(0, 0, PANEL_W, PANEL_H);\r\n\r\n // Slab\r\n this.roundRect(0, 0, PANEL_W, PANEL_H, 28);\r\n c.fillStyle = 'rgba(22,24,30,0.84)'; c.fill();\r\n c.lineWidth = 2; c.strokeStyle = 'rgba(255,255,255,0.08)'; c.stroke();\r\n\r\n // Title, centered and truncated with an ellipsis so it never runs into the Exit button\r\n const title = this.actions.title();\r\n if (title) {\r\n c.font = '600 30px system-ui,\"Segoe UI\",Roboto,sans-serif';\r\n c.fillStyle = TEXT; c.textAlign = 'center'; c.textBaseline = 'middle';\r\n const maxW = 2 * L.exit.x - 48 - PANEL_W; // keep the centered title clear of the top-right pill\r\n c.fillText(this.ellipsize(title, maxW), PANEL_W / 2, L.title.y + L.title.h / 2);\r\n }\r\n\r\n // Top pills\r\n this.drawPill(L.recenter, 'Recenter', this.hover === 'recenter');\r\n this.drawPill(L.exit, 'Exit VR', this.hover === 'exit');\r\n\r\n // Play / pause\r\n const pc = { x: L.play.x + L.play.w / 2, y: L.play.y + L.play.h / 2 };\r\n c.beginPath(); c.arc(pc.x, pc.y, 44, 0, Math.PI * 2);\r\n c.fillStyle = this.hover === 'play' ? ACCENT : 'rgba(255,255,255,0.14)'; c.fill();\r\n c.fillStyle = '#fff';\r\n if (this.actions.isPlaying()) {\r\n c.fillRect(pc.x - 13, pc.y - 16, 9, 32);\r\n c.fillRect(pc.x + 4, pc.y - 16, 9, 32);\r\n } else {\r\n c.beginPath(); c.moveTo(pc.x - 12, pc.y - 17); c.lineTo(pc.x - 12, pc.y + 17); c.lineTo(pc.x + 18, pc.y); c.closePath(); c.fill();\r\n }\r\n\r\n // Volume: speaker + track\r\n this.drawSpeaker(L.volIcon, muted);\r\n const volFrac = muted ? 0 : Math.max(0, Math.min(1, vol));\r\n this.drawBar(L.volBar, volFrac, this.hover === 'volume');\r\n\r\n // Seek + times\r\n const seekFrac = dur > 0 ? Math.max(0, Math.min(1, cur / dur)) : 0;\r\n this.drawBar(L.seekBar, seekFrac, this.hover === 'seek', true);\r\n c.fillStyle = MUTED_TEXT; c.font = '22px system-ui,sans-serif'; c.textBaseline = 'alphabetic';\r\n c.textAlign = 'left'; c.fillText(formatTime(cur), L.timeCur.x, L.timeCur.y);\r\n c.textAlign = 'right'; c.fillText(dur > 0 ? formatTime(dur) : '--:--', L.timeDur.x, L.timeDur.y);\r\n\r\n this.texture.needsUpdate = true;\r\n }\r\n\r\n private drawPill(r: Rect, label: string, active: boolean): void {\r\n const c = this.ctx;\r\n this.roundRect(r.x, r.y, r.w, r.h, r.h / 2);\r\n c.fillStyle = active ? ACCENT : 'rgba(255,255,255,0.12)'; c.fill();\r\n c.fillStyle = active ? '#fff' : TEXT;\r\n c.font = '600 24px system-ui,sans-serif'; c.textAlign = 'center'; c.textBaseline = 'middle';\r\n c.fillText(label, r.x + r.w / 2, r.y + r.h / 2);\r\n }\r\n\r\n private drawBar(r: { x: number; y: number; w: number; h: number }, frac: number, hovered: boolean, knob = false): void {\r\n const c = this.ctx, rad = r.h / 2;\r\n this.roundRect(r.x, r.y, r.w, r.h, rad); c.fillStyle = 'rgba(255,255,255,0.22)'; c.fill();\r\n if (frac > 0) { this.roundRect(r.x, r.y, r.w * frac, r.h, rad); c.fillStyle = ACCENT; c.fill(); }\r\n if (knob || hovered) {\r\n c.beginPath(); c.arc(r.x + r.w * frac, r.y + r.h / 2, hovered ? 12 : 9, 0, Math.PI * 2);\r\n c.fillStyle = '#fff'; c.fill();\r\n }\r\n }\r\n\r\n private drawSpeaker(r: { x: number; y: number; w: number; h: number }, muted: boolean): void {\r\n const c = this.ctx, x = r.x, y = r.y + r.h / 2;\r\n c.fillStyle = muted ? MUTED_TEXT : TEXT;\r\n c.beginPath();\r\n c.moveTo(x, y - 7); c.lineTo(x + 10, y - 7); c.lineTo(x + 20, y - 16);\r\n c.lineTo(x + 20, y + 16); c.lineTo(x + 10, y + 7); c.lineTo(x, y + 7); c.closePath(); c.fill();\r\n c.strokeStyle = muted ? '#e06a6a' : TEXT; c.lineWidth = 3;\r\n if (muted) {\r\n c.beginPath(); c.moveTo(x + 26, y - 9); c.lineTo(x + 40, y + 9); c.moveTo(x + 40, y - 9); c.lineTo(x + 26, y + 9); c.stroke();\r\n } else {\r\n c.beginPath(); c.arc(x + 24, y, 8, -Math.PI / 3, Math.PI / 3); c.stroke();\r\n c.beginPath(); c.arc(x + 24, y, 15, -Math.PI / 3, Math.PI / 3); c.stroke();\r\n }\r\n }\r\n\r\n dispose(): void {\r\n for (const fn of this.cleanups) fn();\r\n for (let i = 0; i < this.controllers.length; i++) {\r\n const c = this.controllers[i];\r\n c.remove(this.lasers[i]);\r\n this.lasers[i].geometry.dispose();\r\n (this.lasers[i].material as THREE.Material).dispose();\r\n this.scene.remove(c);\r\n }\r\n this.scene.remove(this.panel);\r\n this.panel.geometry.dispose();\r\n (this.panel.material as THREE.Material).dispose();\r\n this.scene.remove(this.cursor);\r\n this.cursor.geometry.dispose();\r\n (this.cursor.material as THREE.Material).dispose();\r\n this.texture.dispose();\r\n }\r\n}\r\n\r\n/** performance.now(), but tolerant of environments without it (tests). */\r\nfunction performanceNow(): number {\r\n return typeof performance !== 'undefined' ? performance.now() : 0;\r\n}\r\n","import * as THREE from 'three';\r\nimport type { Projection } from '../types.js';\r\nimport { MODES } from './projections.js';\r\nimport { VRControls } from './VRControls.js';\r\n\r\n/**\r\n * Maps a video onto the geometry for a chosen projection (inside-out 180/360\r\n * sphere or a flat screen plane). Stereo is done the way the reference VR180\r\n * players do it: a single mesh, with the shared video texture shifted per eye\r\n * in onBeforeRender (no per-eye meshes or camera layers). Sizes to its canvas's\r\n * container (not the window), for embedding.\r\n */\r\nexport class StereoScene {\r\n readonly renderer: THREE.WebGLRenderer;\r\n readonly scene = new THREE.Scene();\r\n readonly camera: THREE.PerspectiveCamera;\r\n readonly maxAnisotropy: number;\r\n\r\n private readonly canvas: HTMLCanvasElement;\r\n private readonly video: HTMLVideoElement;\r\n private readonly texture: THREE.VideoTexture;\r\n private readonly meshes: THREE.Mesh[] = [];\r\n private readonly frameCbs: (() => void)[] = [];\r\n private vrControls?: VRControls;\r\n private readonly animate = (time?: number) => {\r\n for (const cb of this.frameCbs) cb();\r\n this.vrControls?.update(time ?? 0);\r\n // Upload the current video frame synchronously, before the render, every frame. This\r\n // is load-bearing on the Quest: without it, VideoTexture's own async (video-frame-\r\n // callback) upload can land mid-render during an XR eye pass and corrupt the triangles\r\n // being drawn — the \"hard triangle edges popping to black\". Reference VR180 players do\r\n // the same. (Yes, it re-uploads at headset rate; that's the cost of a stable image.)\r\n if (this.video.readyState >= this.video.HAVE_CURRENT_DATA) this.texture.needsUpdate = true;\r\n this.renderer.render(this.scene, this.camera);\r\n };\r\n private readonly ro: ResizeObserver;\r\n private currentMode: Projection;\r\n private currentSwap: boolean;\r\n // local-floor as OPTIONAL (widest device support — don't hard-fail headsets without it).\r\n // Deliberately NOT requesting 'layers': it flips three.js to an XRProjectionLayer depth\r\n // path that flickers on the Quest.\r\n private readonly vrSessionInit: XRSessionInit = { optionalFeatures: ['local-floor'] };\r\n\r\n constructor(opts: {\r\n canvas: HTMLCanvasElement; video: HTMLVideoElement;\r\n projection?: Projection; swapEyes?: boolean; fov?: number; supersampling?: number;\r\n }) {\r\n const { canvas, video, projection = '180-sbs', swapEyes = false, fov = 70, supersampling = 1.5 } = opts;\r\n this.canvas = canvas;\r\n this.video = video;\r\n this.currentMode = MODES[projection] ? projection : '180-sbs';\r\n this.currentSwap = swapEyes;\r\n\r\n this.renderer = new THREE.WebGLRenderer({ canvas, antialias: true });\r\n this.renderer.setPixelRatio(this.pixelRatioFor(supersampling));\r\n this.renderer.setSize(this.w(), this.h(), false);\r\n this.renderer.xr.enabled = true;\r\n\r\n this.camera = new THREE.PerspectiveCamera(fov, this.w() / this.h(), 0.1, 1000);\r\n this.camera.position.set(0, 0, 0);\r\n\r\n this.maxAnisotropy = this.renderer.capabilities.getMaxAnisotropy();\r\n this.texture = new THREE.VideoTexture(video);\r\n this.texture.colorSpace = THREE.SRGBColorSpace;\r\n this.texture.minFilter = THREE.LinearFilter;\r\n this.texture.magFilter = THREE.LinearFilter;\r\n // Keep anisotropy at the default (1). Cranking it to max makes the equirect video\r\n // sphere shimmer/flicker at grazing angles (the upper part of the view) once the head\r\n // moves in VR — the reference VR180 players leave it at the default. maxAnisotropy is\r\n // still exposed on the instance for consumers who want to opt back in.\r\n this.texture.anisotropy = 1;\r\n\r\n this.applyProjection(this.currentMode, this.currentSwap);\r\n video.addEventListener('loadedmetadata', () => {\r\n if (MODES[this.currentMode].flat) this.applyProjection(this.currentMode, this.currentSwap);\r\n });\r\n\r\n this.renderer.setAnimationLoop(this.animate);\r\n\r\n // In-VR controls live only for the duration of an immersive session: the DOM\r\n // control bar isn't visible inside the headset, so we draw a panel in the scene.\r\n this.renderer.xr.addEventListener('sessionstart', () => this.buildVRControls());\r\n this.renderer.xr.addEventListener('sessionend', () => {\r\n this.vrControls?.dispose(); this.vrControls = undefined;\r\n this.video.pause(); // stop playback when leaving VR\r\n });\r\n\r\n this.ro = new ResizeObserver(() => this.resize());\r\n this.ro.observe(canvas);\r\n }\r\n\r\n private buildVRControls() {\r\n const v = this.video;\r\n this.vrControls = new VRControls({\r\n renderer: this.renderer,\r\n scene: this.scene,\r\n actions: {\r\n isPlaying: () => !v.paused,\r\n currentTime: () => v.currentTime,\r\n duration: () => v.duration || 0,\r\n volume: () => v.volume,\r\n muted: () => v.muted,\r\n title: () => this.vrTitle,\r\n togglePlay: () => { if (v.paused) void v.play(); else v.pause(); },\r\n seekFraction: (f) => { if (v.duration) v.currentTime = f * v.duration; },\r\n setVolume: (x) => { v.volume = x; if (x > 0) v.muted = false; },\r\n toggleMute: () => { v.muted = !v.muted; },\r\n exitVR: () => this.exitVR(),\r\n recenter: () => this.recenter(),\r\n },\r\n });\r\n }\r\n\r\n /** Recenter the view: make the viewer's current spot and facing the origin, so the\r\n * video front is straight ahead again. Resets yaw and horizontal position; keeps\r\n * head height and a level horizon (no pitch/roll). */\r\n recenter(): void {\r\n const xr = this.renderer.xr;\r\n if (!xr.isPresenting) return;\r\n const baseRef = xr.getReferenceSpace();\r\n if (!baseRef) return;\r\n const cam = xr.getCamera();\r\n const pos = cam.getWorldPosition(new THREE.Vector3());\r\n const yaw = new THREE.Euler().setFromQuaternion(cam.getWorldQuaternion(new THREE.Quaternion()), 'YXZ').y;\r\n const q = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, yaw, 0, 'YXZ'));\r\n const offset = new XRRigidTransform({ x: pos.x, y: 0, z: pos.z }, { x: q.x, y: q.y, z: q.z, w: q.w });\r\n xr.setReferenceSpace(baseRef.getOffsetReferenceSpace(offset));\r\n }\r\n\r\n /** Optional title shown on the in-VR control panel. */\r\n setVRTitle(t: string) { this.vrTitle = t; }\r\n private vrTitle = '';\r\n\r\n private w() { return this.canvas.clientWidth || 1; }\r\n private h() { return this.canvas.clientHeight || 1; }\r\n private pixelRatioFor(ss: number) { return Math.min(window.devicePixelRatio * ss, 4); }\r\n\r\n private planeAspect(mode: Projection) {\r\n const vw = this.video.videoWidth, vh = this.video.videoHeight;\r\n if (!vw || !vh) return 16 / 9;\r\n return MODES[mode].aspect === 'per-eye' ? (vw / 2) / vh : vw / vh;\r\n }\r\n\r\n private buildGeometry(mode: Projection): THREE.BufferGeometry {\r\n // Match the reference VR180 geometry: 64×32 sphere, front gore at phiStart -π/2.\r\n const kind = MODES[mode].geom;\r\n if (kind === 'sphere180') { const g = new THREE.SphereGeometry(500, 64, 32, -Math.PI / 2, Math.PI, 0, Math.PI); g.scale(-1, 1, 1); return g; }\r\n if (kind === 'sphere360') { const g = new THREE.SphereGeometry(500, 64, 32); g.scale(-1, 1, 1); return g; }\r\n const h = 2.4, w = h * this.planeAspect(mode);\r\n const g = new THREE.PlaneGeometry(w, h); g.translate(0, 0, -2); return g;\r\n }\r\n\r\n /** Shift the shared video texture for the eye/half being drawn. Runs from the mesh's\r\n * onBeforeRender, so it's called once per eye per frame (the reference approach). */\r\n private updateStereoUV(cam: THREE.Camera) {\r\n const map = this.texture;\r\n const split = MODES[this.currentMode].split;\r\n // Robust eye detection (the reference player's cascade): direct camera-ref match, then\r\n // view-matrix X, then projection asymmetry — so a frame never gets the wrong half.\r\n let isLeft = true;\r\n if (this.renderer.xr.isPresenting) {\r\n const c = cam as THREE.PerspectiveCamera;\r\n const cams = (this.renderer.xr.getCamera() as unknown as { cameras?: THREE.PerspectiveCamera[] }).cameras;\r\n if (cams && cams.length >= 2) {\r\n if (c === cams[0]) isLeft = true;\r\n else if (c === cams[1]) isLeft = false;\r\n else isLeft = Math.abs(c.matrixWorldInverse.elements[12] - cams[0].matrixWorldInverse.elements[12])\r\n <= Math.abs(c.matrixWorldInverse.elements[12] - cams[1].matrixWorldInverse.elements[12]);\r\n } else {\r\n isLeft = c.projectionMatrix.elements[8] <= 0;\r\n }\r\n }\r\n const second = !isLeft !== this.currentSwap; // this eye shows the 2nd half (right / bottom)\r\n if (split === 'sbs') { map.repeat.set(0.5, 1); map.offset.set(second ? 0.5 : 0, 0); }\r\n else if (split === 'tb') { map.repeat.set(1, 0.5); map.offset.set(0, second ? 0 : 0.5); }\r\n else { map.repeat.set(1, 1); map.offset.set(0, 0); }\r\n }\r\n\r\n private clearMeshes() {\r\n for (const m of this.meshes) { this.scene.remove(m); m.geometry.dispose(); (m.material as THREE.Material).dispose(); }\r\n this.meshes.length = 0;\r\n }\r\n\r\n private applyProjection(mode: Projection, swap: boolean) {\r\n if (!MODES[mode]) mode = '180-sbs';\r\n this.clearMeshes();\r\n this.currentMode = mode; this.currentSwap = swap;\r\n // One mesh (reference approach), plain FrontSide MeshBasicMaterial. Stereo is handled\r\n // per eye by updateStereoUV via onBeforeRender — not by per-eye meshes or camera layers.\r\n const mesh = new THREE.Mesh(this.buildGeometry(mode), new THREE.MeshBasicMaterial({ map: this.texture }));\r\n mesh.layers.set(0); // desktop + both XR eyes see the single mesh\r\n if (MODES[mode].geom === 'sphere180') mesh.rotation.y = Math.PI / 2; // orient the 180 gore forward\r\n mesh.onBeforeRender = (_r, _s, cam) => this.updateStereoUV(cam);\r\n this.scene.add(mesh); this.meshes.push(mesh);\r\n }\r\n\r\n setProjection(p: Projection) { this.applyProjection(p, this.currentSwap); }\r\n setSwapEyes(v: boolean) { this.applyProjection(this.currentMode, v); }\r\n setFov(deg: number) { this.camera.fov = deg; this.camera.updateProjectionMatrix(); }\r\n setSupersampling(ss: number) { this.renderer.setPixelRatio(this.pixelRatioFor(ss)); this.renderer.setSize(this.w(), this.h(), false); }\r\n getProjection() { return this.currentMode; }\r\n isFlat() { return !!MODES[this.currentMode].flat; }\r\n onFrame(cb: () => void) { this.frameCbs.push(cb); }\r\n pauseRendering() { this.renderer.setAnimationLoop(null); }\r\n resumeRendering() { this.renderer.setAnimationLoop(this.animate); }\r\n\r\n /** Enter immersive VR. Rejects with a readable message on failure so the caller can\r\n * surface it — instead of the silent no-op three.js's VRButton produces. Called from\r\n * a click handler so the request keeps the user's transient activation. */\r\n async enterVR(): Promise<void> {\r\n if (!navigator.xr) throw new Error('WebXR is unavailable here — open the page over HTTPS or localhost.');\r\n const session = await navigator.xr.requestSession('immersive-vr', this.vrSessionInit);\r\n await this.renderer.xr.setSession(session);\r\n }\r\n exitVR(): void { void this.renderer.xr.getSession()?.end(); }\r\n /** Arm the browser/headset's own \"Enter VR\" affordance (e.g. the Quest system button). */\r\n offerVR(): void {\r\n const offer = (navigator.xr as { offerSession?: (m: XRSessionMode, i: XRSessionInit) => Promise<XRSession> } | undefined)?.offerSession;\r\n if (!offer) return;\r\n const run = () => offer.call(navigator.xr, 'immersive-vr', this.vrSessionInit)\r\n .then((s) => this.renderer.xr.setSession(s))\r\n .catch(() => { /* the UA declined the offer or the user dismissed it */ });\r\n this.renderer.xr.addEventListener('sessionend', () => void run());\r\n void run();\r\n }\r\n\r\n resize = () => {\r\n if (this.renderer.xr.isPresenting) return; // never resize while the headset drives the size\r\n const w = this.w(), h = this.h();\r\n this.camera.aspect = w / h;\r\n this.camera.updateProjectionMatrix();\r\n this.renderer.setSize(w, h, false);\r\n };\r\n\r\n dispose() {\r\n this.ro.disconnect();\r\n this.renderer.setAnimationLoop(null);\r\n this.vrControls?.dispose();\r\n this.texture.dispose();\r\n this.clearMeshes();\r\n this.renderer.dispose();\r\n }\r\n}\r\n","import type { Camera } from 'three';\r\n\r\nconst DEG = Math.PI / 180;\r\n\r\n/** Clamp look angles (degrees) to keep the view inside the front hemisphere. */\r\nexport function clampAngles(lon: number, lat: number): { lon: number; lat: number } {\r\n return {\r\n lon: Math.max(-90, Math.min(90, lon)),\r\n lat: Math.max(-85, Math.min(85, lat)),\r\n };\r\n}\r\n\r\n/**\r\n * Pointer/touch drag → camera yaw/pitch, clamped to the front hemisphere.\r\n * Disabled while an XR session drives the head pose, and for flat-screen modes.\r\n */\r\nexport class LookControls {\r\n private lon = 0;\r\n private lat = 0;\r\n private dragging = false;\r\n private px = 0;\r\n private py = 0;\r\n private enabled = true;\r\n private readonly isPresenting: () => boolean;\r\n\r\n constructor(\r\n private readonly camera: Camera,\r\n private readonly dom: HTMLElement,\r\n opts: { isPresenting?: () => boolean } = {},\r\n ) {\r\n this.isPresenting = opts.isPresenting ?? (() => false);\r\n this.dom.addEventListener('pointerdown', this.onDown);\r\n this.dom.addEventListener('pointermove', this.onMove);\r\n this.dom.addEventListener('pointerup', this.onUp);\r\n this.dom.addEventListener('pointercancel', this.onUp);\r\n }\r\n\r\n private capture(fn: 'setPointerCapture' | 'releasePointerCapture', id: number) {\r\n try { this.dom[fn]?.(id); } catch { /* pointer not active */ }\r\n }\r\n\r\n private onDown = (e: PointerEvent) => {\r\n if (!this.enabled) return;\r\n this.dragging = true;\r\n this.px = e.clientX;\r\n this.py = e.clientY;\r\n this.capture('setPointerCapture', e.pointerId);\r\n };\r\n\r\n private onMove = (e: PointerEvent) => {\r\n if (!this.dragging) return;\r\n const dx = e.clientX - this.px;\r\n const dy = e.clientY - this.py;\r\n this.px = e.clientX;\r\n this.py = e.clientY;\r\n ({ lon: this.lon, lat: this.lat } = clampAngles(this.lon - dx * 0.15, this.lat + dy * 0.15));\r\n };\r\n\r\n private onUp = (e: PointerEvent) => {\r\n this.dragging = false;\r\n this.capture('releasePointerCapture', e.pointerId);\r\n };\r\n\r\n update() {\r\n if (this.isPresenting() || !this.enabled) return;\r\n const phi = (90 - this.lat) * DEG;\r\n const theta = this.lon * DEG;\r\n this.camera.lookAt(\r\n Math.sin(phi) * Math.sin(theta),\r\n Math.cos(phi),\r\n -Math.sin(phi) * Math.cos(theta),\r\n );\r\n }\r\n\r\n reset() { this.lon = 0; this.lat = 0; }\r\n\r\n setEnabled(v: boolean) {\r\n this.enabled = v;\r\n if (!v) { this.lon = 0; this.lat = 0; this.camera.lookAt(0, 0, -1); }\r\n }\r\n\r\n getAngles() { return { lon: this.lon, lat: this.lat }; }\r\n\r\n dispose() {\r\n this.dom.removeEventListener('pointerdown', this.onDown);\r\n this.dom.removeEventListener('pointermove', this.onMove);\r\n this.dom.removeEventListener('pointerup', this.onUp);\r\n this.dom.removeEventListener('pointercancel', this.onUp);\r\n }\r\n}\r\n","import type Hls from 'hls.js';\r\nimport type { StreamFormat } from './proxy.js';\r\n\r\n/**\r\n * Attaches a source to a `<video>`. Progressive files go straight on `video.src`;\r\n * HLS/DASH-as-HLS go through hls.js — which is imported dynamically, so consumers\r\n * who never play HLS don't pay for it (and it's an optional dependency).\r\n */\r\nexport class VideoSource {\r\n private hls: Hls | null = null;\r\n\r\n async attach(\r\n video: HTMLVideoElement,\r\n source: { url: string; format: StreamFormat },\r\n opts: { crossOrigin?: string | null } = {},\r\n ): Promise<void> {\r\n this.dispose();\r\n const co = opts.crossOrigin === undefined ? 'anonymous' : opts.crossOrigin;\r\n if (co === null) video.removeAttribute('crossorigin');\r\n else video.crossOrigin = co;\r\n video.playsInline = true;\r\n\r\n const { url, format } = source;\r\n const isHlsLike = format === 'hls' || format === 'dash';\r\n const nativeHls = video.canPlayType('application/vnd.apple.mpegurl') !== '';\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n const cleanup = () => {\r\n video.removeEventListener('loadedmetadata', onLoaded);\r\n video.removeEventListener('error', onError);\r\n };\r\n const onLoaded = () => { cleanup(); resolve(); };\r\n const onError = () => {\r\n cleanup();\r\n const code = video.error ? video.error.code : 'unknown';\r\n reject(new Error(`Video failed to load (media error ${code}). Is the source reachable and CORS-clean?`));\r\n };\r\n video.addEventListener('loadedmetadata', onLoaded);\r\n video.addEventListener('error', onError);\r\n\r\n if (isHlsLike && !nativeHls) {\r\n import('hls.js').then(({ default: HlsCtor }) => {\r\n if (!HlsCtor.isSupported()) {\r\n cleanup();\r\n reject(new Error('HLS/DASH stream needs hls.js, which is unsupported in this browser.'));\r\n return;\r\n }\r\n this.hls = new HlsCtor({ enableWorker: true });\r\n this.hls.on(HlsCtor.Events.ERROR, (_e, data) => {\r\n if (data.fatal) { cleanup(); this.dispose(); reject(new Error(`HLS fatal error: ${data.type} / ${data.details}`)); }\r\n });\r\n this.hls.loadSource(url);\r\n this.hls.attachMedia(video);\r\n }).catch(() => {\r\n cleanup();\r\n reject(new Error('This looks like an HLS/DASH stream but hls.js could not be loaded. Install \"hls.js\".'));\r\n });\r\n } else {\r\n video.src = url;\r\n video.load();\r\n }\r\n });\r\n }\r\n\r\n dispose() {\r\n if (this.hls) { this.hls.destroy(); this.hls = null; }\r\n }\r\n}\r\n"],"names":["detectFormat","rawUrl","pathname","lower","ENDPOINTS","buildProxyUrl","proxy","format","base","params","name","value","MODES","PROJECTIONS","isFlatMode","p","_a","detectProjection","url","s","tb","sbs","formatTime","seconds","m","h","ss","PANEL_W","PANEL_H","panelLayout","W","H","pad","clamp01","v","inRect","r","x","y","padX","padY","hitTest","layout","ACCENT","TEXT","MUTED_TEXT","TOGGLE_BUTTONS","IDLE_HIDE_MS","MOTION_ROT","MOTION_POS","REVEAL_COOLDOWN_MS","VRControls","opts","THREE","pw","ph","session","trackedCount","i","c","laser","select","squeeze","connect","e","disconnect","ev","geo","mat","time","l","hover","cursorAt","controller","posed","hits","index","hit","performanceNow","q","prevQ","prevP","pressed","src","g","b","cam","pos","quat","yaw","yawQuat","forward","text","maxWidth","t","w","force","cur","dur","vol","muted","key","L","title","maxW","pc","volFrac","seekFrac","label","active","frac","hovered","knob","rad","fn","StereoScene","cb","canvas","video","projection","swapEyes","fov","supersampling","f","xr","baseRef","offset","mode","vw","vh","kind","map","split","isLeft","cams","second","swap","mesh","_r","_s","deg","offer","run","DEG","clampAngles","lon","lat","LookControls","camera","dom","dx","dy","id","_b","phi","theta","VideoSource","source","co","isHlsLike","nativeHls","resolve","reject","cleanup","onLoaded","onError","code","HlsCtor","_e","data"],"mappings":"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"}