Linux平台仅录制指定进程屏幕输出的技术实现方案咨询
Linux指定进程录屏实现方案
你推测的「在进程画面输出抵达屏幕前拦截抓取」的思路完全可行。Linux桌面的图形渲染链路中,每个进程的UI内容都会作为独立的渲染缓存提交给系统合成器,再由合成器叠加所有窗口后输出到屏幕,和标准输出的流程逻辑类似,完全可以单独抓取指定进程的所有渲染内容,完全不会混入其他进程的弹窗、悬浮窗等界面。
原方案的问题
你贴的PIL+OpenCV全局区域抓屏的方案,是直接读取合成器最终输出的全屏帧,自然无法区分不同进程的内容,只能做区域裁剪,不符合你的需求。
import datetime from PIL import ImageGrab import numpy as np import cv2 time_stamp = datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S') file_name = f'{time_stamp}.mp4' fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v') captured_video = cv2.VideoWriter(file_name, fourcc, 20.0, (630, 330)) while True: img = ImageGrab.grab(bbox=(0, 0, 630, 330)) img_np = np.array(img) img_final = cv2.cvtColor(img_np, cv2.COLOR_BGR2RGB) cv2.imshow("Screen Recorder", img_final) captured_video.write(img_final) if cv2.waitKey(10) == ord('q'): break
可行实现路径
1. X11桌面环境实现(兼容性最高)
当前绝大多数Linux发行版默认还是X11架构,可按如下步骤实现:
- 首先通过PID匹配该进程关联的所有X11窗口:遍历所有顶层窗口的
_NET_WM_PID属性,筛选出和目标PID一致的窗口ID集合 - 直接抓取指定窗口的渲染缓存:调用Xlib的
XGetImage接口针对窗口ID做捕获,只要开启窗口内容重定向属性,哪怕窗口被遮挡、最小化也能正常抓取内容,不会混入其他进程的界面 - 后续的视频编码逻辑可以直接沿用你现有代码中OpenCV写MP4的部分,只需要替换掉帧来源即可
2. Wayland桌面环境实现
Wayland因为安全限制不允许随意读取其他进程窗口内容,可通过xdg-desktop-portal的窗口捕获接口,指定仅捕获目标PID所属的窗口即可,GNOME、KDE等主流桌面环境都已支持该能力。
简化代码示例(Python X11版本)
把你原有代码的抓屏逻辑替换为如下内容即可:
import Xlib.display from PIL import Image import numpy as np import cv2 # 初始化X11连接 display = Xlib.display.Display() root = display.screen().root target_pid = 31328 # 替换为你要录制的进程PID # 遍历匹配目标PID的窗口 target_window = None for window in root.query_tree().children: pid = window.get_full_property(display.intern_atom('_NET_WM_PID'), 0) if pid and pid.value[0] == target_pid: target_window = window break # 原有编码逻辑保留,仅替换帧来源 time_stamp = datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S') file_name = f'{time_stamp}.mp4' geom = target_window.get_geometry() fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v') captured_video = cv2.VideoWriter(file_name, fourcc, 20.0, (geom.width, geom.height)) while True: raw_img = target_window.get_image(0, 0, geom.width, geom.height, Xlib.X.ZPixmap, 0xffffffff) img = Image.frombytes("RGB", (geom.width, geom.height), raw_img.data, "raw", "BGRX") img_np = np.array(img) img_final = cv2.cvtColor(img_np, cv2.COLOR_BGR2RGB) cv2.imshow("Screen Recorder", img_final) captured_video.write(img_final) if cv2.waitKey(10) == ord('q'): break
内容的提问来源于stack exchange,提问作者Abdul Moez
相关产品推荐
相关产品推荐

