From 96e51180f4ac6350b11d61778c04804744372fef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com> Date: Tue, 7 Nov 2023 10:08:57 +0000 Subject: [PATCH] chore: improved video capture strategy Makes use of temporary directory. --- examples/python/pocket.py | 4 ++-- examples/python/pocket_pyboy.py | 4 ++-- src/python/boytacean/gb.py | 38 +++++++++++++++++++++++---------- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/examples/python/pocket.py b/examples/python/pocket.py index 04febfa6..e9e96d33 100644 --- a/examples/python/pocket.py +++ b/examples/python/pocket.py @@ -1,5 +1,5 @@ from time import time -from boytacean import GameBoy +from boytacean import GameBoy, VISUAL_FREQ FRAME_COUNT = 12000 @@ -10,5 +10,5 @@ for _ in range(FRAME_COUNT): gb.next_frame() total = time() - start print(f"Time taken: {total:.2f} seconds") -print(f"Speedup: {FRAME_COUNT / total / 60:.2f}x") +print(f"Speedup: {FRAME_COUNT / total / VISUAL_FREQ:.2f}x") gb.save_image("pocket.png") diff --git a/examples/python/pocket_pyboy.py b/examples/python/pocket_pyboy.py index d641ee58..8ee4d8d4 100644 --- a/examples/python/pocket_pyboy.py +++ b/examples/python/pocket_pyboy.py @@ -1,5 +1,5 @@ from time import time -from pyboy import PyBoy +from pyboy import PyBoy, VISUAL_FREQ FRAME_COUNT = 12000 @@ -11,5 +11,5 @@ with PyBoy("../../res/roms/demo/pocket.gb", disable_renderer=True) as pyboy: pyboy.tick() total = time() - start print(f"Time taken: {total:.2f} seconds") - print(f"Speedup: {FRAME_COUNT / total / 60:.2f}x") + print(f"Speedup: {FRAME_COUNT / total / VISUAL_FREQ:.2f}x") pyboy.screen_image().save("pocket_pyboy.png") diff --git a/src/python/boytacean/gb.py b/src/python/boytacean/gb.py index 23b82118..773e1704 100644 --- a/src/python/boytacean/gb.py +++ b/src/python/boytacean/gb.py @@ -1,7 +1,10 @@ from enum import Enum from os import remove from glob import glob +from shutil import rmtree +from tempfile import mkdtemp from contextlib import contextmanager +from typing import Any from PIL.Image import Image, frombytes @@ -27,6 +30,7 @@ class GameBoy: _frame_index: int = 0 _start_frame: int | None _frame_gap: int + _capture_temp_dir: str | None def __init__( self, @@ -42,6 +46,7 @@ class GameBoy: self._frame_index = 0 self._next_frame = None self._frame_gap = VISUAL_FREQ + self._capture_temp_dir = None self._system = GameBoyRust(mode.value) self._system.set_ppu_enabled(ppu_enabled) self._system.set_apu_enabled(apu_enabled) @@ -83,9 +88,7 @@ This is a [Game Boy](https://en.wikipedia.org/wiki/Game_Boy) emulator built usin def next_frame(self) -> int: cycles = self._system.next_frame() self._frame_index += 1 - if self._next_frame != None and self._frame_index >= self._next_frame: - self._next_frame = self._next_frame + self._frame_gap - self.save_image(f"frame_{self._frame_index}.png") + self._on_next_frame() return cycles def frame_buffer(self): @@ -100,27 +103,33 @@ This is a [Game Boy](https://en.wikipedia.org/wiki/Game_Boy) emulator built usin image = self.image() image.save(filename, format=format) - def video(self, encoder="H264"): + def video(self, encoder="avc1", display=True) -> Any: from cv2 import VideoWriter, VideoWriter_fourcc, imread - from IPython.display import Video + from IPython.display import Video, display as _display - images = glob("*.png") + image_paths = glob(f"{self._capture_temp_dir}/*.png") + video_path = f"{self._capture_temp_dir}/video.mp4" encoder = VideoWriter( - "output.mp4", + video_path, VideoWriter_fourcc(*encoder), VISUAL_FREQ / self._frame_gap, (DISPLAY_WIDTH, DISPLAY_HEIGHT), ) try: - for image_file in sorted(images): + for image_file in sorted(image_paths): img = imread(image_file) encoder.write(img) finally: encoder.release() - return Video("output.mp4", embed=True, html_attributes="controls loop autoplay") + video = Video(video_path, embed=True, html_attributes="controls loop autoplay") + + if display: + _display(video) + + return video def set_palette(self, name: str): if not name in PALETTES: @@ -180,13 +189,20 @@ This is a [Game Boy](https://en.wikipedia.org/wiki/Game_Boy) emulator built usin try: yield finally: + self.video() self._stop_capture() + def _on_next_frame(self): + if self._next_frame != None and self._frame_index >= self._next_frame: + self._next_frame = self._next_frame + self._frame_gap + self.save_image(f"{self._capture_temp_dir}/frame_{self._frame_index}.png") + def _start_capture(self, fps=5): self._next_frame = self._frame_index + self._frame_gap self._frame_gap = int(VISUAL_FREQ / fps) + self._capture_temp_dir = mkdtemp() def _stop_capture(self): self._next_frame = None - for file in glob("*.png"): - remove(file) + if self._capture_temp_dir: + rmtree(self._capture_temp_dir) -- GitLab