在python中使用ffmpeg获取视频时长
Posted
技术标签:
【中文标题】在python中使用ffmpeg获取视频时长【英文标题】:Using ffmpeg to obtain video durations in python 【发布时间】:2015-09-10 13:29:05 【问题描述】:我已经在我的 PC 上使用 pip ffprobe 命令安装了 ffprobe,并从 here 安装了 ffmpeg。
但是,我在运行here 列出的代码时仍然遇到问题。
我尝试使用以下代码失败。
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
有谁知道怎么回事?我没有正确引用目录吗?我需要确保.py
和视频文件位于特定位置吗?
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)
如果问题有点基本,我们深表歉意。我所需要的只是能够遍历一组视频文件并获得它们的开始时间和结束时间。
谢谢!
【问题讨论】:
【参考方案1】:没有必要迭代FFprobe
的输出。 one simple command 只返回输入文件的持续时间:
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 <input_video>
您可以使用以下方法来获取持续时间:
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)
【讨论】:
@JavierC.H.不要使用字符串格式来制作 bash 命令。如果input_video
是由用户提供的,那么您就可以让他们轻松地从您的 Python 代码中运行任意 bash 命令。
是否可以将其作为 HH:MM:SS 格式作为ffmpeg -i file.mkv 2>&1 | grep -o -P "(?<=Duration: ).*?(?=,)"
的输出
对于持续时间为N/A
的任何人,您可能需要重新打包文件以添加标题。见***.com/a/40117749/1717535【参考方案2】:
我建议使用 FFprobe(与 FFmpeg 一起提供)。
Chamath 给出的答案非常接近,但最终对我来说失败了。
请注意,我使用的是 Python 3.5 和 3.6,这对我有用。
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)
如果你想把这个函数放到一个类中并在 Django (1.8 - 1.11) 中使用它,只需更改一行并将这个函数放入你的类中,如下所示:
def get_duration(file):
到:
def get_duration(self, file):
注意:在本地使用相对路径对我有用,但生产服务器需要绝对路径。您可以使用os.path.abspath(os.path.dirname(file))
获取视频或音频文件的路径。
【讨论】:
shell=True
不推荐【参考方案3】:
我认为 Chamath 的第二条评论回答了这个问题:您的脚本中某处有一个奇怪的字符,可能是因为您使用的是 ` 而不是p>
作为备注,对于您正在做的事情,您也可以尝试MoviePy,它会像您一样解析 ffmpeg 输出(但也许将来我会使用 Chamath 的 ffprobe 方法,它看起来更干净):
import moviepy.editor as mp
duration = mp.VideoFileClip("my_video.mp4").duration
【讨论】:
【参考方案4】:使用ffprobe
更新解决方案,基于@llogan 指导和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()
由于stderr
输出导致的脆弱解决方案:
ffmpeg
的stderr
输出不适用于机器解析和 被认为是脆弱的。
我从以下文档 (https://codingwithcody.com/2014/05/14/get-video-duration-with-ffmpeg-and-python/) 和 https://***.com/a/6239379/2402577 获得帮助
其实sed是不需要的:
ffmpeg -i file.mp4 2>&1 | grep -o -P "(?<=Duration: ).*?(?=,)"
您可以使用以下方法获取HH:MM:SS
格式的持续时间:
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()
两者的示例输出:01:37:11.83
【讨论】:
如果单独使用 ffprobe,则grep
是不必要的。例如 ee How to extract duration time from ffmpeg output? 和 How to get video duration in seconds?。此外,ffmpeg 的 stderr 输出不适用于机器解析,被认为是脆弱的。这就是 ffprobe 的用途。
@llogan:感谢您的意见。我已根据您的评论更新了我的答案。为了更好地理解,我保留了旧答案。【参考方案5】:
您是否尝试过添加编码?正如查马斯所说,这个错误是典型的。 将 utf-8 编码添加到您的脚本头中:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
【讨论】:
【参考方案6】:我喜欢用 ffmpeg 构建一个共享库,并在 python 中加载它。 C++代码:
#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;
然后用gcc编译,得到一个共享库。 Python代码:
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')
它在我的 python 程序中运行良好。
【讨论】:
【参考方案7】:Python 代码
<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:\s1(?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>
【讨论】:
解析来自ffmpeg
的输出是脆弱的,不适合脚本使用。请改用ffprobe
,如Chamath's answer所示。
您可能应该添加一个解释,说明这对 ffmpeg/ffprobe 和 Python 的新手有什么作用。【参考方案8】:
我们还可以使用 ffmpeg 来获取任何视频或音频文件的时长。
要安装 ffmpeg,请遵循 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:\s1(?P<hours>\d+?):(?P<minutes>\d+?):(?P<seconds>\d+\.\d+?),", stdout, re.DOTALL).groupdict()
print (matches['hours'])
print (matches['minutes'])
print (matches['seconds'])
【讨论】:
解析来自ffmpeg
的输出是脆弱的,并不是为脚本使用而设计的。请改用ffprobe
,如Chamath's answer所示。以上是关于在python中使用ffmpeg获取视频时长的主要内容,如果未能解决你的问题,请参考以下文章