2015-12-18 10 views
5

Ich benutze ffmpeg-light, JuicyPixels und gloss, um ein Video mit Haskell anzuzeigen. Ich möchte die Metadaten von Videos finden, die ich automatisch spiele, aber ich habe noch keinen Weg gefunden, dies zu tun.Wie finde ich mp4-Metadaten mit ffmpeg-light in haskell?

Ich möchte auf Metadaten wie die Auflösung und die Framerate des Videos zugreifen.

Können Sie mir helfen?

EDIT:

Ich habe Ihre Lösung @CRDrost versucht, aber das Video nun mit 2x Normalgeschwindigkeit. Ich nehme an, dass die Funktion imageReaderTime falsche Zeitstempel gibt.

EDIT 2:

Die abnorme Spielgeschwindigkeit ist ein Fehler in der ffmpeg-Licht-Bibliothek. Ich habe eine issue im Github-Repository geöffnet.

Meine aktualisierten 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) = (f a, b) -- applies f to first element of a 2-tuple 

mapSnd :: (b -> c) -> (a, b) -> (a, c) 
mapSnd f (a, b) = (a, f b) -- applies f to the second element of a 2-tuple 

Antwort

1

(a) Ich denkenvoid cleanup überflüssig ist und nur cleanup funktioniert, aber Ich mag Sie nicht 100% sicher bin, was das IO() Wert genau der Fall ist.

Ich sehe keinen direkten Weg, um FPS zu lesen, aber imageReaderTime erzeugt Zeitstempel in Sekunden, die Ihnen einen guten Indikator geben würden. Um den Zeitstempel propagieren Sie ändern müssen:

nextFrame :: IO (Maybe (Image PixelRGB8, Double)) -> IO (Double, Picture) 
nextFrame getFrame = fmap fromImageRGB8 . swap . fromJust <$> getFrame 

Dann würden Sie sagen:

stampedFrames <- replicateM frameCount $ nextFrame getFrame 
let (tstamps, frames) = unzip stampedFrames 
let approx_fps = fromIntegral (length tstamps)/(maximum tstamps - minimum tstamps) 

Schließlich können Sie approx_fps als Parameter an frameAt geben, die Double verwenden müssen, statt Float oder auch irgendeine Art-Zwangsfunktion.

aber für das, was Sie tun, was könnte besser sein, ist wie etwas zu haben:

frameAt :: [(Double, Picture)] -> Double -> Picture 
frameAt list time = snd . head . dropWhile ((< time) . fst) $ list 

Diese Liste nimmt, fällt alle Elemente, deren erstes Element (Zeitstempel) geringer ist als die gewünschte Zeit und gibt dann das zweite Element (Bild) des ersten Paars, das danach auftritt, zurück. Es ist kein FPS-Raten erforderlich.