diff --git a/src/common/AdaptationSet.cpp b/src/common/AdaptationSet.cpp index 6d2dd6fbf..b65f1fc35 100644 --- a/src/common/AdaptationSet.cpp +++ b/src/common/AdaptationSet.cpp @@ -215,3 +215,25 @@ CAdaptationSet* PLAYLIST::CAdaptationSet::FindMergeable( return nullptr; } + +PLAYLIST::CAdaptationSet* PLAYLIST::CAdaptationSet::FindByStreamType( + std::vector>& adpSets, StreamType type) +{ + auto itAdpSet = std::find_if(adpSets.cbegin(), adpSets.cend(), + [&type](const std::unique_ptr& item) + { return item->GetStreamType() == type; }); + if (itAdpSet != adpSets.cend()) + return (*itAdpSet).get(); + + return nullptr; +} + +PLAYLIST::CAdaptationSet* PLAYLIST::CAdaptationSet::FindByFirstAVStream( + std::vector>& adpSets) +{ + CAdaptationSet* adp = CAdaptationSet::FindByStreamType(adpSets, StreamType::VIDEO); + if (!adp) + adp = CAdaptationSet::FindByStreamType(adpSets, StreamType::AUDIO); + + return adp; +} diff --git a/src/common/AdaptationSet.h b/src/common/AdaptationSet.h index bf400c29f..f60d53619 100644 --- a/src/common/AdaptationSet.h +++ b/src/common/AdaptationSet.h @@ -85,10 +85,17 @@ class ATTR_DLL_LOCAL CAdaptationSet : public CCommonSegAttribs, public CCommonAt CSpinCache& SegmentTimelineDuration() { return m_segmentTimelineDuration; } bool HasSegmentTimelineDuration() { return !m_segmentTimelineDuration.IsEmpty(); } - std::optional& GetSegmentTemplate() { return m_segmentTemplate; } - std::optional GetSegmentTemplate() const { return m_segmentTemplate; } - void SetSegmentTemplate(const CSegmentTemplate& segTemplate) { m_segmentTemplate = segTemplate; } - bool HasSegmentTemplate() const { return m_segmentTemplate.has_value(); } + /*! + * \brief Get the timescale of segment durations tag. + * \return The timescale value if set, otherwise NO_VALUE. + */ + uint64_t GetSegDurationsTimescale() const { return m_segDurationsTimescale; } + + /*! + * \brief Set the timescale of segment durations tag. + * \param timescale The timescale value. + */ + void SetSegDurationsTimescale(const uint64_t timescale) { m_segDurationsTimescale = timescale; } void AddRepresentation(std::unique_ptr& representation); std::vector>& GetRepresentations() { return m_representations; } @@ -139,6 +146,22 @@ class ATTR_DLL_LOCAL CAdaptationSet : public CCommonSegAttribs, public CCommonAt static CAdaptationSet* FindMergeable(std::vector>& adpSets, CAdaptationSet* adpSet); + /*! + * \brief Try find the first adpSet of specified type. + * \param adpSets The adaptation set list where to search + * \param type The type to search + * \return The adaptation set if found, otherwise nullptr + */ + static CAdaptationSet* FindByStreamType(std::vector>& adpSets, + StreamType type); + + /*! + * \brief Try find the first video adpSet, if not found, try find the first audio adpSet. + * \param adpSets The adaptation set list where to search + * \return The adaptation set if found, otherwise nullptr + */ + static CAdaptationSet* FindByFirstAVStream(std::vector>& adpSets); + protected: std::vector> m_representations; @@ -157,8 +180,7 @@ class ATTR_DLL_LOCAL CAdaptationSet : public CCommonSegAttribs, public CCommonAt std::vector m_switchingIds; CSpinCache m_segmentTimelineDuration; - - std::optional m_segmentTemplate; + uint64_t m_segDurationsTimescale{NO_VALUE}; // Custom ISAdaptive attributes (used on DASH only) bool m_isImpaired{false}; diff --git a/src/common/AdaptiveTree.h b/src/common/AdaptiveTree.h index a19bcb6dd..f5f3aa64f 100644 --- a/src/common/AdaptiveTree.h +++ b/src/common/AdaptiveTree.h @@ -68,7 +68,7 @@ class ATTR_DLL_LOCAL AdaptiveTree std::string base_url_; std::optional initial_sequence_; // HLS only - uint64_t m_totalTimeSecs{0}; // Total playing time in seconds + uint64_t m_totalTimeSecs{0}; // Total playing time in seconds (can include all periods/chapters or timeshift) uint64_t stream_start_{0}; uint64_t available_time_{0}; uint64_t m_liveDelay{0}; // Apply a delay in seconds from the live edge diff --git a/src/common/CommonSegAttribs.cpp b/src/common/CommonSegAttribs.cpp index 759fe3274..7556dfa99 100644 --- a/src/common/CommonSegAttribs.cpp +++ b/src/common/CommonSegAttribs.cpp @@ -15,22 +15,6 @@ PLAYLIST::CCommonSegAttribs::CCommonSegAttribs(CCommonSegAttribs* parent /* = nu m_parentCommonSegAttribs = parent; } -std::optional& PLAYLIST::CCommonSegAttribs::GetSegmentList() -{ - if (m_segmentList.has_value()) - return m_segmentList; - if (m_parentCommonSegAttribs && m_parentCommonSegAttribs->m_segmentList.has_value()) - return m_parentCommonSegAttribs->m_segmentList; - - return m_segmentList; // Empty data -} - -bool PLAYLIST::CCommonSegAttribs::HasSegmentList() -{ - return m_segmentList.has_value() || - (m_parentCommonSegAttribs && m_parentCommonSegAttribs->m_segmentList.has_value()); -} - uint64_t PLAYLIST::CCommonSegAttribs::GetSegmentEndNr() { if (m_segEndNr.has_value()) diff --git a/src/common/CommonSegAttribs.h b/src/common/CommonSegAttribs.h index d20a54a5e..b3f7d5841 100644 --- a/src/common/CommonSegAttribs.h +++ b/src/common/CommonSegAttribs.h @@ -8,23 +8,29 @@ #pragma once +#include "SegTemplate.h" #include "SegmentList.h" #include namespace PLAYLIST { -// CCommonSegAttribs class provide attribute data -// of class itself or when not set of the parent class (if any). +// This class provide common place for shared members/methods +// with the possibility to retrieve the value from the parent class, when needed. class ATTR_DLL_LOCAL CCommonSegAttribs { public: CCommonSegAttribs(CCommonSegAttribs* parent = nullptr); virtual ~CCommonSegAttribs() {} - std::optional& GetSegmentList(); + std::optional& GetSegmentList() { return m_segmentList; } void SetSegmentList(const CSegmentList& segmentList) { m_segmentList = segmentList; } - bool HasSegmentList(); + bool HasSegmentList() { return m_segmentList.has_value(); } + + std::optional& GetSegmentTemplate() { return m_segmentTemplate; } + std::optional GetSegmentTemplate() const { return m_segmentTemplate; } + void SetSegmentTemplate(const CSegmentTemplate& segTemplate) { m_segmentTemplate = segTemplate; } + bool HasSegmentTemplate() const { return m_segmentTemplate.has_value(); } /*! * \brief Get the optional segment end number. Use HasSegmentEndNr method to know if the value is set. @@ -37,6 +43,7 @@ class ATTR_DLL_LOCAL CCommonSegAttribs protected: CCommonSegAttribs* m_parentCommonSegAttribs{nullptr}; std::optional m_segmentList; + std::optional m_segmentTemplate; std::optional m_segEndNr; }; diff --git a/src/common/Period.h b/src/common/Period.h index a14fce8bd..b96917b65 100644 --- a/src/common/Period.h +++ b/src/common/Period.h @@ -43,24 +43,45 @@ class ATTR_DLL_LOCAL CPeriod : public CCommonSegAttribs std::string GetBaseUrl() const { return m_baseUrl; } void SetBaseUrl(std::string_view baseUrl) { m_baseUrl = baseUrl; } - //! @todo: SetTimescale can be set/updated by period, adaptationSet and/or representation - //! maybe is possible improve how are updated these variables in a better way - uint32_t GetTimescale() const { return m_timescale; } - void SetTimescale(uint32_t timescale) { m_timescale = timescale; } - uint32_t GetSequence() const { return m_sequence; } void SetSequence(uint32_t sequence) { m_sequence = sequence; } + /*! + * \brief Get the start time, in ms. + * \return The start time value, otherwise NO_VALUE if not set. + */ uint64_t GetStart() const { return m_start; } + + /*! + * \brief Set the start time, in ms. + */ void SetStart(uint64_t start) { m_start = start; } uint64_t GetStartPTS() const { return m_startPts; } void SetStartPTS(uint64_t startPts) { m_startPts = startPts; } - - // Could be set also by adaptation set or representation (in ms) + + /*! + * \brief Get the duration, in timescale units. + * \return The duration value. + */ uint64_t GetDuration() const { return m_duration; } + + /*! + * \brief Set the duration, in timescale units. + */ void SetDuration(uint64_t duration) { m_duration = duration; } + /*! + * \brief Get the timescale unit. + * \return The timescale unit, if not set default value is 1000. + */ + uint32_t GetTimescale() const { return m_timescale; } + + /*! + * \brief Set the timescale unit. + */ + void SetTimescale(uint32_t timescale) { m_timescale = timescale; } + EncryptionState GetEncryptionState() const { return m_encryptionState; } void SetEncryptionState(EncryptionState encryptState) { m_encryptionState = encryptState; } @@ -74,11 +95,6 @@ class ATTR_DLL_LOCAL CPeriod : public CCommonSegAttribs CSpinCache& SegmentTimelineDuration() { return m_segmentTimelineDuration; } bool HasSegmentTimelineDuration() { return !m_segmentTimelineDuration.IsEmpty(); } - std::optional& GetSegmentTemplate() { return m_segmentTemplate; } - std::optional GetSegmentTemplate() const { return m_segmentTemplate; } - void SetSegmentTemplate(const CSegmentTemplate& segTemplate) { m_segmentTemplate = segTemplate; } - bool HasSegmentTemplate() const { return m_segmentTemplate.has_value(); } - void CopyHLSData(const CPeriod* other); void AddAdaptationSet(std::unique_ptr& adaptationSet); @@ -126,13 +142,12 @@ class ATTR_DLL_LOCAL CPeriod : public CCommonSegAttribs std::string m_baseUrl; uint32_t m_timescale{1000}; uint32_t m_sequence{0}; - uint64_t m_start{0}; + uint64_t m_start{NO_VALUE}; uint64_t m_startPts{0}; uint64_t m_duration{0}; EncryptionState m_encryptionState{EncryptionState::UNENCRYPTED}; bool m_isSecureDecoderNeeded{false}; CSpinCache m_segmentTimelineDuration; - std::optional m_segmentTemplate; }; } // namespace adaptive diff --git a/src/common/Representation.cpp b/src/common/Representation.cpp index 1ffdc937d..e60ea1e53 100644 --- a/src/common/Representation.cpp +++ b/src/common/Representation.cpp @@ -10,6 +10,8 @@ #include "utils/StringUtils.h" +#include + using namespace PLAYLIST; using namespace UTILS; @@ -50,3 +52,26 @@ void PLAYLIST::CRepresentation::CopyHLSData(const CRepresentation* other) m_isWaitForSegment = other->m_isWaitForSegment; m_initSegment = other->m_initSegment; } + +void PLAYLIST::CRepresentation::SetScaling() +{ + if (!m_timescale) + { + timescale_ext_ = timescale_int_ = 1; + return; + } + + timescale_ext_ = STREAM_TIME_BASE; + timescale_int_ = m_timescale; + + while (timescale_ext_ > 1) + { + if ((timescale_int_ / 10) * 10 == timescale_int_) + { + timescale_ext_ /= 10; + timescale_int_ /= 10; + } + else + break; + } +} diff --git a/src/common/Representation.h b/src/common/Representation.h index 8a3809622..8d9ac30c5 100644 --- a/src/common/Representation.h +++ b/src/common/Representation.h @@ -107,24 +107,35 @@ class ATTR_DLL_LOCAL CRepresentation : public CCommonSegAttribs, public CCommonA CSpinCache SegmentTimeline() const { return m_segmentTimeline; } bool HasSegmentTimeline() { return !m_segmentTimeline.IsEmpty(); } - std::optional& GetSegmentTemplate() { return m_segmentTemplate; } - std::optional GetSegmentTemplate() const { return m_segmentTemplate; } - void SetSegmentTemplate(const CSegmentTemplate& segTemplate) { m_segmentTemplate = segTemplate; } - bool HasSegmentTemplate() const { return m_segmentTemplate.has_value(); } - std::optional& GetSegmentBase() { return m_segmentBase; } void SetSegmentBase(const CSegmentBase& segBase) { m_segmentBase = segBase; } bool HasSegmentBase() const { return m_segmentBase.has_value(); } - uint32_t GetTimescale() const { return m_timescale; } - void SetTimescale(uint32_t timescale) { m_timescale = timescale; } - uint64_t GetStartNumber() const { return m_startNumber; } void SetStartNumber(uint64_t startNumber) { m_startNumber = startNumber; } + /*! + * \brief Get the duration, in timescale units. + * \return The duration value. + */ uint64_t GetDuration() const { return m_duration; } + + /*! + * \brief Set the duration, in timescale units. + */ void SetDuration(uint64_t duration) { m_duration = duration; } + /*! + * \brief Get the timescale unit. + * \return The timescale unit, otherwise 0 if not set. + */ + uint32_t GetTimescale() const { return m_timescale; } + + /*! + * \brief Set the timescale unit. + */ + void SetTimescale(uint32_t timescale) { m_timescale = timescale; } + /*! * \brief Determines when the representation contains subtitles as single file * for the entire duration of the video. @@ -225,28 +236,7 @@ class ATTR_DLL_LOCAL CRepresentation : public CCommonSegAttribs, public CCommonA uint32_t timescale_ext_{0}; uint32_t timescale_int_{0}; - void SetScaling() - { - if (!m_timescale) - { - timescale_ext_ = timescale_int_ = 1; - return; - } - - timescale_ext_ = 1000000; - timescale_int_ = m_timescale; - - while (timescale_ext_ > 1) - { - if ((timescale_int_ / 10) * 10 == timescale_int_) - { - timescale_ext_ /= 10; - timescale_int_ /= 10; - } - else - break; - } - } + void SetScaling(); std::chrono::time_point repLastUpdated_; @@ -269,7 +259,6 @@ class ATTR_DLL_LOCAL CRepresentation : public CCommonSegAttribs, public CCommonA uint16_t m_hdcpVersion{0}; // 0 if not set - std::optional m_segmentTemplate; std::optional m_segmentBase; std::optional m_initSegment; diff --git a/src/common/SegTemplate.cpp b/src/common/SegTemplate.cpp index 854bb2174..4335fa8e5 100644 --- a/src/common/SegTemplate.cpp +++ b/src/common/SegTemplate.cpp @@ -19,9 +19,10 @@ using namespace PLAYLIST; using namespace UTILS; using namespace kodi::tools; -PLAYLIST::CSegmentTemplate::CSegmentTemplate(CSegmentTemplate* parent /* = nullptr */) +PLAYLIST::CSegmentTemplate::CSegmentTemplate(const std::optional& other) { - m_parentSegTemplate = parent; + if (other.has_value()) + *this = *other; } std::string PLAYLIST::CSegmentTemplate::GetInitialization() const @@ -29,9 +30,6 @@ std::string PLAYLIST::CSegmentTemplate::GetInitialization() const if (!m_initialization.empty()) return m_initialization; - if (m_parentSegTemplate) - return m_parentSegTemplate->GetInitialization(); - return ""; // Default value } @@ -40,9 +38,6 @@ std::string PLAYLIST::CSegmentTemplate::GetMedia() const if (!m_media.empty()) return m_media; - if (m_parentSegTemplate) - return m_parentSegTemplate->GetMedia(); - return ""; // Default value } @@ -51,9 +46,6 @@ uint32_t PLAYLIST::CSegmentTemplate::GetTimescale() const if (m_timescale.has_value()) return *m_timescale; - if (m_parentSegTemplate) - return m_parentSegTemplate->GetTimescale(); - return 0; // Default value } @@ -62,9 +54,6 @@ uint32_t PLAYLIST::CSegmentTemplate::GetDuration() const if (m_duration.has_value()) return *m_duration; - if (m_parentSegTemplate) - return m_parentSegTemplate->GetDuration(); - return 0; // Default value } @@ -73,9 +62,6 @@ uint64_t PLAYLIST::CSegmentTemplate::GetStartNumber() const if (m_startNumber.has_value()) return *m_startNumber; - if (m_parentSegTemplate) - return m_parentSegTemplate->GetStartNumber(); - return 1; // Default value } @@ -84,9 +70,6 @@ uint64_t PLAYLIST::CSegmentTemplate::GetEndNumber() const if (m_endNumber.has_value()) return *m_endNumber; - if (m_parentSegTemplate) - return m_parentSegTemplate->GetEndNumber(); - return 0; // Default value } diff --git a/src/common/SegTemplate.h b/src/common/SegTemplate.h index 65551b55d..30d77ff49 100644 --- a/src/common/SegTemplate.h +++ b/src/common/SegTemplate.h @@ -28,8 +28,9 @@ class CSegment; class ATTR_DLL_LOCAL CSegmentTemplate { public: - CSegmentTemplate(CSegmentTemplate* parent = nullptr); - ~CSegmentTemplate() {} + CSegmentTemplate() = default; + CSegmentTemplate(const std::optional& other); + ~CSegmentTemplate() = default; std::string GetInitialization() const; void SetInitialization(std::string_view init) { m_initialization = init; } @@ -75,8 +76,6 @@ class ATTR_DLL_LOCAL CSegmentTemplate std::optional m_duration; std::optional m_startNumber; std::optional m_endNumber; - - CSegmentTemplate* m_parentSegTemplate{nullptr}; }; } // namespace PLAYLIST diff --git a/src/common/Segment.h b/src/common/Segment.h index b2d9d76b4..afd5491aa 100644 --- a/src/common/Segment.h +++ b/src/common/Segment.h @@ -39,7 +39,7 @@ class ATTR_DLL_LOCAL CSegment uint64_t m_duration = 0; // If available gives the media duration of a segment (depends on type of stream e.g. HLS) uint16_t pssh_set_ = PSSHSET_POS_DEFAULT; - uint64_t m_time{0}; + uint64_t m_time{0}; // The start time, in timescale units uint64_t m_number{0}; /*! diff --git a/src/common/SegmentList.cpp b/src/common/SegmentList.cpp index 8d8c68350..7ed5994bb 100644 --- a/src/common/SegmentList.cpp +++ b/src/common/SegmentList.cpp @@ -11,32 +11,10 @@ using namespace PLAYLIST; -uint64_t PLAYLIST::CSegmentList::GetStartNumber() const +PLAYLIST::CSegmentList::CSegmentList(const std::optional& other) { - if (m_startNumber > 0 || !m_parentSegList) - return m_startNumber; - return m_parentSegList->GetStartNumber(); -} - -uint64_t PLAYLIST::CSegmentList::GetDuration() const -{ - if (m_duration > 0 || !m_parentSegList) - return m_duration; - return m_parentSegList->GetDuration(); -} - -uint32_t PLAYLIST::CSegmentList::GetTimescale() const -{ - if (m_timescale > 0 || !m_parentSegList) - return m_timescale; - return m_parentSegList->GetTimescale(); -} - -uint64_t PLAYLIST::CSegmentList::GetPresTimeOffset() const -{ - if (m_ptsOffset > 0 || !m_parentSegList) - return m_ptsOffset; - return m_parentSegList->GetPresTimeOffset(); + if (other.has_value()) + *this = *other; } void PLAYLIST::CSegmentList::SetInitRange(std::string_view range) diff --git a/src/common/SegmentList.h b/src/common/SegmentList.h index ae6ded345..ebefeb276 100644 --- a/src/common/SegmentList.h +++ b/src/common/SegmentList.h @@ -16,6 +16,7 @@ #include #endif +#include #include #include @@ -27,19 +28,20 @@ class CSegment; class ATTR_DLL_LOCAL CSegmentList { public: - CSegmentList(CSegmentList* parent = nullptr) { m_parentSegList = parent; } - ~CSegmentList() {} + CSegmentList() = default; + CSegmentList(const std::optional& other); + ~CSegmentList() = default; - uint64_t GetStartNumber() const; + uint64_t GetStartNumber() const { return m_startNumber; } void SetStartNumber(uint64_t startNumber) { m_startNumber = startNumber; } - uint64_t GetDuration() const; + uint64_t GetDuration() const { return m_duration; } void SetDuration(uint64_t duration) { m_duration = duration; } - uint32_t GetTimescale() const; + uint32_t GetTimescale() const { return m_timescale; } void SetTimescale(uint32_t timescale) { m_timescale = timescale; } - uint64_t GetPresTimeOffset() const; + uint64_t GetPresTimeOffset() const { return m_ptsOffset; } void SetPresTimeOffset(uint64_t ptsOffset) { m_ptsOffset = ptsOffset; } void SetInitSourceUrl(std::string_view url) { m_initSourceUrl = url; } @@ -56,8 +58,6 @@ class ATTR_DLL_LOCAL CSegmentList uint64_t m_initRangeBegin = NO_VALUE; uint64_t m_initRangeEnd = NO_VALUE; std::string m_initSourceUrl; - - CSegmentList* m_parentSegList{nullptr}; }; } // namespace adaptive diff --git a/src/parser/DASHTree.cpp b/src/parser/DASHTree.cpp index 1133dbd7b..bf359bc5c 100644 --- a/src/parser/DASHTree.cpp +++ b/src/parser/DASHTree.cpp @@ -188,56 +188,94 @@ bool adaptive::CDashTree::ParseManifest(const std::string& data) ParseTagPeriod(node, mpdUrl); } - // Cleanup periods - bool hasTotalTimeSecs = m_totalTimeSecs > 0; + // For multi-periods streaming must be ensured the duration of each period: + // - If "duration" attribute is provided on each Period tag, do nothing + // - If "duration" attribute is missing, but "start" attribute, use this last one to calculate the duration + // - If both attributes are missing, try get the duration from a representation + + uint64_t totalDuration{0}; // Calculated duration, in seconds + double mpdTotalDuration = m_mediaPresDuration; // MPD total duration, in seconds + if (mpdTotalDuration == 0) + mpdTotalDuration = m_timeShiftBufferDepth; for (auto itPeriod = m_periods.begin(); itPeriod != m_periods.end();) { auto& period = *itPeriod; - if (hasTotalTimeSecs && period->GetDuration() == 0) + + // Skip periods with duration already provided + if (period->GetDuration() > 0) { - auto nextPeriod = itPeriod + 1; - if (nextPeriod == m_periods.end()) + ++itPeriod; + continue; + } + + auto nextPeriod = itPeriod + 1; + if (nextPeriod == m_periods.end()) // Next period, not found + { + if (period->GetStart() != NO_VALUE && mpdTotalDuration > 0) { - period->SetDuration( - ((m_totalTimeSecs * 1000 - period->GetStart()) * period->GetTimescale()) / 1000); + period->SetDuration(static_cast((mpdTotalDuration * 1000 - period->GetStart()) * + period->GetTimescale() / 1000)); } - else + else // Try get duration / timescale from a representation { - period->SetDuration( - (((*nextPeriod)->GetStart() - period->GetStart()) * period->GetTimescale()) / 1000); + CAdaptationSet* adp = CAdaptationSet::FindByFirstAVStream(period->GetAdaptationSets()); + if (adp) + { + auto& rep = adp->GetRepresentations()[0]; + if (rep->GetDuration() > 0) + { + period->SetDuration(rep->GetDuration()); + period->SetTimescale(rep->GetTimescale()); + totalDuration += rep->GetDuration() / rep->GetTimescale(); + } + } } } - if (period->GetAdaptationSets().empty()) + else // Next period, found { - if (hasTotalTimeSecs) - m_totalTimeSecs -= period->GetDuration() / period->GetTimescale(); - - itPeriod = m_periods.erase(itPeriod); + if (period->GetStart() != NO_VALUE && (*nextPeriod)->GetStart() != NO_VALUE) + { + period->SetDuration(static_cast((*nextPeriod)->GetStart() - period->GetStart()) * + period->GetTimescale() / 1000); + } + else // Try get duration / timescale from a representation + { + CAdaptationSet* adp = CAdaptationSet::FindByFirstAVStream(period->GetAdaptationSets()); + if (adp) + { + auto& rep = adp->GetRepresentations()[0]; + if (rep->GetDuration() > 0) + { + period->SetDuration(rep->GetDuration()); + period->SetTimescale(rep->GetTimescale()); + totalDuration += rep->GetDuration() / rep->GetTimescale(); + } + } + } } - else - { - if (!hasTotalTimeSecs) - m_totalTimeSecs += period->GetDuration() / period->GetTimescale(); - itPeriod++; - } + ++itPeriod; } + if (mpdTotalDuration > 0) + m_totalTimeSecs = static_cast(mpdTotalDuration); + else + m_totalTimeSecs = totalDuration; + return true; } void adaptive::CDashTree::ParseTagMPDAttribs(pugi::xml_node nodeMPD) { - double mediaPresDuration = + m_mediaPresDuration = XML::ParseDuration(XML::GetAttrib(nodeMPD, "mediaPresentationDuration")); m_isLive = XML::GetAttrib(nodeMPD, "type") == "dynamic"; - double timeShiftBufferDepth{0}; std::string timeShiftBufferDepthStr; if (XML::QueryAttrib(nodeMPD, "timeShiftBufferDepth", timeShiftBufferDepthStr)) - timeShiftBufferDepth = XML::ParseDuration(timeShiftBufferDepthStr); + m_timeShiftBufferDepth = XML::ParseDuration(timeShiftBufferDepthStr); std::string availabilityStartTimeStr; if (XML::QueryAttrib(nodeMPD, "availabilityStartTime", availabilityStartTimeStr)) @@ -255,11 +293,6 @@ void adaptive::CDashTree::ParseTagMPDAttribs(pugi::xml_node nodeMPD) m_allowInsertLiveSegments = m_minimumUpdatePeriod == 0; m_updateInterval = static_cast(duration * 1000); } - - if (mediaPresDuration == 0) - m_totalTimeSecs = static_cast(timeShiftBufferDepth); - else - m_totalTimeSecs = static_cast(mediaPresDuration); } void adaptive::CDashTree::ParseTagPeriod(pugi::xml_node nodePeriod, std::string_view mpdUrl) @@ -269,9 +302,13 @@ void adaptive::CDashTree::ParseTagPeriod(pugi::xml_node nodePeriod, std::string_ period->SetSequence(m_periodCurrentSeq++); // Parse attributes + period->SetId(XML::GetAttrib(nodePeriod, "id")); - period->SetStart( - static_cast(XML::ParseDuration(XML::GetAttrib(nodePeriod, "start")) * 1000)); + + std::string_view start = XML::GetAttrib(nodePeriod, "start"); + if (!start.empty()) + period->SetStart(static_cast(XML::ParseDuration(start) * 1000)); + period->SetDuration( static_cast(XML::ParseDuration(XML::GetAttrib(nodePeriod, "duration")) * 1000)); @@ -307,15 +344,6 @@ void adaptive::CDashTree::ParseTagPeriod(pugi::xml_node nodePeriod, std::string_ segTemplate.GetTimescale()); period->SetStartPTS(startPts); - - if (period->GetDuration() == 0 && segTemplate.GetTimescale() > 0) - { - // Calculate total duration of segments - auto& segTLData = period->SegmentTimelineDuration().GetData(); - uint32_t sum = std::accumulate(segTLData.begin(), segTLData.end(), 0); - period->SetDuration(sum); - period->SetTimescale(segTemplate.GetTimescale()); - } } period->SetSegmentTemplate(segTemplate); @@ -333,17 +361,11 @@ void adaptive::CDashTree::ParseTagPeriod(pugi::xml_node nodePeriod, std::string_ uint64_t duration; if (XML::QueryAttrib(nodeSeglist, "duration", duration)) - { segList.SetDuration(duration); - period->SetDuration(duration); - } uint32_t timescale; if (XML::QueryAttrib(nodeSeglist, "timescale", timescale)) - { segList.SetTimescale(timescale); - period->SetTimescale(timescale); - } period->SetSegmentList(segList); } @@ -500,75 +522,43 @@ void adaptive::CDashTree::ParseTagAdaptationSet(pugi::xml_node nodeAdp, PLAYLIST adpSet->SetBaseUrl(URL::Join(period->GetBaseUrl(), baseUrlText)); } - // Parse tag - // No dash spec, looks like a custom Amazon video service implementation - xml_node nodeSegDur = nodeAdp.child("SegmentDurations"); - if (nodeSegDur) - { - period->SetTimescale(XML::GetAttribUint32(nodeSegDur, "timescale", 1000)); - - // Parse tags - e.g. - // add all duration values as timeline segments - for (xml_node node : nodeSegDur.children("S")) - { - adpSet->SegmentTimelineDuration().GetData().emplace_back(XML::GetAttribUint32(node, "d")); - } - } - // Parse tag xml_node nodeSegTpl = nodeAdp.child("SegmentTemplate"); - if (nodeSegTpl) + if (nodeSegTpl || period->HasSegmentTemplate()) { - CSegmentTemplate segTemplate; - if (period->HasSegmentTemplate()) - segTemplate = *period->GetSegmentTemplate(); + CSegmentTemplate segTemplate{period->GetSegmentTemplate()}; - ParseSegmentTemplate(nodeSegTpl, &segTemplate); - - // Parse child - xml_node nodeSegTL = nodeSegTpl.child("SegmentTimeline"); - if (nodeSegTL) + if (nodeSegTpl) { - uint64_t startPts = ParseTagSegmentTimeline(nodeSegTL, adpSet->SegmentTimelineDuration(), - segTemplate.GetTimescale()); + ParseSegmentTemplate(nodeSegTpl, &segTemplate); - adpSet->SetStartPTS(startPts); - - if (period->GetDuration() == 0 && segTemplate.GetTimescale() > 0) + // Parse child + xml_node nodeSegTL = nodeSegTpl.child("SegmentTimeline"); + if (nodeSegTL) { - // Calculate total duration of segments - auto& segTLData = adpSet->SegmentTimelineDuration().GetData(); - uint32_t sum = std::accumulate(segTLData.begin(), segTLData.end(), 0); - period->SetDuration(sum); - period->SetTimescale(segTemplate.GetTimescale()); + uint64_t startPts = ParseTagSegmentTimeline(nodeSegTL, adpSet->SegmentTimelineDuration(), + segTemplate.GetTimescale()); + + adpSet->SetStartPTS(startPts); } } + adpSet->SetSegmentTemplate(segTemplate); } - else if (period->HasSegmentTemplate()) - adpSet->SetSegmentTemplate(*period->GetSegmentTemplate()); // Parse tag xml_node nodeSeglist = nodeAdp.child("SegmentList"); if (nodeSeglist) { - CSegmentList segList; - if (adpSet->HasSegmentList()) - segList = *adpSet->GetSegmentList(); + CSegmentList segList{adpSet->GetSegmentList()}; uint64_t duration; if (XML::QueryAttrib(nodeSeglist, "duration", duration)) - { segList.SetDuration(duration); - period->SetDuration(duration); - } uint32_t timescale; if (XML::QueryAttrib(nodeSeglist, "timescale", timescale)) - { segList.SetTimescale(timescale); - period->SetTimescale(timescale); - } uint64_t presTimeOffset; if (XML::QueryAttrib(nodeSeglist, "presentationTimeOffset", presTimeOffset)) @@ -591,6 +581,25 @@ void adaptive::CDashTree::ParseTagAdaptationSet(pugi::xml_node nodeAdp, PLAYLIST } } + // Parse tag + // No dash spec, looks like a custom Amazon video service implementation + // used to define the duration of each SegmentURL in the SegmentList + xml_node nodeSegDur = nodeAdp.child("SegmentDurations"); + if (nodeSegDur) + { + uint64_t timescale; + if (XML::QueryAttrib(nodeSegDur, "timescale", timescale)) + adpSet->SetSegDurationsTimescale(timescale); + + // Parse tags - e.g. + // add all duration values as timeline segments + for (xml_node node : nodeSegDur.children("S")) + { + adpSet->SegmentTimelineDuration().GetData().emplace_back(XML::GetAttribUint32(node, "d")); + } + } + + // Parse child tags for (xml_node node : nodeAdp.children("Representation")) { @@ -804,55 +813,46 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, // Parse tag xml_node nodeSegTpl = nodeRepr.child("SegmentTemplate"); - if (nodeSegTpl) + if (nodeSegTpl || adpSet->HasSegmentTemplate()) { - CSegmentTemplate segTemplate; - if (adpSet->HasSegmentTemplate()) - segTemplate = *adpSet->GetSegmentTemplate(); - - ParseSegmentTemplate(nodeSegTpl, &segTemplate); - - if (segTemplate.HasInitialization()) - repr->SetInitSegment(segTemplate.MakeInitSegment()); + CSegmentTemplate segTemplate{adpSet->GetSegmentTemplate()}; - // Parse child - xml_node nodeSegTL = nodeSegTpl.child("SegmentTimeline"); - if (nodeSegTL) + if (nodeSegTpl) { - uint64_t totalTimeSecs{0}; - if (repr->GetDuration() > 0 && repr->GetTimescale() > 0) - totalTimeSecs = repr->GetDuration() / repr->GetTimescale(); + ParseSegmentTemplate(nodeSegTpl, &segTemplate); - uint64_t startPts = - ParseTagSegmentTimeline(nodeSegTL, repr->SegmentTimeline(), segTemplate.GetTimescale(), - totalTimeSecs, &segTemplate); + // Parse child + xml_node nodeSegTL = nodeSegTpl.child("SegmentTimeline"); + if (nodeSegTL) + { + uint64_t startPts = + ParseTagSegmentTimeline(nodeSegTL, repr->SegmentTimeline(), segTemplate); - repr->nextPts_ = startPts; + repr->nextPts_ = startPts; + } } - repr->SetTimescale(segTemplate.GetTimescale()); - repr->SetSegmentTemplate(segTemplate); - repr->SetStartNumber(segTemplate.GetStartNumber()); - } - else if (adpSet->HasSegmentTemplate()) - { - CSegmentTemplate segTemplate = *adpSet->GetSegmentTemplate(); - repr->SetTimescale(segTemplate.GetTimescale()); - repr->SetSegmentTemplate(segTemplate); + if (repr->HasSegmentTimeline()) + { + auto& lastSeg = repr->SegmentTimeline().GetData().back(); + uint64_t totalDuration = lastSeg.startPTS_ + lastSeg.m_duration; + repr->SetDuration(totalDuration); + repr->SetTimescale(segTemplate.GetTimescale()); + } - repr->SetStartNumber(segTemplate.GetStartNumber()); + repr->SetSegmentTemplate(segTemplate); if (segTemplate.HasInitialization()) repr->SetInitSegment(segTemplate.MakeInitSegment()); + + repr->SetStartNumber(segTemplate.GetStartNumber()); } // Parse tag xml_node nodeSeglist = nodeRepr.child("SegmentList"); if (nodeSeglist) { - CSegmentList segList; - if (repr->HasSegmentList()) - segList = *repr->GetSegmentList(); + CSegmentList segList{adpSet->GetSegmentList()}; uint64_t duration; if (XML::QueryAttrib(nodeSeglist, "duration", duration)) @@ -870,13 +870,11 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, if (XML::QueryAttrib(nodeSeglist, "startNumber", startNumber)) segList.SetStartNumber(startNumber); - if (segList.GetStartNumber() > 0) - repr->SetStartNumber(segList.GetStartNumber()); - if (segList.GetStartNumber() > 0) { repr->SetStartNumber(segList.GetStartNumber()); - + //! @todo: SetPresTimeOffset is used with a calculation with repr->GetDuration() + //! that at this point of code has no value, must be fixed segList.SetPresTimeOffset(segList.GetPresTimeOffset() + repr->GetStartNumber() * repr->GetDuration()); } @@ -896,16 +894,18 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, repr->SetInitSegment(segList.MakeInitSegment()); } - if (segList.GetTimescale() > 0 && segList.GetDuration() > 0) - { - // Reserve memory to speedup - repr->SegmentTimeline().GetData().reserve( - EstimateSegmentsCount(segList.GetDuration(), segList.GetTimescale())); - } + + // Reserve memory to speedup + repr->SegmentTimeline().GetData().reserve( + EstimateSegmentsCount(segList.GetDuration(), segList.GetTimescale())); // Parse child tags size_t index{0}; uint64_t previousStartPts{0}; + // If tag is present it could use a different timescale + bool isTsRescale = adpSet->GetSegDurationsTimescale() != NO_VALUE && + adpSet->GetSegDurationsTimescale() != segList.GetTimescale(); + for (xml_node node : nodeSeglist.children("SegmentURL")) { CSegment seg; @@ -924,8 +924,21 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, seg.range_end_ = rangeEnd; } - uint32_t* tlDuration = adpSet->SegmentTimelineDuration().Get(index); - uint64_t duration = tlDuration ? *tlDuration : segList.GetDuration(); + uint64_t duration; + uint32_t* sdDuration = adpSet->SegmentTimelineDuration().Get(index); // tag is present + if (sdDuration) + { + duration = *sdDuration; + if (isTsRescale) + { + duration = + static_cast(static_cast(duration) / + adpSet->GetSegDurationsTimescale() * segList.GetTimescale()); + } + } + else + duration = segList.GetDuration(); + if (isTimelineEmpty) seg.startPTS_ = segList.GetPresTimeOffset(); else @@ -938,24 +951,12 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, repr->nextPts_ = seg.startPTS_; } - if (period->GetDuration() == 0 && segList.GetTimescale() > 0) - { - // Calculate total duration of segments - auto& segTLData = adpSet->SegmentTimelineDuration().GetData(); - uint32_t sum = std::accumulate(segTLData.begin(), segTLData.end(), 0); - - if (segList.GetDuration() == 0) - segList.SetDuration(sum); - - period->SetDuration(sum); - period->SetTimescale(segList.GetTimescale()); - } - - if (segList.GetDuration() > 0) - repr->SetDuration(segList.GetDuration()); + // Calculate total duration of segments + auto& segTLData = adpSet->SegmentTimelineDuration().GetData(); + uint64_t sum = std::accumulate(segTLData.begin(), segTLData.end(), 0ULL); - if (segList.GetTimescale() > 0) - repr->SetTimescale(segList.GetTimescale()); + repr->SetDuration(sum); + repr->SetTimescale(segList.GetTimescale()); repr->SetSegmentList(segList); } @@ -1039,18 +1040,60 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, } // Generate timeline segments - if (repr->HasSegmentTemplate()) + if (repr->HasSegmentTemplate() && !repr->HasSegmentTimeline()) { auto& segTemplate = repr->GetSegmentTemplate(); - uint64_t reprTotalTimeSecs = m_totalTimeSecs; - if (period->GetDuration() > 0) - reprTotalTimeSecs = period->GetDuration() / period->GetTimescale(); - - if (!segTemplate->GetMedia().empty() && reprTotalTimeSecs > 0 && - segTemplate->GetTimescale() > 0 && - (segTemplate->GetDuration() > 0 || adpSet->HasSegmentTimelineDuration())) + if (segTemplate->GetMedia().empty()) + { + LOG::LogF(LOGWARNING, + "Cannot generate segments timeline, SegmentTemplate has no media attribute."); + } + else if (segTemplate->GetTimescale() == 0) + { + LOG::LogF(LOGWARNING, + "Cannot generate segments timeline, SegmentTemplate has no timescale attribute."); + } + else if (segTemplate->GetDuration() == 0 && !adpSet->HasSegmentTimelineDuration()) + { + // In the SegmentTemplate tag must be present the "duration" attribute or the SegmentTimeline tag + LOG::LogF(LOGWARNING, + "Cannot generate segments timeline, SegmentTemplate has no duration attribute."); + } + else { + repr->SetTimescale(segTemplate->GetTimescale()); + + // Set the representation duration + if (period->GetDuration() > 0) + { + double durationSecs = static_cast(period->GetDuration()) / period->GetTimescale(); + repr->SetDuration(static_cast(durationSecs * segTemplate->GetTimescale())); + } + else if (adpSet->HasSegmentTimelineDuration()) + { + // Calculate total duration of segments from timeline + auto& segTLData = adpSet->SegmentTimelineDuration().GetData(); + repr->SetDuration(std::accumulate(segTLData.begin(), segTLData.end(), 0ULL)); + + } + else if (m_mediaPresDuration > 0) + { + // Note m_mediaPresDuration include the time of each period, use it may not be always correct + // maybe segment timeline generation code should be done after the full manifest parsing + // where we can determinate/calculate the duration of each period + repr->SetDuration(static_cast(m_mediaPresDuration * segTemplate->GetTimescale())); + } + else if (m_timeShiftBufferDepth > 0) + { + repr->SetDuration(static_cast(m_timeShiftBufferDepth * segTemplate->GetTimescale())); + } + else // Single segment + { + repr->SetDuration(segTemplate->GetDuration()); + } + + // Calculate total segments size_t segmentsCount{0}; if (adpSet->HasSegmentTimelineDuration()) @@ -1066,45 +1109,46 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, segmentsCount = repr->GetSegmentEndNr(); else // Calculate the number of segments { - double lengthSecs = - static_cast(segTemplate->GetDuration()) / segTemplate->GetTimescale(); - segmentsCount = - static_cast(std::ceil(static_cast(reprTotalTimeSecs) / lengthSecs)); + segmentsCount = static_cast( + std::ceil(static_cast(repr->GetDuration()) / segTemplate->GetDuration())); } } uint64_t segStartNumber = repr->GetStartNumber(); - uint64_t segStartPts = adpSet->GetStartPTS(); - + //! @todo: In the following condition the "segTemplate->GetDuration() > 0" + //! could means "only for use cases that not provide SegmentTimeline tag" + //! if so should be changed with HasSegmentTimelineDuration if (m_isLive && !segTemplate->HasVariableTime() && segTemplate->GetDuration() > 0) { - uint64_t sampleTime = period->GetStart() / 1000; - segStartNumber += static_cast( - static_cast(stream_start_ - available_time_ - reprTotalTimeSecs - sampleTime) * - segTemplate->GetTimescale() / segTemplate->GetDuration() + - 1); - } - else if (segTemplate->GetDuration() == 0 && adpSet->HasSegmentTimelineDuration()) - { - uint32_t duration = - static_cast((reprTotalTimeSecs * segTemplate->GetTimescale()) / - adpSet->SegmentTimelineDuration().GetSize()); - segTemplate->SetDuration(duration); + uint64_t sampleTime{0}; + if (period->GetStart() != NO_VALUE) + sampleTime = period->GetStart() / 1000; + + int64_t reprDurationSecs = repr->GetDuration() / repr->GetTimescale(); + segStartNumber += (stream_start_ - available_time_ - reprDurationSecs - sampleTime) * + segTemplate->GetTimescale() / segTemplate->GetDuration() + + 1; } - uint32_t segTplDuration = segTemplate->GetDuration(); + // Get the default segment duration + uint64_t segDefDuration; + if (adpSet->HasSegmentTimelineDuration()) + segDefDuration = repr->GetDuration() / adpSet->SegmentTimelineDuration().GetSize(); + else + segDefDuration = segTemplate->GetDuration(); + // Reserve memory to speedup repr->SegmentTimeline().GetData().reserve(segmentsCount); CSegment seg; seg.m_number = segStartNumber; - seg.startPTS_ = segStartPts; - seg.m_time = segStartPts; + seg.startPTS_ = adpSet->GetStartPTS(); + seg.m_time = adpSet->GetStartPTS(); for (size_t pos{0}; pos < segmentsCount; pos++) { uint32_t* tlDuration = adpSet->SegmentTimelineDuration().Get(pos); - uint32_t duration = tlDuration ? *tlDuration : segTplDuration; + uint64_t duration = tlDuration ? *tlDuration : segDefDuration; seg.m_duration = duration; repr->SegmentTimeline().GetData().push_back(seg); @@ -1125,12 +1169,6 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, } } - // Sanitize period - if (period->GetTimescale() == 0) - period->SetTimescale(repr->GetTimescale()); - if (period->GetDuration() == 0) - period->SetDuration(repr->GetDuration()); - // YouTube fix if (repr->GetStartNumber() > m_firstStartNumber) m_firstStartNumber = repr->GetStartNumber(); @@ -1157,11 +1195,9 @@ uint64_t adaptive::CDashTree::ParseTagSegmentTimeline(pugi::xml_node nodeSegTL, if (SCTimeline.IsEmpty()) { - if (duration > 0) - { - // Reserve memory to speedup - SCTimeline.GetData().reserve(EstimateSegmentsCount(duration, timescale)); - } + // Reserve memory to speedup + SCTimeline.GetData().reserve(EstimateSegmentsCount(duration, timescale)); + startPts = time; nextPts = time; } @@ -1187,14 +1223,10 @@ uint64_t adaptive::CDashTree::ParseTagSegmentTimeline(pugi::xml_node nodeSegTL, uint64_t adaptive::CDashTree::ParseTagSegmentTimeline(xml_node nodeSegTL, CSpinCache& SCTimeline, - uint32_t timescale /* = 1000 */, - uint64_t totalTimeSecs /* = 0 */, - CSegmentTemplate* segTemplate /* = nullptr */) + CSegmentTemplate& segTemplate) { uint64_t startPts{0}; - uint64_t startNumber{0}; - if (segTemplate) - startNumber = segTemplate->GetStartNumber(); + uint64_t startNumber = segTemplate.GetStartNumber(); // Parse tags - e.g. uint64_t nextPts{0}; @@ -1212,13 +1244,8 @@ uint64_t adaptive::CDashTree::ParseTagSegmentTimeline(xml_node nodeSegTL, if (SCTimeline.IsEmpty()) { // Reserve memory to speedup operations - if (segTemplate && segTemplate->GetDuration() > 0 && segTemplate->GetTimescale() > 0) - { - SCTimeline.GetData().reserve(EstimateSegmentsCount( - segTemplate->GetDuration(), segTemplate->GetTimescale(), totalTimeSecs)); - } - else - SCTimeline.GetData().reserve(EstimateSegmentsCount(duration, timescale, totalTimeSecs)); + SCTimeline.GetData().reserve(EstimateSegmentsCount( + duration > 0 ? duration : segTemplate.GetDuration(), segTemplate.GetTimescale())); seg.m_number = startNumber; } @@ -1227,6 +1254,7 @@ uint64_t adaptive::CDashTree::ParseTagSegmentTimeline(xml_node nodeSegTL, seg.m_time = nextPts; seg.startPTS_ = nextPts; + seg.m_duration = duration; for (; repeat > 0; --repeat) { @@ -1237,6 +1265,7 @@ uint64_t adaptive::CDashTree::ParseTagSegmentTimeline(xml_node nodeSegTL, seg.m_time = nextPts; seg.m_number += 1; seg.startPTS_ += duration; + seg.m_duration = duration; } } else @@ -1482,18 +1511,17 @@ uint32_t adaptive::CDashTree::ParseAudioChannelConfig(pugi::xml_node node) return channels; } -size_t adaptive::CDashTree::EstimateSegmentsCount(uint64_t duration, - uint32_t timescale, - uint64_t totalTimeSecs /* = 0 */) +size_t adaptive::CDashTree::EstimateSegmentsCount(uint64_t duration, uint32_t timescale) const { + if (timescale == 0) + timescale = 1; + double lengthSecs{static_cast(duration) / timescale}; if (lengthSecs < 1) lengthSecs = 1; - if (totalTimeSecs == 0) - totalTimeSecs = std::max(m_totalTimeSecs, static_cast(1)); - - return static_cast(totalTimeSecs / lengthSecs); + double totalTimeSecs = std::max(m_mediaPresDuration, 1.0); + return std::max(static_cast(totalTimeSecs / lengthSecs), static_cast(1)); } void adaptive::CDashTree::MergeAdpSets() @@ -1667,7 +1695,8 @@ void adaptive::CDashTree::RefreshLiveSegments() if (itPeriod != m_periods.end()) period = (*itPeriod).get(); - if (!period && updPeriod->GetId().empty() && updPeriod->GetStart() == 0) + if (!period && updPeriod->GetId().empty() && + (updPeriod->GetStart() == 0 || updPeriod->GetStart() == NO_VALUE)) { // not found, fallback match based on position if (index < m_periods.size()) diff --git a/src/parser/DASHTree.h b/src/parser/DASHTree.h index 5b0bf9118..10b989ec9 100644 --- a/src/parser/DASHTree.h +++ b/src/parser/DASHTree.h @@ -65,9 +65,7 @@ class ATTR_DLL_LOCAL CDashTree : public adaptive::AdaptiveTree uint32_t timescale = 1000); uint64_t ParseTagSegmentTimeline(pugi::xml_node nodeSegTL, PLAYLIST::CSpinCache& SCTimeline, - uint32_t timescale = 1000, - uint64_t totalTimeSecs = 0, - PLAYLIST::CSegmentTemplate* segTemplate = nullptr); + PLAYLIST::CSegmentTemplate& segTemplate); void ParseSegmentTemplate(pugi::xml_node node, PLAYLIST::CSegmentTemplate* segTpl); @@ -80,9 +78,9 @@ class ATTR_DLL_LOCAL CDashTree : public adaptive::AdaptiveTree uint32_t ParseAudioChannelConfig(pugi::xml_node node); /* - * \brief Estimate the count of segments on the period duration + * \brief Try estimate the count of segments on the MPD duration */ - size_t EstimateSegmentsCount(uint64_t duration, uint32_t timescale, uint64_t totalTimeSecs = 0); + size_t EstimateSegmentsCount(uint64_t duration, uint32_t timescale) const; void MergeAdpSets(); @@ -115,6 +113,9 @@ class ATTR_DLL_LOCAL CDashTree : public adaptive::AdaptiveTree // Period sequence incremented to every new period added uint32_t m_periodCurrentSeq{0}; + double m_timeShiftBufferDepth{0}; // MPD Timeshift buffer attribute value, in seconds + double m_mediaPresDuration{0}; // MPD Media presentation duration attribute value, in seconds (may be not provided) + uint64_t m_minimumUpdatePeriod{0}; // in seconds bool m_allowInsertLiveSegments{false}; // Determines if a custom PSSH initialization license data is provided