UtilityMediaServiceChild.cpp (8931B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 sts=2 et sw=2 tw=80: */ 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 "UtilityMediaServiceChild.h" 8 9 #include "base/basictypes.h" 10 #include "mozilla/AppShutdown.h" 11 #include "mozilla/dom/ContentParent.h" 12 #include "mozilla/gfx/gfxVars.h" 13 14 #ifdef MOZ_WMF_MEDIA_ENGINE 15 # include "mozilla/StaticPrefs_media.h" 16 # include "mozilla/gfx/GPUProcessManager.h" 17 # include "mozilla/ipc/UtilityProcessManager.h" 18 # include "mozilla/layers/PVideoBridge.h" 19 # include "mozilla/layers/VideoBridgeParent.h" 20 # include "mozilla/layers/VideoBridgeUtils.h" 21 #endif 22 23 #ifdef MOZ_WMF_CDM 24 # include "mozilla/dom/Promise.h" 25 # include "mozilla/EMEUtils.h" 26 # include "mozilla/PMFCDM.h" 27 #endif 28 29 namespace mozilla::ipc { 30 31 NS_IMETHODIMP UtilityMediaServiceChildShutdownObserver::Observe( 32 nsISupports* aSubject, const char* aTopic, const char16_t* aData) { 33 MOZ_ASSERT(strcmp(aTopic, "ipc:utility-shutdown") == 0); 34 35 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService(); 36 if (observerService) { 37 observerService->RemoveObserver(this, "ipc:utility-shutdown"); 38 } 39 40 UtilityMediaServiceChild::Shutdown(mSandbox); 41 return NS_OK; 42 } 43 44 NS_IMPL_ISUPPORTS(UtilityMediaServiceChildShutdownObserver, nsIObserver); 45 46 static EnumeratedArray<SandboxingKind, StaticRefPtr<UtilityMediaServiceChild>, 47 size_t(SandboxingKind::COUNT)> 48 sAudioDecoderChilds; 49 50 UtilityMediaServiceChild::UtilityMediaServiceChild(SandboxingKind aKind) 51 : mSandbox(aKind), mAudioDecoderChildStart(TimeStamp::Now()) { 52 MOZ_ASSERT(NS_IsMainThread()); 53 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService(); 54 if (observerService) { 55 auto* obs = new UtilityMediaServiceChildShutdownObserver(aKind); 56 observerService->AddObserver(obs, "ipc:utility-shutdown", false); 57 } 58 } 59 60 nsresult UtilityMediaServiceChild::BindToUtilityProcess( 61 RefPtr<UtilityProcessParent> aUtilityParent) { 62 Endpoint<PUtilityMediaServiceChild> utilityMediaServiceChildEnd; 63 Endpoint<PUtilityMediaServiceParent> utilityMediaServiceParentEnd; 64 nsresult rv = PUtilityMediaService::CreateEndpoints( 65 aUtilityParent->OtherEndpointProcInfo(), EndpointProcInfo::Current(), 66 &utilityMediaServiceParentEnd, &utilityMediaServiceChildEnd); 67 68 if (NS_FAILED(rv)) { 69 MOZ_ASSERT(false, "Protocol endpoints failure"); 70 return NS_ERROR_FAILURE; 71 } 72 73 nsTArray<gfx::GfxVarUpdate> updates = gfx::gfxVars::FetchNonDefaultVars(); 74 if (!aUtilityParent->SendStartUtilityMediaService( 75 std::move(utilityMediaServiceParentEnd), std::move(updates))) { 76 MOZ_ASSERT(false, "StartUtilityMediaService service failure"); 77 return NS_ERROR_FAILURE; 78 } 79 80 Bind(std::move(utilityMediaServiceChildEnd)); 81 82 PROFILER_MARKER_UNTYPED("UtilityMediaServiceChild::BindToUtilityProcess", IPC, 83 MarkerOptions(MarkerTiming::IntervalUntilNowFrom( 84 mAudioDecoderChildStart))); 85 return NS_OK; 86 } 87 88 void UtilityMediaServiceChild::ActorDestroy(ActorDestroyReason aReason) { 89 MOZ_ASSERT(NS_IsMainThread()); 90 gfx::gfxVars::RemoveReceiver(this); 91 #ifdef MOZ_WMF_MEDIA_ENGINE 92 mHasCreatedVideoBridge = State::None; 93 if (auto* gpm = gfx::GPUProcessManager::Get()) { 94 // Note: the manager could have shutdown already. 95 gpm->RemoveListener(this); 96 } 97 #endif 98 Shutdown(mSandbox); 99 } 100 101 void UtilityMediaServiceChild::Bind( 102 Endpoint<PUtilityMediaServiceChild>&& aEndpoint) { 103 MOZ_ASSERT(NS_IsMainThread()); 104 if (NS_WARN_IF(!aEndpoint.Bind(this))) { 105 MOZ_ASSERT_UNREACHABLE("Failed to bind UtilityMediaServiceChild!"); 106 return; 107 } 108 gfx::gfxVars::AddReceiver(this); 109 } 110 111 /* static */ 112 void UtilityMediaServiceChild::Shutdown(SandboxingKind aKind) { 113 sAudioDecoderChilds[aKind] = nullptr; 114 } 115 116 /* static */ 117 RefPtr<UtilityMediaServiceChild> UtilityMediaServiceChild::GetSingleton( 118 SandboxingKind aKind) { 119 MOZ_ASSERT(NS_IsMainThread()); 120 bool shutdown = AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMWillShutdown); 121 if (!sAudioDecoderChilds[aKind] && !shutdown) { 122 sAudioDecoderChilds[aKind] = new UtilityMediaServiceChild(aKind); 123 } 124 return sAudioDecoderChilds[aKind]; 125 } 126 127 mozilla::ipc::IPCResult 128 UtilityMediaServiceChild::RecvUpdateMediaCodecsSupported( 129 const RemoteMediaIn& aLocation, 130 const media::MediaCodecsSupported& aSupported) { 131 dom::ContentParent::BroadcastMediaCodecsSupportedUpdate(aLocation, 132 aSupported); 133 return IPC_OK(); 134 } 135 136 void UtilityMediaServiceChild::OnVarChanged( 137 const nsTArray<gfx::GfxVarUpdate>& aVar) { 138 SendUpdateVar(aVar); 139 } 140 141 #ifdef MOZ_WMF_MEDIA_ENGINE 142 mozilla::ipc::IPCResult 143 UtilityMediaServiceChild::RecvCompleteCreatedVideoBridge() { 144 MOZ_ASSERT(NS_IsMainThread()); 145 MOZ_ASSERT(mSandbox == SandboxingKind::MF_MEDIA_ENGINE_CDM); 146 if (mHasCreatedVideoBridge == State::Creating) { 147 mHasCreatedVideoBridge = State::Created; 148 } else { 149 MOZ_ASSERT_UNREACHABLE("Video bridge created but was not creating?"); 150 } 151 return IPC_OK(); 152 } 153 154 void UtilityMediaServiceChild::OnCompositorUnexpectedShutdown() { 155 MOZ_ASSERT(NS_IsMainThread()); 156 MOZ_ASSERT(mSandbox == SandboxingKind::MF_MEDIA_ENGINE_CDM); 157 mHasCreatedVideoBridge = State::None; 158 159 if (auto* gpm = gfx::GPUProcessManager::Get()) { 160 if (auto utilpm = UtilityProcessManager::GetSingleton()) 161 if (auto parent = utilpm->GetProcessParent(mSandbox)) { 162 if (NS_SUCCEEDED(gpm->CreateUtilityMFCDMVideoBridge( 163 this, parent->OtherEndpointProcInfo()))) { 164 mHasCreatedVideoBridge = State::Creating; 165 } 166 } 167 } 168 } 169 170 bool UtilityMediaServiceChild::CreateVideoBridge( 171 mozilla::ipc::EndpointProcInfo aOtherProcess) { 172 MOZ_ASSERT(NS_IsMainThread()); 173 MOZ_ASSERT(mSandbox == SandboxingKind::MF_MEDIA_ENGINE_CDM); 174 175 // Creating or already created, avoiding reinit a bridge. 176 if (mHasCreatedVideoBridge != State::None) { 177 return true; 178 } 179 180 auto* gpm = gfx::GPUProcessManager::Get(); 181 if (NS_WARN_IF(!gpm) || NS_WARN_IF(NS_FAILED(gpm->EnsureGPUReady())) || 182 NS_WARN_IF( 183 NS_FAILED(gpm->CreateUtilityMFCDMVideoBridge(this, aOtherProcess)))) { 184 return false; 185 } 186 187 gpm->AddListener(this); 188 mHasCreatedVideoBridge = State::Creating; 189 return true; 190 } 191 #endif 192 193 #ifdef MOZ_WMF_CDM 194 void UtilityMediaServiceChild::GetKeySystemCapabilities( 195 dom::Promise* aPromise) { 196 EME_LOG("Ask capabilities for all supported CDMs"); 197 SendGetKeySystemCapabilities()->Then( 198 NS_GetCurrentThread(), __func__, 199 [promise = RefPtr<dom::Promise>(aPromise)]( 200 CopyableTArray<MFCDMCapabilitiesIPDL>&& result) { 201 FallibleTArray<dom::CDMInformation> cdmInfo; 202 for (const auto& capabilities : result) { 203 EME_LOG("Received capabilities for %s", 204 NS_ConvertUTF16toUTF8(capabilities.keySystem()).get()); 205 for (const auto& v : capabilities.videoCapabilities()) { 206 for (const auto& scheme : v.encryptionSchemes()) { 207 EME_LOG(" capabilities: video=%s, scheme=%s", 208 NS_ConvertUTF16toUTF8(v.contentType()).get(), 209 EnumValueToString(scheme)); 210 } 211 } 212 for (const auto& a : capabilities.audioCapabilities()) { 213 for (const auto& scheme : a.encryptionSchemes()) { 214 EME_LOG(" capabilities: audio=%s, scheme=%s", 215 NS_ConvertUTF16toUTF8(a.contentType()).get(), 216 EnumValueToString(scheme)); 217 } 218 } 219 auto* info = cdmInfo.AppendElement(fallible); 220 if (!info) { 221 promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY); 222 return; 223 } 224 info->mKeySystemName = capabilities.keySystem(); 225 226 KeySystemConfig config; 227 MFCDMCapabilitiesIPDLToKeySystemConfig(capabilities, config); 228 info->mCapabilities = config.GetDebugInfo(); 229 info->mClearlead = 230 DoesKeySystemSupportClearLead(info->mKeySystemName); 231 if (capabilities.isHardwareDecryption()) { 232 info->mIsHardwareDecryption = true; 233 } 234 } 235 promise->MaybeResolve(cdmInfo); 236 }, 237 [promise = RefPtr<dom::Promise>(aPromise)]( 238 const mozilla::ipc::ResponseRejectReason& aReason) { 239 EME_LOG("IPC failure for GetKeySystemCapabilities!"); 240 promise->MaybeReject(NS_ERROR_DOM_MEDIA_CDM_ERR); 241 }); 242 } 243 244 mozilla::ipc::IPCResult UtilityMediaServiceChild::RecvDisableHardwareDRM() { 245 MOZ_ASSERT(NS_IsMainThread()); 246 static constexpr const char* kHardDRMPref = "media.eme.hwdrm.failed"; 247 Preferences::SetBool(kHardDRMPref, true); 248 return IPC_OK(); 249 } 250 #endif 251 252 } // namespace mozilla::ipc