[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