{-
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.Scale 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 <filename> 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.makeBuffer
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.getStreamIndex packet) $
do
frameFinished <- FF.decodeVideo codecContext frame packet
when frameFinished $
do
-- Rescale the picture
SDL.lockSurface rgbSurface
S.scale scaler
(FF.getPictureSlice frame) (FF.getPictureStride frame)
0 height
(FF.getPictureSlice frameRGB) (FF.getPictureStride 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