在 Windows 上的 Python Popen 子进程中暂停 FFmpeg 编码
Posted
技术标签:
【中文标题】在 Windows 上的 Python Popen 子进程中暂停 FFmpeg 编码【英文标题】:Pause a FFmpeg encoding in a Python Popen subprocess on Windows 【发布时间】:2021-03-17 01:31:38 【问题描述】:我正在尝试暂停 FFmpeg 在 非 shell 子进程中的编码(这对于它如何在更大的程序中发挥作用很重要)。这可以通过单独按键盘上的“暂停/中断”键来完成,我正在尝试将其发送到 Popen。
命令本身必须是跨平台兼容的,所以我不能以任何方式包装它,但我可以根据需要发送信号或运行特定于平台的函数。
我查看了how to send a "Ctrl+Break" to a subprocess via pid or handler,它建议发送一个信号,但这引发了“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()
然后尝试使用 GenerateConsoleCtrlEvent 创建 Ctrl+Break 事件,如此处所述https://docs.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()
我尝试过psutil
暂停功能,但即使在“暂停”时,它也会使 CPU 负载保持在很高的水平。
即使它不能与整个程序一起工作,我至少尝试过设置creationflags=CREATE_NEW_PROCESS_GROUP
,这使得 SIGBREAK 不会出错,但也不会暂停它。因为 Ctrl-Break 事件将完全停止编码而不是暂停它。
【问题讨论】:
有什么解决办法吗?我一直在努力解决这个问题。 我认为发送c
到stdin 会导致显示命令菜单,效果是一样的。也许proc.stdin.write(b'c');proc.stdin.flush()
然后只是发送一个输入到标准输入简历?我认为这可能行得通?
@sytech Pressing c
确实会暂停它以等待过滤器输入,并且可以跨平台工作,但存在导致一个 CPU 保持峰值的相同问题。而在 powershell 中使用 pause
按钮或在 linux 上使用 kill -STOP
可以让它们全部释放。然而,这可能是最好的答案,我只是在寻找不可能的答案。
@CasualDemon 在我的机器中,psutil
工作正常,并且在进程暂停时没有使用 CPU。在 Windows 中,PsSuspend 也可以使用。我测试的方式是在暂停和恢复之间添加sleep(60)
。
【参考方案1】:
Linux/Unix 解决方案:
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)
注意start_new_session=True
在Popen
调用中并在send_signal_to_task
函数中使用os.killpg
而不是os.kill
。其原因与您报告的即使处于暂停状态也有大量 CPU 使用率的原因相同。 ffmpeg
产生许多子进程。 start_new_session=True
将创建一个新的进程组,os.killpg
向组中的所有进程发送信号。
跨平台解决方案应该由psutil
模块提供,但您可能应该研究它是否支持进程组作为上述变体:
>>> 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()
参考:https://pypi.org/project/psutil/
关于在 Windows 中模拟进程组的一些提示:Popen waiting for child process even when the immediate child has terminated
【讨论】:
以上是关于在 Windows 上的 Python Popen 子进程中暂停 FFmpeg 编码的主要内容,如果未能解决你的问题,请参考以下文章
为啥带有 shell=True 的 subprocess.Popen() 在 Linux 和 Windows 上的工作方式不同?
python, windows : 用 shlex 解析命令行
如何在 Windows 上使用带有内置命令的 subprocess.Popen
python 使用``subprocess.Popen``修复python 2.7 windows unicode问题。