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/. */
6 #include "GMPVideoEncoderParent.h"
8 #include "GMPContentParent.h"
9 #include "GMPCrashHelper.h"
11 #include "GMPMessageUtils.h"
12 #include "GMPVideoEncodedFrameImpl.h"
13 #include "GMPVideoi420FrameImpl.h"
14 #include "mozilla/gmp/GMPTypes.h"
15 #include "mozilla/Unused.h"
16 #include "nsAutoRef.h"
18 #include "nsThreadUtils.h"
19 #include "runnable_utils.h"
21 namespace mozilla::gmp
{
26 #define __CLASS__ "GMPVideoEncoderParent"
29 // Initial: mIsOpen == false
30 // on InitDecode success -> Open
31 // on Shutdown -> Dead
32 // Open: mIsOpen == true
34 // on ActorDestroy -> Dead
35 // on Shutdown -> Dead
36 // Dead: mIsOpen == false
38 GMPVideoEncoderParent::GMPVideoEncoderParent(GMPContentParent
* aPlugin
)
39 : GMPSharedMemManager(aPlugin
),
42 mActorDestroyed(false),
46 mPluginId(aPlugin
->GetPluginId()) {
50 GMPVideoHostImpl
& GMPVideoEncoderParent::Host() { return mVideoHost
; }
52 // Note: may be called via Terminated()
53 void GMPVideoEncoderParent::Close() {
54 GMP_LOG_DEBUG("%s::%s: %p", __CLASS__
, __FUNCTION__
, this);
55 MOZ_ASSERT(mPlugin
->GMPEventTarget()->IsOnCurrentThread());
56 // Consumer is done with us; we can shut down. No more callbacks should
57 // be made to mCallback. Note: do this before Shutdown()!
60 // Let Shutdown mark us as dead so it knows if we had been alive
62 // In case this is the last reference
63 RefPtr
<GMPVideoEncoderParent
> kungfudeathgrip(this);
68 GMPErr
GMPVideoEncoderParent::InitEncode(
69 const GMPVideoCodec
& aCodecSettings
,
70 const nsTArray
<uint8_t>& aCodecSpecific
,
71 GMPVideoEncoderCallbackProxy
* aCallback
, int32_t aNumberOfCores
,
72 uint32_t aMaxPayloadSize
) {
73 GMP_LOG_DEBUG("%s::%s: %p", __CLASS__
, __FUNCTION__
, this);
75 NS_WARNING("Trying to re-init an in-use GMP video encoder!");
80 MOZ_ASSERT(mPlugin
->GMPEventTarget()->IsOnCurrentThread());
81 MOZ_ASSERT(!mCallback
);
86 mCallback
= aCallback
;
88 if (!SendInitEncode(aCodecSettings
, aCodecSpecific
, aNumberOfCores
,
94 // Async IPC, we don't have access to a return value.
98 GMPErr
GMPVideoEncoderParent::Encode(
99 GMPUniquePtr
<GMPVideoi420Frame
> aInputFrame
,
100 const nsTArray
<uint8_t>& aCodecSpecificInfo
,
101 const nsTArray
<GMPVideoFrameType
>& aFrameTypes
) {
103 NS_WARNING("Trying to use an dead GMP video encoder");
104 return GMPGenericErr
;
107 MOZ_ASSERT(mPlugin
->GMPEventTarget()->IsOnCurrentThread());
109 GMPUniquePtr
<GMPVideoi420FrameImpl
> inputFrameImpl(
110 static_cast<GMPVideoi420FrameImpl
*>(aInputFrame
.release()));
112 // Very rough kill-switch if the plugin stops processing. If it's merely
113 // hung and continues, we'll come back to life eventually.
114 // 3* is because we're using 3 buffers per frame for i420 data for now.
115 if ((NumInUse(GMPSharedMem::kGMPFrameData
) >
116 3 * GMPSharedMem::kGMPBufLimit
) ||
117 (NumInUse(GMPSharedMem::kGMPEncodedData
) > GMPSharedMem::kGMPBufLimit
)) {
119 "%s::%s: Out of mem buffers. Frame Buffers:%lu Max:%lu, Encoded "
120 "Buffers: %lu Max: %lu",
121 __CLASS__
, __FUNCTION__
,
122 static_cast<unsigned long>(NumInUse(GMPSharedMem::kGMPFrameData
)),
123 static_cast<unsigned long>(3 * GMPSharedMem::kGMPBufLimit
),
124 static_cast<unsigned long>(NumInUse(GMPSharedMem::kGMPEncodedData
)),
125 static_cast<unsigned long>(GMPSharedMem::kGMPBufLimit
));
126 return GMPGenericErr
;
129 GMPVideoi420FrameData frameData
;
130 inputFrameImpl
->InitFrameData(frameData
);
132 if (!SendEncode(frameData
, aCodecSpecificInfo
, aFrameTypes
)) {
133 GMP_LOG_ERROR("%s::%s: failed to send encode", __CLASS__
, __FUNCTION__
);
134 return GMPGenericErr
;
137 // Async IPC, we don't have access to a return value.
141 GMPErr
GMPVideoEncoderParent::SetChannelParameters(uint32_t aPacketLoss
,
144 NS_WARNING("Trying to use an invalid GMP video encoder!");
145 return GMPGenericErr
;
148 MOZ_ASSERT(mPlugin
->GMPEventTarget()->IsOnCurrentThread());
150 if (!SendSetChannelParameters(aPacketLoss
, aRTT
)) {
151 return GMPGenericErr
;
154 // Async IPC, we don't have access to a return value.
158 GMPErr
GMPVideoEncoderParent::SetRates(uint32_t aNewBitRate
,
159 uint32_t aFrameRate
) {
161 NS_WARNING("Trying to use an dead GMP video decoder");
162 return GMPGenericErr
;
165 MOZ_ASSERT(mPlugin
->GMPEventTarget()->IsOnCurrentThread());
167 if (!SendSetRates(aNewBitRate
, aFrameRate
)) {
168 return GMPGenericErr
;
171 // Async IPC, we don't have access to a return value.
175 GMPErr
GMPVideoEncoderParent::SetPeriodicKeyFrames(bool aEnable
) {
177 NS_WARNING("Trying to use an invalid GMP video encoder!");
178 return GMPGenericErr
;
181 MOZ_ASSERT(mPlugin
->GMPEventTarget()->IsOnCurrentThread());
183 if (!SendSetPeriodicKeyFrames(aEnable
)) {
184 return GMPGenericErr
;
187 // Async IPC, we don't have access to a return value.
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());
199 mShuttingDown
= true;
201 // Notify client we're gone! Won't occur after Close()
203 mCallback
->Terminated();
208 if (!mActorDestroyed
) {
209 Unused
<< SendEncodingComplete();
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
);
217 mActorDestroyed
= true;
219 // May call Close() (and Shutdown()) immediately or with a delay
220 mCallback
->Terminated();
224 // Ignore any return code. It is OK for this to fail without killing the
226 mPlugin
->VideoEncoderDestroyed(this);
229 mVideoHost
.ActorDestroyed(); // same as DoneWithAPI
230 MaybeDisconnect(aWhy
== AbnormalShutdown
);
233 mozilla::ipc::IPCResult
GMPVideoEncoderParent::RecvEncoded(
234 const GMPVideoEncodedFrameData
& aEncodedFrame
,
235 nsTArray
<uint8_t>&& aCodecSpecificInfo
) {
237 auto f
= new GMPVideoEncodedFrameImpl(aEncodedFrame
, &mVideoHost
);
238 mCallback
->Encoded(f
, aCodecSpecificInfo
);
244 mozilla::ipc::IPCResult
GMPVideoEncoderParent::RecvError(const GMPErr
& aError
) {
246 mCallback
->Error(aError
);
252 mozilla::ipc::IPCResult
GMPVideoEncoderParent::RecvShutdown() {
257 mozilla::ipc::IPCResult
GMPVideoEncoderParent::RecvParentShmemForPool(
258 Shmem
&& aFrameBuffer
) {
259 if (aFrameBuffer
.IsWritable()) {
260 // This test may be paranoia now that we don't shut down the VideoHost
261 // in ::Shutdown, but doesn't hurt
262 if (mVideoHost
.SharedMemMgr()) {
263 mVideoHost
.SharedMemMgr()->MgrDeallocShmem(GMPSharedMem::kGMPFrameData
,
267 "%s::%s: %p Called in shutdown, ignoring and freeing directly",
268 __CLASS__
, __FUNCTION__
, this);
269 DeallocShmem(aFrameBuffer
);
275 mozilla::ipc::IPCResult
GMPVideoEncoderParent::RecvNeedShmem(
276 const uint32_t& aEncodedBufferSize
, Shmem
* aMem
) {
279 // This test may be paranoia now that we don't shut down the VideoHost
280 // in ::Shutdown, but doesn't hurt
281 if (!mVideoHost
.SharedMemMgr() ||
282 !mVideoHost
.SharedMemMgr()->MgrAllocShmem(GMPSharedMem::kGMPEncodedData
,
283 aEncodedBufferSize
, &mem
)) {
285 "%s::%s: Failed to get a shared mem buffer for Child! size %u",
286 __CLASS__
, __FUNCTION__
, aEncodedBufferSize
);
287 return IPC_FAIL(this, "Failed to get a shared mem buffer for Child!");
294 mozilla::ipc::IPCResult
GMPVideoEncoderParent::Recv__delete__() {
296 // Ignore any return code. It is OK for this to fail without killing the
298 mPlugin
->VideoEncoderDestroyed(this);
305 } // namespace mozilla::gmp