如何在Python中获取视频的时长?

73
我需要在Python中获取视频的时长。 我需要获取的视频格式是 MP4,Flash视频,AVI和MOV ... 我有一个共享主机解决方案,因此没有FFmpeg支持。
你有什么建议吗?
谢谢!

3
由于您没有FFmpeg(答案无法使用),请查看以下其他答案:[0](https://dev59.com/nW7Xa4cB1Zd3GeqPuNTS /309483),[1](https://dev59.com/Qms05IYBdhLWcg3wQvuq),[2](http://stackoverflow.com/q/11615384/309483),[3](https://dev59.com/jmLVa4cB1Zd3GeqPwnzo)。 - Janus Troelsen
15个回答

87

你可以使用外部命令ffprobe来实现这个目的。具体来说,运行来自FFmpeg Wiki的此Bash命令

import subprocess

def get_length(filename):
    result = subprocess.run(["ffprobe", "-v", "error", "-show_entries",
                             "format=duration", "-of",
                             "default=noprint_wrappers=1:nokey=1", filename],
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT)
    return float(result.stdout)

9
您还可以在-show_streams之后使用-print_format json,然后将结果json.loads成一个漂亮的字典,其中包含所有ffprobe信息。这也需要解析ffprobe打印出的额外信息(尚未找到隐藏它的选项)。 - dennmat
2
@dennmat:-loglevel 不是 -log_level(至少在 ffprobe 2.3.3 中是这样的) - Von
7
Python封装器ffprobe也许很方便,int(float(FFProbe(filename).video[0].duration) * 1000)可以得到毫秒。 - user1472229
3
使用 ffprobe Python 模块,可以通过以下代码获取文件的时长:from ffprobe import FFProbe; FFProbe('path/to/file').streams[0].duration - kmonsoor
2
当我运行这段代码时,出现了“文件未找到”的错误,尽管该文件确实存在。 - FAHAD SIDDIQUI
显示剩余2条评论

74

(2020年答案)

解决方案:

  1. opencv 0.0065秒 ✔
  2. ffprobe 0.0998秒
  3. moviepy 2.8239秒

OpenCV方法:

def with_opencv(filename):
    import cv2
    video = cv2.VideoCapture(filename)

    duration = video.get(cv2.CAP_PROP_POS_MSEC)
    frame_count = video.get(cv2.CAP_PROP_FRAME_COUNT)

    return duration, frame_count

用法: print(with_opencv('my_video.webm'))


其他:

ffprobe 方法:

def with_ffprobe(filename):
    import subprocess, json

    result = subprocess.check_output(
            f'ffprobe -v quiet -show_streams -select_streams v:0 -of json "{filename}"',
            shell=True).decode()
    fields = json.loads(result)['streams'][0]

    duration = fields['tags']['DURATION']
    fps      = eval(fields['r_frame_rate'])
    return duration, fps

moviepy 方法:

def with_moviepy(filename):
    from moviepy.editor import VideoFileClip
    clip = VideoFileClip(filename)
    duration       = clip.duration
    fps            = clip.fps
    width, height  = clip.size
    return duration, fps, (width, height)

9
你的OpenCV方法确实很有趣。不过它似乎容易出错,经常返回0值,显然是由于编解码器的原因。我无法正确地计算单个文件的数量。如果你能稍微解释一下,我会很高兴点赞 =) - Btc Sources
7
似乎可以通过读取帧率来解决: fps = video.get(cv2.CAP_PROP_FPS) duration = frame_count / fps - pzelasko
4
对于我的视频,OpenCV代码返回了0的持续时间。但帧数值是正确的,因此可以调用 fps = video.get(cv2.CAP_PROP_FPS) 并通过 frame_count/fps 获取持续时间。 OpenCV方法比使用ffmpeg的子进程更快,但您需要在系统中安装许多东西才能使其正常工作。 对我来说,'pymediainfo'似乎快了近一倍。 - kostr22
1
OpenCV 不可靠,请参见官方 GitHub 问题 https://github.com/opencv/opencv/issues/15749#issuecomment-796512723。 - Stormsson

38

根据此处报道,您可以使用moviepy模块来获取视频的长度。

