Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/oox/qa/unit/data/   (LibreOffice Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 17 kB image not shown  

Impressum MP4Demuxer.cpp

  Sprache: C
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "MP4Demuxer.h"

#include <stdint.h>

#include <algorithm>

#include "AnnexB.h"
#include "BufferStream.h"
#include "H264.h"
#include "H265.h"
#include "MP4Decoder.h"
#include "MP4Metadata.h"
#include "MoofParser.h"
#include "ResourceStream.h"
#include "SampleIterator.h"
#include "TimeUnits.h"
#include "VPXDecoder.h"
#include "mozilla/Span.h"
#include "mozilla/StaticPrefs_media.h"
#include "nsPrintfCString.h"

#define LOG(arg, ...)                                                 \
  DDMOZ_LOG(gMediaDemuxerLog, mozilla::LogLevel::Debug, "::%s: " arg, \
            __func__, ##__VA_ARGS__)

namespace mozilla {

using TimeUnit = media::TimeUnit;
using TimeInterval = media::TimeInterval;
using TimeIntervals = media::TimeIntervals;

DDLoggedTypeDeclNameAndBase(MP4TrackDemuxer, MediaTrackDemuxer);

class MP4TrackDemuxer : public MediaTrackDemuxer,
                        public DecoderDoctorLifeLogger<MP4TrackDemuxer> {
 public:
  MP4TrackDemuxer(MediaResource* aResource, UniquePtr<TrackInfo>&& aInfo,
                  const IndiceWrapper& aIndices, uint32_t aTimeScale);

  UniquePtr<TrackInfo> GetInfo() const override;

  RefPtr<SeekPromise> Seek(const TimeUnit& aTime) override;

  RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override;

  void Reset() override;

  nsresult GetNextRandomAccessPoint(TimeUnit* aTime) override;

  RefPtr<SkipAccessPointPromise> SkipToNextRandomAccessPoint(
      const TimeUnit& aTimeThreshold) override;

  TimeIntervals GetBuffered() override;

  void NotifyDataRemoved();
  void NotifyDataArrived();

 private:
  Result<already_AddRefed<MediaRawData>, MediaResult> GetNextSample();
  void EnsureUpToDateIndex();
  void SetNextKeyFrameTime();
  RefPtr<MediaResource> mResource;
  RefPtr<ResourceStream> mStream;
  UniquePtr<TrackInfo> mInfo;
  RefPtr<MP4SampleIndex> mIndex;
  UniquePtr<SampleIterator> mIterator;
  Maybe<TimeUnit> mNextKeyframeTime;
  // Queued samples extracted by the demuxer, but not yet returned.
  RefPtr<MediaRawData> mQueuedSample;
  bool mNeedReIndex;
  enum CodecType { kH264, kVP9, kAAC, kHEVC, kOther } mType = kOther;
};

MP4Demuxer::MP4Demuxer(MediaResource* aResource)
    : mResource(aResource),
      mStream(new ResourceStream(aResource)),
      mIsSeekable(false) {
  DDLINKCHILD("resource", aResource);
  DDLINKCHILD("stream", mStream.get());
}

RefPtr<MP4Demuxer::InitPromise> MP4Demuxer::Init() {
  AutoPinned<ResourceStream> stream(mStream);

  // 'result' will capture the first warning, if any.
  MediaResult result{NS_OK};

  MP4Metadata::ResultAndByteBuffer initData = MP4Metadata::Metadata(stream);
  if (!initData.Ref()) {
    return InitPromise::CreateAndReject(
        NS_FAILED(initData.Result())
            ? std::move(initData.Result())
            : MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
                          RESULT_DETAIL("Invalid MP4 metadata or OOM")),
        __func__);
  } else if (NS_FAILED(initData.Result()) && result == NS_OK) {
    result = std::move(initData.Result());
  }

  RefPtr<BufferStream> bufferstream = new BufferStream(initData.Ref());

  MP4Metadata metadata{bufferstream};
  DDLINKCHILD("metadata", &metadata);
  nsresult rv = metadata.Parse();
  if (NS_FAILED(rv)) {
    return InitPromise::CreateAndReject(
        MediaResult(rv, RESULT_DETAIL("Parse MP4 metadata failed")), __func__);
  }

  auto audioTrackCount = metadata.GetNumberTracks(TrackInfo::kAudioTrack);
  if (audioTrackCount.Ref() == MP4Metadata::NumberTracksError()) {
    if (StaticPrefs::media_playback_warnings_as_errors()) {
      return InitPromise::CreateAndReject(
          MediaResult(
              NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
              RESULT_DETAIL("Invalid audio track (%s)",
                            audioTrackCount.Result().Description().get())),
          __func__);
    }
    audioTrackCount.Ref() = 0;
  }

  auto videoTrackCount = metadata.GetNumberTracks(TrackInfo::kVideoTrack);
  if (videoTrackCount.Ref() == MP4Metadata::NumberTracksError()) {
    if (StaticPrefs::media_playback_warnings_as_errors()) {
      return InitPromise::CreateAndReject(
          MediaResult(
              NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
              RESULT_DETAIL("Invalid video track (%s)",
                            videoTrackCount.Result().Description().get())),
          __func__);
    }
    videoTrackCount.Ref() = 0;
  }

  if (audioTrackCount.Ref() == 0 && videoTrackCount.Ref() == 0) {
    return InitPromise::CreateAndReject(
        MediaResult(
            NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
            RESULT_DETAIL("No MP4 audio (%s) or video (%s) tracks",
                          audioTrackCount.Result().Description().get(),
                          videoTrackCount.Result().Description().get())),
        __func__);
  }

  if (NS_FAILED(audioTrackCount.Result()) && result == NS_OK) {
    result = std::move(audioTrackCount.Result());
  }
  if (NS_FAILED(videoTrackCount.Result()) && result == NS_OK) {
    result = std::move(videoTrackCount.Result());
  }

  if (audioTrackCount.Ref() != 0) {
    for (size_t i = 0; i < audioTrackCount.Ref(); i++) {
      MP4Metadata::ResultAndTrackInfo info =
          metadata.GetTrackInfo(TrackInfo::kAudioTrack, i);
      if (!info.Ref()) {
        if (StaticPrefs::media_playback_warnings_as_errors()) {
          return InitPromise::CreateAndReject(
              MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
                          RESULT_DETAIL("Invalid MP4 audio track (%s)",
                                        info.Result().Description().get())),
              __func__);
        }
        if (result == NS_OK) {
          result =
              MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
                          RESULT_DETAIL("Invalid MP4 audio track (%s)",
                                        info.Result().Description().get()));
        }
        continue;
      } else if (NS_FAILED(info.Result()) && result == NS_OK) {
        result = std::move(info.Result());
      }
      MP4Metadata::ResultAndIndice indices =
          metadata.GetTrackIndice(info.Ref()->mTrackId);
      if (!indices.Ref()) {
        if (NS_FAILED(info.Result()) && result == NS_OK) {
          result = std::move(indices.Result());
        }
        continue;
      }
      LOG("Created audio track demuxer for info (%s)",
          info.Ref()->ToString().get());
      RefPtr<MP4TrackDemuxer> demuxer =
          new MP4TrackDemuxer(mResource, std::move(info.Ref()),
                              *indices.Ref().get(), info.Ref()->mTimeScale);
      DDLINKCHILD("audio demuxer", demuxer.get());
      mAudioDemuxers.AppendElement(std::move(demuxer));
    }
  }

  if (videoTrackCount.Ref() != 0) {
    for (size_t i = 0; i < videoTrackCount.Ref(); i++) {
      MP4Metadata::ResultAndTrackInfo info =
          metadata.GetTrackInfo(TrackInfo::kVideoTrack, i);
      if (!info.Ref()) {
        if (StaticPrefs::media_playback_warnings_as_errors()) {
          return InitPromise::CreateAndReject(
              MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
                          RESULT_DETAIL("Invalid MP4 video track (%s)",
                                        info.Result().Description().get())),
              __func__);
        }
        if (result == NS_OK) {
          result =
              MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
                          RESULT_DETAIL("Invalid MP4 video track (%s)",
                                        info.Result().Description().get()));
        }
        continue;
      } else if (NS_FAILED(info.Result()) && result == NS_OK) {
        result = std::move(info.Result());
      }
      MP4Metadata::ResultAndIndice indices =
          metadata.GetTrackIndice(info.Ref()->mTrackId);
      if (!indices.Ref()) {
        if (NS_FAILED(info.Result()) && result == NS_OK) {
          result = std::move(indices.Result());
        }
        continue;
      }
      LOG("Created video track demuxer for info (%s)",
          info.Ref()->ToString().get());
      RefPtr<MP4TrackDemuxer> demuxer =
          new MP4TrackDemuxer(mResource, std::move(info.Ref()),
                              *indices.Ref().get(), info.Ref()->mTimeScale);
      DDLINKCHILD("video demuxer", demuxer.get());
      mVideoDemuxers.AppendElement(std::move(demuxer));
    }
  }

  MP4Metadata::ResultAndCryptoFile cryptoFile = metadata.Crypto();
  if (NS_FAILED(cryptoFile.Result()) && result == NS_OK) {
    result = std::move(cryptoFile.Result());
  }
  MOZ_ASSERT(cryptoFile.Ref());
  if (cryptoFile.Ref()->valid) {
    const nsTArray<PsshInfo>& psshs = cryptoFile.Ref()->pssh;
    for (uint32_t i = 0; i < psshs.Length(); i++) {
      mCryptoInitData.AppendElements(psshs[i].data);
    }
  }

  mIsSeekable = metadata.CanSeek();

  return InitPromise::CreateAndResolve(result, __func__);
}

