Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/dom/media/hls/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 21 kB image not shown  

Quelle  HLSDemuxer.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 "HLSDemuxer.h"

#include <algorithm>
#include <limits>
#include <stdint.h>

#include "HLSUtils.h"
#include "MediaCodec.h"
#include "mozilla/java/GeckoAudioInfoWrappers.h"
#include "mozilla/java/GeckoHLSDemuxerWrapperNatives.h"
#include "mozilla/java/GeckoVideoInfoWrappers.h"
#include "mozilla/Unused.h"
#include "nsPrintfCString.h"

namespace mozilla {

static Atomic<uint32_t> sStreamSourceID(0u);

typedef TrackInfo::TrackType TrackType;
using media::TimeInterval;
using media::TimeIntervals;
using media::TimeUnit;

static VideoRotation getVideoInfoRotation(int aRotation) {
  switch (aRotation) {
    case 0:
      return VideoRotation::kDegree_0;
    case 90:
      return VideoRotation::kDegree_90;
    case 180:
      return VideoRotation::kDegree_180;
    case 270:
      return VideoRotation::kDegree_270;
    default:
      return VideoRotation::kDegree_0;
  }
}

static mozilla::StereoMode getStereoMode(int aMode) {
  switch (aMode) {
    case 0:
      return mozilla::StereoMode::MONO;
    case 1:
      return mozilla::StereoMode::TOP_BOTTOM;
    case 2:
      return mozilla::StereoMode::LEFT_RIGHT;
    default:
      return mozilla::StereoMode::MONO;
  }
}

// HLSDemuxerCallbacksSupport is a native implemented callback class for
// Callbacks in GeckoHLSDemuxerWrapper.java.
// The callback functions will be invoked by JAVA-side thread.
// Should dispatch the task to the demuxer's task queue.
// We ensure the callback will never be invoked after
// HLSDemuxerCallbacksSupport::DisposeNative has been called in ~HLSDemuxer.
class HLSDemuxer::HLSDemuxerCallbacksSupport
    : public java::GeckoHLSDemuxerWrapper::Callbacks::Natives<
          HLSDemuxerCallbacksSupport> {
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HLSDemuxerCallbacksSupport)
 public:
  typedef java::GeckoHLSDemuxerWrapper::Callbacks::Natives<
      HLSDemuxerCallbacksSupport>
      NativeCallbacks;
  using NativeCallbacks::AttachNative;
  using NativeCallbacks::DisposeNative;

  explicit HLSDemuxerCallbacksSupport(HLSDemuxer* aDemuxer)
      : mMutex("HLSDemuxerCallbacksSupport"), mDemuxer(aDemuxer) {
    MOZ_ASSERT(mDemuxer);
  }

  void OnInitialized(bool aHasAudio, bool aHasVideo) {
    HLS_DEBUG("HLSDemuxerCallbacksSupport""OnInitialized");
    MutexAutoLock lock(mMutex);
    if (!mDemuxer) {
      return;
    }
    RefPtr<HLSDemuxerCallbacksSupport> self = this;
    nsresult rv = mDemuxer->GetTaskQueue()->Dispatch(NS_NewRunnableFunction(
        "HLSDemuxer::HLSDemuxerCallbacksSupport::OnInitialized", [=]() {
          MutexAutoLock lock(self->mMutex);
          if (self->mDemuxer) {
            self->mDemuxer->OnInitialized(aHasAudio, aHasVideo);
          }
        }));
    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
    Unused << rv;
  }

  void OnError(int aErrorCode) {
    HLS_DEBUG("HLSDemuxerCallbacksSupport""Got error(%d) from java side",
              aErrorCode);
    MutexAutoLock lock(mMutex);
    if (!mDemuxer) {
      return;
    }
    RefPtr<HLSDemuxerCallbacksSupport> self = this;
    nsresult rv = mDemuxer->GetTaskQueue()->Dispatch(NS_NewRunnableFunction(
        "HLSDemuxer::HLSDemuxerCallbacksSupport::OnError", [=]() {
          MutexAutoLock lock(self->mMutex);
          if (self->mDemuxer) {
            self->mDemuxer->OnError(aErrorCode);
          }
        }));
    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
    Unused << rv;
  }

  void Detach() {
    MutexAutoLock lock(mMutex);
    mDemuxer = nullptr;
  }

  Mutex mMutex MOZ_UNANNOTATED;

 private:
  ~HLSDemuxerCallbacksSupport() {}
  HLSDemuxer* mDemuxer;
};

