From f8cd66727cda7c0e58289ca0eaf9158b32b0dbfa Mon Sep 17 00:00:00 2001 From: JonnyBurger Date: Sat, 18 Oct 2025 11:23:44 +0200 Subject: [PATCH 1/7] Make Mediabunny more resilient against `data` boxes ending abruptly --- src/isobmff/isobmff-reader.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/isobmff/isobmff-reader.ts b/src/isobmff/isobmff-reader.ts index f3090a1..ddb6f94 100644 --- a/src/isobmff/isobmff-reader.ts +++ b/src/isobmff/isobmff-reader.ts @@ -70,6 +70,10 @@ export const readDataBox = (slice: FileSlice) => { return null; } + if (slice.remainingLength < 4) { + return null; + } + const typeIndicator = readU32Be(slice); slice.skip(4); // Locale indicator const data = readBytes(slice, header.contentSize - 8); From cdeeb6c6fa639c47f8678b8b59359675076de62e Mon Sep 17 00:00:00 2001 From: JonnyBurger Date: Sat, 18 Oct 2025 11:25:51 +0200 Subject: [PATCH 2/7] Explain how to use examples dev server --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e754a7c..610962e 100644 --- a/README.md +++ b/README.md @@ -200,7 +200,7 @@ npm run lint # ESLint npm run docs:generate # Generates API docs npm run docs:dev # Start docs development server -npm run dev # Start examples development server - +npm run dev # Start examples development server, will run at http://localhost:5173/examples/[name]/index.html + npm run docs:build # Build docs and examples ``` \ No newline at end of file From 3767aea7d9ba087e2c45ec2387f3ccd13418436f Mon Sep 17 00:00:00 2001 From: JonnyBurger Date: Sat, 18 Oct 2025 17:24:00 +0200 Subject: [PATCH 3/7] Fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 610962e..0796dcc 100644 --- a/README.md +++ b/README.md @@ -200,7 +200,7 @@ npm run lint # ESLint npm run docs:generate # Generates API docs npm run docs:dev # Start docs development server -npm run dev # Start examples development server, will run at http://localhost:5173/examples/[name]/index.html +npm run dev # Start examples development server, will run at http://localhost:5173/examples/[name]/ npm run docs:build # Build docs and examples ``` \ No newline at end of file From e676de412920c192604d4d1462fc3f0c054317c5 Mon Sep 17 00:00:00 2001 From: Vanilagy <1696106+Vanilagy@users.noreply.github.com> Date: Tue, 21 Oct 2025 16:18:48 +0200 Subject: [PATCH 4/7] Terminate reading a cluster early if another cluster is encountered (fixes #164) --- src/matroska/matroska-demuxer.ts | 79 +++++++++++++++++--------------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/src/matroska/matroska-demuxer.ts b/src/matroska/matroska-demuxer.ts index bc1fdc2..c9655d5 100644 --- a/src/matroska/matroska-demuxer.ts +++ b/src/matroska/matroska-demuxer.ts @@ -637,7 +637,10 @@ export class MatroskaDemuxer extends Demuxer { this.currentCluster = cluster; if (dataSlice) { - this.readContiguousElements(dataSlice); + // Read the children of the cluster, stopping early at level 0 or 1 EBML elements. We do this because some + // clusters have incorrect sizes that are too large + const endPos = this.readContiguousElements(dataSlice, LEVEL_0_AND_1_EBML_IDS); + cluster.elementEndPos = endPos; } for (const [, trackData] of cluster.trackData) { @@ -913,24 +916,31 @@ export class MatroskaDemuxer extends Demuxer { } } - readContiguousElements(slice: FileSlice) { + readContiguousElements(slice: FileSlice, stopIds?: number[]) { const startIndex = slice.filePos; while (slice.filePos - startIndex <= slice.length - MIN_HEADER_SIZE) { - const foundElement = this.traverseElement(slice); + const startPos = slice.filePos; + const foundElement = this.traverseElement(slice, stopIds); if (!foundElement) { - break; + return startPos; } } + + return slice.filePos; } - traverseElement(slice: FileSlice): boolean { + traverseElement(slice: FileSlice, stopIds?: number[]): boolean { const header = readElementHeader(slice); if (!header) { return false; } + if (stopIds && stopIds.includes(header.id)) { + return false; + } + const { id, size } = header; const dataStartPos = slice.filePos; assertDefinedSize(size); @@ -2174,6 +2184,8 @@ abstract class MatroskaTrackBacking implements InputTrackBacking { if (id === EBMLId.Cluster) { currentCluster = await demuxer.readCluster(elementStartPos, segment); + // readCluster computes the proper size even if it's undefined in the header, so let's use that instead + size = currentCluster.elementEndPos - dataStartPos; const { blockIndex, correctBlockFound } = getMatchInCluster(currentCluster); if (correctBlockFound) { @@ -2190,44 +2202,37 @@ abstract class MatroskaTrackBacking implements InputTrackBacking { // Undefined element size (can happen in livestreamed files). In this case, we need to do some // searching to determine the actual size of the element. - if (id === EBMLId.Cluster) { - // The cluster should have already computed its length, we can just copy that result - assert(currentCluster); - size = currentCluster.elementEndPos - dataStartPos; - } else { - // Search for the next element at level 0 or 1 - const nextElementPos = await searchForNextElementId( - demuxer.reader, - dataStartPos, - LEVEL_0_AND_1_EBML_IDS, - segment.elementEndPos, - ); + assert(id !== EBMLId.Cluster); // Undefined cluster sizes are fixed further up - size = nextElementPos.pos - dataStartPos; - } + // Search for the next element at level 0 or 1 + const nextElementPos = await searchForNextElementId( + demuxer.reader, + dataStartPos, + LEVEL_0_AND_1_EBML_IDS, + segment.elementEndPos, + ); - const endPos = dataStartPos + size; - if (segment.elementEndPos !== null && endPos > segment.elementEndPos - MIN_HEADER_SIZE) { - // No more elements fit in this segment + size = nextElementPos.pos - dataStartPos; + } + + const endPos = dataStartPos + size; + if (segment.elementEndPos === null) { + // Check the next element. If it's a new segment, we know this segment ends here. The new + // segment is just ignored, since we're likely in a livestreamed file and thus only care about + // the first segment. + + let slice = demuxer.reader.requestSliceRange(endPos, MIN_HEADER_SIZE, MAX_HEADER_SIZE); + if (slice instanceof Promise) slice = await slice; + if (!slice) break; + + const elementId = readElementId(slice); + if (elementId === EBMLId.Segment) { + segment.elementEndPos = endPos; // We now know the segment's size break; - } else { - // Check the next element. If it's a new segment, we know this segment ends here. The new - // segment is just ignored, since we're likely in a livestreamed file and thus only care about - // the first segment. - - let slice = demuxer.reader.requestSliceRange(endPos, MIN_HEADER_SIZE, MAX_HEADER_SIZE); - if (slice instanceof Promise) slice = await slice; - if (!slice) break; - - const elementId = readElementId(slice); - if (elementId === EBMLId.Segment) { - segment.elementEndPos = endPos; - break; - } } } - currentPos = dataStartPos + size; + currentPos = endPos; } // Catch faulty cue points From 8629709cd7c745e41d387a89932833336fe58075 Mon Sep 17 00:00:00 2001 From: Vanilagy <1696106+Vanilagy@users.noreply.github.com> Date: Tue, 21 Oct 2025 16:27:04 +0200 Subject: [PATCH 5/7] < 8 --- src/isobmff/isobmff-reader.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/isobmff/isobmff-reader.ts b/src/isobmff/isobmff-reader.ts index ddb6f94..873ca83 100644 --- a/src/isobmff/isobmff-reader.ts +++ b/src/isobmff/isobmff-reader.ts @@ -70,7 +70,7 @@ export const readDataBox = (slice: FileSlice) => { return null; } - if (slice.remainingLength < 4) { + if (slice.remainingLength < 8) { return null; } From c7b45eabf03eff0aed535c79e9c3335060a88e8e Mon Sep 17 00:00:00 2001 From: Vanilagy <1696106+Vanilagy@users.noreply.github.com> Date: Tue, 21 Oct 2025 16:27:27 +0200 Subject: [PATCH 6/7] Explanatory comment --- src/isobmff/isobmff-reader.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/isobmff/isobmff-reader.ts b/src/isobmff/isobmff-reader.ts index 873ca83..67e0ab8 100644 --- a/src/isobmff/isobmff-reader.ts +++ b/src/isobmff/isobmff-reader.ts @@ -71,6 +71,7 @@ export const readDataBox = (slice: FileSlice) => { } if (slice.remainingLength < 8) { + // Box is too small return null; } From a14c0885d70bbe388eb5f65b77e057bcba7084a6 Mon Sep 17 00:00:00 2001 From: Vanilagy <1696106+Vanilagy@users.noreply.github.com> Date: Tue, 21 Oct 2025 16:28:03 +0200 Subject: [PATCH 7/7] Bump patch --- package-lock.json | 12 ++++++------ package.json | 2 +- packages/mp3-encoder/package.json | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9089b22..23c1888 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mediabunny", - "version": "1.24.1", + "version": "1.24.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mediabunny", - "version": "1.24.1", + "version": "1.24.2", "license": "MPL-2.0", "workspaces": [ "packages/*" @@ -7749,9 +7749,9 @@ } }, "node_modules/mediabunny": { - "version": "1.24.0", - "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.24.0.tgz", - "integrity": "sha512-9fQfAYlnuVSHMfbJtKK0pAgDlfwGDoyLQZ7gbOCug9jHPfjdi0XYQwvi/kaOFFMD70ouPkXzvKRc3AMPwkSK6Q==", + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.24.1.tgz", + "integrity": "sha512-k7ki4KGgFE7F4XaUn0sDpPzdB8Hgms5Hq3QonzXyVSMh75oRuyjNhuAWJtAAlzs45gUerlIHZCvHd6/5H7duow==", "license": "MPL-2.0", "peer": true, "workspaces": [ @@ -12242,7 +12242,7 @@ }, "packages/mp3-encoder": { "name": "@mediabunny/mp3-encoder", - "version": "1.24.1", + "version": "1.24.2", "license": "MPL-2.0", "devDependencies": { "@types/emscripten": "^1.40.1" diff --git a/package.json b/package.json index b34fc17..b6b5c5b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mediabunny", "author": "Vanilagy", - "version": "1.24.1", + "version": "1.24.2", "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 99cb907..12c2d76 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.24.1", + "version": "1.24.2", "description": "MP3 encoder extension for Mediabunny, based on LAME.", "main": "./dist/bundles/mediabunny-mp3-encoder.mjs", "module": "./dist/bundles/mediabunny-mp3-encoder.mjs",