[vlc-commits] demux: adaptive: add BOLA/nearoptimal logic

Francois Cartegnie git at videolan.org
Wed Apr 19 11:00:04 CEST 2017


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Thu Apr 13 22:01:00 2017 +0200| [379f0fe5eadaa267b77671d10bac3d8f83b15694] | committer: Francois Cartegnie

demux: adaptive: add BOLA/nearoptimal logic

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=379f0fe5eadaa267b77671d10bac3d8f83b15694
---

 modules/demux/Makefile.am                          |   2 +
 modules/demux/adaptive/PlaylistManager.cpp         |  10 +
 modules/demux/adaptive/adaptive.cpp                |   3 +
 .../demux/adaptive/logic/AbstractAdaptationLogic.h |   3 +-
 .../adaptive/logic/NearOptimalAdaptationLogic.cpp  | 249 +++++++++++++++++++++
 .../adaptive/logic/NearOptimalAdaptationLogic.hpp  |  74 ++++++
 6 files changed, 340 insertions(+), 1 deletion(-)

diff --git a/modules/demux/Makefile.am b/modules/demux/Makefile.am
index 644fcbbccf..fb57a0e34b 100644
--- a/modules/demux/Makefile.am
+++ b/modules/demux/Makefile.am
@@ -314,6 +314,8 @@ libadaptive_plugin_la_SOURCES = \
     demux/adaptive/logic/AlwaysLowestAdaptationLogic.cpp \
     demux/adaptive/logic/AlwaysLowestAdaptationLogic.hpp \
     demux/adaptive/logic/IDownloadRateObserver.h \
+    demux/adaptive/logic/NearOptimalAdaptationLogic.cpp \
+    demux/adaptive/logic/NearOptimalAdaptationLogic.hpp \
     demux/adaptive/logic/PredictiveAdaptationLogic.hpp \
     demux/adaptive/logic/PredictiveAdaptationLogic.cpp \
     demux/adaptive/logic/RateBasedAdaptationLogic.h \
diff --git a/modules/demux/adaptive/PlaylistManager.cpp b/modules/demux/adaptive/PlaylistManager.cpp
index b3dad9937a..8f7797b05d 100644
--- a/modules/demux/adaptive/PlaylistManager.cpp
+++ b/modules/demux/adaptive/PlaylistManager.cpp
@@ -34,6 +34,7 @@
 #include "logic/RateBasedAdaptationLogic.h"
 #include "logic/AlwaysLowestAdaptationLogic.hpp"
 #include "logic/PredictiveAdaptationLogic.hpp"
+#include "logic/NearOptimalAdaptationLogic.hpp"
 #include "tools/Debug.hpp"
 #include <vlc_stream.h>
 #include <vlc_demux.h>
@@ -738,6 +739,15 @@ AbstractAdaptationLogic *PlaylistManager::createLogic(AbstractAdaptationLogic::L
             logic = ratelogic;
             break;
         }
