从 Node 杀死 Python 进程不会杀死 Python 的子进程(子进程是 ffmpeg.exe)
Killing a Python process from Node doesn't kill Python's child process (child process being ffmpeg.exe)
我正在开发 Electron 应用程序。在这个应用程序中,我生成了一个 Python 进程,文件路径作为参数,然后文件本身被传递给 ffmpeg(通过 ffmpeg-python 模块),然后通过一些 Tensorflow 函数.
我正在尝试处理用户在整个后台进程进行时关闭 Electron 应用程序的情况。不过,从我的测试来看,ffmpeg 的进程似乎无论如何都会保持运行。我在 Windows 上,正在查看任务管理器,但我不确定发生了什么:关闭 Electron 应用程序时 window,有时 ffmpeg.exe 将是一个进程,有时它会留在电子进程组中。
我注意到如果我通过关闭 window 来终止 Electron 的进程,一旦 ffmpeg 完成它的工作,python 进程也会关闭,所以我猜这是半工作。问题是,ffmpeg 正在做大量的事情,如果用户需要关闭 window,那么 ffmpeg 进程也需要被杀死。但我无法以任何方式实现。
我已经尝试了一些东西,所以我会粘贴一些代码:
main.js
// retrieve video data
ipcMain.handle('get-games', async (event, arg) => {
const spawn = require('child_process').spawn;
const pythonProcess = spawn('python', ["./backend/predict_games.py", arg]);
// sets pythonProcess as a global variable to be accessed when quitting the app
global.childProcess = pythonProcess;
return new Promise((resolve, reject) => {
let result = "";
pythonProcess.stdout.on('data', async (data) => {
data = String(data);
if (data.startsWith("{"))
result = JSON.parse(data);
});
pythonProcess.on('close', () => {
resolve(result);
})
pythonProcess.on('error', (err) => {
reject(err);
});
})
});
app.on('before-quit', function () {
global.childProcess.kill('SIGINT');
});
predict_games.py
(ffmpeg部分)
def convert_video_to_frames(fps, input_file):
# a few useful directories
local_dir = os.path.dirname(os.path.abspath(__file__))
snapshots_dir = fr"{local_dir}/snapshots/{input_file.stem}"
# creates snapshots folder if it doesn't exist
Path(snapshots_dir).mkdir(parents=True, exist_ok=True)
print(f"Processing: {Path(fr'{input_file}')}")
try:
(
ffmpeg.input(Path(input_file))
.filter("fps", fps=fps)
.output(f"{snapshots_dir}/%d.jpg", s="426x240", start_number=0)
.run(capture_stdout=True, capture_stderr=True)
)
except ffmpeg.Error as e:
print("stdout:", e.stdout.decode("utf8"))
print("stderr:", e.stderr.decode("utf8"))
有人知道吗?
好吧,我终于能够解决这个问题了!由于 ffmpeg-python 只是好旧的 ffmpeg 的绑定集合,可执行文件本身仍然是模块的核心。这也意味着当ffmpeg为运行时,出现类似这样的画面:
...
Metadata:
handler_name : VideoHandler
vendor_id : [0][0][0][0]
Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 159 kb/s (default)
Metadata:
handler_name : SoundHandler
vendor_id : [0][0][0][0]
Stream mapping:
Stream #0:0 (h264) -> fps:default
fps:default -> Stream #0:0 (mjpeg)
...
Press [q] to stop, [?] for help
是幕后发生的事情。一旦意识到这一点,我所要做的就是找到一种方法将 'q' 发送到 ffmpeg 的标准输入。我通过在 window-all-closed
事件中添加此片段来做到这一点:
app.on('window-all-closed', () => {
// writes a 'q' in ffmpeg's terminal to quit ffmpeg's process
// reminder: python gets closed when the Electron app is closed
global.childProcess.stdin.write("q\n");
if (process.platform !== 'darwin') app.quit()
})
与问题中的片段相比,Python 脚本本身没有受到影响,这是我最终修改的唯一内容。现在每次我退出我的 Electron 应用程序时,ffmpeg 都会收到 'q'。 Python 进程不需要手动终止,因为 Electron 已经为您完成了。
所以问题解决了。 :)
我正在开发 Electron 应用程序。在这个应用程序中,我生成了一个 Python 进程,文件路径作为参数,然后文件本身被传递给 ffmpeg(通过 ffmpeg-python 模块),然后通过一些 Tensorflow 函数.
我正在尝试处理用户在整个后台进程进行时关闭 Electron 应用程序的情况。不过,从我的测试来看,ffmpeg 的进程似乎无论如何都会保持运行。我在 Windows 上,正在查看任务管理器,但我不确定发生了什么:关闭 Electron 应用程序时 window,有时 ffmpeg.exe 将是一个进程,有时它会留在电子进程组中。
我注意到如果我通过关闭 window 来终止 Electron 的进程,一旦 ffmpeg 完成它的工作,python 进程也会关闭,所以我猜这是半工作。问题是,ffmpeg 正在做大量的事情,如果用户需要关闭 window,那么 ffmpeg 进程也需要被杀死。但我无法以任何方式实现。
我已经尝试了一些东西,所以我会粘贴一些代码:
main.js
// retrieve video data
ipcMain.handle('get-games', async (event, arg) => {
const spawn = require('child_process').spawn;
const pythonProcess = spawn('python', ["./backend/predict_games.py", arg]);
// sets pythonProcess as a global variable to be accessed when quitting the app
global.childProcess = pythonProcess;
return new Promise((resolve, reject) => {
let result = "";
pythonProcess.stdout.on('data', async (data) => {
data = String(data);
if (data.startsWith("{"))
result = JSON.parse(data);
});
pythonProcess.on('close', () => {
resolve(result);
})
pythonProcess.on('error', (err) => {
reject(err);
});
})
});
app.on('before-quit', function () {
global.childProcess.kill('SIGINT');
});
predict_games.py
(ffmpeg部分)
def convert_video_to_frames(fps, input_file):
# a few useful directories
local_dir = os.path.dirname(os.path.abspath(__file__))
snapshots_dir = fr"{local_dir}/snapshots/{input_file.stem}"
# creates snapshots folder if it doesn't exist
Path(snapshots_dir).mkdir(parents=True, exist_ok=True)
print(f"Processing: {Path(fr'{input_file}')}")
try:
(
ffmpeg.input(Path(input_file))
.filter("fps", fps=fps)
.output(f"{snapshots_dir}/%d.jpg", s="426x240", start_number=0)
.run(capture_stdout=True, capture_stderr=True)
)
except ffmpeg.Error as e:
print("stdout:", e.stdout.decode("utf8"))
print("stderr:", e.stderr.decode("utf8"))
有人知道吗?
好吧,我终于能够解决这个问题了!由于 ffmpeg-python 只是好旧的 ffmpeg 的绑定集合,可执行文件本身仍然是模块的核心。这也意味着当ffmpeg为运行时,出现类似这样的画面:
...
Metadata:
handler_name : VideoHandler
vendor_id : [0][0][0][0]
Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 159 kb/s (default)
Metadata:
handler_name : SoundHandler
vendor_id : [0][0][0][0]
Stream mapping:
Stream #0:0 (h264) -> fps:default
fps:default -> Stream #0:0 (mjpeg)
...
Press [q] to stop, [?] for help
是幕后发生的事情。一旦意识到这一点,我所要做的就是找到一种方法将 'q' 发送到 ffmpeg 的标准输入。我通过在 window-all-closed
事件中添加此片段来做到这一点:
app.on('window-all-closed', () => {
// writes a 'q' in ffmpeg's terminal to quit ffmpeg's process
// reminder: python gets closed when the Electron app is closed
global.childProcess.stdin.write("q\n");
if (process.platform !== 'darwin') app.quit()
})
与问题中的片段相比,Python 脚本本身没有受到影响,这是我最终修改的唯一内容。现在每次我退出我的 Electron 应用程序时,ffmpeg 都会收到 'q'。 Python 进程不需要手动终止,因为 Electron 已经为您完成了。
所以问题解决了。 :)