Fix AAC error in Matroska muxer, remove special VideoFrame path for VideoSample due to Chromium bug

This commit is contained in:
Vanilagy
2026-06-30 16:04:30 +02:00
parent 40e35d6f14
commit bfbcea69b4
3 changed files with 51 additions and 55 deletions
+46 -19
View File
@@ -118,25 +118,52 @@ class ProresDecoder extends CustomVideoDecoder {
this.trimCodedHeightToVisibleHeight(result); this.trimCodedHeightToVisibleHeight(result);
} }
const sample = new VideoSample(result.frameData, { const colorSpaceInit = {
format: result.pixelFormat, primaries: result.colorPrimariesString as VideoColorPrimaries | undefined,
codedWidth: result.codedWidth, matrix: result.colorMatrixString as VideoMatrixCoefficients | undefined,
codedHeight: result.codedHeight, transfer: result.colorTransferString as VideoTransferCharacteristics | undefined,
visibleRect: { fullRange: result.colorRangeFull,
left: 0, };
top: 0,
width: result.visibleWidth, let sample: VideoSample;
height: result.visibleHeight, if (typeof VideoFrame !== 'undefined') {
}, // Create a VideoFrame directly; this avoids the frame data being copied twice
timestamp: packet.timestamp, const frame = new VideoFrame(result.frameData, {
duration: packet.duration, format: result.pixelFormat as VideoPixelFormat,
colorSpace: { codedWidth: result.codedWidth,
primaries: result.colorPrimariesString as VideoColorPrimaries | undefined, codedHeight: result.codedHeight,
matrix: result.colorMatrixString as VideoMatrixCoefficients | undefined, visibleRect: {
transfer: result.colorTransferString as VideoTransferCharacteristics | undefined, x: 0,
fullRange: result.colorRangeFull, y: 0,
}, width: result.visibleWidth,
}); height: result.visibleHeight,
},
timestamp: packet.microsecondTimestamp,
duration: packet.microsecondDuration,
colorSpace: colorSpaceInit,
});
sample = new VideoSample(frame, {
timestamp: packet.timestamp,
duration: packet.duration,
});
} else {
sample = new VideoSample(result.frameData, {
format: result.pixelFormat,
codedWidth: result.codedWidth,
codedHeight: result.codedHeight,
visibleRect: {
left: 0,
top: 0,
width: result.visibleWidth,
height: result.visibleHeight,
},
timestamp: packet.timestamp,
duration: packet.duration,
colorSpace: colorSpaceInit,
});
}
this.onSample(sample); this.onSample(sample);
} }
+1 -1
View File
@@ -844,7 +844,7 @@ export class MatroskaMuxer extends Muxer {
}, },
chunkQueue: [], chunkQueue: [],
lastWrittenMsTimestamp: null, lastWrittenMsTimestamp: null,
codecPrivate: meta.decoderConfig.description ?? null, codecPrivate: decoderConfig.description ?? null,
closed: false, closed: false,
}; };
+4 -35
View File
@@ -469,41 +469,10 @@ export class VideoSample implements Disposable {
this.squarePixelHeight = this.visibleRect.height; this.squarePixelHeight = this.visibleRect.height;
} }
// If VideoFrame is available, route through it directly instead of holding onto the buffer. Going // As an optimization, one could check if VideoFrame is defined and if it is, create a VideoFrame here from
// buffer -> VideoSample -> VideoFrame would copy the data twice (once here, once in toVideoFrame); // the data. Since VideoFrames are typically needed anyway, doing it this way would avoid an additional
// building the VideoFrame now means it's only ever copied once. // copy of the frame data. But due to https://issues.chromium.org/issues/529413114, this is currently
if (typeof VideoFrame !== 'undefined' && !init._doNotCopy) { // not done.
let videoFrame: VideoFrame | null = null;
try {
videoFrame = new VideoFrame(toUint8Array(data), {
format: init.format as VideoPixelFormat,
codedWidth: init.codedWidth!,
codedHeight: init.codedHeight!,
layout,
colorSpace: colorSpaceInit,
visibleRect: {
x: this.visibleRect.left,
y: this.visibleRect.top,
width: this.visibleRect.width,
height: this.visibleRect.height,
},
displayWidth: this.squarePixelWidth,
displayHeight: this.squarePixelHeight,
timestamp: Math.trunc(init.timestamp! * SECOND_TO_MICROSECOND_FACTOR),
// Drag 0 to undefined
duration: Math.trunc((init.duration ?? 0) * SECOND_TO_MICROSECOND_FACTOR) || undefined,
});
} catch {
// Ignore the error and move on like it didn't happen. We don't want errors caused by the local
// VideoFrame implementation to prevent us from creating the VideoSample. Errors, for example, could
// be caused by an unsupported pixel format.
}
if (videoFrame) {
return new VideoSample(videoFrame, init);
}
}
this._data = init._doNotCopy this._data = init._doNotCopy
? toUint8Array(data) ? toUint8Array(data)