简体   繁体   English

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

[英]How to record my computer screen with high FPS?

I'm trying to add a high FPS screen recorder to my application.我正在尝试将高 FPS 屏幕录像机添加到我的应用程序中。 I use Python 3.7 on Windows.我在 Windows 上使用 Python 3.7。 The modules and methods I've tried are mss (python-mss) and d3dshot , but I'm still only achieving 15-19 FPS for a long video (more than 20 seconds).我尝试过的模块和方法是mss (python-mss)d3dshot ,但对于长视频(超过 20 秒),我仍然只能达到 15-19 FPS。 The resolution I'm recording at is 1920 x 1080.我录制的分辨率是 1920 x 1080。

What is the best way to optimize screen recording?优化屏幕录制的最佳方法是什么? I've tried to use the multiprocessing library, but it seems like it's still not fast enough.我尝试使用multiprocessing库,但它似乎仍然不够快。 I'm not sure I'm using it in the optimal way, what are some ways I could use it to improve processing performance?我不确定我是否以最佳方式使用它,有哪些方法可以使用它来提高处理性能?

Using OBS Studio, I'm able to get 30 FPS, no matter how long the video is.使用 OBS Studio,无论视频多长,我都能获得 30 FPS。 My objective is to achieve the same results with my own code.我的目标是用我自己的代码实现相同的结果。

Here is what I've written so far:这是我到目前为止所写的:

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()

The goal is to capture a game, while performing automated keyboard and mouse actions.目标是捕捉游戏,同时执行自动化的键盘和鼠标操作。

I have faced the same problem in trying to get high speed recording for games.在尝试为游戏进行高速录制时,我遇到了同样的问题。 This was the fastest solution I was able to find for Windows.这是我为 Windows 找到的最快的解决方案。 The code is using raw buffer objects and leads to around ~27 FPS.该代码使用原始缓冲区对象并导致大约 27 FPS。 I cannot find the original post on which this code is based, but if someone finds it I will add the reference.我找不到此代码所基于的原始帖子,但如果有人找到它,我会添加参考。

Note that the framerate will significantly increase if you make the region smaller than 1920x1080.请注意,如果您将区域设置为小于 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='')


Edit编辑

I noticed some small mistake in the window show function, and the self.screenshot calls in the __getitem__ and __next__ method.我注意到 window show function 以及__getitem____next__方法中的self.screenshot调用中的一些小错误。 These have been resolved.这些都已解决。

Next to the for example using the ScreenCapture as a context manager, I added an example of using it as a generator.在使用ScreenCapture作为上下文管理器的示例旁边,我添加了一个将其用作生成器的示例。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何高质量录制屏幕 - how to record screen in high quality 如何在Linux中编程截图(高fps)(编程) - How to take screenshot (high fps) in Linux (programming) 如何使用 cv2.videowriter 使用 Tkinter 录制计算机屏幕并且在录制时不显示屏幕(imgshow) - How to use cv2.videowriter to record the computer screen with the use of Tkinter and without displaying the screen while recording it (imgshow) 如何在cv2中增加屏幕录像机的fps - How to increase the fps of screen recorder in cv2 如何提高具有高 fps 视频的 FER 处理(分析)速度? - How to increase the speed of FER processing ( analyze ) for videos having high fps? 如何在python中使用picamera V2以90 FPS录制视频 - How to record a video with 90 FPS with picamera V2 in python 大家好,我正在尝试使用 python 制作屏幕录像机程序,但是我的程序运行 15 fps 我如何提高到每秒 60 帧 - hi guys im trying make screen recorder program with python but my program running 15 fps how i improve to 60 frame per second 如何获取任何计算机的屏幕大小 - How to get the screen size of any computer 如何根据 fps 为屏幕上的每次更新缩放移动对象的速度,以便在任何给定的 fps 下都可以具有相同的移动速度? - How to scale the speed of a moving object for each update on the screen according to fps so it can be the same moving speed at any given fps? 使用Python在以太网上实现高FPS直播 - High FPS livestream over ethernet using Python
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM