[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