diff --git a/dom/media/gmp-plugin/gmp-test-decryptor.h b/dom/media/gmp-plugin/gmp-test-decryptor.h index d8bd23e6d5c1..3b17e42c5adc 100644 --- a/dom/media/gmp-plugin/gmp-test-decryptor.h +++ b/dom/media/gmp-plugin/gmp-test-decryptor.h @@ -11,12 +11,12 @@ #include #include "mozilla/Attributes.h" -class FakeDecryptor : public GMPDecryptor { +class FakeDecryptor : public GMPDecryptor7 { public: explicit FakeDecryptor(GMPDecryptorHost* aHost); - void Init(GMPDecryptorCallback* aCallback, bool, bool) override { + void Init(GMPDecryptorCallback* aCallback) override { mCallback = aCallback; } diff --git a/dom/media/gmp/GMPContentChild.cpp b/dom/media/gmp/GMPContentChild.cpp index b0f797346276..2f513c3f7c1a 100644 --- a/dom/media/gmp/GMPContentChild.cpp +++ b/dom/media/gmp/GMPContentChild.cpp @@ -109,28 +109,120 @@ GMPContentChild::DeallocPGMPVideoEncoderChild(PGMPVideoEncoderChild* aActor) return true; } +// Adapts GMPDecryptor7 to the current GMPDecryptor version. +class GMPDecryptor7BackwardsCompat : public GMPDecryptor { +public: + explicit GMPDecryptor7BackwardsCompat(GMPDecryptor7* aDecryptorV7) + : mDecryptorV7(aDecryptorV7) + { + } + + void Init(GMPDecryptorCallback* aCallback, + bool aDistinctiveIdentifierRequired, + bool aPersistentStateRequired) override + { + // Distinctive identifier and persistent state arguments not present + // in v7 interface. + mDecryptorV7->Init(aCallback); + } + + void CreateSession(uint32_t aCreateSessionToken, + uint32_t aPromiseId, + const char* aInitDataType, + uint32_t aInitDataTypeSize, + const uint8_t* aInitData, + uint32_t aInitDataSize, + GMPSessionType aSessionType) override + { + mDecryptorV7->CreateSession(aCreateSessionToken, + aPromiseId, + aInitDataType, + aInitDataTypeSize, + aInitData, + aInitDataSize, + aSessionType); + } + + void LoadSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) override + { + mDecryptorV7->LoadSession(aPromiseId, aSessionId, aSessionIdLength); + } + + void UpdateSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength, + const uint8_t* aResponse, + uint32_t aResponseSize) override + { + mDecryptorV7->UpdateSession(aPromiseId, + aSessionId, + aSessionIdLength, + aResponse, + aResponseSize); + } + + void CloseSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) override + { + mDecryptorV7->CloseSession(aPromiseId, aSessionId, aSessionIdLength); + } + + void RemoveSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) override + { + mDecryptorV7->RemoveSession(aPromiseId, aSessionId, aSessionIdLength); + } + + void SetServerCertificate(uint32_t aPromiseId, + const uint8_t* aServerCert, + uint32_t aServerCertSize) override + { + mDecryptorV7->SetServerCertificate(aPromiseId, aServerCert, aServerCertSize); + } + + void Decrypt(GMPBuffer* aBuffer, + GMPEncryptedBufferMetadata* aMetadata) override + { + mDecryptorV7->Decrypt(aBuffer, aMetadata); + } + + void DecryptingComplete() override + { + mDecryptorV7->DecryptingComplete(); + delete this; + } +private: + GMPDecryptor7* mDecryptorV7; +}; + bool GMPContentChild::RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor) { GMPDecryptorChild* child = static_cast(aActor); GMPDecryptorHost* host = static_cast(child); - void* session = nullptr; - GMPErr err = mGMPChild->GetAPI(GMP_API_DECRYPTOR, host, &session); - if (err != GMPNoErr || !session) { + void* ptr = nullptr; + GMPErr err = mGMPChild->GetAPI(GMP_API_DECRYPTOR, host, &ptr); + GMPDecryptor* decryptor = nullptr; + if (GMP_SUCCEEDED(err) && ptr) { + decryptor = static_cast(ptr); + } else if (err != GMPNoErr) { // We Adapt the previous GMPDecryptor version to the current, so that - // Gecko thinks it's only talking to the current version. Helpfully, - // v7 is ABI compatible with v8, it only has different enumerations. - // If the GMP uses a v8-only enum value in an IPDL message, the IPC - // layer will terminate, so we rev'd the API version to signal to the - // GMP that it's safe to use the new enum values. - err = mGMPChild->GetAPI(GMP_API_DECRYPTOR_BACKWARDS_COMPAT, host, &session); - if (err != GMPNoErr || !session) { + // Gecko thinks it's only talking to the current version. v7 differs + // from v9 in its Init() function arguments, and v9 has extra enumeration + // members at the end of the key status enumerations. + err = mGMPChild->GetAPI(GMP_API_DECRYPTOR_BACKWARDS_COMPAT, host, &ptr); + if (err != GMPNoErr || !ptr) { return false; } + decryptor = new GMPDecryptor7BackwardsCompat(static_cast(ptr)); } - child->Init(static_cast(session)); + child->Init(decryptor); return true; } diff --git a/dom/media/gmp/gmp-api/gmp-decryption.h b/dom/media/gmp/gmp-api/gmp-decryption.h index fc8555917d1e..0a1b129c2e93 100644 --- a/dom/media/gmp/gmp-api/gmp-decryption.h +++ b/dom/media/gmp/gmp-api/gmp-decryption.h @@ -213,8 +213,9 @@ enum GMPSessionType { kGMPSessionInvalid = 2 // Must always be last. }; -// Gecko supports the current GMPDecryptor version, and the previous. -#define GMP_API_DECRYPTOR "eme-decrypt-v8" +// Gecko supports the current GMPDecryptor version, and the obsolete +// version that the Adobe GMP still uses. +#define GMP_API_DECRYPTOR "eme-decrypt-v9" #define GMP_API_DECRYPTOR_BACKWARDS_COMPAT "eme-decrypt-v7" // API exposed by plugin library to manage decryption sessions. @@ -327,4 +328,111 @@ public: virtual ~GMPDecryptor() {} }; +// v7 is the latest decryptor version supported by the Adobe GMP. +// +// API name macro: GMP_API_DECRYPTOR_BACKWARDS_COMPAT +// Host API: GMPDecryptorHost +class GMPDecryptor7 { +public: + + // Sets the callback to use with the decryptor to return results + // to Gecko. + virtual void Init(GMPDecryptorCallback* aCallback) = 0; + + // Initiates the creation of a session given |aType| and |aInitData|, and + // the generation of a license request message. + // + // This corresponds to a MediaKeySession.generateRequest() call in JS. + // + // The GMPDecryptor must do the following, in order, upon this method + // being called: + // + // 1. Generate a sessionId to expose to JS, and call + // GMPDecryptorCallback::SetSessionId(aCreateSessionToken, sessionId...) + // with the sessionId to be exposed to JS/EME on the MediaKeySession + // object on which generateRequest() was called, and then + // 2. send any messages to JS/EME required to generate a license request + // given the supplied initData, and then + // 3. generate a license request message, and send it to JS/EME, and then + // 4. call GMPDecryptorCallback::ResolvePromise(). + // + // Note: GMPDecryptorCallback::SetSessionId(aCreateSessionToken, sessionId, ...) + // *must* be called before GMPDecryptorCallback::SendMessage(sessionId, ...) + // will work. + // + // If generating the request fails, reject aPromiseId by calling + // GMPDecryptorCallback::RejectPromise(). + virtual void CreateSession(uint32_t aCreateSessionToken, + uint32_t aPromiseId, + const char* aInitDataType, + uint32_t aInitDataTypeSize, + const uint8_t* aInitData, + uint32_t aInitDataSize, + GMPSessionType aSessionType) = 0; + + // Loads a previously loaded persistent session. + // + // This corresponds to a MediaKeySession.load() call in JS. + // + // The GMPDecryptor must do the following, in order, upon this method + // being called: + // + // 1. Send any messages to JS/EME, or read from storage, whatever is + // required to load the session, and then + // 2. if there is no session with the given sessionId loadable, call + // ResolveLoadSessionPromise(aPromiseId, false), otherwise + // 2. mark the session's keys as usable, and then + // 3. update the session's expiration, and then + // 4. call GMPDecryptorCallback::ResolveLoadSessionPromise(aPromiseId, true). + // + // If loading the session fails due to error, reject aPromiseId by calling + // GMPDecryptorCallback::RejectPromise(). + virtual void LoadSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) = 0; + + // Updates the session with |aResponse|. + // This corresponds to a MediaKeySession.update() call in JS. + virtual void UpdateSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength, + const uint8_t* aResponse, + uint32_t aResponseSize) = 0; + + // Releases the resources (keys) for the specified session. + // This corresponds to a MediaKeySession.close() call in JS. + virtual void CloseSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) = 0; + + // Removes the resources (keys) for the specified session. + // This corresponds to a MediaKeySession.remove() call in JS. + virtual void RemoveSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) = 0; + + // Resolve/reject promise on completion. + // This corresponds to a MediaKeySession.setServerCertificate() call in JS. + virtual void SetServerCertificate(uint32_t aPromiseId, + const uint8_t* aServerCert, + uint32_t aServerCertSize) = 0; + + // Asynchronously decrypts aBuffer in place. When the decryption is + // complete, GMPDecryptor should write the decrypted data back into the + // same GMPBuffer object and return it to Gecko by calling Decrypted(), + // with the GMPNoErr successcode. If decryption fails, call Decrypted() + // with a failure code, and an error event will fire on the media element. + // Note: When Decrypted() is called and aBuffer is passed back, aBuffer + // is deleted. Don't forget to call Decrypted(), as otherwise aBuffer's + // memory will leak! + virtual void Decrypt(GMPBuffer* aBuffer, + GMPEncryptedBufferMetadata* aMetadata) = 0; + + // Called when the decryption operations are complete. + // Do not call the GMPDecryptorCallback's functions after this is called. + virtual void DecryptingComplete() = 0; + + virtual ~GMPDecryptor7() {} +}; + #endif // GMP_DECRYPTION_h_ diff --git a/media/gmp-clearkey/0.1/clearkey.info.in b/media/gmp-clearkey/0.1/clearkey.info.in index 392f47f20add..8b3445d59987 100644 --- a/media/gmp-clearkey/0.1/clearkey.info.in +++ b/media/gmp-clearkey/0.1/clearkey.info.in @@ -2,9 +2,9 @@ Name: clearkey Description: ClearKey Gecko Media Plugin Version: 1 #ifdef ENABLE_WMF -APIs: eme-decrypt-v8[org.w3.clearkey], decode-audio[aac:org.w3.clearkey], decode-video[h264:org.w3.clearkey] +APIs: eme-decrypt-v9[org.w3.clearkey], decode-audio[aac:org.w3.clearkey], decode-video[h264:org.w3.clearkey] Libraries: dxva2.dll, d3d9.dll, msmpeg2vdec.dll, msmpeg2adec.dll, MSAudDecMFT.dll, evr.dll, mfheaacdec.dll, mfh264dec.dll, mfplat.dll #else -APIs: eme-decrypt-v8[org.w3.clearkey] +APIs: eme-decrypt-v9[org.w3.clearkey] Libraries: #endif