One of my colleagues was recording a Zoom meeting. The session ended in such a way that the recording was left unfinished, named video2013876469.mp4.tmp. Trying to play it in Videos just gave the error “This file is invalid and cannot be played”. ffplay gave the more helpful error “moov atom not found”. It wasn’t too hard to recover the file, but it did involve a bit of podman knowledge, so I’m writing it up here for posterity.
A bit of DuckDuckGo-ing tells us that the moov atom is, roughly, the index of the video. Evidently Zoom writes it at the end of the file when the recording is stopped cleanly; so if that doesn’t happen, this metadata is missing and the file cannot be played. A little more DuckDuckGo-ing tells us that untrunc can be used to recover such a truncated recording, given an untruncated recording from the same source. Basically it uses the metadata from the complete recording to infer what the metadata should be for the truncated recording, and fill it in.
I built untrunc with podman
as follows:
git clone https://github.com/ponchio/untrunc.git cd untrunc podman build -t untrunc .
I made a fresh directory in my home directory and placed the truncated video and a successful Zoom recording into it:
mkdir ~/tmp # Make the directory world-writable (see later note) chmod 777 ~/tmp cp ~/Downloads/video2013876469.mp4.tmp ~/tmp cp ~/Downloads/zoom_0.mp4 ~/tmp
Now I ran the untrunc container, mounting $HOME/tmp
from my host system to /files
in the container, and passing paths relative to that:
podman run --rm -it -v $HOME/tmp:files \ localhost/untrunc:latest \ /files/zoom_0.mp4 \ /files/video2013876469.mp4.tmp
and off it went:
Reading: /files/zoom_0.mp4 Repair: /files/video2013876469.mp4.tmp Mdat not found! Trying a different approach to locate mdat start Repair: /files/video2013876469.mp4.tmp Backtracked enough! Trying a different approach to locate mdat start Repair: /files/video2013876469.mp4.tmp Mdat not found! […] Found 71359 packets. Found 39648 chunks for mp4a Found 31711 chunks for avc1 Saving to: /files/video2013876469.mp4_fixed.mp4
The fixed file is now at ~/tmp/video2013876469.mp4_fixed.mp4.
Tinkering with the directory permissions was needed because untrunc’s Dockerfile creates a user within the container and runs the entrypoint as that user, rather than running as root. I believe in the Docker world this is considered good practice because root in a Docker container is root on the host, too. However I’m using podman as an unprivileged user; so UID 0 in the container is my user on the host; and the untrunc user in the container ends up mapped to UID 166535 on the host, which doesn’t have write access to the directory I mounted into the container. Honestly I don’t know how you’re supposed to manage this, so I just made the directory world-writable and moved on with my life.