DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (d38398e5144e)

VCS Links

EBMLElement

State

VInt

WebMBufferedParser

WebMBufferedState

WebMTimeDataOffset

Macros

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
/* -*- 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(WebMBufferedParser_h_)
#define WebMBufferedParser_h_

#include "nsISupportsImpl.h"
#include "nsTArray.h"
#include "mozilla/ReentrantMonitor.h"
#include "MediaResource.h"

namespace mozilla {

// Stores a stream byte offset and the scaled timecode of the block at
// that offset.
struct WebMTimeDataOffset
{
  WebMTimeDataOffset(int64_t aEndOffset, uint64_t aTimecode,
                     int64_t aInitOffset, int64_t aSyncOffset,
                     int64_t aClusterEndOffset)
    : mEndOffset(aEndOffset)
    , mInitOffset(aInitOffset)
    , mSyncOffset(aSyncOffset)
    , mClusterEndOffset(aClusterEndOffset)
    , mTimecode(aTimecode)
  {}

  bool operator==(int64_t aEndOffset) const {
    return mEndOffset == aEndOffset;
  }

  bool operator!=(int64_t aEndOffset) const {
    return mEndOffset != aEndOffset;
  }

  bool operator<(int64_t aEndOffset) const {
    return mEndOffset < aEndOffset;
  }

  int64_t mEndOffset;
  int64_t mInitOffset;
  int64_t mSyncOffset;
  int64_t mClusterEndOffset;
  uint64_t 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 WebMBufferedParser
{
  explicit WebMBufferedParser(int64_t aOffset)
    : mStartOffset(aOffset)
    , mCurrentOffset(aOffset)
    , mInitEndOffset(-1)
    , mBlockEndOffset(-1)
    , mState(READ_ELEMENT_ID)
    , mNextState(READ_ELEMENT_ID)
    , mVIntRaw(false)
    , mLastInitStartOffset(-1)
    , mClusterSyncPos(0)
    , mVIntLeft(0)
    , mBlockSize(0)
    , mClusterTimecode(0)
    , mClusterOffset(0)
    , mClusterEndOffset(-1)
    , mBlockOffset(0)
    , mBlockTimecode(0)
    , mBlockTimecodeLength(0)
    , mSkipBytes(0)
    , mTimecodeScale(1000000)
    , mGotTimecodeScale(false)
  {
    if (mStartOffset != 0) {
      mState = FIND_CLUSTER_SYNC;
    }
  }

  uint32_t GetTimecodeScale() {
    MOZ_ASSERT(mGotTimecodeScale);
    return mTimecodeScale;
  }

  // If this parser is not expected to parse a segment info, it must be told
  // the appropriate timecode scale to use from elsewhere.
  void SetTimecodeScale(uint32_t aTimecodeScale) {
    mTimecodeScale = aTimecodeScale;
    mGotTimecodeScale = true;
  }

  // Steps the parser through aLength bytes of data.  Always consumes
  // aLength bytes.  Updates mCurrentOffset before returning.  Acquires
  // aReentrantMonitor before using aMapping.
  // Returns false if an error was encountered.
  bool Append(const unsigned char* aBuffer, uint32_t aLength,
              nsTArray<WebMTimeDataOffset>& aMapping,
              ReentrantMonitor& aReentrantMonitor);

  bool operator==(int64_t aOffset) const {
    return mCurrentOffset == aOffset;
  }

  bool operator<(int64_t aOffset) const {
    return mCurrentOffset < aOffset;
  }

  // Returns the start offset of the init (EBML) or media segment (Cluster)
  // following the aOffset position. If none were found, returns mBlockEndOffset.
  // This allows to determine the end of the interval containg aOffset.
  int64_t EndSegmentOffset(int64_t 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.
  int64_t mStartOffset;

  // Current offset within the stream.  Updated in chunks as Append() consumes
  // data.
  int64_t mCurrentOffset;

  // Tracks element's end offset. This indicates the end of the first init
  // segment. Will only be set if a Segment Information has been found.
  int64_t mInitEndOffset;

  // End offset of the last block parsed.
  // Will only be set if a complete block has been parsed.
  int64_t mBlockEndOffset;

private:
  enum State {
    // Parser start state.  Expects to begin at a valid EBML element.  Move
    // to READ_VINT with mVIntRaw true, then return to READ_ELEMENT_SIZE.
    READ_ELEMENT_ID,

    // Store element ID read into mVInt into mElement.mID.  Move to
    // READ_VINT with mVIntRaw false, then return to PARSE_ELEMENT.
    READ_ELEMENT_SIZE,

    // Parser start state for parsers started at an arbitrary offset.  Scans
    // forward for the first cluster, then move to READ_ELEMENT_ID.
    FIND_CLUSTER_SYNC,

    // Simplistic core of the parser.  Does not pay attention to nesting of
    // elements.  Checks mElement for an element ID of interest, then moves
    // to the next state as determined by the element ID.
    PARSE_ELEMENT,

    // 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.mValue and the length is
    // stored in mVInt.mLength.  The number of bytes left to read is stored
    // in mVIntLeft.
    READ_VINT,

    // Reads the remaining mVIntLeft bytes into mVInt.mValue.
    READ_VINT_REST,

    // mVInt holds the parsed timecode scale, store it in mTimecodeScale,
    // then return READ_ELEMENT_ID.
    READ_TIMECODESCALE,

    // mVInt holds the parsed cluster timecode, store it in
    // mClusterTimecode, then return to READ_ELEMENT_ID.
    READ_CLUSTER_TIMECODE,

    // mBlockTimecodeLength holds the remaining length of the block timecode
    // left to read.  Read each byte of the timecode into mBlockTimecode.
    // Once complete, calculate the scaled timecode from the cluster
    // timecode, block timecode, and timecode scale, and insert a
    // WebMTimeDataOffset entry into aMapping if one is not already present
    // for this offset.
    READ_BLOCK_TIMECODE,

    // Will skip the current tracks element and set mInitEndOffset if an init
    // segment has been found.
    // Currently, only assumes it's the end of the tracks element.
    CHECK_INIT_FOUND,

    // Skip mSkipBytes of data before resuming parse at mNextState.
    SKIP_DATA,
  };

  // 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;

  struct VInt {
    VInt() : mValue(0), mLength(0) {}
    uint64_t mValue;
    uint64_t mLength;
  };

  struct EBMLElement {
    uint64_t Length() { return mID.mLength + mSize.mLength; }
    VInt mID;
    VInt mSize;
  };

  EBMLElement mElement;

  VInt mVInt;

  bool mVIntRaw;

  // EBML start offset. This indicates the start of the last init segment
  // parsed. Will only be set if an EBML element has been found.
  int64_t mLastInitStartOffset;

  // Current match position within CLUSTER_SYNC_ID.  Used to find sync
  // within arbitrary data.
  uint32_t mClusterSyncPos;

  // Number of bytes of mVInt left to read.  mVInt is complete once this
  // reaches 0.
  uint32_t mVIntLeft;

  // Size of the block currently being parsed.  Any unused data within the
  // block is skipped once the block timecode has been parsed.
  uint64_t mBlockSize;

  // Cluster-level timecode.
  uint64_t mClusterTimecode;

  // Start offset of the cluster currently being parsed.  Used as the sync
  // point offset for the offset-to-time mapping as each block timecode is
  // been parsed.
  int64_t mClusterOffset;

  // End offset of the cluster currently being parsed. -1 if unknown.
  int64_t mClusterEndOffset;

  // 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.
  int64_t mBlockOffset;

  // Block-level timecode.  This is summed with mClusterTimecode to produce
  // an absolute timecode for the offset-to-time mapping.
  int16_t mBlockTimecode;

  // Number of bytes of mBlockTimecode left to read.
  uint32_t mBlockTimecodeLength;

  // Count of bytes left to skip before resuming parse at mNextState.
  // Mostly used to skip block payload data after reading a block timecode.
  uint32_t mSkipBytes;

  // Timecode scale read from the segment info and used to scale absolute
  // timecodes.
  uint32_t mTimecodeScale;

  // True if we read the timecode scale from the segment info or have
  // confirmed that the default value is to be used.
  bool mGotTimecodeScale;
};

class WebMBufferedState final
{
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebMBufferedState)

public:
  WebMBufferedState()
    : mReentrantMonitor("WebMBufferedState")
    , mLastBlockOffset(-1)
  {
    MOZ_COUNT_CTOR(WebMBufferedState);
  }

  void NotifyDataArrived(const unsigned char* aBuffer, uint32_t aLength, int64_t aOffset);
  void Reset();
  void UpdateIndex(const MediaByteRangeSet& aRanges, MediaResource* aResource);
  bool CalculateBufferedForRange(int64_t aStartOffset, int64_t aEndOffset,
                                 uint64_t* aStartTime, uint64_t* aEndTime);

  // Returns true if mTimeMapping is not empty and sets aOffset to
  // the latest offset for which decoding can resume without data
  // dependencies to arrive at aTime. aTime will be clamped to the start
  // of mTimeMapping if it is earlier than the first element, and to the end
  // if later than the last
  bool GetOffsetForTime(uint64_t aTime, int64_t* aOffset);

  // Returns end offset of init segment or -1 if none found.
  int64_t GetInitEndOffset();
  // Returns the end offset of the last complete block or -1 if none found.
  int64_t GetLastBlockOffset();

  // Returns start time
  bool GetStartTime(uint64_t *aTime);

  // Returns keyframe for time
  bool GetNextKeyframeTime(uint64_t aTime, uint64_t* aKeyframeTime);

private:
  // Private destructor, to discourage deletion outside of Release():
  ~WebMBufferedState() {
    MOZ_COUNT_DTOR(WebMBufferedState);
  }

  // Synchronizes access to the mTimeMapping array and mLastBlockOffset.
  ReentrantMonitor mReentrantMonitor;

  // Sorted (by offset) map of data offsets to timecodes.  Populated
  // on the main thread as data is received and parsed by WebMBufferedParsers.
  nsTArray<WebMTimeDataOffset> mTimeMapping;
  // The last complete block parsed. -1 if not set.
  int64_t mLastBlockOffset;

  // Sorted (by offset) live parser instances.  Main thread only.
  nsTArray<WebMBufferedParser> mRangeParsers;
};

} // namespace mozilla

#endif