psychopy.org | Reference | Downloads | Github

What is the official way to loop a movie?

Hi, I tried every means I could figure out but just couldn’t loop a movie. Now the only way I can do for looping a movie is to set a stop time (so that Psychopy can be aware that the playback has reached its end) and re-create an MovieStim object. All other approaches just do not work, e.g., seek() doesn’t work, loop attribute doesn’t work, etc. Obviously my way of re-creating a movie simulation is awkward, so what is the official way to loop a movie? This problem has puzzled me for a long time. Thanks a lot for an answer to save me out.

PS: I am using pyglet 1.2.4.

Don’t know if this is still relevant to the original poster, but having done a bit of messing around with MovieStim, I thought I should share some useful things I have learned about the interplay between seek, pause, play, and draw.

I’m using Psychopy 1.84.1 on a mac running OSX 10.9.5, playing .mov quicktime movie files.

To loop movies, I use a global frame counter that starts from 0. Every time the screen is flipped while the movie is playing, I add 1, and then compare the frame counter to the duration of the movie in FRAMES, which you can get with movie.duration*60 (assuming 60fps). Ultimately, what trips the loop is this code:

if frameCount >= (dispMovie.duration*60)-3:
        dispMovie.pause()
        dispMovie.draw()
        win.flip()
        print('repeating at frame ' + str(frameCount))
        frameCount = 0
        dispMovie.seek(frameCount)

Why -3? Because I want the second-to-last frame, and there’s some imprecision in calculating the number of frames this way. -2 should be sufficient, but -3 protects against rounding errors. If your last three frames matter, maybe add some blanks at the end.

The important thing to know about this is that the “seek” doesn’t take effect until the next time dispMovie.play() is called. That is, if I wanted it to show the first frame again but not advance, I would have to call “play” before calling “draw”, or it would continue to get the frame that it stopped on. As soon as “play” is called, it seems to sort itself out and draw the correct frame when “draw” is called.

1 Like

Update: I’ve discovered a better way.

if dispMovie.getCurrentFrameTime() >= dispMovie.duration-.05:
        print('repeating at ' + str(dispMovie.getCurrentFrameTime())) 
        pauseCount = 0
        dispMovie.seek(0.01) #'seek' and 'play' have interesting interactions w/r/t audio. 
        dispMovie.draw() # Comment this out to blank between loops.
        win.flip()

Rather than relying on a manual frame count, which I’ve found to be deeply inconsistent, this uses the movie’s self-reported time and picks when to loop based on that. Everything is now in seconds rather than frames. It seeks .01 because seek(0) seems to introduce a lot of glitches.