uint32_t MP4Demuxer::GetNumberTracks(TrackInfo::TrackType aType) const {
  switch (aType) {
    case TrackInfo::kAudioTrack:
      return uint32_t(mAudioDemuxers.Length());
    case TrackInfo::kVideoTrack:
      return uint32_t(mVideoDemuxers.Length());
    default:
      return 0;
  }
}

already_AddRefed<MediaTrackDemuxer> MP4Demuxer::GetTrackDemuxer(
    TrackInfo::TrackType aType, uint32_t aTrackNumber) {
  switch (aType) {
    case TrackInfo::kAudioTrack:
      if (aTrackNumber >= uint32_t(mAudioDemuxers.Length())) {
        return nullptr;
      }
      return RefPtr<MediaTrackDemuxer>(mAudioDemuxers[aTrackNumber]).forget();
    case TrackInfo::kVideoTrack:
      if (aTrackNumber >= uint32_t(mVideoDemuxers.Length())) {
        return nullptr;
      }
      return RefPtr<MediaTrackDemuxer>(mVideoDemuxers[aTrackNumber]).forget();
    default:
      return nullptr;
  }
}

bool MP4Demuxer::IsSeekable() const { return mIsSeekable; }

void MP4Demuxer::NotifyDataArrived() {
  for (auto& dmx : mAudioDemuxers) {
    dmx->NotifyDataArrived();
  }
  for (auto& dmx : mVideoDemuxers) {
    dmx->NotifyDataArrived();
  }
}

