@remotion/media-parser 4.0.265 → 4.0.267
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/buffer-iterator.d.ts +1 -0
- package/dist/buffer-iterator.js +17 -0
- package/dist/containers/aac/parse-aac.js +1 -1
- package/dist/containers/avc/create-sps-pps-data.js +15 -1
- package/dist/containers/avc/interpret-sps.js +8 -2
- package/dist/containers/avc/parse-avc.js +23 -24
- package/dist/containers/flac/parse-streaminfo.js +1 -1
- package/dist/containers/iso-base-media/get-moov-atom.js +3 -2
- package/dist/containers/iso-base-media/process-box.js +9 -2
- package/dist/containers/iso-base-media/trun.js +1 -1
- package/dist/containers/m3u/after-manifest-fetch.d.ts +14 -0
- package/dist/containers/m3u/after-manifest-fetch.js +67 -0
- package/dist/containers/m3u/fetch-m3u8-stream.d.ts +3 -0
- package/dist/containers/m3u/fetch-m3u8-stream.js +18 -0
- package/dist/containers/m3u/get-chunks.d.ts +6 -0
- package/dist/containers/m3u/get-chunks.js +20 -0
- package/dist/containers/m3u/get-duration-from-m3u.d.ts +2 -0
- package/dist/containers/m3u/get-duration-from-m3u.js +9 -0
- package/dist/containers/m3u/get-playlist.d.ts +3 -0
- package/dist/containers/m3u/get-playlist.js +27 -0
- package/dist/containers/m3u/get-streams.d.ts +24 -0
- package/dist/containers/m3u/get-streams.js +80 -0
- package/dist/containers/m3u/parse-directive.d.ts +2 -0
- package/dist/containers/m3u/parse-directive.js +72 -0
- package/dist/containers/m3u/parse-m3u-manifest.d.ts +8 -0
- package/dist/containers/m3u/parse-m3u-manifest.js +18 -0
- package/dist/containers/m3u/parse-m3u-media-directive.d.ts +2 -0
- package/dist/containers/m3u/parse-m3u-media-directive.js +31 -0
- package/dist/containers/m3u/parse-m3u.d.ts +4 -0
- package/dist/containers/m3u/parse-m3u.js +35 -0
- package/dist/containers/m3u/parse-m3u8-text.d.ts +2 -0
- package/dist/containers/m3u/parse-m3u8-text.js +23 -0
- package/dist/containers/m3u/parse-stream-inf.d.ts +3 -0
- package/dist/containers/m3u/parse-stream-inf.js +62 -0
- package/dist/containers/m3u/return-packets.d.ts +16 -0
- package/dist/containers/m3u/return-packets.js +71 -0
- package/dist/containers/m3u/select-stream.d.ts +10 -0
- package/dist/containers/m3u/select-stream.js +19 -0
- package/dist/containers/m3u/types.d.ts +62 -0
- package/dist/containers/m3u/types.js +2 -0
- package/dist/containers/mp3/parse-mpeg-header.js +1 -1
- package/dist/containers/riff/expect-riff-box.js +1 -1
- package/dist/containers/transport-stream/boxes.d.ts +4 -1
- package/dist/containers/transport-stream/find-separator.d.ts +1 -2
- package/dist/containers/transport-stream/find-separator.js +7 -13
- package/dist/containers/transport-stream/handle-aac-packet.js +1 -1
- package/dist/containers/transport-stream/handle-avc-packet.js +1 -1
- package/dist/containers/transport-stream/parse-packet.js +7 -1
- package/dist/containers/transport-stream/parse-pat.d.ts +2 -1
- package/dist/containers/transport-stream/parse-pat.js +16 -1
- package/dist/containers/transport-stream/parse-stream-packet.js +86 -74
- package/dist/containers/wav/parse-fmt.js +1 -1
- package/dist/containers/webm/parse-ebml.js +9 -2
- package/dist/download-and-parse-media.js +32 -29
- package/dist/emit-available-info.js +13 -1
- package/dist/emitter.d.ts +4 -0
- package/dist/emitter.js +4 -0
- package/dist/esm/fetch.mjs +148 -0
- package/dist/esm/index.mjs +1321 -528
- package/dist/esm/node-writer.mjs +113 -0
- package/dist/esm/node.mjs +37 -106
- package/dist/esm/{from-web-file.mjs → web-file.mjs} +2 -7
- package/dist/fetch.d.ts +1 -0
- package/dist/fetch.js +17 -0
- package/dist/file-types/detect-file-type.d.ts +5 -1
- package/dist/file-types/detect-file-type.js +5 -1
- package/dist/file-types/index.js +3 -0
- package/dist/forward-controller.d.ts +7 -0
- package/dist/forward-controller.js +25 -0
- package/dist/get-container.js +3 -0
- package/dist/get-duration.js +35 -2
- package/dist/get-fields-from-callbacks.js +1 -0
- package/dist/get-fps.js +7 -0
- package/dist/get-tracks.d.ts +2 -0
- package/dist/get-tracks.js +29 -4
- package/dist/has-all-info.js +4 -0
- package/dist/index.d.ts +46 -9
- package/dist/index.js +3 -1
- package/dist/init-video.js +8 -0
- package/dist/internal-parse-media.js +11 -4
- package/dist/is-audio-structure.js +3 -0
- package/dist/make-hvc1-codec-strings.js +3 -3
- package/dist/media-parser-controller.js +3 -1
- package/dist/metadata/get-metadata.js +26 -3
- package/dist/node-writer.d.ts +1 -0
- package/dist/node-writer.js +17 -0
- package/dist/node.d.ts +1 -0
- package/dist/node.js +17 -0
- package/dist/options.d.ts +9 -1
- package/dist/parse-media.js +11 -8
- package/dist/parse-result.d.ts +2 -1
- package/dist/readers/fetch/get-body-and-reader.d.ts +8 -0
- package/dist/readers/fetch/get-body-and-reader.js +42 -0
- package/dist/readers/fetch/resolve-url.d.ts +1 -0
- package/dist/readers/fetch/resolve-url.js +15 -0
- package/dist/readers/from-fetch.js +19 -40
- package/dist/readers/from-node.js +1 -7
- package/dist/readers/from-web-file.js +1 -6
- package/dist/readers/reader.d.ts +1 -2
- package/dist/register-track.d.ts +8 -3
- package/dist/register-track.js +30 -17
- package/dist/run-parse-iteration.js +6 -1
- package/dist/state/can-skip-tracks.js +2 -1
- package/dist/state/emitted-fields.js +1 -0
- package/dist/state/m3u-state.d.ts +29 -0
- package/dist/state/m3u-state.js +48 -0
- package/dist/state/need-samples-for-fields.js +1 -0
- package/dist/state/parser-state.d.ts +36 -1
- package/dist/state/parser-state.js +4 -1
- package/dist/state/structure.d.ts +1 -0
- package/dist/state/structure.js +7 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/web-file.d.ts +1 -0
- package/dist/web-file.js +17 -0
- package/package.json +32 -32
- package/dist/containers/mp3/get-tracks-from-mp3.d.ts +0 -4
- package/dist/containers/mp3/get-tracks-from-mp3.js +0 -25
- package/dist/esm/from-fetch.mjs +0 -237
- package/dist/esm/from-node.mjs +0 -50
package/dist/esm/index.mjs
CHANGED
|
@@ -1,237 +1,3 @@
|
|
|
1
|
-
// src/errors.ts
|
|
2
|
-
class IsAGifError extends Error {
|
|
3
|
-
mimeType;
|
|
4
|
-
sizeInBytes;
|
|
5
|
-
fileName;
|
|
6
|
-
constructor({
|
|
7
|
-
message,
|
|
8
|
-
mimeType,
|
|
9
|
-
sizeInBytes,
|
|
10
|
-
fileName
|
|
11
|
-
}) {
|
|
12
|
-
super(message);
|
|
13
|
-
this.fileName = "IsAGifError";
|
|
14
|
-
this.mimeType = mimeType;
|
|
15
|
-
this.sizeInBytes = sizeInBytes;
|
|
16
|
-
this.fileName = fileName;
|
|
17
|
-
if (Error.captureStackTrace) {
|
|
18
|
-
Error.captureStackTrace(this, IsAGifError);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
class IsAnImageError extends Error {
|
|
24
|
-
imageType;
|
|
25
|
-
dimensions;
|
|
26
|
-
mimeType;
|
|
27
|
-
sizeInBytes;
|
|
28
|
-
fileName;
|
|
29
|
-
constructor({
|
|
30
|
-
dimensions,
|
|
31
|
-
imageType,
|
|
32
|
-
message,
|
|
33
|
-
mimeType,
|
|
34
|
-
sizeInBytes,
|
|
35
|
-
fileName
|
|
36
|
-
}) {
|
|
37
|
-
super(message);
|
|
38
|
-
this.name = "IsAnImageError";
|
|
39
|
-
this.imageType = imageType;
|
|
40
|
-
this.dimensions = dimensions;
|
|
41
|
-
this.mimeType = mimeType;
|
|
42
|
-
this.sizeInBytes = sizeInBytes;
|
|
43
|
-
this.fileName = fileName;
|
|
44
|
-
if (Error.captureStackTrace) {
|
|
45
|
-
Error.captureStackTrace(this, IsAnImageError);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
class IsAPdfError extends Error {
|
|
51
|
-
mimeType;
|
|
52
|
-
sizeInBytes;
|
|
53
|
-
fileName;
|
|
54
|
-
constructor({
|
|
55
|
-
message,
|
|
56
|
-
mimeType,
|
|
57
|
-
sizeInBytes,
|
|
58
|
-
fileName
|
|
59
|
-
}) {
|
|
60
|
-
super(message);
|
|
61
|
-
this.name = "IsAPdfError";
|
|
62
|
-
this.mimeType = mimeType;
|
|
63
|
-
this.sizeInBytes = sizeInBytes;
|
|
64
|
-
this.fileName = fileName;
|
|
65
|
-
if (Error.captureStackTrace) {
|
|
66
|
-
Error.captureStackTrace(this, IsAPdfError);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
class IsAnUnsupportedFileTypeError extends Error {
|
|
72
|
-
mimeType;
|
|
73
|
-
sizeInBytes;
|
|
74
|
-
fileName;
|
|
75
|
-
constructor({
|
|
76
|
-
message,
|
|
77
|
-
mimeType,
|
|
78
|
-
sizeInBytes,
|
|
79
|
-
fileName
|
|
80
|
-
}) {
|
|
81
|
-
super(message);
|
|
82
|
-
this.name = "IsAnUnsupportedFileTypeError";
|
|
83
|
-
this.mimeType = mimeType;
|
|
84
|
-
this.sizeInBytes = sizeInBytes;
|
|
85
|
-
this.fileName = fileName;
|
|
86
|
-
if (Error.captureStackTrace) {
|
|
87
|
-
Error.captureStackTrace(this, IsAnUnsupportedFileTypeError);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
class IsAnUnsupportedAudioTypeError extends Error {
|
|
93
|
-
mimeType;
|
|
94
|
-
sizeInBytes;
|
|
95
|
-
fileName;
|
|
96
|
-
audioType;
|
|
97
|
-
constructor({
|
|
98
|
-
message,
|
|
99
|
-
mimeType,
|
|
100
|
-
sizeInBytes,
|
|
101
|
-
fileName,
|
|
102
|
-
audioType
|
|
103
|
-
}) {
|
|
104
|
-
super(message);
|
|
105
|
-
this.name = "IsAnUnsupportedAudioTypeError";
|
|
106
|
-
this.mimeType = mimeType;
|
|
107
|
-
this.sizeInBytes = sizeInBytes;
|
|
108
|
-
this.fileName = fileName;
|
|
109
|
-
this.audioType = audioType;
|
|
110
|
-
if (Error.captureStackTrace) {
|
|
111
|
-
Error.captureStackTrace(this, IsAnUnsupportedAudioTypeError);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
class MediaParserAbortError extends Error {
|
|
117
|
-
constructor(message) {
|
|
118
|
-
super(message);
|
|
119
|
-
this.name = "MediaParserAbortError";
|
|
120
|
-
this.cause = undefined;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
var hasBeenAborted = (error) => {
|
|
124
|
-
return error instanceof MediaParserAbortError;
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
// src/readers/from-fetch.ts
|
|
128
|
-
function parseContentRange(input) {
|
|
129
|
-
const matches = input.match(/^(\w+) ((\d+)-(\d+)|\*)\/(\d+|\*)$/);
|
|
130
|
-
if (!matches)
|
|
131
|
-
return null;
|
|
132
|
-
const [, unit, , start, end, size] = matches;
|
|
133
|
-
const range = {
|
|
134
|
-
unit,
|
|
135
|
-
start: start != null ? Number(start) : null,
|
|
136
|
-
end: end != null ? Number(end) : null,
|
|
137
|
-
size: size === "*" ? null : Number(size)
|
|
138
|
-
};
|
|
139
|
-
if (range.start === null && range.end === null && range.size === null) {
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
return range;
|
|
143
|
-
}
|
|
144
|
-
var validateContentRangeAndDetectIfSupported = (actualRange, parsedContentRange, statusCode) => {
|
|
145
|
-
if (statusCode === 206) {
|
|
146
|
-
return { supportsContentRange: true };
|
|
147
|
-
}
|
|
148
|
-
if (typeof actualRange === "number" && parsedContentRange?.start !== actualRange) {
|
|
149
|
-
if (actualRange === 0) {
|
|
150
|
-
return { supportsContentRange: false };
|
|
151
|
-
}
|
|
152
|
-
throw new Error(`Range header (${actualRange}) does not match content-range header (${parsedContentRange?.start})`);
|
|
153
|
-
}
|
|
154
|
-
if (actualRange !== null && typeof actualRange !== "number" && (parsedContentRange?.start !== actualRange[0] || parsedContentRange?.end !== actualRange[1])) {
|
|
155
|
-
throw new Error(`Range header (${actualRange}) does not match content-range header (${parsedContentRange?.start})`);
|
|
156
|
-
}
|
|
157
|
-
return { supportsContentRange: true };
|
|
158
|
-
};
|
|
159
|
-
var fetchReader = {
|
|
160
|
-
read: async ({ src, range, controller }) => {
|
|
161
|
-
if (typeof src !== "string") {
|
|
162
|
-
throw new Error("src must be a string when using `fetchReader`");
|
|
163
|
-
}
|
|
164
|
-
const resolvedUrl = typeof window !== "undefined" && typeof window.location !== "undefined" ? new URL(src, window.location.origin).toString() : src;
|
|
165
|
-
if (!resolvedUrl.startsWith("https://") && !resolvedUrl.startsWith("blob:") && !resolvedUrl.startsWith("http://")) {
|
|
166
|
-
return Promise.reject(new Error(resolvedUrl + " is not a URL - needs to start with http:// or https:// or blob:. If you want to read a local file, pass `reader: nodeReader` to parseMedia()."));
|
|
167
|
-
}
|
|
168
|
-
const ownController = new AbortController;
|
|
169
|
-
const cache = typeof navigator !== "undefined" && navigator.userAgent.includes("Cloudflare-Workers") ? undefined : "no-store";
|
|
170
|
-
const actualRange = range === null ? 0 : range;
|
|
171
|
-
const res = await fetch(resolvedUrl, {
|
|
172
|
-
headers: typeof actualRange === "number" ? {
|
|
173
|
-
Range: `bytes=${actualRange}-`
|
|
174
|
-
} : {
|
|
175
|
-
Range: `bytes=${`${actualRange[0]}-${actualRange[1]}`}`
|
|
176
|
-
},
|
|
177
|
-
signal: ownController.signal,
|
|
178
|
-
cache
|
|
179
|
-
});
|
|
180
|
-
const contentRange = res.headers.get("content-range");
|
|
181
|
-
const parsedContentRange = contentRange ? parseContentRange(contentRange) : null;
|
|
182
|
-
const { supportsContentRange } = validateContentRangeAndDetectIfSupported(actualRange, parsedContentRange, res.status);
|
|
183
|
-
controller._internals.signal.addEventListener("abort", () => {
|
|
184
|
-
ownController.abort(new MediaParserAbortError("Aborted by user"));
|
|
185
|
-
}, { once: true });
|
|
186
|
-
if (res.status.toString().startsWith("4") || res.status.toString().startsWith("5")) {
|
|
187
|
-
throw new Error(`Server returned status code ${res.status} for ${src} and range ${actualRange}`);
|
|
188
|
-
}
|
|
189
|
-
if (!res.body) {
|
|
190
|
-
throw new Error("No body");
|
|
191
|
-
}
|
|
192
|
-
const length = res.headers.get("content-length");
|
|
193
|
-
const contentLength = length === null ? null : parseInt(length, 10);
|
|
194
|
-
const contentDisposition = res.headers.get("content-disposition");
|
|
195
|
-
const name = contentDisposition?.match(/filename="([^"]+)"/)?.[1];
|
|
196
|
-
const fallbackName = src.split("/").pop();
|
|
197
|
-
const reader = res.body.getReader();
|
|
198
|
-
if (controller) {
|
|
199
|
-
controller._internals.signal.addEventListener("abort", () => {
|
|
200
|
-
reader.cancel().catch(() => {
|
|
201
|
-
});
|
|
202
|
-
}, { once: true });
|
|
203
|
-
}
|
|
204
|
-
return {
|
|
205
|
-
reader: {
|
|
206
|
-
reader,
|
|
207
|
-
abort: () => {
|
|
208
|
-
ownController.abort();
|
|
209
|
-
}
|
|
210
|
-
},
|
|
211
|
-
contentLength,
|
|
212
|
-
contentType: res.headers.get("content-type"),
|
|
213
|
-
name: name ?? fallbackName,
|
|
214
|
-
supportsContentRange
|
|
215
|
-
};
|
|
216
|
-
},
|
|
217
|
-
getLength: async (src) => {
|
|
218
|
-
if (typeof src !== "string") {
|
|
219
|
-
throw new Error("src must be a string when using `fetchReader`");
|
|
220
|
-
}
|
|
221
|
-
const res = await fetch(src, {
|
|
222
|
-
method: "HEAD"
|
|
223
|
-
});
|
|
224
|
-
if (!res.body) {
|
|
225
|
-
throw new Error("No body");
|
|
226
|
-
}
|
|
227
|
-
const length = res.headers.get("content-length");
|
|
228
|
-
if (!length) {
|
|
229
|
-
throw new Error("No content-length");
|
|
230
|
-
}
|
|
231
|
-
return parseInt(length, 10);
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
|
|
235
1
|
// src/aac-codecprivate.ts
|
|
236
2
|
var getSampleRateFromSampleFrequencyIndex = (samplingFrequencyIndex) => {
|
|
237
3
|
switch (samplingFrequencyIndex) {
|
|
@@ -1135,6 +901,9 @@ var isFlac = (data) => {
|
|
|
1135
901
|
const flacPattern = new Uint8Array([102, 76, 97, 67]);
|
|
1136
902
|
return matchesPattern(flacPattern)(data.subarray(0, 4));
|
|
1137
903
|
};
|
|
904
|
+
var isM3u = (data) => {
|
|
905
|
+
return new TextDecoder("utf-8").decode(data.slice(0, 7)) === "#EXTM3U";
|
|
906
|
+
};
|
|
1138
907
|
|
|
1139
908
|
// src/file-types/bmp.ts
|
|
1140
909
|
function getBmpDimensions(bmpData) {
|
|
@@ -1282,6 +1051,9 @@ var detectFileType = (data) => {
|
|
|
1282
1051
|
if (isFlac(data)) {
|
|
1283
1052
|
return { type: "flac" };
|
|
1284
1053
|
}
|
|
1054
|
+
if (isM3u(data)) {
|
|
1055
|
+
return { type: "m3u" };
|
|
1056
|
+
}
|
|
1285
1057
|
const webp = isWebp(data);
|
|
1286
1058
|
if (webp) {
|
|
1287
1059
|
return webp;
|
|
@@ -1395,6 +1167,21 @@ var getArrayBufferIterator = (initialData, maxBytes) => {
|
|
|
1395
1167
|
counter.decrement(1);
|
|
1396
1168
|
return new TextDecoder().decode(new Uint8Array(bytes));
|
|
1397
1169
|
};
|
|
1170
|
+
const readUntilLineEnd = () => {
|
|
1171
|
+
const bytes = [];
|
|
1172
|
+
while (true) {
|
|
1173
|
+
if (bytesRemaining() === 0) {
|
|
1174
|
+
return null;
|
|
1175
|
+
}
|
|
1176
|
+
const byte = getUint8();
|
|
1177
|
+
bytes.push(byte);
|
|
1178
|
+
if (byte === 10) {
|
|
1179
|
+
break;
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
const str = new TextDecoder().decode(new Uint8Array(bytes)).trim();
|
|
1183
|
+
return str;
|
|
1184
|
+
};
|
|
1398
1185
|
const getUint8 = () => {
|
|
1399
1186
|
const val = view.getUint8(counter.getDiscardedOffset());
|
|
1400
1187
|
counter.increment(1);
|
|
@@ -1822,6 +1609,7 @@ var getArrayBufferIterator = (initialData, maxBytes) => {
|
|
|
1822
1609
|
readExpGolomb,
|
|
1823
1610
|
startCheckpoint,
|
|
1824
1611
|
getFlacCodecNumber,
|
|
1612
|
+
readUntilLineEnd,
|
|
1825
1613
|
getSyncSafeInt32
|
|
1826
1614
|
};
|
|
1827
1615
|
};
|
|
@@ -1975,6 +1763,11 @@ var combineUint8Arrays = (arrays) => {
|
|
|
1975
1763
|
return result;
|
|
1976
1764
|
};
|
|
1977
1765
|
|
|
1766
|
+
// src/truthy.ts
|
|
1767
|
+
function truthy(value) {
|
|
1768
|
+
return Boolean(value);
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1978
1771
|
// src/containers/avc/create-sps-pps-data.ts
|
|
1979
1772
|
function serializeUint16(value) {
|
|
1980
1773
|
const buffer = new ArrayBuffer(2);
|
|
@@ -1996,8 +1789,9 @@ var createSpsPpsData = (avc1Profile) => {
|
|
|
1996
1789
|
avc1Profile.sps.sps,
|
|
1997
1790
|
new Uint8Array([1]),
|
|
1998
1791
|
serializeUint16(avc1Profile.pps.pps.length),
|
|
1999
|
-
avc1Profile.pps.pps
|
|
2000
|
-
|
|
1792
|
+
avc1Profile.pps.pps,
|
|
1793
|
+
[66, 77, 88].some((b) => avc1Profile.sps.spsData.profile === b) ? null : new Uint8Array([253, 248, 248, 0])
|
|
1794
|
+
].filter(truthy));
|
|
2001
1795
|
};
|
|
2002
1796
|
|
|
2003
1797
|
// src/add-avc-profile-to-track.ts
|
|
@@ -2013,29 +1807,45 @@ var addAvcProfileToTrack = (track, avc1Profile) => {
|
|
|
2013
1807
|
};
|
|
2014
1808
|
|
|
2015
1809
|
// src/register-track.ts
|
|
2016
|
-
var
|
|
1810
|
+
var registerVideoTrack = async ({
|
|
2017
1811
|
state,
|
|
2018
1812
|
track,
|
|
2019
1813
|
container
|
|
2020
1814
|
}) => {
|
|
2021
1815
|
if (state.callbacks.tracks.getTracks().find((t) => t.trackId === track.trackId)) {
|
|
2022
1816
|
Log.trace(state.logLevel, `Track ${track.trackId} already registered, skipping`);
|
|
2023
|
-
return;
|
|
1817
|
+
return null;
|
|
2024
1818
|
}
|
|
2025
|
-
if (track.type
|
|
2026
|
-
|
|
2027
|
-
if (state.onVideoTrack) {
|
|
2028
|
-
const callback = await state.onVideoTrack({ track, container });
|
|
2029
|
-
await state.callbacks.registerVideoSampleCallback(track.trackId, callback ?? null);
|
|
2030
|
-
}
|
|
1819
|
+
if (track.type !== "video") {
|
|
1820
|
+
throw new Error("Expected video track");
|
|
2031
1821
|
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
1822
|
+
state.callbacks.tracks.addTrack(track);
|
|
1823
|
+
if (!state.onVideoTrack) {
|
|
1824
|
+
return null;
|
|
1825
|
+
}
|
|
1826
|
+
const callback = await state.onVideoTrack({ track, container });
|
|
1827
|
+
await state.callbacks.registerVideoSampleCallback(track.trackId, callback ?? null);
|
|
1828
|
+
return callback;
|
|
1829
|
+
};
|
|
1830
|
+
var registerAudioTrack = async ({
|
|
1831
|
+
state,
|
|
1832
|
+
track,
|
|
1833
|
+
container
|
|
1834
|
+
}) => {
|
|
1835
|
+
if (state.callbacks.tracks.getTracks().find((t) => t.trackId === track.trackId)) {
|
|
1836
|
+
Log.trace(state.logLevel, `Track ${track.trackId} already registered, skipping`);
|
|
1837
|
+
return null;
|
|
2038
1838
|
}
|
|
1839
|
+
if (track.type !== "audio") {
|
|
1840
|
+
throw new Error("Expected audio track");
|
|
1841
|
+
}
|
|
1842
|
+
state.callbacks.tracks.addTrack(track);
|
|
1843
|
+
if (!state.onAudioTrack) {
|
|
1844
|
+
return null;
|
|
1845
|
+
}
|
|
1846
|
+
const callback = await state.onAudioTrack({ track, container });
|
|
1847
|
+
await state.callbacks.registerAudioSampleCallback(track.trackId, callback ?? null);
|
|
1848
|
+
return callback;
|
|
2039
1849
|
};
|
|
2040
1850
|
var registerVideoTrackWhenProfileIsAvailable = ({
|
|
2041
1851
|
state,
|
|
@@ -2043,7 +1853,7 @@ var registerVideoTrackWhenProfileIsAvailable = ({
|
|
|
2043
1853
|
container
|
|
2044
1854
|
}) => {
|
|
2045
1855
|
state.riff.registerOnAvcProfileCallback(async (profile) => {
|
|
2046
|
-
await
|
|
1856
|
+
await registerVideoTrack({
|
|
2047
1857
|
state,
|
|
2048
1858
|
track: addAvcProfileToTrack(track, profile),
|
|
2049
1859
|
container
|
|
@@ -2392,6 +2202,9 @@ var isAudioStructure = (structure) => {
|
|
|
2392
2202
|
if (structure.type === "riff") {
|
|
2393
2203
|
return false;
|
|
2394
2204
|
}
|
|
2205
|
+
if (structure.type === "m3u") {
|
|
2206
|
+
return false;
|
|
2207
|
+
}
|
|
2395
2208
|
throw new Error(`Unhandled structure type: ${structure}`);
|
|
2396
2209
|
};
|
|
2397
2210
|
|
|
@@ -2496,6 +2309,9 @@ var getFps = (state) => {
|
|
|
2496
2309
|
if (segments.type === "transport-stream") {
|
|
2497
2310
|
return null;
|
|
2498
2311
|
}
|
|
2312
|
+
if (segments.type === "m3u") {
|
|
2313
|
+
return null;
|
|
2314
|
+
}
|
|
2499
2315
|
if (segments.type === "mp3" || segments.type === "wav" || segments.type === "flac" || segments.type === "aac") {
|
|
2500
2316
|
return null;
|
|
2501
2317
|
}
|
|
@@ -2519,20 +2335,10 @@ var hasFps = (state) => {
|
|
|
2519
2335
|
if (structure.type === "transport-stream") {
|
|
2520
2336
|
return true;
|
|
2521
2337
|
}
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
// src/containers/mp3/get-tracks-from-mp3.ts
|
|
2526
|
-
var getTracksFromMp3OrWavOrAac = (parserState) => {
|
|
2527
|
-
const tracks2 = parserState.callbacks.tracks.getTracks();
|
|
2528
|
-
if (tracks2.length === 0) {
|
|
2529
|
-
throw new Error("No tracks found");
|
|
2338
|
+
if (structure.type === "m3u") {
|
|
2339
|
+
return true;
|
|
2530
2340
|
}
|
|
2531
|
-
return
|
|
2532
|
-
audioTracks: tracks2.filter((t) => t.type === "audio"),
|
|
2533
|
-
otherTracks: [],
|
|
2534
|
-
videoTracks: []
|
|
2535
|
-
};
|
|
2341
|
+
return hasFpsSuitedForSlowFps(state);
|
|
2536
2342
|
};
|
|
2537
2343
|
|
|
2538
2344
|
// src/containers/riff/timescale.ts
|
|
@@ -2638,11 +2444,6 @@ var hasAllTracksFromAvi = (state) => {
|
|
|
2638
2444
|
}
|
|
2639
2445
|
};
|
|
2640
2446
|
|
|
2641
|
-
// src/truthy.ts
|
|
2642
|
-
function truthy(value) {
|
|
2643
|
-
return Boolean(value);
|
|
2644
|
-
}
|
|
2645
|
-
|
|
2646
2447
|
// src/containers/transport-stream/traversal.ts
|
|
2647
2448
|
var findProgramAssociationTableOrThrow = (structure) => {
|
|
2648
2449
|
const box = structure.boxes.find((b) => b.type === "transport-stream-pat-box");
|
|
@@ -2704,8 +2505,8 @@ var getHvc1CodecString = (data) => {
|
|
|
2704
2505
|
const generalProfileSpaceTierFlagAndIdc = data.getUint8();
|
|
2705
2506
|
let generalProfileCompatibility = data.getUint32();
|
|
2706
2507
|
const generalProfileSpace = generalProfileSpaceTierFlagAndIdc >> 6;
|
|
2707
|
-
const generalTierFlag = generalProfileSpaceTierFlagAndIdc >> 5;
|
|
2708
|
-
const generalProfileIdc = generalProfileSpaceTierFlagAndIdc
|
|
2508
|
+
const generalTierFlag = (generalProfileSpaceTierFlagAndIdc & 32) >> 5;
|
|
2509
|
+
const generalProfileIdc = generalProfileSpaceTierFlagAndIdc & 31;
|
|
2709
2510
|
const generalConstraintIndicator = data.getSlice(6);
|
|
2710
2511
|
const generalLevelIdc = data.getUint8();
|
|
2711
2512
|
let profileId = 0;
|
|
@@ -2726,7 +2527,7 @@ var getHvc1CodecString = (data) => {
|
|
|
2726
2527
|
hasByte = true;
|
|
2727
2528
|
}
|
|
2728
2529
|
}
|
|
2729
|
-
return `${profileSpaceChar}${generalProfileIdc.toString(16)}.${profileId.toString(16)}.${generalTierChar}${generalLevelIdc}
|
|
2530
|
+
return `${profileSpaceChar}${generalProfileIdc.toString(16)}.${profileId.toString(16)}.${generalTierChar}${generalLevelIdc}${generalConstraintString ? "." : ""}${generalConstraintString}`;
|
|
2730
2531
|
};
|
|
2731
2532
|
|
|
2732
2533
|
// src/containers/webm/av1-codec-private.ts
|
|
@@ -3387,6 +3188,9 @@ var getHasTracks = (state) => {
|
|
|
3387
3188
|
if (structure.type === "flac") {
|
|
3388
3189
|
return state.callbacks.tracks.hasAllTracks();
|
|
3389
3190
|
}
|
|
3191
|
+
if (structure.type === "m3u") {
|
|
3192
|
+
return state.callbacks.tracks.hasAllTracks();
|
|
3193
|
+
}
|
|
3390
3194
|
throw new Error("Unknown container " + structure);
|
|
3391
3195
|
};
|
|
3392
3196
|
var getTracksFromMa = (segments, state) => {
|
|
@@ -3445,6 +3249,17 @@ var getTracksFromIsoBaseMedia = (state) => {
|
|
|
3445
3249
|
otherTracks
|
|
3446
3250
|
};
|
|
3447
3251
|
};
|
|
3252
|
+
var defaultGetTracks = (parserState) => {
|
|
3253
|
+
const tracks2 = parserState.callbacks.tracks.getTracks();
|
|
3254
|
+
if (tracks2.length === 0) {
|
|
3255
|
+
throw new Error("No tracks found");
|
|
3256
|
+
}
|
|
3257
|
+
return {
|
|
3258
|
+
audioTracks: tracks2.filter((t) => t.type === "audio"),
|
|
3259
|
+
otherTracks: [],
|
|
3260
|
+
videoTracks: tracks2.filter((t) => t.type === "video")
|
|
3261
|
+
};
|
|
3262
|
+
};
|
|
3448
3263
|
var getTracks = (state) => {
|
|
3449
3264
|
const structure = state.getStructure();
|
|
3450
3265
|
if (structure.type === "matroska") {
|
|
@@ -3459,8 +3274,8 @@ var getTracks = (state) => {
|
|
|
3459
3274
|
if (structure.type === "transport-stream") {
|
|
3460
3275
|
return getTracksFromTransportStream(state);
|
|
3461
3276
|
}
|
|
3462
|
-
if (structure.type === "mp3" || structure.type === "wav" || structure.type === "flac" || structure.type === "aac") {
|
|
3463
|
-
return
|
|
3277
|
+
if (structure.type === "mp3" || structure.type === "wav" || structure.type === "flac" || structure.type === "aac" || structure.type === "m3u") {
|
|
3278
|
+
return defaultGetTracks(state);
|
|
3464
3279
|
}
|
|
3465
3280
|
throw new Error(`Unknown container${structure}`);
|
|
3466
3281
|
};
|
|
@@ -4873,7 +4688,7 @@ var parseTrun = ({
|
|
|
4873
4688
|
size
|
|
4874
4689
|
}) => {
|
|
4875
4690
|
const version = iterator.getUint8();
|
|
4876
|
-
if (version !== 0) {
|
|
4691
|
+
if (version !== 0 && version !== 1) {
|
|
4877
4692
|
throw new Error(`Unsupported TRUN version ${version}`);
|
|
4878
4693
|
}
|
|
4879
4694
|
const flags = iterator.getUint24();
|
|
@@ -5057,8 +4872,15 @@ var processBox = async (state) => {
|
|
|
5057
4872
|
state
|
|
5058
4873
|
});
|
|
5059
4874
|
const transformedTrack = makeBaseMediaTrack(box);
|
|
5060
|
-
if (transformedTrack) {
|
|
5061
|
-
await
|
|
4875
|
+
if (transformedTrack && transformedTrack.type === "video") {
|
|
4876
|
+
await registerVideoTrack({
|
|
4877
|
+
state,
|
|
4878
|
+
track: transformedTrack,
|
|
4879
|
+
container: "mp4"
|
|
4880
|
+
});
|
|
4881
|
+
}
|
|
4882
|
+
if (transformedTrack && transformedTrack.type === "audio") {
|
|
4883
|
+
await registerAudioTrack({
|
|
5062
4884
|
state,
|
|
5063
4885
|
track: transformedTrack,
|
|
5064
4886
|
container: "mp4"
|
|
@@ -5639,8 +5461,15 @@ var postprocessEbml = async ({
|
|
|
5639
5461
|
track: ebml,
|
|
5640
5462
|
timescale: state.webm.getTimescale()
|
|
5641
5463
|
});
|
|
5642
|
-
if (track) {
|
|
5643
|
-
await
|
|
5464
|
+
if (track && track.type === "audio") {
|
|
5465
|
+
await registerAudioTrack({
|
|
5466
|
+
state,
|
|
5467
|
+
track,
|
|
5468
|
+
container: "webm"
|
|
5469
|
+
});
|
|
5470
|
+
}
|
|
5471
|
+
if (track && track.type === "video") {
|
|
5472
|
+
await registerVideoTrack({
|
|
5644
5473
|
state,
|
|
5645
5474
|
track,
|
|
5646
5475
|
container: "webm"
|
|
@@ -5699,6 +5528,73 @@ var postprocessEbml = async ({
|
|
|
5699
5528
|
return ebml;
|
|
5700
5529
|
};
|
|
5701
5530
|
|
|
5531
|
+
// src/containers/m3u/get-streams.ts
|
|
5532
|
+
var isIndependentSegments = (structure) => {
|
|
5533
|
+
if (structure === null || structure.type !== "m3u") {
|
|
5534
|
+
return false;
|
|
5535
|
+
}
|
|
5536
|
+
return structure.boxes.some((box) => box.type === "m3u-independent-segments" || box.type === "m3u-stream-info");
|
|
5537
|
+
};
|
|
5538
|
+
var getM3uStreams = (structure, originalSrc) => {
|
|
5539
|
+
if (structure === null || structure.type !== "m3u") {
|
|
5540
|
+
return null;
|
|
5541
|
+
}
|
|
5542
|
+
const boxes = [];
|
|
5543
|
+
for (let i = 0;i < structure.boxes.length; i++) {
|
|
5544
|
+
const str = structure.boxes[i];
|
|
5545
|
+
if (str.type === "m3u-stream-info") {
|
|
5546
|
+
const next = structure.boxes[i + 1];
|
|
5547
|
+
if (next.type !== "m3u-text-value") {
|
|
5548
|
+
throw new Error("Expected m3u-text-value");
|
|
5549
|
+
}
|
|
5550
|
+
const dedicatedAudioTracks = [];
|
|
5551
|
+
if (str.audio) {
|
|
5552
|
+
const match = structure.boxes.filter((box) => {
|
|
5553
|
+
return box.type === "m3u-media-info" && box.groupId === str.audio;
|
|
5554
|
+
});
|
|
5555
|
+
for (const audioTrack of match) {
|
|
5556
|
+
dedicatedAudioTracks.push({
|
|
5557
|
+
autoselect: audioTrack.autoselect,
|
|
5558
|
+
channels: audioTrack.channels,
|
|
5559
|
+
default: audioTrack.default,
|
|
5560
|
+
groupId: audioTrack.groupId,
|
|
5561
|
+
language: audioTrack.language,
|
|
5562
|
+
name: audioTrack.name,
|
|
5563
|
+
url: originalSrc && originalSrc.startsWith("http") ? new URL(audioTrack.uri, originalSrc).href : audioTrack.uri
|
|
5564
|
+
});
|
|
5565
|
+
}
|
|
5566
|
+
}
|
|
5567
|
+
boxes.push({
|
|
5568
|
+
url: originalSrc && originalSrc.startsWith("http") ? new URL(next.value, originalSrc).href : next.value,
|
|
5569
|
+
averageBandwidth: str.averageBandwidth,
|
|
5570
|
+
bandwidth: str.bandwidth,
|
|
5571
|
+
codecs: str.codecs,
|
|
5572
|
+
resolution: str.resolution,
|
|
5573
|
+
dedicatedAudioTracks
|
|
5574
|
+
});
|
|
5575
|
+
}
|
|
5576
|
+
}
|
|
5577
|
+
if (boxes.length === 0) {
|
|
5578
|
+
return null;
|
|
5579
|
+
}
|
|
5580
|
+
const sorted = boxes.slice().sort((a, b) => {
|
|
5581
|
+
const aResolution = a.resolution ? a.resolution.width * a.resolution.height : 0;
|
|
5582
|
+
const bResolution = b.resolution ? b.resolution.width * b.resolution.height : 0;
|
|
5583
|
+
return bResolution - aResolution;
|
|
5584
|
+
});
|
|
5585
|
+
return sorted.map((box, index) => ({ ...box, id: index }));
|
|
5586
|
+
};
|
|
5587
|
+
var m3uHasStreams = (state) => {
|
|
5588
|
+
const structure = state.getStructureOrNull();
|
|
5589
|
+
if (!structure) {
|
|
5590
|
+
return false;
|
|
5591
|
+
}
|
|
5592
|
+
if (structure.type !== "m3u") {
|
|
5593
|
+
return true;
|
|
5594
|
+
}
|
|
5595
|
+
return state.m3u.hasFinishedManifest();
|
|
5596
|
+
};
|
|
5597
|
+
|
|
5702
5598
|
// src/get-container.ts
|
|
5703
5599
|
var getContainer = (segments) => {
|
|
5704
5600
|
if (segments.type === "iso-base-media") {
|
|
@@ -5728,6 +5624,9 @@ var getContainer = (segments) => {
|
|
|
5728
5624
|
if (segments.type === "aac") {
|
|
5729
5625
|
return "aac";
|
|
5730
5626
|
}
|
|
5627
|
+
if (segments.type === "m3u") {
|
|
5628
|
+
return "m3u8";
|
|
5629
|
+
}
|
|
5731
5630
|
throw new Error("Unknown container " + segments);
|
|
5732
5631
|
};
|
|
5733
5632
|
var hasContainer = (boxes) => {
|
|
@@ -5994,6 +5893,35 @@ var getSamplePositionsFromTrack = ({
|
|
|
5994
5893
|
return samplePositions;
|
|
5995
5894
|
};
|
|
5996
5895
|
|
|
5896
|
+
// src/containers/m3u/get-playlist.ts
|
|
5897
|
+
var getPlaylist = (structure) => {
|
|
5898
|
+
const isIndependent = isIndependentSegments(structure);
|
|
5899
|
+
if (!isIndependent) {
|
|
5900
|
+
return {
|
|
5901
|
+
type: "m3u-playlist",
|
|
5902
|
+
boxes: structure.boxes
|
|
5903
|
+
};
|
|
5904
|
+
}
|
|
5905
|
+
const playlists = structure.boxes.filter((box) => box.type === "m3u-playlist");
|
|
5906
|
+
if (playlists.length !== 1) {
|
|
5907
|
+
throw new Error("Expected one playlist");
|
|
5908
|
+
}
|
|
5909
|
+
return playlists[0];
|
|
5910
|
+
};
|
|
5911
|
+
var getDurationFromPlaylist = (playlist) => {
|
|
5912
|
+
const duration2 = playlist.boxes.filter((box) => box.type === "m3u-extinf");
|
|
5913
|
+
if (duration2.length === 0) {
|
|
5914
|
+
throw new Error("Expected duration in m3u playlist");
|
|
5915
|
+
}
|
|
5916
|
+
return duration2.reduce((acc, d) => acc + d.value, 0);
|
|
5917
|
+
};
|
|
5918
|
+
|
|
5919
|
+
// src/containers/m3u/get-duration-from-m3u.ts
|
|
5920
|
+
var getDurationFromM3u = (structure) => {
|
|
5921
|
+
const playlist = getPlaylist(structure);
|
|
5922
|
+
return getDurationFromPlaylist(playlist);
|
|
5923
|
+
};
|
|
5924
|
+
|
|
5997
5925
|
// src/containers/mp3/get-frame-length.ts
|
|
5998
5926
|
var getUnroundedMpegFrameLength = ({
|
|
5999
5927
|
samplesPerFrame,
|
|
@@ -6146,6 +6074,27 @@ var getDurationFromMatroska = (segments) => {
|
|
|
6146
6074
|
}
|
|
6147
6075
|
return duration2.value.value / timestampScale2.value.value * 1000;
|
|
6148
6076
|
};
|
|
6077
|
+
var isoHasDuration = (parserState) => {
|
|
6078
|
+
const structure = parserState.getIsoStructure();
|
|
6079
|
+
const moovBox = getMoovBox(parserState);
|
|
6080
|
+
if (!moovBox) {
|
|
6081
|
+
return false;
|
|
6082
|
+
}
|
|
6083
|
+
const mvhdBox = getMvhdBox(moovBox);
|
|
6084
|
+
if (!mvhdBox) {
|
|
6085
|
+
return false;
|
|
6086
|
+
}
|
|
6087
|
+
if (mvhdBox.type !== "mvhd-box") {
|
|
6088
|
+
throw new Error("Expected mvhd-box");
|
|
6089
|
+
}
|
|
6090
|
+
if (mvhdBox.durationInSeconds > 0) {
|
|
6091
|
+
return true;
|
|
6092
|
+
}
|
|
6093
|
+
const moofBoxes = getMoofBoxes(structure.boxes);
|
|
6094
|
+
const hasMvex = moovBox.children.some((b) => b.type === "regular-box" && b.boxType === "mvex");
|
|
6095
|
+
const isFragmented = moofBoxes.length > 0 || hasMvex;
|
|
6096
|
+
return !isFragmented;
|
|
6097
|
+
};
|
|
6149
6098
|
var getDurationFromIsoBaseMedia = (parserState) => {
|
|
6150
6099
|
const structure = parserState.getIsoStructure();
|
|
6151
6100
|
const moovBox = getMoovBox(parserState);
|
|
@@ -6207,14 +6156,24 @@ var getDuration = (parserState) => {
|
|
|
6207
6156
|
if (structure.type === "flac") {
|
|
6208
6157
|
return getDurationFromFlac(parserState);
|
|
6209
6158
|
}
|
|
6159
|
+
if (structure.type === "m3u") {
|
|
6160
|
+
return getDurationFromM3u(parserState.getM3uStructure());
|
|
6161
|
+
}
|
|
6210
6162
|
throw new Error("Has no duration " + structure);
|
|
6211
6163
|
};
|
|
6212
6164
|
var hasDuration = (parserState) => {
|
|
6165
|
+
const structure = parserState.getStructureOrNull();
|
|
6166
|
+
if (structure === null) {
|
|
6167
|
+
return false;
|
|
6168
|
+
}
|
|
6169
|
+
if (structure.type === "iso-base-media") {
|
|
6170
|
+
return isoHasDuration(parserState);
|
|
6171
|
+
}
|
|
6213
6172
|
return getHasTracks(parserState);
|
|
6214
6173
|
};
|
|
6215
6174
|
var hasSlowDuration = (parserState) => {
|
|
6216
6175
|
try {
|
|
6217
|
-
return getDuration(parserState) !== null;
|
|
6176
|
+
return hasDuration(parserState) && getDuration(parserState) !== null;
|
|
6218
6177
|
} catch {
|
|
6219
6178
|
return false;
|
|
6220
6179
|
}
|
|
@@ -6485,7 +6444,7 @@ var getMetadata = (state) => {
|
|
|
6485
6444
|
if (structure.type === "riff") {
|
|
6486
6445
|
return getMetadataFromRiff(structure);
|
|
6487
6446
|
}
|
|
6488
|
-
if (structure.type === "transport-stream") {
|
|
6447
|
+
if (structure.type === "transport-stream" || structure.type === "m3u") {
|
|
6489
6448
|
return [];
|
|
6490
6449
|
}
|
|
6491
6450
|
if (structure.type === "mp3") {
|
|
@@ -6504,7 +6463,10 @@ var getMetadata = (state) => {
|
|
|
6504
6463
|
if (structure.type === "flac") {
|
|
6505
6464
|
return getMetadataFromFlac(structure) ?? [];
|
|
6506
6465
|
}
|
|
6507
|
-
|
|
6466
|
+
if (structure.type === "iso-base-media") {
|
|
6467
|
+
return getMetadataFromIsoBase(state);
|
|
6468
|
+
}
|
|
6469
|
+
throw new Error("Unknown container " + structure);
|
|
6508
6470
|
};
|
|
6509
6471
|
var hasMetadata = (structure) => {
|
|
6510
6472
|
if (structure.type === "mp3") {
|
|
@@ -6513,7 +6475,25 @@ var hasMetadata = (structure) => {
|
|
|
6513
6475
|
if (structure.type === "wav") {
|
|
6514
6476
|
return getMetadataFromWav(structure) !== null;
|
|
6515
6477
|
}
|
|
6516
|
-
|
|
6478
|
+
if (structure.type === "m3u" || structure.type === "transport-stream") {
|
|
6479
|
+
return true;
|
|
6480
|
+
}
|
|
6481
|
+
if (structure.type === "flac") {
|
|
6482
|
+
return getMetadataFromFlac(structure) !== null;
|
|
6483
|
+
}
|
|
6484
|
+
if (structure.type === "iso-base-media") {
|
|
6485
|
+
return false;
|
|
6486
|
+
}
|
|
6487
|
+
if (structure.type === "matroska") {
|
|
6488
|
+
return false;
|
|
6489
|
+
}
|
|
6490
|
+
if (structure.type === "riff") {
|
|
6491
|
+
return false;
|
|
6492
|
+
}
|
|
6493
|
+
if (structure.type === "aac") {
|
|
6494
|
+
return true;
|
|
6495
|
+
}
|
|
6496
|
+
throw new Error("Unknown container " + structure);
|
|
6517
6497
|
};
|
|
6518
6498
|
|
|
6519
6499
|
// src/get-location.ts
|
|
@@ -6891,6 +6871,17 @@ var emitAvailableInfo = async ({
|
|
|
6891
6871
|
}
|
|
6892
6872
|
continue;
|
|
6893
6873
|
}
|
|
6874
|
+
if (key === "m3uStreams") {
|
|
6875
|
+
if (!emittedFields.m3uStreams && hasInfo.m3uStreams) {
|
|
6876
|
+
const streams = getM3uStreams(state.getStructureOrNull(), typeof state.src === "string" ? state.src : null);
|
|
6877
|
+
await callbacks.onM3uStreams?.(streams);
|
|
6878
|
+
if (fieldsInReturnValue.m3uStreams) {
|
|
6879
|
+
returnValue.m3uStreams = streams;
|
|
6880
|
+
}
|
|
6881
|
+
emittedFields.m3uStreams = true;
|
|
6882
|
+
}
|
|
6883
|
+
continue;
|
|
6884
|
+
}
|
|
6894
6885
|
throw new Error(`Unhandled key: ${key}`);
|
|
6895
6886
|
}
|
|
6896
6887
|
};
|
|
@@ -6928,6 +6919,7 @@ var getFieldsFromCallback = ({
|
|
|
6928
6919
|
sampleRate: Boolean(callbacks.onSampleRate),
|
|
6929
6920
|
slowAudioBitrate: Boolean(callbacks.onSlowAudioBitrate),
|
|
6930
6921
|
slowVideoBitrate: Boolean(callbacks.onSlowVideoBitrate),
|
|
6922
|
+
m3uStreams: Boolean(callbacks.onM3uStreams),
|
|
6931
6923
|
...fields
|
|
6932
6924
|
};
|
|
6933
6925
|
return newFields;
|
|
@@ -6961,7 +6953,8 @@ var needsSamples = {
|
|
|
6961
6953
|
numberOfAudioChannels: false,
|
|
6962
6954
|
sampleRate: false,
|
|
6963
6955
|
slowAudioBitrate: true,
|
|
6964
|
-
slowVideoBitrate: true
|
|
6956
|
+
slowVideoBitrate: true,
|
|
6957
|
+
m3uStreams: false
|
|
6965
6958
|
};
|
|
6966
6959
|
var needsToIterateOverSamples = ({
|
|
6967
6960
|
fields,
|
|
@@ -7052,6 +7045,9 @@ var getAvailableInfo = ({
|
|
|
7052
7045
|
if (key === "sampleRate") {
|
|
7053
7046
|
return hasSampleRate(state);
|
|
7054
7047
|
}
|
|
7048
|
+
if (key === "m3uStreams") {
|
|
7049
|
+
return m3uHasStreams(state);
|
|
7050
|
+
}
|
|
7055
7051
|
throw new Error(`Unknown key: ${key}`);
|
|
7056
7052
|
});
|
|
7057
7053
|
const entries = [];
|
|
@@ -7080,7 +7076,8 @@ var hasAllInfo = ({
|
|
|
7080
7076
|
class MediaParserEmitter {
|
|
7081
7077
|
listeners = {
|
|
7082
7078
|
pause: [],
|
|
7083
|
-
resume: []
|
|
7079
|
+
resume: [],
|
|
7080
|
+
abort: []
|
|
7084
7081
|
};
|
|
7085
7082
|
addEventListener = (name, callback) => {
|
|
7086
7083
|
this.listeners[name].push(callback);
|
|
@@ -7099,7 +7096,136 @@ class MediaParserEmitter {
|
|
|
7099
7096
|
dispatchResume = () => {
|
|
7100
7097
|
this.dispatchEvent("resume", undefined);
|
|
7101
7098
|
};
|
|
7099
|
+
dispatchAbort = (reason) => {
|
|
7100
|
+
this.dispatchEvent("abort", { reason });
|
|
7101
|
+
};
|
|
7102
|
+
}
|
|
7103
|
+
|
|
7104
|
+
// src/errors.ts
|
|
7105
|
+
class IsAGifError extends Error {
|
|
7106
|
+
mimeType;
|
|
7107
|
+
sizeInBytes;
|
|
7108
|
+
fileName;
|
|
7109
|
+
constructor({
|
|
7110
|
+
message,
|
|
7111
|
+
mimeType,
|
|
7112
|
+
sizeInBytes,
|
|
7113
|
+
fileName
|
|
7114
|
+
}) {
|
|
7115
|
+
super(message);
|
|
7116
|
+
this.fileName = "IsAGifError";
|
|
7117
|
+
this.mimeType = mimeType;
|
|
7118
|
+
this.sizeInBytes = sizeInBytes;
|
|
7119
|
+
this.fileName = fileName;
|
|
7120
|
+
if (Error.captureStackTrace) {
|
|
7121
|
+
Error.captureStackTrace(this, IsAGifError);
|
|
7122
|
+
}
|
|
7123
|
+
}
|
|
7124
|
+
}
|
|
7125
|
+
|
|
7126
|
+
class IsAnImageError extends Error {
|
|
7127
|
+
imageType;
|
|
7128
|
+
dimensions;
|
|
7129
|
+
mimeType;
|
|
7130
|
+
sizeInBytes;
|
|
7131
|
+
fileName;
|
|
7132
|
+
constructor({
|
|
7133
|
+
dimensions,
|
|
7134
|
+
imageType,
|
|
7135
|
+
message,
|
|
7136
|
+
mimeType,
|
|
7137
|
+
sizeInBytes,
|
|
7138
|
+
fileName
|
|
7139
|
+
}) {
|
|
7140
|
+
super(message);
|
|
7141
|
+
this.name = "IsAnImageError";
|
|
7142
|
+
this.imageType = imageType;
|
|
7143
|
+
this.dimensions = dimensions;
|
|
7144
|
+
this.mimeType = mimeType;
|
|
7145
|
+
this.sizeInBytes = sizeInBytes;
|
|
7146
|
+
this.fileName = fileName;
|
|
7147
|
+
if (Error.captureStackTrace) {
|
|
7148
|
+
Error.captureStackTrace(this, IsAnImageError);
|
|
7149
|
+
}
|
|
7150
|
+
}
|
|
7151
|
+
}
|
|
7152
|
+
|
|
7153
|
+
class IsAPdfError extends Error {
|
|
7154
|
+
mimeType;
|
|
7155
|
+
sizeInBytes;
|
|
7156
|
+
fileName;
|
|
7157
|
+
constructor({
|
|
7158
|
+
message,
|
|
7159
|
+
mimeType,
|
|
7160
|
+
sizeInBytes,
|
|
7161
|
+
fileName
|
|
7162
|
+
}) {
|
|
7163
|
+
super(message);
|
|
7164
|
+
this.name = "IsAPdfError";
|
|
7165
|
+
this.mimeType = mimeType;
|
|
7166
|
+
this.sizeInBytes = sizeInBytes;
|
|
7167
|
+
this.fileName = fileName;
|
|
7168
|
+
if (Error.captureStackTrace) {
|
|
7169
|
+
Error.captureStackTrace(this, IsAPdfError);
|
|
7170
|
+
}
|
|
7171
|
+
}
|
|
7172
|
+
}
|
|
7173
|
+
|
|
7174
|
+
class IsAnUnsupportedFileTypeError extends Error {
|
|
7175
|
+
mimeType;
|
|
7176
|
+
sizeInBytes;
|
|
7177
|
+
fileName;
|
|
7178
|
+
constructor({
|
|
7179
|
+
message,
|
|
7180
|
+
mimeType,
|
|
7181
|
+
sizeInBytes,
|
|
7182
|
+
fileName
|
|
7183
|
+
}) {
|
|
7184
|
+
super(message);
|
|
7185
|
+
this.name = "IsAnUnsupportedFileTypeError";
|
|
7186
|
+
this.mimeType = mimeType;
|
|
7187
|
+
this.sizeInBytes = sizeInBytes;
|
|
7188
|
+
this.fileName = fileName;
|
|
7189
|
+
if (Error.captureStackTrace) {
|
|
7190
|
+
Error.captureStackTrace(this, IsAnUnsupportedFileTypeError);
|
|
7191
|
+
}
|
|
7192
|
+
}
|
|
7193
|
+
}
|
|
7194
|
+
|
|
7195
|
+
class IsAnUnsupportedAudioTypeError extends Error {
|
|
7196
|
+
mimeType;
|
|
7197
|
+
sizeInBytes;
|
|
7198
|
+
fileName;
|
|
7199
|
+
audioType;
|
|
7200
|
+
constructor({
|
|
7201
|
+
message,
|
|
7202
|
+
mimeType,
|
|
7203
|
+
sizeInBytes,
|
|
7204
|
+
fileName,
|
|
7205
|
+
audioType
|
|
7206
|
+
}) {
|
|
7207
|
+
super(message);
|
|
7208
|
+
this.name = "IsAnUnsupportedAudioTypeError";
|
|
7209
|
+
this.mimeType = mimeType;
|
|
7210
|
+
this.sizeInBytes = sizeInBytes;
|
|
7211
|
+
this.fileName = fileName;
|
|
7212
|
+
this.audioType = audioType;
|
|
7213
|
+
if (Error.captureStackTrace) {
|
|
7214
|
+
Error.captureStackTrace(this, IsAnUnsupportedAudioTypeError);
|
|
7215
|
+
}
|
|
7216
|
+
}
|
|
7217
|
+
}
|
|
7218
|
+
|
|
7219
|
+
class MediaParserAbortError extends Error {
|
|
7220
|
+
constructor(message) {
|
|
7221
|
+
super(message);
|
|
7222
|
+
this.name = "MediaParserAbortError";
|
|
7223
|
+
this.cause = undefined;
|
|
7224
|
+
}
|
|
7102
7225
|
}
|
|
7226
|
+
var hasBeenAborted = (error) => {
|
|
7227
|
+
return error instanceof MediaParserAbortError;
|
|
7228
|
+
};
|
|
7103
7229
|
|
|
7104
7230
|
// src/pause-signal.ts
|
|
7105
7231
|
var makePauseSignal = (emitter) => {
|
|
@@ -7143,13 +7269,14 @@ var mediaParserController = () => {
|
|
|
7143
7269
|
const pauseSignal = makePauseSignal(emitter);
|
|
7144
7270
|
const checkForAbortAndPause = async () => {
|
|
7145
7271
|
if (abortController.signal.aborted) {
|
|
7146
|
-
throw new
|
|
7272
|
+
throw new MediaParserAbortError("Aborted");
|
|
7147
7273
|
}
|
|
7148
7274
|
await pauseSignal.waitUntilResume();
|
|
7149
7275
|
};
|
|
7150
7276
|
return {
|
|
7151
7277
|
abort: (reason) => {
|
|
7152
7278
|
abortController.abort(reason);
|
|
7279
|
+
emitter.dispatchAbort(reason);
|
|
7153
7280
|
},
|
|
7154
7281
|
pause: pauseSignal.pause,
|
|
7155
7282
|
resume: pauseSignal.resume,
|
|
@@ -7279,7 +7406,7 @@ var parseAac = async (state) => {
|
|
|
7279
7406
|
iterator.counter.decrement(iterator.counter.getOffset() - startOffset);
|
|
7280
7407
|
const data = iterator.getSlice(frameLength);
|
|
7281
7408
|
if (state.callbacks.tracks.getTracks().length === 0) {
|
|
7282
|
-
await
|
|
7409
|
+
await registerAudioTrack({
|
|
7283
7410
|
state,
|
|
7284
7411
|
container: "aac",
|
|
7285
7412
|
track: {
|
|
@@ -7656,7 +7783,7 @@ var parseStreamInfo = async ({
|
|
|
7656
7783
|
totalSamples
|
|
7657
7784
|
};
|
|
7658
7785
|
state.getFlacStructure().boxes.push(flacStreamInfo);
|
|
7659
|
-
await
|
|
7786
|
+
await registerAudioTrack({
|
|
7660
7787
|
container: "flac",
|
|
7661
7788
|
state,
|
|
7662
7789
|
track: {
|
|
@@ -7814,7 +7941,8 @@ var emittedState = () => {
|
|
|
7814
7941
|
numberOfAudioChannels: false,
|
|
7815
7942
|
sampleRate: false,
|
|
7816
7943
|
slowAudioBitrate: false,
|
|
7817
|
-
slowVideoBitrate: false
|
|
7944
|
+
slowVideoBitrate: false,
|
|
7945
|
+
m3uStreams: false
|
|
7818
7946
|
};
|
|
7819
7947
|
return emittedFields;
|
|
7820
7948
|
};
|
|
@@ -7889,6 +8017,52 @@ var eventLoopState = (logLevel) => {
|
|
|
7889
8017
|
return { eventLoopBreakIfNeeded };
|
|
7890
8018
|
};
|
|
7891
8019
|
|
|
8020
|
+
// src/state/m3u-state.ts
|
|
8021
|
+
var m3uState = () => {
|
|
8022
|
+
let selectedStream = null;
|
|
8023
|
+
let hasEmittedVideoTrack = false;
|
|
8024
|
+
let hasEmittedAudioTrack = false;
|
|
8025
|
+
let hasEmittedDoneWithTracks = false;
|
|
8026
|
+
let hasFinishedManifest = false;
|
|
8027
|
+
let readyToIterateOverM3u = false;
|
|
8028
|
+
let lastChunkProcessed = -1;
|
|
8029
|
+
let allChunksProcessed = false;
|
|
8030
|
+
return {
|
|
8031
|
+
setSelectedStream: (stream) => {
|
|
8032
|
+
selectedStream = stream;
|
|
8033
|
+
},
|
|
8034
|
+
getSelectedStream: () => selectedStream,
|
|
8035
|
+
setHasEmittedVideoTrack: (callback) => {
|
|
8036
|
+
hasEmittedVideoTrack = callback;
|
|
8037
|
+
},
|
|
8038
|
+
hasEmittedVideoTrack: () => hasEmittedVideoTrack,
|
|
8039
|
+
setHasEmittedAudioTrack: (callback) => {
|
|
8040
|
+
hasEmittedAudioTrack = callback;
|
|
8041
|
+
},
|
|
8042
|
+
hasEmittedAudioTrack: () => hasEmittedAudioTrack,
|
|
8043
|
+
setHasEmittedDoneWithTracks: () => {
|
|
8044
|
+
hasEmittedDoneWithTracks = true;
|
|
8045
|
+
},
|
|
8046
|
+
hasEmittedDoneWithTracks: () => hasEmittedDoneWithTracks,
|
|
8047
|
+
setReadyToIterateOverM3u: () => {
|
|
8048
|
+
readyToIterateOverM3u = true;
|
|
8049
|
+
},
|
|
8050
|
+
isReadyToIterateOverM3u: () => readyToIterateOverM3u,
|
|
8051
|
+
setLastChunkProcessed: (chunk) => {
|
|
8052
|
+
lastChunkProcessed = chunk;
|
|
8053
|
+
},
|
|
8054
|
+
getLastChunkProcessed: () => lastChunkProcessed,
|
|
8055
|
+
getAllChunksProcessed: () => allChunksProcessed,
|
|
8056
|
+
setAllChunksProcessed: () => {
|
|
8057
|
+
allChunksProcessed = true;
|
|
8058
|
+
},
|
|
8059
|
+
setHasFinishedManifest: () => {
|
|
8060
|
+
hasFinishedManifest = true;
|
|
8061
|
+
},
|
|
8062
|
+
hasFinishedManifest: () => hasFinishedManifest
|
|
8063
|
+
};
|
|
8064
|
+
};
|
|
8065
|
+
|
|
7892
8066
|
// src/state/mp3.ts
|
|
7893
8067
|
var makeMp3State = () => {
|
|
7894
8068
|
let mp3Info = null;
|
|
@@ -7941,7 +8115,7 @@ var needsTracksForField = ({
|
|
|
7941
8115
|
}
|
|
7942
8116
|
return true;
|
|
7943
8117
|
}
|
|
7944
|
-
if (field === "audioCodec" || field === "durationInSeconds" || field === "slowDurationInSeconds" || field === "slowFps" || field === "fps" || field === "isHdr" || field === "rotation" || field === "structure" || field === "tracks" || field === "unrotatedDimensions" || field === "videoCodec" || field === "metadata" || field === "location" || field === "slowKeyframes" || field === "slowNumberOfFrames" || field === "keyframes" || field === "images" || field === "sampleRate" || field === "numberOfAudioChannels" || field === "slowAudioBitrate" || field === "slowVideoBitrate") {
|
|
8118
|
+
if (field === "audioCodec" || field === "durationInSeconds" || field === "slowDurationInSeconds" || field === "slowFps" || field === "fps" || field === "isHdr" || field === "rotation" || field === "structure" || field === "tracks" || field === "unrotatedDimensions" || field === "videoCodec" || field === "metadata" || field === "location" || field === "slowKeyframes" || field === "slowNumberOfFrames" || field === "keyframes" || field === "images" || field === "sampleRate" || field === "numberOfAudioChannels" || field === "slowAudioBitrate" || field === "slowVideoBitrate" || field === "m3uStreams") {
|
|
7945
8119
|
return true;
|
|
7946
8120
|
}
|
|
7947
8121
|
if (field === "container" || field === "internalStats" || field === "mimeType" || field === "name" || field === "size") {
|
|
@@ -8224,6 +8398,13 @@ var structureState = () => {
|
|
|
8224
8398
|
}
|
|
8225
8399
|
return struc;
|
|
8226
8400
|
},
|
|
8401
|
+
getM3uStructure: () => {
|
|
8402
|
+
const struc = getStructure();
|
|
8403
|
+
if (struc.type !== "m3u") {
|
|
8404
|
+
throw new Error("Invalid structure type");
|
|
8405
|
+
}
|
|
8406
|
+
return struc;
|
|
8407
|
+
},
|
|
8227
8408
|
getRiffStructure: () => {
|
|
8228
8409
|
const struc = getStructure();
|
|
8229
8410
|
if (struc.type !== "riff") {
|
|
@@ -8411,7 +8592,8 @@ var makeParserState = ({
|
|
|
8411
8592
|
mode,
|
|
8412
8593
|
src,
|
|
8413
8594
|
readerInterface,
|
|
8414
|
-
onDiscardedData
|
|
8595
|
+
onDiscardedData,
|
|
8596
|
+
selectM3uStreamFn
|
|
8415
8597
|
}) => {
|
|
8416
8598
|
let skippedBytes = 0;
|
|
8417
8599
|
const iterator = getArrayBufferIterator(new Uint8Array([]), contentLength);
|
|
@@ -8441,6 +8623,7 @@ var makeParserState = ({
|
|
|
8441
8623
|
mp3Info,
|
|
8442
8624
|
aac: aacState(),
|
|
8443
8625
|
flac: flacState(),
|
|
8626
|
+
m3u: m3uState(),
|
|
8444
8627
|
callbacks: sampleCallback({
|
|
8445
8628
|
controller,
|
|
8446
8629
|
hasAudioTrackHandlers,
|
|
@@ -8474,7 +8657,8 @@ var makeParserState = ({
|
|
|
8474
8657
|
eventLoop: eventLoopState(logLevel),
|
|
8475
8658
|
src,
|
|
8476
8659
|
readerInterface,
|
|
8477
|
-
discardReadBytes
|
|
8660
|
+
discardReadBytes,
|
|
8661
|
+
selectM3uStreamFn
|
|
8478
8662
|
};
|
|
8479
8663
|
};
|
|
8480
8664
|
|
|
@@ -8498,11 +8682,11 @@ var getMoovAtom = async ({
|
|
|
8498
8682
|
structure: true
|
|
8499
8683
|
},
|
|
8500
8684
|
onAudioTrack: state.onAudioTrack ? async ({ track, container }) => {
|
|
8501
|
-
await
|
|
8685
|
+
await registerAudioTrack({ state, track, container });
|
|
8502
8686
|
return null;
|
|
8503
8687
|
} : null,
|
|
8504
8688
|
onVideoTrack: state.onVideoTrack ? async ({ track, container }) => {
|
|
8505
|
-
await
|
|
8689
|
+
await registerVideoTrack({ state, track, container });
|
|
8506
8690
|
return null;
|
|
8507
8691
|
} : null,
|
|
8508
8692
|
contentLength: state.contentLength,
|
|
@@ -8510,7 +8694,8 @@ var getMoovAtom = async ({
|
|
|
8510
8694
|
mode: "query",
|
|
8511
8695
|
readerInterface: state.readerInterface,
|
|
8512
8696
|
src: state.src,
|
|
8513
|
-
onDiscardedData: null
|
|
8697
|
+
onDiscardedData: null,
|
|
8698
|
+
selectM3uStreamFn: state.selectM3uStreamFn
|
|
8514
8699
|
});
|
|
8515
8700
|
while (true) {
|
|
8516
8701
|
const result = await reader.reader.read();
|
|
@@ -8549,85 +8734,699 @@ var parseMdatSection = async (state) => {
|
|
|
8549
8734
|
if (maySkipVideoData({ state })) {
|
|
8550
8735
|
return makeSkip(endOfMdat);
|
|
8551
8736
|
}
|
|
8552
|
-
const alreadyHas = getHasTracks(state);
|
|
8553
|
-
if (!alreadyHas) {
|
|
8554
|
-
const moov = await getMoovAtom({
|
|
8555
|
-
endOfMdat,
|
|
8556
|
-
state
|
|
8737
|
+
const alreadyHas = getHasTracks(state);
|
|
8738
|
+
if (!alreadyHas) {
|
|
8739
|
+
const moov = await getMoovAtom({
|
|
8740
|
+
endOfMdat,
|
|
8741
|
+
state
|
|
8742
|
+
});
|
|
8743
|
+
state.iso.moov.setMoovBox(moov);
|
|
8744
|
+
state.callbacks.tracks.setIsDone(state.logLevel);
|
|
8745
|
+
state.getIsoStructure().boxes.push(moov);
|
|
8746
|
+
return parseMdatSection(state);
|
|
8747
|
+
}
|
|
8748
|
+
if (!state.iso.flatSamples.getSamples(videoSection.start)) {
|
|
8749
|
+
state.iso.flatSamples.setSamples(videoSection.start, calculateFlatSamples(state));
|
|
8750
|
+
}
|
|
8751
|
+
const flatSamples = state.iso.flatSamples.getSamples(videoSection.start);
|
|
8752
|
+
const { iterator } = state;
|
|
8753
|
+
const samplesWithIndex = flatSamples.find((sample) => {
|
|
8754
|
+
return sample.samplePosition.offset === iterator.counter.getOffset();
|
|
8755
|
+
});
|
|
8756
|
+
if (!samplesWithIndex) {
|
|
8757
|
+
const nextSample_ = flatSamples.filter((s) => s.samplePosition.offset > iterator.counter.getOffset()).sort((a, b) => a.samplePosition.offset - b.samplePosition.offset)[0];
|
|
8758
|
+
if (nextSample_) {
|
|
8759
|
+
iterator.discard(nextSample_.samplePosition.offset - iterator.counter.getOffset());
|
|
8760
|
+
return null;
|
|
8761
|
+
}
|
|
8762
|
+
return makeSkip(endOfMdat);
|
|
8763
|
+
}
|
|
8764
|
+
if (iterator.bytesRemaining() < samplesWithIndex.samplePosition.size) {
|
|
8765
|
+
return null;
|
|
8766
|
+
}
|
|
8767
|
+
const bytes = iterator.getSlice(samplesWithIndex.samplePosition.size);
|
|
8768
|
+
const { cts, dts, duration: duration2, isKeyframe, offset } = samplesWithIndex.samplePosition;
|
|
8769
|
+
if (samplesWithIndex.track.type === "audio") {
|
|
8770
|
+
await state.callbacks.onAudioSample(samplesWithIndex.track.trackId, convertAudioOrVideoSampleToWebCodecsTimestamps({
|
|
8771
|
+
data: bytes,
|
|
8772
|
+
timestamp: cts,
|
|
8773
|
+
duration: duration2,
|
|
8774
|
+
cts,
|
|
8775
|
+
dts,
|
|
8776
|
+
trackId: samplesWithIndex.track.trackId,
|
|
8777
|
+
type: isKeyframe ? "key" : "delta",
|
|
8778
|
+
offset,
|
|
8779
|
+
timescale: samplesWithIndex.track.timescale
|
|
8780
|
+
}, samplesWithIndex.track.timescale));
|
|
8781
|
+
}
|
|
8782
|
+
if (samplesWithIndex.track.type === "video") {
|
|
8783
|
+
const nalUnitType = bytes[4] & 31;
|
|
8784
|
+
let isRecoveryPoint = false;
|
|
8785
|
+
if (nalUnitType === 6) {
|
|
8786
|
+
const seiType = bytes[5];
|
|
8787
|
+
isRecoveryPoint = seiType === 6;
|
|
8788
|
+
}
|
|
8789
|
+
await state.callbacks.onVideoSample(samplesWithIndex.track.trackId, convertAudioOrVideoSampleToWebCodecsTimestamps({
|
|
8790
|
+
data: bytes,
|
|
8791
|
+
timestamp: cts,
|
|
8792
|
+
duration: duration2,
|
|
8793
|
+
cts,
|
|
8794
|
+
dts,
|
|
8795
|
+
trackId: samplesWithIndex.track.trackId,
|
|
8796
|
+
type: isKeyframe && !isRecoveryPoint ? "key" : "delta",
|
|
8797
|
+
offset,
|
|
8798
|
+
timescale: samplesWithIndex.track.timescale
|
|
8799
|
+
}, samplesWithIndex.track.timescale));
|
|
8800
|
+
}
|
|
8801
|
+
return null;
|
|
8802
|
+
};
|
|
8803
|
+
|
|
8804
|
+
// src/containers/iso-base-media/parse-boxes.ts
|
|
8805
|
+
var parseIsoBaseMedia = async (state) => {
|
|
8806
|
+
const videoSectionState2 = state.videoSection.isInVideoSectionState(state.iterator);
|
|
8807
|
+
if (videoSectionState2 === "in-section") {
|
|
8808
|
+
const skipTo = await parseMdatSection(state);
|
|
8809
|
+
return skipTo;
|
|
8810
|
+
}
|
|
8811
|
+
const result = await processBox(state);
|
|
8812
|
+
if (result) {
|
|
8813
|
+
state.getIsoStructure().boxes.push(result);
|
|
8814
|
+
}
|
|
8815
|
+
return null;
|
|
8816
|
+
};
|
|
8817
|
+
|
|
8818
|
+
// src/containers/m3u/parse-stream-inf.ts
|
|
8819
|
+
function splitRespectingQuotes(input) {
|
|
8820
|
+
const result = [];
|
|
8821
|
+
let currentPart = "";
|
|
8822
|
+
let insideQuote = false;
|
|
8823
|
+
for (let i = 0;i < input.length; i++) {
|
|
8824
|
+
const char = input[i];
|
|
8825
|
+
if (char === '"') {
|
|
8826
|
+
insideQuote = !insideQuote;
|
|
8827
|
+
currentPart += char;
|
|
8828
|
+
} else if (char === "," && !insideQuote) {
|
|
8829
|
+
result.push(currentPart);
|
|
8830
|
+
currentPart = "";
|
|
8831
|
+
} else {
|
|
8832
|
+
currentPart += char;
|
|
8833
|
+
}
|
|
8834
|
+
}
|
|
8835
|
+
if (currentPart) {
|
|
8836
|
+
result.push(currentPart);
|
|
8837
|
+
}
|
|
8838
|
+
return result;
|
|
8839
|
+
}
|
|
8840
|
+
var parseStreamInf = (str) => {
|
|
8841
|
+
const quotes = splitRespectingQuotes(str);
|
|
8842
|
+
const map = {};
|
|
8843
|
+
for (const quote of quotes) {
|
|
8844
|
+
const firstColon = quote.indexOf("=");
|
|
8845
|
+
const key = firstColon === -1 ? quote : quote.slice(0, firstColon);
|
|
8846
|
+
const value = firstColon === -1 ? null : quote.slice(firstColon + 1);
|
|
8847
|
+
if (value === null) {
|
|
8848
|
+
throw new Error("Value is null");
|
|
8849
|
+
}
|
|
8850
|
+
const actualValue = value?.startsWith('"') && value?.endsWith('"') ? value.slice(1, -1) : value;
|
|
8851
|
+
map[key] = actualValue;
|
|
8852
|
+
}
|
|
8853
|
+
return {
|
|
8854
|
+
type: "m3u-stream-info",
|
|
8855
|
+
averageBandwidth: map["AVERAGE-BANDWIDTH"] ? parseInt(map["AVERAGE-BANDWIDTH"], 10) : null,
|
|
8856
|
+
bandwidth: map.BANDWIDTH ? parseInt(map.BANDWIDTH, 10) : null,
|
|
8857
|
+
codecs: map.CODECS ? map.CODECS.split(",") : null,
|
|
8858
|
+
resolution: map.RESOLUTION ? {
|
|
8859
|
+
width: parseInt(map.RESOLUTION.split("x")[0], 10),
|
|
8860
|
+
height: parseInt(map.RESOLUTION.split("x")[1], 10)
|
|
8861
|
+
} : null,
|
|
8862
|
+
audio: map.AUDIO || null
|
|
8863
|
+
};
|
|
8864
|
+
};
|
|
8865
|
+
|
|
8866
|
+
// src/containers/m3u/parse-m3u-media-directive.ts
|
|
8867
|
+
var parseM3uMediaDirective = (str) => {
|
|
8868
|
+
const quotes = splitRespectingQuotes(str);
|
|
8869
|
+
const map = {};
|
|
8870
|
+
for (const quote of quotes) {
|
|
8871
|
+
const firstColon = quote.indexOf("=");
|
|
8872
|
+
const key = firstColon === -1 ? quote : quote.slice(0, firstColon);
|
|
8873
|
+
const value = firstColon === -1 ? null : quote.slice(firstColon + 1);
|
|
8874
|
+
if (value === null) {
|
|
8875
|
+
throw new Error("Value is null");
|
|
8876
|
+
}
|
|
8877
|
+
const actualValue = value?.startsWith('"') && value?.endsWith('"') ? value.slice(1, -1) : value;
|
|
8878
|
+
map[key] = actualValue;
|
|
8879
|
+
}
|
|
8880
|
+
return {
|
|
8881
|
+
type: "m3u-media-info",
|
|
8882
|
+
autoselect: map.AUTOSELECT === "YES",
|
|
8883
|
+
channels: map.CHANNELS ? parseInt(map.CHANNELS, 10) : null,
|
|
8884
|
+
default: map.DEFAULT === "YES",
|
|
8885
|
+
groupId: map["GROUP-ID"],
|
|
8886
|
+
language: map.LANGUAGE || null,
|
|
8887
|
+
name: map.NAME || null,
|
|
8888
|
+
uri: map.URI
|
|
8889
|
+
};
|
|
8890
|
+
};
|
|
8891
|
+
|
|
8892
|
+
// src/containers/m3u/parse-directive.ts
|
|
8893
|
+
var parseM3uDirective = (str) => {
|
|
8894
|
+
const firstColon = str.indexOf(":");
|
|
8895
|
+
const directive = firstColon === -1 ? str : str.slice(0, firstColon);
|
|
8896
|
+
const value = firstColon === -1 ? null : str.slice(firstColon + 1);
|
|
8897
|
+
if (directive === "#EXT-X-VERSION") {
|
|
8898
|
+
if (!value) {
|
|
8899
|
+
throw new Error("EXT-X-VERSION directive must have a value");
|
|
8900
|
+
}
|
|
8901
|
+
return {
|
|
8902
|
+
type: "m3u-version",
|
|
8903
|
+
version: value
|
|
8904
|
+
};
|
|
8905
|
+
}
|
|
8906
|
+
if (directive === "#EXT-X-INDEPENDENT-SEGMENTS") {
|
|
8907
|
+
return {
|
|
8908
|
+
type: "m3u-independent-segments"
|
|
8909
|
+
};
|
|
8910
|
+
}
|
|
8911
|
+
if (directive === "#EXT-X-MEDIA") {
|
|
8912
|
+
if (!value) {
|
|
8913
|
+
throw new Error("EXT-X-MEDIA directive must have a value");
|
|
8914
|
+
}
|
|
8915
|
+
const parsed = parseM3uMediaDirective(value);
|
|
8916
|
+
return parsed;
|
|
8917
|
+
}
|
|
8918
|
+
if (directive === "#EXT-X-TARGETDURATION") {
|
|
8919
|
+
if (!value) {
|
|
8920
|
+
throw new Error("EXT-X-TARGETDURATION directive must have a value");
|
|
8921
|
+
}
|
|
8922
|
+
return {
|
|
8923
|
+
type: "m3u-target-duration",
|
|
8924
|
+
duration: parseFloat(value)
|
|
8925
|
+
};
|
|
8926
|
+
}
|
|
8927
|
+
if (directive === "#EXTINF") {
|
|
8928
|
+
if (!value) {
|
|
8929
|
+
throw new Error("EXTINF has no value");
|
|
8930
|
+
}
|
|
8931
|
+
return {
|
|
8932
|
+
type: "m3u-extinf",
|
|
8933
|
+
value: parseFloat(value)
|
|
8934
|
+
};
|
|
8935
|
+
}
|
|
8936
|
+
if (directive === "#EXT-X-ENDLIST") {
|
|
8937
|
+
return {
|
|
8938
|
+
type: "m3u-endlist"
|
|
8939
|
+
};
|
|
8940
|
+
}
|
|
8941
|
+
if (directive === "#EXT-X-PLAYLIST-TYPE") {
|
|
8942
|
+
if (!value) {
|
|
8943
|
+
throw new Error("#EXT-X-PLAYLIST-TYPE. directive must have a value");
|
|
8944
|
+
}
|
|
8945
|
+
return {
|
|
8946
|
+
type: "m3u-playlist-type",
|
|
8947
|
+
playlistType: value
|
|
8948
|
+
};
|
|
8949
|
+
}
|
|
8950
|
+
if (directive === "#EXT-X-STREAM-INF") {
|
|
8951
|
+
if (!value) {
|
|
8952
|
+
throw new Error("EXT-X-STREAM-INF directive must have a value");
|
|
8953
|
+
}
|
|
8954
|
+
const res = parseStreamInf(value);
|
|
8955
|
+
return res;
|
|
8956
|
+
}
|
|
8957
|
+
throw new Error(`Unknown directive ${directive}. Value: ${value}`);
|
|
8958
|
+
};
|
|
8959
|
+
|
|
8960
|
+
// src/containers/m3u/parse-m3u8-text.ts
|
|
8961
|
+
var parseM3u8Text = (line, boxes) => {
|
|
8962
|
+
if (line === "#EXTM3U") {
|
|
8963
|
+
boxes.push({
|
|
8964
|
+
type: "m3u-header"
|
|
8965
|
+
});
|
|
8966
|
+
return;
|
|
8967
|
+
}
|
|
8968
|
+
if (line.startsWith("#")) {
|
|
8969
|
+
boxes.push(parseM3uDirective(line));
|
|
8970
|
+
return;
|
|
8971
|
+
}
|
|
8972
|
+
if (line.trim()) {
|
|
8973
|
+
boxes.push({
|
|
8974
|
+
type: "m3u-text-value",
|
|
8975
|
+
value: line
|
|
8976
|
+
});
|
|
8977
|
+
}
|
|
8978
|
+
};
|
|
8979
|
+
|
|
8980
|
+
// src/containers/m3u/fetch-m3u8-stream.ts
|
|
8981
|
+
var fetchM3u8Stream = async (stream) => {
|
|
8982
|
+
const res = await fetch(stream.url);
|
|
8983
|
+
if (!res.ok) {
|
|
8984
|
+
throw new Error(`Failed to fetch ${stream.url} (HTTP code: ${res.status})`);
|
|
8985
|
+
}
|
|
8986
|
+
const text = await res.text();
|
|
8987
|
+
const lines = text.split(`
|
|
8988
|
+
`);
|
|
8989
|
+
const boxes = [];
|
|
8990
|
+
for (const line of lines) {
|
|
8991
|
+
parseM3u8Text(line, boxes);
|
|
8992
|
+
}
|
|
8993
|
+
return boxes;
|
|
8994
|
+
};
|
|
8995
|
+
|
|
8996
|
+
// src/forward-controller.ts
|
|
8997
|
+
var forwardMediaParserController = ({
|
|
8998
|
+
parentController,
|
|
8999
|
+
childController
|
|
9000
|
+
}) => {
|
|
9001
|
+
const onAbort = ({ detail }) => {
|
|
9002
|
+
childController.abort(detail.reason);
|
|
9003
|
+
};
|
|
9004
|
+
const onResume = () => {
|
|
9005
|
+
childController.resume();
|
|
9006
|
+
};
|
|
9007
|
+
const onPause = () => {
|
|
9008
|
+
childController.pause();
|
|
9009
|
+
};
|
|
9010
|
+
parentController.addEventListener("abort", onAbort);
|
|
9011
|
+
parentController.addEventListener("resume", onResume);
|
|
9012
|
+
parentController.addEventListener("pause", onPause);
|
|
9013
|
+
return {
|
|
9014
|
+
cleanup: () => {
|
|
9015
|
+
parentController.removeEventListener("abort", onAbort);
|
|
9016
|
+
parentController.removeEventListener("resume", onResume);
|
|
9017
|
+
parentController.removeEventListener("pause", onPause);
|
|
9018
|
+
}
|
|
9019
|
+
};
|
|
9020
|
+
};
|
|
9021
|
+
|
|
9022
|
+
// src/containers/m3u/select-stream.ts
|
|
9023
|
+
var selectStream = async ({
|
|
9024
|
+
streams,
|
|
9025
|
+
fn
|
|
9026
|
+
}) => {
|
|
9027
|
+
if (streams.length < 1) {
|
|
9028
|
+
throw new Error("No streams found");
|
|
9029
|
+
}
|
|
9030
|
+
const selectedStreamId = await fn({ streams });
|
|
9031
|
+
const selectedStream = streams.find((stream) => stream.id === selectedStreamId);
|
|
9032
|
+
if (!selectedStream) {
|
|
9033
|
+
throw new Error(`No stream with the id ${selectedStreamId} found`);
|
|
9034
|
+
}
|
|
9035
|
+
return Promise.resolve(selectedStream);
|
|
9036
|
+
};
|
|
9037
|
+
var defaultSelectM3uStreamFn = ({ streams }) => {
|
|
9038
|
+
return Promise.resolve(streams[0].id);
|
|
9039
|
+
};
|
|
9040
|
+
|
|
9041
|
+
// src/readers/fetch/get-body-and-reader.ts
|
|
9042
|
+
var getLengthAndReader = async (endsWithM3u8, res, ownController) => {
|
|
9043
|
+
if (endsWithM3u8) {
|
|
9044
|
+
const text = await res.text();
|
|
9045
|
+
const encoded = new TextEncoder().encode(text);
|
|
9046
|
+
const stream = new ReadableStream({
|
|
9047
|
+
start(controller) {
|
|
9048
|
+
controller.enqueue(encoded);
|
|
9049
|
+
controller.close();
|
|
9050
|
+
}
|
|
9051
|
+
});
|
|
9052
|
+
return {
|
|
9053
|
+
contentLength: encoded.byteLength,
|
|
9054
|
+
reader: {
|
|
9055
|
+
reader: stream.getReader(),
|
|
9056
|
+
abort() {
|
|
9057
|
+
ownController.abort();
|
|
9058
|
+
}
|
|
9059
|
+
},
|
|
9060
|
+
needsContentRange: false
|
|
9061
|
+
};
|
|
9062
|
+
}
|
|
9063
|
+
const length = res.headers.get("content-length");
|
|
9064
|
+
const contentLength = length === null ? null : parseInt(length, 10);
|
|
9065
|
+
if (!res.body) {
|
|
9066
|
+
throw new Error("No body");
|
|
9067
|
+
}
|
|
9068
|
+
const reader = res.body.getReader();
|
|
9069
|
+
return {
|
|
9070
|
+
reader: {
|
|
9071
|
+
reader,
|
|
9072
|
+
abort: () => {
|
|
9073
|
+
ownController.abort();
|
|
9074
|
+
}
|
|
9075
|
+
},
|
|
9076
|
+
contentLength,
|
|
9077
|
+
needsContentRange: true
|
|
9078
|
+
};
|
|
9079
|
+
};
|
|
9080
|
+
|
|
9081
|
+
// src/readers/fetch/resolve-url.ts
|
|
9082
|
+
var resolveUrl = (src) => {
|
|
9083
|
+
try {
|
|
9084
|
+
const resolvedUrl = typeof window !== "undefined" && typeof window.location !== "undefined" ? new URL(src, window.location.origin) : new URL(src);
|
|
9085
|
+
return resolvedUrl;
|
|
9086
|
+
} catch {
|
|
9087
|
+
return src;
|
|
9088
|
+
}
|
|
9089
|
+
};
|
|
9090
|
+
|
|
9091
|
+
// src/readers/from-fetch.ts
|
|
9092
|
+
function parseContentRange(input) {
|
|
9093
|
+
const matches = input.match(/^(\w+) ((\d+)-(\d+)|\*)\/(\d+|\*)$/);
|
|
9094
|
+
if (!matches)
|
|
9095
|
+
return null;
|
|
9096
|
+
const [, unit, , start, end, size] = matches;
|
|
9097
|
+
const range2 = {
|
|
9098
|
+
unit,
|
|
9099
|
+
start: start != null ? Number(start) : null,
|
|
9100
|
+
end: end != null ? Number(end) : null,
|
|
9101
|
+
size: size === "*" ? null : Number(size)
|
|
9102
|
+
};
|
|
9103
|
+
if (range2.start === null && range2.end === null && range2.size === null) {
|
|
9104
|
+
return null;
|
|
9105
|
+
}
|
|
9106
|
+
return range2;
|
|
9107
|
+
}
|
|
9108
|
+
var validateContentRangeAndDetectIfSupported = (actualRange, parsedContentRange, statusCode) => {
|
|
9109
|
+
if (statusCode === 206) {
|
|
9110
|
+
return { supportsContentRange: true };
|
|
9111
|
+
}
|
|
9112
|
+
if (typeof actualRange === "number" && parsedContentRange?.start !== actualRange) {
|
|
9113
|
+
if (actualRange === 0) {
|
|
9114
|
+
return { supportsContentRange: false };
|
|
9115
|
+
}
|
|
9116
|
+
throw new Error(`Range header (${actualRange}) does not match content-range header (${parsedContentRange?.start})`);
|
|
9117
|
+
}
|
|
9118
|
+
if (actualRange !== null && typeof actualRange !== "number" && (parsedContentRange?.start !== actualRange[0] || parsedContentRange?.end !== actualRange[1])) {
|
|
9119
|
+
throw new Error(`Range header (${actualRange}) does not match content-range header (${parsedContentRange?.start})`);
|
|
9120
|
+
}
|
|
9121
|
+
return { supportsContentRange: true };
|
|
9122
|
+
};
|
|
9123
|
+
var fetchReader = {
|
|
9124
|
+
read: async ({ src, range: range2, controller }) => {
|
|
9125
|
+
if (typeof src !== "string") {
|
|
9126
|
+
throw new Error("src must be a string when using `fetchReader`");
|
|
9127
|
+
}
|
|
9128
|
+
const resolvedUrl = resolveUrl(src);
|
|
9129
|
+
const resolvedUrlString = resolvedUrl.toString();
|
|
9130
|
+
if (!resolvedUrlString.startsWith("https://") && !resolvedUrlString.startsWith("blob:") && !resolvedUrlString.startsWith("http://")) {
|
|
9131
|
+
return Promise.reject(new Error(`${resolvedUrlString} is not a URL - needs to start with http:// or https:// or blob:. If you want to read a local file, pass \`reader: nodeReader\` to parseMedia().`));
|
|
9132
|
+
}
|
|
9133
|
+
const ownController = new AbortController;
|
|
9134
|
+
const cache = typeof navigator !== "undefined" && navigator.userAgent.includes("Cloudflare-Workers") ? undefined : "no-store";
|
|
9135
|
+
const actualRange = range2 === null ? 0 : range2;
|
|
9136
|
+
const endsWithM3u8 = (typeof resolvedUrl === "string" ? resolvedUrl : resolvedUrl.pathname).endsWith(".m3u8");
|
|
9137
|
+
const headers = actualRange === 0 && endsWithM3u8 ? {} : typeof actualRange === "number" ? {
|
|
9138
|
+
Range: `bytes=${actualRange}-`
|
|
9139
|
+
} : {
|
|
9140
|
+
Range: `bytes=${`${actualRange[0]}-${actualRange[1]}`}`
|
|
9141
|
+
};
|
|
9142
|
+
const res = await fetch(resolvedUrl, {
|
|
9143
|
+
headers,
|
|
9144
|
+
signal: ownController.signal,
|
|
9145
|
+
cache
|
|
9146
|
+
});
|
|
9147
|
+
const contentRange = res.headers.get("content-range");
|
|
9148
|
+
const parsedContentRange = contentRange ? parseContentRange(contentRange) : null;
|
|
9149
|
+
const { supportsContentRange } = validateContentRangeAndDetectIfSupported(actualRange, parsedContentRange, res.status);
|
|
9150
|
+
controller._internals.signal.addEventListener("abort", () => {
|
|
9151
|
+
ownController.abort(new MediaParserAbortError("Aborted by user"));
|
|
9152
|
+
}, { once: true });
|
|
9153
|
+
if (res.status.toString().startsWith("4") || res.status.toString().startsWith("5")) {
|
|
9154
|
+
throw new Error(`Server returned status code ${res.status} for ${src} and range ${actualRange}`);
|
|
9155
|
+
}
|
|
9156
|
+
const contentDisposition = res.headers.get("content-disposition");
|
|
9157
|
+
const name = contentDisposition?.match(/filename="([^"]+)"/)?.[1];
|
|
9158
|
+
const fallbackName = src.split("/").pop();
|
|
9159
|
+
const { contentLength, needsContentRange, reader } = await getLengthAndReader(endsWithM3u8, res, ownController);
|
|
9160
|
+
if (controller) {
|
|
9161
|
+
controller._internals.signal.addEventListener("abort", () => {
|
|
9162
|
+
reader.reader.cancel().catch(() => {
|
|
9163
|
+
});
|
|
9164
|
+
}, { once: true });
|
|
9165
|
+
}
|
|
9166
|
+
return {
|
|
9167
|
+
reader,
|
|
9168
|
+
contentLength,
|
|
9169
|
+
contentType: res.headers.get("content-type"),
|
|
9170
|
+
name: name ?? fallbackName,
|
|
9171
|
+
supportsContentRange,
|
|
9172
|
+
needsContentRange
|
|
9173
|
+
};
|
|
9174
|
+
}
|
|
9175
|
+
};
|
|
9176
|
+
|
|
9177
|
+
// src/parse-media.ts
|
|
9178
|
+
var parseMedia = (options) => {
|
|
9179
|
+
return internalParseMedia({
|
|
9180
|
+
fields: options.fields ?? null,
|
|
9181
|
+
logLevel: options.logLevel ?? "info",
|
|
9182
|
+
onAudioCodec: options.onAudioCodec ?? null,
|
|
9183
|
+
onAudioTrack: options.onAudioTrack ?? null,
|
|
9184
|
+
onContainer: options.onContainer ?? null,
|
|
9185
|
+
onDimensions: options.onDimensions ?? null,
|
|
9186
|
+
onDurationInSeconds: options.onDurationInSeconds ?? null,
|
|
9187
|
+
onFps: options.onFps ?? null,
|
|
9188
|
+
onImages: options.onImages ?? null,
|
|
9189
|
+
onInternalStats: options.onInternalStats ?? null,
|
|
9190
|
+
onIsHdr: options.onIsHdr ?? null,
|
|
9191
|
+
onKeyframes: options.onKeyframes ?? null,
|
|
9192
|
+
onLocation: options.onLocation ?? null,
|
|
9193
|
+
onMetadata: options.onMetadata ?? null,
|
|
9194
|
+
onMimeType: options.onMimeType ?? null,
|
|
9195
|
+
onName: options.onName ?? null,
|
|
9196
|
+
onNumberOfAudioChannels: options.onNumberOfAudioChannels ?? null,
|
|
9197
|
+
onParseProgress: options.onParseProgress ?? null,
|
|
9198
|
+
onRotation: options.onRotation ?? null,
|
|
9199
|
+
onSampleRate: options.onSampleRate ?? null,
|
|
9200
|
+
onSize: options.onSize ?? null,
|
|
9201
|
+
onSlowAudioBitrate: options.onSlowAudioBitrate ?? null,
|
|
9202
|
+
onSlowDurationInSeconds: options.onSlowDurationInSeconds ?? null,
|
|
9203
|
+
onSlowFps: options.onSlowFps ?? null,
|
|
9204
|
+
onSlowKeyframes: options.onSlowKeyframes ?? null,
|
|
9205
|
+
onSlowNumberOfFrames: options.onSlowNumberOfFrames ?? null,
|
|
9206
|
+
onSlowVideoBitrate: options.onSlowVideoBitrate ?? null,
|
|
9207
|
+
onStructure: options.onStructure ?? null,
|
|
9208
|
+
onM3uStreams: options.onM3uStreams ?? null,
|
|
9209
|
+
onTracks: options.onTracks ?? null,
|
|
9210
|
+
onUnrotatedDimensions: options.onUnrotatedDimensions ?? null,
|
|
9211
|
+
onVideoCodec: options.onVideoCodec ?? null,
|
|
9212
|
+
onVideoTrack: options.onVideoTrack ?? null,
|
|
9213
|
+
progressIntervalInMs: options.progressIntervalInMs ?? null,
|
|
9214
|
+
reader: options.reader ?? fetchReader,
|
|
9215
|
+
controller: options.controller ?? undefined,
|
|
9216
|
+
selectM3uStream: options.selectM3uStream ?? defaultSelectM3uStreamFn,
|
|
9217
|
+
src: options.src,
|
|
9218
|
+
mode: "query",
|
|
9219
|
+
onDiscardedData: null,
|
|
9220
|
+
onError: () => ({ action: "fail" }),
|
|
9221
|
+
acknowledgeRemotionLicense: Boolean(options.acknowledgeRemotionLicense),
|
|
9222
|
+
apiName: "parseMedia()"
|
|
9223
|
+
});
|
|
9224
|
+
};
|
|
9225
|
+
|
|
9226
|
+
// src/containers/m3u/get-chunks.ts
|
|
9227
|
+
var getChunks = (playlist) => {
|
|
9228
|
+
const chunks = [];
|
|
9229
|
+
for (let i = 0;i < playlist.boxes.length; i++) {
|
|
9230
|
+
const box = playlist.boxes[i];
|
|
9231
|
+
if (box.type === "m3u-extinf") {
|
|
9232
|
+
const nextBox = playlist.boxes[i + 1];
|
|
9233
|
+
i++;
|
|
9234
|
+
if (nextBox.type !== "m3u-text-value") {
|
|
9235
|
+
throw new Error("Expected m3u-text-value");
|
|
9236
|
+
}
|
|
9237
|
+
chunks.push({ duration: box.value, url: nextBox.value });
|
|
9238
|
+
}
|
|
9239
|
+
continue;
|
|
9240
|
+
}
|
|
9241
|
+
return chunks;
|
|
9242
|
+
};
|
|
9243
|
+
|
|
9244
|
+
// src/containers/m3u/return-packets.ts
|
|
9245
|
+
var iteratorOverTsFiles = async ({
|
|
9246
|
+
structure,
|
|
9247
|
+
onVideoTrack,
|
|
9248
|
+
m3uState: m3uState2,
|
|
9249
|
+
onAudioTrack,
|
|
9250
|
+
onDoneWithTracks,
|
|
9251
|
+
playlistUrl,
|
|
9252
|
+
logLevel,
|
|
9253
|
+
parentController
|
|
9254
|
+
}) => {
|
|
9255
|
+
const playlist = getPlaylist(structure);
|
|
9256
|
+
const chunks = getChunks(playlist);
|
|
9257
|
+
const lastChunkProcessed = m3uState2.getLastChunkProcessed();
|
|
9258
|
+
const chunkIndex = lastChunkProcessed + 1;
|
|
9259
|
+
const chunk = chunks[chunkIndex];
|
|
9260
|
+
const isLastChunk = chunkIndex === chunks.length - 1;
|
|
9261
|
+
const src = new URL(chunk.url, playlistUrl).toString();
|
|
9262
|
+
const childController = mediaParserController();
|
|
9263
|
+
const forwarded = forwardMediaParserController({
|
|
9264
|
+
childController,
|
|
9265
|
+
parentController
|
|
9266
|
+
});
|
|
9267
|
+
await parseMedia({
|
|
9268
|
+
src,
|
|
9269
|
+
acknowledgeRemotionLicense: true,
|
|
9270
|
+
logLevel,
|
|
9271
|
+
controller: childController,
|
|
9272
|
+
onTracks: () => {
|
|
9273
|
+
if (!m3uState2.hasEmittedDoneWithTracks()) {
|
|
9274
|
+
m3uState2.setHasEmittedDoneWithTracks();
|
|
9275
|
+
onDoneWithTracks();
|
|
9276
|
+
return null;
|
|
9277
|
+
}
|
|
9278
|
+
},
|
|
9279
|
+
onAudioTrack: async ({ track }) => {
|
|
9280
|
+
const callbackOrFalse = m3uState2.hasEmittedAudioTrack();
|
|
9281
|
+
if (callbackOrFalse === false) {
|
|
9282
|
+
const callback = await onAudioTrack(track);
|
|
9283
|
+
if (!callback) {
|
|
9284
|
+
m3uState2.setHasEmittedAudioTrack(null);
|
|
9285
|
+
return null;
|
|
9286
|
+
}
|
|
9287
|
+
m3uState2.setHasEmittedAudioTrack(callback);
|
|
9288
|
+
return (sample) => {
|
|
9289
|
+
return callback(sample);
|
|
9290
|
+
};
|
|
9291
|
+
}
|
|
9292
|
+
return callbackOrFalse;
|
|
9293
|
+
},
|
|
9294
|
+
onVideoTrack: async ({ track }) => {
|
|
9295
|
+
const callbackOrFalse = m3uState2.hasEmittedVideoTrack();
|
|
9296
|
+
if (callbackOrFalse === false) {
|
|
9297
|
+
const callback = await onVideoTrack(track);
|
|
9298
|
+
if (!callback) {
|
|
9299
|
+
m3uState2.setHasEmittedVideoTrack(null);
|
|
9300
|
+
return null;
|
|
9301
|
+
}
|
|
9302
|
+
m3uState2.setHasEmittedVideoTrack(callback);
|
|
9303
|
+
return (sample) => {
|
|
9304
|
+
return callback(sample);
|
|
9305
|
+
};
|
|
9306
|
+
}
|
|
9307
|
+
return callbackOrFalse;
|
|
9308
|
+
}
|
|
9309
|
+
});
|
|
9310
|
+
m3uState2.setLastChunkProcessed(chunkIndex);
|
|
9311
|
+
if (isLastChunk) {
|
|
9312
|
+
m3uState2.setAllChunksProcessed();
|
|
9313
|
+
}
|
|
9314
|
+
forwarded.cleanup();
|
|
9315
|
+
};
|
|
9316
|
+
|
|
9317
|
+
// src/containers/m3u/after-manifest-fetch.ts
|
|
9318
|
+
var afterManifestFetch = async ({
|
|
9319
|
+
structure,
|
|
9320
|
+
m3uState: m3uState2,
|
|
9321
|
+
src,
|
|
9322
|
+
selectM3uStreamFn
|
|
9323
|
+
}) => {
|
|
9324
|
+
const independentSegments = isIndependentSegments(structure);
|
|
9325
|
+
if (!independentSegments) {
|
|
9326
|
+
if (!src) {
|
|
9327
|
+
throw new Error("No src");
|
|
9328
|
+
}
|
|
9329
|
+
m3uState2.setSelectedStream({
|
|
9330
|
+
type: "initial-url",
|
|
9331
|
+
url: src
|
|
8557
9332
|
});
|
|
8558
|
-
|
|
8559
|
-
state.callbacks.tracks.setIsDone(state.logLevel);
|
|
8560
|
-
state.getIsoStructure().boxes.push(moov);
|
|
8561
|
-
return parseMdatSection(state);
|
|
9333
|
+
return m3uState2.setReadyToIterateOverM3u();
|
|
8562
9334
|
}
|
|
8563
|
-
|
|
8564
|
-
|
|
9335
|
+
const streams = getM3uStreams(structure, src);
|
|
9336
|
+
if (streams === null) {
|
|
9337
|
+
throw new Error("No streams found");
|
|
8565
9338
|
}
|
|
8566
|
-
const
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
});
|
|
8571
|
-
if (!samplesWithIndex) {
|
|
8572
|
-
const nextSample_ = flatSamples.filter((s) => s.samplePosition.offset > iterator.counter.getOffset()).sort((a, b) => a.samplePosition.offset - b.samplePosition.offset)[0];
|
|
8573
|
-
if (nextSample_) {
|
|
8574
|
-
iterator.discard(nextSample_.samplePosition.offset - iterator.counter.getOffset());
|
|
8575
|
-
return null;
|
|
8576
|
-
}
|
|
8577
|
-
return makeSkip(endOfMdat);
|
|
9339
|
+
const selectedStream = await selectStream({ streams, fn: selectM3uStreamFn });
|
|
9340
|
+
m3uState2.setSelectedStream({ type: "selected-stream", stream: selectedStream });
|
|
9341
|
+
if (!selectedStream.resolution) {
|
|
9342
|
+
throw new Error("Stream does not have a resolution");
|
|
8578
9343
|
}
|
|
8579
|
-
|
|
8580
|
-
|
|
9344
|
+
const boxes = await fetchM3u8Stream(selectedStream);
|
|
9345
|
+
structure.boxes.push({ type: "m3u-playlist", boxes });
|
|
9346
|
+
m3uState2.setReadyToIterateOverM3u();
|
|
9347
|
+
};
|
|
9348
|
+
var runOverM3u = async ({
|
|
9349
|
+
state,
|
|
9350
|
+
structure
|
|
9351
|
+
}) => {
|
|
9352
|
+
const selectedStream = state.m3u.getSelectedStream();
|
|
9353
|
+
if (!selectedStream) {
|
|
9354
|
+
throw new Error("No stream selected");
|
|
8581
9355
|
}
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
},
|
|
9356
|
+
await iteratorOverTsFiles({
|
|
9357
|
+
playlistUrl: selectedStream.type === "initial-url" ? selectedStream.url : selectedStream.stream.url,
|
|
9358
|
+
structure,
|
|
9359
|
+
logLevel: state.logLevel,
|
|
9360
|
+
onDoneWithTracks() {
|
|
9361
|
+
state.callbacks.tracks.setIsDone(state.logLevel);
|
|
9362
|
+
},
|
|
9363
|
+
onAudioTrack: (track) => {
|
|
9364
|
+
return registerAudioTrack({
|
|
9365
|
+
container: "m3u8",
|
|
9366
|
+
state,
|
|
9367
|
+
track
|
|
9368
|
+
});
|
|
9369
|
+
},
|
|
9370
|
+
onVideoTrack: (track) => {
|
|
9371
|
+
return registerVideoTrack({
|
|
9372
|
+
container: "m3u8",
|
|
9373
|
+
state,
|
|
9374
|
+
track
|
|
9375
|
+
});
|
|
9376
|
+
},
|
|
9377
|
+
m3uState: state.m3u,
|
|
9378
|
+
parentController: state.controller
|
|
9379
|
+
});
|
|
9380
|
+
};
|
|
9381
|
+
|
|
9382
|
+
// src/containers/m3u/parse-m3u-manifest.ts
|
|
9383
|
+
var parseM3uManifest = ({
|
|
9384
|
+
iterator,
|
|
9385
|
+
structure,
|
|
9386
|
+
contentLength
|
|
9387
|
+
}) => {
|
|
9388
|
+
const start = iterator.startCheckpoint();
|
|
9389
|
+
const line = iterator.readUntilLineEnd();
|
|
9390
|
+
if (iterator.counter.getOffset() > contentLength) {
|
|
9391
|
+
throw new Error("Unexpected end of file");
|
|
8596
9392
|
}
|
|
8597
|
-
if (
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
if (nalUnitType === 6) {
|
|
8601
|
-
const seiType = bytes[5];
|
|
8602
|
-
isRecoveryPoint = seiType === 6;
|
|
8603
|
-
}
|
|
8604
|
-
await state.callbacks.onVideoSample(samplesWithIndex.track.trackId, convertAudioOrVideoSampleToWebCodecsTimestamps({
|
|
8605
|
-
data: bytes,
|
|
8606
|
-
timestamp: cts,
|
|
8607
|
-
duration: duration2,
|
|
8608
|
-
cts,
|
|
8609
|
-
dts,
|
|
8610
|
-
trackId: samplesWithIndex.track.trackId,
|
|
8611
|
-
type: isKeyframe && !isRecoveryPoint ? "key" : "delta",
|
|
8612
|
-
offset,
|
|
8613
|
-
timescale: samplesWithIndex.track.timescale
|
|
8614
|
-
}, samplesWithIndex.track.timescale));
|
|
9393
|
+
if (line === null) {
|
|
9394
|
+
start.returnToCheckpoint();
|
|
9395
|
+
return Promise.resolve(null);
|
|
8615
9396
|
}
|
|
8616
|
-
|
|
9397
|
+
parseM3u8Text(line, structure.boxes);
|
|
9398
|
+
return Promise.resolve(null);
|
|
8617
9399
|
};
|
|
8618
9400
|
|
|
8619
|
-
// src/containers/
|
|
8620
|
-
var
|
|
8621
|
-
const
|
|
8622
|
-
if (
|
|
8623
|
-
|
|
8624
|
-
|
|
9401
|
+
// src/containers/m3u/parse-m3u.ts
|
|
9402
|
+
var parseM3u = async ({ state }) => {
|
|
9403
|
+
const structure = state.getM3uStructure();
|
|
9404
|
+
if (state.m3u.isReadyToIterateOverM3u()) {
|
|
9405
|
+
await runOverM3u({
|
|
9406
|
+
state,
|
|
9407
|
+
structure
|
|
9408
|
+
});
|
|
9409
|
+
return null;
|
|
8625
9410
|
}
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
9411
|
+
if (state.m3u.hasFinishedManifest()) {
|
|
9412
|
+
await afterManifestFetch({
|
|
9413
|
+
structure,
|
|
9414
|
+
m3uState: state.m3u,
|
|
9415
|
+
src: typeof state.src === "string" ? state.src : null,
|
|
9416
|
+
selectM3uStreamFn: state.selectM3uStreamFn
|
|
9417
|
+
});
|
|
9418
|
+
return null;
|
|
8629
9419
|
}
|
|
8630
|
-
|
|
9420
|
+
const box = await parseM3uManifest({
|
|
9421
|
+
iterator: state.iterator,
|
|
9422
|
+
structure,
|
|
9423
|
+
contentLength: state.contentLength
|
|
9424
|
+
});
|
|
9425
|
+
const isDoneNow = state.iterator.counter.getOffset() === state.contentLength;
|
|
9426
|
+
if (isDoneNow) {
|
|
9427
|
+
state.m3u.setHasFinishedManifest();
|
|
9428
|
+
}
|
|
9429
|
+
return box;
|
|
8631
9430
|
};
|
|
8632
9431
|
|
|
8633
9432
|
// src/containers/mp3/id3.ts
|
|
@@ -8926,7 +9725,7 @@ var parseMpegHeader = async ({
|
|
|
8926
9725
|
bitrateKbit,
|
|
8927
9726
|
startOfMpegStream: initialOffset
|
|
8928
9727
|
});
|
|
8929
|
-
await
|
|
9728
|
+
await registerAudioTrack({
|
|
8930
9729
|
container: "mp3",
|
|
8931
9730
|
state,
|
|
8932
9731
|
track: {
|
|
@@ -9274,7 +10073,7 @@ var expectRiffBox = async (state) => {
|
|
|
9274
10073
|
index: state.riff.getNextTrackIndex(),
|
|
9275
10074
|
strf: box.strf
|
|
9276
10075
|
});
|
|
9277
|
-
await
|
|
10076
|
+
await registerAudioTrack({
|
|
9278
10077
|
state,
|
|
9279
10078
|
track: audioTrack,
|
|
9280
10079
|
container: "avi"
|
|
@@ -9381,26 +10180,25 @@ var readSps = (iterator) => {
|
|
|
9381
10180
|
let frame_crop_top_offset = null;
|
|
9382
10181
|
let frame_crop_bottom_offset = null;
|
|
9383
10182
|
let vui_parameters = null;
|
|
9384
|
-
if (
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
|
|
9399
|
-
|
|
9400
|
-
|
|
9401
|
-
|
|
9402
|
-
|
|
9403
|
-
throw new Error("Not implemented");
|
|
10183
|
+
if (profile === 100 || profile === 110 || profile === 122 || profile === 244 || profile === 44 || profile === 83 || profile === 86 || profile === 118 || profile === 128 || profile === 138 || profile === 139 || profile === 134 || profile === 135) {
|
|
10184
|
+
const chromaFormat = iterator.readExpGolomb();
|
|
10185
|
+
if (chromaFormat === 3) {
|
|
10186
|
+
separate_colour_plane_flag = iterator.getBits(1);
|
|
10187
|
+
}
|
|
10188
|
+
bit_depth_luma_minus8 = iterator.readExpGolomb();
|
|
10189
|
+
bit_depth_chroma_minus8 = iterator.readExpGolomb();
|
|
10190
|
+
qpprime_y_zero_transform_bypass_flag = iterator.getBits(1);
|
|
10191
|
+
const seq_scaling_matrix_present_flag = iterator.getBits(1);
|
|
10192
|
+
const seq_scaling_list_present_flag = [];
|
|
10193
|
+
if (seq_scaling_matrix_present_flag) {
|
|
10194
|
+
for (let i = 0;i < (chromaFormat !== 3 ? 8 : 12); i++) {
|
|
10195
|
+
seq_scaling_list_present_flag[i] = iterator.getBits(1);
|
|
10196
|
+
if (seq_scaling_list_present_flag[i]) {
|
|
10197
|
+
if (i < 6) {
|
|
10198
|
+
throw new Error("Not implemented");
|
|
10199
|
+
} else {
|
|
10200
|
+
throw new Error("Not implemented");
|
|
10201
|
+
}
|
|
9404
10202
|
}
|
|
9405
10203
|
}
|
|
9406
10204
|
}
|
|
@@ -9742,6 +10540,20 @@ var parsePat = (iterator) => {
|
|
|
9742
10540
|
discardRestOfPacket(iterator);
|
|
9743
10541
|
return tables;
|
|
9744
10542
|
};
|
|
10543
|
+
var parseSdt = (iterator) => {
|
|
10544
|
+
iterator.startReadingBits();
|
|
10545
|
+
iterator.getBits(8);
|
|
10546
|
+
iterator.getBits(1);
|
|
10547
|
+
iterator.getBits(1);
|
|
10548
|
+
iterator.getBits(2);
|
|
10549
|
+
const sectionLength = iterator.getBits(12);
|
|
10550
|
+
iterator.stopReadingBits();
|
|
10551
|
+
iterator.discard(sectionLength);
|
|
10552
|
+
discardRestOfPacket(iterator);
|
|
10553
|
+
return {
|
|
10554
|
+
type: "transport-stream-sdt-box"
|
|
10555
|
+
};
|
|
10556
|
+
};
|
|
9745
10557
|
|
|
9746
10558
|
// src/containers/transport-stream/parse-pes.ts
|
|
9747
10559
|
var parsePes = (iterator) => {
|
|
@@ -9935,9 +10747,10 @@ var readAdtsHeader = (buffer) => {
|
|
|
9935
10747
|
};
|
|
9936
10748
|
|
|
9937
10749
|
// src/containers/transport-stream/find-separator.ts
|
|
9938
|
-
function
|
|
10750
|
+
function findNthSubarrayIndex(array, subarray, n) {
|
|
9939
10751
|
const subarrayLength = subarray.length;
|
|
9940
10752
|
const arrayLength = array.length;
|
|
10753
|
+
let count = 0;
|
|
9941
10754
|
for (let i = 0;i <= arrayLength - subarrayLength; i++) {
|
|
9942
10755
|
let match = true;
|
|
9943
10756
|
for (let j = 0;j < subarrayLength; j++) {
|
|
@@ -9947,28 +10760,22 @@ function findSubarrayIndex(array, subarray) {
|
|
|
9947
10760
|
}
|
|
9948
10761
|
}
|
|
9949
10762
|
if (match) {
|
|
9950
|
-
|
|
9951
|
-
|
|
10763
|
+
count++;
|
|
10764
|
+
if (count === n) {
|
|
10765
|
+
return i;
|
|
9952
10766
|
}
|
|
9953
|
-
return i;
|
|
9954
10767
|
}
|
|
9955
10768
|
}
|
|
9956
10769
|
return -1;
|
|
9957
10770
|
}
|
|
9958
|
-
var findNextSeparator = (restOfPacket, transportStreamEntry) => {
|
|
9959
|
-
if (transportStreamEntry.streamType === 27) {
|
|
9960
|
-
return findSubarrayIndex(restOfPacket, new Uint8Array([0, 0, 1, 9]));
|
|
9961
|
-
}
|
|
9962
|
-
throw new Error(`Unsupported stream ID ${transportStreamEntry.streamType}`);
|
|
9963
|
-
};
|
|
9964
10771
|
|
|
9965
10772
|
// src/containers/avc/interpret-sps.ts
|
|
9966
10773
|
var getDimensionsFromSps = (sps) => {
|
|
9967
10774
|
const height = sps.pic_height_in_map_units_minus1;
|
|
9968
10775
|
const width = sps.pic_width_in_mbs_minus1;
|
|
9969
10776
|
return {
|
|
9970
|
-
height: (height + 1) * 16,
|
|
9971
|
-
width: (width + 1) * 16
|
|
10777
|
+
height: (height + 1) * 16 - (sps.frame_crop_bottom_offset ?? 0) * 2 - (sps.frame_crop_top_offset ?? 0) * 2,
|
|
10778
|
+
width: (width + 1) * 16 - (sps.frame_crop_right_offset ?? 0) * 2 - (sps.frame_crop_left_offset ?? 0) * 2
|
|
9972
10779
|
};
|
|
9973
10780
|
};
|
|
9974
10781
|
var getSampleAspectRatioFromSps = (sps) => {
|
|
@@ -10044,7 +10851,7 @@ var handleAvcPacket = async ({
|
|
|
10044
10851
|
},
|
|
10045
10852
|
color: getVideoColorFromSps(spsAndPps.sps.spsData)
|
|
10046
10853
|
};
|
|
10047
|
-
await
|
|
10854
|
+
await registerVideoTrack({ track, state, container: "transport-stream" });
|
|
10048
10855
|
}
|
|
10049
10856
|
const sample = {
|
|
10050
10857
|
cts: streamBuffer.pesHeader.pts,
|
|
@@ -10088,7 +10895,7 @@ var handleAacPacket = async ({
|
|
|
10088
10895
|
numberOfChannels: channelConfiguration,
|
|
10089
10896
|
sampleRate
|
|
10090
10897
|
};
|
|
10091
|
-
await
|
|
10898
|
+
await registerAudioTrack({
|
|
10092
10899
|
track,
|
|
10093
10900
|
state,
|
|
10094
10901
|
container: "transport-stream"
|
|
@@ -10161,36 +10968,35 @@ var processFinalStreamBuffers = async ({
|
|
|
10161
10968
|
|
|
10162
10969
|
// src/containers/transport-stream/parse-stream-packet.ts
|
|
10163
10970
|
var parseAdtsStream = async ({
|
|
10164
|
-
restOfPacket,
|
|
10165
10971
|
transportStreamEntry,
|
|
10166
10972
|
state,
|
|
10167
10973
|
structure,
|
|
10168
10974
|
offset
|
|
10169
10975
|
}) => {
|
|
10170
10976
|
const { streamBuffers, nextPesHeaderStore: nextPesHeader } = state.transportStream;
|
|
10171
|
-
|
|
10172
|
-
|
|
10173
|
-
|
|
10174
|
-
buffer
|
|
10175
|
-
|
|
10176
|
-
|
|
10177
|
-
|
|
10178
|
-
|
|
10179
|
-
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
streamBuffer.buffer,
|
|
10184
|
-
restOfPacket.slice(0, bytesToTake)
|
|
10185
|
-
]);
|
|
10186
|
-
if (expectedLength === streamBuffer.buffer.byteLength) {
|
|
10977
|
+
while (true) {
|
|
10978
|
+
const streamBuffer = streamBuffers.get(transportStreamEntry.pid);
|
|
10979
|
+
if (!streamBuffer) {
|
|
10980
|
+
throw new Error("Stream buffer not found");
|
|
10981
|
+
}
|
|
10982
|
+
const expectedLength = readAdtsHeader(streamBuffer.buffer)?.frameLength ?? null;
|
|
10983
|
+
if (expectedLength === null) {
|
|
10984
|
+
break;
|
|
10985
|
+
}
|
|
10986
|
+
if (expectedLength > streamBuffer.buffer.length) {
|
|
10987
|
+
break;
|
|
10988
|
+
}
|
|
10187
10989
|
await processStreamBuffer({
|
|
10188
|
-
streamBuffer
|
|
10990
|
+
streamBuffer: {
|
|
10991
|
+
buffer: streamBuffer.buffer.slice(0, expectedLength),
|
|
10992
|
+
offset,
|
|
10993
|
+
pesHeader: streamBuffer.pesHeader
|
|
10994
|
+
},
|
|
10189
10995
|
programId: transportStreamEntry.pid,
|
|
10190
10996
|
state,
|
|
10191
10997
|
structure
|
|
10192
10998
|
});
|
|
10193
|
-
const rest =
|
|
10999
|
+
const rest = streamBuffer.buffer.slice(expectedLength);
|
|
10194
11000
|
streamBuffers.set(transportStreamEntry.pid, {
|
|
10195
11001
|
buffer: rest,
|
|
10196
11002
|
pesHeader: nextPesHeader.getNextPesHeader(),
|
|
@@ -10199,82 +11005,91 @@ var parseAdtsStream = async ({
|
|
|
10199
11005
|
}
|
|
10200
11006
|
};
|
|
10201
11007
|
var parseAvcStream = async ({
|
|
10202
|
-
restOfPacket,
|
|
10203
|
-
transportStreamEntry,
|
|
10204
11008
|
programId,
|
|
10205
11009
|
state,
|
|
10206
11010
|
structure,
|
|
10207
|
-
|
|
11011
|
+
streamBuffer
|
|
10208
11012
|
}) => {
|
|
10209
|
-
const indexOfSeparator =
|
|
10210
|
-
|
|
10211
|
-
|
|
10212
|
-
if (indexOfSeparator === -1) {
|
|
10213
|
-
if (streamBuffer) {
|
|
10214
|
-
streamBuffer.buffer = combineUint8Arrays([
|
|
10215
|
-
streamBuffer.buffer,
|
|
10216
|
-
restOfPacket
|
|
10217
|
-
]);
|
|
10218
|
-
return;
|
|
10219
|
-
}
|
|
10220
|
-
streamBuffers.set(programId, {
|
|
10221
|
-
pesHeader: nextPesHeader.getNextPesHeader(),
|
|
10222
|
-
buffer: restOfPacket,
|
|
10223
|
-
offset
|
|
10224
|
-
});
|
|
10225
|
-
return;
|
|
10226
|
-
}
|
|
10227
|
-
if (streamBuffer) {
|
|
10228
|
-
const packet = restOfPacket.slice(0, indexOfSeparator);
|
|
10229
|
-
streamBuffer.buffer = combineUint8Arrays([streamBuffer.buffer, packet]);
|
|
10230
|
-
await processStreamBuffer({
|
|
10231
|
-
state,
|
|
10232
|
-
streamBuffer,
|
|
10233
|
-
programId,
|
|
10234
|
-
structure
|
|
10235
|
-
});
|
|
10236
|
-
const rest = restOfPacket.slice(indexOfSeparator);
|
|
10237
|
-
streamBuffers.set(programId, {
|
|
10238
|
-
pesHeader: nextPesHeader.getNextPesHeader(),
|
|
10239
|
-
buffer: rest,
|
|
10240
|
-
offset
|
|
10241
|
-
});
|
|
10242
|
-
return;
|
|
10243
|
-
}
|
|
10244
|
-
if (indexOfSeparator !== 0) {
|
|
10245
|
-
throw new Error("No stream buffer found but new separator is not at the beginning");
|
|
11013
|
+
const indexOfSeparator = findNthSubarrayIndex(streamBuffer.buffer, new Uint8Array([0, 0, 1, 9]), 2);
|
|
11014
|
+
if (indexOfSeparator === -1 || indexOfSeparator === 0) {
|
|
11015
|
+
return null;
|
|
10246
11016
|
}
|
|
10247
|
-
|
|
10248
|
-
|
|
10249
|
-
|
|
10250
|
-
|
|
11017
|
+
const packet = streamBuffer.buffer.slice(0, indexOfSeparator);
|
|
11018
|
+
const rest = streamBuffer.buffer.slice(indexOfSeparator);
|
|
11019
|
+
await processStreamBuffer({
|
|
11020
|
+
state,
|
|
11021
|
+
streamBuffer: {
|
|
11022
|
+
offset: streamBuffer.offset,
|
|
11023
|
+
pesHeader: streamBuffer.pesHeader,
|
|
11024
|
+
buffer: packet
|
|
11025
|
+
},
|
|
11026
|
+
programId,
|
|
11027
|
+
structure
|
|
10251
11028
|
});
|
|
11029
|
+
return rest;
|
|
10252
11030
|
};
|
|
10253
|
-
var parseStream = ({
|
|
11031
|
+
var parseStream = async ({
|
|
10254
11032
|
transportStreamEntry,
|
|
10255
11033
|
state,
|
|
10256
11034
|
programId,
|
|
10257
11035
|
structure
|
|
10258
11036
|
}) => {
|
|
10259
11037
|
const { iterator } = state;
|
|
10260
|
-
|
|
11038
|
+
let restOfPacket = getRestOfPacket(iterator);
|
|
11039
|
+
const offset = iterator.counter.getOffset();
|
|
10261
11040
|
if (transportStreamEntry.streamType === 27) {
|
|
10262
|
-
|
|
10263
|
-
|
|
10264
|
-
transportStreamEntry
|
|
10265
|
-
|
|
10266
|
-
|
|
10267
|
-
|
|
10268
|
-
|
|
10269
|
-
|
|
11041
|
+
const { streamBuffers, nextPesHeaderStore: nextPesHeader } = state.transportStream;
|
|
11042
|
+
while (true) {
|
|
11043
|
+
if (!streamBuffers.has(transportStreamEntry.pid)) {
|
|
11044
|
+
streamBuffers.set(programId, {
|
|
11045
|
+
pesHeader: nextPesHeader.getNextPesHeader(),
|
|
11046
|
+
buffer: new Uint8Array([]),
|
|
11047
|
+
offset
|
|
11048
|
+
});
|
|
11049
|
+
}
|
|
11050
|
+
const streamBuffer = streamBuffers.get(transportStreamEntry.pid);
|
|
11051
|
+
streamBuffer.buffer = combineUint8Arrays([
|
|
11052
|
+
streamBuffer.buffer,
|
|
11053
|
+
restOfPacket
|
|
11054
|
+
]);
|
|
11055
|
+
const rest = await parseAvcStream({
|
|
11056
|
+
state,
|
|
11057
|
+
programId,
|
|
11058
|
+
structure,
|
|
11059
|
+
streamBuffer: streamBuffers.get(transportStreamEntry.pid)
|
|
11060
|
+
});
|
|
11061
|
+
if (rest !== null) {
|
|
11062
|
+
streamBuffers.delete(transportStreamEntry.pid);
|
|
11063
|
+
if (rest.length === 0) {
|
|
11064
|
+
break;
|
|
11065
|
+
}
|
|
11066
|
+
restOfPacket = rest;
|
|
11067
|
+
} else {
|
|
11068
|
+
break;
|
|
11069
|
+
}
|
|
11070
|
+
}
|
|
11071
|
+
return;
|
|
10270
11072
|
}
|
|
10271
11073
|
if (transportStreamEntry.streamType === 15) {
|
|
11074
|
+
const { streamBuffers, nextPesHeaderStore: nextPesHeader } = state.transportStream;
|
|
11075
|
+
const streamBuffer = streamBuffers.get(transportStreamEntry.pid);
|
|
11076
|
+
if (!streamBuffer) {
|
|
11077
|
+
streamBuffers.set(transportStreamEntry.pid, {
|
|
11078
|
+
buffer: restOfPacket,
|
|
11079
|
+
pesHeader: nextPesHeader.getNextPesHeader(),
|
|
11080
|
+
offset
|
|
11081
|
+
});
|
|
11082
|
+
} else {
|
|
11083
|
+
streamBuffer.buffer = combineUint8Arrays([
|
|
11084
|
+
streamBuffer.buffer,
|
|
11085
|
+
restOfPacket
|
|
11086
|
+
]);
|
|
11087
|
+
}
|
|
10272
11088
|
return parseAdtsStream({
|
|
10273
|
-
restOfPacket,
|
|
10274
11089
|
transportStreamEntry,
|
|
10275
11090
|
state,
|
|
10276
11091
|
structure,
|
|
10277
|
-
offset
|
|
11092
|
+
offset
|
|
10278
11093
|
});
|
|
10279
11094
|
}
|
|
10280
11095
|
throw new Error(`Unsupported stream type ${transportStreamEntry.streamType}`);
|
|
@@ -10335,7 +11150,10 @@ var parsePacket = async ({
|
|
|
10335
11150
|
if (programId === 0) {
|
|
10336
11151
|
return Promise.resolve(parsePat(iterator));
|
|
10337
11152
|
}
|
|
10338
|
-
|
|
11153
|
+
if (programId === 17) {
|
|
11154
|
+
return Promise.resolve(parseSdt(iterator));
|
|
11155
|
+
}
|
|
11156
|
+
const program = programId === 17 ? null : getProgramForId(structure, programId);
|
|
10339
11157
|
if (program) {
|
|
10340
11158
|
const pmt = parsePmt(iterator);
|
|
10341
11159
|
return Promise.resolve(pmt);
|
|
@@ -10426,7 +11244,7 @@ var parseFmt = async ({
|
|
|
10426
11244
|
type: "wav-fmt"
|
|
10427
11245
|
};
|
|
10428
11246
|
state.getWavStructure().boxes.push(wavHeader);
|
|
10429
|
-
await
|
|
11247
|
+
await registerAudioTrack({
|
|
10430
11248
|
state,
|
|
10431
11249
|
track: {
|
|
10432
11250
|
type: "audio",
|
|
@@ -10764,6 +11582,14 @@ var initVideo = ({
|
|
|
10764
11582
|
});
|
|
10765
11583
|
return;
|
|
10766
11584
|
}
|
|
11585
|
+
if (fileType.type === "m3u") {
|
|
11586
|
+
Log.verbose(state.logLevel, "Detected M3U");
|
|
11587
|
+
state.setStructure({
|
|
11588
|
+
type: "m3u",
|
|
11589
|
+
boxes: []
|
|
11590
|
+
});
|
|
11591
|
+
return;
|
|
11592
|
+
}
|
|
10767
11593
|
if (fileType.type === "gif") {
|
|
10768
11594
|
return Promise.reject(new IsAGifError({
|
|
10769
11595
|
message: "GIF files are not yet supported",
|
|
@@ -10808,10 +11634,13 @@ var runParseIteration = async ({
|
|
|
10808
11634
|
contentLength,
|
|
10809
11635
|
name
|
|
10810
11636
|
}) => {
|
|
11637
|
+
const structure = state.getStructureOrNull();
|
|
11638
|
+
if (structure && structure.type === "m3u") {
|
|
11639
|
+
return parseM3u({ state });
|
|
11640
|
+
}
|
|
10811
11641
|
if (state.iterator.bytesRemaining() === 0) {
|
|
10812
11642
|
return Promise.reject(new Error("no bytes"));
|
|
10813
11643
|
}
|
|
10814
|
-
const structure = state.getStructureOrNull();
|
|
10815
11644
|
if (structure === null) {
|
|
10816
11645
|
await initVideo({ state, mimeType, name, contentLength });
|
|
10817
11646
|
return null;
|
|
@@ -10906,6 +11735,7 @@ var internalParseMedia = async function({
|
|
|
10906
11735
|
onError,
|
|
10907
11736
|
acknowledgeRemotionLicense,
|
|
10908
11737
|
apiName,
|
|
11738
|
+
selectM3uStream: selectM3uStreamFn,
|
|
10909
11739
|
...more
|
|
10910
11740
|
}) {
|
|
10911
11741
|
warnIfRemotionLicenseNotAcknowledged({
|
|
@@ -10918,17 +11748,19 @@ var internalParseMedia = async function({
|
|
|
10918
11748
|
fields: fieldsInReturnValue,
|
|
10919
11749
|
callbacks: more
|
|
10920
11750
|
});
|
|
11751
|
+
Log.verbose(logLevel, `Reading ${typeof src === "string" ? src : src.name}`);
|
|
10921
11752
|
const {
|
|
10922
11753
|
reader: readerInstance,
|
|
10923
11754
|
contentLength,
|
|
10924
11755
|
name,
|
|
10925
11756
|
contentType,
|
|
10926
|
-
supportsContentRange
|
|
11757
|
+
supportsContentRange,
|
|
11758
|
+
needsContentRange
|
|
10927
11759
|
} = await readerInterface.read({ src, range: null, controller });
|
|
10928
11760
|
if (contentLength === null) {
|
|
10929
11761
|
throw new Error('Cannot read media without a content length. This is currently not supported. Ensure the media has a "Content-Length" HTTP header.');
|
|
10930
11762
|
}
|
|
10931
|
-
if (!supportsContentRange) {
|
|
11763
|
+
if (!supportsContentRange && needsContentRange) {
|
|
10932
11764
|
throw new Error('Cannot read media without it supporting the "Content-Range" header. This is currently not supported. Ensure the media supports the "Content-Range" HTTP header.');
|
|
10933
11765
|
}
|
|
10934
11766
|
const hasAudioTrackHandlers = Boolean(onAudioTrack);
|
|
@@ -10954,7 +11786,8 @@ var internalParseMedia = async function({
|
|
|
10954
11786
|
mode,
|
|
10955
11787
|
readerInterface,
|
|
10956
11788
|
src,
|
|
10957
|
-
onDiscardedData
|
|
11789
|
+
onDiscardedData,
|
|
11790
|
+
selectM3uStreamFn
|
|
10958
11791
|
});
|
|
10959
11792
|
const { iterator } = state;
|
|
10960
11793
|
let currentReader = readerInstance;
|
|
@@ -10994,6 +11827,9 @@ var internalParseMedia = async function({
|
|
|
10994
11827
|
return true;
|
|
10995
11828
|
}
|
|
10996
11829
|
if (state.iterator.counter.getOffset() === contentLength) {
|
|
11830
|
+
if (state.getStructure().type === "m3u" && !state.m3u.getAllChunksProcessed()) {
|
|
11831
|
+
return false;
|
|
11832
|
+
}
|
|
10997
11833
|
Log.verbose(logLevel, "Reached end of file");
|
|
10998
11834
|
await state.discardReadBytes(true);
|
|
10999
11835
|
return true;
|
|
@@ -11037,7 +11873,7 @@ var internalParseMedia = async function({
|
|
|
11037
11873
|
}));
|
|
11038
11874
|
if (!errored) {
|
|
11039
11875
|
Log.trace(logLevel, `Continuing parsing of file, currently at position ${iterator.counter.getOffset()}/${contentLength} (0x${iterator.counter.getOffset().toString(16)})`);
|
|
11040
|
-
if (iterationWithThisOffset > 300) {
|
|
11876
|
+
if (iterationWithThisOffset > 300 && state.getStructure().type !== "m3u") {
|
|
11041
11877
|
throw new Error("Infinite loop detected. The parser is not progressing. This is likely a bug in the parser. You can report this at https://remotion.dev/report and we will fix it as soon as possible.");
|
|
11042
11878
|
}
|
|
11043
11879
|
try {
|
|
@@ -11135,6 +11971,7 @@ var downloadAndParseMedia = async (options) => {
|
|
|
11135
11971
|
onAudioTrack: null,
|
|
11136
11972
|
onContainer: options.onContainer ?? null,
|
|
11137
11973
|
onDimensions: options.onDimensions ?? null,
|
|
11974
|
+
selectM3uStream: options.selectM3uStream ?? defaultSelectM3uStreamFn,
|
|
11138
11975
|
onDiscardedData: async (data) => {
|
|
11139
11976
|
await content.write(data);
|
|
11140
11977
|
},
|
|
@@ -11160,6 +11997,7 @@ var downloadAndParseMedia = async (options) => {
|
|
|
11160
11997
|
onSlowNumberOfFrames: options.onSlowNumberOfFrames ?? null,
|
|
11161
11998
|
onSlowVideoBitrate: options.onSlowVideoBitrate ?? null,
|
|
11162
11999
|
onStructure: options.onStructure ?? null,
|
|
12000
|
+
onM3uStreams: options.onM3uStreams ?? null,
|
|
11163
12001
|
onTracks: options.onTracks ?? null,
|
|
11164
12002
|
onUnrotatedDimensions: options.onUnrotatedDimensions ?? null,
|
|
11165
12003
|
onVideoCodec: options.onVideoCodec ?? null,
|
|
@@ -11183,54 +12021,8 @@ var downloadAndParseMedia = async (options) => {
|
|
|
11183
12021
|
await content.finish();
|
|
11184
12022
|
return returnValue;
|
|
11185
12023
|
};
|
|
11186
|
-
// src/parse-media.ts
|
|
11187
|
-
var parseMedia = (options) => {
|
|
11188
|
-
return internalParseMedia({
|
|
11189
|
-
fields: options.fields ?? null,
|
|
11190
|
-
logLevel: options.logLevel ?? "info",
|
|
11191
|
-
onAudioCodec: options.onAudioCodec ?? null,
|
|
11192
|
-
onAudioTrack: options.onAudioTrack ?? null,
|
|
11193
|
-
onContainer: options.onContainer ?? null,
|
|
11194
|
-
onDimensions: options.onDimensions ?? null,
|
|
11195
|
-
onDurationInSeconds: options.onDurationInSeconds ?? null,
|
|
11196
|
-
onFps: options.onFps ?? null,
|
|
11197
|
-
onImages: options.onImages ?? null,
|
|
11198
|
-
onInternalStats: options.onInternalStats ?? null,
|
|
11199
|
-
onIsHdr: options.onIsHdr ?? null,
|
|
11200
|
-
onKeyframes: options.onKeyframes ?? null,
|
|
11201
|
-
onLocation: options.onLocation ?? null,
|
|
11202
|
-
onMetadata: options.onMetadata ?? null,
|
|
11203
|
-
onMimeType: options.onMimeType ?? null,
|
|
11204
|
-
onName: options.onName ?? null,
|
|
11205
|
-
onNumberOfAudioChannels: options.onNumberOfAudioChannels ?? null,
|
|
11206
|
-
onParseProgress: options.onParseProgress ?? null,
|
|
11207
|
-
onRotation: options.onRotation ?? null,
|
|
11208
|
-
onSampleRate: options.onSampleRate ?? null,
|
|
11209
|
-
onSize: options.onSize ?? null,
|
|
11210
|
-
onSlowAudioBitrate: options.onSlowAudioBitrate ?? null,
|
|
11211
|
-
onSlowDurationInSeconds: options.onSlowDurationInSeconds ?? null,
|
|
11212
|
-
onSlowFps: options.onSlowFps ?? null,
|
|
11213
|
-
onSlowKeyframes: options.onSlowKeyframes ?? null,
|
|
11214
|
-
onSlowNumberOfFrames: options.onSlowNumberOfFrames ?? null,
|
|
11215
|
-
onSlowVideoBitrate: options.onSlowVideoBitrate ?? null,
|
|
11216
|
-
onStructure: options.onStructure ?? null,
|
|
11217
|
-
onTracks: options.onTracks ?? null,
|
|
11218
|
-
onUnrotatedDimensions: options.onUnrotatedDimensions ?? null,
|
|
11219
|
-
onVideoCodec: options.onVideoCodec ?? null,
|
|
11220
|
-
onVideoTrack: options.onVideoTrack ?? null,
|
|
11221
|
-
progressIntervalInMs: options.progressIntervalInMs ?? null,
|
|
11222
|
-
reader: options.reader ?? fetchReader,
|
|
11223
|
-
controller: options.controller ?? undefined,
|
|
11224
|
-
src: options.src,
|
|
11225
|
-
mode: "query",
|
|
11226
|
-
onDiscardedData: null,
|
|
11227
|
-
onError: () => ({ action: "fail" }),
|
|
11228
|
-
acknowledgeRemotionLicense: Boolean(options.acknowledgeRemotionLicense),
|
|
11229
|
-
apiName: "parseMedia()"
|
|
11230
|
-
});
|
|
11231
|
-
};
|
|
11232
12024
|
// src/version.ts
|
|
11233
|
-
var VERSION = "4.0.
|
|
12025
|
+
var VERSION = "4.0.267";
|
|
11234
12026
|
|
|
11235
12027
|
// src/index.ts
|
|
11236
12028
|
var MediaParserInternals = {
|
|
@@ -11253,6 +12045,7 @@ export {
|
|
|
11253
12045
|
mediaParserController,
|
|
11254
12046
|
hasBeenAborted,
|
|
11255
12047
|
downloadAndParseMedia,
|
|
12048
|
+
defaultSelectM3uStreamFn,
|
|
11256
12049
|
VERSION,
|
|
11257
12050
|
MediaParserInternals,
|
|
11258
12051
|
MediaParserAbortError,
|