Changeset 256429 in webkit


Ignore:
Timestamp:
Feb 12, 2020 3:11:22 AM (4 years ago)
Author:
cturner@igalia.com
Message:

[EME][GStreamer] Introduce CDMProxy
https://bugs.webkit.org/show_bug.cgi?id=206730

Reviewed by Xabier Rodriguez-Calvar.

Introduce a new subclass of CDMInstance, CDMProxyInstance.

CDMInstance is a main-thread only class, its purpose is to provide
an interface that will satisfy the JavaScript EME APIs, which by
design, don't actually interact with a real DRM system, what might
also be called "The CDM Instance". That's why the naming is
misleading here, CDMInstance isn't actually an instance of a real
CDM, rather it is a facade for JavaScript.

CDMProxyInstance is a sub-class which provides two APIs,
1/ For background media threads to safely interrogate the status
of keys and to perform decryption using said keys. This API is
exposed by CDMProxy and is platform specific.
2/ For media players to safely assess the state of decryptors and
what their status is.

CDMProxy exists to allow thread-safe access to a real CDM, along
with their quirks. The two main problems GTK integrators of a CDM
have in WebKit is key management and CDM access from background
media threads.

Key management is how to manage the state of keys in a way
coherent with JavaScript, the cross-platform EME implementation,
the real CDMs themselves, and the background media
threads. CDMProxy provides a default key store interface for
sub-classes. Sub-classes can, on receipt of new information from a
real CDM, tell CDMProxy about the keys and their
statuses. CDMProxy is intended to Do The Right Thing from there,
letting JavaScript and WebCore know all the details they need to
about the new state, as well as the media decode threads when they
next come asking.

Access from background threads is ill-advised when using the
thread-unsafe CDMInstance. We used to get away with doing exactly
this, but WebCore recently became more diligent about
thread-unsafe access of main-thread-only data, and we had to do
something to fix the asserts. That something was ProxyCDM (a
terrible name, removed now), which was a hack and please forget
about it. CDMProxy can be safely passed to background threads, and
it will by default provide a thread-safe view of the key store for
background threads to interrogate the key store and perform
CDM-specific decryption / decode.

Covered by existing tests.

  • WebCore.xcodeproj/project.pbxproj: Add CDMProxy to build

definition.

  • platform/GStreamer.cmake: Add CDMProxy include directories and

implementation files.

  • platform/encryptedmedia/CDMInstance.h: Remove references to old

ProxyCDM shim. This approach was a temporary hack to avoid some
thread-safety asserts. I took the liberty of renaming to CDMProxy,
for no other reason that it felt like a better name, sorry for the
confusion.

  • platform/encryptedmedia/CDMProxy.cpp: Added.

(WebCore::Key::idAsString const):
(WebCore::Key::valueAsString const):
(WebCore::KeyStore::containsKeyID const):
(WebCore::KeyStore::merge):
(WebCore::KeyStore::allKeysAsReleased const):
(WebCore::KeyStore::addKeys):
(WebCore::KeyStore::add):
(WebCore::KeyStore::removeAllKeysFrom):
(WebCore::KeyStore::remove):
(WebCore::KeyStore::keyValue const):
(WebCore::KeyStore::convertToJSKeyStatusVector const):
(WebCore::CDMProxy::updateKeyStore):
(WebCore::CDMProxy::setInstance):
(WebCore::CDMProxy::keyValue const):
(WebCore::CDMProxy::startedWaitingForKey const):
(WebCore::CDMProxy::stoppedWaitingForKey const):
(WebCore::CDMProxy::tryWaitForKey const):
(WebCore::CDMProxy::keyAvailableUnlocked const):
(WebCore::CDMProxy::keyAvailable const):
(WebCore::CDMProxy::getOrWaitForKey const):
(WebCore::CDMInstanceProxy::startedWaitingForKey):
(WebCore::CDMInstanceProxy::stoppedWaitingForKey):
(WebCore::CDMInstanceProxy::mergeKeysFrom):
(WebCore::CDMInstanceProxy::removeAllKeysFrom):

  • platform/encryptedmedia/CDMProxy.h: Added.

(WebCore::Key::create):
(WebCore::Key::idAsSharedBuffer const):
(WebCore::Key::id const):
(WebCore::Key::value const):
(WebCore::Key::value):
(WebCore::Key::status const):
(WebCore::Key::operator==):
(WebCore::Key::addSessionReference):
(WebCore::Key::removeSessionReference):
(WebCore::Key::numSessionReferences const):
(WebCore::Key::Key):
(WebCore::KeyStore::removeAllKeys):
(WebCore::KeyStore::numKeys const):
(WebCore::KeyStore::begin):
(WebCore::KeyStore::begin const):
(WebCore::KeyStore::end):
(WebCore::KeyStore::end const):
(WebCore::KeyStore::rbegin):
(WebCore::KeyStore::rbegin const):
(WebCore::KeyStore::rend):
(WebCore::KeyStore::rend const):
(WebCore::CDMInstanceProxy::setProxy):
(WebCore::CDMInstanceProxy::proxy const):
(WebCore::CDMInstanceProxy::isWaitingForKey const):
(WebCore::CDMInstanceProxy::setPlayer):
(WebCore::CDMInstanceProxy::trackSession):

  • platform/encryptedmedia/clearkey/CDMClearKey.cpp:

(WebCore::parseLicenseFormat): Refactored to use new Key class.
(WebCore::extractKeyidsLocationFromCencInitData): Refactored to
use better named constants.
(WebCore::extractKeyidsFromCencInitData): Ditto.
(WebCore::CDMInstanceClearKey::createSession): Use the
trackSession method to allow easier monitoring of all EME
sessions.
(WebCore::CDMInstanceSessionClearKey::requestLicense): Refactor to
keep track of generated session IDs.
(WebCore::CDMInstanceSessionClearKey::updateLicense): Refactor to
use new key management classes.
(WebCore::CDMInstanceSessionClearKey::loadSession): Ditto.
(WebCore::CDMInstanceSessionClearKey::removeSessionData): Ditto.
(WebCore::ClearKeyState::keys): Deleted.
(WebCore::ClearKeyState::singleton): Deleted.
(WebCore::isolatedKey): Deleted.
(WebCore::ProxyCDMClearKey::isolatedKeys const): Deleted.
(WebCore::CDMInstanceClearKey::CDMInstanceClearKey): Deleted.
(WebCore::CDMInstanceClearKey::Key::keyIDAsString const): Deleted.
(WebCore::CDMInstanceClearKey::Key::keyValueAsString const): Deleted.
(WebCore::operator==): Deleted.
(WebCore::operator<): Deleted. It looks like we were unnecessarily
sorting the key vectors. It doesn't make any sense to impose an
ordering on keys, so I removed it.

  • platform/encryptedmedia/clearkey/CDMClearKey.h: Refactor to use

new key management classes.

  • platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.h:

Remove obsolete ProxyCDM method.

  • platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp:

(WebCore::MediaPlayerPrivateGStreamer::parseInitDataFromProtectionMessage):
Refactored out of syncMessage. The code was moved here and into
waitForCDMAttachment.
(WebCore::MediaPlayerPrivateGStreamer::waitForCDMAttachment):
Ditto.
(WebCore::MediaPlayerPrivateGStreamer::handleSyncMessage):
(WebCore::MediaPlayerPrivateGStreamer::handleMessage): Remove
waiting-for-key code, it was all broken anyway.
(WebCore::MediaPlayerPrivateGStreamer::cdmInstanceAttached):
Enforce use of CDMInstanceProxy, all GStreamer-based ports will be
using the CDMProxy derived classes from background
threads. CDMProxy's need a handle to the MediaPlayer for
waitingForKey management, so wire that in here too. Also wire the
GStreamer specific ClearKey proxy here.
(WebCore::MediaPlayerPrivateGStreamer::cdmInstanceDetached):
(WebCore::MediaPlayerPrivateGStreamer::waitingForKey const): Key
status notifications are the responsibility of the CDMProxy now.
(WebCore::MediaPlayerPrivateGStreamer::setWaitingForKey): Deleted.

  • platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h: Now

we use CDMInstanceProxy, not CDMInstance.

  • platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp:

Added. G*-specific ClearKey implementation.
(WebCore::CDMProxyClearKey::~CDMProxyClearKey):
(WebCore::CDMProxyClearKey::cencSetCounterVector):
(WebCore::CDMProxyClearKey::cencSetDecryptionKey):
(WebCore::CDMProxyClearKey::cencDecryptFullSample):
(WebCore::CDMProxyClearKey::cencDecryptSubsampled):
(WebCore::CDMProxyClearKey::cencDecrypt):
(WebCore::CDMProxyClearKey::initializeGcrypt):

  • platform/graphics/gstreamer/eme/CDMProxyClearKey.h: Added.
  • platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp:

Refactor to use CDMProxy.
(webkit_media_clear_key_decrypt_class_init):
(webkit_media_clear_key_decrypt_init):
(finalize):
(cdmProxyAttached):
(decrypt):
(handleKeyResponse): Deleted. This was a badly named method that
has been changed to cdmProxyAttached.
(findAndSetKey): Deleted.

  • platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp:

(webkit_media_common_encryption_decrypt_class_init):
(transformInPlace):
(isCDMProxyAvailable):
(getCDMProxyFromGstContext):
(attachCDMProxy):
(installCDMProxyIfNotAvailable):
(sinkEventHandler):
(changeState):
(setContext):
(): Deleted.
(isCDMInstanceAvailable): Deleted.
(queryHandler): Deleted.

  • platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h:
  • testing/MockCDMFactory.cpp:

(WebCore::MockCDMInstance::proxyCDM const): Deleted.

  • testing/MockCDMFactory.h:
Location:
trunk/Source/WebCore
Files:
4 added
15 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r256428 r256429  
     12020-02-12  Charlie Turner  <cturner@igalia.com>
     2
     3        [EME][GStreamer] Introduce CDMProxy
     4        https://bugs.webkit.org/show_bug.cgi?id=206730
     5
     6        Reviewed by Xabier Rodriguez-Calvar.
     7
     8        Introduce a new subclass of CDMInstance, CDMProxyInstance.
     9
     10        CDMInstance is a main-thread only class, its purpose is to provide
     11        an interface that will satisfy the JavaScript EME APIs, which by
     12        design, don't actually interact with a real DRM system, what might
     13        also be called "The CDM Instance". That's why the naming is
     14        misleading here, CDMInstance isn't actually an instance of a real
     15        CDM, rather it is a facade for JavaScript.
     16
     17        CDMProxyInstance is a sub-class which provides two APIs,
     18        1/ For background media threads to safely interrogate the status
     19        of keys and to perform decryption using said keys. This API is
     20        exposed by CDMProxy and is platform specific.
     21        2/ For media players to safely assess the state of decryptors and
     22        what their status is.
     23
     24        CDMProxy exists to allow thread-safe access to a real CDM, along
     25        with their quirks. The two main problems GTK integrators of a CDM
     26        have in WebKit is key management and CDM access from background
     27        media threads.
     28
     29        Key management is how to manage the state of keys in a way
     30        coherent with JavaScript, the cross-platform EME implementation,
     31        the real CDMs themselves, and the background media
     32        threads. CDMProxy provides a default key store interface for
     33        sub-classes. Sub-classes can, on receipt of new information from a
     34        real CDM, tell CDMProxy about the keys and their
     35        statuses. CDMProxy is intended to Do The Right Thing from there,
     36        letting JavaScript and WebCore know all the details they need to
     37        about the new state, as well as the media decode threads when they
     38        next come asking.
     39
     40        Access from background threads is ill-advised when using the
     41        thread-unsafe CDMInstance. We used to get away with doing exactly
     42        this, but WebCore recently became more diligent about
     43        thread-unsafe access of main-thread-only data, and we had to do
     44        something to fix the asserts. That something was ProxyCDM (a
     45        terrible name, removed now), which was a hack and please forget
     46        about it. CDMProxy can be safely passed to background threads, and
     47        it will by default provide a thread-safe view of the key store for
     48        background threads to interrogate the key store and perform
     49        CDM-specific decryption / decode.
     50
     51        Covered by existing tests.
     52
     53        * WebCore.xcodeproj/project.pbxproj: Add CDMProxy to build
     54        definition.
     55        * platform/GStreamer.cmake: Add CDMProxy include directories and
     56        implementation files.
     57        * platform/encryptedmedia/CDMInstance.h: Remove references to old
     58        ProxyCDM shim. This approach was a temporary hack to avoid some
     59        thread-safety asserts. I took the liberty of renaming to CDMProxy,
     60        for no other reason that it felt like a better name, sorry for the
     61        confusion.
     62        * platform/encryptedmedia/CDMProxy.cpp: Added.
     63        (WebCore::Key::idAsString const):
     64        (WebCore::Key::valueAsString const):
     65        (WebCore::KeyStore::containsKeyID const):
     66        (WebCore::KeyStore::merge):
     67        (WebCore::KeyStore::allKeysAsReleased const):
     68        (WebCore::KeyStore::addKeys):
     69        (WebCore::KeyStore::add):
     70        (WebCore::KeyStore::removeAllKeysFrom):
     71        (WebCore::KeyStore::remove):
     72        (WebCore::KeyStore::keyValue const):
     73        (WebCore::KeyStore::convertToJSKeyStatusVector const):
     74        (WebCore::CDMProxy::updateKeyStore):
     75        (WebCore::CDMProxy::setInstance):
     76        (WebCore::CDMProxy::keyValue const):
     77        (WebCore::CDMProxy::startedWaitingForKey const):
     78        (WebCore::CDMProxy::stoppedWaitingForKey const):
     79        (WebCore::CDMProxy::tryWaitForKey const):
     80        (WebCore::CDMProxy::keyAvailableUnlocked const):
     81        (WebCore::CDMProxy::keyAvailable const):
     82        (WebCore::CDMProxy::getOrWaitForKey const):
     83        (WebCore::CDMInstanceProxy::startedWaitingForKey):
     84        (WebCore::CDMInstanceProxy::stoppedWaitingForKey):
     85        (WebCore::CDMInstanceProxy::mergeKeysFrom):
     86        (WebCore::CDMInstanceProxy::removeAllKeysFrom):
     87        * platform/encryptedmedia/CDMProxy.h: Added.
     88        (WebCore::Key::create):
     89        (WebCore::Key::idAsSharedBuffer const):
     90        (WebCore::Key::id const):
     91        (WebCore::Key::value const):
     92        (WebCore::Key::value):
     93        (WebCore::Key::status const):
     94        (WebCore::Key::operator==):
     95        (WebCore::Key::addSessionReference):
     96        (WebCore::Key::removeSessionReference):
     97        (WebCore::Key::numSessionReferences const):
     98        (WebCore::Key::Key):
     99        (WebCore::KeyStore::removeAllKeys):
     100        (WebCore::KeyStore::numKeys const):
     101        (WebCore::KeyStore::begin):
     102        (WebCore::KeyStore::begin const):
     103        (WebCore::KeyStore::end):
     104        (WebCore::KeyStore::end const):
     105        (WebCore::KeyStore::rbegin):
     106        (WebCore::KeyStore::rbegin const):
     107        (WebCore::KeyStore::rend):
     108        (WebCore::KeyStore::rend const):
     109        (WebCore::CDMInstanceProxy::setProxy):
     110        (WebCore::CDMInstanceProxy::proxy const):
     111        (WebCore::CDMInstanceProxy::isWaitingForKey const):
     112        (WebCore::CDMInstanceProxy::setPlayer):
     113        (WebCore::CDMInstanceProxy::trackSession):
     114        * platform/encryptedmedia/clearkey/CDMClearKey.cpp:
     115        (WebCore::parseLicenseFormat): Refactored to use new Key class.
     116        (WebCore::extractKeyidsLocationFromCencInitData): Refactored to
     117        use better named constants.
     118        (WebCore::extractKeyidsFromCencInitData): Ditto.
     119        (WebCore::CDMInstanceClearKey::createSession): Use the
     120        trackSession method to allow easier monitoring of all EME
     121        sessions.
     122        (WebCore::CDMInstanceSessionClearKey::requestLicense): Refactor to
     123        keep track of generated session IDs.
     124        (WebCore::CDMInstanceSessionClearKey::updateLicense): Refactor to
     125        use new key management classes.
     126        (WebCore::CDMInstanceSessionClearKey::loadSession): Ditto.
     127        (WebCore::CDMInstanceSessionClearKey::removeSessionData): Ditto.
     128        (WebCore::ClearKeyState::keys): Deleted.
     129        (WebCore::ClearKeyState::singleton): Deleted.
     130        (WebCore::isolatedKey): Deleted.
     131        (WebCore::ProxyCDMClearKey::isolatedKeys const): Deleted.
     132        (WebCore::CDMInstanceClearKey::CDMInstanceClearKey): Deleted.
     133        (WebCore::CDMInstanceClearKey::Key::keyIDAsString const): Deleted.
     134        (WebCore::CDMInstanceClearKey::Key::keyValueAsString const): Deleted.
     135        (WebCore::operator==): Deleted.
     136        (WebCore::operator<): Deleted. It looks like we were unnecessarily
     137        sorting the key vectors. It doesn't make any sense to impose an
     138        ordering on keys, so I removed it.
     139        * platform/encryptedmedia/clearkey/CDMClearKey.h: Refactor to use
     140        new key management classes.
     141        * platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.h:
     142        Remove obsolete ProxyCDM method.
     143        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp:
     144        (WebCore::MediaPlayerPrivateGStreamer::parseInitDataFromProtectionMessage):
     145        Refactored out of syncMessage. The code was moved here and into
     146        waitForCDMAttachment.
     147        (WebCore::MediaPlayerPrivateGStreamer::waitForCDMAttachment):
     148        Ditto.
     149        (WebCore::MediaPlayerPrivateGStreamer::handleSyncMessage):
     150        (WebCore::MediaPlayerPrivateGStreamer::handleMessage): Remove
     151        waiting-for-key code, it was all broken anyway.
     152        (WebCore::MediaPlayerPrivateGStreamer::cdmInstanceAttached):
     153        Enforce use of CDMInstanceProxy, all GStreamer-based ports will be
     154        using the CDMProxy derived classes from background
     155        threads. CDMProxy's need a handle to the MediaPlayer for
     156        waitingForKey management, so wire that in here too. Also wire the
     157        GStreamer specific ClearKey proxy here.
     158        (WebCore::MediaPlayerPrivateGStreamer::cdmInstanceDetached):
     159        (WebCore::MediaPlayerPrivateGStreamer::waitingForKey const): Key
     160        status notifications are the responsibility of the CDMProxy now.
     161        (WebCore::MediaPlayerPrivateGStreamer::setWaitingForKey): Deleted.
     162        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h: Now
     163        we use CDMInstanceProxy, not CDMInstance.
     164        * platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp:
     165        Added. G*-specific ClearKey implementation.
     166        (WebCore::CDMProxyClearKey::~CDMProxyClearKey):
     167        (WebCore::CDMProxyClearKey::cencSetCounterVector):
     168        (WebCore::CDMProxyClearKey::cencSetDecryptionKey):
     169        (WebCore::CDMProxyClearKey::cencDecryptFullSample):
     170        (WebCore::CDMProxyClearKey::cencDecryptSubsampled):
     171        (WebCore::CDMProxyClearKey::cencDecrypt):
     172        (WebCore::CDMProxyClearKey::initializeGcrypt):
     173        * platform/graphics/gstreamer/eme/CDMProxyClearKey.h: Added.
     174        * platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp:
     175        Refactor to use CDMProxy.
     176        (webkit_media_clear_key_decrypt_class_init):
     177        (webkit_media_clear_key_decrypt_init):
     178        (finalize):
     179        (cdmProxyAttached):
     180        (decrypt):
     181        (handleKeyResponse): Deleted. This was a badly named method that
     182        has been changed to cdmProxyAttached.
     183        (findAndSetKey): Deleted.
     184        * platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp:
     185        (webkit_media_common_encryption_decrypt_class_init):
     186        (transformInPlace):
     187        (isCDMProxyAvailable):
     188        (getCDMProxyFromGstContext):
     189        (attachCDMProxy):
     190        (installCDMProxyIfNotAvailable):
     191        (sinkEventHandler):
     192        (changeState):
     193        (setContext):
     194        (): Deleted.
     195        (isCDMInstanceAvailable): Deleted.
     196        (queryHandler): Deleted.
     197        * platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h:
     198        * testing/MockCDMFactory.cpp:
     199        (WebCore::MockCDMInstance::proxyCDM const): Deleted.
     200        * testing/MockCDMFactory.h:
     201
    12022020-02-12  Víctor Manuel Jáquez Leal  <vjaquez@igalia.com>
    2203
  • trunk/Source/WebCore/Sources.txt

    r256417 r256429  
    18201820
    18211821platform/encryptedmedia/CDMFactory.cpp
     1822platform/encryptedmedia/CDMProxy.cpp
    18221823
    18231824platform/graphics/ANGLEWebKitBridge.cpp
  • trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj

    r256424 r256429  
    1388413884                CD94A5D11F71CB6B00F525C5 /* CDMFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CDMFactory.cpp; sourceTree = "<group>"; };
    1388513885                CD94A5D21F71CB6B00F525C5 /* CDMFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDMFactory.h; sourceTree = "<group>"; };
     13886                CD94A5CF1F71CB6100F525D5 /* CDMProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CDMProxy.cpp; sourceTree = "<group>"; };
     13887                CD94A5CF1F71CB6100F525C5 /* CDMProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDMProxy.h; sourceTree = "<group>"; };
    1388613888                CD94A5D31F71CB6C00F525C5 /* CDMMediaCapability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDMMediaCapability.h; sourceTree = "<group>"; };
    1388713889                CD94A5D41F71CB6C00F525C5 /* CDMKeyStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDMKeyStatus.h; sourceTree = "<group>"; };
     
    2626126263                                CD94A5D61F71CB6D00F525C5 /* CDMMessageType.h */,
    2626226264                                CD94A5CF1F71CB6A00F525C5 /* CDMPrivate.h */,
     26265                                CD94A5CF1F71CB6100F525D5 /* CDMProxy.cpp */,
     26266                                CD94A5CF1F71CB6100F525C5 /* CDMProxy.h */,
    2626326267                                CD94A5CC1F71CB6900F525C5 /* CDMRequirement.h */,
    2626426268                                CD94A5D01F71CB6B00F525C5 /* CDMRestrictions.h */,
  • trunk/Source/WebCore/platform/GStreamer.cmake

    r252917 r256429  
    165165
    166166    list(APPEND WebCore_SOURCES
     167        platform/encryptedmedia/CDMProxy.cpp
    167168        platform/encryptedmedia/clearkey/CDMClearKey.cpp
    168 
    169169        platform/graphics/gstreamer/eme/CDMFactoryGStreamer.cpp
     170        platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp
    170171    )
    171172
  • trunk/Source/WebCore/platform/encryptedmedia/CDMInstance.h

    r249548 r256429  
    4040
    4141class SharedBuffer;
    42 
    4342class CDMInstanceSession;
    44 class ProxyCDM;
    45 
    46 // Handle to a "real" CDM, not the JavaScript facade. This can be used
    47 // from background threads (i.e. decryptors).
    48 class ProxyCDM : public ThreadSafeRefCounted<ProxyCDM> {
    49 public:
    50     virtual ~ProxyCDM() = default;
    51 };
    52 
    5343struct CDMKeySystemConfiguration;
    5444
     
    7868    virtual const String& keySystem() const = 0;
    7969    virtual RefPtr<CDMInstanceSession> createSession() = 0;
    80     virtual RefPtr<ProxyCDM> proxyCDM() const = 0;
    8170
    8271    enum class HDCPStatus {
  • trunk/Source/WebCore/platform/encryptedmedia/clearkey/CDMClearKey.cpp

    r254334 r256429  
    3737#include "Logging.h"
    3838#include "SharedBuffer.h"
     39#include <algorithm>
     40#include <iterator>
    3941#include <wtf/JSONValues.h>
    4042#include <wtf/MainThread.h>
     
    4345
    4446namespace WebCore {
    45 
    46 // ClearKey CENC SystemID.
    47 // https://www.w3.org/TR/eme-initdata-cenc/#common-system
    48 const uint8_t clearKeyCencSystemId[] = { 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b };
    49 const unsigned clearKeyCencSystemIdSize = sizeof(clearKeyCencSystemId);
    50 const unsigned keyIdSize = 16;
    51 
    52 class ClearKeyState {
    53     using KeyStore = HashMap<String, Vector<CDMInstanceClearKey::Key>>;
    54 
    55 public:
    56     static ClearKeyState& singleton();
    57 
    58     KeyStore& keys() { return m_keys; }
    59 
    60 private:
    61     friend class NeverDestroyed<ClearKeyState>;
    62     ClearKeyState();
    63     KeyStore m_keys;
    64 };
    65 
    66 ClearKeyState& ClearKeyState::singleton()
    67 {
    68     static NeverDestroyed<ClearKeyState> s_state;
    69     return s_state;
    70 }
    71 
    72 ClearKeyState::ClearKeyState() = default;
    7347
    7448static RefPtr<JSON::Object> parseJSONObject(const SharedBuffer& buffer)
     
    8963}
    9064
    91 static Optional<Vector<CDMInstanceClearKey::Key>> parseLicenseFormat(const JSON::Object& root)
     65static Optional<Vector<RefPtr<Key>>> parseLicenseFormat(const JSON::Object& root)
    9266{
    9367    // If the 'keys' key is present in the root object, parse the JSON further
     
    10276        return WTF::nullopt;
    10377
    104     Vector<CDMInstanceClearKey::Key> decodedKeys;
     78    Vector<RefPtr<Key>> decodedKeys;
    10579    bool validFormat = std::all_of(keysArray->begin(), keysArray->end(),
    10680        [&decodedKeys] (const auto& value) {
     
    11791                return false;
    11892
    119             Vector<char> keyIDData, keyValueData;
     93            Vector<uint8_t> keyIDData, keyValueData;
    12094            if (!WTF::base64URLDecode(keyID, { keyIDData }) || !WTF::base64URLDecode(keyValue, { keyValueData }))
    12195                return false;
    12296
    123             decodedKeys.append({ CDMInstanceSession::KeyStatus::Usable, SharedBuffer::create(WTFMove(keyIDData)), SharedBuffer::create(WTFMove(keyValueData)) });
     97            decodedKeys.append(Key::create(CDMInstanceSession::KeyStatus::Usable, WTFMove(keyIDData), WTFMove(keyValueData)));
    12498            return true;
    12599        });
     
    182156
    183157        // Check the overflow InitData.
    184         if (index + 12 + clearKeyCencSystemIdSize >= initDataSize)
     158        if (index + 12 + ClearKey::cencSystemIdSize >= initDataSize)
    185159            return keyIdsMap;
    186160
     
    192166
    193167        // 12 = BMFF box header + Full box header.
    194         if (!memcmp(&data[index + 12], clearKeyCencSystemId, clearKeyCencSystemIdSize)) {
     168        if (!memcmp(&data[index + 12], ClearKey::cencSystemId, ClearKey::cencSystemIdSize)) {
    195169            foundPssh = true;
    196170            break;
     
    203177        return keyIdsMap;
    204178
    205     index += (12 + clearKeyCencSystemIdSize); // 12 (BMFF box header + Full box header) + SystemID size.
     179    index += (12 + ClearKey::cencSystemIdSize); // 12 (BMFF box header + Full box header) + SystemID size.
    206180
    207181    // Check the overflow.
     
    213187
    214188    // Check the overflow.
    215     if ((index + (keyIdsMap.first * keyIdSize)) >= initDataSize)
     189    if ((index + (keyIdsMap.first * ClearKey::KeyIDSizeInBytes)) >= initDataSize)
    216190        return keyIdsMap;
    217191
     
    252226    // An array of key IDs. Each element of the array is the base64url encoding of the octet sequence containing the key ID value.
    253227    for (unsigned i = 0; i < keyIdCount; i++) {
    254         String keyId = WTF::base64URLEncode(&data[index], keyIdSize);
     228        String keyId = WTF::base64URLEncode(&data[index], ClearKey::KeyIDSizeInBytes);
    255229        keyIdsArray->pushString(keyId);
    256         index += keyIdSize;
     230        index += ClearKey::KeyIDSizeInBytes;
    257231    }
    258232
     
    457431}
    458432
    459 // This is for thread-safety during an architectural situation that is
    460 // less than ideal. The GStreamer decryptors currently need to iterate
    461 // all known session keys to find the key data for priming
    462 // GCrypt. Ideally, all decryption would be the responsibility of
    463 // ProxyCDM object like this one. What the background GStreamer
    464 // thread was doing was getting copies (i.e. ref()'s) of SharedBuffers
    465 // created on the main-thread. With the new safety assertions in
    466 // WebKit, we can no longer do this. Instead, convert the refcounted
    467 // SharedBuffers into Strings which can be safely copied across
    468 // threads.
    469 static ProxyCDMClearKey::Key isolatedKey(const CDMInstanceClearKey::Key& key)
    470 {
    471     return { key.status, String(key.keyIDData->data(), key.keyIDData->size()), String(key.keyValueData->data(), key.keyValueData->size()) };
    472 }
    473 
    474 const Vector<ProxyCDMClearKey::Key> ProxyCDMClearKey::isolatedKeys() const
    475 {
    476     // Return the keys of all sessions, may be copied to background threads.
    477     Vector<ProxyCDMClearKey::Key> allKeys { };
    478     auto locker = holdLock(m_keysMutex);
    479     size_t initialCapacity = 0;
    480     for (auto& keyVector : ClearKeyState::singleton().keys().values())
    481         initialCapacity += keyVector.size();
    482     allKeys.reserveInitialCapacity(initialCapacity);
    483 
    484     for (auto& keyVector : ClearKeyState::singleton().keys().values()) {
    485         for (auto& key : keyVector)
    486             allKeys.uncheckedAppend(isolatedKey(key));
    487     }
    488 
    489     return allKeys;
    490 }
    491 
    492 CDMInstanceClearKey::CDMInstanceClearKey()
    493     : m_proxyCDM(adoptRef(*new ProxyCDMClearKey()))
    494 {
    495 }
    496 
    497433CDMInstanceClearKey::~CDMInstanceClearKey() = default;
    498434
     
    536472RefPtr<CDMInstanceSession> CDMInstanceClearKey::createSession()
    537473{
    538     return adoptRef(new CDMInstanceSessionClearKey());
    539 }
    540 
    541 String CDMInstanceClearKey::Key::keyIDAsString() const
    542 {
    543     return makeString("[", keyIDData->toHexString(), "]");
    544 }
    545 
    546 String CDMInstanceClearKey::Key::keyValueAsString() const
    547 {
    548     return makeString("[", keyValueData->toHexString(), "]");
    549 }
    550 
    551 bool operator==(const CDMInstanceClearKey::Key& k1, const CDMInstanceClearKey::Key& k2)
    552 {
    553     ASSERT(k1.keyIDData);
    554     ASSERT(k2.keyIDData);
    555 
    556     return *k1.keyIDData == *k2.keyIDData;
    557 }
    558 
    559 bool operator<(const CDMInstanceClearKey::Key& k1, const CDMInstanceClearKey::Key& k2)
    560 {
    561     ASSERT(k1.keyIDData);
    562     ASSERT(k2.keyIDData);
    563 
    564     if (k1.keyIDData->size() != k2.keyIDData->size())
    565         return k1.keyIDData->size() < k2.keyIDData->size();
    566 
    567     return memcmp(k1.keyIDData->data(), k2.keyIDData->data(), k1.keyIDData->size());
     474    RefPtr<CDMInstanceSession> newSession = adoptRef(new CDMInstanceSessionClearKey(*this));
     475    trackSession(newSession);
     476    return newSession;
    568477}
    569478
     
    573482    ++s_sessionIdValue;
    574483
     484    m_sessionID = String::number(s_sessionIdValue);
     485
    575486    if (equalLettersIgnoringASCIICase(initDataType, "cenc"))
    576487        initData = extractKeyidsFromCencInitData(initData.get());
     
    580491
    581492    callOnMainThread(
    582         [weakThis = makeWeakPtr(*this), callback = WTFMove(callback), initData = WTFMove(initData), sessionIdValue = s_sessionIdValue]() mutable {
     493        [weakThis = makeWeakPtr(*this), this, callback = WTFMove(callback), initData = WTFMove(initData)]() mutable {
    583494            if (!weakThis)
    584495                return;
    585496
    586             callback(WTFMove(initData), String::number(sessionIdValue), false, Succeeded);
     497            callback(WTFMove(initData), m_sessionID, false, Succeeded);
    587498        });
    588499}
     
    590501void CDMInstanceSessionClearKey::updateLicense(const String& sessionId, LicenseType, const SharedBuffer& response, LicenseUpdateCallback&& callback)
    591502{
    592     // Use a helper functor that schedules the callback dispatch, avoiding
    593     // duplicated callOnMainThread() calls.
     503#if LOG_DISABLED
     504    // We only use the sesion ID for debug logging. The verbose preprocessor checks are because
     505    // the Mac port has -Werror -Wunused-parameter.
     506    UNUSED_PARAM(sessionId);
     507#endif
    594508    auto dispatchCallback =
    595509        [this, &callback](bool sessionWasClosed, Optional<KeyStatusVector>&& changedKeys, SuccessValue succeeded) {
     
    605519    RefPtr<JSON::Object> root = parseJSONObject(response);
    606520    if (!root) {
     521        LOG(EME, "EME - ClearKey - session %s update payload was not valid JSON", sessionId.utf8().data());
    607522        dispatchCallback(false, WTF::nullopt, SuccessValue::Failed);
    608523        return;
    609524    }
    610525
    611     LOG(EME, "EME - ClearKey - updating license for session %s", sessionId.utf8().data());
     526    LOG(EME, "EME - ClearKey - updating license for session %s which currently contains %u keys", sessionId.utf8().data(), m_keyStore.numKeys());
    612527
    613528    if (auto decodedKeys = parseLicenseFormat(*root)) {
    614         // Retrieve the target Vector of Key objects for this session.
    615         // FIXME: Refactor this state management code.
    616         Vector<CDMInstanceClearKey::Key>& keyVector = ClearKeyState::singleton().keys().ensure(sessionId, [] { return Vector<CDMInstanceClearKey::Key> { }; }).iterator->value;
    617 
    618         bool keysChanged = false;
    619         for (auto& decodedKey : *decodedKeys) {
    620             LOG(EME, "EME - ClearKey - Decoded a key with ID %s and key data %s", decodedKey.keyIDAsString().utf8().data(), decodedKey.keyValueAsString().utf8().data());
    621             auto keyWithMatchingKeyID = std::find_if(keyVector.begin(), keyVector.end(),
    622                 [&decodedKey] (const CDMInstanceClearKey::Key& containedKey) {
    623                     return containedKey == decodedKey;
    624                 });
    625             if (keyWithMatchingKeyID != keyVector.end()) {
    626                 LOG(EME, "EME - ClearKey - Existing key found with data %s", keyWithMatchingKeyID->keyValueAsString().utf8().data());
    627 
    628                 if (!keyWithMatchingKeyID->hasSameKeyValue(decodedKey)) {
    629                     LOG(EME, "EME - ClearKey - Updating key since the data are different");
    630                     *keyWithMatchingKeyID = WTFMove(decodedKey);
    631                     keysChanged = true;
    632                 }
    633             } else {
    634                 LOG(EME, "EME - ClearKey - This is a new key");
    635                 keyVector.append(WTFMove(decodedKey));
    636                 keysChanged = true;
    637             }
    638         }
    639 
    640         LOG(EME, "EME - ClearKey - Update has provided %zu keys", keyVector.size());
     529        bool keysChanged = m_keyStore.addKeys(WTFMove(*decodedKeys));
     530
     531        LOG(EME, "EME - ClearKey - session %s has %u keys after update()", sessionId.utf8().data(), m_keyStore.numKeys());
    641532
    642533        Optional<KeyStatusVector> changedKeys;
    643534        if (keysChanged) {
    644             // Sort by key IDs.
    645             std::sort(keyVector.begin(), keyVector.end());
    646 
    647             KeyStatusVector keyStatusVector;
    648             keyStatusVector.reserveInitialCapacity(keyVector.size());
    649             for (auto& key : keyVector)
    650                 keyStatusVector.uncheckedAppend(std::pair<Ref<SharedBuffer>, KeyStatus> { *key.keyIDData, key.status });
    651 
    652             changedKeys = WTFMove(keyStatusVector);
     535            LOG(EME, "EME - ClearKey - session %s has changed keys", sessionId.utf8().data());
     536            m_parentInstance.mergeKeysFrom(m_keyStore);
     537            changedKeys = m_keyStore.convertToJSKeyStatusVector();
    653538        }
    654539
     
    658543
    659544    if (parseLicenseReleaseAcknowledgementFormat(*root)) {
    660         // FIXME: Retrieve the key ID information and use it to validate the keys for this sessionId.
    661         ClearKeyState::singleton().keys().remove(sessionId);
     545        LOG(EME, "EME - ClearKey - session %s release acknowledged, clearing all known keys", sessionId.utf8().data());
     546        m_parentInstance.removeAllKeysFrom(m_keyStore);
     547        m_keyStore.removeAllKeys();
    662548        dispatchCallback(true, WTF::nullopt, SuccessValue::Succeeded);
    663549        return;
    664550    }
    665551
    666     // Bail in case no format was recognized.
     552    LOG(EME, "EME - ClearKey - session %s update payload was an unrecognized format", sessionId.utf8().data());
    667553    dispatchCallback(false, WTF::nullopt, SuccessValue::Failed);
    668554}
     
    670556void CDMInstanceSessionClearKey::loadSession(LicenseType, const String& sessionId, const String&, LoadSessionCallback&& callback)
    671557{
    672     // Use a helper functor that schedules the callback dispatch, avoiding duplicated callOnMainThread() calls.
    673     auto dispatchCallback =
    674         [this, &callback](Optional<KeyStatusVector>&& existingKeys, SuccessValue success, SessionLoadFailure loadFailure) {
    675             callOnMainThread(
    676                 [weakThis = makeWeakPtr(*this), callback = WTFMove(callback), existingKeys = WTFMove(existingKeys), success, loadFailure]() mutable {
    677                     if (!weakThis)
    678                         return;
    679 
    680                     callback(WTFMove(existingKeys), WTF::nullopt, WTF::nullopt, success, loadFailure);
    681                 });
    682         };
    683 
    684     // Construct the KeyStatusVector object, representing all the known keys for this session.
    685     KeyStatusVector keyStatusVector;
    686     {
    687         auto& keys = ClearKeyState::singleton().keys();
    688         auto it = keys.find(sessionId);
    689         if (it == keys.end()) {
    690             dispatchCallback(WTF::nullopt, Failed, SessionLoadFailure::NoSessionData);
     558#ifdef NDEBUG
     559    UNUSED_PARAM(sessionId);
     560#endif
     561
     562    ASSERT(sessionId == m_sessionID);
     563    KeyStatusVector keyStatusVector = m_keyStore.convertToJSKeyStatusVector();
     564    callOnMainThread([weakThis = makeWeakPtr(*this), callback = WTFMove(callback), &keyStatusVector]() mutable {
     565        if (!weakThis)
    691566            return;
    692         }
    693 
    694         auto& keyVector = it->value;
    695         keyStatusVector.reserveInitialCapacity(keyVector.size());
    696         for (auto& key : keyVector)
    697             keyStatusVector.uncheckedAppend(std::pair<Ref<SharedBuffer>, KeyStatus> { *key.keyIDData, key.status });
    698     }
    699 
    700     dispatchCallback(WTFMove(keyStatusVector), Succeeded, SessionLoadFailure::None);
     567
     568        callback(WTFMove(keyStatusVector), WTF::nullopt, WTF::nullopt, Succeeded, SessionLoadFailure::None);
     569    });
    701570}
    702571
     
    714583void CDMInstanceSessionClearKey::removeSessionData(const String& sessionId, LicenseType, RemoveSessionDataCallback&& callback)
    715584{
    716     // Use a helper functor that schedules the callback dispatch, avoiding duplicated callOnMainThread() calls.
     585#ifdef NDEBUG
     586    UNUSED_PARAM(sessionId);
     587#endif
     588
     589    ASSERT(sessionId == m_sessionID);
     590
    717591    auto dispatchCallback =
    718592        [this, &callback](KeyStatusVector&& keyStatusVector, Optional<Ref<SharedBuffer>>&& message, SuccessValue success) {
     
    728602    // Construct the KeyStatusVector object, representing released keys, and the message in the
    729603    // 'license release' format.
    730     KeyStatusVector keyStatusVector;
     604    KeyStatusVector keyStatusVector = m_keyStore.allKeysAsReleased();
    731605    RefPtr<SharedBuffer> message;
    732606    {
    733         // Retrieve information for the given session ID, bailing if none is found.
    734         auto& keys = ClearKeyState::singleton().keys();
    735         auto it = keys.find(sessionId);
    736         if (it == keys.end()) {
    737             dispatchCallback(KeyStatusVector { }, WTF::nullopt, SuccessValue::Failed);
    738             return;
    739         }
    740 
    741         // Retrieve the Key vector, containing all the keys for this session, and
    742         // then remove the key map entry for this session.
    743         auto keyVector = WTFMove(it->value);
    744         keys.remove(it);
    745 
    746         // Construct the KeyStatusVector object, pairing key IDs with the 'released' status.
    747         keyStatusVector.reserveInitialCapacity(keyVector.size());
    748         for (auto& key : keyVector)
    749             keyStatusVector.uncheckedAppend(std::pair<Ref<SharedBuffer>, KeyStatus> { *key.keyIDData, KeyStatus::Released });
    750 
    751607        // Construct JSON that represents the 'license release' format, creating a 'kids' array
    752608        // of base64URL-encoded key IDs for all keys that were associated with this session.
     
    754610        {
    755611            auto array = JSON::Array::create();
    756             for (auto& key : keyVector) {
    757                 ASSERT(key.keyIDData->size() <= std::numeric_limits<unsigned>::max());
    758                 array->pushString(WTF::base64URLEncode(key.keyIDData->data(), static_cast<unsigned>(key.keyIDData->size())));
     612            for (const auto& key : m_keyStore) {
     613                ASSERT(key->id().size() <= std::numeric_limits<unsigned>::max());
     614                array->pushString(WTF::base64URLEncode(key->id().data(), key->id().size()));
    759615            }
    760616            rootObject->setArray("kids", WTFMove(array));
     
    767623    }
    768624
     625    m_keyStore.removeAllKeys();
    769626    dispatchCallback(WTFMove(keyStatusVector), Ref<SharedBuffer>(*message), SuccessValue::Succeeded);
    770627}
  • trunk/Source/WebCore/platform/encryptedmedia/clearkey/CDMClearKey.h

    r254334 r256429  
    3232
    3333#include "CDMFactory.h"
    34 #include "CDMInstance.h"
    3534#include "CDMInstanceSession.h"
    3635#include "CDMPrivate.h"
     36#include "CDMProxy.h"
    3737#include "SharedBuffer.h"
    3838#include <wtf/WeakPtr.h>
    3939
    4040namespace WebCore {
     41
     42namespace ClearKey {
     43
     44// ClearKey CENC SystemID.
     45// https://www.w3.org/TR/eme-initdata-cenc/#common-system
     46const uint8_t cencSystemId[] = { 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b };
     47const unsigned cencSystemIdSize = sizeof(cencSystemId);
     48enum {
     49    AES128CTRBlockSizeInBytes = 16,
     50    KeyIDSizeInBytes = 16,
     51    IVSizeInBytes = 16,
     52};
     53
     54} // namespace ClearKey
    4155
    4256class CDMFactoryClearKey final : public CDMFactory {
     
    7892};
    7993
    80 class ProxyCDMClearKey final : public ProxyCDM {
     94class CDMInstanceClearKey final : public CDMInstanceProxy, public CanMakeWeakPtr<CDMInstanceClearKey> {
    8195public:
    82     struct Key {
    83         CDMInstanceSession::KeyStatus status;
    84         String keyIDData;
    85         String keyValueData;
    86     };
    87 
    88     virtual ~ProxyCDMClearKey() = default;
    89     const Vector<Key> isolatedKeys() const;
    90 private:
    91     mutable Lock m_keysMutex;
    92 };
    93 
    94 class CDMInstanceClearKey final : public CDMInstance, public CanMakeWeakPtr<CDMInstanceClearKey> {
    95 public:
    96     CDMInstanceClearKey();
    9796    virtual ~CDMInstanceClearKey();
    9897
     98    // CDMInstance
    9999    ImplementationType implementationType() const final { return ImplementationType::ClearKey; }
    100 
    101100    SuccessValue initializeWithConfiguration(const CDMKeySystemConfiguration&) final;
    102101    SuccessValue setDistinctiveIdentifiersAllowed(bool) final;
     
    106105    const String& keySystem() const final;
    107106    RefPtr<CDMInstanceSession> createSession() final;
    108 
    109     struct Key {
    110         CDMInstanceSession::KeyStatus status;
    111         RefPtr<SharedBuffer> keyIDData;
    112         RefPtr<SharedBuffer> keyValueData;
    113 
    114         String keyIDAsString() const;
    115         String keyValueAsString() const;
    116 
    117         bool hasSameKeyValue(const Key &other)
    118         {
    119             ASSERT(keyValueData);
    120             ASSERT(other.keyValueData);
    121             return *keyValueData == *other.keyValueData;
    122         }
    123 
    124         // Two keys are equal if they have the same ID, ignoring key value and status.
    125         friend bool operator==(const Key &k1, const Key &k2);
    126         // Key's are ordered by their IDs, first by size, then by contents.
    127         friend bool operator<(const Key &k1, const Key &k2);
    128 
    129         friend bool operator!=(const Key &k1, const Key &k2) { return !(operator==(k1, k2)); }
    130         friend bool operator>(const Key &k1, const Key &k2) { return !operator==(k1, k2) && !operator<(k1, k2); }
    131         friend bool operator<=(const Key &k1, const Key &k2) { return !operator>(k1, k2); }
    132         friend bool operator>=(const Key &k1, const Key &k2) { return !operator<(k1, k2); }
    133     };
    134 
    135     RefPtr<ProxyCDM> proxyCDM() const final { return m_proxyCDM; }
    136 
    137 private:
    138     RefPtr<ProxyCDM> m_proxyCDM;
    139107};
    140108
    141109class CDMInstanceSessionClearKey final : public CDMInstanceSession, public CanMakeWeakPtr<CDMInstanceSessionClearKey> {
    142110public:
     111    CDMInstanceSessionClearKey(CDMInstanceClearKey& parent)
     112        : m_parentInstance(parent) { }
    143113    void requestLicense(LicenseType, const AtomString& initDataType, Ref<SharedBuffer>&& initData, LicenseCallback&&) final;
    144114    void updateLicense(const String&, LicenseType, const SharedBuffer&, LicenseUpdateCallback&&) final;
     
    147117    void removeSessionData(const String&, LicenseType, RemoveSessionDataCallback&&) final;
    148118    void storeRecordOfKeyUsage(const String&) final;
     119private:
     120    String m_sessionID;
     121    CDMInstanceClearKey& m_parentInstance;
     122    KeyStore m_keyStore;
    149123};
    150124
  • trunk/Source/WebCore/platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.h

    r254288 r256429  
    8181    SuccessValue setStorageDirectory(const String&) final;
    8282    RefPtr<CDMInstanceSession> createSession() final;
    83     RefPtr<ProxyCDM> proxyCDM() const final { return nullptr; }
    8483
    8584    const String& keySystem() const final;
  • trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp

    r256353 r256429  
    6666#if ENABLE(ENCRYPTED_MEDIA)
    6767#include "CDMInstance.h"
     68#include "CDMProxyClearKey.h"
    6869#include "GStreamerEMEUtilities.h"
    6970#include "SharedBuffer.h"
     
    17891790}
    17901791
     1792InitData MediaPlayerPrivateGStreamer::parseInitDataFromProtectionMessage(GstMessage* message)
     1793{
     1794    ASSERT(!isMainThread());
     1795
     1796    InitData initData;
     1797    {
     1798        LockHolder lock(m_protectionMutex);
     1799        ProtectionSystemEvents protectionSystemEvents(message);
     1800        GST_TRACE_OBJECT(pipeline(), "found %zu protection events, %zu decryptors available", protectionSystemEvents.events().size(), protectionSystemEvents.availableSystems().size());
     1801
     1802        for (auto& event : protectionSystemEvents.events()) {
     1803            const char* eventKeySystemId = nullptr;
     1804            GstBuffer* data = nullptr;
     1805            gst_event_parse_protection(event.get(), &eventKeySystemId, &data, nullptr);
     1806
     1807            initData.append({eventKeySystemId, data});
     1808            m_handledProtectionEvents.add(GST_EVENT_SEQNUM(event.get()));
     1809        }
     1810    }
     1811
     1812    return initData;
     1813}
     1814
     1815bool MediaPlayerPrivateGStreamer::waitForCDMAttachment()
     1816{
     1817    if (isMainThread()) {
     1818        GST_ERROR_OBJECT(pipeline(), "can't block the main thread waiting for a CDM instance");
     1819        ASSERT_NOT_REACHED();
     1820        return false;
     1821    }
     1822
     1823    GST_INFO_OBJECT(pipeline(), "waiting for a CDM instance");
     1824
     1825    bool didCDMAttach = false;
     1826    {
     1827        auto cdmAttachmentLocker = holdLock(m_cdmAttachmentMutex);
     1828        didCDMAttach = m_cdmAttachmentCondition.waitFor(m_cdmAttachmentMutex, 4_s, [this]() {
     1829            return isCDMAttached();
     1830        });
     1831    }
     1832
     1833    return didCDMAttach;
     1834}
     1835
    17911836bool MediaPlayerPrivateGStreamer::handleSyncMessage(GstMessage* message)
    17921837{
     
    18241869#if ENABLE(ENCRYPTED_MEDIA)
    18251870    if (!g_strcmp0(contextType, "drm-preferred-decryption-system-id")) {
    1826         if (isMainThread()) {
    1827             GST_ERROR("can't handle drm-preferred-decryption-system-id need context message in the main thread");
    1828             ASSERT_NOT_REACHED();
    1829             return false;
    1830         }
    1831         GST_DEBUG_OBJECT(pipeline(), "handling drm-preferred-decryption-system-id need context message");
    1832 
    1833         InitData initData;
    1834         {
    1835             LockHolder lock(m_protectionMutex);
    1836             ProtectionSystemEvents protectionSystemEvents(message);
    1837             GST_TRACE("found %zu protection events, %zu decryptors available", protectionSystemEvents.events().size(), protectionSystemEvents.availableSystems().size());
    1838 
    1839             for (auto& event : protectionSystemEvents.events()) {
    1840                 const char* eventKeySystemId = nullptr;
    1841                 GstBuffer* data = nullptr;
    1842                 gst_event_parse_protection(event.get(), &eventKeySystemId, &data, nullptr);
    1843 
    1844                 initData.append({eventKeySystemId, data});
    1845                 m_handledProtectionEvents.add(GST_EVENT_SEQNUM(event.get()));
    1846             }
    1847         }
    1848         initializationDataEncountered(WTFMove(initData));
    1849 
    1850         GST_INFO_OBJECT(pipeline(), "waiting for a CDM instance");
    1851 
    1852         bool didCDMAttach = false;
    1853         {
    1854             auto cdmAttachmentLocker = holdLock(m_cdmAttachmentMutex);
    1855             didCDMAttach = m_cdmAttachmentCondition.waitFor(m_cdmAttachmentMutex, 4_s, [this]() {
    1856                 return isCDMAttached();
    1857             });
    1858         }
    1859 
    1860         if (didCDMAttach && !isPlayerShuttingDown() && !m_cdmInstance->keySystem().isEmpty()) {
     1871        initializationDataEncountered(parseInitDataFromProtectionMessage(message));
     1872        bool isCDMAttached = waitForCDMAttachment();
     1873        if (isCDMAttached && !isPlayerShuttingDown() && !m_cdmInstance->keySystem().isEmpty()) {
    18611874            const char* preferredKeySystemUuid = GStreamerEMEUtilities::keySystemToUuid(m_cdmInstance->keySystem());
    18621875            GST_INFO_OBJECT(pipeline(), "working with key system %s, continuing with key system %s on %s", m_cdmInstance->keySystem().utf8().data(), preferredKeySystemUuid, GST_MESSAGE_SRC_NAME(message));
     
    18661879            gst_structure_set(contextStructure, "decryption-system-id", G_TYPE_STRING, preferredKeySystemUuid, nullptr);
    18671880            gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message)), context.get());
    1868         } else
    1869             GST_WARNING("CDM instance not initialized");
    1870 
    1871         return true;
     1881            return true;
     1882        }
     1883
     1884        GST_WARNING_OBJECT(pipeline(), "waiting for a CDM failed, no CDM available");
     1885        return false;
    18721886    }
    18731887#endif // ENABLE(ENCRYPTED_MEDIA)
     
    21982212            processMpegTsSection(section);
    21992213            gst_mpegts_section_unref(section);
    2200         }
    2201 #endif
    2202 #if ENABLE(ENCRYPTED_MEDIA)
    2203         else if (gst_structure_has_name(structure, "drm-waiting-for-key")) {
    2204             GST_DEBUG_OBJECT(pipeline(), "drm-waiting-for-key message from %s", GST_MESSAGE_SRC_NAME(message));
    2205             setWaitingForKey(true);
    2206             // FIXME: The decryptors should be able to attempt to decrypt after being created and linked in a pipeline but currently they are not and current
    2207             // architecture does not make this very easy. Fortunately, the arch will change soon and it does not pay off to fix this now with something that could be
    2208             // more convoluted. In the meantime, force attempt to decrypt when they get blocked.
    2209             attemptToDecryptWithLocalInstance();
    2210         } else if (gst_structure_has_name(structure, "drm-key-received")) {
    2211             GST_DEBUG_OBJECT(pipeline(), "drm-key-received message from %s", GST_MESSAGE_SRC_NAME(message));
    2212             setWaitingForKey(false);
    22132214        }
    22142215#endif
     
    37293730    }
    37303731
    3731     m_cdmInstance = &instance;
    3732 
    3733     GRefPtr<GstContext> context = adoptGRef(gst_context_new("drm-cdm-instance", FALSE));
     3732    m_cdmInstance = reinterpret_cast<CDMInstanceProxy*>(&instance);
     3733    RELEASE_ASSERT(m_cdmInstance);
     3734    m_cdmInstance->setPlayer(m_player);
     3735    m_cdmInstance->setProxy(adoptRef(*new CDMProxyClearKey));
     3736
     3737    GRefPtr<GstContext> context = adoptGRef(gst_context_new("drm-cdm-proxy", FALSE));
    37343738    GstStructure* contextStructure = gst_context_writable_structure(context.get());
    3735     gst_structure_set(contextStructure, "cdm-instance", G_TYPE_POINTER, m_cdmInstance->proxyCDM().get(), nullptr);
     3739    gst_structure_set(contextStructure, "cdm-proxy", G_TYPE_POINTER, m_cdmInstance->proxy().get(), nullptr);
    37363740    gst_element_set_context(GST_ELEMENT(m_pipeline.get()), context.get());
    37373741
    3738     GST_DEBUG_OBJECT(m_pipeline.get(), "CDM proxy instance %p dispatched as context", m_cdmInstance->proxyCDM().get());
     3742    GST_DEBUG_OBJECT(m_pipeline.get(), "CDM proxy instance %p dispatched as context", m_cdmInstance->proxy().get());
    37393743
    37403744    LockHolder lock(m_cdmAttachmentMutex);
     
    37583762    m_cdmInstance = nullptr;
    37593763
    3760     GRefPtr<GstContext> context = adoptGRef(gst_context_new("drm-cdm-instance", FALSE));
     3764    GRefPtr<GstContext> context = adoptGRef(gst_context_new("drm-cdm-proxy", FALSE));
    37613765    gst_element_set_context(GST_ELEMENT(m_pipeline.get()), context.get());
    37623766}
     
    37913795}
    37923796
    3793 void MediaPlayerPrivateGStreamer::setWaitingForKey(bool isWaitingForKey)
    3794 {
    3795     // We bail out if values did not change or if we are requested to not wait anymore but there are still waiting decryptors.
    3796     GST_TRACE("isWaitingForKey %s, m_isWaitingForKey %s", boolForPrinting(isWaitingForKey), boolForPrinting(m_isWaitingForKey));
    3797     if (isWaitingForKey == m_isWaitingForKey || (!isWaitingForKey && this->waitingForKey()))
    3798         return;
    3799 
    3800     m_isWaitingForKey = isWaitingForKey;
    3801     GST_DEBUG("waiting for key changed %s", boolForPrinting(m_isWaitingForKey));
    3802     m_player->waitingForKeyChanged();
    3803 }
    3804 
    38053797bool MediaPlayerPrivateGStreamer::waitingForKey() const
    38063798{
    3807     if (!m_pipeline)
     3799    if (!m_pipeline || !m_cdmInstance)
    38083800        return false;
    38093801
    3810     GstState state;
    3811     gst_element_get_state(m_pipeline.get(), &state, nullptr, 0);
    3812 
    3813     bool result = false;
    3814     GRefPtr<GstQuery> query = adoptGRef(gst_query_new_custom(GST_QUERY_CUSTOM, gst_structure_new_empty("any-decryptor-waiting-for-key")));
    3815     if (state >= GST_STATE_PAUSED) {
    3816         result = gst_element_query(m_pipeline.get(), query.get());
    3817         GST_TRACE("query result %s, on %s", boolForPrinting(result), gst_element_state_get_name(state));
    3818     } else if (state >= GST_STATE_READY) {
    3819         // Running a query in the pipeline is easier but it only works when the pipeline is set up and running, otherwise we need to inspect it and ask the decryptors directly.
    3820         GUniquePtr<GstIterator> iterator(gst_bin_iterate_recurse(GST_BIN(m_pipeline.get())));
    3821         GstIteratorResult iteratorResult;
    3822         do {
    3823             iteratorResult = gst_iterator_fold(iterator.get(), [](const GValue *item, GValue *, gpointer data) -> gboolean {
    3824                 GstElement* element = GST_ELEMENT(g_value_get_object(item));
    3825                 GstQuery* query = GST_QUERY(data);
    3826                 return !WEBKIT_IS_MEDIA_CENC_DECRYPT(element) || !gst_element_query(element, query);
    3827             }, nullptr, query.get());
    3828             if (iteratorResult == GST_ITERATOR_RESYNC)
    3829                 gst_iterator_resync(iterator.get());
    3830         } while (iteratorResult == GST_ITERATOR_RESYNC);
    3831         if (iteratorResult == GST_ITERATOR_ERROR)
    3832             GST_WARNING("iterator returned an error");
    3833         result = iteratorResult == GST_ITERATOR_OK;
    3834         GST_TRACE("iterator result %d, waiting %s", iteratorResult, boolForPrinting(result));
    3835     }
    3836 
    3837     return result;
     3802    return m_cdmInstance->isWaitingForKey();
    38383803}
    38393804#endif
  • trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h

    r254682 r256429  
    7575#include "TextureMapperPlatformLayerProxyProvider.h"
    7676#endif
     77#endif
     78
     79#if ENABLE(ENCRYPTED_MEDIA)
     80#include "CDMProxy.h"
    7781#endif
    7882
     
    372376    Lock m_cdmAttachmentMutex;
    373377    Condition m_cdmAttachmentCondition;
    374     RefPtr<const CDMInstance> m_cdmInstance;
     378    RefPtr<CDMInstanceProxy> m_cdmInstance;
    375379
    376380    Lock m_protectionMutex; // Guards access to m_handledProtectionEvents.
     
    451455    void attemptToDecryptWithLocalInstance();
    452456    void initializationDataEncountered(InitData&&);
    453     void setWaitingForKey(bool);
     457    InitData parseInitDataFromProtectionMessage(GstMessage*);
     458    bool waitForCDMAttachment();
    454459#endif
    455460
  • trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp

    r253367 r256429  
    2626
    2727#include "CDMClearKey.h"
     28#include "CDMProxyClearKey.h"
    2829#include "GStreamerCommon.h"
    2930#include "GStreamerEMEUtilities.h"
     
    3839#define WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_CK_DECRYPT, WebKitMediaClearKeyDecryptPrivate))
    3940struct _WebKitMediaClearKeyDecryptPrivate {
    40     RefPtr<ProxyCDMClearKey> proxyCDM;
    41     gcry_cipher_hd_t handle;
     41    RefPtr<CDMProxyClearKey> cdmProxy;
    4242};
    4343
    4444static void finalize(GObject*);
    45 static bool handleKeyResponse(WebKitMediaCommonEncryptionDecrypt* self, RefPtr<ProxyCDM>);
     45static bool cdmProxyAttached(WebKitMediaCommonEncryptionDecrypt* self, RefPtr<CDMProxy>);
    4646static bool decrypt(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* iv, GstBuffer* keyid, GstBuffer* sample, unsigned subSamplesCount, GstBuffer* subSamples);
    4747
     
    8585    WebKitMediaCommonEncryptionDecryptClass* cencClass = WEBKIT_MEDIA_CENC_DECRYPT_CLASS(klass);
    8686    cencClass->protectionSystemId = GStreamerEMEUtilities::s_ClearKeyUUID;
    87     cencClass->handleKeyResponse = GST_DEBUG_FUNCPTR(handleKeyResponse);
     87    cencClass->cdmProxyAttached = GST_DEBUG_FUNCPTR(cdmProxyAttached);
    8888    cencClass->decrypt = GST_DEBUG_FUNCPTR(decrypt);
    8989
     
    9494{
    9595    WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(self);
    96 
    9796    self->priv = priv;
    9897    new (priv) WebKitMediaClearKeyDecryptPrivate();
    99     if (gcry_error_t error = gcry_cipher_open(&(priv->handle), GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE)) {
    100         GST_ERROR_OBJECT(self, "Failed to create AES 128 CTR cipher handle: %s", gpg_strerror(error));
    101         ASSERT(!error);
    102     }
    10398}
    10499
     
    107102    WebKitMediaClearKeyDecrypt* self = WEBKIT_MEDIA_CK_DECRYPT(object);
    108103    WebKitMediaClearKeyDecryptPrivate* priv = self->priv;
    109     gcry_cipher_close(priv->handle);
    110104    priv->~WebKitMediaClearKeyDecryptPrivate();
    111105
     
    113107}
    114108
    115 static bool handleKeyResponse(WebKitMediaCommonEncryptionDecrypt* self, RefPtr<ProxyCDM> proxyCDM)
     109static bool cdmProxyAttached(WebKitMediaCommonEncryptionDecrypt* self, RefPtr<CDMProxy> cdmProxy)
    116110{
    117111    WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self));
    118     priv->proxyCDM = reinterpret_cast<ProxyCDMClearKey*>(proxyCDM.get());
    119     return priv->proxyCDM;
     112    priv->cdmProxy = reinterpret_cast<CDMProxyClearKey*>(cdmProxy.get());
     113    return priv->cdmProxy;
    120114}
    121115
    122 static bool findAndSetKey(WebKitMediaClearKeyDecryptPrivate* priv, const String&& keyID)
     116static bool decrypt(WebKitMediaCommonEncryptionDecrypt* self, GstBuffer* ivBuffer, GstBuffer* keyIDBuffer, GstBuffer* buffer, unsigned subsampleCount, GstBuffer* subsamplesBuffer)
    123117{
    124     String keyValue;
     118    WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self));
    125119
    126     for (const auto& key : priv->proxyCDM->isolatedKeys()) {
    127         if (key.keyIDData == keyID) {
    128             keyValue = key.keyValueData;
    129             break;
    130         }
    131     }
    132 
    133     if (keyValue.isEmpty()) {
    134         GST_ERROR_OBJECT(priv, "failed to find a decryption key");
    135         return false;
    136     }
    137 
    138     ASSERT(keyValue.sizeInBytes() == CLEARKEY_SIZE);
    139     if (gcry_error_t error = gcry_cipher_setkey(priv->handle, keyValue.characters8(), keyValue.sizeInBytes())) {
    140         GST_ERROR_OBJECT(priv, "gcry_cipher_setkey failed: %s", gpg_strerror(error));
    141         return false;
    142     }
    143 
    144     return true;
    145 }
    146 
    147 static bool decrypt(WebKitMediaCommonEncryptionDecrypt* self, GstBuffer* ivBuffer, GstBuffer* keyIDBuffer, GstBuffer* buffer, unsigned subSampleCount, GstBuffer* subSamplesBuffer)
    148 {
    149     if (!ivBuffer) {
    150         GST_ERROR_OBJECT(self, "no IV buffer");
     120    if (!ivBuffer || !keyIDBuffer || !buffer) {
     121        GST_ERROR_OBJECT(self, "invalid decrypt() parameter");
    151122        return false;
    152123    }
     
    155126    if (!mappedIVBuffer) {
    156127        GST_ERROR_OBJECT(self, "failed to map IV buffer");
    157         return false;
    158     }
    159 
    160     uint8_t ctr[CLEARKEY_SIZE];
    161     if (mappedIVBuffer->size() == 8) {
    162         memset(ctr + 8, 0, 8);
    163         memcpy(ctr, mappedIVBuffer->data(), 8);
    164     } else {
    165         ASSERT(mappedIVBuffer->size() == CLEARKEY_SIZE);
    166         memcpy(ctr, mappedIVBuffer->data(), CLEARKEY_SIZE);
    167     }
    168 
    169     WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self));
    170     gcry_error_t cipherError = gcry_cipher_setctr(priv->handle, ctr, CLEARKEY_SIZE);
    171     if (cipherError) {
    172         GST_ERROR_OBJECT(self, "gcry_cipher_setctr failed: %s", gpg_strerror(cipherError));
    173         return false;
    174     }
    175 
    176     if (!buffer) {
    177         GST_ERROR_OBJECT(self, "No buffer to decrypt");
    178128        return false;
    179129    }
     
    191141    }
    192142
    193     findAndSetKey(priv, String(mappedKeyIdBuffer->data(), mappedKeyIdBuffer->size()));
    194 
    195     unsigned position = 0;
    196     unsigned sampleIndex = 0;
    197 
    198     if (!subSampleCount) {
    199         // Full sample encryption.
    200         GST_TRACE_OBJECT(self, "full sample encryption: %zu encrypted bytes", mappedBuffer->size());
    201 
    202         // Check if the buffer is empty.
    203         if (mappedBuffer->size()) {
    204             cipherError = gcry_cipher_decrypt(priv->handle, mappedBuffer->data(), mappedBuffer->size(), 0, 0);
    205             if (cipherError) {
    206                 GST_ERROR_OBJECT(self, "full sample decryption failed: %s", gpg_strerror(cipherError));
    207                 return false;
    208             }
     143    RefPtr<GstMappedBuffer> mappedSubsamplesBuffer;
     144    CDMProxyClearKey::cencDecryptContext context;
     145    context.keyID = mappedKeyIdBuffer->data();
     146    context.keyIDSizeInBytes = mappedKeyIdBuffer->size();
     147    context.iv = mappedIVBuffer->data();
     148    context.ivSizeInBytes = mappedIVBuffer->size();
     149    context.encryptedBuffer = mappedBuffer->data();
     150    context.encryptedBufferSizeInBytes = mappedBuffer->size();
     151    context.numSubsamples = subsampleCount;
     152    if (!subsampleCount)
     153        context.subsamplesBuffer = nullptr;
     154    else {
     155        ASSERT(subsamplesBuffer);
     156        mappedSubsamplesBuffer = WebCore::GstMappedBuffer::create(subsamplesBuffer, GST_MAP_READ);
     157        if (!mappedSubsamplesBuffer) {
     158            GST_ERROR_OBJECT(self, "Failed to map subsample buffer");
     159            return false;
    209160        }
    210         return true;
     161        context.subsamplesBuffer = mappedSubsamplesBuffer->data();
     162        context.subsamplesBufferSizeInBytes = mappedSubsamplesBuffer->size();
    211163    }
    212164
    213     // Check subSamplesBuffer isn't null.
    214     if (!subSamplesBuffer) {
    215         GST_ERROR_OBJECT(self, "Error, the subSampleBuffer is null");
    216         return false;
    217     }
    218 
    219     // Subsample encryption.
    220     auto mappedSubSamplesBuffer = WebCore::GstMappedBuffer::create(subSamplesBuffer, GST_MAP_READ);
    221     if (!mappedSubSamplesBuffer) {
    222         GST_ERROR_OBJECT(self, "Failed to map subsample buffer");
    223         return false;
    224     }
    225 
    226     GUniquePtr<GstByteReader> reader(gst_byte_reader_new(mappedSubSamplesBuffer->data(), mappedSubSamplesBuffer->size()));
    227     GST_DEBUG_OBJECT(self, "position: %d, size: %zu", position, mappedBuffer->size());
    228 
    229     while (position < mappedBuffer->size()) {
    230         guint16 nBytesClear = 0;
    231         guint32 nBytesEncrypted = 0;
    232 
    233         if (sampleIndex < subSampleCount) {
    234             if (!gst_byte_reader_get_uint16_be(reader.get(), &nBytesClear)
    235                 || !gst_byte_reader_get_uint32_be(reader.get(), &nBytesEncrypted)) {
    236                 GST_DEBUG_OBJECT(self, "unsupported");
    237                 return false;
    238             }
    239             sampleIndex++;
    240         } else {
    241             nBytesClear = 0;
    242             nBytesEncrypted = mappedBuffer->size() - position;
    243         }
    244 
    245         GST_TRACE_OBJECT(self, "subsample index %u - %hu bytes clear (todo=%zu)", sampleIndex, nBytesClear, mappedBuffer->size() - position);
    246         position += nBytesClear;
    247         if (nBytesEncrypted) {
    248             GST_TRACE_OBJECT(self, "subsample index %u - %u bytes encrypted (todo=%zu)", sampleIndex, nBytesEncrypted, mappedBuffer->size() - position);
    249             cipherError = gcry_cipher_decrypt(priv->handle, mappedBuffer->data() + position, nBytesEncrypted, 0, 0);
    250             if (cipherError) {
    251                 GST_ERROR_OBJECT(self, "sub sample index %u decryption failed: %s", sampleIndex, gpg_strerror(cipherError));
    252                 return false;
    253             }
    254             position += nBytesEncrypted;
    255         }
    256     }
    257 
    258     return true;
     165    return priv->cdmProxy->cencDecrypt(context);
    259166}
    260167
  • trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp

    r249548 r256429  
    2626#if ENABLE(ENCRYPTED_MEDIA) && USE(GSTREAMER)
    2727
     28#include "CDMProxy.h"
    2829#include "GStreamerCommon.h"
    2930#include "GStreamerEMEUtilities.h"
     
    3132#include <wtf/PrintStream.h>
    3233#include <wtf/RunLoop.h>
    33 
    34 using WebCore::ProxyCDM;
     34#include <wtf/Scope.h>
     35
     36using WebCore::CDMProxy;
    3537
    3638#define WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_CENC_DECRYPT, WebKitMediaCommonEncryptionDecryptPrivate))
    3739struct _WebKitMediaCommonEncryptionDecryptPrivate {
    3840    GRefPtr<GstEvent> protectionEvent;
    39     RefPtr<ProxyCDM> proxyCDM;
    40     bool keyReceived { false };
    41     bool waitingForKey { false };
    42     Lock mutex;
    43     Condition condition;
     41    RefPtr<CDMProxy> cdmProxy;
     42
     43    Lock cdmAttachmentMutex;
     44    Condition cdmAttachmentCondition;
    4445};
     46
     47static constexpr Seconds MaxSecondsToWaitForCDMProxy = 5_s;
    4548
    4649static GstStateChangeReturn changeState(GstElement*, GstStateChange transition);
     
    4952static GstFlowReturn transformInPlace(GstBaseTransform*, GstBuffer*);
    5053static gboolean sinkEventHandler(GstBaseTransform*, GstEvent*);
    51 static gboolean queryHandler(GstBaseTransform*, GstPadDirection, GstQuery*);
    52 static bool isCDMInstanceAvailable(WebKitMediaCommonEncryptionDecrypt*);
    5354static void setContext(GstElement*, GstContext*);
    54 
    5555
    5656GST_DEBUG_CATEGORY_STATIC(webkit_media_common_encryption_decrypt_debug_category);
     
    7777    baseTransformClass->transform_ip_on_passthrough = FALSE;
    7878    baseTransformClass->sink_event = GST_DEBUG_FUNCPTR(sinkEventHandler);
    79     baseTransformClass->query = GST_DEBUG_FUNCPTR(queryHandler);
    8079
    8180    g_type_class_add_private(klass, sizeof(WebKitMediaCommonEncryptionDecryptPrivate));
     
    178177    WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(base);
    179178    WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
    180     LockHolder locker(priv->mutex);
    181 
    182     // The key might not have been received yet. Wait for it.
    183     if (!priv->keyReceived) {
    184         GST_DEBUG_OBJECT(self, "key not available yet, waiting for it");
    185         if (GST_STATE(GST_ELEMENT(self)) < GST_STATE_PAUSED || (GST_STATE_TARGET(GST_ELEMENT(self)) != GST_STATE_VOID_PENDING && GST_STATE_TARGET(GST_ELEMENT(self)) < GST_STATE_PAUSED)) {
    186             GST_ERROR_OBJECT(self, "can't process key requests in less than PAUSED state");
     179
     180    LockHolder locker(priv->cdmAttachmentMutex);
     181
     182    // The CDM instance needs to be negotiated before we can begin decryption.
     183    if (!priv->cdmProxy) {
     184        GST_DEBUG_OBJECT(self, "CDM not available, going to wait for it");
     185        priv->cdmAttachmentCondition.waitFor(priv->cdmAttachmentMutex, MaxSecondsToWaitForCDMProxy, [priv] {
     186            return priv->cdmProxy;
     187        });
     188        if (!priv->cdmProxy) {
     189            GST_ERROR_OBJECT(self, "CDMProxy was not retrieved in time");
    187190            return GST_FLOW_NOT_SUPPORTED;
    188191        }
    189         // Send "drm-cdm-instance-needed" message to the player to resend the CDMInstance if available and inform we are waiting for key.
    190         priv->waitingForKey = true;
    191         gst_element_post_message(GST_ELEMENT(self), gst_message_new_element(GST_OBJECT(self), gst_structure_new_empty("drm-waiting-for-key")));
    192 
    193         priv->condition.waitFor(priv->mutex, Seconds(5), [priv] {
    194             return priv->keyReceived;
    195         });
    196         if (!priv->keyReceived) {
    197             GST_ERROR_OBJECT(self, "key not available");
    198             return GST_FLOW_NOT_SUPPORTED;
    199         }
    200         GST_DEBUG_OBJECT(self, "key received, continuing");
    201         priv->waitingForKey = false;
    202         gst_element_post_message(GST_ELEMENT(self), gst_message_new_element(GST_OBJECT(self), gst_structure_new_empty("drm-key-received")));
     192        GST_DEBUG_OBJECT(self, "CDM now available with address %p", priv->cdmProxy.get());
    203193    }
    204194
     
    208198        return GST_FLOW_NOT_SUPPORTED;
    209199    }
     200    auto removeProtectionMetaOnReturn = makeScopeExit([buffer, protectionMeta] {
     201        gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
     202    });
    210203
    211204    unsigned ivSize;
    212205    if (!gst_structure_get_uint(protectionMeta->info, "iv_size", &ivSize)) {
    213206        GST_ERROR_OBJECT(self, "Failed to get iv_size");
    214         gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
    215207        return GST_FLOW_NOT_SUPPORTED;
    216208    }
     
    219211    if (!gst_structure_get_boolean(protectionMeta->info, "encrypted", &encrypted)) {
    220212        GST_ERROR_OBJECT(self, "Failed to get encrypted flag");
    221         gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
    222         return GST_FLOW_NOT_SUPPORTED;
    223     }
    224 
    225     if (!ivSize || !encrypted) {
    226         gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
     213        return GST_FLOW_NOT_SUPPORTED;
     214    }
     215
     216    if (!ivSize || !encrypted)
    227217        return GST_FLOW_OK;
    228     }
    229218
    230219    GST_DEBUG_OBJECT(base, "protection meta: %" GST_PTR_FORMAT, protectionMeta->info);
     
    233222    if (!gst_structure_get_uint(protectionMeta->info, "subsample_count", &subSampleCount)) {
    234223        GST_ERROR_OBJECT(self, "Failed to get subsample_count");
    235         gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
    236224        return GST_FLOW_NOT_SUPPORTED;
    237225    }
     
    243231        if (!value) {
    244232            GST_ERROR_OBJECT(self, "Failed to get subsamples");
    245             gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
    246233            return GST_FLOW_NOT_SUPPORTED;
    247234        }
    248235        subSamplesBuffer = gst_value_get_buffer(value);
     236        if (!subSamplesBuffer) {
     237            GST_ERROR_OBJECT(self, "There is no subsamples buffer, but a positive subsample count");
     238            return GST_FLOW_NOT_SUPPORTED;
     239        }
    249240    }
    250241
     
    253244    if (!value) {
    254245        GST_ERROR_OBJECT(self, "Failed to get key id for buffer");
    255         gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
    256246        return GST_FLOW_NOT_SUPPORTED;
    257247    }
     
    261251    if (!value) {
    262252        GST_ERROR_OBJECT(self, "Failed to get IV for sample");
    263         gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
    264253        return GST_FLOW_NOT_SUPPORTED;
    265254    }
     
    271260    if (!klass->decrypt(self, ivBuffer, keyIDBuffer, buffer, subSampleCount, subSamplesBuffer)) {
    272261        GST_ERROR_OBJECT(self, "Decryption failed");
    273         gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
    274         return GST_FLOW_NOT_SUPPORTED;
    275     }
    276 
    277     gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
     262        return GST_FLOW_NOT_SUPPORTED;
     263    }
     264
    278265    return GST_FLOW_OK;
    279266}
    280267
    281 static bool isCDMInstanceAvailable(WebKitMediaCommonEncryptionDecrypt* self)
    282 {
    283     WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
    284 
    285     ASSERT(priv->mutex.isLocked());
    286 
    287     if (!priv->proxyCDM) {
    288         GRefPtr<GstContext> context = adoptGRef(gst_element_get_context(GST_ELEMENT(self), "drm-cdm-instance"));
    289         // According to the GStreamer documentation, if we can't find the context, we should run a downstream query, then an upstream one and then send a bus
    290         // message. In this case that does not make a lot of sense since only the app (player) answers it, meaning that no query is going to solve it. A message
    291         // could be helpful but the player sets the context as soon as it gets the CDMInstance and if it does not have it, we have no way of asking for one as it is
    292         // something provided by crossplatform code. This means that we won't be able to answer the bus request in any way either. Summing up, neither queries nor bus
    293         // requests are useful here.
    294         if (context) {
    295             const GValue* value = gst_structure_get_value(gst_context_get_structure(context.get()), "cdm-instance");
    296             priv->proxyCDM = value ? reinterpret_cast<ProxyCDM*>(g_value_get_pointer(value)) : nullptr;
    297             if (priv->proxyCDM)
    298                 GST_DEBUG_OBJECT(self, "received a new CDM proxy instance %p, refcount %u", priv->proxyCDM.get(), priv->proxyCDM->refCount());
    299             else
    300                 GST_TRACE_OBJECT(self, "CDM instance was detached");
    301         }
    302     }
    303 
    304     GST_TRACE_OBJECT(self, "CDM instance available %s", boolForPrinting(priv->proxyCDM.get()));
    305     return priv->proxyCDM;
     268static bool isCDMProxyAvailable(WebKitMediaCommonEncryptionDecrypt* self)
     269{
     270    WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
     271    auto locker = holdLock(priv->cdmAttachmentMutex);
     272    return priv->cdmProxy;
     273}
     274
     275static CDMProxy* getCDMProxyFromGstContext(WebKitMediaCommonEncryptionDecrypt* self)
     276{
     277    GRefPtr<GstContext> context = adoptGRef(gst_element_get_context(GST_ELEMENT(self), "drm-cdm-proxy"));
     278    CDMProxy* proxy = nullptr;
     279
     280    // According to the GStreamer documentation, if we can't find the context, we should run a downstream query, then an upstream one and then send a bus
     281    // message. In this case that does not make a lot of sense since only the app (player) answers it, meaning that no query is going to solve it. A message
     282    // could be helpful but the player sets the context as soon as it gets the CDMInstance and if it does not have it, we have no way of asking for one as it is
     283    // something provided by crossplatform code. This means that we won't be able to answer the bus request in any way either. Summing up, neither queries nor bus
     284    // requests are useful here.
     285    if (context) {
     286        const GValue* value = gst_structure_get_value(gst_context_get_structure(context.get()), "cdm-proxy");
     287        if (value) {
     288            proxy = reinterpret_cast<CDMProxy*>(g_value_get_pointer(value));
     289            if (proxy) {
     290                GST_DEBUG_OBJECT(self, "received a new CDM proxy instance %p, refcount %u", proxy, proxy->refCount());
     291                return proxy;
     292            }
     293        }
     294        GST_TRACE_OBJECT(self, "CDMProxy not available in the context");
     295    }
     296    return nullptr;
     297}
     298
     299static void attachCDMProxy(WebKitMediaCommonEncryptionDecrypt* self, CDMProxy* proxy)
     300{
     301    WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
     302    WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self);
     303
     304    auto locker = holdLock(priv->cdmAttachmentMutex);
     305    GST_ERROR_OBJECT(self, "Attaching CDMProxy %p", proxy);
     306    priv->cdmProxy = proxy;
     307    klass->cdmProxyAttached(self, priv->cdmProxy);
     308    priv->cdmAttachmentCondition.notifyOne();
     309}
     310
     311static gboolean installCDMProxyIfNotAvailable(WebKitMediaCommonEncryptionDecrypt* self)
     312{
     313    if (!isCDMProxyAvailable(self)) {
     314        gboolean result = FALSE;
     315
     316        CDMProxy* proxy = getCDMProxyFromGstContext(self);
     317        if (proxy) {
     318            attachCDMProxy(self, proxy);
     319            result = TRUE;
     320        } else {
     321            GST_ERROR_OBJECT(self, "Failed to retrieve CDMProxy from context");
     322            result = FALSE;
     323        }
     324        return result;
     325    }
     326
     327    return TRUE;
    306328}
    307329
     
    309331{
    310332    WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(trans);
    311     WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
    312     WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self);
    313333    gboolean result = FALSE;
    314334
     335    // FIXME: https://bugs.webkit.org/show_bug.cgi?id=191355
     336    // We should be handling protection events in this class in
     337    // addition to out-of-band data. In regular playback, after a
     338    // preferred system ID context is set, any future protection
     339    // events will not be handled by the demuxer, so they must be
     340    // handled in here.
    315341    switch (GST_EVENT_TYPE(event)) {
    316342    case GST_EVENT_CUSTOM_DOWNSTREAM_OOB: {
    317         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=191355
    318         // We should be handling protection events in this class in
    319         // addition to out-of-band data. In regular playback, after a
    320         // preferred system ID context is set, any future protection
    321         // events will not be handled by the demuxer, so the must be
    322         // handled in here.
    323         LockHolder locker(priv->mutex);
    324         if (!isCDMInstanceAvailable(self)) {
    325             GST_ERROR_OBJECT(self, "No CDM instance available");
    326             result = FALSE;
    327             break;
    328         }
    329 
    330         if (klass->handleKeyResponse(self, priv->proxyCDM)) {
    331             GST_DEBUG_OBJECT(self, "key received");
    332             priv->keyReceived = true;
    333             priv->condition.notifyOne();
    334         }
    335 
     343        ASSERT(gst_event_has_name(event, "attempt-to-decrypt"));
     344        GST_DEBUG_OBJECT(self, "Handling attempt-to-decrypt");
     345        result = installCDMProxyIfNotAvailable(self);
    336346        gst_event_unref(event);
    337         result = TRUE;
    338347        break;
    339348    }
     
    346355}
    347356
    348 static gboolean queryHandler(GstBaseTransform* trans, GstPadDirection direction, GstQuery* query)
    349 {
    350     if (gst_structure_has_name(gst_query_get_structure(query), "any-decryptor-waiting-for-key")) {
    351         WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(trans);
    352         GST_TRACE_OBJECT(trans, "any-decryptor-waiting-for-key query, waitingforkey %s", boolForPrinting(priv->waitingForKey));
    353         return priv->waitingForKey;
    354     }
    355     return GST_BASE_TRANSFORM_CLASS(parent_class)->query(trans, direction, query);
    356 }
    357 
    358357static GstStateChangeReturn changeState(GstElement* element, GstStateChange transition)
    359358{
     
    364363    case GST_STATE_CHANGE_PAUSED_TO_READY:
    365364        GST_DEBUG_OBJECT(self, "PAUSED->READY");
    366         priv->condition.notifyOne();
     365        priv->cdmAttachmentCondition.notifyOne();
    367366        break;
    368367    default:
     
    381380    WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(element);
    382381    WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
    383 
    384     if (gst_context_has_context_type(context, "drm-cdm-instance")) {
    385         const GValue* value = gst_structure_get_value(gst_context_get_structure(context), "cdm-instance");
    386         LockHolder locker(priv->mutex);
    387         priv->proxyCDM = value ? reinterpret_cast<ProxyCDM*>(g_value_get_pointer(value)) : nullptr;
    388         GST_DEBUG_OBJECT(self, "received new CDMInstance %p", priv->proxyCDM.get());
     382    WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self);
     383
     384    if (gst_context_has_context_type(context, "drm-cdm-proxy")) {
     385        const GValue* value = gst_structure_get_value(gst_context_get_structure(context), "cdm-proxy");
     386        LockHolder locker(priv->cdmAttachmentMutex);
     387        priv->cdmProxy = value ? reinterpret_cast<CDMProxy*>(g_value_get_pointer(value)) : nullptr;
     388        GST_DEBUG_OBJECT(self, "received new CDMInstance %p", priv->cdmProxy.get());
     389        klass->cdmProxyAttached(self, priv->cdmProxy);
    389390        return;
    390391    }
  • trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h

    r249548 r256429  
    2525#if ENABLE(ENCRYPTED_MEDIA) && USE(GSTREAMER)
    2626
    27 #include <CDMInstance.h>
     27#include <CDMProxy.h>
    2828#include <gst/base/gstbasetransform.h>
    2929#include <gst/gst.h>
     
    5656
    5757    const char* protectionSystemId;
    58     bool (*handleKeyResponse)(WebKitMediaCommonEncryptionDecrypt*, RefPtr<WebCore::ProxyCDM>);
    59     bool (*decrypt)(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* ivBuffer, GstBuffer* keyIDBuffer, GstBuffer* buffer, unsigned subSamplesCount, GstBuffer* subSamplesBuffer);
     58    bool (*cdmProxyAttached)(WebKitMediaCommonEncryptionDecrypt*, RefPtr<WebCore::CDMProxy>);
     59    bool (*decrypt)(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* ivBuffer, GstBuffer* keyIDBuffer, GstBuffer* buffer, unsigned subsamplesCount, GstBuffer* subsamplesBuffer);
    6060};
    6161
  • trunk/Source/WebCore/testing/MockCDMFactory.cpp

    r249548 r256429  
    227227}
    228228
    229 ProxyCDMMock::~ProxyCDMMock() = default;
    230 
    231229MockCDMInstance::MockCDMInstance(WeakPtr<MockCDM> cdm)
    232230    : m_cdm(cdm)
     
    295293{
    296294    return adoptRef(new MockCDMInstanceSession(makeWeakPtr(*this)));
    297 }
    298 
    299 RefPtr<ProxyCDM> MockCDMInstance::proxyCDM() const
    300 {
    301     return adoptRef(new ProxyCDMMock());
    302295}
    303296
  • trunk/Source/WebCore/testing/MockCDMFactory.h

    r249548 r256429  
    100100};
    101101
    102 class ProxyCDMMock final : public ProxyCDM {
    103 public:
    104     virtual ~ProxyCDMMock();
    105 };
    106 
    107102class MockCDM : public CDMPrivate, public CanMakeWeakPtr<MockCDM> {
    108103    WTF_MAKE_FAST_ALLOCATED;
     
    151146    const String& keySystem() const final;
    152147    RefPtr<CDMInstanceSession> createSession() final;
    153     RefPtr<ProxyCDM> proxyCDM() const final;
    154148
    155149    WeakPtr<MockCDM> m_cdm;
Note: See TracChangeset for help on using the changeset viewer.