FFmpeg recipe

FFmpeg: Rotate a Video 90° (Lossless Metadata)

Two approaches: change container rotation metadata (lossless, instant, but some players ignore it), or re-encode with the transpose filter (slower, universal).

Command

# Lossless metadata rotation (instant, but some players ignore):
ffmpeg -i input.mp4 -metadata:s:v rotate=90 -c copy -y output_rotated.mp4

# Re-encode (universal):
ffmpeg -i input.mp4 -vf "transpose=1" -c:v libx264 -crf 23 -c:a copy -y output_rotated.mp4
Prefer no terminal?
Use the Video Rotator in your browser
Open Video Rotator →

What each flag does

-iInput file. Can be a video, audio, or image. Repeat for multiple inputs.
-c copyStream-copy: no re-encoding. Output is bit-identical between cut points. Fastest possible.
-c:vVideo codec for output. e.g. libx264, libx265, libvpx-vp9.
-crfConstant Rate Factor — quality target (lower = better, larger file). 23 is visually lossless for libx264.
-c:aAudio codec for output. e.g. aac, libmp3lame, copy.
transpose=10 = 90° counterclockwise + flip, 1 = 90° clockwise, 2 = 90° counterclockwise, 3 = 90° clockwise + flip.

Notes & gotchas

  • For 180°, use -vf "transpose=1,transpose=1" or hflip,vflip.
  • iPhone videos sometimes already have rotation metadata that flips them — check with ffprobe before assuming orientation.

Check it worked

ffprobe -v error -show_entries stream=width,height -of csv=p=0 output.mp4

Width and height swapped relative to the source.

What to change

The metadata version is instant and fragileIt sets a flag and copies the streams. Players that honour the flag rotate; the rest show the original orientation.
transpose bakes it in0 is 90 counter-clockwise, 1 is 90 clockwise, 2 is 90 counter-clockwise with vertical flip, 3 is 90 clockwise with flip.
Clear the old flagIf you rotate with transpose on a file that already has a rotation flag, add -metadata:s:v rotate=0 or it will be rotated twice.

If it fails

The video still appears the original way up.

Why: The file carries a rotation flag in its metadata and the player is applying it on top of your rotation.

Fix: Clear the flag with -metadata:s:v rotate=0 after rotating the pixels.

Rotation had no effect at all with -c copy.

Why: Stream copy cannot change pixels, so only metadata could have changed.

Fix: Re-encode with the transpose filter, or set only the rotation metadata if a flag is all you need.

Related recipes