Progress not working
pdc1 opened this issue · comments
Hello,
I am using python 3.11 (Windows), ffmpeg 6.1.1 (cygwin), python-ffmpeg 2.0.10.
I tried a simple example converting MP3 to Opus, and it worked just fine. Then I added @ffmpeg.on("progress")
and now it appears to hang (no CPU usage, no progress displayed).
When I kill the run with ^C, I get:
d:/cygwin64/bin/ffmpeg.exe -loglevel warning -i input.mp3 -codec:a libopus -b:a 32k -application voip output.opus
Traceback (most recent call last):
File "D:\Projects\MiscScripts\process_audiobooks.py", line 47, in <module>
main()
File "D:\Projects\MiscScripts\process_audiobooks.py", line 43, in main
ffmpeg.execute()
File "C:\Users\Paul\AppData\Local\Programs\Python\Python311\Lib\site-packages\ffmpeg\ffmpeg.py", line 188, in execute
done, pending = concurrent.futures.wait(futures, return_when=concurrent.futures.FIRST_EXCEPTION)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Paul\AppData\Local\Programs\Python\Python311\Lib\concurrent\futures\_base.py", line 305, in wait
waiter.event.wait(timeout)
File "C:\Users\Paul\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 622, in wait
signaled = self._cond.wait(timeout)
^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Paul\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 320, in wait
waiter.acquire()
KeyboardInterrupt
My script is based on one of the examples:
from ffmpeg import FFmpeg, Progress
FFMPEG_EXE = "d:/cygwin64/bin/ffmpeg.exe"
def main():
ffmpeg = (
FFmpeg(FFMPEG_EXE)
.option("loglevel", value="warning") # <- don't do this! (see below)
.input("input.mp3")
.output(
"output.opus",
{"codec:a": "libopus", "b:a": "32k", "application": "voip"},
)
)
print(" ".join(ffmpeg.arguments))
@ffmpeg.on("progress")
def on_progress(progress: Progress):
print("progress:", progress)
if progress.frame > 200:
ffmpeg.terminate()
ffmpeg.execute()
if __name__ == "__main__":
main()
I spent some time investigating and found that I needed .option("y")
to overwrite the output file (which existed from an earlier test), and I removed the .option("loglevel", value="warning")
so it will output the statistics (oops!). Now the ffmpeg
runs (and completes), but still there are no progress updates.
If I run the ffmpeg command normally, I get status updates like:
size= 768kB time=00:04:16.23 bitrate= 24.6kbits/s speed=39.4x
I notice in ffmpeg / statistics.py
that it looks like the class is expecting frame
, fps
, size
, time
, bitrate
, and speed
, and will not return status unless all are present. My output is missing frame
and fps
.
Might this be a difference in a newer version of ffmpeg? Or perhaps is a function of doing an audio-only operation?
I found that commenting out the check for all fields allowed my example to work! In ffmpeg / statistics.py
:
@dataclass(frozen=True)
class Statistics:
frame: int = 0
fps: float = 0.0
size: int = 0
time: timedelta = field(default_factory=timedelta)
bitrate: float = 0.0
speed: float = 0.0
@classmethod
def from_line(cls, line: str) -> Optional[Self]:
statistics = {key: value for key, value in _pattern.findall(line)}
here -> # if len(statistics) != len(_field_factory):
-> # return None
fields = {key: _field_factory[key](value) for key, value in statistics.items() if value != "N/A"}
return Statistics(**fields)
Looks like some versions of ffmpeg use KiB and some use kB in the size. Changing ffmpeg versions or updating line 19 in statistics.py resolved the issue for me.
Looking at the git history, this was introduced in version 2.0.9. Downgrading to 2.0.8 solves this problem. However, this "feature" is not very useful and I support the above pull request as a long term fix.
Solved on version 2.0.11