Anleitungen

Dateien im Browser reparieren: So funktioniert IntactFile

Die meisten „beschädigten“ Mediendateien sind gar nicht beschädigt – die Daten sind intakt, es fehlt nur der Index, der sie zuordnet. IntactFile baut diesen Index lokal wieder auf: Die Datei wird von der Festplatte gelesen, im Tab per WebAssembly repariert und zurückgeschrieben. Null Bytes werden hochgeladen, und Sie können es im Network-Tab selbst nachprüfen. So funktioniert es – und hier hört es ehrlicherweise auf zu funktionieren.

Die tote Datei, die gar nicht tot ist

Jemand zieht eine SD-Karte, während die Kameraleuchte noch an ist. Eine Drohne stürzt ins Wasser. OBS stürzt nach drei Stunden ab. Ein Smartphone drosselt wegen Überhitzung und die Aufnahme-App wird mitten im Schreibvorgang beendet. In jedem dieser Fälle ist das Ergebnis dasselbe: eine Videodatei von mehreren Gigabyte, die kein Player öffnen will.ffmpeg gibt moov atom not found aus. Das Betriebssystem bietet an, sie zu löschen. Nach allen äußeren Anzeichen wirkt sie tot.

Meistens ist sie es nicht. Eine MP4- oder MOV-Datei besteht größtenteils aus einem einzigen riesigen Block komprimierter Frames – dermdat-Box – mit einem kleinen Index am Ende, demmoov-Atom. Das moov ist die Karte: Es hält fest, wo jeder Frame beginnt, wie groß er ist, wann er angezeigt werden soll und welche Codec-Konfiguration ein Decoder zur Initialisierung braucht. Ein Player liest zuerst den Index und navigiert damit durch die Daten. Ohne ihn weiß der Player nicht, wo auch nur ein einziger Frame beginnt, und gibt deshalb vollständig auf, statt „den Großteil“ der Datei abzuspielen.

Und hier kommt der grausame Teil des Designs: Das moov-Atom wird zuletzt geschrieben, in einem Durchgang, in dem Moment, in dem eine Aufnahme finalisiert wird. Es kann nicht früher geschrieben werden, denn der Byte-Offset eines Frames lässt sich nicht kennen, bevor der Frame existiert, und die Kamera hat keine Ahnung, ob Sie in zehn Sekunden oder erst in zwei Stunden stoppen. Also schreiben Recorder die Frames beim Kodieren fortlaufend in mdat und schreiben den Index erst, wenn Sie auf Stopp drücken. Wird diese Finalisierung unterbrochen – herausspringender Akku, Notabschaltung wegen Überhitzung, gezogene Karte, Absturz –, erhalten Sie eine Datei voller echtem, intaktem Material ganz ohne Index. Die Bytes sind da. Die Karte wurde nie geschrieben.

IntactFile existiert genau für diese Lücke zwischen „die Daten haben überlebt“ und „die Datei öffnet sich“. Bei einer MP4 oder MOV mit fehlendem moov baut es den Index wieder auf. Bei vielen anderen Formaten baut es die jeweils fehlende, entsprechende Struktur wieder auf. Und all das geschieht auf Ihrem Rechner, ohne dass die Datei je den Tab verlässt.

Warum Reparatur Upload bedeutet – und warum wir es anders gemacht haben

Suchen Sie nach einem Werkzeug zur Dateireparatur, und fast jedes Ergebnis funktioniert gleich: Sie laden Ihre Datei auf einen Server hoch, ein Prozess auf diesem Server führt die eigentliche Reparatur durch, und Sie laden das Ergebnis herunter. Dafür gibt es einen naheliegenden Grund. Die Reparaturlogik ist üblicherweise eine native Binärdatei – ein C- oder C++-Programm rund um Bibliotheken wie ffmpeg –, und der einfache Ort, eine native Binärdatei auszuführen, ist ein Rechner, den man selbst kontrolliert. Der Browser war jahrelang der falsche Ort für solchen Code.

