@remotion/media-parser 4.0.290 → 4.0.291

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 (160) hide show
  1. package/dist/containers/iso-base-media/base-media-box.d.ts +0 -1
  2. package/dist/containers/iso-base-media/collect-sample-positions-from-moof-boxes.d.ts +4 -1
  3. package/dist/containers/iso-base-media/collect-sample-positions-from-moof-boxes.js +9 -5
  4. package/dist/containers/iso-base-media/find-keyframe-before-time.js +16 -11
  5. package/dist/containers/iso-base-media/find-track-to-seek.d.ts +14 -0
  6. package/dist/containers/iso-base-media/find-track-to-seek.js +39 -0
  7. package/dist/containers/iso-base-media/get-children.js +2 -2
  8. package/dist/containers/iso-base-media/get-keyframes.js +6 -1
  9. package/dist/containers/iso-base-media/get-mfra-seeking-box.d.ts +3 -1
  10. package/dist/containers/iso-base-media/get-mfra-seeking-box.js +5 -1
  11. package/dist/containers/iso-base-media/get-moov-atom.js +6 -3
  12. package/dist/containers/iso-base-media/get-sample-position-bounds.js +3 -1
  13. package/dist/containers/iso-base-media/get-sample-positions-from-track.js +1 -1
  14. package/dist/containers/iso-base-media/get-seeking-byte-from-fragmented-mp4.d.ts +14 -0
  15. package/dist/containers/iso-base-media/get-seeking-byte-from-fragmented-mp4.js +89 -0
  16. package/dist/containers/iso-base-media/get-seeking-byte.d.ts +3 -3
  17. package/dist/containers/iso-base-media/get-seeking-byte.js +32 -96
  18. package/dist/containers/iso-base-media/mdat/calculate-jump-marks.d.ts +6 -0
  19. package/dist/containers/iso-base-media/mdat/calculate-jump-marks.js +131 -0
  20. package/dist/containers/iso-base-media/mdat/mdat.d.ts +2 -2
  21. package/dist/containers/iso-base-media/mdat/mdat.js +18 -2
  22. package/dist/containers/iso-base-media/mfra/find-best-segment-from-tfra.d.ts +3 -3
  23. package/dist/containers/iso-base-media/mfra/find-best-segment-from-tfra.js +2 -2
  24. package/dist/containers/iso-base-media/mfra/get-mfra-atom.d.ts +5 -1
  25. package/dist/containers/iso-base-media/mfra/get-mfra-atom.js +3 -1
  26. package/dist/containers/iso-base-media/mfra/get-mfro-atom.d.ts +5 -1
  27. package/dist/containers/iso-base-media/mfra/get-mfro-atom.js +3 -1
  28. package/dist/containers/iso-base-media/parse-boxes.js +5 -2
  29. package/dist/containers/iso-base-media/process-box.d.ts +16 -5
  30. package/dist/containers/iso-base-media/process-box.js +206 -118
  31. package/dist/containers/iso-base-media/sample-positions.d.ts +25 -0
  32. package/dist/containers/iso-base-media/sample-positions.js +37 -0
  33. package/dist/containers/iso-base-media/stsd/samples.js +1 -0
  34. package/dist/containers/iso-base-media/stsd/stsc.d.ts +1 -6
  35. package/dist/containers/iso-base-media/stsd/stsc.js +2 -5
  36. package/dist/containers/iso-base-media/stsd/stss.d.ts +1 -1
  37. package/dist/containers/iso-base-media/stsd/stss.js +2 -2
  38. package/dist/containers/iso-base-media/turn-sample-positions-into-array.d.ts +19 -0
  39. package/dist/containers/iso-base-media/turn-sample-positions-into-array.js +73 -0
  40. package/dist/containers/m3u/after-manifest-fetch.d.ts +5 -1
  41. package/dist/containers/m3u/after-manifest-fetch.js +3 -1
  42. package/dist/containers/m3u/first-sample-in-m3u-chunk.d.ts +13 -0
  43. package/dist/containers/m3u/first-sample-in-m3u-chunk.js +31 -0
  44. package/dist/containers/m3u/get-seeking-byte.d.ts +13 -0
  45. package/dist/containers/m3u/get-seeking-byte.js +32 -0
  46. package/dist/containers/m3u/get-streams.d.ts +1 -0
  47. package/dist/containers/m3u/get-streams.js +1 -0
  48. package/dist/containers/m3u/iterate-over-segment-files.d.ts +5 -3
  49. package/dist/containers/m3u/iterate-over-segment-files.js +11 -1
  50. package/dist/containers/m3u/parse-m3u-media-directive.js +1 -0
  51. package/dist/containers/m3u/parse-m3u.js +8 -0
  52. package/dist/containers/m3u/process-m3u-chunk.d.ts +12 -0
  53. package/dist/containers/m3u/process-m3u-chunk.js +274 -0
  54. package/dist/containers/m3u/run-over-m3u.js +7 -80
  55. package/dist/containers/m3u/sample-sorter.d.ts +1 -0
  56. package/dist/containers/m3u/sample-sorter.js +4 -1
  57. package/dist/containers/m3u/seek/get-chunk-to-seek-to.d.ts +5 -0
  58. package/dist/containers/m3u/seek/get-chunk-to-seek-to.js +14 -0
  59. package/dist/containers/m3u/seeking-hints.d.ts +2 -0
  60. package/dist/containers/m3u/seeking-hints.js +9 -0
  61. package/dist/containers/m3u/select-stream.d.ts +2 -1
  62. package/dist/containers/m3u/select-stream.js +7 -2
  63. package/dist/containers/m3u/types.d.ts +1 -0
  64. package/dist/containers/riff/seek/fetch-idx1.d.ts +3 -1
  65. package/dist/containers/riff/seek/fetch-idx1.js +3 -1
  66. package/dist/containers/transport-stream/handle-aac-packet.d.ts +2 -2
  67. package/dist/containers/transport-stream/handle-avc-packet.d.ts +2 -2
  68. package/dist/containers/transport-stream/process-audio.d.ts +2 -2
  69. package/dist/containers/transport-stream/process-stream-buffers.d.ts +3 -3
  70. package/dist/containers/transport-stream/process-video.d.ts +2 -2
  71. package/dist/containers/webm/get-sample-from-block.d.ts +12 -2
  72. package/dist/containers/webm/get-sample-from-block.js +40 -9
  73. package/dist/containers/webm/parse-ebml.js +28 -10
  74. package/dist/containers/webm/seek/fetch-web-cues.d.ts +3 -1
  75. package/dist/containers/webm/seek/fetch-web-cues.js +3 -1
  76. package/dist/containers/webm/state-for-processing.d.ts +2 -2
  77. package/dist/controller/media-parser-controller.d.ts +1 -1
  78. package/dist/controller/media-parser-controller.js +6 -2
  79. package/dist/controller/seek-signal.d.ts +1 -5
  80. package/dist/download-and-parse-media.js +1 -1
  81. package/dist/esm/index.mjs +1400 -611
  82. package/dist/esm/node.mjs +23 -3
  83. package/dist/esm/server-worker.mjs +8 -1
  84. package/dist/esm/universal.mjs +168 -15
  85. package/dist/esm/web.mjs +145 -13
  86. package/dist/esm/worker-server-entry.mjs +1467 -635
  87. package/dist/esm/worker-web-entry.mjs +1439 -634
  88. package/dist/esm/worker.mjs +8 -1
  89. package/dist/get-audio-codec.js +3 -0
  90. package/dist/get-duration.js +2 -1
  91. package/dist/get-fps.js +2 -1
  92. package/dist/get-sample-positions-from-mp4.js +10 -5
  93. package/dist/get-sample-positions.js +4 -4
  94. package/dist/get-seeking-byte.d.ts +5 -3
  95. package/dist/get-seeking-byte.js +19 -10
  96. package/dist/get-seeking-hints.d.ts +3 -3
  97. package/dist/get-seeking-hints.js +18 -13
  98. package/dist/get-tracks.d.ts +9 -1
  99. package/dist/get-tracks.js +13 -6
  100. package/dist/index.d.ts +21 -5
  101. package/dist/init-video.js +3 -2
  102. package/dist/internal-parse-media.js +13 -4
  103. package/dist/iterator/buffer-iterator.js +5 -3
  104. package/dist/metadata/metadata-from-iso.js +2 -1
  105. package/dist/options.d.ts +6 -1
  106. package/dist/parse-loop.js +22 -6
  107. package/dist/parse-media-on-worker-entry.js +1 -0
  108. package/dist/parse-media.js +1 -1
  109. package/dist/parse-result.d.ts +2 -2
  110. package/dist/perform-seek.d.ts +3 -1
  111. package/dist/perform-seek.js +3 -1
  112. package/dist/readers/fetch/get-body-and-reader.js +17 -2
  113. package/dist/readers/from-fetch.d.ts +17 -1
  114. package/dist/readers/from-fetch.js +68 -13
  115. package/dist/readers/from-node.js +24 -2
  116. package/dist/readers/from-web-file.js +3 -0
  117. package/dist/readers/reader.d.ts +19 -2
  118. package/dist/readers/universal.js +9 -0
  119. package/dist/readers/web.js +6 -0
  120. package/dist/register-track.d.ts +3 -3
  121. package/dist/seek-backwards.d.ts +3 -1
  122. package/dist/seek-backwards.js +4 -1
  123. package/dist/seek-forwards.d.ts +3 -1
  124. package/dist/seek-forwards.js +3 -1
  125. package/dist/seeking-hints.d.ts +4 -1
  126. package/dist/set-seeking-hints.js +4 -0
  127. package/dist/skip.d.ts +5 -0
  128. package/dist/skip.js +6 -1
  129. package/dist/state/can-skip-tracks.d.ts +1 -0
  130. package/dist/state/can-skip-tracks.js +10 -6
  131. package/dist/state/iso-base-media/cached-sample-positions.d.ts +15 -1
  132. package/dist/state/iso-base-media/cached-sample-positions.js +9 -4
  133. package/dist/state/iso-base-media/iso-state.d.ts +5 -1
  134. package/dist/state/iso-base-media/iso-state.js +2 -1
  135. package/dist/state/iso-base-media/lazy-mfra-load.d.ts +3 -1
  136. package/dist/state/iso-base-media/lazy-mfra-load.js +2 -1
  137. package/dist/state/keyframes.js +1 -0
  138. package/dist/state/m3u-state.d.ts +15 -4
  139. package/dist/state/m3u-state.js +20 -0
  140. package/dist/state/matroska/lazy-cues-fetch.d.ts +3 -1
  141. package/dist/state/matroska/lazy-cues-fetch.js +2 -1
  142. package/dist/state/matroska/webm.d.ts +3 -1
  143. package/dist/state/matroska/webm.js +2 -1
  144. package/dist/state/parser-state.d.ts +29 -13
  145. package/dist/state/parser-state.js +19 -5
  146. package/dist/state/riff/lazy-idx1-fetch.d.ts +3 -1
  147. package/dist/state/riff/lazy-idx1-fetch.js +2 -1
  148. package/dist/state/riff.d.ts +3 -1
  149. package/dist/state/riff.js +2 -1
  150. package/dist/state/sample-callbacks.d.ts +3 -2
  151. package/dist/state/sample-callbacks.js +3 -3
  152. package/dist/version.d.ts +1 -1
  153. package/dist/version.js +1 -1
  154. package/dist/work-on-seek-request.d.ts +6 -3
  155. package/dist/work-on-seek-request.js +13 -13
  156. package/dist/worker/forward-controller-to-worker.js +1 -1
  157. package/dist/worker/serialize-error.js +26 -3
  158. package/dist/worker/worker-types.d.ts +7 -1
  159. package/dist/worker-server.js +2 -2
  160. package/package.json +3 -3
@@ -98,6 +98,40 @@ class MediaParserAbortError extends Error {
98
98
  }
99
99
  }
100
100
 
101
+ // src/log.ts
102
+ var logLevels = ["trace", "verbose", "info", "warn", "error"];
103
+ var getNumberForLogLevel = (level) => {
104
+ return logLevels.indexOf(level);
105
+ };
106
+ var isEqualOrBelowLogLevel = (currentLevel, level) => {
107
+ return getNumberForLogLevel(currentLevel) <= getNumberForLogLevel(level);
108
+ };
109
+ var Log = {
110
+ trace: (logLevel, ...args) => {
111
+ if (isEqualOrBelowLogLevel(logLevel, "trace")) {
112
+ return console.log(...args);
113
+ }
114
+ },
115
+ verbose: (logLevel, ...args) => {
116
+ if (isEqualOrBelowLogLevel(logLevel, "verbose")) {
117
+ return console.log(...args);
118
+ }
119
+ },
120
+ info: (logLevel, ...args) => {
121
+ if (isEqualOrBelowLogLevel(logLevel, "info")) {
122
+ return console.log(...args);
123
+ }
124
+ },
125
+ warn: (logLevel, ...args) => {
126
+ if (isEqualOrBelowLogLevel(logLevel, "warn")) {
127
+ return console.warn(...args);
128
+ }
129
+ },
130
+ error: (...args) => {
131
+ return console.error(...args);
132
+ }
133
+ };
134
+
101
135
  // src/readers/fetch/get-body-and-reader.ts
