Fix incorrect decoded audio sample timestamps in Firefox

This commit is contained in:
Vanilagy
2025-09-01 16:39:19 +02:00
parent 69e1383796
commit d2507a5a72
4 changed files with 25 additions and 10 deletions
+6 -6
View File
@@ -1,12 +1,12 @@
{ {
"name": "mediabunny", "name": "mediabunny",
"version": "1.12.0", "version": "1.12.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "mediabunny", "name": "mediabunny",
"version": "1.12.0", "version": "1.12.1",
"license": "MPL-2.0", "license": "MPL-2.0",
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
@@ -6147,9 +6147,9 @@
} }
}, },
"node_modules/mediabunny": { "node_modules/mediabunny": {
"version": "1.11.2", "version": "1.12.0",
"resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.11.2.tgz", "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.12.0.tgz",
"integrity": "sha512-dZpaq+YMKo5dUz6HlwWtZJfKPf3rZCq9BXurC7/zVmgjB3sTW5l32VT65gWm/ojv0vUwH+WbtolkmOQX1viz5g==", "integrity": "sha512-BARnydaYDY9iCar+ZRdnaKER31c2J4otsEVcSRsizXRrFLv97LXSd5ilD5FQF52AaM6FiqLm7iB3Zl1C+a5u9w==",
"license": "MPL-2.0", "license": "MPL-2.0",
"peer": true, "peer": true,
"workspaces": [ "workspaces": [
@@ -9478,7 +9478,7 @@
}, },
"packages/mp3-encoder": { "packages/mp3-encoder": {
"name": "@mediabunny/mp3-encoder", "name": "@mediabunny/mp3-encoder",
"version": "1.12.0", "version": "1.12.1",
"license": "MPL-2.0", "license": "MPL-2.0",
"devDependencies": { "devDependencies": {
"@types/emscripten": "^1.40.1" "@types/emscripten": "^1.40.1"
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "mediabunny", "name": "mediabunny",
"author": "Vanilagy", "author": "Vanilagy",
"version": "1.12.0", "version": "1.12.1",
"description": "Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.", "description": "Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.",
"type": "module", "type": "module",
"workspaces": [ "workspaces": [
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "@mediabunny/mp3-encoder", "name": "@mediabunny/mp3-encoder",
"author": "Vanilagy", "author": "Vanilagy",
"version": "1.12.0", "version": "1.12.1",
"description": "MP3 encoder extension for Mediabunny, based on LAME.", "description": "MP3 encoder extension for Mediabunny, based on LAME.",
"main": "./dist/bundles/mediabunny-mp3-encoder.mjs", "main": "./dist/bundles/mediabunny-mp3-encoder.mjs",
"module": "./dist/bundles/mediabunny-mp3-encoder.mjs", "module": "./dist/bundles/mediabunny-mp3-encoder.mjs",
+17 -2
View File
@@ -1217,6 +1217,10 @@ class AudioDecoderWrapper extends DecoderWrapper<AudioSample> {
customDecoderCallSerializer = new CallSerializer(); customDecoderCallSerializer = new CallSerializer();
customDecoderQueueSize = 0; customDecoderQueueSize = 0;
// Internal state to accumulate a precise current timestamp based on audio durations, not the (potentially
// inaccurate) packet timestamps.
currentTimestamp: number | null = null;
constructor( constructor(
onSample: (sample: AudioSample) => unknown, onSample: (sample: AudioSample) => unknown,
onError: (error: DOMException) => unknown, onError: (error: DOMException) => unknown,
@@ -1226,6 +1230,17 @@ class AudioDecoderWrapper extends DecoderWrapper<AudioSample> {
super(onSample, onError); super(onSample, onError);
const sampleHandler = (sample: AudioSample) => { const sampleHandler = (sample: AudioSample) => {
if (
this.currentTimestamp === null
|| Math.abs(sample.timestamp - this.currentTimestamp) >= sample.duration
) {
// We need to sync with the sample timestamp again
this.currentTimestamp = sample.timestamp;
}
const preciseTimestamp = this.currentTimestamp;
this.currentTimestamp += sample.duration;
if (sample.numberOfFrames === 0) { if (sample.numberOfFrames === 0) {
// We skip zero-data (empty) AudioSamples. These are sometimes emitted, for example, by Firefox when it // We skip zero-data (empty) AudioSamples. These are sometimes emitted, for example, by Firefox when it
// decodes Vorbis (at the start). // decodes Vorbis (at the start).
@@ -1235,7 +1250,7 @@ class AudioDecoderWrapper extends DecoderWrapper<AudioSample> {
// Round the timestamp to the sample rate // Round the timestamp to the sample rate
const sampleRate = decoderConfig.sampleRate; const sampleRate = decoderConfig.sampleRate;
sample.setTimestamp(Math.round(sample.timestamp * sampleRate) / sampleRate); sample.setTimestamp(Math.round(preciseTimestamp * sampleRate) / sampleRate);
onSample(sample); onSample(sample);
}; };
@@ -1320,7 +1335,7 @@ class PcmAudioDecoderWrapper extends DecoderWrapper<AudioSample> {
writeOutputValue: (view: DataView, byteOffset: number, value: number) => void; writeOutputValue: (view: DataView, byteOffset: number, value: number) => void;
// Internal state to accumulate a precise current timestamp based on audio durations, not the (potentially // Internal state to accumulate a precise current timestamp based on audio durations, not the (potentially
// inaccurate) sample timestamps. // inaccurate) packet timestamps.
currentTimestamp: number | null = null; currentTimestamp: number | null = null;
constructor( constructor(