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 #include "GMPContentChild.h"
8 #include <stdio.h>
9 #include "mozilla/Unused.h"
10 #include "GMPVideoEncodedFrameImpl.h"
11 #include "GMPVideoi420FrameImpl.h"
12 #include "runnable_utils.h"
13 
14 namespace mozilla::gmp {
15 
GMPVideoEncoderChild(GMPContentChild * aPlugin)16 GMPVideoEncoderChild::GMPVideoEncoderChild(GMPContentChild* aPlugin)
17     : GMPSharedMemManager(aPlugin),
18       mPlugin(aPlugin),
19       mVideoEncoder(nullptr),
20       mVideoHost(this),
21       mNeedShmemIntrCount(0),
22       mPendingEncodeComplete(false) {
23   MOZ_ASSERT(mPlugin);
24 }
25 
~GMPVideoEncoderChild()26 GMPVideoEncoderChild::~GMPVideoEncoderChild() {
27   MOZ_ASSERT(!mNeedShmemIntrCount);
28 }
29 
Init(GMPVideoEncoder * aEncoder)30 void GMPVideoEncoderChild::Init(GMPVideoEncoder* aEncoder) {
31   MOZ_ASSERT(aEncoder,
32              "Cannot initialize video encoder child without a video encoder!");
33   mVideoEncoder = aEncoder;
34 }
35 
Host()36 GMPVideoHostImpl& GMPVideoEncoderChild::Host() { return mVideoHost; }
37 
Encoded(GMPVideoEncodedFrame * aEncodedFrame,const uint8_t * aCodecSpecificInfo,uint32_t aCodecSpecificInfoLength)38 void GMPVideoEncoderChild::Encoded(GMPVideoEncodedFrame* aEncodedFrame,
39                                    const uint8_t* aCodecSpecificInfo,
40                                    uint32_t aCodecSpecificInfoLength) {
41   MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
42 
43   auto ef = static_cast<GMPVideoEncodedFrameImpl*>(aEncodedFrame);
44 
45   GMPVideoEncodedFrameData frameData;
46   ef->RelinquishFrameData(frameData);
47 
48   nsTArray<uint8_t> codecSpecific;
49   codecSpecific.AppendElements(aCodecSpecificInfo, aCodecSpecificInfoLength);
50   SendEncoded(frameData, codecSpecific);
51 
52   aEncodedFrame->Destroy();
53 }
54 
Error(GMPErr aError)55 void GMPVideoEncoderChild::Error(GMPErr aError) {
56   MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
57 
58   SendError(aError);
59 }
60 
RecvInitEncode(const GMPVideoCodec & aCodecSettings,nsTArray<uint8_t> && aCodecSpecific,const int32_t & aNumberOfCores,const uint32_t & aMaxPayloadSize)61 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvInitEncode(
62     const GMPVideoCodec& aCodecSettings, nsTArray<uint8_t>&& aCodecSpecific,
63     const int32_t& aNumberOfCores, const uint32_t& aMaxPayloadSize) {
64   if (!mVideoEncoder) {
65     return IPC_FAIL_NO_REASON(this);
66   }
67 
68   // Ignore any return code. It is OK for this to fail without killing the
69   // process.
70   mVideoEncoder->InitEncode(aCodecSettings, aCodecSpecific.Elements(),
71                             aCodecSpecific.Length(), this, aNumberOfCores,
72                             aMaxPayloadSize);
73 
74   return IPC_OK();
75 }
76 
RecvEncode(const GMPVideoi420FrameData & aInputFrame,nsTArray<uint8_t> && aCodecSpecificInfo,nsTArray<GMPVideoFrameType> && aFrameTypes)77 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvEncode(
78     const GMPVideoi420FrameData& aInputFrame,
79     nsTArray<uint8_t>&& aCodecSpecificInfo,
80     nsTArray<GMPVideoFrameType>&& aFrameTypes) {
81   if (!mVideoEncoder) {
82     return IPC_FAIL_NO_REASON(this);
83   }
84 
85   auto f = new GMPVideoi420FrameImpl(aInputFrame, &mVideoHost);
86 
87   // Ignore any return code. It is OK for this to fail without killing the
88   // process.
89   mVideoEncoder->Encode(f, aCodecSpecificInfo.Elements(),
90                         aCodecSpecificInfo.Length(), aFrameTypes.Elements(),
91                         aFrameTypes.Length());
92 
93   return IPC_OK();
94 }
95 
RecvChildShmemForPool(Shmem && aEncodedBuffer)96 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvChildShmemForPool(
97     Shmem&& aEncodedBuffer) {
98   if (aEncodedBuffer.IsWritable()) {
99     mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMem::kGMPEncodedData,
100                                                aEncodedBuffer);
101   }
102   return IPC_OK();
103 }
104 
RecvSetChannelParameters(const uint32_t & aPacketLoss,const uint32_t & aRTT)105 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvSetChannelParameters(
106     const uint32_t& aPacketLoss, const uint32_t& aRTT) {
107   if (!mVideoEncoder) {
108     return IPC_FAIL_NO_REASON(this);
109   }
110 
111   // Ignore any return code. It is OK for this to fail without killing the
112   // process.
113   mVideoEncoder->SetChannelParameters(aPacketLoss, aRTT);
114 
115   return IPC_OK();
116 }
117 
RecvSetRates(const uint32_t & aNewBitRate,const uint32_t & aFrameRate)118 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvSetRates(
119     const uint32_t& aNewBitRate, const uint32_t& aFrameRate) {
120   if (!mVideoEncoder) {
121     return IPC_FAIL_NO_REASON(this);
122   }
123 
124   // Ignore any return code. It is OK for this to fail without killing the
125   // process.
126   mVideoEncoder->SetRates(aNewBitRate, aFrameRate);
127 
128   return IPC_OK();
129 }
130 
RecvSetPeriodicKeyFrames(const bool & aEnable)131 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvSetPeriodicKeyFrames(
132     const bool& aEnable) {
133   if (!mVideoEncoder) {
134     return IPC_FAIL_NO_REASON(this);
135   }
136 
137   // Ignore any return code. It is OK for this to fail without killing the
138   // process.
139   mVideoEncoder->SetPeriodicKeyFrames(aEnable);
140 
141   return IPC_OK();
142 }
143 
RecvEncodingComplete()144 mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvEncodingComplete() {
145   MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
146 
147   if (mNeedShmemIntrCount) {
148     // There's a GMP blocked in Alloc() waiting for the CallNeedShem() to
149     // return a frame they can use. Don't call the GMP's EncodingComplete()
150     // now and don't delete the GMPVideoEncoderChild, defer processing the
151     // EncodingComplete() until once the Alloc() finishes.
152     mPendingEncodeComplete = true;
153     return IPC_OK();
154   }
155 
156   if (!mVideoEncoder) {
157     Unused << Send__delete__(this);
158     return IPC_FAIL_NO_REASON(this);
159   }
160 
161   // Ignore any return code. It is OK for this to fail without killing the
162   // process.
163   mVideoEncoder->EncodingComplete();
164 
165   mVideoHost.DoneWithAPI();
166 
167   mPlugin = nullptr;
168 
169   Unused << Send__delete__(this);
170 
171   return IPC_OK();
172 }
173 
Alloc(size_t aSize,Shmem::SharedMemory::SharedMemoryType aType,Shmem * aMem)174 bool GMPVideoEncoderChild::Alloc(size_t aSize,
175                                  Shmem::SharedMemory::SharedMemoryType aType,
176                                  Shmem* aMem) {
177   MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
178 
179   bool rv;
180 #ifndef SHMEM_ALLOC_IN_CHILD
181   ++mNeedShmemIntrCount;
182   rv = CallNeedShmem(aSize, aMem);
183   --mNeedShmemIntrCount;
184   if (mPendingEncodeComplete && mNeedShmemIntrCount == 0) {
185     mPendingEncodeComplete = false;
186     mPlugin->GMPMessageLoop()->PostTask(
187         NewRunnableMethod("gmp::GMPVideoEncoderChild::RecvEncodingComplete",
188                           this, &GMPVideoEncoderChild::RecvEncodingComplete));
189   }
190 #else
191 #  ifdef GMP_SAFE_SHMEM
192   rv = AllocShmem(aSize, aType, aMem);
193 #  else
194   rv = AllocUnsafeShmem(aSize, aType, aMem);
195 #  endif
196 #endif
197   return rv;
198 }
199 
Dealloc(Shmem && aMem)200 void GMPVideoEncoderChild::Dealloc(Shmem&& aMem) {
201 #ifndef SHMEM_ALLOC_IN_CHILD
202   SendParentShmemForPool(std::move(aMem));
203 #else
204   DeallocShmem(aMem);
205 #endif
206 }
207 
208 }  // namespace mozilla::gmp
209