Commit 85b636e7 authored by Francois Cartegnie's avatar Francois 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 ...@@ -54,7 +54,7 @@ SegmentTracker::SegmentTracker(AbstractAdaptationLogic *logic_, BaseAdaptationSe
initializing = true; initializing = true;
index_sent = false; index_sent = false;
init_sent = false; init_sent = false;
prevRepresentation = NULL; curRepresentation = NULL;
setAdaptationLogic(logic_); setAdaptationLogic(logic_);
adaptationSet = adaptSet; adaptationSet = adaptSet;
format = StreamFormat::UNSUPPORTED; format = StreamFormat::UNSUPPORTED;
...@@ -73,8 +73,8 @@ void SegmentTracker::setAdaptationLogic(AbstractAdaptationLogic *logic_) ...@@ -73,8 +73,8 @@ void SegmentTracker::setAdaptationLogic(AbstractAdaptationLogic *logic_)
void SegmentTracker::reset() void SegmentTracker::reset()
{ {
notify(SegmentTrackerEvent(prevRepresentation, NULL)); notify(SegmentTrackerEvent(curRepresentation, NULL));
prevRepresentation = NULL; curRepresentation = NULL;
init_sent = false; init_sent = false;
index_sent = false; index_sent = false;
initializing = true; initializing = true;
...@@ -83,7 +83,7 @@ void SegmentTracker::reset() ...@@ -83,7 +83,7 @@ void SegmentTracker::reset()
SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed, HTTPConnectionManager *connManager) SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed, HTTPConnectionManager *connManager)
{ {
BaseRepresentation *rep = NULL; BaseRepresentation *rep = NULL, *prevRep = NULL;
ISegment *segment; ISegment *segment;
if(!adaptationSet) if(!adaptationSet)
...@@ -92,26 +92,27 @@ SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed, HTTPConnectionM ...@@ -92,26 +92,27 @@ SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed, HTTPConnectionM
/* Ensure we don't keep chaining init/index without data */ /* Ensure we don't keep chaining init/index without data */
if( initializing ) if( initializing )
{ {
if( prevRepresentation ) if( curRepresentation )
switch_allowed = false; switch_allowed = false;
else else
switch_allowed = true; switch_allowed = true;
} }
if( !switch_allowed || if( !switch_allowed ||
(prevRepresentation && prevRepresentation->getSwitchPolicy() == SegmentInformation::SWITCH_UNAVAILABLE) ) (curRepresentation && curRepresentation->getSwitchPolicy() == SegmentInformation::SWITCH_UNAVAILABLE) )
rep = prevRepresentation; rep = curRepresentation;
else else
rep = logic->getNextRepresentation(adaptationSet, prevRepresentation); rep = logic->getNextRepresentation(adaptationSet, curRepresentation);
if ( rep == NULL ) if ( rep == NULL )
return NULL; return NULL;
if(rep != prevRepresentation) if(rep != curRepresentation)
{ {
notify(SegmentTrackerEvent(prevRepresentation, rep)); notify(SegmentTrackerEvent(curRepresentation, rep));
prevRepresentation = rep; prevRep = curRepresentation;
curRepresentation = rep;
init_sent = false; init_sent = false;
index_sent = false; index_sent = false;
initializing = true; initializing = true;
...@@ -119,7 +120,16 @@ SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed, HTTPConnectionM ...@@ -119,7 +120,16 @@ SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed, HTTPConnectionM
/* Ensure ephemere content is updated/loaded */ /* Ensure ephemere content is updated/loaded */
if(rep->needsUpdate()) 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) if(!init_sent)
{ {
...@@ -171,7 +181,7 @@ SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed, HTTPConnectionM ...@@ -171,7 +181,7 @@ SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed, HTTPConnectionM
bool SegmentTracker::setPositionByTime(mtime_t time, bool restarted, bool tryonly) bool SegmentTracker::setPositionByTime(mtime_t time, bool restarted, bool tryonly)
{ {
uint64_t segnumber; uint64_t segnumber;
BaseRepresentation *rep = prevRepresentation; BaseRepresentation *rep = curRepresentation;
if(!rep) if(!rep)
rep = logic->getNextRepresentation(adaptationSet, NULL); rep = logic->getNextRepresentation(adaptationSet, NULL);
...@@ -198,8 +208,8 @@ void SegmentTracker::setPositionByNumber(uint64_t segnumber, bool restarted) ...@@ -198,8 +208,8 @@ void SegmentTracker::setPositionByNumber(uint64_t segnumber, bool restarted)
mtime_t SegmentTracker::getSegmentStart() const mtime_t SegmentTracker::getSegmentStart() const
{ {
if(prevRepresentation) if(curRepresentation)
return prevRepresentation->getPlaybackTimeBySegmentNumber(count); return curRepresentation->getPlaybackTimeBySegmentNumber(count);
else else
return 0; return 0;
} }
...@@ -218,8 +228,8 @@ void SegmentTracker::pruneFromCurrent() ...@@ -218,8 +228,8 @@ void SegmentTracker::pruneFromCurrent()
void SegmentTracker::updateSelected() void SegmentTracker::updateSelected()
{ {
if(prevRepresentation) if(curRepresentation)
prevRepresentation->runLocalUpdates(getSegmentStart(), count); curRepresentation->runLocalUpdates(getSegmentStart(), count, true);
} }
void SegmentTracker::notify(const SegmentTrackerEvent &event) void SegmentTracker::notify(const SegmentTrackerEvent &event)
......
...@@ -113,7 +113,7 @@ namespace adaptative ...@@ -113,7 +113,7 @@ namespace adaptative
StreamFormat format; StreamFormat format;
AbstractAdaptationLogic *logic; AbstractAdaptationLogic *logic;
BaseAdaptationSet *adaptationSet; BaseAdaptationSet *adaptationSet;
BaseRepresentation *prevRepresentation; BaseRepresentation *curRepresentation;
std::list<SegmentTrackerListenerInterface *> listeners; std::list<SegmentTrackerListenerInterface *> listeners;
}; };
} }
......
...@@ -40,6 +40,7 @@ BaseRepresentation::BaseRepresentation( BaseAdaptationSet *set ) : ...@@ -40,6 +40,7 @@ BaseRepresentation::BaseRepresentation( BaseAdaptationSet *set ) :
adaptationSet ( set ), adaptationSet ( set ),
bandwidth (0) bandwidth (0)
{ {
b_consistent = true;
} }
BaseRepresentation::~BaseRepresentation () BaseRepresentation::~BaseRepresentation ()
...@@ -76,6 +77,11 @@ bool BaseRepresentation::needsUpdate() const ...@@ -76,6 +77,11 @@ bool BaseRepresentation::needsUpdate() const
return false; return false;
} }
bool BaseRepresentation::consistentSegmentNumber() const
{
return b_consistent;
}
void BaseRepresentation::debug(vlc_object_t *obj, int indent) const void BaseRepresentation::debug(vlc_object_t *obj, int indent) const
{ {
std::string text(indent, ' '); std::string text(indent, ' ');
......
...@@ -59,6 +59,7 @@ namespace adaptative ...@@ -59,6 +59,7 @@ namespace adaptative
const std::list<std::string> & getCodecs () const; const std::list<std::string> & getCodecs () const;
void addCodec (const std::string &); void addCodec (const std::string &);
virtual bool needsUpdate() const; virtual bool needsUpdate() const;
bool consistentSegmentNumber () const;
virtual void debug (vlc_object_t *,int = 0) const; virtual void debug (vlc_object_t *,int = 0) const;
...@@ -71,6 +72,7 @@ namespace adaptative ...@@ -71,6 +72,7 @@ namespace adaptative
BaseAdaptationSet *adaptationSet; BaseAdaptationSet *adaptationSet;
uint64_t bandwidth; uint64_t bandwidth;
std::list<std::string> codecs; std::list<std::string> codecs;
bool b_consistent;
}; };
} }
} }
......
...@@ -398,7 +398,14 @@ void SegmentInformation::pruneBySegmentNumber(uint64_t num) ...@@ -398,7 +398,14 @@ void SegmentInformation::pruneBySegmentNumber(uint64_t num)
childs.at(i)->pruneBySegmentNumber(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 ...@@ -86,7 +86,8 @@ namespace adaptative
virtual void mergeWith(SegmentInformation *, mtime_t); virtual void mergeWith(SegmentInformation *, mtime_t);
virtual void mergeWithTimeline(SegmentTimeline *); /* ! don't use with global merge */ virtual void mergeWithTimeline(SegmentTimeline *); /* ! don't use with global merge */
virtual void pruneBySegmentNumber(uint64_t); 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: protected:
std::size_t getAllSegments(std::vector<ISegment *> &) const; std::size_t getAllSegments(std::vector<ISegment *> &) const;
......
...@@ -38,6 +38,7 @@ HLSSegment::HLSSegment( ICanonicalUrl *parent, uint64_t seq ) : ...@@ -38,6 +38,7 @@ HLSSegment::HLSSegment( ICanonicalUrl *parent, uint64_t seq ) :
Segment( parent ) Segment( parent )
{ {
setSequenceNumber(seq); setSequenceNumber(seq);
utcTime = 0;
#ifdef HAVE_GCRYPT #ifdef HAVE_GCRYPT
ctx = NULL; ctx = NULL;
#endif #endif
...@@ -122,6 +123,11 @@ void HLSSegment::onChunkDownload(block_t **pp_block, SegmentChunk *chunk, BaseRe ...@@ -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) void HLSSegment::setEncryption(SegmentEncryption &enc)
{ {
encryption = enc; encryption = enc;
......
...@@ -52,14 +52,18 @@ namespace hls ...@@ -52,14 +52,18 @@ namespace hls
class HLSSegment : public Segment class HLSSegment : public Segment
{ {
friend class M3U8Parser;
public: public:
HLSSegment( ICanonicalUrl *parent, uint64_t sequence ); HLSSegment( ICanonicalUrl *parent, uint64_t sequence );
virtual ~HLSSegment(); virtual ~HLSSegment();
void setEncryption(SegmentEncryption &); void setEncryption(SegmentEncryption &);
mtime_t getUTCTime() const;
void debug(vlc_object_t *, int) const; /* reimpl */ void debug(vlc_object_t *, int) const; /* reimpl */
virtual int compare(ISegment *) const; /* reimpl */ virtual int compare(ISegment *) const; /* reimpl */
protected: protected:
mtime_t utcTime;
virtual void onChunkDownload(block_t **, SegmentChunk *, BaseRepresentation *); /* reimpl */ virtual void onChunkDownload(block_t **, SegmentChunk *, BaseRepresentation *); /* reimpl */
SegmentEncryption encryption; SegmentEncryption encryption;
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "../adaptative/playlist/SegmentList.h" #include "../adaptative/playlist/SegmentList.h"
#include "../adaptative/tools/Retrieve.hpp" #include "../adaptative/tools/Retrieve.hpp"
#include "../adaptative/tools/Helper.h" #include "../adaptative/tools/Helper.h"
#include "../adaptative/tools/Conversions.hpp"
#include "M3U8.hpp" #include "M3U8.hpp"
#include "Tags.hpp" #include "Tags.hpp"
...@@ -214,6 +215,7 @@ void M3U8Parser::parseSegments(vlc_object_t *p_obj, Representation *rep, const s ...@@ -214,6 +215,7 @@ void M3U8Parser::parseSegments(vlc_object_t *p_obj, Representation *rep, const s
mtime_t totalduration = 0; mtime_t totalduration = 0;
mtime_t nzStartTime = 0; mtime_t nzStartTime = 0;
mtime_t absReferenceTime = VLC_TS_INVALID;
uint64_t sequenceNumber = 0; uint64_t sequenceNumber = 0;
bool discontinuity = false; bool discontinuity = false;
std::size_t prevbyterangeoffset = 0; std::size_t prevbyterangeoffset = 0;
...@@ -267,6 +269,12 @@ void M3U8Parser::parseSegments(vlc_object_t *p_obj, Representation *rep, const s ...@@ -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); segment->startTime.Set(nzStartTime * rep->timescale.Get() / CLOCK_FREQ);
nzStartTime += nzDuration; nzStartTime += nzDuration;
totalduration += nzDuration; totalduration += nzDuration;
if(absReferenceTime > VLC_TS_INVALID)
{
segment->utcTime = absReferenceTime;
absReferenceTime += nzDuration;
}
} }
ctx_extinf = NULL; ctx_extinf = NULL;
} }
...@@ -302,6 +310,12 @@ void M3U8Parser::parseSegments(vlc_object_t *p_obj, Representation *rep, const s ...@@ -302,6 +310,12 @@ void M3U8Parser::parseSegments(vlc_object_t *p_obj, Representation *rep, const s
ctx_byterange = static_cast<const SingleValueTag *>(tag); ctx_byterange = static_cast<const SingleValueTag *>(tag);
break; 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: case AttributesTag::EXTXKEY:
{ {
const AttributesTag *keytag = static_cast<const AttributesTag *>(tag); const AttributesTag *keytag = static_cast<const AttributesTag *>(tag);
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "Representation.hpp" #include "Representation.hpp"
#include "M3U8.hpp" #include "M3U8.hpp"
#include "Parser.hpp" #include "Parser.hpp"
#include "HLSSegment.hpp"
#include "../adaptative/playlist/BasePeriod.h" #include "../adaptative/playlist/BasePeriod.h"
#include "../adaptative/playlist/BaseAdaptationSet.h" #include "../adaptative/playlist/BaseAdaptationSet.h"
#include "../adaptative/playlist/SegmentList.h" #include "../adaptative/playlist/SegmentList.h"
...@@ -101,7 +102,7 @@ bool Representation::needsUpdate() const ...@@ -101,7 +102,7 @@ bool Representation::needsUpdate() const
return true; 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 time_t now = time(NULL);
const AbstractPlaylist *playlist = getPlaylist(); const AbstractPlaylist *playlist = getPlaylist();
...@@ -111,6 +112,7 @@ void Representation::runLocalUpdates(mtime_t /*currentplaybacktime*/, uint64_t n ...@@ -111,6 +112,7 @@ void Representation::runLocalUpdates(mtime_t /*currentplaybacktime*/, uint64_t n
parser.appendSegmentsFromPlaylistURI(playlist->getVLCObject(), this); parser.appendSegmentsFromPlaylistURI(playlist->getVLCObject(), this);
b_loaded = true; b_loaded = true;
if(prune)
pruneBySegmentNumber(number); pruneBySegmentNumber(number);
/* Compute new update time */ /* Compute new update time */
...@@ -143,3 +145,31 @@ void Representation::getDurationsRange(mtime_t *min, mtime_t *max) const ...@@ -143,3 +145,31 @@ void Representation::getDurationsRange(mtime_t *min, mtime_t *max) const
return; return;
BaseRepresentation::getDurationsRange(min, max); 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 ...@@ -49,8 +49,9 @@ namespace hls
bool initialized() const; bool initialized() const;
virtual bool needsUpdate() const; /* reimpl */ virtual bool needsUpdate() const; /* reimpl */
virtual void debug(vlc_object_t *, int) 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 void getDurationsRange(mtime_t *, mtime_t *) const; /* reimpl */
virtual uint64_t translateSegmentNumber(uint64_t, const SegmentInformation *) const; /* reimpl */
private: private:
StreamFormat streamFormat; 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