GMPContentParent.cpp (7415B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "GMPContentParent.h" 7 8 #include "ChromiumCDMParent.h" 9 #include "GMPLog.h" 10 #include "GMPParent.h" 11 #include "GMPServiceChild.h" 12 #include "GMPVideoDecoderParent.h" 13 #include "GMPVideoEncoderParent.h" 14 #include "base/task.h" 15 #include "mozIGeckoMediaPluginService.h" 16 #include "mozilla/Logging.h" 17 18 namespace mozilla::gmp { 19 20 static const char* GetBoolString(bool aBool) { 21 return aBool ? "true" : "false"; 22 } 23 24 GMPContentParent::GMPContentParent(GMPParent* aParent) 25 : mParent(aParent), mPluginId(0) { 26 GMP_LOG_DEBUG("GMPContentParent::GMPContentParent(this=%p), aParent=%p", this, 27 aParent); 28 if (mParent) { 29 SetDisplayName(mParent->GetDisplayName()); 30 SetPluginId(mParent->GetPluginId()); 31 SetPluginType(mParent->GetPluginType()); 32 } 33 } 34 35 GMPContentParent::~GMPContentParent() { 36 GMP_LOG_DEBUG( 37 "GMPContentParent::~GMPContentParent(this=%p) mVideoDecoders.IsEmpty=%s, " 38 "mVideoEncoders.IsEmpty=%s, mChromiumCDMs.IsEmpty=%s, " 39 "mCloseBlockerCount=%" PRIu32, 40 this, GetBoolString(mVideoDecoders.IsEmpty()), 41 GetBoolString(mVideoEncoders.IsEmpty()), 42 GetBoolString(mChromiumCDMs.IsEmpty()), mCloseBlockerCount); 43 } 44 45 void GMPContentParent::ActorDestroy(ActorDestroyReason aWhy) { 46 GMP_LOG_DEBUG("GMPContentParent::ActorDestroy(this=%p, aWhy=%d)", this, 47 static_cast<int>(aWhy)); 48 MOZ_ASSERT(mVideoDecoders.IsEmpty() && mVideoEncoders.IsEmpty() && 49 mChromiumCDMs.IsEmpty()); 50 } 51 52 void GMPContentParent::ChromiumCDMDestroyed(ChromiumCDMParent* aCDM) { 53 GMP_LOG_DEBUG("GMPContentParent::ChromiumCDMDestroyed(this=%p, aCDM=%p)", 54 this, aCDM); 55 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 56 57 MOZ_ALWAYS_TRUE(mChromiumCDMs.RemoveElement(aCDM)); 58 CloseIfUnused(); 59 } 60 61 void GMPContentParent::VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder) { 62 GMP_LOG_DEBUG("GMPContentParent::VideoDecoderDestroyed(this=%p, aDecoder=%p)", 63 this, aDecoder); 64 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 65 66 // If the constructor fails, we'll get called before it's added 67 (void)NS_WARN_IF(!mVideoDecoders.RemoveElement(aDecoder)); 68 CloseIfUnused(); 69 } 70 71 void GMPContentParent::VideoEncoderDestroyed(GMPVideoEncoderParent* aEncoder) { 72 GMP_LOG_DEBUG("GMPContentParent::VideoEncoderDestroyed(this=%p, aEncoder=%p)", 73 this, aEncoder); 74 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 75 76 // If the constructor fails, we'll get called before it's added 77 (void)NS_WARN_IF(!mVideoEncoders.RemoveElement(aEncoder)); 78 CloseIfUnused(); 79 } 80 81 void GMPContentParent::AddCloseBlocker() { 82 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 83 ++mCloseBlockerCount; 84 GMP_LOG_DEBUG( 85 "GMPContentParent::AddCloseBlocker(this=%p) mCloseBlockerCount=%" PRIu32, 86 this, mCloseBlockerCount); 87 } 88 89 void GMPContentParent::RemoveCloseBlocker() { 90 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 91 --mCloseBlockerCount; 92 GMP_LOG_DEBUG( 93 "GMPContentParent::RemoveCloseBlocker(this=%p) " 94 "mCloseBlockerCount=%" PRIu32, 95 this, mCloseBlockerCount); 96 CloseIfUnused(); 97 } 98 99 void GMPContentParent::CloseIfUnused() { 100 MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread()); 101 GMP_LOG_DEBUG( 102 "GMPContentParent::CloseIfUnused(this=%p) mVideoDecoders.IsEmpty=%s, " 103 "mVideoEncoders.IsEmpty=%s, mChromiumCDMs.IsEmpty=%s, " 104 "mCloseBlockerCount=%" PRIu32, 105 this, GetBoolString(mVideoDecoders.IsEmpty()), 106 GetBoolString(mVideoEncoders.IsEmpty()), 107 GetBoolString(mChromiumCDMs.IsEmpty()), mCloseBlockerCount); 108 if (mVideoDecoders.IsEmpty() && mVideoEncoders.IsEmpty() && 109 mChromiumCDMs.IsEmpty() && mCloseBlockerCount == 0) { 110 RefPtr<GMPContentParent> toClose; 111 if (mParent) { 112 toClose = mParent->ForgetGMPContentParent(); 113 } else { 114 toClose = this; 115 RefPtr<GeckoMediaPluginServiceChild> gmp( 116 GeckoMediaPluginServiceChild::GetSingleton()); 117 if (gmp) { 118 gmp->RemoveGMPContentParent(toClose); 119 } 120 } 121 NS_DispatchToCurrentThread(NewRunnableMethod( 122 "gmp::GMPContentParent::Close", toClose, &GMPContentParent::Close)); 123 } 124 } 125 126 nsCOMPtr<nsISerialEventTarget> GMPContentParent::GMPEventTarget() { 127 if (!mGMPEventTarget) { 128 GMP_LOG_DEBUG("GMPContentParent::GMPEventTarget(this=%p)", this); 129 nsCOMPtr<mozIGeckoMediaPluginService> mps = 130 do_GetService("@mozilla.org/gecko-media-plugin-service;1"); 131 MOZ_ASSERT(mps); 132 if (!mps) { 133 return nullptr; 134 } 135 // Not really safe if we just grab to the mGMPEventTarget, as we don't know 136 // what thread we're running on and other threads may be trying to 137 // access this without locks! However, debug only, and primary failure 138 // mode outside of compiler-helped TSAN is a leak. But better would be 139 // to use swap() under a lock. 140 nsCOMPtr<nsIThread> gmpThread; 141 mps->GetThread(getter_AddRefs(gmpThread)); 142 MOZ_ASSERT(gmpThread); 143 144 mGMPEventTarget = gmpThread; 145 } 146 147 return mGMPEventTarget; 148 } 149 150 already_AddRefed<ChromiumCDMParent> GMPContentParent::GetChromiumCDM( 151 const nsCString& aKeySystem) { 152 GMP_LOG_DEBUG("GMPContentParent::GetChromiumCDM(this=%p aKeySystem=%s)", this, 153 aKeySystem.get()); 154 155 RefPtr<ChromiumCDMParent> parent = new ChromiumCDMParent(this, GetPluginId()); 156 // TODO: Remove parent from mChromiumCDMs in ChromiumCDMParent::Destroy(). 157 mChromiumCDMs.AppendElement(parent); 158 159 if (!SendPChromiumCDMConstructor(parent, aKeySystem)) { 160 MOZ_ASSERT(!mChromiumCDMs.Contains(parent)); 161 return nullptr; 162 } 163 164 return parent.forget(); 165 } 166 167 nsresult GMPContentParent::GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD) { 168 GMP_LOG_DEBUG("GMPContentParent::GetGMPVideoDecoder(this=%p)", this); 169 170 RefPtr<GMPVideoDecoderParent> vdp = new GMPVideoDecoderParent(this); 171 if (!SendPGMPVideoDecoderConstructor(vdp)) { 172 return NS_ERROR_FAILURE; 173 } 174 175 // This addref corresponds to the Proxy pointer the consumer is returned. 176 // It's dropped by calling Close() on the interface. 177 vdp.get()->AddRef(); 178 *aGMPVD = vdp; 179 mVideoDecoders.AppendElement(vdp); 180 181 return NS_OK; 182 } 183 184 nsresult GMPContentParent::GetGMPVideoEncoder(GMPVideoEncoderParent** aGMPVE) { 185 GMP_LOG_DEBUG("GMPContentParent::GetGMPVideoEncoder(this=%p)", this); 186 187 RefPtr<GMPVideoEncoderParent> vep = new GMPVideoEncoderParent(this); 188 if (!SendPGMPVideoEncoderConstructor(vep)) { 189 return NS_ERROR_FAILURE; 190 } 191 192 // This addref corresponds to the Proxy pointer the consumer is returned. 193 // It's dropped by calling Close() on the interface. 194 vep.get()->AddRef(); 195 *aGMPVE = vep; 196 mVideoEncoders.AppendElement(vep); 197 198 return NS_OK; 199 } 200 201 void GMPContentParentCloseBlocker::Destroy() { 202 MOZ_ASSERT(mParent); 203 MOZ_ASSERT(mEventTarget); 204 205 if (!mEventTarget->IsOnCurrentThread()) { 206 mEventTarget->Dispatch(NS_NewRunnableFunction( 207 __func__, [parent = std::move(mParent), eventTarget = mEventTarget]() { 208 parent->RemoveCloseBlocker(); 209 })); 210 mEventTarget = nullptr; 211 return; 212 } 213 214 mParent->RemoveCloseBlocker(); 215 mParent = nullptr; 216 mEventTarget = nullptr; 217 } 218 219 } // namespace mozilla::gmp