102
136
  var getLengthAndReader = async ({
103
137
  canLiveWithoutContentLength,
@@ -110,10 +144,22 @@ var getLengthAndReader = async ({
110
144
  if (requestedWithoutRange || canLiveWithoutContentLength && contentLength === null) {
111
145
  const buffer = await res.arrayBuffer();
112
146
  const encoded = new Uint8Array(buffer);
147
+ let streamCancelled = false;
113
148
  const stream = new ReadableStream({
114
149
  start(controller) {
115
- controller.enqueue(encoded);
116
- controller.close();
150
+ if (ownController.signal.aborted) {
151
+ return;
152
+ }
153
+ if (streamCancelled) {
154
+ return;
155
+ }
156
+ try {
157
+ controller.enqueue(encoded);
158
+ controller.close();
159
+ } catch {}
160
+ },
161
+ cancel() {
162
+ streamCancelled = true;
117
163
  }
118
164
  });
119
165
  return {
@@ -189,14 +235,11 @@ var validateContentRangeAndDetectIfSupported = ({
189
235
  }
190
236
  return { supportsContentRange: true };
191
237
  };
192
- var fetchReadContent = async ({
193
- src,
238
+ var makeFetchRequest = async ({
194
239
  range,
240
+ src,
195
241
  controller
196
242
  }) => {
197
- if (typeof src !== "string" && src instanceof URL === false) {
198
- throw new Error("src must be a string when using `fetchReader`");
199
- }
200
243
  const resolvedUrl = resolveUrl(src);
201
244
  const resolvedUrlString = resolvedUrl.toString();
202
245
  if (!resolvedUrlString.startsWith("https://") && !resolvedUrlString.startsWith("blob:") && !resolvedUrlString.startsWith("http://")) {
@@ -225,21 +268,83 @@ var fetchReadContent = async ({
225
268
  parsedContentRange,
226
269
  statusCode: res.status
227
270
  });
228
- controller._internals.signal.addEventListener("abort", () => {
229
- ownController.abort(new MediaParserAbortError("Aborted by user"));
230
- }, { once: true });
271
+ if (controller) {
272
+ controller._internals.signal.addEventListener("abort", () => {
273
+ ownController.abort(new MediaParserAbortError("Aborted by user"));
274
+ }, { once: true });
275
+ }
231
276
  if (res.status.toString().startsWith("4") || res.status.toString().startsWith("5")) {
232
- throw new Error(`Server returned status code ${res.status} for ${src} and range ${requestedRange}`);
277
+ throw new Error(`Server returned status code ${res.status} for ${resolvedUrl} and range ${requestedRange}`);
233
278
  }
234
279
  const contentDisposition = res.headers.get("content-disposition");
235
280
  const name = contentDisposition?.match(/filename="([^"]+)"/)?.[1];
236
- const fallbackName = src.toString().split("/").pop();
237
281
  const { contentLength, needsContentRange, reader } = await getLengthAndReader({
238
282
  canLiveWithoutContentLength,
239
283
  res,
240
284
  ownController,
241
285
  requestedWithoutRange: requestWithoutRange
242
286
  });
287
+ const contentType = res.headers.get("content-type");
288
+ return {
289
+ contentLength,
290
+ needsContentRange,
291
+ reader,
292
+ name,
293
+ contentType,
294
+ supportsContentRange
295
+ };
296
+ };
297
+ var cacheKey = ({
298
+ src,
299
+ range
300
+ }) => {
301
+ return `${src}-${JSON.stringify(range)}`;
302
+ };
303
+ var makeFetchRequestOrGetCached = ({
304
+ range,
305
+ src,
306
+ controller,
307
+ logLevel,
308
+ prefetchCache
309
+ }) => {
310
+ const key = cacheKey({ src, range });
311
+ const cached = prefetchCache.get(key);
312
+ if (cached) {
313
+ Log.verbose(logLevel, `Reading from preload cache for ${key}`);
314
+ return cached;
315
+ }
316
+ Log.verbose(logLevel, `Fetching ${key}`);
317
+ const result = makeFetchRequest({ range, src, controller });
318
+ prefetchCache.set(key, result);
319
+ return result;
320
+ };
321
+ var fetchReadContent = async ({
322
+ src,
323
+ range,
324
+ controller,
325
+ logLevel,
326
+ prefetchCache
327
+ }) => {
328
+ if (typeof src !== "string" && src instanceof URL === false) {
329
+ throw new Error("src must be a string when using `fetchReader`");
330
+ }
331
+ const fallbackName = src.toString().split("/").pop();
332
+ const {
333
+ reader,
334
+ contentLength,
335
+ needsContentRange,
336
+ name,
337
+ supportsContentRange,
338
+ contentType
339
+ } = await makeFetchRequestOrGetCached({
340
+ range,
341
+ src,
342
+ controller,
343
+ logLevel,
344
+ prefetchCache
345
+ });
346
+ const key = cacheKey({ src, range });
347
+ prefetchCache.delete(key);
243
348
  if (controller) {
244
349
  controller._internals.signal.addEventListener("abort", () => {
245
350
  reader.reader.cancel().catch(() => {});
@@ -248,12 +353,33 @@ var fetchReadContent = async ({
248
353
  return {
249
354
  reader,
250
355
  contentLength,
251
- contentType: res.headers.get("content-type"),
356
+ contentType,
252
357
  name: name ?? fallbackName,
253
358
  supportsContentRange,
254
359
  needsContentRange
255
360
  };
256
361
  };
362
+ var fetchPreload = ({
363
+ src,
364
+ range,
365
+ logLevel,
366
+ prefetchCache
367
+ }) => {
368
+ if (typeof src !== "string" && src instanceof URL === false) {
369
+ throw new Error("src must be a string when using `fetchReader`");
370
+ }
371
+ const key = cacheKey({ src, range });
372
+ if (prefetchCache.has(key)) {
373
+ return prefetchCache.get(key);
374
+ }
375
+ makeFetchRequestOrGetCached({
376
+ range,
377
+ src,
378
+ controller: null,
379
+ logLevel,
380
+ prefetchCache
381
+ });
382
+ };
257
383
  var fetchReadWholeAsText = async (src) => {
258
384
  if (typeof src !== "string" && src instanceof URL === false) {
259
385
  throw new Error("src must be a string when using `fetchReader`");
@@ -274,7 +400,6 @@ var fetchCreateAdjacentFileSource = (relativePath, src) => {
274
400
  // src/readers/from-node.ts
275
401
  import { createReadStream, promises, statSync } from "fs";
276
402
  import { dirname, join, relative, sep } from "path";
277
- import { Readable } from "stream";
278
403
  var nodeReadContent = ({ src, range, controller }) => {
279
404
  if (typeof src !== "string") {
280
405
  throw new Error("src must be a string when using `nodeReader`");
@@ -289,7 +414,27 @@ var nodeReadContent = ({ src, range, controller }) => {
289
414
  ownController.abort();
290
415
  }, { once: true });
291
416
  const stats = statSync(src);
292
- const reader = Readable.toWeb(stream).getReader();
417
+ let readerCancelled = false;
418
+ const reader = new ReadableStream({
419
+ start(c) {
420
+ if (readerCancelled) {
421
+ return;
422
+ }
423
+ stream.on("data", (chunk) => {
424
+ c.enqueue(chunk);
425
+ });
426
+ stream.on("end", () => {
427
+ c.close();
428
+ });
429
+ stream.on("error", (err) => {
430
+ c.error(err);
431
+ });
432
+ },
433
+ cancel() {
434
+ readerCancelled = true;
435
+ stream.destroy();
436
+ }
437
+ }).getReader();
293
438
  if (controller) {
294
439
  controller._internals.signal.addEventListener("abort", () => {
295
440
  reader.cancel().catch(() => {});
@@ -405,12 +550,21 @@ var universalReader = {
405
550
  return fetchCreateAdjacentFileSource(relativePath, src);
406
551
  }
407
552
  return nodeCreateAdjacentFileSource(relativePath, src);
553
+ },
554
+ preload: ({ src, range, logLevel, prefetchCache }) => {
555
+ if (src instanceof Blob) {
556
+ return;
557
+ }
558
+ if (src.toString().startsWith("http") || src.toString().startsWith("blob:")) {
559
+ return fetchPreload({ range, src, logLevel, prefetchCache });
560
+ }
408
561
  }
409
562
  };
410
563
  // src/containers/m3u/select-stream.ts
411
564
  var selectAssociatedPlaylists = async ({
412
565
  playlists,
413
- fn
566
+ fn,
567
+ skipAudioTracks
414
568
  }) => {
415
569
  if (playlists.length < 1) {
416
570
  return Promise.resolve([]);
@@ -419,12 +573,17 @@ var selectAssociatedPlaylists = async ({
419
573
  if (!Array.isArray(streams)) {
420
574
  throw new Error("Expected an array of associated playlists");
421
575
  }
576
+ const selectedStreams = [];
422
577
  for (const stream of streams) {
578
+ if (stream.isAudio && skipAudioTracks) {
579
+ continue;
580
+ }
423
581
  if (!playlists.find((playlist) => playlist.src === stream.src)) {
424
582
  throw new Error(`The associated playlist ${JSON.stringify(streams)} cannot be selected because it was not in the list of selectable playlists`);
425
583
  }
584
+ selectedStreams.push(stream);
426
585
  }
427
- return streams;
586
+ return selectedStreams;
428
587
  };
429
588
  var defaultSelectM3uAssociatedPlaylists = ({ associatedPlaylists }) => {
430
589
  if (associatedPlaylists.length === 1) {
@@ -596,7 +755,11 @@ var mediaParserController = () => {
596
755
  const performedSeeksSignal = performedSeeksStats();
597
756
  const checkForAbortAndPause = async () => {
598
757
  if (abortController.signal.aborted) {
599
- throw new MediaParserAbortError("Aborted");
758
+ const err = new MediaParserAbortError("Aborted");
759
+ if (abortController.signal.reason) {
760
+ err.cause = abortController.signal.reason;
761
+ }
762
+ throw err;
600
763
  }
601
764
  await pauseSignal.waitUntilResume();
602
765
  };
@@ -618,7 +781,7 @@ var mediaParserController = () => {
618
781
  abortController.abort(reason);
619
782
  emitter.dispatchAbort(reason);
620
783
  },
621
- _experimentalSeek: seekSignal.seek,
784
+ seek: seekSignal.seek,
622
785
  pause: pauseSignal.pause,
623
786
  resume: pauseSignal.resume,
624
787
  addEventListener: emitter.addEventListener,
@@ -672,7 +835,8 @@ var getM3uStreams = ({
672
835
  language: audioTrack.language,
673
836
  name: audioTrack.name,
674
837
  src: readerInterface.createAdjacentFileSource(audioTrack.uri, originalSrc),
675
- id: associatedPlaylists.length
838
+ id: associatedPlaylists.length,
839
+ isAudio: true
676
840
  });
677
841
  }
678
842
  }
@@ -1033,7 +1197,7 @@ var getFpsFromIsoMaseMedia = (state) => {
1033
1197
  const moovBox = getMoovBoxFromState({
1034
1198
  structureState: state.structure,
1035
1199
  isoState: state.iso,
1036
- mp4HeaderSegment: state.mp4HeaderSegment,
1200
+ mp4HeaderSegment: state.m3uPlaylistContext?.mp4HeaderSegment ?? null,
1037
1201
  mayUsePrecomputed: true
1038
1202
  });
1039
1203
  if (!moovBox) {
@@ -2192,40 +2356,6 @@ var detectFileType = (data) => {
2192
2356
  return { type: "unknown" };
2193
2357
  };
2194
2358
 
2195
- // src/log.ts
2196
- var logLevels = ["trace", "verbose", "info", "warn", "error"];
2197
- var getNumberForLogLevel = (level) => {
2198
- return logLevels.indexOf(level);
2199
- };
2200
- var isEqualOrBelowLogLevel = (currentLevel, level) => {
2201
- return getNumberForLogLevel(currentLevel) <= getNumberForLogLevel(level);
2202
- };
2203
- var Log = {
2204
- trace: (logLevel, ...args) => {
2205
- if (isEqualOrBelowLogLevel(logLevel, "trace")) {
2206
- return console.log(...args);
2207
- }
2208
- },
2209
- verbose: (logLevel, ...args) => {
2210
- if (isEqualOrBelowLogLevel(logLevel, "verbose")) {
2211
- return console.log(...args);
2212
- }
2213
- },
2214
- info: (logLevel, ...args) => {
2215
- if (isEqualOrBelowLogLevel(logLevel, "info")) {
2216
- return console.log(...args);
2217
- }
2218
- },
2219
- warn: (logLevel, ...args) => {
2220
- if (isEqualOrBelowLogLevel(logLevel, "warn")) {
2221
- return console.warn(...args);
2222
- }
2223
- },
2224
- error: (...args) => {
2225
- return console.error(...args);
2226
- }
2227
- };
2228
-
2229
2359
  // src/iterator/buffer-manager.ts
2230
2360
  var bufferManager = ({
2231
2361
  initialData,
@@ -2403,7 +2533,7 @@ var getArrayBufferIterator = (initialData, maxBytes) => {
2403
2533
  const six = getUint8();
2404
2534
  const seven = getUint8();
2405
2535
  const eight = getUint8();
2406
- return eight << 56 | seven << 48 | six << 40 | five << 32 | four << 24 | three << 16 | two << 8 | one;
2536
+ return (eight << 56 | seven << 48 | six << 40 | five << 32 | four << 24 | three << 16 | two << 8 | one) >>> 0;
2407
2537
  }
2408
2538
  function byteArrayToBigInt(byteArray) {
2409
2539
  let result = BigInt(0);
@@ -2425,7 +2555,8 @@ var getArrayBufferIterator = (initialData, maxBytes) => {
2425
2555
  return Number(bigInt);
2426
2556
  };
2427
2557
  const getFourByteNumber = () => {
2428
- return getUint8() << 24 | getUint8() << 16 | getUint8() << 8 | getUint8();
2558
+ const unsigned = getUint8() << 24 | getUint8() << 16 | getUint8() << 8 | getUint8();
2559
+ return unsigned >>> 0;
2429
2560
  };
2430
2561
  const getPaddedFourByteNumber = () => {
2431
2562
  let lastInt = 128;
@@ -4091,7 +4222,7 @@ var isoBaseMediaHasTracks = (state, mayUsePrecomputed) => {
4091
4222
  return Boolean(getMoovBoxFromState({
4092
4223
  structureState: state.structure,
4093
4224
  isoState: state.iso,
4094
- mp4HeaderSegment: state.mp4HeaderSegment,
4225
+ mp4HeaderSegment: state.m3uPlaylistContext?.mp4HeaderSegment ?? null,
4095
4226
  mayUsePrecomputed
4096
4227
  }));
4097
4228
  };
@@ -4176,11 +4307,16 @@ var getTracksFromMoovBox = (moovBox) => {
4176
4307
  otherTracks
4177
4308
  };
4178
4309
  };
4179
- var getTracksFromIsoBaseMedia = (state, mayUsePrecomputed) => {
4310
+ var getTracksFromIsoBaseMedia = ({
4311
+ mayUsePrecomputed,
4312
+ structure,
4313
+ isoState,
4314
+ m3uPlaylistContext
4315
+ }) => {
4180
4316
  const moovBox = getMoovBoxFromState({
4181
- structureState: state.structure,
4182
- isoState: state.iso,
4183
- mp4HeaderSegment: state.mp4HeaderSegment,
4317
+ structureState: structure,
4318
+ isoState,
4319
+ mp4HeaderSegment: m3uPlaylistContext?.mp4HeaderSegment ?? null,
4184
4320
  mayUsePrecomputed
4185
4321
  });
4186
4322
  if (!moovBox) {
@@ -4209,7 +4345,12 @@ var getTracks = (state, mayUsePrecomputed) => {
4209
4345
  return getCategorizedTracksFromMatroska(state);
4210
4346
  }
4211
4347
  if (structure.type === "iso-base-media") {
4212
- return getTracksFromIsoBaseMedia(state, mayUsePrecomputed);
4348
+ return getTracksFromIsoBaseMedia({
4349
+ isoState: state.iso,
4350
+ m3uPlaylistContext: state.m3uPlaylistContext,
4351
+ structure: state.structure,
4352
+ mayUsePrecomputed
4353
+ });
4213
4354
  }
4214
4355
  if (structure.type === "riff") {
4215
4356
  return getTracksFromAvi(structure, state);
@@ -4415,6 +4556,9 @@ var getAudioCodecFromAudioCodecInfo = (codec) => {
4415
4556
  if (codec.format === "ac-3") {
4416
4557
  return "ac3";
4417
4558
  }
4559
+ if (codec.format === "Opus") {
4560
+ return "opus";
4561
+ }
4418
4562
  if (codec.format === "mp4a") {
4419
4563
  if (codec.primarySpecificator === 64) {
4420
4564
  return "aac";
@@ -4597,11 +4741,15 @@ var collectSamplePositionsFromMoofBoxes = ({
4597
4741
  tkhdBox
4598
4742
  }) => {
4599
4743
  const isComplete = tfraBoxes.length > 0 && tfraBoxes.every((t) => t.entries.length === moofBoxes.length);
4600
- const samplePositions = moofBoxes.map((m) => {
4601
- return getSamplesFromMoof({
4602
- moofBox: m,
4603
- trackId: tkhdBox.trackId
4604
- });
4744
+ const samplePositions = moofBoxes.map((m, index) => {
4745
+ const isLastFragment = index === moofBoxes.length - 1 && isComplete;
4746
+ return {
4747
+ isLastFragment,
4748
+ samples: getSamplesFromMoof({
4749
+ moofBox: m,
4750
+ trackId: tkhdBox.trackId
4751
+ })
4752
+ };
4605
4753
  });
4606
4754
  return { samplePositions, isComplete };
4607
4755
  };
@@ -4634,14 +4782,14 @@ var getSamplePositions = ({
4634
4782
  const samples = [];
4635
4783
  let samplesPerChunk = 1;
4636
4784
  for (let i = 0;i < chunks.length; i++) {
4637
- const hasEntry = stscBox.entries.find((entry) => entry.firstChunk === i + 1);
4638
- if (hasEntry) {
4639
- samplesPerChunk = hasEntry.samplesPerChunk;
4785
+ const hasEntry = stscBox.entries.get(i + 1);
4786
+ if (hasEntry !== undefined) {
4787
+ samplesPerChunk = hasEntry;
4640
4788
  }
4641
4789
  let offsetInThisChunk = 0;
4642
4790
  for (let j = 0;j < samplesPerChunk; j++) {
4643
4791
  const size = stszBox.countType === "fixed" ? stszBox.sampleSize : stszBox.entries[samples.length];
4644
- const isKeyframe = stssBox ? stssBox.sampleNumber.includes(samples.length + 1) : true;
4792
+ const isKeyframe = stssBox ? stssBox.sampleNumber.has(samples.length + 1) : true;
4645
4793
  const delta = sttsDeltas[samples.length];
4646
4794
  const ctsOffset = cttsEntries[samples.length];
4647
4795
  const cts = dts + ctsOffset;
@@ -4685,11 +4833,16 @@ var getGroupedSamplesPositionsFromMp4 = ({
4685
4833
  }
4686
4834
  const samples = [];
4687
4835
  let timestamp = 0;
4836
+ const stscKeys = Array.from(stscBox.entries.keys());
4688
4837
  for (let i = 0;i < stcoBox.entries.length; i++) {
4689
4838
  const entry = stcoBox.entries[i];
4690
4839
  const chunk = i + 1;
4691
- const stscEntry = stscBox.entries.findLast((e) => e.firstChunk <= chunk);
4692
- if (!stscEntry) {
4840
+ const stscEntry = stscKeys.findLast((e) => e <= chunk);
4841
+ if (stscEntry === undefined) {
4842
+ throw new Error("should not be");
4843
+ }
4844
+ const samplesPerChunk = stscBox.entries.get(stscEntry);
4845
+ if (samplesPerChunk === undefined) {
4693
4846
  throw new Error("should not be");
4694
4847
  }
4695
4848
  samples.push({
@@ -4697,13 +4850,13 @@ var getGroupedSamplesPositionsFromMp4 = ({
4697
4850
  cts: timestamp,
4698
4851
  dts: timestamp,
4699
4852
  offset: Number(entry),
4700
- size: stszBox.sampleSize * stscEntry.samplesPerChunk,
4701
- duration: stscEntry.samplesPerChunk,
4853
+ size: stszBox.sampleSize * samplesPerChunk,
4854
+ duration: samplesPerChunk,
4702
4855
  isKeyframe: true,
4703
4856
  bigEndian,
4704
4857
  chunkSize: stszBox.sampleSize
4705
4858
  });
4706
- timestamp += stscEntry.samplesPerChunk;
4859
+ timestamp += samplesPerChunk;
4707
4860
  }
4708
4861
  return samples;
4709
4862
  };
@@ -4780,7 +4933,7 @@ var getSamplePositionsFromTrack = ({
4780
4933
  tkhdBox
4781
4934
  });
4782
4935
  return {
4783
- samplePositions: samplePositions.flat(1),
4936
+ samplePositions: samplePositions.map((s) => s.samples).flat(1),
4784
4937
  isComplete
4785
4938
  };
4786
4939
  }
@@ -5014,7 +5167,7 @@ var getDurationFromIsoBaseMedia = (parserState) => {
5014
5167
  const moovBox = getMoovBoxFromState({
5015
5168
  structureState: parserState.structure,
5016
5169
  isoState: parserState.iso,
5017
- mp4HeaderSegment: parserState.mp4HeaderSegment,
5170
+ mp4HeaderSegment: parserState.m3uPlaylistContext?.mp4HeaderSegment ?? null,
5018
5171
  mayUsePrecomputed: true
5019
5172
  });
5020
5173
  if (!moovBox) {
@@ -5120,7 +5273,12 @@ var hasHdr = (state) => {
5120
5273
 
5121
5274
  // src/containers/iso-base-media/get-keyframes.ts
5122
5275
  var getKeyframesFromIsoBaseMedia = (state) => {
5123
- const { videoTracks } = getTracksFromIsoBaseMedia(state, true);
5276
+ const { videoTracks } = getTracksFromIsoBaseMedia({
5277
+ isoState: state.iso,
5278
+ m3uPlaylistContext: state.m3uPlaylistContext,
5279
+ structure: state.structure,
5280
+ mayUsePrecomputed: true
5281
+ });
5124
5282
  const structure = state.structure.getIsoStructure();
5125
5283
  const moofBoxes = getMoofBoxes(structure.boxes);
5126
5284
  const tfraBoxes = getTfraBoxes(structure);
@@ -5274,7 +5432,7 @@ var getMetadataFromIsoBase = (state) => {
5274
5432
  const moov = getMoovBoxFromState({
5275
5433
  structureState: state.structure,
5276
5434
  isoState: state.iso,
5277
- mp4HeaderSegment: state.mp4HeaderSegment,
5435
+ mp4HeaderSegment: state.m3uPlaylistContext?.mp4HeaderSegment ?? null,
5278
5436
  mayUsePrecomputed: true
5279
5437
  });
5280
5438
  if (!moov) {
@@ -5540,6 +5698,75 @@ var getSeekingByteForFlac = ({
5540
5698
  return null;
5541
5699
  };
5542
5700
 
5701
+ // src/containers/iso-base-media/find-keyframe-before-time.ts
5702
+ var findKeyframeBeforeTime = ({
5703
+ samplePositions,
5704
+ time,
5705
+ timescale,
5706
+ mediaSections,
5707
+ logLevel
5708
+ }) => {
5709
+ let videoByte = 0;
5710
+ let videoSample = null;
5711
+ for (const sample of samplePositions) {
5712
+ const ctsInSeconds = sample.cts / timescale;
5713
+ const dtsInSeconds = sample.dts / timescale;
5714
+ if (!sample.isKeyframe) {
5715
+ continue;
5716
+ }
5717
+ if (!(ctsInSeconds <= time || dtsInSeconds <= time)) {
5718
+ continue;
5719
+ }
5720
+ if (videoByte <= sample.offset) {
5721
+ videoByte = sample.offset;
5722
+ videoSample = sample;
5723
+ }
5724
+ }
5725
+ if (!videoSample) {
5726
+ throw new Error("No sample found");
5727
+ }
5728
+ const mediaSection = mediaSections.find((section) => videoSample.offset >= section.start && videoSample.offset < section.start + section.size);
5729
+ if (!mediaSection) {
5730
+ Log.trace(logLevel, "Found a sample, but the offset has not yet been marked as a video section yet. Not yet able to seek, but probably once we have started reading the next box.", videoSample);
5731
+ return null;
5732
+ }
5733
+ return videoSample.offset;
5734
+ };
5735
+
5736
+ // src/containers/iso-base-media/find-track-to-seek.ts
5737
+ var findAnyTrackWithSamplePositions = (allTracks, struc) => {
5738
+ for (const track of allTracks) {
5739
+ if (track.type === "video" || track.type === "audio") {
5740
+ const { samplePositions } = getSamplePositionsFromTrack({
5741
+ trakBox: track.trakBox,
5742
+ moofBoxes: getMoofBoxes(struc.boxes),
5743
+ tfraBoxes: getTfraBoxes(struc)
5744
+ });
5745
+ if (samplePositions.length === 0) {
5746
+ continue;
5747
+ }
5748
+ return { track, samplePositions };
5749
+ }
5750
+ }
5751
+ return null;
5752
+ };
5753
+ var findTrackToSeek = (allTracks, structure) => {
5754
+ const firstVideoTrack = allTracks.find((t) => t.type === "video");
5755
+ const struc = structure.getIsoStructure();
5756
+ if (!firstVideoTrack) {
5757
+ return findAnyTrackWithSamplePositions(allTracks, struc);
5758
+ }
5759
+ const { samplePositions } = getSamplePositionsFromTrack({
5760
+ trakBox: firstVideoTrack.trakBox,
5761
+ moofBoxes: getMoofBoxes(struc.boxes),
5762
+ tfraBoxes: getTfraBoxes(struc)
5763
+ });
5764
+ if (samplePositions.length === 0) {
5765
+ return findAnyTrackWithSamplePositions(allTracks, struc);
5766
+ }
5767
+ return { track: firstVideoTrack, samplePositions };
5768
+ };
5769
+
5543
5770
  // src/state/video-section.ts
5544
5771
  var isByteInMediaSection = ({
5545
5772
  position,
@@ -5608,42 +5835,13 @@ var mediaSectionState = () => {
5608
5835
  };
5609
5836
  };
5610
5837
 
5611
- // src/containers/iso-base-media/find-keyframe-before-time.ts
5612
- var findKeyframeBeforeTime = ({
5613
- samplePositions,
5614
- time,
5615
- timescale,
5616
- mediaSections,
5617
- logLevel
5618
- }) => {
5619
- let byte = 0;
5620
- let sam = null;
5621
- for (const sample of samplePositions) {
5622
- const ctsInSeconds = sample.cts / timescale;
5623
- const dtsInSeconds = sample.dts / timescale;
5624
- if ((ctsInSeconds <= time || dtsInSeconds <= time) && byte <= sample.offset && sample.isKeyframe) {
5625
- byte = sample.offset;
5626
- sam = sample;
5627
- }
5628
- }
5629
- if (!sam) {
5630
- throw new Error("No sample found");
5631
- }
5632
- const mediaSection = mediaSections.find((section) => sam.offset >= section.start && sam.offset < section.start + section.size);
5633
- if (!mediaSection) {
5634
- Log.trace(logLevel, "Found a sample, but the offset has not yet been marked as a video section yet. Not yet able to seek, but probably once we have started reading the next box.", sam);
5635
- return null;
5636
- }
5637
- return sam.offset;
5638
- };
5639
-
5640
5838
  // src/containers/iso-base-media/get-sample-position-bounds.ts
5641
5839
  var getSamplePositionBounds = (samplePositions, timescale) => {
5642
5840
  let min = Infinity;
5643
5841
  let max = -Infinity;
5644
5842
  for (const samplePosition of samplePositions) {
5645
5843
  const timestampMin = Math.min(samplePosition.cts, samplePosition.dts);
5646
- const timestampMax = Math.max(samplePosition.cts, samplePosition.dts);
5844
+ const timestampMax = Math.max(samplePosition.cts, samplePosition.dts) + (samplePosition.duration ?? 0);
5647
5845
  if (timestampMin < min) {
5648
5846
  min = timestampMin;
5649
5847
  }
@@ -5658,10 +5856,10 @@ var getSamplePositionBounds = (samplePositions, timescale) => {
5658
5856
  var findBestSegmentFromTfra = ({
5659
5857
  mfra,
5660
5858
  time,
5661
- firstVideoTrack,
5859
+ firstTrack,
5662
5860
  timescale
5663
5861
  }) => {
5664
- const tfra = mfra.find((b) => b.type === "tfra-box" && b.trackId === firstVideoTrack.trackId);
5862
+ const tfra = mfra.find((b) => b.type === "tfra-box" && b.trackId === firstTrack.trackId);
5665
5863
  if (!tfra) {
5666
5864
  return null;
5667
5865
  }
@@ -5682,130 +5880,191 @@ var findBestSegmentFromTfra = ({
5682
5880
  };
5683
5881
  };
5684
5882
 
5685
- // src/containers/iso-base-media/get-seeking-byte.ts
5686
- var getSeekingByteFromIsoBaseMedia = async ({
5883
+ // src/containers/iso-base-media/get-seeking-byte-from-fragmented-mp4.ts
5884
+ var getSeekingByteFromFragmentedMp4 = async ({
5687
5885
  info,
5688
5886
  time,
5689
5887
  logLevel,
5690
5888
  currentPosition,
5691
5889
  isoState,
5692
- mp4HeaderSegment,
5693
- structure
5890
+ allTracks,
5891
+ isLastChunkInPlaylist
5694
5892
  }) => {
5695
- const tracks2 = getTracksFromMoovBox(info.moovBox);
5696
- const allTracks = [
5697
- ...tracks2.videoTracks,
5698
- ...tracks2.audioTracks,
5699
- ...tracks2.otherTracks
5700
- ];
5701
- const hasMoov = Boolean(getMoovBoxFromState({
5702
- mp4HeaderSegment,
5703
- structureState: structure,
5704
- isoState,
5705
- mayUsePrecomputed: false
5706
- }));
5707
- if (!hasMoov) {
5708
- Log.trace(logLevel, "No moov box found, must wait");
5709
- return {
5710
- type: "valid-but-must-wait"
5711
- };
5712
- }
5713
5893
  const firstVideoTrack = allTracks.find((t) => t.type === "video");
5714
- if (!firstVideoTrack) {
5715
- throw new Error("No video track found");
5894
+ const firstTrack = firstVideoTrack ?? allTracks.find((t) => t.type === "audio");
5895
+ if (!firstTrack) {
5896
+ throw new Error("no video and no audio tracks");
5716
5897
  }
5717
- const { timescale } = firstVideoTrack;
5718
- if (info.moofBoxes.length > 0) {
5719
- const tkhdBox = getTkhdBox(firstVideoTrack.trakBox);
5720
- if (!tkhdBox) {
5721
- throw new Error("Expected tkhd box in trak box");
5722
- }
5723
- const { samplePositions: samplePositionsArray } = collectSamplePositionsFromMoofBoxes({
5724
- moofBoxes: info.moofBoxes,
5725
- tfraBoxes: info.tfraBoxes,
5726
- tkhdBox
5727
- });
5728
- Log.trace(logLevel, "Fragmented MP4 - Checking if we have seeking info for this time range");
5729
- for (const positions of samplePositionsArray) {
5730
- const { min, max } = getSamplePositionBounds(positions, timescale);
5731
- if (min <= time && time <= max) {
5732
- Log.trace(logLevel, `Fragmented MP4 - Found that we have seeking info for this time range: ${min} <= ${time} <= ${max}`);
5733
- const kf = findKeyframeBeforeTime({
5734
- samplePositions: positions,
5735
- time,
5736
- timescale,
5737
- logLevel,
5738
- mediaSections: info.mediaSections
5739
- });
5740
- if (kf) {
5741
- return {
5742
- type: "do-seek",
5743
- byte: kf
5744
- };
5745
- }
5746
- }
5747
- }
5748
- const atom = await (info.mfraAlreadyLoaded ? Promise.resolve(info.mfraAlreadyLoaded) : isoState.mfra.triggerLoad());
5749
- if (atom) {
5750
- const moofOffset = findBestSegmentFromTfra({
5751
- mfra: atom,
5898
+ const tkhdBox = getTkhdBox(firstTrack.trakBox);
5899
+ if (!tkhdBox) {
5900
+ throw new Error("Expected tkhd box in trak box");
5901
+ }
5902
+ const { samplePositions: samplePositionsArray } = collectSamplePositionsFromMoofBoxes({
5903
+ moofBoxes: info.moofBoxes,
5904
+ tfraBoxes: info.tfraBoxes,
5905
+ tkhdBox
5906
+ });
5907
+ Log.trace(logLevel, "Fragmented MP4 - Checking if we have seeking info for this time range");
5908
+ for (const positions of samplePositionsArray) {
5909
+ const { min, max } = getSamplePositionBounds(positions.samples, firstTrack.timescale);
5910
+ if (min <= time && (positions.isLastFragment || isLastChunkInPlaylist || time <= max)) {
5911
+ Log.trace(logLevel, `Fragmented MP4 - Found that we have seeking info for this time range: ${min} <= ${time} <= ${max}`);
5912
+ const kf = findKeyframeBeforeTime({
5913
+ samplePositions: positions.samples,
5752
5914
  time,
5753
- firstVideoTrack,
5754
- timescale
5915
+ timescale: firstTrack.timescale,
5916
+ logLevel,
5917
+ mediaSections: info.mediaSections
5755
5918
  });
5756
- if (moofOffset !== null && !(moofOffset.start <= currentPosition && currentPosition < moofOffset.end)) {
5757
- Log.verbose(logLevel, `Fragmented MP4 - Found based on mfra information that we should seek to: ${moofOffset.start} ${moofOffset.end}`);
5919
+ if (kf) {
5758
5920
  return {
5759
- type: "intermediary-seek",
5760
- byte: moofOffset.start
5921
+ type: "do-seek",
5922
+ byte: kf
5761
5923
  };
5762
5924
  }
5763
5925
  }
5764
- Log.trace(logLevel, "Fragmented MP4 - No seeking info found for this time range.");
5765
- if (isByteInMediaSection({
5766
- position: currentPosition,
5767
- mediaSections: info.mediaSections
5768
- }) !== "in-section") {
5926
+ }
5927
+ const atom = await (info.mfraAlreadyLoaded ? Promise.resolve(info.mfraAlreadyLoaded) : isoState.mfra.triggerLoad());
5928
+ if (atom) {
5929
+ const moofOffset = findBestSegmentFromTfra({
5930
+ mfra: atom,
5931
+ time,
5932
+ firstTrack,
5933
+ timescale: firstTrack.timescale
5934
+ });
5935
+ if (moofOffset !== null && !(moofOffset.start <= currentPosition && currentPosition < moofOffset.end)) {
5936
+ Log.verbose(logLevel, `Fragmented MP4 - Found based on mfra information that we should seek to: ${moofOffset.start} ${moofOffset.end}`);
5769
5937
  return {
5770
- type: "valid-but-must-wait"
5938
+ type: "intermediary-seek",
5939
+ byte: moofOffset.start
5771
5940
  };
5772
5941
  }
5773
- Log.trace(logLevel, "Fragmented MP4 - Inside the wrong video section, skipping to the end of the section");
5774
- const mediaSection = getCurrentMediaSection({
5775
- offset: currentPosition,
5776
- mediaSections: info.mediaSections
5777
- });
5778
- if (!mediaSection) {
5779
- throw new Error("No video section defined");
5780
- }
5942
+ }
5943
+ Log.trace(logLevel, "Fragmented MP4 - No seeking info found for this time range.");
5944
+ if (isByteInMediaSection({
5945
+ position: currentPosition,
5946
+ mediaSections: info.mediaSections
5947
+ }) !== "in-section") {
5781
5948
  return {
5782
- type: "intermediary-seek",
5783
- byte: mediaSection.start + mediaSection.size
5949
+ type: "valid-but-must-wait"
5784
5950
  };
5785
5951
  }
5786
- const { samplePositions, isComplete } = getSamplePositionsFromTrack({
5787
- trakBox: firstVideoTrack.trakBox,
5788
- moofBoxes: info.moofBoxes,
5789
- tfraBoxes: info.tfraBoxes
5952
+ Log.trace(logLevel, "Fragmented MP4 - Inside the wrong video section, skipping to the end of the section");
5953
+ const mediaSection = getCurrentMediaSection({
5954
+ offset: currentPosition,
5955
+ mediaSections: info.mediaSections
5790
5956
  });
5791
- if (!isComplete) {
5792
- throw new Error("Incomplete sample positions");
5957
+ if (!mediaSection) {
5958
+ throw new Error("No video section defined");
5793
5959
  }
5960
+ return {
5961
+ type: "intermediary-seek",
5962
+ byte: mediaSection.start + mediaSection.size
5963
+ };
5964
+ };
5965
+
5966
+ // src/containers/iso-base-media/get-seeking-byte.ts
5967
+ var getSeekingByteFromIsoBaseMedia = ({
5968
+ info,
5969
+ time,
5970
+ logLevel,
5971
+ currentPosition,
5972
+ isoState,
5973
+ m3uPlaylistContext,
5974
+ structure
5975
+ }) => {
5976
+ const tracks2 = getTracksFromIsoBaseMedia({
5977
+ isoState,
5978
+ m3uPlaylistContext,
5979
+ structure,
5980
+ mayUsePrecomputed: false
5981
+ });
5982
+ const allTracks = [
5983
+ ...tracks2.videoTracks,
5984
+ ...tracks2.audioTracks,
5985
+ ...tracks2.otherTracks
5986
+ ];
5987
+ const hasMoov = Boolean(getMoovBoxFromState({
5988
+ structureState: structure,
5989
+ isoState,
5990
+ mayUsePrecomputed: false,
5991
+ mp4HeaderSegment: m3uPlaylistContext?.mp4HeaderSegment ?? null
5992
+ }));
5993
+ if (!hasMoov) {
5994
+ Log.trace(logLevel, "No moov box found, must wait");
5995
+ return Promise.resolve({
5996
+ type: "valid-but-must-wait"
5997
+ });
5998
+ }
5999
+ if (info.moofBoxes.length > 0) {
6000
+ return getSeekingByteFromFragmentedMp4({
6001
+ info,
6002
+ time,
6003
+ logLevel,
6004
+ currentPosition,
6005
+ isoState,
6006
+ allTracks,
6007
+ isLastChunkInPlaylist: m3uPlaylistContext?.isLastChunkInPlaylist ?? false
6008
+ });
6009
+ }
6010
+ const trackWithSamplePositions = findTrackToSeek(allTracks, structure);
6011
+ if (!trackWithSamplePositions) {
6012
+ return Promise.resolve({
6013
+ type: "valid-but-must-wait"
6014
+ });
6015
+ }
6016
+ const { track, samplePositions } = trackWithSamplePositions;
5794
6017
  const keyframe = findKeyframeBeforeTime({
5795
6018
  samplePositions,
5796
6019
  time,
5797
- timescale,
6020
+ timescale: track.timescale,
5798
6021
  logLevel,
5799
6022
  mediaSections: info.mediaSections
5800
6023
  });
5801
6024
  if (keyframe) {
5802
- return {
6025
+ return Promise.resolve({
5803
6026
  type: "do-seek",
5804
6027
  byte: keyframe
5805
- };
6028
+ });
5806
6029
  }
5807
- return {
6030
+ return Promise.resolve({
5808
6031
  type: "invalid"
6032
+ });
6033
+ };
6034
+
6035
+ // src/containers/m3u/get-seeking-byte.ts
6036
+ var clearM3uStateInPrepareForSeek = ({
6037
+ m3uState,
6038
+ logLevel
6039
+ }) => {
6040
+ const selectedPlaylists = m3uState.getSelectedPlaylists();
6041
+ for (const playlistUrl of selectedPlaylists) {
6042
+ const streamRun = m3uState.getM3uStreamRun(playlistUrl);
6043
+ if (streamRun) {
6044
+ streamRun.abort();
6045
+ }
6046
+ Log.trace(logLevel, "Clearing M3U stream run for", playlistUrl);
6047
+ m3uState.setM3uStreamRun(playlistUrl, null);
6048
+ }
6049
+ m3uState.clearAllChunksProcessed();
6050
+ m3uState.sampleSorter.clearSamples();
6051
+ };
6052
+ var getSeekingByteForM3u8 = ({
6053
+ time,
6054
+ currentPosition,
6055
+ m3uState,
6056
+ logLevel
6057
+ }) => {
6058
+ clearM3uStateInPrepareForSeek({ m3uState, logLevel });
6059
+ const selectedPlaylists = m3uState.getSelectedPlaylists();
6060
+ for (const playlistUrl of selectedPlaylists) {
6061
+ m3uState.setSeekToSecondsToProcess(playlistUrl, {
6062
+ targetTime: time
6063
+ });
6064
+ }
6065
+ return {
6066
+ type: "do-seek",
6067
+ byte: currentPosition
5809
6068
  };
5810
6069
  };
5811
6070
 
@@ -6742,9 +7001,10 @@ var getSeekingByte = ({
6742
7001
  transportStream,
6743
7002
  webmState,
6744
7003
  mediaSection,
6745
- mp4HeaderSegment,
7004
+ m3uPlaylistContext,
6746
7005
  structure,
6747
- riffState
7006
+ riffState,
7007
+ m3uState
6748
7008
  }) => {
6749
7009
  if (info.type === "iso-base-media-seeking-hints") {
6750
7010
  return getSeekingByteFromIsoBaseMedia({
@@ -6753,8 +7013,8 @@ var getSeekingByte = ({
6753
7013
  logLevel,
6754
7014
  currentPosition,
6755
7015
  isoState,
6756
- mp4HeaderSegment,
6757
- structure
7016
+ structure,
7017
+ m3uPlaylistContext
6758
7018
  });
6759
7019
  }
6760
7020
  if (info.type === "wav-seeking-hints") {
@@ -6819,6 +7079,14 @@ var getSeekingByte = ({
6819
7079
  seekingHints: info
6820
7080
  }));
6821
7081
  }
7082
+ if (info.type === "m3u8-seeking-hints") {
7083
+ return Promise.resolve(getSeekingByteForM3u8({
7084
+ time,
7085
+ currentPosition,
7086
+ m3uState,
7087
+ logLevel
7088
+ }));
7089
+ }
6822
7090
  throw new Error(`Unknown seeking info type: ${info}`);
6823
7091
  };
6824
7092
 
@@ -6907,6 +7175,13 @@ var getSeekingHintsFromMp4 = ({
6907
7175
  };
6908
7176
  var setSeekingHintsForMp4 = ({}) => {};
6909
7177
 
7178
+ // src/containers/m3u/seeking-hints.ts
7179
+ var getSeekingHintsForM3u = () => {
7180
+ return {
7181
+ type: "m3u8-seeking-hints"
7182
+ };
7183
+ };
7184
+
6910
7185
  // src/containers/mp3/seeking-hints.ts
6911
7186
  var getSeekingHintsForMp3 = ({
6912
7187
  mp3State,
@@ -7053,7 +7328,7 @@ var setSeekingHintsForWebm = ({
7053
7328
  // src/get-seeking-hints.ts
7054
7329
  var getSeekingHints = ({
7055
7330
  structureState,
7056
- mp4HeaderSegment,
7331
+ m3uPlaylistContext,
7057
7332
  mediaSectionState: mediaSectionState2,
7058
7333
  isoState,
7059
7334
  transportStream,
@@ -7075,7 +7350,7 @@ var getSeekingHints = ({
7075
7350
  return getSeekingHintsFromMp4({
7076
7351
  structureState,
7077
7352
  isoState,
7078
- mp4HeaderSegment,
7353
+ mp4HeaderSegment: m3uPlaylistContext?.mp4HeaderSegment ?? null,
7079
7354
  mediaSectionState: mediaSectionState2
7080
7355
  });
7081
7356
  }
@@ -7118,7 +7393,10 @@ var getSeekingHints = ({
7118
7393
  samplesObserved
7119
7394
  });
7120
7395
  }
7121
- throw new Error(`Seeking is not supported for this format: ${structure.type}`);
7396
+ if (structure.type === "m3u") {
7397
+ return getSeekingHintsForM3u();
7398
+ }
7399
+ throw new Error(`Seeking is not supported for this format: ${structure}`);
7122
7400
  };
7123
7401
 
7124
7402
  // src/seek-backwards.ts
@@ -7129,10 +7407,12 @@ var seekBackwards = async ({
7129
7407
  src,
7130
7408
  controller,
7131
7409
  logLevel,
7132
- currentReader
7410
+ currentReader,
7411
+ prefetchCache
7133
7412
  }) => {
7134
7413
  const howManyBytesWeCanGoBack = iterator.counter.getDiscardedOffset();
7135
7414
  if (iterator.counter.getOffset() - howManyBytesWeCanGoBack <= seekTo) {
7415
+ Log.verbose(logLevel, `Seeking back to ${seekTo}`);
7136
7416
  iterator.skipTo(seekTo);
7137
7417
  return;
7138
7418
  }
@@ -7141,7 +7421,9 @@ var seekBackwards = async ({
7141
7421
  const { reader: newReader } = await readerInterface.read({
7142
7422
  src,
7143
7423
  range: seekTo,
7144
- controller
7424
+ controller,
7425
+ logLevel,
7426
+ prefetchCache
7145
7427
  });
7146
7428
  iterator.replaceData(new Uint8Array([]), seekTo);
7147
7429
  Log.verbose(logLevel, `Re-reading took ${Date.now() - time}ms. New position: ${iterator.counter.getOffset()}`);
@@ -7211,7 +7493,8 @@ var seekForward = async ({
7211
7493
  readerInterface,
7212
7494
  src,
7213
7495
  controller,
7214
- discardReadBytes
7496
+ discardReadBytes,
7497
+ prefetchCache
7215
7498
  }) => {
7216
7499
  if (userInitiated) {
7217
7500
  disallowForwardSeekIfSamplesAreNeeded({
@@ -7232,7 +7515,9 @@ var seekForward = async ({
7232
7515
  const { reader: newReader } = await readerInterface.read({
7233
7516
  src,
7234
7517
  range: seekTo,
7235
- controller
7518
+ controller,
7519
+ logLevel,
7520
+ prefetchCache
7236
7521
  });
7237
7522
  iterator.skipTo(seekTo);
7238
7523
  await discardReadBytes(true);
@@ -7255,7 +7540,8 @@ var performSeek = async ({
7255
7540
  readerInterface,
7256
7541
  src,
7257
7542
  discardReadBytes,
7258
- fields
7543
+ fields,
7544
+ prefetchCache
7259
7545
  }) => {
7260
7546
  const byteInMediaSection = isByteInMediaSection({
7261
7547
  position: seekTo,
@@ -7304,7 +7590,8 @@ var performSeek = async ({
7304
7590
  readerInterface,
7305
7591
  src,
7306
7592
  controller,
7307
- discardReadBytes
7593
+ discardReadBytes,
7594
+ prefetchCache
7308
7595
  });
7309
7596
  } else {
7310
7597
  await seekBackwards({
@@ -7314,7 +7601,8 @@ var performSeek = async ({
7314
7601
  logLevel,
7315
7602
  currentReader,
7316
7603
  readerInterface,
7317
- src
7604
+ src,
7605
+ prefetchCache
7318
7606
  });
7319
7607
  }
7320
7608
  await controller._internals.checkForAbortAndPause();
@@ -7327,7 +7615,7 @@ var turnSeekIntoByte = async ({
7327
7615
  logLevel,
7328
7616
  iterator,
7329
7617
  structureState,
7330
- mp4HeaderSegment,
7618
+ m3uPlaylistContext,
7331
7619
  isoState,
7332
7620
  transportStream,
7333
7621
  tracksState,
@@ -7338,7 +7626,8 @@ var turnSeekIntoByte = async ({
7338
7626
  riffState,
7339
7627
  mp3State,
7340
7628
  contentLength,
7341
- aacState
7629
+ aacState,
7630
+ m3uState
7342
7631
  }) => {
7343
7632
  const mediaSections = mediaSectionState2.getMediaSections();
7344
7633
  if (mediaSections.length === 0) {
@@ -7355,7 +7644,6 @@ var turnSeekIntoByte = async ({
7355
7644
  riffState,
7356
7645
  samplesObserved,
7357
7646
  structureState,
7358
- mp4HeaderSegment,
7359
7647
  mediaSectionState: mediaSectionState2,
7360
7648
  isoState,
7361
7649
  transportStream,
@@ -7365,7 +7653,8 @@ var turnSeekIntoByte = async ({
7365
7653
  flacState,
7366
7654
  mp3State,
7367
7655
  contentLength,
7368
- aacState
7656
+ aacState,
7657
+ m3uPlaylistContext
7369
7658
  });
7370
7659
  if (!seekingHints) {
7371
7660
  Log.trace(logLevel, "No seeking info, cannot seek yet");
@@ -7382,18 +7671,13 @@ var turnSeekIntoByte = async ({
7382
7671
  transportStream,
7383
7672
  webmState,
7384
7673
  mediaSection: mediaSectionState2,
7385
- mp4HeaderSegment,
7674
+ m3uPlaylistContext,
7386
7675
  structure: structureState,
7387
- riffState
7676
+ riffState,
7677
+ m3uState
7388
7678
  });
7389
7679
  return seekingByte;
7390
7680
  }
7391
- if (seek2.type === "byte") {
7392
- return {
7393
- type: "do-seek",
7394
- byte: seek2.byte
7395
- };
7396
- }
7397
7681
  throw new Error(`Cannot process seek request for ${seek2}: ${JSON.stringify(seek2)}`);
7398
7682
  };
7399
7683
  var getWorkOnSeekRequestOptions = (state) => {
@@ -7407,7 +7691,7 @@ var getWorkOnSeekRequestOptions = (state) => {
7407
7691
  contentLength: state.contentLength,
7408
7692
  readerInterface: state.readerInterface,
7409
7693
  mediaSection: state.mediaSection,
7410
- mp4HeaderSegment: state.mp4HeaderSegment,
7694
+ m3uPlaylistContext: state.m3uPlaylistContext,
7411
7695
  mode: state.mode,
7412
7696
  seekInfiniteLoop: state.seekInfiniteLoop,
7413
7697
  currentReader: state.currentReader,
@@ -7421,7 +7705,9 @@ var getWorkOnSeekRequestOptions = (state) => {
7421
7705
  samplesObserved: state.samplesObserved,
7422
7706
  riffState: state.riff,
7423
7707
  mp3State: state.mp3,
7424
- aacState: state.aac
7708
+ aacState: state.aac,
7709
+ m3uState: state.m3u,
7710
+ prefetchCache: state.prefetchCache
7425
7711
  };
7426
7712
  };
7427
7713
  var workOnSeekRequest = async (options) => {
@@ -7429,7 +7715,7 @@ var workOnSeekRequest = async (options) => {
7429
7715
  logLevel,
7430
7716
  controller,
7431
7717
  mediaSection,
7432
- mp4HeaderSegment,
7718
+ m3uPlaylistContext,
7433
7719
  isoState,
7434
7720
  iterator,
7435
7721
  structureState,
@@ -7449,20 +7735,22 @@ var workOnSeekRequest = async (options) => {
7449
7735
  samplesObserved,
7450
7736
  riffState,
7451
7737
  mp3State,
7452
- aacState
7738
+ aacState,
7739
+ prefetchCache,
7740
+ m3uState
7453
7741
  } = options;
7454
7742
  const seek2 = controller._internals.seekSignal.getSeek();
7455
7743
  if (!seek2) {
7456
7744
  return;
7457
7745
  }
7458
- Log.trace(logLevel, `Has seek request: ${JSON.stringify(seek2)}`);
7746
+ Log.trace(logLevel, `Has seek request for ${src}: ${JSON.stringify(seek2)}`);
7459
7747
  const resolution = await turnSeekIntoByte({
7460
7748
  seek: seek2,
7461
7749
  mediaSectionState: mediaSection,
7462
7750
  logLevel,
7463
7751
  iterator,
7464
7752
  structureState,
7465
- mp4HeaderSegment,
7753
+ m3uPlaylistContext,
7466
7754
  isoState,
7467
7755
  transportStream,
7468
7756
  tracksState,
@@ -7473,7 +7761,8 @@ var workOnSeekRequest = async (options) => {
7473
7761
  riffState,
7474
7762
  mp3State,
7475
7763
  contentLength,
7476
- aacState
7764
+ aacState,
7765
+ m3uState
7477
7766
  });
7478
7767
  Log.trace(logLevel, `Seek action: ${JSON.stringify(resolution)}`);
7479
7768
  if (resolution.type === "intermediary-seek") {
@@ -7491,7 +7780,8 @@ var workOnSeekRequest = async (options) => {
7491
7780
  readerInterface,
7492
7781
  src,
7493
7782
  discardReadBytes,
7494
- fields
7783
+ fields,
7784
+ prefetchCache
7495
7785
  });
7496
7786
  return;
7497
7787
  }
@@ -7510,7 +7800,8 @@ var workOnSeekRequest = async (options) => {
7510
7800
  readerInterface,
7511
7801
  src,
7512
7802
  discardReadBytes,
7513
- fields
7803
+ fields,
7804
+ prefetchCache
7514
7805
  });
7515
7806
  const { hasChanged } = controller._internals.seekSignal.clearSeekIfStillSame(seek2);
7516
7807
  if (hasChanged) {
@@ -8136,6 +8427,10 @@ var makeSkip = (skipTo) => ({
8136
8427
  action: "skip",
8137
8428
  skipTo
8138
8429
  });
8430
+ var makeFetchMoreData = (bytesNeeded) => ({
8431
+ action: "fetch-more-data",
8432
+ bytesNeeded
8433
+ });
8139
8434
 
8140
8435
  // src/containers/flac/get-block-size.ts
8141
8436
  var getBlockSize = (iterator) => {
@@ -8590,11 +8885,12 @@ var calculateFlatSamples = (state) => {
8590
8885
  samplePosition
8591
8886
  };
8592
8887
  });
8593
- }).flat(1);
8888
+ });
8594
8889
  return flatSamples;
8595
8890
  };
8596
8891
  var cachedSamplePositionsState = () => {
8597
8892
  const cachedForMdatStart = {};
8893
+ const jumpMarksForMdatStart = {};
8598
8894
  return {
8599
8895
  getSamples: (mdatStart) => {
8600
8896
  if (cachedForMdatStart[mdatStart]) {
@@ -8604,6 +8900,12 @@ var cachedSamplePositionsState = () => {
8604
8900
  },
8605
8901
  setSamples: (mdatStart, samples) => {
8606
8902
  cachedForMdatStart[mdatStart] = samples;
8903
+ },
8904
+ setJumpMarks: (mdatStart, marks) => {
8905
+ jumpMarksForMdatStart[mdatStart] = marks;
8906
+ },
8907
+ getJumpMarks: (mdatStart) => {
8908
+ return jumpMarksForMdatStart[mdatStart];
8607
8909
  }
8608
8910
  };
8609
8911
  };
@@ -8633,17 +8935,21 @@ var makeCanSkipTracksState = ({
8633
8935
  hasVideoTrackHandlers,
8634
8936
  structure
8635
8937
  }) => {
8938
+ const doFieldsNeedTracks = () => {
8939
+ const keys = Object.keys(fields ?? {});
8940
+ const selectedKeys = keys.filter((k) => fields[k]);
8941
+ return selectedKeys.some((k) => needsTracksForField({
8942
+ field: k,
8943
+ structure: structure.getStructureOrNull()
8944
+ }));
8945
+ };
8636
8946
  return {
8947
+ doFieldsNeedTracks,
8637
8948
  canSkipTracks: () => {
8638
8949
  if (hasAudioTrackHandlers || hasVideoTrackHandlers) {
8639
8950
  return false;
8640
8951
  }
8641
- const keys = Object.keys(fields ?? {});
8642
- const selectedKeys = keys.filter((k) => fields[k]);
8643
- return !selectedKeys.some((k) => needsTracksForField({
8644
- field: k,
8645
- structure: structure.getStructureOrNull()
8646
- }));
8952
+ return !doFieldsNeedTracks();
8647
8953
  }
8648
8954
  };
8649
8955
  };
@@ -8934,10 +9240,10 @@ var getIsoBaseMediaChildren = async ({
8934
9240
  onlyIfMdatAtomExpected: null,
8935
9241
  contentLength
8936
9242
  });
8937
- if (!parsed) {
9243
+ if (parsed.type !== "box") {
8938
9244
  throw new Error("Expected box");
8939
9245
  }
8940
- boxes.push(parsed);
9246
+ boxes.push(parsed.box);
8941
9247
  }
8942
9248
  if (iterator.counter.getOffset() > size + initial) {
8943
9249
  throw new Error(`read too many bytes - size: ${size}, read: ${iterator.counter.getOffset() - initial}. initial offset: ${initial}`);
@@ -9637,7 +9943,7 @@ var parseStsc = ({
9637
9943
  }
9638
9944
  const flags = iterator.getSlice(3);
9639
9945
  const entryCount = iterator.getUint32();
9640
- const entries = [];
9946
+ const entries = new Map;
9641
9947
  for (let i = 0;i < entryCount; i++) {
9642
9948
  const firstChunk = iterator.getUint32();
9643
9949
  const samplesPerChunk = iterator.getUint32();
@@ -9645,10 +9951,7 @@ var parseStsc = ({
9645
9951
  if (sampleDescriptionIndex !== 1) {
9646
9952
  throw new Error(`Expected sampleDescriptionIndex to be 1, but got ${sampleDescriptionIndex}`);
9647
9953
  }
9648
- entries.push({
9649
- firstChunk,
9650
- samplesPerChunk
9651
- });
9954
+ entries.set(firstChunk, samplesPerChunk);
9652
9955
  }
9653
9956
  return {
9654
9957
  type: "stsc-box",
@@ -9719,7 +10022,8 @@ var audioTags = [
9719
10022
  1836253269,
9720
10023
  ".mp3",
9721
10024
  "mp4a",
9722
- "ac-3"
10025
+ "ac-3",
10026
+ "Opus"
9723
10027
  ];
9724
10028
  var processIsoFormatBox = async ({
9725
10029
  iterator,
@@ -9992,9 +10296,9 @@ var parseStss = ({
9992
10296
  }
9993
10297
  const flags = iterator.getSlice(3);
9994
10298
  const sampleCount = iterator.getUint32();
9995
- const sampleNumber = [];
10299
+ const sampleNumber = new Set;
9996
10300
  for (let i = 0;i < sampleCount; i++) {
9997
- sampleNumber.push(iterator.getUint32());
10301
+ sampleNumber.add(iterator.getUint32());
9998
10302
  }
9999
10303
  const bytesRemainingInBox = boxSize - (iterator.counter.getOffset() - offset);
10000
10304
  if (bytesRemainingInBox > 0) {
@@ -10310,8 +10614,11 @@ var processBox = async ({
10310
10614
  const boxSizeRaw = iterator.getFourByteNumber();
10311
10615
  if (boxSizeRaw === 0) {
10312
10616
  return {
10313
- type: "void-box",
10314
- boxSize: 0
10617
+ type: "box",
10618
+ box: {
10619
+ type: "void-box",
10620
+ boxSize: 0
10621
+ }
10315
10622
  };
10316
10623
  }
10317
10624
  if (boxSizeRaw === 1 && iterator.bytesRemaining() < 12 || iterator.bytesRemaining() < 4) {
@@ -10325,120 +10632,177 @@ var processBox = async ({
10325
10632
  const headerLength = iterator.counter.getOffset() - startOff;
10326
10633
  if (boxType === "mdat") {
10327
10634
  if (!onlyIfMdatAtomExpected) {
10328
- return null;
10635
+ return { type: "nothing" };
10329
10636
  }
10330
10637
  const { mediaSectionState: mediaSectionState2 } = onlyIfMdatAtomExpected;
10331
10638
  mediaSectionState2.addMediaSection({
10332
10639
  size: boxSize - headerLength,
10333
10640
  start: iterator.counter.getOffset()
10334
10641
  });
10335
- return null;
10642
+ return { type: "nothing" };
10336
10643
  }
10337
10644
  if (bytesRemaining < boxSize) {
10338
10645
  returnToCheckpoint();
10339
- return null;
10646
+ return {
10647
+ type: "fetch-more-data",
10648
+ bytesNeeded: makeFetchMoreData(boxSize - bytesRemaining)
10649
+ };
10340
10650
  }
10341
10651
  if (boxType === "ftyp") {
10342
- return parseFtyp({ iterator, size: boxSize, offset: fileOffset });
10652
+ return {
10653
+ type: "box",
10654
+ box: await parseFtyp({ iterator, size: boxSize, offset: fileOffset })
10655
+ };
10343
10656
  }
10344
10657
  if (boxType === "colr") {
10345
- return parseColorParameterBox({
10346
- iterator,
10347
- size: boxSize
10348
- });
10658
+ return {
10659
+ type: "box",
10660
+ box: await parseColorParameterBox({
10661
+ iterator,
10662
+ size: boxSize
10663
+ })
10664
+ };
10349
10665
  }
10350
10666
  if (boxType === "mvhd") {
10351
- return parseMvhd({ iterator, offset: fileOffset, size: boxSize });
10667
+ return {
10668
+ type: "box",
10669
+ box: await parseMvhd({ iterator, offset: fileOffset, size: boxSize })
10670
+ };
10352
10671
  }
10353
10672
  if (boxType === "tkhd") {
10354
- return parseTkhd({ iterator, offset: fileOffset, size: boxSize });
10673
+ return {
10674
+ type: "box",
10675
+ box: await parseTkhd({ iterator, offset: fileOffset, size: boxSize })
10676
+ };
10355
10677
  }
10356
10678
  if (boxType === "trun") {
10357
- return parseTrun({ iterator, offset: fileOffset, size: boxSize });
10679
+ return {
10680
+ type: "box",
10681
+ box: await parseTrun({ iterator, offset: fileOffset, size: boxSize })
10682
+ };
10358
10683
  }
10359
10684
  if (boxType === "tfdt") {
10360
- return parseTfdt({ iterator, size: boxSize, offset: fileOffset });
10685
+ return {
10686
+ type: "box",
10687
+ box: await parseTfdt({ iterator, size: boxSize, offset: fileOffset })
10688
+ };
10361
10689
  }
10362
10690
  if (boxType === "stsd") {
10363
- return parseStsd({
10364
- offset: fileOffset,
10365
- size: boxSize,
10366
- iterator,
10367
- logLevel,
10368
- contentLength
10369
- });
10691
+ return {
10692
+ type: "box",
10693
+ box: await parseStsd({
10694
+ offset: fileOffset,
10695
+ size: boxSize,
10696
+ iterator,
10697
+ logLevel,
10698
+ contentLength
10699
+ })
10700
+ };
10370
10701
  }
10371
10702
  if (boxType === "stsz") {
10372
- return parseStsz({
10373
- iterator,
10374
- offset: fileOffset,
10375
- size: boxSize
10376
- });
10703
+ return {
10704
+ type: "box",
10705
+ box: await parseStsz({
10706
+ iterator,
10707
+ offset: fileOffset,
10708
+ size: boxSize
10709
+ })
10710
+ };
10377
10711
  }
10378
10712
  if (boxType === "stco" || boxType === "co64") {
10379
- return parseStco({
10380
- iterator,
10381
- offset: fileOffset,
10382
- size: boxSize,
10383
- mode64Bit: boxType === "co64"
10384
- });
10713
+ return {
10714
+ type: "box",
10715
+ box: await parseStco({
10716
+ iterator,
10717
+ offset: fileOffset,
10718
+ size: boxSize,
10719
+ mode64Bit: boxType === "co64"
10720
+ })
10721
+ };
10385
10722
  }
10386
10723
  if (boxType === "pasp") {
10387
- return parsePasp({
10388
- iterator,
10389
- offset: fileOffset,
10390
- size: boxSize
10391
- });
10724
+ return {
10725
+ type: "box",
10726
+ box: await parsePasp({
10727
+ iterator,
10728
+ offset: fileOffset,
10729
+ size: boxSize
10730
+ })
10731
+ };
10392
10732
  }
10393
10733
  if (boxType === "stss") {
10394
- return parseStss({
10395
- iterator,
10396
- offset: fileOffset,
10397
- boxSize
10398
- });
10734
+ return {
10735
+ type: "box",
10736
+ box: await parseStss({
10737
+ iterator,
10738
+ offset: fileOffset,
10739
+ boxSize
10740
+ })
10741
+ };
10399
10742
  }
10400
10743
  if (boxType === "ctts") {
10401
- return parseCtts({
10402
- iterator,
10403
- offset: fileOffset,
10404
- size: boxSize
10405
- });
10744
+ return {
10745
+ type: "box",
10746
+ box: await parseCtts({
10747
+ iterator,
10748
+ offset: fileOffset,
10749
+ size: boxSize
10750
+ })
10751
+ };
10406
10752
  }
10407
10753
  if (boxType === "stsc") {
10408
- return parseStsc({
10409
- iterator,
10410
- offset: fileOffset,
10411
- size: boxSize
10412
- });
10754
+ return {
10755
+ type: "box",
10756
+ box: await parseStsc({
10757
+ iterator,
10758
+ offset: fileOffset,
10759
+ size: boxSize
10760
+ })
10761
+ };
10413
10762
  }
10414
10763
  if (boxType === "mebx") {
10415
- return parseMebx({
10416
- offset: fileOffset,
10417
- size: boxSize,
10418
- iterator,
10419
- logLevel,
10420
- contentLength
10421
- });
10764
+ return {
10765
+ type: "box",
10766
+ box: await parseMebx({
10767
+ offset: fileOffset,
10768
+ size: boxSize,
10769
+ iterator,
10770
+ logLevel,
10771
+ contentLength
10772
+ })
10773
+ };
10422
10774
  }
10423
10775
  if (boxType === "hdlr") {
10424
- return parseHdlr({ iterator, size: boxSize, offset: fileOffset });
10776
+ return {
10777
+ type: "box",
10778
+ box: await parseHdlr({ iterator, size: boxSize, offset: fileOffset })
10779
+ };
10425
10780
  }
10426
10781
  if (boxType === "keys") {
10427
- return parseKeys({ iterator, size: boxSize, offset: fileOffset });
10782
+ return {
10783
+ type: "box",
10784
+ box: await parseKeys({ iterator, size: boxSize, offset: fileOffset })
10785
+ };
10428
10786
  }
10429
10787
  if (boxType === "ilst") {
10430
- return parseIlstBox({
10431
- iterator,
10432
- offset: fileOffset,
10433
- size: boxSize
10434
- });
10788
+ return {
10789
+ type: "box",
10790
+ box: await parseIlstBox({
10791
+ iterator,
10792
+ offset: fileOffset,
10793
+ size: boxSize
10794
+ })
10795
+ };
10435
10796
  }
10436
10797
  if (boxType === "tfra") {
10437
- return parseTfraBox({
10438
- iterator,
10439
- offset: fileOffset,
10440
- size: boxSize
10441
- });
10798
+ return {
10799
+ type: "box",
10800
+ box: await parseTfraBox({
10801
+ iterator,
10802
+ offset: fileOffset,
10803
+ size: boxSize
10804
+ })
10805
+ };
10442
10806
  }
10443
10807
  if (boxType === "moov") {
10444
10808
  if (!onlyIfMoovAtomExpected) {
@@ -10447,12 +10811,12 @@ var processBox = async ({
10447
10811
  const { tracks: tracks2, isoState } = onlyIfMoovAtomExpected;
10448
10812
  if (tracks2.hasAllTracks()) {
10449
10813
  iterator.discard(boxSize - 8);
10450
- return null;
10814
+ return { type: "nothing" };
10451
10815
  }
10452
10816
  if (isoState && isoState.moov.getMoovBoxAndPrecomputed() && !isoState.moov.getMoovBoxAndPrecomputed()?.precomputed) {
10453
10817
  Log.verbose(logLevel, "Moov box already parsed, skipping");
10454
10818
  iterator.discard(boxSize - 8);
10455
- return null;
10819
+ return { type: "nothing" };
10456
10820
  }
10457
10821
  const box = await parseMoov({
10458
10822
  offset: fileOffset,
@@ -10463,7 +10827,7 @@ var processBox = async ({
10463
10827
  contentLength
10464
10828
  });
10465
10829
  tracks2.setIsDone(logLevel);
10466
- return box;
10830
+ return { type: "box", box };
10467
10831
  }
10468
10832
  if (boxType === "trak") {
10469
10833
  if (!onlyIfMoovAtomExpected) {
@@ -10498,54 +10862,75 @@ var processBox = async ({
10498
10862
  onAudioTrack
10499
10863
  });
10500
10864
  }
10501
- return box;
10865
+ return { type: "box", box };
10502
10866
  }
10503
10867
  if (boxType === "stts") {
10504
- return parseStts({
10505
- data: iterator,
10506
- size: boxSize,
10507
- fileOffset
10508
- });
10868
+ return {
10869
+ type: "box",
10870
+ box: await parseStts({
10871
+ data: iterator,
10872
+ size: boxSize,
10873
+ fileOffset
10874
+ })
10875
+ };
10509
10876
  }
10510
10877
  if (boxType === "avcC") {
10511
- return parseAvcc({
10512
- data: iterator,
10513
- size: boxSize
10514
- });
10878
+ return {
10879
+ type: "box",
10880
+ box: await parseAvcc({
10881
+ data: iterator,
10882
+ size: boxSize
10883
+ })
10884
+ };
10515
10885
  }
10516
10886
  if (boxType === "av1C") {
10517
- return parseAv1C({
10518
- data: iterator,
10519
- size: boxSize
10520
- });
10887
+ return {
10888
+ type: "box",
10889
+ box: await parseAv1C({
10890
+ data: iterator,
10891
+ size: boxSize
10892
+ })
10893
+ };
10521
10894
  }
10522
10895
  if (boxType === "hvcC") {
10523
- return parseHvcc({
10524
- data: iterator,
10525
- size: boxSize,
10526
- offset: fileOffset
10527
- });
10896
+ return {
10897
+ type: "box",
10898
+ box: await parseHvcc({
10899
+ data: iterator,
10900
+ size: boxSize,
10901
+ offset: fileOffset
10902
+ })
10903
+ };
10528
10904
  }
10529
10905
  if (boxType === "tfhd") {
10530
- return getTfhd({
10531
- iterator,
10532
- offset: fileOffset,
10533
- size: boxSize
10534
- });
10906
+ return {
10907
+ type: "box",
10908
+ box: await getTfhd({
10909
+ iterator,
10910
+ offset: fileOffset,
10911
+ size: boxSize
10912
+ })
10913
+ };
10535
10914
  }
10536
10915
  if (boxType === "mdhd") {
10537
- return parseMdhd({
10538
- data: iterator,
10539
- size: boxSize,
10540
- fileOffset
10541
- });
10916
+ return {
10917
+ type: "box",
10918
+ box: await parseMdhd({
10919
+ data: iterator,
10920
+ size: boxSize,
10921
+ fileOffset
10922
+ })
10923
+ };
10542
10924
  }
10543
10925
  if (boxType === "esds") {
10544
- return parseEsds({
10545
- data: iterator,
10546
- size: boxSize,
10547
- fileOffset
10548
- });
10926
+ return {
10927
+ type: "box",
10928
+ box: await parseEsds({
10929
+ data: iterator,
10930
+ size: boxSize,
10931
+ fileOffset
10932
+ })
10933
+ };
10549
10934
  }
10550
10935
  if (boxType === "moof") {
10551
10936
  onlyIfMoovAtomExpected?.isoState?.mfra.triggerLoad();
@@ -10559,20 +10944,26 @@ var processBox = async ({
10559
10944
  contentLength
10560
10945
  });
10561
10946
  return {
10562
- type: "regular-box",
10563
- boxType,
10564
- boxSize,
10565
- children,
10566
- offset: fileOffset
10947
+ type: "box",
10948
+ box: {
10949
+ type: "regular-box",
10950
+ boxType,
10951
+ boxSize,
10952
+ children,
10953
+ offset: fileOffset
10954
+ }
10567
10955
  };
10568
10956
  }
10569
10957
  iterator.discard(boxSize - 8);
10570
10958
  return {
10571
- type: "regular-box",
10572
- boxType,
10573
- boxSize,
10574
- children: [],
10575
- offset: fileOffset
10959
+ type: "box",
10960
+ box: {
10961
+ type: "regular-box",
10962
+ boxType,
10963
+ boxSize,
10964
+ children: [],
10965
+ offset: fileOffset
10966
+ }
10576
10967
  };
10577
10968
  };
10578
10969
 
@@ -10581,7 +10972,7 @@ var getMoovAtom = async ({
10581
10972
  endOfMdat,
10582
10973
  state
10583
10974
  }) => {
10584
- const headerSegment = state.mp4HeaderSegment;
10975
+ const headerSegment = state.m3uPlaylistContext?.mp4HeaderSegment;
10585
10976
  if (headerSegment) {
10586
10977
  const segment = getMoovFromFromIsoStructure(headerSegment);
10587
10978
  if (!segment) {
@@ -10594,7 +10985,9 @@ var getMoovAtom = async ({
10594
10985
  const { reader } = await state.readerInterface.read({
10595
10986
  src: state.src,
10596
10987
  range: endOfMdat,
10597
- controller: state.controller
10988
+ controller: state.controller,
10989
+ logLevel: state.logLevel,
10990
+ prefetchCache: state.prefetchCache
10598
10991
  });
10599
10992
  const onAudioTrack = state.onAudioTrack ? async ({ track, container }) => {
10600
10993
  await registerAudioTrack({
@@ -10651,8 +11044,8 @@ var getMoovAtom = async ({
10651
11044
  onlyIfMdatAtomExpected: null,
10652
11045
  contentLength: state.contentLength - endOfMdat
10653
11046
  });
10654
- if (box) {
10655
- boxes.push(box);
11047
+ if (box.type === "box") {
11048
+ boxes.push(box.box);
10656
11049
  }
10657
11050
  if (iterator.counter.getOffset() + endOfMdat > state.contentLength) {
10658
11051
  throw new Error("Read past end of file");
@@ -10669,6 +11062,121 @@ var getMoovAtom = async ({
10669
11062
  return moov;
10670
11063
  };
10671
11064
 
11065
+ // src/containers/iso-base-media/mdat/calculate-jump-marks.ts
11066
+ var MAX_SPREAD_IN_SECONDS = 8;
11067
+ var getKey = (samplePositionTrack) => {
11068
+ return `${samplePositionTrack.track.trackId}-${samplePositionTrack.samplePosition.dts}`;
11069
+ };
11070
+ var findBestJump = ({
11071
+ allSamplesSortedByOffset,
11072
+ visited,
11073
+ progresses
11074
+ }) => {
11075
+ const minProgress = Math.min(...Object.values(progresses));
11076
+ const trackNumberWithLowestProgress = Object.entries(progresses).find(([, progress]) => progress === minProgress)?.[0];
11077
+ const firstSampleAboveMinProgress = allSamplesSortedByOffset.findIndex((sample) => sample.track.trackId === Number(trackNumberWithLowestProgress) && !visited.has(getKey(sample)));
11078
+ return firstSampleAboveMinProgress;
11079
+ };
11080
+ var calculateJumpMarks = (samplePositionTracks, endOfMdat) => {
11081
+ const progresses = {};
11082
+ for (const track of samplePositionTracks) {
11083
+ progresses[track[0].track.trackId] = 0;
11084
+ }
11085
+ const jumpMarks = [];
11086
+ const allSamplesSortedByOffset = samplePositionTracks.flat(1).sort((a, b) => a.samplePosition.offset - b.samplePosition.offset);
11087
+ let indexToVisit = 0;
11088
+ const visited = new Set;
11089
+ let rollOverToProcess = false;
11090
+ const increaseIndex = () => {
11091
+ indexToVisit++;
11092
+ if (indexToVisit >= allSamplesSortedByOffset.length) {
11093
+ rollOverToProcess = true;
11094
+ indexToVisit = 0;
11095
+ }
11096
+ };
11097
+ let lastVisitedSample = null;
11098
+ const addJumpMark = ({
11099
+ firstSampleAboveMinProgress
11100
+ }) => {
11101
+ if (!lastVisitedSample) {
11102
+ throw new Error("no last visited sample");
11103
+ }
11104
+ const jumpMark = {
11105
+ afterSampleWithOffset: lastVisitedSample.samplePosition.offset,
11106
+ jumpToOffset: allSamplesSortedByOffset[firstSampleAboveMinProgress].samplePosition.offset
11107
+ };
11108
+ indexToVisit = firstSampleAboveMinProgress;
11109
+ jumpMarks.push(jumpMark);
11110
+ };
11111
+ const addFinalJumpIfNecessary = () => {
11112
+ if (indexToVisit === allSamplesSortedByOffset.length - 1) {
11113
+ return;
11114
+ }
11115
+ jumpMarks.push({
11116
+ afterSampleWithOffset: allSamplesSortedByOffset[indexToVisit].samplePosition.offset,
11117
+ jumpToOffset: endOfMdat
11118
+ });
11119
+ };
11120
+ const considerJump = () => {
11121
+ const firstSampleAboveMinProgress = findBestJump({
11122
+ allSamplesSortedByOffset,
11123
+ visited,
11124
+ progresses
11125
+ });
11126
+ if (firstSampleAboveMinProgress > -1 && firstSampleAboveMinProgress !== indexToVisit + 1) {
11127
+ addJumpMark({ firstSampleAboveMinProgress });
11128
+ indexToVisit = firstSampleAboveMinProgress;
11129
+ } else {
11130
+ while (true) {
11131
+ increaseIndex();
11132
+ if (!visited.has(getKey(allSamplesSortedByOffset[indexToVisit]))) {
11133
+ break;
11134
+ }
11135
+ }
11136
+ }
11137
+ };
11138
+ while (true) {
11139
+ const currentSamplePosition = allSamplesSortedByOffset[indexToVisit];
11140
+ const sampleKey = getKey(currentSamplePosition);
11141
+ if (visited.has(sampleKey)) {
11142
+ considerJump();
11143
+ continue;
11144
+ }
11145
+ visited.add(sampleKey);
11146
+ if (rollOverToProcess) {
11147
+ if (!lastVisitedSample) {
11148
+ throw new Error("no last visited sample");
11149
+ }
11150
+ jumpMarks.push({
11151
+ afterSampleWithOffset: lastVisitedSample.samplePosition.offset,
11152
+ jumpToOffset: currentSamplePosition.samplePosition.offset
11153
+ });
11154
+ rollOverToProcess = false;
11155
+ }
11156
+ lastVisitedSample = currentSamplePosition;
11157
+ if (visited.size === allSamplesSortedByOffset.length) {
11158
+ addFinalJumpIfNecessary();
11159
+ break;
11160
+ }
11161
+ const timestamp = currentSamplePosition.samplePosition.dts / currentSamplePosition.track.timescale;
11162
+ progresses[currentSamplePosition.track.trackId] = timestamp;
11163
+ const progressValues = Object.values(progresses);
11164
+ const maxProgress = Math.max(...progressValues);
11165
+ const minProgress = Math.min(...progressValues);
11166
+ const spread = maxProgress - minProgress;
11167
+ if (visited.size === allSamplesSortedByOffset.length) {
11168
+ addFinalJumpIfNecessary();
11169
+ break;
11170
+ }
11171
+ if (spread > MAX_SPREAD_IN_SECONDS) {
11172
+ considerJump();
11173
+ } else {
11174
+ increaseIndex();
11175
+ }
11176
+ }
11177
+ return jumpMarks;
11178
+ };
11179
+
10672
11180
  // src/containers/iso-base-media/mdat/postprocess-bytes.ts
10673
11181
  var postprocessBytes = ({
10674
11182
  bytes,
@@ -10718,9 +11226,13 @@ var parseMdatSection = async (state) => {
10718
11226
  return parseMdatSection(state);
10719
11227
  }
10720
11228
  if (!state.iso.flatSamples.getSamples(mediaSection.start)) {
10721
- state.iso.flatSamples.setSamples(mediaSection.start, calculateFlatSamples(state));
11229
+ const flattedSamples = calculateFlatSamples(state);
11230
+ const calcedJumpMarks = calculateJumpMarks(flattedSamples, endOfMdat);
11231
+ state.iso.flatSamples.setJumpMarks(mediaSection.start, calcedJumpMarks);
11232
+ state.iso.flatSamples.setSamples(mediaSection.start, flattedSamples.flat(1));
10722
11233
  }
10723
11234
  const flatSamples = state.iso.flatSamples.getSamples(mediaSection.start);
11235
+ const jumpMarks = state.iso.flatSamples.getJumpMarks(mediaSection.start);
10724
11236
  const { iterator } = state;
10725
11237
  const samplesWithIndex = flatSamples.find((sample) => {
10726
11238
  return sample.samplePosition.offset === iterator.counter.getOffset();
@@ -10733,8 +11245,11 @@ var parseMdatSection = async (state) => {
10733
11245
  }
10734
11246
  return makeSkip(endOfMdat);
10735
11247
  }
11248
+ if (samplesWithIndex.samplePosition.offset + samplesWithIndex.samplePosition.size > state.contentLength) {
11249
+ return makeSkip(endOfMdat);
11250
+ }
10736
11251
  if (iterator.bytesRemaining() < samplesWithIndex.samplePosition.size) {
10737
- return null;
11252
+ return makeFetchMoreData(samplesWithIndex.samplePosition.size - iterator.bytesRemaining());
10738
11253
  }
10739
11254
  const { cts, dts, duration: duration2, isKeyframe, offset, bigEndian, chunkSize } = samplesWithIndex.samplePosition;
10740
11255
  const bytes = postprocessBytes({
@@ -10782,6 +11297,10 @@ var parseMdatSection = async (state) => {
10782
11297
  });
10783
11298
  await state.callbacks.onVideoSample(samplesWithIndex.track.trackId, videoSample);
10784
11299
  }
11300
+ const jump = jumpMarks.find((j) => j.afterSampleWithOffset === offset);
11301
+ if (jump) {
11302
+ return makeSkip(jump.jumpToOffset);
11303
+ }
10785
11304
  return null;
10786
11305
  };
10787
11306
 
@@ -10808,8 +11327,11 @@ var parseIsoBaseMedia = async (state) => {
10808
11327
  },
10809
11328
  contentLength: state.contentLength
10810
11329
  });
10811
- if (result) {
10812
- state.structure.getIsoStructure().boxes.push(result);
11330
+ if (result.type === "fetch-more-data") {
11331
+ return result.bytesNeeded;
11332
+ }
11333
+ if (result.type === "box") {
11334
+ state.structure.getIsoStructure().boxes.push(result.box);
10813
11335
  }
10814
11336
  return null;
10815
11337
  };
@@ -10888,7 +11410,8 @@ var parseM3uMediaDirective = (str) => {
10888
11410
  groupId: map["GROUP-ID"],
10889
11411
  language: map.LANGUAGE || null,
10890
11412
  name: map.NAME || null,
10891
- uri: map.URI
11413
+ uri: map.URI,
11414
+ mediaType: map.TYPE || null
10892
11415
  };
10893
11416
  };
10894
11417
 
@@ -11048,7 +11571,9 @@ var afterManifestFetch = async ({
11048
11571
  selectM3uStreamFn,
11049
11572
  logLevel,
11050
11573
  selectAssociatedPlaylistsFn,
11051
- readerInterface
11574
+ readerInterface,
11575
+ onAudioTrack,
11576
+ canSkipTracks
11052
11577
  }) => {
11053
11578
  const independentSegments = isIndependentSegments(structure);
11054
11579
  if (!independentSegments) {
@@ -11073,9 +11598,11 @@ var afterManifestFetch = async ({
11073
11598
  type: "selected-stream",
11074
11599
  stream: selectedPlaylist
11075
11600
  });
11601
+ const skipAudioTracks = onAudioTrack === null && canSkipTracks.doFieldsNeedTracks() === false;
11076
11602
  const associatedPlaylists = await selectAssociatedPlaylists({
11077
11603
  playlists: selectedPlaylist.associatedPlaylists,
11078
- fn: selectAssociatedPlaylistsFn
11604
+ fn: selectAssociatedPlaylistsFn,
11605
+ skipAudioTracks
11079
11606
  });
11080
11607
  m3uState.setAssociatedPlaylists(associatedPlaylists);
11081
11608
  const playlistUrls = [
@@ -11159,6 +11686,12 @@ var webReader = {
11159
11686
  return webFileReadWholeAsText(src);
11160
11687
  }
11161
11688
  return fetchReadWholeAsText(src);
11689
+ },
11690
+ preload: ({ range: range2, src, logLevel, prefetchCache }) => {
11691
+ if (src instanceof Blob) {
11692
+ return;
11693
+ }
11694
+ return fetchPreload({ range: range2, src, logLevel, prefetchCache });
11162
11695
  }
11163
11696
  };
11164
11697
  // src/parse-media.ts
@@ -11202,7 +11735,7 @@ var parseMedia = (options) => {
11202
11735
  controller: options.controller ?? undefined,
11203
11736
  selectM3uStream: options.selectM3uStream ?? defaultSelectM3uStreamFn,
11204
11737
  selectM3uAssociatedPlaylists: options.selectM3uAssociatedPlaylists ?? defaultSelectM3uAssociatedPlaylists,
11205
- mp4HeaderSegment: options.mp4HeaderSegment ?? null,
11738
+ m3uPlaylistContext: options.m3uPlaylistContext ?? null,
11206
11739
  src: options.src,
11207
11740
  mode: "query",
11208
11741
  onDiscardedData: null,
@@ -11214,6 +11747,39 @@ var parseMedia = (options) => {
11214
11747
  });
11215
11748
  };
11216
11749
 
11750
+ // src/containers/m3u/first-sample-in-m3u-chunk.ts
11751
+ var considerSeekBasedOnChunk = async ({
11752
+ sample,
11753
+ parentController,
11754
+ childController,
11755
+ callback,
11756
+ m3uState,
11757
+ playlistUrl,
11758
+ subtractChunks,
11759
+ chunkIndex
11760
+ }) => {
11761
+ const pendingSeek = m3uState.getSeekToSecondsToProcess(playlistUrl);
11762
+ if (pendingSeek === null) {
11763
+ await callback(sample);
11764
+ return;
11765
+ }
11766
+ const timestamp = Math.min(sample.dts / sample.timescale, sample.cts / sample.timescale);
11767
+ if (timestamp > pendingSeek.targetTime && chunkIndex !== null && chunkIndex > 0) {
11768
+ m3uState.setNextSeekShouldSubtractChunks(playlistUrl, subtractChunks + 1);
11769
+ parentController.seek({
11770
+ type: "keyframe-before-time",
11771
+ timeInSeconds: pendingSeek.targetTime
11772
+ });
11773
+ return;
11774
+ }
11775
+ childController.seek({
11776
+ type: "keyframe-before-time",
11777
+ timeInSeconds: pendingSeek.targetTime
11778
+ });
11779
+ m3uState.setNextSeekShouldSubtractChunks(playlistUrl, 0);
11780
+ m3uState.setSeekToSecondsToProcess(playlistUrl, null);
11781
+ };
11782
+
11217
11783
  // src/containers/m3u/get-chunks.ts
11218
11784
  var getChunks = (playlist) => {
11219
11785
  const chunks = [];
@@ -11231,127 +11797,282 @@ var getChunks = (playlist) => {
11231
11797
  }
11232
11798
  chunks.push({ duration: box.value, url: nextBox.value, isHeader: false });
11233
11799
  }
11234
- continue;
11800
+ continue;
11801
+ }
11802
+ return chunks;
11803
+ };
11804
+
11805
+ // src/containers/m3u/seek/get-chunk-to-seek-to.ts
11806
+ var getChunkToSeekTo = ({
11807
+ chunks,
11808
+ seekToSecondsToProcess
11809
+ }) => {
11810
+ let duration2 = 0;
11811
+ for (let i = 0;i < chunks.length; i++) {
11812
+ if (duration2 >= seekToSecondsToProcess) {
11813
+ return Math.max(0, i - 1);
11814
+ }
11815
+ duration2 += chunks[i].duration;
11235
11816
  }
11236
- return chunks;
11817
+ return Math.max(0, chunks.length - 1);
11237
11818
  };
11238
11819
 
11239
- // src/containers/m3u/iterate-over-segment-files.ts
11240
- var iteratorOverSegmentFiles = async ({
11241
- structure,
11242
- onVideoTrack,
11243
- m3uState,
11244
- onAudioTrack,
11245
- onDoneWithTracks,
11820
+ // src/containers/m3u/process-m3u-chunk.ts
11821
+ var processM3uChunk = ({
11246
11822
  playlistUrl,
11247
- logLevel,
11248
- parentController,
11249
- onInitialProgress,
11250
- readerInterface
11823
+ state,
11824
+ structure,
11825
+ audioDone,
11826
+ videoDone
11251
11827
  }) => {
11252
- const playlist = getPlaylist(structure, playlistUrl);
11253
- const chunks = getChunks(playlist);
11254
- let resolver = onInitialProgress;
11255
- let rejector = (_e) => {};
11256
- for (const chunk of chunks) {
11257
- resolver = onInitialProgress;
11258
- rejector = (_e) => {};
11259
- const childController = mediaParserController();
11260
- const forwarded = forwardMediaParserControllerPauseResume({
11261
- childController,
11262
- parentController
11828
+ const { promise, reject, resolve } = withResolvers();
11829
+ const onGlobalAudioTrack = audioDone ? null : async (track) => {
11830
+ const existingTracks = state.callbacks.tracks.getTracks();
11831
+ let { trackId } = track;
11832
+ while (existingTracks.find((t) => t.trackId === trackId)) {
11833
+ trackId++;
11834
+ }
11835
+ const onAudioSample = await registerAudioTrack({
11836
+ container: "m3u8",
11837
+ track: {
11838
+ ...track,
11839
+ trackId
11840
+ },
11841
+ registerAudioSampleCallback: state.callbacks.registerAudioSampleCallback,
11842
+ tracks: state.callbacks.tracks,
11843
+ logLevel: state.logLevel,
11844
+ onAudioTrack: state.onAudioTrack
11263
11845
  });
11264
- const makeContinuationFn = () => {
11265
- return {
11266
- continue() {
11267
- const { promise, reject, resolve } = withResolvers();
11268
- resolver = resolve;
11269
- rejector = reject;
11270
- childController.resume();
11271
- return promise;
11272
- },
11273
- abort() {
11274
- childController.abort();
11275
- }
11276
- };
11846
+ state.m3u.sampleSorter.addToStreamWithTrack(playlistUrl);
11847
+ if (onAudioSample === null) {
11848
+ return null;
11849
+ }
11850
+ state.m3u.sampleSorter.addAudioStreamToConsider(playlistUrl, onAudioSample);
11851
+ return async (sample) => {
11852
+ await state.m3u.sampleSorter.addAudioSample(playlistUrl, sample);
11277
11853
  };
11278
- const isLastChunk = chunk === chunks[chunks.length - 1];
11279
- await childController._internals.checkForAbortAndPause();
11280
- const src = readerInterface.createAdjacentFileSource(chunk.url, playlistUrl);
11281
- try {
11282
- const mp4HeaderSegment = m3uState.getMp4HeaderSegment(playlistUrl);
11283
- const data = await parseMedia({
11284
- src,
11285
- acknowledgeRemotionLicense: true,
11286
- logLevel,
11287
- controller: childController,
11288
- progressIntervalInMs: 0,
11289
- onParseProgress: () => {
11290
- childController.pause();
11291
- resolver(makeContinuationFn());
11292
- },
11293
- fields: chunk.isHeader ? { structure: true } : undefined,
11294
- onTracks: () => {
11295
- if (!m3uState.hasEmittedDoneWithTracks(playlistUrl)) {
11296
- m3uState.setHasEmittedDoneWithTracks(playlistUrl);
11297
- onDoneWithTracks();
11298
- return null;
11854
+ };
11855
+ const onGlobalVideoTrack = videoDone ? null : async (track) => {
11856
+ const existingTracks = state.callbacks.tracks.getTracks();
11857
+ let { trackId } = track;
11858
+ while (existingTracks.find((t) => t.trackId === trackId)) {
11859
+ trackId++;
11860
+ }
11861
+ const onVideoSample = await registerVideoTrack({
11862
+ container: "m3u8",
11863
+ track: {
11864
+ ...track,
11865
+ trackId
11866
+ },
11867
+ logLevel: state.logLevel,
11868
+ onVideoTrack: state.onVideoTrack,
11869
+ registerVideoSampleCallback: state.callbacks.registerVideoSampleCallback,
11870
+ tracks: state.callbacks.tracks
11871
+ });
11872
+ state.m3u.sampleSorter.addToStreamWithTrack(playlistUrl);
11873
+ if (onVideoSample === null) {
11874
+ return null;
11875
+ }
11876
+ state.m3u.sampleSorter.addVideoStreamToConsider(playlistUrl, onVideoSample);
11877
+ return async (sample) => {
11878
+ await state.m3u.sampleSorter.addVideoSample(playlistUrl, sample);
11879
+ };
11880
+ };
11881
+ const pausableIterator = async () => {
11882
+ const playlist = getPlaylist(structure, playlistUrl);
11883
+ const chunks = getChunks(playlist);
11884
+ const seekToSecondsToProcess = state.m3u.getSeekToSecondsToProcess(playlistUrl);
11885
+ const chunksToSubtract = state.m3u.getNextSeekShouldSubtractChunks(playlistUrl);
11886
+ let chunkIndex = null;
11887
+ if (seekToSecondsToProcess !== null) {
11888
+ chunkIndex = Math.max(0, getChunkToSeekTo({
11889
+ chunks,
11890
+ seekToSecondsToProcess: seekToSecondsToProcess.targetTime
11891
+ }) - chunksToSubtract);
11892
+ }
11893
+ const currentPromise = {
11894
+ resolver: () => {
11895
+ return;
11896
+ },
11897
+ rejector: reject
11898
+ };
11899
+ const requiresHeaderToBeFetched = chunks[0].isHeader;
11900
+ for (const chunk of chunks) {
11901
+ const mp4HeaderSegment = state.m3u.getMp4HeaderSegment(playlistUrl);
11902
+ if (requiresHeaderToBeFetched && mp4HeaderSegment && chunk.isHeader) {
11903
+ continue;
11904
+ }
11905
+ if (chunkIndex !== null && chunks.indexOf(chunk) < chunkIndex && !chunk.isHeader) {
11906
+ continue;
11907
+ }
11908
+ currentPromise.resolver = (newRun) => {
11909
+ state.m3u.setM3uStreamRun(playlistUrl, newRun);
11910
+ resolve();
11911
+ };
11912
+ currentPromise.rejector = reject;
11913
+ const childController = mediaParserController();
11914
+ const forwarded = forwardMediaParserControllerPauseResume({
11915
+ childController,
11916
+ parentController: state.controller
11917
+ });
11918
+ const nextChunk = chunks[chunks.indexOf(chunk) + 1];
11919
+ if (nextChunk) {
11920
+ const nextChunkSource = state.readerInterface.createAdjacentFileSource(nextChunk.url, playlistUrl);
11921
+ state.readerInterface.preload({
11922
+ logLevel: state.logLevel,
11923
+ range: null,
11924
+ src: nextChunkSource,
11925
+ prefetchCache: state.prefetchCache
11926
+ });
11927
+ }
11928
+ const makeContinuationFn = () => {
11929
+ return {
11930
+ continue() {
11931
+ const resolver = withResolvers();
11932
+ currentPromise.resolver = resolver.resolve;
11933
+ currentPromise.rejector = resolver.reject;
11934
+ childController.resume();
11935
+ return resolver.promise;
11936
+ },
11937
+ abort() {
11938
+ childController.abort();
11299
11939
  }
11300
- },
11301
- onAudioTrack: onAudioTrack === null ? null : async ({ track }) => {
11302
- const callbackOrFalse = m3uState.hasEmittedAudioTrack(playlistUrl);
11303
- if (callbackOrFalse === false) {
11304
- const callback = await onAudioTrack(track);
11305
- if (!callback) {
11306
- m3uState.setHasEmittedAudioTrack(playlistUrl, null);
11940
+ };
11941
+ };
11942
+ const isLastChunk = chunk === chunks[chunks.length - 1];
11943
+ await childController._internals.checkForAbortAndPause();
11944
+ const src = state.readerInterface.createAdjacentFileSource(chunk.url, playlistUrl);
11945
+ try {
11946
+ const data = await parseMedia({
11947
+ src,
11948
+ acknowledgeRemotionLicense: true,
11949
+ logLevel: state.logLevel,
11950
+ controller: childController,
11951
+ progressIntervalInMs: 0,
11952
+ onParseProgress: () => {
11953
+ childController.pause();
11954
+ currentPromise.resolver(makeContinuationFn());
11955
+ },
11956
+ fields: chunk.isHeader ? { structure: true } : undefined,
11957
+ onTracks: () => {
11958
+ if (!state.m3u.hasEmittedDoneWithTracks(playlistUrl)) {
11959
+ state.m3u.setHasEmittedDoneWithTracks(playlistUrl);
11960
+ const allDone = state.m3u.setTracksDone(playlistUrl);
11961
+ if (allDone) {
11962
+ state.callbacks.tracks.setIsDone(state.logLevel);
11963
+ }
11964
+ return null;
11965
+ }
11966
+ },
11967
+ onAudioTrack: onGlobalAudioTrack === null ? null : async ({ track }) => {
11968
+ const callbackOrFalse = state.m3u.hasEmittedAudioTrack(playlistUrl);
11969
+ if (callbackOrFalse === false) {
11970
+ const callback = await onGlobalAudioTrack(track);
11971
+ if (!callback) {
11972
+ state.m3u.setHasEmittedAudioTrack(playlistUrl, null);
11973
+ return null;
11974
+ }
11975
+ state.m3u.setHasEmittedAudioTrack(playlistUrl, callback);
11976
+ return async (sample) => {
11977
+ await considerSeekBasedOnChunk({
11978
+ sample,
11979
+ callback,
11980
+ parentController: state.controller,
11981
+ childController,
11982
+ m3uState: state.m3u,
11983
+ playlistUrl,
11984
+ subtractChunks: chunksToSubtract,
11985
+ chunkIndex
11986
+ });
11987
+ };
11988
+ }
11989
+ if (callbackOrFalse === null) {
11307
11990
  return null;
11308
11991
  }
11309
- m3uState.setHasEmittedAudioTrack(playlistUrl, callback);
11310
- return (sample) => {
11311
- return callback(sample);
11992
+ return async (sample) => {
11993
+ await considerSeekBasedOnChunk({
11994
+ sample,
11995
+ m3uState: state.m3u,
11996
+ playlistUrl,
11997
+ callback: callbackOrFalse,
11998
+ parentController: state.controller,
11999
+ childController,
12000
+ subtractChunks: chunksToSubtract,
12001
+ chunkIndex
12002
+ });
11312
12003
  };
11313
- }
11314
- return callbackOrFalse;
11315
- },
11316
- onVideoTrack: onVideoTrack === null ? null : async ({ track }) => {
11317
- const callbackOrFalse = m3uState.hasEmittedVideoTrack(playlistUrl);
11318
- if (callbackOrFalse === false) {
11319
- const callback = await onVideoTrack({
11320
- ...track,
11321
- m3uStreamFormat: chunk.isHeader || mp4HeaderSegment ? "mp4" : "ts"
11322
- });
11323
- if (!callback) {
11324
- m3uState.setHasEmittedVideoTrack(playlistUrl, null);
12004
+ },
12005
+ onVideoTrack: onGlobalVideoTrack === null ? null : async ({ track }) => {
12006
+ const callbackOrFalse = state.m3u.hasEmittedVideoTrack(playlistUrl);
12007
+ if (callbackOrFalse === false) {
12008
+ const callback = await onGlobalVideoTrack({
12009
+ ...track,
12010
+ m3uStreamFormat: chunk.isHeader || mp4HeaderSegment ? "mp4" : "ts"
12011
+ });
12012
+ if (!callback) {
12013
+ state.m3u.setHasEmittedVideoTrack(playlistUrl, null);
12014
+ return null;
12015
+ }
12016
+ state.m3u.setHasEmittedVideoTrack(playlistUrl, callback);
12017
+ return async (sample) => {
12018
+ await considerSeekBasedOnChunk({
12019
+ sample,
12020
+ m3uState: state.m3u,
12021
+ playlistUrl,
12022
+ callback,
12023
+ parentController: state.controller,
12024
+ childController,
12025
+ subtractChunks: chunksToSubtract,
12026
+ chunkIndex
12027
+ });
12028
+ };
12029
+ }
12030
+ if (callbackOrFalse === null) {
11325
12031
  return null;
11326
12032
  }
11327
- m3uState.setHasEmittedVideoTrack(playlistUrl, callback);
11328
- return (sample) => {
11329
- return callback(sample);
12033
+ return async (sample) => {
12034
+ await considerSeekBasedOnChunk({
12035
+ sample,
12036
+ m3uState: state.m3u,
12037
+ playlistUrl,
12038
+ callback: callbackOrFalse,
12039
+ parentController: state.controller,
12040
+ childController,
12041
+ subtractChunks: chunksToSubtract,
12042
+ chunkIndex
12043
+ });
11330
12044
  };
12045
+ },
12046
+ reader: state.readerInterface,
12047
+ makeSamplesStartAtZero: false,
12048
+ m3uPlaylistContext: {
12049
+ mp4HeaderSegment,
12050
+ isLastChunkInPlaylist: isLastChunk
11331
12051
  }
11332
- return callbackOrFalse;
11333
- },
11334
- reader: readerInterface,
11335
- mp4HeaderSegment,
11336
- makeSamplesStartAtZero: false
11337
- });
11338
- if (chunk.isHeader) {
11339
- if (data.structure.type !== "iso-base-media") {
11340
- throw new Error("Expected an mp4 file");
12052
+ });
12053
+ if (chunk.isHeader) {
12054
+ if (data.structure.type !== "iso-base-media") {
12055
+ throw new Error("Expected an mp4 file");
12056
+ }
12057
+ state.m3u.setMp4HeaderSegment(playlistUrl, data.structure);
11341
12058
  }
11342
- m3uState.setMp4HeaderSegment(playlistUrl, data.structure);
12059
+ } catch (e) {
12060
+ currentPromise.rejector(e);
12061
+ throw e;
12062
+ }
12063
+ forwarded.cleanup();
12064
+ if (!isLastChunk) {
12065
+ childController.pause();
12066
+ currentPromise.resolver(makeContinuationFn());
11343
12067
  }
11344
- } catch (e) {
11345
- rejector(e);
11346
- throw e;
11347
- }
11348
- forwarded.cleanup();
11349
- if (!isLastChunk) {
11350
- childController.pause();
11351
- resolver(makeContinuationFn());
11352
12068
  }
11353
- }
11354
- resolver(null);
12069
+ currentPromise.resolver(null);
12070
+ };
12071
+ const run = pausableIterator();
12072
+ run.catch((err) => {
12073
+ reject(err);
12074
+ });
12075
+ return promise;
11355
12076
  };
11356
12077
 
11357
12078
  // src/containers/m3u/run-over-m3u.ts
@@ -11382,80 +12103,12 @@ var runOverM3u = async ({
11382
12103
  return;
11383
12104
  }
11384
12105
  Log.trace(logLevel, "Starting new M3U parsing process for", playlistUrl);
11385
- return new Promise((resolve, reject) => {
11386
- const run = iteratorOverSegmentFiles({
11387
- playlistUrl,
11388
- structure,
11389
- onInitialProgress: (newRun) => {
11390
- state.m3u.setM3uStreamRun(playlistUrl, newRun);
11391
- resolve();
11392
- },
11393
- logLevel: state.logLevel,
11394
- onDoneWithTracks() {
11395
- const allDone = state.m3u.setTracksDone(playlistUrl);
11396
- if (allDone) {
11397
- state.callbacks.tracks.setIsDone(state.logLevel);
11398
- }
11399
- },
11400
- onAudioTrack: audioDone ? null : async (track) => {
11401
- const existingTracks = state.callbacks.tracks.getTracks();
11402
- let { trackId } = track;
11403
- while (existingTracks.find((t) => t.trackId === trackId)) {
11404
- trackId++;
11405
- }
11406
- const onAudioSample = await registerAudioTrack({
11407
- container: "m3u8",
11408
- track: {
11409
- ...track,
11410
- trackId
11411
- },
11412
- registerAudioSampleCallback: state.callbacks.registerAudioSampleCallback,
11413
- tracks: state.callbacks.tracks,
11414
- logLevel: state.logLevel,
11415
- onAudioTrack: state.onAudioTrack
11416
- });
11417
- state.m3u.sampleSorter.addToStreamWithTrack(playlistUrl);
11418
- if (onAudioSample === null) {
11419
- return null;
11420
- }
11421
- state.m3u.sampleSorter.addAudioStreamToConsider(playlistUrl, onAudioSample);
11422
- return async (sample) => {
11423
- await state.m3u.sampleSorter.addAudioSample(playlistUrl, sample);
11424
- };
11425
- },
11426
- onVideoTrack: videoDone ? null : async (track) => {
11427
- const existingTracks = state.callbacks.tracks.getTracks();
11428
- let { trackId } = track;
11429
- while (existingTracks.find((t) => t.trackId === trackId)) {
11430
- trackId++;
11431
- }
11432
- const onVideoSample = await registerVideoTrack({
11433
- container: "m3u8",
11434
- track: {
11435
- ...track,
11436
- trackId
11437
- },
11438
- logLevel: state.logLevel,
11439
- onVideoTrack: state.onVideoTrack,
11440
- registerVideoSampleCallback: state.callbacks.registerVideoSampleCallback,
11441
- tracks: state.callbacks.tracks
11442
- });
11443
- state.m3u.sampleSorter.addToStreamWithTrack(playlistUrl);
11444
- if (onVideoSample === null) {
11445
- return null;
11446
- }
11447
- state.m3u.sampleSorter.addVideoStreamToConsider(playlistUrl, onVideoSample);
11448
- return async (sample) => {
11449
- await state.m3u.sampleSorter.addVideoSample(playlistUrl, sample);
11450
- };
11451
- },
11452
- m3uState: state.m3u,
11453
- parentController: state.controller,
11454
- readerInterface: state.readerInterface
11455
- });
11456
- run.catch((err) => {
11457
- reject(err);
11458
- });
12106
+ await processM3uChunk({
12107
+ playlistUrl,
12108
+ state,
12109
+ structure,
12110
+ audioDone,
12111
+ videoDone
11459
12112
  });
11460
12113
  };
11461
12114
 
@@ -11477,6 +12130,10 @@ var parseM3u = async ({ state }) => {
11477
12130
  if (typeof state.src !== "string" && !(state.src instanceof URL)) {
11478
12131
  throw new Error("Expected src to be a string");
11479
12132
  }
12133
+ state.mediaSection.addMediaSection({
12134
+ start: 0,
12135
+ size: state.contentLength + 1
12136
+ });
11480
12137
  await afterManifestFetch({
11481
12138
  structure,
11482
12139
  m3uState: state.m3u,
@@ -11484,7 +12141,9 @@ var parseM3u = async ({ state }) => {
11484
12141
  selectM3uStreamFn: state.selectM3uStreamFn,
11485
12142
  logLevel: state.logLevel,
11486
12143
  selectAssociatedPlaylistsFn: state.selectM3uAssociatedPlaylistsFn,
11487
- readerInterface: state.readerInterface
12144
+ readerInterface: state.readerInterface,
12145
+ onAudioTrack: state.onAudioTrack,
12146
+ canSkipTracks: state.callbacks.canSkipTracksState
11488
12147
  });
11489
12148
  return null;
11490
12149
  }
@@ -13663,23 +14322,62 @@ var parseBlockFlags = (iterator, type) => {
13663
14322
  };
13664
14323
 
13665
14324
  // src/containers/webm/get-sample-from-block.ts
13666
- var addAvcToTrackIfNecessary = ({
14325
+ var addAvcToTrackAndActivateTrackIfNecessary = async ({
13667
14326
  partialVideoSample,
13668
14327
  codec,
13669
14328
  structureState: structureState2,
13670
14329
  webmState,
13671
- trackNumber: trackNumber2
14330
+ trackNumber: trackNumber2,
14331
+ logLevel,
14332
+ callbacks,
14333
+ onVideoTrack
13672
14334
  }) => {
13673
- if (codec === "V_MPEG4/ISO/AVC" && getTracksFromMatroska({ structureState: structureState2, webmState }).missingInfo.length > 0) {
13674
- const parsed = parseAvc(partialVideoSample.data);
13675
- for (const parse of parsed) {
13676
- if (parse.type === "avc-profile") {
13677
- webmState.setAvcProfileForTrackNumber(trackNumber2, parse);
14335
+ if (codec !== "V_MPEG4/ISO/AVC") {
14336
+ return;
14337
+ }
14338
+ const missingTracks = getTracksFromMatroska({
14339
+ structureState: structureState2,
14340
+ webmState
14341
+ }).missingInfo;
14342
+ if (missingTracks.length === 0) {
14343
+ return;
14344
+ }
14345
+ const parsed = parseAvc(partialVideoSample.data);
14346
+ for (const parse of parsed) {
14347
+ if (parse.type === "avc-profile") {
14348
+ webmState.setAvcProfileForTrackNumber(trackNumber2, parse);
14349
+ const track = missingTracks.find((t) => t.trackId === trackNumber2);
14350
+ if (!track) {
14351
+ throw new Error("Could not find track " + trackNumber2);
14352
+ }
14353
+ const resolvedTracks = getTracksFromMatroska({
14354
+ structureState: structureState2,
14355
+ webmState
14356
+ }).resolved;
14357
+ const resolvedTrack = resolvedTracks.find((t) => t.trackId === trackNumber2);
14358
+ if (!resolvedTrack) {
14359
+ throw new Error("Could not find track " + trackNumber2);
13678
14360
  }
14361
+ await registerVideoTrack({
14362
+ track: resolvedTrack,
14363
+ container: "webm",
14364
+ logLevel,
14365
+ onVideoTrack,
14366
+ registerVideoSampleCallback: callbacks.registerVideoSampleCallback,
14367
+ tracks: callbacks.tracks
14368
+ });
13679
14369
  }
13680
14370
  }
13681
14371
  };
13682
- var getSampleFromBlock = (ebml, webmState, offset, structureState2) => {
14372
+ var getSampleFromBlock = async ({
14373
+ ebml,
14374
+ webmState,
14375
+ offset,
14376
+ structureState: structureState2,
14377
+ callbacks,
14378
+ logLevel,
14379
+ onVideoTrack
14380
+ }) => {
13683
14381
  const iterator = getArrayBufferIterator(ebml.value, ebml.value.length);
13684
14382
  const trackNumber2 = iterator.getVint();
13685
14383
  if (trackNumber2 === null) {
@@ -13717,12 +14415,15 @@ var getSampleFromBlock = (ebml, webmState, offset, structureState2) => {
13717
14415
  partialVideoSample
13718
14416
  };
13719
14417
  }
13720
- addAvcToTrackIfNecessary({
14418
+ await addAvcToTrackAndActivateTrackIfNecessary({
13721
14419
  codec,
13722
14420
  partialVideoSample,
13723
14421
  structureState: structureState2,
13724
14422
  webmState,
13725
- trackNumber: trackNumber2
14423
+ trackNumber: trackNumber2,
14424
+ callbacks,
14425
+ logLevel,
14426
+ onVideoTrack
13726
14427
  });
13727
14428
  const sample = {
13728
14429
  ...partialVideoSample,
@@ -13882,21 +14583,31 @@ var postprocessEbml = async ({
13882
14583
  });
13883
14584
  }
13884
14585
  if (track && track.type === "video") {
13885
- await registerVideoTrack({
13886
- track,
13887
- container: "webm",
13888
- logLevel,
13889
- onVideoTrack,
13890
- registerVideoSampleCallback: callbacks.registerVideoSampleCallback,
13891
- tracks: callbacks.tracks
13892
- });
14586
+ if (track.codec !== NO_CODEC_PRIVATE_SHOULD_BE_DERIVED_FROM_SPS) {
14587
+ await registerVideoTrack({
14588
+ track,
14589
+ container: "webm",
14590
+ logLevel,
14591
+ onVideoTrack,
14592
+ registerVideoSampleCallback: callbacks.registerVideoSampleCallback,
14593
+ tracks: callbacks.tracks
14594
+ });
14595
+ }
13893
14596
  }
13894
14597
  }
13895
14598
  if (ebml.type === "Timestamp") {
13896
14599
  webmState.setTimestampOffset(offset, ebml.value.value);
13897
14600
  }
13898
14601
  if (ebml.type === "Block" || ebml.type === "SimpleBlock") {
13899
- const sample = getSampleFromBlock(ebml, webmState, offset, structureState2);
14602
+ const sample = await getSampleFromBlock({
14603
+ ebml,
14604
+ webmState,
14605
+ offset,
14606
+ structureState: structureState2,
14607
+ callbacks,
14608
+ logLevel,
14609
+ onVideoTrack
14610
+ });
13900
14611
  if (sample.type === "video-sample") {
13901
14612
  await callbacks.onVideoSample(sample.videoSample.trackId, sample.videoSample);
13902
14613
  return {
@@ -13927,7 +14638,15 @@ var postprocessEbml = async ({
13927
14638
  throw new Error("Expected block segment");
13928
14639
  }
13929
14640
  const hasReferenceBlock = ebml.value.find((c) => c.type === "ReferenceBlock");
13930
- const sample = block2.value.length === 0 ? null : getSampleFromBlock(block2, webmState, offset, structureState2);
14641
+ const sample = block2.value.length === 0 ? null : await getSampleFromBlock({
14642
+ ebml: block2,
14643
+ webmState,
14644
+ offset,
14645
+ structureState: structureState2,
14646
+ callbacks,
14647
+ logLevel,
14648
+ onVideoTrack
14649
+ });
13931
14650
  if (sample && sample.type === "partial-video-sample") {
13932
14651
  const completeFrame = {
13933
14652
  ...sample.partialVideoSample,
@@ -14128,9 +14847,9 @@ var initVideo = async ({ state }) => {
14128
14847
  });
14129
14848
  return;
14130
14849
  }
14131
- if (state.mp4HeaderSegment) {
14850
+ if (state.m3uPlaylistContext?.mp4HeaderSegment) {
14132
14851
  Log.verbose(state.logLevel, "Detected ISO Base Media segment");
14133
- const moovAtom = getMoovFromFromIsoStructure(state.mp4HeaderSegment);
14852
+ const moovAtom = getMoovFromFromIsoStructure(state.m3uPlaylistContext.mp4HeaderSegment);
14134
14853
  if (!moovAtom) {
14135
14854
  throw new Error("No moov box found");
14136
14855
  }
@@ -14353,19 +15072,33 @@ var parseLoop = async ({
14353
15072
  try {
14354
15073
  await triggerInfoEmit(state);
14355
15074
  await state.controller._internals.checkForAbortAndPause();
14356
- const skip = await runParseIteration({
15075
+ const result = await runParseIteration({
14357
15076
  state
14358
15077
  });
14359
- if (skip !== null) {
14360
- state.increaseSkippedBytes(skip.skipTo - state.iterator.counter.getOffset());
14361
- if (skip.skipTo === state.contentLength) {
14362
- state.iterator.discard(skip.skipTo - state.iterator.counter.getOffset());
15078
+ if (result !== null && result.action === "fetch-more-data") {
15079
+ Log.verbose(state.logLevel, `Need to fetch ${result.bytesNeeded} more bytes before we can continue`);
15080
+ const startBytesRemaining = state.iterator.bytesRemaining();
15081
+ while (true) {
15082
+ const done = await fetchMoreData(state);
15083
+ if (done) {
15084
+ break;
15085
+ }
15086
+ if (state.iterator.bytesRemaining() - startBytesRemaining >= result.bytesNeeded) {
15087
+ break;
15088
+ }
15089
+ }
15090
+ continue;
15091
+ }
15092
+ if (result !== null && result.action === "skip") {
15093
+ state.increaseSkippedBytes(result.skipTo - state.iterator.counter.getOffset());
15094
+ if (result.skipTo === state.contentLength) {
15095
+ state.iterator.discard(result.skipTo - state.iterator.counter.getOffset());
14363
15096
  Log.verbose(state.logLevel, "Skipped to end of file, not fetching.");
14364
15097
  break;
14365
15098
  }
14366
15099
  const seekStart = Date.now();
14367
15100
  await performSeek({
14368
- seekTo: skip.skipTo,
15101
+ seekTo: result.skipTo,
14369
15102
  userInitiated: false,
14370
15103
  controller: state.controller,
14371
15104
  mediaSection: state.mediaSection,
@@ -14378,7 +15111,8 @@ var parseLoop = async ({
14378
15111
  readerInterface: state.readerInterface,
14379
15112
  fields: state.fields,
14380
15113
  src: state.src,
14381
- discardReadBytes: state.discardReadBytes
15114
+ discardReadBytes: state.discardReadBytes,
15115
+ prefetchCache: state.prefetchCache
14382
15116
  });
14383
15117
  state.timings.timeSeeking += Date.now() - seekStart;
14384
15118
  }
@@ -14485,6 +15219,9 @@ var setSeekingHints = ({
14485
15219
  setSeekingHintsForAac();
14486
15220
  return;
14487
15221
  }
15222
+ if (hints.type === "m3u8-seeking-hints") {
15223
+ return;
15224
+ }
14488
15225
  throw new Error(`Unknown seeking hints type: ${hints}`);
14489
15226
  };
14490
15227
 
@@ -14640,12 +15377,16 @@ var getMfraAtom = async ({
14640
15377
  contentLength,
14641
15378
  readerInterface,
14642
15379
  controller,
14643
- parentSize
15380
+ parentSize,
15381
+ logLevel,
15382
+ prefetchCache
14644
15383
  }) => {
14645
15384
  const result = await readerInterface.read({
14646
15385
  controller,
14647
15386
  range: [contentLength - parentSize, contentLength - 1],
14648
- src
15387
+ src,
15388
+ logLevel,
15389
+ prefetchCache
14649
15390
  });
14650
15391
  const iterator = getArrayBufferIterator(new Uint8Array, parentSize);
14651
15392
  while (true) {
@@ -14665,12 +15406,16 @@ var getMfroAtom = async ({
14665
15406
  src,
14666
15407
  contentLength,
14667
15408
  readerInterface,
14668
- controller
15409
+ controller,
15410
+ logLevel,
15411
+ prefetchCache
14669
15412
  }) => {
14670
15413
  const result = await readerInterface.read({
14671
15414
  controller,
14672
15415
  range: [contentLength - 16, contentLength - 1],
14673
- src
15416
+ src,
15417
+ logLevel,
15418
+ prefetchCache
14674
15419
  });
14675
15420
  const { value } = await result.reader.reader.read();
14676
15421
  if (!value) {
@@ -14705,13 +15450,16 @@ var getMfraSeekingBox = async ({
14705
15450
  controller,
14706
15451
  readerInterface,
14707
15452
  src,
14708
- logLevel
15453
+ logLevel,
15454
+ prefetchCache
14709
15455
  }) => {
14710
15456
  const parentSize = await getMfroAtom({
14711
15457
  contentLength,
14712
15458
  controller,
14713
15459
  readerInterface,
14714
- src
15460
+ src,
15461
+ logLevel,
15462
+ prefetchCache
14715
15463
  });
14716
15464
  if (!parentSize) {
14717
15465
  return null;
@@ -14721,7 +15469,9 @@ var getMfraSeekingBox = async ({
14721
15469
  controller,
14722
15470
  readerInterface,
14723
15471
  src,
14724
- parentSize
15472
+ parentSize,
15473
+ logLevel,
15474
+ prefetchCache
14725
15475
  });
14726
15476
  mfraAtom.discard(8);
14727
15477
  return getIsoBaseMediaChildren({
@@ -14739,7 +15489,8 @@ var lazyMfraLoad = ({
14739
15489
  controller,
14740
15490
  readerInterface,
14741
15491
  src,
14742
- logLevel
15492
+ logLevel,
15493
+ prefetchCache
14743
15494
  }) => {
14744
15495
  let prom = null;
14745
15496
  let result = null;
@@ -14753,7 +15504,8 @@ var lazyMfraLoad = ({
14753
15504
  controller,
14754
15505
  readerInterface,
14755
15506
  src,
14756
- logLevel
15507
+ logLevel,
15508
+ prefetchCache
14757
15509
  }).then((boxes) => {
14758
15510
  Log.verbose(logLevel, "Lazily found mfra atom.");
14759
15511
  result = boxes;
@@ -14794,7 +15546,8 @@ var isoBaseMediaState = ({
14794
15546
  controller,
14795
15547
  readerInterface,
14796
15548
  src,
14797
- logLevel
15549
+ logLevel,
15550
+ prefetchCache
14798
15551
  }) => {
14799
15552
  return {
14800
15553
  flatSamples: cachedSamplePositionsState(),
@@ -14804,7 +15557,8 @@ var isoBaseMediaState = ({
14804
15557
  controller,
14805
15558
  readerInterface,
14806
15559
  src,
14807
- logLevel
15560
+ logLevel,
15561
+ prefetchCache
14808
15562
  }),
14809
15563
  moof: precomputedMoofState(),
14810
15564
  tfra: precomputedTfraState()
@@ -14821,6 +15575,7 @@ var keyframesState = () => {
14821
15575
  keyframes.push(keyframe);
14822
15576
  };
14823
15577
  const getKeyframes2 = () => {
15578
+ keyframes.sort((a, b) => a.positionInBytes - b.positionInBytes);
14824
15579
  return keyframes;
14825
15580
  };
14826
15581
  const setFromSeekingHints = (keyframesFromHints) => {
@@ -14843,8 +15598,11 @@ var sampleSorter = ({
14843
15598
  const streamsWithTracks = [];
14844
15599
  const audioCallbacks = {};
14845
15600
  const videoCallbacks = {};
14846
- const latestSample = {};
15601
+ let latestSample = {};
14847
15602
  return {
15603
+ clearSamples: () => {
15604
+ latestSample = {};
15605
+ },
14848
15606
  addToStreamWithTrack: (src) => {
14849
15607
  streamsWithTracks.push(src);
14850
15608
  },
@@ -14917,6 +15675,8 @@ var m3uState = (logLevel) => {
14917
15675
  const hasEmittedAudioTrack = {};
14918
15676
  const hasEmittedDoneWithTracks = {};
14919
15677
  let hasFinishedManifest = false;
15678
+ const seekToSecondsToProcess = {};
15679
+ const nextSeekShouldSubtractChunks = {};
14920
15680
  let readyToIterateOverM3u = false;
14921
15681
  const allChunksProcessed = {};
14922
15682
  const m3uStreamRuns = {};
@@ -14978,6 +15738,11 @@ var m3uState = (logLevel) => {
14978
15738
  setAllChunksProcessed: (src) => {
14979
15739
  allChunksProcessed[src] = true;
14980
15740
  },
15741
+ clearAllChunksProcessed: () => {
15742
+ Object.keys(allChunksProcessed).forEach((key) => {
15743
+ delete allChunksProcessed[key];
15744
+ });
15745
+ },
14981
15746
  getAllChunksProcessedForPlaylist,
14982
15747
  getAllChunksProcessedOverall: () => {
14983
15748
  if (!selectedMainPlaylist) {
@@ -15005,6 +15770,11 @@ var m3uState = (logLevel) => {
15005
15770
  getTrackDone: (playlistUrl) => {
15006
15771
  return tracksDone[playlistUrl];
15007
15772
  },
15773
+ clearTracksDone: () => {
15774
+ Object.keys(tracksDone).forEach((key) => {
15775
+ delete tracksDone[key];
15776
+ });
15777
+ },
15008
15778
  getM3uStreamRun: (playlistUrl) => m3uStreamRuns[playlistUrl] ?? null,
15009
15779
  abortM3UStreamRuns: () => {
15010
15780
  const values = Object.values(m3uStreamRuns);
@@ -15023,7 +15793,15 @@ var m3uState = (logLevel) => {
15023
15793
  getSelectedPlaylists,
15024
15794
  sampleSorter: sampleSorter({ logLevel, getAllChunksProcessedForPlaylist }),
15025
15795
  setMp4HeaderSegment,
15026
- getMp4HeaderSegment
15796
+ getMp4HeaderSegment,
15797
+ setSeekToSecondsToProcess: (playlistUrl, m3uSeek) => {
15798
+ seekToSecondsToProcess[playlistUrl] = m3uSeek;
15799
+ },
15800
+ getSeekToSecondsToProcess: (playlistUrl) => seekToSecondsToProcess[playlistUrl] ?? null,
15801
+ setNextSeekShouldSubtractChunks: (playlistUrl, chunks) => {
15802
+ nextSeekShouldSubtractChunks[playlistUrl] = chunks;
15803
+ },
15804
+ getNextSeekShouldSubtractChunks: (playlistUrl) => nextSeekShouldSubtractChunks[playlistUrl] ?? 0
15027
15805
  };
15028
15806
  };
15029
15807
 
@@ -15072,12 +15850,15 @@ var fetchWebmCues = async ({
15072
15850
  readerInterface,
15073
15851
  controller,
15074
15852
  position,
15075
- logLevel
15853
+ logLevel,
15854
+ prefetchCache
15076
15855
  }) => {
15077
15856
  const result = await readerInterface.read({
15078
15857
  controller,
15079
15858
  range: position,
15080
- src
15859
+ src,
15860
+ logLevel,
15861
+ prefetchCache
15081
15862
  });
15082
15863
  const { value } = await result.reader.reader.read();
15083
15864
  if (!value) {
@@ -15104,7 +15885,8 @@ var lazyCuesFetch = ({
15104
15885
  controller,
15105
15886
  logLevel,
15106
15887
  readerInterface,
15107
- src
15888
+ src,
15889
+ prefetchCache
15108
15890
  }) => {
15109
15891
  let prom = null;
15110
15892
  let sOffset = null;
@@ -15126,7 +15908,8 @@ var lazyCuesFetch = ({
15126
15908
  logLevel,
15127
15909
  position,
15128
15910
  readerInterface,
15129
- src
15911
+ src,
15912
+ prefetchCache
15130
15913
  }).then((cues) => {
15131
15914
  Log.verbose(logLevel, "Cues loaded");
15132
15915
  result = cues;
@@ -15188,7 +15971,8 @@ var webmState = ({
15188
15971
  controller,
15189
15972
  logLevel,
15190
15973
  readerInterface,
15191
- src
15974
+ src,
15975
+ prefetchCache
15192
15976
  }) => {
15193
15977
  const trackEntries = {};
15194
15978
  const onTrackEntrySegment = (trackEntry2) => {
@@ -15249,7 +16033,8 @@ var webmState = ({
15249
16033
  controller,
15250
16034
  logLevel,
15251
16035
  readerInterface,
15252
- src
16036
+ src,
16037
+ prefetchCache
15253
16038
  });
15254
16039
  const getTimeStampMapForSeekingHints = () => {
15255
16040
  return timestampMap;
@@ -15330,13 +16115,16 @@ var fetchIdx1 = async ({
15330
16115
  readerInterface,
15331
16116
  controller,
15332
16117
  position,
15333
- logLevel
16118
+ logLevel,
16119
+ prefetchCache
15334
16120
  }) => {
15335
16121
  Log.verbose(logLevel, "Making request to fetch idx1 from ", src, "position", position);
15336
16122
  const result = await readerInterface.read({
15337
16123
  controller,
15338
16124
  range: position,
15339
- src
16125
+ src,
16126
+ logLevel,
16127
+ prefetchCache
15340
16128
  });
15341
16129
  const iterator = getArrayBufferIterator(new Uint8Array, Infinity);
15342
16130
  while (true) {
@@ -15367,7 +16155,8 @@ var lazyIdx1Fetch = ({
15367
16155
  controller,
15368
16156
  logLevel,
15369
16157
  readerInterface,
15370
- src
16158
+ src,
16159
+ prefetchCache
15371
16160
  }) => {
15372
16161
  let prom = null;
15373
16162
  let result = null;
@@ -15383,7 +16172,8 @@ var lazyIdx1Fetch = ({
15383
16172
  logLevel,
15384
16173
  position,
15385
16174
  readerInterface,
15386
- src
16175
+ src,
16176
+ prefetchCache
15387
16177
  }).then((entries) => {
15388
16178
  prom = null;
15389
16179
  result = entries;
@@ -15504,7 +16294,8 @@ var riffSpecificState = ({
15504
16294
  controller,
15505
16295
  logLevel,
15506
16296
  readerInterface,
15507
- src
16297
+ src,
16298
+ prefetchCache
15508
16299
  }) => {
15509
16300
  let avcProfile = null;
15510
16301
  let nextTrackIndex = 0;
@@ -15523,7 +16314,8 @@ var riffSpecificState = ({
15523
16314
  controller,
15524
16315
  logLevel,
15525
16316
  readerInterface,
15526
- src
16317
+ src,
16318
+ prefetchCache
15527
16319
  });
15528
16320
  const sampleCounter = riffSampleCounter();
15529
16321
  return {
@@ -15544,7 +16336,7 @@ var riffSpecificState = ({
15544
16336
  };
15545
16337
 
15546
16338
  // src/state/sample-callbacks.ts
15547
- var sampleCallback = ({
16339
+ var callbacksState = ({
15548
16340
  controller,
15549
16341
  hasAudioTrackHandlers,
15550
16342
  hasVideoTrackHandlers,
@@ -15851,14 +16643,15 @@ var makeParserState = ({
15851
16643
  onDiscardedData,
15852
16644
  selectM3uStreamFn,
15853
16645
  selectM3uAssociatedPlaylistsFn,
15854
- mp4HeaderSegment,
16646
+ m3uPlaylistContext,
15855
16647
  contentType,
15856
16648
  name,
15857
16649
  callbacks,
15858
16650
  fieldsInReturnValue,
15859
16651
  mimeType,
15860
16652
  initialReaderInstance,
15861
- makeSamplesStartAtZero
16653
+ makeSamplesStartAtZero,
16654
+ prefetchCache
15862
16655
  }) => {
15863
16656
  let skippedBytes = 0;
15864
16657
  const returnValue = {};
@@ -15890,22 +16683,35 @@ var makeParserState = ({
15890
16683
  callbacks
15891
16684
  });
15892
16685
  return {
15893
- riff: riffSpecificState({ controller, logLevel, readerInterface, src }),
16686
+ riff: riffSpecificState({
16687
+ controller,
16688
+ logLevel,
16689
+ readerInterface,
16690
+ src,
16691
+ prefetchCache
16692
+ }),
15894
16693
  transportStream: transportStreamState(),
15895
- webm: webmState({ controller, logLevel, readerInterface, src }),
16694
+ webm: webmState({
16695
+ controller,
16696
+ logLevel,
16697
+ readerInterface,
16698
+ src,
16699
+ prefetchCache
16700
+ }),
15896
16701
  iso: isoBaseMediaState({
15897
16702
  contentLength,
15898
16703
  controller,
15899
16704
  readerInterface,
15900
16705
  src,
15901
- logLevel
16706
+ logLevel,
16707
+ prefetchCache
15902
16708
  }),
15903
16709
  mp3,
15904
16710
  aac: aacState(),
15905
16711
  flac: flacState(),
15906
16712
  m3u: m3uState(logLevel),
15907
16713
  timings,
15908
- callbacks: sampleCallback({
16714
+ callbacks: callbacksState({
15909
16715
  controller,
15910
16716
  hasAudioTrackHandlers,
15911
16717
  hasVideoTrackHandlers,
@@ -15943,7 +16749,7 @@ var makeParserState = ({
15943
16749
  discardReadBytes,
15944
16750
  selectM3uStreamFn,
15945
16751
  selectM3uAssociatedPlaylistsFn,
15946
- mp4HeaderSegment,
16752
+ m3uPlaylistContext,
15947
16753
  contentType,
15948
16754
  name,
15949
16755
  returnValue,
@@ -15953,7 +16759,8 @@ var makeParserState = ({
15953
16759
  errored,
15954
16760
  currentReader: currentReaderState,
15955
16761
  seekInfiniteLoop,
15956
- makeSamplesStartAtZero
16762
+ makeSamplesStartAtZero,
16763
+ prefetchCache
15957
16764
  };
15958
16765
  };
15959
16766
 
@@ -16032,7 +16839,7 @@ var internalParseMedia = async function({
16032
16839
  apiName,
16033
16840
  selectM3uStream: selectM3uStreamFn,
16034
16841
  selectM3uAssociatedPlaylists: selectM3uAssociatedPlaylistsFn,
16035
- mp4HeaderSegment,
16842
+ m3uPlaylistContext,
16036
16843
  makeSamplesStartAtZero,
16037
16844
  seekingHints,
16038
16845
  ...more
@@ -16044,6 +16851,7 @@ var internalParseMedia = async function({
16044
16851
  apiName
16045
16852
  });
16046
16853
  Log.verbose(logLevel, `Reading ${typeof src === "string" ? src : src instanceof URL ? src.toString() : src instanceof File ? src.name : src.toString()}`);
16854
+ const prefetchCache = new Map;
16047
16855
  const {
16048
16856
  reader: readerInstance,
16049
16857
  contentLength,
@@ -16051,7 +16859,13 @@ var internalParseMedia = async function({
16051
16859
  contentType,
16052
16860
  supportsContentRange,
16053
16861
  needsContentRange
16054
- } = await readerInterface.read({ src, range: null, controller });
16862
+ } = await readerInterface.read({
16863
+ src,
16864
+ range: null,
16865
+ controller,
16866
+ logLevel,
16867
+ prefetchCache
16868
+ });
16055
16869
  if (contentLength === null) {
16056
16870
  throw new Error(`Cannot read media ${src} without a content length. This is currently not supported. Ensure the media has a "Content-Length" HTTP header.`);
16057
16871
  }
@@ -16074,14 +16888,15 @@ var internalParseMedia = async function({
16074
16888
  onDiscardedData,
16075
16889
  selectM3uStreamFn,
16076
16890
  selectM3uAssociatedPlaylistsFn,
16077
- mp4HeaderSegment,
16891
+ m3uPlaylistContext,
16078
16892
  contentType,
16079
16893
  name,
16080
16894
  callbacks: more,
16081
16895
  fieldsInReturnValue: _fieldsInReturnValue ?? {},
16082
16896
  mimeType: contentType,
16083
16897
  initialReaderInstance: readerInstance,
16084
- makeSamplesStartAtZero
16898
+ makeSamplesStartAtZero,
16899
+ prefetchCache
16085
16900
  });
16086
16901
  if (seekingHints) {
16087
16902
  setSeekingHints({ hints: seekingHints, state });
@@ -16091,7 +16906,7 @@ var internalParseMedia = async function({
16091
16906
  keyframesState: state.keyframes,
16092
16907
  webmState: state.webm,
16093
16908
  structureState: state.structure,
16094
- mp4HeaderSegment: state.mp4HeaderSegment,
16909
+ m3uPlaylistContext: state.m3uPlaylistContext,
16095
16910
  mediaSectionState: state.mediaSection,
16096
16911
  isoState: state.iso,
16097
16912
  transportStream: state.transportStream,
@@ -16120,6 +16935,7 @@ var internalParseMedia = async function({
16120
16935
  state.iterator?.destroy();
16121
16936
  state.callbacks.tracks.ensureHasTracksAtEnd(state.fields);
16122
16937
  state.m3u.abortM3UStreamRuns();
16938
+ prefetchCache.clear();
16123
16939
  if (state.errored) {
16124
16940
  throw state.errored;
16125
16941
  }
@@ -16137,7 +16953,7 @@ var forwardMediaParserControllerToWorker = (controller) => {
16137
16953
  return;
16138
16954
  }
16139
16955
  if (message.type === "request-seek") {
16140
- controller._experimentalSeek(message.payload);
16956
+ controller.seek(message.payload);
16141
16957
  return;
16142
16958
  }
16143
16959
  if (message.type === "request-get-seeking-hints") {
@@ -16218,6 +17034,22 @@ var serializeError = ({
16218
17034
  seekingHints
16219
17035
  };
16220
17036
  }
17037
+ if (error.name === "AbortError") {
17038
+ return {
17039
+ type: "response-error",
17040
+ errorName: "AbortError",
17041
+ errorMessage: error.message,
17042
+ errorStack: error.stack ?? ""
17043
+ };
17044
+ }
17045
+ if (error.name === "NotReadableError") {
17046
+ return {
17047
+ type: "response-error",
17048
+ errorName: "NotReadableError",
17049
+ errorMessage: error.message,
17050
+ errorStack: error.stack ?? ""
17051
+ };
17052
+ }
16221
17053
  if (error.name !== "Error") {
16222
17054
  Log.warn(logLevel, `Original error was of type ${error.name} did not properly propagate`);
16223
17055
  }
@@ -16267,7 +17099,7 @@ var startParsing = async (message, reader) => {
16267
17099
  acknowledgeRemotionLicense,
16268
17100
  logLevel: userLogLevel,
16269
17101
  progressIntervalInMs,
16270
- mp4HeaderSegment,
17102
+ m3uPlaylistContext,
16271
17103
  seekingHints,
16272
17104
  makeSamplesStartAtZero
16273
17105
  } = payload;
@@ -16503,7 +17335,7 @@ var startParsing = async (message, reader) => {
16503
17335
  }
16504
17336
  return res.value;
16505
17337
  } : defaultSelectM3uStreamFn,
16506
- mp4HeaderSegment: mp4HeaderSegment ?? null,
17338
+ m3uPlaylistContext: m3uPlaylistContext ?? null,
16507
17339
  selectM3uAssociatedPlaylists: postM3uAssociatedPlaylistsSelection ? async (playlists) => {
16508
17340
  const res = await executeCallback({
16509
17341
  callbackType: "m3u-associated-playlists-selection",