void MP4Demuxer::NotifyDataRemoved() {
  for (auto& dmx : mAudioDemuxers) {
    dmx->NotifyDataRemoved();
  }
  for (auto& dmx : mVideoDemuxers) {
    dmx->NotifyDataRemoved();
  }
}

UniquePtr<EncryptionInfo> MP4Demuxer::GetCrypto() {
  UniquePtr<EncryptionInfo> crypto;
  if (!mCryptoInitData.IsEmpty()) {
    crypto.reset(new EncryptionInfo{});
    crypto->AddInitData(u"cenc"_ns, mCryptoInitData);
  }
  return crypto;
}

MP4TrackDemuxer::MP4TrackDemuxer(MediaResource* aResource,
                                 UniquePtr<TrackInfo>&& aInfo,
                                 const IndiceWrapper& aIndices,
                                 uint32_t aTimeScale)
    : mResource(aResource),
      mStream(new ResourceStream(aResource)),
      mInfo(std::move(aInfo)),
      mIndex(new MP4SampleIndex(aIndices, mStream, mInfo->mTrackId,
                                mInfo->IsAudio(), aTimeScale)),
      mIterator(MakeUnique<SampleIterator>(mIndex)),
      mNeedReIndex(true) {
  EnsureUpToDateIndex();  // Force update of index

  VideoInfo* videoInfo = mInfo->GetAsVideoInfo();
  AudioInfo* audioInfo = mInfo->GetAsAudioInfo();
  if (videoInfo && MP4Decoder::IsH264(mInfo->mMimeType)) {
    mType = kH264;
    RefPtr<MediaByteBuffer> extraData = videoInfo->mExtraData;
    SPSData spsdata;
    if (H264::DecodeSPSFromExtraData(extraData, spsdata) &&
        spsdata.pic_width > 0 && spsdata.pic_height > 0 &&
        H264::EnsureSPSIsSane(spsdata)) {
      videoInfo->mImage.width = spsdata.pic_width;
      videoInfo->mImage.height = spsdata.pic_height;
      videoInfo->mDisplay.width = spsdata.display_width;
      videoInfo->mDisplay.height = spsdata.display_height;
    }
  } else if (videoInfo && VPXDecoder::IsVP9(mInfo->mMimeType)) {
    mType = kVP9;
  } else if (audioInfo && MP4Decoder::IsAAC(mInfo->mMimeType)) {
    mType = kAAC;
  } else if (videoInfo && MP4Decoder::IsHEVC(mInfo->mMimeType)) {
    mType = kHEVC;
    if (auto rv = H265::DecodeSPSFromHVCCExtraData(videoInfo->mExtraData);
        rv.isOk()) {
      const auto sps = rv.unwrap();
      videoInfo->mImage.width = sps.GetImageSize().Width();
      videoInfo->mImage.height = sps.GetImageSize().Height();
      videoInfo->mDisplay.width = sps.GetDisplaySize().Width();
      videoInfo->mDisplay.height = sps.GetDisplaySize().Height();
    }
  }
}

