packages feed

ebml-0.1.0.0: src/Codec/EBML.hs

{- | This module is intended to be imported qualified:

> import qualified Codec.EBML as EBML

Decode a webm file with:

> EBML.decodeWebMFile "path/file.webm"

Split a webm stream segments with:

> let streamReader = EBML.newStreamReader
> buf <- acquire data
> EBML.feedReader buf streamReader

References:

 - EBML specification introduction: https://matroska-org.github.io/libebml/specs.html
 - Document layout: https://www.matroska.org/technical/diagram.html
 - The matroska schema: https://github.com/ietf-wg-cellar/matroska-specification/blob/master/ebml_matroska.xml
 - The matroska schema doc: https://www.matroska.org/technical/elements.html
 - The webm guidelines: https://www.webmproject.org/docs/container/
 - The MSE byte stream format spec: https://w3c.github.io/media-source/index.html#byte-stream-format-specs
 - The MSE byte stream format for webm: https://w3c.github.io/mse-byte-stream-format-webm/
-}
module Codec.EBML (
    -- * WebM decoder
    decodeWebM,
    WebMDocument (..),
    WebMCluster (..),

    -- * Raw EBML decoder
    decodeEBMLDocument,
    webmSchemas,

    -- * EBML stream reader
    module Codec.EBML.Stream,

    -- * EBML data types
    EBMLDocument (..),
    EBMLElement (..),
    EBMLValue (..),
    EBMLElementHeader (..),
    EBMLID (..),

    -- * EBML schema data types
    EBMLSchema (..),

    -- * Helpers
    decodeEBMLFile,
    decodeWebMFile,
    prettyEBMLDocument,

    -- * Low-level API, mostly for testing
    EBMLSchemas,
    compileSchemas,
    getDocument,
    getElementHeader,
    getElementID,
    getDataSize,
    getElement,
) where

import Data.Binary.Get (runGetOrFail)
import Data.ByteString.Lazy qualified as LBS
import Data.Text (Text)
import Data.Text qualified as Text

import Codec.EBML.Element
import Codec.EBML.Get
import Codec.EBML.Header
import Codec.EBML.Pretty
import Codec.EBML.Schema
import Codec.EBML.Stream
import Codec.EBML.WebM

-- | Lazy decode a 'WebMDocument'.
decodeWebM :: LBS.ByteString -> Either Text WebMDocument
decodeWebM lbs = decodeWebMDocument =<< decodeEBMLDocument webmSchemas lbs

-- | The webm document schemas.
webmSchemas :: [EBMLSchema]
webmSchemas = schemaHeader

-- | Lazy decode a 'EBMLDocument'.
decodeEBMLDocument :: [EBMLSchema] -> LBS.ByteString -> Either Text EBMLDocument
decodeEBMLDocument schemas lbs = case runGetOrFail (getDocument (compileSchemas schemas)) lbs of
    Left (_, _, err) -> Left (Text.pack err)
    Right ("", _, x) -> Right x
    Right (_rest, l, _) -> Left ("Left over data at " <> Text.pack (show l))

-- | Decode a raw EBML file.
decodeEBMLFile :: [EBMLSchema] -> FilePath -> IO (Either Text EBMLDocument)
decodeEBMLFile schemas fp = decodeEBMLDocument schemas <$> LBS.readFile fp

-- | Decode a webm file.
decodeWebMFile :: FilePath -> IO (Either Text WebMDocument)
decodeWebMFile fp = do
    ebml <- decodeEBMLFile webmSchemas fp
    pure $ decodeWebMDocument =<< ebml