(a) I think that void cleanup redundant and only cleanup works, but I like that you are not 100% sure that this is exactly what IO () does exactly.
I do not see a direct way to read FPS, but imageReaderTime creates timestamps in seconds, which will give you a good indicator. To expand the timestamp, you need to change:
nextFrame :: IO (Maybe (Image PixelRGB8, Double)) -> IO (Double, Picture) nextFrame getFrame = fmap fromImageRGB8 . swap . fromJust <$> getFrame
Then you would say:
stampedFrames <- replicateM frameCount $ nextFrame getFrame let (tstamps, frames) = unzip stampedFrames let approx_fps = fromIntegral (length tstamps) / (maximum tstamps - minimum tstamps)
Finally, you can pass approx_fps as the parameter to frameAt , which should use Double , not Float , as well as some type input function.
However, for what you are doing, it might be better to have something like:
frameAt :: [(Double, Picture)] -> Double -> Picture frameAt list time = snd . head . dropWhile ((< time) . fst) $ list
This takes a list, discards all elements whose first element (timestamp) is less than the requested time, and then returns the second element (image) of the first pair that occurs after that. No need for FPS.
source share