Commit c5db60a7 authored by François Cartegnie's avatar François Cartegnie 🤞

demux: adaptative: rewrite using synchronous demuxers

Can now flush buffers on demux restart.
Do align pcr after sending to decoders instead of always incrementing
by the target value (avoid dropping blocks across segments on restart).
Always issue a fakees to demuxer, then recycle on execution.
Avoids double deletion with duplicate Del commands (demuxer 0..n+self n).
Can now handle HLS discontinuities.
Drops the streamoutput layer.
parent d666b6e5
...@@ -309,8 +309,6 @@ libadaptative_plugin_la_SOURCES = \ ...@@ -309,8 +309,6 @@ libadaptative_plugin_la_SOURCES = \
demux/adaptative/plumbing/FakeESOutID.hpp \ demux/adaptative/plumbing/FakeESOutID.hpp \
demux/adaptative/plumbing/SourceStream.cpp \ demux/adaptative/plumbing/SourceStream.cpp \
demux/adaptative/plumbing/SourceStream.hpp \ demux/adaptative/plumbing/SourceStream.hpp \
demux/adaptative/plumbing/StreamOutput.cpp \
demux/adaptative/plumbing/StreamOutput.hpp \
demux/adaptative/ChunksSource.hpp \ demux/adaptative/ChunksSource.hpp \
demux/adaptative/PlaylistManager.cpp \ demux/adaptative/PlaylistManager.cpp \
demux/adaptative/PlaylistManager.h \ demux/adaptative/PlaylistManager.h \
...@@ -364,6 +362,8 @@ libadaptative_dash_SOURCES = \ ...@@ -364,6 +362,8 @@ libadaptative_dash_SOURCES = \
demux/dash/xml/Node.h \ demux/dash/xml/Node.h \
demux/dash/DASHManager.cpp \ demux/dash/DASHManager.cpp \
demux/dash/DASHManager.h \ demux/dash/DASHManager.h \
demux/dash/DASHStream.cpp \
demux/dash/DASHStream.hpp \
demux/dash/DASHStreamFormat.hpp demux/dash/DASHStreamFormat.hpp
libadaptative_hls_SOURCES = \ libadaptative_hls_SOURCES = \
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
#include "logic/AlwaysBestAdaptationLogic.h" #include "logic/AlwaysBestAdaptationLogic.h"
#include "logic/RateBasedAdaptationLogic.h" #include "logic/RateBasedAdaptationLogic.h"
#include "logic/AlwaysLowestAdaptationLogic.hpp" #include "logic/AlwaysLowestAdaptationLogic.hpp"
#include "plumbing/StreamOutput.hpp" #include "tools/Debug.hpp"
#include <vlc_stream.h> #include <vlc_stream.h>
#include <vlc_demux.h> #include <vlc_demux.h>
...@@ -45,12 +45,12 @@ using namespace adaptative; ...@@ -45,12 +45,12 @@ using namespace adaptative;
PlaylistManager::PlaylistManager( demux_t *p_demux_, PlaylistManager::PlaylistManager( demux_t *p_demux_,
AbstractPlaylist *pl, AbstractPlaylist *pl,
AbstractStreamOutputFactory *factory, AbstractStreamFactory *factory,
AbstractAdaptationLogic::LogicType type ) : AbstractAdaptationLogic::LogicType type ) :
conManager ( NULL ), conManager ( NULL ),
logicType ( type ), logicType ( type ),
playlist ( pl ), playlist ( pl ),
streamOutputFactory( factory ), streamFactory ( factory ),
p_demux ( p_demux_ ), p_demux ( p_demux_ ),
nextPlaylistupdate ( 0 ), nextPlaylistupdate ( 0 ),
i_nzpcr ( 0 ) i_nzpcr ( 0 )
...@@ -61,14 +61,14 @@ PlaylistManager::PlaylistManager( demux_t *p_demux_, ...@@ -61,14 +61,14 @@ PlaylistManager::PlaylistManager( demux_t *p_demux_,
PlaylistManager::~PlaylistManager () PlaylistManager::~PlaylistManager ()
{ {
delete conManager; delete conManager;
delete streamOutputFactory; delete streamFactory;
unsetPeriod(); unsetPeriod();
delete playlist; delete playlist;
} }
void PlaylistManager::unsetPeriod() void PlaylistManager::unsetPeriod()
{ {
std::vector<Stream *>::iterator it; std::vector<AbstractStream *>::iterator it;
for(it=streams.begin(); it!=streams.end(); ++it) for(it=streams.begin(); it!=streams.end(); ++it)
delete *it; delete *it;
streams.clear(); streams.clear();
...@@ -84,50 +84,46 @@ bool PlaylistManager::setupPeriod() ...@@ -84,50 +84,46 @@ bool PlaylistManager::setupPeriod()
for(it=sets.begin();it!=sets.end();++it) for(it=sets.begin();it!=sets.end();++it)
{ {
BaseAdaptationSet *set = *it; BaseAdaptationSet *set = *it;
if(set) if(set && streamFactory)
{ {
Stream *st = new (std::nothrow) Stream(p_demux, set->getStreamFormat());
if(!st)
continue;
AbstractAdaptationLogic *logic = createLogic(logicType); AbstractAdaptationLogic *logic = createLogic(logicType);
if(!logic) if(!logic)
continue;
SegmentTracker *tracker = new (std::nothrow) SegmentTracker(logic, set);
if(!tracker)
{ {
delete st; delete logic;
continue; continue;
} }
SegmentTracker *tracker = new (std::nothrow) SegmentTracker(logic, set); AbstractStream *st = streamFactory->create(p_demux, set->getStreamFormat(),
try logic, tracker, conManager);
if(!st)
{
delete tracker;
delete logic;
continue;
}
streams.push_back(st);
/* Generate stream description */
std::list<std::string> languages;
if(!set->getLang().empty())
{
languages = set->getLang();
}
else if(!set->getRepresentations().empty())
{ {
if(!tracker || !streamOutputFactory) languages = set->getRepresentations().front()->getLang();
{
delete tracker;
delete logic;
throw VLC_ENOMEM;
}
std::list<std::string> languages;
if(!set->getLang().empty())
{
languages = set->getLang();
}
else if(!set->getRepresentations().empty())
{
languages = set->getRepresentations().front()->getLang();
}
if(!languages.empty())
st->setLanguage(languages.front());
if(!set->description.Get().empty())
st->setDescription(set->description.Get());
st->create(logic, tracker, streamOutputFactory);
streams.push_back(st);
} catch (int) {
delete st;
} }
if(!languages.empty())
st->setLanguage(languages.front());
if(!set->description.Get().empty())
st->setDescription(set->description.Get());
} }
} }
return true; return true;
...@@ -135,11 +131,10 @@ bool PlaylistManager::setupPeriod() ...@@ -135,11 +131,10 @@ bool PlaylistManager::setupPeriod()
bool PlaylistManager::start() bool PlaylistManager::start()
{ {
if(!setupPeriod()) if(!conManager && !(conManager = new (std::nothrow) HTTPConnectionManager(VLC_OBJECT(p_demux->s))))
return false; return false;
conManager = new (std::nothrow) HTTPConnectionManager(VLC_OBJECT(p_demux->s)); if(!setupPeriod())
if(!conManager)
return false; return false;
playlist->playbackStart.Set(time(NULL)); playlist->playbackStart.Set(time(NULL));
...@@ -148,14 +143,14 @@ bool PlaylistManager::start() ...@@ -148,14 +143,14 @@ bool PlaylistManager::start()
return true; return true;
} }
Stream::status PlaylistManager::demux(mtime_t nzdeadline, bool send) AbstractStream::status PlaylistManager::demux(mtime_t nzdeadline, bool send)
{ {
Stream::status i_return = Stream::status_eof; AbstractStream::status i_return = AbstractStream::status_eof;
std::vector<Stream *>::iterator it; std::vector<AbstractStream *>::iterator it;
for(it=streams.begin(); it!=streams.end(); ++it) for(it=streams.begin(); it!=streams.end(); ++it)
{ {
Stream *st = *it; AbstractStream *st = *it;
if (st->isDisabled()) if (st->isDisabled())
{ {
...@@ -165,25 +160,28 @@ Stream::status PlaylistManager::demux(mtime_t nzdeadline, bool send) ...@@ -165,25 +160,28 @@ Stream::status PlaylistManager::demux(mtime_t nzdeadline, bool send)
continue; continue;
} }
Stream::status i_ret = st->demux(conManager, nzdeadline, send); AbstractStream::status i_ret = st->demux(nzdeadline, send);
if(i_ret == AbstractStream::status_buffering)
if(i_ret == Stream::status_buffering)
{ {
i_return = Stream::status_buffering; i_return = AbstractStream::status_buffering;
} }
else if(i_ret == Stream::status_demuxed && else if(i_ret == AbstractStream::status_demuxed &&
i_return != Stream::status_buffering) i_return != AbstractStream::status_buffering)
{ {
i_return = Stream::status_demuxed; i_return = AbstractStream::status_demuxed;
}
else if(i_ret == AbstractStream::status_dis)
{
i_return = AbstractStream::status_dis;
} }
} }
/* might be end of current period */ /* might be end of current period */
if(i_return == Stream::status_eof && currentPeriod) if(i_return == AbstractStream::status_eof && currentPeriod)
{ {
unsetPeriod(); unsetPeriod();
currentPeriod = playlist->getNextPeriod(currentPeriod); currentPeriod = playlist->getNextPeriod(currentPeriod);
i_return = (setupPeriod()) ? Stream::status_eop : Stream::status_eof; i_return = (setupPeriod()) ? AbstractStream::status_eop : AbstractStream::status_eof;
} }
return i_return; return i_return;
...@@ -192,10 +190,10 @@ Stream::status PlaylistManager::demux(mtime_t nzdeadline, bool send) ...@@ -192,10 +190,10 @@ Stream::status PlaylistManager::demux(mtime_t nzdeadline, bool send)
mtime_t PlaylistManager::getPCR() const mtime_t PlaylistManager::getPCR() const
{ {
mtime_t pcr = VLC_TS_INVALID; mtime_t pcr = VLC_TS_INVALID;
std::vector<Stream *>::const_iterator it; std::vector<AbstractStream *>::const_iterator it;
for(it=streams.begin(); it!=streams.end(); ++it) for(it=streams.begin(); it!=streams.end(); ++it)
{ {
if ((*it)->isDisabled()) if ((*it)->isDisabled() || (*it)->isEOF())
continue; continue;
if(pcr == VLC_TS_INVALID || pcr > (*it)->getPCR()) if(pcr == VLC_TS_INVALID || pcr > (*it)->getPCR())
pcr = (*it)->getPCR(); pcr = (*it)->getPCR();
...@@ -206,10 +204,10 @@ mtime_t PlaylistManager::getPCR() const ...@@ -206,10 +204,10 @@ mtime_t PlaylistManager::getPCR() const
mtime_t PlaylistManager::getFirstDTS() const mtime_t PlaylistManager::getFirstDTS() const
{ {
mtime_t dts = VLC_TS_INVALID; mtime_t dts = VLC_TS_INVALID;
std::vector<Stream *>::const_iterator it; std::vector<AbstractStream *>::const_iterator it;
for(it=streams.begin(); it!=streams.end(); ++it) for(it=streams.begin(); it!=streams.end(); ++it)
{ {
if ((*it)->isDisabled()) if ((*it)->isDisabled() || (*it)->isEOF())
continue; continue;
if(dts == VLC_TS_INVALID || dts > (*it)->getFirstDTS()) if(dts == VLC_TS_INVALID || dts > (*it)->getFirstDTS())
dts = (*it)->getFirstDTS(); dts = (*it)->getFirstDTS();
...@@ -220,7 +218,7 @@ mtime_t PlaylistManager::getFirstDTS() const ...@@ -220,7 +218,7 @@ mtime_t PlaylistManager::getFirstDTS() const
int PlaylistManager::esCount() const int PlaylistManager::esCount() const
{ {
int es = 0; int es = 0;
std::vector<Stream *>::const_iterator it; std::vector<AbstractStream *>::const_iterator it;
for(it=streams.begin(); it!=streams.end(); ++it) for(it=streams.begin(); it!=streams.end(); ++it)
{ {
es += (*it)->esCount(); es += (*it)->esCount();
...@@ -242,7 +240,7 @@ bool PlaylistManager::setPosition(mtime_t time) ...@@ -242,7 +240,7 @@ bool PlaylistManager::setPosition(mtime_t time)
for(int real = 0; real < 2; real++) for(int real = 0; real < 2; real++)
{ {
/* Always probe if we can seek first */ /* Always probe if we can seek first */
std::vector<Stream *>::iterator it; std::vector<AbstractStream *>::iterator it;
for(it=streams.begin(); it!=streams.end(); ++it) for(it=streams.begin(); it!=streams.end(); ++it)
{ {
ret &= (*it)->setPosition(time, !real); ret &= (*it)->setPosition(time, !real);
...@@ -258,7 +256,7 @@ bool PlaylistManager::seekAble() const ...@@ -258,7 +256,7 @@ bool PlaylistManager::seekAble() const
if(playlist->isLive()) if(playlist->isLive())
return false; return false;
std::vector<Stream *>::const_iterator it; std::vector<AbstractStream *>::const_iterator it;
for(it=streams.begin(); it!=streams.end(); ++it) for(it=streams.begin(); it!=streams.end(); ++it)
{ {
if(!(*it)->seekAble()) if(!(*it)->seekAble())
...@@ -269,7 +267,7 @@ bool PlaylistManager::seekAble() const ...@@ -269,7 +267,7 @@ bool PlaylistManager::seekAble() const
bool PlaylistManager::updatePlaylist() bool PlaylistManager::updatePlaylist()
{ {
std::vector<Stream *>::const_iterator it; std::vector<AbstractStream *>::const_iterator it;
for(it=streams.begin(); it!=streams.end(); ++it) for(it=streams.begin(); it!=streams.end(); ++it)
(*it)->runUpdates(); (*it)->runUpdates();
...@@ -287,7 +285,7 @@ int PlaylistManager::doDemux(int64_t increment) ...@@ -287,7 +285,7 @@ int PlaylistManager::doDemux(int64_t increment)
{ {
if(i_nzpcr == VLC_TS_INVALID) if(i_nzpcr == VLC_TS_INVALID)
{ {
if( Stream::status_eof == demux(i_nzpcr + increment, false) ) if( AbstractStream::status_eof == demux(i_nzpcr + increment, false) )
{ {
return VLC_DEMUXER_EOF; return VLC_DEMUXER_EOF;
} }
...@@ -296,19 +294,20 @@ int PlaylistManager::doDemux(int64_t increment) ...@@ -296,19 +294,20 @@ int PlaylistManager::doDemux(int64_t increment)
i_nzpcr = getPCR(); i_nzpcr = getPCR();
} }
Stream::status status = demux(i_nzpcr + increment, true); AbstractStream::status status = demux(i_nzpcr + increment, true);
AdvDebug(msg_Dbg( p_demux, "doDemux() status %d dts %ld pcr %ld", status, getFirstDTS(), getPCR() ));
switch(status) switch(status)
{ {
case Stream::status_eof: case AbstractStream::status_eof:
return VLC_DEMUXER_EOF; return VLC_DEMUXER_EOF;
case Stream::status_buffering: case AbstractStream::status_buffering:
break; break;
case Stream::status_eop: case AbstractStream::status_dis:
case AbstractStream::status_eop:
i_nzpcr = VLC_TS_INVALID; i_nzpcr = VLC_TS_INVALID;
es_out_Control(p_demux->out, ES_OUT_RESET_PCR); es_out_Control(p_demux->out, ES_OUT_RESET_PCR);
break; break;
case Stream::status_demuxed: case AbstractStream::status_demuxed:
if( i_nzpcr != VLC_TS_INVALID ) if( i_nzpcr != VLC_TS_INVALID )
{ {
i_nzpcr += increment; i_nzpcr += increment;
......
...@@ -43,20 +43,17 @@ namespace adaptative ...@@ -43,20 +43,17 @@ namespace adaptative
using namespace logic; using namespace logic;
using namespace http; using namespace http;
class AbstractStreamFactory;
class AbstractStreamOutputFactory;
class PlaylistManager class PlaylistManager
{ {
public: public:
PlaylistManager( demux_t *, AbstractPlaylist *, PlaylistManager( demux_t *, AbstractPlaylist *,
AbstractStreamOutputFactory *, AbstractStreamFactory *,
AbstractAdaptationLogic::LogicType type ); AbstractAdaptationLogic::LogicType type );
virtual ~PlaylistManager (); virtual ~PlaylistManager ();
bool start(); bool start();
Stream::status demux(mtime_t, bool); AbstractStream::status demux(mtime_t, bool);
mtime_t getDuration() const; mtime_t getDuration() const;
mtime_t getPCR() const; mtime_t getPCR() const;
mtime_t getFirstDTS() const; mtime_t getFirstDTS() const;
...@@ -82,9 +79,9 @@ namespace adaptative ...@@ -82,9 +79,9 @@ namespace adaptative
HTTPConnectionManager *conManager; HTTPConnectionManager *conManager;
AbstractAdaptationLogic::LogicType logicType; AbstractAdaptationLogic::LogicType logicType;
AbstractPlaylist *playlist; AbstractPlaylist *playlist;
AbstractStreamOutputFactory *streamOutputFactory; AbstractStreamFactory *streamFactory;
demux_t *p_demux; demux_t *p_demux;
std::vector<Stream *> streams; std::vector<AbstractStream *> streams;
time_t nextPlaylistupdate; time_t nextPlaylistupdate;
mtime_t i_nzpcr; mtime_t i_nzpcr;
BasePeriod *currentPeriod; BasePeriod *currentPeriod;
......
...@@ -22,37 +22,65 @@ ...@@ -22,37 +22,65 @@
#include "http/HTTPConnectionManager.h" #include "http/HTTPConnectionManager.h"
#include "logic/AbstractAdaptationLogic.h" #include "logic/AbstractAdaptationLogic.h"
#include "playlist/SegmentChunk.hpp" #include "playlist/SegmentChunk.hpp"
#include "plumbing/StreamOutput.hpp"
#include "SegmentTracker.hpp" #include "SegmentTracker.hpp"
#include "plumbing/SourceStream.hpp"
#include "plumbing/CommandsQueue.hpp"
#include "tools/Debug.hpp"
#include <vlc_demux.h> #include <vlc_demux.h>
using namespace adaptative; using namespace adaptative;
using namespace adaptative::http; using namespace adaptative::http;
using namespace adaptative::logic; using namespace adaptative::logic;
Stream::Stream(demux_t * demux_, const StreamFormat &format_) AbstractStream::AbstractStream(demux_t * demux_, const StreamFormat &format_)
{ {
p_demux = demux_; p_realdemux = demux_;
type = UNKNOWN; type = UNKNOWN;
format = format_; format = format_;
output = NULL;
adaptationLogic = NULL; adaptationLogic = NULL;
currentChunk = NULL; currentChunk = NULL;
eof = false; eof = false;
dead = false;
disabled = false; disabled = false;
flushing = false;
restarting_output = false;
discontinuity = false;
segmentTracker = NULL; segmentTracker = NULL;
streamOutputFactory = NULL; pcr = VLC_TS_INVALID;
demuxer = NULL;
fakeesout = NULL;
/* Don't even try if not supported */
if((unsigned)format == StreamFormat::UNSUPPORTED)
throw VLC_EGENERIC;
demuxersource = new (std::nothrow) ChunksSourceStream( VLC_OBJECT(p_realdemux), this );
if(!demuxersource)
throw VLC_EGENERIC;
CommandsFactory *factory = new CommandsFactory();
fakeesout = new (std::nothrow) FakeESOut(p_realdemux->out, factory);
if(!fakeesout)
{
delete demuxersource;
throw VLC_EGENERIC;
}
fakeesout->setExtraInfoProvider( this );
} }
Stream::~Stream() AbstractStream::~AbstractStream()
{ {
delete currentChunk; delete currentChunk;
delete adaptationLogic; delete adaptationLogic;
delete output;
delete segmentTracker; delete segmentTracker;
delete demuxer;
delete demuxersource;
delete fakeesout;
} }
StreamType Stream::mimeToType(const std::string &mime) StreamType AbstractStream::mimeToType(const std::string &mime)
{ {
StreamType mimetype; StreamType mimetype;
if (!mime.compare(0, 6, "video/")) if (!mime.compare(0, 6, "video/"))
...@@ -66,97 +94,103 @@ StreamType Stream::mimeToType(const std::string &mime) ...@@ -66,97 +94,103 @@ StreamType Stream::mimeToType(const std::string &mime)
return mimetype; return mimetype;
} }
void Stream::create(AbstractAdaptationLogic *logic, SegmentTracker *tracker, void AbstractStream::bind(AbstractAdaptationLogic *logic, SegmentTracker *tracker,
const AbstractStreamOutputFactory *factory) HTTPConnectionManager *conn)
{ {
adaptationLogic = logic; adaptationLogic = logic;
segmentTracker = tracker; segmentTracker = tracker;
streamOutputFactory = factory; connManager = conn;
updateFormat(format);
} }
void Stream::updateFormat(StreamFormat &newformat) void AbstractStream::prepareFormatChange()
{ {
if( format == newformat && output ) if(demuxer)
return; {
/* Enqueue Del Commands for all current ES */
output = streamOutputFactory->create(p_demux, newformat, output); demuxer->drain();
/* Enqueue Del Commands for all current ES */
format = newformat; fakeesout->scheduleAllForDeletion();
if(!output) fakeesout->schedulePCRReset();
throw VLC_EGENERIC; fakeesout->commandsqueue.Commit();
output->setLanguage(language); /* ignoring demuxer's own Del commands */
output->setDescription(description); fakeesout->commandsqueue.setDrop(true);
delete demuxer;
fakeesout->commandsqueue.setDrop(false);
demuxer = NULL;
}
} }
void Stream::setLanguage(const std::string &lang) void AbstractStream::setLanguage(const std::string &lang)
{ {
language = lang; language = lang;