Merge main into release for tag v1.7.4

This commit is contained in:
github-actions[bot]
2025-08-13 07:59:44 +00:00
6 changed files with 87 additions and 24 deletions
+6 -6
View File
@@ -1,12 +1,12 @@
{ {
"name": "mediabunny", "name": "mediabunny",
"version": "1.7.3", "version": "1.7.4",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "mediabunny", "name": "mediabunny",
"version": "1.7.3", "version": "1.7.4",
"license": "MPL-2.0", "license": "MPL-2.0",
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
@@ -5900,9 +5900,9 @@
} }
}, },
"node_modules/mediabunny": { "node_modules/mediabunny": {
"version": "1.7.2", "version": "1.7.3",
"resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.7.2.tgz", "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.7.3.tgz",
"integrity": "sha512-M84Ice7IR1OpWmmDoUh691Hjpf+w2UTt+VWUH0WkiF5MdfFZ63OAF2TmOhbAEVZyvQ088xlvmaT01t00Qgy/QA==", "integrity": "sha512-wh0bTZ0bLKIdkn0gMMF/uxEVh85Ft/qH1OtUKJO8k5tdqP0RG7rMfoXLZI6r+wZ9VadfQ4xxYZTEallAWZ74jw==",
"license": "MPL-2.0", "license": "MPL-2.0",
"peer": true, "peer": true,
"workspaces": [ "workspaces": [
@@ -9017,7 +9017,7 @@
}, },
"packages/mp3-encoder": { "packages/mp3-encoder": {
"name": "@mediabunny/mp3-encoder", "name": "@mediabunny/mp3-encoder",
"version": "1.7.3", "version": "1.7.4",
"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.7.3", "version": "1.7.4",
"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.7.3", "version": "1.7.4",
"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",
+16 -3
View File
@@ -10,7 +10,7 @@ import { Demuxer } from './demuxer';
import { Input } from './input'; import { Input } from './input';
import { IsobmffDemuxer } from './isobmff/isobmff-demuxer'; import { IsobmffDemuxer } from './isobmff/isobmff-demuxer';
import { IsobmffReader } from './isobmff/isobmff-reader'; import { IsobmffReader } from './isobmff/isobmff-reader';
import { EBMLId, EBMLReader } from './matroska/ebml'; import { EBMLId, EBMLReader, MIN_HEADER_SIZE } from './matroska/ebml';
import { MatroskaDemuxer } from './matroska/matroska-demuxer'; import { MatroskaDemuxer } from './matroska/matroska-demuxer';
import { Mp3Demuxer } from './mp3/mp3-demuxer'; import { Mp3Demuxer } from './mp3/mp3-demuxer';
import { FRAME_HEADER_SIZE } from '../shared/mp3-misc'; import { FRAME_HEADER_SIZE } from '../shared/mp3-misc';
@@ -106,6 +106,10 @@ export class QuickTimeInputFormat extends IsobmffInputFormat {
} }
} }
function foo() {
return 5;
}
/** /**
* Matroska file format. * Matroska file format.
* @public * @public
@@ -120,6 +124,12 @@ export class MatroskaInputFormat extends InputFormat {
const ebmlReader = new EBMLReader(input._mainReader); const ebmlReader = new EBMLReader(input._mainReader);
const varIntSize = ebmlReader.readVarIntSize(); const varIntSize = ebmlReader.readVarIntSize();
if (varIntSize === null) {
return false;
}
foo();
if (varIntSize < 1 || varIntSize > 8) { if (varIntSize < 1 || varIntSize > 8) {
return false; return false;
} }
@@ -135,8 +145,11 @@ export class MatroskaInputFormat extends InputFormat {
} }
const startPos = ebmlReader.pos; const startPos = ebmlReader.pos;
while (ebmlReader.pos < startPos + dataSize) { while (ebmlReader.pos <= startPos + dataSize - MIN_HEADER_SIZE) {
const { id, size } = ebmlReader.readElementHeader(); const header = ebmlReader.readElementHeader();
if (!header) break;
const { id, size } = header;
const dataStartPos = ebmlReader.pos; const dataStartPos = ebmlReader.pos;
if (size === null) return false; if (size === null) return false;
+25 -6
View File
@@ -379,7 +379,7 @@ export class EBMLWriter {
const MAX_VAR_INT_SIZE = 8; const MAX_VAR_INT_SIZE = 8;
export const MIN_HEADER_SIZE = 2; // 1-byte ID and 1-byte size export const MIN_HEADER_SIZE = 2; // 1-byte ID and 1-byte size
export const MAX_HEADER_SIZE = 4 + MAX_VAR_INT_SIZE; // 4-byte ID and 8-byte size export const MAX_HEADER_SIZE = 2 * MAX_VAR_INT_SIZE; // 8-byte ID and 8-byte size
export class EBMLReader { export class EBMLReader {
pos = 0; pos = 0;
@@ -411,9 +411,13 @@ export class EBMLReader {
const { view, offset } = this.reader.getViewAndOffset(this.pos, this.pos + 1); const { view, offset } = this.reader.getViewAndOffset(this.pos, this.pos + 1);
const firstByte = view.getUint8(offset); const firstByte = view.getUint8(offset);
if (firstByte === 0) {
return null; // Invalid VINT
}
let width = 1; let width = 1;
let mask = 0x80; let mask = 0x80;
while ((firstByte & mask) === 0 && width < 8) { while ((firstByte & mask) === 0) {
width++; width++;
mask >>= 1; mask >>= 1;
} }
@@ -426,10 +430,14 @@ export class EBMLReader {
const { view, offset } = this.reader.getViewAndOffset(this.pos, this.pos + 1); const { view, offset } = this.reader.getViewAndOffset(this.pos, this.pos + 1);
const firstByte = view.getUint8(offset); const firstByte = view.getUint8(offset);
if (firstByte === 0) {
return null; // Invalid VINT
}
// Find the position of VINT_MARKER, which determines the width // Find the position of VINT_MARKER, which determines the width
let width = 1; let width = 1;
let mask = 1 << 7; let mask = 1 << 7;
while ((firstByte & mask) === 0 && width < MAX_VAR_INT_SIZE) { while ((firstByte & mask) === 0) {
width++; width++;
mask >>= 1; mask >>= 1;
} }
@@ -509,8 +517,11 @@ export class EBMLReader {
readElementId() { readElementId() {
const size = this.readVarIntSize(); const size = this.readVarIntSize();
const id = this.readUnsignedInt(size); if (size === null) {
return null;
}
const id = this.readUnsignedInt(size);
return id; return id;
} }
@@ -538,6 +549,10 @@ export class EBMLReader {
readElementHeader() { readElementHeader() {
const id = this.readElementId(); const id = this.readElementId();
if (id === null) {
return null;
}
const size = this.readElementSize(); const size = this.readElementSize();
return { id, size }; return { id, size };
@@ -548,13 +563,17 @@ export class EBMLReader {
const loadChunkSize = 2 ** 20; // 1 MiB const loadChunkSize = 2 ** 20; // 1 MiB
const idsSet = new Set(ids); const idsSet = new Set(ids);
while (this.pos < until - MAX_HEADER_SIZE) { while (this.pos <= until - MIN_HEADER_SIZE) {
if (!this.reader.rangeIsLoaded(this.pos, this.pos + MAX_HEADER_SIZE)) { if (!this.reader.rangeIsLoaded(this.pos, Math.min(this.pos + MAX_HEADER_SIZE, until))) {
await this.reader.loadRange(this.pos, Math.min(this.pos + loadChunkSize, until)); await this.reader.loadRange(this.pos, Math.min(this.pos + loadChunkSize, until));
} }
const elementStartPos = this.pos; const elementStartPos = this.pos;
const elementHeader = this.readElementHeader(); const elementHeader = this.readElementHeader();
if (!elementHeader) {
break;
}
if (idsSet.has(elementHeader.id)) { if (idsSet.has(elementHeader.id)) {
return elementStartPos; return elementStartPos;
} }
+38 -7
View File
@@ -239,6 +239,10 @@ export class MatroskaDemuxer extends Demuxer {
); );
const header = this.metadataReader.readElementHeader(); const header = this.metadataReader.readElementHeader();
if (!header) {
break; // Zero padding at the end of the file triggers this, for example
}
const id = header.id; const id = header.id;
let size = header.size; let size = header.size;
const startPos = this.metadataReader.pos; const startPos = this.metadataReader.pos;
@@ -318,14 +322,19 @@ export class MatroskaDemuxer extends Demuxer {
); );
let clusterEncountered = false; let clusterEncountered = false;
while (this.metadataReader.pos < this.currentSegment.elementEndPos) { while (this.metadataReader.pos <= this.currentSegment.elementEndPos - MIN_HEADER_SIZE) {
await this.metadataReader.reader.loadRange( await this.metadataReader.reader.loadRange(
this.metadataReader.pos, this.metadataReader.pos,
this.metadataReader.pos + MAX_HEADER_SIZE, this.metadataReader.pos + MAX_HEADER_SIZE,
); );
const elementStartPos = this.metadataReader.pos; const elementStartPos = this.metadataReader.pos;
const { id, size } = this.metadataReader.readElementHeader(); const header = this.metadataReader.readElementHeader();
if (!header) {
break;
}
const { id, size } = header;
const dataStartPos = this.metadataReader.pos; const dataStartPos = this.metadataReader.pos;
const metadataElementIndex = METADATA_ELEMENTS.findIndex(x => x.id === id); const metadataElementIndex = METADATA_ELEMENTS.findIndex(x => x.id === id);
@@ -392,7 +401,10 @@ export class MatroskaDemuxer extends Demuxer {
this.metadataReader.pos, this.metadataReader.pos,
this.metadataReader.pos + 2 ** 12, // Load a larger range, assuming the correct element will be there this.metadataReader.pos + 2 ** 12, // Load a larger range, assuming the correct element will be there
); );
const { id, size } = this.metadataReader.readElementHeader(); const header = this.metadataReader.readElementHeader();
if (!header) continue;
const { id, size } = header;
if (id !== target.id) continue; if (id !== target.id) continue;
assertDefinedSize(size); assertDefinedSize(size);
@@ -469,6 +481,8 @@ export class MatroskaDemuxer extends Demuxer {
const elementStartPos = this.metadataReader.pos; const elementStartPos = this.metadataReader.pos;
const elementHeader = this.metadataReader.readElementHeader(); const elementHeader = this.metadataReader.readElementHeader();
assert(elementHeader);
const id = elementHeader.id; const id = elementHeader.id;
let size = elementHeader.size; let size = elementHeader.size;
const dataStartPos = this.metadataReader.pos; const dataStartPos = this.metadataReader.pos;
@@ -720,12 +734,20 @@ export class MatroskaDemuxer extends Demuxer {
const startIndex = reader.pos; const startIndex = reader.pos;
while (reader.pos - startIndex <= totalSize - MIN_HEADER_SIZE) { while (reader.pos - startIndex <= totalSize - MIN_HEADER_SIZE) {
this.traverseElement(reader); const foundElement = this.traverseElement(reader);
if (!foundElement) {
break;
}
} }
} }
traverseElement(reader: EBMLReader) { traverseElement(reader: EBMLReader): boolean {
const { id, size } = reader.readElementHeader(); const header = reader.readElementHeader();
if (!header) {
return false;
}
const { id, size } = header;
const dataStartPos = reader.pos; const dataStartPos = reader.pos;
assertDefinedSize(size); assertDefinedSize(size);
@@ -1113,6 +1135,8 @@ export class MatroskaDemuxer extends Demuxer {
if (!this.currentCluster) break; if (!this.currentCluster) break;
const trackNumber = reader.readVarInt(); const trackNumber = reader.readVarInt();
if (trackNumber === null) break;
const relativeTimestamp = reader.readS16(); const relativeTimestamp = reader.readS16();
const flags = reader.readU8(); const flags = reader.readU8();
@@ -1148,6 +1172,8 @@ export class MatroskaDemuxer extends Demuxer {
if (!this.currentCluster) break; if (!this.currentCluster) break;
const trackNumber = reader.readVarInt(); const trackNumber = reader.readVarInt();
if (trackNumber === null) break;
const relativeTimestamp = reader.readS16(); const relativeTimestamp = reader.readS16();
const flags = reader.readU8(); const flags = reader.readU8();
@@ -1184,6 +1210,7 @@ export class MatroskaDemuxer extends Demuxer {
} }
reader.pos = dataStartPos + size; reader.pos = dataStartPos + size;
return true;
} }
} }
@@ -1562,7 +1589,7 @@ abstract class MatroskaTrackBacking implements InputTrackBacking {
} }
} }
while (metadataReader.pos < segment.elementEndPos) { while (metadataReader.pos <= segment.elementEndPos - MIN_HEADER_SIZE) {
if (prevCluster) { if (prevCluster) {
const trackData = prevCluster.trackData.get(this.internalTrack.id); const trackData = prevCluster.trackData.get(this.internalTrack.id);
if (trackData && trackData.startTimestamp > latestTimestamp) { if (trackData && trackData.startTimestamp > latestTimestamp) {
@@ -1582,6 +1609,10 @@ abstract class MatroskaTrackBacking implements InputTrackBacking {
await metadataReader.reader.loadRange(metadataReader.pos, metadataReader.pos + MAX_HEADER_SIZE); await metadataReader.reader.loadRange(metadataReader.pos, metadataReader.pos + MAX_HEADER_SIZE);
const elementStartPos = metadataReader.pos; const elementStartPos = metadataReader.pos;
const elementHeader = metadataReader.readElementHeader(); const elementHeader = metadataReader.readElementHeader();
if (!elementHeader) {
break;
}
const id = elementHeader.id; const id = elementHeader.id;
let size = elementHeader.size; let size = elementHeader.size;
const dataStartPos = metadataReader.pos; const dataStartPos = metadataReader.pos;