Changeset 256429 in webkit
- Timestamp:
- Feb 12, 2020 3:11:22 AM (4 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 4 added
- 15 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r256428 r256429 1 2020-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 1 202 2020-02-12 Víctor Manuel Jáquez Leal <vjaquez@igalia.com> 2 203 -
trunk/Source/WebCore/Sources.txt
r256417 r256429 1820 1820 1821 1821 platform/encryptedmedia/CDMFactory.cpp 1822 platform/encryptedmedia/CDMProxy.cpp 1822 1823 1823 1824 platform/graphics/ANGLEWebKitBridge.cpp -
trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj
r256424 r256429 13884 13884 CD94A5D11F71CB6B00F525C5 /* CDMFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CDMFactory.cpp; sourceTree = "<group>"; }; 13885 13885 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>"; }; 13886 13888 CD94A5D31F71CB6C00F525C5 /* CDMMediaCapability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDMMediaCapability.h; sourceTree = "<group>"; }; 13887 13889 CD94A5D41F71CB6C00F525C5 /* CDMKeyStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDMKeyStatus.h; sourceTree = "<group>"; }; … … 26261 26263 CD94A5D61F71CB6D00F525C5 /* CDMMessageType.h */, 26262 26264 CD94A5CF1F71CB6A00F525C5 /* CDMPrivate.h */, 26265 CD94A5CF1F71CB6100F525D5 /* CDMProxy.cpp */, 26266 CD94A5CF1F71CB6100F525C5 /* CDMProxy.h */, 26263 26267 CD94A5CC1F71CB6900F525C5 /* CDMRequirement.h */, 26264 26268 CD94A5D01F71CB6B00F525C5 /* CDMRestrictions.h */, -
trunk/Source/WebCore/platform/GStreamer.cmake
r252917 r256429 165 165 166 166 list(APPEND WebCore_SOURCES 167 platform/encryptedmedia/CDMProxy.cpp 167 168 platform/encryptedmedia/clearkey/CDMClearKey.cpp 168 169 169 platform/graphics/gstreamer/eme/CDMFactoryGStreamer.cpp 170 platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp 170 171 ) 171 172 -
trunk/Source/WebCore/platform/encryptedmedia/CDMInstance.h
r249548 r256429 40 40 41 41 class SharedBuffer; 42 43 42 class CDMInstanceSession; 44 class ProxyCDM;45 46 // Handle to a "real" CDM, not the JavaScript facade. This can be used47 // from background threads (i.e. decryptors).48 class ProxyCDM : public ThreadSafeRefCounted<ProxyCDM> {49 public:50 virtual ~ProxyCDM() = default;51 };52 53 43 struct CDMKeySystemConfiguration; 54 44 … … 78 68 virtual const String& keySystem() const = 0; 79 69 virtual RefPtr<CDMInstanceSession> createSession() = 0; 80 virtual RefPtr<ProxyCDM> proxyCDM() const = 0;81 70 82 71 enum class HDCPStatus { -
trunk/Source/WebCore/platform/encryptedmedia/clearkey/CDMClearKey.cpp
r254334 r256429 37 37 #include "Logging.h" 38 38 #include "SharedBuffer.h" 39 #include <algorithm> 40 #include <iterator> 39 41 #include <wtf/JSONValues.h> 40 42 #include <wtf/MainThread.h> … … 43 45 44 46 namespace WebCore { 45 46 // ClearKey CENC SystemID.47 // https://www.w3.org/TR/eme-initdata-cenc/#common-system48 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;73 47 74 48 static RefPtr<JSON::Object> parseJSONObject(const SharedBuffer& buffer) … … 89 63 } 90 64 91 static Optional<Vector< CDMInstanceClearKey::Key>> parseLicenseFormat(const JSON::Object& root)65 static Optional<Vector<RefPtr<Key>>> parseLicenseFormat(const JSON::Object& root) 92 66 { 93 67 // If the 'keys' key is present in the root object, parse the JSON further … … 102 76 return WTF::nullopt; 103 77 104 Vector< CDMInstanceClearKey::Key> decodedKeys;78 Vector<RefPtr<Key>> decodedKeys; 105 79 bool validFormat = std::all_of(keysArray->begin(), keysArray->end(), 106 80 [&decodedKeys] (const auto& value) { … … 117 91 return false; 118 92 119 Vector< char> keyIDData, keyValueData;93 Vector<uint8_t> keyIDData, keyValueData; 120 94 if (!WTF::base64URLDecode(keyID, { keyIDData }) || !WTF::base64URLDecode(keyValue, { keyValueData })) 121 95 return false; 122 96 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))); 124 98 return true; 125 99 }); … … 182 156 183 157 // Check the overflow InitData. 184 if (index + 12 + clearKeyCencSystemIdSize >= initDataSize)158 if (index + 12 + ClearKey::cencSystemIdSize >= initDataSize) 185 159 return keyIdsMap; 186 160 … … 192 166 193 167 // 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)) { 195 169 foundPssh = true; 196 170 break; … … 203 177 return keyIdsMap; 204 178 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. 206 180 207 181 // Check the overflow. … … 213 187 214 188 // Check the overflow. 215 if ((index + (keyIdsMap.first * keyIdSize)) >= initDataSize)189 if ((index + (keyIdsMap.first * ClearKey::KeyIDSizeInBytes)) >= initDataSize) 216 190 return keyIdsMap; 217 191 … … 252 226 // An array of key IDs. Each element of the array is the base64url encoding of the octet sequence containing the key ID value. 253 227 for (unsigned i = 0; i < keyIdCount; i++) { 254 String keyId = WTF::base64URLEncode(&data[index], keyIdSize);228 String keyId = WTF::base64URLEncode(&data[index], ClearKey::KeyIDSizeInBytes); 255 229 keyIdsArray->pushString(keyId); 256 index += keyIdSize;230 index += ClearKey::KeyIDSizeInBytes; 257 231 } 258 232 … … 457 431 } 458 432 459 // This is for thread-safety during an architectural situation that is460 // less than ideal. The GStreamer decryptors currently need to iterate461 // all known session keys to find the key data for priming462 // GCrypt. Ideally, all decryption would be the responsibility of463 // ProxyCDM object like this one. What the background GStreamer464 // thread was doing was getting copies (i.e. ref()'s) of SharedBuffers465 // created on the main-thread. With the new safety assertions in466 // WebKit, we can no longer do this. Instead, convert the refcounted467 // SharedBuffers into Strings which can be safely copied across468 // 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() const475 {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 497 433 CDMInstanceClearKey::~CDMInstanceClearKey() = default; 498 434 … … 536 472 RefPtr<CDMInstanceSession> CDMInstanceClearKey::createSession() 537 473 { 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; 568 477 } 569 478 … … 573 482 ++s_sessionIdValue; 574 483 484 m_sessionID = String::number(s_sessionIdValue); 485 575 486 if (equalLettersIgnoringASCIICase(initDataType, "cenc")) 576 487 initData = extractKeyidsFromCencInitData(initData.get()); … … 580 491 581 492 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 { 583 494 if (!weakThis) 584 495 return; 585 496 586 callback(WTFMove(initData), String::number(sessionIdValue), false, Succeeded);497 callback(WTFMove(initData), m_sessionID, false, Succeeded); 587 498 }); 588 499 } … … 590 501 void CDMInstanceSessionClearKey::updateLicense(const String& sessionId, LicenseType, const SharedBuffer& response, LicenseUpdateCallback&& callback) 591 502 { 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 594 508 auto dispatchCallback = 595 509 [this, &callback](bool sessionWasClosed, Optional<KeyStatusVector>&& changedKeys, SuccessValue succeeded) { … … 605 519 RefPtr<JSON::Object> root = parseJSONObject(response); 606 520 if (!root) { 521 LOG(EME, "EME - ClearKey - session %s update payload was not valid JSON", sessionId.utf8().data()); 607 522 dispatchCallback(false, WTF::nullopt, SuccessValue::Failed); 608 523 return; 609 524 } 610 525 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()); 612 527 613 528 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()); 641 532 642 533 Optional<KeyStatusVector> changedKeys; 643 534 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(); 653 538 } 654 539 … … 658 543 659 544 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(); 662 548 dispatchCallback(true, WTF::nullopt, SuccessValue::Succeeded); 663 549 return; 664 550 } 665 551 666 // Bail in case no format was recognized.552 LOG(EME, "EME - ClearKey - session %s update payload was an unrecognized format", sessionId.utf8().data()); 667 553 dispatchCallback(false, WTF::nullopt, SuccessValue::Failed); 668 554 } … … 670 556 void CDMInstanceSessionClearKey::loadSession(LicenseType, const String& sessionId, const String&, LoadSessionCallback&& callback) 671 557 { 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) 691 566 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 }); 701 570 } 702 571 … … 714 583 void CDMInstanceSessionClearKey::removeSessionData(const String& sessionId, LicenseType, RemoveSessionDataCallback&& callback) 715 584 { 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 717 591 auto dispatchCallback = 718 592 [this, &callback](KeyStatusVector&& keyStatusVector, Optional<Ref<SharedBuffer>>&& message, SuccessValue success) { … … 728 602 // Construct the KeyStatusVector object, representing released keys, and the message in the 729 603 // 'license release' format. 730 KeyStatusVector keyStatusVector ;604 KeyStatusVector keyStatusVector = m_keyStore.allKeysAsReleased(); 731 605 RefPtr<SharedBuffer> message; 732 606 { 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, and742 // 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 751 607 // Construct JSON that represents the 'license release' format, creating a 'kids' array 752 608 // of base64URL-encoded key IDs for all keys that were associated with this session. … … 754 610 { 755 611 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())); 759 615 } 760 616 rootObject->setArray("kids", WTFMove(array)); … … 767 623 } 768 624 625 m_keyStore.removeAllKeys(); 769 626 dispatchCallback(WTFMove(keyStatusVector), Ref<SharedBuffer>(*message), SuccessValue::Succeeded); 770 627 } -
trunk/Source/WebCore/platform/encryptedmedia/clearkey/CDMClearKey.h
r254334 r256429 32 32 33 33 #include "CDMFactory.h" 34 #include "CDMInstance.h"35 34 #include "CDMInstanceSession.h" 36 35 #include "CDMPrivate.h" 36 #include "CDMProxy.h" 37 37 #include "SharedBuffer.h" 38 38 #include <wtf/WeakPtr.h> 39 39 40 40 namespace WebCore { 41 42 namespace ClearKey { 43 44 // ClearKey CENC SystemID. 45 // https://www.w3.org/TR/eme-initdata-cenc/#common-system 46 const uint8_t cencSystemId[] = { 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b }; 47 const unsigned cencSystemIdSize = sizeof(cencSystemId); 48 enum { 49 AES128CTRBlockSizeInBytes = 16, 50 KeyIDSizeInBytes = 16, 51 IVSizeInBytes = 16, 52 }; 53 54 } // namespace ClearKey 41 55 42 56 class CDMFactoryClearKey final : public CDMFactory { … … 78 92 }; 79 93 80 class ProxyCDMClearKey final : public ProxyCDM{94 class CDMInstanceClearKey final : public CDMInstanceProxy, public CanMakeWeakPtr<CDMInstanceClearKey> { 81 95 public: 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();97 96 virtual ~CDMInstanceClearKey(); 98 97 98 // CDMInstance 99 99 ImplementationType implementationType() const final { return ImplementationType::ClearKey; } 100 101 100 SuccessValue initializeWithConfiguration(const CDMKeySystemConfiguration&) final; 102 101 SuccessValue setDistinctiveIdentifiersAllowed(bool) final; … … 106 105 const String& keySystem() const final; 107 106 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;139 107 }; 140 108 141 109 class CDMInstanceSessionClearKey final : public CDMInstanceSession, public CanMakeWeakPtr<CDMInstanceSessionClearKey> { 142 110 public: 111 CDMInstanceSessionClearKey(CDMInstanceClearKey& parent) 112 : m_parentInstance(parent) { } 143 113 void requestLicense(LicenseType, const AtomString& initDataType, Ref<SharedBuffer>&& initData, LicenseCallback&&) final; 144 114 void updateLicense(const String&, LicenseType, const SharedBuffer&, LicenseUpdateCallback&&) final; … … 147 117 void removeSessionData(const String&, LicenseType, RemoveSessionDataCallback&&) final; 148 118 void storeRecordOfKeyUsage(const String&) final; 119 private: 120 String m_sessionID; 121 CDMInstanceClearKey& m_parentInstance; 122 KeyStore m_keyStore; 149 123 }; 150 124 -
trunk/Source/WebCore/platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.h
r254288 r256429 81 81 SuccessValue setStorageDirectory(const String&) final; 82 82 RefPtr<CDMInstanceSession> createSession() final; 83 RefPtr<ProxyCDM> proxyCDM() const final { return nullptr; }84 83 85 84 const String& keySystem() const final; -
trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
r256353 r256429 66 66 #if ENABLE(ENCRYPTED_MEDIA) 67 67 #include "CDMInstance.h" 68 #include "CDMProxyClearKey.h" 68 69 #include "GStreamerEMEUtilities.h" 69 70 #include "SharedBuffer.h" … … 1789 1790 } 1790 1791 1792 InitData 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 1815 bool 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 1791 1836 bool MediaPlayerPrivateGStreamer::handleSyncMessage(GstMessage* message) 1792 1837 { … … 1824 1869 #if ENABLE(ENCRYPTED_MEDIA) 1825 1870 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()) { 1861 1874 const char* preferredKeySystemUuid = GStreamerEMEUtilities::keySystemToUuid(m_cdmInstance->keySystem()); 1862 1875 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)); … … 1866 1879 gst_structure_set(contextStructure, "decryption-system-id", G_TYPE_STRING, preferredKeySystemUuid, nullptr); 1867 1880 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; 1872 1886 } 1873 1887 #endif // ENABLE(ENCRYPTED_MEDIA) … … 2198 2212 processMpegTsSection(section); 2199 2213 gst_mpegts_section_unref(section); 2200 }2201 #endif2202 #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 current2207 // 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 be2208 // 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);2213 2214 } 2214 2215 #endif … … 3729 3730 } 3730 3731 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)); 3734 3738 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); 3736 3740 gst_element_set_context(GST_ELEMENT(m_pipeline.get()), context.get()); 3737 3741 3738 GST_DEBUG_OBJECT(m_pipeline.get(), "CDM proxy instance %p dispatched as context", m_cdmInstance->proxy CDM().get());3742 GST_DEBUG_OBJECT(m_pipeline.get(), "CDM proxy instance %p dispatched as context", m_cdmInstance->proxy().get()); 3739 3743 3740 3744 LockHolder lock(m_cdmAttachmentMutex); … … 3758 3762 m_cdmInstance = nullptr; 3759 3763 3760 GRefPtr<GstContext> context = adoptGRef(gst_context_new("drm-cdm- instance", FALSE));3764 GRefPtr<GstContext> context = adoptGRef(gst_context_new("drm-cdm-proxy", FALSE)); 3761 3765 gst_element_set_context(GST_ELEMENT(m_pipeline.get()), context.get()); 3762 3766 } … … 3791 3795 } 3792 3796 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 3805 3797 bool MediaPlayerPrivateGStreamer::waitingForKey() const 3806 3798 { 3807 if (!m_pipeline )3799 if (!m_pipeline || !m_cdmInstance) 3808 3800 return false; 3809 3801 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(); 3838 3803 } 3839 3804 #endif -
trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h
r254682 r256429 75 75 #include "TextureMapperPlatformLayerProxyProvider.h" 76 76 #endif 77 #endif 78 79 #if ENABLE(ENCRYPTED_MEDIA) 80 #include "CDMProxy.h" 77 81 #endif 78 82 … … 372 376 Lock m_cdmAttachmentMutex; 373 377 Condition m_cdmAttachmentCondition; 374 RefPtr< const CDMInstance> m_cdmInstance;378 RefPtr<CDMInstanceProxy> m_cdmInstance; 375 379 376 380 Lock m_protectionMutex; // Guards access to m_handledProtectionEvents. … … 451 455 void attemptToDecryptWithLocalInstance(); 452 456 void initializationDataEncountered(InitData&&); 453 void setWaitingForKey(bool); 457 InitData parseInitDataFromProtectionMessage(GstMessage*); 458 bool waitForCDMAttachment(); 454 459 #endif 455 460 -
trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp
r253367 r256429 26 26 27 27 #include "CDMClearKey.h" 28 #include "CDMProxyClearKey.h" 28 29 #include "GStreamerCommon.h" 29 30 #include "GStreamerEMEUtilities.h" … … 38 39 #define WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_CK_DECRYPT, WebKitMediaClearKeyDecryptPrivate)) 39 40 struct _WebKitMediaClearKeyDecryptPrivate { 40 RefPtr<ProxyCDMClearKey> proxyCDM; 41 gcry_cipher_hd_t handle; 41 RefPtr<CDMProxyClearKey> cdmProxy; 42 42 }; 43 43 44 44 static void finalize(GObject*); 45 static bool handleKeyResponse(WebKitMediaCommonEncryptionDecrypt* self, RefPtr<ProxyCDM>);45 static bool cdmProxyAttached(WebKitMediaCommonEncryptionDecrypt* self, RefPtr<CDMProxy>); 46 46 static bool decrypt(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* iv, GstBuffer* keyid, GstBuffer* sample, unsigned subSamplesCount, GstBuffer* subSamples); 47 47 … … 85 85 WebKitMediaCommonEncryptionDecryptClass* cencClass = WEBKIT_MEDIA_CENC_DECRYPT_CLASS(klass); 86 86 cencClass->protectionSystemId = GStreamerEMEUtilities::s_ClearKeyUUID; 87 cencClass-> handleKeyResponse = GST_DEBUG_FUNCPTR(handleKeyResponse);87 cencClass->cdmProxyAttached = GST_DEBUG_FUNCPTR(cdmProxyAttached); 88 88 cencClass->decrypt = GST_DEBUG_FUNCPTR(decrypt); 89 89 … … 94 94 { 95 95 WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(self); 96 97 96 self->priv = priv; 98 97 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 }103 98 } 104 99 … … 107 102 WebKitMediaClearKeyDecrypt* self = WEBKIT_MEDIA_CK_DECRYPT(object); 108 103 WebKitMediaClearKeyDecryptPrivate* priv = self->priv; 109 gcry_cipher_close(priv->handle);110 104 priv->~WebKitMediaClearKeyDecryptPrivate(); 111 105 … … 113 107 } 114 108 115 static bool handleKeyResponse(WebKitMediaCommonEncryptionDecrypt* self, RefPtr<ProxyCDM> proxyCDM)109 static bool cdmProxyAttached(WebKitMediaCommonEncryptionDecrypt* self, RefPtr<CDMProxy> cdmProxy) 116 110 { 117 111 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; 120 114 } 121 115 122 static bool findAndSetKey(WebKitMediaClearKeyDecryptPrivate* priv, const String&& keyID)116 static bool decrypt(WebKitMediaCommonEncryptionDecrypt* self, GstBuffer* ivBuffer, GstBuffer* keyIDBuffer, GstBuffer* buffer, unsigned subsampleCount, GstBuffer* subsamplesBuffer) 123 117 { 124 String keyValue;118 WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self)); 125 119 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"); 151 122 return false; 152 123 } … … 155 126 if (!mappedIVBuffer) { 156 127 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");178 128 return false; 179 129 } … … 191 141 } 192 142 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; 209 160 } 210 return true; 161 context.subsamplesBuffer = mappedSubsamplesBuffer->data(); 162 context.subsamplesBufferSizeInBytes = mappedSubsamplesBuffer->size(); 211 163 } 212 164 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); 259 166 } 260 167 -
trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp
r249548 r256429 26 26 #if ENABLE(ENCRYPTED_MEDIA) && USE(GSTREAMER) 27 27 28 #include "CDMProxy.h" 28 29 #include "GStreamerCommon.h" 29 30 #include "GStreamerEMEUtilities.h" … … 31 32 #include <wtf/PrintStream.h> 32 33 #include <wtf/RunLoop.h> 33 34 using WebCore::ProxyCDM; 34 #include <wtf/Scope.h> 35 36 using WebCore::CDMProxy; 35 37 36 38 #define WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_CENC_DECRYPT, WebKitMediaCommonEncryptionDecryptPrivate)) 37 39 struct _WebKitMediaCommonEncryptionDecryptPrivate { 38 40 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; 44 45 }; 46 47 static constexpr Seconds MaxSecondsToWaitForCDMProxy = 5_s; 45 48 46 49 static GstStateChangeReturn changeState(GstElement*, GstStateChange transition); … … 49 52 static GstFlowReturn transformInPlace(GstBaseTransform*, GstBuffer*); 50 53 static gboolean sinkEventHandler(GstBaseTransform*, GstEvent*); 51 static gboolean queryHandler(GstBaseTransform*, GstPadDirection, GstQuery*);52 static bool isCDMInstanceAvailable(WebKitMediaCommonEncryptionDecrypt*);53 54 static void setContext(GstElement*, GstContext*); 54 55 55 56 56 GST_DEBUG_CATEGORY_STATIC(webkit_media_common_encryption_decrypt_debug_category); … … 77 77 baseTransformClass->transform_ip_on_passthrough = FALSE; 78 78 baseTransformClass->sink_event = GST_DEBUG_FUNCPTR(sinkEventHandler); 79 baseTransformClass->query = GST_DEBUG_FUNCPTR(queryHandler);80 79 81 80 g_type_class_add_private(klass, sizeof(WebKitMediaCommonEncryptionDecryptPrivate)); … … 178 177 WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(base); 179 178 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"); 187 190 return GST_FLOW_NOT_SUPPORTED; 188 191 } 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()); 203 193 } 204 194 … … 208 198 return GST_FLOW_NOT_SUPPORTED; 209 199 } 200 auto removeProtectionMetaOnReturn = makeScopeExit([buffer, protectionMeta] { 201 gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta)); 202 }); 210 203 211 204 unsigned ivSize; 212 205 if (!gst_structure_get_uint(protectionMeta->info, "iv_size", &ivSize)) { 213 206 GST_ERROR_OBJECT(self, "Failed to get iv_size"); 214 gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));215 207 return GST_FLOW_NOT_SUPPORTED; 216 208 } … … 219 211 if (!gst_structure_get_boolean(protectionMeta->info, "encrypted", &encrypted)) { 220 212 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) 227 217 return GST_FLOW_OK; 228 }229 218 230 219 GST_DEBUG_OBJECT(base, "protection meta: %" GST_PTR_FORMAT, protectionMeta->info); … … 233 222 if (!gst_structure_get_uint(protectionMeta->info, "subsample_count", &subSampleCount)) { 234 223 GST_ERROR_OBJECT(self, "Failed to get subsample_count"); 235 gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));236 224 return GST_FLOW_NOT_SUPPORTED; 237 225 } … … 243 231 if (!value) { 244 232 GST_ERROR_OBJECT(self, "Failed to get subsamples"); 245 gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));246 233 return GST_FLOW_NOT_SUPPORTED; 247 234 } 248 235 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 } 249 240 } 250 241 … … 253 244 if (!value) { 254 245 GST_ERROR_OBJECT(self, "Failed to get key id for buffer"); 255 gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));256 246 return GST_FLOW_NOT_SUPPORTED; 257 247 } … … 261 251 if (!value) { 262 252 GST_ERROR_OBJECT(self, "Failed to get IV for sample"); 263 gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));264 253 return GST_FLOW_NOT_SUPPORTED; 265 254 } … … 271 260 if (!klass->decrypt(self, ivBuffer, keyIDBuffer, buffer, subSampleCount, subSamplesBuffer)) { 272 261 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 278 265 return GST_FLOW_OK; 279 266 } 280 267 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; 268 static 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 275 static 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 299 static 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 311 static 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; 306 328 } 307 329 … … 309 331 { 310 332 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);313 333 gboolean result = FALSE; 314 334 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. 315 341 switch (GST_EVENT_TYPE(event)) { 316 342 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); 336 346 gst_event_unref(event); 337 result = TRUE;338 347 break; 339 348 } … … 346 355 } 347 356 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 358 357 static GstStateChangeReturn changeState(GstElement* element, GstStateChange transition) 359 358 { … … 364 363 case GST_STATE_CHANGE_PAUSED_TO_READY: 365 364 GST_DEBUG_OBJECT(self, "PAUSED->READY"); 366 priv->c ondition.notifyOne();365 priv->cdmAttachmentCondition.notifyOne(); 367 366 break; 368 367 default: … … 381 380 WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(element); 382 381 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); 389 390 return; 390 391 } -
trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h
r249548 r256429 25 25 #if ENABLE(ENCRYPTED_MEDIA) && USE(GSTREAMER) 26 26 27 #include <CDM Instance.h>27 #include <CDMProxy.h> 28 28 #include <gst/base/gstbasetransform.h> 29 29 #include <gst/gst.h> … … 56 56 57 57 const char* protectionSystemId; 58 bool (* handleKeyResponse)(WebKitMediaCommonEncryptionDecrypt*, RefPtr<WebCore::ProxyCDM>);59 bool (*decrypt)(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* ivBuffer, GstBuffer* keyIDBuffer, GstBuffer* buffer, unsigned sub SamplesCount, GstBuffer* subSamplesBuffer);58 bool (*cdmProxyAttached)(WebKitMediaCommonEncryptionDecrypt*, RefPtr<WebCore::CDMProxy>); 59 bool (*decrypt)(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* ivBuffer, GstBuffer* keyIDBuffer, GstBuffer* buffer, unsigned subsamplesCount, GstBuffer* subsamplesBuffer); 60 60 }; 61 61 -
trunk/Source/WebCore/testing/MockCDMFactory.cpp
r249548 r256429 227 227 } 228 228 229 ProxyCDMMock::~ProxyCDMMock() = default;230 231 229 MockCDMInstance::MockCDMInstance(WeakPtr<MockCDM> cdm) 232 230 : m_cdm(cdm) … … 295 293 { 296 294 return adoptRef(new MockCDMInstanceSession(makeWeakPtr(*this))); 297 }298 299 RefPtr<ProxyCDM> MockCDMInstance::proxyCDM() const300 {301 return adoptRef(new ProxyCDMMock());302 295 } 303 296 -
trunk/Source/WebCore/testing/MockCDMFactory.h
r249548 r256429 100 100 }; 101 101 102 class ProxyCDMMock final : public ProxyCDM {103 public:104 virtual ~ProxyCDMMock();105 };106 107 102 class MockCDM : public CDMPrivate, public CanMakeWeakPtr<MockCDM> { 108 103 WTF_MAKE_FAST_ALLOCATED; … … 151 146 const String& keySystem() const final; 152 147 RefPtr<CDMInstanceSession> createSession() final; 153 RefPtr<ProxyCDM> proxyCDM() const final;154 148 155 149 WeakPtr<MockCDM> m_cdm;
Note: See TracChangeset
for help on using the changeset viewer.