Browse Source

demux: adaptive: add new BufferingLogic

pull/104/head
Francois Cartegnie 6 years ago
parent
commit
b1e7eb53d6
  1. 2
      modules/demux/Makefile.am
  2. 381
      modules/demux/adaptive/logic/BufferingLogic.cpp
  3. 81
      modules/demux/adaptive/logic/BufferingLogic.hpp

2
modules/demux/Makefile.am

@ -342,6 +342,8 @@ libadaptive_plugin_la_SOURCES = \
demux/adaptive/logic/AlwaysBestAdaptationLogic.h \
demux/adaptive/logic/AlwaysLowestAdaptationLogic.cpp \
demux/adaptive/logic/AlwaysLowestAdaptationLogic.hpp \
demux/adaptive/logic/BufferingLogic.cpp \
demux/adaptive/logic/BufferingLogic.hpp \
demux/adaptive/logic/IDownloadRateObserver.h \
demux/adaptive/logic/NearOptimalAdaptationLogic.cpp \
demux/adaptive/logic/NearOptimalAdaptationLogic.hpp \

381
modules/demux/adaptive/logic/BufferingLogic.cpp

@ -0,0 +1,381 @@
/*
* BufferingLogic.cpp
*****************************************************************************
* Copyright (C) 2014 - 2020 VideoLabs, VideoLAN and VLC authors
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "BufferingLogic.hpp"
#include "../playlist/BaseRepresentation.h"
#include "../playlist/BasePeriod.h"
#include "../playlist/AbstractPlaylist.hpp"
#include "../playlist/SegmentTemplate.h"
#include "../playlist/SegmentTimeline.h"
#include "../playlist/SegmentList.h"
#include "../playlist/SegmentBase.h"
#include <limits>
#include <cassert>
using namespace adaptive;
using namespace adaptive::playlist;
using namespace adaptive::logic;
const vlc_tick_t AbstractBufferingLogic::BUFFERING_LOWEST_LIMIT = VLC_TICK_FROM_SEC(2);
const vlc_tick_t AbstractBufferingLogic::DEFAULT_MIN_BUFFERING = VLC_TICK_FROM_SEC(6);
const vlc_tick_t AbstractBufferingLogic::DEFAULT_MAX_BUFFERING = VLC_TICK_FROM_SEC(30);
const vlc_tick_t AbstractBufferingLogic::DEFAULT_LIVE_BUFFERING = VLC_TICK_FROM_SEC(15);
AbstractBufferingLogic::AbstractBufferingLogic()
{
userMinBuffering = 0;
userMaxBuffering = 0;
userLiveDelay = 0;
}
void AbstractBufferingLogic::setLowDelay(bool b)
{
userLowLatency = b;
}
void AbstractBufferingLogic::setUserMinBuffering(vlc_tick_t v)
{
userMinBuffering = v;
}
void AbstractBufferingLogic::setUserMaxBuffering(vlc_tick_t v)
{
userMaxBuffering = v;
}
void AbstractBufferingLogic::setUserLiveDelay(vlc_tick_t v)
{
userLiveDelay = v;
}
DefaultBufferingLogic::DefaultBufferingLogic()
: AbstractBufferingLogic()
{
}
uint64_t DefaultBufferingLogic::getStartSegmentNumber(BaseRepresentation *rep) const
{
if(rep->getPlaylist()->isLive())
return getLiveStartSegmentNumber(rep);
const MediaSegmentTemplate *segmentTemplate = rep->inheritSegmentTemplate();
if(segmentTemplate)
{
const SegmentTimeline *timeline = segmentTemplate->inheritSegmentTimeline();
if(timeline)
return timeline->minElementNumber();
return segmentTemplate->inheritStartNumber();
}
const SegmentList *list = rep->inheritSegmentList();
if(list)
return list->getStartIndex();
const SegmentBase *base = rep->inheritSegmentBase();
if(base)
return base->getSequenceNumber();
return 0;
}
vlc_tick_t DefaultBufferingLogic::getMinBuffering(const AbstractPlaylist *p) const
{
if(isLowLatency(p))
return BUFFERING_LOWEST_LIMIT;
else if(userMinBuffering)
return std::max(BUFFERING_LOWEST_LIMIT, userMinBuffering);
else if(p->getMinBuffering())
return std::max(BUFFERING_LOWEST_LIMIT, p->getMinBuffering());
else
return DEFAULT_MIN_BUFFERING;
}
vlc_tick_t DefaultBufferingLogic::getMaxBuffering(const AbstractPlaylist *p) const
{
if(isLowLatency(p))
return getMinBuffering(p);
else if(p->isLive())
{
if(userLiveDelay)
return std::max(getMinBuffering(p), userLiveDelay);
else
return std::max(getMinBuffering(p), DEFAULT_LIVE_BUFFERING);
}
else
{
if(userMaxBuffering)
return std::max(getMinBuffering(p), userMaxBuffering);
else if(p->getMaxBuffering())
return std::max(getMinBuffering(p), p->getMaxBuffering());
else
return std::max(getMinBuffering(p), DEFAULT_MAX_BUFFERING);
}
}
uint64_t DefaultBufferingLogic::getLiveStartSegmentNumber(BaseRepresentation *rep) const
{
AbstractPlaylist *playlist = rep->getPlaylist();
/* Get buffering amount */
vlc_tick_t i_buffering = getBufferingAmount(playlist);
/* Try to never buffer up to really end */
/* Enforce no overlap for demuxers segments 3.0.0 */
/* FIXME: check duration instead ? */
const unsigned SAFETY_BUFFERING_EDGE_OFFSET = 1;
const unsigned SAFETY_EXPURGING_OFFSET = 2;
SegmentList *segmentList = rep->inheritSegmentList();
SegmentBase *segmentBase = rep->inheritSegmentBase();
MediaSegmentTemplate *mediaSegmentTemplate = rep->inheritSegmentTemplate();
if(mediaSegmentTemplate)
{
uint64_t start = 0;
const Timescale timescale = mediaSegmentTemplate->inheritTimescale();
const SegmentTimeline *timeline = mediaSegmentTemplate->inheritSegmentTimeline();
if(timeline)
{
uint64_t safeMinElementNumber = timeline->minElementNumber();
uint64_t safeMaxElementNumber = timeline->maxElementNumber();
stime_t safeedgetime, safestarttime, duration;
for(unsigned i=0; i<SAFETY_BUFFERING_EDGE_OFFSET; i++)
{
if(safeMinElementNumber == safeMaxElementNumber)
break;
safeMaxElementNumber--;
}
bool b_ret = timeline->getScaledPlaybackTimeDurationBySegmentNumber(safeMaxElementNumber,
&safeedgetime, &duration);
if(unlikely(!b_ret))
return 0;
safeedgetime += duration - 1;
for(unsigned i=0; i<SAFETY_EXPURGING_OFFSET; i++)
{
if(safeMinElementNumber + 1 >= safeMaxElementNumber)
break;
safeMinElementNumber++;
}
b_ret = timeline->getScaledPlaybackTimeDurationBySegmentNumber(safeMinElementNumber,
&safestarttime, &duration);
if(unlikely(!b_ret))
return 0;
if(playlist->timeShiftBufferDepth.Get())
{
stime_t edgetime;
bool b_ret = timeline->getScaledPlaybackTimeDurationBySegmentNumber(timeline->maxElementNumber(),
&edgetime, &duration);
if(unlikely(!b_ret))
return 0;
edgetime += duration - 1;
stime_t timeshiftdepth = timescale.ToScaled(playlist->timeShiftBufferDepth.Get());
if(safestarttime + timeshiftdepth < edgetime)
{
safestarttime = edgetime - timeshiftdepth;
safeMinElementNumber = timeline->getElementNumberByScaledPlaybackTime(safestarttime);
}
}
assert(safestarttime<=safeedgetime);
stime_t starttime;
if(safeedgetime - safestarttime > timescale.ToScaled(i_buffering))
starttime = safeedgetime - timescale.ToScaled(i_buffering);
else
starttime = safestarttime;
start = timeline->getElementNumberByScaledPlaybackTime(starttime);
assert(start >= timeline->minElementNumber());
assert(start >= safeMinElementNumber);
assert(start <= timeline->maxElementNumber());
assert(start <= safeMaxElementNumber);
return start;
}
/* Else compute, current time and timeshiftdepth based */
else if(mediaSegmentTemplate->duration.Get())
{
/* Compute playback offset and effective finished segment from wall time */
vlc_tick_t now = vlc_tick_from_sec(time(NULL));
vlc_tick_t playbacktime = now - i_buffering;
vlc_tick_t minavailtime = playlist->availabilityStartTime.Get() + rep->getPeriodStart();
const uint64_t startnumber = mediaSegmentTemplate->inheritStartNumber();
const Timescale timescale = mediaSegmentTemplate->inheritTimescale();
if(!timescale)
return startnumber;
const vlc_tick_t duration = timescale.ToTime(mediaSegmentTemplate->inheritDuration());
if(!duration)
return startnumber;
/* restrict to DVR window */
if(playlist->timeShiftBufferDepth.Get())
{
vlc_tick_t elapsed = now - minavailtime;
elapsed = elapsed - (elapsed % duration); /* align to last segment */
vlc_tick_t alignednow = minavailtime + elapsed;
if(mediaSegmentTemplate->duration.Get() < elapsed)
minavailtime = alignednow - mediaSegmentTemplate->duration.Get();
if(playbacktime < minavailtime)
playbacktime = minavailtime;
}
/* Get completed segment containing the time ref */
start = mediaSegmentTemplate->getLiveTemplateNumber(playbacktime);
if (unlikely(start < startnumber))
{
assert(startnumber > start); /* blame getLiveTemplateNumber() */
start = startnumber;
}
const uint64_t max_safety_offset = playbacktime - minavailtime / duration;
const uint64_t safety_offset = std::min((uint64_t)SAFETY_BUFFERING_EDGE_OFFSET,
max_safety_offset);
if(startnumber + safety_offset <= start)
start -= safety_offset;
else
start = startnumber;
return start;
}
}
else if (segmentList && !segmentList->getSegments().empty())
{
const Timescale timescale = segmentList->inheritTimescale();
const std::vector<ISegment *> list = segmentList->getSegments();
const ISegment *back = list.back();
/* working around HLS discontinuities by using durations */
stime_t totallistduration = 0;
for(auto it = list.begin(); it != list.end(); ++it)
totallistduration += (*it)->duration.Get();
/* Apply timeshift restrictions */
stime_t availableduration;
if(playlist->timeShiftBufferDepth.Get())
{
availableduration = std::min(totallistduration,
timescale.ToScaled(playlist->timeShiftBufferDepth.Get()));
}
else availableduration = totallistduration;
uint64_t availableliststartnumber = list.front()->getSequenceNumber();
if(totallistduration != availableduration)
{
stime_t offset = totallistduration - availableduration;
for(auto it = list.begin(); it != list.end(); ++it)
{
availableliststartnumber = (*it)->getSequenceNumber();
if(offset < (*it)->duration.Get())
break;
offset -= (*it)->duration.Get();
}
}
uint64_t safeedgenumber = back->getSequenceNumber() -
std::min((uint64_t)list.size() - 1,
(uint64_t)SAFETY_BUFFERING_EDGE_OFFSET);
uint64_t safestartnumber = availableliststartnumber;
if(safeedgenumber > safestartnumber)
safestartnumber -= std::min(safeedgenumber-safestartnumber - 1,
(uint64_t)SAFETY_EXPURGING_OFFSET);
stime_t maxbufferizable = 0;
stime_t safeedgeduration = 0;
for(auto it = list.begin(); it != list.end(); ++it)
{
if((*it)->getSequenceNumber() < safestartnumber)
continue;
if((*it)->getSequenceNumber() <= safeedgenumber)
maxbufferizable += (*it)->duration.Get();
else
safeedgeduration += (*it)->duration.Get();
}
stime_t tobuffer = std::min(maxbufferizable, timescale.ToScaled(i_buffering));
stime_t skipduration = totallistduration - safeedgeduration - tobuffer ;
uint64_t start = safestartnumber;
for(auto it = list.begin(); it != list.end(); ++it)
{
start = (*it)->getSequenceNumber();
if((*it)->duration.Get() < skipduration)
break;
skipduration -= (*it)->duration.Get();
}
return start;
}
else if(segmentBase)
{
const std::vector<ISegment *> list = segmentBase->subSegments();
if(!list.empty())
return segmentBase->getSequenceNumber();
const Timescale timescale = rep->inheritTimescale();
const ISegment *back = list.back();
const stime_t bufferingstart = back->startTime.Get() + back->duration.Get() -
timescale.ToScaled(i_buffering);
uint64_t start;
if(!SegmentInfoCommon::getSegmentNumberByScaledTime(list, bufferingstart, &start))
return list.front()->getSequenceNumber();
if(segmentBase->getSequenceNumber() + SAFETY_BUFFERING_EDGE_OFFSET <= start)
start -= SAFETY_BUFFERING_EDGE_OFFSET;
else
start = segmentBase->getSequenceNumber();
return start;
}
return std::numeric_limits<uint64_t>::max();
}
vlc_tick_t DefaultBufferingLogic::getBufferingAmount(const AbstractPlaylist *p) const
{
const vlc_tick_t i_min_buffering = getMinBuffering(p);
const vlc_tick_t i_max_buffering = getMaxBuffering(p);
/* Get buffering amount */
vlc_tick_t i_buffering = i_max_buffering;
if(p->suggestedPresentationDelay.Get() &&
p->suggestedPresentationDelay.Get() > i_min_buffering)
{
i_buffering = p->suggestedPresentationDelay.Get();
}
if(p->timeShiftBufferDepth.Get())
i_buffering = std::min(i_buffering, p->timeShiftBufferDepth.Get());
return i_buffering;
}
bool DefaultBufferingLogic::isLowLatency(const AbstractPlaylist *p) const
{
if(userLowLatency.isSet())
return userLowLatency.value();
return p->isLowLatency();
}

