@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.
- package/dist/containers/iso-base-media/base-media-box.d.ts +0 -1
- package/dist/containers/iso-base-media/collect-sample-positions-from-moof-boxes.d.ts +4 -1
- package/dist/containers/iso-base-media/collect-sample-positions-from-moof-boxes.js +9 -5
- package/dist/containers/iso-base-media/find-keyframe-before-time.js +16 -11
- package/dist/containers/iso-base-media/find-track-to-seek.d.ts +14 -0
- package/dist/containers/iso-base-media/find-track-to-seek.js +39 -0
- package/dist/containers/iso-base-media/get-children.js +2 -2
- package/dist/containers/iso-base-media/get-keyframes.js +6 -1
- package/dist/containers/iso-base-media/get-mfra-seeking-box.d.ts +3 -1
- package/dist/containers/iso-base-media/get-mfra-seeking-box.js +5 -1
- package/dist/containers/iso-base-media/get-moov-atom.js +6 -3
- package/dist/containers/iso-base-media/get-sample-position-bounds.js +3 -1
- package/dist/containers/iso-base-media/get-sample-positions-from-track.js +1 -1
- package/dist/containers/iso-base-media/get-seeking-byte-from-fragmented-mp4.d.ts +14 -0
- package/dist/containers/iso-base-media/get-seeking-byte-from-fragmented-mp4.js +89 -0
- package/dist/containers/iso-base-media/get-seeking-byte.d.ts +3 -3
- package/dist/containers/iso-base-media/get-seeking-byte.js +32 -96
- package/dist/containers/iso-base-media/mdat/calculate-jump-marks.d.ts +6 -0
- package/dist/containers/iso-base-media/mdat/calculate-jump-marks.js +131 -0
- package/dist/containers/iso-base-media/mdat/mdat.d.ts +2 -2
- package/dist/containers/iso-base-media/mdat/mdat.js +18 -2
- package/dist/containers/iso-base-media/mfra/find-best-segment-from-tfra.d.ts +3 -3
- package/dist/containers/iso-base-media/mfra/find-best-segment-from-tfra.js +2 -2
- package/dist/containers/iso-base-media/mfra/get-mfra-atom.d.ts +5 -1
- package/dist/containers/iso-base-media/mfra/get-mfra-atom.js +3 -1
- package/dist/containers/iso-base-media/mfra/get-mfro-atom.d.ts +5 -1
- package/dist/containers/iso-base-media/mfra/get-mfro-atom.js +3 -1
- package/dist/containers/iso-base-media/parse-boxes.js +5 -2
- package/dist/containers/iso-base-media/process-box.d.ts +16 -5
- package/dist/containers/iso-base-media/process-box.js +206 -118
- package/dist/containers/iso-base-media/sample-positions.d.ts +25 -0
- package/dist/containers/iso-base-media/sample-positions.js +37 -0
- package/dist/containers/iso-base-media/stsd/samples.js +1 -0
- package/dist/containers/iso-base-media/stsd/stsc.d.ts +1 -6
- package/dist/containers/iso-base-media/stsd/stsc.js +2 -5
- package/dist/containers/iso-base-media/stsd/stss.d.ts +1 -1
- package/dist/containers/iso-base-media/stsd/stss.js +2 -2
- package/dist/containers/iso-base-media/turn-sample-positions-into-array.d.ts +19 -0
- package/dist/containers/iso-base-media/turn-sample-positions-into-array.js +73 -0
- package/dist/containers/m3u/after-manifest-fetch.d.ts +5 -1
- package/dist/containers/m3u/after-manifest-fetch.js +3 -1
- package/dist/containers/m3u/first-sample-in-m3u-chunk.d.ts +13 -0
- package/dist/containers/m3u/first-sample-in-m3u-chunk.js +31 -0
- package/dist/containers/m3u/get-seeking-byte.d.ts +13 -0
- package/dist/containers/m3u/get-seeking-byte.js +32 -0
- package/dist/containers/m3u/get-streams.d.ts +1 -0
- package/dist/containers/m3u/get-streams.js +1 -0
- package/dist/containers/m3u/iterate-over-segment-files.d.ts +5 -3
- package/dist/containers/m3u/iterate-over-segment-files.js +11 -1
- package/dist/containers/m3u/parse-m3u-media-directive.js +1 -0
- package/dist/containers/m3u/parse-m3u.js +8 -0
- package/dist/containers/m3u/process-m3u-chunk.d.ts +12 -0
- package/dist/containers/m3u/process-m3u-chunk.js +274 -0
- package/dist/containers/m3u/run-over-m3u.js +7 -80
- package/dist/containers/m3u/sample-sorter.d.ts +1 -0
- package/dist/containers/m3u/sample-sorter.js +4 -1
- package/dist/containers/m3u/seek/get-chunk-to-seek-to.d.ts +5 -0
- package/dist/containers/m3u/seek/get-chunk-to-seek-to.js +14 -0
- package/dist/containers/m3u/seeking-hints.d.ts +2 -0
- package/dist/containers/m3u/seeking-hints.js +9 -0
- package/dist/containers/m3u/select-stream.d.ts +2 -1
- package/dist/containers/m3u/select-stream.js +7 -2
- package/dist/containers/m3u/types.d.ts +1 -0
- package/dist/containers/riff/seek/fetch-idx1.d.ts +3 -1
- package/dist/containers/riff/seek/fetch-idx1.js +3 -1
- package/dist/containers/transport-stream/handle-aac-packet.d.ts +2 -2
- package/dist/containers/transport-stream/handle-avc-packet.d.ts +2 -2
- package/dist/containers/transport-stream/process-audio.d.ts +2 -2
- package/dist/containers/transport-stream/process-stream-buffers.d.ts +3 -3
- package/dist/containers/transport-stream/process-video.d.ts +2 -2
- package/dist/containers/webm/get-sample-from-block.d.ts +12 -2
- package/dist/containers/webm/get-sample-from-block.js +40 -9
- package/dist/containers/webm/parse-ebml.js +28 -10
- package/dist/containers/webm/seek/fetch-web-cues.d.ts +3 -1
- package/dist/containers/webm/seek/fetch-web-cues.js +3 -1
- package/dist/containers/webm/state-for-processing.d.ts +2 -2
- package/dist/controller/media-parser-controller.d.ts +1 -1
- package/dist/controller/media-parser-controller.js +6 -2
- package/dist/controller/seek-signal.d.ts +1 -5
- package/dist/download-and-parse-media.js +1 -1
- package/dist/esm/index.mjs +1400 -611
- package/dist/esm/node.mjs +23 -3
- package/dist/esm/server-worker.mjs +8 -1
- package/dist/esm/universal.mjs +168 -15
- package/dist/esm/web.mjs +145 -13
- package/dist/esm/worker-server-entry.mjs +1467 -635
- package/dist/esm/worker-web-entry.mjs +1439 -634
- package/dist/esm/worker.mjs +8 -1
- package/dist/get-audio-codec.js +3 -0
- package/dist/get-duration.js +2 -1
- package/dist/get-fps.js +2 -1
- package/dist/get-sample-positions-from-mp4.js +10 -5
- package/dist/get-sample-positions.js +4 -4
- package/dist/get-seeking-byte.d.ts +5 -3
- package/dist/get-seeking-byte.js +19 -10
- package/dist/get-seeking-hints.d.ts +3 -3
- package/dist/get-seeking-hints.js +18 -13
- package/dist/get-tracks.d.ts +9 -1
- package/dist/get-tracks.js +13 -6
- package/dist/index.d.ts +21 -5
- package/dist/init-video.js +3 -2
- package/dist/internal-parse-media.js +13 -4
- package/dist/iterator/buffer-iterator.js +5 -3
- package/dist/metadata/metadata-from-iso.js +2 -1
- package/dist/options.d.ts +6 -1
- package/dist/parse-loop.js +22 -6
- package/dist/parse-media-on-worker-entry.js +1 -0
- package/dist/parse-media.js +1 -1
- package/dist/parse-result.d.ts +2 -2
- package/dist/perform-seek.d.ts +3 -1
- package/dist/perform-seek.js +3 -1
- package/dist/readers/fetch/get-body-and-reader.js +17 -2
- package/dist/readers/from-fetch.d.ts +17 -1
- package/dist/readers/from-fetch.js +68 -13
- package/dist/readers/from-node.js +24 -2
- package/dist/readers/from-web-file.js +3 -0
- package/dist/readers/reader.d.ts +19 -2
- package/dist/readers/universal.js +9 -0
- package/dist/readers/web.js +6 -0
- package/dist/register-track.d.ts +3 -3
- package/dist/seek-backwards.d.ts +3 -1
- package/dist/seek-backwards.js +4 -1
- package/dist/seek-forwards.d.ts +3 -1
- package/dist/seek-forwards.js +3 -1
- package/dist/seeking-hints.d.ts +4 -1
- package/dist/set-seeking-hints.js +4 -0
- package/dist/skip.d.ts +5 -0
- package/dist/skip.js +6 -1
- package/dist/state/can-skip-tracks.d.ts +1 -0
- package/dist/state/can-skip-tracks.js +10 -6
- package/dist/state/iso-base-media/cached-sample-positions.d.ts +15 -1
- package/dist/state/iso-base-media/cached-sample-positions.js +9 -4
- package/dist/state/iso-base-media/iso-state.d.ts +5 -1
- package/dist/state/iso-base-media/iso-state.js +2 -1
- package/dist/state/iso-base-media/lazy-mfra-load.d.ts +3 -1
- package/dist/state/iso-base-media/lazy-mfra-load.js +2 -1
- package/dist/state/keyframes.js +1 -0
- package/dist/state/m3u-state.d.ts +15 -4
- package/dist/state/m3u-state.js +20 -0
- package/dist/state/matroska/lazy-cues-fetch.d.ts +3 -1
- package/dist/state/matroska/lazy-cues-fetch.js +2 -1
- package/dist/state/matroska/webm.d.ts +3 -1
- package/dist/state/matroska/webm.js +2 -1
- package/dist/state/parser-state.d.ts +29 -13
- package/dist/state/parser-state.js +19 -5
- package/dist/state/riff/lazy-idx1-fetch.d.ts +3 -1
- package/dist/state/riff/lazy-idx1-fetch.js +2 -1
- package/dist/state/riff.d.ts +3 -1
- package/dist/state/riff.js +2 -1
- package/dist/state/sample-callbacks.d.ts +3 -2
- package/dist/state/sample-callbacks.js +3 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/work-on-seek-request.d.ts +6 -3
- package/dist/work-on-seek-request.js +13 -13
- package/dist/worker/forward-controller-to-worker.js +1 -1
- package/dist/worker/serialize-error.js +26 -3
- package/dist/worker/worker-types.d.ts +7 -1
- package/dist/worker-server.js +2 -2
- package/package.json +3 -3
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { LogLevel } from '../../log';
|
|
2
2
|
import type { ReaderInterface } from '../../readers/reader';
|
|
3
|
+
import type { CanSkipTracksState } from '../../state/can-skip-tracks';
|
|
3
4
|
import type { M3uState } from '../../state/m3u-state';
|
|
5
|
+
import type { OnAudioTrack } from '../../webcodec-sample-types';
|
|
4
6
|
import type { SelectM3uAssociatedPlaylistsFn, SelectM3uStreamFn } from './select-stream';
|
|
5
7
|
import type { M3uStructure } from './types';
|
|
6
|
-
export declare const afterManifestFetch: ({ structure, m3uState, src, selectM3uStreamFn, logLevel, selectAssociatedPlaylistsFn, readerInterface, }: {
|
|
8
|
+
export declare const afterManifestFetch: ({ structure, m3uState, src, selectM3uStreamFn, logLevel, selectAssociatedPlaylistsFn, readerInterface, onAudioTrack, canSkipTracks, }: {
|
|
7
9
|
structure: M3uStructure;
|
|
8
10
|
m3uState: M3uState;
|
|
9
11
|
src: string;
|
|
@@ -11,4 +13,6 @@ export declare const afterManifestFetch: ({ structure, m3uState, src, selectM3uS
|
|
|
11
13
|
selectAssociatedPlaylistsFn: SelectM3uAssociatedPlaylistsFn;
|
|
12
14
|
logLevel: LogLevel;
|
|
13
15
|
readerInterface: ReaderInterface;
|
|
16
|
+
onAudioTrack: OnAudioTrack | null;
|
|
17
|
+
canSkipTracks: CanSkipTracksState;
|
|
14
18
|
}) => Promise<void>;
|
|
@@ -5,7 +5,7 @@ const log_1 = require("../../log");
|
|
|
5
5
|
const fetch_m3u8_stream_1 = require("./fetch-m3u8-stream");
|
|
6
6
|
const get_streams_1 = require("./get-streams");
|
|
7
7
|
const select_stream_1 = require("./select-stream");
|
|
8
|
-
const afterManifestFetch = async ({ structure, m3uState, src, selectM3uStreamFn, logLevel, selectAssociatedPlaylistsFn, readerInterface, }) => {
|
|
8
|
+
const afterManifestFetch = async ({ structure, m3uState, src, selectM3uStreamFn, logLevel, selectAssociatedPlaylistsFn, readerInterface, onAudioTrack, canSkipTracks, }) => {
|
|
9
9
|
const independentSegments = (0, get_streams_1.isIndependentSegments)(structure);
|
|
10
10
|
if (!independentSegments) {
|
|
11
11
|
if (!src) {
|
|
@@ -29,9 +29,11 @@ const afterManifestFetch = async ({ structure, m3uState, src, selectM3uStreamFn,
|
|
|
29
29
|
type: 'selected-stream',
|
|
30
30
|
stream: selectedPlaylist,
|
|
31
31
|
});
|
|
32
|
+
const skipAudioTracks = onAudioTrack === null && canSkipTracks.doFieldsNeedTracks() === false;
|
|
32
33
|
const associatedPlaylists = await (0, select_stream_1.selectAssociatedPlaylists)({
|
|
33
34
|
playlists: selectedPlaylist.associatedPlaylists,
|
|
34
35
|
fn: selectAssociatedPlaylistsFn,
|
|
36
|
+
skipAudioTracks,
|
|
35
37
|
});
|
|
36
38
|
m3uState.setAssociatedPlaylists(associatedPlaylists);
|
|
37
39
|
const playlistUrls = [
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { MediaParserController } from '../../controller/media-parser-controller';
|
|
2
|
+
import type { M3uState } from '../../state/m3u-state';
|
|
3
|
+
import type { AudioOrVideoSample } from '../../webcodec-sample-types';
|
|
4
|
+
export declare const considerSeekBasedOnChunk: ({ sample, parentController, childController, callback, m3uState, playlistUrl, subtractChunks, chunkIndex, }: {
|
|
5
|
+
sample: AudioOrVideoSample;
|
|
6
|
+
callback: (sample: AudioOrVideoSample) => void | Promise<void>;
|
|
7
|
+
parentController: MediaParserController;
|
|
8
|
+
childController: MediaParserController;
|
|
9
|
+
playlistUrl: string;
|
|
10
|
+
m3uState: M3uState;
|
|
11
|
+
subtractChunks: number;
|
|
12
|
+
chunkIndex: number | null;
|
|
13
|
+
}) => Promise<void>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.considerSeekBasedOnChunk = void 0;
|
|
4
|
+
const considerSeekBasedOnChunk = async ({ sample, parentController, childController, callback, m3uState, playlistUrl, subtractChunks, chunkIndex, }) => {
|
|
5
|
+
const pendingSeek = m3uState.getSeekToSecondsToProcess(playlistUrl);
|
|
6
|
+
// If there is not even a seek to consider, just call the callback
|
|
7
|
+
if (pendingSeek === null) {
|
|
8
|
+
await callback(sample);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const timestamp = Math.min(sample.dts / sample.timescale, sample.cts / sample.timescale);
|
|
12
|
+
// Already too far, now we should go to the previous chunk
|
|
13
|
+
if (timestamp > pendingSeek.targetTime &&
|
|
14
|
+
chunkIndex !== null &&
|
|
15
|
+
chunkIndex > 0) {
|
|
16
|
+
m3uState.setNextSeekShouldSubtractChunks(playlistUrl, subtractChunks + 1);
|
|
17
|
+
parentController.seek({
|
|
18
|
+
type: 'keyframe-before-time',
|
|
19
|
+
timeInSeconds: pendingSeek.targetTime,
|
|
20
|
+
});
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
// We are good, we have not gone too far! Don't emit sample and seek and clear pending seek
|
|
24
|
+
childController.seek({
|
|
25
|
+
type: 'keyframe-before-time',
|
|
26
|
+
timeInSeconds: pendingSeek.targetTime,
|
|
27
|
+
});
|
|
28
|
+
m3uState.setNextSeekShouldSubtractChunks(playlistUrl, 0);
|
|
29
|
+
m3uState.setSeekToSecondsToProcess(playlistUrl, null);
|
|
30
|
+
};
|
|
31
|
+
exports.considerSeekBasedOnChunk = considerSeekBasedOnChunk;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { LogLevel } from '../../log';
|
|
2
|
+
import type { M3uState } from '../../state/m3u-state';
|
|
3
|
+
import type { SeekResolution } from '../../work-on-seek-request';
|
|
4
|
+
export declare const clearM3uStateInPrepareForSeek: ({ m3uState, logLevel, }: {
|
|
5
|
+
m3uState: M3uState;
|
|
6
|
+
logLevel: LogLevel;
|
|
7
|
+
}) => void;
|
|
8
|
+
export declare const getSeekingByteForM3u8: ({ time, currentPosition, m3uState, logLevel, }: {
|
|
9
|
+
time: number;
|
|
10
|
+
currentPosition: number;
|
|
11
|
+
m3uState: M3uState;
|
|
12
|
+
logLevel: LogLevel;
|
|
13
|
+
}) => SeekResolution;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSeekingByteForM3u8 = exports.clearM3uStateInPrepareForSeek = void 0;
|
|
4
|
+
const log_1 = require("../../log");
|
|
5
|
+
const clearM3uStateInPrepareForSeek = ({ m3uState, logLevel, }) => {
|
|
6
|
+
const selectedPlaylists = m3uState.getSelectedPlaylists();
|
|
7
|
+
for (const playlistUrl of selectedPlaylists) {
|
|
8
|
+
const streamRun = m3uState.getM3uStreamRun(playlistUrl);
|
|
9
|
+
if (streamRun) {
|
|
10
|
+
streamRun.abort();
|
|
11
|
+
}
|
|
12
|
+
log_1.Log.trace(logLevel, 'Clearing M3U stream run for', playlistUrl);
|
|
13
|
+
m3uState.setM3uStreamRun(playlistUrl, null);
|
|
14
|
+
}
|
|
15
|
+
m3uState.clearAllChunksProcessed();
|
|
16
|
+
m3uState.sampleSorter.clearSamples();
|
|
17
|
+
};
|
|
18
|
+
exports.clearM3uStateInPrepareForSeek = clearM3uStateInPrepareForSeek;
|
|
19
|
+
const getSeekingByteForM3u8 = ({ time, currentPosition, m3uState, logLevel, }) => {
|
|
20
|
+
(0, exports.clearM3uStateInPrepareForSeek)({ m3uState, logLevel });
|
|
21
|
+
const selectedPlaylists = m3uState.getSelectedPlaylists();
|
|
22
|
+
for (const playlistUrl of selectedPlaylists) {
|
|
23
|
+
m3uState.setSeekToSecondsToProcess(playlistUrl, {
|
|
24
|
+
targetTime: time,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
type: 'do-seek',
|
|
29
|
+
byte: currentPosition,
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
exports.getSeekingByteForM3u8 = getSeekingByteForM3u8;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { MediaParserController } from '../../controller/media-parser-controller';
|
|
2
|
+
import type { PrefetchCache } from '../../fetch';
|
|
2
3
|
import type { AudioTrack, VideoTrack } from '../../get-tracks';
|
|
3
4
|
import type { LogLevel } from '../../log';
|
|
4
5
|
import type { ReaderInterface } from '../../readers/reader';
|
|
5
|
-
import type {
|
|
6
|
+
import type { M3uRun, M3uState } from '../../state/m3u-state';
|
|
6
7
|
import type { OnAudioSample, OnVideoSample } from '../../webcodec-sample-types';
|
|
7
8
|
import type { M3uStructure } from './types';
|
|
8
|
-
export declare const iteratorOverSegmentFiles: ({ structure, onVideoTrack, m3uState, onAudioTrack, onDoneWithTracks, playlistUrl, logLevel, parentController, onInitialProgress, readerInterface, }: {
|
|
9
|
+
export declare const iteratorOverSegmentFiles: ({ structure, onVideoTrack, m3uState, onAudioTrack, onDoneWithTracks, playlistUrl, logLevel, parentController, onInitialProgress, readerInterface, prefetchCache, }: {
|
|
9
10
|
structure: M3uStructure;
|
|
10
11
|
onVideoTrack: null | ((track: VideoTrack) => Promise<OnVideoSample | null>);
|
|
11
12
|
onAudioTrack: null | ((track: AudioTrack) => Promise<OnAudioSample | null>);
|
|
@@ -14,6 +15,7 @@ export declare const iteratorOverSegmentFiles: ({ structure, onVideoTrack, m3uSt
|
|
|
14
15
|
playlistUrl: string;
|
|
15
16
|
logLevel: LogLevel;
|
|
16
17
|
parentController: MediaParserController;
|
|
17
|
-
onInitialProgress: (run:
|
|
18
|
+
onInitialProgress: (run: M3uRun | null) => void;
|
|
18
19
|
readerInterface: ReaderInterface;
|
|
20
|
+
prefetchCache: PrefetchCache;
|
|
19
21
|
}) => Promise<void>;
|
|
@@ -7,7 +7,7 @@ const parse_media_1 = require("../../parse-media");
|
|
|
7
7
|
const with_resolvers_1 = require("../../with-resolvers");
|
|
8
8
|
const get_chunks_1 = require("./get-chunks");
|
|
9
9
|
const get_playlist_1 = require("./get-playlist");
|
|
10
|
-
const iteratorOverSegmentFiles = async ({ structure, onVideoTrack, m3uState, onAudioTrack, onDoneWithTracks, playlistUrl, logLevel, parentController, onInitialProgress, readerInterface, }) => {
|
|
10
|
+
const iteratorOverSegmentFiles = async ({ structure, onVideoTrack, m3uState, onAudioTrack, onDoneWithTracks, playlistUrl, logLevel, parentController, onInitialProgress, readerInterface, prefetchCache, }) => {
|
|
11
11
|
const playlist = (0, get_playlist_1.getPlaylist)(structure, playlistUrl);
|
|
12
12
|
const chunks = (0, get_chunks_1.getChunks)(playlist);
|
|
13
13
|
let resolver = onInitialProgress;
|
|
@@ -22,6 +22,16 @@ const iteratorOverSegmentFiles = async ({ structure, onVideoTrack, m3uState, onA
|
|
|
22
22
|
childController,
|
|
23
23
|
parentController,
|
|
24
24
|
});
|
|
25
|
+
const nextChunk = chunks[chunks.indexOf(chunk) + 1];
|
|
26
|
+
if (nextChunk) {
|
|
27
|
+
const nextChunkSource = readerInterface.createAdjacentFileSource(nextChunk.url, playlistUrl);
|
|
28
|
+
readerInterface.preload({
|
|
29
|
+
logLevel,
|
|
30
|
+
range: null,
|
|
31
|
+
src: nextChunkSource,
|
|
32
|
+
prefetchCache,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
25
35
|
const makeContinuationFn = () => {
|
|
26
36
|
return {
|
|
27
37
|
continue() {
|
|
@@ -21,6 +21,12 @@ const parseM3u = async ({ state }) => {
|
|
|
21
21
|
if (typeof state.src !== 'string' && !(state.src instanceof URL)) {
|
|
22
22
|
throw new Error('Expected src to be a string');
|
|
23
23
|
}
|
|
24
|
+
state.mediaSection.addMediaSection({
|
|
25
|
+
start: 0,
|
|
26
|
+
// We do a pseudo-seek when seeking m3u, which will be the same byte
|
|
27
|
+
// as we are currently in, which in most cases is the end of the file.
|
|
28
|
+
size: state.contentLength + 1,
|
|
29
|
+
});
|
|
24
30
|
await (0, after_manifest_fetch_1.afterManifestFetch)({
|
|
25
31
|
structure,
|
|
26
32
|
m3uState: state.m3u,
|
|
@@ -29,6 +35,8 @@ const parseM3u = async ({ state }) => {
|
|
|
29
35
|
logLevel: state.logLevel,
|
|
30
36
|
selectAssociatedPlaylistsFn: state.selectM3uAssociatedPlaylistsFn,
|
|
31
37
|
readerInterface: state.readerInterface,
|
|
38
|
+
onAudioTrack: state.onAudioTrack,
|
|
39
|
+
canSkipTracks: state.callbacks.canSkipTracksState,
|
|
32
40
|
});
|
|
33
41
|
return null;
|
|
34
42
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ParserState } from '../../state/parser-state';
|
|
2
|
+
import type { M3uStructure } from './types';
|
|
3
|
+
export type PendingSeek = {
|
|
4
|
+
pending: number | null;
|
|
5
|
+
};
|
|
6
|
+
export declare const processM3uChunk: ({ playlistUrl, state, structure, audioDone, videoDone, }: {
|
|
7
|
+
playlistUrl: string;
|
|
8
|
+
state: ParserState;
|
|
9
|
+
structure: M3uStructure;
|
|
10
|
+
audioDone: boolean;
|
|
11
|
+
videoDone: boolean;
|
|
12
|
+
}) => Promise<void>;
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.processM3uChunk = void 0;
|
|
4
|
+
const media_parser_controller_1 = require("../../controller/media-parser-controller");
|
|
5
|
+
const forward_controller_pause_resume_abort_1 = require("../../forward-controller-pause-resume-abort");
|
|
6
|
+
const parse_media_1 = require("../../parse-media");
|
|
7
|
+
const register_track_1 = require("../../register-track");
|
|
8
|
+
const with_resolvers_1 = require("../../with-resolvers");
|
|
9
|
+
const first_sample_in_m3u_chunk_1 = require("./first-sample-in-m3u-chunk");
|
|
10
|
+
const get_chunks_1 = require("./get-chunks");
|
|
11
|
+
const get_playlist_1 = require("./get-playlist");
|
|
12
|
+
const get_chunk_to_seek_to_1 = require("./seek/get-chunk-to-seek-to");
|
|
13
|
+
const processM3uChunk = ({ playlistUrl, state, structure, audioDone, videoDone, }) => {
|
|
14
|
+
const { promise, reject, resolve } = (0, with_resolvers_1.withResolvers)();
|
|
15
|
+
const onGlobalAudioTrack = audioDone
|
|
16
|
+
? null
|
|
17
|
+
: async (track) => {
|
|
18
|
+
const existingTracks = state.callbacks.tracks.getTracks();
|
|
19
|
+
let { trackId } = track;
|
|
20
|
+
while (existingTracks.find((t) => t.trackId === trackId)) {
|
|
21
|
+
trackId++;
|
|
22
|
+
}
|
|
23
|
+
const onAudioSample = await (0, register_track_1.registerAudioTrack)({
|
|
24
|
+
container: 'm3u8',
|
|
25
|
+
track: {
|
|
26
|
+
...track,
|
|
27
|
+
trackId,
|
|
28
|
+
},
|
|
29
|
+
registerAudioSampleCallback: state.callbacks.registerAudioSampleCallback,
|
|
30
|
+
tracks: state.callbacks.tracks,
|
|
31
|
+
logLevel: state.logLevel,
|
|
32
|
+
onAudioTrack: state.onAudioTrack,
|
|
33
|
+
});
|
|
34
|
+
state.m3u.sampleSorter.addToStreamWithTrack(playlistUrl);
|
|
35
|
+
if (onAudioSample === null) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
state.m3u.sampleSorter.addAudioStreamToConsider(playlistUrl, onAudioSample);
|
|
39
|
+
return async (sample) => {
|
|
40
|
+
await state.m3u.sampleSorter.addAudioSample(playlistUrl, sample);
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
const onGlobalVideoTrack = videoDone
|
|
44
|
+
? null
|
|
45
|
+
: async (track) => {
|
|
46
|
+
const existingTracks = state.callbacks.tracks.getTracks();
|
|
47
|
+
let { trackId } = track;
|
|
48
|
+
while (existingTracks.find((t) => t.trackId === trackId)) {
|
|
49
|
+
trackId++;
|
|
50
|
+
}
|
|
51
|
+
const onVideoSample = await (0, register_track_1.registerVideoTrack)({
|
|
52
|
+
container: 'm3u8',
|
|
53
|
+
track: {
|
|
54
|
+
...track,
|
|
55
|
+
trackId,
|
|
56
|
+
},
|
|
57
|
+
logLevel: state.logLevel,
|
|
58
|
+
onVideoTrack: state.onVideoTrack,
|
|
59
|
+
registerVideoSampleCallback: state.callbacks.registerVideoSampleCallback,
|
|
60
|
+
tracks: state.callbacks.tracks,
|
|
61
|
+
});
|
|
62
|
+
state.m3u.sampleSorter.addToStreamWithTrack(playlistUrl);
|
|
63
|
+
if (onVideoSample === null) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
state.m3u.sampleSorter.addVideoStreamToConsider(playlistUrl, onVideoSample);
|
|
67
|
+
return async (sample) => {
|
|
68
|
+
await state.m3u.sampleSorter.addVideoSample(playlistUrl, sample);
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
// This function will run through the whole playlist step by step, and pause itself
|
|
72
|
+
// On the next run it will continue
|
|
73
|
+
const pausableIterator = async () => {
|
|
74
|
+
const playlist = (0, get_playlist_1.getPlaylist)(structure, playlistUrl);
|
|
75
|
+
const chunks = (0, get_chunks_1.getChunks)(playlist);
|
|
76
|
+
const seekToSecondsToProcess = state.m3u.getSeekToSecondsToProcess(playlistUrl);
|
|
77
|
+
const chunksToSubtract = state.m3u.getNextSeekShouldSubtractChunks(playlistUrl);
|
|
78
|
+
let chunkIndex = null;
|
|
79
|
+
if (seekToSecondsToProcess !== null) {
|
|
80
|
+
chunkIndex = Math.max(0, (0, get_chunk_to_seek_to_1.getChunkToSeekTo)({
|
|
81
|
+
chunks,
|
|
82
|
+
seekToSecondsToProcess: seekToSecondsToProcess.targetTime,
|
|
83
|
+
}) - chunksToSubtract);
|
|
84
|
+
}
|
|
85
|
+
const currentPromise = {
|
|
86
|
+
resolver: (() => undefined),
|
|
87
|
+
rejector: reject,
|
|
88
|
+
};
|
|
89
|
+
const requiresHeaderToBeFetched = chunks[0].isHeader;
|
|
90
|
+
for (const chunk of chunks) {
|
|
91
|
+
const mp4HeaderSegment = state.m3u.getMp4HeaderSegment(playlistUrl);
|
|
92
|
+
if (requiresHeaderToBeFetched && mp4HeaderSegment && chunk.isHeader) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (chunkIndex !== null &&
|
|
96
|
+
chunks.indexOf(chunk) < chunkIndex &&
|
|
97
|
+
!chunk.isHeader) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
currentPromise.resolver = (newRun) => {
|
|
101
|
+
state.m3u.setM3uStreamRun(playlistUrl, newRun);
|
|
102
|
+
resolve();
|
|
103
|
+
};
|
|
104
|
+
currentPromise.rejector = reject;
|
|
105
|
+
const childController = (0, media_parser_controller_1.mediaParserController)();
|
|
106
|
+
const forwarded = (0, forward_controller_pause_resume_abort_1.forwardMediaParserControllerPauseResume)({
|
|
107
|
+
childController,
|
|
108
|
+
parentController: state.controller,
|
|
109
|
+
});
|
|
110
|
+
const nextChunk = chunks[chunks.indexOf(chunk) + 1];
|
|
111
|
+
if (nextChunk) {
|
|
112
|
+
const nextChunkSource = state.readerInterface.createAdjacentFileSource(nextChunk.url, playlistUrl);
|
|
113
|
+
state.readerInterface.preload({
|
|
114
|
+
logLevel: state.logLevel,
|
|
115
|
+
range: null,
|
|
116
|
+
src: nextChunkSource,
|
|
117
|
+
prefetchCache: state.prefetchCache,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
const makeContinuationFn = () => {
|
|
121
|
+
return {
|
|
122
|
+
continue() {
|
|
123
|
+
const resolver = (0, with_resolvers_1.withResolvers)();
|
|
124
|
+
currentPromise.resolver = resolver.resolve;
|
|
125
|
+
currentPromise.rejector = resolver.reject;
|
|
126
|
+
childController.resume();
|
|
127
|
+
return resolver.promise;
|
|
128
|
+
},
|
|
129
|
+
abort() {
|
|
130
|
+
childController.abort();
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
const isLastChunk = chunk === chunks[chunks.length - 1];
|
|
135
|
+
await childController._internals.checkForAbortAndPause();
|
|
136
|
+
const src = state.readerInterface.createAdjacentFileSource(chunk.url, playlistUrl);
|
|
137
|
+
try {
|
|
138
|
+
const data = await (0, parse_media_1.parseMedia)({
|
|
139
|
+
src,
|
|
140
|
+
acknowledgeRemotionLicense: true,
|
|
141
|
+
logLevel: state.logLevel,
|
|
142
|
+
controller: childController,
|
|
143
|
+
progressIntervalInMs: 0,
|
|
144
|
+
onParseProgress: () => {
|
|
145
|
+
childController.pause();
|
|
146
|
+
currentPromise.resolver(makeContinuationFn());
|
|
147
|
+
},
|
|
148
|
+
fields: chunk.isHeader ? { structure: true } : undefined,
|
|
149
|
+
onTracks: () => {
|
|
150
|
+
if (!state.m3u.hasEmittedDoneWithTracks(playlistUrl)) {
|
|
151
|
+
state.m3u.setHasEmittedDoneWithTracks(playlistUrl);
|
|
152
|
+
const allDone = state.m3u.setTracksDone(playlistUrl);
|
|
153
|
+
if (allDone) {
|
|
154
|
+
state.callbacks.tracks.setIsDone(state.logLevel);
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
onAudioTrack: onGlobalAudioTrack === null
|
|
160
|
+
? null
|
|
161
|
+
: async ({ track }) => {
|
|
162
|
+
const callbackOrFalse = state.m3u.hasEmittedAudioTrack(playlistUrl);
|
|
163
|
+
if (callbackOrFalse === false) {
|
|
164
|
+
const callback = await onGlobalAudioTrack(track);
|
|
165
|
+
if (!callback) {
|
|
166
|
+
state.m3u.setHasEmittedAudioTrack(playlistUrl, null);
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
state.m3u.setHasEmittedAudioTrack(playlistUrl, callback);
|
|
170
|
+
return async (sample) => {
|
|
171
|
+
await (0, first_sample_in_m3u_chunk_1.considerSeekBasedOnChunk)({
|
|
172
|
+
sample,
|
|
173
|
+
callback,
|
|
174
|
+
parentController: state.controller,
|
|
175
|
+
childController,
|
|
176
|
+
m3uState: state.m3u,
|
|
177
|
+
playlistUrl,
|
|
178
|
+
subtractChunks: chunksToSubtract,
|
|
179
|
+
chunkIndex,
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (callbackOrFalse === null) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
return async (sample) => {
|
|
187
|
+
await (0, first_sample_in_m3u_chunk_1.considerSeekBasedOnChunk)({
|
|
188
|
+
sample,
|
|
189
|
+
m3uState: state.m3u,
|
|
190
|
+
playlistUrl,
|
|
191
|
+
callback: callbackOrFalse,
|
|
192
|
+
parentController: state.controller,
|
|
193
|
+
childController,
|
|
194
|
+
subtractChunks: chunksToSubtract,
|
|
195
|
+
chunkIndex,
|
|
196
|
+
});
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
onVideoTrack: onGlobalVideoTrack === null
|
|
200
|
+
? null
|
|
201
|
+
: async ({ track }) => {
|
|
202
|
+
const callbackOrFalse = state.m3u.hasEmittedVideoTrack(playlistUrl);
|
|
203
|
+
if (callbackOrFalse === false) {
|
|
204
|
+
const callback = await onGlobalVideoTrack({
|
|
205
|
+
...track,
|
|
206
|
+
m3uStreamFormat: chunk.isHeader || mp4HeaderSegment ? 'mp4' : 'ts',
|
|
207
|
+
});
|
|
208
|
+
if (!callback) {
|
|
209
|
+
state.m3u.setHasEmittedVideoTrack(playlistUrl, null);
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
state.m3u.setHasEmittedVideoTrack(playlistUrl, callback);
|
|
213
|
+
return async (sample) => {
|
|
214
|
+
await (0, first_sample_in_m3u_chunk_1.considerSeekBasedOnChunk)({
|
|
215
|
+
sample,
|
|
216
|
+
m3uState: state.m3u,
|
|
217
|
+
playlistUrl,
|
|
218
|
+
callback,
|
|
219
|
+
parentController: state.controller,
|
|
220
|
+
childController,
|
|
221
|
+
subtractChunks: chunksToSubtract,
|
|
222
|
+
chunkIndex,
|
|
223
|
+
});
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
if (callbackOrFalse === null) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
return async (sample) => {
|
|
230
|
+
await (0, first_sample_in_m3u_chunk_1.considerSeekBasedOnChunk)({
|
|
231
|
+
sample,
|
|
232
|
+
m3uState: state.m3u,
|
|
233
|
+
playlistUrl,
|
|
234
|
+
callback: callbackOrFalse,
|
|
235
|
+
parentController: state.controller,
|
|
236
|
+
childController,
|
|
237
|
+
subtractChunks: chunksToSubtract,
|
|
238
|
+
chunkIndex,
|
|
239
|
+
});
|
|
240
|
+
};
|
|
241
|
+
},
|
|
242
|
+
reader: state.readerInterface,
|
|
243
|
+
makeSamplesStartAtZero: false,
|
|
244
|
+
m3uPlaylistContext: {
|
|
245
|
+
mp4HeaderSegment,
|
|
246
|
+
isLastChunkInPlaylist: isLastChunk,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
if (chunk.isHeader) {
|
|
250
|
+
if (data.structure.type !== 'iso-base-media') {
|
|
251
|
+
throw new Error('Expected an mp4 file');
|
|
252
|
+
}
|
|
253
|
+
state.m3u.setMp4HeaderSegment(playlistUrl, data.structure);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch (e) {
|
|
257
|
+
currentPromise.rejector(e);
|
|
258
|
+
throw e;
|
|
259
|
+
}
|
|
260
|
+
forwarded.cleanup();
|
|
261
|
+
if (!isLastChunk) {
|
|
262
|
+
childController.pause();
|
|
263
|
+
currentPromise.resolver(makeContinuationFn());
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
currentPromise.resolver(null);
|
|
267
|
+
};
|
|
268
|
+
const run = pausableIterator();
|
|
269
|
+
run.catch((err) => {
|
|
270
|
+
reject(err);
|
|
271
|
+
});
|
|
272
|
+
return promise;
|
|
273
|
+
};
|
|
274
|
+
exports.processM3uChunk = processM3uChunk;
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.runOverM3u = void 0;
|
|
4
4
|
const log_1 = require("../../log");
|
|
5
|
-
const
|
|
6
|
-
const iterate_over_segment_files_1 = require("./iterate-over-segment-files");
|
|
5
|
+
const process_m3u_chunk_1 = require("./process-m3u-chunk");
|
|
7
6
|
const runOverM3u = async ({ state, structure, playlistUrl, logLevel, }) => {
|
|
8
7
|
const tracksDone = state.m3u.getTrackDone(playlistUrl);
|
|
9
8
|
const hasAudioStreamToConsider = state.m3u.sampleSorter.hasAudioStreamToConsider(playlistUrl);
|
|
@@ -26,84 +25,12 @@ const runOverM3u = async ({ state, structure, playlistUrl, logLevel, }) => {
|
|
|
26
25
|
return;
|
|
27
26
|
}
|
|
28
27
|
log_1.Log.trace(logLevel, 'Starting new M3U parsing process for', playlistUrl);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
resolve();
|
|
36
|
-
},
|
|
37
|
-
logLevel: state.logLevel,
|
|
38
|
-
onDoneWithTracks() {
|
|
39
|
-
const allDone = state.m3u.setTracksDone(playlistUrl);
|
|
40
|
-
if (allDone) {
|
|
41
|
-
state.callbacks.tracks.setIsDone(state.logLevel);
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
onAudioTrack: audioDone
|
|
45
|
-
? null
|
|
46
|
-
: async (track) => {
|
|
47
|
-
const existingTracks = state.callbacks.tracks.getTracks();
|
|
48
|
-
let { trackId } = track;
|
|
49
|
-
while (existingTracks.find((t) => t.trackId === trackId)) {
|
|
50
|
-
trackId++;
|
|
51
|
-
}
|
|
52
|
-
const onAudioSample = await (0, register_track_1.registerAudioTrack)({
|
|
53
|
-
container: 'm3u8',
|
|
54
|
-
track: {
|
|
55
|
-
...track,
|
|
56
|
-
trackId,
|
|
57
|
-
},
|
|
58
|
-
registerAudioSampleCallback: state.callbacks.registerAudioSampleCallback,
|
|
59
|
-
tracks: state.callbacks.tracks,
|
|
60
|
-
logLevel: state.logLevel,
|
|
61
|
-
onAudioTrack: state.onAudioTrack,
|
|
62
|
-
});
|
|
63
|
-
state.m3u.sampleSorter.addToStreamWithTrack(playlistUrl);
|
|
64
|
-
if (onAudioSample === null) {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
state.m3u.sampleSorter.addAudioStreamToConsider(playlistUrl, onAudioSample);
|
|
68
|
-
return async (sample) => {
|
|
69
|
-
await state.m3u.sampleSorter.addAudioSample(playlistUrl, sample);
|
|
70
|
-
};
|
|
71
|
-
},
|
|
72
|
-
onVideoTrack: videoDone
|
|
73
|
-
? null
|
|
74
|
-
: async (track) => {
|
|
75
|
-
const existingTracks = state.callbacks.tracks.getTracks();
|
|
76
|
-
let { trackId } = track;
|
|
77
|
-
while (existingTracks.find((t) => t.trackId === trackId)) {
|
|
78
|
-
trackId++;
|
|
79
|
-
}
|
|
80
|
-
const onVideoSample = await (0, register_track_1.registerVideoTrack)({
|
|
81
|
-
container: 'm3u8',
|
|
82
|
-
track: {
|
|
83
|
-
...track,
|
|
84
|
-
trackId,
|
|
85
|
-
},
|
|
86
|
-
logLevel: state.logLevel,
|
|
87
|
-
onVideoTrack: state.onVideoTrack,
|
|
88
|
-
registerVideoSampleCallback: state.callbacks.registerVideoSampleCallback,
|
|
89
|
-
tracks: state.callbacks.tracks,
|
|
90
|
-
});
|
|
91
|
-
state.m3u.sampleSorter.addToStreamWithTrack(playlistUrl);
|
|
92
|
-
if (onVideoSample === null) {
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
state.m3u.sampleSorter.addVideoStreamToConsider(playlistUrl, onVideoSample);
|
|
96
|
-
return async (sample) => {
|
|
97
|
-
await state.m3u.sampleSorter.addVideoSample(playlistUrl, sample);
|
|
98
|
-
};
|
|
99
|
-
},
|
|
100
|
-
m3uState: state.m3u,
|
|
101
|
-
parentController: state.controller,
|
|
102
|
-
readerInterface: state.readerInterface,
|
|
103
|
-
});
|
|
104
|
-
run.catch((err) => {
|
|
105
|
-
reject(err);
|
|
106
|
-
});
|
|
28
|
+
await (0, process_m3u_chunk_1.processM3uChunk)({
|
|
29
|
+
playlistUrl,
|
|
30
|
+
state,
|
|
31
|
+
structure,
|
|
32
|
+
audioDone,
|
|
33
|
+
videoDone,
|
|
107
34
|
});
|
|
108
35
|
};
|
|
109
36
|
exports.runOverM3u = runOverM3u;
|
|
@@ -4,6 +4,7 @@ export declare const sampleSorter: ({ logLevel, getAllChunksProcessedForPlaylist
|
|
|
4
4
|
logLevel: LogLevel;
|
|
5
5
|
getAllChunksProcessedForPlaylist: (src: string) => boolean;
|
|
6
6
|
}) => {
|
|
7
|
+
clearSamples: () => void;
|
|
7
8
|
addToStreamWithTrack: (src: string) => void;
|
|
8
9
|
addVideoStreamToConsider: (src: string, callback: OnVideoSample) => void;
|
|
9
10
|
addAudioStreamToConsider: (src: string, callback: OnAudioSample) => void;
|
|
@@ -6,8 +6,11 @@ const sampleSorter = ({ logLevel, getAllChunksProcessedForPlaylist, }) => {
|
|
|
6
6
|
const streamsWithTracks = [];
|
|
7
7
|
const audioCallbacks = {};
|
|
8
8
|
const videoCallbacks = {};
|
|
9
|
-
|
|
9
|
+
let latestSample = {};
|
|
10
10
|
return {
|
|
11
|
+
clearSamples: () => {
|
|
12
|
+
latestSample = {};
|
|
13
|
+
},
|
|
11
14
|
addToStreamWithTrack: (src) => {
|
|
12
15
|
streamsWithTracks.push(src);
|
|
13
16
|
},
|