GMPVideoEncoderChild.cpp (6821B)
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 "GMPVideoEncoderChild.h" 7 8 #include "GMPContentChild.h" 9 #include "GMPPlatform.h" 10 #include "GMPVideoEncodedFrameImpl.h" 11 #include "mozilla/StaticPrefs_media.h" 12 #include "runnable_utils.h" 13 14 namespace mozilla::gmp { 15 16 GMPVideoEncoderChild::GMPVideoEncoderChild(GMPContentChild* aPlugin) 17 : mPlugin(aPlugin), mVideoEncoder(nullptr), mVideoHost(this) { 18 MOZ_ASSERT(mPlugin); 19 } 20 21 GMPVideoEncoderChild::~GMPVideoEncoderChild() = default; 22 23 bool GMPVideoEncoderChild::MgrIsOnOwningThread() const { 24 return !mPlugin || mPlugin->GMPMessageLoop() == MessageLoop::current(); 25 } 26 27 void GMPVideoEncoderChild::Init(GMPVideoEncoder* aEncoder) { 28 MOZ_ASSERT(aEncoder, 29 "Cannot initialize video encoder child without a video encoder!"); 30 mVideoEncoder = aEncoder; 31 } 32 33 GMPVideoHostImpl& GMPVideoEncoderChild::Host() { return mVideoHost; } 34 35 void GMPVideoEncoderChild::Encoded(GMPVideoEncodedFrame* aEncodedFrame, 36 const uint8_t* aCodecSpecificInfo, 37 uint32_t aCodecSpecificInfoLength) { 38 if (NS_WARN_IF(!mPlugin)) { 39 aEncodedFrame->Destroy(); 40 return; 41 } 42 43 MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); 44 45 auto ef = static_cast<GMPVideoEncodedFrameImpl*>(aEncodedFrame); 46 47 if (GMPSharedMemManager* memMgr = mVideoHost.SharedMemMgr()) { 48 ipc::Shmem inputShmem; 49 if (memMgr->MgrTakeShmem(GMPSharedMemClass::Decoded, &inputShmem)) { 50 (void)SendReturnShmem(std::move(inputShmem)); 51 } 52 } 53 54 nsTArray<uint8_t> codecSpecific; 55 codecSpecific.AppendElements(aCodecSpecificInfo, aCodecSpecificInfoLength); 56 57 GMPVideoEncodedFrameData frameData; 58 ipc::Shmem frameShmem; 59 nsTArray<uint8_t> frameArray; 60 if (ef->RelinquishFrameData(frameData, frameShmem)) { 61 (void)SendEncodedShmem(frameData, std::move(frameShmem), codecSpecific); 62 } else if (ef->RelinquishFrameData(frameData, frameArray)) { 63 (void)SendEncodedData(frameData, std::move(frameArray), codecSpecific); 64 } else { 65 MOZ_CRASH("Encoded without any frame data!"); 66 } 67 68 mLatestEncodedTimestamp = frameData.mTimestamp(); 69 70 aEncodedFrame->Destroy(); 71 } 72 73 void GMPVideoEncoderChild::MgrDecodedFrameDestroyed( 74 GMPVideoi420FrameImpl* aFrame) { 75 if (NS_WARN_IF(!mPlugin)) { 76 return; 77 } 78 79 // The OpenH264 encoder destroys the input frame if it has skipped encoding 80 // it. When it has encoded it, it calls the Encoded() callback before 81 // destroying the frame. 82 MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); 83 if (aFrame->Timestamp() > mLatestEncodedTimestamp) { 84 (void)SendDroppedFrame(aFrame->Timestamp()); 85 } 86 } 87 88 void GMPVideoEncoderChild::Error(GMPErr aError) { 89 if (NS_WARN_IF(!mPlugin)) { 90 return; 91 } 92 93 MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); 94 95 SendError(aError); 96 } 97 98 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvInitEncode( 99 const GMPVideoCodec& aCodecSettings, nsTArray<uint8_t>&& aCodecSpecific, 100 const int32_t& aNumberOfCores, const uint32_t& aMaxPayloadSize) { 101 if (!mVideoEncoder) { 102 return IPC_FAIL(this, "!mVideoDecoder"); 103 } 104 105 // Ignore any return code. It is OK for this to fail without killing the 106 // process. 107 mVideoEncoder->InitEncode(aCodecSettings, aCodecSpecific.Elements(), 108 aCodecSpecific.Length(), this, aNumberOfCores, 109 aMaxPayloadSize); 110 111 return IPC_OK(); 112 } 113 114 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvGiveShmem( 115 ipc::Shmem&& aOutputShmem) { 116 if (GMPSharedMemManager* memMgr = mVideoHost.SharedMemMgr()) { 117 memMgr->MgrGiveShmem(GMPSharedMemClass::Encoded, std::move(aOutputShmem)); 118 } else { 119 DeallocShmem(aOutputShmem); 120 } 121 122 return IPC_OK(); 123 } 124 125 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvEncode( 126 const GMPVideoi420FrameData& aInputFrame, ipc::Shmem&& aInputShmem, 127 nsTArray<uint8_t>&& aCodecSpecificInfo, 128 nsTArray<GMPVideoFrameType>&& aFrameTypes) { 129 if (!mVideoEncoder) { 130 DeallocShmem(aInputShmem); 131 return IPC_FAIL(this, "!mVideoDecoder"); 132 } 133 134 // The `this` destroyed callback outlives the frame, because `mVideoEncoder` 135 // is responsible for destroying the frame, and we outlive `mVideoEncoder`. 136 auto* f = new GMPVideoi420FrameImpl(aInputFrame, std::move(aInputShmem), 137 &mVideoHost, HostReportPolicy::Destroyed); 138 139 // Ignore any return code. It is OK for this to fail without killing the 140 // process. 141 mVideoEncoder->Encode(f, aCodecSpecificInfo.Elements(), 142 aCodecSpecificInfo.Length(), aFrameTypes.Elements(), 143 aFrameTypes.Length()); 144 145 return IPC_OK(); 146 } 147 148 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvSetChannelParameters( 149 const uint32_t& aPacketLoss, const uint32_t& aRTT) { 150 if (!mVideoEncoder) { 151 return IPC_FAIL(this, "!mVideoDecoder"); 152 } 153 154 // Ignore any return code. It is OK for this to fail without killing the 155 // process. 156 mVideoEncoder->SetChannelParameters(aPacketLoss, aRTT); 157 158 return IPC_OK(); 159 } 160 161 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvSetRates( 162 const uint32_t& aNewBitRate, const uint32_t& aFrameRate) { 163 if (!mVideoEncoder) { 164 return IPC_FAIL(this, "!mVideoDecoder"); 165 } 166 167 // Ignore any return code. It is OK for this to fail without killing the 168 // process. 169 mVideoEncoder->SetRates(aNewBitRate, aFrameRate); 170 171 return IPC_OK(); 172 } 173 174 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvSetPeriodicKeyFrames( 175 const bool& aEnable) { 176 if (!mVideoEncoder) { 177 return IPC_FAIL(this, "!mVideoDecoder"); 178 } 179 180 // Ignore any return code. It is OK for this to fail without killing the 181 // process. 182 mVideoEncoder->SetPeriodicKeyFrames(aEnable); 183 184 return IPC_OK(); 185 } 186 187 void GMPVideoEncoderChild::ActorDestroy(ActorDestroyReason why) { 188 // If there are no decoded frames, then we know that OpenH264 has destroyed 189 // any outstanding references to its pending encode frames. This means it 190 // should be safe to destroy the encoder since there should not be any pending 191 // sync callbacks. 192 if (!SpinPendingGmpEventsUntil( 193 [&]() -> bool { return mVideoHost.IsDecodedFramesEmpty(); }, 194 StaticPrefs::media_gmp_coder_shutdown_timeout_ms())) { 195 NS_WARNING("Timed out waiting for synchronous events!"); 196 } 197 198 if (mVideoEncoder) { 199 // Ignore any return code. It is OK for this to fail without killing the 200 // process. 201 mVideoEncoder->EncodingComplete(); 202 mVideoEncoder = nullptr; 203 } 204 205 mVideoHost.DoneWithAPI(); 206 207 mPlugin = nullptr; 208 } 209 210 } // namespace mozilla::gmp