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