1

I've been using mss for screen capturing for a while, and no matter what I can't get it above 60 fps, even with multiprocessing. Here's the code:

import multiprocessing as mp
import time
import mss


def task():
    start = time.time()
    
    for x in range(600):
        with mss.mss() as sct:
            sct.grab({'mon': 1, 'top': 690, 'left': 750, 'width': 450, 'height': 50})

    print(time.time() - start)

if __name__ == '__main__':
    for x in range(3):
        p = mp.Process(target=task)
        p.start()

Changing the int in the range() function does not make a difference to overall capturing speed. Here are the results:

1 process, 60 fps per process, 60 fps combined, ~10 seconds per process
2 processes, 30 fps per process, 60 fps combined, ~20 seconds per process
3 processes, 20 fps per process, 60 fps combined, ~30 seconds per process

Anyone know what could be causing this?

It should be noted that changing the bounding box ({'mon': 1, 'top': 690, 'left': 750, 'width': 450, 'height': 50}) resolution does not affect the framerate. Capturing my whole screen, down to a single pixel, it's all at 60 fps.

Extra Info
Python 3.10.4
Windows 10 Laptop, ASUS TUF Gaming FXGT505-FXGT505
Intel I7 9750H, GTX 1650, 16gb ram, 144hz screen w/ 60hz monitor as primary
In the cases where I actively do image processing with mss screen capture, the processing does not affect the framerate, even with multiprocessing.

BappoHotel
  • 52
  • 10
  • I'm sure that line `with mss.mss() as sct:` or `sct.grab()` acquires some global mutex inside of mss library. And this mutex allows to be acquired only 60 times per second. I'm sure MSS library has some internal state of Grabber and this state produces screens only 60 times per second, not more. This should be special limitation of library itself. This might be a tweakable behaviour, in library could be a config to raise a limit from 60 to 120. – Arty Oct 14 '22 at 08:45
  • @Arty I appreciate it, but I would've thought if that was the case more people would have this problem? I've done some research over the past weeks to try to see if anyone else was having this issue and I couldn't find anything that wasn't resolved by changing screen resolution. Still, assuming you're right, how would I go about finding how to change this? – BappoHotel Oct 14 '22 at 08:53
  • @Arty If it makes any difference, I usually do `sct = mss.mss()` on one line and `sct.grab()` on another, and it still provides the same issue. – BappoHotel Oct 14 '22 at 08:54
  • It means that lock is acquired inside `sct.grab()`, not `with` statement. I have looked into code of that library and it shows that `sct.grab()` uses Windows API functions like `CreateCompatibleBitmap()` and `SelectObject()` and `DeleteObject()` and `BitBlt()` and `GetDIBits()`. I'm sure one of Windows API functions among these ones inserts some pause like 1/60 of second. Also these functions may just wait till next monitor synchronization time point, once in 1/60 seconds. So this pause is naturally inserted by Win API. – Arty Oct 14 '22 at 09:03
  • @Arty Could you point me to which file you found those being used in? Was it a file in the `site-packages/mss` directory, or somewhere else? – BappoHotel Oct 14 '22 at 09:19
  • Inside Python installation folder you can find `mss` library. It is located on my machine `C:\bin\Python39\Lib\site-packages\mss\windows.py`. This windows.py file implements `grab()` function, which contains all mentioned above by me WinAPI functions. Inside windows.py file just look for line `def _grab_impl(self, monitor):` - body of this function uses all mentioned WinAPI. – Arty Oct 14 '22 at 09:24
  • @Arty Thank you for your patience, my skills are very narrow in scope, I'm more into the data-science part of python and typically don't have to go digging through files like this to try to solve minor issues. – BappoHotel Oct 14 '22 at 09:26

1 Answers1

1

Arty pointed out the following:

It means that lock is acquired inside sct.grab(), not with statement. I have looked into code of that library and it shows that sct.grab() uses Windows API functions like CreateCompatibleBitmap() and SelectObject() and DeleteObject() and BitBlt() and GetDIBits(). I'm sure one of Windows API functions among these ones inserts some pause like 1/60 of second. Also these functions may just wait till next monitor synchronization time point, once in 1/60 seconds. So this pause is naturally inserted by Win API

As a result, I disconnected my monitor entirely, and this did speed up the capture rate to 144 hz. Disconnecting my monitor is a good temporary solution for me, but some people only have a 60hz monitor and nothing else. I am going to look for a more permanent one by sleuthing through the Win Api, once I figure out how to do so. Any help would be appreciated.

It should be noted that changing the monitor that is being captured to the 144hz monitor still limits the capture rate to 60fps.

BappoHotel
  • 52
  • 10