HLSDemuxer::HLSDemuxer(int aPlayerId)
    : mTaskQueue(TaskQueue::Create(
          GetMediaThreadPool(MediaThreadType::SUPERVISOR), "HLSDemuxer",
          /* aSupportsTailDispatch = */ false)) {
  MOZ_ASSERT(NS_IsMainThread());
  HLSDemuxerCallbacksSupport::Init();
  mJavaCallbacks = java::GeckoHLSDemuxerWrapper::Callbacks::New();
  MOZ_ASSERT(mJavaCallbacks);

  mCallbackSupport = new HLSDemuxerCallbacksSupport(this);
  HLSDemuxerCallbacksSupport::AttachNative(mJavaCallbacks, mCallbackSupport);

  mHLSDemuxerWrapper =
      java::GeckoHLSDemuxerWrapper::Create(aPlayerId, mJavaCallbacks);
  MOZ_ASSERT(mHLSDemuxerWrapper);
}

void HLSDemuxer::OnInitialized(bool aHasAudio, bool aHasVideo) {
  MOZ_ASSERT(OnTaskQueue());

  if (aHasAudio) {
    mAudioDemuxer = new HLSTrackDemuxer(this, TrackInfo::TrackType::kAudioTrack,
                                        MakeUnique<AudioInfo>());
  }
  if (aHasVideo) {
    mVideoDemuxer = new HLSTrackDemuxer(this, TrackInfo::TrackType::kVideoTrack,
                                        MakeUnique<VideoInfo>());
  }

  mInitPromise.ResolveIfExists(NS_OK, __func__);
}

void HLSDemuxer::OnError(int aErrorCode) {
  MOZ_ASSERT(OnTaskQueue());
  mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
}

RefPtr<HLSDemuxer::InitPromise> HLSDemuxer::Init() {
  RefPtr<HLSDemuxer> self = this;
  return InvokeAsync(GetTaskQueue(), __func__, [self]() {
    RefPtr<InitPromise> p = self->mInitPromise.Ensure(__func__);
    return p;
  });
}

void HLSDemuxer::NotifyDataArrived() {
  HLS_DEBUG("HLSDemuxer""NotifyDataArrived");
}

uint32_t HLSDemuxer::GetNumberTracks(TrackType aType) const {
  switch (aType) {
    case TrackType::kAudioTrack:
      return mHLSDemuxerWrapper->GetNumberOfTracks(TrackType::kAudioTrack);
    case TrackType::kVideoTrack:
      return mHLSDemuxerWrapper->GetNumberOfTracks(TrackType::kVideoTrack);
    default:
      return 0;
  }
}

already_AddRefed<MediaTrackDemuxer> HLSDemuxer::GetTrackDemuxer(
    TrackType aType, uint32_t aTrackNumber) {
  RefPtr<HLSTrackDemuxer> e = nullptr;
  if (aType == TrackInfo::TrackType::kAudioTrack) {
    e = mAudioDemuxer;
  } else {
    e = mVideoDemuxer;
  }
  return e.forget();
}

bool HLSDemuxer::IsSeekable() const {
  return !mHLSDemuxerWrapper->IsLiveStream();
}

UniquePtr<EncryptionInfo> HLSDemuxer::GetCrypto() {
  // TODO: Currently, our HLS implementation doesn't support encrypted content.
  // Return null at this stage.
  return nullptr;
}

TimeUnit HLSDemuxer::GetNextKeyFrameTime() {
  MOZ_ASSERT(mHLSDemuxerWrapper);
  return TimeUnit::FromMicroseconds(mHLSDemuxerWrapper->GetNextKeyFrameTime());
}

bool HLSDemuxer::OnTaskQueue() const { return mTaskQueue->IsCurrentThreadIn(); }

HLSDemuxer::~HLSDemuxer() {
  HLS_DEBUG("HLSDemuxer""~HLSDemuxer()");
  mCallbackSupport->Detach();
  if (mHLSDemuxerWrapper) {
    mHLSDemuxerWrapper->Destroy();
    mHLSDemuxerWrapper = nullptr;
  }
  if (mJavaCallbacks) {
    HLSDemuxerCallbacksSupport::DisposeNative(mJavaCallbacks);
    mJavaCallbacks = nullptr;
  }
  mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
}

