def load_media(self, vidSource): """ Loads a video. Parameters ---------- vidSource : str The path to the video file """ if not os.path.exists(vidSource): print("File not found: " + vidSource) pygame.display.quit() pygame.quit() sys.exit(1) self.decoder.load_media(vidSource) self.decoder.loop = self.loop pygame.display.set_caption(os.path.split(vidSource)[1]) self.vidsize = self.decoder.clip.size self.destsize = self.calc_scaled_res(self.windowSize, self.vidsize) self.vidPos = ((self.windowSize[0] - self.destsize[0]) / 2, (self.windowSize[1] - self.destsize[1]) / 2) self.__textureSetup() if (self.decoder.audioformat): if self.soundrenderer == "pygame": from mediadecoder.soundrenderers import SoundrendererPygame self.audio = SoundrendererPygame(self.decoder.audioformat) elif self.soundrenderer == "pyaudio": from mediadecoder.soundrenderers.pyaudiorenderer import SoundrendererPyAudio self.audio = SoundrendererPyAudio(self.decoder.audioformat) elif self.soundrenderer == "sounddevice": from mediadecoder.soundrenderers.sounddevicerenderer import SoundrendererSounddevice self.audio = SoundrendererSounddevice(self.decoder.audioformat) self.decoder.set_audiorenderer(self.audio)
def load_media(_, vidSource, audio=True): logger.debug('loading %s' % vidSource) if not os.path.exists(vidSource): raise Exception("File not found: " + vidSource) _.decoder.load_media(vidSource, audio) _.decoder.loop = _.loop _.vidsize = _.decoder.clip.size _.width = _.vidsize[0] _.height = _.vidsize[1] _.__textureSetup() if (_.decoder.audioformat): if _.soundrenderer == "pygame": from mediadecoder.soundrenderers import SoundrendererPygame _.audio = SoundrendererPygame(_.decoder.audioformat) elif _.soundrenderer == "pyaudio": from mediadecoder.soundrenderers.pyaudiorenderer import SoundrendererPyAudio _.audio = SoundrendererPyAudio(_.decoder.audioformat) elif _.soundrenderer == "sounddevice": from mediadecoder.soundrenderers.sounddevicerenderer import SoundrendererSounddevice _.audio = SoundrendererSounddevice(_.decoder.audioformat) _.decoder.set_audiorenderer(_.audio) _.play()
def preload(self): """Preload stimulus to memory. Notes ----- When the audio from the video should be played as well, the audiosystem has to be stopped (by calling ``expyriment.control.stop_audiosystem()``) BEFORE the video stimulus is preloaded! After the stimulus has been played the audiosystem can be started again (by calling ``expyriment.control.start_audiosystem()``). """ if not self._is_preloaded: if not _internals.active_exp.is_initialized: message = "Can't preload video. Expyriment needs to be " + \ "initialized before preloading a video." raise RuntimeError(message) if self._backend == "pygame": self._file = pygame.movie.Movie(self._filename) size = self._file.get_size() elif self._backend == "mediadecoder": if self.get_ffmpeg_binary() is None: raise RuntimeError("'ffmpeg' not found!") from mediadecoder.states import PLAYING from mediadecoder.decoder import Decoder self._file = Decoder(mediafile=self._filename, videorenderfunc=self._update_surface) if _internals.active_exp._screen.open_gl: import moviepy.video.fx.all as vfx self._file.clip = vfx.mirror_y(self._file.clip) if self._file.audioformat: try: from mediadecoder.soundrenderers.sounddevicerenderer import SoundrendererSounddevice self._audio = SoundrendererSounddevice( self._file.audioformat) self._audio_renderer = "sounddevice" except ImportError: from mediadecoder.soundrenderers import SoundrendererPygame self._audio = SoundrendererPygame( self._file.audioformat) self._audio_renderer = "pygame" self._file.set_audiorenderer(self._audio) size = self._file.clip.size else: raise RuntimeError("Unknown backend '{0}'!".format( self._backend)) screen_size = _internals.active_exp.screen.surface.get_size() self._pos = [ screen_size[0] // 2 - size[0] // 2 + self._position[0], screen_size[1] // 2 - size[1] // 2 - self._position[1] ] if self._backend == "pygame": self._surface = pygame.surface.Surface(size) self._file.set_display(self._surface) self._is_paused = False self._is_preloaded = True
class VideoPlayer(): """ This is an example videoplayer that uses pygame+pyopengl to render a video. It uses the Decoder object to decode the video- and audiostream frame by frame. It shows each videoframe in a window and places the audioframes in a buffer (or queue) from which they are fetched by the soundrenderer object. """ def __init__(self, dimensions, fullscreen=False, soundrenderer="pyaudio", loop=False): """ Constructor. Parameters ---------- dimensions : tuple (width, height) The dimension of the window in which the video should be shown. Aspect ratio is maintained. fullscreen : bool, optional Indicates whether the video should be displayed in fullscreen. soundrenderer : {'pyaudio','pygame'} Designates which sound backend should render the sound. """ pygame.init() (windowWidth, windowHeight) = dimensions flags = pygame.DOUBLEBUF | pygame.OPENGL | pygame.HWSURFACE self.fullscreen = fullscreen if fullscreen: flags = flags | pygame.FULLSCREEN pygame.display.set_mode((windowWidth, windowHeight), flags) self.windowSize = (windowWidth, windowHeight) self.soundrenderer = soundrenderer self.loop = loop self.texUpdated = False self.__initGL() self.decoder = Decoder( videorenderfunc=self.__texUpdate, ) self.texture_locked = False def __initGL(self): glViewport(0, 0, self.windowSize[0], self.windowSize[1]) glPushAttrib(GL_ENABLE_BIT) glDisable(GL_DEPTH_TEST) glDisable(GL_CULL_FACE) glDepthFunc(GL_ALWAYS) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glMatrixMode(GL_PROJECTION) glPushMatrix() glLoadIdentity() glOrtho(0.0, self.windowSize[0], self.windowSize[1], 0.0, 0.0, 1.0) glMatrixMode(GL_MODELVIEW) glPushMatrix() glColor4f(1, 1, 1, 1) glClearColor(0.0, 0.0, 0.0, 1.0) glClearDepth(1.0) def calc_scaled_res(self, screen_res, image_res): """Calculate appropriate texture size. Calculate size or required texture so that it will fill the window, but retains the movies original aspect ratio. Parameters ---------- screen_res : tuple Display window size/Resolution image_res : tuple Image width and height Returns ------- tuple width and height of image scaled to window/screen """ rs = screen_res[0] / float(screen_res[1]) ri = image_res[0] / float(image_res[1]) if rs > ri: return (int(image_res[0] * screen_res[1] / image_res[1]), screen_res[1]) else: return (screen_res[0], int(image_res[1] * screen_res[0] / image_res[0])) def load_media(self, vidSource): """ Loads a video. Parameters ---------- vidSource : str The path to the video file """ if not os.path.exists(vidSource): print("File not found: " + vidSource) pygame.display.quit() pygame.quit() sys.exit(1) self.decoder.load_media(vidSource) self.decoder.loop = self.loop pygame.display.set_caption(os.path.split(vidSource)[1]) self.vidsize = self.decoder.clip.size self.destsize = self.calc_scaled_res(self.windowSize, self.vidsize) self.vidPos = ((self.windowSize[0] - self.destsize[0]) / 2, (self.windowSize[1] - self.destsize[1]) / 2) self.__textureSetup() if (self.decoder.audioformat): if self.soundrenderer == "pygame": from mediadecoder.soundrenderers import SoundrendererPygame self.audio = SoundrendererPygame(self.decoder.audioformat) elif self.soundrenderer == "pyaudio": from mediadecoder.soundrenderers.pyaudiorenderer import SoundrendererPyAudio self.audio = SoundrendererPyAudio(self.decoder.audioformat) elif self.soundrenderer == "sounddevice": from mediadecoder.soundrenderers.sounddevicerenderer import SoundrendererSounddevice self.audio = SoundrendererSounddevice(self.decoder.audioformat) self.decoder.set_audiorenderer(self.audio) def __textureSetup(self): # Setup texture in OpenGL to render video to glEnable(GL_TEXTURE_2D) glMatrixMode(GL_MODELVIEW) self.textureNo = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, self.textureNo) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) # Fill texture with black to begin with. img = np.zeros([self.vidsize[0], self.vidsize[1], 3], dtype=np.uint8) img.fill(0) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, self.vidsize[0], self.vidsize[1], 0, GL_RGB, GL_UNSIGNED_BYTE, img) # Create display list which draws to the quad to which the texture is rendered (x, y) = self.vidPos (w, h) = self.destsize x, y = int(x), int(y) w, h = int(w), int(h) self.frameQuad = glGenLists(1); glNewList(self.frameQuad, GL_COMPILE) glBegin(GL_QUADS) glTexCoord2f(0.0, 0.0) glVertex3i(x, y, 0) glTexCoord2f(1.0, 0.0) glVertex3i(x + w, y, 0) glTexCoord2f(1.0, 1.0) glVertex3i(x + w, y + h, 0) glTexCoord2f(0.0, 1.0) glVertex3i(x, y + h, 0) glEnd() glEndList() # Clear The Screen And The Depth Buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) def __texUpdate(self, frame): """ Update the texture with the newly supplied frame. """ # Retrieve buffer from videosink if self.texture_locked: return self.buffer = frame self.texUpdated = True def __drawFrame(self): """ Draws a single frame. """ # Clear The Screen And The Depth Buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glCallList(self.frameQuad) def play(self): """ Starts playback. """ # Signal player to start video playback self.paused = False # Start listening for incoming audio frames if self.decoder.audioformat: self.audio.start() self.decoder.play() # While video is playing, render frames while self.decoder.status in [mediadecoder.PLAYING, mediadecoder.PAUSED]: texture_update_time = 0 if self.texUpdated: t1 = time.time() # Update texture self.texture_locked = True glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, self.vidsize[0], self.vidsize[1], GL_RGB, GL_UNSIGNED_BYTE, self.buffer) self.texture_locked = False texture_update_time = int((time.time() - t1) * 1000) self.texUpdated = False # Draw the texture to the back buffer t1 = time.time() self.__drawFrame() draw_time = (time.time() - t1) * 1000 # Flip the buffer to show frame to screen t1 = time.time() pygame.display.flip() flip_time = (time.time() - t1) * 1000 logger.debug("================== Frame {} ========================" "".format(self.decoder.current_frame_no)) if texture_update_time: logger.debug("Texture updated in {0} ms".format(texture_update_time)) logger.debug("Texture drawn in {0} ms".format(draw_time)) logger.debug("Screen flipped in {0} ms".format(flip_time)) logger.debug("-----------------------------------------------------") logger.debug("Total: {} ms".format(texture_update_time + draw_time + flip_time)) for e in pygame.event.get(): if e.type == pygame.QUIT: self.stop() if e.type == pygame.KEYDOWN: # Quitting if e.key == pygame.K_ESCAPE: self.stop() # Pausing elif e.key == pygame.K_SPACE: self.pause() # Seeking elif e.key == pygame.K_RIGHT: new_time = min( self.decoder.current_playtime + 10, self.decoder.duration) self.decoder.seek(new_time) elif e.key == pygame.K_LEFT: new_time = max( self.decoder.current_playtime - 10, 0) self.decoder.seek(new_time) pygame.event.pump() # Prevent freezing of screen while dragging # Without this sleep, the video rendering threard goes haywire... time.sleep(0.005) if self.decoder.audioformat: self.audio.close_stream() pygame.quit() def stop(self): """ Stops playback. """ self.decoder.stop() def pause(self): """ Pauses playback. """ if self.decoder.status == mediadecoder.PAUSED: self.decoder.pause() self.paused = False elif self.decoder.status == mediadecoder.PLAYING: self.decoder.pause() self.paused = True else: print("Player not in pausable state")
class VideoPlayer(): """ This is an example videoplayer that uses pygame+pyopengl to render a video. It uses the Decoder object to decode the video- and audiostream frame by frame. It shows each videoframe in a window and places the audioframes in a buffer (or queue) from which they are fetched by the soundrenderer object. """ def __init__(self, dimensions, fullscreen=False, soundrenderer="pyaudio", loop=False): """ Constructor. Parameters ---------- dimensions : tuple (width, height) The dimension of the window in which the video should be shown. Aspect ratio is maintained. fullscreen : bool, optional Indicates whether the video should be displayed in fullscreen. soundrenderer : {'pyaudio','pygame'} Designates which sound backend should render the sound. """ pygame.init() (windowWidth, windowHeight) = dimensions flags = pygame.DOUBLEBUF | pygame.OPENGL | pygame.HWSURFACE self.fullscreen = fullscreen if fullscreen: flags = flags | pygame.FULLSCREEN pygame.display.set_mode((windowWidth, windowHeight), flags) self.windowSize = (windowWidth, windowHeight) self.soundrenderer = soundrenderer self.loop = loop self.texUpdated = False self.__initGL() self.decoder = Decoder(videorenderfunc=self.__texUpdate, ) self.texture_locked = False def __initGL(self): glViewport(0, 0, self.windowSize[0], self.windowSize[1]) glPushAttrib(GL_ENABLE_BIT) glDisable(GL_DEPTH_TEST) glDisable(GL_CULL_FACE) glDepthFunc(GL_ALWAYS) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glMatrixMode(GL_PROJECTION) glPushMatrix() glLoadIdentity() glOrtho(0.0, self.windowSize[0], self.windowSize[1], 0.0, 0.0, 1.0) glMatrixMode(GL_MODELVIEW) glPushMatrix() glColor4f(1, 1, 1, 1) glClearColor(0.0, 0.0, 0.0, 1.0) glClearDepth(1.0) def calc_scaled_res(self, screen_res, image_res): """Calculate appropriate texture size. Calculate size or required texture so that it will fill the window, but retains the movies original aspect ratio. Parameters ---------- screen_res : tuple Display window size/Resolution image_res : tuple Image width and height Returns ------- tuple width and height of image scaled to window/screen """ rs = screen_res[0] / float(screen_res[1]) ri = image_res[0] / float(image_res[1]) if rs > ri: return (int(image_res[0] * screen_res[1] / image_res[1]), screen_res[1]) else: return (screen_res[0], int(image_res[1] * screen_res[0] / image_res[0])) def load_media(self, vidSource): """ Loads a video. Parameters ---------- vidSource : str The path to the video file """ if not os.path.exists(vidSource): print("File not found: " + vidSource) pygame.display.quit() pygame.quit() sys.exit(1) self.decoder.load_media(vidSource) self.decoder.loop = self.loop pygame.display.set_caption(os.path.split(vidSource)[1]) self.vidsize = self.decoder.clip.size self.destsize = self.calc_scaled_res(self.windowSize, self.vidsize) self.vidPos = ((self.windowSize[0] - self.destsize[0]) / 2, (self.windowSize[1] - self.destsize[1]) / 2) self.__textureSetup() if (self.decoder.audioformat): if self.soundrenderer == "pygame": from mediadecoder.soundrenderers import SoundrendererPygame self.audio = SoundrendererPygame(self.decoder.audioformat) elif self.soundrenderer == "pyaudio": from mediadecoder.soundrenderers.pyaudiorenderer import SoundrendererPyAudio self.audio = SoundrendererPyAudio(self.decoder.audioformat) elif self.soundrenderer == "sounddevice": from mediadecoder.soundrenderers.sounddevicerenderer import SoundrendererSounddevice self.audio = SoundrendererSounddevice(self.decoder.audioformat) self.decoder.set_audiorenderer(self.audio) def __textureSetup(self): # Setup texture in OpenGL to render video to glEnable(GL_TEXTURE_2D) glMatrixMode(GL_MODELVIEW) self.textureNo = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, self.textureNo) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) # Fill texture with black to begin with. img = np.zeros([self.vidsize[0], self.vidsize[1], 3], dtype=np.uint8) img.fill(0) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, self.vidsize[0], self.vidsize[1], 0, GL_RGB, GL_UNSIGNED_BYTE, img) # Create display list which draws to the quad to which the texture is rendered (x, y) = self.vidPos (w, h) = self.destsize x, y = int(x), int(y) w, h = int(w), int(h) self.frameQuad = glGenLists(1) glNewList(self.frameQuad, GL_COMPILE) glBegin(GL_QUADS) glTexCoord2f(0.0, 0.0) glVertex3i(x, y, 0) glTexCoord2f(1.0, 0.0) glVertex3i(x + w, y, 0) glTexCoord2f(1.0, 1.0) glVertex3i(x + w, y + h, 0) glTexCoord2f(0.0, 1.0) glVertex3i(x, y + h, 0) glEnd() glEndList() # Clear The Screen And The Depth Buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) def __texUpdate(self, frame): """ Update the texture with the newly supplied frame. """ # Retrieve buffer from videosink if self.texture_locked: return self.buffer = frame self.texUpdated = True def __drawFrame(self): """ Draws a single frame. """ # Clear The Screen And The Depth Buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glCallList(self.frameQuad) def play(self): """ Starts playback. """ # Signal player to start video playback self.paused = False # Start listening for incoming audio frames if self.decoder.audioformat: self.audio.start() self.decoder.play() # While video is playing, render frames while self.decoder.status in [ mediadecoder.PLAYING, mediadecoder.PAUSED ]: texture_update_time = 0 if self.texUpdated: t1 = time.time() # Update texture self.texture_locked = True glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, self.vidsize[0], self.vidsize[1], GL_RGB, GL_UNSIGNED_BYTE, self.buffer) self.texture_locked = False texture_update_time = int((time.time() - t1) * 1000) self.texUpdated = False # Draw the texture to the back buffer t1 = time.time() self.__drawFrame() draw_time = (time.time() - t1) * 1000 # Flip the buffer to show frame to screen t1 = time.time() pygame.display.flip() flip_time = (time.time() - t1) * 1000 logger.debug("================== Frame {} ========================" "".format(self.decoder.current_frame_no)) if texture_update_time: logger.debug( "Texture updated in {0} ms".format(texture_update_time)) logger.debug("Texture drawn in {0} ms".format(draw_time)) logger.debug("Screen flipped in {0} ms".format(flip_time)) logger.debug( "-----------------------------------------------------") logger.debug("Total: {} ms".format(texture_update_time + draw_time + flip_time)) for e in pygame.event.get(): if e.type == pygame.QUIT: self.stop() if e.type == pygame.KEYDOWN: # Quitting if e.key == pygame.K_ESCAPE: self.stop() # Pausing elif e.key == pygame.K_SPACE: self.pause() # Seeking elif e.key == pygame.K_RIGHT: new_time = min(self.decoder.current_playtime + 10, self.decoder.duration) self.decoder.seek(new_time) elif e.key == pygame.K_LEFT: new_time = max(self.decoder.current_playtime - 10, 0) self.decoder.seek(new_time) pygame.event.pump() # Prevent freezing of screen while dragging # Without this sleep, the video rendering threard goes haywire... time.sleep(0.005) if self.decoder.audioformat: self.audio.close_stream() pygame.quit() def stop(self): """ Stops playback. """ self.decoder.stop() def pause(self): """ Pauses playback. """ if self.decoder.status == mediadecoder.PAUSED: self.decoder.pause() self.paused = False elif self.decoder.status == mediadecoder.PLAYING: self.decoder.pause() self.paused = True else: print("Player not in pausable state")