Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/gmp/ChromiumCDMParent.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 "ChromiumCDMParent.h"
7
8
#include "ChromiumCDMCallback.h"
9
#include "ChromiumCDMCallbackProxy.h"
10
#include "ChromiumCDMProxy.h"
11
#include "content_decryption_module.h"
12
#include "GMPContentChild.h"
13
#include "GMPContentParent.h"
14
#include "GMPLog.h"
15
#include "GMPService.h"
16
#include "GMPUtils.h"
17
#include "mozilla/dom/MediaKeyMessageEventBinding.h"
18
#include "mozilla/gmp/GMPTypes.h"
19
#include "mozilla/StaticPrefs.h"
20
#include "mozilla/Unused.h"
21
#include "AnnexB.h"
22
#include "H264.h"
23
24
#define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead
25
26
namespace mozilla {
27
namespace gmp {
28
29
using namespace eme;
30
31
ChromiumCDMParent::ChromiumCDMParent(GMPContentParent* aContentParent,
32
                                     uint32_t aPluginId)
33
  : mPluginId(aPluginId)
34
  , mContentParent(aContentParent)
35
  , mVideoShmemLimit(StaticPrefs::MediaEmeChromiumApiVideoShmems())
36
0
{
37
0
  GMP_LOG(
38
0
    "ChromiumCDMParent::ChromiumCDMParent(this=%p, contentParent=%p, id=%u)",
39
0
    this,
40
0
    aContentParent,
41
0
    aPluginId);
42
0
}
43
44
bool
45
ChromiumCDMParent::Init(ChromiumCDMCallback* aCDMCallback,
46
                        bool aAllowDistinctiveIdentifier,
47
                        bool aAllowPersistentState,
48
                        nsIEventTarget* aMainThread,
49
                        nsCString& aOutFailureReason)
50
0
{
51
0
  GMP_LOG("ChromiumCDMParent::Init(this=%p) shutdown=%d abormalShutdown=%d "
52
0
          "actorDestroyed=%d",
53
0
          this,
54
0
          mIsShutdown,
55
0
          mAbnormalShutdown,
56
0
          mActorDestroyed);
57
0
  if (!aCDMCallback || !aMainThread) {
58
0
    aOutFailureReason = nsPrintfCString("ChromiumCDMParent::Init() failed "
59
0
                                        "nullCallback=%d nullMainThread=%d",
60
0
                                        !aCDMCallback,
61
0
                                        !aMainThread);
62
0
    GMP_LOG("ChromiumCDMParent::Init(this=%p) failure since aCDMCallback(%p) or"
63
0
            " aMainThread(%p) is nullptr", this, aCDMCallback, aMainThread);
64
0
    return false;
65
0
  }
66
0
  mCDMCallback = aCDMCallback;
67
0
  mMainThread = aMainThread;
68
0
69
0
  if (SendInit(aAllowDistinctiveIdentifier,
70
0
               aAllowPersistentState)) {
71
0
    return true;
72
0
  }
73
0
74
0
  RefPtr<gmp::GeckoMediaPluginService> service =
75
0
    gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
76
0
  bool xpcomWillShutdown = service && service->XPCOMWillShutdownReceived();
77
0
  aOutFailureReason = nsPrintfCString(
78
0
    "ChromiumCDMParent::Init() failed "
79
0
    "shutdown=%d cdmCrash=%d actorDestroyed=%d browserShutdown=%d",
80
0
    mIsShutdown,
81
0
    mAbnormalShutdown,
82
0
    mActorDestroyed,
83
0
    xpcomWillShutdown);
84
0
  return false;
85
0
}
86
87
void
88
ChromiumCDMParent::CreateSession(uint32_t aCreateSessionToken,
89
                                 uint32_t aSessionType,
90
                                 uint32_t aInitDataType,
91
                                 uint32_t aPromiseId,
92
                                 const nsTArray<uint8_t>& aInitData)
93
0
{
94
0
  GMP_LOG("ChromiumCDMParent::CreateSession(this=%p)", this);
95
0
  if (mIsShutdown) {
96
0
    RejectPromise(aPromiseId,
97
0
                  NS_ERROR_DOM_INVALID_STATE_ERR,
98
0
                  NS_LITERAL_CSTRING("CDM is shutdown."));
99
0
    return;
100
0
  }
101
0
  if (!SendCreateSessionAndGenerateRequest(
102
0
        aPromiseId, aSessionType, aInitDataType, aInitData)) {
103
0
    RejectPromise(
104
0
      aPromiseId,
105
0
      NS_ERROR_DOM_INVALID_STATE_ERR,
106
0
      NS_LITERAL_CSTRING("Failed to send generateRequest to CDM process."));
107
0
    return;
108
0
  }
109
0
  mPromiseToCreateSessionToken.Put(aPromiseId, aCreateSessionToken);
110
0
}
111
112
void
113
ChromiumCDMParent::LoadSession(uint32_t aPromiseId,
114
                               uint32_t aSessionType,
115
                               nsString aSessionId)
116
0
{
117
0
  GMP_LOG("ChromiumCDMParent::LoadSession(this=%p, pid=%u, type=%u, sid=%s)",
118
0
          this,
119
0
          aPromiseId,
120
0
          aSessionType,
121
0
          NS_ConvertUTF16toUTF8(aSessionId).get());
122
0
  if (mIsShutdown) {
123
0
    RejectPromise(aPromiseId,
124
0
                  NS_ERROR_DOM_INVALID_STATE_ERR,
125
0
                  NS_LITERAL_CSTRING("CDM is shutdown."));
126
0
    return;
127
0
  }
128
0
  if (!SendLoadSession(
129
0
        aPromiseId, aSessionType, NS_ConvertUTF16toUTF8(aSessionId))) {
130
0
    RejectPromise(
131
0
      aPromiseId,
132
0
      NS_ERROR_DOM_INVALID_STATE_ERR,
133
0
      NS_LITERAL_CSTRING("Failed to send loadSession to CDM process."));
134
0
    return;
135
0
  }
136
0
}
137
138
void
139
ChromiumCDMParent::SetServerCertificate(uint32_t aPromiseId,
140
                                        const nsTArray<uint8_t>& aCert)
141
0
{
142
0
  GMP_LOG("ChromiumCDMParent::SetServerCertificate(this=%p)", this);
143
0
  if (mIsShutdown) {
144
0
    RejectPromise(aPromiseId,
145
0
                  NS_ERROR_DOM_INVALID_STATE_ERR,
146
0
                  NS_LITERAL_CSTRING("CDM is shutdown."));
147
0
    return;
148
0
  }
149
0
  if (!SendSetServerCertificate(aPromiseId, aCert)) {
150
0
    RejectPromise(
151
0
      aPromiseId,
152
0
      NS_ERROR_DOM_INVALID_STATE_ERR,
153
0
      NS_LITERAL_CSTRING("Failed to send setServerCertificate to CDM process"));
154
0
  }
155
0
}
156
157
void
158
ChromiumCDMParent::UpdateSession(const nsCString& aSessionId,
159
                                 uint32_t aPromiseId,
160
                                 const nsTArray<uint8_t>& aResponse)
161
0
{
162
0
  GMP_LOG("ChromiumCDMParent::UpdateSession(this=%p)", this);
163
0
  if (mIsShutdown) {
164
0
    RejectPromise(aPromiseId,
165
0
                  NS_ERROR_DOM_INVALID_STATE_ERR,
166
0
                  NS_LITERAL_CSTRING("CDM is shutdown."));
167
0
    return;
168
0
  }
169
0
  if (!SendUpdateSession(aPromiseId, aSessionId, aResponse)) {
170
0
    RejectPromise(
171
0
      aPromiseId,
172
0
      NS_ERROR_DOM_INVALID_STATE_ERR,
173
0
      NS_LITERAL_CSTRING("Failed to send updateSession to CDM process"));
174
0
  }
175
0
}
176
177
void
178
ChromiumCDMParent::CloseSession(const nsCString& aSessionId,
179
                                uint32_t aPromiseId)
180
0
{
181
0
  GMP_LOG("ChromiumCDMParent::CloseSession(this=%p)", this);
182
0
  if (mIsShutdown) {
183
0
    RejectPromise(aPromiseId,
184
0
                  NS_ERROR_DOM_INVALID_STATE_ERR,
185
0
                  NS_LITERAL_CSTRING("CDM is shutdown."));
186
0
    return;
187
0
  }
188
0
  if (!SendCloseSession(aPromiseId, aSessionId)) {
189
0
    RejectPromise(
190
0
      aPromiseId,
191
0
      NS_ERROR_DOM_INVALID_STATE_ERR,
192
0
      NS_LITERAL_CSTRING("Failed to send closeSession to CDM process"));
193
0
  }
194
0
}
195
196
void
197
ChromiumCDMParent::RemoveSession(const nsCString& aSessionId,
198
                                 uint32_t aPromiseId)
199
0
{
200
0
  GMP_LOG("ChromiumCDMParent::RemoveSession(this=%p)", this);
201
0
  if (mIsShutdown) {
202
0
    RejectPromise(aPromiseId,
203
0
                  NS_ERROR_DOM_INVALID_STATE_ERR,
204
0
                  NS_LITERAL_CSTRING("CDM is shutdown."));
205
0
    return;
206
0
  }
207
0
  if (!SendRemoveSession(aPromiseId, aSessionId)) {
208
0
    RejectPromise(
209
0
      aPromiseId,
210
0
      NS_ERROR_DOM_INVALID_STATE_ERR,
211
0
      NS_LITERAL_CSTRING("Failed to send removeSession to CDM process"));
212
0
  }
213
0
}
214
215
void
216
ChromiumCDMParent::GetStatusForPolicy(uint32_t aPromiseId,
217
                                      const nsCString& aMinHdcpVersion)
218
0
{
219
0
  GMP_LOG("ChromiumCDMParent::GetStatusForPolicy(this=%p)", this);
220
0
  if (mIsShutdown) {
221
0
    RejectPromise(aPromiseId,
222
0
                  NS_ERROR_DOM_INVALID_STATE_ERR,
223
0
                  NS_LITERAL_CSTRING("CDM is shutdown."));
224
0
    return;
225
0
  }
226
0
  if (!SendGetStatusForPolicy(aPromiseId, aMinHdcpVersion)) {
227
0
    RejectPromise(
228
0
      aPromiseId,
229
0
      NS_ERROR_DOM_INVALID_STATE_ERR,
230
0
      NS_LITERAL_CSTRING("Failed to send getStatusForPolicy to CDM process"));
231
0
  }
232
0
}
233
234
bool
235
ChromiumCDMParent::InitCDMInputBuffer(gmp::CDMInputBuffer& aBuffer,
236
                                      MediaRawData* aSample)
237
0
{
238
0
  const CryptoSample& crypto = aSample->mCrypto;
239
0
  if (crypto.mEncryptedSizes.Length() != crypto.mPlainSizes.Length()) {
240
0
    GMP_LOG("InitCDMInputBuffer clear/cipher subsamples don't match");
241
0
    return false;
242
0
  }
243
0
244
0
  Shmem shmem;
245
0
  if (!AllocShmem(aSample->Size(), Shmem::SharedMemory::TYPE_BASIC, &shmem)) {
246
0
    return false;
247
0
  }
248
0
  memcpy(shmem.get<uint8_t>(), aSample->Data(), aSample->Size());
249
0
250
0
  aBuffer = gmp::CDMInputBuffer(shmem,
251
0
                                crypto.mKeyId,
252
0
                                crypto.mIV,
253
0
                                aSample->mTime.ToMicroseconds(),
254
0
                                aSample->mDuration.ToMicroseconds(),
255
0
                                crypto.mPlainSizes,
256
0
                                crypto.mEncryptedSizes,
257
0
                                crypto.mValid
258
0
                                  ? GMPEncryptionScheme::kGMPEncryptionCenc
259
0
                                  : GMPEncryptionScheme::kGMPEncryptionNone);
260
0
  MOZ_ASSERT(
261
0
    aBuffer.mEncryptionScheme() == GMPEncryptionScheme::kGMPEncryptionNone ||
262
0
      aBuffer.mEncryptionScheme() == GMPEncryptionScheme::kGMPEncryptionCenc,
263
0
    "aBuffer should use either no encryption or cenc, other kinds are not yet "
264
0
    "supported");
265
0
  return true;
266
0
}
267
268
bool
269
ChromiumCDMParent::SendBufferToCDM(uint32_t aSizeInBytes)
270
0
{
271
0
  GMP_LOG("ChromiumCDMParent::SendBufferToCDM() size=%" PRIu32, aSizeInBytes);
272
0
  Shmem shmem;
273
0
  if (!AllocShmem(aSizeInBytes, Shmem::SharedMemory::TYPE_BASIC, &shmem)) {
274
0
    return false;
275
0
  }
276
0
  if (!SendGiveBuffer(shmem)) {
277
0
    DeallocShmem(shmem);
278
0
    return false;
279
0
  }
280
0
  return true;
281
0
}
282
283
RefPtr<DecryptPromise>
284
ChromiumCDMParent::Decrypt(MediaRawData* aSample)
285
0
{
286
0
  if (mIsShutdown) {
287
0
    return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, aSample),
288
0
                                           __func__);
289
0
  }
290
0
  CDMInputBuffer buffer;
291
0
  if (!InitCDMInputBuffer(buffer, aSample)) {
292
0
    return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, aSample),
293
0
                                           __func__);
