Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pause a FFmpeg encoding in a Python Popen subprocess on Windows

I am trying to pause an encode of FFmpeg while it is in a non-shell subprocess (This is important to how it plays into a larger program). This can be done by presssing the "Pause / Break" key on the keyboard by itself, and I am trying to send that to Popen.

The command itself must be cross platform compatible, so I cannot wrap it in any way, but I can send signals or run functions that are platform specific as needed.

I looked at how to send a "Ctrl+Break" to a subprocess via pid or handler and it suggested to send a signal, but that raised a "ValueError: Unsupported signal: 21"

from subprocess import Popen, PIPE
import signal


if __name__ == '__main__':
    command = "ffmpeg.exe -y -i example_video.mkv -map 0:v -c:v libx265 -preset slow -crf 18 output.mkv"
    proc = Popen(command, stdin=PIPE, shell=False)

    try:
        proc.send_signal(signal.SIGBREAK)
    finally:
        proc.wait()

Then attempted to use GenerateConsoleCtrlEvent to create a Ctrl+Break event as described here https://learn.microsoft.com/en-us/windows/console/generateconsolectrlevent

from subprocess import Popen, PIPE
import ctypes


if __name__ == '__main__':
    command = "ffmpeg.exe -y -i example_video.mkv -map 0:v -c:v libx265 -preset slow -crf 18 output.mkv"
    proc = Popen(command, stdin=PIPE, shell=False)

    try:
        ctypes.windll.kernel32.GenerateConsoleCtrlEvent(1, proc.pid)
    finally:
        proc.wait()

I have tried psutil pause feature, but it keeps the CPU load really high even when "paused".

Even though it wouldn't work with the program overall, I have at least tried setting creationflags=CREATE_NEW_PROCESS_GROUP which makes the SIGBREAK not error, but also not pause it. For the Ctrl-Break event will entirely stop the encode instead of pausing it.

like image 497
CasualDemon Avatar asked Dec 05 '20 01:12

CasualDemon


People also ask

How do I pause ffmpeg conversion?

A simple method is to suspend it with ctrl+z. Or you could get the PID with pgrep ffmpeg then use kill -s SIGSTOP to suspend.

How do I close ffmpeg file?

How to stop ffmpeg remotely? You create an empty file that you write 'q' to when you are ready to stop it.

How do I pause FFmpeg while transcoding?

Click F12 ("Pause") and when ready, resume by giving the console window focus and clicking the "Enter" key. This will not let you close the console window or reboot the computer. Sorry, something went wrong. In Windows 10, run ffmpeg from a PowerShell window. Now while transcoding, if you select a block of text the process will be paused.

Why does FFmpeg exit with CODE 255?

This behaviour is actually expected. If you kill ffmpeg process it will exit with code 255 (easily reproducible in the terminal). I don't think introducing a method to kill the process is a good idea though.

What happens when you kill FFmpeg process?

If you kill ffmpeg process it will exit with code 255 (easily reproducible in the terminal). I don't think introducing a method to kill the process is a good idea though.

What is the use of encoding parameter In Popen?

Popen has an encoding parameter that can be used instead of text=True or universal_newlines=True. Python supports two locale-dependent encodings in Windows. “mbcs” (alias “ansi”) is the process ANSI codepage, and “oem” is the process OEM codepage.


Video Answer


1 Answers

Linux/Unix solution:

import subprocess, os, signal
# ...
# Start the task:
proc = subprocess.Popen(..., start_new_session=True)
# ...
def send_signal_to_task(pid, signal):
        #gpid = os.getpgid(pid)  # WARNING This does not work
        gpid = pid  # But this does!
        print(f"Sending {signal} to process group {gpid}...")
        os.killpg(gpid, signal)
# ...
# Pause and resume:
send_signal_to_task(proc.pid, signal.SIGSTOP)
send_signal_to_task(proc.pid, signal.SIGCONT)

Notice start_new_session=True in Popen call and using of os.killpg instead of os.kill in send_signal_to_task function. The reason for this is the same as as reason for why you have large CPU usage even in paused state as you reported. ffmpeg spawns a number of child processes. start_new_session=True will create a new process group and os.killpg sends signal to all processes in group.

Cross-platform solution is supposed to be provided by psutil module but you probably should research whether it supports process groups as the variant above:

>>> import psutil
>>> psutil.pids()
[1, 2, 3, 4, 5, 6, 7, 46, 48, 50, 51, 178, 182, 222, 223, 224, 268, 1215,
 1216, 1220, 1221, 1243, 1244, 1301, 1601, 2237, 2355, 2637, 2774, 3932,
 4176, 4177, 4185, 4187, 4189, 4225, 4243, 4245, 4263, 4282, 4306, 4311,
 4312, 4313, 4314, 4337, 4339, 4357, 4358, 4363, 4383, 4395, 4408, 4433,
 4443, 4445, 4446, 5167, 5234, 5235, 5252, 5318, 5424, 5644, 6987, 7054,
 7055, 7071]
>>> p = psutil.Process(7055)
>>> p.suspend()
>>> p.resume()

Reference: https://pypi.org/project/psutil/

Some pointers on emulating process groups in Windows: Popen waiting for child process even when the immediate child has terminated

like image 182
Roman Pavelka Avatar answered Oct 18 '22 19:10

Roman Pavelka