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.
@@ -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
- async getDurationFromMetadata(segment) {
11731
+ getDurationFromMetadata(segment) {
11702
11732
  if (segment.duration <= 0) {
11703
11733
  return null;
11704
11734
  }
11705
- let minTimestamp = null;
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: 2 },
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: 22186 /* CodecDelay */, data: 0 },
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 - this.startTimestamp;
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 = totalBits / (totalDuration || 1);
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: packet.timestamp + this._timestampOffset,
41048
+ timestamp: packetTimestamp,
40987
41049
  duration: packet.duration
40988
41050
  });
40989
41051
  this._reportProgress(outputTrackId, modifiedPacket.timestamp + modifiedPacket.duration);