Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: remove auto close feature in VideoRecorder #42

Merged
merged 6 commits into from
Nov 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions gymnasium/wrappers/monitoring/video_recorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def __init__(
metadata: Optional[dict] = None,
enabled: bool = True,
base_path: Optional[str] = None,
disable_logger: bool = False,
):
"""Video recorder renders a nice movie of a rollout, frame by frame.

Expand All @@ -33,6 +34,7 @@ def __init__(
metadata (Optional[dict]): Contents to save to the metadata file.
enabled (bool): Whether to actually record video, or just no-op (for convenience)
base_path (Optional[str]): Alternatively, path to the video file without extension, which will be added.
disable_logger (bool): Whether to disable moviepy logger or not.

Raises:
Error: You can pass at most one of `path` or `base_path`
Expand All @@ -48,6 +50,7 @@ def __init__(

self._async = env.metadata.get("semantics.async")
self.enabled = enabled
self.disable_logger = disable_logger
self._closed = False

self.render_history = []
Expand Down Expand Up @@ -153,9 +156,9 @@ def close(self):
"MoviePy is not installed, run `pip install moviepy`"
)

logger.debug(f"Closing video encoder: path={self.path}")
clip = ImageSequenceClip(self.recorded_frames, fps=self.frames_per_sec)
clip.write_videofile(self.path)
moviepy_logger = None if self.disable_logger else "bar"
clip.write_videofile(self.path, logger=moviepy_logger)
else:
# No frames captured. Set metadata.
if self.metadata is None:
Expand All @@ -175,4 +178,5 @@ def write_metadata(self):
def __del__(self):
"""Closes the environment correctly when the recorder is deleted."""
# Make sure we've closed up shop when garbage collecting
self.close()
if not self._closed:
logger.warn("Unable to save last video! Did you call close()?")
9 changes: 5 additions & 4 deletions gymnasium/wrappers/record_video.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def __init__(
step_trigger: Callable[[int], bool] = None,
video_length: int = 0,
name_prefix: str = "rl-video",
disable_logger: bool = False,
):
"""Wrapper records videos of rollouts.

Expand All @@ -56,6 +57,8 @@ def __init__(
video_length (int): The length of recorded episodes. If 0, entire episodes are recorded.
Otherwise, snippets of the specified length are captured
name_prefix (str): Will be prepended to the filename of the recordings
disable_logger (bool): Whether to disable moviepy logger or not.

"""
super().__init__(env)

Expand All @@ -68,6 +71,7 @@ def __init__(
self.episode_trigger = episode_trigger
self.step_trigger = step_trigger
self.video_recorder: Optional[video_recorder.VideoRecorder] = None
self.disable_logger = disable_logger

self.video_folder = os.path.abspath(video_folder)
# Create output folder if needed
Expand Down Expand Up @@ -119,6 +123,7 @@ def start_video_recorder(self):
env=self.env,
base_path=base_path,
metadata={"step_id": self.step_id, "episode_id": self.episode_id},
disable_logger=self.disable_logger,
)

self.video_recorder.capture_frame()
Expand Down Expand Up @@ -205,7 +210,3 @@ def close(self):
"""Closes the wrapper then the video recorder."""
super().close()
self.close_video_recorder()

def __del__(self):
"""Closes the video recorder."""
self.close_video_recorder()
27 changes: 0 additions & 27 deletions tests/wrappers/test_video_recorder.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import gc
import os
import re
import time

import pytest

Expand Down Expand Up @@ -45,31 +43,6 @@ def test_record_simple():
assert os.fstat(f.fileno()).st_size > 100


def test_autoclose():
def record():
env = gym.make(
"CartPole-v1", render_mode="rgb_array_list", disable_env_checker=True
)
rec = VideoRecorder(env)
env.reset()
rec.capture_frame()

rec_path = rec.path

# The function ends without an explicit `rec.close()` call
# The Python interpreter will implicitly do `del rec` on garbage cleaning
return rec_path

rec_path = record()

gc.collect() # do explicit garbage collection for test
time.sleep(5) # wait for subprocess exiting

assert os.path.exists(rec_path)
f = open(rec_path)
assert os.fstat(f.fileno()).st_size > 100


def test_no_frames():
env = BrokenRecordableEnv()
rec = VideoRecorder(env)
Expand Down