Python调用ffmpeg录屏:跨进程发送输入停止录屏失败求助
问题:跨脚本向FFmpeg进程发送输入停止录屏失败
我用FFmpeg实现自定义录屏功能,分为两个脚本:
- 脚本A:启动录屏,创建FFmpeg子进程录制屏幕,将进程PID写入文件
- 脚本B:读取PID文件,向对应进程发送'q'输入以停止录屏
脚本A代码
import shlex import subprocess import time import datetime # Variables success = True flowName = "workflow1" # Get current date and time now = datetime.datetime.now() formatted_date = now.strftime("%Y-%m-%d %H:%M:%S") folderDestination = f"C:\\Users\\MadPe\\Desktop\\pipRec\\workFlows\\{flowName}\\" formatted_date = formatted_date.replace(":",".").replace(" ","_") fileName = folderDestination + formatted_date + ".mp4" ffmpegPath = r"C:\Users\MadPe\Desktop\pipRec\ffmpeg\bin\ffmpeg.exe" command = shlex.split(f'"{ffmpegPath}" -f gdigrab -framerate 30 -offset_x 0 -offset_y 0 -video_size 1920x1080 -i desktop -y "{fileName}"') process = subprocess.Popen(command, stdin=subprocess.PIPE) pid = process.pid startTime = time.time() with open("filename.txt", "w") as file: file.write(str(pid)) #process.stdin.write(b'q\n') #-- 直接在本脚本执行可以正常停止 #process.stdin.flush()
脚本B代码
import psutil # Stop recording and kill Process with open("filename.txt", "r") as f: pid = int(f.read().strip()) if psutil.pid_exists(pid): process = psutil.Process(pid) process.stdin.write(b'q\n') process.stdin.flush() else: print(f"Process with PID {pid} is not running.")
问题原因
运行脚本B报错的核心原因是:psutil的Process对象并没有stdin属性。跨进程无法直接访问另一个Python脚本创建的subprocess的标准输入管道——每个进程的文件句柄是独立隔离的,不能跨进程直接调用。
解决方法
方法1:使用命名管道(推荐,优雅停止)
通过Windows命名管道给FFmpeg提供标准输入,让脚本B可以往管道写入停止命令。
修改脚本A:
import shlex import subprocess import datetime import win32pipe import win32file # Variables success = True flowName = "workflow1" pipe_name = r'\\.\pipe\ffmpeg_rec_pipe' # 创建命名管道 pipe = win32pipe.CreateNamedPipe( pipe_name, win32pipe.PIPE_ACCESS_OUTBOUND, win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_WAIT, 1, 65536, 65536, 0, None ) # 等待FFmpeg连接管道 win32pipe.ConnectNamedPipe(pipe, None) # 生成文件名 now = datetime.datetime.now() formatted_date = now.strftime("%Y-%m-%d %H:%M:%S").replace(":",".").replace(" ","_") folderDestination = f"C:\\Users\\MadPe\\Desktop\\pipRec\\workFlows\\{flowName}\\" fileName = folderDestination + formatted_date + ".mp4" ffmpegPath = r"C:\Users\MadPe\Desktop\pipRec\ffmpeg\bin\ffmpeg.exe" # 启动FFmpeg,将标准输入绑定到命名管道 command = shlex.split(f'"{ffmpegPath}" -f gdigrab -framerate 30 -offset_x 0 -offset_y 0 -video_size 1920x1080 -i desktop -y "{fileName}"') process = subprocess.Popen(command, stdin=pipe) # 将PID和管道名写入文件 with open("filename.txt", "w") as file: file.write(f"{process.pid}\n{pipe_name}")
修改脚本B:
import win32file # 读取PID和管道名 with open("filename.txt", "r") as f: lines = f.read().splitlines() pipe_name = lines[1] # 打开命名管道并发送停止命令 try: pipe = win32file.CreateFile( pipe_name, win32file.GENERIC_WRITE, 0, None, win32file.OPEN_EXISTING, 0, None ) win32file.WriteFile(pipe, b'q\n') win32file.CloseHandle(pipe) print("已发送停止命令") except Exception as e: print(f"发送失败:{str(e)}")
方法2:向FFmpeg窗口发送键盘事件
如果不想用命名管道,可以通过窗口句柄找到FFmpeg的控制台窗口,发送'q'按键事件。
修改脚本B:
import psutil import win32gui import win32api import win32con def get_process_window(pid): # 枚举所有窗口,找到对应PID的窗口 hwnd_list = [] def enum_callback(hwnd, extra): if win32gui.GetWindowThreadProcessId(hwnd)[1] == pid: extra.append(hwnd) return True win32gui.EnumWindows(enum_callback, hwnd_list) return hwnd_list[0] if hwnd_list else None with open("filename.txt", "r") as f: pid = int(f.read().strip()) if psutil.pid_exists(pid): hwnd = get_process_window(pid) if hwnd: # 激活窗口并发送q键 win32gui.SetForegroundWindow(hwnd) win32api.keybd_event(ord('Q'), 0, 0, 0) win32api.keybd_event(ord('Q'), 0, win32con.KEYEVENTF_KEYUP, 0) print("已发送停止按键") else: print("未找到FFmpeg对应的控制台窗口") else: print(f"PID {pid}的进程不存在")
内容的提问来源于stack exchange,提问作者Noddy
相关产品推荐
相关产品推荐

