From 69e1383796507d684a08f6cf6aff320911a343b0 Mon Sep 17 00:00:00 2001 From: Vanilagy <1696106+Vanilagy@users.noreply.github.com> Date: Mon, 1 Sep 2025 16:11:42 +0200 Subject: [PATCH 1/2] Add thing --- docs/index.md | 1 + docs/public/sponsors/remotion.png | Bin 0 -> 9622 bytes 2 files changed, 1 insertion(+) create mode 100644 docs/public/sponsors/remotion.png diff --git a/docs/index.md b/docs/index.md index 6e2db99..f35a333 100644 --- a/docs/index.md +++ b/docs/index.md @@ -88,6 +88,7 @@ const bundleSizes = [ const sponsors = { gold: [ + { image: '/sponsors/remotion.png', name: 'Remotion', url: 'https://remotion.dev/' }, { image: '/sponsors/gling.svg', name: 'Gling AI', url: 'https://www.gling.ai/' }, { image: '/sponsors/diffusionstudio.png', name: 'Diffusion Studio', url: 'https://diffusion.studio/' }, { image: '/sponsors/kino.jpg', name: 'Kino', url: 'https://kino.ai/' }, diff --git a/docs/public/sponsors/remotion.png b/docs/public/sponsors/remotion.png new file mode 100644 index 0000000000000000000000000000000000000000..f76b0618f9d022027fe1de5b5834060c87fefd5d GIT binary patch literal 9622 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 Date: Mon, 1 Sep 2025 16:39:19 +0200 Subject: [PATCH 2/2] Fix incorrect decoded audio sample timestamps in Firefox --- package-lock.json | 12 ++++++------ package.json | 2 +- packages/mp3-encoder/package.json | 2 +- src/media-sink.ts | 19 +++++++++++++++++-- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index dbc4aae..bd1b3e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mediabunny", - "version": "1.12.0", + "version": "1.12.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mediabunny", - "version": "1.12.0", + "version": "1.12.1", "license": "MPL-2.0", "workspaces": [ "packages/*" @@ -6147,9 +6147,9 @@ } }, "node_modules/mediabunny": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.11.2.tgz", - "integrity": "sha512-dZpaq+YMKo5dUz6HlwWtZJfKPf3rZCq9BXurC7/zVmgjB3sTW5l32VT65gWm/ojv0vUwH+WbtolkmOQX1viz5g==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.12.0.tgz", + "integrity": "sha512-BARnydaYDY9iCar+ZRdnaKER31c2J4otsEVcSRsizXRrFLv97LXSd5ilD5FQF52AaM6FiqLm7iB3Zl1C+a5u9w==", "license": "MPL-2.0", "peer": true, "workspaces": [ @@ -9478,7 +9478,7 @@ }, "packages/mp3-encoder": { "name": "@mediabunny/mp3-encoder", - "version": "1.12.0", + "version": "1.12.1", "license": "MPL-2.0", "devDependencies": { "@types/emscripten": "^1.40.1" diff --git a/package.json b/package.json index 7b443cd..8e85294 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mediabunny", "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.", "type": "module", "workspaces": [ diff --git a/packages/mp3-encoder/package.json b/packages/mp3-encoder/package.json index b860afd..f1be561 100644 --- a/packages/mp3-encoder/package.json +++ b/packages/mp3-encoder/package.json @@ -1,7 +1,7 @@ { "name": "@mediabunny/mp3-encoder", "author": "Vanilagy", - "version": "1.12.0", + "version": "1.12.1", "description": "MP3 encoder extension for Mediabunny, based on LAME.", "main": "./dist/bundles/mediabunny-mp3-encoder.mjs", "module": "./dist/bundles/mediabunny-mp3-encoder.mjs", diff --git a/src/media-sink.ts b/src/media-sink.ts index 6506f4f..b419e46 100644 --- a/src/media-sink.ts +++ b/src/media-sink.ts @@ -1217,6 +1217,10 @@ class AudioDecoderWrapper extends DecoderWrapper { customDecoderCallSerializer = new CallSerializer(); 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( onSample: (sample: AudioSample) => unknown, onError: (error: DOMException) => unknown, @@ -1226,6 +1230,17 @@ class AudioDecoderWrapper extends DecoderWrapper { super(onSample, onError); 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) { // We skip zero-data (empty) AudioSamples. These are sometimes emitted, for example, by Firefox when it // decodes Vorbis (at the start). @@ -1235,7 +1250,7 @@ class AudioDecoderWrapper extends DecoderWrapper { // Round the timestamp to the sample rate const sampleRate = decoderConfig.sampleRate; - sample.setTimestamp(Math.round(sample.timestamp * sampleRate) / sampleRate); + sample.setTimestamp(Math.round(preciseTimestamp * sampleRate) / sampleRate); onSample(sample); }; @@ -1320,7 +1335,7 @@ class PcmAudioDecoderWrapper extends DecoderWrapper { writeOutputValue: (view: DataView, byteOffset: number, value: number) => void; // 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; constructor(