module Codec.Container.Ogg.Track (
OggTrack (..),
newTrack,
nullTrack,
bosToTrack,
gpToTimestamp,
gpToGranules,
gpSplit,
gpExplain,
ContentTypeImplied,
contentTypeImplies
) where
import qualified Data.ByteString.Lazy as L
import Data.Bits
import Data.Ratio
import Text.Printf
import Codec.Container.Ogg.ContentType
import Codec.Container.Ogg.Granulepos
import Codec.Container.Ogg.Granulerate
import Codec.Container.Ogg.MessageHeaders
import Codec.Container.Ogg.Serial
import Codec.Container.Ogg.Timestamp
data OggTrack =
OggTrack {
trackSerialno :: Serial,
trackType :: Maybe ContentType,
trackHeaders :: Int,
trackGranulerate :: Maybe Granulerate,
trackGranuleshift :: Maybe Int,
trackMetadata :: MessageHeaders
}
class (ContentTyped a) => ContentTypeImplied a where
contentTypeImplies :: [OggTrack] -> ContentType -> a -> Bool
trackIsType :: ContentType -> OggTrack -> Bool
trackIsType t0 track
| (Just t0) == t1 = True
| otherwise = False
where t1 = trackType track
instance ContentTyped OggTrack where
contentTypeIs = trackIsType
contentTypeOf = trackType
instance ContentTypeImplied OggTrack where
contentTypeImplies _ = trackIsType
instance Serialled OggTrack where
serialOf = trackSerialno
nullTrack :: OggTrack
nullTrack = OggTrack 0 Nothing 0 Nothing Nothing mhEmpty
newTrack :: Serial -> OggTrack
newTrack serialno = OggTrack serialno Nothing 0 Nothing Nothing mhEmpty
bosToTrack :: Serial -> L.ByteString -> OggTrack
bosToTrack s d = OggTrack s ctype nh gr gs mh
where
ctype = identify d
nh = maybe 1 (\x -> headers x d) ctype
gr = maybe Nothing (\x -> granulerate x d) ctype
gs = maybe Nothing (\x -> granuleshift x d) ctype
mh = maybe mhEmpty (\x -> metadata x d) ctype
gpToTimestamp :: Granulepos -> OggTrack -> Maybe Timestamp
gpToTimestamp mgp track
| g == Nothing = Nothing
| r == Nothing = Nothing
| otherwise = Just (Timestamp timestamp)
where g = gpToGranules mgp track
r = trackGranulerate track
timestamp = (granules*d, n)
n = numerator gr
d = denominator gr
Just granules = g
Just (Granulerate gr) = r
gpExplain :: Granulepos -> OggTrack -> String
gpExplain mgp track
| s == Nothing = show mgp
| otherwise = show keyframe ++ "|" ++ show delta
where s = gpSplit mgp track
Just (keyframe, delta) = s
gpToGranules :: Granulepos -> OggTrack -> Maybe Integer
gpToGranules mgp track
| s == Nothing = Nothing
| otherwise = Just (keyframe + delta)
where s = gpSplit mgp track
Just (keyframe, delta) = s
gpSplit :: Granulepos -> OggTrack -> Maybe (Integer, Integer)
gpSplit mgp track
| mgp == Granulepos Nothing = Nothing
| trackGranuleshift track == Nothing = Just (gp, 0)
| otherwise = Just (keyframe, delta)
where Granulepos (Just w64gp) = mgp
Just gShift = trackGranuleshift track
gp = fromIntegral w64gp
keyframe = fromIntegral $ w64gp `shiftR` gShift
delta = fromIntegral $ gp (keyframe `shiftL` gShift)
instance Eq OggTrack where
(==) track1 track2 = s1 == s2
where s1 = trackSerialno track1
s2 = trackSerialno track2
instance Ord OggTrack where
compare track1 track2 = compare s1 s2
where s1 = trackSerialno track1
s2 = trackSerialno track2
instance Show OggTrack where
show (OggTrack serialno ctype _ _ _ mhdrs) =
t ++ ": serialno " ++ s ++ "\n" ++ m
where s = printf "%010u" ((fromIntegral serialno) :: Int)
t = maybe "(Unknown)" show ctype
m = unlines $ zipWith (++) (repeat "\t") (lines $ show mhdrs)