from moviepy.editor import VideoFileClip
clip = VideoFileClip("my_video.mp4")
print( clip.duration )

6
ffpobe比moviepy快10倍。 - Soorena
谢谢。对我来说可行。 - tired and bored dev
2
这对我来说足够快速和简单。谢谢。请注意,此代码的输出单位为秒。 - mikey
同样适用于URL。 - tommy.carstensen

17
为了让事情变得更加容易,以下代码将输出转换为JSON格式。
您可以通过使用probe(filename)来使用它,或者通过使用duration(filename)来获取持续时间。
json_info     = probe(filename)
secondes_dot_ = duration(filename) # float number of seconds

它在已经安装了ffprobeUbuntu 14.04上运行。该代码并非为速度或美观而优化,但它可以在我的机器上工作,希望它能有所帮助。
#
# Command line use of 'ffprobe':
#
# ffprobe -loglevel quiet -print_format json \
#         -show_format    -show_streams \
#         video-file-name.mp4
#
# man ffprobe # for more information about ffprobe
#

import subprocess32 as sp
import json


def probe(vid_file_path):
    ''' Give a json from ffprobe command line

    @vid_file_path : The absolute (full) path of the video file, string.
    '''
    if type(vid_file_path) != str:
        raise Exception('Gvie ffprobe a full file path of the video')
        return

    command = ["ffprobe",
            "-loglevel",  "quiet",
            "-print_format", "json",
             "-show_format",
             "-show_streams",
             vid_file_path
             ]

    pipe = sp.Popen(command, stdout=sp.PIPE, stderr=sp.STDOUT)
    out, err = pipe.communicate()
    return json.loads(out)


def duration(vid_file_path):
    ''' Video's duration in seconds, return a float number
    '''
    _json = probe(vid_file_path)

    if 'format' in _json:
        if 'duration' in _json['format']:
            return float(_json['format']['duration'])

    if 'streams' in _json:
        # commonly stream 0 is the video
        for s in _json['streams']:
            if 'duration' in s:
                return float(s['duration'])

    # if everything didn't happen,
    # we got here because no single 'return' in the above happen.
    raise Exception('I found no duration')
    #return None


if __name__ == "__main__":
    video_file_path = "/tmp/tt1.mp4"
    duration(video_file_path) # 10.008

2
在返回 jason.loads(out) 之前,需要将 out 解码为 str。 out = out.decode('utf-8') - Soorena
1
喜欢这个解决方案。 - TaeWoo

17

发现这个新的Python库:https://github.com/sbraz/pymediainfo

要获取时长:

from pymediainfo import MediaInfo
media_info = MediaInfo.parse('my_video_file.mov')
#duration in milliseconds
duration_in_ms = media_info.tracks[0].duration

以上代码已经测试过并且可以正常运行,但是你应该进行更多的检查,因为它在很大程度上依赖于MediaInfo的输出。


需要单独的库(libmediainfo)。 - Tomasz Gandor
这是我找到的最快的,甚至比moviepy或ffprobe还要快。你是个英雄,谢谢!! - DaWe
你应该使用max([float(track.duration) for track in MediaInfo.parse('my_video_file.mov').tracks])来检查所有轨道,因为第一条轨道的长度不一定与整个视频的长度相同。 - Nexarius

13

使用https://github.com/kkroening/ffmpeg-python这个现代化的方法(pip install ffmpeg-python --user),别忘了也要安装ffmpeg

获取视频信息:

import ffmpeg

info=ffmpeg.probe(filename)

print(f"duration={info['format']['duration']}")
print(f"framerate={info['streams'][0]['avg_frame_rate']}")

使用ffmpeg-python包,您还可以轻松创建、编辑和应用视频过滤器。


感谢提及库引用。 - N D Thokare

7
from subprocess import check_output

file_name = "movie.mp4"

#For Windows
a = str(check_output('ffprobe -i  "'+file_name+'" 2>&1 |findstr "Duration"',shell=True)) 

#For Linux
#a = str(check_output('ffprobe -i  "'+file_name+'" 2>&1 |grep "Duration"',shell=True)) 

a = a.split(",")[0].split("Duration:")[1].strip()

