Python 如何以高FPS录制我的电脑屏幕?

Python 如何以高FPS录制我的电脑屏幕?,python,python-3.x,screenshot,screen-recording,python-mss,Python,Python 3.x,Screenshot,Screen Recording,Python Mss,我正在尝试向我的应用程序添加一个高FPS屏幕录制器。 我在Windows上使用Python 3.7。 我尝试过的模块和方法是mss(pythonmss)和d3dshot,但对于长视频(超过20秒),我仍然只能达到15-19 FPS。 我记录的分辨率是1920 x 1080 优化屏幕录制的最佳方法是什么?我曾尝试使用多处理库,但似乎速度还不够快。我不确定我是否以最佳方式使用它,我可以用哪些方法来提高处理性能 使用OBS Studio,无论视频有多长,我都能以每秒30帧的速度播放。我的目标是用我自己

我正在尝试向我的应用程序添加一个高FPS屏幕录制器。 我在Windows上使用Python 3.7。 我尝试过的模块和方法是
mss(pythonmss)
d3dshot
,但对于长视频(超过20秒),我仍然只能达到15-19 FPS。 我记录的分辨率是1920 x 1080

优化屏幕录制的最佳方法是什么?我曾尝试使用
多处理
库,但似乎速度还不够快。我不确定我是否以最佳方式使用它,我可以用哪些方法来提高处理性能

使用OBS Studio,无论视频有多长,我都能以每秒30帧的速度播放。我的目标是用我自己的代码实现同样的结果

以下是我到目前为止所写的内容:

from multiprocessing import Process, Queue
from time import sleep, time

import cv2
import d3dshot
import numpy as np


def grab(queue):


    d = d3dshot.create(capture_output="numpy", frame_buffer_size=500)

    d.capture()
    sleep(0.1)
    c=0
    
    begin = time()

    while time() - begin < 30:
        starter = time()

        frame = d.get_latest_frame()

        queue.put(frame)
        c+=1
        ender = time()

        sleep(max(0, 1/60 - (ender -starter)))

    # Tell the other worker to stop
    queue.put(None)

    final=time()
    
    print(c/(final-begin))

    d.stop()


def save(queue):
    SCREEN_SIZE = 1920, 1080
    
    # Define the codec and create VideoWriter object
    fourcc = cv2.VideoWriter_fourcc(*'DIVX') # In Windows: DIVX 
    out = cv2.VideoWriter(r"output.avi",fourcc, 30.0, (SCREEN_SIZE))
    # type: (Queue) -> None

    last_img = None
    while "there are screenshots":

        img = queue.get()
        if img is None:
            break
        if img is last_img:
            continue
        
        out.write(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

        

        last_img = img


if __name__ == "__main__":
    # The screenshots queue
    queue = Queue()  # type: Queue
    

    # 2 processes: one for grabing and one for saving PNG files
    Process(target=grab, args=(queue,)).start()
    Process(target=save, args=(queue,)).start()
来自多处理导入进程,队列
从时间导入睡眠,时间
进口cv2
导入d3dshot
将numpy作为np导入
def抓取(队列):
d=d3dshot.create(捕获输出=“numpy”,帧缓冲区大小=500)
d、 捕获()
睡眠(0.1)
c=0
开始=时间()
当时间()开始<30时:
启动器=时间()
frame=d.获取最新的帧()
队列.放置(帧)
c+=1
ender=时间()
睡眠(最大值(0,1/60-(ender-starter)))
#叫另一个工人停下来
queue.put(无)
最终=时间()
打印(c/(最终开始))
d、 停止()
def保存(队列):
屏幕尺寸=19201080
#定义编解码器并创建VideoWriter对象
fourcc=cv2.VideoWriter_fourcc(*'DIVX')#在Windows中:DIVX
out=cv2.VideoWriter(r“output.avi”,fourcc,30.0,(屏幕大小))
#类型:(队列)->无
最后一次检查=无
虽然“有截图”:
img=queue.get()
如果img为无:
打破
如果img是最后一个img:
持续
out.write(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
最后一个img=img
如果名称=“\uuuuu main\uuuuuuuu”:
#截图队列
queue=queue()#类型:queue
#2个过程:一个用于抓取,一个用于保存PNG文件
进程(target=grab,args=(queue,).start()
进程(目标=保存,参数=(队列,).start()

目标是在执行自动键盘和鼠标操作的同时捕获游戏。

我在尝试获取游戏的高速录制时也遇到了同样的问题。这是我为Windows找到的最快的解决方案。代码使用的是原始缓冲区对象,大约每秒27帧。我找不到此代码所基于的原始帖子,但如果有人找到它,我将添加引用

请注意,如果使区域小于1920x1080,帧率将显著增加

"""
Alternative screen capture device, when there is no camera of webcam connected
to the desktop.
"""

import logging
import sys
import time
import cv2
import numpy as np

if sys.platform == 'win32':
    import win32gui, win32ui, win32con, win32api
else:
    logging.warning(f"Screen capture is not supported on platform: `{sys.platform}`")

from collections import namedtuple


class ScreenCapture:
    """
        Captures a fixed  region of the total screen. If no region is given
        it will take the full screen size.
        region_ltrb: Tuple[int, int, int, int]
            Specific region that has to be taken from the screen using
            the top left `x` and `y`,  bottom right `x` and `y` (ltrb coordinates).
    """
    __region = namedtuple('region', ('x', 'y', 'width', 'height'))

    def __init__(self, region_ltrb=None):
        self.region = region_ltrb
        self.hwin = win32gui.GetDesktopWindow()

        # Time management
        self._time_start = time.time()
        self._time_taken = 0
        self._time_average = 0.04

    def __getitem__(self, item):
        return self.screenshot()

    def __next__(self):
        return self.screenshot()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()
        if exc_type and isinstance(exc_val, StopIteration):
            return True
        return False

    @staticmethod
    def screen_dimensions():
        """ Retrieve total screen dimensions.  """
        left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN)
        top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN)
        height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN)
        width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN)
        return left, top, height, width

    @property
    def fps(self):
        return int(1 / self._time_average) * (self._time_average > 0)

    @property
    def region(self):
        return self._region

    @property
    def size(self):
        return self._region.width, self._region.height

    @region.setter
    def region(self, value):
        if value is None:
            self._region = self.__region(*self.screen_dimensions())
        else:
            assert len(value) == 4, f"Region requires 4 input, x, y of left top, and x, y of right bottom."
            left, top, x2, y2 = value
            width = x2 - left + 1
            height = y2 - top + 1
            self._region = self.__region(*list(map(int, (left, top, width, height))))

    def screenshot(self, color=None):
        """
            Takes a  part of the screen, defined by the region.
            :param color: cv2.COLOR_....2...
                Converts the created BGRA image to the requested image output.
            :return: np.ndarray
                An image of the region in BGRA values.
        """
        left, top, width, height = self._region
        hwindc = win32gui.GetWindowDC(self.hwin)
        srcdc = win32ui.CreateDCFromHandle(hwindc)
        memdc = srcdc.CreateCompatibleDC()

        bmp = win32ui.CreateBitmap()
        bmp.CreateCompatibleBitmap(srcdc, width, height)
        memdc.SelectObject(bmp)
        memdc.BitBlt((0, 0), (width, height), srcdc, (left, top), win32con.SRCCOPY)

        signed_ints_array = bmp.GetBitmapBits(True)
        img = np.frombuffer(signed_ints_array, dtype='uint8')
        img.shape = (height, width, 4)

        srcdc.DeleteDC()
        memdc.DeleteDC()
        win32gui.ReleaseDC(self.hwin, hwindc)
        win32gui.DeleteObject(bmp.GetHandle())

        # This makes sure that the FPS are taken in comparison to screenshots rates and vary only slightly.
        self._time_taken, self._time_start = time.time() - self._time_start, time.time()
        self._time_average = self._time_average * 0.95 + self._time_taken * 0.05

        if color is not None:
            return cv2.cvtColor(img, color)
        return img

    def show(self, screenshot=None):
        """ Displays an image to the screen. """
        image = screenshot if screenshot is not None else self.screenshot()
        cv2.imshow('Screenshot', image)

        if cv2.waitKey(1) & 0xff == ord('q'):
            raise StopIteration
        return image

    def close(self):
        """ Needs to be called before exiting when `show` is used, otherwise an error will occur.  """
        cv2.destroyWindow('Screenshot')

    def scale(self, src: np.ndarray, size: tuple):
        return cv2.resize(src, size, interpolation=cv2.INTER_LINEAR_EXACT)

    def save(self, path, screenshot=None):
        """ Store the current screenshot in the provided path. Full path, with img name is required.) """
        image = screenshot if screenshot is not None else self.screenshot
        cv2.imwrite(filename=path, img=image)


if __name__ == '__main__':
    # Example usage when displaying.
    with ScreenCapture((0, 0, 1920, 1080)) as capture:
        for _ in range(100):
            capture.show()
            print(f"\rCapture framerate: {capture.fps}", end='')

    # Example usage as generator.
    start_time = time.perf_counter()
    for frame, screenshot in enumerate(ScreenCapture((0, 0, 1920, 1080)), start=1):
        print(f"\rFPS: {frame / (time.perf_counter() - start_time):3.0f}", end='')


编辑 我注意到窗口
show
函数中有一些小错误,以及
\uu getitem\uuuuuuu
\uuuu next\uuuu
方法中的
self.screenshot
调用。这些问题已经解决


在使用“代码> > SurvithStudio<代码>作为上下文管理器的旁边,我添加了一个使用它作为生成器的例子。

记住Python通常不是一种语言的快,而不是用C+C++编写的OBS。@ Xiddoc在这里有什么关系?该代码在Python中是否执行了大量的工作?实际工作是在库中完成的,可以用C++编写。python中的代码只是在函数之间传递引用,所以速度可能很快