/src/mozilla-central/dom/media/webm/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 | 0 | {} |
29 | | |
30 | 0 | bool operator==(int64_t aEndOffset) const { |
31 | 0 | return mEndOffset == aEndOffset; |
32 | 0 | } |
33 | | |
34 | 0 | bool operator!=(int64_t aEndOffset) const { |
35 | 0 | return mEndOffset != aEndOffset; |
36 | 0 | } |
37 | | |
38 | 0 | bool operator<(int64_t aEndOffset) const { |
39 | 0 | return mEndOffset < aEndOffset; |
40 | 0 | } |
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 | | { |
80 | | if (mStartOffset != 0) { |
81 | | mState = FIND_CLUSTER_SYNC; |
82 | | } |
83 | | } |
84 | | |
85 | | uint32_t GetTimecodeScale() { |
86 | | MOZ_ASSERT(mGotTimecodeScale); |
87 | | return mTimecodeScale; |
88 | | } |
89 | | |
90 | | // Use this function when we would only feed media segment for the parser. |
91 | | void AppendMediaSegmentOnly() |
92 | | { |
93 | | mGotTimecodeScale = true; |
94 | | } |
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 | 0 | void SetTimecodeScale(uint32_t aTimecodeScale) { |
99 | 0 | mTimecodeScale = aTimecodeScale; |
100 | 0 | mGotTimecodeScale = true; |
101 | 0 | } |
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 | 0 | bool operator==(int64_t aOffset) const { |
112 | 0 | return mCurrentOffset == aOffset; |
113 | 0 | } |
114 | | |
115 | 0 | bool operator<(int64_t aOffset) const { |
116 | 0 | return mCurrentOffset < aOffset; |
117 | 0 | } |
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 | | VInt() : mValue(0), mLength(0) {} |
207 | | uint64_t mValue; |
208 | | uint64_t mLength; |
209 | | }; |
210 | | |
211 | | struct EBMLElement { |
212 | | 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 | 0 | { |
287 | 0 | MOZ_COUNT_CTOR(WebMBufferedState); |
288 | 0 | } |
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 | 0 | ~WebMBufferedState() { |
317 | 0 | MOZ_COUNT_DTOR(WebMBufferedState); |
318 | 0 | } |
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 |