h, m, s = a.split(':')
duration = int(h) * 3600 + int(m) * 60 + float(s)

print(duration)

一个踩的评价没关系,但请有礼貌地解释一下原因!上面发布的代码是对OP提出的问题的一个明确的解决方案。 - DeWil
在Windows机器上,这个答案不是假设用户已经将ffprobe添加到了环境变量中吗?我知道你只是想强调Windows上缺少grep的问题,但要使用那个确切的语法,就需要编辑环境变量或者在与ffprobe相同的目录下运行。 - Lovethenakedgun

5

As reported here https://www.reddit.com/r/moviepy/comments/2bsnrq/is_it_possible_to_get_the_length_of_a_video/

you could use the moviepy module

from moviepy.editor import VideoFileClip 
clip = VideoFileClip("my_video.mp4") 
print( clip.duration )
如果你试图获取一个文件夹中多个视频的持续时间,可能会崩溃并显示以下错误: AttributeError: 'AudioFileClip'对象没有属性'reader'
因此,为了避免这种情况,你需要添加


clip.close()

根据这个链接: https://zulko.github.io/moviepy/_modules/moviepy/video/io/VideoFileClip.html

因此代码应该像这样:

from moviepy.editor import VideoFileClip
clip = VideoFileClip("my_video.mp4")
print( clip.duration )
clip.close()

干杯! :)

5
我想分享一个我想出来的函数。这个函数基本上只使用ffprobe参数。
from subprocess import  check_output, CalledProcessError, STDOUT 


def getDuration(filename):

    command = [
        'ffprobe', 
        '-v', 
        'error', 
        '-show_entries', 
        'format=duration', 
        '-of', 
        'default=noprint_wrappers=1:nokey=1', 
        filename
      ]

    try:
        output = check_output( command, stderr=STDOUT ).decode()
    except CalledProcessError as e:
        output = e.output.decode()

    return output


fn = '/app/648c89e8-d31f-4164-a1af-034g0191348b.mp4'
print( getDuration(  fn ) )

输出的持续时间如下:

7.338000

这个解决方案替换了我的以前的代码。只需一次复制粘贴,我的程序就又可以正常工作了。谢谢。 - Xammax

4

这位pymediainfo的回答真的帮了我很多,谢谢。

作为一个初学者,找出缺失的内容(sudo apt install mediainfo),以及其他方法中如何处理属性确实需要花费一些时间(见下文)。

因此,这里提供了一个额外的示例:

# sudo apt install mediainfo
# pip3 install pymediainfo
from pymediainfo import MediaInfo
media_info = MediaInfo.parse('/home/pi/Desktop/a.mp4')
for track in media_info.tracks:
    #for k in track.to_data().keys():
    #    print("{}.{}={}".format(track.track_type,k,track.to_data()[k]))
    if track.track_type == 'Video':
        print("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
        print("{} width                 {}".format(track.track_type,track.to_data()["width"]))
        print("{} height                {}".format(track.track_type,track.to_data()["height"]))
        print("{} duration              {}s".format(track.track_type,track.to_data()["duration"]/1000.0))
        print("{} duration              {}".format(track.track_type,track.to_data()["other_duration"][3][0:8]))
        print("{} other_format          {}".format(track.track_type,track.to_data()["other_format"][0]))
        print("{} codec_id              {}".format(track.track_type,track.to_data()["codec_id"]))
        print("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
    elif track.track_type == 'Audio':
        print("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
        print("{} format                {}".format(track.track_type,track.to_data()["format"]))
        print("{} codec_id              {}".format(track.track_type,track.to_data()["codec_id"]))
        print("{} channel_s             {}".format(track.track_type,track.to_data()["channel_s"]))
        print("{} other_channel_s       {}".format(track.track_type,track.to_data()["other_channel_s"][0]))
        print("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
print("********************************************************************")

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Video width                 1920
Video height                1080
Video duration              383.84s
Video duration              00:06:23
Video other_format          AVC
Video codec_id              avc1
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Audio format                AAC
Audio codec_id              mp4a-40-2
Audio channel_s             2
Audio other_channel_s       2 channels
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接