[vlc-commits] demux: adaptive: add new BufferingLogic
Francois Cartegnie
git at videolan.org
Tue Mar 24 23:35:17 CET 2020
vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Fri Mar 20 17:11:18 2020 +0100| [b1e7eb53d687bef3d32070842ee30199fbe9ee6f] | committer: Francois Cartegnie
demux: adaptive: add new BufferingLogic
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=b1e7eb53d687bef3d32070842ee30199fbe9ee6f
---
modules/demux/Makefile.am | 2 +
modules/demux/adaptive/logic/BufferingLogic.cpp | 381 ++++++++++++++++++++++++
modules/demux/adaptive/logic/BufferingLogic.hpp | 81 +++++
3 files changed, 464 insertions(+)
diff --git a/modules/demux/Makefile.am b/modules/demux/Makefile.am
index 3aa42a2683..954d2982e0 100644
--- a/modules/demux/Makefile.am
+++ b/modules/demux/Makefile.am
@@ -342,6 +342,8 @@ libadaptive_plugin_la_SOURCES = \
demux/adaptive/logic/AlwaysBestAdaptationLogic.h \
demux/adaptive/logic/AlwaysLowestAdaptationLogic.cpp \
demux/adaptive/logic/AlwaysLowestAdaptationLogic.hpp \
+ demux/adaptive/logic/BufferingLogic.cpp \
+ demux/adaptive/logic/BufferingLogic.hpp \
demux/adaptive/logic/IDownloadRateObserver.h \
demux/adaptive/logic/NearOptimalAdaptationLogic.cpp \
demux/adaptive/logic/NearOptimalAdaptationLogic.hpp \
diff --git a/modules/demux/adaptive/logic/BufferingLogic.cpp b/modules/demux/adaptive/logic/BufferingLogic.cpp
new file mode 100644
index 0000000000..6bece82156
--- /dev/null
+++ b/modules/demux/adaptive/logic/BufferingLogic.cpp
@@ -0,0 +1,381 @@
+/*
+ * BufferingLogic.cpp
+ *****************************************************************************
+ * Copyright (C) 2014 - 2020 VideoLabs, VideoLAN and VLC authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "BufferingLogic.hpp"
+#include "../playlist/BaseRepresentation.h"
+#include "../playlist/BasePeriod.h"
+#include "../playlist/AbstractPlaylist.hpp"
+#include "../playlist/SegmentTemplate.h"
+#include "../playlist/SegmentTimeline.h"
+#include "../playlist/SegmentList.h"
+#include "../playlist/SegmentBase.h"
+
+#include <limits>
+#include <cassert>
+
+using namespace adaptive;
+using namespace adaptive::playlist;
+using namespace adaptive::logic;
+
+const vlc_tick_t AbstractBufferingLogic::BUFFERING_LOWEST_LIMIT = VLC_TICK_FROM_SEC(2);
+const vlc_tick_t AbstractBufferingLogic::DEFAULT_MIN_BUFFERING = VLC_TICK_FROM_SEC(6);
+const vlc_tick_t AbstractBufferingLogic::DEFAULT_MAX_BUFFERING = VLC_TICK_FROM_SEC(30);
+const vlc_tick_t AbstractBufferingLogic::DEFAULT_LIVE_BUFFERING = VLC_TICK_FROM_SEC(15);
+
+AbstractBufferingLogic::AbstractBufferingLogic()
+{
+ userMinBuffering = 0;
+ userMaxBuffering = 0;
+ userLiveDelay = 0;
+}
+
+void AbstractBufferingLogic::setLowDelay(bool b)
+{
+ userLowLatency = b;
+}
+
+void AbstractBufferingLogic::setUserMinBuffering(vlc_tick_t v)
+{
+ userMinBuffering = v;
+}
+
+void AbstractBufferingLogic::setUserMaxBuffering(vlc_tick_t v)
+{
+ userMaxBuffering = v;
+}
+
+void AbstractBufferingLogic::setUserLiveDelay(vlc_tick_t v)
+{
+ userLiveDelay = v;
+}
+
+DefaultBufferingLogic::DefaultBufferingLogic()
+ : AbstractBufferingLogic()
+{
+
+}
+
+uint64_t DefaultBufferingLogic::getStartSegmentNumber(BaseRepresentation *rep) const
+{
+ if(rep->getPlaylist()->isLive())
+ return getLiveStartSegmentNumber(rep);
+
+ const MediaSegmentTemplate *segmentTemplate = rep->inheritSegmentTemplate();
+ if(segmentTemplate)
+ {
+ const SegmentTimeline *timeline = segmentTemplate->inheritSegmentTimeline();
+ if(timeline)
+ return timeline->minElementNumber();
+ return segmentTemplate->inheritStartNumber();
+ }
+
+ const SegmentList *list = rep->inheritSegmentList();
+ if(list)
+ return list->getStartIndex();
+
+ const SegmentBase *base = rep->inheritSegmentBase();
+ if(base)
+ return base->getSequenceNumber();
+
+ return 0;
+}
+
+vlc_tick_t DefaultBufferingLogic::getMinBuffering(const AbstractPlaylist *p) const
+{
+ if(isLowLatency(p))
+ return BUFFERING_LOWEST_LIMIT;
+ else if(userMinBuffering)
+ return std::max(BUFFERING_LOWEST_LIMIT, userMinBuffering);
+ else if(p->getMinBuffering())
+ return std::max(BUFFERING_LOWEST_LIMIT, p->getMinBuffering());
+ else
+ return DEFAULT_MIN_BUFFERING;
+}
+
+vlc_tick_t DefaultBufferingLogic::getMaxBuffering(const AbstractPlaylist *p) const
+{
+ if(isLowLatency(p))
+ return getMinBuffering(p);
+ else if(p->isLive())
+ {
+ if(userLiveDelay)
+ return std::max(getMinBuffering(p), userLiveDelay);
+ else
+ return std::max(getMinBuffering(p), DEFAULT_LIVE_BUFFERING);
+ }
+ else
+ {
+ if(userMaxBuffering)
+ return std::max(getMinBuffering(p), userMaxBuffering);
+ else if(p->getMaxBuffering())
+ return std::max(getMinBuffering(p), p->getMaxBuffering());
+ else
+ return std::max(getMinBuffering(p), DEFAULT_MAX_BUFFERING);
+ }
+}
+
+uint64_t DefaultBufferingLogic::getLiveStartSegmentNumber(BaseRepresentation *rep) const
+{
+ AbstractPlaylist *playlist = rep->getPlaylist();
+
+ /* Get buffering amount */
+ vlc_tick_t i_buffering = getBufferingAmount(playlist);
+
+ /* Try to never buffer up to really end */
+ /* Enforce no overlap for demuxers segments 3.0.0 */
+ /* FIXME: check duration instead ? */
+ const unsigned SAFETY_BUFFERING_EDGE_OFFSET = 1;
+ const unsigned SAFETY_EXPURGING_OFFSET = 2;
+
+ SegmentList *segmentList = rep->inheritSegmentList();
+ SegmentBase *segmentBase = rep->inheritSegmentBase();
+ MediaSegmentTemplate *mediaSegmentTemplate = rep->inheritSegmentTemplate();
+ if(mediaSegmentTemplate)
+ {
+ uint64_t start = 0;
+ const Timescale timescale = mediaSegmentTemplate->inheritTimescale();
+
+ const SegmentTimeline *timeline = mediaSegmentTemplate->inheritSegmentTimeline();
+ if(timeline)
+ {
+ uint64_t safeMinElementNumber = timeline->minElementNumber();
+ uint64_t safeMaxElementNumber = timeline->maxElementNumber();
+ stime_t safeedgetime, safestarttime, duration;
+ for(unsigned i=0; i<SAFETY_BUFFERING_EDGE_OFFSET; i++)
+ {
+ if(safeMinElementNumber == safeMaxElementNumber)
+ break;
+ safeMaxElementNumber--;
+ }
+ bool b_ret = timeline->getScaledPlaybackTimeDurationBySegmentNumber(safeMaxElementNumber,
+ &safeedgetime, &duration);
+ if(unlikely(!b_ret))
+ return 0;
+ safeedgetime += duration - 1;
+
+ for(unsigned i=0; i<SAFETY_EXPURGING_OFFSET; i++)
+ {
+ if(safeMinElementNumber + 1 >= safeMaxElementNumber)
+ break;
+ safeMinElementNumber++;
+ }
+ b_ret = timeline->getScaledPlaybackTimeDurationBySegmentNumber(safeMinElementNumber,
+ &safestarttime, &duration);
+ if(unlikely(!b_ret))
+ return 0;
+
+ if(playlist->timeShiftBufferDepth.Get())
+ {
+ stime_t edgetime;
+ bool b_ret = timeline->getScaledPlaybackTimeDurationBySegmentNumber(timeline->maxElementNumber(),
+ &edgetime, &duration);
+ if(unlikely(!b_ret))
+ return 0;
+ edgetime += duration - 1;
+ stime_t timeshiftdepth = timescale.ToScaled(playlist->timeShiftBufferDepth.Get());
+ if(safestarttime + timeshiftdepth < edgetime)
+ {
+ safestarttime = edgetime - timeshiftdepth;
+ safeMinElementNumber = timeline->getElementNumberByScaledPlaybackTime(safestarttime);
+ }
+ }
+ assert(safestarttime<=safeedgetime);
+
+ stime_t starttime;
+ if(safeedgetime - safestarttime > timescale.ToScaled(i_buffering))
+ starttime = safeedgetime - timescale.ToScaled(i_buffering);
+ else
+ starttime = safestarttime;
+
+ start = timeline->getElementNumberByScaledPlaybackTime(starttime);
+ assert(start >= timeline->minElementNumber());
+ assert(start >= safeMinElementNumber);
+ assert(start <= timeline->maxElementNumber());
+ assert(start <= safeMaxElementNumber);
+
+ return start;
+ }
+ /* Else compute, current time and timeshiftdepth based */
+ else if(mediaSegmentTemplate->duration.Get())
+ {
+ /* Compute playback offset and effective finished segment from wall time */
+ vlc_tick_t now = vlc_tick_from_sec(time(NULL));
+ vlc_tick_t playbacktime = now - i_buffering;
+ vlc_tick_t minavailtime = playlist->availabilityStartTime.Get() + rep->getPeriodStart();
+ const uint64_t startnumber = mediaSegmentTemplate->inheritStartNumber();
+ const Timescale timescale = mediaSegmentTemplate->inheritTimescale();
+ if(!timescale)
+ return startnumber;
+ const vlc_tick_t duration = timescale.ToTime(mediaSegmentTemplate->inheritDuration());
+ if(!duration)
+ return startnumber;
+
+ /* restrict to DVR window */
+ if(playlist->timeShiftBufferDepth.Get())
+ {
+ vlc_tick_t elapsed = now - minavailtime;
+ elapsed = elapsed - (elapsed % duration); /* align to last segment */
+ vlc_tick_t alignednow = minavailtime + elapsed;
+ if(mediaSegmentTemplate->duration.Get() < elapsed)
+ minavailtime = alignednow - mediaSegmentTemplate->duration.Get();
+
+ if(playbacktime < minavailtime)
+ playbacktime = minavailtime;
+ }
+ /* Get completed segment containing the time ref */
+ start = mediaSegmentTemplate->getLiveTemplateNumber(playbacktime);
+ if (unlikely(start < startnumber))
+ {
+ assert(startnumber > start); /* blame getLiveTemplateNumber() */
+ start = startnumber;
+ }
+
+ const uint64_t max_safety_offset = playbacktime - minavailtime / duration;
+ const uint64_t safety_offset = std::min((uint64_t)SAFETY_BUFFERING_EDGE_OFFSET,
+ max_safety_offset);
+ if(startnumber + safety_offset <= start)
+ start -= safety_offset;
+ else
+ start = startnumber;
+
+ return start;
+ }
+ }
+ else if (segmentList && !segmentList->getSegments().empty())
+ {
+ const Timescale timescale = segmentList->inheritTimescale();
+ const std::vector<ISegment *> list = segmentList->getSegments();
+ const ISegment *back = list.back();
+
+ /* working around HLS discontinuities by using durations */
+ stime_t totallistduration = 0;
+ for(auto it = list.begin(); it != list.end(); ++it)
+ totallistduration += (*it)->duration.Get();
+
+ /* Apply timeshift restrictions */
+ stime_t availableduration;
+ if(playlist->timeShiftBufferDepth.Get())
+ {
+ availableduration = std::min(totallistduration,
+ timescale.ToScaled(playlist->timeShiftBufferDepth.Get()));
+ }
+ else availableduration = totallistduration;
+
+ uint64_t availableliststartnumber = list.front()->getSequenceNumber();
+ if(totallistduration != availableduration)
+ {
+ stime_t offset = totallistduration - availableduration;
+ for(auto it = list.begin(); it != list.end(); ++it)
+ {
+ availableliststartnumber = (*it)->getSequenceNumber();
+ if(offset < (*it)->duration.Get())
+ break;
+ offset -= (*it)->duration.Get();
+ }
+ }
+
+ uint64_t safeedgenumber = back->getSequenceNumber() -
+ std::min((uint64_t)list.size() - 1,
+ (uint64_t)SAFETY_BUFFERING_EDGE_OFFSET);
+
+ uint64_t safestartnumber = availableliststartnumber;
+ if(safeedgenumber > safestartnumber)
+ safestartnumber -= std::min(safeedgenumber-safestartnumber - 1,
+ (uint64_t)SAFETY_EXPURGING_OFFSET);
+
+ stime_t maxbufferizable = 0;
+ stime_t safeedgeduration = 0;
+ for(auto it = list.begin(); it != list.end(); ++it)
+ {
+ if((*it)->getSequenceNumber() < safestartnumber)
+ continue;
+ if((*it)->getSequenceNumber() <= safeedgenumber)
+ maxbufferizable += (*it)->duration.Get();
+ else
+ safeedgeduration += (*it)->duration.Get();
+ }
+
+ stime_t tobuffer = std::min(maxbufferizable, timescale.ToScaled(i_buffering));
+ stime_t skipduration = totallistduration - safeedgeduration - tobuffer ;
+ uint64_t start = safestartnumber;
+ for(auto it = list.begin(); it != list.end(); ++it)
+ {
+ start = (*it)->getSequenceNumber();
+ if((*it)->duration.Get() < skipduration)
+ break;
+ skipduration -= (*it)->duration.Get();
+ }
+
+ return start;
+ }
+ else if(segmentBase)
+ {
+ const std::vector<ISegment *> list = segmentBase->subSegments();
+ if(!list.empty())
+ return segmentBase->getSequenceNumber();
+
+ const Timescale timescale = rep->inheritTimescale();
+ const ISegment *back = list.back();
+ const stime_t bufferingstart = back->startTime.Get() + back->duration.Get() -
+ timescale.ToScaled(i_buffering);
+
+ uint64_t start;
+ if(!SegmentInfoCommon::getSegmentNumberByScaledTime(list, bufferingstart, &start))
+ return list.front()->getSequenceNumber();
+
+ if(segmentBase->getSequenceNumber() + SAFETY_BUFFERING_EDGE_OFFSET <= start)
+ start -= SAFETY_BUFFERING_EDGE_OFFSET;
+ else
+ start = segmentBase->getSequenceNumber();
+
+ return start;
+ }
+
+ return std::numeric_limits<uint64_t>::max();
+}
+
+vlc_tick_t DefaultBufferingLogic::getBufferingAmount(const AbstractPlaylist *p) const
+{
+ const vlc_tick_t i_min_buffering = getMinBuffering(p);
+ const vlc_tick_t i_max_buffering = getMaxBuffering(p);
+
+ /* Get buffering amount */
+ vlc_tick_t i_buffering = i_max_buffering;
+ if(p->suggestedPresentationDelay.Get() &&
+ p->suggestedPresentationDelay.Get() > i_min_buffering)
+ {
+ i_buffering = p->suggestedPresentationDelay.Get();
+ }
+
+ if(p->timeShiftBufferDepth.Get())
+ i_buffering = std::min(i_buffering, p->timeShiftBufferDepth.Get());
+
+ return i_buffering;
+}
+
+bool DefaultBufferingLogic::isLowLatency(const AbstractPlaylist *p) const
+{
+ if(userLowLatency.isSet())
+ return userLowLatency.value();
+ return p->isLowLatency();
+}
diff --git a/modules/demux/adaptive/logic/BufferingLogic.hpp b/modules/demux/adaptive/logic/BufferingLogic.hpp
new file mode 100644
index 0000000000..7c147ccf93
--- /dev/null
+++ b/modules/demux/adaptive/logic/BufferingLogic.hpp
@@ -0,0 +1,81 @@
+/*
+ * BufferingLogic.hpp
+ *****************************************************************************
+ * Copyright (C) 2014 - 2020 VideoLabs, VideoLAN and VLC authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef BUFFERINGLOGIC_HPP
+#define BUFFERINGLOGIC_HPP
+
+#include <vector>
+#include <vlc_common.h>
+#include "../tools/Properties.hpp"
+
+namespace adaptive
+{
+ namespace playlist
+ {
+ class BaseRepresentation;
+ class AbstractPlaylist;
+ }
+
+ namespace logic
+ {
+ using namespace playlist;
+
+ class AbstractBufferingLogic
+ {
+ public:
+ AbstractBufferingLogic();
+ virtual ~AbstractBufferingLogic() {}
+
+ virtual uint64_t getStartSegmentNumber(BaseRepresentation *) const = 0;
+ virtual vlc_tick_t getMinBuffering(const AbstractPlaylist *) const = 0;
+ virtual vlc_tick_t getMaxBuffering(const AbstractPlaylist *) const = 0;
+ void setUserMinBuffering(vlc_tick_t);
+ void setUserMaxBuffering(vlc_tick_t);
+ void setUserLiveDelay(vlc_tick_t);
+ void setLowDelay(bool);
+
+ protected:
+ static const vlc_tick_t BUFFERING_LOWEST_LIMIT;
+ static const vlc_tick_t DEFAULT_MIN_BUFFERING;
+ static const vlc_tick_t DEFAULT_MAX_BUFFERING;
+ static const vlc_tick_t DEFAULT_LIVE_BUFFERING;
+ vlc_tick_t userMinBuffering;
+ vlc_tick_t userMaxBuffering;
+ vlc_tick_t userLiveDelay;
+ Undef<bool> userLowLatency;
+ };
+
+ class DefaultBufferingLogic : public AbstractBufferingLogic
+ {
+ public:
+ DefaultBufferingLogic();
+ virtual ~DefaultBufferingLogic() {}
+ virtual uint64_t getStartSegmentNumber(BaseRepresentation *) const; /* impl */
+ virtual vlc_tick_t getMinBuffering(const AbstractPlaylist *) const; /* impl */
+ virtual vlc_tick_t getMaxBuffering(const AbstractPlaylist *) const; /* impl */
+
+ protected:
+ vlc_tick_t getBufferingAmount(const AbstractPlaylist *) const;
+ uint64_t getLiveStartSegmentNumber(BaseRepresentation *) const;
+ bool isLowLatency(const AbstractPlaylist *) const;
+ };
+ }
+}
+
+#endif
More information about the vlc-commits
mailing list