ffmpeg-light-0.14.0: src/Codec/FFmpeg/Types.hsc
{-# LANGUAGE ForeignFunctionInterface, FlexibleInstances,
GeneralizedNewtypeDeriving #-}
module Codec.FFmpeg.Types where
import Codec.FFmpeg.Enums
import Control.Monad (zipWithM_,when)
import Data.Maybe (fromMaybe)
import Foreign.C.String (CString)
import Foreign.C.Types
import Foreign.Ptr
import Foreign.Storable
import Foreign.Marshal.Alloc (malloc)
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/audio_fifo.h>
#include <libavutil/avutil.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include "hscMacros.h"
#include "nameCompat.h"
class HasPtr a where
getPtr :: a -> Ptr ()
instance HasPtr (Ptr ()) where getPtr = id
newtype AVFormatContext = AVFormatContext (Ptr ()) deriving (Storable, HasPtr)
#mkField NumStreams, CInt
#mkField Streams, (Ptr AVStream)
#mkField OutputFormat, AVOutputFormat
#mkField IOContext, AVIOContext
#mkField InputFormat, AVInputFormat
#hasField AVFormatContext, NumStreams, nb_streams
#hasField AVFormatContext, Streams, streams
#hasField AVFormatContext, OutputFormat, oformat
#hasField AVFormatContext, InputFormat, iformat
#hasField AVFormatContext, IOContext, pb
#hasField AVFormatContext, VideoCodecID, video_codec_id
setFilename :: AVFormatContext -> String -> IO ()
setFilename ctx fn =
do let ptr = getPtr ctx
dst = (#ptr AVFormatContext, filename) ptr
bytes = map (fromIntegral . fromEnum) fn
zipWithM_ (pokeElemOff dst) bytes [(0 :: CInt) ..]
foreign import ccall "av_input_video_device_next"
av_input_video_device_next :: AVInputFormat -> IO AVInputFormat
setCamera :: AVFormatContext -> IO ()
setCamera ctx = do
ipt <- getCameraAVInputFormat (AVInputFormat nullPtr)
setInputFormat ctx ipt
where
-- Currently straight-line, but we can filter each 'nxt' based on
-- predicates, such as device ('avfoundtion', 'v4l2' etc) in the
-- future, if needed.
getCameraAVInputFormat :: AVInputFormat -> IO AVInputFormat
getCameraAVInputFormat p = do
nxt <- av_input_video_device_next p
when (nullPtr == getPtr nxt) (error "No video input device found.")
return nxt
foreign import ccall "avformat_alloc_context"
avformat_alloc_context :: IO (Ptr ())
mallocAVFormatContext :: IO AVFormatContext
mallocAVFormatContext = AVFormatContext <$> avformat_alloc_context
newtype AVCodecContext = AVCodecContext (Ptr ()) deriving (Storable, HasPtr)
foreign import ccall "avcodec_alloc_context3"
avcodec_alloc_context3 :: AVCodec -> IO AVCodecContext
#mkField BitRate, CInt
#mkField SampleFormat, AVSampleFormat
#mkField Width, CInt
#mkField Height, CInt
#mkField TimeBase, AVRational
#mkField GopSize, CInt
#mkField PixelFormat, AVPixelFormat
#mkField CodecFlags, CodecFlag
#mkField CodecID, AVCodecID
#mkField PrivData, (Ptr ())
#mkField TicksPerFrame, CInt
#mkField RawAspectRatio, AVRational
#mkField SampleRate, CInt
#mkField ChannelLayout, CULong
#mkField Channels, CInt
#mkField FrameSize, CInt
#mkField FrameRate, AVRational
#hasField AVCodecContext, BitRate, bit_rate
#hasField AVCodecContext, Width, width
#hasField AVCodecContext, Height, height
#hasField AVCodecContext, TimeBase, time_base
#hasField AVCodecContext, GopSize, gop_size
#hasField AVCodecContext, PixelFormat, pix_fmt
#hasField AVCodecContext, CodecFlags, flags
#hasField AVCodecContext, CodecID, codec_id
#hasField AVCodecContext, PrivData, priv_data
#hasField AVCodecContext, TicksPerFrame, ticks_per_frame
#hasField AVCodecContext, RawAspectRatio, sample_aspect_ratio
#hasField AVCodecContext, SampleRate, sample_rate
#hasField AVCodecContext, ChannelLayout, channel_layout
#hasField AVCodecContext, Channels, channels
#hasField AVCodecContext, SampleFormat, sample_fmt
#hasField AVCodecContext, FrameSize, frame_size
#hasField AVCodecContext, FrameRate, framerate
getFps :: (HasTimeBase a, HasTicksPerFrame a) => a -> IO CDouble
getFps x = do
timeBase <- getTimeBase x
ticksPerFrame <- getTicksPerFrame x
pure (1.0 / av_q2d timeBase / fromIntegral ticksPerFrame)
getAspectRatio :: HasRawAspectRatio a => a -> IO (Maybe AVRational)
getAspectRatio = fmap nonZeroAVRational . getRawAspectRatio
-- | When unspecified, the most likely pixel shape is a square
guessAspectRatio :: HasRawAspectRatio a => a -> IO AVRational
guessAspectRatio = fmap (fromMaybe (AVRational 1 1)) . getAspectRatio
setAspectRatio :: HasRawAspectRatio a => a -> Maybe AVRational -> IO ()
setAspectRatio x Nothing = setRawAspectRatio x (AVRational 0 1)
setAspectRatio x (Just ratio) = setRawAspectRatio x ratio
newtype AVCodecParameters = AVCodecParameters (Ptr ()) deriving (Storable, HasPtr)
foreign import ccall "avcodec_parameters_from_context"
avcodec_parameters_from_context :: AVCodecParameters
-> AVCodecContext
-> IO CInt
newtype AVStream = AVStream (Ptr ()) deriving (Storable, HasPtr)
#mkField Id, CInt
#mkField CodecContext, AVCodecContext
#mkField StreamIndex, CInt
#mkField CodecParams, AVCodecParameters
#hasField AVStream, Id, id
#hasField AVStream, TimeBase, time_base
#hasField AVStream, CodecContext, codec
#hasField AVStream, StreamIndex, index
#hasField AVStream, CodecParams, codecpar
newtype AVCodec = AVCodec (Ptr ()) deriving (Storable, HasPtr)
#mkField LongName, CString
#mkField Name, CString
#mkField PixelFormats, (Ptr AVPixelFormat)
#mkField SampleFormats, (Ptr AVSampleFormat)
#mkField ChannelLayouts, (Ptr CULong)
#mkField SupportedSampleRates, (Ptr CInt)
#mkField Capabilities, CInt
#hasField AVCodec, LongName, long_name
#hasField AVCodec, Name, name
#hasField AVCodec, CodecID, id
#hasField AVCodec, PixelFormats, pix_fmts
#hasField AVCodec, SampleFormats, sample_fmts
#hasField AVCodec, ChannelLayouts, channel_layouts
#hasField AVCodec, SupportedSampleRates, supported_samplerates
#hasField AVCodec, Capabilities, capabilities
newtype AVDictionary = AVDictionary (Ptr ()) deriving (Storable, HasPtr)
newtype AVFrame = AVFrame (Ptr ()) deriving (Storable, HasPtr)
#mkField Pts, CLong
#mkField PktPts, CLong
#mkField LineSize, CInt
#mkField Data, (Ptr (Ptr ()))
#mkField ExtendedData, (Ptr (Ptr ()))
#mkField NumSamples, CInt
#mkField Format, CInt
#hasField AVFrame, PixelFormat, format
#hasField AVFrame, SampleFormat, format
#hasField AVFrame, Width, width
#hasField AVFrame, Height, height
#hasField AVFrame, LineSize, linesize
#hasField AVFrame, Pts, pts
#hasField AVFrame, PktPts, pkt_pts
#hasField AVFrame, Data, data
#hasField AVFrame, ExtendedData, extended_data
#hasField AVFrame, NumSamples, nb_samples
#hasField AVFrame, Format, format
#hasField AVFrame, Channels, channels
#hasField AVFrame, ChannelLayout, channel_layout
#hasField AVFrame, SampleRate, sample_rate
newtype AVPicture = AVPicture (Ptr ()) deriving (Storable, HasPtr)
#hasField AVPicture, Data, data
newtype SwsContext = SwsContext (Ptr ()) deriving (Storable, HasPtr)
newtype AVOutputFormat = AVOutputFormat (Ptr ()) deriving (Storable, HasPtr)
#mkField FormatFlags, FormatFlag
#mkField VideoCodecID, AVCodecID
#mkField AudioCodecID, AVCodecID
#hasField AVOutputFormat, FormatFlags, flags
#hasField AVOutputFormat, VideoCodecID, video_codec
#hasField AVOutputFormat, AudioCodecID, audio_codec
newtype AVInputFormat = AVInputFormat (Ptr ()) deriving (Storable, HasPtr)
newtype AVClass = AVClass (Ptr ()) deriving (Storable, HasPtr)
#mkField AVClass, AVClass
#hasField AVInputFormat, AVClass, priv_class
#if LIBAVUTIL_VERSION_MAJOR > 52
getAVCategory :: AVInputFormat -> IO Category
getAVCategory aif =
do c <- getAVClass aif
if nullPtr == getPtr c
then return (Category (-1))
else Category <$> peek ((#ptr AVClass, category) $ castPtr $ getPtr c)
newtype Category = Category CInt deriving (Eq,Ord,Show,Read,Enum)
#enum Category, Category, AV_CLASS_CATEGORY_NA, AV_CLASS_CATEGORY_INPUT,\
AV_CLASS_CATEGORY_OUTPUT, AV_CLASS_CATEGORY_MUXER, AV_CLASS_CATEGORY_DEMUXER,\
AV_CLASS_CATEGORY_ENCODER, AV_CLASS_CATEGORY_DECODER, AV_CLASS_CATEGORY_FILTER,\
AV_CLASS_CATEGORY_BITSTREAM_FILTER, AV_CLASS_CATEGORY_SWSCALER, AV_CLASS_CATEGORY_SWRESAMPLER,\
AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT, AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT,\
AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT, AV_CLASS_CATEGORY_DEVICE_OUTPUT, AV_CLASS_CATEGORY_DEVICE_INPUT,\
AV_CLASS_CATEGORY_NB
#endif
newtype AVIOContext = AVIOContext (Ptr ()) deriving (Storable, HasPtr)
newtype AVPacket = AVPacket (Ptr ()) deriving (Storable, HasPtr)
#mkField PktData, (Ptr ())
#mkField Size, CInt
#mkField PacketFlags, PacketFlag
#mkField Dts, CLong
#mkField Duration, CULong
#hasField AVPacket, PktData, data
#hasField AVPacket, Size, size
#hasField AVPacket, PacketFlags, flags
#hasField AVPacket, StreamIndex, stream_index
#hasField AVPacket, Pts, pts
#hasField AVPacket, Dts, dts
#hasField AVPacket, Duration, duration
-- | @sizeof@ the 'AVPacket' structure in bytes.
packetSize :: Int
packetSize = #size AVPacket
pictureSize :: Int
pictureSize = #size AVPicture
newtype SwrContext = SwrContext (Ptr ()) deriving (Storable, HasPtr)
newtype AVAudioFifo = AVAudioFifo (Ptr ()) deriving (Storable, HasPtr)
foreign import ccall "av_samples_alloc_array_and_samples"
av_samples_alloc_array_and_samples :: Ptr (Ptr (Ptr CUChar))
-> Ptr CInt
-> CInt
-> CInt
-> AVSampleFormat
-> CInt
-> IO CInt
foreign import ccall "av_audio_fifo_free"
av_audio_fifo_free :: AVAudioFifo -> IO ()
foreign import ccall "av_audio_fifo_alloc"
av_audio_fifo_alloc :: AVSampleFormat -> CInt -> CInt -> IO AVAudioFifo
foreign import ccall "av_audio_fifo_realloc"
av_audio_fifo_realloc :: AVAudioFifo -> CInt -> IO CInt
foreign import ccall "av_audio_fifo_write"
av_audio_fifo_write :: AVAudioFifo -> Ptr (Ptr ()) -> CInt -> IO CInt
foreign import ccall "av_audio_fifo_peek"
av_audio_fifo_peek :: AVAudioFifo -> Ptr (Ptr ()) -> CInt -> IO CInt
foreign import ccall "av_audio_fifo_peek_at"
av_audio_fifo_peek_at :: AVAudioFifo -> Ptr (Ptr ()) -> CInt -> CInt -> IO CInt
foreign import ccall "av_audio_fifo_read"
av_audio_fifo_read :: AVAudioFifo -> Ptr (Ptr ()) -> CInt -> IO CInt
foreign import ccall "av_audio_fifo_drain"
av_audio_fifo_drain :: AVAudioFifo -> CInt -> IO CInt
foreign import ccall "av_audio_fifo_reset"
av_audio_fifo_reset :: AVAudioFifo -> IO ()
foreign import ccall "av_audio_fifo_size"
av_audio_fifo_size :: AVAudioFifo -> IO CInt
foreign import ccall "av_audio_fifo_space"
av_audio_fifo_space :: AVAudioFifo -> IO CInt
-- * Types with Haskell equivalents
data AVRational = AVRational { numerator :: CInt
, denomenator :: CInt } deriving Show
-- | FFmpeg often uses 0 to mean "unknown"; use 'Nothing' instead.VRational
nonZeroAVRational :: AVRational -> Maybe AVRational
nonZeroAVRational (AVRational 0 _) = Nothing
nonZeroAVRational ratio = Just ratio
instance Storable AVRational where
sizeOf _ = #size AVRational
alignment _ = #size AVRational
peek ptr = AVRational <$> (#peek AVRational, num) ptr
<*> (#peek AVRational, den) ptr
poke ptr (AVRational n d) = do (#poke AVRational, num) ptr n
(#poke AVRational, den) ptr d
foreign import ccall "av_rescale_rnd"
av_rescale_rnd :: CLong -> CLong -> CLong -> AVRoundMode -> CLong
-- | Convert an 'AVRational' to a 'Double'
av_q2d :: AVRational -> CDouble
av_q2d r = fromIntegral (numerator r) / fromIntegral (denomenator r)
-- | Rescale an integer from one time base to another.
av_rescale_q :: CLong -> AVRational -> AVRational -> CLong
av_rescale_q a bq cq = av_rescale_rnd a b c avRoundNearInf
where b = fromIntegral (numerator bq) * fromIntegral (denomenator cq)
c = fromIntegral (numerator cq) * fromIntegral (denomenator bq)
foreign import ccall "av_packet_rescale_ts"
av_packet_rescale_ts :: AVPacket -> Ptr AVRational -> Ptr AVRational -> IO ()
foreign import ccall "av_packet_unref"
av_packet_unref :: AVPacket -> IO ()
packet_rescale_ts :: AVPacket -> AVRational -> AVRational -> IO ()
packet_rescale_ts packet rat1 rat2 = do
ptr1 <- malloc
ptr2 <- malloc
poke ptr1 rat1
poke ptr2 rat2
av_packet_rescale_ts packet ptr1 ptr2
#if LIBAVFORMAT_VERSION_MAJOR < 57
data AVFrac = AVFrac { fracVal :: CLong
, fracNum :: CLong
, fracDen :: CLong } deriving Show
instance Storable AVFrac where
sizeOf _ = #size AVFrac
alignment _ = #size AVFrac
peek ptr = AVFrac <$> (#peek AVFrac, val) ptr
<*> (#peek AVFrac, num) ptr
<*> (#peek AVFrac, den) ptr
poke ptr (AVFrac v n d) = do (#poke AVFrac, val) ptr v
(#poke AVFrac, num) ptr n
(#poke AVFrac, den) ptr d
#endif
-- | The input source can be a file or a camera. When using 'Camera',
-- frequently in the form @Camera "0:0" defaultCameraConfig@, the first input video device
-- enumerated by libavdevice is selected.
data InputSource = File FilePath | Camera String CameraConfig
deriving (Eq, Ord, Show, Read)
data CameraConfig =
CameraConfig { framerate :: Maybe Int
, resolution :: Maybe (Int,Int)
, format :: Maybe String
-- ^ Short name for a video format (e.g. @"v4l2"@)
}
deriving (Eq,Ord,Show,Read)
defaultCameraConfig :: CameraConfig
defaultCameraConfig = CameraConfig (Just 30) Nothing Nothing