UniquePtr<TrackInfo> MP4TrackDemuxer::GetInfo() const { return mInfo->Clone(); }

void MP4TrackDemuxer::EnsureUpToDateIndex() {
  if (!mNeedReIndex) {
    return;
  }
  AutoPinned<MediaResource> resource(mResource);
  MediaByteRangeSet byteRanges;
  nsresult rv = resource->GetCachedRanges(byteRanges);
  if (NS_FAILED(rv)) {
    return;
  }
  mIndex->UpdateMoofIndex(byteRanges);
  mNeedReIndex = false;
}

RefPtr<MP4TrackDemuxer::SeekPromise> MP4TrackDemuxer::Seek(
    const TimeUnit& aTime) {
  auto seekTime = aTime;
  mQueuedSample = nullptr;

  mIterator->Seek(seekTime);

#ifdef MOZ_APPLEMEDIA
  bool hasSeenValidSamples = false, seekingFromFirstSyncSample = false;
#endif
  // Check what time we actually seeked to.
  do {
    auto next = GetNextSample();
    if (next.isErr()) {
      auto error = next.unwrapErr();
#ifdef MOZ_APPLEMEDIA
      // On macOS, only IDR frames are marked as key frames. This means some
      // sync samples (e.g., Recovery SEI or I-slices) are not treated as key
      // frames, as they may not be supported by Apple’s VideoToolbox H.264
      // decoder. If we have seen samples but found no key frame, it indicates
      // we need to look further back for an earlier sample where an IDR frame
      // should exist per spec. In that case, we retry from the first sync
      // sample in the GOP, which is typically an IDR frame. This situation
      // suggests a poorly muxed file that wastes seek time, but unfortunately
      // such cases do occur in real-world content.
      if (mType == kH264 && error == NS_ERROR_DOM_MEDIA_END_OF_STREAM &&
          hasSeenValidSamples && !seekingFromFirstSyncSample) {
        LOG("Can not find a key frame from the closet sync sample, try again "
            "from the first sync sample");
        seekingFromFirstSyncSample = true;
        mIterator->Seek(seekTime, SampleIterator::SyncSampleMode::First);
        continue;
      }
#endif
      return SeekPromise::CreateAndReject(error, __func__);
    }
    RefPtr<MediaRawData> sample = next.unwrap();
    if (!sample->Size()) {
      // This sample can't be decoded, continue searching.
      continue;
    }
    if (sample->mKeyframe) {
      MOZ_DIAGNOSTIC_ASSERT(sample->HasValidTime());
      mQueuedSample = sample;
      seekTime = mQueuedSample->mTime;
    }
#ifdef MOZ_APPLEMEDIA
    hasSeenValidSamples = true;
#endif
  } while (!mQueuedSample);

  SetNextKeyFrameTime();

  return SeekPromise::CreateAndResolve(seekTime, __func__);
}