HLSTrackDemuxer::HLSTrackDemuxer(HLSDemuxer* aParent,
                                 TrackInfo::TrackType aType,
                                 UniquePtr<TrackInfo> aTrackInfo)
    : mParent(aParent),
      mType(aType),
      mMutex("HLSTrackDemuxer"),
      mTrackInfo(std::move(aTrackInfo)) {
  // Only support audio and video track currently.
  MOZ_ASSERT(mType == TrackInfo::kVideoTrack ||
             mType == TrackInfo::kAudioTrack);
  UpdateMediaInfo(0);
}

UniquePtr<TrackInfo> HLSTrackDemuxer::GetInfo() const {
  MutexAutoLock lock(mMutex);
  return mTrackInfo->Clone();
}

RefPtr<HLSTrackDemuxer::SeekPromise> HLSTrackDemuxer::Seek(
    const TimeUnit& aTime) {
  MOZ_ASSERT(mParent, "Called after BreackCycle()");
  return InvokeAsync<TimeUnit&&>(mParent->GetTaskQueue(), this, __func__,
                                 &HLSTrackDemuxer::DoSeek, aTime);
}

RefPtr<HLSTrackDemuxer::SeekPromise> HLSTrackDemuxer::DoSeek(
    const TimeUnit& aTime) {
  MOZ_ASSERT(mParent, "Called after BreackCycle()");
  MOZ_ASSERT(mParent->OnTaskQueue());
  mQueuedSample = nullptr;
  int64_t seekTimeUs = aTime.ToMicroseconds();
  bool result = mParent->mHLSDemuxerWrapper->Seek(seekTimeUs);
  if (!result) {
    return SeekPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA,
                                        __func__);
  }
  TimeUnit seekTime = TimeUnit::FromMicroseconds(seekTimeUs);
  return SeekPromise::CreateAndResolve(seekTime, __func__);
}

RefPtr<HLSTrackDemuxer::SamplesPromise> HLSTrackDemuxer::GetSamples(
    int32_t aNumSamples) {
  MOZ_ASSERT(mParent, "Called after BreackCycle()");
  return InvokeAsync(mParent->GetTaskQueue(), this, __func__,
                     &HLSTrackDemuxer::DoGetSamples, aNumSamples);
}

RefPtr<HLSTrackDemuxer::SamplesPromise> HLSTrackDemuxer::DoGetSamples(
    int32_t aNumSamples) {
  MOZ_ASSERT(mParent, "Called after BreackCycle()");
  MOZ_ASSERT(mParent->OnTaskQueue());
  if (!aNumSamples) {
    return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
                                           __func__);
  }
  RefPtr<SamplesHolder> samples = new SamplesHolder;
  if (mQueuedSample) {
    if (mQueuedSample->mEOS) {
      return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM,
                                             __func__);
    }
    MOZ_ASSERT(mQueuedSample->mKeyframe, "mQueuedSample must be a keyframe");
    samples->AppendSample(mQueuedSample);
    mQueuedSample = nullptr;
    aNumSamples--;
  }
  if (aNumSamples == 0) {
    // Return the queued sample.
    return SamplesPromise::CreateAndResolve(samples, __func__);
  }
  mozilla::jni::ObjectArray::LocalRef demuxedSamples =
      (mType == TrackInfo::kAudioTrack)
          ? mParent->mHLSDemuxerWrapper->GetSamples(TrackInfo::kAudioTrack,
                                                    aNumSamples)
          : mParent->mHLSDemuxerWrapper->GetSamples(TrackInfo::kVideoTrack,
                                                    aNumSamples);
  nsTArray<jni::Object::LocalRef> sampleObjectArray(
      demuxedSamples->GetElements());

  if (sampleObjectArray.IsEmpty()) {
    return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA,
                                           __func__);
  }

  for (auto&& demuxedSample : sampleObjectArray) {
    java::GeckoHLSSample::LocalRef sample(std::move(demuxedSample));
    if (sample->IsEOS()) {
      HLS_DEBUG("HLSTrackDemuxer""Met BUFFER_FLAG_END_OF_STREAM.");
      if (samples->GetSamples().IsEmpty()) {
        return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM,
                                               __func__);
      }
      mQueuedSample = new MediaRawData();
      mQueuedSample->mEOS = true;
      break;
    }
    RefPtr<MediaRawData> mrd = ConvertToMediaRawData(sample);
    if (!mrd) {
      return SamplesPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
    }
    if (!mrd->HasValidTime()) {
      return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
                                             __func__);
    }
    samples->AppendSample(mrd);
  }
  if (mType == TrackInfo::kVideoTrack &&
      (mNextKeyframeTime.isNothing() ||
       samples->GetSamples().LastElement()->mTime >=
           mNextKeyframeTime.value())) {
    // Only need to find NextKeyFrame for Video
    UpdateNextKeyFrameTime();
  }

  return SamplesPromise::CreateAndResolve(samples, __func__);
}

