packages feed

hs-ffmpeg-0.3.3: src/Media/FFMpeg/Format.hsc

-- -*- haskell -*-
{-# LANGUAGE ForeignFunctionInterface #-}

{- |Module 'Media.FFMpeg.Format' implements bindings to AVFormat library

   (c) 2009 Vasyl Pasternak
 -}

module Media.FFMpeg.Format 
    (
     libAVFormatVersion
    ,registerAll
    ,withFFMpeg

    ,FormatContext
    ,findStreamInfo
    ,dumpFormat
    ,newContext
    ,openInputFile

    ,Stream
    ,getStreams
    ,getCodecContext
    ,readFrame
    ) where

#include "ffmpeg.h"

import Foreign
import Foreign.C.Types
import Foreign.C.String
import Data.Version
import Control.Monad (liftM)
import Text.Printf (printf)

import Media.FFMpeg.Common
import Media.FFMpeg.Util
import Media.FFMpeg.Codec

libAVFormatVersion :: Version
libAVFormatVersion = fromVersionNum #{const LIBAVFORMAT_VERSION_INT}

--
-- | registerAll, this should be the first instruction in before working wiht
-- ffmpeg library
--
foreign import ccall "av_register_all" registerAll :: IO ()

--
-- | control helper
--
withFFMpeg :: IO a -> IO a
withFFMpeg io = registerAll >> io


-- |FormatContext - wrapper to AVFormatContext
newtype FormatContext = FormatContext (ForeignPtr FormatContext)

instance ExternalPointer FormatContext where
    withThis (FormatContext fmt) io = withForeignPtr fmt (io . castPtr)

--
-- | findStreamInfo
foreign import ccall "av_find_stream_info" _find_stream_info :: Ptr () -> IO CInt

findStreamInfo :: FormatContext -> IO ()
findStreamInfo fmt = 
    withThis fmt $ \fmt' -> 
        throwIf_ (< 0) 
                     (printf "findStreamInfo: failed with error code %d\n" . cToInt) 
                     (_find_stream_info fmt')

-- 
-- | dumpFormat - outputs the format into console
--
foreign import ccall "dump_format" _dump_format :: 
    Ptr () -> CInt -> CString -> CInt -> IO ()

dumpFormat :: FormatContext -> String -> IO ()
dumpFormat fmt s =
    withThis fmt $ \fmt' ->
        withCString s $ \s' ->
            _dump_format fmt' 0 s' 0


--
-- | newContext - allocates new FormatContext
--
foreign import ccall "avformat_alloc_context" _alloc_context :: IO (Ptr ())

newContext :: IO FormatContext
newContext = do 
  p <- throwIf (== nullPtr) 
               (\_ -> "allocContext: failed to allocate context") 
               _alloc_context
  liftM (FormatContext . castForeignPtr) $ newAvForeignPtr p

--
-- |openInputFile -- opens media file
--
foreign import ccall "av_open_input_file" _open_file :: 
    Ptr (Ptr ()) -> CString -> Ptr () -> CInt -> Ptr () -> IO CInt

foreign import ccall "av_close_input_file" _close_file ::
    Ptr a -> IO ()

openInputFile :: String -> IO FormatContext
openInputFile name = 
    withCString name $ \s ->
        alloca $ \pp -> do
          throwIf_ (/=0) 
                       (printf "openInputFile: fail to open %s - errorcode %d\n" name . cToInt) $
                       _open_file pp s nullPtr 0 nullPtr
          ptr <- peek pp
          liftM (FormatContext . castForeignPtr) $ newFinForeignPtr _close_file ptr


--
-- |Stream - wrapper to AVStream
--
newtype Stream = Stream (Ptr Stream)

instance ExternalPointer Stream where
    withThis (Stream s) io = io (castPtr s)

--
-- |getStreams -- retrieve all streams from the FormatContext
--
getStreams :: FormatContext -> [Stream]
getStreams fmt = unsafePerformIO $
                 withThis fmt $ \fmt' -> do
                   count <- liftM fromIntegral $ 
                            (#{peek AVFormatContext, nb_streams} fmt' :: IO CUInt)
                   let streamsPtr = fmt' `plusPtr` #{offset AVFormatContext, streams}
                   liftM (map Stream) $ peekArray count streamsPtr

--
-- |getCodecContext - reads codec context from the stream structure
--
getCodecContext :: Stream -> Maybe CodecContext
getCodecContext s = unsafePerformIO $ 
                    withThis s $ \s' -> do
                      p <- liftM justPtr $ (#{peek AVStream, codec} s' :: IO (Ptr ()))
                      maybe (return Nothing) (liftM Just . toCodecContext) p
                           

--
-- |readFrame - reads frame from file, return False if error of EOF occured,
-- True otherwise
--
foreign import ccall "av_read_frame" _read_frame :: Ptr () -> Ptr () -> IO CInt

readFrame :: FormatContext -> Packet -> IO Bool
readFrame fmt pkt = 
    withThis fmt $ \fmt' ->
        withThis pkt $ \pkt' ->
            liftM (>= 0) $ _read_frame fmt' pkt'