[EME] Create CDMProxyFactory
authorcalvaris@igalia.com <calvaris@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 16 Jun 2020 14:57:23 +0000 (14:57 +0000)
committercalvaris@igalia.com <calvaris@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 16 Jun 2020 14:57:23 +0000 (14:57 +0000)
https://bugs.webkit.org/show_bug.cgi?id=212695

Reviewed by Youenn Fablet.

Currently in the GStreamer port we have the CDMProxy cabled in the
player private and set to the instance. Setting this too late can
create key sync issues that are worked around. This does not seem
to be needed because it can be created in the constructor from a
CDMProxyFactory if we just use the key system that we already
have.

This patch declares a CDMProxyFactory that is used in the
CDMInstanceProxy to instantiate the proper CDMProxy in its
constructor.

CDMProxyFactoryClearKey is created and added to the GStreamer
platform CDMProxyFactories.

Initializing too fast creates flakyness in several ClearKey tests
because of asking for protected memory so we need to initialize it
in a lazy way.

No new tests, just a rework.

* platform/encryptedmedia/CDMProxy.cpp:
(WebCore::CDMProxyFactory::registerFactory):
(WebCore::CDMProxyFactory::unregisterFactory):
(WebCore::CDMProxyFactory::createCDMProxyForKeySystem):
(WebCore::CDMProxyFactory::platformRegisterFactories):
* platform/encryptedmedia/CDMProxy.h:
(WebCore::CDMProxyFactory::~CDMProxyFactory):
(WebCore::CDMInstanceProxy::CDMInstanceProxy):
* platform/encryptedmedia/clearkey/CDMClearKey.cpp:
(WebCore::CDMInstanceClearKey::CDMInstanceClearKey):
(WebCore::CDMInstanceClearKey::keySystem const):
* platform/encryptedmedia/clearkey/CDMClearKey.h:
* platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp:
(WebCore::MediaPlayerPrivateGStreamer::cdmInstanceAttached):
* platform/graphics/gstreamer/eme/CDMFactoryGStreamer.cpp:
(WebCore::CDMProxyFactory::platformRegisterFactories):
* platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp:
(WebCore::CDMProxyFactoryClearKey::singleton):
(WebCore::CDMProxyFactoryClearKey::createCDMProxy):
(WebCore::CDMProxyFactoryClearKey::supportsKeySystem):
* platform/graphics/gstreamer/eme/CDMProxyClearKey.h:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@263087 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/platform/encryptedmedia/CDMProxy.cpp
Source/WebCore/platform/encryptedmedia/CDMProxy.h
Source/WebCore/platform/encryptedmedia/clearkey/CDMClearKey.cpp
Source/WebCore/platform/encryptedmedia/clearkey/CDMClearKey.h
Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
Source/WebCore/platform/graphics/gstreamer/eme/CDMFactoryGStreamer.cpp
Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp
Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.h

