mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 08:35:26 +00:00
212 lines
7.3 KiB
C++
212 lines
7.3 KiB
C++
/* -*- 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<nsWebMTimeDataOffset>& 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<nsWebMTimeDataOffset> mTimeMapping;
|
|
|
|
// Sorted (by offset) live parser instances. Main thread only.
|
|
nsTArray<nsWebMBufferedParser> mRangeParsers;
|
|
};
|
|
|
|
#endif
|