void HLSTrackDemuxer::UpdateMediaInfo(int index) {
  MOZ_ASSERT(mParent->OnTaskQueue());
  MOZ_ASSERT(mParent->mHLSDemuxerWrapper);
  MutexAutoLock lock(mMutex);
  jni::Object::LocalRef infoObj = nullptr;
  if (mType == TrackType::kAudioTrack) {
    infoObj = mParent->mHLSDemuxerWrapper->GetAudioInfo(index);
    if (!infoObj) {
      NS_WARNING("Failed to get audio info from Java wrapper");
      return;
    }
    auto* audioInfo = mTrackInfo->GetAsAudioInfo();
    MOZ_ASSERT(audioInfo != nullptr);
    HLS_DEBUG("HLSTrackDemuxer""Update audio info (%d)", index);
    java::GeckoAudioInfo::LocalRef audioInfoObj(std::move(infoObj));
    audioInfo->mRate = audioInfoObj->Rate();
    audioInfo->mChannels = audioInfoObj->Channels();
    audioInfo->mProfile = audioInfoObj->Profile();
    audioInfo->mBitDepth = audioInfoObj->BitDepth();
    audioInfo->mMimeType =
        NS_ConvertUTF16toUTF8(audioInfoObj->MimeType()->ToString());
    audioInfo->mDuration = TimeUnit::FromMicroseconds(audioInfoObj->Duration());
    jni::ByteArray::LocalRef csdBytes = audioInfoObj->CodecSpecificData();
    if (csdBytes) {
      auto&& csd = csdBytes->GetElements();
      AudioCodecSpecificBinaryBlob blob;
      blob.mBinaryBlob->AppendElements(reinterpret_cast<uint8_t*>(&csd[0]),
                                       csd.Length());
      audioInfo->mCodecSpecificConfig =
          AudioCodecSpecificVariant{std::move(blob)};
    }
  } else {
    infoObj = mParent->mHLSDemuxerWrapper->GetVideoInfo(index);
    if (!infoObj) {
      NS_WARNING("Failed to get video info from Java wrapper");
      return;
    }
    auto* videoInfo = mTrackInfo->GetAsVideoInfo();
    MOZ_ASSERT(videoInfo != nullptr);
    java::GeckoVideoInfo::LocalRef videoInfoObj(std::move(infoObj));
    videoInfo->mStereoMode = getStereoMode(videoInfoObj->StereoMode());
    videoInfo->mRotation = getVideoInfoRotation(videoInfoObj->Rotation());
    videoInfo->mImage.width = videoInfoObj->DisplayWidth();
    videoInfo->mImage.height = videoInfoObj->DisplayHeight();
    videoInfo->mDisplay.width = videoInfoObj->PictureWidth();
    videoInfo->mDisplay.height = videoInfoObj->PictureHeight();
    videoInfo->mMimeType =
        NS_ConvertUTF16toUTF8(videoInfoObj->MimeType()->ToString());
    videoInfo->mDuration = TimeUnit::FromMicroseconds(videoInfoObj->Duration());
    HLS_DEBUG("HLSTrackDemuxer""Update video info (%d) / I(%dx%d) / D(%dx%d)",
              index, videoInfo->mImage.width, videoInfo->mImage.height,
              videoInfo->mDisplay.width, videoInfo->mDisplay.height);
  }
}

