├── README.md ├── pyvidplayer.py └── requirements.txt /README.md: -------------------------------------------------------------------------------- 1 | # OUTDATED! 2 | Use this instead -> https://github.com/ree1261/pyvidplayer2 3 | 4 | # pyvidplayer 5 | An extremely easy to use module that plays videos on Pygame. 6 | 7 | BaralTech has a good tutorial ---> https://www.youtube.com/watch?v=Xu8SLkvFq8I&ab_channel=BaralTech 8 | 9 | # updated once again! 10 | There was an egregious bug that prevented ```active``` from turning false on some videos, which I barely fixed. 11 | Playing videos in Python is just very janky but hopefully this script can serve you well. 12 | 13 | Changes: 14 | - fixed (hopefully) the ```active``` bug 15 | - changed some variables to internal use 16 | - added back the close function 17 | - added typing hints 18 | - added seperate pause and resume functions 19 | - significantly reduced console output (although unfortunately there's still some) 20 | 21 | notice: Try to save the issues tab for genuine bugs with the script. If you just have a question, you can email me at anrayliu@gmail.com 22 | 23 | # **Example** 24 | ``` 25 | import pygame 26 | from pyvidplayer import Video 27 | 28 | pygame.init() 29 | win = pygame.display.set_mode((1280, 720)) 30 | clock = pygame.time.Clock() 31 | 32 | #provide video class with the path to your video 33 | vid = Video("vid.mp4") 34 | 35 | while True: 36 | key = None 37 | for event in pygame.event.get(): 38 | if event.type == pygame.QUIT: 39 | vid.close() 40 | pygame.quit() 41 | exit() 42 | elif event.type == pygame.KEYDOWN: 43 | key = pygame.key.name(event.key) 44 | 45 | #your program frame rate does not affect video playback 46 | clock.tick(60) 47 | 48 | if key == "r": 49 | vid.restart() #rewind video to beginning 50 | elif key == "p": 51 | vid.toggle_pause() #pause/plays video 52 | elif key == "right": 53 | vid.seek(15) #skip 15 seconds in video 54 | elif key == "left": 55 | vid.seek(-15) #rewind 15 seconds in video 56 | elif key == "up": 57 | vid.set_volume(1.0) #max volume 58 | elif key == "down": 59 | vid.set_volume(0.0) #min volume 60 | 61 | #draws the video to the given surface, at the given position 62 | vid.draw(win, (0, 0), force_draw=False) 63 | 64 | pygame.display.update() 65 | ``` 66 | 67 | # Properties 68 | The video class now has a bunch of new properties 69 | - ```path``` 70 | - ```name``` 71 | - ```frame_count``` 72 | - ```frame_rate``` 73 | - ```duration``` 74 | - ```original_size``` 75 | - ```current_size``` 76 | - ```active``` - becomes false when the video finishes playing 77 | - ```frame_surf``` - current video frame as a pygame surface 78 | - ```alt_resize``` - fallback resizing function in case the usual one fails. by default this is 79 | ```pygame.transform.smoothscale```, which is a bit cpu intensive, so you can switch it 80 | to ```pygame.transform.scale``` if you don't mind the video looking uglier 81 | 82 | # Functions 83 | - ```restart()``` 84 | - ```set_size(size)``` - resizes the video with ffpyplayer's resize function. This is a lot 85 | lighter on the cpu than the fallback function, but it sometimes doesn't work 86 | - ```set_volume(volume)``` - from 0.0 to 1.0 87 | - ```get_volume()``` 88 | - ```toggle_pause()``` 89 | - ```get_paused()``` 90 | - ```pause()``` 91 | - ```resume()``` 92 | - ```close()``` - releases video resources 93 | - ```get_pos()``` - returns the current time in the video in seconds 94 | - ```seek(time)``` - moves forwards or backwards by time in seconds in the video. 95 | Note that when seeking backwards, the video will temporaily freeze. This seems to 96 | be an issue with ffpyplayer, and I can't fix it (trust me I tried) 97 | - ```draw(surf, pos, force_draw=True)``` - draws the current video frame onto the given surface at the given position. If 98 | ```force_draw``` is enabled, a surface will be drawn every time draw is called. If it's 99 | disabled, a surface will only be drawn when a new frame from the video is made which saves cpu 100 | -------------------------------------------------------------------------------- /pyvidplayer.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import os 3 | from ffpyplayer.player import MediaPlayer 4 | from ffpyplayer.tools import set_loglevel 5 | from pymediainfo import MediaInfo 6 | from errno import ENOENT 7 | 8 | 9 | class Video: 10 | def __init__(self, path): 11 | if not os.path.exists(path): 12 | raise FileNotFoundError(ENOENT, os.strerror(ENOENT), path) 13 | set_loglevel("quiet") 14 | 15 | self.path = path 16 | self.name = os.path.splitext(os.path.basename(path))[0] 17 | 18 | self._video = MediaPlayer(path) 19 | self._frame_num = 0 20 | 21 | info = MediaInfo.parse(path).video_tracks[0] 22 | 23 | self.frame_rate = float(info.frame_rate) 24 | self.frame_count = int(info.frame_count) 25 | self.frame_delay = 1 / self.frame_rate 26 | self.duration = info.duration / 1000 27 | self.original_size = (info.width, info.height) 28 | self.current_size = self.original_size 29 | 30 | self.active = True 31 | self.frame_surf = pygame.Surface((0, 0)) 32 | 33 | self.alt_resize = pygame.transform.smoothscale 34 | 35 | def close(self): 36 | self._video.close_player() 37 | 38 | def restart(self): 39 | self._video.seek(0, relative=False) 40 | self._frame_num = 0 41 | self.frame_surf = None 42 | self.active = True 43 | 44 | def set_size(self, size: tuple): 45 | self._video.set_size(*size) 46 | self.current_size = size 47 | 48 | # volume goes from 0.0 to 1.0 49 | def set_volume(self, volume: float): 50 | self._video.set_volume(volume) 51 | 52 | def get_volume(self) -> float: 53 | return self._video.get_volume() 54 | 55 | def get_paused(self) -> bool: 56 | return self._video.get_pause() 57 | 58 | def pause(self): 59 | self._video.set_pause(True) 60 | 61 | def resume(self): 62 | self._video.set_pause(False) 63 | 64 | # gets time in seconds 65 | def get_pos(self) -> float: 66 | return self._video.get_pts() 67 | 68 | def toggle_pause(self): 69 | self._video.toggle_pause() 70 | 71 | def _update(self): 72 | updated = False 73 | 74 | if self._frame_num + 1 == self.frame_count: 75 | self.active = False 76 | return False 77 | 78 | while self._video.get_pts() > self._frame_num * self.frame_delay: 79 | frame = self._video.get_frame()[0] 80 | self._frame_num += 1 81 | 82 | if frame != None: 83 | size = frame[0].get_size() 84 | img = pygame.image.frombuffer(frame[0].to_bytearray()[0], size, "RGB") 85 | if size != self.current_size: 86 | img = self.alt_resize(img, self.current_size) 87 | self.frame_surf = img 88 | 89 | updated = True 90 | 91 | return updated 92 | 93 | # seek uses seconds 94 | def seek(self, seek_time: int): 95 | vid_time = self._video.get_pts() 96 | if vid_time + seek_time < self.duration and self.active: 97 | self._video.seek(seek_time) 98 | while vid_time + seek_time < self._frame_num * self.frame_delay: 99 | self._frame_num -= 1 100 | 101 | def draw(self, surf: pygame.Surface, pos: tuple, force_draw: bool = True) -> bool: 102 | if self.active and (self._update() or force_draw): 103 | surf.blit(self.frame_surf, pos) 104 | return True 105 | 106 | return False 107 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ffpyplayer==4.3.2 2 | pygame==2.0.1 3 | pymediainfo==5.1.0 4 | --------------------------------------------------------------------------------