mediabunny 1.26.0 → 1.27.1

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/README.md CHANGED
@@ -17,7 +17,10 @@ Mediabunny is a JavaScript library for reading, writing, and converting media fi
17
17
 
18
18
  <div align="center">
19
19
  <a href="https://remotion.dev/" target="_blank" rel="sponsored">
20
- <img src="./docs/public/sponsors/remotion.png" width="60" height="60" alt="Remotion">
20
+ <picture>
21
+ <source srcset="./docs/public/sponsors/remotion-dark.png" media="(prefers-color-scheme: dark)">
22
+ <img src="./docs/public/sponsors/remotion-light.png" width="60" height="60" alt="Remotion">
23
+ </picture>
21
24
  </a>
22
25
  &nbsp;&nbsp;&nbsp;&nbsp;
23
26
  <a href="https://www.gling.ai/" target="_blank" rel="sponsored">
@@ -128,7 +128,9 @@ var Mediabunny = (() => {
128
128
  TextSubtitleSource: () => TextSubtitleSource,
129
129
  UrlSource: () => UrlSource,
130
130
  VIDEO_CODECS: () => VIDEO_CODECS,
131
+ VIDEO_SAMPLE_PIXEL_FORMATS: () => VIDEO_SAMPLE_PIXEL_FORMATS,
131
132
  VideoSample: () => VideoSample,
133
+ VideoSampleColorSpace: () => VideoSampleColorSpace,
132
134
  VideoSampleSink: () => VideoSampleSink,
133
135
  VideoSampleSource: () => VideoSampleSource,
134
136
  VideoSource: () => VideoSource,
@@ -1930,27 +1932,44 @@ var Mediabunny = (() => {
1930
1932
  }
1931
1933
  return new Uint8Array(result);
1932
1934
  };
1933
- var transformAnnexBToLengthPrefixed = (packetData) => {
1934
- const NAL_UNIT_LENGTH_SIZE = 4;
1935
- const nalUnits = findNalUnitsInAnnexB(packetData);
1936
- if (nalUnits.length === 0) {
1937
- return null;
1938
- }
1939
- let totalSize = 0;
1935
+ var ANNEX_B_START_CODE = new Uint8Array([0, 0, 0, 1]);
1936
+ var concatNalUnitsInAnnexB = (nalUnits) => {
1937
+ const totalLength = nalUnits.reduce((a, b) => a + ANNEX_B_START_CODE.byteLength + b.byteLength, 0);
1938
+ const result = new Uint8Array(totalLength);
1939
+ let offset = 0;
1940
1940
  for (const nalUnit of nalUnits) {
1941
- totalSize += NAL_UNIT_LENGTH_SIZE + nalUnit.byteLength;
1941
+ result.set(ANNEX_B_START_CODE, offset);
1942
+ offset += ANNEX_B_START_CODE.byteLength;
1943
+ result.set(nalUnit, offset);
1944
+ offset += nalUnit.byteLength;
1942
1945
  }
1943
- const avccData = new Uint8Array(totalSize);
1944
- const dataView = new DataView(avccData.buffer);
1946
+ return result;
1947
+ };
1948
+ var concatNalUnitsInLengthPrefixed = (nalUnits, lengthSize) => {
1949
+ const totalLength = nalUnits.reduce((a, b) => a + lengthSize + b.byteLength, 0);
1950
+ const result = new Uint8Array(totalLength);
1945
1951
  let offset = 0;
1946
1952
  for (const nalUnit of nalUnits) {
1947
- const length = nalUnit.byteLength;
1948
- dataView.setUint32(offset, length, false);
1949
- offset += 4;
1950
- avccData.set(nalUnit, offset);
1953
+ const dataView = new DataView(result.buffer, result.byteOffset, result.byteLength);
1954
+ switch (lengthSize) {
1955
+ case 1:
1956
+ dataView.setUint8(offset, nalUnit.byteLength);
1957
+ break;
1958
+ case 2:
1959
+ dataView.setUint16(offset, nalUnit.byteLength, false);
1960
+ break;
1961
+ case 3:
1962
+ setUint24(dataView, offset, nalUnit.byteLength, false);
1963
+ break;
1964
+ case 4:
1965
+ dataView.setUint32(offset, nalUnit.byteLength, false);
1966
+ break;
1967
+ }
1968
+ offset += lengthSize;
1969
+ result.set(nalUnit, offset);
1951
1970
  offset += nalUnit.byteLength;
1952
1971
  }
1953
- return avccData;
1972
+ return result;
1954
1973
  };
1955
1974
  var extractAvcNalUnits = (packetData, decoderConfig) => {
1956
1975
  if (decoderConfig.description) {
@@ -1962,6 +1981,16 @@ var Mediabunny = (() => {
1962
1981
  return findNalUnitsInAnnexB(packetData);
1963
1982
  }
1964
1983
  };
1984
+ var concatAvcNalUnits = (nalUnits, decoderConfig) => {
1985
+ if (decoderConfig.description) {
1986
+ const bytes2 = toUint8Array(decoderConfig.description);
1987
+ const lengthSizeMinusOne = bytes2[4] & 3;
1988
+ const lengthSize = lengthSizeMinusOne + 1;
1989
+ return concatNalUnitsInLengthPrefixed(nalUnits, lengthSize);
1990
+ } else {
1991
+ return concatNalUnitsInAnnexB(nalUnits);
1992
+ }
1993
+ };
1965
1994
  var extractNalUnitTypeForAvc = (data) => {
1966
1995
  return data[0] & 31;
1967
1996
  };
@@ -3834,6 +3863,43 @@ var Mediabunny = (() => {
3834
3863
  }
3835
3864
  });
3836
3865
  }
3866
+ var VIDEO_SAMPLE_PIXEL_FORMATS = [
3867
+ // 4:2:0 Y, U, V
3868
+ "I420",
3869
+ "I420P10",
3870
+ "I420P12",
3871
+ // 4:2:0 Y, U, V, A
3872
+ "I420A",
3873
+ "I420AP10",
3874
+ "I420AP12",
3875
+ // 4:2:2 Y, U, V
3876
+ "I422",
3877
+ "I422P10",
3878
+ "I422P12",
3879
+ // 4:2:2 Y, U, V, A
3880
+ "I422A",
3881
+ "I422AP10",
3882
+ "I422AP12",
3883
+ // 4:4:4 Y, U, V
3884
+ "I444",
3885
+ "I444P10",
3886
+ "I444P12",
3887
+ // 4:4:4 Y, U, V, A
3888
+ "I444A",
3889
+ "I444AP10",
3890
+ "I444AP12",
3891
+ // 4:2:0 Y, UV
3892
+ "NV12",
3893
+ // 4:4:4 RGBA
3894
+ "RGBA",
3895
+ // 4:4:4 RGBX (opaque)
3896
+ "RGBX",
3897
+ // 4:4:4 BGRA
3898
+ "BGRA",
3899
+ // 4:4:4 BGRX (opaque)
3900
+ "BGRX"
3901
+ ];
3902
+ var VIDEO_SAMPLE_PIXEL_FORMATS_SET = new Set(VIDEO_SAMPLE_PIXEL_FORMATS);
3837
3903
  var VideoSample = class _VideoSample {
3838
3904
  constructor(data, init) {
3839
3905
  /** @internal */
@@ -3842,8 +3908,8 @@ var Mediabunny = (() => {
3842
3908
  if (!init || typeof init !== "object") {
3843
3909
  throw new TypeError("init must be an object.");
3844
3910
  }
3845
- if (!("format" in init) || typeof init.format !== "string") {
3846
- throw new TypeError("init.format must be a string.");
3911
+ if (init.format === void 0 || !VIDEO_SAMPLE_PIXEL_FORMATS_SET.has(init.format)) {
3912
+ throw new TypeError("init.format must be one of: " + VIDEO_SAMPLE_PIXEL_FORMATS.join(", "));
3847
3913
  }
3848
3914
  if (!Number.isInteger(init.codedWidth) || init.codedWidth <= 0) {
3849
3915
  throw new TypeError("init.codedWidth must be a positive integer.");
@@ -3861,13 +3927,14 @@ var Mediabunny = (() => {
3861
3927
  throw new TypeError("init.duration, when provided, must be a non-negative number.");
3862
3928
  }
3863
3929
  this._data = toUint8Array(data).slice();
3930
+ this._layout = init.layout ?? createDefaultPlaneLayout(init.format, init.codedWidth, init.codedHeight);
3864
3931
  this.format = init.format;
3865
3932
  this.codedWidth = init.codedWidth;
3866
3933
  this.codedHeight = init.codedHeight;
3867
3934
  this.rotation = init.rotation ?? 0;
3868
3935
  this.timestamp = init.timestamp;
3869
3936
  this.duration = init.duration ?? 0;
3870
- this.colorSpace = new VideoColorSpace(init.colorSpace);
3937
+ this.colorSpace = new VideoSampleColorSpace(init.colorSpace);
3871
3938
  } else if (typeof VideoFrame !== "undefined" && data instanceof VideoFrame) {
3872
3939
  if (init?.rotation !== void 0 && ![0, 90, 180, 270].includes(init.rotation)) {
3873
3940
  throw new TypeError("init.rotation, when provided, must be 0, 90, 180, or 270.");
@@ -3879,13 +3946,14 @@ var Mediabunny = (() => {
3879
3946
  throw new TypeError("init.duration, when provided, must be a non-negative number.");
3880
3947
  }
3881
3948
  this._data = data;
3949
+ this._layout = null;
3882
3950
  this.format = data.format;
3883
3951
  this.codedWidth = data.displayWidth;
3884
3952
  this.codedHeight = data.displayHeight;
3885
3953
  this.rotation = init?.rotation ?? 0;
3886
3954
  this.timestamp = init?.timestamp ?? data.timestamp / 1e6;
3887
3955
  this.duration = init?.duration ?? (data.duration ?? 0) / 1e6;
3888
- this.colorSpace = data.colorSpace;
3956
+ this.colorSpace = new VideoSampleColorSpace(data.colorSpace);
3889
3957
  } else if (typeof HTMLImageElement !== "undefined" && data instanceof HTMLImageElement || typeof SVGImageElement !== "undefined" && data instanceof SVGImageElement || typeof ImageBitmap !== "undefined" && data instanceof ImageBitmap || typeof HTMLVideoElement !== "undefined" && data instanceof HTMLVideoElement || typeof HTMLCanvasElement !== "undefined" && data instanceof HTMLCanvasElement || typeof OffscreenCanvas !== "undefined" && data instanceof OffscreenCanvas) {
3890
3958
  if (!init || typeof init !== "object") {
3891
3959
  throw new TypeError("init must be an object.");
@@ -3933,13 +4001,14 @@ var Mediabunny = (() => {
3933
4001
  assert(context);
3934
4002
  context.drawImage(data, 0, 0);
3935
4003
  this._data = canvas;
4004
+ this._layout = null;
3936
4005
  this.format = "RGBX";
3937
4006
  this.codedWidth = width;
3938
4007
  this.codedHeight = height;
3939
4008
  this.rotation = init.rotation ?? 0;
3940
4009
  this.timestamp = init.timestamp;
3941
4010
  this.duration = init.duration ?? 0;
3942
- this.colorSpace = new VideoColorSpace({
4011
+ this.colorSpace = new VideoSampleColorSpace({
3943
4012
  matrix: "rgb",
3944
4013
  primaries: "bt709",
3945
4014
  transfer: "iec61966-2-1",
@@ -3986,8 +4055,10 @@ var Mediabunny = (() => {
3986
4055
  rotation: this.rotation
3987
4056
  });
3988
4057
  } else if (this._data instanceof Uint8Array) {
3989
- return new _VideoSample(this._data.slice(), {
4058
+ assert(this._layout);
4059
+ return new _VideoSample(this._data, {
3990
4060
  format: this.format,
4061
+ layout: this._layout,
3991
4062
  codedWidth: this.codedWidth,
3992
4063
  codedHeight: this.codedHeight,
3993
4064
  timestamp: this.timestamp,
@@ -4023,34 +4094,71 @@ var Mediabunny = (() => {
4023
4094
  }
4024
4095
  this._closed = true;
4025
4096
  }
4026
- /** Returns the number of bytes required to hold this video sample's pixel data. */
4027
- allocationSize() {
4097
+ /**
4098
+ * Returns the number of bytes required to hold this video sample's pixel data. Throws if `format` is `null`;
4099
+ * specify an explicit RGB format in the options in this case.
4100
+ */
4101
+ allocationSize(options = {}) {
4102
+ validateVideoFrameCopyToOptions(options);
4028
4103
  if (this._closed) {
4029
4104
  throw new Error("VideoSample is closed.");
4030
4105
  }
4106
+ if ((options.format ?? this.format) === null) {
4107
+ throw new Error(
4108
+ "Cannot get allocation size when format is null. Please manually provide an RGB pixel format in the options instead."
4109
+ );
4110
+ }
4031
4111
  assert(this._data !== null);
4112
+ if (!isVideoFrame(this._data)) {
4113
+ if (options.colorSpace || options.format && options.format !== this.format || options.layout || options.rect) {
4114
+ const videoFrame = this.toVideoFrame();
4115
+ const size = videoFrame.allocationSize(options);
4116
+ videoFrame.close();
4117
+ return size;
4118
+ }
4119
+ }
4032
4120
  if (isVideoFrame(this._data)) {
4033
- return this._data.allocationSize();
4121
+ return this._data.allocationSize(options);
4034
4122
  } else if (this._data instanceof Uint8Array) {
4035
4123
  return this._data.byteLength;
4036
4124
  } else {
4037
4125
  return this.codedWidth * this.codedHeight * 4;
4038
4126
  }
4039
4127
  }
4040
- /** Copies this video sample's pixel data to an ArrayBuffer or ArrayBufferView. */
4041
- async copyTo(destination) {
4128
+ /**
4129
+ * Copies this video sample's pixel data to an ArrayBuffer or ArrayBufferView. Throws if `format` is `null`;
4130
+ * specify an explicit RGB format in the options in this case.
4131
+ * @returns The byte layout of the planes of the copied data.
4132
+ */
4133
+ async copyTo(destination, options = {}) {
4042
4134
  if (!isAllowSharedBufferSource(destination)) {
4043
4135
  throw new TypeError("destination must be an ArrayBuffer or an ArrayBuffer view.");
4044
4136
  }
4137
+ validateVideoFrameCopyToOptions(options);
4045
4138
  if (this._closed) {
4046
4139
  throw new Error("VideoSample is closed.");
4047
4140
  }
4141
+ if ((options.format ?? this.format) === null) {
4142
+ throw new Error(
4143
+ "Cannot copy video sample data when format is null. Please manually provide an RGB pixel format in the options instead."
4144
+ );
4145
+ }
4048
4146
  assert(this._data !== null);
4147
+ if (!isVideoFrame(this._data)) {
4148
+ if (options.colorSpace || options.format && options.format !== this.format || options.layout || options.rect) {
4149
+ const videoFrame = this.toVideoFrame();
4150
+ const layout = await videoFrame.copyTo(destination, options);
4151
+ videoFrame.close();
4152
+ return layout;
4153
+ }
4154
+ }
4049
4155
  if (isVideoFrame(this._data)) {
4050
- await this._data.copyTo(destination);
4156
+ return this._data.copyTo(destination, options);
4051
4157
  } else if (this._data instanceof Uint8Array) {
4158
+ assert(this._layout);
4052
4159
  const dest = toUint8Array(destination);
4053
4160
  dest.set(this._data);
4161
+ return this._layout;
4054
4162
  } else {
4055
4163
  const canvas = this._data;
4056
4164
  const context = canvas.getContext("2d");
@@ -4058,6 +4166,10 @@ var Mediabunny = (() => {
4058
4166
  const imageData = context.getImageData(0, 0, this.codedWidth, this.codedHeight);
4059
4167
  const dest = toUint8Array(destination);
4060
4168
  dest.set(imageData.data);
4169
+ return [{
4170
+ offset: 0,
4171
+ stride: 4 * this.codedWidth
4172
+ }];
4061
4173
  }
4062
4174
  }
4063
4175
  /**
@@ -4303,6 +4415,24 @@ var Mediabunny = (() => {
4303
4415
  this.close();
4304
4416
  }
4305
4417
  };
4418
+ var VideoSampleColorSpace = class {
4419
+ /** Creates a new VideoSampleColorSpace. */
4420
+ constructor(init) {
4421
+ this.primaries = init?.primaries ?? null;
4422
+ this.transfer = init?.transfer ?? null;
4423
+ this.matrix = init?.matrix ?? null;
4424
+ this.fullRange = init?.fullRange ?? null;
4425
+ }
4426
+ /** Serializes the color space to a JSON object. */
4427
+ toJSON() {
4428
+ return {
4429
+ primaries: this.primaries,
4430
+ transfer: this.transfer,
4431
+ matrix: this.matrix,
4432
+ fullRange: this.fullRange
4433
+ };
4434
+ }
4435
+ };
4306
4436
  var isVideoFrame = (x) => {
4307
4437
  return typeof VideoFrame !== "undefined" && x instanceof VideoFrame;
4308
4438
  };
@@ -4331,6 +4461,128 @@ var Mediabunny = (() => {
4331
4461
  throw new TypeError(prefix + "crop.height must be a non-negative integer.");
4332
4462
  }
4333
4463
  };
4464
+ var validateVideoFrameCopyToOptions = (options) => {
4465
+ if (!options || typeof options !== "object") {
4466
+ throw new TypeError("options must be an object.");
4467
+ }
4468
+ if (options.colorSpace !== void 0 && !["display-p3", "srgb"].includes(options.colorSpace)) {
4469
+ throw new TypeError("options.colorSpace, when provided, must be 'display-p3' or 'srgb'.");
4470
+ }
4471
+ if (options.format !== void 0 && typeof options.format !== "string") {
4472
+ throw new TypeError("options.format, when provided, must be a string.");
4473
+ }
4474
+ if (options.layout !== void 0) {
4475
+ if (!Array.isArray(options.layout)) {
4476
+ throw new TypeError("options.layout, when provided, must be an array.");
4477
+ }
4478
+ for (const plane of options.layout) {
4479
+ if (!plane || typeof plane !== "object") {
4480
+ throw new TypeError("Each entry in options.layout must be an object.");
4481
+ }
4482
+ if (!Number.isInteger(plane.offset) || plane.offset < 0) {
4483
+ throw new TypeError("plane.offset must be a non-negative integer.");
4484
+ }
4485
+ if (!Number.isInteger(plane.stride) || plane.stride < 0) {
4486
+ throw new TypeError("plane.stride must be a non-negative integer.");
4487
+ }
4488
+ }
4489
+ }
4490
+ if (options.rect !== void 0) {
4491
+ if (!options.rect || typeof options.rect !== "object") {
4492
+ throw new TypeError("options.rect, when provided, must be an object.");
4493
+ }
4494
+ if (options.rect.x !== void 0 && (!Number.isInteger(options.rect.x) || options.rect.x < 0)) {
4495
+ throw new TypeError("options.rect.x, when provided, must be a non-negative integer.");
4496
+ }
4497
+ if (options.rect.y !== void 0 && (!Number.isInteger(options.rect.y) || options.rect.y < 0)) {
4498
+ throw new TypeError("options.rect.y, when provided, must be a non-negative integer.");
4499
+ }
4500
+ if (options.rect.width !== void 0 && (!Number.isInteger(options.rect.width) || options.rect.width < 0)) {
4501
+ throw new TypeError("options.rect.width, when provided, must be a non-negative integer.");
4502
+ }
4503
+ if (options.rect.height !== void 0 && (!Number.isInteger(options.rect.height) || options.rect.height < 0)) {
4504
+ throw new TypeError("options.rect.height, when provided, must be a non-negative integer.");
4505
+ }
4506
+ }
4507
+ };
4508
+ var createDefaultPlaneLayout = (format, codedWidth, codedHeight) => {
4509
+ const planes = getPlaneConfigs(format);
4510
+ const layouts = [];
4511
+ let currentOffset = 0;
4512
+ for (const plane of planes) {
4513
+ const planeWidth = Math.ceil(codedWidth / plane.widthDivisor);
4514
+ const planeHeight = Math.ceil(codedHeight / plane.heightDivisor);
4515
+ const stride = planeWidth * plane.sampleBytes;
4516
+ const planeSize = stride * planeHeight;
4517
+ layouts.push({
4518
+ offset: currentOffset,
4519
+ stride
4520
+ });
4521
+ currentOffset += planeSize;
4522
+ }
4523
+ return layouts;
4524
+ };
4525
+ var getPlaneConfigs = (format) => {
4526
+ const yuv = (yBytes, uvBytes, subX, subY, hasAlpha) => {
4527
+ const configs = [
4528
+ { sampleBytes: yBytes, widthDivisor: 1, heightDivisor: 1 },
4529
+ { sampleBytes: uvBytes, widthDivisor: subX, heightDivisor: subY },
4530
+ { sampleBytes: uvBytes, widthDivisor: subX, heightDivisor: subY }
4531
+ ];
4532
+ if (hasAlpha) {
4533
+ configs.push({ sampleBytes: yBytes, widthDivisor: 1, heightDivisor: 1 });
4534
+ }
4535
+ return configs;
4536
+ };
4537
+ switch (format) {
4538
+ case "I420":
4539
+ return yuv(1, 1, 2, 2, false);
4540
+ case "I420P10":
4541
+ case "I420P12":
4542
+ return yuv(2, 2, 2, 2, false);
4543
+ case "I420A":
4544
+ return yuv(1, 1, 2, 2, true);
4545
+ case "I420AP10":
4546
+ case "I420AP12":
4547
+ return yuv(2, 2, 2, 2, true);
4548
+ case "I422":
4549
+ return yuv(1, 1, 2, 1, false);
4550
+ case "I422P10":
4551
+ case "I422P12":
4552
+ return yuv(2, 2, 2, 1, false);
4553
+ case "I422A":
4554
+ return yuv(1, 1, 2, 1, true);
4555
+ case "I422AP10":
4556
+ case "I422AP12":
4557
+ return yuv(2, 2, 2, 1, true);
4558
+ case "I444":
4559
+ return yuv(1, 1, 1, 1, false);
4560
+ case "I444P10":
4561
+ case "I444P12":
4562
+ return yuv(2, 2, 1, 1, false);
4563
+ case "I444A":
4564
+ return yuv(1, 1, 1, 1, true);
4565
+ case "I444AP10":
4566
+ case "I444AP12":
4567
+ return yuv(2, 2, 1, 1, true);
4568
+ case "NV12":
4569
+ return [
4570
+ { sampleBytes: 1, widthDivisor: 1, heightDivisor: 1 },
4571
+ { sampleBytes: 2, widthDivisor: 2, heightDivisor: 2 }
4572
+ // Interleaved U and V
4573
+ ];
4574
+ case "RGBA":
4575
+ case "RGBX":
4576
+ case "BGRA":
4577
+ case "BGRX":
4578
+ return [
4579
+ { sampleBytes: 4, widthDivisor: 1, heightDivisor: 1 }
4580
+ ];
4581
+ default:
4582
+ assertNever(format);
4583
+ assert(false);
4584
+ }
4585
+ };
4334
4586
  var AUDIO_SAMPLE_FORMATS = /* @__PURE__ */ new Set(
4335
4587
  ["f32", "f32-planar", "s16", "s16-planar", "s32", "s32-planar", "u8", "u8-planar"]
4336
4588
  );
@@ -5086,7 +5338,6 @@ var Mediabunny = (() => {
5086
5338
  let terminated = false;
5087
5339
  let outOfBandError = null;
5088
5340
  (async () => {
5089
- const decoderError = new Error();
5090
5341
  const decoder = await this._createDecoder((sample) => {
5091
5342
  onQueueDequeue();
5092
5343
  if (sample.timestamp >= endTimestamp) {
@@ -5115,7 +5366,6 @@ var Mediabunny = (() => {
5115
5366
  }
5116
5367
  }, (error) => {
5117
5368
  if (!outOfBandError) {
5118
- error.stack = decoderError.stack;
5119
5369
  outOfBandError = error;
5120
5370
  onQueueNotEmpty();
5121
5371
  }
@@ -5225,7 +5475,6 @@ var Mediabunny = (() => {
5225
5475
  ({ promise: queueNotEmpty, resolve: onQueueNotEmpty } = promiseWithResolvers());
5226
5476
  };
5227
5477
  (async () => {
5228
- const decoderError = new Error();
5229
5478
  const decoder = await this._createDecoder((sample) => {
5230
5479
  onQueueDequeue();
5231
5480
  if (terminated) {
@@ -5246,7 +5495,6 @@ var Mediabunny = (() => {
5246
5495
  }
5247
5496
  }, (error) => {
5248
5497
  if (!outOfBandError) {
5249
- error.stack = decoderError.stack;
5250
5498
  outOfBandError = error;
5251
5499
  onQueueNotEmpty();
5252
5500
  }
@@ -5436,6 +5684,7 @@ var Mediabunny = (() => {
5436
5684
  }
5437
5685
  }
5438
5686
  }
5687
+ const stack = new Error("Decoding error").stack;
5439
5688
  this.decoder = new VideoDecoder({
5440
5689
  output: (frame) => {
5441
5690
  try {
@@ -5444,7 +5693,10 @@ var Mediabunny = (() => {
5444
5693
  this.onError(error);
5445
5694
  }
5446
5695
  },
5447
- error: onError
5696
+ error: (error) => {
5697
+ error.stack = stack;
5698
+ this.onError(error);
5699
+ }
5448
5700
  });
5449
5701
  this.decoder.configure(this.decoderConfig);
5450
5702
  }
@@ -5467,7 +5719,6 @@ var Mediabunny = (() => {
5467
5719
  }
5468
5720
  this.raslSkipped = true;
5469
5721
  }
5470
- this.currentPacketIndex++;
5471
5722
  if (this.customDecoder) {
5472
5723
  this.customDecoderQueueSize++;
5473
5724
  void this.customDecoderCallSerializer.call(() => this.customDecoder.decode(packet)).then(() => this.customDecoderQueueSize--);
@@ -5476,9 +5727,19 @@ var Mediabunny = (() => {
5476
5727
  if (!isWebKit()) {
5477
5728
  insertSorted(this.inputTimestamps, packet.timestamp, (x) => x);
5478
5729
  }
5730
+ if (isChromium() && this.currentPacketIndex === 0 && this.codec === "avc") {
5731
+ const nalUnits = extractAvcNalUnits(packet.data, this.decoderConfig);
5732
+ const filteredNalUnits = nalUnits.filter((x) => {
5733
+ const type = extractNalUnitTypeForAvc(x);
5734
+ return !(type >= 20 && type <= 31);
5735
+ });
5736
+ const newData = concatAvcNalUnits(filteredNalUnits, this.decoderConfig);
5737
+ packet = new EncodedPacket(newData, packet.type, packet.timestamp, packet.duration);
5738
+ }
5479
5739
  this.decoder.decode(packet.toEncodedVideoChunk());
5480
5740
  this.decodeAlphaData(packet);
5481
5741
  }
5742
+ this.currentPacketIndex++;
5482
5743
  }
5483
5744
  decodeAlphaData(packet) {
5484
5745
  if (!packet.sideData.alpha || this.mergerCreationFailed) {
@@ -5517,6 +5778,7 @@ var Mediabunny = (() => {
5517
5778
  }
5518
5779
  }
5519
5780
  };
5781
+ const stack = new Error("Decoding error").stack;
5520
5782
  this.alphaDecoder = new VideoDecoder({
5521
5783
  output: (frame) => {
5522
5784
  try {
@@ -5525,7 +5787,10 @@ var Mediabunny = (() => {
5525
5787
  this.onError(error);
5526
5788
  }
5527
5789
  },
5528
- error: this.onError
5790
+ error: (error) => {
5791
+ error.stack = stack;
5792
+ this.onError(error);
5793
+ }
5529
5794
  });
5530
5795
  this.alphaDecoder.configure(this.decoderConfig);
5531
5796
  }
@@ -6029,6 +6294,7 @@ var Mediabunny = (() => {
6029
6294
  };
6030
6295
  void this.customDecoderCallSerializer.call(() => this.customDecoder.init());
6031
6296
  } else {
6297
+ const stack = new Error("Decoding error").stack;
6032
6298
  this.decoder = new AudioDecoder({
6033
6299
  output: (data) => {
6034
6300
  try {
@@ -6037,7 +6303,10 @@ var Mediabunny = (() => {
6037
6303
  this.onError(error);
6038
6304
  }
6039
6305
  },
6040
- error: onError
6306
+ error: (error) => {
6307
+ error.stack = stack;
6308
+ this.onError(error);
6309
+ }
6041
6310
  });
6042
6311
  this.decoder.configure(decoderConfig);
6043
6312
  }
@@ -19297,13 +19566,13 @@ var Mediabunny = (() => {
19297
19566
  const trackData = this.getVideoTrackData(track, packet, meta);
19298
19567
  let packetData = packet.data;
19299
19568
  if (trackData.info.requiresAnnexBTransformation) {
19300
- const transformedData = transformAnnexBToLengthPrefixed(packetData);
19301
- if (!transformedData) {
19569
+ const nalUnits = findNalUnitsInAnnexB(packetData);
19570
+ if (nalUnits.length === 0) {
19302
19571
  throw new Error(
19303
19572
  "Failed to transform packet data. Make sure all packets are provided in Annex B format, as specified in ITU-T-REC-H.264 and ITU-T-REC-H.265."
19304
19573
  );
19305
19574
  }
19306
- packetData = transformedData;
19575
+ packetData = concatNalUnitsInLengthPrefixed(nalUnits, 4);
19307
19576
  }
19308
19577
  const timestamp = this.validateAndNormalizeTimestamp(
19309
19578
  trackData.track,
@@ -24525,6 +24794,11 @@ ${cue.notes ?? ""}`;
24525
24794
  if (videoOptions?.processedHeight !== void 0 && (!Number.isInteger(videoOptions.processedHeight) || videoOptions.processedHeight <= 0)) {
24526
24795
  throw new TypeError("options.video.processedHeight, when provided, must be a positive integer.");
24527
24796
  }
24797
+ if (videoOptions?.hardwareAcceleration !== void 0 && !["no-preference", "prefer-hardware", "prefer-software"].includes(videoOptions.hardwareAcceleration)) {
24798
+ throw new TypeError(
24799
+ "options.video.hardwareAcceleration, when provided, must be 'no-preference', 'prefer-hardware' or 'prefer-software'."
24800
+ );
24801
+ }
24528
24802
  };
24529
24803
  var validateAudioOptions = (audioOptions) => {
24530
24804
  if (audioOptions !== void 0 && (!audioOptions || typeof audioOptions !== "object")) {
@@ -24953,7 +25227,8 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
24953
25227
  bitrate,
24954
25228
  keyFrameInterval: trackOptions.keyFrameInterval,
24955
25229
  sizeChangeBehavior: trackOptions.fit ?? "passThrough",
24956
- alpha
25230
+ alpha,
25231
+ hardwareAcceleration: trackOptions.hardwareAcceleration
24957
25232
  };
24958
25233
  const source = new VideoSampleSource(encodingConfig);
24959
25234
  videoSource = source;