ContainerParser.cpp (28121B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 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 "ContainerParser.h" 8 9 #include <algorithm> 10 11 #include "AtomType.h" 12 #include "BufferReader.h" 13 #include "ByteStream.h" 14 #include "MP4Interval.h" 15 #include "MediaData.h" 16 #include "MoofParser.h" 17 #include "SampleIterator.h" 18 #include "SourceBufferResource.h" 19 #include "WebMBufferedParser.h" 20 #include "mozilla/ErrorResult.h" 21 #include "mozilla/IntegerPrintfMacros.h" 22 #include "mozilla/Logging.h" 23 #include "mozilla/Maybe.h" 24 #include "mozilla/Try.h" 25 #include "nsMimeTypes.h" 26 27 extern mozilla::LogModule* GetMediaSourceSamplesLog(); 28 29 #define MSE_DEBUG(arg, ...) \ 30 DDMOZ_LOG(GetMediaSourceSamplesLog(), mozilla::LogLevel::Debug, \ 31 "(%s)::%s: " arg, mType.OriginalString().Data(), __func__, \ 32 ##__VA_ARGS__) 33 #define MSE_DEBUGV(arg, ...) \ 34 DDMOZ_LOG(GetMediaSourceSamplesLog(), mozilla::LogLevel::Verbose, \ 35 "(%s)::%s: " arg, mType.OriginalString().Data(), __func__, \ 36 ##__VA_ARGS__) 37 #define MSE_DEBUGVEX(_this, arg, ...) \ 38 DDMOZ_LOGEX(_this, GetMediaSourceSamplesLog(), mozilla::LogLevel::Verbose, \ 39 "(%s)::%s: " arg, mType.OriginalString().Data(), __func__, \ 40 ##__VA_ARGS__) 41 42 namespace mozilla { 43 44 ContainerParser::ContainerParser(const MediaContainerType& aType) 45 : mHasInitData(false), mTotalParsed(0), mGlobalOffset(0), mType(aType) {} 46 47 ContainerParser::~ContainerParser() = default; 48 49 MediaResult ContainerParser::IsInitSegmentPresent(const MediaSpan& aData) { 50 MSE_DEBUG( 51 "aLength=%zu [%x%x%x%x]", aData.Length(), 52 aData.Length() > 0 ? aData[0] : 0, aData.Length() > 1 ? aData[1] : 0, 53 aData.Length() > 2 ? aData[2] : 0, aData.Length() > 3 ? aData[3] : 0); 54 return NS_ERROR_NOT_AVAILABLE; 55 } 56 57 MediaResult ContainerParser::IsMediaSegmentPresent(const MediaSpan& aData) { 58 MSE_DEBUG( 59 "aLength=%zu [%x%x%x%x]", aData.Length(), 60 aData.Length() > 0 ? aData[0] : 0, aData.Length() > 1 ? aData[1] : 0, 61 aData.Length() > 2 ? aData[2] : 0, aData.Length() > 3 ? aData[3] : 0); 62 return NS_ERROR_NOT_AVAILABLE; 63 } 64 65 MediaResult ContainerParser::ParseStartAndEndTimestamps(const MediaSpan& aData, 66 media::TimeUnit& aStart, 67 media::TimeUnit& aEnd) { 68 return NS_ERROR_NOT_AVAILABLE; 69 } 70 71 bool ContainerParser::TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs) { 72 return llabs(aLhs - aRhs) <= GetRoundingError(); 73 } 74 75 int64_t ContainerParser::GetRoundingError() { 76 NS_WARNING("Using default ContainerParser::GetRoundingError implementation"); 77 return 0; 78 } 79 80 bool ContainerParser::HasCompleteInitData() { 81 return mHasInitData && !!mInitData->Length(); 82 } 83 84 MediaByteBuffer* ContainerParser::InitData() { return mInitData; } 85 86 MediaByteRange ContainerParser::InitSegmentRange() { 87 return mCompleteInitSegmentRange; 88 } 89 90 MediaByteRange ContainerParser::MediaHeaderRange() { 91 return mCompleteMediaHeaderRange; 92 } 93 94 MediaByteRange ContainerParser::MediaSegmentRange() { 95 return mCompleteMediaSegmentRange; 96 } 97 98 DDLoggedTypeDeclNameAndBase(WebMContainerParser, ContainerParser); 99 100 class WebMContainerParser 101 : public ContainerParser, 102 public DecoderDoctorLifeLogger<WebMContainerParser> { 103 public: 104 explicit WebMContainerParser(const MediaContainerType& aType) 105 : ContainerParser(aType), mParser(0), mOffset(0) {} 106 107 static const unsigned NS_PER_USEC = 1000; 108 109 MediaResult IsInitSegmentPresent(const MediaSpan& aData) override { 110 ContainerParser::IsInitSegmentPresent(aData); 111 if (aData.Length() < 4) { 112 return NS_ERROR_NOT_AVAILABLE; 113 } 114 115 WebMBufferedParser parser(0); 116 nsTArray<WebMTimeDataOffset> mapping; 117 if (auto result = parser.Append(aData.Elements(), aData.Length(), mapping); 118 NS_FAILED(result)) { 119 return result; 120 } 121 return parser.mInitEndOffset > 0 ? NS_OK : NS_ERROR_NOT_AVAILABLE; 122 } 123 124 MediaResult IsMediaSegmentPresent(const MediaSpan& aData) override { 125 ContainerParser::IsMediaSegmentPresent(aData); 126 if (aData.Length() < 4) { 127 return NS_ERROR_NOT_AVAILABLE; 128 } 129 130 WebMBufferedParser parser(0); 131 nsTArray<WebMTimeDataOffset> mapping; 132 parser.AppendMediaSegmentOnly(); 133 if (auto result = parser.Append(aData.Elements(), aData.Length(), mapping); 134 NS_FAILED(result)) { 135 return result; 136 } 137 return parser.GetClusterOffset() >= 0 ? NS_OK : NS_ERROR_NOT_AVAILABLE; 138 } 139 140 MediaResult ParseStartAndEndTimestamps(const MediaSpan& aData, 141 media::TimeUnit& aStart, 142 media::TimeUnit& aEnd) override { 143 bool initSegment = NS_SUCCEEDED(IsInitSegmentPresent(aData)); 144 145 if (mLastMapping && 146 (initSegment || NS_SUCCEEDED(IsMediaSegmentPresent(aData)))) { 147 // The last data contained a complete cluster but we can only detect it 148 // now that a new one is starting. 149 // We use mOffset as end position to ensure that any blocks not reported 150 // by WebMBufferParser are properly skipped. 151 mCompleteMediaSegmentRange = 152 MediaByteRange(mLastMapping.ref().mSyncOffset, mOffset) + 153 mGlobalOffset; 154 mLastMapping.reset(); 155 MSE_DEBUG("New cluster found at start, ending previous one"); 156 return NS_ERROR_NOT_AVAILABLE; 157 } 158 159 if (initSegment) { 160 mOffset = 0; 161 mParser = WebMBufferedParser(0); 162 mOverlappedMapping.Clear(); 163 mInitData = new MediaByteBuffer(); 164 mResource = new SourceBufferResource(); 165 DDLINKCHILD("resource", mResource.get()); 166 mCompleteInitSegmentRange = MediaByteRange(); 167 mCompleteMediaHeaderRange = MediaByteRange(); 168 mCompleteMediaSegmentRange = MediaByteRange(); 169 mGlobalOffset = mTotalParsed; 170 } 171 172 // XXX if it only adds new mappings, overlapped but not available 173 // (e.g. overlap < 0) frames are "lost" from the reported mappings here. 174 nsTArray<WebMTimeDataOffset> mapping; 175 mapping.AppendElements(mOverlappedMapping); 176 mOverlappedMapping.Clear(); 177 if (auto result = mParser.Append(aData.Elements(), aData.Length(), mapping); 178 NS_FAILED(result)) { 179 return result; 180 } 181 if (mResource) { 182 mResource->AppendData(aData); 183 } 184 185 // XXX This is a bit of a hack. Assume if there are no timecodes 186 // present and it's an init segment that it's _just_ an init segment. 187 // We should be more precise. 188 if (initSegment || !HasCompleteInitData()) { 189 if (mParser.mInitEndOffset > 0) { 190 MOZ_DIAGNOSTIC_ASSERT(mInitData && mResource && 191 mParser.mInitEndOffset <= 192 mResource->GetCachedDataEnd(0)); 193 if (!mInitData->SetLength(mParser.mInitEndOffset, fallible)) { 194 // Super unlikely OOM 195 return NS_ERROR_OUT_OF_MEMORY; 196 } 197 mCompleteInitSegmentRange = 198 MediaByteRange(0, mParser.mInitEndOffset) + mGlobalOffset; 199 char* buffer = reinterpret_cast<char*>(mInitData->Elements()); 200 mResource->ReadFromCache(buffer, 0, mParser.mInitEndOffset); 201 MSE_DEBUG("Stashed init of %" PRId64 " bytes.", mParser.mInitEndOffset); 202 mResource = nullptr; 203 } else { 204 MSE_DEBUG("Incomplete init found."); 205 } 206 mHasInitData = true; 207 } 208 mOffset += aData.Length(); 209 mTotalParsed += aData.Length(); 210 211 if (mapping.IsEmpty()) { 212 return NS_ERROR_NOT_AVAILABLE; 213 } 214 215 // Calculate media range for first media segment. 216 217 // Check if we have a cluster finishing in the current data. 218 uint32_t endIdx = mapping.Length() - 1; 219 bool foundNewCluster = false; 220 while (mapping[0].mSyncOffset != mapping[endIdx].mSyncOffset) { 221 endIdx -= 1; 222 foundNewCluster = true; 223 } 224 225 int32_t completeIdx = endIdx; 226 while (completeIdx >= 0 && mOffset < mapping[completeIdx].mEndOffset) { 227 MSE_DEBUG("block is incomplete, missing: %" PRId64, 228 mapping[completeIdx].mEndOffset - mOffset); 229 completeIdx -= 1; 230 } 231 232 // Save parsed blocks for which we do not have all data yet. 233 mOverlappedMapping.AppendElements(mapping.Elements() + completeIdx + 1, 234 mapping.Length() - completeIdx - 1); 235 236 if (completeIdx < 0) { 237 mLastMapping.reset(); 238 return NS_ERROR_NOT_AVAILABLE; 239 } 240 241 if (mCompleteMediaHeaderRange.IsEmpty()) { 242 mCompleteMediaHeaderRange = 243 MediaByteRange(mapping[0].mSyncOffset, mapping[0].mEndOffset) + 244 mGlobalOffset; 245 } 246 247 if (foundNewCluster && mOffset >= mapping[endIdx].mEndOffset) { 248 // We now have all information required to delimit a complete cluster. 249 int64_t endOffset = mapping[endIdx + 1].mSyncOffset; 250 if (mapping[endIdx + 1].mInitOffset > mapping[endIdx].mInitOffset) { 251 // We have a new init segment before this cluster. 252 endOffset = mapping[endIdx + 1].mInitOffset; 253 } 254 mCompleteMediaSegmentRange = 255 MediaByteRange(mapping[endIdx].mSyncOffset, endOffset) + 256 mGlobalOffset; 257 } else if (mapping[endIdx].mClusterEndOffset >= 0 && 258 mOffset >= mapping[endIdx].mClusterEndOffset) { 259 mCompleteMediaSegmentRange = 260 MediaByteRange( 261 mapping[endIdx].mSyncOffset, 262 mParser.EndSegmentOffset(mapping[endIdx].mClusterEndOffset)) + 263 mGlobalOffset; 264 } 265 266 Maybe<WebMTimeDataOffset> previousMapping; 267 if (completeIdx) { 268 previousMapping = Some(mapping[completeIdx - 1]); 269 } else { 270 previousMapping = mLastMapping; 271 } 272 273 mLastMapping = Some(mapping[completeIdx]); 274 275 if (!previousMapping && completeIdx + 1u >= mapping.Length()) { 276 // We have no previous nor next block available, 277 // so we can't estimate this block's duration. 278 return NS_ERROR_NOT_AVAILABLE; 279 } 280 281 uint64_t frameDuration = 282 (completeIdx + 1u < mapping.Length()) 283 ? mapping[completeIdx + 1].mTimecode - 284 mapping[completeIdx].mTimecode 285 : mapping[completeIdx].mTimecode - previousMapping.ref().mTimecode; 286 aStart = media::TimeUnit::FromNanoseconds( 287 AssertedCast<int64_t>(mapping[0].mTimecode)); 288 aEnd = media::TimeUnit::FromNanoseconds( 289 AssertedCast<int64_t>(mapping[completeIdx].mTimecode + frameDuration)); 290 291 MSE_DEBUG("[%" PRId64 ", %" PRId64 "] [fso=%" PRId64 ", leo=%" PRId64 292 ", l=%zu processedIdx=%u fs=%" PRId64 "]", 293 aStart.ToMicroseconds(), aEnd.ToMicroseconds(), 294 mapping[0].mSyncOffset, mapping[completeIdx].mEndOffset, 295 mapping.Length(), completeIdx, mCompleteMediaSegmentRange.mEnd); 296 297 return NS_OK; 298 } 299 300 int64_t GetRoundingError() override { 301 int64_t error = mParser.GetTimecodeScale() / NS_PER_USEC; 302 return error * 2; 303 } 304 305 private: 306 WebMBufferedParser mParser; 307 nsTArray<WebMTimeDataOffset> mOverlappedMapping; 308 int64_t mOffset; 309 Maybe<WebMTimeDataOffset> mLastMapping; 310 }; 311 312 DDLoggedTypeDeclNameAndBase(MP4Stream, ByteStream); 313 314 class MP4Stream : public ByteStream, public DecoderDoctorLifeLogger<MP4Stream> { 315 public: 316 explicit MP4Stream(SourceBufferResource* aResource); 317 virtual ~MP4Stream(); 318 nsresult ReadAt(int64_t aOffset, void* aBuffer, size_t aCount, 319 size_t* aBytesRead) override; 320 nsresult CachedReadAt(int64_t aOffset, void* aBuffer, size_t aCount, 321 size_t* aBytesRead) override; 322 bool Length(int64_t* aSize) override; 323 const uint8_t* GetContiguousAccess(int64_t aOffset, size_t aSize) override; 324 325 private: 326 RefPtr<SourceBufferResource> mResource; 327 }; 328 329 MP4Stream::MP4Stream(SourceBufferResource* aResource) : mResource(aResource) { 330 MOZ_COUNT_CTOR(MP4Stream); 331 MOZ_ASSERT(aResource); 332 DDLINKCHILD("resource", aResource); 333 } 334 335 MP4Stream::~MP4Stream() { MOZ_COUNT_DTOR(MP4Stream); } 336 337 nsresult MP4Stream::ReadAt(int64_t aOffset, void* aBuffer, size_t aCount, 338 size_t* aBytesRead) { 339 return CachedReadAt(aOffset, aBuffer, aCount, aBytesRead); 340 } 341 342 nsresult MP4Stream::CachedReadAt(int64_t aOffset, void* aBuffer, size_t aCount, 343 size_t* aBytesRead) { 344 nsresult rv = mResource->ReadFromCache(reinterpret_cast<char*>(aBuffer), 345 aOffset, aCount); 346 if (NS_FAILED(rv)) { 347 *aBytesRead = 0; 348 return rv; 349 } 350 *aBytesRead = aCount; 351 return rv; 352 } 353 354 const uint8_t* MP4Stream::GetContiguousAccess(int64_t aOffset, size_t aSize) { 355 return mResource->GetContiguousAccess(aOffset, aSize); 356 } 357 358 bool MP4Stream::Length(int64_t* aSize) { 359 if (mResource->GetLength() < 0) return false; 360 *aSize = mResource->GetLength(); 361 return true; 362 } 363 364 DDLoggedTypeDeclNameAndBase(MP4ContainerParser, ContainerParser); 365 366 class MP4ContainerParser : public ContainerParser, 367 public DecoderDoctorLifeLogger<MP4ContainerParser> { 368 public: 369 explicit MP4ContainerParser(const MediaContainerType& aType) 370 : ContainerParser(aType) {} 371 372 MediaResult IsInitSegmentPresent(const MediaSpan& aData) override { 373 ContainerParser::IsInitSegmentPresent(aData); 374 // Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4 375 // file is the 'ftyp' atom followed by a file type. We just check for a 376 // vaguely valid 'ftyp' atom. 377 if (aData.Length() < 8) { 378 return NS_ERROR_NOT_AVAILABLE; 379 } 380 AtomParser parser(*this, aData, AtomParser::StopAt::eInitSegment); 381 if (!parser.IsValid()) { 382 return MediaResult( 383 NS_ERROR_FAILURE, 384 RESULT_DETAIL("Invalid Top-Level Box:%s", parser.LastInvalidBox())); 385 } 386 return parser.StartWithInitSegment() ? NS_OK : NS_ERROR_NOT_AVAILABLE; 387 } 388 389 MediaResult IsMediaSegmentPresent(const MediaSpan& aData) override { 390 if (aData.Length() < 8) { 391 return NS_ERROR_NOT_AVAILABLE; 392 } 393 AtomParser parser(*this, aData, AtomParser::StopAt::eMediaSegment); 394 if (!parser.IsValid()) { 395 return MediaResult( 396 NS_ERROR_FAILURE, 397 RESULT_DETAIL("Invalid Box:%s", parser.LastInvalidBox())); 398 } 399 return parser.StartWithMediaSegment() ? NS_OK : NS_ERROR_NOT_AVAILABLE; 400 } 401 402 private: 403 class AtomParser { 404 public: 405 enum class StopAt { eInitSegment, eMediaSegment, eEnd }; 406 407 AtomParser(const MP4ContainerParser& aParser, const MediaSpan& aData, 408 StopAt aStop = StopAt::eEnd) { 409 mValid = Init(aParser, aData, aStop).isOk(); 410 } 411 412 Result<Ok, nsresult> Init(const MP4ContainerParser& aParser, 413 const MediaSpan& aData, StopAt aStop) { 414 const MediaContainerType mType( 415 aParser.ContainerType()); // for logging macro. 416 BufferReader reader(aData); 417 AtomType initAtom("moov"); 418 AtomType mediaAtom("moof"); 419 AtomType dataAtom("mdat"); 420 421 // Valid top-level boxes defined in ISO/IEC 14496-12 (Table 1) 422 static const AtomType validBoxes[] = { 423 "ftyp", "moov", // init segment 424 "pdin", "free", "sidx", // optional prior moov box 425 "styp", "moof", "mdat", // media segment 426 "mfra", "skip", "meta", "meco", "ssix", "prft", // others. 427 "pssh", // optional with encrypted EME, though ignored. 428 "emsg", // ISO23009-1:2014 Section 5.10.3.3 429 "bloc", "uuid" // boxes accepted by chrome. 430 }; 431 432 while (reader.Remaining() >= 8) { 433 uint64_t size = MOZ_TRY(reader.ReadU32()); 434 const uint8_t* typec = reader.Peek(4); 435 AtomType type(MOZ_TRY(reader.ReadU32())); 436 // We've seen fourcc not being ASCII in the wild. In this rare case, 437 // print hex values instead of the ascii representation. 438 if (isprint(typec[0]) && isprint(typec[1]) && isprint(typec[2]) && 439 isprint(typec[3])) { 440 MSE_DEBUGVEX(&aParser, "Checking atom:'%c%c%c%c' @ %u", typec[0], 441 typec[1], typec[2], typec[3], 442 (uint32_t)reader.Offset() - 8); 443 } else { 444 MSE_DEBUGVEX(&aParser, 445 "Checking atom (not ASCII):'0x%02x%02x%02x%02x' @ %u", 446 typec[0], typec[1], typec[2], typec[3], 447 (uint32_t)reader.Offset() - 8); 448 } 449 if (std::find(std::begin(validBoxes), std::end(validBoxes), type) == 450 std::end(validBoxes)) { 451 // No valid box found, no point continuing. 452 mLastInvalidBox[0] = typec[0]; 453 mLastInvalidBox[1] = typec[1]; 454 mLastInvalidBox[2] = typec[2]; 455 mLastInvalidBox[3] = typec[3]; 456 mLastInvalidBox[4] = '\0'; 457 return Err(NS_ERROR_FAILURE); 458 } 459 if (mInitOffset.isNothing() && AtomType(type) == initAtom) { 460 mInitOffset = Some(reader.Offset()); 461 } 462 if (mMediaOffset.isNothing() && AtomType(type) == mediaAtom) { 463 mMediaOffset = Some(reader.Offset()); 464 } 465 if (mDataOffset.isNothing() && AtomType(type) == dataAtom) { 466 mDataOffset = Some(reader.Offset()); 467 } 468 if (size == 1) { 469 // 64 bits size. 470 size = MOZ_TRY(reader.ReadU64()); 471 } else if (size == 0) { 472 // Atom extends to the end of the buffer, it can't have what we're 473 // looking for. 474 break; 475 } 476 if (reader.Remaining() < size - 8) { 477 // Incomplete atom. 478 break; 479 } 480 reader.Read(size - 8); 481 482 if (aStop == StopAt::eInitSegment && (mInitOffset || mMediaOffset)) { 483 // When we're looking for an init segment, if we encountered a media 484 // segment, it we will need to be processed first. So we can stop 485 // right away if we have found a media segment. 486 break; 487 } 488 if (aStop == StopAt::eMediaSegment && 489 (mInitOffset || (mMediaOffset && mDataOffset))) { 490 // When we're looking for a media segment, if we encountered an init 491 // segment, it we will need to be processed first. So we can stop 492 // right away if we have found an init segment. 493 break; 494 } 495 } 496 497 return Ok(); 498 } 499 500 bool StartWithInitSegment() const { 501 return mInitOffset.isSome() && (mMediaOffset.isNothing() || 502 mInitOffset.ref() < mMediaOffset.ref()); 503 } 504 bool StartWithMediaSegment() const { 505 return mMediaOffset.isSome() && (mInitOffset.isNothing() || 506 mMediaOffset.ref() < mInitOffset.ref()); 507 } 508 bool IsValid() const { return mValid; } 509 const char* LastInvalidBox() const { return mLastInvalidBox; } 510 511 private: 512 Maybe<size_t> mInitOffset; 513 Maybe<size_t> mMediaOffset; 514 Maybe<size_t> mDataOffset; 515 bool mValid; 516 char mLastInvalidBox[5]; 517 }; 518 519 public: 520 MediaResult ParseStartAndEndTimestamps(const MediaSpan& aData, 521 media::TimeUnit& aStart, 522 media::TimeUnit& aEnd) override { 523 bool initSegment = NS_SUCCEEDED(IsInitSegmentPresent(aData)); 524 if (initSegment) { 525 mResource = new SourceBufferResource(); 526 DDLINKCHILD("resource", mResource.get()); 527 mStream = new MP4Stream(mResource); 528 // We use a timestampOffset of 0 for ContainerParser, and require 529 // consumers of ParseStartAndEndTimestamps to add their timestamp offset 530 // manually. This allows the ContainerParser to be shared across different 531 // timestampOffsets. 532 mParser = MakeUnique<MoofParser>(mStream, AsVariant(ParseAllTracks{}), 533 /* aIsAudio = */ false); 534 DDLINKCHILD("parser", mParser.get()); 535 mInitData = new MediaByteBuffer(); 536 mCompleteInitSegmentRange = MediaByteRange(); 537 mCompleteMediaHeaderRange = MediaByteRange(); 538 mCompleteMediaSegmentRange = MediaByteRange(); 539 mGlobalOffset = mTotalParsed; 540 } else if (!mStream || !mParser) { 541 mTotalParsed += aData.Length(); 542 return NS_ERROR_NOT_AVAILABLE; 543 } 544 545 MOZ_DIAGNOSTIC_ASSERT(mResource && mParser && mInitData, 546 "Should have received an init segment first"); 547 548 mResource->AppendData(aData); 549 MediaByteRangeSet byteRanges; 550 byteRanges += MediaByteRange(int64_t(mParser->mOffset), 551 mResource->GetCachedDataEnd(mParser->mOffset)); 552 mParser->RebuildFragmentedIndex(byteRanges); 553 554 if (initSegment || !HasCompleteInitData()) { 555 MediaByteRange& range = mParser->mInitRange; 556 if (range.Length()) { 557 mCompleteInitSegmentRange = range + mGlobalOffset; 558 if (!mInitData->SetLength(range.Length(), fallible)) { 559 // Super unlikely OOM 560 return NS_ERROR_OUT_OF_MEMORY; 561 } 562 char* buffer = reinterpret_cast<char*>(mInitData->Elements()); 563 mResource->ReadFromCache(buffer, range.mStart, range.Length()); 564 MSE_DEBUG("Stashed init of %" PRIu64 " bytes.", range.Length()); 565 } else { 566 MSE_DEBUG("Incomplete init found."); 567 } 568 mHasInitData = true; 569 } 570 mTotalParsed += aData.Length(); 571 572 MP4Interval<media::TimeUnit> compositionRange = 573 mParser->GetCompositionRange(byteRanges); 574 575 mCompleteMediaHeaderRange = 576 mParser->FirstCompleteMediaHeader() + mGlobalOffset; 577 mCompleteMediaSegmentRange = 578 mParser->FirstCompleteMediaSegment() + mGlobalOffset; 579 580 if (HasCompleteInitData()) { 581 mResource->EvictData(mParser->mOffset, mParser->mOffset); 582 } 583 584 if (compositionRange.IsNull()) { 585 return NS_ERROR_NOT_AVAILABLE; 586 } 587 aStart = compositionRange.start; 588 aEnd = compositionRange.end; 589 MSE_DEBUG("[%" PRId64 ", %" PRId64 "]", aStart.ToMicroseconds(), 590 aEnd.ToMicroseconds()); 591 return NS_OK; 592 } 593 594 // Gaps of up to 35ms (marginally longer than a single frame at 30fps) are 595 // considered to be sequential frames. 596 int64_t GetRoundingError() override { return 35000; } 597 598 private: 599 RefPtr<MP4Stream> mStream; 600 UniquePtr<MoofParser> mParser; 601 }; 602 DDLoggedTypeDeclNameAndBase(ADTSContainerParser, ContainerParser); 603 604 class ADTSContainerParser 605 : public ContainerParser, 606 public DecoderDoctorLifeLogger<ADTSContainerParser> { 607 public: 608 explicit ADTSContainerParser(const MediaContainerType& aType) 609 : ContainerParser(aType) {} 610 611 typedef struct { 612 size_t header_length; // Length of just the initialization data. 613 size_t frame_length; // Includes header_length; 614 uint8_t aac_frames; // Number of AAC frames in the ADTS frame. 615 bool have_crc; 616 } Header; 617 618 /// Helper to parse the ADTS header, returning data we care about. 619 /// Returns true if the header is parsed successfully. 620 /// Returns false if the header is invalid or incomplete, 621 /// without modifying the passed-in Header object. 622 bool Parse(const MediaSpan& aData, Header& header) { 623 // ADTS initialization segments are just the packet header. 624 if (aData.Length() < 7) { 625 MSE_DEBUG("buffer too short for header."); 626 return false; 627 } 628 // Check 0xfffx sync word plus layer 0. 629 if ((aData[0] != 0xff) || ((aData[1] & 0xf6) != 0xf0)) { 630 MSE_DEBUG("no syncword."); 631 return false; 632 } 633 bool have_crc = !(aData[1] & 0x01); 634 if (have_crc && aData.Length() < 9) { 635 MSE_DEBUG("buffer too short for header with crc."); 636 return false; 637 } 638 uint8_t frequency_index = (aData[2] & 0x3c) >> 2; 639 MOZ_ASSERT(frequency_index < 16); 640 if (frequency_index == 15) { 641 MSE_DEBUG("explicit frequency disallowed."); 642 return false; 643 } 644 size_t header_length = have_crc ? 9 : 7; 645 size_t data_length = ((aData[3] & 0x03) << 11) | ((aData[4] & 0xff) << 3) | 646 ((aData[5] & 0xe0) >> 5); 647 uint8_t frames = (aData[6] & 0x03) + 1; 648 MOZ_ASSERT(frames > 0); 649 MOZ_ASSERT(frames < 4); 650 651 // Return successfully parsed data. 652 header.header_length = header_length; 653 header.frame_length = header_length + data_length; 654 header.aac_frames = frames; 655 header.have_crc = have_crc; 656 return true; 657 } 658 659 MediaResult IsInitSegmentPresent(const MediaSpan& aData) override { 660 // Call superclass for logging. 661 ContainerParser::IsInitSegmentPresent(aData); 662 663 Header header; 664 if (!Parse(aData, header)) { 665 return NS_ERROR_NOT_AVAILABLE; 666 } 667 668 MSE_DEBUGV("%llu byte frame %d aac frames%s", 669 (unsigned long long)header.frame_length, (int)header.aac_frames, 670 header.have_crc ? " crc" : ""); 671 672 return NS_OK; 673 } 674 675 MediaResult IsMediaSegmentPresent(const MediaSpan& aData) override { 676 // Call superclass for logging. 677 ContainerParser::IsMediaSegmentPresent(aData); 678 679 // Make sure we have a header so we know how long the frame is. 680 // NB this assumes the media segment buffer starts with an 681 // initialization segment. Since every frame has an ADTS header 682 // this is a normal place to divide packets, but we can re-parse 683 // mInitData if we need to handle separate media segments. 684 Header header; 685 if (!Parse(aData, header)) { 686 return NS_ERROR_NOT_AVAILABLE; 687 } 688 // We're supposed to return true as long as aData contains the 689 // start of a media segment, whether or not it's complete. So 690 // return true if we have any data beyond the header. 691 if (aData.Length() <= header.header_length) { 692 return NS_ERROR_NOT_AVAILABLE; 693 } 694 695 // We should have at least a partial frame. 696 return NS_OK; 697 } 698 699 MediaResult ParseStartAndEndTimestamps(const MediaSpan& aData, 700 media::TimeUnit& aStart, 701 media::TimeUnit& aEnd) override { 702 // ADTS header. 703 Header header; 704 if (!Parse(aData, header)) { 705 return NS_ERROR_NOT_AVAILABLE; 706 } 707 mHasInitData = true; 708 mCompleteInitSegmentRange = 709 MediaByteRange(0, int64_t(header.header_length)); 710 711 // Cache raw header in case the caller wants a copy. 712 mInitData = new MediaByteBuffer(header.header_length); 713 mInitData->AppendElements(aData.Elements(), header.header_length); 714 715 // Check that we have enough data for the frame body. 716 if (aData.Length() < header.frame_length) { 717 MSE_DEBUGV( 718 "Not enough data for %llu byte frame" 719 " in %llu byte buffer.", 720 (unsigned long long)header.frame_length, 721 (unsigned long long)(aData.Length())); 722 return NS_ERROR_NOT_AVAILABLE; 723 } 724 mCompleteMediaSegmentRange = 725 MediaByteRange(header.header_length, header.frame_length); 726 // The ADTS MediaSource Byte Stream Format document doesn't 727 // define media header. Just treat it the same as the whole 728 // media segment. 729 mCompleteMediaHeaderRange = mCompleteMediaSegmentRange; 730 731 MSE_DEBUG("[%" PRId64 ", %" PRId64 "]", aStart.ToMicroseconds(), 732 aEnd.ToMicroseconds()); 733 // We don't update timestamps, regardless. 734 return NS_ERROR_NOT_AVAILABLE; 735 } 736 737 // Audio shouldn't have gaps. 738 // Especially when we generate the timestamps ourselves. 739 int64_t GetRoundingError() override { return 0; } 740 }; 741 742 /*static*/ 743 UniquePtr<ContainerParser> ContainerParser::CreateForMIMEType( 744 const MediaContainerType& aType) { 745 if (aType.Type() == MEDIAMIMETYPE(VIDEO_WEBM) || 746 aType.Type() == MEDIAMIMETYPE(AUDIO_WEBM)) { 747 return MakeUnique<WebMContainerParser>(aType); 748 } 749 750 if (aType.Type() == MEDIAMIMETYPE(VIDEO_MP4) || 751 aType.Type() == MEDIAMIMETYPE(AUDIO_MP4)) { 752 return MakeUnique<MP4ContainerParser>(aType); 753 } 754 if (aType.Type() == MEDIAMIMETYPE("audio/aac")) { 755 return MakeUnique<ADTSContainerParser>(aType); 756 } 757 758 return MakeUnique<ContainerParser>(aType); 759 } 760 761 #undef MSE_DEBUG 762 #undef MSE_DEBUGV 763 #undef MSE_DEBUGVEX 764 765 } // namespace mozilla