@remotion/webcodecs 4.0.287 → 4.0.288

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.
Files changed (78) hide show
  1. package/dist/audio-data/data-types.d.ts +1 -0
  2. package/dist/audio-data/data-types.js +26 -0
  3. package/dist/audio-data/is-planar-format.d.ts +1 -0
  4. package/dist/audio-data/is-planar-format.js +7 -0
  5. package/dist/audio-encoder.js +5 -1
  6. package/dist/can-reencode-audio-track.d.ts +2 -1
  7. package/dist/can-reencode-audio-track.js +2 -2
  8. package/dist/convert-audiodata.d.ts +10 -0
  9. package/dist/convert-audiodata.js +85 -0
  10. package/dist/create/iso-base-media/codec-specific/create-codec-specific-data.js +8 -0
  11. package/dist/create/iso-base-media/create-iso-base-media.js +2 -0
  12. package/dist/create/iso-base-media/example-stts.js +620 -0
  13. package/dist/create/matroska/create-matroska-media.js +1 -1
  14. package/dist/create/matroska/matroska-utils.d.ts +1 -1
  15. package/dist/default-on-audio-track-handler.js +2 -0
  16. package/dist/esm/index.mjs +176 -15
  17. package/dist/get-wave-audio-decoder.js +29 -1
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.js +3 -1
  20. package/dist/on-audio-track-handler.d.ts +1 -0
  21. package/dist/on-audio-track.js +3 -3
  22. package/dist/resample-audiodata.d.ts +4 -0
  23. package/dist/resample-audiodata.js +35 -0
  24. package/dist/wav-audio-encoder.d.ts +1 -1
  25. package/dist/wav-audio-encoder.js +11 -4
  26. package/package.json +12 -7
  27. package/dist/test/avc1.test.d.ts +0 -1
  28. package/dist/test/avc1.test.js +0 -39
  29. package/dist/test/avcc.test.d.ts +0 -1
  30. package/dist/test/avcc.test.js +0 -15
  31. package/dist/test/avi-to-mp4.test.d.ts +0 -1
  32. package/dist/test/avi-to-mp4.test.js +0 -15
  33. package/dist/test/cmt.test.d.ts +0 -1
  34. package/dist/test/cmt.test.js +0 -13
  35. package/dist/test/colr.test.d.ts +0 -1
  36. package/dist/test/colr.test.js +0 -16
  37. package/dist/test/correct-byte-length.test.d.ts +0 -1
  38. package/dist/test/correct-byte-length.test.js +0 -23
  39. package/dist/test/create-ftyp.test.d.ts +0 -1
  40. package/dist/test/create-ftyp.test.js +0 -47
  41. package/dist/test/create-mvhd.test.d.ts +0 -1
  42. package/dist/test/create-mvhd.test.js +0 -108
  43. package/dist/test/ctts.test.d.ts +0 -1
  44. package/dist/test/ctts.test.js +0 -49
  45. package/dist/test/dinf.test.d.ts +0 -1
  46. package/dist/test/dinf.test.js +0 -12
  47. package/dist/test/ilst.test.d.ts +0 -1
  48. package/dist/test/ilst.test.js +0 -22
  49. package/dist/test/mdhd.test.d.ts +0 -1
  50. package/dist/test/mdhd.test.js +0 -17
  51. package/dist/test/meta.test.d.ts +0 -1
  52. package/dist/test/meta.test.js +0 -26
  53. package/dist/test/mp4-header-length.test.d.ts +0 -1
  54. package/dist/test/mp4-header-length.test.js +0 -12
  55. package/dist/test/mp4a.test.d.ts +0 -1
  56. package/dist/test/mp4a.test.js +0 -24
  57. package/dist/test/pasp.test.d.ts +0 -1
  58. package/dist/test/pasp.test.js +0 -12
  59. package/dist/test/remux-serverside.test.d.ts +0 -1
  60. package/dist/test/remux-serverside.test.js +0 -23
  61. package/dist/test/stbl.test.d.ts +0 -0
  62. package/dist/test/stbl.test.js +0 -176
  63. package/dist/test/stco.test.d.ts +0 -1
  64. package/dist/test/stco.test.js +0 -34
  65. package/dist/test/stsc.test.d.ts +0 -1
  66. package/dist/test/stsc.test.js +0 -63
  67. package/dist/test/stss.test.d.ts +0 -1
  68. package/dist/test/stss.test.js +0 -14
  69. package/dist/test/stsz.test.d.ts +0 -1
  70. package/dist/test/stsz.test.js +0 -43
  71. package/dist/test/stts.test.d.ts +0 -1
  72. package/dist/test/stts.test.js +0 -12
  73. package/dist/test/tkhd.test.d.ts +0 -1
  74. package/dist/test/tkhd.test.js +0 -175
  75. package/dist/test/too.test.d.ts +0 -1
  76. package/dist/test/too.test.js +0 -12
  77. package/dist/test/url.test.d.ts +0 -1
  78. package/dist/test/url.test.js +0 -11
