GMPVideoDecoderParent.cpp (13725B)
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 "GMPVideoDecoderParent.h" 7 8 #include "GMPContentParent.h" 9 #include "GMPLog.h" 10 #include "GMPMessageUtils.h" 11 #include "GMPUtils.h" 12 #include "GMPVideoEncodedFrameImpl.h" 13 #include "GMPVideoi420FrameImpl.h" 14 #include "mozilla/gmp/GMPTypes.h" 15 #include "nsAutoRef.h" 16 #include "nsPrintfCString.h" 17 #include "nsThreadUtils.h" 18 19 namespace mozilla::gmp { 20 21 // States: 22 // Initial: mIsOpen == false 23 // on InitDecode success -> Open 24 // on Shutdown -> Dead 25 // Open: mIsOpen == true 26 // on Close -> Dead 27 // on ActorDestroy -> Dead 28 // on Shutdown -> Dead 29 // Dead: mIsOpen == false 30 31 GMPVideoDecoderParent::GMPVideoDecoderParent(GMPContentParent* aPlugin) 32 : mIsOpen(false), 33 mShuttingDown(false), 34 mActorDestroyed(false), 35 mIsAwaitingResetComplete(false), 36 mIsAwaitingDrainComplete(false), 37 mPlugin(aPlugin), 38 mCallback(nullptr), 39 mVideoHost(this), 40 mPluginId(aPlugin->GetPluginId()), 41 mPluginType(aPlugin->GetPluginType()), 42 mFrameCount(0) { 43 MOZ_ASSERT(mPlugin); 44 } 45 46 GMPVideoDecoderParent::~GMPVideoDecoderParent() = default; 47 48 bool GMPVideoDecoderParent::MgrIsOnOwningThread() const { 49 return !mPlugin || mPlugin->GMPEventTarget()->IsOnCurrentThread(); 50 } 51 52 GMPVideoHostImpl& GMPVideoDecoderParent::Host() { return mVideoHost; } 53 54 // Note: may be called via Terminated() 55 void GMPVideoDecoderParent::Close() { 56 GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::Close()", this); 57 MOZ_ASSERT(!mPlugin || mPlugin->GMPEventTarget()->IsOnCurrentThread()); 58 59 // Ensure if we've received a Close while waiting for a ResetComplete 60 // or DrainComplete notification, we'll unblock the caller before processing 61 // the close. This seems unlikely to happen, but better to be careful. 62 UnblockResetAndDrain(); 63 64 // Consumer is done with us; we can shut down. No more callbacks should 65 // be made to mCallback. Note: do this before Shutdown()! 66 mCallback = nullptr; 67 // Let Shutdown mark us as dead so it knows if we had been alive 68 69 // In case this is the last reference 70 RefPtr<GMPVideoDecoderParent> kungfudeathgrip(this); 71 Release(); 72 Shutdown(); 73 } 74 75 nsresult GMPVideoDecoderParent::InitDecode( 76 const GMPVideoCodec& aCodecSettings, 77 const nsTArray<uint8_t>& aCodecSpecific, 78 GMPVideoDecoderCallbackProxy* aCallback, int32_t aCoreCount) { 79 GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::InitDecode()", this); 80 81 if (mActorDestroyed) { 82 NS_WARNING("Trying to use a destroyed GMP video decoder!"); 83 return NS_ERROR_FAILURE; 84 } 85 if (mIsOpen) { 86 NS_WARNING("Trying to re-init an in-use GMP video decoder!"); 87 return NS_ERROR_FAILURE; 88 } 89 90 MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread()); 91 92 if (!aCallback) { 93 return NS_ERROR_FAILURE; 94 } 95 mCallback = aCallback; 96 97 if (!SendInitDecode(aCodecSettings, aCodecSpecific, aCoreCount)) { 98 return NS_ERROR_FAILURE; 99 } 100 mIsOpen = true; 101 102 // Async IPC, we don't have access to a return value. 103 return NS_OK; 104 } 105 106 nsresult GMPVideoDecoderParent::Decode( 107 GMPUniquePtr<GMPVideoEncodedFrame> aInputFrame, bool aMissingFrames, 108 const nsTArray<uint8_t>& aCodecSpecificInfo, int64_t aRenderTimeMs) { 109 GMP_LOG_VERBOSE( 110 "GMPVideoDecoderParent[%p]::Decode() timestamp=%" PRId64 " keyframe=%d", 111 this, aInputFrame->TimeStamp(), aInputFrame->FrameType() == kGMPKeyFrame); 112 113 if (!mIsOpen) { 114 GMP_LOG_ERROR( 115 "GMPVideoDecoderParent[%p]::Decode() ERROR; dead GMPVideoDecoder", 116 this); 117 NS_WARNING("Trying to use an dead GMP video decoder"); 118 return NS_ERROR_FAILURE; 119 } 120 121 MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread()); 122 123 GMPUniquePtr<GMPVideoEncodedFrameImpl> inputFrameImpl( 124 static_cast<GMPVideoEncodedFrameImpl*>(aInputFrame.release())); 125 126 GMPVideoEncodedFrameData frameData; 127 ipc::Shmem frameShmem; 128 if (!inputFrameImpl->RelinquishFrameData(frameData, frameShmem)) { 129 GMP_LOG_ERROR( 130 "GMPVideoDecoderParent[%p]::Decode() ERROR; missing input shmem", this); 131 return NS_ERROR_FAILURE; 132 } 133 134 if (mDecodedShmemSize > 0) { 135 if (GMPSharedMemManager* memMgr = mVideoHost.SharedMemMgr()) { 136 ipc::Shmem outputShmem; 137 if (memMgr->MgrTakeShmem(GMPSharedMemClass::Decoded, mDecodedShmemSize, 138 &outputShmem)) { 139 (void)SendGiveShmem(std::move(outputShmem)); 140 } 141 } 142 } 143 144 if (!SendDecode(frameData, std::move(frameShmem), aMissingFrames, 145 aCodecSpecificInfo, aRenderTimeMs)) { 146 GMP_LOG_ERROR( 147 "GMPVideoDecoderParent[%p]::Decode() ERROR; SendDecode() failure.", 148 this); 149 return NS_ERROR_FAILURE; 150 } 151 mFrameCount++; 152 153 // Async IPC, we don't have access to a return value. 154 return NS_OK; 155 } 156 157 nsresult GMPVideoDecoderParent::Reset() { 158 GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::Reset()", this); 159 160 if (!mIsOpen) { 161 NS_WARNING("Trying to use an dead GMP video decoder"); 162 return NS_ERROR_FAILURE; 163 } 164 165 MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread()); 166 167 if (!SendReset()) { 168 return NS_ERROR_FAILURE; 169 } 170 171 mIsAwaitingResetComplete = true; 172 173 RefPtr<GMPVideoDecoderParent> self(this); 174 nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction( 175 "gmp::GMPVideoDecoderParent::Reset", [self]() -> void { 176 GMP_LOG_DEBUG( 177 "GMPVideoDecoderParent[%p]::ResetCompleteTimeout() timed out " 178 "waiting for ResetComplete", 179 self.get()); 180 self->mResetCompleteTimeout = nullptr; 181 LogToBrowserConsole(nsLiteralString( 182 u"GMPVideoDecoderParent timed out waiting for ResetComplete()")); 183 }); 184 CancelResetCompleteTimeout(); 185 nsCOMPtr<nsISerialEventTarget> target = mPlugin->GMPEventTarget(); 186 mResetCompleteTimeout = SimpleTimer::Create(task, 5000, target); 187 188 // Async IPC, we don't have access to a return value. 189 return NS_OK; 190 } 191 192 void GMPVideoDecoderParent::CancelResetCompleteTimeout() { 193 if (mResetCompleteTimeout) { 194 mResetCompleteTimeout->Cancel(); 195 mResetCompleteTimeout = nullptr; 196 } 197 } 198 199 nsresult GMPVideoDecoderParent::Drain() { 200 GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::Drain() frameCount=%d", this, 201 mFrameCount); 202 203 if (!mIsOpen) { 204 NS_WARNING("Trying to use an dead GMP video decoder"); 205 return NS_ERROR_FAILURE; 206 } 207 208 MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread()); 209 210 if (!SendDrain()) { 211 return NS_ERROR_FAILURE; 212 } 213 214 mIsAwaitingDrainComplete = true; 215 216 // Async IPC, we don't have access to a return value. 217 return NS_OK; 218 } 219 220 nsCString GMPVideoDecoderParent::GetDisplayName() const { 221 if (NS_WARN_IF(!mIsOpen)) { 222 return ""_ns; 223 } 224 225 MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread()); 226 return mPlugin->GetDisplayName(); 227 } 228 229 // Note: Consider keeping ActorDestroy sync'd up when making changes here. 230 nsresult GMPVideoDecoderParent::Shutdown() { 231 GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::Shutdown()", this); 232 MOZ_ASSERT(!mPlugin || mPlugin->GMPEventTarget()->IsOnCurrentThread()); 233 234 if (mShuttingDown) { 235 return NS_OK; 236 } 237 mShuttingDown = true; 238 239 // Ensure if we've received a shutdown while waiting for a ResetComplete 240 // or DrainComplete notification, we'll unblock the caller before processing 241 // the shutdown. 242 UnblockResetAndDrain(); 243 244 // Notify client we're gone! Won't occur after Close() 245 if (mCallback) { 246 mCallback->Terminated(); 247 mCallback = nullptr; 248 } 249 250 mIsOpen = false; 251 if (!mActorDestroyed) { 252 (void)Send__delete__(this); 253 } 254 255 return NS_OK; 256 } 257 258 // Note: Keep this sync'd up with Shutdown 259 void GMPVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy) { 260 GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::ActorDestroy reason=%d", this, 261 aWhy); 262 263 mIsOpen = false; 264 mActorDestroyed = true; 265 266 // Ensure if we've received a destroy while waiting for a ResetComplete 267 // or DrainComplete notification, we'll unblock the caller before processing 268 // the error. 269 UnblockResetAndDrain(); 270 271 if (mCallback) { 272 // May call Close() (and Shutdown()) immediately or with a delay 273 mCallback->Terminated(); 274 mCallback = nullptr; 275 } 276 if (mPlugin) { 277 // Ignore any return code. It is OK for this to fail without killing the 278 // process. 279 mPlugin->VideoDecoderDestroyed(this); 280 mPlugin = nullptr; 281 } 282 mVideoHost.ActorDestroyed(); 283 MaybeDisconnect(aWhy == AbnormalShutdown); 284 } 285 286 bool GMPVideoDecoderParent::HandleDecoded( 287 const GMPVideoi420FrameData& aDecodedFrame, size_t aDecodedSize) { 288 --mFrameCount; 289 if (aDecodedFrame.mUpdatedTimestamp() && 290 aDecodedFrame.mUpdatedTimestamp().value() != aDecodedFrame.mTimestamp()) { 291 GMP_LOG_VERBOSE( 292 "GMPVideoDecoderParent[%p]::HandleDecoded() timestamp=[%" PRId64 293 " -> %" PRId64 "] frameCount=%d", 294 this, aDecodedFrame.mTimestamp(), 295 aDecodedFrame.mUpdatedTimestamp().value(), mFrameCount); 296 } else { 297 GMP_LOG_VERBOSE( 298 "GMPVideoDecoderParent[%p]::HandleDecoded() timestamp=%" PRId64 299 " frameCount=%d", 300 this, aDecodedFrame.mTimestamp(), mFrameCount); 301 } 302 303 if (mCallback) { 304 if (GMPVideoi420FrameImpl::CheckFrameData(aDecodedFrame, aDecodedSize)) { 305 return true; 306 } else { 307 GMP_LOG_ERROR( 308 "GMPVideoDecoderParent[%p]::HandleDecoded() " 309 "timestamp=%" PRId64 " decoded frame corrupt, ignoring", 310 this, aDecodedFrame.mTimestamp()); 311 // TODO: Verify if we should take more serious the arrival of 312 // a corrupted frame, see bug 1750506. 313 } 314 } 315 316 return false; 317 } 318 319 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvReturnShmem( 320 ipc::Shmem&& aInputShmem) { 321 if (GMPSharedMemManager* memMgr = mVideoHost.SharedMemMgr()) { 322 memMgr->MgrGiveShmem(GMPSharedMemClass::Encoded, std::move(aInputShmem)); 323 } else { 324 DeallocShmem(aInputShmem); 325 } 326 327 return IPC_OK(); 328 } 329 330 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvDecodedShmem( 331 const GMPVideoi420FrameData& aDecodedFrame, ipc::Shmem&& aDecodedShmem) { 332 if (HandleDecoded(aDecodedFrame, aDecodedShmem.Size<uint8_t>())) { 333 auto* f = new GMPVideoi420FrameImpl(aDecodedFrame, std::move(aDecodedShmem), 334 &mVideoHost); 335 mCallback->Decoded(f); 336 } else { 337 DeallocShmem(aDecodedShmem); 338 } 339 return IPC_OK(); 340 } 341 342 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvDecodedData( 343 const GMPVideoi420FrameData& aDecodedFrame, 344 nsTArray<uint8_t>&& aDecodedArray) { 345 if (HandleDecoded(aDecodedFrame, aDecodedArray.Length())) { 346 mDecodedShmemSize = std::max(mDecodedShmemSize, aDecodedArray.Length()); 347 auto* f = new GMPVideoi420FrameImpl(aDecodedFrame, std::move(aDecodedArray), 348 &mVideoHost); 349 mCallback->Decoded(f); 350 } 351 352 return IPC_OK(); 353 } 354 355 mozilla::ipc::IPCResult 356 GMPVideoDecoderParent::RecvReceivedDecodedReferenceFrame( 357 const uint64_t& aPictureId) { 358 if (mCallback) { 359 mCallback->ReceivedDecodedReferenceFrame(aPictureId); 360 } 361 362 return IPC_OK(); 363 } 364 365 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvReceivedDecodedFrame( 366 const uint64_t& aPictureId) { 367 if (mCallback) { 368 mCallback->ReceivedDecodedFrame(aPictureId); 369 } 370 371 return IPC_OK(); 372 } 373 374 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvInputDataExhausted() { 375 GMP_LOG_VERBOSE("GMPVideoDecoderParent[%p]::RecvInputDataExhausted()", this); 376 377 if (mCallback) { 378 mCallback->InputDataExhausted(); 379 } 380 381 return IPC_OK(); 382 } 383 384 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvDrainComplete() { 385 GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::RecvDrainComplete() frameCount=%d", 386 this, mFrameCount); 387 nsAutoString msg; 388 msg.AppendLiteral( 389 "GMPVideoDecoderParent::RecvDrainComplete() outstanding frames="); 390 msg.AppendInt(mFrameCount); 391 LogToBrowserConsole(msg); 392 393 if (mCallback && mIsAwaitingDrainComplete) { 394 mIsAwaitingDrainComplete = false; 395 396 mCallback->DrainComplete(); 397 } 398 399 return IPC_OK(); 400 } 401 402 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvResetComplete() { 403 GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::RecvResetComplete()", this); 404 405 CancelResetCompleteTimeout(); 406 407 if (mCallback && mIsAwaitingResetComplete) { 408 mIsAwaitingResetComplete = false; 409 mFrameCount = 0; 410 411 mCallback->ResetComplete(); 412 } 413 414 return IPC_OK(); 415 } 416 417 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvError(const GMPErr& aError) { 418 GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::RecvError(error=%d)", this, aError); 419 420 if (mCallback) { 421 // Ensure if we've received an error while waiting for a ResetComplete 422 // or DrainComplete notification, we'll unblock the caller before processing 423 // the error. 424 UnblockResetAndDrain(); 425 426 mCallback->Error(aError); 427 } 428 429 return IPC_OK(); 430 } 431 432 mozilla::ipc::IPCResult GMPVideoDecoderParent::RecvShutdown() { 433 GMP_LOG_DEBUG("GMPVideoDecoderParent[%p]::RecvShutdown()", this); 434 435 Shutdown(); 436 return IPC_OK(); 437 } 438 439 void GMPVideoDecoderParent::UnblockResetAndDrain() { 440 GMP_LOG_DEBUG( 441 "GMPVideoDecoderParent[%p]::UnblockResetAndDrain() " 442 "awaitingResetComplete=%d awaitingDrainComplete=%d", 443 this, mIsAwaitingResetComplete, mIsAwaitingDrainComplete); 444 445 if (!mCallback) { 446 MOZ_ASSERT(!mIsAwaitingResetComplete); 447 MOZ_ASSERT(!mIsAwaitingDrainComplete); 448 return; 449 } 450 if (mIsAwaitingResetComplete) { 451 mIsAwaitingResetComplete = false; 452 mCallback->ResetComplete(); 453 } 454 if (mIsAwaitingDrainComplete) { 455 mIsAwaitingDrainComplete = false; 456 mCallback->DrainComplete(); 457 } 458 CancelResetCompleteTimeout(); 459 } 460 461 } // namespace mozilla::gmp