Skip to content

Commit

Permalink
Improvements to video players (#1786)
Browse files Browse the repository at this point in the history
* improvements to cv2 video player

* Added video for use with videoPlayerViews

* typing improvements

* exception handling

* remove error on cv2 not found

* snake.mp4 added to resources listing

* Update arcade/experimental/video_cv2.py

Co-authored-by: Paul <36696816+pushfoo@users.noreply.github.com>

* earth.mp4 replaces snake.mp4, fixed docstrings

* Fix: ignore unused imports linting

* earth.mp4 credits

* *.mp4 added to manifest

---------

Co-authored-by: Paul <36696816+pushfoo@users.noreply.github.com>
  • Loading branch information
MrWardKKHS and pushfoo authored Jun 8, 2023
1 parent 71f61ea commit c836a53
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 27 deletions.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ recursive-include arcade *.typed
recursive-include arcade/resources *.txt *.md *.url
recursive-include arcade/resources *.mp3 *.wav *.ogg
recursive-include arcade/resources *.png *.jpg *.gif
recursive-include arcade/resources *.mp4
recursive-include arcade/resources *.json
recursive-include arcade/resources *.glsl
recursive-include arcade/resources *.yml
Expand Down
16 changes: 13 additions & 3 deletions arcade/experimental/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
"""
Experimental stuff. API may change.
"""
from .video_player import VideoPlayer
from .video_player import VideoPlayerView
from .video_player import VideoPlayer, VideoPlayerView
from .texture_render_target import RenderTargetTexture
from .shadertoy import Shadertoy, ShadertoyBuffer, ShadertoyBase
from .crt_filter import CRTFilter
from .bloom_filter import BloomFilter


__all__ = [
"VideoPlayer",
"VideoPlayerView",
Expand All @@ -19,3 +17,15 @@
"CRTFilter",
"BloomFilter",
]

# Keep cv2 an optional dependency
try:
from .video_cv2 import VideoPlayerCV2, CV2PlayerView # noqa: F401

__all__.extend([
"VideoPlayerCV2",
"CV2PlayerView",
])

except ImportError:
pass
42 changes: 29 additions & 13 deletions arcade/experimental/video_cv2.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ class VideoPlayerCV2:
:param path: Path of the video that is to be played.
"""

def __init__(self, path: Union[str, Path]):
def __init__(self, path: Union[str, Path], loop: bool = False):
self.loop = loop

self.ctx = arcade.get_window().ctx

Expand Down Expand Up @@ -74,31 +75,34 @@ def __init__(self, path: Union[str, Path]):
self.current_frame = 0
self.time: float = 0.0

# Get the number of frames in the video
self.frames: int = self.video.get(cv2.CAP_PROP_FRAME_COUNT)

# Create and configure the OpenGL texture for the video
self.texture = self.ctx.texture((self._width, self._height), components=3)
# Swap the components in the texture because cv2 returns BGR data
# Leave the alpha component as always 1
self.texture.swizzle = "BGR1"

@property
def width(self):
def width(self) -> int:
"""Video width."""
return self._width

@property
def height(self):
def height(self) -> int:
"""Video height."""
return self._height

def draw(self):
def draw(self) -> None:
"""Call this in `on_draw`."""

# Bind video texture to texture channel 0
self.texture.use(unit=0)
# Draw a fullscreen quad using our texture program
self.quad_fs.render(self.program)

def update(self, delta_time):
def update(self, delta_time: float) -> None:
"""Move the frame forward."""
self.time += delta_time

Expand All @@ -110,27 +114,39 @@ def update(self, delta_time):
exists, frame = self.video.read()
if exists:
self.texture.write(frame)
# loop if we are at the end of the video
elif self.loop:
self.time = 0.0
self.video.set(cv2.CAP_PROP_POS_FRAMES, 0)



class CV2PlayerView(arcade.View):
def __init__(self, path: str):
"""
A simple view to hold a video player using cv2.
Requires the opencv-python module to be installed.
:param path: Path of the video that is to be played.
:param resize: Change the window size to the video size
"""
def __init__(self, path: Union[str, Path], loop: bool = False, resize: bool = False):
super().__init__()

self.video_player = VideoPlayerCV2(path)
self.video_player = VideoPlayerCV2(path, loop)

# Change the window size to the video size
self.window.set_size(self.video_player.width, self.video_player.height)
if resize:
self.window.set_size(self.video_player.width, self.video_player.height)

def on_draw(self):
def on_draw(self) -> None:
self.clear()

self.video_player.draw()

def on_update(self, delta_time: float):
def on_update(self, delta_time: float) -> None:
self.video_player.update(delta_time)


if __name__ == '__main__':
window = arcade.Window(800, 600, "Video Player")
window.show_view(CV2PlayerView("/home/ibrahim/PycharmProjects/pyweek/35/Tetris-in-Ohio/assets/rain.mp4"))
window.show_view(CV2PlayerView(":resources:video/earth.mp4", loop=True, resize=False))
window.run()
19 changes: 8 additions & 11 deletions arcade/experimental/video_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class VideoPlayer:
:param loop: Pass `True` to make the video loop.
"""

def __init__(self, path: Union[str, Path], loop=False):
def __init__(self, path: Union[str, Path], loop: bool = False):
self.player = pyglet.media.Player()
self.player.loop = loop
self.player.queue(pyglet.media.load(str(arcade.resources.resolve_resource_path(path))))
Expand All @@ -31,7 +31,7 @@ def __init__(self, path: Union[str, Path], loop=False):
self._width = arcade.get_window().width
self._height = arcade.get_window().height

def draw(self, left: int = 0, bottom: int = 0, size: Optional[Tuple[int, int]] = None):
def draw(self, left: int = 0, bottom: int = 0, size: Optional[Tuple[int, int]] = None) -> None:
"""
Call this in `on_draw`.
Expand All @@ -53,16 +53,16 @@ def draw(self, left: int = 0, bottom: int = 0, size: Optional[Tuple[int, int]] =
)

@property
def width(self):
def width(self) -> int:
"""Video width."""
return self._width

@property
def height(self):
def height(self) -> int:
"""Video height."""
return self._height

def get_video_size(self):
def get_video_size(self) -> Tuple[int, int]:
if not self.player.source or not self.player.source.video_format:
return 0, 0
video_format = self.player.source.video_format
Expand All @@ -74,20 +74,17 @@ def get_video_size(self):
height /= video_format.sample_aspect
return width, height


class VideoPlayerView(arcade.View):
def __init__(self, path) -> None:
def __init__(self, path: Union[str, Path]) -> None:
super().__init__()

self.video_player = VideoPlayer(path)

def on_draw(self):
def on_draw(self) -> None:
self.clear()

self.video_player.draw()


if __name__ == '__main__':
window = arcade.Window(800, 600, "Video Player")
window.show_view(VideoPlayerView("/home/ibrahim/PycharmProjects/pyweek/35/Tetris-in-Ohio/assets/rain.mp4"))
window.show_view(VideoPlayerView(":resources:video/earth.mp4"))
window.run()
1 change: 1 addition & 0 deletions arcade/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ def get_resource_handle_paths(handle: str) -> List[Path]:
map_test_map_6 = ':assets:tiled_maps/test_map_6.json'
map_test_map_7 = ':assets:tiled_maps/test_map_7.json'
map_test_objects = ':assets:tiled_maps/test_objects.json'
video_earth = ':assets:video/earth.mp4'
gui_button_square_blue = ':system:gui_basic_assets/button_square_blue.png'
gui_button_square_blue_pressed = ':system:gui_basic_assets/button_square_blue_pressed.png'
gui_larger = ':system:gui_basic_assets/icons/larger.png'
Expand Down
Binary file added arcade/resources/assets/video/earth.mp4
Binary file not shown.
13 changes: 13 additions & 0 deletions arcade/resources/assets/video/readme.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
earth.mp4 is a public domain video that can be found in the NASA Scientific Visualization Studio.

https://svs.gsfc.nasa.gov/30082

Full Credits:
NASA Earth Observatory image by Robert Simmon, using Suomi NPP VIIRS data provided courtesy of Chris Elvidge (NOAA National Geophysical Data Center). Suomi NPP is the result of a partnership between NASA, NOAA, and the Department of Defense.

Animator: Robert Simmon (Sigma Space Corporation) [Lead]
Writer: Heather Hanson (GST)
Project support: Eric Sokolowsky (GST)

Further NASA SVS copyright and credit information can be found here:
https://svs.gsfc.nasa.gov/help/#copyrights-and-credits

0 comments on commit c836a53

Please sign in to comment.