Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/WebMBufferedParser.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim:set ts=2 sw=2 sts=2 et cindent: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
#if !defined(WebMBufferedParser_h_)
7
#define WebMBufferedParser_h_
8
9
#include "nsISupportsImpl.h"
10
#include "nsTArray.h"
11
#include "mozilla/ReentrantMonitor.h"
12
#include "MediaResource.h"
13
14
namespace mozilla {
15
16
// Stores a stream byte offset and the scaled timecode of the block at
17
// that offset.
18
struct WebMTimeDataOffset
19
{
20
  WebMTimeDataOffset(int64_t aEndOffset, uint64_t aTimecode,
21
                     int64_t aInitOffset, int64_t aSyncOffset,
22
                     int64_t aClusterEndOffset)
23
    : mEndOffset(aEndOffset)
24
    , mInitOffset(aInitOffset)
25
    , mSyncOffset(aSyncOffset)
26
    , mClusterEndOffset(aClusterEndOffset)
27
    , mTimecode(aTimecode)
28
  {}
29
30
  bool operator==(int64_t aEndOffset) const {
31
    return mEndOffset == aEndOffset;
32
  }
33
34
  bool operator!=(int64_t aEndOffset) const {
35
    return mEndOffset != aEndOffset;
36
  }
37
38
  bool operator<(int64_t aEndOffset) const {
39
    return mEndOffset < aEndOffset;
40
  }
41
42
  int64_t mEndOffset;
43
  int64_t mInitOffset;
44
  int64_t mSyncOffset;
45
  int64_t mClusterEndOffset;
46
  uint64_t mTimecode;
47
};
48
49
// A simple WebM parser that produces data offset to timecode pairs as it
50
// consumes blocks.  A new parser is created for each distinct range of data
51
// received and begins parsing from the first WebM cluster within that
52
// range.  Old parsers are destroyed when their range merges with a later
53
// parser or an already parsed range.  The parser may start at any position
54
// within the stream.
55
struct WebMBufferedParser
56
{
57
  explicit WebMBufferedParser(int64_t aOffset)
58
    : mStartOffset(aOffset)
59
    , mCurrentOffset(aOffset)
60
    , mInitEndOffset(-1)
61
    , mBlockEndOffset(-1)
62
    , mState(READ_ELEMENT_ID)
63
    , mNextState(READ_ELEMENT_ID)
64
    , mVIntRaw(false)
65
    , mLastInitStartOffset(-1)
66
    , mClusterSyncPos(0)
67
    , mVIntLeft(0)
68
    , mBlockSize(0)
69
    , mClusterTimecode(0)
70
    , mClusterOffset(-1)
71
    , mClusterEndOffset(-1)
72
    , mBlockOffset(0)
73
    , mBlockTimecode(0)
74
    , mBlockTimecodeLength(0)
75
    , mSkipBytes(0)
76
    , mTimecodeScale(1000000)
77
    , mGotTimecodeScale(false)
78
    , mGotClusterTimecode(false)
79
0
  {
80
0
    if (mStartOffset != 0) {
81
0
      mState = FIND_CLUSTER_SYNC;
82
0
    }
83
0
  }
84
85
0
  uint32_t GetTimecodeScale() {
86
0
    MOZ_ASSERT(mGotTimecodeScale);
87
0
    return mTimecodeScale;
88
0
  }
89
90
  // Use this function when we would only feed media segment for the parser.
91
  void AppendMediaSegmentOnly()
92
0
  {
93
0
    mGotTimecodeScale = true;
94
0
  }
95
96
  // If this parser is not expected to parse a segment info, it must be told
97
  // the appropriate timecode scale to use from elsewhere.
98
  void SetTimecodeScale(uint32_t aTimecodeScale) {
99
    mTimecodeScale = aTimecodeScale;
100
    mGotTimecodeScale = true;
101
  }
102
103
  // Steps the parser through aLength bytes of data.  Always consumes
104
  // aLength bytes.  Updates mCurrentOffset before returning.  Acquires
105
  // aReentrantMonitor before using aMapping.
106
  // Returns false if an error was encountered.
107
  bool Append(const unsigned char* aBuffer, uint32_t aLength,
108
              nsTArray<WebMTimeDataOffset>& aMapping,
109
              ReentrantMonitor& aReentrantMonitor);
110
111
  bool operator==(int64_t aOffset) const {
112
    return mCurrentOffset == aOffset;
113
  }
114
115
  bool operator<(int64_t aOffset) const {
116
    return mCurrentOffset < aOffset;
117
  }
118
119
  // Returns the start offset of the init (EBML) or media segment (Cluster)
120
  // following the aOffset position. If none were found, returns mBlockEndOffset.
121
  // This allows to determine the end of the interval containg aOffset.
122
  int64_t EndSegmentOffset(int64_t aOffset);
123
124
  // Return the Cluster offset, return -1 if we can't find the Cluster.
125
  int64_t GetClusterOffset() const;
126
127
  // The offset at which this parser started parsing.  Used to merge
128
  // adjacent parsers, in which case the later parser adopts the earlier
129
  // parser's mStartOffset.
130
  int64_t mStartOffset;
131
132
  // Current offset within the stream.  Updated in chunks as Append() consumes
133
  // data.
134
  int64_t mCurrentOffset;
135
136
  // Tracks element's end offset. This indicates the end of the first init
137
  // segment. Will only be set if a Segment Information has been found.
138
  int64_t mInitEndOffset;
139
140
  // End offset of the last block parsed.
141
  // Will only be set if a complete block has been parsed.
142
  int64_t mBlockEndOffset;
143
144
private:
145
  enum State {
146
    // Parser start state.  Expects to begin at a valid EBML element.  Move
147
    // to READ_VINT with mVIntRaw true, then return to READ_ELEMENT_SIZE.
148
    READ_ELEMENT_ID,
149
150
    // Store element ID read into mVInt into mElement.mID.  Move to
151
    // READ_VINT with mVIntRaw false, then return to PARSE_ELEMENT.
152
    READ_ELEMENT_SIZE,
153
154
    // Parser start state for parsers started at an arbitrary offset.  Scans
155
    // forward for the first cluster, then move to READ_ELEMENT_ID.
156
    FIND_CLUSTER_SYNC,
157
158
    // Simplistic core of the parser.  Does not pay attention to nesting of
159
    // elements.  Checks mElement for an element ID of interest, then moves
160
    // to the next state as determined by the element ID.
161
    PARSE_ELEMENT,
162
163
    // Read the first byte of a variable length integer.  The first byte
164
    // encodes both the variable integer's length and part of the value.
165
    // The value read so far is stored in mVInt.mValue and the length is
166
    // stored in mVInt.mLength.  The number of bytes left to read is stored
167
    // in mVIntLeft.
168
    READ_VINT,
169
170
    // Reads the remaining mVIntLeft bytes into mVInt.mValue.
171
    READ_VINT_REST,
172
173
    // mVInt holds the parsed timecode scale, store it in mTimecodeScale,
174
    // then return READ_ELEMENT_ID.
175
    READ_TIMECODESCALE,
176
177
    // mVInt holds the parsed cluster timecode, store it in
178
    // mClusterTimecode, then return to READ_ELEMENT_ID.
179
    READ_CLUSTER_TIMECODE,
180
181
    // mBlockTimecodeLength holds the remaining length of the block timecode
182
    // left to read.  Read each byte of the timecode into mBlockTimecode.
183
    // Once complete, calculate the scaled timecode from the cluster
184
    // timecode, block timecode, and timecode scale, and insert a
185
    // WebMTimeDataOffset entry into aMapping if one is not already present
186
    // for this offset.
187
    READ_BLOCK_TIMECODE,
188
189
    // Will skip the current tracks element and set mInitEndOffset if an init
190
    // segment has been found.
191
    // Currently, only assumes it's the end of the tracks element.
192
    CHECK_INIT_FOUND,
193
194
    // Skip mSkipBytes of data before resuming parse at mNextState.
195
    SKIP_DATA,
196
  };
197
198
  // Current state machine action.
199
  State mState;
200
201
  // Next state machine action.  SKIP_DATA and READ_VINT_REST advance to
202
  // mNextState when the current action completes.
203
  State mNextState;
204
205
  struct VInt {
206
0
    VInt() : mValue(0), mLength(0) {}
207
    uint64_t mValue;
208
    uint64_t mLength;
209
  };
210
211
  struct EBMLElement {
212
0
    uint64_t Length() { return mID.mLength + mSize.mLength; }
213
    VInt mID;
214
    VInt mSize;
215
  };
216
217
  EBMLElement mElement;
218
219
  VInt mVInt;
220
221
  bool mVIntRaw;
222
223
  // EBML start offset. This indicates the start of the last init segment
224
  // parsed. Will only be set if an EBML element has been found.
225
  int64_t mLastInitStartOffset;
226
227
  // Current match position within CLUSTER_SYNC_ID.  Used to find sync
228
  // within arbitrary data.
229
  uint32_t mClusterSyncPos;
230
231
  // Number of bytes of mVInt left to read.  mVInt is complete once this
232
  // reaches 0.
233
  uint32_t mVIntLeft;
234
235
  // Size of the block currently being parsed.  Any unused data within the
236
  // block is skipped once the block timecode has been parsed.
237
  uint64_t mBlockSize;
238
239
  // Cluster-level timecode.
240
  uint64_t mClusterTimecode;
241
242
  // Start offset of the cluster currently being parsed.  Used as the sync
243
  // point offset for the offset-to-time mapping as each block timecode is
244
  // been parsed. -1 if unknown.
245
  int64_t mClusterOffset;
246
247
  // End offset of the cluster currently being parsed. -1 if unknown.
248
  int64_t mClusterEndOffset;
249
250
  // Start offset of the block currently being parsed.  Used as the byte
251
  // offset for the offset-to-time mapping once the block timecode has been
252
  // parsed.
253
  int64_t mBlockOffset;
254
255
  // Block-level timecode.  This is summed with mClusterTimecode to produce
256
  // an absolute timecode for the offset-to-time mapping.
257
  int16_t mBlockTimecode;
258
259
  // Number of bytes of mBlockTimecode left to read.
260
  uint32_t mBlockTimecodeLength;
261
262
  // Count of bytes left to skip before resuming parse at mNextState.
263
  // Mostly used to skip block payload data after reading a block timecode.
264
  uint32_t mSkipBytes;
265
266
  // Timecode scale read from the segment info and used to scale absolute
267
  // timecodes.
268
  uint32_t mTimecodeScale;
269
270
  // True if we read the timecode scale from the segment info or have
271
  // confirmed that the default value is to be used.
272
  bool mGotTimecodeScale;
273
274
  // True if we've read the cluster time code.
275
  bool mGotClusterTimecode;
276
};
277
278
class WebMBufferedState final
279
{
280
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebMBufferedState)
281
282
public:
283
  WebMBufferedState()
284
    : mReentrantMonitor("WebMBufferedState")
285
    , mLastBlockOffset(-1)
286
  {
287
    MOZ_COUNT_CTOR(WebMBufferedState);
288
  }
289
290
  void NotifyDataArrived(const unsigned char* aBuffer, uint32_t aLength, int64_t aOffset);
291
  void Reset();
292
  void UpdateIndex(const MediaByteRangeSet& aRanges, MediaResource* aResource);
293
  bool CalculateBufferedForRange(int64_t aStartOffset, int64_t aEndOffset,
294
                                 uint64_t* aStartTime, uint64_t* aEndTime);
295
296
  // Returns true if mTimeMapping is not empty and sets aOffset to
297
  // the latest offset for which decoding can resume without data
298
  // dependencies to arrive at aTime. aTime will be clamped to the start
299
  // of mTimeMapping if it is earlier than the first element, and to the end
300
  // if later than the last
301
  bool GetOffsetForTime(uint64_t aTime, int64_t* aOffset);
302
303
  // Returns end offset of init segment or -1 if none found.
304
  int64_t GetInitEndOffset();
305
  // Returns the end offset of the last complete block or -1 if none found.
306
  int64_t GetLastBlockOffset();
307
308
  // Returns start time
309
  bool GetStartTime(uint64_t *aTime);
310
311
  // Returns keyframe for time
312
  bool GetNextKeyframeTime(uint64_t aTime, uint64_t* aKeyframeTime);
313
314
private:
315
  // Private destructor, to discourage deletion outside of Release():
316
  ~WebMBufferedState() {
317
    MOZ_COUNT_DTOR(WebMBufferedState);
318
  }
319
320
  // Synchronizes access to the mTimeMapping array and mLastBlockOffset.
321
  ReentrantMonitor mReentrantMonitor;
322
323
  // Sorted (by offset) map of data offsets to timecodes.  Populated
324
  // on the main thread as data is received and parsed by WebMBufferedParsers.
325
  nsTArray<WebMTimeDataOffset> mTimeMapping;
326
  // The last complete block parsed. -1 if not set.
327
  int64_t mLastBlockOffset;
328
329
  // Sorted (by offset) live parser instances.  Main thread only.
330
  nsTArray<WebMBufferedParser> mRangeParsers;
331
};
332
333
} // namespace mozilla
334
335
#endif