MFMediaEngineParent.cpp (27136B)
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 "MFMediaEngineParent.h" 6 7 #include <audiosessiontypes.h> 8 #include <intsafe.h> 9 #include <mfapi.h> 10 11 #ifdef MOZ_WMF_CDM 12 # include "MFCDMParent.h" 13 # include "MFContentProtectionManager.h" 14 #endif 15 16 #include "MFMediaEngineExtension.h" 17 #include "MFMediaEngineStream.h" 18 #include "MFMediaEngineUtils.h" 19 #include "MFMediaEngineVideoStream.h" 20 #include "MFMediaSource.h" 21 #include "RemoteMediaManagerParent.h" 22 #include "WMF.h" 23 #include "mozilla/ClearOnShutdown.h" 24 #include "mozilla/RemoteDecodeUtils.h" 25 #include "mozilla/ScopeExit.h" 26 #include "mozilla/StaticMutex.h" 27 #include "mozilla/StaticPrefs_media.h" 28 #include "mozilla/StaticPtr.h" 29 #include "mozilla/gfx/DeviceManagerDx.h" 30 #include "mozilla/ipc/UtilityMediaServiceParent.h" 31 #include "mozilla/ipc/UtilityProcessChild.h" 32 33 namespace mozilla { 34 35 #define LOG(msg, ...) \ 36 MOZ_LOG(gMFMediaEngineLog, LogLevel::Debug, \ 37 ("MFMediaEngineParent=%p, Id=%" PRId64 ", " msg, this, this->Id(), \ 38 ##__VA_ARGS__)) 39 40 using MediaEngineMap = nsTHashMap<nsUint64HashKey, MFMediaEngineParent*>; 41 static StaticAutoPtr<MediaEngineMap> sMediaEngines; 42 43 using Microsoft::WRL::ComPtr; 44 using Microsoft::WRL::MakeAndInitialize; 45 46 StaticMutex sMediaEnginesLock; 47 48 static void RegisterMediaEngine(MFMediaEngineParent* aMediaEngine) { 49 MOZ_ASSERT(aMediaEngine); 50 StaticMutexAutoLock lock(sMediaEnginesLock); 51 if (!sMediaEngines) { 52 sMediaEngines = new MediaEngineMap(); 53 } 54 sMediaEngines->InsertOrUpdate(aMediaEngine->Id(), aMediaEngine); 55 } 56 57 static void UnregisterMedieEngine(MFMediaEngineParent* aMediaEngine) { 58 StaticMutexAutoLock lock(sMediaEnginesLock); 59 if (sMediaEngines) { 60 sMediaEngines->Remove(aMediaEngine->Id()); 61 } 62 } 63 64 /* static */ 65 MFMediaEngineParent* MFMediaEngineParent::GetMediaEngineById(uint64_t aId) { 66 StaticMutexAutoLock lock(sMediaEnginesLock); 67 return sMediaEngines->Get(aId); 68 } 69 70 MFMediaEngineParent::MFMediaEngineParent(RemoteMediaManagerParent* aManager, 71 nsISerialEventTarget* aManagerThread) 72 : mMediaEngineId(++sMediaEngineIdx), 73 mManager(aManager), 74 mManagerThread(aManagerThread) { 75 MOZ_ASSERT(aManager); 76 MOZ_ASSERT(aManagerThread); 77 MOZ_ASSERT(mMediaEngineId != 0); 78 MOZ_ASSERT(XRE_IsUtilityProcess()); 79 MOZ_ASSERT(GetCurrentSandboxingKind() == 80 ipc::SandboxingKind::MF_MEDIA_ENGINE_CDM); 81 LOG("Created MFMediaEngineParent"); 82 RegisterMediaEngine(this); 83 mIPDLSelfRef = this; 84 CreateMediaEngine(); 85 } 86 87 MFMediaEngineParent::~MFMediaEngineParent() { 88 LOG("Destoryed MFMediaEngineParent"); 89 DestroyEngineIfExists(); 90 UnregisterMedieEngine(this); 91 } 92 93 void MFMediaEngineParent::DestroyEngineIfExists( 94 const Maybe<MediaResult>& aError) { 95 LOG("DestroyEngineIfExists, hasError=%d", aError.isSome()); 96 ENGINE_MARKER("MFMediaEngineParent::DestroyEngineIfExists"); 97 mMediaEngineNotify = nullptr; 98 mMediaEngineExtension = nullptr; 99 if (mMediaSource) { 100 mMediaSource->ShutdownTaskQueue(); 101 mMediaSource = nullptr; 102 } 103 #ifdef MOZ_WMF_CDM 104 if (mContentProtectionManager) { 105 mContentProtectionManager->Shutdown(); 106 mContentProtectionManager = nullptr; 107 } 108 #endif 109 if (mMediaEngine) { 110 LOG_IF_FAILED(mMediaEngine->Shutdown()); 111 mMediaEngine = nullptr; 112 } 113 mMediaEngineEventListener.DisconnectIfExists(); 114 mRequestSampleListener.DisconnectIfExists(); 115 if (mDXGIDeviceManager) { 116 mDXGIDeviceManager = nullptr; 117 } 118 if (aError) { 119 (void)SendNotifyError(*aError); 120 } 121 } 122 123 void MFMediaEngineParent::CreateMediaEngine() { 124 LOG("CreateMediaEngine"); 125 auto errorExit = MakeScopeExit([&] { 126 MediaResult error(NS_ERROR_DOM_MEDIA_FATAL_ERR, "Failed to create engine"); 127 DestroyEngineIfExists(Some(error)); 128 }); 129 130 if (!wmf::MediaFoundationInitializer::HasInitialized()) { 131 NS_WARNING("Failed to initialize media foundation"); 132 return; 133 } 134 135 InitializeDXGIDeviceManager(); 136 137 // Create an attribute and set mandatory information that are required for 138 // a media engine creation. 139 ComPtr<IMFAttributes> creationAttributes; 140 RETURN_VOID_IF_FAILED(wmf::MFCreateAttributes(&creationAttributes, 6)); 141 RETURN_VOID_IF_FAILED( 142 MakeAndInitialize<MFMediaEngineNotify>(&mMediaEngineNotify)); 143 mMediaEngineEventListener = mMediaEngineNotify->MediaEngineEvent().Connect( 144 mManagerThread, this, &MFMediaEngineParent::HandleMediaEngineEvent); 145 RETURN_VOID_IF_FAILED(creationAttributes->SetUnknown( 146 MF_MEDIA_ENGINE_CALLBACK, mMediaEngineNotify.Get())); 147 RETURN_VOID_IF_FAILED(creationAttributes->SetUINT32( 148 MF_MEDIA_ENGINE_AUDIO_CATEGORY, AudioCategory_Media)); 149 RETURN_VOID_IF_FAILED( 150 MakeAndInitialize<MFMediaEngineExtension>(&mMediaEngineExtension)); 151 RETURN_VOID_IF_FAILED(creationAttributes->SetUnknown( 152 MF_MEDIA_ENGINE_EXTENSION, mMediaEngineExtension.Get())); 153 RETURN_VOID_IF_FAILED( 154 creationAttributes->SetUINT32(MF_MEDIA_ENGINE_CONTENT_PROTECTION_FLAGS, 155 MF_MEDIA_ENGINE_ENABLE_PROTECTED_CONTENT)); 156 if (mDXGIDeviceManager) { 157 RETURN_VOID_IF_FAILED(creationAttributes->SetUnknown( 158 MF_MEDIA_ENGINE_DXGI_MANAGER, mDXGIDeviceManager.Get())); 159 } 160 161 ComPtr<IMFMediaEngineClassFactory> factory; 162 RETURN_VOID_IF_FAILED(CoCreateInstance(CLSID_MFMediaEngineClassFactory, 163 nullptr, CLSCTX_INPROC_SERVER, 164 IID_PPV_ARGS(&factory))); 165 const bool isLowLatency = StaticPrefs::media_wmf_low_latency_enabled(); 166 static const DWORD MF_MEDIA_ENGINE_DEFAULT = 0; 167 RETURN_VOID_IF_FAILED(factory->CreateInstance( 168 isLowLatency ? MF_MEDIA_ENGINE_REAL_TIME_MODE : MF_MEDIA_ENGINE_DEFAULT, 169 creationAttributes.Get(), &mMediaEngine)); 170 171 LOG("Created media engine successfully"); 172 mIsCreatedMediaEngine = true; 173 ENGINE_MARKER("MFMediaEngineParent::CreatedMediaEngine"); 174 errorExit.release(); 175 } 176 177 void MFMediaEngineParent::InitializeDXGIDeviceManager() { 178 auto* deviceManager = gfx::DeviceManagerDx::Get(); 179 if (!deviceManager) { 180 return; 181 } 182 RefPtr<ID3D11Device> d3d11Device = deviceManager->CreateMediaEngineDevice(); 183 if (!d3d11Device) { 184 return; 185 } 186 187 auto errorExit = MakeScopeExit([&] { 188 mDXGIDeviceManager = nullptr; 189 wmf::MFUnlockDXGIDeviceManager(); 190 }); 191 UINT deviceResetToken; 192 RETURN_VOID_IF_FAILED( 193 wmf::MFLockDXGIDeviceManager(&deviceResetToken, &mDXGIDeviceManager)); 194 if (!mDXGIDeviceManager) { 195 return; 196 } 197 RETURN_VOID_IF_FAILED( 198 mDXGIDeviceManager->ResetDevice(d3d11Device.get(), deviceResetToken)); 199 LOG("Initialized DXGI manager"); 200 errorExit.release(); 201 } 202 203 #ifndef ENSURE_EVENT_DISPATCH_DURING_PLAYING 204 # define ENSURE_EVENT_DISPATCH_DURING_PLAYING(event) \ 205 do { \ 206 if (mMediaEngine->IsPaused()) { \ 207 LOG("Ignore incorrect '%s' during pausing!", event); \ 208 return; \ 209 } \ 210 } while (false) 211 #endif 212 213 void MFMediaEngineParent::HandleMediaEngineEvent( 214 MFMediaEngineEventWrapper aEvent) { 215 AssertOnManagerThread(); 216 LOG("Received media engine event %s", MediaEngineEventToStr(aEvent.mEvent)); 217 ENGINE_MARKER_TEXT( 218 "MFMediaEngineParent::HandleMediaEngineEvent", 219 nsPrintfCString("%s", MediaEngineEventToStr(aEvent.mEvent))); 220 switch (aEvent.mEvent) { 221 case MF_MEDIA_ENGINE_EVENT_ERROR: { 222 MOZ_ASSERT(aEvent.mParam1 && aEvent.mParam2); 223 auto error = static_cast<MF_MEDIA_ENGINE_ERR>(*aEvent.mParam1); 224 auto result = static_cast<HRESULT>(*aEvent.mParam2); 225 NotifyError(error, result); 226 break; 227 } 228 case MF_MEDIA_ENGINE_EVENT_FORMATCHANGE: { 229 if (mMediaEngine->HasVideo()) { 230 NotifyVideoResizing(); 231 } 232 break; 233 } 234 case MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY: { 235 if (mMediaEngine->HasVideo()) { 236 EnsureDcompSurfaceHandle(); 237 } 238 [[fallthrough]]; 239 } 240 case MF_MEDIA_ENGINE_EVENT_LOADEDDATA: 241 case MF_MEDIA_ENGINE_EVENT_WAITING: 242 case MF_MEDIA_ENGINE_EVENT_SEEKED: 243 case MF_MEDIA_ENGINE_EVENT_BUFFERINGSTARTED: 244 case MF_MEDIA_ENGINE_EVENT_BUFFERINGENDED: 245 (void)SendNotifyEvent(aEvent.mEvent); 246 break; 247 case MF_MEDIA_ENGINE_EVENT_PLAYING: 248 ENSURE_EVENT_DISPATCH_DURING_PLAYING( 249 MediaEngineEventToStr(aEvent.mEvent)); 250 (void)SendNotifyEvent(aEvent.mEvent); 251 break; 252 case MF_MEDIA_ENGINE_EVENT_ENDED: { 253 ENSURE_EVENT_DISPATCH_DURING_PLAYING( 254 MediaEngineEventToStr(aEvent.mEvent)); 255 (void)SendNotifyEvent(aEvent.mEvent); 256 UpdateStatisticsData(); 257 break; 258 } 259 case MF_MEDIA_ENGINE_EVENT_TIMEUPDATE: { 260 auto currentTimeInSeconds = mMediaEngine->GetCurrentTime(); 261 (void)SendUpdateCurrentTime(currentTimeInSeconds); 262 UpdateStatisticsData(); 263 break; 264 } 265 default: 266 LOG("Unhandled event=%s", MediaEngineEventToStr(aEvent.mEvent)); 267 break; 268 } 269 } 270 271 void MFMediaEngineParent::NotifyError(MF_MEDIA_ENGINE_ERR aError, 272 HRESULT aResult) { 273 if (aError == MF_MEDIA_ENGINE_ERR_NOERROR) { 274 return; 275 } 276 #ifdef MOZ_WMF_CDM 277 // A special error requires to reset the hareware context, not a real error. 278 if (aResult == DRM_E_TEE_INVALID_HWDRM_STATE) { 279 LOG("Notify error 'DRM_E_TEE_INVALID_HWDRM_STATE', hr=%lx", aResult); 280 ENGINE_MARKER( 281 "MFMediaEngineParent,Received 'DRM_E_TEE_INVALID_HWDRM_STATE'"); 282 auto* proxy = mContentProtectionManager 283 ? mContentProtectionManager->GetCDMProxy() 284 : nullptr; 285 if (proxy) { 286 proxy->OnHardwareContextReset(); 287 } 288 return; 289 } 290 #endif 291 LOG("Notify error '%s', hr=%lx", MFMediaEngineErrorToStr(aError), aResult); 292 ENGINE_MARKER_TEXT( 293 "MFMediaEngineParent::NotifyError", 294 nsPrintfCString("%s, hr=%lx", MFMediaEngineErrorToStr(aError), aResult)); 295 switch (aError) { 296 case MF_MEDIA_ENGINE_ERR_ABORTED: 297 case MF_MEDIA_ENGINE_ERR_NETWORK: 298 // We ignore these two because we fetch data by ourselves. 299 return; 300 case MF_MEDIA_ENGINE_ERR_DECODE: { 301 MediaResult error(NS_ERROR_DOM_MEDIA_DECODE_ERR, 302 nsPrintfCString("Decoder error (hr=%lx)", aResult), 303 Some(static_cast<int32_t>(aResult))); 304 #ifdef MOZ_WMF_CDM 305 if (aResult == MSPR_E_NO_DECRYPTOR_AVAILABLE) { 306 NotifyDisableHWDRM(); 307 } 308 #endif 309 (void)SendNotifyError(error); 310 return; 311 } 312 case MF_MEDIA_ENGINE_ERR_SRC_NOT_SUPPORTED: { 313 MediaResult error( 314 NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR, 315 nsPrintfCString("Source not supported (hr=%lx)", aResult), 316 Some(static_cast<int32_t>(aResult))); 317 (void)SendNotifyError(error); 318 return; 319 } 320 case MF_MEDIA_ENGINE_ERR_ENCRYPTED: { 321 MediaResult error(NS_ERROR_DOM_MEDIA_FATAL_ERR, 322 nsPrintfCString("Encrypted error (hr=%lx)", aResult), 323 Some(static_cast<int32_t>(aResult))); 324 (void)SendNotifyError(error); 325 return; 326 } 327 default: 328 MOZ_ASSERT_UNREACHABLE("Unsupported error"); 329 return; 330 } 331 } 332 333 MFMediaEngineStreamWrapper* MFMediaEngineParent::GetMediaEngineStream( 334 TrackType aType, const CreateDecoderParams& aParam) { 335 // Has been shutdowned. 336 if (!mMediaSource) { 337 return nullptr; 338 } 339 LOG("Create a media engine decoder for %s", TrackTypeToStr(aType)); 340 if (aType == TrackType::kAudioTrack) { 341 auto* stream = mMediaSource->GetAudioStream(); 342 return new MFMediaEngineStreamWrapper(stream, stream->GetTaskQueue(), 343 aParam); 344 } 345 MOZ_ASSERT(aType == TrackType::kVideoTrack); 346 auto* stream = mMediaSource->GetVideoStream(); 347 stream->AsVideoStream()->SetKnowsCompositor(aParam.mKnowsCompositor); 348 stream->AsVideoStream()->SetConfig(aParam.mConfig); 349 return new MFMediaEngineStreamWrapper(stream, stream->GetTaskQueue(), aParam); 350 } 351 352 mozilla::ipc::IPCResult MFMediaEngineParent::RecvInitMediaEngine( 353 const MediaEngineInfoIPDL& aInfo, InitMediaEngineResolver&& aResolver) { 354 AssertOnManagerThread(); 355 if (!mIsCreatedMediaEngine) { 356 aResolver(0); 357 return IPC_OK(); 358 } 359 // Metadata preload is controlled by content process side before creating 360 // media engine. 361 if (aInfo.preload()) { 362 // TODO : really need this? 363 (void)mMediaEngine->SetPreload(MF_MEDIA_ENGINE_PRELOAD_AUTOMATIC); 364 } 365 RETURN_PARAM_IF_FAILED( 366 SetMediaInfo(aInfo.mediaInfo(), aInfo.encryptedCustomIdent()), IPC_OK()); 367 aResolver(mMediaEngineId); 368 return IPC_OK(); 369 } 370 371 HRESULT MFMediaEngineParent::SetMediaInfo(const MediaInfoIPDL& aInfo, 372 bool aIsEncryptedCustomInit) { 373 AssertOnManagerThread(); 374 MOZ_ASSERT(mIsCreatedMediaEngine, "Hasn't created media engine?"); 375 MOZ_ASSERT(!mMediaSource); 376 377 LOG("SetMediaInfo"); 378 379 auto errorExit = MakeScopeExit([&] { 380 MediaResult error(NS_ERROR_DOM_MEDIA_FATAL_ERR, 381 "Failed to create media source"); 382 DestroyEngineIfExists(Some(error)); 383 }); 384 385 // Create media source and set it to the media engine. 386 NS_ENSURE_TRUE(SUCCEEDED(MakeAndInitialize<MFMediaSource>( 387 &mMediaSource, aInfo.audioInfo(), aInfo.videoInfo(), 388 mManagerThread, aIsEncryptedCustomInit)), 389 IPC_OK()); 390 391 const bool isEncrypted = mMediaSource->IsEncrypted(); 392 ENGINE_MARKER("MFMediaEngineParent,CreatedMediaSource"); 393 nsPrintfCString message( 394 "Created the media source, audio=%s, video=%s, encrypted-audio=%s, " 395 "encrypted-video=%s, aIsEncryptedCustomInit=%d, isEncrypted=%d", 396 aInfo.audioInfo() ? aInfo.audioInfo()->mMimeType.BeginReading() : "none", 397 aInfo.videoInfo() ? aInfo.videoInfo()->mMimeType.BeginReading() : "none", 398 aInfo.audioInfo() && aInfo.audioInfo()->mCrypto.IsEncrypted() ? "yes" 399 : "no", 400 aInfo.videoInfo() && aInfo.videoInfo()->mCrypto.IsEncrypted() ? "yes" 401 : "no", 402 aIsEncryptedCustomInit, isEncrypted); 403 LOG("%s", message.get()); 404 405 if (aInfo.videoInfo()) { 406 ComPtr<IMFMediaEngineEx> mediaEngineEx; 407 RETURN_IF_FAILED(mMediaEngine.As(&mediaEngineEx)); 408 RETURN_IF_FAILED(mediaEngineEx->EnableWindowlessSwapchainMode(true)); 409 LOG("Enabled dcomp swap chain mode"); 410 ENGINE_MARKER("MFMediaEngineParent,EnabledSwapChain"); 411 if (isEncrypted) { 412 // Microsoft recommends to disable low latency with DRM. 413 RETURN_IF_FAILED(mediaEngineEx->SetRealTimeMode(false)); 414 LOG("Turned off the real time mode for encrypted playback"); 415 } 416 } 417 418 mRequestSampleListener = mMediaSource->RequestSampleEvent().Connect( 419 mManagerThread, this, &MFMediaEngineParent::HandleRequestSample); 420 errorExit.release(); 421 422 #ifdef MOZ_WMF_CDM 423 if (isEncrypted && !mContentProtectionManager) { 424 // We will set the source later when the CDM proxy is ready. 425 return S_OK; 426 } 427 428 if (isEncrypted && mContentProtectionManager) { 429 auto* proxy = mContentProtectionManager->GetCDMProxy(); 430 MOZ_ASSERT(proxy); 431 mMediaSource->SetCDMProxy(proxy); 432 } 433 #endif 434 435 SetMediaSourceOnEngine(); 436 return S_OK; 437 } 438 439 void MFMediaEngineParent::SetMediaSourceOnEngine() { 440 AssertOnManagerThread(); 441 MOZ_ASSERT(mMediaSource); 442 443 auto errorExit = MakeScopeExit([&] { 444 MediaResult error(NS_ERROR_DOM_MEDIA_FATAL_ERR, 445 "Failed to set media source"); 446 DestroyEngineIfExists(Some(error)); 447 }); 448 449 mMediaEngineExtension->SetMediaSource(mMediaSource.Get()); 450 451 // We use the source scheme in order to let the media engine to load our 452 // custom source. 453 RETURN_VOID_IF_FAILED( 454 mMediaEngine->SetSource(SysAllocString(L"MFRendererSrc"))); 455 456 LOG("Finished setup our custom media source to the media engine"); 457 ENGINE_MARKER("MFMediaEngineParent,FinishedSetupMediaSource"); 458 errorExit.release(); 459 } 460 461 mozilla::ipc::IPCResult MFMediaEngineParent::RecvPlay() { 462 AssertOnManagerThread(); 463 if (!mMediaEngine) { 464 LOG("Engine has been shutdowned!"); 465 return IPC_OK(); 466 } 467 LOG("Play, expected playback rate %f, default playback rate=%f", 468 mPlaybackRate, mMediaEngine->GetDefaultPlaybackRate()); 469 ENGINE_MARKER("MFMediaEngineParent,Play"); 470 NS_ENSURE_TRUE(SUCCEEDED(mMediaEngine->Play()), IPC_OK()); 471 return IPC_OK(); 472 } 473 474 mozilla::ipc::IPCResult MFMediaEngineParent::RecvPause() { 475 AssertOnManagerThread(); 476 if (!mMediaEngine) { 477 LOG("Engine has been shutdowned!"); 478 return IPC_OK(); 479 } 480 LOG("Pause"); 481 ENGINE_MARKER("MFMediaEngineParent,Pause"); 482 NS_ENSURE_TRUE(SUCCEEDED(mMediaEngine->Pause()), IPC_OK()); 483 return IPC_OK(); 484 } 485 486 mozilla::ipc::IPCResult MFMediaEngineParent::RecvSeek( 487 double aTargetTimeInSecond) { 488 AssertOnManagerThread(); 489 if (!mMediaEngine) { 490 return IPC_OK(); 491 } 492 493 // If the target time is already equal to the current time, the media engine 494 // doesn't perform seek internally so we won't be able to receive `seeked` 495 // event. Therefore, we simply return `seeked` here because we're already in 496 // the target time. 497 const auto currentTimeInSeconds = mMediaEngine->GetCurrentTime(); 498 if (currentTimeInSeconds == aTargetTimeInSecond) { 499 (void)SendNotifyEvent(MF_MEDIA_ENGINE_EVENT_SEEKED); 500 return IPC_OK(); 501 } 502 503 LOG("Seek to %f", aTargetTimeInSecond); 504 ENGINE_MARKER_TEXT("MFMediaEngineParent,Seek", 505 nsPrintfCString("%f", aTargetTimeInSecond)); 506 NS_ENSURE_TRUE(SUCCEEDED(mMediaEngine->SetCurrentTime(aTargetTimeInSecond)), 507 IPC_OK()); 508 509 return IPC_OK(); 510 } 511 512 mozilla::ipc::IPCResult MFMediaEngineParent::RecvSetCDMProxyId( 513 uint64_t aProxyId) { 514 if (!mMediaEngine) { 515 return IPC_OK(); 516 } 517 #ifdef MOZ_WMF_CDM 518 LOG("SetCDMProxy, Id=%" PRIu64, aProxyId); 519 MFCDMParent* cdmParent = MFCDMParent::GetCDMById(aProxyId); 520 MOZ_DIAGNOSTIC_ASSERT(cdmParent); 521 RETURN_PARAM_IF_FAILED( 522 MakeAndInitialize<MFContentProtectionManager>(&mContentProtectionManager), 523 IPC_OK()); 524 525 ComPtr<IMFMediaEngineProtectedContent> protectedMediaEngine; 526 RETURN_PARAM_IF_FAILED(mMediaEngine.As(&protectedMediaEngine), IPC_OK()); 527 RETURN_PARAM_IF_FAILED(protectedMediaEngine->SetContentProtectionManager( 528 mContentProtectionManager.Get()), 529 IPC_OK()); 530 531 RefPtr<MFCDMProxy> proxy = cdmParent->GetMFCDMProxy(); 532 if (!proxy) { 533 LOG("Failed to get MFCDMProxy!"); 534 return IPC_OK(); 535 } 536 537 RETURN_PARAM_IF_FAILED(mContentProtectionManager->SetCDMProxy(proxy), 538 IPC_OK()); 539 // TODO : is it possible to set CDM proxy before creating media source? If so, 540 // handle that as well. 541 if (mMediaSource) { 542 mMediaSource->SetCDMProxy(proxy); 543 SetMediaSourceOnEngine(); 544 } 545 LOG("Set CDM Proxy successfully on the media engine!"); 546 #endif 547 return IPC_OK(); 548 } 549 550 mozilla::ipc::IPCResult MFMediaEngineParent::RecvSetVolume(double aVolume) { 551 AssertOnManagerThread(); 552 if (mMediaEngine) { 553 LOG("SetVolume=%f", aVolume); 554 ENGINE_MARKER_TEXT("MFMediaEngineParent,SetVolume", 555 nsPrintfCString("%f", aVolume)); 556 NS_ENSURE_TRUE(SUCCEEDED(mMediaEngine->SetVolume(aVolume)), IPC_OK()); 557 } 558 return IPC_OK(); 559 } 560 561 mozilla::ipc::IPCResult MFMediaEngineParent::RecvSetPlaybackRate( 562 double aPlaybackRate) { 563 AssertOnManagerThread(); 564 if (aPlaybackRate <= 0) { 565 LOG("Not support zero or negative playback rate"); 566 return IPC_OK(); 567 } 568 LOG("SetPlaybackRate=%f", aPlaybackRate); 569 ENGINE_MARKER_TEXT("MFMediaEngineParent,SetPlaybackRate", 570 nsPrintfCString("%f", aPlaybackRate)); 571 mPlaybackRate = aPlaybackRate; 572 NS_ENSURE_TRUE(SUCCEEDED(mMediaEngine->SetPlaybackRate(mPlaybackRate)), 573 IPC_OK()); 574 // The media Engine uses the default playback rate to determine the playback 575 // rate when calling `play()`. So if we don't change default playback rate 576 // together, the playback rate would fallback to 1 after pausing or 577 // seeking, which would be different from our expected playback rate. 578 NS_ENSURE_TRUE(SUCCEEDED(mMediaEngine->SetDefaultPlaybackRate(mPlaybackRate)), 579 IPC_OK()); 580 return IPC_OK(); 581 } 582 583 mozilla::ipc::IPCResult MFMediaEngineParent::RecvSetLooping(bool aLooping) { 584 AssertOnManagerThread(); 585 // We handle looping by seeking back to the head by ourselves, so we don't 586 // rely on the media engine for looping. 587 return IPC_OK(); 588 } 589 590 mozilla::ipc::IPCResult MFMediaEngineParent::RecvNotifyEndOfStream( 591 TrackInfo::TrackType aType) { 592 AssertOnManagerThread(); 593 MOZ_ASSERT(mMediaSource); 594 LOG("NotifyEndOfStream, type=%s", TrackTypeToStr(aType)); 595 mMediaSource->NotifyEndOfStream(aType); 596 return IPC_OK(); 597 } 598 599 mozilla::ipc::IPCResult MFMediaEngineParent::RecvShutdown() { 600 AssertOnManagerThread(); 601 LOG("Shutdown"); 602 ENGINE_MARKER("MFMediaEngineParent,Shutdown"); 603 DestroyEngineIfExists(); 604 return IPC_OK(); 605 } 606 607 void MFMediaEngineParent::Destroy() { 608 AssertOnManagerThread(); 609 mIPDLSelfRef = nullptr; 610 } 611 612 void MFMediaEngineParent::HandleRequestSample(const SampleRequest& aRequest) { 613 AssertOnManagerThread(); 614 MOZ_ASSERT(aRequest.mType == TrackInfo::TrackType::kAudioTrack || 615 aRequest.mType == TrackInfo::TrackType::kVideoTrack); 616 (void)SendRequestSample(aRequest.mType, aRequest.mIsEnough); 617 } 618 619 void MFMediaEngineParent::AssertOnManagerThread() const { 620 MOZ_ASSERT(mManagerThread->IsOnCurrentThread()); 621 } 622 623 Maybe<gfx::IntSize> MFMediaEngineParent::DetectVideoSizeChange() { 624 AssertOnManagerThread(); 625 MOZ_ASSERT(mMediaEngine); 626 MOZ_ASSERT(mMediaEngine->HasVideo()); 627 628 DWORD width, height; 629 RETURN_PARAM_IF_FAILED(mMediaEngine->GetNativeVideoSize(&width, &height), 630 Nothing()); 631 if (width != mDisplayWidth || height != mDisplayHeight) { 632 ENGINE_MARKER_TEXT("MFMediaEngineParent,VideoSizeChange", 633 nsPrintfCString("%lux%lu", width, height)); 634 LOG("Updated video size [%lux%lu] -> [%lux%lu] ", mDisplayWidth, 635 mDisplayHeight, width, height); 636 mDisplayWidth = width; 637 mDisplayHeight = height; 638 return Some(gfx::IntSize{width, height}); 639 } 640 return Nothing(); 641 } 642 643 void MFMediaEngineParent::EnsureDcompSurfaceHandle() { 644 AssertOnManagerThread(); 645 MOZ_ASSERT(mMediaEngine); 646 MOZ_ASSERT(mMediaEngine->HasVideo()); 647 648 ComPtr<IMFMediaEngineEx> mediaEngineEx; 649 RETURN_VOID_IF_FAILED(mMediaEngine.As(&mediaEngineEx)); 650 651 // Ensure that the width and height is already up-to-date. 652 gfx::IntSize size{mDisplayWidth, mDisplayHeight}; 653 if (auto newSize = DetectVideoSizeChange()) { 654 size = *newSize; 655 } 656 657 // Update stream size before asking for a handle. If we don't update the 658 // size, media engine will create the dcomp surface in a wrong size. 659 RECT rect = {0, 0, (LONG)size.width, (LONG)size.height}; 660 RETURN_VOID_IF_FAILED(mediaEngineEx->UpdateVideoStream( 661 nullptr /* pSrc */, &rect, nullptr /* pBorderClr */)); 662 663 HANDLE surfaceHandle = INVALID_HANDLE_VALUE; 664 RETURN_VOID_IF_FAILED(mediaEngineEx->GetVideoSwapchainHandle(&surfaceHandle)); 665 if (surfaceHandle && surfaceHandle != INVALID_HANDLE_VALUE) { 666 LOG("EnsureDcompSurfaceHandle, handle=%p, size=[%dx%d]", surfaceHandle, 667 size.width, size.height); 668 mMediaSource->SetDCompSurfaceHandle(surfaceHandle, size); 669 } else { 670 NS_WARNING("SurfaceHandle is not ready yet"); 671 } 672 } 673 674 void MFMediaEngineParent::NotifyVideoResizing() { 675 AssertOnManagerThread(); 676 if (auto newSize = DetectVideoSizeChange()) { 677 (void)SendNotifyResizing(newSize->width, newSize->height); 678 } 679 } 680 681 void MFMediaEngineParent::UpdateStatisticsData() { 682 AssertOnManagerThread(); 683 684 // Statistic data is only for video. 685 if (!mMediaEngine->HasVideo()) { 686 return; 687 } 688 689 ComPtr<IMFMediaEngineEx> mediaEngineEx; 690 RETURN_VOID_IF_FAILED(mMediaEngine.As(&mediaEngineEx)); 691 692 struct scopePropVariant : public PROPVARIANT { 693 scopePropVariant() { PropVariantInit(this); } 694 ~scopePropVariant() { PropVariantClear(this); } 695 scopePropVariant(scopePropVariant const&) = delete; 696 scopePropVariant& operator=(scopePropVariant const&) = delete; 697 }; 698 699 // https://docs.microsoft.com/en-us/windows/win32/api/mfmediaengine/ne-mfmediaengine-mf_media_engine_statistic 700 scopePropVariant renderedFramesProp, droppedFramesProp; 701 RETURN_VOID_IF_FAILED(mediaEngineEx->GetStatistics( 702 MF_MEDIA_ENGINE_STATISTIC_FRAMES_RENDERED, &renderedFramesProp)); 703 RETURN_VOID_IF_FAILED(mediaEngineEx->GetStatistics( 704 MF_MEDIA_ENGINE_STATISTIC_FRAMES_DROPPED, &droppedFramesProp)); 705 706 const unsigned long renderedFrames = renderedFramesProp.ulVal; 707 const unsigned long droppedFrames = droppedFramesProp.ulVal; 708 709 // As the amount of rendered frame MUST increase monotonically. If the new 710 // statistic data show the decreasing, which means the media engine has reset 711 // the statistic data and started a new one. (That will happens after calling 712 // flush internally) 713 if (renderedFrames < mCurrentPlaybackStatisticData.renderedFrames()) { 714 mPrevPlaybackStatisticData = 715 StatisticData{mPrevPlaybackStatisticData.renderedFrames() + 716 mCurrentPlaybackStatisticData.renderedFrames(), 717 mPrevPlaybackStatisticData.droppedFrames() + 718 mCurrentPlaybackStatisticData.droppedFrames()}; 719 mCurrentPlaybackStatisticData = StatisticData{}; 720 } 721 722 if (mCurrentPlaybackStatisticData.renderedFrames() != renderedFrames || 723 mCurrentPlaybackStatisticData.droppedFrames() != droppedFrames) { 724 mCurrentPlaybackStatisticData = 725 StatisticData{renderedFrames, droppedFrames}; 726 const uint64_t totalRenderedFrames = 727 mPrevPlaybackStatisticData.renderedFrames() + 728 mCurrentPlaybackStatisticData.renderedFrames(); 729 const uint64_t totalDroppedFrames = 730 mPrevPlaybackStatisticData.droppedFrames() + 731 mCurrentPlaybackStatisticData.droppedFrames(); 732 LOG("Update statistic data, rendered=%" PRIu64 ", dropped=%" PRIu64, 733 totalRenderedFrames, totalDroppedFrames); 734 (void)SendUpdateStatisticData( 735 StatisticData{totalRenderedFrames, totalDroppedFrames}); 736 } 737 } 738 739 #ifdef MOZ_WMF_CDM 740 void MFMediaEngineParent::NotifyDisableHWDRM() { 741 AssertOnManagerThread(); 742 RefPtr<ipc::UtilityProcessChild> upc = 743 ipc::UtilityProcessChild::GetSingleton(); 744 if (!upc) { 745 return; 746 } 747 748 RefPtr<ipc::UtilityMediaServiceParent> umsp = upc->GetMediaService(); 749 if (!umsp) { 750 return; 751 } 752 753 ENGINE_MARKER("MFMediaEngineParent::NotifyDisableHWDRM"); 754 NS_DispatchToMainThread(NS_NewRunnableFunction( 755 "MFMediaEngineParent::NotifyDisableHWDRM", 756 [umsp]() { (void)umsp->SendDisableHardwareDRM(); })); 757 } 758 #endif 759 760 #undef LOG 761 #undef RETURN_IF_FAILED 762 #undef ENSURE_EVENT_DISPATCH_DURING_PLAYING 763 764 } // namespace mozilla