Getting realtime output from ffmpeg to be used in progress bar (PyQt4, stdout)

后端 未结 7 1119
感情败类
感情败类 2020-12-02 10:08

I\'ve looked at a number of questions but still can\'t quite figure this out. I\'m using PyQt, and am hoping to run ffmpeg -i file.mp4 file.avi and get the out

7条回答
  •  挽巷
    挽巷 (楼主)
    2020-12-02 10:48

    Here is a dedicated function that yields the progress in percent, and it works with any ffmpeg command you might already have (as a list of strings):

    for progress in run_ffmpeg_command(["ffmpeg", "-i", "test.mp4", "test2.mp4"])
      print(progress)
    

    This will print 0 through 100.

    The idea is to enable the -progress option, parse the duration from the stderr output and then, once you get the progress time, simply divide it. The code borrows from this Gist.

    import subprocess
    import re
    from typing import Iterator
    
    DUR_REGEX = re.compile(
        r"Duration: (?P\d{2}):(?P\d{2}):(?P\d{2})\.(?P\d{2})"
    )
    TIME_REGEX = re.compile(
        r"out_time=(?P\d{2}):(?P\d{2}):(?P\d{2})\.(?P\d{2})"
    )
    
    
    def to_ms(s=None, des=None, **kwargs) -> float:
        if s:
            hour = int(s[0:2])
            minute = int(s[3:5])
            sec = int(s[6:8])
            ms = int(s[10:11])
        else:
            hour = int(kwargs.get("hour", 0))
            minute = int(kwargs.get("min", 0))
            sec = int(kwargs.get("sec", 0))
            ms = int(kwargs.get("ms", 0))
    
        result = (hour * 60 * 60 * 1000) + (minute * 60 * 1000) + (sec * 1000) + ms
        if des and isinstance(des, int):
            return round(result, des)
        return result
    
    
    def run_ffmpeg_command(cmd: "list[str]") -> Iterator[int]:
        """
        Run an ffmpeg command, trying to capture the process output and calculate
        the duration / progress.
        Yields the progress in percent.
        """
        total_dur = None
    
        cmd_with_progress = [cmd[0]] + ["-progress", "-", "-nostats"] + cmd[1:]
    
        stderr = []
    
        p = subprocess.Popen(
            cmd_with_progress,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            universal_newlines=False,
        )
    
        while True:
            line = p.stdout.readline().decode("utf8", errors="replace").strip()
            if line == "" and p.poll() is not None:
                break
            stderr.append(line.strip())
    
            if not total_dur and DUR_REGEX.search(line):
                total_dur = DUR_REGEX.search(line).groupdict()
                total_dur = to_ms(**total_dur)
                continue
            if total_dur:
                result = TIME_REGEX.search(line)
                if result:
                    elapsed_time = to_ms(**result.groupdict())
                    yield int(elapsed_time / total_dur * 100)
    
        if p.returncode != 0:
            raise RuntimeError(
                "Error running command {}: {}".format(cmd, str("\n".join(stderr)))
            )
    
        yield 100
    

提交回复
热议问题