tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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