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