tor-browser

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

MP4Demuxer.cpp (24994B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "MP4Demuxer.h"
      8 
      9 #include <stdint.h>
     10 
     11 #include <algorithm>
     12 
     13 #include "AnnexB.h"
     14 #include "BufferStream.h"
     15 #include "H264.h"
     16 #include "H265.h"
     17 #include "MP4Decoder.h"
     18 #include "MP4Metadata.h"
     19 #include "MoofParser.h"
     20 #include "ResourceStream.h"
     21 #include "SampleIterator.h"
     22 #include "TimeUnits.h"
     23 #include "VPXDecoder.h"
     24 #include "mozilla/Span.h"
     25 #include "mozilla/StaticPrefs_media.h"
     26 #include "nsPrintfCString.h"
     27 
     28 #define LOG(arg, ...)                                                 \
     29  DDMOZ_LOG(gMediaDemuxerLog, mozilla::LogLevel::Debug, "::%s: " arg, \
     30            __func__, ##__VA_ARGS__)
     31 
     32 namespace mozilla {
     33 
     34 using TimeUnit = media::TimeUnit;
     35 using TimeInterval = media::TimeInterval;
     36 using TimeIntervals = media::TimeIntervals;
     37 
     38 DDLoggedTypeDeclNameAndBase(MP4TrackDemuxer, MediaTrackDemuxer);
     39 
     40 class MP4TrackDemuxer : public MediaTrackDemuxer,
     41                        public DecoderDoctorLifeLogger<MP4TrackDemuxer> {
     42 public:
     43  MP4TrackDemuxer(MediaResource* aResource, UniquePtr<TrackInfo>&& aInfo,
     44                  const IndiceWrapper& aIndices, uint32_t aTimeScale);
     45 
     46  UniquePtr<TrackInfo> GetInfo() const override;
     47 
     48  RefPtr<SeekPromise> Seek(const TimeUnit& aTime) override;
     49 
     50  RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override;
     51 
     52  void Reset() override;
     53 
     54  nsresult GetNextRandomAccessPoint(TimeUnit* aTime) override;
     55 
     56  RefPtr<SkipAccessPointPromise> SkipToNextRandomAccessPoint(
     57      const TimeUnit& aTimeThreshold) override;
     58 
     59  TimeIntervals GetBuffered() override;
     60 
     61  void NotifyDataRemoved();
     62  void NotifyDataArrived();
     63 
     64 private:
     65  Result<already_AddRefed<MediaRawData>, MediaResult> GetNextSample();
     66  void EnsureUpToDateIndex();
     67  void SetNextKeyFrameTime();
     68  RefPtr<MediaResource> mResource;
     69  RefPtr<ResourceStream> mStream;
     70  UniquePtr<TrackInfo> mInfo;
     71  RefPtr<MP4SampleIndex> mIndex;
     72  UniquePtr<SampleIterator> mIterator;
     73  Maybe<TimeUnit> mNextKeyframeTime;
     74  // Queued samples extracted by the demuxer, but not yet returned.
     75  RefPtr<MediaRawData> mQueuedSample;
     76  bool mNeedReIndex;
     77  enum CodecType { kH264, kVP9, kAAC, kHEVC, kOther } mType = kOther;
     78 };
     79 
     80 MP4Demuxer::MP4Demuxer(MediaResource* aResource)
     81    : mResource(aResource),
     82      mStream(new ResourceStream(aResource)),
     83      mIsSeekable(false) {
     84  DDLINKCHILD("resource", aResource);
     85  DDLINKCHILD("stream", mStream.get());
     86 }
     87 
     88 RefPtr<MP4Demuxer::InitPromise> MP4Demuxer::Init() {
     89  AutoPinned<ResourceStream> stream(mStream);
     90 
     91  // 'result' will capture the first warning, if any.
     92  MediaResult result{NS_OK};
     93 
     94  MP4Metadata::ResultAndByteBuffer initData = MP4Metadata::Metadata(stream);
     95  if (!initData.Ref()) {
     96    return InitPromise::CreateAndReject(
     97        NS_FAILED(initData.Result())
     98            ? std::move(initData.Result())
     99            : MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
    100                          RESULT_DETAIL("Invalid MP4 metadata or OOM")),
    101        __func__);
    102  } else if (NS_FAILED(initData.Result()) && result == NS_OK) {
    103    result = std::move(initData.Result());
    104  }
    105 
    106  RefPtr<BufferStream> bufferstream = new BufferStream(initData.Ref());
    107 
    108  MP4Metadata metadata{bufferstream};
    109  DDLINKCHILD("metadata", &metadata);
    110  nsresult rv = metadata.Parse();
    111  if (NS_FAILED(rv)) {
    112    return InitPromise::CreateAndReject(
    113        MediaResult(rv, RESULT_DETAIL("Parse MP4 metadata failed")), __func__);
    114  }
    115 
    116  auto audioTrackCount = metadata.GetNumberTracks(TrackInfo::kAudioTrack);
    117  if (audioTrackCount.Ref() == MP4Metadata::NumberTracksError()) {
    118    if (StaticPrefs::media_playback_warnings_as_errors()) {
    119      return InitPromise::CreateAndReject(
    120          MediaResult(
    121              NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
    122              RESULT_DETAIL("Invalid audio track (%s)",
    123                            audioTrackCount.Result().Description().get())),
    124          __func__);
    125    }
    126    audioTrackCount.Ref() = 0;
    127  }
    128 
    129  auto videoTrackCount = metadata.GetNumberTracks(TrackInfo::kVideoTrack);
    130  if (videoTrackCount.Ref() == MP4Metadata::NumberTracksError()) {
    131    if (StaticPrefs::media_playback_warnings_as_errors()) {
    132      return InitPromise::CreateAndReject(
    133          MediaResult(
    134              NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
    135              RESULT_DETAIL("Invalid video track (%s)",
    136                            videoTrackCount.Result().Description().get())),
    137          __func__);
    138    }
    139    videoTrackCount.Ref() = 0;
    140  }
    141 
    142  if (audioTrackCount.Ref() == 0 && videoTrackCount.Ref() == 0) {
    143    return InitPromise::CreateAndReject(
    144        MediaResult(
    145            NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
    146            RESULT_DETAIL("No MP4 audio (%s) or video (%s) tracks",
    147                          audioTrackCount.Result().Description().get(),
    148                          videoTrackCount.Result().Description().get())),
    149        __func__);
    150  }
    151 
    152  if (NS_FAILED(audioTrackCount.Result()) && result == NS_OK) {
    153    result = std::move(audioTrackCount.Result());
    154  }
    155  if (NS_FAILED(videoTrackCount.Result()) && result == NS_OK) {
    156    result = std::move(videoTrackCount.Result());
    157  }
    158 
    159  if (audioTrackCount.Ref() != 0) {
    160    for (size_t i = 0; i < audioTrackCount.Ref(); i++) {
    161      MP4Metadata::ResultAndTrackInfo info =
    162          metadata.GetTrackInfo(TrackInfo::kAudioTrack, i);
    163      if (!info.Ref()) {
    164        if (StaticPrefs::media_playback_warnings_as_errors()) {
    165          return InitPromise::CreateAndReject(
    166              MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
    167                          RESULT_DETAIL("Invalid MP4 audio track (%s)",
    168                                        info.Result().Description().get())),
    169              __func__);
    170        }
    171        if (result == NS_OK) {
    172          result =
    173              MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
    174                          RESULT_DETAIL("Invalid MP4 audio track (%s)",
    175                                        info.Result().Description().get()));
    176        }
    177        continue;
    178      } else if (NS_FAILED(info.Result()) && result == NS_OK) {
    179        result = std::move(info.Result());
    180      }
    181      MP4Metadata::ResultAndIndice indices =
    182          metadata.GetTrackIndice(info.Ref()->mTrackId);
    183      if (!indices.Ref()) {
    184        if (NS_FAILED(info.Result()) && result == NS_OK) {
    185          result = std::move(indices.Result());
    186        }
    187        continue;
    188      }
    189      LOG("Created audio track demuxer for info (%s)",
    190          info.Ref()->ToString().get());
    191      RefPtr<MP4TrackDemuxer> demuxer =
    192          new MP4TrackDemuxer(mResource, std::move(info.Ref()),
    193                              *indices.Ref().get(), info.Ref()->mTimeScale);
    194      DDLINKCHILD("audio demuxer", demuxer.get());
    195      mAudioDemuxers.AppendElement(std::move(demuxer));
    196    }
    197  }
    198 
    199  if (videoTrackCount.Ref() != 0) {
    200    for (size_t i = 0; i < videoTrackCount.Ref(); i++) {
    201      MP4Metadata::ResultAndTrackInfo info =
    202          metadata.GetTrackInfo(TrackInfo::kVideoTrack, i);
    203      if (!info.Ref()) {
    204        if (StaticPrefs::media_playback_warnings_as_errors()) {
    205          return InitPromise::CreateAndReject(
    206              MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
    207                          RESULT_DETAIL("Invalid MP4 video track (%s)",
    208                                        info.Result().Description().get())),
    209              __func__);
    210        }
    211        if (result == NS_OK) {
    212          result =
    213              MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
    214                          RESULT_DETAIL("Invalid MP4 video track (%s)",
    215                                        info.Result().Description().get()));
    216        }
    217        continue;
    218      } else if (NS_FAILED(info.Result()) && result == NS_OK) {
    219        result = std::move(info.Result());
    220      }
    221      MP4Metadata::ResultAndIndice indices =
    222          metadata.GetTrackIndice(info.Ref()->mTrackId);
    223      if (!indices.Ref()) {
    224        if (NS_FAILED(info.Result()) && result == NS_OK) {
    225          result = std::move(indices.Result());
    226        }
    227        continue;
    228      }
    229      LOG("Created video track demuxer for info (%s)",
    230          info.Ref()->ToString().get());
    231      RefPtr<MP4TrackDemuxer> demuxer =
    232          new MP4TrackDemuxer(mResource, std::move(info.Ref()),
    233                              *indices.Ref().get(), info.Ref()->mTimeScale);
    234      DDLINKCHILD("video demuxer", demuxer.get());
    235      mVideoDemuxers.AppendElement(std::move(demuxer));
    236    }
    237  }
    238 
    239  MP4Metadata::ResultAndCryptoFile cryptoFile = metadata.Crypto();
    240  if (NS_FAILED(cryptoFile.Result()) && result == NS_OK) {
    241    result = std::move(cryptoFile.Result());
    242  }
    243  MOZ_ASSERT(cryptoFile.Ref());
    244  if (cryptoFile.Ref()->valid) {
    245    const nsTArray<PsshInfo>& psshs = cryptoFile.Ref()->pssh;
    246    for (uint32_t i = 0; i < psshs.Length(); i++) {
    247      mCryptoInitData.AppendElements(psshs[i].data);
    248    }
    249  }
    250 
    251  mIsSeekable = metadata.CanSeek();
    252 
    253  return InitPromise::CreateAndResolve(result, __func__);
    254 }
    255 
    256 uint32_t MP4Demuxer::GetNumberTracks(TrackInfo::TrackType aType) const {
    257  switch (aType) {
    258    case TrackInfo::kAudioTrack:
    259      return uint32_t(mAudioDemuxers.Length());
    260    case TrackInfo::kVideoTrack:
    261      return uint32_t(mVideoDemuxers.Length());
    262    default:
    263      return 0;
    264  }
    265 }
    266 
    267 already_AddRefed<MediaTrackDemuxer> MP4Demuxer::GetTrackDemuxer(
    268    TrackInfo::TrackType aType, uint32_t aTrackNumber) {
    269  switch (aType) {
    270    case TrackInfo::kAudioTrack:
    271      if (aTrackNumber >= uint32_t(mAudioDemuxers.Length())) {
    272        return nullptr;
    273      }
    274      return RefPtr<MediaTrackDemuxer>(mAudioDemuxers[aTrackNumber]).forget();
    275    case TrackInfo::kVideoTrack:
    276      if (aTrackNumber >= uint32_t(mVideoDemuxers.Length())) {
    277        return nullptr;
    278      }
    279      return RefPtr<MediaTrackDemuxer>(mVideoDemuxers[aTrackNumber]).forget();
    280    default:
    281      return nullptr;
    282  }
    283 }
    284 
    285 bool MP4Demuxer::IsSeekable() const { return mIsSeekable; }
    286 
    287 void MP4Demuxer::NotifyDataArrived() {
    288  for (auto& dmx : mAudioDemuxers) {
    289    dmx->NotifyDataArrived();
    290  }
    291  for (auto& dmx : mVideoDemuxers) {
    292    dmx->NotifyDataArrived();
    293  }
    294 }
    295 
    296 void MP4Demuxer::NotifyDataRemoved() {
    297  for (auto& dmx : mAudioDemuxers) {
    298    dmx->NotifyDataRemoved();
    299  }
    300  for (auto& dmx : mVideoDemuxers) {
    301    dmx->NotifyDataRemoved();
    302  }
    303 }
    304 
    305 UniquePtr<EncryptionInfo> MP4Demuxer::GetCrypto() {
    306  UniquePtr<EncryptionInfo> crypto;
    307  if (!mCryptoInitData.IsEmpty()) {
    308    crypto.reset(new EncryptionInfo{});
    309    crypto->AddInitData(u"cenc"_ns, mCryptoInitData);
    310  }
    311  return crypto;
    312 }
    313 
    314 MP4TrackDemuxer::MP4TrackDemuxer(MediaResource* aResource,
    315                                 UniquePtr<TrackInfo>&& aInfo,
    316                                 const IndiceWrapper& aIndices,
    317                                 uint32_t aTimeScale)
    318    : mResource(aResource),
    319      mStream(new ResourceStream(aResource)),
    320      mInfo(std::move(aInfo)),
    321      mIndex(new MP4SampleIndex(aIndices, mStream, mInfo->mTrackId,
    322                                mInfo->IsAudio(), aTimeScale)),
    323      mIterator(MakeUnique<SampleIterator>(mIndex)),
    324      mNeedReIndex(true) {
    325  EnsureUpToDateIndex();  // Force update of index
    326 
    327  VideoInfo* videoInfo = mInfo->GetAsVideoInfo();
    328  AudioInfo* audioInfo = mInfo->GetAsAudioInfo();
    329  if (videoInfo && MP4Decoder::IsH264(mInfo->mMimeType)) {
    330    mType = kH264;
    331    RefPtr<MediaByteBuffer> extraData = videoInfo->mExtraData;
    332    SPSData spsdata;
    333    if (H264::DecodeSPSFromExtraData(extraData, spsdata) &&
    334        spsdata.pic_width > 0 && spsdata.pic_height > 0 &&
    335        H264::EnsureSPSIsSane(spsdata)) {
    336      videoInfo->mImage.width = spsdata.pic_width;
    337      videoInfo->mImage.height = spsdata.pic_height;
    338      videoInfo->mDisplay.width = spsdata.display_width;
    339      videoInfo->mDisplay.height = spsdata.display_height;
    340    }
    341  } else if (videoInfo && VPXDecoder::IsVP9(mInfo->mMimeType)) {
    342    mType = kVP9;
    343  } else if (audioInfo && MP4Decoder::IsAAC(mInfo->mMimeType)) {
    344    mType = kAAC;
    345  } else if (videoInfo && MP4Decoder::IsHEVC(mInfo->mMimeType)) {
    346    mType = kHEVC;
    347    if (auto rv = H265::DecodeSPSFromHVCCExtraData(videoInfo->mExtraData);
    348        rv.isOk()) {
    349      const auto sps = rv.unwrap();
    350      videoInfo->mImage.width = sps.GetImageSize().Width();
    351      videoInfo->mImage.height = sps.GetImageSize().Height();
    352      videoInfo->mDisplay.width = sps.GetDisplaySize().Width();
    353      videoInfo->mDisplay.height = sps.GetDisplaySize().Height();
    354    }
    355  }
    356 }
    357 
    358 UniquePtr<TrackInfo> MP4TrackDemuxer::GetInfo() const { return mInfo->Clone(); }
    359 
    360 void MP4TrackDemuxer::EnsureUpToDateIndex() {
    361  if (!mNeedReIndex) {
    362    return;
    363  }
    364  AutoPinned<MediaResource> resource(mResource);
    365  MediaByteRangeSet byteRanges;
    366  nsresult rv = resource->GetCachedRanges(byteRanges);
    367  if (NS_FAILED(rv)) {
    368    return;
    369  }
    370  mIndex->UpdateMoofIndex(byteRanges);
    371  mNeedReIndex = false;
    372 }
    373 
    374 RefPtr<MP4TrackDemuxer::SeekPromise> MP4TrackDemuxer::Seek(
    375    const TimeUnit& aTime) {
    376  auto seekTime = aTime;
    377  mQueuedSample = nullptr;
    378 
    379  mIterator->Seek(seekTime);
    380 
    381 #ifdef MOZ_APPLEMEDIA
    382  bool hasSeenValidSamples = false, seekingFromFirstSyncSample = false;
    383 #endif
    384  // Check what time we actually seeked to.
    385  do {
    386    auto next = GetNextSample();
    387    if (next.isErr()) {
    388      auto error = next.unwrapErr();
    389 #ifdef MOZ_APPLEMEDIA
    390      // On macOS, only IDR frames are marked as key frames. This means some
    391      // sync samples (e.g., Recovery SEI or I-slices) are not treated as key
    392      // frames, as they may not be supported by Apple’s VideoToolbox H.264
    393      // decoder. If we have seen samples but found no key frame, it indicates
    394      // we need to look further back for an earlier sample where an IDR frame
    395      // should exist per spec. In that case, we retry from the first sync
    396      // sample in the GOP, which is typically an IDR frame. This situation
    397      // suggests a poorly muxed file that wastes seek time, but unfortunately
    398      // such cases do occur in real-world content.
    399      if (mType == kH264 && error == NS_ERROR_DOM_MEDIA_END_OF_STREAM &&
    400          hasSeenValidSamples && !seekingFromFirstSyncSample) {
    401        LOG("Can not find a key frame from the closet sync sample, try again "
    402            "from the first sync sample");
    403        seekingFromFirstSyncSample = true;
    404        mIterator->Seek(seekTime, SampleIterator::SyncSampleMode::First);
    405        continue;
    406      }
    407 #endif
    408      return SeekPromise::CreateAndReject(error, __func__);
    409    }
    410    RefPtr<MediaRawData> sample = next.unwrap();
    411    if (!sample->Size()) {
    412      // This sample can't be decoded, continue searching.
    413      continue;
    414    }
    415    if (sample->mKeyframe) {
    416      MOZ_DIAGNOSTIC_ASSERT(sample->HasValidTime());
    417      mQueuedSample = sample;
    418      seekTime = mQueuedSample->mTime;
    419    }
    420 #ifdef MOZ_APPLEMEDIA
    421    hasSeenValidSamples = true;
    422 #endif
    423  } while (!mQueuedSample);
    424 
    425  SetNextKeyFrameTime();
    426 
    427  return SeekPromise::CreateAndResolve(seekTime, __func__);
    428 }
    429 
    430 Result<already_AddRefed<MediaRawData>, MediaResult>
    431 MP4TrackDemuxer::GetNextSample() {
    432  auto next = mIterator->GetNext();
    433  if (next.isErr()) {
    434    return next;
    435  }
    436  RefPtr<MediaRawData> sample = next.unwrap();
    437 
    438  if (mInfo->GetAsVideoInfo()) {
    439    sample->mExtraData = mInfo->GetAsVideoInfo()->mExtraData;
    440    if (mType == kH264 && !sample->mCrypto.IsEncrypted()) {
    441      H264::FrameType type = H264::GetFrameType(sample);
    442      switch (type) {
    443        case H264::FrameType::I_FRAME_IDR:
    444        case H264::FrameType::I_FRAME_OTHER:
    445        case H264::FrameType::OTHER: {
    446          bool keyframe = type == H264::FrameType::I_FRAME_IDR;
    447 #ifndef MOZ_APPLEMEDIA
    448          // The Apple VideoToolbox H.264 decoder could return a bad data error
    449          // if a non-IDR I-frame is provided as the first sample to the decoder
    450          // after seeking. Therefore, only IDR frames should be marked as key
    451          // frames.
    452          keyframe = keyframe || type == H264::FrameType::I_FRAME_OTHER;
    453 #endif
    454          if (sample->mKeyframe != keyframe) {
    455            NS_WARNING(nsPrintfCString("Frame incorrectly marked as %skeyframe "
    456                                       "@ pts:%" PRId64 " dur:%" PRId64
    457                                       " dts:%" PRId64,
    458                                       keyframe ? "" : "non-",
    459                                       sample->mTime.ToMicroseconds(),
    460                                       sample->mDuration.ToMicroseconds(),
    461                                       sample->mTimecode.ToMicroseconds())
    462                           .get());
    463            sample->mKeyframe = keyframe;
    464          }
    465          break;
    466        }
    467        case H264::FrameType::INVALID:
    468          NS_WARNING(nsPrintfCString("Invalid H264 frame @ pts:%" PRId64
    469                                     " dur:%" PRId64 " dts:%" PRId64,
    470                                     sample->mTime.ToMicroseconds(),
    471                                     sample->mDuration.ToMicroseconds(),
    472                                     sample->mTimecode.ToMicroseconds())
    473                         .get());
    474          // We could reject the sample now, however demuxer errors are fatal.
    475          // So we keep the invalid frame, relying on the H264 decoder to
    476          // handle the error later.
    477          // TODO: make demuxer errors non-fatal.
    478          break;
    479      }
    480    } else if (mType == kVP9 && !sample->mCrypto.IsEncrypted()) {
    481      bool keyframe = VPXDecoder::IsKeyframe(
    482          Span<const uint8_t>(sample->Data(), sample->Size()),
    483          VPXDecoder::Codec::VP9);
    484      if (sample->mKeyframe != keyframe) {
    485        NS_WARNING(nsPrintfCString(
    486                       "Frame incorrectly marked as %skeyframe "
    487                       "@ pts:%" PRId64 " dur:%" PRId64 " dts:%" PRId64,
    488                       keyframe ? "" : "non-", sample->mTime.ToMicroseconds(),
    489                       sample->mDuration.ToMicroseconds(),
    490                       sample->mTimecode.ToMicroseconds())
    491                       .get());
    492        sample->mKeyframe = keyframe;
    493      }
    494    }
    495  }
    496 
    497  // Adjust trimming information if needed.
    498  if (mInfo->GetAsAudioInfo()) {
    499    AudioInfo* info = mInfo->GetAsAudioInfo();
    500    TimeUnit originalPts = sample->mTime;
    501    TimeUnit originalEnd = sample->GetEndTime();
    502    if (sample->mTime.IsNegative()) {
    503      sample->mTime = TimeUnit::Zero(originalPts);
    504      sample->mDuration = std::max(TimeUnit::Zero(sample->mTime),
    505                                   originalPts + sample->mDuration);
    506      sample->mOriginalPresentationWindow =
    507          Some(TimeInterval{originalPts, originalEnd});
    508    }
    509    // The demuxer only knows the presentation time of the packet, not the
    510    // actual number of samples that will be decoded from this packet.
    511    // However we need to trim the last packet to the correct duration.
    512    // Find the actual size of the decoded packet to know how many samples to
    513    // trim. This only works because the packet size are constant.
    514    TimeUnit totalMediaDurationIncludingTrimming =
    515        info->mDuration - info->mMediaTime;
    516    if (mType == kAAC &&
    517        sample->GetEndTime() >= totalMediaDurationIncludingTrimming &&
    518        totalMediaDurationIncludingTrimming.IsPositive()) {
    519      // Seek backward a bit.
    520      mIterator->Seek(sample->mTime - sample->mDuration);
    521      RefPtr<MediaRawData> previousSample =
    522          mIterator->GetNext().unwrapOr(nullptr);
    523      if (previousSample) {
    524        TimeInterval fullPacketDuration{previousSample->mTime,
    525                                        previousSample->GetEndTime()};
    526        sample->mOriginalPresentationWindow = Some(TimeInterval{
    527            originalPts, originalPts + fullPacketDuration.Length()});
    528      }
    529      // Seek back so we're back at the original location -- there's no packet
    530      // left anyway.
    531      mIterator->Seek(sample->mTime);
    532      RefPtr<MediaRawData> dummy = mIterator->GetNext().unwrapOr(nullptr);
    533    }
    534  }
    535 
    536  if (MOZ_LOG_TEST(gMediaDemuxerLog, LogLevel::Verbose)) {
    537    bool isAudio = mInfo->GetAsAudioInfo();
    538    TimeUnit originalStart = TimeUnit::Invalid();
    539    TimeUnit originalEnd = TimeUnit::Invalid();
    540    if (sample->mOriginalPresentationWindow) {
    541      originalStart = sample->mOriginalPresentationWindow->mStart;
    542      originalEnd = sample->mOriginalPresentationWindow->mEnd;
    543    }
    544    LOG("%s packet demuxed (track id: %d): [%s,%s], duration: %s (original "
    545        "time: [%s,%s])",
    546        isAudio ? "Audio" : "Video", mInfo->mTrackId,
    547        sample->mTime.ToString().get(), sample->GetEndTime().ToString().get(),
    548        sample->mDuration.ToString().get(), originalStart.ToString().get(),
    549        originalEnd.ToString().get());
    550  }
    551 
    552  return sample.forget();
    553 }
    554 
    555 RefPtr<MP4TrackDemuxer::SamplesPromise> MP4TrackDemuxer::GetSamples(
    556    int32_t aNumSamples) {
    557  EnsureUpToDateIndex();
    558  RefPtr<SamplesHolder> samples = new SamplesHolder;
    559  if (!aNumSamples) {
    560    return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
    561                                           __func__);
    562  }
    563 
    564  if (mQueuedSample) {
    565    NS_ASSERTION(mQueuedSample->mKeyframe, "mQueuedSample must be a keyframe");
    566    samples->AppendSample(std::move(mQueuedSample));
    567    MOZ_ASSERT(!mQueuedSample);
    568    aNumSamples--;
    569  }
    570  while (aNumSamples) {
    571    auto next = GetNextSample();
    572    if (next.isErr()) {
    573      nsresult rv = next.inspectErr().Code();
    574      if ((rv != NS_ERROR_DOM_MEDIA_END_OF_STREAM &&
    575           rv != NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) ||
    576          samples->GetSamples().IsEmpty()) {
    577        return SamplesPromise::CreateAndReject(next.unwrapErr(), __func__);
    578      }
    579      break;
    580    }
    581    RefPtr<MediaRawData> sample = next.unwrap();
    582    if (!sample->Size()) {
    583      continue;
    584    }
    585    MOZ_DIAGNOSTIC_ASSERT(sample->HasValidTime());
    586    samples->AppendSample(std::move(sample));
    587    aNumSamples--;
    588  }
    589 
    590  if (mNextKeyframeTime.isNothing() ||
    591      samples->GetSamples().LastElement()->mTime >= mNextKeyframeTime.value()) {
    592    SetNextKeyFrameTime();
    593  }
    594  return SamplesPromise::CreateAndResolve(samples, __func__);
    595 }
    596 
    597 void MP4TrackDemuxer::SetNextKeyFrameTime() {
    598  mNextKeyframeTime.reset();
    599  TimeUnit frameTime = mIterator->GetNextKeyframeTime();
    600  if (frameTime.IsValid()) {
    601    mNextKeyframeTime.emplace(frameTime);
    602  }
    603 }
    604 
    605 void MP4TrackDemuxer::Reset() {
    606  mQueuedSample = nullptr;
    607  // TODO: verify this
    608  mIterator->Seek(TimeUnit::FromNegativeInfinity());
    609  SetNextKeyFrameTime();
    610 }
    611 
    612 nsresult MP4TrackDemuxer::GetNextRandomAccessPoint(TimeUnit* aTime) {
    613  if (mNextKeyframeTime.isNothing()) {
    614    // There's no next key frame.
    615    *aTime = TimeUnit::FromInfinity();
    616  } else {
    617    *aTime = mNextKeyframeTime.value();
    618  }
    619  return NS_OK;
    620 }
    621 
    622 RefPtr<MP4TrackDemuxer::SkipAccessPointPromise>
    623 MP4TrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold) {
    624  mQueuedSample = nullptr;
    625  // Loop until we reach the next keyframe after the threshold.
    626  uint32_t parsed = 0;
    627  Maybe<SkipFailureHolder> failure;
    628  while (true) {
    629    auto next = GetNextSample();
    630    if (next.isErr()) {
    631      failure.emplace(next.unwrapErr(), parsed);
    632      break;
    633    }
    634    RefPtr<MediaRawData> sample = next.unwrap();
    635    parsed++;
    636    MOZ_DIAGNOSTIC_ASSERT(sample->HasValidTime());
    637    if (sample->mKeyframe && sample->mTime >= aTimeThreshold) {
    638      mQueuedSample = sample;
    639      break;
    640    }
    641  }
    642  SetNextKeyFrameTime();
    643  if (failure.isSome()) {
    644    return SkipAccessPointPromise::CreateAndReject(failure.extract(), __func__);
    645  }
    646  return SkipAccessPointPromise::CreateAndResolve(parsed, __func__);
    647 }
    648 
    649 TimeIntervals MP4TrackDemuxer::GetBuffered() {
    650  EnsureUpToDateIndex();
    651  AutoPinned<MediaResource> resource(mResource);
    652  MediaByteRangeSet byteRanges;
    653  nsresult rv = resource->GetCachedRanges(byteRanges);
    654 
    655  if (NS_FAILED(rv)) {
    656    return TimeIntervals();
    657  }
    658 
    659  TimeIntervals timeRanges = mIndex->ConvertByteRangesToTimeRanges(byteRanges);
    660  if (AudioInfo* info = mInfo->GetAsAudioInfo(); info) {
    661    // Trim as in GetNextSample().
    662    TimeUnit totalMediaDurationIncludingTrimming =
    663        info->mDuration - info->mMediaTime;
    664    auto end = TimeUnit::FromInfinity();
    665    if (mType == kAAC && totalMediaDurationIncludingTrimming.IsPositive()) {
    666      end = info->mDuration;
    667    }
    668    if (timeRanges.GetStart().IsNegative() || timeRanges.GetEnd() > end) {
    669      TimeInterval trimming(TimeUnit::Zero(timeRanges.GetStart()), end);
    670      timeRanges = timeRanges.Intersection(trimming);
    671    }
    672  }
    673 
    674  return timeRanges;
    675 }
    676 
    677 void MP4TrackDemuxer::NotifyDataArrived() { mNeedReIndex = true; }
    678 
    679 void MP4TrackDemuxer::NotifyDataRemoved() {
    680  AutoPinned<MediaResource> resource(mResource);
    681  MediaByteRangeSet byteRanges;
    682  nsresult rv = resource->GetCachedRanges(byteRanges);
    683  if (NS_FAILED(rv)) {
    684    return;
    685  }
    686  mIndex->UpdateMoofIndex(byteRanges, true /* can evict */);
    687  mNeedReIndex = false;
    688 }
    689 
    690 }  // namespace mozilla
    691 
    692 #undef LOG