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