diff --git a/src/CompKodiProps.cpp b/src/CompKodiProps.cpp index 96693204a..f0f031727 100644 --- a/src/CompKodiProps.cpp +++ b/src/CompKodiProps.cpp @@ -376,7 +376,7 @@ void ADP::KODI_PROPS::CCompKodiProps::ParseDrmOldProps( { // Translate data from old ISA properties to the new DRM config - if (!STRING::KeyExists(props, PROP_LICENSE_TYPE) && !STRING::KeyExists(props, PROP_LICENSE_KEY)) + if (!STRING::KeyExists(props, PROP_LICENSE_TYPE)) return; /* *! @todo: TO UNCOMMENT WHEN DRM AUTO-SELECTION WILL BE FULL IMPLEMENTED diff --git a/src/Session.cpp b/src/Session.cpp index 09660ebf4..d932b5686 100644 --- a/src/Session.cpp +++ b/src/Session.cpp @@ -24,17 +24,12 @@ #include "utils/Utils.h" #include "utils/log.h" -#include - -#include - -using namespace kodi::tools; using namespace adaptive; using namespace PLAYLIST; using namespace SESSION; using namespace UTILS; -CSession::~CSession() +SESSION::CSession::~CSession() { LOG::Log(LOGDEBUG, "CSession::~CSession()"); DeleteStreams(); @@ -57,7 +52,7 @@ void SESSION::CSession::DeleteStreams() m_streams.clear(); } -void CSession::SetSupportedDecrypterURN(std::vector& keySystems) +void SESSION::CSession::SetSupportedDecrypterURN(std::vector& keySystems) { std::string decrypterPath = CSrvBroker::GetSettings().GetDecrypterPath(); if (decrypterPath.empty()) @@ -81,7 +76,7 @@ void CSession::SetSupportedDecrypterURN(std::vector& keySystem m_decrypter->SetLibraryPath(decrypterPath); } -void CSession::DisposeSampleDecrypter() +void SESSION::CSession::DisposeSampleDecrypter() { if (m_decrypter) { @@ -93,7 +88,7 @@ void CSession::DisposeSampleDecrypter() } } -void CSession::DisposeDecrypter() +void SESSION::CSession::DisposeDecrypter() { DisposeSampleDecrypter(); m_decrypter = nullptr; @@ -103,7 +98,7 @@ void CSession::DisposeDecrypter() | initialize +---------------------------------------------------------------------*/ -bool CSession::Initialize(std::string manifestUrl) +bool SESSION::CSession::Initialize(std::string manifestUrl) { m_reprChooser = CHOOSER::CreateRepresentationChooser(); @@ -196,7 +191,7 @@ bool CSession::Initialize(std::string manifestUrl) return InitializePeriod(isSessionOpened); } -void CSession::CheckHDCP() +void SESSION::CSession::CheckHDCP() { //! @todo: is needed to implement an appropriate CP check to //! remove HDCPOVERRIDE setting workaround @@ -238,9 +233,9 @@ void CSession::CheckHDCP() } } -bool CSession::PreInitializeDRM(std::string& challengeB64, - std::string& sessionId, - bool& isSessionOpened) +bool SESSION::CSession::PreInitializeDRM(std::string& challengeB64, + std::string& sessionId, + bool& isSessionOpened) { auto& drmPropCfg = CSrvBroker::GetKodiProps().GetDrmConfig(); @@ -318,7 +313,7 @@ bool CSession::PreInitializeDRM(std::string& challengeB64, return true; } -bool CSession::InitializeDRM(bool addDefaultKID /* = false */) +bool SESSION::CSession::InitializeDRM(bool addDefaultKID /* = false */) { bool isSecureVideoSession{false}; m_cdmSessions.resize(m_adaptiveTree->m_currentPeriod->GetPSSHSets().size()); @@ -519,7 +514,7 @@ bool CSession::InitializeDRM(bool addDefaultKID /* = false */) return true; } -bool CSession::InitializePeriod(bool isSessionOpened /* = false */) +bool SESSION::CSession::InitializePeriod(bool isSessionOpened /* = false */) { bool isPsshChanged{true}; bool isReusePssh{true}; @@ -625,11 +620,11 @@ bool CSession::InitializePeriod(bool isSessionOpened /* = false */) return true; } -void CSession::AddStream(PLAYLIST::CAdaptationSet* adp, - PLAYLIST::CRepresentation* initialRepr, - bool isDefaultRepr, - uint32_t uniqueId, - std::string_view audioLanguageOrig) +void SESSION::CSession::AddStream(PLAYLIST::CAdaptationSet* adp, + PLAYLIST::CRepresentation* initialRepr, + bool isDefaultRepr, + uint32_t uniqueId, + std::string_view audioLanguageOrig) { m_streams.push_back(std::make_unique(m_adaptiveTree, adp, initialRepr)); @@ -687,7 +682,7 @@ void CSession::AddStream(PLAYLIST::CAdaptationSet* adp, UpdateStream(stream); } -void CSession::UpdateStream(CStream& stream) +void SESSION::CSession::UpdateStream(CStream& stream) { // On this method we set stream info provided by manifest parsing, but these info could be // changed by sample readers just before the start of playback by using GetInformation() methods @@ -877,7 +872,7 @@ void CSession::UpdateStream(CStream& stream) stream.m_info.SetCodecInternalName(codecStr); } -void CSession::PrepareStream(CStream* stream) +void SESSION::CSession::PrepareStream(CStream* stream) { if (!m_adaptiveTree->IsReqPrepareStream()) return; @@ -942,7 +937,7 @@ const char* SESSION::CSession::GetCDMSession(unsigned int index) return m_cdmSessions[index].m_cdmSessionStr; } -uint64_t CSession::PTSToElapsed(uint64_t pts) +uint64_t SESSION::CSession::PTSToElapsed(uint64_t pts) { if (m_timingStream) { @@ -968,7 +963,7 @@ uint64_t CSession::PTSToElapsed(uint64_t pts) return pts; } -uint64_t CSession::GetTimeshiftBufferStart() +uint64_t SESSION::CSession::GetTimeshiftBufferStart() { if (m_timingStream) { @@ -985,7 +980,7 @@ uint64_t CSession::GetTimeshiftBufferStart() } // TODO: clean this up along with seektime -void CSession::StartReader( +void SESSION::CSession::StartReader( CStream* stream, uint64_t seekTime, int64_t ptsDiff, bool preceeding, bool timing) { ISampleReader* streamReader = stream->GetReader(); @@ -1013,12 +1008,12 @@ void CSession::StartReader( m_changed = true; } -void CSession::OnScreenResChange() +void SESSION::CSession::OnScreenResChange() { m_reprChooser->OnUpdateScreenRes(); }; -bool CSession::GetNextSample(ISampleReader*& sampleReader) +bool SESSION::CSession::GetNextSample(ISampleReader*& sampleReader) { CStream* res{nullptr}; CStream* waiting{nullptr}; @@ -1083,7 +1078,7 @@ bool CSession::GetNextSample(ISampleReader*& sampleReader) return false; } -bool CSession::SeekTime(double seekTime, unsigned int streamId, bool preceeding) +bool SESSION::CSession::SeekTime(double seekTime, unsigned int streamId, bool preceeding) { bool ret{false}; @@ -1220,7 +1215,7 @@ bool CSession::SeekTime(double seekTime, unsigned int streamId, bool preceeding) return ret; } -void CSession::OnDemuxRead() +void SESSION::CSession::OnDemuxRead() { if (m_adaptiveTree->IsChangingPeriod() && m_adaptiveTree->IsChangingPeriodDone()) { @@ -1234,7 +1229,7 @@ void CSession::OnDemuxRead() } } -void CSession::OnSegmentChanged(adaptive::AdaptiveStream* adStream) +void SESSION::CSession::OnSegmentChanged(adaptive::AdaptiveStream* adStream) { for (auto& stream : m_streams) { @@ -1251,7 +1246,7 @@ void CSession::OnSegmentChanged(adaptive::AdaptiveStream* adStream) } } -void CSession::OnStreamChange(adaptive::AdaptiveStream* adStream) +void SESSION::CSession::OnStreamChange(adaptive::AdaptiveStream* adStream) { for (auto& stream : m_streams) { @@ -1263,7 +1258,38 @@ void CSession::OnStreamChange(adaptive::AdaptiveStream* adStream) } } -std::shared_ptr CSession::GetSingleSampleDecrypter(std::string sessionId) +bool SESSION::CSession::OnGetStream(int streamid, kodi::addon::InputstreamInfo& info) +{ + CStream* stream(GetStream(streamid - GetPeriodId() * 1000)); + + if (stream) + { + const uint16_t psshSetPos = stream->m_adStream.getRepresentation()->m_psshSetPos; + if (psshSetPos != PSSHSET_POS_DEFAULT || + stream->m_adStream.getPeriod()->GetEncryptionState() == EncryptionState::NOT_SUPPORTED) + { + if (!GetSingleSampleDecryptor(psshSetPos)) + { + // If the stream is protected with a unsupported DRM, we have to stop the playback, + // since there are no ways to stop playback when Kodi request streams + // we are forced to delete all CStream's here, so that when demux reader will starts + // will have no data to process, and so stop the playback + // (other streams may have been requested/opened before this one) + LOG::Log(LOGERROR, "GetStream(%d): Decrypter for the stream not found"); + DeleteStreams(); + return false; + } + } + + info = stream->m_info; + return true; + } + + return false; +} + +std::shared_ptr SESSION::CSession::GetSingleSampleDecrypter( + std::string sessionId) { for (std::vector::iterator b(m_cdmSessions.begin() + 1), e(m_cdmSessions.end()); b != e; ++b) @@ -1274,7 +1300,7 @@ std::shared_ptr CSession::GetSingleSampleDec return nullptr; } -uint32_t CSession::GetIncludedStreamMask() const +uint32_t SESSION::CSession::GetIncludedStreamMask() const { //! @todo: this conversion must be reworked can easily be broken and cause hidden problems const INPUTSTREAM_TYPE adp2ips[] = {INPUTSTREAM_TYPE_NONE, INPUTSTREAM_TYPE_VIDEO, @@ -1288,7 +1314,7 @@ uint32_t CSession::GetIncludedStreamMask() const return res; } -STREAM_CRYPTO_KEY_SYSTEM CSession::GetCryptoKeySystem(std::string_view keySystem) const +STREAM_CRYPTO_KEY_SYSTEM SESSION::CSession::GetCryptoKeySystem(std::string_view keySystem) const { if (keySystem == DRM::KS_WIDEVINE) return STREAM_CRYPTO_KEY_SYSTEM_WIDEVINE; @@ -1318,7 +1344,7 @@ int CSession::GetChapter() const return -1; } -int CSession::GetChapterCount() const +int SESSION::CSession::GetChapterCount() const { if (m_adaptiveTree && m_adaptiveTree->m_periods.size() > 1) return static_cast(m_adaptiveTree->m_periods.size()); @@ -1326,7 +1352,7 @@ int CSession::GetChapterCount() const return 0; } -std::string CSession::GetChapterName(int ch) const +std::string SESSION::CSession::GetChapterName(int ch) const { if (m_adaptiveTree) { @@ -1338,7 +1364,7 @@ std::string CSession::GetChapterName(int ch) const return "[Unknown]"; } -int64_t CSession::GetChapterPos(int ch) const +int64_t SESSION::CSession::GetChapterPos(int ch) const { int64_t sum{0}; --ch; @@ -1352,7 +1378,7 @@ int64_t CSession::GetChapterPos(int ch) const return sum / STREAM_TIME_BASE; } -uint64_t CSession::GetChapterStartTime() const +uint64_t SESSION::CSession::GetChapterStartTime() const { uint64_t start_time = 0; for (std::unique_ptr& p : m_adaptiveTree->m_periods) @@ -1365,7 +1391,7 @@ uint64_t CSession::GetChapterStartTime() const return start_time; } -int CSession::GetPeriodId() const +int SESSION::CSession::GetPeriodId() const { if (m_adaptiveTree) { @@ -1384,7 +1410,7 @@ int CSession::GetPeriodId() const return -1; } -bool CSession::SeekChapter(int ch) +bool SESSION::CSession::SeekChapter(int ch) { if (m_adaptiveTree->IsChangingPeriod()) return true; @@ -1412,10 +1438,10 @@ bool CSession::SeekChapter(int ch) return false; } -void CSession::ExtractStreamProtectionData(const PLAYLIST::CPeriod::PSSHSet& psshSet, - std::string& defaultKid, - std::vector& initData, - const std::vector& keySystems) +void SESSION::CSession::ExtractStreamProtectionData(const PLAYLIST::CPeriod::PSSHSet& psshSet, + std::string& defaultKid, + std::vector& initData, + const std::vector& keySystems) { auto initialRepr = m_reprChooser->GetRepresentation(psshSet.adaptation_set_); diff --git a/src/Session.h b/src/Session.h index ebf435404..05dedf034 100644 --- a/src/Session.h +++ b/src/Session.h @@ -318,6 +318,14 @@ class ATTR_DLL_LOCAL CSession : public adaptive::AdaptiveStreamObserver */ void OnStreamChange(adaptive::AdaptiveStream* adStream) override; + /*! + * \brief Callback from CInputStreamAdaptive::GetStream. + * \param streamid The requested stream id + * \param info The stream info object (can be updated) + * \return True to allow Kodi core to load the stream, otherwise false + */ + bool OnGetStream(int streamid, kodi::addon::InputstreamInfo& info); + protected: /*! \brief Check for and load decrypter module matching the supplied key system * \param key_system [OUT] Will be assigned to if a decrypter is found matching diff --git a/src/main.cpp b/src/main.cpp index a6ec67eea..ea35b4048 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -127,38 +127,13 @@ void CInputStreamAdaptive::GetCapabilities(kodi::addon::InputstreamCapabilities& bool CInputStreamAdaptive::GetStream(int streamid, kodi::addon::InputstreamInfo& info) { + // GetStream is called by Kodi twice times, before and after OpenStream. LOG::Log(LOGDEBUG, "GetStream(%d)", streamid); - CStream* stream(m_session->GetStream(streamid - m_session->GetPeriodId() * 1000)); - - 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; - } - - return false; + // Return false prevents this stream from loading, but does not stop playback, + // Kodi core will continue to request another stream of same type (a/v) + // as long as one is successful + return m_session->OnGetStream(streamid, info); } void CInputStreamAdaptive::UnlinkIncludedStreams(CStream* stream)