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