12

I've installed ffprobe using the pip ffprobe command on my PC, and installed ffmpeg from here.

However, I'm still having trouble running the code listed here.

I try to use the following code unsuccessfully.

SyntaxError: Non-ASCII character '\xe2' in file GetVideoDurations.py
on line 12, but no encoding declared; see
http://python.org/dev/peps/pep-0263/ for details

Does anyone know what's wrong? Am I not referencing the directories correctly? Do I need to make sure the .py and video files are in a specific location?

import subprocess

def getLength(filename):
    result = subprocess.Popen(["ffprobe", "filename"],
    stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
    return [x for x in result.stdout.readlines() if "Duration" in x]

fileToWorkWith = ‪'C:\Users\PC\Desktop\Video.mkv'

getLength(fileToWorkWith)

Apologies if the question is somewhat basic. All I need is to be able to iterate over a group of video files and get their start time and end time.

Thank you!

Martin Brisiak
  • 3,872
  • 12
  • 37
  • 51
OSK
  • 157
  • 1
  • 2
  • 10

9 Answers9

20

There is no need to iterate though the output of FFprobe. There is one simple command which returns only the duration of the input file:

ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 <input_video>

You can use the following method instead to get the duration:

def get_length(input_video):
    result = subprocess.run(['ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', input_video], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    return float(result.stdout)
Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Chamath
  • 2,016
  • 2
  • 21
  • 30
  • @JavierC.H. don't use string formatting for making bash commands. If `input_video` is supplied by the user, you're letting them trivially run arbitrary bash commands from your Python code. – Boris Verkhovskiy Dec 28 '19 at 18:42
  • Is it possible to have it as HH:MM:SS format as output of `ffmpeg -i file.mkv 2>&1 | grep -o -P "(?<=Duration: ).*?(?=,)"` – alper Jun 22 '20 at 13:35
  • For anyone getting a duration of `N/A`, maybe you need to repackage the file to add headers to it. See https://stackoverflow.com/a/40117749/1717535 – Fabien Snauwaert Sep 02 '20 at 12:54
12

I'd suggest using FFprobe (comes with FFmpeg).

The answer Chamath gave was pretty close, but ultimately failed for me.

Just as a note, I'm using Python 3.5 and 3.6 and this is what worked for me.

import subprocess 

def get_duration(file):
    """Get the duration of a video using ffprobe."""
    cmd = 'ffprobe -i {} -show_entries format=duration -v quiet -of csv="p=0"'.format(file)
    output = subprocess.check_output(
        cmd,
        shell=True, # Let this run in the shell
        stderr=subprocess.STDOUT
    )
    # return round(float(output))  # ugly, but rounds your seconds up or down
    return float(output)

If you want to throw this function into a class and use it in Django (1.8 - 1.11), just change one line and put this function into your class, like so:

def get_duration(file):

to:

def get_duration(self, file):

Note: Using a relative path worked for me locally, but the production server required an absolute path. You can use os.path.abspath(os.path.dirname(file)) to get the path to your video or audio file.

Kalob Taulien
  • 1,817
  • 17
  • 22
8

Using the python ffmpeg package (https://pypi.org/project/python-ffmpeg)

import ffmpeg
duration = ffmpeg.probe(local_file_path)["format"]["duration"]

where local_file_path is a relative or absolute path to your file.

Jorgu
  • 137
  • 1
  • 11
  • 1
    Should be the accepted answer. Works under Python 3.9 and 3.11 and does not need subprocess output checking – Fipsi Dec 23 '22 at 13:23
2

I think Chamath's second comment answers the question: you have a strange character somewhere in your script, either because you are using a ` instead of a ' or you have a word with non-english accents, something like this.

As a remark, for what you are doing you can also try MoviePy which parses the ffmpeg output like you do (but maybe in the future I'll use Chamath's ffprobe method it looks cleaner):

import moviepy.editor as mp
duration =  mp.VideoFileClip("my_video.mp4").duration
Zulko
  • 3,540
  • 1
  • 22
  • 18
2

Updated solution using ffprobe based on @llogan guidance with the pointed link:

import subprocess

def get_duration(input_video):
    cmd = ["ffprobe", "-i", input_video, "-show_entries", "format=duration",
           "-v", "quiet", "-sexagesimal", "-of", "csv=p=0"]
    return subprocess.check_output(cmd).decode("utf-8").strip()

Fragile Solution due to stderr output:

the stderr output from ffmpeg is not intended for machine parsing and is considered fragile.

I get help from the following documentation (https://codingwithcody.com/2014/05/14/get-video-duration-with-ffmpeg-and-python/) and https://stackoverflow.com/a/6239379/2402577

Actually, sed is unnecessary: ffmpeg -i file.mp4 2>&1 | grep -o -P "(?<=Duration: ).*?(?=,)"


You can use the following method to get the duration in HH:MM:SS format:

import subprocess

def get_duration(input_video):
    # cmd: ffmpeg -i file.mkv 2>&1 | grep -o -P "(?<=Duration: ).*?(?=,)"
    p1 = subprocess.Popen(['ffmpeg',  '-i', input_video], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    p2 = subprocess.Popen(["grep",  "-o", "-P", "(?<=Duration: ).*?(?=,)"], stdin=p1.stdout, stdout=subprocess.PIPE)
    p1.stdout.close()
    return p2.communicate()[0].decode("utf-8").strip()

Example output for both: 01:37:11.83

alper
  • 2,919
  • 9
  • 53
  • 102
  • `grep` is unnecessary if you use ffprobe alone. For examples ee [How to extract duration time from ffmpeg output?](https://stackoverflow.com/a/22243834/) and [How to get video duration in seconds?](https://superuser.com/a/945604/). Also, the stderr output from ffmpeg is not intended for machine parsing and is considered fragile. That's what ffprobe is for. – llogan Jun 22 '20 at 17:12
  • @llogan: Thank you for your input. I have updated my answer based on your comment. I kept the old answer for better understanding. – alper Jun 22 '20 at 18:05
0

Have you tried adding the encoding? That error is typical of that, as Chamath said. Add the utf-8 encoding to your script header:

#!/usr/bin/env python
# -*- coding: utf-8 -*- 
0

I like to build a shared library with ffmpeg, and load it in python.
C++ code:

#ifdef __WIN32__
#define LIB_CLASS __declspec(dllexport)
#else
#define LIB_CLASS
#endif
extern "C" {
#define __STDC_CONSTANT_MACROS
#include "libavformat/avformat.h"
}
extern "C" LIB_CLASS int64_t getDur(const char* url) {
    AVFormatContext* pFormatContext = avformat_alloc_context();
    if (avformat_open_input(&pFormatContext, url, NULL, NULL)) {
        avformat_free_context(pFormatContext);
        return -1;
    }
    int64_t t = pFormatContext->duration;
    avformat_close_input(&pFormatContext);
    avformat_free_context(pFormatContext);
    return t;
}

Then use gcc to compile it and get a shared library.
Python code:

from ctypes import *
lib = CDLL('/the/path/to/your/library')
getDur = lib.getDur
getDur.restype = c_longlong
duration = getDur('the path/URL to your file')

It works well in my python program.

lifegpc
  • 21
  • 2
-1

Python Code

<code>
cmnd = ['/root/bin/ffmpeg',  '-i', videopath]
process = subprocess.Popen(cmnd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, stderr = process.communicate()

#This matches regex to get the time in H:M:S format
matches = re.search(r"Duration:\s{1}(?P<hours>\d+?):(?P<minutes>\d+?):(?P<seconds>\d+\.\d+?),", stdout, re.DOTALL).groupdict()
t_hour = matches['hours']
t_min  = matches['minutes']
t_sec  = matches['seconds']

t_hour_sec = int(t_hour) * 3600
t_min_sec = int(t_min) * 60
t_s_sec   = int(round(float(t_sec)))

total_sec = t_hour_sec + t_min_sec + t_s_sec

#This matches1 is to get the frame rate of a video
matches1 = re.search(r'(\d+) fps', stdout)
frame_rate = matches1.group(0) // This will give 20fps
frame_rate = matches1.group(1) //It will give 20

</code>
upendra
  • 339
  • 4
  • 20
  • Parsing the output from `ffmpeg` is fragile and is not designed to be used by scripts. Use `ffprobe` instead as shown in [Chamath's answer](http://stackoverflow.com/a/31025482/1109017). – llogan Mar 14 '17 at 17:22
  • You should probably add an explanation as to what this is doing for people who are new with ffmpeg/ffprobe and Python. – Kalob Taulien Nov 16 '18 at 15:39
-2

we can also use ffmpeg to get the duration of any video or audio files.

To install ffmpeg follow this link

import subprocess
import re

process = subprocess.Popen(['ffmpeg',  '-i', path_of_video_file], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, stderr = process.communicate()
matches = re.search(r"Duration:\s{1}(?P<hours>\d+?):(?P<minutes>\d+?):(?P<seconds>\d+\.\d+?),", stdout, re.DOTALL).groupdict()

print (matches['hours'])
print (matches['minutes'])
print (matches['seconds'])
  • Parsing the output from `ffmpeg` is fragile and is not designed to be used by scripts. Use `ffprobe` instead as shown in [Chamath's answer](http://stackoverflow.com/a/31025482/1109017). – llogan Mar 14 '17 at 17:21