WebMBufferedParser.cpp (24701B)
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 7 #include "WebMBufferedParser.h" 8 9 #include <algorithm> 10 11 #include "MediaDataDemuxer.h" 12 #include "mozilla/CheckedInt.h" 13 #include "nsThreadUtils.h" 14 15 #define WEBM_DEBUG(arg, ...) \ 16 MOZ_LOG(gMediaDemuxerLog, mozilla::LogLevel::Debug, \ 17 ("WebMBufferedParser(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) 18 19 namespace mozilla { 20 21 static uint32_t VIntLength(unsigned char aFirstByte, uint32_t* aMask) { 22 uint32_t count = 1; 23 uint32_t mask = 1 << 7; 24 while (count < 8) { 25 if ((aFirstByte & mask) != 0) { 26 break; 27 } 28 mask >>= 1; 29 count += 1; 30 } 31 if (aMask) { 32 *aMask = mask; 33 } 34 NS_ASSERTION(count >= 1 && count <= 8, "Insane VInt length."); 35 return count; 36 } 37 38 constexpr uint8_t EBML_MAX_ID_LENGTH_DEFAULT = 4; 39 constexpr uint8_t EBML_MAX_SIZE_LENGTH_DEFAULT = 8; 40 41 WebMBufferedParser::WebMBufferedParser(int64_t aOffset) 42 : mStartOffset(aOffset), 43 mCurrentOffset(aOffset), 44 mInitEndOffset(-1), 45 mBlockEndOffset(-1), 46 mState(READ_ELEMENT_ID), 47 mNextState(READ_ELEMENT_ID), 48 mVIntRaw(false), 49 mLastInitStartOffset(-1), 50 mLastInitSize(0), 51 mEBMLMaxIdLength(EBML_MAX_ID_LENGTH_DEFAULT), 52 mEBMLMaxSizeLength(EBML_MAX_SIZE_LENGTH_DEFAULT), 53 mClusterSyncPos(0), 54 mVIntLeft(0), 55 mBlockSize(0), 56 mClusterTimecode(0), 57 mClusterOffset(-1), 58 mClusterEndOffset(-1), 59 mBlockOffset(0), 60 mBlockTimecode(0), 61 mBlockTimecodeLength(0), 62 mSkipBytes(0), 63 mTimecodeScale(1000000), 64 mGotTimecodeScale(false), 65 mGotClusterTimecode(false) { 66 if (mStartOffset != 0) { 67 mState = FIND_CLUSTER_SYNC; 68 } 69 } 70 71 void WebMBufferedParser::SetTimecodeScale(uint32_t aTimecodeScale) { 72 mTimecodeScale = aTimecodeScale; 73 WEBM_DEBUG("%" PRIu32, mTimecodeScale); 74 mGotTimecodeScale = true; 75 } 76 77 MediaResult WebMBufferedParser::Append(const unsigned char* aBuffer, 78 uint32_t aLength, 79 nsTArray<WebMTimeDataOffset>& aMapping) { 80 static const uint32_t EBML_ID = 0x1a45dfa3; 81 static const uint32_t SEGMENT_ID = 0x18538067; 82 static const uint32_t SEGINFO_ID = 0x1549a966; 83 static const uint32_t TRACKS_ID = 0x1654AE6B; 84 static const uint32_t CLUSTER_ID = 0x1f43b675; 85 static const uint32_t TIMECODESCALE_ID = 0x2ad7b1; 86 static const unsigned char TIMECODE_ID = 0xe7; 87 static const unsigned char BLOCKGROUP_ID = 0xa0; 88 static const unsigned char BLOCK_ID = 0xa1; 89 static const unsigned char SIMPLEBLOCK_ID = 0xa3; 90 static const uint16_t EBML_MAX_ID_LENGTH_ID = 0x42f2; 91 static const uint16_t EBML_MAX_SIZE_LENGTH_ID = 0x42f3; 92 static const uint32_t BLOCK_TIMECODE_LENGTH = 2; 93 94 static const unsigned char CLUSTER_SYNC_ID[] = {0x1f, 0x43, 0xb6, 0x75}; 95 96 const unsigned char* p = aBuffer; 97 98 // Parse each byte in aBuffer one-by-one, producing timecodes and updating 99 // aMapping as we go. Parser pauses at end of stream (which may be at any 100 // point within the parse) and resumes parsing the next time Append is 101 // called with new data. 102 while (p < aBuffer + aLength) { 103 switch (mState) { 104 case READ_ELEMENT_ID: 105 mVIntRaw = true; 106 mState = READ_VINT; 107 mNextState = READ_ELEMENT_SIZE; 108 break; 109 case READ_ELEMENT_SIZE: 110 if (mVInt.mLength > mEBMLMaxIdLength) { 111 nsPrintfCString detail("Invalid element id of length %" PRIu64, 112 mVInt.mLength); 113 WEBM_DEBUG("%s", detail.get()); 114 return MediaResult(NS_ERROR_FAILURE, detail); 115 } 116 mVIntRaw = false; 117 mElement.mID = mVInt; 118 mState = READ_VINT; 119 mNextState = PARSE_ELEMENT; 120 break; 121 case FIND_CLUSTER_SYNC: 122 if (*p++ == CLUSTER_SYNC_ID[mClusterSyncPos]) { 123 mClusterSyncPos += 1; 124 } else { 125 mClusterSyncPos = 0; 126 } 127 if (mClusterSyncPos == sizeof(CLUSTER_SYNC_ID)) { 128 mVInt.mValue = CLUSTER_ID; 129 mVInt.mLength = sizeof(CLUSTER_SYNC_ID); 130 mState = READ_ELEMENT_SIZE; 131 } 132 break; 133 case PARSE_ELEMENT: 134 if (mVInt.mLength > mEBMLMaxSizeLength) { 135 nsPrintfCString detail("Invalid element size of length %" PRIu64, 136 mVInt.mLength); 137 WEBM_DEBUG("%s", detail.get()); 138 return MediaResult(NS_ERROR_FAILURE, detail); 139 } 140 mElement.mSize = mVInt; 141 switch (mElement.mID.mValue) { 142 case SEGMENT_ID: 143 mState = READ_ELEMENT_ID; 144 break; 145 case SEGINFO_ID: 146 mGotTimecodeScale = true; 147 mState = READ_ELEMENT_ID; 148 break; 149 case TIMECODE_ID: 150 mVInt = VInt(); 151 mVIntLeft = mElement.mSize.mValue; 152 mState = READ_VINT_REST; 153 mNextState = READ_CLUSTER_TIMECODE; 154 break; 155 case TIMECODESCALE_ID: 156 mVInt = VInt(); 157 mVIntLeft = mElement.mSize.mValue; 158 mState = READ_VINT_REST; 159 mNextState = READ_TIMECODESCALE; 160 break; 161 case CLUSTER_ID: 162 mClusterOffset = mCurrentOffset + (p - aBuffer) - 163 (mElement.mID.mLength + mElement.mSize.mLength); 164 // Handle "unknown" length; 165 if (mElement.mSize.mValue + 1 != 166 uint64_t(1) << (mElement.mSize.mLength * 7)) { 167 mClusterEndOffset = mClusterOffset + mElement.mID.mLength + 168 mElement.mSize.mLength + 169 mElement.mSize.mValue; 170 } else { 171 mClusterEndOffset = -1; 172 } 173 mGotClusterTimecode = false; 174 mState = READ_ELEMENT_ID; 175 break; 176 case BLOCKGROUP_ID: 177 mState = READ_ELEMENT_ID; 178 break; 179 case SIMPLEBLOCK_ID: 180 /* FALLTHROUGH */ 181 case BLOCK_ID: 182 if (!mGotClusterTimecode) { 183 WEBM_DEBUG( 184 "The Timecode element must appear before any Block or " 185 "SimpleBlock elements in a Cluster"); 186 return MediaResult( 187 NS_ERROR_FAILURE, 188 "The Timecode element must appear before any Block or " 189 "SimpleBlock elements in a Cluster"); 190 } 191 mBlockSize = mElement.mSize.mValue; 192 mBlockTimecode = 0; 193 mBlockTimecodeLength = BLOCK_TIMECODE_LENGTH; 194 mBlockOffset = mCurrentOffset + (p - aBuffer) - 195 (mElement.mID.mLength + mElement.mSize.mLength); 196 mState = READ_VINT; 197 mNextState = READ_BLOCK_TIMECODE; 198 break; 199 case TRACKS_ID: 200 mSkipBytes = mElement.mSize.mValue; 201 mState = CHECK_INIT_FOUND; 202 break; 203 case EBML_MAX_ID_LENGTH_ID: 204 case EBML_MAX_SIZE_LENGTH_ID: 205 if (int64_t currentOffset = mCurrentOffset + (p - aBuffer); 206 currentOffset < mLastInitStartOffset || 207 currentOffset >= mLastInitStartOffset + mLastInitSize) { 208 nsPrintfCString str("Unexpected %s outside init segment", 209 mElement.mID.mValue == EBML_MAX_ID_LENGTH_ID 210 ? "EBMLMaxIdLength" 211 : "EBMLMaxSizeLength"); 212 WEBM_DEBUG("%s", str.get()); 213 return MediaResult(NS_ERROR_FAILURE, str); 214 } 215 if (mElement.mSize.mValue > 8) { 216 // https://www.rfc-editor.org/rfc/rfc8794.html (EBML): 217 // An Unsigned Integer Element MUST declare a length from zero 218 // to eight octets. 219 nsPrintfCString str("Bad length of %s size", 220 mElement.mID.mValue == EBML_MAX_ID_LENGTH_ID 221 ? "EBMLMaxIdLength" 222 : "EBMLMaxSizeLength"); 223 WEBM_DEBUG("%s", str.get()); 224 return MediaResult(NS_ERROR_FAILURE, str); 225 } 226 mVInt = VInt(); 227 mVIntLeft = mElement.mSize.mValue; 228 mState = READ_VINT_REST; 229 mNextState = mElement.mID.mValue == EBML_MAX_ID_LENGTH_ID 230 ? READ_EBML_MAX_ID_LENGTH 231 : READ_EBML_MAX_SIZE_LENGTH; 232 break; 233 case EBML_ID: 234 mLastInitStartOffset = 235 mCurrentOffset + (p - aBuffer) - 236 (mElement.mID.mLength + mElement.mSize.mLength); 237 mLastInitSize = mElement.mSize.mValue; 238 mEBMLMaxIdLength = EBML_MAX_ID_LENGTH_DEFAULT; 239 mEBMLMaxSizeLength = EBML_MAX_SIZE_LENGTH_DEFAULT; 240 mState = READ_ELEMENT_ID; 241 break; 242 default: 243 mSkipBytes = mElement.mSize.mValue; 244 mState = SKIP_DATA; 245 mNextState = READ_ELEMENT_ID; 246 break; 247 } 248 break; 249 case READ_VINT: { 250 unsigned char c = *p++; 251 uint32_t mask; 252 mVInt.mLength = VIntLength(c, &mask); 253 mVIntLeft = mVInt.mLength - 1; 254 mVInt.mValue = mVIntRaw ? c : c & ~mask; 255 mState = READ_VINT_REST; 256 break; 257 } 258 case READ_VINT_REST: 259 if (mVIntLeft) { 260 mVInt.mValue <<= 8; 261 mVInt.mValue |= *p++; 262 mVIntLeft -= 1; 263 } else { 264 mState = mNextState; 265 } 266 break; 267 case READ_TIMECODESCALE: 268 if (!mGotTimecodeScale) { 269 WEBM_DEBUG("Should get the SegmentInfo first"); 270 return MediaResult(NS_ERROR_FAILURE, 271 "TimecodeScale appeared before SegmentInfo"); 272 } 273 mTimecodeScale = mVInt.mValue; 274 WEBM_DEBUG("READ_TIMECODESCALE %" PRIu32, mTimecodeScale); 275 mState = READ_ELEMENT_ID; 276 break; 277 case READ_CLUSTER_TIMECODE: 278 mClusterTimecode = mVInt.mValue; 279 mGotClusterTimecode = true; 280 mState = READ_ELEMENT_ID; 281 break; 282 case READ_BLOCK_TIMECODE: 283 if (mBlockTimecodeLength) { 284 mBlockTimecode <<= 8; 285 mBlockTimecode |= *p++; 286 mBlockTimecodeLength -= 1; 287 } else { 288 // It's possible we've parsed this data before, so avoid inserting 289 // duplicate WebMTimeDataOffset entries. 290 { 291 int64_t endOffset = mBlockOffset + mBlockSize + 292 mElement.mID.mLength + mElement.mSize.mLength; 293 uint32_t idx = aMapping.IndexOfFirstElementGt(endOffset); 294 if (idx == 0 || aMapping[idx - 1] != endOffset) { 295 // Don't insert invalid negative timecodes. 296 if (mBlockTimecode >= 0 || 297 mClusterTimecode >= uint16_t(abs(mBlockTimecode))) { 298 if (!mGotTimecodeScale) { 299 WEBM_DEBUG("Should get the TimecodeScale first"); 300 return MediaResult(NS_ERROR_FAILURE, 301 "Timecode appeared before SegmentInfo"); 302 } 303 uint64_t absTimecode = mClusterTimecode + mBlockTimecode; 304 absTimecode *= mTimecodeScale; 305 // Avoid creating an entry if the timecode is out of order 306 // (invalid according to the WebM specification) so that 307 // ordering invariants of aMapping are not violated. 308 if (idx == 0 || aMapping[idx - 1].mTimecode <= absTimecode || 309 (idx + 1 < aMapping.Length() && 310 aMapping[idx + 1].mTimecode >= absTimecode)) { 311 WebMTimeDataOffset entry(endOffset, absTimecode, 312 mLastInitStartOffset, mClusterOffset, 313 mClusterEndOffset); 314 aMapping.InsertElementAt(idx, entry); 315 } else { 316 WEBM_DEBUG("Out of order timecode %" PRIu64 317 " in Cluster at %" PRId64 " ignored", 318 absTimecode, mClusterOffset); 319 } 320 } 321 } 322 } 323 324 // Skip rest of block header and the block's payload. 325 mBlockSize -= mVInt.mLength; 326 mBlockSize -= BLOCK_TIMECODE_LENGTH; 327 mSkipBytes = uint32_t(mBlockSize); 328 mState = SKIP_DATA; 329 mNextState = READ_ELEMENT_ID; 330 } 331 break; 332 case READ_EBML_MAX_ID_LENGTH: 333 if (mElement.mSize.mLength == 0) { 334 // https://www.rfc-editor.org/rfc/rfc8794.html (EBML): 335 // If an Empty Element has a default value declared, then the EBML 336 // Reader MUST interpret the value of the Empty Element as the 337 // default value. 338 mVInt.mValue = EBML_MAX_ID_LENGTH_DEFAULT; 339 } 340 if (mVInt.mValue < 4 || mVInt.mValue > 5) { 341 // https://www.ietf.org/archive/id/draft-ietf-cellar-matroska-13.html 342 // (Matroska): 343 // The EBMLMaxIDLength of the EBML Header MUST be "4". 344 // 345 // Also Matroska: 346 // Element IDs are encoded using the VINT mechanism described in 347 // Section 4 of [RFC8794] and can be between one and five octets 348 // long. Five-octet-long Element IDs are possible only if declared 349 // in the EBML header. 350 nsPrintfCString detail("Invalid EMBLMaxIdLength %" PRIu64, 351 mVInt.mValue); 352 WEBM_DEBUG("%s", detail.get()); 353 return MediaResult(NS_ERROR_FAILURE, detail); 354 } 355 mEBMLMaxIdLength = mVInt.mValue; 356 mState = READ_ELEMENT_ID; 357 break; 358 case READ_EBML_MAX_SIZE_LENGTH: 359 if (mElement.mSize.mLength == 0) { 360 // https://www.rfc-editor.org/rfc/rfc8794.html (EBML): 361 // If an Empty Element has a default value declared, then the EBML 362 // Reader MUST interpret the value of the Empty Element as the 363 // default value. 364 mVInt.mValue = EBML_MAX_SIZE_LENGTH_DEFAULT; 365 } 366 if (mVInt.mValue < 1 || mVInt.mValue > 8) { 367 // https://www.ietf.org/archive/id/draft-ietf-cellar-matroska-13.html 368 // (Matroska): 369 // The EBMLMaxSizeLength of the EBML Header MUST be between "1" and 370 // "8" inclusive. 371 nsPrintfCString detail("Invalid EMBLMaxSizeLength %" PRIu64, 372 mVInt.mValue); 373 WEBM_DEBUG("%s", detail.get()); 374 return MediaResult(NS_ERROR_FAILURE, detail); 375 } 376 mEBMLMaxSizeLength = mVInt.mValue; 377 mState = READ_ELEMENT_ID; 378 break; 379 case SKIP_DATA: 380 if (mSkipBytes) { 381 uint32_t left = aLength - (p - aBuffer); 382 left = std::min(left, mSkipBytes); 383 p += left; 384 mSkipBytes -= left; 385 } 386 if (!mSkipBytes) { 387 mBlockEndOffset = mCurrentOffset + (p - aBuffer); 388 mState = mNextState; 389 } 390 break; 391 case CHECK_INIT_FOUND: 392 if (mSkipBytes) { 393 uint32_t left = aLength - (p - aBuffer); 394 left = std::min(left, mSkipBytes); 395 p += left; 396 mSkipBytes -= left; 397 } 398 if (!mSkipBytes) { 399 if (mInitEndOffset < 0) { 400 mInitEndOffset = mCurrentOffset + (p - aBuffer); 401 mBlockEndOffset = mCurrentOffset + (p - aBuffer); 402 } 403 mState = READ_ELEMENT_ID; 404 } 405 break; 406 } 407 } 408 409 NS_ASSERTION(p == aBuffer + aLength, "Must have parsed to end of data."); 410 mCurrentOffset += aLength; 411 412 return NS_OK; 413 } 414 415 int64_t WebMBufferedParser::EndSegmentOffset(int64_t aOffset) { 416 if (mLastInitStartOffset > aOffset || mClusterOffset > aOffset) { 417 return std::min( 418 mLastInitStartOffset >= 0 ? mLastInitStartOffset : INT64_MAX, 419 mClusterOffset >= 0 ? mClusterOffset : INT64_MAX); 420 } 421 return mBlockEndOffset; 422 } 423 424 int64_t WebMBufferedParser::GetClusterOffset() const { return mClusterOffset; } 425 426 // SyncOffsetComparator and TimeComparator are slightly confusing, in that 427 // the nsTArray they're used with (mTimeMapping) is sorted by mEndOffset and 428 // these comparators are used on the other fields of WebMTimeDataOffset. 429 // This is only valid because timecodes are required to be monotonically 430 // increasing within a file (thus establishing an ordering relationship with 431 // mTimecode), and mEndOffset is derived from mSyncOffset. 432 struct SyncOffsetComparator { 433 bool Equals(const WebMTimeDataOffset& a, const int64_t& b) const { 434 return a.mSyncOffset == b; 435 } 436 437 bool LessThan(const WebMTimeDataOffset& a, const int64_t& b) const { 438 return a.mSyncOffset < b; 439 } 440 }; 441 442 struct TimeComparator { 443 bool Equals(const WebMTimeDataOffset& a, const uint64_t& b) const { 444 return a.mTimecode == b; 445 } 446 447 bool LessThan(const WebMTimeDataOffset& a, const uint64_t& b) const { 448 return a.mTimecode < b; 449 } 450 }; 451 452 bool WebMBufferedState::CalculateBufferedForRange(int64_t aStartOffset, 453 int64_t aEndOffset, 454 uint64_t* aStartTime, 455 uint64_t* aEndTime) { 456 MutexAutoLock lock(mMutex); 457 458 // Find the first WebMTimeDataOffset at or after aStartOffset. 459 uint32_t start = mTimeMapping.IndexOfFirstElementGt(aStartOffset - 1, 460 SyncOffsetComparator()); 461 if (start == mTimeMapping.Length()) { 462 return false; 463 } 464 465 // Find the first WebMTimeDataOffset at or before aEndOffset. 466 uint32_t end = mTimeMapping.IndexOfFirstElementGt(aEndOffset); 467 if (end > 0) { 468 end -= 1; 469 } 470 471 // Range is empty. 472 if (end <= start) { 473 return false; 474 } 475 476 NS_ASSERTION(mTimeMapping[start].mSyncOffset >= aStartOffset && 477 mTimeMapping[end].mEndOffset <= aEndOffset, 478 "Computed time range must lie within data range."); 479 if (start > 0) { 480 NS_ASSERTION(mTimeMapping[start - 1].mSyncOffset < aStartOffset, 481 "Must have found least WebMTimeDataOffset for start"); 482 } 483 if (end < mTimeMapping.Length() - 1) { 484 NS_ASSERTION(mTimeMapping[end + 1].mEndOffset > aEndOffset, 485 "Must have found greatest WebMTimeDataOffset for end"); 486 } 487 488 MOZ_ASSERT(mTimeMapping[end].mTimecode >= mTimeMapping[end - 1].mTimecode); 489 uint64_t frameDuration = 490 mTimeMapping[end].mTimecode - mTimeMapping[end - 1].mTimecode; 491 *aStartTime = mTimeMapping[start].mTimecode; 492 CheckedUint64 endTime{mTimeMapping[end].mTimecode}; 493 endTime += frameDuration; 494 if (!endTime.isValid()) { 495 WEBM_DEBUG("End time overflow during CalculateBufferedForRange."); 496 return false; 497 } 498 *aEndTime = endTime.value(); 499 return true; 500 } 501 502 bool WebMBufferedState::GetOffsetForTime(uint64_t aTime, int64_t* aOffset) { 503 MutexAutoLock lock(mMutex); 504 505 if (mTimeMapping.IsEmpty()) { 506 return false; 507 } 508 509 uint64_t time = aTime; 510 if (time > 0) { 511 time = time - 1; 512 } 513 uint32_t idx = mTimeMapping.IndexOfFirstElementGt(time, TimeComparator()); 514 if (idx == mTimeMapping.Length()) { 515 // Clamp to end 516 *aOffset = mTimeMapping[mTimeMapping.Length() - 1].mSyncOffset; 517 } else { 518 // Idx is within array or has been clamped to start 519 *aOffset = mTimeMapping[idx].mSyncOffset; 520 } 521 return true; 522 } 523 524 void WebMBufferedState::NotifyDataArrived(const unsigned char* aBuffer, 525 uint32_t aLength, int64_t aOffset) { 526 uint32_t idx = mRangeParsers.IndexOfFirstElementGt(aOffset - 1); 527 if (idx == 0 || !(mRangeParsers[idx - 1] == aOffset)) { 528 // If the incoming data overlaps an already parsed range, adjust the 529 // buffer so that we only reparse the new data. It's also possible to 530 // have an overlap where the end of the incoming data is within an 531 // already parsed range, but we don't bother handling that other than by 532 // avoiding storing duplicate timecodes when the parser runs. 533 if (idx != mRangeParsers.Length() && 534 mRangeParsers[idx].mStartOffset <= aOffset) { 535 // Complete overlap, skip parsing. 536 if (aOffset + aLength <= mRangeParsers[idx].mCurrentOffset) { 537 return; 538 } 539 540 // Partial overlap, adjust the buffer to parse only the new data. 541 int64_t adjust = mRangeParsers[idx].mCurrentOffset - aOffset; 542 NS_ASSERTION(adjust >= 0, "Overlap detection bug."); 543 aBuffer += adjust; 544 aLength -= uint32_t(adjust); 545 } else { 546 mRangeParsers.InsertElementAt(idx, WebMBufferedParser(aOffset)); 547 if (idx != 0) { 548 mRangeParsers[idx].SetTimecodeScale( 549 mRangeParsers[0].GetTimecodeScale()); 550 } 551 } 552 } 553 554 { 555 MutexAutoLock lock(mMutex); 556 mRangeParsers[idx].Append(aBuffer, aLength, mTimeMapping); 557 } 558 559 // Merge parsers with overlapping regions and clean up the remnants. 560 uint32_t i = 0; 561 while (i + 1 < mRangeParsers.Length()) { 562 if (mRangeParsers[i].mCurrentOffset >= mRangeParsers[i + 1].mStartOffset) { 563 mRangeParsers[i + 1].mStartOffset = mRangeParsers[i].mStartOffset; 564 mRangeParsers[i + 1].mInitEndOffset = mRangeParsers[i].mInitEndOffset; 565 mRangeParsers.RemoveElementAt(i); 566 } else { 567 i += 1; 568 } 569 } 570 } 571 572 void WebMBufferedState::Reset() { 573 MutexAutoLock lock(mMutex); 574 mRangeParsers.Clear(); 575 mTimeMapping.Clear(); 576 } 577 578 void WebMBufferedState::UpdateIndex(const MediaByteRangeSet& aRanges, 579 MediaResource* aResource) { 580 for (uint32_t index = 0; index < aRanges.Length(); index++) { 581 const MediaByteRange& range = aRanges[index]; 582 int64_t offset = range.mStart; 583 uint32_t length = range.mEnd - range.mStart; 584 585 uint32_t idx = mRangeParsers.IndexOfFirstElementGt(offset - 1); 586 if (!idx || !(mRangeParsers[idx - 1] == offset)) { 587 // If the incoming data overlaps an already parsed range, adjust the 588 // buffer so that we only reparse the new data. It's also possible to 589 // have an overlap where the end of the incoming data is within an 590 // already parsed range, but we don't bother handling that other than by 591 // avoiding storing duplicate timecodes when the parser runs. 592 if (idx != mRangeParsers.Length() && 593 mRangeParsers[idx].mStartOffset <= offset) { 594 // Complete overlap, skip parsing. 595 if (offset + length <= mRangeParsers[idx].mCurrentOffset) { 596 continue; 597 } 598 599 // Partial overlap, adjust the buffer to parse only the new data. 600 int64_t adjust = mRangeParsers[idx].mCurrentOffset - offset; 601 NS_ASSERTION(adjust >= 0, "Overlap detection bug."); 602 offset += adjust; 603 length -= uint32_t(adjust); 604 } else { 605 mRangeParsers.InsertElementAt(idx, WebMBufferedParser(offset)); 606 if (idx) { 607 mRangeParsers[idx].SetTimecodeScale( 608 mRangeParsers[0].GetTimecodeScale()); 609 } 610 } 611 } 612 613 MediaResourceIndex res(aResource); 614 while (length > 0) { 615 static const uint32_t BLOCK_SIZE = 1048576; 616 uint32_t block = std::min(length, BLOCK_SIZE); 617 RefPtr<MediaByteBuffer> bytes = res.CachedMediaReadAt(offset, block); 618 if (!bytes) { 619 break; 620 } 621 NotifyDataArrived(bytes->Elements(), bytes->Length(), offset); 622 length -= bytes->Length(); 623 offset += bytes->Length(); 624 } 625 } 626 } 627 628 int64_t WebMBufferedState::GetInitEndOffset() { 629 if (mRangeParsers.IsEmpty()) { 630 return -1; 631 } 632 return mRangeParsers[0].mInitEndOffset; 633 } 634 635 bool WebMBufferedState::GetStartTime(uint64_t* aTime) { 636 MutexAutoLock lock(mMutex); 637 638 if (mTimeMapping.IsEmpty()) { 639 return false; 640 } 641 642 uint32_t idx = mTimeMapping.IndexOfFirstElementGt(0, SyncOffsetComparator()); 643 if (idx == mTimeMapping.Length()) { 644 return false; 645 } 646 647 *aTime = mTimeMapping[idx].mTimecode; 648 return true; 649 } 650 651 bool WebMBufferedState::GetNextKeyframeTime(uint64_t aTime, 652 uint64_t* aKeyframeTime) { 653 MutexAutoLock lock(mMutex); 654 int64_t offset = 0; 655 bool rv = GetOffsetForTime(aTime, &offset); 656 if (!rv) { 657 return false; 658 } 659 uint32_t idx = 660 mTimeMapping.IndexOfFirstElementGt(offset, SyncOffsetComparator()); 661 if (idx == mTimeMapping.Length()) { 662 return false; 663 } 664 *aKeyframeTime = mTimeMapping[idx].mTimecode; 665 return true; 666 } 667 } // namespace mozilla 668 669 #undef WEBM_DEBUG