diff --git a/src/Session.cpp b/src/Session.cpp index 12574ce2b..085392961 100644 --- a/src/Session.cpp +++ b/src/Session.cpp @@ -287,7 +287,9 @@ bool CSession::Initialize() m_adaptiveTree->PostOpen(m_kodiProps); - return InitializePeriod(isSessionOpened); + bool isPeriodInit = InitializePeriod(isSessionOpened); + m_reprChooser->PostInit(); + return isPeriodInit; } void CSession::CheckHDCP() @@ -353,7 +355,7 @@ bool CSession::PreInitializeDRM(std::string& challengeB64, } m_cdmSessions.resize(2); - memset(&m_cdmSessions.front(), 0, sizeof(CCdmSession)); + // Try to initialize an SingleSampleDecryptor LOG::LogF(LOGDEBUG, "Entering encryption section"); @@ -423,7 +425,6 @@ bool CSession::InitializeDRM(bool addDefaultKID /* = false */) { bool isSecureVideoSession{false}; m_cdmSessions.resize(m_adaptiveTree->m_currentPeriod->GetPSSHSets().size()); - memset(&m_cdmSessions.front(), 0, sizeof(CCdmSession)); // Try to initialize an SingleSampleDecryptor if (m_adaptiveTree->m_currentPeriod->GetEncryptionState() != @@ -485,8 +486,7 @@ bool CSession::InitializeDRM(bool addDefaultKID /* = false */) { auto initialRepr{m_reprChooser->GetRepresentation(sessionPsshset.adaptation_set_)}; - CStream stream{*m_adaptiveTree, sessionPsshset.adaptation_set_, initialRepr, m_kodiProps, - false}; + CStream stream{*m_adaptiveTree, sessionPsshset.adaptation_set_, initialRepr, m_kodiProps}; stream.m_isEnabled = true; stream.m_adStream.start_stream(); @@ -682,18 +682,20 @@ bool CSession::InitializeDRM(bool addDefaultKID /* = false */) CheckHDCP(); m_reprChooser->SetSecureSession(isSecureVideoSession); - m_reprChooser->PostInit(); return true; } bool CSession::InitializePeriod(bool isSessionOpened /* = false */) { - bool psshChanged{true}; + bool isPsshChanged{true}; + bool isReusePssh{true}; if (m_adaptiveTree->m_nextPeriod) { - psshChanged = + isPsshChanged = !(m_adaptiveTree->m_currentPeriod->GetPSSHSets() == m_adaptiveTree->m_nextPeriod->GetPSSHSets()); + isReusePssh = !isPsshChanged && m_adaptiveTree->m_nextPeriod->GetEncryptionState() == + EncryptionState::ENCRYPTED_SUPPORTED; m_adaptiveTree->m_currentPeriod = m_adaptiveTree->m_nextPeriod; m_adaptiveTree->m_nextPeriod = nullptr; } @@ -709,8 +711,11 @@ bool CSession::InitializePeriod(bool isSessionOpened /* = false */) // create SESSION::STREAM objects. One for each AdaptationSet m_streams.clear(); - if (!psshChanged) - LOG::Log(LOGDEBUG, "Reusing DRM psshSets for new period!"); + if (!isPsshChanged) + { + if (isReusePssh) + LOG::Log(LOGDEBUG, "Reusing DRM psshSets for new period!"); + } else { if (isSessionOpened) @@ -771,7 +776,6 @@ bool CSession::InitializePeriod(bool isSessionOpened /* = false */) } } - m_firstPeriodInitialized = true; return true; } @@ -780,8 +784,7 @@ void CSession::AddStream(PLAYLIST::CAdaptationSet* adp, bool isDefaultRepr, uint32_t uniqueId) { - m_streams.push_back(std::make_unique(*m_adaptiveTree, adp, initialRepr, m_kodiProps, - m_firstPeriodInitialized)); + m_streams.push_back(std::make_unique(*m_adaptiveTree, adp, initialRepr, m_kodiProps)); CStream& stream{*m_streams.back()}; @@ -1225,7 +1228,7 @@ bool CSession::GetNextSample(ISampleReader*& sampleReader) { // Once the start PTS has been acquired for the timing stream, set this value // to the other stream readers - if (stream.get() != timingStream && + if (timingStream && stream.get() != timingStream && timingStream->GetReader()->GetStartPTS() != STREAM_NOPTS_VALUE && streamReader->GetStartPTS() == STREAM_NOPTS_VALUE) { diff --git a/src/Session.h b/src/Session.h index 78aec67f9..4d2e0f37c 100644 --- a/src/Session.h +++ b/src/Session.h @@ -364,9 +364,9 @@ class ATTR_DLL_LOCAL CSession : public adaptive::AdaptiveStreamObserver struct CCdmSession { - SSD::SSD_DECRYPTER::SSD_CAPS m_decrypterCaps; - Adaptive_CencSingleSampleDecrypter* m_cencSingleSampleDecrypter; - const char* m_cdmSessionStr = nullptr; + SSD::SSD_DECRYPTER::SSD_CAPS m_decrypterCaps{}; + Adaptive_CencSingleSampleDecrypter* m_cencSingleSampleDecrypter{nullptr}; + const char* m_cdmSessionStr{nullptr}; bool m_sharedCencSsd{false}; }; std::vector m_cdmSessions; @@ -385,7 +385,6 @@ class ATTR_DLL_LOCAL CSession : public adaptive::AdaptiveStreamObserver uint8_t m_drmConfig{0}; bool m_settingNoSecureDecoder{false}; bool m_settingIsHdcpOverride{false}; - bool m_firstPeriodInitialized{false}; std::unique_ptr m_KodiHost; }; } // namespace SESSION diff --git a/src/Stream.h b/src/Stream.h index 783669897..66952113f 100644 --- a/src/Stream.h +++ b/src/Stream.h @@ -23,12 +23,11 @@ class ATTR_DLL_LOCAL CStream CStream(adaptive::AdaptiveTree& tree, PLAYLIST::CAdaptationSet* adp, PLAYLIST::CRepresentation* initialRepr, - const UTILS::PROPERTIES::KodiProperties& kodiProps, - bool chooseRep) + const UTILS::PROPERTIES::KodiProperties& kodiProps) : m_isEnabled{false}, m_isEncrypted{false}, m_mainId{0}, - m_adStream{tree, adp, initialRepr, kodiProps, chooseRep}, + m_adStream{tree, adp, initialRepr, kodiProps}, m_hasSegmentChanged{false}, m_isValid{true} {}; diff --git a/src/common/AdaptiveStream.cpp b/src/common/AdaptiveStream.cpp index e24e1a7b3..d87e9eeb5 100644 --- a/src/common/AdaptiveStream.cpp +++ b/src/common/AdaptiveStream.cpp @@ -36,8 +36,7 @@ uint32_t AdaptiveStream::globalClsId = 0; AdaptiveStream::AdaptiveStream(AdaptiveTree& tree, PLAYLIST::CAdaptationSet* adp, PLAYLIST::CRepresentation* initialRepr, - const UTILS::PROPERTIES::KodiProperties& kodiProps, - bool choose_rep) + const UTILS::PROPERTIES::KodiProperties& kodiProps) : thread_data_(nullptr), tree_(tree), observer_(nullptr), @@ -53,9 +52,6 @@ AdaptiveStream::AdaptiveStream(AdaptiveTree& tree, m_fixateInitialization(false), m_segmentFileOffset(0), play_timeshift_buffer_(kodiProps.m_playTimeshiftBuffer), - choose_rep_(choose_rep), - rep_counter_(1), - prev_rep_(0), last_rep_(0) { current_rep_->current_segment_ = nullptr; @@ -583,17 +579,6 @@ bool AdaptiveStream::start_stream() if (!current_rep_) return false; - if (choose_rep_) - { - choose_rep_ = false; - current_rep_ = tree_.GetRepChooser()->GetRepresentation(current_adp_); - } - - if (!current_rep_->IsPrepared()) - { - tree_.prepareRepresentation(current_period_, current_adp_, current_rep_, false); - } - //! @todo: the assured_buffer_duration_ and max_buffer_duration_ //! isnt implemeted correctly and need to be reworked, //! these properties are intended to determine the amount of buffer @@ -807,14 +792,16 @@ bool AdaptiveStream::ensureSegment() stream_changed_ = false; CSegment* nextSegment{nullptr}; last_rep_ = current_rep_; - if (valid_segment_buffers_) + + if (valid_segment_buffers_ > 0) { - // rotate element 0 to the end + // Move the segment at initial position 0 to the end, because consumed std::rotate(segment_buffers_.begin(), segment_buffers_.begin() + 1, segment_buffers_.begin() + available_segment_buffers_); --valid_segment_buffers_; --available_segment_buffers_; - + // Check if next segment use same representation of previous one + // if not, update the current representation if (segment_buffers_[0]->rep != current_rep_) { current_rep_->SetIsEnabled(false); @@ -823,7 +810,7 @@ bool AdaptiveStream::ensureSegment() stream_changed_ = true; } } - if (valid_segment_buffers_) + if (valid_segment_buffers_ > 0) { if (!segment_buffers_[0]->segment.IsInitialization()) { @@ -834,14 +821,6 @@ bool AdaptiveStream::ensureSegment() else nextSegment = current_rep_->get_next_segment(current_rep_->current_segment_); - if(prev_rep_== current_rep_) - rep_counter_++; - else - { - rep_counter_=1; - prev_rep_=current_rep_; - } - if (nextSegment) { currentPTSOffset_ = @@ -862,37 +841,26 @@ bool AdaptiveStream::ensureSegment() size_t nextsegmentPosold = current_rep_->get_segment_pos(nextSegment); uint64_t nextsegno = current_rep_->getSegmentNumber(nextSegment); - CRepresentation* newRep{nullptr}; - bool lastSeg{false}; - if (current_period_ != tree_.m_periods.back().get()) - { - if (nextsegmentPosold + available_segment_buffers_ == - current_rep_->SegmentTimeline().GetSize() - 1) - { - lastSeg = true; - } - } - - if (segment_buffers_[0]->segment.IsInitialization() || - valid_segment_buffers_ == 0 || - current_adp_->GetStreamType() != StreamType::VIDEO) - { - newRep = current_rep_; - } - else if (lastSeg) // Don't change reps on last segment of period, use the rep of preceeding seg - { - newRep = segment_buffers_[valid_segment_buffers_ - 1]->rep; - } - else + CRepresentation* newRep = current_rep_; + bool isBufferFull = valid_segment_buffers_ >= max_buffer_length_; + + if (!segment_buffers_[0]->segment.IsInitialization() && available_segment_buffers_ > 0 && + !isBufferFull) // Defer until we have some free buffer { - // Defer until we have some free buffer - if (available_segment_buffers_ < max_buffer_length_) { - newRep = tree_.GetRepChooser()->GetNextRepresentation( - current_adp_, segment_buffers_[available_segment_buffers_ - 1]->rep); - } + // The representation from the last added segment buffer + CRepresentation* prevRep = segment_buffers_[available_segment_buffers_ - 1]->rep; + + bool isLastSegment = nextsegmentPosold + available_segment_buffers_ == + current_rep_->SegmentTimeline().GetSize() - 1; + // Dont change representation if it is the last segment of a period otherwise when it comes + // the time to play the last segment in a period, AdaptiveStream wasn't able to insert the + // initialization segment (in the case of fMP4) and you would get corrupted or blank video + // for the last segment + if (isLastSegment) + newRep = prevRep; else - newRep = current_rep_; + newRep = tree_.GetRepChooser()->GetNextRepresentation(current_adp_, prevRep); } // If the representation has been changed, segments may have to be generated (DASH) @@ -905,6 +873,8 @@ bool AdaptiveStream::ensureSegment() current_period_, current_adp_, newRep, tree_.IsLive()); } + // Add to the buffer next future segment + size_t nextsegmentPos = static_cast(nextsegno - newRep->GetStartNumber()); if (nextsegmentPos + available_segment_buffers_ >= newRep->SegmentTimeline().GetSize()) { diff --git a/src/common/AdaptiveStream.h b/src/common/AdaptiveStream.h index 3a189a37b..fdf32a392 100644 --- a/src/common/AdaptiveStream.h +++ b/src/common/AdaptiveStream.h @@ -38,8 +38,7 @@ class AdaptiveStream; AdaptiveStream(AdaptiveTree& tree, PLAYLIST::CAdaptationSet* adpSet, PLAYLIST::CRepresentation* initialRepr, - const UTILS::PROPERTIES::KodiProperties& kodiProps, - bool choose_rep_); + const UTILS::PROPERTIES::KodiProperties& kodiProps); virtual ~AdaptiveStream(); void set_observer(AdaptiveStreamObserver *observer){ observer_ = observer; }; void Reset(); @@ -224,14 +223,14 @@ class AdaptiveStream; // We need to store here because linked to representation uint8_t m_decrypterIv[16]; - // number of segmentbuffers whith valid segment, always >= valid_segment_buffers_ - size_t available_segment_buffers_{0}; - // number of segment_buffers which are downloaded / downloading + // Minimum segment buffer size (segment_buffers_) uint32_t assured_buffer_length_{0}; + // The segment buffer size (segment_buffers_), so the max number of segments that can be downloaded and stored in memory uint32_t max_buffer_length_{0}; + // Number of segments stored in segment buffer (segment_buffers_) queued for downloading, always >= valid_segment_buffers_ + size_t available_segment_buffers_{0}; + // Number of segments stored in segment buffer (segment_buffers_) currently in download and downloaded size_t valid_segment_buffers_{0}; - uint32_t rep_counter_; - PLAYLIST::CRepresentation* prev_rep_; // used for rep_counter_ PLAYLIST::CRepresentation* last_rep_; // used to align new live rep with old std::size_t segment_read_pos_; @@ -243,7 +242,6 @@ class AdaptiveStream; uint64_t m_segmentFileOffset; bool play_timeshift_buffer_; bool stream_changed_ = false; - bool choose_rep_; // Class ID for debug log purpose, allow the LOG prints of each AdaptiveStream to be distinguished uint32_t clsId; diff --git a/src/common/Chooser.h b/src/common/Chooser.h index f185a0f92..d0b259652 100644 --- a/src/common/Chooser.h +++ b/src/common/Chooser.h @@ -36,7 +36,7 @@ class ATTR_DLL_LOCAL IRepresentationChooser virtual void Initialize(const UTILS::PROPERTIES::ChooserProps& props) {} /*! - * \brief Post initialization, will be called just after DRM initialization + * \brief Post initialization, will be called after the session initialization */ virtual void PostInit() {} @@ -72,7 +72,7 @@ class ATTR_DLL_LOCAL IRepresentationChooser } /*! - * \brief Set if the DRM use a secure session + * \brief Called at each DRM initialization to set if the secure session is currently being used. * \param isSecureSession Set true if a secure session is in use */ virtual void SetSecureSession(const bool isSecureSession) { m_isSecureSession = isSecureSession; } diff --git a/src/common/ChooserDefault.cpp b/src/common/ChooserDefault.cpp index 364b3ee07..fcfcf0046 100644 --- a/src/common/ChooserDefault.cpp +++ b/src/common/ChooserDefault.cpp @@ -89,6 +89,12 @@ void CRepresentationChooserDefault::Initialize(const UTILS::PROPERTIES::ChooserP m_ignoreScreenResChange); } +void CRepresentationChooserDefault::SetSecureSession(const bool isSecureSession) +{ + m_isSecureSession = isSecureSession; + RefreshResolution(); +} + void CRepresentationChooserDefault::PostInit() { RefreshResolution(); @@ -114,21 +120,28 @@ void CRepresentationChooserDefault::PostInit() m_screenWidth, m_screenHeight, m_bandwidthCurrent); } -void CRepresentationChooserDefault::RefreshResolution() +void CRepresentationChooserDefault::CheckResolution() { - if (m_screenWidth == m_screenCurrentWidth && m_screenHeight == m_screenCurrentHeight) - return; - - // Update the screen resolution values only after n seconds - // to prevent too fast update when Kodi window will be resized - if (m_screenResLastUpdate.has_value() && - std::chrono::duration_cast(std::chrono::steady_clock::now() - - *m_screenResLastUpdate) - .count() < SCREEN_RES_REFRESH_SECS) + if (m_screenWidth != m_screenCurrentWidth || m_screenHeight != m_screenCurrentHeight) { - return; + // Update the screen resolution values only after n seconds + // to prevent too fast update when Kodi window will be resized + if (m_screenResLastUpdate.has_value() && + std::chrono::duration_cast(std::chrono::steady_clock::now() - + *m_screenResLastUpdate) + .count() < SCREEN_RES_REFRESH_SECS) + { + return; + } + RefreshResolution(); + m_screenResLastUpdate = std::chrono::steady_clock::now(); + LOG::Log(LOGDEBUG, "[Repr. chooser] Screen resolution has changed: %ix%i", m_screenCurrentWidth, + m_screenCurrentHeight); } +} +void CRepresentationChooserDefault::RefreshResolution() +{ m_screenWidth = m_ignoreScreenRes ? 16384 : m_screenCurrentWidth; m_screenHeight = m_ignoreScreenRes ? 16384 : m_screenCurrentHeight; @@ -143,10 +156,6 @@ void CRepresentationChooserDefault::RefreshResolution() if (m_screenHeight > userResLimit.second) m_screenHeight = userResLimit.second; } - - LOG::Log(LOGDEBUG, "[Repr. chooser] Screen resolution has changed: %ix%i", m_screenCurrentWidth, - m_screenCurrentHeight); - m_screenResLastUpdate = std::chrono::steady_clock::now(); } void CRepresentationChooserDefault::SetDownloadSpeed(const double speed) @@ -179,25 +188,21 @@ void CRepresentationChooserDefault::SetDownloadSpeed(const double speed) PLAYLIST::CRepresentation* CRepresentationChooserDefault::GetNextRepresentation( PLAYLIST::CAdaptationSet* adp, PLAYLIST::CRepresentation* currentRep) { - if (!m_ignoreScreenRes && !m_ignoreScreenResChange) - RefreshResolution(); + bool isVideoStreamType = adp->GetStreamType() == StreamType::VIDEO; + + if (isVideoStreamType && !m_ignoreScreenRes && !m_ignoreScreenResChange) + CheckResolution(); CRepresentationSelector selector(m_screenWidth, m_screenHeight); uint32_t bandwidth; // From bandwidth take in consideration: // 90% of bandwidth for video - 10 % for other - if (adp->GetStreamType() == StreamType::VIDEO) + if (isVideoStreamType) bandwidth = static_cast(m_bandwidthCurrentLimited * 0.9); else bandwidth = static_cast(m_bandwidthCurrentLimited * 0.1); - if (adp->GetStreamType() == StreamType::VIDEO) // To avoid fill too much the log - { - LOG::Log(LOGDEBUG, "[Repr. chooser] Current average bandwidth: %u bit/s (filtered to %u bit/s)", - m_bandwidthCurrent, bandwidth); - } - CRepresentation* nextRep{nullptr}; int bestScore{-1}; @@ -223,8 +228,12 @@ PLAYLIST::CRepresentation* CRepresentationChooserDefault::GetNextRepresentation( if (!nextRep) nextRep = selector.Lowest(adp); - if (adp->GetStreamType() == StreamType::VIDEO) + if (isVideoStreamType) // Only video, to avoid fill too much the log + { + LOG::Log(LOGDEBUG, "[Repr. chooser] Current average bandwidth: %u bit/s (filtered to %u bit/s)", + m_bandwidthCurrent, bandwidth); LogDetails(currentRep, nextRep); + } if (m_isForceStartsMaxRes) m_isForceStartsMaxRes = false; diff --git a/src/common/ChooserDefault.h b/src/common/ChooserDefault.h index 21966e34b..80ac8aa1e 100644 --- a/src/common/ChooserDefault.h +++ b/src/common/ChooserDefault.h @@ -28,6 +28,7 @@ class ATTR_DLL_LOCAL CRepresentationChooserDefault : public IRepresentationChoos ~CRepresentationChooserDefault() override {} virtual void Initialize(const UTILS::PROPERTIES::ChooserProps& props) override; + virtual void SetSecureSession(const bool isSecureSession) override; virtual void PostInit() override; void SetDownloadSpeed(const double speed) override; @@ -36,6 +37,11 @@ class ATTR_DLL_LOCAL CRepresentationChooserDefault : public IRepresentationChoos PLAYLIST::CRepresentation* currentRep) override; protected: + /*! + * \brief Check if screen resolution is changed, if so will refresh values + */ + void CheckResolution(); + /*! * \brief Refresh screen resolution values from the current */ diff --git a/src/test/TestDASHTree.cpp b/src/test/TestDASHTree.cpp index 94d4bd2a2..f7327e7b2 100644 --- a/src/test/TestDASHTree.cpp +++ b/src/test/TestDASHTree.cpp @@ -114,7 +114,7 @@ class DASHTreeAdaptiveStreamTest : public DASHTreeTest UTILS::PROPERTIES::KodiProperties kodiProps; kodiProps.m_playTimeshiftBuffer = playTimeshiftBuffer; - return new TestAdaptiveStream(*tree, adp, initialRepr, kodiProps, false); + return new TestAdaptiveStream(*tree, adp, initialRepr, kodiProps); } void ReadSegments(TestAdaptiveStream* stream, diff --git a/src/test/TestHelper.h b/src/test/TestHelper.h index bbcb8add2..904c197b6 100644 --- a/src/test/TestHelper.h +++ b/src/test/TestHelper.h @@ -61,9 +61,8 @@ class TestAdaptiveStream : public adaptive::AdaptiveStream TestAdaptiveStream(adaptive::AdaptiveTree& tree, PLAYLIST::CAdaptationSet* adp, PLAYLIST::CRepresentation* initialRepr, - const UTILS::PROPERTIES::KodiProperties& kodiProps, - bool choose_rep) - : adaptive::AdaptiveStream(tree, adp, initialRepr, kodiProps, choose_rep) + const UTILS::PROPERTIES::KodiProperties& kodiProps) + : adaptive::AdaptiveStream(tree, adp, initialRepr, kodiProps) { }