diff --git a/src/Session.cpp b/src/Session.cpp index 8ff6d7d8f..fd6814914 100644 --- a/src/Session.cpp +++ b/src/Session.cpp @@ -65,7 +65,7 @@ CSession::CSession(const std::string& manifestUrl) : m_manifestUrl(manifestUrl) CSession::~CSession() { LOG::Log(LOGDEBUG, "CSession::~CSession()"); - m_streams.clear(); + DeleteStreams(); DisposeDecrypter(); if (m_adaptiveTree) @@ -79,6 +79,12 @@ CSession::~CSession() m_reprChooser = nullptr; } +void SESSION::CSession::DeleteStreams() +{ + LOG::Log(LOGDEBUG, "CSession::DeleteStreams()"); + m_streams.clear(); +} + void CSession::SetSupportedDecrypterURN(std::vector& keySystems) { std::string decrypterPath = CSrvBroker::GetSettings().GetDecrypterPath(); diff --git a/src/Session.h b/src/Session.h index 0e6aa3147..3d06779d3 100644 --- a/src/Session.h +++ b/src/Session.h @@ -30,6 +30,8 @@ class ATTR_DLL_LOCAL CSession : public adaptive::AdaptiveStreamObserver CSession(const std::string& manifestUrl); virtual ~CSession(); + void DeleteStreams(); + /*! \brief Initialize the session * \return True if has success, false otherwise */ diff --git a/src/main.cpp b/src/main.cpp index 964dd0d42..4d8217d83 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -136,6 +136,27 @@ bool CInputStreamAdaptive::GetStream(int streamid, kodi::addon::InputstreamInfo& if (stream) { + // If the stream is encrypted, verify if the decrypter has been initialized + // this is important for HLS because the DRM it is initialized at later time + // so on the OpenStream, instead of CSession::Initialize->InitializePeriod->InitializeDRM + // Since kodi initialize one single stream at time, can happens that or another stream + // has been opened before this one, or another stream will be opened after this one (e.g. unencrypted) + // so if you dont delete all streams, the kodi demux reader still starts + // and a corrupted playback will starts. + // NOTE: GetStream is called by Kodi twice times, before and after OpenStream, on HLS + // the first time all streams are unencrypted because child manifest has not been downloaded + const uint16_t psshSetPos = stream->m_adStream.getRepresentation()->m_psshSetPos; + if (psshSetPos != PSSHSET_POS_DEFAULT || + stream->m_adStream.getPeriod()->GetEncryptionState() == EncryptionState::NOT_SUPPORTED) + { + if (!m_session->GetSingleSampleDecryptor(psshSetPos)) + { + LOG::Log(LOGERROR, "GetStream(%d): Decrypter for the stream not found"); + m_session->DeleteStreams(); + return false; + } + } + info = stream->m_info; return true; } diff --git a/src/parser/DASHTree.cpp b/src/parser/DASHTree.cpp index efb2a47ed..18261c1e3 100644 --- a/src/parser/DASHTree.cpp +++ b/src/parser/DASHTree.cpp @@ -1343,26 +1343,41 @@ bool adaptive::CDashTree::GetProtectionData( //! @todo: this should not be a task of parser, moreover missing an appropriate KID extraction from mp4 box auto& kodiProps = CSrvBroker::GetKodiProps(); ProtectionScheme ckProtScheme; - if (!protSelected && !protCommon && kodiProps.GetLicenseType() == DRM::KS_CLEARKEY) + if (kodiProps.GetLicenseType() == DRM::KS_CLEARKEY) { - for (const ProtectionScheme& protScheme : reprProtSchemes) + std::string_view defaultKid; + if (protSelected) + defaultKid = protSelected->kid; + if (defaultKid.empty() && protCommon) + defaultKid = protCommon->kid; + + if (defaultKid.empty()) { - if (!protScheme.kid.empty()) + for (const ProtectionScheme& protScheme : reprProtSchemes) { - ckProtScheme.kid = protScheme.kid; - break; + if (!protScheme.kid.empty()) + { + defaultKid = protScheme.kid; + break; + } } - } - if (ckProtScheme.kid.empty()) - { - for (const ProtectionScheme& protScheme : adpProtSchemes) + if (defaultKid.empty()) { - ckProtScheme.kid = protScheme.kid; - break; + for (const ProtectionScheme& protScheme : adpProtSchemes) + { + if (!protScheme.kid.empty()) + { + defaultKid = protScheme.kid; + break; + } + } } - } - if (!ckProtScheme.kid.empty()) + if (protCommon) + ckProtScheme = *protCommon; + + ckProtScheme.kid = defaultKid; protCommon = &ckProtScheme; + } } bool isEncrypted{false};