diff --git a/README.md b/README.md index e754a7c..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 - +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 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", diff --git a/src/isobmff/isobmff-reader.ts b/src/isobmff/isobmff-reader.ts index f3090a1..67e0ab8 100644 --- a/src/isobmff/isobmff-reader.ts +++ b/src/isobmff/isobmff-reader.ts @@ -70,6 +70,11 @@ export const readDataBox = (slice: FileSlice) => { return null; } + if (slice.remainingLength < 8) { + // Box is too small + return null; + } + const typeIndicator = readU32Be(slice); slice.skip(4); // Locale indicator const data = readBytes(slice, header.contentSize - 8); 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