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/. */
7 #include "HLSDemuxer.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 "mozilla/Unused.h"
19 #include "nsPrintfCString.h"
23 static Atomic
<uint32_t> sStreamSourceID(0u);
25 typedef TrackInfo::TrackType TrackType
;
26 using media::TimeInterval
;
27 using media::TimeIntervals
;
28 using media::TimeUnit
;
30 static VideoInfo::Rotation
getVideoInfoRotation(int aRotation
) {
33 return VideoInfo::Rotation::kDegree_0
;
35 return VideoInfo::Rotation::kDegree_90
;
37 return VideoInfo::Rotation::kDegree_180
;
39 return VideoInfo::Rotation::kDegree_270
;
41 return VideoInfo::Rotation::kDegree_0
;
45 static mozilla::StereoMode
getStereoMode(int aMode
) {
48 return mozilla::StereoMode::MONO
;
50 return mozilla::StereoMode::TOP_BOTTOM
;
52 return mozilla::StereoMode::LEFT_RIGHT
;
54 return mozilla::StereoMode::MONO
;
58 // HLSDemuxerCallbacksSupport is a native implemented callback class for
59 // Callbacks in GeckoHLSDemuxerWrapper.java.
60 // The callback functions will be invoked by JAVA-side thread.
61 // Should dispatch the task to the demuxer's task queue.
62 // We ensure the callback will never be invoked after
63 // HLSDemuxerCallbacksSupport::DisposeNative has been called in ~HLSDemuxer.
64 class HLSDemuxer::HLSDemuxerCallbacksSupport
65 : public java::GeckoHLSDemuxerWrapper::Callbacks::Natives
<
66 HLSDemuxerCallbacksSupport
> {
67 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HLSDemuxerCallbacksSupport
)
69 typedef java::GeckoHLSDemuxerWrapper::Callbacks::Natives
<
70 HLSDemuxerCallbacksSupport
>
72 using NativeCallbacks::AttachNative
;
73 using NativeCallbacks::DisposeNative
;
75 explicit HLSDemuxerCallbacksSupport(HLSDemuxer
* aDemuxer
)
76 : mMutex("HLSDemuxerCallbacksSupport"), mDemuxer(aDemuxer
) {
80 void OnInitialized(bool aHasAudio
, bool aHasVideo
) {
81 HLS_DEBUG("HLSDemuxerCallbacksSupport", "OnInitialized");
82 MutexAutoLock
lock(mMutex
);
86 RefPtr
<HLSDemuxerCallbacksSupport
> self
= this;
87 nsresult rv
= mDemuxer
->GetTaskQueue()->Dispatch(NS_NewRunnableFunction(
88 "HLSDemuxer::HLSDemuxerCallbacksSupport::OnInitialized", [=]() {
89 MutexAutoLock
lock(self
->mMutex
);
91 self
->mDemuxer
->OnInitialized(aHasAudio
, aHasVideo
);
94 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv
));
98 void OnError(int aErrorCode
) {
99 HLS_DEBUG("HLSDemuxerCallbacksSupport", "Got error(%d) from java side",
101 MutexAutoLock
lock(mMutex
);
105 RefPtr
<HLSDemuxerCallbacksSupport
> self
= this;
106 nsresult rv
= mDemuxer
->GetTaskQueue()->Dispatch(NS_NewRunnableFunction(
107 "HLSDemuxer::HLSDemuxerCallbacksSupport::OnError", [=]() {
108 MutexAutoLock
lock(self
->mMutex
);
109 if (self
->mDemuxer
) {
110 self
->mDemuxer
->OnError(aErrorCode
);
113 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv
));
118 MutexAutoLock
lock(mMutex
);
122 Mutex mMutex MOZ_UNANNOTATED
;
125 ~HLSDemuxerCallbacksSupport() {}
126 HLSDemuxer
* mDemuxer
;
129 HLSDemuxer::HLSDemuxer(int aPlayerId
)
130 : mTaskQueue(TaskQueue::Create(
131 GetMediaThreadPool(MediaThreadType::SUPERVISOR
), "HLSDemuxer",
132 /* aSupportsTailDispatch = */ false)) {
133 MOZ_ASSERT(NS_IsMainThread());
134 HLSDemuxerCallbacksSupport::Init();
135 mJavaCallbacks
= java::GeckoHLSDemuxerWrapper::Callbacks::New();
136 MOZ_ASSERT(mJavaCallbacks
);
138 mCallbackSupport
= new HLSDemuxerCallbacksSupport(this);
139 HLSDemuxerCallbacksSupport::AttachNative(mJavaCallbacks
, mCallbackSupport
);
142 java::GeckoHLSDemuxerWrapper::Create(aPlayerId
, mJavaCallbacks
);
143 MOZ_ASSERT(mHLSDemuxerWrapper
);
146 void HLSDemuxer::OnInitialized(bool aHasAudio
, bool aHasVideo
) {
147 MOZ_ASSERT(OnTaskQueue());
150 mAudioDemuxer
= new HLSTrackDemuxer(this, TrackInfo::TrackType::kAudioTrack
,
151 MakeUnique
<AudioInfo
>());
154 mVideoDemuxer
= new HLSTrackDemuxer(this, TrackInfo::TrackType::kVideoTrack
,
155 MakeUnique
<VideoInfo
>());
158 mInitPromise
.ResolveIfExists(NS_OK
, __func__
);
161 void HLSDemuxer::OnError(int aErrorCode
) {
162 MOZ_ASSERT(OnTaskQueue());
163 mInitPromise
.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED
, __func__
);
166 RefPtr
<HLSDemuxer::InitPromise
> HLSDemuxer::Init() {
167 RefPtr
<HLSDemuxer
> self
= this;
168 return InvokeAsync(GetTaskQueue(), __func__
, [self
]() {
169 RefPtr
<InitPromise
> p
= self
->mInitPromise
.Ensure(__func__
);
174 void HLSDemuxer::NotifyDataArrived() {
175 HLS_DEBUG("HLSDemuxer", "NotifyDataArrived");
178 uint32_t HLSDemuxer::GetNumberTracks(TrackType aType
) const {
180 case TrackType::kAudioTrack
:
181 return mHLSDemuxerWrapper
->GetNumberOfTracks(TrackType::kAudioTrack
);
182 case TrackType::kVideoTrack
:
183 return mHLSDemuxerWrapper
->GetNumberOfTracks(TrackType::kVideoTrack
);
189 already_AddRefed
<MediaTrackDemuxer
> HLSDemuxer::GetTrackDemuxer(
190 TrackType aType
, uint32_t aTrackNumber
) {
191 RefPtr
<HLSTrackDemuxer
> e
= nullptr;
192 if (aType
== TrackInfo::TrackType::kAudioTrack
) {
200 bool HLSDemuxer::IsSeekable() const {
201 return !mHLSDemuxerWrapper
->IsLiveStream();
204 UniquePtr
<EncryptionInfo
> HLSDemuxer::GetCrypto() {
205 // TODO: Currently, our HLS implementation doesn't support encrypted content.
206 // Return null at this stage.
210 TimeUnit
HLSDemuxer::GetNextKeyFrameTime() {
211 MOZ_ASSERT(mHLSDemuxerWrapper
);
212 return TimeUnit::FromMicroseconds(mHLSDemuxerWrapper
->GetNextKeyFrameTime());
215 bool HLSDemuxer::OnTaskQueue() const { return mTaskQueue
->IsCurrentThreadIn(); }
217 HLSDemuxer::~HLSDemuxer() {
218 HLS_DEBUG("HLSDemuxer", "~HLSDemuxer()");
219 mCallbackSupport
->Detach();
220 if (mHLSDemuxerWrapper
) {
221 mHLSDemuxerWrapper
->Destroy();
222 mHLSDemuxerWrapper
= nullptr;
224 if (mJavaCallbacks
) {
225 HLSDemuxerCallbacksSupport::DisposeNative(mJavaCallbacks
);
226 mJavaCallbacks
= nullptr;
228 mInitPromise
.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED
, __func__
);
231 HLSTrackDemuxer::HLSTrackDemuxer(HLSDemuxer
* aParent
,
232 TrackInfo::TrackType aType
,
233 UniquePtr
<TrackInfo
> aTrackInfo
)
236 mMutex("HLSTrackDemuxer"),
237 mTrackInfo(std::move(aTrackInfo
)) {
238 // Only support audio and video track currently.
239 MOZ_ASSERT(mType
== TrackInfo::kVideoTrack
||
240 mType
== TrackInfo::kAudioTrack
);
244 UniquePtr
<TrackInfo
> HLSTrackDemuxer::GetInfo() const {
245 MutexAutoLock
lock(mMutex
);
246 return mTrackInfo
->Clone();
249 RefPtr
<HLSTrackDemuxer::SeekPromise
> HLSTrackDemuxer::Seek(
250 const TimeUnit
& aTime
) {
251 MOZ_ASSERT(mParent
, "Called after BreackCycle()");
252 return InvokeAsync
<TimeUnit
&&>(mParent
->GetTaskQueue(), this, __func__
,
253 &HLSTrackDemuxer::DoSeek
, aTime
);
256 RefPtr
<HLSTrackDemuxer::SeekPromise
> HLSTrackDemuxer::DoSeek(
257 const TimeUnit
& aTime
) {
258 MOZ_ASSERT(mParent
, "Called after BreackCycle()");
259 MOZ_ASSERT(mParent
->OnTaskQueue());
260 mQueuedSample
= nullptr;
261 int64_t seekTimeUs
= aTime
.ToMicroseconds();
262 bool result
= mParent
->mHLSDemuxerWrapper
->Seek(seekTimeUs
);
264 return SeekPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA
,
267 TimeUnit seekTime
= TimeUnit::FromMicroseconds(seekTimeUs
);
268 return SeekPromise::CreateAndResolve(seekTime
, __func__
);
271 RefPtr
<HLSTrackDemuxer::SamplesPromise
> HLSTrackDemuxer::GetSamples(
272 int32_t aNumSamples
) {
273 MOZ_ASSERT(mParent
, "Called after BreackCycle()");
274 return InvokeAsync(mParent
->GetTaskQueue(), this, __func__
,
275 &HLSTrackDemuxer::DoGetSamples
, aNumSamples
);
278 RefPtr
<HLSTrackDemuxer::SamplesPromise
> HLSTrackDemuxer::DoGetSamples(
279 int32_t aNumSamples
) {
280 MOZ_ASSERT(mParent
, "Called after BreackCycle()");
281 MOZ_ASSERT(mParent
->OnTaskQueue());
283 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR
,
286 RefPtr
<SamplesHolder
> samples
= new SamplesHolder
;
288 if (mQueuedSample
->mEOS
) {
289 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM
,
292 MOZ_ASSERT(mQueuedSample
->mKeyframe
, "mQueuedSample must be a keyframe");
293 samples
->AppendSample(mQueuedSample
);
294 mQueuedSample
= nullptr;
297 if (aNumSamples
== 0) {
298 // Return the queued sample.
299 return SamplesPromise::CreateAndResolve(samples
, __func__
);
301 mozilla::jni::ObjectArray::LocalRef demuxedSamples
=
302 (mType
== TrackInfo::kAudioTrack
)
303 ? mParent
->mHLSDemuxerWrapper
->GetSamples(TrackInfo::kAudioTrack
,
305 : mParent
->mHLSDemuxerWrapper
->GetSamples(TrackInfo::kVideoTrack
,
307 nsTArray
<jni::Object::LocalRef
> sampleObjectArray(
308 demuxedSamples
->GetElements());
310 if (sampleObjectArray
.IsEmpty()) {
311 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA
,
315 for (auto&& demuxedSample
: sampleObjectArray
) {
316 java::GeckoHLSSample::LocalRef
sample(std::move(demuxedSample
));
317 if (sample
->IsEOS()) {
318 HLS_DEBUG("HLSTrackDemuxer", "Met BUFFER_FLAG_END_OF_STREAM.");
319 if (samples
->GetSamples().IsEmpty()) {
320 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM
,
323 mQueuedSample
= new MediaRawData();
324 mQueuedSample
->mEOS
= true;
327 RefPtr
<MediaRawData
> mrd
= ConvertToMediaRawData(sample
);
329 return SamplesPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY
, __func__
);
331 if (!mrd
->HasValidTime()) {
332 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR
,
335 samples
->AppendSample(mrd
);
337 if (mType
== TrackInfo::kVideoTrack
&&
338 (mNextKeyframeTime
.isNothing() ||
339 samples
->GetSamples().LastElement()->mTime
>=
340 mNextKeyframeTime
.value())) {
341 // Only need to find NextKeyFrame for Video
342 UpdateNextKeyFrameTime();
345 return SamplesPromise::CreateAndResolve(samples
, __func__
);
348 void HLSTrackDemuxer::UpdateMediaInfo(int index
) {
349 MOZ_ASSERT(mParent
->OnTaskQueue());
350 MOZ_ASSERT(mParent
->mHLSDemuxerWrapper
);
351 MutexAutoLock
lock(mMutex
);
352 jni::Object::LocalRef infoObj
= nullptr;
353 if (mType
== TrackType::kAudioTrack
) {
354 infoObj
= mParent
->mHLSDemuxerWrapper
->GetAudioInfo(index
);
356 NS_WARNING("Failed to get audio info from Java wrapper");
359 auto* audioInfo
= mTrackInfo
->GetAsAudioInfo();
360 MOZ_ASSERT(audioInfo
!= nullptr);
361 HLS_DEBUG("HLSTrackDemuxer", "Update audio info (%d)", index
);
362 java::GeckoAudioInfo::LocalRef
audioInfoObj(std::move(infoObj
));
363 audioInfo
->mRate
= audioInfoObj
->Rate();
364 audioInfo
->mChannels
= audioInfoObj
->Channels();
365 audioInfo
->mProfile
= audioInfoObj
->Profile();
366 audioInfo
->mBitDepth
= audioInfoObj
->BitDepth();
367 audioInfo
->mMimeType
=
368 NS_ConvertUTF16toUTF8(audioInfoObj
->MimeType()->ToString());
369 audioInfo
->mDuration
= TimeUnit::FromMicroseconds(audioInfoObj
->Duration());
370 jni::ByteArray::LocalRef csdBytes
= audioInfoObj
->CodecSpecificData();
372 auto&& csd
= csdBytes
->GetElements();
373 AudioCodecSpecificBinaryBlob blob
;
374 blob
.mBinaryBlob
->AppendElements(reinterpret_cast<uint8_t*>(&csd
[0]),
376 audioInfo
->mCodecSpecificConfig
=
377 AudioCodecSpecificVariant
{std::move(blob
)};
380 infoObj
= mParent
->mHLSDemuxerWrapper
->GetVideoInfo(index
);
382 NS_WARNING("Failed to get video info from Java wrapper");
385 auto* videoInfo
= mTrackInfo
->GetAsVideoInfo();
386 MOZ_ASSERT(videoInfo
!= nullptr);
387 java::GeckoVideoInfo::LocalRef
videoInfoObj(std::move(infoObj
));
388 videoInfo
->mStereoMode
= getStereoMode(videoInfoObj
->StereoMode());
389 videoInfo
->mRotation
= getVideoInfoRotation(videoInfoObj
->Rotation());
390 videoInfo
->mImage
.width
= videoInfoObj
->DisplayWidth();
391 videoInfo
->mImage
.height
= videoInfoObj
->DisplayHeight();
392 videoInfo
->mDisplay
.width
= videoInfoObj
->PictureWidth();
393 videoInfo
->mDisplay
.height
= videoInfoObj
->PictureHeight();
394 videoInfo
->mMimeType
=
395 NS_ConvertUTF16toUTF8(videoInfoObj
->MimeType()->ToString());
396 videoInfo
->mDuration
= TimeUnit::FromMicroseconds(videoInfoObj
->Duration());
397 HLS_DEBUG("HLSTrackDemuxer", "Update video info (%d) / I(%dx%d) / D(%dx%d)",
398 index
, videoInfo
->mImage
.width
, videoInfo
->mImage
.height
,
399 videoInfo
->mDisplay
.width
, videoInfo
->mDisplay
.height
);
403 CryptoSample
HLSTrackDemuxer::ExtractCryptoSample(
405 java::sdk::MediaCodec::CryptoInfo::LocalRef aCryptoInfo
) {
407 return CryptoSample
{};
409 // Extract Crypto information
411 char const* msg
= "";
413 HLS_DEBUG("HLSTrackDemuxer", "Sample has Crypto Info");
416 if (NS_FAILED(aCryptoInfo
->Mode(&mode
))) {
417 msg
= "Error when extracting encryption mode.";
420 // We currently only handle ctr mode.
421 if (mode
!= java::sdk::MediaCodec::CRYPTO_MODE_AES_CTR
) {
422 msg
= "Error: unexpected encryption mode.";
426 crypto
.mCryptoScheme
= CryptoScheme::Cenc
;
428 mozilla::jni::ByteArray::LocalRef ivData
;
429 if (NS_FAILED(aCryptoInfo
->Iv(&ivData
))) {
430 msg
= "Error when extracting encryption IV.";
433 // Data in mIV is uint8_t and jbyte is signed char
434 auto&& ivArr
= ivData
->GetElements();
435 crypto
.mIV
.AppendElements(reinterpret_cast<uint8_t*>(&ivArr
[0]),
437 crypto
.mIVSize
= ivArr
.Length();
438 mozilla::jni::ByteArray::LocalRef keyData
;
439 if (NS_FAILED(aCryptoInfo
->Key(&keyData
))) {
440 msg
= "Error when extracting encryption key.";
443 auto&& keyArr
= keyData
->GetElements();
444 // Data in mKeyId is uint8_t and jbyte is signed char
445 crypto
.mKeyId
.AppendElements(reinterpret_cast<uint8_t*>(&keyArr
[0]),
448 mozilla::jni::IntArray::LocalRef clearData
;
449 if (NS_FAILED(aCryptoInfo
->NumBytesOfClearData(&clearData
))) {
450 msg
= "Error when extracting clear data.";
453 auto&& clearArr
= clearData
->GetElements();
454 // Data in mPlainSizes is uint32_t, NumBytesOfClearData is int32_t
455 crypto
.mPlainSizes
.AppendElements(reinterpret_cast<uint32_t*>(&clearArr
[0]),
458 mozilla::jni::IntArray::LocalRef encryptedData
;
459 if (NS_FAILED(aCryptoInfo
->NumBytesOfEncryptedData(&encryptedData
))) {
460 msg
= "Error when extracting encrypted data.";
463 auto&& encryptedArr
= encryptedData
->GetElements();
464 // Data in mEncryptedSizes is uint32_t, NumBytesOfEncryptedData is int32_t
465 crypto
.mEncryptedSizes
.AppendElements(
466 reinterpret_cast<uint32_t*>(&encryptedArr
[0]), encryptedArr
.Length());
467 int subSamplesNum
= 0;
468 if (NS_FAILED(aCryptoInfo
->NumSubSamples(&subSamplesNum
))) {
469 msg
= "Error when extracting subsamples.";
472 crypto
.mPlainSizes
[0] -= (aSampleSize
- subSamplesNum
);
477 HLS_DEBUG("HLSTrackDemuxer", "%s", msg
);
478 return CryptoSample
{};
481 RefPtr
<MediaRawData
> HLSTrackDemuxer::ConvertToMediaRawData(
482 java::GeckoHLSSample::LocalRef aSample
) {
483 java::sdk::MediaCodec::BufferInfo::LocalRef info
= aSample
->Info();
484 // Currently extract PTS, Size and Data without Crypto information.
485 // Transform java Sample into MediaRawData
486 RefPtr
<MediaRawData
> mrd
= new MediaRawData();
487 int64_t presentationTimeUs
= 0;
488 bool ok
= NS_SUCCEEDED(info
->PresentationTimeUs(&presentationTimeUs
));
489 mrd
->mTime
= TimeUnit::FromMicroseconds(presentationTimeUs
);
490 mrd
->mTimecode
= TimeUnit::FromMicroseconds(presentationTimeUs
);
491 mrd
->mKeyframe
= aSample
->IsKeyFrame();
492 mrd
->mDuration
= (mType
== TrackInfo::kVideoTrack
)
493 ? TimeUnit::FromMicroseconds(aSample
->Duration())
497 ok
&= NS_SUCCEEDED(info
->Size(&size
));
499 HLS_DEBUG("HLSTrackDemuxer",
500 "Error occurred during extraction from Sample java object.");
504 // Update A/V stream souce ID & Audio/VideoInfo for MFR.
505 auto sampleFormatIndex
= aSample
->FormatIndex();
506 if (mLastFormatIndex
!= sampleFormatIndex
) {
507 mLastFormatIndex
= sampleFormatIndex
;
508 UpdateMediaInfo(mLastFormatIndex
);
509 MutexAutoLock
lock(mMutex
);
510 mrd
->mTrackInfo
= new TrackInfoSharedPtr(*mTrackInfo
, ++sStreamSourceID
);
513 // Write payload into MediaRawData
514 UniquePtr
<MediaRawDataWriter
> writer(mrd
->CreateWriter());
515 if (!writer
->SetSize(size
)) {
516 HLS_DEBUG("HLSTrackDemuxer", "Exit failed to allocate media buffer");
519 jni::ByteBuffer::LocalRef dest
=
520 jni::ByteBuffer::New(writer
->Data(), writer
->Size());
521 aSample
->WriteToByteBuffer(dest
);
523 writer
->mCrypto
= ExtractCryptoSample(writer
->Size(), aSample
->CryptoInfo());
527 void HLSTrackDemuxer::Reset() {
528 MOZ_ASSERT(mParent
, "Called after BreackCycle()");
529 mQueuedSample
= nullptr;
532 void HLSTrackDemuxer::UpdateNextKeyFrameTime() {
533 MOZ_ASSERT(mParent
, "Called after BreackCycle()");
534 TimeUnit nextKeyFrameTime
= mParent
->GetNextKeyFrameTime();
535 if (nextKeyFrameTime
!= mNextKeyframeTime
.refOr(TimeUnit::FromInfinity())) {
536 HLS_DEBUG("HLSTrackDemuxer", "Update mNextKeyframeTime to %" PRId64
,
537 nextKeyFrameTime
.ToMicroseconds());
538 mNextKeyframeTime
= Some(nextKeyFrameTime
);
542 nsresult
HLSTrackDemuxer::GetNextRandomAccessPoint(TimeUnit
* aTime
) {
543 if (mNextKeyframeTime
.isNothing()) {
544 // There's no next key frame.
545 *aTime
= TimeUnit::FromInfinity();
547 *aTime
= mNextKeyframeTime
.value();
552 RefPtr
<HLSTrackDemuxer::SkipAccessPointPromise
>
553 HLSTrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit
& aTimeThreshold
) {
554 return InvokeAsync(mParent
->GetTaskQueue(), this, __func__
,
555 &HLSTrackDemuxer::DoSkipToNextRandomAccessPoint
,
559 RefPtr
<HLSTrackDemuxer::SkipAccessPointPromise
>
560 HLSTrackDemuxer::DoSkipToNextRandomAccessPoint(const TimeUnit
& aTimeThreshold
) {
561 MOZ_ASSERT(mParent
, "Called after BreackCycle()");
562 MOZ_ASSERT(mParent
->OnTaskQueue());
563 mQueuedSample
= nullptr;
566 MediaResult result
= NS_ERROR_DOM_MEDIA_END_OF_STREAM
;
568 mozilla::jni::ObjectArray::LocalRef demuxedSamples
=
569 mParent
->mHLSDemuxerWrapper
->GetSamples(mType
, 1);
570 nsTArray
<jni::Object::LocalRef
> sampleObjectArray(
571 demuxedSamples
->GetElements());
572 if (sampleObjectArray
.IsEmpty()) {
573 result
= NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA
;
577 java::GeckoHLSSample::LocalRef
sample(std::move(sampleObjectArray
[0]));
578 if (sample
->IsEOS()) {
579 result
= NS_ERROR_DOM_MEDIA_END_OF_STREAM
;
582 if (sample
->IsKeyFrame()) {
583 java::sdk::MediaCodec::BufferInfo::LocalRef info
= sample
->Info();
584 int64_t presentationTimeUs
= 0;
585 if (NS_SUCCEEDED(info
->PresentationTimeUs(&presentationTimeUs
)) &&
586 TimeUnit::FromMicroseconds(presentationTimeUs
) >= aTimeThreshold
) {
587 RefPtr
<MediaRawData
> rawData
= ConvertToMediaRawData(sample
);
589 result
= NS_ERROR_OUT_OF_MEMORY
;
592 if (!rawData
->HasValidTime()) {
593 result
= NS_ERROR_DOM_MEDIA_DEMUXER_ERR
;
597 mQueuedSample
= rawData
;
604 return SkipAccessPointPromise::CreateAndReject(
605 SkipFailureHolder(result
, parsed
), __func__
);
607 return SkipAccessPointPromise::CreateAndResolve(parsed
, __func__
);
610 TimeIntervals
HLSTrackDemuxer::GetBuffered() {
611 MOZ_ASSERT(mParent
, "Called after BreackCycle()");
612 int64_t bufferedTime
= mParent
->mHLSDemuxerWrapper
->GetBuffered(); // us
613 return TimeIntervals(
614 TimeInterval(TimeUnit(), TimeUnit::FromMicroseconds(bufferedTime
)));
617 void HLSTrackDemuxer::BreakCycles() {
618 RefPtr
<HLSTrackDemuxer
> self
= this;
619 nsCOMPtr
<nsIRunnable
> task
= NS_NewRunnableFunction(
620 "HLSTrackDemuxer::BreakCycles", [self
]() { self
->mParent
= nullptr; });
621 nsresult rv
= mParent
->GetTaskQueue()->Dispatch(task
.forget());
622 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv
));
626 HLSTrackDemuxer::~HLSTrackDemuxer() {}
628 } // namespace mozilla