diff options
Diffstat (limited to 'Adaptation')
-rw-r--r-- | Adaptation/AbstractAdaptationLogic.cpp | 54 | ||||
-rw-r--r-- | Adaptation/AbstractAdaptationLogic.h | 58 | ||||
-rw-r--r-- | Adaptation/AdaptationLogicFactory.cpp | 48 | ||||
-rw-r--r-- | Adaptation/AdaptationLogicFactory.h | 40 | ||||
-rw-r--r-- | Adaptation/AlwaysLowestLogic.cpp | 71 | ||||
-rw-r--r-- | Adaptation/AlwaysLowestLogic.h | 48 | ||||
-rw-r--r-- | Adaptation/Bola.cpp | 506 | ||||
-rw-r--r-- | Adaptation/Bola.h | 118 | ||||
-rw-r--r-- | Adaptation/BufferBasedAdaptation.cpp | 141 | ||||
-rw-r--r-- | Adaptation/BufferBasedAdaptation.h | 64 | ||||
-rw-r--r-- | Adaptation/BufferBasedAdaptationWithRateBased.cpp | 206 | ||||
-rw-r--r-- | Adaptation/BufferBasedAdaptationWithRateBased.h | 72 | ||||
-rw-r--r-- | Adaptation/BufferBasedThreeThresholdAdaptation.cpp | 166 | ||||
-rw-r--r-- | Adaptation/BufferBasedThreeThresholdAdaptation.h | 69 | ||||
-rw-r--r-- | Adaptation/IAdaptationLogic.h | 115 | ||||
-rw-r--r-- | Adaptation/Panda.cpp | 274 | ||||
-rw-r--r-- | Adaptation/Panda.h | 89 | ||||
-rw-r--r-- | Adaptation/RateBasedAdaptation.cpp | 140 | ||||
-rw-r--r-- | Adaptation/RateBasedAdaptation.h | 61 |
19 files changed, 2340 insertions, 0 deletions
diff --git a/Adaptation/AbstractAdaptationLogic.cpp b/Adaptation/AbstractAdaptationLogic.cpp new file mode 100644 index 00000000..4f578958 --- /dev/null +++ b/Adaptation/AbstractAdaptationLogic.cpp @@ -0,0 +1,54 @@ +/* + * AbstractAdaptationLogic.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "AbstractAdaptationLogic.h" + +using namespace libdash::framework::adaptation; +using namespace dash::mpd; + +AbstractAdaptationLogic::AbstractAdaptationLogic(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid) : + mpd (mpd), + period (period), + adaptationSet (adaptationSet), + representation (NULL), + isVideo (isVid) +{ +} + +AbstractAdaptationLogic::~AbstractAdaptationLogic() +{ +} + +uint32_t AbstractAdaptationLogic::getPosition() +{ + return 0; +} + +void AbstractAdaptationLogic::setPosition(uint32_t segmentNumber) +{ + this->segmentNumber = segmentNumber; +} + +IRepresentation* AbstractAdaptationLogic::getRepresentation() +{ + return this->representation; +} + +void AbstractAdaptationLogic::setRepresentation(IPeriod *period, IAdaptationSet *adaptationSet, IRepresentation *representation) +{ + this->period = period; + this->adaptationSet = adaptationSet; + this->representation = representation; +} + +void AbstractAdaptationLogic::dLTimeUpdate(double time) +{ +} diff --git a/Adaptation/AbstractAdaptationLogic.h b/Adaptation/AbstractAdaptationLogic.h new file mode 100644 index 00000000..24fab91d --- /dev/null +++ b/Adaptation/AbstractAdaptationLogic.h @@ -0,0 +1,58 @@ +/* + * AbstractAdaptationLogic.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_ABSTRACTADAPTATIONLOGIC_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_ABSTRACTADAPTATIONLOGIC_H_ + +#include "IAdaptationLogic.h" +#include "IMPD.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class AbstractAdaptationLogic : public IAdaptationLogic +{ +public: + AbstractAdaptationLogic(dash::mpd::IMPD *mpd, dash::mpd::IPeriod* period, dash::mpd::IAdaptationSet *adaptationSet, bool isVideo); + virtual ~AbstractAdaptationLogic(); + + virtual uint32_t getPosition(); + virtual void setPosition(uint32_t segmentNumber); + virtual dash::mpd::IRepresentation* getRepresentation (); + virtual void setRepresentation(dash::mpd::IPeriod *period, + dash::mpd::IAdaptationSet *adaptationSet, + dash::mpd::IRepresentation *representation); + + virtual LogicType getType() = 0; + virtual bool isUserDependent() = 0; + virtual bool isRateBased() = 0; + virtual bool isBufferBased() = 0; + virtual void bitrateUpdate(uint64_t, uint32_t) = 0; + virtual void bufferUpdate(uint32_t, int) = 0; + virtual void onEOS(bool value)= 0; + virtual void dLTimeUpdate(double) = 0; + + virtual void checkedByDASHReceiver() = 0; +protected: + dash::mpd::IMPD *mpd; + dash::mpd::IPeriod *period; + dash::mpd::IAdaptationSet *adaptationSet; + dash::mpd::IRepresentation *representation; + uint32_t segmentNumber; + bool isVideo; +}; +} +} +} +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_ABSTRACTADAPTATIONLOGIC_H_ */ diff --git a/Adaptation/AdaptationLogicFactory.cpp b/Adaptation/AdaptationLogicFactory.cpp new file mode 100644 index 00000000..6901a597 --- /dev/null +++ b/Adaptation/AdaptationLogicFactory.cpp @@ -0,0 +1,48 @@ +/* + * AdaptationLogicFactory.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "AdaptationLogicFactory.h" +#include<stdio.h> + +using namespace libdash::framework::adaptation; +using namespace dash::mpd; + +IAdaptationLogic* AdaptationLogicFactory::create(LogicType logic, IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet,bool isVid, struct AdaptationParameters* paramsForAdaptation) +{ + Debug("Adaptation Logic for %s: ", isVid ? "video" : "audio"); + switch(logic) + { + case AlwaysLowest: + Debug("Always lowest\n"); + return new AlwaysLowestLogic(mpd, period, adaptationSet, isVid, paramsForAdaptation); + case RateBased: + Debug("Rate based\n"); + return new RateBasedAdaptation(mpd,period,adaptationSet, isVid, paramsForAdaptation); + case BufferBased: + Debug("Buffer based\n"); + return new BufferBasedAdaptation(mpd,period,adaptationSet, isVid, paramsForAdaptation); + case BufferRateBased: + Debug("Buffer Rate based\n"); + return new BufferBasedAdaptationWithRateBased(mpd,period,adaptationSet, isVid, paramsForAdaptation); + case BufferBasedThreeThreshold: + Debug("Buffer based 3 threshold\n"); + return new BufferBasedThreeThresholdAdaptation(mpd,period,adaptationSet, isVid, paramsForAdaptation); + case Panda: + Debug("Panda\n"); + return new PandaAdaptation(mpd, period, adaptationSet, isVid, paramsForAdaptation); + case Bola: + Debug("Bola\n"); + return new BolaAdaptation(mpd, period, adaptationSet, isVid, paramsForAdaptation); + default: + Debug("default => return Always Lowest\n"); + return new AlwaysLowestLogic(mpd, period, adaptationSet, isVid, paramsForAdaptation); + } +} diff --git a/Adaptation/AdaptationLogicFactory.h b/Adaptation/AdaptationLogicFactory.h new file mode 100644 index 00000000..0f1616a7 --- /dev/null +++ b/Adaptation/AdaptationLogicFactory.h @@ -0,0 +1,40 @@ +/* + * AdaptationLogicFactory.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_ADAPTATIONLOGICFACTORY_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_ADAPTATIONLOGICFACTORY_H_ + +#include "IAdaptationLogic.h" +#include "AlwaysLowestLogic.h" +#include "RateBasedAdaptation.h" +#include "BufferBasedAdaptation.h" +#include "BufferBasedAdaptationWithRateBased.h" +#include "BufferBasedThreeThresholdAdaptation.h" +#include "Panda.h" +#include "Bola.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class AdaptationLogicFactory +{ +public: + static IAdaptationLogic* create(libdash::framework::adaptation::LogicType logic, + dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params); +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_ADAPTATIONLOGICFACTORY_H_ */ diff --git a/Adaptation/AlwaysLowestLogic.cpp b/Adaptation/AlwaysLowestLogic.cpp new file mode 100644 index 00000000..54409bfd --- /dev/null +++ b/Adaptation/AlwaysLowestLogic.cpp @@ -0,0 +1,71 @@ +/* + * AlwaysLowestLogic.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "AlwaysLowestLogic.h" +#include<stdio.h> + +using namespace libdash::framework::adaptation; +using namespace libdash::framework::input; +using namespace dash::mpd; + +AlwaysLowestLogic::AlwaysLowestLogic(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params) : + AbstractAdaptationLogic(mpd, period, adaptationSet, isVid) +{ + this->representation = this->adaptationSet->GetRepresentation().at(0); +} + +AlwaysLowestLogic::~AlwaysLowestLogic() +{ +} + +LogicType AlwaysLowestLogic::getType() +{ + return adaptation::AlwaysLowest; +} + +bool AlwaysLowestLogic::isUserDependent() +{ + return false; +} + +bool AlwaysLowestLogic::isRateBased() +{ + return false; +} + +bool AlwaysLowestLogic::isBufferBased() +{ + return false; +} + +void AlwaysLowestLogic::bitrateUpdate(uint64_t bps, uint32_t segNum) +{ +} + +void AlwaysLowestLogic::bufferUpdate(uint32_t bufferfill, int maxC) +{ +} + +void AlwaysLowestLogic::setMultimediaManager(viper::managers::IMultimediaManagerBase *mmM) +{ +} + +void AlwaysLowestLogic::onEOS(bool value) +{ +} + +void AlwaysLowestLogic::dLTimeUpdate(double time) +{ +} + +void AlwaysLowestLogic::checkedByDASHReceiver() +{ +} diff --git a/Adaptation/AlwaysLowestLogic.h b/Adaptation/AlwaysLowestLogic.h new file mode 100644 index 00000000..782a8b29 --- /dev/null +++ b/Adaptation/AlwaysLowestLogic.h @@ -0,0 +1,48 @@ +/* + * AlwaysLowestLogic.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_ALWAYSLOWESTLOGIC_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_ALWAYSLOWESTLOGIC_H_ + +#include "IMPD.h" +#include "AbstractAdaptationLogic.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class AlwaysLowestLogic : public AbstractAdaptationLogic +{ +public: + AlwaysLowestLogic(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params); + virtual ~AlwaysLowestLogic(); + + virtual LogicType getType(); + virtual bool isUserDependent(); + virtual bool isRateBased(); + virtual bool isBufferBased(); + virtual void bitrateUpdate(uint64_t, uint32_t); + virtual void dLTimeUpdate(double time); + virtual void bufferUpdate(uint32_t, int); + virtual void setMultimediaManager(viper::managers::IMultimediaManagerBase *mmM); + virtual void onEOS(bool value); + virtual void checkedByDASHReceiver(); +private: + + +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_ALWAYSLOWESTLOGIC_H_ */ diff --git a/Adaptation/Bola.cpp b/Adaptation/Bola.cpp new file mode 100644 index 00000000..6477e0f5 --- /dev/null +++ b/Adaptation/Bola.cpp @@ -0,0 +1,506 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Bola.h" +#include <stdio.h> +#include <math.h> +#include <chrono> +#include <string> +#include <stdint.h> +#include <iostream> +#include <sstream> +#include <chrono> +#include <inttypes.h> +#include <stdlib.h> +#include <stdarg.h> +#include <algorithm> +#include <inttypes.h> +#include <time.h> +#include <limits.h> +#include <errno.h> + +const double MINIMUM_BUFFER_LEVEL_SPACING = 5.0; // The minimum space required between buffer levels (in seconds). +const uint32_t THROUGHPUT_SAMPLES = 3; // Number of samples considered for throughput estimate. +const double SAFETY_FACTOR = 0.9; // Safety factor used with bandwidth estimate. + +using namespace dash::mpd; +using namespace libdash::framework::adaptation; +using namespace libdash::framework::input; +using namespace libdash::framework::mpd; + +using std::bind; +using std::placeholders::_1; +using std::placeholders::_2; + + +using duration_in_seconds = std::chrono::duration<double, std::ratio<1, 1> >; + +BolaAdaptation::BolaAdaptation(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params) : + AbstractAdaptationLogic (mpd, period, adaptationSet, isVid) +{ + this->bufferMaxSizeSeconds =(double) params->segmentBufferSize * params->segmentDuration; + this->alphaRate = params->Bola_Alpha; + this->bufferTargetSeconds = params->Bola_bufferTargetSeconds; + + // Set Bola init STATE + this->initState = true; + this->bolaState = STARTUP; + + this->lastDownloadTimeInstant = 0.0; + this->currentDownloadTimeInstant = 0.0; + //this->lastSegmentDownloadTime = 0.0; + this->currentQuality = 0; + + this->bufferTargetPerc = (uint32_t) ( round(this->bufferTargetSeconds / this->bufferMaxSizeSeconds)*100 ); + + /// Retrieve available bitrates + std::vector<IRepresentation* > representations = this->adaptationSet->GetRepresentation(); + + this->availableBitrates.clear(); + Debug("BOLA Available Bitrates...\n"); + for(size_t i = 0; i < representations.size(); i++) + { + this->availableBitrates.push_back((uint64_t)(representations.at(i)->GetBandwidth())); + Debug("%d - %I64u bps\n", i+1, this->availableBitrates[i]); + } + // Check if they are in increasing order (i.e., bitrate[0] <= bitrate[1], etc.) + + this->bitrateCount = this->availableBitrates.size(); + + // We check if we have only one birate value or if the bitrate list is irregular (i.e., it is not strictly increasing) + if (this->bitrateCount < 2 || this->availableBitrates[0] >= this->availableBitrates[1] || this->availableBitrates[this->bitrateCount - 2] >= this->availableBitrates[this->bitrateCount - 1]) { + this->bolaState = ONE_BITRATE; + // return 0; // Check if exit with a message is necessary + } + + // Check if the following is correct + this->totalDuration = TimeResolver::getDurationInSec(this->mpd->GetMediaPresentationDuration()); +// this->segmentDuration = (double) (representations.at(0)->GetSegmentTemplate()->GetDuration() / representations.at(0)->GetSegmentTemplate()->GetTimescale() ); + this->segmentDuration = 2.0; + Debug("Total Duration - BOLA:\t%f\nSegment Duration - BOLA:\t%f\n",this->totalDuration, this->segmentDuration); + // if not correct --> segmentDuration = 2.0; + + // Compute the BOLA Buffer Target + this->bolaBufferTargetSeconds = this->bufferTargetSeconds; + if (this->bolaBufferTargetSeconds < this->segmentDuration + MINIMUM_BUFFER_LEVEL_SPACING) + { + this->bolaBufferTargetSeconds = this->segmentDuration + MINIMUM_BUFFER_LEVEL_SPACING; + } + Debug("BOLA Buffer Target Seconds:\t%f\n",this->bolaBufferTargetSeconds); + + // Compute UTILTY vector, Vp, and gp + Debug("BOLA Utility Values...\n"); + for (uint32_t i = 0; i < this->bitrateCount; ++i) { + this->utilityVector.push_back( log(((double)this->availableBitrates[i] * (1./(double)this->availableBitrates[0])))); + Debug("%d - %f\n", i+1, this->utilityVector[i]); + } + + this->Vp = (this->bolaBufferTargetSeconds - this->segmentDuration) / this->utilityVector[this->bitrateCount - 1]; + this->gp = 1.0 + this->utilityVector[this->bitrateCount - 1] / (this->bolaBufferTargetSeconds / this->segmentDuration - 1.0); + + Debug("BOLA Parameters:\tVp: %f\tgp: %f\n",this->Vp, this->gp); + /* If bufferTargetSeconds (not bolaBufferTargetSecond) is large enough, we might guarantee that Bola will never rebuffer + * unless the network bandwidth drops below the lowest encoded bitrate level. For this to work, Bola needs to use the real buffer + * level without the additional virtualBuffer. Also, for this to work efficiently, we need to make sure that if the buffer level + * drops to one fragment during a download, the current download does not have more bits remaining than the size of one fragment + * at the lowest quality*/ + + this->maxRtt = 0.2; // Is this reasonable? + if(this->bolaBufferTargetSeconds == this->bufferTargetSeconds) { + this->safetyGuarantee = true; + } + if (this->safetyGuarantee) { + Debug("BOLA SafetyGuarantee...\n"); + // we might need to adjust Vp and gp + double VpNew = this->Vp; + double gpNew = this->gp; + for (uint32_t i = 1; i < this->bitrateCount; ++i) { + double threshold = VpNew * (gpNew - this->availableBitrates[0] * this->utilityVector[i] / (this->availableBitrates[i] - this->availableBitrates[0])); + double minThreshold = this->segmentDuration * (2.0 - this->availableBitrates[0] / this->availableBitrates[i]) + maxRtt; + if (minThreshold >= this->bufferTargetSeconds) { + safetyGuarantee = false; + break; + } + if (threshold < minThreshold) { + VpNew = VpNew * (this->bufferTargetSeconds - minThreshold) / (this->bufferTargetSeconds - threshold); + gpNew = minThreshold / VpNew + this->utilityVector[i] * this->availableBitrates[0] / (this->availableBitrates[i] - this->availableBitrates[0]); + } + } + if (safetyGuarantee && (this->bufferTargetSeconds - this->segmentDuration) * VpNew / this->Vp < MINIMUM_BUFFER_LEVEL_SPACING) { + safetyGuarantee = false; + } + if (safetyGuarantee) { + this->Vp = VpNew; + this->gp = gpNew; + } + } + + Debug("BOLA New Parameters:\tVp: %f\tgp: %f\n",this->Vp, this->gp); + + // Capping of the virtual buffer (when using it) + this->bolaBufferMaxSeconds = this->Vp * (this->utilityVector[this->bitrateCount - 1] + this->gp); + Debug("BOLA Max Buffer Seconds:\t%f\n",this->bolaBufferMaxSeconds); + + this->virtualBuffer = 0.0; // Check if we should use either the virtualBuffer or the safetyGuarantee + + this->instantBw = 0; + this->averageBw = 0; + this->batchBw = 0; // Computed every THROUGHPUT_SAMPLES samples (average) + this->batchBwCount = 0; + + this->multimediaManager = NULL; + this->lastBufferFill = 0; // (?) + this->bufferEOS = false; + this->shouldAbort = false; + this->isCheckedForReceiver = false; + + this->representation = representations.at(0); + this->currentBitrate = (uint64_t) this->representation->GetBandwidth(); + + Debug("BOLA Init Params - \tAlpha: %f \t BufferTarget: %f\n",this->alphaRate, this->bufferTargetSeconds); + Debug("BOLA Init Current BitRate - %I64u\n",this->currentBitrate); + Debug("Buffer Adaptation BOLA: STARTED\n"); +} + +BolaAdaptation::~BolaAdaptation() +{ +} + +LogicType BolaAdaptation::getType() +{ + return adaptation::BufferBased; +} + +bool BolaAdaptation::isUserDependent() +{ + return false; +} + +bool BolaAdaptation::isRateBased() +{ + return true; +} +bool BolaAdaptation::isBufferBased() +{ + return true; +} + +void BolaAdaptation::setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager) +{ + this->multimediaManager = _mmManager; +} + +void BolaAdaptation::notifyBitrateChange() +{ + if(this->multimediaManager) + if(this->multimediaManager->isStarted() && !this->multimediaManager->isStopping()) + if(this->isVideo) + this->multimediaManager->setVideoQuality(this->period, this->adaptationSet, this->representation); + else + this->multimediaManager->setAudioQuality(this->period, this->adaptationSet, this->representation); + //Should Abort is done here to avoid race condition with DASHReceiver::DoBuffering() + if(this->shouldAbort) + { + this->multimediaManager->shouldAbort(this->isVideo); + } + this->shouldAbort = false; +} + +uint64_t BolaAdaptation::getBitrate() +{ + return this->currentBitrate; +} + +int BolaAdaptation::getQualityFromThroughput(uint64_t bps) { + int q = 0; + for (int i = 0; i < this->availableBitrates.size(); ++i) { + if (this->availableBitrates[i] > bps) { + break; + } + q = i; + } + return q; +} + +int BolaAdaptation::getQualityFromBufferLevel(double bufferLevelSec) { + int quality = this->bitrateCount - 1; + double score = 0.0; + for (int i = 0; i < this->bitrateCount; ++i) { + double s = (this->utilityVector[i] + this->gp - bufferLevelSec / this->Vp) / this->availableBitrates[i]; + if (s > score) { + score = s; + quality = i; + } + } + return quality; +} + +void BolaAdaptation::setBitrate(uint32_t bufferFill) +{ + // *** NB *** Insert Log messages + + if(this->initState) + { + this->initState = false; + + if(this->bolaState != ONE_BITRATE) + { + if(this->batchBw != 0) // Check the current estimated throughput (batch mean) + this->currentQuality = getQualityFromThroughput(this->batchBw*SAFETY_FACTOR); + //else --> quality unchanged + } + //this->representation = this->availableBitrates[this->currentQuality]; + //this->currentBitrate = (uint64_t) this->representation->GetBandwidth(); + this->representation = this->adaptationSet->GetRepresentation().at(this->currentQuality); + this->currentBitrate = (uint64_t) this->availableBitrates[this->currentQuality]; + Debug("INIT - Current Bitrate:\t%I64u\n", this->currentBitrate); + Debug("ADAPTATION_LOGIC:\tFor %s:\tlast_buffer: %f\tbuffer_level: %f, instantaneousBw: %lu, AverageBW: %lu, choice: %d\n",isVideo ? "video" : "audio",(double)lastBufferFill/100 , (double)bufferFill/100, this->instantBw, this->averageBw , this->currentQuality); + this->lastBufferFill = bufferFill; + return; + } + + if(this->bolaState == ONE_BITRATE) { + this->currentQuality = 0; + //this->representation = this->availableBitrates[this->currentQuality]; + //this->currentBitrate = (uint64_t) this->representation->GetBandwidth(); + this->representation = this->adaptationSet->GetRepresentation().at(this->currentQuality); + this->currentBitrate = (uint64_t) this->availableBitrates[this->currentQuality]; + Debug("ONE BITRATE - Current Bitrate:\t%I64u\n", this->currentBitrate); + Debug("ADAPTATION_LOGIC:\tFor %s:\tlast_buffer: %f\tbuffer_level: %f, instantaneousBw: %lu, AverageBW: %lu, choice: %d\n",isVideo ? "video" : "audio",(double)lastBufferFill/100 , (double)bufferFill/100, this->instantBw, this->averageBw , this->currentQuality); + this->lastBufferFill = bufferFill; + return; + } + + // Obtain bufferFill in seconds; + double bufferLevelSeconds = (double)( (bufferFill * this->bufferMaxSizeSeconds) *1./100); + int bolaQuality = getQualityFromBufferLevel(bufferLevelSeconds); + + Debug("REGULAR - Buffer Level Seconds:\t%f; Bola Quality:\t%d\n", bufferLevelSeconds, bolaQuality); + + + if (bufferLevelSeconds <= 0.1) { + // rebuffering occurred, reset virtual buffer + this->virtualBuffer = 0.0; + } + + // We check if the safetyGuarantee should be used. if not, we use the virtual buffer + // STILL NOT COMPLETE; Find a way to retrieved time since the last download + if (!this->safetyGuarantee) // we can use virtualBuffer + { + // find out if there was delay because of lack of availability or because bolaBufferTarget > bufferTarget + // TODO + //double timeSinceLastDownload = getDelayFromLastFragmentInSeconds(); // Define function + double timeSinceLastDownload = this->currentDownloadTimeInstant - this->lastDownloadTimeInstant; + + Debug("VirtualBuffer - Time Since Last Download:\t%f\n", timeSinceLastDownload); + + if (timeSinceLastDownload > 0.0) { + this->virtualBuffer += timeSinceLastDownload; + } + if ( (bufferLevelSeconds + this->virtualBuffer) > this->bolaBufferMaxSeconds) { + this->virtualBuffer = this->bolaBufferMaxSeconds - bufferLevelSeconds; + } + if (this->virtualBuffer < 0.0) { + this->virtualBuffer = 0.0; + } + + Debug("VirtualBuffer - Virtual Buffer Value:\t%f\n", this->virtualBuffer); + + // Update currentDownloadTimeInstant + this->lastDownloadTimeInstant = this->currentDownloadTimeInstant; + + // Update bolaQuality using virtualBuffer: bufferLevel might be artificially low because of lack of availability + + int bolaQualityVirtual = getQualityFromBufferLevel(bufferLevelSeconds + this->virtualBuffer); + Debug("VirtualBuffer - Bola Quality Virtual:\t%d\n", bolaQualityVirtual); + if (bolaQualityVirtual > bolaQuality) { + // May use quality higher than that indicated by real buffer level. + // In this case, make sure there is enough throughput to download a fragment before real buffer runs out. + int maxQuality = bolaQuality; + while (maxQuality < bolaQualityVirtual && (this->availableBitrates[maxQuality + 1] * this->segmentDuration) / (this->currentBitrate * SAFETY_FACTOR) < bufferLevelSeconds) + { + ++maxQuality; + } + // TODO: maybe we can use a more conservative level here, but this should be OK + Debug("VirtualBuffer - Bola Quality Virtual HIGHER than Bola Quality - Max Quality:\t%d\n", maxQuality); + if (maxQuality > bolaQuality) + { + // We can (and will) download at a quality higher than that indicated by real buffer level. + if (bolaQualityVirtual <= maxQuality) { + // we can download fragment indicated by real+virtual buffer without rebuffering + bolaQuality = bolaQualityVirtual; + } else { + // downloading fragment indicated by real+virtual rebuffers, use lower quality + bolaQuality = maxQuality; + // deflate virtual buffer to match quality + double targetBufferLevel = this->Vp * (this->gp + this->utilityVector[bolaQuality]); + if (bufferLevelSeconds + this->virtualBuffer > targetBufferLevel) { + this->virtualBuffer = targetBufferLevel - bufferLevelSeconds; + if (this->virtualBuffer < 0.0) { // should be false + this->virtualBuffer = 0.0; + } + } + } + } + } + } + + + if (this->bolaState == STARTUP || this->bolaState == STARTUP_NO_INC) { + // in startup phase, use some throughput estimation + + int quality = getQualityFromThroughput(this->batchBw*SAFETY_FACTOR); + + if (this->batchBw <= 0.0) { + // something went wrong - go to steady state + this->bolaState = STEADY; + } + if (this->bolaState == STARTUP && quality < this->currentQuality) { + // Since the quality is decreasing during startup, it will not be allowed to increase again. + this->bolaState = STARTUP_NO_INC; + } + if (this->bolaState == STARTUP_NO_INC && quality > this->currentQuality) { + // In this state the quality is not allowed to increase until steady state. + quality = this->currentQuality; + } + if (quality <= bolaQuality) { + // Since the buffer is full enough for steady state operation to match startup operation, switch over to steady state. + this->bolaState = STEADY; + } + if (this->bolaState != STEADY) { + // still in startup mode + this->currentQuality = quality; + //this->representation = this->availableBitrates[this->currentQuality]; + //this->currentBitrate = (uint64_t) this->representation->GetBandwidth(); + this->representation = this->adaptationSet->GetRepresentation().at(this->currentQuality); + this->currentBitrate = (uint64_t) this->availableBitrates[this->currentQuality]; + Debug("STILL IN STARTUP - Current Bitrate:\t%I64u\n", this->currentBitrate); + Debug("ADAPTATION_LOGIC:\tFor %s:\tlast_buffer: %f\tbuffer_level: %f, instantaneousBw: %lu, AverageBW: %lu, choice: %d\n",isVideo ? "video" : "audio",(double)lastBufferFill/100 , (double)bufferFill/100, this->instantBw, this->averageBw , this->currentQuality); + this->lastBufferFill = bufferFill; + return; + } + } + + // Steady State + + // In order to avoid oscillation, the "BOLA-O" variant is implemented. + // When network bandwidth lies between two encoded bitrate levels, stick to the lowest one. + double delaySeconds = 0.0; + if (bolaQuality > this->currentQuality) { + Debug("STEADY -- BOLA QUALITY:\t%d - HIGHER than - CURRENT QUALITY:\t%I64u\n", bolaQuality, this->currentBitrate); + // do not multiply throughput by bandwidthSafetyFactor here; + // we are not using throughput estimation but capping bitrate to avoid oscillations + int quality = getQualityFromThroughput(this->batchBw); + if (bolaQuality > quality) { + // only intervene if we are trying to *increase* quality to an *unsustainable* level + if (quality < this->currentQuality) { + // The aim is only to avoid oscillations - do not drop below current quality + quality = this->currentQuality; + } else { + // We are dropping to an encoded bitrate which is a little less than the network bandwidth + // since bitrate levels are discrete. Quality 'quality' might lead to buffer inflation, + // so we deflate the buffer to the level that 'quality' gives positive utility. + double targetBufferLevel = this->Vp * (this->utilityVector[quality] + this->gp); + delaySeconds = bufferLevelSeconds - targetBufferLevel; + } + bolaQuality = quality; + } + } + + if (delaySeconds > 0.0) { + // first reduce virtual buffer + if (delaySeconds > this->virtualBuffer) { + delaySeconds -= this->virtualBuffer; + this->virtualBuffer = 0.0; + } else { + this->virtualBuffer -= delaySeconds; + delaySeconds = 0.0; + } + } + if (delaySeconds > 0.0) { + // TODO Check the scope of this function. Is it a delayed request? + // streamProcessor.getScheduleController().setTimeToLoadDelay(1000.0 * delaySeconds); + // NEED TO CHECK THIS + Debug("STEADY -- DELAY DOWNLOAD OF:\t%f\n", delaySeconds); + this->multimediaManager->setTargetDownloadingTime(this->isVideo, delaySeconds); + } + + this->currentQuality = bolaQuality; + //this->representation = this->availableBitrates[this->currentQuality]; + this->representation = this->adaptationSet->GetRepresentation().at(this->currentQuality); + this->currentBitrate = (uint64_t) this->availableBitrates[this->currentQuality]; + Debug("STEADY - Current Bitrate:\t%I64u\n", this->currentBitrate); + Debug("ADAPTATION_LOGIC:\tFor %s:\tlast_buffer: %f\tbuffer_level: %f, instantaneousBw: %lu, AverageBW: %lu, choice: %d\n",isVideo ? "video" : "audio",(double)lastBufferFill/100 , (double)bufferFill/100, this->instantBw, this->averageBw , this->currentQuality); + this->lastBufferFill = bufferFill; +} + +void BolaAdaptation::bitrateUpdate(uint64_t bps, uint32_t segNum) +{ + this->instantBw = bps; + + // Avg bandwidth estimate with EWMA + if(this->averageBw == 0) + { + this->averageBw = bps; + } + else + { + this->averageBw = this->alphaRate*this->averageBw + (1 - this->alphaRate)*bps; + } + + // Avg bandwidth estimate with batch mean of THROUGHPUT_SAMPLES sample + this->batchBwCount++; + this->batchBwSamples.push_back(bps); + + if(this->batchBwCount++ == THROUGHPUT_SAMPLES) + { + for(int i=0; i<THROUGHPUT_SAMPLES; i++) + this->batchBw += this->batchBwSamples[i]; + + this->batchBw /= THROUGHPUT_SAMPLES; + + Debug("BATCH BW:\t%I64u\n", this->batchBw); + + this->batchBwCount=0; + this->batchBwSamples.clear(); + } +} + +void BolaAdaptation::dLTimeUpdate(double time) +{ + auto m_now = std::chrono::system_clock::now(); + auto m_now_sec = std::chrono::time_point_cast<std::chrono::seconds>(m_now); + + auto now_value = m_now_sec.time_since_epoch(); + double dl_instant = now_value.count(); + //this->lastSegmentDownloadTime = time; + //this->currentDownloadTimeInstant = std::chrono::duration_cast<duration_in_seconds>(system_clock::now()).count(); + this->currentDownloadTimeInstant = dl_instant; +} + +void BolaAdaptation::onEOS(bool value) +{ + this->bufferEOS = value; +} + +void BolaAdaptation::checkedByDASHReceiver() +{ + this->isCheckedForReceiver = false; +} +void BolaAdaptation::bufferUpdate(uint32_t bufferFill, int maxC) +{ + this->setBitrate(bufferFill); + this->notifyBitrateChange(); +} diff --git a/Adaptation/Bola.h b/Adaptation/Bola.h new file mode 100644 index 00000000..29011664 --- /dev/null +++ b/Adaptation/Bola.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_BOLA_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_BOLA_H_ + +#include "AbstractAdaptationLogic.h" +#include "../MPD/AdaptationSetStream.h" +#include "../Input/IDASHReceiverObserver.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class BolaAdaptation : public AbstractAdaptationLogic +{ +public: + BolaAdaptation (dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params); + virtual ~BolaAdaptation(); + + virtual LogicType getType(); + virtual bool isUserDependent(); + virtual bool isRateBased(); + virtual bool isBufferBased(); + virtual void bitrateUpdate(uint64_t bps, uint32_t segNum); + virtual void dLTimeUpdate(double time); + virtual void bufferUpdate(uint32_t bufferFill, int maxC); + void setBitrate(uint32_t bufferFill); + uint64_t getBitrate(); + virtual void setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager); + void notifyBitrateChange(); + void onEOS(bool value); + void checkedByDASHReceiver(); + + int getQualityFromThroughput(uint64_t bps); + int getQualityFromBufferLevel(double bufferLevelSec); + +private: + enum BolaState + { + ONE_BITRATE, // If one bitrate (or init failed), always NO_CHANGE + STARTUP, // Download fragments at most recently measured throughput + STARTUP_NO_INC, // If quality increased then decreased during startup, then quality cannot be increased. + STEADY // The buffer is primed (should be above bufferTarget) + }; + + bool initState; + double bufferMaxSizeSeconds; // Usually set to 30s + double bufferTargetSeconds; // It is passed as an init parameter. + // It states the difference between STARTUP and STEADY + // 12s following dash.js implementation + + double bolaBufferTargetSeconds; // BOLA introduces a virtual buffer level in order to make quality decisions + // as it was filled (instead of the actual bufferTargetSeconds) + + double bolaBufferMaxSeconds; // When using the virtual buffer, it must be capped. + + uint32_t bufferTargetPerc; // Computed considering a bufferSize = 30s + double totalDuration; // Total video duration in seconds (taken from MPD) + double segmentDuration; // Segment duration in seconds + + std::vector<uint64_t> availableBitrates; + std::vector<double> utilityVector; + uint32_t bitrateCount; // Number of available bitrates + BolaState bolaState; // Keeps track of Bola state + + // Bola Vp and gp (multiplied by the segment duration 'p') + // They are dimensioned such that log utility would always prefer + // - the lowest bitrate when bufferLevel = segmentDuration + // - the highest bitrate when bufferLevel = bufferTarget + double Vp; + double gp; + + bool safetyGuarantee; + double maxRtt; + + double virtualBuffer; + + uint64_t currentBitrate; + int currentQuality; + uint64_t batchBw; + int batchBwCount; + std::vector<uint64_t> batchBwSamples; + uint64_t instantBw; + uint64_t averageBw; + + double lastDownloadTimeInstant; + double currentDownloadTimeInstant; + double lastSegmentDownloadTime; + + uint32_t lastBufferFill; + bool bufferEOS; + bool shouldAbort; + double alphaRate; + bool isCheckedForReceiver; + + viper::managers::IMultimediaManagerBase *multimediaManager; + dash::mpd::IRepresentation *representation; +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_BOLA_H_ */ diff --git a/Adaptation/BufferBasedAdaptation.cpp b/Adaptation/BufferBasedAdaptation.cpp new file mode 100644 index 00000000..18d4a592 --- /dev/null +++ b/Adaptation/BufferBasedAdaptation.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BufferBasedAdaptation.h" +#include<stdio.h> + + +using namespace dash::mpd; +using namespace libdash::framework::adaptation; +using namespace libdash::framework::input; +using namespace libdash::framework::mpd; + +BufferBasedAdaptation::BufferBasedAdaptation (IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params) : + AbstractAdaptationLogic (mpd, period, adaptationSet, isVid) +{ + this->reservoirThreshold = params->BufferBased_reservoirThreshold; + this->maxThreshold = params->BufferBased_maxThreshold; + + std::vector<IRepresentation* > representations = this->adaptationSet->GetRepresentation(); + + this->representation = this->adaptationSet->GetRepresentation().at(0); + this->multimediaManager = NULL; + this->lastBufferFill = 0; + this->bufferEOS = false; + this->shouldAbort = false; + Debug("BufferBasedParams:\t%f\t%f\n", (double)reservoirThreshold/100, (double)maxThreshold/100); + Debug("Buffer Adaptation: STARTED\n"); +} + +BufferBasedAdaptation::~BufferBasedAdaptation () +{ +} + +LogicType BufferBasedAdaptation::getType() +{ + return adaptation::BufferBased; +} + +bool BufferBasedAdaptation::isUserDependent() +{ + return false; +} + +bool BufferBasedAdaptation::isRateBased() +{ + return false; +} +bool BufferBasedAdaptation::isBufferBased() +{ + return true; +} + +void BufferBasedAdaptation::setMultimediaManager (viper::managers::IMultimediaManagerBase *_mmManager) +{ + this->multimediaManager = _mmManager; +} + +void BufferBasedAdaptation::notifyBitrateChange() +{ + if(this->multimediaManager) + if(this->multimediaManager->isStarted() && !this->multimediaManager->isStopping()) + if(this->isVideo) + this->multimediaManager->setVideoQuality(this->period, this->adaptationSet, this->representation); + else + this->multimediaManager->setAudioQuality(this->period, this->adaptationSet, this->representation); + + if(this->shouldAbort) + { + this->multimediaManager->shouldAbort(this->isVideo); + } + this->shouldAbort = false; +} + +uint64_t BufferBasedAdaptation::getBitrate() +{ + return this->currentBitrate; +} + +void BufferBasedAdaptation::setBitrate(uint32_t bufferFill) +{ + std::vector<IRepresentation *> representations; + representations = this->adaptationSet->GetRepresentation(); + size_t i = 0; + + if(representations.size() == 1) + { + i = 0; + } + else + { + while(bufferFill > this->reservoirThreshold + i * (this->maxThreshold - this->reservoirThreshold)/(representations.size()-1)) + { + i++; + } + } + if((size_t)i >= (size_t)(representations.size())) + i = representations.size() - 1; + this->representation = representations.at(i); + if( 0 && !this->bufferEOS && this->lastBufferFill > this->reservoirThreshold && bufferFill <= this->reservoirThreshold) + { + this->shouldAbort = true; + } + Debug("ADAPTATION_LOGIC:\tFor %s:\tlast_buffer: %f\tbuffer_level: %f, choice: %lu, should_trigger_abort: %s\n",isVideo ? "video" : "audio",(double)lastBufferFill/100 , (double)bufferFill/100, i, this->shouldAbort ? "YES" : "NO"); + this->lastBufferFill = bufferFill; + +} + +void BufferBasedAdaptation::bitrateUpdate(uint64_t bps, uint32_t segNum) +{ +} + +void BufferBasedAdaptation::dLTimeUpdate(double time) +{ +} + +void BufferBasedAdaptation::onEOS(bool value) +{ + this->bufferEOS = value; +} + +void BufferBasedAdaptation::checkedByDASHReceiver() +{ +} + +void BufferBasedAdaptation::bufferUpdate(uint32_t bufferFill, int maxC) +{ + this->setBitrate(bufferFill); + this->notifyBitrateChange(); +} diff --git a/Adaptation/BufferBasedAdaptation.h b/Adaptation/BufferBasedAdaptation.h new file mode 100644 index 00000000..32ad8045 --- /dev/null +++ b/Adaptation/BufferBasedAdaptation.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATION_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATION_H_ + +#include "AbstractAdaptationLogic.h" +#include "../MPD/AdaptationSetStream.h" +#include "../Input/IDASHReceiverObserver.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class BufferBasedAdaptation : public AbstractAdaptationLogic +{ +public: + BufferBasedAdaptation(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params); + virtual ~BufferBasedAdaptation(); + + virtual LogicType getType(); + virtual bool isUserDependent(); + virtual bool isRateBased(); + virtual bool isBufferBased(); + virtual void bitrateUpdate(uint64_t bps, uint32_t segNum); + virtual void bufferUpdate(uint32_t bufferFill, int maxC); + virtual void dLTimeUpdate(double time); + void setBitrate(uint32_t bufferFill); + uint64_t getBitrate(); + virtual void setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager); + void notifyBitrateChange(); + void onEOS(bool value); + void checkedByDASHReceiver(); + +private: + uint64_t currentBitrate; + std::vector<uint64_t> availableBitrates; + viper::managers::IMultimediaManagerBase *multimediaManager; + dash::mpd::IRepresentation *representation; + uint32_t reservoirThreshold; + uint32_t maxThreshold; + uint32_t lastBufferFill; + bool bufferEOS; + bool shouldAbort; +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATION_H_ */ diff --git a/Adaptation/BufferBasedAdaptationWithRateBased.cpp b/Adaptation/BufferBasedAdaptationWithRateBased.cpp new file mode 100644 index 00000000..3a1895c0 --- /dev/null +++ b/Adaptation/BufferBasedAdaptationWithRateBased.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BufferBasedAdaptationWithRateBased.h" +#include<stdio.h> + +using namespace dash::mpd; +using namespace libdash::framework::adaptation; +using namespace libdash::framework::input; +using namespace libdash::framework::mpd; + +BufferBasedAdaptationWithRateBased::BufferBasedAdaptationWithRateBased(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params) : + AbstractAdaptationLogic(mpd, period, adaptationSet, isVid) +{ + this->alphaRate = params->Adaptech_Alpha; + this->reservoirThreshold = params->Adaptech_FirstThreshold; + this->maxThreshold = params->Adaptech_SecondThreshold; + this->switchUpThreshold = params->Adaptech_SwitchUpThreshold; + this->slackParam = params->Adaptech_SlackParameter; + + std::vector<IRepresentation* > representations = this->adaptationSet->GetRepresentation(); + + this->m_count = 0; + this->instantBw = 0; + this->averageBw = 0; + this->representation = this->adaptationSet->GetRepresentation().at(0); + this->multimediaManager = NULL; + this->lastBufferFill = 0; + this->bufferEOS = false; + this->shouldAbort = false; + this->isCheckedForReceiver = false; + this->myQuality = 0; + Debug("BufferRateBasedParams:\talpha:%f\tfirst threshold: %f\tsecond threshold: %f\tswitch-up margin: %d\tSlack: %f\n",this->alphaRate, (double)reservoirThreshold/100, (double)maxThreshold/100, this->switchUpThreshold, this->slackParam); + Debug("Buffer Adaptation: STARTED\n"); +} +BufferBasedAdaptationWithRateBased::~BufferBasedAdaptationWithRateBased () +{ +} + +LogicType BufferBasedAdaptationWithRateBased::getType() +{ + return adaptation::BufferBased; +} + +bool BufferBasedAdaptationWithRateBased::isUserDependent() +{ + return false; +} + +bool BufferBasedAdaptationWithRateBased::isRateBased() +{ + return true; +} +bool BufferBasedAdaptationWithRateBased::isBufferBased() +{ + return true; +} + +void BufferBasedAdaptationWithRateBased::setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager) +{ + this->multimediaManager = _mmManager; +} + +void BufferBasedAdaptationWithRateBased::notifyBitrateChange() +{ + if(this->multimediaManager) + if(this->multimediaManager->isStarted() && !this->multimediaManager->isStopping()) + if(this->isVideo) + this->multimediaManager->setVideoQuality(this->period, this->adaptationSet, this->representation); + else + this->multimediaManager->setAudioQuality(this->period, this->adaptationSet, this->representation); + //Should Abort is done here to avoid race condition with DASHReceiver::DoBuffering() + if(this->shouldAbort) + { + this->multimediaManager->shouldAbort(this->isVideo); + } + this->shouldAbort = false; +} + +uint64_t BufferBasedAdaptationWithRateBased::getBitrate() +{ + return this->currentBitrate; +} + +void BufferBasedAdaptationWithRateBased::setBitrate(uint32_t bufferFill) +{ + uint32_t phi1, phi2; + std::vector<IRepresentation *> representations; + representations = this->adaptationSet->GetRepresentation(); + size_t i = 0; + + Debug("bufferlevel: %u, instant rate %lu, average rate %lu\n", bufferFill, this->instantBw, this->averageBw); + phi1 = 0; + phi2 = 0; + while(i < representations.size()) + { + if(phi1 == 0 && representations.at(i)->GetBandwidth() > slackParam * this->instantBw) + { + phi1 = representations.at((i == 0) ? i : i -1)->GetBandwidth(); + } + if(phi2 == 0 && representations.at(i)->GetBandwidth() > slackParam * this->averageBw) + { + phi2 = representations.at((i == 0) ? i : i -1)->GetBandwidth(); + } + i++; + } + + if(!phi1) + phi1 = representations.at(representations.size() - 1)->GetBandwidth(); + + if(!phi2) + phi2 = representations.at(representations.size() - 1)->GetBandwidth(); + + if(bufferFill < this->reservoirThreshold) + { + this->m_count = 0; + this->myQuality = 0; + } + else + { + if(bufferFill < this->maxThreshold) + { + this->m_count = 0; + if(this->currentBitrate > phi1) + { + if(this->myQuality > 0) + { + this->myQuality--; + } + } + else + { + if(this->currentBitrate < phi1) + { + if(this->myQuality < representations.size() - 1) + { + this->myQuality++; + } + } + } + } + else + { // bufferFill > this->maxThreshold + if(this->currentBitrate < phi2) + { + m_count++; + + if(m_count >= switchUpThreshold && this->myQuality < representations.size() - 1) + { + this->m_count = 0; + this->myQuality++; + } + } + } + } + this->representation = representations.at(this->myQuality); + this->currentBitrate = (uint64_t) this->representation->GetBandwidth(); + Debug("ADAPTATION_LOGIC:\tFor %s:\tlast_buffer: %f\tbuffer_level: %f, instantaneousBw: %lu, AverageBW: %lu, choice: %d\n",isVideo ? "video" : "audio",(double)lastBufferFill/100 , (double)bufferFill/100, this->instantBw, this->averageBw , this->myQuality); +} + +void BufferBasedAdaptationWithRateBased::bitrateUpdate(uint64_t bps, uint32_t segNum) +{ + Debug("rate estimation: %lu\n", bps); + this->instantBw = bps; + if(this->averageBw == 0) + { + this->averageBw = bps; + } + else + { + this->averageBw = this->alphaRate*this->averageBw + (1 - this->alphaRate)*bps; + } +} + +void BufferBasedAdaptationWithRateBased::onEOS(bool value) +{ + this->bufferEOS = value; +} + +void BufferBasedAdaptationWithRateBased::checkedByDASHReceiver() +{ + this->isCheckedForReceiver = false; +} +void BufferBasedAdaptationWithRateBased::bufferUpdate(uint32_t bufferFill, int maxC) +{ + Debug("buffer update: %u\n", bufferFill); + this->setBitrate(bufferFill); + this->notifyBitrateChange(); +} + +void BufferBasedAdaptationWithRateBased::dLTimeUpdate(double time) +{ +} + diff --git a/Adaptation/BufferBasedAdaptationWithRateBased.h b/Adaptation/BufferBasedAdaptationWithRateBased.h new file mode 100644 index 00000000..5c787d30 --- /dev/null +++ b/Adaptation/BufferBasedAdaptationWithRateBased.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATIONRATE_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATIONRATE_H_ + +#include "AbstractAdaptationLogic.h" +#include "../MPD/AdaptationSetStream.h" +#include "../Input/IDASHReceiverObserver.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class BufferBasedAdaptationWithRateBased : public AbstractAdaptationLogic +{ +public: + BufferBasedAdaptationWithRateBased(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params); + virtual ~BufferBasedAdaptationWithRateBased(); + + virtual LogicType getType(); + virtual bool isUserDependent(); + virtual bool isRateBased(); + virtual bool isBufferBased(); + virtual void bitrateUpdate(uint64_t bps, uint32_t segNum); + virtual void bufferUpdate(uint32_t bufferFill, int maxC); + virtual void dLTimeUpdate(double time); + void setBitrate(uint32_t bufferFill); + uint64_t getBitrate(); + virtual void setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager); + void notifyBitrateChange(); + void onEOS(bool value); + void checkedByDASHReceiver(); + +private: + uint64_t currentBitrate; + std::vector<uint64_t> availableBitrates; + viper::managers::IMultimediaManagerBase *multimediaManager; + dash::mpd::IRepresentation *representation; + uint32_t reservoirThreshold; + uint32_t maxThreshold; + uint32_t lastBufferFill; + int m_count; + int switchUpThreshold; + bool bufferEOS; + bool shouldAbort; + double alphaRate; + uint64_t averageBw; + uint64_t instantBw; + int myQuality; + double slackParam; + bool isCheckedForReceiver; +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATIONRATE_H_ */ diff --git a/Adaptation/BufferBasedThreeThresholdAdaptation.cpp b/Adaptation/BufferBasedThreeThresholdAdaptation.cpp new file mode 100644 index 00000000..021e7b7c --- /dev/null +++ b/Adaptation/BufferBasedThreeThresholdAdaptation.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BufferBasedThreeThresholdAdaptation.h" +#include<stdio.h> + +using namespace dash::mpd; +using namespace libdash::framework::adaptation; +using namespace libdash::framework::input; +using namespace libdash::framework::mpd; + +BufferBasedThreeThresholdAdaptation::BufferBasedThreeThresholdAdaptation(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params) : + AbstractAdaptationLogic(mpd, period, adaptationSet, isVid) +{ + this->firstThreshold = params->BufferThreeThreshold_FirstThreshold; + this->secondThreshold = params->BufferThreeThreshold_SecondThreshold; + this->thirdThreshold = params->BufferThreeThreshold_ThirdThreshold; + this->slackParam = params->BufferThreeThreshold_slackParameter; + std::vector<IRepresentation* > representations = this->adaptationSet->GetRepresentation(); + this->representation = this->adaptationSet->GetRepresentation().at(0); + this->multimediaManager = NULL; + this->lastBufferFill = 0; + this->bufferEOS = false; + this->shouldAbort = false; + this->isCheckedForReceiver = false; + Debug("BufferRateBasedParams:\t%f\t%f\t%f\n",(double)this->firstThreshold/100, (double)secondThreshold/100, (double)thirdThreshold/100); + Debug("Buffer Adaptation: STARTED\n"); +} + +BufferBasedThreeThresholdAdaptation::~BufferBasedThreeThresholdAdaptation() +{ +} + +LogicType BufferBasedThreeThresholdAdaptation::getType() +{ + return adaptation::BufferBasedThreeThreshold; +} + +bool BufferBasedThreeThresholdAdaptation::isUserDependent() +{ + return false; +} + +bool BufferBasedThreeThresholdAdaptation::isRateBased() +{ + return true; +} + +bool BufferBasedThreeThresholdAdaptation::isBufferBased() +{ + return true; +} + +void BufferBasedThreeThresholdAdaptation::setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager) +{ + this->multimediaManager = _mmManager; +} + +void BufferBasedThreeThresholdAdaptation::notifyBitrateChange() +{ + if(this->multimediaManager) + if(this->multimediaManager->isStarted() && !this->multimediaManager->isStopping()) + if(this->isVideo) + this->multimediaManager->setVideoQuality(this->period, this->adaptationSet, this->representation); + else + this->multimediaManager->setAudioQuality(this->period, this->adaptationSet, this->representation); + //Should Abort is done here to avoid race condition with DASHReceiver::DoBuffering() + if(this->shouldAbort) + { + this->multimediaManager->shouldAbort(this->isVideo); + } + this->shouldAbort = false; +} + +uint64_t BufferBasedThreeThresholdAdaptation::getBitrate() +{ + return this->currentBitrate; +} + +void BufferBasedThreeThresholdAdaptation::setBitrate(uint32_t bufferFill) +{ + uint32_t phi1, phi2; + std::vector<IRepresentation *> representations; + representations = this->adaptationSet->GetRepresentation(); + size_t i = 0; + + if(this->isCheckedForReceiver) + { + return; + } + this->isCheckedForReceiver = true; + + + if(bufferFill < this->firstThreshold) + { + this->myQuality = 0; + } + else + { + if(bufferFill < this->secondThreshold) + { + if(this->currentBitrate >= this->instantBw) + { + if(this->myQuality > 0) + { + this->myQuality--; + } + } + } + else + { + if(bufferFill < this->thirdThreshold) + { + } + else + {// bufferLevel > thirdThreshold + if(this->currentBitrate <= this->instantBw) + { + if(this->myQuality < representations.size() - 1) + this->myQuality++; + } + } + } + } + this->representation = representations.at(this->myQuality); + this->currentBitrate = (uint64_t) this->representation->GetBandwidth(); + Debug("ADAPTATION_LOGIC:\tFor %s:\tlast_buffer: %f\tbuffer_level: %f, instantaneousBw: %lu, choice: %d\n",isVideo ? "video" : "audio",(double)lastBufferFill/100 , (double)bufferFill/100, this->instantBw, this->myQuality); +} + +void BufferBasedThreeThresholdAdaptation::bitrateUpdate(uint64_t bps, uint32_t segNum) +{ + this->instantBw = bps; +} + +void BufferBasedThreeThresholdAdaptation::onEOS(bool value) +{ + this->bufferEOS = value; +} + +void BufferBasedThreeThresholdAdaptation::checkedByDASHReceiver() +{ + this->isCheckedForReceiver = false; +} +void BufferBasedThreeThresholdAdaptation::bufferUpdate(uint32_t bufferFill, int maxC) +{ + this->setBitrate(bufferFill); + this->notifyBitrateChange(); +} + +void BufferBasedThreeThresholdAdaptation::dLTimeUpdate(double time) +{ +} + + diff --git a/Adaptation/BufferBasedThreeThresholdAdaptation.h b/Adaptation/BufferBasedThreeThresholdAdaptation.h new file mode 100644 index 00000000..62160bc3 --- /dev/null +++ b/Adaptation/BufferBasedThreeThresholdAdaptation.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATIONTHREE_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATIONTHREE_H_ + +#include "AbstractAdaptationLogic.h" +#include "../MPD/AdaptationSetStream.h" +#include "../Input/IDASHReceiverObserver.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class BufferBasedThreeThresholdAdaptation : public AbstractAdaptationLogic +{ +public: + BufferBasedThreeThresholdAdaptation(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params); + virtual ~BufferBasedThreeThresholdAdaptation(); + + virtual LogicType getType(); + virtual bool isUserDependent(); + virtual bool isRateBased(); + virtual bool isBufferBased(); + virtual void bitrateUpdate(uint64_t bps, uint32_t segNum); + virtual void bufferUpdate(uint32_t bufferFill, int maxC); + virtual void dLTimeUpdate(double time); + void setBitrate(uint32_t bufferFill); + uint64_t getBitrate(); + virtual void setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager); + void notifyBitrateChange(); + void onEOS(bool value); + void checkedByDASHReceiver(); + +private: + uint64_t currentBitrate; + std::vector<uint64_t> availableBitrates; + viper::managers::IMultimediaManagerBase *multimediaManager; + dash::mpd::IRepresentation *representation; + uint32_t secondThreshold; + uint32_t thirdThreshold; + uint32_t lastBufferFill; + bool bufferEOS; + bool shouldAbort; + uint32_t firstThreshold; + uint64_t instantBw; + int myQuality; + double slackParam; + bool isCheckedForReceiver; +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATIONTHREE_H_ */ diff --git a/Adaptation/IAdaptationLogic.h b/Adaptation/IAdaptationLogic.h new file mode 100644 index 00000000..bfe980c7 --- /dev/null +++ b/Adaptation/IAdaptationLogic.h @@ -0,0 +1,115 @@ +/* + * IAdaptationLogic.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_IADAPTATIONLOGIC_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_IADAPTATIONLOGIC_H_ +#include "../Input/MediaObject.h" +#include "../Input/DASHReceiver.h" +#include "IRepresentation.h" +#include "../Managers/IMultimediaManagerBase.h" + +namespace libdash +{ +namespace framework +{ +namespace input +{ +class DASHReceiver; +} +namespace adaptation +{ +//#define START __LINE__ +//ADAPTATIONLOGIC Count is an hack to have the number of adaptation logic that we can use +#define FOREACH_ADAPTATIONLOGIC(ADAPTATIONLOGIC) \ + ADAPTATIONLOGIC(AlwaysLowest) \ + ADAPTATIONLOGIC(RateBased) \ + ADAPTATIONLOGIC(BufferBased) \ + ADAPTATIONLOGIC(BufferRateBased) \ + ADAPTATIONLOGIC(BufferBasedThreeThreshold) \ + ADAPTATIONLOGIC(Panda) \ + ADAPTATIONLOGIC(Bola) \ + ADAPTATIONLOGIC(Count) \ + + +#define GENERATE_ENUM(ENUM) ENUM, +#define GENERATE_STRING(STRING) #STRING, + +enum LogicType { + FOREACH_ADAPTATIONLOGIC(GENERATE_ENUM) +}; + +static const char *LogicType_string[] = { + FOREACH_ADAPTATIONLOGIC(GENERATE_STRING) +}; + +class IAdaptationLogic +{ +public: + virtual ~IAdaptationLogic() {} + + virtual uint32_t getPosition() = 0; + virtual void setPosition(uint32_t segmentNumber) = 0; + virtual dash::mpd::IRepresentation* getRepresentation() = 0; + virtual void setRepresentation(dash::mpd::IPeriod *period, + dash::mpd::IAdaptationSet *adaptationSet, + dash::mpd::IRepresentation *representation)= 0; + virtual LogicType getType() = 0; + virtual bool isUserDependent()= 0; + virtual void bitrateUpdate(uint64_t bps, uint32_t segNum) = 0; + virtual void dLTimeUpdate(double time) = 0; + virtual void bufferUpdate(uint32_t bufferfillstate, int maxC) = 0; + virtual bool isRateBased() = 0; + virtual bool isBufferBased() = 0; + virtual void setMultimediaManager(viper::managers::IMultimediaManagerBase *mmManager)= 0; + virtual void onEOS(bool value) = 0; + virtual void checkedByDASHReceiver() = 0; +}; + +struct AdaptationParameters +{ + int segmentBufferSize; + double segmentDuration; + + //RATE BASED + double Rate_Alpha; + + //BUFFER BASED + int BufferBased_reservoirThreshold; + int BufferBased_maxThreshold; + + //BOLA + double Bola_Alpha; + double Bola_bufferTargetSeconds; + + //ADAPTECH + double Adaptech_Alpha; + int Adaptech_FirstThreshold; + int Adaptech_SecondThreshold; + int Adaptech_SwitchUpThreshold; + double Adaptech_SlackParameter; + + //BUFFER THREE THRESHOLDS + int BufferThreeThreshold_FirstThreshold; + int BufferThreeThreshold_SecondThreshold; + int BufferThreeThreshold_ThirdThreshold; + double BufferThreeThreshold_slackParameter; + //PANDA + double Panda_Alpha; + double Panda_Beta; + double Panda_Bmin; + double Panda_K; + double Panda_W; + double Panda_Epsilon; +}; +} +} +} +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_IADAPTATIONLOGIC_H_ */ diff --git a/Adaptation/Panda.cpp b/Adaptation/Panda.cpp new file mode 100644 index 00000000..cb2ec660 --- /dev/null +++ b/Adaptation/Panda.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Panda.h" +#include<stdio.h> + + +using namespace dash::mpd; +using namespace libdash::framework::adaptation; +using namespace libdash::framework::input; +using namespace libdash::framework::mpd; + +PandaAdaptation::PandaAdaptation(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params) : + AbstractAdaptationLogic (mpd, period, adaptationSet, isVid) +{ + this->param_Alpha = params->Panda_Alpha; + this->param_Beta = params->Panda_Beta; + this->param_Bmin = params->Panda_Bmin; + this->param_K = params->Panda_K; + this->param_W = params->Panda_W; + this->param_Epsilon = params->Panda_Epsilon; + + this->segmentDuration = params->segmentDuration; + this->bufferMaxSizeSeconds = params->segmentBufferSize * this->segmentDuration; + this->targetBw = 0; + this->targetInterTime = 0.0; + + this->averageBw = 0; + this->smoothBw = 0; + this->instantBw = 0; + this->targetBw = 0; + + this->targetInterTime = 0.0; + this->interTime = 0.0; + + this->alpha_ewma = 0.8; + + this->bufferLevel = 0; + this->bufferLevelSeconds = 0.0; + + this->downloadTime = 0.0; + + this->isVideo = isVid; + this->mpd = mpd; + this->adaptationSet = adaptationSet; + this->period = period; + this->multimediaManager = NULL; + this->representation = NULL; + this->currentBitrate = 0; + this->current = 0; + + // Retrieve the available bitrates + std::vector<IRepresentation* > representations = this->adaptationSet->GetRepresentation(); + + this->availableBitrates.clear(); + Debug("PANDA Available Bitrates...\n"); + for(size_t i = 0; i < representations.size(); i++) + { + this->availableBitrates.push_back((uint64_t)(representations.at(i)->GetBandwidth())); + Debug("%d - %I64u bps\n", i+1, this->availableBitrates[i]); + } + + this->representation = this->adaptationSet->GetRepresentation().at(0); + this->currentBitrate = (uint64_t) this->representation->GetBandwidth(); + + Debug("Panda parameters: K= %f, Bmin = %f, alpha = %f, beta = %f, W = %f\n", param_K, param_Bmin, param_Alpha, param_Beta, param_W); +} + +PandaAdaptation::~PandaAdaptation() { +} + +LogicType PandaAdaptation::getType() +{ + return adaptation::Panda; +} + +bool PandaAdaptation::isUserDependent() +{ + return false; +} + +bool PandaAdaptation::isRateBased() +{ + return true; +} + +bool PandaAdaptation::isBufferBased() +{ + return true; +} + +void PandaAdaptation::setMultimediaManager (viper::managers::IMultimediaManagerBase *_mmManager) +{ + this->multimediaManager = _mmManager; +} + +void PandaAdaptation::notifyBitrateChange() +{ + if(this->multimediaManager->isStarted() && !this->multimediaManager->isStopping()) + if(this->isVideo) + this->multimediaManager->setVideoQuality(this->period, this->adaptationSet, this->representation); + else + this->multimediaManager->setAudioQuality(this->period, this->adaptationSet, this->representation); +} + +uint64_t PandaAdaptation::getBitrate() +{ + return this->currentBitrate; +} + +void PandaAdaptation::quantizer() +{ + this->deltaUp = this->param_Epsilon * (double)this->smoothBw; + this->deltaDown = 0.0; + + Debug("** DELTA UP:\t%f\n", this->deltaUp); + + uint64_t smoothBw_UP = this->smoothBw - this->deltaUp; + uint64_t smoothBw_DOWN = this->smoothBw - this->deltaDown; + + Debug("** Smooth-BW UP:\t%d\t Smooth-BW DOWN:\t%d\n", smoothBw_UP, smoothBw_DOWN); + + std::vector<IRepresentation *> representations; + representations = this->adaptationSet->GetRepresentation(); + uint32_t numQualLevels = representations.size(); + + // We have to find bitrateMin and bitrateMax + uint64_t bitrateDown, bitrateUp; + + // DOWN + uint32_t iDown = 0; + uint32_t i_d,i_u; + for (i_d = 0; i_d < this->availableBitrates.size(); ++i_d) { + if (this->availableBitrates[i_d] > smoothBw_DOWN) { + break; + } + } + if(i_d > 0) + iDown = i_d-1; + else + iDown = 0; + + bitrateDown = (uint64_t) representations.at(iDown)->GetBandwidth(); + Debug("** Bitrate DOWN:\t%d\t at Quality:\t%d\n", bitrateDown, iDown); + + // UP + uint32_t iUp = 0; + for (i_u = 0; i_u < this->availableBitrates.size(); ++i_u) { + if (this->availableBitrates[i_u] > smoothBw_UP) { + break; + } + } + if(i_u > 0) + iUp = i_u-1; + else + iUp = 0; + + bitrateUp = (uint64_t) representations.at(iUp)->GetBandwidth(); + Debug("** Bitrate UP:\t%d\t at Quality:\t%d\n", bitrateUp, iUp); + + Debug("** Current RATE:\t%d\n Current QUALITY:\t%d\n", this->currentBitrate, this->current); + + + // Next bitrate computation + if(this->currentBitrate < bitrateUp) + { + this->currentBitrate = bitrateUp; + this->current = iUp; + } + else if(this->currentBitrate <= bitrateDown && this->currentBitrate >= bitrateUp) + { + Debug("** CURRENT UNCHANGED **\n"); + } + else + { + this->currentBitrate = bitrateDown; + this->current = iDown; + } + this->representation = this->adaptationSet->GetRepresentation().at(this->current); +} + +void PandaAdaptation::setBitrate(uint64_t bps) +{ + + // 1. Calculating the targetBW + if(this->targetBw) + { + //this->targetBw = this->targetBw + param_K * this->interTime * (param_W - ((this->targetBw - bps + this->param_W) > 0 ? this->targetBw - bps + this->param_W: 0)); + if ((double)this->targetBw - (double)bps + this->param_W > 0) + this->targetBw = this->targetBw + (uint64_t)(param_K * this->interTime * (param_W - ((double)this->targetBw - (double)bps + this->param_W))); + else + this->targetBw = this->targetBw + (uint64_t)(param_K * this->interTime * param_W); + } + else + this->targetBw = bps; + + Debug("** INSTANTANEOUS BW:\t%d\n", bps); + Debug("** CLASSIC EWMA BW:\t%d\n", this->averageBw); + Debug("** PANDA TARGET BW:\t%d\n", this->targetBw); + + // 2. Calculating the smoothBW + if(this->interTime) + this->smoothBw = (uint64_t)((double)this->smoothBw - this->param_Alpha * this->interTime * ((double)this->smoothBw - (double)this->targetBw)); + else + this->smoothBw = this->targetBw; + + Debug("** PANDA SMOOTH BW:\t%d\n", this->smoothBw); + + // 3. Quantization + this->quantizer(); + Debug("ADAPTATION_LOGIC:\tFor %s:\tlast_buffer: %f\tbuffer_level: %f, instantaneousBw: %lu, AverageBW: %lu, choice: %d\n",isVideo ? "video" : "audio",(double)lastBufferLevel/100 , (double)bufferLevel/100, this->instantBw, this->averageBw , this->current); + this->lastBufferLevel = this->bufferLevel; + + // 4. Computing the "actual inter time" + this->bufferLevelSeconds = (double)((this->bufferLevel * this->bufferMaxSizeSeconds) *1./100); + this->targetInterTime = ((double)this->currentBitrate * segmentDuration) * 1./this->smoothBw + param_Beta * (this->bufferLevelSeconds - param_Bmin); + Debug("** TARGET INTER TIME:\t%f\n", this->targetInterTime); + Debug("** DOWNLOAD TIME:\t%f\n", this->downloadTime); + this->targetInterTime = (this->targetInterTime > 0) ? this->targetInterTime : 0.0; + this->interTime = this->targetInterTime > this->downloadTime ? this->targetInterTime : this->downloadTime; + this->interTime = this->interTime > 3 ? 3 : this->interTime; + + Debug("** ACTUAL INTER TIME:\t%f\n", this->interTime); + this->multimediaManager->setTargetDownloadingTime(this->isVideo, interTime); +} + +void PandaAdaptation::bitrateUpdate(uint64_t bps, uint32_t segNum) +{ + this->instantBw = bps; + + // Avg bandwidth estimate with EWMA + if(this->averageBw == 0) + { + this->averageBw = bps; + } + else + { + this->averageBw = this->alpha_ewma*this->averageBw + (1 - this->alpha_ewma)*bps; + } + + this->setBitrate(bps); + this->notifyBitrateChange(); +} + +void PandaAdaptation::dLTimeUpdate(double time) +{ + this->downloadTime = time; +} + +void PandaAdaptation::bufferUpdate(uint32_t bufferfill, int maxC) +{ + Debug("bufferlvl: %d\n", bufferfill); + this->bufferLevel = bufferfill; +} + +void PandaAdaptation::onEOS(bool value) +{ +} + +void PandaAdaptation::checkedByDASHReceiver() +{ +} diff --git a/Adaptation/Panda.h b/Adaptation/Panda.h new file mode 100644 index 00000000..000131a1 --- /dev/null +++ b/Adaptation/Panda.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_PANDA_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_PANDA_H_ + +#include "AbstractAdaptationLogic.h" +#include "../MPD/AdaptationSetStream.h" +#include "../Input/IDASHReceiverObserver.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class PandaAdaptation : public AbstractAdaptationLogic +{ +public: + PandaAdaptation(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params); + virtual ~PandaAdaptation(); + + virtual LogicType getType(); + virtual bool isUserDependent(); + virtual bool isRateBased(); + virtual bool isBufferBased(); + virtual void bitrateUpdate(uint64_t bps, uint32_t segNum); + virtual void dLTimeUpdate(double time); + virtual void bufferUpdate(uint32_t bufferFill, int maxC); + void setBitrate(uint64_t bufferFill); + uint64_t getBitrate(); + virtual void setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager); + void notifyBitrateChange(); + void onEOS(bool value); + void checkedByDASHReceiver(); + + void quantizer(); +private: + uint64_t currentBitrate; + + std::vector<uint64_t> availableBitrates; + viper::managers::IMultimediaManagerBase *multimediaManager; + dash::mpd::IRepresentation *representation; + + uint64_t averageBw; // Classic EWMA + uint64_t instantBw; + uint64_t smoothBw; // Panda paper smoothed y[n] + uint64_t targetBw; // Panda paper x[n] bw estimation + + double param_Alpha; + double alpha_ewma; + double param_Epsilon; + double param_K; + double param_W; + double param_Beta; + double param_Bmin; + + double interTime; // Actual inter time + double targetInterTime; // Target inter time + double downloadTime; + + uint32_t bufferLevel; + uint32_t lastBufferLevel; + double bufferMaxSizeSeconds; // Usually set to 60s + double bufferLevelSeconds; // Current buffer level [s] + + double segmentDuration; + double deltaUp; + double deltaDown; + size_t current; +}; + +} /* namespace adaptation */ +} /* namespace framework */ +} /* namespace libdash */ + +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_PANDA_H_ */ diff --git a/Adaptation/RateBasedAdaptation.cpp b/Adaptation/RateBasedAdaptation.cpp new file mode 100644 index 00000000..73d4ee9b --- /dev/null +++ b/Adaptation/RateBasedAdaptation.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "RateBasedAdaptation.h" +#include<stdio.h> +using namespace dash::mpd; +using namespace libdash::framework::adaptation; +using namespace libdash::framework::input; +using namespace libdash::framework::mpd; + +RateBasedAdaptation::RateBasedAdaptation(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params) : + AbstractAdaptationLogic(mpd, period, adaptationSet, isVid) +{ + std::vector<IRepresentation* > representations = this->adaptationSet->GetRepresentation(); + + this->availableBitrates.clear(); + for(size_t i = 0; i < representations.size(); i++) + { + this->availableBitrates.push_back((uint64_t)(representations.at(i)->GetBandwidth())); + } + this->currentBitrate = this->availableBitrates.at(0); + this->representation = representations.at(0); + this->multimediaManager = NULL; + this->alpha = params->Rate_Alpha; + Debug("RateBasedParams:\t%f\n",alpha); + this->averageBw = 0; +} + +RateBasedAdaptation::~RateBasedAdaptation() +{ +} + +LogicType RateBasedAdaptation::getType() +{ + return adaptation::RateBased; +} + +bool RateBasedAdaptation::isUserDependent() +{ + return false; +} + +bool RateBasedAdaptation::isRateBased() +{ + return true; +} + +bool RateBasedAdaptation::isBufferBased() +{ + return false; +} + +void RateBasedAdaptation::setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager) +{ + this->multimediaManager = _mmManager; +} + +void RateBasedAdaptation::notifyBitrateChange() +{ + if(this->multimediaManager->isStarted() && !this->multimediaManager->isStopping()) + if(this->isVideo) + this->multimediaManager->setVideoQuality(this->period, this->adaptationSet, this->representation); + else + this->multimediaManager->setAudioQuality(this->period, this->adaptationSet, this->representation); +} + +uint64_t RateBasedAdaptation::getBitrate() +{ + return this->currentBitrate; +} + +void RateBasedAdaptation::setBitrate(uint64_t bps) +{ + std::vector<IRepresentation *> representations; + representations = this->adaptationSet->GetRepresentation(); + size_t i = 0; + this->ewma(bps); + for(i = 0;i < representations.size();i++) + { + if(representations.at(i)->GetBandwidth() > this->averageBw) + { + if(i > 0) + i--; + break; + } + } + if((size_t)i == (size_t)(representations.size())) + i = i-1; + + Debug("ADAPTATION_LOGIC:\tFor %s:\tBW_estimation(ewma): %lu, choice: %lu\n", (this->isVideo ? "video" : "audio"), this->averageBw, i); + this->representation = representations.at(i); + this->currentBitrate = this->representation->GetBandwidth(); +} + +void RateBasedAdaptation::bitrateUpdate(uint64_t bps, uint32_t segNum) +{ + Debug("Rate Based adaptation: speed received: %lu\n", bps); + this->setBitrate(bps); + this->notifyBitrateChange(); +} + +void RateBasedAdaptation::ewma(uint64_t bps) +{ + if(averageBw) + { + averageBw = alpha*averageBw + (1-alpha)*bps; + } + else + { + averageBw = bps; + } +} + +void RateBasedAdaptation::onEOS(bool value) +{ +} + +void RateBasedAdaptation::checkedByDASHReceiver() +{ +} + +void RateBasedAdaptation::bufferUpdate(uint32_t bufferfill, int maxC) +{ +} + +void RateBasedAdaptation::dLTimeUpdate(double time) +{ +} diff --git a/Adaptation/RateBasedAdaptation.h b/Adaptation/RateBasedAdaptation.h new file mode 100644 index 00000000..cbf7471c --- /dev/null +++ b/Adaptation/RateBasedAdaptation.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_RATEBASEDADAPTATION_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_RATEBASEDADAPTATION_H_ + +#include "AbstractAdaptationLogic.h" +#include "../MPD/AdaptationSetStream.h" +#include "../Input/IDASHReceiverObserver.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class RateBasedAdaptation : public AbstractAdaptationLogic +{ +public: + RateBasedAdaptation(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params); + virtual ~RateBasedAdaptation(); + + virtual LogicType getType(); + virtual bool isUserDependent(); + virtual bool isRateBased(); + virtual bool isBufferBased(); + virtual void bitrateUpdate(uint64_t bps, uint32_t segNum); + virtual void bufferUpdate(uint32_t bufferfill, int maxC); + virtual void dLTimeUpdate(double time); + void setBitrate(uint64_t bps); + uint64_t getBitrate(); + virtual void setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager); + void notifyBitrateChange(); + void onEOS(bool value); + void ewma(uint64_t bps); + void checkedByDASHReceiver(); +private: + uint64_t currentBitrate; + std::vector<uint64_t> availableBitrates; + viper::managers::IMultimediaManagerBase *multimediaManager; + dash::mpd::IRepresentation *representation; + double alpha; + uint64_t averageBw; +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_RATEBASEDADAPTATION_H_ */ |