Skip to content

Instantly share code, notes, and snippets.

@nitori
Created August 31, 2022 16:26
Show Gist options
  • Save nitori/a2d0029c9f2795b995b29ae57a23fff1 to your computer and use it in GitHub Desktop.
Save nitori/a2d0029c9f2795b995b29ae57a23fff1 to your computer and use it in GitHub Desktop.
Uses ffmpeg to grab screen and outputs in Surface on pygame
from __future__ import annotations
from threading import Thread, Event
from queue import Queue
import subprocess
import pygame
MONITORS = {
'left': (-1920, 0, 1920, 1080),
'middle': (0, 0, 2560, 1440), # presumably main monitor, hence offset is (0, 0)
'right': (2560, 0, 1920, 1080),
}
def ffmpeg_thread(monitor: str, q: Queue, stop_ev: Event, w: int, h: int) -> None:
sx, sy, sw, sh = MONITORS[monitor]
proc = subprocess.Popen([
'ffmpeg',
'-hide_banner',
'-loglevel', 'warning',
'-f', 'gdigrab',
'-probesize', '42M',
'-framerate', '15',
'-offset_x', f'{sx}',
'-offset_y', f'{sy}',
'-video_size', f'{sw}x{sh}',
'-show_region', '1',
'-i', 'desktop',
'-vf', f'scale={w}:{h}',
'-f', 'rawvideo',
'-pix_fmt', 'rgb24',
'-vcodec', 'rawvideo',
'-',
], stdout=subprocess.PIPE)
buf = b''
framesize = w * h * 3
while not stop_ev.is_set():
chunk = proc.stdout.read(framesize)
if not chunk:
break
buf += chunk
while len(buf) >= framesize:
q.put(buf[:framesize])
buf = buf[framesize:]
proc.terminate()
proc.wait()
def main():
grabwh = 854, 480
ffmpeg_q = Queue()
ffmpeg_ev = Event()
ffmpeg_t = Thread(target=ffmpeg_thread, args=('left', ffmpeg_q, ffmpeg_ev, *grabwh))
ffmpeg_t.start()
pygame.init()
pw, ph = 1280, 720
screen = pygame.display.set_mode((pw, ph))
screen.fill((0, 0, 0))
clock = pygame.time.Clock()
last_frame = None
vx = 3
vy = 2
sx = 0
sy = 0
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
ffmpeg_ev.set()
ffmpeg_t.join()
return
frame = last_frame
while not ffmpeg_q.empty():
frame = ffmpeg_q.get()
if frame is not None:
last_frame = frame
surface = pygame.image.frombuffer(frame, grabwh, 'RGB')
sx += vx
sy += vy
if sx < 0 or sx > pw - grabwh[0]:
vx = -vx
if sy < 0 or sy > ph - grabwh[1]:
vy = -vy
screen.fill((0, 0, 0))
screen.blit(surface, (sx, sy))
pygame.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment