packages feed

hs-ffmpeg-0.2.3: src/Media/FFMpeg/Internals/AVCodec.hsc

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

{- | Module 'Media.FFMpeg.Internals.AVFormat' implements 
   avformat header (its structures and datatypes) along with 
   getters and setters to work with them
   
   (c) 2009 Vasyl Pasternak
 -}

module Media.FFMpeg.Internals.AVCodec
    (
     -- Enumerations
     CodecType (..)
    ,CodecId (..)
     
    ,WithCodecType (..)
    ,WithCodecId (..)
    ,WithVideoProps (..)

    -- CodecContext 
    ,CodecContext
    ,withCodecContext
    ,toCodecContext
    ,openCodec
    ,decodeVideo
     
    -- Codec
    ,Codec 
    ,withCodec
    ,findDecoder
     
    -- Frame
    ,Frame
    ,withFrame
    ,allocFrame

    -- Picture
    ,CommonPicture (..)
    ,pictureGetSize

    -- Packet
    ,Packet
    ,withPacket
    ,allocPacket
    ,cleanPacket
    ,getStreamIndex
    )where

import Foreign
import Foreign.C.String
import Control.Monad (liftM)
import Text.Printf
import Media.FFMpeg.Internals.Types
import Media.FFMpeg.Internals.Utils

#include "libavcodec/avcodec.h"
#include "macros.hsc2hs.h"


-- |Different enumerations
-- |CodecType
#{begin_enum CodecType, CODEC_TYPE_UNKNOWN}
#{add_enum CODEC_TYPE_VIDEO}
#{add_enum CODEC_TYPE_AUDIO}
#{add_enum CODEC_TYPE_DATA}
#{add_enum CODEC_TYPE_SUBTITLE}
#{add_enum CODEC_TYPE_ATTACHMENT}
#{add_enum CODEC_TYPE_NB}
#{end_enum "Eq,Ord,Show"}

-- | CodecId
#{begin_enum CodecId, CODEC_ID_NONE}
#{add_enum CODEC_ID_MPEG1VIDEO}
#{add_enum CODEC_ID_MPEG2VIDEO}
#{add_enum CODEC_ID_MPEG2VIDEO_XVMC}
#{add_enum CODEC_ID_H261}
#{add_enum CODEC_ID_H263}
#{add_enum CODEC_ID_RV10}
#{add_enum CODEC_ID_RV20}
#{add_enum CODEC_ID_MJPEG}
#{add_enum CODEC_ID_MJPEGB}
#{add_enum CODEC_ID_LJPEG}
#{add_enum CODEC_ID_SP5X}
#{add_enum CODEC_ID_JPEGLS}
#{add_enum CODEC_ID_MPEG4}
#{add_enum CODEC_ID_RAWVIDEO}
#{add_enum CODEC_ID_MSMPEG4V1}
#{add_enum CODEC_ID_MSMPEG4V2}
#{add_enum CODEC_ID_MSMPEG4V3}
#{add_enum CODEC_ID_WMV1}
#{add_enum CODEC_ID_WMV2}
#{add_enum CODEC_ID_H263P}
#{add_enum CODEC_ID_H263I}
#{add_enum CODEC_ID_FLV1}
#{add_enum CODEC_ID_SVQ1}
#{add_enum CODEC_ID_SVQ3}
#{add_enum CODEC_ID_DVVIDEO}
#{add_enum CODEC_ID_HUFFYUV}
#{add_enum CODEC_ID_CYUV}
#{add_enum CODEC_ID_H264}
#{add_enum CODEC_ID_INDEO3}
#{add_enum CODEC_ID_VP3}
#{add_enum CODEC_ID_THEORA}
#{add_enum CODEC_ID_ASV1}
#{add_enum CODEC_ID_ASV2}
#{add_enum CODEC_ID_FFV1}
#{add_enum CODEC_ID_4XM}
#{add_enum CODEC_ID_VCR1}
#{add_enum CODEC_ID_CLJR}
#{add_enum CODEC_ID_MDEC}
#{add_enum CODEC_ID_ROQ}
#{add_enum CODEC_ID_INTERPLAY_VIDEO}
#{add_enum CODEC_ID_XAN_WC3}
#{add_enum CODEC_ID_XAN_WC4}
#{add_enum CODEC_ID_RPZA}
#{add_enum CODEC_ID_CINEPAK}
#{add_enum CODEC_ID_WS_VQA}
#{add_enum CODEC_ID_MSRLE}
#{add_enum CODEC_ID_MSVIDEO1}
#{add_enum CODEC_ID_IDCIN}
#{add_enum CODEC_ID_8BPS}
#{add_enum CODEC_ID_SMC}
#{add_enum CODEC_ID_FLIC}
#{add_enum CODEC_ID_TRUEMOTION1}
#{add_enum CODEC_ID_VMDVIDEO}
#{add_enum CODEC_ID_MSZH}
#{add_enum CODEC_ID_ZLIB}
#{add_enum CODEC_ID_QTRLE}
#{add_enum CODEC_ID_SNOW}
#{add_enum CODEC_ID_TSCC}
#{add_enum CODEC_ID_ULTI}
#{add_enum CODEC_ID_QDRAW}
#{add_enum CODEC_ID_VIXL}
#{add_enum CODEC_ID_QPEG}
#{add_enum CODEC_ID_XVID}
#{add_enum CODEC_ID_PNG}
#{add_enum CODEC_ID_PPM}
#{add_enum CODEC_ID_PBM}
#{add_enum CODEC_ID_PGM}
#{add_enum CODEC_ID_PGMYUV}
#{add_enum CODEC_ID_PAM}
#{add_enum CODEC_ID_FFVHUFF}
#{add_enum CODEC_ID_RV30}
#{add_enum CODEC_ID_RV40}
#{add_enum CODEC_ID_VC1}
#{add_enum CODEC_ID_WMV3}
#{add_enum CODEC_ID_LOCO}
#{add_enum CODEC_ID_WNV1}
#{add_enum CODEC_ID_AASC}
#{add_enum CODEC_ID_INDEO2}
#{add_enum CODEC_ID_FRAPS}
#{add_enum CODEC_ID_TRUEMOTION2}
#{add_enum CODEC_ID_BMP}
#{add_enum CODEC_ID_CSCD}
#{add_enum CODEC_ID_MMVIDEO}
#{add_enum CODEC_ID_ZMBV}
#{add_enum CODEC_ID_AVS}
#{add_enum CODEC_ID_SMACKVIDEO}
#{add_enum CODEC_ID_NUV}
#{add_enum CODEC_ID_KMVC}
#{add_enum CODEC_ID_FLASHSV}
#{add_enum CODEC_ID_CAVS}
#{add_enum CODEC_ID_JPEG2000}
#{add_enum CODEC_ID_VMNC}
#{add_enum CODEC_ID_VP5}
#{add_enum CODEC_ID_VP6}
#{add_enum CODEC_ID_VP6F}
#{add_enum CODEC_ID_TARGA}
#{add_enum CODEC_ID_DSICINVIDEO}
#{add_enum CODEC_ID_TIERTEXSEQVIDEO}
#{add_enum CODEC_ID_TIFF}
#{add_enum CODEC_ID_GIF}
#{add_enum CODEC_ID_FFH264}
#{add_enum CODEC_ID_DXA}
#{add_enum CODEC_ID_DNXHD}
#{add_enum CODEC_ID_THP}
#{add_enum CODEC_ID_SGI}
#{add_enum CODEC_ID_C93}
#{add_enum CODEC_ID_BETHSOFTVID}
#{add_enum CODEC_ID_PTX}
#{add_enum CODEC_ID_TXD}
#{add_enum CODEC_ID_VP6A}
#{add_enum CODEC_ID_AMV}
#{add_enum CODEC_ID_VB}
#{add_enum CODEC_ID_PCX}
#{add_enum CODEC_ID_SUNRAST}
#{add_enum CODEC_ID_INDEO4}
#{add_enum CODEC_ID_INDEO5}
#{add_enum CODEC_ID_MIMIC}
#{add_enum CODEC_ID_RL2}
#{add_enum CODEC_ID_8SVX_EXP}
#{add_enum CODEC_ID_8SVX_FIB}
#{add_enum CODEC_ID_ESCAPE124}
#{add_enum CODEC_ID_DIRAC}
#{add_enum CODEC_ID_BFI}
#{add_enum CODEC_ID_CMV}
#{add_enum CODEC_ID_MOTIONPIXELS}
#{add_enum CODEC_ID_TGV}
#{add_enum CODEC_ID_TGQ}
#{add_enum CODEC_ID_TQI}
#{add_enum CODEC_ID_AURA}
#{add_enum CODEC_ID_AURA2}
#{add_enum CODEC_ID_PCM_S16LE}
#{add_enum CODEC_ID_PCM_S16BE}
#{add_enum CODEC_ID_PCM_U16LE}
#{add_enum CODEC_ID_PCM_U16BE}
#{add_enum CODEC_ID_PCM_S8}
#{add_enum CODEC_ID_PCM_U8}
#{add_enum CODEC_ID_PCM_MULAW}
#{add_enum CODEC_ID_PCM_ALAW}
#{add_enum CODEC_ID_PCM_S32LE}
#{add_enum CODEC_ID_PCM_S32BE}
#{add_enum CODEC_ID_PCM_U32LE}
#{add_enum CODEC_ID_PCM_U32BE}
#{add_enum CODEC_ID_PCM_S24LE}
#{add_enum CODEC_ID_PCM_S24BE}
#{add_enum CODEC_ID_PCM_U24LE}
#{add_enum CODEC_ID_PCM_U24BE}
#{add_enum CODEC_ID_PCM_S24DAUD}
#{add_enum CODEC_ID_PCM_ZORK}
#{add_enum CODEC_ID_PCM_S16LE_PLANAR}
#{add_enum CODEC_ID_PCM_DVD}
#{add_enum CODEC_ID_PCM_F32BE}
#{add_enum CODEC_ID_PCM_F32LE}
#{add_enum CODEC_ID_PCM_F64BE}
#{add_enum CODEC_ID_PCM_F64LE}
#{add_enum CODEC_ID_ADPCM_IMA_QT}
#{add_enum CODEC_ID_ADPCM_IMA_WAV}
#{add_enum CODEC_ID_ADPCM_IMA_DK3}
#{add_enum CODEC_ID_ADPCM_IMA_DK4}
#{add_enum CODEC_ID_ADPCM_IMA_WS}
#{add_enum CODEC_ID_ADPCM_IMA_SMJPEG}
#{add_enum CODEC_ID_ADPCM_MS}
#{add_enum CODEC_ID_ADPCM_4XM}
#{add_enum CODEC_ID_ADPCM_XA}
#{add_enum CODEC_ID_ADPCM_ADX}
#{add_enum CODEC_ID_ADPCM_EA}
#{add_enum CODEC_ID_ADPCM_G726}
#{add_enum CODEC_ID_ADPCM_CT}
#{add_enum CODEC_ID_ADPCM_SWF}
#{add_enum CODEC_ID_ADPCM_YAMAHA}
#{add_enum CODEC_ID_ADPCM_SBPRO_4}
#{add_enum CODEC_ID_ADPCM_SBPRO_3}
#{add_enum CODEC_ID_ADPCM_SBPRO_2}
#{add_enum CODEC_ID_ADPCM_THP}
#{add_enum CODEC_ID_ADPCM_IMA_AMV}
#{add_enum CODEC_ID_ADPCM_EA_R1}
#{add_enum CODEC_ID_ADPCM_EA_R3}
#{add_enum CODEC_ID_ADPCM_EA_R2}
#{add_enum CODEC_ID_ADPCM_IMA_EA_SEAD}
#{add_enum CODEC_ID_ADPCM_IMA_EA_EACS}
#{add_enum CODEC_ID_ADPCM_EA_XAS}
#{add_enum CODEC_ID_ADPCM_EA_MAXIS_XA}
#{add_enum CODEC_ID_ADPCM_IMA_ISS}
#{add_enum CODEC_ID_AMR_NB}
#{add_enum CODEC_ID_AMR_WB}
#{add_enum CODEC_ID_RA_144}
#{add_enum CODEC_ID_RA_288}
#{add_enum CODEC_ID_ROQ_DPCM}
#{add_enum CODEC_ID_INTERPLAY_DPCM}
#{add_enum CODEC_ID_XAN_DPCM}
#{add_enum CODEC_ID_SOL_DPCM}
#{add_enum CODEC_ID_MP2}
#{add_enum CODEC_ID_MP3}
#{add_enum CODEC_ID_AAC}
#{add_enum CODEC_ID_AC3}
#{add_enum CODEC_ID_DTS}
#{add_enum CODEC_ID_VORBIS}
#{add_enum CODEC_ID_DVAUDIO}
#{add_enum CODEC_ID_WMAV1}
#{add_enum CODEC_ID_WMAV2}
#{add_enum CODEC_ID_MACE3}
#{add_enum CODEC_ID_MACE6}
#{add_enum CODEC_ID_VMDAUDIO}
#{add_enum CODEC_ID_SONIC}
#{add_enum CODEC_ID_SONIC_LS}
#{add_enum CODEC_ID_FLAC}
#{add_enum CODEC_ID_MP3ADU}
#{add_enum CODEC_ID_MP3ON4}
#{add_enum CODEC_ID_SHORTEN}
#{add_enum CODEC_ID_ALAC}
#{add_enum CODEC_ID_WESTWOOD_SND1}
#{add_enum CODEC_ID_GSM}
#{add_enum CODEC_ID_QDM2}
#{add_enum CODEC_ID_COOK}
#{add_enum CODEC_ID_TRUESPEECH}
#{add_enum CODEC_ID_TTA}
#{add_enum CODEC_ID_SMACKAUDIO}
#{add_enum CODEC_ID_QCELP}
#{add_enum CODEC_ID_WAVPACK}
#{add_enum CODEC_ID_DSICINAUDIO}
#{add_enum CODEC_ID_IMC}
#{add_enum CODEC_ID_MUSEPACK7}
#{add_enum CODEC_ID_MLP}
#{add_enum CODEC_ID_GSM_MS}
#{add_enum CODEC_ID_ATRAC3}
#{add_enum CODEC_ID_VOXWARE}
#{add_enum CODEC_ID_APE}
#{add_enum CODEC_ID_NELLYMOSER}
#{add_enum CODEC_ID_MUSEPACK8}
#{add_enum CODEC_ID_SPEEX}
#{add_enum CODEC_ID_WMAVOICE}
#{add_enum CODEC_ID_WMAPRO}
#{add_enum CODEC_ID_WMALOSSLESS}
#{add_enum CODEC_ID_ATRAC3P}
#{add_enum CODEC_ID_EAC3}
#{add_enum CODEC_ID_SIPR}
#{add_enum CODEC_ID_MP1}
#{add_enum CODEC_ID_TWINVQ}
#{add_enum CODEC_ID_TRUEHD}
#{add_enum CODEC_ID_DVD_SUBTITLE}
#{add_enum CODEC_ID_DVB_SUBTITLE}
#{add_enum CODEC_ID_TEXT}
#{add_enum CODEC_ID_XSUB}
#{add_enum CODEC_ID_SSA}
#{add_enum CODEC_ID_MOV_TEXT}
#{add_enum CODEC_ID_TTF}
#{add_enum CODEC_ID_PROBE}
#{add_enum CODEC_ID_MPEG2TS}
#{end_enum "Eq,Ord,Show"}


class WithCodecId a where
    getCodecId :: a -> CodecId

class WithCodecType a where
    getCodecType :: a -> CodecType

class WithVideoProps a where
    getVideoWidth :: a -> Int
    getVideoHeight :: a -> Int
    getPixelFormat :: a -> PixelFormat

-- |CodecContext - wrapper to AVCodecContext
newtype CodecContext = CodecContext (ForeignPtr CodecContext)

withCodecContext :: CodecContext -> (Ptr a -> IO b) -> IO b
withCodecContext (CodecContext ctx) clos = withForeignPtr ctx (clos . castPtr)

toCodecContext :: Ptr a -> IO CodecContext
toCodecContext p =  liftM CodecContext $ (newForeignPtr_ . castPtr) p

instance WithCodecId CodecContext where
    getCodecId ctx = 
        unsafePerformIO $
        withCodecContext ctx $ \ctx' -> 
            do
              val <- #{peek AVCodecContext, codec_id} ctx' :: IO #{type enum CodecID}
              return $ (toEnum . fromIntegral) val

instance WithCodecType CodecContext where
    getCodecType ctx = 
        unsafePerformIO $
        withCodecContext ctx $ \ctx' -> 
            do
              val <- #{peek AVCodecContext, codec_type} ctx' :: IO #{type enum CodecType}
              return $ (toEnum . fromIntegral) val

instance WithVideoProps CodecContext where
    getVideoWidth ctx = 
        unsafePerformIO $
        withCodecContext ctx $ \ctx' ->
            liftM fromIntegral $ (#{peek AVCodecContext, width} ctx'  :: IO #{type int})

    getVideoHeight ctx =
        unsafePerformIO $
        withCodecContext ctx $ \ctx' -> 
            liftM fromIntegral $ (#{peek AVCodecContext, height} ctx' :: IO #{type int})

    getPixelFormat ctx = 
        unsafePerformIO $ 
        withCodecContext ctx $ \ctx' ->
            do
              val <- #{peek AVCodecContext, pix_fmt} ctx' :: IO #{type enum PixelFormat}
              return $ (toEnum . fromIntegral) val

-- |CodecContext function
foreign import ccall "avcodec_open" _open :: VoidP -> VoidP -> IO SInt
foreign import ccall "avcodec_close" _close :: VoidP -> IO ()

openCodec :: CodecContext -> Codec -> IO ()
openCodec ctx@(CodecContext ct) c = 
    withCodecContext ctx $ \ctx' ->
        withCodec c $ \c' -> do
          throwIf_ (<0)
                   (printf "openCodec: fails to open codec '%s' - errorcode %d\n" (show $ getCodecId c)) $ _open ctx' c'
          finalizer <- mkFinalizerPtr _close
          addForeignPtrFinalizer finalizer (castForeignPtr ct)


-- |CodecContext getters/setters

                               

-- |Codec - implementation of AVCodec
newtype Codec = Codec (Ptr Codec)

withCodec :: Codec -> (Ptr a -> IO b) -> IO b
withCodec (Codec c) clos = clos (castPtr c)


-- |Codec getters/setters
instance WithCodecId Codec where
    getCodecId ctx = 
        unsafePerformIO $
        withCodec ctx $ \ctx' -> 
            do
              val <- #{peek AVCodec, id} ctx' :: IO #{type enum CodecID}
              return $ (toEnum . fromIntegral) val

instance WithCodecType Codec where
    getCodecType ctx = 
        unsafePerformIO $
        withCodec ctx $ \ctx' -> 
            do
              val <- #{peek AVCodec, type} ctx' :: IO #{type enum CodecType}
              return $ (toEnum . fromIntegral) val


-- |Codec functions

foreign import ccall "avcodec_find_decoder" _find_decoder :: #{type enum CodecID} -> IO VoidP

findDecoder :: CodecId -> IO (Maybe Codec)
findDecoder cid = do
  res <- _find_decoder (fromIntegral $ fromEnum cid)
  if res == nullPtr then return Nothing else return $ Just $ Codec $ castPtr res


foreign import ccall "avcodec_decode_video2" _decode_video :: 
    VoidP -> VoidP -> (Ptr SInt) -> VoidP -> IO SInt

-- | returns True if complete picture is decompressed
decodeVideo :: CodecContext -> Frame -> Packet -> IO Bool
decodeVideo ctx frm pkt = 
    withCodecContext ctx $ \ctx' ->
        withFrame frm $ \frm' ->
            withPacket pkt $ \pkt' ->
                alloca $ \gotPic' -> do
                  throwIf (<0)
                          (printf "decodeVideo: failed to decode video packet: %d")
                          (_decode_video ctx' frm' gotPic' pkt')
                  peek gotPic' >>= return . (>0)


-- |AVFrame implementation
newtype Frame = Frame (ForeignPtr Frame)

withFrame :: Frame -> (Ptr a -> IO b) -> IO b
withFrame (Frame f) clos = withForeignPtr f (clos . castPtr)

foreign import ccall "avcodec_alloc_frame" _alloc_frame :: IO VoidP

allocFrame :: IO Frame
allocFrame = do
  p <- throwIf (== nullPtr)
               (\_ -> "allocFrame: failed to allocate frame")
               _alloc_frame
  finalizer <- mkFinalizerPtr avFree
  newForeignPtr finalizer p >>= return . Frame . castForeignPtr

instance CommonPicture Frame where
    withCPicture = withFrame


-- |AVPicture 


foreign import ccall "avpicture_fill" _picture_fill ::
    VoidP -> VoidP -> #{type int} -> #{type int} -> #{type int} -> IO #{type int}

type CPicture = VoidP

class CommonPicture a where
    withCPicture :: a -> (CPicture -> IO b) -> IO b

    pictureFill :: a -> Buffer -> PixelFormat -> Int -> Int -> IO ()
    pictureFill pic buff pf w h = 
        withCPicture pic $ \pic' ->
            withBuffer buff $ \buff' -> 
                _picture_fill pic' (castPtr buff') 
                     (fromIntegral $ fromEnum pf)
                     (fromIntegral w)
                     (fromIntegral h) >> return ()
    
    getPictureSlice :: a -> PictureSlice
    getPictureSlice pic =
        unsafePerformIO $
        withCPicture pic $ \pic' ->
            return $ castPtr $ pic' `plusPtr` #{offset AVPicture, data}

    getPictureStride :: a -> PictureStride
    getPictureStride pic = 
        unsafePerformIO $
        withCPicture pic $ \pic' ->
            return $ castPtr $ pic' `plusPtr` #{offset AVPicture, linesize}


-- |Other functions
 
foreign import ccall "avpicture_get_size" _picture_get_size :: 
    #{type enum PixelFormat} -> #{type int} -> #{type int} -> IO (#{type int})

pictureGetSize :: PixelFormat -> Int -> Int -> IO Int
pictureGetSize pf w h = do
  liftM fromIntegral $ 
        (_picture_get_size ((fromIntegral . fromEnum) pf) (fromIntegral w) (fromIntegral h) :: IO (#{type int}))
 

-- |AVPacket

newtype Packet = Packet (ForeignPtr Packet)

withPacket :: Packet -> (Ptr a -> IO b) -> IO b
withPacket (Packet p) clos = withForeignPtr p (clos . castPtr)

foreign import ccall "av_init_packet" _init_packet :: VoidP -> IO ()
foreign import ccall "b_free_packet" _free_packet :: VoidP -> IO ()

allocPacket :: IO Packet
allocPacket = do
  p <- mallocBytes #{size AVPacket}
  _init_packet p
  fp <- newForeignPtr finalizerFree p
  deinit <- mkFinalizerPtr _free_packet
  addForeignPtrFinalizer deinit fp 
  return $ Packet $ castForeignPtr fp

cleanPacket :: Packet -> IO ()
cleanPacket pkt = withPacket pkt _free_packet


-- | AVPacket getters/setters

getStreamIndex :: Packet -> Int
getStreamIndex pkt = 
    unsafePerformIO $
    withPacket pkt $ \pkt' ->
        liftM fromIntegral $ 
              (#{peek AVPacket, stream_index} pkt' :: IO #{type int})