@remotion/media-parser 4.0.230 → 4.0.232

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 (110) hide show
  1. package/dist/add-avc-profile-to-track.d.ts +3 -0
  2. package/dist/add-avc-profile-to-track.js +35 -0
  3. package/dist/add-new-matroska-tracks.d.ts +6 -1
  4. package/dist/add-new-matroska-tracks.js +16 -1
  5. package/dist/boxes/avc/parse-avc.d.ts +18 -0
  6. package/dist/boxes/avc/parse-avc.js +96 -0
  7. package/dist/boxes/iso-base-media/make-track.js +3 -3
  8. package/dist/boxes/iso-base-media/mdat/mdat.d.ts +2 -2
  9. package/dist/boxes/iso-base-media/mdat/mdat.js +5 -2
  10. package/dist/boxes/iso-base-media/moov/moov.js +2 -2
  11. package/dist/boxes/iso-base-media/process-box.d.ts +5 -5
  12. package/dist/boxes/iso-base-media/process-box.js +38 -37
  13. package/dist/boxes/iso-base-media/stsd/mebx.js +2 -2
  14. package/dist/boxes/iso-base-media/stsd/samples.d.ts +2 -2
  15. package/dist/boxes/iso-base-media/stsd/samples.js +9 -9
  16. package/dist/boxes/iso-base-media/trak/trak.js +2 -2
  17. package/dist/boxes/iso-base-media/traversal.d.ts +1 -1
  18. package/dist/boxes/riff/expect-riff-box.d.ts +16 -0
  19. package/dist/boxes/riff/expect-riff-box.js +49 -0
  20. package/dist/boxes/riff/get-tracks-from-avi.d.ts +21 -0
  21. package/dist/boxes/riff/get-tracks-from-avi.js +108 -0
  22. package/dist/boxes/riff/is-movi.d.ts +2 -0
  23. package/dist/boxes/riff/is-movi.js +12 -0
  24. package/dist/boxes/riff/parse-avih.d.ts +6 -0
  25. package/dist/boxes/riff/parse-avih.js +32 -0
  26. package/dist/boxes/riff/parse-box.d.ts +13 -0
  27. package/dist/boxes/riff/parse-box.js +113 -0
  28. package/dist/boxes/riff/parse-fmt-box.d.ts +7 -0
  29. package/dist/boxes/riff/parse-fmt-box.js +33 -0
  30. package/dist/boxes/riff/parse-list-box.d.ts +8 -0
  31. package/dist/boxes/riff/parse-list-box.js +30 -0
  32. package/dist/boxes/riff/parse-movi.d.ts +17 -0
  33. package/dist/boxes/riff/parse-movi.js +122 -0
  34. package/dist/boxes/riff/parse-riff-box.d.ts +10 -0
  35. package/dist/boxes/riff/parse-riff-box.js +33 -0
  36. package/dist/boxes/riff/parse-strf.d.ts +7 -0
  37. package/dist/boxes/riff/parse-strf.js +67 -0
  38. package/dist/boxes/riff/parse-strh.d.ts +6 -0
  39. package/dist/boxes/riff/parse-strh.js +46 -0
  40. package/dist/boxes/riff/riff-box.d.ts +81 -0
  41. package/dist/boxes/riff/riff-box.js +2 -0
  42. package/dist/boxes/riff/strf.d.ts +7 -0
  43. package/dist/boxes/riff/strf.js +67 -0
  44. package/dist/boxes/riff/timescale.d.ts +1 -0
  45. package/dist/boxes/riff/timescale.js +4 -0
  46. package/dist/boxes/riff/traversal.d.ts +8 -0
  47. package/dist/boxes/riff/traversal.js +36 -0
  48. package/dist/boxes/webm/parse-ebml.js +2 -2
  49. package/dist/boxes/webm/parse-webm-header.d.ts +2 -2
  50. package/dist/boxes/webm/parse-webm-header.js +23 -4
  51. package/dist/boxes/webm/segments/parse-children.d.ts +12 -7
  52. package/dist/boxes/webm/segments/parse-children.js +67 -57
  53. package/dist/boxes/webm/segments.d.ts +8 -3
  54. package/dist/boxes/webm/segments.js +70 -39
  55. package/dist/boxes/webm/traversal.d.ts +2 -2
  56. package/dist/buffer-iterator.d.ts +6 -1
  57. package/dist/buffer-iterator.js +24 -5
  58. package/dist/create/iso-base-media/create-iso-base-media.d.ts +1 -1
  59. package/dist/create/iso-base-media/create-iso-base-media.js +4 -9
  60. package/dist/create/matroska/cluster.d.ts +7 -1
  61. package/dist/create/matroska/cluster.js +8 -5
  62. package/dist/create/matroska/create-matroska-media.d.ts +1 -1
  63. package/dist/create/matroska/create-matroska-media.js +27 -14
  64. package/dist/create/media-fn.d.ts +1 -1
  65. package/dist/create/mp3/create-mp3.d.ts +2 -0
  66. package/dist/create/mp3/create-mp3.js +49 -0
  67. package/dist/create/wav/create-wav.d.ts +2 -0
  68. package/dist/create/wav/create-wav.js +108 -0
  69. package/dist/emit-available-info.d.ts +2 -2
  70. package/dist/emit-available-info.js +28 -13
  71. package/dist/esm/buffer.mjs +2 -2
  72. package/dist/esm/from-node.mjs +2 -1
  73. package/dist/esm/index.mjs +1513 -331
  74. package/dist/esm/web-fs.mjs +2 -2
  75. package/dist/get-audio-codec.d.ts +3 -3
  76. package/dist/get-audio-codec.js +1 -6
  77. package/dist/get-container.d.ts +3 -3
  78. package/dist/get-container.js +9 -7
  79. package/dist/get-dimensions.d.ts +3 -3
  80. package/dist/get-duration.d.ts +8 -3
  81. package/dist/get-duration.js +37 -15
  82. package/dist/get-fps.d.ts +3 -3
  83. package/dist/get-fps.js +36 -2
  84. package/dist/get-tracks.d.ts +4 -7
  85. package/dist/get-tracks.js +55 -27
  86. package/dist/get-video-codec.d.ts +5 -4
  87. package/dist/get-video-codec.js +39 -15
  88. package/dist/has-all-info.d.ts +2 -2
  89. package/dist/has-all-info.js +9 -9
  90. package/dist/index.d.ts +5 -3
  91. package/dist/index.js +5 -1
  92. package/dist/options.d.ts +17 -10
  93. package/dist/parse-media.js +43 -14
  94. package/dist/parse-result.d.ts +35 -6
  95. package/dist/parse-video.d.ts +3 -3
  96. package/dist/parse-video.js +8 -16
  97. package/dist/parser-context.d.ts +1 -0
  98. package/dist/parser-state.d.ts +11 -0
  99. package/dist/parser-state.js +30 -0
  100. package/dist/readers/from-node.js +2 -1
  101. package/dist/readers/reader.d.ts +2 -2
  102. package/dist/register-track.d.ts +13 -0
  103. package/dist/register-track.js +25 -0
  104. package/dist/version.d.ts +1 -0
  105. package/dist/version.js +5 -0
  106. package/dist/writers/buffer-implementation/writer.d.ts +2 -2
  107. package/dist/writers/buffer-implementation/writer.js +2 -2
  108. package/dist/writers/web-fs.js +2 -3
  109. package/dist/writers/writer.d.ts +5 -3
  110. package/package.json +3 -3
