Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/gmp/GMPVideoEncoderParent.cpp
Line
Count
Source (jump to first uncovered line)
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
0
#define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
31
0
#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
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
  mPluginId(aPlugin->GetPluginId())
59
0
{
60
0
  MOZ_ASSERT(mPlugin);
61
0
}
62
63
GMPVideoHostImpl&
64
GMPVideoEncoderParent::Host()
65
0
{
66
0
  return mVideoHost;
67
0
}
68
69
// Note: may be called via Terminated()
70
void
71
GMPVideoEncoderParent::Close()
72
0
{
73
0
  LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
74
0
  MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
75
0
  // Consumer is done with us; we can shut down.  No more callbacks should
76
0
  // be made to mCallback.  Note: do this before Shutdown()!
77
0
  mCallback = nullptr;
78
0
79
0
  // Let Shutdown mark us as dead so it knows if we had been alive
80
0
81
0
  // 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
GMPVideoEncoderParent::InitEncode(const GMPVideoCodec& aCodecSettings,
89
                                  const nsTArray<uint8_t>& aCodecSpecific,
90
                                  GMPVideoEncoderCallbackProxy* aCallback,
91
                                  int32_t aNumberOfCores,
92
                                  uint32_t aMaxPayloadSize)
93
0
{
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
0
  }
99
0
100
0
  MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
101
0
  MOZ_ASSERT(!mCallback);
102
0
103
0
  if (!aCallback) {
104
0
    return GMPGenericErr;
105
0
  }
106
0
  mCallback = aCallback;
107
0
108
0
  if (!SendInitEncode(aCodecSettings, aCodecSpecific, aNumberOfCores, aMaxPayloadSize)) {
109
0
    return GMPGenericErr;
110
0
  }
111
0
  mIsOpen = true;
112
0
113
0
  // Async IPC, we don't have access to a return value.
114
0
  return GMPNoErr;
115
0
}
116
117
GMPErr
118
GMPVideoEncoderParent::Encode(GMPUniquePtr<GMPVideoi420Frame> aInputFrame,
119
                              const nsTArray<uint8_t>& aCodecSpecificInfo,
120
                              const nsTArray<GMPVideoFrameType>& aFrameTypes)
121
0
{
122
0
  if (!mIsOpen) {
123
0
    NS_WARNING("Trying to use an dead GMP video encoder");
124
0
    return GMPGenericErr;
125
0
  }
126
0
127
0
  MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
128
0
129
0
  GMPUniquePtr<GMPVideoi420FrameImpl> inputFrameImpl(
130
0
    static_cast<GMPVideoi420FrameImpl*>(aInputFrame.release()));
131
0
132
0
  // Very rough kill-switch if the plugin stops processing.  If it's merely
133
0
  // hung and continues, we'll come back to life eventually.
134
0
  // 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
0
  }
139
0
140
0
  GMPVideoi420FrameData frameData;
141
0
  inputFrameImpl->InitFrameData(frameData);
142
0
143
0
  if (!SendEncode(frameData,
144
0
                  aCodecSpecificInfo,
145
0
                  aFrameTypes)) {
146
0
    return GMPGenericErr;
147
0
  }
148
0
149
0
  // Async IPC, we don't have access to a return value.
150
0
  return GMPNoErr;
151
0
}
152
153
GMPErr
154
GMPVideoEncoderParent::SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT)
155
0
{
156
0
  if (!mIsOpen) {
157
0
    NS_WARNING("Trying to use an invalid GMP video encoder!");
158
0
    return GMPGenericErr;
159
0
  }
160
0
161
0
  MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
162
0
163
0
  if (!SendSetChannelParameters(aPacketLoss, aRTT)) {
164
0
    return GMPGenericErr;
165
0
  }
166
0
167
0
  // Async IPC, we don't have access to a return value.
168
0
  return GMPNoErr;
169
0
}
170
171
GMPErr
172
GMPVideoEncoderParent::SetRates(uint32_t aNewBitRate, uint32_t aFrameRate)
173
0
{
174
0
  if (!mIsOpen) {
175
0
    NS_WARNING("Trying to use an dead GMP video decoder");
176
0
    return GMPGenericErr;
177
0
  }
178
0
179
0
  MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
180
0
181
0
  if (!SendSetRates(aNewBitRate, aFrameRate)) {
182
0
    return GMPGenericErr;
183
0
  }
184
0
185
0
  // Async IPC, we don't have access to a return value.
186
0
  return GMPNoErr;
187
0
}
188
189
GMPErr
190
GMPVideoEncoderParent::SetPeriodicKeyFrames(bool aEnable)
191
0
{
192
0
  if (!mIsOpen) {
193
0
    NS_WARNING("Trying to use an invalid GMP video encoder!");
194
0
    return GMPGenericErr;
195
0
  }
196
0
197
0
  MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
198
0
199
0
  if (!SendSetPeriodicKeyFrames(aEnable)) {
200
0
    return GMPGenericErr;
201
0
  }
202
0
203
0
  // Async IPC, we don't have access to a return value.
204
0
  return GMPNoErr;
205
0
}
206
207
// Note: Consider keeping ActorDestroy sync'd up when making changes here.
208
void
209
GMPVideoEncoderParent::Shutdown()
210
0
{
211
0
  LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
212
0
  MOZ_ASSERT(mPlugin->GMPEventTarget()->IsOnCurrentThread());
213
0
214
0
  if (mShuttingDown) {
215
0
    return;
216
0
  }
217
0
  mShuttingDown = true;
218
0
219
0
  // Notify client we're gone!  Won't occur after Close()
220
0
  if (mCallback) {
221
0
    mCallback->Terminated();
222
0
    mCallback = nullptr;
223
0
  }
224
0
225
0
  mIsOpen = false;
226
0
  if (!mActorDestroyed) {
227
0
    Unused << SendEncodingComplete();
228
0
  }
229
0
}
230
231
// Note: Keep this sync'd up with Shutdown
232
void
233
GMPVideoEncoderParent::ActorDestroy(ActorDestroyReason aWhy)
234
0
{
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
0
    // May call Close() (and Shutdown()) immediately or with a delay
240
0
    mCallback->Terminated();
241
0
    mCallback = nullptr;
242
0
  }