294
0
  }
295
0
  // Send a buffer to the CDM to store the output. The CDM will either fill
296
0
  // it with the decrypted sample and return it, or deallocate it on failure.
297
0
  if (!SendBufferToCDM(aSample->Size())) {
298
0
    DeallocShmem(buffer.mData());
299
0
    return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, aSample),
300
0
                                           __func__);
301
0
  }
302
0
303
0
  RefPtr<DecryptJob> job = new DecryptJob(aSample);
304
0
  if (!SendDecrypt(job->mId, buffer)) {
305
0
    GMP_LOG(
306
0
      "ChromiumCDMParent::Decrypt(this=%p) failed to send decrypt message",
307
0
      this);
308
0
    DeallocShmem(buffer.mData());
309
0
    return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, aSample),
310
0
                                           __func__);
311
0
  }
312
0
  RefPtr<DecryptPromise> promise = job->Ensure();
313
0
  mDecrypts.AppendElement(job);
314
0
  return promise;
315
0
}
316
317
ipc::IPCResult
318
ChromiumCDMParent::Recv__delete__()
319
0
{
320
0
  MOZ_ASSERT(mIsShutdown);
321
0
  GMP_LOG("ChromiumCDMParent::Recv__delete__(this=%p)", this);
322
0
  if (mContentParent) {
323
0
    mContentParent->ChromiumCDMDestroyed(this);
324
0
    mContentParent = nullptr;
325
0
  }
326
0
  return IPC_OK();
327
0
}
328
329
ipc::IPCResult
330
ChromiumCDMParent::RecvOnResolvePromiseWithKeyStatus(const uint32_t& aPromiseId,
331
                                                     const uint32_t& aKeyStatus)
