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 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla code.
17 : *
18 : * The Initial Developer of the Original Code is the Mozilla Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2010
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Matthew Gregan <kinetik@flim.org>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 : #if !defined(nsWebMBufferedParser_h_)
39 : #define nsWebMBufferedParser_h_
40 :
41 : #include "nsISupportsImpl.h"
42 : #include "nsTArray.h"
43 : #include "mozilla/ReentrantMonitor.h"
44 :
45 : class nsTimeRanges;
46 : using mozilla::ReentrantMonitor;
47 :
48 : // Stores a stream byte offset and the scaled timecode of the block at
49 : // that offset. The timecode must be scaled by the stream's timecode
50 : // scale before use.
51 : struct nsWebMTimeDataOffset
52 0 : {
53 0 : nsWebMTimeDataOffset(PRInt64 aOffset, PRUint64 aTimecode)
54 0 : : mOffset(aOffset), mTimecode(aTimecode)
55 0 : {}
56 :
57 0 : bool operator==(PRInt64 aOffset) const {
58 0 : return mOffset == aOffset;
59 : }
60 :
61 0 : bool operator<(PRInt64 aOffset) const {
62 0 : return mOffset < aOffset;
63 : }
64 :
65 : PRInt64 mOffset;
66 : PRUint64 mTimecode;
67 : };
68 :
69 : // A simple WebM parser that produces data offset to timecode pairs as it
70 : // consumes blocks. A new parser is created for each distinct range of data
71 : // received and begins parsing from the first WebM cluster within that
72 : // range. Old parsers are destroyed when their range merges with a later
73 : // parser or an already parsed range. The parser may start at any position
74 : // within the stream.
75 : struct nsWebMBufferedParser
76 0 : {
77 0 : nsWebMBufferedParser(PRInt64 aOffset)
78 0 : : mStartOffset(aOffset), mCurrentOffset(aOffset), mState(CLUSTER_SYNC), mClusterIDPos(0)
79 0 : {}
80 :
81 : // Steps the parser through aLength bytes of data. Always consumes
82 : // aLength bytes. Updates mCurrentOffset before returning. Acquires
83 : // aReentrantMonitor before using aMapping.
84 : void Append(const unsigned char* aBuffer, PRUint32 aLength,
85 : nsTArray<nsWebMTimeDataOffset>& aMapping,
86 : ReentrantMonitor& aReentrantMonitor);
87 :
88 0 : bool operator==(PRInt64 aOffset) const {
89 0 : return mCurrentOffset == aOffset;
90 : }
91 :
92 0 : bool operator<(PRInt64 aOffset) const {
93 0 : return mCurrentOffset < aOffset;
94 : }
95 :
96 : // The offset at which this parser started parsing. Used to merge
97 : // adjacent parsers, in which case the later parser adopts the earlier
98 : // parser's mStartOffset.
99 : PRInt64 mStartOffset;
100 :
101 : // Current offset with the stream. Updated in chunks as Append() consumes
102 : // data.
103 : PRInt64 mCurrentOffset;
104 :
105 : private:
106 : enum State {
107 : // Parser start state. Scans forward searching for stream sync by
108 : // matching CLUSTER_ID with the curernt byte. The match state is stored
109 : // in mClusterIDPos. Once this reaches sizeof(CLUSTER_ID), stream may
110 : // have sync. The parser then advances to read the cluster size and
111 : // timecode.
112 : CLUSTER_SYNC,
113 :
114 : /*
115 : The the parser states below assume that CLUSTER_SYNC has found a valid
116 : sync point within the data. If parsing fails in these states, the
117 : parser returns to CLUSTER_SYNC to find a new sync point.
118 : */
119 :
120 : // Read the first byte of a variable length integer. The first byte
121 : // encodes both the variable integer's length and part of the value.
122 : // The value read so far is stored in mVInt and the length is stored in
123 : // mVIntLength. The number of bytes left to read is stored in
124 : // mVIntLeft.
125 : READ_VINT,
126 :
127 : // Reads the remaining mVIntLeft bytes into mVInt.
128 : READ_VINT_REST,
129 :
130 : // Check that the next element is TIMECODE_ID. The cluster timecode is
131 : // required to be the first element in a cluster. Advances to READ_VINT
132 : // to read the timecode's length into mVInt.
133 : TIMECODE_SYNC,
134 :
135 : // mVInt holds the length of the variable length unsigned integer
136 : // containing the cluster timecode. Read mVInt bytes into
137 : // mClusterTimecode.
138 : READ_CLUSTER_TIMECODE,
139 :
140 : // Skips elements with a cluster until BLOCKGROUP_ID or SIMPLEBLOCK_ID
141 : // is found. If BLOCKGROUP_ID is found, the parser returns to
142 : // ANY_BLOCK_ID searching for a BLOCK_ID. Once a block or simpleblock
143 : // is found, the current data offset is stored in mBlockOffset. If the
144 : // current byte is the beginning of a four byte variant integer, it
145 : // indicates the parser has reached a top-level element ID and the
146 : // parser returns to CLUSTER_SYNC.
147 : ANY_BLOCK_SYNC,
148 :
149 : // Start reading a block. Blocks and simpleblocks are parsed the same
150 : // way as the initial layouts are identical. mBlockSize is initialized
151 : // from mVInt (holding the element size), and mBlockTimecode(Length) is
152 : // initialized for parsing.
153 : READ_BLOCK,
154 :
155 : // Reads mBlockTimecodeLength bytes of data into mBlockTimecode. When
156 : // mBlockTimecodeLength reaches 0, the timecode has been read. The sum
157 : // of mClusterTimecode and mBlockTimecode is stored as a pair with
158 : // mBlockOffset into the offset-to-time map.
159 : READ_BLOCK_TIMECODE,
160 :
161 : // Skip mSkipBytes of data before resuming parse at mNextState.
162 : SKIP_DATA,
163 :
164 : // Skip the content of an element. mVInt holds the element length.
165 : SKIP_ELEMENT
166 : };
167 :
168 : // Current state machine action.
169 : State mState;
170 :
171 : // Next state machine action. SKIP_DATA and READ_VINT_REST advance to
172 : // mNextState when the current action completes.
173 : State mNextState;
174 :
175 : // Match position within CLUSTER_ID. Used to find sync within arbitrary
176 : // data.
177 : PRUint32 mClusterIDPos;
178 :
179 : // Variable length integer read from data.
180 : PRUint64 mVInt;
181 :
182 : // Encoding length of mVInt. This is the total number of bytes used to
183 : // encoding mVInt's value.
184 : PRUint32 mVIntLength;
185 :
186 : // Number of bytes of mVInt left to read. mVInt is complete once this
187 : // reaches 0.
188 : PRUint32 mVIntLeft;
189 :
190 : // Size of the block currently being parsed. Any unused data within the
191 : // block is skipped once the block timecode has been parsed.
192 : PRUint64 mBlockSize;
193 :
194 : // Cluster-level timecode.
195 : PRUint64 mClusterTimecode;
196 :
197 : // Start offset of the block currently being parsed. Used as the byte
198 : // offset for the offset-to-time mapping once the block timecode has been
199 : // parsed.
200 : PRInt64 mBlockOffset;
201 :
202 : // Block-level timecode. This is summed with mClusterTimecode to produce
203 : // an absolute timecode for the offset-to-time mapping.
204 : PRInt16 mBlockTimecode;
205 :
206 : // Number of bytes of mBlockTimecode left to read.
207 : PRUint32 mBlockTimecodeLength;
208 :
209 : // Count of bytes left to skip before resuming parse at mNextState.
210 : // Mostly used to skip block payload data after reading a block timecode.
211 : PRUint32 mSkipBytes;
212 : };
213 :
214 : class nsWebMBufferedState
215 : {
216 0 : NS_INLINE_DECL_REFCOUNTING(nsWebMBufferedState)
217 :
218 : public:
219 0 : nsWebMBufferedState() : mReentrantMonitor("nsWebMBufferedState") {
220 0 : MOZ_COUNT_CTOR(nsWebMBufferedState);
221 0 : }
222 :
223 0 : ~nsWebMBufferedState() {
224 0 : MOZ_COUNT_DTOR(nsWebMBufferedState);
225 0 : }
226 :
227 : void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRInt64 aOffset);
228 : void CalculateBufferedForRange(nsTimeRanges* aBuffered,
229 : PRInt64 aStartOffset, PRInt64 aEndOffset,
230 : PRUint64 aTimecodeScale,
231 : PRInt64 aStartTimeOffsetNS);
232 :
233 : private:
234 : // Synchronizes access to the mTimeMapping array.
235 : ReentrantMonitor mReentrantMonitor;
236 :
237 : // Sorted (by offset) map of data offsets to timecodes. Populated
238 : // on the main thread as data is received and parsed by nsWebMBufferedParsers.
239 : nsTArray<nsWebMTimeDataOffset> mTimeMapping;
240 :
241 : // Sorted (by offset) live parser instances. Main thread only.
242 : nsTArray<nsWebMBufferedParser> mRangeParsers;
243 : };
244 :
245 : #endif
|