@remotion/media-parser 4.0.284 → 4.0.286

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 (143) hide show
  1. package/dist/aac-codecprivate.d.ts +1 -1
  2. package/dist/combine-uint8-arrays.d.ts +1 -1
  3. package/dist/containers/aac/parse-aac.js +0 -3
  4. package/dist/containers/avc/create-sps-pps-data.d.ts +1 -1
  5. package/dist/containers/flac/get-channel-count.d.ts +1 -1
  6. package/dist/containers/flac/parse-flac-frame.js +0 -2
  7. package/dist/containers/flac/parse-streaminfo.js +0 -2
  8. package/dist/containers/iso-base-media/collect-sample-positions-from-trak.js +4 -3
  9. package/dist/containers/iso-base-media/get-moov-atom.js +0 -4
  10. package/dist/containers/iso-base-media/mdat/mdat.js +0 -3
  11. package/dist/containers/iso-base-media/mfra/get-mfra-atom.d.ts +3 -3
  12. package/dist/containers/iso-base-media/parse-boxes.js +0 -2
  13. package/dist/containers/iso-base-media/process-box.d.ts +0 -2
  14. package/dist/containers/iso-base-media/process-box.js +1 -3
  15. package/dist/containers/m3u/iterate-over-segment-files.js +1 -0
  16. package/dist/containers/m3u/run-over-m3u.js +0 -3
  17. package/dist/containers/mp3/parse-mpeg-header.js +0 -3
  18. package/dist/containers/riff/expect-riff-box.js +0 -2
  19. package/dist/containers/riff/parse-movi.js +0 -3
  20. package/dist/containers/transport-stream/adts-header.d.ts +1 -1
  21. package/dist/containers/transport-stream/discard-rest-of-packet.d.ts +1 -1
  22. package/dist/containers/transport-stream/find-separator.d.ts +17 -1
  23. package/dist/containers/transport-stream/find-separator.js +6 -5
  24. package/dist/containers/transport-stream/get-seeking-info.d.ts +4 -0
  25. package/dist/containers/transport-stream/get-seeking-info.js +17 -0
  26. package/dist/containers/transport-stream/handle-aac-packet.d.ts +10 -3
  27. package/dist/containers/transport-stream/handle-aac-packet.js +21 -16
  28. package/dist/containers/transport-stream/handle-avc-packet.d.ts +10 -3
  29. package/dist/containers/transport-stream/handle-avc-packet.js +26 -17
  30. package/dist/containers/transport-stream/parse-packet.d.ts +8 -4
  31. package/dist/containers/transport-stream/parse-packet.js +15 -16
  32. package/dist/containers/transport-stream/parse-pes.d.ts +5 -1
  33. package/dist/containers/transport-stream/parse-pes.js +4 -3
  34. package/dist/containers/transport-stream/parse-stream-packet.d.ts +6 -6
  35. package/dist/containers/transport-stream/parse-stream-packet.js +10 -115
  36. package/dist/containers/transport-stream/parse-transport-stream.js +15 -3
  37. package/dist/containers/transport-stream/process-stream-buffers.d.ts +26 -6
  38. package/dist/containers/transport-stream/process-stream-buffers.js +77 -12
  39. package/dist/containers/transport-stream/traversal.d.ts +3 -1
  40. package/dist/containers/transport-stream/traversal.js +10 -2
  41. package/dist/containers/wav/parse-fmt.js +0 -2
  42. package/dist/containers/wav/parse-media-section.js +0 -2
  43. package/dist/containers/webm/cues/fetch-web-cues.d.ts +12 -0
  44. package/dist/containers/webm/cues/fetch-web-cues.js +32 -0
  45. package/dist/containers/webm/cues/format-cues.d.ts +8 -0
  46. package/dist/containers/webm/cues/format-cues.js +41 -0
  47. package/dist/containers/webm/cues/get-seeking-byte.d.ts +14 -0
  48. package/dist/containers/webm/cues/get-seeking-byte.js +91 -0
  49. package/dist/containers/webm/fetch-web-cues.d.ts +12 -0
  50. package/dist/containers/webm/fetch-web-cues.js +29 -0
  51. package/dist/containers/webm/get-byte-for-cues.d.ts +5 -0
  52. package/dist/containers/webm/get-byte-for-cues.js +33 -0
  53. package/dist/containers/webm/get-ready-tracks.d.ts +9 -4
  54. package/dist/containers/webm/get-ready-tracks.js +6 -6
  55. package/dist/containers/webm/get-sample-from-block.d.ts +3 -2
  56. package/dist/containers/webm/get-sample-from-block.js +9 -8
  57. package/dist/containers/webm/get-seeking-byte.d.ts +14 -0
  58. package/dist/containers/webm/get-seeking-byte.js +91 -0
  59. package/dist/containers/webm/get-seeking-info.d.ts +3 -0
  60. package/dist/containers/webm/get-seeking-info.js +17 -0
  61. package/dist/containers/webm/parse-ebml.d.ts +5 -4
  62. package/dist/containers/webm/parse-ebml.js +29 -34
  63. package/dist/containers/webm/parse-webm-header.js +14 -2
  64. package/dist/containers/webm/seek/fetch-web-cues.d.ts +12 -0
  65. package/dist/containers/webm/seek/fetch-web-cues.js +32 -0
  66. package/dist/containers/webm/seek/format-cues.d.ts +8 -0
  67. package/dist/containers/webm/seek/format-cues.js +42 -0
  68. package/dist/containers/webm/seek/get-seeking-byte.d.ts +14 -0
  69. package/dist/containers/webm/seek/get-seeking-byte.js +100 -0
  70. package/dist/containers/webm/seek/get-seeking-info.d.ts +3 -0
  71. package/dist/containers/webm/seek/get-seeking-info.js +17 -0
  72. package/dist/containers/webm/segments/all-segments.d.ts +1 -0
  73. package/dist/containers/webm/segments.d.ts +10 -4
  74. package/dist/containers/webm/segments.js +30 -12
  75. package/dist/containers/webm/state-for-processing.d.ts +15 -0
  76. package/dist/containers/webm/state-for-processing.js +14 -0
  77. package/dist/controller/seek-signal.d.ts +2 -2
  78. package/dist/download-and-parse-media.js +2 -1
  79. package/dist/emit-audio-sample.d.ts +2 -5
  80. package/dist/emit-audio-sample.js +2 -5
  81. package/dist/esm/index.mjs +5057 -4394
  82. package/dist/esm/universal.mjs +1 -1
  83. package/dist/esm/web.mjs +1 -1
  84. package/dist/esm/worker-server-entry.mjs +2327 -1664
  85. package/dist/esm/worker-web-entry.mjs +2327 -1664
  86. package/dist/file-types/detect-file-type.js +3 -1
  87. package/dist/get-audio-codec.d.ts +2 -1
  88. package/dist/get-audio-codec.js +15 -1
  89. package/dist/get-sample-positions-from-mp4.d.ts +3 -0
  90. package/dist/get-sample-positions-from-mp4.js +46 -0
  91. package/dist/get-seeking-byte.d.ts +19 -0
  92. package/dist/get-seeking-byte.js +50 -0
  93. package/dist/get-seeking-info.d.ts +5 -10
  94. package/dist/get-seeking-info.js +12 -25
  95. package/dist/get-tracks.js +6 -2
  96. package/dist/index.d.ts +44 -19
  97. package/dist/init-video.js +4 -3
  98. package/dist/internal-parse-media.js +3 -2
  99. package/dist/iterator/buffer-iterator.d.ts +3 -3
  100. package/dist/iterator/buffer-manager.d.ts +3 -3
  101. package/dist/log.d.ts +5 -5
  102. package/dist/options.d.ts +1 -0
  103. package/dist/parse-loop.js +0 -3
  104. package/dist/parse-media-on-worker-entry.d.ts +1 -1
  105. package/dist/parse-media.js +2 -1
  106. package/dist/readers/from-web-file.js +1 -1
  107. package/dist/register-track.d.ts +2 -5
  108. package/dist/register-track.js +2 -10
  109. package/dist/seek-backwards.js +2 -3
  110. package/dist/seeking-info.d.ts +14 -1
  111. package/dist/state/iso-base-media/cached-sample-positions.d.ts +1 -1
  112. package/dist/state/keyframes.js +3 -0
  113. package/dist/state/matroska/lazy-cues-fetch.d.ts +19 -0
  114. package/dist/state/matroska/lazy-cues-fetch.js +51 -0
  115. package/dist/state/matroska/lazy-seek-fetch.d.ts +1 -0
  116. package/dist/state/matroska/lazy-seek-fetch.js +5 -0
  117. package/dist/state/matroska/webm.d.ts +46 -0
  118. package/dist/state/matroska/webm.js +121 -0
  119. package/dist/state/matroska.d.ts +0 -0
  120. package/dist/state/matroska.js +1 -0
  121. package/dist/state/parser-state.d.ts +34 -9
  122. package/dist/state/parser-state.js +7 -6
  123. package/dist/state/sample-callbacks.d.ts +1 -1
  124. package/dist/state/sample-callbacks.js +9 -9
  125. package/dist/state/samples-observed/slow-duration-fps.d.ts +11 -0
  126. package/dist/state/samples-observed/slow-duration-fps.js +92 -0
  127. package/dist/state/seek-infinite-loop.js +12 -2
  128. package/dist/state/transport-stream/observed-pes-header.d.ts +13 -0
  129. package/dist/state/transport-stream/observed-pes-header.js +29 -0
  130. package/dist/state/transport-stream/pts-start-offset.d.ts +5 -0
  131. package/dist/state/transport-stream/pts-start-offset.js +13 -0
  132. package/dist/state/transport-stream/start-offset.d.ts +2 -1
  133. package/dist/state/transport-stream/start-offset.js +3 -3
  134. package/dist/state/transport-stream/transport-stream.d.ts +6 -0
  135. package/dist/state/transport-stream/transport-stream.js +4 -2
  136. package/dist/state/video-section.js +14 -2
  137. package/dist/version.d.ts +1 -1
  138. package/dist/version.js +1 -1
  139. package/dist/work-on-seek-request.d.ts +8 -0
  140. package/dist/work-on-seek-request.js +24 -6
  141. package/dist/worker-server.js +1 -0
  142. package/dist/worker.d.ts +0 -1
  143. package/package.json +4 -4
@@ -361,7 +361,7 @@ var webFileReadContent = ({ src, range, controller }) => {
361
361
  }
362
362
  },
363
363
  contentLength: src.size,
364
- name: src.name,
364
+ name: src instanceof File ? src.name : src.toString(),
365
365
  supportsContentRange: true,
366
366
  contentType: src.type,
367
367
  needsContentRange: true
@@ -1943,12 +1943,13 @@ var isTransportStream = (data) => {
1943
1943
  return data[0] === 71 && data[188] === 71;
1944
1944
  };
1945
1945
  var isMp3 = (data) => {
1946
- const mpegPattern = new Uint8Array([255, 243, 228, 100]);
1946
+ const mpegPattern = new Uint8Array([255, 243]);
1947
+ const mpegPattern2 = new Uint8Array([255, 251]);
1947
1948
  const id3v4Pattern = new Uint8Array([73, 68, 51, 4]);
1948
1949
  const id3v3Pattern = new Uint8Array([73, 68, 51, 3]);
1949
1950
  const id3v2Pattern = new Uint8Array([73, 68, 51, 2]);
1950
1951
  const subarray = data.subarray(0, 4);
1951
- return matchesPattern(mpegPattern)(subarray) || matchesPattern(id3v4Pattern)(subarray) || matchesPattern(id3v3Pattern)(subarray) || matchesPattern(id3v2Pattern)(subarray);
1952
+ return matchesPattern(mpegPattern)(subarray) || matchesPattern(mpegPattern2)(subarray) || matchesPattern(id3v4Pattern)(subarray) || matchesPattern(id3v3Pattern)(subarray) || matchesPattern(id3v2Pattern)(subarray);
1952
1953
  };
1953
1954
  var isGif = (data) => {
1954
1955
  const gifPattern = new Uint8Array([71, 73, 70, 56]);
@@ -3326,8 +3327,15 @@ var findProgramAssociationTableOrThrow = (structure) => {
3326
3327
  }
3327
3328
  return box;
3328
3329
  };