332
0
{
333
0
  GMP_LOG("ChromiumCDMParent::RecvOnResolvePromiseWithKeyStatus(this=%p, pid=%u, "
334
0
          "keystatus=%u)",
335
0
          this,
336
0
          aPromiseId,
337
0
          aKeyStatus);
338
0
  if (!mCDMCallback || mIsShutdown) {
339
0
    return IPC_OK();
340
0
  }
341
0
342
0
  mCDMCallback->ResolvePromiseWithKeyStatus(aPromiseId, aKeyStatus);
343
0
344
0
  return IPC_OK();
345
0
}
346
347
ipc::IPCResult
348
ChromiumCDMParent::RecvOnResolveNewSessionPromise(const uint32_t& aPromiseId,
349
                                                  const nsCString& aSessionId)
350
0
{
351
0
  GMP_LOG("ChromiumCDMParent::RecvOnResolveNewSessionPromise(this=%p, pid=%u, "
352
0
          "sid=%s)",
353
0
          this,
354
0
          aPromiseId,
355
0
          aSessionId.get());
356
0
  if (!mCDMCallback || mIsShutdown) {
357
0
    return IPC_OK();
358
0
  }
359
0
360
0
  Maybe<uint32_t> token = mPromiseToCreateSessionToken.GetAndRemove(aPromiseId);
361
0
  if (token.isNothing()) {
362
0
    RejectPromise(aPromiseId,
363
0
                  NS_ERROR_DOM_INVALID_STATE_ERR,
364
0
                  NS_LITERAL_CSTRING("Lost session token for new session."));
365
0
    return IPC_OK();
366
0
  }
367
0
368
0
  mCDMCallback->SetSessionId(token.value(), aSessionId);
369
0
370
0
  ResolvePromise(aPromiseId);
371
0
372
0
  return IPC_OK();
373
0
}
374
375
ipc::IPCResult
376
ChromiumCDMParent::RecvResolveLoadSessionPromise(const uint32_t& aPromiseId,
377
                                                 const bool& aSuccessful)
378
0
{
379
0
  GMP_LOG("ChromiumCDMParent::RecvResolveLoadSessionPromise(this=%p, pid=%u, "
380
0
          "successful=%d)",
381
0
          this,
382
0
          aPromiseId,
383
0
          aSuccessful);
384
0
  if (!mCDMCallback || mIsShutdown) {
385
0
    return IPC_OK();
386
0
  }
387
0
388
0
  mCDMCallback->ResolveLoadSessionPromise(aPromiseId, aSuccessful);
389
0
390
0
  return IPC_OK();
391
0
}
392
393
void
394
ChromiumCDMParent::ResolvePromise(uint32_t aPromiseId)
395
0
{
396
0
  GMP_LOG(
397
0
    "ChromiumCDMParent::ResolvePromise(this=%p, pid=%u)", this, aPromiseId);
398
0
399
0
  // Note: The MediaKeys rejects all pending DOM promises when it
400
0
  // initiates shutdown.
401
0
  if (!mCDMCallback || mIsShutdown) {
402
0
    return;
403
0
  }
404
0
405
0
  mCDMCallback->ResolvePromise(aPromiseId);
406
0
}
407
408
ipc::IPCResult
409
ChromiumCDMParent::RecvOnResolvePromise(const uint32_t& aPromiseId)
410
0
{
411
0
  ResolvePromise(aPromiseId);
412
0
  return IPC_OK();
413
0
}
414
415
void
416
ChromiumCDMParent::RejectPromise(uint32_t aPromiseId,
417
                                 nsresult aException,
418
                                 const nsCString& aErrorMessage)
419
0
{
420
0
  GMP_LOG(
421
0
    "ChromiumCDMParent::RejectPromise(this=%p, pid=%u)", this, aPromiseId);
422
0
  // Note: The MediaKeys rejects all pending DOM promises when it
423
0
  // initiates shutdown.
424
0
  if (!mCDMCallback || mIsShutdown) {
425
0
    return;
426
0
  }
427
0
428
0
  mCDMCallback->RejectPromise(aPromiseId, aException, aErrorMessage);
429
0
}
430
431
static nsresult
432
ToNsresult(uint32_t aException)
433
0
{
434
0
  switch (static_cast<cdm::Exception>(aException)) {
435
0
    case cdm::Exception::kExceptionNotSupportedError:
436
0
      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
437
0
    case cdm::Exception::kExceptionInvalidStateError:
438
0
      return NS_ERROR_DOM_INVALID_STATE_ERR;
439
0
    case cdm::Exception::kExceptionTypeError:
440
0
      return NS_ERROR_DOM_TYPE_ERR;
441
0
    case cdm::Exception::kExceptionQuotaExceededError:
442
0
      return NS_ERROR_DOM_QUOTA_EXCEEDED_ERR;
443
0
  };
444
0
  MOZ_ASSERT_UNREACHABLE("Invalid cdm::Exception enum value.");
445
0
  return NS_ERROR_DOM_TIMEOUT_ERR; // Note: Unique placeholder.
446
0
}
447
448
ipc::IPCResult
449
ChromiumCDMParent::RecvOnRejectPromise(const uint32_t& aPromiseId,
450
                                       const uint32_t& aException,
451
                                       const uint32_t& aSystemCode,
452
                                       const nsCString& aErrorMessage)
