【问题标题】:Extract the middle frame of a video in Python using ffmpeg?使用ffmpeg在Python中提取视频的中间帧?
【发布时间】:2014-06-10 13:19:46
【问题描述】:

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

如果我执行:

ffmpeg -i /tmp/0.mts

我收到一行返回到 shell 的信息:

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

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

如果我正在循环浏览大量视频,有没有办法让 ffmpeg 保持“打开”状态,而无需打开/关闭它来识别每个文件?

【问题讨论】:

    标签: python ffmpeg


    【解决方案1】:

    事实证明,不久前我不得不解决或多或少完全相同的问题。 另一个要求是不要使用ffprobe(因为它可能不可用,但ffmpeg 会)。 @LordNeckbeard 所建议的ffprobe 会更好地完成获得持续时间的任务。

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

    #!/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)
    

    【讨论】:

    • 对于 Python 3,第 56 行应为:m = re.search(r"Duration:\s*(\d+):(\d+):(\d+)\.(\d+)", result.decode('utf-8'))
    猜你喜欢
    • 1970-01-01
    • 2010-11-07
    • 1970-01-01
    • 2022-01-03
    • 2021-09-04
    • 1970-01-01
    • 2020-05-18
    • 2012-05-27
    相关资源
    最近更新 更多