{- | 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 :: ByteString -> Either Text WebMDocument
decodeWebM ByteString
lbs = EBMLDocument -> Either Text WebMDocument
decodeWebMDocument forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< [EBMLSchema] -> ByteString -> Either Text EBMLDocument
decodeEBMLDocument [EBMLSchema]
webmSchemas ByteString
lbs

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

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

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

-- | Decode a webm file.
decodeWebMFile :: FilePath -> IO (Either Text WebMDocument)
decodeWebMFile :: String -> IO (Either Text WebMDocument)
decodeWebMFile String
fp = do
    Either Text EBMLDocument
ebml <- [EBMLSchema] -> String -> IO (Either Text EBMLDocument)
decodeEBMLFile [EBMLSchema]
webmSchemas String
fp
    forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ EBMLDocument -> Either Text WebMDocument
decodeWebMDocument forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Either Text EBMLDocument
ebml