gecko-dev/dom/media/hls/HLSDemuxer.cpp

655 строки
21 KiB
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/Unused.h"
#include "nsPrintfCString.h"
using namespace mozilla::java;
namespace mozilla {
static Atomic<uint32_t> sStreamSourceID(0u);
typedef TrackInfo::TrackType TrackType;
using media::TimeUnit;
using media::TimeIntervals;
using media::TimeInterval;
static
VideoInfo::Rotation getVideoInfoRotation(int aRotation)
{
switch (aRotation) {
case 0:
return VideoInfo::Rotation::kDegree_0;
case 90:
return VideoInfo::Rotation::kDegree_90;
case 180:
return VideoInfo::Rotation::kDegree_180;
case 270:
return VideoInfo::Rotation::kDegree_270;
default:
return VideoInfo::Rotation::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 GeckoHLSDemuxerWrapper::Callbacks::Natives<HLSDemuxerCallbacksSupport>
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HLSDemuxerCallbacksSupport)
public:
typedef GeckoHLSDemuxerWrapper::Callbacks::Natives<HLSDemuxerCallbacksSupport> NativeCallbacks;
using NativeCallbacks::DisposeNative;
using NativeCallbacks::AttachNative;
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;
private:
~HLSDemuxerCallbacksSupport() { }
HLSDemuxer* mDemuxer;
};
HLSDemuxer::HLSDemuxer(int aPlayerId)
: mTaskQueue(new AutoTaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
/* aSupportsTailDispatch = */ false))
{
MOZ_ASSERT(NS_IsMainThread());
HLSDemuxerCallbacksSupport::Init();
mJavaCallbacks = GeckoHLSDemuxerWrapper::Callbacks::New();
MOZ_ASSERT(mJavaCallbacks);
mCallbackSupport = new HLSDemuxerCallbacksSupport(this);
HLSDemuxerCallbacksSupport::AttachNative(mJavaCallbacks,
mCallbackSupport);
mHLSDemuxerWrapper = 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(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->mSamples.AppendElement(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(Move(demuxedSample));
if (sample->IsEOS()) {
HLS_DEBUG("HLSTrackDemuxer", "Met BUFFER_FLAG_END_OF_STREAM.");
if (samples->mSamples.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__);
}
samples->mSamples.AppendElement(mrd);
}
if (mType == TrackInfo::kVideoTrack &&
(mNextKeyframeTime.isNothing() ||
samples->mSamples.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);
auto* audioInfo = mTrackInfo->GetAsAudioInfo();
if (infoObj && audioInfo) {
HLS_DEBUG("HLSTrackDemuxer", "Update audio info (%d)", index);
java::GeckoAudioInfo::LocalRef audioInfoObj(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());
auto&& csd = audioInfoObj->CodecSpecificData()->GetElements();
audioInfo->mCodecSpecificConfig->Clear();
audioInfo->mCodecSpecificConfig->AppendElements(reinterpret_cast<uint8_t*>(&csd[0]),
csd.Length());
}
} else {
infoObj = mParent->mHLSDemuxerWrapper->GetVideoInfo(index);
auto* videoInfo = mTrackInfo->GetAsVideoInfo();
if (infoObj && videoInfo) {
java::GeckoVideoInfo::LocalRef videoInfoObj(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::CryptoInfo::LocalRef aCryptoInfo)
{
if (!aCryptoInfo) {
return CryptoSample{};
}
// Extract Crypto information
CryptoSample crypto;
char const* msg = "";
do {
HLS_DEBUG("HLSTrackDemuxer", "Sample has Crypto Info");
crypto.mValid = true;
int32_t mode = 0;
if (NS_FAILED(aCryptoInfo->Mode(&mode))) {
msg = "Error when extracting encryption mode.";
break;
}
crypto.mMode = mode;
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;
}
// Data in mPlainSizes is uint16_t, NumBytesOfClearData is int32_t
// , so need a for loop to copy
for (const auto& b : clearData->GetElements()) {
crypto.mPlainSizes.AppendElement(b);
}
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::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(Move(sampleObjectArray[0]));
if (sample->IsEOS()) {
result = NS_ERROR_DOM_MEDIA_END_OF_STREAM;
break;
}
if (sample->IsKeyFrame()) {
java::sdk::BufferInfo::LocalRef info = sample->Info();
int64_t presentationTimeUs = 0;
bool ok = NS_SUCCEEDED(info->PresentationTimeUs(&presentationTimeUs));
if (ok && TimeUnit::FromMicroseconds(presentationTimeUs) >= aTimeThreshold) {
found = true;
mQueuedSample = ConvertToMediaRawData(sample);
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