{- Testing the ffmpeg video decoding. Rewritten from the C code, from the FFMpeg tutorial (http://www.dranger.com/ffmpeg/) (c) 2009 Vasyl Pasternak -} module Main (main) where -- FFMpeg import qualified Media.FFMpeg as FF import qualified Media.FFMpeg.SWScale as S -- Common import Data.Version import System.Environment (getArgs, getProgName) import Text.Printf (printf) import Data.Maybe (fromJust) import Data.IORef (newIORef, readIORef, modifyIORef) import Control.Monad (when) import System.IO -- (WriteMode, withBinaryFile, hPutStr, hPutBuf) -- SDL import Graphics.UI.SDL as SDL main :: IO () main = do getArgs >>= \args -> case args of filename:[] -> decodeFile filename otherwise -> getProgName >>= \pn -> (putStrLn . unlines) ["Usage: \n" ,printf "%s filename\n" pn ,"Where is the path to the video file"] -- The main rutine, do all work decodeFile :: String -> IO () decodeFile filename = do SDL.init [InitTimer, InitAudio, InitVideo] FF.withFFMpeg $ do -- Print FFMpeg version printf "FFMpeg library initialized\n" printf "\tlibAVFormat version: %s\n" (showVersion FF.libAVFormatVersion) printf "\tlibAVCodec version: %s\n" (showVersion FF.libAVCodecVersion) -- Open file ctx <- FF.openInputFile filename FF.findStreamInfo ctx -- FF.dumpFormat ctx filename printf "File: %s\n" filename -- Get all streams from file let streams = FF.getStreams ctx printf "Found %d streams\n" (length streams) -- Retrive video streams let videoStreams = filter (\(_,s) -> maybe False ((==FF.CodecTypeVideo) . FF.getCodecType) (FF.getCodecContext s)) $ zip [0..] streams if null videoStreams then do printf "No video streams found. Exiting...\n" else do let (streamIdx, videoStream) = head videoStreams printf "Extracting video information from stream %d\n" (streamIdx :: Int) -- Starting decoding video let codecContext = fromJust $ FF.getCodecContext videoStream let codecId = FF.getCodecId codecContext printf "Video codec is '%s'\n" (show codecId) (Just decoder) <- FF.findDecoder codecId FF.openCodec codecContext decoder let (width, height, pixfmt) = (FF.getVideoWidth codecContext, FF.getVideoHeight codecContext, FF.getPixelFormat codecContext) printf "Video dimensions are %dx%d\n" width height printf "Video pixel format is %s\n" (show pixfmt) -- init primary SDL surface screen <- SDL.setVideoMode width height 0 [] -- init output surface rgbSurface <- SDL.createRGBSurface [] width height 24 0x0000FF 0x00FF00 0xFF0000 0x000000 frame <- FF.allocFrame frameRGB <- FF.allocFrame surfacePixels <- SDL.surfaceGetPixels rgbSurface >>= FF.castBuffer FF.pictureFill frameRGB surfacePixels FF.PixFmtRgb24 width height packet <- FF.allocPacket -- Prepare resizer scaler <- S.getContext (width, height, pixfmt) (width, height, FF.PixFmtRgb24) [S.SwsBicubic] -- use IORef to cound packets idxRef <- newIORef (0 :: Int) while (FF.readFrame ctx packet) $ do when (streamIdx == FF.packetGetStreamIndex packet) $ do frameFinished <- FF.decodeVideo codecContext frame packet when frameFinished $ do -- Rescale the picture SDL.lockSurface rgbSurface S.scale scaler (FF.pictureGetSlice frame) (FF.pictureGetStride frame) 0 height (FF.pictureGetSlice frameRGB) (FF.pictureGetStride frameRGB) -- show on screen SDL.unlockSurface rgbSurface SDL.blitSurface rgbSurface Nothing screen Nothing SDL.flip screen idx <- readIORef idxRef modifyIORef idxRef (+1) printf "\rDecoded frame %05d" (idx) FF.cleanPacket packet printf "\n" -- While cycle implementation while :: IO Bool -> IO () -> IO () while cond body = do c <- cond when c $ body >> while cond body