hello alltogether,
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.
problem:
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))