index 8aebd47..fbd0fef 100644 (file)
@@ -1,3 +1,52 @@
+2020-06-16  Xabier Rodriguez Calvar  <calvaris@igalia.com>
+
+        [EME] Create CDMProxyFactory
+        https://bugs.webkit.org/show_bug.cgi?id=212695
+
+        Reviewed by Youenn Fablet.
+
+        Currently in the GStreamer port we have the CDMProxy cabled in the
+        player private and set to the instance. Setting this too late can
+        create key sync issues that are worked around. This does not seem
+        to be needed because it can be created in the constructor from a
+        CDMProxyFactory if we just use the key system that we already
+        have.
+
+        This patch declares a CDMProxyFactory that is used in the
+        CDMInstanceProxy to instantiate the proper CDMProxy in its
+        constructor.
+
+        CDMProxyFactoryClearKey is created and added to the GStreamer
+        platform CDMProxyFactories.
+
+        Initializing too fast creates flakyness in several ClearKey tests
+        because of asking for protected memory so we need to initialize it
+        in a lazy way.
+
+        No new tests, just a rework.
+
+        * platform/encryptedmedia/CDMProxy.cpp:
+        (WebCore::CDMProxyFactory::registerFactory):
+        (WebCore::CDMProxyFactory::unregisterFactory):
+        (WebCore::CDMProxyFactory::createCDMProxyForKeySystem):
+        (WebCore::CDMProxyFactory::platformRegisterFactories):
+        * platform/encryptedmedia/CDMProxy.h:
+        (WebCore::CDMProxyFactory::~CDMProxyFactory):
+        (WebCore::CDMInstanceProxy::CDMInstanceProxy):
+        * platform/encryptedmedia/clearkey/CDMClearKey.cpp:
+        (WebCore::CDMInstanceClearKey::CDMInstanceClearKey):
+        (WebCore::CDMInstanceClearKey::keySystem const):
+        * platform/encryptedmedia/clearkey/CDMClearKey.h:
+        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp:
+        (WebCore::MediaPlayerPrivateGStreamer::cdmInstanceAttached):
+        * platform/graphics/gstreamer/eme/CDMFactoryGStreamer.cpp:
+        (WebCore::CDMProxyFactory::platformRegisterFactories):
+        * platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp:
+        (WebCore::CDMProxyFactoryClearKey::singleton):
+        (WebCore::CDMProxyFactoryClearKey::createCDMProxy):
+        (WebCore::CDMProxyFactoryClearKey::supportsKeySystem):
+        * platform/graphics/gstreamer/eme/CDMProxyClearKey.h:
+
 2020-06-16  youenn fablet  <youenn@apple.com>
 
         Make sure MediaRecorder.requestData returns data with the new writer backend
