@remotion/media-parser 4.0.271 → 4.0.272

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.
@@ -1,2 +1,2 @@
1
1
  import type { BufferIterator } from '../../buffer-iterator';
2
- export declare const getChannelCount: (iterator: BufferIterator) => 2 | 8 | 1 | 7 | 3 | 4 | 5 | 6;
2
+ export declare const getChannelCount: (iterator: BufferIterator) => 2 | 8 | 1 | 7 | 3 | 5 | 4 | 6;
@@ -1,2 +1,2 @@
1
1
  import type { FlacStructure } from './types';
2
- export declare const getMetadataFromFlac: (structure: FlacStructure) => import("../..").MetadataEntry[] | null;
2
+ export declare const getMetadataFromFlac: (structure: FlacStructure) => import("../..").MediaParserMetadataEntry[] | null;
@@ -7,8 +7,8 @@ import type { OnAudioSample, OnVideoSample } from '../../webcodec-sample-types';
7
7
  import type { M3uStructure } from './types';
8
8
  export declare const iteratorOverSegmentFiles: ({ structure, onVideoTrack, m3uState, onAudioTrack, onDoneWithTracks, playlistUrl, logLevel, parentController, onInitialProgress, readerInterface, }: {
9
9
  structure: M3uStructure;
10
- onVideoTrack: (track: VideoTrack) => Promise<OnVideoSample | null>;
11
- onAudioTrack: (track: AudioTrack) => Promise<OnAudioSample | null>;
10
+ onVideoTrack: null | ((track: VideoTrack) => Promise<OnVideoSample | null>);
11
+ onAudioTrack: null | ((track: AudioTrack) => Promise<OnAudioSample | null>);
12
12
  onDoneWithTracks: () => void;
13
13
  m3uState: M3uState;
14
14
  playlistUrl: string;
@@ -52,36 +52,40 @@ export const iteratorOverSegmentFiles = async ({ structure, onVideoTrack, m3uSta
52
52
  return null;
53
53
  }
54
54
  },
55
- onAudioTrack: async ({ track }) => {
56
- const callbackOrFalse = m3uState.hasEmittedAudioTrack(playlistUrl);
57
- if (callbackOrFalse === false) {
58
- const callback = await onAudioTrack(track);
59
- if (!callback) {
60
- m3uState.setHasEmittedAudioTrack(playlistUrl, null);
61
- return null;
55
+ onAudioTrack: onAudioTrack === null
56
+ ? null
57
+ : async ({ track }) => {
58
+ const callbackOrFalse = m3uState.hasEmittedAudioTrack(playlistUrl);
59
+ if (callbackOrFalse === false) {
60
+ const callback = await onAudioTrack(track);
61
+ if (!callback) {
62
+ m3uState.setHasEmittedAudioTrack(playlistUrl, null);
63
+ return null;
64
+ }
65
+ m3uState.setHasEmittedAudioTrack(playlistUrl, callback);
66
+ return (sample) => {
67
+ return callback(sample);
68
+ };
62
69
  }
63
- m3uState.setHasEmittedAudioTrack(playlistUrl, callback);
64
- return (sample) => {
65
- return callback(sample);
66
- };
67
- }
68
- return callbackOrFalse;
69
- },
70
- onVideoTrack: async ({ track }) => {
71
- const callbackOrFalse = m3uState.hasEmittedVideoTrack(playlistUrl);
72
- if (callbackOrFalse === false) {
73
- const callback = await onVideoTrack(track);
74
- if (!callback) {
75
- m3uState.setHasEmittedVideoTrack(playlistUrl, null);
76
- return null;
70
+ return callbackOrFalse;
71
+ },
72
+ onVideoTrack: onVideoTrack === null
73
+ ? null
74
+ : async ({ track }) => {
75
+ const callbackOrFalse = m3uState.hasEmittedVideoTrack(playlistUrl);
76
+ if (callbackOrFalse === false) {
77
+ const callback = await onVideoTrack(track);
78
+ if (!callback) {
79
+ m3uState.setHasEmittedVideoTrack(playlistUrl, null);
80
+ return null;
81
+ }
82
+ m3uState.setHasEmittedVideoTrack(playlistUrl, callback);
83
+ return (sample) => {
84
+ return callback(sample);
85
+ };
77
86
  }
78
- m3uState.setHasEmittedVideoTrack(playlistUrl, callback);
79
- return (sample) => {
80
- return callback(sample);
81
- };
82
- }
83
- return callbackOrFalse;
84
- },
87
+ return callbackOrFalse;
88
+ },
85
89
  reader: readerInterface,
86
90
  mp4HeaderSegment,
87
91
  });
@@ -82,6 +82,20 @@ export const parseM3uDirective = (str) => {
82
82
  const res = parseStreamInf(value);
83
83
  return res;
84
84
  }
85
+ if (directive === '#EXT-X-I-FRAME-STREAM-INF') {
86
+ return {
87
+ type: 'm3u-i-frame-stream-info',
88
+ };
89
+ }
90
+ if (directive === '#EXT-X-ALLOW-CACHE') {
91
+ if (!value) {
92
+ throw new Error('#EXT-X-ALLOW-CACHE directive must have a value');
93
+ }
94
+ return {
95
+ type: 'm3u-allow-cache',
96
+ allowsCache: value === 'YES',
97
+ };
98
+ }
85
99
  if (directive === '#EXT-X-MAP') {
86
100
  if (!value) {
87
101
  throw new Error('#EXT-X-MAP directive must have a value');
@@ -2,6 +2,16 @@ import { Log } from '../../log';
2
2
  import { registerAudioTrack, registerVideoTrack } from '../../register-track';
3
3
  import { iteratorOverSegmentFiles } from './iterate-over-segment-files';
4
4
  export const runOverM3u = async ({ state, structure, playlistUrl, logLevel, }) => {
5
+ const tracksDone = state.m3u.getTrackDone(playlistUrl);
6
+ const hasAudioStreamToConsider = state.m3u.sampleSorter.hasAudioStreamToConsider(playlistUrl);
7
+ const hasVideoStreamToConsider = state.m3u.sampleSorter.hasVideoStreamToConsider(playlistUrl);
8
+ const audioDone = !hasAudioStreamToConsider && tracksDone;
9
+ const videoDone = !hasVideoStreamToConsider && tracksDone;
10
+ const bothDone = audioDone && videoDone;
11
+ if (bothDone) {
12
+ state.m3u.setAllChunksProcessed(playlistUrl);
13
+ return;
14
+ }
5
15
  const existingRun = state.m3u.getM3uStreamRun(playlistUrl);
6
16
  if (existingRun) {
7
17
  Log.trace(logLevel, 'Existing M3U parsing process found for', playlistUrl);
@@ -28,52 +38,56 @@ export const runOverM3u = async ({ state, structure, playlistUrl, logLevel, }) =
28
38
  state.callbacks.tracks.setIsDone(state.logLevel);
29
39
  }
30
40
  },
31
- onAudioTrack: async (track) => {
32
- const existingTracks = state.callbacks.tracks.getTracks();
33
- let { trackId } = track;
34
- while (existingTracks.find((t) => t.trackId === trackId)) {
35
- trackId++;
36
- }
37
- const onAudioSample = await registerAudioTrack({
38
- container: 'm3u8',
39
- state,
40
- track: {
41
- ...track,
42
- trackId,
43
- },
44
- });
45
- state.m3u.sampleSorter.addToStreamWithTrack(playlistUrl);
46
- if (onAudioSample === null) {
47
- return null;
48
- }
49
- state.m3u.sampleSorter.addAudioStreamToConsider(playlistUrl, onAudioSample);
50
- return async (sample) => {
51
- await state.m3u.sampleSorter.addAudioSample(playlistUrl, sample);
52
- };
53
- },
54
- onVideoTrack: async (track) => {
55
- const existingTracks = state.callbacks.tracks.getTracks();
56
- let { trackId } = track;
57
- while (existingTracks.find((t) => t.trackId === trackId)) {
58
- trackId++;
59
- }
60
- const onVideoSample = await registerVideoTrack({
61
- container: 'm3u8',
62
- state,
63
- track: {
64
- ...track,
65
- trackId,
66
- },
67
- });
68
- state.m3u.sampleSorter.addToStreamWithTrack(playlistUrl);
69
- if (onVideoSample === null) {
70
- return null;
71
- }
72
- state.m3u.sampleSorter.addVideoStreamToConsider(playlistUrl, onVideoSample);
73
- return async (sample) => {
74
- await state.m3u.sampleSorter.addVideoSample(playlistUrl, sample);
75
- };
76
- },
41
+ onAudioTrack: audioDone
42
+ ? null
43
+ : async (track) => {
44
+ const existingTracks = state.callbacks.tracks.getTracks();
45
+ let { trackId } = track;
46
+ while (existingTracks.find((t) => t.trackId === trackId)) {
47
+ trackId++;
48
+ }
49
+ const onAudioSample = await registerAudioTrack({
50
+ container: 'm3u8',
51
+ state,
52
+ track: {
53
+ ...track,
54
+ trackId,
55
+ },
56
+ });
57
+ state.m3u.sampleSorter.addToStreamWithTrack(playlistUrl);
58
+ if (onAudioSample === null) {
59
+ return null;
60
+ }
61
+ state.m3u.sampleSorter.addAudioStreamToConsider(playlistUrl, onAudioSample);
62
+ return async (sample) => {
63
+ await state.m3u.sampleSorter.addAudioSample(playlistUrl, sample);
64
+ };
65
+ },
66
+ onVideoTrack: videoDone
67
+ ? null
68
+ : async (track) => {
69
+ const existingTracks = state.callbacks.tracks.getTracks();
70
+ let { trackId } = track;
71
+ while (existingTracks.find((t) => t.trackId === trackId)) {
72
+ trackId++;
73
+ }
74
+ const onVideoSample = await registerVideoTrack({
75
+ container: 'm3u8',
76
+ state,
77
+ track: {
78
+ ...track,
79
+ trackId,
80
+ },
81
+ });
82
+ state.m3u.sampleSorter.addToStreamWithTrack(playlistUrl);
83
+ if (onVideoSample === null) {
84
+ return null;
85
+ }
86
+ state.m3u.sampleSorter.addVideoStreamToConsider(playlistUrl, onVideoSample);
87
+ return async (sample) => {
88
+ await state.m3u.sampleSorter.addVideoSample(playlistUrl, sample);
89
+ };
90
+ },
77
91
  m3uState: state.m3u,
78
92
  parentController: state.controller,
79
93
  readerInterface: state.readerInterface,
@@ -7,6 +7,8 @@ export declare const sampleSorter: ({ logLevel, getAllChunksProcessedForPlaylist
7
7
  addToStreamWithTrack: (src: string) => void;
8
8
  addVideoStreamToConsider: (src: string, callback: OnVideoSample) => void;
9
9
  addAudioStreamToConsider: (src: string, callback: OnAudioSample) => void;
10
+ hasAudioStreamToConsider: (src: string) => boolean;
11
+ hasVideoStreamToConsider: (src: string) => boolean;
10
12
  addAudioSample: (src: string, sample: AudioOrVideoSample) => Promise<void>;
11
13
  addVideoSample: (src: string, sample: AudioOrVideoSample) => Promise<void>;
12
14
  getNextStreamToRun: (streams: string[]) => string;
@@ -14,6 +14,12 @@ export const sampleSorter = ({ logLevel, getAllChunksProcessedForPlaylist, }) =>
14
14
  addAudioStreamToConsider: (src, callback) => {
15
15
  audioCallbacks[src] = callback;
16
16
  },
17
+ hasAudioStreamToConsider: (src) => {
18
+ return Boolean(audioCallbacks[src]);
19
+ },
20
+ hasVideoStreamToConsider: (src) => {
21
+ return Boolean(videoCallbacks[src]);
22
+ },
17
23
  addAudioSample: async (src, sample) => {
18
24
  const callback = audioCallbacks[src];
19
25
  if (!callback) {
@@ -25,7 +31,7 @@ export const sampleSorter = ({ logLevel, getAllChunksProcessedForPlaylist, }) =>
25
31
  addVideoSample: async (src, sample) => {
26
32
  const callback = videoCallbacks[src];
27
33
  if (!callback) {
28
- throw new Error('No callback found for audio sample');
34
+ throw new Error('No callback found for video sample.');
29
35
  }
30
36
  latestSample[src] = sample.dts;
31
37
  await callback(sample);
@@ -33,6 +39,9 @@ export const sampleSorter = ({ logLevel, getAllChunksProcessedForPlaylist, }) =>
33
39
  getNextStreamToRun: (streams) => {
34
40
  var _a, _b, _c;
35
41
  for (const stream of streams) {
42
+ if (getAllChunksProcessedForPlaylist(stream)) {
43
+ continue;
44
+ }
36
45
  // If a stream does not have a track yet, work on that
37
46
  if (!streamsWithTracks.includes(stream)) {
38
47
  Log.trace(logLevel, `Did not yet detect track of ${stream}, working on that`);
@@ -49,6 +58,9 @@ export const sampleSorter = ({ logLevel, getAllChunksProcessedForPlaylist, }) =>
49
58
  }
50
59
  }
51
60
  for (const stream of streams) {
61
+ if (getAllChunksProcessedForPlaylist(stream)) {
62
+ continue;
63
+ }
52
64
  if (((_c = latestSample[stream]) !== null && _c !== void 0 ? _c : 0) === smallestDts) {
53
65
  Log.trace(logLevel, `Working on ${stream} because it has the smallest DTS`);
54
66
  return stream;
@@ -40,6 +40,9 @@ export type M3uDiscontinuitySequence = {
40
40
  type: 'm3u-discontinuity-sequence';
41
41
  value: number;
42
42
  };
43
+ export type M3uIFrameStreamInfo = {
44
+ type: 'm3u-i-frame-stream-info';
45
+ };
43
46
  export type M3uMap = {
44
47
  type: 'm3u-map';
45
48
  value: string;
@@ -69,7 +72,11 @@ export type M3uTextValue = {
69
72
  type: 'm3u-text-value';
70
73
  value: string;
71
74
  };
72
- export type M3uBox = M3uHeader | M3uPlaylist | M3uVersion | M3uIndependentSegments | M3uStreamInfo | M3uTargetDuration | M3uPlaylistType | M3uExtInf | M3uMedia | M3uMediaInfo | M3uEndList | M3uMediaSequence | M3uDiscontinuitySequence | M3uMap | M3uTextValue;
75
+ export type M3uAllowCache = {
76
+ type: 'm3u-allow-cache';
77
+ allowsCache: boolean;
78
+ };
79
+ export type M3uBox = M3uHeader | M3uPlaylist | M3uVersion | M3uIndependentSegments | M3uStreamInfo | M3uTargetDuration | M3uPlaylistType | M3uExtInf | M3uMedia | M3uMediaInfo | M3uEndList | M3uMediaSequence | M3uDiscontinuitySequence | M3uMap | M3uIFrameStreamInfo | M3uTextValue | M3uAllowCache;
73
80
  export type M3uStructure = {
74
81
  type: 'm3u';
75
82
  boxes: M3uBox[];
@@ -1,4 +1,6 @@
1
1
  import type { ParserState } from '../../state/parser-state';
2
2
  import type { AllTracks } from '../riff/get-tracks-from-avi';
3
+ import type { TransportStreamEntry } from './parse-pmt';
4
+ export declare const filterStreamsBySupportedTypes: (streams: TransportStreamEntry[]) => TransportStreamEntry[];
3
5
  export declare const getTracksFromTransportStream: (parserState: ParserState) => AllTracks;
4
6
  export declare const hasAllTracksFromTransportStream: (parserState: ParserState) => boolean;
@@ -1,15 +1,19 @@
1
1
  import { truthy } from '../../truthy';
2
2
  import { findProgramMapTableOrThrow } from './traversal';
3
+ export const filterStreamsBySupportedTypes = (streams) => {
4
+ return streams.filter((stream) => stream.streamType === 27 || stream.streamType === 15);
5
+ };
3
6
  export const getTracksFromTransportStream = (parserState) => {
4
7
  const structure = parserState.getTsStructure();
5
8
  const programMapTable = findProgramMapTableOrThrow(structure);
6
9
  const parserTracks = parserState.callbacks.tracks.getTracks();
7
- const mapped = programMapTable.streams
10
+ const mapped = filterStreamsBySupportedTypes(programMapTable.streams)
8
11
  .map((stream) => {
9
12
  return parserTracks.find((track) => track.trackId === stream.pid);
10
13
  })
11
14
  .filter(truthy);
12
- if (mapped.length !== programMapTable.streams.length) {
15
+ if (mapped.length !==
16
+ filterStreamsBySupportedTypes(programMapTable.streams).length) {
13
17
  throw new Error('Not all tracks found');
14
18
  }
15
19
  return {
@@ -8,11 +8,12 @@ const parsePmtTable = ({ iterator, tableId, sectionLength, }) => {
8
8
  const sectionNumber = iterator.getBits(8);
9
9
  const lastSectionNumber = iterator.getBits(8);
10
10
  const tables = [];
11
+ iterator.getBits(3); // reserved
12
+ iterator.getBits(13); // PCR PID
13
+ iterator.getBits(4); // reserved
14
+ const programInfoLength = iterator.getBits(12);
15
+ iterator.getBits(programInfoLength * 8); // program descriptor
11
16
  for (let i = sectionNumber; i <= lastSectionNumber; i++) {
12
- iterator.getBits(3); // reserved
13
- iterator.getBits(13); // PCR PID
14
- iterator.getBits(4); // reserved
15
- const programInfoLength = iterator.getBits(12);
16
17
  const streams = [];
17
18
  while (true) {
18
19
  const streamType = iterator.getBits(8);
@@ -22,7 +23,6 @@ const parsePmtTable = ({ iterator, tableId, sectionLength, }) => {
22
23
  const esInfoLength = iterator.getBits(12);
23
24
  iterator.getBits(esInfoLength * 8);
24
25
  streams.push({ streamType, pid: elementaryPid });
25
- iterator.getBits(programInfoLength * 8); // program descriptor
26
26
  const remaining = sectionLength - (iterator.counter.getOffset() - start);
27
27
  if (remaining <= 4) {
28
28
  break;
@@ -1,3 +1,4 @@
1
+ import { filterStreamsBySupportedTypes } from './get-tracks';
1
2
  import { handleAacPacket } from './handle-aac-packet';
2
3
  import { handleAvcPacket } from './handle-avc-packet';
3
4
  import { findProgramMapTableOrThrow, getStreamForId } from './traversal';
@@ -27,7 +28,7 @@ export const processStreamBuffer = async ({ streamBuffer, state, programId, stru
27
28
  if (!state.callbacks.tracks.hasAllTracks()) {
28
29
  const tracksRegistered = state.callbacks.tracks.getTracks().length;
29
30
  const { streams } = findProgramMapTableOrThrow(structure);
30
- if (streams.length === tracksRegistered) {
31
+ if (filterStreamsBySupportedTypes(streams).length === tracksRegistered) {
31
32
  state.callbacks.tracks.setIsDone(state.logLevel);
32
33
  }
33
34
  }
@@ -2479,14 +2479,17 @@ var getStreamForId = (structure, packetIdentifier) => {
2479
2479
  };
2480
2480
 
2481
2481
  // src/containers/transport-stream/get-tracks.ts
2482
+ var filterStreamsBySupportedTypes = (streams) => {
2483
+ return streams.filter((stream) => stream.streamType === 27 || stream.streamType === 15);
2484
+ };
2482
2485
  var getTracksFromTransportStream = (parserState) => {
2483
2486
  const structure = parserState.getTsStructure();
2484
2487
  const programMapTable = findProgramMapTableOrThrow(structure);
2485
2488
  const parserTracks = parserState.callbacks.tracks.getTracks();
2486
- const mapped = programMapTable.streams.map((stream) => {
2489
+ const mapped = filterStreamsBySupportedTypes(programMapTable.streams).map((stream) => {
2487
2490
  return parserTracks.find((track) => track.trackId === stream.pid);
2488
2491
  }).filter(truthy);
2489
- if (mapped.length !== programMapTable.streams.length) {
2492
+ if (mapped.length !== filterStreamsBySupportedTypes(programMapTable.streams).length) {
2490
2493
  throw new Error("Not all tracks found");
2491
2494
  }
2492
2495
  return {
@@ -8322,6 +8325,12 @@ var sampleSorter = ({
8322
8325
  addAudioStreamToConsider: (src, callback) => {
8323
8326
  audioCallbacks[src] = callback;
8324
8327
  },
8328
+ hasAudioStreamToConsider: (src) => {
8329
+ return Boolean(audioCallbacks[src]);
8330
+ },
8331
+ hasVideoStreamToConsider: (src) => {
8332
+ return Boolean(videoCallbacks[src]);
8333
+ },
8325
8334
  addAudioSample: async (src, sample) => {
8326
8335
  const callback = audioCallbacks[src];
8327
8336
  if (!callback) {
@@ -8333,13 +8342,16 @@ var sampleSorter = ({
8333
8342
  addVideoSample: async (src, sample) => {
8334
8343
  const callback = videoCallbacks[src];
8335
8344
  if (!callback) {
8336
- throw new Error("No callback found for audio sample");
8345
+ throw new Error("No callback found for video sample.");
8337
8346
  }
8338
8347
  latestSample[src] = sample.dts;
8339
8348
  await callback(sample);
8340
8349
  },
8341
8350
  getNextStreamToRun: (streams) => {
8342
8351
  for (const stream of streams) {
8352
+ if (getAllChunksProcessedForPlaylist(stream)) {
8353
+ continue;
8354
+ }
8343
8355
  if (!streamsWithTracks.includes(stream)) {
8344
8356
  Log.trace(logLevel, `Did not yet detect track of ${stream}, working on that`);
8345
8357
  return stream;
@@ -8355,6 +8367,9 @@ var sampleSorter = ({
8355
8367
  }
8356
8368
  }
8357
8369
  for (const stream of streams) {
8370
+ if (getAllChunksProcessedForPlaylist(stream)) {
8371
+ continue;
8372
+ }
8358
8373
  if ((latestSample[stream] ?? 0) === smallestDts) {
8359
8374
  Log.trace(logLevel, `Working on ${stream} because it has the smallest DTS`);
8360
8375
  return stream;
@@ -8426,7 +8441,7 @@ var m3uState = (logLevel) => {
8426
8441
  setHasEmittedDoneWithTracks: (src) => {
8427
8442
  hasEmittedDoneWithTracks[src] = true;
8428
8443
  },
8429
- hasEmittedDoneWithTracks: (src) => hasEmittedDoneWithTracks[src],
8444
+ hasEmittedDoneWithTracks: (src) => hasEmittedDoneWithTracks[src] !== undefined,
8430
8445
  setReadyToIterateOverM3u: () => {
8431
8446
  readyToIterateOverM3u = true;
8432
8447
  },
@@ -8458,6 +8473,9 @@ var m3uState = (logLevel) => {
8458
8473
  const selectedPlaylists = getSelectedPlaylists();
8459
8474
  return selectedPlaylists.every((url) => tracksDone[url]);
8460
8475
  },
8476
+ getTrackDone: (playlistUrl) => {
8477
+ return tracksDone[playlistUrl];
8478
+ },
8461
8479
  getM3uStreamRun: (playlistUrl) => m3uStreamRuns[playlistUrl] ?? null,
8462
8480
  abortM3UStreamRuns: () => {
8463
8481
  const values = Object.values(m3uStreamRuns);
@@ -8564,7 +8582,7 @@ var makeCanSkipTracksState = ({
8564
8582
  };
8565
8583
 
8566
8584
  // src/state/has-tracks-section.ts
8567
- var makeTracksSectionState = (canSkipTracksState) => {
8585
+ var makeTracksSectionState = (canSkipTracksState, src) => {
8568
8586
  const tracks2 = [];
8569
8587
  let doneWithTracks = false;
8570
8588
  return {
@@ -8589,7 +8607,7 @@ var makeTracksSectionState = (canSkipTracksState) => {
8589
8607
  return;
8590
8608
  }
8591
8609
  if (!doneWithTracks) {
8592
- throw new Error("Error in Media Parser: End of parsing has been reached, but no tracks have been found");
8610
+ throw new Error("Error in Media Parser: End of parsing of " + src + " has been reached, but no tracks have been found ");
8593
8611
  }
8594
8612
  }
8595
8613
  };
@@ -8604,7 +8622,8 @@ var sampleCallback = ({
8604
8622
  keyframes,
8605
8623
  emittedFields,
8606
8624
  slowDurationAndFpsState,
8607
- structure
8625
+ structure,
8626
+ src
8608
8627
  }) => {
8609
8628
  const videoSampleCallbacks = {};
8610
8629
  const audioSampleCallbacks = {};
@@ -8616,7 +8635,7 @@ var sampleCallback = ({
8616
8635
  hasVideoTrackHandlers,
8617
8636
  structure
8618
8637
  });
8619
- const tracksState = makeTracksSectionState(canSkipTracksState);
8638
+ const tracksState = makeTracksSectionState(canSkipTracksState, src);
8620
8639
  const samplesForTrack = {};
8621
8640
  return {
8622
8641
  registerVideoSampleCallback: async (id, callback) => {
@@ -9065,7 +9084,8 @@ var makeParserState = ({
9065
9084
  keyframes,
9066
9085
  emittedFields,
9067
9086
  slowDurationAndFpsState: slowDurationAndFps,
9068
- structure
9087
+ structure,
9088
+ src
9069
9089
  }),
9070
9090
  getInternalStats: () => ({
9071
9091
  skippedBytes,
@@ -9420,6 +9440,20 @@ var parseM3uDirective = (str) => {
9420
9440
  const res = parseStreamInf(value);
9421
9441
  return res;
9422
9442
  }
9443
+ if (directive === "#EXT-X-I-FRAME-STREAM-INF") {
9444
+ return {
9445
+ type: "m3u-i-frame-stream-info"
9446
+ };
9447
+ }
9448
+ if (directive === "#EXT-X-ALLOW-CACHE") {
9449
+ if (!value) {
9450
+ throw new Error("#EXT-X-ALLOW-CACHE directive must have a value");
9451
+ }
9452
+ return {
9453
+ type: "m3u-allow-cache",
9454
+ allowsCache: value === "YES"
9455
+ };
9456
+ }
9423
9457
  if (directive === "#EXT-X-MAP") {
9424
9458
  if (!value) {
9425
9459
  throw new Error("#EXT-X-MAP directive must have a value");
@@ -9993,7 +10027,7 @@ var iteratorOverSegmentFiles = async ({
9993
10027
  return null;
9994
10028
  }
9995
10029
  },
9996
- onAudioTrack: async ({ track }) => {
10030
+ onAudioTrack: onAudioTrack === null ? null : async ({ track }) => {
9997
10031
  const callbackOrFalse = m3uState2.hasEmittedAudioTrack(playlistUrl);
9998
10032
  if (callbackOrFalse === false) {
9999
10033
  const callback = await onAudioTrack(track);
@@ -10008,7 +10042,7 @@ var iteratorOverSegmentFiles = async ({
10008
10042
  }
10009
10043
  return callbackOrFalse;
10010
10044
  },
10011
- onVideoTrack: async ({ track }) => {
10045
+ onVideoTrack: onVideoTrack === null ? null : async ({ track }) => {
10012
10046
  const callbackOrFalse = m3uState2.hasEmittedVideoTrack(playlistUrl);
10013
10047
  if (callbackOrFalse === false) {
10014
10048
  const callback = await onVideoTrack(track);
@@ -10052,6 +10086,16 @@ var runOverM3u = async ({
10052
10086
  playlistUrl,
10053
10087
  logLevel
10054
10088
  }) => {
10089
+ const tracksDone = state.m3u.getTrackDone(playlistUrl);
10090
+ const hasAudioStreamToConsider = state.m3u.sampleSorter.hasAudioStreamToConsider(playlistUrl);
10091
+ const hasVideoStreamToConsider = state.m3u.sampleSorter.hasVideoStreamToConsider(playlistUrl);
10092
+ const audioDone = !hasAudioStreamToConsider && tracksDone;
10093
+ const videoDone = !hasVideoStreamToConsider && tracksDone;
10094
+ const bothDone = audioDone && videoDone;
10095
+ if (bothDone) {
10096
+ state.m3u.setAllChunksProcessed(playlistUrl);
10097
+ return;
10098
+ }
10055
10099
  const existingRun = state.m3u.getM3uStreamRun(playlistUrl);
10056
10100
  if (existingRun) {
10057
10101
  Log.trace(logLevel, "Existing M3U parsing process found for", playlistUrl);
@@ -10078,7 +10122,7 @@ var runOverM3u = async ({
10078
10122
  state.callbacks.tracks.setIsDone(state.logLevel);
10079
10123
  }
10080
10124
  },
10081
- onAudioTrack: async (track) => {
10125
+ onAudioTrack: audioDone ? null : async (track) => {
10082
10126
  const existingTracks = state.callbacks.tracks.getTracks();
10083
10127
  let { trackId } = track;
10084
10128
  while (existingTracks.find((t) => t.trackId === trackId)) {
@@ -10101,7 +10145,7 @@ var runOverM3u = async ({
10101
10145
  await state.m3u.sampleSorter.addAudioSample(playlistUrl, sample);
10102
10146
  };
10103
10147
  },
10104
- onVideoTrack: async (track) => {
10148
+ onVideoTrack: videoDone ? null : async (track) => {
10105
10149
  const existingTracks = state.callbacks.tracks.getTracks();
10106
10150
  let { trackId } = track;
10107
10151
  while (existingTracks.find((t) => t.trackId === trackId)) {
@@ -11175,11 +11219,12 @@ var parsePmtTable = ({
11175
11219
  const sectionNumber = iterator.getBits(8);
11176
11220
  const lastSectionNumber = iterator.getBits(8);
11177
11221
  const tables = [];
11222
+ iterator.getBits(3);
11223
+ iterator.getBits(13);
11224
+ iterator.getBits(4);
11225
+ const programInfoLength = iterator.getBits(12);
11226
+ iterator.getBits(programInfoLength * 8);
11178
11227
  for (let i = sectionNumber;i <= lastSectionNumber; i++) {
11179
- iterator.getBits(3);
11180
- iterator.getBits(13);
11181
- iterator.getBits(4);
11182
- const programInfoLength = iterator.getBits(12);
11183
11228
  const streams = [];
11184
11229
  while (true) {
11185
11230
  const streamType = iterator.getBits(8);
@@ -11189,7 +11234,6 @@ var parsePmtTable = ({
11189
11234
  const esInfoLength = iterator.getBits(12);
11190
11235
  iterator.getBits(esInfoLength * 8);
11191
11236
  streams.push({ streamType, pid: elementaryPid });
11192
- iterator.getBits(programInfoLength * 8);
11193
11237
  const remaining = sectionLength - (iterator.counter.getOffset() - start);
11194
11238
  if (remaining <= 4) {
11195
11239
  break;
@@ -11476,7 +11520,7 @@ var processStreamBuffer = async ({
11476
11520
  if (!state.callbacks.tracks.hasAllTracks()) {
11477
11521
  const tracksRegistered = state.callbacks.tracks.getTracks().length;
11478
11522
  const { streams } = findProgramMapTableOrThrow(structure);
11479
- if (streams.length === tracksRegistered) {
11523
+ if (filterStreamsBySupportedTypes(streams).length === tracksRegistered) {
11480
11524
  state.callbacks.tracks.setIsDone(state.logLevel);
11481
11525
  }
11482
11526
  }
@@ -12957,7 +13001,7 @@ var downloadAndParseMedia = async (options) => {
12957
13001
  return returnValue;
12958
13002
  };
12959
13003
  // src/version.ts
12960
- var VERSION = "4.0.271";
13004
+ var VERSION = "4.0.272";
12961
13005
 
12962
13006
  // src/index.ts
12963
13007
  var MediaParserInternals = {
@@ -3131,14 +3131,17 @@ var getStreamForId = (structure, packetIdentifier) => {
3131
3131
  };
3132
3132
 
3133
3133
  // src/containers/transport-stream/get-tracks.ts
3134
+ var filterStreamsBySupportedTypes = (streams) => {
3135
+ return streams.filter((stream) => stream.streamType === 27 || stream.streamType === 15);
3136
+ };
3134
3137
  var getTracksFromTransportStream = (parserState) => {
3135
3138
  const structure = parserState.getTsStructure();
3136
3139
  const programMapTable = findProgramMapTableOrThrow(structure);
3137
3140
  const parserTracks = parserState.callbacks.tracks.getTracks();
3138
- const mapped = programMapTable.streams.map((stream) => {
3141
+ const mapped = filterStreamsBySupportedTypes(programMapTable.streams).map((stream) => {
3139
3142
  return parserTracks.find((track) => track.trackId === stream.pid);
3140
3143
  }).filter(truthy);
3141
- if (mapped.length !== programMapTable.streams.length) {
3144
+ if (mapped.length !== filterStreamsBySupportedTypes(programMapTable.streams).length) {
3142
3145
  throw new Error("Not all tracks found");
3143
3146
  }
3144
3147
  return {
@@ -6516,6 +6519,12 @@ var sampleSorter = ({
6516
6519
  addAudioStreamToConsider: (src, callback) => {
6517
6520
  audioCallbacks[src] = callback;
6518
6521
  },
6522
+ hasAudioStreamToConsider: (src) => {
6523
+ return Boolean(audioCallbacks[src]);
6524
+ },
6525
+ hasVideoStreamToConsider: (src) => {
6526
+ return Boolean(videoCallbacks[src]);
6527
+ },
6519
6528
  addAudioSample: async (src, sample) => {
6520
6529
  const callback = audioCallbacks[src];
6521
6530
  if (!callback) {
@@ -6527,13 +6536,16 @@ var sampleSorter = ({
6527
6536
  addVideoSample: async (src, sample) => {
6528
6537
  const callback = videoCallbacks[src];
6529
6538
  if (!callback) {
6530
- throw new Error("No callback found for audio sample");
6539
+ throw new Error("No callback found for video sample.");
6531
6540
  }
6532
6541
  latestSample[src] = sample.dts;
6533
6542
  await callback(sample);
6534
6543
  },
6535
6544
  getNextStreamToRun: (streams) => {
6536
6545
  for (const stream of streams) {
6546
+ if (getAllChunksProcessedForPlaylist(stream)) {
6547
+ continue;
6548
+ }
6537
6549
  if (!streamsWithTracks.includes(stream)) {
6538
6550
  Log.trace(logLevel, `Did not yet detect track of ${stream}, working on that`);
6539
6551
  return stream;
@@ -6549,6 +6561,9 @@ var sampleSorter = ({
6549
6561
  }
6550
6562
  }
6551
6563
  for (const stream of streams) {
6564
+ if (getAllChunksProcessedForPlaylist(stream)) {
6565
+ continue;
6566
+ }
6552
6567
  if ((latestSample[stream] ?? 0) === smallestDts) {
6553
6568
  Log.trace(logLevel, `Working on ${stream} because it has the smallest DTS`);
6554
6569
  return stream;
@@ -6620,7 +6635,7 @@ var m3uState = (logLevel) => {
6620
6635
  setHasEmittedDoneWithTracks: (src) => {
6621
6636
  hasEmittedDoneWithTracks[src] = true;
6622
6637
  },
6623
- hasEmittedDoneWithTracks: (src) => hasEmittedDoneWithTracks[src],
6638
+ hasEmittedDoneWithTracks: (src) => hasEmittedDoneWithTracks[src] !== undefined,
6624
6639
  setReadyToIterateOverM3u: () => {
6625
6640
  readyToIterateOverM3u = true;
6626
6641
  },
@@ -6652,6 +6667,9 @@ var m3uState = (logLevel) => {
6652
6667
  const selectedPlaylists = getSelectedPlaylists();
6653
6668
  return selectedPlaylists.every((url) => tracksDone[url]);
6654
6669
  },
6670
+ getTrackDone: (playlistUrl) => {
6671
+ return tracksDone[playlistUrl];
6672
+ },
6655
6673
  getM3uStreamRun: (playlistUrl) => m3uStreamRuns[playlistUrl] ?? null,
6656
6674
  abortM3UStreamRuns: () => {
6657
6675
  const values = Object.values(m3uStreamRuns);
@@ -6758,7 +6776,7 @@ var makeCanSkipTracksState = ({
6758
6776
  };
6759
6777
 
6760
6778
  // src/state/has-tracks-section.ts
6761
- var makeTracksSectionState = (canSkipTracksState) => {
6779
+ var makeTracksSectionState = (canSkipTracksState, src) => {
6762
6780
  const tracks2 = [];
6763
6781
  let doneWithTracks = false;
6764
6782
  return {
@@ -6783,7 +6801,7 @@ var makeTracksSectionState = (canSkipTracksState) => {
6783
6801
  return;
6784
6802
  }
6785
6803
  if (!doneWithTracks) {
6786
- throw new Error("Error in Media Parser: End of parsing has been reached, but no tracks have been found");
6804
+ throw new Error("Error in Media Parser: End of parsing of " + src + " has been reached, but no tracks have been found ");
6787
6805
  }
6788
6806
  }
6789
6807
  };
@@ -6798,7 +6816,8 @@ var sampleCallback = ({
6798
6816
  keyframes,
6799
6817
  emittedFields,
6800
6818
  slowDurationAndFpsState,
6801
- structure
6819
+ structure,
6820
+ src
6802
6821
  }) => {
6803
6822
  const videoSampleCallbacks = {};
6804
6823
  const audioSampleCallbacks = {};
@@ -6810,7 +6829,7 @@ var sampleCallback = ({
6810
6829
  hasVideoTrackHandlers,
6811
6830
  structure
6812
6831
  });
6813
- const tracksState = makeTracksSectionState(canSkipTracksState);
6832
+ const tracksState = makeTracksSectionState(canSkipTracksState, src);
6814
6833
  const samplesForTrack = {};
6815
6834
  return {
6816
6835
  registerVideoSampleCallback: async (id, callback) => {
@@ -7259,7 +7278,8 @@ var makeParserState = ({
7259
7278
  keyframes,
7260
7279
  emittedFields,
7261
7280
  slowDurationAndFpsState: slowDurationAndFps,
7262
- structure
7281
+ structure,
7282
+ src
7263
7283
  }),
7264
7284
  getInternalStats: () => ({
7265
7285
  skippedBytes,
@@ -9242,6 +9262,20 @@ var parseM3uDirective = (str) => {
9242
9262
  const res = parseStreamInf(value);
9243
9263
  return res;
9244
9264
  }
9265
+ if (directive === "#EXT-X-I-FRAME-STREAM-INF") {
9266
+ return {
9267
+ type: "m3u-i-frame-stream-info"
9268
+ };
9269
+ }
9270
+ if (directive === "#EXT-X-ALLOW-CACHE") {
9271
+ if (!value) {
9272
+ throw new Error("#EXT-X-ALLOW-CACHE directive must have a value");
9273
+ }
9274
+ return {
9275
+ type: "m3u-allow-cache",
9276
+ allowsCache: value === "YES"
9277
+ };
9278
+ }
9245
9279
  if (directive === "#EXT-X-MAP") {
9246
9280
  if (!value) {
9247
9281
  throw new Error("#EXT-X-MAP directive must have a value");
@@ -9548,7 +9582,7 @@ var iteratorOverSegmentFiles = async ({
9548
9582
  return null;
9549
9583
  }
9550
9584
  },
9551
- onAudioTrack: async ({ track }) => {
9585
+ onAudioTrack: onAudioTrack === null ? null : async ({ track }) => {
9552
9586
  const callbackOrFalse = m3uState2.hasEmittedAudioTrack(playlistUrl);
9553
9587
  if (callbackOrFalse === false) {
9554
9588
  const callback = await onAudioTrack(track);
@@ -9563,7 +9597,7 @@ var iteratorOverSegmentFiles = async ({
9563
9597
  }
9564
9598
  return callbackOrFalse;
9565
9599
  },
9566
- onVideoTrack: async ({ track }) => {
9600
+ onVideoTrack: onVideoTrack === null ? null : async ({ track }) => {
9567
9601
  const callbackOrFalse = m3uState2.hasEmittedVideoTrack(playlistUrl);
9568
9602
  if (callbackOrFalse === false) {
9569
9603
  const callback = await onVideoTrack(track);
@@ -9607,6 +9641,16 @@ var runOverM3u = async ({
9607
9641
  playlistUrl,
9608
9642
  logLevel
9609
9643
  }) => {
9644
+ const tracksDone = state.m3u.getTrackDone(playlistUrl);
9645
+ const hasAudioStreamToConsider = state.m3u.sampleSorter.hasAudioStreamToConsider(playlistUrl);
9646
+ const hasVideoStreamToConsider = state.m3u.sampleSorter.hasVideoStreamToConsider(playlistUrl);
9647
+ const audioDone = !hasAudioStreamToConsider && tracksDone;
9648
+ const videoDone = !hasVideoStreamToConsider && tracksDone;
9649
+ const bothDone = audioDone && videoDone;
9650
+ if (bothDone) {
9651
+ state.m3u.setAllChunksProcessed(playlistUrl);
9652
+ return;
9653
+ }
9610
9654
  const existingRun = state.m3u.getM3uStreamRun(playlistUrl);
9611
9655
  if (existingRun) {
9612
9656
  Log.trace(logLevel, "Existing M3U parsing process found for", playlistUrl);
@@ -9633,7 +9677,7 @@ var runOverM3u = async ({
9633
9677
  state.callbacks.tracks.setIsDone(state.logLevel);
9634
9678
  }
9635
9679
  },
9636
- onAudioTrack: async (track) => {
9680
+ onAudioTrack: audioDone ? null : async (track) => {
9637
9681
  const existingTracks = state.callbacks.tracks.getTracks();
9638
9682
  let { trackId } = track;
9639
9683
  while (existingTracks.find((t) => t.trackId === trackId)) {
@@ -9656,7 +9700,7 @@ var runOverM3u = async ({
9656
9700
  await state.m3u.sampleSorter.addAudioSample(playlistUrl, sample);
9657
9701
  };
9658
9702
  },
9659
- onVideoTrack: async (track) => {
9703
+ onVideoTrack: videoDone ? null : async (track) => {
9660
9704
  const existingTracks = state.callbacks.tracks.getTracks();
9661
9705
  let { trackId } = track;
9662
9706
  while (existingTracks.find((t) => t.trackId === trackId)) {
@@ -10959,11 +11003,12 @@ var parsePmtTable = ({
10959
11003
  const sectionNumber = iterator.getBits(8);
10960
11004
  const lastSectionNumber = iterator.getBits(8);
10961
11005
  const tables = [];
11006
+ iterator.getBits(3);
11007
+ iterator.getBits(13);
11008
+ iterator.getBits(4);
11009
+ const programInfoLength = iterator.getBits(12);
11010
+ iterator.getBits(programInfoLength * 8);
10962
11011
  for (let i = sectionNumber;i <= lastSectionNumber; i++) {
10963
- iterator.getBits(3);
10964
- iterator.getBits(13);
10965
- iterator.getBits(4);
10966
- const programInfoLength = iterator.getBits(12);
10967
11012
  const streams = [];
10968
11013
  while (true) {
10969
11014
  const streamType = iterator.getBits(8);
@@ -10973,7 +11018,6 @@ var parsePmtTable = ({
10973
11018
  const esInfoLength = iterator.getBits(12);
10974
11019
  iterator.getBits(esInfoLength * 8);
10975
11020
  streams.push({ streamType, pid: elementaryPid });
10976
- iterator.getBits(programInfoLength * 8);
10977
11021
  const remaining = sectionLength - (iterator.counter.getOffset() - start);
10978
11022
  if (remaining <= 4) {
10979
11023
  break;
@@ -11260,7 +11304,7 @@ var processStreamBuffer = async ({
11260
11304
  if (!state.callbacks.tracks.hasAllTracks()) {
11261
11305
  const tracksRegistered = state.callbacks.tracks.getTracks().length;
11262
11306
  const { streams } = findProgramMapTableOrThrow(structure);
11263
- if (streams.length === tracksRegistered) {
11307
+ if (filterStreamsBySupportedTypes(streams).length === tracksRegistered) {
11264
11308
  state.callbacks.tracks.setIsDone(state.logLevel);
11265
11309
  }
11266
11310
  }
@@ -3066,14 +3066,17 @@ var getStreamForId = (structure, packetIdentifier) => {
3066
3066
  };
3067
3067
 
3068
3068
  // src/containers/transport-stream/get-tracks.ts
3069
+ var filterStreamsBySupportedTypes = (streams) => {
3070
+ return streams.filter((stream) => stream.streamType === 27 || stream.streamType === 15);
3071
+ };
3069
3072
  var getTracksFromTransportStream = (parserState) => {
3070
3073
  const structure = parserState.getTsStructure();
3071
3074
  const programMapTable = findProgramMapTableOrThrow(structure);
3072
3075
  const parserTracks = parserState.callbacks.tracks.getTracks();
3073
- const mapped = programMapTable.streams.map((stream) => {
3076
+ const mapped = filterStreamsBySupportedTypes(programMapTable.streams).map((stream) => {
3074
3077
  return parserTracks.find((track) => track.trackId === stream.pid);
3075
3078
  }).filter(truthy);
3076
- if (mapped.length !== programMapTable.streams.length) {
3079
+ if (mapped.length !== filterStreamsBySupportedTypes(programMapTable.streams).length) {
3077
3080
  throw new Error("Not all tracks found");
3078
3081
  }
3079
3082
  return {
@@ -6451,6 +6454,12 @@ var sampleSorter = ({
6451
6454
  addAudioStreamToConsider: (src, callback) => {
6452
6455
  audioCallbacks[src] = callback;
6453
6456
  },
6457
+ hasAudioStreamToConsider: (src) => {
6458
+ return Boolean(audioCallbacks[src]);
6459
+ },
6460
+ hasVideoStreamToConsider: (src) => {
6461
+ return Boolean(videoCallbacks[src]);
6462
+ },
6454
6463
  addAudioSample: async (src, sample) => {
6455
6464
  const callback = audioCallbacks[src];
6456
6465
  if (!callback) {
@@ -6462,13 +6471,16 @@ var sampleSorter = ({
6462
6471
  addVideoSample: async (src, sample) => {
6463
6472
  const callback = videoCallbacks[src];
6464
6473
  if (!callback) {
6465
- throw new Error("No callback found for audio sample");
6474
+ throw new Error("No callback found for video sample.");
6466
6475
  }
6467
6476
  latestSample[src] = sample.dts;
6468
6477
  await callback(sample);
6469
6478
  },
6470
6479
  getNextStreamToRun: (streams) => {
6471
6480
  for (const stream of streams) {
6481
+ if (getAllChunksProcessedForPlaylist(stream)) {
6482
+ continue;
6483
+ }
6472
6484
  if (!streamsWithTracks.includes(stream)) {
6473
6485
  Log.trace(logLevel, `Did not yet detect track of ${stream}, working on that`);
6474
6486
  return stream;
@@ -6484,6 +6496,9 @@ var sampleSorter = ({
6484
6496
  }
6485
6497
  }
6486
6498
  for (const stream of streams) {
6499
+ if (getAllChunksProcessedForPlaylist(stream)) {
6500
+ continue;
6501
+ }
6487
6502
  if ((latestSample[stream] ?? 0) === smallestDts) {
6488
6503
  Log.trace(logLevel, `Working on ${stream} because it has the smallest DTS`);
6489
6504
  return stream;
@@ -6555,7 +6570,7 @@ var m3uState = (logLevel) => {
6555
6570
  setHasEmittedDoneWithTracks: (src) => {
6556
6571
  hasEmittedDoneWithTracks[src] = true;
6557
6572
  },
6558
- hasEmittedDoneWithTracks: (src) => hasEmittedDoneWithTracks[src],
6573
+ hasEmittedDoneWithTracks: (src) => hasEmittedDoneWithTracks[src] !== undefined,
6559
6574
  setReadyToIterateOverM3u: () => {
6560
6575
  readyToIterateOverM3u = true;
6561
6576
  },
@@ -6587,6 +6602,9 @@ var m3uState = (logLevel) => {
6587
6602
  const selectedPlaylists = getSelectedPlaylists();
6588
6603
  return selectedPlaylists.every((url) => tracksDone[url]);
6589
6604
  },
6605
+ getTrackDone: (playlistUrl) => {
6606
+ return tracksDone[playlistUrl];
6607
+ },
6590
6608
  getM3uStreamRun: (playlistUrl) => m3uStreamRuns[playlistUrl] ?? null,
6591
6609
  abortM3UStreamRuns: () => {
6592
6610
  const values = Object.values(m3uStreamRuns);
@@ -6693,7 +6711,7 @@ var makeCanSkipTracksState = ({
6693
6711
  };
6694
6712
 
6695
6713
  // src/state/has-tracks-section.ts
6696
- var makeTracksSectionState = (canSkipTracksState) => {
6714
+ var makeTracksSectionState = (canSkipTracksState, src) => {
6697
6715
  const tracks2 = [];
6698
6716
  let doneWithTracks = false;
6699
6717
  return {
@@ -6718,7 +6736,7 @@ var makeTracksSectionState = (canSkipTracksState) => {
6718
6736
  return;
6719
6737
  }
6720
6738
  if (!doneWithTracks) {
6721
- throw new Error("Error in Media Parser: End of parsing has been reached, but no tracks have been found");
6739
+ throw new Error("Error in Media Parser: End of parsing of " + src + " has been reached, but no tracks have been found ");
6722
6740
  }
6723
6741
  }
6724
6742
  };
@@ -6733,7 +6751,8 @@ var sampleCallback = ({
6733
6751
  keyframes,
6734
6752
  emittedFields,
6735
6753
  slowDurationAndFpsState,
6736
- structure
6754
+ structure,
6755
+ src
6737
6756
  }) => {
6738
6757
  const videoSampleCallbacks = {};
6739
6758
  const audioSampleCallbacks = {};
@@ -6745,7 +6764,7 @@ var sampleCallback = ({
6745
6764
  hasVideoTrackHandlers,
6746
6765
  structure
6747
6766
  });
6748
- const tracksState = makeTracksSectionState(canSkipTracksState);
6767
+ const tracksState = makeTracksSectionState(canSkipTracksState, src);
6749
6768
  const samplesForTrack = {};
6750
6769
  return {
6751
6770
  registerVideoSampleCallback: async (id, callback) => {
@@ -7194,7 +7213,8 @@ var makeParserState = ({
7194
7213
  keyframes,
7195
7214
  emittedFields,
7196
7215
  slowDurationAndFpsState: slowDurationAndFps,
7197
- structure
7216
+ structure,
7217
+ src
7198
7218
  }),
7199
7219
  getInternalStats: () => ({
7200
7220
  skippedBytes,
@@ -9177,6 +9197,20 @@ var parseM3uDirective = (str) => {
9177
9197
  const res = parseStreamInf(value);
9178
9198
  return res;
9179
9199
  }
9200
+ if (directive === "#EXT-X-I-FRAME-STREAM-INF") {
9201
+ return {
9202
+ type: "m3u-i-frame-stream-info"
9203
+ };
9204
+ }
9205
+ if (directive === "#EXT-X-ALLOW-CACHE") {
9206
+ if (!value) {
9207
+ throw new Error("#EXT-X-ALLOW-CACHE directive must have a value");
9208
+ }
9209
+ return {
9210
+ type: "m3u-allow-cache",
9211
+ allowsCache: value === "YES"
9212
+ };
9213
+ }
9180
9214
  if (directive === "#EXT-X-MAP") {
9181
9215
  if (!value) {
9182
9216
  throw new Error("#EXT-X-MAP directive must have a value");
@@ -9461,7 +9495,7 @@ var iteratorOverSegmentFiles = async ({
9461
9495
  return null;
9462
9496
  }
9463
9497
  },
9464
- onAudioTrack: async ({ track }) => {
9498
+ onAudioTrack: onAudioTrack === null ? null : async ({ track }) => {
9465
9499
  const callbackOrFalse = m3uState2.hasEmittedAudioTrack(playlistUrl);
9466
9500
  if (callbackOrFalse === false) {
9467
9501
  const callback = await onAudioTrack(track);
@@ -9476,7 +9510,7 @@ var iteratorOverSegmentFiles = async ({
9476
9510
  }
9477
9511
  return callbackOrFalse;
9478
9512
  },
9479
- onVideoTrack: async ({ track }) => {
9513
+ onVideoTrack: onVideoTrack === null ? null : async ({ track }) => {
9480
9514
  const callbackOrFalse = m3uState2.hasEmittedVideoTrack(playlistUrl);
9481
9515
  if (callbackOrFalse === false) {
9482
9516
  const callback = await onVideoTrack(track);
@@ -9520,6 +9554,16 @@ var runOverM3u = async ({
9520
9554
  playlistUrl,
9521
9555
  logLevel
9522
9556
  }) => {
9557
+ const tracksDone = state.m3u.getTrackDone(playlistUrl);
9558
+ const hasAudioStreamToConsider = state.m3u.sampleSorter.hasAudioStreamToConsider(playlistUrl);
9559
+ const hasVideoStreamToConsider = state.m3u.sampleSorter.hasVideoStreamToConsider(playlistUrl);
9560
+ const audioDone = !hasAudioStreamToConsider && tracksDone;
9561
+ const videoDone = !hasVideoStreamToConsider && tracksDone;
9562
+ const bothDone = audioDone && videoDone;
9563
+ if (bothDone) {
9564
+ state.m3u.setAllChunksProcessed(playlistUrl);
9565
+ return;
9566
+ }
9523
9567
  const existingRun = state.m3u.getM3uStreamRun(playlistUrl);
9524
9568
  if (existingRun) {
9525
9569
  Log.trace(logLevel, "Existing M3U parsing process found for", playlistUrl);
@@ -9546,7 +9590,7 @@ var runOverM3u = async ({
9546
9590
  state.callbacks.tracks.setIsDone(state.logLevel);
9547
9591
  }
9548
9592
  },
9549
- onAudioTrack: async (track) => {
9593
+ onAudioTrack: audioDone ? null : async (track) => {
9550
9594
  const existingTracks = state.callbacks.tracks.getTracks();
9551
9595
  let { trackId } = track;
9552
9596
  while (existingTracks.find((t) => t.trackId === trackId)) {
@@ -9569,7 +9613,7 @@ var runOverM3u = async ({
9569
9613
  await state.m3u.sampleSorter.addAudioSample(playlistUrl, sample);
9570
9614
  };
9571
9615
  },
9572
- onVideoTrack: async (track) => {
9616
+ onVideoTrack: videoDone ? null : async (track) => {
9573
9617
  const existingTracks = state.callbacks.tracks.getTracks();
9574
9618
  let { trackId } = track;
9575
9619
  while (existingTracks.find((t) => t.trackId === trackId)) {
@@ -10872,11 +10916,12 @@ var parsePmtTable = ({
10872
10916
  const sectionNumber = iterator.getBits(8);
10873
10917
  const lastSectionNumber = iterator.getBits(8);
10874
10918
  const tables = [];
10919
+ iterator.getBits(3);
10920
+ iterator.getBits(13);
10921
+ iterator.getBits(4);
10922
+ const programInfoLength = iterator.getBits(12);
10923
+ iterator.getBits(programInfoLength * 8);
10875
10924
  for (let i = sectionNumber;i <= lastSectionNumber; i++) {
10876
- iterator.getBits(3);
10877
- iterator.getBits(13);
10878
- iterator.getBits(4);
10879
- const programInfoLength = iterator.getBits(12);
10880
10925
  const streams = [];
10881
10926
  while (true) {
10882
10927
  const streamType = iterator.getBits(8);
@@ -10886,7 +10931,6 @@ var parsePmtTable = ({
10886
10931
  const esInfoLength = iterator.getBits(12);
10887
10932
  iterator.getBits(esInfoLength * 8);
10888
10933
  streams.push({ streamType, pid: elementaryPid });
10889
- iterator.getBits(programInfoLength * 8);
10890
10934
  const remaining = sectionLength - (iterator.counter.getOffset() - start);
10891
10935
  if (remaining <= 4) {
10892
10936
  break;
@@ -11173,7 +11217,7 @@ var processStreamBuffer = async ({
11173
11217
  if (!state.callbacks.tracks.hasAllTracks()) {
11174
11218
  const tracksRegistered = state.callbacks.tracks.getTracks().length;
11175
11219
  const { streams } = findProgramMapTableOrThrow(structure);
11176
- if (streams.length === tracksRegistered) {
11220
+ if (filterStreamsBySupportedTypes(streams).length === tracksRegistered) {
11177
11221
  state.callbacks.tracks.setIsDone(state.logLevel);
11178
11222
  }
11179
11223
  }
package/dist/index.d.ts CHANGED
@@ -940,6 +940,7 @@ export declare const MediaParserInternals: {
940
940
  hasFinishedManifest: () => boolean;
941
941
  setM3uStreamRun: (playlistUrl: string, run: import("./state/m3u-state").ExistingM3uRun | null) => void;
942
942
  setTracksDone: (playlistUrl: string) => boolean;
943
+ getTrackDone: (playlistUrl: string) => boolean;
943
944
  getM3uStreamRun: (playlistUrl: string) => import("./state/m3u-state").ExistingM3uRun;
944
945
  abortM3UStreamRuns: () => void;
945
946
  setAssociatedPlaylists: (playlists: import("./containers/m3u/get-streams").M3uAssociatedPlaylist[]) => void;
@@ -949,6 +950,8 @@ export declare const MediaParserInternals: {
949
950
  addToStreamWithTrack: (src: string) => void;
950
951
  addVideoStreamToConsider: (src: string, callback: import("./webcodec-sample-types").OnVideoSample) => void;
951
952
  addAudioStreamToConsider: (src: string, callback: import("./webcodec-sample-types").OnAudioSample) => void;
953
+ hasAudioStreamToConsider: (src: string) => boolean;
954
+ hasVideoStreamToConsider: (src: string) => boolean;
952
955
  addAudioSample: (src: string, sample: import("./webcodec-sample-types").AudioOrVideoSample) => Promise<void>;
953
956
  addVideoSample: (src: string, sample: import("./webcodec-sample-types").AudioOrVideoSample) => Promise<void>;
954
957
  getNextStreamToRun: (streams: string[]) => string;
@@ -1,8 +1,8 @@
1
1
  import type { Track } from '../get-tracks';
2
2
  import type { LogLevel } from '../log';
3
- import type { Options, ParseMediaFields } from '../options';
3
+ import type { Options, ParseMediaFields, ParseMediaSrc } from '../options';
4
4
  import type { CanSkipTracksState } from './can-skip-tracks';
5
- export declare const makeTracksSectionState: (canSkipTracksState: CanSkipTracksState) => {
5
+ export declare const makeTracksSectionState: (canSkipTracksState: CanSkipTracksState, src: ParseMediaSrc) => {
6
6
  hasAllTracks: () => boolean;
7
7
  getIsDone: () => boolean;
8
8
  setIsDone: (logLevel: LogLevel) => void;
@@ -1,5 +1,5 @@
1
1
  import { Log } from '../log';
2
- export const makeTracksSectionState = (canSkipTracksState) => {
2
+ export const makeTracksSectionState = (canSkipTracksState, src) => {
3
3
  const tracks = [];
4
4
  let doneWithTracks = false;
5
5
  return {
@@ -24,7 +24,9 @@ export const makeTracksSectionState = (canSkipTracksState) => {
24
24
  return;
25
25
  }
26
26
  if (!doneWithTracks) {
27
- throw new Error('Error in Media Parser: End of parsing has been reached, but no tracks have been found');
27
+ throw new Error('Error in Media Parser: End of parsing of ' +
28
+ src +
29
+ ' has been reached, but no tracks have been found ');
28
30
  }
29
31
  },
30
32
  };
@@ -31,6 +31,7 @@ export declare const m3uState: (logLevel: LogLevel) => {
31
31
  hasFinishedManifest: () => boolean;
32
32
  setM3uStreamRun: (playlistUrl: string, run: ExistingM3uRun | null) => void;
33
33
  setTracksDone: (playlistUrl: string) => boolean;
34
+ getTrackDone: (playlistUrl: string) => boolean;
34
35
  getM3uStreamRun: (playlistUrl: string) => ExistingM3uRun;
35
36
  abortM3UStreamRuns: () => void;
36
37
  setAssociatedPlaylists: (playlists: M3uAssociatedPlaylist[]) => void;
@@ -40,6 +41,8 @@ export declare const m3uState: (logLevel: LogLevel) => {
40
41
  addToStreamWithTrack: (src: string) => void;
41
42
  addVideoStreamToConsider: (src: string, callback: OnVideoSample) => void;
42
43
  addAudioStreamToConsider: (src: string, callback: OnAudioSample) => void;
44
+ hasAudioStreamToConsider: (src: string) => boolean;
45
+ hasVideoStreamToConsider: (src: string) => boolean;
43
46
  addAudioSample: (src: string, sample: import("../webcodec-sample-types").AudioOrVideoSample) => Promise<void>;
44
47
  addVideoSample: (src: string, sample: import("../webcodec-sample-types").AudioOrVideoSample) => Promise<void>;
45
48
  getNextStreamToRun: (streams: string[]) => string;
@@ -62,7 +62,7 @@ export const m3uState = (logLevel) => {
62
62
  setHasEmittedDoneWithTracks: (src) => {
63
63
  hasEmittedDoneWithTracks[src] = true;
64
64
  },
65
- hasEmittedDoneWithTracks: (src) => hasEmittedDoneWithTracks[src],
65
+ hasEmittedDoneWithTracks: (src) => hasEmittedDoneWithTracks[src] !== undefined,
66
66
  setReadyToIterateOverM3u: () => {
67
67
  readyToIterateOverM3u = true;
68
68
  },
@@ -94,6 +94,9 @@ export const m3uState = (logLevel) => {
94
94
  const selectedPlaylists = getSelectedPlaylists();
95
95
  return selectedPlaylists.every((url) => tracksDone[url]);
96
96
  },
97
+ getTrackDone: (playlistUrl) => {
98
+ return tracksDone[playlistUrl];
99
+ },
97
100
  getM3uStreamRun: (playlistUrl) => { var _a; return (_a = m3uStreamRuns[playlistUrl]) !== null && _a !== void 0 ? _a : null; },
98
101
  abortM3UStreamRuns: () => {
99
102
  const values = Object.values(m3uStreamRuns);
@@ -226,6 +226,7 @@ export declare const makeParserState: ({ hasAudioTrackHandlers, hasVideoTrackHan
226
226
  hasFinishedManifest: () => boolean;
227
227
  setM3uStreamRun: (playlistUrl: string, run: import("./m3u-state").ExistingM3uRun | null) => void;
228
228
  setTracksDone: (playlistUrl: string) => boolean;
229
+ getTrackDone: (playlistUrl: string) => boolean;
229
230
  getM3uStreamRun: (playlistUrl: string) => import("./m3u-state").ExistingM3uRun;
230
231
  abortM3UStreamRuns: () => void;
231
232
  setAssociatedPlaylists: (playlists: import("..").M3uAssociatedPlaylist[]) => void;
@@ -235,6 +236,8 @@ export declare const makeParserState: ({ hasAudioTrackHandlers, hasVideoTrackHan
235
236
  addToStreamWithTrack: (src: string) => void;
236
237
  addVideoStreamToConsider: (src: string, callback: import("../webcodec-sample-types").OnVideoSample) => void;
237
238
  addAudioStreamToConsider: (src: string, callback: import("../webcodec-sample-types").OnAudioSample) => void;
239
+ hasAudioStreamToConsider: (src: string) => boolean;
240
+ hasVideoStreamToConsider: (src: string) => boolean;
238
241
  addAudioSample: (src: string, sample: import("../webcodec-sample-types").AudioOrVideoSample) => Promise<void>;
239
242
  addVideoSample: (src: string, sample: import("../webcodec-sample-types").AudioOrVideoSample) => Promise<void>;
240
243
  getNextStreamToRun: (streams: string[]) => string;
@@ -54,6 +54,7 @@ export const makeParserState = ({ hasAudioTrackHandlers, hasVideoTrackHandlers,
54
54
  emittedFields,
55
55
  slowDurationAndFpsState: slowDurationAndFps,
56
56
  structure,
57
+ src,
57
58
  }),
58
59
  getInternalStats: () => {
59
60
  var _a;
@@ -1,10 +1,10 @@
1
1
  import type { MediaParserController } from '../media-parser-controller';
2
- import type { AllOptions, Options, ParseMediaFields } from '../options';
2
+ import type { AllOptions, Options, ParseMediaFields, ParseMediaSrc } from '../options';
3
3
  import type { AudioOrVideoSample, OnAudioSample, OnVideoSample } from '../webcodec-sample-types';
4
4
  import { type KeyframesState } from './keyframes';
5
5
  import type { SlowDurationAndFpsState } from './slow-duration-fps';
6
6
  import type { StructureState } from './structure';
7
- export declare const sampleCallback: ({ controller, hasAudioTrackHandlers, hasVideoTrackHandlers, fields, keyframes, emittedFields, slowDurationAndFpsState, structure, }: {
7
+ export declare const sampleCallback: ({ controller, hasAudioTrackHandlers, hasVideoTrackHandlers, fields, keyframes, emittedFields, slowDurationAndFpsState, structure, src, }: {
8
8
  controller: MediaParserController;
9
9
  hasAudioTrackHandlers: boolean;
10
10
  hasVideoTrackHandlers: boolean;
@@ -13,6 +13,7 @@ export declare const sampleCallback: ({ controller, hasAudioTrackHandlers, hasVi
13
13
  emittedFields: AllOptions<ParseMediaFields>;
14
14
  slowDurationAndFpsState: SlowDurationAndFpsState;
15
15
  structure: StructureState;
16
+ src: ParseMediaSrc;
16
17
  }) => {
17
18
  registerVideoSampleCallback: (id: number, callback: OnVideoSample | null) => Promise<void>;
18
19
  onAudioSample: (trackId: number, audioSample: AudioOrVideoSample) => Promise<void>;
@@ -1,7 +1,7 @@
1
1
  import { makeCanSkipTracksState } from './can-skip-tracks';
2
2
  import { makeTracksSectionState } from './has-tracks-section';
3
3
  import { needsToIterateOverSamples } from './need-samples-for-fields';
4
- export const sampleCallback = ({ controller, hasAudioTrackHandlers, hasVideoTrackHandlers, fields, keyframes, emittedFields, slowDurationAndFpsState, structure, }) => {
4
+ export const sampleCallback = ({ controller, hasAudioTrackHandlers, hasVideoTrackHandlers, fields, keyframes, emittedFields, slowDurationAndFpsState, structure, src, }) => {
5
5
  const videoSampleCallbacks = {};
6
6
  const audioSampleCallbacks = {};
7
7
  const queuedAudioSamples = {};
@@ -12,7 +12,7 @@ export const sampleCallback = ({ controller, hasAudioTrackHandlers, hasVideoTrac
12
12
  hasVideoTrackHandlers,
13
13
  structure,
14
14
  });
15
- const tracksState = makeTracksSectionState(canSkipTracksState);
15
+ const tracksState = makeTracksSectionState(canSkipTracksState, src);
16
16
  const samplesForTrack = {};
17
17
  return {
18
18
  registerVideoSampleCallback: async (id, callback) => {
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "4.0.271";
1
+ export declare const VERSION = "4.0.272";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Automatically generated on publish
2
- export const VERSION = '4.0.271';
2
+ export const VERSION = '4.0.272';
package/package.json CHANGED
@@ -3,15 +3,15 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/media-parser"
4
4
  },
5
5
  "name": "@remotion/media-parser",
6
- "version": "4.0.271",
6
+ "version": "4.0.272",
7
7
  "main": "dist/index.js",
8
8
  "sideEffects": false,
9
9
  "devDependencies": {
10
10
  "@types/wicg-file-system-access": "2023.10.5",
11
11
  "eslint": "9.19.0",
12
12
  "@types/bun": "1.2.0",
13
- "@remotion/example-videos": "4.0.271",
14
- "@remotion/eslint-config-internal": "4.0.271"
13
+ "@remotion/example-videos": "4.0.272",
14
+ "@remotion/eslint-config-internal": "4.0.272"
15
15
  },
16
16
  "publishConfig": {
17
17
  "access": "public"