How can I find video rotation and rotate the clip accordingly using moviepy?

5.6k views Asked by At

I'm using moviepy to import some videos, but the videos that should be in portrait mode are imported in landscape. I need to check whether the rotation has been changed, and if it has, rotate it back.

Is this functionality built into moviepy? If not, how else can I check it?

1

There are 1 answers

6
Tom Burrows On BEST ANSWER

I have now worked out a solution to the question.

Moviepy, for some reason, rotates portrait videos to landscape when it imports them. In order to automatically import them back, you need to find the video metadata that records its rotation, then rotate the video as needed. The way I did this is with ffprobe, which can be installed for windows using this youtube tutorial. Note that you will need to delete the ffmpeg.exe file in ffmpeg/bin, as you only need ffprobe.exe. If you don't delete ffmpeg.exe, moviepy will use that one instead of the one it is supposed to be using. This led to some strange problems on my system.

Once ffprobe is installed, you can run the following python function for each video being imported:

import subprocess
import shlex
import json

def get_rotation(file_path_with_file_name):
    """
    Function to get the rotation of the input video file.
    Adapted from gist.github.com/oldo/dc7ee7f28851922cca09/revisions using the ffprobe comamand by Lord Neckbeard from
    stackoverflow.com/questions/5287603/how-to-extract-orientation-information-from-videos?noredirect=1&lq=1

    Returns a rotation None, 90, 180 or 270
    """
    cmd = "ffprobe -loglevel error -select_streams v:0 -show_entries stream_tags=rotate -of default=nw=1:nk=1"
    args = shlex.split(cmd)
    args.append(file_path_with_file_name)
    # run the ffprobe process, decode stdout into utf-8 & convert to JSON
    ffprobe_output = subprocess.check_output(args).decode('utf-8')
    if len(ffprobe_output) > 0:  # Output of cmdis None if it should be 0
        ffprobe_output = json.loads(ffprobe_output)
        rotation = ffprobe_output

    else:
        rotation = 0

    return rotation

This calls the ffprobe command ffprobe -loglevel error -select_streams v:0 -show_entries stream_tags=rotate -of default=nw=1:nk=1 your_file_name.mp4, then returns the rotation metadata for that file.

Then call the following function which calls the above function, and rotates the clip you pass to it. Note that the argument clip is a movie VideoFileClip object, and the argument file_path is the complete path to the file that clip is (e.g. file_path could be /usr/local/documents/mymovie.mp3)

from moviepy.editor import *
def rotate_and_resize(clip, file_path):
    rotation = get_rotation(file_path)
    if rotation == 90:  # If video is in portrait
        clip = vfx.rotate(clip, -90)
    elif rotation == 270:  # Moviepy can only cope with 90, -90, and 180 degree turns
        clip = vfx.rotate(clip, 90)  # Moviepy can only cope with 90, -90, and 180 degree turns
    elif rotation == 180:
        clip = vfx.rotate(clip, 180)

    clip = clip.resize(height=720)  # You may want this line, but it is not necessary 
    return clip