tor-browser

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

ContainerParser.cpp (28121B)


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