shifro 0.2.0 → 1.0.0

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/shifro.cjs DELETED
@@ -1,1066 +0,0 @@
1
- //#region rolldown:runtime
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (all) => {
9
- let target = {};
10
- for (var name in all) __defProp(target, name, {
11
- get: all[name],
12
- enumerable: true
13
- });
14
- return target;
15
- };
16
- var __copyProps = (to, from, except, desc) => {
17
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
18
- key = keys[i];
19
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
20
- get: ((k) => from[k]).bind(null, key),
21
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
22
- });
23
- }
24
- return to;
25
- };
26
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
27
- value: mod,
28
- enumerable: true
29
- }) : target, mod));
30
-
31
- //#endregion
32
- let mp4box = require("mp4box");
33
- mp4box = __toESM(mp4box);
34
- let node_fs = require("node:fs");
35
- node_fs = __toESM(node_fs);
36
- let node_stream = require("node:stream");
37
- node_stream = __toESM(node_stream);
38
-
39
- //#region lib/decrypt.ts
40
- const decrypt = async ({ key, iv, data, algorithm = "AES-CTR" }) => {
41
- const algorithmName = algorithm.toUpperCase();
42
- const cryptoKey = await crypto.subtle.importKey("raw", key, { name: algorithmName }, false, ["decrypt"]);
43
- const decryptedData = await crypto.subtle.decrypt({
44
- name: algorithmName,
45
- counter: iv,
46
- length: 64
47
- }, cryptoKey, data);
48
- return new Uint8Array(decryptedData);
49
- };
50
-
51
- //#endregion
52
- //#region lib/data-view-reader.ts
53
- const toDataView = (uint8Array) => {
54
- const arrayBuffer = uint8Array.buffer;
55
- return new DataView(arrayBuffer, uint8Array.byteOffset, uint8Array.byteLength);
56
- };
57
- const fromDataView = (dataView, position = 0, length) => {
58
- return new Uint8Array(dataView.buffer, dataView.byteOffset + position, length);
59
- };
60
- var DataViewReaderError = class extends Error {
61
- constructor(severity, category, code, message) {
62
- super(message);
63
- this.severity = severity;
64
- this.category = category;
65
- this.code = code;
66
- this.name = "DataViewReaderError";
67
- }
68
- };
69
- var DataViewReader = class {
70
- dataView;
71
- littleEndian;
72
- position = 0;
73
- constructor(data, endianness = "BE") {
74
- this.dataView = toDataView(data);
75
- this.littleEndian = endianness === "LE";
76
- }
77
- getDataView() {
78
- return this.dataView;
79
- }
80
- hasMoreData() {
81
- return this.position < this.dataView.byteLength;
82
- }
83
- getPosition() {
84
- return this.position;
85
- }
86
- getLength() {
87
- return this.dataView.byteLength;
88
- }
89
- readUint8() {
90
- this.checkBounds(1);
91
- const value = this.dataView.getUint8(this.position);
92
- this.position += 1;
93
- return value;
94
- }
95
- readUint16() {
96
- this.checkBounds(2);
97
- const value = this.dataView.getUint16(this.position, this.littleEndian);
98
- this.position += 2;
99
- return value;
100
- }
101
- readUint32() {
102
- this.checkBounds(4);
103
- const value = this.dataView.getUint32(this.position, this.littleEndian);
104
- this.position += 4;
105
- return value;
106
- }
107
- readInt32() {
108
- this.checkBounds(4);
109
- const value = this.dataView.getInt32(this.position, this.littleEndian);
110
- this.position += 4;
111
- return value;
112
- }
113
- readUint64() {
114
- let low, high;
115
- try {
116
- if (this.littleEndian) {
117
- low = this.dataView.getUint32(this.position, true);
118
- high = this.dataView.getUint32(this.position + 4, true);
119
- } else {
120
- high = this.dataView.getUint32(this.position, false);
121
- low = this.dataView.getUint32(this.position + 4, false);
122
- }
123
- } catch (e) {
124
- throw this.outOfBounds();
125
- }
126
- if (high > 2097151) throw new DataViewReaderError("CRITICAL", "MEDIA", "JS_INTEGER_OVERFLOW", "64-bit integer overflow");
127
- this.position += 8;
128
- return high * Math.pow(2, 32) + low;
129
- }
130
- readBytes(bytes) {
131
- this.checkBounds(bytes);
132
- const value = fromDataView(this.dataView, this.position, bytes);
133
- this.position += bytes;
134
- return value;
135
- }
136
- skip(bytes) {
137
- this.checkBounds(bytes);
138
- this.position += bytes;
139
- }
140
- rewind(bytes) {
141
- if (this.position < bytes) throw this.outOfBounds();
142
- this.position -= bytes;
143
- }
144
- seek(position) {
145
- if (position < 0 || position > this.dataView.byteLength) throw this.outOfBounds();
146
- this.position = position;
147
- }
148
- readTerminatedString() {
149
- const start = this.position;
150
- while (this.hasMoreData() && this.dataView.getUint8(this.position) !== 0) this.position++;
151
- if (!this.hasMoreData()) throw this.outOfBounds();
152
- const buffer = new Uint8Array(this.dataView.buffer, start, this.position - start);
153
- this.position++;
154
- return new TextDecoder().decode(buffer);
155
- }
156
- checkBounds(bytes) {
157
- if (this.position + bytes > this.dataView.byteLength) throw this.outOfBounds();
158
- }
159
- outOfBounds() {
160
- return new DataViewReaderError("CRITICAL", "MEDIA", "BUFFER_READ_OUT_OF_BOUNDS", "Read operation out of bounds");
161
- }
162
- };
163
-
164
- //#endregion
165
- //#region lib/parser.ts
166
- let BoxType = /* @__PURE__ */ function(BoxType$1) {
167
- BoxType$1[BoxType$1["BASIC_BOX"] = 0] = "BASIC_BOX";
168
- BoxType$1[BoxType$1["FULL_BOX"] = 1] = "FULL_BOX";
169
- return BoxType$1;
170
- }({});
171
- var Mp4Parser = class Mp4Parser {
172
- headers = /* @__PURE__ */ new Map();
173
- boxDefinitions = /* @__PURE__ */ new Map();
174
- done = false;
175
- box(type, definition) {
176
- const typeCode = Mp4Parser.typeFromString(type);
177
- this.headers.set(typeCode, BoxType.BASIC_BOX);
178
- this.boxDefinitions.set(typeCode, definition);
179
- return this;
180
- }
181
- fullBox(type, definition) {
182
- const typeCode = Mp4Parser.typeFromString(type);
183
- this.headers.set(typeCode, BoxType.FULL_BOX);
184
- this.boxDefinitions.set(typeCode, definition);
185
- return this;
186
- }
187
- stop() {
188
- this.done = true;
189
- }
190
- parse(data, partialOkay = false, stopOnPartial = false) {
191
- const reader = new DataViewReader(data);
192
- this.done = false;
193
- while (reader.hasMoreData() && !this.done) this.parseNext(0, reader, partialOkay, stopOnPartial);
194
- }
195
- parseNext(absStart, reader, partialOkay, stopOnPartial) {
196
- const start = reader.getPosition();
197
- if (stopOnPartial && start + 8 > reader.getLength()) {
198
- this.done = true;
199
- return;
200
- }
201
- let size = reader.readUint32();
202
- const type = reader.readUint32();
203
- const name = Mp4Parser.typeToString(type);
204
- let has64BitSize = false;
205
- switch (size) {
206
- case 0:
207
- size = reader.getLength() - start;
208
- break;
209
- case 1:
210
- if (stopOnPartial && reader.getPosition() + 8 > reader.getLength()) {
211
- this.done = true;
212
- return;
213
- }
214
- size = Number(reader.readUint64());
215
- has64BitSize = true;
216
- break;
217
- }
218
- const boxDefinition = this.boxDefinitions.get(type);
219
- if (boxDefinition) {
220
- let version = null;
221
- let flags = null;
222
- if (this.headers.get(type) === BoxType.FULL_BOX) {
223
- if (stopOnPartial && reader.getPosition() + 4 > reader.getLength()) {
224
- this.done = true;
225
- return;
226
- }
227
- const versionAndFlags = reader.readUint32();
228
- version = versionAndFlags >>> 24;
229
- flags = versionAndFlags & 16777215;
230
- }
231
- let end = start + size;
232
- if (partialOkay && end > reader.getLength()) end = reader.getLength();
233
- if (stopOnPartial && end > reader.getLength()) {
234
- this.done = true;
235
- return;
236
- }
237
- const payloadSize = end - reader.getPosition();
238
- const payload = payloadSize > 0 ? reader.readBytes(payloadSize) : new Uint8Array();
239
- const payloadReader = new DataViewReader(payload);
240
- const box = {
241
- name,
242
- parser: this,
243
- partialOkay: !!partialOkay,
244
- version,
245
- flags,
246
- reader: payloadReader,
247
- size,
248
- start: start + absStart,
249
- has64BitSize
250
- };
251
- boxDefinition(box);
252
- } else {
253
- const skipLength = Math.min(start + size - reader.getPosition(), reader.getLength() - reader.getPosition());
254
- reader.skip(skipLength);
255
- }
256
- }
257
- static children(box) {
258
- const headerSize = Mp4Parser.headerSize(box);
259
- while (box.reader.hasMoreData() && !box.parser.done) box.parser.parseNext(box.start + headerSize, box.reader, box.partialOkay);
260
- }
261
- static sampleDescription(box) {
262
- const headerSize = Mp4Parser.headerSize(box);
263
- const count = box.reader.readUint32();
264
- for (let i = 0; i < count; i++) {
265
- box.parser.parseNext(box.start + headerSize, box.reader, box.partialOkay);
266
- if (box.parser.done) break;
267
- }
268
- }
269
- static visualSampleEntry(box) {
270
- const headerSize = Mp4Parser.headerSize(box);
271
- box.reader.skip(78);
272
- while (box.reader.hasMoreData() && !box.parser.done) box.parser.parseNext(box.start + headerSize, box.reader, box.partialOkay);
273
- }
274
- static audioSampleEntry(box) {
275
- const headerSize = Mp4Parser.headerSize(box);
276
- box.reader.skip(8);
277
- const version = box.reader.readUint16();
278
- box.reader.skip(6);
279
- if (version === 2) box.reader.skip(48);
280
- else box.reader.skip(12);
281
- if (version === 1) box.reader.skip(16);
282
- while (box.reader.hasMoreData() && !box.parser.done) box.parser.parseNext(box.start + headerSize, box.reader, box.partialOkay);
283
- }
284
- static allData(callback) {
285
- return (box) => {
286
- const remaining = box.reader.getLength() - box.reader.getPosition();
287
- callback(box.reader.readBytes(remaining));
288
- };
289
- }
290
- static typeFromString(name) {
291
- if (name.length !== 4) throw new Error("MP4 box names must be 4 characters long");
292
- let code = 0;
293
- for (const char of name) code = code << 8 | char.charCodeAt(0);
294
- return code;
295
- }
296
- static typeToString(type) {
297
- return String.fromCharCode(type >> 24 & 255, type >> 16 & 255, type >> 8 & 255, type & 255);
298
- }
299
- static headerSize(box) {
300
- const basicHeaderSize = 8;
301
- const size64Bit = box.has64BitSize ? 8 : 0;
302
- const versionAndFlags = box.flags !== null ? 4 : 0;
303
- return basicHeaderSize + size64Bit + versionAndFlags;
304
- }
305
- };
306
-
307
- //#endregion
308
- //#region lib/parsing/senc.ts
309
- const parseSencBox = (reader, flags) => {
310
- if (flags && flags & 1) {
311
- reader.readUint8();
312
- reader.readUint8();
313
- reader.readBytes(16);
314
- }
315
- const sampleCount = reader.readUint32();
316
- const samples = [];
317
- for (let i = 0; i < sampleCount; i++) {
318
- const ivSize = 8;
319
- const iv = new Uint8Array(16);
320
- iv.set(reader.readBytes(ivSize));
321
- const sample = {
322
- iv,
323
- subsamples: []
324
- };
325
- if (flags && flags & 2) {
326
- const subsampleCount = reader.readUint16();
327
- for (let j = 0; j < subsampleCount; j++) {
328
- const bytesOfClearData = reader.readUint16();
329
- const bytesOfEncryptedData = reader.readUint32();
330
- sample.subsamples.push({
331
- bytesOfClearData,
332
- bytesOfEncryptedData
333
- });
334
- }
335
- }
336
- samples.push(sample);
337
- }
338
- return { samples };
339
- };
340
-
341
- //#endregion
342
- //#region lib/parsing/trun.ts
343
- const parseTrunBox = (reader, flags, version) => {
344
- const sampleCount = reader.readUint32();
345
- const samples = [];
346
- let dataOffset = null;
347
- if (flags & 1) dataOffset = reader.readInt32();
348
- if (flags & 4) reader.skip(4);
349
- for (let i = 0; i < sampleCount; i++) {
350
- const sample = {};
351
- if (flags & 256) sample.duration = reader.readUint32();
352
- if (flags & 512) sample.size = reader.readUint32();
353
- if (flags & 1024) reader.skip(4);
354
- if (flags & 2048) sample.compositionTimeOffset = version == 0 ? reader.readUint32() : reader.readInt32();
355
- samples.push(sample);
356
- }
357
- return {
358
- samples,
359
- dataOffset
360
- };
361
- };
362
-
363
- //#endregion
364
- //#region lib/parsing/tfhd.ts
365
- const parseTfhdBox = (reader, flags) => {
366
- let defaultSampleDuration;
367
- let defaultSampleSize;
368
- let baseDataOffset;
369
- let sampleDescriptionIndex;
370
- const trackId = reader.readUint32();
371
- if (flags & 1) baseDataOffset = reader.readUint64();
372
- if (flags & 2) sampleDescriptionIndex = reader.readUint32();
373
- if (flags & 8) defaultSampleDuration = reader.readUint32();
374
- if (flags & 16) defaultSampleSize = reader.readUint32();
375
- return {
376
- trackId,
377
- defaultSampleDuration,
378
- defaultSampleSize,
379
- baseDataOffset,
380
- sampleDescriptionIndex
381
- };
382
- };
383
-
384
- //#endregion
385
- //#region lib/buffer.ts
386
- const indexOfUint8Array = (buffer, search, start) => {
387
- if (search.length === 0) return -1;
388
- const max = buffer.length - search.length + 1;
389
- for (let i = start; i < max; i++) {
390
- let match = true;
391
- for (let j = 0; j < search.length; j++) if (buffer[i + j] !== search[j]) {
392
- match = false;
393
- break;
394
- }
395
- if (match) return i;
396
- }
397
- return -1;
398
- };
399
- const bufferReplaceAll = (buffer, original, replacement) => {
400
- const encoder = new TextEncoder();
401
- const originalBytes = encoder.encode(original);
402
- const replacementBytes = encoder.encode(replacement);
403
- if (originalBytes.length !== replacementBytes.length) throw new Error("Original and replacement must have the same byte length");
404
- let idx = 0;
405
- do {
406
- idx = indexOfUint8Array(buffer, originalBytes, 0);
407
- if (idx !== -1) buffer.set(replacementBytes, idx);
408
- } while (idx !== -1);
409
- };
410
- const bufferIncludes = (buffer, search, offset = 0) => {
411
- const encoder = new TextEncoder();
412
- let searchBytes;
413
- if (typeof search === "string") searchBytes = encoder.encode(search);
414
- else if (typeof search === "number") searchBytes = new Uint8Array([search]);
415
- else searchBytes = search;
416
- if (searchBytes.length === 0) return true;
417
- if (buffer.length < searchBytes.length) return false;
418
- return indexOfUint8Array(buffer, searchBytes, offset) !== -1;
419
- };
420
- const concatUint8Array = (list, totalLength) => {
421
- if (list.length === 0) return new Uint8Array(0);
422
- let calculatedLength = totalLength ?? list.reduce((acc, curr) => acc + curr.length, 0);
423
- calculatedLength = Math.min(calculatedLength, list.reduce((acc, curr) => acc + curr.length, 0));
424
- const result = new Uint8Array(calculatedLength);
425
- let offset = 0;
426
- for (const bytes of list) {
427
- if (offset >= calculatedLength) break;
428
- const chunk = bytes.subarray(0, Math.min(bytes.length, calculatedLength - offset));
429
- result.set(chunk, offset);
430
- offset += chunk.length;
431
- }
432
- return result;
433
- };
434
- const copyUint8Array = (source, target, targetStart = 0, sourceStart = 0, sourceEnd = source.length) => {
435
- targetStart = Math.max(0, Math.min(targetStart, target.length));
436
- sourceStart = Math.max(0, Math.min(sourceStart, source.length));
437
- sourceEnd = Math.min(sourceEnd, source.length);
438
- if (sourceEnd < sourceStart) sourceEnd = sourceStart;
439
- const bytesToCopy = Math.min(sourceEnd - sourceStart, target.length - targetStart);
440
- if (bytesToCopy <= 0) return 0;
441
- target.set(source.subarray(sourceStart, sourceStart + bytesToCopy), targetStart);
442
- return bytesToCopy;
443
- };
444
- const writeUint8Array = (target, str, offset = 0, maxBytes) => {
445
- const encoded = new TextEncoder().encode(str);
446
- const safeOffset = Math.max(0, Math.min(offset, target.length));
447
- const remainingSpace = target.length - safeOffset;
448
- let bytesToWrite = encoded.length;
449
- if (typeof maxBytes === "number") bytesToWrite = Math.min(bytesToWrite, maxBytes);
450
- bytesToWrite = Math.min(bytesToWrite, remainingSpace);
451
- if (bytesToWrite <= 0) return 0;
452
- target.set(encoded.subarray(0, bytesToWrite), safeOffset);
453
- return bytesToWrite;
454
- };
455
- const toHex = (data) => Array.from(data).map((byte) => byte.toString(16).padStart(2, "0")).join("");
456
- const toBase64 = (data) => {
457
- const binString = Array.from(data, (byte) => String.fromCodePoint(byte)).join("");
458
- return btoa(binString);
459
- };
460
- const parseHex = (hex) => hex.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) ?? [];
461
-
462
- //#endregion
463
- //#region lib/parsing/tenc.ts
464
- const parseTencBox = (reader) => {
465
- reader.readUint8();
466
- reader.readUint8();
467
- reader.readUint8();
468
- reader.readUint8();
469
- return { defaultKID: toHex(reader.readBytes(16)) };
470
- };
471
-
472
- //#endregion
473
- //#region lib/parsing/pssh.ts
474
- const parsePsshBox = (box) => {
475
- const SYSTEM_ID_WIDEVINE = "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed".replaceAll("-", "");
476
- const version = box.version ?? 0;
477
- if (version !== 0 && version !== 1) throw new Error("PSSH version can only be 0 or 1");
478
- const systemIdBytes = box.reader.readBytes(16);
479
- const systemIdHex = toHex(systemIdBytes);
480
- const systemIdUuid = [
481
- systemIdHex.slice(0, 8),
482
- systemIdHex.slice(8, 12),
483
- systemIdHex.slice(12, 16),
484
- systemIdHex.slice(16, 20),
485
- systemIdHex.slice(20)
486
- ].join("-");
487
- const dataView = box.reader.getDataView();
488
- const pssh = new Uint8Array(dataView.buffer, dataView.byteOffset - 12, box.size);
489
- const psshBase64 = toBase64(pssh);
490
- const keyIds = [];
491
- if (version >= 1) {
492
- const kidCount = box.reader.readUint32();
493
- for (let i = 0; i < kidCount; i++) keyIds.push(toHex(box.reader.readBytes(16)));
494
- }
495
- const dataSize = box.reader.readUint32();
496
- const systemData = box.reader.readBytes(dataSize);
497
- if (systemIdHex === SYSTEM_ID_WIDEVINE) {}
498
- return {
499
- version,
500
- systemId: systemIdUuid,
501
- pssh: psshBase64,
502
- keyIds,
503
- systemData
504
- };
505
- };
506
-
507
- //#endregion
508
- //#region lib/initialization.ts
509
- const isInitData = (chunk) => {
510
- try {
511
- let hasMoov = false;
512
- let hasMoof = false;
513
- new Mp4Parser().box("moov", () => {
514
- hasMoov = true;
515
- }).box("moof", () => {
516
- hasMoof = true;
517
- }).parse(chunk, true, true);
518
- return hasMoov && !hasMoof;
519
- } catch (e) {
520
- console.log(e);
521
- return false;
522
- }
523
- };
524
- const getOriginalCodec = (chunk) => {
525
- let format = null;
526
- new Mp4Parser().box("moov", Mp4Parser.children).box("trak", Mp4Parser.children).box("mdia", Mp4Parser.children).box("minf", Mp4Parser.children).box("stbl", Mp4Parser.children).fullBox("stsd", Mp4Parser.sampleDescription).box("encv", Mp4Parser.visualSampleEntry).box("enca", Mp4Parser.audioSampleEntry).box("sinf", Mp4Parser.children).box("frma", (box) => {
527
- const bytes = box.reader.readBytes(4);
528
- format = new TextDecoder().decode(bytes);
529
- }).parse(chunk, true, true);
530
- return format;
531
- };
532
- const processInit = async (chunk) => {
533
- const originalCodec = getOriginalCodec(chunk);
534
- bufferReplaceAll(chunk, "sinf", "skip");
535
- bufferReplaceAll(chunk, "pssh", "skip");
536
- if (originalCodec) {
537
- bufferReplaceAll(chunk, "encv", originalCodec);
538
- bufferReplaceAll(chunk, "enca", originalCodec);
539
- } else {
540
- console.warn("Could not detect original codec, using fallback values");
541
- if ((bufferIncludes(chunk, "encv") ? "video" : "audio") === "video") bufferReplaceAll(chunk, "encv", "avc1");
542
- else bufferReplaceAll(chunk, "enca", "mp4a");
543
- }
544
- return chunk;
545
- };
546
- const parseInit = (data) => {
547
- const initInfo = {
548
- schemeType: "",
549
- defaultKID: "",
550
- psshList: []
551
- };
552
- new Mp4Parser().box("moov", Mp4Parser.children).box("trak", Mp4Parser.children).box("mdia", Mp4Parser.children).box("minf", Mp4Parser.children).box("stbl", Mp4Parser.children).fullBox("stsd", Mp4Parser.sampleDescription).fullBox("pssh", (box) => initInfo.psshList.push(parsePsshBox(box))).box("encv", Mp4Parser.visualSampleEntry).box("enca", Mp4Parser.audioSampleEntry).box("sinf", Mp4Parser.children).box("schi", Mp4Parser.children).fullBox("schm", (box) => {
553
- initInfo.schemeType = new TextDecoder().decode(box.reader.readBytes(4));
554
- }).fullBox("tenc", (box) => {
555
- const { defaultKID } = parseTencBox(box.reader);
556
- initInfo.defaultKID = defaultKID;
557
- }).parse(data, false, true);
558
- return initInfo;
559
- };
560
-
561
- //#endregion
562
- //#region lib/process.ts
563
- const processEncryptedSegment = async (segment, transformSample) => {
564
- if (isInitData(segment)) return processInit(segment);
565
- let sencInfo;
566
- let trunInfo;
567
- let tfhdInfo;
568
- let mdatOffset;
569
- new Mp4Parser().box("moof", Mp4Parser.children).box("traf", Mp4Parser.children).fullBox("tfhd", (box) => {
570
- tfhdInfo = parseTfhdBox(box.reader, box.flags);
571
- }).fullBox("trun", (box) => {
572
- trunInfo = parseTrunBox(box.reader, box.flags, box.version);
573
- }).fullBox("senc", (box) => {
574
- sencInfo = parseSencBox(box.reader, box.flags);
575
- }).parse(segment, true, true);
576
- mdatOffset = trunInfo.dataOffset;
577
- if (sencInfo.samples.length !== trunInfo.samples.length) throw new Error(`sample count mismatch: trun has ${trunInfo.samples.length}, senc has ${sencInfo.samples.length}`);
578
- let position = 0;
579
- let time = 0;
580
- for (let i = 0; i < sencInfo.samples.length; i++) {
581
- const sencSample = sencInfo.samples[i];
582
- const trunSample = trunInfo.samples[i];
583
- const expectedSize = trunInfo.samples[i].size || tfhdInfo.defaultSampleSize || 0;
584
- if (!sencSample.subsamples.length) sencSample.subsamples.push({
585
- bytesOfClearData: 0,
586
- bytesOfEncryptedData: expectedSize
587
- });
588
- if (sencSample.subsamples.some((subsample) => subsample.bytesOfEncryptedData > 0)) {
589
- let offset = 0;
590
- const encryptedParts = [];
591
- for (const subsample of sencSample.subsamples) {
592
- offset += subsample.bytesOfClearData;
593
- if (subsample.bytesOfEncryptedData > 0) {
594
- const encryptedData$1 = segment.subarray(mdatOffset + position + offset, mdatOffset + position + offset + subsample.bytesOfEncryptedData);
595
- encryptedParts.push(encryptedData$1);
596
- }
597
- offset += subsample.bytesOfEncryptedData;
598
- }
599
- const encryptedData = concatUint8Array(encryptedParts);
600
- const subsampleParams = {
601
- iv: sencSample.iv,
602
- data: encryptedData,
603
- timestamp: time
604
- };
605
- const decryptedData = await transformSample(subsampleParams);
606
- if (decryptedData) {
607
- offset = 0;
608
- let decryptedOffset = 0;
609
- const decryptedSampleParts = [];
610
- for (const subsample of sencSample.subsamples) {
611
- if (subsample.bytesOfClearData > 0) {
612
- const clearData = segment.subarray(mdatOffset + position + offset, mdatOffset + position + offset + subsample.bytesOfClearData);
613
- decryptedSampleParts.push(clearData);
614
- offset += subsample.bytesOfClearData;
615
- }
616
- if (subsample.bytesOfEncryptedData > 0) {
617
- const decryptedPart = decryptedData.subarray(decryptedOffset, decryptedOffset + subsample.bytesOfEncryptedData);
618
- decryptedSampleParts.push(decryptedPart);
619
- decryptedOffset += subsample.bytesOfEncryptedData;
620
- offset += subsample.bytesOfEncryptedData;
621
- }
622
- }
623
- const decryptedSample = concatUint8Array(decryptedSampleParts);
624
- copyUint8Array(decryptedSample, segment, mdatOffset + position);
625
- }
626
- }
627
- position += expectedSize;
628
- time += trunSample.duration || tfhdInfo.defaultSampleDuration || 0;
629
- }
630
- new Mp4Parser().box("moof", Mp4Parser.children).box("traf", Mp4Parser.children).box("senc", (box) => {
631
- const newName = "skip";
632
- const offset = box.start + 4;
633
- writeUint8Array(segment, newName, offset);
634
- }).parse(segment, true, true);
635
- return segment;
636
- };
637
-
638
- //#endregion
639
- //#region lib/stream.ts
640
- const findInit = (buffer) => {
641
- let moovEnd = null;
642
- new Mp4Parser().box("moov", (box) => {
643
- moovEnd = box.start + box.size;
644
- }).parse(buffer, true, true);
645
- const init = moovEnd ? buffer.subarray(0, moovEnd) : null;
646
- return {
647
- moovEnd,
648
- init
649
- };
650
- };
651
- const findSegment = (buffer) => {
652
- let moofStart = null;
653
- let mdatEnd = null;
654
- new Mp4Parser().box("moof", (box) => {
655
- if (moofStart === null) moofStart = box.start;
656
- }).box("mdat", (box) => {
657
- if (mdatEnd === null) mdatEnd = box.start + box.size;
658
- }).parse(buffer, true, true);
659
- return {
660
- segment: moofStart !== null && mdatEnd !== null ? buffer.subarray(moofStart, mdatEnd) : null,
661
- moofStart,
662
- mdatEnd
663
- };
664
- };
665
- var Mp4SegmentTransformer = class {
666
- buffer = new Uint8Array();
667
- isProcessingInit = true;
668
- scheme = null;
669
- processedBytes = 0;
670
- constructor(options) {
671
- this.options = options;
672
- }
673
- async transform(chunk, controller) {
674
- try {
675
- this.buffer = concatUint8Array([this.buffer, chunk]);
676
- let shouldContinue = true;
677
- while (shouldContinue) {
678
- if (this.isProcessingInit) {
679
- const { init, moovEnd } = findInit(this.buffer);
680
- if (!moovEnd || this.buffer.length < moovEnd) break;
681
- if (init && isInitData(init)) {
682
- this.scheme = parseInit(init).schemeType;
683
- const processedInit = await processInit(init);
684
- controller.enqueue(processedInit);
685
- this.updateProgress(processedInit.byteLength);
686
- this.buffer = this.buffer.subarray(moovEnd);
687
- this.isProcessingInit = false;
688
- continue;
689
- }
690
- }
691
- const { moofStart, mdatEnd, segment } = findSegment(this.buffer);
692
- if (moofStart === null || mdatEnd === null || !segment || this.buffer.length < mdatEnd) break;
693
- const onSubsampleData = (params) => {
694
- params.encryptionScheme = this.scheme;
695
- return this.options.transformSample(params);
696
- };
697
- const processedSegment = await processEncryptedSegment(segment, onSubsampleData);
698
- controller.enqueue(processedSegment);
699
- this.updateProgress(processedSegment.byteLength);
700
- this.buffer = this.buffer.subarray(mdatEnd);
701
- shouldContinue = this.buffer.length >= 8;
702
- }
703
- } catch (error) {
704
- controller.error(error);
705
- }
706
- }
707
- async flush(controller) {
708
- if (this.buffer.length > 0) {
709
- controller.enqueue(this.buffer);
710
- this.updateProgress(this.buffer.byteLength);
711
- }
712
- }
713
- updateProgress(bytes) {
714
- this.processedBytes += bytes;
715
- if (this.options.onProgress) this.options.onProgress(this.processedBytes);
716
- }
717
- };
718
- const processStream = async (readable, writable, { transformSample, onProgress, preventClose }) => {
719
- const transformer = new Mp4SegmentTransformer({
720
- transformSample,
721
- onProgress
722
- });
723
- const transform = new TransformStream({
724
- transform: (chunk, controller) => transformer.transform(chunk, controller),
725
- flush: (controller) => transformer.flush(controller)
726
- });
727
- await readable.pipeThrough(transform).pipeTo(writable, { preventClose });
728
- };
729
-
730
- //#endregion
731
- //#region lib/decrypt-stream.ts
732
- function getSencForMoofNumber(mp4, num) {
733
- const tenc = mp4.getBox("tenc");
734
- tenc.default_isProtected;
735
- tenc.default_KID;
736
- const perSampleIvSize = tenc.default_Per_Sample_IV_Size;
737
- const moof = mp4.moofs[num];
738
- const mdat = mp4.mdats[num];
739
- const traf = moof.trafs[0];
740
- const trun = traf.truns[0];
741
- const senc = traf.senc;
742
- const reader = new DataViewReader(senc.data);
743
- const sampleCount = reader.readUint32();
744
- senc.samples = [];
745
- for (let i = 0; i < sampleCount; i++) {
746
- trun.sample_size[i];
747
- trun.sample_duration[i];
748
- const sample = {};
749
- const iv = new Uint8Array(16);
750
- iv.set(reader.readBytes(perSampleIvSize));
751
- sample.InitializationVector = iv;
752
- if (senc.flags & 2) {
753
- sample.subsamples = [];
754
- const subsampleCount = reader.readUint16();
755
- for (let j = 0; j < subsampleCount; j++) {
756
- let subsample = {};
757
- subsample.BytesOfClearData = reader.readUint16();
758
- subsample.BytesOfProtectedData = reader.readUint32();
759
- sample.subsamples.push(subsample);
760
- }
761
- }
762
- senc.samples.push(sample);
763
- }
764
- return {
765
- moof,
766
- mdat,
767
- traf,
768
- trun,
769
- senc
770
- };
771
- }
772
- var DecryptTransformer = class {
773
- input;
774
- inputReady;
775
- output;
776
- buffer = new Uint8Array();
777
- bytesRead = 0;
778
- processedBytes = 0;
779
- samplesProcessingQueue = [];
780
- constructor(options = {}) {
781
- this.options = options;
782
- this.input = (0, mp4box.createFile)(true);
783
- this.output = (0, mp4box.createFile)(false);
784
- this.inputReady = new Promise((resolve) => {
785
- this.input.onReady = resolve;
786
- });
787
- this.inputReady.then((info) => {
788
- const track = info.tracks[0];
789
- const totalSamples = track.nb_samples;
790
- this.input.setExtractionOptions(track.id, void 0, { nbSamples: totalSamples });
791
- const init = this.processInit();
792
- this.buffer = concatUint8Array([this.buffer, init]);
793
- this.input.onSamples = (_id, _user, samples) => {
794
- this.samplesProcessingQueue.push(this.processSamples(samples));
795
- };
796
- this.input.start();
797
- });
798
- }
799
- processInit() {
800
- const initStream = new mp4box.DataStream();
801
- const ftyp = this.output.ftyp;
802
- const moov = this.output.moov;
803
- const stsd = moov.traks[0].mdia.minf.stbl.stsd;
804
- const encSampleEntry = stsd.entries?.find((box) => !box.box_name);
805
- const frma = encSampleEntry.sinf.frma;
806
- const decSampleEntry = new mp4box.BoxParser.sampleEntry[frma.data_format]();
807
- for (const key of Object.keys(encSampleEntry)) decSampleEntry.set(key, encSampleEntry[key]);
808
- decSampleEntry.boxes = decSampleEntry.boxes?.filter((box) => box.box_name !== "ProtectionSchemeInfoBox");
809
- stsd.addEntry(decSampleEntry);
810
- stsd.entries = stsd.entries?.filter((box) => !!box.box_name);
811
- moov.psshs = [];
812
- moov.boxes = this.output.moov.boxes?.filter((box) => box.box_name !== "ProtectionSystemSpecificHeaderBox");
813
- ftyp.write(initStream);
814
- moov.write(initStream);
815
- return new Uint8Array(initStream.buffer);
816
- }
817
- async processSamples(samples) {
818
- const trackId = samples[0].track_id;
819
- let moof = this.input.moofs[samples[0].moof_number - 1];
820
- let traf = moof.trafs[0];
821
- traf.truns[0];
822
- for (const sample of samples) {
823
- const { senc } = getSencForMoofNumber(this.input, sample.moof_number - 1);
824
- const sencSample = senc.samples[sample.number_in_traf];
825
- const encryptedParts = [];
826
- let sampleOffset = 0;
827
- for (const subsample of sencSample.subsamples) {
828
- encryptedParts.push(sample.data.subarray(sampleOffset + subsample.BytesOfClearData, sampleOffset + subsample.BytesOfClearData + subsample.BytesOfProtectedData));
829
- sampleOffset += subsample.BytesOfClearData + subsample.BytesOfProtectedData;
830
- }
831
- const encryptedData = concatUint8Array(encryptedParts);
832
- const decrypted = await this.options.transformSample?.({
833
- data: encryptedData,
834
- iv: sencSample.InitializationVector,
835
- timestamp: sample.cts,
836
- encryptionScheme: "cenc"
837
- });
838
- const decryptedParts = [];
839
- let sample_idx = 0;
840
- let pt_idx = 0;
841
- for (const subsample of sencSample.subsamples) {
842
- decryptedParts.push(sample.data.subarray(sample_idx, sample_idx + subsample.BytesOfClearData));
843
- sample_idx += subsample.BytesOfClearData + subsample.BytesOfProtectedData;
844
- decryptedParts.push(decrypted.subarray(pt_idx, pt_idx + subsample.BytesOfProtectedData));
845
- pt_idx += subsample.BytesOfProtectedData;
846
- }
847
- const decryptedSample = concatUint8Array(decryptedParts);
848
- sample.data = decryptedSample;
849
- sample.size = decryptedSample.byteLength;
850
- }
851
- traf.boxes = traf.boxes?.filter((box) => box.box_name !== "PiffSampleEncryptionBox" && box.box_name !== "SampleEncryptionBox" && box.box_name !== "SampleAuxiliaryInformationOffsetsBox" && box.box_name !== "SampleAuxiliaryInformationSizesBox");
852
- const stream$1 = new mp4box.DataStream();
853
- moof.write(stream$1);
854
- moof.trafs[0].truns[0].data_offset = moof.size + 8;
855
- stream$1.adjustUint32(moof.trafs[0].truns[0].data_offset_position, moof.trafs[0].truns[0].data_offset);
856
- const mdat = new mp4box.BoxParser.box.mdat();
857
- const samplesData = concatUint8Array(samples.map((s) => s.data));
858
- const samplesMp4Buffer = mp4box.MP4BoxBuffer.fromArrayBuffer(samplesData.buffer, 0);
859
- mdat.stream = new mp4box.MultiBufferStream(samplesMp4Buffer);
860
- mdat.write(stream$1);
861
- const segment = new Uint8Array(stream$1.buffer);
862
- const nextSampleNum = Math.max(...samples.map((s) => s.moof_number)) + 1;
863
- this.input.releaseUsedSamples(trackId, nextSampleNum);
864
- return segment;
865
- }
866
- async transform(chunk, controller) {
867
- try {
868
- const boxBuffer = mp4box.MP4BoxBuffer.fromArrayBuffer(chunk.buffer, this.bytesRead);
869
- this.input.appendBuffer(boxBuffer);
870
- this.output.appendBuffer(boxBuffer);
871
- this.bytesRead += chunk.length;
872
- if (this.samplesProcessingQueue.length > 0) {
873
- const segments = await Promise.all(this.samplesProcessingQueue);
874
- this.samplesProcessingQueue = [];
875
- const processed = concatUint8Array(segments);
876
- this.buffer = concatUint8Array([this.buffer, processed]);
877
- }
878
- if (this.buffer.length > 0) {
879
- controller.enqueue(this.buffer);
880
- this.updateProgress(this.buffer.byteLength);
881
- this.buffer = new Uint8Array();
882
- }
883
- } catch (error) {
884
- controller.error(error);
885
- }
886
- }
887
- async flush(controller) {
888
- if (this.buffer.length > 0) {
889
- controller.enqueue(this.buffer);
890
- this.updateProgress(this.buffer.byteLength);
891
- }
892
- }
893
- updateProgress(bytes) {
894
- this.processedBytes += bytes;
895
- if (this.options.onProgress) this.options.onProgress(this.processedBytes);
896
- }
897
- };
898
- var DecryptStream = class extends TransformStream {
899
- constructor(options = {}) {
900
- const transformer = new DecryptTransformer(options);
901
- super({
902
- transform: (chunk, controller) => transformer.transform(chunk, controller),
903
- flush: (controller) => transformer.flush(controller)
904
- });
905
- }
906
- };
907
-
908
- //#endregion
909
- //#region lib/node/exports.ts
910
- var exports_exports = /* @__PURE__ */ __export({
911
- fs: () => node_fs,
912
- stream: () => node_stream.default
913
- });
914
-
915
- //#endregion
916
- //#region lib/api.ts
917
- const node = typeof exports_exports !== "undefined" ? exports_exports : void 0;
918
- var FilePathSource = class {
919
- readable;
920
- constructor(filePath) {
921
- this.filePath = filePath;
922
- const readStream = node.fs.createReadStream(filePath);
923
- this.readable = node.stream.Readable.toWeb(readStream);
924
- }
925
- };
926
- var FilePathTarget = class {
927
- writable;
928
- constructor(filePath) {
929
- this.filePath = filePath;
930
- const writeStream = node.fs.createWriteStream(filePath);
931
- this.writable = node.stream.Writable.toWeb(writeStream);
932
- }
933
- };
934
- var StreamSource = class {
935
- constructor(readable) {
936
- this.readable = readable;
937
- }
938
- };
939
- var StreamTarget = class {
940
- constructor(writable) {
941
- this.writable = writable;
942
- }
943
- };
944
- var BufferSource = class {
945
- readable;
946
- constructor(buffer) {
947
- this.buffer = buffer;
948
- this.readable = new ReadableStream({ start(controller) {
949
- controller.enqueue(buffer);
950
- controller.close();
951
- } });
952
- }
953
- };
954
- var BufferTarget = class {
955
- writable;
956
- buffer = null;
957
- constructor() {
958
- const chunks = [];
959
- this.writable = new WritableStream({
960
- start: () => {},
961
- write: (chunk) => {
962
- chunks.push(chunk);
963
- },
964
- abort: (reason) => {
965
- console.error("WritableStream aborted:", reason);
966
- },
967
- close: () => {
968
- this.buffer = concatUint8Array(chunks);
969
- }
970
- });
971
- }
972
- };
973
- var Input = class {
974
- source;
975
- constructor(options) {
976
- this.source = options.source;
977
- }
978
- };
979
- var Output = class {
980
- target;
981
- constructor(options) {
982
- this.target = options.target;
983
- }
984
- };
985
- var Decryption = class Decryption {
986
- decrypt;
987
- input;
988
- output;
989
- static async init(options) {
990
- const decryption = new Decryption(options);
991
- await decryption.#init();
992
- return decryption;
993
- }
994
- constructor(options) {
995
- this.input = options.input;
996
- this.output = options.output;
997
- const key = new Uint8Array(parseHex(options.keys[0].key));
998
- const decryptFn = async (params) => decryptWithKey(key, params);
999
- this.decrypt = new DecryptStream({
1000
- transformSample: decryptFn,
1001
- onProgress: options.onProgress
1002
- });
1003
- }
1004
- async #init() {}
1005
- async execute() {
1006
- const readable = this.input.source.readable;
1007
- const writable = this.output.target.writable;
1008
- const decrypt$1 = this.decrypt;
1009
- await readable.pipeThrough(decrypt$1).pipeTo(writable);
1010
- }
1011
- };
1012
-
1013
- //#endregion
1014
- //#region shifro.ts
1015
- const decryptWithKey = async (key, params) => {
1016
- const scheme = params.encryptionScheme;
1017
- const iv = params.iv;
1018
- const data = params.data;
1019
- if (scheme === "cbcs") return decrypt({
1020
- key,
1021
- iv,
1022
- data,
1023
- algorithm: "AES-CBC"
1024
- });
1025
- else return decrypt({
1026
- key,
1027
- iv,
1028
- data,
1029
- algorithm: "AES-CTR"
1030
- });
1031
- };
1032
- const decryptSegment = async (segment, params) => {
1033
- const hasKey = "key" in params;
1034
- const key = new Uint8Array(parseHex(hasKey ? params.key : ""));
1035
- const decryptFn = async (subsampleParams) => decryptWithKey(key, {
1036
- encryptionScheme: "encryptionScheme" in params ? params.encryptionScheme : void 0,
1037
- ...subsampleParams
1038
- });
1039
- const transformSample = hasKey ? decryptFn : params.transformSample;
1040
- return processEncryptedSegment(segment, transformSample);
1041
- };
1042
- const decryptStream = async (readable, writable, { preventClose, onProgress,...params }) => {
1043
- const hasKey = "key" in params;
1044
- const key = new Uint8Array(parseHex(hasKey ? params.key : ""));
1045
- const decryptFn = async (params$1) => decryptWithKey(key, params$1);
1046
- const transformSample = hasKey ? decryptFn : params.transformSample;
1047
- await processStream(readable, writable, {
1048
- preventClose,
1049
- onProgress,
1050
- transformSample
1051
- });
1052
- };
1053
-
1054
- //#endregion
1055
- exports.BufferSource = BufferSource;
1056
- exports.BufferTarget = BufferTarget;
1057
- exports.Decryption = Decryption;
1058
- exports.FilePathSource = FilePathSource;
1059
- exports.FilePathTarget = FilePathTarget;
1060
- exports.Input = Input;
1061
- exports.Output = Output;
1062
- exports.StreamSource = StreamSource;
1063
- exports.StreamTarget = StreamTarget;
1064
- exports.decryptSegment = decryptSegment;
1065
- exports.decryptStream = decryptStream;
1066
- exports.decryptWithKey = decryptWithKey;