From f414ddfb55e3cb96f44ffb76f0761182541d3810 Mon Sep 17 00:00:00 2001 From: CastagnaIT Date: Mon, 2 Sep 2024 15:22:33 +0200 Subject: [PATCH] [Decrypters] Reworked to use smartpointers, new wv interfaces --- lib/jni/jni/src/ClassLoader.cpp | 4 +- lib/jni/jni/src/ClassLoader.h | 3 + lib/jni/jni/src/MediaDrmOnEventListener.h | 5 +- src/Session.cpp | 17 +- src/Session.h | 9 +- src/common/AdaptiveCencSampleDecrypter.cpp | 4 +- src/common/AdaptiveCencSampleDecrypter.h | 6 +- src/decrypters/HelperWv.h | 60 ++++++ src/decrypters/Helpers.cpp | 28 +++ src/decrypters/Helpers.h | 8 + src/decrypters/IDecrypter.h | 18 +- src/decrypters/clearkey/ClearKeyDecrypter.cpp | 32 ++-- src/decrypters/clearkey/ClearKeyDecrypter.h | 12 +- src/decrypters/widevine/WVCdmAdapter.cpp | 101 +++++++--- src/decrypters/widevine/WVCdmAdapter.h | 53 +++--- .../widevine/WVCencSingleSampleDecrypter.cpp | 106 +++++++---- .../widevine/WVCencSingleSampleDecrypter.h | 24 +-- src/decrypters/widevine/WVDecrypter.cpp | 90 +++++---- src/decrypters/widevine/WVDecrypter.h | 32 ++-- .../widevineandroid/WVCdmAdapter.cpp | 150 +++++++++++---- src/decrypters/widevineandroid/WVCdmAdapter.h | 106 ++++++++--- .../WVCencSingleSampleDecrypter.cpp | 143 ++++++++------ .../WVCencSingleSampleDecrypter.h | 34 ++-- .../widevineandroid/WVDecrypter.cpp | 180 ++++++------------ src/decrypters/widevineandroid/WVDecrypter.h | 88 ++------- src/main.cpp | 2 +- src/samplereader/FragmentedSampleReader.cpp | 2 +- src/samplereader/FragmentedSampleReader.h | 4 +- src/samplereader/SampleReader.h | 2 +- 29 files changed, 756 insertions(+), 567 deletions(-) diff --git a/lib/jni/jni/src/ClassLoader.cpp b/lib/jni/jni/src/ClassLoader.cpp index a3c7ee2c2..9785f4fed 100644 --- a/lib/jni/jni/src/ClassLoader.cpp +++ b/lib/jni/jni/src/ClassLoader.cpp @@ -11,7 +11,7 @@ using namespace jni; -CJNIClassLoader::CJNIClassLoader(const std::string &dexPath) +jni::CJNIClassLoader::CJNIClassLoader(const std::string &dexPath) : CJNIBase("dalvik/system/PathClassLoader") { jhobject systemLoader = call_static_method("java/lang/ClassLoader", "getSystemClassLoader", "()Ljava/lang/ClassLoader;"); @@ -21,7 +21,7 @@ CJNIClassLoader::CJNIClassLoader(const std::string &dexPath) m_object.setGlobal(); } -jhclass CJNIClassLoader::loadClass(std::string className) const +jhclass jni::CJNIClassLoader::loadClass(std::string className) const { return call_method(m_object, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", diff --git a/lib/jni/jni/src/ClassLoader.h b/lib/jni/jni/src/ClassLoader.h index 332d1b1d7..4dee6f201 100644 --- a/lib/jni/jni/src/ClassLoader.h +++ b/lib/jni/jni/src/ClassLoader.h @@ -10,6 +10,8 @@ #include "JNIBase.h" +namespace jni +{ class CJNIClassLoader : public CJNIBase { public: @@ -22,3 +24,4 @@ class CJNIClassLoader : public CJNIBase private: CJNIClassLoader(); }; +} diff --git a/lib/jni/jni/src/MediaDrmOnEventListener.h b/lib/jni/jni/src/MediaDrmOnEventListener.h index c279de2f7..c44bd6e9e 100644 --- a/lib/jni/jni/src/MediaDrmOnEventListener.h +++ b/lib/jni/jni/src/MediaDrmOnEventListener.h @@ -10,12 +10,9 @@ #include "JNIBase.h" - -class CJNIClassLoader; - namespace jni { - +class CJNIClassLoader; class CJNIMediaDrm; class CJNIMediaDrmOnEventListener : public CJNIBase, public CJNIInterfaceImplem diff --git a/src/Session.cpp b/src/Session.cpp index 57df1e689..23d9eb7a1 100644 --- a/src/Session.cpp +++ b/src/Session.cpp @@ -115,16 +115,7 @@ void CSession::DisposeSampleDecrypter() for (auto& cdmSession : m_cdmSessions) { cdmSession.m_cdmSessionStr = nullptr; - if (!cdmSession.m_sharedCencSsd) - { - m_decrypter->DestroySingleSampleDecrypter(cdmSession.m_cencSingleSampleDecrypter); - cdmSession.m_cencSingleSampleDecrypter = nullptr; - } - else - { - cdmSession.m_cencSingleSampleDecrypter = nullptr; - cdmSession.m_sharedCencSsd = false; - } + cdmSession.m_cencSingleSampleDecrypter = nullptr; } } } @@ -485,16 +476,15 @@ bool CSession::InitializeDRM(bool addDefaultKID /* = false */) { LOG::Log(LOGDEBUG, "Initializing stream with KID: %s", defaultKidStr.c_str()); + // If a decrypter has the default KID, re-use the same decrypter for also this session for (size_t i{1}; i < ses; ++i) { if (m_decrypter->HasLicenseKey(m_cdmSessions[i].m_cencSingleSampleDecrypter, defaultKid)) { session.m_cencSingleSampleDecrypter = m_cdmSessions[i].m_cencSingleSampleDecrypter; - session.m_sharedCencSsd = true; break; } } - } else if (defaultKid.empty()) { @@ -503,7 +493,6 @@ bool CSession::InitializeDRM(bool addDefaultKID /* = false */) if (sessionPsshset.pssh_ == m_adaptiveTree->m_currentPeriod->GetPSSHSets()[i].pssh_) { session.m_cencSingleSampleDecrypter = m_cdmSessions[i].m_cencSingleSampleDecrypter; - session.m_sharedCencSsd = true; break; } } @@ -1312,7 +1301,7 @@ void CSession::OnStreamChange(adaptive::AdaptiveStream* adStream) } } -Adaptive_CencSingleSampleDecrypter* CSession::GetSingleSampleDecrypter(std::string sessionId) +std::shared_ptr CSession::GetSingleSampleDecrypter(std::string sessionId) { for (std::vector::iterator b(m_cdmSessions.begin() + 1), e(m_cdmSessions.end()); b != e; ++b) diff --git a/src/Session.h b/src/Session.h index 666ea23ea..289ceba6a 100644 --- a/src/Session.h +++ b/src/Session.h @@ -20,6 +20,8 @@ #include #endif +#include + class Adaptive_CencSingleSampleDecrypter; namespace SESSION @@ -136,7 +138,7 @@ class ATTR_DLL_LOCAL CSession : public adaptive::AdaptiveStreamObserver * \param index The index (psshSet number) of the cdm session * \return The single sample decrypter */ - Adaptive_CencSingleSampleDecrypter* GetSingleSampleDecryptor(unsigned int index) const + std::shared_ptr GetSingleSampleDecryptor(unsigned int index) const { return m_cdmSessions[index].m_cencSingleSampleDecrypter; } @@ -150,7 +152,7 @@ class ATTR_DLL_LOCAL CSession : public adaptive::AdaptiveStreamObserver * \param sessionId The session id string to match * \return The single sample decrypter */ - Adaptive_CencSingleSampleDecrypter* GetSingleSampleDecrypter(std::string sessionId); + std::shared_ptr GetSingleSampleDecrypter(std::string sessionId); /*! \brief Get decrypter capabilities for a single sample decrypter * \param index The index (psshSet number) of the cdm session @@ -348,9 +350,8 @@ class ATTR_DLL_LOCAL CSession : public adaptive::AdaptiveStreamObserver struct CCdmSession { DRM::DecrypterCapabilites m_decrypterCaps; - Adaptive_CencSingleSampleDecrypter* m_cencSingleSampleDecrypter{nullptr}; + std::shared_ptr m_cencSingleSampleDecrypter; const char* m_cdmSessionStr{nullptr}; - bool m_sharedCencSsd{false}; }; std::vector m_cdmSessions; diff --git a/src/common/AdaptiveCencSampleDecrypter.cpp b/src/common/AdaptiveCencSampleDecrypter.cpp index 0edbfe2cc..4d0fb5dff 100644 --- a/src/common/AdaptiveCencSampleDecrypter.cpp +++ b/src/common/AdaptiveCencSampleDecrypter.cpp @@ -9,9 +9,9 @@ #include "AdaptiveCencSampleDecrypter.h" CAdaptiveCencSampleDecrypter::CAdaptiveCencSampleDecrypter( - Adaptive_CencSingleSampleDecrypter* singleSampleDecrypter, + std::shared_ptr singleSampleDecrypter, AP4_CencSampleInfoTable* sampleInfoTable) - : AP4_CencSampleDecrypter(singleSampleDecrypter, sampleInfoTable) + : AP4_CencSampleDecrypter(singleSampleDecrypter.get(), sampleInfoTable) { m_decrypter = singleSampleDecrypter; } diff --git a/src/common/AdaptiveCencSampleDecrypter.h b/src/common/AdaptiveCencSampleDecrypter.h index b0d4e85fc..2df65d706 100644 --- a/src/common/AdaptiveCencSampleDecrypter.h +++ b/src/common/AdaptiveCencSampleDecrypter.h @@ -8,12 +8,14 @@ #include "AdaptiveDecrypter.h" +#include + #include class CAdaptiveCencSampleDecrypter : public AP4_CencSampleDecrypter { public: - CAdaptiveCencSampleDecrypter(Adaptive_CencSingleSampleDecrypter* singleSampleDecrypter, + CAdaptiveCencSampleDecrypter(std::shared_ptr singleSampleDecrypter, AP4_CencSampleInfoTable* sampleInfoTable); ~CAdaptiveCencSampleDecrypter() override {}; @@ -23,5 +25,5 @@ class CAdaptiveCencSampleDecrypter : public AP4_CencSampleDecrypter const AP4_UI08* iv); protected: - Adaptive_CencSingleSampleDecrypter* m_decrypter; + std::shared_ptr m_decrypter; }; diff --git a/src/decrypters/HelperWv.h b/src/decrypters/HelperWv.h index 623404bb5..83b22825f 100644 --- a/src/decrypters/HelperWv.h +++ b/src/decrypters/HelperWv.h @@ -8,9 +8,69 @@ #pragma once +#ifdef INPUTSTREAM_TEST_BUILD +#include "test/KodiStubs.h" +#else +#include +#endif + #include +#include #include +enum class CdmMessageType +{ + UNKNOWN, + SESSION_MESSAGE, + SESSION_KEY_CHANGE, + EVENT_KEY_REQUIRED, +}; + +struct CdmMessage +{ + std::string sessionId; + CdmMessageType type{CdmMessageType::UNKNOWN}; + std::vector data; + uint32_t status{0}; +}; + +class ATTR_DLL_LOCAL IWVObserver // Observer called by IWVSubject interface +{ +public: + virtual ~IWVObserver() = default; + virtual void OnNotify(const CdmMessage& message) = 0; +}; + +class ATTR_DLL_LOCAL IWVSubject // Subject to make callbacks to IWVObserver interfaces +{ +public: + virtual ~IWVSubject() = default; + virtual void AttachObserver(IWVObserver* observer) = 0; + virtual void DetachObserver(IWVObserver* observer) = 0; + virtual void NotifyObservers(const CdmMessage& message) = 0; +}; + +template +class ATTR_DLL_LOCAL IWVCdmAdapter : public IWVSubject +{ +public: + virtual ~IWVCdmAdapter() = default; + + virtual std::shared_ptr GetCDM() = 0; + + virtual const std::string& GetLicenseUrl() = 0; + + virtual void SetCodecInstance(void* instance) {} + virtual void ResetCodecInstance() {} + + virtual std::string_view GetKeySystem() = 0; + + virtual std::string_view GetLibraryPath() const { return ""; } + //! @todo: add here this method for convenience needed investigate better to better cleanup, + //! also Load/Save certificate methods need a full code cleanup + virtual void SaveServiceCertificate() {} +}; + namespace DRM { diff --git a/src/decrypters/Helpers.cpp b/src/decrypters/Helpers.cpp index dd50ee015..f85977ad9 100644 --- a/src/decrypters/Helpers.cpp +++ b/src/decrypters/Helpers.cpp @@ -94,6 +94,34 @@ std::vector DRM::UrnsToSystemIds(const std::vector +#include #include #include class Adaptive_CencSingleSampleDecrypter; -class AP4_DataBuffer; enum class CryptoMode; namespace DRM @@ -85,7 +85,7 @@ class IDecrypter * \param cryptoMode The crypto/cypher mode to initialise with * \return The single sample decrypter if successfully created */ - virtual Adaptive_CencSingleSampleDecrypter* CreateSingleSampleDecrypter( + virtual std::shared_ptr CreateSingleSampleDecrypter( std::vector& initData, std::string_view optionalKeyParameter, const std::vector& defaultKeyId, @@ -93,12 +93,6 @@ class IDecrypter bool skipSessionMessage, CryptoMode cryptoMode) = 0; - /** - * \brief Destroy the decrypter - * \param decrypter The single sample decrypter instance to destroy - */ - virtual void DestroySingleSampleDecrypter(Adaptive_CencSingleSampleDecrypter* decrypter) = 0; - /** * \brief Determine the capabilities of the decrypter against the supplied media type and KeyID * \param decrypter The single sample decrypter to use for this check @@ -106,7 +100,7 @@ class IDecrypter * \param media The type of media being decrypted (audio/video) * \param caps The capabilities object to be populated */ - virtual void GetCapabilities(Adaptive_CencSingleSampleDecrypter* decrypter, + virtual void GetCapabilities(std::shared_ptr decrypter, const std::vector& keyId, uint32_t media, DecrypterCapabilites& caps) = 0; @@ -117,7 +111,7 @@ class IDecrypter * \param keyid The KeyID to check for a valid license * \return True if the KeyID has a license otherwise false */ - virtual bool HasLicenseKey(Adaptive_CencSingleSampleDecrypter* decrypter, + virtual bool HasLicenseKey(std::shared_ptr decrypter, const std::vector& keyId) = 0; /** @@ -131,7 +125,7 @@ class IDecrypter * \param decrypter The single sample decrypter to use for license challenge * \return The license data in Base64 format */ - virtual std::string GetChallengeB64Data(Adaptive_CencSingleSampleDecrypter* decrypter) = 0; + virtual std::string GetChallengeB64Data(std::shared_ptr decrypter) = 0; /** * \brief Open VideoCodec for decoding video in a secure pathway to Kodi @@ -139,7 +133,7 @@ class IDecrypter * \param initData The data for initialising the codec * \return True if the decoder was opened successfully otherwise false */ - virtual bool OpenVideoDecoder(Adaptive_CencSingleSampleDecrypter* decrypter, + virtual bool OpenVideoDecoder(std::shared_ptr decrypter, const VIDEOCODEC_INITDATA* initData) = 0; /** diff --git a/src/decrypters/clearkey/ClearKeyDecrypter.cpp b/src/decrypters/clearkey/ClearKeyDecrypter.cpp index cceaf61cc..b98643309 100644 --- a/src/decrypters/clearkey/ClearKeyDecrypter.cpp +++ b/src/decrypters/clearkey/ClearKeyDecrypter.cpp @@ -32,7 +32,7 @@ bool CClearKeyDecrypter::OpenDRMSystem(std::string_view licenseURL, return true; } -Adaptive_CencSingleSampleDecrypter* CClearKeyDecrypter::CreateSingleSampleDecrypter( +std::shared_ptr CClearKeyDecrypter::CreateSingleSampleDecrypter( std::vector& initData, std::string_view optionalKeyParameter, const std::vector& defaultkeyid, @@ -46,7 +46,7 @@ Adaptive_CencSingleSampleDecrypter* CClearKeyDecrypter::CreateSingleSampleDecryp return nullptr; } - CClearKeyCencSingleSampleDecrypter* decrypter = nullptr; + std::shared_ptr decrypter; auto& cfgLic = CSrvBroker::GetKodiProps().GetDrmConfig(std::string(DRM::KS_CLEARKEY)).license; // If keys / license url are provided by Kodi property, those of the manifest will be overwritten @@ -56,33 +56,35 @@ Adaptive_CencSingleSampleDecrypter* CClearKeyDecrypter::CreateSingleSampleDecryp if ((!cfgLic.keys.empty() || !initData.empty()) && cfgLic.serverUrl.empty()) // Keys provided from manifest or Kodi property { - decrypter = new CClearKeyCencSingleSampleDecrypter(initData, defaultkeyid, cfgLic.keys, this); + decrypter = std::make_shared(initData, defaultkeyid, + cfgLic.keys, this); } else // Clearkey license server URL provided { - decrypter = new CClearKeyCencSingleSampleDecrypter(licenseUrl, cfgLic.reqHeaders, defaultkeyid, this); + decrypter = std::make_shared(licenseUrl, cfgLic.reqHeaders, + defaultkeyid, this); } if (!decrypter->HasKeys()) { - delete decrypter; - decrypter = nullptr; + return nullptr; } return decrypter; } -void CClearKeyDecrypter::DestroySingleSampleDecrypter(Adaptive_CencSingleSampleDecrypter* decrypter) +bool CClearKeyDecrypter::HasLicenseKey( + std::shared_ptr decrypter, + const std::vector& keyId) { if (decrypter) { - delete static_cast(decrypter); - } -} + auto clearKeyDecrypter = + std::dynamic_pointer_cast(decrypter); -bool CClearKeyDecrypter::HasLicenseKey(Adaptive_CencSingleSampleDecrypter* decrypter, - const std::vector& keyId) -{ - if (decrypter) - return static_cast(decrypter)->HasKeyId(keyId); + if (clearKeyDecrypter) + return clearKeyDecrypter->HasKeyId(keyId); + else + LOG::LogF(LOGFATAL, "Cannot cast the decrypter shared pointer."); + } return false; } diff --git a/src/decrypters/clearkey/ClearKeyDecrypter.h b/src/decrypters/clearkey/ClearKeyDecrypter.h index 1e2f0dc35..a88f334e4 100644 --- a/src/decrypters/clearkey/ClearKeyDecrypter.h +++ b/src/decrypters/clearkey/ClearKeyDecrypter.h @@ -22,28 +22,28 @@ class CClearKeyDecrypter : public IDecrypter virtual bool OpenDRMSystem(std::string_view licenseURL, const std::vector& serverCertificate, const uint8_t config) override; - virtual Adaptive_CencSingleSampleDecrypter* CreateSingleSampleDecrypter( + virtual std::shared_ptr CreateSingleSampleDecrypter( std::vector& initData, std::string_view optionalKeyParameter, const std::vector& defaultkeyid, std::string_view licenseUrl, bool skipSessionMessage, CryptoMode cryptoMode) override; - virtual void DestroySingleSampleDecrypter(Adaptive_CencSingleSampleDecrypter* decrypter) override; - virtual void GetCapabilities(Adaptive_CencSingleSampleDecrypter* decrypter, + + virtual void GetCapabilities(std::shared_ptr decrypter, const std::vector& keyid, uint32_t media, DRM::DecrypterCapabilites& caps) override { } - virtual bool HasLicenseKey(Adaptive_CencSingleSampleDecrypter* decrypter, + virtual bool HasLicenseKey(std::shared_ptr decrypter, const std::vector& keyid) override; virtual bool IsInitialised() override { return true; } - virtual std::string GetChallengeB64Data(Adaptive_CencSingleSampleDecrypter* decrypter) override + virtual std::string GetChallengeB64Data(std::shared_ptr decrypter) override { return ""; } - virtual bool OpenVideoDecoder(Adaptive_CencSingleSampleDecrypter* decrypter, + virtual bool OpenVideoDecoder(std::shared_ptr decrypter, const VIDEOCODEC_INITDATA* initData) override { return false; diff --git a/src/decrypters/widevine/WVCdmAdapter.cpp b/src/decrypters/widevine/WVCdmAdapter.cpp index f3a1d45bd..12589eb07 100644 --- a/src/decrypters/widevine/WVCdmAdapter.cpp +++ b/src/decrypters/widevine/WVCdmAdapter.cpp @@ -22,11 +22,11 @@ using namespace UTILS; namespace { #if WIN32 - constexpr const char* LIBRARY_FILENAME = "widevinecdm.dll"; +constexpr const char* LIBRARY_FILENAME = "widevinecdm.dll"; #elif TARGET_DARWIN - constexpr const char* LIBRARY_FILENAME = "libwidevinecdm.dylib"; +constexpr const char* LIBRARY_FILENAME = "libwidevinecdm.dylib"; #else - constexpr const char* LIBRARY_FILENAME = "libwidevinecdm.so"; +constexpr const char* LIBRARY_FILENAME = "libwidevinecdm.so"; #endif } // unnamed namespace @@ -34,7 +34,7 @@ CWVCdmAdapter::CWVCdmAdapter(std::string_view licenseURL, const std::vector& serverCert, const uint8_t config, CWVDecrypter* host) - : m_licenseUrl(licenseURL), m_host(host), m_codecInstance(nullptr) + : m_licenseUrl(licenseURL), m_host(host) { if (m_host->GetLibraryPath().empty()) { @@ -62,35 +62,36 @@ CWVCdmAdapter::CWVCdmAdapter(std::string_view licenseURL, basePath = FILESYS::PathCombine(basePath, DRM::GenerateUrlDomainHash(licUrl)); basePath += FILESYS::SEPARATOR; - wv_adapter = std::shared_ptr(new media::CdmAdapter( + m_cdmAdapter = std::make_shared( "com.widevine.alpha", cdmPath, basePath, media::CdmConfig(false, (config & DRM::IDecrypter::CONFIG_PERSISTENTSTORAGE) != 0), - dynamic_cast(this))); - if (!wv_adapter->valid()) + dynamic_cast(this)); + + if (!m_cdmAdapter->valid()) { LOG::Log(LOGERROR, "Unable to load widevine shared library (%s)", cdmPath.c_str()); - wv_adapter = nullptr; + m_cdmAdapter = nullptr; return; } if (!serverCert.empty()) - wv_adapter->SetServerCertificate(0, serverCert.data(), serverCert.size()); + m_cdmAdapter->SetServerCertificate(0, serverCert.data(), serverCert.size()); // For backward compatibility: If no | is found in URL, use the most common working config if (m_licenseUrl.find('|') == std::string::npos) m_licenseUrl += "|Content-Type=application%2Foctet-stream|R{SSM}|"; - //wv_adapter->GetStatusForPolicy(); - //wv_adapter->QueryOutputProtectionStatus(); + // m_cdmAdapter->GetStatusForPolicy(); + // m_cdmAdapter->QueryOutputProtectionStatus(); } CWVCdmAdapter::~CWVCdmAdapter() { - if (wv_adapter) + if (m_cdmAdapter) { - wv_adapter->RemoveClient(); - LOG::Log(LOGERROR, "Instances: %u", wv_adapter.use_count()); - wv_adapter = nullptr; + m_cdmAdapter->RemoveClient(); + // LOG::LogF(LOGDEBUG, "CDM Adapter instances: %u", m_cdmAdapter.use_count()); + m_cdmAdapter = nullptr; } } @@ -101,21 +102,24 @@ void CWVCdmAdapter::OnCDMMessage(const char* session, size_t data_size, uint32_t status) { - LOG::Log(LOGDEBUG, "CDMMessage: %u arrived!", msg); - std::vector::iterator b(ssds.begin()), e(ssds.end()); - for (; b != e; ++b) - if (!(*b)->GetSessionId() || strncmp((*b)->GetSessionId(), session, session_size) == 0) - break; - - if (b == ssds.end()) - return; + LOG::Log(LOGDEBUG, "CDM message: type %i arrived", msg); + CdmMessageType type; if (msg == CDMADPMSG::kSessionMessage) - { - (*b)->SetSession(session, session_size, data, data_size); - } + type = CdmMessageType::SESSION_MESSAGE; else if (msg == CDMADPMSG::kSessionKeysChange) - (*b)->AddSessionKey(data, data_size, status); + type = CdmMessageType::SESSION_KEY_CHANGE; + else + return; + + CdmMessage cdmMsg; + cdmMsg.sessionId.assign(session, session + session_size); + cdmMsg.type = type; + cdmMsg.data.assign(data, data + data_size); + cdmMsg.status = status; + + // Send the message to attached CWVCencSingleSampleDecrypter instances + NotifyObservers(cdmMsg); } cdm::Buffer* CWVCdmAdapter::AllocateBuffer(size_t sz) @@ -129,5 +133,46 @@ cdm::Buffer* CWVCdmAdapter::AllocateBuffer(size_t sz) return buf; } return nullptr; - ; +} + +void CWVCdmAdapter::SetCodecInstance(void* instance) +{ + m_codecInstance = reinterpret_cast(instance); +} + +void CWVCdmAdapter::ResetCodecInstance() +{ + m_codecInstance = nullptr; +} + +std::string_view CWVCdmAdapter::GetKeySystem() +{ + return KS_WIDEVINE; +} + +std::string_view CWVCdmAdapter::GetLibraryPath() const +{ + return m_host->GetLibraryPath(); +} + +void CWVCdmAdapter::AttachObserver(IWVObserver* observer) +{ + std::lock_guard lock(m_observer_mutex); + m_observers.emplace_back(observer); +} + +void CWVCdmAdapter::DetachObserver(IWVObserver* observer) +{ + std::lock_guard lock(m_observer_mutex); + m_observers.remove(observer); +} + +void CWVCdmAdapter::NotifyObservers(const CdmMessage& message) +{ + std::lock_guard lock(m_observer_mutex); + for (IWVObserver* observer : m_observers) + { + if (observer) + observer->OnNotify(message); + } } diff --git a/src/decrypters/widevine/WVCdmAdapter.h b/src/decrypters/widevine/WVCdmAdapter.h index 58ed28130..c6ffffc9a 100644 --- a/src/decrypters/widevine/WVCdmAdapter.h +++ b/src/decrypters/widevine/WVCdmAdapter.h @@ -9,15 +9,20 @@ #pragma once #include "CdmBuffer.h" - #include "cdm/media/cdm/cdm_adapter.h" +#include "decrypters/HelperWv.h" + +#include +#include + #include #include class CWVDecrypter; class CWVCencSingleSampleDecrypter; -class ATTR_DLL_LOCAL CWVCdmAdapter : public media::CdmAdapterClient +class ATTR_DLL_LOCAL CWVCdmAdapter : public media::CdmAdapterClient, + public IWVCdmAdapter { public: CWVCdmAdapter(std::string_view licenseURL, @@ -26,44 +31,36 @@ class ATTR_DLL_LOCAL CWVCdmAdapter : public media::CdmAdapterClient CWVDecrypter* host); virtual ~CWVCdmAdapter(); + // media::CdmAdapterClient interface methods + virtual void OnCDMMessage(const char* session, uint32_t session_size, CDMADPMSG msg, const uint8_t* data, size_t data_size, uint32_t status) override; - virtual cdm::Buffer* AllocateBuffer(size_t sz) override; - void insertssd(CWVCencSingleSampleDecrypter* ssd) { ssds.push_back(ssd); }; - void removessd(CWVCencSingleSampleDecrypter* ssd) - { - std::vector::iterator res( - std::find(ssds.begin(), ssds.end(), ssd)); - if (res != ssds.end()) - ssds.erase(res); - }; + // IWVCdmAdapter interface methods + + std::shared_ptr GetCDM() override { return m_cdmAdapter; } + const std::string& GetLicenseUrl() override { return m_licenseUrl; } + void SetCodecInstance(void* instance) override; + void ResetCodecInstance() override; + std::string_view GetKeySystem() override; + std::string_view GetLibraryPath() const override; - media::CdmAdapter* GetCdmAdapter() { return wv_adapter.get(); }; - const std::string& GetLicenseURL() { return m_licenseUrl; }; + // IWVSubject interface methods - cdm::Status DecryptAndDecodeFrame(cdm::InputBuffer_2& cdm_in, - media::CdmVideoFrame* frame, - kodi::addon::CInstanceVideoCodec* codecInstance) - { - // DecryptAndDecodeFrame calls CdmAdapter::Allocate which calls Host->GetBuffer - // that cast hostInstance to CInstanceVideoCodec to get the frame buffer - // so we have temporary set the host instance - m_codecInstance = codecInstance; - cdm::Status ret = wv_adapter->DecryptAndDecodeFrame(cdm_in, frame); - m_codecInstance = nullptr; - return ret; - } + void AttachObserver(IWVObserver* observer) override; + void DetachObserver(IWVObserver* observer) override; + void NotifyObservers(const CdmMessage& message) override; private: - std::shared_ptr wv_adapter; + std::shared_ptr m_cdmAdapter; std::string m_licenseUrl; - kodi::addon::CInstanceVideoCodec* m_codecInstance; + kodi::addon::CInstanceVideoCodec* m_codecInstance{nullptr}; CWVDecrypter* m_host; - std::vector ssds; + std::list m_observers; + std::mutex m_observer_mutex; }; diff --git a/src/decrypters/widevine/WVCencSingleSampleDecrypter.cpp b/src/decrypters/widevine/WVCencSingleSampleDecrypter.cpp index 481e8349c..a93741b54 100644 --- a/src/decrypters/widevine/WVCencSingleSampleDecrypter.cpp +++ b/src/decrypters/widevine/WVCencSingleSampleDecrypter.cpp @@ -14,7 +14,6 @@ #include "CdmFixedBuffer.h" #include "CdmTypeConversion.h" #include "WVCdmAdapter.h" -#include "WVDecrypter.h" #include "cdm/media/cdm/cdm_adapter.h" #include "jsmn.h" #include "decrypters/Helpers.h" @@ -31,25 +30,24 @@ using namespace UTILS; -void CWVCencSingleSampleDecrypter::SetSession(const char* session, - uint32_t sessionSize, +void CWVCencSingleSampleDecrypter::SetSession(const std::string sessionId, const uint8_t* data, - size_t dataSize) + const size_t dataSize) { std::lock_guard lock(m_renewalLock); - m_strSession = std::string(session, sessionSize); + m_strSession = sessionId; m_challenge.SetData(data, dataSize); LOG::LogF(LOGDEBUG, "Opened widevine session ID: %s", m_strSession.c_str()); } -CWVCencSingleSampleDecrypter::CWVCencSingleSampleDecrypter(CWVCdmAdapter& drm, - std::vector& pssh, - const std::vector& defaultKeyId, - bool skipSessionMessage, - CryptoMode cryptoMode, - CWVDecrypter* host) - : m_wvCdmAdapter(drm), +CWVCencSingleSampleDecrypter::CWVCencSingleSampleDecrypter( + IWVCdmAdapter* cdmAdapter, + std::vector& pssh, + const std::vector& defaultKeyId, + bool skipSessionMessage, + CryptoMode cryptoMode) + : m_cdmAdapter(cdmAdapter), m_pssh(pssh), m_hdcpVersion(99), m_hdcpLimit(0), @@ -57,8 +55,7 @@ CWVCencSingleSampleDecrypter::CWVCencSingleSampleDecrypter(CWVCdmAdapter& drm, m_promiseId(1), m_isDrained(true), m_defaultKeyId(defaultKeyId), - m_EncryptionMode(cryptoMode), - m_host(host) + m_EncryptionMode(cryptoMode) { SetParentIsOwner(false); @@ -69,21 +66,22 @@ CWVCencSingleSampleDecrypter::CWVCencSingleSampleDecrypter(CWVCdmAdapter& drm, return; } - m_wvCdmAdapter.insertssd(this); + m_cdmAdapter->AttachObserver(this); if (CSrvBroker::GetSettings().IsDebugLicense()) { - std::string debugFilePath = - FILESYS::PathCombine(m_host->GetLibraryPath(), "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED.init"); + std::string debugFilePath = FILESYS::PathCombine(m_cdmAdapter->GetLibraryPath(), + "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED.init"); std::string data{reinterpret_cast(m_pssh.data()), m_pssh.size()}; UTILS::FILESYS::SaveFile(debugFilePath, data, true); } - drm.GetCdmAdapter()->CreateSessionAndGenerateRequest(m_promiseId++, cdm::SessionType::kTemporary, - cdm::InitDataType::kCenc, m_pssh.data(), - static_cast(m_pssh.size())); + m_cdmAdapter->GetCDM()->CreateSessionAndGenerateRequest( + m_promiseId++, cdm::SessionType::kTemporary, cdm::InitDataType::kCenc, m_pssh.data(), + static_cast(m_pssh.size())); + //! @todo: loop with thread sleep should be removed, use callbacks int retrycount = 0; while (m_strSession.empty() && ++retrycount < 100) std::this_thread::sleep_for(std::chrono::milliseconds(10)); @@ -97,13 +95,19 @@ CWVCencSingleSampleDecrypter::CWVCencSingleSampleDecrypter(CWVCdmAdapter& drm, if (skipSessionMessage) return; + //! @todo: this loop is not so clear while (m_challenge.GetDataSize() > 0 && SendSessionMessage()) ; } CWVCencSingleSampleDecrypter::~CWVCencSingleSampleDecrypter() { - m_wvCdmAdapter.removessd(this); + // This decrypter can be used/shared with more streams "sessions" + // since it is used wrapped in a shared_ptr the destructor will be called only + // when the last stream "session" will be deleted, and so it can close the CDM session. + CloseSessionId(); + + m_cdmAdapter->DetachObserver(this); } void CWVCencSingleSampleDecrypter::GetCapabilities(const std::vector& keyId, @@ -209,7 +213,7 @@ void CWVCencSingleSampleDecrypter::CloseSessionId() if (!m_strSession.empty()) { LOG::LogF(LOGDEBUG, "Closing widevine session ID: %s", m_strSession.c_str()); - m_wvCdmAdapter.GetCdmAdapter()->CloseSession(++m_promiseId, m_strSession.data(), + m_cdmAdapter->GetCDM()->CloseSession(++m_promiseId, m_strSession.data(), m_strSession.size()); LOG::LogF(LOGDEBUG, "Widevine session ID %s closed", m_strSession.c_str()); @@ -234,7 +238,7 @@ void CWVCencSingleSampleDecrypter::CheckLicenseRenewal() bool CWVCencSingleSampleDecrypter::SendSessionMessage() { - std::vector blocks{StringUtils::Split(m_wvCdmAdapter.GetLicenseURL(), '|')}; + std::vector blocks{STRING::SplitToVec(m_cdmAdapter->GetLicenseUrl(), '|')}; if (blocks.size() != 4) { @@ -246,7 +250,7 @@ bool CWVCencSingleSampleDecrypter::SendSessionMessage() if (CSrvBroker::GetSettings().IsDebugLicense()) { std::string debugFilePath = FILESYS::PathCombine( - m_host->GetLibraryPath(), "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED.challenge"); + m_cdmAdapter->GetLibraryPath(), "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED.challenge"); std::string data{reinterpret_cast(m_challenge.GetData()), m_challenge.GetDataSize()}; UTILS::FILESYS::SaveFile(debugFilePath, data, true); @@ -288,17 +292,17 @@ bool CWVCencSingleSampleDecrypter::SendSessionMessage() bool serverCertRequest; //Process headers - std::vector headers{StringUtils::Split(blocks[1], '&')}; + std::vector headers{STRING::SplitToVec(blocks[1], '&')}; for (std::string& headerStr : headers) { - std::vector header{StringUtils::Split(headerStr, '=')}; + std::vector header{STRING::SplitToVec(headerStr, '=')}; if (!header.empty()) { - StringUtils::Trim(header[0]); + STRING::Trim(header[0]); std::string value; if (header.size() > 1) { - StringUtils::Trim(header[1]); + STRING::Trim(header[1]); value = STRING::URLDecode(header[1]); } file.AddHeader(header[0].c_str(), value.c_str()); @@ -469,7 +473,7 @@ bool CWVCencSingleSampleDecrypter::SendSessionMessage() if (CSrvBroker::GetSettings().IsDebugLicense()) { std::string debugFilePath = FILESYS::PathCombine( - m_host->GetLibraryPath(), "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED.response"); + m_cdmAdapter->GetLibraryPath(), "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED.response"); FILESYS::SaveFile(debugFilePath, response, true); } @@ -494,7 +498,7 @@ bool CWVCencSingleSampleDecrypter::SendSessionMessage() jsmn_init(&jsn); int i(0), numTokens = jsmn_parse(&jsn, response.c_str(), response.size(), tokens, 256); - std::vector jsonVals{StringUtils::Split(blocks[3].substr(dataPos), ';')}; + std::vector jsonVals{STRING::SplitToVec(blocks[3].substr(dataPos), ';')}; // Find HDCP limit if (jsonVals.size() > 1) @@ -535,7 +539,7 @@ bool CWVCencSingleSampleDecrypter::SendSessionMessage() respData = BASE64::DecodeToStr(respData); } - m_wvCdmAdapter.GetCdmAdapter()->UpdateSession( + m_cdmAdapter->GetCDM()->UpdateSession( ++m_promiseId, m_strSession.data(), m_strSession.size(), reinterpret_cast(respData.c_str()), respData.size()); } @@ -553,7 +557,7 @@ bool CWVCencSingleSampleDecrypter::SendSessionMessage() { payloadPos += 4; if (blocks[3][1] == 'B') - m_wvCdmAdapter.GetCdmAdapter()->UpdateSession( + m_cdmAdapter->GetCDM()->UpdateSession( ++m_promiseId, m_strSession.data(), m_strSession.size(), reinterpret_cast(response.c_str() + payloadPos), response.size() - payloadPos); @@ -573,7 +577,7 @@ bool CWVCencSingleSampleDecrypter::SendSessionMessage() { std::string decRespData{BASE64::DecodeToStr(response)}; - m_wvCdmAdapter.GetCdmAdapter()->UpdateSession( + m_cdmAdapter->GetCDM()->UpdateSession( ++m_promiseId, m_strSession.data(), m_strSession.size(), reinterpret_cast(decRespData.c_str()), decRespData.size()); } @@ -585,7 +589,7 @@ bool CWVCencSingleSampleDecrypter::SendSessionMessage() } else // its binary - simply push the returned data as update { - m_wvCdmAdapter.GetCdmAdapter()->UpdateSession( + m_cdmAdapter->GetCDM()->UpdateSession( ++m_promiseId, m_strSession.data(), m_strSession.size(), reinterpret_cast(response.data()), response.size()); } @@ -601,6 +605,21 @@ bool CWVCencSingleSampleDecrypter::SendSessionMessage() return true; } +void CWVCencSingleSampleDecrypter::OnNotify(const CdmMessage& message) +{ + if (!m_strSession.empty() && m_strSession != message.sessionId) + return; + + if (message.type == CdmMessageType::SESSION_MESSAGE) + { + SetSession(message.sessionId, message.data.data(), message.data.size()); + } + else if (message.type == CdmMessageType::SESSION_KEY_CHANGE) + { + AddSessionKey(message.data.data(), message.data.size(), message.status); + } +} + void CWVCencSingleSampleDecrypter::AddSessionKey(const uint8_t* data, size_t dataSize, uint32_t status) @@ -745,7 +764,7 @@ AP4_Result CWVCencSingleSampleDecrypter::DecryptSampleData(AP4_UI32 poolId, const AP4_UI16* bytesOfCleartextData, const AP4_UI32* bytesOfEncryptedData) { - if (!m_wvCdmAdapter.GetCdmAdapter()) + if (!m_cdmAdapter->GetCDM()) { dataOut.SetData(dataIn.GetData(), dataIn.GetDataSize()); return AP4_SUCCESS; @@ -975,7 +994,7 @@ AP4_Result CWVCencSingleSampleDecrypter::DecryptSampleData(AP4_UI32 poolId, cdmOut.SetDecryptedBuffer(&buf); CheckLicenseRenewal(); - ret = m_wvCdmAdapter.GetCdmAdapter()->Decrypt(cdmIn, &cdmOut); + ret = m_cdmAdapter->GetCDM()->Decrypt(cdmIn, &cdmOut); if (ret == cdm::Status::kSuccess) { @@ -1015,12 +1034,12 @@ bool CWVCencSingleSampleDecrypter::OpenVideoDecoder(const VIDEOCODEC_INITDATA* i if (currVidConfig.codec == vconfig.codec && currVidConfig.profile == vconfig.profile) return true; - m_wvCdmAdapter.GetCdmAdapter()->DeinitializeDecoder(cdm::StreamType::kStreamTypeVideo); + m_cdmAdapter->GetCDM()->DeinitializeDecoder(cdm::StreamType::kStreamTypeVideo); } m_currentVideoDecConfig = vconfig; - cdm::Status ret = m_wvCdmAdapter.GetCdmAdapter()->InitializeVideoDecoder(vconfig); + cdm::Status ret = m_cdmAdapter->GetCDM()->InitializeVideoDecoder(vconfig); m_videoFrames.clear(); m_isDrained = true; @@ -1053,8 +1072,13 @@ VIDEOCODEC_RETVAL CWVCencSingleSampleDecrypter::DecryptAndDecodeVideo( CheckLicenseRenewal(); media::CdmVideoFrame videoFrame; - cdm::Status status = - m_wvCdmAdapter.DecryptAndDecodeFrame(inputBuffer, &videoFrame, codecInstance); + + // DecryptAndDecodeFrame calls CdmAdapter::Allocate which calls Host->GetBuffer + // that cast hostInstance to CInstanceVideoCodec to get the frame buffer + // so we have temporary set the host instance + m_cdmAdapter->SetCodecInstance(codecInstance); + cdm::Status status = m_cdmAdapter->GetCDM()->DecryptAndDecodeFrame(inputBuffer, &videoFrame); + m_cdmAdapter->ResetCodecInstance(); if (status == cdm::Status::kSuccess) { @@ -1126,7 +1150,7 @@ VIDEOCODEC_RETVAL CWVCencSingleSampleDecrypter::VideoFrameDataToPicture( void CWVCencSingleSampleDecrypter::ResetVideo() { - m_wvCdmAdapter.GetCdmAdapter()->ResetDecoder(cdm::kStreamTypeVideo); + m_cdmAdapter->GetCDM()->ResetDecoder(cdm::kStreamTypeVideo); m_isDrained = true; } diff --git a/src/decrypters/widevine/WVCencSingleSampleDecrypter.h b/src/decrypters/widevine/WVCencSingleSampleDecrypter.h index dd82a2315..6adeecd63 100644 --- a/src/decrypters/widevine/WVCencSingleSampleDecrypter.h +++ b/src/decrypters/widevine/WVCencSingleSampleDecrypter.h @@ -8,34 +8,32 @@ #pragma once -#include "../IDecrypter.h" #include "cdm/media/cdm/api/content_decryption_module.h" #include "common/AdaptiveCencSampleDecrypter.h" +#include "decrypters/HelperWv.h" +#include "decrypters/IDecrypter.h" #include #include #include -class CWVDecrypter; -class CWVCdmAdapter; - namespace media { +class CdmAdapter; class CdmVideoFrame; } using namespace DRM; -class ATTR_DLL_LOCAL CWVCencSingleSampleDecrypter : public Adaptive_CencSingleSampleDecrypter +class ATTR_DLL_LOCAL CWVCencSingleSampleDecrypter : public Adaptive_CencSingleSampleDecrypter, + public IWVObserver { public: - // methods - CWVCencSingleSampleDecrypter(CWVCdmAdapter& drm, + CWVCencSingleSampleDecrypter(IWVCdmAdapter* cdmAdapter, std::vector& pssh, const std::vector& defaultKeyId, bool skipSessionMessage, - CryptoMode cryptoMode, - CWVDecrypter* host); + CryptoMode cryptoMode); virtual ~CWVCencSingleSampleDecrypter(); void GetCapabilities(const std::vector& keyId, @@ -45,7 +43,7 @@ class ATTR_DLL_LOCAL CWVCencSingleSampleDecrypter : public Adaptive_CencSingleSa void CloseSessionId(); AP4_DataBuffer GetChallengeData(); - void SetSession(const char* session, uint32_t sessionSize, const uint8_t* data, size_t dataSize); + void SetSession(const std::string sessionId, const uint8_t* data, const size_t dataSize); void AddSessionKey(const uint8_t* data, size_t dataSize, uint32_t status); bool HasKeyId(const std::vector& keyid); @@ -85,11 +83,14 @@ class ATTR_DLL_LOCAL CWVCencSingleSampleDecrypter : public Adaptive_CencSingleSa void SetDefaultKeyId(const std::vector& keyId) override; void AddKeyId(const std::vector& keyId) override; + // IWVObserver interface + void OnNotify(const CdmMessage& message) override; + private: void CheckLicenseRenewal(); bool SendSessionMessage(); - CWVCdmAdapter& m_wvCdmAdapter; + IWVCdmAdapter* m_cdmAdapter; std::string m_strSession; std::vector m_pssh; AP4_DataBuffer m_challenge; @@ -146,5 +147,4 @@ class ATTR_DLL_LOCAL CWVCencSingleSampleDecrypter : public Adaptive_CencSingleSa CryptoMode m_EncryptionMode; std::optional m_currentVideoDecConfig; - CWVDecrypter* m_host; }; diff --git a/src/decrypters/widevine/WVDecrypter.cpp b/src/decrypters/widevine/WVDecrypter.cpp index 4044e9cfd..3f9c5deeb 100644 --- a/src/decrypters/widevine/WVDecrypter.cpp +++ b/src/decrypters/widevine/WVDecrypter.cpp @@ -16,7 +16,7 @@ #include "utils/StringUtils.h" #include "utils/log.h" -#include +#include //! @todo: cleanup! remove me to use FileUtils.h #if defined(__linux__) && (defined(__aarch64__) || defined(__arm64__)) #include @@ -26,11 +26,8 @@ using namespace DRM; using namespace UTILS; using namespace kodi::tools; - CWVDecrypter::~CWVDecrypter() { - delete m_WVCdmAdapter; - m_WVCdmAdapter = nullptr; #if defined(__linux__) && (defined(__aarch64__) || defined(__arm64__)) if (m_hdlLibLoader) dlclose(m_hdlLibLoader); @@ -95,12 +92,12 @@ bool CWVDecrypter::OpenDRMSystem(std::string_view licenseURL, LOG::LogF(LOGERROR, "License Key property cannot be empty"); return false; } - m_WVCdmAdapter = new CWVCdmAdapter(licenseURL, serverCertificate, config, this); + m_WVCdmAdapter = std::make_shared(licenseURL, serverCertificate, config, this); - return m_WVCdmAdapter->GetCdmAdapter() != nullptr; + return m_WVCdmAdapter->GetCDM() != nullptr; } -Adaptive_CencSingleSampleDecrypter* CWVDecrypter::CreateSingleSampleDecrypter( +std::shared_ptr CWVDecrypter::CreateSingleSampleDecrypter( std::vector& initData, std::string_view optionalKeyParameter, const std::vector& defaultKeyId, @@ -108,30 +105,19 @@ Adaptive_CencSingleSampleDecrypter* CWVDecrypter::CreateSingleSampleDecrypter( bool skipSessionMessage, CryptoMode cryptoMode) { - CWVCencSingleSampleDecrypter* decrypter = new CWVCencSingleSampleDecrypter( - *m_WVCdmAdapter, initData, defaultKeyId, skipSessionMessage, cryptoMode, this); + auto decrypter = std::make_shared( + m_WVCdmAdapter.get(), initData, defaultKeyId, skipSessionMessage, cryptoMode); if (!decrypter->GetSessionId()) { - delete decrypter; - decrypter = nullptr; + return nullptr; } return decrypter; } -void CWVDecrypter::DestroySingleSampleDecrypter(Adaptive_CencSingleSampleDecrypter* decrypter) -{ - if (decrypter) - { - // close session before dispose - static_cast(decrypter)->CloseSessionId(); - delete static_cast(decrypter); - } -} - -void CWVDecrypter::GetCapabilities(Adaptive_CencSingleSampleDecrypter* decrypter, +void CWVDecrypter::GetCapabilities(std::shared_ptr decrypter, const std::vector& keyId, uint32_t media, - DecrypterCapabilites& caps) + DRM::DecrypterCapabilites& caps) { if (!decrypter) { @@ -139,35 +125,62 @@ void CWVDecrypter::GetCapabilities(Adaptive_CencSingleSampleDecrypter* decrypter return; } - static_cast(decrypter)->GetCapabilities(keyId, media, caps); + auto wvDecrypter = std::dynamic_pointer_cast(decrypter); + if (wvDecrypter) + { + wvDecrypter->GetCapabilities(keyId, media, caps); + } + else + LOG::LogF(LOGFATAL, "Cannot cast the decrypter shared pointer."); } -bool CWVDecrypter::HasLicenseKey(Adaptive_CencSingleSampleDecrypter* decrypter, +bool CWVDecrypter::HasLicenseKey(std::shared_ptr decrypter, const std::vector& keyId) { - if (decrypter) - return static_cast(decrypter)->HasKeyId(keyId); + auto wvDecrypter = std::dynamic_pointer_cast(decrypter); + if (wvDecrypter) + { + return wvDecrypter->HasKeyId(keyId); + } + else + LOG::LogF(LOGFATAL, "Cannot cast the decrypter shared pointer."); + return false; } -std::string CWVDecrypter::GetChallengeB64Data(Adaptive_CencSingleSampleDecrypter* decrypter) +std::string CWVDecrypter::GetChallengeB64Data( + std::shared_ptr decrypter) { - if (!decrypter) - return ""; + auto wvDecrypter = std::dynamic_pointer_cast(decrypter); + if (wvDecrypter) + { + AP4_DataBuffer challengeData = wvDecrypter->GetChallengeData(); + return BASE64::Encode(challengeData.GetData(), challengeData.GetDataSize()); + } + else + LOG::LogF(LOGFATAL, "Cannot cast the decrypter shared pointer."); - AP4_DataBuffer challengeData = - static_cast(decrypter)->GetChallengeData(); - return BASE64::Encode(challengeData.GetData(), challengeData.GetDataSize()); + return ""; } -bool CWVDecrypter::OpenVideoDecoder(Adaptive_CencSingleSampleDecrypter* decrypter, +bool CWVDecrypter::OpenVideoDecoder(std::shared_ptr decrypter, const VIDEOCODEC_INITDATA* initData) { - if (!decrypter || !initData) + if (!initData) + { + LOG::LogF(LOGERROR, "Cannot open video decoder, missing init data"); return false; + } - m_decodingDecrypter = static_cast(decrypter); - return m_decodingDecrypter->OpenVideoDecoder(initData); + m_decodingDecrypter = std::dynamic_pointer_cast(decrypter); + if (m_decodingDecrypter) + { + return m_decodingDecrypter->OpenVideoDecoder(initData); + } + else + LOG::LogF(LOGFATAL, "Cannot cast the decrypter shared pointer."); + + return false; } VIDEOCODEC_RETVAL CWVDecrypter::DecryptAndDecodeVideo( @@ -191,7 +204,10 @@ VIDEOCODEC_RETVAL CWVDecrypter::VideoFrameDataToPicture( void CWVDecrypter::ResetVideo() { if (m_decodingDecrypter) + { m_decodingDecrypter->ResetVideo(); + m_decodingDecrypter = nullptr; + } } void CWVDecrypter::SetLibraryPath(std::string_view libraryPath) diff --git a/src/decrypters/widevine/WVDecrypter.h b/src/decrypters/widevine/WVDecrypter.h index 357cc7116..6281c159d 100644 --- a/src/decrypters/widevine/WVDecrypter.h +++ b/src/decrypters/widevine/WVDecrypter.h @@ -8,21 +8,13 @@ #include "../IDecrypter.h" -#include - class CWVCdmAdapter; class CWVCencSingleSampleDecrypter; -using namespace DRM; -using namespace kodi::tools; - - -/*********************************************************************************************/ - -class ATTR_DLL_LOCAL CWVDecrypter : public IDecrypter +class ATTR_DLL_LOCAL CWVDecrypter : public DRM::IDecrypter { public: - CWVDecrypter() : m_WVCdmAdapter(nullptr), m_decodingDecrypter(nullptr){}; + CWVDecrypter() = default; virtual ~CWVDecrypter() override; virtual bool Initialize() override; @@ -31,23 +23,23 @@ class ATTR_DLL_LOCAL CWVDecrypter : public IDecrypter virtual bool OpenDRMSystem(std::string_view licenseURL, const std::vector& serverCertificate, const uint8_t config) override; - virtual Adaptive_CencSingleSampleDecrypter* CreateSingleSampleDecrypter( + virtual std::shared_ptr CreateSingleSampleDecrypter( std::vector& initData, std::string_view optionalKeyParameter, const std::vector& defaultKeyId, std::string_view licenseUrl, bool skipSessionMessage, CryptoMode cryptoMode) override; - virtual void DestroySingleSampleDecrypter(Adaptive_CencSingleSampleDecrypter* decrypter) override; - virtual void GetCapabilities(Adaptive_CencSingleSampleDecrypter* decrypter, + + virtual void GetCapabilities(std::shared_ptr decrypter, const std::vector& keyId, uint32_t media, - DecrypterCapabilites& caps) override; - virtual bool HasLicenseKey(Adaptive_CencSingleSampleDecrypter* decrypter, + DRM::DecrypterCapabilites& caps) override; + virtual bool HasLicenseKey(std::shared_ptr decrypter, const std::vector& keyId) override; virtual bool IsInitialised() override { return m_WVCdmAdapter != nullptr; } - virtual std::string GetChallengeB64Data(Adaptive_CencSingleSampleDecrypter* decrypter) override; - virtual bool OpenVideoDecoder(Adaptive_CencSingleSampleDecrypter* decrypter, + virtual std::string GetChallengeB64Data(std::shared_ptr decrypter) override; + virtual bool OpenVideoDecoder(std::shared_ptr decrypter, const VIDEOCODEC_INITDATA* initData) override; virtual VIDEOCODEC_RETVAL DecryptAndDecodeVideo(kodi::addon::CInstanceVideoCodec* codecInstance, const DEMUX_PACKET* sample) override; @@ -60,8 +52,10 @@ class ATTR_DLL_LOCAL CWVDecrypter : public IDecrypter virtual std::string_view GetLibraryPath() const override { return m_libraryPath; } private: - CWVCdmAdapter* m_WVCdmAdapter; - CWVCencSingleSampleDecrypter* m_decodingDecrypter; + std::shared_ptr m_WVCdmAdapter; + std::shared_ptr m_decodingDecrypter; std::string m_libraryPath; +#if defined(__linux__) && (defined(__aarch64__) || defined(__arm64__)) void* m_hdlLibLoader{nullptr}; // Aarch64 loader library handle +#endif }; diff --git a/src/decrypters/widevineandroid/WVCdmAdapter.cpp b/src/decrypters/widevineandroid/WVCdmAdapter.cpp index b6dafe0fb..21c57a7ac 100644 --- a/src/decrypters/widevineandroid/WVCdmAdapter.cpp +++ b/src/decrypters/widevineandroid/WVCdmAdapter.cpp @@ -9,22 +9,39 @@ #include "WVCdmAdapter.h" #include "WVDecrypter.h" +#include "decrypters/HelperWv.h" #include "decrypters/Helpers.h" #include "utils/FileUtils.h" #include "utils/log.h" #include -#include using namespace DRM; -using namespace jni; +using namespace UTILS; -CWVCdmAdapterA::CWVCdmAdapterA(WV_KEYSYSTEM ks, +CMediaDrmOnEventListener::CMediaDrmOnEventListener( + CMediaDrmOnEventCallback* decrypterEventCallback, + std::shared_ptr classLoader) + : jni::CJNIMediaDrmOnEventListener(classLoader.get()) +{ + m_decrypterEventCallback = decrypterEventCallback; +} + +void CMediaDrmOnEventListener::onEvent(const jni::CJNIMediaDrm& mediaDrm, + const std::vector& sessionId, + int event, + int extra, + const std::vector& data) +{ + m_decrypterEventCallback->OnMediaDrmEvent(mediaDrm, sessionId, event, extra, data); +} + +CWVCdmAdapterA::CWVCdmAdapterA(std::string_view keySystem, std::string_view licenseURL, const std::vector& serverCert, - CJNIMediaDrmOnEventListener* listener, + std::shared_ptr jniClassLoader, CWVDecrypterA* host) - : m_keySystem(ks), m_mediaDrm(0), m_licenseUrl(licenseURL), m_host(host) + : m_keySystem(keySystem), m_licenseUrl(licenseURL), m_host(host) { if (licenseURL.empty()) { @@ -32,16 +49,6 @@ CWVCdmAdapterA::CWVCdmAdapterA(WV_KEYSYSTEM ks, return; } - std::string drmName; - if (ks == WIDEVINE) - drmName = "widevine"; - else if (ks == PLAYREADY) - drmName = "playready"; - else if (ks == WISEPLAY) - drmName = "wiseplay"; - else - drmName = "undefined"; - // The license url come from license_key kodi property // we have to kept only the url without the parameters specified after pipe "|" char std::string licUrl = m_licenseUrl; @@ -51,52 +58,58 @@ CWVCdmAdapterA::CWVCdmAdapterA(WV_KEYSYSTEM ks, // Build up a CDM path to store decrypter specific stuff, each domain gets it own path // the domain name is hashed to generate a short folder name + std::string drmName = DRM::KeySystemToDrmName(m_keySystem); std::string basePath = FILESYS::PathCombine(FILESYS::GetAddonUserPath(), drmName); basePath = FILESYS::PathCombine(basePath, DRM::GenerateUrlDomainHash(licUrl)); basePath += FILESYS::SEPARATOR; m_strBasePath = basePath; int64_t mostSigBits(0), leastSigBits(0); - const uint8_t* keySystem = GetKeySystem(); + const uint8_t* systemUuid = DRM::KeySystemToUUID(m_keySystem); + if (!systemUuid) + { + LOG::LogF(LOGERROR, "Unable to get the system UUID"); + return; + } for (unsigned int i(0); i < 8; ++i) - mostSigBits = (mostSigBits << 8) | keySystem[i]; + mostSigBits = (mostSigBits << 8) | systemUuid[i]; for (unsigned int i(8); i < 16; ++i) - leastSigBits = (leastSigBits << 8) | keySystem[i]; + leastSigBits = (leastSigBits << 8) | systemUuid[i]; - CJNIUUID uuid(mostSigBits, leastSigBits); - m_mediaDrm = new CJNIMediaDrm(uuid); - if (xbmc_jnienv()->ExceptionCheck() || !*m_mediaDrm) + jni::CJNIUUID uuid(mostSigBits, leastSigBits); + m_cdmAdapter = std::make_shared(uuid); + if (xbmc_jnienv()->ExceptionCheck() || !*m_cdmAdapter.get()) { LOG::LogF(LOGERROR, "Unable to initialize MediaDrm"); xbmc_jnienv()->ExceptionClear(); - delete m_mediaDrm, m_mediaDrm = nullptr; return; } - m_mediaDrm->setOnEventListener(*listener); + // Create media drm EventListener (unique_ptr explanation on class comment) + m_mediaDrmEventListener = std::make_unique(this, jniClassLoader); + m_cdmAdapter->setOnEventListener(*m_mediaDrmEventListener.get()); if (xbmc_jnienv()->ExceptionCheck()) { LOG::LogF(LOGERROR, "Exception during installation of EventListener"); xbmc_jnienv()->ExceptionClear(); - m_mediaDrm->release(); - delete m_mediaDrm, m_mediaDrm = nullptr; + m_cdmAdapter->release(); return; } - std::vector strDeviceId = m_mediaDrm->getPropertyByteArray("deviceUniqueId"); + std::vector strDeviceId = m_cdmAdapter->getPropertyByteArray("deviceUniqueId"); xbmc_jnienv()->ExceptionClear(); - std::string strSecurityLevel = m_mediaDrm->getPropertyString("securityLevel"); + std::string strSecurityLevel = m_cdmAdapter->getPropertyString("securityLevel"); xbmc_jnienv()->ExceptionClear(); - std::string strSystemId = m_mediaDrm->getPropertyString("systemId"); + std::string strSystemId = m_cdmAdapter->getPropertyString("systemId"); xbmc_jnienv()->ExceptionClear(); - if (m_keySystem == WIDEVINE) + if (m_keySystem == DRM::KS_WIDEVINE) { - //m_mediaDrm->setPropertyString("sessionSharing", "enable"); + //m_cdmAdapter->setPropertyString("sessionSharing", "enable"); if (!serverCert.empty()) { - m_mediaDrm->setPropertyByteArray("serviceCertificate", serverCert); + m_cdmAdapter->setPropertyByteArray("serviceCertificate", serverCert); } else LoadServiceCertificate(); @@ -105,8 +118,7 @@ CWVCdmAdapterA::CWVCdmAdapterA(WV_KEYSYSTEM ks, { LOG::LogF(LOGERROR, "Exception setting Service Certificate"); xbmc_jnienv()->ExceptionClear(); - m_mediaDrm->release(); - delete m_mediaDrm, m_mediaDrm = nullptr; + m_cdmAdapter->release(); return; } } @@ -117,9 +129,9 @@ CWVCdmAdapterA::CWVCdmAdapterA(WV_KEYSYSTEM ks, if (m_licenseUrl.find('|') == std::string::npos) { - if (m_keySystem == WIDEVINE) + if (m_keySystem == DRM::KS_WIDEVINE) m_licenseUrl += "|Content-Type=application%2Foctet-stream|R{SSM}|"; - else if (m_keySystem == PLAYREADY) + else if (m_keySystem == DRM::KS_PLAYREADY) m_licenseUrl += "|Content-Type=text%2Fxml&SOAPAction=http%3A%2F%2Fschemas.microsoft.com%" "2FDRM%2F2007%2F03%2Fprotocols%2FAcquireLicense|R{SSM}|"; else @@ -129,16 +141,14 @@ CWVCdmAdapterA::CWVCdmAdapterA(WV_KEYSYSTEM ks, CWVCdmAdapterA::~CWVCdmAdapterA() { - if (m_mediaDrm) + if (m_cdmAdapter) { - m_mediaDrm->release(); + m_cdmAdapter->release(); if (xbmc_jnienv()->ExceptionCheck()) { LOG::LogF(LOGERROR, "Exception releasing media drm"); xbmc_jnienv()->ExceptionClear(); } - delete m_mediaDrm; - m_mediaDrm = nullptr; } } @@ -166,7 +176,7 @@ void CWVCdmAdapterA::LoadServiceCertificate() std::chrono::time_point_cast(now).time_since_epoch().count(); if (certTime < nowTime && nowTime - certTime < 86400) - m_mediaDrm->setPropertyByteArray("serviceCertificate", + m_cdmAdapter->setPropertyByteArray("serviceCertificate", std::vector(data + 8, data + sz)); else free(data), data = nullptr; @@ -174,7 +184,7 @@ void CWVCdmAdapterA::LoadServiceCertificate() if (!data) { LOG::Log(LOGDEBUG, "Requesting new Service Certificate"); - m_mediaDrm->setPropertyString("privacyMode", "enable"); + m_cdmAdapter->setPropertyString("privacyMode", "enable"); } else { @@ -183,9 +193,33 @@ void CWVCdmAdapterA::LoadServiceCertificate() } } +void CWVCdmAdapterA::OnMediaDrmEvent(const jni::CJNIMediaDrm& mediaDrm, + const std::vector& sessionId, + int event, + int extra, + const std::vector& data) +{ + LOG::Log(LOGDEBUG, "MediaDrm event: type %i arrived", event); + + CdmMessageType type; + if (event == jni::CJNIMediaDrm::EVENT_KEY_REQUIRED) + type = CdmMessageType::EVENT_KEY_REQUIRED; + else + return; + + CdmMessage cdmMsg; + cdmMsg.sessionId.assign(sessionId.data(), sessionId.data() + sessionId.size()); + cdmMsg.type = type; + cdmMsg.data.assign(data.data(), data.data() + data.size()); + cdmMsg.status = extra; + + // Send the message to attached CWVCencSingleSampleDecrypterA instances + NotifyObservers(cdmMsg); +} + void CWVCdmAdapterA::SaveServiceCertificate() { - const std::vector sc = m_mediaDrm->getPropertyByteArray("serviceCertificate"); + const std::vector sc = m_cdmAdapter->getPropertyByteArray("serviceCertificate"); if (xbmc_jnienv()->ExceptionCheck()) { LOG::LogF(LOGWARNING, "Exception retrieving Service Certificate"); @@ -211,3 +245,35 @@ void CWVCdmAdapterA::SaveServiceCertificate() fclose(f); } } + +std::string_view CWVCdmAdapterA::GetKeySystem() +{ + return m_keySystem; +} + +std::string_view CWVCdmAdapterA::GetLibraryPath() const +{ + return m_host->GetLibraryPath(); +} + +void CWVCdmAdapterA::AttachObserver(IWVObserver* observer) +{ + std::lock_guard lock(m_observer_mutex); + m_observers.emplace_back(observer); +} + +void CWVCdmAdapterA::DetachObserver(IWVObserver* observer) +{ + std::lock_guard lock(m_observer_mutex); + m_observers.remove(observer); +} + +void CWVCdmAdapterA::NotifyObservers(const CdmMessage& message) +{ + std::lock_guard lock(m_observer_mutex); + for (IWVObserver* observer : m_observers) + { + if (observer) + observer->OnNotify(message); + } +} diff --git a/src/decrypters/widevineandroid/WVCdmAdapter.h b/src/decrypters/widevineandroid/WVCdmAdapter.h index 11839c410..7b1682f2f 100644 --- a/src/decrypters/widevineandroid/WVCdmAdapter.h +++ b/src/decrypters/widevineandroid/WVCdmAdapter.h @@ -8,54 +8,100 @@ #pragma once -#include +#include "decrypters/HelperWv.h" + +#include #include +#include + +#ifdef INPUTSTREAM_TEST_BUILD +#include "test/KodiStubs.h" +#else #include +#endif + +#include +#include + +class CWVDecrypterA; -enum WV_KEYSYSTEM +class CMediaDrmOnEventCallback { - NONE, - WIDEVINE, - PLAYREADY, - WISEPLAY +public: + CMediaDrmOnEventCallback() = default; + virtual ~CMediaDrmOnEventCallback() = default; + + virtual void OnMediaDrmEvent(const jni::CJNIMediaDrm& mediaDrm, + const std::vector& sessionId, + int event, + int extra, + const std::vector& data) = 0; }; -class CWVDecrypterA; +/*! + * \brief This class is derived from CJNIMediaDrmOnEventListener to allow us + * to initialize the base class at later time, since the constructors + * of CJNIMediaDrmOnEventListener need to access to the global + * xbmc_jnienv method immediately. + */ +class CMediaDrmOnEventListener : public jni::CJNIMediaDrmOnEventListener +{ +public: + CMediaDrmOnEventListener(CMediaDrmOnEventCallback* decrypterEventCallback, + std::shared_ptr classLoader); + virtual ~CMediaDrmOnEventListener() = default; + + virtual void onEvent(const jni::CJNIMediaDrm& mediaDrm, + const std::vector& sessionId, + int event, + int extra, + const std::vector& data) override; + +private: + CMediaDrmOnEventCallback* m_decrypterEventCallback; +}; -class ATTR_DLL_LOCAL CWVCdmAdapterA +class ATTR_DLL_LOCAL CWVCdmAdapterA : public CMediaDrmOnEventCallback, + public IWVCdmAdapter { public: - CWVCdmAdapterA(WV_KEYSYSTEM ks, + CWVCdmAdapterA(std::string_view keySystem, std::string_view licenseURL, const std::vector& serverCert, - jni::CJNIMediaDrmOnEventListener* listener, + std::shared_ptr jniClassLoader, CWVDecrypterA* host); ~CWVCdmAdapterA(); - jni::CJNIMediaDrm* GetMediaDrm() { return m_mediaDrm; }; - - const std::string& GetLicenseURL() const { return m_licenseUrl; }; - - const uint8_t* GetKeySystem() const - { - static const uint8_t keysystemId[3][16] = { - {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, - 0xed}, - {0x9A, 0x04, 0xF0, 0x79, 0x98, 0x40, 0x42, 0x86, 0xAB, 0x92, 0xE6, 0x5B, 0xE0, 0x88, 0x5F, - 0x95}, - {0x3d, 0x5e, 0x6d, 0x35, 0x9b, 0x9a, 0x41, 0xe8, 0xb8, 0x43, 0xdd, 0x3c, 0x6e, 0x72, 0xc4, - 0x2c}, - }; - return keysystemId[m_keySystem - 1]; - } - WV_KEYSYSTEM GetKeySystemType() const { return m_keySystem; }; - void SaveServiceCertificate(); + // IWVCdmAdapter interface methods + + std::shared_ptr GetCDM() override { return m_cdmAdapter; } + const std::string& GetLicenseUrl() override { return m_licenseUrl; } + std::string_view GetKeySystem() override; + std::string_view GetLibraryPath() const override; + void SaveServiceCertificate() override; + + // IWVSubject interface methods + + void AttachObserver(IWVObserver* observer) override; + void DetachObserver(IWVObserver* observer) override; + void NotifyObservers(const CdmMessage& message) override; private: void LoadServiceCertificate(); - WV_KEYSYSTEM m_keySystem; - jni::CJNIMediaDrm* m_mediaDrm; + // CMediaDrmOnEventCallback interface methods + void OnMediaDrmEvent(const jni::CJNIMediaDrm& mediaDrm, + const std::vector& sessionId, + int event, + int extra, + const std::vector& data) override; + + std::shared_ptr m_cdmAdapter; + std::list m_observers; + std::mutex m_observer_mutex; + + std::string m_keySystem; + std::unique_ptr m_mediaDrmEventListener; std::string m_licenseUrl; std::string m_strBasePath; CWVDecrypterA* m_host; diff --git a/src/decrypters/widevineandroid/WVCencSingleSampleDecrypter.cpp b/src/decrypters/widevineandroid/WVCencSingleSampleDecrypter.cpp index 184a1459e..0545afb58 100644 --- a/src/decrypters/widevineandroid/WVCencSingleSampleDecrypter.cpp +++ b/src/decrypters/widevineandroid/WVCencSingleSampleDecrypter.cpp @@ -10,35 +10,35 @@ #include "CompSettings.h" #include "SrvBroker.h" -#include "WVCdmAdapter.h" -#include "WVDecrypter.h" -#include "jsmn.h" +#include "decrypters/HelperWv.h" #include "decrypters/Helpers.h" +#include "jsmn.h" #include "utils/Base64Utils.h" #include "utils/CurlUtils.h" #include "utils/DigestMD5Utils.h" #include "utils/FileUtils.h" #include "utils/StringUtils.h" -#include "utils/Utils.h" #include "utils/log.h" +#include #include +#include + using namespace UTILS; -using namespace kodi::tools; - -CWVCencSingleSampleDecrypterA::CWVCencSingleSampleDecrypterA(CWVCdmAdapterA& drm, - std::vector& pssh, - std::string_view optionalKeyParameter, - const std::vector& defaultKeyId, - CWVDecrypterA* host) - : m_mediaDrm(drm), + +CWVCencSingleSampleDecrypterA::CWVCencSingleSampleDecrypterA( + IWVCdmAdapter* cdmAdapter, + std::vector& pssh, + std::string_view optionalKeyParameter, + const std::vector& defaultKeyId) + : m_cdmAdapter(cdmAdapter), + m_pssh(pssh), m_isProvisioningRequested(false), m_isKeyUpdateRequested(false), m_hdcpLimit(0), m_resolutionLimit(0), - m_defaultKeyId{defaultKeyId}, - m_host{host} + m_defaultKeyId{defaultKeyId} { SetParentIsOwner(false); @@ -49,12 +49,12 @@ CWVCencSingleSampleDecrypterA::CWVCencSingleSampleDecrypterA(CWVCdmAdapterA& drm return; } - m_pssh = pssh; + m_cdmAdapter->AttachObserver(this); if (CSrvBroker::GetSettings().IsDebugLicense()) { std::string debugFilePath = - FILESYS::PathCombine(m_host->GetLibraryPath(), "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED.init"); + FILESYS::PathCombine(m_cdmAdapter->GetLibraryPath(), "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED.init"); std::string data{reinterpret_cast(m_pssh.data()), m_pssh.size()}; FILESYS::SaveFile(debugFilePath, data, true); } @@ -65,7 +65,7 @@ CWVCencSingleSampleDecrypterA::CWVCencSingleSampleDecrypterA(CWVCdmAdapterA& drm m_optParams["PRCustomData"] = optionalKeyParameter; /* - std::vector pui = m_mediaDrm.GetMediaDrm()->getPropertyByteArray("provisioningUniqueId"); + std::vector pui = m_cdmAdapter->GetCDM()->getPropertyByteArray("provisioningUniqueId"); xbmc_jnienv()->ExceptionClear(); if (pui.size() > 0) { @@ -76,7 +76,8 @@ CWVCencSingleSampleDecrypterA::CWVCencSingleSampleDecrypterA(CWVCdmAdapterA& drm bool L3FallbackRequested = false; RETRY_OPEN: - m_sessionId = m_mediaDrm.GetMediaDrm()->openSession(); + m_sessionIdVec = m_cdmAdapter->GetCDM()->openSession(); + m_sessionId.assign(m_sessionIdVec.cbegin(), m_sessionIdVec.cend()); if (xbmc_jnienv()->ExceptionCheck()) { xbmc_jnienv()->ExceptionClear(); @@ -87,12 +88,12 @@ CWVCencSingleSampleDecrypterA::CWVCencSingleSampleDecrypterA(CWVCdmAdapterA& drm if (!ProvisionRequest()) { if (!L3FallbackRequested && - m_mediaDrm.GetMediaDrm()->getPropertyString("securityLevel") == "L1") + m_cdmAdapter->GetCDM()->getPropertyString("securityLevel") == "L1") { LOG::LogF(LOGWARNING, "L1 provisioning failed - retrying with L3..."); L3FallbackRequested = true; m_isProvisioningRequested = false; - m_mediaDrm.GetMediaDrm()->setPropertyString("securityLevel", "L3"); + m_cdmAdapter->GetCDM()->setPropertyString("securityLevel", "L3"); goto RETRY_OPEN; } else @@ -107,46 +108,55 @@ CWVCencSingleSampleDecrypterA::CWVCencSingleSampleDecrypterA(CWVCdmAdapterA& drm } } - if (m_sessionId.size() == 0) + if (m_sessionId.empty()) { LOG::LogF(LOGERROR, "Unable to open DRM session"); return; } - memcpy(m_sessionIdChar, m_sessionId.data(), m_sessionId.size()); - m_sessionIdChar[m_sessionId.size()] = 0; - - if (m_mediaDrm.GetKeySystemType() != PLAYREADY) + if (m_cdmAdapter->GetKeySystem() != DRM::KS_PLAYREADY) { - int maxSecuritylevel = m_mediaDrm.GetMediaDrm()->getMaxSecurityLevel(); + int maxSecuritylevel = m_cdmAdapter->GetCDM()->getMaxSecurityLevel(); xbmc_jnienv()->ExceptionClear(); - LOG::Log(LOGDEBUG, "Session ID: %s, Max security level: %d", m_sessionIdChar, maxSecuritylevel); + LOG::Log(LOGDEBUG, "Session ID: %s, Max security level: %d", m_sessionId.c_str(), maxSecuritylevel); } } CWVCencSingleSampleDecrypterA::~CWVCencSingleSampleDecrypterA() { + // This decrypter can be used/shared with more streams "sessions" + // since it is used wrapped in a shared_ptr the destructor will be called only + // when the last stream "session" will be deleted, and so it can close the CDM session. if (!m_sessionId.empty()) { - m_mediaDrm.GetMediaDrm()->removeKeys(m_sessionId); + /* + m_cdmAdapter->GetCDM()->removeKeys(m_sessionIdVec); if (xbmc_jnienv()->ExceptionCheck()) { LOG::LogF(LOGERROR, "removeKeys has raised an exception"); xbmc_jnienv()->ExceptionClear(); } - m_mediaDrm.GetMediaDrm()->closeSession(m_sessionId); + */ + m_cdmAdapter->GetCDM()->closeSession(m_sessionIdVec); if (xbmc_jnienv()->ExceptionCheck()) { LOG::LogF(LOGERROR, "closeSession has raised an exception"); xbmc_jnienv()->ExceptionClear(); } + else + LOG::LogF(LOGDEBUG, "MediaDrm Session ID %s closed", m_sessionId.c_str()); + + m_sessionIdVec.clear(); + m_sessionId.clear(); } + + m_cdmAdapter->DetachObserver(this); } const char* CWVCencSingleSampleDecrypterA::GetSessionId() { - return m_sessionIdChar; + return m_sessionId.c_str(); } std::vector CWVCencSingleSampleDecrypterA::GetChallengeData() @@ -163,10 +173,11 @@ bool CWVCencSingleSampleDecrypterA::HasLicenseKey(const std::vector& ke void CWVCencSingleSampleDecrypterA::GetCapabilities(const std::vector& keyId, uint32_t media, - DecrypterCapabilites& caps) + DRM::DecrypterCapabilites& caps) { - caps = {DecrypterCapabilites::SSD_SECURE_PATH | DecrypterCapabilites::SSD_ANNEXB_REQUIRED, 0, - m_hdcpLimit}; + caps = {DRM::DecrypterCapabilites::SSD_SECURE_PATH | + DRM::DecrypterCapabilites::SSD_ANNEXB_REQUIRED, + 0, m_hdcpLimit}; if (caps.hdcpLimit == 0) caps.hdcpLimit = m_resolutionLimit; @@ -174,21 +185,32 @@ void CWVCencSingleSampleDecrypterA::GetCapabilities(const std::vector& // Note: Currently we check for L1 only, Kodi core at later time check if secure decoder is needed // by using requiresSecureDecoderComponent method of MediaDrm API // https://github.com/xbmc/xbmc/blob/Nexus/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp#L639-L641 - if (m_mediaDrm.GetMediaDrm()->getPropertyString("securityLevel") == "L1") + if (m_cdmAdapter->GetCDM()->getPropertyString("securityLevel") == "L1") { caps.hdcpLimit = m_resolutionLimit; //No restriction - caps.flags |= DecrypterCapabilites::SSD_SECURE_DECODER; + caps.flags |= DRM::DecrypterCapabilites::SSD_SECURE_DECODER; } LOG::LogF(LOGDEBUG, "hdcpLimit: %i", caps.hdcpLimit); caps.hdcpVersion = 99; } +void CWVCencSingleSampleDecrypterA::OnNotify(const CdmMessage& message) +{ + if (!m_sessionId.empty() && m_sessionId != message.sessionId) + return; + + if (message.type == CdmMessageType::EVENT_KEY_REQUIRED) + { + RequestNewKeys(); + } +} + bool CWVCencSingleSampleDecrypterA::ProvisionRequest() { - LOG::Log(LOGWARNING, "Provision data request (DRM:%p)", m_mediaDrm.GetMediaDrm()); + LOG::Log(LOGWARNING, "Provision data request (MediaDrm instance: %p)", m_cdmAdapter->GetCDM().get()); - CJNIMediaDrmProvisionRequest request = m_mediaDrm.GetMediaDrm()->getProvisionRequest(); + jni::CJNIMediaDrmProvisionRequest request = m_cdmAdapter->GetCDM()->getProvisionRequest(); if (xbmc_jnienv()->ExceptionCheck()) { LOG::LogF(LOGERROR, "getProvisionRequest has raised an exception"); @@ -227,7 +249,7 @@ bool CWVCencSingleSampleDecrypterA::ProvisionRequest() } std::copy(response.begin(), response.end(), std::back_inserter(provData)); - m_mediaDrm.GetMediaDrm()->provideProvisionResponse(provData); + m_cdmAdapter->GetCDM()->provideProvisionResponse(provData); if (xbmc_jnienv()->ExceptionCheck()) { LOG::LogF(LOGERROR, "provideProvisionResponse has raised an exception"); @@ -239,8 +261,8 @@ bool CWVCencSingleSampleDecrypterA::ProvisionRequest() bool CWVCencSingleSampleDecrypterA::GetKeyRequest(std::vector& keyRequestData) { - CJNIMediaDrmKeyRequest keyRequest = m_mediaDrm.GetMediaDrm()->getKeyRequest( - m_sessionId, m_pssh, "video/mp4", CJNIMediaDrm::KEY_TYPE_STREAMING, m_optParams); + jni::CJNIMediaDrmKeyRequest keyRequest = m_cdmAdapter->GetCDM()->getKeyRequest( + m_sessionIdVec, m_pssh, "video/mp4", jni::CJNIMediaDrm::KEY_TYPE_STREAMING, m_optParams); if (xbmc_jnienv()->ExceptionCheck()) { @@ -272,6 +294,7 @@ bool CWVCencSingleSampleDecrypterA::KeyUpdateRequest(bool waitKeys, bool skipSes if (skipSessionMessage) return true; + //! @todo: exists ways to wait callbacks without make uses of wait loop and thread sleep m_isKeyUpdateRequested = false; if (!SendSessionMessage(m_keyRequestData)) return false; @@ -290,14 +313,14 @@ bool CWVCencSingleSampleDecrypterA::KeyUpdateRequest(bool waitKeys, bool skipSes } } - if (m_mediaDrm.GetKeySystemType() != PLAYREADY) + if (m_cdmAdapter->GetKeySystem() != DRM::KS_PLAYREADY) { - int securityLevel = m_mediaDrm.GetMediaDrm()->getSecurityLevel(m_sessionId); + int securityLevel = m_cdmAdapter->GetCDM()->getSecurityLevel(m_sessionIdVec); xbmc_jnienv()->ExceptionClear(); LOG::Log(LOGDEBUG, "Security level: %d", securityLevel); std::map keyStatus = - m_mediaDrm.GetMediaDrm()->queryKeyStatus(m_sessionId); + m_cdmAdapter->GetCDM()->queryKeyStatus(m_sessionIdVec); LOG::Log(LOGDEBUG, "Key status (%ld):", keyStatus.size()); for (auto const& ks : keyStatus) { @@ -309,7 +332,7 @@ bool CWVCencSingleSampleDecrypterA::KeyUpdateRequest(bool waitKeys, bool skipSes bool CWVCencSingleSampleDecrypterA::SendSessionMessage(const std::vector& keyRequestData) { - std::vector blocks{StringUtils::Split(m_mediaDrm.GetLicenseURL(), '|')}; + std::vector blocks{STRING::SplitToVec(m_cdmAdapter->GetLicenseUrl(), '|')}; if (blocks.size() != 4) { @@ -321,8 +344,8 @@ bool CWVCencSingleSampleDecrypterA::SendSessionMessage(const std::vector& if (CSrvBroker::GetSettings().IsDebugLicense()) { std::string debugFilePath = FILESYS::PathCombine( - m_host->GetLibraryPath(), "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED.challenge"); - UTILS::FILESYS::SaveFile(debugFilePath, keyRequestData.data(), true); + m_cdmAdapter->GetLibraryPath(), "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED.challenge"); + FILESYS::SaveFile(debugFilePath, keyRequestData.data(), true); } //Process placeholder in GET String @@ -357,17 +380,17 @@ bool CWVCencSingleSampleDecrypterA::SendSessionMessage(const std::vector& std::string contentType; //Process headers - std::vector headers{StringUtils::Split(blocks[1], '&')}; + std::vector headers{STRING::SplitToVec(blocks[1], '&')}; for (std::string& headerStr : headers) { - std::vector header{StringUtils::Split(headerStr, '=')}; + std::vector header{STRING::SplitToVec(headerStr, '=')}; if (!header.empty()) { - StringUtils::Trim(header[0]); + STRING::Trim(header[0]); std::string value; if (header.size() > 1) { - StringUtils::Trim(header[1]); + STRING::Trim(header[1]); value = STRING::URLDecode(header[1]); } file.AddHeader(header[0], value); @@ -522,8 +545,8 @@ bool CWVCencSingleSampleDecrypterA::SendSessionMessage(const std::vector& if (CSrvBroker::GetSettings().IsDebugLicense()) { std::string debugFilePath = FILESYS::PathCombine( - m_host->GetLibraryPath(), "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED.postdata"); - UTILS::FILESYS::SaveFile(debugFilePath, blocks[2], true); + m_cdmAdapter->GetLibraryPath(), "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED.postdata"); + FILESYS::SaveFile(debugFilePath, blocks[2], true); } } @@ -565,7 +588,7 @@ bool CWVCencSingleSampleDecrypterA::SendSessionMessage(const std::vector& return false; } - if (m_mediaDrm.GetKeySystemType() == PLAYREADY && + if (m_cdmAdapter->GetKeySystem() == DRM::KS_PLAYREADY && response.find("") == std::string::npos) { std::string::size_type dstPos(response.find("")); @@ -583,8 +606,8 @@ bool CWVCencSingleSampleDecrypterA::SendSessionMessage(const std::vector& if (CSrvBroker::GetSettings().IsDebugLicense()) { std::string debugFilePath = FILESYS::PathCombine( - m_host->GetLibraryPath(), "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED.response"); - UTILS::FILESYS::SaveFile(debugFilePath, response, true); + m_cdmAdapter->GetLibraryPath(), "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED.response"); + FILESYS::SaveFile(debugFilePath, response, true); } if (!blocks[3].empty() && blocks[3][0] != 'R' && @@ -607,7 +630,7 @@ bool CWVCencSingleSampleDecrypterA::SendSessionMessage(const std::vector& jsmn_init(&jsn); int i(0), numTokens = jsmn_parse(&jsn, response.c_str(), response.size(), tokens, 256); - std::vector jsonVals{StringUtils::Split(blocks[3].substr(dataPos), ';')}; + std::vector jsonVals{STRING::SplitToVec(blocks[3].substr(dataPos), ';')}; // Find HDCP limit if (jsonVals.size() > 1) @@ -685,8 +708,8 @@ bool CWVCencSingleSampleDecrypterA::SendSessionMessage(const std::vector& } } - m_keySetId = m_mediaDrm.GetMediaDrm()->provideKeyResponse( - m_sessionId, std::vector(response.data(), response.data() + response.size())); + m_keySetId = m_cdmAdapter->GetCDM()->provideKeyResponse( + m_sessionIdVec, std::vector(response.data(), response.data() + response.size())); if (xbmc_jnienv()->ExceptionCheck()) { LOG::LogF(LOGERROR, "provideKeyResponse has raised an exception"); @@ -695,7 +718,7 @@ bool CWVCencSingleSampleDecrypterA::SendSessionMessage(const std::vector& } if (keyRequestData.size() == 2) - m_mediaDrm.SaveServiceCertificate(); + m_cdmAdapter->SaveServiceCertificate(); LOG::Log(LOGDEBUG, "License update successful"); return true; @@ -750,7 +773,7 @@ AP4_Result CWVCencSingleSampleDecrypterA::DecryptSampleData(AP4_UI32 poolId, const AP4_UI16* bytesOfCleartextData, const AP4_UI32* bytesOfEncryptedData) { - if (!m_mediaDrm.GetMediaDrm()) + if (!m_cdmAdapter->GetCDM()) return AP4_ERROR_INVALID_STATE; if (dataIn.GetDataSize() > 0) diff --git a/src/decrypters/widevineandroid/WVCencSingleSampleDecrypter.h b/src/decrypters/widevineandroid/WVCencSingleSampleDecrypter.h index 65ec495b6..5f8f0d833 100644 --- a/src/decrypters/widevineandroid/WVCencSingleSampleDecrypter.h +++ b/src/decrypters/widevineandroid/WVCencSingleSampleDecrypter.h @@ -7,30 +7,31 @@ */ #pragma once -#include "../IDecrypter.h" + #include "common/AdaptiveCencSampleDecrypter.h" +#include "decrypters/HelperWv.h" +#include "decrypters/IDecrypter.h" #include #include -#include - -class CWVCdmAdapterA; -class CWVDecrypterA; +namespace jni +{ +class CJNIMediaDrm; +} -class ATTR_DLL_LOCAL CWVCencSingleSampleDecrypterA : public Adaptive_CencSingleSampleDecrypter +class ATTR_DLL_LOCAL CWVCencSingleSampleDecrypterA : public Adaptive_CencSingleSampleDecrypter, + public IWVObserver { public: - // methods - CWVCencSingleSampleDecrypterA(CWVCdmAdapterA& drm, + CWVCencSingleSampleDecrypterA(IWVCdmAdapter* cdmAdapter, std::vector& pssh, std::string_view optionalKeyParameter, - const std::vector& defaultKeyId, - CWVDecrypterA* host); + const std::vector& defaultKeyId); virtual ~CWVCencSingleSampleDecrypterA(); bool StartSession(bool skipSessionMessage) { return KeyUpdateRequest(true, skipSessionMessage); }; - const std::vector& GetSessionIdRaw() { return m_sessionId; }; + virtual const char* GetSessionId() override; std::vector GetChallengeData(); virtual bool HasLicenseKey(const std::vector& keyId); @@ -67,23 +68,26 @@ class ATTR_DLL_LOCAL CWVCencSingleSampleDecrypterA : public Adaptive_CencSingleS void RequestNewKeys() { m_isKeyUpdateRequested = true; }; + // IWVObserver interface + void OnNotify(const CdmMessage& message) override; + private: bool ProvisionRequest(); bool GetKeyRequest(std::vector& keyRequestData); bool KeyUpdateRequest(bool waitForKeys, bool skipSessionMessage); bool SendSessionMessage(const std::vector& keyRequestData); - CWVCdmAdapterA& m_mediaDrm; + IWVCdmAdapter* m_cdmAdapter; + std::vector m_pssh; std::vector m_initialPssh; std::map m_optParams; - CWVDecrypterA* m_host; - std::vector m_sessionId; + std::string m_sessionId; + std::vector m_sessionIdVec; std::vector m_keySetId; std::vector m_keyRequestData; - char m_sessionIdChar[128]; bool m_isProvisioningRequested; bool m_isKeyUpdateRequested; diff --git a/src/decrypters/widevineandroid/WVDecrypter.cpp b/src/decrypters/widevineandroid/WVDecrypter.cpp index 131841af2..0dce31a55 100644 --- a/src/decrypters/widevineandroid/WVDecrypter.cpp +++ b/src/decrypters/widevineandroid/WVDecrypter.cpp @@ -8,52 +8,26 @@ #include "WVDecrypter.h" -#include "decrypters/Helpers.h" +#include "WVCdmAdapter.h" #include "WVCencSingleSampleDecrypter.h" #include "common/AdaptiveDecrypter.h" +#include "decrypters/Helpers.h" #include "jsmn.h" -#include "kodi/tools/StringUtils.h" #include "utils/Base64Utils.h" -#include "utils/DigestMD5Utils.h" -#include "utils/StringUtils.h" -#include "utils/Utils.h" - -#include -#include -#include -#include -#include +#include "utils/log.h" -#include #include #include -#include using namespace DRM; using namespace UTILS; -using namespace kodi::tools; namespace { kodi::platform::CInterfaceAndroidSystem* ANDROID_SYSTEM{nullptr}; } // unnamed namespace -CMediaDrmOnEventListener::CMediaDrmOnEventListener(CMediaDrmOnEventCallback* decrypterEventCallback, - CJNIClassLoader* classLoader) - : CJNIMediaDrmOnEventListener(classLoader), m_decrypterEventCallback(decrypterEventCallback) -{ -} - -void CMediaDrmOnEventListener::onEvent(const CJNIMediaDrm& mediaDrm, - const std::vector& sessionId, - int event, - int extra, - const std::vector& data) -{ - m_decrypterEventCallback->OnMediaDrmEvent(mediaDrm, sessionId, event, extra, data); -} - -CWVDecrypterA::CWVDecrypterA() : m_keySystem(NONE), m_WVCdmAdapter(nullptr) +CWVDecrypterA::CWVDecrypterA() { // CInterfaceAndroidSystem need to be initialized at runtime // then we have to set it to global variable just now @@ -62,13 +36,9 @@ CWVDecrypterA::CWVDecrypterA() : m_keySystem(NONE), m_WVCdmAdapter(nullptr) CWVDecrypterA::~CWVDecrypterA() { - delete m_WVCdmAdapter; - m_WVCdmAdapter = nullptr; - #ifdef DRMTHREAD m_jniCondition.notify_one(); m_jniWorker->join(); - delete m_jniWorker; #endif }; @@ -86,30 +56,32 @@ void JNIThread(JavaVM* vm) std::vector CWVDecrypterA::SelectKeySystems(std::string_view keySystem) { LOG::Log(LOGDEBUG, "Key system request: %s", keySystem); - std::vector keySystems; + if (keySystem == KS_WIDEVINE) { - m_keySystem = WIDEVINE; - keySystems.push_back(URN_WIDEVINE); + m_keySystem = keySystem; + return {URN_WIDEVINE}; } else if (keySystem == KS_WISEPLAY) { - m_keySystem = WISEPLAY; - keySystems.push_back(URN_WISEPLAY); + m_keySystem = keySystem; + return {URN_WISEPLAY}; } else if (keySystem == KS_PLAYREADY) { - m_keySystem = PLAYREADY; - keySystems.push_back(URN_PLAYREADY); + m_keySystem = keySystem; + return {URN_PLAYREADY}; } - return keySystems; + + m_keySystem.clear(); + return {}; } bool CWVDecrypterA::OpenDRMSystem(std::string_view licenseURL, const std::vector& serverCertificate, const uint8_t config) { - if (m_keySystem == NONE) + if (m_keySystem.empty()) return false; if (licenseURL.empty()) @@ -118,13 +90,13 @@ bool CWVDecrypterA::OpenDRMSystem(std::string_view licenseURL, return false; } - m_WVCdmAdapter = new CWVCdmAdapterA(m_keySystem, licenseURL, serverCertificate, - m_mediaDrmEventListener.get(), this); + m_WVCdmAdapter = std::make_shared(m_keySystem, licenseURL, serverCertificate, + m_classLoader, this); - return m_WVCdmAdapter->GetMediaDrm(); + return m_WVCdmAdapter->GetCDM() != nullptr; } -Adaptive_CencSingleSampleDecrypter* CWVDecrypterA::CreateSingleSampleDecrypter( +std::shared_ptr CWVDecrypterA::CreateSingleSampleDecrypter( std::vector& initData, std::string_view optionalKeyParameter, const std::vector& defaultKeyId, @@ -132,102 +104,71 @@ Adaptive_CencSingleSampleDecrypter* CWVDecrypterA::CreateSingleSampleDecrypter( bool skipSessionMessage, CryptoMode cryptoMode) { - CWVCencSingleSampleDecrypterA* decrypter = new CWVCencSingleSampleDecrypterA( - *m_WVCdmAdapter, initData, optionalKeyParameter, defaultKeyId, this); - - { - std::lock_guard lk(m_decrypterListMutex); - m_decrypterList.push_back(decrypter); - } + std::shared_ptr decrypter = + std::make_shared(m_WVCdmAdapter.get(), initData, + optionalKeyParameter, defaultKeyId); if (!(*decrypter->GetSessionId() && decrypter->StartSession(skipSessionMessage))) { - DestroySingleSampleDecrypter(decrypter); return nullptr; } return decrypter; } -void CWVDecrypterA::DestroySingleSampleDecrypter(Adaptive_CencSingleSampleDecrypter* decrypter) +void CWVDecrypterA::GetCapabilities(std::shared_ptr decrypter, + const std::vector& keyId, + uint32_t media, + DRM::DecrypterCapabilites& caps) { - if (decrypter) + if (!decrypter) { - std::vector::const_iterator res = - std::find(m_decrypterList.begin(), m_decrypterList.end(), decrypter); - if (res != m_decrypterList.end()) - { - std::lock_guard lk(m_decrypterListMutex); - m_decrypterList.erase(res); - } - delete static_cast(decrypter); + caps = {0, 0, 0}; + return; } -} -void CWVDecrypterA::GetCapabilities(Adaptive_CencSingleSampleDecrypter* decrypter, - const std::vector& keyId, - uint32_t media, - DecrypterCapabilites& caps) -{ - if (decrypter) - static_cast(decrypter)->GetCapabilities(keyId, media, caps); + auto wvDecrypter = std::dynamic_pointer_cast(decrypter); + if (wvDecrypter) + { + wvDecrypter->GetCapabilities(keyId, media, caps); + } else - caps = {0, 0, 0}; + LOG::LogF(LOGFATAL, "Cannot cast the decrypter shared pointer."); } -bool CWVDecrypterA::HasLicenseKey(Adaptive_CencSingleSampleDecrypter* decrypter, +bool CWVDecrypterA::HasLicenseKey(std::shared_ptr decrypter, const std::vector& keyId) { - if (decrypter) - return static_cast(decrypter)->HasLicenseKey(keyId); - return false; -} - -std::string CWVDecrypterA::GetChallengeB64Data(Adaptive_CencSingleSampleDecrypter* decrypter) -{ - if (!decrypter) - return ""; + auto wvDecrypter = std::dynamic_pointer_cast(decrypter); + if (wvDecrypter) + { + return wvDecrypter->HasLicenseKey(keyId); + } + else + LOG::LogF(LOGFATAL, "Cannot cast the decrypter shared pointer."); - const std::vector data = static_cast(decrypter)->GetChallengeData(); - return BASE64::Encode(data); + return false; } -void CWVDecrypterA::OnMediaDrmEvent(const CJNIMediaDrm& mediaDrm, - const std::vector& sessionId, - int event, - int extra, - const std::vector& data) +std::string CWVDecrypterA::GetChallengeB64Data(std::shared_ptr decrypter) { - LOG::LogF(LOGDEBUG, "%d arrived, #decrypter: %lu", event, m_decrypterList.size()); - //we have only one DRM system running (m_WVCdmAdapter) so there is no need to compare mediaDrm - std::lock_guard lk(m_decrypterListMutex); - for (std::vector::iterator b(m_decrypterList.begin()), - e(m_decrypterList.end()); - b != e; ++b) + auto wvDecrypter = std::dynamic_pointer_cast(decrypter); + if (wvDecrypter) { - if (sessionId.empty() || (*b)->GetSessionIdRaw() == sessionId) - { - switch (event) - { - case CJNIMediaDrm::EVENT_KEY_REQUIRED: - (*b)->RequestNewKeys(); - break; - default:; - } - } - else - { - LOG::LogF(LOGDEBUG, "Session does not match: sizes: %lu -> %lu", sessionId.size(), - (*b)->GetSessionIdRaw().size()); - } + const std::vector challengeData = wvDecrypter->GetChallengeData(); + return BASE64::Encode(challengeData); } + else + LOG::LogF(LOGFATAL, "Cannot cast the decrypter shared pointer."); + + return ""; } bool CWVDecrypterA::Initialize() { #ifdef DRMTHREAD std::unique_lock lk(m_jniMutex); - m_jniWorker = new std::thread(&CWVDecrypterA::JNIThread, this, - reinterpret_cast(m_androidSystem.GetJNIEnv())); + m_jniWorker = std::make_unique( + &CWVDecrypterA::JNIThread, this, reinterpret_cast(m_androidSystem.GetJNIEnv())); m_jniCondition.wait(lk); #endif if (xbmc_jnienv()->ExceptionCheck()) @@ -239,7 +180,6 @@ bool CWVDecrypterA::Initialize() } //JNIEnv* env = static_cast(m_androidSystem.GetJNIEnv()); - CJNIClassLoader* classLoader; CJNIBase::SetSDKVersion(m_androidSystem.GetSDKVersion()); CJNIBase::SetBaseClassName(m_androidSystem.GetClassName()); LOG::Log(LOGDEBUG, "WVDecrypter JNI, SDK version: %d", m_androidSystem.GetSDKVersion()); @@ -249,25 +189,23 @@ bool CWVDecrypterA::Initialize() apkEnv = getenv("KODI_ANDROID_APK"); if (!apkEnv) + { + LOG::LogF(LOGERROR, "Cannot get enviroment XBMC_ANDROID_APK/KODI_ANDROID_APK value"); return false; + } std::string apkPath = apkEnv; - //! @todo: make classLoader a smartpointer - classLoader = new CJNIClassLoader(apkPath); + m_classLoader = std::make_shared(apkEnv); if (xbmc_jnienv()->ExceptionCheck()) { LOG::LogF(LOGERROR, "Failed to create ClassLoader"); xbmc_jnienv()->ExceptionDescribe(); xbmc_jnienv()->ExceptionClear(); - delete classLoader, classLoader = nullptr; - return false; } - m_classLoader = classLoader; - m_mediaDrmEventListener = std::make_unique(this, m_classLoader); return true; } diff --git a/src/decrypters/widevineandroid/WVDecrypter.h b/src/decrypters/widevineandroid/WVDecrypter.h index f009aca68..fdd398994 100644 --- a/src/decrypters/widevineandroid/WVDecrypter.h +++ b/src/decrypters/widevineandroid/WVDecrypter.h @@ -8,61 +8,24 @@ #pragma once -#include "../IDecrypter.h" -#include "WVCdmAdapter.h" -#include "utils/Base64Utils.h" -#include "utils/log.h" +#include "decrypters/IDecrypter.h" #include #include -#include -#include -#include - -class CWVCencSingleSampleDecrypterA; +#ifdef DRMTHREAD +#include +#endif -using namespace UTILS; -using namespace DRM; -using namespace jni; +#include -class CMediaDrmOnEventCallback +class CWVCdmAdapterA; +namespace jni { -public: - CMediaDrmOnEventCallback() = default; - virtual ~CMediaDrmOnEventCallback() = default; - - virtual void OnMediaDrmEvent(const CJNIMediaDrm& mediaDrm, - const std::vector& sessionId, - int event, - int extra, - const std::vector& data) = 0; -}; +class CJNIClassLoader; +} -/*! - * \brief This class is derived from CJNIMediaDrmOnEventListener to allow us - * to initialize the base class at later time, since the constructors - * of CJNIMediaDrmOnEventListener need to access to the global - * xbmc_jnienv method immediately. - */ -class CMediaDrmOnEventListener : public CJNIMediaDrmOnEventListener -{ -public: - CMediaDrmOnEventListener(CMediaDrmOnEventCallback* decrypterEventCallback, - CJNIClassLoader* classLoader); - virtual ~CMediaDrmOnEventListener() = default; - - virtual void onEvent(const CJNIMediaDrm& mediaDrm, - const std::vector& sessionId, - int event, - int extra, - const std::vector& data) override; - -private: - CMediaDrmOnEventCallback* m_decrypterEventCallback; -}; - -class ATTR_DLL_LOCAL CWVDecrypterA : public IDecrypter, public CMediaDrmOnEventCallback +class ATTR_DLL_LOCAL CWVDecrypterA : public DRM::IDecrypter { public: CWVDecrypterA(); @@ -87,7 +50,7 @@ class ATTR_DLL_LOCAL CWVDecrypterA : public IDecrypter, public CMediaDrmOnEventC const std::vector& serverCertificate, const uint8_t config) override; - virtual Adaptive_CencSingleSampleDecrypter* CreateSingleSampleDecrypter( + virtual std::shared_ptr CreateSingleSampleDecrypter( std::vector& initData, std::string_view optionalKeyParameter, const std::vector& defaultKeyId, @@ -95,21 +58,19 @@ class ATTR_DLL_LOCAL CWVDecrypterA : public IDecrypter, public CMediaDrmOnEventC bool skipSessionMessage, CryptoMode cryptoMode) override; - virtual void DestroySingleSampleDecrypter(Adaptive_CencSingleSampleDecrypter* decrypter) override; - - virtual void GetCapabilities(Adaptive_CencSingleSampleDecrypter* decrypter, + virtual void GetCapabilities(std::shared_ptr decrypter, const std::vector& keyId, uint32_t media, - DecrypterCapabilites& caps) override; + DRM::DecrypterCapabilites& caps) override; - virtual bool HasLicenseKey(Adaptive_CencSingleSampleDecrypter* decrypter, + virtual bool HasLicenseKey(std::shared_ptr decrypter, const std::vector& keyId) override; - virtual std::string GetChallengeB64Data(Adaptive_CencSingleSampleDecrypter* decrypter) override; + virtual std::string GetChallengeB64Data(std::shared_ptr decrypter) override; virtual bool IsInitialised() override { return m_WVCdmAdapter != nullptr; } - virtual bool OpenVideoDecoder(Adaptive_CencSingleSampleDecrypter* decrypter, + virtual bool OpenVideoDecoder(std::shared_ptr decrypter, const VIDEOCODEC_INITDATA* initData) override { return false; @@ -135,25 +96,16 @@ class ATTR_DLL_LOCAL CWVDecrypterA : public IDecrypter, public CMediaDrmOnEventC } virtual std::string_view GetLibraryPath() const override { return m_libraryPath; } - virtual void OnMediaDrmEvent(const CJNIMediaDrm& mediaDrm, - const std::vector& sessionId, - int event, - int extra, - const std::vector& data) override; - private: std::string m_libraryPath; kodi::platform::CInterfaceAndroidSystem m_androidSystem; - std::unique_ptr m_mediaDrmEventListener; - WV_KEYSYSTEM m_keySystem; - CWVCdmAdapterA* m_WVCdmAdapter; - inline static CJNIClassLoader* m_classLoader{nullptr}; - std::vector m_decrypterList; - std::mutex m_decrypterListMutex; + std::string m_keySystem; + std::shared_ptr m_WVCdmAdapter; + std::shared_ptr m_classLoader; std::string m_retvalHelper; #ifdef DRMTHREAD std::mutex m_jniMutex; std::condition_variable m_jniCondition; - std::thread* m_jniWorker; + std::unique_ptr m_jniWorker; #endif }; diff --git a/src/main.cpp b/src/main.cpp index 4d8217d83..1ed55f60d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -610,7 +610,7 @@ bool CVideoCodecAdaptive::Open(const kodi::addon::VideoCodecInitdata& initData) m_name += ".decoder"; std::string sessionId(initData.GetCryptoSession().GetSessionId()); - Adaptive_CencSingleSampleDecrypter* ssd(m_session->GetSingleSampleDecrypter(sessionId)); + std::shared_ptr ssd(m_session->GetSingleSampleDecrypter(sessionId)); return m_session->GetDecrypter()->OpenVideoDecoder( ssd, initData.GetCStructure()); diff --git a/src/samplereader/FragmentedSampleReader.cpp b/src/samplereader/FragmentedSampleReader.cpp index 3adc07b1c..b49706ae0 100644 --- a/src/samplereader/FragmentedSampleReader.cpp +++ b/src/samplereader/FragmentedSampleReader.cpp @@ -98,7 +98,7 @@ bool CFragmentedSampleReader::Initialize(SESSION::CStream* stream) return true; } -void CFragmentedSampleReader::SetDecrypter(Adaptive_CencSingleSampleDecrypter* ssd, +void CFragmentedSampleReader::SetDecrypter(std::shared_ptr ssd, const DRM::DecrypterCapabilites& dcaps) { if (ssd) diff --git a/src/samplereader/FragmentedSampleReader.h b/src/samplereader/FragmentedSampleReader.h index ae2b3f985..de7b0c5d1 100644 --- a/src/samplereader/FragmentedSampleReader.h +++ b/src/samplereader/FragmentedSampleReader.h @@ -26,7 +26,7 @@ class ATTR_DLL_LOCAL CFragmentedSampleReader : public ISampleReader, public AP4_ ~CFragmentedSampleReader(); virtual bool Initialize(SESSION::CStream* stream) override; - virtual void SetDecrypter(Adaptive_CencSingleSampleDecrypter* ssd, + virtual void SetDecrypter(std::shared_ptr ssd, const DRM::DecrypterCapabilites& dcaps) override; AP4_Result Start(bool& bStarted) override; @@ -80,7 +80,7 @@ class ATTR_DLL_LOCAL CFragmentedSampleReader : public ISampleReader, public AP4_ CodecHandler* m_codecHandler{nullptr}; std::vector m_defaultKey; AP4_ProtectedSampleDescription* m_protectedDesc{nullptr}; - Adaptive_CencSingleSampleDecrypter* m_singleSampleDecryptor{nullptr}; + std::shared_ptr m_singleSampleDecryptor{nullptr}; CAdaptiveCencSampleDecrypter* m_decrypter{nullptr}; CryptoInfo m_readerCryptoInfo{}; }; diff --git a/src/samplereader/SampleReader.h b/src/samplereader/SampleReader.h index 8f00674ea..b020693e0 100644 --- a/src/samplereader/SampleReader.h +++ b/src/samplereader/SampleReader.h @@ -45,7 +45,7 @@ class ATTR_DLL_LOCAL ISampleReader public: virtual ~ISampleReader() = default; virtual bool Initialize(SESSION::CStream* stream) { return true; } - virtual void SetDecrypter(Adaptive_CencSingleSampleDecrypter* ssd, + virtual void SetDecrypter(std::shared_ptr ssd, const DRM::DecrypterCapabilites& dcaps){}; /*! * \brief Defines if the end of the stream is reached