fix: sanitize packet-scan range against NaN/Infinity duration

mediaInfo.duration from web-demuxer can be NaN or Infinity on Chromium
Linux (same MediaRecorder bug this PR otherwise addresses). That value
flowed straight into Math.max + demuxer.read() as scanEndSec, producing
an invalid range argument and breaking the ground-truth packet scan.

Guard both mediaInfo.duration and videoStream.duration with
Number.isFinite before Math.max; validateDuration() already handled the
downstream use.

Drop redundant WebDemuxer.read() / getDecoderConfig() type casts while
here — the generics infer the chunk/config type from the media string
literal, so the `as ReadableStream<EncodedVideoChunk>` and
`as AudioDecoderConfig` are no-ops.
This commit is contained in:
Enriquefft
2026-04-16 14:18:40 -05:00
parent 83ea025ed8
commit 61e895a75a
2 changed files with 14 additions and 10 deletions
+5 -6
View File
@@ -62,7 +62,7 @@ export class AudioProcessor {
): Promise<void> {
let audioConfig: AudioDecoderConfig;
try {
audioConfig = (await demuxer.getDecoderConfig("audio")) as AudioDecoderConfig;
audioConfig = await demuxer.getDecoderConfig("audio");
} catch {
console.warn("[AudioProcessor] No audio track found, skipping");
return;
@@ -87,11 +87,10 @@ export class AudioProcessor {
typeof readEndSec === "number" && Number.isFinite(readEndSec)
? Math.max(0, readEndSec)
: undefined;
const audioStream = (
const audioStream =
safeReadEndSec !== undefined
? demuxer.read("audio", 0, safeReadEndSec)
: demuxer.read("audio")
) as ReadableStream<EncodedAudioChunk>;
: demuxer.read("audio");
const reader = audioStream.getReader();
try {
@@ -396,8 +395,8 @@ export class AudioProcessor {
try {
await demuxer.load(file);
const audioConfig = (await demuxer.getDecoderConfig("audio")) as AudioDecoderConfig;
const reader = (demuxer.read("audio") as ReadableStream<EncodedAudioChunk>).getReader();
const audioConfig = await demuxer.getDecoderConfig("audio");
const reader = demuxer.read("audio").getReader();
let isFirstChunk = true;
try {
+9 -4
View File
@@ -231,11 +231,16 @@ export class StreamingVideoDecoder {
// MediaRecorder (especially on Linux) writes unreliable container durations.
// Packet timestamps are ground truth — no decode needed, just timestamp reads.
// Pass explicit range because some containers are truncated without one.
const scanEndSec = Math.max(mediaInfo.duration, videoStream?.duration ?? 0, 0) + 0.5;
// Sanitize because mediaInfo.duration can be NaN/Infinity (Chromium Linux bug),
// which would propagate into demuxer.read() as an invalid endpoint.
const containerDurationSec = Number.isFinite(mediaInfo.duration) ? mediaInfo.duration : 0;
const streamDurationSec =
typeof videoStream?.duration === "number" && Number.isFinite(videoStream.duration)
? videoStream.duration
: 0;
const scanEndSec = Math.max(containerDurationSec, streamDurationSec, 0) + 0.5;
let maxPacketEndUs = 0;
const scanReader = (
this.demuxer.read("video", 0, scanEndSec) as ReadableStream<EncodedVideoChunk>
).getReader();
const scanReader = this.demuxer.read("video", 0, scanEndSec).getReader();
try {
while (true) {
const { done, value } = await scanReader.read();