Bug 1278198 - Adapt Adobe GMP's obsolete GMPDecryptor interface to new interface. r=gerald

The Adobe GMP only supports up to GMPDecryptor version 7. We're now up to
version 9.  So we need to provide an adaptor to convert the old version to run
with the new interface.

MozReview-Commit-ID: 5dKreev7JMv

--HG--
extra : rebase_source : b9aa1b66ad23e9f7ddbe60b71c94c161ad974818
This commit is contained in:
Chris Pearce 2016-07-14 13:33:48 +12:00
parent 8b28b48c65
commit 30716f1d57
4 changed files with 217 additions and 17 deletions

View File

@ -11,12 +11,12 @@
#include <string>
#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;
}

View File

@ -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<GMPDecryptorChild*>(aActor);
GMPDecryptorHost* host = static_cast<GMPDecryptorHost*>(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<GMPDecryptor*>(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<GMPDecryptor7*>(ptr));
}
child->Init(static_cast<GMPDecryptor*>(session));
child->Init(decryptor);
return true;
}

View File

@ -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_

View File

@ -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