Line data Source code
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 "GMPVideoEncoderParent.h"
7 : #include "mozilla/Logging.h"
8 : #include "GMPVideoi420FrameImpl.h"
9 : #include "GMPVideoEncodedFrameImpl.h"
10 : #include "mozilla/Unused.h"
11 : #include "GMPMessageUtils.h"
12 : #include "nsAutoRef.h"
13 : #include "GMPContentParent.h"
14 : #include "mozilla/gmp/GMPTypes.h"
15 : #include "nsThread.h"
16 : #include "nsThreadUtils.h"
17 : #include "runnable_utils.h"
18 : #include "GMPUtils.h"
19 : #include "mozilla/SystemGroup.h"
20 : #include "GMPCrashHelper.h"
21 :
22 : namespace mozilla {
23 :
24 : #ifdef LOG
25 : #undef LOG
26 : #endif
27 :
28 : extern LogModule* GetGMPLog();
29 :
30 : #define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
31 : #define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
32 :
33 : #ifdef __CLASS__
34 : #undef __CLASS__
35 : #endif
36 : #define __CLASS__ "GMPVideoEncoderParent"
37 :
38 : namespace gmp {
39 :
40 : // States:
41 : // Initial: mIsOpen == false
42 : // on InitDecode success -> Open
43 : // on Shutdown -> Dead
44 : // Open: mIsOpen == true
45 : // on Close -> Dead
46 : // on ActorDestroy -> Dead
47 : // on Shutdown -> Dead
48 : // Dead: mIsOpen == false
49 :
50 0 : GMPVideoEncoderParent::GMPVideoEncoderParent(GMPContentParent *aPlugin)
51 : : GMPSharedMemManager(aPlugin),
52 : mIsOpen(false),
53 : mShuttingDown(false),
54 : mActorDestroyed(false),
55 : mPlugin(aPlugin),
56 : mCallback(nullptr),
57 : mVideoHost(this),
58 0 : mPluginId(aPlugin->GetPluginId())
59 : {
60 0 : MOZ_ASSERT(mPlugin);
61 0 : }
62 :
63 : GMPVideoHostImpl&
64 0 : GMPVideoEncoderParent::Host()
65 : {
66 0 : return mVideoHost;
67 : }
68 :
69 : // Note: may be called via Terminated()
70 : void
71 0 : GMPVideoEncoderParent::Close()
72 : {
73 0 : LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
74 0 : MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
75 : // Consumer is done with us; we can shut down. No more callbacks should
76 : // be made to mCallback. Note: do this before Shutdown()!
77 0 : mCallback = nullptr;
78 :
79 : // Let Shutdown mark us as dead so it knows if we had been alive
80 :
81 : // In case this is the last reference
82 0 : RefPtr<GMPVideoEncoderParent> kungfudeathgrip(this);
83 0 : Release();
84 0 : Shutdown();
85 0 : }
86 :
87 : GMPErr
88 0 : GMPVideoEncoderParent::InitEncode(const GMPVideoCodec& aCodecSettings,
89 : const nsTArray<uint8_t>& aCodecSpecific,
90 : GMPVideoEncoderCallbackProxy* aCallback,
91 : int32_t aNumberOfCores,
92 : uint32_t aMaxPayloadSize)
93 : {
94 0 : LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
95 0 : if (mIsOpen) {
96 0 : NS_WARNING("Trying to re-init an in-use GMP video encoder!");
97 0 : return GMPGenericErr;;
98 : }
99 :
100 0 : MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
101 0 : MOZ_ASSERT(!mCallback);
102 :
103 0 : if (!aCallback) {
104 0 : return GMPGenericErr;
105 : }
106 0 : mCallback = aCallback;
107 :
108 0 : if (!SendInitEncode(aCodecSettings, aCodecSpecific, aNumberOfCores, aMaxPayloadSize)) {
109 0 : return GMPGenericErr;
110 : }
111 0 : mIsOpen = true;
112 :
113 : // Async IPC, we don't have access to a return value.
114 0 : return GMPNoErr;
115 : }
116 :
117 : GMPErr
118 0 : GMPVideoEncoderParent::Encode(GMPUniquePtr<GMPVideoi420Frame> aInputFrame,
119 : const nsTArray<uint8_t>& aCodecSpecificInfo,
120 : const nsTArray<GMPVideoFrameType>& aFrameTypes)
121 : {
122 0 : if (!mIsOpen) {
123 0 : NS_WARNING("Trying to use an dead GMP video encoder");
124 0 : return GMPGenericErr;
125 : }
126 :
127 0 : MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
128 :
129 : GMPUniquePtr<GMPVideoi420FrameImpl> inputFrameImpl(
130 0 : static_cast<GMPVideoi420FrameImpl*>(aInputFrame.release()));
131 :
132 : // Very rough kill-switch if the plugin stops processing. If it's merely
133 : // hung and continues, we'll come back to life eventually.
134 : // 3* is because we're using 3 buffers per frame for i420 data for now.
135 0 : if ((NumInUse(GMPSharedMem::kGMPFrameData) > 3*GMPSharedMem::kGMPBufLimit) ||
136 0 : (NumInUse(GMPSharedMem::kGMPEncodedData) > GMPSharedMem::kGMPBufLimit)) {
137 0 : return GMPGenericErr;
138 : }
139 :
140 0 : GMPVideoi420FrameData frameData;
141 0 : inputFrameImpl->InitFrameData(frameData);
142 :
143 0 : if (!SendEncode(frameData,
144 : aCodecSpecificInfo,
145 : aFrameTypes)) {
146 0 : return GMPGenericErr;
147 : }
148 :
149 : // Async IPC, we don't have access to a return value.
150 0 : return GMPNoErr;
151 : }
152 :
153 : GMPErr
154 0 : GMPVideoEncoderParent::SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT)
155 : {
156 0 : if (!mIsOpen) {
157 0 : NS_WARNING("Trying to use an invalid GMP video encoder!");
158 0 : return GMPGenericErr;
159 : }
160 :
161 0 : MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
162 :
163 0 : if (!SendSetChannelParameters(aPacketLoss, aRTT)) {
164 0 : return GMPGenericErr;
165 : }
166 :
167 : // Async IPC, we don't have access to a return value.
168 0 : return GMPNoErr;
169 : }
170 :
171 : GMPErr
172 0 : GMPVideoEncoderParent::SetRates(uint32_t aNewBitRate, uint32_t aFrameRate)
173 : {
174 0 : if (!mIsOpen) {
175 0 : NS_WARNING("Trying to use an dead GMP video decoder");
176 0 : return GMPGenericErr;
177 : }
178 :
179 0 : MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
180 :
181 0 : if (!SendSetRates(aNewBitRate, aFrameRate)) {
182 0 : return GMPGenericErr;
183 : }
184 :
185 : // Async IPC, we don't have access to a return value.
186 0 : return GMPNoErr;
187 : }
188 :
189 : GMPErr
190 0 : GMPVideoEncoderParent::SetPeriodicKeyFrames(bool aEnable)
191 : {
192 0 : if (!mIsOpen) {
193 0 : NS_WARNING("Trying to use an invalid GMP video encoder!");
194 0 : return GMPGenericErr;
195 : }
196 :
197 0 : MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
198 :
199 0 : if (!SendSetPeriodicKeyFrames(aEnable)) {
200 0 : return GMPGenericErr;
201 : }
202 :
203 : // Async IPC, we don't have access to a return value.
204 0 : return GMPNoErr;
205 : }
206 :
207 : // Note: Consider keeping ActorDestroy sync'd up when making changes here.
208 : void
209 0 : GMPVideoEncoderParent::Shutdown()
210 : {
211 0 : LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
212 0 : MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
213 :
214 0 : if (mShuttingDown) {
215 0 : return;
216 : }
217 0 : mShuttingDown = true;
218 :
219 : // Notify client we're gone! Won't occur after Close()
220 0 : if (mCallback) {
221 0 : mCallback->Terminated();
222 0 : mCallback = nullptr;
223 : }
224 :
225 0 : mIsOpen = false;
226 0 : if (!mActorDestroyed) {
227 0 : Unused << SendEncodingComplete();
228 : }
229 : }
230 :
231 : // Note: Keep this sync'd up with Shutdown
232 : void
233 0 : GMPVideoEncoderParent::ActorDestroy(ActorDestroyReason aWhy)
234 : {
235 0 : LOGD(("%s::%s: %p (%d)", __CLASS__, __FUNCTION__, this, (int) aWhy));
236 0 : mIsOpen = false;
237 0 : mActorDestroyed = true;
238 0 : if (mCallback) {
239 : // May call Close() (and Shutdown()) immediately or with a delay
240 0 : mCallback->Terminated();
241 0 : mCallback = nullptr;
242 : }
243 0 : if (mPlugin) {
244 : // Ignore any return code. It is OK for this to fail without killing the process.
245 0 : mPlugin->VideoEncoderDestroyed(this);
246 0 : mPlugin = nullptr;
247 : }
248 0 : mVideoHost.ActorDestroyed(); // same as DoneWithAPI
249 0 : MaybeDisconnect(aWhy == AbnormalShutdown);
250 0 : }
251 :
252 : mozilla::ipc::IPCResult
253 0 : GMPVideoEncoderParent::RecvEncoded(const GMPVideoEncodedFrameData& aEncodedFrame,
254 : InfallibleTArray<uint8_t>&& aCodecSpecificInfo)
255 : {
256 0 : if (!mCallback) {
257 0 : return IPC_FAIL_NO_REASON(this);
258 : }
259 :
260 0 : auto f = new GMPVideoEncodedFrameImpl(aEncodedFrame, &mVideoHost);
261 : // Ignore any return code. It is OK for this to fail without killing the process.
262 : // This can be called on any thread (or more than one)
263 0 : mCallback->Encoded(f, aCodecSpecificInfo);
264 0 : f->Destroy();
265 0 : return IPC_OK();
266 : }
267 :
268 : mozilla::ipc::IPCResult
269 0 : GMPVideoEncoderParent::RecvError(const GMPErr& aError)
270 : {
271 0 : if (!mCallback) {
272 0 : return IPC_FAIL_NO_REASON(this);
273 : }
274 :
275 : // Ignore any return code. It is OK for this to fail without killing the process.
276 0 : mCallback->Error(aError);
277 :
278 0 : return IPC_OK();
279 : }
280 :
281 : mozilla::ipc::IPCResult
282 0 : GMPVideoEncoderParent::RecvShutdown()
283 : {
284 0 : Shutdown();
285 0 : return IPC_OK();
286 : }
287 :
288 : mozilla::ipc::IPCResult
289 0 : GMPVideoEncoderParent::RecvParentShmemForPool(Shmem&& aFrameBuffer)
290 : {
291 0 : if (aFrameBuffer.IsWritable()) {
292 : // This test may be paranoia now that we don't shut down the VideoHost
293 : // in ::Shutdown, but doesn't hurt
294 0 : if (mVideoHost.SharedMemMgr()) {
295 0 : mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMem::kGMPFrameData,
296 0 : aFrameBuffer);
297 : } else {
298 0 : LOGD(("%s::%s: %p Called in shutdown, ignoring and freeing directly", __CLASS__, __FUNCTION__, this));
299 0 : DeallocShmem(aFrameBuffer);
300 : }
301 : }
302 0 : return IPC_OK();
303 : }
304 :
305 : mozilla::ipc::IPCResult
306 0 : GMPVideoEncoderParent::AnswerNeedShmem(const uint32_t& aEncodedBufferSize,
307 : Shmem* aMem)
308 : {
309 0 : ipc::Shmem mem;
310 :
311 : // This test may be paranoia now that we don't shut down the VideoHost
312 : // in ::Shutdown, but doesn't hurt
313 0 : if (!mVideoHost.SharedMemMgr() ||
314 0 : !mVideoHost.SharedMemMgr()->MgrAllocShmem(GMPSharedMem::kGMPEncodedData,
315 0 : aEncodedBufferSize,
316 0 : ipc::SharedMemory::TYPE_BASIC, &mem))
317 : {
318 0 : LOG(LogLevel::Error, ("%s::%s: Failed to get a shared mem buffer for Child! size %u",
319 : __CLASS__, __FUNCTION__, aEncodedBufferSize));
320 0 : return IPC_FAIL_NO_REASON(this);
321 : }
322 0 : *aMem = mem;
323 0 : mem = ipc::Shmem();
324 0 : return IPC_OK();
325 : }
326 :
327 : mozilla::ipc::IPCResult
328 0 : GMPVideoEncoderParent::Recv__delete__()
329 : {
330 0 : if (mPlugin) {
331 : // Ignore any return code. It is OK for this to fail without killing the process.
332 0 : mPlugin->VideoEncoderDestroyed(this);
333 0 : mPlugin = nullptr;
334 : }
335 :
336 0 : return IPC_OK();
337 : }
338 :
339 : } // namespace gmp
340 : } // namespace mozilla
|