I played around and did quite some testing, but couldn’t really find a solution for the following puzzle. maybe someone here can clearify to me what’s happening. if not, maybe it helps others, who run into similar problems.
for an eeg experiment I want to present video stimuli with decent and reliable time-precision. I use moviestim3 and a set of mp4 video clips, all encoded using the same codec specifications.
I first decided for a frame-refresh based presentation loop, since this should in general give you the most control over your stimuli.
the output looks good (not sped up or similar). yet, neither on my coding machine (mac os, ~60hz), nor on my lab pcs (windows, 144hz lcd) I could get this to work with reliable accuracy.
eg. presenting 10s of the clips on my 144hz panel (ie. for 10x144 = 1440 frames) the actual playback duration differs from the defined (10s) in a range between 50 to 500ms! scaling with presentation duration.
using a countdown-timer + while loop, I also have some milliseconds inaccuracies, but these don’t scale up with longer presentation durations, and are much more reproduceable.
note that I check the timing very rudimentally using the system clock and log-file
in both conditions there are barely frames dropped, and most interestingly the frame counter for the while loop usually results in identical frame counts (eg. 1440 frames for 10s).
I really dont understand what’s happening, and in general what moviestim does is not very transparent to me. is it possible that the movie-objects hold some additional information on frame-timing that differ from the displays refresh-rates and can hence not be reproduced presenting them frame by frame?
I am curious to here your comments on that!
below some simplified code:
# init import os import logging import numpy as np from psychopy import visual, core, event, monitors, gui, parallel # parameter nStim = 2 time_pres = 10 # in s framerate = 144 # in hz trig_start = 255 # trigger id trig_end = 250 # trigger id dir_stim = 'path/to/stimuli' stimlist = ['ABC.mp4', 'XYZ.mp4'] # init logfile logging.basicConfig(filename='logfile.log', format='%(asctime)s - %(levelname)s: %(message)s', level=logging.DEBUG) console = logging.StreamHandler() # log also to console logging.getLogger('').addHandler(console) # init window screen = monitors.Monitor('testMonitor', width=53, distance=60) win = visual.Window(size=[1920, 1080]], screen=0, winType='pyglet', allowGUI=False, # show mouse fullscr=True, monitor=screen color='grey', colorSpace='rgb', units='pix', useFBO=True ) # init trigger function def send_trigger(triggerID): logging.debug('trigger: %i' % triggerID) # load movies video_paths =  movies =  for iStim, stim in enumerate(stimlist): video_paths.append(os.path.join(dir_stim, stim)) movies.append( visual.MovieStim3(win, video_paths[iStim], size=(1280, 720), pos=[0, 0], flipVert=False, flipHoriz=False, loop=False, noAudio=True) )
presentation loop (frame-by-frame)
frames_pres = time_pres * framerate for iStim in range(nStim): # let's draw a dynamic stimulus for exactly n frames for iFrame in range(frames_pres): movies[iStim].draw() # flip buffer win.flip() # send trigger after first flip and start recording flip-duration if iFrame == 0: win.recordFrameIntervals = True # check for framedrops send_trigger(trig_start) # send end-of-presentation trigger win.flip() # send trigger send_trigger(trig_end) # stop frame-flip recording win.recordFrameIntervals = False # log info about presentation logging.info('STIMULUS: %i' % iStim) logging.info('FRAMES DROPPED: %i' % win.nDroppedFrames) logging.info('MEAN FRAMEINTERVAL: %f s' % np.mean(win.frameIntervals)) logging.info('EFF. FRAMERATE: %f Hz' % (1./np.mean(win.frameIntervals))) logging.info('PRESENTED FRAMES: %i' % len(win.frameIntervals))
alternative presentation loop
for iStim in range(nStim): timer_dyn = core.CountdownTimer(time_pres) trig = True while timer_dyn.getTime() > 0: # draw movie movies[iStim].draw() # flip win.flip() # send trigger at first flip and start recording flip-duration if trig: win.recordFrameIntervals = True # check for framedrops send_trigger(trig_start) trig = False # send end-of-presentation trigger win.flip() send_trigger(trig_end) # stop frame-flip recording win.recordFrameIntervals = False # log info about presentation logging.info('STIMULUS: %i' % iStim) logging.info('FRAMES DROPPED: %i' % win.nDroppedFrames) logging.info('MEAN FRAMEINTERVAL: %f s' % np.mean(win.frameIntervals)) logging.info('EFF. FRAMERATE: %f Hz' % (1./np.mean(win.frameIntervals))) logging.info('PRESENTED FRAMES: %i' % len(win.frameIntervals))