mediabus-0.3.2.0: src/Data/MediaBus/Media/Reframe.hs
-- | This module contains functions and types for reframing the sequence number
-- and timestamps of 'Frame's.
--
-- This means that this module will allow you to record a set of incoming
-- frames using 'pushFrame'. Whenever you want to extract the timing and
-- sequence number information of the next frame with a given duration,
-- use 'generateFrame'.
module Data.MediaBus.Media.Reframe
( initialReframerState
, runReframer
, pushStartFrame
, pushFrame
, PushFrameError(..)
, nextFrameAvailableDuration
, nextFrameTimestamp
, generateFrame
, type ReframerT
, ReframerSt()
, ReframeError(..)
, mkReFrameError
) where
import Control.Exception
import Control.Monad
import Control.Monad.Trans.State.Strict as State
import Data.MediaBus.Media.Stream
import Data.Typeable
-- | Create an empty initial state.
initialReframerState
:: (Num d, Num s)
=> ReframerSt s d
initialReframerState = MkTimeFrame 0 0 0
-- | Run state 'ReframerSt' state transformer.
runReframer
:: Monad m
=> ReframerT m s d a -> ReframerSt s d -> m (a, ReframerSt s d)
runReframer = runStateT
-- | Reset the current timing and sequence number, and start with the given
-- start time.
pushStartFrame
:: (Num d, Monad m)
=> d -> ReframerT m s d ()
pushStartFrame d = do
(MkTimeFrame _ s _) <- State.get
State.put (MkTimeFrame d s d)
-- | Increase the available duration by the duration in the frame, iff the
-- timestamp of the given frame matches exactly the timestamp after the end
-- of the available period, otherwise do nothing with the state and return
-- 'True'.
pushFrame
:: (Num d, Monad m, Eq d, Ord d)
=> Frame s d d -> ReframerT m s d (Maybe PushFrameError)
pushFrame (MkFrame ts _ dur) = do
(MkTimeFrame startTs nextSeq endTs) <- State.get
let endTs' = ts + dur
when (ts == endTs) (State.put (MkTimeFrame startTs nextSeq endTs'))
return
(if | ts < endTs && endTs' < endTs -> Just InputFrameIsLate
| ts < endTs && endTs' >= endTs -> Just InputFrameOverlaps
| ts > endTs -> Just InputFrameIsEarly
| ts == endTs -> Nothing)
-- | Specifies in what way 'pushFrame' failed
data PushFrameError
= InputFrameIsLate -- ^ The input frame ends before '_endTs'
| InputFrameOverlaps -- ^ The input frame starts before, and ends after '_endTs'
| InputFrameIsEarly -- ^ The input frame begins after '_endTs'
deriving (Eq, Ord, Show, Enum)
-- | Return the duration of the frames recorded with 'pushFrame'.
nextFrameAvailableDuration
:: (Num d, Monad m)
=> ReframerT m s d d
nextFrameAvailableDuration = do
(MkTimeFrame startTs _ endTs) <- State.get
return (endTs - startTs)
-- | Return the timestamp of the frame being build. .
nextFrameTimestamp
:: (Num d, Monad m)
=> ReframerT m s d d
nextFrameTimestamp = do
(MkTimeFrame startTs _ _) <- State.get
return startTs
-- | Try to create a frame with the given duration, and update the state
-- accordingly, the actual duration, that was available is
-- put into the payload field of the frame returned.
-- The start time stamp of the next frame
-- is always incremented by the @wantedDureation@ regardless of wether it was
-- available.
generateFrame
:: (Num s, Num d, Monad m, Eq d, Ord d, Show d)
=> d -> ReframerT m s d (Frame s d d)
generateFrame wantedDuration = do
(MkTimeFrame startTs nextSeq endTs) <- State.get
available <- nextFrameAvailableDuration
let endTs' = max (startTs + wantedDuration) endTs
State.put (MkTimeFrame (startTs + wantedDuration) (nextSeq + 1) endTs')
return (MkFrame startTs nextSeq (wantedDuration `min` available))
-- | Reframer state.
data ReframerSt s d = MkTimeFrame
{ _startTs :: !d
, _nextSeqNum :: !s
, _endTs :: !d
} deriving (Typeable)
instance (Show s, Show d) =>
Show (ReframerSt s d) where
showsPrec d MkTimeFrame {_startTs, _nextSeqNum, _endTs} =
showParen
(d > 10)
(showString "reframer-state: " .
showString "start: " .
showsPrec 11 _startTs .
showString ", end: " .
showsPrec 11 _endTs . showString ", next-sn: " . showsPrec 11 _nextSeqNum)
-- | The 'ReframerSt' 'StateT' transformer
type ReframerT m s d a = StateT (ReframerSt s d) m a
-- | The exception type for 'encodeLinearToAacC'
data ReframeError s d = MkReframeError
{ reframeError :: String
, reframeErrorRequestedOutput :: Maybe d
, reframeErrorSt :: ReframerSt s d
} deriving (Typeable)
instance (Show s, Show d) =>
Show (ReframeError s d) where
showsPrec d MkReframeError { reframeError
, reframeErrorRequestedOutput
, reframeErrorSt
} =
showParen
(d > 10)
(showString "reframe-error: " .
showsPrec 11 reframeError .
maybe
id
(\ro -> showString ", requested: " . showsPrec 11 ro)
reframeErrorRequestedOutput .
showString ", " . showsPrec 11 reframeErrorSt)
instance (Show s, Typeable s, Show d, Typeable d) =>
Exception (ReframeError s d)
-- | Utility function to generate a 'ReframeError' with the current state.
mkReFrameError
:: Monad m
=> String -> Maybe d -> ReframerT m s d (ReframeError s d)
mkReFrameError msg requestedOutputDuration =
MkReframeError msg requestedOutputDuration <$> State.get