packages feed

isobmff-builder 0.1.0.0 → 0.1.0.1

raw patch · 2 files changed

+194/−193 lines, 2 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Data.ByteString.IsoBaseFileFormat.Boxes.Box: BoxHeader :: BoxSize -> BoxType -> BoxHeader
- Data.ByteString.IsoBaseFileFormat.Boxes.Box: data BoxHeader
- Data.ByteString.IsoBaseFileFormat.Boxes.Box: instance GHC.Classes.Eq Data.ByteString.IsoBaseFileFormat.Boxes.Box.BoxHeader
- Data.ByteString.IsoBaseFileFormat.Boxes.Box: instance GHC.Show.Show Data.ByteString.IsoBaseFileFormat.Boxes.Box.BoxHeader

Files

isobmff-builder.cabal view
@@ -1,5 +1,5 @@ name:                isobmff-builder-version:             0.1.0.0+version:             0.1.0.1 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
src/Data/ByteString/IsoBaseFileFormat/Boxes/Box.hs view
@@ -1,7 +1,12 @@ {-# LANGUAGE UndecidableInstances #-} --- | Definition of the most basic elements in an ISOBMFF file /boxes/.--- See Chapter 4 in the standard document. Since the standard+-- | Definition of the most basic element in an ISOBMFF file: a /box/.  See+-- Chapter 4 in the standard document.  A box is a container with a type, a+-- size, possible some data and some nested boxes.  The standard defines - among+-- other characteristics - what boxes exist, what data they contain and how they+-- are nested into each other.  This library tries to capture some of these+-- characteristics using modern Haskell type system features to provide compile+-- checks for (partial) standard compliance. module Data.ByteString.IsoBaseFileFormat.Boxes.Box        (module Data.ByteString.IsoBaseFileFormat.Boxes.Box, module X)        where@@ -20,6 +25,191 @@ import Data.Type.Equality import GHC.Exts +-- * Basic Types and classes++-- | 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++-- | 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)))++-- | 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++-- | 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++-- * Boxes++-- | 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 Size and Type++-- | 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)++-- | 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))+ -- * Type-safe box composition  -- | A box that may contain nested boxes. The nested boxes are type checked to@@ -139,7 +329,7 @@   :<>: Text " MUST be nested inside boxes of these types: "   :$$: ShowType (RestrictedTo c) --- * Common box /combinators/+-- * Full Boxes  -- | A `Box` with /version/ and /branding/ information type FullBox t = Extend FullBoxHeader t@@ -216,192 +406,3 @@     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