Wir wollten es nicht so bauen, aus Gründen, die praktisch sind, bevor sie philosophisch werden. Die Dateien, die Menschen reparieren müssen, sind genau die Dateien, die mühsam hochzuladen sind: Aufnahmen von mehreren Gigabyte, oft über eine langsame Verbindung, oft die einzige Kopie von etwas Unersetzlichem. Jemanden zu bitten, ein 6 GB großes Hochzeitsvideo auf den Server eines Fremden hochzuladen, die Übertragung zweimal abzuwarten und darauf zu vertrauen, dass das Original danach gelöscht wird, ist eine schlechte Erfahrung um eine noch schlechtere Datenschutzgeschichte herum. Der Inhalt der Datei geht uns nichts an, und der einfachste Weg, das zu respektieren, ist, es uns unmöglich zu machen, ihn zu sehen.

Die Vorgabe, die wir uns setzten, war deshalb unmissverständlich: Die Datei verlässt niemals Ihr Gerät. Nicht „verschlüsselt bei der Übertragung“, nicht „nach der Verarbeitung gelöscht“ – überhaupt nicht übertragen. Das machte das ganze Problem zu einer technischen Frage: Kann die Reparatur selbst im Browser laufen? Dank WebAssembly lautet die Antwort inzwischen ja, und im Rest dieses Beitrags geht es darum, was dafür nötig war und wo die Nähte noch sichtbar sind.

Ein moov-Atom Schritt für Schritt neu aufbauen

Die Rekonstruktion eines fehlenden moov ist der Paradefall, deshalb lohnt es sich, ihn durchzugehen. Das Problem: Sie habenftyp (einen winzigen „das ist eine MP4“-Header), ein riesigesmdat und keinen Index. Die Chance, die den Wiederaufbau möglich macht, ist, dass mdat für einen Parser, der die Codecs versteht, nicht wirklich undurchsichtig ist. Frames werden als längenpräfixierte Einheiten gespeichert, und der Bitstream jedes Codecs trägt eine erkennbare Struktur – Startcodes und Header, die den Typ eines Frames, seine Abmessungen und seine Grenzen markieren. Ein Parser, der weiß, wie eine H.264- oder HEVC-Access-Unit aussieht, kann die Rohdaten durchlaufen und die Grenzen wiederherstellen, die der Index einst beschrieb.

Das ist die Art von Reparatur, die das Open-Source-Werkzeuguntrunc etabliert hat. Wir behandeln untruncstrikt als algorithmische Referenz – es steht unter der GPL – und unsere Video-Engine ist eine Clean-Room-Neuimplementierung in TypeScript, was den identischen Code sowohl in einem Browser-Tab als auch in Node laufen lässt. Die Schritte sind:

  1. Die Frame-Grenzen finden. mdatdurchsuchen und anhand der Längenpräfixe und Codec-Signaturen bestimmen, wo jeder Video- und Audio-Frame beginnt und endet.
  2. Klassifizieren und vermessen. Jeden Frame seinem Stream zuordnen und seine Größe und seinen Byte-Offset festhalten – so entsteht Sample für Sample das Rohmaterial der Sample-Size-Tabelle (stsz) und der Chunk-Offset-Tabelle (stco).
  3. Timing und Konfiguration wiederherstellen. Das Timing pro Frame und die Decoder-Konfiguration lagen im verlorenen Index. Wenn die beschädigte Datei sie nicht liefern kann, zeigt ein intakter Referenzclip derselben Kamera, aufgenommen mit denselben Einstellungen, der Engine, wie „normal“ aussieht – die Bildrate, die Zeitbasis, die Codec-Parametersätze.
  4. Einen frischen Index schreiben und finalisieren. Die wiederhergestellten Sample-Tabellen zu einem neuen moovzusammensetzen und eine spezifikationskonforme Datei ausgeben.

Das Ergebnis spielt alles ab, was physisch überlebt hat, und endet dort, wo die Daten enden. Die ehrlichen Grenzen ergeben sich direkt aus dem Mechanismus. Die allerletzten Sekunden – jene, die noch im Speicherpuffer der Kamera stecken und nie auf den Datenträger geschrieben wurden – sind wirklich verloren; kein Scan kann Frames finden, die nie geschrieben wurden. Aufnahmen mit stark unregelmäßigem Timing können mit leichtem Audio-/Video-Versatz zurückkommen, weil die rekonstruierte Uhr abgeleitet und nicht aufgezeichnet ist. Und wenn mdat selbst aus Nullen besteht, weil der Datenträger und nicht der Recorder versagt hat, gibt es keine Frames zu finden. MOV ist das QuickTime-Format, aus dem MP4 hervorgegangen ist, und teilt genau diese Struktur, deshalb sind eine.mov, die sich nicht öffnet, und eine .mp4, die sich nicht öffnet, fast immer dasselbe Problem in unterschiedlichem Gewand.