Result<already_AddRefed<MediaRawData>, MediaResult>
MP4TrackDemuxer::GetNextSample() {
  auto next = mIterator->GetNext();
  if (next.isErr()) {
    return next;
  }
  RefPtr<MediaRawData> sample = next.unwrap();

  if (mInfo->GetAsVideoInfo()) {
    sample->mExtraData = mInfo->GetAsVideoInfo()->mExtraData;
    if (mType == kH264 && !sample->mCrypto.IsEncrypted()) {
      H264::FrameType type = H264::GetFrameType(sample);
      switch (type) {
        case H264::FrameType::I_FRAME_IDR:
        case H264::FrameType::I_FRAME_OTHER:
        case H264::FrameType::OTHER: {
          bool keyframe = type == H264::FrameType::I_FRAME_IDR;
#ifndef MOZ_APPLEMEDIA
          // The Apple VideoToolbox H.264 decoder could return a bad data error
          // if a non-IDR I-frame is provided as the first sample to the decoder
          // after seeking. Therefore, only IDR frames should be marked as key
          // frames.
          keyframe = keyframe || type == H264::FrameType::I_FRAME_OTHER;
#endif
          if (sample->mKeyframe != keyframe) {
            NS_WARNING(nsPrintfCString("Frame incorrectly marked as %skeyframe "
                                       "@ pts:%" PRId64 " dur:%" PRId64
                                       " dts:%" PRId64,
                                       keyframe ? "" : "non-",
                                       sample->mTime.ToMicroseconds(),
                                       sample->mDuration.ToMicroseconds(),
                                       sample->mTimecode.ToMicroseconds())
                           .get());
            sample->mKeyframe = keyframe;
          }
          break;
        }
        case H264::FrameType::INVALID:
          NS_WARNING(nsPrintfCString("Invalid H264 frame @ pts:%" PRId64
                                     " dur:%" PRId64 " dts:%" PRId64,
                                     sample->mTime.ToMicroseconds(),
                                     sample->mDuration.ToMicroseconds(),
                                     sample->mTimecode.ToMicroseconds())
                         .get());
          // We could reject the sample now, however demuxer errors are fatal.
          // So we keep the invalid frame, relying on the H264 decoder to
          // handle the error later.
          // TODO: make demuxer errors non-fatal.
          break;
      }
    } else if (mType == kVP9 && !sample->mCrypto.IsEncrypted()) {
      bool keyframe = VPXDecoder::IsKeyframe(
          Span<const uint8_t>(sample->Data(), sample->Size()),
          VPXDecoder::Codec::VP9);
      if (sample->mKeyframe != keyframe) {
        NS_WARNING(nsPrintfCString(
                       "Frame incorrectly marked as %skeyframe "
                       "@ pts:%" PRId64 " dur:%" PRId64 " dts:%" PRId64,
                       keyframe ? "" : "non-", sample->mTime.ToMicroseconds(),
                       sample->mDuration.ToMicroseconds(),
                       sample->mTimecode.ToMicroseconds())
                       .get());
        sample->mKeyframe = keyframe;
      }
    }
  }

  // Adjust trimming information if needed.
  if (mInfo->GetAsAudioInfo()) {
    AudioInfo* info = mInfo->GetAsAudioInfo();
    TimeUnit originalPts = sample->mTime;
    TimeUnit originalEnd = sample->GetEndTime();
    if (sample->mTime.IsNegative()) {
      sample->mTime = TimeUnit::Zero(originalPts);
      sample->mDuration = std::max(TimeUnit::Zero(sample->mTime),
                                   originalPts + sample->mDuration);
      sample->mOriginalPresentationWindow =
          Some(TimeInterval{originalPts, originalEnd});
    }
    // The demuxer only knows the presentation time of the packet, not the
    // actual number of samples that will be decoded from this packet.
    // However we need to trim the last packet to the correct duration.
    // Find the actual size of the decoded packet to know how many samples to
    // trim. This only works because the packet size are constant.
    TimeUnit totalMediaDurationIncludingTrimming =
        info->mDuration - info->mMediaTime;
    if (mType == kAAC &&
        sample->GetEndTime() >= totalMediaDurationIncludingTrimming &&
        totalMediaDurationIncludingTrimming.IsPositive()) {
      // Seek backward a bit.
      mIterator->Seek(sample->mTime - sample->mDuration);
      RefPtr<MediaRawData> previousSample =
          mIterator->GetNext().unwrapOr(nullptr);
      if (previousSample) {
        TimeInterval fullPacketDuration{previousSample->mTime,
                                        previousSample->GetEndTime()};
        sample->mOriginalPresentationWindow = Some(TimeInterval{
            originalPts, originalPts + fullPacketDuration.Length()});
      }
      // Seek back so we're back at the original location -- there's no packet
      // left anyway.
      mIterator->Seek(sample->mTime);
      RefPtr<MediaRawData> dummy = mIterator->GetNext().unwrapOr(nullptr);
    }
  }

  if (MOZ_LOG_TEST(gMediaDemuxerLog, LogLevel::Verbose)) {
    bool isAudio = mInfo->GetAsAudioInfo();
    TimeUnit originalStart = TimeUnit::Invalid();
    TimeUnit originalEnd = TimeUnit::Invalid();
    if (sample->mOriginalPresentationWindow) {
      originalStart = sample->mOriginalPresentationWindow->mStart;
      originalEnd = sample->mOriginalPresentationWindow->mEnd;
    }
    LOG("%s packet demuxed (track id: %d): [%s,%s], duration: %s (original "
        "time: [%s,%s])",
        isAudio ? "Audio" : "Video", mInfo->mTrackId,
        sample->mTime.ToString().get(), sample->GetEndTime().ToString().get(),
        sample->mDuration.ToString().get(), originalStart.ToString().get(),
        originalEnd.ToString().get());
  }

  return sample.forget();
}