index f9a067b..1c462f0 100644 (file)
 
 namespace WebCore {
 
+Vector<CDMProxyFactory*>& CDMProxyFactory::registeredFactories()
+{
+    static auto factories = makeNeverDestroyed(platformRegisterFactories());
+    return factories;
+}
+
+void CDMProxyFactory::registerFactory(CDMProxyFactory& factory)
+{
+    ASSERT(!registeredFactories().contains(&factory));
+    registeredFactories().append(&factory);
+}
+
+void CDMProxyFactory::unregisterFactory(CDMProxyFactory& factory)
+{
+    ASSERT(registeredFactories().contains(&factory));
+    registeredFactories().removeAll(&factory);
+}
+
+RefPtr<CDMProxy> CDMProxyFactory::createCDMProxyForKeySystem(const String& keySystem)
+{
+    RefPtr<CDMProxy> cdmProxy;
+    for (auto* factory : CDMProxyFactory::registeredFactories()) {
+        if (factory->supportsKeySystem(keySystem)) {
+            cdmProxy = factory->createCDMProxy(keySystem);
+            break;
+        }
+    }
+#if USE(GSTREAMER)
+    ASSERT(cdmProxy);
+#endif
+    return cdmProxy;
+}
+
+#if !USE(GSTREAMER)
+Vector<CDMProxyFactory*> CDMProxyFactory::platformRegisterFactories()
+{
+    Vector<CDMProxyFactory*> factories;
+    return factories;
+}
+#endif
+
 namespace {
 
 static String vectorToHexString(const Vector<uint8_t>& vec)
index b46ef67..f2c3e34 100644 (file)
@@ -156,6 +156,28 @@ private:
     KeyStore m_keyStore;
 };
 
+class CDMProxyFactory {
+public:
+    virtual ~CDMProxyFactory()
+    {
+        unregisterFactory(*this);
+    };
+
+    WEBCORE_EXPORT static void registerFactory(CDMProxyFactory&);
+    WEBCORE_EXPORT static void unregisterFactory(CDMProxyFactory&);
+    WEBCORE_EXPORT static WARN_UNUSED_RETURN RefPtr<CDMProxy> createCDMProxyForKeySystem(const String&);
+
+protected:
+    virtual RefPtr<CDMProxy> createCDMProxy(const String&) = 0;
+    virtual bool supportsKeySystem(const String&) = 0;
+
+private:
+    // Platform-specific function that's called when the list of
+    // registered CDMProxyFactory objects is queried for the first time.
+    static Vector<CDMProxyFactory*> platformRegisterFactories();
+    WEBCORE_EXPORT static Vector<CDMProxyFactory*>& registeredFactories();
+};
+
 class CDMInstanceSessionProxy : public CDMInstanceSession, public CanMakeWeakPtr<CDMInstanceSessionProxy, WeakPtrFactoryInitialization::Eager> {
 };
 
@@ -163,19 +185,16 @@ class CDMInstanceSessionProxy : public CDMInstanceSession, public CanMakeWeakPtr
 // from "real CDM" state changes to JS.
 class CDMInstanceProxy : public CDMInstance {
 public:
-    CDMInstanceProxy() = default;
+    explicit CDMInstanceProxy(const String& keySystem)
+    {
+        ASSERT(isMainThread());
+        m_cdmProxy = CDMProxyFactory::createCDMProxyForKeySystem(keySystem);
+        if (m_cdmProxy)
+            m_cdmProxy->setInstance(this);
+    }
     virtual ~CDMInstanceProxy() = default;
 
     // Main-thread only.
-    void setProxy(Ref<CDMProxy>&& proxy)
-    {
-        m_cdmProxy = WTFMove(proxy);
-        m_cdmProxy->setInstance(this);
-        // The CDM instance may be attached after an update(). Not
-        // recommended, but apps and the W3C test-suite do this.
-        if (m_keyStore.hasKeys())
-            m_cdmProxy->updateKeyStore(m_keyStore);
-    }
     void mergeKeysFrom(const KeyStore&);
     void removeAllKeysFrom(const KeyStore&);
 
index cf2d5f1..65322ed 100644 (file)
@@ -434,6 +434,7 @@ Optional<String> CDMPrivateClearKey::sanitizeSessionId(const String& sessionId)
     return sessionId;
 }
 
+CDMInstanceClearKey::CDMInstanceClearKey() : CDMInstanceProxy("org.w3.clearkey"_s) { };
 CDMInstanceClearKey::~CDMInstanceClearKey() = default;
 
 void CDMInstanceClearKey::initializeWithConfiguration(const CDMKeySystemConfiguration&, AllowDistinctiveIdentifiers distinctiveIdentifiers, AllowPersistentState persistentState, SuccessCallback&& callback)
index 006c45a..8bf3627 100644 (file)
@@ -93,6 +93,7 @@ public:
 
 class CDMInstanceClearKey final : public CDMInstanceProxy, public CanMakeWeakPtr<CDMInstanceClearKey> {
 public:
+    CDMInstanceClearKey();
     virtual ~CDMInstanceClearKey();
 
     // CDMInstance
index acc51a2..8701aa6 100644 (file)
@@ -3532,7 +3532,6 @@ void MediaPlayerPrivateGStreamer::cdmInstanceAttached(CDMInstance& instance)
     m_cdmInstance = reinterpret_cast<CDMInstanceProxy*>(&instance);
     RELEASE_ASSERT(m_cdmInstance);
     m_cdmInstance->setPlayer(m_player);
-    m_cdmInstance->setProxy(adoptRef(*new CDMProxyClearKey));
 
     GRefPtr<GstContext> context = adoptGRef(gst_context_new("drm-cdm-proxy", FALSE));
     GstStructure* contextStructure = gst_context_writable_structure(context.get());
index ca3757c..0f9a68d 100644 (file)
@@ -31,7 +31,7 @@
 
 #if ENABLE(ENCRYPTED_MEDIA)
 
-#include "CDMClearKey.h"
+#include "CDMProxyClearKey.h"
 
 namespace WebCore {
 
@@ -40,6 +40,14 @@ void CDMFactory::platformRegisterFactories(Vector<CDMFactory*>& factories)
     factories.append(&CDMFactoryClearKey::singleton());
 }
 
+Vector<CDMProxyFactory*> CDMProxyFactory::platformRegisterFactories()
+{
+    Vector<CDMProxyFactory*> factories;
+    factories.reserveInitialCapacity(1);
+    factories.uncheckedAppend(&CDMProxyFactoryClearKey::singleton());
+    return factories;
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(ENCRYPTED_MEDIA)
index c1b6b34..91d7b23 100644 (file)
@@ -64,9 +64,28 @@ static bool readUInt16(const uint8_t* buffer, size_t bufferSize, size_t& offset,
 
 } // namespace {}
 
+CDMProxyFactoryClearKey& CDMProxyFactoryClearKey::singleton()
+{
+    static NeverDestroyed<CDMProxyFactoryClearKey> s_factory;
+    return s_factory;
+}
+
+RefPtr<CDMProxy> CDMProxyFactoryClearKey::createCDMProxy(const String& keySystem)
+{
+    ASSERT_UNUSED(keySystem, supportsKeySystem(keySystem));
+    return adoptRef(new CDMProxyClearKey());
+}
+
+bool CDMProxyFactoryClearKey::supportsKeySystem(const String& keySystem)
+{
+    // `org.w3.clearkey` is the only supported key system.
+    return equalLettersIgnoringASCIICase(keySystem, "org.w3.clearkey");
+}
+
 CDMProxyClearKey::~CDMProxyClearKey()
 {
-    gcry_cipher_close(m_gcryHandle);
+    if (m_gCryptHandle)
+        gcry_cipher_close(*m_gCryptHandle);
 }
 
 bool CDMProxyClearKey::cencSetCounterVector(const cencDecryptContext& input)
@@ -83,7 +102,7 @@ bool CDMProxyClearKey::cencSetCounterVector(const cencDecryptContext& input)
     } else
         memcpy(ctr, input.iv, ClearKey::IVSizeInBytes);
 
-    if (gcry_error_t cipherError = gcry_cipher_setctr(m_gcryHandle, ctr, ClearKey::IVSizeInBytes)) {
+    if (gcry_error_t cipherError = gcry_cipher_setctr(gCryptHandle(), ctr, ClearKey::IVSizeInBytes)) {
 #if !LOG_DISABLED
         LOG(EME, "EME - CDMProxyClearKey - ERROR(gcry_cipher_setctr): %s", gpg_strerror(cipherError));
 #else
@@ -107,7 +126,7 @@ bool CDMProxyClearKey::cencSetDecryptionKey(const cencDecryptContext& in)
     if (!keyData)
         return false;
 
-    if (gcry_error_t cipherError = gcry_cipher_setkey(m_gcryHandle, keyData->data(), keyData->size())) {
+    if (gcry_error_t cipherError = gcry_cipher_setkey(gCryptHandle(), keyData->data(), keyData->size())) {
 #if !LOG_DISABLED
         LOG(EME, "EME - CDMProxyClearKey - ERROR(gcry_cipher_setkey): %s", gpg_strerror(cipherError));
 #else
@@ -126,7 +145,7 @@ bool CDMProxyClearKey::cencDecryptFullSample(cencDecryptContext& in)
 
     LOG(EME, "EME - CDMProxyClearKey - full-sample decryption: %zu encrypted bytes", in.encryptedBufferSizeInBytes);
 
-    if (gcry_error_t cipherError = gcry_cipher_decrypt(m_gcryHandle, in.encryptedBuffer, in.encryptedBufferSizeInBytes, 0, 0)) {
+    if (gcry_error_t cipherError = gcry_cipher_decrypt(gCryptHandle(), in.encryptedBuffer, in.encryptedBufferSizeInBytes, 0, 0)) {
 #if !LOG_DISABLED
         LOG(EME, "EME - CDMProxyClearKey - ERROR(gcry_cipher_decrypt): %s", gpg_strerror(cipherError));
 #else
@@ -170,7 +189,7 @@ bool CDMProxyClearKey::cencDecryptSubsampled(cencDecryptContext& input)
         if (subsampleNumEncryptedBytes) {
             LOG(EME, "EME - subsample index %u - %u bytes encrypted (%lu bytes left to decrypt)", subsampleIndex, subsampleNumEncryptedBytes, input.encryptedBufferSizeInBytes - encryptedBufferByteOffset);
 
-            if (gcry_error_t cipherError = gcry_cipher_decrypt(m_gcryHandle, input.encryptedBuffer + encryptedBufferByteOffset, subsampleNumEncryptedBytes, 0, 0)) {
+            if (gcry_error_t cipherError = gcry_cipher_decrypt(gCryptHandle(), input.encryptedBuffer + encryptedBufferByteOffset, subsampleNumEncryptedBytes, 0, 0)) {
 #if !LOG_DISABLED
                 LOG(EME, "EME - CDMProxyClearKey - ERROR(gcry_cipher_decrypt): %s", gpg_strerror(cipherError));
 #else
@@ -194,16 +213,21 @@ bool CDMProxyClearKey::cencDecrypt(CDMProxyClearKey::cencDecryptContext& input)
     return input.isSubsampled() ? cencDecryptSubsampled(input) : cencDecryptFullSample(input);
 }
 
-void CDMProxyClearKey::initializeGcrypt()
+gcry_cipher_hd_t& CDMProxyClearKey::gCryptHandle()
 {
-    if (gcry_error_t error = gcry_cipher_open(&m_gcryHandle, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE)) {
+    if (!m_gCryptHandle) {
+        m_gCryptHandle.emplace();
+        if (gcry_error_t error = gcry_cipher_open(&m_gCryptHandle.value(), GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE)) {
 #if !LOG_DISABLED
-        LOG(EME, "EME - CDMProxyClearKey - ERROR(gcry_cipher_open): %s", gpg_strerror(error));
+            LOG(EME, "EME - CDMProxyClearKey - ERROR(gcry_cipher_open): %s", gpg_strerror(error));
 #else
-        UNUSED_VARIABLE(error);
+            UNUSED_VARIABLE(error);
 #endif
-        RELEASE_ASSERT(false && "Should not get this far with a functional GCrypt!");
+            RELEASE_ASSERT(false && "Should not get this far with a functional GCrypt!");
+        }
     }
+
+    return *m_gCryptHandle;
 }
 
 } // namespace WebCore
index 50240c9..6f93d7a 100644 (file)
 #include "SharedBuffer.h"
 #include <gcrypt.h>
 #include <wtf/Condition.h>
+#include <wtf/Optional.h>
 #include <wtf/VectorHash.h>
 
 namespace WebCore {
 
+class CDMProxyFactoryClearKey final : public CDMProxyFactory {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    static CDMProxyFactoryClearKey& singleton();
+
+    ~CDMProxyFactoryClearKey() = default;
+
+private:
+    friend class NeverDestroyed<CDMProxyFactoryClearKey>;
+
+    CDMProxyFactoryClearKey() = default;
+    RefPtr<CDMProxy> createCDMProxy(const String&) final;
+    bool supportsKeySystem(const String&) final;
+};
+
 // This is the thread-safe API that decode threads should use to make use of a
 // platform CDM module.
 class CDMProxyClearKey final : public CDMProxy, public CanMakeWeakPtr<CDMProxyClearKey, WeakPtrFactoryInitialization::Eager> {
 public:
-    CDMProxyClearKey()
-    {
-        initializeGcrypt();
-    }
+    CDMProxyClearKey() = default;
     virtual ~CDMProxyClearKey();
 
     // FIXME: There's a lack of consistency between SharedBuffers,
@@ -88,7 +101,7 @@ public:
     bool cencDecrypt(cencDecryptContext&);
 
 private:
-    void initializeGcrypt();
+    gcry_cipher_hd_t& gCryptHandle();
 
     // FIXME: For now we only support AES in CTR mode, in the future
     // we will surely have to support more protection schemes. Can we
@@ -99,7 +112,7 @@ private:
     bool cencDecryptSubsampled(cencDecryptContext&);
 
     // FIXME: It would be nice to use something in WebCore for crypto...
-    gcry_cipher_hd_t m_gcryHandle;
+    Optional<gcry_cipher_hd_t> m_gCryptHandle { WTF::nullopt };
 };
 
 } // namespace WebCore