@intactfile/engines 0.1.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/README.md +68 -0
- package/dist/archive/diagnose.d.ts +37 -0
- package/dist/archive/diagnose.d.ts.map +1 -0
- package/dist/archive/diagnose.js +165 -0
- package/dist/archive/diagnose.js.map +1 -0
- package/dist/archive/format.d.ts +52 -0
- package/dist/archive/format.d.ts.map +1 -0
- package/dist/archive/format.js +87 -0
- package/dist/archive/format.js.map +1 -0
- package/dist/archive/host.d.ts +83 -0
- package/dist/archive/host.d.ts.map +1 -0
- package/dist/archive/host.js +92 -0
- package/dist/archive/host.js.map +1 -0
- package/dist/archive/index.d.ts +22 -0
- package/dist/archive/index.d.ts.map +1 -0
- package/dist/archive/index.js +19 -0
- package/dist/archive/index.js.map +1 -0
- package/dist/archive/salvage.d.ts +73 -0
- package/dist/archive/salvage.d.ts.map +1 -0
- package/dist/archive/salvage.js +210 -0
- package/dist/archive/salvage.js.map +1 -0
- package/dist/byte-source.d.ts +19 -0
- package/dist/byte-source.d.ts.map +1 -0
- package/dist/byte-source.js +16 -0
- package/dist/byte-source.js.map +1 -0
- package/dist/bytesource/blob.d.ts +14 -0
- package/dist/bytesource/blob.d.ts.map +1 -0
- package/dist/bytesource/blob.js +23 -0
- package/dist/bytesource/blob.js.map +1 -0
- package/dist/crc32.d.ts +2 -0
- package/dist/crc32.d.ts.map +1 -0
- package/dist/crc32.js +18 -0
- package/dist/crc32.js.map +1 -0
- package/dist/detect/index.d.ts +16 -0
- package/dist/detect/index.d.ts.map +1 -0
- package/dist/detect/index.js +159 -0
- package/dist/detect/index.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/jpeg/browser-loader.d.ts +21 -0
- package/dist/jpeg/browser-loader.d.ts.map +1 -0
- package/dist/jpeg/browser-loader.js +28 -0
- package/dist/jpeg/browser-loader.js.map +1 -0
- package/dist/jpeg/diagnose.d.ts +50 -0
- package/dist/jpeg/diagnose.d.ts.map +1 -0
- package/dist/jpeg/diagnose.js +124 -0
- package/dist/jpeg/diagnose.js.map +1 -0
- package/dist/jpeg/exif.d.ts +11 -0
- package/dist/jpeg/exif.d.ts.map +1 -0
- package/dist/jpeg/exif.js +50 -0
- package/dist/jpeg/exif.js.map +1 -0
- package/dist/jpeg/format.d.ts +106 -0
- package/dist/jpeg/format.d.ts.map +1 -0
- package/dist/jpeg/format.js +248 -0
- package/dist/jpeg/format.js.map +1 -0
- package/dist/jpeg/index.d.ts +18 -0
- package/dist/jpeg/index.d.ts.map +1 -0
- package/dist/jpeg/index.js +14 -0
- package/dist/jpeg/index.js.map +1 -0
- package/dist/jpeg/mozjpeg-host.d.ts +60 -0
- package/dist/jpeg/mozjpeg-host.d.ts.map +1 -0
- package/dist/jpeg/mozjpeg-host.js +138 -0
- package/dist/jpeg/mozjpeg-host.js.map +1 -0
- package/dist/jpeg/repair.d.ts +89 -0
- package/dist/jpeg/repair.d.ts.map +1 -0
- package/dist/jpeg/repair.js +216 -0
- package/dist/jpeg/repair.js.map +1 -0
- package/dist/node.d.ts +11 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +29 -0
- package/dist/node.js.map +1 -0
- package/dist/pdf/browser-loader.d.ts +30 -0
- package/dist/pdf/browser-loader.d.ts.map +1 -0
- package/dist/pdf/browser-loader.js +21 -0
- package/dist/pdf/browser-loader.js.map +1 -0
- package/dist/pdf/diagnose.d.ts +23 -0
- package/dist/pdf/diagnose.d.ts.map +1 -0
- package/dist/pdf/diagnose.js +113 -0
- package/dist/pdf/diagnose.js.map +1 -0
- package/dist/pdf/format.d.ts +61 -0
- package/dist/pdf/format.d.ts.map +1 -0
- package/dist/pdf/format.js +150 -0
- package/dist/pdf/format.js.map +1 -0
- package/dist/pdf/index.d.ts +17 -0
- package/dist/pdf/index.d.ts.map +1 -0
- package/dist/pdf/index.js +12 -0
- package/dist/pdf/index.js.map +1 -0
- package/dist/pdf/qpdf-host.d.ts +66 -0
- package/dist/pdf/qpdf-host.d.ts.map +1 -0
- package/dist/pdf/qpdf-host.js +149 -0
- package/dist/pdf/qpdf-host.js.map +1 -0
- package/dist/pdf/rebuild.d.ts +32 -0
- package/dist/pdf/rebuild.d.ts.map +1 -0
- package/dist/pdf/rebuild.js +90 -0
- package/dist/pdf/rebuild.js.map +1 -0
- package/dist/pdf/repair.d.ts +61 -0
- package/dist/pdf/repair.d.ts.map +1 -0
- package/dist/pdf/repair.js +167 -0
- package/dist/pdf/repair.js.map +1 -0
- package/dist/png/diagnose.d.ts +50 -0
- package/dist/png/diagnose.d.ts.map +1 -0
- package/dist/png/diagnose.js +261 -0
- package/dist/png/diagnose.js.map +1 -0
- package/dist/png/format.d.ts +133 -0
- package/dist/png/format.d.ts.map +1 -0
- package/dist/png/format.js +350 -0
- package/dist/png/format.js.map +1 -0
- package/dist/png/index.d.ts +13 -0
- package/dist/png/index.d.ts.map +1 -0
- package/dist/png/index.js +10 -0
- package/dist/png/index.js.map +1 -0
- package/dist/png/repair.d.ts +31 -0
- package/dist/png/repair.d.ts.map +1 -0
- package/dist/png/repair.js +335 -0
- package/dist/png/repair.js.map +1 -0
- package/dist/sqlite/diagnose.d.ts +31 -0
- package/dist/sqlite/diagnose.d.ts.map +1 -0
- package/dist/sqlite/diagnose.js +169 -0
- package/dist/sqlite/diagnose.js.map +1 -0
- package/dist/sqlite/format.d.ts +103 -0
- package/dist/sqlite/format.d.ts.map +1 -0
- package/dist/sqlite/format.js +237 -0
- package/dist/sqlite/format.js.map +1 -0
- package/dist/sqlite/index.d.ts +24 -0
- package/dist/sqlite/index.d.ts.map +1 -0
- package/dist/sqlite/index.js +19 -0
- package/dist/sqlite/index.js.map +1 -0
- package/dist/sqlite/recover.d.ts +66 -0
- package/dist/sqlite/recover.d.ts.map +1 -0
- package/dist/sqlite/recover.js +305 -0
- package/dist/sqlite/recover.js.map +1 -0
- package/dist/sqlite/scan.d.ts +94 -0
- package/dist/sqlite/scan.d.ts.map +1 -0
- package/dist/sqlite/scan.js +356 -0
- package/dist/sqlite/scan.js.map +1 -0
- package/dist/sqlite/sqlite-host.d.ts +37 -0
- package/dist/sqlite/sqlite-host.d.ts.map +1 -0
- package/dist/sqlite/sqlite-host.js +80 -0
- package/dist/sqlite/sqlite-host.js.map +1 -0
- package/dist/sqlite/wal.d.ts +14 -0
- package/dist/sqlite/wal.d.ts.map +1 -0
- package/dist/sqlite/wal.js +79 -0
- package/dist/sqlite/wal.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/video/diagnose.d.ts +41 -0
- package/dist/video/diagnose.d.ts.map +1 -0
- package/dist/video/diagnose.js +188 -0
- package/dist/video/diagnose.js.map +1 -0
- package/dist/video/format.d.ts +87 -0
- package/dist/video/format.d.ts.map +1 -0
- package/dist/video/format.js +273 -0
- package/dist/video/format.js.map +1 -0
- package/dist/video/index.d.ts +21 -0
- package/dist/video/index.d.ts.map +1 -0
- package/dist/video/index.js +17 -0
- package/dist/video/index.js.map +1 -0
- package/dist/video/libav-host.d.ts +88 -0
- package/dist/video/libav-host.d.ts.map +1 -0
- package/dist/video/libav-host.js +66 -0
- package/dist/video/libav-host.js.map +1 -0
- package/dist/video/mp4-rebuild.d.ts +167 -0
- package/dist/video/mp4-rebuild.d.ts.map +1 -0
- package/dist/video/mp4-rebuild.js +270 -0
- package/dist/video/mp4-rebuild.js.map +1 -0
- package/dist/video/mp4box-host.d.ts +62 -0
- package/dist/video/mp4box-host.d.ts.map +1 -0
- package/dist/video/mp4box-host.js +209 -0
- package/dist/video/mp4box-host.js.map +1 -0
- package/dist/video/reconstruct.d.ts +97 -0
- package/dist/video/reconstruct.d.ts.map +1 -0
- package/dist/video/reconstruct.js +271 -0
- package/dist/video/reconstruct.js.map +1 -0
- package/dist/video/remux.d.ts +85 -0
- package/dist/video/remux.d.ts.map +1 -0
- package/dist/video/remux.js +350 -0
- package/dist/video/remux.js.map +1 -0
- package/dist/zip/diagnose.d.ts +22 -0
- package/dist/zip/diagnose.d.ts.map +1 -0
- package/dist/zip/diagnose.js +126 -0
- package/dist/zip/diagnose.js.map +1 -0
- package/dist/zip/format.d.ts +79 -0
- package/dist/zip/format.d.ts.map +1 -0
- package/dist/zip/format.js +129 -0
- package/dist/zip/format.js.map +1 -0
- package/dist/zip/repair.d.ts +32 -0
- package/dist/zip/repair.d.ts.map +1 -0
- package/dist/zip/repair.js +82 -0
- package/dist/zip/repair.js.map +1 -0
- package/dist/zip/salvage.d.ts +23 -0
- package/dist/zip/salvage.d.ts.map +1 -0
- package/dist/zip/salvage.js +140 -0
- package/dist/zip/salvage.js.map +1 -0
- package/dist/zip/scan.d.ts +64 -0
- package/dist/zip/scan.d.ts.map +1 -0
- package/dist/zip/scan.js +386 -0
- package/dist/zip/scan.js.map +1 -0
- package/dist/zip/writer.d.ts +14 -0
- package/dist/zip/writer.d.ts.map +1 -0
- package/dist/zip/writer.js +131 -0
- package/dist/zip/writer.js.map +1 -0
- package/package.json +71 -0
- package/src/archive/LICENSE-unrar.txt +53 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Video engine subpath export (`@intactfile/engines/video`). Kept out of the
|
|
3
|
+
* root export so browser bundles that never remux video never see the
|
|
4
|
+
* libav.js wasm dependency; even here, the wasm only loads on the first
|
|
5
|
+
* remuxVideo call. diagnoseVideo is pure parsing and is also re-exported from
|
|
6
|
+
* the package root.
|
|
7
|
+
*/
|
|
8
|
+
export { diagnoseVideo } from './diagnose.js';
|
|
9
|
+
export type { VideoDiagnosis, VideoDamageClass, VideoContainer, VideoBoxInfo } from './diagnose.js';
|
|
10
|
+
export { remuxVideo, planRemux, outputContainerFor, REMUX_STRATEGIES, AVFMT_FLAG_GENPTS, AVFMT_FLAG_IGNIDX, } from './remux.js';
|
|
11
|
+
export type { RemuxPlan, RemuxOptions, RemuxStrategy, RemuxStrategyId, RemuxAttempt, OutputContainer, VideoRepairOutcome, VideoRepairReport, } from './remux.js';
|
|
12
|
+
export { setLibAVLoader, resetLibAVHost, getLibAV } from './libav-host.js';
|
|
13
|
+
export type { LibAVLoader, LibAVWrapper, LibAVInstance } from './libav-host.js';
|
|
14
|
+
export { scanMp4Boxes, scanMkv, scanAvi, checkMoovIntegrity } from './format.js';
|
|
15
|
+
export type { Mp4Scan, Mp4Box, MkvScan, AviScan } from './format.js';
|
|
16
|
+
export { reconstructMoov, chooseSampleSizes } from './reconstruct.js';
|
|
17
|
+
export type { VideoReconstructReport, VideoReconstructOutcome, ReconstructOptions, ReconstructStrategy, } from './reconstruct.js';
|
|
18
|
+
export { setMp4BoxLoader, resetMp4BoxHost, getMp4Box, parseReferenceMoov, ReferenceParseError, } from './mp4box-host.js';
|
|
19
|
+
export type { Mp4BoxLoader, Mp4BoxModule } from './mp4box-host.js';
|
|
20
|
+
export type { MoovTemplate, MoovTemplateTrack } from './mp4-rebuild.js';
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/video/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpG,OAAO,EACL,UAAU,EACV,SAAS,EACT,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,SAAS,EACT,YAAY,EACZ,aAAa,EACb,eAAe,EACf,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3E,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjF,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAKrE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACtE,YAAY,EACV,sBAAsB,EACtB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,eAAe,EACf,eAAe,EACf,SAAS,EACT,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACnE,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Video engine subpath export (`@intactfile/engines/video`). Kept out of the
|
|
3
|
+
* root export so browser bundles that never remux video never see the
|
|
4
|
+
* libav.js wasm dependency; even here, the wasm only loads on the first
|
|
5
|
+
* remuxVideo call. diagnoseVideo is pure parsing and is also re-exported from
|
|
6
|
+
* the package root.
|
|
7
|
+
*/
|
|
8
|
+
export { diagnoseVideo } from './diagnose.js';
|
|
9
|
+
export { remuxVideo, planRemux, outputContainerFor, REMUX_STRATEGIES, AVFMT_FLAG_GENPTS, AVFMT_FLAG_IGNIDX, } from './remux.js';
|
|
10
|
+
export { setLibAVLoader, resetLibAVHost, getLibAV } from './libav-host.js';
|
|
11
|
+
export { scanMp4Boxes, scanMkv, scanAvi, checkMoovIntegrity } from './format.js';
|
|
12
|
+
// Untrunc-class moov reconstruction (reference-file, clean-room TS). Kept out
|
|
13
|
+
// of the pure root diagnose path; it only pulls in mp4box.js (a parser) on the
|
|
14
|
+
// first reconstructMoov call, via the injectable mp4box host.
|
|
15
|
+
export { reconstructMoov, chooseSampleSizes } from './reconstruct.js';
|
|
16
|
+
export { setMp4BoxLoader, resetMp4BoxHost, getMp4Box, parseReferenceMoov, ReferenceParseError, } from './mp4box-host.js';
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/video/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,EACL,UAAU,EACV,SAAS,EACT,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAWpB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3E,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjF,8EAA8E;AAC9E,+EAA+E;AAC/E,8DAA8D;AAC9D,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAOtE,OAAO,EACL,eAAe,EACf,eAAe,EACf,SAAS,EACT,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isolated host for libav.js (@libav.js/variant-webcodecs 6.9.8, LGPL-2.1).
|
|
3
|
+
*
|
|
4
|
+
* Variant choice: `webcodecs` is the lightest official variant that carries
|
|
5
|
+
* the demuxers+muxers we need for container repair — mov/mp4/3gp demux, mp4 +
|
|
6
|
+
* mov mux, matroska demux (mkv AND webm), matroska + webm mux, ogg/flac/wav —
|
|
7
|
+
* with NO video encoders/decoders (only parsers/bsfs for h264/hevc/vp8/vp9/
|
|
8
|
+
* av1/aac, and small LGPL-safe audio codecs: libopus/flac/pcm).
|
|
9
|
+
* wasm size: ~2.30 MB single-threaded (libav-6.9.8.1-webcodecs.wasm.wasm).
|
|
10
|
+
* NOTE: no official variant ships an AVI demuxer; AVI remux honestly fails
|
|
11
|
+
* until we ship a custom fragment build (configs fragment `demuxer-avi`).
|
|
12
|
+
*
|
|
13
|
+
* Loading is injectable, same pattern as pdf/qpdf-host.ts: Node's default
|
|
14
|
+
* loader resolves the CJS wrapper from node_modules; a browser host calls
|
|
15
|
+
* setLibAVLoader() with the wrapper module and factory options (e.g. a
|
|
16
|
+
* `base`/`wasmurl` pointing at R2) before using remuxVideo.
|
|
17
|
+
*/
|
|
18
|
+
/** The (small) slice of the libav.js instance API this package calls. */
|
|
19
|
+
export interface LibAVInstance {
|
|
20
|
+
AVERROR_EOF: number;
|
|
21
|
+
AVIO_FLAG_WRITE: number;
|
|
22
|
+
AVMEDIA_TYPE_VIDEO: number;
|
|
23
|
+
AVMEDIA_TYPE_AUDIO: number;
|
|
24
|
+
writeFile(name: string, content: Uint8Array): Promise<unknown>;
|
|
25
|
+
readFile(name: string): Promise<Uint8Array>;
|
|
26
|
+
unlink(name: string): Promise<void>;
|
|
27
|
+
avformat_open_input_js(url: string, fmt: number, options: number): Promise<number>;
|
|
28
|
+
avformat_find_stream_info(ic: number, options: number): Promise<number>;
|
|
29
|
+
avformat_close_input_js(ic: number): Promise<void>;
|
|
30
|
+
av_read_frame(ic: number, pkt: number): Promise<number>;
|
|
31
|
+
AVFormatContext_flags(ptr: number): Promise<number>;
|
|
32
|
+
AVFormatContext_flags_s(ptr: number, val: number): Promise<void>;
|
|
33
|
+
AVFormatContext_nb_streams(ptr: number): Promise<number>;
|
|
34
|
+
AVFormatContext_streams_a(ptr: number, idx: number): Promise<number>;
|
|
35
|
+
AVFormatContext_duration(ptr: number): Promise<number>;
|
|
36
|
+
AVFormatContext_durationhi(ptr: number): Promise<number>;
|
|
37
|
+
AVStream_codecpar(ptr: number): Promise<number>;
|
|
38
|
+
AVStream_time_base_num(ptr: number): Promise<number>;
|
|
39
|
+
AVStream_time_base_den(ptr: number): Promise<number>;
|
|
40
|
+
AVCodecParameters_codec_type(ptr: number): Promise<number>;
|
|
41
|
+
AVCodecParameters_codec_tag_s(ptr: number, val: number): Promise<void>;
|
|
42
|
+
avformat_alloc_output_context2_js(oformat: number, formatName: string | null, filename: string): Promise<number>;
|
|
43
|
+
avformat_new_stream(oc: number, c: number): Promise<number>;
|
|
44
|
+
avcodec_parameters_copy(dst: number, src: number): Promise<number>;
|
|
45
|
+
av_opt_set(obj: number, name: string, val: string, searchFlags: number): Promise<number>;
|
|
46
|
+
avio_open2_js(url: string, flags: number, intCb: number, options: number): Promise<number>;
|
|
47
|
+
avio_close(pb: number): Promise<number>;
|
|
48
|
+
AVFormatContext_pb_s(ptr: number, val: number): Promise<void>;
|
|
49
|
+
avformat_write_header(oc: number, options: number): Promise<number>;
|
|
50
|
+
av_interleaved_write_frame(oc: number, pkt: number): Promise<number>;
|
|
51
|
+
av_write_trailer(oc: number): Promise<number>;
|
|
52
|
+
avformat_free_context(oc: number): Promise<void>;
|
|
53
|
+
av_packet_alloc(): Promise<number>;
|
|
54
|
+
av_packet_free_js(pkt: number): Promise<void>;
|
|
55
|
+
av_packet_unref(pkt: number): Promise<void>;
|
|
56
|
+
av_packet_rescale_ts_js(pkt: number, tbSrcNum: number, tbSrcDen: number, tbDstNum: number, tbDstDen: number): Promise<void>;
|
|
57
|
+
AVPacket_stream_index(pkt: number): Promise<number>;
|
|
58
|
+
AVPacket_stream_index_s(pkt: number, val: number): Promise<void>;
|
|
59
|
+
AVPacket_flags(pkt: number): Promise<number>;
|
|
60
|
+
AVPacket_pts(pkt: number): Promise<number>;
|
|
61
|
+
AVPacket_ptshi(pkt: number): Promise<number>;
|
|
62
|
+
AVPacket_duration(pkt: number): Promise<number>;
|
|
63
|
+
AVPacket_durationhi(pkt: number): Promise<number>;
|
|
64
|
+
AVPacket_pos_s(pkt: number, val: number): Promise<void>;
|
|
65
|
+
AVPacket_poshi_s(pkt: number, val: number): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
/** A libav.js wrapper module (the thing whose `.LibAV()` creates instances). */
|
|
68
|
+
export interface LibAVWrapper {
|
|
69
|
+
LibAV(opts?: Record<string, unknown>): Promise<unknown>;
|
|
70
|
+
}
|
|
71
|
+
export interface LibAVLoader {
|
|
72
|
+
wrapper: LibAVWrapper;
|
|
73
|
+
/** Options for the factory (noworker, base, wasmurl, ...). */
|
|
74
|
+
factoryOpts?: Record<string, unknown>;
|
|
75
|
+
}
|
|
76
|
+
/** Inject how the libav.js wrapper is obtained (browser hosts). */
|
|
77
|
+
export declare function setLibAVLoader(load: () => Promise<LibAVLoader>): void;
|
|
78
|
+
/** Drop the cached instance (tests, or after a wasm crash). */
|
|
79
|
+
export declare function resetLibAVHost(): void;
|
|
80
|
+
/**
|
|
81
|
+
* The libav.js instance is a reusable library (unlike the one-shot qpdf CLI):
|
|
82
|
+
* it is created once and cached. Runs are serialized so MEMFS filenames and
|
|
83
|
+
* memory pressure never interact between concurrent repairs.
|
|
84
|
+
*/
|
|
85
|
+
export declare function getLibAV(): Promise<LibAVInstance>;
|
|
86
|
+
/** Serialized, exclusive access to the shared libav instance. */
|
|
87
|
+
export declare function withLibAV<T>(fn: (libav: LibAVInstance, runId: number) => Promise<T>): Promise<T>;
|
|
88
|
+
//# sourceMappingURL=libav-host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"libav-host.d.ts","sourceRoot":"","sources":["../../src/video/libav-host.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isolated host for libav.js (@libav.js/variant-webcodecs 6.9.8, LGPL-2.1).
|
|
3
|
+
*
|
|
4
|
+
* Variant choice: `webcodecs` is the lightest official variant that carries
|
|
5
|
+
* the demuxers+muxers we need for container repair — mov/mp4/3gp demux, mp4 +
|
|
6
|
+
* mov mux, matroska demux (mkv AND webm), matroska + webm mux, ogg/flac/wav —
|
|
7
|
+
* with NO video encoders/decoders (only parsers/bsfs for h264/hevc/vp8/vp9/
|
|
8
|
+
* av1/aac, and small LGPL-safe audio codecs: libopus/flac/pcm).
|
|
9
|
+
* wasm size: ~2.30 MB single-threaded (libav-6.9.8.1-webcodecs.wasm.wasm).
|
|
10
|
+
* NOTE: no official variant ships an AVI demuxer; AVI remux honestly fails
|
|
11
|
+
* until we ship a custom fragment build (configs fragment `demuxer-avi`).
|
|
12
|
+
*
|
|
13
|
+
* Loading is injectable, same pattern as pdf/qpdf-host.ts: Node's default
|
|
14
|
+
* loader resolves the CJS wrapper from node_modules; a browser host calls
|
|
15
|
+
* setLibAVLoader() with the wrapper module and factory options (e.g. a
|
|
16
|
+
* `base`/`wasmurl` pointing at R2) before using remuxVideo.
|
|
17
|
+
*/
|
|
18
|
+
let loaderOverride = null;
|
|
19
|
+
let instancePromise = null;
|
|
20
|
+
/** Inject how the libav.js wrapper is obtained (browser hosts). */
|
|
21
|
+
export function setLibAVLoader(load) {
|
|
22
|
+
loaderOverride = load;
|
|
23
|
+
instancePromise = null;
|
|
24
|
+
}
|
|
25
|
+
/** Drop the cached instance (tests, or after a wasm crash). */
|
|
26
|
+
export function resetLibAVHost() {
|
|
27
|
+
instancePromise = null;
|
|
28
|
+
}
|
|
29
|
+
async function defaultNodeLoader() {
|
|
30
|
+
const isNode = typeof process !== 'undefined' && !!process.versions?.node;
|
|
31
|
+
if (!isNode) {
|
|
32
|
+
throw new Error('libav host: no loader configured. In the browser, call setLibAVLoader() with the ' +
|
|
33
|
+
'@libav.js/variant-webcodecs wrapper and a base/wasm URL before remuxing video.');
|
|
34
|
+
}
|
|
35
|
+
const { createRequire } = await import('node:module');
|
|
36
|
+
const require = createRequire(import.meta.url);
|
|
37
|
+
const wrapper = require('@libav.js/variant-webcodecs');
|
|
38
|
+
return { wrapper, factoryOpts: { noworker: true } };
|
|
39
|
+
}
|
|
40
|
+
async function createInstance() {
|
|
41
|
+
const { wrapper, factoryOpts } = await (loaderOverride ?? defaultNodeLoader)();
|
|
42
|
+
const instance = await wrapper.LibAV({ noworker: true, ...factoryOpts });
|
|
43
|
+
return instance;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The libav.js instance is a reusable library (unlike the one-shot qpdf CLI):
|
|
47
|
+
* it is created once and cached. Runs are serialized so MEMFS filenames and
|
|
48
|
+
* memory pressure never interact between concurrent repairs.
|
|
49
|
+
*/
|
|
50
|
+
export function getLibAV() {
|
|
51
|
+
instancePromise ??= createInstance().catch((e) => {
|
|
52
|
+
instancePromise = null;
|
|
53
|
+
throw e;
|
|
54
|
+
});
|
|
55
|
+
return instancePromise;
|
|
56
|
+
}
|
|
57
|
+
let runChain = Promise.resolve();
|
|
58
|
+
let runCounter = 0;
|
|
59
|
+
/** Serialized, exclusive access to the shared libav instance. */
|
|
60
|
+
export function withLibAV(fn) {
|
|
61
|
+
const id = runCounter++;
|
|
62
|
+
const run = runChain.then(async () => fn(await getLibAV(), id));
|
|
63
|
+
runChain = run.catch(() => undefined);
|
|
64
|
+
return run;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=libav-host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"libav-host.js","sourceRoot":"","sources":["../../src/video/libav-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAwEH,IAAI,cAAc,GAAwC,IAAI,CAAC;AAC/D,IAAI,eAAe,GAAkC,IAAI,CAAC;AAE1D,mEAAmE;AACnE,MAAM,UAAU,cAAc,CAAC,IAAgC;IAC7D,cAAc,GAAG,IAAI,CAAC;IACtB,eAAe,GAAG,IAAI,CAAC;AACzB,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,cAAc;IAC5B,eAAe,GAAG,IAAI,CAAC;AACzB,CAAC;AAED,KAAK,UAAU,iBAAiB;IAC9B,MAAM,MAAM,GAAG,OAAO,OAAO,KAAK,WAAW,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC;IAC1E,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,mFAAmF;YACjF,gFAAgF,CACnF,CAAC;IACJ,CAAC;IACD,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,OAAO,CAAC,6BAA6B,CAAiB,CAAC;IACvE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;AACtD,CAAC;AAED,KAAK,UAAU,cAAc;IAC3B,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,cAAc,IAAI,iBAAiB,CAAC,EAAE,CAAC;IAC/E,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,WAAW,EAAE,CAAC,CAAC;IACzE,OAAO,QAAyB,CAAC;AACnC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ;IACtB,eAAe,KAAK,cAAc,EAAE,CAAC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE;QACxD,eAAe,GAAG,IAAI,CAAC;QACvB,MAAM,CAAC,CAAC;IACV,CAAC,CAAC,CAAC;IACH,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,IAAI,QAAQ,GAAqB,OAAO,CAAC,OAAO,EAAE,CAAC;AACnD,IAAI,UAAU,GAAG,CAAC,CAAC;AAEnB,iEAAiE;AACjE,MAAM,UAAU,SAAS,CAAI,EAAuD;IAClF,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;IACxB,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC,MAAM,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IAChE,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACtC,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, wasm-free primitives for untrunc-class moov reconstruction:
|
|
3
|
+
* - MP4 box byte writers (u8/u16/u32/u64, box, fullBox, cat);
|
|
4
|
+
* - sample-boundary scanning of a raw mdat payload (length-prefixed H.264/
|
|
5
|
+
* H.265 NAL units → access units, plus a standalone ADTS/AAC scanner);
|
|
6
|
+
* - a sample-table (stbl) builder and moov assembler from a reference-derived
|
|
7
|
+
* MoovTemplate.
|
|
8
|
+
*
|
|
9
|
+
* Nothing here touches mp4box.js or libav.js — the reference is parsed
|
|
10
|
+
* elsewhere (mp4box-host.ts) into a MoovTemplate of raw serialized boxes plus
|
|
11
|
+
* scalar cadence, and these functions turn a scanned sample list into a
|
|
12
|
+
* playable moov. That keeps the hard-to-test glue thin and everything below
|
|
13
|
+
* unit-testable with hand-built byte arrays.
|
|
14
|
+
*
|
|
15
|
+
* This is a clean-room reimplementation of the *idea* behind untrunc
|
|
16
|
+
* (reconstruct an index from raw media using a same-camera reference). No line
|
|
17
|
+
* of the GPL-2.0 untrunc source is copied or linked.
|
|
18
|
+
*/
|
|
19
|
+
export declare function u8(v: number): Uint8Array;
|
|
20
|
+
export declare function u16(v: number): Uint8Array;
|
|
21
|
+
export declare function u32(v: number): Uint8Array;
|
|
22
|
+
export declare function u64(v: number): Uint8Array;
|
|
23
|
+
export declare function cat(...parts: Uint8Array[]): Uint8Array;
|
|
24
|
+
/** A plain box: [size][type][...children]. */
|
|
25
|
+
export declare function box(type: string, ...children: Uint8Array[]): Uint8Array;
|
|
26
|
+
/** A FullBox: [size][type][version][flags:24][...children]. */
|
|
27
|
+
export declare function fullBox(type: string, version: number, flags: number, ...children: Uint8Array[]): Uint8Array;
|
|
28
|
+
export declare function readU32(bytes: Uint8Array, offset: number): number;
|
|
29
|
+
export type VideoNalCodec = 'avc' | 'hevc';
|
|
30
|
+
/** Map an stsd sample-entry fourcc to the NAL codec family, or null. */
|
|
31
|
+
export declare function nalCodecForFourcc(fourcc: string): VideoNalCodec | null;
|
|
32
|
+
/**
|
|
33
|
+
* Read the NAL length field size from a serialized stsd box by locating the
|
|
34
|
+
* avcC / hvcC config record. Returns 1..4, or null when neither is present.
|
|
35
|
+
*
|
|
36
|
+
* avcC: lengthSizeMinusOne lives in the 5th payload byte (low 2 bits).
|
|
37
|
+
* hvcC: lengthSizeMinusOne lives in payload byte 21 (low 2 bits).
|
|
38
|
+
*/
|
|
39
|
+
export declare function nalLengthSizeFromStsd(stsd: Uint8Array): number | null;
|
|
40
|
+
export interface NalUnit {
|
|
41
|
+
/** Offset of the length prefix, relative to the payload start. */
|
|
42
|
+
start: number;
|
|
43
|
+
/** Total bytes of this unit (length prefix + NAL body). */
|
|
44
|
+
size: number;
|
|
45
|
+
/** NAL unit type (H.264: byte&0x1f; H.265: (byte>>1)&0x3f). */
|
|
46
|
+
type: number;
|
|
47
|
+
isVcl: boolean;
|
|
48
|
+
isAud: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Walk a length-prefixed NAL stream (AVCC / HVCC in-mp4 storage). Stops at the
|
|
52
|
+
* first byte where a length prefix would read past the end (a truncated tail),
|
|
53
|
+
* or where a zero/implausible length appears (padding).
|
|
54
|
+
*/
|
|
55
|
+
export declare function scanLengthPrefixedNals(payload: Uint8Array, nalLengthSize: number, codec: VideoNalCodec): {
|
|
56
|
+
nals: NalUnit[];
|
|
57
|
+
bytesConsumed: number;
|
|
58
|
+
truncatedTail: boolean;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Group NAL units into access units (= samples). Heuristic (single-slice per
|
|
62
|
+
* picture, which covers recorder output and our corpus):
|
|
63
|
+
* - non-VCL units (SPS/PPS/VPS/SEI/AUD) attach to the *following* sample;
|
|
64
|
+
* - once a sample already contains a VCL unit, the next unit starts a new
|
|
65
|
+
* sample.
|
|
66
|
+
* Multi-slice pictures would be over-split; the caller reconciles against the
|
|
67
|
+
* reference sample count and falls back to the reference size model when they
|
|
68
|
+
* disagree.
|
|
69
|
+
*/
|
|
70
|
+
export declare function groupNalsIntoSamples(nals: readonly NalUnit[]): number[];
|
|
71
|
+
export interface SampleScan {
|
|
72
|
+
sizes: number[];
|
|
73
|
+
bytesAccounted: number;
|
|
74
|
+
leftoverBytes: number;
|
|
75
|
+
truncatedTail: boolean;
|
|
76
|
+
nalCount: number;
|
|
77
|
+
}
|
|
78
|
+
/** Full scan of a video mdat payload → per-sample sizes. */
|
|
79
|
+
export declare function scanAvcHevcSamples(payload: Uint8Array, nalLengthSize: number, codec: VideoNalCodec): SampleScan;
|
|
80
|
+
/**
|
|
81
|
+
* Standalone ADTS/AAC frame scanner. Only relevant when the raw payload is an
|
|
82
|
+
* ADTS elementary stream (some recorders); raw AAC stored inside mp4 mdat is
|
|
83
|
+
* *not* ADTS-framed and must instead be sized from the reference stsz. Provided
|
|
84
|
+
* for completeness and unit-tested independently.
|
|
85
|
+
*/
|
|
86
|
+
export declare function scanAdtsFrames(payload: Uint8Array): {
|
|
87
|
+
sizes: number[];
|
|
88
|
+
bytesAccounted: number;
|
|
89
|
+
leftoverBytes: number;
|
|
90
|
+
};
|
|
91
|
+
export interface MoovTemplateTrack {
|
|
92
|
+
trackId: number;
|
|
93
|
+
/** 'vide' | 'soun' | ... (from hdlr). */
|
|
94
|
+
handlerType: string;
|
|
95
|
+
/** stsd first sample-entry fourcc, e.g. 'avc1'. */
|
|
96
|
+
codecFourcc: string;
|
|
97
|
+
/** Full codec string when available, e.g. 'avc1.42c01e'. */
|
|
98
|
+
codec: string | null;
|
|
99
|
+
mediaTimescale: number;
|
|
100
|
+
/** stts entries from the reference (count/delta pairs). */
|
|
101
|
+
sttsEntries: {
|
|
102
|
+
count: number;
|
|
103
|
+
delta: number;
|
|
104
|
+
}[];
|
|
105
|
+
/** Reference sample count (stsz.sample_count). */
|
|
106
|
+
sampleCount: number;
|
|
107
|
+
/** stsz.sample_size (0 ⇒ per-sample table). */
|
|
108
|
+
constantSampleSize: number;
|
|
109
|
+
/** Reference per-sample size table (empty when constant). */
|
|
110
|
+
sampleSizes: number[];
|
|
111
|
+
/** stss present in the reference (sparse sync samples). */
|
|
112
|
+
hasStss: boolean;
|
|
113
|
+
syncSamples: number[];
|
|
114
|
+
/** NAL length field size (avc/hevc) or null. */
|
|
115
|
+
nalLengthSize: number | null;
|
|
116
|
+
tkhd: Uint8Array;
|
|
117
|
+
mdhd: Uint8Array;
|
|
118
|
+
hdlr: Uint8Array;
|
|
119
|
+
/** minf media header: vmhd / smhd / etc. */
|
|
120
|
+
minfHeader: Uint8Array;
|
|
121
|
+
dinf: Uint8Array;
|
|
122
|
+
stsd: Uint8Array;
|
|
123
|
+
}
|
|
124
|
+
export interface MoovTemplate {
|
|
125
|
+
movieTimescale: number;
|
|
126
|
+
movieDuration: number;
|
|
127
|
+
/** Raw serialized mvhd, reused verbatim. */
|
|
128
|
+
mvhd: Uint8Array;
|
|
129
|
+
tracks: MoovTemplateTrack[];
|
|
130
|
+
}
|
|
131
|
+
export interface StblParams {
|
|
132
|
+
/** Verbatim stsd box from the reference (codec config). */
|
|
133
|
+
stsd: Uint8Array;
|
|
134
|
+
/** Per-sample byte sizes, in decode order. */
|
|
135
|
+
sampleSizes: number[];
|
|
136
|
+
/** Time-to-sample entries (count/delta pairs). */
|
|
137
|
+
stts: {
|
|
138
|
+
count: number;
|
|
139
|
+
delta: number;
|
|
140
|
+
}[];
|
|
141
|
+
/** Absolute file offset of the first sample (mdat payload start). */
|
|
142
|
+
chunkOffset: number;
|
|
143
|
+
/** 1-based sync sample numbers, or null to omit stss (⇒ every sample syncs). */
|
|
144
|
+
syncSamples: number[] | null;
|
|
145
|
+
}
|
|
146
|
+
/** Build a complete stbl box (stsd, stts, stsc, stsz, stco/co64[, stss]). */
|
|
147
|
+
export declare function buildStbl(p: StblParams): Uint8Array;
|
|
148
|
+
/** Assemble a single-track moov: moov(mvhd, trak(tkhd, mdia(mdhd, hdlr, minf(header, dinf, stbl)))). */
|
|
149
|
+
export declare function assembleMoov(template: MoovTemplate, track: MoovTemplateTrack, stbl: Uint8Array): Uint8Array;
|
|
150
|
+
/** Minimal ftyp used only when the broken file has none. */
|
|
151
|
+
export declare function defaultFtyp(): Uint8Array;
|
|
152
|
+
/**
|
|
153
|
+
* Splice a faststart MP4: ftyp + moov + mdat(payload). The moov is built twice
|
|
154
|
+
* so the stco chunk offset can point at the final mdat payload location; the
|
|
155
|
+
* moov length is independent of the offset *value*, so the two builds are the
|
|
156
|
+
* same size and the offset is stable.
|
|
157
|
+
*/
|
|
158
|
+
export declare function spliceFaststartMp4(template: MoovTemplate, track: MoovTemplateTrack, params: {
|
|
159
|
+
stsd: Uint8Array;
|
|
160
|
+
sampleSizes: number[];
|
|
161
|
+
stts: {
|
|
162
|
+
count: number;
|
|
163
|
+
delta: number;
|
|
164
|
+
}[];
|
|
165
|
+
syncSamples: number[] | null;
|
|
166
|
+
}, ftyp: Uint8Array, payload: Uint8Array): Uint8Array;
|
|
167
|
+
//# sourceMappingURL=mp4-rebuild.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mp4-rebuild.d.ts","sourceRoot":"","sources":["../../src/video/mp4-rebuild.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, wasm-free primitives for untrunc-class moov reconstruction:
|
|
3
|
+
* - MP4 box byte writers (u8/u16/u32/u64, box, fullBox, cat);
|
|
4
|
+
* - sample-boundary scanning of a raw mdat payload (length-prefixed H.264/
|
|
5
|
+
* H.265 NAL units → access units, plus a standalone ADTS/AAC scanner);
|
|
6
|
+
* - a sample-table (stbl) builder and moov assembler from a reference-derived
|
|
7
|
+
* MoovTemplate.
|
|
8
|
+
*
|
|
9
|
+
* Nothing here touches mp4box.js or libav.js — the reference is parsed
|
|
10
|
+
* elsewhere (mp4box-host.ts) into a MoovTemplate of raw serialized boxes plus
|
|
11
|
+
* scalar cadence, and these functions turn a scanned sample list into a
|
|
12
|
+
* playable moov. That keeps the hard-to-test glue thin and everything below
|
|
13
|
+
* unit-testable with hand-built byte arrays.
|
|
14
|
+
*
|
|
15
|
+
* This is a clean-room reimplementation of the *idea* behind untrunc
|
|
16
|
+
* (reconstruct an index from raw media using a same-camera reference). No line
|
|
17
|
+
* of the GPL-2.0 untrunc source is copied or linked.
|
|
18
|
+
*/
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Byte writers (mirrors the corpus generator's box helpers, kept independent)
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
const te = new TextEncoder();
|
|
23
|
+
export function u8(v) {
|
|
24
|
+
return new Uint8Array([v & 0xff]);
|
|
25
|
+
}
|
|
26
|
+
export function u16(v) {
|
|
27
|
+
return new Uint8Array([(v >>> 8) & 0xff, v & 0xff]);
|
|
28
|
+
}
|
|
29
|
+
export function u32(v) {
|
|
30
|
+
return new Uint8Array([(v >>> 24) & 0xff, (v >>> 16) & 0xff, (v >>> 8) & 0xff, v & 0xff]);
|
|
31
|
+
}
|
|
32
|
+
export function u64(v) {
|
|
33
|
+
const out = new Uint8Array(8);
|
|
34
|
+
out.set(u32(Math.floor(v / 0x1_0000_0000)), 0);
|
|
35
|
+
out.set(u32(v % 0x1_0000_0000), 4);
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
export function cat(...parts) {
|
|
39
|
+
let total = 0;
|
|
40
|
+
for (const p of parts)
|
|
41
|
+
total += p.length;
|
|
42
|
+
const out = new Uint8Array(total);
|
|
43
|
+
let pos = 0;
|
|
44
|
+
for (const p of parts) {
|
|
45
|
+
out.set(p, pos);
|
|
46
|
+
pos += p.length;
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
/** A plain box: [size][type][...children]. */
|
|
51
|
+
export function box(type, ...children) {
|
|
52
|
+
const body = cat(...children);
|
|
53
|
+
return cat(u32(body.length + 8), te.encode(type), body);
|
|
54
|
+
}
|
|
55
|
+
/** A FullBox: [size][type][version][flags:24][...children]. */
|
|
56
|
+
export function fullBox(type, version, flags, ...children) {
|
|
57
|
+
return box(type, new Uint8Array([version & 0xff, (flags >>> 16) & 0xff, (flags >>> 8) & 0xff, flags & 0xff]), ...children);
|
|
58
|
+
}
|
|
59
|
+
export function readU32(bytes, offset) {
|
|
60
|
+
return ((bytes[offset] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3]) >>> 0;
|
|
61
|
+
}
|
|
62
|
+
/** Map an stsd sample-entry fourcc to the NAL codec family, or null. */
|
|
63
|
+
export function nalCodecForFourcc(fourcc) {
|
|
64
|
+
if (fourcc === 'avc1' || fourcc === 'avc3')
|
|
65
|
+
return 'avc';
|
|
66
|
+
if (fourcc === 'hvc1' || fourcc === 'hev1')
|
|
67
|
+
return 'hevc';
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Read the NAL length field size from a serialized stsd box by locating the
|
|
72
|
+
* avcC / hvcC config record. Returns 1..4, or null when neither is present.
|
|
73
|
+
*
|
|
74
|
+
* avcC: lengthSizeMinusOne lives in the 5th payload byte (low 2 bits).
|
|
75
|
+
* hvcC: lengthSizeMinusOne lives in payload byte 21 (low 2 bits).
|
|
76
|
+
*/
|
|
77
|
+
export function nalLengthSizeFromStsd(stsd) {
|
|
78
|
+
const avcC = indexOfAscii(stsd, 'avcC');
|
|
79
|
+
if (avcC >= 0) {
|
|
80
|
+
const b = stsd[avcC + 4 + 4];
|
|
81
|
+
if (b !== undefined)
|
|
82
|
+
return (b & 0x03) + 1;
|
|
83
|
+
}
|
|
84
|
+
const hvcC = indexOfAscii(stsd, 'hvcC');
|
|
85
|
+
if (hvcC >= 0) {
|
|
86
|
+
const b = stsd[hvcC + 4 + 21];
|
|
87
|
+
if (b !== undefined)
|
|
88
|
+
return (b & 0x03) + 1;
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
function indexOfAscii(bytes, needle) {
|
|
93
|
+
const n = te.encode(needle);
|
|
94
|
+
outer: for (let i = 0; i + n.length <= bytes.length; i += 1) {
|
|
95
|
+
for (let j = 0; j < n.length; j += 1) {
|
|
96
|
+
if (bytes[i + j] !== n[j])
|
|
97
|
+
continue outer;
|
|
98
|
+
}
|
|
99
|
+
return i;
|
|
100
|
+
}
|
|
101
|
+
return -1;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Walk a length-prefixed NAL stream (AVCC / HVCC in-mp4 storage). Stops at the
|
|
105
|
+
* first byte where a length prefix would read past the end (a truncated tail),
|
|
106
|
+
* or where a zero/implausible length appears (padding).
|
|
107
|
+
*/
|
|
108
|
+
export function scanLengthPrefixedNals(payload, nalLengthSize, codec) {
|
|
109
|
+
const nals = [];
|
|
110
|
+
const end = payload.length;
|
|
111
|
+
let p = 0;
|
|
112
|
+
let truncatedTail = false;
|
|
113
|
+
while (p + nalLengthSize <= end) {
|
|
114
|
+
let len = 0;
|
|
115
|
+
for (let i = 0; i < nalLengthSize; i += 1)
|
|
116
|
+
len = len * 256 + payload[p + i];
|
|
117
|
+
if (len <= 0)
|
|
118
|
+
break; // padding / invalid: stop cleanly
|
|
119
|
+
const bodyStart = p + nalLengthSize;
|
|
120
|
+
const unitEnd = bodyStart + len;
|
|
121
|
+
if (unitEnd > end) {
|
|
122
|
+
truncatedTail = true; // a real unit is cut short
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
const header = payload[bodyStart];
|
|
126
|
+
const secondByte = payload[bodyStart + 1] ?? 0;
|
|
127
|
+
const info = classifyNal(header, secondByte, codec);
|
|
128
|
+
nals.push({ start: p, size: nalLengthSize + len, type: info.type, isVcl: info.isVcl, isAud: info.isAud });
|
|
129
|
+
p = unitEnd;
|
|
130
|
+
}
|
|
131
|
+
return { nals, bytesConsumed: p, truncatedTail };
|
|
132
|
+
}
|
|
133
|
+
function classifyNal(header, _second, codec) {
|
|
134
|
+
if (codec === 'avc') {
|
|
135
|
+
const type = header & 0x1f;
|
|
136
|
+
return { type, isVcl: type >= 1 && type <= 5, isAud: type === 9 };
|
|
137
|
+
}
|
|
138
|
+
// HEVC: forbidden_zero_bit(1) | nal_unit_type(6) | layer_id | tid
|
|
139
|
+
const type = (header >> 1) & 0x3f;
|
|
140
|
+
return { type, isVcl: type <= 31, isAud: type === 35 };
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Group NAL units into access units (= samples). Heuristic (single-slice per
|
|
144
|
+
* picture, which covers recorder output and our corpus):
|
|
145
|
+
* - non-VCL units (SPS/PPS/VPS/SEI/AUD) attach to the *following* sample;
|
|
146
|
+
* - once a sample already contains a VCL unit, the next unit starts a new
|
|
147
|
+
* sample.
|
|
148
|
+
* Multi-slice pictures would be over-split; the caller reconciles against the
|
|
149
|
+
* reference sample count and falls back to the reference size model when they
|
|
150
|
+
* disagree.
|
|
151
|
+
*/
|
|
152
|
+
export function groupNalsIntoSamples(nals) {
|
|
153
|
+
const sizes = [];
|
|
154
|
+
let curSize = 0;
|
|
155
|
+
let curHasVcl = false;
|
|
156
|
+
for (const nal of nals) {
|
|
157
|
+
const startNew = curSize > 0 && (curHasVcl || nal.isAud);
|
|
158
|
+
if (startNew) {
|
|
159
|
+
sizes.push(curSize);
|
|
160
|
+
curSize = 0;
|
|
161
|
+
curHasVcl = false;
|
|
162
|
+
}
|
|
163
|
+
curSize += nal.size;
|
|
164
|
+
if (nal.isVcl)
|
|
165
|
+
curHasVcl = true;
|
|
166
|
+
}
|
|
167
|
+
if (curSize > 0)
|
|
168
|
+
sizes.push(curSize);
|
|
169
|
+
return sizes;
|
|
170
|
+
}
|
|
171
|
+
/** Full scan of a video mdat payload → per-sample sizes. */
|
|
172
|
+
export function scanAvcHevcSamples(payload, nalLengthSize, codec) {
|
|
173
|
+
const { nals, bytesConsumed, truncatedTail } = scanLengthPrefixedNals(payload, nalLengthSize, codec);
|
|
174
|
+
const sizes = groupNalsIntoSamples(nals);
|
|
175
|
+
const bytesAccounted = sizes.reduce((n, s) => n + s, 0);
|
|
176
|
+
return {
|
|
177
|
+
sizes,
|
|
178
|
+
bytesAccounted,
|
|
179
|
+
leftoverBytes: payload.length - bytesConsumed,
|
|
180
|
+
truncatedTail,
|
|
181
|
+
nalCount: nals.length,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Standalone ADTS/AAC frame scanner. Only relevant when the raw payload is an
|
|
186
|
+
* ADTS elementary stream (some recorders); raw AAC stored inside mp4 mdat is
|
|
187
|
+
* *not* ADTS-framed and must instead be sized from the reference stsz. Provided
|
|
188
|
+
* for completeness and unit-tested independently.
|
|
189
|
+
*/
|
|
190
|
+
export function scanAdtsFrames(payload) {
|
|
191
|
+
const sizes = [];
|
|
192
|
+
const end = payload.length;
|
|
193
|
+
let p = 0;
|
|
194
|
+
while (p + 7 <= end) {
|
|
195
|
+
// syncword: 0xFFF (12 bits)
|
|
196
|
+
if (payload[p] !== 0xff || (payload[p + 1] & 0xf0) !== 0xf0)
|
|
197
|
+
break;
|
|
198
|
+
// aac_frame_length: 13 bits spanning bytes 3..5
|
|
199
|
+
const len = ((payload[p + 3] & 0x03) << 11) | (payload[p + 4] << 3) | ((payload[p + 5] & 0xe0) >> 5);
|
|
200
|
+
if (len < 7 || p + len > end)
|
|
201
|
+
break;
|
|
202
|
+
sizes.push(len);
|
|
203
|
+
p += len;
|
|
204
|
+
}
|
|
205
|
+
const bytesAccounted = sizes.reduce((n, s) => n + s, 0);
|
|
206
|
+
return { sizes, bytesAccounted, leftoverBytes: end - p };
|
|
207
|
+
}
|
|
208
|
+
/** Build a complete stbl box (stsd, stts, stsc, stsz, stco/co64[, stss]). */
|
|
209
|
+
export function buildStbl(p) {
|
|
210
|
+
const n = p.sampleSizes.length;
|
|
211
|
+
const sttsEntries = p.stts.length > 0 ? p.stts : [{ count: n, delta: 0 }];
|
|
212
|
+
const sttsBody = [u32(sttsEntries.length)];
|
|
213
|
+
for (const e of sttsEntries)
|
|
214
|
+
sttsBody.push(u32(e.count), u32(e.delta));
|
|
215
|
+
const stts = fullBox('stts', 0, 0, ...sttsBody);
|
|
216
|
+
// One chunk holding every sample: first_chunk=1, samples_per_chunk=n, sdi=1.
|
|
217
|
+
const stsc = fullBox('stsc', 0, 0, u32(1), u32(1), u32(n), u32(1));
|
|
218
|
+
const allEqual = n > 0 && p.sampleSizes.every((s) => s === p.sampleSizes[0]);
|
|
219
|
+
let stsz;
|
|
220
|
+
if (allEqual) {
|
|
221
|
+
stsz = fullBox('stsz', 0, 0, u32(p.sampleSizes[0]), u32(n));
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
const sizes = p.sampleSizes.map((s) => u32(s));
|
|
225
|
+
stsz = fullBox('stsz', 0, 0, u32(0), u32(n), ...sizes);
|
|
226
|
+
}
|
|
227
|
+
let stco;
|
|
228
|
+
if (p.chunkOffset > 0xffffffff) {
|
|
229
|
+
stco = fullBox('co64', 0, 0, u32(1), u64(p.chunkOffset));
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
stco = fullBox('stco', 0, 0, u32(1), u32(p.chunkOffset));
|
|
233
|
+
}
|
|
234
|
+
const children = [p.stsd, stts, stsc, stsz, stco];
|
|
235
|
+
if (p.syncSamples && p.syncSamples.length > 0) {
|
|
236
|
+
const body = [u32(p.syncSamples.length)];
|
|
237
|
+
for (const s of p.syncSamples)
|
|
238
|
+
body.push(u32(s));
|
|
239
|
+
children.push(fullBox('stss', 0, 0, ...body));
|
|
240
|
+
}
|
|
241
|
+
return box('stbl', ...children);
|
|
242
|
+
}
|
|
243
|
+
/** Assemble a single-track moov: moov(mvhd, trak(tkhd, mdia(mdhd, hdlr, minf(header, dinf, stbl)))). */
|
|
244
|
+
export function assembleMoov(template, track, stbl) {
|
|
245
|
+
const minf = box('minf', track.minfHeader, track.dinf, stbl);
|
|
246
|
+
const mdia = box('mdia', track.mdhd, track.hdlr, minf);
|
|
247
|
+
const trak = box('trak', track.tkhd, mdia);
|
|
248
|
+
return box('moov', template.mvhd, trak);
|
|
249
|
+
}
|
|
250
|
+
/** Minimal ftyp used only when the broken file has none. */
|
|
251
|
+
export function defaultFtyp() {
|
|
252
|
+
return box('ftyp', te.encode('isom'), u32(512), te.encode('isomiso2avc1mp41'));
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Splice a faststart MP4: ftyp + moov + mdat(payload). The moov is built twice
|
|
256
|
+
* so the stco chunk offset can point at the final mdat payload location; the
|
|
257
|
+
* moov length is independent of the offset *value*, so the two builds are the
|
|
258
|
+
* same size and the offset is stable.
|
|
259
|
+
*/
|
|
260
|
+
export function spliceFaststartMp4(template, track, params, ftyp, payload) {
|
|
261
|
+
const MDAT_HEADER = 8;
|
|
262
|
+
const probe = buildStbl({ ...params, chunkOffset: 0 });
|
|
263
|
+
const probeMoov = assembleMoov(template, track, probe);
|
|
264
|
+
const chunkOffset = ftyp.length + probeMoov.length + MDAT_HEADER;
|
|
265
|
+
const stbl = buildStbl({ ...params, chunkOffset });
|
|
266
|
+
const moov = assembleMoov(template, track, stbl);
|
|
267
|
+
const mdat = box('mdat', payload);
|
|
268
|
+
return cat(ftyp, moov, mdat);
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=mp4-rebuild.js.map
|