Commit 85b636e7 authored by François Cartegnie's avatar François Cartegnie 🤞

demux: adaptative: handle inconsistent numbering (hls)

HLS can have inconsistent numbering through playlists.
In that case, a datetime is used to provide date and time reference
for the first segment.
We need then to convert our segment numbers using that
reference to be able to switch to the correct segment
number.

Exemple:
http://b028.wpc.azureedge.net/80B028/Samples/0e8848ca-1db7-41a3-8867-fe911144c045/d34d8807-5597-47a1-8408-52ec5fc99027.ism/Manifest(format=m3u8-aapl-v3)
parent 0fa3d858
......@@ -54,7 +54,7 @@ SegmentTracker::SegmentTracker(AbstractAdaptationLogic *logic_, BaseAdaptationSe
initializing = true;
index_sent = false;
init_sent = false;
prevRepresentation = NULL;
curRepresentation = NULL;
setAdaptationLogic(logic_);
adaptationSet = adaptSet;
format = StreamFormat::UNSUPPORTED;
......@@ -73,8 +73,8 @@ void SegmentTracker::setAdaptationLogic(AbstractAdaptationLogic *logic_)
void SegmentTracker::reset()
{
notify(SegmentTrackerEvent(prevRepresentation, NULL));
prevRepresentation = NULL;
notify(SegmentTrackerEvent(curRepresentation, NULL));
curRepresentation = NULL;
init_sent = false;
index_sent = false;
initializing = true;
......@@ -83,7 +83,7 @@ void SegmentTracker::reset()
SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed, HTTPConnectionManager *connManager)
{
BaseRepresentation *rep = NULL;
BaseRepresentation *rep = NULL, *prevRep = NULL;
ISegment *segment;
if(!adaptationSet)
......@@ -92,26 +92,27 @@ SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed, HTTPConnectionM
/* Ensure we don't keep chaining init/index without data */
if( initializing )
{
if( prevRepresentation )
if( curRepresentation )
switch_allowed = false;
else
switch_allowed = true;
}
if( !switch_allowed ||
(prevRepresentation && prevRepresentation->getSwitchPolicy() == SegmentInformation::SWITCH_UNAVAILABLE) )
rep = prevRepresentation;
(curRepresentation && curRepresentation->getSwitchPolicy() == SegmentInformation::SWITCH_UNAVAILABLE) )
rep = curRepresentation;
else
rep = logic->getNextRepresentation(adaptationSet, prevRepresentation);
rep = logic->getNextRepresentation(adaptationSet, curRepresentation);
if ( rep == NULL )
return NULL;
if(rep != prevRepresentation)
if(rep != curRepresentation)
{
notify(SegmentTrackerEvent(prevRepresentation, rep));
prevRepresentation = rep;
notify(SegmentTrackerEvent(curRepresentation, rep));
prevRep = curRepresentation;
curRepresentation = rep;
init_sent = false;
index_sent = false;
initializing = true;
......@@ -119,7 +120,16 @@ SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed, HTTPConnectionM
/* Ensure ephemere content is updated/loaded */
if(rep->needsUpdate())
updateSelected();
rep->runLocalUpdates(getSegmentStart(), count, false);
if(prevRep && !rep->consistentSegmentNumber())
{
/* Convert our segment number */
count = rep->translateSegmentNumber(count, prevRep);
}
if(!rep->consistentSegmentNumber())
curRepresentation->pruneBySegmentNumber(count);
if(!init_sent)
{
......@@ -171,7 +181,7 @@ SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed, HTTPConnectionM
bool SegmentTracker::setPositionByTime(mtime_t time, bool restarted, bool tryonly)
{
uint64_t segnumber;
BaseRepresentation *rep = prevRepresentation;
BaseRepresentation *rep = curRepresentation;
if(!rep)
rep = logic->getNextRepresentation(adaptationSet, NULL);
......@@ -198,8 +208,8 @@ void SegmentTracker::setPositionByNumber(uint64_t segnumber, bool restarted)
mtime_t SegmentTracker::getSegmentStart() const
{
if(prevRepresentation)
return prevRepresentation->getPlaybackTimeBySegmentNumber(count);
if(curRepresentation)
return curRepresentation->getPlaybackTimeBySegmentNumber(count);
else
return 0;
}
......@@ -218,8 +228,8 @@ void SegmentTracker::pruneFromCurrent()
void SegmentTracker::updateSelected()
{
if(prevRepresentation)
prevRepresentation->runLocalUpdates(getSegmentStart(), count);
if(curRepresentation)
curRepresentation->runLocalUpdates(getSegmentStart(), count, true);
}
void SegmentTracker::notify(const SegmentTrackerEvent &event)
......
......@@ -113,7 +113,7 @@ namespace adaptative
StreamFormat format;
AbstractAdaptationLogic *logic;
BaseAdaptationSet *adaptationSet;
BaseRepresentation *prevRepresentation;
BaseRepresentation *curRepresentation;
std::list<SegmentTrackerListenerInterface *> listeners;
};
}
......
......@@ -40,6 +40,7 @@ BaseRepresentation::BaseRepresentation( BaseAdaptationSet *set ) :
adaptationSet ( set ),
bandwidth (0)
{
b_consistent = true;
}
BaseRepresentation::~BaseRepresentation ()
......@@ -76,6 +77,11 @@ bool BaseRepresentation::needsUpdate() const
return false;
}
bool BaseRepresentation::consistentSegmentNumber() const
{
return b_consistent;
}
void BaseRepresentation::debug(vlc_object_t *obj, int indent) const
{
std::string text(indent, ' ');
......
......@@ -59,6 +59,7 @@ namespace adaptative
const std::list<std::string> & getCodecs () const;
void addCodec (const std::string &);
virtual bool needsUpdate() const;
bool consistentSegmentNumber () const;
virtual void debug (vlc_object_t *,int = 0) const;
......@@ -71,6 +72,7 @@ namespace adaptative
BaseAdaptationSet *adaptationSet;
uint64_t bandwidth;
std::list<std::string> codecs;
bool b_consistent;
};
}
}
......
......@@ -398,7 +398,14 @@ void SegmentInformation::pruneBySegmentNumber(uint64_t num)
childs.at(i)->pruneBySegmentNumber(num);
}
void SegmentInformation::runLocalUpdates(mtime_t, uint64_t)
uint64_t SegmentInformation::translateSegmentNumber(uint64_t num, const SegmentInformation *from) const
{
mtime_t time = from->getPlaybackTimeBySegmentNumber(num);
getSegmentNumberByTime(time, &num);
return num;
}
void SegmentInformation::runLocalUpdates(mtime_t, uint64_t, bool)
{
}
......
......@@ -86,7 +86,8 @@ namespace adaptative
virtual void mergeWith(SegmentInformation *, mtime_t);
virtual void mergeWithTimeline(SegmentTimeline *); /* ! don't use with global merge */
virtual void pruneBySegmentNumber(uint64_t);
virtual void runLocalUpdates(mtime_t, uint64_t);
virtual uint64_t translateSegmentNumber(uint64_t, const SegmentInformation *) const;
virtual void runLocalUpdates(mtime_t, uint64_t, bool);
protected:
std::size_t getAllSegments(std::vector<ISegment *> &) const;
......
......@@ -38,6 +38,7 @@ HLSSegment::HLSSegment( ICanonicalUrl *parent, uint64_t seq ) :
Segment( parent )
{
setSequenceNumber(seq);
utcTime = 0;
#ifdef HAVE_GCRYPT
ctx = NULL;
#endif
......@@ -122,6 +123,11 @@ void HLSSegment::onChunkDownload(block_t **pp_block, SegmentChunk *chunk, BaseRe
}
}
mtime_t HLSSegment::getUTCTime() const
{
return utcTime;
}
void HLSSegment::setEncryption(SegmentEncryption &enc)
{
encryption = enc;
......
......@@ -52,14 +52,18 @@ namespace hls
class HLSSegment : public Segment
{
friend class M3U8Parser;
public:
HLSSegment( ICanonicalUrl *parent, uint64_t sequence );
virtual ~HLSSegment();
void setEncryption(SegmentEncryption &);
mtime_t getUTCTime() const;
void debug(vlc_object_t *, int) const; /* reimpl */
virtual int compare(ISegment *) const; /* reimpl */
protected:
mtime_t utcTime;
virtual void onChunkDownload(block_t **, SegmentChunk *, BaseRepresentation *); /* reimpl */
SegmentEncryption encryption;
......
......@@ -29,6 +29,7 @@
#include "../adaptative/playlist/SegmentList.h"
#include "../adaptative/tools/Retrieve.hpp"
#include "../adaptative/tools/Helper.h"
#include "../adaptative/tools/Conversions.hpp"
#include "M3U8.hpp"
#include "Tags.hpp"
......@@ -214,6 +215,7 @@ void M3U8Parser::parseSegments(vlc_object_t *p_obj, Representation *rep, const s
mtime_t totalduration = 0;
mtime_t nzStartTime = 0;
mtime_t absReferenceTime = VLC_TS_INVALID;
uint64_t sequenceNumber = 0;
bool discontinuity = false;
std::size_t prevbyterangeoffset = 0;
......@@ -267,6 +269,12 @@ void M3U8Parser::parseSegments(vlc_object_t *p_obj, Representation *rep, const s
segment->startTime.Set(nzStartTime * rep->timescale.Get() / CLOCK_FREQ);
nzStartTime += nzDuration;
totalduration += nzDuration;
if(absReferenceTime > VLC_TS_INVALID)
{
segment->utcTime = absReferenceTime;
absReferenceTime += nzDuration;
}
}
ctx_extinf = NULL;
}
......@@ -302,6 +310,12 @@ void M3U8Parser::parseSegments(vlc_object_t *p_obj, Representation *rep, const s
ctx_byterange = static_cast<const SingleValueTag *>(tag);
break;
case SingleValueTag::EXTXPROGRAMDATETIME:
rep->b_consistent = false;
absReferenceTime = VLC_TS_0 +
UTCTime(static_cast<const SingleValueTag *>(tag)->getValue().value).mtime();
break;
case AttributesTag::EXTXKEY:
{
const AttributesTag *keytag = static_cast<const AttributesTag *>(tag);
......
......@@ -26,6 +26,7 @@
#include "Representation.hpp"
#include "M3U8.hpp"
#include "Parser.hpp"
#include "HLSSegment.hpp"
#include "../adaptative/playlist/BasePeriod.h"
#include "../adaptative/playlist/BaseAdaptationSet.h"
#include "../adaptative/playlist/SegmentList.h"
......@@ -101,7 +102,7 @@ bool Representation::needsUpdate() const
return true;
}
void Representation::runLocalUpdates(mtime_t /*currentplaybacktime*/, uint64_t number)
void Representation::runLocalUpdates(mtime_t, uint64_t number, bool prune)
{
const time_t now = time(NULL);
const AbstractPlaylist *playlist = getPlaylist();
......@@ -111,7 +112,8 @@ void Representation::runLocalUpdates(mtime_t /*currentplaybacktime*/, uint64_t n
parser.appendSegmentsFromPlaylistURI(playlist->getVLCObject(), this);
b_loaded = true;
pruneBySegmentNumber(number);
if(prune)
pruneBySegmentNumber(number);
/* Compute new update time */
mtime_t mininterval = 0;
......@@ -143,3 +145,31 @@ void Representation::getDurationsRange(mtime_t *min, mtime_t *max) const
return;
BaseRepresentation::getDurationsRange(min, max);
}
uint64_t Representation::translateSegmentNumber(uint64_t num, const SegmentInformation *from) const
{
if(consistentSegmentNumber())
return num;
ISegment *fromSeg = from->getSegment(SegmentInfoType::INFOTYPE_MEDIA, num);
HLSSegment *fromHlsSeg = dynamic_cast<HLSSegment *>(fromSeg);
if(!fromHlsSeg)
return 1;
const mtime_t utcTime = fromHlsSeg->getUTCTime();
std::vector<ISegment *> list;
std::vector<ISegment *>::const_iterator it;
getSegments(SegmentInfoType::INFOTYPE_MEDIA, list);
for(it=list.begin(); it != list.end(); ++it)
{
const HLSSegment *hlsSeg = dynamic_cast<HLSSegment *>(*it);
if(hlsSeg)
{
if (hlsSeg->getUTCTime() <= utcTime)
num = hlsSeg->getSequenceNumber();
else
return num;
}
}
return 1;
}
......@@ -49,8 +49,9 @@ namespace hls
bool initialized() const;
virtual bool needsUpdate() const; /* reimpl */
virtual void debug(vlc_object_t *, int) const; /* reimpl */
virtual void runLocalUpdates(mtime_t, uint64_t); /* reimpl */
virtual void runLocalUpdates(mtime_t, uint64_t, bool); /* reimpl */
virtual void getDurationsRange(mtime_t *, mtime_t *) const; /* reimpl */
virtual uint64_t translateSegmentNumber(uint64_t, const SegmentInformation *) const; /* reimpl */
private:
StreamFormat streamFormat;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment