FixMyVideos

How FixMyVideos Recovers Corrupted Video — A Technical Walkthrough

If you landed here looking for marketing copy, the rest of the site has plenty. This page is for the people who actually want to know what happens between upload and playable file.

We're going to walk through the architecture of our recovery engine from the perspective of an engineer who knows what a container is. We'll explain why corrupted video files are recoverable at all, what the recovery passes are doing, and what makes the hard cases hard. Where we use a clever heuristic that took us a long time to get right, we'll tell you what category of heuristic it is and skip the magic numbers.

Why a video file goes from "playing" to "permanently broken"

A modern video file is two things glued together:

  1. A container — the file format wrapper (MP4, MOV, MKV, AVI). The container is essentially an index. It tells the player: here is a video track, it's H.264, the timescale is 30000/1001, frame 1 starts at byte offset 12,488 and is 7,210 bytes long, frame 2 starts at...
  2. The codec payload — the actual compressed video and audio data. H.264 NAL units, ProRes frames, AAC packets, PCM samples. This is the data your screen ends up showing.

When you play a file in QuickTime, VLC, or DaVinci Resolve, the player reads the container's index first, jumps to the byte offsets it points at, and feeds the codec payload to the decoder. The container is the map. The codec is the territory.

This split is the reason most "corrupted" files are recoverable — and the reason most aren't actually corrupted in the codec sense at all.

What "MOOV atom not found" actually means

In MP4 and MOV files, the index lives in a top-level structure called the MOOV atom. The MOOV atom is built incrementally during recording and only finalised and written to disk at the moment the camera stops the recording cleanly. If the recording is interrupted — battery dies, SD card pulls out, recording crash on the camera firmware, an Atomos Ninja loses power, a drone hits the ground — the camera never writes the MOOV. What's on the SD card is:

  • The container's outer wrapper (the ftyp atom at the start, declaring "this is an MP4").
  • Megabytes-to-gigabytes of mdat (media data) — the raw codec payload.
  • No index.

QuickTime opens the file, reads ftyp, looks for the MOOV that says where the frames are, finds mdat instead, and throws up its hands. The user sees "the file is corrupted." The file is not corrupted. The file has 99.7% of the bytes a player needs and is missing the 0.3% that tells the player how to use them.

This is the single most common case we recover from. It's also the one most existing tools fail at because they assume the broken file resembles a known reference and try to graft a borrowed index on top — which works for one camera model and breaks subtly for the next.

Other failure modes

Less common but still recoverable, in roughly descending order:

  • Truncated files: the camera stopped mid-write, leaving a partial frame at the end and an incomplete MOOV. Same recovery as above plus a careful tail-trim.
  • Corrupted chunks: a few hundred to a few thousand bytes inside the mdat got zeroed out (filesystem error, bad sector). Most of the file is intact; we identify and skip the dead range.
  • Wrong endianness audio: this is an Atomos / external recorder special. The PCM audio chunks were written with the wrong byte order. Audio plays as static until you swap.
  • Garbled headers: the ftyp and the start of MOOV are damaged but mdat is fine. We rebuild the wrapper from scratch.
  • Container/codec mismatch: someone renamed an .mov to .mp4 and the metadata reflects MOV semantics. Players choke on the inconsistency. We rewrite the wrapper.

We do not handle:

  • True codec-level damage where the H.264 NAL units themselves are scrambled or overwritten beyond a tolerable density. If 30% of your frames are gone, no tool on Earth can hallucinate them back.
  • Files where the entire video track payload was overwritten with zeros. There is nothing to recover from.
  • Encrypted DRM-protected files where you don't have the key. Out of scope on principle.

If your file falls into the recoverable categories, you're at the top of the funnel. The rest of this page is what happens after upload.

The architecture in one diagram

UploadMP4 / MOV / MKV / AVI · ≤ 50 GBPass 1 — Diagnosticffprobe + mediainfo +byte-level header probeengine_cache.jsonPass 2 — Container scanWalk atoms, find what is intact /missingPass 3 — Codec detectionFrame-marker scan in mdat(ProRes / H.264 / H.265 / MJPEG)(other codecs via registry)Pass 4 — Frame indexingBuild virtual stsz / stco tablefrom observed framesPass 5 — Audio probingSample-rate / channel /endianness / syncPass 6 — Container rebuildSynthesise MOOV, write output,transcode previewOutputRecovered file + 5-second preview

Six passes, in order, every time. Some passes early-exit when nothing's broken (a file with a valid MOOV but truncated audio doesn't need pass 4). The pass order is fixed because each pass reads the cache produced by the previous one.

Pass 1: diagnostic

The diagnostic pass runs three probes in parallel and reconciles their answers:

  • ffprobe (industry standard, ships with FFmpeg). Tells us what FFmpeg thinks the file is. If FFmpeg can't even read the header, that's a strong signal about which atoms are missing.
  • mediainfo (industry standard, MediaArea). A second opinion. Different parser, different failure modes — when ffprobe and mediainfo disagree, the disagreement itself is diagnostic.
  • A byte-level header probe of our own. Walks the first ~1 MB and the last ~1 MB looking for atom signatures (ftyp, mdat, moov, free, wide, mdat-large variants), the ICPF ProRes frame magic, H.264 start codes, HEVC VPS/SPS markers, and a handful of camera-specific signatures. The point is to find what's actually in the file regardless of whether the index claims it.

The output of pass 1 is a structured diagnosis: which atoms exist, which are missing, what the apparent codec is, what the apparent container is, and a corruption classification (moov_missing, truncated, corrupted_header, wrong_endianness_audio, etc.). This classification drives every subsequent pass.

You can run the same probes yourself: ffprobe -v error -show_format -show_streams your_file.mov and mediainfo --Inform=XML your_file.mov. If both report nothing useful, you're in our recoverable territory.

Pass 2: container scan

Container scan walks the byte stream from offset zero to EOF, finding every atom-sized region. An atom in MP4/MOV starts with a 4-byte big-endian size and a 4-byte type identifier (moov, mdat, udta, free, skip, ...). We don't trust the sizes — corrupted size fields would send us into garbage — so we cross-check by looking for the next plausible atom signature within a reasonable range.

The output is a "what's intact" map:

  • ftyp at offset 0, valid? yes/no.
  • mdat starting at offset X, length Y bytes.
  • moov present? if so, where, and does it parse?
  • free/skip regions (camera padding) — ignore.

If MOOV is present and parses, the file is barely corrupted and the recovery is essentially a re-mux. If MOOV is absent or unparseable, we move to pass 3.

Pass 3: codec detection

This is where the engine earns its keep. The mdat payload is the only ground truth left. We need to identify the codec by scanning frame markers.

  • ProRes uses an 8-byte header per frame that includes the magic string icpf at a fixed offset. ProRes frames are intra-only (every frame is a keyframe), so once we lock onto the cadence, we have an exact frame count.
  • H.264 uses NAL unit start codes (00 00 00 01 or 00 00 01) followed by a 1-byte NAL type. We look for SPS (NAL type 7), PPS (8), and IDR (5) frames, then count the gaps between IDRs to learn the GOP structure.
  • H.265 / HEVC is similar but with VPS (32), SPS (33), PPS (34), and a more complex slice-header structure.
  • MJPEG, MPEG-2, DV each have their own frame markers. Less common in the modern camera fleet, but supported.

Frame detection is a registry. Adding a new codec means writing one detector class that implements find_frames(byte_stream) -> list[frame_offset, frame_size, frame_type] and registering it. The rest of the pipeline doesn't care which codec it is.

What we'll say about the heuristics: detection isn't just pattern matching. Many byte sequences look like NAL start codes when they aren't. Many icpf substrings appear inside compressed data. The detectors do a second-stage validation pass — for ProRes, sanity-check the declared frame size against the next-frame offset; for H.264, validate that SPS parameters are internally consistent; and so on. We'll skip the specific validation thresholds because they're the part of the engine that took the longest to tune, but the category of validation (declared-size vs observed-size cross-check; parameter-set consistency) is unsurprising and what any serious implementation would do.

Pass 4: frame indexing — building a virtual MOOV

Once we have a list of frames with byte offsets, sizes, and types, we can build the index a player needs. In MP4/MOV terms, that's:

  • stsz — sample size table. One entry per frame, the frame's compressed size in bytes.
  • stco or co64 — chunk offset table. Where each chunk lives in mdat.
  • stts — time-to-sample table. Frame durations.
  • stsc — sample-to-chunk map.
  • stss — sync sample table. Which frames are keyframes.
  • stsd — sample description. Codec parameters, resolution, pixel format.

For files larger than 4 GB — most modern camera output, certainly anything from a Sony FX6 / FX9 or an Atomos rig — we use co64 (64-bit offsets), not stco (32-bit). This is one of those non-negotiable details: stco overflows silently past 4 GB and produces a file that opens but plays garbage halfway through. If you've ever recovered a "fixed" file that plays the first 8 minutes and then degenerates into colour noise, you've seen stco overflow.

The stsd atom has a specific quirk: it does not contain a reserved uint32 between version/flags and entry count, even though some old reference implementations and one influential blog post claim it does. Get this wrong by 4 bytes and the whole MOOV reads as garbage. We learned this the hard way once and have a regression test for it now.

Sample descriptions need the actual codec parameters extracted from the frame headers. For H.264 this means parsing the SPS — width, height, profile, level, chroma format. For ProRes it means the codec four-cc (apcn, apch, apcs, etc.) and the width/height/colour primaries from the frame header. We extract these from the first valid frame found in pass 3, which is why pass 4 depends on pass 3's output.

The MOOV atom builder runs a fixed-iteration size resolution loop because there's a circular dependency: the size of the MOOV depends on the byte offsets of mdat and child atoms; those offsets depend on the size of the MOOV; the size of the MOOV depends on... You converge by iterating. We use a fixed iteration count chosen empirically — too few and the offsets are wrong; too many and you waste cycles.

Pass 5: audio probing

