Как найти метаданные mp4 с помощью ffmpeg-light в haskell?
Я использую ffmpeg-light, JuicyPixels и глянец для отображения видео с помощью Haskell. Я хочу найти метаданные видео, которое я играю автоматически, но я еще не нашел способ сделать это.
Я хотел бы получить доступ к метаданным, таким как разрешение и частота кадров видео.
Вы можете мне помочь?
РЕДАКТИРОВАТЬ:
Я попробовал ваше решение @CRDrost, но теперь видео воспроизводится с 2-кратной нормальной скоростью. Я предполагаю, что функция imageReaderTime дает неправильные метки времени.
РЕДАКТИРОВАТЬ 2:
Ненормальная скорость воспроизведения - это ошибка в библиотеке ffmpeg-light. Я открыл проблему в репозитории github.
Мой обновленный код:
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
1 ответ
(а) я думаю void cleanup
избыточно и просто cleanup
работает, но ты мне нравишься не уверен на 100%, что это IO ()
значение делает точно.
Я не вижу прямой способ прочитать FPS, но imageReaderTime
производит метки времени в секундах, что даст вам хороший индикатор. Чтобы распространить метку времени, вам нужно изменить:
nextFrame :: IO (Maybe (Image PixelRGB8, Double)) -> IO (Double, Picture)
nextFrame getFrame = fmap fromImageRGB8 . swap . fromJust <$> getFrame
Тогда вы скажете:
stampedFrames <- replicateM frameCount $ nextFrame getFrame
let (tstamps, frames) = unzip stampedFrames
let approx_fps = fromIntegral (length tstamps) / (maximum tstamps - minimum tstamps)
Наконец вы можете пройти approx_fps
в качестве параметра для frameAt
, который придется использовать Double
скорее, чем Float
или какая-то функция принуждения типов.
Однако для того, что вы делаете, лучше всего иметь что-то вроде:
frameAt :: [(Double, Picture)] -> Double -> Picture
frameAt list time = snd . head . dropWhile ((< time) . fst) $ list
Он берет список, удаляет все элементы, чей первый элемент (временная метка) меньше запрашиваемого времени, а затем возвращает второй элемент (изображение) первой пары, которая появляется после этого. Не нужно угадывать FPS.