MFMediaEngineStream.cpp (23424B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "MFMediaEngineStream.h" 6 7 #include <vcruntime.h> 8 9 #include "AudioConverter.h" 10 #include "MFMediaEngineUtils.h" 11 #include "MFMediaSource.h" 12 #include "TimeUnits.h" 13 #include "WMF.h" 14 #include "WMFUtils.h" 15 #include "mozilla/ProfilerLabels.h" 16 #include "mozilla/ProfilerMarkerTypes.h" 17 #include "mozilla/ScopeExit.h" 18 19 namespace mozilla { 20 21 // Don't use this log on the task queue, because it would be racy for `mStream`. 22 #define WLOGV(msg, ...) \ 23 MOZ_LOG(gMFMediaEngineLog, LogLevel::Verbose, \ 24 ("MFMediaEngineStreamWrapper for stream %p (%s, id=%lu), " msg, \ 25 mStream.Get(), mStream->GetDescriptionName().get(), \ 26 mStream->DescriptorId(), ##__VA_ARGS__)) 27 28 #define SLOG(msg, ...) \ 29 MOZ_LOG( \ 30 gMFMediaEngineLog, LogLevel::Debug, \ 31 ("MFMediaStream=%p (%s, id=%lu), " msg, this, \ 32 this->GetDescriptionName().get(), this->DescriptorId(), ##__VA_ARGS__)) 33 34 #define SLOGV(msg, ...) \ 35 MOZ_LOG( \ 36 gMFMediaEngineLog, LogLevel::Verbose, \ 37 ("MFMediaStream=%p (%s, id=%lu), " msg, this, \ 38 this->GetDescriptionName().get(), this->DescriptorId(), ##__VA_ARGS__)) 39 40 using Microsoft::WRL::ComPtr; 41 42 RefPtr<MediaDataDecoder::InitPromise> MFMediaEngineStreamWrapper::Init() { 43 MOZ_ASSERT(mStream->DescriptorId(), "Stream hasn't been initialized!"); 44 WLOGV("Init"); 45 return InitPromise::CreateAndResolve(mStream->TrackType(), __func__); 46 } 47 48 RefPtr<MediaDataDecoder::DecodePromise> MFMediaEngineStreamWrapper::Decode( 49 MediaRawData* aSample) { 50 WLOGV("Decode"); 51 if (!mStream || mStream->IsShutdown()) { 52 return DecodePromise::CreateAndReject( 53 MediaResult(NS_ERROR_FAILURE, "MFMediaEngineStreamWrapper is shutdown"), 54 __func__); 55 } 56 RefPtr<MediaRawData> sample = aSample; 57 return InvokeAsync(mTaskQueue, mStream.Get(), __func__, 58 &MFMediaEngineStream::OutputData, std::move(sample)); 59 } 60 61 RefPtr<MediaDataDecoder::DecodePromise> MFMediaEngineStreamWrapper::Drain() { 62 WLOGV("Drain"); 63 if (!mStream || mStream->IsShutdown()) { 64 return DecodePromise::CreateAndReject( 65 MediaResult(NS_ERROR_FAILURE, "MFMediaEngineStreamWrapper is shutdown"), 66 __func__); 67 } 68 return InvokeAsync(mTaskQueue, mStream.Get(), __func__, 69 &MFMediaEngineStream::Drain); 70 } 71 72 RefPtr<MediaDataDecoder::FlushPromise> MFMediaEngineStreamWrapper::Flush() { 73 WLOGV("Flush"); 74 if (!mStream || mStream->IsShutdown()) { 75 return FlushPromise::CreateAndReject( 76 MediaResult(NS_ERROR_FAILURE, "MFMediaEngineStreamWrapper is shutdown"), 77 __func__); 78 } 79 return InvokeAsync(mTaskQueue, mStream.Get(), __func__, 80 &MFMediaEngineStream::Flush); 81 } 82 83 RefPtr<ShutdownPromise> MFMediaEngineStreamWrapper::Shutdown() { 84 // Stream shutdown is controlled by the media source, so we don't need to call 85 // its shutdown. 86 WLOGV("Disconnect wrapper"); 87 if (!mStream) { 88 // This promise must only ever be resolved. See the definition of the 89 // original abstract function. 90 return ShutdownPromise::CreateAndResolve(false, __func__); 91 } 92 mStream = nullptr; 93 mTaskQueue = nullptr; 94 return ShutdownPromise::CreateAndResolve(true, __func__); 95 } 96 97 nsCString MFMediaEngineStreamWrapper::GetDescriptionName() const { 98 return mStream ? mStream->GetDescriptionName() : nsLiteralCString("none"); 99 } 100 101 nsCString MFMediaEngineStreamWrapper::GetCodecName() const { 102 return mStream ? mStream->GetCodecName() : nsLiteralCString("none"); 103 } 104 105 MediaDataDecoder::ConversionRequired 106 MFMediaEngineStreamWrapper::NeedsConversion() const { 107 return mStream ? mStream->NeedsConversion() 108 : MediaDataDecoder::ConversionRequired::kNeedNone; 109 } 110 111 bool MFMediaEngineStreamWrapper::ShouldDecoderAlwaysBeRecycled() const { 112 return true; 113 } 114 115 bool MFMediaEngineStreamWrapper::IsHardwareAccelerated( 116 nsACString& aFailureReason) const { 117 if (!mStream) { 118 return false; 119 } 120 // Video is always hardware accelerated. 121 return mStream->AsVideoStream() != nullptr; 122 } 123 124 MFMediaEngineStream::MFMediaEngineStream() 125 : mIsShutdown(false), 126 mIsSelected(false), 127 mRawDataQueueForFeedingEngine(true /* aEnablePreciseDuration */), 128 mRawDataQueueForGeneratingOutput(true /* aEnablePreciseDuration */), 129 mReceivedEOS(false) { 130 MOZ_COUNT_CTOR(MFMediaEngineStream); 131 } 132 133 MFMediaEngineStream::~MFMediaEngineStream() { 134 MOZ_ASSERT(IsShutdown()); 135 MOZ_COUNT_DTOR(MFMediaEngineStream); 136 } 137 138 HRESULT MFMediaEngineStream::RuntimeClassInitialize( 139 uint64_t aStreamId, const TrackInfo& aInfo, bool aIsEncryptedCustomInit, 140 MFMediaSource* aParentSource) { 141 mParentSource = aParentSource; 142 mTaskQueue = aParentSource->GetTaskQueue(); 143 MOZ_ASSERT(mTaskQueue); 144 mStreamId = aStreamId; 145 mIsEncryptedCustomInit = aIsEncryptedCustomInit; 146 147 auto errorExit = MakeScopeExit([&] { 148 SLOG("Failed to initialize media stream (id=%" PRIu64 ")", aStreamId); 149 mIsShutdown = true; 150 (void)mMediaEventQueue->Shutdown(); 151 }); 152 153 RETURN_IF_FAILED(wmf::MFCreateEventQueue(&mMediaEventQueue)); 154 155 ComPtr<IMFMediaType> mediaType; 156 // The inherited stream would return different type based on their media info. 157 RETURN_IF_FAILED(CreateMediaType(aInfo, mediaType.GetAddressOf())); 158 RETURN_IF_FAILED(GenerateStreamDescriptor(mediaType)); 159 SLOG("Initialized %s (id=%" PRIu64 ", descriptorId=%lu)", 160 GetDescriptionName().get(), aStreamId, mStreamDescriptorId); 161 errorExit.release(); 162 return S_OK; 163 } 164 165 HRESULT MFMediaEngineStream::GenerateStreamDescriptor( 166 ComPtr<IMFMediaType>& aMediaType) { 167 RETURN_IF_FAILED(wmf::MFCreateStreamDescriptor( 168 mStreamId, 1 /* stream amount */, aMediaType.GetAddressOf(), 169 &mStreamDescriptor)); 170 RETURN_IF_FAILED( 171 mStreamDescriptor->GetStreamIdentifier(&mStreamDescriptorId)); 172 if (IsEncrypted()) { 173 RETURN_IF_FAILED(mStreamDescriptor->SetUINT32(MF_SD_PROTECTED, 1)); 174 } 175 return S_OK; 176 } 177 178 HRESULT MFMediaEngineStream::Start(const PROPVARIANT* aPosition) { 179 AssertOnMFThreadPool(); 180 if (!IsSelected()) { 181 SLOG("No need to start non-selected stream"); 182 return S_OK; 183 } 184 if (IsShutdown()) { 185 return MF_E_SHUTDOWN; 186 } 187 SLOG("Start"); 188 const bool isFromCurrentPosition = aPosition->vt == VT_EMPTY; 189 RETURN_IF_FAILED(QueueEvent(MEStreamStarted, GUID_NULL, S_OK, aPosition)); 190 MOZ_ASSERT(mTaskQueue); 191 (void)mTaskQueue->Dispatch(NS_NewRunnableFunction( 192 "MFMediaEngineStream::Start", 193 [self = RefPtr{this}, isFromCurrentPosition, this]() { 194 if (!isFromCurrentPosition && IsEnded()) { 195 SLOG("Stream restarts again from a new position, reset EOS"); 196 mReceivedEOS = false; 197 } 198 // Process pending requests (if any) which happened when the stream 199 // wasn't allowed to serve samples. Eg. stream is paused. Or resend the 200 // ended event if the stream is ended already. 201 ReplySampleRequestIfPossible(); 202 })); 203 return S_OK; 204 } 205 206 HRESULT MFMediaEngineStream::Seek(const PROPVARIANT* aPosition) { 207 AssertOnMFThreadPool(); 208 if (!IsSelected()) { 209 SLOG("No need to seek non-selected stream"); 210 return S_OK; 211 } 212 SLOG("Seek"); 213 RETURN_IF_FAILED(QueueEvent(MEStreamSeeked, GUID_NULL, S_OK, aPosition)); 214 return S_OK; 215 } 216 217 HRESULT MFMediaEngineStream::Stop() { 218 AssertOnMFThreadPool(); 219 if (!IsSelected()) { 220 SLOG("No need to stop non-selected stream"); 221 return S_OK; 222 } 223 SLOG("Stop"); 224 RETURN_IF_FAILED(QueueEvent(MEStreamStopped, GUID_NULL, S_OK, nullptr)); 225 return S_OK; 226 } 227 228 HRESULT MFMediaEngineStream::Pause() { 229 AssertOnMFThreadPool(); 230 if (!IsSelected()) { 231 SLOG("No need to pause non-selected stream"); 232 return S_OK; 233 } 234 SLOG("Pause"); 235 RETURN_IF_FAILED(QueueEvent(MEStreamPaused, GUID_NULL, S_OK, nullptr)); 236 return S_OK; 237 } 238 239 void MFMediaEngineStream::Shutdown() { 240 AssertOnMFThreadPool(); 241 if (IsShutdown()) { 242 return; 243 } 244 SLOG("Shutdown"); 245 mIsShutdown = true; 246 // After this method is called, all IMFMediaEventQueue methods return 247 // MF_E_SHUTDOWN. 248 RETURN_VOID_IF_FAILED(mMediaEventQueue->Shutdown()); 249 ComPtr<MFMediaEngineStream> self = this; 250 MOZ_ASSERT(mTaskQueue); 251 (void)mTaskQueue->Dispatch( 252 NS_NewRunnableFunction("MFMediaEngineStream::Shutdown", [self]() { 253 self->mParentSource = nullptr; 254 self->mRawDataQueueForFeedingEngine.Reset(); 255 self->mRawDataQueueForGeneratingOutput.Reset(); 256 self->ShutdownCleanUpOnTaskQueue(); 257 self->mTaskQueue = nullptr; 258 })); 259 } 260 261 IFACEMETHODIMP 262 MFMediaEngineStream::GetMediaSource(IMFMediaSource** aMediaSource) { 263 AssertOnMFThreadPool(); 264 if (IsShutdown()) { 265 return MF_E_SHUTDOWN; 266 } 267 RETURN_IF_FAILED(mParentSource.CopyTo(aMediaSource)); 268 return S_OK; 269 } 270 271 IFACEMETHODIMP MFMediaEngineStream::GetStreamDescriptor( 272 IMFStreamDescriptor** aStreamDescriptor) { 273 AssertOnMFThreadPool(); 274 if (IsShutdown()) { 275 return MF_E_SHUTDOWN; 276 } 277 if (!mStreamDescriptor) { 278 SLOG("Hasn't initialized stream descriptor"); 279 return MF_E_NOT_INITIALIZED; 280 } 281 RETURN_IF_FAILED(mStreamDescriptor.CopyTo(aStreamDescriptor)); 282 return S_OK; 283 } 284 285 IFACEMETHODIMP MFMediaEngineStream::RequestSample(IUnknown* aToken) { 286 AssertOnMFThreadPool(); 287 if (IsShutdown()) { 288 return MF_E_SHUTDOWN; 289 } 290 291 ComPtr<IUnknown> token = aToken; 292 ComPtr<MFMediaEngineStream> self = this; 293 MOZ_ASSERT(mTaskQueue); 294 (void)mTaskQueue->Dispatch(NS_NewRunnableFunction( 295 "MFMediaEngineStream::RequestSample", [token, self, this]() { 296 AssertOnTaskQueue(); 297 mSampleRequestTokens.push(token); 298 SLOGV("RequestSample, token amount=%zu", mSampleRequestTokens.size()); 299 ReplySampleRequestIfPossible(); 300 if (!HasEnoughRawData() && mParentSource && !IsEnded()) { 301 SendRequestSampleEvent(false /* isEnough */); 302 } 303 })); 304 return S_OK; 305 } 306 307 void MFMediaEngineStream::ReplySampleRequestIfPossible() { 308 AssertOnTaskQueue(); 309 if (IsEnded()) { 310 // We have no more sample to return, clean all pending requests. 311 while (!mSampleRequestTokens.empty()) { 312 mSampleRequestTokens.pop(); 313 } 314 MOZ_ASSERT(mSampleRequestTokens.empty()); 315 NotifyEndEvent(); 316 return; 317 } 318 319 if (mSampleRequestTokens.empty() || 320 mRawDataQueueForFeedingEngine.GetSize() == 0) { 321 return; 322 } 323 324 if (!ShouldServeSamples()) { 325 SLOGV("Not deliver samples if the stream is not started"); 326 return; 327 } 328 329 // Push data into the mf media event queue if the media engine is already 330 // waiting for data. 331 ComPtr<IMFSample> inputSample; 332 RETURN_VOID_IF_FAILED(CreateInputSample(inputSample.GetAddressOf())); 333 ComPtr<IUnknown> token = mSampleRequestTokens.front(); 334 RETURN_VOID_IF_FAILED( 335 inputSample->SetUnknown(MFSampleExtension_Token, token.Get())); 336 mSampleRequestTokens.pop(); 337 RETURN_VOID_IF_FAILED(mMediaEventQueue->QueueEventParamUnk( 338 MEMediaSample, GUID_NULL, S_OK, inputSample.Get())); 339 } 340 341 void MFMediaEngineStream::NotifyEndEvent() { 342 AssertOnTaskQueue(); 343 SLOG("Notify end event"); 344 MOZ_ASSERT(mRawDataQueueForFeedingEngine.GetSize() == 0); 345 RETURN_VOID_IF_FAILED(mMediaEventQueue->QueueEventParamUnk( 346 MEEndOfStream, GUID_NULL, S_OK, nullptr)); 347 mEndedEvent.Notify(TrackType()); 348 PROFILER_MARKER_TEXT("MFMediaEngineStream:NotifyEnd", MEDIA_PLAYBACK, {}, 349 nsPrintfCString("stream=%s, id=%" PRIu64, 350 GetDescriptionName().get(), mStreamId)); 351 } 352 353 bool MFMediaEngineStream::ShouldServeSamples() const { 354 AssertOnTaskQueue(); 355 return mParentSource && 356 mParentSource->GetState() == MFMediaSource::State::Started && 357 mIsSelected; 358 } 359 360 HRESULT MFMediaEngineStream::CreateInputSample(IMFSample** aSample) { 361 AssertOnTaskQueue(); 362 363 ComPtr<IMFSample> sample; 364 RETURN_IF_FAILED(wmf::MFCreateSample(&sample)); 365 366 MOZ_ASSERT(mRawDataQueueForFeedingEngine.GetSize() != 0); 367 RefPtr<MediaRawData> data = mRawDataQueueForFeedingEngine.PopFront(); 368 SLOGV("CreateInputSample, pop data [%" PRId64 ", %" PRId64 369 "] (duration=%" PRId64 ", kf=%d, encrypted=%d), queue size=%zu", 370 data->mTime.ToMicroseconds(), data->GetEndTime().ToMicroseconds(), 371 data->mDuration.ToMicroseconds(), data->mKeyframe, 372 data->mCrypto.IsEncrypted(), mRawDataQueueForFeedingEngine.GetSize()); 373 PROFILER_MARKER( 374 nsPrintfCString( 375 "pop %s (stream=%" PRIu64 ")", 376 TrackType() == TrackInfo::TrackType::kVideoTrack ? "video" : "audio", 377 mStreamId), 378 MEDIA_PLAYBACK, {}, MediaSampleMarker, data->mTime.ToMicroseconds(), 379 data->GetEndTime().ToMicroseconds(), 380 mRawDataQueueForFeedingEngine.GetSize()); 381 382 // Copy data into IMFMediaBuffer 383 ComPtr<IMFMediaBuffer> buffer; 384 BYTE* dst = nullptr; 385 DWORD maxLength = 0; 386 RETURN_IF_FAILED( 387 wmf::MFCreateMemoryBuffer(data->Size(), buffer.GetAddressOf())); 388 RETURN_IF_FAILED(buffer->Lock(&dst, &maxLength, 0)); 389 memcpy(dst, data->Data(), data->Size()); 390 RETURN_IF_FAILED(buffer->Unlock()); 391 RETURN_IF_FAILED(buffer->SetCurrentLength(data->Size())); 392 393 // Setup sample attributes 394 RETURN_IF_FAILED(sample->AddBuffer(buffer.Get())); 395 RETURN_IF_FAILED( 396 sample->SetSampleTime(UsecsToHNs(data->mTime.ToMicroseconds()))); 397 RETURN_IF_FAILED( 398 sample->SetSampleDuration(UsecsToHNs(data->mDuration.ToMicroseconds()))); 399 if (data->mKeyframe) { 400 RETURN_IF_FAILED(sample->SetUINT32(MFSampleExtension_CleanPoint, 1)); 401 } 402 403 // Setup encrypt attributes 404 if (data->mCrypto.IsEncrypted()) { 405 RETURN_IF_FAILED(AddEncryptAttributes(sample.Get(), data->mCrypto)); 406 } 407 408 *aSample = sample.Detach(); 409 return S_OK; 410 } 411 412 HRESULT MFMediaEngineStream::AddEncryptAttributes( 413 IMFSample* aSample, const CryptoSample& aCryptoConfig) { 414 // Scheme 415 MFSampleEncryptionProtectionScheme protectionScheme; 416 if (aCryptoConfig.mCryptoScheme == CryptoScheme::Cenc) { 417 SLOG("Set CENC encryption"); 418 protectionScheme = MFSampleEncryptionProtectionScheme:: 419 MF_SAMPLE_ENCRYPTION_PROTECTION_SCHEME_AES_CTR; 420 } else if (aCryptoConfig.mCryptoScheme == CryptoScheme::Cbcs || 421 aCryptoConfig.mCryptoScheme == CryptoScheme::Cbcs_1_9) { 422 protectionScheme = MFSampleEncryptionProtectionScheme:: 423 MF_SAMPLE_ENCRYPTION_PROTECTION_SCHEME_AES_CBC; 424 SLOG("Set CBC pattern encryption, crypt=%u, skip=%u", 425 aCryptoConfig.mCryptByteBlock, aCryptoConfig.mSkipByteBlock); 426 // Only need to set them when they are non-zero. See 427 // https://learn.microsoft.com/en-us/windows/win32/medfound/mfsampleextension-encryption-cryptbyteblock 428 // https://learn.microsoft.com/en-us/windows/win32/medfound/mfsampleextension-encryption-skipbyteblock 429 if (aCryptoConfig.mCryptByteBlock > 0 && aCryptoConfig.mSkipByteBlock > 0) { 430 RETURN_IF_FAILED( 431 aSample->SetUINT32(MFSampleExtension_Encryption_CryptByteBlock, 432 aCryptoConfig.mCryptByteBlock)); 433 RETURN_IF_FAILED( 434 aSample->SetUINT32(MFSampleExtension_Encryption_SkipByteBlock, 435 aCryptoConfig.mSkipByteBlock)); 436 } 437 } else { 438 SLOG("Unexpected encryption scheme"); 439 return MF_E_UNEXPECTED; 440 } 441 RETURN_IF_FAILED(aSample->SetUINT32( 442 MFSampleExtension_Encryption_ProtectionScheme, protectionScheme)); 443 444 // KID 445 if (aCryptoConfig.mKeyId.Length() != sizeof(GUID)) { 446 SLOG("Unsupported key ID size (%zu)", aCryptoConfig.mKeyId.Length()); 447 return MF_E_UNEXPECTED; 448 } 449 GUID keyId; 450 GUIDFromByteArray(aCryptoConfig.mKeyId, keyId); 451 RETURN_IF_FAILED(aSample->SetGUID(MFSampleExtension_Content_KeyID, keyId)); 452 // TODO : if we want to suspend/resume the media engine, then we can consider 453 // to store last key id and set it in CDM to refresh the decryptor. 454 455 // IV 456 if (aCryptoConfig.mIVSize != 0) { 457 // Per-sample IV, usually seen in CENC. 458 SLOG("Use sample IV for decryption, IV size=%u", aCryptoConfig.mIVSize); 459 RETURN_IF_FAILED(aSample->SetBlob( 460 MFSampleExtension_Encryption_SampleID, 461 reinterpret_cast<const uint8_t*>(aCryptoConfig.mIV.Elements()), 462 aCryptoConfig.mIVSize)); 463 } else { 464 // A constant IV for all samples, usually seen in CBCS. 465 SLOG("Use constant IV for decryption, constantIV length=%zu", 466 aCryptoConfig.mConstantIV.Length()); 467 RETURN_IF_FAILED(aSample->SetBlob( 468 MFSampleExtension_Encryption_SampleID, 469 reinterpret_cast<const uint8_t*>(aCryptoConfig.mConstantIV.Elements()), 470 aCryptoConfig.mConstantIV.Length())); 471 } 472 473 // Subsample entries. 474 MOZ_ASSERT(aCryptoConfig.mEncryptedSizes.Length() == 475 aCryptoConfig.mPlainSizes.Length()); 476 size_t numSubsamples = aCryptoConfig.mEncryptedSizes.Length(); 477 if (numSubsamples != 0) { 478 std::vector<MediaFoundationSubsampleEntry> subsampleEntries; 479 for (size_t idx = 0; idx < numSubsamples; idx++) { 480 subsampleEntries.push_back(MediaFoundationSubsampleEntry{ 481 aCryptoConfig.mPlainSizes[idx], aCryptoConfig.mEncryptedSizes[idx]}); 482 } 483 const uint32_t entriesSize = 484 sizeof(MediaFoundationSubsampleEntry) * numSubsamples; 485 RETURN_IF_FAILED(aSample->SetBlob( 486 MFSampleExtension_Encryption_SubSample_Mapping, 487 reinterpret_cast<const uint8_t*>(subsampleEntries.data()), 488 entriesSize)); 489 } 490 491 return S_OK; 492 } 493 494 IFACEMETHODIMP MFMediaEngineStream::GetEvent(DWORD aFlags, 495 IMFMediaEvent** aEvent) { 496 AssertOnMFThreadPool(); 497 MOZ_ASSERT(mMediaEventQueue); 498 RETURN_IF_FAILED(mMediaEventQueue->GetEvent(aFlags, aEvent)); 499 return S_OK; 500 } 501 502 IFACEMETHODIMP MFMediaEngineStream::BeginGetEvent(IMFAsyncCallback* aCallback, 503 IUnknown* aState) { 504 AssertOnMFThreadPool(); 505 MOZ_ASSERT(mMediaEventQueue); 506 RETURN_IF_FAILED(mMediaEventQueue->BeginGetEvent(aCallback, aState)); 507 return S_OK; 508 } 509 510 IFACEMETHODIMP MFMediaEngineStream::EndGetEvent(IMFAsyncResult* aResult, 511 IMFMediaEvent** aEvent) { 512 AssertOnMFThreadPool(); 513 MOZ_ASSERT(mMediaEventQueue); 514 RETURN_IF_FAILED(mMediaEventQueue->EndGetEvent(aResult, aEvent)); 515 return S_OK; 516 } 517 518 IFACEMETHODIMP MFMediaEngineStream::QueueEvent(MediaEventType aType, 519 REFGUID aExtendedType, 520 HRESULT aStatus, 521 const PROPVARIANT* aValue) { 522 AssertOnMFThreadPool(); 523 MOZ_ASSERT(mMediaEventQueue); 524 RETURN_IF_FAILED(mMediaEventQueue->QueueEventParamVar(aType, aExtendedType, 525 aStatus, aValue)); 526 SLOG("Queued event %s", MediaEventTypeToStr(aType)); 527 return S_OK; 528 } 529 530 void MFMediaEngineStream::SetSelected(bool aSelected) { 531 AssertOnMFThreadPool(); 532 SLOG("Select=%d", aSelected); 533 mIsSelected = aSelected; 534 } 535 536 void MFMediaEngineStream::NotifyNewData(MediaRawData* aSample) { 537 AssertOnTaskQueue(); 538 if (IsShutdown()) { 539 return; 540 } 541 const bool wasEnough = HasEnoughRawData(); 542 mRawDataQueueForFeedingEngine.Push(aSample); 543 mRawDataQueueForGeneratingOutput.Push(aSample); 544 SLOGV("NotifyNewData, push data [%" PRId64 ", %" PRId64 545 "], queue size=%zu, queue duration=%" PRId64, 546 aSample->mTime.ToMicroseconds(), aSample->GetEndTime().ToMicroseconds(), 547 mRawDataQueueForFeedingEngine.GetSize(), 548 mRawDataQueueForFeedingEngine.PreciseDuration()); 549 if (mReceivedEOS) { 550 SLOG("Receive a new data, cancel old EOS flag"); 551 mReceivedEOS = false; 552 } 553 ReplySampleRequestIfPossible(); 554 if (!wasEnough && HasEnoughRawData()) { 555 SendRequestSampleEvent(true /* isEnough */); 556 } 557 } 558 559 void MFMediaEngineStream::SendRequestSampleEvent(bool aIsEnough) { 560 AssertOnTaskQueue(); 561 SLOGV("data is %s, queue duration=%" PRId64, 562 aIsEnough ? "enough" : "not enough", 563 mRawDataQueueForFeedingEngine.PreciseDuration()); 564 mParentSource->mRequestSampleEvent.Notify( 565 SampleRequest{TrackType(), aIsEnough}); 566 } 567 568 void MFMediaEngineStream::NotifyEndOfStreamInternal() { 569 AssertOnTaskQueue(); 570 if (mReceivedEOS) { 571 return; 572 } 573 SLOG("EOS"); 574 mReceivedEOS = true; 575 ReplySampleRequestIfPossible(); 576 } 577 578 bool MFMediaEngineStream::IsEnded() const { 579 AssertOnTaskQueue(); 580 return mReceivedEOS && mRawDataQueueForFeedingEngine.GetSize() == 0; 581 } 582 583 RefPtr<MediaDataDecoder::FlushPromise> MFMediaEngineStream::Flush() { 584 if (IsShutdown()) { 585 return MediaDataDecoder::FlushPromise::CreateAndReject( 586 MediaResult(NS_ERROR_FAILURE, 587 RESULT_DETAIL("MFMediaEngineStream is shutdown")), 588 __func__); 589 } 590 AssertOnTaskQueue(); 591 SLOG("Flush"); 592 mRawDataQueueForFeedingEngine.Reset(); 593 mRawDataQueueForGeneratingOutput.Reset(); 594 mReceivedEOS = false; 595 return MediaDataDecoder::FlushPromise::CreateAndResolve(true, __func__); 596 } 597 598 RefPtr<MediaDataDecoder::DecodePromise> MFMediaEngineStream::OutputData( 599 RefPtr<MediaRawData> aSample) { 600 if (IsShutdown()) { 601 return MediaDataDecoder::DecodePromise::CreateAndReject( 602 MediaResult(NS_ERROR_FAILURE, 603 RESULT_DETAIL("MFMediaEngineStream is shutdown")), 604 __func__); 605 } 606 AssertOnTaskQueue(); 607 NotifyNewData(aSample); 608 MediaDataDecoder::DecodedData outputs; 609 if (RefPtr<MediaData> outputData = OutputDataInternal()) { 610 outputs.AppendElement(outputData); 611 SLOGV("Output data [%" PRId64 ",%" PRId64 "]", 612 outputData->mTime.ToMicroseconds(), 613 outputData->GetEndTime().ToMicroseconds()); 614 } 615 return MediaDataDecoder::DecodePromise::CreateAndResolve(std::move(outputs), 616 __func__); 617 }; 618 619 RefPtr<MediaDataDecoder::DecodePromise> MFMediaEngineStream::Drain() { 620 if (IsShutdown()) { 621 return MediaDataDecoder::DecodePromise::CreateAndReject( 622 MediaResult(NS_ERROR_FAILURE, 623 RESULT_DETAIL("MFMediaEngineStream is shutdown")), 624 __func__); 625 } 626 AssertOnTaskQueue(); 627 MediaDataDecoder::DecodedData outputs; 628 while (RefPtr<MediaData> outputData = OutputDataInternal()) { 629 outputs.AppendElement(outputData); 630 SLOGV("Output data [%" PRId64 ",%" PRId64 "]", 631 outputData->mTime.ToMicroseconds(), 632 outputData->GetEndTime().ToMicroseconds()); 633 } 634 return MediaDataDecoder::DecodePromise::CreateAndResolve(std::move(outputs), 635 __func__); 636 } 637 638 void MFMediaEngineStream::AssertOnTaskQueue() const { 639 MOZ_ASSERT(mTaskQueue && mTaskQueue->IsCurrentThreadIn()); 640 } 641 642 void MFMediaEngineStream::AssertOnMFThreadPool() const { 643 // We can't really assert the thread id from thread pool, because it would 644 // change any time. So we just assert this is not the task queue, and use the 645 // explicit function name to indicate what thread we should run on. 646 // TODO : this assertion is not precise, because the running thread could be 647 // the stream wrapper thread as well, 648 MOZ_ASSERT(!mTaskQueue || !mTaskQueue->IsCurrentThreadIn()); 649 } 650 651 #undef WLOGV 652 #undef SLOG 653 #undef SLOGV 654 655 } // namespace mozilla