@@ -152,7 +152,7 @@ const createMatroskaMedia = async ({ writer, onBytesProgress, onMillisecondsProg
152
152
  }
153
153
  });
154
154
  },
155
- getBlob: async () => {
155
+ getBlob: () => {
156
156
  return w.getBlob();
157
157
  },
158
158
  remove: async () => {
@@ -19,7 +19,7 @@ export type EbmlParsedOrUint8Array<T extends Ebml> = {
19
19
  value: EbmlValueOrUint8Array<T>;
20
20
  minVintWidth: number | null;
21
21
  };
22
- export declare const measureEBMLVarInt: (value: number) => 2 | 1 | 5 | 4 | 3 | 6;
22
+ export declare const measureEBMLVarInt: (value: number) => 2 | 4 | 1 | 5 | 3 | 6;
23
23
  export declare const getVariableInt: (value: number, minWidth: number | null) => Uint8Array<ArrayBuffer>;
24
24
  export declare const makeMatroskaBytes: (fields: PossibleEbmlOrUint8Array) => BytesAndOffset;
25
25
  export type PossibleEbmlOrUint8Array = Prettify<{
@@ -19,6 +19,7 @@ const defaultOnAudioTrackHandler = async ({ track, defaultAudioCodec, logLevel,
19
19
  audioCodec: defaultAudioCodec,
20
20
  track,
21
21
  bitrate,
22
+ sampleRate: null,
22
23
  });
23
24
  if (canReencode) {
24
25
  media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (audio): Cannot copy, but re-encode, therefore re-encoding`);
@@ -26,6 +27,7 @@ const defaultOnAudioTrackHandler = async ({ track, defaultAudioCodec, logLevel,
26
27
  type: 'reencode',
27
28
  bitrate,
28
29
  audioCodec: defaultAudioCodec,
30
+ sampleRate: null,
29
31
  });
30
32
  }
31
33
  media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (audio): Can neither re-encode nor copy, failing render`);
@@ -428,6 +428,33 @@ var setRemotionImported = () => {
428
428
  };
429
429
 
430
430
  // src/get-wave-audio-decoder.ts
431
+ var getBytesPerSample = (sampleFormat) => {
432
+ if (sampleFormat === "s16") {
433
+ return 2;
434
+ }
435
+ if (sampleFormat === "s32") {
436
+ return 4;
437
+ }
438
+ if (sampleFormat === "f32") {
439
+ return 4;
440
+ }
441
+ if (sampleFormat === "u8") {
442
+ return 1;
443
+ }
444
+ if (sampleFormat === "f32-planar") {
445
+ return 4;
446
+ }
447
+ if (sampleFormat === "s16-planar") {
448
+ return 2;
449
+ }
450
+ if (sampleFormat === "s32-planar") {
451
+ return 4;
452
+ }
453
+ if (sampleFormat === "u8-planar") {
454
+ return 1;
455
+ }
456
+ throw new Error(`Unsupported sample format: ${sampleFormat}`);
457
+ };
431
458
  var getWaveAudioDecoder = ({
432
459
  onFrame,
433
460
  track,
@@ -435,11 +462,12 @@ var getWaveAudioDecoder = ({
435
462
  }) => {
436
463
  let queue = Promise.resolve();
437
464
  const processSample = async (audioSample) => {
465
+ const bytesPerSample = getBytesPerSample(sampleFormat);
438
466
  await onFrame(new AudioData({
439
467
  data: audioSample.data,
440
468
  format: sampleFormat,
441
469
  numberOfChannels: track.numberOfChannels,
442
- numberOfFrames: audioSample.data.byteLength / 2,
470
+ numberOfFrames: audioSample.data.byteLength / bytesPerSample / track.numberOfChannels,
443
471
  sampleRate: track.sampleRate,
444
472
  timestamp: audioSample.timestamp
445
473
  }));
@@ -778,25 +806,142 @@ var createAudioDecoder = ({
778
806
  // src/audio-encoder.ts
779
807
  import { MediaParserAbortError } from "@remotion/media-parser";
780
808
 
809
+ // src/audio-data/data-types.ts
810
+ var getDataTypeForAudioFormat = (format) => {
811
+ switch (format) {
812
+ case "f32":
813
+ return Float32Array;
814
+ case "f32-planar":
815
+ return Float32Array;
816
+ case "s16":
817
+ return Int16Array;
818
+ case "s16-planar":
819
+ return Int16Array;
820
+ case "u8":
821
+ return Uint8Array;
822
+ case "u8-planar":
823
+ return Uint8Array;
824
+ case "s32":
825
+ return Int32Array;
826
+ case "s32-planar":
827
+ return Int32Array;
828
+ default:
829
+ throw new Error(`Unsupported audio format: ${format}`);
830
+ }
831
+ };
832
+
833
+ // src/audio-data/is-planar-format.ts
834
+ var isPlanarFormat = (format) => {
835
+ return format.includes("-planar");
836
+ };
837
+
838
+ // src/convert-audiodata.ts
839
+ var validateRange = (format, value) => {
840
+ if (format === "f32" || format === "f32-planar") {
841
+ if (value < -1 || value > 1) {
842
+ throw new Error("All values in a Float32 array must be between -1 and 1");
843
+ }
844
+ }
845
+ };
846
+ var convertAudioData = ({
847
+ audioData,
848
+ newSampleRate = audioData.sampleRate,
849
+ format = audioData.format
850
+ }) => {
851
+ const {
852
+ numberOfChannels,
853
+ sampleRate: currentSampleRate,
854
+ numberOfFrames: currentNumberOfFrames
855
+ } = audioData;
856
+ const ratio = currentSampleRate / newSampleRate;
857
+ const newNumberOfFrames = Math.floor(currentNumberOfFrames / ratio);
858
+ if (newNumberOfFrames === 0) {
859
+ throw new Error("Cannot resample - the given sample rate would result in less than 1 sample");
860
+ }
861
+ if (newSampleRate < 3000 || newSampleRate > 768000) {
862
+ throw new Error("newSampleRate must be between 3000 and 768000");
863
+ }
864
+ if (!format) {
865
+ throw new Error("AudioData format is not set");
866
+ }
867
+ if (format === audioData.format && newNumberOfFrames === currentNumberOfFrames) {
868
+ return audioData.clone();
869
+ }
870
+ const DataType = getDataTypeForAudioFormat(format);
871
+ const isPlanar = isPlanarFormat(format);
872
+ const planes = isPlanar ? numberOfChannels : 1;
873
+ const srcChannels = new Array(planes).fill(true).map(() => new DataType((isPlanar ? 1 : numberOfChannels) * currentNumberOfFrames));
874
+ for (let i = 0;i < planes; i++) {
875
+ audioData.clone().copyTo(srcChannels[i], {
876
+ planeIndex: i,
877
+ format
878
+ });
879
+ }
880
+ const data = new DataType(newNumberOfFrames * numberOfChannels);
881
+ const chunkSize = currentNumberOfFrames / newNumberOfFrames;
882
+ for (let newFrameIndex = 0;newFrameIndex < newNumberOfFrames; newFrameIndex++) {
883
+ const start = Math.floor(newFrameIndex * chunkSize);
884
+ const end = Math.max(Math.floor(start + chunkSize), start + 1);
885
+ if (isPlanar) {
886
+ for (let channelIndex = 0;channelIndex < numberOfChannels; channelIndex++) {
887
+ const chunk = srcChannels[channelIndex].slice(start, end);
888
+ const average = chunk.reduce((a, b) => {
889
+ return a + b;
890
+ }, 0) / chunk.length;
891
+ validateRange(format, average);
892
+ data[newFrameIndex + channelIndex * newNumberOfFrames] = average;
893
+ }
894
+ } else {
895
+ const sampleCountAvg = end - start;
896
+ for (let channelIndex = 0;channelIndex < numberOfChannels; channelIndex++) {
897
+ const items = [];
898
+ for (let k = 0;k < sampleCountAvg; k++) {
899
+ const num = srcChannels[0][(start + k) * numberOfChannels + channelIndex];
900
+ items.push(num);
901
+ }
902
+ const average = items.reduce((a, b) => a + b, 0) / items.length;
903
+ validateRange(format, average);
904
+ data[newFrameIndex * numberOfChannels + channelIndex] = average;
905
+ }
906
+ }
907
+ }
908
+ const newAudioData = new AudioData({
909
+ data,
910
+ format,
911
+ numberOfChannels,
912
+ numberOfFrames: newNumberOfFrames,
913
+ sampleRate: newSampleRate,
914
+ timestamp: audioData.timestamp
915
+ });
916
+ return newAudioData;
917
+ };
918
+
781
919
  // src/wav-audio-encoder.ts
782
920
  var getWaveAudioEncoder = ({
783
921
  onChunk,
784
- controller
922
+ controller,
923
+ config
785
924
  }) => {
786
925
  return {
787
926
  close: () => {
788
927
  return Promise.resolve();
789
928
  },
790
- encodeFrame: (audioData) => {
929
+ encodeFrame: (unconvertedAudioData) => {
791
930
  if (controller._internals.signal.aborted) {
792
931
  return Promise.resolve();
793
932
  }
933
+ const audioData = convertAudioData({
934
+ audioData: unconvertedAudioData,
935
+ newSampleRate: config.sampleRate,
936
+ format: "s16"
937
+ });
938
+ unconvertedAudioData.close();
794
939
  const chunk = {
795
940
  timestamp: audioData.timestamp,
796
941
  duration: audioData.duration,
797
942
  type: "key",
798
- copyTo: (destination) => audioData.copyTo(destination, { planeIndex: 0, format: "s16" }),
799
- byteLength: audioData.allocationSize({ planeIndex: 0, format: "s16" })
943
+ copyTo: (destination) => audioData.copyTo(destination, { planeIndex: 0 }),
944
+ byteLength: audioData.allocationSize({ planeIndex: 0 })
800
945
  };
801
946
  return onChunk(chunk);
802
947
  },
@@ -820,7 +965,11 @@ var createAudioEncoder = ({
820
965
  throw new MediaParserAbortError("Not creating audio encoder, already aborted");
821
966
  }
822
967
  if (codec === "wav") {
823
- return getWaveAudioEncoder({ onChunk, controller });
968
+ return getWaveAudioEncoder({
969
+ onChunk,
970
+ controller,
971
+ config: audioEncoderConfig
972
+ });
824
973
  }
825
974
  const ioSynchronizer = makeIoSynchronizer({
826
975
  logLevel,
@@ -1017,7 +1166,8 @@ var getAudioEncoderConfig = async (config) => {
1017
1166
  var canReencodeAudioTrack = async ({
1018
1167
  track,
1019
1168
  audioCodec,
1020
- bitrate
1169
+ bitrate,
1170
+ sampleRate
1021
1171
  }) => {
1022
1172
  const audioDecoderConfig = await getAudioDecoderConfig(track);
1023
1173
  if (audioCodec === "wav" && audioDecoderConfig) {
@@ -1026,7 +1176,7 @@ var canReencodeAudioTrack = async ({
1026
1176
  const audioEncoderConfig = await getAudioEncoderConfig({
1027
1177
  codec: audioCodec,
1028
1178
  numberOfChannels: track.numberOfChannels,
1029
- sampleRate: track.sampleRate,
1179
+ sampleRate: sampleRate ?? track.sampleRate,
1030
1180
  bitrate
1031
1181
  });
1032
1182
  return Boolean(audioDecoderConfig && audioEncoderConfig);
@@ -1618,14 +1768,16 @@ var defaultOnAudioTrackHandler = async ({
1618
1768
  const canReencode = await canReencodeAudioTrack({
1619
1769
  audioCodec: defaultAudioCodec,
1620
1770
  track,
1621
- bitrate
1771
+ bitrate,
1772
+ sampleRate: null
1622
1773
  });
1623
1774
  if (canReencode) {
1624
1775
  MediaParserInternals2.Log.verbose(logLevel, `Track ${track.trackId} (audio): Cannot copy, but re-encode, therefore re-encoding`);
1625
1776
  return Promise.resolve({
1626
1777
  type: "reencode",
1627
1778
  bitrate,
1628
- audioCodec: defaultAudioCodec
1779
+ audioCodec: defaultAudioCodec,
1780
+ sampleRate: null
1629
1781
  });
1630
1782
  }
1631
1783
  MediaParserInternals2.Log.verbose(logLevel, `Track ${track.trackId} (audio): Can neither re-encode nor copy, failing render`);
@@ -1707,7 +1859,7 @@ var makeAudioTrackHandler = ({
1707
1859
  }
1708
1860
  const audioEncoderConfig = await getAudioEncoderConfig({
1709
1861
  numberOfChannels: track.numberOfChannels,
1710
- sampleRate: track.sampleRate,
1862
+ sampleRate: audioOperation.sampleRate ?? track.sampleRate,
1711
1863
  codec: audioOperation.audioCodec,
1712
1864
  bitrate: audioOperation.bitrate
1713
1865
  });
@@ -1727,7 +1879,7 @@ var makeAudioTrackHandler = ({
1727
1879
  }
1728
1880
  const codecPrivate = audioOperation.audioCodec === "aac" ? MediaParserInternals3.createAacCodecPrivate({
1729
1881
  audioObjectType: 2,
1730
- sampleRate: audioEncoderConfig.sampleRate,
1882
+ sampleRate: audioOperation.sampleRate ?? audioEncoderConfig.sampleRate,
1731
1883
  channelConfiguration: audioEncoderConfig.numberOfChannels,
1732
1884
  codecPrivate: null
1733
1885
  }) : null;
@@ -1735,7 +1887,7 @@ var makeAudioTrackHandler = ({
1735
1887
  type: "audio",
1736
1888
  codec: audioOperation.audioCodec === "wav" ? "pcm-s16" : audioOperation.audioCodec,
1737
1889
  numberOfChannels: audioEncoderConfig.numberOfChannels,
1738
- sampleRate: audioEncoderConfig.sampleRate,
1890
+ sampleRate: audioOperation.sampleRate ?? audioEncoderConfig.sampleRate,
1739
1891
  codecPrivate,
1740
1892
  timescale: track.timescale
1741
1893
  });
@@ -3066,6 +3218,9 @@ var createMp4a = ({
3066
3218
  var createCodecSpecificData = (track) => {
3067
3219
  if (track.type === "video") {
3068
3220
  if (track.codec === "h264") {
3221
+ if (!track.codecPrivate) {
3222
+ return new Uint8Array([]);
3223
+ }
3069
3224
  return createAvc1Data({
3070
3225
  avccBox: createAvccBox(track.codecPrivate),
3071
3226
  compressorName: "WebCodecs",
@@ -3079,6 +3234,9 @@ var createCodecSpecificData = (track) => {
3079
3234
  });
3080
3235
  }
3081
3236
  if (track.codec === "h265") {
3237
+ if (!track.codecPrivate) {
3238
+ return new Uint8Array([]);
3239
+ }
3082
3240
  return createHvc1Data({
3083
3241
  hvccBox: createHvccBox(track.codecPrivate),
3084
3242
  compressorName: "WebCodecs",
@@ -3781,7 +3939,9 @@ var createIsoBaseMedia = async ({
3781
3939
  cts: Math.round(chunk.cts / 1e6 * currentTrack.timescale),
3782
3940
  dts: Math.round(chunk.dts / 1e6 * currentTrack.timescale),
3783
3941
  duration: Math.round((chunk.duration ?? 0) / 1e6 * currentTrack.timescale),
3784
- size: chunk.data.length
3942
+ size: chunk.data.length,
3943
+ bigEndian: false,
3944
+ chunkSize: null
3785
3945
  };
3786
3946
  lastChunkWasVideo = isVideo;
3787
3947
  samplePositions[trackNumber].push(samplePositionToAdd);
@@ -4588,7 +4748,7 @@ var createMatroskaMedia = async ({
4588
4748
  }
4589
4749
  });
4590
4750
  },
4591
- getBlob: async () => {
4751
+ getBlob: () => {
4592
4752
  return w.getBlob();
4593
4753
  },
4594
4754
  remove: async () => {
@@ -5113,6 +5273,7 @@ export {
5113
5273
  createAudioEncoder,
5114
5274
  createAudioDecoder,
5115
5275
  convertMedia,
5276
+ convertAudioData,
5116
5277
  canReencodeVideoTrack,
5117
5278
  canReencodeAudioTrack,
5118
5279
  canCopyVideoTrack,
@@ -1,15 +1,43 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getWaveAudioDecoder = void 0;
4
+ const getBytesPerSample = (sampleFormat) => {
5
+ if (sampleFormat === 's16') {
6
+ return 2;
7
+ }
8
+ if (sampleFormat === 's32') {
9
+ return 4;
10
+ }
11
+ if (sampleFormat === 'f32') {
12
+ return 4;
13
+ }
14
+ if (sampleFormat === 'u8') {
15
+ return 1;
16
+ }
17
+ if (sampleFormat === 'f32-planar') {
18
+ return 4;
19
+ }
20
+ if (sampleFormat === 's16-planar') {
21
+ return 2;
22
+ }
23
+ if (sampleFormat === 's32-planar') {
24
+ return 4;
25
+ }
26
+ if (sampleFormat === 'u8-planar') {
27
+ return 1;
28
+ }
29
+ throw new Error(`Unsupported sample format: ${sampleFormat}`);
30
+ };
4
31
  // TODO: Should also be subject to throttling
5
32
  const getWaveAudioDecoder = ({ onFrame, track, sampleFormat, }) => {
6
33
  let queue = Promise.resolve();
7
34
  const processSample = async (audioSample) => {
35
+ const bytesPerSample = getBytesPerSample(sampleFormat);
8
36
  await onFrame(new AudioData({
9
37
  data: audioSample.data,
10
38
  format: sampleFormat,
11
39
  numberOfChannels: track.numberOfChannels,
12
- numberOfFrames: audioSample.data.byteLength / 2,
40
+ numberOfFrames: audioSample.data.byteLength / bytesPerSample / track.numberOfChannels,
13
41
  sampleRate: track.sampleRate,
14
42
  timestamp: audioSample.timestamp,
15
43
  }));
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export { canCopyAudioTrack } from './can-copy-audio-track';
6
6
  export { canCopyVideoTrack } from './can-copy-video-track';
7
7
  export { canReencodeAudioTrack } from './can-reencode-audio-track';
8
8
  export { canReencodeVideoTrack } from './can-reencode-video-track';
9
+ export { convertAudioData, ConvertAudioDataOptions } from './convert-audiodata';
9
10
  export { convertMedia } from './convert-media';
10
11
  export type { ConvertMediaOnAudioData, ConvertMediaOnProgress, ConvertMediaOnVideoFrame, ConvertMediaProgress, ConvertMediaResult, } from './convert-media';
11
12
  export { defaultOnAudioTrackHandler } from './default-on-audio-track-handler';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WebCodecsInternals = exports.webcodecsController = exports.createVideoEncoder = exports.createVideoDecoder = exports.getDefaultVideoCodec = exports.getDefaultAudioCodec = exports.getAvailableVideoCodecs = exports.getAvailableContainers = exports.getAvailableAudioCodecs = exports.defaultOnVideoTrackHandler = exports.defaultOnAudioTrackHandler = exports.convertMedia = exports.canReencodeVideoTrack = exports.canReencodeAudioTrack = exports.canCopyVideoTrack = exports.canCopyAudioTrack = exports.createAudioEncoder = exports.createAudioDecoder = void 0;
3
+ exports.WebCodecsInternals = exports.webcodecsController = exports.createVideoEncoder = exports.createVideoDecoder = exports.getDefaultVideoCodec = exports.getDefaultAudioCodec = exports.getAvailableVideoCodecs = exports.getAvailableContainers = exports.getAvailableAudioCodecs = exports.defaultOnVideoTrackHandler = exports.defaultOnAudioTrackHandler = exports.convertMedia = exports.convertAudioData = exports.canReencodeVideoTrack = exports.canReencodeAudioTrack = exports.canCopyVideoTrack = exports.canCopyAudioTrack = exports.createAudioEncoder = exports.createAudioDecoder = void 0;
4
4
  const rotate_and_resize_video_frame_1 = require("./rotate-and-resize-video-frame");
5
5
  const rotation_1 = require("./rotation");
6
6
  const set_remotion_imported_1 = require("./set-remotion-imported");
@@ -16,6 +16,8 @@ var can_reencode_audio_track_1 = require("./can-reencode-audio-track");
16
16
  Object.defineProperty(exports, "canReencodeAudioTrack", { enumerable: true, get: function () { return can_reencode_audio_track_1.canReencodeAudioTrack; } });
17
17
  var can_reencode_video_track_1 = require("./can-reencode-video-track");
18
18
  Object.defineProperty(exports, "canReencodeVideoTrack", { enumerable: true, get: function () { return can_reencode_video_track_1.canReencodeVideoTrack; } });
19
+ var convert_audiodata_1 = require("./convert-audiodata");
20
+ Object.defineProperty(exports, "convertAudioData", { enumerable: true, get: function () { return convert_audiodata_1.convertAudioData; } });
19
21
  var convert_media_1 = require("./convert-media");
20
22
  Object.defineProperty(exports, "convertMedia", { enumerable: true, get: function () { return convert_media_1.convertMedia; } });
21
23
  var default_on_audio_track_handler_1 = require("./default-on-audio-track-handler");
@@ -5,6 +5,7 @@ export type AudioOperation = {
5
5
  type: 'reencode';
6
6
  bitrate: number;
7
7
  audioCodec: ConvertMediaAudioCodec;
8
+ sampleRate: number | null;
8
9
  } | {
9
10
  type: 'copy';
10
11
  } | {
@@ -58,7 +58,7 @@ const makeAudioTrackHandler = ({ state, defaultAudioCodec: audioCodec, controlle
58
58
  }
59
59
  const audioEncoderConfig = await (0, audio_encoder_config_1.getAudioEncoderConfig)({
60
60
  numberOfChannels: track.numberOfChannels,
61
- sampleRate: track.sampleRate,
61
+ sampleRate: audioOperation.sampleRate ?? track.sampleRate,
62
62
  codec: audioOperation.audioCodec,
63
63
  bitrate: audioOperation.bitrate,
64
64
  });
@@ -79,7 +79,7 @@ const makeAudioTrackHandler = ({ state, defaultAudioCodec: audioCodec, controlle
79
79
  const codecPrivate = audioOperation.audioCodec === 'aac'
80
80
  ? media_parser_1.MediaParserInternals.createAacCodecPrivate({
81
81
  audioObjectType: 2,
82
- sampleRate: audioEncoderConfig.sampleRate,
82
+ sampleRate: audioOperation.sampleRate ?? audioEncoderConfig.sampleRate,
83
83
  channelConfiguration: audioEncoderConfig.numberOfChannels,
84
84
  codecPrivate: null,
85
85
  })
@@ -90,7 +90,7 @@ const makeAudioTrackHandler = ({ state, defaultAudioCodec: audioCodec, controlle
90
90
  ? 'pcm-s16'
91
91
  : audioOperation.audioCodec,
92
92
  numberOfChannels: audioEncoderConfig.numberOfChannels,
93
- sampleRate: audioEncoderConfig.sampleRate,
93
+ sampleRate: audioOperation.sampleRate ?? audioEncoderConfig.sampleRate,
94
94
  codecPrivate,
95
95
  timescale: track.timescale,
96
96
  });
@@ -0,0 +1,4 @@
1
+ export declare const resampleAudioData: ({ audioData, newSampleRate, }: {
2
+ audioData: AudioData;
3
+ newSampleRate: number;
4
+ }) => AudioData;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resampleAudioData = void 0;
4
+ const resampleAudioData = ({ audioData, newSampleRate, }) => {
5
+ const { numberOfChannels, sampleRate: currentSampleRate, numberOfFrames, } = audioData;
6
+ const ratio = currentSampleRate / newSampleRate;
7
+ const newNumberOfFrames = Math.floor(numberOfFrames / ratio);
8
+ // TODO: Float32Array hardcoded
9
+ const src = new Float32Array(numberOfChannels * numberOfFrames);
10
+ audioData.clone().copyTo(src, {
11
+ // TODO: Plane index hardcoded
12
+ planeIndex: 0,
13
+ });
14
+ const data = new Float32Array(newNumberOfFrames * numberOfChannels);
15
+ const chunkSize = numberOfFrames / newNumberOfFrames;
16
+ for (let i = 0; i < newNumberOfFrames; i++) {
17
+ const start = Math.floor(i * chunkSize);
18
+ const end = Math.max(Math.floor(start + chunkSize), start + 1);
19
+ const chunk = src.slice(start, end);
20
+ const average = chunk.reduce((a, b) => a + b, 0) / chunk.length;
21
+ for (let j = 0; j < numberOfChannels; j++) {
22
+ data[i * numberOfChannels + j] = average;
23
+ }
24
+ }
25
+ const newAudioData = new AudioData({
26
+ data,
27
+ format: audioData.format,
28
+ numberOfChannels,
29
+ numberOfFrames: newNumberOfFrames,
30
+ sampleRate: newSampleRate,
31
+ timestamp: audioData.timestamp,
32
+ });
33
+ return newAudioData;
34
+ };
35
+ exports.resampleAudioData = resampleAudioData;
@@ -1,2 +1,2 @@
1
1
  import type { AudioEncoderInit, WebCodecsAudioEncoder } from './audio-encoder';
2
- export declare const getWaveAudioEncoder: ({ onChunk, controller, }: Pick<AudioEncoderInit, "onChunk" | "controller">) => WebCodecsAudioEncoder;
2
+ export declare const getWaveAudioEncoder: ({ onChunk, controller, config, }: Pick<AudioEncoderInit, "onChunk" | "controller" | "config">) => WebCodecsAudioEncoder;
@@ -1,21 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getWaveAudioEncoder = void 0;
4
- const getWaveAudioEncoder = ({ onChunk, controller, }) => {
4
+ const convert_audiodata_1 = require("./convert-audiodata");
5
+ const getWaveAudioEncoder = ({ onChunk, controller, config, }) => {
5
6
  return {
6
7
  close: () => {
7
8
  return Promise.resolve();
8
9
  },
9
- encodeFrame: (audioData) => {
10
+ encodeFrame: (unconvertedAudioData) => {
10
11
  if (controller._internals.signal.aborted) {
11
12
  return Promise.resolve();
12
13
  }
14
+ const audioData = (0, convert_audiodata_1.convertAudioData)({
15
+ audioData: unconvertedAudioData,
16
+ newSampleRate: config.sampleRate,
17
+ format: 's16',
18
+ });
19
+ unconvertedAudioData.close();
13
20
  const chunk = {
14
21
  timestamp: audioData.timestamp,
15
22
  duration: audioData.duration,
16
23
  type: 'key',
17
- copyTo: (destination) => audioData.copyTo(destination, { planeIndex: 0, format: 's16' }),
18
- byteLength: audioData.allocationSize({ planeIndex: 0, format: 's16' }),
24
+ copyTo: (destination) => audioData.copyTo(destination, { planeIndex: 0 }),
25
+ byteLength: audioData.allocationSize({ planeIndex: 0 }),
19
26
  };
20
27
  return onChunk(chunk);
21
28
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/webcodecs",
3
- "version": "4.0.287",
3
+ "version": "4.0.288",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "module": "dist/esm/index.mjs",
@@ -12,20 +12,24 @@
12
12
  "url": "https://github.com/remotion-dev/remotion/issues"
13
13
  },
14
14
  "files": [
15
- "dist"
15
+ "dist",
16
+ "!dist/test",
17
+ "!dist/it-tests"
16
18
  ],
17
19
  "author": "Jonny Burger <jonny@remotion.dev>",
18
20
  "license": "Remotion License (See https://remotion.dev/docs/webcodecs#license)",
19
21
  "dependencies": {
20
- "@remotion/media-parser": "4.0.287",
21
- "@remotion/licensing": "4.0.287"
22
+ "@remotion/media-parser": "4.0.288",
23
+ "@remotion/licensing": "4.0.288"
22
24
  },
23
25
  "peerDependencies": {},
24
26
  "devDependencies": {
25
27
  "@types/dom-webcodecs": "0.1.11",
28
+ "playwright": "1.51.1",
29
+ "@playwright/test": "1.51.1",
26
30
  "eslint": "9.19.0",
27
- "@remotion/eslint-config-internal": "4.0.287",
28
- "@remotion/example-videos": "4.0.287"
31
+ "@remotion/eslint-config-internal": "4.0.288",
32
+ "@remotion/example-videos": "4.0.288"
29
33
  },
30
34
  "keywords": [],
31
35
  "publishConfig": {
@@ -76,7 +80,8 @@
76
80
  "scripts": {
77
81
  "formatting": "prettier src --check",
78
82
  "lint": "eslint src",
79
- "test": "bun test src",
83
+ "test": "bun test src/test",
84
+ "testwebcodecs": "playwright test src/it-tests",
80
85
  "watch": "tsc -w",
81
86
  "make": "tsc -d && bun --env-file=../.env.bundle bundle.ts"
82
87
  }
@@ -1 +0,0 @@
1
- export {};
@@ -1,39 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const bun_test_1 = require("bun:test");
4
- const avc1_1 = require("../create/iso-base-media/codec-specific/avc1");
5
- const create_avcc_1 = require("../create/iso-base-media/trak/mdia/minf/stbl/stsd/create-avcc");
6
- const create_pasp_1 = require("../create/iso-base-media/trak/mdia/minf/stbl/stsd/create-pasp");
7
- // bun segfaults here
8
- if (process.platform !== 'win32') {
9
- const reference = new Uint8Array([
10
- 0, 0, 0, 161, 97, 118, 99, 49, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
11
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 128, 1, 104, 0, 72, 0, 0, 0, 72, 0, 0, 0, 0,
12
- 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
13
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 255, 0, 0, 0, 59, 97, 118, 99,
14
- 67, 1, 100, 0, 30, 255, 225, 0, 30, 103, 100, 0, 30, 172, 217, 64, 160, 47,
15
- 249, 112, 22, 224, 64, 64, 180, 160, 0, 0, 3, 0, 32, 0, 0, 7, 129, 226, 197,
16
- 178, 192, 1, 0, 6, 104, 235, 224, 140, 178, 44, 253, 248, 248, 0, 0, 0, 0,
17
- 16, 112, 97, 115, 112, 0, 0, 0, 1, 0, 0, 0, 1,
18
- ]);
19
- (0, bun_test_1.test)('create avc1box', () => {
20
- const privateData = new Uint8Array([
21
- 0x01, 0x64, 0x00, 0x1e, 0xff, 0xe1, 0x00, 0x1e, 0x67, 0x64, 0x00, 0x1e,
22
- 0xac, 0xd9, 0x40, 0xa0, 0x2f, 0xf9, 0x70, 0x16, 0xe0, 0x40, 0x40, 0xb4,
23
- 0xa0, 0x00, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x07, 0x81, 0xe2, 0xc5,
24
- 0xb2, 0xc0, 0x01, 0x00, 0x06, 0x68, 0xeb, 0xe0, 0x8c, 0xb2, 0x2c, 0xfd,
25
- 0xf8, 0xf8, 0x00,
26
- ]);
27
- (0, bun_test_1.expect)((0, avc1_1.createAvc1Data)({
28
- pasp: (0, create_pasp_1.createPasp)(1, 1),
29
- avccBox: (0, create_avcc_1.createAvccBox)(privateData),
30
- width: 640,
31
- height: 360,
32
- horizontalResolution: 72,
33
- verticalResolution: 72,
34
- compressorName: '',
35
- depth: 24,
36
- type: 'avc1-data',
37
- })).toEqual(reference);
38
- });
39
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,15 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const bun_test_1 = require("bun:test");
4
- const create_avcc_1 = require("../create/iso-base-media/trak/mdia/minf/stbl/stsd/create-avcc");
5
- (0, bun_test_1.test)('avcc box', () => {
6
- (0, bun_test_1.expect)((0, create_avcc_1.createAvccBox)(new Uint8Array([
7
- 1, 100, 0, 32, 255, 225, 0, 27, 103, 100, 0, 32, 172, 217, 64, 68, 2,
8
- 39, 150, 92, 4, 64, 0, 0, 3, 0, 64, 0, 0, 12, 3, 198, 12, 101, 128, 1,
9
- 0, 6, 104, 235, 224, 140, 178, 44, 253, 248, 248, 0,
10
- ]))).toEqual(new Uint8Array([
11
- 0, 0, 0, 56, 97, 118, 99, 67, 1, 100, 0, 32, 255, 225, 0, 27, 103, 100, 0,
12
- 32, 172, 217, 64, 68, 2, 39, 150, 92, 4, 64, 0, 0, 3, 0, 64, 0, 0, 12, 3,
13
- 198, 12, 101, 128, 1, 0, 6, 104, 235, 224, 140, 178, 44, 253, 248, 248, 0,
14
- ]));
15
- });
@@ -1 +0,0 @@
1
- export {};