WebKit Bugzilla
Attachment 356374 Details for
Bug 192229
: [GStreamer][EME][ClearKey] Request keys from CDMInstance rather than passing via bus messages
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-192229-20181203121005.patch (text/plain), 81.42 KB, created by
Charlie Turner
on 2018-12-03 04:10:07 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Charlie Turner
Created:
2018-12-03 04:10:07 PST
Size:
81.42 KB
patch
obsolete
>Subversion Revision: 238143 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 9ef3c8454b2d65f69b3fa08ab577ad142f2fd740..f405efbd82a4d23f2790fc1bd48bb679931971ba 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,92 @@ >+2018-11-30 Charlie Turner <cturner@igalia.com> >+ >+ [EME] Create and move to a decryption API implemented by CDMInstances >+ https://bugs.webkit.org/show_bug.cgi?id=192229 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ No new tests, this is a refactoring. >+ >+ * platform/GStreamer.cmake: Remove the ClearKey decryptor, this >+ patch moves to a single-decryptor architecture. >+ * platform/encryptedmedia/CDMInstance.h: Introduce new abstraction >+ for handling decryption behind the CDMInstance interface. >+ (WebCore::CDMInstance::SubSample::SubSample): CDMs can perform >+ efficient decryption of subsample encrypted data without having to >+ perform extra copies. Therefore it makes sense to let the CDM >+ handle subsamples, rather than the decryptors. For ClearKey this >+ doesn't help, but for other systems it can. >+ (WebCore::CDMInstance::DecryptionConfig::createCencConfig): >+ (WebCore::CDMInstance::DecryptionConfig::DecryptionConfig): >+ Introduce decryption configuration helper class. >+ (WebCore::CDMInstance::DecryptionConfig::mode const): >+ (WebCore::CDMInstance::DecryptionConfig::iv): >+ (WebCore::CDMInstance::DecryptionConfig::keyID): >+ (WebCore::CDMInstance::DecryptionConfig::isSubSample const): >+ (WebCore::CDMInstance::DecryptionConfig::subSamples): >+ * platform/encryptedmedia/CDMInstanceSession.h: >+ (WebCore::CDMInstanceSession::decrypt): Introduce new virtual >+ method of decrypting. The default implementation returns failure >+ so that other ports don't have to be changed. >+ * platform/encryptedmedia/clearkey/CDMClearKey.cpp: Static >+ variables were cleaned up, and many useless comments that only >+ reiterating C++ in English were removed, and the glorified global >+ state via a singleton class was eliminated. >+ (WebCore::parseLicenseFormat): Update signature, since the Key >+ struct was moved to the session object, which the only place it is >+ now used. >+ (WebCore::keyIDToHexString): Helper method to pretty-print binary >+ strings, used for KeyIDs, really an abstraction for key ids with a >+ string() operator should be introduced in the future. >+ (WebCore::CDMInstanceClearKey::createSession): Adjust to keep tack >+ of which sessions have been created. This is used to look up an >+ appropriate session for decryption. >+ (WebCore::CDMInstanceClearKey::decrypt): See above. >+ (WebCore::CDMInstanceClearKey::sessionForKeyID const): Lookup a >+ session containing a given key ID. >+ (WebCore::CDMInstanceSessionClearKey::CDMInstanceSessionClearKey): >+ Hold a reference to the parent instance, this may be useful in the >+ future, and moves us more inline with the owning graph used by FPS. >+ (WebCore::CDMInstanceSessionClearKey::requestLicense): Set our >+ session id in the session instance. >+ (WebCore::CDMInstanceSessionClearKey::updateLicense): Updated to >+ assert we are invoked as the right session, and to use the new >+ m_keys member that keeps track of the key ids associated with this >+ session. Also remove more C++-in-English comments... >+ (WebCore::CDMInstanceSessionClearKey::loadSession): Ditto. >+ (WebCore::CDMInstanceSessionClearKey::removeSessionData): Ditto. >+ (WebCore::CDMInstanceSessionClearKey::decrypt): Real decryption >+ logic moved here, and refactored heavily. >+ (WebCore::CDMInstanceSessionClearKey::keyIDs): Return a vector of >+ all known key IDs *in this session* rather than globally. >+ * platform/encryptedmedia/clearkey/CDMClearKey.h: Adjusted for new >+ abstractions. >+ * platform/graphics/gstreamer/GStreamerCommon.cpp: >+ (WebCore::initializeGStreamerAndRegisterWebKitElements): Use >+ webkitcenc as our decryptor, rather than the now deleted >+ webkitclearkey. >+ * platform/graphics/gstreamer/GStreamerCommon.h: >+ (WebCore::GstMappedBuffer::sharedBuffer const): Add a new utility >+ method to convert a mapped buffer into a SharedBuffer. >+ * platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp: Removed. >+ * platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.h: Removed. >+ * platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp: >+ (webkit_media_common_encryption_decrypt_class_init): Use the new >+ CDMInstance API, and rename the long method names, since we are in >+ C++ and don't need namespacing in method names. >+ (finalize): >+ (transformCaps): >+ (populateSubSamples): >+ (transformInPlace): >+ (sinkEventHandler): >+ (queryHandler): >+ (changeState): >+ * platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h: >+ * platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp: >+ attemptToDecryptWithInstance is no longer needed, since key >+ management is handled in CDMInstance now. >+ * platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.h: Ditto. >+ > 2018-11-13 Timothy Hatcher <timothy@apple.com> > > Treat supported-color-schemes as the second highest priority property. >diff --git a/Source/WebCore/platform/GStreamer.cmake b/Source/WebCore/platform/GStreamer.cmake >index d53544a7dc7c836299c00ad953a03401ff620f06..911b6bc1f679852762e771fe3ed51eea0ae0bdaa 100644 >--- a/Source/WebCore/platform/GStreamer.cmake >+++ b/Source/WebCore/platform/GStreamer.cmake >@@ -21,7 +21,6 @@ if (ENABLE_VIDEO OR ENABLE_WEB_AUDIO) > platform/graphics/gstreamer/VideoTrackPrivateGStreamer.cpp > platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp > >- platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp > platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp > > platform/graphics/gstreamer/mse/AppendPipeline.cpp >diff --git a/Source/WebCore/platform/encryptedmedia/CDMInstance.h b/Source/WebCore/platform/encryptedmedia/CDMInstance.h >index 04e8b35c370959fef410f1887e139ef6e412230a..ed1b452de3aab07d2ed9821803b6d70355c42ff9 100644 >--- a/Source/WebCore/platform/encryptedmedia/CDMInstance.h >+++ b/Source/WebCore/platform/encryptedmedia/CDMInstance.h >@@ -30,6 +30,7 @@ > #include "CDMKeyStatus.h" > #include "CDMMessageType.h" > #include "CDMSessionType.h" >+#include "SharedBuffer.h" > #include <utility> > #include <wtf/Forward.h> > #include <wtf/Optional.h> >@@ -60,6 +61,44 @@ public: > Succeeded, > }; > >+ enum class EncryptionMode { >+ Unencrypted, >+ Cenc, >+ Cbcs, >+ }; >+ >+ struct SubSample { >+ SubSample() >+ : clearBytes(0), cipherBytes(0) { } >+ SubSample(uint32_t clearBytes, uint32_t cipherBytes) >+ : clearBytes(clearBytes), cipherBytes(cipherBytes) { } >+ uint32_t clearBytes; >+ uint32_t cipherBytes; >+ }; >+ >+ class DecryptionConfig : public RefCounted<DecryptionConfig> { >+ public: >+ static Ref<DecryptionConfig> createCencConfig(Ref<SharedBuffer>&& iv, Ref<SharedBuffer>&& keyId, Vector<SubSample>&& subSamples) >+ { >+ return adoptRef(*new DecryptionConfig(EncryptionMode::Cenc, WTFMove(iv), WTFMove(keyId), WTFMove(subSamples))); >+ } >+ >+ DecryptionConfig(const EncryptionMode& encryptionMode, Ref<SharedBuffer>&& iv, Ref<SharedBuffer>&& keyId, Vector<SubSample>&& subSamples) >+ : m_encryptionMode(encryptionMode), m_iv(WTFMove(iv)), m_keyId(WTFMove(keyId)), m_subSamples(WTFMove(subSamples)) { } >+ >+ EncryptionMode mode() const { return m_encryptionMode; } >+ Ref<SharedBuffer>& iv() { return m_iv; } >+ Ref<SharedBuffer>& keyID() { return m_keyId; } >+ bool isSubSample() const { return m_subSamples.size(); } >+ Vector<SubSample>& subSamples() { return m_subSamples; } >+ >+ private: >+ EncryptionMode m_encryptionMode; >+ Ref<SharedBuffer> m_iv; >+ Ref<SharedBuffer> m_keyId; >+ Vector<SubSample> m_subSamples; >+ }; >+ > virtual SuccessValue initializeWithConfiguration(const CDMKeySystemConfiguration&) = 0; > virtual SuccessValue setDistinctiveIdentifiersAllowed(bool) = 0; > virtual SuccessValue setPersistentStateAllowed(bool) = 0; >@@ -67,6 +106,7 @@ public: > virtual SuccessValue setStorageDirectory(const String&) = 0; > virtual const String& keySystem() const = 0; > virtual RefPtr<CDMInstanceSession> createSession() = 0; >+ virtual bool decrypt(Ref<DecryptionConfig>&&, uint8_t*, uint32_t) { return false; } > > enum class HDCPStatus { > Unknown, >diff --git a/Source/WebCore/platform/encryptedmedia/CDMInstanceSession.h b/Source/WebCore/platform/encryptedmedia/CDMInstanceSession.h >index aa48f373426de8fadc9d3001e4033ec139cdbf99..a10a3de70e50bf53eb2424f78d11ffa72d90b1b2 100644 >--- a/Source/WebCore/platform/encryptedmedia/CDMInstanceSession.h >+++ b/Source/WebCore/platform/encryptedmedia/CDMInstanceSession.h >@@ -28,6 +28,7 @@ > #if ENABLE(ENCRYPTED_MEDIA) > > #include "CDMKeyStatus.h" >+#include "CDMInstance.h" > #include "CDMMessageType.h" > #include "CDMSessionType.h" > #include <wtf/RefCounted.h> >@@ -90,6 +91,8 @@ public: > virtual void removeSessionData(const String& sessionId, LicenseType, RemoveSessionDataCallback&&) = 0; > > virtual void storeRecordOfKeyUsage(const String& sessionId) = 0; >+ >+ virtual bool decrypt(Ref<CDMInstance::DecryptionConfig>&&, uint8_t*, uint32_t) { return false; } > }; > > } // namespace WebCore >diff --git a/Source/WebCore/platform/encryptedmedia/clearkey/CDMClearKey.cpp b/Source/WebCore/platform/encryptedmedia/clearkey/CDMClearKey.cpp >index 5a7ff8631206d85d82a2c04080eae06b775cf2e2..0fce6ad8644f46810c47302d74c1fa8e34a6aedb 100644 >--- a/Source/WebCore/platform/encryptedmedia/clearkey/CDMClearKey.cpp >+++ b/Source/WebCore/platform/encryptedmedia/clearkey/CDMClearKey.cpp >@@ -34,41 +34,24 @@ > #include "CDMKeySystemConfiguration.h" > #include "CDMRestrictions.h" > #include "CDMSessionType.h" >+#include "Logging.h" > #include "SharedBuffer.h" >+#include <gcrypt.h> >+#include <wtf/Algorithms.h> > #include <wtf/JSONValues.h> > #include <wtf/MainThread.h> > #include <wtf/NeverDestroyed.h> > #include <wtf/text/Base64.h> >+#include <wtf/text/StringBuilder.h> > > namespace WebCore { > > // ClearKey CENC SystemID. > // https://www.w3.org/TR/eme-initdata-cenc/#common-system >-const uint8_t clearKeyCencSystemId[] = { 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b }; >-const unsigned clearKeyCencSystemIdSize = sizeof(clearKeyCencSystemId); >-const unsigned keyIdSize = 16; >- >-class ClearKeyState { >- using KeyStore = HashMap<String, Vector<CDMInstanceClearKey::Key>>; >- >-public: >- static ClearKeyState& singleton(); >- >- KeyStore& keys() { return m_keys; } >- >-private: >- friend class NeverDestroyed<ClearKeyState>; >- ClearKeyState(); >- KeyStore m_keys; >-}; >- >-ClearKeyState& ClearKeyState::singleton() >-{ >- static NeverDestroyed<ClearKeyState> s_state; >- return s_state; >-} >- >-ClearKeyState::ClearKeyState() = default; >+static const uint8_t clearKeyCencSystemId[] = { 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b }; >+static const unsigned clearKeyCencSystemIdSize = sizeof(clearKeyCencSystemId); >+static const unsigned keyIdSize = 16; >+static const unsigned ivSize = 16; > > static RefPtr<JSON::Object> parseJSONObject(const SharedBuffer& buffer) > { >@@ -87,7 +70,7 @@ static RefPtr<JSON::Object> parseJSONObject(const SharedBuffer& buffer) > return object; > } > >-static std::optional<Vector<CDMInstanceClearKey::Key>> parseLicenseFormat(const JSON::Object& root) >+static std::optional<Vector<CDMInstanceSessionClearKey::Key>> parseLicenseFormat(const JSON::Object& root) > { > // If the 'keys' key is present in the root object, parse the JSON further > // according to the specified 'license' format. >@@ -100,7 +83,7 @@ static std::optional<Vector<CDMInstanceClearKey::Key>> parseLicenseFormat(const > if (!it->value->asArray(keysArray)) > return std::nullopt; > >- Vector<CDMInstanceClearKey::Key> decodedKeys; >+ Vector<CDMInstanceSessionClearKey::Key> decodedKeys; > bool validFormat = std::all_of(keysArray->begin(), keysArray->end(), > [&decodedKeys] (const auto& value) { > RefPtr<JSON::Object> keyObject; >@@ -145,6 +128,7 @@ static bool parseLicenseReleaseAcknowledgementFormat(const JSON::Object& root) > return true; > } > >+// FIXME: Remove this manual parsing and instead use the ISOBox classes in WebCore. > // https://www.w3.org/TR/eme-initdata-cenc/#common-system > // 4.1 Definition > // The SystemID is 1077efec-c0b2-4d02-ace3-3c1e52e2fb4b. >@@ -288,6 +272,17 @@ static Ref<SharedBuffer> extractKeyIdFromWebMInitData(const SharedBuffer& initDa > return keyIds; > } > >+static String keyIDToHexString(const Ref<SharedBuffer>& keyID) >+{ >+ StringBuilder builder; >+ for (unsigned i = 0; i < keyID->size(); i++) { >+ builder.append(String::format("0x%2x", (uint8_t)keyID->data()[i])); >+ if (i < keyID->size() - 1) >+ builder.append(' '); >+ } >+ return builder.toString(); >+} >+ > CDMFactoryClearKey& CDMFactoryClearKey::singleton() > { > static NeverDestroyed<CDMFactoryClearKey> s_factory; >@@ -309,7 +304,6 @@ std::unique_ptr<CDMPrivate> CDMFactoryClearKey::createCDM(const String& keySyste > > bool CDMFactoryClearKey::supportsKeySystem(const String& keySystem) > { >- // `org.w3.clearkey` is the only supported key system. > return equalLettersIgnoringASCIICase(keySystem, "org.w3.clearkey"); > } > >@@ -318,7 +312,6 @@ CDMPrivateClearKey::~CDMPrivateClearKey() = default; > > bool CDMPrivateClearKey::supportsInitDataType(const AtomicString& initDataType) const > { >- // `keyids` and 'cenc' are the only supported init data type. > return (equalLettersIgnoringASCIICase(initDataType, "keyids") || equalLettersIgnoringASCIICase(initDataType, "cenc") || equalLettersIgnoringASCIICase(initDataType, "webm")); > } > >@@ -330,12 +323,9 @@ static bool containsPersistentLicenseType(const Vector<CDMSessionType>& types) > > bool CDMPrivateClearKey::supportsConfiguration(const CDMKeySystemConfiguration& configuration) const > { >- // Reject any configuration that marks distinctive identifier as required. > if (configuration.distinctiveIdentifier == CDMRequirement::Required) > return false; > >- // Reject any configuration that marks persistent state as required, unless >- // the 'persistent-license' session type has to be supported. > if (configuration.persistentState == CDMRequirement::Required && !containsPersistentLicenseType(configuration.sessionTypes)) > return false; > >@@ -344,19 +334,13 @@ bool CDMPrivateClearKey::supportsConfiguration(const CDMKeySystemConfiguration& > > bool CDMPrivateClearKey::supportsConfigurationWithRestrictions(const CDMKeySystemConfiguration& configuration, const CDMRestrictions& restrictions) const > { >- // Reject any configuration that marks distincitive identifier as required, or that marks >- // distinctive identifier as optional even when restrictions mark it as denied. > if ((configuration.distinctiveIdentifier == CDMRequirement::Optional && restrictions.distinctiveIdentifierDenied) > || configuration.distinctiveIdentifier == CDMRequirement::Required) > return false; > >- // Reject any configuration that marks persistent state as optional even when >- // restrictions mark it as denied. > if (configuration.persistentState == CDMRequirement::Optional && restrictions.persistentStateDenied) > return false; > >- // Reject any configuration that marks persistent state as required, unless >- // the 'persistent-license' session type has to be supported. > if (configuration.persistentState == CDMRequirement::Required && !containsPersistentLicenseType(configuration.sessionTypes)) > return false; > >@@ -365,7 +349,6 @@ bool CDMPrivateClearKey::supportsConfigurationWithRestrictions(const CDMKeySyste > > bool CDMPrivateClearKey::supportsSessionTypeWithConfiguration(CDMSessionType& sessionType, const CDMKeySystemConfiguration& configuration) const > { >- // Only support the 'temporary' and 'persistent-license' session types. > if (sessionType != CDMSessionType::Temporary && sessionType != CDMSessionType::PersistentLicense) > return false; > return supportsConfiguration(configuration); >@@ -373,7 +356,6 @@ bool CDMPrivateClearKey::supportsSessionTypeWithConfiguration(CDMSessionType& se > > bool CDMPrivateClearKey::supportsRobustness(const String& robustness) const > { >- // Only empty `robustness` string is supported. > return robustness.isEmpty(); > } > >@@ -387,7 +369,6 @@ CDMRequirement CDMPrivateClearKey::distinctiveIdentifiersRequirement(const CDMKe > > CDMRequirement CDMPrivateClearKey::persistentStateRequirement(const CDMKeySystemConfiguration&, const CDMRestrictions& restrictions) const > { >- // Persistent state is not allowed if it's been denied, otherwise it's optional. > if (restrictions.persistentStateDenied) > return CDMRequirement::NotAllowed; > return CDMRequirement::Optional; >@@ -405,32 +386,26 @@ RefPtr<CDMInstance> CDMPrivateClearKey::createInstance() > > void CDMPrivateClearKey::loadAndInitialize() > { >- // No-op. > } > > bool CDMPrivateClearKey::supportsServerCertificates() const > { >- // Server certificates are not supported. > return false; > } > > bool CDMPrivateClearKey::supportsSessions() const > { >- // Sessions are supported. > return true; > } > > bool CDMPrivateClearKey::supportsInitData(const AtomicString& initDataType, const SharedBuffer& initData) const > { >- // Validate the initData buffer as an JSON object in keyids case. > if (equalLettersIgnoringASCIICase(initDataType, "keyids") && parseJSONObject(initData)) > return true; > >- // Validate the initData buffer as CENC initData. > if (equalLettersIgnoringASCIICase(initDataType, "cenc") && isCencInitData(initData)) > return true; > >- // Validate the initData buffer as WebM initData. > if (equalLettersIgnoringASCIICase(initDataType, "webm") && !initData.isEmpty()) > return true; > >@@ -439,7 +414,6 @@ bool CDMPrivateClearKey::supportsInitData(const AtomicString& initDataType, cons > > RefPtr<SharedBuffer> CDMPrivateClearKey::sanitizeResponse(const SharedBuffer& response) const > { >- // Validate the response buffer as an JSON object. > if (!parseJSONObject(response)) > return nullptr; > >@@ -448,7 +422,6 @@ RefPtr<SharedBuffer> CDMPrivateClearKey::sanitizeResponse(const SharedBuffer& re > > std::optional<String> CDMPrivateClearKey::sanitizeSessionId(const String& sessionId) const > { >- // Validate the session ID string as an 32-bit integer. > bool ok; > sessionId.toUIntStrict(&ok); > if (!ok) >@@ -456,39 +429,28 @@ std::optional<String> CDMPrivateClearKey::sanitizeSessionId(const String& sessio > return sessionId; > } > >-CDMInstanceClearKey::CDMInstanceClearKey() >-{ >-} >- >-CDMInstanceClearKey::~CDMInstanceClearKey() = default; >- > CDMInstance::SuccessValue CDMInstanceClearKey::initializeWithConfiguration(const CDMKeySystemConfiguration&) > { >- // No-op. > return Succeeded; > } > > CDMInstance::SuccessValue CDMInstanceClearKey::setDistinctiveIdentifiersAllowed(bool allowed) > { >- // Reject setting distinctive identifiers as allowed. > return !allowed ? Succeeded : Failed; > } > > CDMInstance::SuccessValue CDMInstanceClearKey::setPersistentStateAllowed(bool allowed) > { >- // Reject setting persistent state as allowed. > return !allowed ? Succeeded : Failed; > } > > CDMInstance::SuccessValue CDMInstanceClearKey::setServerCertificate(Ref<SharedBuffer>&&) > { >- // Reject setting any server certificate. > return Failed; > } > > CDMInstance::SuccessValue CDMInstanceClearKey::setStorageDirectory(const String& storageDirectory) > { >- // Reject any persistent state storage. > return storageDirectory.isEmpty() ? Succeeded : Failed; > } > >@@ -501,22 +463,44 @@ const String& CDMInstanceClearKey::keySystem() const > > RefPtr<CDMInstanceSession> CDMInstanceClearKey::createSession() > { >- return adoptRef(new CDMInstanceSessionClearKey()); >+ auto session = adoptRef(*new CDMInstanceSessionClearKey(*this)); >+ m_sessions.append(makeWeakPtr(session.get())); >+ return session; > } > >-const Vector<CDMInstanceClearKey::Key> CDMInstanceClearKey::keys() const >+bool CDMInstanceClearKey::decrypt(Ref<CDMInstance::DecryptionConfig>&& config, uint8_t* encryptedBuffer, uint32_t encryptedBufferSize) > { >- // Return the keys of all sessions. >- Vector<CDMInstanceClearKey::Key> allKeys { }; >- size_t initialCapacity = 0; >- for (auto& key : ClearKeyState::singleton().keys().values()) >- initialCapacity += key.size(); >- allKeys.reserveInitialCapacity(initialCapacity); >+ LOG(EME, "EME ClearKey - decrypting, looking up session for key ID [%s]", keyIDToHexString(config->keyID()).utf8().data()); > >- for (auto& key : ClearKeyState::singleton().keys().values()) >- allKeys.appendVector(key); >+ auto* session = sessionForKeyID(config->keyID()); >+ if (!session) { >+ LOG(EME, "Failed to find matching session"); >+ return false; >+ } >+ >+ LOG(EME, "EME ClearKey - found session with address %p", session); >+ return session->decrypt(WTFMove(config), encryptedBuffer, encryptedBufferSize); >+} > >- return allKeys; >+CDMInstanceSessionClearKey* CDMInstanceClearKey::sessionForKeyID(const Ref<SharedBuffer>& keyID) const >+{ >+ for (auto& sessionInterface : m_sessions) { >+ if (!sessionInterface) >+ continue; >+ >+ auto sessionKeys = sessionInterface->keyIDs(); >+ if (anyOf(sessionKeys, [&](auto& sessionKey) { >+ return keyID == sessionKey; >+ })) >+ return sessionInterface.get(); >+ } >+ LOG(EME, "EME ClearKey - found no matching session for the given key IDs"); >+ return nullptr; >+} >+ >+CDMInstanceSessionClearKey::CDMInstanceSessionClearKey(Ref<CDMInstanceClearKey>&& instance) >+ : m_instance(WTFMove(instance)) >+{ > } > > void CDMInstanceSessionClearKey::requestLicense(LicenseType, const AtomicString& initDataType, Ref<SharedBuffer>&& initData, LicenseCallback&& callback) >@@ -524,6 +508,9 @@ void CDMInstanceSessionClearKey::requestLicense(LicenseType, const AtomicString& > static uint32_t s_sessionIdValue = 0; > ++s_sessionIdValue; > >+ m_sessionId = String::number(s_sessionIdValue); >+ LOG(EME, "EME ClearKey - new license request, our session id is %s", m_sessionId.utf8().data()); >+ > if (equalLettersIgnoringASCIICase(initDataType, "cenc")) > initData = extractKeyidsFromCencInitData(initData.get()); > >@@ -531,16 +518,19 @@ void CDMInstanceSessionClearKey::requestLicense(LicenseType, const AtomicString& > initData = extractKeyIdFromWebMInitData(initData.get()); > > callOnMainThread( >- [weakThis = makeWeakPtr(*this), callback = WTFMove(callback), initData = WTFMove(initData), sessionIdValue = s_sessionIdValue]() mutable { >+ [weakThis = makeWeakPtr(*this), callback = WTFMove(callback), initData = WTFMove(initData), sessionIdValue = m_sessionId]() mutable { > if (!weakThis) > return; > >- callback(WTFMove(initData), String::number(sessionIdValue), false, Succeeded); >+ callback(WTFMove(initData), sessionIdValue, false, Succeeded); > }); > } > > void CDMInstanceSessionClearKey::updateLicense(const String& sessionId, LicenseType, const SharedBuffer& response, LicenseUpdateCallback&& callback) > { >+ LOG(EME, "EME ClearKey - received update request for session id %s, our id is %s", sessionId.utf8().data(), m_sessionId.utf8().data()); >+ RELEASE_ASSERT(sessionId == m_sessionId); >+ > // Use a helper functor that schedules the callback dispatch, avoiding > // duplicated callOnMainThread() calls. > auto dispatchCallback = >@@ -554,58 +544,41 @@ void CDMInstanceSessionClearKey::updateLicense(const String& sessionId, LicenseT > }); > }; > >- // Parse the response buffer as an JSON object. > RefPtr<JSON::Object> root = parseJSONObject(response); > if (!root) { > dispatchCallback(false, std::nullopt, SuccessValue::Failed); > return; > } > >- // Parse the response using 'license' formatting, if possible. > if (auto decodedKeys = parseLicenseFormat(*root)) { >- // Retrieve the target Vector of Key objects for this session. >- auto& keyVector = ClearKeyState::singleton().keys().ensure(sessionId, [] { return Vector<CDMInstanceClearKey::Key> { }; }).iterator->value; >- >- // For each decoded key, find an existing item for the decoded key's ID. If none exist, >- // the key is decoded. Otherwise, the key is updated in case there's a mismatch between >- // the size or data of the existing and proposed key. > bool keysChanged = false; > for (auto& key : *decodedKeys) { >- auto it = std::find_if(keyVector.begin(), keyVector.end(), >- [&key] (const CDMInstanceClearKey::Key& containedKey) { >- return containedKey.keyIDData->size() == key.keyIDData->size() >- && !std::memcmp(containedKey.keyIDData->data(), key.keyIDData->data(), containedKey.keyIDData->size()); >+ auto it = std::find_if(m_keys.begin(), m_keys.end(), >+ [&key] (const Key& containedKey) { >+ return containedKey.id->size() == key.id->size() >+ && !std::memcmp(containedKey.id->data(), key.id->data(), containedKey.id->size()); > }); >- if (it != keyVector.end()) { >- auto& existingKey = it->keyValueData; >- auto& proposedKey = key.keyValueData; >+ if (it != m_keys.end()) { >+ auto& existingKey = it->value; >+ auto& proposedKey = key.value; > >- // Update the existing Key if it differs from the proposed key in key value. > if (existingKey->size() != proposedKey->size() || std::memcmp(existingKey->data(), proposedKey->data(), existingKey->size())) { > *it = WTFMove(key); > keysChanged = true; > } > } else { >- // In case a Key for this key ID doesn't exist yet, append the new one to keyVector. >- keyVector.append(WTFMove(key)); >+ m_keys.append(WTFMove(key)); > keysChanged = true; > } > } > >- // In case of changed keys, we have to provide a KeyStatusVector of all the keys for >- // this session. > std::optional<KeyStatusVector> changedKeys; > if (keysChanged) { >- // First a helper Vector is constructed, cotaining pairs of SharedBuffer RefPtrs >- // representint key ID data, and the corresponding key statuses. >- // We can't use KeyStatusVector here because this Vector has to be sorted, which >- // is not possible to do on Ref<> objects. > Vector<std::pair<RefPtr<SharedBuffer>, KeyStatus>> keys; >- keys.reserveInitialCapacity(keyVector.size()); >- for (auto& it : keyVector) >- keys.uncheckedAppend(std::pair<RefPtr<SharedBuffer>, KeyStatus> { it.keyIDData, it.status }); >+ keys.reserveInitialCapacity(m_keys.size()); >+ for (auto& it : m_keys) >+ keys.uncheckedAppend(std::pair<RefPtr<SharedBuffer>, KeyStatus> { it.id, it.status }); > >- // Sort first by size, second by data. > std::sort(keys.begin(), keys.end(), > [] (const auto& a, const auto& b) { > if (a.first->size() != b.first->size()) >@@ -614,8 +587,6 @@ void CDMInstanceSessionClearKey::updateLicense(const String& sessionId, LicenseT > return std::memcmp(a.first->data(), b.first->data(), a.first->size()) < 0; > }); > >- // Finally construct the mirroring KeyStatusVector object and move it into the >- // std::optional<> object that will be passed to the callback. > KeyStatusVector keyStatusVector; > keyStatusVector.reserveInitialCapacity(keys.size()); > for (auto& it : keys) >@@ -628,10 +599,8 @@ void CDMInstanceSessionClearKey::updateLicense(const String& sessionId, LicenseT > return; > } > >- // Parse the response using 'license release acknowledgement' formatting, if possible. > if (parseLicenseReleaseAcknowledgementFormat(*root)) { >- // FIXME: Retrieve the key ID information and use it to validate the keys for this sessionId. >- ClearKeyState::singleton().keys().remove(sessionId); >+ m_keys.clear(); > dispatchCallback(true, std::nullopt, SuccessValue::Succeeded); > return; > } >@@ -642,6 +611,11 @@ void CDMInstanceSessionClearKey::updateLicense(const String& sessionId, LicenseT > > void CDMInstanceSessionClearKey::loadSession(LicenseType, const String& sessionId, const String&, LoadSessionCallback&& callback) > { >+ LOG(EME, "EME ClearKey - load session id %s", sessionId.utf8().data()); >+ RELEASE_ASSERT(m_sessionId.isEmpty()); >+ >+ m_sessionId = sessionId; >+ > // Use a helper functor that schedules the callback dispatch, avoiding duplicated callOnMainThread() calls. > auto dispatchCallback = > [this, &callback](std::optional<KeyStatusVector>&& existingKeys, SuccessValue success, SessionLoadFailure loadFailure) { >@@ -654,20 +628,16 @@ void CDMInstanceSessionClearKey::loadSession(LicenseType, const String& sessionI > }); > }; > >- // Construct the KeyStatusVector object, representing all the known keys for this session. > KeyStatusVector keyStatusVector; > { >- auto& keys = ClearKeyState::singleton().keys(); >- auto it = keys.find(sessionId); >- if (it == keys.end()) { >+ if (!m_keys.size()) { > dispatchCallback(std::nullopt, Failed, SessionLoadFailure::NoSessionData); > return; > } > >- auto& keyVector = it->value; >- keyStatusVector.reserveInitialCapacity(keyVector.size()); >- for (auto& key : keyVector) >- keyStatusVector.uncheckedAppend(std::pair<Ref<SharedBuffer>, KeyStatus> { *key.keyIDData, key.status }); >+ keyStatusVector.reserveInitialCapacity(m_keys.size()); >+ for (auto& key : m_keys) >+ keyStatusVector.uncheckedAppend(std::pair<Ref<SharedBuffer>, KeyStatus> { *key.id, key.status }); > } > > dispatchCallback(WTFMove(keyStatusVector), Succeeded, SessionLoadFailure::None); >@@ -686,6 +656,9 @@ void CDMInstanceSessionClearKey::closeSession(const String&, CloseSessionCallbac > > void CDMInstanceSessionClearKey::removeSessionData(const String& sessionId, LicenseType, RemoveSessionDataCallback&& callback) > { >+ LOG(EME, "EME ClearKey - remove session id %s, our id is %s", sessionId.utf8().data(), m_sessionId.utf8().data()); >+ RELEASE_ASSERT(sessionId == m_sessionId); >+ > // Use a helper functor that schedules the callback dispatch, avoiding duplicated callOnMainThread() calls. > auto dispatchCallback = > [this, &callback](KeyStatusVector&& keyStatusVector, std::optional<Ref<SharedBuffer>>&& message, SuccessValue success) { >@@ -703,32 +676,24 @@ void CDMInstanceSessionClearKey::removeSessionData(const String& sessionId, Lice > KeyStatusVector keyStatusVector; > RefPtr<SharedBuffer> message; > { >- // Retrieve information for the given session ID, bailing if none is found. >- auto& keys = ClearKeyState::singleton().keys(); >- auto it = keys.find(sessionId); >- if (it == keys.end()) { >+ if (!m_keys.size()) { > dispatchCallback(KeyStatusVector { }, std::nullopt, SuccessValue::Failed); > return; > } > >- // Retrieve the Key vector, containing all the keys for this session, and >- // then remove the key map entry for this session. >- auto keyVector = WTFMove(it->value); >- keys.remove(it); >- > // Construct the KeyStatusVector object, pairing key IDs with the 'released' status. >- keyStatusVector.reserveInitialCapacity(keyVector.size()); >- for (auto& key : keyVector) >- keyStatusVector.uncheckedAppend(std::pair<Ref<SharedBuffer>, KeyStatus> { *key.keyIDData, KeyStatus::Released }); >+ keyStatusVector.reserveInitialCapacity(m_keys.size()); >+ for (auto& key : m_keys) >+ keyStatusVector.uncheckedAppend(std::pair<Ref<SharedBuffer>, KeyStatus> { *key.id, KeyStatus::Released }); > > // Construct JSON that represents the 'license release' format, creating a 'kids' array > // of base64URL-encoded key IDs for all keys that were associated with this session. > auto rootObject = JSON::Object::create(); > { > auto array = JSON::Array::create(); >- for (auto& key : keyVector) { >- ASSERT(key.keyIDData->size() <= std::numeric_limits<unsigned>::max()); >- array->pushString(WTF::base64URLEncode(key.keyIDData->data(), static_cast<unsigned>(key.keyIDData->size()))); >+ for (auto& key : m_keys) { >+ ASSERT(key.id->size() <= std::numeric_limits<unsigned>::max()); >+ array->pushString(WTF::base64URLEncode(key.id->data(), static_cast<unsigned>(key.id->size()))); > } > rootObject->setArray("kids", WTFMove(array)); > } >@@ -737,6 +702,8 @@ void CDMInstanceSessionClearKey::removeSessionData(const String& sessionId, Lice > String messageString = rootObject->toJSONString(); > CString messageCString = messageString.utf8(); > message = SharedBuffer::create(messageCString.data(), messageCString.length()); >+ >+ m_keys.clear(); > } > > dispatchCallback(WTFMove(keyStatusVector), Ref<SharedBuffer>(*message), SuccessValue::Succeeded); >@@ -746,6 +713,103 @@ void CDMInstanceSessionClearKey::storeRecordOfKeyUsage(const String&) > { > } > >+bool CDMInstanceSessionClearKey::decrypt(Ref<CDMInstance::DecryptionConfig>&& config, uint8_t* encryptedBuffer, uint32_t encryptedBufferSize) >+{ >+ // It is tempting to have the cipher handle as a data member of >+ // this class, however, due to the lifetime of this class being >+ // dependent on JS GC, there are situations were you need more >+ // than the fixed number of allowed GCrypt cipher handles open >+ // simultaneously, so we have to tightly control the lifetime of >+ // the handles in this method. >+ // REVIEW: Should we use the cross-platform abstractions under >+ // WebCore/crypto instead of directly using gcrypt here? >+ gcry_cipher_hd_t handle; >+ // FIXME: This is assuming the content has been encrypted in a >+ // specific manner, it will fail when using CBC mode, or pattern >+ // encryption for example. The cipher should ideally be created >+ // from the DecryptionConfig class. >+ if (gcry_error_t error = gcry_cipher_open(&handle, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE)) >+ RELEASE_ASSERT_WITH_MESSAGE(!error, "failed to create AES 128 CTR cipher handle: %s", gpg_strerror(error)); >+ >+ // REVIEW: This padding gives me anxiety. I've seen various >+ // standards for crypto padding, but I see nothing specific for >+ // AES-128, or if zero padding like this even makes sense. It >+ // would seem all IVs for AES-128 should be 16 bytes, but in our >+ // tests they are not! Some clarification would be good here. >+ LOG(EME, "EME ClearKey session - iv has %lu bytes", config->iv()->size()); >+ RELEASE_ASSERT_WITH_MESSAGE(config->iv()->size() == ivSize || config->iv()->size() == ivSize / 2, "unexpected IV length"); >+ if (config->iv()->size() == ivSize / 2) >+ config->iv()->append(Vector<char>(8, 0)); >+ >+ if (gcry_error_t error = gcry_cipher_setctr(handle, config->iv()->data(), ivSize)) { >+ LOG(EME, "EME ClearKey - gcry_cipher_setctr failed: %s", gpg_strerror(error)); >+ return false; >+ } >+ >+ LOG(EME, "EME ClearKey - searching for key ID [%s]", keyIDToHexString(config->keyID()).utf8().data()); >+ // FIXME: We technically already know the matching key id from the >+ // callsite of this decrypt method, could save a walk across our >+ // keys if the calling code is rearranged a little. >+ auto matchingKey = std::find_if(m_keys.begin(), m_keys.end(), >+ [&config](const Key& existingKey) { >+ return config->keyID() == *existingKey.id; >+ }); >+ if (matchingKey == m_keys.end()) { >+ LOG(EME, "EME ClearKey - failed to find key id in set of known keys"); >+ gcry_cipher_close(handle); >+ return false; >+ } >+ >+ if (gcry_error_t error = gcry_cipher_setkey(handle, matchingKey->value->data(), matchingKey->value->size())) { >+ LOG(EME, "EME ClearKey - gcry_cipher_setkey failed: %s", gpg_strerror(error)); >+ return false; >+ } >+ >+ if (!config->isSubSample()) { >+ LOG(EME, "EME ClearKey - decrypting full sample of %" PRIu32 " bytes", encryptedBufferSize); >+ if (gcry_error_t error = gcry_cipher_decrypt(handle, encryptedBuffer, encryptedBufferSize, 0, 0)) { >+ LOG(EME, "EME ClearKey - full sample decryption failed: %s", gpg_strerror(error)); >+ return false; >+ } >+ gcry_cipher_close(handle); >+ return true; >+ } >+ >+ uint32_t offset { 0 }; >+ LOG(EME, "EME ClearKey - there are %lu subsample ranges to process", config->subSamples().size()); >+ for (const auto& subSampleRange : config->subSamples()) { >+ offset += subSampleRange.clearBytes; >+ >+ if (!subSampleRange.cipherBytes) { >+ LOG(EME, "EME ClearKey - ignoring clear subsample of size %u at offset %u", subSampleRange.clearBytes, offset); >+ continue; >+ } >+ >+ LOG(EME, "EME ClearKey - decrypting subsample, [clear=%u, cipher=%u] offset=%u", subSampleRange.clearBytes, subSampleRange.cipherBytes, offset); >+ >+ if (gcry_error_t error = gcry_cipher_decrypt(handle, encryptedBuffer + offset, subSampleRange.cipherBytes, 0, 0)) { >+ LOG(EME, "EME ClearKey - decryption subsamples failed: %s", gpg_strerror(error)); >+ gcry_cipher_close(handle); >+ return false; >+ } >+ offset += subSampleRange.cipherBytes; >+ } >+ >+ LOG(EME, "EME ClearKey - decrypting subsamples succeeded"); >+ gcry_cipher_close(handle); >+ return true; >+} >+ >+Vector<Ref<SharedBuffer>> CDMInstanceSessionClearKey::keyIDs() >+{ >+ Vector<Ref<SharedBuffer>> keyIDs; >+ keyIDs.reserveInitialCapacity(m_keys.size()); >+ for (auto& key : m_keys) >+ keyIDs.uncheckedAppend(*key.id); >+ >+ return keyIDs; >+} >+ > } // namespace WebCore > > #endif // ENABLE(ENCRYPTED_MEDIA) >diff --git a/Source/WebCore/platform/encryptedmedia/clearkey/CDMClearKey.h b/Source/WebCore/platform/encryptedmedia/clearkey/CDMClearKey.h >index f11ccdc8c421c99249d4d80c0a7f5c03534550b4..a4fb4b6007c95c2950fe38ec7d3e15f791985f2f 100644 >--- a/Source/WebCore/platform/encryptedmedia/clearkey/CDMClearKey.h >+++ b/Source/WebCore/platform/encryptedmedia/clearkey/CDMClearKey.h >@@ -34,11 +34,14 @@ > #include "CDMInstance.h" > #include "CDMInstanceSession.h" > #include "CDMPrivate.h" >+#include "Logging.h" > #include "SharedBuffer.h" > #include <wtf/WeakPtr.h> > > namespace WebCore { > >+class CDMInstanceSessionClearKey; >+ > class CDMFactoryClearKey final : public CDMFactory { > public: > static CDMFactoryClearKey& singleton(); >@@ -77,8 +80,7 @@ public: > > class CDMInstanceClearKey final : public CDMInstance, public CanMakeWeakPtr<CDMInstanceClearKey> { > public: >- CDMInstanceClearKey(); >- virtual ~CDMInstanceClearKey(); >+ virtual ~CDMInstanceClearKey() = default; > > ImplementationType implementationType() const final { return ImplementationType::ClearKey; } > >@@ -89,24 +91,38 @@ public: > SuccessValue setStorageDirectory(const String&) final; > const String& keySystem() const final; > RefPtr<CDMInstanceSession> createSession() final; >+ bool decrypt(Ref<DecryptionConfig>&&, uint8_t*, uint32_t) final; > >- struct Key { >- CDMInstanceSession::KeyStatus status; >- RefPtr<SharedBuffer> keyIDData; >- RefPtr<SharedBuffer> keyValueData; >- }; >+ CDMInstanceSessionClearKey* sessionForKeyID(const Ref<SharedBuffer>&) const; > >- const Vector<Key> keys() const; >+private: >+ Vector<WeakPtr<CDMInstanceSessionClearKey>> m_sessions; > }; > > class CDMInstanceSessionClearKey final : public CDMInstanceSession, public CanMakeWeakPtr<CDMInstanceSessionClearKey> { > public: >+ struct Key { >+ CDMInstanceSession::KeyStatus status; >+ RefPtr<SharedBuffer> id; >+ RefPtr<SharedBuffer> value; >+ }; >+ >+ CDMInstanceSessionClearKey(Ref<CDMInstanceClearKey>&&); >+ > void requestLicense(LicenseType, const AtomicString& initDataType, Ref<SharedBuffer>&& initData, LicenseCallback&&) final; > void updateLicense(const String&, LicenseType, const SharedBuffer&, LicenseUpdateCallback&&) final; > void loadSession(LicenseType, const String&, const String&, LoadSessionCallback&&) final; > void closeSession(const String&, CloseSessionCallback&&) final; > void removeSessionData(const String&, LicenseType, RemoveSessionDataCallback&&) final; > void storeRecordOfKeyUsage(const String&) final; >+ bool decrypt(Ref<CDMInstance::DecryptionConfig>&&, uint8_t*, uint32_t) final; >+ >+ Vector<Ref<SharedBuffer>> keyIDs(); >+ >+private: >+ String m_sessionId; >+ Ref<CDMInstanceClearKey> m_instance; >+ Vector<Key> m_keys; > }; > > } // namespace WebCore >diff --git a/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.cpp b/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.cpp >index 52649bba7ab8c4d9ab7539ae329964838381fc99..d15234e08ec4f364278c603f92738dc0ea0c8e88 100644 >--- a/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.cpp >+++ b/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.cpp >@@ -47,7 +47,7 @@ > #endif > > #if ENABLE(ENCRYPTED_MEDIA) >-#include "WebKitClearKeyDecryptorGStreamer.h" >+#include "WebKitCommonEncryptionDecryptorGStreamer.h" > #endif > > #if ENABLE(VIDEO) >@@ -259,7 +259,7 @@ bool initializeGStreamerAndRegisterWebKitElements() > std::call_once(onceFlag, [] { > #if ENABLE(ENCRYPTED_MEDIA) > if (webkitGstCheckVersion(1, 6, 1)) >- gst_element_register(nullptr, "webkitclearkey", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_MEDIA_CK_DECRYPT); >+ gst_element_register(nullptr, "webkitcenc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_MEDIA_CENC_DECRYPT); > #endif > > #if ENABLE(MEDIA_STREAM) && GST_CHECK_VERSION(1, 10, 0) >diff --git a/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.h b/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.h >index f7c8d2af06054d7173dc4c6a588ea6e7ec60f589..93882e26d774e7be2e7a43d47186a806dcdd8d74 100644 >--- a/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.h >+++ b/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.h >@@ -23,6 +23,7 @@ > #include "FloatSize.h" > #include "GRefPtrGStreamer.h" > #include "GUniquePtrGStreamer.h" >+#include "SharedBuffer.h" > #include <gst/gst.h> > #include <gst/video/video-format.h> > #include <gst/video/video-info.h> >@@ -112,6 +113,8 @@ public: > > size_t size() const { ASSERT(m_isValid); return static_cast<size_t>(m_info.size); } > >+ Ref<SharedBuffer> sharedBuffer() const { return SharedBuffer::create(data(), size()); } >+ > explicit operator bool() const { return m_isValid; } > > private: >diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp >deleted file mode 100644 >index f941e528f47cbd069579bc5e633581e6b610c723..0000000000000000000000000000000000000000 >--- a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp >+++ /dev/null >@@ -1,299 +0,0 @@ >-/* GStreamer ClearKey common encryption decryptor >- * >- * Copyright (C) 2016 Metrological >- * Copyright (C) 2016 Igalia S.L >- * >- * This library is free software; you can redistribute it and/or >- * modify it under the terms of the GNU Library General Public >- * License as published by the Free Software Foundation; either >- * version 2 of the License, or (at your option) any later version. >- * >- * This library is distributed in the hope that it will be useful, >- * but WITHOUT ANY WARRANTY; without even the implied warranty of >- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >- * Library General Public License for more details. >- * >- * You should have received a copy of the GNU Library General Public >- * License along with this library; if not, write to the >- * Free Software Foundation, Inc., 51 Franklin Street, Suite 500, >- * Boston, MA 02110-1335, USA. >- */ >- >-#include "config.h" >-#include "WebKitClearKeyDecryptorGStreamer.h" >- >-#if ENABLE(ENCRYPTED_MEDIA) && USE(GSTREAMER) >- >-#include "GStreamerCommon.h" >-#include "GStreamerEMEUtilities.h" >-#include <gcrypt.h> >-#include <gst/base/gstbytereader.h> >-#include <wtf/RunLoop.h> >- >-#define CLEARKEY_SIZE 16 >- >-struct Key { >- GRefPtr<GstBuffer> keyID; >- GRefPtr<GstBuffer> keyValue; >-}; >- >-#define WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_CK_DECRYPT, WebKitMediaClearKeyDecryptPrivate)) >-struct _WebKitMediaClearKeyDecryptPrivate { >- Vector<Key> keys; >- gcry_cipher_hd_t handle; >-}; >- >-static void webKitMediaClearKeyDecryptorFinalize(GObject*); >-static gboolean webKitMediaClearKeyDecryptorHandleKeyResponse(WebKitMediaCommonEncryptionDecrypt* self, GstEvent*); >-static gboolean webKitMediaClearKeyDecryptorDecrypt(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* iv, GstBuffer* keyid, GstBuffer* sample, unsigned subSamplesCount, GstBuffer* subSamples); >- >-GST_DEBUG_CATEGORY_STATIC(webkit_media_clear_key_decrypt_debug_category); >-#define GST_CAT_DEFAULT webkit_media_clear_key_decrypt_debug_category >- >-static GstStaticPadTemplate sinkTemplate = GST_STATIC_PAD_TEMPLATE("sink", >- GST_PAD_SINK, >- GST_PAD_ALWAYS, >- GST_STATIC_CAPS("application/x-cenc, original-media-type=(string)video/x-h264, protection-system=(string)" WEBCORE_GSTREAMER_EME_UTILITIES_CLEARKEY_UUID "; " >- "application/x-cenc, original-media-type=(string)audio/mpeg, protection-system=(string)" WEBCORE_GSTREAMER_EME_UTILITIES_CLEARKEY_UUID";" >- "application/x-webm-enc, original-media-type=(string)video/x-vp8;" >- "application/x-webm-enc, original-media-type=(string)video/x-vp9;")); >- >-static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src", >- GST_PAD_SRC, >- GST_PAD_ALWAYS, >- GST_STATIC_CAPS("video/x-h264; audio/mpeg; video/x-vp8; video/x-vp9")); >- >-#define webkit_media_clear_key_decrypt_parent_class parent_class >-G_DEFINE_TYPE(WebKitMediaClearKeyDecrypt, webkit_media_clear_key_decrypt, WEBKIT_TYPE_MEDIA_CENC_DECRYPT); >- >-static void webkit_media_clear_key_decrypt_class_init(WebKitMediaClearKeyDecryptClass* klass) >-{ >- GObjectClass* gobjectClass = G_OBJECT_CLASS(klass); >- gobjectClass->finalize = webKitMediaClearKeyDecryptorFinalize; >- >- GstElementClass* elementClass = GST_ELEMENT_CLASS(klass); >- gst_element_class_add_pad_template(elementClass, gst_static_pad_template_get(&sinkTemplate)); >- gst_element_class_add_pad_template(elementClass, gst_static_pad_template_get(&srcTemplate)); >- >- gst_element_class_set_static_metadata(elementClass, >- "Decrypt content encrypted using ISOBMFF ClearKey Common Encryption", >- GST_ELEMENT_FACTORY_KLASS_DECRYPTOR, >- "Decrypts media that has been encrypted using ISOBMFF ClearKey Common Encryption.", >- "Philippe Normand <philn@igalia.com>"); >- >- GST_DEBUG_CATEGORY_INIT(webkit_media_clear_key_decrypt_debug_category, >- "webkitclearkey", 0, "ClearKey decryptor"); >- >- WebKitMediaCommonEncryptionDecryptClass* cencClass = WEBKIT_MEDIA_CENC_DECRYPT_CLASS(klass); >- cencClass->protectionSystemId = WebCore::GStreamerEMEUtilities::s_ClearKeyUUID; >- cencClass->handleKeyResponse = GST_DEBUG_FUNCPTR(webKitMediaClearKeyDecryptorHandleKeyResponse); >- cencClass->decrypt = GST_DEBUG_FUNCPTR(webKitMediaClearKeyDecryptorDecrypt); >- >- g_type_class_add_private(klass, sizeof(WebKitMediaClearKeyDecryptPrivate)); >-} >- >-static void webkit_media_clear_key_decrypt_init(WebKitMediaClearKeyDecrypt* self) >-{ >- WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(self); >- >- self->priv = priv; >- new (priv) WebKitMediaClearKeyDecryptPrivate(); >- if (gcry_error_t error = gcry_cipher_open(&(priv->handle), GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE)) { >- GST_ERROR_OBJECT(self, "Failed to create AES 128 CTR cipher handle: %s", gpg_strerror(error)); >- ASSERT(!error); >- } >-} >- >-static void webKitMediaClearKeyDecryptorFinalize(GObject* object) >-{ >- WebKitMediaClearKeyDecrypt* self = WEBKIT_MEDIA_CK_DECRYPT(object); >- WebKitMediaClearKeyDecryptPrivate* priv = self->priv; >- gcry_cipher_close(priv->handle); >- priv->~WebKitMediaClearKeyDecryptPrivate(); >- >- GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object)); >-} >- >-static gboolean webKitMediaClearKeyDecryptorHandleKeyResponse(WebKitMediaCommonEncryptionDecrypt* self, GstEvent* event) >-{ >- WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self)); >- const GstStructure* structure = gst_event_get_structure(event); >- >- // Demand the `drm-cipher-clearkey` GstStructure. >- if (!gst_structure_has_name(structure, "drm-cipher-clearkey")) >- return FALSE; >- >- // Retrieve the `key-ids` GStreamer value list. >- const GValue* keyIDsList = gst_structure_get_value(structure, "key-ids"); >- ASSERT(keyIDsList && GST_VALUE_HOLDS_LIST(keyIDsList)); >- unsigned keyIDsListSize = gst_value_list_get_size(keyIDsList); >- >- // Retrieve the `key-values` GStreamer value list. >- const GValue* keyValuesList = gst_structure_get_value(structure, "key-values"); >- ASSERT(keyValuesList && GST_VALUE_HOLDS_LIST(keyValuesList)); >- unsigned keyValuesListSize = gst_value_list_get_size(keyValuesList); >- >- // Bail if somehow the two lists don't match in size. >- if (keyIDsListSize != keyValuesListSize) >- return FALSE; >- >- // Clear out the previous list of keys. >- priv->keys.clear(); >- >- // Append the retrieved GstBuffer objects containing each key's ID and value to the list of Key objects. >- for (unsigned i = 0; i < keyIDsListSize; ++i) { >- GRefPtr<GstBuffer> keyIDBuffer(gst_value_get_buffer(gst_value_list_get_value(keyIDsList, i))); >- GRefPtr<GstBuffer> keyValueBuffer(gst_value_get_buffer(gst_value_list_get_value(keyValuesList, i))); >- priv->keys.append(Key { WTFMove(keyIDBuffer), WTFMove(keyValueBuffer) }); >- } >- >- return TRUE; >-} >- >-static gboolean webKitMediaClearKeyDecryptorFindAndSetKey(WebKitMediaClearKeyDecryptPrivate* priv, const WebCore::GstMappedBuffer& keyIDBuffer) >-{ >- GRefPtr<GstBuffer> keyBuffer; >- for (auto& key : priv->keys) { >- if (key.keyID.get() == keyIDBuffer) { >- keyBuffer = key.keyValue; >- break; >- } >- } >- >- if (!keyBuffer) { >- GST_ERROR_OBJECT(priv, "Failed to find an appropriate key buffer"); >- return false; >- } >- >- WebCore::GstMappedBuffer mappedKeyValueBuffer(keyBuffer.get(), GST_MAP_READ); >- if (!mappedKeyValueBuffer) { >- GST_ERROR_OBJECT(priv, "Failed to map decryption key"); >- return false; >- } >- >- ASSERT(mappedKeyValueBuffer.size() == CLEARKEY_SIZE); >- if (gcry_error_t error = gcry_cipher_setkey(priv->handle, mappedKeyValueBuffer.data(), mappedKeyValueBuffer.size())) { >- GST_ERROR_OBJECT(priv, "gcry_cipher_setkey failed: %s", gpg_strerror(error)); >- return false; >- } >- >- return true; >-} >- >-static gboolean webKitMediaClearKeyDecryptorDecrypt(WebKitMediaCommonEncryptionDecrypt* self, GstBuffer* ivBuffer, GstBuffer* keyIDBuffer, GstBuffer* buffer, unsigned subSampleCount, GstBuffer* subSamplesBuffer) >-{ >- // Check ivBuffer isn't null. >- if (!ivBuffer) { >- GST_ERROR_OBJECT(self, "Error, the ivBuffer is null"); >- return false; >- } >- >- WebCore::GstMappedBuffer mappedIVBuffer(ivBuffer, GST_MAP_READ); >- if (!mappedIVBuffer) { >- GST_ERROR_OBJECT(self, "Failed to map IV"); >- return false; >- } >- >- uint8_t ctr[CLEARKEY_SIZE]; >- if (mappedIVBuffer.size() == 8) { >- memset(ctr + 8, 0, 8); >- memcpy(ctr, mappedIVBuffer.data(), 8); >- } else { >- ASSERT(mappedIVBuffer.size() == CLEARKEY_SIZE); >- memcpy(ctr, mappedIVBuffer.data(), CLEARKEY_SIZE); >- } >- >- WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self)); >- gcry_error_t cipherError = gcry_cipher_setctr(priv->handle, ctr, CLEARKEY_SIZE); >- if (cipherError) { >- GST_ERROR_OBJECT(self, "gcry_cipher_setctr failed: %s", gpg_strerror(cipherError)); >- return false; >- } >- >- // Check buffer isn't null. >- if (!buffer) { >- GST_ERROR_OBJECT(self, "No buffer to decrypt"); >- return false; >- } >- >- WebCore::GstMappedBuffer mappedKeyIdBuffer(keyIDBuffer, GST_MAP_READ); >- if (!mappedKeyIdBuffer) { >- GST_ERROR_OBJECT(self, "Failed to map key id buffer"); >- return false; >- } >- >- WebCore::GstMappedBuffer mappedBuffer(buffer, GST_MAP_READWRITE); >- if (!mappedBuffer) { >- GST_ERROR_OBJECT(self, "Failed to map buffer"); >- return false; >- } >- >- webKitMediaClearKeyDecryptorFindAndSetKey(priv, mappedKeyIdBuffer); >- >- unsigned position = 0; >- unsigned sampleIndex = 0; >- >- if (!subSampleCount) { >- // Full sample encryption. >- GST_TRACE_OBJECT(self, "full sample encryption: %zu encrypted bytes", mappedBuffer.size()); >- >- // Check if the buffer is empty. >- if (mappedBuffer.size()) { >- cipherError = gcry_cipher_decrypt(priv->handle, mappedBuffer.data(), mappedBuffer.size(), 0, 0); >- if (cipherError) { >- GST_ERROR_OBJECT(self, "full sample decryption failed: %s", gpg_strerror(cipherError)); >- return false; >- } >- } >- return true; >- } >- >- // Check subSamplesBuffer isn't null. >- if (!subSamplesBuffer) { >- GST_ERROR_OBJECT(self, "Error, the subSampleBuffer is null"); >- return false; >- } >- >- // Subsample encryption. >- WebCore::GstMappedBuffer mappedSubSamplesBuffer(subSamplesBuffer, GST_MAP_READ); >- if (!mappedSubSamplesBuffer) { >- GST_ERROR_OBJECT(self, "Failed to map subsample buffer"); >- return false; >- } >- >- GUniquePtr<GstByteReader> reader(gst_byte_reader_new(mappedSubSamplesBuffer.data(), mappedSubSamplesBuffer.size())); >- GST_DEBUG_OBJECT(self, "position: %d, size: %zu", position, mappedBuffer.size()); >- >- while (position < mappedBuffer.size()) { >- guint16 nBytesClear = 0; >- guint32 nBytesEncrypted = 0; >- >- if (sampleIndex < subSampleCount) { >- if (!gst_byte_reader_get_uint16_be(reader.get(), &nBytesClear) >- || !gst_byte_reader_get_uint32_be(reader.get(), &nBytesEncrypted)) { >- GST_DEBUG_OBJECT(self, "unsupported"); >- return false; >- } >- sampleIndex++; >- } else { >- nBytesClear = 0; >- nBytesEncrypted = mappedBuffer.size() - position; >- } >- >- GST_TRACE_OBJECT(self, "subsample index %u - %hu bytes clear (todo=%zu)", sampleIndex, nBytesClear, mappedBuffer.size() - position); >- position += nBytesClear; >- if (nBytesEncrypted) { >- GST_TRACE_OBJECT(self, "subsample index %u - %u bytes encrypted (todo=%zu)", sampleIndex, nBytesEncrypted, mappedBuffer.size() - position); >- cipherError = gcry_cipher_decrypt(priv->handle, mappedBuffer.data() + position, nBytesEncrypted, 0, 0); >- if (cipherError) { >- GST_ERROR_OBJECT(self, "sub sample index %u decryption failed: %s", sampleIndex, gpg_strerror(cipherError)); >- return false; >- } >- position += nBytesEncrypted; >- } >- } >- >- return true; >-} >- >-#endif // ENABLE(ENCRYPTED_MEDIA) && USE(GSTREAMER) >diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.h >deleted file mode 100644 >index 9321517792320115290fa11c76181e595807eea7..0000000000000000000000000000000000000000 >--- a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.h >+++ /dev/null >@@ -1,54 +0,0 @@ >-/* GStreamer ClearKey common encryption decryptor >- * >- * Copyright (C) 2016 Metrological >- * Copyright (C) 2016 Igalia S.L >- * >- * This library is free software; you can redistribute it and/or >- * modify it under the terms of the GNU Library General Public >- * License as published by the Free Software Foundation; either >- * version 2 of the License, or (at your option) any later version. >- * >- * This library is distributed in the hope that it will be useful, >- * but WITHOUT ANY WARRANTY; without even the implied warranty of >- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >- * Library General Public License for more details. >- * >- * You should have received a copy of the GNU Library General Public >- * License along with this library; if not, write to the >- * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, >- * Boston, MA 02110-1301, USA. >- */ >- >-#pragma once >- >-#if ENABLE(ENCRYPTED_MEDIA) && USE(GSTREAMER) >- >-#include "WebKitCommonEncryptionDecryptorGStreamer.h" >- >-G_BEGIN_DECLS >- >-#define WEBKIT_TYPE_MEDIA_CK_DECRYPT (webkit_media_clear_key_decrypt_get_type()) >-#define WEBKIT_MEDIA_CK_DECRYPT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_MEDIA_CK_DECRYPT, WebKitMediaClearKeyDecrypt)) >-#define WEBKIT_MEDIA_CK_DECRYPT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WEBKIT_TYPE_MEDIA_CK_DECRYPT, WebKitMediaClearKeyDecryptClass)) >-#define WEBKIT_IS_MEDIA_CK_DECRYPT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_MEDIA_CK_DECRYPT)) >-#define WEBKIT_IS_MEDIA_CK_DECRYPT_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass), WEBKIT_TYPE_MEDIA_CK_DECRYPT)) >- >-typedef struct _WebKitMediaClearKeyDecrypt WebKitMediaClearKeyDecrypt; >-typedef struct _WebKitMediaClearKeyDecryptClass WebKitMediaClearKeyDecryptClass; >-typedef struct _WebKitMediaClearKeyDecryptPrivate WebKitMediaClearKeyDecryptPrivate; >- >-GType webkit_media_clear_key_decrypt_get_type(void); >- >-struct _WebKitMediaClearKeyDecrypt { >- WebKitMediaCommonEncryptionDecrypt parent; >- >- WebKitMediaClearKeyDecryptPrivate* priv; >-}; >- >-struct _WebKitMediaClearKeyDecryptClass { >- WebKitMediaCommonEncryptionDecryptClass parentClass; >-}; >- >-G_END_DECLS >- >-#endif // ENABLE(ENCRYPTED_MEDIA) && USE(GSTREAMER) >diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp >index 8bff38c039bb9f110a855cf181ce7ce7c718d65a..03ac2443d0341b5e87a95c01a280e613ae9d7d20 100644 >--- a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp >+++ b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp >@@ -26,6 +26,7 @@ > #if ENABLE(ENCRYPTED_MEDIA) && USE(GSTREAMER) > > #include "GStreamerCommon.h" >+#include "GStreamerEMEUtilities.h" > #include <CDMInstance.h> > #include <wtf/Condition.h> > #include <wtf/PrintStream.h> >@@ -35,19 +36,18 @@ > struct _WebKitMediaCommonEncryptionDecryptPrivate { > GRefPtr<GstEvent> protectionEvent; > RefPtr<WebCore::CDMInstance> cdmInstance; >- bool keyReceived; >+ bool keyReceived { false }; > bool waitingForKey { false }; > Lock mutex; > Condition condition; > }; > >-static GstStateChangeReturn webKitMediaCommonEncryptionDecryptorChangeState(GstElement*, GstStateChange transition); >-static void webKitMediaCommonEncryptionDecryptorFinalize(GObject*); >-static GstCaps* webkitMediaCommonEncryptionDecryptTransformCaps(GstBaseTransform*, GstPadDirection, GstCaps*, GstCaps*); >-static GstFlowReturn webkitMediaCommonEncryptionDecryptTransformInPlace(GstBaseTransform*, GstBuffer*); >-static gboolean webkitMediaCommonEncryptionDecryptSinkEventHandler(GstBaseTransform*, GstEvent*); >-static gboolean webkitMediaCommonEncryptionDecryptorQueryHandler(GstBaseTransform*, GstPadDirection, GstQuery*); >- >+static GstStateChangeReturn changeState(GstElement*, GstStateChange transition); >+static void finalize(GObject*); >+static GstCaps* transformCaps(GstBaseTransform*, GstPadDirection, GstCaps*, GstCaps*); >+static GstFlowReturn transformInPlace(GstBaseTransform*, GstBuffer*); >+static gboolean sinkEventHandler(GstBaseTransform*, GstEvent*); >+static gboolean queryHandler(GstBaseTransform*, GstPadDirection, GstQuery*); > > GST_DEBUG_CATEGORY_STATIC(webkit_media_common_encryption_decrypt_debug_category); > #define GST_CAT_DEFAULT webkit_media_common_encryption_decrypt_debug_category >@@ -55,23 +55,48 @@ GST_DEBUG_CATEGORY_STATIC(webkit_media_common_encryption_decrypt_debug_category) > #define webkit_media_common_encryption_decrypt_parent_class parent_class > G_DEFINE_TYPE(WebKitMediaCommonEncryptionDecrypt, webkit_media_common_encryption_decrypt, GST_TYPE_BASE_TRANSFORM); > >+static GstStaticPadTemplate sinkTemplate = GST_STATIC_PAD_TEMPLATE("sink", >+ GST_PAD_SINK, >+ GST_PAD_ALWAYS, >+ GST_STATIC_CAPS("application/x-cenc, original-media-type=(string)video/x-h264, protection-system=(string)" WEBCORE_GSTREAMER_EME_UTILITIES_CLEARKEY_UUID "; " >+ "application/x-cenc, original-media-type=(string)audio/mpeg, protection-system=(string)" WEBCORE_GSTREAMER_EME_UTILITIES_CLEARKEY_UUID";" >+ "application/x-webm-enc, original-media-type=(string)video/x-vp8;" >+ "application/x-webm-enc, original-media-type=(string)video/x-vp9;")); >+ >+static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src", >+ GST_PAD_SRC, >+ GST_PAD_ALWAYS, >+ GST_STATIC_CAPS("video/x-h264; audio/mpeg; video/x-vp8; video/x-vp9")); >+ > static void webkit_media_common_encryption_decrypt_class_init(WebKitMediaCommonEncryptionDecryptClass* klass) > { > GObjectClass* gobjectClass = G_OBJECT_CLASS(klass); >- gobjectClass->finalize = webKitMediaCommonEncryptionDecryptorFinalize; >+ gobjectClass->finalize = finalize; >+ >+ GstElementClass* elementClass = GST_ELEMENT_CLASS(klass); >+ gst_element_class_add_pad_template(elementClass, gst_static_pad_template_get(&sinkTemplate)); >+ gst_element_class_add_pad_template(elementClass, gst_static_pad_template_get(&srcTemplate)); >+ >+ gst_element_class_set_static_metadata(elementClass, >+ "Decrypt content created for EME", >+ GST_ELEMENT_FACTORY_KLASS_DECRYPTOR, >+ "Decryptor element that interfaces to WebKit CDM APIs", >+ "Igalia S.L"); > > GST_DEBUG_CATEGORY_INIT(webkit_media_common_encryption_decrypt_debug_category, >- "webkitcenc", 0, "Common Encryption base class"); >+ "webkitcenc", 0, "WebKit decryptor"); > >- GstElementClass* elementClass = GST_ELEMENT_CLASS(klass); >- elementClass->change_state = GST_DEBUG_FUNCPTR(webKitMediaCommonEncryptionDecryptorChangeState); >+ elementClass->change_state = GST_DEBUG_FUNCPTR(changeState); > > GstBaseTransformClass* baseTransformClass = GST_BASE_TRANSFORM_CLASS(klass); >- baseTransformClass->transform_ip = GST_DEBUG_FUNCPTR(webkitMediaCommonEncryptionDecryptTransformInPlace); >- baseTransformClass->transform_caps = GST_DEBUG_FUNCPTR(webkitMediaCommonEncryptionDecryptTransformCaps); >+ baseTransformClass->transform_ip = GST_DEBUG_FUNCPTR(transformInPlace); >+ baseTransformClass->transform_caps = GST_DEBUG_FUNCPTR(transformCaps); > baseTransformClass->transform_ip_on_passthrough = FALSE; >- baseTransformClass->sink_event = GST_DEBUG_FUNCPTR(webkitMediaCommonEncryptionDecryptSinkEventHandler); >- baseTransformClass->query = GST_DEBUG_FUNCPTR(webkitMediaCommonEncryptionDecryptorQueryHandler); >+ baseTransformClass->sink_event = GST_DEBUG_FUNCPTR(sinkEventHandler); >+ baseTransformClass->query = GST_DEBUG_FUNCPTR(queryHandler); >+ >+ WebKitMediaCommonEncryptionDecryptClass* cencClass = WEBKIT_MEDIA_CENC_DECRYPT_CLASS(klass); >+ cencClass->protectionSystemId = WebCore::GStreamerEMEUtilities::s_ClearKeyUUID; > > g_type_class_add_private(klass, sizeof(WebKitMediaCommonEncryptionDecryptPrivate)); > } >@@ -89,7 +114,7 @@ static void webkit_media_common_encryption_decrypt_init(WebKitMediaCommonEncrypt > gst_base_transform_set_gap_aware(base, FALSE); > } > >-static void webKitMediaCommonEncryptionDecryptorFinalize(GObject* object) >+static void finalize(GObject* object) > { > WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(object); > WebKitMediaCommonEncryptionDecryptPrivate* priv = self->priv; >@@ -98,7 +123,7 @@ static void webKitMediaCommonEncryptionDecryptorFinalize(GObject* object) > GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object)); > } > >-static GstCaps* webkitMediaCommonEncryptionDecryptTransformCaps(GstBaseTransform* base, GstPadDirection direction, GstCaps* caps, GstCaps* filter) >+static GstCaps* transformCaps(GstBaseTransform* base, GstPadDirection direction, GstCaps* caps, GstCaps* filter) > { > if (direction == GST_PAD_UNKNOWN) > return nullptr; >@@ -193,7 +218,51 @@ static GstCaps* webkitMediaCommonEncryptionDecryptTransformCaps(GstBaseTransform > return transformedCaps; > } > >-static GstFlowReturn webkitMediaCommonEncryptionDecryptTransformInPlace(GstBaseTransform* base, GstBuffer* buffer) >+static bool populateSubSamples(WebKitMediaCommonEncryptionDecrypt* self, GstBuffer* subSamplesBuffer, uint32_t subSampleCount, uint32_t totalSampleSize, Vector<WebCore::CDMInstance::SubSample>& subSamples) >+{ >+ if (!subSamplesBuffer) { >+ GST_ERROR_OBJECT(self, "subsample buffer is null"); >+ return false; >+ } >+ >+ WebCore::GstMappedBuffer mappedSubSamplesBuffer(subSamplesBuffer, GST_MAP_READ); >+ if (!mappedSubSamplesBuffer) { >+ GST_ERROR_OBJECT(self, "failed to map subsample buffer"); >+ return false; >+ } >+ >+ uint32_t position = 0; >+ uint32_t sampleIndex = 0; >+ >+ GUniquePtr<GstByteReader> reader(gst_byte_reader_new(mappedSubSamplesBuffer.data(), mappedSubSamplesBuffer.size())); >+ >+ while (position < totalSampleSize) { >+ uint16_t nBytesClear = 0; >+ uint32_t nBytesEncrypted = 0; >+ >+ if (sampleIndex < subSampleCount) { >+ if (!gst_byte_reader_get_uint16_be(reader.get(), &nBytesClear) >+ || !gst_byte_reader_get_uint32_be(reader.get(), &nBytesEncrypted)) { >+ GST_DEBUG_OBJECT(self, "subsamples in incorrect format"); >+ return false; >+ } >+ sampleIndex++; >+ } else { >+ nBytesClear = 0; >+ nBytesEncrypted = totalSampleSize - position; >+ } >+ >+ subSamples.append({nBytesClear, nBytesEncrypted}); >+ >+ GST_TRACE_OBJECT(self, "subsample index %u - %hu bytes clear (todo=%" PRIu32 ")", sampleIndex, nBytesClear, totalSampleSize - position); >+ position += nBytesClear + nBytesEncrypted; >+ } >+ >+ return true; >+} >+ >+ >+static GstFlowReturn transformInPlace(GstBaseTransform* base, GstBuffer* buffer) > { > WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(base); > WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self); >@@ -250,7 +319,7 @@ static GstFlowReturn webkitMediaCommonEncryptionDecryptTransformInPlace(GstBaseT > > GST_DEBUG_OBJECT(base, "protection meta: %" GST_PTR_FORMAT, protectionMeta->info); > >- unsigned subSampleCount; >+ uint32_t subSampleCount; > if (!gst_structure_get_uint(protectionMeta->info, "subsample_count", &subSampleCount)) { > GST_ERROR_OBJECT(self, "Failed to get subsample_count"); > gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); >@@ -270,7 +339,6 @@ static GstFlowReturn webkitMediaCommonEncryptionDecryptTransformInPlace(GstBaseT > } > > value = gst_structure_get_value(protectionMeta->info, "kid"); >- > if (!value) { > GST_ERROR_OBJECT(self, "Failed to get key id for buffer"); > gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); >@@ -286,12 +354,46 @@ static GstFlowReturn webkitMediaCommonEncryptionDecryptTransformInPlace(GstBaseT > } > > GstBuffer* ivBuffer = gst_value_get_buffer(value); >- WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self); > >- GST_TRACE_OBJECT(self, "decrypting"); >- if (!klass->decrypt(self, ivBuffer, keyIDBuffer, buffer, subSampleCount, subSamplesBuffer)) { >- GST_ERROR_OBJECT(self, "Decryption failed"); >- gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); >+ if (!ivBuffer) { >+ GST_ERROR_OBJECT(self, "Error, the ivBuffer is null"); >+ return GST_FLOW_NOT_SUPPORTED; >+ } >+ >+ if (!buffer) { >+ GST_ERROR_OBJECT(self, "No buffer to decrypt"); >+ return GST_FLOW_NOT_SUPPORTED; >+ } >+ >+ WebCore::GstMappedBuffer mappedIVBuffer(ivBuffer, GST_MAP_READ); >+ if (!mappedIVBuffer) { >+ GST_ERROR_OBJECT(self, "Failed to map IV"); >+ return GST_FLOW_NOT_SUPPORTED; >+ } >+ >+ WebCore::GstMappedBuffer mappedKeyIdBuffer(keyIDBuffer, GST_MAP_READ); >+ if (!mappedKeyIdBuffer) { >+ GST_ERROR_OBJECT(self, "Failed to map key id buffer"); >+ return GST_FLOW_NOT_SUPPORTED; >+ } >+ >+ WebCore::GstMappedBuffer mappedBuffer(buffer, GST_MAP_READWRITE); >+ if (!mappedBuffer) { >+ GST_ERROR_OBJECT(self, "Failed to map buffer"); >+ return GST_FLOW_NOT_SUPPORTED; >+ } >+ >+ Vector<WebCore::CDMInstance::SubSample> subSamples; >+ if (subSampleCount && !populateSubSamples(self, subSamplesBuffer, subSampleCount, mappedBuffer.size(), subSamples)) { >+ GST_ERROR_OBJECT(self, "failed to parse subsample ranges"); >+ return GST_FLOW_NOT_SUPPORTED; >+ } >+ >+ auto decryptionConfiguration = WebCore::CDMInstance::DecryptionConfig::createCencConfig(mappedIVBuffer.sharedBuffer(), >+ mappedKeyIdBuffer.sharedBuffer(), >+ WTFMove(subSamples)); >+ if (!priv->cdmInstance->decrypt(WTFMove(decryptionConfiguration), mappedBuffer.data(), mappedBuffer.size())) { >+ GST_ERROR_OBJECT(self, "failed to decrypt"); > return GST_FLOW_NOT_SUPPORTED; > } > >@@ -299,12 +401,11 @@ static GstFlowReturn webkitMediaCommonEncryptionDecryptTransformInPlace(GstBaseT > return GST_FLOW_OK; > } > >- >-static gboolean webkitMediaCommonEncryptionDecryptSinkEventHandler(GstBaseTransform* trans, GstEvent* event) >+static gboolean sinkEventHandler(GstBaseTransform* trans, GstEvent* event) > { > WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(trans); > WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self); >- WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self); >+ > gboolean result = FALSE; > > >@@ -317,19 +418,33 @@ static gboolean webkitMediaCommonEncryptionDecryptSinkEventHandler(GstBaseTransf > // events will not be handled by the demuxer, so the must be > // handled in here. > const GstStructure* structure = gst_event_get_structure(event); >- gst_structure_get(structure, "cdm-instance", G_TYPE_POINTER, &priv->cdmInstance, nullptr); >+ >+ // We must use a raw pointer to receive the bit-wise copy from >+ // glib's lcopy_value. If you pass an address to a RefPtr, you >+ // will bitwise copy over the first field of that class, which >+ // just so happens to be a pointer and dragons don't come out >+ // of your nose. However, this will bypass the ref-counting >+ // machinary in the RefPtr class, which means we'll lose a >+ // reference. >+ WebCore::CDMInstance* instance = nullptr; >+ gst_structure_get(structure, "cdm-instance", G_TYPE_POINTER, &instance, nullptr); >+ // Now force a ref-count bump. >+ priv->cdmInstance = instance; >+ > if (!priv->cdmInstance) { > GST_ERROR_OBJECT(self, "No CDM instance received"); > result = FALSE; > break; > } >- GST_DEBUG_OBJECT(self, "received a cdm instance %p", priv->cdmInstance.get()); > >- if (klass->handleKeyResponse(self, event)) { >- GST_DEBUG_OBJECT(self, "key received"); >- priv->keyReceived = true; >- priv->condition.notifyOne(); >- } >+ GST_DEBUG_OBJECT(self, "received a cdm instance %p, refcount %u", priv->cdmInstance.get(), priv->cdmInstance->refCount()); >+ >+ // FIXME: We are no longer interested in waiting for *keys*, >+ // rather just the CDM instance, rename these variables. This >+ // will be simplified and removed by the GstContext, so no >+ // point doing it in this patch. >+ priv->keyReceived = true; >+ priv->condition.notifyOne(); > > gst_event_unref(event); > result = TRUE; >@@ -343,7 +458,7 @@ static gboolean webkitMediaCommonEncryptionDecryptSinkEventHandler(GstBaseTransf > return result; > } > >-static gboolean webkitMediaCommonEncryptionDecryptorQueryHandler(GstBaseTransform* trans, GstPadDirection direction, GstQuery* query) >+static gboolean queryHandler(GstBaseTransform* trans, GstPadDirection direction, GstQuery* query) > { > if (gst_structure_has_name(gst_query_get_structure(query), "any-decryptor-waiting-for-key")) { > WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(trans); >@@ -353,7 +468,7 @@ static gboolean webkitMediaCommonEncryptionDecryptorQueryHandler(GstBaseTransfor > return GST_BASE_TRANSFORM_CLASS(parent_class)->query(trans, direction, query); > } > >-static GstStateChangeReturn webKitMediaCommonEncryptionDecryptorChangeState(GstElement* element, GstStateChange transition) >+static GstStateChangeReturn changeState(GstElement* element, GstStateChange transition) > { > WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(element); > WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self); >diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h >index 22211254eb7d9ef5466057fd7d5434219ecef2dd..d650f46b775a015f94c2424e46463fda884df75e 100644 >--- a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h >+++ b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h >@@ -53,8 +53,6 @@ struct _WebKitMediaCommonEncryptionDecryptClass { > GstBaseTransformClass parentClass; > > const char* protectionSystemId; >- gboolean (*handleKeyResponse)(WebKitMediaCommonEncryptionDecrypt*, GstEvent* event); >- gboolean (*decrypt)(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* ivBuffer, GstBuffer* keyIDBuffer, GstBuffer* buffer, unsigned subSamplesCount, GstBuffer* subSamplesBuffer); > }; > > G_END_DECLS >diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp >index 80381f35149eaaca39b5a2206929736c6323e904..e6e804bd6e4154fe8f49386cea737c20a3eb2b88 100644 >--- a/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp >+++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp >@@ -903,43 +903,6 @@ MediaTime MediaPlayerPrivateGStreamerMSE::maxMediaTimeSeekable() const > return result; > } > >-#if ENABLE(ENCRYPTED_MEDIA) >-void MediaPlayerPrivateGStreamerMSE::attemptToDecryptWithInstance(CDMInstance& instance) >-{ >- if (is<CDMInstanceClearKey>(instance)) { >- auto& ckInstance = downcast<CDMInstanceClearKey>(instance); >- if (ckInstance.keys().isEmpty()) >- return; >- >- GValue keyIDList = G_VALUE_INIT, keyValueList = G_VALUE_INIT; >- g_value_init(&keyIDList, GST_TYPE_LIST); >- g_value_init(&keyValueList, GST_TYPE_LIST); >- >- auto appendBuffer = >- [](GValue* valueList, const SharedBuffer& buffer) >- { >- GValue* bufferValue = g_new0(GValue, 1); >- g_value_init(bufferValue, GST_TYPE_BUFFER); >- gst_value_take_buffer(bufferValue, >- gst_buffer_new_wrapped(g_memdup(buffer.data(), buffer.size()), buffer.size())); >- gst_value_list_append_and_take_value(valueList, bufferValue); >- }; >- >- for (auto& key : ckInstance.keys()) { >- appendBuffer(&keyIDList, *key.keyIDData); >- appendBuffer(&keyValueList, *key.keyValueData); >- } >- >- GUniquePtr<GstStructure> structure(gst_structure_new_empty("drm-cipher-clearkey")); >- gst_structure_set_value(structure.get(), "key-ids", &keyIDList); >- gst_structure_set_value(structure.get(), "key-values", &keyValueList); >- gst_structure_set(structure.get(), "cdm-instance", G_TYPE_POINTER, &instance, nullptr); >- >- gst_element_send_event(m_playbackPipeline->pipeline(), gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM_OOB, structure.release())); >- } >-} >-#endif >- > } // namespace WebCore. > > #endif // USE(GSTREAMER) >diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.h b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.h >index 2257e9e6830acb18c6f08f7186bf7758b72735d7..2dfa732fcbf26ba687f1a9814ab7663eef985474 100644 >--- a/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.h >+++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.h >@@ -85,10 +85,6 @@ public: > static bool supportsCodec(String codec); > static bool supportsAllCodecs(const Vector<String>& codecs); > >-#if ENABLE(ENCRYPTED_MEDIA) >- void attemptToDecryptWithInstance(CDMInstance&) final; >-#endif >- > private: > static void getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>&); > static MediaPlayer::SupportsType supportsType(const MediaEngineSupportParameters&); >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index 03cf9cf78ad092fa65290ea6c5f90cdc3510f783..29824744f1601a3d137099779fc278852a1f53ed 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,13 @@ >+2018-11-30 Charlie Turner <cturner@igalia.com> >+ >+ [EME] Create and move to a decryption API implemented by CDMInstances >+ https://bugs.webkit.org/show_bug.cgi?id=192229 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * MiniBrowser/wpe/main.cpp: >+ (main): Enable encrypted-media by default. >+ > 2018-11-13 Daniel Bates <dabates@apple.com> > > Consolidate WebKit UIKitSPI.h and UIKitTestSPI.h >diff --git a/Tools/MiniBrowser/wpe/main.cpp b/Tools/MiniBrowser/wpe/main.cpp >index e0c29b2129934371274a6f25451eae9821c4689f..5c448a8e07126189f3411ce71010fbfd62911ee0 100644 >--- a/Tools/MiniBrowser/wpe/main.cpp >+++ b/Tools/MiniBrowser/wpe/main.cpp >@@ -177,6 +177,7 @@ int main(int argc, char *argv[]) > "enable-developer-extras", TRUE, > "enable-webgl", TRUE, > "enable-media-stream", TRUE, >+ "enable-encrypted-media", TRUE, > nullptr); > > auto* webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 476d1baa26893b46c6a20a8631aa373b36ce39be..63dc14f3958f5d3eaf02ad01cfd32e5050ba2a3d 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,15 @@ >+2018-11-30 Charlie Turner <cturner@igalia.com> >+ >+ [EME] Create and move to a decryption API implemented by CDMInstances >+ https://bugs.webkit.org/show_bug.cgi?id=192229 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Update to new failure output after this refactoring. >+ >+ * platform/gtk/imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-playback-persistent-license-events.https-expected.txt: >+ * platform/wpe/imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-playback-persistent-license-events.https-expected.txt: >+ > 2018-11-13 Matt Baker <mattbaker@apple.com> > > Web Inspector: Table should support select all (Cmd-A) >diff --git a/LayoutTests/platform/gtk/imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-playback-persistent-license-events.https-expected.txt b/LayoutTests/platform/gtk/imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-playback-persistent-license-events.https-expected.txt >index d1fb1b2e7212dd1dbb33ffb6f94de5de3af0eaa3..5c276f3fc5a547efa77e0fdef70820a4ba9b80c0 100644 >--- a/LayoutTests/platform/gtk/imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-playback-persistent-license-events.https-expected.txt >+++ b/LayoutTests/platform/gtk/imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-playback-persistent-license-events.https-expected.txt >@@ -1,3 +1,3 @@ > >-FAIL org.w3.clearkey, persistent-license, mp4, playback, check events assert_equals: Expected 1st event to be 'generaterequest' expected "generaterequest" but got "license-request" >+FAIL org.w3.clearkey, persistent-license, mp4, playback, check events assert_equals: Expected 1st event to be 'generaterequest' expected "generaterequest" but got "playing" > >diff --git a/LayoutTests/platform/wpe/imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-playback-persistent-license-events.https-expected.txt b/LayoutTests/platform/wpe/imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-playback-persistent-license-events.https-expected.txt >index d1fb1b2e7212dd1dbb33ffb6f94de5de3af0eaa3..5c276f3fc5a547efa77e0fdef70820a4ba9b80c0 100644 >--- a/LayoutTests/platform/wpe/imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-playback-persistent-license-events.https-expected.txt >+++ b/LayoutTests/platform/wpe/imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-playback-persistent-license-events.https-expected.txt >@@ -1,3 +1,3 @@ > >-FAIL org.w3.clearkey, persistent-license, mp4, playback, check events assert_equals: Expected 1st event to be 'generaterequest' expected "generaterequest" but got "license-request" >+FAIL org.w3.clearkey, persistent-license, mp4, playback, check events assert_equals: Expected 1st event to be 'generaterequest' expected "generaterequest" but got "playing" >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 192229
:
356374
|
356384
|
357229
|
359479
|
359480