mediabunny 1.56.0 → 1.56.2

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.
@@ -273,6 +273,13 @@ var Mediabunny = (() => {
273
273
  const result = (1 << leadingZeroBits) - 1 + bitstream.readBits(leadingZeroBits);
274
274
  return result;
275
275
  };
276
+ var writeExpGolomb = (bitstream, value) => {
277
+ const codeNum = value + 1;
278
+ const leadingZeroBits = Math.floor(Math.log2(codeNum));
279
+ bitstream.writeBits(leadingZeroBits, 0);
280
+ bitstream.writeBits(1, 1);
281
+ bitstream.writeBits(leadingZeroBits, codeNum - 2 ** leadingZeroBits);
282
+ };
276
283
  var readSignedExpGolomb = (bitstream) => {
277
284
  const codeNum = readExpGolomb(bitstream);
278
285
  return (codeNum & 1) === 0 ? -(codeNum >> 1) : codeNum + 1 >> 1;
@@ -1299,9 +1306,9 @@ var Mediabunny = (() => {
1299
1306
  throw new TypeError("tags.raw, when provided, must be an object.");
1300
1307
  }
1301
1308
  for (const value of Object.values(tags.raw)) {
1302
- if (value !== null && typeof value !== "string" && !(value instanceof Uint8Array) && !(value instanceof RichImageData) && !(value instanceof AttachedFile) && !isRecordStringString(value)) {
1309
+ if (value !== null && typeof value !== "string" && !(Array.isArray(value) && value.every((x) => typeof x === "string")) && !(value instanceof Uint8Array) && !(value instanceof RichImageData) && !(value instanceof AttachedFile) && !isRecordStringString(value)) {
1303
1310
  throw new TypeError(
1304
- "Each value in tags.raw must be a string, Uint8Array, RichImageData, AttachedFile, Record<string, string>, or null."
1311
+ "Each value in tags.raw must be a string, string array, Uint8Array, RichImageData, AttachedFile, Record<string, string>, or null."
1305
1312
  );
1306
1313
  }
1307
1314
  }
@@ -1387,6 +1394,16 @@ var Mediabunny = (() => {
1387
1394
  }
1388
1395
  this.pos = end;
1389
1396
  }
1397
+ copyBits(n, other) {
1398
+ let i = 0;
1399
+ for (i; i < n - 7; i += 8) {
1400
+ this.writeBits(8, other.readBits(8));
1401
+ }
1402
+ const leftover = n - i;
1403
+ if (leftover > 0) {
1404
+ this.writeBits(leftover, other.readBits(leftover));
1405
+ }
1406
+ }
1390
1407
  readAlignedByte() {
1391
1408
  if (this.pos % 8 !== 0) {
1392
1409
  throw new Error("Bitstream is not byte-aligned.");
@@ -1663,6 +1680,19 @@ var Mediabunny = (() => {
1663
1680
  }
1664
1681
  return new Uint8Array(result);
1665
1682
  };
1683
+ var addEmulationPreventionBytes = (data) => {
1684
+ const result = [];
1685
+ let zeroCount = 0;
1686
+ for (const byte of data) {
1687
+ if (zeroCount === 2 && byte <= 3) {
1688
+ result.push(3);
1689
+ zeroCount = 0;
1690
+ }
1691
+ result.push(byte);
1692
+ zeroCount = byte === 0 ? zeroCount + 1 : 0;
1693
+ }
1694
+ return new Uint8Array(result);
1695
+ };
1666
1696
  var ANNEX_B_START_CODE = new Uint8Array([0, 0, 0, 1]);
1667
1697
  var concatNalUnitsInAnnexB = (nalUnits) => {
1668
1698
  const totalLength = nalUnits.reduce((a, b) => a + ANNEX_B_START_CODE.byteLength + b.byteLength, 0);
@@ -1782,8 +1812,7 @@ var Mediabunny = (() => {
1782
1812
  bytes2.push(pps[i]);
1783
1813
  }
1784
1814
  }
1785
- if (record.avcProfileIndication === 100 || record.avcProfileIndication === 110 || record.avcProfileIndication === 122 || record.avcProfileIndication === 144) {
1786
- assert(record.chromaFormat !== null);
1815
+ if ((record.avcProfileIndication === 100 || record.avcProfileIndication === 110 || record.avcProfileIndication === 122 || record.avcProfileIndication === 144) && record.chromaFormat !== null) {
1787
1816
  assert(record.bitDepthLumaMinus8 !== null);
1788
1817
  assert(record.bitDepthChromaMinus8 !== null);
1789
1818
  assert(record.sequenceParameterSetExt !== null);
@@ -1883,7 +1912,8 @@ var Mediabunny = (() => {
1883
1912
  };
1884
1913
  var parseAvcSps = (sps) => {
1885
1914
  try {
1886
- const bitstream = new Bitstream(removeEmulationPreventionBytes(sps));
1915
+ const emulationUnpreventedBytes = removeEmulationPreventionBytes(sps);
1916
+ const bitstream = new Bitstream(emulationUnpreventedBytes);
1887
1917
  bitstream.skipBits(1);
1888
1918
  bitstream.skipBits(2);
1889
1919
  const nalUnitType = bitstream.readBits(5);
@@ -1979,6 +2009,9 @@ var Mediabunny = (() => {
1979
2009
  let pixelAspectRatio = { num: 1, den: 1 };
1980
2010
  let numReorderFrames = null;
1981
2011
  let maxDecFrameBuffering = null;
2012
+ let bitstreamRestrictionFlagBitOffset = null;
2013
+ let bitstreamRestrictionFlag = null;
2014
+ const vuiParametersFlagBitOffset = bitstream.pos;
1982
2015
  const vuiParametersPresentFlag = bitstream.readBits(1);
1983
2016
  if (vuiParametersPresentFlag) {
1984
2017
  const aspectRatioInfoPresentFlag = bitstream.readBits(1);
@@ -2034,7 +2067,8 @@ var Mediabunny = (() => {
2034
2067
  bitstream.skipBits(1);
2035
2068
  }
2036
2069
  bitstream.skipBits(1);
2037
- const bitstreamRestrictionFlag = bitstream.readBits(1);
2070
+ bitstreamRestrictionFlagBitOffset = bitstream.pos;
2071
+ bitstreamRestrictionFlag = bitstream.readBits(1);
2038
2072
  if (bitstreamRestrictionFlag) {
2039
2073
  bitstream.skipBits(1);
2040
2074
  readExpGolomb(bitstream);
@@ -2068,6 +2102,7 @@ var Mediabunny = (() => {
2068
2102
  }
2069
2103
  assert(maxDecFrameBuffering !== null);
2070
2104
  return {
2105
+ emulationUnpreventedBytes,
2071
2106
  profileIdc,
2072
2107
  constraintFlags,
2073
2108
  levelIdc,
@@ -2085,7 +2120,10 @@ var Mediabunny = (() => {
2085
2120
  transferCharacteristics,
2086
2121
  fullRangeFlag,
2087
2122
  numReorderFrames,
2088
- maxDecFrameBuffering
2123
+ maxDecFrameBuffering,
2124
+ vuiParametersFlagBitOffset,
2125
+ bitstreamRestrictionFlagBitOffset,
2126
+ bitstreamRestrictionFlag
2089
2127
  };
2090
2128
  } catch (error) {
2091
2129
  Logging._error("Error parsing AVC SPS:", error);
@@ -2106,6 +2144,40 @@ var Mediabunny = (() => {
2106
2144
  bitstream.skipBits(5);
2107
2145
  bitstream.skipBits(5);
2108
2146
  };
2147
+ var addAvcBitstreamRestriction = (sps) => {
2148
+ assert(sps.bitstreamRestrictionFlag !== 1);
2149
+ const modifiedBytes = new Uint8Array(sps.emulationUnpreventedBytes.byteLength + 64);
2150
+ const oldBitstream = new Bitstream(sps.emulationUnpreventedBytes);
2151
+ const newBitstream = new Bitstream(modifiedBytes);
2152
+ if (sps.bitstreamRestrictionFlag === null) {
2153
+ newBitstream.copyBits(sps.vuiParametersFlagBitOffset, oldBitstream);
2154
+ newBitstream.writeBits(1, 1);
2155
+ newBitstream.writeBits(1, 0);
2156
+ newBitstream.writeBits(1, 0);
2157
+ newBitstream.writeBits(1, 0);
2158
+ newBitstream.writeBits(1, 0);
2159
+ newBitstream.writeBits(1, 0);
2160
+ newBitstream.writeBits(1, 0);
2161
+ newBitstream.writeBits(1, 0);
2162
+ newBitstream.writeBits(1, 0);
2163
+ } else {
2164
+ assert(sps.bitstreamRestrictionFlagBitOffset !== null);
2165
+ newBitstream.copyBits(sps.bitstreamRestrictionFlagBitOffset, oldBitstream);
2166
+ }
2167
+ newBitstream.writeBits(1, 1);
2168
+ newBitstream.writeBits(1, 1);
2169
+ writeExpGolomb(newBitstream, 2);
2170
+ writeExpGolomb(newBitstream, 1);
2171
+ writeExpGolomb(newBitstream, 16);
2172
+ writeExpGolomb(newBitstream, 16);
2173
+ writeExpGolomb(newBitstream, sps.numReorderFrames);
2174
+ writeExpGolomb(newBitstream, sps.maxDecFrameBuffering);
2175
+ newBitstream.writeBits(1, 1);
2176
+ newBitstream.writeBits((8 - newBitstream.pos % 8) % 8, 0);
2177
+ const byteLength = newBitstream.pos / 8;
2178
+ assert(Number.isInteger(byteLength));
2179
+ return addEmulationPreventionBytes(modifiedBytes.subarray(0, byteLength));
2180
+ };
2109
2181
  var concatHevcNalUnits = (nalUnits, decoderConfig) => {
2110
2182
  if (decoderConfig.description) {
2111
2183
  const bytes2 = toUint8Array(decoderConfig.description);
@@ -3422,7 +3494,13 @@ var Mediabunny = (() => {
3422
3494
  const key = string.slice(0, separatorIndex).toUpperCase();
3423
3495
  const value = string.slice(separatorIndex + 1);
3424
3496
  metadataTags.raw ??= {};
3425
- metadataTags.raw[key] ??= value;
3497
+ if (Array.isArray(metadataTags.raw[key])) {
3498
+ metadataTags.raw[key] = [...metadataTags.raw[key], value];
3499
+ } else if (typeof metadataTags.raw[key] === "string") {
3500
+ metadataTags.raw[key] = [metadataTags.raw[key], value];
3501
+ } else {
3502
+ metadataTags.raw[key] ??= value;
3503
+ }
3426
3504
  switch (key) {
3427
3505
  case "TITLE":
3428
3506
  {
@@ -3569,7 +3647,7 @@ var Mediabunny = (() => {
3569
3647
  currentView.setUint32(0, encodedVendorString.length, true);
3570
3648
  currentBuffer.set(encodedVendorString, 4);
3571
3649
  commentHeaderParts.push(currentBuffer);
3572
- const writtenTags = /* @__PURE__ */ new Set();
3650
+ const writtenTags = [];
3573
3651
  const addCommentTag = (key, value) => {
3574
3652
  const joined = `${key}=${value}`;
3575
3653
  const encoded = textEncoder.encode(joined);
@@ -3578,7 +3656,7 @@ var Mediabunny = (() => {
3578
3656
  currentView.setUint32(0, encoded.length, true);
3579
3657
  currentBuffer.set(encoded, 4);
3580
3658
  commentHeaderParts.push(currentBuffer);
3581
- writtenTags.add(key);
3659
+ writtenTags.push(key);
3582
3660
  };
3583
3661
  for (const { key, value } of keyValueIterator(tags)) {
3584
3662
  switch (key) {
@@ -3620,12 +3698,7 @@ var Mediabunny = (() => {
3620
3698
  break;
3621
3699
  case "date":
3622
3700
  {
3623
- const rawVersion = tags.raw?.["DATE"] ?? tags.raw?.["date"];
3624
- if (rawVersion && typeof rawVersion === "string") {
3625
- addCommentTag("DATE", rawVersion);
3626
- } else {
3627
- addCommentTag("DATE", value.toISOString().slice(0, 10));
3628
- }
3701
+ addCommentTag("DATE", value.toISOString().slice(0, 10));
3629
3702
  }
3630
3703
  ;
3631
3704
  break;
@@ -3714,16 +3787,23 @@ var Mediabunny = (() => {
3714
3787
  if (tags.raw) {
3715
3788
  for (const key in tags.raw) {
3716
3789
  const value = tags.raw[key] ?? tags.raw[key.toLowerCase()];
3717
- if (key === "vendor" || value == null || writtenTags.has(key)) {
3790
+ if (key === "vendor" || value == null || writtenTags.includes(key)) {
3718
3791
  continue;
3719
3792
  }
3720
3793
  if (typeof value === "string") {
3721
3794
  addCommentTag(key, value);
3795
+ } else if (Array.isArray(value)) {
3796
+ const isOnlyStrings = value.every((x) => typeof x === "string");
3797
+ if (isOnlyStrings) {
3798
+ for (const elem of value) {
3799
+ addCommentTag(key, elem);
3800
+ }
3801
+ }
3722
3802
  }
3723
3803
  }
3724
3804
  }
3725
3805
  const listLengthBuffer = new Uint8Array(4);
3726
- toDataView(listLengthBuffer).setUint32(0, writtenTags.size, true);
3806
+ toDataView(listLengthBuffer).setUint32(0, writtenTags.length, true);
3727
3807
  commentHeaderParts.splice(2, 0, listLengthBuffer);
3728
3808
  const commentHeaderLength = commentHeaderParts.reduce((a, b) => a + b.length, 0);
3729
3809
  const commentHeader = new Uint8Array(commentHeaderLength);
@@ -24070,11 +24150,20 @@ var Mediabunny = (() => {
24070
24150
  );
24071
24151
  if (record && record.sequenceParameterSets.length > 0) {
24072
24152
  const sps = parseAvcSps(record.sequenceParameterSets[0]);
24073
- if (sps && sps.frameMbsOnlyFlag === 0) {
24074
- this.decoderConfig = {
24075
- ...this.decoderConfig,
24076
- hardwareAcceleration: "prefer-software"
24077
- };
24153
+ if (sps) {
24154
+ if (sps.frameMbsOnlyFlag === 0) {
24155
+ this.decoderConfig = {
24156
+ ...this.decoderConfig,
24157
+ hardwareAcceleration: "prefer-software"
24158
+ };
24159
+ }
24160
+ if (sps.maxDecFrameBuffering !== 0 && sps.bitstreamRestrictionFlag !== 1) {
24161
+ record.sequenceParameterSets[0] = addAvcBitstreamRestriction(sps);
24162
+ this.decoderConfig = {
24163
+ ...this.decoderConfig,
24164
+ description: serializeAvcDecoderConfigurationRecord(record)
24165
+ };
24166
+ }
24078
24167
  }
24079
24168
  }
24080
24169
  }
@@ -24151,6 +24240,19 @@ var Mediabunny = (() => {
24151
24240
  filteredNalUnits.push(packet.data.subarray(loc.offset, loc.offset + loc.length));
24152
24241
  }
24153
24242
  }
24243
+ if (!this.decoderConfig.description) {
24244
+ for (let i = 0; i < filteredNalUnits.length; i++) {
24245
+ const nalUnit = filteredNalUnits[i];
24246
+ if (extractNalUnitTypeForAvc(nalUnit[0]) !== 7 /* SPS */) {
24247
+ continue;
24248
+ }
24249
+ const sps = parseAvcSps(nalUnit);
24250
+ if (sps && sps.maxDecFrameBuffering !== 0 && sps.bitstreamRestrictionFlag !== 1) {
24251
+ filteredNalUnits[i] = addAvcBitstreamRestriction(sps);
24252
+ }
24253
+ break;
24254
+ }
24255
+ }
24154
24256
  const newData = concatAvcNalUnits(filteredNalUnits, this.decoderConfig);
24155
24257
  packet = new EncodedPacket(newData, packet.type, packet.timestamp, packet.duration);
24156
24258
  } else if (this.codec === "hevc") {
@@ -34450,11 +34552,11 @@ ${cue.notes ?? ""}`;
34450
34552
  };
34451
34553
  } else if (sourceNum === 2 && targetNum === 4) {
34452
34554
  this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {
34453
- return sourceData[sourceFrameIndex * sourceNum + targetChannelIndex] * +(targetChannelIndex < 2);
34555
+ return targetChannelIndex < 2 ? sourceData[sourceFrameIndex * sourceNum + targetChannelIndex] : 0;
34454
34556
  };
34455
34557
  } else if (sourceNum === 2 && targetNum === 6) {
34456
34558
  this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {
34457
- return sourceData[sourceFrameIndex * sourceNum + targetChannelIndex] * +(targetChannelIndex < 2);
34559
+ return targetChannelIndex < 2 ? sourceData[sourceFrameIndex * sourceNum + targetChannelIndex] : 0;
34458
34560
  };
34459
34561
  } else if (sourceNum === 4 && targetNum === 1) {
34460
34562
  this.channelMixer = (sourceData, sourceFrameIndex) => {
@@ -39642,7 +39744,7 @@ ${cue.notes ?? ""}`;
39642
39744
  }
39643
39745
  const inputAndOutputFormatMatch = inputFormat.mimeType === this.output.format.mimeType;
39644
39746
  const rawTagsAreUnchanged = inputTags.raw === outputTags.raw;
39645
- if (inputTags.raw && rawTagsAreUnchanged && !inputAndOutputFormatMatch) {
39747
+ if (rawTagsAreUnchanged && !inputAndOutputFormatMatch) {
39646
39748
  delete outputTags.raw;
39647
39749
  }
39648
39750
  this.output.setMetadataTags(outputTags);