@@ -1965,14 +1965,15 @@ var createIsoBaseMedia = async ({
1965
1965
  writer,
1966
1966
  onBytesProgress,
1967
1967
  onMillisecondsProgress,
1968
- logLevel
1968
+ logLevel,
1969
+ filename
1969
1970
  }) => {
1970
1971
  const header = createIsoBaseMediaFtyp({
1971
1972
  compatibleBrands: ["isom", "iso2", "avc1", "mp42"],
1972
1973
  majorBrand: "isom",
1973
1974
  minorBrand: 512
1974
1975
  });
1975
- const w = await writer.createContent();
1976
+ const w = await writer.createContent({ filename, mimeType: "video/mp4" });
1976
1977
  await w.write(header);
1977
1978
  let durationInUnits = 0;
1978
1979
  const currentTracks = [];
@@ -2072,9 +2073,8 @@ var createIsoBaseMedia = async ({
2072
2073
  };
2073
2074
  const waitForFinishPromises = [];
2074
2075
  return {
2075
- save: async () => {
2076
- const file = await w.save();
2077
- return file;
2076
+ save: () => {
2077
+ return w.save();
2078
2078
  },
2079
2079
  remove: async () => {
2080
2080
  await w.remove();
@@ -2117,10 +2117,6 @@ var createIsoBaseMedia = async ({
2117
2117
  await updateMdatSize();
2118
2118
  Log.verbose(logLevel, "All write operations done. Waiting for finish...");
2119
2119
  await w.waitForFinish();
2120
- },
2121
- updateDuration: (duration2) => {
2122
- operationProm.current = operationProm.current.then(() => updateDuration(duration2));
2123
- return operationProm.current;
2124
2120
  }
2125
2121
  };
2126
2122
  };
@@ -2182,11 +2178,17 @@ var canFitInCluster = ({
2182
2178
  }
2183
2179
  return timecodeRelativeToCluster <= maxClusterTimestamp;
2184
2180
  };
2185
- var makeCluster = async (w, clusterStartTimestamp, timescale) => {
2181
+ var makeCluster = async ({
2182
+ writer,
2183
+ clusterStartTimestamp,
2184
+ timescale,
2185
+ logLevel
2186
+ }) => {
2187
+ Log.verbose(logLevel, `Making new Matroska cluster with timestamp ${clusterStartTimestamp}`);
2186
2188
  const cluster = createClusterSegment(timestampToClusterTimestamp(clusterStartTimestamp, timescale));
2187
- const clusterVIntPosition = w.getWrittenByteCount() + cluster.offsets.offset + matroskaToHex(matroskaElements.Cluster).byteLength;
2189
+ const clusterVIntPosition = writer.getWrittenByteCount() + cluster.offsets.offset + matroskaToHex(matroskaElements.Cluster).byteLength;
2188
2190
  let clusterSize = cluster.bytes.byteLength - matroskaToHex(matroskaElements.Cluster).byteLength - CLUSTER_MIN_VINT_WIDTH;
2189
- await w.write(cluster.bytes);
2191
+ await writer.write(cluster.bytes);
2190
2192
  const addSample = async (chunk, trackNumber2) => {
2191
2193
  const timecodeRelativeToCluster = timestampToClusterTimestamp(chunk.timestamp, timescale) - timestampToClusterTimestamp(clusterStartTimestamp, timescale);
2192
2194
  if (!canFitInCluster({ clusterStartTimestamp, chunk, timescale })) {
@@ -2202,8 +2204,8 @@ var makeCluster = async (w, clusterStartTimestamp, timescale) => {
2202
2204
  timecodeRelativeToCluster
2203
2205
  });
2204
2206
  clusterSize += simpleBlock2.byteLength;
2205
- await w.updateDataAt(clusterVIntPosition, getVariableInt(clusterSize, CLUSTER_MIN_VINT_WIDTH));
2206
- await w.write(simpleBlock2);
2207
+ await writer.updateDataAt(clusterVIntPosition, getVariableInt(clusterSize, CLUSTER_MIN_VINT_WIDTH));
2208
+ await writer.write(simpleBlock2);
2207
2209
  return { timecodeRelativeToCluster };
2208
2210
  };
2209
2211
  const shouldMakeNewCluster = ({
@@ -2219,6 +2221,7 @@ var makeCluster = async (w, clusterStartTimestamp, timescale) => {
2219
2221
  timescale
2220
2222
  });
2221
2223
  if (!canFit) {
2224
+ Log.verbose(logLevel, `Cannot fit ${chunk.timestamp} in cluster ${clusterStartTimestamp}. Creating new cluster`);
2222
2225
  return true;
2223
2226
  }
2224
2227
  const keyframe = chunk.type === "key";
@@ -2938,10 +2941,12 @@ var timescale = 1e6;
2938
2941
  var createMatroskaMedia = async ({
2939
2942
  writer,
2940
2943
  onBytesProgress,
2941
- onMillisecondsProgress
2944
+ onMillisecondsProgress,
2945
+ filename,
2946
+ logLevel
2942
2947
  }) => {
2943
2948
  const header = makeMatroskaHeader();
2944
- const w = await writer.createContent();
2949
+ const w = await writer.createContent({ filename, mimeType: "video/webm" });
2945
2950
  await w.write(header.bytes);
2946
2951
  const matroskaInfo = makeMatroskaInfo({
2947
2952
  timescale
@@ -2993,7 +2998,12 @@ var createMatroskaMedia = async ({
2993
2998
  };
2994
2999
  await w.write(matroskaSegment.bytes);
2995
3000
  const clusterOffset = w.getWrittenByteCount();
2996
- let currentCluster = await makeCluster(w, 0, timescale);
3001
+ let currentCluster = await makeCluster({
3002
+ writer: w,
3003
+ clusterStartTimestamp: 0,
3004
+ timescale,
3005
+ logLevel
3006
+ });
2997
3007
  seeks.push({
2998
3008
  hexString: matroskaElements.Cluster,
2999
3009
  byte: clusterOffset - seekHeadOffset
@@ -3001,9 +3011,14 @@ var createMatroskaMedia = async ({
3001
3011
  const trackNumberProgresses = {};
3002
3012
  const getClusterOrMakeNew = async ({
3003
3013
  chunk,
3004
- isVideo
3014
+ isVideo,
3015
+ trackNumber: trackNumber2
3005
3016
  }) => {
3006
- const smallestProgress = Math.min(...Object.values(trackNumberProgresses));
3017
+ const trackProgressValues = Object.values(trackNumberProgresses);
3018
+ const smallestProgress = trackProgressValues.length === 0 ? 0 : Math.min(...trackProgressValues);
3019
+ if (chunk.type === "key") {
3020
+ trackNumberProgresses[trackNumber2] = chunk.timestamp;
3021
+ }
3007
3022
  if (!currentCluster.shouldMakeNewCluster({
3008
3023
  newT: smallestProgress,
3009
3024
  isVideo,
@@ -3011,7 +3026,12 @@ var createMatroskaMedia = async ({
3011
3026
  })) {
3012
3027
  return { cluster: currentCluster, isNew: false, smallestProgress };
3013
3028
  }
3014
- currentCluster = await makeCluster(w, smallestProgress, timescale);
3029
+ currentCluster = await makeCluster({
3030
+ writer: w,
3031
+ clusterStartTimestamp: smallestProgress,
3032
+ timescale,
3033
+ logLevel
3034
+ });
3015
3035
  return { cluster: currentCluster, isNew: true, smallestProgress };
3016
3036
  };
3017
3037
  const updateDuration = async (newDuration) => {
@@ -3024,10 +3044,10 @@ var createMatroskaMedia = async ({
3024
3044
  trackNumber: trackNumber2,
3025
3045
  isVideo
3026
3046
  }) => {
3027
- trackNumberProgresses[trackNumber2] = chunk.timestamp;
3028
3047
  const { cluster, isNew, smallestProgress } = await getClusterOrMakeNew({
3029
3048
  chunk,
3030
- isVideo
3049
+ isVideo,
3050
+ trackNumber: trackNumber2
3031
3051
  });
3032
3052
  const newDuration = Math.round((chunk.timestamp + (chunk.duration ?? 0)) / 1000);
3033
3053
  await updateDuration(newDuration);
@@ -3061,9 +3081,8 @@ var createMatroskaMedia = async ({
3061
3081
  }
3062
3082
  });
3063
3083
  },
3064
- save: async () => {
3065
- const file = await w.save();
3066
- return file;
3084
+ save: () => {
3085
+ return w.save();
3067
3086
  },
3068
3087
  remove: async () => {
3069
3088
  await w.remove();
@@ -3072,10 +3091,6 @@ var createMatroskaMedia = async ({
3072
3091
  operationProm.current = operationProm.current.then(() => addSample({ chunk, trackNumber: trackNumber2, isVideo }));
3073
3092
  return operationProm.current;
3074
3093
  },
3075
- updateDuration: (duration2) => {
3076
- operationProm.current = operationProm.current.then(() => updateDuration(duration2));
3077
- return operationProm.current;
3078
- },
3079
3094
  addTrack: (track) => {
3080
3095
  const trackNumber2 = currentTracks.length + 1;
3081
3096
  operationProm.current = operationProm.current.then(() => addTrack({ ...track, trackNumber: trackNumber2 }));
@@ -3101,6 +3116,119 @@ var createMatroskaMedia = async ({
3101
3116
  };
3102
3117
  };
3103
3118
 
3119
+ // src/create/wav/create-wav.ts
3120
+ var numberTo32BiIntLittleEndian = (num) => {
3121
+ return new Uint8Array([
3122
+ num & 255,
3123
+ num >> 8 & 255,
3124
+ num >> 16 & 255,
3125
+ num >> 24 & 255
3126
+ ]);
3127
+ };
3128
+ var numberTo16BitLittleEndian = (num) => {
3129
+ return new Uint8Array([num & 255, num >> 8 & 255]);
3130
+ };
3131
+ var BIT_DEPTH = 16;
3132
+ var BYTES_PER_SAMPLE = BIT_DEPTH / 8;
3133
+ var createWav = async ({
3134
+ filename,
3135
+ logLevel,
3136
+ onBytesProgress,
3137
+ onMillisecondsProgress,
3138
+ writer
3139
+ }) => {
3140
+ const w = await writer.createContent({ filename, mimeType: "audio/wav" });
3141
+ await w.write(new Uint8Array([82, 73, 70, 70]));
3142
+ const sizePosition = w.getWrittenByteCount();
3143
+ await w.write(new Uint8Array([0, 0, 0, 0]));
3144
+ await w.write(new Uint8Array([87, 65, 86, 69]));
3145
+ await w.write(new Uint8Array([102, 109, 116, 32]));
3146
+ await w.write(new Uint8Array([16, 0, 0, 0]));
3147
+ await w.write(new Uint8Array([1, 0]));
3148
+ const channelNumPosition = w.getWrittenByteCount();
3149
+ await w.write(new Uint8Array([1, 0]));
3150
+ const sampleRatePosition = w.getWrittenByteCount();
3151
+ await w.write(new Uint8Array([0, 0, 0, 0]));
3152
+ const byteRatePosition = w.getWrittenByteCount();
3153
+ await w.write(new Uint8Array([0, 0, 0, 0]));
3154
+ const blockAlignPosition = w.getWrittenByteCount();
3155
+ await w.write(new Uint8Array([0, 0]));
3156
+ await w.write(numberTo16BitLittleEndian(BIT_DEPTH));
3157
+ await w.write(new Uint8Array([100, 97, 116, 97]));
3158
+ const dataSizePosition = w.getWrittenByteCount();
3159
+ await w.write(new Uint8Array([0, 0, 0, 0]));
3160
+ const operationProm = { current: Promise.resolve() };
3161
+ const updateSize = async () => {
3162
+ const size = w.getWrittenByteCount() - sizePosition - 4;
3163
+ await w.updateDataAt(sizePosition, numberTo32BiIntLittleEndian(size));
3164
+ const dataSize = w.getWrittenByteCount() - dataSizePosition - 4;
3165
+ await w.updateDataAt(dataSizePosition, numberTo32BiIntLittleEndian(dataSize));
3166
+ };
3167
+ const updateChannelNum = async (numberOfChannels) => {
3168
+ await w.updateDataAt(channelNumPosition, new Uint8Array([numberOfChannels, 0]));
3169
+ };
3170
+ const updateSampleRate = async (sampleRate) => {
3171
+ await w.updateDataAt(sampleRatePosition, numberTo32BiIntLittleEndian(sampleRate));
3172
+ };
3173
+ const updateByteRate = async ({
3174
+ sampleRate,
3175
+ numberOfChannels
3176
+ }) => {
3177
+ await w.updateDataAt(byteRatePosition, numberTo32BiIntLittleEndian(sampleRate * numberOfChannels + BYTES_PER_SAMPLE));
3178
+ };
3179
+ const updateBlockAlign = async (numberOfChannels) => {
3180
+ await w.updateDataAt(blockAlignPosition, new Uint8Array(numberTo16BitLittleEndian(numberOfChannels * BYTES_PER_SAMPLE)));
3181
+ };
3182
+ const addSample = async (chunk) => {
3183
+ Log.verbose(logLevel, "Adding sample", chunk);
3184
+ await w.write(chunk.data);
3185
+ onMillisecondsProgress((chunk.timestamp + (chunk.duration ?? 0)) / 1000);
3186
+ onBytesProgress(w.getWrittenByteCount());
3187
+ };
3188
+ const waitForFinishPromises = [];
3189
+ return {
3190
+ save: () => {
3191
+ return w.save();
3192
+ },
3193
+ remove: () => {
3194
+ return w.remove();
3195
+ },
3196
+ addSample: ({ chunk, trackNumber: trackNumber2 }) => {
3197
+ if (trackNumber2 !== 1) {
3198
+ throw new Error("Only one track supported for WAV");
3199
+ }
3200
+ operationProm.current = operationProm.current.then(() => addSample(chunk));
3201
+ return operationProm.current;
3202
+ },
3203
+ updateTrackSampleRate: () => {
3204
+ throw new Error("updateTrackSampleRate() not implemented for WAV encoder");
3205
+ },
3206
+ addWaitForFinishPromise: (promise) => {
3207
+ waitForFinishPromises.push(promise);
3208
+ },
3209
+ async waitForFinish() {
3210
+ Log.verbose(logLevel, "All write operations queued. Waiting for finish...");
3211
+ await Promise.all(waitForFinishPromises.map((p) => p()));
3212
+ await operationProm.current;
3213
+ await updateSize();
3214
+ await w.waitForFinish();
3215
+ },
3216
+ addTrack: async (track) => {
3217
+ if (track.type !== "audio") {
3218
+ throw new Error("Only audio tracks supported for WAV");
3219
+ }
3220
+ await updateChannelNum(track.numberOfChannels);
3221
+ await updateSampleRate(track.sampleRate);
3222
+ await updateByteRate({
3223
+ sampleRate: track.sampleRate,
3224
+ numberOfChannels: track.numberOfChannels
3225
+ });
3226
+ await updateBlockAlign(track.numberOfChannels);
3227
+ return Promise.resolve({ trackNumber: 1 });
3228
+ }
3229
+ };
3230
+ };
3231
+
3104
3232
  // src/boxes/iso-base-media/traversal.ts
3105
3233
  var getMoovBox = (segments) => {
3106
3234
  const moovBox = segments.find((s) => s.type === "moov-box");
@@ -3266,6 +3394,34 @@ var getMdatBox = (anySegment) => {
3266
3394
  return mdat;
3267
3395
  };
3268
3396
 
3397
+ // src/boxes/riff/traversal.ts
3398
+ var isRiffAvi = (structure) => {
3399
+ return structure.boxes.some((box) => box.type === "riff-header" && box.fileType === "AVI");
3400
+ };
3401
+ var getHdlrBox = (structure) => {
3402
+ return structure.boxes.find((box) => box.type === "list-box" && box.listType === "hdrl");
3403
+ };
3404
+ var getAvihBox = (structure) => {
3405
+ const hdlrBox = getHdlrBox(structure);
3406
+ if (!hdlrBox) {
3407
+ return null;
3408
+ }
3409
+ return hdlrBox.children.find((box) => box.type === "avih-box");
3410
+ };
3411
+ var getStrlBoxes = (structure) => {
3412
+ const hdlrBox = getHdlrBox(structure);
3413
+ if (!hdlrBox) {
3414
+ return [];
3415
+ }
3416
+ return hdlrBox.children.filter((box) => box.type === "list-box" && box.listType === "strl");
3417
+ };
3418
+ var getStrhBox = (strlBoxChildren) => {
3419
+ return strlBoxChildren.find((box) => box.type === "strh-box");
3420
+ };
3421
+ var getStrfBox = (strlBoxChildren) => {
3422
+ return strlBoxChildren.find((box) => box.type === "strf-box-audio" || box.type === "strf-box-video") ?? null;
3423
+ };
3424
+
3269
3425
  // src/get-fps.ts
3270
3426
  var calculateFps = ({
3271
3427
  sttsBox,
@@ -3324,8 +3480,8 @@ var getFpsFromMp4TrakBox = (trakBox) => {
3324
3480
  durationInSamples: timescaleAndDuration.duration
3325
3481
  });
3326
3482
  };
3327
- var getFps = (segments) => {
3328
- const moovBox = getMoovBox(segments);
3483
+ var getFpsFromIsoMaseMedia = (structure) => {
3484
+ const moovBox = getMoovBox(structure.boxes);
3329
3485
  if (!moovBox) {
3330
3486
  return null;
3331
3487
  }
@@ -3336,8 +3492,37 @@ var getFps = (segments) => {
3336
3492
  }
3337
3493
  return getFpsFromMp4TrakBox(trackBox);
3338
3494
  };
3495
+ var getFpsFromAvi = (structure) => {
3496
+ const strl = getStrlBoxes(structure);
3497
+ for (const s of strl) {
3498
+ const strh = getStrhBox(s.children);
3499
+ if (!strh) {
3500
+ throw new Error("No strh box");
3501
+ }
3502
+ if (strh.fccType === "auds") {
3503
+ continue;
3504
+ }
3505
+ return strh.rate;
3506
+ }
3507
+ return null;
3508
+ };
3509
+ var getFps = (segments) => {
3510
+ if (segments.type === "iso-base-media") {
3511
+ return getFpsFromIsoMaseMedia(segments);
3512
+ }
3513
+ if (segments.type === "riff") {
3514
+ return getFpsFromAvi(segments);
3515
+ }
3516
+ if (segments.type === "matroska") {
3517
+ return null;
3518
+ }
3519
+ throw new Error("Cannot get fps, not implemented");
3520
+ };
3339
3521
  var hasFps = (boxes) => {
3340
3522
  try {
3523
+ if (boxes.type === "matroska") {
3524
+ return true;
3525
+ }
3341
3526
  return getFps(boxes) !== null;
3342
3527
  } catch {
3343
3528
  return false;
@@ -3361,11 +3546,7 @@ var getAudioCodec = (boxes, parserState) => {
3361
3546
  return null;
3362
3547
  };
3363
3548
  var hasAudioCodec = (boxes, state) => {
3364
- try {
3365
- return getAudioCodec(boxes, state) !== null;
3366
- } catch {
3367
- return false;
3368
- }
3549
+ return hasTracks(boxes, state);
3369
3550
  };
3370
3551
  var getCodecSpecificatorFromEsdsBox = ({
3371
3552
  child
@@ -3727,6 +3908,9 @@ var getArrayBufferIterator = (initialData, maxBytes) => {
3727
3908
  const allowDiscard = () => {
3728
3909
  discardAllowed = true;
3729
3910
  };
3911
+ const discard = (length) => {
3912
+ counter.increment(length);
3913
+ };
3730
3914
  const getUint8 = () => {
3731
3915
  const val = view.getUint8(counter.getDiscardedOffset());
3732
3916
  counter.increment(1);
@@ -3772,8 +3956,8 @@ var getArrayBufferIterator = (initialData, maxBytes) => {
3772
3956
  }
3773
3957
  return lastInt;
3774
3958
  };
3775
- const getUint32 = (littleEndian = false) => {
3776
- const val = view.getUint32(counter.getDiscardedOffset(), littleEndian);
3959
+ const getUint32 = () => {
3960
+ const val = view.getUint32(counter.getDiscardedOffset());
3777
3961
  counter.increment(4);
3778
3962
  return val;
3779
3963
  };
@@ -3782,6 +3966,18 @@ var getArrayBufferIterator = (initialData, maxBytes) => {
3782
3966
  counter.increment(8);
3783
3967
  return val;
3784
3968
  };
3969
+ const startBox = (size) => {
3970
+ const startOffset = counter.getOffset();
3971
+ return {
3972
+ discardRest: () => discard(size - (counter.getOffset() - startOffset)),
3973
+ expectNoMoreBytes: () => {
3974
+ const remaining = size - (counter.getOffset() - startOffset);
3975
+ if (remaining !== 0) {
3976
+ throw new Error("expected 0 bytes, got " + remaining);
3977
+ }
3978
+ }
3979
+ };
3980
+ };
3785
3981
  const getUint32Le = () => {
3786
3982
  const val = view.getUint32(counter.getDiscardedOffset(), true);
3787
3983
  counter.increment(4);
@@ -3925,9 +4121,7 @@ var getArrayBufferIterator = (initialData, maxBytes) => {
3925
4121
  leb128,
3926
4122
  removeBytesRead,
3927
4123
  isWebm,
3928
- discard: (length) => {
3929
- counter.increment(length);
3930
- },
4124
+ discard,
3931
4125
  getEightByteNumber,
3932
4126
  getFourByteNumber,
3933
4127
  getSlice,
@@ -4021,6 +4215,11 @@ var getArrayBufferIterator = (initialData, maxBytes) => {
4021
4215
  counter.increment(2);
4022
4216
  return val;
4023
4217
  },
4218
+ getUint16Le: () => {
4219
+ const val = view.getUint16(counter.getDiscardedOffset(), true);
4220
+ counter.increment(2);
4221
+ return val;
4222
+ },
4024
4223
  getUint24: () => {
4025
4224
  const val1 = view.getUint8(counter.getDiscardedOffset());
4026
4225
  const val2 = view.getUint8(counter.getDiscardedOffset() + 1);
@@ -4076,7 +4275,8 @@ var getArrayBufferIterator = (initialData, maxBytes) => {
4076
4275
  destroy,
4077
4276
  isMp3,
4078
4277
  disallowDiscard,
4079
- allowDiscard
4278
+ allowDiscard,
4279
+ startBox
4080
4280
  };
4081
4281
  };
4082
4282
 
@@ -4185,14 +4385,7 @@ var getVideoCodecFromIsoTrak = (trakBox) => {
4185
4385
  }
4186
4386
  throw new Error("Could not find video codec");
4187
4387
  };
4188
- var getVideoCodec = (boxes) => {
4189
- const moovBox = getMoovBox(boxes);
4190
- if (moovBox) {
4191
- const trakBox = getTraks(moovBox).filter((t) => trakBoxContainsVideo(t))[0];
4192
- if (trakBox) {
4193
- return getVideoCodecFromIsoTrak(trakBox);
4194
- }
4195
- }
4388
+ var getVideoCodecFromMatroska = (boxes) => {
4196
4389
  const mainSegment = boxes.find((b) => b.type === "Segment");
4197
4390
  if (!mainSegment || mainSegment.type !== "Segment") {
4198
4391
  return null;
@@ -4223,14 +4416,44 @@ var getVideoCodec = (boxes) => {
4223
4416
  }
4224
4417
  }
4225
4418
  }
4226
- return null;
4419
+ throw new Error("Could not find video codec");
4227
4420
  };
4228
- var hasVideoCodec = (boxes) => {
4229
- try {
4230
- return getVideoCodec(boxes) !== null;
4231
- } catch {
4232
- return false;
4421
+ var getVideoCodecFromAvi = (structure) => {
4422
+ const strl = getStrlBoxes(structure);
4423
+ for (const s of strl) {
4424
+ const strh = getStrhBox(s.children);
4425
+ if (!strh) {
4426
+ throw new Error("No strh box");
4427
+ }
4428
+ if (strh.fccType === "auds") {
4429
+ continue;
4430
+ }
4431
+ if (strh.handler === "H264") {
4432
+ return "h264";
4433
+ }
4434
+ }
4435
+ throw new Error("Unsupported codec");
4436
+ };
4437
+ var getVideoCodec = (boxes) => {
4438
+ if (boxes.type === "iso-base-media") {
4439
+ const moovBox = getMoovBox(boxes.boxes);
4440
+ if (moovBox) {
4441
+ const trakBox = getTraks(moovBox).filter((t) => trakBoxContainsVideo(t))[0];
4442
+ if (trakBox) {
4443
+ return getVideoCodecFromIsoTrak(trakBox);
4444
+ }
4445
+ }
4446
+ }
4447
+ if (boxes.type === "riff") {
4448
+ return getVideoCodecFromAvi(boxes);
4233
4449
  }
4450
+ if (boxes.type === "matroska") {
4451
+ return getVideoCodecFromMatroska(boxes.boxes);
4452
+ }
4453
+ return null;
4454
+ };
4455
+ var hasVideoCodec = (boxes, state) => {
4456
+ return hasTracks(boxes, state);
4234
4457
  };
4235
4458
  var getVideoPrivateData = (trakBox) => {
4236
4459
  const videoSample = getStsdVideoConfig(trakBox);
@@ -4321,7 +4544,7 @@ var makeBaseMediaTrack = (trakBox) => {
4321
4544
  sampleRate,
4322
4545
  description,
4323
4546
  trakBox,
4324
- codecPrivate: getCodecPrivateFromTrak(trakBox),
4547
+ codecPrivate: getCodecPrivateFromTrak(trakBox) ?? description ?? null,
4325
4548
  codecWithoutConfig: getAudioCodecFromTrack(trakBox)
4326
4549
  };
4327
4550
  }
@@ -4380,6 +4603,135 @@ var makeBaseMediaTrack = (trakBox) => {
4380
4603
  return track;
4381
4604
  };
4382
4605
 
4606
+ // src/add-avc-profile-to-track.ts
4607
+ var addAvcProfileToTrack = (track, avc1Profile) => {
4608
+ if (avc1Profile === null) {
4609
+ return track;
4610
+ }
4611
+ return {
4612
+ ...track,
4613
+ codec: `avc1.${avc1Profile.sps.profile.toString(16).padStart(2, "0")}${avc1Profile.sps.compatibility.toString(16).padStart(2, "0")}${avc1Profile.sps.level.toString(16).padStart(2, "0")}`,
4614
+ codecPrivate: combineUint8Arrays([
4615
+ new Uint8Array([
4616
+ 1,
4617
+ avc1Profile.sps.level,
4618
+ avc1Profile.sps.compatibility,
4619
+ avc1Profile.sps.profile,
4620
+ 255,
4621
+ 225
4622
+ ]),
4623
+ serializeUint16(avc1Profile.sps.sps.length),
4624
+ avc1Profile.sps.sps,
4625
+ new Uint8Array([1]),
4626
+ serializeUint16(avc1Profile.pps.pps.length),
4627
+ avc1Profile.pps.pps
4628
+ ])
4629
+ };
4630
+ };
4631
+
4632
+ // src/boxes/riff/timescale.ts
4633
+ var MEDIA_PARSER_RIFF_TIMESCALE = 1e6;
4634
+
4635
+ // src/boxes/riff/get-tracks-from-avi.ts
4636
+ var getNumberOfTracks = (structure) => {
4637
+ const avihBox = getAvihBox(structure);
4638
+ if (avihBox) {
4639
+ return avihBox.streams;
4640
+ }
4641
+ throw new Error("No avih box found");
4642
+ };
4643
+ var makeAviAudioTrack = ({
4644
+ strf,
4645
+ index
4646
+ }) => {
4647
+ if (strf.formatTag !== 255) {
4648
+ throw new Error(`Unsupported audio format ${strf.formatTag}`);
4649
+ }
4650
+ return {
4651
+ type: "audio",
4652
+ codec: "mp4a.40.2",
4653
+ codecPrivate: new Uint8Array([18, 16]),
4654
+ codecWithoutConfig: "aac",
4655
+ description: new Uint8Array([18, 16]),
4656
+ numberOfChannels: strf.numberOfChannels,
4657
+ sampleRate: strf.sampleRate,
4658
+ timescale: MEDIA_PARSER_RIFF_TIMESCALE,
4659
+ trackId: index,
4660
+ trakBox: null
4661
+ };
4662
+ };
4663
+ var makeAviVideoTrack = ({
4664
+ strh,
4665
+ strf,
4666
+ index
4667
+ }) => {
4668
+ if (strh.handler !== "H264") {
4669
+ throw new Error(`Unsupported video codec ${strh.handler}`);
4670
+ }
4671
+ return {
4672
+ codecPrivate: null,
4673
+ codec: "to-be-overriden-later",
4674
+ codecWithoutConfig: "h264",
4675
+ codedHeight: strf.height,
4676
+ codedWidth: strf.width,
4677
+ width: strf.width,
4678
+ height: strf.height,
4679
+ type: "video",
4680
+ displayAspectHeight: strf.height,
4681
+ timescale: MEDIA_PARSER_RIFF_TIMESCALE,
4682
+ description: undefined,
4683
+ trackId: index,
4684
+ color: {
4685
+ fullRange: null,
4686
+ matrixCoefficients: null,
4687
+ primaries: null,
4688
+ transferCharacteristics: null
4689
+ },
4690
+ displayAspectWidth: strf.width,
4691
+ trakBox: null,
4692
+ rotation: 0,
4693
+ sampleAspectRatio: {
4694
+ numerator: 1,
4695
+ denominator: 1
4696
+ },
4697
+ fps: strh.rate / strh.scale
4698
+ };
4699
+ };
4700
+ var getTracksFromAvi = (structure, state) => {
4701
+ if (!isRiffAvi(structure)) {
4702
+ throw new Error("Not an AVI file");
4703
+ }
4704
+ const videoTracks = [];
4705
+ const audioTracks = [];
4706
+ const otherTracks = [];
4707
+ const boxes = getStrlBoxes(structure);
4708
+ let i = 0;
4709
+ for (const box of boxes) {
4710
+ const strh = getStrhBox(box.children);
4711
+ const strf = getStrfBox(box.children);
4712
+ if (!strh || !strf) {
4713
+ continue;
4714
+ }
4715
+ if (strf.type === "strf-box-video") {
4716
+ videoTracks.push(addAvcProfileToTrack(makeAviVideoTrack({ strh, strf, index: i }), state.getAvcProfile()));
4717
+ } else if (strh.fccType === "auds") {
4718
+ audioTracks.push(makeAviAudioTrack({ strf, index: i }));
4719
+ } else {
4720
+ throw new Error(`Unsupported track type ${strh.fccType}`);
4721
+ }
4722
+ i++;
4723
+ }
4724
+ return { audioTracks, otherTracks, videoTracks };
4725
+ };
4726
+ var hasAllTracksFromAvi = (structure, state) => {
4727
+ if (!isRiffAvi(structure)) {
4728
+ throw new Error("Not an AVI file");
4729
+ }
4730
+ const numberOfTracks = getNumberOfTracks(structure);
4731
+ const tracks2 = getTracksFromAvi(structure, state);
4732
+ return tracks2.videoTracks.length + tracks2.audioTracks.length + tracks2.otherTracks.length === numberOfTracks;
4733
+ };
4734
+
4383
4735
  // src/make-hvc1-codec-strings.ts
4384
4736
  var getHvc1CodecString = (data) => {
4385
4737
  const configurationVersion = data.getUint8();
@@ -4753,48 +5105,63 @@ var getTracksFromMatroska = (segment, timescale2) => {
4753
5105
  };
4754
5106
 
4755
5107
  // src/get-tracks.ts
4756
- var getNumberOfTracks = (moovBox) => {
5108
+ var getNumberOfTracks2 = (moovBox) => {
4757
5109
  const mvHdBox = getMvhdBox(moovBox);
4758
5110
  if (!mvHdBox) {
4759
5111
  return 0;
4760
5112
  }
4761
5113
  return mvHdBox.nextTrackId - 1;
4762
5114
  };
4763
- var hasTracks = (segments) => {
4764
- const mainSegment = getMainSegment(segments);
4765
- if (mainSegment) {
5115
+ var hasTracks = (structure, state) => {
5116
+ if (structure.type === "matroska") {
5117
+ const mainSegment = getMainSegment(structure.boxes);
5118
+ if (!mainSegment) {
5119
+ throw new Error("No main segment found");
5120
+ }
4766
5121
  return getTracksSegment(mainSegment) !== null;
4767
5122
  }
4768
- const moovBox = getMoovBox(segments);
4769
- if (!moovBox) {
4770
- return false;
5123
+ if (structure.type === "iso-base-media") {
5124
+ const moovBox = getMoovBox(structure.boxes);
5125
+ if (!moovBox) {
5126
+ return false;
5127
+ }
5128
+ const numberOfTracks = getNumberOfTracks2(moovBox);
5129
+ const tracks2 = getTraks(moovBox);
5130
+ return tracks2.length === numberOfTracks;
4771
5131
  }
4772
- const numberOfTracks = getNumberOfTracks(moovBox);
4773
- const tracks2 = getTraks(moovBox);
4774
- return tracks2.length === numberOfTracks;
5132
+ if (structure.type === "riff") {
5133
+ return hasAllTracksFromAvi(structure, state);
5134
+ }
5135
+ throw new Error("Unknown container");
4775
5136
  };
4776
- var getTracks = (segments, state) => {
5137
+ var getTracksFromMa = (segments, state) => {
4777
5138
  const videoTracks = [];
4778
5139
  const audioTracks = [];
4779
5140
  const otherTracks = [];
4780
5141
  const mainSegment = segments.find((s) => s.type === "Segment");
4781
- if (mainSegment && mainSegment.type === "Segment") {
4782
- const matroskaTracks = getTracksFromMatroska(mainSegment, state.getTimescale());
4783
- for (const track of matroskaTracks) {
4784
- if (track.type === "video") {
4785
- videoTracks.push(track);
4786
- } else if (track.type === "audio") {
4787
- audioTracks.push(track);
4788
- } else if (track.type === "other") {
4789
- otherTracks.push(track);
4790
- }
5142
+ if (!mainSegment) {
5143
+ throw new Error("No main segment found");
5144
+ }
5145
+ const matroskaTracks = getTracksFromMatroska(mainSegment, state.getTimescale());
5146
+ for (const track of matroskaTracks) {
5147
+ if (track.type === "video") {
5148
+ videoTracks.push(track);
5149
+ } else if (track.type === "audio") {
5150
+ audioTracks.push(track);
5151
+ } else if (track.type === "other") {
5152
+ otherTracks.push(track);
4791
5153
  }
4792
- return {
4793
- videoTracks,
4794
- audioTracks,
4795
- otherTracks
4796
- };
4797
5154
  }
5155
+ return {
5156
+ videoTracks,
5157
+ audioTracks,
5158
+ otherTracks
5159
+ };
5160
+ };
5161
+ var getTracksFromIsoBaseMedia = (segments) => {
5162
+ const videoTracks = [];
5163
+ const audioTracks = [];
5164
+ const otherTracks = [];
4798
5165
  const moovBox = getMoovBox(segments);
4799
5166
  if (!moovBox) {
4800
5167
  return {
@@ -4823,18 +5190,33 @@ var getTracks = (segments, state) => {
4823
5190
  otherTracks
4824
5191
  };
4825
5192
  };
5193
+ var getTracks = (segments, state) => {
5194
+ if (segments.type === "matroska") {
5195
+ return getTracksFromMa(segments.boxes, state);
5196
+ }
5197
+ if (segments.type === "iso-base-media") {
5198
+ return getTracksFromIsoBaseMedia(segments.boxes);
5199
+ }
5200
+ if (segments.type === "riff") {
5201
+ return getTracksFromAvi(segments, state);
5202
+ }
5203
+ throw new Error("Unknown container");
5204
+ };
4826
5205
 
4827
5206
  // src/get-container.ts
4828
5207
  var getContainer = (segments) => {
4829
- const moovBox = getMoovBox(segments);
4830
- if (moovBox) {
5208
+ if (segments.type === "iso-base-media") {
4831
5209
  return "mp4";
4832
5210
  }
4833
- const mainSegment = getMainSegment(segments);
4834
- if (mainSegment) {
5211
+ if (segments.type === "matroska") {
4835
5212
  return "webm";
4836
5213
  }
4837
- return null;
5214
+ if (segments.type === "riff") {
5215
+ if (isRiffAvi(segments)) {
5216
+ return "avi";
5217
+ }
5218
+ }
5219
+ throw new Error("Unknown container");
4838
5220
  };
4839
5221
  var hasContainer = (boxes) => {
4840
5222
  try {
@@ -5058,16 +5440,12 @@ var getDurationFromMatroska = (segments) => {
5058
5440
  }
5059
5441
  return duration2.value.value / timestampScale2.value.value * 1000;
5060
5442
  };
5061
- var getDuration = (boxes, parserState) => {
5062
- const matroskaBox = boxes.find((b) => b.type === "Segment");
5063
- if (matroskaBox) {
5064
- return getDurationFromMatroska(boxes);
5065
- }
5066
- const moovBox = getMoovBox(boxes);
5443
+ var getDurationFromIsoBaseMedia = (structure, parserState) => {
5444
+ const moovBox = getMoovBox(structure.boxes);
5067
5445
  if (!moovBox) {
5068
5446
  return null;
5069
5447
  }
5070
- const moofBox = getMoofBox(boxes);
5448
+ const moofBox = getMoofBox(structure.boxes);
5071
5449
  const mvhdBox = getMvhdBox(moovBox);
5072
5450
  if (!mvhdBox) {
5073
5451
  return null;
@@ -5078,7 +5456,7 @@ var getDuration = (boxes, parserState) => {
5078
5456
  if (mvhdBox.durationInSeconds > 0) {
5079
5457
  return mvhdBox.durationInSeconds;
5080
5458
  }
5081
- const tracks2 = getTracks(boxes, parserState);
5459
+ const tracks2 = getTracks(structure, parserState);
5082
5460
  const allTracks = [
5083
5461
  ...tracks2.videoTracks,
5084
5462
  ...tracks2.audioTracks,
@@ -5093,13 +5471,34 @@ var getDuration = (boxes, parserState) => {
5093
5471
  const highestTimestamp = Math.max(...allSamples);
5094
5472
  return highestTimestamp;
5095
5473
  };
5096
- var hasDuration = (boxes, parserState) => {
5097
- try {
5098
- const duration2 = getDuration(boxes, parserState);
5099
- return getDuration(boxes, parserState) !== null && duration2 !== 0;
5100
- } catch {
5101
- return false;
5474
+ var getDurationFromAvi = (structure) => {
5475
+ const strl = getStrlBoxes(structure);
5476
+ const lengths = [];
5477
+ for (const s of strl) {
5478
+ const strh = getStrhBox(s.children);
5479
+ if (!strh) {
5480
+ throw new Error("No strh box");
5481
+ }
5482
+ const samplesPerSecond = strh.rate / strh.scale;
5483
+ const streamLength = strh.length / samplesPerSecond;
5484
+ lengths.push(streamLength);
5485
+ }
5486
+ return Math.max(...lengths);
5487
+ };
5488
+ var getDuration = (structure, parserState) => {
5489
+ if (structure.type === "matroska") {
5490
+ return getDurationFromMatroska(structure.boxes);
5491
+ }
5492
+ if (structure.type === "iso-base-media") {
5493
+ return getDurationFromIsoBaseMedia(structure, parserState);
5102
5494
  }
5495
+ if (structure.type === "riff") {
5496
+ return getDurationFromAvi(structure);
5497
+ }
5498
+ throw new Error("Has no duration");
5499
+ };
5500
+ var hasDuration = (structure, parserState) => {
5501
+ return hasTracks(structure, parserState);
5103
5502
  };
5104
5503
 
5105
5504
  // src/emit-available-info.ts
@@ -5114,15 +5513,15 @@ var emitAvailableInfo = ({
5114
5513
  }) => {
5115
5514
  const keys = Object.keys(hasInfo);
5116
5515
  for (const key of keys) {
5117
- if (key === "boxes") {
5118
- if (hasInfo.boxes && returnValue.boxes === undefined) {
5119
- moreFields.onBoxes?.(parseResult.segments);
5120
- returnValue.boxes = parseResult.segments;
5516
+ if (key === "structure") {
5517
+ if (parseResult && hasInfo.structure && returnValue.structure === undefined) {
5518
+ moreFields.onStructure?.(parseResult.segments);
5519
+ returnValue.structure = parseResult.segments;
5121
5520
  }
5122
5521
  continue;
5123
5522
  }
5124
5523
  if (key === "durationInSeconds") {
5125
- if (hasInfo.durationInSeconds && returnValue.durationInSeconds === undefined) {
5524
+ if (hasInfo.durationInSeconds && returnValue.durationInSeconds === undefined && parseResult) {
5126
5525
  const durationInSeconds = getDuration(parseResult.segments, state);
5127
5526
  moreFields.onDurationInSeconds?.(durationInSeconds);
5128
5527
  returnValue.durationInSeconds = durationInSeconds;
@@ -5130,7 +5529,7 @@ var emitAvailableInfo = ({
5130
5529
  continue;
5131
5530
  }
5132
5531
  if (key === "dimensions") {
5133
- if (hasInfo.dimensions && returnValue.dimensions === undefined) {
5532
+ if (hasInfo.dimensions && returnValue.dimensions === undefined && parseResult) {
5134
5533
  const dimensionsQueried = getDimensions(parseResult.segments, state);
5135
5534
  const dimensions = {
5136
5535
  height: dimensionsQueried.height,
@@ -5142,7 +5541,7 @@ var emitAvailableInfo = ({
5142
5541
  continue;
5143
5542
  }
5144
5543
  if (key === "unrotatedDimensions") {
5145
- if (returnValue.unrotatedDimensions === undefined && hasInfo.unrotatedDimensions) {
5544
+ if (returnValue.unrotatedDimensions === undefined && hasInfo.unrotatedDimensions && parseResult) {
5146
5545
  const dimensionsQueried = getDimensions(parseResult.segments, state);
5147
5546
  const unrotatedDimensions = {
5148
5547
  height: dimensionsQueried.unrotatedHeight,
@@ -5154,7 +5553,7 @@ var emitAvailableInfo = ({
5154
5553
  continue;
5155
5554
  }
5156
5555
  if (key === "rotation") {
5157
- if (returnValue.rotation === undefined && hasInfo.rotation) {
5556
+ if (returnValue.rotation === undefined && hasInfo.rotation && parseResult) {
5158
5557
  const dimensionsQueried = getDimensions(parseResult.segments, state);
5159
5558
  const { rotation } = dimensionsQueried;
5160
5559
  moreFields.onRotation?.(rotation);
@@ -5163,7 +5562,7 @@ var emitAvailableInfo = ({
5163
5562
  continue;
5164
5563
  }
5165
5564
  if (key === "fps") {
5166
- if (returnValue.fps === undefined && hasInfo.fps) {
5565
+ if (returnValue.fps === undefined && hasInfo.fps && parseResult) {
5167
5566
  const fps = getFps(parseResult.segments);
5168
5567
  moreFields.onFps?.(fps);
5169
5568
  returnValue.fps = fps;
@@ -5171,7 +5570,7 @@ var emitAvailableInfo = ({
5171
5570
  continue;
5172
5571
  }
5173
5572
  if (key === "videoCodec") {
5174
- if (returnValue.videoCodec === undefined && hasInfo.videoCodec) {
5573
+ if (returnValue.videoCodec === undefined && hasInfo.videoCodec && parseResult) {
5175
5574
  const videoCodec = getVideoCodec(parseResult.segments);
5176
5575
  moreFields.onVideoCodec?.(videoCodec);
5177
5576
  returnValue.videoCodec = videoCodec;
@@ -5179,7 +5578,7 @@ var emitAvailableInfo = ({
5179
5578
  continue;
5180
5579
  }
5181
5580
  if (key === "audioCodec") {
5182
- if (returnValue.audioCodec === undefined && hasInfo.audioCodec) {
5581
+ if (returnValue.audioCodec === undefined && hasInfo.audioCodec && parseResult) {
5183
5582
  const audioCodec = getAudioCodec(parseResult.segments, state);
5184
5583
  moreFields.onAudioCodec?.(audioCodec);
5185
5584
  returnValue.audioCodec = audioCodec;
@@ -5187,7 +5586,7 @@ var emitAvailableInfo = ({
5187
5586
  continue;
5188
5587
  }
5189
5588
  if (key === "tracks") {
5190
- if (hasInfo.tracks && returnValue.videoTracks === undefined && returnValue.audioTracks === undefined) {
5589
+ if (hasInfo.tracks && returnValue.videoTracks === undefined && returnValue.audioTracks === undefined && parseResult) {
5191
5590
  const { videoTracks, audioTracks } = getTracks(parseResult.segments, state);
5192
5591
  moreFields.onTracks?.({ videoTracks, audioTracks });
5193
5592
  returnValue.videoTracks = videoTracks;
@@ -5218,7 +5617,7 @@ var emitAvailableInfo = ({
5218
5617
  continue;
5219
5618
  }
5220
5619
  if (key === "container") {
5221
- if (returnValue.container === undefined && hasInfo.container) {
5620
+ if (returnValue.container === undefined && hasInfo.container && parseResult) {
5222
5621
  const container = getContainer(parseResult.segments);
5223
5622
  moreFields.onContainer?.(container);
5224
5623
  returnValue.container = container;
@@ -5233,26 +5632,26 @@ var emitAvailableInfo = ({
5233
5632
  var getAvailableInfo = (options, parseResult, state) => {
5234
5633
  const keys = Object.entries(options).filter(([, value]) => value);
5235
5634
  const infos = keys.map(([key]) => {
5236
- if (key === "boxes") {
5237
- return parseResult.status === "done";
5635
+ if (key === "structure") {
5636
+ return Boolean(parseResult && parseResult.status === "done");
5238
5637
  }
5239
5638
  if (key === "durationInSeconds") {
5240
- return hasDuration(parseResult.segments, state);
5639
+ return Boolean(parseResult && hasDuration(parseResult.segments, state));
5241
5640
  }
5242
5641
  if (key === "dimensions" || key === "rotation" || key === "unrotatedDimensions") {
5243
- return hasDimensions(parseResult.segments, state);
5642
+ return Boolean(parseResult && hasDimensions(parseResult.segments, state));
5244
5643
  }
5245
5644
  if (key === "fps") {
5246
- return hasFps(parseResult.segments);
5645
+ return Boolean(parseResult && hasFps(parseResult.segments));
5247
5646
  }
5248
5647
  if (key === "videoCodec") {
5249
- return hasVideoCodec(parseResult.segments);
5648
+ return Boolean(parseResult && hasVideoCodec(parseResult.segments, state));
5250
5649
  }
5251
5650
  if (key === "audioCodec") {
5252
- return hasAudioCodec(parseResult.segments, state);
5651
+ return Boolean(parseResult && hasAudioCodec(parseResult.segments, state));
5253
5652
  }
5254
5653
  if (key === "tracks") {
5255
- return hasTracks(parseResult.segments);
5654
+ return Boolean(parseResult && hasTracks(parseResult.segments, state));
5256
5655
  }
5257
5656
  if (key === "internalStats") {
5258
5657
  return false;
@@ -5264,7 +5663,7 @@ var getAvailableInfo = (options, parseResult, state) => {
5264
5663
  return true;
5265
5664
  }
5266
5665
  if (key === "container") {
5267
- return hasContainer(parseResult.segments);
5666
+ return Boolean(parseResult && hasContainer(parseResult.segments));
5268
5667
  }
5269
5668
  throw new Error(`Unknown key: ${key}`);
5270
5669
  });
@@ -5276,6 +5675,35 @@ var getAvailableInfo = (options, parseResult, state) => {
5276
5675
  return Object.fromEntries(entries);
5277
5676
  };
5278
5677
 
5678
+ // src/register-track.ts
5679
+ var registerTrack = async ({
5680
+ state,
5681
+ options,
5682
+ track
5683
+ }) => {
5684
+ if (track.type === "video" && options.onVideoTrack) {
5685
+ const callback = await options.onVideoTrack(track);
5686
+ await state.registerVideoSampleCallback(track.trackId, callback ?? null);
5687
+ }
5688
+ if (track.type === "audio" && options.onAudioTrack) {
5689
+ const callback = await options.onAudioTrack(track);
5690
+ await state.registerAudioSampleCallback(track.trackId, callback ?? null);
5691
+ }
5692
+ };
5693
+ var registerVideoTrackWhenProfileIsAvailable = ({
5694
+ options,
5695
+ state,
5696
+ track
5697
+ }) => {
5698
+ state.registerOnAvcProfileCallback(async (profile) => {
5699
+ await registerTrack({
5700
+ options,
5701
+ state,
5702
+ track: addAvcProfileToTrack(track, profile)
5703
+ });
5704
+ });
5705
+ };
5706
+
5279
5707
  // src/boxes/iso-base-media/esds/decoder-specific-config.ts
5280
5708
  var parseDecoderSpecificConfig = (iterator, logLevel) => {
5281
5709
  const layerTag = iterator.getUint8();
@@ -5450,7 +5878,10 @@ var parseMdat = async ({
5450
5878
  signal,
5451
5879
  maySkipSampleProcessing
5452
5880
  }) => {
5453
- const alreadyHas = hasTracks(existingBoxes);
5881
+ const alreadyHas = hasTracks({
5882
+ type: "iso-base-media",
5883
+ boxes: existingBoxes
5884
+ }, options.parserState);
5454
5885
  if (!alreadyHas) {
5455
5886
  if (maySkipSampleProcessing) {
5456
5887
  data.discard(size - (data.counter.getOffset() - fileOffset));
@@ -5470,7 +5901,7 @@ var parseMdat = async ({
5470
5901
  fileOffset
5471
5902
  });
5472
5903
  }
5473
- const tracks2 = getTracks(existingBoxes, options.parserState);
5904
+ const tracks2 = getTracks({ type: "iso-base-media", boxes: existingBoxes }, options.parserState);
5474
5905
  const allTracks = [
5475
5906
  ...tracks2.videoTracks,
5476
5907
  ...tracks2.audioTracks,
@@ -5589,7 +6020,7 @@ var parseMoov = async ({
5589
6020
  signal,
5590
6021
  logLevel
5591
6022
  }) => {
5592
- const children = await parseBoxes({
6023
+ const children = await parseIsoBaseMediaBoxes({
5593
6024
  iterator,
5594
6025
  maxBytes: size - (iterator.counter.getOffset() - offset),
5595
6026
  allowIncompleteBoxes: false,
@@ -5606,7 +6037,7 @@ var parseMoov = async ({
5606
6037
  offset,
5607
6038
  boxSize: size,
5608
6039
  type: "moov-box",
5609
- children: children.segments
6040
+ children: children.segments.boxes
5610
6041
  };
5611
6042
  };
5612
6043
 
@@ -5911,7 +6342,7 @@ var parseMebx = async ({
5911
6342
  }) => {
5912
6343
  iterator.discard(6);
5913
6344
  const dataReferenceIndex = iterator.getUint16();
5914
- const children = await parseBoxes({
6345
+ const children = await parseIsoBaseMediaBoxes({
5915
6346
  iterator,
5916
6347
  maxBytes: iterator.counter.getOffset() - offset,
5917
6348
  allowIncompleteBoxes: false,
@@ -5930,7 +6361,7 @@ var parseMebx = async ({
5930
6361
  offset,
5931
6362
  dataReferenceIndex,
5932
6363
  format: "mebx",
5933
- children: children.segments
6364
+ children: children.segments.boxes
5934
6365
  };
5935
6366
  };
5936
6367
 
@@ -6121,7 +6552,7 @@ var processSample = async ({
6121
6552
  const packetSize = iterator.getUint16();
6122
6553
  const sampleRate = iterator.getFixedPointUnsigned1616Number();
6123
6554
  const bytesRemainingInBox = boxSize - (iterator.counter.getOffset() - fileOffset);
6124
- const children = await parseBoxes({
6555
+ const children = await parseIsoBaseMediaBoxes({
6125
6556
  iterator,
6126
6557
  allowIncompleteBoxes: false,
6127
6558
  maxBytes: bytesRemainingInBox,
@@ -6153,7 +6584,7 @@ var processSample = async ({
6153
6584
  bytesPerPacket: null,
6154
6585
  bytesPerFrame: null,
6155
6586
  bitsPerSample: null,
6156
- children: children.segments
6587
+ children: children.segments.boxes
6157
6588
  }
6158
6589
  };
6159
6590
  }
@@ -6168,7 +6599,7 @@ var processSample = async ({
6168
6599
  const bytesPerFrame = iterator.getUint32();
6169
6600
  const bytesPerSample = iterator.getUint32();
6170
6601
  const bytesRemainingInBox = boxSize - (iterator.counter.getOffset() - fileOffset);
6171
- const children = await parseBoxes({
6602
+ const children = await parseIsoBaseMediaBoxes({
6172
6603
  iterator,
6173
6604
  allowIncompleteBoxes: false,
6174
6605
  maxBytes: bytesRemainingInBox,
@@ -6200,7 +6631,7 @@ var processSample = async ({
6200
6631
  bytesPerPacket,
6201
6632
  bytesPerFrame,
6202
6633
  bitsPerSample: bytesPerSample,
6203
- children: children.segments
6634
+ children: children.segments.boxes
6204
6635
  }
6205
6636
  };
6206
6637
  }
@@ -6219,7 +6650,7 @@ var processSample = async ({
6219
6650
  const bytesPerFrame = iterator.getUint32();
6220
6651
  const samplesPerPacket = iterator.getUint32();
6221
6652
  const bytesRemainingInBox = boxSize - (iterator.counter.getOffset() - fileOffset);
6222
- const children = await parseBoxes({
6653
+ const children = await parseIsoBaseMediaBoxes({
6223
6654
  iterator,
6224
6655
  allowIncompleteBoxes: false,
6225
6656
  maxBytes: bytesRemainingInBox,
@@ -6251,7 +6682,7 @@ var processSample = async ({
6251
6682
  bytesPerPacket: null,
6252
6683
  bytesPerFrame,
6253
6684
  bitsPerSample: bitsPerCodedSample,
6254
- children: children.segments
6685
+ children: children.segments.boxes
6255
6686
  }
6256
6687
  };
6257
6688
  }
@@ -6273,7 +6704,7 @@ var processSample = async ({
6273
6704
  const depth = iterator.getUint16();
6274
6705
  const colorTableId = iterator.getInt16();
6275
6706
  const bytesRemainingInBox = boxSize - (iterator.counter.getOffset() - fileOffset);
6276
- const children = bytesRemainingInBox > 8 ? await parseBoxes({
6707
+ const children = bytesRemainingInBox > 8 ? await parseIsoBaseMediaBoxes({
6277
6708
  iterator,
6278
6709
  allowIncompleteBoxes: false,
6279
6710
  maxBytes: bytesRemainingInBox,
@@ -6282,7 +6713,7 @@ var processSample = async ({
6282
6713
  continueMdat: false,
6283
6714
  signal,
6284
6715
  logLevel
6285
- }) : (iterator.discard(bytesRemainingInBox), { status: "done", segments: [] });
6716
+ }) : (iterator.discard(bytesRemainingInBox), { status: "done", segments: { boxes: [], type: "iso-base-media" } });
6286
6717
  if (children.status === "incomplete") {
6287
6718
  throw new Error("Incomplete boxes are not allowed");
6288
6719
  }
@@ -6307,7 +6738,7 @@ var processSample = async ({
6307
6738
  compressorName,
6308
6739
  depth,
6309
6740
  colorTableId,
6310
- descriptors: children.segments
6741
+ descriptors: children.segments.boxes
6311
6742
  }
6312
6743
  };
6313
6744
  }
@@ -6629,7 +7060,7 @@ var parseTrak = async ({
6629
7060
  signal,
6630
7061
  logLevel
6631
7062
  }) => {
6632
- const children = await parseBoxes({
7063
+ const children = await parseIsoBaseMediaBoxes({
6633
7064
  iterator: data,
6634
7065
  maxBytes: size - (data.counter.getOffset() - offsetAtStart),
6635
7066
  allowIncompleteBoxes: false,
@@ -6646,7 +7077,7 @@ var parseTrak = async ({
6646
7077
  offset: offsetAtStart,
6647
7078
  boxSize: size,
6648
7079
  type: "trak-box",
6649
- children: children.segments
7080
+ children: children.segments.boxes
6650
7081
  };
6651
7082
  };
6652
7083
 
@@ -6703,7 +7134,7 @@ var getChildren = async ({
6703
7134
  }) => {
6704
7135
  const parseChildren = boxType === "mdia" || boxType === "minf" || boxType === "stbl" || boxType === "moof" || boxType === "dims" || boxType === "wave" || boxType === "traf" || boxType === "stsb";
6705
7136
  if (parseChildren) {
6706
- const parsed = await parseBoxes({
7137
+ const parsed = await parseIsoBaseMediaBoxes({
6707
7138
  iterator,
6708
7139
  maxBytes: bytesRemainingInBox,
6709
7140
  allowIncompleteBoxes: false,
@@ -6716,7 +7147,7 @@ var getChildren = async ({
6716
7147
  if (parsed.status === "incomplete") {
6717
7148
  throw new Error("Incomplete boxes are not allowed");
6718
7149
  }
6719
- return parsed.segments;
7150
+ return parsed.segments.boxes;
6720
7151
  }
6721
7152
  if (bytesRemainingInBox < 0) {
6722
7153
  throw new Error("Box size is too big " + JSON.stringify({ boxType }));
@@ -6790,7 +7221,7 @@ var processBox = async ({
6790
7221
  const boxSize = boxSizeRaw === 1 ? iterator.getEightByteNumber() : boxSizeRaw;
6791
7222
  if (bytesRemaining < boxSize) {
6792
7223
  if (boxType === "mdat") {
6793
- const shouldSkip = (options.canSkipVideoData || !hasTracks(parsedBoxes)) && options.supportsContentRange;
7224
+ const shouldSkip = (options.canSkipVideoData || !hasTracks({ type: "iso-base-media", boxes: parsedBoxes }, options.parserState)) && options.supportsContentRange;
6794
7225
  if (shouldSkip) {
6795
7226
  const skipTo = fileOffset + boxSize;
6796
7227
  const bytesToSkip = skipTo - iterator.counter.getOffset();
@@ -7019,14 +7450,11 @@ var processBox = async ({
7019
7450
  });
7020
7451
  const transformedTrack = makeBaseMediaTrack(box);
7021
7452
  if (transformedTrack) {
7022
- if (transformedTrack.type === "audio") {
7023
- const callback = await options.onAudioTrack?.(transformedTrack);
7024
- await options.parserState.registerAudioSampleCallback(transformedTrack.trackId, callback ?? null);
7025
- }
7026
- if (transformedTrack.type === "video") {
7027
- const callback = await options.onVideoTrack?.(transformedTrack);
7028
- await options.parserState.registerVideoSampleCallback(transformedTrack.trackId, callback ?? null);
7029
- }
7453
+ await registerTrack({
7454
+ options,
7455
+ state: options.parserState,
7456
+ track: transformedTrack
7457
+ });
7030
7458
  }
7031
7459
  return {
7032
7460
  type: "complete",
@@ -7167,7 +7595,7 @@ var processBox = async ({
7167
7595
  skipTo: null
7168
7596
  };
7169
7597
  };
7170
- var parseBoxes = async ({
7598
+ var parseIsoBaseMediaBoxes = async ({
7171
7599
  iterator,
7172
7600
  maxBytes,
7173
7601
  allowIncompleteBoxes,
@@ -7177,9 +7605,12 @@ var parseBoxes = async ({
7177
7605
  signal,
7178
7606
  logLevel
7179
7607
  }) => {
7180
- let boxes = initialBoxes;
7608
+ const structure = {
7609
+ type: "iso-base-media",
7610
+ boxes: initialBoxes
7611
+ };
7181
7612
  const initialOffset = iterator.counter.getOffset();
7182
- const alreadyHasMdat = boxes.find((b) => b.type === "mdat-box");
7613
+ const alreadyHasMdat = structure.boxes.find((b) => b.type === "mdat-box");
7183
7614
  while (iterator.bytesRemaining() > 0 && iterator.counter.getOffset() - initialOffset < maxBytes) {
7184
7615
  const result = continueMdat ? await parseMdatPartially({
7185
7616
  iterator,
@@ -7202,13 +7633,13 @@ var parseBoxes = async ({
7202
7633
  }
7203
7634
  return {
7204
7635
  status: "incomplete",
7205
- segments: boxes,
7636
+ segments: structure,
7206
7637
  continueParsing: () => {
7207
- return parseBoxes({
7638
+ return parseIsoBaseMediaBoxes({
7208
7639
  iterator,
7209
7640
  maxBytes,
7210
7641
  allowIncompleteBoxes,
7211
- initialBoxes: boxes,
7642
+ initialBoxes: structure.boxes,
7212
7643
  options,
7213
7644
  continueMdat: false,
7214
7645
  signal,
@@ -7221,13 +7652,13 @@ var parseBoxes = async ({
7221
7652
  if (result.type === "partial-mdat-box") {
7222
7653
  return {
7223
7654
  status: "incomplete",
7224
- segments: boxes,
7655
+ segments: structure,
7225
7656
  continueParsing: () => {
7226
- return Promise.resolve(parseBoxes({
7657
+ return Promise.resolve(parseIsoBaseMediaBoxes({
7227
7658
  iterator,
7228
7659
  maxBytes,
7229
7660
  allowIncompleteBoxes,
7230
- initialBoxes: boxes,
7661
+ initialBoxes: structure.boxes,
7231
7662
  options,
7232
7663
  continueMdat: result,
7233
7664
  signal,
@@ -7238,15 +7669,15 @@ var parseBoxes = async ({
7238
7669
  };
7239
7670
  }
7240
7671
  if (result.box.type === "mdat-box" && alreadyHasMdat) {
7241
- boxes = boxes.filter((b) => b.type !== "mdat-box");
7242
- boxes.push(result.box);
7672
+ structure.boxes = structure.boxes.filter((b) => b.type !== "mdat-box");
7673
+ structure.boxes.push(result.box);
7243
7674
  iterator.allowDiscard();
7244
7675
  if (result.box.status !== "samples-processed") {
7245
7676
  throw new Error("unexpected");
7246
7677
  }
7247
7678
  break;
7248
7679
  } else {
7249
- boxes.push(result.box);
7680
+ structure.boxes.push(result.box);
7250
7681
  }
7251
7682
  if (result.skipTo !== null) {
7252
7683
  if (!options.supportsContentRange) {
@@ -7254,13 +7685,13 @@ var parseBoxes = async ({
7254
7685
  }
7255
7686
  return {
7256
7687
  status: "incomplete",
7257
- segments: boxes,
7688
+ segments: structure,
7258
7689
  continueParsing: () => {
7259
- return parseBoxes({
7690
+ return parseIsoBaseMediaBoxes({
7260
7691
  iterator,
7261
7692
  maxBytes,
7262
7693
  allowIncompleteBoxes,
7263
- initialBoxes: boxes,
7694
+ initialBoxes: structure.boxes,
7264
7695
  options,
7265
7696
  continueMdat: false,
7266
7697
  signal,
@@ -7273,13 +7704,13 @@ var parseBoxes = async ({
7273
7704
  if (iterator.bytesRemaining() < 0) {
7274
7705
  return {
7275
7706
  status: "incomplete",
7276
- segments: boxes,
7707
+ segments: structure,
7277
7708
  continueParsing: () => {
7278
- return parseBoxes({
7709
+ return parseIsoBaseMediaBoxes({
7279
7710
  iterator,
7280
7711
  maxBytes,
7281
7712
  allowIncompleteBoxes,
7282
- initialBoxes: boxes,
7713
+ initialBoxes: structure.boxes,
7283
7714
  options,
7284
7715
  continueMdat: false,
7285
7716
  signal,
@@ -7291,22 +7722,22 @@ var parseBoxes = async ({
7291
7722
  }
7292
7723
  iterator.removeBytesRead();
7293
7724
  }
7294
- const mdatState = getMdatBox(boxes);
7725
+ const mdatState = getMdatBox(structure.boxes);
7295
7726
  const skipped = mdatState?.status === "samples-skipped" && !options.canSkipVideoData && options.supportsContentRange;
7296
7727
  const buffered = mdatState?.status === "samples-buffered" && !options.canSkipVideoData;
7297
7728
  if (skipped || buffered) {
7298
7729
  return {
7299
7730
  status: "incomplete",
7300
- segments: boxes,
7731
+ segments: structure,
7301
7732
  continueParsing: () => {
7302
7733
  if (buffered) {
7303
7734
  iterator.skipTo(mdatState.fileOffset, false);
7304
7735
  }
7305
- return parseBoxes({
7736
+ return parseIsoBaseMediaBoxes({
7306
7737
  iterator,
7307
7738
  maxBytes,
7308
7739
  allowIncompleteBoxes: false,
7309
- initialBoxes: boxes,
7740
+ initialBoxes: structure.boxes,
7310
7741
  options,
7311
7742
  continueMdat: false,
7312
7743
  signal,
@@ -7318,24 +7749,651 @@ var parseBoxes = async ({
7318
7749
  }
7319
7750
  return {
7320
7751
  status: "done",
7321
- segments: boxes
7752
+ segments: structure
7322
7753
  };
7323
7754
  };
7324
7755
 
7325
- // src/add-new-matroska-tracks.ts
7326
- var registerTrack = async ({
7327
- state,
7328
- options,
7329
- track
7330
- }) => {
7331
- if (track.type === "video" && options.onVideoTrack) {
7332
- const callback = await options.onVideoTrack(track);
7333
- await state.registerVideoSampleCallback(track.trackId, callback ?? null);
7756
+ // src/boxes/riff/is-movi.ts
7757
+ var isMoviAtom = (iterator, ckId) => {
7758
+ if (ckId !== "LIST") {
7759
+ return false;
7334
7760
  }
7335
- if (track.type === "audio" && options.onAudioTrack) {
7336
- const callback = await options.onAudioTrack(track);
7337
- await state.registerAudioSampleCallback(track.trackId, callback ?? null);
7761
+ const listType = iterator.getByteString(4);
7762
+ iterator.counter.decrement(4);
7763
+ return listType === "movi";
7764
+ };
7765
+
7766
+ // src/boxes/avc/parse-avc.ts
7767
+ var readSps = (iterator) => {
7768
+ const profile = iterator.getUint8();
7769
+ const compatibility = iterator.getUint8();
7770
+ const level = iterator.getUint8();
7771
+ return {
7772
+ profile,
7773
+ compatibility,
7774
+ level
7775
+ };
7776
+ };
7777
+ var findEnd = (buffer) => {
7778
+ let zeroesInARow = 0;
7779
+ for (let i = 0;i < buffer.length; i++) {
7780
+ const val = buffer[i];
7781
+ if (val === 0) {
7782
+ zeroesInARow++;
7783
+ continue;
7784
+ }
7785
+ if (zeroesInARow >= 2 && val === 1) {
7786
+ return i - zeroesInARow;
7787
+ }
7788
+ zeroesInARow = 0;
7789
+ }
7790
+ return null;
7791
+ };
7792
+ var inspect = (buffer) => {
7793
+ const iterator = getArrayBufferIterator(buffer, buffer.byteLength);
7794
+ iterator.startReadingBits();
7795
+ iterator.getBits(1);
7796
+ iterator.getBits(2);
7797
+ const type = iterator.getBits(5);
7798
+ iterator.stopReadingBits();
7799
+ if (type === 7) {
7800
+ const end = findEnd(buffer);
7801
+ const data = readSps(iterator);
7802
+ const sps = buffer.slice(1, end === null ? Infinity : end);
7803
+ return {
7804
+ level: data.level,
7805
+ profile: data.profile,
7806
+ compatibility: data.compatibility,
7807
+ sps,
7808
+ type: "avc-profile"
7809
+ };
7810
+ }
7811
+ if (type === 5) {
7812
+ return {
7813
+ type: "keyframe"
7814
+ };
7815
+ }
7816
+ if (type === 8) {
7817
+ const end = findEnd(buffer);
7818
+ const pps = buffer.slice(0, end === null ? Infinity : end);
7819
+ return {
7820
+ type: "avc-pps",
7821
+ pps
7822
+ };
7823
+ }
7824
+ if (type === 1) {
7825
+ return {
7826
+ type: "delta-frame"
7827
+ };
7828
+ }
7829
+ iterator.destroy();
7830
+ return null;
7831
+ };
7832
+ var parseAvc = (buffer) => {
7833
+ let zeroesInARow = 0;
7834
+ const infos = [];
7835
+ for (let i = 0;i < buffer.length; i++) {
7836
+ const val = buffer[i];
7837
+ if (val === 0) {
7838
+ zeroesInARow++;
7839
+ continue;
7840
+ }
7841
+ if (zeroesInARow >= 2 && val === 1) {
7842
+ zeroesInARow = 0;
7843
+ const info = inspect(buffer.slice(i + 1, i + 100));
7844
+ if (info) {
7845
+ infos.push(info);
7846
+ if (info.type === "keyframe" || info.type === "delta-frame") {
7847
+ break;
7848
+ }
7849
+ }
7850
+ }
7851
+ if (val !== 1) {
7852
+ zeroesInARow = 0;
7853
+ }
7854
+ }
7855
+ return infos;
7856
+ };
7857
+
7858
+ // src/boxes/riff/parse-movi.ts
7859
+ var getStrhForIndex = (structure, trackId) => {
7860
+ const boxes = getStrlBoxes(structure);
7861
+ const box = boxes[trackId];
7862
+ if (!box) {
7863
+ throw new Error("Expected box");
7864
+ }
7865
+ const strh = getStrhBox(box.children);
7866
+ if (!strh) {
7867
+ throw new Error("strh");
7868
+ }
7869
+ return strh;
7870
+ };
7871
+ var handleChunk = async ({
7872
+ iterator,
7873
+ options,
7874
+ structure,
7875
+ ckId,
7876
+ ckSize
7877
+ }) => {
7878
+ const videoChunk = ckId.match(/^([0-9]{2})dc$/);
7879
+ if (videoChunk) {
7880
+ const trackId = parseInt(videoChunk[1], 10);
7881
+ const strh = getStrhForIndex(structure, trackId);
7882
+ const samplesPerSecond = strh.rate / strh.scale;
7883
+ const nthSample = options.parserState.getSamplesForTrack(trackId);
7884
+ const timeInSec = nthSample / samplesPerSecond;
7885
+ const timestamp = Math.floor(timeInSec * MEDIA_PARSER_RIFF_TIMESCALE);
7886
+ const duration2 = Math.floor(1 / samplesPerSecond * MEDIA_PARSER_RIFF_TIMESCALE);
7887
+ const data = iterator.getSlice(ckSize);
7888
+ const infos = parseAvc(data);
7889
+ const keyOrDelta = infos.find((i) => i.type === "keyframe" || i.type === "delta-frame");
7890
+ if (!keyOrDelta) {
7891
+ throw new Error("expected avc to contain info about key or delta");
7892
+ }
7893
+ const avcProfile = infos.find((i) => i.type === "avc-profile");
7894
+ const ppsProfile = infos.find((i) => i.type === "avc-pps");
7895
+ if (avcProfile && ppsProfile) {
7896
+ await options.parserState.onProfile({ pps: ppsProfile, sps: avcProfile });
7897
+ }
7898
+ await options.parserState.onVideoSample(trackId, {
7899
+ cts: timestamp,
7900
+ dts: timestamp,
7901
+ data,
7902
+ duration: duration2,
7903
+ timestamp,
7904
+ trackId,
7905
+ type: keyOrDelta.type === "keyframe" ? "key" : "delta"
7906
+ });
7907
+ } else {
7908
+ const audioChunk = ckId.match(/^([0-9]{2})wb$/);
7909
+ if (audioChunk) {
7910
+ const trackId = parseInt(audioChunk[1], 10);
7911
+ const strh = getStrhForIndex(structure, trackId);
7912
+ const samplesPerSecond = strh.rate / strh.scale;
7913
+ const nthSample = options.parserState.getSamplesForTrack(trackId);
7914
+ const timeInSec = nthSample / samplesPerSecond;
7915
+ const timestamp = timeInSec * MEDIA_PARSER_RIFF_TIMESCALE;
7916
+ const duration2 = 1 / samplesPerSecond * MEDIA_PARSER_RIFF_TIMESCALE;
7917
+ await options.parserState.onAudioSample(trackId, {
7918
+ cts: timestamp,
7919
+ dts: timestamp,
7920
+ data: iterator.getSlice(ckSize),
7921
+ duration: duration2,
7922
+ timestamp,
7923
+ trackId,
7924
+ type: "key"
7925
+ });
7926
+ }
7927
+ }
7928
+ };
7929
+ var parseMovi = async ({
7930
+ iterator,
7931
+ maxOffset,
7932
+ options,
7933
+ structure
7934
+ }) => {
7935
+ while (iterator.counter.getOffset() < maxOffset) {
7936
+ if (iterator.bytesRemaining() < 8) {
7937
+ return {
7938
+ type: "incomplete",
7939
+ continueParsing: () => {
7940
+ return Promise.resolve(parseMovi({ iterator, maxOffset, options, structure }));
7941
+ }
7942
+ };
7943
+ }
7944
+ const ckId = iterator.getByteString(4);
7945
+ const ckSize = iterator.getUint32Le();
7946
+ if (iterator.bytesRemaining() < ckSize) {
7947
+ iterator.counter.decrement(8);
7948
+ return {
7949
+ type: "incomplete",
7950
+ continueParsing: () => {
7951
+ return Promise.resolve(parseMovi({ iterator, maxOffset, options, structure }));
7952
+ }
7953
+ };
7954
+ }
7955
+ await handleChunk({ iterator, options, structure, ckId, ckSize });
7956
+ while (iterator.counter.getOffset() < maxOffset && iterator.bytesRemaining() > 0) {
7957
+ if (iterator.getUint8() !== 0) {
7958
+ iterator.counter.decrement(1);
7959
+ break;
7960
+ }
7961
+ }
7962
+ }
7963
+ if (iterator.counter.getOffset() === maxOffset) {
7964
+ return {
7965
+ type: "complete",
7966
+ box: {
7967
+ type: "movi-box"
7968
+ }
7969
+ };
7970
+ }
7971
+ if (iterator.counter.getOffset() > maxOffset) {
7972
+ throw new Error("Oops, this should not happen!");
7973
+ }
7974
+ return {
7975
+ type: "incomplete",
7976
+ continueParsing: () => {
7977
+ return Promise.resolve(parseMovi({ iterator, maxOffset, options, structure }));
7978
+ }
7979
+ };
7980
+ };
7981
+
7982
+ // src/boxes/riff/parse-avih.ts
7983
+ var parseAvih = ({
7984
+ iterator,
7985
+ size
7986
+ }) => {
7987
+ const { expectNoMoreBytes } = iterator.startBox(size);
7988
+ const dwMicroSecPerFrame = iterator.getUint32Le();
7989
+ const dwMaxBytesPerSec = iterator.getUint32Le();
7990
+ const paddingGranularity = iterator.getUint32Le();
7991
+ const flags = iterator.getUint32Le();
7992
+ const totalFrames = iterator.getUint32Le();
7993
+ const initialFrames = iterator.getUint32Le();
7994
+ const streams = iterator.getUint32Le();
7995
+ const suggestedBufferSize = iterator.getUint32Le();
7996
+ const width = iterator.getUint32Le();
7997
+ const height = iterator.getUint32Le();
7998
+ iterator.discard(16);
7999
+ expectNoMoreBytes();
8000
+ return {
8001
+ type: "avih-box",
8002
+ microSecPerFrame: dwMicroSecPerFrame,
8003
+ maxBytesPerSecond: dwMaxBytesPerSec,
8004
+ paddingGranularity,
8005
+ flags,
8006
+ totalFrames,
8007
+ initialFrames,
8008
+ streams,
8009
+ suggestedBufferSize,
8010
+ height,
8011
+ width
8012
+ };
8013
+ };
8014
+
8015
+ // src/boxes/riff/parse-fmt-box.ts
8016
+ var parseFmtBox = ({
8017
+ iterator,
8018
+ boxes,
8019
+ size
8020
+ }) => {
8021
+ const box = iterator.startBox(size);
8022
+ const header = boxes.find((b) => b.type === "riff-header");
8023
+ if (!header) {
8024
+ throw new Error("Expected RIFF header");
8025
+ }
8026
+ if (header.fileType !== "WAVE") {
8027
+ throw new Error("Only supporting WAVE type");
8028
+ }
8029
+ const wFormatTag = iterator.getUint16Le();
8030
+ if (wFormatTag !== 1) {
8031
+ throw new Error("Expected wFormatTag to be 1, only supporting this");
8032
+ }
8033
+ const numberOfChannels = iterator.getUint16Le();
8034
+ const sampleRate = iterator.getUint32Le();
8035
+ const byteRate = iterator.getUint32Le();
8036
+ const blockAlign = iterator.getUint16Le();
8037
+ const bitsPerSample = iterator.getUint16Le();
8038
+ box.expectNoMoreBytes();
8039
+ return {
8040
+ type: "wave-format-box",
8041
+ formatTag: wFormatTag,
8042
+ numberOfChannels,
8043
+ sampleRate,
8044
+ blockAlign,
8045
+ byteRate,
8046
+ bitsPerSample
8047
+ };
8048
+ };
8049
+
8050
+ // src/boxes/riff/parse-list-box.ts
8051
+ var parseListBox = async ({
8052
+ iterator,
8053
+ size,
8054
+ options
8055
+ }) => {
8056
+ const counter = iterator.counter.getOffset();
8057
+ const listType = iterator.getByteString(4);
8058
+ if (listType === "movi") {
8059
+ throw new Error("should not be handled here");
8060
+ }
8061
+ const structure = {
8062
+ type: "riff",
8063
+ boxes: []
8064
+ };
8065
+ const result = await parseRiffBody({
8066
+ structure,
8067
+ iterator,
8068
+ maxOffset: counter + size,
8069
+ options
8070
+ });
8071
+ if (result.status === "incomplete") {
8072
+ throw new Error(`Should only parse complete boxes (${listType})`);
8073
+ }
8074
+ return {
8075
+ type: "list-box",
8076
+ listType,
8077
+ children: structure.boxes
8078
+ };
8079
+ };
8080
+
8081
+ // src/boxes/riff/parse-strf.ts
8082
+ var parseStrfAudio = ({
8083
+ iterator,
8084
+ size
8085
+ }) => {
8086
+ const box = iterator.startBox(size);
8087
+ const formatTag = iterator.getUint16Le();
8088
+ const numberOfChannels = iterator.getUint16Le();
8089
+ const samplesPerSec = iterator.getUint32Le();
8090
+ const avgBytesPerSec = iterator.getUint32Le();
8091
+ const blockAlign = iterator.getUint16Le();
8092
+ const bitsPerSample = iterator.getUint16Le();
8093
+ const cbSize = iterator.getUint16Le();
8094
+ box.expectNoMoreBytes();
8095
+ return {
8096
+ type: "strf-box-audio",
8097
+ avgBytesPerSecond: avgBytesPerSec,
8098
+ bitsPerSample,
8099
+ blockAlign,
8100
+ cbSize,
8101
+ formatTag,
8102
+ numberOfChannels,
8103
+ sampleRate: samplesPerSec
8104
+ };
8105
+ };
8106
+ var parseStrfVideo = ({
8107
+ iterator,
8108
+ size
8109
+ }) => {
8110
+ const box = iterator.startBox(size);
8111
+ const biSize = iterator.getUint32Le();
8112
+ const width = iterator.getInt32Le();
8113
+ const height = iterator.getInt32Le();
8114
+ const planes = iterator.getUint16Le();
8115
+ const bitCount = iterator.getUint16Le();
8116
+ const compression = iterator.getByteString(4);
8117
+ const sizeImage = iterator.getUint32Le();
8118
+ const xPelsPerMeter = iterator.getInt32Le();
8119
+ const yPelsPerMeter = iterator.getInt32Le();
8120
+ const clrUsed = iterator.getUint32Le();
8121
+ const clrImportant = iterator.getUint32Le();
8122
+ box.expectNoMoreBytes();
8123
+ return {
8124
+ type: "strf-box-video",
8125
+ biSize,
8126
+ bitCount,
8127
+ clrImportant,
8128
+ clrUsed,
8129
+ compression,
8130
+ height,
8131
+ planes,
8132
+ sizeImage,
8133
+ width,
8134
+ xPelsPerMeter,
8135
+ yPelsPerMeter
8136
+ };
8137
+ };
8138
+ var parseStrf = ({
8139
+ iterator,
8140
+ size,
8141
+ boxes
8142
+ }) => {
8143
+ const strh = boxes.find((b) => b.type === "strh-box");
8144
+ if (!strh) {
8145
+ throw new Error("strh box not found");
8146
+ }
8147
+ if (strh.fccType === "vids") {
8148
+ return parseStrfVideo({ iterator, size });
8149
+ }
8150
+ if (strh.fccType === "auds") {
8151
+ return parseStrfAudio({ iterator, size });
8152
+ }
8153
+ throw new Error(`Unsupported fccType: ${strh.fccType}`);
8154
+ };
8155
+
8156
+ // src/boxes/riff/parse-strh.ts
8157
+ var parseStrh = ({
8158
+ iterator,
8159
+ size
8160
+ }) => {
8161
+ const box = iterator.startBox(size);
8162
+ const fccType = iterator.getByteString(4);
8163
+ if (fccType !== "vids" && fccType !== "auds") {
8164
+ throw new Error("Expected AVI handler to be vids / auds");
8165
+ }
8166
+ const handler = fccType === "vids" ? iterator.getByteString(4) : iterator.getUint32Le();
8167
+ if (typeof handler === "string" && handler !== "H264") {
8168
+ throw new Error(`Only H264 is supported as a stream type in .avi, got ${handler}`);
8169
+ }
8170
+ if (fccType === "auds" && handler !== 1) {
8171
+ throw new Error(`Only "1" is supported as a stream type in .avi, got ${handler}`);
8172
+ }
8173
+ const flags = iterator.getUint32Le();
8174
+ const priority = iterator.getUint16Le();
8175
+ const language2 = iterator.getUint16Le();
8176
+ const initialFrames = iterator.getUint32Le();
8177
+ const scale = iterator.getUint32Le();
8178
+ const rate = iterator.getUint32Le();
8179
+ const start = iterator.getUint32Le();
8180
+ const length = iterator.getUint32Le();
8181
+ const suggestedBufferSize = iterator.getUint32Le();
8182
+ const quality = iterator.getUint32Le();
8183
+ const sampleSize = iterator.getUint32Le();
8184
+ box.discardRest();
8185
+ return {
8186
+ type: "strh-box",
8187
+ fccType,
8188
+ handler,
8189
+ flags,
8190
+ priority,
8191
+ initialFrames,
8192
+ length,
8193
+ quality,
8194
+ rate,
8195
+ sampleSize,
8196
+ scale,
8197
+ start,
8198
+ suggestedBufferSize,
8199
+ language: language2
8200
+ };
8201
+ };
8202
+
8203
+ // src/boxes/riff/parse-riff-box.ts
8204
+ var parseRiffBox = ({
8205
+ iterator,
8206
+ size,
8207
+ id,
8208
+ boxes,
8209
+ options
8210
+ }) => {
8211
+ if (id === "fmt") {
8212
+ return Promise.resolve(parseFmtBox({ iterator, boxes, size }));
8213
+ }
8214
+ if (id === "LIST") {
8215
+ return parseListBox({ iterator, size, options });
8216
+ }
8217
+ if (id === "avih") {
8218
+ return Promise.resolve(parseAvih({ iterator, size }));
8219
+ }
8220
+ if (id === "strh") {
8221
+ return Promise.resolve(parseStrh({ iterator, size }));
8222
+ }
8223
+ if (id === "strf") {
8224
+ return Promise.resolve(parseStrf({ iterator, size, boxes }));
8225
+ }
8226
+ iterator.discard(size);
8227
+ const box = {
8228
+ type: "riff-box",
8229
+ size,
8230
+ id
8231
+ };
8232
+ return Promise.resolve(box);
8233
+ };
8234
+
8235
+ // src/boxes/riff/expect-riff-box.ts
8236
+ var expectRiffBox = async ({
8237
+ iterator,
8238
+ options,
8239
+ structure
8240
+ }) => {
8241
+ if (iterator.bytesRemaining() < 16) {
8242
+ return {
8243
+ type: "incomplete",
8244
+ continueParsing() {
8245
+ return expectRiffBox({ structure, iterator, options });
8246
+ }
8247
+ };
8248
+ }
8249
+ const ckId = iterator.getByteString(4);
8250
+ const ckSize = iterator.getUint32Le();
8251
+ if (isMoviAtom(iterator, ckId)) {
8252
+ iterator.discard(4);
8253
+ return parseMovi({
8254
+ iterator,
8255
+ maxOffset: ckSize + iterator.counter.getOffset() - 4,
8256
+ options,
8257
+ structure
8258
+ });
8259
+ }
8260
+ if (iterator.bytesRemaining() < ckSize) {
8261
+ iterator.counter.decrement(8);
8262
+ return {
8263
+ type: "incomplete",
8264
+ continueParsing: () => {
8265
+ return expectRiffBox({ structure, iterator, options });
8266
+ }
8267
+ };
8268
+ }
8269
+ return {
8270
+ type: "complete",
8271
+ box: await parseRiffBox({
8272
+ id: ckId,
8273
+ iterator,
8274
+ size: ckSize,
8275
+ boxes: structure.boxes,
8276
+ options
8277
+ })
8278
+ };
8279
+ };
8280
+
8281
+ // src/boxes/riff/parse-box.ts
8282
+ var continueAfterRiffBoxResult = ({
8283
+ result,
8284
+ structure,
8285
+ iterator,
8286
+ maxOffset,
8287
+ options
8288
+ }) => {
8289
+ if (result.type === "incomplete") {
8290
+ return Promise.resolve({
8291
+ status: "incomplete",
8292
+ async continueParsing() {
8293
+ return Promise.resolve(continueAfterRiffBoxResult({
8294
+ result: await result.continueParsing(),
8295
+ structure,
8296
+ iterator,
8297
+ maxOffset,
8298
+ options
8299
+ }));
8300
+ },
8301
+ segments: structure,
8302
+ skipTo: null
8303
+ });
8304
+ }
8305
+ if (result.type === "complete") {
8306
+ if (result.box) {
8307
+ structure.boxes.push(result.box);
8308
+ }
8309
+ }
8310
+ return parseRiffBody({ iterator, maxOffset, options, structure });
8311
+ };
8312
+ var parseRiffBody = async ({
8313
+ iterator,
8314
+ structure,
8315
+ maxOffset,
8316
+ options
8317
+ }) => {
8318
+ while (iterator.bytesRemaining() > 0 && iterator.counter.getOffset() < maxOffset) {
8319
+ const result = await expectRiffBox({
8320
+ iterator,
8321
+ options,
8322
+ structure
8323
+ });
8324
+ if (result.type === "incomplete") {
8325
+ return {
8326
+ status: "incomplete",
8327
+ async continueParsing() {
8328
+ return Promise.resolve(continueAfterRiffBoxResult({
8329
+ iterator,
8330
+ maxOffset,
8331
+ options,
8332
+ result: await result.continueParsing(),
8333
+ structure
8334
+ }));
8335
+ },
8336
+ segments: structure,
8337
+ skipTo: null
8338
+ };
8339
+ }
8340
+ if (result.box === null) {
8341
+ continue;
8342
+ }
8343
+ structure.boxes.push(result.box);
8344
+ if (result.box.type === "strf-box-video" || result.box.type === "strf-box-audio") {
8345
+ const strh = getStrhBox(structure.boxes);
8346
+ const strf = getStrfBox(structure.boxes);
8347
+ if (!strh || !strf) {
8348
+ throw new Error("strh or strf box missing");
8349
+ }
8350
+ if (strf.type === "strf-box-audio" && options.onAudioTrack) {
8351
+ const audioTrack = makeAviAudioTrack({
8352
+ index: options.nextTrackIndex,
8353
+ strf
8354
+ });
8355
+ await registerTrack({
8356
+ options,
8357
+ state: options.parserState,
8358
+ track: audioTrack
8359
+ });
8360
+ }
8361
+ if (options.onVideoTrack && strf.type === "strf-box-video") {
8362
+ const videoTrack = makeAviVideoTrack({
8363
+ strh,
8364
+ index: options.nextTrackIndex,
8365
+ strf
8366
+ });
8367
+ registerVideoTrackWhenProfileIsAvailable({
8368
+ options,
8369
+ state: options.parserState,
8370
+ track: videoTrack
8371
+ });
8372
+ }
8373
+ options.nextTrackIndex++;
8374
+ }
7338
8375
  }
8376
+ return {
8377
+ status: "done",
8378
+ segments: structure
8379
+ };
8380
+ };
8381
+ var parseRiff = ({
8382
+ iterator,
8383
+ options
8384
+ }) => {
8385
+ const structure = { type: "riff", boxes: [] };
8386
+ const riff = iterator.getByteString(4);
8387
+ if (riff !== "RIFF") {
8388
+ throw new Error("Not a RIFF file");
8389
+ }
8390
+ const size = iterator.getUint32Le();
8391
+ const fileType = iterator.getByteString(4);
8392
+ if (fileType !== "WAVE" && fileType !== "AVI") {
8393
+ throw new Error(`File type ${fileType} not supported`);
8394
+ }
8395
+ structure.boxes.push({ type: "riff-header", fileSize: size, fileType });
8396
+ return parseRiffBody({ iterator, structure, maxOffset: Infinity, options });
7339
8397
  };
7340
8398
 
7341
8399
  // src/boxes/webm/segments/block-simple-block-flags.ts
@@ -7605,32 +8663,54 @@ var postprocessEbml = async ({
7605
8663
  };
7606
8664
 
7607
8665
  // src/boxes/webm/segments.ts
7608
- var parseSegment = async ({
7609
- segmentId,
8666
+ var continueAfterMatroskaParseResult = async ({
8667
+ result,
7610
8668
  iterator,
7611
- length,
7612
8669
  parserContext,
7613
- headerReadSoFar
8670
+ segment
7614
8671
  }) => {
7615
- if (length < 0) {
7616
- throw new Error(`Expected length of ${segmentId} to be greater or equal 0`);
8672
+ if (result.status === "done") {
8673
+ throw new Error("Should not continue after done");
7617
8674
  }
7618
- iterator.counter.decrement(headerReadSoFar);
7619
- const offset = iterator.counter.getOffset();
7620
- const ebml = await parseEbml(iterator, parserContext);
7621
- const remapped = await postprocessEbml({ offset, ebml, parserContext });
7622
- return remapped;
8675
+ const proceeded = await result.continueParsing();
8676
+ if (proceeded.status === "done") {
8677
+ return {
8678
+ status: "done",
8679
+ segment
8680
+ };
8681
+ }
8682
+ return {
8683
+ continueParsing() {
8684
+ return continueAfterMatroskaParseResult({
8685
+ result: proceeded,
8686
+ iterator,
8687
+ parserContext,
8688
+ segment
8689
+ });
8690
+ },
8691
+ segment: null,
8692
+ status: "incomplete"
8693
+ };
7623
8694
  };
7624
- var expectSegment = async (iterator, parserContext) => {
7625
- const offset = iterator.counter.getOffset();
8695
+ var expectSegment = async ({
8696
+ iterator,
8697
+ parserContext,
8698
+ offset,
8699
+ children
8700
+ }) => {
8701
+ iterator.counter.decrement(iterator.counter.getOffset() - offset);
7626
8702
  if (iterator.bytesRemaining() === 0) {
7627
8703
  return {
7628
8704
  status: "incomplete",
7629
- segments: [],
7630
8705
  continueParsing: () => {
7631
- return Promise.resolve(expectSegment(iterator, parserContext));
8706
+ return expectAndProcessSegment({
8707
+ iterator,
8708
+ parserContext,
8709
+ offset,
8710
+ children
8711
+ });
7632
8712
  },
7633
- skipTo: null
8713
+ segment: null
7634
8714
  };
7635
8715
  }
7636
8716
  const segmentId = iterator.getMatroskaSegmentId();
@@ -7638,11 +8718,15 @@ var expectSegment = async (iterator, parserContext) => {
7638
8718
  iterator.counter.decrement(iterator.counter.getOffset() - offset);
7639
8719
  return {
7640
8720
  status: "incomplete",
7641
- segments: [],
7642
8721
  continueParsing: () => {
7643
- return Promise.resolve(expectSegment(iterator, parserContext));
8722
+ return expectAndProcessSegment({
8723
+ iterator,
8724
+ parserContext,
8725
+ offset,
8726
+ children
8727
+ });
7644
8728
  },
7645
- skipTo: null
8729
+ segment: null
7646
8730
  };
7647
8731
  }
7648
8732
  const offsetBeforeVInt = iterator.counter.getOffset();
@@ -7652,41 +8736,43 @@ var expectSegment = async (iterator, parserContext) => {
7652
8736
  iterator.counter.decrement(iterator.counter.getOffset() - offset);
7653
8737
  return {
7654
8738
  status: "incomplete",
7655
- segments: [],
7656
8739
  continueParsing: () => {
7657
- return Promise.resolve(expectSegment(iterator, parserContext));
8740
+ return expectSegment({ iterator, parserContext, offset, children });
7658
8741
  },
7659
- skipTo: null
8742
+ segment: null
7660
8743
  };
7661
8744
  }
7662
8745
  const bytesRemainingNow = iterator.byteLength() - iterator.counter.getOffset();
7663
8746
  if (segmentId === "0x18538067" || segmentId === "0x1f43b675") {
8747
+ const newSegment = {
8748
+ type: segmentId === "0x18538067" ? "Segment" : "Cluster",
8749
+ minVintWidth: offsetAfterVInt - offsetBeforeVInt,
8750
+ value: []
8751
+ };
7664
8752
  const main = await expectChildren({
7665
8753
  iterator,
7666
8754
  length,
7667
- initialChildren: [],
7668
- wrap: segmentId === "0x18538067" ? (s) => ({
7669
- type: "Segment",
7670
- value: s,
7671
- minVintWidth: offsetAfterVInt - offsetBeforeVInt
7672
- }) : (s) => ({
7673
- type: "Cluster",
7674
- value: s,
7675
- minVintWidth: offsetAfterVInt - offsetBeforeVInt
7676
- }),
7677
- parserContext
8755
+ children: newSegment.value,
8756
+ parserContext,
8757
+ startOffset: iterator.counter.getOffset()
7678
8758
  });
7679
8759
  if (main.status === "incomplete") {
7680
8760
  return {
7681
8761
  status: "incomplete",
7682
- segments: main.segments,
7683
- skipTo: null,
7684
- continueParsing: main.continueParsing
8762
+ continueParsing: () => {
8763
+ return continueAfterMatroskaParseResult({
8764
+ iterator,
8765
+ parserContext,
8766
+ result: main,
8767
+ segment: newSegment
8768
+ });
8769
+ },
8770
+ segment: newSegment
7685
8771
  };
7686
8772
  }
7687
8773
  return {
7688
8774
  status: "done",
7689
- segments: main.segments
8775
+ segment: newSegment
7690
8776
  };
7691
8777
  }
7692
8778
  if (bytesRemainingNow < length) {
@@ -7694,11 +8780,10 @@ var expectSegment = async (iterator, parserContext) => {
7694
8780
  iterator.counter.decrement(bytesRead);
7695
8781
  return {
7696
8782
  status: "incomplete",
7697
- segments: [],
8783
+ segment: null,
7698
8784
  continueParsing: () => {
7699
- return Promise.resolve(expectSegment(iterator, parserContext));
7700
- },
7701
- skipTo: null
8785
+ return expectSegment({ iterator, parserContext, offset, children });
8786
+ }
7702
8787
  };
7703
8788
  }
7704
8789
  const segment = await parseSegment({
@@ -7710,131 +8795,179 @@ var expectSegment = async (iterator, parserContext) => {
7710
8795
  });
7711
8796
  return {
7712
8797
  status: "done",
7713
- segments: [segment]
8798
+ segment
7714
8799
  };
7715
8800
  };
8801
+ var parseSegment = async ({
8802
+ segmentId,
8803
+ iterator,
8804
+ length,
8805
+ parserContext,
8806
+ headerReadSoFar
8807
+ }) => {
8808
+ if (length < 0) {
8809
+ throw new Error(`Expected length of ${segmentId} to be greater or equal 0`);
8810
+ }
8811
+ iterator.counter.decrement(headerReadSoFar);
8812
+ const offset = iterator.counter.getOffset();
8813
+ const ebml = await parseEbml(iterator, parserContext);
8814
+ const remapped = await postprocessEbml({ offset, ebml, parserContext });
8815
+ return remapped;
8816
+ };
7716
8817
 
7717
8818
  // src/boxes/webm/segments/parse-children.ts
7718
8819
  var processParseResult = ({
7719
8820
  parseResult,
7720
- children,
7721
- wrap
8821
+ children
7722
8822
  }) => {
8823
+ if (parseResult.segment && !children.includes(parseResult.segment)) {
8824
+ children.push(parseResult.segment);
8825
+ }
7723
8826
  if (parseResult.status === "incomplete") {
7724
8827
  return {
7725
8828
  status: "incomplete",
7726
- segments: [],
8829
+ segment: parseResult.segment,
7727
8830
  continueParsing: async () => {
7728
8831
  const newParseResult = await parseResult.continueParsing();
7729
8832
  return processParseResult({
7730
8833
  children,
7731
- parseResult: newParseResult,
7732
- wrap
8834
+ parseResult: newParseResult
7733
8835
  });
7734
- },
7735
- skipTo: null
8836
+ }
7736
8837
  };
7737
8838
  }
7738
- for (const segment of parseResult.segments) {
7739
- children.push(segment);
7740
- }
7741
8839
  return {
7742
8840
  status: "done",
7743
- segments: wrap ? [wrap(children)] : children
8841
+ segment: parseResult.segment
7744
8842
  };
7745
8843
  };
7746
- var continueParsingfunction = ({
7747
- result,
8844
+ var expectAndProcessSegment = async ({
7748
8845
  iterator,
8846
+ parserContext,
8847
+ offset,
8848
+ children
8849
+ }) => {
8850
+ const segment = await expectSegment({
8851
+ iterator,
8852
+ parserContext,
8853
+ offset,
8854
+ children
8855
+ });
8856
+ return processParseResult({
8857
+ children,
8858
+ parseResult: segment
8859
+ });
8860
+ };
8861
+ var continueAfterSegmentResult = async ({
8862
+ result,
8863
+ length,
7749
8864
  children,
7750
- wrap,
7751
8865
  parserContext,
7752
- length
7753
- }) => async () => {
7754
- if (result.status !== "incomplete") {
7755
- throw new Error("expected incomplete");
8866
+ iterator,
8867
+ startOffset
8868
+ }) => {
8869
+ if (result.status === "done") {
8870
+ throw new Error("Should not continue after done");
7756
8871
  }
7757
- const offset = iterator.counter.getOffset();
7758
- const continued = await result.continueParsing();
7759
- if (continued.status === "incomplete") {
7760
- if (!parserContext.supportsContentRange) {
7761
- throw new Error("Content-Range header is not supported by the reader, but was asked to seek");
7762
- }
8872
+ const segmentResult = await result.continueParsing();
8873
+ if (segmentResult.status === "done") {
7763
8874
  return {
7764
8875
  status: "incomplete",
7765
- continueParsing: continueParsingfunction({
7766
- result: continued,
7767
- iterator,
7768
- children,
7769
- wrap,
7770
- parserContext,
7771
- length: length - (iterator.counter.getOffset() - offset)
7772
- }),
7773
- skipTo: continued.skipTo,
7774
- segments: wrap ? [wrap(children)] : children
8876
+ continueParsing: () => {
8877
+ return expectChildren({
8878
+ children,
8879
+ iterator,
8880
+ length,
8881
+ parserContext,
8882
+ startOffset
8883
+ });
8884
+ },
8885
+ skipTo: null
7775
8886
  };
7776
8887
  }
7777
- return expectChildren({
7778
- iterator,
7779
- length: length - (iterator.counter.getOffset() - offset),
7780
- initialChildren: children,
7781
- wrap,
7782
- parserContext
7783
- });
8888
+ return {
8889
+ status: "incomplete",
8890
+ continueParsing: () => {
8891
+ return continueAfterSegmentResult({
8892
+ result: segmentResult,
8893
+ children,
8894
+ iterator,
8895
+ length,
8896
+ parserContext,
8897
+ startOffset
8898
+ });
8899
+ },
8900
+ skipTo: null
8901
+ };
7784
8902
  };
7785
8903
  var expectChildren = async ({
7786
8904
  iterator,
7787
8905
  length,
7788
- initialChildren,
7789
- wrap,
7790
- parserContext
8906
+ children,
8907
+ parserContext,
8908
+ startOffset
7791
8909
  }) => {
7792
- const children = [...initialChildren];
7793
- const startOffset = iterator.counter.getOffset();
7794
8910
  while (iterator.counter.getOffset() < startOffset + length) {
7795
8911
  if (iterator.bytesRemaining() === 0) {
7796
8912
  break;
7797
8913
  }
7798
- const parseResult = await expectSegment(iterator, parserContext);
7799
- const child = processParseResult({
7800
- children,
7801
- parseResult,
7802
- wrap
8914
+ const currentOffset = iterator.counter.getOffset();
8915
+ const child = await expectAndProcessSegment({
8916
+ iterator,
8917
+ parserContext,
8918
+ offset: currentOffset,
8919
+ children
7803
8920
  });
7804
8921
  if (child.status === "incomplete") {
7805
- if (!parserContext.supportsContentRange) {
7806
- throw new Error("Content-Range header is not supported by the reader, but was asked to seek");
7807
- }
7808
8922
  return {
7809
8923
  status: "incomplete",
7810
- continueParsing: continueParsingfunction({
7811
- result: child,
7812
- iterator,
7813
- children,
7814
- wrap,
7815
- parserContext,
7816
- length: length - (iterator.counter.getOffset() - startOffset)
7817
- }),
7818
- skipTo: child.skipTo,
7819
- segments: wrap ? [wrap(children)] : children
8924
+ continueParsing: () => {
8925
+ return continueAfterSegmentResult({
8926
+ result: child,
8927
+ children,
8928
+ iterator,
8929
+ length: length - (currentOffset - startOffset),
8930
+ parserContext,
8931
+ startOffset: currentOffset
8932
+ });
8933
+ },
8934
+ skipTo: null
7820
8935
  };
7821
8936
  }
7822
8937
  }
7823
8938
  return {
7824
- status: "done",
7825
- segments: wrap ? [wrap(children)] : children
8939
+ status: "done"
7826
8940
  };
7827
8941
  };
7828
8942
 
7829
8943
  // src/boxes/webm/parse-webm-header.ts
7830
- var parseWebm = (counter, parserContext) => {
7831
- return expectChildren({
8944
+ var continueAfterMatroskaResult = (result, structure) => {
8945
+ if (result.status === "done") {
8946
+ return {
8947
+ status: "done",
8948
+ segments: structure
8949
+ };
8950
+ }
8951
+ return {
8952
+ status: "incomplete",
8953
+ segments: structure,
8954
+ continueParsing: async () => {
8955
+ const newResult = await result.continueParsing();
8956
+ return continueAfterMatroskaResult(newResult, structure);
8957
+ },
8958
+ skipTo: null
8959
+ };
8960
+ };
8961
+ var parseWebm = async (counter, parserContext) => {
8962
+ const structure = { type: "matroska", boxes: [] };
8963
+ const results = await expectChildren({
7832
8964
  iterator: counter,
7833
8965
  length: Infinity,
7834
- initialChildren: [],
7835
- wrap: null,
7836
- parserContext
8966
+ children: structure.boxes,
8967
+ parserContext,
8968
+ startOffset: counter.counter.getOffset()
7837
8969
  });
8970
+ return continueAfterMatroskaResult(results, structure);
7838
8971
  };
7839
8972
 
7840
8973
  // src/parse-video.ts
@@ -7845,25 +8978,14 @@ var parseVideo = ({
7845
8978
  logLevel
7846
8979
  }) => {
7847
8980
  if (iterator.bytesRemaining() === 0) {
7848
- return Promise.resolve({
7849
- status: "incomplete",
7850
- segments: [],
7851
- continueParsing: () => {
7852
- return parseVideo({
7853
- iterator,
7854
- options,
7855
- signal,
7856
- logLevel
7857
- });
7858
- },
7859
- skipTo: null
7860
- });
8981
+ return Promise.reject(new Error("no bytes"));
7861
8982
  }
7862
8983
  if (iterator.isRiff()) {
7863
- throw new Error("AVI files are not yet supported");
8984
+ return Promise.resolve(parseRiff({ iterator, options }));
7864
8985
  }
7865
8986
  if (iterator.isIsoBaseMedia()) {
7866
- return parseBoxes({
8987
+ Log.verbose(logLevel, "Detected ISO Base Media container");
8988
+ return parseIsoBaseMediaBoxes({
7867
8989
  iterator,
7868
8990
  maxBytes: Infinity,
7869
8991
  allowIncompleteBoxes: true,
@@ -7875,7 +8997,8 @@ var parseVideo = ({
7875
8997
  });
7876
8998
  }
7877
8999
  if (iterator.isWebm()) {
7878
- return Promise.resolve(parseWebm(iterator, options));
9000
+ Log.verbose(logLevel, "Detected Matroska container");
9001
+ return parseWebm(iterator, options);
7879
9002
  }
7880
9003
  if (iterator.isMp3()) {
7881
9004
  return Promise.reject(new Error("MP3 files are not yet supported"));
@@ -7940,8 +9063,23 @@ var makeParserState = ({
7940
9063
  }
7941
9064
  return timestampMap.get(byteOffset);
7942
9065
  };
9066
+ const samplesForTrack = {};
9067
+ const profileCallbacks = [];
9068
+ const registerOnAvcProfileCallback = (callback) => {
9069
+ profileCallbacks.push(callback);
9070
+ };
9071
+ let avcProfile = null;
9072
+ const onProfile = async (profile) => {
9073
+ avcProfile = profile;
9074
+ for (const callback of profileCallbacks) {
9075
+ await callback(profile);
9076
+ }
9077
+ profileCallbacks.length = 0;
9078
+ };
7943
9079
  return {
7944
9080
  onTrackEntrySegment,
9081
+ onProfile,
9082
+ registerOnAvcProfileCallback,
7945
9083
  getTrackInfoByNumber: (id) => trackEntries[id],
7946
9084
  registerVideoSampleCallback: async (id, callback) => {
7947
9085
  if (callback === null) {
@@ -7973,6 +9111,10 @@ var makeParserState = ({
7973
9111
  if (signal?.aborted) {
7974
9112
  throw new Error("Aborted");
7975
9113
  }
9114
+ if (typeof samplesForTrack[trackId] === "undefined") {
9115
+ samplesForTrack[trackId] = 0;
9116
+ }
9117
+ samplesForTrack[trackId]++;
7976
9118
  const callback = audioSampleCallbacks[trackId];
7977
9119
  if (callback) {
7978
9120
  await callback(audioSample);
@@ -7989,6 +9131,10 @@ var makeParserState = ({
7989
9131
  if (signal?.aborted) {
7990
9132
  throw new Error("Aborted");
7991
9133
  }
9134
+ if (typeof samplesForTrack[trackId] === "undefined") {
9135
+ samplesForTrack[trackId] = 0;
9136
+ }
9137
+ samplesForTrack[trackId]++;
7992
9138
  const callback = videoSampleCallbacks[trackId];
7993
9139
  if (callback) {
7994
9140
  await callback(videoSample);
@@ -8003,7 +9149,13 @@ var makeParserState = ({
8003
9149
  },
8004
9150
  getInternalStats: () => ({}),
8005
9151
  getTimescale,
8006
- setTimescale
9152
+ setTimescale,
9153
+ getSamplesForTrack: (trackId) => {
9154
+ return samplesForTrack[trackId] ?? 0;
9155
+ },
9156
+ getAvcProfile: () => {
9157
+ return avcProfile;
9158
+ }
8007
9159
  };
8008
9160
  };
8009
9161
 
@@ -8016,6 +9168,7 @@ var parseMedia = async ({
8016
9168
  onVideoTrack,
8017
9169
  signal,
8018
9170
  logLevel = "info",
9171
+ onParseProgress,
8019
9172
  ...more
8020
9173
  }) => {
8021
9174
  const state = makeParserState({
@@ -8035,14 +9188,41 @@ var parseMedia = async ({
8035
9188
  const moreFields = more;
8036
9189
  let iterator = null;
8037
9190
  let parseResult = null;
9191
+ const canSkipVideoData = !onVideoTrack && !onAudioTrack;
9192
+ if (canSkipVideoData) {
9193
+ Log.verbose(logLevel, "Only parsing metadata, because no onVideoTrack and onAudioTrack callbacks were passed.");
9194
+ } else {
9195
+ Log.verbose(logLevel, "Parsing video data, because onVideoTrack/onAudioTrack callbacks were passed.");
9196
+ }
8038
9197
  const options = {
8039
- canSkipVideoData: !(onAudioTrack || onVideoTrack),
9198
+ canSkipVideoData,
8040
9199
  onAudioTrack: onAudioTrack ?? null,
8041
9200
  onVideoTrack: onVideoTrack ?? null,
8042
9201
  parserState: state,
8043
9202
  nullifySamples: !(typeof process !== "undefined" && typeof process.env !== "undefined" && process.env.KEEP_SAMPLES === "true"),
8044
- supportsContentRange
9203
+ supportsContentRange,
9204
+ nextTrackIndex: 0
9205
+ };
9206
+ const hasAllInfo = () => {
9207
+ if (parseResult === null) {
9208
+ return false;
9209
+ }
9210
+ const availableInfo = getAvailableInfo(fields ?? {}, parseResult, state);
9211
+ return Object.values(availableInfo).every(Boolean);
9212
+ };
9213
+ const triggerInfoEmit = () => {
9214
+ const availableInfo = getAvailableInfo(fields ?? {}, parseResult, state);
9215
+ emitAvailableInfo({
9216
+ hasInfo: availableInfo,
9217
+ moreFields,
9218
+ parseResult,
9219
+ state,
9220
+ returnValue,
9221
+ contentLength,
9222
+ name
9223
+ });
8045
9224
  };
9225
+ triggerInfoEmit();
8046
9226
  while (parseResult === null || parseResult.status === "incomplete") {
8047
9227
  if (signal?.aborted) {
8048
9228
  throw new Error("Aborted");
@@ -8069,7 +9249,14 @@ var parseMedia = async ({
8069
9249
  if (!iterator) {
8070
9250
  throw new Error("Unexpected null");
8071
9251
  }
9252
+ await onParseProgress?.({
9253
+ bytes: iterator.counter.getOffset(),
9254
+ percentage: contentLength ? iterator.counter.getOffset() / contentLength : null,
9255
+ totalBytes: contentLength
9256
+ });
9257
+ triggerInfoEmit();
8072
9258
  if (parseResult && parseResult.status === "incomplete") {
9259
+ Log.verbose(logLevel, "Continuing parsing of file, currently at position", iterator.counter.getOffset(), getAvailableInfo(fields ?? {}, parseResult, state));
8073
9260
  parseResult = await parseResult.continueParsing();
8074
9261
  } else {
8075
9262
  parseResult = await parseVideo({
@@ -8079,18 +9266,7 @@ var parseMedia = async ({
8079
9266
  logLevel
8080
9267
  });
8081
9268
  }
8082
- const availableInfo = getAvailableInfo(fields ?? {}, parseResult, state);
8083
- const hasAllInfo = Object.values(availableInfo).every(Boolean);
8084
- emitAvailableInfo({
8085
- hasInfo: availableInfo,
8086
- moreFields,
8087
- parseResult,
8088
- state,
8089
- returnValue,
8090
- contentLength,
8091
- name
8092
- });
8093
- if (hasAllInfo && !onVideoTrack && !onAudioTrack) {
9269
+ if (hasAllInfo() && !onVideoTrack && !onAudioTrack) {
8094
9270
  break;
8095
9271
  }
8096
9272
  if (parseResult && parseResult.status === "incomplete" && parseResult.skipTo !== null) {
@@ -8103,6 +9279,7 @@ var parseMedia = async ({
8103
9279
  iterator.skipTo(parseResult.skipTo, true);
8104
9280
  }
8105
9281
  }
9282
+ Log.verbose(logLevel, "Finished parsing file");
8106
9283
  emitAvailableInfo({
8107
9284
  hasInfo: Object.keys(fields ?? {}).reduce((acc, key) => {
8108
9285
  acc[key] = true;
@@ -8119,13 +9296,18 @@ var parseMedia = async ({
8119
9296
  iterator?.destroy();
8120
9297
  return returnValue;
8121
9298
  };
9299
+ // src/version.ts
9300
+ var VERSION = "4.0.232";
9301
+
8122
9302
  // src/index.ts
8123
9303
  var MediaParserInternals = {
8124
9304
  createMatroskaMedia,
8125
9305
  createIsoBaseMedia,
9306
+ createWav,
8126
9307
  Log
8127
9308
  };
8128
9309
  export {
8129
9310
  parseMedia,
9311
+ VERSION,
8130
9312
  MediaParserInternals
8131
9313
  };