fix(lab): stream synchronized replay fragments

This commit is contained in:
DCCONSTRUCTIONS
2026-08-23 23:36:04 +03:00
parent a55ef475d9
commit a7caed70c1
9 changed files with 443 additions and 34 deletions
@@ -422,6 +422,10 @@ test("M4.6 viewer keeps media and spatial panes on one playback clock", async ()
assert.match(visual, /separatorLabel="Изменить размер VIDEO\/CAMERA и 3D\/PLAN"/);
assert.match(visual, /secondaryMode=\{\{/);
assert.match(visual, /playback=\{playbackController\.playback\}/);
assert.match(visual, /clock: mediaMode === "video" \? "external" : "animation"/);
assert.match(visual, /segmentSequence=\{frame \? frame\.sequence \+ 1 : undefined\}/);
assert.match(visual, /segmentCount=\{timeline\.frameCount\}/);
assert.match(visual, /onPlaybackChange=\{playbackController\.synchronize\}/);
assert.match(visual, /currentSeconds: playbackController\.playback\.currentSeconds/);
assert.doesNotMatch(visual, /setPlaying\(false\)/);
assert.match(visualCss, /m4-replay-threat-visual__deck > \.nodedc-split-pane/);
@@ -430,6 +434,8 @@ test("M4.6 viewer keeps media and spatial panes on one playback clock", async ()
assert.match(visualCss, /width: 33\.333333%/);
assert.match(visualCss, /flex-flow: column nowrap/);
assert.match(visualCss, /m4-replay-threat-visual__overlay > div/);
assert.match(visualCss, /grid-template-columns: minmax\(0, 1fr\)/);
assert.match(visualCss, /width: min\(22rem, calc\(33\.333333% - 0\.8rem\)\)/);
assert.match(visualCss, /laboratory-metric-evidence-scene__legend/);
assert.match(visualCss, /bottom: auto/);
assert.match(videoScene, /<RecordedFmp4Player/);
@@ -8,6 +8,7 @@ let server;
let fetchRecordedMediaArchive;
let recordedMediaPresentationState;
let recordedMediaSeekableCoverage;
let recordedMediaFragmentUrl;
before(async () => {
server = await createServer({
@@ -19,6 +20,7 @@ before(async () => {
fetchRecordedMediaArchive,
recordedMediaPresentationState,
recordedMediaSeekableCoverage,
recordedMediaFragmentUrl,
} = await server.ssrLoadModule("/src/components/RecordedFmp4Player.tsx"));
});
@@ -144,13 +146,38 @@ test("decoded duration and seekable range cover the complete declared epoch", ()
assert.equal(recordedMediaSeekableCoverage(20, 20, 20, 1, 1.01), false);
});
test("recorded player range-streams and never builds a whole-video RAM Blob", async () => {
test("recorded player keeps full-archive range fallback and uses bounded generation-bound fragments", async () => {
const source = await readFile(
new URL("../src/components/RecordedFmp4Player.tsx", import.meta.url),
"utf8",
);
assert.match(source, /video\.src\s*=\s*descriptor\.streamUrl/);
assert.doesNotMatch(source, /new Blob\(|response\.arrayBuffer\(|SourceBuffer/);
assert.doesNotMatch(source, /new Blob\(/);
assert.match(source, /new MediaSource\(\)/);
assert.match(source, /segmentSequence \+ 36/);
assert.match(source, /pumpRecordedSegmentWindow/);
const { manifest, generation } = fixture();
const epoch = {
ordinal: 1,
timelineStartSeconds: 0,
timelineEndSeconds: 36_000,
mediaType: manifest.epochs[0].media_type,
byteLength: manifest.epochs[0].byte_length,
streamUrl: manifest.epochs[0].stream_url,
};
assert.equal(
recordedMediaFragmentUrl(epoch, generation, "init"),
`/api/v1/observation-sessions/session-1/media/camera-1/epochs/1/init.mp4?generation=${generation}`,
);
assert.equal(
recordedMediaFragmentUrl(epoch, generation, 17),
`/api/v1/observation-sessions/session-1/media/camera-1/epochs/1/segments/17.m4s?generation=${generation}`,
);
assert.throws(
() => recordedMediaFragmentUrl(epoch, "b".repeat(64), 17),
/не привязан/,
);
});
test("loading and error overlays fully conceal recorded camera pixels", async () => {