Skip to content

Commit 3410fdd

Browse files
committed
fix(producer): wire ffprobe frame count into the artifact duration probe
The frame-count gate added in #3395 accepts an expectedFrames value from the orchestrator, but defaultArtifactDurationProbe was still returning only durationSeconds - so the wire was half-built and the assertion short-circuited on undefined for every real render. Forward meta.frames from ffprobe so the field-packet case the issue names (container duration correct, stream shorter) is actually caught by the frame-count check, not just the duration one. extractMediaMetadata now populates a new frames field from the video stream's nb_frames tag, returning undefined when the demuxer did not report one (fragmented MP4, malformed streams, muxes that require -count_packets). Callers that gate on the count must treat undefined as no answer; the assertion already does. The previous CI run (#32589981916) cancelled shard-6 at the 1h job timeout after bun install failed to extract the aws-cdk-lib tarball mid-Docker-build - a cache flake, not a code regression. Pushing a follow-up commit retriggers CI against the now-populated cache layer; the regression should clear without further code changes.
1 parent 42a055a commit 3410fdd

2 files changed

Lines changed: 26 additions & 1 deletion

File tree

packages/engine/src/utils/ffprobe.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ export interface VideoMetadata {
188188
hasAlpha: boolean;
189189
/** Color space info from the video stream. Null if ffprobe didn't report it. */
190190
colorSpace: VideoColorSpace | null;
191+
/** Decoded frame count from the video stream's `nb_frames`. Omitted when the
192+
* container does not surface a reliable count (still images, malformed
193+
* streams, or muxes that require `-count_packets` to populate). Callers
194+
* that gate on a frame count must treat `undefined` as "no answer". */
195+
frames?: number;
191196
}
192197

193198
export interface AudioMetadata {
@@ -731,6 +736,7 @@ export async function extractMediaMetadata(filePath: string): Promise<VideoMetad
731736
isVFR: false,
732737
hasAlpha: false,
733738
colorSpace: stillImageMeta.colorSpace,
739+
frames: 1,
734740
};
735741
}
736742
throw new Error("[FFmpeg] No video stream found");
@@ -775,6 +781,14 @@ export async function extractMediaMetadata(filePath: string): Promise<VideoMetad
775781
const streamDuration = videoStream.duration ? parseFloat(videoStream.duration) : 0;
776782
const parsedStreamStart = videoStream.start_time ? parseFloat(videoStream.start_time) : 0;
777783
const streamStart = Number.isFinite(parsedStreamStart) ? parsedStreamStart : 0;
784+
// `nb_frames` is populated by the container demuxer; a muxer that requires
785+
// `-count_packets` to enumerate frames will leave it undefined, in which
786+
// case the caller must treat the frame-count check as "no answer". This
787+
// path is hit by, for example, fragmented MP4 where the moov box lacks a
788+
// frame count — the captured duration is still correct.
789+
const parsedNbFrames = videoStream.nb_frames ? parseInt(videoStream.nb_frames, 10) : NaN;
790+
const frames =
791+
Number.isFinite(parsedNbFrames) && parsedNbFrames > 0 ? parsedNbFrames : undefined;
778792

779793
return {
780794
durationSeconds: containerDuration,
@@ -788,6 +802,7 @@ export async function extractMediaMetadata(filePath: string): Promise<VideoMetad
788802
isVFR,
789803
hasAlpha,
790804
colorSpace,
805+
frames,
791806
};
792807
})();
793808

packages/producer/src/services/render/artifactTransaction.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,17 @@ export type ArtifactDurationProbe = (path: string) => Promise<ArtifactDurationPr
5656

5757
async function defaultArtifactDurationProbe(path: string): Promise<ArtifactDurationProbeResult> {
5858
const meta = await extractMediaMetadata(path);
59-
return { durationSeconds: meta.durationSeconds };
59+
// Forward the probed frame count when ffprobe reported one. The frame
60+
// count check (#3395) catches the multi-worker encode mode where the
61+
// container duration is reported correctly but the decoded stream is
62+
// shorter; without forwarding frames here, the caller's `expectedFrames`
63+
// is silently dropped (the assertion short-circuits on `undefined`). A
64+
// probe that cannot determine frames returns `undefined` — the caller
65+
// treats that as "no answer" and does not throw.
66+
return {
67+
durationSeconds: meta.durationSeconds,
68+
frames: meta.frames,
69+
};
6070
}
6171

6272
/**

0 commit comments

Comments
 (0)