VideoBridgeParent.cpp (9891B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 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 "VideoBridgeParent.h" 8 #include "CompositorThread.h" 9 #include "mozilla/DataMutex.h" 10 #include "mozilla/ipc/Endpoint.h" 11 #include "mozilla/layers/PTextureParent.h" 12 #include "mozilla/layers/TextureHost.h" 13 #include "mozilla/layers/VideoBridgeUtils.h" 14 #include "mozilla/webrender/RenderThread.h" 15 16 namespace mozilla::layers { 17 18 using namespace mozilla::ipc; 19 using namespace mozilla::gfx; 20 21 using VideoBridgeTable = EnumeratedArray<VideoBridgeSource, VideoBridgeParent*, 22 size_t(VideoBridgeSource::_Count)>; 23 24 NS_IMPL_NONLOGGING_ADDREF_INHERITED(VideoBridgeParent, HostIPCAllocator) 25 NS_IMPL_NONLOGGING_RELEASE_INHERITED(VideoBridgeParent, HostIPCAllocator) 26 27 MOZ_RUNINIT static StaticDataMutex<VideoBridgeTable> sVideoBridgeFromProcess( 28 "VideoBridges"); 29 static Atomic<bool> sVideoBridgeParentShutDown(false); 30 31 VideoBridgeParent::VideoBridgeParent(VideoBridgeSource aSource) 32 : mMonitor("VideoBridgeParent::mMonitor"), 33 mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()), 34 mClosed(false) { 35 auto videoBridgeFromProcess = sVideoBridgeFromProcess.Lock(); 36 switch (aSource) { 37 case VideoBridgeSource::RddProcess: 38 case VideoBridgeSource::GpuProcess: 39 case VideoBridgeSource::MFMediaEngineCDMProcess: 40 (*videoBridgeFromProcess)[aSource] = this; 41 break; 42 default: 43 MOZ_CRASH("Unhandled case"); 44 } 45 } 46 47 VideoBridgeParent::~VideoBridgeParent() { 48 auto videoBridgeFromProcess = sVideoBridgeFromProcess.Lock(); 49 for (auto& bridgeParent : *videoBridgeFromProcess) { 50 if (bridgeParent == this) { 51 bridgeParent = nullptr; 52 } 53 } 54 } 55 56 /* static */ 57 void VideoBridgeParent::Open(Endpoint<PVideoBridgeParent>&& aEndpoint, 58 VideoBridgeSource aSource) { 59 RefPtr<VideoBridgeParent> parent = new VideoBridgeParent(aSource); 60 61 CompositorThread()->Dispatch( 62 NewRunnableMethod<Endpoint<PVideoBridgeParent>&&>( 63 "gfx::layers::VideoBridgeParent::Bind", parent, 64 &VideoBridgeParent::Bind, std::move(aEndpoint))); 65 } 66 67 void VideoBridgeParent::Bind(Endpoint<PVideoBridgeParent>&& aEndpoint) { 68 if (!aEndpoint.Bind(this)) { 69 // We can't recover from this. 70 MOZ_CRASH("Failed to bind VideoBridgeParent to endpoint"); 71 } 72 } 73 74 /* static */ 75 RefPtr<VideoBridgeParent> VideoBridgeParent::GetSingleton( 76 const Maybe<VideoBridgeSource>& aSource) { 77 MOZ_ASSERT(aSource.isSome()); 78 auto videoBridgeFromProcess = sVideoBridgeFromProcess.Lock(); 79 switch (aSource.value()) { 80 case VideoBridgeSource::RddProcess: 81 case VideoBridgeSource::GpuProcess: 82 case VideoBridgeSource::MFMediaEngineCDMProcess: 83 MOZ_ASSERT((*videoBridgeFromProcess)[aSource.value()]); 84 return RefPtr{(*videoBridgeFromProcess)[aSource.value()]}; 85 default: 86 MOZ_CRASH("Unhandled case"); 87 } 88 } 89 90 already_AddRefed<TextureHost> VideoBridgeParent::LookupTextureAsync( 91 const dom::ContentParentId& aContentId, uint64_t aSerial) { 92 MonitorAutoLock lock(mMonitor); 93 94 // We raced shutting down the actor. This can happen when another thread is 95 // keeping the VideoBridgeParent object alive, by waiting for a texture lookup 96 // to complete. While the lookup should fail quickly, it may not release the 97 // actor in time to have removed it from the singleton array. 98 if (NS_WARN_IF(!mCompositorThreadHolder)) { 99 return nullptr; 100 } 101 102 MOZ_ASSERT(mCompositorThreadHolder->IsInThread()); 103 104 auto* actor = mTextureMap[aSerial]; 105 if (NS_WARN_IF(!actor)) { 106 return nullptr; 107 } 108 109 if (NS_WARN_IF(aContentId != TextureHost::GetTextureContentId(actor))) { 110 return nullptr; 111 } 112 113 return do_AddRef(TextureHost::AsTextureHost(actor)); 114 } 115 116 already_AddRefed<TextureHost> VideoBridgeParent::LookupTexture( 117 const dom::ContentParentId& aContentId, uint64_t aSerial) { 118 MonitorAutoLock lock(mMonitor); 119 120 // We raced shutting down the actor. 121 if (NS_WARN_IF(!mCompositorThreadHolder)) { 122 return nullptr; 123 } 124 125 auto* actor = mTextureMap[aSerial]; 126 if (actor) { 127 if (NS_WARN_IF(aContentId != TextureHost::GetTextureContentId(actor))) { 128 return nullptr; 129 } 130 return do_AddRef(TextureHost::AsTextureHost(actor)); 131 } 132 133 // We cannot block on the Compositor thread because that is the thread we get 134 // the IPC calls for the update on. 135 if (NS_WARN_IF(mCompositorThreadHolder->IsInThread())) { 136 MOZ_ASSERT_UNREACHABLE("Should never call on Compositor thread!"); 137 return nullptr; 138 } 139 140 // Canvas may have raced ahead of VideoBridgeParent setting up the 141 // PTextureParent IPDL object. This should happen only rarely/briefly. Since 142 // we know that the PTexture constructor must be in the send queue, we can 143 // block until the IPDL ping comes back. 144 bool complete = false; 145 146 auto resolve = [&](void_t&&) { 147 MonitorAutoLock lock(mMonitor); 148 complete = true; 149 lock.NotifyAll(); 150 }; 151 152 auto reject = [&](ipc::ResponseRejectReason) { 153 MonitorAutoLock lock(mMonitor); 154 complete = true; 155 lock.NotifyAll(); 156 }; 157 158 mCompositorThreadHolder->Dispatch( 159 NS_NewRunnableFunction("VideoBridgeParent::LookupTexture", [&]() { 160 if (CanSend()) { 161 SendPing(std::move(resolve), std::move(reject)); 162 } else { 163 reject(ipc::ResponseRejectReason::ChannelClosed); 164 } 165 })); 166 167 while (!complete) { 168 lock.Wait(); 169 } 170 171 actor = mTextureMap[aSerial]; 172 if (!actor) { 173 return nullptr; 174 } 175 176 if (NS_WARN_IF(aContentId != TextureHost::GetTextureContentId(actor))) { 177 return nullptr; 178 } 179 180 return do_AddRef(TextureHost::AsTextureHost(actor)); 181 } 182 183 void VideoBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { 184 bool shutdown = sVideoBridgeParentShutDown; 185 186 if (!shutdown && aWhy == AbnormalShutdown) { 187 gfxCriticalNote 188 << "VideoBridgeParent receives IPC close with reason=AbnormalShutdown"; 189 } 190 191 { 192 MonitorAutoLock lock(mMonitor); 193 // Can't alloc/dealloc shmems from now on. 194 mClosed = true; 195 mCompositorThreadHolder = nullptr; 196 } 197 } 198 199 /* static */ 200 void VideoBridgeParent::Shutdown() { 201 CompositorThread()->Dispatch(NS_NewRunnableFunction( 202 "VideoBridgeParent::Shutdown", 203 []() -> void { VideoBridgeParent::ShutdownInternal(); })); 204 } 205 206 /* static */ 207 void VideoBridgeParent::ShutdownInternal() { 208 sVideoBridgeParentShutDown = true; 209 210 nsTArray<RefPtr<VideoBridgeParent>> bridges; 211 212 // We don't want to hold the sVideoBridgeFromProcess lock when the 213 // VideoBridgeParent objects are closed without holding a reference to them. 214 { 215 auto videoBridgeFromProcess = sVideoBridgeFromProcess.Lock(); 216 for (auto& bridgeParent : *videoBridgeFromProcess) { 217 if (bridgeParent) { 218 bridges.AppendElement(bridgeParent); 219 } 220 } 221 } 222 223 for (auto& bridge : bridges) { 224 bridge->Close(); 225 } 226 } 227 228 /* static */ 229 void VideoBridgeParent::UnregisterExternalImages() { 230 MOZ_ASSERT(sVideoBridgeParentShutDown); 231 232 auto videoBridgeFromProcess = sVideoBridgeFromProcess.Lock(); 233 for (auto& bridgeParent : *videoBridgeFromProcess) { 234 if (bridgeParent) { 235 bridgeParent->DoUnregisterExternalImages(); 236 } 237 } 238 } 239 240 void VideoBridgeParent::DoUnregisterExternalImages() { 241 const ManagedContainer<PTextureParent>& textures = ManagedPTextureParent(); 242 for (const auto& key : textures) { 243 RefPtr<TextureHost> texture = TextureHost::AsTextureHost(key); 244 245 if (texture) { 246 texture->MaybeDestroyRenderTexture(); 247 } 248 } 249 } 250 251 PTextureParent* VideoBridgeParent::AllocPTextureParent( 252 const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock, 253 const LayersBackend& aLayersBackend, const TextureFlags& aFlags, 254 const dom::ContentParentId& aContentId, const uint64_t& aSerial) { 255 PTextureParent* parent = TextureHost::CreateIPDLActor( 256 this, aSharedData, std::move(aReadLock), aLayersBackend, aFlags, 257 aContentId, aSerial, Nothing()); 258 259 if (!parent) { 260 return nullptr; 261 } 262 263 MonitorAutoLock lock(mMonitor); 264 mTextureMap[aSerial] = parent; 265 return parent; 266 } 267 268 bool VideoBridgeParent::DeallocPTextureParent(PTextureParent* actor) { 269 MonitorAutoLock lock(mMonitor); 270 mTextureMap.erase(TextureHost::GetTextureSerial(actor)); 271 return TextureHost::DestroyIPDLActor(actor); 272 } 273 274 void VideoBridgeParent::SendAsyncMessage( 275 const nsTArray<AsyncParentMessageData>& aMessage) { 276 MOZ_ASSERT(false, "AsyncMessages not supported"); 277 } 278 279 bool VideoBridgeParent::AllocShmem(size_t aSize, ipc::Shmem* aShmem) { 280 { 281 MonitorAutoLock lock(mMonitor); 282 if (mClosed) { 283 return false; 284 } 285 } 286 return PVideoBridgeParent::AllocShmem(aSize, aShmem); 287 } 288 289 bool VideoBridgeParent::AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) { 290 { 291 MonitorAutoLock lock(mMonitor); 292 if (mClosed) { 293 return false; 294 } 295 } 296 return PVideoBridgeParent::AllocUnsafeShmem(aSize, aShmem); 297 } 298 299 bool VideoBridgeParent::DeallocShmem(ipc::Shmem& aShmem) { 300 { 301 MonitorAutoLock lock(mMonitor); 302 if (mCompositorThreadHolder && !mCompositorThreadHolder->IsInThread()) { 303 mCompositorThreadHolder->Dispatch(NS_NewRunnableFunction( 304 "gfx::layers::VideoBridgeParent::DeallocShmem", 305 [self = RefPtr{this}, shmem = std::move(aShmem)]() mutable { 306 self->DeallocShmem(shmem); 307 })); 308 return true; 309 } 310 311 if (mClosed) { 312 return false; 313 } 314 } 315 316 return PVideoBridgeParent::DeallocShmem(aShmem); 317 } 318 319 bool VideoBridgeParent::IsSameProcess() const { 320 return OtherPid() == base::GetCurrentProcId(); 321 } 322 323 void VideoBridgeParent::NotifyNotUsed(PTextureParent* aTexture, 324 uint64_t aTransactionId) {} 325 326 } // namespace mozilla::layers