3329
- var findProgramMapTableOrThrow = (structure) => {
3330
+ var findProgramMapOrNull = (structure) => {
3330
3331
  const box = structure.boxes.find((b) => b.type === "transport-stream-pmt-box");
3332
+ if (!box) {
3333
+ return null;
3334
+ }
3335
+ return box;
3336
+ };
3337
+ var findProgramMapTableOrThrow = (structure) => {
3338
+ const box = findProgramMapOrNull(structure);
3331
3339
  if (!box) {
3332
3340
  throw new Error("No PMT box found");
3333
3341
  }
@@ -3948,10 +3956,10 @@ var getTrack = ({
3948
3956
 
3949
3957
  // src/containers/webm/get-ready-tracks.ts
3950
3958
  var getTracksFromMatroska = ({
3951
- state
3959
+ structureState,
3960
+ webmState
3952
3961
  }) => {
3953
- const webmState = state.webm;
3954
- const structure = state.structure.getMatroskaStructure();
3962
+ const structure = structureState.getMatroskaStructure();
3955
3963
  const mainSegment = getMainSegment(structure.boxes);
3956
3964
  if (!mainSegment) {
3957
3965
  throw new Error("No main segment");
@@ -3992,14 +4000,18 @@ var getTracksFromMatroska = ({
3992
4000
  }
3993
4001
  return { missingInfo, resolved: resolvedTracks };
3994
4002
  };
3995
- var matroskaHasTracks = (state) => {
3996
- const structure = state.structure.getMatroskaStructure();
4003
+ var matroskaHasTracks = ({
4004
+ structureState,
4005
+ webmState
4006
+ }) => {
4007
+ const structure = structureState.getMatroskaStructure();
3997
4008
  const mainSegment = getMainSegment(structure.boxes);
3998
4009
  if (!mainSegment) {
3999
4010
  return false;
4000
4011
  }
4001
4012
  return getTracksSegment(mainSegment) !== null && getTracksFromMatroska({
4002
- state
4013
+ structureState,
4014
+ webmState
4003
4015
  }).missingInfo.length === 0;
4004
4016
  };
4005
4017
 
@@ -4014,7 +4026,10 @@ var isoBaseMediaHasTracks = (state) => {
4014
4026
  var getHasTracks = (state) => {
4015
4027
  const structure = state.structure.getStructure();
4016
4028
  if (structure.type === "matroska") {
4017
- return matroskaHasTracks(state);
4029
+ return matroskaHasTracks({
4030
+ structureState: state.structure,
4031
+ webmState: state.webm
4032
+ });
4018
4033
  }
4019
4034
  if (structure.type === "iso-base-media") {
4020
4035
  return isoBaseMediaHasTracks(state);
@@ -4047,7 +4062,8 @@ var getCategorizedTracksFromMatroska = (state) => {
4047
4062
  const audioTracks = [];
4048
4063
  const otherTracks = [];
4049
4064
  const { resolved } = getTracksFromMatroska({
4050
- state
4065
+ structureState: state.structure,
4066
+ webmState: state.webm
4051
4067
  });
4052
4068
  for (const track of resolved) {
4053
4069
  if (track.type === "video") {
@@ -4270,6 +4286,9 @@ var getAudioCodecFromTrak = (trak) => {
4270
4286
  var isLpcmAudioCodec = (trak) => {
4271
4287
  return getAudioCodecFromTrak(trak)?.format === "lpcm";
4272
4288
  };
4289
+ var isIn24AudioCodec = (trak) => {
4290
+ return getAudioCodecFromTrak(trak)?.format === "in24";
4291
+ };
4273
4292
  var getAudioCodecStringFromTrak = (trak) => {
4274
4293
  const codec = getAudioCodecFromTrak(trak);
4275
4294
  if (!codec) {
@@ -4281,6 +4300,12 @@ var getAudioCodecStringFromTrak = (trak) => {
4281
4300
  description: codec.description
4282
4301
  };
4283
4302
  }
4303
+ if (codec.format === "in24") {
4304
+ return {
4305
+ codecString: "pcm-s24",
4306
+ description: undefined
4307
+ };
4308
+ }
4284
4309
  const codecStringWithoutMp3Exception = [
4285
4310
  codec.format,
4286
4311
  codec.primarySpecificator ? codec.primarySpecificator.toString(16) : null,
@@ -4296,6 +4321,9 @@ var getAudioCodecFromAudioCodecInfo = (codec) => {
4296
4321
  if (codec.format === "twos") {
4297
4322
  return "pcm-s16";
4298
4323
  }
4324
+ if (codec.format === "in24") {
4325
+ return "pcm-s24";
4326
+ }
4299
4327
  if (codec.format === "lpcm") {
4300
4328
  return "pcm-s16";
4301
4329
  }
@@ -4547,8 +4575,8 @@ var getSamplePositions = ({
4547
4575
  return samples;
4548
4576
  };
4549
4577
 
4550
- // src/get-sample-positions-from-lpcm.ts
4551
- var getSamplePositionsFromLpcm = (trakBox) => {
4578
+ // src/get-sample-positions-from-mp4.ts
4579
+ var getGroupedSamplesPositionsFromMp4 = (trakBox) => {
4552
4580
  const stscBox = getStscBox(trakBox);
4553
4581
  const stszBox = getStszBox(trakBox);
4554
4582
  const stcoBox = getStcoBox(trakBox);
@@ -4591,8 +4619,9 @@ var getSamplePositionsFromLpcm = (trakBox) => {
4591
4619
  var collectSamplePositionsFromTrak = (trakBox) => {
4592
4620
  const isLpcm = isLpcmAudioCodec(trakBox);
4593
4621
  const timescaleAndDuration = getTimescaleAndDuration(trakBox);
4594
- if (isLpcm) {
4595
- return getSamplePositionsFromLpcm(trakBox);
4622
+ const isIn24 = isIn24AudioCodec(trakBox);
4623
+ if (isLpcm || isIn24) {
4624
+ return getGroupedSamplesPositionsFromMp4(trakBox);
4596
4625
  }
4597
4626
  const stszBox = getStszBox(trakBox);
4598
4627
  const stcoBox = getStcoBox(trakBox);
@@ -5390,9 +5419,16 @@ var mediaSectionState = () => {
5390
5419
  const mediaSections = [];
5391
5420
  const addMediaSection = (section) => {
5392
5421
  const overlaps = mediaSections.some((existingSection) => section.start < existingSection.start + existingSection.size && section.start + section.size > existingSection.start);
5393
- if (!overlaps) {
5394
- mediaSections.push(section);
5422
+ if (overlaps) {
5423
+ return;
5424
+ }
5425
+ for (let i = mediaSections.length - 1;i >= 0; i--) {
5426
+ const existingSection = mediaSections[i];
5427
+ if (section.start <= existingSection.start && section.start + section.size >= existingSection.start + existingSection.size) {
5428
+ mediaSections.splice(i, 1);
5429
+ }
5395
5430
  }
5431
+ mediaSections.push(section);
5396
5432
  };
5397
5433
  const getMediaSections = () => {
5398
5434
  return mediaSections;
@@ -5608,33 +5644,6 @@ var getSeekingByteFromIsoBaseMedia = async ({
5608
5644
  };
5609
5645
  };
5610
5646
 
5611
- // src/containers/iso-base-media/get-seeking-info-from-mp4.ts
5612
- var getSeekingInfoFromMp4 = ({
5613
- structureState,
5614
- isoState,
5615
- mp4HeaderSegment,
5616
- mediaSectionState: mediaSectionState2
5617
- }) => {
5618
- const structure = structureState.getIsoStructure();
5619
- const moovAtom = getMoovBoxFromState({
5620
- isoState,
5621
- mp4HeaderSegment,
5622
- structureState
5623
- });
5624
- const moofBoxes = getMoofBoxes(structure.boxes);
5625
- const tfraBoxes = getTfraBoxes(structure);
5626
- if (!moovAtom) {
5627
- return null;
5628
- }
5629
- return {
5630
- type: "iso-base-media-seeking-info",
5631
- moovBox: moovAtom,
5632
- moofBoxes,
5633
- tfraBoxes,
5634
- mediaSections: mediaSectionState2.getMediaSections()
5635
- };
5636
- };
5637
-
5638
5647
  // src/containers/wav/get-seeking-byte.ts
5639
5648
  var WAVE_SAMPLES_PER_SECOND = 25;
5640
5649
  var getSeekingByteFromWav = ({
@@ -5651,482 +5660,1204 @@ var getSeekingByteFromWav = ({
5651
5660
  });
5652
5661
  };
5653
5662
 
5654
- // src/containers/wav/get-seeking-info.ts
5655
- var getSeekingInfoFromWav = ({
5656
- structure,
5657
- mediaSectionState: mediaSectionState2
5663
+ // src/containers/webm/seek/get-seeking-byte.ts
5664
+ var toSeconds = (timeInTimescale, track) => {
5665
+ return timeInTimescale / track.timescale * 1000;
5666
+ };
5667
+ var findBiggestCueBeforeTime = ({
5668
+ cues,
5669
+ time,
5670
+ track
5658
5671
  }) => {
5659
- const fmtBox = structure.boxes.find((box) => box.type === "wav-fmt");
5660
- if (!fmtBox) {
5661
- return null;
5672
+ let biggestCueBeforeTime;
5673
+ for (const cue of cues) {
5674
+ const cueTimeInSeconds = toSeconds(cue.timeInTimescale, track);
5675
+ if (cueTimeInSeconds < time && (!biggestCueBeforeTime || cueTimeInSeconds > toSeconds(biggestCueBeforeTime.timeInTimescale, track))) {
5676
+ biggestCueBeforeTime = cue;
5677
+ }
5662
5678
  }
5663
- const mediaSection = mediaSectionState2.getMediaSections();
5664
- if (mediaSection.length !== 1) {
5665
- return null;
5679
+ return biggestCueBeforeTime;
5680
+ };
5681
+ var findKeyframeBeforeTime2 = ({
5682
+ keyframes,
5683
+ time
5684
+ }) => {
5685
+ let keyframeBeforeTime;
5686
+ for (const keyframe of keyframes) {
5687
+ if (keyframe.decodingTimeInSeconds < time && (!keyframeBeforeTime || keyframe.decodingTimeInSeconds > keyframeBeforeTime.decodingTimeInSeconds)) {
5688
+ keyframeBeforeTime = keyframe;
5689
+ }
5666
5690
  }
5667
- return {
5668
- type: "wav-seeking-info",
5669
- sampleRate: fmtBox.sampleRate,
5670
- blockAlign: fmtBox.blockAlign,
5671
- mediaSections: mediaSection[0]
5672
- };
5691
+ return keyframeBeforeTime?.positionInBytes ?? null;
5673
5692
  };
5674
-
5675
- // src/get-seeking-info.ts
5676
- var getSeekingInfo = ({
5677
- structureState,
5678
- mp4HeaderSegment,
5679
- mediaSectionState: mediaSectionState2,
5680
- isoState
5693
+ var getByteFromCues = ({
5694
+ cuesResponse,
5695
+ time,
5696
+ info,
5697
+ logLevel
5681
5698
  }) => {
5682
- const structure = structureState.getStructureOrNull();
5683
- if (!structure) {
5699
+ if (!cuesResponse) {
5700
+ Log.trace(logLevel, "Has no Matroska cues at the moment, cannot use them");
5684
5701
  return null;
5685
5702
  }
5686
- if (structure.type === "iso-base-media") {
5687
- return getSeekingInfoFromMp4({
5688
- structureState,
5689
- isoState,
5690
- mp4HeaderSegment,
5691
- mediaSectionState: mediaSectionState2
5692
- });
5693
- }
5694
- if (structure.type === "wav") {
5695
- return getSeekingInfoFromWav({
5696
- structure,
5697
- mediaSectionState: mediaSectionState2
5698
- });
5703
+ const { cues, segmentOffset } = cuesResponse;
5704
+ Log.trace(logLevel, "Has Matroska cues. Will use them to perform a seek.");
5705
+ const biggestCueBeforeTime = findBiggestCueBeforeTime({
5706
+ cues,
5707
+ time,
5708
+ track: info.track
5709
+ });
5710
+ if (!biggestCueBeforeTime) {
5711
+ return null;
5699
5712
  }
5700
- throw new Error(`Seeking is not supported for this format: ${structure.type}`);
5713
+ return biggestCueBeforeTime.clusterPositionInSegment + segmentOffset;
5701
5714
  };
5702
- var getSeekingByte = ({
5703
- info,
5715
+ var getSeekingByteFromMatroska = async ({
5704
5716
  time,
5717
+ webmState,
5718
+ info,
5705
5719
  logLevel,
5706
- currentPosition,
5707
- isoState
5720
+ mediaSection,
5721
+ keyframes
5708
5722
  }) => {
5709
- if (info.type === "iso-base-media-seeking-info") {
5710
- return getSeekingByteFromIsoBaseMedia({
5711
- info,
5712
- time,
5713
- logLevel,
5714
- currentPosition,
5715
- isoState
5716
- });
5723
+ if (!info.track) {
5724
+ Log.trace(logLevel, "No video track found, cannot seek yet");
5725
+ return {
5726
+ type: "valid-but-must-wait"
5727
+ };
5717
5728
  }
5718
- if (info.type === "wav-seeking-info") {
5719
- return getSeekingByteFromWav({
5720
- info,
5721
- time
5722
- });
5729
+ const cuesResponse = await webmState.cues.getLoadedCues();
5730
+ const byteFromObservedKeyframe = findKeyframeBeforeTime2({
5731
+ keyframes: keyframes.getKeyframes(),
5732
+ time
5733
+ });
5734
+ const byteFromCues = getByteFromCues({
5735
+ cuesResponse,
5736
+ time,
5737
+ info,
5738
+ logLevel
5739
+ });
5740
+ const byteFromFirstMediaSection = webmState.getFirstCluster()?.start ?? null;
5741
+ const seekPossibilities = [
5742
+ byteFromCues,
5743
+ byteFromObservedKeyframe,
5744
+ byteFromFirstMediaSection
5745
+ ].filter((n) => n !== null);
5746
+ const byteToSeekTo = seekPossibilities.length === 0 ? null : Math.max(...seekPossibilities);
5747
+ if (byteToSeekTo === null) {
5748
+ return {
5749
+ type: "invalid"
5750
+ };
5723
5751
  }
5724
- throw new Error(`Unknown seeking info type: ${info}`);
5752
+ mediaSection.addMediaSection({
5753
+ start: byteToSeekTo,
5754
+ size: 1
5755
+ });
5756
+ return {
5757
+ type: "do-seek",
5758
+ byte: byteToSeekTo
5759
+ };
5725
5760
  };
5726
5761
 
5727
- // src/seek-backwards.ts
5728
- var seekBackwards = async ({
5729
- iterator,
5730
- seekTo,
5731
- readerInterface,
5732
- src,
5733
- controller,
5734
- logLevel,
5735
- currentReader
5762
+ // src/convert-audio-or-video-sample.ts
5763
+ var TARGET_TIMESCALE = 1e6;
5764
+ var fixFloat = (value) => {
5765
+ if (value % 1 < 0.0000001) {
5766
+ return Math.floor(value);
5767
+ }
5768
+ if (value % 1 > 0.9999999) {
5769
+ return Math.ceil(value);
5770
+ }
5771
+ return value;
5772
+ };
5773
+ var convertAudioOrVideoSampleToWebCodecsTimestamps = ({
5774
+ sample,
5775
+ timescale
5736
5776
  }) => {
5737
- const howManyBytesNotYetDiscarded = iterator.counter.getDiscardedOffset();
5738
- const howManyBytesWeCanGoBack = iterator.counter.getOffset() - howManyBytesNotYetDiscarded;
5739
- if (iterator.counter.getOffset() - howManyBytesWeCanGoBack <= seekTo) {
5740
- iterator.skipTo(seekTo);
5741
- return;
5777
+ if (timescale === TARGET_TIMESCALE) {
5778
+ return sample;
5742
5779
  }
5743
- const time = Date.now();
5744
- Log.verbose(logLevel, `Seeking in video from position ${iterator.counter.getOffset()} -> ${seekTo}. Re-reading because this portion is not available`);
5745
- const { reader: newReader } = await readerInterface.read({
5746
- src,
5747
- range: seekTo,
5748
- controller
5749
- });
5750
- iterator.replaceData(new Uint8Array([]), seekTo);
5751
- Log.verbose(logLevel, `Re-reading took ${Date.now() - time}ms. New position: ${iterator.counter.getOffset()}`);
5752
- currentReader.setCurrent(newReader);
5780
+ const { cts, dts, timestamp } = sample;
5781
+ return {
5782
+ cts: fixFloat(cts * (TARGET_TIMESCALE / timescale)),
5783
+ dts: fixFloat(dts * (TARGET_TIMESCALE / timescale)),
5784
+ timestamp: fixFloat(timestamp * (TARGET_TIMESCALE / timescale)),
5785
+ duration: sample.duration === undefined ? undefined : fixFloat(sample.duration * (TARGET_TIMESCALE / timescale)),
5786
+ data: sample.data,
5787
+ trackId: sample.trackId,
5788
+ type: sample.type,
5789
+ offset: sample.offset,
5790
+ timescale: TARGET_TIMESCALE
5791
+ };
5753
5792
  };
5754
5793
 
5755
- // src/state/need-samples-for-fields.ts
5756
- var fieldsNeedSamplesMap = {
5757
- slowDurationInSeconds: true,
5758
- slowFps: true,
5759
- slowKeyframes: true,
5760
- slowNumberOfFrames: true,
5761
- audioCodec: false,
5762
- container: false,
5763
- dimensions: false,
5764
- durationInSeconds: false,
5765
- fps: false,
5766
- internalStats: false,
5767
- isHdr: false,
5768
- name: false,
5769
- rotation: false,
5770
- size: false,
5771
- structure: false,
5772
- tracks: false,
5773
- unrotatedDimensions: false,
5774
- videoCodec: false,
5775
- metadata: false,
5776
- location: false,
5777
- mimeType: false,
5778
- keyframes: false,
5779
- images: false,
5780
- numberOfAudioChannels: false,
5781
- sampleRate: false,
5782
- slowAudioBitrate: true,
5783
- slowVideoBitrate: true,
5784
- m3uStreams: false
5785
- };
5786
- var needsToIterateOverSamples = ({
5787
- fields,
5788
- emittedFields
5794
+ // src/emit-audio-sample.ts
5795
+ var emitAudioSample = async ({
5796
+ trackId,
5797
+ audioSample,
5798
+ callbacks
5789
5799
  }) => {
5790
- const keys = Object.keys(fields ?? {});
5791
- const selectedKeys = keys.filter((k) => fields[k]);
5792
- return selectedKeys.some((k) => fieldsNeedSamplesMap[k] && !emittedFields[k]);
5800
+ await callbacks.onAudioSample(trackId, audioSample);
5793
5801
  };
5794
-
5795
- // src/disallow-forward-seek-if-samples-are-needed.ts
5796
- var disallowForwardSeekIfSamplesAreNeeded = ({
5797
- seekTo,
5798
- previousPosition,
5799
- fields
5802
+ var emitVideoSample = async ({
5803
+ trackId,
5804
+ videoSample,
5805
+ callbacks
5800
5806
  }) => {
5801
- const fieldsNeedingSamples = Object.entries(fields).filter(([, value]) => value).map(([key]) => key).filter((key) => fieldsNeedSamplesMap[key]);
5802
- if (fieldsNeedingSamples.length > 0) {
5803
- throw new Error(`Forward seeking is not allowed when the following fields are requested from parseMedia(): ${fieldsNeedingSamples.join(", ")}. Seek was from 0x${previousPosition.toString(16)} to 0x${seekTo.toString(16)}. Either don't seek forward, or don't request these fields.`);
5804
- }
5807
+ await callbacks.onVideoSample(trackId, videoSample);
5805
5808
  };
5806
5809
 
5807
- // src/seek-forwards.ts
5808
- var seekForward = async ({
5809
- seekTo,
5810
- userInitiated,
5811
- iterator,
5812
- fields,
5813
- logLevel,
5814
- currentReader,
5815
- readerInterface,
5816
- src,
5817
- controller,
5818
- discardReadBytes
5810
+ // src/register-track.ts
5811
+ var registerVideoTrack = async ({
5812
+ track,
5813
+ container,
5814
+ logLevel,
5815
+ onVideoTrack,
5816
+ registerVideoSampleCallback,
5817
+ tracks: tracks2
5819
5818
  }) => {
5820
- if (userInitiated) {
5821
- disallowForwardSeekIfSamplesAreNeeded({
5822
- fields,
5823
- seekTo,
5824
- previousPosition: iterator.counter.getOffset()
5825
- });
5819
+ if (tracks2.getTracks().find((t) => t.trackId === track.trackId)) {
5820
+ Log.trace(logLevel, `Track ${track.trackId} already registered, skipping`);
5821
+ return null;
5826
5822
  }
5827
- const alreadyHasBuffer = iterator.bytesRemaining() >= seekTo - iterator.counter.getOffset();
5828
- Log.verbose(logLevel, `Performing seek from ${iterator.counter.getOffset()} to ${seekTo}`);
5829
- if (alreadyHasBuffer) {
5830
- iterator.skipTo(seekTo);
5831
- Log.verbose(logLevel, `Already read ahead enough, skipping forward`);
5832
- return;
5823
+ if (track.type !== "video") {
5824
+ throw new Error("Expected video track");
5833
5825
  }
5834
- const time = Date.now();
5835
- Log.verbose(logLevel, `Skipping over video data from position ${iterator.counter.getOffset()} -> ${seekTo}. Re-reading because this portion is not available`);
5836
- const { reader: newReader } = await readerInterface.read({
5837
- src,
5838
- range: seekTo,
5839
- controller
5826
+ tracks2.addTrack(track);
5827
+ if (!onVideoTrack) {
5828
+ return null;
5829
+ }
5830
+ const callback = await onVideoTrack({
5831
+ track,
5832
+ container
5840
5833
  });
5841
- iterator.skipTo(seekTo);
5842
- await discardReadBytes(true);
5843
- Log.verbose(logLevel, `Re-reading took ${Date.now() - time}ms. New position: ${iterator.counter.getOffset()}`);
5844
- currentReader.setCurrent(newReader);
5834
+ await registerVideoSampleCallback(track.trackId, callback ?? null);
5835
+ return callback;
5845
5836
  };
5846
-
5847
- // src/perform-seek.ts
5848
- var performSeek = async ({
5849
- seekTo,
5850
- userInitiated,
5851
- controller,
5852
- mediaSection,
5853
- iterator,
5854
- seekInfiniteLoop,
5837
+ var registerAudioTrack = async ({
5838
+ track,
5839
+ container,
5840
+ tracks: tracks2,
5855
5841
  logLevel,
5856
- mode,
5857
- contentLength,
5858
- currentReader,
5859
- readerInterface,
5860
- src,
5861
- discardReadBytes,
5862
- fields
5842
+ onAudioTrack,
5843
+ registerAudioSampleCallback
5863
5844
  }) => {
5864
- const byteInMediaSection = isByteInMediaSection({
5865
- position: seekTo,
5866
- mediaSections: mediaSection.getMediaSections()
5867
- });
5868
- if (byteInMediaSection !== "in-section" && userInitiated) {
5869
- const sections = mediaSection.getMediaSections();
5870
- const sectionStrings = sections.map((section) => {
5871
- return `start: ${section.start}, end: ${section.size + section.start}`;
5872
- });
5873
- throw new Error(`Cannot seek to a byte that is not in the video section. Seeking to: ${seekTo}, sections: ${sectionStrings.join(" | ")}`);
5874
- }
5875
- seekInfiniteLoop.registerSeek(seekTo);
5876
- if (seekTo <= iterator.counter.getOffset() && mode === "download") {
5877
- throw new Error(`Seeking backwards is not supported in parseAndDownloadMedia() mode. Current position: ${iterator.counter.getOffset()}, seekTo: ${seekTo}`);
5878
- }
5879
- if (seekTo > contentLength) {
5880
- throw new Error(`Cannot seek beyond the end of the file: ${seekTo} > ${contentLength}`);
5845
+ if (tracks2.getTracks().find((t) => t.trackId === track.trackId)) {
5846
+ Log.trace(logLevel, `Track ${track.trackId} already registered, skipping`);
5847
+ return null;
5881
5848
  }
5882
- if (mode === "download") {
5883
- Log.verbose(logLevel, `Skipping over video data from position ${iterator.counter.getOffset()} -> ${seekTo}. Fetching but not reading all the data inbetween because in download mode`);
5884
- iterator.discard(seekTo - iterator.counter.getOffset());
5885
- return;
5849
+ if (track.type !== "audio") {
5850
+ throw new Error("Expected audio track");
5886
5851
  }
5887
- await controller._internals.checkForAbortAndPause();
5888
- const alreadyAtByte = iterator.counter.getOffset() === seekTo;
5889
- if (alreadyAtByte) {
5890
- Log.verbose(logLevel, `Already at the desired position, seeking done`);
5891
- controller._internals.performedSeeksSignal.markLastSeekAsUserInitiated();
5892
- return;
5852
+ tracks2.addTrack(track);
5853
+ if (!onAudioTrack) {
5854
+ return null;
5893
5855
  }
5894
- const skippingForward = seekTo > iterator.counter.getOffset();
5895
- controller._internals.performedSeeksSignal.recordSeek({
5896
- from: iterator.counter.getOffset(),
5897
- to: seekTo,
5898
- type: userInitiated ? "user-initiated" : "internal"
5856
+ const callback = await onAudioTrack({
5857
+ track,
5858
+ container
5899
5859
  });
5900
- if (skippingForward) {
5901
- await seekForward({
5902
- seekTo,
5903
- userInitiated,
5904
- iterator,
5905
- fields,
5906
- logLevel,
5907
- currentReader,
5908
- readerInterface,
5909
- src,
5910
- controller,
5911
- discardReadBytes
5912
- });
5913
- } else {
5914
- await seekBackwards({
5915
- controller,
5916
- seekTo,
5917
- iterator,
5918
- logLevel,
5919
- currentReader,
5920
- readerInterface,
5921
- src
5922
- });
5923
- }
5924
- await controller._internals.checkForAbortAndPause();
5860
+ await registerAudioSampleCallback(track.trackId, callback ?? null);
5861
+ return callback;
5925
5862
  };
5926
-
5927
- // src/work-on-seek-request.ts
5928
- var turnSeekIntoByte = async ({
5929
- seek: seek2,
5930
- mediaSectionState: mediaSectionState2,
5931
- logLevel,
5932
- iterator,
5933
- structureState,
5934
- mp4HeaderSegment,
5935
- isoState
5863
+ var registerVideoTrackWhenProfileIsAvailable = ({
5864
+ state,
5865
+ track,
5866
+ container
5936
5867
  }) => {
5937
- const mediaSections = mediaSectionState2.getMediaSections();
5938
- if (mediaSections.length === 0) {
5939
- Log.trace(logLevel, "No video sections defined, cannot seek yet");
5940
- return {
5941
- type: "valid-but-must-wait"
5942
- };
5943
- }
5944
- if (seek2.type === "keyframe-before-time-in-seconds") {
5945
- const seekingInfo = getSeekingInfo({
5946
- structureState,
5947
- mp4HeaderSegment,
5948
- mediaSectionState: mediaSectionState2,
5949
- isoState
5950
- });
5951
- if (!seekingInfo) {
5952
- Log.trace(logLevel, "No seeking info, cannot seek yet");
5953
- return {
5954
- type: "valid-but-must-wait"
5955
- };
5956
- }
5957
- const seekingByte = await getSeekingByte({
5958
- info: seekingInfo,
5959
- time: seek2.time,
5960
- logLevel,
5961
- currentPosition: iterator.counter.getOffset(),
5962
- isoState
5868
+ state.riff.registerOnAvcProfileCallback(async (profile) => {
5869
+ await registerVideoTrack({
5870
+ track: addAvcProfileToTrack(track, profile),
5871
+ container,
5872
+ logLevel: state.logLevel,
5873
+ onVideoTrack: state.onVideoTrack,
5874
+ registerVideoSampleCallback: state.callbacks.registerVideoSampleCallback,
5875
+ tracks: state.callbacks.tracks
5963
5876
  });
5964
- return seekingByte;
5965
- }
5966
- if (seek2.type === "byte") {
5877
+ });
5878
+ };
5879
+
5880
+ // src/containers/avc/interpret-sps.ts
5881
+ var getDimensionsFromSps = (sps) => {
5882
+ const height = sps.pic_height_in_map_units_minus1;
5883
+ const width = sps.pic_width_in_mbs_minus1;
5884
+ return {
5885
+ height: (height + 1) * 16 - (sps.frame_crop_bottom_offset ?? 0) * 2 - (sps.frame_crop_top_offset ?? 0) * 2,
5886
+ width: (width + 1) * 16 - (sps.frame_crop_right_offset ?? 0) * 2 - (sps.frame_crop_left_offset ?? 0) * 2
5887
+ };
5888
+ };
5889
+ var getSampleAspectRatioFromSps = (sps) => {
5890
+ if (sps.vui_parameters?.sar_height && sps.vui_parameters.sar_width) {
5967
5891
  return {
5968
- type: "do-seek",
5969
- byte: seek2.byte
5892
+ width: sps.vui_parameters.sar_width,
5893
+ height: sps.vui_parameters.sar_height
5970
5894
  };
5971
5895
  }
5972
- throw new Error(`Cannot process seek request for ${seek2}: ${JSON.stringify(seek2)}`);
5896
+ return {
5897
+ width: 1,
5898
+ height: 1
5899
+ };
5973
5900
  };
5974
- var getWorkOnSeekRequestOptions = (state) => {
5901
+ var getVideoColorFromSps = (sps) => {
5902
+ const matrixCoefficients2 = sps.vui_parameters?.matrix_coefficients;
5903
+ const transferCharacteristics2 = sps.vui_parameters?.transfer_characteristics;
5904
+ const colorPrimaries = sps.vui_parameters?.colour_primaries;
5975
5905
  return {
5976
- logLevel: state.logLevel,
5977
- controller: state.controller,
5978
- isoState: state.iso,
5979
- iterator: state.iterator,
5980
- structureState: state.structure,
5981
- src: state.src,
5982
- contentLength: state.contentLength,
5983
- readerInterface: state.readerInterface,
5984
- mediaSection: state.mediaSection,
5985
- mp4HeaderSegment: state.mp4HeaderSegment,
5986
- mode: state.mode,
5987
- seekInfiniteLoop: state.seekInfiniteLoop,
5988
- currentReader: state.currentReader,
5989
- discardReadBytes: state.discardReadBytes,
5990
- fields: state.fields
5906
+ matrixCoefficients: matrixCoefficients2 ? getMatrixCoefficientsFromIndex(matrixCoefficients2) : null,
5907
+ transferCharacteristics: transferCharacteristics2 ? getTransferCharacteristicsFromIndex(transferCharacteristics2) : null,
5908
+ primaries: colorPrimaries ? getPrimariesFromIndex(colorPrimaries) : null,
5909
+ fullRange: sps.vui_parameters?.video_full_range_flag ?? null
5991
5910
  };
5992
5911
  };
5993
- var workOnSeekRequest = async (options) => {
5994
- const {
5995
- logLevel,
5996
- controller,
5997
- mediaSection,
5998
- mp4HeaderSegment,
5999
- isoState,
6000
- iterator,
6001
- structureState,
6002
- src,
6003
- contentLength,
6004
- readerInterface,
6005
- mode,
6006
- seekInfiniteLoop,
6007
- currentReader,
6008
- discardReadBytes,
6009
- fields
6010
- } = options;
6011
- const seek2 = controller._internals.seekSignal.getSeek();
6012
- if (!seek2) {
6013
- return;
6014
- }
6015
- Log.trace(logLevel, `Has seek request: ${JSON.stringify(seek2)}`);
6016
- const resolution = await turnSeekIntoByte({
6017
- seek: seek2,
6018
- mediaSectionState: mediaSection,
6019
- logLevel,
6020
- iterator,
6021
- structureState,
6022
- mp4HeaderSegment,
6023
- isoState
6024
- });
6025
- Log.trace(logLevel, `Seek action: ${JSON.stringify(resolution)}`);
6026
- if (resolution.type === "intermediary-seek") {
6027
- await performSeek({
6028
- seekTo: resolution.byte,
6029
- userInitiated: false,
6030
- controller,
6031
- mediaSection,
6032
- iterator,
6033
- logLevel,
6034
- mode,
6035
- contentLength,
6036
- seekInfiniteLoop,
6037
- currentReader,
6038
- readerInterface,
6039
- src,
6040
- discardReadBytes,
6041
- fields
6042
- });
6043
- return;
5912
+
5913
+ // src/containers/avc/key.ts
5914
+ var getKeyFrameOrDeltaFromAvcInfo = (infos) => {
5915
+ const keyOrDelta = infos.find((i) => i.type === "keyframe" || i.type === "delta-frame");
5916
+ if (!keyOrDelta) {
5917
+ throw new Error("expected avc to contain info about key or delta");
6044
5918
  }
6045
- if (resolution.type === "do-seek") {
6046
- await performSeek({
6047
- seekTo: resolution.byte,
6048
- userInitiated: true,
6049
- controller,
6050
- mediaSection,
6051
- iterator,
6052
- logLevel,
6053
- mode,
6054
- contentLength,
6055
- seekInfiniteLoop,
6056
- currentReader,
6057
- readerInterface,
6058
- src,
6059
- discardReadBytes,
6060
- fields
6061
- });
6062
- const { hasChanged } = controller._internals.seekSignal.clearSeekIfStillSame(seek2);
6063
- if (hasChanged) {
6064
- Log.trace(logLevel, `Seek request has changed while seeking, seeking again`);
6065
- await workOnSeekRequest(options);
5919
+ return keyOrDelta.type === "keyframe" ? "key" : "delta";
5920
+ };
5921
+
5922
+ // src/containers/avc/parse-avc.ts
5923
+ var Extended_SAR = 255;
5924
+ var readVuiParameters = (iterator) => {
5925
+ let sar_width = null;
5926
+ let sar_height = null;
5927
+ let overscan_appropriate_flag = null;
5928
+ let video_format = null;
5929
+ let video_full_range_flag = null;
5930
+ let colour_primaries = null;
5931
+ let transfer_characteristics = null;
5932
+ let matrix_coefficients = null;
5933
+ let chroma_sample_loc_type_top_field = null;
5934
+ let chroma_sample_loc_type_bottom_field = null;
5935
+ const aspect_ratio_info_present_flag = iterator.getBits(1);
5936
+ if (aspect_ratio_info_present_flag) {
5937
+ const aspect_ratio_idc = iterator.getBits(8);
5938
+ if (aspect_ratio_idc === Extended_SAR) {
5939
+ sar_width = iterator.getBits(16);
5940
+ sar_height = iterator.getBits(16);
6066
5941
  }
6067
- return;
6068
5942
  }
6069
- if (resolution.type === "invalid") {
6070
- throw new Error(`The seek request ${JSON.stringify(seek2)} cannot be processed`);
5943
+ const overscan_info_present_flag = iterator.getBits(1);
5944
+ if (overscan_info_present_flag) {
5945
+ overscan_appropriate_flag = iterator.getBits(1);
6071
5946
  }
6072
- if (resolution.type === "valid-but-must-wait") {
6073
- Log.trace(logLevel, "Seek request is valid but cannot be processed yet");
5947
+ const video_signal_type_present_flag = iterator.getBits(1);
5948
+ if (video_signal_type_present_flag) {
5949
+ video_format = iterator.getBits(3);
5950
+ video_full_range_flag = Boolean(iterator.getBits(1));
5951
+ const colour_description_present_flag = iterator.getBits(1);
5952
+ if (colour_description_present_flag) {
5953
+ colour_primaries = iterator.getBits(8);
5954
+ transfer_characteristics = iterator.getBits(8);
5955
+ matrix_coefficients = iterator.getBits(8);
5956
+ }
5957
+ }
5958
+ const chroma_loc_info_present_flag = iterator.getBits(1);
5959
+ if (chroma_loc_info_present_flag) {
5960
+ chroma_sample_loc_type_top_field = iterator.readExpGolomb();
5961
+ chroma_sample_loc_type_bottom_field = iterator.readExpGolomb();
6074
5962
  }
5963
+ return {
5964
+ sar_width,
5965
+ sar_height,
5966
+ overscan_appropriate_flag,
5967
+ chroma_sample_loc_type_bottom_field,
5968
+ chroma_sample_loc_type_top_field,
5969
+ colour_primaries,
5970
+ matrix_coefficients,
5971
+ transfer_characteristics,
5972
+ video_format,
5973
+ video_full_range_flag
5974
+ };
6075
5975
  };
6076
-
6077
- // src/emit-available-info.ts
6078
- var emitAvailableInfo = async ({
6079
- hasInfo,
6080
- state
6081
- }) => {
6082
- const keys = Object.keys(hasInfo);
6083
- const {
6084
- emittedFields,
6085
- fieldsInReturnValue,
6086
- returnValue,
6087
- name,
6088
- callbackFunctions
6089
- } = state;
6090
- for (const key of keys) {
6091
- await workOnSeekRequest(getWorkOnSeekRequestOptions(state));
6092
- if (key === "structure") {
6093
- if (hasInfo.structure && !emittedFields.structure) {
6094
- await callbackFunctions.onStructure?.(state.structure.getStructure());
6095
- if (fieldsInReturnValue.structure) {
6096
- returnValue.structure = state.structure.getStructure();
6097
- }
6098
- emittedFields.structure = true;
6099
- }
6100
- continue;
5976
+ var readSps = (iterator) => {
5977
+ const profile = iterator.getUint8();
5978
+ const compatibility = iterator.getUint8();
5979
+ const level = iterator.getUint8();
5980
+ iterator.startReadingBits();
5981
+ const seq_parameter_set_id = iterator.readExpGolomb();
5982
+ let separate_colour_plane_flag = null;
5983
+ let bit_depth_luma_minus8 = null;
5984
+ let bit_depth_chroma_minus8 = null;
5985
+ let qpprime_y_zero_transform_bypass_flag = null;
5986
+ let log2_max_frame_num_minus4 = null;
5987
+ let log2_max_pic_order_cnt_lsb_minus4 = null;
5988
+ let max_num_ref_frames = null;
5989
+ let gaps_in_frame_num_value_allowed_flag = null;
5990
+ let mb_adaptive_frame_field_flag = null;
5991
+ let direct_8x8_inference_flag = null;
5992
+ let frame_crop_left_offset = null;
5993
+ let frame_crop_right_offset = null;
5994
+ let frame_crop_top_offset = null;
5995
+ let frame_crop_bottom_offset = null;
5996
+ let vui_parameters = null;
5997
+ if (profile === 100 || profile === 110 || profile === 122 || profile === 244 || profile === 44 || profile === 83 || profile === 86 || profile === 118 || profile === 128 || profile === 138 || profile === 139 || profile === 134 || profile === 135) {
5998
+ const chromaFormat = iterator.readExpGolomb();
5999
+ if (chromaFormat === 3) {
6000
+ separate_colour_plane_flag = iterator.getBits(1);
6101
6001
  }
6102
- if (key === "durationInSeconds") {
6103
- if (hasInfo.durationInSeconds) {
6104
- if (!emittedFields.durationInSeconds) {
6105
- const durationInSeconds = getDuration(state);
6106
- await callbackFunctions.onDurationInSeconds?.(durationInSeconds);
6107
- if (fieldsInReturnValue.durationInSeconds) {
6108
- returnValue.durationInSeconds = durationInSeconds;
6002
+ bit_depth_luma_minus8 = iterator.readExpGolomb();
6003
+ bit_depth_chroma_minus8 = iterator.readExpGolomb();
6004
+ qpprime_y_zero_transform_bypass_flag = iterator.getBits(1);
6005
+ const seq_scaling_matrix_present_flag = iterator.getBits(1);
6006
+ const seq_scaling_list_present_flag = [];
6007
+ if (seq_scaling_matrix_present_flag) {
6008
+ for (let i = 0;i < (chromaFormat !== 3 ? 8 : 12); i++) {
6009
+ seq_scaling_list_present_flag[i] = iterator.getBits(1);
6010
+ if (seq_scaling_list_present_flag[i]) {
6011
+ if (i < 6) {
6012
+ throw new Error("Not implemented");
6013
+ } else {
6014
+ throw new Error("Not implemented");
6109
6015
  }
6110
- emittedFields.durationInSeconds = true;
6111
6016
  }
6112
6017
  }
6113
- continue;
6114
6018
  }
6115
- if (key === "slowDurationInSeconds") {
6116
- if (hasInfo.slowDurationInSeconds && !emittedFields.slowDurationInSeconds) {
6117
- const slowDurationInSeconds = getDuration(state) ?? state.slowDurationAndFps.getSlowDurationInSeconds();
6118
- await callbackFunctions.onSlowDurationInSeconds?.(slowDurationInSeconds);
6119
- if (fieldsInReturnValue.slowDurationInSeconds) {
6120
- returnValue.slowDurationInSeconds = slowDurationInSeconds;
6121
- }
6122
- emittedFields.slowDurationInSeconds = true;
6123
- }
6019
+ }
6020
+ log2_max_frame_num_minus4 = iterator.readExpGolomb();
6021
+ const pic_order_cnt_type = iterator.readExpGolomb();
6022
+ if (pic_order_cnt_type === 0) {
6023
+ log2_max_pic_order_cnt_lsb_minus4 = iterator.readExpGolomb();
6024
+ } else if (pic_order_cnt_type === 1) {
6025
+ throw new Error("pic_order_cnt_type = 1 not implemented");
6026
+ }
6027
+ max_num_ref_frames = iterator.readExpGolomb();
6028
+ gaps_in_frame_num_value_allowed_flag = iterator.getBits(1);
6029
+ const pic_width_in_mbs_minus1 = iterator.readExpGolomb();
6030
+ const pic_height_in_map_units_minus1 = iterator.readExpGolomb();
6031
+ const frame_mbs_only_flag = iterator.getBits(1);
6032
+ if (!frame_mbs_only_flag) {
6033
+ mb_adaptive_frame_field_flag = iterator.getBits(1);
6034
+ }
6035
+ direct_8x8_inference_flag = iterator.getBits(1);
6036
+ const frame_cropping_flag = iterator.getBits(1);
6037
+ if (frame_cropping_flag) {
6038
+ frame_crop_left_offset = iterator.readExpGolomb();
6039
+ frame_crop_right_offset = iterator.readExpGolomb();
6040
+ frame_crop_top_offset = iterator.readExpGolomb();
6041
+ frame_crop_bottom_offset = iterator.readExpGolomb();
6042
+ }
6043
+ const vui_parameters_present_flag = iterator.getBits(1);
6044
+ if (vui_parameters_present_flag) {
6045
+ vui_parameters = readVuiParameters(iterator);
6046
+ }
6047
+ iterator.stopReadingBits();
6048
+ return {
6049
+ profile,
6050
+ compatibility,
6051
+ level,
6052
+ bit_depth_chroma_minus8,
6053
+ bit_depth_luma_minus8,
6054
+ gaps_in_frame_num_value_allowed_flag,
6055
+ log2_max_frame_num_minus4,
6056
+ log2_max_pic_order_cnt_lsb_minus4,
6057
+ max_num_ref_frames,
6058
+ pic_height_in_map_units_minus1,
6059
+ pic_width_in_mbs_minus1,
6060
+ qpprime_y_zero_transform_bypass_flag,
6061
+ separate_colour_plane_flag,
6062
+ seq_parameter_set_id,
6063
+ direct_8x8_inference_flag,
6064
+ frame_crop_bottom_offset,
6065
+ frame_crop_left_offset,
6066
+ frame_crop_right_offset,
6067
+ frame_crop_top_offset,
6068
+ mb_adaptive_frame_field_flag,
6069
+ vui_parameters
6070
+ };
6071
+ };
6072
+ var findEnd = (buffer) => {
6073
+ let zeroesInARow = 0;
6074
+ for (let i = 0;i < buffer.length; i++) {
6075
+ const val = buffer[i];
6076
+ if (val === 0) {
6077
+ zeroesInARow++;
6124
6078
  continue;
6125
6079
  }
6126
- if (key === "fps") {
6127
- if (hasInfo.fps) {
6128
- if (!emittedFields.fps) {
6129
- const fps = getFps(state);
6080
+ if (zeroesInARow >= 2 && val === 1) {
6081
+ return i - zeroesInARow;
6082
+ }
6083
+ zeroesInARow = 0;
6084
+ }
6085
+ return null;
6086
+ };
6087
+ var inspect = (buffer) => {
6088
+ const iterator = getArrayBufferIterator(buffer, buffer.byteLength);
6089
+ iterator.startReadingBits();
6090
+ iterator.getBits(1);
6091
+ iterator.getBits(2);
6092
+ const type = iterator.getBits(5);
6093
+ iterator.stopReadingBits();
6094
+ if (type === 7) {
6095
+ const end = findEnd(buffer);
6096
+ const data = readSps(iterator);
6097
+ const sps = buffer.slice(0, end === null ? Infinity : end);
6098
+ return {
6099
+ spsData: data,
6100
+ sps,
6101
+ type: "avc-profile"
6102
+ };
6103
+ }
6104
+ if (type === 5) {
6105
+ return {
6106
+ type: "keyframe"
6107
+ };
6108
+ }
6109
+ if (type === 8) {
6110
+ const end = findEnd(buffer);
6111
+ const pps = buffer.slice(0, end === null ? Infinity : end);
6112
+ return {
6113
+ type: "avc-pps",
6114
+ pps
6115
+ };
6116
+ }
6117
+ if (type === 1) {
6118
+ return {
6119
+ type: "delta-frame"
6120
+ };
6121
+ }
6122
+ iterator.destroy();
6123
+ return null;
6124
+ };
6125
+ var parseAvc = (buffer) => {
6126
+ let zeroesInARow = 0;
6127
+ const infos = [];
6128
+ for (let i = 0;i < buffer.length; i++) {
6129
+ const val = buffer[i];
6130
+ if (val === 0) {
6131
+ zeroesInARow++;
6132
+ continue;
6133
+ }
6134
+ if (zeroesInARow >= 2 && val === 1) {
6135
+ zeroesInARow = 0;
6136
+ const info = inspect(buffer.slice(i + 1, i + 100));
6137
+ if (info) {
6138
+ infos.push(info);
6139
+ if (info.type === "keyframe" || info.type === "delta-frame") {
6140
+ break;
6141
+ }
6142
+ }
6143
+ }
6144
+ if (val !== 1) {
6145
+ zeroesInARow = 0;
6146
+ }
6147
+ }
6148
+ return infos;
6149
+ };
6150
+
6151
+ // src/containers/avc/sps-and-pps.ts
6152
+ var getSpsAndPps = (infos) => {
6153
+ const avcProfile = infos.find((i) => i.type === "avc-profile");
6154
+ const ppsProfile = infos.find((i) => i.type === "avc-pps");
6155
+ if (!avcProfile || !ppsProfile) {
6156
+ throw new Error("Expected avcProfile and ppsProfile");
6157
+ }
6158
+ return { pps: ppsProfile, sps: avcProfile };
6159
+ };
6160
+
6161
+ // src/containers/transport-stream/handle-avc-packet.ts
6162
+ var MPEG_TIMESCALE = 90000;
6163
+ var handleAvcPacket = async ({
6164
+ streamBuffer,
6165
+ programId,
6166
+ offset,
6167
+ sampleCallbacks,
6168
+ logLevel,
6169
+ onVideoTrack,
6170
+ transportStream,
6171
+ makeSamplesStartAtZero
6172
+ }) => {
6173
+ const avc = parseAvc(streamBuffer.getBuffer());
6174
+ const isTrackRegistered = sampleCallbacks.tracks.getTracks().find((t) => {
6175
+ return t.trackId === programId;
6176
+ });
6177
+ if (!isTrackRegistered) {
6178
+ const spsAndPps = getSpsAndPps(avc);
6179
+ const dimensions = getDimensionsFromSps(spsAndPps.sps.spsData);
6180
+ const sampleAspectRatio = getSampleAspectRatioFromSps(spsAndPps.sps.spsData);
6181
+ const startOffset = makeSamplesStartAtZero ? Math.min(streamBuffer.pesHeader.pts, streamBuffer.pesHeader.dts ?? Infinity) : 0;
6182
+ transportStream.startOffset.setOffset(programId, startOffset);
6183
+ const track = {
6184
+ m3uStreamFormat: null,
6185
+ rotation: 0,
6186
+ trackId: programId,
6187
+ type: "video",
6188
+ timescale: MPEG_TIMESCALE,
6189
+ codec: getCodecStringFromSpsAndPps(spsAndPps.sps),
6190
+ codecPrivate: createSpsPpsData(spsAndPps),
6191
+ fps: null,
6192
+ codedWidth: dimensions.width,
6193
+ codedHeight: dimensions.height,
6194
+ height: dimensions.height,
6195
+ width: dimensions.width,
6196
+ displayAspectWidth: dimensions.width,
6197
+ displayAspectHeight: dimensions.height,
6198
+ trakBox: null,
6199
+ codecWithoutConfig: "h264",
6200
+ description: undefined,
6201
+ sampleAspectRatio: {
6202
+ denominator: sampleAspectRatio.height,
6203
+ numerator: sampleAspectRatio.width
6204
+ },
6205
+ color: getVideoColorFromSps(spsAndPps.sps.spsData)
6206
+ };
6207
+ await registerVideoTrack({
6208
+ track,
6209
+ container: "transport-stream",
6210
+ logLevel,
6211
+ onVideoTrack,
6212
+ registerVideoSampleCallback: sampleCallbacks.registerVideoSampleCallback,
6213
+ tracks: sampleCallbacks.tracks
6214
+ });
6215
+ }
6216
+ const type = getKeyFrameOrDeltaFromAvcInfo(avc);
6217
+ const sample = {
6218
+ cts: streamBuffer.pesHeader.pts - transportStream.startOffset.getOffset(programId),
6219
+ dts: (streamBuffer.pesHeader.dts ?? streamBuffer.pesHeader.pts) - transportStream.startOffset.getOffset(programId),
6220
+ timestamp: streamBuffer.pesHeader.pts - transportStream.startOffset.getOffset(programId),
6221
+ duration: undefined,
6222
+ data: streamBuffer.getBuffer(),
6223
+ trackId: programId,
6224
+ type,
6225
+ offset,
6226
+ timescale: MPEG_TIMESCALE
6227
+ };
6228
+ if (type === "key") {
6229
+ transportStream.observedPesHeaders.markPtsAsKeyframe(streamBuffer.pesHeader.pts);
6230
+ }
6231
+ await emitVideoSample({
6232
+ trackId: programId,
6233
+ videoSample: convertAudioOrVideoSampleToWebCodecsTimestamps({
6234
+ sample,
6235
+ timescale: MPEG_TIMESCALE
6236
+ }),
6237
+ callbacks: sampleCallbacks
6238
+ });
6239
+ transportStream.lastEmittedSample.setLastEmittedSample(sample);
6240
+ };
6241
+
6242
+ // src/state/transport-stream/observed-pes-header.ts
6243
+ var makeObservedPesHeader = () => {
6244
+ const pesHeaders = [];
6245
+ const confirmedAsKeyframe = [];
6246
+ const state = {
6247
+ pesHeaders,
6248
+ addPesHeader: (pesHeader) => {
6249
+ if (pesHeaders.find((p) => p.offset === pesHeader.offset)) {
6250
+ return;
6251
+ }
6252
+ pesHeaders.push(pesHeader);
6253
+ },
6254
+ markPtsAsKeyframe: (pts) => {
6255
+ confirmedAsKeyframe.push(pts);
6256
+ },
6257
+ getPesKeyframeHeaders: () => {
6258
+ return pesHeaders.filter((p) => confirmedAsKeyframe.includes(p.pts));
6259
+ }
6260
+ };
6261
+ return state;
6262
+ };
6263
+ var getLastKeyFrameBeforeTimeInSeconds = ({
6264
+ observedPesHeaders,
6265
+ timeInSeconds,
6266
+ ptsStartOffset
6267
+ }) => {
6268
+ return observedPesHeaders.findLast((k) => (k.pts - ptsStartOffset) / MPEG_TIMESCALE <= timeInSeconds);
6269
+ };
6270
+
6271
+ // src/get-seeking-byte.ts
6272
+ var getSeekingByte = ({
6273
+ info,
6274
+ time,
6275
+ logLevel,
6276
+ currentPosition,
6277
+ isoState,
6278
+ transportStream,
6279
+ webmState,
6280
+ mediaSection,
6281
+ keyframes
6282
+ }) => {
6283
+ if (info.type === "iso-base-media-seeking-info") {
6284
+ return getSeekingByteFromIsoBaseMedia({
6285
+ info,
6286
+ time,
6287
+ logLevel,
6288
+ currentPosition,
6289
+ isoState
6290
+ });
6291
+ }
6292
+ if (info.type === "wav-seeking-info") {
6293
+ return getSeekingByteFromWav({
6294
+ info,
6295
+ time
6296
+ });
6297
+ }
6298
+ if (info.type === "webm-seeking-info") {
6299
+ return getSeekingByteFromMatroska({
6300
+ info,
6301
+ time,
6302
+ webmState,
6303
+ logLevel,
6304
+ mediaSection,
6305
+ keyframes
6306
+ });
6307
+ }
6308
+ if (info.type === "transport-stream-seeking-info") {
6309
+ const lastKeyframeBeforeTimeInSeconds = getLastKeyFrameBeforeTimeInSeconds({
6310
+ observedPesHeaders: info.observedPesHeaders,
6311
+ timeInSeconds: time,
6312
+ ptsStartOffset: info.ptsStartOffset
6313
+ });
6314
+ const byte = lastKeyframeBeforeTimeInSeconds?.offset ?? 0;
6315
+ transportStream.resetBeforeSeek();
6316
+ return Promise.resolve({
6317
+ type: "do-seek",
6318
+ byte
6319
+ });
6320
+ }
6321
+ throw new Error(`Unknown seeking info type: ${info}`);
6322
+ };
6323
+
6324
+ // src/containers/iso-base-media/get-seeking-info-from-mp4.ts
6325
+ var getSeekingInfoFromMp4 = ({
6326
+ structureState,
6327
+ isoState,
6328
+ mp4HeaderSegment,
6329
+ mediaSectionState: mediaSectionState2
6330
+ }) => {
6331
+ const structure = structureState.getIsoStructure();
6332
+ const moovAtom = getMoovBoxFromState({
6333
+ isoState,
6334
+ mp4HeaderSegment,
6335
+ structureState
6336
+ });
6337
+ const moofBoxes = getMoofBoxes(structure.boxes);
6338
+ const tfraBoxes = getTfraBoxes(structure);
6339
+ if (!moovAtom) {
6340
+ return null;
6341
+ }
6342
+ return {
6343
+ type: "iso-base-media-seeking-info",
6344
+ moovBox: moovAtom,
6345
+ moofBoxes,
6346
+ tfraBoxes,
6347
+ mediaSections: mediaSectionState2.getMediaSections()
6348
+ };
6349
+ };
6350
+
6351
+ // src/containers/transport-stream/get-seeking-info.ts
6352
+ var getSeekingInfoFromTransportStream = (transportStream, tracksState) => {
6353
+ const firstVideoTrack = tracksState.getTracks().find((t) => t.type === "video");
6354
+ if (!firstVideoTrack) {
6355
+ throw new Error("No video track found");
6356
+ }
6357
+ return {
6358
+ type: "transport-stream-seeking-info",
6359
+ observedPesHeaders: transportStream.observedPesHeaders.getPesKeyframeHeaders(),
6360
+ ptsStartOffset: transportStream.startOffset.getOffset(firstVideoTrack.trackId)
6361
+ };
6362
+ };
6363
+
6364
+ // src/containers/wav/get-seeking-info.ts
6365
+ var getSeekingInfoFromWav = ({
6366
+ structure,
6367
+ mediaSectionState: mediaSectionState2
6368
+ }) => {
6369
+ const fmtBox = structure.boxes.find((box) => box.type === "wav-fmt");
6370
+ if (!fmtBox) {
6371
+ return null;
6372
+ }
6373
+ const mediaSection = mediaSectionState2.getMediaSections();
6374
+ if (mediaSection.length !== 1) {
6375
+ return null;
6376
+ }
6377
+ return {
6378
+ type: "wav-seeking-info",
6379
+ sampleRate: fmtBox.sampleRate,
6380
+ blockAlign: fmtBox.blockAlign,
6381
+ mediaSections: mediaSection[0]
6382
+ };
6383
+ };
6384
+
6385
+ // src/containers/webm/seek/get-seeking-info.ts
6386
+ var getSeekingInfoFromMatroska = (tracksState) => {
6387
+ const tracks2 = tracksState.getTracks();
6388
+ const firstVideoTrack = tracks2.find((track) => track.type === "video");
6389
+ return {
6390
+ type: "webm-seeking-info",
6391
+ track: firstVideoTrack ? {
6392
+ timescale: firstVideoTrack.timescale,
6393
+ trackId: firstVideoTrack.trackId
6394
+ } : null
6395
+ };
6396
+ };
6397
+
6398
+ // src/get-seeking-info.ts
6399
+ var getSeekingInfo = ({
6400
+ structureState,
6401
+ mp4HeaderSegment,
6402
+ mediaSectionState: mediaSectionState2,
6403
+ isoState,
6404
+ transportStream,
6405
+ tracksState
6406
+ }) => {
6407
+ const structure = structureState.getStructureOrNull();
6408
+ if (!structure) {
6409
+ return null;
6410
+ }
6411
+ if (structure.type === "iso-base-media") {
6412
+ return getSeekingInfoFromMp4({
6413
+ structureState,
6414
+ isoState,
6415
+ mp4HeaderSegment,
6416
+ mediaSectionState: mediaSectionState2
6417
+ });
6418
+ }
6419
+ if (structure.type === "wav") {
6420
+ return getSeekingInfoFromWav({
6421
+ structure,
6422
+ mediaSectionState: mediaSectionState2
6423
+ });
6424
+ }
6425
+ if (structure.type === "matroska") {
6426
+ return getSeekingInfoFromMatroska(tracksState);
6427
+ }
6428
+ if (structure.type === "transport-stream") {
6429
+ return getSeekingInfoFromTransportStream(transportStream, tracksState);
6430
+ }
6431
+ throw new Error(`Seeking is not supported for this format: ${structure.type}`);
6432
+ };
6433
+
6434
+ // src/seek-backwards.ts
6435
+ var seekBackwards = async ({
6436
+ iterator,
6437
+ seekTo,
6438
+ readerInterface,
6439
+ src,
6440
+ controller,
6441
+ logLevel,
6442
+ currentReader
6443
+ }) => {
6444
+ const howManyBytesWeCanGoBack = iterator.counter.getDiscardedOffset();
6445
+ if (iterator.counter.getOffset() - howManyBytesWeCanGoBack <= seekTo) {
6446
+ iterator.skipTo(seekTo);
6447
+ return;
6448
+ }
6449
+ const time = Date.now();
6450
+ Log.verbose(logLevel, `Seeking in video from position ${iterator.counter.getOffset()} -> ${seekTo}. Re-reading because this portion is not available.`);
6451
+ const { reader: newReader } = await readerInterface.read({
6452
+ src,
6453
+ range: seekTo,
6454
+ controller
6455
+ });
6456
+ iterator.replaceData(new Uint8Array([]), seekTo);
6457
+ Log.verbose(logLevel, `Re-reading took ${Date.now() - time}ms. New position: ${iterator.counter.getOffset()}`);
6458
+ currentReader.setCurrent(newReader);
6459
+ };
6460
+
6461
+ // src/state/need-samples-for-fields.ts
6462
+ var fieldsNeedSamplesMap = {
6463
+ slowDurationInSeconds: true,
6464
+ slowFps: true,
6465
+ slowKeyframes: true,
6466
+ slowNumberOfFrames: true,
6467
+ audioCodec: false,
6468
+ container: false,
6469
+ dimensions: false,
6470
+ durationInSeconds: false,
6471
+ fps: false,
6472
+ internalStats: false,
6473
+ isHdr: false,
6474
+ name: false,
6475
+ rotation: false,
6476
+ size: false,
6477
+ structure: false,
6478
+ tracks: false,
6479
+ unrotatedDimensions: false,
6480
+ videoCodec: false,
6481
+ metadata: false,
6482
+ location: false,
6483
+ mimeType: false,
6484
+ keyframes: false,
6485
+ images: false,
6486
+ numberOfAudioChannels: false,
6487
+ sampleRate: false,
6488
+ slowAudioBitrate: true,
6489
+ slowVideoBitrate: true,
6490
+ m3uStreams: false
6491
+ };
6492
+ var needsToIterateOverSamples = ({
6493
+ fields,
6494
+ emittedFields
6495
+ }) => {
6496
+ const keys = Object.keys(fields ?? {});
6497
+ const selectedKeys = keys.filter((k) => fields[k]);
6498
+ return selectedKeys.some((k) => fieldsNeedSamplesMap[k] && !emittedFields[k]);
6499
+ };
6500
+
6501
+ // src/disallow-forward-seek-if-samples-are-needed.ts
6502
+ var disallowForwardSeekIfSamplesAreNeeded = ({
6503
+ seekTo,
6504
+ previousPosition,
6505
+ fields
6506
+ }) => {
6507
+ const fieldsNeedingSamples = Object.entries(fields).filter(([, value]) => value).map(([key]) => key).filter((key) => fieldsNeedSamplesMap[key]);
6508
+ if (fieldsNeedingSamples.length > 0) {
6509
+ throw new Error(`Forward seeking is not allowed when the following fields are requested from parseMedia(): ${fieldsNeedingSamples.join(", ")}. Seek was from 0x${previousPosition.toString(16)} to 0x${seekTo.toString(16)}. Either don't seek forward, or don't request these fields.`);
6510
+ }
6511
+ };
6512
+
6513
+ // src/seek-forwards.ts
6514
+ var seekForward = async ({
6515
+ seekTo,
6516
+ userInitiated,
6517
+ iterator,
6518
+ fields,
6519
+ logLevel,
6520
+ currentReader,
6521
+ readerInterface,
6522
+ src,
6523
+ controller,
6524
+ discardReadBytes
6525
+ }) => {
6526
+ if (userInitiated) {
6527
+ disallowForwardSeekIfSamplesAreNeeded({
6528
+ fields,
6529
+ seekTo,
6530
+ previousPosition: iterator.counter.getOffset()
6531
+ });
6532
+ }
6533
+ const alreadyHasBuffer = iterator.bytesRemaining() >= seekTo - iterator.counter.getOffset();
6534
+ Log.verbose(logLevel, `Performing seek from ${iterator.counter.getOffset()} to ${seekTo}`);
6535
+ if (alreadyHasBuffer) {
6536
+ iterator.skipTo(seekTo);
6537
+ Log.verbose(logLevel, `Already read ahead enough, skipping forward`);
6538
+ return;
6539
+ }
6540
+ const time = Date.now();
6541
+ Log.verbose(logLevel, `Skipping over video data from position ${iterator.counter.getOffset()} -> ${seekTo}. Re-reading because this portion is not available`);
6542
+ const { reader: newReader } = await readerInterface.read({
6543
+ src,
6544
+ range: seekTo,
6545
+ controller
6546
+ });
6547
+ iterator.skipTo(seekTo);
6548
+ await discardReadBytes(true);
6549
+ Log.verbose(logLevel, `Re-reading took ${Date.now() - time}ms. New position: ${iterator.counter.getOffset()}`);
6550
+ currentReader.setCurrent(newReader);
6551
+ };
6552
+
6553
+ // src/perform-seek.ts
6554
+ var performSeek = async ({
6555
+ seekTo,
6556
+ userInitiated,
6557
+ controller,
6558
+ mediaSection,
6559
+ iterator,
6560
+ seekInfiniteLoop,
6561
+ logLevel,
6562
+ mode,
6563
+ contentLength,
6564
+ currentReader,
6565
+ readerInterface,
6566
+ src,
6567
+ discardReadBytes,
6568
+ fields
6569
+ }) => {
6570
+ const byteInMediaSection = isByteInMediaSection({
6571
+ position: seekTo,
6572
+ mediaSections: mediaSection.getMediaSections()
6573
+ });
6574
+ if (byteInMediaSection !== "in-section" && userInitiated) {
6575
+ const sections = mediaSection.getMediaSections();
6576
+ const sectionStrings = sections.map((section) => {
6577
+ return `start: ${section.start}, end: ${section.size + section.start}`;
6578
+ });
6579
+ throw new Error(`Cannot seek to a byte that is not in the video section. Seeking to: ${seekTo}, sections: ${sectionStrings.join(" | ")}`);
6580
+ }
6581
+ seekInfiniteLoop.registerSeek(seekTo);
6582
+ if (seekTo <= iterator.counter.getOffset() && mode === "download") {
6583
+ throw new Error(`Seeking backwards is not supported in parseAndDownloadMedia() mode. Current position: ${iterator.counter.getOffset()}, seekTo: ${seekTo}`);
6584
+ }
6585
+ if (seekTo > contentLength) {
6586
+ throw new Error(`Cannot seek beyond the end of the file: ${seekTo} > ${contentLength}`);
6587
+ }
6588
+ if (mode === "download") {
6589
+ Log.verbose(logLevel, `Skipping over video data from position ${iterator.counter.getOffset()} -> ${seekTo}. Fetching but not reading all the data inbetween because in download mode`);
6590
+ iterator.discard(seekTo - iterator.counter.getOffset());
6591
+ return;
6592
+ }
6593
+ await controller._internals.checkForAbortAndPause();
6594
+ const alreadyAtByte = iterator.counter.getOffset() === seekTo;
6595
+ if (alreadyAtByte) {
6596
+ Log.verbose(logLevel, `Already at the desired position, seeking done`);
6597
+ controller._internals.performedSeeksSignal.markLastSeekAsUserInitiated();
6598
+ return;
6599
+ }
6600
+ const skippingForward = seekTo > iterator.counter.getOffset();
6601
+ controller._internals.performedSeeksSignal.recordSeek({
6602
+ from: iterator.counter.getOffset(),
6603
+ to: seekTo,
6604
+ type: userInitiated ? "user-initiated" : "internal"
6605
+ });
6606
+ if (skippingForward) {
6607
+ await seekForward({
6608
+ seekTo,
6609
+ userInitiated,
6610
+ iterator,
6611
+ fields,
6612
+ logLevel,
6613
+ currentReader,
6614
+ readerInterface,
6615
+ src,
6616
+ controller,
6617
+ discardReadBytes
6618
+ });
6619
+ } else {
6620
+ await seekBackwards({
6621
+ controller,
6622
+ seekTo,
6623
+ iterator,
6624
+ logLevel,
6625
+ currentReader,
6626
+ readerInterface,
6627
+ src
6628
+ });
6629
+ }
6630
+ await controller._internals.checkForAbortAndPause();
6631
+ };
6632
+
6633
+ // src/work-on-seek-request.ts
6634
+ var turnSeekIntoByte = async ({
6635
+ seek: seek2,
6636
+ mediaSectionState: mediaSectionState2,
6637
+ logLevel,
6638
+ iterator,
6639
+ structureState,
6640
+ mp4HeaderSegment,
6641
+ isoState,
6642
+ transportStream,
6643
+ tracksState,
6644
+ webmState,
6645
+ keyframes
6646
+ }) => {
6647
+ const mediaSections = mediaSectionState2.getMediaSections();
6648
+ if (mediaSections.length === 0) {
6649
+ Log.trace(logLevel, "No media sections defined, cannot seek yet");
6650
+ return {
6651
+ type: "valid-but-must-wait"
6652
+ };
6653
+ }
6654
+ if (seek2.type === "keyframe-before-time") {
6655
+ if (seek2.timeInSeconds < 0) {
6656
+ throw new Error(`Cannot seek to a negative time: ${JSON.stringify(seek2)}`);
6657
+ }
6658
+ const seekingInfo = getSeekingInfo({
6659
+ structureState,
6660
+ mp4HeaderSegment,
6661
+ mediaSectionState: mediaSectionState2,
6662
+ isoState,
6663
+ transportStream,
6664
+ tracksState
6665
+ });
6666
+ if (!seekingInfo) {
6667
+ Log.trace(logLevel, "No seeking info, cannot seek yet");
6668
+ return {
6669
+ type: "valid-but-must-wait"
6670
+ };
6671
+ }
6672
+ const seekingByte = await getSeekingByte({
6673
+ info: seekingInfo,
6674
+ time: seek2.timeInSeconds,
6675
+ logLevel,
6676
+ currentPosition: iterator.counter.getOffset(),
6677
+ isoState,
6678
+ transportStream,
6679
+ webmState,
6680
+ mediaSection: mediaSectionState2,
6681
+ keyframes
6682
+ });
6683
+ return seekingByte;
6684
+ }
6685
+ if (seek2.type === "byte") {
6686
+ return {
6687
+ type: "do-seek",
6688
+ byte: seek2.byte
6689
+ };
6690
+ }
6691
+ throw new Error(`Cannot process seek request for ${seek2}: ${JSON.stringify(seek2)}`);
6692
+ };
6693
+ var getWorkOnSeekRequestOptions = (state) => {
6694
+ return {
6695
+ logLevel: state.logLevel,
6696
+ controller: state.controller,
6697
+ isoState: state.iso,
6698
+ iterator: state.iterator,
6699
+ structureState: state.structure,
6700
+ src: state.src,
6701
+ contentLength: state.contentLength,
6702
+ readerInterface: state.readerInterface,
6703
+ mediaSection: state.mediaSection,
6704
+ mp4HeaderSegment: state.mp4HeaderSegment,
6705
+ mode: state.mode,
6706
+ seekInfiniteLoop: state.seekInfiniteLoop,
6707
+ currentReader: state.currentReader,
6708
+ discardReadBytes: state.discardReadBytes,
6709
+ fields: state.fields,
6710
+ transportStream: state.transportStream,
6711
+ tracksState: state.callbacks.tracks,
6712
+ webmState: state.webm,
6713
+ keyframes: state.keyframes
6714
+ };
6715
+ };
6716
+ var workOnSeekRequest = async (options) => {
6717
+ const {
6718
+ logLevel,
6719
+ controller,
6720
+ mediaSection,
6721
+ mp4HeaderSegment,
6722
+ isoState,
6723
+ iterator,
6724
+ structureState,
6725
+ src,
6726
+ contentLength,
6727
+ readerInterface,
6728
+ mode,
6729
+ seekInfiniteLoop,
6730
+ currentReader,
6731
+ discardReadBytes,
6732
+ fields,
6733
+ transportStream,
6734
+ tracksState,
6735
+ webmState,
6736
+ keyframes
6737
+ } = options;
6738
+ const seek2 = controller._internals.seekSignal.getSeek();
6739
+ if (!seek2) {
6740
+ return;
6741
+ }
6742
+ Log.trace(logLevel, `Has seek request: ${JSON.stringify(seek2)}`);
6743
+ const resolution = await turnSeekIntoByte({
6744
+ seek: seek2,
6745
+ mediaSectionState: mediaSection,
6746
+ logLevel,
6747
+ iterator,
6748
+ structureState,
6749
+ mp4HeaderSegment,
6750
+ isoState,
6751
+ transportStream,
6752
+ tracksState,
6753
+ webmState,
6754
+ keyframes
6755
+ });
6756
+ Log.trace(logLevel, `Seek action: ${JSON.stringify(resolution)}`);
6757
+ if (resolution.type === "intermediary-seek") {
6758
+ await performSeek({
6759
+ seekTo: resolution.byte,
6760
+ userInitiated: false,
6761
+ controller,
6762
+ mediaSection,
6763
+ iterator,
6764
+ logLevel,
6765
+ mode,
6766
+ contentLength,
6767
+ seekInfiniteLoop,
6768
+ currentReader,
6769
+ readerInterface,
6770
+ src,
6771
+ discardReadBytes,
6772
+ fields
6773
+ });
6774
+ return;
6775
+ }
6776
+ if (resolution.type === "do-seek") {
6777
+ await performSeek({
6778
+ seekTo: resolution.byte,
6779
+ userInitiated: true,
6780
+ controller,
6781
+ mediaSection,
6782
+ iterator,
6783
+ logLevel,
6784
+ mode,
6785
+ contentLength,
6786
+ seekInfiniteLoop,
6787
+ currentReader,
6788
+ readerInterface,
6789
+ src,
6790
+ discardReadBytes,
6791
+ fields
6792
+ });
6793
+ const { hasChanged } = controller._internals.seekSignal.clearSeekIfStillSame(seek2);
6794
+ if (hasChanged) {
6795
+ Log.trace(logLevel, `Seek request has changed while seeking, seeking again`);
6796
+ await workOnSeekRequest(options);
6797
+ }
6798
+ return;
6799
+ }
6800
+ if (resolution.type === "invalid") {
6801
+ throw new Error(`The seek request ${JSON.stringify(seek2)} cannot be processed`);
6802
+ }
6803
+ if (resolution.type === "valid-but-must-wait") {
6804
+ Log.trace(logLevel, "Seek request is valid but cannot be processed yet");
6805
+ }
6806
+ };
6807
+
6808
+ // src/emit-available-info.ts
6809
+ var emitAvailableInfo = async ({
6810
+ hasInfo,
6811
+ state
6812
+ }) => {
6813
+ const keys = Object.keys(hasInfo);
6814
+ const {
6815
+ emittedFields,
6816
+ fieldsInReturnValue,
6817
+ returnValue,
6818
+ name,
6819
+ callbackFunctions
6820
+ } = state;
6821
+ for (const key of keys) {
6822
+ await workOnSeekRequest(getWorkOnSeekRequestOptions(state));
6823
+ if (key === "structure") {
6824
+ if (hasInfo.structure && !emittedFields.structure) {
6825
+ await callbackFunctions.onStructure?.(state.structure.getStructure());
6826
+ if (fieldsInReturnValue.structure) {
6827
+ returnValue.structure = state.structure.getStructure();
6828
+ }
6829
+ emittedFields.structure = true;
6830
+ }
6831
+ continue;
6832
+ }
6833
+ if (key === "durationInSeconds") {
6834
+ if (hasInfo.durationInSeconds) {
6835
+ if (!emittedFields.durationInSeconds) {
6836
+ const durationInSeconds = getDuration(state);
6837
+ await callbackFunctions.onDurationInSeconds?.(durationInSeconds);
6838
+ if (fieldsInReturnValue.durationInSeconds) {
6839
+ returnValue.durationInSeconds = durationInSeconds;
6840
+ }
6841
+ emittedFields.durationInSeconds = true;
6842
+ }
6843
+ }
6844
+ continue;
6845
+ }
6846
+ if (key === "slowDurationInSeconds") {
6847
+ if (hasInfo.slowDurationInSeconds && !emittedFields.slowDurationInSeconds) {
6848
+ const slowDurationInSeconds = getDuration(state) ?? state.slowDurationAndFps.getSlowDurationInSeconds();
6849
+ await callbackFunctions.onSlowDurationInSeconds?.(slowDurationInSeconds);
6850
+ if (fieldsInReturnValue.slowDurationInSeconds) {
6851
+ returnValue.slowDurationInSeconds = slowDurationInSeconds;
6852
+ }
6853
+ emittedFields.slowDurationInSeconds = true;
6854
+ }
6855
+ continue;
6856
+ }
6857
+ if (key === "fps") {
6858
+ if (hasInfo.fps) {
6859
+ if (!emittedFields.fps) {
6860
+ const fps = getFps(state);
6130
6861
  await callbackFunctions.onFps?.(fps);
6131
6862
  if (fieldsInReturnValue.fps) {
6132
6863
  returnValue.fps = fps;
@@ -6544,175 +7275,44 @@ var triggerInfoEmit = async (state) => {
6544
7275
  });
6545
7276
  await emitAvailableInfo({
6546
7277
  hasInfo: availableInfo,
6547
- state
6548
- });
6549
- };
6550
-
6551
- // src/check-if-done.ts
6552
- var checkIfDone = async (state) => {
6553
- const startCheck = Date.now();
6554
- const hasAll = hasAllInfo({
6555
- state
6556
- });
6557
- state.timings.timeCheckingIfDone += Date.now() - startCheck;
6558
- if (hasAll && state.mode === "query") {
6559
- Log.verbose(state.logLevel, "Got all info, skipping to the end.");
6560
- state.increaseSkippedBytes(state.contentLength - state.iterator.counter.getOffset());
6561
- return true;
6562
- }
6563
- if (state.iterator.counter.getOffset() === state.contentLength) {
6564
- if (state.structure.getStructure().type === "m3u" && !state.m3u.getAllChunksProcessedOverall()) {
6565
- return false;
6566
- }
6567
- Log.verbose(state.logLevel, "Reached end of file");
6568
- await state.discardReadBytes(true);
6569
- return true;
6570
- }
6571
- if (state.iterator.counter.getOffset() + state.iterator.bytesRemaining() === state.contentLength && state.errored) {
6572
- Log.verbose(state.logLevel, "Reached end of file and errorred");
6573
- return true;
6574
- }
6575
- return false;
6576
- };
6577
-
6578
- // src/make-progress-object.ts
6579
- var makeProgressObject = (state) => {
6580
- return {
6581
- bytes: state.iterator.counter.getOffset(),
6582
- percentage: state.contentLength ? state.iterator.counter.getOffset() / state.contentLength : null,
6583
- totalBytes: state.contentLength
6584
- };
6585
- };
6586
-
6587
- // src/convert-audio-or-video-sample.ts
6588
- var TARGET_TIMESCALE = 1e6;
6589
- var fixFloat = (value) => {
6590
- if (value % 1 < 0.0000001) {
6591
- return Math.floor(value);
6592
- }
6593
- if (value % 1 > 0.9999999) {
6594
- return Math.ceil(value);
6595
- }
6596
- return value;
6597
- };
6598
- var convertAudioOrVideoSampleToWebCodecsTimestamps = ({
6599
- sample,
6600
- timescale
6601
- }) => {
6602
- if (timescale === TARGET_TIMESCALE) {
6603
- return sample;
6604
- }
6605
- const { cts, dts, timestamp } = sample;
6606
- return {
6607
- cts: fixFloat(cts * (TARGET_TIMESCALE / timescale)),
6608
- dts: fixFloat(dts * (TARGET_TIMESCALE / timescale)),
6609
- timestamp: fixFloat(timestamp * (TARGET_TIMESCALE / timescale)),
6610
- duration: sample.duration === undefined ? undefined : fixFloat(sample.duration * (TARGET_TIMESCALE / timescale)),
6611
- data: sample.data,
6612
- trackId: sample.trackId,
6613
- type: sample.type,
6614
- offset: sample.offset,
6615
- timescale: TARGET_TIMESCALE
6616
- };
6617
- };
6618
-
6619
- // src/emit-audio-sample.ts
6620
- var emitAudioSample = async ({
6621
- trackId,
6622
- audioSample,
6623
- workOnSeekRequestOptions,
6624
- callbacks
6625
- }) => {
6626
- await callbacks.onAudioSample(trackId, audioSample);
6627
- await workOnSeekRequest(workOnSeekRequestOptions);
6628
- };
6629
- var emitVideoSample = async ({
6630
- trackId,
6631
- videoSample,
6632
- workOnSeekRequestOptions,
6633
- callbacks
6634
- }) => {
6635
- await callbacks.onVideoSample(trackId, videoSample);
6636
- await workOnSeekRequest(workOnSeekRequestOptions);
6637
- };
6638
-
6639
- // src/register-track.ts
6640
- var registerVideoTrack = async ({
6641
- track,
6642
- container,
6643
- logLevel,
6644
- workOnSeekRequestOptions,
6645
- onVideoTrack,
6646
- registerVideoSampleCallback,
6647
- tracks: tracks2
6648
- }) => {
6649
- if (tracks2.getTracks().find((t) => t.trackId === track.trackId)) {
6650
- Log.trace(logLevel, `Track ${track.trackId} already registered, skipping`);
6651
- return null;
6652
- }
6653
- if (track.type !== "video") {
6654
- throw new Error("Expected video track");
6655
- }
6656
- tracks2.addTrack(track);
6657
- if (!onVideoTrack) {
6658
- return null;
6659
- }
6660
- const callback = await onVideoTrack({
6661
- track,
6662
- container
7278
+ state
6663
7279
  });
6664
- await registerVideoSampleCallback(track.trackId, callback ?? null);
6665
- if (workOnSeekRequestOptions) {
6666
- await workOnSeekRequest(workOnSeekRequestOptions);
6667
- }
6668
- return callback;
6669
7280
  };
6670
- var registerAudioTrack = async ({
6671
- workOnSeekRequestOptions,
6672
- track,
6673
- container,
6674
- tracks: tracks2,
6675
- logLevel,
6676
- onAudioTrack,
6677
- registerAudioSampleCallback
6678
- }) => {
6679
- if (tracks2.getTracks().find((t) => t.trackId === track.trackId)) {
6680
- Log.trace(logLevel, `Track ${track.trackId} already registered, skipping`);
6681
- return null;
6682
- }
6683
- if (track.type !== "audio") {
6684
- throw new Error("Expected audio track");
7281
+
7282
+ // src/check-if-done.ts
7283
+ var checkIfDone = async (state) => {
7284
+ const startCheck = Date.now();
7285
+ const hasAll = hasAllInfo({
7286
+ state
7287
+ });
7288
+ state.timings.timeCheckingIfDone += Date.now() - startCheck;
7289
+ if (hasAll && state.mode === "query") {
7290
+ Log.verbose(state.logLevel, "Got all info, skipping to the end.");
7291
+ state.increaseSkippedBytes(state.contentLength - state.iterator.counter.getOffset());
7292
+ return true;
6685
7293
  }
6686
- tracks2.addTrack(track);
6687
- if (!onAudioTrack) {
6688
- return null;
7294
+ if (state.iterator.counter.getOffset() === state.contentLength) {
7295
+ if (state.structure.getStructure().type === "m3u" && !state.m3u.getAllChunksProcessedOverall()) {
7296
+ return false;
7297
+ }
7298
+ Log.verbose(state.logLevel, "Reached end of file");
7299
+ await state.discardReadBytes(true);
7300
+ return true;
6689
7301
  }
6690
- const callback = await onAudioTrack({
6691
- track,
6692
- container
6693
- });
6694
- await registerAudioSampleCallback(track.trackId, callback ?? null);
6695
- if (workOnSeekRequestOptions) {
6696
- await workOnSeekRequest(workOnSeekRequestOptions);
7302
+ if (state.iterator.counter.getOffset() + state.iterator.bytesRemaining() === state.contentLength && state.errored) {
7303
+ Log.verbose(state.logLevel, "Reached end of file and errorred");
7304
+ return true;
6697
7305
  }
6698
- return callback;
7306
+ return false;
6699
7307
  };
6700
- var registerVideoTrackWhenProfileIsAvailable = ({
6701
- state,
6702
- track,
6703
- container
6704
- }) => {
6705
- state.riff.registerOnAvcProfileCallback(async (profile) => {
6706
- await registerVideoTrack({
6707
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
6708
- track: addAvcProfileToTrack(track, profile),
6709
- container,
6710
- logLevel: state.logLevel,
6711
- onVideoTrack: state.onVideoTrack,
6712
- registerVideoSampleCallback: state.callbacks.registerVideoSampleCallback,
6713
- tracks: state.callbacks.tracks
6714
- });
6715
- });
7308
+
7309
+ // src/make-progress-object.ts
7310
+ var makeProgressObject = (state) => {
7311
+ return {
7312
+ bytes: state.iterator.counter.getOffset(),
7313
+ percentage: state.contentLength ? state.iterator.counter.getOffset() / state.contentLength : null,
7314
+ totalBytes: state.contentLength
7315
+ };
6716
7316
  };
6717
7317
 
6718
7318
  // src/containers/aac/parse-aac.ts
@@ -6759,7 +7359,6 @@ var parseAac = async (state) => {
6759
7359
  const data = iterator.getSlice(frameLength);
6760
7360
  if (state.callbacks.tracks.getTracks().length === 0) {
6761
7361
  await registerAudioTrack({
6762
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
6763
7362
  container: "aac",
6764
7363
  track: {
6765
7364
  codec: mapAudioObjectTypeToCodecString(audioObjectType),
@@ -6799,7 +7398,6 @@ var parseAac = async (state) => {
6799
7398
  },
6800
7399
  timescale: 1
6801
7400
  }),
6802
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
6803
7401
  callbacks: state.callbacks
6804
7402
  });
6805
7403
  return Promise.resolve(null);
@@ -7007,7 +7605,6 @@ var emitSample = async ({
7007
7605
  },
7008
7606
  timescale: 1
7009
7607
  }),
7010
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
7011
7608
  callbacks: state.callbacks
7012
7609
  });
7013
7610
  iterator.destroy();
@@ -7157,7 +7754,6 @@ var parseStreamInfo = async ({
7157
7754
  state.structure.getFlacStructure().boxes.push(flacStreamInfo);
7158
7755
  await registerAudioTrack({
7159
7756
  container: "flac",
7160
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
7161
7757
  track: {
7162
7758
  codec: "flac",
7163
7759
  type: "audio",
@@ -9108,7 +9704,7 @@ var processBox = async ({
9108
9704
  if (!onlyIfMoovAtomExpected) {
9109
9705
  throw new Error("State is required");
9110
9706
  }
9111
- const { workOnSeekRequestOptions, tracks: tracks2, onAudioTrack, onVideoTrack } = onlyIfMoovAtomExpected;
9707
+ const { tracks: tracks2, onAudioTrack, onVideoTrack } = onlyIfMoovAtomExpected;
9112
9708
  const box = await parseTrak({
9113
9709
  size: boxSize,
9114
9710
  offsetAtStart: fileOffset,
@@ -9118,7 +9714,6 @@ var processBox = async ({
9118
9714
  const transformedTrack = makeBaseMediaTrack(box);
9119
9715
  if (transformedTrack && transformedTrack.type === "video") {
9120
9716
  await registerVideoTrack({
9121
- workOnSeekRequestOptions,
9122
9717
  track: transformedTrack,
9123
9718
  container: "mp4",
9124
9719
  logLevel,
@@ -9129,7 +9724,6 @@ var processBox = async ({
9129
9724
  }
9130
9725
  if (transformedTrack && transformedTrack.type === "audio") {
9131
9726
  await registerAudioTrack({
9132
- workOnSeekRequestOptions,
9133
9727
  track: transformedTrack,
9134
9728
  container: "mp4",
9135
9729
  registerAudioSampleCallback: onlyIfMoovAtomExpected.registerAudioSampleCallback,
@@ -9237,7 +9831,6 @@ var getMoovAtom = async ({
9237
9831
  });
9238
9832
  const onAudioTrack = state.onAudioTrack ? async ({ track, container }) => {
9239
9833
  await registerAudioTrack({
9240
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
9241
9834
  track,
9242
9835
  container,
9243
9836
  logLevel: state.logLevel,
@@ -9249,7 +9842,6 @@ var getMoovAtom = async ({
9249
9842
  } : null;
9250
9843
  const onVideoTrack = state.onVideoTrack ? async ({ track, container }) => {
9251
9844
  await registerVideoTrack({
9252
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
9253
9845
  track,
9254
9846
  container,
9255
9847
  logLevel: state.logLevel,
@@ -9284,7 +9876,6 @@ var getMoovAtom = async ({
9284
9876
  onlyIfMoovAtomExpected: {
9285
9877
  tracks: tracksState,
9286
9878
  isoState: null,
9287
- workOnSeekRequestOptions: null,
9288
9879
  onAudioTrack,
9289
9880
  onVideoTrack,
9290
9881
  registerVideoSampleCallback: () => Promise.resolve(),
@@ -9372,7 +9963,6 @@ var parseMdatSection = async (state) => {
9372
9963
  },
9373
9964
  timescale: samplesWithIndex.track.timescale
9374
9965
  }),
9375
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
9376
9966
  callbacks: state.callbacks
9377
9967
  });
9378
9968
  }
@@ -9399,7 +9989,6 @@ var parseMdatSection = async (state) => {
9399
9989
  },
9400
9990
  timescale: samplesWithIndex.track.timescale
9401
9991
  }),
9402
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
9403
9992
  callbacks: state.callbacks
9404
9993
  });
9405
9994
  }
@@ -9419,7 +10008,6 @@ var parseIsoBaseMedia = async (state) => {
9419
10008
  onlyIfMoovAtomExpected: {
9420
10009
  tracks: state.callbacks.tracks,
9421
10010
  isoState: state.iso,
9422
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
9423
10011
  onAudioTrack: state.onAudioTrack,
9424
10012
  onVideoTrack: state.onVideoTrack,
9425
10013
  registerAudioSampleCallback: state.callbacks.registerAudioSampleCallback,
@@ -9829,7 +10417,8 @@ var parseMedia = (options) => {
9829
10417
  onDiscardedData: null,
9830
10418
  onError: () => ({ action: "fail" }),
9831
10419
  acknowledgeRemotionLicense: Boolean(options.acknowledgeRemotionLicense),
9832
- apiName: "parseMedia()"
10420
+ apiName: "parseMedia()",
10421
+ makeSamplesStartAtZero: options.makeSamplesStartAtZero ?? true
9833
10422
  });
9834
10423
  };
9835
10424
 
@@ -9950,7 +10539,8 @@ var iteratorOverSegmentFiles = async ({
9950
10539
  return callbackOrFalse;
9951
10540
  },
9952
10541
  reader: readerInterface,
9953
- mp4HeaderSegment
10542
+ mp4HeaderSegment,
10543
+ makeSamplesStartAtZero: false
9954
10544
  });
9955
10545
  if (chunk.isHeader) {
9956
10546
  if (data.structure.type !== "iso-base-media") {
@@ -10022,7 +10612,6 @@ var runOverM3u = async ({
10022
10612
  }
10023
10613
  const onAudioSample = await registerAudioTrack({
10024
10614
  container: "m3u8",
10025
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
10026
10615
  track: {
10027
10616
  ...track,
10028
10617
  trackId
@@ -10049,7 +10638,6 @@ var runOverM3u = async ({
10049
10638
  }
10050
10639
  const onVideoSample = await registerVideoTrack({
10051
10640
  container: "m3u8",
10052
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
10053
10641
  track: {
10054
10642
  ...track,
10055
10643
  trackId
@@ -10429,7 +11017,6 @@ var parseMpegHeader = async ({
10429
11017
  state.mp3Info.setMp3Info(info);
10430
11018
  await registerAudioTrack({
10431
11019
  container: "mp3",
10432
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
10433
11020
  track: {
10434
11021
  type: "audio",
10435
11022
  codec: "mp3",
@@ -10479,7 +11066,6 @@ var parseMpegHeader = async ({
10479
11066
  trackId: 0,
10480
11067
  type: "key"
10481
11068
  },
10482
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
10483
11069
  callbacks: state.callbacks
10484
11070
  });
10485
11071
  }
@@ -10637,419 +11223,180 @@ var parseStrfVideo = ({
10637
11223
  const compression = iterator.getByteString(4, false);
10638
11224
  const sizeImage = iterator.getUint32Le();
10639
11225
  const xPelsPerMeter = iterator.getInt32Le();
10640
- const yPelsPerMeter = iterator.getInt32Le();
10641
- const clrUsed = iterator.getUint32Le();
10642
- const clrImportant = iterator.getUint32Le();
10643
- box.expectNoMoreBytes();
10644
- return {
10645
- type: "strf-box-video",
10646
- biSize,
10647
- bitCount,
10648
- clrImportant,
10649
- clrUsed,
10650
- compression,
10651
- height,
10652
- planes,
10653
- sizeImage,
10654
- width,
10655
- xPelsPerMeter,
10656
- yPelsPerMeter
10657
- };
10658
- };
10659
- var parseStrf = ({
10660
- iterator,
10661
- size,
10662
- fccType
10663
- }) => {
10664
- if (fccType === "vids") {
10665
- return parseStrfVideo({ iterator, size });
10666
- }
10667
- if (fccType === "auds") {
10668
- return parseStrfAudio({ iterator, size });
10669
- }
10670
- throw new Error(`Unsupported fccType: ${fccType}`);
10671
- };
10672
-
10673
- // src/containers/riff/parse-strh.ts
10674
- var parseStrh = ({
10675
- iterator,
10676
- size
10677
- }) => {
10678
- const box = iterator.startBox(size);
10679
- const fccType = iterator.getByteString(4, false);
10680
- if (fccType !== "vids" && fccType !== "auds") {
10681
- throw new Error("Expected AVI handler to be vids / auds");
10682
- }
10683
- const handler = fccType === "vids" ? iterator.getByteString(4, false) : iterator.getUint32Le();
10684
- if (typeof handler === "string" && handler !== "H264") {
10685
- throw new Error(`Only H264 is supported as a stream type in .avi, got ${handler}`);
10686
- }
10687
- if (fccType === "auds" && handler !== 1) {
10688
- throw new Error(`Only "1" is supported as a stream type in .avi, got ${handler}`);
10689
- }
10690
- const flags = iterator.getUint32Le();
10691
- const priority = iterator.getUint16Le();
10692
- const language2 = iterator.getUint16Le();
10693
- const initialFrames = iterator.getUint32Le();
10694
- const scale = iterator.getUint32Le();
10695
- const rate = iterator.getUint32Le();
10696
- const start = iterator.getUint32Le();
10697
- const length = iterator.getUint32Le();
10698
- const suggestedBufferSize = iterator.getUint32Le();
10699
- const quality = iterator.getUint32Le();
10700
- const sampleSize = iterator.getUint32Le();
10701
- box.discardRest();
10702
- const ckId = iterator.getByteString(4, false);
10703
- const ckSize = iterator.getUint32Le();
10704
- if (ckId !== "strf") {
10705
- throw new Error(`Expected strf, got ${JSON.stringify(ckId)}`);
10706
- }
10707
- if (iterator.bytesRemaining() < ckSize) {
10708
- throw new Error("Expected strf to be complete");
10709
- }
10710
- const strf = parseStrf({ iterator, size: ckSize, fccType });
10711
- return {
10712
- type: "strh-box",
10713
- fccType,
10714
- handler,
10715
- flags,
10716
- priority,
10717
- initialFrames,
10718
- length,
10719
- quality,
10720
- rate,
10721
- sampleSize,
10722
- scale,
10723
- start,
10724
- suggestedBufferSize,
10725
- language: language2,
10726
- strf
10727
- };
10728
- };
10729
-
10730
- // src/containers/riff/parse-riff-box.ts
10731
- var parseRiffBox = ({
10732
- size,
10733
- id,
10734
- state
10735
- }) => {
10736
- const { iterator } = state;
10737
- if (id === "LIST") {
10738
- return parseListBox({ size, state });
10739
- }
10740
- if (id === "ISFT") {
10741
- return Promise.resolve(parseIsft({ iterator, size }));
10742
- }
10743
- if (id === "avih") {
10744
- return Promise.resolve(parseAvih({ iterator, size }));
10745
- }
10746
- if (id === "strh") {
10747
- return Promise.resolve(parseStrh({ iterator, size }));
10748
- }
10749
- iterator.discard(size);
10750
- const box = {
10751
- type: "riff-box",
10752
- size,
10753
- id
10754
- };
10755
- return Promise.resolve(box);
10756
- };
10757
-
10758
- // src/containers/riff/expect-riff-box.ts
10759
- var expectRiffBox = async (state) => {
10760
- const { iterator } = state;
10761
- if (state.iterator.bytesRemaining() < 16) {
10762
- return null;
10763
- }
10764
- const checkpoint = iterator.startCheckpoint();
10765
- const ckId = iterator.getByteString(4, false);
10766
- const ckSize = iterator.getUint32Le();
10767
- if (isMoviAtom(iterator, ckId)) {
10768
- iterator.discard(4);
10769
- state.mediaSection.addMediaSection({
10770
- start: iterator.counter.getOffset(),
10771
- size: ckSize - 4
10772
- });
10773
- return null;
10774
- }
10775
- if (iterator.bytesRemaining() < ckSize) {
10776
- checkpoint.returnToCheckpoint();
10777
- return null;
10778
- }
10779
- const box = await parseRiffBox({
10780
- id: ckId,
10781
- size: ckSize,
10782
- state
10783
- });
10784
- if (box.type === "strh-box") {
10785
- if (box.strf.type === "strf-box-audio" && state.onAudioTrack) {
10786
- const audioTrack = makeAviAudioTrack({
10787
- index: state.riff.getNextTrackIndex(),
10788
- strf: box.strf
10789
- });
10790
- await registerAudioTrack({
10791
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
10792
- track: audioTrack,
10793
- container: "avi",
10794
- registerAudioSampleCallback: state.callbacks.registerAudioSampleCallback,
10795
- tracks: state.callbacks.tracks,
10796
- logLevel: state.logLevel,
10797
- onAudioTrack: state.onAudioTrack
10798
- });
10799
- }
10800
- if (state.onVideoTrack && box.strf.type === "strf-box-video") {
10801
- const videoTrack = makeAviVideoTrack({
10802
- strh: box,
10803
- index: state.riff.getNextTrackIndex(),
10804
- strf: box.strf
10805
- });
10806
- registerVideoTrackWhenProfileIsAvailable({
10807
- state,
10808
- track: videoTrack,
10809
- container: "avi"
10810
- });
10811
- }
10812
- state.riff.incrementNextTrackIndex();
10813
- }
10814
- return box;
10815
- };
10816
-
10817
- // src/containers/avc/key.ts
10818
- var getKeyFrameOrDeltaFromAvcInfo = (infos) => {
10819
- const keyOrDelta = infos.find((i) => i.type === "keyframe" || i.type === "delta-frame");
10820
- if (!keyOrDelta) {
10821
- throw new Error("expected avc to contain info about key or delta");
10822
- }
10823
- return keyOrDelta.type === "keyframe" ? "key" : "delta";
11226
+ const yPelsPerMeter = iterator.getInt32Le();
11227
+ const clrUsed = iterator.getUint32Le();
11228
+ const clrImportant = iterator.getUint32Le();
11229
+ box.expectNoMoreBytes();
11230
+ return {
11231
+ type: "strf-box-video",
11232
+ biSize,
11233
+ bitCount,
11234
+ clrImportant,
11235
+ clrUsed,
11236
+ compression,
11237
+ height,
11238
+ planes,
11239
+ sizeImage,
11240
+ width,
11241
+ xPelsPerMeter,
11242
+ yPelsPerMeter
11243
+ };
10824
11244
  };
10825
-
10826
- // src/containers/avc/parse-avc.ts
10827
- var Extended_SAR = 255;
10828
- var readVuiParameters = (iterator) => {
10829
- let sar_width = null;
10830
- let sar_height = null;
10831
- let overscan_appropriate_flag = null;
10832
- let video_format = null;
10833
- let video_full_range_flag = null;
10834
- let colour_primaries = null;
10835
- let transfer_characteristics = null;
10836
- let matrix_coefficients = null;
10837
- let chroma_sample_loc_type_top_field = null;
10838
- let chroma_sample_loc_type_bottom_field = null;
10839
- const aspect_ratio_info_present_flag = iterator.getBits(1);
10840
- if (aspect_ratio_info_present_flag) {
10841
- const aspect_ratio_idc = iterator.getBits(8);
10842
- if (aspect_ratio_idc === Extended_SAR) {
10843
- sar_width = iterator.getBits(16);
10844
- sar_height = iterator.getBits(16);
10845
- }
10846
- }
10847
- const overscan_info_present_flag = iterator.getBits(1);
10848
- if (overscan_info_present_flag) {
10849
- overscan_appropriate_flag = iterator.getBits(1);
10850
- }
10851
- const video_signal_type_present_flag = iterator.getBits(1);
10852
- if (video_signal_type_present_flag) {
10853
- video_format = iterator.getBits(3);
10854
- video_full_range_flag = Boolean(iterator.getBits(1));
10855
- const colour_description_present_flag = iterator.getBits(1);
10856
- if (colour_description_present_flag) {
10857
- colour_primaries = iterator.getBits(8);
10858
- transfer_characteristics = iterator.getBits(8);
10859
- matrix_coefficients = iterator.getBits(8);
10860
- }
11245
+ var parseStrf = ({
11246
+ iterator,
11247
+ size,
11248
+ fccType
11249
+ }) => {
11250
+ if (fccType === "vids") {
11251
+ return parseStrfVideo({ iterator, size });
10861
11252
  }
10862
- const chroma_loc_info_present_flag = iterator.getBits(1);
10863
- if (chroma_loc_info_present_flag) {
10864
- chroma_sample_loc_type_top_field = iterator.readExpGolomb();
10865
- chroma_sample_loc_type_bottom_field = iterator.readExpGolomb();
11253
+ if (fccType === "auds") {
11254
+ return parseStrfAudio({ iterator, size });
10866
11255
  }
10867
- return {
10868
- sar_width,
10869
- sar_height,
10870
- overscan_appropriate_flag,
10871
- chroma_sample_loc_type_bottom_field,
10872
- chroma_sample_loc_type_top_field,
10873
- colour_primaries,
10874
- matrix_coefficients,
10875
- transfer_characteristics,
10876
- video_format,
10877
- video_full_range_flag
10878
- };
11256
+ throw new Error(`Unsupported fccType: ${fccType}`);
10879
11257
  };
10880
- var readSps = (iterator) => {
10881
- const profile = iterator.getUint8();
10882
- const compatibility = iterator.getUint8();
10883
- const level = iterator.getUint8();
10884
- iterator.startReadingBits();
10885
- const seq_parameter_set_id = iterator.readExpGolomb();
10886
- let separate_colour_plane_flag = null;
10887
- let bit_depth_luma_minus8 = null;
10888
- let bit_depth_chroma_minus8 = null;
10889
- let qpprime_y_zero_transform_bypass_flag = null;
10890
- let log2_max_frame_num_minus4 = null;
10891
- let log2_max_pic_order_cnt_lsb_minus4 = null;
10892
- let max_num_ref_frames = null;
10893
- let gaps_in_frame_num_value_allowed_flag = null;
10894
- let mb_adaptive_frame_field_flag = null;
10895
- let direct_8x8_inference_flag = null;
10896
- let frame_crop_left_offset = null;
10897
- let frame_crop_right_offset = null;
10898
- let frame_crop_top_offset = null;
10899
- let frame_crop_bottom_offset = null;
10900
- let vui_parameters = null;
10901
- if (profile === 100 || profile === 110 || profile === 122 || profile === 244 || profile === 44 || profile === 83 || profile === 86 || profile === 118 || profile === 128 || profile === 138 || profile === 139 || profile === 134 || profile === 135) {
10902
- const chromaFormat = iterator.readExpGolomb();
10903
- if (chromaFormat === 3) {
10904
- separate_colour_plane_flag = iterator.getBits(1);
10905
- }
10906
- bit_depth_luma_minus8 = iterator.readExpGolomb();
10907
- bit_depth_chroma_minus8 = iterator.readExpGolomb();
10908
- qpprime_y_zero_transform_bypass_flag = iterator.getBits(1);
10909
- const seq_scaling_matrix_present_flag = iterator.getBits(1);
10910
- const seq_scaling_list_present_flag = [];
10911
- if (seq_scaling_matrix_present_flag) {
10912
- for (let i = 0;i < (chromaFormat !== 3 ? 8 : 12); i++) {
10913
- seq_scaling_list_present_flag[i] = iterator.getBits(1);
10914
- if (seq_scaling_list_present_flag[i]) {
10915
- if (i < 6) {
10916
- throw new Error("Not implemented");
10917
- } else {
10918
- throw new Error("Not implemented");
10919
- }
10920
- }
10921
- }
10922
- }
11258
+
11259
+ // src/containers/riff/parse-strh.ts
11260
+ var parseStrh = ({
11261
+ iterator,
11262
+ size
11263
+ }) => {
11264
+ const box = iterator.startBox(size);
11265
+ const fccType = iterator.getByteString(4, false);
11266
+ if (fccType !== "vids" && fccType !== "auds") {
11267
+ throw new Error("Expected AVI handler to be vids / auds");
10923
11268
  }
10924
- log2_max_frame_num_minus4 = iterator.readExpGolomb();
10925
- const pic_order_cnt_type = iterator.readExpGolomb();
10926
- if (pic_order_cnt_type === 0) {
10927
- log2_max_pic_order_cnt_lsb_minus4 = iterator.readExpGolomb();
10928
- } else if (pic_order_cnt_type === 1) {
10929
- throw new Error("pic_order_cnt_type = 1 not implemented");
11269
+ const handler = fccType === "vids" ? iterator.getByteString(4, false) : iterator.getUint32Le();
11270
+ if (typeof handler === "string" && handler !== "H264") {
11271
+ throw new Error(`Only H264 is supported as a stream type in .avi, got ${handler}`);
10930
11272
  }
10931
- max_num_ref_frames = iterator.readExpGolomb();
10932
- gaps_in_frame_num_value_allowed_flag = iterator.getBits(1);
10933
- const pic_width_in_mbs_minus1 = iterator.readExpGolomb();
10934
- const pic_height_in_map_units_minus1 = iterator.readExpGolomb();
10935
- const frame_mbs_only_flag = iterator.getBits(1);
10936
- if (!frame_mbs_only_flag) {
10937
- mb_adaptive_frame_field_flag = iterator.getBits(1);
11273
+ if (fccType === "auds" && handler !== 1) {
11274
+ throw new Error(`Only "1" is supported as a stream type in .avi, got ${handler}`);
10938
11275
  }
10939
- direct_8x8_inference_flag = iterator.getBits(1);
10940
- const frame_cropping_flag = iterator.getBits(1);
10941
- if (frame_cropping_flag) {
10942
- frame_crop_left_offset = iterator.readExpGolomb();
10943
- frame_crop_right_offset = iterator.readExpGolomb();
10944
- frame_crop_top_offset = iterator.readExpGolomb();
10945
- frame_crop_bottom_offset = iterator.readExpGolomb();
11276
+ const flags = iterator.getUint32Le();
11277
+ const priority = iterator.getUint16Le();
11278
+ const language2 = iterator.getUint16Le();
11279
+ const initialFrames = iterator.getUint32Le();
11280
+ const scale = iterator.getUint32Le();
11281
+ const rate = iterator.getUint32Le();
11282
+ const start = iterator.getUint32Le();
11283
+ const length = iterator.getUint32Le();
11284
+ const suggestedBufferSize = iterator.getUint32Le();
11285
+ const quality = iterator.getUint32Le();
11286
+ const sampleSize = iterator.getUint32Le();
11287
+ box.discardRest();
11288
+ const ckId = iterator.getByteString(4, false);
11289
+ const ckSize = iterator.getUint32Le();
11290
+ if (ckId !== "strf") {
11291
+ throw new Error(`Expected strf, got ${JSON.stringify(ckId)}`);
10946
11292
  }
10947
- const vui_parameters_present_flag = iterator.getBits(1);
10948
- if (vui_parameters_present_flag) {
10949
- vui_parameters = readVuiParameters(iterator);
11293
+ if (iterator.bytesRemaining() < ckSize) {
11294
+ throw new Error("Expected strf to be complete");
10950
11295
  }
10951
- iterator.stopReadingBits();
11296
+ const strf = parseStrf({ iterator, size: ckSize, fccType });
10952
11297
  return {
10953
- profile,
10954
- compatibility,
10955
- level,
10956
- bit_depth_chroma_minus8,
10957
- bit_depth_luma_minus8,
10958
- gaps_in_frame_num_value_allowed_flag,
10959
- log2_max_frame_num_minus4,
10960
- log2_max_pic_order_cnt_lsb_minus4,
10961
- max_num_ref_frames,
10962
- pic_height_in_map_units_minus1,
10963
- pic_width_in_mbs_minus1,
10964
- qpprime_y_zero_transform_bypass_flag,
10965
- separate_colour_plane_flag,
10966
- seq_parameter_set_id,
10967
- direct_8x8_inference_flag,
10968
- frame_crop_bottom_offset,
10969
- frame_crop_left_offset,
10970
- frame_crop_right_offset,
10971
- frame_crop_top_offset,
10972
- mb_adaptive_frame_field_flag,
10973
- vui_parameters
11298
+ type: "strh-box",
11299
+ fccType,
11300
+ handler,
11301
+ flags,
11302
+ priority,
11303
+ initialFrames,
11304
+ length,
11305
+ quality,
11306
+ rate,
11307
+ sampleSize,
11308
+ scale,
11309
+ start,
11310
+ suggestedBufferSize,
11311
+ language: language2,
11312
+ strf
10974
11313
  };
10975
11314
  };
10976
- var findEnd = (buffer) => {
10977
- let zeroesInARow = 0;
10978
- for (let i = 0;i < buffer.length; i++) {
10979
- const val = buffer[i];
10980
- if (val === 0) {
10981
- zeroesInARow++;
10982
- continue;
10983
- }
10984
- if (zeroesInARow >= 2 && val === 1) {
10985
- return i - zeroesInARow;
10986
- }
10987
- zeroesInARow = 0;
10988
- }
10989
- return null;
10990
- };
10991
- var inspect = (buffer) => {
10992
- const iterator = getArrayBufferIterator(buffer, buffer.byteLength);
10993
- iterator.startReadingBits();
10994
- iterator.getBits(1);
10995
- iterator.getBits(2);
10996
- const type = iterator.getBits(5);
10997
- iterator.stopReadingBits();
10998
- if (type === 7) {
10999
- const end = findEnd(buffer);
11000
- const data = readSps(iterator);
11001
- const sps = buffer.slice(0, end === null ? Infinity : end);
11002
- return {
11003
- spsData: data,
11004
- sps,
11005
- type: "avc-profile"
11006
- };
11315
+
11316
+ // src/containers/riff/parse-riff-box.ts
11317
+ var parseRiffBox = ({
11318
+ size,
11319
+ id,
11320
+ state
11321
+ }) => {
11322
+ const { iterator } = state;
11323
+ if (id === "LIST") {
11324
+ return parseListBox({ size, state });
11007
11325
  }
11008
- if (type === 5) {
11009
- return {
11010
- type: "keyframe"
11011
- };
11326
+ if (id === "ISFT") {
11327
+ return Promise.resolve(parseIsft({ iterator, size }));
11012
11328
  }
11013
- if (type === 8) {
11014
- const end = findEnd(buffer);
11015
- const pps = buffer.slice(0, end === null ? Infinity : end);
11016
- return {
11017
- type: "avc-pps",
11018
- pps
11019
- };
11329
+ if (id === "avih") {
11330
+ return Promise.resolve(parseAvih({ iterator, size }));
11020
11331
  }
11021
- if (type === 1) {
11022
- return {
11023
- type: "delta-frame"
11024
- };
11332
+ if (id === "strh") {
11333
+ return Promise.resolve(parseStrh({ iterator, size }));
11025
11334
  }
11026
- iterator.destroy();
11027
- return null;
11335
+ iterator.discard(size);
11336
+ const box = {
11337
+ type: "riff-box",
11338
+ size,
11339
+ id
11340
+ };
11341
+ return Promise.resolve(box);
11028
11342
  };
11029
- var parseAvc = (buffer) => {
11030
- let zeroesInARow = 0;
11031
- const infos = [];
11032
- for (let i = 0;i < buffer.length; i++) {
11033
- const val = buffer[i];
11034
- if (val === 0) {
11035
- zeroesInARow++;
11036
- continue;
11037
- }
11038
- if (zeroesInARow >= 2 && val === 1) {
11039
- zeroesInARow = 0;
11040
- const info = inspect(buffer.slice(i + 1, i + 100));
11041
- if (info) {
11042
- infos.push(info);
11043
- if (info.type === "keyframe" || info.type === "delta-frame") {
11044
- break;
11045
- }
11046
- }
11343
+
11344
+ // src/containers/riff/expect-riff-box.ts
11345
+ var expectRiffBox = async (state) => {
11346
+ const { iterator } = state;
11347
+ if (state.iterator.bytesRemaining() < 16) {
11348
+ return null;
11349
+ }
11350
+ const checkpoint = iterator.startCheckpoint();
11351
+ const ckId = iterator.getByteString(4, false);
11352
+ const ckSize = iterator.getUint32Le();
11353
+ if (isMoviAtom(iterator, ckId)) {
11354
+ iterator.discard(4);
11355
+ state.mediaSection.addMediaSection({
11356
+ start: iterator.counter.getOffset(),
11357
+ size: ckSize - 4
11358
+ });
11359
+ return null;
11360
+ }
11361
+ if (iterator.bytesRemaining() < ckSize) {
11362
+ checkpoint.returnToCheckpoint();
11363
+ return null;
11364
+ }
11365
+ const box = await parseRiffBox({
11366
+ id: ckId,
11367
+ size: ckSize,
11368
+ state
11369
+ });
11370
+ if (box.type === "strh-box") {
11371
+ if (box.strf.type === "strf-box-audio" && state.onAudioTrack) {
11372
+ const audioTrack = makeAviAudioTrack({
11373
+ index: state.riff.getNextTrackIndex(),
11374
+ strf: box.strf
11375
+ });
11376
+ await registerAudioTrack({
11377
+ track: audioTrack,
11378
+ container: "avi",
11379
+ registerAudioSampleCallback: state.callbacks.registerAudioSampleCallback,
11380
+ tracks: state.callbacks.tracks,
11381
+ logLevel: state.logLevel,
11382
+ onAudioTrack: state.onAudioTrack
11383
+ });
11047
11384
  }
11048
- if (val !== 1) {
11049
- zeroesInARow = 0;
11385
+ if (state.onVideoTrack && box.strf.type === "strf-box-video") {
11386
+ const videoTrack = makeAviVideoTrack({
11387
+ strh: box,
11388
+ index: state.riff.getNextTrackIndex(),
11389
+ strf: box.strf
11390
+ });
11391
+ registerVideoTrackWhenProfileIsAvailable({
11392
+ state,
11393
+ track: videoTrack,
11394
+ container: "avi"
11395
+ });
11050
11396
  }
11397
+ state.riff.incrementNextTrackIndex();
11051
11398
  }
11052
- return infos;
11399
+ return box;
11053
11400
  };
11054
11401
 
11055
11402
  // src/containers/riff/parse-movi.ts
@@ -11105,7 +11452,6 @@ var handleChunk = async ({
11105
11452
  },
11106
11453
  timescale: 1
11107
11454
  }),
11108
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
11109
11455
  callbacks: state.callbacks
11110
11456
  });
11111
11457
  return;
@@ -11135,7 +11481,6 @@ var handleChunk = async ({
11135
11481
  },
11136
11482
  timescale: 1
11137
11483
  }),
11138
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
11139
11484
  callbacks: state.callbacks
11140
11485
  });
11141
11486
  }
@@ -11296,7 +11641,10 @@ var parseSdt = (iterator) => {
11296
11641
  };
11297
11642
 
11298
11643
  // src/containers/transport-stream/parse-pes.ts
11299
- var parsePes = (iterator) => {
11644
+ var parsePes = ({
11645
+ iterator,
11646
+ offset
11647
+ }) => {
11300
11648
  const ident = iterator.getUint24();
11301
11649
  if (ident !== 1) {
11302
11650
  throw new Error(`Unexpected PES packet start code: ${ident.toString(16)}`);
@@ -11328,7 +11676,7 @@ var parsePes = (iterator) => {
11328
11676
  iterator.getBits(1);
11329
11677
  iterator.getBits(1);
11330
11678
  const pesHeaderLength = iterator.getBits(8);
11331
- const offset = iterator.counter.getOffset();
11679
+ const offsetAfterHeader = iterator.counter.getOffset();
11332
11680
  let pts = null;
11333
11681
  if (!ptsPresent) {
11334
11682
  throw new Error(`PTS is required`);
@@ -11359,12 +11707,13 @@ var parsePes = (iterator) => {
11359
11707
  dts = dts1 << 30 | dts2 << 15 | dts3;
11360
11708
  }
11361
11709
  iterator.stopReadingBits();
11362
- iterator.discard(pesHeaderLength - (iterator.counter.getOffset() - offset));
11710
+ iterator.discard(pesHeaderLength - (iterator.counter.getOffset() - offsetAfterHeader));
11363
11711
  const packet = {
11364
11712
  dts,
11365
11713
  pts,
11366
11714
  streamId,
11367
- priority
11715
+ priority,
11716
+ offset
11368
11717
  };
11369
11718
  return packet;
11370
11719
  };
@@ -11434,64 +11783,19 @@ var parsePmt = (iterator) => {
11434
11783
  return tables;
11435
11784
  };
11436
11785
 
11437
- // src/containers/transport-stream/adts-header.ts
11438
- var readAdtsHeader = (buffer) => {
11439
- if (buffer.byteLength < 9) {
11440
- return null;
11441
- }
11442
- const iterator = getArrayBufferIterator(buffer, buffer.byteLength);
11443
- iterator.startReadingBits();
11444
- const bits = iterator.getBits(12);
11445
- if (bits !== 4095) {
11446
- throw new Error("Invalid ADTS header ");
11447
- }
11448
- const id = iterator.getBits(1);
11449
- if (id !== 0) {
11450
- throw new Error("Only supporting MPEG-4 for .ts");
11451
- }
11452
- const layer = iterator.getBits(2);
11453
- if (layer !== 0) {
11454
- throw new Error("Only supporting layer 0 for .ts");
11455
- }
11456
- const protectionAbsent = iterator.getBits(1);
11457
- const audioObjectType = iterator.getBits(2);
11458
- const samplingFrequencyIndex = iterator.getBits(4);
11459
- const sampleRate = getSampleRateFromSampleFrequencyIndex(samplingFrequencyIndex);
11460
- iterator.getBits(1);
11461
- const channelConfiguration = iterator.getBits(3);
11462
- const codecPrivate2 = createAacCodecPrivate({
11463
- audioObjectType,
11464
- sampleRate,
11465
- channelConfiguration,
11466
- codecPrivate: null
11467
- });
11468
- iterator.getBits(1);
11469
- iterator.getBits(1);
11470
- iterator.getBits(1);
11471
- iterator.getBits(1);
11472
- const frameLength = iterator.getBits(13);
11473
- iterator.getBits(11);
11474
- iterator.getBits(2);
11475
- if (!protectionAbsent) {
11476
- iterator.getBits(16);
11477
- }
11478
- iterator.stopReadingBits();
11479
- iterator.destroy();
11480
- return {
11481
- frameLength,
11482
- codecPrivate: codecPrivate2,
11483
- channelConfiguration,
11484
- sampleRate,
11485
- audioObjectType
11486
- };
11487
- };
11488
-
11489
11786
  // src/containers/transport-stream/find-separator.ts
11490
- function findNthSubarrayIndex(array, subarray, n) {
11787
+ function findNthSubarrayIndex({
11788
+ array,
11789
+ subarray,
11790
+ n,
11791
+ startIndex,
11792
+ startCount
11793
+ }) {
11491
11794
  const subarrayLength = subarray.length;
11492
11795
  const arrayLength = array.length;
11493
- let count = 0;
11494
- for (let i = 0;i <= arrayLength - subarrayLength; i++) {
11796
+ let count = startCount;
11797
+ let i = startIndex;
11798
+ for (i;i <= arrayLength - subarrayLength; i++) {
11495
11799
  let match = true;
11496
11800
  for (let j = 0;j < subarrayLength; j++) {
11497
11801
  if (array[i + j] !== subarray[j]) {
@@ -11502,144 +11806,87 @@ function findNthSubarrayIndex(array, subarray, n) {
11502
11806
  if (match) {
11503
11807
  count++;
11504
11808
  if (count === n) {
11505
- return i;
11809
+ return { type: "found", index: i };
11506
11810
  }
11507
11811
  }
11508
11812
  }
11509
- return -1;
11813
+ return { type: "not-found", index: i, count };
11510
11814
  }
11511
11815
 
11512
- // src/containers/avc/interpret-sps.ts
11513
- var getDimensionsFromSps = (sps) => {
11514
- const height = sps.pic_height_in_map_units_minus1;
11515
- const width = sps.pic_width_in_mbs_minus1;
11516
- return {
11517
- height: (height + 1) * 16 - (sps.frame_crop_bottom_offset ?? 0) * 2 - (sps.frame_crop_top_offset ?? 0) * 2,
11518
- width: (width + 1) * 16 - (sps.frame_crop_right_offset ?? 0) * 2 - (sps.frame_crop_left_offset ?? 0) * 2
11519
- };
11520
- };
11521
- var getSampleAspectRatioFromSps = (sps) => {
11522
- if (sps.vui_parameters?.sar_height && sps.vui_parameters.sar_width) {
11523
- return {
11524
- width: sps.vui_parameters.sar_width,
11525
- height: sps.vui_parameters.sar_height
11526
- };
11527
- }
11528
- return {
11529
- width: 1,
11530
- height: 1
11531
- };
11532
- };
11533
- var getVideoColorFromSps = (sps) => {
11534
- const matrixCoefficients2 = sps.vui_parameters?.matrix_coefficients;
11535
- const transferCharacteristics2 = sps.vui_parameters?.transfer_characteristics;
11536
- const colorPrimaries = sps.vui_parameters?.colour_primaries;
11537
- return {
11538
- matrixCoefficients: matrixCoefficients2 ? getMatrixCoefficientsFromIndex(matrixCoefficients2) : null,
11539
- transferCharacteristics: transferCharacteristics2 ? getTransferCharacteristicsFromIndex(transferCharacteristics2) : null,
11540
- primaries: colorPrimaries ? getPrimariesFromIndex(colorPrimaries) : null,
11541
- fullRange: sps.vui_parameters?.video_full_range_flag ?? null
11542
- };
11543
- };
11544
-
11545
- // src/containers/avc/sps-and-pps.ts
11546
- var getSpsAndPps = (infos) => {
11547
- const avcProfile = infos.find((i) => i.type === "avc-profile");
11548
- const ppsProfile = infos.find((i) => i.type === "avc-pps");
11549
- if (!avcProfile || !ppsProfile) {
11550
- throw new Error("Expected avcProfile and ppsProfile");
11551
- }
11552
- return { pps: ppsProfile, sps: avcProfile };
11553
- };
11554
-
11555
- // src/containers/transport-stream/handle-avc-packet.ts
11556
- var MPEG_TIMESCALE = 90000;
11557
- var handleAvcPacket = async ({
11558
- streamBuffer,
11559
- programId,
11560
- state,
11561
- offset
11562
- }) => {
11563
- const avc = parseAvc(streamBuffer.buffer);
11564
- const isTrackRegistered = state.callbacks.tracks.getTracks().find((t) => {
11565
- return t.trackId === programId;
11566
- });
11567
- if (!isTrackRegistered) {
11568
- const spsAndPps = getSpsAndPps(avc);
11569
- const dimensions = getDimensionsFromSps(spsAndPps.sps.spsData);
11570
- const sampleAspectRatio = getSampleAspectRatioFromSps(spsAndPps.sps.spsData);
11571
- const track = {
11572
- m3uStreamFormat: null,
11573
- rotation: 0,
11574
- trackId: programId,
11575
- type: "video",
11576
- timescale: MPEG_TIMESCALE,
11577
- codec: getCodecStringFromSpsAndPps(spsAndPps.sps),
11578
- codecPrivate: createSpsPpsData(spsAndPps),
11579
- fps: null,
11580
- codedWidth: dimensions.width,
11581
- codedHeight: dimensions.height,
11582
- height: dimensions.height,
11583
- width: dimensions.width,
11584
- displayAspectWidth: dimensions.width,
11585
- displayAspectHeight: dimensions.height,
11586
- trakBox: null,
11587
- codecWithoutConfig: "h264",
11588
- description: undefined,
11589
- sampleAspectRatio: {
11590
- denominator: sampleAspectRatio.height,
11591
- numerator: sampleAspectRatio.width
11592
- },
11593
- color: getVideoColorFromSps(spsAndPps.sps.spsData)
11594
- };
11595
- await registerVideoTrack({
11596
- track,
11597
- container: "transport-stream",
11598
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
11599
- logLevel: state.logLevel,
11600
- onVideoTrack: state.onVideoTrack,
11601
- registerVideoSampleCallback: state.callbacks.registerVideoSampleCallback,
11602
- tracks: state.callbacks.tracks
11603
- });
11816
+ // src/containers/transport-stream/adts-header.ts
11817
+ var readAdtsHeader = (buffer) => {
11818
+ if (buffer.byteLength < 9) {
11819
+ return null;
11604
11820
  }
11605
- const sample = {
11606
- cts: streamBuffer.pesHeader.pts,
11607
- dts: streamBuffer.pesHeader.dts ?? streamBuffer.pesHeader.pts,
11608
- timestamp: streamBuffer.pesHeader.pts,
11609
- duration: undefined,
11610
- data: new Uint8Array(streamBuffer.buffer),
11611
- trackId: programId,
11612
- type: getKeyFrameOrDeltaFromAvcInfo(avc),
11613
- offset,
11614
- timescale: MPEG_TIMESCALE
11615
- };
11616
- await emitVideoSample({
11617
- trackId: programId,
11618
- videoSample: convertAudioOrVideoSampleToWebCodecsTimestamps({
11619
- sample,
11620
- timescale: MPEG_TIMESCALE
11621
- }),
11622
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
11623
- callbacks: state.callbacks
11821
+ const iterator = getArrayBufferIterator(buffer, buffer.byteLength);
11822
+ iterator.startReadingBits();
11823
+ const bits = iterator.getBits(12);
11824
+ if (bits !== 4095) {
11825
+ throw new Error("Invalid ADTS header ");
11826
+ }
11827
+ const id = iterator.getBits(1);
11828
+ if (id !== 0) {
11829
+ throw new Error("Only supporting MPEG-4 for .ts");
11830
+ }
11831
+ const layer = iterator.getBits(2);
11832
+ if (layer !== 0) {
11833
+ throw new Error("Only supporting layer 0 for .ts");
11834
+ }
11835
+ const protectionAbsent = iterator.getBits(1);
11836
+ const audioObjectType = iterator.getBits(2);
11837
+ const samplingFrequencyIndex = iterator.getBits(4);
11838
+ const sampleRate = getSampleRateFromSampleFrequencyIndex(samplingFrequencyIndex);
11839
+ iterator.getBits(1);
11840
+ const channelConfiguration = iterator.getBits(3);
11841
+ const codecPrivate2 = createAacCodecPrivate({
11842
+ audioObjectType,
11843
+ sampleRate,
11844
+ channelConfiguration,
11845
+ codecPrivate: null
11624
11846
  });
11847
+ iterator.getBits(1);
11848
+ iterator.getBits(1);
11849
+ iterator.getBits(1);
11850
+ iterator.getBits(1);
11851
+ const frameLength = iterator.getBits(13);
11852
+ iterator.getBits(11);
11853
+ iterator.getBits(2);
11854
+ if (!protectionAbsent) {
11855
+ iterator.getBits(16);
11856
+ }
11857
+ iterator.stopReadingBits();
11858
+ iterator.destroy();
11859
+ return {
11860
+ frameLength,
11861
+ codecPrivate: codecPrivate2,
11862
+ channelConfiguration,
11863
+ sampleRate,
11864
+ audioObjectType
11865
+ };
11625
11866
  };
11626
11867
 
11627
11868
  // src/containers/transport-stream/handle-aac-packet.ts
11628
11869
  var handleAacPacket = async ({
11629
11870
  streamBuffer,
11630
- state,
11631
11871
  programId,
11632
- offset
11872
+ offset,
11873
+ sampleCallbacks,
11874
+ logLevel,
11875
+ onAudioTrack,
11876
+ transportStream,
11877
+ makeSamplesStartAtZero
11633
11878
  }) => {
11634
- const adtsHeader = readAdtsHeader(streamBuffer.buffer);
11879
+ const adtsHeader = readAdtsHeader(streamBuffer.getBuffer());
11635
11880
  if (!adtsHeader) {
11636
11881
  throw new Error("Invalid ADTS header - too short");
11637
11882
  }
11638
11883
  const { channelConfiguration, codecPrivate: codecPrivate2, sampleRate, audioObjectType } = adtsHeader;
11639
- const isTrackRegistered = state.callbacks.tracks.getTracks().find((t) => {
11884
+ const isTrackRegistered = sampleCallbacks.tracks.getTracks().find((t) => {
11640
11885
  return t.trackId === programId;
11641
11886
  });
11642
11887
  if (!isTrackRegistered) {
11888
+ const startOffset = makeSamplesStartAtZero ? Math.min(streamBuffer.pesHeader.pts, streamBuffer.pesHeader.dts ?? Infinity) : 0;
11889
+ transportStream.startOffset.setOffset(programId, startOffset);
11643
11890
  const track = {
11644
11891
  type: "audio",
11645
11892
  codecPrivate: codecPrivate2,
@@ -11655,19 +11902,18 @@ var handleAacPacket = async ({
11655
11902
  await registerAudioTrack({
11656
11903
  track,
11657
11904
  container: "transport-stream",
11658
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
11659
- registerAudioSampleCallback: state.callbacks.registerAudioSampleCallback,
11660
- tracks: state.callbacks.tracks,
11661
- logLevel: state.logLevel,
11662
- onAudioTrack: state.onAudioTrack
11905
+ registerAudioSampleCallback: sampleCallbacks.registerAudioSampleCallback,
11906
+ tracks: sampleCallbacks.tracks,
11907
+ logLevel,
11908
+ onAudioTrack
11663
11909
  });
11664
11910
  }
11665
11911
  const sample = {
11666
- cts: streamBuffer.pesHeader.pts,
11667
- dts: streamBuffer.pesHeader.dts ?? streamBuffer.pesHeader.pts,
11668
- timestamp: streamBuffer.pesHeader.pts,
11912
+ cts: streamBuffer.pesHeader.pts - transportStream.startOffset.getOffset(programId),
11913
+ dts: (streamBuffer.pesHeader.dts ?? streamBuffer.pesHeader.pts) - transportStream.startOffset.getOffset(programId),
11914
+ timestamp: streamBuffer.pesHeader.pts - transportStream.startOffset.getOffset(programId),
11669
11915
  duration: undefined,
11670
- data: new Uint8Array(streamBuffer.buffer),
11916
+ data: streamBuffer.getBuffer(),
11671
11917
  trackId: programId,
11672
11918
  type: "key",
11673
11919
  offset,
@@ -11679,196 +11925,162 @@ var handleAacPacket = async ({
11679
11925
  sample,
11680
11926
  timescale: MPEG_TIMESCALE
11681
11927
  }),
11682
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
11683
- callbacks: state.callbacks
11928
+ callbacks: sampleCallbacks
11684
11929
  });
11930
+ transportStream.lastEmittedSample.setLastEmittedSample(sample);
11685
11931
  };
11686
11932
 
11687
11933
  // src/containers/transport-stream/process-stream-buffers.ts
11934
+ var makeTransportStreamPacketBuffer = ({
11935
+ buffers,
11936
+ pesHeader,
11937
+ offset
11938
+ }) => {
11939
+ let currentBuf = buffers ? [buffers] : [];
11940
+ let subarrayIndex = null;
11941
+ const getBuffer = () => {
11942
+ if (currentBuf.length === 0) {
11943
+ return new Uint8Array;
11944
+ }
11945
+ if (currentBuf.length === 1) {
11946
+ return currentBuf[0];
11947
+ }
11948
+ currentBuf = [combineUint8Arrays(currentBuf)];
11949
+ return currentBuf[0];
11950
+ };
11951
+ let fastFind = null;
11952
+ return {
11953
+ pesHeader,
11954
+ offset,
11955
+ getBuffer,
11956
+ addBuffer: (buffer) => {
11957
+ currentBuf.push(buffer);
11958
+ subarrayIndex = null;
11959
+ },
11960
+ get2ndSubArrayIndex: () => {
11961
+ if (subarrayIndex === null) {
11962
+ const result = findNthSubarrayIndex({
11963
+ array: getBuffer(),
11964
+ subarray: new Uint8Array([0, 0, 1, 9]),
11965
+ n: 2,
11966
+ startIndex: fastFind?.index ?? 0,
11967
+ startCount: fastFind?.count ?? 0
11968
+ });
11969
+ if (result.type === "found") {
11970
+ subarrayIndex = result.index;
11971
+ fastFind = null;
11972
+ } else {
11973
+ fastFind = result;
11974
+ return -1;
11975
+ }
11976
+ }
11977
+ return subarrayIndex;
11978
+ }
11979
+ };
11980
+ };
11688
11981
  var processStreamBuffer = async ({
11689
11982
  streamBuffer,
11690
- state,
11691
11983
  programId,
11692
- structure
11984
+ structure,
11985
+ sampleCallbacks,
11986
+ logLevel,
11987
+ onAudioTrack,
11988
+ onVideoTrack,
11989
+ transportStream,
11990
+ makeSamplesStartAtZero
11693
11991
  }) => {
11694
11992
  const stream = getStreamForId(structure, programId);
11695
11993
  if (!stream) {
11696
11994
  throw new Error("No stream found");
11697
11995
  }
11996
+ if (stream.streamType === 2) {
11997
+ throw new Error("H.262 video stream not supported");
11998
+ }
11698
11999
  if (stream.streamType === 27) {
11699
12000
  await handleAvcPacket({
11700
12001
  programId,
11701
12002
  streamBuffer,
11702
- state,
11703
- offset: streamBuffer.offset
12003
+ sampleCallbacks,
12004
+ logLevel,
12005
+ onVideoTrack,
12006
+ offset: streamBuffer.offset,
12007
+ transportStream,
12008
+ makeSamplesStartAtZero
11704
12009
  });
11705
12010
  } else if (stream.streamType === 15) {
11706
12011
  await handleAacPacket({
11707
12012
  streamBuffer,
11708
- state,
11709
12013
  programId,
11710
- offset: streamBuffer.offset
12014
+ offset: streamBuffer.offset,
12015
+ sampleCallbacks,
12016
+ logLevel,
12017
+ onAudioTrack,
12018
+ transportStream,
12019
+ makeSamplesStartAtZero
11711
12020
  });
11712
12021
  }
11713
- if (!state.callbacks.tracks.hasAllTracks()) {
11714
- const tracksRegistered = state.callbacks.tracks.getTracks().length;
12022
+ if (!sampleCallbacks.tracks.hasAllTracks()) {
12023
+ const tracksRegistered = sampleCallbacks.tracks.getTracks().length;
11715
12024
  const { streams } = findProgramMapTableOrThrow(structure);
11716
12025
  if (filterStreamsBySupportedTypes(streams).length === tracksRegistered) {
11717
- state.callbacks.tracks.setIsDone(state.logLevel);
12026
+ sampleCallbacks.tracks.setIsDone(logLevel);
11718
12027
  }
11719
12028
  }
11720
12029
  };
11721
12030
  var processFinalStreamBuffers = async ({
11722
- state,
11723
- structure
12031
+ structure,
12032
+ sampleCallbacks,
12033
+ logLevel,
12034
+ onAudioTrack,
12035
+ onVideoTrack,
12036
+ transportStream,
12037
+ makeSamplesStartAtZero
11724
12038
  }) => {
11725
- for (const [programId, buffer] of state.transportStream.streamBuffers) {
11726
- if (buffer.buffer.byteLength > 0) {
12039
+ for (const [programId, buffer] of transportStream.streamBuffers) {
12040
+ if (buffer.getBuffer().byteLength > 0) {
11727
12041
  await processStreamBuffer({
11728
12042
  streamBuffer: buffer,
11729
- state,
11730
12043
  programId,
11731
- structure
12044
+ structure,
12045
+ sampleCallbacks,
12046
+ logLevel,
12047
+ onAudioTrack,
12048
+ onVideoTrack,
12049
+ transportStream,
12050
+ makeSamplesStartAtZero
11732
12051
  });
11733
- state.transportStream.streamBuffers.delete(programId);
12052
+ transportStream.streamBuffers.delete(programId);
11734
12053
  }
11735
12054
  }
11736
12055
  };
11737
12056
 
11738
12057
  // src/containers/transport-stream/parse-stream-packet.ts
11739
- var parseAdtsStream = async ({
11740
- transportStreamEntry,
11741
- state,
11742
- structure,
11743
- offset
11744
- }) => {
11745
- const { streamBuffers, nextPesHeaderStore: nextPesHeader } = state.transportStream;
11746
- while (true) {
11747
- const streamBuffer = streamBuffers.get(transportStreamEntry.pid);
11748
- if (!streamBuffer) {
11749
- throw new Error("Stream buffer not found");
11750
- }
11751
- const expectedLength = readAdtsHeader(streamBuffer.buffer)?.frameLength ?? null;
11752
- if (expectedLength === null) {
11753
- break;
11754
- }
11755
- if (expectedLength > streamBuffer.buffer.length) {
11756
- break;
11757
- }
11758
- await processStreamBuffer({
11759
- streamBuffer: {
11760
- buffer: streamBuffer.buffer.slice(0, expectedLength),
11761
- offset,
11762
- pesHeader: streamBuffer.pesHeader
11763
- },
11764
- programId: transportStreamEntry.pid,
11765
- state,
11766
- structure
11767
- });
11768
- const rest = streamBuffer.buffer.slice(expectedLength);
11769
- streamBuffers.set(transportStreamEntry.pid, {
11770
- buffer: rest,
11771
- pesHeader: nextPesHeader.getNextPesHeader(),
11772
- offset
11773
- });
11774
- }
11775
- };
11776
- var parseAvcStream = async ({
11777
- programId,
11778
- state,
11779
- structure,
11780
- streamBuffer
11781
- }) => {
11782
- const indexOfSeparator = findNthSubarrayIndex(streamBuffer.buffer, new Uint8Array([0, 0, 1, 9]), 2);
11783
- if (indexOfSeparator === -1 || indexOfSeparator === 0) {
11784
- return null;
11785
- }
11786
- const packet = streamBuffer.buffer.slice(0, indexOfSeparator);
11787
- const rest = streamBuffer.buffer.slice(indexOfSeparator);
11788
- await processStreamBuffer({
11789
- state,
11790
- streamBuffer: {
11791
- offset: streamBuffer.offset,
11792
- pesHeader: streamBuffer.pesHeader,
11793
- buffer: packet
11794
- },
11795
- programId,
11796
- structure
11797
- });
11798
- return rest;
11799
- };
11800
- var parseStream = async ({
12058
+ var parseStream = ({
11801
12059
  transportStreamEntry,
11802
- state,
11803
12060
  programId,
11804
- structure
12061
+ iterator,
12062
+ transportStream
11805
12063
  }) => {
11806
- const { iterator } = state;
11807
- let restOfPacket = getRestOfPacket(iterator);
12064
+ const restOfPacket = getRestOfPacket(iterator);
11808
12065
  const offset = iterator.counter.getOffset();
11809
- if (transportStreamEntry.streamType === 27) {
11810
- const { streamBuffers, nextPesHeaderStore: nextPesHeader } = state.transportStream;
11811
- while (true) {
11812
- if (!streamBuffers.has(transportStreamEntry.pid)) {
11813
- streamBuffers.set(programId, {
11814
- pesHeader: nextPesHeader.getNextPesHeader(),
11815
- buffer: new Uint8Array([]),
11816
- offset
11817
- });
11818
- }
11819
- const streamBuffer = streamBuffers.get(transportStreamEntry.pid);
11820
- streamBuffer.buffer = combineUint8Arrays([
11821
- streamBuffer.buffer,
11822
- restOfPacket
11823
- ]);
11824
- const rest = await parseAvcStream({
11825
- state,
11826
- programId,
11827
- structure,
11828
- streamBuffer: streamBuffers.get(transportStreamEntry.pid)
11829
- });
11830
- if (rest !== null) {
11831
- streamBuffers.delete(transportStreamEntry.pid);
11832
- if (rest.length === 0) {
11833
- break;
11834
- }
11835
- restOfPacket = rest;
11836
- } else {
11837
- break;
11838
- }
11839
- }
11840
- return;
11841
- }
11842
- if (transportStreamEntry.streamType === 15) {
11843
- const { streamBuffers, nextPesHeaderStore: nextPesHeader } = state.transportStream;
11844
- const streamBuffer = streamBuffers.get(transportStreamEntry.pid);
11845
- if (!streamBuffer) {
11846
- streamBuffers.set(transportStreamEntry.pid, {
11847
- buffer: restOfPacket,
11848
- pesHeader: nextPesHeader.getNextPesHeader(),
11849
- offset
11850
- });
11851
- } else {
11852
- streamBuffer.buffer = combineUint8Arrays([
11853
- streamBuffer.buffer,
11854
- restOfPacket
11855
- ]);
11856
- }
11857
- return parseAdtsStream({
11858
- transportStreamEntry,
11859
- state,
11860
- structure,
12066
+ const { streamBuffers, nextPesHeaderStore: nextPesHeader } = transportStream;
12067
+ if (!streamBuffers.has(transportStreamEntry.pid)) {
12068
+ streamBuffers.set(programId, makeTransportStreamPacketBuffer({
12069
+ pesHeader: nextPesHeader.getNextPesHeader(),
12070
+ buffers: null,
11861
12071
  offset
11862
- });
12072
+ }));
11863
12073
  }
11864
- throw new Error(`Unsupported stream type ${transportStreamEntry.streamType}`);
12074
+ const streamBuffer = streamBuffers.get(transportStreamEntry.pid);
12075
+ streamBuffer.addBuffer(restOfPacket);
11865
12076
  };
11866
12077
 
11867
12078
  // src/containers/transport-stream/parse-packet.ts
11868
- var parsePacket = async ({
11869
- parserState
12079
+ var parsePacket = ({
12080
+ iterator,
12081
+ structure,
12082
+ transportStream
11870
12083
  }) => {
11871
- const { iterator } = parserState;
11872
12084
  const offset = iterator.counter.getOffset();
11873
12085
  const syncByte = iterator.getUint8();
11874
12086
  if (syncByte !== 71) {
@@ -11905,58 +12117,230 @@ var parsePacket = async ({
11905
12117
  }
11906
12118
  const read = iterator.counter.getOffset() - offset;
11907
12119
  if (read === 188) {
11908
- return Promise.resolve(null);
12120
+ return null;
11909
12121
  }
11910
- const structure = parserState.structure.getTsStructure();
11911
12122
  const pat = structure.boxes.find((b) => b.type === "transport-stream-pmt-box");
11912
12123
  const isPes = payloadUnitStartIndicator && pat?.streams.find((e) => e.pid === programId);
11913
12124
  if (isPes) {
11914
- const packetPes = parsePes(iterator);
11915
- parserState.transportStream.nextPesHeaderStore.setNextPesHeader(packetPes);
12125
+ const packetPes = parsePes({ iterator, offset });
12126
+ transportStream.nextPesHeaderStore.setNextPesHeader(packetPes);
12127
+ transportStream.observedPesHeaders.addPesHeader(packetPes);
11916
12128
  } else if (payloadUnitStartIndicator === 1) {
11917
12129
  iterator.getUint8();
11918
12130
  }
11919
12131
  if (programId === 0) {
11920
- return Promise.resolve(parsePat(iterator));
12132
+ return parsePat(iterator);
11921
12133
  }
11922
12134
  if (programId === 17) {
11923
- return Promise.resolve(parseSdt(iterator));
12135
+ return parseSdt(iterator);
11924
12136
  }
11925
12137
  const program = programId === 17 ? null : getProgramForId(structure, programId);
11926
12138
  if (program) {
11927
12139
  const pmt = parsePmt(iterator);
11928
- return Promise.resolve(pmt);
12140
+ return pmt;
11929
12141
  }
11930
- const stream = getStreamForId(structure, programId);
11931
- if (stream) {
11932
- await parseStream({
11933
- transportStreamEntry: stream,
11934
- state: parserState,
11935
- programId,
11936
- structure
12142
+ const transportStreamEntry = getStreamForId(structure, programId);
12143
+ if (transportStreamEntry) {
12144
+ parseStream({
12145
+ transportStreamEntry,
12146
+ iterator,
12147
+ transportStream,
12148
+ programId
11937
12149
  });
11938
- return Promise.resolve(null);
12150
+ return null;
12151
+ }
12152
+ throw new Error("Unknown packet identifier");
12153
+ };
12154
+
12155
+ // src/containers/transport-stream/process-audio.ts
12156
+ var canProcessAudio = ({
12157
+ streamBuffer
12158
+ }) => {
12159
+ const expectedLength = readAdtsHeader(streamBuffer.getBuffer())?.frameLength ?? null;
12160
+ if (expectedLength === null) {
12161
+ return false;
12162
+ }
12163
+ if (expectedLength > streamBuffer.getBuffer().length) {
12164
+ return false;
12165
+ }
12166
+ return true;
12167
+ };
12168
+ var processAudio = async ({
12169
+ transportStreamEntry,
12170
+ structure,
12171
+ offset,
12172
+ sampleCallbacks,
12173
+ logLevel,
12174
+ onAudioTrack,
12175
+ onVideoTrack,
12176
+ transportStream,
12177
+ makeSamplesStartAtZero
12178
+ }) => {
12179
+ const { streamBuffers, nextPesHeaderStore: nextPesHeader } = transportStream;
12180
+ const streamBuffer = streamBuffers.get(transportStreamEntry.pid);
12181
+ if (!streamBuffer) {
12182
+ throw new Error("Stream buffer not found");
12183
+ }
12184
+ const expectedLength = readAdtsHeader(streamBuffer.getBuffer())?.frameLength ?? null;
12185
+ if (expectedLength === null) {
12186
+ throw new Error("Expected length is null");
12187
+ }
12188
+ if (expectedLength > streamBuffer.getBuffer().length) {
12189
+ throw new Error("Expected length is greater than stream buffer length");
12190
+ }
12191
+ await processStreamBuffer({
12192
+ streamBuffer: makeTransportStreamPacketBuffer({
12193
+ buffers: streamBuffer.getBuffer().slice(0, expectedLength),
12194
+ offset,
12195
+ pesHeader: streamBuffer.pesHeader
12196
+ }),
12197
+ programId: transportStreamEntry.pid,
12198
+ structure,
12199
+ sampleCallbacks,
12200
+ logLevel,
12201
+ onAudioTrack,
12202
+ onVideoTrack,
12203
+ transportStream,
12204
+ makeSamplesStartAtZero
12205
+ });
12206
+ const rest = streamBuffer.getBuffer().slice(expectedLength);
12207
+ streamBuffers.set(transportStreamEntry.pid, makeTransportStreamPacketBuffer({
12208
+ buffers: rest,
12209
+ pesHeader: nextPesHeader.getNextPesHeader(),
12210
+ offset
12211
+ }));
12212
+ };
12213
+
12214
+ // src/containers/transport-stream/process-video.ts
12215
+ var canProcessVideo = ({
12216
+ streamBuffer
12217
+ }) => {
12218
+ const indexOfSeparator = streamBuffer.get2ndSubArrayIndex();
12219
+ if (indexOfSeparator === -1 || indexOfSeparator === 0) {
12220
+ return false;
12221
+ }
12222
+ return true;
12223
+ };
12224
+ var processVideo = async ({
12225
+ programId,
12226
+ structure,
12227
+ streamBuffer,
12228
+ sampleCallbacks,
12229
+ logLevel,
12230
+ onAudioTrack,
12231
+ onVideoTrack,
12232
+ transportStream,
12233
+ makeSamplesStartAtZero
12234
+ }) => {
12235
+ const indexOfSeparator = streamBuffer.get2ndSubArrayIndex();
12236
+ if (indexOfSeparator === -1 || indexOfSeparator === 0) {
12237
+ throw new Error("cannot process avc stream");
12238
+ }
12239
+ const buf = streamBuffer.getBuffer();
12240
+ const packet = buf.slice(0, indexOfSeparator);
12241
+ const rest = buf.slice(indexOfSeparator);
12242
+ await processStreamBuffer({
12243
+ streamBuffer: makeTransportStreamPacketBuffer({
12244
+ offset: streamBuffer.offset,
12245
+ pesHeader: streamBuffer.pesHeader,
12246
+ buffers: packet
12247
+ }),
12248
+ programId,
12249
+ structure,
12250
+ sampleCallbacks,
12251
+ logLevel,
12252
+ onAudioTrack,
12253
+ onVideoTrack,
12254
+ transportStream,
12255
+ makeSamplesStartAtZero
12256
+ });
12257
+ return rest;
12258
+ };
12259
+
12260
+ // src/containers/transport-stream/process-sample-if-possible.ts
12261
+ var processSampleIfPossible = async (state) => {
12262
+ const programMap = findProgramMapOrNull(state.structure.getTsStructure());
12263
+ if (!programMap) {
12264
+ return;
12265
+ }
12266
+ let processed = false;
12267
+ for (const stream of programMap.streams) {
12268
+ const streamBuffer = state.transportStream.streamBuffers.get(stream.pid);
12269
+ if (!streamBuffer) {
12270
+ continue;
12271
+ }
12272
+ if (stream.streamType === 27) {
12273
+ if (canProcessVideo({ streamBuffer })) {
12274
+ const rest = await processVideo({
12275
+ programId: stream.pid,
12276
+ structure: state.structure.getTsStructure(),
12277
+ streamBuffer,
12278
+ sampleCallbacks: state.callbacks,
12279
+ logLevel: state.logLevel,
12280
+ onAudioTrack: state.onAudioTrack,
12281
+ onVideoTrack: state.onVideoTrack,
12282
+ transportStream: state.transportStream,
12283
+ makeSamplesStartAtZero: state.makeSamplesStartAtZero
12284
+ });
12285
+ state.transportStream.streamBuffers.delete(stream.pid);
12286
+ state.transportStream.streamBuffers.set(stream.pid, makeTransportStreamPacketBuffer({
12287
+ pesHeader: state.transportStream.nextPesHeaderStore.getNextPesHeader(),
12288
+ buffers: rest,
12289
+ offset: state.iterator.counter.getOffset()
12290
+ }));
12291
+ processed = true;
12292
+ break;
12293
+ }
12294
+ }
12295
+ if (stream.streamType === 15) {
12296
+ if (canProcessAudio({ streamBuffer })) {
12297
+ await processAudio({
12298
+ structure: state.structure.getTsStructure(),
12299
+ offset: state.iterator.counter.getOffset(),
12300
+ sampleCallbacks: state.callbacks,
12301
+ logLevel: state.logLevel,
12302
+ onAudioTrack: state.onAudioTrack,
12303
+ onVideoTrack: state.onVideoTrack,
12304
+ transportStream: state.transportStream,
12305
+ makeSamplesStartAtZero: state.makeSamplesStartAtZero,
12306
+ transportStreamEntry: stream
12307
+ });
12308
+ processed = true;
12309
+ break;
12310
+ }
12311
+ }
11939
12312
  }
11940
- throw new Error("Unknown packet identifier");
12313
+ return processed;
11941
12314
  };
11942
12315
 
11943
12316
  // src/containers/transport-stream/parse-transport-stream.ts
11944
12317
  var parseTransportStream = async (state) => {
11945
12318
  const structure = state.structure.getTsStructure();
12319
+ const processed = await processSampleIfPossible(state);
12320
+ if (processed) {
12321
+ return Promise.resolve(null);
12322
+ }
11946
12323
  const { iterator } = state;
11947
12324
  if (iterator.bytesRemaining() < 188) {
11948
12325
  return Promise.resolve(null);
11949
12326
  }
11950
- const packet = await parsePacket({
11951
- parserState: state
12327
+ const packet = parsePacket({
12328
+ iterator,
12329
+ structure,
12330
+ transportStream: state.transportStream
11952
12331
  });
11953
12332
  if (packet) {
11954
12333
  structure.boxes.push(packet);
11955
12334
  }
11956
12335
  if (iterator.bytesRemaining() === 0) {
11957
12336
  await processFinalStreamBuffers({
11958
- state,
11959
- structure
12337
+ transportStream: state.transportStream,
12338
+ structure,
12339
+ sampleCallbacks: state.callbacks,
12340
+ logLevel: state.logLevel,
12341
+ onAudioTrack: state.onAudioTrack,
12342
+ onVideoTrack: state.onVideoTrack,
12343
+ makeSamplesStartAtZero: state.makeSamplesStartAtZero
11960
12344
  });
11961
12345
  }
11962
12346
  return Promise.resolve(null);
@@ -12014,7 +12398,6 @@ var parseFmt = async ({
12014
12398
  };
12015
12399
  state.structure.getWavStructure().boxes.push(wavHeader);
12016
12400
  await registerAudioTrack({
12017
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
12018
12401
  track: {
12019
12402
  type: "audio",
12020
12403
  codec: format,
@@ -12139,7 +12522,6 @@ var parseMediaSection2 = async ({
12139
12522
  },
12140
12523
  timescale: 1
12141
12524
  }),
12142
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
12143
12525
  callbacks: state.callbacks
12144
12526
  });
12145
12527
  return null;
@@ -12172,6 +12554,35 @@ var parseWav = (state) => {
12172
12554
  throw new Error(`Unknown WAV box type ${type}`);
12173
12555
  };
12174
12556
 
12557
+ // src/containers/webm/get-byte-for-cues.ts
12558
+ var getByteForSeek = ({
12559
+ seekHeadSegment,
12560
+ offset
12561
+ }) => {
12562
+ const value = seekHeadSegment.value.map((v) => {
12563
+ if (v.type !== "Seek") {
12564
+ return null;
12565
+ }
12566
+ const seekId2 = v.value.find((_v) => {
12567
+ return _v.type === "SeekID" && _v.value === matroskaElements.Cues;
12568
+ });
12569
+ if (!seekId2) {
12570
+ return null;
12571
+ }
12572
+ const seekPosition2 = v.value.find((_v) => {
12573
+ return _v.type === "SeekPosition";
12574
+ });
12575
+ if (!seekPosition2) {
12576
+ return false;
12577
+ }
12578
+ return seekPosition2.value;
12579
+ }).filter(truthy);
12580
+ if (value.length === 0) {
12581
+ return null;
12582
+ }
12583
+ return value[0].value + offset;
12584
+ };
12585
+
12175
12586
  // src/containers/webm/segments/block-simple-block-flags.ts
12176
12587
  var parseBlockFlags = (iterator, type) => {
12177
12588
  if (type === matroskaElements.Block) {
@@ -12208,19 +12619,20 @@ var parseBlockFlags = (iterator, type) => {
12208
12619
  var addAvcToTrackIfNecessary = ({
12209
12620
  partialVideoSample,
12210
12621
  codec,
12211
- state,
12622
+ structureState: structureState2,
12623
+ webmState,
12212
12624
  trackNumber: trackNumber2
12213
12625
  }) => {
12214
- if (codec === "V_MPEG4/ISO/AVC" && getTracksFromMatroska({ state }).missingInfo.length > 0) {
12626
+ if (codec === "V_MPEG4/ISO/AVC" && getTracksFromMatroska({ structureState: structureState2, webmState }).missingInfo.length > 0) {
12215
12627
  const parsed = parseAvc(partialVideoSample.data);
12216
12628
  for (const parse of parsed) {
12217
12629
  if (parse.type === "avc-profile") {
12218
- state.webm.setAvcProfileForTrackNumber(trackNumber2, parse);
12630
+ webmState.setAvcProfileForTrackNumber(trackNumber2, parse);
12219
12631
  }
12220
12632
  }
12221
12633
  }
12222
12634
  };
12223
- var getSampleFromBlock = (ebml, state, offset) => {
12635
+ var getSampleFromBlock = (ebml, webmState, offset, structureState2) => {
12224
12636
  const iterator = getArrayBufferIterator(ebml.value, ebml.value.length);
12225
12637
  const trackNumber2 = iterator.getVint();
12226
12638
  if (trackNumber2 === null) {
@@ -12228,9 +12640,9 @@ var getSampleFromBlock = (ebml, state, offset) => {
12228
12640
  }
12229
12641
  const timecodeRelativeToCluster = iterator.getInt16();
12230
12642
  const { keyframe } = parseBlockFlags(iterator, ebml.type === "SimpleBlock" ? matroskaElements.SimpleBlock : matroskaElements.Block);
12231
- const { codec, trackTimescale } = state.webm.getTrackInfoByNumber(trackNumber2);
12232
- const clusterOffset = state.webm.getTimestampOffsetForByteOffset(offset);
12233
- const timescale = state.webm.getTimescale();
12643
+ const { codec, trackTimescale } = webmState.getTrackInfoByNumber(trackNumber2);
12644
+ const clusterOffset = webmState.getTimestampOffsetForByteOffset(offset);
12645
+ const timescale = webmState.getTimescale();
12234
12646
  if (clusterOffset === undefined) {
12235
12647
  throw new Error("Could not find offset for byte offset " + offset);
12236
12648
  }
@@ -12261,7 +12673,8 @@ var getSampleFromBlock = (ebml, state, offset) => {
12261
12673
  addAvcToTrackIfNecessary({
12262
12674
  codec,
12263
12675
  partialVideoSample,
12264
- state,
12676
+ structureState: structureState2,
12677
+ webmState,
12265
12678
  trackNumber: trackNumber2
12266
12679
  });
12267
12680
  const sample = {
@@ -12299,8 +12712,7 @@ var getSampleFromBlock = (ebml, state, offset) => {
12299
12712
  };
12300
12713
 
12301
12714
  // src/containers/webm/parse-ebml.ts
12302
- var parseEbml = async (state) => {
12303
- const { iterator } = state;
12715
+ var parseEbml = async (iterator, statesForProcessing) => {
12304
12716
  const hex = iterator.getMatroskaSegmentId();
12305
12717
  if (hex === null) {
12306
12718
  throw new Error("Not enough bytes left to parse EBML - this should not happen");
@@ -12369,12 +12781,12 @@ var parseEbml = async (state) => {
12369
12781
  break;
12370
12782
  }
12371
12783
  const offset = iterator.counter.getOffset();
12372
- const value = await parseEbml(state);
12373
- const remapped = await postprocessEbml({
12784
+ const value = await parseEbml(iterator, statesForProcessing);
12785
+ const remapped = statesForProcessing ? await postprocessEbml({
12374
12786
  offset,
12375
12787
  ebml: value,
12376
- state
12377
- });
12788
+ statesForProcessing
12789
+ }) : value;
12378
12790
  children.push(remapped);
12379
12791
  const offsetNow = iterator.counter.getOffset();
12380
12792
  if (offsetNow - startOffset > size) {
@@ -12391,54 +12803,58 @@ var parseEbml = async (state) => {
12391
12803
  var postprocessEbml = async ({
12392
12804
  offset,
12393
12805
  ebml,
12394
- state
12806
+ statesForProcessing: {
12807
+ webmState,
12808
+ callbacks,
12809
+ logLevel,
12810
+ onAudioTrack,
12811
+ onVideoTrack,
12812
+ structureState: structureState2
12813
+ }
12395
12814
  }) => {
12396
12815
  if (ebml.type === "TimestampScale") {
12397
- state.webm.setTimescale(ebml.value.value);
12816
+ webmState.setTimescale(ebml.value.value);
12398
12817
  }
12399
12818
  if (ebml.type === "Tracks") {
12400
- state.callbacks.tracks.setIsDone(state.logLevel);
12819
+ callbacks.tracks.setIsDone(logLevel);
12401
12820
  }
12402
12821
  if (ebml.type === "TrackEntry") {
12403
- state.webm.onTrackEntrySegment(ebml);
12822
+ webmState.onTrackEntrySegment(ebml);
12404
12823
  const track = getTrack({
12405
12824
  track: ebml,
12406
- timescale: state.webm.getTimescale()
12825
+ timescale: webmState.getTimescale()
12407
12826
  });
12408
12827
  if (track && track.type === "audio") {
12409
12828
  await registerAudioTrack({
12410
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
12411
12829
  track,
12412
12830
  container: "webm",
12413
- registerAudioSampleCallback: state.callbacks.registerAudioSampleCallback,
12414
- tracks: state.callbacks.tracks,
12415
- logLevel: state.logLevel,
12416
- onAudioTrack: state.onAudioTrack
12831
+ registerAudioSampleCallback: callbacks.registerAudioSampleCallback,
12832
+ tracks: callbacks.tracks,
12833
+ logLevel,
12834
+ onAudioTrack
12417
12835
  });
12418
12836
  }
12419
12837
  if (track && track.type === "video") {
12420
12838
  await registerVideoTrack({
12421
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
12422
12839
  track,
12423
12840
  container: "webm",
12424
- logLevel: state.logLevel,
12425
- onVideoTrack: state.onVideoTrack,
12426
- registerVideoSampleCallback: state.callbacks.registerVideoSampleCallback,
12427
- tracks: state.callbacks.tracks
12841
+ logLevel,
12842
+ onVideoTrack,
12843
+ registerVideoSampleCallback: callbacks.registerVideoSampleCallback,
12844
+ tracks: callbacks.tracks
12428
12845
  });
12429
12846
  }
12430
12847
  }
12431
12848
  if (ebml.type === "Timestamp") {
12432
- state.webm.setTimestampOffset(offset, ebml.value.value);
12849
+ webmState.setTimestampOffset(offset, ebml.value.value);
12433
12850
  }
12434
12851
  if (ebml.type === "Block" || ebml.type === "SimpleBlock") {
12435
- const sample = getSampleFromBlock(ebml, state, offset);
12852
+ const sample = getSampleFromBlock(ebml, webmState, offset, structureState2);
12436
12853
  if (sample.type === "video-sample") {
12437
12854
  await emitVideoSample({
12438
12855
  trackId: sample.videoSample.trackId,
12439
12856
  videoSample: sample.videoSample,
12440
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
12441
- callbacks: state.callbacks
12857
+ callbacks
12442
12858
  });
12443
12859
  return {
12444
12860
  type: "Block",
@@ -12450,8 +12866,7 @@ var postprocessEbml = async ({
12450
12866
  await emitAudioSample({
12451
12867
  trackId: sample.audioSample.trackId,
12452
12868
  audioSample: sample.audioSample,
12453
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
12454
- callbacks: state.callbacks
12869
+ callbacks
12455
12870
  });
12456
12871
  return {
12457
12872
  type: "Block",
@@ -12473,7 +12888,7 @@ var postprocessEbml = async ({
12473
12888
  throw new Error("Expected block segment");
12474
12889
  }
12475
12890
  const hasReferenceBlock = ebml.value.find((c) => c.type === "ReferenceBlock");
12476
- const sample = block2.value.length === 0 ? null : getSampleFromBlock(block2, state, offset);
12891
+ const sample = block2.value.length === 0 ? null : getSampleFromBlock(block2, webmState, offset, structureState2);
12477
12892
  if (sample && sample.type === "partial-video-sample") {
12478
12893
  const completeFrame = {
12479
12894
  ...sample.partialVideoSample,
@@ -12482,8 +12897,7 @@ var postprocessEbml = async ({
12482
12897
  await emitVideoSample({
12483
12898
  trackId: sample.partialVideoSample.trackId,
12484
12899
  videoSample: completeFrame,
12485
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
12486
- callbacks: state.callbacks
12900
+ callbacks
12487
12901
  });
12488
12902
  }
12489
12903
  return {
@@ -12497,10 +12911,12 @@ var postprocessEbml = async ({
12497
12911
 
12498
12912
  // src/containers/webm/segments.ts
12499
12913
  var expectSegment = async ({
12500
- state,
12501
- isInsideSegment
12914
+ statesForProcessing,
12915
+ isInsideSegment,
12916
+ iterator,
12917
+ logLevel,
12918
+ mediaSectionState: mediaSectionState2
12502
12919
  }) => {
12503
- const { iterator } = state;
12504
12920
  if (iterator.bytesRemaining() === 0) {
12505
12921
  throw new Error("has no bytes");
12506
12922
  }
@@ -12519,9 +12935,12 @@ var expectSegment = async ({
12519
12935
  return null;
12520
12936
  }
12521
12937
  const bytesRemainingNow = iterator.bytesRemaining();
12522
- Log.trace(state.logLevel, "Segment ID:", ebmlMap[segmentId]?.name, "Size:" + size, bytesRemainingNow);
12938
+ Log.trace(logLevel, "Segment ID:", ebmlMap[segmentId]?.name, "Size:" + size, bytesRemainingNow);
12523
12939
  if (segmentId === matroskaElements.Segment) {
12524
- state.webm.addSegment({
12940
+ if (!statesForProcessing) {
12941
+ throw new Error("States for processing are required");
12942
+ }
12943
+ statesForProcessing.webmState.addSegment({
12525
12944
  start: offset,
12526
12945
  size
12527
12946
  });
@@ -12536,7 +12955,16 @@ var expectSegment = async ({
12536
12955
  if (isInsideSegment === null) {
12537
12956
  throw new Error("Expected to be inside segment");
12538
12957
  }
12539
- state.webm.addCluster({
12958
+ if (!statesForProcessing) {
12959
+ throw new Error("States for processing are required");
12960
+ }
12961
+ if (mediaSectionState2) {
12962
+ mediaSectionState2.addMediaSection({
12963
+ start: offset,
12964
+ size
12965
+ });
12966
+ }
12967
+ statesForProcessing.webmState.addCluster({
12540
12968
  start: offset,
12541
12969
  size,
12542
12970
  segment: isInsideSegment.index
@@ -12555,37 +12983,75 @@ var expectSegment = async ({
12555
12983
  const segment = await parseSegment({
12556
12984
  segmentId,
12557
12985
  length: size,
12558
- state,
12559
- headerReadSoFar: iterator.counter.getOffset() - offset
12986
+ headerReadSoFar: iterator.counter.getOffset() - offset,
12987
+ statesForProcessing,
12988
+ iterator
12560
12989
  });
12561
12990
  return segment;
12562
12991
  };
12563
12992
  var parseSegment = async ({
12564
12993
  segmentId,
12565
12994
  length,
12566
- state,
12567
- headerReadSoFar
12995
+ iterator,
12996
+ headerReadSoFar,
12997
+ statesForProcessing
12568
12998
  }) => {
12569
12999
  if (length < 0) {
12570
13000
  throw new Error(`Expected length of ${segmentId} to be greater or equal 0`);
12571
13001
  }
12572
- state.iterator.counter.decrement(headerReadSoFar);
12573
- const offset = state.iterator.counter.getOffset();
12574
- const ebml = await parseEbml(state);
12575
- const remapped = await postprocessEbml({ offset, ebml, state });
13002
+ iterator.counter.decrement(headerReadSoFar);
13003
+ const offset = iterator.counter.getOffset();
13004
+ const ebml = await parseEbml(iterator, statesForProcessing);
13005
+ if (!statesForProcessing) {
13006
+ return ebml;
13007
+ }
13008
+ const remapped = await postprocessEbml({
13009
+ offset,
13010
+ ebml,
13011
+ statesForProcessing
13012
+ });
12576
13013
  return remapped;
12577
13014
  };
12578
13015
 
13016
+ // src/containers/webm/state-for-processing.ts
13017
+ var selectStatesForProcessing = ({
13018
+ callbacks,
13019
+ logLevel,
13020
+ onAudioTrack,
13021
+ onVideoTrack,
13022
+ structure,
13023
+ webm
13024
+ }) => {
13025
+ return {
13026
+ webmState: webm,
13027
+ callbacks,
13028
+ logLevel,
13029
+ onAudioTrack,
13030
+ onVideoTrack,
13031
+ structureState: structure
13032
+ };
13033
+ };
13034
+
12579
13035
  // src/containers/webm/parse-webm-header.ts
12580
13036
  var parseWebm = async (state) => {
12581
13037
  const structure = state.structure.getMatroskaStructure();
12582
13038
  const { iterator } = state;
13039
+ const offset = iterator.counter.getOffset();
12583
13040
  const isInsideSegment = state.webm.isInsideSegment(iterator);
12584
- const isInsideCluster = state.webm.isInsideCluster(iterator);
13041
+ const isInsideCluster = state.webm.isInsideCluster(offset);
12585
13042
  const results = await expectSegment({
12586
- state,
12587
- isInsideSegment
13043
+ iterator,
13044
+ logLevel: state.logLevel,
13045
+ statesForProcessing: selectStatesForProcessing(state),
13046
+ isInsideSegment,
13047
+ mediaSectionState: state.mediaSection
12588
13048
  });
13049
+ if (results?.type === "SeekHead") {
13050
+ const position = getByteForSeek({ seekHeadSegment: results, offset });
13051
+ if (position !== null) {
13052
+ state.webm.cues.triggerLoad(position, offset);
13053
+ }
13054
+ }
12589
13055
  if (results === null) {
12590
13056
  return null;
12591
13057
  }
@@ -12636,7 +13102,6 @@ var initVideo = async ({ state }) => {
12636
13102
  const tracks2 = getTracksFromMoovBox(moovAtom);
12637
13103
  for (const track of tracks2.videoTracks) {
12638
13104
  await registerVideoTrack({
12639
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
12640
13105
  track,
12641
13106
  container: "mp4",
12642
13107
  logLevel: state.logLevel,
@@ -12647,7 +13112,6 @@ var initVideo = async ({ state }) => {
12647
13112
  }
12648
13113
  for (const track of tracks2.audioTracks) {
12649
13114
  await registerAudioTrack({
12650
- workOnSeekRequestOptions: getWorkOnSeekRequestOptions(state),
12651
13115
  track,
12652
13116
  container: "mp4",
12653
13117
  registerAudioSampleCallback: state.callbacks.registerAudioSampleCallback,
@@ -12681,6 +13145,10 @@ var initVideo = async ({ state }) => {
12681
13145
  }
12682
13146
  if (fileType.type === "transport-stream") {
12683
13147
  Log.verbose(state.logLevel, "Detected MPEG-2 Transport Stream");
13148
+ state.mediaSection.addMediaSection({
13149
+ start: 0,
13150
+ size: contentLength
13151
+ });
12684
13152
  state.structure.setStructure({
12685
13153
  boxes: [],
12686
13154
  type: "transport-stream"
@@ -12849,12 +13317,10 @@ var parseLoop = async ({
12849
13317
  }
12850
13318
  try {
12851
13319
  await triggerInfoEmit(state);
12852
- const start = Date.now();
12853
13320
  await state.controller._internals.checkForAbortAndPause();
12854
13321
  const skip = await runParseIteration({
12855
13322
  state
12856
13323
  });
12857
- state.timings.timeIterating += Date.now() - start;
12858
13324
  if (skip !== null) {
12859
13325
  state.increaseSkippedBytes(skip.skipTo - state.iterator.counter.getOffset());
12860
13326
  if (skip.skipTo === state.contentLength) {
@@ -12902,7 +13368,6 @@ var parseLoop = async ({
12902
13368
  iterationWithThisOffset++;
12903
13369
  } else {
12904
13370
  iterationWithThisOffset = 0;
12905
- state.seekInfiniteLoop.reset();
12906
13371
  }
12907
13372
  }
12908
13373
  };
@@ -13223,6 +13688,9 @@ var keyframesState = () => {
13223
13688
  const keyframes = [];
13224
13689
  return {
13225
13690
  addKeyframe: (keyframe) => {
13691
+ if (keyframes.find((k) => k.positionInBytes === keyframe.positionInBytes)) {
13692
+ return;
13693
+ }
13226
13694
  keyframes.push(keyframe);
13227
13695
  },
13228
13696
  getKeyframes: () => {
@@ -13374,52 +13842,291 @@ var m3uState = (logLevel) => {
13374
13842
  setAllChunksProcessed: (src) => {
13375
13843
  allChunksProcessed[src] = true;
13376
13844
  },
13377
- getAllChunksProcessedForPlaylist,
13378
- getAllChunksProcessedOverall: () => {
13379
- if (!selectedMainPlaylist) {
13380
- return false;
13845
+ getAllChunksProcessedForPlaylist,
13846
+ getAllChunksProcessedOverall: () => {
13847
+ if (!selectedMainPlaylist) {
13848
+ return false;
13849
+ }
13850
+ const selectedPlaylists = getSelectedPlaylists();
13851
+ return selectedPlaylists.every((url) => allChunksProcessed[url]);
13852
+ },
13853
+ setHasFinishedManifest: () => {
13854
+ hasFinishedManifest = true;
13855
+ },
13856
+ hasFinishedManifest: () => hasFinishedManifest,
13857
+ setM3uStreamRun: (playlistUrl, run) => {
13858
+ if (!run) {
13859
+ delete m3uStreamRuns[playlistUrl];
13860
+ return;
13861
+ }
13862
+ m3uStreamRuns[playlistUrl] = run;
13863
+ },
13864
+ setTracksDone: (playlistUrl) => {
13865
+ tracksDone[playlistUrl] = true;
13866
+ const selectedPlaylists = getSelectedPlaylists();
13867
+ return selectedPlaylists.every((url) => tracksDone[url]);
13868
+ },
13869
+ getTrackDone: (playlistUrl) => {
13870
+ return tracksDone[playlistUrl];
13871
+ },
13872
+ getM3uStreamRun: (playlistUrl) => m3uStreamRuns[playlistUrl] ?? null,
13873
+ abortM3UStreamRuns: () => {
13874
+ const values = Object.values(m3uStreamRuns);
13875
+ if (values.length === 0) {
13876
+ return;
13877
+ }
13878
+ Log.trace(logLevel, `Aborting ${values.length} M3U stream runs`);
13879
+ values.forEach((run) => {
13880
+ run.abort();
13881
+ });
13882
+ },
13883
+ setAssociatedPlaylists: (playlists) => {
13884
+ associatedPlaylists = playlists;
13885
+ },
13886
+ getAssociatedPlaylists: () => associatedPlaylists,
13887
+ getSelectedPlaylists,
13888
+ sampleSorter: sampleSorter({ logLevel, getAllChunksProcessedForPlaylist }),
13889
+ setMp4HeaderSegment,
13890
+ getMp4HeaderSegment
13891
+ };
13892
+ };
13893
+
13894
+ // src/containers/webm/seek/format-cues.ts
13895
+ var formatCues = (cues) => {
13896
+ const matroskaCues = [];
13897
+ for (const cue of cues) {
13898
+ if (cue.type === "Crc32") {
13899
+ continue;
13900
+ }
13901
+ if (cue.type !== "CuePoint") {
13902
+ throw new Error("Expected CuePoint");
13903
+ }
13904
+ const cueTime = cue.value.find((_cue) => _cue.type === "CueTime");
13905
+ if (!cueTime) {
13906
+ throw new Error("Expected CueTime");
13907
+ }
13908
+ const cueTrackPositions = cue.value.find((c) => c.type === "CueTrackPositions");
13909
+ if (!cueTrackPositions) {
13910
+ throw new Error("Expected CueTrackPositions");
13911
+ }
13912
+ const cueTimeValue = cueTime.value.value;
13913
+ const cueTrack = cueTrackPositions.value.find((_c) => _c.type === "CueTrack");
13914
+ if (!cueTrack) {
13915
+ throw new Error("Expected CueTrack");
13916
+ }
13917
+ const cueClusterPosition = cueTrackPositions.value.find((_c) => _c.type === "CueClusterPosition");
13918
+ if (!cueClusterPosition) {
13919
+ throw new Error("Expected CueClusterPosition");
13920
+ }
13921
+ const cueRelativePosition = cueTrackPositions.value.find((_c) => _c.type === "CueRelativePosition");
13922
+ const matroskaCue = {
13923
+ trackId: cueTrack.value.value,
13924
+ timeInTimescale: cueTimeValue,
13925
+ clusterPositionInSegment: cueClusterPosition.value.value,
13926
+ relativePosition: cueRelativePosition?.value.value ?? 0
13927
+ };
13928
+ matroskaCues.push(matroskaCue);
13929
+ }
13930
+ return matroskaCues;
13931
+ };
13932
+
13933
+ // src/containers/webm/seek/fetch-web-cues.ts
13934
+ var fetchWebmCues = async ({
13935
+ src,
13936
+ readerInterface,
13937
+ controller,
13938
+ position,
13939
+ logLevel
13940
+ }) => {
13941
+ const result = await readerInterface.read({
13942
+ controller,
13943
+ range: position,
13944
+ src
13945
+ });
13946
+ const { value } = await result.reader.reader.read();
13947
+ if (!value) {
13948
+ return null;
13949
+ }
13950
+ result.reader.abort();
13951
+ const iterator = getArrayBufferIterator(value, value.length);
13952
+ const segment = await expectSegment({
13953
+ iterator,
13954
+ logLevel,
13955
+ statesForProcessing: null,
13956
+ isInsideSegment: null,
13957
+ mediaSectionState: null
13958
+ });
13959
+ iterator.destroy();
13960
+ if (!segment?.value) {
13961
+ return null;
13962
+ }
13963
+ return formatCues(segment.value);
13964
+ };
13965
+
13966
+ // src/state/matroska/lazy-cues-fetch.ts
13967
+ var lazyCuesFetch = ({
13968
+ controller,
13969
+ logLevel,
13970
+ readerInterface,
13971
+ src
13972
+ }) => {
13973
+ let prom = null;
13974
+ let sOffset = null;
13975
+ const triggerLoad = (position, segmentOffset) => {
13976
+ if (prom) {
13977
+ return prom;
13978
+ }
13979
+ if (sOffset && sOffset !== segmentOffset) {
13980
+ throw new Error("Segment offset mismatch");
13981
+ }
13982
+ sOffset = segmentOffset;
13983
+ Log.verbose(logLevel, "Cues box found, trying to lazy load cues");
13984
+ prom = fetchWebmCues({
13985
+ controller,
13986
+ logLevel,
13987
+ position,
13988
+ readerInterface,
13989
+ src
13990
+ }).then((cues) => {
13991
+ Log.verbose(logLevel, "Cues loaded");
13992
+ return cues;
13993
+ });
13994
+ return prom;
13995
+ };
13996
+ const getLoadedCues = async () => {
13997
+ if (!prom) {
13998
+ return null;
13999
+ }
14000
+ const cues = await prom;
14001
+ if (!cues) {
14002
+ return null;
14003
+ }
14004
+ if (!sOffset) {
14005
+ throw new Error("Segment offset not set");
14006
+ }
14007
+ return {
14008
+ cues,
14009
+ segmentOffset: sOffset
14010
+ };
14011
+ };
14012
+ return {
14013
+ triggerLoad,
14014
+ getLoadedCues
14015
+ };
14016
+ };
14017
+
14018
+ // src/state/matroska/webm.ts
14019
+ var webmState = ({
14020
+ controller,
14021
+ logLevel,
14022
+ readerInterface,
14023
+ src
14024
+ }) => {
14025
+ const trackEntries = {};
14026
+ const onTrackEntrySegment = (trackEntry2) => {
14027
+ const trackId = getTrackId(trackEntry2);
14028
+ if (!trackId) {
14029
+ throw new Error("Expected track id");
14030
+ }
14031
+ if (trackEntries[trackId]) {
14032
+ return;
14033
+ }
14034
+ const codec = getTrackCodec(trackEntry2);
14035
+ if (!codec) {
14036
+ throw new Error("Expected codec");
14037
+ }
14038
+ const trackTimescale = getTrackTimestampScale(trackEntry2);
14039
+ trackEntries[trackId] = {
14040
+ codec: codec.value,
14041
+ trackTimescale: trackTimescale?.value ?? null
14042
+ };
14043
+ };
14044
+ const timestampMap = new Map;
14045
+ const getTimestampOffsetForByteOffset = (byteOffset) => {
14046
+ const entries = Array.from(timestampMap.entries());
14047
+ const sortedByByteOffset = entries.sort((a, b) => {
14048
+ return a[0] - b[0];
14049
+ }).reverse();
14050
+ for (const [offset, timestamp] of sortedByByteOffset) {
14051
+ if (offset >= byteOffset) {
14052
+ continue;
14053
+ }
14054
+ return timestamp;
14055
+ }
14056
+ return timestampMap.get(byteOffset);
14057
+ };
14058
+ const setTimestampOffset = (byteOffset, timestamp) => {
14059
+ timestampMap.set(byteOffset, timestamp);
14060
+ };
14061
+ let timescale = null;
14062
+ const setTimescale = (newTimescale) => {
14063
+ timescale = newTimescale;
14064
+ };
14065
+ const getTimescale = () => {
14066
+ if (timescale === null) {
14067
+ return 1e6;
14068
+ }
14069
+ return timescale;
14070
+ };
14071
+ const segments = [];
14072
+ const clusters = [];
14073
+ const avcProfilesMap = {};
14074
+ const setAvcProfileForTrackNumber = (trackNumber2, avcProfile) => {
14075
+ avcProfilesMap[trackNumber2] = avcProfile;
14076
+ };
14077
+ const getAvcProfileForTrackNumber = (trackNumber2) => {
14078
+ return avcProfilesMap[trackNumber2] ?? null;
14079
+ };
14080
+ const cues = lazyCuesFetch({
14081
+ controller,
14082
+ logLevel,
14083
+ readerInterface,
14084
+ src
14085
+ });
14086
+ return {
14087
+ cues,
14088
+ onTrackEntrySegment,
14089
+ getTrackInfoByNumber: (id) => trackEntries[id],
14090
+ setTimestampOffset,
14091
+ getTimestampOffsetForByteOffset,
14092
+ getTimescale,
14093
+ setTimescale,
14094
+ addSegment: (seg) => {
14095
+ const segment = {
14096
+ ...seg,
14097
+ index: segments.length
14098
+ };
14099
+ segments.push(segment);
14100
+ },
14101
+ addCluster: (cluster) => {
14102
+ const exists = clusters.some((existingCluster) => existingCluster.start === cluster.start);
14103
+ if (!exists) {
14104
+ clusters.push(cluster);
13381
14105
  }
13382
- const selectedPlaylists = getSelectedPlaylists();
13383
- return selectedPlaylists.every((url) => allChunksProcessed[url]);
13384
14106
  },
13385
- setHasFinishedManifest: () => {
13386
- hasFinishedManifest = true;
14107
+ getFirstCluster: () => {
14108
+ return clusters.find((cluster) => cluster.segment === 0);
13387
14109
  },
13388
- hasFinishedManifest: () => hasFinishedManifest,
13389
- setM3uStreamRun: (playlistUrl, run) => {
13390
- if (!run) {
13391
- delete m3uStreamRuns[playlistUrl];
13392
- return;
14110
+ isInsideSegment: (iterator) => {
14111
+ const offset = iterator.counter.getOffset();
14112
+ const insideClusters = segments.filter((cluster) => {
14113
+ return offset >= cluster.start && offset <= cluster.start + cluster.size;
14114
+ });
14115
+ if (insideClusters.length > 1) {
14116
+ throw new Error("Expected to only be inside 1 cluster");
13393
14117
  }
13394
- m3uStreamRuns[playlistUrl] = run;
13395
- },
13396
- setTracksDone: (playlistUrl) => {
13397
- tracksDone[playlistUrl] = true;
13398
- const selectedPlaylists = getSelectedPlaylists();
13399
- return selectedPlaylists.every((url) => tracksDone[url]);
13400
- },
13401
- getTrackDone: (playlistUrl) => {
13402
- return tracksDone[playlistUrl];
14118
+ return insideClusters[0] ?? null;
13403
14119
  },
13404
- getM3uStreamRun: (playlistUrl) => m3uStreamRuns[playlistUrl] ?? null,
13405
- abortM3UStreamRuns: () => {
13406
- const values = Object.values(m3uStreamRuns);
13407
- if (values.length === 0) {
13408
- return;
14120
+ isInsideCluster: (offset) => {
14121
+ for (const cluster of clusters) {
14122
+ if (offset >= cluster.start && offset <= cluster.start + cluster.size) {
14123
+ return cluster;
14124
+ }
13409
14125
  }
13410
- Log.trace(logLevel, `Aborting ${values.length} M3U stream runs`);
13411
- values.forEach((run) => {
13412
- run.abort();
13413
- });
13414
- },
13415
- setAssociatedPlaylists: (playlists) => {
13416
- associatedPlaylists = playlists;
14126
+ return null;
13417
14127
  },
13418
- getAssociatedPlaylists: () => associatedPlaylists,
13419
- getSelectedPlaylists,
13420
- sampleSorter: sampleSorter({ logLevel, getAllChunksProcessedForPlaylist }),
13421
- setMp4HeaderSegment,
13422
- getMp4HeaderSegment
14128
+ setAvcProfileForTrackNumber,
14129
+ getAvcProfileForTrackNumber
13423
14130
  };
13424
14131
  };
13425
14132
 
@@ -13550,19 +14257,19 @@ var sampleCallback = ({
13550
14257
  }
13551
14258
  }
13552
14259
  }
14260
+ if (videoSample.type === "key") {
14261
+ keyframes.addKeyframe({
14262
+ trackId,
14263
+ decodingTimeInSeconds: videoSample.dts / videoSample.timescale,
14264
+ positionInBytes: videoSample.offset,
14265
+ presentationTimeInSeconds: videoSample.cts / videoSample.timescale,
14266
+ sizeInBytes: videoSample.data.length
14267
+ });
14268
+ }
13553
14269
  if (needsToIterateOverSamples({
13554
14270
  fields,
13555
14271
  emittedFields
13556
14272
  })) {
13557
- if (fields.slowKeyframes && videoSample.type === "key") {
13558
- keyframes.addKeyframe({
13559
- trackId,
13560
- decodingTimeInSeconds: videoSample.dts / videoSample.timescale,
13561
- positionInBytes: videoSample.offset,
13562
- presentationTimeInSeconds: videoSample.cts / videoSample.timescale,
13563
- sizeInBytes: videoSample.data.length
13564
- });
13565
- }
13566
14273
  slowDurationAndFpsState.addVideoSample(videoSample);
13567
14274
  }
13568
14275
  },
@@ -13586,28 +14293,8 @@ var sampleCallback = ({
13586
14293
  };
13587
14294
  };
13588
14295
 
13589
- // src/state/seek-infinite-loop.ts
13590
- var seekInfiniteLoopDetectionState = () => {
13591
- let lastSeek = null;
13592
- return {
13593
- registerSeek: (byte) => {
13594
- if (!lastSeek || lastSeek.byte !== byte) {
13595
- lastSeek = { byte, numberOfTimes: 1 };
13596
- return;
13597
- }
13598
- lastSeek.numberOfTimes++;
13599
- if (lastSeek.numberOfTimes >= 10) {
13600
- throw new Error(`Seeking infinite loop detected: Seeked to byte 0x${byte.toString(16)} ${lastSeek.numberOfTimes} times in a row with no position change in the file. Check your usage of .seek().`);
13601
- }
13602
- },
13603
- reset: () => {
13604
- lastSeek = null;
13605
- }
13606
- };
13607
- };
13608
-
13609
- // src/state/slow-duration-fps.ts
13610
- var slowDurationAndFpsState = () => {
14296
+ // src/state/samples-observed/slow-duration-fps.ts
14297
+ var samplesObservedState = () => {
13611
14298
  let smallestVideoSample;
13612
14299
  let largestVideoSample;
13613
14300
  let smallestAudioSample;
@@ -13636,51 +14323,85 @@ var slowDurationAndFpsState = () => {
13636
14323
  }
13637
14324
  return Math.max(videoDuration ?? 0, audioDuration ?? 0);
13638
14325
  };
14326
+ const addVideoSample = (videoSample) => {
14327
+ videoSamples.set(videoSample.cts, videoSample.data.byteLength);
14328
+ const presentationTimeInSeconds = videoSample.cts / videoSample.timescale;
14329
+ if (largestVideoSample === undefined || presentationTimeInSeconds > largestVideoSample) {
14330
+ largestVideoSample = presentationTimeInSeconds;
14331
+ }
14332
+ if (smallestVideoSample === undefined || presentationTimeInSeconds < smallestVideoSample) {
14333
+ smallestVideoSample = presentationTimeInSeconds;
14334
+ }
14335
+ };
14336
+ const addAudioSample = (audioSample) => {
14337
+ audioSamples.set(audioSample.cts, audioSample.data.byteLength);
14338
+ const presentationTimeInSeconds = audioSample.cts / audioSample.timescale;
14339
+ if (largestAudioSample === undefined || presentationTimeInSeconds > largestAudioSample) {
14340
+ largestAudioSample = presentationTimeInSeconds;
14341
+ }
14342
+ if (smallestAudioSample === undefined || presentationTimeInSeconds < smallestAudioSample) {
14343
+ smallestAudioSample = presentationTimeInSeconds;
14344
+ }
14345
+ };
14346
+ const getFps2 = () => {
14347
+ const videoDuration = getSlowVideoDurationInSeconds() ?? 0;
14348
+ if (videoDuration === 0) {
14349
+ return 0;
14350
+ }
14351
+ return videoSamples.size / videoDuration;
14352
+ };
14353
+ const getSlowNumberOfFrames = () => videoSamples.size;
14354
+ const getAudioBitrate = () => {
14355
+ const audioDuration = getSlowDurationInSeconds();
14356
+ if (audioDuration === 0 || audioSamples.size === 0) {
14357
+ return null;
14358
+ }
14359
+ const audioSizesInBytes = Array.from(audioSamples.values()).reduce((acc, size) => acc + size, 0);
14360
+ return audioSizesInBytes * 8 / audioDuration;
14361
+ };
14362
+ const getVideoBitrate = () => {
14363
+ const videoDuration = getSlowDurationInSeconds();
14364
+ if (videoDuration === 0 || videoSamples.size === 0) {
14365
+ return null;
14366
+ }
14367
+ const videoSizesInBytes = Array.from(videoSamples.values()).reduce((acc, size) => acc + size, 0);
14368
+ return videoSizesInBytes * 8 / videoDuration;
14369
+ };
13639
14370
  return {
13640
- addVideoSample: (videoSample) => {
13641
- videoSamples.set(videoSample.cts, videoSample.data.byteLength);
13642
- const presentationTimeInSeconds = videoSample.cts / videoSample.timescale;
13643
- if (largestVideoSample === undefined || presentationTimeInSeconds > largestVideoSample) {
13644
- largestVideoSample = presentationTimeInSeconds;
13645
- }
13646
- if (smallestVideoSample === undefined || presentationTimeInSeconds < smallestVideoSample) {
13647
- smallestVideoSample = presentationTimeInSeconds;
13648
- }
13649
- },
13650
- addAudioSample: (audioSample) => {
13651
- audioSamples.set(audioSample.cts, audioSample.data.byteLength);
13652
- const presentationTimeInSeconds = audioSample.cts / audioSample.timescale;
13653
- if (largestAudioSample === undefined || presentationTimeInSeconds > largestAudioSample) {
13654
- largestAudioSample = presentationTimeInSeconds;
13655
- }
13656
- if (smallestAudioSample === undefined || presentationTimeInSeconds < smallestAudioSample) {
13657
- smallestAudioSample = presentationTimeInSeconds;
13658
- }
13659
- },
14371
+ addVideoSample,
14372
+ addAudioSample,
13660
14373
  getSlowDurationInSeconds,
13661
- getFps: () => {
13662
- const videoDuration = getSlowVideoDurationInSeconds() ?? 0;
13663
- if (videoDuration === 0) {
13664
- return 0;
14374
+ getFps: getFps2,
14375
+ getSlowNumberOfFrames,
14376
+ getAudioBitrate,
14377
+ getVideoBitrate
14378
+ };
14379
+ };
14380
+
14381
+ // src/state/seek-infinite-loop.ts
14382
+ var seekInfiniteLoopDetectionState = () => {
14383
+ let lastSeek = null;
14384
+ let firstSeekTime = null;
14385
+ return {
14386
+ registerSeek: (byte) => {
14387
+ const now = Date.now();
14388
+ if (!lastSeek || lastSeek.byte !== byte) {
14389
+ lastSeek = { byte, numberOfTimes: 1 };
14390
+ firstSeekTime = now;
14391
+ return;
13665
14392
  }
13666
- return videoSamples.size / videoDuration;
13667
- },
13668
- getSlowNumberOfFrames: () => videoSamples.size,
13669
- getAudioBitrate: () => {
13670
- const audioDuration = getSlowDurationInSeconds();
13671
- if (audioDuration === 0 || audioSamples.size === 0) {
13672
- return null;
14393
+ lastSeek.numberOfTimes++;
14394
+ if (lastSeek.numberOfTimes >= 10 && firstSeekTime && now - firstSeekTime <= 2000) {
14395
+ throw new Error(`Seeking infinite loop detected: Seeked to byte 0x${byte.toString(16)} ${lastSeek.numberOfTimes} times in a row in the last 2 seconds. Check your usage of .seek().`);
13673
14396
  }
13674
- const audioSizesInBytes = Array.from(audioSamples.values()).reduce((acc, size) => acc + size, 0);
13675
- return audioSizesInBytes * 8 / audioDuration;
13676
- },
13677
- getVideoBitrate: () => {
13678
- const videoDuration = getSlowDurationInSeconds();
13679
- if (videoDuration === 0 || videoSamples.size === 0) {
13680
- return null;
14397
+ if (now - firstSeekTime > 2000) {
14398
+ lastSeek = { byte, numberOfTimes: 1 };
14399
+ firstSeekTime = now;
13681
14400
  }
13682
- const videoSizesInBytes = Array.from(videoSamples.values()).reduce((acc, size) => acc + size, 0);
13683
- return videoSizesInBytes * 8 / videoDuration;
14401
+ },
14402
+ reset: () => {
14403
+ lastSeek = null;
14404
+ firstSeekTime = null;
13684
14405
  }
13685
14406
  };
13686
14407
  };
@@ -13696,7 +14417,21 @@ var timingsState = () => {
13696
14417
  };
13697
14418
  };
13698
14419
 
13699
- // src/containers/transport-stream/next-pes-header-store.ts
14420
+ // src/state/transport-stream/last-emitted-sample.ts
14421
+ var lastEmittedSampleState = () => {
14422
+ let lastEmittedSample = null;
14423
+ return {
14424
+ setLastEmittedSample: (sample) => {
14425
+ lastEmittedSample = sample;
14426
+ },
14427
+ getLastEmittedSample: () => lastEmittedSample,
14428
+ resetLastEmittedSample: () => {
14429
+ lastEmittedSample = null;
14430
+ }
14431
+ };
14432
+ };
14433
+
14434
+ // src/state/transport-stream/next-pes-header-store.ts
13700
14435
  var makeNextPesHeaderStore = () => {
13701
14436
  let nextPesHeader = null;
13702
14437
  return {
@@ -13712,111 +14447,34 @@ var makeNextPesHeaderStore = () => {
13712
14447
  };
13713
14448
  };
13714
14449
 
13715
- // src/state/transport-stream.ts
13716
- var transportStreamState = () => {
14450
+ // src/state/transport-stream/pts-start-offset.ts
14451
+ var ptsStartOffsetStore = () => {
14452
+ const offsets = {};
13717
14453
  return {
13718
- nextPesHeaderStore: makeNextPesHeaderStore(),
13719
- streamBuffers: new Map
14454
+ getOffset: (trackId) => offsets[trackId] || 0,
14455
+ setOffset: (trackId, newOffset) => {
14456
+ offsets[trackId] = newOffset;
14457
+ }
13720
14458
  };
13721
14459
  };
13722
14460
 
13723
- // src/state/webm.ts
13724
- var webmState = () => {
13725
- const trackEntries = {};
13726
- const onTrackEntrySegment = (trackEntry2) => {
13727
- const trackId = getTrackId(trackEntry2);
13728
- if (!trackId) {
13729
- throw new Error("Expected track id");
13730
- }
13731
- if (trackEntries[trackId]) {
13732
- return;
13733
- }
13734
- const codec = getTrackCodec(trackEntry2);
13735
- if (!codec) {
13736
- throw new Error("Expected codec");
13737
- }
13738
- const trackTimescale = getTrackTimestampScale(trackEntry2);
13739
- trackEntries[trackId] = {
13740
- codec: codec.value,
13741
- trackTimescale: trackTimescale?.value ?? null
13742
- };
13743
- };
13744
- const timestampMap = new Map;
13745
- const getTimestampOffsetForByteOffset = (byteOffset) => {
13746
- const entries = Array.from(timestampMap.entries());
13747
- const sortedByByteOffset = entries.sort((a, b) => {
13748
- return a[0] - b[0];
13749
- }).reverse();
13750
- for (const [offset, timestamp] of sortedByByteOffset) {
13751
- if (offset >= byteOffset) {
13752
- continue;
13753
- }
13754
- return timestamp;
13755
- }
13756
- return timestampMap.get(byteOffset);
13757
- };
13758
- const setTimestampOffset = (byteOffset, timestamp) => {
13759
- timestampMap.set(byteOffset, timestamp);
13760
- };
13761
- let timescale = null;
13762
- const setTimescale = (newTimescale) => {
13763
- timescale = newTimescale;
13764
- };
13765
- const getTimescale = () => {
13766
- if (timescale === null) {
13767
- return 1e6;
13768
- }
13769
- return timescale;
13770
- };
13771
- const segments = [];
13772
- const clusters = [];
13773
- const avcProfilesMap = {};
13774
- const setAvcProfileForTrackNumber = (trackNumber2, avcProfile) => {
13775
- avcProfilesMap[trackNumber2] = avcProfile;
13776
- };
13777
- const getAvcProfileForTrackNumber = (trackNumber2) => {
13778
- return avcProfilesMap[trackNumber2] ?? null;
13779
- };
13780
- return {
13781
- onTrackEntrySegment,
13782
- getTrackInfoByNumber: (id) => trackEntries[id],
13783
- setTimestampOffset,
13784
- getTimestampOffsetForByteOffset,
13785
- timescale,
13786
- getTimescale,
13787
- setTimescale,
13788
- addSegment: (seg) => {
13789
- const segment = {
13790
- ...seg,
13791
- index: segments.length
13792
- };
13793
- segments.push(segment);
13794
- },
13795
- addCluster: (cluster) => {
13796
- clusters.push(cluster);
13797
- },
13798
- isInsideSegment: (iterator) => {
13799
- const offset = iterator.counter.getOffset();
13800
- const insideClusters = segments.filter((cluster) => {
13801
- return offset >= cluster.start && offset <= cluster.start + cluster.size;
13802
- });
13803
- if (insideClusters.length > 1) {
13804
- throw new Error("Expected to only be inside 1 cluster");
13805
- }
13806
- return insideClusters[0] ?? null;
13807
- },
13808
- isInsideCluster: (iterator) => {
13809
- for (const cluster of clusters) {
13810
- const offset = iterator.counter.getOffset();
13811
- if (offset >= cluster.start && offset <= cluster.start + cluster.size) {
13812
- return cluster;
13813
- }
13814
- }
13815
- return null;
14461
+ // src/state/transport-stream/transport-stream.ts
14462
+ var transportStreamState = () => {
14463
+ const streamBuffers = new Map;
14464
+ const startOffset = ptsStartOffsetStore();
14465
+ const lastEmittedSample = lastEmittedSampleState();
14466
+ const state = {
14467
+ nextPesHeaderStore: makeNextPesHeaderStore(),
14468
+ observedPesHeaders: makeObservedPesHeader(),
14469
+ streamBuffers,
14470
+ startOffset,
14471
+ resetBeforeSeek: () => {
14472
+ state.streamBuffers.clear();
14473
+ state.nextPesHeaderStore = makeNextPesHeaderStore();
13816
14474
  },
13817
- setAvcProfileForTrackNumber,
13818
- getAvcProfileForTrackNumber
14475
+ lastEmittedSample
13819
14476
  };
14477
+ return state;
13820
14478
  };
13821
14479
 
13822
14480
  // src/state/parser-state.ts
@@ -13840,7 +14498,8 @@ var makeParserState = ({
13840
14498
  callbacks,
13841
14499
  fieldsInReturnValue,
13842
14500
  mimeType,
13843
- initialReaderInstance
14501
+ initialReaderInstance,
14502
+ makeSamplesStartAtZero
13844
14503
  }) => {
13845
14504
  let skippedBytes = 0;
13846
14505
  const returnValue = {};
@@ -13851,7 +14510,7 @@ var makeParserState = ({
13851
14510
  const structure = structureState();
13852
14511
  const keyframes = keyframesState();
13853
14512
  const emittedFields = emittedState();
13854
- const slowDurationAndFps = slowDurationAndFpsState();
14513
+ const slowDurationAndFps = samplesObservedState();
13855
14514
  const mp3Info = makeMp3State();
13856
14515
  const images = imagesState();
13857
14516
  const timings = timingsState();
@@ -13874,7 +14533,7 @@ var makeParserState = ({
13874
14533
  return {
13875
14534
  riff: riffSpecificState(),
13876
14535
  transportStream: transportStreamState(),
13877
- webm: webmState(),
14536
+ webm: webmState({ controller, logLevel, readerInterface, src }),
13878
14537
  iso: isoBaseMediaState({
13879
14538
  contentLength,
13880
14539
  controller,
@@ -13934,7 +14593,8 @@ var makeParserState = ({
13934
14593
  mimeType,
13935
14594
  errored,
13936
14595
  currentReader: currentReaderState,
13937
- seekInfiniteLoop
14596
+ seekInfiniteLoop,
14597
+ makeSamplesStartAtZero
13938
14598
  };
13939
14599
  };
13940
14600
 
@@ -14016,6 +14676,7 @@ var internalParseMedia = async function({
14016
14676
  selectM3uStream: selectM3uStreamFn,
14017
14677
  selectM3uAssociatedPlaylists: selectM3uAssociatedPlaylistsFn,
14018
14678
  mp4HeaderSegment,
14679
+ makeSamplesStartAtZero,
14019
14680
  ...more
14020
14681
  }) {
14021
14682
  controller._internals.markAsReadyToEmitEvents();
@@ -14024,7 +14685,7 @@ var internalParseMedia = async function({
14024
14685
  logLevel,
14025
14686
  apiName
14026
14687
  });
14027
- Log.verbose(logLevel, `Reading ${typeof src === "string" ? src : src instanceof URL ? src.toString() : src.name}`);
14688
+ Log.verbose(logLevel, `Reading ${typeof src === "string" ? src : src instanceof URL ? src.toString() : src instanceof File ? src.name : src.toString()}`);
14028
14689
  const {
14029
14690
  reader: readerInstance,
14030
14691
  contentLength,
@@ -14061,7 +14722,8 @@ var internalParseMedia = async function({
14061
14722
  callbacks: more,
14062
14723
  fieldsInReturnValue: _fieldsInReturnValue ?? {},
14063
14724
  mimeType: contentType,
14064
- initialReaderInstance: readerInstance
14725
+ initialReaderInstance: readerInstance,
14726
+ makeSamplesStartAtZero
14065
14727
  });
14066
14728
  if (!hasAudioTrackHandlers && !hasVideoTrackHandlers && Object.values(state.fields).every((v) => !v) && mode === "query") {
14067
14729
  Log.warn(logLevel, new Error("Warning - No `fields` and no `on*` callbacks were passed to `parseMedia()`. Specify the data you would like to retrieve."));
@@ -14492,7 +15154,8 @@ var startParsing = async (message, reader) => {
14492
15154
  });
14493
15155
  };
14494
15156
  } : null,
14495
- onDiscardedData: null
15157
+ onDiscardedData: null,
15158
+ makeSamplesStartAtZero: true
14496
15159
  });
14497
15160
  post({
14498
15161
  type: "response-done",