RefPtr<MP4TrackDemuxer::SamplesPromise> MP4TrackDemuxer::GetSamples(
    int32_t aNumSamples) {
  EnsureUpToDateIndex();
  RefPtr<SamplesHolder> samples = new SamplesHolder;
  if (!aNumSamples) {
    return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
                                           __func__);
  }

  if (mQueuedSample) {
    NS_ASSERTION(mQueuedSample->mKeyframe, "mQueuedSample must be a keyframe");
    samples->AppendSample(std::move(mQueuedSample));
    MOZ_ASSERT(!mQueuedSample);
    aNumSamples--;
  }
  while (aNumSamples) {
    auto next = GetNextSample();
    if (next.isErr()) {
      nsresult rv = next.inspectErr().Code();
      if ((rv != NS_ERROR_DOM_MEDIA_END_OF_STREAM &&
           rv != NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) ||
          samples->GetSamples().IsEmpty()) {
        return SamplesPromise::CreateAndReject(next.unwrapErr(), __func__);
      }
      break;
    }
    RefPtr<MediaRawData> sample = next.unwrap();
    if (!sample->Size()) {
      continue;
    }
    MOZ_DIAGNOSTIC_ASSERT(sample->HasValidTime());
    samples->AppendSample(std::move(sample));
    aNumSamples--;
  }

  if (mNextKeyframeTime.isNothing() ||
      samples->GetSamples().LastElement()->mTime >= mNextKeyframeTime.value()) {
    SetNextKeyFrameTime();
  }
  return SamplesPromise::CreateAndResolve(samples, __func__);
}