+        case AbstractAdaptationLogic::NearOptimal:
+        {
+            NearOptimalAdaptationLogic *noplogic =
+                    new (std::nothrow) NearOptimalAdaptationLogic(VLC_OBJECT(p_demux));
+            if(noplogic)
+                conn->setDownloadRateObserver(noplogic);
+            logic = noplogic;
+            break;
+        }
         case AbstractAdaptationLogic::Default:
         case AbstractAdaptationLogic::Predictive:
         {
diff --git a/modules/demux/adaptive/adaptive.cpp b/modules/demux/adaptive/adaptive.cpp
index 1ba7d9744c..e22b749e3e 100644
--- a/modules/demux/adaptive/adaptive.cpp
+++ b/modules/demux/adaptive/adaptive.cpp
@@ -77,6 +77,7 @@ static void Close   (vlc_object_t *);
 static const AbstractAdaptationLogic::LogicType pi_logics[] = {
                                 AbstractAdaptationLogic::Default,
                                 AbstractAdaptationLogic::Predictive,
+                                AbstractAdaptationLogic::NearOptimal,
                                 AbstractAdaptationLogic::RateBased,
                                 AbstractAdaptationLogic::FixedRate,
                                 AbstractAdaptationLogic::AlwaysLowest,
@@ -85,6 +86,7 @@ static const AbstractAdaptationLogic::LogicType pi_logics[] = {
 static const char *const ppsz_logics_values[] = {
                                 "",
                                 "predictive",
+                                "nearoptimal",
                                 "rate",
                                 "fixedrate",
                                 "lowest",
@@ -92,6 +94,7 @@ static const char *const ppsz_logics_values[] = {
 
 static const char *const ppsz_logics[] = { N_("Default"),
                                            N_("Predictive"),
+                                           N_("Near Optimal"),
                                            N_("Bandwidth Adaptive"),
                                            N_("Fixed Bandwidth"),
                                            N_("Lowest Bandwidth/Quality"),
diff --git a/modules/demux/adaptive/logic/AbstractAdaptationLogic.h b/modules/demux/adaptive/logic/AbstractAdaptationLogic.h
index c7c5642d9e..9049dea688 100644
--- a/modules/demux/adaptive/logic/AbstractAdaptationLogic.h
+++ b/modules/demux/adaptive/logic/AbstractAdaptationLogic.h
@@ -59,7 +59,8 @@ namespace adaptive
                     AlwaysLowest,
                     RateBased,
                     FixedRate,
-                    Predictive
+                    Predictive,
+                    NearOptimal,
                 };
 
             protected:
diff --git a/modules/demux/adaptive/logic/NearOptimalAdaptationLogic.cpp b/modules/demux/adaptive/logic/NearOptimalAdaptationLogic.cpp
new file mode 100644
index 0000000000..5ee5de0b47
--- /dev/null
+++ b/modules/demux/adaptive/logic/NearOptimalAdaptationLogic.cpp
@@ -0,0 +1,249 @@
+/*
+ * NearOptimalAdaptationLogic.cpp
+ *****************************************************************************
+ * Copyright (C) 2017 - VideoLAN 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 "NearOptimalAdaptationLogic.hpp"
+#include "Representationselectors.hpp"
+
+#include "../playlist/BaseAdaptationSet.h"
+#include "../playlist/BaseRepresentation.h"
+#include "../playlist/BasePeriod.h"
+#include "../http/Chunk.h"
+#include "../tools/Debug.hpp"
+
+#include <cmath>
+
+using namespace adaptive::logic;
+using namespace adaptive;
+
+/*
+ * Multi stream version of BOLA: Near-Optimal Bitrate Adaptation for Online Videos
+ * http://arxiv.org/abs/1601.06748
+ */
+
+#define minimumBufferS (CLOCK_FREQ * 6)  /* Qmin */
+#define bufferTargetS  (CLOCK_FREQ * 30) /* Qmax */
+
+NearOptimalContext::NearOptimalContext()
+{
+    buffering_min = minimumBufferS;
+    buffering_level = 0;
+    buffering_target = bufferTargetS;
+    last_download_rate = 0;
+}
+
+NearOptimalAdaptationLogic::NearOptimalAdaptationLogic(vlc_object_t *p_obj_):
+    AbstractAdaptationLogic()
+{
+    p_obj = p_obj_;
+    usedBps = 0;
+    currentBps = 0;
+    vlc_mutex_init(&lock);
+}
+
+NearOptimalAdaptationLogic::~NearOptimalAdaptationLogic()
+{
+    vlc_mutex_destroy(&lock);
+}
+
+BaseRepresentation *
+NearOptimalAdaptationLogic::getNextQualityIndex( BaseAdaptationSet *adaptSet, RepresentationSelector &selector,
+                                                 float gammaP, mtime_t VD, mtime_t Q )
+{
+    BaseRepresentation *ret = NULL;
+    BaseRepresentation *prev = NULL;
+    float argmax;
+    for(BaseRepresentation *rep = selector.lowest(adaptSet);
+                            rep && rep != prev; rep = selector.higher(adaptSet, rep))
+    {
+        float arg = ( VD * (getUtility(rep) + gammaP) - Q ) / rep->getBandwidth();
+        if(ret == NULL || argmax <= arg)
+        {
+            ret = rep;
+            argmax = arg;
+        }
+        prev = rep;
+    }
+    return ret;
+}
+
+BaseRepresentation *NearOptimalAdaptationLogic::getNextRepresentation(BaseAdaptationSet *adaptSet, BaseRepresentation *prevRep)
+{
+    RepresentationSelector selector(maxwidth, maxheight);
+
+    const float umin = getUtility(selector.lowest(adaptSet));
+    const float umax = getUtility(selector.highest(adaptSet));
+
+    vlc_mutex_lock(&lock);
+
+    std::map<ID, NearOptimalContext>::iterator it = streams.find(adaptSet->getID());
+    if(it == streams.end())
+    {
+        vlc_mutex_unlock(&lock);
+        return selector.lowest(adaptSet);
+    }
+    NearOptimalContext ctxcopy = (*it).second;
+
+    const unsigned bps = getAvailableBw(currentBps, prevRep);
+
+    vlc_mutex_unlock(&lock);
+
+    const float gammaP = 1.0 + (umax - umin) / ((float)ctxcopy.buffering_target / ctxcopy.buffering_min - 1.0);
+    const float Vd = ((float)ctxcopy.buffering_min / CLOCK_FREQ - 1.0) / (umin + gammaP);
+
+    BaseRepresentation *m;
+    if(prevRep == NULL) /* Starting */
+    {
+        m = selector.select(adaptSet, bps);
+    }
+    else
+    {
+        /* noted m* */
+        m = getNextQualityIndex(adaptSet, selector, gammaP - umin /* umin == Sm, utility = std::log(S/Sm) */,
+                                Vd, (float)ctxcopy.buffering_level / CLOCK_FREQ);
+        if(m->getBandwidth() < prevRep->getBandwidth()) /* m*[n] < m*[n-1] */
+        {
+            BaseRepresentation *mp = selector.select(adaptSet, bps); /* m' */
+            if(mp->getBandwidth() <= m->getBandwidth())
+            {
+                mp = m;
+            }
+            else if(mp->getBandwidth() > prevRep->getBandwidth())
+            {
+                mp = prevRep;
+            }
+            else
+            {
+                mp = selector.lower(adaptSet, mp);
+            }
+            m = mp;
+        }
+    }
+
+    BwDebug( msg_Info(p_obj, "buffering level %.2f% rep %ld kBps %zu kBps",
+             (float) 100 * ctxcopy.buffering_level / ctxcopy.buffering_target, m->getBandwidth()/8000, bps / 8000); );
+
+    return m;
+}
+
+float NearOptimalAdaptationLogic::getUtility(const BaseRepresentation *rep)
+{
+    float ret;
+    std::map<uint64_t, float>::iterator it = utilities.find(rep->getBandwidth());
+    if(it == utilities.end())
+    {
+        ret = std::log((float)rep->getBandwidth());
+        utilities.insert(std::pair<uint64_t, float>(rep->getBandwidth(), ret));
+    }
+    else ret = (*it).second;
+    return ret;
+}
+
+unsigned NearOptimalAdaptationLogic::getAvailableBw(unsigned i_bw, const BaseRepresentation *curRep) const
+{
+    unsigned i_remain = i_bw;
+    if(i_remain > usedBps)
+        i_remain -= usedBps;
+    else
+        i_remain = 0;
+    if(curRep)
+        i_remain += curRep->getBandwidth();
+    return i_remain > i_bw ? i_remain : i_bw;
+}
+
+unsigned NearOptimalAdaptationLogic::getMaxCurrentBw() const
+{
+    unsigned i_max_bitrate = 0;
+    for(std::map<ID, NearOptimalContext>::const_iterator it = streams.begin();
+                                                         it != streams.end(); ++it)
+        i_max_bitrate = std::max(i_max_bitrate, ((*it).second).last_download_rate);
+    return i_max_bitrate;
+}
+
+void NearOptimalAdaptationLogic::updateDownloadRate(const ID &id, size_t dlsize, mtime_t time)
+{
+    vlc_mutex_lock(&lock);
+    std::map<ID, NearOptimalContext>::iterator it = streams.find(id);
+    if(it != streams.end())
+    {
+        NearOptimalContext &ctx = (*it).second;
+        ctx.last_download_rate = ctx.average.push(CLOCK_FREQ * dlsize * 8 / time);
+    }
+    currentBps = getMaxCurrentBw();
+    vlc_mutex_unlock(&lock);
+}
+
+void NearOptimalAdaptationLogic::trackerEvent(const SegmentTrackerEvent &event)
+{
+    switch(event.type)
+    {
+    case SegmentTrackerEvent::SWITCHING:
+        {
+            vlc_mutex_lock(&lock);
+            if(event.u.switching.prev)
+                usedBps -= event.u.switching.prev->getBandwidth();
+            if(event.u.switching.next)
+                usedBps += event.u.switching.next->getBandwidth();
+                 BwDebug(msg_Info(p_obj, "New total bandwidth usage %zu kBps", (usedBps / 8000)));
+            vlc_mutex_unlock(&lock);
+        }
+        break;
+
+    case SegmentTrackerEvent::BUFFERING_STATE:
+        {
+            const ID &id = *event.u.buffering.id;
+            vlc_mutex_lock(&lock);
+            if(event.u.buffering.enabled)
+            {
+                if(streams.find(id) == streams.end())
+                {
+                    NearOptimalContext ctx;
+                    streams.insert(std::pair<ID, NearOptimalContext>(id, ctx));
+                }
+            }
+            else
+            {
+                std::map<ID, NearOptimalContext>::iterator it = streams.find(id);
+                if(it != streams.end())
+                    streams.erase(it);
+            }
+            vlc_mutex_unlock(&lock);
+            BwDebug(msg_Info(p_obj, "Stream %s is now known %sactive", id.str().c_str(),
+                         (event.u.buffering.enabled) ? "" : "in"));
+        }
+        break;
+
+    case SegmentTrackerEvent::BUFFERING_LEVEL_CHANGE:
+        {
+            const ID &id = *event.u.buffering.id;
+            vlc_mutex_lock(&lock);
+            NearOptimalContext &ctx = streams[id];
+            ctx.buffering_level = event.u.buffering_level.current;
+            ctx.buffering_target = event.u.buffering_level.target;
+            vlc_mutex_unlock(&lock);
+        }
+        break;
+
+    default:
+            break;
+    }
+}
diff --git a/modules/demux/adaptive/logic/NearOptimalAdaptationLogic.hpp b/modules/demux/adaptive/logic/NearOptimalAdaptationLogic.hpp
new file mode 100644
index 0000000000..19f2109533
--- /dev/null
+++ b/modules/demux/adaptive/logic/NearOptimalAdaptationLogic.hpp
@@ -0,0 +1,74 @@
+/*
+ * NearOptimalAdaptationLogic.hpp
+ *****************************************************************************
+ * Copyright (C) 2017 - VideoLAN 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 NEAROPTIMALADAPTATIONLOGIC_HPP
+#define NEAROPTIMALADAPTATIONLOGIC_HPP
+
+#include "AbstractAdaptationLogic.h"
+#include "Representationselectors.hpp"
+#include "../tools/MovingAverage.hpp"
+#include <map>
+
+namespace adaptive
+{
+    namespace logic
+    {
+        class NearOptimalContext
+        {
+            friend class NearOptimalAdaptationLogic;
+
+            public:
+                NearOptimalContext();
+
+            private:
+                mtime_t buffering_min;
+                mtime_t buffering_level;
+                mtime_t buffering_target;
+                unsigned last_download_rate;
+                MovingAverage<unsigned> average;
+        };
+
+        class NearOptimalAdaptationLogic : public AbstractAdaptationLogic
+        {
+            public:
+                NearOptimalAdaptationLogic(vlc_object_t *);
+                virtual ~NearOptimalAdaptationLogic();
+
+                virtual BaseRepresentation* getNextRepresentation(BaseAdaptationSet *, BaseRepresentation *);
+                virtual void                updateDownloadRate     (const ID &, size_t, mtime_t); /* reimpl */
+                virtual void                trackerEvent           (const SegmentTrackerEvent &); /* reimpl */
+
+            private:
+                BaseRepresentation *        getNextQualityIndex( BaseAdaptationSet *, RepresentationSelector &,
+                                                                 float gammaP, mtime_t VD,
+                                                                 mtime_t Q /*current buffer level*/);
+                float                       getUtility(const BaseRepresentation *);
+                unsigned                    getAvailableBw(unsigned, const BaseRepresentation *) const;
+                unsigned                    getMaxCurrentBw() const;
+                std::map<adaptive::ID, NearOptimalContext> streams;
+                std::map<uint64_t, float>   utilities;
+                unsigned                    currentBps;
+                unsigned                    usedBps;
+                vlc_object_t *              p_obj;
+                vlc_mutex_t                 lock;
+        };
+    }
+}
+
+#endif // NEAROPTIMALADAPTATIONLOGIC_HPP



More information about the vlc-commits mailing list