How to find mp4 metadata with ffmpeg-light in haskell?

I use ffmpeg-light, JuicyPixels and gloss to display video from Haskell. I want to find the metadata of the videos that I play automatically, but I have not yet found a way to do this.

I would like to access metadata such as resolution and video frame rate.

Can you help me?

EDIT:

I tried your solution @CRDrost, but now the video plays at normal speed 2 times. I assume that the imageReaderTime function indicates invalid timestamps.

EDIT 2:

Abnormal playback speed is an error in the ffmpeg-light library. I opened issue in github repository.

My updated code:

import Graphics.Gloss import Codec.FFmpeg import Codec.FFmpeg.Juicy import Codec.Picture import Control.Applicative import Data.Maybe import Graphics.Gloss.Juicy import Control.Monad -- import System.IO.Unsafe (unsafePerformIO)-- for debugging purposes resolution :: (Int,Int) resolution = (640, 360) frameCount :: Int frameCount = 100 main :: IO () main = do initFFmpeg (getFrame, cleanup) <- imageReaderTime "big_buck_bunny.mp4" frames <- replicateM frameCount $ nextFrame getFrame cleanup animate (InWindow "Nice Window" resolution (10,10)) white (frameAt frames) nextFrame :: IO (Maybe (Image PixelRGB8, Double)) -> IO (Picture, Float) nextFrame getFrame = mapSnd realToFrac . mapFst fromImageRGB8 . fromJust <$> getFrame frameAt :: [(Picture, Float)] -> Float -> Picture frameAt list time = fst . head . dropWhile ((< time) . snd) $ list mapFst :: (a -> c) -> (a, b) -> (c, b) mapFst f (a, b) = (fa, b) -- applies f to first element of a 2-tuple mapSnd :: (b -> c) -> (a, b) -> (a, c) mapSnd f (a, b) = (a, fb) -- applies f to the second element of a 2-tuple 
+5
source share
1 answer

(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.

+1
source

Source: https://habr.com/ru/post/1238667/


All Articles