Я разрабатываю приложение Electron. В этом приложении я создаю процесс Python с путем к файлу в качестве аргумента, а затем сам файл передается в ffmpeg (через модуль ffmpeg-python), а затем проходит через некоторые функции Tensorflow.
Я пытаюсь обработать случай, когда пользователь закрывает приложение Electron, пока идет весь фоновый процесс. Однако из моих тестов кажется, что процесс ffmpeg остается в рабочем состоянии, несмотря ни на что. Я работаю в Windows, смотрю на диспетчер задач и не понимаю, что происходит: при закрытии окна приложения Electron иногда ffmpeg.exe будет отдельным процессом, иногда он остается в Electron группа процессов.
Я заметил, что если я убью процесс Electron, закрыв окно, процесс python также закроется, как только ffmpeg выполнит свою работу, поэтому я думаю, что это работает наполовину. Проблема в том, что ffmpeg выполняет интенсивную работу, и если пользователю нужно закрыть окно, то процесс 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"))
Кто-нибудь знает?
@kesh Спасибо за ваш ответ, я совсем не знаком с IPC, поэтому не уверен, что понимаю вас. Итак, вы говорите, что я должен каким-то образом получить экземпляр popen ffmpeg со стороны скрипта Python, а затем каким-то образом убить экземпляр, как только Python получит сигнал SIGINT/SIGTERM (не уверен, что есть разница), это правильно? У вас есть какие-либо ресурсы относительно того, как я могу это сделать? Я пишу jpg просто потому, что это расширение, которое мне нужно, чтобы ffmpeg дал мне, если я что-то не упустил.
Первое, что я бы попробовал, это вызвать pythonProcess.kill()
, когда пользователь закрывает окно (может быть, событие close
окна браузера? Я давно не использовал электрон). Это также должно убить подпроцесс FFmpeg. Если это не сработает (pythonProcess
мертв, но процесс FFmpeg жив), только тогда подумайте о том, чтобы вернуть Popen
из ffmpeg-python
(вам нужно посмотреть его документацию о том, как вернуть запущенный процесс)
ре: jpg. ну, да, я имел в виду, почему вы не получаете данные кадра напрямую через канал stdout, конвертируете их в массив numpy и передаете их Tensorflow. Это должно быть намного быстрее, чем запись кадров на диск. (это, очевидно, не связано с вашим вопросом, мне просто было любопытно.)
о, мой плохой, я слепой, как обычно. Только сейчас я увидел ваш обратный звонок before-quit
. Как только вы поймете, как получить объект Popen
, см. эта почта, чтобы убить процесс FFmpeg при выходе из Python.
Я пробовал подход pythonProcess.kill()
, но, к сожалению, он не работает. Я попробовал пару вещей и понял, что процесс, который я убиваю, имеет другой PID, чем ffmpeg (сравнивая атрибут .pid с pid в диспетчере задач). Когда я закрываю приложение Electron, я просто убиваю процесс Python, но процесс ffmpeg просто остается в живых. Также я не могу понять закономерность, но либо группа процессов Electron остается открытой (только с ffmpeg.exe внутри нее), либо ffmpeg.exe появляется как отдельный процесс (с тем же PID, что и раньше). Я честно не понимаю.
@kesh Не беспокойся, если честно, большое спасибо за то, что помог мне с этим. Я посмотрю вашу ссылку, когда проснусь завтра, еще раз спасибо и хорошего дня / ночи!
@kesh Наконец-то я нашел решение и поставил его в качестве ответа! Кроме того, чтобы ответить на вопрос: jpg. Честно говоря, я не думал об этом, я тоже новичок в TF, поэтому я думаю, что это то, что я считал лучшим решением в то время, ха-ха. В ближайшее время изменю его в соответствии с вашим предложением, так что большое спасибо за подсказку!
Хорошо, наконец-то я смог это исправить! Поскольку 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 уже делает это за вас.
Итак, проблема решена. :)
Кажется, вам нужен механизм, чтобы убить процесс python от электрона. Процесс ffmpeg должен быть убит вместе с ним в процессе. Если нет, вам нужно получить экземпляр Fmpeg Popen и убить его, когда python получит запрос на завершение от js. Наконец, почему вы пишете файл jpg вместо того, чтобы получать данные напрямую через канал stdout?