Die anderen Formate, kurz

Sobald man Beschädigung als „die Nutzdaten sind in Ordnung, der Index ist kaputt“ begreift, taucht dieselbe Form in Format nach Format auf. Die meisten Container- und Dokumentformate halten ihre Daten an einem Ort und ein Inhaltsverzeichnis an einem anderen, und fast immer stirbt zuerst das Inhaltsverzeichnis. Jede der folgenden Engines baut eine andere Spielart dieser Tabelle wieder auf.

Was jede Engine wiederaufbaut und wie sie implementiert ist.
FormatWas kaputt istWie es wiederaufgebaut wird
MP4 / MOVFehlender oder abgeschnittener moov-IndexClean-Room-Rekonstruktion in TypeScript aus den erhaltenen Frames
ZIP / OOXMLVerlorenes Central Directory (der Index des Archivs)Reines TypeScript: lokale File-Header scannen, das Central Directory neu aufbauen
PDFDefekte Cross-Reference-Tabelle (xref)Ein TypeScript-xref-Vorlauf, dann qpdf, kompiliert zu WebAssembly
JPEGBeschädigte oder fehlende Header-TabellenDekodierung mit mozjpeg / jSquash plus Transplantation eines Spender-Headers
PNGBeschädigte Chunk-Längen, CRCs oder StrukturReine TypeScript-Byte-Chirurgie am Chunk-Stream
SQLiteBeschädigte B-Tree-SeitenErreichbare Datensätze retten, dann die Datenbank neu aufbauen
RAR / 7zArchiv lässt sich nicht entpackenEntpacken über 7z, kompiliert zu WebAssembly

Ein paar sind einen Satz mehr wert. ZIP und OOXML sind dieselbe Engine: Eine .docx, .xlsx oder.pptx ist ein ZIP-Archiv, und ZIP hält am Ende ein „Central Directory“, das jeden Eintrag auflistet. Geht es verloren, sind die Einträge selbst noch da, jeder mit seinem eigenen lokalen Header, also durchläuft die Engine die Datei, findet diese Header und baut das Directory um sie herum wieder auf – ohne Bibliothek, reines TypeScript.PDF führt eine Cross-Reference-Tabelle, die jedes Objekt einem Byte-Offset zuordnet; ist sie defekt, leitet ein TypeScript-Vorlauf die Offsets neu ab, bevor die Datei an qpdf übergeben wird, das wir zu WebAssembly kompiliert haben, statt eine Binärdatei aufzurufen.JPEG ist der Sonderfall: Die komprimierten Scan-Daten überleben oft, während der Header mit den Quantisierungs- und Huffman-Tabellen es nicht tut, also transplantieren wir einen Spender-Header aus einem intakten Foto derselben Kamera und dekodieren dann über mozjpeg. Andere Tabellen, überall dasselbe Prinzip: den Index wieder aufbauen, die Daten unangetastet lassen.

Die Architektur: WebAssembly, in Ihrem Tab

All dies clientseitig laufen zu lassen, ist eine WebAssembly-Geschichte mit ein paar scharfen Kanten, zu denen man ehrlich sein sollte. Manche Engines sind reines TypeScript und laufen einfach. Andere umhüllen bestehenden nativen Code, der zu WASM kompiliert wurde – qpdf für PDF, 7z für Archive und, für die Videoarbeit, libav.js. Die Wahl von libav.js ist bewusst und lizenzgetrieben: Es ist ein LGPL-Build der FFmpeg-Bibliotheken, der sich in einem Produkt ausliefern lässt, und kein GPL-Build von ffmpeg, der das nicht wäre. Lizenzierung ist hier eine echte technische Einschränkung, kein nachträglicher Gedanke.

Die beiden härteren Einschränkungen sind Speicher und Isolation.

Speicher: streamen, nicht puffern

