@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,271 @@
|
|
|
1
|
+
import { MemorySource } from '../byte-source.js';
|
|
2
|
+
import { diagnoseVideo } from './diagnose.js';
|
|
3
|
+
import { scanMp4Boxes } from './format.js';
|
|
4
|
+
import { defaultFtyp, nalCodecForFourcc, scanAvcHevcSamples, spliceFaststartMp4, } from './mp4-rebuild.js';
|
|
5
|
+
import { getMp4Box, parseReferenceMoov, ReferenceParseError, setMp4BoxLoader, resetMp4BoxHost, } from './mp4box-host.js';
|
|
6
|
+
export { setMp4BoxLoader, resetMp4BoxHost, getMp4Box, parseReferenceMoov, ReferenceParseError };
|
|
7
|
+
const MP4_CONTAINERS = new Set(['mp4', 'mov']);
|
|
8
|
+
/**
|
|
9
|
+
* Reconstruct a missing moov for `broken` using `reference` as a template.
|
|
10
|
+
* Returns the rebuilt file plus an honest report. On any failure the returned
|
|
11
|
+
* `video` is empty (0 bytes) — never fabricated.
|
|
12
|
+
*/
|
|
13
|
+
export async function reconstructMoov(broken, reference, opts = {}) {
|
|
14
|
+
const notes = [];
|
|
15
|
+
const diagnosis = opts.diagnosis ?? (await diagnoseVideo(broken));
|
|
16
|
+
const base = {
|
|
17
|
+
damageClass: diagnosis.damageClass,
|
|
18
|
+
container: diagnosis.container,
|
|
19
|
+
bytesIn: broken.size,
|
|
20
|
+
};
|
|
21
|
+
const fail = (reason, extra = {}) => {
|
|
22
|
+
notes.push(reason);
|
|
23
|
+
return {
|
|
24
|
+
video: new Uint8Array(0),
|
|
25
|
+
report: {
|
|
26
|
+
outcome: 'failed',
|
|
27
|
+
...base,
|
|
28
|
+
confidence: extra.confidence ?? 'high',
|
|
29
|
+
reason,
|
|
30
|
+
usedReference: extra.usedReference ?? false,
|
|
31
|
+
referenceTracks: extra.referenceTracks ?? 0,
|
|
32
|
+
referenceCodec: extra.referenceCodec ?? null,
|
|
33
|
+
strategy: extra.strategy ?? null,
|
|
34
|
+
samplesRecovered: extra.samplesRecovered ?? 0,
|
|
35
|
+
mdatPayloadBytes: extra.mdatPayloadBytes ?? 0,
|
|
36
|
+
bytesAccounted: extra.bytesAccounted ?? 0,
|
|
37
|
+
leftoverBytes: extra.leftoverBytes ?? 0,
|
|
38
|
+
truncatedTail: extra.truncatedTail ?? false,
|
|
39
|
+
sampleCountMismatch: extra.sampleCountMismatch ?? false,
|
|
40
|
+
reDiagnosis: extra.reDiagnosis ?? null,
|
|
41
|
+
notes,
|
|
42
|
+
bytesIn: broken.size,
|
|
43
|
+
bytesOut: 0,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
// --- 1. The broken file must be a missing-moov MP4/MOV with an mdat. ------
|
|
48
|
+
if (!MP4_CONTAINERS.has(diagnosis.container)) {
|
|
49
|
+
return fail(`moov reconstruction only applies to MP4/MOV; input diagnosed as "${diagnosis.container}". ` +
|
|
50
|
+
'MKV/WebM/AVI indexes are rebuilt by the remux engine instead.');
|
|
51
|
+
}
|
|
52
|
+
const brokenScan = await scanMp4Boxes(broken, diagnosis.dataOffset);
|
|
53
|
+
if (brokenScan.hasMoov) {
|
|
54
|
+
return fail('the broken file already has a moov: this is a remux/broken-index case, not moov ' +
|
|
55
|
+
'reconstruction. Use remuxVideo instead.');
|
|
56
|
+
}
|
|
57
|
+
const mdat = brokenScan.boxes.find((b) => b.type === 'mdat');
|
|
58
|
+
if (!mdat) {
|
|
59
|
+
return fail('no mdat box in the broken file: there is no media payload to reconstruct an index from.');
|
|
60
|
+
}
|
|
61
|
+
// --- 2. Parse the reference moov (mp4box.js) into a template. -------------
|
|
62
|
+
if (opts.mp4boxLoader) {
|
|
63
|
+
// setMp4BoxLoader also clears the cached module, so the override takes hold.
|
|
64
|
+
setMp4BoxLoader(opts.mp4boxLoader);
|
|
65
|
+
}
|
|
66
|
+
if (reference.size < 8) {
|
|
67
|
+
return fail('no reference provided: reconstruction needs a healthy same-camera reference clip.');
|
|
68
|
+
}
|
|
69
|
+
let template;
|
|
70
|
+
try {
|
|
71
|
+
const refBytes = await reference.read(0, reference.size);
|
|
72
|
+
template = await parseReferenceMoov(refBytes);
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
if (e instanceof ReferenceParseError) {
|
|
76
|
+
return fail(e.message, { usedReference: false });
|
|
77
|
+
}
|
|
78
|
+
return fail(`mp4box could not load/parse the reference: ${e instanceof Error ? e.message : String(e)}`, { usedReference: false });
|
|
79
|
+
}
|
|
80
|
+
const referenceCodec = template.tracks[0]?.codec ?? template.tracks[0]?.codecFourcc ?? null;
|
|
81
|
+
const refInfo = { usedReference: true, referenceTracks: template.tracks.length, referenceCodec };
|
|
82
|
+
// --- 3. Scope: single-track reconstruction (timebox). --------------------
|
|
83
|
+
if (template.tracks.length !== 1) {
|
|
84
|
+
return fail(`reference has ${template.tracks.length} tracks; multi-track reconstruction is not yet supported. ` +
|
|
85
|
+
'Interleaved video+audio needs mdat de-interleaving from the reference chunk cadence (see TODO).', refInfo);
|
|
86
|
+
}
|
|
87
|
+
const track = template.tracks[0];
|
|
88
|
+
const nalCodec = nalCodecForFourcc(track.codecFourcc);
|
|
89
|
+
// --- 4. Read the mdat payload and scan for sample boundaries. ------------
|
|
90
|
+
const headerSize = mdat.largesize ? 16 : 8;
|
|
91
|
+
const payloadStart = mdat.offset + headerSize;
|
|
92
|
+
const declaredEnd = mdat.offset + mdat.size;
|
|
93
|
+
const payloadEnd = Math.min(declaredEnd > mdat.offset ? declaredEnd : broken.size, broken.size);
|
|
94
|
+
const payload = await broken.read(payloadStart, Math.max(0, payloadEnd - payloadStart));
|
|
95
|
+
if (payload.length === 0) {
|
|
96
|
+
return fail('the mdat payload is empty; nothing to reconstruct.', refInfo);
|
|
97
|
+
}
|
|
98
|
+
let scan = null;
|
|
99
|
+
if (track.handlerType === 'vide' && nalCodec && track.nalLengthSize) {
|
|
100
|
+
scan = scanAvcHevcSamples(payload, track.nalLengthSize, nalCodec);
|
|
101
|
+
}
|
|
102
|
+
const chosen = chooseSampleSizes(scan, track, payload.length);
|
|
103
|
+
if (!chosen) {
|
|
104
|
+
return fail(track.handlerType === 'vide'
|
|
105
|
+
? 'could not determine sample boundaries from the mdat: the NAL scan did not tile the payload ' +
|
|
106
|
+
'and the reference size model does not fit either (likely a codec/reference mismatch).'
|
|
107
|
+
: `single-track reconstruction of a "${track.handlerType}" track needs the reference sample-size ` +
|
|
108
|
+
'model to tile the payload exactly; it does not (raw audio in mdat has no self-delimiting frames).', { ...refInfo, strategy: scan ? 'nal-scan' : null, mdatPayloadBytes: payload.length });
|
|
109
|
+
}
|
|
110
|
+
const bytesAccounted = chosen.sizes.reduce((n, s) => n + s, 0);
|
|
111
|
+
const leftoverBytes = payload.length - bytesAccounted;
|
|
112
|
+
const sampleCountMismatch = track.sampleCount > 0 && chosen.sizes.length !== track.sampleCount;
|
|
113
|
+
if (chosen.strategy === 'nal-scan') {
|
|
114
|
+
notes.push(`recovered ${chosen.sizes.length} sample(s) by scanning length-prefixed ${nalCodec} NAL units`);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
notes.push(`recovered ${chosen.sizes.length} sample(s) using the reference size model ` +
|
|
118
|
+
'(sample boundaries taken from the reference, not byte-verified against the payload)');
|
|
119
|
+
}
|
|
120
|
+
if (chosen.truncated)
|
|
121
|
+
notes.push('the mdat payload is truncated: the final sample was cut and dropped');
|
|
122
|
+
if (leftoverBytes > 0)
|
|
123
|
+
notes.push(`${leftoverBytes} trailing byte(s) did not form a sample and were dropped`);
|
|
124
|
+
if (sampleCountMismatch) {
|
|
125
|
+
notes.push(`recovered ${chosen.sizes.length} samples vs ${track.sampleCount} in the reference; ` +
|
|
126
|
+
'reused reference durations may be approximate');
|
|
127
|
+
}
|
|
128
|
+
// --- 5. Build the moov and splice a faststart MP4. -----------------------
|
|
129
|
+
const ftyp = await readFtyp(broken, brokenScan);
|
|
130
|
+
const stts = buildStts(track, chosen.sizes.length);
|
|
131
|
+
const output = spliceFaststartMp4(template, track, {
|
|
132
|
+
stsd: track.stsd,
|
|
133
|
+
sampleSizes: chosen.sizes,
|
|
134
|
+
stts,
|
|
135
|
+
syncSamples: track.hasStss && !sampleCountMismatch ? track.syncSamples : null,
|
|
136
|
+
}, ftyp, payload.subarray(0, bytesAccounted));
|
|
137
|
+
// --- 6. Verify: the rebuilt file must re-parse and NOT be missing-moov. ---
|
|
138
|
+
const verify = await diagnoseVideo(new MemorySource(output));
|
|
139
|
+
notes.push(...diagnosis.notes.map((n) => `input: ${n}`));
|
|
140
|
+
const reportBody = {
|
|
141
|
+
...base,
|
|
142
|
+
reason: null,
|
|
143
|
+
usedReference: true,
|
|
144
|
+
referenceTracks: template.tracks.length,
|
|
145
|
+
referenceCodec,
|
|
146
|
+
strategy: chosen.strategy,
|
|
147
|
+
samplesRecovered: chosen.sizes.length,
|
|
148
|
+
mdatPayloadBytes: payload.length,
|
|
149
|
+
bytesAccounted,
|
|
150
|
+
leftoverBytes,
|
|
151
|
+
truncatedTail: chosen.truncated,
|
|
152
|
+
sampleCountMismatch,
|
|
153
|
+
reDiagnosis: verify.damageClass,
|
|
154
|
+
notes,
|
|
155
|
+
bytesIn: broken.size,
|
|
156
|
+
bytesOut: output.length,
|
|
157
|
+
};
|
|
158
|
+
if (verify.damageClass === 'missing-moov' || verify.container === 'unknown') {
|
|
159
|
+
return {
|
|
160
|
+
video: new Uint8Array(0),
|
|
161
|
+
report: {
|
|
162
|
+
...reportBody,
|
|
163
|
+
outcome: 'failed',
|
|
164
|
+
confidence: 'high',
|
|
165
|
+
bytesOut: 0,
|
|
166
|
+
reason: `verification failed: rebuilt output re-diagnosed as "${verify.damageClass}"; not claiming success`,
|
|
167
|
+
notes: [...notes, `verification failed: rebuilt output re-diagnosed as "${verify.damageClass}"`],
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
if (verify.damageClass !== 'none') {
|
|
172
|
+
// The moov is present (not missing-moov) but the container still has a
|
|
173
|
+
// caveat (e.g. broken-index/truncated-mdat). Honest partial, not success.
|
|
174
|
+
notes.push(`rebuilt output re-diagnoses as "${verify.damageClass}" — index present but imperfect`);
|
|
175
|
+
return {
|
|
176
|
+
video: output,
|
|
177
|
+
report: {
|
|
178
|
+
...reportBody,
|
|
179
|
+
outcome: 'partial',
|
|
180
|
+
confidence: 'medium',
|
|
181
|
+
reason: `index rebuilt; output still re-diagnoses as "${verify.damageClass}"`,
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
// Re-diagnoses fully clean.
|
|
186
|
+
const clean = !chosen.truncated && leftoverBytes === 0 && !sampleCountMismatch && chosen.strategy === 'nal-scan';
|
|
187
|
+
if (clean) {
|
|
188
|
+
notes.push('rebuilt output re-diagnoses as clean (none)');
|
|
189
|
+
return { video: output, report: { ...reportBody, outcome: 'success', confidence: 'high' } };
|
|
190
|
+
}
|
|
191
|
+
notes.push('rebuilt output re-diagnoses as clean, but with recovery caveats (see notes): honest partial');
|
|
192
|
+
let partialReason;
|
|
193
|
+
if (chosen.truncated)
|
|
194
|
+
partialReason = 'mdat truncated: recovered the samples before the cut; the rest is physically gone';
|
|
195
|
+
else if (leftoverBytes > 0)
|
|
196
|
+
partialReason = `${leftoverBytes} trailing byte(s) could not be assigned to a sample and were dropped`;
|
|
197
|
+
else if (chosen.strategy === 'reference-size-guided')
|
|
198
|
+
partialReason = 'sample boundaries taken from the reference size model (not byte-verified)';
|
|
199
|
+
else if (sampleCountMismatch)
|
|
200
|
+
partialReason = `recovered ${chosen.sizes.length} samples vs ${track.sampleCount} in the reference`;
|
|
201
|
+
else
|
|
202
|
+
partialReason = 'partial recovery';
|
|
203
|
+
return {
|
|
204
|
+
video: output,
|
|
205
|
+
report: { ...reportBody, outcome: 'partial', confidence: 'medium', reason: partialReason },
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Reconcile the NAL scan with the reference size model:
|
|
210
|
+
* - a scan that tiles the payload exactly wins (byte-accurate);
|
|
211
|
+
* - else a reference model that tiles the payload exactly is used;
|
|
212
|
+
* - else a partial scan is used as best-effort (with dropped tail);
|
|
213
|
+
* - else give up (null → honest failure).
|
|
214
|
+
*/
|
|
215
|
+
export function chooseSampleSizes(scan, track, payloadLen) {
|
|
216
|
+
if (scan && scan.sizes.length > 0 && scan.bytesAccounted === payloadLen && !scan.truncatedTail) {
|
|
217
|
+
return { sizes: scan.sizes, strategy: 'nal-scan', truncated: false };
|
|
218
|
+
}
|
|
219
|
+
const refModel = referenceSizeModel(track, payloadLen);
|
|
220
|
+
if (refModel) {
|
|
221
|
+
return { sizes: refModel, strategy: 'reference-size-guided', truncated: scan ? scan.truncatedTail || scan.leftoverBytes > 0 : false };
|
|
222
|
+
}
|
|
223
|
+
if (scan && scan.sizes.length > 0) {
|
|
224
|
+
return { sizes: scan.sizes, strategy: 'nal-scan', truncated: scan.truncatedTail || scan.leftoverBytes > 0 };
|
|
225
|
+
}
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
/** Reference stsz model, only when it tiles the payload exactly. */
|
|
229
|
+
function referenceSizeModel(track, payloadLen) {
|
|
230
|
+
if (track.constantSampleSize > 0 && payloadLen % track.constantSampleSize === 0) {
|
|
231
|
+
return new Array(payloadLen / track.constantSampleSize).fill(track.constantSampleSize);
|
|
232
|
+
}
|
|
233
|
+
if (track.sampleSizes.length > 0) {
|
|
234
|
+
const total = track.sampleSizes.reduce((n, s) => n + s, 0);
|
|
235
|
+
if (total === payloadLen)
|
|
236
|
+
return track.sampleSizes.slice();
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
/** Time-to-sample: reuse the reference cadence when its count matches; else a single-delta run. */
|
|
241
|
+
function buildStts(track, sampleCount) {
|
|
242
|
+
const refTotal = track.sttsEntries.reduce((n, e) => n + e.count, 0);
|
|
243
|
+
if (track.sttsEntries.length > 0 && refTotal === sampleCount)
|
|
244
|
+
return track.sttsEntries.map((e) => ({ ...e }));
|
|
245
|
+
const delta = track.sttsEntries[0]?.delta ?? Math.max(1, Math.round(track.mediaTimescale / 30));
|
|
246
|
+
return [{ count: sampleCount, delta }];
|
|
247
|
+
}
|
|
248
|
+
/** Return the broken file's ftyp bytes, or a synthesized default. */
|
|
249
|
+
async function readFtyp(broken, scan) {
|
|
250
|
+
const ftyp = scan.boxes.find((b) => b.type === 'ftyp');
|
|
251
|
+
if (ftyp)
|
|
252
|
+
return broken.read(ftyp.offset, ftyp.size);
|
|
253
|
+
return defaultFtyp();
|
|
254
|
+
}
|
|
255
|
+
// ---------------------------------------------------------------------------
|
|
256
|
+
// TODO (documented gap — see the timebox note in the header):
|
|
257
|
+
// * Multi-track (video+audio) and audio-only reconstruction. Raw AAC inside
|
|
258
|
+
// mdat is not self-delimiting, so audio samples must be sized from the
|
|
259
|
+
// reference stsz, and interleaved tracks must be de-multiplexed by walking
|
|
260
|
+
// the mdat in the reference's chunk order (stsc/chunk cadence). This needs
|
|
261
|
+
// a per-chunk assignment pass and per-track stco tables; the single-track
|
|
262
|
+
// path above is the foundation for it.
|
|
263
|
+
// * Multi-slice-per-picture H.264/H.265: the AU grouper treats every VCL NAL
|
|
264
|
+
// as a new sample. Multi-slice frames are reconciled today by falling back
|
|
265
|
+
// to the reference size model; a first_mb_in_slice==0 check would make the
|
|
266
|
+
// NAL scan itself slice-aware.
|
|
267
|
+
// * B-frame reordering (ctts/composition offsets) is not reconstructed; the
|
|
268
|
+
// rebuilt moov assumes decode order == presentation order. For streams with
|
|
269
|
+
// B-frames this is a playable approximation, not frame-accurate timing.
|
|
270
|
+
// ---------------------------------------------------------------------------
|
|
271
|
+
//# sourceMappingURL=reconstruct.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconstruct.js","sourceRoot":"","sources":["../../src/video/reconstruct.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stream-copy video remux on libav.js (LGPL) — the production replacement for
|
|
3
|
+
* the dev-only GPL ffmpeg.wasm path.
|
|
4
|
+
*
|
|
5
|
+
* Strategy ladder mirrors the app prototype's `-c copy` ladder:
|
|
6
|
+
* (a) copy-faststart — plain stream copy, moov/index moved to the front;
|
|
7
|
+
* (b) genpts-copy — retry with AVFMT_FLAG_GENPTS|AVFMT_FLAG_IGNIDX
|
|
8
|
+
* (the `-fflags +genpts+ignidx` semantics of docs/04 §2).
|
|
9
|
+
*
|
|
10
|
+
* Honesty rules: 'missing-moov' fails with an explanation pointing at future
|
|
11
|
+
* moov reconstruction (never a fake success); 'truncated-mdat' is at best a
|
|
12
|
+
* partial (frames past the cut are physically gone); an output is only
|
|
13
|
+
* claimed when libav actually produced bytes and copied at least one packet.
|
|
14
|
+
*/
|
|
15
|
+
import type { ByteSource } from '../byte-source.js';
|
|
16
|
+
import { type VideoDamageClass, type VideoDiagnosis } from './diagnose.js';
|
|
17
|
+
export declare const AVFMT_FLAG_GENPTS = 1;
|
|
18
|
+
export declare const AVFMT_FLAG_IGNIDX = 2;
|
|
19
|
+
export type RemuxStrategyId = 'copy-faststart' | 'genpts-copy';
|
|
20
|
+
export interface RemuxStrategy {
|
|
21
|
+
id: RemuxStrategyId;
|
|
22
|
+
label: string;
|
|
23
|
+
description: string;
|
|
24
|
+
/** AVFormatContext flags OR-ed onto the demuxer before reading. */
|
|
25
|
+
demuxFlags: number;
|
|
26
|
+
}
|
|
27
|
+
/** Ordered ladder: the first strategy is tried first. */
|
|
28
|
+
export declare const REMUX_STRATEGIES: readonly RemuxStrategy[];
|
|
29
|
+
export type OutputContainer = 'mp4' | 'mkv' | 'webm';
|
|
30
|
+
export interface RemuxPlan {
|
|
31
|
+
/** False when a remux is pointless; `skipReason` explains why. */
|
|
32
|
+
attempt: boolean;
|
|
33
|
+
skipReason: string | null;
|
|
34
|
+
/** Bytes to strip from the front (garbage prefix) before demuxing. */
|
|
35
|
+
stripPrefixBytes: number;
|
|
36
|
+
output: OutputContainer;
|
|
37
|
+
/** libav muxer name for the output. */
|
|
38
|
+
formatName: string;
|
|
39
|
+
/** mov muxer option; null for non-mp4 outputs. */
|
|
40
|
+
movflags: string | null;
|
|
41
|
+
strategies: readonly RemuxStrategy[];
|
|
42
|
+
}
|
|
43
|
+
/** Map input container to output: mkv stays mkv, webm stays webm, rest → mp4. */
|
|
44
|
+
export declare function outputContainerFor(diagnosis: Pick<VideoDiagnosis, 'container'>): OutputContainer;
|
|
45
|
+
/** Pure planning step: what to run for a given diagnosis (unit-testable). */
|
|
46
|
+
export declare function planRemux(diagnosis: VideoDiagnosis): RemuxPlan;
|
|
47
|
+
export interface RemuxAttempt {
|
|
48
|
+
strategy: RemuxStrategyId;
|
|
49
|
+
ok: boolean;
|
|
50
|
+
packetsCopied: number;
|
|
51
|
+
error: string | null;
|
|
52
|
+
}
|
|
53
|
+
export type VideoRepairOutcome = 'success' | 'partial' | 'failed';
|
|
54
|
+
export interface VideoRepairReport {
|
|
55
|
+
outcome: VideoRepairOutcome;
|
|
56
|
+
damageClass: VideoDamageClass;
|
|
57
|
+
container: VideoDiagnosis['container'];
|
|
58
|
+
outputContainer: OutputContainer | null;
|
|
59
|
+
/** Winning ladder rung, or null when everything failed. */
|
|
60
|
+
strategy: RemuxStrategyId | null;
|
|
61
|
+
streamsFound: number;
|
|
62
|
+
streamsCopied: number;
|
|
63
|
+
packetsCopied: number;
|
|
64
|
+
/** Packets the demuxer flagged as corrupt (copied anyway, counted honestly). */
|
|
65
|
+
corruptPackets: number;
|
|
66
|
+
/** True when packet reading stopped on an error before end-of-file. */
|
|
67
|
+
stoppedEarly: boolean;
|
|
68
|
+
/** Container-declared duration of the input, in seconds, if derivable. */
|
|
69
|
+
durationSeconds: number | null;
|
|
70
|
+
/** Duration actually recovered (last copied packet end), in seconds, if derivable. */
|
|
71
|
+
recoveredSeconds: number | null;
|
|
72
|
+
attempts: RemuxAttempt[];
|
|
73
|
+
notes: string[];
|
|
74
|
+
bytesIn: number;
|
|
75
|
+
bytesOut: number;
|
|
76
|
+
}
|
|
77
|
+
export interface RemuxOptions {
|
|
78
|
+
/** Reuse an existing diagnosis instead of re-scanning. */
|
|
79
|
+
diagnosis?: VideoDiagnosis;
|
|
80
|
+
}
|
|
81
|
+
export declare function remuxVideo(src: ByteSource, opts?: RemuxOptions): Promise<{
|
|
82
|
+
video: Uint8Array;
|
|
83
|
+
report: VideoRepairReport;
|
|
84
|
+
}>;
|
|
85
|
+
//# sourceMappingURL=remux.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remux.d.ts","sourceRoot":"","sources":["../../src/video/remux.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAiB,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAI1F,eAAO,MAAM,iBAAiB,IAAS,CAAC;AACxC,eAAO,MAAM,iBAAiB,IAAS,CAAC;AAKxC,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG,aAAa,CAAC;AAE/D,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,eAAe,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,yDAAyD;AACzD,eAAO,MAAM,gBAAgB,EAAE,SAAS,aAAa,EAepD,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAErD,MAAM,WAAW,SAAS;IACxB,kEAAkE;IAClE,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,sEAAsE;IACtE,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,eAAe,CAAC;IACxB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,SAAS,aAAa,EAAE,CAAC;CACtC;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC,GAAG,eAAe,CAIhG;AAQD,6EAA6E;AAC7E,wBAAgB,SAAS,CAAC,SAAS,EAAE,cAAc,GAAG,SAAS,CAuB9D;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,eAAe,CAAC;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,WAAW,EAAE,gBAAgB,CAAC;IAC9B,SAAS,EAAE,cAAc,CAAC,WAAW,CAAC,CAAC;IACvC,eAAe,EAAE,eAAe,GAAG,IAAI,CAAC;IACxC,2DAA2D;IAC3D,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,gFAAgF;IAChF,cAAc,EAAE,MAAM,CAAC;IACvB,uEAAuE;IACvE,YAAY,EAAE,OAAO,CAAC;IACtB,0EAA0E;IAC1E,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,sFAAsF;IACtF,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,0DAA0D;IAC1D,SAAS,CAAC,EAAE,cAAc,CAAC;CAC5B;AAeD,wBAAsB,UAAU,CAC9B,GAAG,EAAE,UAAU,EACf,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,CAAC,CAyH3D"}
|