@@ -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
168181bool 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
756768bool 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
761781void DRM::CDRMEngine::Dispose ()
0 commit comments