Bug 1315850 - Send decrypt operations to Chromium CDM. r=gerald
We still use the same EMEDecryptor MediaDataDecoder as is used by the existing
EME decrypting path.
MozReview-Commit-ID: 3pXPjChctLb
--- a/dom/media/gmp/ChromiumCDMChild.cpp
+++ b/dom/media/gmp/ChromiumCDMChild.cpp
@@ -305,20 +305,88 @@ ChromiumCDMChild::RecvRemoveSession(cons
aPromiseId,
aSessionId.get());
if (mCDM) {
mCDM->RemoveSession(aPromiseId, aSessionId.get(), aSessionId.Length());
}
return IPC_OK();
}
+void
+ChromiumCDMChild::DecryptFailed(uint32_t aId, cdm::Status aStatus)
+{
+ Unused << SendDecrypted(aId, aStatus, nsTArray<uint8_t>());
+}
+
+static void
+InitInputBuffer(const CDMInputBuffer& aBuffer,
+ nsTArray<cdm::SubsampleEntry>& aSubSamples,
+ cdm::InputBuffer& aInputBuffer)
+{
+ aInputBuffer.data = aBuffer.mData().Elements();
+ aInputBuffer.data_size = aBuffer.mData().Length();
+
+ if (aBuffer.mIsEncrypted()) {
+ aInputBuffer.key_id = aBuffer.mKeyId().Elements();
+ aInputBuffer.key_id_size = aBuffer.mKeyId().Length();
+
+ aInputBuffer.iv = aBuffer.mIV().Elements();
+ aInputBuffer.iv_size = aBuffer.mIV().Length();
+
+ aSubSamples.SetCapacity(aBuffer.mClearBytes().Length());
+ for (size_t i = 0; i < aBuffer.mCipherBytes().Length(); i++) {
+ aSubSamples.AppendElement(cdm::SubsampleEntry(aBuffer.mClearBytes()[i],
+ aBuffer.mCipherBytes()[i]));
+ }
+ aInputBuffer.subsamples = aSubSamples.Elements();
+ aInputBuffer.num_subsamples = aSubSamples.Length();
+ }
+ aInputBuffer.timestamp = aBuffer.mTimestamp();
+}
+
mozilla::ipc::IPCResult
-ChromiumCDMChild::RecvDecrypt(const CDMInputBuffer& aBuffer)
+ChromiumCDMChild::RecvDecrypt(const uint32_t& aId,
+ const CDMInputBuffer& aBuffer)
{
GMP_LOG("ChromiumCDMChild::RecvDecrypt()");
+ if (!mCDM) {
+ GMP_LOG("ChromiumCDMChild::RecvDecrypt() no CDM");
+ DecryptFailed(aId, cdm::kDecryptError);
+ return IPC_OK();
+ }
+ if (aBuffer.mClearBytes().Length() != aBuffer.mCipherBytes().Length()) {
+ GMP_LOG("ChromiumCDMChild::RecvDecrypt() clear/cipher bytes length doesn't "
+ "match");
+ DecryptFailed(aId, cdm::kDecryptError);
+ return IPC_OK();
+ }
+
+ cdm::InputBuffer input;
+ nsTArray<cdm::SubsampleEntry> subsamples;
+ InitInputBuffer(aBuffer, subsamples, input);
+
+ WidevineDecryptedBlock output;
+ cdm::Status status = mCDM->Decrypt(input, &output);
+
+ if (status != cdm::kSuccess) {
+ DecryptFailed(aId, status);
+ return IPC_OK();
+ }
+
+ if (!output.DecryptedBuffer() ||
+ output.DecryptedBuffer()->Size() != aBuffer.mData().Length()) {
+ // The sizes of the input and output should exactly match.
+ DecryptFailed(aId, cdm::kDecryptError);
+ return IPC_OK();
+ }
+
+ nsTArray<uint8_t> buf =
+ static_cast<WidevineBuffer*>(output.DecryptedBuffer())->ExtractBuffer();
+ Unused << SendDecrypted(aId, cdm::kSuccess, buf);
+
return IPC_OK();
}
mozilla::ipc::IPCResult
ChromiumCDMChild::RecvInitializeVideoDecoder(
const CDMVideoDecoderConfig& aConfig)
{
GMP_LOG("ChromiumCDMChild::RecvInitializeVideoDecoder()");
--- a/dom/media/gmp/ChromiumCDMChild.h
+++ b/dom/media/gmp/ChromiumCDMChild.h
@@ -86,25 +86,28 @@ protected:
nsTArray<uint8_t>&& aInitData) override;
ipc::IPCResult RecvUpdateSession(const uint32_t& aPromiseId,
const nsCString& aSessionId,
nsTArray<uint8_t>&& aResponse) override;
ipc::IPCResult RecvCloseSession(const uint32_t& aPromiseId,
const nsCString& aSessionId) override;
ipc::IPCResult RecvRemoveSession(const uint32_t& aPromiseId,
const nsCString& aSessionId) override;
- ipc::IPCResult RecvDecrypt(const CDMInputBuffer& aBuffer) override;
+ ipc::IPCResult RecvDecrypt(const uint32_t& aId,
+ const CDMInputBuffer& aBuffer) override;
ipc::IPCResult RecvInitializeVideoDecoder(
const CDMVideoDecoderConfig& aConfig) override;
ipc::IPCResult RecvDeinitializeVideoDecoder() override;
ipc::IPCResult RecvResetVideoDecoder() override;
ipc::IPCResult RecvDecryptAndDecodeFrame(
const CDMInputBuffer& aBuffer) override;
ipc::IPCResult RecvDestroy() override;
+ void DecryptFailed(uint32_t aId, cdm::Status aStatus);
+
GMPContentChild* mPlugin = nullptr;
cdm::ContentDecryptionModule_8* mCDM = nullptr;
};
} // namespace gmp
} // namespace mozilla
#endif // ChromiumCDMChild_h_
--- a/dom/media/gmp/ChromiumCDMParent.cpp
+++ b/dom/media/gmp/ChromiumCDMParent.cpp
@@ -104,16 +104,60 @@ ChromiumCDMParent::RemoveSession(const n
if (!SendRemoveSession(aPromiseId, aSessionId)) {
RejectPromise(
aPromiseId,
NS_ERROR_DOM_INVALID_STATE_ERR,
NS_LITERAL_CSTRING("Failed to send removeSession to CDM process"));
}
}
+static bool
+InitCDMInputBuffer(gmp::CDMInputBuffer& aBuffer, MediaRawData* aSample)
+{
+ const CryptoSample& crypto = aSample->mCrypto;
+ if (crypto.mEncryptedSizes.Length() != crypto.mPlainSizes.Length()) {
+ GMP_LOG("InitCDMInputBuffer clear/cipher subsamples don't match");
+ return false;
+ }
+
+ nsTArray<uint8_t> data;
+ data.AppendElements(aSample->Data(), aSample->Size());
+
+ aBuffer = gmp::CDMInputBuffer(data,
+ crypto.mKeyId,
+ crypto.mIV,
+ aSample->mTime,
+ aSample->mDuration,
+ crypto.mPlainSizes,
+ crypto.mEncryptedSizes,
+ crypto.mValid);
+ return true;
+}
+
+RefPtr<DecryptPromise>
+ChromiumCDMParent::Decrypt(MediaRawData* aSample)
+{
+ CDMInputBuffer buffer;
+ if (!InitCDMInputBuffer(buffer, aSample)) {
+ return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, aSample),
+ __func__);
+ }
+ RefPtr<DecryptJob> job = new DecryptJob(aSample);
+ if (!SendDecrypt(job->mId, buffer)) {
+ GMP_LOG(
+ "ChromiumCDMParent::Decrypt(this=%p) failed to send decrypt message",
+ this);
+ return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, aSample),
+ __func__);
+ }
+ RefPtr<DecryptPromise> promise = job->Ensure();
+ mDecrypts.AppendElement(job);
+ return promise;
+}
+
ipc::IPCResult
ChromiumCDMParent::Recv__delete__()
{
GMP_LOG("ChromiumCDMParent::Recv__delete__(this=%p)", this);
return IPC_OK();
}
ipc::IPCResult
@@ -361,20 +405,45 @@ ChromiumCDMParent::RecvOnLegacySessionEr
&ChromiumCDMProxy::OnSessionError,
NS_ConvertUTF8toUTF16(aSessionId),
ToNsresult(aError),
aSystemCode,
NS_ConvertUTF8toUTF16(aMessage)));
return IPC_OK();
}
+DecryptStatus
+ToDecryptStatus(uint32_t aError)
+{
+ switch (static_cast<cdm::Status>(aError)) {
+ case cdm::kSuccess:
+ return DecryptStatus::Ok;
+ case cdm::kNoKey:
+ return DecryptStatus::NoKeyErr;
+ default:
+ return DecryptStatus::GenericErr;
+ }
+}
+
ipc::IPCResult
-ChromiumCDMParent::RecvDecrypted(const uint32_t& aStatus,
+ChromiumCDMParent::RecvDecrypted(const uint32_t& aId,
+ const uint32_t& aStatus,
nsTArray<uint8_t>&& aData)
{
+ GMP_LOG("ChromiumCDMParent::RecvDecrypted(this=%p, id=%u, status=%u)",
+ this,
+ aId,
+ aStatus);
+ for (size_t i = 0; i < mDecrypts.Length(); i++) {
+ if (mDecrypts[i]->mId == aId) {
+ mDecrypts[i]->PostResult(ToDecryptStatus(aStatus), aData);
+ mDecrypts.RemoveElementAt(i);
+ break;
+ }
+ }
return IPC_OK();
}
ipc::IPCResult
ChromiumCDMParent::RecvDecoded(const CDMVideoFrame& aFrame)
{
return IPC_OK();
}
--- a/dom/media/gmp/ChromiumCDMParent.h
+++ b/dom/media/gmp/ChromiumCDMParent.h
@@ -7,19 +7,21 @@
#define ChromiumCDMParent_h_
#include "GMPCrashHelper.h"
#include "GMPCrashHelperHolder.h"
#include "GMPMessageUtils.h"
#include "mozilla/gmp/PChromiumCDMParent.h"
#include "mozilla/RefPtr.h"
#include "nsDataHashtable.h"
+#include "DecryptJob.h"
namespace mozilla {
+class MediaRawData;
class ChromiumCDMProxy;
namespace gmp {
class GMPContentParent;
class ChromiumCDMParent final
: public PChromiumCDMParent
@@ -48,16 +50,18 @@ public:
void UpdateSession(const nsCString& aSessionId,
uint32_t aPromiseId,
const nsTArray<uint8_t>& aResponse);
void CloseSession(const nsCString& aSessionId, uint32_t aPromiseId);
void RemoveSession(const nsCString& aSessionId, uint32_t aPromiseId);
+ RefPtr<DecryptPromise> Decrypt(MediaRawData* aSample);
+
// TODO: Add functions for clients to send data to CDM, and
// a Close() function.
protected:
~ChromiumCDMParent() {}
ipc::IPCResult Recv__delete__() override;
ipc::IPCResult RecvOnResolveNewSessionPromise(
@@ -77,17 +81,18 @@ protected:
ipc::IPCResult RecvOnExpirationChange(
const nsCString& aSessionId,
const double& aSecondsSinceEpoch) override;
ipc::IPCResult RecvOnSessionClosed(const nsCString& aSessionId) override;
ipc::IPCResult RecvOnLegacySessionError(const nsCString& aSessionId,
const uint32_t& aError,
const uint32_t& aSystemCode,
const nsCString& aMessage) override;
- ipc::IPCResult RecvDecrypted(const uint32_t& aStatus,
+ ipc::IPCResult RecvDecrypted(const uint32_t& aId,
+ const uint32_t& aStatus,
nsTArray<uint8_t>&& aData) override;
ipc::IPCResult RecvDecoded(const CDMVideoFrame& aFrame) override;
ipc::IPCResult RecvDecodeFailed(const uint32_t& aStatus) override;
ipc::IPCResult RecvShutdown() override;
void ActorDestroy(ActorDestroyReason aWhy) override;
void RejectPromise(uint32_t aPromiseId,
nsresult aError,
@@ -97,14 +102,15 @@ protected:
const uint32_t mPluginId;
GMPContentParent* mContentParent;
// Note: this pointer is a weak reference because otherwise it would cause
// a cycle, as ChromiumCDMProxy has a strong reference to the
// ChromiumCDMParent.
ChromiumCDMProxy* mProxy = nullptr;
nsDataHashtable<nsUint32HashKey, uint32_t> mPromiseToCreateSessionToken;
+ nsTArray<RefPtr<DecryptJob>> mDecrypts;
};
} // namespace gmp
} // namespace mozilla
#endif // ChromiumCDMParent_h_
--- a/dom/media/gmp/ChromiumCDMProxy.cpp
+++ b/dom/media/gmp/ChromiumCDMProxy.cpp
@@ -4,17 +4,16 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ChromiumCDMProxy.h"
#include "mozilla/dom/MediaKeySession.h"
#include "GMPUtils.h"
#include "nsPrintfCString.h"
#include "GMPService.h"
-#include "mozilla/dom/MediaKeySession.h"
namespace mozilla {
ChromiumCDMProxy::ChromiumCDMProxy(dom::MediaKeys* aKeys,
const nsAString& aKeySystem,
GMPCrashHelper* aCrashHelper,
bool aDistinctiveIdentifierRequired,
bool aPersistentStateRequired,
@@ -469,18 +468,20 @@ CDMCaps&
ChromiumCDMProxy::Capabilites()
{
return mCapabilites;
}
RefPtr<DecryptPromise>
ChromiumCDMProxy::Decrypt(MediaRawData* aSample)
{
- return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, nullptr),
- __func__);
+ RefPtr<gmp::ChromiumCDMParent> cdm = mCDM;
+ RefPtr<MediaRawData> sample = aSample;
+ return InvokeAsync(
+ mGMPThread, __func__, [cdm, sample]() { return cdm->Decrypt(sample); });
}
void
ChromiumCDMProxy::GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
nsTArray<nsCString>& aSessionIds)
{
CDMCaps::AutoLock caps(Capabilites());
caps.GetSessionIdsForKeyId(aKeyId, aSessionIds);
--- a/dom/media/gmp/PChromiumCDM.ipdl
+++ b/dom/media/gmp/PChromiumCDM.ipdl
@@ -31,17 +31,17 @@ child:
uint8_t[] aResponse);
async CloseSession(uint32_t aPromiseId,
nsCString aSessionId);
async RemoveSession(uint32_t aPromiseId,
nsCString aSessionId);
- async Decrypt(CDMInputBuffer aBuffer);
+ async Decrypt(uint32_t aId, CDMInputBuffer aBuffer);
async InitializeVideoDecoder(CDMVideoDecoderConfig aConfig);
async DeinitializeVideoDecoder();
async ResetVideoDecoder();
async DecryptAndDecodeFrame(CDMInputBuffer aBuffer);
@@ -74,17 +74,17 @@ parent:
async OnSessionClosed(nsCString aSessionId);
async OnLegacySessionError(nsCString aSessionId,
uint32_t aError,
uint32_t aSystemCode,
nsCString aMessage);
// Return values of cdm::ContentDecryptionModule8::Decrypt
- async Decrypted(uint32_t aStatus, uint8_t[] aData);
+ async Decrypted(uint32_t aId, uint32_t aStatus, uint8_t[] aData);
// Return values of cdm::ContentDecryptionModule8::DecryptAndDecodeFrame
async Decoded(CDMVideoFrame aFrame);
async DecodeFailed(uint32_t aStatus);
async Shutdown();
};