CryptoSample HLSTrackDemuxer::ExtractCryptoSample(
    size_t aSampleSize,
    java::sdk::MediaCodec::CryptoInfo::LocalRef aCryptoInfo) {
  if (!aCryptoInfo) {
    return CryptoSample{};
  }
  // Extract Crypto information
  CryptoSample crypto;
  char const* msg = "";
  do {
    HLS_DEBUG("HLSTrackDemuxer""Sample has Crypto Info");

    int32_t mode = 0;
    if (NS_FAILED(aCryptoInfo->Mode(&mode))) {
      msg = "Error when extracting encryption mode.";
      break;
    }
    // We currently only handle ctr mode.
    if (mode != java::sdk::MediaCodec::CRYPTO_MODE_AES_CTR) {
      msg = "Error: unexpected encryption mode.";
      break;
    }

    crypto.mCryptoScheme = CryptoScheme::Cenc;

    mozilla::jni::ByteArray::LocalRef ivData;
    if (NS_FAILED(aCryptoInfo->Iv(&ivData))) {
      msg = "Error when extracting encryption IV.";
      break;
    }
    // Data in mIV is uint8_t and jbyte is signed char
    auto&& ivArr = ivData->GetElements();
    crypto.mIV.AppendElements(reinterpret_cast<uint8_t*>(&ivArr[0]),
                              ivArr.Length());
    crypto.mIVSize = ivArr.Length();
    mozilla::jni::ByteArray::LocalRef keyData;
    if (NS_FAILED(aCryptoInfo->Key(&keyData))) {
      msg = "Error when extracting encryption key.";
      break;
    }
    auto&& keyArr = keyData->GetElements();
    // Data in mKeyId is uint8_t and jbyte is signed char
    crypto.mKeyId.AppendElements(reinterpret_cast<uint8_t*>(&keyArr[0]),
                                 keyArr.Length());

    mozilla::jni::IntArray::LocalRef clearData;
    if (NS_FAILED(aCryptoInfo->NumBytesOfClearData(&clearData))) {
      msg = "Error when extracting clear data.";
      break;
    }
    auto&& clearArr = clearData->GetElements();
    // Data in mPlainSizes is uint32_t, NumBytesOfClearData is int32_t
    crypto.mPlainSizes.AppendElements(reinterpret_cast<uint32_t*>(&clearArr[0]),
                                      clearArr.Length());

    mozilla::jni::IntArray::LocalRef encryptedData;
    if (NS_FAILED(aCryptoInfo->NumBytesOfEncryptedData(&encryptedData))) {
      msg = "Error when extracting encrypted data.";
      break;
    }
    auto&& encryptedArr = encryptedData->GetElements();
    // Data in mEncryptedSizes is uint32_t, NumBytesOfEncryptedData is int32_t
    crypto.mEncryptedSizes.AppendElements(
        reinterpret_cast<uint32_t*>(&encryptedArr[0]), encryptedArr.Length());
    int subSamplesNum = 0;
    if (NS_FAILED(aCryptoInfo->NumSubSamples(&subSamplesNum))) {
      msg = "Error when extracting subsamples.";
      break;
    }
    crypto.mPlainSizes[0] -= (aSampleSize - subSamplesNum);

    return crypto;
  } while (false);

  HLS_DEBUG("HLSTrackDemuxer""%s", msg);
  return CryptoSample{};
}

RefPtr<MediaRawData> HLSTrackDemuxer::ConvertToMediaRawData(
    java::GeckoHLSSample::LocalRef aSample) {
  java::sdk::MediaCodec::BufferInfo::LocalRef info = aSample->Info();
  // Currently extract PTS, Size and Data without Crypto information.
  // Transform java Sample into MediaRawData
  RefPtr<MediaRawData> mrd = new MediaRawData();
  int64_t presentationTimeUs = 0;
  bool ok = NS_SUCCEEDED(info->PresentationTimeUs(&presentationTimeUs));
  mrd->mTime = TimeUnit::FromMicroseconds(presentationTimeUs);
  mrd->mTimecode = TimeUnit::FromMicroseconds(presentationTimeUs);
  mrd->mKeyframe = aSample->IsKeyFrame();
  mrd->mDuration = (mType == TrackInfo::kVideoTrack)
                       ? TimeUnit::FromMicroseconds(aSample->Duration())
                       : TimeUnit::Zero();

  int32_t size = 0;
  ok &= NS_SUCCEEDED(info->Size(&size));
  if (!ok) {
    HLS_DEBUG("HLSTrackDemuxer",
              "Error occurred during extraction from Sample java object.");
    return nullptr;
  }

  // Update A/V stream souce ID & Audio/VideoInfo for MFR.
  auto sampleFormatIndex = aSample->FormatIndex();
  if (mLastFormatIndex != sampleFormatIndex) {
    mLastFormatIndex = sampleFormatIndex;
    UpdateMediaInfo(mLastFormatIndex);
    MutexAutoLock lock(mMutex);
    mrd->mTrackInfo = new TrackInfoSharedPtr(*mTrackInfo, ++sStreamSourceID);
  }

  // Write payload into MediaRawData
  UniquePtr<MediaRawDataWriter> writer(mrd->CreateWriter());
  if (!writer->SetSize(size)) {
    HLS_DEBUG("HLSTrackDemuxer""Exit failed to allocate media buffer");
    return nullptr;
  }
  jni::ByteBuffer::LocalRef dest =
      jni::ByteBuffer::New(writer->Data(), writer->Size());
  aSample->WriteToByteBuffer(dest);

  writer->mCrypto = ExtractCryptoSample(writer->Size(), aSample->CryptoInfo());
  return mrd;
}

