Le fichier mort qui ne l'est pas
Quelqu'un retire une carte SD alors que le voyant de la caméra est encore allumé. Un drone tombe dans l'eau. OBS plante à la troisième heure. Un téléphone bride sa fréquence pour cause de surchauffe et l'application d'enregistrement est tuée en pleine écriture. Dans chacun de ces cas, le résultat est le même : un fichier vidéo de plusieurs gigaoctets que tous les lecteurs refusent d'ouvrir. ffmpeg affichemoov atom not found. Le système d'exploitation propose de le supprimer. À tous les signes extérieurs, il paraît mort.
En général, il ne l'est pas. Un fichier MP4 ou MOV est essentiellement un énorme bloc d'images compressées — la boîte mdat — suivi, à la fin, d'un petit index appelé l'atome moov. Lemoov est la carte : il consigne où commence chaque image, sa taille, le moment où elle doit être affichée, et la configuration du codec dont un décodeur a besoin pour s'initialiser. Un lecteur lit d'abord l'index et s'en sert pour naviguer dans les données. Sans lui, le lecteur ignore où commence ne serait-ce qu'une seule image, alors il abandonne complètement au lieu de lire « la majeure partie » du fichier.
Voici le côté cruel de la conception : l'atome moov est écriten dernier, en une seule passe, au moment où l'enregistrement est finalisé. Il ne peut pas être écrit plus tôt, car l'offset en octets d'une image ne peut pas être connu avant que l'image existe, et la caméra n'a aucune idée de si vous allez arrêter dans dix secondes ou dans deux heures. Les enregistreurs écrivent donc les images dans mdat en flux à mesure qu'ils les encodent, et n'écrivent l'index qu'au moment où vous appuyez sur stop. Interrompez cette finalisation — batterie qui lâche, arrêt pour surchauffe, carte retirée, plantage — et vous obtenez un fichier plein de séquences réelles et intactes, sans aucun index. Les octets sont là. La carte n'a jamais été écrite.
IntactFile existe précisément pour cet écart entre « les données ont survécu » et « le fichier s'ouvre ». Pour un MP4 ou un MOV aumoov manquant, il reconstruit l'index. Pour beaucoup d'autres formats, il reconstruit la structure équivalente qui a disparu. Et il fait tout cela sur votre machine, sans que le fichier ne quitte jamais l'onglet.
Pourquoi réparer signifie téléverser — et pourquoi nous ne l'avons pas fait
Cherchez un outil de réparation de fichiers et presque tous les résultats fonctionnent de la même façon : vous téléversez votre fichier sur un serveur, un processus sur ce serveur effectue la réparation proprement dite, puis vous téléchargez le résultat. Il y a une raison simple à cela. La logique de réparation est en général un binaire natif — un programme en C ou C++ bâti autour de bibliothèques comme ffmpeg — et l'endroit facile où exécuter un binaire natif, c'est une machine que vous contrôlez. Le navigateur a été, pendant des années, le mauvais endroit pour exécuter ce genre de code.
Nous ne voulions pas le construire ainsi, pour des raisons qui sont pratiques avant d'être philosophiques. Les fichiers que les gens ont besoin de faire réparer sont exactement ceux qui sont pénibles à téléverser : des enregistrements de plusieurs gigaoctets, souvent sur une connexion lente, souvent l'unique copie de quelque chose d'irremplaçable. Demander à quelqu'un de téléverser une vidéo de mariage de 6 GB sur le serveur d'un inconnu, d'attendre deux fois le transfert et de croire que l'original est ensuite supprimé, c'est une mauvaise expérience enveloppée dans une histoire de confidentialité pire encore. Le contenu du fichier ne nous regarde pas, et la façon la plus simple de le respecter est de nous mettre dans l'impossibilité de le voir.
La contrainte que nous nous sommes fixée était donc catégorique : le fichier ne quitte jamais votre appareil. Pas « chiffré pendant le transfert », pas « supprimé après traitement » — pas transféré du tout. Cela a transformé tout le problème en une question d'ingénierie : la réparation elle-même peut-elle s'exécuter dans le navigateur ? Grâce à WebAssembly, la réponse est désormais oui, et le reste de cet article raconte ce qu'il a fallu pour y parvenir, et où les coutures restent visibles.
Reconstruire un atome moov, étape par étape
Reconstruire un moov manquant est le cas emblématique, il vaut donc la peine de le détailler. Le problème : vous avez ftyp(un minuscule en-tête « ceci est un MP4 »), un mdatgigantesque, et aucun index. Ce qui rend la reconstruction possible, c'est que mdat n'est pas vraiment opaque pour un analyseur qui comprend les codecs. Les images sont stockées comme des unités préfixées par leur longueur, et le flux binaire de chaque codec porte une structure reconnaissable — des codes de début et des en-têtes qui marquent le type d'une image, ses dimensions, ses limites. Un analyseur qui sait à quoi ressemble une unité d'accès H.264 ou HEVC peut parcourir les données brutes et retrouver les limites que l'index décrivait autrefois.
C'est la classe de réparation qu'a établie l'outil open sourceuntrunc. Nous traitons untrunc strictement comme une référence algorithmique — il est sous licence GPL — et notre moteur vidéo est une réimplémentation clean-room en TypeScript, ce qui permet au code identique de tourner dans un onglet de navigateur et dans Node. Les étapes sont les suivantes :
- Trouver les limites des images. Parcourir
mdaten utilisant les préfixes de longueur et les signatures de codec pour repérer où commence et où finit chaque image vidéo et audio. - Classer et mesurer. Attribuer chaque image à son flux et consigner sa taille et son offset en octets, en reconstituant échantillon par échantillon la matière première des tables de taille d'échantillon (
stsz) et d'offset de chunk (stco). - Récupérer la temporisation et la configuration. La temporisation par image et la configuration du décodeur résidaient dans l'index perdu. Quand le fichier cassé ne peut pas les fournir, un clip de référence sain issu de la même caméra, enregistré avec les mêmes réglages, apprend au moteur à quoi ressemble le « normal » — la fréquence d'images, la timescale, les jeux de paramètres du codec.
- Écrire un index neuf et finaliser. Assembler les tables d'échantillons récupérées en un nouveau
moovet écrire un fichier conforme à la spécification.
Le résultat lit tout ce qui a physiquement survécu, en s'arrêtant là où s'arrêtent les données. Les limites honnêtes découlent directement du mécanisme. Les toutes dernières secondes — celles encore présentes dans la mémoire tampon de la caméra, jamais écrites sur le stockage — sont réellement perdues ; aucune analyse ne peut trouver des images qui n'ont jamais été écrites. Les enregistrements à la temporisation très irrégulière peuvent revenir avec une légère dérive audio/vidéo, car l'horloge reconstruite est déduite plutôt qu'enregistrée. Et si mdatlui-même n'est que des zéros parce que c'est le stockage qui a lâché et non l'enregistreur, il n'y a aucune image à trouver. MOV est le format QuickTime dont MP4 est issu et partage exactement cette structure : un.mov qui ne s'ouvre pas et un .mp4 qui ne s'ouvre pas sont presque toujours le même problème sous des habits différents.
Les autres formats, en bref
Dès lors que l'on conçoit la corruption comme « le contenu utile va bien, l'index est cassé », la même forme réapparaît format après format. La plupart des formats de conteneur et de document gardent leurs données à un endroit et une table des matières ailleurs, et c'est presque toujours la table des matières qui meurt en premier. Chaque moteur ci-dessous reconstruit une variante différente de cette table.
| Format | Ce qui est cassé | Comment c'est reconstruit |
|---|---|---|
| MP4 / MOV | Index moov manquant ou tronqué | Reconstruction TypeScript clean-room à partir des images survivantes |
| ZIP / OOXML | Répertoire central perdu (l'index de l'archive) | TypeScript pur : parcourir les en-têtes de fichier locaux, reconstruire le répertoire central |
Table de références croisées (xref) cassée | Une pré-passe xref en TypeScript, puis qpdf compilé en WebAssembly | |
| JPEG | Tables d'en-tête endommagées ou manquantes | Décodage mozjpeg / jSquash plus une greffe d'en-tête donneur |
| PNG | Longueurs de chunk, CRC ou structure corrompus | Chirurgie d'octets en TypeScript pur sur le flux de chunks |
| SQLite | Pages B-tree endommagées | Récupérer les enregistrements atteignables, puis reconstruire la base de données |
| RAR / 7z | L'archive refuse de s'extraire | Extraction via 7z compilé en WebAssembly |
Quelques-uns méritent une phrase de plus. ZIP et OOXML sont le même moteur : un .docx, un .xlsx ou un.pptx est une archive ZIP, et le ZIP conserve à la fin un « répertoire central » qui recense chaque entrée. Perdez-le et les entrées elles-mêmes sont toujours là, chacune avec son propre en-tête local ; le moteur parcourt donc le fichier pour trouver ces en-têtes et reconstruit le répertoire autour d'eux — sans bibliothèque, en TypeScript pur.PDF conserve une table de références croisées qui associe chaque objet à un offset en octets ; quand elle est cassée, une pré-passe en TypeScript redérive les offsets avant de confier le fichier à qpdf, que nous avons compilé en WebAssembly plutôt que d'appeler un binaire externe.JPEG est le cas à part : les données de scan compressées survivent souvent alors que l'en-tête portant les tables de quantification et de Huffman, non ; nous greffons donc un en-tête donneur issu d'une photo saine prise avec la même caméra, puis décodons via mozjpeg. Des tables différentes, le même principe partout : reconstruire l'index, laisser les données intactes.
L'architecture : WebAssembly, dans votre onglet
Exécuter tout cela côté client est une histoire de WebAssembly avec quelques aspérités qu'il vaut mieux avouer. Certains moteurs sont en TypeScript pur et fonctionnent tels quels. D'autres enveloppent du code natif existant compilé en WASM — qpdf pour le PDF, 7z pour les archives, et, pour le travail vidéo, libav.js. Le choix de libav.js est délibéré et dicté par la licence : c'est une build LGPL des bibliothèques FFmpeg, livrable dans un produit, plutôt qu'une build GPL deffmpeg, qui ne le serait pas. La licence est ici une véritable contrainte d'ingénierie, pas une réflexion après coup.
Les deux contraintes les plus ardues sont la mémoire et l'isolation.
Mémoire : diffuser en flux, pas mettre en tampon
WebAssembly s'exécute dans un espace d'adressage limité — en pratique de l'ordre de 2–4 GB selon le navigateur et la build. Les fichiers que les gens ont le plus besoin de réparer dépassent fréquemment cette taille : un enregistrement non finalisé peut peser plusieurs gigaoctets. Les moteurs ne peuvent donc pas simplement charger tout le fichier en mémoire et démarrer. Ils travaillent en flux — lisant et traitant le fichier par intervalles, ne gardant que ce dont une étape donnée a besoin, et écrivant la sortie de manière incrémentale. Cela maintient la mémoire bornée et rend possible la réparation d'un fichier dans un onglet qui ne pourrait jamais le contenir en entier d'un seul coup. (Nous gardons volontairement les affirmations de performance qualitatives : cela dépend fortement du fichier, du codec et de la machine.)
Isolation : deux origines, à dessein
Un WASM rapide veut des threads, et les threads dans le navigateur ont besoin de SharedArrayBuffer, que les navigateurs n'activent que pour les pages cross-origin isolées — des pages qui envoient les bons en-têtes COOP et COEP et acceptent les restrictions qui les accompagnent. Ces restrictions sont malcommodes pour une page marketing ordinaire qui embarque des éléments tiers comme des publicités. Plutôt que de sacrifier l'un pour l'autre, nous répartissons le produit sur deux origines : une origine marketing classique qui peut porter des publicités, et une origine applicative distincte, cross-origin isolée, servant COOP/COEP pour que les moteurs WASM multithreadés tournent à pleine vitesse. Cela fait plus de pièces mobiles qu'un site unique, mais cela permet à la matière à lire et à la machinerie de réparation d'avoir chacune l'environnement dont elle a besoin.
L'honnêteté comme valeur de retour
La décision de conception la plus importante ne se trouve dans aucun des codecs. C'est que chaque moteur renvoie un résultat explicite —réussi, partiel ouéchoué — accompagné d'une classe de dommage décrivant ce qu'il a trouvé. Il ne fabrique jamais une réussite.
Cela compte parce que la réparation de fichiers est un domaine rempli d'outils qui écrivent un fichier quoi qu'il arrive, vous laissant découvrir des heures plus tard qu'il ne se lit pas, ou qu'il se lit quatre secondes puis se fige. Quand la carte manque et que les données ont survécu, nous pouvons la reconstruire et le dire. Quand les données ont physiquement disparu — écrasées, mises à zéro par un disque défaillant, tronquées — il n'y a rien à reconstruire, et la bonne réponse est de dire cela, clairement, plutôt que de produire un fichier à l'apparence plausible qui échoue au moment critique. Le « partiel » est lui aussi un résultat de premier ordre : récupérer les deux premières heures d'un enregistrement de trois heures en annonçant franchement que la dernière heure n'a pas survécu est un résultat réellement utile, tant que nous ne le déguisons pas en résultat complet.
Traiter le cas d'échec comme un résultat honnête et nommé plutôt que comme une gêne à dissimuler, c'est, selon nous, la différence entre un outil auquel vous pouvez confier l'unique copie de quelque chose et un outil auquel vous ne le pouvez pas.
Vérifiez-le vous-même : ouvrez l'onglet Network
Vous n'avez pas à croire « 0 octet téléversé » sur parole. C'est le genre d'affirmation que vous pouvez vérifier en moins d'une minute, et nous préférons que vous le fassiez :
- Ouvrez l'outil de réparation dans votre navigateur.
- Ouvrez les DevTools (
F12ouCmd/Ctrl + Maj + I) et passez à l'onglet Network. - Déposez un fichier cassé sur la page.
- Observez les requêtes. Vous verrez se charger les ressources propres à la page, mais aucune requête ne transporte le contenu de votre fichier. Les octets vont du disque vers l'onglet puis retournent sur le disque — jamais sur le réseau.
Pour les plus méfiants, le test plus radical : coupez complètement votre connexion réseau et lancez la réparation hors ligne. Elle fonctionne quand même, parce qu'il n'y a jamais eu de serveur dans la boucle au départ. Les mêmes moteurs alimentent aussi un paquet Node pour un usage en ligne de commande et automatisé, mais sur le site web la réparation se fait là où votre fichier se trouve déjà.
FAQ
IntactFile téléverse-t-il mon fichier sur un serveur ?
Non. Chaque moteur s'exécute côté client dans votre navigateur, compilé en WebAssembly. Le fichier est lu depuis votre disque avec la File API, reconstruit dans l'onglet, puis réenregistré sur votre disque. Zéro octet du fichier n'est envoyé sur le réseau. Vous pouvez le vérifier vous-même : ouvrez les DevTools, surveillez l'onglet Network, déposez un fichier et constatez qu'aucune requête ne transporte son contenu. Les mêmes moteurs tournent aussi dans Node pour un usage en ligne de commande et côté serveur, mais sur le site web rien ne quitte votre machine.
Est-ce la même chose qu'untrunc ?
Cela résout la même classe de problème — reconstruire un index MP4/MOV manquant à partir des données multimédias survivantes — mais le code n'est pas untrunc. untrunc est sous licence GPL, nous l'utilisons donc uniquement comme référence algorithmique et avons écrit une implémentation clean-room en TypeScript. Cela permet aussi au moteur exactement identique de tourner sans modification dans le navigateur et dans Node, ce qu'un outil natif en C++ ne peut pas faire sans une build séparée.
Pourquoi certaines réparations demandent-elles un second fichier, sain ?
Quand un MP4/MOV a perdu son index, la temporisation et la configuration du décodeur qui y résidaient ont disparu elles aussi. Un clip de référence sain, enregistré avec la même caméra et les mêmes réglages, permet au moteur d'apprendre à quoi ressemblent une temporisation « normale » des images et une configuration du codec pour votre appareil, puis de les appliquer aux images récupérées. Pour le JPEG, la même idée s'applique aux en-têtes : un en-tête donneur issu d'une photo prise avec la même caméra fournit les tables dont un décodeur a besoin. Si le fichier cassé conserve encore assez de ses propres métadonnées, aucune référence n'est nécessaire.
Peut-il réparer n'importe quel fichier corrompu ?
Non, et il ne prétendra pas le contraire. La réparation du conteneur et de l'index fonctionne quand le contenu utile a survécu et que seule la carte a été perdue ou endommagée — ce qui couvre une large part de la « corruption » du monde réel. Quand les données elles-mêmes ont physiquement disparu (écrasées, mises à zéro par un disque défaillant ou tronquées), il n'y a rien à reconstruire, et tout outil honnête doit le dire. Chaque moteur renvoie un résultat explicite — réussi, partiel ou échoué — avec une classe de dommage, plutôt que de produire un fichier à l'apparence plausible qui ne se lit pas.
Comment gère-t-il les fichiers vidéo très volumineux ?
Les enregistrements non finalisés pèsent souvent plusieurs gigaoctets, et la WebAssembly du navigateur dispose d'un espace d'adressage limité (environ 2–4 GB selon le navigateur et la build). Les moteurs travaillent donc en streaming : ils lisent et traitent le fichier par intervalles plutôt que de tout charger en mémoire d'un coup. Cela maintient l'usage de la mémoire borné et permet de réparer des fichiers de plusieurs gigaoctets dans un onglet qui ne pourrait jamais les contenir tous à la fois.
Envie de la visite au niveau de l'octet du format au cœur de tout cela ? LisezAnatomie du MP4 : ce qu'est l'atome moov et pourquoi votre vidéo ne se lit pas.