mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-09-13 16:30:09 +00:00
fix(venue-crowd): per-video load tokens + unbind renderer on stop
Codex preflight round 2: (1) the global load token let a stinger cancel a committed loop load on the other layer — tokens are now per-element, and a stinger preempting an in-flight loop on its own layer requeues that loop for when the stinger ends; (2) setManifest(null)/deactivate left the last crowd frame bound and visible over the static plate — stop() now unbinds both layers from the renderer and zeroes the mix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
cab652d145
commit
5e5d892a63
@@ -104,9 +104,10 @@
|
|||||||
let _activeLayer = 0; // layer currently showing the loop
|
let _activeLayer = 0; // layer currently showing the loop
|
||||||
let _mix = 0; // 0 → layer0 visible, 1 → layer1 visible
|
let _mix = 0; // 0 → layer0 visible, 1 → layer1 visible
|
||||||
let _fadeRaf = 0;
|
let _fadeRaf = 0;
|
||||||
let _loadToken = 0; // invalidates in-flight canplaythrough waits
|
let _stopGen = 0; // bumped by stop(): invalidates ALL in-flight loads
|
||||||
let _boundToRenderer = false;
|
let _boundToRenderer = false;
|
||||||
let _pendingLoop = null; // transition committed while a stinger played
|
let _pendingLoop = null; // loop switch deferred by an active stinger
|
||||||
|
let _loadingLoop = null; // loop currently waiting on canplaythrough
|
||||||
let _stingerUntilEnded = false;
|
let _stingerUntilEnded = false;
|
||||||
let _lastStingerAt = -Infinity;
|
let _lastStingerAt = -Infinity;
|
||||||
let _prevStreak = 0;
|
let _prevStreak = 0;
|
||||||
@@ -199,12 +200,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load url into the video, resolve when it can play through (or after a
|
// Load url into the video, resolve when it can play through (or after a
|
||||||
// timeout — a stalled fetch must not wedge the crowd forever).
|
// timeout — a stalled fetch must not wedge the crowd forever). Tokens are
|
||||||
|
// per-element: a later load on the SAME video (a stinger preempting the
|
||||||
|
// idle layer) cancels this one, but loads on the other layer don't.
|
||||||
function loadAndPlay(video, url, loop, cb) {
|
function loadAndPlay(video, url, loop, cb) {
|
||||||
const token = ++_loadToken;
|
const token = (video._fbCrowdToken = (video._fbCrowdToken || 0) + 1);
|
||||||
|
const gen = _stopGen;
|
||||||
let settled = false;
|
let settled = false;
|
||||||
const settle = (ok) => {
|
const settle = (ok) => {
|
||||||
if (settled || token !== _loadToken) return;
|
if (settled || token !== video._fbCrowdToken || gen !== _stopGen) return;
|
||||||
settled = true;
|
settled = true;
|
||||||
video.removeEventListener('canplaythrough', onReady);
|
video.removeEventListener('canplaythrough', onReady);
|
||||||
video.removeEventListener('error', onError);
|
video.removeEventListener('error', onError);
|
||||||
@@ -227,7 +231,9 @@
|
|||||||
if (!_manifest || !_videos[0]) return;
|
if (!_manifest || !_videos[0]) return;
|
||||||
const layer = idleLayer();
|
const layer = idleLayer();
|
||||||
const video = _videos[layer];
|
const video = _videos[layer];
|
||||||
|
_loadingLoop = state;
|
||||||
loadAndPlay(video, _manifest.loops[state], true, (ok) => {
|
loadAndPlay(video, _manifest.loops[state], true, (ok) => {
|
||||||
|
if (_loadingLoop === state) _loadingLoop = null;
|
||||||
if (!ok || !_venueActive) return;
|
if (!ok || !_venueActive) return;
|
||||||
fadeMixTo(layer === 1 ? 1 : 0, fadeMs, () => {
|
fadeMixTo(layer === 1 ? 1 : 0, fadeMs, () => {
|
||||||
const old = _videos[_activeLayer];
|
const old = _videos[_activeLayer];
|
||||||
@@ -246,6 +252,13 @@
|
|||||||
_stingerUntilEnded = true;
|
_stingerUntilEnded = true;
|
||||||
const layer = idleLayer();
|
const layer = idleLayer();
|
||||||
const video = _videos[layer];
|
const video = _videos[layer];
|
||||||
|
// The stinger reuses the idle layer's element, cancelling any loop
|
||||||
|
// load still in flight there — requeue that loop for when the
|
||||||
|
// stinger ends (the machine already advanced, nothing re-fires it).
|
||||||
|
if (_loadingLoop) {
|
||||||
|
_pendingLoop = _loadingLoop;
|
||||||
|
_loadingLoop = null;
|
||||||
|
}
|
||||||
const back = () => {
|
const back = () => {
|
||||||
if (!_stingerUntilEnded) return;
|
if (!_stingerUntilEnded) return;
|
||||||
_stingerUntilEnded = false;
|
_stingerUntilEnded = false;
|
||||||
@@ -313,12 +326,24 @@
|
|||||||
|
|
||||||
function stop() {
|
function stop() {
|
||||||
cancelFade();
|
cancelFade();
|
||||||
_loadToken++;
|
_stopGen++;
|
||||||
_stingerUntilEnded = false;
|
_stingerUntilEnded = false;
|
||||||
_pendingLoop = null;
|
_pendingLoop = null;
|
||||||
|
_loadingLoop = null;
|
||||||
for (const v of _videos) {
|
for (const v of _videos) {
|
||||||
if (v && !v.paused) v.pause();
|
if (v && !v.paused) v.pause();
|
||||||
}
|
}
|
||||||
|
// Unbind from the renderer: a paused video still holds its last
|
||||||
|
// frame, and the venue style keeps a bound plane visible whenever
|
||||||
|
// videoWidth > 0 — without this a removed pack would leave a frozen
|
||||||
|
// crowd frame over the static plate. start() re-binds.
|
||||||
|
const setVideo = h3d('h3dVenueBackdropSetVideo');
|
||||||
|
if (_boundToRenderer && setVideo) {
|
||||||
|
setVideo(0, null);
|
||||||
|
setVideo(1, null);
|
||||||
|
}
|
||||||
|
_boundToRenderer = false;
|
||||||
|
setMix(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setVenueActive(on) {
|
function setVenueActive(on) {
|
||||||
|
|||||||
Reference in New Issue
Block a user