WebAssembly läuft in einem begrenzten Adressraum – in der Praxis etwa 2–4 GB, je nach Browser und Build. Die Dateien, die am dringendsten repariert werden müssen, sind häufig größer als das: Eine nicht finalisierte Aufnahme kann viele Gigabyte umfassen. Die Engines können die ganze Datei also nicht einfach in den Speicher laden und loslegen. Sie streamen – lesen und verarbeiten die Datei in Bereichen, halten nur das, was ein bestimmter Schritt braucht, und schreiben die Ausgabe inkrementell. Das hält den Speicherbedarf begrenzt und macht es möglich, eine Datei in einem Tab zu reparieren, der sie niemals als Ganzes fassen könnte. (Wir halten Aussagen zur Leistung bewusst qualitativ: Sie hängt stark von der Datei, dem Codec und dem Rechner ab.)

Isolation: zwei Origins, mit Absicht

Schnelles WASM will Threads, und Threads im Browser brauchenSharedArrayBuffer, das Browser nur für Seiten aktivieren, diecross-origin isolated sind – Seiten, die die richtigen COOP- und COEP-Header senden und die damit verbundenen Einschränkungen in Kauf nehmen. Diese Einschränkungen sind für eine normale Marketing-Seite, die Drittanbieter-Inhalte wie Werbung trägt, unpraktisch. Statt das eine für das andere zu opfern, teilen wir das Produkt auf zwei Origins auf: eine herkömmliche Marketing-Origin, die Werbung tragen kann, und eine separate, cross-origin-isolierte App-Origin, die COOP/COEP ausliefert, damit die mehrfädigen WASM-Engines mit voller Geschwindigkeit laufen können. Das sind mehr bewegliche Teile als bei einer einzigen Website, aber so bekommen das Lesematerial und die Reparaturmaschinerie jeweils die Umgebung, die sie brauchen.

Ehrlichkeit als Rückgabewert

Die wichtigste Design-Entscheidung steckt in keinem der Codecs. Sie besteht darin, dass jede Engine ein explizites Ergebnis liefert –Erfolg, teilweise oderfehlgeschlagen – zusammen mit einer Schadensklasse, die beschreibt, was sie gefunden hat. Sie erfindet niemals einen Erfolg.

Das ist wichtig, weil die Dateireparatur ein Feld voller Werkzeuge ist, die um jeden Preis eine Datei ausgeben und Sie Stunden später entdecken lassen, dass sie sich gar nicht abspielt oder nach vier Sekunden einfriert. Wenn die Karte fehlt und die Daten überlebt haben, können wir sie wieder aufbauen und sagen das auch. Wenn die Daten physisch weg sind – überschrieben, von einer defekten Festplatte auf null gesetzt, abgeschnitten –, gibt es nichts wiederherzustellen, und die richtige Antwort ist, genau das klar zu sagen, statt eine plausibel aussehende Datei auszugeben, die dann versagt, wenn es darauf ankommt. „Teilweise“ ist ebenfalls ein vollwertiges Ergebnis: Die ersten zwei Stunden einer dreistündigen Aufnahme zu retten und offen zu sagen, dass die letzte Stunde nicht überlebt hat, ist ein wirklich nützliches Ergebnis – solange wir es nicht als vollständiges ausgeben.

Den Fehlerfall als ehrliches, benanntes Ergebnis zu behandeln statt als Peinlichkeit, die man versteckt, ist unserer Meinung nach der Unterschied zwischen einem Werkzeug, dem man die einzige Kopie von etwas anvertrauen kann, und einem, dem man das nicht kann.

Prüfen Sie es selbst: Öffnen Sie den Network-Tab

Sie müssen „0 Bytes hochgeladen“ nicht einfach glauben. Es ist die Art von Behauptung, die Sie in unter einer Minute überprüfen können, und uns ist es lieber, Sie tun es:

  1. Öffnen Sie das Reparatur-Werkzeug in Ihrem Browser.
  2. Öffnen Sie die DevTools (F12 oder Cmd/Ctrl + Shift + I) und wechseln Sie zum Network-Tab.
  3. Ziehen Sie eine beschädigte Datei auf die Seite.
  4. Beobachten Sie die Anfragen. Sie sehen die eigenen Assets der Seite laden, aber keine Anfrage überträgt den Inhalt Ihrer Datei. Die Bytes gehen von der Festplatte in den Tab und zurück auf die Festplatte – niemals über die Leitung.

