mediabunny 1.59.0 → 1.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundles/mediabunny.cjs +93 -31
- package/dist/bundles/mediabunny.min.cjs +17 -17
- package/dist/bundles/mediabunny.min.mjs +17 -17
- package/dist/bundles/mediabunny.mjs +93 -31
- package/dist/bundles/mediabunny.node.cjs +93 -31
- package/dist/mediabunny.d.ts +5 -0
- package/dist/modules/src/codec.js +1 -1
- package/dist/modules/src/conversion.d.ts.map +1 -1
- package/dist/modules/src/conversion.js +10 -1
- package/dist/modules/src/hls/hls-muxer.d.ts.map +1 -1
- package/dist/modules/src/hls/hls-muxer.js +33 -3
- package/dist/modules/src/matroska/matroska-demuxer.d.ts +1 -1
- package/dist/modules/src/matroska/matroska-demuxer.d.ts.map +1 -1
- package/dist/modules/src/matroska/matroska-demuxer.js +35 -15
- package/dist/modules/src/matroska/matroska-muxer.d.ts.map +1 -1
- package/dist/modules/src/matroska/matroska-muxer.js +15 -17
- package/dist/modules/src/output.d.ts +5 -0
- package/dist/modules/src/output.d.ts.map +1 -1
- package/dist/modules/src/output.js +6 -0
- package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/codec.ts +1 -1
- package/src/conversion.ts +12 -1
- package/src/hls/hls-muxer.ts +42 -3
- package/src/matroska/matroska-demuxer.ts +44 -17
- package/src/matroska/matroska-muxer.ts +19 -18
- package/src/output.ts +11 -0
|
@@ -5403,7 +5403,7 @@ var Mediabunny = (() => {
|
|
|
5403
5403
|
return "mp3";
|
|
5404
5404
|
} else if (codecString.startsWith("mp4a.40.") || codecString === "mp4a.67") {
|
|
5405
5405
|
return "aac";
|
|
5406
|
-
} else if (codecString === "opus") {
|
|
5406
|
+
} else if (codecString === "opus" || codecString === "Opus") {
|
|
5407
5407
|
return "opus";
|
|
5408
5408
|
} else if (codecString === "vorbis") {
|
|
5409
5409
|
return "vorbis";
|
|
@@ -10278,6 +10278,7 @@ var Mediabunny = (() => {
|
|
|
10278
10278
|
};
|
|
10279
10279
|
this.segments.push(this.currentSegment);
|
|
10280
10280
|
let currentPos = segmentDataStart;
|
|
10281
|
+
const visitedSeekHeadPositions = /* @__PURE__ */ new Set();
|
|
10281
10282
|
while (this.currentSegment.elementEndPos === null || currentPos < this.currentSegment.elementEndPos) {
|
|
10282
10283
|
let slice = this.reader.requestSliceRange(currentPos, MIN_HEADER_SIZE, MAX_HEADER_SIZE);
|
|
10283
10284
|
if (isThenable(slice)) slice = await slice;
|
|
@@ -10304,6 +10305,9 @@ var Mediabunny = (() => {
|
|
|
10304
10305
|
if (metadataElementIndex !== -1) {
|
|
10305
10306
|
const field = METADATA_ELEMENTS[metadataElementIndex].flag;
|
|
10306
10307
|
this.currentSegment[field] = true;
|
|
10308
|
+
if (id === 290298740 /* SeekHead */) {
|
|
10309
|
+
visitedSeekHeadPositions.add(elementStartPos - segmentDataStart);
|
|
10310
|
+
}
|
|
10307
10311
|
assertDefinedSize(size);
|
|
10308
10312
|
let slice2 = this.reader.requestSlice(dataStartPos, size);
|
|
10309
10313
|
if (isThenable(slice2)) slice2 = await slice2;
|
|
@@ -10332,6 +10336,32 @@ var Mediabunny = (() => {
|
|
|
10332
10336
|
currentPos = dataStartPos + size;
|
|
10333
10337
|
}
|
|
10334
10338
|
}
|
|
10339
|
+
if (this.reader.fileSize !== null) {
|
|
10340
|
+
while (true) {
|
|
10341
|
+
const seekEntry = this.currentSegment.seekEntries.find(
|
|
10342
|
+
(x) => x.id === 290298740 /* SeekHead */ && !visitedSeekHeadPositions.has(x.segmentPosition)
|
|
10343
|
+
);
|
|
10344
|
+
if (!seekEntry) {
|
|
10345
|
+
break;
|
|
10346
|
+
}
|
|
10347
|
+
visitedSeekHeadPositions.add(seekEntry.segmentPosition);
|
|
10348
|
+
let slice = this.reader.requestSliceRange(
|
|
10349
|
+
segmentDataStart + seekEntry.segmentPosition,
|
|
10350
|
+
MIN_HEADER_SIZE,
|
|
10351
|
+
MAX_HEADER_SIZE
|
|
10352
|
+
);
|
|
10353
|
+
if (isThenable(slice)) slice = await slice;
|
|
10354
|
+
if (!slice) continue;
|
|
10355
|
+
const header = readElementHeader(slice);
|
|
10356
|
+
if (!header || header.id !== 290298740 /* SeekHead */) continue;
|
|
10357
|
+
const { size } = header;
|
|
10358
|
+
assertDefinedSize(size);
|
|
10359
|
+
let dataSlice = this.reader.requestSlice(slice.filePos, size);
|
|
10360
|
+
if (isThenable(dataSlice)) dataSlice = await dataSlice;
|
|
10361
|
+
if (!dataSlice) continue;
|
|
10362
|
+
this.readContiguousElements(dataSlice);
|
|
10363
|
+
}
|
|
10364
|
+
}
|
|
10335
10365
|
this.currentSegment.seekEntries.sort((a, b) => a.segmentPosition - b.segmentPosition);
|
|
10336
10366
|
if (this.reader.fileSize !== null) {
|
|
10337
10367
|
for (const seekEntry of this.currentSegment.seekEntries) {
|
|
@@ -11698,21 +11728,11 @@ var Mediabunny = (() => {
|
|
|
11698
11728
|
}
|
|
11699
11729
|
}
|
|
11700
11730
|
}
|
|
11701
|
-
|
|
11731
|
+
getDurationFromMetadata(segment) {
|
|
11702
11732
|
if (segment.duration <= 0) {
|
|
11703
11733
|
return null;
|
|
11704
11734
|
}
|
|
11705
|
-
|
|
11706
|
-
for (const track of segment.tracks) {
|
|
11707
|
-
assert(track.trackBacking);
|
|
11708
|
-
const firstPacket = await track.trackBacking.getFirstPacket({ metadataOnly: true });
|
|
11709
|
-
if (firstPacket) {
|
|
11710
|
-
minTimestamp = Math.min(minTimestamp ?? Infinity, firstPacket.timestamp);
|
|
11711
|
-
}
|
|
11712
|
-
}
|
|
11713
|
-
let endTimestamp = segment.duration / segment.timestampFactor;
|
|
11714
|
-
endTimestamp += minTimestamp ?? 0;
|
|
11715
|
-
return endTimestamp;
|
|
11735
|
+
return segment.duration / segment.timestampFactor;
|
|
11716
11736
|
}
|
|
11717
11737
|
};
|
|
11718
11738
|
var MatroskaTrackBacking = class {
|
|
@@ -32557,7 +32577,7 @@ var Mediabunny = (() => {
|
|
|
32557
32577
|
{ id: 17138 /* EBMLMaxIDLength */, data: 4 },
|
|
32558
32578
|
{ id: 17139 /* EBMLMaxSizeLength */, data: 8 },
|
|
32559
32579
|
{ id: 17026 /* DocType */, data: this.format instanceof WebMOutputFormat ? "webm" : "matroska" },
|
|
32560
|
-
{ id: 17031 /* DocTypeVersion */, data:
|
|
32580
|
+
{ id: 17031 /* DocTypeVersion */, data: 4 },
|
|
32561
32581
|
{ id: 17029 /* DocTypeReadVersion */, data: 2 }
|
|
32562
32582
|
] };
|
|
32563
32583
|
this.ebmlWriter.writeEBML(ebmlHeader);
|
|
@@ -32580,14 +32600,6 @@ var Mediabunny = (() => {
|
|
|
32580
32600
|
const kaxAttachments = new Uint8Array([25, 65, 164, 105]);
|
|
32581
32601
|
const kaxTags = new Uint8Array([18, 84, 195, 103]);
|
|
32582
32602
|
const seekHead = { id: 290298740 /* SeekHead */, data: [
|
|
32583
|
-
{ id: 19899 /* Seek */, data: [
|
|
32584
|
-
{ id: 21419 /* SeekID */, data: kaxCues },
|
|
32585
|
-
{
|
|
32586
|
-
id: 21420 /* SeekPosition */,
|
|
32587
|
-
size: 5,
|
|
32588
|
-
data: writeOffsets ? this.ebmlWriter.offsets.get(this.cues) - this.segmentDataOffset : 0
|
|
32589
|
-
}
|
|
32590
|
-
] },
|
|
32591
32603
|
{ id: 19899 /* Seek */, data: [
|
|
32592
32604
|
{ id: 21419 /* SeekID */, data: kaxInfo },
|
|
32593
32605
|
{
|
|
@@ -32619,7 +32631,15 @@ var Mediabunny = (() => {
|
|
|
32619
32631
|
size: 5,
|
|
32620
32632
|
data: writeOffsets ? this.ebmlWriter.offsets.get(this.tagsElement) - this.segmentDataOffset : 0
|
|
32621
32633
|
}
|
|
32622
|
-
] } : null
|
|
32634
|
+
] } : null,
|
|
32635
|
+
{ id: 19899 /* Seek */, data: [
|
|
32636
|
+
{ id: 21419 /* SeekID */, data: kaxCues },
|
|
32637
|
+
{
|
|
32638
|
+
id: 21420 /* SeekPosition */,
|
|
32639
|
+
size: 5,
|
|
32640
|
+
data: writeOffsets ? this.ebmlWriter.offsets.get(this.cues) - this.segmentDataOffset : 0
|
|
32641
|
+
}
|
|
32642
|
+
] }
|
|
32623
32643
|
] };
|
|
32624
32644
|
this.seekHead = seekHead;
|
|
32625
32645
|
}
|
|
@@ -32671,8 +32691,7 @@ var Mediabunny = (() => {
|
|
|
32671
32691
|
{ id: 2274716 /* Language */, data: trackData.track.metadata.languageCode ?? UNDETERMINED_LANGUAGE },
|
|
32672
32692
|
{ id: 134 /* CodecID */, data: codecId },
|
|
32673
32693
|
trackData.codecPrivate ? { id: 25506 /* CodecPrivate */, data: toUint8Array(trackData.codecPrivate) } : null,
|
|
32674
|
-
{ id:
|
|
32675
|
-
{ id: 22203 /* SeekPreRoll */, data: seekPreRollNs },
|
|
32694
|
+
seekPreRollNs > 0 ? { id: 22203 /* SeekPreRoll */, data: seekPreRollNs } : null,
|
|
32676
32695
|
trackData.track.metadata.name !== void 0 ? { id: 21358 /* Name */, data: new EBMLUnicodeString(trackData.track.metadata.name) } : null,
|
|
32677
32696
|
trackData.type === "video" ? this.videoSpecificTrackInfo(trackData) : null,
|
|
32678
32697
|
trackData.type === "audio" ? this.audioSpecificTrackInfo(trackData) : null,
|
|
@@ -33070,7 +33089,7 @@ var Mediabunny = (() => {
|
|
|
33070
33089
|
height: meta.decoderConfig.codedHeight,
|
|
33071
33090
|
aspectRatio,
|
|
33072
33091
|
decoderConfig: meta.decoderConfig,
|
|
33073
|
-
alphaMode: packet ? !!packet.sideData.alpha : null
|
|
33092
|
+
alphaMode: track.metadata.canBeTransparent || (packet ? !!packet.sideData.alpha : null)
|
|
33074
33093
|
},
|
|
33075
33094
|
chunkQueue: [],
|
|
33076
33095
|
lastWrittenMsTimestamp: null,
|
|
@@ -33497,7 +33516,7 @@ ${cue.notes ?? ""}`;
|
|
|
33497
33516
|
const segmentSize = this.writer.getPos() - this.segmentDataOffset;
|
|
33498
33517
|
this.writer.seek(this.ebmlWriter.offsets.get(this.segment) + 4);
|
|
33499
33518
|
this.ebmlWriter.writeVarInt(segmentSize, SEGMENT_SIZE_BYTES);
|
|
33500
|
-
const duration = this.startTimestamp === Infinity ? 0 : this.endTimestamp
|
|
33519
|
+
const duration = this.startTimestamp === Infinity ? 0 : this.endTimestamp;
|
|
33501
33520
|
this.segmentDuration.data = new EBMLFloat64(duration);
|
|
33502
33521
|
this.writer.seek(this.ebmlWriter.offsets.get(this.segmentDuration));
|
|
33503
33522
|
this.ebmlWriter.writeEBML(this.segmentDuration);
|
|
@@ -37374,6 +37393,12 @@ ${cue.notes ?? ""}`;
|
|
|
37374
37393
|
};
|
|
37375
37394
|
|
|
37376
37395
|
// src/hls/hls-muxer.ts
|
|
37396
|
+
var toHlsCodecString = (codecString) => {
|
|
37397
|
+
if (codecString === "opus") {
|
|
37398
|
+
return "Opus";
|
|
37399
|
+
}
|
|
37400
|
+
return codecString;
|
|
37401
|
+
};
|
|
37377
37402
|
var HlsMuxer = class extends Muxer {
|
|
37378
37403
|
constructor(output, format) {
|
|
37379
37404
|
if (!(output._target instanceof PathedTarget)) {
|
|
@@ -38256,8 +38281,31 @@ ${cue.notes ?? ""}`;
|
|
|
38256
38281
|
for (const segment of segments) {
|
|
38257
38282
|
totalBits += 8 * segment.byteSize;
|
|
38258
38283
|
}
|
|
38284
|
+
let averageBitrate = totalBits / (totalDuration || 1);
|
|
38285
|
+
if (segments.length === 0) {
|
|
38286
|
+
for (const track of playlist.tracks) {
|
|
38287
|
+
const trackData = this.trackDatas.find((x) => x.track === track);
|
|
38288
|
+
let trackPeakBitrate = track.source._nominalBitrate ?? track.metadata.bitrate ?? 0;
|
|
38289
|
+
averageBitrate += track.source._nominalBitrate ?? track.metadata.averageBitrate ?? 0;
|
|
38290
|
+
if (trackPeakBitrate === 0) {
|
|
38291
|
+
const mediumQuality = new Quality("medium");
|
|
38292
|
+
if (track.isVideoTrack()) {
|
|
38293
|
+
const decoderConfig = trackData?.info.decoderConfig;
|
|
38294
|
+
trackPeakBitrate = mediumQuality._toVideoBitrate(
|
|
38295
|
+
track.source._codec,
|
|
38296
|
+
decoderConfig?.codedWidth ?? 1920,
|
|
38297
|
+
// Need to assume a resolution
|
|
38298
|
+
decoderConfig?.codedHeight ?? 1080
|
|
38299
|
+
);
|
|
38300
|
+
} else if (track.isAudioTrack()) {
|
|
38301
|
+
trackPeakBitrate = mediumQuality._toAudioBitrate(track.source._codec) ?? 0;
|
|
38302
|
+
}
|
|
38303
|
+
}
|
|
38304
|
+
peakBitrate += trackPeakBitrate;
|
|
38305
|
+
}
|
|
38306
|
+
}
|
|
38259
38307
|
playlist.peakBitrate = peakBitrate;
|
|
38260
|
-
playlist.averageBitrate =
|
|
38308
|
+
playlist.averageBitrate = averageBitrate;
|
|
38261
38309
|
}
|
|
38262
38310
|
async writePlaylist(playlist) {
|
|
38263
38311
|
assert(this.output._target instanceof PathedTarget);
|
|
@@ -38316,7 +38364,7 @@ ${cue.notes ?? ""}`;
|
|
|
38316
38364
|
for (const track of decl.playlist.tracks) {
|
|
38317
38365
|
const trackData = this.trackDatas.find((x) => x.track === track);
|
|
38318
38366
|
const codecString = trackData?.info.decoderConfig.codec ?? track.source._codec;
|
|
38319
|
-
codecs.push(codecString);
|
|
38367
|
+
codecs.push(toHlsCodecString(codecString));
|
|
38320
38368
|
}
|
|
38321
38369
|
let peakDeclBitrate = 0;
|
|
38322
38370
|
let maxRefAverageBitrate = 0;
|
|
@@ -38325,7 +38373,7 @@ ${cue.notes ?? ""}`;
|
|
|
38325
38373
|
const firstTrack = firstRef.playlist.tracks[0];
|
|
38326
38374
|
const trackData = this.trackDatas.find((x) => x.track === firstTrack);
|
|
38327
38375
|
const codecString = trackData?.info.decoderConfig.codec ?? firstTrack.source._codec;
|
|
38328
|
-
codecs.push(codecString);
|
|
38376
|
+
codecs.push(toHlsCodecString(codecString));
|
|
38329
38377
|
for (const ref of decl.references) {
|
|
38330
38378
|
assert(ref.playlist.peakBitrate !== null);
|
|
38331
38379
|
peakDeclBitrate = Math.max(peakDeclBitrate, ref.playlist.peakBitrate);
|
|
@@ -39554,6 +39602,12 @@ ${cue.notes ?? ""}`;
|
|
|
39554
39602
|
`Invalid video frame rate: ${metadata.frameRate}. Must be a positive number.`
|
|
39555
39603
|
);
|
|
39556
39604
|
}
|
|
39605
|
+
if (metadata.hasOnlyKeyPackets !== void 0 && typeof metadata.hasOnlyKeyPackets !== "boolean") {
|
|
39606
|
+
throw new TypeError("metadata.hasOnlyKeyPackets, when provided, must be a boolean.");
|
|
39607
|
+
}
|
|
39608
|
+
if (metadata.canBeTransparent !== void 0 && typeof metadata.canBeTransparent !== "boolean") {
|
|
39609
|
+
throw new TypeError("metadata.canBeTransparent, when provided, must be a boolean.");
|
|
39610
|
+
}
|
|
39557
39611
|
if (metadata.decoderConfig !== void 0) {
|
|
39558
39612
|
validateVideoChunkMetadata({ decoderConfig: metadata.decoderConfig }, source._codec);
|
|
39559
39613
|
}
|
|
@@ -40865,6 +40919,7 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
40865
40919
|
const trackDisposition = await track.getDisposition();
|
|
40866
40920
|
const bitrate = needsTranscode ? null : await track.getBitrate();
|
|
40867
40921
|
const averageBitrate = needsTranscode ? null : await track.getAverageBitrate();
|
|
40922
|
+
const canBeTransparent = alpha === "keep" && await track.canBeTransparent();
|
|
40868
40923
|
this.output.addVideoTrack(videoSource, {
|
|
40869
40924
|
frameRate: trackOptions.frameRate,
|
|
40870
40925
|
// TODO: This condition can be removed when all demuxers properly homogenize to BCP47 in v2
|
|
@@ -40874,6 +40929,7 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
40874
40929
|
rotation: outputTrackRotation,
|
|
40875
40930
|
flip: outputTrackFlip,
|
|
40876
40931
|
transformationMatrix: outputTrackMatrix ?? void 0,
|
|
40932
|
+
canBeTransparent,
|
|
40877
40933
|
group: ownGroup ?? trackOptions.group,
|
|
40878
40934
|
bitrate: bitrate ?? void 0,
|
|
40879
40935
|
averageBitrate: averageBitrate ?? void 0
|
|
@@ -40972,6 +41028,7 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
40972
41028
|
const sink = new EncodedPacketSink(track);
|
|
40973
41029
|
const decoderConfig = await track.getDecoderConfig();
|
|
40974
41030
|
const meta = { decoderConfig: decoderConfig ?? void 0 };
|
|
41031
|
+
let maxTimestamp = null;
|
|
40975
41032
|
if (copyStartPacket) for await (const packet of sink.packets(copyStartPacket)) {
|
|
40976
41033
|
if (this._state === "canceled") {
|
|
40977
41034
|
break;
|
|
@@ -40982,8 +41039,13 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
40982
41039
|
if (this._copyBoundaryPolicy === "shrink" && packet.timestamp + packet.duration > this._endTimestamp) {
|
|
40983
41040
|
break;
|
|
40984
41041
|
}
|
|
41042
|
+
const packetTimestamp = packet.timestamp + this._timestampOffset;
|
|
41043
|
+
if (maxTimestamp !== null && packetTimestamp < maxTimestamp) {
|
|
41044
|
+
continue;
|
|
41045
|
+
}
|
|
41046
|
+
maxTimestamp = packetTimestamp;
|
|
40985
41047
|
const modifiedPacket = packet.clone({
|
|
40986
|
-
timestamp:
|
|
41048
|
+
timestamp: packetTimestamp,
|
|
40987
41049
|
duration: packet.duration
|
|
40988
41050
|
});
|
|
40989
41051
|
this._reportProgress(outputTrackId, modifiedPacket.timestamp + modifiedPacket.duration);
|