453
0
{
454
0
  RejectPromise(aPromiseId, ToNsresult(aException), aErrorMessage);
455
0
  return IPC_OK();
456
0
}
457
458
ipc::IPCResult
459
ChromiumCDMParent::RecvOnSessionMessage(const nsCString& aSessionId,
460
                                        const uint32_t& aMessageType,
461
                                        nsTArray<uint8_t>&& aMessage)
462
0
{
463
0
  GMP_LOG("ChromiumCDMParent::RecvOnSessionMessage(this=%p, sid=%s)",
464
0
          this,
465
0
          aSessionId.get());
466
0
  if (!mCDMCallback || mIsShutdown) {
467
0
    return IPC_OK();
468
0
  }
469
0
470
0
  mCDMCallback->SessionMessage(aSessionId, aMessageType, std::move(aMessage));
471
0
  return IPC_OK();
472
0
}
473
474
ipc::IPCResult
475
ChromiumCDMParent::RecvOnSessionKeysChange(
476
  const nsCString& aSessionId,
477
  nsTArray<CDMKeyInformation>&& aKeysInfo)
478
0
{
479
0
  GMP_LOG("ChromiumCDMParent::RecvOnSessionKeysChange(this=%p)", this);
480
0
  if (!mCDMCallback || mIsShutdown) {
481
0
    return IPC_OK();
482
0
  }
483
0
484
0
  mCDMCallback->SessionKeysChange(aSessionId, std::move(aKeysInfo));
485
0
  return IPC_OK();
486
0
}
487
488
ipc::IPCResult
489
ChromiumCDMParent::RecvOnExpirationChange(const nsCString& aSessionId,
490
                                          const double& aSecondsSinceEpoch)
491
0
{
492
0
  GMP_LOG("ChromiumCDMParent::RecvOnExpirationChange(this=%p) time=%lf",
493
0
          this,
494
0
          aSecondsSinceEpoch);
495
0
  if (!mCDMCallback || mIsShutdown) {
496
0
    return IPC_OK();
497
0
  }
498
0
499
0
  mCDMCallback->ExpirationChange(aSessionId, aSecondsSinceEpoch);
500
0
  return IPC_OK();
501
0
}
502
503
ipc::IPCResult
504
ChromiumCDMParent::RecvOnSessionClosed(const nsCString& aSessionId)
505
0
{
506
0
  GMP_LOG("ChromiumCDMParent::RecvOnSessionClosed(this=%p)", this);
507
0
  if (!mCDMCallback || mIsShutdown) {
508
0
    return IPC_OK();
509
0
  }
510
0
511
0
  mCDMCallback->SessionClosed(aSessionId);
512
0
  return IPC_OK();
513
0
}
514
515
DecryptStatus
516
ToDecryptStatus(uint32_t aStatus)
517
{
518
  switch (static_cast<cdm::Status>(aStatus)) {
519
    case cdm::kSuccess:
520
      return DecryptStatus::Ok;
521
    case cdm::kNoKey:
522
      return DecryptStatus::NoKeyErr;
523
    default:
524
      return DecryptStatus::GenericErr;
525
  }
526
}
527
528
ipc::IPCResult
529
ChromiumCDMParent::RecvDecryptFailed(const uint32_t& aId,
530
                                     const uint32_t& aStatus)
531
0
{
532
0
  GMP_LOG("ChromiumCDMParent::RecvDecryptFailed(this=%p, id=%u, status=%u)",
533
0
          this,
534
0
          aId,
535
0
          aStatus);
536
0
537
0
  if (mIsShutdown) {
538
0
    MOZ_ASSERT(mDecrypts.IsEmpty());
539
0
    return IPC_OK();
540
0
  }
541
0
542
0
  for (size_t i = 0; i < mDecrypts.Length(); i++) {
543
0
    if (mDecrypts[i]->mId == aId) {
544
0
      mDecrypts[i]->PostResult(ToDecryptStatus(aStatus));
545
0
      mDecrypts.RemoveElementAt(i);
546
0
      break;
547
0
    }
548
0
  }
549
0
  return IPC_OK();
550
0
}
551
552
ipc::IPCResult
553
ChromiumCDMParent::RecvDecrypted(const uint32_t& aId,
554
                                 const uint32_t& aStatus,
555
                                 ipc::Shmem&& aShmem)
556
0
{
557
0
  GMP_LOG("ChromiumCDMParent::RecvDecrypted(this=%p, id=%u, status=%u)",
558
0
          this,
559
0
          aId,
560
0
          aStatus);
561
0
562
0
  // We must deallocate the shmem once we've copied the result out of it
563
0
  // in PostResult below.
564
0
  auto autoDeallocateShmem = MakeScopeExit([&, this] { DeallocShmem(aShmem); });
565
0
566
0
  if (mIsShutdown) {
567
0
    MOZ_ASSERT(mDecrypts.IsEmpty());
568
0
    return IPC_OK();
569
0
  }
570
0
  for (size_t i = 0; i < mDecrypts.Length(); i++) {
571
0
    if (mDecrypts[i]->mId == aId) {
572
0
      mDecrypts[i]->PostResult(
573
0
        ToDecryptStatus(aStatus),
574
0
        MakeSpan<const uint8_t>(aShmem.get<uint8_t>(), aShmem.Size<uint8_t>()));
575
0
      mDecrypts.RemoveElementAt(i);
576
0
      break;
577
0
    }
578
0
  }
579
0
  return IPC_OK();
580
0
}
581
582
ipc::IPCResult
583
ChromiumCDMParent::RecvIncreaseShmemPoolSize()
584
0
{
585
0
  GMP_LOG("%s(this=%p) limit=%" PRIu32 " active=%" PRIu32,
586
0
          __func__,
587
0
          this,
588
0
          mVideoShmemLimit,
589
0
          mVideoShmemsActive);
590
0
591
0
  // Put an upper limit on the number of shmems we tolerate the CDM asking
592
0
  // for, to prevent a memory blow-out. In practice, we expect the CDM to
593
0
  // need less than 5, but some encodings require more.
594
0
  // We'd expect CDMs to not have video frames larger than 720p-1080p
595
0
  // (due to DRM robustness requirements), which is about 1.5MB-3MB per
596
0
  // frame.
597
0
  if (mVideoShmemLimit > 50) {
598
0
    mDecodePromise.RejectIfExists(
599
0
      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
600
0
                  RESULT_DETAIL("Failled to ensure CDM has enough shmems.")),
601
0
      __func__);
602
0
    Shutdown();
603
0
    return IPC_OK();
604
0
  }