Für die wirklich Misstrauischen der stärkere Test: Trennen Sie Ihre Netzwerkverbindung komplett und führen Sie die Reparatur offline aus. Sie funktioniert trotzdem, weil von Anfang an kein Server im Spiel war. Dieselben Engines treiben auch ein Node-Paket für die Kommandozeile und die automatisierte Nutzung an, aber auf der Website geschieht die Reparatur dort, wo Ihre Datei bereits ist.

FAQ

Lädt IntactFile meine Datei auf einen Server hoch?

Nein. Jede Engine läuft clientseitig in Ihrem Browser, kompiliert zu WebAssembly. Die Datei wird mit der File API von Ihrer Festplatte gelesen, im Tab wiederhergestellt und zurück auf Ihre Festplatte gespeichert. Null Bytes der Datei werden über das Netzwerk gesendet. Sie können das selbst überprüfen: Öffnen Sie die DevTools, beobachten Sie den Network-Tab, ziehen Sie eine Datei hinein und stellen Sie fest, dass keine Anfrage ihren Inhalt überträgt. Dieselben Engines laufen auch in Node für die Nutzung auf der Kommandozeile und auf dem Server, aber auf der Website verlässt nichts Ihren Rechner.

Ist das dasselbe wie untrunc?

Es löst dieselbe Art von Problem – die Rekonstruktion eines fehlenden MP4/MOV-Index aus den erhaltenen Mediendaten –, aber der Code ist nicht untrunc. untrunc steht unter der GPL-Lizenz, deshalb nutzen wir es nur als algorithmische Referenz und haben eine Clean-Room-Implementierung in TypeScript geschrieben. Dadurch läuft genau dieselbe Engine unverändert im Browser und in Node, was ein natives C++-Werkzeug ohne separaten Build nicht kann.

Warum verlangen manche Reparaturen eine zweite, intakte Datei?

Wenn eine MP4/MOV ihren Index verloren hat, sind auch das dort gespeicherte Timing und die Decoder-Konfiguration verschwunden. Ein intakter Referenzclip, aufgenommen mit derselben Kamera und denselben Einstellungen, lässt die Engine lernen, wie „normales“ Frame-Timing und Codec-Setup für Ihr Gerät aussehen, und wendet sie auf die wiederhergestellten Frames an. Bei JPEG gilt dasselbe Prinzip für Header: Ein Spender-Header aus einem Foto derselben Kamera liefert die Tabellen, die ein Decoder braucht. Wenn die beschädigte Datei noch genug eigene Metadaten enthält, ist keine Referenz nötig.

Kann es jede beschädigte Datei reparieren?

Nein, und es tut auch nicht so. Container- und Index-Reparatur funktioniert, wenn die Nutzdaten überlebt haben und nur die Zuordnung verloren oder beschädigt wurde – was einen großen Teil der „Beschädigungen“ in der Praxis abdeckt. Wenn die eigentlichen Daten physisch weg sind (überschrieben, von einer defekten Festplatte auf null gesetzt oder abgeschnitten), gibt es nichts wiederherzustellen, und jedes ehrliche Werkzeug muss das sagen. Jede Engine liefert ein explizites Ergebnis – Erfolg, teilweise oder fehlgeschlagen – mit einer Schadensklasse, statt eine plausibel aussehende Datei auszugeben, die sich nicht abspielen lässt.

Wie geht es mit sehr großen Videodateien um?

Nicht finalisierte Aufnahmen sind oft mehrere Gigabyte groß, und WebAssembly im Browser hat einen begrenzten Adressraum (je nach Browser und Build grob 2–4 GB). Deshalb streamen die Engines: Sie lesen und verarbeiten die Datei in Bereichen, statt das Ganze auf einmal in den Speicher zu puffern. Das hält den Speicherbedarf begrenzt und macht es möglich, Dateien von mehreren Gigabyte in einem Tab zu reparieren, der sie niemals alle auf einmal fassen könnte.

Möchten Sie den Rundgang durch das Format im Zentrum von all dem auf Byte-Ebene? Lesen SieMP4-Anatomie: Was das moov-Atom ist und warum Ihr Video nicht abspielt.