diff --git a/src/Session.cpp b/src/Session.cpp index 1a5e07125..7f3cfe1b0 100644 --- a/src/Session.cpp +++ b/src/Session.cpp @@ -938,7 +938,7 @@ void CSession::PrepareStream(CStream* stream) // Prepare the representation when the period change usually its not needed, // because the timeline is always already updated - if ((!m_adaptiveTree->IsChangingPeriod() || !repr->HasSegmentTimeline()) && + if ((!m_adaptiveTree->IsChangingPeriod() || repr->Timeline().IsEmpty()) && (startEvent == EVENT_TYPE::STREAM_START || startEvent == EVENT_TYPE::STREAM_ENABLE)) { m_adaptiveTree->PrepareRepresentation(stream->m_adStream.getPeriod(), diff --git a/src/common/AdaptationSet.cpp b/src/common/AdaptationSet.cpp index 5e525ea6c..ae110bf15 100644 --- a/src/common/AdaptationSet.cpp +++ b/src/common/AdaptationSet.cpp @@ -13,6 +13,7 @@ #include "utils/Utils.h" #include // any_of +#include // back_inserter using namespace PLAYLIST; using namespace UTILS; @@ -53,7 +54,7 @@ void PLAYLIST::CAdaptationSet::AddRepresentation( std::vector PLAYLIST::CAdaptationSet::GetRepresentationsPtr() { std::vector ptrReprs; - std::transform(m_representations.begin(), m_representations.end(), back_inserter(ptrReprs), + std::transform(m_representations.begin(), m_representations.end(), std::back_inserter(ptrReprs), [](const std::unique_ptr& r) { return r.get(); }); return ptrReprs; } diff --git a/src/common/AdaptationSet.h b/src/common/AdaptationSet.h index eb3d94dd8..7e23989a7 100644 --- a/src/common/AdaptationSet.h +++ b/src/common/AdaptationSet.h @@ -79,8 +79,8 @@ class ATTR_DLL_LOCAL CAdaptationSet : public CCommonSegAttribs, public CCommonAt void AddSwitchingIds(std::string_view switchingIds); const std::vector& GetSwitchingIds() { return m_switchingIds; } - CSpinCache& SegmentTimelineDuration() { return m_segmentTimelineDuration; } - bool HasSegmentTimelineDuration() { return !m_segmentTimelineDuration.IsEmpty(); } + std::vector& SegmentTimelineDuration() { return m_segmentTimelineDuration; } + bool HasSegmentTimelineDuration() { return !m_segmentTimelineDuration.empty(); } /*! * \brief Get the timescale of segment durations tag. @@ -175,7 +175,7 @@ class ATTR_DLL_LOCAL CAdaptationSet : public CCommonSegAttribs, public CCommonAt std::string m_language; std::vector m_switchingIds; - CSpinCache m_segmentTimelineDuration; + std::vector m_segmentTimelineDuration; uint64_t m_segDurationsTimescale{NO_VALUE}; // Custom ISAdaptive attributes (used on DASH only) diff --git a/src/common/AdaptiveStream.cpp b/src/common/AdaptiveStream.cpp index cd5ecf193..6cb02c307 100644 --- a/src/common/AdaptiveStream.cpp +++ b/src/common/AdaptiveStream.cpp @@ -476,15 +476,10 @@ bool AdaptiveStream::parseIndexRange(PLAYLIST::CRepresentation* rep, seg.m_time = cue.pts; seg.range_begin_ = cue.pos_start; seg.range_end_ = cue.pos_end; - rep->SegmentTimeline().GetData().emplace_back(seg); - - //! todo: use SegmentTimelineDuration should not be needed - if (adpSet->SegmentTimelineDuration().GetSize() < rep->SegmentTimeline().GetSize()) - { - adpSet->SegmentTimelineDuration().GetData().emplace_back( - static_cast(cue.duration)); - } + rep->Timeline().Add(seg); } + + rep->SetDuration(rep->Timeline().GetDuration()); return true; } } @@ -502,7 +497,6 @@ bool AdaptiveStream::parseIndexRange(PLAYLIST::CRepresentation* rep, bool isMoovFound{false}; AP4_Cardinal sidxCount{1}; - uint64_t reprDuration{0}; CSegment seg; seg.startPTS_ = 0; @@ -548,18 +542,11 @@ bool AdaptiveStream::parseIndexRange(PLAYLIST::CRepresentation* rep, { seg.range_begin_ = seg.range_end_ + 1; seg.range_end_ = seg.range_begin_ + refs[i].m_ReferencedSize - 1; - rep->SegmentTimeline().GetData().emplace_back(seg); - - //! todo: use SegmentTimelineDuration should not be needed - if (adpSet->SegmentTimelineDuration().GetSize() < rep->SegmentTimeline().GetSize()) - { - adpSet->SegmentTimelineDuration().GetData().emplace_back(refs[i].m_SubsegmentDuration); - } + rep->Timeline().Add(seg); seg.startPTS_ += refs[i].m_SubsegmentDuration; seg.m_endPts = seg.startPTS_ + refs[i].m_SubsegmentDuration; seg.m_time += refs[i].m_SubsegmentDuration; - reprDuration += refs[i].m_SubsegmentDuration; } sidxCount--; @@ -589,7 +576,7 @@ bool AdaptiveStream::parseIndexRange(PLAYLIST::CRepresentation* rep, rep->SetInitSegment(initSeg); } - rep->SetDuration(reprDuration); + rep->SetDuration(rep->Timeline().GetDuration()); return true; } @@ -644,7 +631,7 @@ bool AdaptiveStream::start_stream(const uint64_t startPts) thread_data_->signal_dl_.wait(lckdl); } - if (current_rep_->SegmentTimeline().IsEmpty()) + if (current_rep_->Timeline().IsEmpty()) { // GenerateSidxSegments assumes mutex_dl locked std::lock_guard lck(thread_data_->mutex_dl_); @@ -679,12 +666,12 @@ bool AdaptiveStream::start_stream(const uint64_t startPts) { if (m_startEvent == EVENT_TYPE::STREAM_START && m_tree->IsLive() && !m_tree->IsChangingPeriod() && !CSrvBroker::GetKodiProps().IsPlayTimeshift() && - !current_rep_->SegmentTimeline().IsEmpty()) + !current_rep_->Timeline().IsEmpty()) { - size_t segPos = current_rep_->SegmentTimeline().GetSize() - 1; + size_t segPos = current_rep_->Timeline().GetSize() - 1; //! @todo: segment duration is not fixed for each segment, this can calculate a wrong delay - uint64_t segDur = current_rep_->get_segment(segPos)->m_endPts - - current_rep_->get_segment(segPos)->startPTS_; + const CSegment* lastSeg = current_rep_->Timeline().GetBack(); + uint64_t segDur = lastSeg->m_endPts - lastSeg->startPTS_; size_t segPosDelay = static_cast((m_tree->m_liveDelay * current_rep_->GetTimescale()) / segDur); @@ -698,16 +685,16 @@ bool AdaptiveStream::start_stream(const uint64_t startPts) segPos = 0; } - current_rep_->current_segment_ = current_rep_->get_segment(segPos); + current_rep_->current_segment_ = current_rep_->Timeline().Get(segPos); } else if (m_startEvent == EVENT_TYPE::REP_CHANGE) // switching streams, align new stream segment no. { uint64_t segmentId = segment_buffers_[0]->segment_number; - if (segmentId >= current_rep_->GetStartNumber() + current_rep_->SegmentTimeline().GetSize()) + if (segmentId >= current_rep_->GetStartNumber() + current_rep_->Timeline().GetSize()) { - segmentId = current_rep_->GetStartNumber() + current_rep_->SegmentTimeline().GetSize() - 1; + segmentId = current_rep_->GetStartNumber() + current_rep_->Timeline().GetSize() - 1; } - current_rep_->current_segment_ = current_rep_->get_segment( + current_rep_->current_segment_ = current_rep_->Timeline().Get( static_cast(segmentId - current_rep_->GetStartNumber())); } else @@ -719,8 +706,7 @@ bool AdaptiveStream::start_stream(const uint64_t startPts) // Reset the event for the next one m_startEvent = EVENT_TYPE::NONE; - const CSegment* next_segment = - current_rep_->get_next_segment(current_rep_->current_segment_); + const CSegment* next_segment = current_rep_->GetNextSegment(); if (!next_segment && current_adp_->GetStreamType() != StreamType::SUBTITLE) { @@ -765,7 +751,7 @@ bool AdaptiveStream::start_stream(const uint64_t startPts) valid_segment_buffers_ = valid_segment_buffers + 1; } - if (!current_rep_->SegmentTimeline().Get(0)) + if (!current_rep_->Timeline().Get(0)) { LOG::LogF(LOGERROR, "[AS-%u] Segment at position 0 not found from representation id: %s", clsId, current_rep_->GetId().data()); @@ -777,7 +763,7 @@ bool AdaptiveStream::start_stream(const uint64_t startPts) currentPTSOffset_ = (next_segment->startPTS_ * current_rep_->timescale_ext_) / current_rep_->timescale_int_; absolutePTSOffset_ = - (current_rep_->SegmentTimeline().Get(0)->startPTS_ * current_rep_->timescale_ext_) / + (current_rep_->Timeline().Get(0)->startPTS_ * current_rep_->timescale_ext_) / current_rep_->timescale_int_; } @@ -823,7 +809,7 @@ bool AdaptiveStream::ensureSegment() if (m_fixateInitialization) return false; - CSegment* nextSegment{nullptr}; + const CSegment* nextSegment{nullptr}; if (valid_segment_buffers_ > 0) { @@ -857,11 +843,11 @@ bool AdaptiveStream::ensureSegment() if (!segment_buffers_[0]->segment.IsInitialization()) { // Search the same segment on the timeline (which in the meantime may have been updated) - nextSegment = current_rep_->GetSegment(segment_buffers_[0]->segment); + nextSegment = current_rep_->Timeline().Find(segment_buffers_[0]->segment); } } else - nextSegment = current_rep_->get_next_segment(current_rep_->current_segment_); + nextSegment = current_rep_->GetNextSegment(); if (!nextSegment && (m_tree->HasManifestUpdates() || m_tree->HasManifestUpdatesSegs()) && !m_tree->IsLastSegment(current_period_, current_rep_, current_rep_->current_segment_)) @@ -873,7 +859,7 @@ bool AdaptiveStream::ensureSegment() getSegmentPos())) { //! @todo: seem to be possible get the segment from InsertLiveSegment and then avoid call get_next_segment - nextSegment = current_rep_->get_next_segment(current_rep_->current_segment_); + nextSegment = current_rep_->GetNextSegment(); } if (!nextSegment && !current_rep_->IsWaitForSegment()) @@ -892,7 +878,7 @@ bool AdaptiveStream::ensureSegment() (nextSegment->startPTS_ * current_rep_->timescale_ext_) / current_rep_->timescale_int_; absolutePTSOffset_ = - (current_rep_->SegmentTimeline().Get(0)->startPTS_ * current_rep_->timescale_ext_) / + (current_rep_->Timeline().Get(0)->startPTS_ * current_rep_->timescale_ext_) / current_rep_->timescale_int_; current_rep_->current_segment_ = nextSegment; @@ -904,7 +890,7 @@ bool AdaptiveStream::ensureSegment() observer_->OnSegmentChanged(this); } - const size_t nextSegPos = current_rep_->get_segment_pos(nextSegment); + const size_t nextSegPos = current_rep_->Timeline().GetPos(nextSegment); CRepresentation* newRep = current_rep_; bool isBufferFull = valid_segment_buffers_ >= max_buffer_length_; @@ -916,7 +902,7 @@ bool AdaptiveStream::ensureSegment() CRepresentation* prevRep = segment_buffers_[available_segment_buffers_ - 1]->rep; bool isLastSegment = nextSegPos + available_segment_buffers_ == - current_rep_->SegmentTimeline().GetSize() - 1; + current_rep_->Timeline().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 @@ -937,24 +923,24 @@ bool AdaptiveStream::ensureSegment() m_tree->OnStreamChange(current_period_, current_adp_, current_rep_, newRep); // If the representation has been changed, segments may have to be generated (DASH) - if (newRep->SegmentTimeline().IsEmpty()) + if (newRep->Timeline().IsEmpty()) GenerateSidxSegments(newRep); } } // Add to the buffer next segment (and the next following segments, if available) - const size_t maxPos = newRep->SegmentTimeline().GetSize(); + const size_t maxPos = newRep->Timeline().GetSize(); size_t segPos; if (available_segment_buffers_ == 0) // Buffer empty, add the current segment segPos = nextSegPos; else // Continue adding segments that follow the last one added in to the buffer { - CSegment* followSeg = - current_rep_->GetNextSegment(segment_buffers_[available_segment_buffers_ - 1]->segment); + const CSegment* followSeg = + current_rep_->Timeline().GetNext(&segment_buffers_[available_segment_buffers_ - 1]->segment); if (followSeg) - segPos = current_rep_->get_segment_pos(followSeg); + segPos = current_rep_->Timeline().GetPos(followSeg); else // No segment, EOS or you need to wait next manifest update segPos = maxPos; } @@ -964,10 +950,10 @@ bool AdaptiveStream::ensureSegment() //! this can cause a bad initial buffering because the stream dont fit the bandwidth. for (size_t index = available_segment_buffers_; index < max_buffer_length_; ++index) { - if (segPos == maxPos) // To avoid out-of-range log prints with get_segment + if (segPos == maxPos) // To avoid out-of-range log prints with Timeline().Get break; - const CSegment* futureSegment = newRep->get_segment(segPos); + const CSegment* futureSegment = newRep->Timeline().Get(segPos); if (futureSegment) { segment_buffers_[index]->segment = *futureSegment; @@ -1108,19 +1094,19 @@ bool AdaptiveStream::retrieveCurrentSegmentBufferSize(size_t& size) uint64_t AdaptiveStream::getMaxTimeMs() { - if (current_rep_->SegmentTimeline().IsEmpty()) + if (current_rep_->Timeline().IsEmpty()) return 0; uint64_t duration{0}; - if (current_rep_->SegmentTimeline().GetSize() > 1) + if (current_rep_->Timeline().GetSize() > 1) { duration = - current_rep_->SegmentTimeline().Get(current_rep_->SegmentTimeline().GetSize() - 1)->startPTS_ - - current_rep_->SegmentTimeline().Get(current_rep_->SegmentTimeline().GetSize() - 2)->startPTS_; + current_rep_->Timeline().Get(current_rep_->Timeline().GetSize() - 1)->startPTS_ - + current_rep_->Timeline().Get(current_rep_->Timeline().GetSize() - 2)->startPTS_; } - uint64_t timeExt = ((current_rep_->SegmentTimeline() - .Get(current_rep_->SegmentTimeline().GetSize() - 1) + uint64_t timeExt = ((current_rep_->Timeline() + .Get(current_rep_->Timeline().GetSize() - 1) ->startPTS_ + duration) * current_rep_->timescale_ext_) / @@ -1145,7 +1131,7 @@ void AdaptiveStream::ResetCurrentSegment(const PLAYLIST::CSegment* newSegment) WaitWorker(); // EnsureSegment loads always the next segment, so go back 1 current_rep_->current_segment_ = - current_rep_->get_segment(current_rep_->get_segment_pos(newSegment) - 1); + current_rep_->Timeline().Get(current_rep_->Timeline().GetPos(newSegment) - 1); // TODO: if new segment is already prefetched, don't ResetActiveBuffer; ResetActiveBuffer(false); } @@ -1199,43 +1185,43 @@ bool AdaptiveStream::seek_time(double seek_seconds, bool preceeding, bool& needR //Skip initialization size_t choosen_seg{0}; - while (choosen_seg < current_rep_->SegmentTimeline().GetSize() && - sec_in_ts > current_rep_->get_segment(choosen_seg)->startPTS_) + while (choosen_seg < current_rep_->Timeline().GetSize() && + sec_in_ts > current_rep_->Timeline().Get(choosen_seg)->startPTS_) { ++choosen_seg; } - if (choosen_seg == current_rep_->SegmentTimeline().GetSize()) + if (choosen_seg == current_rep_->Timeline().GetSize()) { - if (!current_rep_->SegmentTimeline().Get(0)) + if (!current_rep_->Timeline().Get(0)) { LOG::LogF(LOGERROR, "[AS-%u] Segment at position 0 not found from representation id: %s", clsId, current_rep_->GetId().data()); return false; } - if (sec_in_ts < current_rep_->SegmentTimeline().Get(0)->startPTS_ + current_rep_->GetDuration()) + if (sec_in_ts < current_rep_->Timeline().Get(0)->startPTS_ + current_rep_->GetDuration()) --choosen_seg; else return false; } - if (choosen_seg && current_rep_->get_segment(choosen_seg)->startPTS_ > sec_in_ts) + if (choosen_seg && current_rep_->Timeline().Get(choosen_seg)->startPTS_ > sec_in_ts) --choosen_seg; // Never seek into expired segments..... if (choosen_seg < current_rep_->expired_segments_) choosen_seg = current_rep_->expired_segments_; - if (!preceeding && sec_in_ts > current_rep_->get_segment(choosen_seg)->startPTS_ && + if (!preceeding && sec_in_ts > current_rep_->Timeline().Get(choosen_seg)->startPTS_ && current_adp_->GetStreamType() == StreamType::VIDEO) { //Assume that we have I-Frames only at segment start ++choosen_seg; } - CSegment* old_seg = current_rep_->current_segment_; - const CSegment* newSeg = current_rep_->get_segment(choosen_seg); + const CSegment* old_seg = current_rep_->current_segment_; + const CSegment* newSeg = current_rep_->Timeline().Get(choosen_seg); if (newSeg) { @@ -1267,7 +1253,7 @@ bool AdaptiveStream::seek_time(double seek_seconds, bool preceeding, bool& needR size_t adaptive::AdaptiveStream::getSegmentPos() { - return current_rep_->getCurrentSegmentPos(); + return current_rep_->Timeline().GetPos(current_rep_->current_segment_); } bool AdaptiveStream::waitingForSegment() const diff --git a/src/common/AdaptiveTree.cpp b/src/common/AdaptiveTree.cpp index 43d18436b..454d047bf 100644 --- a/src/common/AdaptiveTree.cpp +++ b/src/common/AdaptiveTree.cpp @@ -114,12 +114,12 @@ namespace adaptive void AdaptiveTree::FreeSegments(CPeriod* period, CRepresentation* repr) { - for (auto& segment : repr->SegmentTimeline().GetData()) + for (const CSegment& segment : repr->Timeline()) { period->DecreasePSSHSetUsageCount(segment.pssh_set_); } - repr->SegmentTimeline().Clear(); + repr->Timeline().Clear(); repr->current_segment_ = nullptr; } @@ -189,7 +189,7 @@ namespace adaptive const PLAYLIST::CRepresentation* segRep, const PLAYLIST::CSegment* segment) const { - if (segRep->SegmentTimeline().IsEmpty()) + if (segRep->Timeline().IsEmpty()) return true; if (!segment || !segPeriod || !segRep) @@ -212,7 +212,7 @@ namespace adaptive } else { - const CSegment* lastSeg = segRep->SegmentTimeline().GetBack(); + const CSegment* lastSeg = segRep->Timeline().GetBack(); return segment == lastSeg; } return false; diff --git a/src/common/AdaptiveUtils.cpp b/src/common/AdaptiveUtils.cpp index d60d5c41b..7fe9ab667 100644 --- a/src/common/AdaptiveUtils.cpp +++ b/src/common/AdaptiveUtils.cpp @@ -10,6 +10,7 @@ #include "AdaptiveStream.h" #include "Representation.h" +#include "utils/log.h" #include "utils/Utils.h" #include diff --git a/src/common/AdaptiveUtils.h b/src/common/AdaptiveUtils.h index addeda387..75dea1077 100644 --- a/src/common/AdaptiveUtils.h +++ b/src/common/AdaptiveUtils.h @@ -9,13 +9,11 @@ #pragma once #include -#include #include +#include #include #include -#include "utils/log.h" - // forwards class AP4_Movie; namespace adaptive @@ -119,133 +117,6 @@ bool ParseRangeValues(std::string_view range, AP4_Movie* CreateMovieAtom(adaptive::AdaptiveStream& adStream, kodi::addon::InputstreamInfo& streamInfo); -template -class CSpinCache -{ -public: - /*! - * \brief Get the value pointer from the specified position - * \param pos The position of - * \return value pointer, otherwise nullptr if not found - */ - const T* Get(size_t pos) const - { - if (pos == SEGMENT_NO_POS || m_data.empty()) - return nullptr; - - if (pos >= m_data.size()) - { - LOG::LogF(LOGWARNING, "Position out-of-range (%zu of %zu)", pos, m_data.size()); - return nullptr; - } - - return &m_data[pos]; - } - - /*! - * \brief Get the value pointer from the specified position - * \param pos The position of - * \return value pointer, otherwise nullptr if not found - */ - T* Get(size_t pos) - { - if (pos == SEGMENT_NO_POS || m_data.empty()) - return nullptr; - - if (pos >= m_data.size()) - { - LOG::LogF(LOGWARNING, "Position out-of-range (%zu of %zu)", pos, m_data.size()); - return nullptr; - } - - return &m_data[pos]; - } - - /*! - * \brief Get the last value pointer - * \return value pointer, otherwise nullptr if not found - */ - T* GetBack() - { - if (m_data.empty()) - return nullptr; - - return &m_data.back(); - } - - /*! - * \brief Get the first value pointer - * \return value pointer, otherwise nullptr if not found - */ - T* GetFront() - { - if (m_data.empty()) - return nullptr; - - return &m_data.front(); - } - - /*! - * \brief Get index position of value pointer - * \param elem The pointer to get the position - * \return The index position, or SEGMENT_NO_POS if not found - */ - const size_t GetPosition(const T* elem) const - { - for (size_t i = 0; i < m_data.size(); ++i) - { - if (&m_data[i] == elem) - return i; - } - - return SEGMENT_NO_POS; - }; - - /*! - * \brief Append value to the container, by increasing the count. - * \param elem The value to append - */ - void Append(const T& elem) - { - m_data.emplace_back(elem); - m_appendCount += 1; - } - - void Swap(CSpinCache& other) - { - m_data.swap(other.m_data); - std::swap(m_appendCount, other.m_appendCount); - } - - void Clear() - { - m_data.clear(); - m_appendCount = 0; - } - - bool IsEmpty() const { return m_data.empty(); } - - /*! - * \brief Get the number of the appended elements. - * \return The number of appended elements. - */ - size_t GetAppendCount() const { return m_appendCount; } - - size_t GetSize() const { return m_data.size(); } - - /*! - * \brief Get the number of elements without taking into account those appended. - * \return The number of elements. - */ - size_t GetInitialSize() const { return m_data.size() - m_appendCount; } - - std::deque& GetData() { return m_data; } - -private: - std::deque m_data; - size_t m_appendCount{0}; // Number of appended elements -}; - // \brief Get the position of a pointer within a vector of unique_ptr template size_t GetPtrPosition(const std::vector>& container, const Ptr* ptr) diff --git a/src/common/Period.h b/src/common/Period.h index cc9626bec..a063dd20b 100644 --- a/src/common/Period.h +++ b/src/common/Period.h @@ -8,7 +8,6 @@ #pragma once -#include "AdaptiveUtils.h" #include "CommonSegAttribs.h" #include "SegTemplate.h" #include "utils/CryptoUtils.h" @@ -89,8 +88,8 @@ class ATTR_DLL_LOCAL CPeriod : public CCommonSegAttribs m_isSecureDecoderNeeded = isSecureDecoderNeeded; }; - CSpinCache& SegmentTimelineDuration() { return m_segmentTimelineDuration; } - bool HasSegmentTimelineDuration() { return !m_segmentTimelineDuration.IsEmpty(); } + std::vector& SegmentTimelineDuration() { return m_segmentTimelineDuration; } + bool HasSegmentTimelineDuration() { return !m_segmentTimelineDuration.empty(); } void CopyHLSData(const CPeriod* other); @@ -143,7 +142,7 @@ class ATTR_DLL_LOCAL CPeriod : public CCommonSegAttribs uint64_t m_duration{0}; EncryptionState m_encryptionState{EncryptionState::UNENCRYPTED}; bool m_isSecureDecoderNeeded{false}; - CSpinCache m_segmentTimelineDuration; + std::vector m_segmentTimelineDuration; }; } // namespace adaptive diff --git a/src/common/Representation.cpp b/src/common/Representation.cpp index 79b811b3f..3ca77e9f9 100644 --- a/src/common/Representation.cpp +++ b/src/common/Representation.cpp @@ -51,6 +51,29 @@ void PLAYLIST::CRepresentation::CopyHLSData(const CRepresentation* other) m_isEnabled = other->m_isEnabled; } +const CSegment* PLAYLIST::CRepresentation::GetNextSegment() +{ + return m_segmentTimeline.GetNext(current_segment_); +} + +const uint64_t PLAYLIST::CRepresentation::GetCurrentSegNumber() const +{ + return GetSegNumber(current_segment_); +} + +const uint64_t PLAYLIST::CRepresentation::GetSegNumber(const CSegment* seg) const +{ + if (!seg) + return SEGMENT_NO_NUMBER; + + const size_t segPos = m_segmentTimeline.GetPos(seg); + + if (segPos == SEGMENT_NO_POS) + return SEGMENT_NO_NUMBER; + + return static_cast(segPos) + m_startNumber; +} + void PLAYLIST::CRepresentation::SetScaling() { if (!m_timescale) diff --git a/src/common/Representation.h b/src/common/Representation.h index 396211f01..b0d7b60d2 100644 --- a/src/common/Representation.h +++ b/src/common/Representation.h @@ -103,9 +103,14 @@ class ATTR_DLL_LOCAL CRepresentation : public CCommonSegAttribs, public CCommonA uint16_t GetHdcpVersion() const { return m_hdcpVersion; } void SetHdcpVersion(uint16_t hdcpVersion) { m_hdcpVersion = hdcpVersion; } - CSpinCache& SegmentTimeline() { return m_segmentTimeline; } - CSpinCache SegmentTimeline() const { return m_segmentTimeline; } - bool HasSegmentTimeline() { return !m_segmentTimeline.IsEmpty(); } + /*! + * \brief The segment timeline. + */ + CSegContainer& Timeline() { return m_segmentTimeline; } + /*! + * \brief The segment timeline. + */ + const CSegContainer& Timeline() const { return m_segmentTimeline; } std::optional& GetSegmentBase() { return m_segmentBase; } void SetSegmentBase(const CSegmentBase& segBase) { m_segmentBase = segBase; } @@ -175,157 +180,29 @@ class ATTR_DLL_LOCAL CRepresentation : public CCommonSegAttribs, public CCommonA size_t expired_segments_{0}; - CSegment* current_segment_{nullptr}; - + const CSegment* current_segment_{nullptr}; bool HasInitSegment() const { return m_initSegment.has_value(); } void SetInitSegment(CSegment initSegment) { m_initSegment = initSegment; } std::optional& GetInitSegment() { return m_initSegment; } /*! - * \brief Get the next segment after the one specified. - * \return If found the segment pointer, otherwise nullptr. - */ - CSegment* get_next_segment(const CSegment* seg) - { - if (!seg || seg->IsInitialization()) - return m_segmentTimeline.Get(0); - - const size_t segPos = m_segmentTimeline.GetPosition(seg); - - if (segPos == SEGMENT_NO_POS) - return nullptr; - - size_t nextPos = segPos + 1; - if (nextPos == m_segmentTimeline.GetSize()) - return nullptr; - - return m_segmentTimeline.Get(nextPos); - } - - /*! - * \brief Get the segment from specified position. + * \brief Get segment following the current one. * \return If found the segment pointer, otherwise nullptr. */ - CSegment* get_segment(size_t pos) - { - if (pos == SEGMENT_NO_POS) - return nullptr; - - return m_segmentTimeline.Get(pos); - } - - /*! - * \brief Get the segment position. - * \return If found the position, otherwise SEGMENT_NO_POS. - */ - const size_t get_segment_pos(const CSegment* segment) const - { - if (!segment) - return SEGMENT_NO_POS; - - return m_segmentTimeline.IsEmpty() ? 0 : m_segmentTimeline.GetPosition(segment); - } - - CSegment* GetSegment(const CSegment& segment) - { - // If available, find the segment by number, this is because some - // live services provide inconsistent timestamps between manifest updates - // which will make it ineffective to find the same segment - if (segment.m_number != SEGMENT_NO_NUMBER) - { - const uint64_t number = segment.m_number; - - for (CSegment& segment : m_segmentTimeline.GetData()) - { - if (segment.m_number == number) - return &segment; - } - } - else - { - const uint64_t startPTS = segment.startPTS_; - - for (CSegment& segment : m_segmentTimeline.GetData()) - { - // Search by >= is intended to allow minimizing problems with encoders - // that provide inconsistent timestamps between manifest updates - if (segment.startPTS_ >= startPTS) - return &segment; - } - } - - return nullptr; - } - - CSegment* GetNextSegment(const CSegment& segment) - { - // If available, find the segment by number, this is because some - // live services provide inconsistent timestamps between manifest updates - // which will make it ineffective to find the next segment - if (segment.m_number != SEGMENT_NO_NUMBER) - { - const uint64_t number = segment.m_number; - - for (CSegment& segment : m_segmentTimeline.GetData()) - { - if (segment.m_number > number) - return &segment; - } - } - else - { - const uint64_t startPTS = segment.startPTS_; - - for (CSegment& segment : m_segmentTimeline.GetData()) - { - if (segment.startPTS_ > startPTS) - return &segment; - } - } - return nullptr; - } - - /*! - * \brief Get the position of current segment. - * \return If found the position, otherwise SEGMENT_NO_POS. - */ - const size_t getCurrentSegmentPos() const { return get_segment_pos(current_segment_); } + const CSegment* GetNextSegment(); /*! * \brief Get the segment number of current segment. * \return If found the number, otherwise SEGMENT_NO_NUMBER. */ - const uint64_t getCurrentSegmentNumber() const - { - if (!current_segment_) - return SEGMENT_NO_NUMBER; - - const size_t segPos = get_segment_pos(current_segment_); - - if (segPos == SEGMENT_NO_POS) - return SEGMENT_NO_NUMBER; - - return static_cast(segPos) + m_startNumber; - } + const uint64_t GetCurrentSegNumber() const; /*! * \brief Get the segment number of specified segment. * \return If found the number, otherwise SEGMENT_NO_NUMBER. */ - const uint64_t getSegmentNumber(const CSegment* segment) const - { - if (!segment) - return SEGMENT_NO_NUMBER; - - const size_t segPos = get_segment_pos(current_segment_); - - if (segPos == SEGMENT_NO_POS) - return SEGMENT_NO_NUMBER; - - return static_cast(segPos) + m_startNumber; - } - + const uint64_t GetSegNumber(const CSegment* seg) const; uint32_t timescale_ext_{0}; uint32_t timescale_int_{0}; @@ -355,7 +232,7 @@ class ATTR_DLL_LOCAL CRepresentation : public CCommonSegAttribs, public CCommonA uint64_t m_startNumber{1}; - CSpinCache m_segmentTimeline; + CSegContainer m_segmentTimeline; uint64_t m_duration{0}; uint32_t m_timescale{0}; diff --git a/src/common/Segment.cpp b/src/common/Segment.cpp index f6a0b77b8..f3b3b8572 100644 --- a/src/common/Segment.cpp +++ b/src/common/Segment.cpp @@ -7,3 +7,137 @@ */ #include "Segment.h" +#include "utils/log.h" + +using namespace PLAYLIST; + +const CSegment* PLAYLIST::CSegContainer::Get(size_t pos) const +{ + if (pos == SEGMENT_NO_POS || m_segments.empty()) + return nullptr; + + if (pos >= m_segments.size()) + { + LOG::LogF(LOGWARNING, "Position out-of-range (%zu of %zu)", pos, m_segments.size()); + return nullptr; + } + + return &m_segments[pos]; +} + +const CSegment* PLAYLIST::CSegContainer::GetBack() const +{ + if (m_segments.empty()) + return nullptr; + + return &m_segments.back(); +} + +const CSegment* PLAYLIST::CSegContainer::GetFront() const +{ + if (m_segments.empty()) + return nullptr; + + return &m_segments.front(); +} + +const CSegment* PLAYLIST::CSegContainer::GetNext(const CSegment* seg) const +{ + if (!seg || seg->IsInitialization()) + return GetFront(); + + // If available, find the segment by number, this is because some + // live services provide inconsistent timestamps between manifest updates + // which will make it ineffective to find the next segment + if (seg->m_number != SEGMENT_NO_NUMBER) + { + const uint64_t number = seg->m_number; + + for (const CSegment& segment : m_segments) + { + if (segment.m_number > number) + return &segment; + } + } + else + { + const uint64_t startPTS = seg->startPTS_; + + for (const CSegment& segment : m_segments) + { + if (segment.startPTS_ > startPTS) + return &segment; + } + } + return nullptr; +} + +const CSegment* PLAYLIST::CSegContainer::Find(const CSegment& seg) const +{ + // If available, find the segment by number, this is because some + // live services provide inconsistent timestamps between manifest updates + // which will make it ineffective to find the same segment + if (seg.m_number != SEGMENT_NO_NUMBER) + { + const uint64_t number = seg.m_number; + + for (const CSegment& segment : m_segments) + { + if (segment.m_number == number) + return &segment; + } + } + else + { + const uint64_t startPTS = seg.startPTS_; + + for (const CSegment& segment : m_segments) + { + // Search by >= is intended to allow minimizing problems with encoders + // that provide inconsistent timestamps between manifest updates + if (segment.startPTS_ >= startPTS) + return &segment; + } + } + + return nullptr; +} + +const size_t PLAYLIST::CSegContainer::GetPos(const CSegment* seg) const +{ + for (size_t i = 0; i < m_segments.size(); ++i) + { + if (&m_segments[i] == seg) + return i; + } + + return SEGMENT_NO_POS; +} + +void PLAYLIST::CSegContainer::Add(const CSegment& seg) +{ + m_duration += seg.m_endPts - seg.startPTS_; + m_segments.emplace_back(seg); +} + +void PLAYLIST::CSegContainer::Append(const CSegment& seg) +{ + m_duration += seg.m_endPts - seg.startPTS_; + m_segments.emplace_back(seg); + m_appendCount += 1; +} + +void PLAYLIST::CSegContainer::Swap(CSegContainer& other) +{ + m_segments.swap(other.m_segments); + std::swap(m_appendCount, other.m_appendCount); + std::swap(m_duration, other.m_duration); +} + +void PLAYLIST::CSegContainer::Clear() +{ + m_segments.clear(); + m_appendCount = 0; + m_duration = 0; +} + diff --git a/src/common/Segment.h b/src/common/Segment.h index 58a1b2723..ffc1c0821 100644 --- a/src/common/Segment.h +++ b/src/common/Segment.h @@ -17,8 +17,10 @@ #endif #include +#include #include #include +#include namespace PLAYLIST { @@ -60,4 +62,106 @@ class ATTR_DLL_LOCAL CSegment bool m_isInitialization{false}; }; +class ATTR_DLL_LOCAL CSegContainer +{ +public: + CSegContainer() = default; + ~CSegContainer() = default; + + /*! + * \brief Get the segment pointer from the specified position + * \param pos The position of segment + * \return Segment pointer, otherwise nullptr if not found + */ + const CSegment* Get(size_t pos) const; + + /*! + * \brief Get the last segment pointer + * \return Segment pointer, otherwise nullptr if not found + */ + const CSegment* GetBack() const; + + /*! + * \brief Get the first segment as pointer + * \return Segment pointer, otherwise nullptr if not found + */ + const CSegment* GetFront() const; + + /*! + * \brief Get the next segment after the one specified. + * The search is done by number (if available) otherwise by PTS. + * \return If found the segment pointer, otherwise nullptr. + */ + const CSegment* GetNext(const CSegment* seg) const; + + /*! + * \brief Try find same/similar segment in the timeline. + * The search is done by number (if available) otherwise by PTS. + * \return If found the segment pointer, otherwise nullptr. + */ + const CSegment* Find(const CSegment& seg) const; + + /*! + * \brief Get index position of a segment pointer in the timeline. + * \param elem The segment pointer to get the position + * \return The index position, or SEGMENT_NO_POS if not found + */ + const size_t GetPos(const CSegment* seg) const; + + /*! + * \brief Add a segment to the container. + * \param elem The segment to add + */ + void Add(const CSegment& seg); + + /*! + * \brief Append segment to the container, by increasing the count. + * \param elem The segment to append + */ + void Append(const CSegment& seg); + + void Swap(CSegContainer& other); + + /*! + * \brief Delete all segments and clear the properties. + */ + void Clear(); + + bool IsEmpty() const { return m_segments.empty(); } + + /*! + * \brief Get the number of the appended segments. + * \return The number of appended segments. + */ + size_t GetAppendCount() const { return m_appendCount; } + + /*! + * \brief Get the number of segments. + * \return The number of segments. + */ + size_t GetSize() const { return m_segments.size(); } + + /*! + * \brief Get the number of elements without taking into account those appended. + * \return The number of elements. + */ + size_t GetInitialSize() const { return m_segments.size() - m_appendCount; } + + /*! + * \brief Get the duration of all segments. + * \return The duration of all segments. + */ + uint64_t GetDuration() const { return m_duration; } + + std::deque::const_iterator begin() const { return m_segments.begin(); } + std::deque::const_iterator end() const { return m_segments.end(); } + +private: + // Has been used std::deque because there are uses of pointer references + // deque container keeps memory addresses even if the container size increases (no reallocations) + std::deque m_segments; + size_t m_appendCount{0}; // Number of appended segments + uint64_t m_duration{0}; // Sum of the duration of all segments +}; + } // namespace PLAYLIST diff --git a/src/common/SegmentBase.cpp b/src/common/SegmentBase.cpp index c4688c41f..9e0bee192 100644 --- a/src/common/SegmentBase.cpp +++ b/src/common/SegmentBase.cpp @@ -10,6 +10,7 @@ #include "AdaptiveUtils.h" #include "Segment.h" +#include "utils/log.h" using namespace PLAYLIST; diff --git a/src/common/SegmentList.cpp b/src/common/SegmentList.cpp index 7ed5994bb..25f114518 100644 --- a/src/common/SegmentList.cpp +++ b/src/common/SegmentList.cpp @@ -8,6 +8,7 @@ #include "SegmentList.h" #include "Segment.h" +#include "utils/log.h" using namespace PLAYLIST; diff --git a/src/parser/DASHTree.cpp b/src/parser/DASHTree.cpp index e6537b4a8..d51594a02 100644 --- a/src/parser/DASHTree.cpp +++ b/src/parser/DASHTree.cpp @@ -632,7 +632,7 @@ void adaptive::CDashTree::ParseTagAdaptationSet(pugi::xml_node nodeAdp, PLAYLIST // add all duration values as timeline segments for (xml_node node : nodeSegDur.children("S")) { - adpSet->SegmentTimelineDuration().GetData().emplace_back(XML::GetAttribUint32(node, "d")); + adpSet->SegmentTimelineDuration().emplace_back(XML::GetAttribUint32(node, "d")); } } @@ -875,8 +875,10 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, uint64_t segNumber = segList.GetStartNumber(); // If tag is present it could use a different timescale - bool isTsRescale = adpSet->GetSegDurationsTimescale() != NO_VALUE && - adpSet->GetSegDurationsTimescale() != segList.GetTimescale(); + const size_t TLDurationSize = adpSet->SegmentTimelineDuration().size(); + const bool isTLDurTsRescale = adpSet->HasSegmentTimelineDuration() && + adpSet->GetSegDurationsTimescale() != NO_VALUE && + adpSet->GetSegDurationsTimescale() != segList.GetTimescale(); for (xml_node node : nodeSeglist.children("SegmentURL")) { @@ -895,11 +897,10 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, } uint64_t duration; - uint32_t* sdDuration = adpSet->SegmentTimelineDuration().Get(index); // tag is present - if (sdDuration) + if (TLDurationSize > 0 && index < TLDurationSize) { - duration = *sdDuration; - if (isTsRescale) + duration = adpSet->SegmentTimelineDuration()[index]; + if (isTLDurTsRescale) { duration = static_cast(static_cast(duration) / @@ -914,31 +915,12 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, seg.m_time = segStartPts; seg.m_number = segNumber++; - repr->SegmentTimeline().GetData().push_back(seg); + repr->Timeline().Add(seg); segStartPts += duration; index++; } - // Determines total duration of segments - uint64_t totalDur; - if (adpSet->HasSegmentTimelineDuration()) - { - auto& segTLData = adpSet->SegmentTimelineDuration().GetData(); - totalDur = std::accumulate(segTLData.begin(), segTLData.end(), 0ULL); - if (isTsRescale) - { - totalDur = - static_cast(static_cast(totalDur) / - adpSet->GetSegDurationsTimescale() * segList.GetTimescale()); - } - } - else - { - totalDur = static_cast(repr->SegmentTimeline().GetSize()) * segList.GetDuration(); - } - - repr->SetDuration(totalDur); repr->SetTimescale(segList.GetTimescale()); repr->SetSegmentList(segList); @@ -1012,7 +994,7 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, } if (repr->GetContainerType() == ContainerType::TEXT && repr->GetMimeType() != "application/mp4" && - !repr->HasSegmentBase() && !repr->HasSegmentTemplate() && !repr->HasSegmentTimeline()) + !repr->HasSegmentBase() && !repr->HasSegmentTemplate() && repr->Timeline().IsEmpty()) { // Raw unsegmented subtitles called "sidecar" is a single file specified in the tag, // must not have the MP4 ISOBMFF mime type or any other dash element. @@ -1020,7 +1002,7 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, } // Generate segments from SegmentTemplate - if (repr->HasSegmentTemplate() && !repr->HasSegmentTimeline()) + if (repr->HasSegmentTemplate() && repr->Timeline().IsEmpty()) { auto& segTemplate = repr->GetSegmentTemplate(); @@ -1054,7 +1036,6 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, if (segTemplate->HasTimeline()) // Generate segments from template timeline { uint64_t time{0}; - uint64_t totalDuration{0}; for (const auto& tlElem : segTemplate->Timeline()) { @@ -1078,15 +1059,13 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, seg.m_time = time; - totalDuration += tlElem.duration; - repr->SegmentTimeline().GetData().emplace_back(seg); + repr->Timeline().Add(seg); time += tlElem.duration; } while (repeat-- > 0); } repr->SetTimescale(segTimescale); - repr->SetDuration(totalDuration); } else // Generate segments by using template { @@ -1157,24 +1136,24 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, seg.m_time = time; - repr->SegmentTimeline().GetData().emplace_back(seg); + repr->Timeline().Add(seg); time = seg.m_endPts; } repr->SetTimescale(segTemplate->GetTimescale()); - repr->SetDuration(static_cast(segDuration) * segmentsCount); } } } + repr->SetDuration(repr->Timeline().GetDuration()); repr->SetScaling(); adpSet->AddRepresentation(repr); } void adaptive::CDashTree::ParseTagSegmentTimeline(pugi::xml_node nodeSegTL, - CSpinCache& SCTimeline) + std::vector& SCTimeline) { uint64_t nextPts{0}; @@ -1186,22 +1165,22 @@ void adaptive::CDashTree::ParseTagSegmentTimeline(pugi::xml_node nodeSegTL, uint32_t repeat = XML::GetAttribUint32(node, "r"); repeat += 1; - if (SCTimeline.IsEmpty()) + if (SCTimeline.empty()) { nextPts = time; } else if (time > 0) { //Go back to the previous timestamp to calculate the real gap. - nextPts -= SCTimeline.GetData().back(); - SCTimeline.GetData().back() = static_cast(time - nextPts); + nextPts -= SCTimeline.back(); + SCTimeline.back() = static_cast(time - nextPts); nextPts = time; } if (duration > 0) { for (; repeat > 0; --repeat) { - SCTimeline.GetData().emplace_back(duration); + SCTimeline.emplace_back(duration); nextPts += duration; } } @@ -1674,7 +1653,7 @@ void adaptive::CDashTree::OnUpdateSegments() { auto repr = (*itRepr).get(); - if (updRepr->SegmentTimeline().IsEmpty()) + if (updRepr->Timeline().IsEmpty()) { LOG::LogF(LOGWARNING, "MPD update - Updated timeline has no segments " @@ -1683,16 +1662,16 @@ void adaptive::CDashTree::OnUpdateSegments() continue; } - if (!repr->SegmentTimeline().IsEmpty()) + if (!repr->Timeline().IsEmpty()) { if (!repr->current_segment_) // Representation that should not be used for playback { - repr->SegmentTimeline().Swap(updRepr->SegmentTimeline()); + repr->Timeline().Swap(updRepr->Timeline()); } else { - if (repr->SegmentTimeline().GetInitialSize() == updRepr->SegmentTimeline().GetSize() && - repr->SegmentTimeline().Get(0)->startPTS_ == updRepr->SegmentTimeline().Get(0)->startPTS_) + if (repr->Timeline().GetInitialSize() == updRepr->Timeline().GetSize() && + repr->Timeline().Get(0)->startPTS_ == updRepr->Timeline().Get(0)->startPTS_) { LOG::LogF(LOGDEBUG, "MPD update - No new segments (repr. id \"%s\", period id \"%s\")", @@ -1700,10 +1679,10 @@ void adaptive::CDashTree::OnUpdateSegments() continue; } - CSegment* foundSeg{nullptr}; + const CSegment* foundSeg{nullptr}; const uint64_t segStartPTS = repr->current_segment_->startPTS_; - for (CSegment& segment : updRepr->SegmentTimeline().GetData()) + for (const CSegment& segment : updRepr->Timeline()) { if (segment.startPTS_ == segStartPTS) { @@ -1735,7 +1714,7 @@ void adaptive::CDashTree::OnUpdateSegments() } else { - repr->SegmentTimeline().Swap(updRepr->SegmentTimeline()); + repr->Timeline().Swap(updRepr->Timeline()); repr->current_segment_ = foundSeg; LOG::LogF(LOGDEBUG, "MPD update - Done (repr. id \"%s\", period id \"%s\")", @@ -1743,7 +1722,7 @@ void adaptive::CDashTree::OnUpdateSegments() } } - if (repr->IsWaitForSegment() && (repr->get_next_segment(repr->current_segment_))) + if (repr->IsWaitForSegment() && repr->GetNextSegment()) { repr->SetIsWaitForSegment(false); LOG::LogF(LOGDEBUG, "End WaitForSegment repr. id %s", repr->GetId().data()); @@ -1800,7 +1779,7 @@ bool adaptive::CDashTree::InsertLiveSegment(PLAYLIST::CPeriod* period, //! @todo: expired_segments_ should be reworked, see also other parsers repr->expired_segments_++; - CSegment* segment = repr->SegmentTimeline().Get(pos); + const CSegment* segment = repr->Timeline().Get(pos); if (!segment) { @@ -1821,7 +1800,7 @@ bool adaptive::CDashTree::InsertLiveSegment(PLAYLIST::CPeriod* period, for (auto& repr : adpSet->GetRepresentations()) { - repr->SegmentTimeline().Append(segCopy); + repr->Timeline().Append(segCopy); } return true; } @@ -1837,7 +1816,7 @@ bool adaptive::CDashTree::InsertLiveFragment(PLAYLIST::CAdaptationSet* adpSet, if (!m_isLive || !repr->HasSegmentTemplate() || m_minimumUpdatePeriod != NO_VALUE) return false; - CSegment* lastSeg = repr->SegmentTimeline().GetBack(); + const CSegment* lastSeg = repr->Timeline().GetBack(); if (!lastSeg) return false; @@ -1866,7 +1845,7 @@ bool adaptive::CDashTree::InsertLiveFragment(PLAYLIST::CAdaptationSet* adpSet, for (auto& repr : adpSet->GetRepresentations()) { - repr->SegmentTimeline().Append(segCopy); + repr->Timeline().Append(segCopy); } return true; diff --git a/src/parser/DASHTree.h b/src/parser/DASHTree.h index 8648429ca..34d28d13b 100644 --- a/src/parser/DASHTree.h +++ b/src/parser/DASHTree.h @@ -68,7 +68,7 @@ class ATTR_DLL_LOCAL CDashTree : public adaptive::AdaptiveTree PLAYLIST::CPeriod* period); void ParseTagSegmentTimeline(pugi::xml_node parentNode, - PLAYLIST::CSpinCache& SCTimeline); + std::vector& SCTimeline); void ParseSegmentTemplate(pugi::xml_node node, PLAYLIST::CSegmentTemplate& segTpl); diff --git a/src/parser/HLSTree.cpp b/src/parser/HLSTree.cpp index 7ab48b27c..4065668ec 100644 --- a/src/parser/HLSTree.cpp +++ b/src/parser/HLSTree.cpp @@ -186,7 +186,7 @@ bool adaptive::CHLSTree::PrepareRepresentation(PLAYLIST::CPeriod* period, PLAYLIST::CAdaptationSet* adp, PLAYLIST::CRepresentation* rep) { - if (!m_isLive && rep->HasSegmentTimeline()) + if (!m_isLive && !rep->Timeline().IsEmpty()) return true; if (!ProcessChildManifest(period, adp, rep, SEGMENT_NO_NUMBER)) @@ -225,9 +225,9 @@ void adaptive::CHLSTree::FixMediaSequence(std::stringstream& streamData, { // Get the last segment PTS and number in the last period auto& lastPRep = m_periods.back()->GetAdaptationSets()[adpSetPos]->GetRepresentations()[reprPos]; - if (lastPRep->SegmentTimeline().IsEmpty()) + if (lastPRep->Timeline().IsEmpty()) return; - CSegment* lastSeg = lastPRep->SegmentTimeline().GetBack(); + const CSegment* lastSeg = lastPRep->Timeline().GetBack(); uint64_t segStartPts = lastSeg->startPTS_; // The start PTS refer to date-time uint64_t segNumber = lastSeg->m_number; @@ -462,7 +462,7 @@ bool adaptive::CHLSTree::ProcessChildManifest(PLAYLIST::CPeriod* period, uint64_t mediaSequenceNbr{0}; - CSpinCache newSegments; + CSegContainer newSegments; std::optional newSegment; // Pssh set used between segments @@ -605,7 +605,7 @@ bool adaptive::CHLSTree::ProcessChildManifest(PLAYLIST::CPeriod* period, } else { - CSegment* lastSeg = newSegments.GetBack(); + const CSegment* lastSeg = newSegments.GetBack(); if (lastSeg) startPts = lastSeg->m_endPts; } @@ -701,7 +701,7 @@ bool adaptive::CHLSTree::ProcessChildManifest(PLAYLIST::CPeriod* period, } newSegment->pssh_set_ = psshSetPos; - newSegments.GetData().emplace_back(*newSegment); + newSegments.Add(*newSegment); newSegment.reset(); } else if (tagName == "#EXT-X-DISCONTINUITY-SEQUENCE") @@ -735,7 +735,7 @@ bool adaptive::CHLSTree::ProcessChildManifest(PLAYLIST::CPeriod* period, { auto& pCurrAdp = m_currentPeriod->GetAdaptationSets()[adpSetPos]; auto& pCurrRep = pCurrAdp->GetRepresentations()[reprPos]; - pCurrRep->SegmentTimeline().Clear(); + pCurrRep->Timeline().Clear(); pCurrRep->current_segment_ = nullptr; LOG::Log(LOGDEBUG, "Clear outdated period of discontinuity %u", itPeriod->get()->GetSequence()); @@ -774,8 +774,7 @@ bool adaptive::CHLSTree::ProcessChildManifest(PLAYLIST::CPeriod* period, { period->SetSequence(m_discontSeq + discontCount); - uint64_t dur = newSegments.GetBack()->m_endPts - newSegments.GetFront()->startPTS_; - rep->SetDuration(dur); + rep->SetDuration(newSegments.GetDuration()); if (adp->GetStreamType() != StreamType::SUBTITLE) { @@ -785,7 +784,7 @@ bool adaptive::CHLSTree::ProcessChildManifest(PLAYLIST::CPeriod* period, } FreeSegments(period, rep); - rep->SegmentTimeline().Swap(newSegments); + rep->Timeline().Swap(newSegments); rep->SetStartNumber(mediaSequenceNbr); } @@ -793,7 +792,7 @@ bool adaptive::CHLSTree::ProcessChildManifest(PLAYLIST::CPeriod* period, isSkipUntilDiscont = false; ++discontCount; - mediaSequenceNbr += rep->SegmentTimeline().GetSize(); + mediaSequenceNbr += rep->Timeline().GetSize(); currentSegNumber = mediaSequenceNbr; CPeriod* newPeriod = FindDiscontinuityPeriod(m_discontSeq + discontCount); @@ -880,12 +879,12 @@ bool adaptive::CHLSTree::ProcessChildManifest(PLAYLIST::CPeriod* period, } FreeSegments(period, rep); - rep->SegmentTimeline().Swap(newSegments); + rep->Timeline().Swap(newSegments); rep->SetStartNumber(mediaSequenceNbr); uint64_t reprDur{0}; - if (rep->SegmentTimeline().Get(0)) - reprDur = rep->SegmentTimeline().GetBack()->m_endPts - rep->SegmentTimeline().GetFront()->startPTS_; + if (rep->Timeline().Get(0)) + reprDur = rep->Timeline().GetBack()->m_endPts - rep->Timeline().GetFront()->startPTS_; rep->SetDuration(reprDur); period->SetSequence(m_discontSeq + discontCount); @@ -929,19 +928,19 @@ void adaptive::CHLSTree::PrepareSegments(PLAYLIST::CPeriod* period, } else { - if (segNumber >= rep->GetStartNumber() + rep->SegmentTimeline().GetSize()) + if (segNumber >= rep->GetStartNumber() + rep->Timeline().GetSize()) { - segNumber = rep->GetStartNumber() + rep->SegmentTimeline().GetSize() - 1; + segNumber = rep->GetStartNumber() + rep->Timeline().GetSize() - 1; } rep->current_segment_ = - rep->get_segment(static_cast(segNumber - rep->GetStartNumber())); + rep->Timeline().Get(static_cast(segNumber - rep->GetStartNumber())); } //! @todo: m_currentPeriod != m_periods.back().get() condition should be removed from here //! this is done on AdaptiveStream::ensureSegment on IsLastSegment check if (rep->IsWaitForSegment() && - (rep->get_next_segment(rep->current_segment_) || m_currentPeriod != m_periods.back().get())) + (rep->GetNextSegment() || m_currentPeriod != m_periods.back().get())) { LOG::LogF(LOGDEBUG, "End WaitForSegment stream id \"%s\"", rep->GetId().data()); rep->SetIsWaitForSegment(false); @@ -1054,10 +1053,10 @@ void adaptive::CHLSTree::OnStreamChange(PLAYLIST::CPeriod* period, PLAYLIST::CRepresentation* previousRep, PLAYLIST::CRepresentation* currentRep) { - if (!m_isLive && currentRep->HasSegmentTimeline()) + if (!m_isLive && !currentRep->Timeline().IsEmpty()) return; - const uint64_t currentSegNumber = previousRep->getCurrentSegmentNumber(); + const uint64_t currentSegNumber = previousRep->GetCurrentSegNumber(); ProcessChildManifest(period, adp, currentRep, currentSegNumber); } @@ -1071,7 +1070,7 @@ void adaptive::CHLSTree::OnRequestSegments(PLAYLIST::CPeriod* period, // Save the current segment position before parsing the manifest // to allow find the right segment on updated playlist segments - const uint64_t segNumber = rep->getCurrentSegmentNumber(); + const uint64_t segNumber = rep->GetCurrentSegNumber(); ProcessChildManifest(period, adp, rep, segNumber); } @@ -1114,7 +1113,7 @@ void adaptive::CHLSTree::OnUpdateSegments() { // Save the current segment position before parsing the manifest // to allow find the right segment on updated playlist segments - const uint64_t segNumber = repr->getCurrentSegmentNumber(); + const uint64_t segNumber = repr->GetCurrentSegNumber(); if (!ProcessChildManifest(m_currentPeriod, adpSet, repr, segNumber)) { diff --git a/src/parser/SmoothTree.cpp b/src/parser/SmoothTree.cpp index 543284fab..c7e16abb4 100644 --- a/src/parser/SmoothTree.cpp +++ b/src/parser/SmoothTree.cpp @@ -229,11 +229,11 @@ void adaptive::CSmoothTree::ParseTagStreamIndex(pugi::xml_node nodeSI, uint64_t t{0}; if (XML::QueryAttrib(node, "t", t)) { - if (!adpSet->SegmentTimelineDuration().IsEmpty()) + if (!adpSet->SegmentTimelineDuration().empty()) { //Go back to the previous timestamp to calculate the real gap. - previousPts -= adpSet->SegmentTimelineDuration().GetData().back(); - adpSet->SegmentTimelineDuration().GetData().back() = static_cast(t - previousPts); + previousPts -= adpSet->SegmentTimelineDuration().back(); + adpSet->SegmentTimelineDuration().back() = static_cast(t - previousPts); } else { @@ -252,13 +252,13 @@ void adaptive::CSmoothTree::ParseTagStreamIndex(pugi::xml_node nodeSI, { while (repeatCount--) { - adpSet->SegmentTimelineDuration().GetData().emplace_back(duration); + adpSet->SegmentTimelineDuration().emplace_back(duration); previousPts += duration; } } } - if (adpSet->SegmentTimelineDuration().IsEmpty()) + if (adpSet->SegmentTimelineDuration().empty()) { LOG::LogF(LOGDEBUG, "No generated timeline, adaptation set skipped."); return; @@ -396,7 +396,7 @@ void adaptive::CSmoothTree::CreateSegmentTimeline() uint64_t nextStartPts = adpSet->GetStartPTS() - m_ptsBase; uint64_t index = 1; - for (uint32_t segDuration : adpSet->SegmentTimelineDuration().GetData()) + for (uint32_t segDuration : adpSet->SegmentTimelineDuration()) { CSegment seg; seg.startPTS_ = nextStartPts; @@ -404,7 +404,7 @@ void adaptive::CSmoothTree::CreateSegmentTimeline() seg.m_time = nextStartPts + m_ptsBase; seg.m_number = index; - repr->SegmentTimeline().GetData().emplace_back(seg); + repr->Timeline().Add(seg); nextStartPts += segDuration; index++; @@ -427,7 +427,7 @@ bool adaptive::CSmoothTree::InsertLiveFragment(PLAYLIST::CAdaptationSet* adpSet, //! then add a better way to delete old segments from the timeline based on timeshift window //! this also requires taking care of the Dash parser - CSegment* lastSeg = repr->SegmentTimeline().GetBack(); + const CSegment* lastSeg = repr->Timeline().GetBack(); if (!lastSeg) return false; @@ -458,7 +458,7 @@ bool adaptive::CSmoothTree::InsertLiveFragment(PLAYLIST::CAdaptationSet* adpSet, for (auto& repr : adpSet->GetRepresentations()) { - repr->SegmentTimeline().Append(segCopy); + repr->Timeline().Append(segCopy); } return true; diff --git a/src/samplereader/SampleReaderFactory.cpp b/src/samplereader/SampleReaderFactory.cpp index d451b1d50..8a44655ea 100644 --- a/src/samplereader/SampleReaderFactory.cpp +++ b/src/samplereader/SampleReaderFactory.cpp @@ -16,6 +16,7 @@ #include "samplereader/SubtitleSampleReader.h" #include "samplereader/TSSampleReader.h" #include "samplereader/WebmSampleReader.h" +#include "utils/log.h" #include diff --git a/src/test/TestDASHTree.cpp b/src/test/TestDASHTree.cpp index 3e2dcf51e..5bf7d2223 100644 --- a/src/test/TestDASHTree.cpp +++ b/src/test/TestDASHTree.cpp @@ -270,7 +270,7 @@ TEST_F(DASHTreeTest, CalculateCorrectSegmentNumbersFromSegmentTimeline) OpenTestFile("mpd/segtimeline_live_ast.mpd"); auto& segments = - tree->m_periods[0]->GetAdaptationSets()[0]->GetRepresentations()[0]->SegmentTimeline(); + tree->m_periods[0]->GetAdaptationSets()[0]->GetRepresentations()[0]->Timeline(); EXPECT_EQ(segments.GetSize(), 13); EXPECT_EQ(segments.Get(0)->m_number, 487050); @@ -283,7 +283,7 @@ TEST_F(DASHTreeTest, CalculateCorrectSegmentNumbersFromSegmentTemplateWithPTO) OpenTestFile("mpd/segtpl_pto.mpd"); - auto& segments = tree->m_periods[0]->GetAdaptationSets()[0]->GetRepresentations()[0]->SegmentTimeline(); + auto& segments = tree->m_periods[0]->GetAdaptationSets()[0]->GetRepresentations()[0]->Timeline(); EXPECT_EQ(segments.GetSize(), 450); EXPECT_EQ(segments.Get(0)->m_number, 404314437); @@ -295,7 +295,7 @@ TEST_F(DASHTreeTest, CalculateCorrectSegmentNumbersFromSegmentTemplateWithOldPub OpenTestFile("mpd/segtpl_old_publish_time.mpd"); - auto& segments = tree->m_periods[0]->GetAdaptationSets()[0]->GetRepresentations()[0]->SegmentTimeline(); + auto& segments = tree->m_periods[0]->GetAdaptationSets()[0]->GetRepresentations()[0]->Timeline(); EXPECT_EQ(segments.GetSize(), 30); EXPECT_EQ(segments.Get(0)->m_number, 603271); @@ -492,22 +492,22 @@ TEST_F(DASHTreeTest, CalculateMultipleSegTpl) EXPECT_EQ(STR(adpSets[0]->GetRepresentations()[0]->GetSegmentTemplate()->GetInitialization()), "3c1055cb-a842-4449-b393-7f31693b4a8f_1_448x252init.mp4"); EXPECT_EQ(STR(adpSets[0]->GetRepresentations()[0]->GetSegmentTemplate()->GetMedia()), "3c1055cb-a842-4449-b393-7f31693b4a8f_1_448x252_$Number%09d$.mp4"); EXPECT_EQ(adpSets[0]->GetRepresentations()[0]->GetSegmentTemplate()->GetTimescale(), 120000); - EXPECT_EQ(adpSets[0]->GetRepresentations()[0]->SegmentTimeline().Get(0)->m_number, 3); + EXPECT_EQ(adpSets[0]->GetRepresentations()[0]->Timeline().Get(0)->m_number, 3); EXPECT_EQ(STR(adpSets[0]->GetRepresentations()[1]->GetSegmentTemplate()->GetInitialization()), "3c1055cb-a842-4449-b393-7f31693b4a8f_2_1920x1080init.mp4"); EXPECT_EQ(STR(adpSets[0]->GetRepresentations()[1]->GetSegmentTemplate()->GetMedia()), "3c1055cb-a842-4449-b393-7f31693b4a8f_2_1920x1080_$Number%09d$.mp4"); EXPECT_EQ(adpSets[0]->GetRepresentations()[1]->GetSegmentTemplate()->GetTimescale(), 90000); - EXPECT_EQ(adpSets[0]->GetRepresentations()[1]->SegmentTimeline().Get(0)->m_number, 5); + EXPECT_EQ(adpSets[0]->GetRepresentations()[1]->Timeline().Get(0)->m_number, 5); EXPECT_EQ(STR(adpSets[1]->GetRepresentations()[0]->GetSegmentTemplate()->GetInitialization()), "3c1055cb-a842-4449-b393-7f31693b4a8f_aac1init.mp4"); EXPECT_EQ(STR(adpSets[1]->GetRepresentations()[0]->GetSegmentTemplate()->GetMedia()), "3c1055cb-a842-4449-b393-7f31693b4a8f_aac1_$Number%09d$.mp4"); EXPECT_EQ(adpSets[1]->GetRepresentations()[0]->GetSegmentTemplate()->GetTimescale(), 48000); - EXPECT_EQ(adpSets[1]->GetRepresentations()[0]->SegmentTimeline().Get(0)->m_number, 1); + EXPECT_EQ(adpSets[1]->GetRepresentations()[0]->Timeline().Get(0)->m_number, 1); EXPECT_EQ(STR(adpSets[2]->GetRepresentations()[0]->GetSegmentTemplate()->GetInitialization()), "abc_aac1init.mp4"); EXPECT_EQ(STR(adpSets[2]->GetRepresentations()[0]->GetSegmentTemplate()->GetMedia()), "abc2_$Number%09d$.mp4"); EXPECT_EQ(adpSets[2]->GetRepresentations()[0]->GetSegmentTemplate()->GetTimescale(), 68000); - EXPECT_EQ(adpSets[2]->GetRepresentations()[0]->SegmentTimeline().Get(0)->m_number, 5); + EXPECT_EQ(adpSets[2]->GetRepresentations()[0]->Timeline().Get(0)->m_number, 5); } TEST_F(DASHTreeAdaptiveStreamTest, CalculateRedirectSegTpl) @@ -601,22 +601,22 @@ TEST_F(DASHTreeAdaptiveStreamTest, MisalignedSegmentTimeline) auto& repr = tree->m_currentPeriod->GetAdaptationSets()[1]->GetRepresentations()[0]; // Set the last segment to the current segment to simulate reaching the last segment - repr->current_segment_ = &repr->SegmentTimeline().GetData().back(); + repr->current_segment_ = repr->Timeline().GetBack(); EXPECT_EQ(repr->current_segment_->startPTS_, 95687379264); - EXPECT_EQ(repr->getCurrentSegmentPos(), 4); + EXPECT_EQ(repr->Timeline().GetPos(repr->current_segment_), 4); tree->RunManifestUpdate("mpd/bad_segtimeline_2.mpd"); EXPECT_EQ(repr->current_segment_->startPTS_, 95687381280); - EXPECT_EQ(repr->getCurrentSegmentPos(), 2); + EXPECT_EQ(repr->Timeline().GetPos(repr->current_segment_), 2); tree->RunManifestUpdate("mpd/bad_segtimeline_3.mpd"); EXPECT_EQ(repr->current_segment_->startPTS_, 95687382336); - EXPECT_EQ(repr->getCurrentSegmentPos(), 1); + EXPECT_EQ(repr->Timeline().GetPos(repr->current_segment_), 1); tree->RunManifestUpdate("mpd/bad_segtimeline_4.mpd"); EXPECT_EQ(repr->current_segment_->startPTS_, 95687382337); - EXPECT_EQ(repr->getCurrentSegmentPos(), 0); + EXPECT_EQ(repr->Timeline().GetPos(repr->current_segment_), 0); } TEST_F(DASHTreeTest, AdaptionSetSwitching) @@ -678,7 +678,7 @@ TEST_F(DASHTreeTest, SegmentTemplateStartNumber) EXPECT_EQ(adpSets[0]->GetRepresentations()[0]->GetSegmentTemplate()->GetDuration(), 48000); // Verify segments - auto& rep1Timeline = adpSets[0]->GetRepresentations()[0]->SegmentTimeline(); + auto& rep1Timeline = adpSets[0]->GetRepresentations()[0]->Timeline(); EXPECT_EQ(rep1Timeline.GetSize(), 144); EXPECT_EQ(rep1Timeline.Get(0)->startPTS_, 0); @@ -700,14 +700,14 @@ TEST_F(DASHTreeTest, TSBMiddlePeriods) OpenTestFile("mpd/tsb_middle_periods.mpd"); auto& tlPeriod1 = - tree->m_periods[0]->GetAdaptationSets()[0]->GetRepresentations()[0]->SegmentTimeline(); + tree->m_periods[0]->GetAdaptationSets()[0]->GetRepresentations()[0]->Timeline(); EXPECT_EQ(tlPeriod1.GetSize(), 90); EXPECT_EQ(tlPeriod1.GetFront()->m_number, 856065330); EXPECT_EQ(tlPeriod1.GetBack()->m_number, 856065419); auto& tlPeriod2 = - tree->m_periods[1]->GetAdaptationSets()[0]->GetRepresentations()[0]->SegmentTimeline(); + tree->m_periods[1]->GetAdaptationSets()[0]->GetRepresentations()[0]->Timeline(); EXPECT_EQ(tlPeriod2.GetSize(), 2); EXPECT_EQ(tlPeriod2.GetFront()->m_number, 856065420); @@ -724,14 +724,14 @@ TEST_F(DASHTreeTest, TSBMiddlePeriodsPastNowTime) OpenTestFile("mpd/tsb_middle_periods.mpd"); auto& tlPeriod1 = - tree->m_periods[0]->GetAdaptationSets()[0]->GetRepresentations()[0]->SegmentTimeline(); + tree->m_periods[0]->GetAdaptationSets()[0]->GetRepresentations()[0]->Timeline(); EXPECT_EQ(tlPeriod1.GetSize(), 90); EXPECT_EQ(tlPeriod1.GetFront()->m_number, 856065330); EXPECT_EQ(tlPeriod1.GetBack()->m_number, 856065419); auto& tlPeriod2 = - tree->m_periods[1]->GetAdaptationSets()[0]->GetRepresentations()[0]->SegmentTimeline(); + tree->m_periods[1]->GetAdaptationSets()[0]->GetRepresentations()[0]->Timeline(); EXPECT_EQ(tlPeriod2.GetSize(), 1); EXPECT_EQ(tlPeriod2.GetFront()->m_number, 856065420); @@ -746,7 +746,7 @@ TEST_F(DASHTreeTest, TSBAvailabilityStartTime) OpenTestFile("mpd/tsb_availstarttime.mpd"); auto& tl = - tree->m_periods[0]->GetAdaptationSets()[0]->GetRepresentations()[0]->SegmentTimeline(); + tree->m_periods[0]->GetAdaptationSets()[0]->GetRepresentations()[0]->Timeline(); EXPECT_EQ(tl.GetSize(), 1200); EXPECT_EQ(tl.GetFront()->m_number, 129069); diff --git a/src/test/TestHLSTree.cpp b/src/test/TestHLSTree.cpp index bd8f01664..ecc6106b4 100644 --- a/src/test/TestHLSTree.cpp +++ b/src/test/TestHLSTree.cpp @@ -292,8 +292,8 @@ TEST_F(HLSTreeTest, PtsSetInMultiPeriod) auto& periodSecond = tree->m_periods[1]; auto& adp0rep1 = periodSecond->GetAdaptationSets()[0]->GetRepresentations()[0]; - auto& adp0rep1seg1 = adp0rep1->SegmentTimeline().GetData().front(); - EXPECT_EQ(adp0rep1seg1.startPTS_, 0); + auto adp0rep1seg1 = adp0rep1->Timeline().GetFront(); + EXPECT_EQ(adp0rep1seg1->startPTS_, 0); } { auto& periodFirst = tree->m_periods[0]; @@ -308,8 +308,8 @@ TEST_F(HLSTreeTest, PtsSetInMultiPeriod) auto& periodSecond = tree->m_periods[1]; auto& adp1rep0 = periodSecond->GetAdaptationSets()[1]->GetRepresentations()[0]; - auto& adp1rep0seg1 = adp1rep0->SegmentTimeline().GetData().front(); - EXPECT_EQ(adp1rep0seg1.startPTS_, 0); + auto adp1rep0seg1 = adp1rep0->Timeline().GetFront(); + EXPECT_EQ(adp1rep0seg1->startPTS_, 0); } } diff --git a/src/test/TestSmoothTree.cpp b/src/test/TestSmoothTree.cpp index 7a62d4f94..7943269a5 100644 --- a/src/test/TestSmoothTree.cpp +++ b/src/test/TestSmoothTree.cpp @@ -95,21 +95,21 @@ TEST_F(SmoothTreeTest, CheckAsyncTimelineStartPTS) // Each start with different chunk timestamp // so to sync streams we adjust PTS with which has the lowest timestamp (CSmoothTree::m_ptsBase) auto& period = tree->m_periods[0]; - auto& segTL = period->GetAdaptationSets()[0]->GetRepresentations()[0]->SegmentTimeline(); + auto& segTL = period->GetAdaptationSets()[0]->GetRepresentations()[0]->Timeline(); EXPECT_EQ(segTL.GetSize(), 30); EXPECT_EQ(segTL.Get(0)->startPTS_, 7058030); EXPECT_EQ(segTL.Get(0)->m_time, 3903180167058030); EXPECT_EQ(segTL.Get(0)->m_number, 1); - segTL = period->GetAdaptationSets()[1]->GetRepresentations()[0]->SegmentTimeline(); + segTL = period->GetAdaptationSets()[1]->GetRepresentations()[0]->Timeline(); EXPECT_EQ(segTL.GetSize(), 30); EXPECT_EQ(segTL.Get(0)->startPTS_, 71363); EXPECT_EQ(segTL.Get(0)->m_time, 3903180160071363); EXPECT_EQ(segTL.Get(0)->m_number, 1); - segTL = period->GetAdaptationSets()[3]->GetRepresentations()[0]->SegmentTimeline(); + segTL = period->GetAdaptationSets()[3]->GetRepresentations()[0]->Timeline(); EXPECT_EQ(segTL.GetSize(), 29); EXPECT_EQ(segTL.Get(0)->startPTS_, 0);