605
0
  mVideoShmemLimit++;
606
0
607
0
  EnsureSufficientShmems(mVideoFrameBufferSize);
608
0
609
0
  return IPC_OK();
610
0
}
611
612
bool
613
ChromiumCDMParent::PurgeShmems()
614
0
{
615
0
  GMP_LOG("ChromiumCDMParent::PurgeShmems(this=%p) frame_size=%zu"
616
0
          " limit=%" PRIu32 " active=%" PRIu32,
617
0
          this,
618
0
          mVideoFrameBufferSize,
619
0
          mVideoShmemLimit,
620
0
          mVideoShmemsActive);
621
0
622
0
  if (mVideoShmemsActive == 0) {
623
0
    // We haven't allocated any shmems, nothing to do here.
624
0
    return true;
625
0
  }
626
0
  if (!SendPurgeShmems()) {
627
0
    return false;
628
0
  }
629
0
  mVideoShmemsActive = 0;
630
0
  return true;
631
0
}
632
633
bool
634
ChromiumCDMParent::EnsureSufficientShmems(size_t aVideoFrameSize)
635
0
{
636
0
  GMP_LOG("ChromiumCDMParent::EnsureSufficientShmems(this=%p) "
637
0
          "size=%zu expected_size=%zu limit=%" PRIu32
638
0
          " active=%" PRIu32,
639
0
          this,
640
0
          aVideoFrameSize,
641
0
          mVideoFrameBufferSize,
642
0
          mVideoShmemLimit,
643
0
          mVideoShmemsActive);
644
0
645
0
  // The Chromium CDM API requires us to implement a synchronous
646
0
  // interface to supply buffers to the CDM for it to write decrypted samples
647
0
  // into. We want our buffers to be backed by shmems, in order to reduce
648
0
  // the overhead of transferring decoded frames. However due to sandboxing
649
0
  // restrictions, the CDM process cannot allocate shmems itself.
650
0
  // We don't want to be doing synchronous IPC to request shmems from the
651
0
  // content process, nor do we want to have to do intr IPC or make async
652
0
  // IPC conform to the sync allocation interface. So instead we have the
653
0
  // content process pre-allocate a set of shmems and give them to the CDM
654
0
  // process in advance of them being needed.
655
0
  //
656
0
  // When the CDM needs to allocate a buffer for storing a decoded video
657
0
  // frame, the CDM host gives it one of these shmems' buffers. When this
658
0
  // is sent back to the content process, we upload it to a GPU surface,
659
0
  // and send the shmem back to the CDM process so it can reuse it.
660
0
  //
661
0
  // Normally the CDM won't allocate more than one buffer at once, but
662
0
  // we've seen cases where it allocates multiple buffers, returns one and
663
0
  // holds onto the rest. So we need to ensure we have several extra
664
0
  // shmems pre-allocated for the CDM. This threshold is set by the pref
665
0
  // media.eme.chromium-api.video-shmems.
666
0
  //
667
0
  // We also have a failure recovery mechanism; if the CDM asks for more
668
0
  // buffers than we have shmem's available, ChromiumCDMChild gives the
669
0
  // CDM a non-shared memory buffer, and returns the frame to the parent
670
0
  // in an nsTArray<uint8_t> instead of a shmem. The child then sends a
671
0
  // message to the parent asking it to increase the number of shmems in
672
0
  // the pool. Via this mechanism we should recover from incorrectly
673
0
  // predicting how many shmems to pre-allocate.
674
0
  //
675
0
  // At decoder start up, we guess how big the shmems need to be based on
676
0
  // the video frame dimensions. If we guess wrong, the CDM will follow
677
0
  // the non-shmem path, and we'll re-create the shmems of the correct size.
678
0
  // This meanns we can recover from guessing the shmem size wrong.
679
0
  // We must re-take this path after every decoder de-init/re-init, as the
680
0
  // frame sizes should change every time we switch video stream.
681
0
682
0
  if (mVideoFrameBufferSize < aVideoFrameSize) {
683
0
    if (!PurgeShmems()) {
684
0
      return false;
685
0
    }
686
0
    mVideoFrameBufferSize = aVideoFrameSize;
687
0
  }
688
0
689
0
  while (mVideoShmemsActive < mVideoShmemLimit) {
690
0
    if (!SendBufferToCDM(mVideoFrameBufferSize)) {
691
0
      return false;
692
0
    }
693
0
    mVideoShmemsActive++;
694
0
  }
695
0
696
0
  return true;
697
0
}
698
699
ipc::IPCResult
700
ChromiumCDMParent::RecvDecodedData(const CDMVideoFrame& aFrame,
701
                                   nsTArray<uint8_t>&& aData)
702
0
{
703
0
  GMP_LOG("ChromiumCDMParent::RecvDecodedData(this=%p) time=%" PRId64,
704
0
          this,
705
0
          aFrame.mTimestamp());
706
0
707
0
  if (mIsShutdown || mDecodePromise.IsEmpty()) {
708
0
    return IPC_OK();
709
0
  }
710
0
711
0
  if (!EnsureSufficientShmems(aData.Length())) {
712
0
    mDecodePromise.RejectIfExists(
713
0
      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
714
0
                  RESULT_DETAIL("Failled to ensure CDM has enough shmems.")),
715
0
      __func__);
716
0
    return IPC_OK();
717
0
  }
718
0
719
0
  RefPtr<VideoData> v = CreateVideoFrame(aFrame, aData);
720
0
  if (!v) {
721
0
    mDecodePromise.RejectIfExists(
722
0
      MediaResult(NS_ERROR_OUT_OF_MEMORY,
723
0
                  RESULT_DETAIL("Can't create VideoData")),
724
0
      __func__);
725
0
    return IPC_OK();
726
0
  }
727
0
728
0
  ReorderAndReturnOutput(std::move(v));
729
0
730
0
  return IPC_OK();
731
0
}
732
733
ipc::IPCResult
734
ChromiumCDMParent::RecvDecodedShmem(const CDMVideoFrame& aFrame,
735
                                    ipc::Shmem&& aShmem)