243
0
  if (mPlugin) {
244
0
    // 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
0
  }
248
0
  mVideoHost.ActorDestroyed(); // same as DoneWithAPI
249
0
  MaybeDisconnect(aWhy == AbnormalShutdown);
250
0
}
251
252
mozilla::ipc::IPCResult
253
GMPVideoEncoderParent::RecvEncoded(const GMPVideoEncodedFrameData& aEncodedFrame,
254
                                   InfallibleTArray<uint8_t>&& aCodecSpecificInfo)
255
0
{
256
0
  if (!mCallback) {
257
0
    return IPC_FAIL_NO_REASON(this);
258
0
  }
259
0
260
0
  auto f = new GMPVideoEncodedFrameImpl(aEncodedFrame, &mVideoHost);
261
0
  // Ignore any return code. It is OK for this to fail without killing the process.
262
0
  // 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
0
}
267
268
mozilla::ipc::IPCResult
269
GMPVideoEncoderParent::RecvError(const GMPErr& aError)
270
0
{
271
0
  if (!mCallback) {
272
0
    return IPC_FAIL_NO_REASON(this);
273
0
  }
274
0
275
0
  // Ignore any return code. It is OK for this to fail without killing the process.
276
0
  mCallback->Error(aError);
277
0
278
0
  return IPC_OK();
279
0
}
280
281
mozilla::ipc::IPCResult
282
GMPVideoEncoderParent::RecvShutdown()
283
0
{
284
0
  Shutdown();
285
0
  return IPC_OK();
286
0
}
287
288
mozilla::ipc::IPCResult
289
GMPVideoEncoderParent::RecvParentShmemForPool(Shmem&& aFrameBuffer)
290
0
{
291
0
  if (aFrameBuffer.IsWritable()) {
292
0
    // This test may be paranoia now that we don't shut down the VideoHost
293
0
    // in ::Shutdown, but doesn't hurt
294
0
    if (mVideoHost.SharedMemMgr()) {
295
0
      mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMem::kGMPFrameData,
296
0
                                                 aFrameBuffer);
297
0
    } else {
298
0
      LOGD(("%s::%s: %p Called in shutdown, ignoring and freeing directly", __CLASS__, __FUNCTION__, this));
299
0
      DeallocShmem(aFrameBuffer);
300
0
    }
301
0
  }
302
0
  return IPC_OK();
303
0
}
304
305
mozilla::ipc::IPCResult
306
GMPVideoEncoderParent::AnswerNeedShmem(const uint32_t& aEncodedBufferSize,
307
                                       Shmem* aMem)
308
0
{
309
0
  ipc::Shmem mem;
310
0
311
0
  // This test may be paranoia now that we don't shut down the VideoHost
312
0
  // 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
0
  {
318
0
    LOG(LogLevel::Error, ("%s::%s: Failed to get a shared mem buffer for Child! size %u",
319
0
                       __CLASS__, __FUNCTION__, aEncodedBufferSize));
320
0
    return IPC_FAIL_NO_REASON(this);
321
0
  }
322
0
  *aMem = mem;
323
0
  mem = ipc::Shmem();
324
0
  return IPC_OK();
325
0
}
326
327
mozilla::ipc::IPCResult
328
GMPVideoEncoderParent::Recv__delete__()
329
0
{
330
0
  if (mPlugin) {
331
0
    // 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
0
  }
335
0
336
0
  return IPC_OK();
337
0
}
338
339
} // namespace gmp
340
} // namespace mozilla