使用ffmpeg在Python中提取视频的中间帧? [英] Extract the middle frame of a video in Python using ffmpeg?

查看:342
本文介绍了使用ffmpeg在Python中提取视频的中间帧?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一组视频文件,我想生成缩略图。如何在Python中提取视频的中间帧?



如果我执行:

  ffmpeg -i /tmp/0.mts 

我得到一行返回说:

 持续时间:00:00:04.49,开始:1.016689,比特率:25234 kb / s 

也许我可以在持续时间之后提取时间戳,并以某种方式将其除以2以获取中间帧时间戳,并提取那么?



如果我通过大量视频进行递归,是否有一种方法来保持ffmpeg开放,而无需打开/关闭它即可识别每个文件?

解决方案

事实证明,我不得不或多或少地完全解决相同的事情。
一个额外的要求是不要使用 ffprobe (因为它可能不可用,但 ffmpeg 将) 。

所以这里有一些稀缺文档的代码,应该足够容易理解。如果没有,请不要犹豫。

 #!/ usr / bin / env python 

#任何版权都专用于公有领域。
#http://creativecommons.org/publicdomain/zero/1.0/
#写在2013年 - Nils Maier

import datetime
import os
导入re
导入子流程
import sys


def哪(程序):
有点相当于(1)

def is_executable(fpath):
返回os.path.isfile(fpath)和os.access(fpath,os.X_OK)

如果is_executable(程序) :
返回程序
路径,程序= os.path.split(程序)
如果路径:
返回无
为os.environ中的路径[PATH ] .split(os.pathsep):
path = path.strip(''
exe = os.path.join(路径,程序)
如果is_executable(exe):
return exe
#Windows-style
exe = os.path.join(path,{} .exe.format(program))
如果is_executable(exe):
return exe
return无


def thumb_with_ffmpeg(infile,position = 0.5,ex ecutable = None):

使用ffmpeg

提取缩略图:param infile:文件缩略图。
:参数位置:放置缩略图的位置。默认值:0.5
:param executable:可执行文件使用。默认值:$ PATH
中的第一个ffmpeg:返回:缩略图数据(二进制字符串)


ffmpeg =(可执行文件或ffmpeg)
如果不是ffmpeg:
raise RuntimeError(
找不到ffmpeg可执行文件:{}。format(可执行))
如果位置<0或位置> = 1.0:
raise ValueError(
Position {}不在0.0和1.0之间.format(position))

proc = subprocess.Popen([ffmpeg,-i,infile ],stderr = subprocess.PIPE)
_,result = proc.communicate()
m = re.search(rDuration:\s *(\d +):( \d +): (\d +)\。(\d +),result)
如果不是m:
raise KeyError(无法确定持续时间)
#在这里避免strptime因为它有一些
m = [int(m.group(i))for i in range(1,5)]
duration = datetime.timedelta(hours = m [0],
minutes = m [1],
秒= m [2],
#* 10,因为截断到2位小数位
milliseconds = m [3] * 10
).total_seconds()
target = max ,min(duration * position,duration - 0.1))
target ={:.3f}。format(target)
args = [ffmpeg,
-ss
-i,infile,
-map,v:0,#第一个视频流
-frames:v,1,#1帧
-f,mjpeg,#motion jpeg(aka。 jpeg从1帧)输出
pipe:#pipe输出到stdout
]
proc = subprocess.Popen(args,stdout = subprocess.PIPE,
stderr = subprocess。 PIPE)
输出,_ = proc.communicate()
如果proc.returncode:
raise subprocess.CalledProcessError(proc.returncode,args)
如果没有输出:
raise subprocess.CalledProcessError(-2,args)
返回输出


如果__name__ ==__main__:
来自argparse import ArgumentParser,ArgumentTypeError

def percentage(x):
x = float(x)
如果x < 0.0或x> = 1.0:
raise ArgumentTypeError(
{}不在百分比范围[0.0,1.0)。format(x))
返回x