736
0
{
737
0
  GMP_LOG("ChromiumCDMParent::RecvDecodedShmem(this=%p) time=%" PRId64
738
0
          " duration=%" PRId64,
739
0
          this,
740
0
          aFrame.mTimestamp(),
741
0
          aFrame.mDuration());
742
0
743
0
  // On failure we need to deallocate the shmem we're to return to the
744
0
  // CDM. On success we return it to the CDM to be reused.
745
0
  auto autoDeallocateShmem =
746
0
    MakeScopeExit([&, this] { this->DeallocShmem(aShmem); });
747
0
748
0
  if (mIsShutdown || mDecodePromise.IsEmpty()) {
749
0
    return IPC_OK();
750
0
  }
751
0
752
0
  RefPtr<VideoData> v = CreateVideoFrame(
753
0
    aFrame, MakeSpan<uint8_t>(aShmem.get<uint8_t>(), aShmem.Size<uint8_t>()));
754
0
  if (!v) {
755
0
    mDecodePromise.RejectIfExists(
756
0
      MediaResult(NS_ERROR_OUT_OF_MEMORY,
757
0
                  RESULT_DETAIL("Can't create VideoData")),
758
0
      __func__);
759
0
    return IPC_OK();
760
0
  }
761
0
762
0
  // Return the shmem to the CDM so the shmem can be reused to send us
763
0
  // another frame.
764
0
  if (!SendGiveBuffer(aShmem)) {
765
0
    mDecodePromise.RejectIfExists(
766
0
      MediaResult(NS_ERROR_OUT_OF_MEMORY,
767
0
                  RESULT_DETAIL("Can't return shmem to CDM process")),
768
0
      __func__);
769
0
    return IPC_OK();
770
0
  }
771
0
772
0
  // Don't need to deallocate the shmem since the CDM process is responsible
773
0
  // for it again.
774
0
  autoDeallocateShmem.release();
775
0
776
0
  ReorderAndReturnOutput(std::move(v));
777
0
778
0
  return IPC_OK();
779
0
}
780
781
void
782
ChromiumCDMParent::ReorderAndReturnOutput(RefPtr<VideoData>&& aFrame)
783
0
{
784
0
  if (mMaxRefFrames == 0) {
785
0
    mDecodePromise.ResolveIfExists({ std::move(aFrame) }, __func__);
786
0
    return;
787
0
  }
788
0
  mReorderQueue.Push(std::move(aFrame));
789
0
  MediaDataDecoder::DecodedData results;
790
0
  while (mReorderQueue.Length() > mMaxRefFrames) {
791
0
    results.AppendElement(mReorderQueue.Pop());
792
0
  }
793
0
  mDecodePromise.Resolve(std::move(results), __func__);
794
0
}
795
796
already_AddRefed<VideoData>
797
ChromiumCDMParent::CreateVideoFrame(const CDMVideoFrame& aFrame,
798
                                    Span<uint8_t> aData)
799
0
{
800
0
  VideoData::YCbCrBuffer b;
801
0
  MOZ_ASSERT(aData.Length() > 0);
802
0
803
0
  b.mPlanes[0].mData = aData.Elements();
804
0
  b.mPlanes[0].mWidth = aFrame.mImageWidth();
805
0
  b.mPlanes[0].mHeight = aFrame.mImageHeight();
806
0
  b.mPlanes[0].mStride = aFrame.mYPlane().mStride();
807
0
  b.mPlanes[0].mOffset = aFrame.mYPlane().mPlaneOffset();
808
0
  b.mPlanes[0].mSkip = 0;
809
0
810
0
  b.mPlanes[1].mData = aData.Elements();
811
0
  b.mPlanes[1].mWidth = (aFrame.mImageWidth() + 1) / 2;
812
0
  b.mPlanes[1].mHeight = (aFrame.mImageHeight() + 1) / 2;
813
0
  b.mPlanes[1].mStride = aFrame.mUPlane().mStride();
814
0
  b.mPlanes[1].mOffset = aFrame.mUPlane().mPlaneOffset();
815
0
  b.mPlanes[1].mSkip = 0;
816
0
817
0
  b.mPlanes[2].mData = aData.Elements();
818
0
  b.mPlanes[2].mWidth = (aFrame.mImageWidth() + 1) / 2;
819
0
  b.mPlanes[2].mHeight = (aFrame.mImageHeight() + 1) / 2;
820
0
  b.mPlanes[2].mStride = aFrame.mVPlane().mStride();
821
0
  b.mPlanes[2].mOffset = aFrame.mVPlane().mPlaneOffset();
822
0
  b.mPlanes[2].mSkip = 0;
823
0
824
0
  gfx::IntRect pictureRegion(0, 0, aFrame.mImageWidth(), aFrame.mImageHeight());
825
0
  RefPtr<VideoData> v = VideoData::CreateAndCopyData(
826
0
    mVideoInfo,
827
0
    mImageContainer,
828
0
    mLastStreamOffset,
829
0
    media::TimeUnit::FromMicroseconds(aFrame.mTimestamp()),
830
0
    media::TimeUnit::FromMicroseconds(aFrame.mDuration()),
831
0
    b,
832
0
    false,
833
0
    media::TimeUnit::FromMicroseconds(-1),
834
0
    pictureRegion);
835
0
836
0
  return v.forget();
837
0
}
838
839
ipc::IPCResult
840
ChromiumCDMParent::RecvDecodeFailed(const uint32_t& aStatus)
841
0
{
842
0
  if (mIsShutdown) {
843
0
    MOZ_ASSERT(mDecodePromise.IsEmpty());
844
0
    return IPC_OK();
845
0
  }
846
0
847
0
  if (aStatus == cdm::kNeedMoreData) {
848
0
    mDecodePromise.ResolveIfExists(nsTArray<RefPtr<MediaData>>(), __func__);
849
0
    return IPC_OK();
850
0
  }
851
0
852
0
  mDecodePromise.RejectIfExists(
853
0
    MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
854
0
                RESULT_DETAIL("ChromiumCDMParent::RecvDecodeFailed")),
855
0
    __func__);
856
0
  return IPC_OK();
