GMPVideoDecoderChild.cpp (6890B)
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 "GMPVideoDecoderChild.h" 7 8 #include "GMPContentChild.h" 9 #include "GMPPlatform.h" 10 #include "GMPVideoEncodedFrameImpl.h" 11 #include "GMPVideoi420FrameImpl.h" 12 #include "mozilla/StaticPrefs_media.h" 13 #include "runnable_utils.h" 14 15 namespace mozilla::gmp { 16 17 GMPVideoDecoderChild::GMPVideoDecoderChild(GMPContentChild* aPlugin) 18 : mPlugin(aPlugin), mVideoDecoder(nullptr), mVideoHost(this) { 19 MOZ_ASSERT(mPlugin); 20 } 21 22 GMPVideoDecoderChild::~GMPVideoDecoderChild() = default; 23 24 bool GMPVideoDecoderChild::MgrIsOnOwningThread() const { 25 return !mPlugin || mPlugin->GMPMessageLoop() == MessageLoop::current(); 26 } 27 28 void GMPVideoDecoderChild::Init(GMPVideoDecoder* aDecoder) { 29 MOZ_ASSERT(aDecoder, 30 "Cannot initialize video decoder child without a video decoder!"); 31 mVideoDecoder = aDecoder; 32 } 33 34 GMPVideoHostImpl& GMPVideoDecoderChild::Host() { return mVideoHost; } 35 36 void GMPVideoDecoderChild::Decoded(GMPVideoi420Frame* aDecodedFrame) { 37 if (!aDecodedFrame) { 38 MOZ_CRASH("Not given a decoded frame!"); 39 } 40 41 if (NS_WARN_IF(!mPlugin)) { 42 aDecodedFrame->Destroy(); 43 return; 44 } 45 46 MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); 47 48 auto df = static_cast<GMPVideoi420FrameImpl*>(aDecodedFrame); 49 50 if (GMPSharedMemManager* memMgr = mVideoHost.SharedMemMgr()) { 51 ipc::Shmem inputShmem; 52 if (memMgr->MgrTakeShmem(GMPSharedMemClass::Encoded, &inputShmem)) { 53 (void)SendReturnShmem(std::move(inputShmem)); 54 } 55 } 56 57 GMPVideoi420FrameData frameData; 58 ipc::Shmem frameShmem; 59 nsTArray<uint8_t> frameArray; 60 61 if (df->InitFrameData(frameData, frameShmem)) { 62 (void)SendDecodedShmem(frameData, std::move(frameShmem)); 63 } else if (df->InitFrameData(frameData, frameArray)) { 64 (void)SendDecodedData(frameData, std::move(frameArray)); 65 } else { 66 MOZ_CRASH("Decoded without any frame data!"); 67 } 68 69 aDecodedFrame->Destroy(); 70 } 71 72 void GMPVideoDecoderChild::ReceivedDecodedReferenceFrame( 73 const uint64_t aPictureId) { 74 if (NS_WARN_IF(!mPlugin)) { 75 return; 76 } 77 78 MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); 79 80 SendReceivedDecodedReferenceFrame(aPictureId); 81 } 82 83 void GMPVideoDecoderChild::ReceivedDecodedFrame(const uint64_t aPictureId) { 84 if (NS_WARN_IF(!mPlugin)) { 85 return; 86 } 87 88 MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); 89 90 SendReceivedDecodedFrame(aPictureId); 91 } 92 93 void GMPVideoDecoderChild::InputDataExhausted() { 94 if (NS_WARN_IF(!mPlugin)) { 95 return; 96 } 97 98 MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); 99 100 SendInputDataExhausted(); 101 } 102 103 void GMPVideoDecoderChild::DrainComplete() { 104 MOZ_ASSERT(mOutstandingDrain, "DrainComplete without Drain!"); 105 mOutstandingDrain = false; 106 107 if (NS_WARN_IF(!mPlugin)) { 108 return; 109 } 110 111 MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); 112 113 SendDrainComplete(); 114 } 115 116 void GMPVideoDecoderChild::ResetComplete() { 117 MOZ_ASSERT(mOutstandingReset, "ResetComplete without Reset!"); 118 mOutstandingReset = false; 119 120 if (NS_WARN_IF(!mPlugin)) { 121 return; 122 } 123 124 MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); 125 126 SendResetComplete(); 127 } 128 129 void GMPVideoDecoderChild::Error(GMPErr aError) { 130 if (NS_WARN_IF(!mPlugin)) { 131 return; 132 } 133 134 MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); 135 136 SendError(aError); 137 } 138 139 mozilla::ipc::IPCResult GMPVideoDecoderChild::RecvInitDecode( 140 const GMPVideoCodec& aCodecSettings, nsTArray<uint8_t>&& aCodecSpecific, 141 const int32_t& aCoreCount) { 142 if (!mVideoDecoder) { 143 return IPC_FAIL(this, "!mVideoDecoder"); 144 } 145 146 // Ignore any return code. It is OK for this to fail without killing the 147 // process. 148 mVideoDecoder->InitDecode(aCodecSettings, aCodecSpecific.Elements(), 149 aCodecSpecific.Length(), this, aCoreCount); 150 return IPC_OK(); 151 } 152 153 mozilla::ipc::IPCResult GMPVideoDecoderChild::RecvGiveShmem( 154 ipc::Shmem&& aOutputShmem) { 155 if (GMPSharedMemManager* memMgr = mVideoHost.SharedMemMgr()) { 156 memMgr->MgrGiveShmem(GMPSharedMemClass::Decoded, std::move(aOutputShmem)); 157 } else { 158 DeallocShmem(aOutputShmem); 159 } 160 161 return IPC_OK(); 162 } 163 164 mozilla::ipc::IPCResult GMPVideoDecoderChild::RecvDecode( 165 const GMPVideoEncodedFrameData& aInputFrame, ipc::Shmem&& aInputShmem, 166 const bool& aMissingFrames, nsTArray<uint8_t>&& aCodecSpecificInfo, 167 const int64_t& aRenderTimeMs) { 168 if (!mVideoDecoder) { 169 DeallocShmem(aInputShmem); 170 return IPC_FAIL(this, "!mVideoDecoder"); 171 } 172 173 auto* f = new GMPVideoEncodedFrameImpl(aInputFrame, std::move(aInputShmem), 174 &mVideoHost); 175 176 // Ignore any return code. It is OK for this to fail without killing the 177 // process. 178 mVideoDecoder->Decode(f, aMissingFrames, aCodecSpecificInfo.Elements(), 179 aCodecSpecificInfo.Length(), aRenderTimeMs); 180 181 return IPC_OK(); 182 } 183 184 mozilla::ipc::IPCResult GMPVideoDecoderChild::RecvReset() { 185 if (!mVideoDecoder) { 186 return IPC_FAIL(this, "!mVideoDecoder"); 187 } 188 189 if (mOutstandingReset) { 190 MOZ_ASSERT_UNREACHABLE("Already has outstanding reset!"); 191 return IPC_OK(); 192 } 193 194 // Ignore any return code. It is OK for this to fail without killing the 195 // process. 196 mOutstandingReset = true; 197 mVideoDecoder->Reset(); 198 199 return IPC_OK(); 200 } 201 202 mozilla::ipc::IPCResult GMPVideoDecoderChild::RecvDrain() { 203 if (!mVideoDecoder) { 204 return IPC_FAIL(this, "!mVideoDecoder"); 205 } 206 207 if (mOutstandingDrain) { 208 MOZ_ASSERT_UNREACHABLE("Already has outstanding drain!"); 209 return IPC_OK(); 210 } 211 212 // Ignore any return code. It is OK for this to fail without killing the 213 // process. 214 mOutstandingDrain = true; 215 mVideoDecoder->Drain(); 216 217 return IPC_OK(); 218 } 219 220 void GMPVideoDecoderChild::ActorDestroy(ActorDestroyReason why) { 221 // If there are no encoded frames, then we know that OpenH264 has destroyed 222 // any outstanding references to its pending decode frames. This means it 223 // should be safe to destroy the decoder since there should not be any pending 224 // sync callbacks. 225 if (!SpinPendingGmpEventsUntil( 226 [&]() -> bool { 227 return mOutstandingDrain || mOutstandingReset || 228 mVideoHost.IsEncodedFramesEmpty(); 229 }, 230 StaticPrefs::media_gmp_coder_shutdown_timeout_ms())) { 231 NS_WARNING("Timed out waiting for synchronous events!"); 232 } 233 234 if (mVideoDecoder) { 235 // Ignore any return code. It is OK for this to fail without killing the 236 // process. 237 mVideoDecoder->DecodingComplete(); 238 mVideoDecoder = nullptr; 239 } 240 241 mVideoHost.DoneWithAPI(); 242 243 mPlugin = nullptr; 244 } 245 246 } // namespace mozilla::gmp