Skip to main content
A user records a video in portrait on their phone. It looks correct on the phone, correct in your browser preview, and correct when you play it back. Then your thumbnail comes out on its side. Or your model sees a person lying down. Or the platform you upload to letterboxes it into the middle of a landscape frame. Nothing is corrupt, and nobody made a mistake. The file is telling the truth and half your stack is not listening.

Phones do not record vertical video

They record landscape video and a note saying which way up it goes. The camera sensor has a fixed orientation. Turning the phone does not turn the sensor, so the frames come off it landscape regardless of how you were holding it. What changes is a small piece of metadata — a rotation, stored in the container — that says “display this rotated 90 degrees.” So a 9:16 clip that fills a phone screen is commonly stored as a 16:9 frame:
That is a landscape file by every measure your code can see. And it is a portrait video. The missing half is here:
Width and height describe the pixels. The rotation describes what to do with them. Reading one without the other gives you a confident wrong answer, which is worse than an error, because nothing fails.

Two places the flag hides

Worth knowing if you write the check yourself: ffmpeg reports rotation in two different places depending on how the file was written. Modern files carry a display matrix in the stream’s side data, which is what stream_side_data=rotation reads above. Older files, and some older muxers, use a rotate tag instead:
Both appear in the wild, sometimes from the same phone model across OS versions. A check that reads only one silently misses about half of them — and the failure looks exactly like footage that was never rotated, so there is nothing to notice. The sign conventions differ between the two, and it usually does not matter: for deciding whether the axes are swapped, only the magnitude counts. 90 and 270 swap width and height; 0 and 180 do not.

Who honours it and who does not

This is the part that makes it hard to spot, because the tools you check your work in are the ones that get it right. The pattern: things that display video honour the flag, because displaying is what the flag is for. Things that read pixels frequently do not. That is why this defect survives review. It is correct everywhere a human looks and wrong where the machine looks.

What it costs you

Frame-extraction and vision pipelines. A pose model handed a person lying along the x-axis does not error. It returns keypoints, with confidence, that are wrong. Detection models trained on upright subjects degrade quietly. This is the expensive one, because the output looks like output. Aspect-ratio gating. If you check that uploads are 9:16 before accepting them into a vertical feed, and you check width < height, you reject every phone video that was recorded correctly and accept ones that were not. Thumbnails. Sideways, and usually noticed after they are already in front of users. Re-encoding without care. Some pipelines strip metadata while re-encoding, which discards the rotation without applying it — turning a correct-but-flagged file into a permanently sideways one.

Fixing it

Bake the rotation into the pixels, so the stored frame and the intended frame stop disagreeing:
There is no rotation option in that command, and that is not an omission. ffmpeg applies the rotation on decode by default, then writes the output without a flag — because the pixels no longer need one. Re-encoding is the fix. Verify by reading the same two things back:
Portrait dimensions, and the rotation flag is gone. Anything reading width and height now gets the right answer without knowing to look for anything else.
If you are writing test fixtures, be aware that recent ffmpeg releases dropped both of the usual ways to add a rotation flag — -metadata:s:v rotate=90 is ignored by the mov muxer, and the displaymatrix bitstream filter is not in every build. Generating a rotated fixture may mean editing the container’s tkhd matrix directly. Reading rotation is easy; writing it is not.

When not to bother

If your pipeline only stores and plays video back, leave it alone. The flag exists precisely so that files stay small and players stay correct, and baking rotation in means a full re-encode with the quality cost that carries. Bake it when something downstream reads pixels rather than displaying them — vision models, frame extraction, hand-rolled thumbnails, or a delivery target you do not control.
FastDrop reports this as rotation_metadata, reading both places the flag hides, and can bake it in and verify the result came back upright. Whether it counts as a defect depends on what you are doing with the footage — Why check footage before you process it covers why the same clip gets different answers for different jobs.