ffmpeg-light-0.14.0: src/Codec/FFmpeg/Resampler.hs
module Codec.FFmpeg.Resampler where
import Codec.FFmpeg.Common
import Codec.FFmpeg.Enums
import Codec.FFmpeg.Types
import Control.Concurrent.STM.TChan
import Control.Monad (void, when)
import Control.Monad.STM
import Foreign.C.String
import Foreign.C.Types
import Foreign.Marshal.Alloc
import Foreign.Marshal.Array
import Foreign.Ptr
import Foreign.Storable
foreign import ccall "swr_get_delay"
swr_get_delay :: SwrContext -> CLong -> IO CLong
foreign import ccall "swr_convert"
swr_convert :: SwrContext -> Ptr (Ptr CUChar) -> CInt
-> Ptr (Ptr CUChar) -> CInt -> IO CInt
foreign import ccall "swr_get_out_samples"
swr_get_out_samples :: SwrContext -> CInt -> IO CInt
data AudioParams = AudioParams
{ apChannelLayout :: CULong
, apSampleRate :: CInt
, apSampleFormat :: AVSampleFormat
}
makeResampler :: AVCodecContext
-> AudioParams
-> AudioParams
-> IO (AVFrame -> IO (), IO (Maybe AVFrame))
makeResampler ctx inParams outParams = do
swr <- initSwrContext inParams outParams
frameChan <- newTChanIO
let writeFrame frame = do
srcSamples <- getNumSamples frame
if srcSamples == 0
then return ()
else do
srcRate <- getSampleRate frame
delay <- swr_get_delay swr (fromIntegral srcRate)
let dstSamples = av_rescale_rnd
(delay + fromIntegral srcSamples)
(fromIntegral (apSampleRate outParams))
(fromIntegral srcRate) avRoundUp
srcData = castPtr (hasData frame)
dstDataPtr <- malloc
lineSize <- malloc
dstChannelCount <- av_get_channel_layout_nb_channels
(apChannelLayout outParams)
runWithError "Could not alloc samples"
(av_samples_alloc_array_and_samples dstDataPtr lineSize
dstChannelCount (fromIntegral dstSamples)
(apSampleFormat outParams) 0)
dstData <- peek dstDataPtr
runWithError "Error converting samples"
(swr_convert swr nullPtr 0 srcData srcSamples)
frameSize <- getFrameSize ctx
let convertLoop = do
outSamples <- swr_get_out_samples swr 0
if outSamples < frameSize * dstChannelCount
then return ()
else do
frame <- allocAudioFrame ctx
outSamples <- swr_convert swr (castPtr $ hasData frame)
frameSize nullPtr 0
atomically $ writeTChan frameChan frame
convertLoop
convertLoop
free dstData
free lineSize
return ()
allocAudioFrame :: AVCodecContext -> IO AVFrame
allocAudioFrame ctx = do
frame <- av_frame_alloc
when (getPtr frame == nullPtr)
(error "Error allocating an audio frame")
setFormat frame . getSampleFormatInt =<< getSampleFormat ctx
setChannelLayout frame =<< getChannelLayout ctx
setSampleRate frame =<< getSampleRate ctx
fs <- (do fs <- getFrameSize ctx
if fs == 0
then return 1000
else return fs)
setNumSamples frame fs
runWithError "Error allocating an audio buffer"
(av_frame_get_buffer frame 0)
return frame
readFrame = do
mFrame <- atomically $ tryReadTChan frameChan
case mFrame of
Nothing -> return Nothing
Just _ -> return mFrame
return (writeFrame, readFrame)
initSwrContext :: AudioParams -> AudioParams -> IO SwrContext
initSwrContext inParams outParams = do
swr <- swr_alloc
when (getPtr swr == nullPtr) (error "Could not allocate resampler context")
let set_int str i = do
cStr <- newCString str
av_opt_set_int (getPtr swr) cStr (fromIntegral i) 0
free cStr
set_sample_fmt str fmt = do
cStr <- newCString str
av_opt_set_sample_fmt (getPtr swr) cStr fmt 0
free cStr
-- set_int "in_channel_count" (aoChannelCount inParams)
set_int "in_channel_layout" (apChannelLayout inParams)
set_int "in_sample_rate" (apSampleRate inParams)
set_sample_fmt "in_sample_fmt" (apSampleFormat inParams)
-- set_int "out_channel_count" (aoChannelCount outParams)
set_int "out_channel_layout" (apChannelLayout inParams)
set_int "out_sample_rate" (apSampleRate outParams)
set_sample_fmt "out_sample_fmt" (apSampleFormat outParams)
void $ runWithError "Failed to initialize the resampling context" (swr_init swr)
let get_int str = do
cStr <- newCString str
p <- malloc
r <- av_opt_get_int (getPtr swr) cStr 0 p
v <- peek p
free p
return v
get_sample_fmt str = do
cStr <- newCString str
p <- malloc
r <- av_opt_get_sample_fmt (getPtr swr) cStr 0 p
fmt <- peek p
free p
return fmt
return swr