@@ -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
168184bool 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
756771bool 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
761784void DRM::CDRMEngine::Dispose ()
0 commit comments