Community support

· 4 min read

Rotate a video with FFmpeg, no re-encoding

Using -display_rotation with -c copy to write MP4 rotation metadata, and why the older -metadata:s:v rotate=N recipe silently does nothing now.

ffmpeg -display_rotation:v:0 90 -i bbb5.mp4 -c copy rotated.mp4

Requires FFmpeg 6.0 or newer (February 2023). Stream copy, no re-encoding — finishes in milliseconds regardless of file length. Throughout, bbb5.mp4 is shorthand for big_buck_bunny_720p_5sec.mp4; substitute your own file.

The angle is in counter-clockwise degrees. For 90° clockwise (the usual “phone video plays on its side” fix), use -90 or 270 in place of 90.

Three frames from the same Big Buck Bunny source: original landscape on the left, output of -display_rotation:v:0 90 (counter-clockwise) in the middle, output of -display_rotation:v:0 -90 (clockwise) on the right.

Left: original. Middle: -display_rotation:v:0 90 (90° counter-clockwise). Right: -display_rotation:v:0 -90 (90° clockwise).

Why this works, and why the old recipe doesn’t

-c copy doesn’t decode frames. Rotation lives in display-matrix side data attached to the video stream; the muxer writes that side-data into the container header and packets pass through untouched. Martin Storsjö set the carve-out in 2015: “such side data isn’t copied into the output stream (except when doing stream copy).”

The old recipe still circulating online:

ffmpeg -i bbb5.mp4 -c copy -metadata:s:v:0 rotate=90 rotated.mp4

Silently broken on MP4/MOV in current FFmpeg. Exit 0, file written, no rotation flag. The history:

The replacement, -display_rotation, was added by Jan Ekström in October 2022 and shipped with FFmpeg 6.0. From the official ffmpeg manual:

When the video is being transcoded (rather than copied) and -autorotate is enabled, the video will be rotated at the filtering stage. Otherwise, the metadata will be written into the output file if the muxer supports it.

Container coverage

ContainerBehavior
MP4, MOVDisplay matrix in the tkhd track header; -display_rotation writes it
MKVTAG:ROTATE tag (Matroska has no display matrix); both -display_rotation and the legacy -metadata:s:v:0 rotate=N still work
WebM, AVINo rotation field; both syntaxes silently do nothing — re-encode required

The flag isn’t universally honored downstream either. YouTube ignores it on upload, VLC’s trim/convert can strip it, and a videohelp thread catalogs editors that display the file as if no rotation existed: VirtualDub 2, Magix, AviDemux, plus DaVinci Resolve’s proxy generator.

For mirror-image fixes, -display_hflip and -display_vflip are the companion booleans (no value, same input-per-stream syntax):

ffmpeg -display_hflip -i bbb5.mp4 -c copy mirrored.mp4

When you have to re-encode

Three cases push you back to decoding: the downstream player ignores rotation metadata, the container has no rotation field (WebM, AVI), or you’re on FFmpeg 5.x or older. All three end at transpose:

ffmpeg -i bbb5.mp4 -vf transpose=1 -c:a copy rotated.mp4

Modes:

  • 0 — counter-clockwise + vertical flip
  • 1 — clockwise
  • 2 — counter-clockwise
  • 3 — clockwise + vertical flip

If -display_rotation “does nothing” on a file that already has rotation metadata, you’re on FFmpeg 6.0 or 6.1 — upgrade. James Almer’s August 2025 fix made the option actually override existing rotation; before that, on already-rotated files it could be silently ignored.

Related Posts

View All Posts →