Bug 1278198 - Adapt Adobe GMP's obsolete GMPDecryptor interface to new interface. r=gerald
☠☠ backed out by 5119005846f3 ☠ ☠
authorChris Pearce <cpearce@mozilla.com>
Thu, 14 Jul 2016 13:33:48 +1200
changeset 346147 c9e56c91112ef3fd3c66f1b1c27c22d288ef8c31
parent 346146 1a73ee138e2322740f4099817434f162ac004313
child 346148 3ab20077e16df40c9e4a61860c69f7b1eec024da
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald
bugs1278198
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
dom/media/gmp/GMPContentChild.cpp
dom/media/gmp/gmp-api/gmp-decryption.h
media/gmp-clearkey/0.1/clearkey.info.in
--- a/dom/media/gmp/GMPContentChild.cpp
+++ b/dom/media/gmp/GMPContentChild.cpp
@@ -104,38 +104,130 @@ GMPContentChild::AllocPGMPVideoEncoderCh
 
 bool
 GMPContentChild::DeallocPGMPVideoEncoderChild(PGMPVideoEncoderChild* aActor)
 {
   static_cast<GMPVideoEncoderChild*>(aActor)->Release();
   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;
 }
 
 bool
 GMPContentChild::RecvPGMPAudioDecoderConstructor(PGMPAudioDecoderChild* aActor)
 {
   auto vdc = static_cast<GMPAudioDecoderChild*>(aActor);
--- a/dom/media/gmp/gmp-api/gmp-decryption.h
+++ b/dom/media/gmp/gmp-api/gmp-decryption.h
@@ -208,18 +208,19 @@ public:
 };
 
 enum GMPSessionType {
   kGMPTemporySession = 0,
   kGMPPersistentSession = 1,
   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.
 // When the Host requests this by calling GMPGetAPIFunc().
 //
 // API name macro: GMP_API_DECRYPTOR
 // Host API: GMPDecryptorHost
 class GMPDecryptor {
@@ -322,9 +323,116 @@ public:
 
   // Called when the decryption operations are complete.
   // Do not call the GMPDecryptorCallback's functions after this is called.
   virtual void DecryptingComplete() = 0;
 
   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_
--- a/media/gmp-clearkey/0.1/clearkey.info.in
+++ b/media/gmp-clearkey/0.1/clearkey.info.in
@@ -1,10 +1,10 @@
 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