diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp')
-rw-r--r-- | dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp b/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp new file mode 100644 index 000000000..149fa1701 --- /dev/null +++ b/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp @@ -0,0 +1,541 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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 "WidevineDecryptor.h" + +#include "WidevineAdapter.h" +#include "WidevineUtils.h" +#include "WidevineFileIO.h" +#include <mozilla/SizePrintfMacros.h> +#include <stdarg.h> +#include "base/time.h" + +using namespace cdm; +using namespace std; + +namespace mozilla { + +static map<uint32_t, RefPtr<CDMWrapper>> sDecryptors; + +/* static */ +RefPtr<CDMWrapper> +WidevineDecryptor::GetInstance(uint32_t aInstanceId) +{ + auto itr = sDecryptors.find(aInstanceId); + if (itr != sDecryptors.end()) { + return itr->second; + } + return nullptr; +} + + +WidevineDecryptor::WidevineDecryptor() + : mCallback(nullptr) +{ + Log("WidevineDecryptor created this=%p", this); + AddRef(); // Released in DecryptingComplete(). +} + +WidevineDecryptor::~WidevineDecryptor() +{ + Log("WidevineDecryptor destroyed this=%p", this); +} + +void +WidevineDecryptor::SetCDM(RefPtr<CDMWrapper> aCDM, uint32_t aInstanceId) +{ + mCDM = aCDM; + mInstanceId = aInstanceId; + sDecryptors[mInstanceId] = aCDM; +} + +void +WidevineDecryptor::Init(GMPDecryptorCallback* aCallback, + bool aDistinctiveIdentifierRequired, + bool aPersistentStateRequired) +{ + Log("WidevineDecryptor::Init() this=%p distinctiveId=%d persistentState=%d", + this, aDistinctiveIdentifierRequired, aPersistentStateRequired); + MOZ_ASSERT(aCallback); + mCallback = aCallback; + MOZ_ASSERT(mCDM); + mDistinctiveIdentifierRequired = aDistinctiveIdentifierRequired; + mPersistentStateRequired = aPersistentStateRequired; + if (CDM()) { + CDM()->Initialize(aDistinctiveIdentifierRequired, + aPersistentStateRequired); + } +} + +static SessionType +ToCDMSessionType(GMPSessionType aSessionType) +{ + switch (aSessionType) { + case kGMPTemporySession: return kTemporary; + case kGMPPersistentSession: return kPersistentLicense; + case kGMPSessionInvalid: return kTemporary; + // TODO: kPersistentKeyRelease + } + MOZ_ASSERT(false); // Not supposed to get here. + return kTemporary; +} + +void +WidevineDecryptor::CreateSession(uint32_t aCreateSessionToken, + uint32_t aPromiseId, + const char* aInitDataType, + uint32_t aInitDataTypeSize, + const uint8_t* aInitData, + uint32_t aInitDataSize, + GMPSessionType aSessionType) +{ + Log("Decryptor::CreateSession(token=%d, pid=%d)", aCreateSessionToken, aPromiseId); + InitDataType initDataType; + if (!strcmp(aInitDataType, "cenc")) { + initDataType = kCenc; + } else if (!strcmp(aInitDataType, "webm")) { + initDataType = kWebM; + } else if (!strcmp(aInitDataType, "keyids")) { + initDataType = kKeyIds; + } else { + // Invalid init data type + const char* errorMsg = "Invalid init data type when creating session."; + OnRejectPromise(aPromiseId, kNotSupportedError, 0, errorMsg, sizeof(errorMsg)); + return; + } + mPromiseIdToNewSessionTokens[aPromiseId] = aCreateSessionToken; + CDM()->CreateSessionAndGenerateRequest(aPromiseId, + ToCDMSessionType(aSessionType), + initDataType, + aInitData, aInitDataSize); +} + +void +WidevineDecryptor::LoadSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) +{ + Log("Decryptor::LoadSession(pid=%d, %s)", aPromiseId, aSessionId); + // TODO: session type?? + CDM()->LoadSession(aPromiseId, kPersistentLicense, aSessionId, aSessionIdLength); +} + +void +WidevineDecryptor::UpdateSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength, + const uint8_t* aResponse, + uint32_t aResponseSize) +{ + Log("Decryptor::UpdateSession(pid=%d, session=%s)", aPromiseId, aSessionId); + CDM()->UpdateSession(aPromiseId, aSessionId, aSessionIdLength, aResponse, aResponseSize); +} + +void +WidevineDecryptor::CloseSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) +{ + Log("Decryptor::CloseSession(pid=%d, session=%s)", aPromiseId, aSessionId); + CDM()->CloseSession(aPromiseId, aSessionId, aSessionIdLength); +} + +void +WidevineDecryptor::RemoveSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) +{ + Log("Decryptor::RemoveSession(%s)", aSessionId); + CDM()->RemoveSession(aPromiseId, aSessionId, aSessionIdLength); +} + +void +WidevineDecryptor::SetServerCertificate(uint32_t aPromiseId, + const uint8_t* aServerCert, + uint32_t aServerCertSize) +{ + Log("Decryptor::SetServerCertificate()"); + CDM()->SetServerCertificate(aPromiseId, aServerCert, aServerCertSize); +} + +class WidevineDecryptedBlock : public cdm::DecryptedBlock { +public: + + WidevineDecryptedBlock() + : mBuffer(nullptr) + , mTimestamp(0) + { + } + + ~WidevineDecryptedBlock() { + if (mBuffer) { + mBuffer->Destroy(); + mBuffer = nullptr; + } + } + + void SetDecryptedBuffer(cdm::Buffer* aBuffer) override { + mBuffer = aBuffer; + } + + cdm::Buffer* DecryptedBuffer() override { + return mBuffer; + } + + void SetTimestamp(int64_t aTimestamp) override { + mTimestamp = aTimestamp; + } + + int64_t Timestamp() const override { + return mTimestamp; + } + +private: + cdm::Buffer* mBuffer; + int64_t mTimestamp; +}; + +void +WidevineDecryptor::Decrypt(GMPBuffer* aBuffer, + GMPEncryptedBufferMetadata* aMetadata) +{ + if (!mCallback) { + Log("WidevineDecryptor::Decrypt() this=%p FAIL; !mCallback", this); + return; + } + const GMPEncryptedBufferMetadata* crypto = aMetadata; + InputBuffer sample; + nsTArray<SubsampleEntry> subsamples; + InitInputBuffer(crypto, aBuffer->Id(), aBuffer->Data(), aBuffer->Size(), sample, subsamples); + WidevineDecryptedBlock decrypted; + Status rv = CDM()->Decrypt(sample, &decrypted); + Log("Decryptor::Decrypt(timestamp=%lld) rv=%d sz=%d", + sample.timestamp, rv, decrypted.DecryptedBuffer()->Size()); + if (rv == kSuccess) { + aBuffer->Resize(decrypted.DecryptedBuffer()->Size()); + memcpy(aBuffer->Data(), + decrypted.DecryptedBuffer()->Data(), + decrypted.DecryptedBuffer()->Size()); + } + mCallback->Decrypted(aBuffer, ToGMPErr(rv)); +} + +void +WidevineDecryptor::DecryptingComplete() +{ + Log("WidevineDecryptor::DecryptingComplete() this=%p", this); + // Drop our references to the CDMWrapper. When any other references + // held elsewhere are dropped (for example references held by a + // WidevineVideoDecoder, or a runnable), the CDMWrapper destroys + // the CDM. + mCDM = nullptr; + sDecryptors.erase(mInstanceId); + mCallback = nullptr; + Release(); +} + +class WidevineBuffer : public cdm::Buffer { +public: + explicit WidevineBuffer(size_t aSize) { + Log("WidevineBuffer(size=" PRIuSIZE ") created", aSize); + mBuffer.SetLength(aSize); + } + ~WidevineBuffer() { + Log("WidevineBuffer(size=" PRIuSIZE ") destroyed", Size()); + } + void Destroy() override { delete this; } + uint32_t Capacity() const override { return mBuffer.Length(); }; + uint8_t* Data() override { return mBuffer.Elements(); } + void SetSize(uint32_t aSize) override { mBuffer.SetLength(aSize); } + uint32_t Size() const override { return mBuffer.Length(); } + +private: + WidevineBuffer(const WidevineBuffer&); + void operator=(const WidevineBuffer&); + + nsTArray<uint8_t> mBuffer; +}; + +Buffer* +WidevineDecryptor::Allocate(uint32_t aCapacity) +{ + Log("Decryptor::Allocate(capacity=%u)", aCapacity); + return new WidevineBuffer(aCapacity); +} + +class TimerTask : public GMPTask { +public: + TimerTask(WidevineDecryptor* aDecryptor, + RefPtr<CDMWrapper> aCDM, + void* aContext) + : mDecryptor(aDecryptor) + , mCDM(aCDM) + , mContext(aContext) + { + } + ~TimerTask() override {} + void Run() override { + mCDM->GetCDM()->TimerExpired(mContext); + } + void Destroy() override { delete this; } +private: + RefPtr<WidevineDecryptor> mDecryptor; + RefPtr<CDMWrapper> mCDM; + void* mContext; +}; + +void +WidevineDecryptor::SetTimer(int64_t aDelayMs, void* aContext) +{ + Log("Decryptor::SetTimer(delay_ms=%lld, context=0x%x)", aDelayMs, aContext); + if (mCDM) { + GMPSetTimerOnMainThread(new TimerTask(this, mCDM, aContext), aDelayMs); + } +} + +Time +WidevineDecryptor::GetCurrentWallTime() +{ + return base::Time::Now().ToDoubleT(); +} + +void +WidevineDecryptor::OnResolveNewSessionPromise(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdSize) +{ + if (!mCallback) { + Log("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d) FAIL; !mCallback", aPromiseId); + return; + } + Log("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d)", aPromiseId); + auto iter = mPromiseIdToNewSessionTokens.find(aPromiseId); + if (iter == mPromiseIdToNewSessionTokens.end()) { + Log("FAIL: Decryptor::OnResolveNewSessionPromise(aPromiseId=%d) unknown aPromiseId", aPromiseId); + return; + } + mCallback->SetSessionId(iter->second, aSessionId, aSessionIdSize); + mCallback->ResolvePromise(aPromiseId); + mPromiseIdToNewSessionTokens.erase(iter); +} + +void +WidevineDecryptor::OnResolvePromise(uint32_t aPromiseId) +{ + if (!mCallback) { + Log("Decryptor::OnResolvePromise(aPromiseId=0x%d) FAIL; !mCallback", aPromiseId); + return; + } + Log("Decryptor::OnResolvePromise(aPromiseId=%d)", aPromiseId); + mCallback->ResolvePromise(aPromiseId); +} + +static GMPDOMException +ToGMPDOMException(cdm::Error aError) +{ + switch (aError) { + case kNotSupportedError: return kGMPNotSupportedError; + case kInvalidStateError: return kGMPInvalidStateError; + case kInvalidAccessError: + // Note: Chrome converts kInvalidAccessError to TypeError, since the + // Chromium CDM API doesn't have a type error enum value. The EME spec + // requires TypeError in some places, so we do the same conversion. + // See bug 1313202. + return kGMPTypeError; + case kQuotaExceededError: return kGMPQuotaExceededError; + case kUnknownError: return kGMPInvalidModificationError; // Note: Unique placeholder. + case kClientError: return kGMPAbortError; // Note: Unique placeholder. + case kOutputError: return kGMPSecurityError; // Note: Unique placeholder. + }; + return kGMPTimeoutError; // Note: Unique placeholder. +} + +void +WidevineDecryptor::OnRejectPromise(uint32_t aPromiseId, + Error aError, + uint32_t aSystemCode, + const char* aErrorMessage, + uint32_t aErrorMessageSize) +{ + if (!mCallback) { + Log("Decryptor::OnRejectPromise(aPromiseId=%d, err=%d, sysCode=%u, msg=%s) FAIL; !mCallback", + aPromiseId, (int)aError, aSystemCode, aErrorMessage); + return; + } + Log("Decryptor::OnRejectPromise(aPromiseId=%d, err=%d, sysCode=%u, msg=%s)", + aPromiseId, (int)aError, aSystemCode, aErrorMessage); + mCallback->RejectPromise(aPromiseId, + ToGMPDOMException(aError), + !aErrorMessageSize ? "" : aErrorMessage, + aErrorMessageSize); +} + +static GMPSessionMessageType +ToGMPMessageType(MessageType message_type) +{ + switch (message_type) { + case kLicenseRequest: return kGMPLicenseRequest; + case kLicenseRenewal: return kGMPLicenseRenewal; + case kLicenseRelease: return kGMPLicenseRelease; + } + return kGMPMessageInvalid; +} + +void +WidevineDecryptor::OnSessionMessage(const char* aSessionId, + uint32_t aSessionIdSize, + MessageType aMessageType, + const char* aMessage, + uint32_t aMessageSize, + const char* aLegacyDestinationUrl, + uint32_t aLegacyDestinationUrlLength) +{ + if (!mCallback) { + Log("Decryptor::OnSessionMessage() FAIL; !mCallback"); + return; + } + Log("Decryptor::OnSessionMessage()"); + mCallback->SessionMessage(aSessionId, + aSessionIdSize, + ToGMPMessageType(aMessageType), + reinterpret_cast<const uint8_t*>(aMessage), + aMessageSize); +} + +static GMPMediaKeyStatus +ToGMPKeyStatus(KeyStatus aStatus) +{ + switch (aStatus) { + case kUsable: return kGMPUsable; + case kInternalError: return kGMPInternalError; + case kExpired: return kGMPExpired; + case kOutputRestricted: return kGMPOutputRestricted; + case kOutputDownscaled: return kGMPOutputDownscaled; + case kStatusPending: return kGMPStatusPending; + case kReleased: return kGMPReleased; + } + return kGMPUnknown; +} + +void +WidevineDecryptor::OnSessionKeysChange(const char* aSessionId, + uint32_t aSessionIdSize, + bool aHasAdditionalUsableKey, + const KeyInformation* aKeysInfo, + uint32_t aKeysInfoCount) +{ + if (!mCallback) { + Log("Decryptor::OnSessionKeysChange() FAIL; !mCallback"); + return; + } + Log("Decryptor::OnSessionKeysChange()"); + + nsTArray<GMPMediaKeyInfo> key_infos; + for (uint32_t i = 0; i < aKeysInfoCount; i++) { + key_infos.AppendElement(GMPMediaKeyInfo(aKeysInfo[i].key_id, + aKeysInfo[i].key_id_size, + ToGMPKeyStatus(aKeysInfo[i].status))); + } + mCallback->BatchedKeyStatusChanged(aSessionId, aSessionIdSize, + key_infos.Elements(), key_infos.Length()); +} + +static GMPTimestamp +ToGMPTime(Time aCDMTime) +{ + return static_cast<GMPTimestamp>(aCDMTime * 1000); +} + +void +WidevineDecryptor::OnExpirationChange(const char* aSessionId, + uint32_t aSessionIdSize, + Time aNewExpiryTime) +{ + if (!mCallback) { + Log("Decryptor::OnExpirationChange(sid=%s) t=%lf FAIL; !mCallback", + aSessionId, aNewExpiryTime); + return; + } + Log("Decryptor::OnExpirationChange(sid=%s) t=%lf", aSessionId, aNewExpiryTime); + GMPTimestamp expiry = ToGMPTime(aNewExpiryTime); + if (aNewExpiryTime == 0) { + return; + } + mCallback->ExpirationChange(aSessionId, aSessionIdSize, expiry); +} + +void +WidevineDecryptor::OnSessionClosed(const char* aSessionId, + uint32_t aSessionIdSize) +{ + if (!mCallback) { + Log("Decryptor::OnSessionClosed(sid=%s) FAIL; !mCallback", aSessionId); + return; + } + Log("Decryptor::OnSessionClosed(sid=%s)", aSessionId); + mCallback->SessionClosed(aSessionId, aSessionIdSize); +} + +void +WidevineDecryptor::OnLegacySessionError(const char* aSessionId, + uint32_t aSessionIdLength, + Error aError, + uint32_t aSystemCode, + const char* aErrorMessage, + uint32_t aErrorMessageLength) +{ + if (!mCallback) { + Log("Decryptor::OnLegacySessionError(sid=%s, error=%d) FAIL; !mCallback", + aSessionId, (int)aError); + return; + } + Log("Decryptor::OnLegacySessionError(sid=%s, error=%d)", aSessionId, (int)aError); + mCallback->SessionError(aSessionId, + aSessionIdLength, + ToGMPDOMException(aError), + aSystemCode, + aErrorMessage, + aErrorMessageLength); +} + +void +WidevineDecryptor::SendPlatformChallenge(const char* aServiceId, + uint32_t aServiceIdSize, + const char* aChallenge, + uint32_t aChallengeSize) +{ + Log("Decryptor::SendPlatformChallenge(service_id=%s)", aServiceId); +} + +void +WidevineDecryptor::EnableOutputProtection(uint32_t aDesiredProtectionMask) +{ + Log("Decryptor::EnableOutputProtection(mask=0x%x)", aDesiredProtectionMask); +} + +void +WidevineDecryptor::QueryOutputProtectionStatus() +{ + Log("Decryptor::QueryOutputProtectionStatus()"); +} + +void +WidevineDecryptor::OnDeferredInitializationDone(StreamType aStreamType, + Status aDecoderStatus) +{ + Log("Decryptor::OnDeferredInitializationDone()"); +} + +FileIO* +WidevineDecryptor::CreateFileIO(FileIOClient* aClient) +{ + Log("Decryptor::CreateFileIO()"); + if (!mPersistentStateRequired) { + return nullptr; + } + return new WidevineFileIO(aClient); +} + +} // namespace mozilla |