/*****************************************************************************
* SegmentTimeline.cpp: Implement the SegmentTimeline tag.
*****************************************************************************
* Copyright (C) 1998-2007 VLC authors and VideoLAN
*
* Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
*
* 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 "SegmentTimeline.h"
#include "SegmentBaseType.hpp"
#include "SegmentInformation.hpp"
#include <algorithm>
#include <sstream>
#include <limits>
using namespace adaptive;
using namespace adaptive::playlist;
SegmentTimeline::SegmentTimeline(AbstractMultipleSegmentBaseType *parent_)
: AttrsNode(Type::Timeline, parent_)
{
totalLength = 0;
parent = parent_;
}
SegmentTimeline::~SegmentTimeline()
{
std::list<Element *>::iterator it;
for(it = elements.begin(); it != elements.end(); ++it)
delete *it;
}
void SegmentTimeline::addElement(uint64_t number, stime_t d, uint64_t r, stime_t t)
{
Element *element = new (std::nothrow) Element(number, d, r, t);
if(element)
{
if(!elements.empty() && !t)
{
const Element *el = elements.back();
element->t = el->t + (el->d * (el->r + 1));
}
elements.push_back(element);
totalLength += (d * (r + 1));
}
}
stime_t SegmentTimeline::getMinAheadScaledTime(uint64_t number) const
{
stime_t totalscaledtime = 0;
if(!elements.size() ||
minElementNumber() > number ||
maxElementNumber() < number)
return 0;
std::list<Element *>::const_reverse_iterator it;
for(it = elements.rbegin(); it != elements.rend(); ++it)
{
const Element *el = *it;
if(number > el->number + el->r)
break;
else if(number < el->number)
totalscaledtime += (el->d * (el->r + 1));
else /* within repeat range */
totalscaledtime += el->d * (el->number + el->r - number);
}
return totalscaledtime;
}
uint64_t SegmentTimeline::getElementNumberByScaledPlaybackTime(stime_t scaled) const
{
const Element *prevel = nullptr;
std::list<Element *>::const_iterator it;
if(!elements.size())
return 0;
for(it = elements.begin(); it != elements.end(); ++it)
{
const Element *el = *it;
if(scaled >= el->t)
{
if((uint64_t)scaled < el->t + (el->d * (el->r + 1)))
return el->number + (scaled - el->t) / el->d;
}
/* might have been discontinuity */
else
{
if(prevel) /* > prev but < current */
return prevel->number + prevel->r;
else /* << first of the list */
return el->number;
}
prevel = el;
}
/* time is >> any of the list */
return prevel->number + prevel->r;
}
bool SegmentTimeline::getScaledPlaybackTimeDurationBySegmentNumber(uint64_t number,
stime_t *time, stime_t *duration) const
{
std::list<Element *>::const_iterator it;
for(it = elements.begin(); it != elements.end(); ++it)
{
const Element *el = *it;
if(number >= el->number)
{
if(number <= el->number + el->r)
{
*time = el->t + el->d * (number - el->number);
*duration = el->d;
return true;
}
}
}
return false;
}
stime_t SegmentTimeline::getScaledPlaybackTimeByElementNumber(uint64_t number) const
{
stime_t time = 0, duration = 0;
(void) getScaledPlaybackTimeDurationBySegmentNumber(number, &time, &duration);
return time;
}
stime_t SegmentTimeline::getTotalLength() const
{
return totalLength;
}
uint64_t SegmentTimeline::maxElementNumber() const
{
if(elements.empty())
return 0;
const Element *e = elements.back();
return e->number + e->r;
}
uint64_t SegmentTimeline::minElementNumber() const
{
if(elements.empty())
return 0;
return elements.front()->number;
}
uint64_t SegmentTimeline::getElementIndexBySequence(uint64_t number) const
{
std::list<Element *>::const_iterator it;
for(it = elements.begin(); it != elements.end(); ++it)
{
const Element *el = *it;
if(number >= el->number)
{
if(number <= el->number + el->r)
return std::distance(elements.begin(), it);
}
}
return std::numeric_limits<uint64_t>::max();
}
void SegmentTimeline::pruneByPlaybackTime(vlc_tick_t time)
{
const Timescale timescale = inheritTimescale();
uint64_t num = getElementNumberByScaledPlaybackTime(timescale.ToScaled(time));
pruneBySequenceNumber(num);
}
size_t SegmentTimeline::pruneBySequenceNumber(uint64_t number)
{
size_t prunednow = 0;
while(elements.size())
{
Element *el = elements.front();
if(el->number >= number)
{
break;
}
else if(el->number + el->r >= number)
{
uint64_t count = number - el->number;
el->number += count;
el->t += count * el->d;
el->r -= count;
prunednow += count;
totalLength -= count * el->d;
break;
}
else
{
prunednow += el->r + 1;
elements.pop_front();
totalLength -= (el->d * (el->r + 1));
delete el;
}
}
return prunednow;
}
void SegmentTimeline::updateWith(SegmentTimeline &other)
{
if(elements.empty())
{
while(other.elements.size())
{
elements.push_back(other.elements.front());
other.elements.pop_front();
}
return;
}
Element *last = elements.back();
while(other.elements.size())
{
Element *el = other.elements.front();
other.elements.pop_front();
if(last->contains(el->t)) /* Same element, but prev could have been middle of repeat */
{
const uint64_t count = (el->t - last->t) / last->d;
totalLength -= (last->d * (last->r + 1));
last->r = std::max(last->r, el->r + count);
totalLength += (last->d * (last->r + 1));
delete el;
}
else if(el->t < last->t)
{
delete el;
}
else /* Did not exist in previous list */
{
totalLength += (el->d * (el->r + 1));
elements.push_back(el);
el->number = last->number + last->r + 1;
last = el;
}
}
}
void SegmentTimeline::debug(vlc_object_t *obj, int indent) const
{
std::stringstream ss;
ss << std::string(indent, ' ') << "Timeline";
msg_Dbg(obj, "%s", ss.str().c_str());
std::list<Element *>::const_iterator it;
for(it = elements.begin(); it != elements.end(); ++it)
(*it)->debug(obj, indent + 1);
}
SegmentTimeline::Element::Element(uint64_t number_, stime_t d_, uint64_t r_, stime_t t_)
{
number = number_;
d = d_;
t = t_;
r = r_;
}
bool SegmentTimeline::Element::contains(stime_t time) const
{
if(time >= t && time < t + (stime_t)(r + 1) * d)
return true;
return false;
}
void SegmentTimeline::Element::debug(vlc_object_t *obj, int indent) const
{
std::stringstream ss;
ss.imbue(std::locale("C"));
ss << std::string(indent + 1, ' ') << "Element #" << number
<< " d=" << d << " r=" << r << " @t=" << t;
msg_Dbg(obj, "%s", ss.str().c_str());
}
¤ Dauer der Verarbeitung: 0.4 Sekunden
¤
*© Formatika GbR, Deutschland