From d11d29e31864ade258a5d6bdaff3eb66bcacda19 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sun, 15 Oct 2023 20:58:51 -0700 Subject: [PATCH] Update to latest Omega. --- lib/jni/jni/jutils/jutils-details.hpp | 34 ++++++ lib/jni/jni/src/MediaDrm.cpp | 13 +-- lib/jni/jni/src/MediaDrm.h | 4 +- src/Iaes_decrypter.h | 3 - src/Session.cpp | 105 +++++++++--------- src/Session.h | 4 +- src/common/AdaptiveTree.h | 4 - src/common/AdaptiveTreeFactory.cpp | 71 ++++++------ src/common/AdaptiveTreeFactory.h | 14 ++- src/common/Representation.h | 6 +- src/decrypters/CMakeLists.txt | 4 + src/decrypters/DrmFactory.cpp | 20 +++- src/decrypters/IDecrypter.h | 8 +- src/decrypters/widevine/WVCdmAdapter.cpp | 12 +- src/decrypters/widevine/WVCdmAdapter.h | 4 +- .../widevine/WVCencSingleSampleDecrypter.cpp | 58 +++++----- .../widevine/WVCencSingleSampleDecrypter.h | 4 +- src/decrypters/widevine/WVDecrypter.cpp | 14 +-- src/decrypters/widevine/WVDecrypter.h | 8 +- .../widevineandroid/WVCdmAdapter.cpp | 30 ++--- src/decrypters/widevineandroid/WVCdmAdapter.h | 8 +- .../WVCencSingleSampleDecrypter.cpp | 49 ++++---- .../WVCencSingleSampleDecrypter.h | 2 +- .../widevineandroid/WVDecrypter.cpp | 22 ++-- src/decrypters/widevineandroid/WVDecrypter.h | 8 +- src/parser/SmoothTree.cpp | 2 +- src/test/TestUtils.cpp | 38 +++---- src/utils/CurlUtils.h | 1 - src/utils/Utils.cpp | 100 +++++++++-------- src/utils/Utils.h | 6 +- 30 files changed, 365 insertions(+), 291 deletions(-) diff --git a/lib/jni/jni/jutils/jutils-details.hpp b/lib/jni/jni/jutils/jutils-details.hpp index 2f5b257c5..be1b3d425 100644 --- a/lib/jni/jni/jutils/jutils-details.hpp +++ b/lib/jni/jni/jutils/jutils-details.hpp @@ -65,6 +65,31 @@ struct jcast_helper, jbyteArray> } }; +template<> +struct jcast_helper, jbyteArray> +{ + static std::vector cast(jbyteArray const& v) + { + JNIEnv* env = xbmc_jnienv(); + jsize size = 0; + std::vector vec; + if (v) + { + size = env->GetArrayLength(v); + + vec.reserve(size); + + jbyte* elements = env->GetByteArrayElements(v, NULL); + for (int i = 0; i < size; i++) + { + vec.emplace_back(static_cast(elements[i])); + } + env->ReleaseByteArrayElements(v, elements, JNI_ABORT); + } + return vec; + } +}; + template <> struct jcast_helper, jintArray> { @@ -229,6 +254,15 @@ struct jcast_helper, jhbyteArray> } }; +template<> +struct jcast_helper, jhbyteArray> +{ + static std::vector cast(jhbyteArray const& v) + { + return jcast_helper, jbyteArray>::cast(v.get()); + } +}; + template <> struct jcast_helper, jhintArray> { diff --git a/lib/jni/jni/src/MediaDrm.cpp b/lib/jni/jni/src/MediaDrm.cpp index fd34392d8..03499e29e 100644 --- a/lib/jni/jni/src/MediaDrm.cpp +++ b/lib/jni/jni/src/MediaDrm.cpp @@ -61,14 +61,14 @@ std::string CJNIMediaDrm::getPropertyString(const std::string &propertyName) con jcast(propertyName))); } -std::vector CJNIMediaDrm::getPropertyByteArray(const std::string &propertyName) const +std::vector CJNIMediaDrm::getPropertyByteArray(const std::string &propertyName) const { JNIEnv *env = xbmc_jnienv(); jhbyteArray array = call_method(m_object, "getPropertyByteArray", "(Ljava/lang/String;)[B", jcast(propertyName)); - std::vector result; + std::vector result; if (!env->ExceptionCheck()) { @@ -86,12 +86,11 @@ void CJNIMediaDrm::setPropertyString(const std::string &propertyName, const std: jcast(propertyName), jcast(value)); } -void CJNIMediaDrm::setPropertyByteArray(const std::string &propertyName, const std::vector &value) const +void CJNIMediaDrm::setPropertyByteArray(const std::string &propertyName, const std::vector &value) const { - JNIEnv *env = xbmc_jnienv(); - call_method(m_object, - "setPropertyByteArray", "(Ljava/lang/String;[B)V", - jcast(propertyName), jcast >(value)); + JNIEnv* env = xbmc_jnienv(); + call_method(m_object, "setPropertyByteArray", "(Ljava/lang/String;[B)V", + jcast(propertyName), jcast>(value)); } CJNIMediaDrmKeyRequest CJNIMediaDrm::getKeyRequest( diff --git a/lib/jni/jni/src/MediaDrm.h b/lib/jni/jni/src/MediaDrm.h index 76f285650..957742cde 100644 --- a/lib/jni/jni/src/MediaDrm.h +++ b/lib/jni/jni/src/MediaDrm.h @@ -42,9 +42,9 @@ class CJNIMediaDrm : public CJNIBase void closeSession(const std::vector & sessionId) const; std::string getPropertyString(const std::string &propertyName) const; - std::vector getPropertyByteArray(const std::string &propertyName) const; + std::vector getPropertyByteArray(const std::string& propertyName) const; void setPropertyString(const std::string &propertyName, const std::string &value) const; - void setPropertyByteArray(const std::string &propertyName, const std::vector &value) const; + void setPropertyByteArray(const std::string &propertyName, const std::vector &value) const; CJNIMediaDrmKeyRequest getKeyRequest(const std::vector &scope, const std::vector &init, const std::string &mimeType, int keyType, diff --git a/src/Iaes_decrypter.h b/src/Iaes_decrypter.h index 220238d8c..c1bd63452 100644 --- a/src/Iaes_decrypter.h +++ b/src/Iaes_decrypter.h @@ -30,7 +30,4 @@ class IAESDecrypter virtual void ivFromSequence(uint8_t* buffer, uint64_t sid) = 0; virtual const std::string& getLicenseKey() const = 0; virtual bool RenewLicense(const std::string& pluginUrl) = 0; - -private: - std::string m_licenseKey; }; diff --git a/src/Session.cpp b/src/Session.cpp index d2c8c4bb0..473856222 100644 --- a/src/Session.cpp +++ b/src/Session.cpp @@ -66,9 +66,7 @@ CSession::CSession(const PROPERTIES::KodiProperties& kodiProps, if (!kodiProps.m_serverCertificate.empty()) { - std::string decCert{BASE64::DecodeToStr(kodiProps.m_serverCertificate)}; - m_serverCertificate.SetData(reinterpret_cast(decCert.data()), - static_cast(decCert.size())); + m_serverCertificate = BASE64::Decode(kodiProps.m_serverCertificate); } } @@ -106,7 +104,7 @@ void CSession::SetSupportedDecrypterURN(std::string& key_system) return; } - key_system = m_decrypter->SelectKeySytem(m_kodiProps.m_licenseType.c_str()); + key_system = m_decrypter->SelectKeySytem(m_kodiProps.m_licenseType); m_decrypter->SetLibraryPath(kodi::vfs::TranslateSpecialProtocol(specialpath).c_str()); m_decrypter->SetProfilePath(m_profilePath); m_decrypter->SetDebugSaveLicense(kodi::addon::GetSettingBoolean("debug.save.license")); @@ -310,7 +308,7 @@ bool CSession::PreInitializeDRM(std::string& challengeB64, if (!m_decrypter->IsInitialised()) { - if (!m_decrypter->OpenDRMSystem(m_kodiProps.m_licenseKey.c_str(), m_serverCertificate, + if (!m_decrypter->OpenDRMSystem(m_kodiProps.m_licenseKey, m_serverCertificate, m_drmConfig)) { LOG::LogF(LOGERROR, "OpenDRMSystem failed"); @@ -318,12 +316,10 @@ bool CSession::PreInitializeDRM(std::string& challengeB64, } } - AP4_DataBuffer init_data; - init_data.SetBufferSize(1024); + std::vector initData; // Set the provided PSSH - std::vector decPsshData = BASE64::Decode(psshData); - init_data.SetData(decPsshData.data(), static_cast(decPsshData.size())); + initData = BASE64::Decode(psshData); // Decode the provided KID std::string decKid{BASE64::DecodeToStr(kidData)}; @@ -333,9 +329,9 @@ bool CSession::PreInitializeDRM(std::string& challengeB64, std::string hexKid{StringUtils::ToHexadecimal(decKid)}; LOG::LogF(LOGDEBUG, "Initializing session with KID: %s", hexKid.c_str()); - if (m_decrypter && init_data.GetDataSize() >= 4 && + if (m_decrypter && initData.size() >= 4 && (session.m_cencSingleSampleDecrypter = m_decrypter->CreateSingleSampleDecrypter( - init_data, "", decKid, true, CryptoMode::AES_CTR)) != 0) + initData, "", decKid, true, CryptoMode::AES_CTR)) != 0) { session.m_cdmSessionStr = session.m_cencSingleSampleDecrypter->GetSessionId(); sessionId = session.m_cdmSessionStr; @@ -363,8 +359,7 @@ bool CSession::InitializeDRM(bool addDefaultKID /* = false */) m_cdmSessions.resize(m_adaptiveTree->m_currentPeriod->GetPSSHSets().size()); // Try to initialize an SingleSampleDecryptor - if (m_adaptiveTree->m_currentPeriod->GetEncryptionState() != - EncryptionState::UNENCRYPTED) + if (m_adaptiveTree->m_currentPeriod->GetEncryptionState() != EncryptionState::UNENCRYPTED) { std::string licenseKey{m_kodiProps.m_licenseKey}; @@ -387,13 +382,14 @@ bool CSession::InitializeDRM(bool addDefaultKID /* = false */) if (!m_decrypter->IsInitialised()) { - if (!m_decrypter->OpenDRMSystem(licenseKey.c_str(), m_serverCertificate, m_drmConfig)) + if (!m_decrypter->OpenDRMSystem(licenseKey, m_serverCertificate, m_drmConfig)) { LOG::Log(LOGERROR, "OpenDRMSystem failed"); return false; } } - std::string keySystem = m_adaptiveTree->m_supportedKeySystem.substr(9); // Remove prefix "urn:uuid:" + std::string keySystem = + m_adaptiveTree->m_supportedKeySystem.substr(9); // Remove prefix "urn:uuid:" STRING::ReplaceAll(keySystem, "-", ""); if (keySystem.size() != 32) { @@ -404,7 +400,7 @@ bool CSession::InitializeDRM(bool addDefaultKID /* = false */) // cdmSession 0 is reserved for unencrypted streams for (size_t ses{1}; ses < m_cdmSessions.size(); ++ses) { - AP4_DataBuffer init_data; + std::vector initData; std::string drmOptionalKeyParam; CPeriod::PSSHSet& sessionPsshset = m_adaptiveTree->m_currentPeriod->GetPSSHSets()[ses]; @@ -428,9 +424,7 @@ bool CSession::InitializeDRM(bool addDefaultKID /* = false */) LOG::Log(LOGDEBUG, "License data: Create Widevine PSSH for SmoothStreaming, based on " "license data property"); } - std::vector init_data_v; - CreateISMlicense(sessionPsshset.defaultKID_, licenseData, init_data_v); - init_data.SetData(init_data_v.data(), static_cast(init_data_v.size())); + CreateISMlicense(sessionPsshset.defaultKID_, licenseData, initData); } else if (m_kodiProps.m_licenseType == "com.microsoft.playready") { @@ -453,22 +447,20 @@ bool CSession::InitializeDRM(bool addDefaultKID /* = false */) // This can allow to initialize a DRM that could be also not specified // as supported in the manifest (e.g. missing DASH ContentProtection tags) LOG::Log(LOGDEBUG, "License data: Use PSSH data provided by the license data property"); - std::vector licenseData = BASE64::Decode(m_kodiProps.m_licenseData); - init_data.SetData(licenseData.data(), static_cast(licenseData.size())); + initData = BASE64::Decode(m_kodiProps.m_licenseData); } - if (init_data.GetDataSize() == 0) + if (initData.size() == 0) { if (!sessionPsshset.pssh_.empty()) { // Use the PSSH provided by manifest - init_data.SetData(sessionPsshset.pssh_.data(), - static_cast(sessionPsshset.pssh_.size())); + initData = sessionPsshset.pssh_; } else { // Try extract the PSSH/KID from the stream - if (!ExtractStreamProtectionData(sessionPsshset, init_data, keySystem)) + if (!ExtractStreamProtectionData(sessionPsshset, initData, keySystem)) LOG::Log(LOGERROR, "License data: Cannot extract PSSH/KID data from the stream"); } } @@ -499,7 +491,6 @@ bool CSession::InitializeDRM(bool addDefaultKID /* = false */) break; } } - } else if (defaultKid.empty()) { @@ -518,10 +509,10 @@ bool CSession::InitializeDRM(bool addDefaultKID /* = false */) } } - if (m_decrypter && init_data.GetDataSize() >= 4 && + if (m_decrypter && initData.size() >= 4 && (session.m_cencSingleSampleDecrypter || (session.m_cencSingleSampleDecrypter = m_decrypter->CreateSingleSampleDecrypter( - init_data, drmOptionalKeyParam, defaultKid, false, + initData, drmOptionalKeyParam, defaultKid, false, sessionPsshset.m_cryptoMode == CryptoMode::NONE ? CryptoMode::AES_CTR : sessionPsshset.m_cryptoMode)) != 0)) @@ -533,14 +524,16 @@ bool CSession::InitializeDRM(bool addDefaultKID /* = false */) { m_adaptiveTree->m_currentPeriod->RemovePSSHSet(static_cast(ses)); } - else if (session.m_decrypterCaps.flags & DRM::IDecrypter::DecrypterCapabilites::SSD_SECURE_PATH) + else if (session.m_decrypterCaps.flags & + DRM::IDecrypter::DecrypterCapabilites::SSD_SECURE_PATH) { session.m_cdmSessionStr = session.m_cencSingleSampleDecrypter->GetSessionId(); isSecureVideoSession = true; if (m_settingNoSecureDecoder && !m_kodiProps.m_isLicenseForceSecureDecoder && !m_adaptiveTree->m_currentPeriod->IsSecureDecodeNeeded()) - session.m_decrypterCaps.flags &= ~DRM::IDecrypter::DecrypterCapabilites::SSD_SECURE_DECODER; + session.m_decrypterCaps.flags &= + ~DRM::IDecrypter::DecrypterCapabilites::SSD_SECURE_DECODER; } } else @@ -568,10 +561,10 @@ bool CSession::InitializePeriod(bool isSessionOpened /* = false */) bool isReusePssh{true}; if (m_adaptiveTree->m_nextPeriod) { - isPsshChanged = - !(m_adaptiveTree->m_currentPeriod->GetPSSHSets() == m_adaptiveTree->m_nextPeriod->GetPSSHSets()); + isPsshChanged = !(m_adaptiveTree->m_currentPeriod->GetPSSHSets() == + m_adaptiveTree->m_nextPeriod->GetPSSHSets()); isReusePssh = !isPsshChanged && m_adaptiveTree->m_nextPeriod->GetEncryptionState() == - EncryptionState::ENCRYPTED_SUPPORTED; + EncryptionState::ENCRYPTED_SUPPORTED; m_adaptiveTree->m_currentPeriod = m_adaptiveTree->m_nextPeriod; m_adaptiveTree->m_nextPeriod = nullptr; } @@ -736,8 +729,8 @@ void CSession::UpdateStream(CStream& stream) if (!rep->GetCodecPrivateData().empty()) { - std::string annexb; - const std::string* extraData(&annexb); + std::vector annexb; + const std::vector* extraData(&annexb); const DRM::IDecrypter::DecrypterCapabilites& caps{GetDecrypterCaps(rep->m_psshSetPos)}; @@ -751,8 +744,7 @@ void CSession::UpdateStream(CStream& stream) { extraData = &rep->GetCodecPrivateData(); } - stream.m_info.SetExtraData(reinterpret_cast(extraData->c_str()), - extraData->size()); + stream.m_info.SetExtraData(extraData->data(), extraData->size()); } stream.m_info.SetCodecFourCC(0); @@ -806,7 +798,8 @@ void CSession::UpdateStream(CStream& stream) stream.m_info.SetCodecFourCC(CODEC::MakeFourCC(CODEC::FOURCC_DVHE)); } else if (CODEC::Contains(codecs, CODEC::FOURCC_VP09, codecStr) || - CODEC::Contains(codecs, CODEC::NAME_VP9, codecStr)) // Some streams incorrectly use the name + CODEC::Contains(codecs, CODEC::NAME_VP9, + codecStr)) // Some streams incorrectly use the name { stream.m_info.SetCodecName(CODEC::NAME_VP9); if (STRING::Contains(codecStr, ".")) @@ -834,7 +827,8 @@ void CSession::UpdateStream(CStream& stream) } } else if (CODEC::Contains(codecs, CODEC::FOURCC_AV01, codecStr) || - CODEC::Contains(codecs, CODEC::NAME_AV1, codecStr)) // Some streams incorrectly use the name + CODEC::Contains(codecs, CODEC::NAME_AV1, + codecStr)) // Some streams incorrectly use the name stream.m_info.SetCodecName(CODEC::NAME_AV1); else { @@ -863,7 +857,8 @@ void CSession::UpdateStream(CStream& stream) } else if (CODEC::Contains(codecs, CODEC::FOURCC_OPUS, codecStr)) stream.m_info.SetCodecName(CODEC::NAME_OPUS); - else if (CODEC::Contains(codecs, CODEC::FOURCC_VORB, codecStr) || // Find "vorb" and "vorbis" case + else if (CODEC::Contains(codecs, CODEC::FOURCC_VORB, + codecStr) || // Find "vorb" and "vorbis" case CODEC::Contains(codecs, CODEC::FOURCC_VORB1, codecStr) || CODEC::Contains(codecs, CODEC::FOURCC_VORB1P, codecStr) || CODEC::Contains(codecs, CODEC::FOURCC_VORB2, codecStr) || @@ -881,7 +876,8 @@ void CSession::UpdateStream(CStream& stream) { if (CODEC::Contains(codecs, CODEC::FOURCC_TTML, codecStr) || CODEC::Contains(codecs, CODEC::FOURCC_STPP, codecStr)) - stream.m_info.SetCodecName(CODEC::NAME_SRT); // We convert it to SRT, Kodi dont support TTML yet + stream.m_info.SetCodecName( + CODEC::NAME_SRT); // We convert it to SRT, Kodi dont support TTML yet else if (CODEC::Contains(codecs, CODEC::FOURCC_WVTT, codecStr)) stream.m_info.SetCodecName(CODEC::NAME_WEBVTT); else @@ -1270,8 +1266,8 @@ void CSession::OnSegmentChangedRead(CStream* stream) adaptive::AdaptiveStream& adStream = stream->m_adStream; m_adaptiveTree->InsertLiveSegment(adStream.getPeriod(), adStream.getAdaptationSet(), - adStream.getRepresentation(), adStream.getSegmentPos(), - 0, duration, sr->GetTimeScale()); + adStream.getRepresentation(), adStream.getSegmentPos(), 0, + duration, sr->GetTimeScale()); } } } @@ -1344,7 +1340,7 @@ int CSession::GetChapter() const int CSession::GetChapterCount() const { if (m_adaptiveTree && m_adaptiveTree->m_periods.size() > 1) - return static_cast(m_adaptiveTree->m_periods.size()); + return static_cast(m_adaptiveTree->m_periods.size()); return 0; } @@ -1452,12 +1448,11 @@ AP4_Movie* CSession::CreateMovieAtom(CStream* stream) if (repr->GetContainerType() == ContainerType::MP4 && !repr->HasInitSegment()) { AP4_SampleDescription* sampleDesc; - const std::string& extradata = repr->GetCodecPrivateData(); + const std::vector& extradata = repr->GetCodecPrivateData(); if (stream->m_info.GetCodecName() == CODEC::NAME_H264) { - AP4_MemoryByteStream ms{reinterpret_cast(extradata.data()), - static_cast(extradata.size())}; + AP4_MemoryByteStream ms{extradata.data(), static_cast(extradata.size())}; AP4_AvccAtom* atom = AP4_AvccAtom::Create(static_cast(AP4_ATOM_HEADER_SIZE + extradata.size()), ms); sampleDesc = new AP4_AvcSampleDescription(AP4_SAMPLE_FORMAT_AVC1, stream->m_info.GetWidth(), @@ -1465,8 +1460,7 @@ AP4_Movie* CSession::CreateMovieAtom(CStream* stream) } else if (stream->m_info.GetCodecName() == CODEC::NAME_HEVC) { - AP4_MemoryByteStream ms{reinterpret_cast(extradata.data()), - static_cast(extradata.size())}; + AP4_MemoryByteStream ms{extradata.data(), static_cast(extradata.size())}; AP4_HvccAtom* atom = AP4_HvccAtom::Create(static_cast(AP4_ATOM_HEADER_SIZE + extradata.size()), ms); sampleDesc = new AP4_HevcSampleDescription(AP4_SAMPLE_FORMAT_HEV1, stream->m_info.GetWidth(), @@ -1474,8 +1468,7 @@ AP4_Movie* CSession::CreateMovieAtom(CStream* stream) } else if (stream->m_info.GetCodecName() == CODEC::NAME_AV1) { - AP4_MemoryByteStream ms{reinterpret_cast(extradata.data()), - static_cast(extradata.size())}; + AP4_MemoryByteStream ms{extradata.data(), static_cast(extradata.size())}; AP4_Av1cAtom* atom = AP4_Av1cAtom::Create(static_cast(AP4_ATOM_HEADER_SIZE + extradata.size()), ms); sampleDesc = new AP4_Av1SampleDescription(AP4_SAMPLE_FORMAT_AV01, stream->m_info.GetWidth(), @@ -1525,7 +1518,7 @@ AP4_Movie* CSession::CreateMovieAtom(CStream* stream) } bool CSession::ExtractStreamProtectionData(PLAYLIST::CPeriod::PSSHSet& sessionPsshset, - AP4_DataBuffer& init_data, + std::vector& initData, std::string keySystem) { keySystem = STRING::ToHexadecimal(keySystem); @@ -1547,11 +1540,13 @@ bool CSession::ExtractStreamProtectionData(PLAYLIST::CPeriod::PSSHSet& sessionPs } AP4_Array& pssh{movie->GetPsshAtoms()}; - for (unsigned int i = 0; init_data.GetDataSize() == 0 && i < pssh.ItemCount(); i++) + for (unsigned int i = 0; initData.size() == 0 && i < pssh.ItemCount(); i++) { if (std::memcmp(pssh[i].GetSystemId(), keySystem.c_str(), 16) == 0) { - init_data.AppendData(pssh[i].GetData().GetData(), pssh[i].GetData().GetDataSize()); + const AP4_DataBuffer& dataBuf = pssh[i].GetData(); + initData.insert(initData.end(), dataBuf.GetData(), dataBuf.GetData() + dataBuf.GetDataSize()); + if (sessionPsshset.defaultKID_.empty()) { if (pssh[i].GetKid(0)) @@ -1592,5 +1587,5 @@ bool CSession::ExtractStreamProtectionData(PLAYLIST::CPeriod::PSSHSet& sessionPs } stream.Disable(); - return init_data.GetDataSize() > 0; + return initData.size() > 0; } diff --git a/src/Session.h b/src/Session.h index 2ecbef296..d4b39fd03 100644 --- a/src/Session.h +++ b/src/Session.h @@ -366,14 +366,14 @@ class ATTR_DLL_LOCAL CSession : public adaptive::AdaptiveStreamObserver void DisposeDecrypter(); bool ExtractStreamProtectionData(PLAYLIST::CPeriod::PSSHSet& sessionPsshset, - AP4_DataBuffer& init_data, + std::vector& initData, std::string keySystem); private: const UTILS::PROPERTIES::KodiProperties m_kodiProps; std::string m_manifestUrl; std::string m_profilePath; - AP4_DataBuffer m_serverCertificate; + std::vector m_serverCertificate; std::unique_ptr m_dllHelper; DRM::IDecrypter* m_decrypter{nullptr}; diff --git a/src/common/AdaptiveTree.h b/src/common/AdaptiveTree.h index ff45c420f..a668fd344 100644 --- a/src/common/AdaptiveTree.h +++ b/src/common/AdaptiveTree.h @@ -37,10 +37,6 @@ namespace CHOOSER { class IRepresentationChooser; } -namespace PLAYLIST -{ -enum class TreeType; -} namespace adaptive { diff --git a/src/common/AdaptiveTreeFactory.cpp b/src/common/AdaptiveTreeFactory.cpp index eaae2cc71..cbb195f7c 100644 --- a/src/common/AdaptiveTreeFactory.cpp +++ b/src/common/AdaptiveTreeFactory.cpp @@ -15,6 +15,7 @@ #include "utils/StringUtils.h" #include "utils/log.h" +using namespace adaptive; using namespace PLAYLIST; using namespace UTILS; @@ -22,9 +23,11 @@ adaptive::AdaptiveTree* PLAYLIST_FACTORY::CreateAdaptiveTree( const UTILS::PROPERTIES::KodiProperties& kodiProps, const UTILS::CURL::HTTPResponse& manifestResp) { + TreeType type = TreeType::UNKNOWN; + // Add-on can override manifest type //! @todo: deprecated, to be removed on next Kodi release - PROPERTIES::ManifestType manifestType = kodiProps.m_manifestType; + PROPERTIES::ManifestType manifestTypeProp = kodiProps.m_manifestType; // Detect the manifest type if (kodiProps.m_manifestType == PROPERTIES::ManifestType::UNKNOWN) @@ -33,78 +36,84 @@ adaptive::AdaptiveTree* PLAYLIST_FACTORY::CreateAdaptiveTree( if (STRING::KeyExists(manifestResp.headers, "content-type")) contentType = manifestResp.headers.at("content-type"); - manifestType = InferManifestType(manifestResp.effectiveUrl, contentType, manifestResp.data); + type = InferManifestType(manifestResp.effectiveUrl, contentType, manifestResp.data); } - - if (manifestType == PROPERTIES::ManifestType::UNKNOWN) + else { - LOG::LogF(LOGERROR, - "Cannot detect the manifest type.\n" - "Check if the content-type header is correctly provided in the manifest response."); - return nullptr; + if (manifestTypeProp == PROPERTIES::ManifestType::MPD) + type = TreeType::DASH; + else if (manifestTypeProp == PROPERTIES::ManifestType::HLS) + type = TreeType::HLS; + else if (manifestTypeProp == PROPERTIES::ManifestType::ISM) + type = TreeType::SMOOTH_STREAMING; } - switch (manifestType) + switch (type) { - case PROPERTIES::ManifestType::MPD: - return new adaptive::CDashTree(); + case TreeType::DASH: + return new CDashTree(); + break; + case TreeType::HLS: + return new CHLSTree(); break; - case PROPERTIES::ManifestType::ISM: - return new adaptive::CSmoothTree(); + case TreeType::SMOOTH_STREAMING: + return new CSmoothTree(); break; - case PROPERTIES::ManifestType::HLS: - return new adaptive::CHLSTree(); + case TreeType::UNKNOWN: + LOG::LogF(LOGERROR, + "Cannot detect the manifest type.\n" + "Check if the content-type header is correctly provided in the manifest response."); break; default: - LOG::LogF(LOGFATAL, "Manifest type %i not handled", manifestType); + LOG::LogF(LOGFATAL, "Manifest type %i not handled", type); }; return nullptr; } -UTILS::PROPERTIES::ManifestType PLAYLIST_FACTORY::InferManifestType(std::string_view url, - std::string_view contentType, - std::string_view data) +adaptive::TreeType PLAYLIST_FACTORY::InferManifestType(std::string_view url, + std::string_view contentType, + std::string_view data) { // Try detect manifest type by using mime type specified by the server if (contentType == "application/dash+xml") - return PROPERTIES::ManifestType::MPD; + return TreeType::DASH; else if (contentType == "vnd.apple.mpegurl" || contentType == "application/vnd.apple.mpegurl" || contentType == "application/x-mpegURL") - return PROPERTIES::ManifestType::HLS; + return TreeType::HLS; else if (contentType == "application/vnd.ms-sstr+xml") - return PROPERTIES::ManifestType::ISM; + return TreeType::SMOOTH_STREAMING; // Try detect manifest type by checking file extension std::string ext = STRING::ToLower(FILESYS::GetFileExtension(url.data())); if (ext == "mpd") - return PROPERTIES::ManifestType::MPD; + return TreeType::DASH; else if (ext == "m3u8") - return PROPERTIES::ManifestType::HLS; + return TreeType::HLS; else if (ext == "ism/manifest" || ext == "isml/manifest" || ext == "ism" || ext == "isml") - return PROPERTIES::ManifestType::ISM; + return TreeType::SMOOTH_STREAMING; // Usually we could fall here if add-ons use a proxy to manipulate manifests without providing the // appropriate content-type header in the proxy HTTP response and by using also a custom address, // then as last resort we try detect the manifest type by parsing manifest data - if (!data.empty()) + if (data.size() > 1) { // Try check if we have the optional BOM for UTF16 BE / LE data // in this case always assumes as Smooth Streaming manifest if ((data[0] == '\xFE' && data[1] == '\xFF') || (data[0] == '\xFF' && data[1] == '\xFE')) { - return PROPERTIES::ManifestType::ISM; + return TreeType::SMOOTH_STREAMING; } // Since the data may be very large, limit the parsing to the first 200 characters std::string_view dataSnippet = data.substr(0, 200); if (dataSnippet.find("& GetCodecs() { return m_codecs; } // ISA custom attribute - std::string& GetCodecPrivateData() { return m_codecPrivateData; } - void SetCodecPrivateData(std::string_view codecPrivateData) + const std::vector& GetCodecPrivateData() const { return m_codecPrivateData; } + void SetCodecPrivateData(const std::vector& codecPrivateData) { m_codecPrivateData = codecPrivateData; } @@ -268,7 +268,7 @@ class ATTR_DLL_LOCAL CRepresentation : public CCommonSegAttribs, public CCommonA std::string m_baseUrl; std::set m_codecs; - std::string m_codecPrivateData; + std::vector m_codecPrivateData; uint32_t m_bandwidth{0}; // as bit/s diff --git a/src/decrypters/CMakeLists.txt b/src/decrypters/CMakeLists.txt index fbd013a59..d85fffeed 100644 --- a/src/decrypters/CMakeLists.txt +++ b/src/decrypters/CMakeLists.txt @@ -16,3 +16,7 @@ if(NOT CORE_SYSTEM_NAME STREQUAL ios AND NOT CORE_SYSTEM_NAME STREQUAL darwin_em add_subdirectory(widevine) endif() endif() + +if(MSVC) + add_subdirectory(mediafoundation) +endif() \ No newline at end of file diff --git a/src/decrypters/DrmFactory.cpp b/src/decrypters/DrmFactory.cpp index 7ee3b2388..f5227498c 100644 --- a/src/decrypters/DrmFactory.cpp +++ b/src/decrypters/DrmFactory.cpp @@ -8,6 +8,8 @@ #include "DrmFactory.h" +#include "utils/log.h" + #include #if ANDROID #include "widevineandroid/WVDecrypter.h" @@ -17,6 +19,13 @@ #endif #endif +#if _WIN32 +#include +#if WDK_NTDDI_VERSION >= NTDDI_WIN10_VB // Windows SDK higher than Windows 20H2 +#include "mediafoundation/MFDecrypter.h" +#endif +#endif + using namespace DRM; IDecrypter* DRM::FACTORY::GetDecrypter(STREAM_CRYPTO_KEY_SYSTEM keySystem) @@ -32,8 +41,15 @@ IDecrypter* DRM::FACTORY::GetDecrypter(STREAM_CRYPTO_KEY_SYSTEM keySystem) #endif #endif } - else if (keySystem == STREAM_CRYPTO_KEY_SYSTEM_PLAYREADY || - keySystem == STREAM_CRYPTO_KEY_SYSTEM_WISEPLAY) + else if (keySystem == STREAM_CRYPTO_KEY_SYSTEM_PLAYREADY) + { +#if ANDROID + return new CWVDecrypterA(); +#elif _WIN32 + return new CMFDecrypter(); +#endif + } + else if (keySystem == STREAM_CRYPTO_KEY_SYSTEM_WISEPLAY) { #if ANDROID return new CWVDecrypterA(); diff --git a/src/decrypters/IDecrypter.h b/src/decrypters/IDecrypter.h index a65011e0e..b8e94aaf6 100644 --- a/src/decrypters/IDecrypter.h +++ b/src/decrypters/IDecrypter.h @@ -62,7 +62,7 @@ class IDecrypter * \param keySystem The URN to be matched * \return Supported URN if type matches to capabilities, otherwise null */ - virtual const char* SelectKeySytem(const char* keySystem) = 0; + virtual std::string SelectKeySytem(std::string_view keySystem) = 0; /** * \brief Initialise the DRM system @@ -71,8 +71,8 @@ class IDecrypter * \param config Flags to be passed to the decrypter * \return true on success */ - virtual bool OpenDRMSystem(const char* licenseURL, - const AP4_DataBuffer& serverCertificate, + virtual bool OpenDRMSystem(std::string_view licenseURL, + const std::vector& serverCertificate, const uint8_t config) = 0; /** @@ -85,7 +85,7 @@ class IDecrypter * \return The single sample decrypter if successfully created */ virtual Adaptive_CencSingleSampleDecrypter* CreateSingleSampleDecrypter( - AP4_DataBuffer& pssh, + std::vector& pssh, std::string_view optionalKeyParameter, std::string_view defaultKeyId, bool skipSessionMessage, diff --git a/src/decrypters/widevine/WVCdmAdapter.cpp b/src/decrypters/widevine/WVCdmAdapter.cpp index dd7f40053..2236bc13a 100644 --- a/src/decrypters/widevine/WVCdmAdapter.cpp +++ b/src/decrypters/widevine/WVCdmAdapter.cpp @@ -25,10 +25,10 @@ namespace #endif } // unnamed namespace -CWVCdmAdapter::CWVCdmAdapter(const char* licenseURL, - const AP4_DataBuffer& serverCert, - const uint8_t config, - CWVDecrypter* host) +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) { std::string strLibPath = m_host->GetLibraryPath(); @@ -77,8 +77,8 @@ CWVCdmAdapter::CWVCdmAdapter(const char* licenseURL, return; } - if (serverCert.GetDataSize()) - wv_adapter->SetServerCertificate(0, serverCert.GetData(), serverCert.GetDataSize()); + if (!serverCert.empty()) + wv_adapter->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) diff --git a/src/decrypters/widevine/WVCdmAdapter.h b/src/decrypters/widevine/WVCdmAdapter.h index 605d1d862..58ed28130 100644 --- a/src/decrypters/widevine/WVCdmAdapter.h +++ b/src/decrypters/widevine/WVCdmAdapter.h @@ -20,8 +20,8 @@ class CWVCencSingleSampleDecrypter; class ATTR_DLL_LOCAL CWVCdmAdapter : public media::CdmAdapterClient { public: - CWVCdmAdapter(const char* licenseURL, - const AP4_DataBuffer& serverCert, + CWVCdmAdapter(std::string_view licenseURL, + const std::vector& serverCert, const uint8_t config, CWVDecrypter* host); virtual ~CWVCdmAdapter(); diff --git a/src/decrypters/widevine/WVCencSingleSampleDecrypter.cpp b/src/decrypters/widevine/WVCencSingleSampleDecrypter.cpp index 9a95838a0..1e8ae5958 100644 --- a/src/decrypters/widevine/WVCencSingleSampleDecrypter.cpp +++ b/src/decrypters/widevine/WVCencSingleSampleDecrypter.cpp @@ -41,7 +41,7 @@ void CWVCencSingleSampleDecrypter::SetSession(const char* session, } CWVCencSingleSampleDecrypter::CWVCencSingleSampleDecrypter(CWVCdmAdapter& drm, - AP4_DataBuffer& pssh, + std::vector& pssh, std::string_view defaultKeyId, bool skipSessionMessage, CryptoMode cryptoMode, @@ -59,10 +59,10 @@ CWVCencSingleSampleDecrypter::CWVCencSingleSampleDecrypter(CWVCdmAdapter& drm, { SetParentIsOwner(false); - if (pssh.GetDataSize() > 4096) + if (pssh.size() > 4096) { - LOG::LogF(LOGERROR, "PSSH init data with length %u seems not to be cenc init data", - pssh.GetDataSize()); + LOG::LogF(LOGERROR, "PSSH init data with length %zu seems not to be cenc init data", + pssh.size()); return; } @@ -73,35 +73,42 @@ CWVCencSingleSampleDecrypter::CWVCencSingleSampleDecrypter(CWVCdmAdapter& drm, std::string debugFilePath = FILESYS::PathCombine(m_host->GetProfilePath(), "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED.init"); - std::string data{reinterpret_cast(pssh.GetData()), pssh.GetDataSize()}; + std::string data{reinterpret_cast(pssh.data()), pssh.size()}; UTILS::FILESYS::SaveFile(debugFilePath, data, true); } - if (memcmp(pssh.GetData() + 4, "pssh", 4) != 0) + // No cenc init data with PSSH box format, create one + if (memcmp(pssh.data() + 4, "pssh", 4) != 0) { - unsigned int buf_size = 32 + pssh.GetDataSize(); - uint8_t buf[4096 + 32]; - // This will request a new session and initializes session_id and message members in cdm_adapter. // message will be used to create a license request in the step after CreateSession call. // Initialization data is the widevine cdm pssh code in google proto style found in mpd schemeIdUri - static uint8_t proto[] = {0x00, 0x00, 0x00, 0x63, 0x70, 0x73, 0x73, 0x68, 0x00, 0x00, 0x00, - 0x00, 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8, - 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, 0x00, 0x00, 0x00, 0x00}; - - proto[2] = static_cast((buf_size >> 8) & 0xFF); - proto[3] = static_cast(buf_size & 0xFF); - proto[30] = static_cast((pssh.GetDataSize() >> 8) & 0xFF); - proto[31] = static_cast(pssh.GetDataSize()); - - memcpy(buf, proto, sizeof(proto)); - memcpy(&buf[32], pssh.GetData(), pssh.GetDataSize()); - m_pssh.SetData(buf, buf_size); + + // PSSH box version 0 (no kid's) + static const uint8_t atomHeader[12] = {0x00, 0x00, 0x00, 0x00, 0x70, 0x73, + 0x73, 0x68, 0x00, 0x00, 0x00, 0x00}; + + static const uint8_t widevineSystemId[16] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, + 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed}; + + std::vector psshAtom; + psshAtom.assign(atomHeader, atomHeader + 12); // PSSH Box header + psshAtom.insert(psshAtom.end(), widevineSystemId, widevineSystemId + 16); // System ID + // Add data size bytes + psshAtom.resize(30, 0); // 2 zero bytes + psshAtom.emplace_back(static_cast((pssh.size()) >> 8)); + psshAtom.emplace_back(static_cast(pssh.size())); + + psshAtom.insert(psshAtom.end(), pssh.begin(), pssh.end()); // Data + // Update box size + psshAtom[2] = static_cast(psshAtom.size() >> 8); + psshAtom[3] = static_cast(psshAtom.size()); + m_pssh = psshAtom; } - drm.GetCdmAdapter()->CreateSessionAndGenerateRequest( - m_promiseId++, cdm::SessionType::kTemporary, cdm::InitDataType::kCenc, - reinterpret_cast(m_pssh.GetData()), m_pssh.GetDataSize()); + drm.GetCdmAdapter()->CreateSessionAndGenerateRequest(m_promiseId++, cdm::SessionType::kTemporary, + cdm::InitDataType::kCenc, m_pssh.data(), + static_cast(m_pssh.size())); int retrycount = 0; while (m_strSession.empty() && ++retrycount < 100) @@ -455,7 +462,8 @@ bool CWVCencSingleSampleDecrypter::SendSessionMessage() serverCertRequest = m_challenge.GetDataSize() == 2; m_challenge.SetDataSize(0); - if (!file.Open()) + int statusCode = file.Open(); + if (statusCode == -1 || statusCode >= 400) { LOG::Log(LOGERROR, "License server returned failure"); return false; diff --git a/src/decrypters/widevine/WVCencSingleSampleDecrypter.h b/src/decrypters/widevine/WVCencSingleSampleDecrypter.h index 044660d98..8f98b7d3e 100644 --- a/src/decrypters/widevine/WVCencSingleSampleDecrypter.h +++ b/src/decrypters/widevine/WVCencSingleSampleDecrypter.h @@ -31,7 +31,7 @@ class ATTR_DLL_LOCAL CWVCencSingleSampleDecrypter : public Adaptive_CencSingleSa public: // methods CWVCencSingleSampleDecrypter(CWVCdmAdapter& drm, - AP4_DataBuffer& pssh, + std::vector& pssh, std::string_view defaultKeyId, bool skipSessionMessage, CryptoMode cryptoMode, @@ -91,7 +91,7 @@ class ATTR_DLL_LOCAL CWVCencSingleSampleDecrypter : public Adaptive_CencSingleSa CWVCdmAdapter& m_wvCdmAdapter; std::string m_strSession; - AP4_DataBuffer m_pssh; + std::vector m_pssh; AP4_DataBuffer m_challenge; std::string m_defaultKeyId; struct WVSKEY diff --git a/src/decrypters/widevine/WVDecrypter.cpp b/src/decrypters/widevine/WVDecrypter.cpp index f21b3522a..d0414beba 100644 --- a/src/decrypters/widevine/WVDecrypter.cpp +++ b/src/decrypters/widevine/WVDecrypter.cpp @@ -76,16 +76,16 @@ bool CWVDecrypter::Initialize() return true; } -const char* CWVDecrypter::SelectKeySytem(const char* keySystem) +std::string CWVDecrypter::SelectKeySytem(std::string_view keySystem) { - if (strcmp(keySystem, "com.widevine.alpha")) - return nullptr; + if (keySystem == "com.widevine.alpha") + return "urn:uuid:EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED"; - return "urn:uuid:EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED"; + return ""; } -bool CWVDecrypter::OpenDRMSystem(const char* licenseURL, - const AP4_DataBuffer& serverCertificate, +bool CWVDecrypter::OpenDRMSystem(std::string_view licenseURL, + const std::vector& serverCertificate, const uint8_t config) { m_WVCdmAdapter = new CWVCdmAdapter(licenseURL, serverCertificate, config, this); @@ -94,7 +94,7 @@ bool CWVDecrypter::OpenDRMSystem(const char* licenseURL, } Adaptive_CencSingleSampleDecrypter* CWVDecrypter::CreateSingleSampleDecrypter( - AP4_DataBuffer& pssh, + std::vector& pssh, std::string_view optionalKeyParameter, std::string_view defaultKeyId, bool skipSessionMessage, diff --git a/src/decrypters/widevine/WVDecrypter.h b/src/decrypters/widevine/WVDecrypter.h index cab775a47..95b7d28ba 100644 --- a/src/decrypters/widevine/WVDecrypter.h +++ b/src/decrypters/widevine/WVDecrypter.h @@ -27,12 +27,12 @@ class ATTR_DLL_LOCAL CWVDecrypter : public IDecrypter virtual bool Initialize() override; - virtual const char* SelectKeySytem(const char* keySystem) override; - virtual bool OpenDRMSystem(const char* licenseURL, - const AP4_DataBuffer& serverCertificate, + virtual std::string SelectKeySytem(std::string_view keySystem) override; + virtual bool OpenDRMSystem(std::string_view licenseURL, + const std::vector& serverCertificate, const uint8_t config) override; virtual Adaptive_CencSingleSampleDecrypter* CreateSingleSampleDecrypter( - AP4_DataBuffer& pssh, + std::vector& pssh, std::string_view optionalKeyParameter, std::string_view defaultKeyId, bool skipSessionMessage, diff --git a/src/decrypters/widevineandroid/WVCdmAdapter.cpp b/src/decrypters/widevineandroid/WVCdmAdapter.cpp index 02540e86f..25d781978 100644 --- a/src/decrypters/widevineandroid/WVCdmAdapter.cpp +++ b/src/decrypters/widevineandroid/WVCdmAdapter.cpp @@ -18,10 +18,10 @@ using namespace DRM; using namespace jni; CWVCdmAdapterA::CWVCdmAdapterA(WV_KEYSYSTEM ks, - const char* licenseURL, - const AP4_DataBuffer& serverCert, - CJNIMediaDrmOnEventListener* listener, - CWVDecrypterA* host) + std::string_view licenseURL, + const std::vector& serverCert, + CJNIMediaDrmOnEventListener* listener, + CWVDecrypterA* host) : m_keySystem(ks), m_mediaDrm(0), m_licenseUrl(licenseURL), m_host(host) { std::string strBasePath = m_host->GetProfilePath(); @@ -79,7 +79,7 @@ CWVCdmAdapterA::CWVCdmAdapterA(WV_KEYSYSTEM ks, return; } - std::vector strDeviceId = m_mediaDrm->getPropertyByteArray("deviceUniqueId"); + std::vector strDeviceId = m_mediaDrm->getPropertyByteArray("deviceUniqueId"); xbmc_jnienv()->ExceptionClear(); std::string strSecurityLevel = m_mediaDrm->getPropertyString("securityLevel"); xbmc_jnienv()->ExceptionClear(); @@ -90,10 +90,10 @@ CWVCdmAdapterA::CWVCdmAdapterA(WV_KEYSYSTEM ks, if (m_keySystem == WIDEVINE) { //m_mediaDrm->setPropertyString("sessionSharing", "enable"); - if (serverCert.GetDataSize()) - m_mediaDrm->setPropertyByteArray( - "serviceCertificate", - std::vector(serverCert.GetData(), serverCert.GetData() + serverCert.GetDataSize())); + if (!serverCert.empty()) + { + m_mediaDrm->setPropertyByteArray("serviceCertificate", serverCert); + } else LoadServiceCertificate(); @@ -108,7 +108,7 @@ CWVCdmAdapterA::CWVCdmAdapterA(WV_KEYSYSTEM ks, } LOG::Log(LOGDEBUG, - "MediaDrm initialized (Device unique ID size: %ld, System ID: %s, Security level: %s)", + "MediaDrm initialized (Device unique ID size: %zu, System ID: %s, Security level: %s)", strDeviceId.size(), strSystemId.c_str(), strSecurityLevel.c_str()); if (m_licenseUrl.find('|') == std::string::npos) @@ -141,7 +141,7 @@ CWVCdmAdapterA::~CWVCdmAdapterA() void CWVCdmAdapterA::LoadServiceCertificate() { std::string filename = m_strBasePath + "service_certificate"; - char* data(nullptr); + uint8_t* data(nullptr); size_t sz(0); FILE* f = fopen(filename.c_str(), "rb"); @@ -150,7 +150,7 @@ void CWVCdmAdapterA::LoadServiceCertificate() fseek(f, 0L, SEEK_END); sz = ftell(f); fseek(f, 0L, SEEK_SET); - if (sz > 8 && (data = (char*)malloc(sz))) + if (sz > 8 && (data = (uint8_t*)malloc(sz))) fread(data, 1, sz, f); fclose(f); } @@ -163,7 +163,7 @@ void CWVCdmAdapterA::LoadServiceCertificate() if (certTime < nowTime && nowTime - certTime < 86400) m_mediaDrm->setPropertyByteArray("serviceCertificate", - std::vector(data + 8, data + sz)); + std::vector(data + 8, data + sz)); else free(data), data = nullptr; } @@ -181,7 +181,7 @@ void CWVCdmAdapterA::LoadServiceCertificate() void CWVCdmAdapterA::SaveServiceCertificate() { - std::vector sc = m_mediaDrm->getPropertyByteArray("serviceCertificate"); + const std::vector sc = m_mediaDrm->getPropertyByteArray("serviceCertificate"); if (xbmc_jnienv()->ExceptionCheck()) { LOG::LogF(LOGWARNING, "Exception retrieving Service Certificate"); @@ -202,7 +202,7 @@ void CWVCdmAdapterA::SaveServiceCertificate() auto now = std::chrono::system_clock::now(); uint64_t nowTime = std::chrono::time_point_cast(now).time_since_epoch().count(); - fwrite((char*)&nowTime, 1, sizeof(uint64_t), f); + fwrite((uint8_t*)&nowTime, 1, sizeof(uint64_t), f); fwrite(sc.data(), 1, sc.size(), f); fclose(f); } diff --git a/src/decrypters/widevineandroid/WVCdmAdapter.h b/src/decrypters/widevineandroid/WVCdmAdapter.h index 110720cdf..11839c410 100644 --- a/src/decrypters/widevineandroid/WVCdmAdapter.h +++ b/src/decrypters/widevineandroid/WVCdmAdapter.h @@ -26,10 +26,10 @@ class ATTR_DLL_LOCAL CWVCdmAdapterA { public: CWVCdmAdapterA(WV_KEYSYSTEM ks, - const char* licenseURL, - const AP4_DataBuffer& serverCert, - jni::CJNIMediaDrmOnEventListener* listener, - CWVDecrypterA* host); + std::string_view licenseURL, + const std::vector& serverCert, + jni::CJNIMediaDrmOnEventListener* listener, + CWVDecrypterA* host); ~CWVCdmAdapterA(); jni::CJNIMediaDrm* GetMediaDrm() { return m_mediaDrm; }; diff --git a/src/decrypters/widevineandroid/WVCencSingleSampleDecrypter.cpp b/src/decrypters/widevineandroid/WVCencSingleSampleDecrypter.cpp index 9a901fb9c..ac68fdd3b 100644 --- a/src/decrypters/widevineandroid/WVCencSingleSampleDecrypter.cpp +++ b/src/decrypters/widevineandroid/WVCencSingleSampleDecrypter.cpp @@ -25,7 +25,7 @@ using namespace UTILS; using namespace kodi::tools; CWVCencSingleSampleDecrypterA::CWVCencSingleSampleDecrypterA(CWVCdmAdapterA& drm, - AP4_DataBuffer& pssh, + std::vector& pssh, std::string_view optionalKeyParameter, std::string_view defaultKeyId, CWVDecrypterA* host) @@ -39,10 +39,10 @@ CWVCencSingleSampleDecrypterA::CWVCencSingleSampleDecrypterA(CWVCdmAdapterA& drm { SetParentIsOwner(false); - if (pssh.GetDataSize() > 65535) + if (pssh.size() > 65535) { - LOG::LogF(LOGERROR, "PSSH init data with length %u seems not to be cenc init data", - pssh.GetDataSize()); + LOG::LogF(LOGERROR, "PSSH init data with length %zu seems not to be cenc init data", + pssh.size()); return; } @@ -50,28 +50,31 @@ CWVCencSingleSampleDecrypterA::CWVCencSingleSampleDecrypterA(CWVCdmAdapterA& drm { std::string debugFilePath = FILESYS::PathCombine(m_host->GetProfilePath(), "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED.init"); - std::string data{reinterpret_cast(pssh.GetData()), pssh.GetDataSize()}; + std::string data{reinterpret_cast(pssh.data()), pssh.size()}; FILESYS::SaveFile(debugFilePath, data, true); } - m_pssh.assign(pssh.GetData(), pssh.GetData() + pssh.GetDataSize()); - - if (memcmp(pssh.GetData() + 4, "pssh", 4) != 0) + m_pssh = pssh; + // No cenc init data with PSSH box format, create one + if (memcmp(pssh.data() + 4, "pssh", 4) != 0) { - const uint8_t atomHeader[] = {0x00, 0x00, 0x00, 0x00, 0x70, 0x73, - 0x73, 0x68, 0x00, 0x00, 0x00, 0x00}; - uint8_t atom[32]; - memcpy(atom, atomHeader, 12); - memcpy(atom + 12, m_mediaDrm.GetKeySystem(), 16); - memset(atom + 28, 0, 4); - - m_pssh.insert(m_pssh.begin(), atom, atom + sizeof(atom)); + // PSSH box version 0 (no kid's) + static const uint8_t atomHeader[12] = {0x00, 0x00, 0x00, 0x00, 0x70, 0x73, + 0x73, 0x68, 0x00, 0x00, 0x00, 0x00}; - m_pssh[3] = static_cast(m_pssh.size()); - m_pssh[2] = static_cast(m_pssh.size() >> 8); + std::vector psshAtom; + psshAtom.assign(atomHeader, atomHeader + 12); // PSSH Box header + psshAtom.insert(psshAtom.end(), m_mediaDrm.GetKeySystem(), m_mediaDrm.GetKeySystem() + 16); // System ID + // Add data size bytes + psshAtom.resize(30, 0); // 2 zero bytes + psshAtom.emplace_back(static_cast((m_pssh.size()) >> 8)); + psshAtom.emplace_back(static_cast(m_pssh.size())); - m_pssh[sizeof(atom) - 1] = static_cast(m_pssh.size()) - sizeof(atom); - m_pssh[sizeof(atom) - 2] = static_cast((m_pssh.size() - sizeof(atom)) >> 8); + psshAtom.insert(psshAtom.end(), m_pssh.begin(), m_pssh.end()); // Data + // Update box size + psshAtom[2] = static_cast(psshAtom.size() >> 8); + psshAtom[3] = static_cast(psshAtom.size()); + m_pssh = psshAtom; } m_initialPssh = m_pssh; @@ -222,7 +225,8 @@ bool CWVCencSingleSampleDecrypterA::ProvisionRequest() file.AddHeader("Content-Type", "application/json"); file.AddHeader("postdata", reqData.c_str()); - if (!file.Open()) + int statusCode = file.Open(); + if (statusCode == -1 || statusCode >= 400) { LOG::Log(LOGERROR, "Provisioning server returned failure"); return false; @@ -542,7 +546,8 @@ bool CWVCencSingleSampleDecrypterA::SendSessionMessage(const std::vector& file.AddHeader("postdata", encData.c_str()); } - if (!file.Open()) + int statusCode = file.Open(); + if (statusCode == -1 || statusCode >= 400) { LOG::Log(LOGERROR, "License server returned failure"); return false; diff --git a/src/decrypters/widevineandroid/WVCencSingleSampleDecrypter.h b/src/decrypters/widevineandroid/WVCencSingleSampleDecrypter.h index 2ac29b9d0..6c367408f 100644 --- a/src/decrypters/widevineandroid/WVCencSingleSampleDecrypter.h +++ b/src/decrypters/widevineandroid/WVCencSingleSampleDecrypter.h @@ -23,7 +23,7 @@ class ATTR_DLL_LOCAL CWVCencSingleSampleDecrypterA : public Adaptive_CencSingleS public: // methods CWVCencSingleSampleDecrypterA(CWVCdmAdapterA& drm, - AP4_DataBuffer& pssh, + std::vector& pssh, std::string_view optionalKeyParameter, std::string_view defaultKeyId, CWVDecrypterA* host); diff --git a/src/decrypters/widevineandroid/WVDecrypter.cpp b/src/decrypters/widevineandroid/WVDecrypter.cpp index 7c6935bb1..0d3a4e78e 100644 --- a/src/decrypters/widevineandroid/WVDecrypter.cpp +++ b/src/decrypters/widevineandroid/WVDecrypter.cpp @@ -104,31 +104,31 @@ void CWVDecrypterA::SetProfilePath(const std::string& profilePath) kodi::vfs::CreateDirectory(m_strProfilePath.c_str()); } -const char* CWVDecrypterA::SelectKeySytem(const char* keySystem) +std::string CWVDecrypterA::SelectKeySytem(std::string_view keySystem) { - LOG::Log(LOGDEBUG, "Key system request: %s", keySystem); - if (strcmp(keySystem, "com.widevine.alpha") == 0) + LOG::Log(LOGDEBUG, "Key system request: %s", keySystem.data()); + if (keySystem == "com.widevine.alpha") { m_keySystem = WIDEVINE; return "urn:uuid:EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED"; } - else if (strcmp(keySystem, "com.huawei.wiseplay") == 0) + else if (keySystem == "com.huawei.wiseplay") { m_keySystem = WISEPLAY; return "urn:uuid:3D5E6D35-9B9A-41E8-B843-DD3C6E72C42C"; } - else if (strcmp(keySystem, "com.microsoft.playready") == 0) + else if (keySystem == "com.microsoft.playready") { m_keySystem = PLAYREADY; return "urn:uuid:9A04F079-9840-4286-AB92-E65BE0885F95"; } - else - return nullptr; + + return ""; } -bool CWVDecrypterA::OpenDRMSystem(const char* licenseURL, - const AP4_DataBuffer& serverCertificate, - const uint8_t config) +bool CWVDecrypterA::OpenDRMSystem(std::string_view licenseURL, + const std::vector& serverCertificate, + const uint8_t config) { if (m_keySystem == NONE) return false; @@ -140,7 +140,7 @@ bool CWVDecrypterA::OpenDRMSystem(const char* licenseURL, } Adaptive_CencSingleSampleDecrypter* CWVDecrypterA::CreateSingleSampleDecrypter( - AP4_DataBuffer& pssh, + std::vector& pssh, std::string_view optionalKeyParameter, std::string_view defaultKeyId, bool skipSessionMessage, diff --git a/src/decrypters/widevineandroid/WVDecrypter.h b/src/decrypters/widevineandroid/WVDecrypter.h index 52a546c55..6b417f6aa 100644 --- a/src/decrypters/widevineandroid/WVDecrypter.h +++ b/src/decrypters/widevineandroid/WVDecrypter.h @@ -81,14 +81,14 @@ class ATTR_DLL_LOCAL CWVDecrypterA : public IDecrypter, public CMediaDrmOnEventC } #endif - virtual const char* SelectKeySytem(const char* keySystem) override; + virtual std::string SelectKeySytem(std::string_view keySystem) override; - virtual bool OpenDRMSystem(const char* licenseURL, - const AP4_DataBuffer& serverCertificate, + virtual bool OpenDRMSystem(std::string_view licenseURL, + const std::vector& serverCertificate, const uint8_t config) override; virtual Adaptive_CencSingleSampleDecrypter* CreateSingleSampleDecrypter( - AP4_DataBuffer& pssh, + std::vector& pssh, std::string_view optionalKeyParameter, std::string_view defaultKeyId, bool skipSessionMessage, diff --git a/src/parser/SmoothTree.cpp b/src/parser/SmoothTree.cpp index ac09e51b9..d64b6757a 100644 --- a/src/parser/SmoothTree.cpp +++ b/src/parser/SmoothTree.cpp @@ -356,7 +356,7 @@ void adaptive::CSmoothTree::ParseTagQualityLevel(pugi::xml_node nodeQI, esds |= (sidx << 7); - std::string codecPrivateData; + std::vector codecPrivateData; codecPrivateData.resize(2); codecPrivateData[0] = esds >> 8; codecPrivateData[1] = esds & 0xFF; diff --git a/src/test/TestUtils.cpp b/src/test/TestUtils.cpp index 9580e9770..a56f5675f 100644 --- a/src/test/TestUtils.cpp +++ b/src/test/TestUtils.cpp @@ -144,57 +144,57 @@ TEST_F(UtilsTest, JoinUrls) TEST_F(UtilsTest, AdaptiveTreeFactory_DASH) { - PROPERTIES::ManifestType type; + adaptive::TreeType type; std::string testDataRegular; testHelper::LoadFile("mpd/treefactory_test_regular.mpd", testDataRegular); // just uncommon url (e.g. proxy) must fails type = InferManifestType("localhost/proxy/getmanifest", "", ""); - EXPECT_EQ(type, PROPERTIES::ManifestType::UNKNOWN); + EXPECT_EQ(type, adaptive::TreeType::UNKNOWN); // test data parsing type = InferManifestType("localhost/proxy/getmanifest", "", testDataRegular); - EXPECT_EQ(type, PROPERTIES::ManifestType::MPD); + EXPECT_EQ(type, adaptive::TreeType::DASH); // test header type = InferManifestType("localhost/proxy/getmanifest", "application/dash+xml", ""); - EXPECT_EQ(type, PROPERTIES::ManifestType::MPD); + EXPECT_EQ(type, adaptive::TreeType::DASH); // test url type = InferManifestType("http://www.someservice.com/cdm1/manifest.mpd", "", ""); - EXPECT_EQ(type, PROPERTIES::ManifestType::MPD); + EXPECT_EQ(type, adaptive::TreeType::DASH); } TEST_F(UtilsTest, AdaptiveTreeFactory_HLS) { - PROPERTIES::ManifestType type; + adaptive::TreeType type; std::string testDataRegular; testHelper::LoadFile("hls/treefactory_test_regular.m3u8", testDataRegular); // test data parsing type = InferManifestType("localhost/proxy/getmanifest", "", testDataRegular); - EXPECT_EQ(type, PROPERTIES::ManifestType::HLS); + EXPECT_EQ(type, adaptive::TreeType::HLS); // test header type = InferManifestType("localhost/proxy/getmanifest", "vnd.apple.mpegurl", ""); - EXPECT_EQ(type, PROPERTIES::ManifestType::HLS); + EXPECT_EQ(type, adaptive::TreeType::HLS); // test header type = InferManifestType("localhost/proxy/getmanifest", "application/vnd.apple.mpegurl", ""); - EXPECT_EQ(type, PROPERTIES::ManifestType::HLS); + EXPECT_EQ(type, adaptive::TreeType::HLS); // test header type = InferManifestType("localhost/proxy/getmanifest", "application/x-mpegURL", ""); - EXPECT_EQ(type, PROPERTIES::ManifestType::HLS); + EXPECT_EQ(type, adaptive::TreeType::HLS); // test url type = InferManifestType("http://www.someservice.com/cdm1/manifest.m3u8", "", ""); - EXPECT_EQ(type, PROPERTIES::ManifestType::HLS); + EXPECT_EQ(type, adaptive::TreeType::HLS); } TEST_F(UtilsTest, AdaptiveTreeFactory_ISM) { - PROPERTIES::ManifestType type; + adaptive::TreeType type; std::string testDataUTF8; std::string testDataUTF16leBOM; testHelper::LoadFile("ism/treefactory_test_utf8.ism", testDataUTF8); @@ -202,31 +202,31 @@ TEST_F(UtilsTest, AdaptiveTreeFactory_ISM) // test UTF8 data parsing type = InferManifestType("localhost/proxy/getmanifest", "", testDataUTF8); - EXPECT_EQ(type, PROPERTIES::ManifestType::ISM); + EXPECT_EQ(type, adaptive::TreeType::SMOOTH_STREAMING); // test UTF16 LE with BOM data parsing type = InferManifestType("localhost/proxy/getmanifest", "", testDataUTF16leBOM); - EXPECT_EQ(type, PROPERTIES::ManifestType::ISM); + EXPECT_EQ(type, adaptive::TreeType::SMOOTH_STREAMING); // test header type = InferManifestType("localhost/proxy/getmanifest", "application/vnd.ms-sstr+xml", ""); - EXPECT_EQ(type, PROPERTIES::ManifestType::ISM); + EXPECT_EQ(type, adaptive::TreeType::SMOOTH_STREAMING); // test url type = InferManifestType("http://www.someservice.com/cdm1/manifest.ism/Manifest", "", ""); - EXPECT_EQ(type, PROPERTIES::ManifestType::ISM); + EXPECT_EQ(type, adaptive::TreeType::SMOOTH_STREAMING); // test url type = InferManifestType("http://www.someservice.com/cdm1/manifest.isml/Manifest", "", ""); - EXPECT_EQ(type, PROPERTIES::ManifestType::ISM); + EXPECT_EQ(type, adaptive::TreeType::SMOOTH_STREAMING); // test url type = InferManifestType("http://www.someservice.com/cdm1/manifest.ism", "", ""); - EXPECT_EQ(type, PROPERTIES::ManifestType::ISM); + EXPECT_EQ(type, adaptive::TreeType::SMOOTH_STREAMING); // test url type = InferManifestType("http://www.someservice.com/cdm1/manifest.isml", "", ""); - EXPECT_EQ(type, PROPERTIES::ManifestType::ISM); + EXPECT_EQ(type, adaptive::TreeType::SMOOTH_STREAMING); } TEST_F(UtilsTest, SegTemplateFormatUrlChecks) diff --git a/src/utils/CurlUtils.h b/src/utils/CurlUtils.h index 5f55b38ed..5377092dd 100644 --- a/src/utils/CurlUtils.h +++ b/src/utils/CurlUtils.h @@ -12,7 +12,6 @@ #include "test/KodiStubs.h" #else #include -//! @Todo: forward CFile? #include #endif diff --git a/src/utils/Utils.cpp b/src/utils/Utils.cpp index 1c6a544a3..faaac6e3c 100644 --- a/src/utils/Utils.cpp +++ b/src/utils/Utils.cpp @@ -21,16 +21,18 @@ using namespace UTILS; using namespace kodi::tools; -std::string UTILS::AnnexbToHvcc(const char* b16Data) +std::vector UTILS::AnnexbToHvcc(const char* b16Data) { size_t sz = strlen(b16Data) >> 1; size_t szRun(sz); - std::string result; + std::vector result; if (sz > 1024) return result; - uint8_t buffer[1024], *data(buffer); + std::vector buffer(szRun); + uint8_t* data = buffer.data(); + while (szRun--) { *data = (STRING::ToHexNibble(*b16Data) << 4) + STRING::ToHexNibble(*(b16Data + 1)); @@ -40,13 +42,12 @@ std::string UTILS::AnnexbToHvcc(const char* b16Data) if (sz <= 6 || buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 1) { - result = std::string(reinterpret_cast(buffer), sz); - return result; + return buffer; } - data = buffer + 4; + data = buffer.data() + 4; uint8_t* nalPos[4] = {data, nullptr, nullptr, nullptr}; - uint8_t* end = buffer + sz; + uint8_t* end = buffer.data() + sz; while (data + 4 <= end && (data[0] != 0 || data[1] != 0 || data[2] != 0 || data[3] != 1)) { @@ -77,7 +78,7 @@ std::string UTILS::AnnexbToHvcc(const char* b16Data) { sz = 22 + sz - 12 + 16; result.resize(sz, 0); // Unknown HVCC fields - data = reinterpret_cast(&result[22]); + data = result.data() + 22; *data = 3, ++data; //numSequences; for (unsigned int i(0); i < 3; ++i) { @@ -85,38 +86,39 @@ std::string UTILS::AnnexbToHvcc(const char* b16Data) data[0] = 0, data[1] = 1, data += 2; //count nals uint16_t nalSz = static_cast(nalPos[i + 1] - nalPos[i] - 4); data[0] = nalSz >> 8, data[1] = nalSz & 0xFF, data += 2; //count nals - memcpy(data, nalPos[i], nalSz), data += nalSz; + std::memcpy(data, nalPos[i], nalSz), data += nalSz; } } return result; } -std::string UTILS::AnnexbToAvc(const char* b16Data) +std::vector UTILS::AnnexbToAvc(const char* b16Data) { - size_t sz = strlen(b16Data) >> 1; - size_t szRun(sz); - std::string result; + size_t sz = std::strlen(b16Data) >> 1; + size_t szRun = sz; + std::vector result; if (sz > 1024) return result; - uint8_t buffer[1024], *data(buffer); + std::vector buffer(szRun); + uint8_t* bufferData = buffer.data(); + while (szRun--) { - *data = (STRING::ToHexNibble(*b16Data) << 4) + STRING::ToHexNibble(*(b16Data + 1)); + *bufferData = (STRING::ToHexNibble(*b16Data) << 4) + STRING::ToHexNibble(*(b16Data + 1)); b16Data += 2; - ++data; + ++bufferData; } if (sz <= 6 || buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 1) { - result = std::string(reinterpret_cast(buffer), sz); - return result; + return buffer; } - uint8_t *sps = 0, *pps = 0, *end = buffer + sz; + uint8_t *sps = 0, *pps = 0, *end = buffer.data() + sz; - sps = pps = buffer + 4; + sps = pps = buffer.data() + 4; while (pps + 4 <= end && (pps[0] != 0 || pps[1] != 0 || pps[2] != 0 || pps[3] != 1)) { @@ -133,59 +135,61 @@ std::string UTILS::AnnexbToAvc(const char* b16Data) size_t pos(0); result[pos++] = 1; - result[pos++] = static_cast(sps[1]); - result[pos++] = static_cast(sps[2]); - result[pos++] = static_cast(sps[3]); - result[pos++] = - static_cast(0xFFU); //6 bits reserved(111111) + 2 bits nal size length - 1 (11) - result[pos++] = static_cast(0xe1U); //3 bits reserved (111) + 5 bits number of sps (00001) + result[pos++] = sps[1]; + result[pos++] = sps[2]; + result[pos++] = sps[3]; + result[pos++] = 0xFFU; //6 bits reserved(111111) + 2 bits nal size length - 1 (11) + result[pos++] = 0xe1U; //3 bits reserved (111) + 5 bits number of sps (00001) sz = pps - sps - 4; - result[pos++] = static_cast(sz >> 8); - result[pos++] = static_cast(sz & 0xFF); - result.replace(pos, sz, reinterpret_cast(sps), sz); - pos += sz; + result[pos++] = static_cast(sz >> 8); + result[pos++] = static_cast(sz & 0xFF); + for (size_t i = 0; i < sz; ++i) + { + result[pos++] = sps[i]; + } result[pos++] = 1; sz = end - pps; - result[pos++] = static_cast(sz >> 8); - result[pos++] = static_cast(sz & 0xFF); - result.replace(pos, sz, reinterpret_cast(pps), sz); - pos += sz; + result[pos++] = static_cast(sz >> 8); + result[pos++] = static_cast(sz & 0xFF); + for (size_t i = 0; i < sz; ++i) + { + result[pos++] = pps[i]; + } return result; } -std::string UTILS::AvcToAnnexb(const std::string& avc) +std::vector UTILS::AvcToAnnexb(const std::vector& avc) { if (avc.size() < 8) - return ""; + return {}; // check if's already annexb, avc starts with 1 if (avc[0] == 0) return avc; - const uint8_t* avc_data(reinterpret_cast(avc.data())); - size_t avc_data_size(avc.size()); - // calculate size - uint8_t buffer[1024]; - uint8_t buffer_size(4); + std::vector buffer(1024); //! @todo: fixed size should be removed + uint8_t buffer_size = 4; buffer[0] = buffer[1] = buffer[2] = 0; buffer[3] = 1; //skip avc header - avc_data += 6; + size_t avc_data_size = avc.size(); + const uint8_t* avc_data = avc.data() + 6; avc_data_size -= 6; + //sizeof SPS - std::uint16_t sz(*avc_data); + uint16_t sz = *avc_data; ++avc_data; --avc_data_size; sz = (sz << 8) | *avc_data; ++avc_data; --avc_data_size; //SPS - memcpy(buffer + buffer_size, avc_data, sz); + memcpy(buffer.data() + buffer_size, avc_data, sz); buffer_size += sz, avc_data_size -= sz, avc_data += sz; // Number PPS @@ -202,10 +206,12 @@ std::string UTILS::AvcToAnnexb(const std::string& avc) ppssz = (ppssz << 8) | *avc_data; ++avc_data; --avc_data_size; - memcpy(buffer + buffer_size, avc_data, ppssz), buffer_size += ppssz, avc_data_size -= ppssz, - avc_data += ppssz; + std::memcpy(buffer.data() + buffer_size, avc_data, ppssz); + buffer_size += ppssz; + avc_data_size -= ppssz; + avc_data += ppssz; } - return std::string(reinterpret_cast(buffer), buffer_size); + return {buffer.begin(), buffer.begin() + buffer_size}; } std::string UTILS::ConvertKIDtoWVKID(std::string_view kid) diff --git a/src/utils/Utils.h b/src/utils/Utils.h index 57f68953c..dd136556d 100644 --- a/src/utils/Utils.h +++ b/src/utils/Utils.h @@ -20,9 +20,9 @@ namespace UTILS { -std::string AnnexbToHvcc(const char* b16Data); -std::string AnnexbToAvc(const char* b16Data); -std::string AvcToAnnexb(const std::string& avc); +std::vector AnnexbToHvcc(const char* b16Data); +std::vector AnnexbToAvc(const char* b16Data); +std::vector AvcToAnnexb(const std::vector& avc); std::string ConvertKIDtoWVKID(std::string_view kid); std::string ConvertKIDtoUUID(std::string_view kid); bool CreateISMlicense(std::string_view key,