You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

365 lines
9.8 KiB

/*
* SegmentTracker.cpp
*****************************************************************************
* Copyright (C) 2014 - 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 "SegmentTracker.hpp"
#include "playlist/AbstractPlaylist.hpp"
#include "playlist/BaseRepresentation.h"
#include "playlist/BaseAdaptationSet.h"
#include "playlist/Segment.h"
#include "playlist/SegmentChunk.hpp"
#include "logic/AbstractAdaptationLogic.h"
using namespace adaptive;
using namespace adaptive::logic;
using namespace adaptive::playlist;
SegmentTrackerEvent::SegmentTrackerEvent(SegmentChunk *s)
{
type = DISCONTINUITY;
u.discontinuity.sc = s;
}
SegmentTrackerEvent::SegmentTrackerEvent(BaseRepresentation *prev, BaseRepresentation *next)
{
type = SWITCHING;
u.switching.prev = prev;
u.switching.next = next;
}
SegmentTrackerEvent::SegmentTrackerEvent(const StreamFormat *fmt)
{
type = FORMATCHANGE;
u.format.f = fmt;
}
SegmentTrackerEvent::SegmentTrackerEvent(const ID &id, bool enabled)
{
type = BUFFERING_STATE;
u.buffering.enabled = enabled;
u.buffering.id = &id;
}
SegmentTrackerEvent::SegmentTrackerEvent(const ID &id, mtime_t min, mtime_t current, mtime_t target)
{
type = BUFFERING_LEVEL_CHANGE;
u.buffering_level.minimum = min;
u.buffering_level.current = current;
u.buffering_level.target = target;
u.buffering.id = &id;
}
SegmentTrackerEvent::SegmentTrackerEvent(const ID &id, mtime_t duration)
{
type = SEGMENT_CHANGE;
u.segment.duration = duration;
u.segment.id = &id;
}
SegmentTracker::SegmentTracker(AbstractAdaptationLogic *logic_, BaseAdaptationSet *adaptSet)
{
first = true;
curNumber = next = 0;
initializing = true;
index_sent = false;
init_sent = false;
curRepresentation = NULL;
setAdaptationLogic(logic_);
adaptationSet = adaptSet;
format = StreamFormat::UNSUPPORTED;
}
SegmentTracker::~SegmentTracker()
{
reset();
}
void SegmentTracker::setAdaptationLogic(AbstractAdaptationLogic *logic_)
{
logic = logic_;
registerListener(logic);
}
StreamFormat SegmentTracker::getCurrentFormat() const
{
BaseRepresentation *rep = curRepresentation;
if(!rep)
rep = logic->getNextRepresentation(adaptationSet, NULL);
if(rep)
{
/* Ensure ephemere content is updated/loaded */
if(rep->needsUpdate())
(void) rep->runLocalUpdates(0, curNumber, false);
return rep->getStreamFormat();
}
return StreamFormat();
}
bool SegmentTracker::segmentsListReady() const
{
BaseRepresentation *rep = curRepresentation;
if(!rep)
rep = logic->getNextRepresentation(adaptationSet, NULL);
if(rep && rep->getPlaylist()->isLive())
return rep->getMinAheadTime(curNumber) > 0;
return true;
}
void SegmentTracker::reset()
{
notify(SegmentTrackerEvent(curRepresentation, NULL));
curRepresentation = NULL;
init_sent = false;
index_sent = false;
initializing = true;
format = StreamFormat::UNSUPPORTED;
}
SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed,
AbstractConnectionManager *connManager)
{
BaseRepresentation *rep = NULL, *prevRep = NULL;
ISegment *segment;
if(!adaptationSet)
return NULL;
/* Ensure we don't keep chaining init/index without data */
if( initializing )
{
if( curRepresentation )
switch_allowed = false;
else
switch_allowed = true;
}
if( !switch_allowed ||
(curRepresentation && curRepresentation->getSwitchPolicy() == SegmentInformation::SWITCH_UNAVAILABLE) )
rep = curRepresentation;
else
rep = logic->getNextRepresentation(adaptationSet, curRepresentation);
if ( rep == NULL )
return NULL;
if(rep != curRepresentation)
{
notify(SegmentTrackerEvent(curRepresentation, rep));
prevRep = curRepresentation;
curRepresentation = rep;
init_sent = false;
index_sent = false;
initializing = true;
}
bool b_updated = false;
/* Ensure ephemere content is updated/loaded */
if(rep->needsUpdate())
b_updated = rep->runLocalUpdates(getPlaybackTime(), curNumber, false);
if(prevRep && !rep->consistentSegmentNumber())
{
/* Convert our segment number */
next = rep->translateSegmentNumber(next, prevRep);
}
else if(first && rep->getPlaylist()->isLive())
{
next = rep->getLiveStartSegmentNumber(next);
first = false;
}
if(b_updated)
{
if(!rep->consistentSegmentNumber())
curRepresentation->pruneBySegmentNumber(curNumber);
curRepresentation->scheduleNextUpdate(next);
}
if(rep->getStreamFormat() != format)
{
/* Initial format ? */
if(format == StreamFormat(StreamFormat::UNSUPPORTED))
{
format = rep->getStreamFormat();
}
else
{
format = rep->getStreamFormat();
notify(SegmentTrackerEvent(&format)); /* Notify new demux format */
return NULL; /* Force current demux to end */
}
}
if(format == StreamFormat(StreamFormat::UNSUPPORTED))
{
return NULL; /* Can't return chunk because no demux will be created */
}
if(!init_sent)
{
init_sent = true;
segment = rep->getSegment(BaseRepresentation::INFOTYPE_INIT);
if(segment)
return segment->toChunk(next, rep, connManager);
}
if(!index_sent)
{
index_sent = true;
segment = rep->getSegment(BaseRepresentation::INFOTYPE_INDEX);
if(segment)
return segment->toChunk(next, rep, connManager);
}
bool b_gap = false;
segment = rep->getNextSegment(BaseRepresentation::INFOTYPE_MEDIA, next, &next, &b_gap);
if(!segment)
{
reset();
return NULL;
}
if(initializing)
{
b_gap = false;
/* stop initializing after 1st chunk */
initializing = false;
}
SegmentChunk *chunk = segment->toChunk(next, rep, connManager);
/* Notify new segment length for stats / logic */
if(chunk)
{
const Timescale timescale = rep->inheritTimescale();
notify(SegmentTrackerEvent(rep->getAdaptationSet()->getID(),
timescale.ToTime(segment->duration.Get())));
}
/* We need to check segment/chunk format changes, as we can't rely on representation's (HLS)*/
if(chunk && format != chunk->getStreamFormat())
{
format = chunk->getStreamFormat();
notify(SegmentTrackerEvent(&format));
}
/* Handle both implicit and explicit discontinuities */
if( (b_gap && next) || (chunk && chunk->discontinuity) )
{
notify(SegmentTrackerEvent(chunk));
}
if(chunk)
{
curNumber = next;
next++;
}
return chunk;
}
bool SegmentTracker::setPositionByTime(mtime_t time, bool restarted, bool tryonly)
{
uint64_t segnumber;
BaseRepresentation *rep = curRepresentation;
if(!rep)
rep = logic->getNextRepresentation(adaptationSet, NULL);
if(rep &&
rep->getSegmentNumberByTime(time, &segnumber))
{
if(!tryonly)
setPositionByNumber(segnumber, restarted);
return true;
}
return false;
}
void SegmentTracker::setPositionByNumber(uint64_t segnumber, bool restarted)
{
if(restarted)
{
initializing = true;
index_sent = false;
init_sent = false;
}
curNumber = next = segnumber;
}
mtime_t SegmentTracker::getPlaybackTime() const
{
mtime_t time, duration;
BaseRepresentation *rep = curRepresentation;
if(!rep)
rep = logic->getNextRepresentation(adaptationSet, NULL);
if(rep &&
rep->getPlaybackTimeDurationBySegmentNumber(next, &time, &duration))
{
return time;
}
return 0;
}
mtime_t SegmentTracker::getMinAheadTime() const
{
BaseRepresentation *rep = curRepresentation;
if(!rep)
rep = logic->getNextRepresentation(adaptationSet, NULL);
if(rep)
return rep->getMinAheadTime(curNumber);
return 0;
}
void SegmentTracker::notifyBufferingState(bool enabled) const
{
notify(SegmentTrackerEvent(adaptationSet->getID(), enabled));
}
void SegmentTracker::notifyBufferingLevel(mtime_t min, mtime_t current, mtime_t target) const
{
notify(SegmentTrackerEvent(adaptationSet->getID(), min, current, target));
}
void SegmentTracker::registerListener(SegmentTrackerListenerInterface *listener)
{
listeners.push_back(listener);
}
void SegmentTracker::updateSelected()
{
if(curRepresentation && curRepresentation->needsUpdate())
{
curRepresentation->runLocalUpdates(getPlaybackTime(), curNumber, true);
curRepresentation->scheduleNextUpdate(curNumber);
}
}
void SegmentTracker::notify(const SegmentTrackerEvent &event) const
{
std::list<SegmentTrackerListenerInterface *>::const_iterator it;
for(it=listeners.begin();it != listeners.end(); ++it)
(*it)->trackerEvent(event);
}