{-# LANGUAGE FlexibleContexts, FlexibleInstances, ScopedTypeVariables, UndecidableInstances #-} module Codec.FFmpeg.Scaler where import Codec.FFmpeg.Common import Codec.FFmpeg.Enums import Codec.FFmpeg.Internal.Linear (V2(..)) import Codec.FFmpeg.Types import Codec.Picture import Data.Maybe (fromMaybe) import qualified Data.Vector.Storable as V import Foreign.C.Types import Foreign.Marshal.Array (withArray) import Foreign.Ptr (castPtr, nullPtr, Ptr) import Foreign.Storable (Storable(sizeOf)) data ImageInfo = ImageInfo { imgWidth :: CInt , imgHeight :: CInt , imgFormat :: AVPixelFormat } -- | @swsInit srcInfo dstInfo alg@ initializations an 'SwsContext' to -- scale and convert from @srcInfo@ to @dstInfo@ using the algorithm -- @alg@ when scaling. swsInit :: ImageInfo -> ImageInfo -> SwsAlgorithm -> IO SwsContext swsInit = swsReset (SwsContext nullPtr) -- | Obtain a context for converting the source to destination -- format. If the given context is already configured for the required -- conversion, it is returned. Otherwise, the given context is freed -- and a new, configured context is returned. See 'swsInit' for a -- description of the arguments. swsReset :: SwsContext -> ImageInfo -> ImageInfo -> SwsAlgorithm -> IO SwsContext swsReset ctx src dst alg = sws_getCachedContext ctx srcW srcH srcFmt dstW dstH dstFmt alg nullPtr nullPtr nullPtr where ImageInfo srcW srcH srcFmt = src ImageInfo dstW dstH dstFmt = dst -- | A common interface required of arguments to 'swsScale' (a higher -- level wrapper for the 'sws_scale' function from @libswscale@). class SwsCompatible a where swsPlanes :: a -> (Ptr (Ptr CUChar) -> IO r) -> IO r swsStrides :: a -> (Ptr CInt -> IO r) -> IO r sliceHeight :: a -> (CInt -> IO r) -> IO r instance SwsCompatible AVFrame where swsPlanes frame k = k (castPtr $ hasData frame) swsStrides frame k = k (hasLineSize frame) sliceHeight frame k = getHeight frame >>= k instance (Pixel a, Storable (PixelBaseComponent a)) => SwsCompatible (Image a) where swsPlanes img k = V.unsafeWith (imageData img) $ \ptr -> withArray (castPtr ptr : replicate 7 nullPtr) k swsStrides img k = withArray (stride : replicate 7 0) k where sz = sizeOf (undefined::PixelBaseComponent a) * componentCount (undefined :: a) stride = fromIntegral $ imageWidth img * sz sliceHeight img k = k (fromIntegral $ imageHeight img) instance SwsCompatible (AVPixelFormat, V2 CInt, V.Vector CUChar) where swsPlanes (_,_,p) k = V.unsafeWith p $ \ptr -> withArray (castPtr ptr : replicate 7 nullPtr) k swsStrides (fmt, V2 w _, _) k = withArray (stride : replicate 7 0) k where sz = fromMaybe (error $ "Unknown pixel stride for format "++show fmt) (avPixelStride fmt) stride = w * fromIntegral sz sliceHeight (_, V2 _ h, _) k = k h -- | Supplies a continuation with all components provided by the -- 'SwsCompatible' class. withSws :: SwsCompatible a => a -> (Ptr (Ptr CUChar) -> Ptr CInt -> CInt -> IO r) -> IO r withSws img k = swsPlanes img $ \planes -> swsStrides img $ \strides -> sliceHeight img $ \height -> k planes strides height -- | @swsScale ctx src dst@ scales the entire @src@ image to @dst@ -- using the previously initialized @ctx@. swsScale :: (SwsCompatible src, SwsCompatible dst) => SwsContext -> src -> dst -> IO CInt swsScale ctx src dst = withSws src $ \srcPlanes srcStrides srcHeight -> withSws dst $ \dstPlanes dstStrides _ -> sws_scale ctx srcPlanes srcStrides 0 srcHeight dstPlanes dstStrides