81
modules/demux/adaptive/logic/BufferingLogic.hpp

@ -0,0 +1,81 @@
/*
* BufferingLogic.hpp
*****************************************************************************
* Copyright (C) 2014 - 2020 VideoLabs, VideoLAN and VLC authors
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef BUFFERINGLOGIC_HPP
#define BUFFERINGLOGIC_HPP
#include <vector>
#include <vlc_common.h>
#include "../tools/Properties.hpp"
namespace adaptive
{
namespace playlist
{
class BaseRepresentation;
class AbstractPlaylist;
}
namespace logic
{
using namespace playlist;
class AbstractBufferingLogic
{
public:
AbstractBufferingLogic();
virtual ~AbstractBufferingLogic() {}
virtual uint64_t getStartSegmentNumber(BaseRepresentation *) const = 0;
virtual vlc_tick_t getMinBuffering(const AbstractPlaylist *) const = 0;
virtual vlc_tick_t getMaxBuffering(const AbstractPlaylist *) const = 0;
void setUserMinBuffering(vlc_tick_t);
void setUserMaxBuffering(vlc_tick_t);
void setUserLiveDelay(vlc_tick_t);
void setLowDelay(bool);
protected:
static const vlc_tick_t BUFFERING_LOWEST_LIMIT;
static const vlc_tick_t DEFAULT_MIN_BUFFERING;
static const vlc_tick_t DEFAULT_MAX_BUFFERING;
static const vlc_tick_t DEFAULT_LIVE_BUFFERING;
vlc_tick_t userMinBuffering;
vlc_tick_t userMaxBuffering;
vlc_tick_t userLiveDelay;
Undef<bool> userLowLatency;
};
class DefaultBufferingLogic : public AbstractBufferingLogic
{
public:
DefaultBufferingLogic();
virtual ~DefaultBufferingLogic() {}
virtual uint64_t getStartSegmentNumber(BaseRepresentation *) const; /* impl */
virtual vlc_tick_t getMinBuffering(const AbstractPlaylist *) const; /* impl */
virtual vlc_tick_t getMaxBuffering(const AbstractPlaylist *) const; /* impl */
protected:
vlc_tick_t getBufferingAmount(const AbstractPlaylist *) const;
uint64_t getLiveStartSegmentNumber(BaseRepresentation *) const;
bool isLowLatency(const AbstractPlaylist *) const;
};
}
}
#endif
Loading…
Cancel
Save