Skip to content

Commit 88820e2

Browse files
committed
wip
1 parent f731fc1 commit 88820e2

24 files changed

+466
-367
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
44a65c4f936cfa6b4b0114eee7a809efeb26a3fa442f70e95c998183d252953c
1+
821a0adc4c9af976ee06fa5d6a75cb27ec5eed457553414b17a681597538a8f3
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
libandroidjni https://github.com/xbmc/libandroidjni/archive/2f8346bbe794cc3bd41b4195a59f0470470aa52e.tar.gz
1+
libandroidjni https://github.com/CastagnaIT/libandroidjni/archive/refs/heads/crypto_support.tar.gz

src/Session.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,6 @@ SResult SESSION::CSession::Initialize(std::string manifestUrl)
9999
auto& kodiProps = CSrvBroker::GetKodiProps();
100100
std::map<std::string, std::string> manifestHeaders = kodiProps.GetManifestHeaders();
101101

102-
m_drmEngine.Initialize();
103-
104102
DRM::DRMSession session;
105103
// Pre-initialize the DRM allow to generate the challenge and session ID data
106104
// used to make licensed manifest requests

src/common/AdaptiveDecrypter.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include "decrypters/DrmEngineDefines.h"
1212
#include "utils/CryptoUtils.h"
13+
#include "utils/ResultType.h"
1314

1415
#include <cstdint>
1516
#include <stdexcept>
@@ -59,6 +60,17 @@ class Adaptive_CencSingleSampleDecrypter : public AP4_CencSingleSampleDecrypter
5960
virtual AP4_UI32 AddPool() { return 0; }
6061
virtual void RemovePool(AP4_UI32 poolId) {}
6162

63+
/*
64+
* \brief Create a DRM session.
65+
* \param pssh[OPT] A PSSH to make the license request, its use depends on DRM.
66+
* \param licenseUrl[OPT] A license URL or URI to make the license request, its use depends on DRM.
67+
* \param skipSessionMessage[OPT] Skip the session message to avoid make a license request, by default should be set false.
68+
* \return SResult::Ok on success, otherwise a SResult failed status.
69+
*/
70+
virtual SResult CreateSession(const std::vector<uint8_t>& pssh,
71+
std::string_view licenseUrl,
72+
bool skipSessionMessage) = 0;
73+
6274
/*!
6375
* \brief The session ID, is mandatory to distinguish sessions.
6476
*/

src/decrypters/DrmEngine.cpp

Lines changed: 131 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -46,35 +46,6 @@ STREAM_CRYPTO_KEY_SYSTEM KSToCryptoKeySystem(std::string_view keySystem)
4646
return STREAM_CRYPTO_KEY_SYSTEM_NONE;
4747
}
4848

