fix: include epsilon subtration in totalFrame calculation

This commit is contained in:
Theodor Peifer
2026-04-13 17:30:16 +02:00
parent d21dd1cbf1
commit 46c611bd3f
+12 -13
View File
@@ -2,7 +2,7 @@ import { WebDemuxer } from "web-demuxer";
import type { SpeedRegion, TrimRegion } from "@/components/video-editor/types";
const SOURCE_LOAD_TIMEOUT_MS = 60_000;
const EPSILON_SEC = 0.001;
/**
* Build a full WebCodecs-compatible AV1 codec string from the AV1CodecConfigurationRecord.
* web-demuxer may return a bare "av01" when the WASM-side parser fails to read
@@ -249,7 +249,6 @@ export class StreamingVideoDecoder {
Math.ceil(((segment.endSec - segment.startSec) / segment.speed) * targetFrameRate),
);
const frameDurationUs = 1_000_000 / targetFrameRate;
const epsilonSec = 0.001;
// Async frame queue — decoder pushes, consumer pulls
const pendingFrames: VideoFrame[] = [];
@@ -360,7 +359,7 @@ export class StreamingVideoDecoder {
const sourceTimeSec =
segment.startSec + (segmentFrameIndex / targetFrameRate) * segment.speed;
if (sourceTimeSec >= segment.endSec - epsilonSec) return false;
if (sourceTimeSec >= segment.endSec - EPSILON_SEC) return false;
const clone = new VideoFrame(heldFrame, { timestamp: heldFrame.timestamp });
await onFrame(clone, exportFrameIndex * frameDurationUs, sourceTimeSec * 1000);
@@ -379,7 +378,7 @@ export class StreamingVideoDecoder {
// Finalize completed segments before handling this frame.
while (
segmentIdx < segments.length &&
frameTimeSec >= segments[segmentIdx].endSec - epsilonSec
frameTimeSec >= segments[segmentIdx].endSec - EPSILON_SEC
) {
const segment = segments[segmentIdx];
while (!this.cancelled && (await emitHeldFrameForTarget(segment))) {
@@ -391,7 +390,7 @@ export class StreamingVideoDecoder {
if (
heldFrame &&
segmentIdx < segments.length &&
heldFrameSec < segments[segmentIdx].startSec - epsilonSec
heldFrameSec < segments[segmentIdx].startSec - EPSILON_SEC
) {
heldFrame.close();
heldFrame = null;
@@ -406,7 +405,7 @@ export class StreamingVideoDecoder {
const currentSegment = segments[segmentIdx];
// Before current segment (trimmed region or pre-roll).
if (frameTimeSec < currentSegment.startSec - epsilonSec) {
if (frameTimeSec < currentSegment.startSec - EPSILON_SEC) {
frame.close();
continue;
}
@@ -427,7 +426,7 @@ export class StreamingVideoDecoder {
const sourceTimeSec =
currentSegment.startSec + (segmentFrameIndex / targetFrameRate) * currentSegment.speed;
if (sourceTimeSec >= currentSegment.endSec - epsilonSec) {
if (sourceTimeSec >= currentSegment.endSec - EPSILON_SEC) {
break;
}
if (sourceTimeSec > handoffBoundarySec) {
@@ -449,7 +448,7 @@ export class StreamingVideoDecoder {
if (heldFrame && segmentIdx < segments.length) {
while (!this.cancelled && segmentIdx < segments.length) {
const segment = segments[segmentIdx];
if (heldFrameSec < segment.startSec - epsilonSec) {
if (heldFrameSec < segment.startSec - EPSILON_SEC) {
break;
}
@@ -461,7 +460,7 @@ export class StreamingVideoDecoder {
segmentFrameIndex = 0;
if (
segmentIdx < segments.length &&
heldFrameSec < segments[segmentIdx].startSec - epsilonSec
heldFrameSec < segments[segmentIdx].startSec - EPSILON_SEC
) {
break;
}
@@ -549,10 +548,10 @@ export class StreamingVideoDecoder {
(sum, seg) => sum + (seg.endSec - seg.startSec) / seg.speed,
0,
),
totalFrames: segments.reduce(
(sum, seg) => sum + Math.ceil(((seg.endSec - seg.startSec) / seg.speed) * targetFrameRate),
0,
),
totalFrames: segments.reduce((sum, seg) => {
const segDur = seg.endSec - seg.startSec - EPSILON_SEC;
return sum + Math.max(0, Math.ceil((segDur / seg.speed) * targetFrameRate));
}, 0),
};
}