packages feed

isobmff-builder (empty) → 0.1.0.0

raw patch · 10 files changed

+637/−0 lines, 10 filesdep +basedep +bytestringdep +type-listsetup-changed

Dependencies added: base, bytestring, type-list

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Sven Heyll (c) 2016++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Author name here nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ isobmff-builder.cabal view
@@ -0,0 +1,57 @@+name:                isobmff-builder+version:             0.1.0.0+synopsis:            A (bytestring-) builder for the ISO base media file format ISO-14496-12+description:         Please see README.md+homepage:            https://github.com/sheyll/isobmff-builder#readme+license:             BSD3+license-file:        LICENSE+author:              Sven Heyll+maintainer:          sven.heyll@gmail.com+copyright:           2016 Sven Heyll+category:            Codec+build-type:          Simple+-- extra-source-files:+cabal-version:       >=1.10++library+  hs-source-dirs:      src+  exposed-modules:     Data.ByteString.IsoBaseFileFormat.Builder+                     , Data.ByteString.IsoBaseFileFormat.Boxes.Box+                     , Data.ByteString.IsoBaseFileFormat.Boxes.FileType+                     , Data.ByteString.IsoBaseFileFormat.Boxes.MediaData+                     , Data.ByteString.IsoBaseFileFormat.Boxes.Skip+                     , Data.ByteString.IsoBaseFileFormat.Boxes.ProgressiveDownloadInformation+                     , Data.ByteString.IsoBaseFileFormat.Boxes.MovieBox+  build-depends:       base >= 4.7 && < 5+                     , bytestring >= 0.10.8.1+                     , type-list >= 0.5.0.0+  default-language:    Haskell2010+  default-extensions:  ConstraintKinds+                     , CPP+                     , DataKinds+                     , DefaultSignatures+                     , DeriveDataTypeable+                     , DeriveFunctor+                     , DeriveGeneric+                     , FlexibleInstances+                     , FlexibleContexts+                     , FunctionalDependencies+                     , GADTs+                     , GeneralizedNewtypeDeriving+                     , KindSignatures+                     , MultiParamTypeClasses+                     , OverloadedStrings+                     , QuasiQuotes+                     , RecordWildCards+                     , RankNTypes+                     , ScopedTypeVariables+                     , StandaloneDeriving+                     , TemplateHaskell+                     , TupleSections+                     , TypeFamilies+                     , TypeInType+                     , TypeOperators+                     , TypeSynonymInstances+source-repository head+  type:     git+  location: https://github.com/githubuser/isobmff-builder
+ src/Data/ByteString/IsoBaseFileFormat/Boxes/Box.hs view
@@ -0,0 +1,407 @@+{-# LANGUAGE UndecidableInstances #-}++-- | Definition of the most basic elements in an ISOBMFF file /boxes/.+-- See Chapter 4 in the standard document. Since the standard+module Data.ByteString.IsoBaseFileFormat.Boxes.Box+       (module Data.ByteString.IsoBaseFileFormat.Boxes.Box, module X)+       where++import Data.Bits as X+import Data.ByteString.Builder as X+import Data.Monoid as X+import Data.Proxy as X+import Data.Word as X+import GHC.TypeLits as X+import Data.String+import Data.Type.Equality++import Data.Type.List+import Data.Type.Bool+import Data.Type.Equality+import GHC.Exts++-- * Type-safe box composition++-- | A box that may contain nested boxes. The nested boxes are type checked to+-- be valid in the container box. This results in a container-box with only+-- valid and all required child boxes. This is checked by the type system.+boxes :: forall ts t.+         (IsBoxType t,ValidBoxes t ts)+      => Boxes t ts -> Box t+boxes = box++-- | A container-box with child boxes.+data Boxes (cont :: x) (boxTypes :: [x]) where+        Parent :: IsBoxType t => Box t -> Boxes t '[]+        (:-) :: IsBoxType t => Boxes c ts -> Box t -> Boxes c (t ': ts)++infixl 2 :-++-- | A container box that contains only the parent, and no children (yet).+type Container parent = Boxes parent '[]++-- | An operator for @Parent parent :- firstChild@+(^-) :: (IsBoxType t, IsBoxType u) => Box t -> Box u -> Boxes t '[u]+parent ^- firstChild = Parent parent :- firstChild++infixl 2 ^-++-- | To be nested into a box, 'Boxes' must be an instance of 'IsBoxContent'.+-- This instance concatenates all nested boxes.+instance (IsBoxType t,ValidBoxes t bs) => IsBoxContent (Boxes t bs) where+  boxSize bs = boxSize (UnverifiedBoxes bs)+  boxBuilder bs = boxBuilder (UnverifiedBoxes bs)++-- | An internal wrapper type around 'Boxes' for the 'IsBoxContent' instance.+-- Since the 'IsBoxContent' instance recursivly deconstructs a 'Boxes' the+-- constraints for the validity 'ValidBoxes' cannot be asserted. To circumvent+-- this the 'IsBoxContent' instance for 'Boxes' delegates to the instance of+-- 'UnverifiedBoxes', which has no 'ValidBoxes' constraint in the instance head.+newtype UnverifiedBoxes t ts = UnverifiedBoxes (Boxes t ts)++instance IsBoxContent (UnverifiedBoxes t bs) where+  boxSize (UnverifiedBoxes (Parent c)) = boxSize c+  boxSize (UnverifiedBoxes (bs :- b)) = boxSize (UnverifiedBoxes bs) + boxSize b+  boxBuilder (UnverifiedBoxes (Parent c)) = boxBuilder c+  boxBuilder (UnverifiedBoxes (bs :- b)) = boxBuilder (UnverifiedBoxes bs) <> boxBuilder b++-- * Type level consistency checks++-- | A type-level check that uses 'BoxRules' to check that the contained boxes+-- are standard conform.+type ValidBoxes t ts =+  ( AllAllowedIn t ts ~ 'True+  , HasAllRequiredBoxes t (RequiredNestedBoxes t) ts ~ 'True+  , CheckTopLevelOk t ~ 'True)++-- | A type function to check that all nested boxes are allowed in the+-- container.+type family AllAllowedIn (container :: k) (boxes :: [k]) :: Bool+     where+        AllAllowedIn c '[] = 'True+        AllAllowedIn c (t ': ts) =+          If (CheckAllowedIn c t (RestrictedTo t))+             (AllAllowedIn c ts)+             (TypeError (NotAllowedMsg c t))++type family CheckAllowedIn (c :: k) (t :: k) (a :: Maybe [k]) :: Bool where+  CheckAllowedIn c t 'Nothing   = 'True+  CheckAllowedIn c t ('Just rs) = Find c rs+++-- | The custom (type-) error message for 'AllAllowedIn'.+type NotAllowedMsg c t =+  Text "Boxes of type: "+  :<>: ShowType c+  :<>: Text " may not contain boxes of type "+  :<>: ShowType t+  :$$: Text "Valid containers for "+  :<>: ShowType t+  :<>: Text " boxes are: "+  :$$: ShowType (RestrictedTo t)+  :$$: ShowType t+  :<>: If (IsTopLevelBox c)+          (Text " boxes may appear top-level in a file.")+          (Text " boxes must be nested.")+++-- | Check that all required boxes have been nested.+type family HasAllRequiredBoxes (c :: k) (req :: [k]) (nested :: [k]) :: Bool+     where+       HasAllRequiredBoxes c '[] nested = 'True+       HasAllRequiredBoxes c (r ': restReq) nested =+         If (Find r nested)+            (HasAllRequiredBoxes c restReq nested)+            (TypeError (MissingRequired c r nested))++type IsSubSet base sub = Intersection base sub == sub++-- | The custom (type-) error message for 'HasAllRequiredBoxes'.+type MissingRequired c r nested =+  Text "Boxes of type: "+  :<>: ShowType c+  :<>: Text " require these nested boxes: "+  :<>: ShowType (RequiredNestedBoxes c)+  :$$: Text "but only these box types were nested: "+  :<>: ShowType nested+  :$$: Text "e.g. this type is missing: "+  :<>: ShowType r++-- | Check that the box may appear top-level.+type family CheckTopLevelOk (t :: k) :: Bool where+   CheckTopLevelOk t = IsTopLevelBox t || TypeError (NotTopLevenError t)++-- | The custom (type-) error message indicating that a box may not appear+-- top-level.+type NotTopLevenError c =+       Text "Boxes of type "+  :<>: ShowType c+  :<>: Text " MUST be nested inside boxes of these types: "+  :$$: ShowType (RestrictedTo c)++-- * Common box /combinators/++-- | A `Box` with /version/ and /branding/ information+type FullBox t = Extend FullBoxHeader t++-- | Create a 'FullBox' from a 'FourCc' 'StdType' and the nested box content.+fullBox+ :: (IsBoxType t, IsBoxContent c)+ => BoxVersion -> BoxFlags 24 -> c -> Box t+fullBox ver fs cnt = box (Extend (FullBoxHeader ver fs) cnt)++-- | The additional header with /version/ and /branding/ information+data FullBoxHeader =+  FullBoxHeader BoxVersion+                (BoxFlags 24)++instance IsBoxContent FullBoxHeader where+  boxSize (FullBoxHeader _ f) = 1 + boxSize f+  boxBuilder (FullBoxHeader (BoxVersion v) f) = word8 v <> boxBuilder f++-- | The box version (in a 'FullBox') is a single byte+newtype BoxVersion =+  BoxVersion Word8++-- | In addition to a 'BoxVersion' there can be 24 bits for custom flags etc in+-- a 'FullBox'.+newtype BoxFlags bits =+  BoxFlags Integer+  deriving (Eq,Show,Num)++-- | Internal function that creates a bit mask with all bits in a 'BoxFlags' set+-- to 1.+boxFlagBitMask :: KnownNat bits+               => BoxFlags bits -> Integer+boxFlagBitMask px = 2 ^ natVal px - 1++-- | Internal function that masks-out all bits higher than 'bits'.+cropBits :: KnownNat bits+         => BoxFlags bits -> BoxFlags bits+cropBits f@(BoxFlags b) = BoxFlags (b .&. boxFlagBitMask f)++-- | Get the number of bytes required to store a number of bits.+instance KnownNat bits => IsBoxContent (BoxFlags bits) where+  boxSize f =+    let minBytes = fromInteger $ natVal f `div` 8+        modBytes = fromInteger $ natVal f `mod` 8+    in BoxSize $ minBytes + signum modBytes+  boxBuilder f@(BoxFlags b) =+    let bytes =+          let (BoxSize bytes') = boxSize f+          in fromIntegral bytes'+        wordSeq n+          | n <= bytes =+            word8 (fromIntegral (shiftR b ((bytes - n) * 8) .&. 255)) <>+            wordSeq (n + 1)+          | otherwise = mempty+    in wordSeq 1++instance KnownNat bits => Bits (BoxFlags bits) where+  (.&.) lf@(BoxFlags l) (BoxFlags r) = cropBits $ BoxFlags $ l .&. r+  (.|.) lf@(BoxFlags l) (BoxFlags r) = cropBits $ BoxFlags $ l .&. r+  xor (BoxFlags l) (BoxFlags r) = cropBits $ BoxFlags $ xor l r+  complement (BoxFlags x) = cropBits $ BoxFlags $ complement x+  shift (BoxFlags x) = cropBits . BoxFlags . shift x+  rotateL = error "TODO rotateL"+  rotateR = error "TODO rotateR"+  bitSize = fromInteger . natVal+  bitSizeMaybe = Just . fromInteger . natVal+  isSigned _ = False+  testBit f n =+    let (BoxFlags b) = cropBits f+    in testBit b n+  bit = cropBits . BoxFlags . bit+  popCount f =+    let (BoxFlags b) = cropBits f+    in popCount b+  zeroBits = BoxFlags 0++-- * /The/ actual Box++-- | Create a 'Box' with a 'StdType' 'FourCc' type.+box :: forall t c.+       (IsBoxType t,IsBoxContent c)+    => c -> Box t+box cnt = Box (toBoxType (Proxy :: Proxy t)) cnt++-- | An /empty/ box. This is for boxes without fields. All these boxes contain+-- is their obligatory 'BoxHeader' possibly nested boxes.+emptyBox :: forall t . (IsBoxType t) => Box t+emptyBox = box ()++-- | A type that wraps the contents of a box and the box type.+data Box (b :: t) where+        Box :: (IsBoxType t,IsBoxContent c) => BoxType -> c -> Box t++instance IsBoxContent (Box t) where+  boxBuilder b@(Box t cnt) = sFix <> tFix <> sExt <> tExt <> boxBuilder cnt+    where s = boxSize b+          sFix = boxBuilder s+          sExt = boxBuilder (BoxSizeExtension s)+          tFix = boxBuilder t+          tExt = boxBuilder (BoxTypeExtension t)+  boxSize b@(Box t cnt) = sPayload + boxSize (BoxSizeExtension sPayload)+    where sPayload =+            boxSize sPayload + boxSize t + boxSize cnt ++            boxSize (BoxTypeExtension t)++-- * Box Meta Data+-- | The box header contains a size and a type. Both can be compact (32bit) or+-- large (64bit size, 17*32bit type).+data BoxHeader =+  BoxHeader BoxSize+            BoxType+  deriving (Show,Eq)++-- | The size of the box. If the size is limited to a (fixed) value, it can be+-- provided as a 'Word64' which will be represented as either a 32bit compact+-- size or as 64 bit /largesize/. If 'UnlimitedSize' is used, the box extends to+-- the end of the file.+data BoxSize+  = UnlimitedSize+  | BoxSize Word64+  deriving (Show,Eq)++instance IsBoxContent BoxSize where+  boxSize _ = BoxSize 4+  boxBuilder UnlimitedSize = word32BE 0+  boxBuilder (BoxSize n) =+    word32BE $+    if n < 2 ^ 32+       then fromIntegral n+       else 1++instance Num BoxSize where+  (+) UnlimitedSize _ = UnlimitedSize+  (+) _ UnlimitedSize = UnlimitedSize+  (+) (BoxSize l) (BoxSize r) = BoxSize (l + r)+  (-) UnlimitedSize _ = UnlimitedSize+  (-) _ UnlimitedSize = UnlimitedSize+  (-) (BoxSize l) (BoxSize r) = BoxSize (l - r)+  (*) UnlimitedSize _ = UnlimitedSize+  (*) _ UnlimitedSize = UnlimitedSize+  (*) (BoxSize l) (BoxSize r) = BoxSize (l * r)+  abs UnlimitedSize = UnlimitedSize+  abs (BoxSize n) = BoxSize (abs n)+  signum UnlimitedSize = UnlimitedSize+  signum (BoxSize n) = BoxSize (signum n)+  fromInteger n = BoxSize $ fromInteger n++-- | The 'BoxSize' can be > 2^32 in which case an 'BoxSizeExtension' must be+-- added after the type field.+data BoxSizeExtension =+  BoxSizeExtension BoxSize++instance IsBoxContent BoxSizeExtension where+  boxBuilder (BoxSizeExtension UnlimitedSize) = mempty+  boxBuilder (BoxSizeExtension (BoxSize n)) =+    if n < 2 ^ 32+       then mempty+       else word64BE n+  boxSize (BoxSizeExtension UnlimitedSize) = 0+  boxSize (BoxSizeExtension (BoxSize n)) =+    BoxSize $+    if n < 2 ^ 32+       then 0+       else 8++-- | A box has a /type/, this is the value level representation for the box type.+data BoxType+  =+    -- | `FourCc` can be used as @boxType@ in `Box`, standard four letter character+    -- code, e.g. @ftyp@+    StdType FourCc+  |+    -- | CustomBoxType defines custom @boxType@s in `Box`es.+    CustomBoxType String+  deriving (Show,Eq)++-- | Convert type level box types to values+class BoxRules t => IsBoxType (t :: k) where+  toBoxType :: proxy t -> BoxType++instance (BoxRules t, KnownSymbol t) => IsBoxType t where+  toBoxType _ = StdType (fromString (symbolVal (Proxy :: Proxy t)))++-- | A class that describes (on the type level) how a box can be nested into+-- other boxes (see 'Boxes').+class BoxRules (t :: k) where+  -- | List of boxes that this box can be nested into.+  type RestrictedTo t :: Maybe [k]+  type RestrictedTo t = 'Just '[]+  -- | If the box is also allowed 'top-level' i.e. in the file directly, not+  -- nested in an other box.+  type IsTopLevelBox t :: Bool+  type IsTopLevelBox t = 'True+  -- | Describes which nested boxes MUST be present in a box using 'boxes'.+  type RequiredNestedBoxes t :: [k]+  type RequiredNestedBoxes t = '[]+  -- | Describes how many times a box should be present in a container (-box).+  type GetCardinality t (c :: k) :: Cardinality+  type GetCardinality t any = ExactlyOnce++-- | Describes how many times a box should be present in a container.+data Cardinality = AtMostOnce | ExactlyOnce | OnceOrMore++-- | A type containin a printable four letter character code.+newtype FourCc =+  FourCc (Char,Char,Char,Char)+  deriving (Show,Eq)++instance IsString FourCc where+  fromString str+    | length str == 4 =+      let [a,b,c,d] = str+      in FourCc (a,b,c,d)+    | otherwise =+      error ("cannot make a 'FourCc' of a String which isn't exactly 4 bytes long: " +++             show str ++ " has a length of " ++ show (length str))++instance IsBoxContent FourCc where+  boxSize _ = 4+  boxBuilder (FourCc (a,b,c,d)) = putW a <> putW b <> putW c <> putW d+    where putW = word8 . fromIntegral . fromEnum++instance IsBoxContent BoxType where+  boxSize _ = boxSize (FourCc undefined)+  boxBuilder t =+    case t of+      StdType x -> boxBuilder x+      CustomBoxType u -> boxBuilder (FourCc ('u','u','i','d'))++-- | When using custom types extra data must be written after the extra size+-- information. Since the box type and the optional custom box type are not+-- guaranteed to be consequtive, this type handles the /second/ part seperately.+data BoxTypeExtension =+  BoxTypeExtension BoxType++instance IsBoxContent BoxTypeExtension where+  boxSize (BoxTypeExtension (StdType _)) = 0+  boxSize (BoxTypeExtension (CustomBoxType _)) = 16 * 4+  boxBuilder (BoxTypeExtension (StdType _)) = mempty+  boxBuilder (BoxTypeExtension (CustomBoxType str)) =+    mconcat (map (word8 . fromIntegral . fromEnum)+                 (take (16 * 4) str) +++             repeat (word8 0))++-- | Box content composed of box contents @a@ and @b@.+data Extend a b =+  Extend a+         b++instance (IsBoxContent p,IsBoxContent c) => IsBoxContent (Extend p c) where+  boxSize (Extend p c) = boxSize p + boxSize c+  boxBuilder (Extend p c) = boxBuilder p <> boxBuilder c++-- | Types that go into a box. A box content is a piece of data that can be+-- reused in different instances of 'IsBox'. It has no 'BoxType' and hence+-- defines no box.+class IsBoxContent a  where+  boxSize :: a -> BoxSize+  boxBuilder :: a -> Builder++-- | An empty box content can by represented by @()@ (i.e. /unit/).+instance IsBoxContent () where+  boxSize _ = 0+  boxBuilder _ = mempty
+ src/Data/ByteString/IsoBaseFileFormat/Boxes/FileType.hs view
@@ -0,0 +1,24 @@+module Data.ByteString.IsoBaseFileFormat.Boxes.FileType where++import Data.ByteString.IsoBaseFileFormat.Boxes.Box++-- | File Type Box+type FileTypeBox = Box "ftyp"++instance BoxRules "ftyp"++-- | Create a 'FileTypeBox' from a major brand, a minor version and a list of+-- compatible brands+fileTypeBox :: FileType -> FileTypeBox+fileTypeBox = box++-- | Contents of a 'ftyp' box are some 'FourCc' /brands/ and a version.+data FileType =+  FileType {majorBrand :: FourCc+           ,minorVersion :: Word32+           ,compatibleBrands :: [FourCc]}++instance IsBoxContent FileType where+  boxSize (FileType maj ver comps) = boxSize maj + 4 + sum (boxSize <$> comps)+  boxBuilder (FileType maj ver comps) =+    boxBuilder maj <> word32BE ver <> mconcat (boxBuilder <$> comps)
+ src/Data/ByteString/IsoBaseFileFormat/Boxes/MediaData.hs view
@@ -0,0 +1,21 @@+module Data.ByteString.IsoBaseFileFormat.Boxes.MediaData where++import Data.ByteString.IsoBaseFileFormat.Boxes.Box+import qualified Data.ByteString as B++-- | Media data box+type MediaDataBox = Box "mdat"++instance BoxRules "mdat"++-- | Create a 'MediaDataBox' from a strict 'ByteString'+mediaDataBox :: MediaData -> MediaDataBox+mediaDataBox = box++-- | Contents of a 'mdat' box are just bytes from a strict 'ByteString'+newtype MediaData =+  MediaData B.ByteString++instance IsBoxContent MediaData where+  boxSize (MediaData bs) = fromIntegral $ B.length bs+  boxBuilder (MediaData bs) = byteString bs
+ src/Data/ByteString/IsoBaseFileFormat/Boxes/MovieBox.hs view
@@ -0,0 +1,39 @@+-- | Meta data for a presentation of a /movie/.+module Data.ByteString.IsoBaseFileFormat.Boxes.MovieBox where++import Data.ByteString.IsoBaseFileFormat.Boxes.Box++-- Uncomment this to see what the custom compiler errors look like:+--+-- import Data.ByteString.IsoBaseFileFormat.Boxes.Skip+-- xxx :: Box "moov"+-- xxx = movieBox+--         (movieBoxContainer :- skipBox (Skip 100))++-- | The metadata for a presentation, a single 'MovieBox' which occurs only once+-- and top-level. It is pretty empty on it's own, but it contains nested boxes+-- with all the relevant meta data.+type MovieBox = Box "moov"++instance BoxRules "moov" where+  type RequiredNestedBoxes "moov" = '["mvhd", "trak"]++-- | Compose a movie box from the required 'Boxes'.+movieBox :: forall ts. ValidBoxes "moov" ts => Boxes "moov" ts -> Box "moov"+movieBox = boxes++-- | The movie box container, use this to create movie boxes filled with nested+--   boxes, for example:+--+-- >  xxx :: Box "moov"+-- >  xxx = movieBox+-- >          (movieBoxContainer+-- >           :- movieHeaderBox (MovieHeader ...)+-- >           :- trackBox+-- >              (trackBoxContainer+-- >               :- trackHeaderBox+-- >               :- trackReferenceBox+-- >               :- trackGroupingIndication))+--+movieBoxContainer :: Container "moov"+movieBoxContainer = Parent emptyBox
+ src/Data/ByteString/IsoBaseFileFormat/Boxes/ProgressiveDownloadInformation.hs view
@@ -0,0 +1,25 @@+module Data.ByteString.IsoBaseFileFormat.Boxes.ProgressiveDownloadInformation+       where++import Data.ByteString.IsoBaseFileFormat.Boxes.Box++-- | A Box with progressive download information+type ProgressiveDownloadInformationBox = Box "pdin"++instance BoxRules "pdin"++-- | Create a 'pdin' box+progressiveDownloadInformationBox+  :: ProgressiveDownloadInformation -> ProgressiveDownloadInformationBox+progressiveDownloadInformationBox = fullBox (BoxVersion 0) zeroBits++-- | Information for progressive media data download/playback encompasses the+-- delay for initial playback and expected download bit rate.+data ProgressiveDownloadInformation =+  ProgressiveDownloadInformation {pdinRate :: Word32 -- ^ Effective download bitrate in byte/sec+                                 ,pdinDelay :: Word32 -- ^ Delay in milli seconds+                                 }++instance IsBoxContent ProgressiveDownloadInformation where+  boxSize _ = 8+  boxBuilder pdin = word32BE (pdinRate pdin) <> word32BE (pdinDelay pdin)
+ src/Data/ByteString/IsoBaseFileFormat/Boxes/Skip.hs view
@@ -0,0 +1,21 @@+module Data.ByteString.IsoBaseFileFormat.Boxes.Skip where++import Data.ByteString.IsoBaseFileFormat.Boxes.Box++-- | A filler box, the contents are skipped+type SkipBox = Box "skip"++instance BoxRules "skip" where+  type RestrictedTo "skip" = 'Nothing++-- | Create a 'SkipBox' with a given size.+skipBox :: Skip -> SkipBox+skipBox = box++-- | Contents of a 'skip' box are just any number of filler bytes.+newtype Skip =+  Skip Int++instance IsBoxContent Skip where+  boxSize (Skip bs) = fromIntegral bs+  boxBuilder (Skip bs) = mconcat (replicate bs (word8 0))
+ src/Data/ByteString/IsoBaseFileFormat/Builder.hs view
@@ -0,0 +1,11 @@+-- | This module re-exports all modules needed to build /ISOBMFF/ documents.+module Data.ByteString.IsoBaseFileFormat.Builder+  (module Data.ByteString.IsoBaseFileFormat.Builder, module X)+  where++import Data.ByteString.IsoBaseFileFormat.Boxes.Box as X+import Data.ByteString.IsoBaseFileFormat.Boxes.FileType as X+import Data.ByteString.IsoBaseFileFormat.Boxes.MediaData as X+import Data.ByteString.IsoBaseFileFormat.Boxes.MovieBox as X+import Data.ByteString.IsoBaseFileFormat.Boxes.ProgressiveDownloadInformation as X+import Data.ByteString.IsoBaseFileFormat.Boxes.Skip as X