WebMBufferedParser.h (10766B)
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 "MediaResource.h" 10 # include "MediaResult.h" 11 # include "mozilla/Mutex.h" 12 # include "nsISupportsImpl.h" 13 # include "nsTArray.h" 14 15 namespace mozilla { 16 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) {} 28 29 bool operator==(int64_t aEndOffset) const { return mEndOffset == aEndOffset; } 30 31 bool operator!=(int64_t aEndOffset) const { return mEndOffset != aEndOffset; } 32 33 bool operator<(int64_t aEndOffset) const { return mEndOffset < aEndOffset; } 34 35 int64_t mEndOffset; 36 int64_t mInitOffset; 37 int64_t mSyncOffset; 38 int64_t mClusterEndOffset; 39 // In nanoseconds 40 uint64_t mTimecode; 41 }; 42 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); 51 52 uint32_t GetTimecodeScale() { 53 MOZ_ASSERT(mGotTimecodeScale); 54 return mTimecodeScale; 55 } 56 57 // Use this function when we would only feed media segment for the parser. 58 void AppendMediaSegmentOnly() { mGotTimecodeScale = true; } 59 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); 63 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); 69 70 bool operator==(int64_t aOffset) const { return mCurrentOffset == aOffset; } 71 72 bool operator<(int64_t aOffset) const { return mCurrentOffset < aOffset; } 73 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); 79 80 // Return the Cluster offset, return -1 if we can't find the Cluster. 81 int64_t GetClusterOffset() const; 82 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; 87 88 // Current offset within the stream. Updated in chunks as Append() consumes 89 // data. 90 int64_t mCurrentOffset; 91 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; 95 96 // End offset of the last block parsed. 97 // Will only be set if a complete block has been parsed. 98 int64_t mBlockEndOffset; 99 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, 105 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, 109 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, 113 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, 118 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, 125 126 // Reads the remaining mVIntLeft bytes into mVInt.mValue. 127 READ_VINT_REST, 128 129 // mVInt holds the parsed timecode scale, store it in mTimecodeScale, 130 // then return READ_ELEMENT_ID. 131 READ_TIMECODESCALE, 132 133 // mVInt holds the parsed cluster timecode, store it in 134 // mClusterTimecode, then return to READ_ELEMENT_ID. 135 READ_CLUSTER_TIMECODE, 136 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, 144 145 // mVInt holds the parsed EBMLMaxIdLength, store it in mEBMLMaxIdLength, 146 // then return to READ_ELEMENT_ID. 147 READ_EBML_MAX_ID_LENGTH, 148 149 // mVInt holds the parsed EBMLMaxSizeLength, store it in mEBMLMaxSizeLength, 150 // then return to READ_ELEMENT_ID. 151 READ_EBML_MAX_SIZE_LENGTH, 152 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, 157 158 // Skip mSkipBytes of data before resuming parse at mNextState. 159 SKIP_DATA, 160 }; 161 162 // Current state machine action. 163 State mState; 164 165 // Next state machine action. SKIP_DATA and READ_VINT_REST advance to 166 // mNextState when the current action completes. 167 State mNextState; 168 169 struct VInt { 170 VInt() : mValue(0), mLength(0) {} 171 uint64_t mValue; 172 uint64_t mLength; 173 }; 174 175 struct EBMLElement { 176 uint64_t Length() { return mID.mLength + mSize.mLength; } 177 VInt mID; 178 VInt mSize; 179 }; 180 181 EBMLElement mElement; 182 183 VInt mVInt; 184 185 bool mVIntRaw; 186 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; 190 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; 194 195 // EBML max id length is the max number of bytes allowed for an element id 196 // vint. 197 uint8_t mEBMLMaxIdLength; 198 199 // EBML max size length is the max number of bytes allowed for an element size 200 // vint. 201 uint8_t mEBMLMaxSizeLength; 202 203 // Current match position within CLUSTER_SYNC_ID. Used to find sync 204 // within arbitrary data. 205 uint32_t mClusterSyncPos; 206 207 // Number of bytes of mVInt left to read. mVInt is complete once this 208 // reaches 0. 209 uint32_t mVIntLeft; 210 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; 214 215 // Cluster-level timecode. 216 uint64_t mClusterTimecode; 217 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; 222 223 // End offset of the cluster currently being parsed. -1 if unknown. 224 int64_t mClusterEndOffset; 225 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; 230 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; 234 235 // Number of bytes of mBlockTimecode left to read. 236 uint32_t mBlockTimecodeLength; 237 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; 241 242 // Timecode scale read from the segment info and used to scale absolute 243 // timecodes. 244 uint32_t mTimecodeScale; 245 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; 249 250 // True if we've read the cluster time code. 251 bool mGotClusterTimecode; 252 }; 253 254 class WebMBufferedState final { 255 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebMBufferedState) 256 257 public: 258 WebMBufferedState() : mMutex("WebMBufferedState") { 259 MOZ_COUNT_CTOR(WebMBufferedState); 260 } 261 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); 268 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); 275 276 // Returns end offset of init segment or -1 if none found. 277 int64_t GetInitEndOffset(); 278 279 // Returns start time 280 bool GetStartTime(uint64_t* aTime); 281 282 // Returns keyframe for time 283 bool GetNextKeyframeTime(uint64_t aTime, uint64_t* aKeyframeTime); 284 285 private: 286 // Private destructor, to discourage deletion outside of Release(): 287 MOZ_COUNTED_DTOR(WebMBufferedState) 288 289 // Synchronizes access to the mTimeMapping array. 290 Mutex mMutex; 291 292 // Sorted (by offset) map of data offsets to timecodes. Populated 293 // on the main thread as data is received and parsed by WebMBufferedParsers. 294 nsTArray<WebMTimeDataOffset> mTimeMapping MOZ_GUARDED_BY(mMutex); 295 296 // Sorted (by offset) live parser instances. Main thread only. 297 nsTArray<WebMBufferedParser> mRangeParsers; 298 }; 299 300 } // namespace mozilla 301 302 #endif