MoofParser.cpp (48035B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "MoofParser.h" 6 7 #include <limits> 8 9 #include "Box.h" 10 #include "MP4Interval.h" 11 #include "MediaDataDemuxer.h" 12 #include "SinfParser.h" 13 #include "mozilla/CheckedInt.h" 14 #include "mozilla/HelperMacros.h" 15 #include "mozilla/Logging.h" 16 #include "mozilla/Try.h" 17 18 #define LOG_ERROR(name, arg, ...) \ 19 MOZ_LOG( \ 20 gMediaDemuxerLog, mozilla::LogLevel::Error, \ 21 (MOZ_STRINGIFY(name) "(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) 22 #define LOG_WARN(name, arg, ...) \ 23 MOZ_LOG( \ 24 gMediaDemuxerLog, mozilla::LogLevel::Warning, \ 25 (MOZ_STRINGIFY(name) "(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) 26 #define LOG_DEBUG(name, arg, ...) \ 27 MOZ_LOG( \ 28 gMediaDemuxerLog, mozilla::LogLevel::Debug, \ 29 (MOZ_STRINGIFY(name) "(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) 30 31 namespace mozilla { 32 33 using TimeUnit = media::TimeUnit; 34 35 const uint32_t kKeyIdSize = 16; 36 37 bool MoofParser::RebuildFragmentedIndex(const MediaByteRangeSet& aByteRanges) { 38 BoxContext context(mSource, aByteRanges); 39 return RebuildFragmentedIndex(context); 40 } 41 42 bool MoofParser::RebuildFragmentedIndex(const MediaByteRangeSet& aByteRanges, 43 bool* aCanEvict) { 44 MOZ_ASSERT(aCanEvict); 45 if (*aCanEvict && mMoofs.Length() > 1) { 46 MOZ_ASSERT(mMoofs.Length() == mMediaRanges.Length()); 47 mMoofs.RemoveElementsAt(0, mMoofs.Length() - 1); 48 mMediaRanges.RemoveElementsAt(0, mMediaRanges.Length() - 1); 49 *aCanEvict = true; 50 } else { 51 *aCanEvict = false; 52 } 53 return RebuildFragmentedIndex(aByteRanges); 54 } 55 56 bool MoofParser::RebuildFragmentedIndex(BoxContext& aContext) { 57 LOG_DEBUG( 58 Moof, 59 "Starting, mTrackParseMode=%s, track#=%" PRIu32 60 " (ignore if multitrack).", 61 mTrackParseMode.is<ParseAllTracks>() ? "multitrack" : "single track", 62 mTrackParseMode.is<ParseAllTracks>() ? 0 63 : mTrackParseMode.as<uint32_t>()); 64 bool foundValidMoof = false; 65 66 for (Box box(&aContext, mOffset); box.IsAvailable(); 67 mOffset = box.NextOffset(), box = box.Next()) { 68 if (box.IsType("moov") && mInitRange.IsEmpty()) { 69 mInitRange = MediaByteRange(0, box.Range().mEnd); 70 ParseMoov(box); 71 } else if (box.IsType("moof")) { 72 Moof moof(box, mTrackParseMode, mTrex, mMvhd, mMdhd, mEdts, mSinf, 73 mIsAudio, &mLastDecodeTime, mTracksEndCts); 74 75 if (!moof.IsValid()) { 76 continue; // Skip to next box. 77 } 78 79 if (!mMoofs.IsEmpty()) { 80 // Stitch time ranges together in the case of a (hopefully small) time 81 // range gap between moofs. 82 mMoofs.LastElement().FixRounding(moof); 83 } 84 85 mMediaRanges.AppendElement(moof.mRange); 86 mMoofs.AppendElement(std::move(moof)); 87 foundValidMoof = true; 88 } else if (box.IsType("mdat") && !Moofs().IsEmpty()) { 89 // Check if we have all our data from last moof. 90 Moof& moof = Moofs().LastElement(); 91 media::Interval<int64_t> datarange(moof.mMdatRange.mStart, 92 moof.mMdatRange.mEnd, 0); 93 media::Interval<int64_t> mdat(box.Range().mStart, box.Range().mEnd, 0); 94 if (datarange.Intersects(mdat)) { 95 mMediaRanges.LastElement() = 96 mMediaRanges.LastElement().Span(box.Range()); 97 } 98 } 99 } 100 MOZ_ASSERT(mTrackParseMode.is<ParseAllTracks>() || 101 mTrex.mTrackId == mTrackParseMode.as<uint32_t>(), 102 "If not parsing all tracks, mTrex should have the same track id " 103 "as the track being parsed."); 104 LOG_DEBUG(Moof, "Done, foundValidMoof=%s.", 105 foundValidMoof ? "true" : "false"); 106 return foundValidMoof; 107 } 108 109 MediaByteRange MoofParser::FirstCompleteMediaHeader() { 110 if (Moofs().IsEmpty()) { 111 return MediaByteRange(); 112 } 113 return Moofs()[0].mRange; 114 } 115 116 MediaByteRange MoofParser::FirstCompleteMediaSegment() { 117 for (uint32_t i = 0; i < mMediaRanges.Length(); i++) { 118 if (mMediaRanges[i].Contains(Moofs()[i].mMdatRange)) { 119 return mMediaRanges[i]; 120 } 121 } 122 return MediaByteRange(); 123 } 124 125 const CencSampleEncryptionInfoEntry* MoofParser::GetSampleEncryptionEntry( 126 size_t aMoof, size_t aSample) const { 127 if (aMoof >= mMoofs.Length()) { 128 return nullptr; 129 } 130 return mMoofs[aMoof].GetSampleEncryptionEntry( 131 aSample, &mTrackSampleToGroupEntries, &mTrackSampleEncryptionInfoEntries); 132 } 133 134 DDLoggedTypeDeclNameAndBase(BlockingStream, ByteStream); 135 136 class BlockingStream : public ByteStream, 137 public DecoderDoctorLifeLogger<BlockingStream> { 138 public: 139 explicit BlockingStream(ByteStream* aStream) : mStream(aStream) { 140 DDLINKCHILD("stream", aStream); 141 } 142 143 nsresult ReadAt(int64_t offset, void* data, size_t size, 144 size_t* bytes_read) override { 145 return mStream->ReadAt(offset, data, size, bytes_read); 146 } 147 148 nsresult CachedReadAt(int64_t offset, void* data, size_t size, 149 size_t* bytes_read) override { 150 return mStream->ReadAt(offset, data, size, bytes_read); 151 } 152 153 virtual bool Length(int64_t* size) override { return mStream->Length(size); } 154 155 private: 156 RefPtr<ByteStream> mStream; 157 }; 158 159 nsresult MoofParser::BlockingReadNextMoof() { 160 LOG_DEBUG(Moof, "Starting."); 161 int64_t length = std::numeric_limits<int64_t>::max(); 162 mSource->Length(&length); 163 RefPtr<BlockingStream> stream = new BlockingStream(mSource); 164 MediaByteRangeSet byteRanges(MediaByteRange(0, length)); 165 166 BoxContext context(stream, byteRanges); 167 Box box(&context, mOffset); 168 for (; box.IsAvailable(); box = box.Next()) { 169 if (box.IsType("moof")) { 170 MediaByteRangeSet parseByteRanges( 171 MediaByteRange(mOffset, box.Range().mEnd)); 172 BoxContext parseContext(stream, parseByteRanges); 173 if (RebuildFragmentedIndex(parseContext)) { 174 LOG_DEBUG(Moof, "Succeeded on RebuildFragmentedIndex, returning NS_OK"); 175 return NS_OK; 176 } 177 } 178 } 179 nsresult rv = box.Offset() == length ? NS_ERROR_DOM_MEDIA_END_OF_STREAM 180 : box.InitStatus(); 181 LOG_DEBUG(Moof, "Couldn't read next moof, returning %s", 182 GetStaticErrorName(rv)); 183 return rv; 184 } 185 186 void MoofParser::ScanForMetadata(mozilla::MediaByteRange& aMoov) { 187 LOG_DEBUG(Moof, "Starting."); 188 int64_t length = std::numeric_limits<int64_t>::max(); 189 mSource->Length(&length); 190 MediaByteRangeSet byteRanges; 191 byteRanges += MediaByteRange(0, length); 192 RefPtr<BlockingStream> stream = new BlockingStream(mSource); 193 194 BoxContext context(stream, byteRanges); 195 for (Box box(&context, mOffset); box.IsAvailable(); box = box.Next()) { 196 if (box.IsType("moov")) { 197 aMoov = box.Range(); 198 break; 199 } 200 } 201 mInitRange = aMoov; 202 LOG_DEBUG(Moof, 203 "Done, mInitRange.mStart=%" PRIi64 ", mInitRange.mEnd=%" PRIi64, 204 mInitRange.mStart, mInitRange.mEnd); 205 } 206 207 already_AddRefed<mozilla::MediaByteBuffer> MoofParser::Metadata() { 208 LOG_DEBUG(Moof, "Starting."); 209 MediaByteRange moov; 210 ScanForMetadata(moov); 211 CheckedInt<MediaByteBuffer::size_type> moovLength = moov.Length(); 212 if (!moovLength.isValid() || !moovLength.value()) { 213 // No moov, or cannot be used as array size. 214 LOG_WARN(Moof, 215 "Did not get usable moov length while trying to parse Metadata."); 216 return nullptr; 217 } 218 219 RefPtr<MediaByteBuffer> metadata = new MediaByteBuffer(); 220 if (!metadata->SetLength(moovLength.value(), fallible)) { 221 LOG_ERROR(Moof, "OOM"); 222 return nullptr; 223 } 224 225 RefPtr<BlockingStream> stream = new BlockingStream(mSource); 226 size_t read; 227 nsresult rv = stream->ReadAt(moov.mStart, metadata->Elements(), 228 moovLength.value(), &read); 229 if (NS_FAILED(rv) || read != moovLength.value()) { 230 LOG_WARN(Moof, "Failed to read moov while trying to parse Metadata."); 231 return nullptr; 232 } 233 LOG_DEBUG(Moof, "Done, found metadata."); 234 return metadata.forget(); 235 } 236 237 MP4Interval<TimeUnit> MoofParser::GetCompositionRange( 238 const MediaByteRangeSet& aByteRanges) { 239 LOG_DEBUG(Moof, "Starting."); 240 MP4Interval<TimeUnit> compositionRange; 241 BoxContext context(mSource, aByteRanges); 242 for (size_t i = 0; i < mMoofs.Length(); i++) { 243 Moof& moof = mMoofs[i]; 244 Box box(&context, moof.mRange.mStart); 245 if (box.IsAvailable()) { 246 compositionRange = compositionRange.Extents(moof.mTimeRange); 247 } 248 } 249 LOG_DEBUG(Moof, 250 "Done, compositionRange.start=%" PRIi64 251 ", compositionRange.end=%" PRIi64 ".", 252 compositionRange.start.ToMicroseconds(), 253 compositionRange.end.ToMicroseconds()); 254 return compositionRange; 255 } 256 257 bool MoofParser::ReachedEnd() { 258 int64_t length; 259 return mSource->Length(&length) && mOffset == length; 260 } 261 262 void MoofParser::ParseMoov(const Box& aBox) { 263 LOG_DEBUG(Moof, "Starting."); 264 for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { 265 if (box.IsType("mvhd")) { 266 mMvhd = Mvhd(box); 267 } else if (box.IsType("trak")) { 268 ParseTrak(box); 269 } else if (box.IsType("mvex")) { 270 ParseMvex(box); 271 } 272 } 273 LOG_DEBUG(Moof, "Done."); 274 } 275 276 void MoofParser::ParseTrak(const Box& aBox) { 277 LOG_DEBUG(Trak, "Starting."); 278 Tkhd tkhd; 279 for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { 280 if (box.IsType("tkhd")) { 281 tkhd = Tkhd(box); 282 } else if (box.IsType("mdia")) { 283 if (mTrackParseMode.is<ParseAllTracks>() || 284 tkhd.mTrackId == mTrackParseMode.as<uint32_t>()) { 285 ParseMdia(box); 286 } 287 } else if (box.IsType("edts") && 288 (mTrackParseMode.is<ParseAllTracks>() || 289 tkhd.mTrackId == mTrackParseMode.as<uint32_t>())) { 290 mEdts = Edts(box); 291 } 292 } 293 LOG_DEBUG(Trak, "Done."); 294 } 295 296 void MoofParser::ParseMdia(const Box& aBox) { 297 LOG_DEBUG(Mdia, "Starting."); 298 for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { 299 if (box.IsType("mdhd")) { 300 mMdhd = Mdhd(box); 301 } else if (box.IsType("minf")) { 302 ParseMinf(box); 303 } 304 } 305 LOG_DEBUG(Mdia, "Done."); 306 } 307 308 void MoofParser::ParseMvex(const Box& aBox) { 309 LOG_DEBUG(Mvex, "Starting."); 310 for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { 311 if (box.IsType("trex")) { 312 Trex trex = Trex(box); 313 if (mTrackParseMode.is<ParseAllTracks>() || 314 trex.mTrackId == mTrackParseMode.as<uint32_t>()) { 315 mTrex = trex; 316 } 317 } 318 } 319 LOG_DEBUG(Mvex, "Done."); 320 } 321 322 void MoofParser::ParseMinf(const Box& aBox) { 323 LOG_DEBUG(Minf, "Starting."); 324 for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { 325 if (box.IsType("stbl")) { 326 ParseStbl(box); 327 } 328 } 329 LOG_DEBUG(Minf, "Done."); 330 } 331 332 void MoofParser::ParseStbl(const Box& aBox) { 333 LOG_DEBUG(Stbl, "Starting."); 334 for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { 335 if (box.IsType("stsd")) { 336 ParseStsd(box); 337 } else if (box.IsType("sgpd")) { 338 Sgpd sgpd(box); 339 if (sgpd.IsValid() && sgpd.mGroupingType == "seig") { 340 mTrackSampleEncryptionInfoEntries.Clear(); 341 if (!mTrackSampleEncryptionInfoEntries.AppendElements( 342 sgpd.mEntries, mozilla::fallible)) { 343 LOG_ERROR(Stbl, "OOM"); 344 return; 345 } 346 } 347 } else if (box.IsType("sbgp")) { 348 Sbgp sbgp(box); 349 if (sbgp.IsValid() && sbgp.mGroupingType == "seig") { 350 mTrackSampleToGroupEntries.Clear(); 351 if (!mTrackSampleToGroupEntries.AppendElements(sbgp.mEntries, 352 mozilla::fallible)) { 353 LOG_ERROR(Stbl, "OOM"); 354 return; 355 } 356 } 357 } 358 } 359 LOG_DEBUG(Stbl, "Done."); 360 } 361 362 void MoofParser::ParseStsd(const Box& aBox) { 363 LOG_DEBUG(Stsd, "Starting."); 364 if (mTrackParseMode.is<ParseAllTracks>()) { 365 // It is not a sane operation to try and map sample description boxes from 366 // multiple tracks onto the parser, which is modeled around storing metadata 367 // for a single track. 368 LOG_DEBUG(Stsd, "Early return due to multitrack parser."); 369 return; 370 } 371 MOZ_ASSERT( 372 mSampleDescriptions.IsEmpty(), 373 "Shouldn't have any sample descriptions yet when starting to parse stsd"); 374 uint32_t numberEncryptedEntries = 0; 375 for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { 376 SampleDescriptionEntry sampleDescriptionEntry{false}; 377 if (box.IsType("encv") || box.IsType("enca")) { 378 ParseEncrypted(box); 379 sampleDescriptionEntry.mIsEncryptedEntry = true; 380 numberEncryptedEntries++; 381 } 382 if (!mSampleDescriptions.AppendElement(sampleDescriptionEntry, 383 mozilla::fallible)) { 384 LOG_ERROR(Stsd, "OOM"); 385 return; 386 } 387 } 388 if (mSampleDescriptions.IsEmpty()) { 389 LOG_WARN(Stsd, 390 "No sample description entries found while parsing Stsd! This " 391 "shouldn't happen, as the spec requires one for each track!"); 392 } 393 if (numberEncryptedEntries > 1) { 394 LOG_WARN(Stsd, 395 "More than one encrypted sample description entry found while " 396 "parsing track! We don't expect this, and it will likely break " 397 "during fragment look up!"); 398 } 399 LOG_DEBUG(Stsd, 400 "Done, numberEncryptedEntries=%" PRIu32 401 ", mSampleDescriptions.Length=%zu", 402 numberEncryptedEntries, mSampleDescriptions.Length()); 403 } 404 405 void MoofParser::ParseEncrypted(const Box& aBox) { 406 LOG_DEBUG(Moof, "Starting."); 407 for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { 408 // Some MP4 files have been found to have multiple sinf boxes in the same 409 // enc* box. This does not match spec anyway, so just choose the first 410 // one that parses properly. 411 if (box.IsType("sinf")) { 412 mSinf = Sinf(box); 413 414 if (mSinf.IsValid()) { 415 break; 416 } 417 } 418 } 419 LOG_DEBUG(Moof, "Done."); 420 } 421 422 class CtsComparator { 423 public: 424 bool Equals(Sample* const aA, Sample* const aB) const { 425 return aA->mCompositionRange.start == aB->mCompositionRange.start; 426 } 427 bool LessThan(Sample* const aA, Sample* const aB) const { 428 return aA->mCompositionRange.start < aB->mCompositionRange.start; 429 } 430 }; 431 432 Moof::Moof(const Box& aBox, const TrackParseMode& aTrackParseMode, 433 const Trex& aTrex, const Mvhd& aMvhd, const Mdhd& aMdhd, 434 const Edts& aEdts, const Sinf& aSinf, const bool aIsAudio, 435 uint64_t* aDecodeTime, nsTArray<TrackEndCts>& aTracksEndCts) 436 : mRange(aBox.Range()), 437 mTfhd(aTrex), 438 // Do not reporting discontuities less than 35ms 439 mMaxRoundingError(TimeUnit::FromSeconds(0.035)) { 440 LOG_DEBUG( 441 Moof, 442 "Starting, aTrackParseMode=%s, track#=%" PRIu32 443 " (ignore if multitrack).", 444 aTrackParseMode.is<ParseAllTracks>() ? "multitrack" : "single track", 445 aTrackParseMode.is<ParseAllTracks>() ? 0 446 : aTrackParseMode.as<uint32_t>()); 447 MOZ_ASSERT(aTrackParseMode.is<ParseAllTracks>() || 448 aTrex.mTrackId == aTrackParseMode.as<uint32_t>(), 449 "If not parsing all tracks, aTrex should have the same track id " 450 "as the track being parsed."); 451 nsTArray<Box> psshBoxes; 452 for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { 453 if (box.IsType("traf")) { 454 ParseTraf(box, aTrackParseMode, aTrex, aMvhd, aMdhd, aEdts, aSinf, 455 aIsAudio, aDecodeTime); 456 } 457 if (box.IsType("pssh")) { 458 psshBoxes.AppendElement(box); 459 } 460 } 461 462 // The EME spec requires that PSSH boxes which are contiguous in the 463 // file are dispatched to the media element in a single "encrypted" event. 464 // So append contiguous boxes here. 465 for (size_t i = 0; i < psshBoxes.Length(); ++i) { 466 Box box = psshBoxes[i]; 467 if (i == 0 || box.Offset() != psshBoxes[i - 1].NextOffset()) { 468 mPsshes.AppendElement(); 469 } 470 nsTArray<uint8_t>& pssh = mPsshes.LastElement(); 471 pssh.AppendElements(std::move(box.ReadCompleteBox())); 472 } 473 474 if (IsValid()) { 475 if (mIndex.Length()) { 476 // Ensure the samples are contiguous with no gaps. 477 nsTArray<Sample*> ctsOrder; 478 for (auto& sample : mIndex) { 479 ctsOrder.AppendElement(&sample); 480 } 481 ctsOrder.Sort(CtsComparator()); 482 483 for (size_t i = 1; i < ctsOrder.Length(); i++) { 484 ctsOrder[i - 1]->mCompositionRange.end = 485 ctsOrder[i]->mCompositionRange.start; 486 } 487 488 // Ensure that there are no gaps between the first sample in this 489 // Moof and the preceeding Moof. 490 if (!ctsOrder.IsEmpty()) { 491 bool found = false; 492 // Track ID of the track we're parsing. 493 const uint32_t trackId = aTrex.mTrackId; 494 // Find the previous CTS end time of Moof preceeding the Moofs we just 495 // parsed, for the track we're parsing. 496 for (auto& prevCts : aTracksEndCts) { 497 if (prevCts.mTrackId == trackId) { 498 // We ensure there are no gaps in samples' CTS between the last 499 // sample in a Moof, and the first sample in the next Moof, if 500 // they're within these many Microseconds of each other. 501 const TimeUnit CROSS_MOOF_CTS_MERGE_THRESHOLD = 502 TimeUnit::FromMicroseconds(1); 503 // We have previously parsed a Moof for this track. Smooth the gap 504 // between samples for this track across the Moof bounary. 505 if (ctsOrder[0]->mCompositionRange.start > prevCts.mCtsEndTime && 506 ctsOrder[0]->mCompositionRange.start - prevCts.mCtsEndTime <= 507 CROSS_MOOF_CTS_MERGE_THRESHOLD) { 508 ctsOrder[0]->mCompositionRange.start = prevCts.mCtsEndTime; 509 } 510 prevCts.mCtsEndTime = ctsOrder.LastElement()->mCompositionRange.end; 511 found = true; 512 break; 513 } 514 } 515 if (!found) { 516 // We've not parsed a Moof for this track yet. Save its CTS end 517 // time for the next Moof we parse. 518 aTracksEndCts.AppendElement(TrackEndCts( 519 trackId, ctsOrder.LastElement()->mCompositionRange.end)); 520 } 521 } 522 523 // In MP4, the duration of a sample is defined as the delta between two 524 // decode timestamps. The operation above has updated the duration of each 525 // sample as a Sample's duration is mCompositionRange.end - 526 // mCompositionRange.start MSE's TrackBuffersManager expects dts that 527 // increased by the sample's duration, so we rewrite the dts accordingly. 528 TimeUnit presentationDuration = 529 ctsOrder.LastElement()->mCompositionRange.end - 530 ctsOrder[0]->mCompositionRange.start; 531 auto decodeOffset = aMdhd.ToTimeUnit(static_cast<int64_t>(*aDecodeTime) - 532 aEdts.mMediaStart); 533 auto offsetOffset = aMvhd.ToTimeUnit(aEdts.mEmptyOffset); 534 TimeUnit endDecodeTime = 535 (decodeOffset.isOk() && offsetOffset.isOk()) 536 ? decodeOffset.unwrap() + offsetOffset.unwrap() 537 : TimeUnit::Zero(aMvhd.mTimescale); 538 TimeUnit decodeDuration = endDecodeTime - mIndex[0].mDecodeTime; 539 double adjust = 0.; 540 if (!presentationDuration.IsZero()) { 541 double num = decodeDuration.ToSeconds(); 542 double denom = presentationDuration.ToSeconds(); 543 if (denom != 0.) { 544 adjust = num / denom; 545 } 546 } 547 548 TimeUnit dtsOffset = mIndex[0].mDecodeTime; 549 TimeUnit compositionDuration(0, aMvhd.mTimescale); 550 // Adjust the dts, ensuring that the new adjusted dts will never be 551 // greater than decodeTime (the next moof's decode start time). 552 for (auto& sample : mIndex) { 553 sample.mDecodeTime = dtsOffset + compositionDuration.MultDouble(adjust); 554 compositionDuration += sample.mCompositionRange.Length(); 555 } 556 mTimeRange = 557 MP4Interval<TimeUnit>(ctsOrder[0]->mCompositionRange.start, 558 ctsOrder.LastElement()->mCompositionRange.end); 559 } 560 // No need to retrieve auxiliary encryption data if we have a senc box: we 561 // won't use it in SampleIterator::GetNext() 562 if (!mSencValid) { 563 ProcessCencAuxInfo(aSinf.mDefaultEncryptionType); 564 } 565 } 566 LOG_DEBUG(Moof, "Done."); 567 } 568 569 bool Moof::GetAuxInfo(AtomType aType, 570 FallibleTArray<MediaByteRange>* aByteRanges) { 571 LOG_DEBUG(Moof, "Starting."); 572 aByteRanges->Clear(); 573 574 Saiz* saiz = nullptr; 575 for (int i = 0;; i++) { 576 if (i == mSaizs.Length()) { 577 LOG_DEBUG(Moof, "Could not find saiz matching aType. Returning false."); 578 return false; 579 } 580 if (mSaizs[i].mAuxInfoType == aType) { 581 saiz = &mSaizs[i]; 582 break; 583 } 584 } 585 Saio* saio = nullptr; 586 for (int i = 0;; i++) { 587 if (i == mSaios.Length()) { 588 LOG_DEBUG(Moof, "Could not find saio matching aType. Returning false."); 589 return false; 590 } 591 if (mSaios[i].mAuxInfoType == aType) { 592 saio = &mSaios[i]; 593 break; 594 } 595 } 596 597 if (saio->mOffsets.Length() == 1) { 598 if (!aByteRanges->SetCapacity(saiz->mSampleInfoSize.Length(), 599 mozilla::fallible)) { 600 LOG_ERROR(Moof, "OOM"); 601 return false; 602 } 603 uint64_t offset = mTfhd.mBaseDataOffset + saio->mOffsets[0]; 604 for (size_t i = 0; i < saiz->mSampleInfoSize.Length(); i++) { 605 if (!aByteRanges->AppendElement( 606 MediaByteRange(offset, offset + saiz->mSampleInfoSize[i]), 607 mozilla::fallible)) { 608 LOG_ERROR(Moof, "OOM"); 609 return false; 610 } 611 offset += saiz->mSampleInfoSize[i]; 612 } 613 LOG_DEBUG( 614 Moof, 615 "Saio has 1 entry. aByteRanges populated accordingly. Returning true."); 616 return true; 617 } 618 619 if (saio->mOffsets.Length() == saiz->mSampleInfoSize.Length()) { 620 if (!aByteRanges->SetCapacity(saiz->mSampleInfoSize.Length(), 621 mozilla::fallible)) { 622 LOG_ERROR(Moof, "OOM"); 623 return false; 624 } 625 for (size_t i = 0; i < saio->mOffsets.Length(); i++) { 626 uint64_t offset = mRange.mStart + saio->mOffsets[i]; 627 if (!aByteRanges->AppendElement( 628 MediaByteRange(offset, offset + saiz->mSampleInfoSize[i]), 629 mozilla::fallible)) { 630 LOG_ERROR(Moof, "OOM"); 631 return false; 632 } 633 } 634 LOG_DEBUG( 635 Moof, 636 "Saio and saiz have same number of entries. aByteRanges populated " 637 "accordingly. Returning true."); 638 return true; 639 } 640 641 LOG_DEBUG(Moof, 642 "Moof::GetAuxInfo could not find any Aux info, returning false."); 643 return false; 644 } 645 646 bool Moof::ProcessCencAuxInfo(AtomType aScheme) { 647 LOG_DEBUG(Moof, "Starting."); 648 FallibleTArray<MediaByteRange> cencRanges; 649 if (!GetAuxInfo(aScheme, &cencRanges) || 650 cencRanges.Length() != mIndex.Length()) { 651 LOG_DEBUG(Moof, "Couldn't find cenc aux info."); 652 return false; 653 } 654 for (int i = 0; i < cencRanges.Length(); i++) { 655 mIndex[i].mCencRange = cencRanges[i]; 656 } 657 LOG_DEBUG(Moof, "Found cenc aux info and stored on index."); 658 return true; 659 } 660 661 const CencSampleEncryptionInfoEntry* Moof::GetSampleEncryptionEntry( 662 size_t aSample, 663 const FallibleTArray<SampleToGroupEntry>* aTrackSampleToGroupEntries, 664 const FallibleTArray<CencSampleEncryptionInfoEntry>* 665 aTrackSampleEncryptionInfoEntries) const { 666 const SampleToGroupEntry* sampleToGroupEntry = nullptr; 667 668 // Default to using the sample to group entries for the fragment, otherwise 669 // fall back to the sample to group entries for the track. 670 const FallibleTArray<SampleToGroupEntry>* sampleToGroupEntries = 671 mFragmentSampleToGroupEntries.Length() != 0 672 ? &mFragmentSampleToGroupEntries 673 : aTrackSampleToGroupEntries; 674 675 if (!sampleToGroupEntries) { 676 return nullptr; 677 } 678 679 uint32_t seen = 0; 680 681 for (const SampleToGroupEntry& entry : *sampleToGroupEntries) { 682 if (seen + entry.mSampleCount > aSample) { 683 sampleToGroupEntry = &entry; 684 break; 685 } 686 seen += entry.mSampleCount; 687 } 688 689 // ISO-14496-12 Section 8.9.2.3 and 8.9.4 : group description index 690 // (1) ranges from 1 to the number of sample group entries in the track 691 // level SampleGroupDescription Box, or (2) takes the value 0 to 692 // indicate that this sample is a member of no group, in this case, the 693 // sample is associated with the default values specified in 694 // TrackEncryption Box, or (3) starts at 0x10001, i.e. the index value 695 // 1, with the value 1 in the top 16 bits, to reference fragment-local 696 // SampleGroupDescription Box. 697 698 // According to the spec, ISO-14496-12, the sum of the sample counts in this 699 // box should be equal to the total number of samples, and, if less, the 700 // reader should behave as if an extra SampleToGroupEntry existed, with 701 // groupDescriptionIndex 0. 702 703 if (!sampleToGroupEntry || sampleToGroupEntry->mGroupDescriptionIndex == 0) { 704 return nullptr; 705 } 706 707 const FallibleTArray<CencSampleEncryptionInfoEntry>* entries = 708 aTrackSampleEncryptionInfoEntries; 709 710 uint32_t groupIndex = sampleToGroupEntry->mGroupDescriptionIndex; 711 712 // If the first bit is set to a one, then we should use the sample group 713 // descriptions from the fragment. 714 if (groupIndex > SampleToGroupEntry::kFragmentGroupDescriptionIndexBase) { 715 groupIndex -= SampleToGroupEntry::kFragmentGroupDescriptionIndexBase; 716 entries = &mFragmentSampleEncryptionInfoEntries; 717 } 718 719 if (!entries) { 720 return nullptr; 721 } 722 723 // The group_index is one based. 724 return groupIndex > entries->Length() ? nullptr 725 : &entries->ElementAt(groupIndex - 1); 726 } 727 728 void Moof::ParseTraf(const Box& aBox, const TrackParseMode& aTrackParseMode, 729 const Trex& aTrex, const Mvhd& aMvhd, const Mdhd& aMdhd, 730 const Edts& aEdts, const Sinf& aSinf, const bool aIsAudio, 731 uint64_t* aDecodeTime) { 732 LOG_DEBUG( 733 Traf, 734 "Starting, aTrackParseMode=%s, track#=%" PRIu32 735 " (ignore if multitrack).", 736 aTrackParseMode.is<ParseAllTracks>() ? "multitrack" : "single track", 737 aTrackParseMode.is<ParseAllTracks>() ? 0 738 : aTrackParseMode.as<uint32_t>()); 739 MOZ_ASSERT(aDecodeTime); 740 MOZ_ASSERT(aTrackParseMode.is<ParseAllTracks>() || 741 aTrex.mTrackId == aTrackParseMode.as<uint32_t>(), 742 "If not parsing all tracks, aTrex should have the same track id " 743 "as the track being parsed."); 744 Tfdt tfdt; 745 746 for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { 747 if (box.IsType("tfhd")) { 748 mTfhd = Tfhd(box, aTrex); 749 } else if (aTrackParseMode.is<ParseAllTracks>() || 750 mTfhd.mTrackId == aTrackParseMode.as<uint32_t>()) { 751 if (box.IsType("tfdt")) { 752 tfdt = Tfdt(box); 753 } else if (box.IsType("sgpd")) { 754 Sgpd sgpd(box); 755 if (sgpd.IsValid() && sgpd.mGroupingType == "seig") { 756 mFragmentSampleEncryptionInfoEntries.Clear(); 757 if (!mFragmentSampleEncryptionInfoEntries.AppendElements( 758 sgpd.mEntries, mozilla::fallible)) { 759 LOG_ERROR(Moof, "OOM"); 760 return; 761 } 762 } 763 } else if (box.IsType("sbgp")) { 764 Sbgp sbgp(box); 765 if (sbgp.IsValid() && sbgp.mGroupingType == "seig") { 766 mFragmentSampleToGroupEntries.Clear(); 767 if (!mFragmentSampleToGroupEntries.AppendElements( 768 sbgp.mEntries, mozilla::fallible)) { 769 LOG_ERROR(Moof, "OOM"); 770 return; 771 } 772 } 773 } else if (box.IsType("saiz")) { 774 if (!mSaizs.AppendElement(Saiz(box, aSinf.mDefaultEncryptionType), 775 mozilla::fallible)) { 776 LOG_ERROR(Moof, "OOM"); 777 return; 778 } 779 } else if (box.IsType("saio")) { 780 if (!mSaios.AppendElement(Saio(box, aSinf.mDefaultEncryptionType), 781 mozilla::fallible)) { 782 LOG_ERROR(Moof, "OOM"); 783 return; 784 } 785 } 786 } 787 } 788 if (aTrackParseMode.is<uint32_t>() && 789 mTfhd.mTrackId != aTrackParseMode.as<uint32_t>()) { 790 LOG_DEBUG(Traf, 791 "Early return as not multitrack parser and track id didn't match " 792 "mTfhd.mTrackId=%" PRIu32, 793 mTfhd.mTrackId); 794 return; 795 } 796 // Second pass: search for trun boxes and senc boxes. 797 uint64_t decodeTime = 798 tfdt.IsValid() ? tfdt.mBaseMediaDecodeTime : *aDecodeTime; 799 Box sencBox; 800 for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { 801 if (box.IsType("trun")) { 802 if (ParseTrun(box, aMvhd, aMdhd, aEdts, aIsAudio, &decodeTime).isOk()) { 803 mValid = true; 804 } else { 805 LOG_WARN(Moof, "ParseTrun failed"); 806 mValid = false; 807 return; 808 } 809 } else if (box.IsType("senc")) { 810 LOG_DEBUG(Moof, "Found senc box"); 811 sencBox = box; 812 } 813 } 814 815 // senc box found: parse it. 816 // We need to parse senc boxes in another pass because we need potential sgpd 817 // and sbgp boxes to have been parsed, as they might override the IV size and 818 // as such the size of senc entries. 819 // trun box shall have been parsed as well, so mIndex has been filled. 820 if (sencBox.IsAvailable()) { 821 if (ParseSenc(sencBox, aSinf).isErr()) [[unlikely]] { 822 LOG_WARN(Moof, "ParseSenc failed"); 823 } 824 } 825 826 *aDecodeTime = decodeTime; 827 LOG_DEBUG(Traf, "Done, setting aDecodeTime=%." PRIu64 ".", decodeTime); 828 } 829 830 void Moof::FixRounding(const Moof& aMoof) { 831 TimeUnit gap = aMoof.mTimeRange.start - mTimeRange.end; 832 if (gap.IsPositive() && gap <= mMaxRoundingError) { 833 mTimeRange.end = aMoof.mTimeRange.start; 834 } 835 } 836 837 Result<Ok, nsresult> Moof::ParseSenc(const Box& aBox, const Sinf& aSinf) { 838 // If we already had a senc box, ignore following ones 839 // Not sure how likely this could be in real life 840 if (mSencValid) [[unlikely]] { 841 LOG_WARN(Moof, "Already found a valid senc box, ignoring new one"); 842 return Ok(); 843 } 844 845 BoxReader reader(aBox); 846 const uint8_t version = MOZ_TRY(reader->ReadU8()); 847 const uint32_t flags = MOZ_TRY(reader->ReadU24()); 848 const uint32_t sampleCount = MOZ_TRY(reader->ReadU32()); 849 // ISO/IEC 23001-7 §7.2: 850 // "sample_count is the number of protected samples in the containing track or 851 // track fragment. This value SHALL be either zero (0) or the total number of 852 // samples in the track or track fragment." 853 if (sampleCount == 0) { 854 LOG_DEBUG(Moof, "senc box has 0 sample_count"); 855 // Though having sample_count = 0 seems to be compliant, return without 856 // error but don't set mSencValid to true in case there is another senc box 857 // or saio/saiz auxiliary data 858 return Ok(); 859 } 860 if (sampleCount != mIndex.Length()) { 861 LOG_ERROR(Moof, "Invalid sample count in senc box: expecting %zu, got %d\n", 862 mIndex.Length(), sampleCount); 863 return Err(NS_ERROR_FAILURE); 864 } 865 866 if (version == 0) { 867 for (size_t i = 0; i < sampleCount; ++i) { 868 Sample& sample = mIndex[i]; 869 const CencSampleEncryptionInfoEntry* sampleInfo = 870 GetSampleEncryptionEntry(i); 871 uint8_t ivSize = sampleInfo ? sampleInfo->mIVSize : aSinf.mDefaultIVSize; 872 if (!reader->ReadArray(sample.mIV, ivSize)) { 873 return Err(MediaResult::Logged( 874 NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 875 RESULT_DETAIL("sample InitializationVector error"), 876 gMediaDemuxerLog)); 877 } 878 // Clear arrays, to be safe, in the (unlikely and invalid) case we started 879 // to parse a previous senc box but it failed halfway. 880 sample.mPlainSizes.Clear(); 881 sample.mEncryptedSizes.Clear(); 882 const bool useSubSampleEncryption = flags & 0x02; 883 if (useSubSampleEncryption) { 884 uint16_t subsampleCount = MOZ_TRY(reader->ReadU16()); 885 for (uint16_t i = 0; i < subsampleCount; ++i) { 886 uint16_t bytesOfClearData = MOZ_TRY(reader->ReadU16()); 887 uint32_t bytesOfProtectedData = MOZ_TRY(reader->ReadU32()); 888 sample.mPlainSizes.AppendElement(bytesOfClearData); 889 sample.mEncryptedSizes.AppendElement(bytesOfProtectedData); 890 } 891 } else { 892 // No UseSubSampleEncryption flag means the entire sample is encrypted. 893 sample.mPlainSizes.AppendElement(0); 894 sample.mEncryptedSizes.AppendElement(sample.mByteRange.Length()); 895 } 896 } 897 } else if (version == 1) { 898 // TODO 899 LOG_ERROR(Senc, "version %d not supported yet", version); 900 return Err(NS_ERROR_FAILURE); 901 } else if (version == 2) { 902 // TODO 903 LOG_ERROR(Senc, "version %d not supported yet", version); 904 return Err(NS_ERROR_FAILURE); 905 } else { 906 LOG_ERROR(Senc, "Unknown version %d", version); 907 return Err(NS_ERROR_FAILURE); 908 } 909 mSencValid = true; 910 return Ok(); 911 } 912 913 Result<Ok, nsresult> Moof::ParseTrun(const Box& aBox, const Mvhd& aMvhd, 914 const Mdhd& aMdhd, const Edts& aEdts, 915 const bool aIsAudio, 916 uint64_t* aDecodeTime) { 917 LOG_DEBUG(Trun, "Starting."); 918 if (!mTfhd.IsValid() || !aMvhd.IsValid() || !aMdhd.IsValid() || 919 !aEdts.IsValid()) { 920 LOG_WARN( 921 Moof, "Invalid dependencies: mTfhd(%d) aMvhd(%d) aMdhd(%d) aEdts(%d)", 922 mTfhd.IsValid(), aMvhd.IsValid(), aMdhd.IsValid(), !aEdts.IsValid()); 923 return Err(NS_ERROR_FAILURE); 924 } 925 926 BoxReader reader(aBox); 927 if (!reader->CanReadType<uint32_t>()) { 928 LOG_WARN(Moof, "Incomplete Box (missing flags)"); 929 return Err(NS_ERROR_FAILURE); 930 } 931 uint32_t flags = MOZ_TRY(reader->ReadU32()); 932 933 if (!reader->CanReadType<uint32_t>()) { 934 LOG_WARN(Moof, "Incomplete Box (missing sampleCount)"); 935 return Err(NS_ERROR_FAILURE); 936 } 937 uint32_t sampleCount = MOZ_TRY(reader->ReadU32()); 938 if (sampleCount == 0) { 939 LOG_DEBUG(Trun, "Trun with no samples, returning."); 940 return Ok(); 941 } 942 943 uint64_t offset = mTfhd.mBaseDataOffset; 944 if (flags & 0x01) { 945 offset += MOZ_TRY(reader->ReadU32()); 946 } 947 uint32_t firstSampleFlags = mTfhd.mDefaultSampleFlags; 948 if (flags & 0x04) { 949 firstSampleFlags = MOZ_TRY(reader->ReadU32()); 950 } 951 nsTArray<MP4Interval<TimeUnit>> timeRanges; 952 uint64_t decodeTime = *aDecodeTime; 953 954 if (!mIndex.SetCapacity(mIndex.Length() + sampleCount, fallible)) { 955 LOG_ERROR(Moof, "Out of Memory"); 956 return Err(NS_ERROR_FAILURE); 957 } 958 959 for (size_t i = 0; i < sampleCount; i++) { 960 uint32_t sampleDuration = mTfhd.mDefaultSampleDuration; 961 if (flags & 0x100) { 962 sampleDuration = MOZ_TRY(reader->ReadU32()); 963 } 964 uint32_t sampleSize = mTfhd.mDefaultSampleSize; 965 if (flags & 0x200) { 966 sampleSize = MOZ_TRY(reader->ReadU32()); 967 } 968 uint32_t sampleFlags = i ? mTfhd.mDefaultSampleFlags : firstSampleFlags; 969 if (flags & 0x400) { 970 sampleFlags = MOZ_TRY(reader->ReadU32()); 971 } 972 int32_t ctsOffset = 0; 973 if (flags & 0x800) { 974 ctsOffset = MOZ_TRY(reader->Read32()); 975 } 976 977 if (sampleSize) { 978 Sample sample; 979 sample.mByteRange = MediaByteRange(offset, offset + sampleSize); 980 offset += sampleSize; 981 982 TimeUnit decodeOffset = 983 MOZ_TRY(aMdhd.ToTimeUnit((int64_t)decodeTime - aEdts.mMediaStart)); 984 TimeUnit emptyOffset = MOZ_TRY(aMvhd.ToTimeUnit(aEdts.mEmptyOffset)); 985 sample.mDecodeTime = decodeOffset + emptyOffset; 986 TimeUnit startCts = MOZ_TRY(aMdhd.ToTimeUnit( 987 (int64_t)decodeTime + ctsOffset - aEdts.mMediaStart)); 988 TimeUnit endCts = 989 MOZ_TRY(aMdhd.ToTimeUnit((int64_t)decodeTime + ctsOffset + 990 sampleDuration - aEdts.mMediaStart)); 991 sample.mCompositionRange = 992 MP4Interval<TimeUnit>(startCts + emptyOffset, endCts + emptyOffset); 993 // Sometimes audio streams don't properly mark their samples as keyframes, 994 // because every audio sample is a keyframe. 995 sample.mSync = !(sampleFlags & 0x1010000) || aIsAudio; 996 997 MOZ_ALWAYS_TRUE(mIndex.AppendElement(sample, fallible)); 998 999 mMdatRange = mMdatRange.Span(sample.mByteRange); 1000 } 1001 decodeTime += sampleDuration; 1002 } 1003 TimeUnit roundTime = MOZ_TRY(aMdhd.ToTimeUnit(sampleCount)); 1004 mMaxRoundingError = roundTime + mMaxRoundingError; 1005 1006 *aDecodeTime = decodeTime; 1007 1008 LOG_DEBUG(Trun, "Done."); 1009 return Ok(); 1010 } 1011 1012 Tkhd::Tkhd(const Box& aBox) : mTrackId(0) { 1013 mValid = Parse(aBox).isOk(); 1014 if (!mValid) { 1015 LOG_WARN(Tkhd, "Parse failed"); 1016 } 1017 } 1018 1019 Result<Ok, nsresult> Tkhd::Parse(const Box& aBox) { 1020 BoxReader reader(aBox); 1021 uint32_t flags = MOZ_TRY(reader->ReadU32()); 1022 uint8_t version = flags >> 24; 1023 if (version == 0) { 1024 uint32_t creationTime = MOZ_TRY(reader->ReadU32()); 1025 uint32_t modificationTime = MOZ_TRY(reader->ReadU32()); 1026 mTrackId = MOZ_TRY(reader->ReadU32()); 1027 [[maybe_unused]] uint32_t reserved = MOZ_TRY(reader->ReadU32()); 1028 uint32_t duration = MOZ_TRY(reader->ReadU32()); 1029 NS_ASSERTION(!reserved, "reserved should be 0"); 1030 1031 mCreationTime = creationTime; 1032 mModificationTime = modificationTime; 1033 mDuration = duration; 1034 } else if (version == 1) { 1035 mCreationTime = MOZ_TRY(reader->ReadU64()); 1036 mModificationTime = MOZ_TRY(reader->ReadU64()); 1037 mTrackId = MOZ_TRY(reader->ReadU32()); 1038 [[maybe_unused]] uint32_t reserved = MOZ_TRY(reader->ReadU32()); 1039 NS_ASSERTION(!reserved, "reserved should be 0"); 1040 mDuration = MOZ_TRY(reader->ReadU64()); 1041 } 1042 return Ok(); 1043 } 1044 1045 Mvhd::Mvhd(const Box& aBox) 1046 : mCreationTime(0), mModificationTime(0), mTimescale(0), mDuration(0) { 1047 mValid = Parse(aBox).isOk(); 1048 if (!mValid) { 1049 LOG_WARN(Mvhd, "Parse failed"); 1050 } 1051 } 1052 1053 Result<Ok, nsresult> Mvhd::Parse(const Box& aBox) { 1054 BoxReader reader(aBox); 1055 1056 uint32_t flags = MOZ_TRY(reader->ReadU32()); 1057 uint8_t version = flags >> 24; 1058 1059 if (version == 0) { 1060 uint32_t creationTime = MOZ_TRY(reader->ReadU32()); 1061 uint32_t modificationTime = MOZ_TRY(reader->ReadU32()); 1062 mTimescale = MOZ_TRY(reader->ReadU32()); 1063 uint32_t duration = MOZ_TRY(reader->ReadU32()); 1064 mCreationTime = creationTime; 1065 mModificationTime = modificationTime; 1066 mDuration = duration; 1067 } else if (version == 1) { 1068 mCreationTime = MOZ_TRY(reader->ReadU64()); 1069 mModificationTime = MOZ_TRY(reader->ReadU64()); 1070 mTimescale = MOZ_TRY(reader->ReadU32()); 1071 mDuration = MOZ_TRY(reader->ReadU64()); 1072 } else { 1073 return Err(NS_ERROR_FAILURE); 1074 } 1075 return Ok(); 1076 } 1077 1078 Mdhd::Mdhd(const Box& aBox) : Mvhd(aBox) {} 1079 1080 Trex::Trex(const Box& aBox) 1081 : mFlags(0), 1082 mTrackId(0), 1083 mDefaultSampleDescriptionIndex(0), 1084 mDefaultSampleDuration(0), 1085 mDefaultSampleSize(0), 1086 mDefaultSampleFlags(0) { 1087 mValid = Parse(aBox).isOk(); 1088 if (!mValid) { 1089 LOG_WARN(Trex, "Parse failed"); 1090 } 1091 } 1092 1093 Result<Ok, nsresult> Trex::Parse(const Box& aBox) { 1094 BoxReader reader(aBox); 1095 1096 mFlags = MOZ_TRY(reader->ReadU32()); 1097 mTrackId = MOZ_TRY(reader->ReadU32()); 1098 mDefaultSampleDescriptionIndex = MOZ_TRY(reader->ReadU32()); 1099 mDefaultSampleDuration = MOZ_TRY(reader->ReadU32()); 1100 mDefaultSampleSize = MOZ_TRY(reader->ReadU32()); 1101 mDefaultSampleFlags = MOZ_TRY(reader->ReadU32()); 1102 1103 return Ok(); 1104 } 1105 1106 Tfhd::Tfhd(const Box& aBox, const Trex& aTrex) 1107 : Trex(aTrex), mBaseDataOffset(0) { 1108 mValid = Parse(aBox).isOk(); 1109 if (!mValid) { 1110 LOG_WARN(Tfhd, "Parse failed"); 1111 } 1112 } 1113 1114 Result<Ok, nsresult> Tfhd::Parse(const Box& aBox) { 1115 MOZ_ASSERT(aBox.IsType("tfhd")); 1116 MOZ_ASSERT(aBox.Parent()->IsType("traf")); 1117 MOZ_ASSERT(aBox.Parent()->Parent()->IsType("moof")); 1118 1119 BoxReader reader(aBox); 1120 1121 mFlags = MOZ_TRY(reader->ReadU32()); 1122 mTrackId = MOZ_TRY(reader->ReadU32()); 1123 mBaseDataOffset = aBox.Parent()->Parent()->Offset(); 1124 if (mFlags & 0x01) { 1125 mBaseDataOffset = MOZ_TRY(reader->ReadU64()); 1126 } 1127 if (mFlags & 0x02) { 1128 mDefaultSampleDescriptionIndex = MOZ_TRY(reader->ReadU32()); 1129 } 1130 if (mFlags & 0x08) { 1131 mDefaultSampleDuration = MOZ_TRY(reader->ReadU32()); 1132 } 1133 if (mFlags & 0x10) { 1134 mDefaultSampleSize = MOZ_TRY(reader->ReadU32()); 1135 } 1136 if (mFlags & 0x20) { 1137 mDefaultSampleFlags = MOZ_TRY(reader->ReadU32()); 1138 } 1139 1140 return Ok(); 1141 } 1142 1143 Tfdt::Tfdt(const Box& aBox) : mBaseMediaDecodeTime(0) { 1144 mValid = Parse(aBox).isOk(); 1145 if (!mValid) { 1146 LOG_WARN(Tfdt, "Parse failed"); 1147 } 1148 } 1149 1150 Result<Ok, nsresult> Tfdt::Parse(const Box& aBox) { 1151 BoxReader reader(aBox); 1152 1153 uint32_t flags = MOZ_TRY(reader->ReadU32()); 1154 uint8_t version = flags >> 24; 1155 if (version == 0) { 1156 mBaseMediaDecodeTime = MOZ_TRY(reader->ReadU32()); 1157 } else if (version == 1) { 1158 mBaseMediaDecodeTime = MOZ_TRY(reader->ReadU64()); 1159 } 1160 return Ok(); 1161 } 1162 1163 Edts::Edts(const Box& aBox) : mMediaStart(0), mEmptyOffset(0) { 1164 mValid = Parse(aBox).isOk(); 1165 if (!mValid) { 1166 LOG_WARN(Edts, "Parse failed"); 1167 } 1168 } 1169 1170 Result<Ok, nsresult> Edts::Parse(const Box& aBox) { 1171 Box child = aBox.FirstChild(); 1172 if (!child.IsType("elst")) { 1173 return Err(NS_ERROR_FAILURE); 1174 } 1175 1176 BoxReader reader(child); 1177 uint32_t flags = MOZ_TRY(reader->ReadU32()); 1178 uint8_t version = flags >> 24; 1179 bool emptyEntry = false; 1180 uint32_t entryCount = MOZ_TRY(reader->ReadU32()); 1181 for (uint32_t i = 0; i < entryCount; i++) { 1182 uint64_t segment_duration; 1183 int64_t media_time; 1184 if (version == 1) { 1185 segment_duration = MOZ_TRY(reader->ReadU64()); 1186 media_time = MOZ_TRY(reader->Read64()); 1187 } else { 1188 segment_duration = MOZ_TRY(reader->ReadU32()); 1189 media_time = MOZ_TRY(reader->Read32()); 1190 } 1191 if (media_time == -1 && i) { 1192 LOG_WARN(Edts, "Multiple empty edit, not handled"); 1193 } else if (media_time == -1) { 1194 if (segment_duration > std::numeric_limits<int64_t>::max()) { 1195 NS_WARNING("Segment duration higher than int64_t max."); 1196 mEmptyOffset = std::numeric_limits<int64_t>::max(); 1197 } else { 1198 mEmptyOffset = static_cast<int64_t>(segment_duration); 1199 } 1200 emptyEntry = true; 1201 } else if (i > 1 || (i > 0 && !emptyEntry)) { 1202 LOG_WARN(Edts, 1203 "More than one edit entry, not handled. A/V sync will be wrong"); 1204 break; 1205 } else { 1206 mMediaStart = media_time; 1207 } 1208 MOZ_TRY(reader->ReadU32()); // media_rate_integer and media_rate_fraction 1209 } 1210 1211 return Ok(); 1212 } 1213 1214 Saiz::Saiz(const Box& aBox, AtomType aDefaultType) 1215 : mAuxInfoType(aDefaultType), mAuxInfoTypeParameter(0) { 1216 mValid = Parse(aBox).isOk(); 1217 if (!mValid) { 1218 LOG_WARN(Saiz, "Parse failed"); 1219 } 1220 } 1221 1222 Result<Ok, nsresult> Saiz::Parse(const Box& aBox) { 1223 BoxReader reader(aBox); 1224 1225 uint32_t flags = MOZ_TRY(reader->ReadU32()); 1226 if (flags & 1) { 1227 mAuxInfoType = MOZ_TRY(reader->ReadU32()); 1228 mAuxInfoTypeParameter = MOZ_TRY(reader->ReadU32()); 1229 } 1230 uint8_t defaultSampleInfoSize = MOZ_TRY(reader->ReadU8()); 1231 uint32_t count = MOZ_TRY(reader->ReadU32()); 1232 if (defaultSampleInfoSize) { 1233 if (!mSampleInfoSize.SetLength(count, fallible)) { 1234 LOG_ERROR(Saiz, "OOM"); 1235 return Err(NS_ERROR_FAILURE); 1236 } 1237 memset(mSampleInfoSize.Elements(), defaultSampleInfoSize, 1238 mSampleInfoSize.Length()); 1239 } else { 1240 if (!reader->ReadArray(mSampleInfoSize, count)) { 1241 LOG_WARN(Saiz, "Incomplete Box (OOM or missing count:%u)", count); 1242 return Err(NS_ERROR_FAILURE); 1243 } 1244 } 1245 return Ok(); 1246 } 1247 1248 Saio::Saio(const Box& aBox, AtomType aDefaultType) 1249 : mAuxInfoType(aDefaultType), mAuxInfoTypeParameter(0) { 1250 mValid = Parse(aBox).isOk(); 1251 if (!mValid) { 1252 LOG_WARN(Saio, "Parse failed"); 1253 } 1254 } 1255 1256 Result<Ok, nsresult> Saio::Parse(const Box& aBox) { 1257 BoxReader reader(aBox); 1258 1259 uint32_t flags = MOZ_TRY(reader->ReadU32()); 1260 uint8_t version = flags >> 24; 1261 if (flags & 1) { 1262 mAuxInfoType = MOZ_TRY(reader->ReadU32()); 1263 mAuxInfoTypeParameter = MOZ_TRY(reader->ReadU32()); 1264 } 1265 1266 size_t count = MOZ_TRY(reader->ReadU32()); 1267 if (!mOffsets.SetCapacity(count, fallible)) { 1268 LOG_ERROR(Saiz, "OOM"); 1269 return Err(NS_ERROR_FAILURE); 1270 } 1271 if (version == 0) { 1272 for (size_t i = 0; i < count; i++) { 1273 uint32_t offset = MOZ_TRY(reader->ReadU32()); 1274 MOZ_ALWAYS_TRUE(mOffsets.AppendElement(offset, fallible)); 1275 } 1276 } else { 1277 for (size_t i = 0; i < count; i++) { 1278 uint64_t offset = MOZ_TRY(reader->ReadU64()); 1279 MOZ_ALWAYS_TRUE(mOffsets.AppendElement(offset, fallible)); 1280 } 1281 } 1282 return Ok(); 1283 } 1284 1285 Sbgp::Sbgp(const Box& aBox) : mGroupingTypeParam(0) { 1286 mValid = Parse(aBox).isOk(); 1287 if (!mValid) { 1288 LOG_WARN(Sbgp, "Parse failed"); 1289 } 1290 } 1291 1292 Result<Ok, nsresult> Sbgp::Parse(const Box& aBox) { 1293 BoxReader reader(aBox); 1294 1295 uint32_t flags = MOZ_TRY(reader->ReadU32()); 1296 const uint8_t version = flags >> 24; 1297 1298 mGroupingType = MOZ_TRY(reader->ReadU32()); 1299 1300 if (version == 1) { 1301 mGroupingTypeParam = MOZ_TRY(reader->ReadU32()); 1302 } 1303 1304 uint32_t count = MOZ_TRY(reader->ReadU32()); 1305 1306 for (uint32_t i = 0; i < count; i++) { 1307 uint32_t sampleCount = MOZ_TRY(reader->ReadU32()); 1308 uint32_t groupDescriptionIndex = MOZ_TRY(reader->ReadU32()); 1309 1310 SampleToGroupEntry entry(sampleCount, groupDescriptionIndex); 1311 if (!mEntries.AppendElement(entry, mozilla::fallible)) { 1312 LOG_ERROR(Sbgp, "OOM"); 1313 return Err(NS_ERROR_FAILURE); 1314 } 1315 } 1316 return Ok(); 1317 } 1318 1319 Sgpd::Sgpd(const Box& aBox) { 1320 mValid = Parse(aBox).isOk(); 1321 if (!mValid) { 1322 LOG_WARN(Sgpd, "Parse failed"); 1323 } 1324 } 1325 1326 Result<Ok, nsresult> Sgpd::Parse(const Box& aBox) { 1327 BoxReader reader(aBox); 1328 1329 uint32_t flags = MOZ_TRY(reader->ReadU32()); 1330 const uint8_t version = flags >> 24; 1331 1332 mGroupingType = MOZ_TRY(reader->ReadU32()); 1333 1334 const uint32_t entrySize = sizeof(uint32_t) + kKeyIdSize; 1335 uint32_t defaultLength = 0; 1336 1337 if (version == 1) { 1338 defaultLength = MOZ_TRY(reader->ReadU32()); 1339 if (defaultLength < entrySize && defaultLength != 0) { 1340 return Err(NS_ERROR_FAILURE); 1341 } 1342 } 1343 1344 uint32_t count = MOZ_TRY(reader->ReadU32()); 1345 1346 for (uint32_t i = 0; i < count; ++i) { 1347 if (version == 1 && defaultLength == 0) { 1348 uint32_t descriptionLength = MOZ_TRY(reader->ReadU32()); 1349 if (descriptionLength < entrySize) { 1350 return Err(NS_ERROR_FAILURE); 1351 } 1352 } 1353 1354 CencSampleEncryptionInfoEntry entry; 1355 bool valid = entry.Init(reader).isOk(); 1356 if (!valid) { 1357 return Err(NS_ERROR_FAILURE); 1358 } 1359 if (!mEntries.AppendElement(entry, mozilla::fallible)) { 1360 LOG_ERROR(Sgpd, "OOM"); 1361 return Err(NS_ERROR_FAILURE); 1362 } 1363 } 1364 return Ok(); 1365 } 1366 1367 Result<Ok, nsresult> CencSampleEncryptionInfoEntry::Init(BoxReader& aReader) { 1368 // Skip a reserved byte. 1369 MOZ_TRY(aReader->ReadU8()); 1370 1371 uint8_t pattern = MOZ_TRY(aReader->ReadU8()); 1372 mCryptByteBlock = pattern >> 4; 1373 mSkipByteBlock = pattern & 0x0f; 1374 1375 uint8_t isEncrypted = MOZ_TRY(aReader->ReadU8()); 1376 mIsEncrypted = isEncrypted != 0; 1377 1378 mIVSize = MOZ_TRY(aReader->ReadU8()); 1379 1380 // Read the key id. 1381 if (!mKeyId.SetLength(kKeyIdSize, fallible)) { 1382 LOG_ERROR(CencSampleEncryptionInfoEntry, "OOM"); 1383 return Err(NS_ERROR_FAILURE); 1384 } 1385 for (uint32_t i = 0; i < kKeyIdSize; ++i) { 1386 mKeyId.ElementAt(i) = MOZ_TRY(aReader->ReadU8()); 1387 } 1388 1389 if (mIsEncrypted) { 1390 if (mIVSize != 8 && mIVSize != 16) { 1391 return Err(NS_ERROR_FAILURE); 1392 } 1393 } else if (mIVSize != 0) { 1394 // Protected content with 0 sized IV indicates a constant IV is present. 1395 // This is used for the cbcs scheme. 1396 uint8_t constantIVSize = MOZ_TRY(aReader->ReadU8()); 1397 if (constantIVSize != 8 && constantIVSize != 16) { 1398 LOG_WARN(CencSampleEncryptionInfoEntry, 1399 "Unexpected constantIVSize: %" PRIu8, constantIVSize); 1400 return Err(NS_ERROR_FAILURE); 1401 } 1402 if (!mConsantIV.SetLength(constantIVSize, mozilla::fallible)) { 1403 LOG_ERROR(CencSampleEncryptionInfoEntry, "OOM"); 1404 return Err(NS_ERROR_FAILURE); 1405 } 1406 for (uint32_t i = 0; i < constantIVSize; ++i) { 1407 mConsantIV.ElementAt(i) = MOZ_TRY(aReader->ReadU8()); 1408 } 1409 } 1410 1411 return Ok(); 1412 } 1413 } // namespace mozilla 1414 1415 #undef LOG_DEBUG 1416 #undef LOG_WARN 1417 #undef LOG_ERROR