GMPVideoEncoderParent.cpp (8646B)
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 "GMPVideoEncoderParent.h" 7 8 #include "GMPContentParent.h" 9 #include "GMPCrashHelper.h" 10 #include "GMPLog.h" 11 #include "GMPMessageUtils.h" 12 #include "GMPVideoEncodedFrameImpl.h" 13 #include "GMPVideoi420FrameImpl.h" 14 #include "mozilla/gmp/GMPTypes.h" 15 #include "nsAutoRef.h" 16 #include "nsThread.h" 17 #include "nsThreadUtils.h" 18 #include "runnable_utils.h" 19 20 namespace mozilla::gmp { 21 22 #ifdef __CLASS__ 23 # undef __CLASS__ 24 #endif 25 #define __CLASS__ "GMPVideoEncoderParent" 26 27 // States: 28 // Initial: mIsOpen == false 29 // on InitDecode success -> Open 30 // on Shutdown -> Dead 31 // Open: mIsOpen == true 32 // on Close -> Dead 33 // on ActorDestroy -> Dead 34 // on Shutdown -> Dead 35 // Dead: mIsOpen == false 36 37 GMPVideoEncoderParent::GMPVideoEncoderParent(GMPContentParent* aPlugin) 38 : mIsOpen(false), 39 mShuttingDown(false), 40 mActorDestroyed(false), 41 mPlugin(aPlugin), 42 mCallback(nullptr), 43 mVideoHost(this), 44 mPluginId(aPlugin->GetPluginId()) { 45 MOZ_ASSERT(mPlugin); 46 } 47 48 bool GMPVideoEncoderParent::MgrIsOnOwningThread() const { 49 return !mPlugin || mPlugin->GMPEventTarget()->IsOnCurrentThread(); 50 } 51 52 GMPVideoHostImpl& GMPVideoEncoderParent::Host() { return mVideoHost; } 53 54 // Note: may be called via Terminated() 55 void GMPVideoEncoderParent::Close() { 56 GMP_LOG_DEBUG("%s::%s: %p", __CLASS__, __FUNCTION__, this); 57 MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread()); 58 // Consumer is done with us; we can shut down. No more callbacks should 59 // be made to mCallback. Note: do this before Shutdown()! 60 mCallback = nullptr; 61 62 // Let Shutdown mark us as dead so it knows if we had been alive 63 64 // In case this is the last reference 65 RefPtr<GMPVideoEncoderParent> kungfudeathgrip(this); 66 Release(); 67 Shutdown(); 68 } 69 70 GMPErr GMPVideoEncoderParent::InitEncode( 71 const GMPVideoCodec& aCodecSettings, 72 const nsTArray<uint8_t>& aCodecSpecific, 73 GMPVideoEncoderCallbackProxy* aCallback, int32_t aNumberOfCores, 74 uint32_t aMaxPayloadSize) { 75 GMP_LOG_DEBUG("%s::%s: %p", __CLASS__, __FUNCTION__, this); 76 if (mIsOpen) { 77 NS_WARNING("Trying to re-init an in-use GMP video encoder!"); 78 return GMPGenericErr; 79 ; 80 } 81 82 MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread()); 83 MOZ_ASSERT(!mCallback); 84 85 if (!aCallback) { 86 return GMPGenericErr; 87 } 88 mCallback = aCallback; 89 90 if (!SendInitEncode(aCodecSettings, aCodecSpecific, aNumberOfCores, 91 aMaxPayloadSize)) { 92 return GMPGenericErr; 93 } 94 mIsOpen = true; 95 96 // Async IPC, we don't have access to a return value. 97 return GMPNoErr; 98 } 99 100 GMPErr GMPVideoEncoderParent::Encode( 101 GMPUniquePtr<GMPVideoi420Frame> aInputFrame, 102 const nsTArray<uint8_t>& aCodecSpecificInfo, 103 const nsTArray<GMPVideoFrameType>& aFrameTypes) { 104 if (!mIsOpen) { 105 NS_WARNING("Trying to use an dead GMP video encoder"); 106 return GMPGenericErr; 107 } 108 109 MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread()); 110 111 GMPUniquePtr<GMPVideoi420FrameImpl> inputFrameImpl( 112 static_cast<GMPVideoi420FrameImpl*>(aInputFrame.release())); 113 114 GMPVideoi420FrameData frameData; 115 ipc::Shmem frameShmem; 116 if (!inputFrameImpl->InitFrameData(frameData, frameShmem)) { 117 GMP_LOG_ERROR("%s::%s: failed to init frame data", __CLASS__, __FUNCTION__); 118 return GMPGenericErr; 119 } 120 121 if (mEncodedShmemSize > 0) { 122 if (GMPSharedMemManager* memMgr = mVideoHost.SharedMemMgr()) { 123 ipc::Shmem outputShmem; 124 if (memMgr->MgrTakeShmem(GMPSharedMemClass::Encoded, mEncodedShmemSize, 125 &outputShmem)) { 126 (void)SendGiveShmem(std::move(outputShmem)); 127 } 128 } 129 } 130 131 if (!SendEncode(frameData, std::move(frameShmem), aCodecSpecificInfo, 132 aFrameTypes)) { 133 GMP_LOG_ERROR("%s::%s: failed to send encode", __CLASS__, __FUNCTION__); 134 return GMPGenericErr; 135 } 136 137 // Async IPC, we don't have access to a return value. 138 return GMPNoErr; 139 } 140 141 GMPErr GMPVideoEncoderParent::SetChannelParameters(uint32_t aPacketLoss, 142 uint32_t aRTT) { 143 if (!mIsOpen) { 144 NS_WARNING("Trying to use an invalid GMP video encoder!"); 145 return GMPGenericErr; 146 } 147 148 MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread()); 149 150 if (!SendSetChannelParameters(aPacketLoss, aRTT)) { 151 return GMPGenericErr; 152 } 153 154 // Async IPC, we don't have access to a return value. 155 return GMPNoErr; 156 } 157 158 GMPErr GMPVideoEncoderParent::SetRates(uint32_t aNewBitRate, 159 uint32_t aFrameRate) { 160 if (!mIsOpen) { 161 NS_WARNING("Trying to use an dead GMP video decoder"); 162 return GMPGenericErr; 163 } 164 165 MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread()); 166 167 if (!SendSetRates(aNewBitRate, aFrameRate)) { 168 return GMPGenericErr; 169 } 170 171 // Async IPC, we don't have access to a return value. 172 return GMPNoErr; 173 } 174 175 GMPErr GMPVideoEncoderParent::SetPeriodicKeyFrames(bool aEnable) { 176 if (!mIsOpen) { 177 NS_WARNING("Trying to use an invalid GMP video encoder!"); 178 return GMPGenericErr; 179 } 180 181 MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread()); 182 183 if (!SendSetPeriodicKeyFrames(aEnable)) { 184 return GMPGenericErr; 185 } 186 187 // Async IPC, we don't have access to a return value. 188 return GMPNoErr; 189 } 190 191 // Note: Consider keeping ActorDestroy sync'd up when making changes here. 192 void GMPVideoEncoderParent::Shutdown() { 193 GMP_LOG_DEBUG("%s::%s: %p", __CLASS__, __FUNCTION__, this); 194 MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread()); 195 196 if (mShuttingDown) { 197 return; 198 } 199 mShuttingDown = true; 200 201 // Notify client we're gone! Won't occur after Close() 202 if (mCallback) { 203 mCallback->Terminated(); 204 mCallback = nullptr; 205 } 206 207 mIsOpen = false; 208 if (!mActorDestroyed) { 209 (void)Send__delete__(this); 210 } 211 } 212 213 // Note: Keep this sync'd up with Shutdown 214 void GMPVideoEncoderParent::ActorDestroy(ActorDestroyReason aWhy) { 215 GMP_LOG_DEBUG("%s::%s: %p (%d)", __CLASS__, __FUNCTION__, this, (int)aWhy); 216 mIsOpen = false; 217 mActorDestroyed = true; 218 if (mCallback) { 219 // May call Close() (and Shutdown()) immediately or with a delay 220 mCallback->Terminated(); 221 mCallback = nullptr; 222 } 223 if (mPlugin) { 224 // Ignore any return code. It is OK for this to fail without killing the 225 // process. 226 mPlugin->VideoEncoderDestroyed(this); 227 mPlugin = nullptr; 228 } 229 mVideoHost.ActorDestroyed(); // same as DoneWithAPI 230 MaybeDisconnect(aWhy == AbnormalShutdown); 231 } 232 233 mozilla::ipc::IPCResult GMPVideoEncoderParent::RecvReturnShmem( 234 ipc::Shmem&& aInputShmem) { 235 if (GMPSharedMemManager* memMgr = mVideoHost.SharedMemMgr()) { 236 memMgr->MgrGiveShmem(GMPSharedMemClass::Decoded, std::move(aInputShmem)); 237 } else { 238 DeallocShmem(aInputShmem); 239 } 240 241 return IPC_OK(); 242 } 243 244 mozilla::ipc::IPCResult GMPVideoEncoderParent::RecvEncodedShmem( 245 const GMPVideoEncodedFrameData& aEncodedFrame, ipc::Shmem&& aEncodedShmem, 246 nsTArray<uint8_t>&& aCodecSpecificInfo) { 247 if (mCallback && GMPVideoEncodedFrameImpl::CheckFrameData( 248 aEncodedFrame, aEncodedShmem.Size<uint8_t>())) { 249 auto* f = new GMPVideoEncodedFrameImpl( 250 aEncodedFrame, std::move(aEncodedShmem), &mVideoHost); 251 mCallback->Encoded(f, aCodecSpecificInfo); 252 f->Destroy(); 253 } else { 254 DeallocShmem(aEncodedShmem); 255 } 256 return IPC_OK(); 257 } 258 259 mozilla::ipc::IPCResult GMPVideoEncoderParent::RecvEncodedData( 260 const GMPVideoEncodedFrameData& aEncodedFrame, 261 nsTArray<uint8_t>&& aEncodedData, nsTArray<uint8_t>&& aCodecSpecificInfo) { 262 if (mCallback && GMPVideoEncodedFrameImpl::CheckFrameData( 263 aEncodedFrame, aEncodedData.Length())) { 264 mEncodedShmemSize = std::max(mEncodedShmemSize, aEncodedData.Length()); 265 auto* f = new GMPVideoEncodedFrameImpl( 266 aEncodedFrame, std::move(aEncodedData), &mVideoHost); 267 mCallback->Encoded(f, aCodecSpecificInfo); 268 f->Destroy(); 269 } 270 return IPC_OK(); 271 } 272 273 mozilla::ipc::IPCResult GMPVideoEncoderParent::RecvDroppedFrame( 274 const uint64_t& aTimestamp) { 275 if (mCallback) { 276 mCallback->Dropped(aTimestamp); 277 } 278 return IPC_OK(); 279 } 280 281 mozilla::ipc::IPCResult GMPVideoEncoderParent::RecvError(const GMPErr& aError) { 282 if (mCallback) { 283 mCallback->Error(aError); 284 } 285 286 return IPC_OK(); 287 } 288 289 mozilla::ipc::IPCResult GMPVideoEncoderParent::RecvShutdown() { 290 Shutdown(); 291 return IPC_OK(); 292 } 293 294 } // namespace mozilla::gmp 295 296 #undef __CLASS__