michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #if !defined(WebMBufferedParser_h_) michael@0: #define WebMBufferedParser_h_ michael@0: michael@0: #include "nsISupportsImpl.h" michael@0: #include "nsTArray.h" michael@0: #include "mozilla/ReentrantMonitor.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: namespace dom { michael@0: class TimeRanges; michael@0: } michael@0: michael@0: // Stores a stream byte offset and the scaled timecode of the block at michael@0: // that offset. The timecode must be scaled by the stream's timecode michael@0: // scale before use. michael@0: struct WebMTimeDataOffset michael@0: { michael@0: WebMTimeDataOffset(int64_t aOffset, uint64_t aTimecode) michael@0: : mOffset(aOffset), mTimecode(aTimecode) michael@0: {} michael@0: michael@0: bool operator==(int64_t aOffset) const { michael@0: return mOffset == aOffset; michael@0: } michael@0: michael@0: bool operator<(int64_t aOffset) const { michael@0: return mOffset < aOffset; michael@0: } michael@0: michael@0: int64_t mOffset; michael@0: uint64_t mTimecode; michael@0: }; michael@0: michael@0: // A simple WebM parser that produces data offset to timecode pairs as it michael@0: // consumes blocks. A new parser is created for each distinct range of data michael@0: // received and begins parsing from the first WebM cluster within that michael@0: // range. Old parsers are destroyed when their range merges with a later michael@0: // parser or an already parsed range. The parser may start at any position michael@0: // within the stream. michael@0: struct WebMBufferedParser michael@0: { michael@0: WebMBufferedParser(int64_t aOffset) michael@0: : mStartOffset(aOffset), mCurrentOffset(aOffset), mState(CLUSTER_SYNC), mClusterIDPos(0) michael@0: {} michael@0: michael@0: // Steps the parser through aLength bytes of data. Always consumes michael@0: // aLength bytes. Updates mCurrentOffset before returning. Acquires michael@0: // aReentrantMonitor before using aMapping. michael@0: void Append(const unsigned char* aBuffer, uint32_t aLength, michael@0: nsTArray& aMapping, michael@0: ReentrantMonitor& aReentrantMonitor); michael@0: michael@0: bool operator==(int64_t aOffset) const { michael@0: return mCurrentOffset == aOffset; michael@0: } michael@0: michael@0: bool operator<(int64_t aOffset) const { michael@0: return mCurrentOffset < aOffset; michael@0: } michael@0: michael@0: // The offset at which this parser started parsing. Used to merge michael@0: // adjacent parsers, in which case the later parser adopts the earlier michael@0: // parser's mStartOffset. michael@0: int64_t mStartOffset; michael@0: michael@0: // Current offset with the stream. Updated in chunks as Append() consumes michael@0: // data. michael@0: int64_t mCurrentOffset; michael@0: michael@0: private: michael@0: enum State { michael@0: // Parser start state. Scans forward searching for stream sync by michael@0: // matching CLUSTER_ID with the curernt byte. The match state is stored michael@0: // in mClusterIDPos. Once this reaches sizeof(CLUSTER_ID), stream may michael@0: // have sync. The parser then advances to read the cluster size and michael@0: // timecode. michael@0: CLUSTER_SYNC, michael@0: michael@0: /* michael@0: The the parser states below assume that CLUSTER_SYNC has found a valid michael@0: sync point within the data. If parsing fails in these states, the michael@0: parser returns to CLUSTER_SYNC to find a new sync point. michael@0: */ michael@0: michael@0: // Read the first byte of a variable length integer. The first byte michael@0: // encodes both the variable integer's length and part of the value. michael@0: // The value read so far is stored in mVInt and the length is stored in michael@0: // mVIntLength. The number of bytes left to read is stored in michael@0: // mVIntLeft. michael@0: READ_VINT, michael@0: michael@0: // Reads the remaining mVIntLeft bytes into mVInt. michael@0: READ_VINT_REST, michael@0: michael@0: // Check that the next element is TIMECODE_ID. The cluster timecode is michael@0: // required to be the first element in a cluster. Advances to READ_VINT michael@0: // to read the timecode's length into mVInt. michael@0: TIMECODE_SYNC, michael@0: michael@0: // mVInt holds the length of the variable length unsigned integer michael@0: // containing the cluster timecode. Read mVInt bytes into michael@0: // mClusterTimecode. michael@0: READ_CLUSTER_TIMECODE, michael@0: michael@0: // Skips elements with a cluster until BLOCKGROUP_ID or SIMPLEBLOCK_ID michael@0: // is found. If BLOCKGROUP_ID is found, the parser returns to michael@0: // ANY_BLOCK_ID searching for a BLOCK_ID. Once a block or simpleblock michael@0: // is found, the current data offset is stored in mBlockOffset. If the michael@0: // current byte is the beginning of a four byte variant integer, it michael@0: // indicates the parser has reached a top-level element ID and the michael@0: // parser returns to CLUSTER_SYNC. michael@0: ANY_BLOCK_SYNC, michael@0: michael@0: // Start reading a block. Blocks and simpleblocks are parsed the same michael@0: // way as the initial layouts are identical. mBlockSize is initialized michael@0: // from mVInt (holding the element size), and mBlockTimecode(Length) is michael@0: // initialized for parsing. michael@0: READ_BLOCK, michael@0: michael@0: // Reads mBlockTimecodeLength bytes of data into mBlockTimecode. When michael@0: // mBlockTimecodeLength reaches 0, the timecode has been read. The sum michael@0: // of mClusterTimecode and mBlockTimecode is stored as a pair with michael@0: // mBlockOffset into the offset-to-time map. michael@0: READ_BLOCK_TIMECODE, michael@0: michael@0: // Skip mSkipBytes of data before resuming parse at mNextState. michael@0: SKIP_DATA, michael@0: michael@0: // Skip the content of an element. mVInt holds the element length. michael@0: SKIP_ELEMENT michael@0: }; michael@0: michael@0: // Current state machine action. michael@0: State mState; michael@0: michael@0: // Next state machine action. SKIP_DATA and READ_VINT_REST advance to michael@0: // mNextState when the current action completes. michael@0: State mNextState; michael@0: michael@0: // Match position within CLUSTER_ID. Used to find sync within arbitrary michael@0: // data. michael@0: uint32_t mClusterIDPos; michael@0: michael@0: // Variable length integer read from data. michael@0: uint64_t mVInt; michael@0: michael@0: // Encoding length of mVInt. This is the total number of bytes used to michael@0: // encoding mVInt's value. michael@0: uint32_t mVIntLength; michael@0: michael@0: // Number of bytes of mVInt left to read. mVInt is complete once this michael@0: // reaches 0. michael@0: uint32_t mVIntLeft; michael@0: michael@0: // Size of the block currently being parsed. Any unused data within the michael@0: // block is skipped once the block timecode has been parsed. michael@0: uint64_t mBlockSize; michael@0: michael@0: // Cluster-level timecode. michael@0: uint64_t mClusterTimecode; michael@0: michael@0: // Start offset of the block currently being parsed. Used as the byte michael@0: // offset for the offset-to-time mapping once the block timecode has been michael@0: // parsed. michael@0: int64_t mBlockOffset; michael@0: michael@0: // Block-level timecode. This is summed with mClusterTimecode to produce michael@0: // an absolute timecode for the offset-to-time mapping. michael@0: int16_t mBlockTimecode; michael@0: michael@0: // Number of bytes of mBlockTimecode left to read. michael@0: uint32_t mBlockTimecodeLength; michael@0: michael@0: // Count of bytes left to skip before resuming parse at mNextState. michael@0: // Mostly used to skip block payload data after reading a block timecode. michael@0: uint32_t mSkipBytes; michael@0: }; michael@0: michael@0: class WebMBufferedState MOZ_FINAL michael@0: { michael@0: NS_INLINE_DECL_REFCOUNTING(WebMBufferedState) michael@0: michael@0: public: michael@0: WebMBufferedState() : mReentrantMonitor("WebMBufferedState") { michael@0: MOZ_COUNT_CTOR(WebMBufferedState); michael@0: } michael@0: michael@0: void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset); michael@0: bool CalculateBufferedForRange(int64_t aStartOffset, int64_t aEndOffset, michael@0: uint64_t* aStartTime, uint64_t* aEndTime); michael@0: bool GetOffsetForTime(uint64_t aTime, int64_t* aOffset); michael@0: michael@0: private: michael@0: // Private destructor, to discourage deletion outside of Release(): michael@0: ~WebMBufferedState() { michael@0: MOZ_COUNT_DTOR(WebMBufferedState); michael@0: } michael@0: michael@0: // Synchronizes access to the mTimeMapping array. michael@0: ReentrantMonitor mReentrantMonitor; michael@0: michael@0: // Sorted (by offset) map of data offsets to timecodes. Populated michael@0: // on the main thread as data is received and parsed by WebMBufferedParsers. michael@0: nsTArray mTimeMapping; michael@0: michael@0: // Sorted (by offset) live parser instances. Main thread only. michael@0: nsTArray mRangeParsers; michael@0: }; michael@0: michael@0: } // namespace mozilla michael@0: michael@0: #endif