Skip to content

Commit dcdcc31

Browse files
committed
wip
1 parent f731fc1 commit dcdcc31

24 files changed

+469
-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: 134 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,106 @@ 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+
if (m_status == EngineStatus::DRM_ERROR)
108+
return false; // something wrong with a previous initialization
109+
110+
//! @todo: to test a way to initialize DRM when manifest is downloaded/parsed
111+
//! in the hoping to have a more smoother playback transition from unencrypted->to->encrypted periods
112+
113+
// This is the list of keysystems supported by at least one DRM
114+
// are ordered by priority where the lower index has higher priority
115+
// by default ClearKey has the lowest priority since real DRMs should be preferred
116+
std::vector<std::string_view> keySystemsPrio = {KS_WIDEVINE, KS_PLAYREADY, KS_WISEPLAY, KS_CLEARKEY};
148117

118+
const auto& kodiProps = CSrvBroker::GetKodiProps();
119+
// Reorder the keysystems list by using the custom DRM configuration, if any
149120
for (auto& [ks, cfg] : kodiProps.GetDrmConfigs())
150121
{
151122
if (cfg.priority.has_value() && *cfg.priority != 0)
152123
{
153-
auto it = std::find(m_supportedKs.begin(), m_supportedKs.end(), ks);
154-
if (it != m_supportedKs.end())
124+
auto it = std::find(keySystemsPrio.begin(), keySystemsPrio.end(), ks);
125+
if (it != keySystemsPrio.end())
155126
{
156-
m_supportedKs.erase(it);
127+
keySystemsPrio.erase(it);
157128

158129
size_t index = *cfg.priority - 1;
159-
if (index >= m_supportedKs.size())
160-
index = m_supportedKs.size() - 1;
130+
if (index >= keySystemsPrio.size())
131+
index = keySystemsPrio.size() - 1;
161132

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

168184
bool DRM::CDRMEngine::PreInitializeDRM(DRMSession& session)
169185
{
170186
auto& kodiProps = CSrvBroker::GetKodiProps();
171187

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-
178188
const auto propDrmCfg = kodiProps.GetDrmConfig(KS_WIDEVINE);
179189

180190
if (!propDrmCfg.priority.has_value() || propDrmCfg.priority != 1 || propDrmCfg.preInitData.empty())
181191
return false;
182192

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

202221
m_keySystem = KS_WIDEVINE;
203222

204-
std::shared_ptr<DRM::IDecrypter> drm;
205-
SResult ret = CreateDRM(m_keySystem, drm);
206-
if (ret.IsFailed())
223+
std::shared_ptr<DRM::IDecrypter> drm = GetDrmInstance(m_keySystem);
224+
if (!drm)
207225
{
208226
m_status = EngineStatus::DRM_ERROR;
209-
LOG::LogF(LOGERROR, "%s", ret.Message().c_str());
210-
GUI::ErrorDialog(ret.Message());
227+
LOG::LogF(LOGERROR, "Cannot get the DRM instance for keysystem %s", m_keySystem.c_str());
228+
GUI::ErrorDialog(GUI::GetLocalizedString(30303));
211229
return false;
212230
}
213231

232+
LOG::LogF(LOGDEBUG, "Initializing session with KID: %s", STRING::ToHexadecimal(kidData).c_str());
233+
214234
DRM::Config drmCfg = CreateDRMConfig(m_keySystem, kodiProps.GetDrmConfig(m_keySystem));
215235

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

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);
245+
const SResult ret = dec->CreateSession(initData, "", true);
228246

229-
if (!dec)
247+
if (ret.IsFailed())
230248
{
231-
LOG::LogF(LOGERROR, "Failed to initialize the decrypter");
232-
m_status = EngineStatus::DECRYPTER_ERROR;
249+
LOG::LogF(LOGERROR, "Failed to create the DRM session");
250+
m_status = EngineStatus::DRM_ERROR;
251+
GUI::ErrorDialog(ret.Message());
233252
return false;
234253
}
235254

236-
m_drms.emplace(m_keySystem, drm);
237-
238255
session.id = dec->GetSessionId();
239256
session.challenge = drm->GetChallengeB64Data(dec);
240257
session.drm = drm;
@@ -262,6 +279,9 @@ bool DRM::CDRMEngine::InitializeSession(std::vector<DRM::DRMInfo> drmInfos,
262279
if (drmInfos.empty())
263280
return false;
264281

282+
if (!Initialize())
283+
return false;
284+
265285
LOG::Log(LOGDEBUG, "Initialize crypto session");
266286

267287
ConfigureClearKey(drmInfos);
@@ -345,24 +365,6 @@ bool DRM::CDRMEngine::InitializeSession(std::vector<DRM::DRMInfo> drmInfos,
345365
ExtractStreamProtectionData(repr, adp, drmInfo);
346366
}
347367

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-
366368
const std::vector<uint8_t> drmInfoKidBytes = DRM::ConvertKidStrToBytes(drmInfo.defaultKid);
367369

368370
if (m_isPreinitialized && m_sessions.size() == 1)
@@ -411,31 +413,43 @@ bool DRM::CDRMEngine::InitializeSession(std::vector<DRM::DRMInfo> drmInfos,
411413
if (canCleanupSessions)
412414
DeleteSessionsByType(mediaType);
413415

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

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-
}
430+
DRM::Config drmCfg = DRM::CreateDRMConfig(m_keySystem, drmPropCfg);
431431

432432
newSes.decrypter = newSes.drm->CreateSingleSampleDecrypter(
433-
drmInfo.initData, drmInfoKidBytes, drmInfo.licenseServerUri, false,
433+
drmCfg, drmInfoKidBytes,
434434
drmInfo.cryptoMode == CryptoMode::NONE ? CryptoMode::AES_CTR : drmInfo.cryptoMode);
435+
435436
if (!newSes.decrypter)
436437
{
437-
LOG::Log(LOGERROR, "Failed to initialize the decrypter");
438438
m_status = EngineStatus::DECRYPTER_ERROR;
439+
LOG::Log(LOGERROR, "Failed to initialize the %s DRM decrypter",
440+
newSes.drm->GetName().c_str());
441+
GUI::ErrorDialog(GUI::GetLocalizedString(30303));
442+
return false;
443+
}
444+
445+
const SResult ret =
446+
newSes.decrypter->CreateSession(drmInfo.initData, drmInfo.licenseServerUri, false);
447+
448+
if (ret.IsFailed())
449+
{
450+
LOG::LogF(LOGERROR, "Failed to create the DRM session");
451+
m_status = EngineStatus::DRM_ERROR;
452+
GUI::ErrorDialog(ret.Message());
439453
return false;
440454
}
441455

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

638652
if (drmInfo)
639653
{
640-
m_keySystem = ks;
654+
m_keySystem = drm.keySystem;
655+
LOG::LogF(LOGDEBUG, "Selected DRM key system: %s", m_keySystem.c_str());
641656
break;
642657
}
643658
}
@@ -755,7 +770,15 @@ void DRM::CDRMEngine::DeleteSessionsByType(const DRMMediaType mediaType)
755770

756771
bool DRM::CDRMEngine::HasKeySystemSupport(std::string_view keySystem) const
757772
{
758-
return std::find(m_supportedKs.cbegin(), m_supportedKs.cend(), keySystem) != m_supportedKs.cend();
773+
return std::any_of(m_drms.cbegin(), m_drms.cend(),
774+
[&keySystem](const DRMInstance& a) { return a.keySystem == keySystem; });
775+
}
776+
777+
std::shared_ptr<DRM::IDecrypter> DRM::CDRMEngine::GetDrmInstance(std::string_view ks) const
778+
{
779+
auto it = std::find_if(m_drms.cbegin(), m_drms.cend(),
780+
[&ks](const DRMInstance& d) { return d.keySystem == ks; });
781+
return it != m_drms.cend() ? it->drm : nullptr;
759782
}
760783

761784
void DRM::CDRMEngine::Dispose()

0 commit comments

Comments
 (0)