Source code

Revision control

Other Tools

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