This commit is contained in:
Siddharth
2026-03-17 20:07:15 -07:00
parent 1680ef9b77
commit 7e65d52847
3 changed files with 47 additions and 24 deletions
+15 -8
View File
@@ -6,22 +6,20 @@ describe("shouldFailDecodeEndedEarly", () => {
expect( expect(
shouldFailDecodeEndedEarly({ shouldFailDecodeEndedEarly({
cancelled: false, cancelled: false,
segmentsLength: 1,
completedSegments: 1,
lastDecodedFrameSec: 5.33, lastDecodedFrameSec: 5.33,
requiredEndSec: 6.498, requiredEndSec: 6.498,
streamDurationSec: 5.33,
}), }),
).toBe(false); ).toBe(false);
}); });
it("fails when decode stops before the remaining segments can be covered", () => { it("fails when decode stops far before the required end", () => {
expect( expect(
shouldFailDecodeEndedEarly({ shouldFailDecodeEndedEarly({
cancelled: false, cancelled: false,
segmentsLength: 2,
completedSegments: 1,
lastDecodedFrameSec: 5.33, lastDecodedFrameSec: 5.33,
requiredEndSec: 6.498, requiredEndSec: 10,
streamDurationSec: 5.33,
}), }),
).toBe(true); ).toBe(true);
}); });
@@ -30,11 +28,20 @@ describe("shouldFailDecodeEndedEarly", () => {
expect( expect(
shouldFailDecodeEndedEarly({ shouldFailDecodeEndedEarly({
cancelled: false, cancelled: false,
segmentsLength: 1,
completedSegments: 0,
lastDecodedFrameSec: null, lastDecodedFrameSec: null,
requiredEndSec: 1, requiredEndSec: 1,
}), }),
).toBe(true); ).toBe(true);
}); });
it("fails when the decoder has not reached the reported stream end", () => {
expect(
shouldFailDecodeEndedEarly({
cancelled: false,
lastDecodedFrameSec: 4.9,
requiredEndSec: 6.498,
streamDurationSec: 5.33,
}),
).toBe(true);
});
}); });
+30 -14
View File
@@ -14,26 +14,22 @@ export interface DecodedVideoInfo {
type EarlyDecodeEndCheck = { type EarlyDecodeEndCheck = {
cancelled: boolean; cancelled: boolean;
segmentsLength: number;
completedSegments: number;
lastDecodedFrameSec: number | null; lastDecodedFrameSec: number | null;
requiredEndSec: number; requiredEndSec: number;
streamDurationSec?: number;
}; };
const EARLY_DECODE_END_THRESHOLD_SEC = 1;
const METADATA_TAIL_TOLERANCE_SEC = 1.5;
const STREAM_DURATION_MATCH_TOLERANCE_SEC = 0.25;
export function shouldFailDecodeEndedEarly({ export function shouldFailDecodeEndedEarly({
cancelled, cancelled,
segmentsLength,
completedSegments,
lastDecodedFrameSec, lastDecodedFrameSec,
requiredEndSec, requiredEndSec,
streamDurationSec,
}: EarlyDecodeEndCheck): boolean { }: EarlyDecodeEndCheck): boolean {
if (cancelled || segmentsLength === 0) { if (cancelled || requiredEndSec <= 0) {
return false;
}
// If we already satisfied every segment, the exporter has successfully
// filled any short metadata tail using the last decoded frame.
if (completedSegments >= segmentsLength) {
return false; return false;
} }
@@ -41,7 +37,28 @@ export function shouldFailDecodeEndedEarly({
return true; return true;
} }
return requiredEndSec - lastDecodedFrameSec > 1; const decodeGapSec = requiredEndSec - lastDecodedFrameSec;
if (decodeGapSec <= EARLY_DECODE_END_THRESHOLD_SEC) {
return false;
}
if (typeof streamDurationSec !== "number" || !Number.isFinite(streamDurationSec)) {
return true;
}
const metadataTailSec = requiredEndSec - streamDurationSec;
const decodedNearStreamEnd =
Math.abs(lastDecodedFrameSec - streamDurationSec) <= STREAM_DURATION_MATCH_TOLERANCE_SEC;
if (
decodedNearStreamEnd &&
metadataTailSec > 0 &&
metadataTailSec <= METADATA_TAIL_TOLERANCE_SEC
) {
return false;
}
return true;
} }
/** Caller must close the VideoFrame after use. */ /** Caller must close the VideoFrame after use. */
@@ -400,10 +417,9 @@ export class StreamingVideoDecoder {
if ( if (
shouldFailDecodeEndedEarly({ shouldFailDecodeEndedEarly({
cancelled: this.cancelled, cancelled: this.cancelled,
segmentsLength: segments.length,
completedSegments: segmentIdx,
lastDecodedFrameSec, lastDecodedFrameSec,
requiredEndSec, requiredEndSec,
streamDurationSec: this.metadata.streamDuration,
}) })
) { ) {
const decodedAtLabel = const decodedAtLabel =
+2 -2
View File
@@ -87,8 +87,8 @@ test("exports a GIF from a loaded video", async () => {
await editorWindow.getByTestId("testId-gif-format-button").click(); await editorWindow.getByTestId("testId-gif-format-button").click();
await editorWindow.getByTestId("testId-export-button").click(); await editorWindow.getByTestId("testId-export-button").click();
// ── 6. Wait for the toast to say exported successfully // ── 6. Wait for the success toast.
await expect(editorWindow.getByText(`GIF exported successfully to pending`)).toBeVisible({ await expect(editorWindow.getByText("GIF exported successfully")).toBeVisible({
timeout: 90_000, timeout: 90_000,
}); });