857
0
}
858
859
ipc::IPCResult
860
ChromiumCDMParent::RecvShutdown()
861
0
{
862
0
  GMP_LOG("ChromiumCDMParent::RecvShutdown(this=%p)", this);
863
0
  Shutdown();
864
0
  return IPC_OK();
865
0
}
866
867
void
868
ChromiumCDMParent::ActorDestroy(ActorDestroyReason aWhy)
869
0
{
870
0
  GMP_LOG("ChromiumCDMParent::ActorDestroy(this=%p, reason=%d)", this, aWhy);
871
0
  MOZ_ASSERT(!mActorDestroyed);
872
0
  mActorDestroyed = true;
873
0
  // Shutdown() will clear mCDMCallback, so let's keep a reference for later use.
874
0
  auto callback = mCDMCallback;
875
0
  if (!mIsShutdown) {
876
0
    // Plugin crash.
877
0
    MOZ_ASSERT(aWhy == AbnormalShutdown);
878
0
    Shutdown();
879
0
  }
880
0
  MOZ_ASSERT(mIsShutdown);
881
0
  RefPtr<ChromiumCDMParent> kungFuDeathGrip(this);
882
0
  if (mContentParent) {
883
0
    mContentParent->ChromiumCDMDestroyed(this);
884
0
    mContentParent = nullptr;
885
0
  }
886
0
  mAbnormalShutdown = (aWhy == AbnormalShutdown);
887
0
  if (mAbnormalShutdown && callback) {
888
0
    callback->Terminated();
889
0
  }
890
0
  MaybeDisconnect(mAbnormalShutdown);
891
0
}
892
893
RefPtr<MediaDataDecoder::InitPromise>
894
ChromiumCDMParent::InitializeVideoDecoder(
895
  const gmp::CDMVideoDecoderConfig& aConfig,
896
  const VideoInfo& aInfo,
897
  RefPtr<layers::ImageContainer> aImageContainer)
898
0
{
899
0
  if (mIsShutdown) {
900
0
    return MediaDataDecoder::InitPromise::CreateAndReject(
901
0
      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
902
0
                  RESULT_DETAIL("ChromiumCDMParent is shutdown")),
903
0
      __func__);
904
0
  }
905
0
906
0
  // The Widevine CDM version 1.4.8.970 and above contain a video decoder that
907
0
  // does not optimally allocate video frames; it requests buffers much larger
908
0
  // than required. The exact formula the CDM uses to calculate their frame
909
0
  // sizes isn't obvious, but they normally request around or slightly more
910
0
  // than 1.5X the optimal amount. So pad the size of buffers we allocate so
911
0
  // that we're likely to have buffers big enough to accomodate the CDM's weird
912
0
  // frame size calculation.
913
0
  const size_t bufferSize =
914
0
    1.7 * I420FrameBufferSizePadded(aInfo.mImage.width, aInfo.mImage.height);
915
0
  if (bufferSize <= 0) {
916
0
    return MediaDataDecoder::InitPromise::CreateAndReject(
917
0
      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
918
0
                  RESULT_DETAIL("Video frame buffer size is invalid.")),
919
0
      __func__);
920
0
  }
921
0
922
0
  if (!EnsureSufficientShmems(bufferSize)) {
923
0
    return MediaDataDecoder::InitPromise::CreateAndReject(
924
0
      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
925
0
                  RESULT_DETAIL("Failed to init shmems for video decoder")),
926
0
      __func__);
927
0
  }
928
0
929
0
  if (!SendInitializeVideoDecoder(aConfig)) {
930
0
    return MediaDataDecoder::InitPromise::CreateAndReject(
931
0
      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
932
0
                  RESULT_DETAIL("Failed to send init video decoder to CDM")),
933
0
      __func__);
934
0
  }
935
0
936
0
  mMaxRefFrames = (aConfig.mCodec() == cdm::VideoCodec::kCodecH264)
937
0
                    ? H264::HasSPS(aInfo.mExtraData)
938
0
                        ? H264::ComputeMaxRefFrames(aInfo.mExtraData)
939
0
                        : 16
940
0
                    : 0;
941
0
942
0
  mVideoDecoderInitialized = true;
943
0
  mImageContainer = aImageContainer;
944
0
  mVideoInfo = aInfo;
945
0
  mVideoFrameBufferSize = bufferSize;
946
0
947
0
  return mInitVideoDecoderPromise.Ensure(__func__);
948
0
}
949
950
ipc::IPCResult
951
ChromiumCDMParent::RecvOnDecoderInitDone(const uint32_t& aStatus)
952
0
{
953
0
  GMP_LOG("ChromiumCDMParent::RecvOnDecoderInitDone(this=%p, status=%u)",
954
0
          this,
955
0
          aStatus);
956
0
  if (mIsShutdown) {
957
0
    MOZ_ASSERT(mInitVideoDecoderPromise.IsEmpty());
958
0
    return IPC_OK();
959
0
  }
960
0
  if (aStatus == static_cast<uint32_t>(cdm::kSuccess)) {
961
0
    mInitVideoDecoderPromise.ResolveIfExists(TrackInfo::kVideoTrack, __func__);
962
0
  } else {
963
0
    mVideoDecoderInitialized = false;
964
0
    mInitVideoDecoderPromise.RejectIfExists(
965
0
      MediaResult(
966
0
        NS_ERROR_DOM_MEDIA_FATAL_ERR,
967
0
        RESULT_DETAIL("CDM init decode failed with %" PRIu32, aStatus)),
968
0
      __func__);
969
0
  }
970
0
  return IPC_OK();
971
0
}
972
973
RefPtr<MediaDataDecoder::DecodePromise>
974
ChromiumCDMParent::DecryptAndDecodeFrame(MediaRawData* aSample)
975
0
{
976
0
  if (mIsShutdown) {
977
0
    return MediaDataDecoder::DecodePromise::CreateAndReject(
978
0
      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
979
0
                  RESULT_DETAIL("ChromiumCDMParent is shutdown")),
980
0
      __func__);
981
0
  }
982
0
983
0
  GMP_LOG("ChromiumCDMParent::DecryptAndDecodeFrame t=%" PRId64,
984
0
          aSample->mTime.ToMicroseconds());
985
0
986
0
  CDMInputBuffer buffer;
987
0
988
0
  if (!InitCDMInputBuffer(buffer, aSample)) {
989
0
    return MediaDataDecoder::DecodePromise::CreateAndReject(
990
0
      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, "Failed to init CDM buffer."),
991
0
      __func__);
992
0
  }
993
0
994
0
  mLastStreamOffset = aSample->mOffset;
995
0
996
0
  if (!SendDecryptAndDecodeFrame(buffer)) {
997
0
    GMP_LOG(
998
0
      "ChromiumCDMParent::Decrypt(this=%p) failed to send decrypt message.",
999
0
      this);
1000
0
    DeallocShmem(buffer.mData());
1001
0
    return MediaDataDecoder::DecodePromise::CreateAndReject(
1002
0
      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
1003
0
                  "Failed to send decrypt to CDM process."),
1004
0
      __func__);
1005
0
  }
1006
0
1007
0
  return mDecodePromise.Ensure(__func__);
1008
0
}
1009
1010
RefPtr<MediaDataDecoder::FlushPromise>
1011
ChromiumCDMParent::FlushVideoDecoder()
1012
0
{
1013
0
  if (mIsShutdown) {
1014
0
    MOZ_ASSERT(mReorderQueue.IsEmpty());
1015
0
    return MediaDataDecoder::FlushPromise::CreateAndReject(
1016
0
      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
1017
0
                  RESULT_DETAIL("ChromiumCDMParent is shutdown")),
1018
0
      __func__);
1019
0
  }