49-
SResult CreateDRM(std::string_view keySystem, std::shared_ptr<DRM::IDecrypter>& drm)
50-
{
51-
std::string decrypterPath = CSrvBroker::GetSettings().GetDecrypterPath();
52-
if (decrypterPath.empty())
53-
{
54-
LOG::LogF(LOGERROR, "No decrypter path set in the add-on settings");
55-
return SResult::Error(GUI::GetLocalizedString(30302));
56-
}
57-
58-
drm = DRM::FACTORY::GetDecrypter(KSToCryptoKeySystem(keySystem));
59-
60-
if (!drm)
61-
{
62-
LOG::LogF(LOGERROR, "Unable to create the DRM decrypter");
63-
return SResult::Error(GUI::GetLocalizedString(30303));
64-
}
65-
66-
drm->SetLibraryPath(decrypterPath);
67-
68-
if (!drm->Initialize())
69-
{
70-
drm = nullptr;
71-
LOG::LogF(LOGERROR, "Unable to initialize the DRM decrypter");
72-
return SResult::Error(GUI::GetLocalizedString(30303));
73-
}
74-
75-
return SResultCode::OK;
76-
}
77-
7849
/*!
7950
* \brief Get a DRMInfo by Key System
8051
* \param drmInfos The manifest DRM info
@@ -128,58 +99,103 @@ bool GetCapabilities(const std::optional<bool> isForceSecureDecoder,
12899
}
129100
} // unnamed namespace
130101

131-
void DRM::CDRMEngine::Initialize()
102+
bool DRM::CDRMEngine::Initialize()
132103
{
133-
// Build the list of the supported DRM's by KeySystem,
134-
// the list depends on the operative system used,
135-
// the order of addition also defines the DRM priority (always prefer real DRM's over ClearKey)
136-
// in future could be improved to take in account also the DRM capability on the target system
137-
138-
// Widevine currently always preferred as first because on android can reach 4k on L1 devices
139-
m_supportedKs.emplace_back(KS_WIDEVINE);
140-
#if ANDROID
141-
m_supportedKs.emplace_back(KS_PLAYREADY);
142-
m_supportedKs.emplace_back(KS_WISEPLAY);
143-
#endif
144-
m_supportedKs.emplace_back(KS_CLEARKEY);
104+
if (!m_drms.empty())
105+
return true; // assume as already initialized
145106

146-
// Sort key systems based on priorities
147-
const auto& kodiProps = CSrvBroker::GetKodiProps();
107+
//! @todo: to test a way to initialize DRM when manifest is downloaded/parsed
108+
//! in the hoping to have a more smoother playback transition from unencrypted->to->encrypted periods
109+
110+
// This is the list of keysystems supported by at least one DRM
111+
// are ordered by priority where the lower index has higher priority
112+
// the ClearKey is set as last since real DRMs should be preferred
113+
std::vector<std::string_view> keySystemsPrio = {KS_WIDEVINE, KS_PLAYREADY, KS_WISEPLAY, KS_CLEARKEY};
148114

115+
const auto& kodiProps = CSrvBroker::GetKodiProps();
116+
// Reorder the keysystems list by using the custom DRM configuration, if any
149117
for (auto& [ks, cfg] : kodiProps.GetDrmConfigs())
150118
{
151119
if (cfg.priority.has_value() && *cfg.priority != 0)
152120
{
153-
auto it = std::find(m_supportedKs.begin(), m_supportedKs.end(), ks);
154-
if (it != m_supportedKs.end())
121+
auto it = std::find(keySystemsPrio.begin(), keySystemsPrio.end(), ks);
122+
if (it != keySystemsPrio.end())
155123
{
156-
m_supportedKs.erase(it);
124+
keySystemsPrio.erase(it);
157125

158126
size_t index = *cfg.priority - 1;
159-
if (index >= m_supportedKs.size())
160-
index = m_supportedKs.size() - 1;
127+
if (index >= keySystemsPrio.size())
128+
index = keySystemsPrio.size() - 1;
161129

162-
m_supportedKs.insert(m_supportedKs.begin() + index, ks);
130+
keySystemsPrio.insert(keySystemsPrio.begin() + index, ks);
163131
}
164132
}
165133
}
134+
135+
// Get all DRM supported by the platform in use to determine which keysystems are supported
136+
std::vector<std::shared_ptr<DRM::IDecrypter>> drms = FACTORY::GetDecrypters();
137+
138+
std::string decrypterPath = CSrvBroker::GetSettings().GetDecrypterPath();
139+
if (decrypterPath.empty())
140+
{
141+
LOG::LogF(LOGERROR,
142+
"Cannot initialize DrmEngine, no decrypter path set in the add-on settings");
143+
m_status = EngineStatus::DRM_ERROR;
144+
return false;
145+
}
146+
147+
// Initialize DRMs
148+
for (auto it = drms.begin(); it != drms.end();)
149+
{
150+
(*it)->SetLibraryPath(decrypterPath);
151+
152+
if (!(*it)->Initialize()) // Failed to initialize DRM, delete it and go on
153+
{
154+
LOG::LogF(LOGERROR, "Unable to initialize %s DRM", (*it)->GetName().c_str());
155+
it = drms.erase(it);
156+
}
157+
else
158+
++it;
159+
}
160+
161+
// Check what keysystems are supported by DRMs by priority order
162+
// and so add the supported one to the DRM list
163+
for (std::string_view ks : keySystemsPrio)
164+
{
165+
for (auto& drm : drms)
166+
{
167+
if (drm->IsKeySystemSupported(ks))
168+
m_drms.emplace_back(ks, drm);
169+
}
170+
}
171+
172+
if (m_drms.empty())
173+
{
174+
LOG::LogF(LOGWARNING, "No DRM available");
175+
return false;
176+
}
177+
178+
return true;
166179
}
167180

168181
bool DRM::CDRMEngine::PreInitializeDRM(DRMSession& session)
169182
{
170183
auto& kodiProps = CSrvBroker::GetKodiProps();
171184

172-
// Pre-initialize the DRM is available for Widevine only.
173-
// Since the manifest will be downloaded later its assumed that
174-
// the manifest support the DRM and that the priority is set to 1.
175-
if (std::find(m_supportedKs.cbegin(), m_supportedKs.cend(), KS_WIDEVINE) == m_supportedKs.cend())
176-
return false;
177-
178185
const auto propDrmCfg = kodiProps.GetDrmConfig(KS_WIDEVINE);
179186

180187
if (!propDrmCfg.priority.has_value() || propDrmCfg.priority != 1 || propDrmCfg.preInitData.empty())
181188
return false;
182189

190+
if (!Initialize())
191+
return false;
192+
193+
// Pre-initialize the DRM is available for Widevine only.
194+
// Since the manifest will be downloaded later its assumed that
195+
// the manifest support the DRM and that the priority is set to 1.
196+
if (!HasKeySystemSupport(KS_WIDEVINE))
197+
return false;
198+
183199
LOG::Log(LOGDEBUG, "Pre-initialize crypto session");
184200
std::vector<uint8_t> initData;
185201
std::vector<uint8_t> kidData;
@@ -201,40 +217,38 @@ bool DRM::CDRMEngine::PreInitializeDRM(DRMSession& session)
201217

202218
m_keySystem = KS_WIDEVINE;
203219

204-
std::shared_ptr<DRM::IDecrypter> drm;
205-
SResult ret = CreateDRM(m_keySystem, drm);
206-
if (ret.IsFailed())
220+
std::shared_ptr<DRM::IDecrypter> drm = GetDrmInstance(m_keySystem);
221+
if (!drm)
207222
{
208223
m_status = EngineStatus::DRM_ERROR;
209-
LOG::LogF(LOGERROR, "%s", ret.Message().c_str());
210-
GUI::ErrorDialog(ret.Message());
224+
LOG::LogF(LOGERROR, "Cannot get the DRM instance for keysystem %s", m_keySystem.c_str());
225+
GUI::ErrorDialog(GUI::GetLocalizedString(30303));
211226
return false;
212227
}
213228

229+
LOG::LogF(LOGDEBUG, "Initializing session with KID: %s", STRING::ToHexadecimal(kidData).c_str());
230+
214231
DRM::Config drmCfg = CreateDRMConfig(m_keySystem, kodiProps.GetDrmConfig(m_keySystem));
215232

216-
ret = drm->OpenDRMSystem(drmCfg);
217-
if (ret.IsFailed())
233+
auto dec = drm->CreateSingleSampleDecrypter(drmCfg, kidData, CryptoMode::AES_CTR);
234+
235+
if (!dec)
218236
{
219-
LOG::LogF(LOGERROR, "Failed to open the DRM");
220-
m_status = EngineStatus::DRM_ERROR;
221-
GUI::ErrorDialog(ret.Message());
237+
LOG::Log(LOGERROR, "Failed to initialize the %s DRM decrypter", drm->GetName().c_str());
238+
m_status = EngineStatus::DECRYPTER_ERROR;
222239
return false;
223240
}
224241

225-
LOG::LogF(LOGDEBUG, "Initializing session with KID: %s", STRING::ToHexadecimal(kidData).c_str());
226-
227-
auto dec = drm->CreateSingleSampleDecrypter(initData, kidData, "", true, CryptoMode::AES_CTR);
242+
const SResult ret = dec->CreateSession(initData, "", true);
228243

229-
if (!dec)
244+
if (ret.IsFailed())
230245
{
231-
LOG::LogF(LOGERROR, "Failed to initialize the decrypter");
232-
m_status = EngineStatus::DECRYPTER_ERROR;
246+
LOG::LogF(LOGERROR, "Failed to create the DRM session");
247+
m_status = EngineStatus::DRM_ERROR;
248+
GUI::ErrorDialog(ret.Message());
233249
return false;
234250
}
235251

236-
m_drms.emplace(m_keySystem, drm);
237-
238252
session.id = dec->GetSessionId();
239253
session.challenge = drm->GetChallengeB64Data(dec);
240254
session.drm = drm;
@@ -262,6 +276,9 @@ bool DRM::CDRMEngine::InitializeSession(std::vector<DRM::DRMInfo> drmInfos,
262276
if (drmInfos.empty())
263277
return false;
264278

279+
if (!Initialize())
280+
return false;
281+
265282
LOG::Log(LOGDEBUG, "Initialize crypto session");
266283

267284
ConfigureClearKey(drmInfos);
@@ -345,24 +362,6 @@ bool DRM::CDRMEngine::InitializeSession(std::vector<DRM::DRMInfo> drmInfos,
345362
ExtractStreamProtectionData(repr, adp, drmInfo);
346363
}
347364

348-
// Create the DRM decrypter
349-
if (!STRING::KeyExists(m_drms, m_keySystem))
350-
{
351-
//! @todo: to test a way to preinitialize DRM when manifest is downloaded/parsed
352-
//! in the hoping to have a more smoother playback transition
353-
//! this can be tested with multiperiods video where first period is unencrypted and second one DRM crypted
354-
std::shared_ptr<DRM::IDecrypter> drm;
355-
SResult ret = CreateDRM(m_keySystem, drm);
356-
if (ret.IsFailed())
357-
{
358-
m_status = EngineStatus::DRM_ERROR;
359-
LOG::LogF(LOGERROR, "%s", ret.Message().c_str());
360-
GUI::ErrorDialog(ret.Message());
361-
return false;
362-
}
363-
m_drms.emplace(m_keySystem, drm);
364-
}
365-
366365
const std::vector<uint8_t> drmInfoKidBytes = DRM::ConvertKidStrToBytes(drmInfo.defaultKid);
367366

368367
if (m_isPreinitialized && m_sessions.size() == 1)
@@ -411,31 +410,43 @@ bool DRM::CDRMEngine::InitializeSession(std::vector<DRM::DRMInfo> drmInfos,
411410
if (canCleanupSessions)
412411
DeleteSessionsByType(mediaType);
413412

413+
std::shared_ptr<DRM::IDecrypter> drm = GetDrmInstance(m_keySystem);
414+
if (!drm)
415+
{
416+
m_status = EngineStatus::DRM_ERROR;
417+
LOG::LogF(LOGERROR, "Cannot get the DRM instance for keysystem %s", m_keySystem.c_str());
418+
GUI::ErrorDialog(GUI::GetLocalizedString(30303));
419+
return false;
420+
}
421+
414422
DRMSession newSes;
415-
newSes.drm = m_drms[m_keySystem];
423+
newSes.drm = drm;
416424
newSes.mediaType = mediaType;
417425
newSes.kid = drmInfo.defaultKid;
418426

419-
if (!newSes.drm->IsInitialised())
420-
{
421-
DRM::Config drmCfg = DRM::CreateDRMConfig(m_keySystem, drmPropCfg);
422-
const SResult ret = newSes.drm->OpenDRMSystem(drmCfg);
423-
if (ret.IsFailed())
424-
{
425-
LOG::LogF(LOGERROR, "Failed to open the DRM");
426-
m_status = EngineStatus::DRM_ERROR;
427-
GUI::ErrorDialog(ret.Message());
428-
return false;
429-
}
430-
}
427+
DRM::Config drmCfg = DRM::CreateDRMConfig(m_keySystem, drmPropCfg);
431428

432429
newSes.decrypter = newSes.drm->CreateSingleSampleDecrypter(
433-
drmInfo.initData, drmInfoKidBytes, drmInfo.licenseServerUri, false,
430+
drmCfg, drmInfoKidBytes,
434431
drmInfo.cryptoMode == CryptoMode::NONE ? CryptoMode::AES_CTR : drmInfo.cryptoMode);
432+
435433
if (!newSes.decrypter)
436434
{
437-
LOG::Log(LOGERROR, "Failed to initialize the decrypter");
438435
m_status = EngineStatus::DECRYPTER_ERROR;
436+
LOG::Log(LOGERROR, "Failed to initialize the %s DRM decrypter",
437+
newSes.drm->GetName().c_str());
438+
GUI::ErrorDialog(GUI::GetLocalizedString(30303));
439+
return false;
440+
}
441+
442+
const SResult ret =
443+
newSes.decrypter->CreateSession(drmInfo.initData, drmInfo.licenseServerUri, false);
444+
445+
if (ret.IsFailed())
446+
{
447+
LOG::LogF(LOGERROR, "Failed to create the DRM session");
448+
m_status = EngineStatus::DRM_ERROR;
449+
GUI::ErrorDialog(ret.Message());
439450
return false;
440451
}
441452

@@ -631,13 +642,14 @@ bool DRM::CDRMEngine::SelectDRM(std::vector<DRM::DRMInfo>& drmInfos)
631642
// Iterate supported DRM Key System's to find a match with the drmInfo's,
632643
// the supported DRM's are ordered by priority
633644
// the lower index have the higher priority
634-
for (const std::string& ks : m_supportedKs)
645+
for (auto& drm : m_drms)
635646
{
636-
const DRM::DRMInfo* drmInfo = GetDRMInfoByKS(drmInfos, ks);
647+
const DRM::DRMInfo* drmInfo = GetDRMInfoByKS(drmInfos, drm.keySystem);
637648

638649
if (drmInfo)
639650
{
640-
m_keySystem = ks;
651+
m_keySystem = drm.keySystem;
652+
LOG::LogF(LOGDEBUG, "Selected DRM key system: %s", m_keySystem.c_str());
641653
break;
642654
}
643655
}
@@ -755,7 +767,15 @@ void DRM::CDRMEngine::DeleteSessionsByType(const DRMMediaType mediaType)
755767

756768
bool DRM::CDRMEngine::HasKeySystemSupport(std::string_view keySystem) const
757769
{
758-
return std::find(m_supportedKs.cbegin(), m_supportedKs.cend(), keySystem) != m_supportedKs.cend();
770+
return std::any_of(m_drms.cbegin(), m_drms.cend(),
771+
[&keySystem](const DRMInstance& a) { return a.keySystem == keySystem; });
772+
}
773+
774+
std::shared_ptr<DRM::IDecrypter> DRM::CDRMEngine::GetDrmInstance(std::string_view ks) const
775+
{
776+
auto it = std::find_if(m_drms.cbegin(), m_drms.cend(),
777+
[&ks](const DRMInstance& d) { return d.keySystem == ks; });
778+
return it != m_drms.cend() ? it->drm : nullptr;
759779
}
760780

761781
void DRM::CDRMEngine::Dispose()

0 commit comments

Comments
 (0)