FFmpeg is a free and open-source video editing tool capable of trimming, cropping, concatenating, muxing, and transcoding almost any type of media file you throw at it.
It's also a very robust solution for implementing video automation, as we use it extensively in our own video editing API. For this tutorial we'll use FFmpeg 5.1.2, but any recent version will do.
The FFmpeg operation we're looking for is called transpose. To rotate a video clockwise 90 degrees , use the following command:
$ ffmpeg -i input.mp4 -vf "transpose=1" output.mp4
If you want to rotate a video 90 degrees counter-clockwise :
$ ffmpeg -i input.mp4 -vf "transpose=2" output.mp4
To clockwise rotate a video 180 degrees , simply apply the transpose operation twice:
$ ffmpeg -i input.mp4 -vf "transpose=1,transpose=1" output.mp4
The video can also be rotated 90 degrees counter-clockwise , then flipped vertically :
$ ffmpeg -i input.mp4 -vf "transpose=0" output.mp4
This rotates the video 90 degrees clockwise , then flips it vertically :
$ ffmpeg -i input.mp4 -vf "transpose=3" output.mp4
Some video containers allow rotation without re-encoding the original video. This is done by setting metadata in the video that specifies that it should be rotated when viewed. It's then up to the video player to display it rotated. This method won't degrade the video quality any further, as re-encoding always has an impact on the video quality.
This approach has the downside of not being supported by all video players, so if you wish to support a wide range of players, it is probably best to re-encode the video as shown above.
The rotation metadata is supported by MP4 and MOV video containers (files ending in .mp4 or .mov). Here is how to set it:
$ ffmpeg -i input-video.mp4 -metadata:s:v rotate="90" -codec copy output-video.mp4
You can read the metadata from the output video file using the ffprobe command-line utility bundled with FFmpeg. Here you can see that the rotation metadata has been set, but the video stream has remained untouched.
1$ ffprobe output.mp4 -hide_banner 2 3Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'output.mp4': 4 Metadata: 5 minor_version : 512 6 major_brand : isom 7 compatible_brands: isomiso2avc1mp41 8 encoder : Lavf58.76.100 9 Duration: 00:00:16.96, start: 0.000000, bitrate: 3122 kb/s 10 Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p(tv, bt709), 1280x720 [SAR 1:1 DAR 16:9], 3112 kb/s, 29.97 fps, 29.97 tbr, 90k tbn, 180k tbc (default) 11 Metadata: 12 rotate : 90 13 handler_name : VideoHandler 14 vendor_id :  15 Side data: 16 displaymatrix: rotation of -90.00 degrees 17 Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 2 kb/s (default) 18 Metadata: 19 handler_name : Stereo 20 vendor_id :  21