1020
0
1021
0
  mReorderQueue.Clear();
1022
0
1023
0
  mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
1024
0
  if (!SendResetVideoDecoder()) {
1025
0
    return MediaDataDecoder::FlushPromise::CreateAndReject(
1026
0
      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, "Failed to send flush to CDM."),
1027
0
      __func__);
1028
0
  }
1029
0
  return mFlushDecoderPromise.Ensure(__func__);
1030
0
}
1031
1032
ipc::IPCResult
1033
ChromiumCDMParent::RecvResetVideoDecoderComplete()
1034
0
{
1035
0
  MOZ_ASSERT(mReorderQueue.IsEmpty());
1036
0
  if (mIsShutdown) {
1037
0
    MOZ_ASSERT(mFlushDecoderPromise.IsEmpty());
1038
0
    return IPC_OK();
1039
0
  }
1040
0
  mFlushDecoderPromise.ResolveIfExists(true, __func__);
1041
0
  return IPC_OK();
1042
0
}
1043
1044
RefPtr<MediaDataDecoder::DecodePromise>
1045
ChromiumCDMParent::Drain()
1046
0
{
1047
0
  MOZ_ASSERT(mDecodePromise.IsEmpty(), "Must wait for decoding to complete");
1048
0
  if (mIsShutdown) {
1049
0
    return MediaDataDecoder::DecodePromise::CreateAndReject(
1050
0
      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
1051
0
                  RESULT_DETAIL("ChromiumCDMParent is shutdown")),
1052
0
      __func__);
1053
0
  }
1054
0
1055
0
  RefPtr<MediaDataDecoder::DecodePromise> p = mDecodePromise.Ensure(__func__);
1056
0
  if (!SendDrain()) {
1057
0
    mDecodePromise.Resolve(MediaDataDecoder::DecodedData(), __func__);
1058
0
  }
1059
0
  return p;
1060
0
}
1061
1062
ipc::IPCResult
1063
ChromiumCDMParent::RecvDrainComplete()
1064
0
{
1065
0
  if (mIsShutdown) {
1066
0
    MOZ_ASSERT(mDecodePromise.IsEmpty());
1067
0
    return IPC_OK();
1068
0
  }
1069
0
1070
0
  MediaDataDecoder::DecodedData samples;
1071
0
  while (!mReorderQueue.IsEmpty()) {
1072
0
    samples.AppendElement(mReorderQueue.Pop());
1073
0
  }
1074
0
1075
0
  mDecodePromise.ResolveIfExists(std::move(samples), __func__);
1076
0
  return IPC_OK();
1077
0
}
1078
RefPtr<ShutdownPromise>
1079
ChromiumCDMParent::ShutdownVideoDecoder()
1080
0
{
1081
0
  if (mIsShutdown || !mVideoDecoderInitialized) {
1082
0
    return ShutdownPromise::CreateAndResolve(true, __func__);
1083
0
  }
1084
0
  mInitVideoDecoderPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED,
1085
0
                                          __func__);
1086
0
  mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
1087
0
  MOZ_ASSERT(mFlushDecoderPromise.IsEmpty());
1088
0
  if (!SendDeinitializeVideoDecoder()) {
1089
0
    return ShutdownPromise::CreateAndResolve(true, __func__);
1090
0
  }
1091
0
  mVideoDecoderInitialized = false;
1092
0
1093
0
  GMP_LOG("ChromiumCDMParent::~ShutdownVideoDecoder(this=%p) ", this);
1094
0
1095
0
  // The ChromiumCDMChild will purge its shmems, so if the decoder is
1096
0
  // reinitialized the shmems need to be re-allocated, and they may need
1097
0
  // to be a different size.
1098
0
  mVideoShmemsActive = 0;
1099
0
  mVideoFrameBufferSize = 0;
1100
0
  return ShutdownPromise::CreateAndResolve(true, __func__);
1101
0
}
1102
1103
void
1104
ChromiumCDMParent::Shutdown()
1105
0
{
1106
0
  GMP_LOG("ChromiumCDMParent::Shutdown(this=%p)", this);
1107
0
1108
0
  if (mIsShutdown) {
1109
0
    return;
1110
0
  }
1111
0
  mIsShutdown = true;
1112
0
1113
0
  // If we're shutting down due to the plugin shutting down due to application
1114
0
  // shutdown, we should tell the CDM proxy to also shutdown. Otherwise the
1115
0
  // proxy will shutdown when the owning MediaKeys is destroyed during cycle
1116
0
  // collection, and that will not shut down cleanly as the GMP thread will be
1117
0
  // shutdown by then.
1118
0
  if (mCDMCallback) {
1119
0
    mCDMCallback->Shutdown();
1120
0
  }
1121
0
1122
0
  // We may be called from a task holding the last reference to the CDM callback, so
1123
0
  // let's clear our local weak pointer to ensure it will not be used afterward
1124
0
  // (including from an already-queued task, e.g.: ActorDestroy).
1125
0
  mCDMCallback = nullptr;
1126
0
1127
0
  mReorderQueue.Clear();
1128
0
1129
0
  for (RefPtr<DecryptJob>& decrypt : mDecrypts) {
1130
0
    decrypt->PostResult(eme::AbortedErr);
1131
0
  }
1132
0
  mDecrypts.Clear();
1133
0
1134
0
  if (mVideoDecoderInitialized && !mActorDestroyed) {
1135
0
    Unused << SendDeinitializeVideoDecoder();
1136
0
    mVideoDecoderInitialized = false;
1137
0
  }
1138
0
1139
0
  // Note: MediaKeys rejects all outstanding promises when it initiates shutdown.
1140
0
  mPromiseToCreateSessionToken.Clear();
1141
0
1142
0
  mInitVideoDecoderPromise.RejectIfExists(
1143
0
    MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
1144
0
                RESULT_DETAIL("ChromiumCDMParent is shutdown")),
1145
0
    __func__);
1146
0
  mDecodePromise.RejectIfExists(
1147
0
    MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
1148
0
                RESULT_DETAIL("ChromiumCDMParent is shutdown")),
1149
0
    __func__);
1150
0
  mFlushDecoderPromise.RejectIfExists(
1151
0
    MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
1152
0
                RESULT_DETAIL("ChromiumCDMParent is shutdown")),
1153
0
    __func__);
1154
0
1155
0
  if (!mActorDestroyed) {
1156
0
    Unused << SendDestroy();
1157
0
  }
1158
0
}
1159
1160
} // namespace gmp
1161
} // namespace mozilla
1162
1163
#undef NS_DispatchToMainThread