vlc | branch: master | Francois Cartegnie <[email protected]> | 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 _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
