Bug 1839170 - Refactor Snap pulling, Add Firefox Snap Core22 and GNOME 42 SDK symbols...
[gecko.git] / dom / media / hls / HLSDemuxer.cpp
blobd8b951c6e7af42c4adebe1b52b5c1f9e39c7f72e
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"
9 #include <algorithm>
10 #include <limits>
11 #include <stdint.h>
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 "mozilla/Unused.h"
19 #include "nsPrintfCString.h"
21 namespace mozilla {
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) {
31 switch (aRotation) {
32 case 0:
33 return VideoInfo::Rotation::kDegree_0;
34 case 90:
35 return VideoInfo::Rotation::kDegree_90;
36 case 180:
37 return VideoInfo::Rotation::kDegree_180;
38 case 270:
39 return VideoInfo::Rotation::kDegree_270;
40 default:
41 return VideoInfo::Rotation::kDegree_0;
45 static mozilla::StereoMode getStereoMode(int aMode) {
46 switch (aMode) {
47 case 0:
48 return mozilla::StereoMode::MONO;
49 case 1:
50 return mozilla::StereoMode::TOP_BOTTOM;
51 case 2:
52 return mozilla::StereoMode::LEFT_RIGHT;
53 default:
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)
68 public:
69 typedef java::GeckoHLSDemuxerWrapper::Callbacks::Natives<
70 HLSDemuxerCallbacksSupport>
71 NativeCallbacks;
72 using NativeCallbacks::AttachNative;
73 using NativeCallbacks::DisposeNative;
75 explicit HLSDemuxerCallbacksSupport(HLSDemuxer* aDemuxer)
76 : mMutex("HLSDemuxerCallbacksSupport"), mDemuxer(aDemuxer) {
77 MOZ_ASSERT(mDemuxer);
80 void OnInitialized(bool aHasAudio, bool aHasVideo) {
81 HLS_DEBUG("HLSDemuxerCallbacksSupport", "OnInitialized");
82 MutexAutoLock lock(mMutex);
83 if (!mDemuxer) {
84 return;
86 RefPtr<HLSDemuxerCallbacksSupport> self = this;
87 nsresult rv = mDemuxer->GetTaskQueue()->Dispatch(NS_NewRunnableFunction(
88 "HLSDemuxer::HLSDemuxerCallbacksSupport::OnInitialized", [=]() {
89 MutexAutoLock lock(self->mMutex);
90 if (self->mDemuxer) {
91 self->mDemuxer->OnInitialized(aHasAudio, aHasVideo);
93 }));
94 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
95 Unused << rv;
98 void OnError(int aErrorCode) {
99 HLS_DEBUG("HLSDemuxerCallbacksSupport", "Got error(%d) from java side",
100 aErrorCode);
101 MutexAutoLock lock(mMutex);
102 if (!mDemuxer) {
103 return;
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);
112 }));
113 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
114 Unused << rv;
117 void Detach() {
118 MutexAutoLock lock(mMutex);
119 mDemuxer = nullptr;
122 Mutex mMutex MOZ_UNANNOTATED;
124 private:
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);
141 mHLSDemuxerWrapper =
142 java::GeckoHLSDemuxerWrapper::Create(aPlayerId, mJavaCallbacks);
143 MOZ_ASSERT(mHLSDemuxerWrapper);
146 void HLSDemuxer::OnInitialized(bool aHasAudio, bool aHasVideo) {
147 MOZ_ASSERT(OnTaskQueue());
149 if (aHasAudio) {
150 mAudioDemuxer = new HLSTrackDemuxer(this, TrackInfo::TrackType::kAudioTrack,
151 MakeUnique<AudioInfo>());
153 if (aHasVideo) {
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__);
170 return p;
174 void HLSDemuxer::NotifyDataArrived() {
175 HLS_DEBUG("HLSDemuxer", "NotifyDataArrived");
178 uint32_t HLSDemuxer::GetNumberTracks(TrackType aType) const {
179 switch (aType) {
180 case TrackType::kAudioTrack:
181 return mHLSDemuxerWrapper->GetNumberOfTracks(TrackType::kAudioTrack);
182 case TrackType::kVideoTrack:
183 return mHLSDemuxerWrapper->GetNumberOfTracks(TrackType::kVideoTrack);
184 default:
185 return 0;
189 already_AddRefed<MediaTrackDemuxer> HLSDemuxer::GetTrackDemuxer(
190 TrackType aType, uint32_t aTrackNumber) {
191 RefPtr<HLSTrackDemuxer> e = nullptr;
192 if (aType == TrackInfo::TrackType::kAudioTrack) {
193 e = mAudioDemuxer;
194 } else {
195 e = mVideoDemuxer;
197 return e.forget();
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.
207 return nullptr;
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)
234 : mParent(aParent),
235 mType(aType),
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);
241 UpdateMediaInfo(0);
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);
263 if (!result) {
264 return SeekPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA,
265 __func__);
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());
282 if (!aNumSamples) {
283 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
284 __func__);
286 RefPtr<SamplesHolder> samples = new SamplesHolder;
287 if (mQueuedSample) {
288 if (mQueuedSample->mEOS) {
289 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM,
290 __func__);
292 MOZ_ASSERT(mQueuedSample->mKeyframe, "mQueuedSample must be a keyframe");
293 samples->AppendSample(mQueuedSample);
294 mQueuedSample = nullptr;
295 aNumSamples--;
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,
304 aNumSamples)
305 : mParent->mHLSDemuxerWrapper->GetSamples(TrackInfo::kVideoTrack,
306 aNumSamples);
307 nsTArray<jni::Object::LocalRef> sampleObjectArray(
308 demuxedSamples->GetElements());
310 if (sampleObjectArray.IsEmpty()) {
311 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA,
312 __func__);
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,
321 __func__);
323 mQueuedSample = new MediaRawData();
324 mQueuedSample->mEOS = true;
325 break;
327 RefPtr<MediaRawData> mrd = ConvertToMediaRawData(sample);
328 if (!mrd) {
329 return SamplesPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
331 if (!mrd->HasValidTime()) {
332 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
333 __func__);
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);
355 if (!infoObj) {
356 NS_WARNING("Failed to get audio info from Java wrapper");
357 return;
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();
371 if (csdBytes) {
372 auto&& csd = csdBytes->GetElements();
373 AudioCodecSpecificBinaryBlob blob;
374 blob.mBinaryBlob->AppendElements(reinterpret_cast<uint8_t*>(&csd[0]),
375 csd.Length());
376 audioInfo->mCodecSpecificConfig =
377 AudioCodecSpecificVariant{std::move(blob)};
379 } else {
380 infoObj = mParent->mHLSDemuxerWrapper->GetVideoInfo(index);
381 if (!infoObj) {
382 NS_WARNING("Failed to get video info from Java wrapper");
383 return;
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(
404 size_t aSampleSize,
405 java::sdk::MediaCodec::CryptoInfo::LocalRef aCryptoInfo) {
406 if (!aCryptoInfo) {
407 return CryptoSample{};
409 // Extract Crypto information
410 CryptoSample crypto;
411 char const* msg = "";
412 do {
413 HLS_DEBUG("HLSTrackDemuxer", "Sample has Crypto Info");
415 int32_t mode = 0;
416 if (NS_FAILED(aCryptoInfo->Mode(&mode))) {
417 msg = "Error when extracting encryption mode.";
418 break;
420 // We currently only handle ctr mode.
421 if (mode != java::sdk::MediaCodec::CRYPTO_MODE_AES_CTR) {
422 msg = "Error: unexpected encryption mode.";
423 break;
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.";
431 break;
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]),
436 ivArr.Length());
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.";
441 break;
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]),
446 keyArr.Length());
448 mozilla::jni::IntArray::LocalRef clearData;
449 if (NS_FAILED(aCryptoInfo->NumBytesOfClearData(&clearData))) {
450 msg = "Error when extracting clear data.";
451 break;
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]),
456 clearArr.Length());
458 mozilla::jni::IntArray::LocalRef encryptedData;
459 if (NS_FAILED(aCryptoInfo->NumBytesOfEncryptedData(&encryptedData))) {
460 msg = "Error when extracting encrypted data.";
461 break;
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.";
470 break;
472 crypto.mPlainSizes[0] -= (aSampleSize - subSamplesNum);
474 return crypto;
475 } while (false);
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())
494 : TimeUnit::Zero();
496 int32_t size = 0;
497 ok &= NS_SUCCEEDED(info->Size(&size));
498 if (!ok) {
499 HLS_DEBUG("HLSTrackDemuxer",
500 "Error occurred during extraction from Sample java object.");
501 return nullptr;
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");
517 return nullptr;
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());
524 return mrd;
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();
546 } else {
547 *aTime = mNextKeyframeTime.value();
549 return NS_OK;
552 RefPtr<HLSTrackDemuxer::SkipAccessPointPromise>
553 HLSTrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold) {
554 return InvokeAsync(mParent->GetTaskQueue(), this, __func__,
555 &HLSTrackDemuxer::DoSkipToNextRandomAccessPoint,
556 aTimeThreshold);
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;
564 uint32_t parsed = 0;
565 bool found = false;
566 MediaResult result = NS_ERROR_DOM_MEDIA_END_OF_STREAM;
567 do {
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;
574 break;
576 parsed++;
577 java::GeckoHLSSample::LocalRef sample(std::move(sampleObjectArray[0]));
578 if (sample->IsEOS()) {
579 result = NS_ERROR_DOM_MEDIA_END_OF_STREAM;
580 break;
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);
588 if (!rawData) {
589 result = NS_ERROR_OUT_OF_MEMORY;
590 break;
592 if (!rawData->HasValidTime()) {
593 result = NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
594 break;
596 found = true;
597 mQueuedSample = rawData;
598 break;
601 } while (true);
603 if (!found) {
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));
623 Unused << rv;
626 HLSTrackDemuxer::~HLSTrackDemuxer() {}
628 } // namespace mozilla