tor-browser

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

HLSDemuxer.cpp (22103B)


      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 "HLSDemuxer.h"
      8 
      9 #include <stdint.h>
     10 
     11 #include <algorithm>
     12 
     13 #include "HLSUtils.h"
     14 #include "MediaCodec.h"
     15 #include "mozilla/java/GeckoAudioInfoWrappers.h"
     16 #include "mozilla/java/GeckoHLSDemuxerWrapperNatives.h"
     17 #include "mozilla/java/GeckoVideoInfoWrappers.h"
     18 #include "nsPrintfCString.h"
     19 
     20 namespace mozilla {
     21 
     22 static Atomic<uint32_t> sStreamSourceID(0u);
     23 
     24 typedef TrackInfo::TrackType TrackType;
     25 using media::TimeInterval;
     26 using media::TimeIntervals;
     27 using media::TimeUnit;
     28 
     29 static VideoRotation getVideoInfoRotation(int aRotation) {
     30  switch (aRotation) {
     31    case 0:
     32      return VideoRotation::kDegree_0;
     33    case 90:
     34      return VideoRotation::kDegree_90;
     35    case 180:
     36      return VideoRotation::kDegree_180;
     37    case 270:
     38      return VideoRotation::kDegree_270;
     39    default:
     40      return VideoRotation::kDegree_0;
     41  }
     42 }
     43 
     44 static mozilla::StereoMode getStereoMode(int aMode) {
     45  switch (aMode) {
     46    case 0:
     47      return mozilla::StereoMode::MONO;
     48    case 1:
     49      return mozilla::StereoMode::TOP_BOTTOM;
     50    case 2:
     51      return mozilla::StereoMode::LEFT_RIGHT;
     52    default:
     53      return mozilla::StereoMode::MONO;
     54  }
     55 }
     56 
     57 // HLSDemuxerCallbacksSupport is a native implemented callback class for
     58 // Callbacks in GeckoHLSDemuxerWrapper.java.
     59 // The callback functions will be invoked by JAVA-side thread.
     60 // Should dispatch the task to the demuxer's task queue.
     61 // We ensure the callback will never be invoked after
     62 // HLSDemuxerCallbacksSupport::DisposeNative has been called in ~HLSDemuxer.
     63 class HLSDemuxer::HLSDemuxerCallbacksSupport
     64    : public java::GeckoHLSDemuxerWrapper::Callbacks::Natives<
     65          HLSDemuxerCallbacksSupport> {
     66  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HLSDemuxerCallbacksSupport)
     67 public:
     68  typedef java::GeckoHLSDemuxerWrapper::Callbacks::Natives<
     69      HLSDemuxerCallbacksSupport>
     70      NativeCallbacks;
     71  using NativeCallbacks::AttachNative;
     72  using NativeCallbacks::DisposeNative;
     73 
     74  explicit HLSDemuxerCallbacksSupport(HLSDemuxer* aDemuxer)
     75      : mMutex("HLSDemuxerCallbacksSupport"), mDemuxer(aDemuxer) {
     76    MOZ_ASSERT(mDemuxer);
     77  }
     78 
     79  void OnInitialized(bool aHasAudio, bool aHasVideo) {
     80    HLS_DEBUG("HLSDemuxerCallbacksSupport", "OnInitialized");
     81    MutexAutoLock lock(mMutex);
     82    if (!mDemuxer) {
     83      return;
     84    }
     85    RefPtr<HLSDemuxerCallbacksSupport> self = this;
     86    nsresult rv = mDemuxer->GetTaskQueue()->Dispatch(NS_NewRunnableFunction(
     87        "HLSDemuxer::HLSDemuxerCallbacksSupport::OnInitialized", [=]() {
     88          MutexAutoLock lock(self->mMutex);
     89          if (self->mDemuxer) {
     90            self->mDemuxer->OnInitialized(aHasAudio, aHasVideo);
     91          }
     92        }));
     93    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
     94    (void)rv;
     95  }
     96 
     97  void OnError(int aErrorCode) {
     98    HLS_DEBUG("HLSDemuxerCallbacksSupport", "Got error(%d) from java side",
     99              aErrorCode);
    100    MutexAutoLock lock(mMutex);
    101    if (!mDemuxer) {
    102      return;
    103    }
    104    RefPtr<HLSDemuxerCallbacksSupport> self = this;
    105    nsresult rv = mDemuxer->GetTaskQueue()->Dispatch(NS_NewRunnableFunction(
    106        "HLSDemuxer::HLSDemuxerCallbacksSupport::OnError", [=]() {
    107          MutexAutoLock lock(self->mMutex);
    108          if (self->mDemuxer) {
    109            self->mDemuxer->OnError(aErrorCode);
    110          }
    111        }));
    112    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
    113    (void)rv;
    114  }
    115 
    116  void Detach() {
    117    MutexAutoLock lock(mMutex);
    118    mDemuxer = nullptr;
    119  }
    120 
    121  Mutex mMutex MOZ_UNANNOTATED;
    122 
    123 private:
    124  ~HLSDemuxerCallbacksSupport() {}
    125  HLSDemuxer* mDemuxer;
    126 };
    127 
    128 HLSDemuxer::HLSDemuxer(int aPlayerId)
    129    : mTaskQueue(TaskQueue::Create(
    130          GetMediaThreadPool(MediaThreadType::SUPERVISOR), "HLSDemuxer",
    131          /* aSupportsTailDispatch = */ false)) {
    132  MOZ_ASSERT(NS_IsMainThread());
    133  HLSDemuxerCallbacksSupport::Init();
    134  mJavaCallbacks = java::GeckoHLSDemuxerWrapper::Callbacks::New();
    135  MOZ_ASSERT(mJavaCallbacks);
    136 
    137  mCallbackSupport = new HLSDemuxerCallbacksSupport(this);
    138  HLSDemuxerCallbacksSupport::AttachNative(mJavaCallbacks, mCallbackSupport);
    139 
    140  mHLSDemuxerWrapper =
    141      java::GeckoHLSDemuxerWrapper::Create(aPlayerId, mJavaCallbacks);
    142  MOZ_ASSERT(mHLSDemuxerWrapper);
    143 }
    144 
    145 void HLSDemuxer::OnInitialized(bool aHasAudio, bool aHasVideo) {
    146  MOZ_ASSERT(OnTaskQueue());
    147 
    148  if (aHasAudio) {
    149    mAudioDemuxer = new HLSTrackDemuxer(this, TrackInfo::TrackType::kAudioTrack,
    150                                        MakeUnique<AudioInfo>());
    151  }
    152  if (aHasVideo) {
    153    mVideoDemuxer = new HLSTrackDemuxer(this, TrackInfo::TrackType::kVideoTrack,
    154                                        MakeUnique<VideoInfo>());
    155  }
    156 
    157  mInitPromise.ResolveIfExists(NS_OK, __func__);
    158 }
    159 
    160 void HLSDemuxer::OnError(int aErrorCode) {
    161  MOZ_ASSERT(OnTaskQueue());
    162  mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
    163 }
    164 
    165 RefPtr<HLSDemuxer::InitPromise> HLSDemuxer::Init() {
    166  RefPtr<HLSDemuxer> self = this;
    167  return InvokeAsync(GetTaskQueue(), __func__, [self]() {
    168    RefPtr<InitPromise> p = self->mInitPromise.Ensure(__func__);
    169    return p;
    170  });
    171 }
    172 
    173 void HLSDemuxer::NotifyDataArrived() {
    174  HLS_DEBUG("HLSDemuxer", "NotifyDataArrived");
    175 }
    176 
    177 uint32_t HLSDemuxer::GetNumberTracks(TrackType aType) const {
    178  switch (aType) {
    179    case TrackType::kAudioTrack:
    180      return mHLSDemuxerWrapper->GetNumberOfTracks(TrackType::kAudioTrack);
    181    case TrackType::kVideoTrack:
    182      return mHLSDemuxerWrapper->GetNumberOfTracks(TrackType::kVideoTrack);
    183    default:
    184      return 0;
    185  }
    186 }
    187 
    188 already_AddRefed<MediaTrackDemuxer> HLSDemuxer::GetTrackDemuxer(
    189    TrackType aType, uint32_t aTrackNumber) {
    190  RefPtr<HLSTrackDemuxer> e = nullptr;
    191  if (aType == TrackInfo::TrackType::kAudioTrack) {
    192    e = mAudioDemuxer;
    193  } else {
    194    e = mVideoDemuxer;
    195  }
    196  return e.forget();
    197 }
    198 
    199 bool HLSDemuxer::IsSeekable() const {
    200  return !mHLSDemuxerWrapper->IsLiveStream();
    201 }
    202 
    203 UniquePtr<EncryptionInfo> HLSDemuxer::GetCrypto() {
    204  // TODO: Currently, our HLS implementation doesn't support encrypted content.
    205  // Return null at this stage.
    206  return nullptr;
    207 }
    208 
    209 TimeUnit HLSDemuxer::GetNextKeyFrameTime() {
    210  MOZ_ASSERT(mHLSDemuxerWrapper);
    211  return TimeUnit::FromMicroseconds(mHLSDemuxerWrapper->GetNextKeyFrameTime());
    212 }
    213 
    214 bool HLSDemuxer::OnTaskQueue() const { return mTaskQueue->IsCurrentThreadIn(); }
    215 
    216 HLSDemuxer::~HLSDemuxer() {
    217  HLS_DEBUG("HLSDemuxer", "~HLSDemuxer()");
    218  mCallbackSupport->Detach();
    219  if (mHLSDemuxerWrapper) {
    220    mHLSDemuxerWrapper->Destroy();
    221    mHLSDemuxerWrapper = nullptr;
    222  }
    223  if (mJavaCallbacks) {
    224    HLSDemuxerCallbacksSupport::DisposeNative(mJavaCallbacks);
    225    mJavaCallbacks = nullptr;
    226  }
    227  mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
    228 }
    229 
    230 HLSTrackDemuxer::HLSTrackDemuxer(HLSDemuxer* aParent,
    231                                 TrackInfo::TrackType aType,
    232                                 UniquePtr<TrackInfo> aTrackInfo)
    233    : mParent(aParent),
    234      mType(aType),
    235      mMutex("HLSTrackDemuxer"),
    236      mTrackInfo(std::move(aTrackInfo)) {
    237  // Only support audio and video track currently.
    238  MOZ_ASSERT(mType == TrackInfo::kVideoTrack ||
    239             mType == TrackInfo::kAudioTrack);
    240  UpdateMediaInfo(0);
    241 }
    242 
    243 UniquePtr<TrackInfo> HLSTrackDemuxer::GetInfo() const {
    244  MutexAutoLock lock(mMutex);
    245  return mTrackInfo->Clone();
    246 }
    247 
    248 RefPtr<HLSTrackDemuxer::SeekPromise> HLSTrackDemuxer::Seek(
    249    const TimeUnit& aTime) {
    250  MOZ_ASSERT(mParent, "Called after BreackCycle()");
    251  return InvokeAsync<TimeUnit&&>(mParent->GetTaskQueue(), this, __func__,
    252                                 &HLSTrackDemuxer::DoSeek, aTime);
    253 }
    254 
    255 RefPtr<HLSTrackDemuxer::SeekPromise> HLSTrackDemuxer::DoSeek(
    256    const TimeUnit& aTime) {
    257  MOZ_ASSERT(mParent, "Called after BreackCycle()");
    258  MOZ_ASSERT(mParent->OnTaskQueue());
    259  mQueuedSample = nullptr;
    260  int64_t seekTimeUs = aTime.ToMicroseconds();
    261  bool result = mParent->mHLSDemuxerWrapper->Seek(seekTimeUs);
    262  if (!result) {
    263    return SeekPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA,
    264                                        __func__);
    265  }
    266  TimeUnit seekTime = TimeUnit::FromMicroseconds(seekTimeUs);
    267  return SeekPromise::CreateAndResolve(seekTime, __func__);
    268 }
    269 
    270 RefPtr<HLSTrackDemuxer::SamplesPromise> HLSTrackDemuxer::GetSamples(
    271    int32_t aNumSamples) {
    272  MOZ_ASSERT(mParent, "Called after BreackCycle()");
    273  return InvokeAsync(mParent->GetTaskQueue(), this, __func__,
    274                     &HLSTrackDemuxer::DoGetSamples, aNumSamples);
    275 }
    276 
    277 RefPtr<HLSTrackDemuxer::SamplesPromise> HLSTrackDemuxer::DoGetSamples(
    278    int32_t aNumSamples) {
    279  MOZ_ASSERT(mParent, "Called after BreackCycle()");
    280  MOZ_ASSERT(mParent->OnTaskQueue());
    281  if (!aNumSamples) {
    282    return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
    283                                           __func__);
    284  }
    285  RefPtr<SamplesHolder> samples = new SamplesHolder;
    286  if (mQueuedSample) {
    287    if (mQueuedSample->mEOS) {
    288      return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM,
    289                                             __func__);
    290    }
    291    MOZ_ASSERT(mQueuedSample->mKeyframe, "mQueuedSample must be a keyframe");
    292    samples->AppendSample(std::move(mQueuedSample));
    293    MOZ_ASSERT(!mQueuedSample);
    294    aNumSamples--;
    295  }
    296  if (aNumSamples == 0) {
    297    // Return the queued sample.
    298    return SamplesPromise::CreateAndResolve(samples, __func__);
    299  }
    300  mozilla::jni::ObjectArray::LocalRef demuxedSamples =
    301      (mType == TrackInfo::kAudioTrack)
    302          ? mParent->mHLSDemuxerWrapper->GetSamples(TrackInfo::kAudioTrack,
    303                                                    aNumSamples)
    304          : mParent->mHLSDemuxerWrapper->GetSamples(TrackInfo::kVideoTrack,
    305                                                    aNumSamples);
    306  nsTArray<jni::Object::LocalRef> sampleObjectArray(
    307      demuxedSamples->GetElements());
    308 
    309  if (sampleObjectArray.IsEmpty()) {
    310    return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA,
    311                                           __func__);
    312  }
    313 
    314  for (auto&& demuxedSample : sampleObjectArray) {
    315    java::GeckoHLSSample::LocalRef sample(std::move(demuxedSample));
    316    if (sample->IsEOS()) {
    317      HLS_DEBUG("HLSTrackDemuxer", "Met BUFFER_FLAG_END_OF_STREAM.");
    318      if (samples->GetSamples().IsEmpty()) {
    319        return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM,
    320                                               __func__);
    321      }
    322      mQueuedSample = new MediaRawData();
    323      mQueuedSample->mEOS = true;
    324      break;
    325    }
    326    RefPtr<MediaRawData> mrd = ConvertToMediaRawData(sample);
    327    if (!mrd) {
    328      return SamplesPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
    329    }
    330    if (!mrd->HasValidTime()) {
    331      return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
    332                                             __func__);
    333    }
    334    samples->AppendSample(std::move(mrd));
    335  }
    336  if (mType == TrackInfo::kVideoTrack &&
    337      (mNextKeyframeTime.isNothing() ||
    338       samples->GetSamples().LastElement()->mTime >=
    339           mNextKeyframeTime.value())) {
    340    // Only need to find NextKeyFrame for Video
    341    UpdateNextKeyFrameTime();
    342  }
    343 
    344  return SamplesPromise::CreateAndResolve(samples, __func__);
    345 }
    346 
    347 void HLSTrackDemuxer::UpdateMediaInfo(int index) {
    348  MOZ_ASSERT(mParent->OnTaskQueue());
    349  MOZ_ASSERT(mParent->mHLSDemuxerWrapper);
    350  MutexAutoLock lock(mMutex);
    351  jni::Object::LocalRef infoObj = nullptr;
    352  if (mType == TrackType::kAudioTrack) {
    353    infoObj = mParent->mHLSDemuxerWrapper->GetAudioInfo(index);
    354    if (!infoObj) {
    355      NS_WARNING("Failed to get audio info from Java wrapper");
    356      return;
    357    }
    358    auto* audioInfo = mTrackInfo->GetAsAudioInfo();
    359    MOZ_ASSERT(audioInfo != nullptr);
    360    HLS_DEBUG("HLSTrackDemuxer", "Update audio info (%d)", index);
    361    java::GeckoAudioInfo::LocalRef audioInfoObj(std::move(infoObj));
    362    audioInfo->mRate = audioInfoObj->Rate();
    363    audioInfo->mChannels = audioInfoObj->Channels();
    364    audioInfo->mProfile = audioInfoObj->Profile();
    365    audioInfo->mBitDepth = audioInfoObj->BitDepth();
    366    audioInfo->mMimeType =
    367        NS_ConvertUTF16toUTF8(audioInfoObj->MimeType()->ToString());
    368    audioInfo->mDuration = TimeUnit::FromMicroseconds(audioInfoObj->Duration());
    369    jni::ByteArray::LocalRef csdBytes = audioInfoObj->CodecSpecificData();
    370    if (csdBytes) {
    371      auto&& csd = csdBytes->GetElements();
    372      AudioCodecSpecificBinaryBlob blob;
    373      blob.mBinaryBlob->AppendElements(reinterpret_cast<uint8_t*>(&csd[0]),
    374                                       csd.Length());
    375      audioInfo->mCodecSpecificConfig =
    376          AudioCodecSpecificVariant{std::move(blob)};
    377    }
    378  } else {
    379    infoObj = mParent->mHLSDemuxerWrapper->GetVideoInfo(index);
    380    if (!infoObj) {
    381      NS_WARNING("Failed to get video info from Java wrapper");
    382      return;
    383    }
    384    auto* videoInfo = mTrackInfo->GetAsVideoInfo();
    385    MOZ_ASSERT(videoInfo != nullptr);
    386    java::GeckoVideoInfo::LocalRef videoInfoObj(std::move(infoObj));
    387    videoInfo->mStereoMode = getStereoMode(videoInfoObj->StereoMode());
    388    videoInfo->mRotation = getVideoInfoRotation(videoInfoObj->Rotation());
    389    videoInfo->mImage.width = videoInfoObj->DisplayWidth();
    390    videoInfo->mImage.height = videoInfoObj->DisplayHeight();
    391    videoInfo->mDisplay.width = videoInfoObj->PictureWidth();
    392    videoInfo->mDisplay.height = videoInfoObj->PictureHeight();
    393    videoInfo->mMimeType =
    394        NS_ConvertUTF16toUTF8(videoInfoObj->MimeType()->ToString());
    395    videoInfo->mDuration = TimeUnit::FromMicroseconds(videoInfoObj->Duration());
    396    HLS_DEBUG("HLSTrackDemuxer", "Update video info (%d) / I(%dx%d) / D(%dx%d)",
    397              index, videoInfo->mImage.width, videoInfo->mImage.height,
    398              videoInfo->mDisplay.width, videoInfo->mDisplay.height);
    399  }
    400 }
    401 
    402 CryptoSample HLSTrackDemuxer::ExtractCryptoSample(
    403    size_t aSampleSize,
    404    java::sdk::MediaCodec::CryptoInfo::LocalRef aCryptoInfo) {
    405  if (!aCryptoInfo) {
    406    return CryptoSample{};
    407  }
    408  // Extract Crypto information
    409  CryptoSample crypto;
    410  char const* msg = "";
    411  do {
    412    HLS_DEBUG("HLSTrackDemuxer", "Sample has Crypto Info");
    413 
    414    int32_t mode = 0;
    415    if (NS_FAILED(aCryptoInfo->Mode(&mode))) {
    416      msg = "Error when extracting encryption mode.";
    417      break;
    418    }
    419    // We currently only handle ctr mode.
    420    if (mode != java::sdk::MediaCodec::CRYPTO_MODE_AES_CTR) {
    421      msg = "Error: unexpected encryption mode.";
    422      break;
    423    }
    424 
    425    crypto.mCryptoScheme = CryptoScheme::Cenc;
    426 
    427    mozilla::jni::ByteArray::LocalRef ivData;
    428    if (NS_FAILED(aCryptoInfo->Iv(&ivData))) {
    429      msg = "Error when extracting encryption IV.";
    430      break;
    431    }
    432    // Data in mIV is uint8_t and jbyte is signed char
    433    auto&& ivArr = ivData->GetElements();
    434    crypto.mIV.AppendElements(reinterpret_cast<uint8_t*>(&ivArr[0]),
    435                              ivArr.Length());
    436    crypto.mIVSize = ivArr.Length();
    437    mozilla::jni::ByteArray::LocalRef keyData;
    438    if (NS_FAILED(aCryptoInfo->Key(&keyData))) {
    439      msg = "Error when extracting encryption key.";
    440      break;
    441    }
    442    auto&& keyArr = keyData->GetElements();
    443    // Data in mKeyId is uint8_t and jbyte is signed char
    444    crypto.mKeyId.AppendElements(reinterpret_cast<uint8_t*>(&keyArr[0]),
    445                                 keyArr.Length());
    446 
    447    mozilla::jni::IntArray::LocalRef clearData;
    448    if (NS_FAILED(aCryptoInfo->NumBytesOfClearData(&clearData))) {
    449      msg = "Error when extracting clear data.";
    450      break;
    451    }
    452    auto&& clearArr = clearData->GetElements();
    453    // Data in mPlainSizes is uint32_t, NumBytesOfClearData is int32_t
    454    crypto.mPlainSizes.AppendElements(reinterpret_cast<uint32_t*>(&clearArr[0]),
    455                                      clearArr.Length());
    456 
    457    mozilla::jni::IntArray::LocalRef encryptedData;
    458    if (NS_FAILED(aCryptoInfo->NumBytesOfEncryptedData(&encryptedData))) {
    459      msg = "Error when extracting encrypted data.";
    460      break;
    461    }
    462    auto&& encryptedArr = encryptedData->GetElements();
    463    // Data in mEncryptedSizes is uint32_t, NumBytesOfEncryptedData is int32_t
    464    crypto.mEncryptedSizes.AppendElements(
    465        reinterpret_cast<uint32_t*>(&encryptedArr[0]), encryptedArr.Length());
    466    int subSamplesNum = 0;
    467    if (NS_FAILED(aCryptoInfo->NumSubSamples(&subSamplesNum))) {
    468      msg = "Error when extracting subsamples.";
    469      break;
    470    }
    471    crypto.mPlainSizes[0] -= (aSampleSize - subSamplesNum);
    472 
    473    return crypto;
    474  } while (false);
    475 
    476  HLS_DEBUG("HLSTrackDemuxer", "%s", msg);
    477  return CryptoSample{};
    478 }
    479 
    480 RefPtr<MediaRawData> HLSTrackDemuxer::ConvertToMediaRawData(
    481    java::GeckoHLSSample::LocalRef aSample) {
    482  java::sdk::MediaCodec::BufferInfo::LocalRef info = aSample->Info();
    483  // Currently extract PTS, Size and Data without Crypto information.
    484  // Transform java Sample into MediaRawData
    485  RefPtr<MediaRawData> mrd = new MediaRawData();
    486  int64_t presentationTimeUs = 0;
    487  bool ok = NS_SUCCEEDED(info->PresentationTimeUs(&presentationTimeUs));
    488  mrd->mTime = TimeUnit::FromMicroseconds(presentationTimeUs);
    489  mrd->mTimecode = TimeUnit::FromMicroseconds(presentationTimeUs);
    490  mrd->mKeyframe = aSample->IsKeyFrame();
    491  mrd->mDuration = (mType == TrackInfo::kVideoTrack)
    492                       ? TimeUnit::FromMicroseconds(aSample->Duration())
    493                       : TimeUnit::Zero();
    494 
    495  int32_t size = 0;
    496  ok &= NS_SUCCEEDED(info->Size(&size));
    497  if (!ok) {
    498    HLS_DEBUG("HLSTrackDemuxer",
    499              "Error occurred during extraction from Sample java object.");
    500    return nullptr;
    501  }
    502 
    503  // Update A/V stream souce ID & Audio/VideoInfo for MFR.
    504  auto sampleFormatIndex = aSample->FormatIndex();
    505  if (mLastFormatIndex != sampleFormatIndex) {
    506    mLastFormatIndex = sampleFormatIndex;
    507    UpdateMediaInfo(mLastFormatIndex);
    508    MutexAutoLock lock(mMutex);
    509    mrd->mTrackInfo = new TrackInfoSharedPtr(*mTrackInfo, ++sStreamSourceID);
    510  }
    511 
    512  // Write payload into MediaRawData
    513  UniquePtr<MediaRawDataWriter> writer(mrd->CreateWriter());
    514  if (!writer->SetSize(size)) {
    515    HLS_DEBUG("HLSTrackDemuxer", "Exit failed to allocate media buffer");
    516    return nullptr;
    517  }
    518  jni::ByteBuffer::LocalRef dest =
    519      jni::ByteBuffer::New(writer->Data(), writer->Size());
    520  aSample->WriteToByteBuffer(dest);
    521 
    522  writer->mCrypto = ExtractCryptoSample(writer->Size(), aSample->CryptoInfo());
    523  return mrd;
    524 }
    525 
    526 void HLSTrackDemuxer::Reset() {
    527  MOZ_ASSERT(mParent, "Called after BreackCycle()");
    528  mQueuedSample = nullptr;
    529 }
    530 
    531 void HLSTrackDemuxer::UpdateNextKeyFrameTime() {
    532  MOZ_ASSERT(mParent, "Called after BreackCycle()");
    533  TimeUnit nextKeyFrameTime = mParent->GetNextKeyFrameTime();
    534  if (nextKeyFrameTime != mNextKeyframeTime.refOr(TimeUnit::FromInfinity())) {
    535    HLS_DEBUG("HLSTrackDemuxer", "Update mNextKeyframeTime to %" PRId64,
    536              nextKeyFrameTime.ToMicroseconds());
    537    mNextKeyframeTime = Some(nextKeyFrameTime);
    538  }
    539 }
    540 
    541 nsresult HLSTrackDemuxer::GetNextRandomAccessPoint(TimeUnit* aTime) {
    542  if (mNextKeyframeTime.isNothing()) {
    543    // There's no next key frame.
    544    *aTime = TimeUnit::FromInfinity();
    545  } else {
    546    *aTime = mNextKeyframeTime.value();
    547  }
    548  return NS_OK;
    549 }
    550 
    551 RefPtr<HLSTrackDemuxer::SkipAccessPointPromise>
    552 HLSTrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold) {
    553  return InvokeAsync(mParent->GetTaskQueue(), this, __func__,
    554                     &HLSTrackDemuxer::DoSkipToNextRandomAccessPoint,
    555                     aTimeThreshold);
    556 }
    557 
    558 RefPtr<HLSTrackDemuxer::SkipAccessPointPromise>
    559 HLSTrackDemuxer::DoSkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold) {
    560  MOZ_ASSERT(mParent, "Called after BreackCycle()");
    561  MOZ_ASSERT(mParent->OnTaskQueue());
    562  mQueuedSample = nullptr;
    563  uint32_t parsed = 0;
    564  bool found = false;
    565  MediaResult result = NS_ERROR_DOM_MEDIA_END_OF_STREAM;
    566  do {
    567    mozilla::jni::ObjectArray::LocalRef demuxedSamples =
    568        mParent->mHLSDemuxerWrapper->GetSamples(mType, 1);
    569    nsTArray<jni::Object::LocalRef> sampleObjectArray(
    570        demuxedSamples->GetElements());
    571    if (sampleObjectArray.IsEmpty()) {
    572      result = NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA;
    573      break;
    574    }
    575    parsed++;
    576    java::GeckoHLSSample::LocalRef sample(std::move(sampleObjectArray[0]));
    577    if (sample->IsEOS()) {
    578      result = NS_ERROR_DOM_MEDIA_END_OF_STREAM;
    579      break;
    580    }
    581    if (sample->IsKeyFrame()) {
    582      java::sdk::MediaCodec::BufferInfo::LocalRef info = sample->Info();
    583      int64_t presentationTimeUs = 0;
    584      if (NS_SUCCEEDED(info->PresentationTimeUs(&presentationTimeUs)) &&
    585          TimeUnit::FromMicroseconds(presentationTimeUs) >= aTimeThreshold) {
    586        RefPtr<MediaRawData> rawData = ConvertToMediaRawData(sample);
    587        if (!rawData) {
    588          result = NS_ERROR_OUT_OF_MEMORY;
    589          break;
    590        }
    591        if (!rawData->HasValidTime()) {
    592          result = NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
    593          break;
    594        }
    595        found = true;
    596        mQueuedSample = rawData;
    597        break;
    598      }
    599    }
    600  } while (true);
    601 
    602  if (!found) {
    603    return SkipAccessPointPromise::CreateAndReject(
    604        SkipFailureHolder(result, parsed), __func__);
    605  }
    606  return SkipAccessPointPromise::CreateAndResolve(parsed, __func__);
    607 }
    608 
    609 TimeIntervals HLSTrackDemuxer::GetBuffered() {
    610  MOZ_ASSERT(mParent, "Called after BreackCycle()");
    611  int64_t bufferedTime = mParent->mHLSDemuxerWrapper->GetBuffered();  // us
    612  return TimeIntervals(
    613      TimeInterval(TimeUnit(), TimeUnit::FromMicroseconds(bufferedTime)));
    614 }
    615 
    616 void HLSTrackDemuxer::BreakCycles() {
    617  RefPtr<HLSTrackDemuxer> self = this;
    618  nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
    619      "HLSTrackDemuxer::BreakCycles", [self]() { self->mParent = nullptr; });
    620  nsresult rv = mParent->GetTaskQueue()->Dispatch(task.forget());
    621  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
    622  (void)rv;
    623 }
    624 
    625 HLSTrackDemuxer::~HLSTrackDemuxer() {}
    626 
    627 }  // namespace mozilla