tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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