Audio is where most "almost-recovered" files fall apart. The video plays, but the audio is static, or it's at the wrong sample rate, or it's offset by half a second from the picture. There's a reason for each.

  • Wrong byte order. External recorders like Atomos and Ninja write little-endian 24-bit PCM. Some tools assume big-endian ("network byte order") and swap unconditionally. We detect endianness by sampling the data and checking which interpretation has a plausible sample distribution. We don't swap unless we're confident.
  • Wrong sample rate / channel count. Camera firmware sometimes records 48 kHz stereo but writes a 44.1 kHz mono header. We sniff actual sample distribution to confirm or override the declared metadata.
  • Audio chunk placement. Audio data is interleaved with video in mdat, but where exactly the audio chunk sits relative to the video group matters for sync. Place audio chunks at the end of their video group instead of the start, and you get a roughly half-second offset that no amount of stretch-to-fit will fix. We learned this on one specific Atomos firmware and it turned out to apply more broadly.
  • Outlier chunks. Most chunks have a dominant size driven by the frame rate and sample format (e.g. about 144 KB for 24-bit stereo 48 kHz at 50 fps). Some chunks deviate — we filter those out rather than letting them desync the rest of the audio track.

The audio probe outputs sample rate, channel count, byte order, and a chunk-sizing strategy. Pass 6 uses these to build the audio track in MOOV.

Pass 6: container rebuild & preview

Pass 6 takes the MOOV synthesised in passes 4–5 and writes the output file:

  1. Copy ftyp (or synthesise one if missing).
  2. Write a freshly built MOOV atom referencing the existing mdat.
  3. Append a free atom for any padding the camera left.
  4. The mdat itself is not copied. It's referenced in place — the recovered file is a thin index pointing at the original bytes. (For the user-facing download, we serve the rebuilt-MOOV-prefixed bytes as one stream; nothing is moved or rewritten on disk in the heavy sense.)

We then transcode a 5-second preview. The preview is always H.264, yuv420p for browser compatibility. ProRes source material is yuv422p10le natively — a browser <video> tag will not play that, and the user will see a black square instead of their video. Forcing yuv420p in the preview path is one of those small details that determines whether a customer buys or bounces.

The full recovery uses the user-confirmed configuration from the preview phase. If the user adjusted parameters interactively (frame rate, audio swap, codec hint), those parameters are stored in client_preview_log.json and the checkout step replays the preview's settings on the full file.

What makes the hard cases hard

Three categories cause most recovery failures, in our data:

  1. Mixed-codec files where someone concatenated two recordings with different codecs (.MOV from a GoPro stitched with .MP4 from a phone, that kind of thing). The frame detector locks onto the dominant codec and the second half decodes as garbage. We escalate these to manual review.
  2. Camera-specific quirks we haven't seen yet. Every couple of weeks a new camera firmware writes its mdat slightly differently and our detectors miss frames or mis-classify keyframes. The fix is short — a new validator threshold, a new stsd field — but only after we see the file. This is why we offer free recovery in exchange for a corpus contribution: every file we see makes the next recovery better.
  3. Files where the corruption is partial but in the wrong place. A bit-flip in the middle of mdat is fine — we lose a frame or two. A bit-flip in the SPS header before the first IDR is catastrophic — we don't know the codec parameters. Sometimes we can extract parameters from a later IDR; sometimes the user has to give us a reference clip from the same camera.

What we can promise and what we can't

We can promise:

  • The preview always reflects the actual recovery output. If the preview looks right, the full file looks right.
  • We never run a recovery you didn't authorise. Every paid recovery uses the configuration the user confirmed in the preview step.
  • Your file isn't used to train anything, sold, or shared. Profile-only persistence: we keep a sanitised diagnosis (codec, container, corruption type, dimensions) so the engine improves; we do not keep the bytes.

We can't promise:

  • Every file is recoverable. Some are too damaged. We tell you that quickly and don't charge.
  • A specific camera or codec works on day one. The codec registry grows file by file. If we haven't seen your camera, we'll try it for free and ship a fix if it's a tractable bug.
  • 100% bit-perfect output. We rebuild the index, not the codec data. If the codec data has a flaw, the recovered file inherits it. In practice, this rarely matters — modern codecs are surprisingly robust to a handful of damaged bytes — but it's worth knowing.

Appendix: tools we use that you can use

  • ffmpeg / ffprobe — the entire video industry runs on this. If ffprobe gives you a sensible answer about your file, half the recovery is done already.
  • mediainfo — the second opinion. Different parser philosophy.
  • untrunc — open-source tool specifically for recovering truncated MP4/MOV files. Worth trying first on a simple "battery died" case before paying anyone (including us).
  • mp4dump / mp4info (from the Bento4 toolkit) — atom-level inspection. If you want to look inside your file structurally without writing code.

If any of these solve your problem on their own, you don't need us. We exist for the cases where they don't — files with no MOOV at all, files with non-standard audio, files from cameras whose firmware does something weird, files where you don't know the codec.


If you have a corrupted file and you've gotten this far, you're our target user. Upload it and we'll show you what comes back. Free preview, no signup, no card up front.

Free preview · No commitment

Ready to recover your video?

Upload your file — get a free 5-second preview in about a minute. Pay only if the preview looks right.

Upload your video →