void HLSTrackDemuxer::Reset() {
  MOZ_ASSERT(mParent, "Called after BreackCycle()");
  mQueuedSample = nullptr;
}

void HLSTrackDemuxer::UpdateNextKeyFrameTime() {
  MOZ_ASSERT(mParent, "Called after BreackCycle()");
  TimeUnit nextKeyFrameTime = mParent->GetNextKeyFrameTime();
  if (nextKeyFrameTime != mNextKeyframeTime.refOr(TimeUnit::FromInfinity())) {
    HLS_DEBUG("HLSTrackDemuxer""Update mNextKeyframeTime to %" PRId64,
              nextKeyFrameTime.ToMicroseconds());
    mNextKeyframeTime = Some(nextKeyFrameTime);
  }
}

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

RefPtr<HLSTrackDemuxer::SkipAccessPointPromise>
HLSTrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold) {
  return InvokeAsync(mParent->GetTaskQueue(), this, __func__,
                     &HLSTrackDemuxer::DoSkipToNextRandomAccessPoint,
                     aTimeThreshold);
}

RefPtr<HLSTrackDemuxer::SkipAccessPointPromise>
HLSTrackDemuxer::DoSkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold) {
  MOZ_ASSERT(mParent, "Called after BreackCycle()");
  MOZ_ASSERT(mParent->OnTaskQueue());
  mQueuedSample = nullptr;
  uint32_t parsed = 0;
  bool found = false;
  MediaResult result = NS_ERROR_DOM_MEDIA_END_OF_STREAM;
  do {
    mozilla::jni::ObjectArray::LocalRef demuxedSamples =
        mParent->mHLSDemuxerWrapper->GetSamples(mType, 1);
    nsTArray<jni::Object::LocalRef> sampleObjectArray(
        demuxedSamples->GetElements());
    if (sampleObjectArray.IsEmpty()) {
      result = NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA;
      break;
    }
    parsed++;
    java::GeckoHLSSample::LocalRef sample(std::move(sampleObjectArray[0]));
    if (sample->IsEOS()) {
      result = NS_ERROR_DOM_MEDIA_END_OF_STREAM;
      break;
    }
    if (sample->IsKeyFrame()) {
      java::sdk::MediaCodec::BufferInfo::LocalRef info = sample->Info();
      int64_t presentationTimeUs = 0;
      if (NS_SUCCEEDED(info->PresentationTimeUs(&presentationTimeUs)) &&
          TimeUnit::FromMicroseconds(presentationTimeUs) >= aTimeThreshold) {
        RefPtr<MediaRawData> rawData = ConvertToMediaRawData(sample);
        if (!rawData) {
          result = NS_ERROR_OUT_OF_MEMORY;
          break;
        }
        if (!rawData->HasValidTime()) {
          result = NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
          break;
        }
        found = true;
        mQueuedSample = rawData;
        break;
      }
    }
  } while (true);

  if (!found) {
    return SkipAccessPointPromise::CreateAndReject(
        SkipFailureHolder(result, parsed), __func__);
  }
  return SkipAccessPointPromise::CreateAndResolve(parsed, __func__);
}

TimeIntervals HLSTrackDemuxer::GetBuffered() {
  MOZ_ASSERT(mParent, "Called after BreackCycle()");
  int64_t bufferedTime = mParent->mHLSDemuxerWrapper->GetBuffered();  // us
  return TimeIntervals(
      TimeInterval(TimeUnit(), TimeUnit::FromMicroseconds(bufferedTime)));
}

void HLSTrackDemuxer::BreakCycles() {
  RefPtr<HLSTrackDemuxer> self = this;
  nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
      "HLSTrackDemuxer::BreakCycles", [self]() { self->mParent = nullptr; });
  nsresult rv = mParent->GetTaskQueue()->Dispatch(task.forget());
  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
  Unused << rv;
}

HLSTrackDemuxer::~HLSTrackDemuxer() {}

}  // namespace mozilla

Messung V0.5
C=94 H=96 G=94

¤ Dauer der Verarbeitung: 0.3 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

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.