1
0
This repository has been archived on 2026-08-25. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
tera/src/media/lifecycle.ts
T

63 lines
1.8 KiB
TypeScript

import type { VideoTexture } from "three";
import type { MediaAuthorizationDecision } from "./types.ts";
export interface MediaSurfaceBinding {
/** All objects are caller-owned. This adapter never obtains or fetches them. */
video: HTMLVideoElement;
texture: VideoTexture;
track?: MediaStreamTrack;
}
export interface MediaSurfaceLifecycle {
bind(binding: MediaSurfaceBinding): void;
apply(decision: MediaAuthorizationDecision): void;
texture(): VideoTexture | null;
optedIn(): boolean;
dispose(): void;
}
/**
* Gate an already-created video texture behind a server decision and explicit
* viewer consent. `play`, `getUserMedia`, `fetch`, `track.stop`, and
* `texture.dispose` are intentionally absent: acquisition and ownership remain
* with the caller.
*/
export function createMediaSurfaceLifecycle(): MediaSurfaceLifecycle {
let binding: MediaSurfaceBinding | null = null;
let allowed = false;
let consent = false;
let disposed = false;
function silence(video: HTMLVideoElement): void {
video.autoplay = false;
video.muted = true;
video.pause();
}
return {
bind(next) {
if (disposed) throw new Error("media surface lifecycle is disposed");
if (binding) silence(binding.video);
binding = next;
silence(next.video);
},
apply(decision) {
if (disposed) return;
allowed = decision.canView && decision.surface.source !== null;
consent = decision.optedIn;
if (!allowed && binding) silence(binding.video);
},
texture: () => (!disposed && allowed && consent ? binding?.texture ?? null : null),
optedIn: () => consent,
dispose() {
if (disposed) return;
disposed = true;
if (binding) silence(binding.video);
binding = null;
allowed = false;
consent = false;
},
};
}