void MP4TrackDemuxer::SetNextKeyFrameTime() {
  mNextKeyframeTime.reset();
  TimeUnit frameTime = mIterator->GetNextKeyframeTime();
  if (frameTime.IsValid()) {
    mNextKeyframeTime.emplace(frameTime);
  }
}

void MP4TrackDemuxer::Reset() {
  mQueuedSample = nullptr;
  // TODO: verify this
  mIterator->Seek(TimeUnit::FromNegativeInfinity());
  SetNextKeyFrameTime();
}

nsresult MP4TrackDemuxer::GetNextRandomAccessPoint(TimeUnit* aTime) {
  if (mNextKeyframeTime.isNothing()) {
    // There's no next key frame.
    *aTime = TimeUnit::FromInfinity();
  } else {
    *aTime = mNextKeyframeTime.value();
  }
  return NS_OK;
}

RefPtr<MP4TrackDemuxer::SkipAccessPointPromise>
MP4TrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold) {
  mQueuedSample = nullptr;
  // Loop until we reach the next keyframe after the threshold.
  uint32_t parsed = 0;
  Maybe<SkipFailureHolder> failure;
  while (true) {
    auto next = GetNextSample();
    if (next.isErr()) {
      failure.emplace(next.unwrapErr(), parsed);
      break;
    }
    RefPtr<MediaRawData> sample = next.unwrap();
    parsed++;
    MOZ_DIAGNOSTIC_ASSERT(sample->HasValidTime());
    if (sample->mKeyframe && sample->mTime >= aTimeThreshold) {
      mQueuedSample = sample;
      break;
    }
  }
  SetNextKeyFrameTime();
  if (failure.isSome()) {
    return SkipAccessPointPromise::CreateAndReject(failure.extract(), __func__);
  }
  return SkipAccessPointPromise::CreateAndResolve(parsed, __func__);
}

TimeIntervals MP4TrackDemuxer::GetBuffered() {
  EnsureUpToDateIndex();
  AutoPinned<MediaResource> resource(mResource);
  MediaByteRangeSet byteRanges;
  nsresult rv = resource->GetCachedRanges(byteRanges);

  if (NS_FAILED(rv)) {
    return TimeIntervals();
  }

  TimeIntervals timeRanges = mIndex->ConvertByteRangesToTimeRanges(byteRanges);
  if (AudioInfo* info = mInfo->GetAsAudioInfo(); info) {
    // Trim as in GetNextSample().
    TimeUnit totalMediaDurationIncludingTrimming =
        info->mDuration - info->mMediaTime;
    auto end = TimeUnit::FromInfinity();
    if (mType == kAAC && totalMediaDurationIncludingTrimming.IsPositive()) {
      end = info->mDuration;
    }
    if (timeRanges.GetStart().IsNegative() || timeRanges.GetEnd() > end) {
      TimeInterval trimming(TimeUnit::Zero(timeRanges.GetStart()), end);
      timeRanges = timeRanges.Intersection(trimming);
    }
  }

  return timeRanges;
}

void MP4TrackDemuxer::NotifyDataArrived() { mNeedReIndex = true; }

void MP4TrackDemuxer::NotifyDataRemoved() {
  AutoPinned<MediaResource> resource(mResource);
  MediaByteRangeSet byteRanges;
  nsresult rv = resource->GetCachedRanges(byteRanges);
  if (NS_FAILED(rv)) {
    return;
  }
  mIndex->UpdateMoofIndex(byteRanges, true /* can evict */);
  mNeedReIndex = false;
}

}  // namespace mozilla

#undef LOG

Messung V0.5 in Prozent
C=91 H=96 G=93

¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet am  2026-08-26) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.