Backed out 7 changesets (bug 1942424) for causing frequent crashes. a=backout
[gecko.git] / dom / media / webm / WebMBufferedParser.h
blobeda31e4c1b551078ec1ddf152e8441665dd65ef0
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_
9 # include "nsISupportsImpl.h"
10 # include "nsTArray.h"
11 # include "mozilla/Mutex.h"
12 # include "MediaResource.h"
13 # include "MediaResult.h"
15 namespace mozilla {
17 // Stores a stream byte offset and the scaled timecode of the block at
18 // that offset.
19 struct WebMTimeDataOffset {
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) {}
29 bool operator==(int64_t aEndOffset) const { return mEndOffset == aEndOffset; }
31 bool operator!=(int64_t aEndOffset) const { return mEndOffset != aEndOffset; }
33 bool operator<(int64_t aEndOffset) const { return mEndOffset < aEndOffset; }
35 int64_t mEndOffset;
36 int64_t mInitOffset;
37 int64_t mSyncOffset;
38 int64_t mClusterEndOffset;
39 // In nanoseconds
40 uint64_t mTimecode;
43 // A simple WebM parser that produces data offset to timecode pairs as it
44 // consumes blocks. A new parser is created for each distinct range of data
45 // received and begins parsing from the first WebM cluster within that
46 // range. Old parsers are destroyed when their range merges with a later
47 // parser or an already parsed range. The parser may start at any position
48 // within the stream.
49 struct WebMBufferedParser {
50 explicit WebMBufferedParser(int64_t aOffset);
52 uint32_t GetTimecodeScale() {
53 MOZ_ASSERT(mGotTimecodeScale);
54 return mTimecodeScale;
57 // Use this function when we would only feed media segment for the parser.
58 void AppendMediaSegmentOnly() { mGotTimecodeScale = true; }
60 // If this parser is not expected to parse a segment info, it must be told
61 // the appropriate timecode scale to use from elsewhere.
62 void SetTimecodeScale(uint32_t aTimecodeScale);
64 // Steps the parser through aLength bytes of data. Always consumes
65 // aLength bytes. Updates mCurrentOffset before returning.
66 // Returns false if an error was encountered.
67 MediaResult Append(const unsigned char* aBuffer, uint32_t aLength,
68 nsTArray<WebMTimeDataOffset>& aMapping);
70 bool operator==(int64_t aOffset) const { return mCurrentOffset == aOffset; }
72 bool operator<(int64_t aOffset) const { return mCurrentOffset < aOffset; }
74 // Returns the start offset of the init (EBML) or media segment (Cluster)
75 // following the aOffset position. If none were found, returns
76 // mBlockEndOffset. This allows to determine the end of the interval containg
77 // aOffset.
78 int64_t EndSegmentOffset(int64_t aOffset);
80 // Return the Cluster offset, return -1 if we can't find the Cluster.
81 int64_t GetClusterOffset() const;
83 // The offset at which this parser started parsing. Used to merge
84 // adjacent parsers, in which case the later parser adopts the earlier
85 // parser's mStartOffset.
86 int64_t mStartOffset;
88 // Current offset within the stream. Updated in chunks as Append() consumes
89 // data.
90 int64_t mCurrentOffset;
92 // Tracks element's end offset. This indicates the end of the first init
93 // segment. Will only be set if a Segment Information has been found.
94 int64_t mInitEndOffset;
96 // End offset of the last block parsed.
97 // Will only be set if a complete block has been parsed.
98 int64_t mBlockEndOffset;
100 private:
101 enum State {
102 // Parser start state. Expects to begin at a valid EBML element. Move
103 // to READ_VINT with mVIntRaw true, then return to READ_ELEMENT_SIZE.
104 READ_ELEMENT_ID,
106 // Store element ID read into mVInt into mElement.mID. Move to
107 // READ_VINT with mVIntRaw false, then return to PARSE_ELEMENT.
108 READ_ELEMENT_SIZE,
110 // Parser start state for parsers started at an arbitrary offset. Scans
111 // forward for the first cluster, then move to READ_ELEMENT_ID.
112 FIND_CLUSTER_SYNC,
114 // Simplistic core of the parser. Does not pay attention to nesting of
115 // elements. Checks mElement for an element ID of interest, then moves
116 // to the next state as determined by the element ID.
117 PARSE_ELEMENT,
119 // Read the first byte of a variable length integer. The first byte
120 // encodes both the variable integer's length and part of the value.
121 // The value read so far is stored in mVInt.mValue and the length is
122 // stored in mVInt.mLength. The number of bytes left to read is stored
123 // in mVIntLeft.
124 READ_VINT,
126 // Reads the remaining mVIntLeft bytes into mVInt.mValue.
127 READ_VINT_REST,
129 // mVInt holds the parsed timecode scale, store it in mTimecodeScale,
130 // then return READ_ELEMENT_ID.
131 READ_TIMECODESCALE,
133 // mVInt holds the parsed cluster timecode, store it in
134 // mClusterTimecode, then return to READ_ELEMENT_ID.
135 READ_CLUSTER_TIMECODE,
137 // mBlockTimecodeLength holds the remaining length of the block timecode
138 // left to read. Read each byte of the timecode into mBlockTimecode.
139 // Once complete, calculate the scaled timecode from the cluster
140 // timecode, block timecode, and timecode scale, and insert a
141 // WebMTimeDataOffset entry into aMapping if one is not already present
142 // for this offset.
143 READ_BLOCK_TIMECODE,
145 // mVInt holds the parsed EBMLMaxIdLength, store it in mEBMLMaxIdLength,
146 // then return to READ_ELEMENT_ID.
147 READ_EBML_MAX_ID_LENGTH,
149 // mVInt holds the parsed EBMLMaxSizeLength, store it in mEBMLMaxSizeLength,
150 // then return to READ_ELEMENT_ID.
151 READ_EBML_MAX_SIZE_LENGTH,
153 // Will skip the current tracks element and set mInitEndOffset if an init
154 // segment has been found.
155 // Currently, only assumes it's the end of the tracks element.
156 CHECK_INIT_FOUND,
158 // Skip mSkipBytes of data before resuming parse at mNextState.
159 SKIP_DATA,
162 // Current state machine action.
163 State mState;
165 // Next state machine action. SKIP_DATA and READ_VINT_REST advance to
166 // mNextState when the current action completes.
167 State mNextState;
169 struct VInt {
170 VInt() : mValue(0), mLength(0) {}
171 uint64_t mValue;
172 uint64_t mLength;
175 struct EBMLElement {
176 uint64_t Length() { return mID.mLength + mSize.mLength; }
177 VInt mID;
178 VInt mSize;
181 EBMLElement mElement;
183 VInt mVInt;
185 bool mVIntRaw;
187 // EBML start offset. This indicates the start of the last init segment
188 // parsed. Will only be set if an EBML element has been found.
189 int64_t mLastInitStartOffset;
191 // EBML element size. This indicates the size of the body of the last init
192 // segment parsed. Will only be set if an EBML element has been found.
193 uint32_t mLastInitSize;
195 // EBML max id length is the max number of bytes allowed for an element id
196 // vint.
197 uint8_t mEBMLMaxIdLength;
199 // EBML max size length is the max number of bytes allowed for an element size
200 // vint.
201 uint8_t mEBMLMaxSizeLength;
203 // Current match position within CLUSTER_SYNC_ID. Used to find sync
204 // within arbitrary data.
205 uint32_t mClusterSyncPos;
207 // Number of bytes of mVInt left to read. mVInt is complete once this
208 // reaches 0.
209 uint32_t mVIntLeft;
211 // Size of the block currently being parsed. Any unused data within the
212 // block is skipped once the block timecode has been parsed.
213 uint64_t mBlockSize;
215 // Cluster-level timecode.
216 uint64_t mClusterTimecode;
218 // Start offset of the cluster currently being parsed. Used as the sync
219 // point offset for the offset-to-time mapping as each block timecode is
220 // been parsed. -1 if unknown.
221 int64_t mClusterOffset;
223 // End offset of the cluster currently being parsed. -1 if unknown.
224 int64_t mClusterEndOffset;
226 // Start offset of the block currently being parsed. Used as the byte
227 // offset for the offset-to-time mapping once the block timecode has been
228 // parsed.
229 int64_t mBlockOffset;
231 // Block-level timecode. This is summed with mClusterTimecode to produce
232 // an absolute timecode for the offset-to-time mapping.
233 int16_t mBlockTimecode;
235 // Number of bytes of mBlockTimecode left to read.
236 uint32_t mBlockTimecodeLength;
238 // Count of bytes left to skip before resuming parse at mNextState.
239 // Mostly used to skip block payload data after reading a block timecode.
240 uint32_t mSkipBytes;
242 // Timecode scale read from the segment info and used to scale absolute
243 // timecodes.
244 uint32_t mTimecodeScale;
246 // True if we read the timecode scale from the segment info or have
247 // confirmed that the default value is to be used.
248 bool mGotTimecodeScale;
250 // True if we've read the cluster time code.
251 bool mGotClusterTimecode;
254 class WebMBufferedState final {
255 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebMBufferedState)
257 public:
258 WebMBufferedState() : mMutex("WebMBufferedState"), mLastBlockOffset(-1) {
259 MOZ_COUNT_CTOR(WebMBufferedState);
262 void NotifyDataArrived(const unsigned char* aBuffer, uint32_t aLength,
263 int64_t aOffset);
264 void Reset();
265 void UpdateIndex(const MediaByteRangeSet& aRanges, MediaResource* aResource);
266 bool CalculateBufferedForRange(int64_t aStartOffset, int64_t aEndOffset,
267 uint64_t* aStartTime, uint64_t* aEndTime);
269 // Returns true if mTimeMapping is not empty and sets aOffset to
270 // the latest offset for which decoding can resume without data
271 // dependencies to arrive at aTime. aTime will be clamped to the start
272 // of mTimeMapping if it is earlier than the first element, and to the end
273 // if later than the last
274 bool GetOffsetForTime(uint64_t aTime, int64_t* aOffset);
276 // Returns end offset of init segment or -1 if none found.
277 int64_t GetInitEndOffset();
278 // Returns the end offset of the last complete block or -1 if none found.
279 int64_t GetLastBlockOffset();
281 // Returns start time
282 bool GetStartTime(uint64_t* aTime);
284 // Returns keyframe for time
285 bool GetNextKeyframeTime(uint64_t aTime, uint64_t* aKeyframeTime);
287 private:
288 // Private destructor, to discourage deletion outside of Release():
289 MOZ_COUNTED_DTOR(WebMBufferedState)
291 // Synchronizes access to the mTimeMapping array and mLastBlockOffset.
292 Mutex mMutex;
294 // Sorted (by offset) map of data offsets to timecodes. Populated
295 // on the main thread as data is received and parsed by WebMBufferedParsers.
296 nsTArray<WebMTimeDataOffset> mTimeMapping MOZ_GUARDED_BY(mMutex);
297 // The last complete block parsed. -1 if not set.
298 int64_t mLastBlockOffset MOZ_GUARDED_BY(mMutex);
300 // Sorted (by offset) live parser instances. Main thread only.
301 nsTArray<WebMBufferedParser> mRangeParsers;
304 } // namespace mozilla
306 #endif