|
| 1 | +{-# LANGUAGE LambdaCase #-} |
| 2 | + |
1 | 3 | module Main (main) where
|
2 | 4 |
|
3 |
| -import Control.Monad.Trans.Class (lift) |
4 |
| -import Data.Binary (get) |
5 |
| -import Data.Binary.Get (Decoder (..), runGetIncremental) |
6 |
| -import qualified Data.ByteString as B (null) |
| 5 | +import Common (FragmentationIndicator (..), consumeAll) |
| 6 | +import Control.Monad (when) |
| 7 | +import Data.Binary (Binary, Get, Word16, Word32, Word64, get) |
| 8 | +import Data.Binary.Get (runGetOrFail) |
7 | 9 | import qualified Data.ByteString.Lazy as L (readFile)
|
8 |
| -import qualified Data.ByteString.Lazy.Internal as L (ByteString (Chunk, Empty), chunk) |
| 10 | +import Data.ByteString.Lazy.Internal (ByteString (Empty)) |
9 | 11 | import Data.Int (Int64)
|
10 |
| -import Lib (TLVPacket) |
11 |
| -import ListT (ListT, cons) |
12 |
| -import qualified ListT (head) |
| 12 | +import qualified Data.Map as Map |
| 13 | +import Lib |
| 14 | +import Message (ControlMessage, ControlMessages (..)) |
| 15 | +import qualified Streamly.Data.Fold as Fold |
| 16 | +import Streamly.Data.Stream (Stream, mapMaybe, postscan, unfoldrM) |
| 17 | +import qualified Streamly.Data.Stream as S (foldr, foldrM, mapM, take, toList) |
| 18 | +import Streamly.Internal.Data.Pipe.Type (Pipe (Pipe), PipeState (..), Step (..)) |
| 19 | +import Streamly.Internal.Data.Stream.StreamD.Transform (transform) |
13 | 20 | import Text.Show.Pretty (pPrint)
|
14 | 21 |
|
15 |
| -parseTLVPackets :: MonadFail m => Decoder TLVPacket -> (Int64, L.ByteString) -> ListT m TLVPacket |
16 |
| -parseTLVPackets (Fail _ loc e) (br, _) = lift $ fail $ "error parse at " ++ show (br + loc) ++ ": " ++ e |
17 |
| -parseTLVPackets (Done r _ p) (_, L.Empty) | B.null r = return p |
18 |
| -parseTLVPackets (Done r o p) (br, input) = cons p $ parseTLVPackets (runGetIncremental get) (o + br, L.chunk r input) |
19 |
| -parseTLVPackets (Partial k) (br, L.Empty) = parseTLVPackets (k Nothing) (br, L.Empty) |
20 |
| -parseTLVPackets (Partial k) (br, L.Chunk bs input) = parseTLVPackets (k (Just bs)) (br, input) |
| 22 | +data ParseState = ParseState ByteString Int64 |
| 23 | + |
| 24 | +type CMState = Map.Map Word16 (Word32, ByteString) |
| 25 | + |
| 26 | +type CMPipeState = PipeState CMState (CMState, [ByteString]) |
| 27 | + |
| 28 | +reassembleControlMessages :: Pipe IO (Word16, Word32, ControlMessages) ControlMessage |
| 29 | +reassembleControlMessages = Pipe consume produce Map.empty |
| 30 | + where |
| 31 | + produce :: (CMState, [ByteString]) -> IO (Step CMPipeState ControlMessage) |
| 32 | + produce (m, []) = return $ Continue $ Consume m |
| 33 | + produce (m, x : xs) = do |
| 34 | + p <- consumeAll get x |
| 35 | + return $ Yield p $ Produce (m, xs) |
| 36 | + |
| 37 | + consume :: CMState -> (Word16, Word32, ControlMessages) -> IO (Step CMPipeState ControlMessage) |
| 38 | + consume m (pid, pseq, ControlMessages ind _ _ msgs) = |
| 39 | + case ind of |
| 40 | + FragmentationIndicatorUndivided -> case msgs of |
| 41 | + Left bs -> return $ Continue $ Produce (m, [bs]) |
| 42 | + Right bs -> return $ Continue $ Produce (m, bs) |
| 43 | + FragmentationIndicatorDividedHead -> do |
| 44 | + when (Map.member pid m) $ fail $ "Packet ID " ++ show pid ++ " already started fragmented message" |
| 45 | + t <- extractOne msgs |
| 46 | + return $ Continue $ Consume $ Map.insert pid (pseq, t) m |
| 47 | + FragmentationIndicatorDividedBody -> do |
| 48 | + case Map.lookup pid m of |
| 49 | + Nothing -> fail $ "Packet ID " ++ show pid ++ " not started fragmented message" |
| 50 | + Just (s, bs) -> do |
| 51 | + when (s >= pseq) $ fail "Sequence number reversed" |
| 52 | + t <- extractOne msgs |
| 53 | + return $ Continue $ Consume $ Map.insert pid (pseq, bs <> t) m |
| 54 | + FragmentationIndicatorDividedEnd -> do |
| 55 | + case Map.lookup pid m of |
| 56 | + Nothing -> fail $ "Packet ID " ++ show pid ++ " not started fragmented message" |
| 57 | + Just (s, bs) -> do |
| 58 | + when (s >= pseq) $ fail "Sequence number reversed" |
| 59 | + t <- extractOne msgs |
| 60 | + return $ Continue $ Produce (Map.delete pid m, [bs <> t]) |
| 61 | + where |
| 62 | + extractOne :: MonadFail m => Either ByteString [ByteString] -> m ByteString |
| 63 | + extractOne (Left bs) = return bs |
| 64 | + extractOne (Right _) = fail "more than one message" |
| 65 | + |
| 66 | +parseTLVPackets :: ParseState -> IO (Maybe (TLVPacket, ParseState)) |
| 67 | +parseTLVPackets (ParseState Empty _) = return Nothing |
| 68 | +parseTLVPackets (ParseState bs o) = do |
| 69 | + case runGetOrFail get bs of |
| 70 | + Left (_, off, err) -> fail $ "error parse at " ++ show (o + off) ++ ": " ++ err |
| 71 | + Right (r, off, p) -> return $ Just (p, ParseState r (o + off)) |
21 | 72 |
|
22 | 73 | main :: IO ()
|
23 | 74 | main = do
|
24 | 75 | file <- L.readFile "F:\\29999.mmts"
|
25 |
| - let packets = parseTLVPackets (runGetIncremental get) (0, file) |
26 |
| - len <- ListT.head packets |
27 |
| - pPrint len |
| 76 | + let packets = unfoldrM parseTLVPackets (ParseState file 0) |
| 77 | + headers = |
| 78 | + mapMaybe |
| 79 | + (\case TLVPacketHeaderCompressedIP c -> Just $ contextIdentificationHeader c; _ -> Nothing) |
| 80 | + packets |
| 81 | + payloads = |
| 82 | + mapMaybe |
| 83 | + (\case ContextIdentificationNoCompressedHeader h -> Just h; _ -> Nothing) |
| 84 | + headers |
| 85 | + fragments = |
| 86 | + mapMaybe |
| 87 | + ( \case |
| 88 | + MMTPPacket |
| 89 | + { packetId = pid, |
| 90 | + packetSequenceNumber = pseq, |
| 91 | + mmtpPayload = MMTPPayloadControlMessages m |
| 92 | + } -> Just (pid, pseq, m) |
| 93 | + _ -> Nothing |
| 94 | + ) |
| 95 | + payloads |
| 96 | + messages = transform reassembleControlMessages fragments |
| 97 | + -- (r, o) <- S.foldrM reassembleControlMessages (pure (mempty, [])) fragments |
| 98 | + |
| 99 | + _ <- S.toList $ S.mapM pPrint messages |
| 100 | + return () |
| 101 | + |
| 102 | +-- len <- S.foldr (\_ c -> c + 1) (0 :: Word64) messages |
| 103 | +-- print len |
28 | 104 |
|
29 | 105 | -- list <- toList $ parseTLVPackets (runGetIncremental get) file
|
30 | 106 | -- print list
|
|
0 commit comments