[Python] Graceful stop FFMPEG recording process on Windows

Problem

When we want to stop ffmpeg stream recording on Windows programmatically, these sollution will not work:

# asume p is the subprocess.Popen command call ffmpeg
os.kill(p.pid, signal.CTRL_C_EVENT)  # parent process get kill too, recording file is not playable
# or 
p.terminate()  # not good, recording file is not playable
# or
p.send_signal(signal.CTRL_C_EVENT) # parent process get kill too, recording file is not playable

Solution

We need call ffmpeg in a sperate window then create a ctrl+c event for that window, this way will make ffmpeg graceful save recording file then exit

import os

base_dir = os.path.dirname(os.path.realpath(__file__))
# flag to kill ffmpeg process
stop_ffmpeg = False

# first, create a .bat file to start ffmpeg
# note: some params is not available in old ffmpeg version
command = f'ffmpeg -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect_on_network_error 1 -reconnect_on_http_error 1 -reconnect_delay_max 3 -i https://some-stream-url -t 3600 -c copy -bsf:a aac_adtstoasc "stream-output.mp4"'
cmd_command = f"@echo OFF\ntitle FFMPEG Task: Demo\n{command}"
cmd_file = f"{base_dir}/ffmpeg_demo.bat"
with open(cmd_file, 'w+') as f:
        f.write(cmd_command)

# execute bat file, get process id (PID)
p = subprocess.Popen(cmd_file, stdin=subprocess.PIPE, creationflags=subprocess.CREATE_NEW_CONSOLE)
while not stop_ffmpeg:
        time.sleep(1)
...
# when we want stop recording, set stop_ffmpeg = True
if stop_ffmpeg:
        subprocess.check_call([sys.executable, 'ctrl_c.py', str(self.sub_p.pid)])
        time.sleep(5)
        p.communicate(input=b"y\n")  # ffmpeg is asking `y` to confirm stop process
import ctypes
import sys

kernel = ctypes.windll.kernel32

pid = int(sys.argv[1])
print(f"Send Ctrl+C to pid: {pid}")
kernel.FreeConsole()
kernel.AttachConsole(pid)
kernel.SetConsoleCtrlHandler(None, 1)
kernel.GenerateConsoleCtrlEvent(0, 0)
sys.exit(0)
Tags:
#windows #python #ffmpeg