mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-05 00:56:23 +00:00
fix(video): hold final frame through composition
This commit is contained in:
@@ -389,7 +389,7 @@ describe("FrameLookupTable", () => {
|
||||
expect(table.getActiveFramePayloads(4.5).get("hero")?.frameIndex).toBe(15);
|
||||
});
|
||||
|
||||
it("does not hold stale frames for non-looping clips after extracted frames end", () => {
|
||||
it("holds the last frame for a non-looping clip until its authored slot ends", () => {
|
||||
const table = createFrameLookupTable(
|
||||
[
|
||||
{
|
||||
@@ -406,7 +406,31 @@ describe("FrameLookupTable", () => {
|
||||
);
|
||||
|
||||
expect(table.getActiveFramePayloads(0.5).has("hero")).toBe(true);
|
||||
expect(table.getActiveFramePayloads(1.5).has("hero")).toBe(false);
|
||||
expect(table.getActiveFramePayloads(1.5).get("hero")?.frameIndex).toBe(29);
|
||||
expect(table.getActiveFramePayloads(4.5).get("hero")?.frameIndex).toBe(29);
|
||||
expect(table.getFrame("hero", 4.5)).toBeTruthy();
|
||||
expect(table.getActiveFramePayloads(5.1).has("hero")).toBe(false);
|
||||
expect(table.getFrame("hero", 5.1)).toBeNull();
|
||||
});
|
||||
|
||||
it("does not invent a held frame when extraction produced no frames", () => {
|
||||
const table = createFrameLookupTable(
|
||||
[
|
||||
{
|
||||
id: "hero",
|
||||
src: "clip.webm",
|
||||
start: 0,
|
||||
end: 5,
|
||||
mediaStart: 0,
|
||||
loop: false,
|
||||
hasAudio: false,
|
||||
},
|
||||
],
|
||||
[fakeExtracted(0, 30)],
|
||||
);
|
||||
|
||||
expect(table.getActiveFramePayloads(4.5).has("hero")).toBe(false);
|
||||
expect(table.getFrame("hero", 4.5)).toBeNull();
|
||||
});
|
||||
|
||||
it("places a relative-reference video in its resolved window end-to-end (was blank)", () => {
|
||||
@@ -448,10 +472,9 @@ describe("FrameLookupTable", () => {
|
||||
expect(table.getActiveFramePayloads(2.5).get("hero")?.frameIndex).toBe(45);
|
||||
});
|
||||
|
||||
it("holds the last frame at the clip end even when the source is shorter than the window", () => {
|
||||
// clip [0,5] with only 1s of source (30 @ 30fps). The mid-clip tail stays
|
||||
// blank (source exhausted), but t === end still holds the last frame to
|
||||
// match the runtime's inclusive visibility.
|
||||
it("holds the last frame across the tail when the source is shorter than the window", () => {
|
||||
// clip [0,5] with only 1s of source (30 @ 30fps). The authored slot is the
|
||||
// visibility contract, so the final source frame fills its remaining tail.
|
||||
const table = createFrameLookupTable(
|
||||
[
|
||||
{
|
||||
@@ -466,7 +489,7 @@ describe("FrameLookupTable", () => {
|
||||
],
|
||||
[fakeExtracted(30, 30)],
|
||||
);
|
||||
expect(table.getActiveFramePayloads(1.5).has("hero")).toBe(false);
|
||||
expect(table.getActiveFramePayloads(1.5).get("hero")?.frameIndex).toBe(29);
|
||||
expect(table.getActiveFramePayloads(5.0).get("hero")?.frameIndex).toBe(29);
|
||||
});
|
||||
|
||||
@@ -1529,4 +1552,9 @@ describe("getFrameAtTime — IEEE 754 boundary precision", () => {
|
||||
const frame = getFrameAtTime(extracted, 0, 0, false, 1.0);
|
||||
expect(frame).toBe("frame-0.jpg");
|
||||
});
|
||||
|
||||
it("returns null after source exhaustion without an authored slot boundary", () => {
|
||||
const extracted = makeExtracted(25, 25);
|
||||
expect(getFrameAtTime(extracted, 3, 0)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user