parser = ArgumentParser(
description =使用ffmpeg从媒体文件中提取缩略图)
parser.add_argument(infile,type = str,help =输入文件)
parser.add_argument(outfile,type = str,help =输出文件)
parser.add_argument( - f,--ffmpeg,type = str,default = None,$ b $
parser.add_argument( - p,--position,type = percentage,default = 0.5,

help =此位置的缩略图(百分比)
默认值:0.5)
args = parser.parse_args()

try:
output = thumb_with_ffmpeg(args.infile,args.position,args.ffmpeg)
with open(args.outfile,wb)作为op:
op.write(输出)
除了例外ex:
print>> sys.stderr,错误:,ex
sys.exit(ex .returncode或1)


I have a collection of video files for which I'd like to generate thumbnails. How can I extract the middle frame of the video in Python?

If I execute:

ffmpeg -i /tmp/0.mts

I get a line returned to the shell that says:

Duration: 00:00:04.49, start: 1.016689, bitrate: 25234 kb/s

Maybe I could extract the timestamp after the duration and somehow divide that by 2 to get the middle frame timestamp, and extract that?

If I'm recursing through a large collection of videos, is there a way to keep ffmpeg "open" without the overhead of opening/closing it to identify each file?

解决方案

As it turns out I had to solve more or less exactly the same thing a while back. One additional requirement was not to use ffprobe (as it might not be available, but ffmpeg would). ffprobe as @LordNeckbeard suggests would have been better for the task of getting the duration otherwise.

So here is some sparsely documented code that should be easy enough to understand nonetheless. If not, don't hesitate to ask.

#!/usr/bin/env python

# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
# Written in 2013 - Nils Maier

import datetime
import os
import re
import subprocess
import sys


def which(program):
    """ Somewhat equivalent to which(1) """

    def is_executable(fpath):
        return os.path.isfile(fpath) and os.access(fpath, os.X_OK)

    if is_executable(program):
        return program
    path, program = os.path.split(program)
    if path:
        return None
    for path in os.environ["PATH"].split(os.pathsep):
        path = path.strip('"')
        exe = os.path.join(path, program)
        if is_executable(exe):
            return exe
        # Windows-style
        exe = os.path.join(path, "{}.exe".format(program))
        if is_executable(exe):
            return exe
    return None


def thumb_with_ffmpeg(infile, position=0.5, executable=None):
    """
    Extract a thumbnail using ffmpeg

    :param infile: File to thumbnail.
    :param position: Position at which to take the thumbnail. Default: 0.5
    :param executable: Executable to use. Default: first "ffmpeg" in $PATH
    :returns: The thumbnail data (binary string)
    """

    ffmpeg = which(executable or "ffmpeg")
    if not ffmpeg:
        raise RuntimeError(
            "Failed to find ffmpeg executable: {}".format(executable))
    if position < 0 or position >= 1.0:
        raise ValueError(
            "Position {} is not between 0.0 and 1.0".format(position))

    proc = subprocess.Popen([ffmpeg, "-i", infile], stderr=subprocess.PIPE)
    _, result = proc.communicate()
    m = re.search(r"Duration:\s*(\d+):(\d+):(\d+)\.(\d+)", result)
    if not m:
        raise KeyError("Cannot determine duration")
    # Avoiding strptime here because it has some issues handling milliseconds.
    m = [int(m.group(i)) for i in range(1, 5)]
    duration = datetime.timedelta(hours=m[0],
                                  minutes=m[1],
                                  seconds=m[2],
                                  # * 10 because truncated to 2 decimal places
                                  milliseconds=m[3] * 10
                                  ).total_seconds()
    target = max(0, min(duration * position, duration - 0.1))
    target = "{:.3f}".format(target)
    args = [ffmpeg,
            "-ss", target,
            "-i", infile,
            "-map", "v:0",     # first video stream
            "-frames:v", "1",  # 1 frame
            "-f", "mjpeg",     # motion jpeg (aka. jpeg since 1 frame) output
            "pipe:"            # pipe output to stdout
            ]
    proc = subprocess.Popen(args, stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE)
    output, _ = proc.communicate()
    if proc.returncode:
        raise subprocess.CalledProcessError(proc.returncode, args)
    if not output:
        raise subprocess.CalledProcessError(-2, args)
    return output


if __name__ == "__main__":
    from argparse import ArgumentParser, ArgumentTypeError

    def percentage(x):
        x = float(x)
        if x < 0.0 or x >= 1.0:
            raise ArgumentTypeError(
                "{} not in percentage range [0.0, 1.0)".format(x))
        return x

    parser = ArgumentParser(
        description="Extract a thumbnail from a media file using ffmpeg")
    parser.add_argument("infile", type=str, help="Input file")
    parser.add_argument("outfile", type=str, help="Output file")
    parser.add_argument("-f", "--ffmpeg", type=str, default=None,
                        help="use this ffmpeg binary, "
                             "default: check $PATH for ffmpeg")
    parser.add_argument("-p", "--position", type=percentage, default=0.5,
                        help="thumbnail at this position (percentage), "
                             "default: 0.5")
    args = parser.parse_args()

    try:
        output = thumb_with_ffmpeg(args.infile, args.position, args.ffmpeg)
        with open(args.outfile, "wb") as op:
            op.write(output)
    except Exception as ex:
        print >>sys.stderr, "Error:", ex
        sys.exit(ex.returncode or 1)

这篇关于使用ffmpeg在Python中提取视频的中间帧?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