Skip to content

Commit c836a53

Browse files
MrWardKKHSpushfoo
andauthored
Improvements to video players (#1786)
* 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 <[email protected]> * earth.mp4 replaces snake.mp4, fixed docstrings * Fix: ignore unused imports linting * earth.mp4 credits * *.mp4 added to manifest --------- Co-authored-by: Paul <[email protected]>
1 parent 71f61ea commit c836a53

File tree

7 files changed

+65
-27
lines changed

7 files changed

+65
-27
lines changed

MANIFEST.in

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ recursive-include arcade *.typed
77
recursive-include arcade/resources *.txt *.md *.url
88
recursive-include arcade/resources *.mp3 *.wav *.ogg
99
recursive-include arcade/resources *.png *.jpg *.gif
10+
recursive-include arcade/resources *.mp4
1011
recursive-include arcade/resources *.json
1112
recursive-include arcade/resources *.glsl
1213
recursive-include arcade/resources *.yml

arcade/experimental/__init__.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
"""
22
Experimental stuff. API may change.
33
"""
4-
from .video_player import VideoPlayer
5-
from .video_player import VideoPlayerView
4+
from .video_player import VideoPlayer, VideoPlayerView
65
from .texture_render_target import RenderTargetTexture
76
from .shadertoy import Shadertoy, ShadertoyBuffer, ShadertoyBase
87
from .crt_filter import CRTFilter
98
from .bloom_filter import BloomFilter
109

11-
1210
__all__ = [
1311
"VideoPlayer",
1412
"VideoPlayerView",
@@ -19,3 +17,15 @@
1917
"CRTFilter",
2018
"BloomFilter",
2119
]
20+
21+
# Keep cv2 an optional dependency
22+
try:
23+
from .video_cv2 import VideoPlayerCV2, CV2PlayerView # noqa: F401
24+
25+
__all__.extend([
26+
"VideoPlayerCV2",
27+
"CV2PlayerView",
28+
])
29+
30+
except ImportError:
31+
pass

arcade/experimental/video_cv2.py

+29-13
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ class VideoPlayerCV2:
2525
:param path: Path of the video that is to be played.
2626
"""
2727

28-
def __init__(self, path: Union[str, Path]):
28+
def __init__(self, path: Union[str, Path], loop: bool = False):
29+
self.loop = loop
2930

3031
self.ctx = arcade.get_window().ctx
3132

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

78+
# Get the number of frames in the video
79+
self.frames: int = self.video.get(cv2.CAP_PROP_FRAME_COUNT)
80+
7781
# Create and configure the OpenGL texture for the video
7882
self.texture = self.ctx.texture((self._width, self._height), components=3)
7983
# Swap the components in the texture because cv2 returns BGR data
8084
# Leave the alpha component as always 1
8185
self.texture.swizzle = "BGR1"
8286

8387
@property
84-
def width(self):
88+
def width(self) -> int:
8589
"""Video width."""
8690
return self._width
8791

8892
@property
89-
def height(self):
93+
def height(self) -> int:
9094
"""Video height."""
9195
return self._height
9296

93-
def draw(self):
97+
def draw(self) -> None:
9498
"""Call this in `on_draw`."""
9599

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

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

@@ -110,27 +114,39 @@ def update(self, delta_time):
110114
exists, frame = self.video.read()
111115
if exists:
112116
self.texture.write(frame)
117+
# loop if we are at the end of the video
118+
elif self.loop:
119+
self.time = 0.0
120+
self.video.set(cv2.CAP_PROP_POS_FRAMES, 0)
121+
113122

114123

115124
class CV2PlayerView(arcade.View):
116-
def __init__(self, path: str):
125+
"""
126+
A simple view to hold a video player using cv2.
127+
128+
Requires the opencv-python module to be installed.
129+
130+
:param path: Path of the video that is to be played.
131+
:param resize: Change the window size to the video size
132+
"""
133+
def __init__(self, path: Union[str, Path], loop: bool = False, resize: bool = False):
117134
super().__init__()
118135

119-
self.video_player = VideoPlayerCV2(path)
136+
self.video_player = VideoPlayerCV2(path, loop)
120137

121-
# Change the window size to the video size
122-
self.window.set_size(self.video_player.width, self.video_player.height)
138+
if resize:
139+
self.window.set_size(self.video_player.width, self.video_player.height)
123140

124-
def on_draw(self):
141+
def on_draw(self) -> None:
125142
self.clear()
126-
127143
self.video_player.draw()
128144

129-
def on_update(self, delta_time: float):
145+
def on_update(self, delta_time: float) -> None:
130146
self.video_player.update(delta_time)
131147

132148

133149
if __name__ == '__main__':
134150
window = arcade.Window(800, 600, "Video Player")
135-
window.show_view(CV2PlayerView("/home/ibrahim/PycharmProjects/pyweek/35/Tetris-in-Ohio/assets/rain.mp4"))
151+
window.show_view(CV2PlayerView(":resources:video/earth.mp4", loop=True, resize=False))
136152
window.run()

arcade/experimental/video_player.py

+8-11
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class VideoPlayer:
2020
:param loop: Pass `True` to make the video loop.
2121
"""
2222

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

34-
def draw(self, left: int = 0, bottom: int = 0, size: Optional[Tuple[int, int]] = None):
34+
def draw(self, left: int = 0, bottom: int = 0, size: Optional[Tuple[int, int]] = None) -> None:
3535
"""
3636
Call this in `on_draw`.
3737
@@ -53,16 +53,16 @@ def draw(self, left: int = 0, bottom: int = 0, size: Optional[Tuple[int, int]] =
5353
)
5454

5555
@property
56-
def width(self):
56+
def width(self) -> int:
5757
"""Video width."""
5858
return self._width
5959

6060
@property
61-
def height(self):
61+
def height(self) -> int:
6262
"""Video height."""
6363
return self._height
6464

65-
def get_video_size(self):
65+
def get_video_size(self) -> Tuple[int, int]:
6666
if not self.player.source or not self.player.source.video_format:
6767
return 0, 0
6868
video_format = self.player.source.video_format
@@ -74,20 +74,17 @@ def get_video_size(self):
7474
height /= video_format.sample_aspect
7575
return width, height
7676

77-
7877
class VideoPlayerView(arcade.View):
79-
def __init__(self, path) -> None:
78+
def __init__(self, path: Union[str, Path]) -> None:
8079
super().__init__()
81-
8280
self.video_player = VideoPlayer(path)
8381

84-
def on_draw(self):
82+
def on_draw(self) -> None:
8583
self.clear()
86-
8784
self.video_player.draw()
8885

8986

9087
if __name__ == '__main__':
9188
window = arcade.Window(800, 600, "Video Player")
92-
window.show_view(VideoPlayerView("/home/ibrahim/PycharmProjects/pyweek/35/Tetris-in-Ohio/assets/rain.mp4"))
89+
window.show_view(VideoPlayerView(":resources:video/earth.mp4"))
9390
window.run()

arcade/resources/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,7 @@ def get_resource_handle_paths(handle: str) -> List[Path]:
749749
map_test_map_6 = ':assets:tiled_maps/test_map_6.json'
750750
map_test_map_7 = ':assets:tiled_maps/test_map_7.json'
751751
map_test_objects = ':assets:tiled_maps/test_objects.json'
752+
video_earth = ':assets:video/earth.mp4'
752753
gui_button_square_blue = ':system:gui_basic_assets/button_square_blue.png'
753754
gui_button_square_blue_pressed = ':system:gui_basic_assets/button_square_blue_pressed.png'
754755
gui_larger = ':system:gui_basic_assets/icons/larger.png'
1.5 MB
Binary file not shown.
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
earth.mp4 is a public domain video that can be found in the NASA Scientific Visualization Studio.
2+
3+
https://svs.gsfc.nasa.gov/30082
4+
5+
Full Credits:
6+
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.
7+
8+
Animator: Robert Simmon (Sigma Space Corporation) [Lead]
9+
Writer: Heather Hanson (GST)
10+
Project support: Eric Sokolowsky (GST)
11+
12+
Further NASA SVS copyright and credit information can be found here:
13+
https://svs.gsfc.nasa.gov/help/#copyrights-and-credits

0 commit comments

Comments
 (0)