Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • videolan/vlc
  • chouquette/vlc
  • bakiewicz.marek122/vlc
  • devnexen/vlc
  • rohanrajpal/vlc
  • blurrrb/vlc
  • gsoc/gsoc2019/darkapex/vlc
  • b1ue/vlc
  • fkuehne/vlc
  • magsoft/vlc
  • chub/vlc
  • cramiro9/vlc
  • robUx4/vlc
  • rom1v/vlc
  • akshayaky/vlc
  • tmk907/vlc
  • akymaster/vlc
  • govind.sharma/vlc
  • psilokos/vlc
  • xjbeta/vlc
  • jahan/vlc
  • 1480c1/vlc
  • amanchande/vlc
  • aaqib/vlc
  • rist/vlc
  • apol/vlc
  • mindfreeze/vlc
  • alexandre-janniaux/vlc
  • sandsmark/vlc
  • jagannatharjun/vlc
  • gsoc/gsoc2020/matiaslgonzalez/vlc
  • gsoc/gsoc2020/jagannatharjun/vlc
  • mstorsjo/vlc
  • gsoc/gsoc2020/vedenta/vlc
  • gsoc/gsoc2020/arnav-ishaan/vlc
  • gsoc/gsoc2020/andreduong/vlc
  • fuzun/vlc
  • gsoc/gsoc2020/vatsin/vlc
  • gsoc/gsoc2020/sagid/vlc
  • yaron/vlc
  • Phoenix/vlc
  • Garf/vlc
  • ePiratWorkarounds/vlc
  • tguillem/vlc
  • jnqnfe/vlc
  • mdc/vlc
  • Vedaa/vlc
  • rasa/vlc
  • quink/vlc
  • yealo/vlc
  • aleksey_ak/vlc
  • ePirat/vlc
  • ilya.yanok/vlc
  • asenat/vlc
  • m/vlc
  • bunjee/vlc
  • BLumia/vlc
  • sagudev/vlc
  • hamedmonji30/vlc
  • nullgemm/vlc
  • DivyamAhuja/vlc
  • thesamesam/vlc
  • dag7/vlc
  • snehil101/vlc
  • haasn/vlc
  • jbk/vlc
  • ValZapod/vlc
  • mfkl/vlc
  • WangChuan/vlc
  • core1024/vlc
  • GhostVaibhav/vlc
  • dfuhrmann/vlc
  • davide.prade/vlc
  • tmatth/vlc
  • Courmisch/vlc
  • zouya/vlc
  • hpi/vlc
  • EwoutH/vlc
  • aleung27/vlc
  • hengwu0/vlc
  • saladin/vlc
  • ashuio/vlc
  • richselwood/vlc
  • verma16Ayush/vlc
  • chemicalflash/vlc
  • PoignardAzur/vlc
  • huangjieNT/vlc
  • Blake-Haydon/vlc
  • AnuthaDev/vlc
  • gsoc/gsoc2021/mpd/vlc
  • nicolas_lequec/vlc
  • sambassaly/vlc
  • thresh/vlc
  • bonniegong/vlc
  • myaashish/vlc
  • stavros.vagionitis/vlc
  • ileoo/vlc
  • louis-santucci/vlc
  • cchristiansen/vlc
  • sabyasachi07/vlc
  • AbduAmeen/vlc
  • ashishb0410/vlc
  • urbanhusky/vlc
  • davidepietrasanta/vlc
  • riksleutelstad/vlc
  • jeremyVignelles/vlc
  • komh/vlc
  • iamjithinjohn/vlc
  • JohannesKauffmann/vlc2
  • kunglao/vlc
  • natzberg/vlc
  • jill/vlc
  • cwendling/vlc
  • adufou/vlc
  • ErwanAirone/vlc
  • HasinduDilshan10/vlc
  • vagrantc/vlc
  • rafiv/macos-bigsur-icon
  • Aymeriic/vlc
  • saranshg20/vlc
  • metzlove24/vlc
  • linkfanel/vlc
  • Ds886/vlc
  • metehan-arslan/vlc
  • Skantes/vlc
  • kgsandundananjaya96/vlc
  • mitchcapper/vlc
  • advaitgupta/vlc
  • StefanBruens/vlc
  • ratajs/vlc
  • T.M.F.B.3761/vlc
  • m222059/vlc
  • casemerrick/vlc
  • joshuaword2alt/vlc
  • sjwaddy/vlc
  • dima/vlc
  • Ybalrid/vlc
  • umxprime/vlc
  • eschmidt/vlc
  • vannieuwenhuysenmichelle/vlc
  • badcf00d/vlc
  • wesinator/vlc
  • louis/vlc
  • xqq/vlc
  • EmperorYP7/vlc
  • NicoLiam/vlc
  • loveleen/vlc
  • rofferom/vlc
  • rbultje/vlc
  • TheUnamed/vlc
  • pratiksharma341/vlc
  • Saurab17/vlc
  • purist.coder/vlc
  • Shuicheng/vlc
  • mdrrubel292/vlc
  • silverbleu00/vlc
  • metif12/vlc
  • asher-m/vlc
  • jeffk/vlc
  • Brandonbr1/vlc
  • beautyyuyanli/vlc
  • rego21/vlc
  • muyangren907/vlc
  • collectionbylawrencejason/vlc
  • evelez/vlc
  • GSMgeeth/vlc
  • Oneric/vlc
  • TJ5/vlc
  • XuanTung95/vlc
  • darrenjenny21/vlc
  • Trenly/vlc
  • RockyTDR/vlc
  • mjakubowski/vlc
  • caprica/vlc
  • ForteFrankie/vlc
  • seannamiller19/vlc
  • junlon2006/vlc
  • kiwiren6666/vlc
  • iuseiphonexs/vlc
  • fenngtun/vlc
  • Rajdutt999/vlc
  • typx/vlc
  • leon.vitanos/vlc
  • robertogarci0938/vlc
  • gsoc/gsoc2022/luc65r/vlc-mpd
  • skeller/vlc
  • MCJack123/vlc
  • luc65r/vlc-mpd
  • popov895/vlc
  • claucambra/vlc
  • brad/vlc
  • matthewmurua88/vlc
  • Tomas8874/vlc
  • philenotfound/vlc
  • makita-do3/vlc
  • LZXCorp/vlc
  • mar0x/vlc
  • senojetkennedy0102/vlc
  • shaneb243/vlc
  • ahmadbader/vlc
  • rajduttcse26/vlc-audio-filters
  • Juniorzito8415/vlc
  • achernyakov/vlc
  • lucasjetgroup/vlc
  • pupdoggy666/vlc
  • gmde9363/vlc
  • alexnwayne/vlc
  • bahareebrahimi781/vlc
  • hamad633666/vlc
  • umghof3112/vlc
  • joe0199771874/vlc
  • Octocats66666666/vlc
  • jjm_223/vlc
  • btech10110.19/vlc
  • sunnykfc028/vlc-audio-filters
  • loic/vlc
  • nguyenminhducmx1/vlc
  • JanekKrueger/vlc
  • bstubbington2/vlc
  • rcombs/vlc
  • Ordissimo/vlc
  • king7532/vlc
  • noobsauce101/vlc
  • schong0525/vlc
  • myQwil/vlc
  • apisbg91/vlc
  • geeboy0101017/vlc
  • kim.faughey/vlc
  • nurupo/vlc
  • yyusea/vlc
  • 0711235879.khco/vlc
  • ialo/vlc
  • iloveyeye2/vlc
  • gdtdftdqtd/vlc
  • leandroconsiglio/vlc
  • AndyHTML2012/vlc
  • ncz/vlc
  • lucenticus/vlc
  • knr1931/vlc
  • kjoonlee/vlc
  • chandrakant100/vlc-qt
  • johge42/vlc
  • polter/vlc
  • hexchain/vlc
  • Tushwrld/vlc
  • mztea928/vlc
  • jbelloncastro/vlc
  • alvinhochun/vlc
  • ghostpiratecrow/vlc
  • ujjwaltwitx/vlc
  • alexsonarin06/vlc
  • adrianbon76/vlc
  • altsod/vlc
  • damien.lucas44/vlc
  • dmytrivtaisa/vlc
  • utk202/vlc
  • aaxhrj/vlc
  • thomas.hermes/vlc
  • structurenewworldorder/vlc
  • slomo/vlc
  • wantlamy/vlc
  • musc.o3cminc/vlc
  • thebarshablog/vlc
  • kerrick/vlc
  • kratos142518/vlc
  • leogps/vlc
  • vacantron/vlc
  • luna_koly/vlc
  • Ratio2/vlc
  • anuoshemohammad/vlc
  • apsun/vlc
  • aaa1115910/vlc
  • alimotmoyo/vlc
  • Ambossmann/vlc
  • Sam-LearnsToCode/vlc
  • Chilledheart/vlc
  • Labnann/vlc
  • ktcoooot1/vlc
  • mohit-marathe/vlc
  • johnddx/vlc
  • manstabuk/vlc
  • Omar-ahmed314/vlc
  • vineethkm/vlc
  • 9Enemi86/vlc
  • radoslav.m.panteleev/vlc
  • ashishami2002/vlc
  • Corbax/vlc
  • firnasahmed/vlc
  • pelayarmalam4/vlc
  • c0ff330k/vlc
  • shikhindahikar/vlc
  • l342723951/vlc
  • christianschwandner/vlc
  • douniwan5788/vlc
  • 7damian7/vlc
  • ferdnyc/vlc
  • f.ales1/vlc
  • pandagby/vlc
  • BaaBaa/vlc
  • jewe37/vlc
  • w00drow/vlc
  • russelltg/vlc
  • ironicallygod/vlc
  • soumyaDghosh/vlc
  • linzihao1999/vlc
  • deyayush6/vlc
  • mibi88/vlc
  • newabdallah10/vlc
  • jhorbincolombia/vlc
  • rimvihaqueshupto/vlc
  • andrewkhon98/vlc
  • fab78/vlc
  • lapaz17/vlc
  • amanna13/vlc
  • mdakram28/vlc
  • 07jw1980/vlc
  • sohamgupta/vlc
  • Eson-Jia1/vlc
  • Sumou/vlc
  • vikram-kangotra/vlc
  • chalice191/vlc
  • olivercalder/vlc
  • aaasg4001/vlc
  • zipdox/vlc
  • kwizart/vlc
  • Dragon-S/vlc
  • jdemeule/vlc
  • gabriel_lt/vlc
  • locutusofborg/vlc
  • sammirata/vlc-librist
  • another/vlc
  • Benjamin_Loison/vlc
  • ahmedmoselhi/vlc
  • petergaal/vlc
  • huynhsontung/vlc
  • dariusmihut/vlc
  • tvermaashutosh/vlc
  • buti/vlc
  • Niram7777/vlc
  • rohan-here/vlc
  • balaji-sivasakthi/vlc
  • rlindner81/vlc
  • Kakadus/vlc
  • djain/vlc
  • ABBurmeister/vlc
  • craighuggins/vlc
  • orbea/vlc
  • maxos/vlc
  • aakarshmj/vlc
  • kblaschke/vlc
  • ankitm/vlc
  • advait-0/vlc
  • mohak2003/vlc
  • yselkowitz/vlc
  • AZM999/vlc-azm
  • andrey.turkin/vlc
  • Disha-Baghel/vlc
  • nowrep/vlc
  • Apeng/vlc
  • Choucroute_melba/vlc
  • autra/vlc
  • eclipseo/vlc
  • fhuber/vlc
  • olafhering/vlc
  • sdasda7777/vlc
  • 1div0/vlc
  • skosnits/vlc-extended-playlist-support
  • dnicolson/vlc
  • Timshel/vlc
  • octopols/vlc
  • MangalK/vlc
  • nima64/vlc
  • misawai/vlc
  • Alexander-Wilms/vlc
  • Maxime2/vlc-fork-for-visualizer
  • ww/vlc
  • jeske/vlc
  • sgross-emlix/vlc
  • morenonatural/vlc
  • freakingLovesVLC/vlc
  • borisgolovnev/vlc
  • mpromonet/vlc
  • diogo.simao-marques/vlc
  • masstock/vlc
  • pratikpatel8982/vlc
  • hugok79/vlc
  • longervision/vlc
  • abhiudaysurya/vlc
  • rishabhgarg/vlc
  • tumic/vlc
  • cart/vlc
  • shubham442/vlc
  • Aditya692005/vlc
  • sammirata/vlc4
  • syrykh/vlc
  • Vvorcun/macos-new-icon
  • AyaanshC/vlc
  • nasso/vlc
  • Quark/vlc
  • sebastinas/vlc
  • rhstone/vlc
  • talregev/vlc
  • Managor/vlc
  • abdsaber000/vlc
404 results
Show changes
Commits on Source (150)
Showing
with 951 additions and 692 deletions
......@@ -283,15 +283,17 @@ if HAVE_DVBPSI
demux_LTLIBRARIES += libts_plugin.la
endif
libadaptive_plugin_la_SOURCES = \
demux/adaptive/playlist/AbstractPlaylist.cpp \
demux/adaptive/playlist/AbstractPlaylist.hpp \
libvlc_adaptive_la_SOURCES = \
demux/adaptive/playlist/BaseAdaptationSet.cpp \
demux/adaptive/playlist/BaseAdaptationSet.h \
demux/adaptive/playlist/BasePeriod.cpp \
demux/adaptive/playlist/BasePeriod.h \
demux/adaptive/playlist/BasePlaylist.cpp \
demux/adaptive/playlist/BasePlaylist.hpp \
demux/adaptive/playlist/BaseRepresentation.cpp \
demux/adaptive/playlist/BaseRepresentation.h \
demux/adaptive/playlist/CodecDescription.cpp \
demux/adaptive/playlist/CodecDescription.hpp \
demux/adaptive/playlist/CommonAttributesElements.cpp \
demux/adaptive/playlist/CommonAttributesElements.h \
demux/adaptive/playlist/ICanonicalUrl.hpp \
......@@ -303,10 +305,10 @@ libadaptive_plugin_la_SOURCES = \
demux/adaptive/playlist/Segment.h \
demux/adaptive/playlist/SegmentBase.cpp \
demux/adaptive/playlist/SegmentBase.h \
demux/adaptive/playlist/SegmentBaseType.cpp \
demux/adaptive/playlist/SegmentBaseType.hpp \
demux/adaptive/playlist/SegmentChunk.cpp \
demux/adaptive/playlist/SegmentChunk.hpp \
demux/adaptive/playlist/SegmentInfoCommon.cpp \
demux/adaptive/playlist/SegmentInfoCommon.h \
demux/adaptive/playlist/SegmentList.cpp \
demux/adaptive/playlist/SegmentList.h \
demux/adaptive/playlist/SegmentTimeline.cpp \
......@@ -355,8 +357,6 @@ libadaptive_plugin_la_SOURCES = \
demux/adaptive/http/HTTPConnection.hpp \
demux/adaptive/http/HTTPConnectionManager.cpp \
demux/adaptive/http/HTTPConnectionManager.h \
demux/adaptive/http/Transport.hpp \
demux/adaptive/http/Transport.cpp \
demux/adaptive/plumbing/CommandsQueue.cpp \
demux/adaptive/plumbing/CommandsQueue.hpp \
demux/adaptive/plumbing/Demuxer.cpp \
......@@ -398,12 +398,12 @@ libadaptive_plugin_la_SOURCES = \
demux/adaptive/xml/DOMParser.h \
demux/adaptive/xml/Node.cpp \
demux/adaptive/xml/Node.h
libadaptive_plugin_la_SOURCES += \
libvlc_adaptive_la_SOURCES += \
demux/mp4/libmp4.c \
demux/mp4/libmp4.h \
meta_engine/ID3Tag.h
libadaptive_dash_SOURCES = \
# DASH specific
libvlc_adaptive_la_SOURCES += \
demux/dash/mpd/AdaptationSet.cpp \
demux/dash/mpd/AdaptationSet.h \
demux/dash/mpd/DASHCommonAttributesElements.cpp \
......@@ -416,14 +416,14 @@ libadaptive_dash_SOURCES = \
demux/dash/mpd/IsoffMainParser.h \
demux/dash/mpd/MPD.cpp \
demux/dash/mpd/MPD.h \
demux/dash/mpd/Period.cpp \
demux/dash/mpd/Period.h \
demux/dash/mpd/Profile.cpp \
demux/dash/mpd/Profile.hpp \
demux/dash/mpd/ProgramInformation.cpp \
demux/dash/mpd/ProgramInformation.h \
demux/dash/mpd/Representation.cpp \
demux/dash/mpd/Representation.h \
demux/dash/mpd/TemplatedUri.cpp \
demux/dash/mpd/TemplatedUri.hpp \
demux/dash/mpd/TrickModeType.cpp \
demux/dash/mpd/TrickModeType.h \
demux/dash/mp4/IndexReader.cpp \
......@@ -432,14 +432,14 @@ libadaptive_dash_SOURCES = \
demux/dash/DASHManager.h \
demux/dash/DASHStream.cpp \
demux/dash/DASHStream.hpp
libadaptive_hls_SOURCES = \
# HLS specific
libvlc_adaptive_la_SOURCES += \
demux/hls/playlist/M3U8.hpp \
demux/hls/playlist/M3U8.cpp \
demux/hls/playlist/Parser.hpp \
demux/hls/playlist/Parser.cpp \
demux/hls/playlist/Representation.hpp \
demux/hls/playlist/Representation.cpp \
demux/hls/playlist/HLSRepresentation.hpp \
demux/hls/playlist/HLSRepresentation.cpp \
demux/hls/playlist/HLSSegment.hpp \
demux/hls/playlist/HLSSegment.cpp \
demux/hls/playlist/Tags.hpp \
......@@ -447,46 +447,71 @@ libadaptive_hls_SOURCES = \
demux/hls/HLSManager.hpp \
demux/hls/HLSManager.cpp \
demux/hls/HLSStreams.hpp \
demux/hls/HLSStreams.cpp
libadaptive_hls_SOURCES += meta_engine/ID3Tag.h \
meta_engine/ID3Meta.h
libadaptive_smooth_SOURCES = \
demux/smooth/mp4/IndexReader.cpp \
demux/smooth/mp4/IndexReader.hpp \
demux/hls/HLSStreams.cpp \
demux/mpeg/timestamps.h
libvlc_adaptive_la_SOURCES += meta_engine/ID3Meta.h
# smooth streaming specific
libvlc_adaptive_la_SOURCES += \
demux/smooth/mp4/SmoothIndexReader.cpp \
demux/smooth/mp4/SmoothIndexReader.hpp \
demux/smooth/playlist/ForgedInitSegment.hpp \
demux/smooth/playlist/ForgedInitSegment.cpp \
demux/smooth/playlist/Manifest.hpp \
demux/smooth/playlist/Manifest.cpp \
demux/smooth/playlist/MemoryChunk.hpp \
demux/smooth/playlist/MemoryChunk.cpp \
demux/smooth/playlist/Parser.hpp \
demux/smooth/playlist/Parser.cpp \
demux/smooth/playlist/Representation.hpp \
demux/smooth/playlist/Representation.cpp \
demux/smooth/playlist/SmoothParser.hpp \
demux/smooth/playlist/SmoothParser.cpp \
demux/smooth/playlist/QualityLevel.cpp \
demux/smooth/playlist/QualityLevel.hpp \
demux/smooth/playlist/SmoothSegment.hpp \
demux/smooth/playlist/SmoothSegment.cpp \
demux/smooth/SmoothManager.hpp \
demux/smooth/SmoothManager.cpp \
demux/smooth/SmoothStream.hpp \
demux/smooth/SmoothStream.cpp
libadaptive_smooth_SOURCES += mux/mp4/libmp4mux.c mux/mp4/libmp4mux.h \
packetizer/h264_nal.c packetizer/hevc_nal.c
libadaptive_plugin_la_SOURCES += $(libadaptive_hls_SOURCES)
libadaptive_plugin_la_SOURCES += $(libadaptive_dash_SOURCES)
libadaptive_plugin_la_SOURCES += $(libadaptive_smooth_SOURCES)
libadaptive_plugin_la_SOURCES += demux/adaptive/adaptive.cpp
libadaptive_plugin_la_CXXFLAGS = $(AM_CXXFLAGS) -I$(srcdir)/demux/adaptive
libadaptive_plugin_la_LIBADD = $(SOCKET_LIBS) $(LIBM)
libvlc_adaptive_la_SOURCES += \
mux/mp4/libmp4mux.c \
mux/mp4/libmp4mux.h \
packetizer/h264_nal.c \
packetizer/hevc_nal.c
libvlc_adaptive_la_CXXFLAGS = $(AM_CXXFLAGS) -I$(srcdir)/demux/adaptive
libvlc_adaptive_la_LIBADD = $(SOCKET_LIBS) $(LIBM)
libvlc_adaptive_la_LDFLAGS = -static
if !HAVE_WIN32
libvlc_adaptive_la_LDFLAGS += -lpthread
endif
libvlc_adaptive_la_LIBADD += libvlc_http.la
if HAVE_ZLIB
libadaptive_plugin_la_LIBADD += -lz
libvlc_adaptive_la_LIBADD += -lz
endif
if HAVE_GCRYPT
libadaptive_plugin_la_CXXFLAGS += $(GCRYPT_CFLAGS)
libadaptive_plugin_la_LIBADD += $(GCRYPT_LIBS)
libvlc_adaptive_la_CXXFLAGS += $(GCRYPT_CFLAGS)
libvlc_adaptive_la_LIBADD += $(GCRYPT_LIBS)
endif
noinst_LTLIBRARIES += libvlc_adaptive.la
libadaptive_plugin_la_SOURCES = demux/adaptive/adaptive.cpp
libadaptive_plugin_la_CXXFLAGS = $(libvlc_adaptive_la_CXXFLAGS)
libadaptive_plugin_la_LIBADD = libvlc_adaptive.la
demux_LTLIBRARIES += libadaptive_plugin.la
adaptive_test_SOURCES = \
demux/adaptive/test/logic/BufferingLogic.cpp \
demux/adaptive/test/tools/Conversions.cpp \
demux/adaptive/test/playlist/Inheritables.cpp \
demux/adaptive/test/playlist/M3U8.cpp \
demux/adaptive/test/playlist/SegmentBase.cpp \
demux/adaptive/test/playlist/SegmentList.cpp \
demux/adaptive/test/playlist/SegmentTemplate.cpp \
demux/adaptive/test/playlist/SegmentTimeline.cpp \
demux/adaptive/test/playlist/TemplatedUri.cpp \
demux/adaptive/test/plumbing/CommandsQueue.cpp \
demux/adaptive/test/test.cpp \
demux/adaptive/test/test.hpp
adaptive_test_LDADD = libvlc_adaptive.la
check_PROGRAMS += adaptive_test
TESTS += adaptive_test
libnoseek_plugin_la_SOURCES = demux/filter/noseek.c
demux_LTLIBRARIES += libnoseek_plugin.la
......@@ -29,7 +29,6 @@ namespace adaptive
public:
virtual ~AbstractSource() {}
virtual block_t *readNextBlock() = 0;
virtual std::string getContentType() = 0;
};
}
......
......@@ -54,3 +54,12 @@ std::string ID::str() const
return id;
}
const ID & Unique::getID() const
{
return id;
}
void Unique::setID(const ID &id_)
{
id = id_;
}
......@@ -37,6 +37,16 @@ namespace adaptive
private:
std::string id;
};
class Unique
{
public:
const ID & getID() const;
void setID(const ID &);
protected:
ID id;
};
}
#endif // ID_HPP
......@@ -26,7 +26,7 @@
#include "PlaylistManager.h"
#include "SegmentTracker.hpp"
#include "SharedResources.hpp"
#include "playlist/AbstractPlaylist.hpp"
#include "playlist/BasePlaylist.hpp"
#include "playlist/BasePeriod.h"
#include "playlist/BaseAdaptationSet.h"
#include "playlist/BaseRepresentation.h"
......@@ -51,25 +51,27 @@ using namespace adaptive;
PlaylistManager::PlaylistManager( demux_t *p_demux_,
SharedResources *res,
AbstractPlaylist *pl,
BasePlaylist *pl,
AbstractStreamFactory *factory,
AbstractAdaptationLogic::LogicType type ) :
logicType ( type ),
logic ( NULL ),
logic ( nullptr ),
playlist ( pl ),
streamFactory ( factory ),
p_demux ( p_demux_ )
{
currentPeriod = playlist->getFirstPeriod();
resources = res;
bufferingLogic = NULL;
bufferingLogic = nullptr;
failedupdates = 0;
b_thread = false;
b_buffering = false;
b_canceled = false;
b_preparsing = false;
nextPlaylistupdate = 0;
demux.i_nzpcr = VLC_TS_INVALID;
demux.i_firstpcr = VLC_TS_INVALID;
demux.pcr_syncpoint = TimestampSynchronizationPoint::RandomAccess;
vlc_mutex_init(&demux.lock);
vlc_cond_init(&demux.cond);
vlc_mutex_init(&lock);
......@@ -118,11 +120,9 @@ bool PlaylistManager::setupPeriod()
if(!bufferingLogic && !(bufferingLogic = createBufferingLogic()))
return false;
std::vector<BaseAdaptationSet*> sets = currentPeriod->getAdaptationSets();
std::vector<BaseAdaptationSet*>::iterator it;
for(it=sets.begin();it!=sets.end();++it)
const std::vector<BaseAdaptationSet*> &sets = currentPeriod->getAdaptationSets();
for(BaseAdaptationSet *set : sets)
{
BaseAdaptationSet *set = *it;
if(set && streamFactory)
{
SegmentTracker *tracker = new SegmentTracker(resources, logic,
......@@ -151,12 +151,14 @@ bool PlaylistManager::setupPeriod()
return true;
}
bool PlaylistManager::init()
bool PlaylistManager::init(bool b_preparsing)
{
this->b_preparsing = b_preparsing;
if(!setupPeriod())
return false;
playlist->playbackStart.Set(time(NULL));
playlist->playbackStart.Set(time(nullptr));
nextPlaylistupdate = playlist->playbackStart.Get();
updateControlsPosition();
......@@ -166,7 +168,7 @@ bool PlaylistManager::init()
bool PlaylistManager::start()
{
if(b_thread)
if(b_thread || b_preparsing)
return false;
b_thread = !vlc_clone(&thread, managerThread,
......@@ -193,14 +195,14 @@ void PlaylistManager::stop()
vlc_cond_signal(&waitcond);
vlc_mutex_unlock(&lock);
vlc_join(thread, NULL);
vlc_join(thread, nullptr);
b_thread = false;
}
}
struct PrioritizedAbstractStream
{
AbstractStream::buffering_status status;
AbstractStream::BufferingStatus status;
mtime_t demuxed_amount;
AbstractStream *st;
};
......@@ -217,10 +219,12 @@ static bool streamCompare(const PrioritizedAbstractStream &a, const Prioritized
return false;
}
AbstractStream::buffering_status PlaylistManager::bufferize(mtime_t i_nzdeadline,
unsigned i_min_buffering, unsigned i_extra_buffering)
AbstractStream::BufferingStatus PlaylistManager::bufferize(mtime_t i_nzdeadline,
mtime_t i_min_buffering,
mtime_t i_max_buffering,
mtime_t i_target_buffering)
{
AbstractStream::buffering_status i_return = AbstractStream::buffering_end;
AbstractStream::BufferingStatus i_return = AbstractStream::BufferingStatus::End;
/* First reorder by status >> buffering level */
std::vector<PrioritizedAbstractStream> prioritized_streams(streams.size());
......@@ -254,21 +258,28 @@ AbstractStream::buffering_status PlaylistManager::bufferize(mtime_t i_nzdeadline
/* initial */
}
AbstractStream::buffering_status i_ret = st->bufferize(i_nzdeadline, i_min_buffering, i_extra_buffering);
if(i_return != AbstractStream::buffering_ongoing) /* Buffering streams need to keep going */
AbstractStream::BufferingStatus i_ret = st->bufferize(i_nzdeadline,
i_min_buffering,
i_max_buffering,
i_target_buffering,
getActiveStreamsCount() <= 1);
if(i_return != AbstractStream::BufferingStatus::Ongoing) /* Buffering streams need to keep going */
{
if(i_ret > i_return)
i_return = i_ret;
}
/* Bail out, will start again (high prio could be same starving stream) */
if( i_return == AbstractStream::buffering_lessthanmin )
if( i_return == AbstractStream::BufferingStatus::Lessthanmin )
break;
}
vlc_mutex_lock(&demux.lock);
if(demux.i_nzpcr == VLC_TS_INVALID &&
i_return != AbstractStream::buffering_lessthanmin /* prevents starting before buffering is reached */ )
/* don't wait minbuffer on simple discontinuity or restart */
(demux.pcr_syncpoint == TimestampSynchronizationPoint::Discontinuity ||
/* prevents starting before buffering is reached */
i_return != AbstractStream::BufferingStatus::Lessthanmin ))
{
demux.i_nzpcr = getFirstDTS();
}
......@@ -277,9 +288,9 @@ AbstractStream::buffering_status PlaylistManager::bufferize(mtime_t i_nzdeadline
return i_return;
}
AbstractStream::status PlaylistManager::dequeue(mtime_t i_floor, mtime_t *pi_nzbarrier)
AbstractStream::Status PlaylistManager::dequeue(mtime_t i_floor, mtime_t *pi_nzbarrier)
{
AbstractStream::status i_return = AbstractStream::status_eof;
AbstractStream::Status i_return = AbstractStream::Status::Eof;
const mtime_t i_nzdeadline = *pi_nzbarrier;
......@@ -287,9 +298,8 @@ AbstractStream::status PlaylistManager::dequeue(mtime_t i_floor, mtime_t *pi_nzb
for(it=streams.begin(); it!=streams.end(); ++it)
{
AbstractStream *st = *it;
mtime_t i_pcr;
AbstractStream::status i_ret = st->dequeue(i_nzdeadline, &i_pcr);
AbstractStream::Status i_ret = st->dequeue(i_nzdeadline, &i_pcr);
if( i_ret > i_return )
i_return = i_ret;
......@@ -300,43 +310,12 @@ AbstractStream::status PlaylistManager::dequeue(mtime_t i_floor, mtime_t *pi_nzb
return i_return;
}
void PlaylistManager::drain()
{
for(;;)
{
bool b_drained = true;
std::vector<AbstractStream *>::iterator it;
for(it=streams.begin(); it!=streams.end(); ++it)
{
AbstractStream *st = *it;
if (!st->isValid() || st->isDisabled())
continue;
b_drained &= st->decodersDrained();
}
if(b_drained)
break;
msleep(20*1000); /* ugly, but we have no way to get feedback */
}
es_out_Control(p_demux->out, ES_OUT_RESET_PCR);
}
mtime_t PlaylistManager::getPCR() const
mtime_t PlaylistManager::getResumeTime() const
{
mtime_t minpcr = VLC_TS_INVALID;
std::vector<AbstractStream *>::const_iterator it;
for(it=streams.begin(); it!=streams.end(); ++it)
{
const mtime_t pcr = (*it)->getPCR();
if(minpcr == VLC_TS_INVALID)
minpcr = pcr;
else if(pcr > VLC_TS_INVALID)
minpcr = std::min(minpcr, pcr);
}
return minpcr;
vlc_mutex_lock(const_cast<vlc_mutex_t *>(&demux.lock));
mtime_t pcr = demux.i_nzpcr;
vlc_mutex_unlock(const_cast<vlc_mutex_t *>(&demux.lock));
return pcr;
}
mtime_t PlaylistManager::getFirstDTS() const
......@@ -354,6 +333,18 @@ mtime_t PlaylistManager::getFirstDTS() const
return mindts;
}
unsigned PlaylistManager::getActiveStreamsCount() const
{
unsigned count = 0;
std::vector<AbstractStream *>::const_iterator it;
for(it=streams.begin(); it!=streams.end(); ++it)
{
if((*it)->isValid() && !(*it)->isDisabled())
count++;
}
return count;
}
bool PlaylistManager::setPosition(mtime_t time)
{
bool ret = true;
......@@ -382,6 +373,16 @@ bool PlaylistManager::setPosition(mtime_t time)
return ret;
}
void PlaylistManager::setLivePause(bool b)
{
if(!started())
return;
for(AbstractStream* st : streams)
if(st->isValid() && !st->isDisabled())
st->setLivePause(b);
}
bool PlaylistManager::needsUpdate() const
{
return playlist->needsUpdates() &&
......@@ -403,20 +404,30 @@ bool PlaylistManager::updatePlaylist()
return true;
}
mtime_t PlaylistManager::getFirstPlaybackTime() const
{
return 0;
}
mtime_t PlaylistManager::getCurrentDemuxTime() const
{
vlc_mutex_locker locker(const_cast<vlc_mutex_t *>(&demux.lock));
return demux.i_nzpcr;
}
mtime_t PlaylistManager::getMinAheadTime() const
{
mtime_t minbuffer = 0;
std::for_each(streams.cbegin(), streams.cend(),
[&minbuffer](const AbstractStream *st) {
if(st->isValid() && !st->isDisabled() && st->isSelected())
{
const mtime_t m = st->getMinAheadTime();
if(m > 0 && (m < minbuffer || minbuffer == 0))
minbuffer = m;
}
});
return minbuffer;
}
bool PlaylistManager::reactivateStream(AbstractStream *stream)
{
return stream->reactivate(getPCR());
return stream->reactivate(getResumeTime());
}
#define DEMUX_INCREMENT (CLOCK_FREQ / 20)
......@@ -444,7 +455,7 @@ int PlaylistManager::doDemux(int64_t increment)
if(!b_dead)
vlc_cond_timedwait(&demux.cond, &demux.lock, mdate() + CLOCK_FREQ / 20);
vlc_mutex_unlock(&demux.lock);
return (b_dead || b_all_disabled) ? AbstractStream::status_eof : AbstractStream::status_buffering;
return (b_dead || b_all_disabled) ? VLC_DEMUXER_EOF : VLC_DEMUXER_SUCCESS;
}
if(demux.i_firstpcr == VLC_TS_INVALID)
......@@ -453,13 +464,13 @@ int PlaylistManager::doDemux(int64_t increment)
mtime_t i_nzbarrier = demux.i_nzpcr + increment;
vlc_mutex_unlock(&demux.lock);
AbstractStream::status status = dequeue(demux.i_nzpcr, &i_nzbarrier);
AbstractStream::Status status = dequeue(demux.i_nzpcr, &i_nzbarrier);
updateControlsPosition();
switch(status)
{
case AbstractStream::status_eof:
case AbstractStream::Status::Eof:
{
/* might be end of current period */
if(currentPeriod)
......@@ -481,19 +492,20 @@ int PlaylistManager::doDemux(int64_t increment)
}
}
break;
case AbstractStream::status_buffering:
case AbstractStream::Status::Buffering:
vlc_mutex_lock(&demux.lock);
vlc_cond_timedwait(&demux.cond, &demux.lock, mdate() + CLOCK_FREQ / 20);
vlc_mutex_unlock(&demux.lock);
break;
case AbstractStream::status_discontinuity:
case AbstractStream::Status::Discontinuity:
vlc_mutex_lock(&demux.lock);
demux.i_nzpcr = VLC_TS_INVALID;
demux.i_firstpcr = VLC_TS_INVALID;
demux.pcr_syncpoint = TimestampSynchronizationPoint::Discontinuity;
es_out_Control(p_demux->out, ES_OUT_RESET_PCR);
vlc_mutex_unlock(&demux.lock);
break;
case AbstractStream::status_demuxed:
case AbstractStream::Status::Demuxed:
vlc_mutex_lock(&demux.lock);
if( demux.i_nzpcr != VLC_TS_INVALID && i_nzbarrier != demux.i_nzpcr )
{
......@@ -534,8 +546,30 @@ int PlaylistManager::doControl(int i_query, va_list args)
case DEMUX_SET_PAUSE_STATE:
{
vlc_mutex_locker locker(&cached.lock);
return cached.b_live ? VLC_EGENERIC : VLC_SUCCESS;
setBufferingRunState(false); /* /!\ always stop buffering process first */
bool b_pause = (bool)va_arg(args, int);
if(playlist->isLive())
{
mtime_t now = mdate();
demux.i_nzpcr = VLC_TS_INVALID;
cached.lastupdate = 0;
if(b_pause)
{
setLivePause(true);
pause_start = now;
msg_Dbg(p_demux,"Buffering and playback paused. No timeshift support.");
}
else
{
setLivePause(false);
msg_Dbg(p_demux,"Resuming buffering/playback after %" PRId64 "ms",
(now-pause_start) / 1000);
es_out_Control(p_demux->out, ES_OUT_RESET_PCR);
setBufferingRunState(true);
}
}
else setBufferingRunState(true);
return VLC_SUCCESS;
}
case DEMUX_GET_TIME:
......@@ -565,7 +599,7 @@ int PlaylistManager::doControl(int i_query, va_list args)
case DEMUX_SET_POSITION:
{
setBufferingRunState(false); /* stop downloader first */
setBufferingRunState(false); /* /!\ always stop buffering process first */
vlc_mutex_locker locker(&cached.lock);
if(cached.playlistLength == 0)
......@@ -586,6 +620,7 @@ int PlaylistManager::doControl(int i_query, va_list args)
return VLC_EGENERIC;
}
demux.pcr_syncpoint = TimestampSynchronizationPoint::RandomAccess;
demux.i_nzpcr = VLC_TS_INVALID;
cached.lastupdate = 0;
setBufferingRunState(true);
......@@ -596,7 +631,7 @@ int PlaylistManager::doControl(int i_query, va_list args)
{
setBufferingRunState(false); /* stop downloader first */
int64_t time = va_arg(args, int64_t);// + getFirstPlaybackTime();
mtime_t time = va_arg(args, int64_t);
if(!setPosition(time))
{
setBufferingRunState(true);
......@@ -604,6 +639,7 @@ int PlaylistManager::doControl(int i_query, va_list args)
}
vlc_mutex_locker locker(&cached.lock);
demux.pcr_syncpoint = TimestampSynchronizationPoint::RandomAccess;
demux.i_nzpcr = VLC_TS_INVALID;
cached.lastupdate = 0;
setBufferingRunState(true);
......@@ -632,7 +668,8 @@ void PlaylistManager::Run()
{
vlc_mutex_lock(&lock);
const mtime_t i_min_buffering = bufferingLogic->getMinBuffering(playlist);
const mtime_t i_extra_buffering = bufferingLogic->getMaxBuffering(playlist) - i_min_buffering;
const mtime_t i_max_buffering = bufferingLogic->getMaxBuffering(playlist);
const mtime_t i_target_buffering = bufferingLogic->getStableBuffering(playlist);
while(1)
{
while(!b_buffering && !b_canceled)
......@@ -642,37 +679,34 @@ void PlaylistManager::Run()
if(needsUpdate())
{
int canc = vlc_savecancel();
if(updatePlaylist())
scheduleNextUpdate();
else
failedupdates++;
vlc_restorecancel(canc);
}
vlc_mutex_lock(&demux.lock);
mtime_t i_nzpcr = demux.i_nzpcr;
vlc_mutex_unlock(&demux.lock);
int canc = vlc_savecancel();
AbstractStream::buffering_status i_return = bufferize(i_nzpcr, i_min_buffering, i_extra_buffering);
vlc_restorecancel( canc );
AbstractStream::BufferingStatus i_return = bufferize(i_nzpcr, i_min_buffering,
i_max_buffering, i_target_buffering);
if(i_return != AbstractStream::buffering_lessthanmin)
if(i_return != AbstractStream::BufferingStatus::Lessthanmin)
{
mtime_t i_deadline = mdate();
if(i_return == AbstractStream::buffering_ongoing)
if(i_return == AbstractStream::BufferingStatus::Ongoing)
i_deadline += (CLOCK_FREQ / 100);
else if(i_return == AbstractStream::buffering_full)
else if(i_return == AbstractStream::BufferingStatus::Full)
i_deadline += (CLOCK_FREQ / 10);
else if(i_return == AbstractStream::buffering_end)
else if(i_return == AbstractStream::BufferingStatus::End)
i_deadline += (CLOCK_FREQ);
else /*if(i_return == AbstractStream::buffering_suspended)*/
else /*if(i_return == AbstractStream::BufferingStatus::suspended)*/
i_deadline += (CLOCK_FREQ / 4);
vlc_mutex_lock(&demux.lock);
// TODO: The current function doesn't seem to modify shared
// state under demux lock.
vlc_cond_signal(&demux.cond);
vlc_mutex_unlock(&demux.lock);
while(b_buffering &&
vlc_cond_timedwait(&waitcond, &lock, i_deadline) == 0 &&
......@@ -688,14 +722,14 @@ void PlaylistManager::Run()
void * PlaylistManager::managerThread(void *opaque)
{
static_cast<PlaylistManager *>(opaque)->Run();
return NULL;
return nullptr;
}
void PlaylistManager::updateControlsPosition()
{
vlc_mutex_locker locker(&cached.lock);
time_t now = time(NULL);
time_t now = time(nullptr);
if(now - cached.lastupdate < 1)
return;
cached.lastupdate = now;
......@@ -789,22 +823,22 @@ void PlaylistManager::updateControlsPosition()
AbstractAdaptationLogic *PlaylistManager::createLogic(AbstractAdaptationLogic::LogicType type, AbstractConnectionManager *conn)
{
vlc_object_t *obj = VLC_OBJECT(p_demux);
AbstractAdaptationLogic *logic = NULL;
AbstractAdaptationLogic *logic = nullptr;
switch(type)
{
case AbstractAdaptationLogic::FixedRate:
case AbstractAdaptationLogic::LogicType::FixedRate:
{
size_t bps = var_InheritInteger(p_demux, "adaptive-bw") * 8192;
logic = new (std::nothrow) FixedRateAdaptationLogic(obj, bps);
break;
}
case AbstractAdaptationLogic::AlwaysLowest:
case AbstractAdaptationLogic::LogicType::AlwaysLowest:
logic = new (std::nothrow) AlwaysLowestAdaptationLogic(obj);
break;
case AbstractAdaptationLogic::AlwaysBest:
case AbstractAdaptationLogic::LogicType::AlwaysBest:
logic = new (std::nothrow) AlwaysBestAdaptationLogic(obj);
break;
case AbstractAdaptationLogic::RateBased:
case AbstractAdaptationLogic::LogicType::RateBased:
{
RateBasedAdaptationLogic *ratelogic =
new (std::nothrow) RateBasedAdaptationLogic(obj);
......@@ -813,8 +847,8 @@ AbstractAdaptationLogic *PlaylistManager::createLogic(AbstractAdaptationLogic::L
logic = ratelogic;
break;
}
case AbstractAdaptationLogic::Default:
case AbstractAdaptationLogic::NearOptimal:
case AbstractAdaptationLogic::LogicType::Default:
case AbstractAdaptationLogic::LogicType::NearOptimal:
{
NearOptimalAdaptationLogic *noplogic =
new (std::nothrow) NearOptimalAdaptationLogic(obj);
......@@ -823,7 +857,7 @@ AbstractAdaptationLogic *PlaylistManager::createLogic(AbstractAdaptationLogic::L
logic = noplogic;
break;
}
case AbstractAdaptationLogic::Predictive:
case AbstractAdaptationLogic::LogicType::Predictive:
{
AbstractAdaptationLogic *predictivelogic =
new (std::nothrow) PredictiveAdaptationLogic(obj);
......
......@@ -30,7 +30,7 @@ namespace adaptive
{
namespace playlist
{
class AbstractPlaylist;
class BasePlaylist;
class BasePeriod;
}
......@@ -47,19 +47,19 @@ namespace adaptive
public:
PlaylistManager( demux_t *,
SharedResources *,
AbstractPlaylist *,
BasePlaylist *,
AbstractStreamFactory *,
AbstractAdaptationLogic::LogicType type );
virtual ~PlaylistManager ();
bool init();
bool init(bool = false);
bool start();
bool started() const;
void stop();
AbstractStream::buffering_status bufferize(mtime_t, unsigned, unsigned);
AbstractStream::status dequeue(mtime_t, mtime_t *);
void drain();
AbstractStream::BufferingStatus bufferize(mtime_t, mtime_t,
mtime_t, mtime_t);
AbstractStream::Status dequeue(mtime_t, mtime_t *);
virtual bool needsUpdate() const;
virtual bool updatePlaylist();
......@@ -74,12 +74,14 @@ namespace adaptive
virtual int doControl(int, va_list);
virtual int doDemux(int64_t);
void setLivePause(bool);
virtual bool setPosition(mtime_t);
mtime_t getPCR() const;
mtime_t getResumeTime() const;
mtime_t getFirstDTS() const;
unsigned getActiveStreamsCount() const;
virtual mtime_t getFirstPlaybackTime() const;
mtime_t getCurrentDemuxTime() const;
mtime_t getMinAheadTime() const;
virtual bool reactivateStream(AbstractStream *);
bool setupPeriod();
......@@ -96,15 +98,22 @@ namespace adaptive
AbstractAdaptationLogic::LogicType logicType;
AbstractAdaptationLogic *logic;
AbstractBufferingLogic *bufferingLogic;
AbstractPlaylist *playlist;
BasePlaylist *playlist;
AbstractStreamFactory *streamFactory;
demux_t *p_demux;
std::vector<AbstractStream *> streams;
BasePeriod *currentPeriod;
enum class TimestampSynchronizationPoint
{
RandomAccess,
Discontinuity,
};
/* shared with demux/buffering */
struct
{
TimestampSynchronizationPoint pcr_syncpoint;
mtime_t i_nzpcr;
mtime_t i_firstpcr;
vlc_mutex_t lock;
......@@ -138,6 +147,8 @@ namespace adaptive
vlc_cond_t waitcond;
bool b_buffering;
bool b_canceled;
mtime_t pause_start;
bool b_preparsing;
};
}
......
......@@ -23,7 +23,7 @@
#endif
#include "SegmentTracker.hpp"
#include "playlist/AbstractPlaylist.hpp"
#include "playlist/BasePlaylist.hpp"
#include "playlist/BaseRepresentation.h"
#include "playlist/BaseAdaptationSet.h"
#include "playlist/Segment.h"
......@@ -38,46 +38,74 @@ using namespace adaptive;
using namespace adaptive::logic;
using namespace adaptive::playlist;
SegmentTrackerEvent::SegmentTrackerEvent(SegmentChunk *s)
TrackerEvent::TrackerEvent(Type t)
{
type = DISCONTINUITY;
u.discontinuity.sc = s;
type = t;
}
SegmentTrackerEvent::SegmentTrackerEvent(BaseRepresentation *prev, BaseRepresentation *next)
TrackerEvent::~TrackerEvent()
{
type = SWITCHING;
u.switching.prev = prev;
u.switching.next = next;
}
TrackerEvent::Type TrackerEvent::getType() const
{
return type;
}
DiscontinuityEvent::DiscontinuityEvent()
: TrackerEvent(Type::Discontinuity)
{
}
SegmentTrackerEvent::SegmentTrackerEvent(const StreamFormat *fmt)
RepresentationSwitchEvent::RepresentationSwitchEvent(BaseRepresentation *prev,
BaseRepresentation *next)
: TrackerEvent(Type::RepresentationSwitch)
{
type = FORMATCHANGE;
u.format.f = fmt;
this->prev = prev;
this->next = next;
}
SegmentTrackerEvent::SegmentTrackerEvent(const ID &id, bool enabled)
FormatChangedEvent::FormatChangedEvent(const StreamFormat *f)
: TrackerEvent(Type::FormatChange)
{
type = BUFFERING_STATE;
u.buffering.enabled = enabled;
u.buffering.id = &id;
this->format = f;
}
SegmentTrackerEvent::SegmentTrackerEvent(const ID &id, mtime_t min, mtime_t current, mtime_t target)
SegmentChangedEvent::SegmentChangedEvent(const ID &id, mtime_t starttime,
mtime_t duration, mtime_t displaytime)
: TrackerEvent(Type::SegmentChange)
{
type = BUFFERING_LEVEL_CHANGE;
u.buffering_level.minimum = min;
u.buffering_level.current = current;
u.buffering_level.target = target;
u.buffering.id = &id;
this->id = &id;
this->duration = duration;
this->starttime = starttime;
this->displaytime = displaytime;
}
SegmentTrackerEvent::SegmentTrackerEvent(const ID &id, mtime_t duration)
BufferingStateUpdatedEvent::BufferingStateUpdatedEvent(const ID &id, bool enabled)
: TrackerEvent(Type::BufferingStateUpdate)
{
type = SEGMENT_CHANGE;
u.segment.duration = duration;
u.segment.id = &id;
this->id = &id;
this->enabled = enabled;
}
BufferingLevelChangedEvent::BufferingLevelChangedEvent(const ID &id,
mtime_t minimum, mtime_t maximum,
mtime_t current, mtime_t target)
: TrackerEvent(Type::BufferingLevelChange)
{
this->id = &id;
this->minimum = minimum;
this->maximum = maximum;
this->current = current;
this->target = target;
}
PositionChangedEvent::PositionChangedEvent()
: TrackerEvent(Type::PositionChange)
{
}
SegmentTracker::SegmentTracker(SharedResources *res,
......@@ -91,7 +119,7 @@ SegmentTracker::SegmentTracker(SharedResources *res,
bufferingLogic = bl;
setAdaptationLogic(logic_);
adaptationSet = adaptSet;
format = StreamFormat::UNKNOWN;
format = StreamFormat::Type::Unknown;
}
SegmentTracker::~SegmentTracker()
......@@ -102,7 +130,7 @@ SegmentTracker::~SegmentTracker()
SegmentTracker::Position::Position()
{
number = std::numeric_limits<uint64_t>::max();
rep = NULL;
rep = nullptr;
init_sent = false;
index_sent = false;
}
......@@ -118,7 +146,7 @@ SegmentTracker::Position::Position(BaseRepresentation *rep, uint64_t number)
bool SegmentTracker::Position::isValid() const
{
return number != std::numeric_limits<uint64_t>::max() &&
rep != NULL;
rep != nullptr;
}
std::string SegmentTracker::Position::toString() const
......@@ -160,7 +188,7 @@ StreamFormat SegmentTracker::getCurrentFormat() const
{
BaseRepresentation *rep = current.rep;
if(!rep)
rep = logic->getNextRepresentation(adaptationSet, NULL);
rep = logic->getNextRepresentation(adaptationSet, nullptr);
if(rep)
{
/* Ensure ephemere content is updated/loaded */
......@@ -171,24 +199,13 @@ StreamFormat SegmentTracker::getCurrentFormat() const
return StreamFormat();
}
std::list<std::string> SegmentTracker::getCurrentCodecs() const
void SegmentTracker::getCodecsDesc(CodecDescriptionList *descs) const
{
BaseRepresentation *rep = current.rep;
if(!rep)
rep = logic->getNextRepresentation(adaptationSet, NULL);
rep = logic->getNextRepresentation(adaptationSet, nullptr);
if(rep)
return rep->getCodecs();
return std::list<std::string>();
}
const std::string & SegmentTracker::getStreamDescription() const
{
return adaptationSet->description.Get();
}
const std::string & SegmentTracker::getStreamLanguage() const
{
return adaptationSet->getLang();
rep->getCodecsDesc(descs);
}
const Role & SegmentTracker::getStreamRole() const
......@@ -198,126 +215,186 @@ const Role & SegmentTracker::getStreamRole() const
void SegmentTracker::reset()
{
notify(SegmentTrackerEvent(current.rep, NULL));
notify(RepresentationSwitchEvent(current.rep, nullptr));
current = Position();
next = Position();
resetChunksSequence();
initializing = true;
format = StreamFormat::UNKNOWN;
format = StreamFormat::Type::Unknown;
}
SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed,
AbstractConnectionManager *connManager)
SegmentTracker::ChunkEntry::ChunkEntry()
{
chunk = nullptr;
}
SegmentTracker::ChunkEntry::ChunkEntry(SegmentChunk *c, Position p, mtime_t s, mtime_t d, mtime_t dt)
{
ISegment *segment;
chunk = c;
pos = p;
duration = d;
starttime = s;
displaytime = dt;
}
bool SegmentTracker::ChunkEntry::isValid() const
{
return chunk && pos.isValid();
}
SegmentTracker::ChunkEntry
SegmentTracker::prepareChunk(bool switch_allowed, Position pos,
AbstractConnectionManager *connManager) const
{
if(!adaptationSet)
return NULL;
return ChunkEntry();
bool b_updated = false;
bool b_switched = false;
/* starting */
if(!next.isValid())
if(!pos.isValid())
{
next = getStartPosition();
b_switched = true;
pos = getStartPosition();
if(!pos.isValid())
return ChunkEntry();
}
else /* continuing, or seek */
{
if(!current.isValid() || !adaptationSet->isSegmentAligned() || initializing)
if(!adaptationSet->isSegmentAligned() || !pos.init_sent || !pos.index_sent)
switch_allowed = false;
if(switch_allowed)
{
Position temp;
temp.rep = logic->getNextRepresentation(adaptationSet, next.rep);
if(temp.rep && temp.rep != next.rep)
temp.rep = logic->getNextRepresentation(adaptationSet, pos.rep);
if(temp.rep && temp.rep != pos.rep)
{
/* Ensure ephemere content is updated/loaded */
if(temp.rep->needsUpdate(next.number))
if(temp.rep->needsUpdate(pos.number))
b_updated = temp.rep->runLocalUpdates(resources);
/* if we need to translate pos */
if(!temp.rep->consistentSegmentNumber())
{
/* Convert our segment number */
temp.number = temp.rep->translateSegmentNumber(next.number, next.rep);
temp.number = temp.rep->translateSegmentNumber(pos.number, pos.rep);
}
else temp.number = next.number;
else temp.number = pos.number;
}
if(temp.isValid())
{
next = temp;
b_switched = current.isValid();
}
pos = temp;
}
}
if(!next.isValid())
return NULL;
ISegment *segment = nullptr;
if(b_switched)
pos.rep->scheduleNextUpdate(pos.number, b_updated);
if(!pos.init_sent)
{
notify(SegmentTrackerEvent(current.rep, next.rep));
initializing = true;
assert(!next.index_sent);
assert(!next.init_sent);
segment = pos.rep->getInitSegment();
if(!segment)
++pos;
}
if(!segment && !pos.index_sent)
{
if(pos.rep->needsIndex())
segment = pos.rep->getIndexSegment();
if(!segment)
++pos;
}
next.rep->scheduleNextUpdate(next.number, b_updated);
current = next;
bool b_gap = true;
if(!segment)
segment = pos.rep->getNextMediaSegment(pos.number, &pos.number, &b_gap);
if(!segment)
return ChunkEntry();
SegmentChunk *segmentChunk = segment->toChunk(resources, connManager, pos.number, pos.rep);
if(!segmentChunk)
return ChunkEntry();
if(current.rep->getStreamFormat() != format)
const Timescale timescale = pos.rep->inheritTimescale();
return ChunkEntry(segmentChunk, pos, VLC_TS_0 + timescale.ToTime(segment->startTime.Get()),
timescale.ToTime(segment->duration.Get()), segment->getDisplayTime());
}
void SegmentTracker::resetChunksSequence()
{
while(!chunkssequence.empty())
{
/* Initial format ? */
if(format == StreamFormat(StreamFormat::UNKNOWN))
{
format = current.rep->getStreamFormat();
}
else
{
format = current.rep->getStreamFormat();
notify(SegmentTrackerEvent(&format)); /* Notify new demux format */
return NULL; /* Force current demux to end */
}
delete chunkssequence.front().chunk;
chunkssequence.pop_front();
}
}
ChunkInterface * SegmentTracker::getNextChunk(bool switch_allowed,
AbstractConnectionManager *connManager)
{
if(!adaptationSet || !next.isValid())
return nullptr;
if(chunkssequence.empty())
{
ChunkEntry chunk = prepareChunk(switch_allowed, next, connManager);
chunkssequence.push_back(chunk);
}
else if(format == StreamFormat(StreamFormat::UNKNOWN) && b_switched)
ChunkEntry chunk = chunkssequence.front();
if(!chunk.isValid())
{
/* Handle the corner case when only the demuxer can know the format and
* demuxer starts after the format change (Probe != buffering) */
notify(SegmentTrackerEvent(&format)); /* Notify new demux format */
return NULL; /* Force current demux to end */
chunkssequence.pop_front();
delete chunk.chunk;
return nullptr;
}
if(format == StreamFormat(StreamFormat::UNSUPPORTED))
/* here next == wanted chunk pos */
bool b_gap = (next.number != chunk.pos.number);
const bool b_switched = (next.rep != chunk.pos.rep);
const bool b_discontinuity = chunk.chunk->discontinuity;
if(b_switched)
{
return NULL; /* Can't return chunk because no demux will be created */
notify(RepresentationSwitchEvent(next.rep, chunk.pos.rep));
initializing = true;
}
if(!current.init_sent)
/* advance or don't trigger duplicate events */
next = current = chunk.pos;
if(format == StreamFormat(StreamFormat::Type::Unsupported))
return nullptr; /* Can't return chunk because no demux will be created */
/* From this point chunk must be returned */
ChunkInterface *returnedChunk;
StreamFormat chunkformat = chunk.chunk->getStreamFormat();
/* Wrap and probe format */
if(chunkformat == StreamFormat(StreamFormat::Type::Unknown))
{
++next;
segment = current.rep->getSegment(BaseRepresentation::INFOTYPE_INIT);
if(segment)
return segment->toChunk(resources, connManager, current.number, current.rep);
current = next;
ProbeableChunk *wrappedck = new ProbeableChunk(chunk.chunk);
const uint8_t *p_peek;
size_t i_peek = wrappedck->peek(&p_peek);
chunkformat = StreamFormat(p_peek, i_peek);
/* fallback on Mime type */
if(chunkformat == StreamFormat(StreamFormat::Type::Unknown))
format = StreamFormat(chunk.chunk->getContentType());
chunk.chunk->setStreamFormat(chunkformat);
returnedChunk = wrappedck;
}
else returnedChunk = chunk.chunk;
if(!current.index_sent)
if(chunkformat != format &&
chunkformat != StreamFormat(StreamFormat::Type::Unknown))
{
++next;
segment = current.rep->getSegment(BaseRepresentation::INFOTYPE_INDEX);
if(segment)
return segment->toChunk(resources, connManager, current.number, current.rep);
current = next;
format = chunkformat;
notify(FormatChangedEvent(&format));
}
bool b_gap = false;
segment = current.rep->getNextSegment(BaseRepresentation::INFOTYPE_MEDIA,
current.number, &current.number, &b_gap);
if(!segment)
return NULL;
if(b_gap)
next = current;
/* pop position and return our chunk */
chunkssequence.pop_front();
chunk.chunk = nullptr;
if(initializing)
{
......@@ -326,40 +403,25 @@ SegmentChunk * SegmentTracker::getNextChunk(bool switch_allowed,
initializing = false;
}
SegmentChunk *chunk = segment->toChunk(resources, connManager, next.number, next.rep);
/* Notify new segment length for stats / logic */
if(chunk)
{
const Timescale timescale = next.rep->inheritTimescale();
notify(SegmentTrackerEvent(next.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));
}
if(chunk.pos.init_sent && chunk.pos.index_sent)
notify(SegmentChangedEvent(adaptationSet->getID(), chunk.starttime, chunk.duration, chunk.displaytime));
/* Handle both implicit and explicit discontinuities */
if( (b_gap && next.number) || (chunk && chunk->discontinuity) )
{
notify(SegmentTrackerEvent(chunk));
}
if(b_gap || b_discontinuity)
notify(DiscontinuityEvent());
if(chunk)
if(!b_gap)
++next;
return chunk;
return returnedChunk;
}
bool SegmentTracker::setPositionByTime(mtime_t time, bool restarted, bool tryonly)
{
Position pos = Position(current.rep, current.number);
if(!pos.isValid())
pos.rep = logic->getNextRepresentation(adaptationSet, NULL);
pos.rep = logic->getNextRepresentation(adaptationSet, nullptr);
if(!pos.rep)
return false;
......@@ -388,12 +450,14 @@ void SegmentTracker::setPosition(const Position &pos, bool restarted)
initializing = true;
current = Position();
next = pos;
resetChunksSequence();
notify(PositionChangedEvent());
}
SegmentTracker::Position SegmentTracker::getStartPosition()
SegmentTracker::Position SegmentTracker::getStartPosition() const
{
Position pos;
pos.rep = logic->getNextRepresentation(adaptationSet, NULL);
pos.rep = logic->getNextRepresentation(adaptationSet, nullptr);
if(pos.rep)
{
/* Ensure ephemere content is updated/loaded */
......@@ -423,7 +487,7 @@ mtime_t SegmentTracker::getPlaybackTime(bool b_next) const
BaseRepresentation *rep = current.rep;
if(!rep)
rep = logic->getNextRepresentation(adaptationSet, NULL);
rep = logic->getNextRepresentation(adaptationSet, nullptr);
if(rep &&
rep->getPlaybackTimeDurationBySegmentNumber(b_next ? next.number : current.number, &time, &duration))
......@@ -445,7 +509,7 @@ mtime_t SegmentTracker::getMinAheadTime() const
{
BaseRepresentation *rep = current.rep;
if(!rep)
rep = logic->getNextRepresentation(adaptationSet, NULL);
rep = logic->getNextRepresentation(adaptationSet, nullptr);
if(rep)
{
/* Ensure ephemere content is updated/loaded */
......@@ -463,12 +527,13 @@ mtime_t SegmentTracker::getMinAheadTime() const
void SegmentTracker::notifyBufferingState(bool enabled) const
{
notify(SegmentTrackerEvent(adaptationSet->getID(), enabled));
notify(BufferingStateUpdatedEvent(adaptationSet->getID(), enabled));
}
void SegmentTracker::notifyBufferingLevel(mtime_t min, mtime_t current, mtime_t target) const
void SegmentTracker::notifyBufferingLevel(mtime_t min, mtime_t max,
mtime_t current, mtime_t target) const
{
notify(SegmentTrackerEvent(adaptationSet->getID(), min, current, target));
notify(BufferingLevelChangedEvent(adaptationSet->getID(), min, max, current, target));
}
void SegmentTracker::registerListener(SegmentTrackerListenerInterface *listener)
......@@ -492,7 +557,7 @@ void SegmentTracker::updateSelected()
}
}
void SegmentTracker::notify(const SegmentTrackerEvent &event) const
void SegmentTracker::notify(const TrackerEvent &event) const
{
std::list<SegmentTrackerListenerInterface *>::const_iterator it;
for(it=listeners.begin();it != listeners.end(); ++it)
......
......@@ -21,6 +21,7 @@
#define SEGMENTTRACKER_HPP
#include "StreamFormat.hpp"
#include "playlist/CodecDescription.hpp"
#include "playlist/Role.hpp"
#include <vlc_common.h>
......@@ -34,6 +35,7 @@ namespace adaptive
namespace http
{
class AbstractConnectionManager;
class ChunkInterface;
}
namespace logic
......@@ -53,63 +55,108 @@ namespace adaptive
using namespace logic;
using namespace http;
class SegmentTrackerEvent
class TrackerEvent
{
public:
SegmentTrackerEvent(SegmentChunk *);
SegmentTrackerEvent(BaseRepresentation *, BaseRepresentation *);
SegmentTrackerEvent(const StreamFormat *);
SegmentTrackerEvent(const ID &, bool);
SegmentTrackerEvent(const ID &, mtime_t, mtime_t, mtime_t);
SegmentTrackerEvent(const ID &, mtime_t);
enum
enum class Type
{
DISCONTINUITY,
SWITCHING,
FORMATCHANGE,
BUFFERING_STATE,
BUFFERING_LEVEL_CHANGE,
SEGMENT_CHANGE,
} type;
union
{
struct
{
SegmentChunk *sc;
} discontinuity;
struct
{
BaseRepresentation *prev;
BaseRepresentation *next;
} switching;
struct
{
const StreamFormat *f;
} format;
struct
{
const ID *id;
bool enabled;
} buffering;
struct
{
const ID *id;
mtime_t minimum;
mtime_t current;
mtime_t target;
} buffering_level;
struct
{
const ID *id;
mtime_t duration;
} segment;
} u;
Discontinuity,
RepresentationSwitch,
FormatChange,
SegmentChange,
BufferingStateUpdate,
BufferingLevelChange,
PositionChange,
};
TrackerEvent() = delete;
virtual ~TrackerEvent() = 0;
Type getType() const;
protected:
TrackerEvent(Type t);
private:
Type type;
};
class DiscontinuityEvent : public TrackerEvent
{
public:
DiscontinuityEvent();
virtual ~DiscontinuityEvent() = default;
};
class RepresentationSwitchEvent : public TrackerEvent
{
public:
RepresentationSwitchEvent() = delete;
RepresentationSwitchEvent(BaseRepresentation *, BaseRepresentation *);
virtual ~RepresentationSwitchEvent() = default;
BaseRepresentation *prev;
BaseRepresentation *next;
};
class FormatChangedEvent : public TrackerEvent
{
public:
FormatChangedEvent() = delete;
FormatChangedEvent(const StreamFormat *);
virtual ~FormatChangedEvent() = default;
const StreamFormat *format;
};
class SegmentChangedEvent : public TrackerEvent
{
public:
SegmentChangedEvent() = delete;
SegmentChangedEvent(const ID &, mtime_t, mtime_t, mtime_t = VLC_TS_INVALID);
virtual ~SegmentChangedEvent() = default;
const ID *id;
mtime_t displaytime;
mtime_t starttime;
mtime_t duration;
};
class BufferingStateUpdatedEvent : public TrackerEvent
{
public:
BufferingStateUpdatedEvent() = delete;
BufferingStateUpdatedEvent(const ID &, bool);
virtual ~BufferingStateUpdatedEvent() = default;
const ID *id;
bool enabled;
};
class BufferingLevelChangedEvent : public TrackerEvent
{
public:
BufferingLevelChangedEvent() = delete;
BufferingLevelChangedEvent(const ID &,
mtime_t, mtime_t, mtime_t, mtime_t);
virtual ~BufferingLevelChangedEvent() = default;
const ID *id;
mtime_t minimum;
mtime_t maximum;
mtime_t current;
mtime_t target;
};
class PositionChangedEvent : public TrackerEvent
{
public:
PositionChangedEvent();
virtual ~PositionChangedEvent() = default;
};
class SegmentTrackerListenerInterface
{
public:
virtual void trackerEvent(const SegmentTrackerEvent &) = 0;
virtual void trackerEvent(const TrackerEvent &) = 0;
virtual ~SegmentTrackerListenerInterface() = default;
};
......@@ -137,28 +184,42 @@ namespace adaptive
};
StreamFormat getCurrentFormat() const;
std::list<std::string> getCurrentCodecs() const;
const std::string & getStreamDescription() const;
const std::string & getStreamLanguage() const;
void getCodecsDesc(CodecDescriptionList *) const;
const Role & getStreamRole() const;
void reset();
SegmentChunk* getNextChunk(bool, AbstractConnectionManager *);
ChunkInterface* getNextChunk(bool, AbstractConnectionManager *);
bool setPositionByTime(mtime_t, bool, bool);
void setPosition(const Position &, bool);
bool setStartPosition();
Position getStartPosition();
Position getStartPosition() const;
mtime_t getPlaybackTime(bool = false) const; /* Current segment start time if selected */
bool getMediaPlaybackRange(mtime_t *, mtime_t *, mtime_t *) const;
mtime_t getMinAheadTime() const;
void notifyBufferingState(bool) const;
void notifyBufferingLevel(mtime_t, mtime_t, mtime_t) const;
void notifyBufferingLevel(mtime_t, mtime_t, mtime_t, mtime_t) const;
void registerListener(SegmentTrackerListenerInterface *);
void updateSelected();
bool bufferingAvailable() const;
private:
class ChunkEntry
{
public:
ChunkEntry();
ChunkEntry(SegmentChunk *c, Position p, mtime_t s, mtime_t d, mtime_t dt);
bool isValid() const;
SegmentChunk *chunk;
Position pos;
mtime_t displaytime;
mtime_t starttime;
mtime_t duration;
};
std::list<ChunkEntry> chunkssequence;
ChunkEntry prepareChunk(bool switch_allowed, Position pos,
AbstractConnectionManager *connManager) const;
void resetChunksSequence();
void setAdaptationLogic(AbstractAdaptationLogic *);
void notify(const SegmentTrackerEvent &) const;
void notify(const TrackerEvent &) const;
bool first;
bool initializing;
Position current;
......
......@@ -24,20 +24,17 @@
#include "SharedResources.hpp"
#include "http/AuthStorage.hpp"
#include "http/HTTPConnectionManager.h"
#include "http/HTTPConnection.hpp"
#include "encryption/Keyring.hpp"
#include <vlc_common.h>
using namespace adaptive;
SharedResources::SharedResources(vlc_object_t *obj, bool local)
SharedResources::SharedResources(AuthStorage *auth, Keyring *ring,
AbstractConnectionManager *conn)
{
authStorage = new AuthStorage(obj);
encryptionKeyring = new Keyring(obj);
HTTPConnectionManager *m = new HTTPConnectionManager(obj, authStorage);
if(m && local)
m->setLocalConnectionsAllowed();
connManager = m;
authStorage = auth;
encryptionKeyring = ring;
connManager = conn;
}
SharedResources::~SharedResources()
......@@ -61,3 +58,18 @@ AbstractConnectionManager * SharedResources::getConnManager()
{
return connManager;
}
SharedResources * SharedResources::createDefault(vlc_object_t *obj,
const std::string & playlisturl)
{
AuthStorage *auth = new AuthStorage(obj);
Keyring *keyring = new Keyring(obj);
HTTPConnectionManager *m = new HTTPConnectionManager(obj);
if(!var_InheritBool(obj, "adaptive-use-access")) /* only use http from access */
m->addFactory(new LibVLCHTTPConnectionFactory(auth));
m->addFactory(new StreamUrlConnectionFactory());
ConnectionParams params(playlisturl);
if(params.isLocal())
m->setLocalConnectionsAllowed();
return new SharedResources(auth, keyring, m);
}
......@@ -21,6 +21,7 @@
#define SHAREDRESOURCES_H_
#include <vlc_common.h>
#include <string>
namespace adaptive
{
......@@ -41,11 +42,13 @@ namespace adaptive
class SharedResources
{
public:
SharedResources(vlc_object_t *, bool = false);
SharedResources(AuthStorage *, Keyring *, AbstractConnectionManager *);
~SharedResources();
AuthStorage *getAuthStorage();
Keyring *getKeyring();
AbstractConnectionManager *getConnManager();
/* Helper */
static SharedResources * createDefault(vlc_object_t *, const std::string &);
private:
AuthStorage *authStorage;
......
......@@ -35,38 +35,44 @@ extern "C"
using namespace adaptive;
StreamFormat::operator unsigned() const
StreamFormat::operator StreamFormat::Type() const
{
return formatid;
return type;
}
std::string StreamFormat::str() const
{
switch(formatid)
switch(type)
{
case MPEG2TS:
case Type::MPEG2TS:
return "TS";
case MP4:
case Type::MP4:
return "MP4";
case WEBVTT:
case Type::WebVTT:
return "WebVTT";
case TTML:
case Type::TTML:
return "Timed Text";
case PACKEDAAC:
case Type::PackedAAC:
return "Packed AAC";
case WEBM:
case Type::PackedMP3:
return "Packed MP3";
case Type::PackedAC3:
return "Packed AC-3";
case Type::WebM:
return "WebM";
case UNSUPPORTED:
case Type::Ogg:
return "Ogg";
case Type::Unsupported:
return "Unsupported";
default:
case UNKNOWN:
case Type::Unknown:
return "Unknown";
}
}
StreamFormat::StreamFormat( unsigned formatid_ )
StreamFormat::StreamFormat( Type type_ )
{
formatid = formatid_;
type = type_;
}
StreamFormat::StreamFormat( const std::string &mimetype )
......@@ -74,22 +80,26 @@ StreamFormat::StreamFormat( const std::string &mimetype )
std::string mime = mimetype;
std::transform(mime.begin(), mime.end(), mime.begin(), ::tolower);
std::string::size_type pos = mime.find("/");
formatid = UNKNOWN;
type = Type::Unknown;
if(pos != std::string::npos)
{
std::string tail = mime.substr(pos + 1);
if(tail == "mp4")
formatid = StreamFormat::MP4;
type = StreamFormat::Type::MP4;
else if(tail == "aac")
formatid = StreamFormat::PACKEDAAC;
type = StreamFormat::Type::PackedAAC;
else if(tail == "mpeg" || tail == "mp3")
type = StreamFormat::Type::PackedMP3;
else if(tail == "ac3")
type = StreamFormat::Type::PackedAC3;
else if (tail == "mp2t")
formatid = StreamFormat::MPEG2TS;
type = StreamFormat::Type::MPEG2TS;
else if (tail == "vtt")
formatid = StreamFormat::WEBVTT;
type = StreamFormat::Type::WebVTT;
else if (tail == "ttml+xml")
formatid = StreamFormat::TTML;
type = StreamFormat::Type::TTML;
else if (tail == "webm")
formatid = StreamFormat::WEBM;
type = StreamFormat::Type::WebM;
}
}
......@@ -98,30 +108,45 @@ static int ID3Callback(uint32_t, const uint8_t *, size_t, void *)
return VLC_EGENERIC;
}
static bool IsWebVTT(const char *p, size_t sz)
{
/* match optional U+FEFF BOM */
const uint8_t webvtt[] = { 0xEF, 0xBB, 0xBF, 0x57, 0x45, 0x42, 0x56, 0x54, 0x54 };
for(int i=3; i>=0; i-=3)
{
if(sz > (size_t)(10 - i) &&
!memcmp(webvtt + i, p, 9 - i) &&
(p[9 - i] == '\n' || p[9 - i] == '\r' || p[9 - i] == ' ' || p[9 - i] == '\t'))
return true;
}
return false;
}
StreamFormat::StreamFormat(const void *data_, size_t sz)
{
const uint8_t *data = reinterpret_cast<const uint8_t *>(data_);
formatid = UNKNOWN;
type = Type::Unknown;
const char moov[] = "ftypmoovmoof";
if(sz > 188 && data[0] == 0x47 && data[188] == 0x47)
formatid = StreamFormat::MPEG2TS;
type = StreamFormat::Type::MPEG2TS;
else if(sz > 8 && (!memcmp(&moov, &data[4], 4) ||
!memcmp(&moov[4], &data[4], 4) ||
!memcmp(&moov[8], &data[4], 4)))
formatid = StreamFormat::MP4;
else if(sz > 7 && !memcmp("WEBVTT", data, 6) &&
std::isspace(static_cast<unsigned char>(data[7])))
formatid = StreamFormat::WEBVTT;
type = StreamFormat::Type::MP4;
else if(IsWebVTT((const char *)data, sz))
type = StreamFormat::Type::WebVTT;
else if(sz > 4 && !memcmp("\x1A\x45\xDF\xA3", data, 4))
formatid = StreamFormat::WEBM;
type = StreamFormat::Type::WebM;
else if(sz > 4 && !memcmp("OggS", data, 4))
type = StreamFormat::Type::Ogg;
else /* Check Packet Audio formats */
{
/* It MUST have ID3 header, but HLS spec is an oxymoron */
if(sz > 10 && ID3TAG_IsTag(data, false))
while(sz > 10 && ID3TAG_IsTag(data, false))
{
size_t tagsize = ID3TAG_Parse(data, sz, ID3Callback, this);
if(tagsize >= sz)
if(tagsize >= sz || tagsize == 0)
return; /* not enough peek */
data += tagsize;
sz -= tagsize;
......@@ -130,7 +155,15 @@ StreamFormat::StreamFormat(const void *data_, size_t sz)
if(sz > 3 && (!memcmp("\xFF\xF1", data, 2) ||
!memcmp("\xFF\xF9", data, 2)))
{
formatid = StreamFormat::PACKEDAAC;
type = StreamFormat::Type::PackedAAC;
}
else if(sz > 4 && data[0] == 0xFF && (data[1] & 0xE6) > 0xE0)
{
type = StreamFormat::Type::PackedMP3;
}
else if(sz > 4 && data[0] == 0x0b && data[1] == 0x77)
{
type = StreamFormat::Type::PackedAC3;
}
}
}
......@@ -140,12 +173,22 @@ StreamFormat::~StreamFormat()
}
bool StreamFormat::operator ==(Type t) const
{
return type == t;
}
bool StreamFormat::operator !=(Type t) const
{
return type != t;
}
bool StreamFormat::operator ==(const StreamFormat &other) const
{
return formatid == other.formatid;
return type == other.type;
}
bool StreamFormat::operator !=(const StreamFormat &other) const
{
return formatid != other.formatid;
return type != other.type;
}
......@@ -28,27 +28,35 @@ namespace adaptive
class StreamFormat
{
public:
static const unsigned UNSUPPORTED = 0;
static const unsigned MPEG2TS = 1;
static const unsigned MP4 = 2;
static const unsigned WEBVTT = 3;
static const unsigned TTML = 4;
static const unsigned PACKEDAAC = 5;
static const unsigned WEBM = 6;
static const unsigned UNKNOWN = 0xFF; /* will probe */
enum class Type
{
Unsupported,
MPEG2TS,
MP4,
WebM,
Ogg,
WebVTT,
TTML,
PackedAAC,
PackedMP3,
PackedAC3,
Unknown,
};
static const unsigned PEEK_SIZE = 4096;
StreamFormat( unsigned = UNSUPPORTED );
StreamFormat( Type = Type::Unsupported );
explicit StreamFormat( const std::string &mime );
StreamFormat( const void *, size_t );
~StreamFormat();
operator unsigned() const;
operator Type() const;
std::string str() const;
bool operator==(Type) const;
bool operator!=(Type) const;
bool operator==(const StreamFormat &) const;
bool operator!=(const StreamFormat &) const;
private:
unsigned formatid;
Type type;
};
}
......
......@@ -31,7 +31,6 @@
#include "playlist/SegmentChunk.hpp"
#include "plumbing/SourceStream.hpp"
#include "plumbing/CommandsQueue.hpp"
#include "tools/FormatNamespace.hpp"
#include "tools/Debug.hpp"
#include <vlc_demux.h>
......@@ -43,8 +42,8 @@ using namespace adaptive::http;
AbstractStream::AbstractStream(demux_t * demux_)
{
p_realdemux = demux_;
format = StreamFormat::UNKNOWN;
currentChunk = NULL;
format = StreamFormat::Type::Unknown;
currentChunk = nullptr;
eof = false;
valid = true;
disabled = false;
......@@ -52,57 +51,51 @@ AbstractStream::AbstractStream(demux_t * demux_)
needrestart = false;
inrestart = false;
demuxfirstchunk = false;
segmentTracker = NULL;
demuxersource = NULL;
demuxer = NULL;
fakeesout = NULL;
segmentTracker = nullptr;
demuxersource = nullptr;
demuxer = nullptr;
fakeesout = nullptr;
notfound_sequence = 0;
last_buffer_status = buffering_lessthanmin;
last_buffer_status = BufferingStatus::Lessthanmin;
vlc_mutex_init(&lock);
}
bool AbstractStream::init(const StreamFormat &format_, SegmentTracker *tracker, AbstractConnectionManager *conn)
{
/* Don't even try if not supported or already init */
if((unsigned)format_ == StreamFormat::UNSUPPORTED || demuxersource)
if(format_ == StreamFormat::Type::Unsupported || demuxersource)
return false;
demuxersource = new (std::nothrow) BufferedChunksSourceStream( VLC_OBJECT(p_realdemux), this );
if(demuxersource)
{
CommandsFactory *factory = new (std::nothrow) CommandsFactory();
if(factory)
AbstractCommandsQueue *commandsqueue = new (std::nothrow) CommandsQueue();
if(factory && commandsqueue)
{
CommandsQueue *commandsqueue = new (std::nothrow) CommandsQueue(factory);
if(commandsqueue)
fakeesout = new (std::nothrow) FakeESOut(p_realdemux->out,
commandsqueue, factory);
if(fakeesout)
{
fakeesout = new (std::nothrow) FakeESOut(p_realdemux->out, commandsqueue);
if(fakeesout)
{
/* All successfull */
fakeesout->setExtraInfoProvider( this );
const Role & streamRole = tracker->getStreamRole();
if(streamRole.isDefault() && streamRole.autoSelectable())
fakeesout->setPriority(ES_PRIORITY_MIN + 10);
else if(!streamRole.autoSelectable())
fakeesout->setPriority(ES_PRIORITY_NOT_DEFAULTABLE);
format = format_;
segmentTracker = tracker;
segmentTracker->registerListener(this);
segmentTracker->notifyBufferingState(true);
connManager = conn;
fakeesout->setExpectedTimestamp(segmentTracker->getPlaybackTime());
declaredCodecs();
return true;
}
delete commandsqueue;
commandsqueue = NULL;
}
else
{
delete factory;
/* All successfull */
fakeesout->setExtraInfoProvider( this );
const Role & streamRole = tracker->getStreamRole();
if(streamRole.isDefault() && streamRole.autoSelectable())
fakeesout->setPriority(ES_PRIORITY_MIN + 10);
else if(!streamRole.autoSelectable())
fakeesout->setPriority(ES_PRIORITY_NOT_DEFAULTABLE);
format = format_;
segmentTracker = tracker;
segmentTracker->registerListener(this);
segmentTracker->notifyBufferingState(true);
connManager = conn;
fakeesout->setExpectedTimestamp(segmentTracker->getPlaybackTime());
declaredCodecs();
return true;
}
}
delete factory;
delete commandsqueue;
delete demuxersource;
}
......@@ -139,8 +132,39 @@ void AbstractStream::prepareRestart(bool b_discontinuity)
fakeEsOut()->commandsQueue()->setDrop(true);
delete demuxer;
fakeEsOut()->commandsQueue()->setDrop(false);
demuxer = NULL;
demuxer = nullptr;
}
}
bool AbstractStream::resetForNewPosition(mtime_t seekMediaTime)
{
// clear eof flag before restartDemux() to prevent readNextBlock() fail
eof = false;
demuxfirstchunk = true;
notfound_sequence = 0;
if(!demuxer || demuxer->needsRestartOnSeek()) /* needs (re)start */
{
delete currentChunk;
currentChunk = nullptr;
needrestart = false;
fakeEsOut()->resetTimestamps();
fakeEsOut()->setExpectedTimestamp(seekMediaTime);
if( !restartDemux() )
{
msg_Info(p_realdemux, "Restart demux failed");
eof = true;
valid = false;
return false;
}
else
{
fakeEsOut()->commandsQueue()->setEOF(false);
}
}
else fakeEsOut()->commandsQueue()->Abort( true );
return true;
}
void AbstractStream::setLanguage(const std::string &lang)
......@@ -153,14 +177,6 @@ void AbstractStream::setDescription(const std::string &desc)
description = desc;
}
mtime_t AbstractStream::getPCR() const
{
vlc_mutex_locker locker(const_cast<vlc_mutex_t *>(&lock));
if(!valid || disabled)
return VLC_TS_INVALID;
return fakeEsOut()->commandsQueue()->getPCR();
}
mtime_t AbstractStream::getMinAheadTime() const
{
if(!segmentTracker)
......@@ -232,12 +248,19 @@ bool AbstractStream::startDemux()
if(demuxer)
return false;
if(!currentChunk)
{
currentChunk = getNextChunk();
needrestart = false;
discontinuity = false;
}
demuxersource->Reset();
demuxfirstchunk = true;
demuxer = createDemux(format);
if(!demuxer && format != StreamFormat())
msg_Err(p_realdemux, "Failed to create demuxer %p %s", (void *)demuxer,
format.str().c_str());
demuxfirstchunk = true;
return !!demuxer;
}
......@@ -288,12 +311,22 @@ bool AbstractStream::isDisabled() const
return disabled;
}
void AbstractStream::setLivePause(bool b)
{
vlc_mutex_locker locker(&lock);
if(!b)
{
segmentTracker->setPosition(segmentTracker->getStartPosition(),
!demuxer || demuxer->needsRestartOnSeek());
}
}
bool AbstractStream::decodersDrained()
{
return fakeEsOut()->decodersDrained();
}
AbstractStream::buffering_status AbstractStream::getLastBufferStatus() const
AbstractStream::BufferingStatus AbstractStream::getLastBufferStatus() const
{
return last_buffer_status;
}
......@@ -303,15 +336,22 @@ mtime_t AbstractStream::getDemuxedAmount(mtime_t from) const
return fakeEsOut()->commandsQueue()->getDemuxedAmount(from);
}
AbstractStream::buffering_status AbstractStream::bufferize(mtime_t nz_deadline,
unsigned i_min_buffering, unsigned i_extra_buffering)
AbstractStream::BufferingStatus AbstractStream::bufferize(mtime_t nz_deadline,
mtime_t i_min_buffering,
mtime_t i_extra_buffering,
mtime_t i_target_buffering,
bool b_keep_alive)
{
last_buffer_status = doBufferize(nz_deadline, i_min_buffering, i_extra_buffering);
last_buffer_status = doBufferize(nz_deadline, i_min_buffering, i_extra_buffering,
i_target_buffering, b_keep_alive);
return last_buffer_status;
}
AbstractStream::buffering_status AbstractStream::doBufferize(mtime_t nz_deadline,
mtime_t i_min_buffering, mtime_t i_extra_buffering)
AbstractStream::BufferingStatus AbstractStream::doBufferize(mtime_t nz_deadline,
mtime_t i_min_buffering,
mtime_t i_max_buffering,
mtime_t i_target_buffering,
bool b_keep_alive)
{
vlc_mutex_lock(&lock);
......@@ -319,11 +359,11 @@ AbstractStream::buffering_status AbstractStream::doBufferize(mtime_t nz_deadline
if(!segmentTracker || !connManager || !valid)
{
vlc_mutex_unlock(&lock);
return AbstractStream::buffering_end;
return BufferingStatus::End;
}
/* Disable streams that are not selected (alternate streams) */
if(esCount() && !isSelected() && !fakeEsOut()->restarting())
if(esCount() && !isSelected() && !fakeEsOut()->restarting() && !b_keep_alive)
{
setDisabled(true);
segmentTracker->reset();
......@@ -331,13 +371,13 @@ AbstractStream::buffering_status AbstractStream::doBufferize(mtime_t nz_deadline
msg_Dbg(p_realdemux, "deactivating %s stream %s",
format.str().c_str(), description.c_str());
vlc_mutex_unlock(&lock);
return AbstractStream::buffering_end;
return BufferingStatus::End;
}
if(fakeEsOut()->commandsQueue()->isDraining())
{
vlc_mutex_unlock(&lock);
return AbstractStream::buffering_suspended;
return BufferingStatus::Suspended;
}
segmentTracker->setStartPosition();
......@@ -346,50 +386,38 @@ AbstractStream::buffering_status AbstractStream::doBufferize(mtime_t nz_deadline
if(!segmentTracker->bufferingAvailable())
{
vlc_mutex_unlock(&lock);
return AbstractStream::buffering_suspended;
return BufferingStatus::Suspended;
}
if(!demuxer)
{
format = segmentTracker->getCurrentFormat();
if(!startDemux())
{
/* If demux fails because of probing failure / wrong format*/
if(discontinuity)
{
msg_Dbg( p_realdemux, "Draining on format change" );
prepareRestart();
discontinuity = false;
fakeEsOut()->commandsQueue()->setDraining();
vlc_mutex_unlock(&lock);
return AbstractStream::buffering_ongoing;
}
valid = false; /* Prevent further retries */
fakeEsOut()->commandsQueue()->setEOF(true);
vlc_mutex_unlock(&lock);
return AbstractStream::buffering_end;
return BufferingStatus::End;
}
}
const int64_t i_total_buffering = i_min_buffering + i_extra_buffering;
mtime_t i_demuxed = fakeEsOut()->commandsQueue()->getDemuxedAmount(nz_deadline);
segmentTracker->notifyBufferingLevel(i_min_buffering, i_demuxed, i_total_buffering);
if(i_demuxed < i_total_buffering) /* not already demuxed */
segmentTracker->notifyBufferingLevel(i_min_buffering, i_max_buffering, i_demuxed, i_target_buffering);
if(i_demuxed < i_max_buffering) /* not already demuxed */
{
mtime_t nz_extdeadline = fakeEsOut()->commandsQueue()->getBufferingLevel() +
(i_total_buffering - i_demuxed) / 4;
nz_deadline = std::max(nz_deadline, nz_extdeadline);
(i_max_buffering - i_demuxed) / 4;
nz_deadline = std::min(nz_extdeadline, nz_deadline + CLOCK_FREQ);
/* need to read, demuxer still buffering, ... */
vlc_mutex_unlock(&lock);
Demuxer::Status demuxStatus = demuxer->demux(nz_deadline);
fakeEsOut()->scheduleNecessaryMilestone();
vlc_mutex_lock(&lock);
if(demuxStatus != Demuxer::Status::STATUS_SUCCESS)
if(demuxStatus != Demuxer::Status::Success)
{
if(discontinuity || needrestart)
{
msg_Dbg(p_realdemux, "Restarting demuxer");
msg_Dbg(p_realdemux, "Restarting demuxer %d %d", needrestart, discontinuity);
prepareRestart(discontinuity);
if(discontinuity)
{
......@@ -399,27 +427,27 @@ AbstractStream::buffering_status AbstractStream::doBufferize(mtime_t nz_deadline
}
needrestart = false;
vlc_mutex_unlock(&lock);
return AbstractStream::buffering_ongoing;
return BufferingStatus::Ongoing;
}
fakeEsOut()->commandsQueue()->setEOF(true);
vlc_mutex_unlock(&lock);
return AbstractStream::buffering_end;
return BufferingStatus::End;
}
i_demuxed = fakeEsOut()->commandsQueue()->getDemuxedAmount(nz_deadline);
segmentTracker->notifyBufferingLevel(i_min_buffering, i_demuxed, i_total_buffering);
segmentTracker->notifyBufferingLevel(i_min_buffering, i_max_buffering, i_demuxed, i_target_buffering);
}
vlc_mutex_unlock(&lock);
if(i_demuxed < i_total_buffering) /* need to read more */
if(i_demuxed < i_max_buffering) /* need to read more */
{
if(i_demuxed < i_min_buffering)
return AbstractStream::buffering_lessthanmin; /* high prio */
return AbstractStream::buffering_ongoing;
return BufferingStatus::Lessthanmin; /* high prio */
return BufferingStatus::Ongoing;
}
return AbstractStream::buffering_full;
return BufferingStatus::Full;
}
AbstractStream::status AbstractStream::dequeue(mtime_t nz_deadline, mtime_t *pi_pcr)
AbstractStream::Status AbstractStream::dequeue(mtime_t nz_deadline, mtime_t *pi_pcr)
{
vlc_mutex_locker locker(&lock);
......@@ -429,86 +457,83 @@ AbstractStream::status AbstractStream::dequeue(mtime_t nz_deadline, mtime_t *pi_
{
AdvDebug(mtime_t pcrvalue = fakeEsOut()->commandsQueue()->getPCR();
mtime_t dtsvalue = fakeEsOut()->commandsQueue()->getFirstDTS();
msg_Dbg(p_realdemux, "Stream %s pcr %" PRId64 " dts %" PRId64 " deadline %" PRId64 " [DRAINING]",
description.c_str(), pcrvalue, dtsvalue, nz_deadline));
mtime_t bufferingLevel = fakeEsOut()->commandsQueue()->getBufferingLevel();
msg_Dbg(p_realdemux, "Stream pcr %" PRId64 " dts %" PRId64 " deadline %" PRId64 " buflevel %" PRId64 "(+%" PRId64 ") [DRAINING] :%s",
pcrvalue, dtsvalue, nz_deadline, bufferingLevel,
pcrvalue ? bufferingLevel - pcrvalue : 0,
description.c_str()));
*pi_pcr = fakeEsOut()->commandsQueue()->Process(p_realdemux->out, VLC_TS_0 + nz_deadline);
*pi_pcr = fakeEsOut()->commandsQueue()->Process(VLC_TS_0 + nz_deadline);
if(!fakeEsOut()->commandsQueue()->isEmpty())
return AbstractStream::status_demuxed;
return Status::Demuxed;
if(!fakeEsOut()->commandsQueue()->isEOF())
{
fakeEsOut()->commandsQueue()->Abort(true); /* reset buffering level and flags */
return AbstractStream::status_discontinuity;
return Status::Discontinuity;
}
}
if(!valid || disabled || fakeEsOut()->commandsQueue()->isEOF())
{
*pi_pcr = nz_deadline;
return AbstractStream::status_eof;
return Status::Eof;
}
mtime_t bufferingLevel = fakeEsOut()->commandsQueue()->getBufferingLevel();
AdvDebug(mtime_t pcrvalue = fakeEsOut()->commandsQueue()->getPCR();
mtime_t dtsvalue = fakeEsOut()->commandsQueue()->getFirstDTS();
msg_Dbg(p_realdemux, "Stream %s pcr %" PRId64 " dts %" PRId64 " deadline %" PRId64 " buflevel %" PRId64,
description.c_str(), pcrvalue, dtsvalue, nz_deadline, bufferingLevel));
msg_Dbg(p_realdemux, "Stream pcr %" PRId64 " dts %" PRId64 " deadline %" PRId64 " buflevel %" PRId64 "(+%" PRId64 "): %s",
pcrvalue, dtsvalue, nz_deadline, bufferingLevel,
pcrvalue ? bufferingLevel - pcrvalue : 0,
description.c_str()));
if(nz_deadline + VLC_TS_0 <= bufferingLevel) /* demuxed */
{
*pi_pcr = fakeEsOut()->commandsQueue()->Process( p_realdemux->out, VLC_TS_0 + nz_deadline );
return AbstractStream::status_demuxed;
*pi_pcr = fakeEsOut()->commandsQueue()->Process( VLC_TS_0 + nz_deadline );
return Status::Demuxed;
}
return AbstractStream::status_buffering;
return Status::Buffering;
}
std::string AbstractStream::getContentType()
ChunkInterface * AbstractStream::getNextChunk() const
{
if (currentChunk == NULL && !eof)
{
const bool b_restarting = fakeEsOut()->restarting();
currentChunk = segmentTracker->getNextChunk(!b_restarting, connManager);
}
if(currentChunk)
return currentChunk->getContentType();
else
return std::string();
const bool b_restarting = fakeEsOut()->restarting();
return segmentTracker->getNextChunk(!b_restarting, connManager);
}
block_t * AbstractStream::readNextBlock()
{
if (currentChunk == NULL && !eof)
{
const bool b_restarting = fakeEsOut()->restarting();
currentChunk = segmentTracker->getNextChunk(!b_restarting, connManager);
}
if (currentChunk == nullptr && !eof)
currentChunk = getNextChunk();
if(discontinuity && demuxfirstchunk)
if(demuxfirstchunk)
{
/* clear up discontinuity on demux start (discontinuity on start segment bug) */
discontinuity = false;
needrestart = false;
}
if(discontinuity || needrestart)
else if(discontinuity || needrestart)
{
msg_Info(p_realdemux, "Encountered discontinuity");
msg_Info(p_realdemux, "Ending demuxer stream. %s%s",
discontinuity ? "[discontinuity]" : "",
needrestart ? "[needrestart]" : "");
/* Force stream/demuxer to end for this call */
return NULL;
return nullptr;
}
if(currentChunk == NULL)
if(currentChunk == nullptr)
{
eof = true;
return NULL;
return nullptr;
}
const bool b_segment_head_chunk = (currentChunk->getBytesRead() == 0);
block_t *block = currentChunk->readBlock();
if(block == NULL)
if(block == nullptr)
{
if(currentChunk->getRequestStatus() == RequestStatus::NotFound &&
++notfound_sequence < 3)
......@@ -516,17 +541,17 @@ block_t * AbstractStream::readNextBlock()
discontinuity = true;
}
delete currentChunk;
currentChunk = NULL;
return NULL;
currentChunk = nullptr;
return nullptr;
}
else notfound_sequence = 0;
demuxfirstchunk = false;
if (currentChunk->isEmpty())
if (!currentChunk->hasMoreData())
{
delete currentChunk;
currentChunk = NULL;
currentChunk = nullptr;
}
block = checkBlock(block, b_segment_head_chunk);
......@@ -543,35 +568,6 @@ bool AbstractStream::setPosition(mtime_t time, bool tryonly)
bool ret = segmentTracker->setPositionByTime(time, b_needs_restart, tryonly);
if(!tryonly && ret)
{
// clear eof flag before restartDemux() to prevent readNextBlock() fail
eof = false;
demuxfirstchunk = true;
notfound_sequence = 0;
if(b_needs_restart)
{
if(currentChunk)
delete currentChunk;
currentChunk = NULL;
needrestart = false;
fakeEsOut()->resetTimestamps();
mtime_t seekMediaTime = segmentTracker->getPlaybackTime(true);
fakeEsOut()->setExpectedTimestamp(seekMediaTime);
if( !restartDemux() )
{
msg_Info(p_realdemux, "Restart demux failed");
eof = true;
valid = false;
ret = false;
}
else
{
fakeEsOut()->commandsQueue()->setEOF(false);
}
}
else fakeEsOut()->commandsQueue()->Abort( true );
// in some cases, media time seek != sent dts
// es_out_Control(p_realdemux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
// VLC_TS_0 + time);
......@@ -609,7 +605,7 @@ AbstractDemuxer * AbstractStream::createDemux(const StreamFormat &format)
if(ret && !ret->create())
{
delete ret;
ret = NULL;
ret = nullptr;
}
else fakeEsOut()->commandsQueue()->Commit();
......@@ -619,115 +615,95 @@ AbstractDemuxer * AbstractStream::createDemux(const StreamFormat &format)
AbstractDemuxer *AbstractStream::newDemux(vlc_object_t *p_obj, const StreamFormat &format,
es_out_t *out, AbstractSourceStream *source) const
{
AbstractDemuxer *ret = NULL;
switch((unsigned)format)
AbstractDemuxer *ret = nullptr;
switch(format)
{
case StreamFormat::MP4:
case StreamFormat::Type::MP4:
ret = new Demuxer(p_obj, "mp4", out, source);
break;
case StreamFormat::MPEG2TS:
case StreamFormat::Type::MPEG2TS:
ret = new Demuxer(p_obj, "ts", out, source);
break;
default:
case StreamFormat::UNSUPPORTED:
case StreamFormat::Type::Unsupported:
break;
}
return ret;
}
void AbstractStream::trackerEvent(const SegmentTrackerEvent &event)
void AbstractStream::trackerEvent(const TrackerEvent &ev)
{
switch(event.type)
switch(ev.getType())
{
case SegmentTrackerEvent::DISCONTINUITY:
case TrackerEvent::Type::Discontinuity:
discontinuity = true;
break;
case SegmentTrackerEvent::FORMATCHANGE:
case TrackerEvent::Type::FormatChange:
{
const FormatChangedEvent &event =
static_cast<const FormatChangedEvent &>(ev);
/* Check if our current demux is still valid */
if(*event.u.format.f != format || format == StreamFormat(StreamFormat::UNKNOWN))
if(*event.format != format)
{
/* Format has changed between segments, we need to drain and change demux */
msg_Info(p_realdemux, "Changing stream format %s -> %s",
format.str().c_str(), event.u.format.f->str().c_str());
format = *event.u.format.f;
/* This is an implict discontinuity */
discontinuity = true;
format.str().c_str(), event.format->str().c_str());
format = *event.format;
needrestart = true;
}
}
break;
case SegmentTrackerEvent::SWITCHING:
if(demuxer && !inrestart)
case TrackerEvent::Type::RepresentationSwitch:
{
const RepresentationSwitchEvent &event =
static_cast<const RepresentationSwitchEvent &>(ev);
if(demuxer && !inrestart && event.prev)
{
if(!demuxer->bitstreamSwitchCompatible() ||
(event.u.switching.next &&
!event.u.switching.next->getAdaptationSet()->isBitSwitchable()))
/* HLS variants can move from TS to Raw AAC */
format == StreamFormat(StreamFormat::Type::Unknown) ||
(event.next &&
!event.next->getAdaptationSet()->isBitSwitchable()))
needrestart = true;
}
AdvDebug(msg_Dbg(p_realdemux, "Stream %s switching %s %s to %s %s",
description.c_str(),
event.prev ? event.prev->getID().str().c_str() : "",
event.prev ? event.prev->getStreamFormat().str().c_str() : "",
event.next ? event.next->getID().str().c_str() : "",
event.next ? event.next->getStreamFormat().str().c_str() : ""));
}
break;
case SegmentTrackerEvent::SEGMENT_CHANGE:
case TrackerEvent::Type::SegmentChange:
if(demuxer && demuxer->needsRestartOnEachSegment() && !inrestart)
{
needrestart = true;
}
break;
case TrackerEvent::Type::PositionChange:
resetForNewPosition(segmentTracker->getPlaybackTime(true));
break;
default:
break;
}
}
static void add_codec_string_from_fourcc(vlc_fourcc_t fourcc,
std::list<std::string> &codecs)
{
std::string codec;
codec.insert(0, reinterpret_cast<const char *>(&fourcc), 4);
codecs.push_back(codec);
}
void AbstractStream::declaredCodecs()
{
const std::string & streamDesc = segmentTracker->getStreamDescription();
const std::string & streamLang = segmentTracker->getStreamLanguage();
std::list<std::string> codecs = segmentTracker->getCurrentCodecs();
if(codecs.empty())
CodecDescriptionList descs;
segmentTracker->getCodecsDesc(&descs);
for(auto it = descs.cbegin(); it != descs.cend(); ++it)
{
const StreamFormat format = segmentTracker->getCurrentFormat();
switch(format)
{
case StreamFormat::TTML:
add_codec_string_from_fourcc(VLC_CODEC_TTML, codecs);
break;
case StreamFormat::WEBVTT:
add_codec_string_from_fourcc(VLC_CODEC_WEBVTT, codecs);
break;
default:
break;
}
}
for(std::list<std::string>::const_iterator it = codecs.begin();
it != codecs.end(); ++it)
{
FormatNamespace fnsp(*it);
es_format_t fmt;
es_format_Init(&fmt, fnsp.getFmt()->i_cat, fnsp.getFmt()->i_codec);
es_format_Copy(&fmt, fnsp.getFmt());
if(!streamLang.empty())
fmt.psz_language = strdup(streamLang.c_str());
if(!streamDesc.empty())
fmt.psz_description = strdup(streamDesc.c_str());
fakeEsOut()->declareEs( &fmt );
es_format_Clean(&fmt);
const es_format_t *fmt = (*it)->getFmt();
if(fmt->i_cat != UNKNOWN_ES)
fakeEsOut()->declareEs(fmt);
}
}
......
......@@ -39,6 +39,7 @@ namespace adaptive
namespace http
{
class AbstractConnectionManager;
class ChunkInterface;
}
namespace playlist
......@@ -61,7 +62,6 @@ namespace adaptive
void setLanguage(const std::string &);
void setDescription(const std::string &);
mtime_t getPCR() const;
mtime_t getMinAheadTime() const;
mtime_t getFirstDTS() const;
int esCount() const;
......@@ -69,23 +69,25 @@ namespace adaptive
virtual bool reactivate(mtime_t);
bool isDisabled() const;
bool isValid() const;
typedef enum {
status_eof = 0, /* prioritized */
status_discontinuity,
status_demuxed,
status_buffering,
} status;
typedef enum {
buffering_end = 0, /* prioritized */
buffering_suspended,
buffering_full,
buffering_ongoing,
buffering_lessthanmin,
} buffering_status;
buffering_status bufferize(mtime_t, unsigned, unsigned);
buffering_status getLastBufferStatus() const;
void setLivePause(bool);
enum class Status {
Eof = 0, /* prioritized */
Discontinuity,
Demuxed,
Buffering,
};
enum class BufferingStatus {
End = 0, /* prioritized */
Suspended,
Full,
Ongoing,
Lessthanmin,
};
BufferingStatus bufferize(mtime_t, mtime_t, mtime_t,
mtime_t, bool = false);
BufferingStatus getLastBufferStatus() const;
mtime_t getDemuxedAmount(mtime_t) const;
status dequeue(mtime_t, mtime_t *);
Status dequeue(mtime_t, mtime_t *);
bool decodersDrained();
virtual bool setPosition(mtime_t, bool);
bool getMediaPlaybackTimes(mtime_t *, mtime_t *, mtime_t *,
......@@ -93,12 +95,11 @@ namespace adaptive
void runUpdates();
/* Used by demuxers fake streams */
virtual std::string getContentType(); /* impl */
virtual block_t *readNextBlock(); /* impl */
virtual block_t *readNextBlock() override;
/**/
virtual void fillExtraFMTInfo( es_format_t * ) const; /* impl */
virtual void trackerEvent(const SegmentTrackerEvent &); /* impl */
virtual void fillExtraFMTInfo( es_format_t * ) const override;
virtual void trackerEvent(const TrackerEvent &) override;
protected:
bool seekAble() const;
......@@ -106,11 +107,12 @@ namespace adaptive
virtual block_t *checkBlock(block_t *, bool) = 0;
AbstractDemuxer * createDemux(const StreamFormat &);
virtual AbstractDemuxer * newDemux(vlc_object_t *, const StreamFormat &,
es_out_t *, AbstractSourceStream *) const; /* impl */
es_out_t *, AbstractSourceStream *) const override;
virtual bool startDemux();
virtual bool restartDemux();
virtual void prepareRestart(bool = true);
bool resetForNewPosition(mtime_t);
bool discontinuity;
bool needrestart;
......@@ -123,7 +125,8 @@ namespace adaptive
AbstractConnectionManager *connManager; /* not owned */
SegmentTracker *segmentTracker;
SegmentChunk *currentChunk;
ChunkInterface * getNextChunk() const;
ChunkInterface *currentChunk;
bool eof;
std::string language;
std::string description;
......@@ -137,8 +140,9 @@ namespace adaptive
private:
void declaredCodecs();
buffering_status doBufferize(mtime_t, mtime_t, mtime_t);
buffering_status last_buffer_status;
BufferingStatus doBufferize(mtime_t, mtime_t, mtime_t,
mtime_t, bool);
BufferingStatus last_buffer_status;
bool valid;
bool disabled;
unsigned notfound_sequence;
......
......@@ -22,8 +22,11 @@
#include <vlc_common.h>
namespace adaptive
{
/* Scaled time */
typedef int64_t stime_t;
using stime_t = int64_t;
class Timescale
{
......@@ -52,5 +55,7 @@ class Timescale
uint64_t scale;
};
}
#endif // TIME_HPP
......@@ -47,7 +47,7 @@
#include "../hls/playlist/M3U8.hpp"
#include "../smooth/SmoothManager.hpp"
#include "../smooth/SmoothStream.hpp"
#include "../smooth/playlist/Parser.hpp"
#include "../smooth/playlist/SmoothParser.hpp"
using namespace adaptive::http;
using namespace adaptive::logic;
......@@ -86,13 +86,13 @@ static void Close (vlc_object_t *);
#define ADAPT_LOWLATENCY_LONGTEXT N_("Overrides low latency parameters")
static const AbstractAdaptationLogic::LogicType pi_logics[] = {
AbstractAdaptationLogic::Default,
AbstractAdaptationLogic::Predictive,
AbstractAdaptationLogic::NearOptimal,
AbstractAdaptationLogic::RateBased,
AbstractAdaptationLogic::FixedRate,
AbstractAdaptationLogic::AlwaysLowest,
AbstractAdaptationLogic::AlwaysBest};
AbstractAdaptationLogic::LogicType::Default,
AbstractAdaptationLogic::LogicType::Predictive,
AbstractAdaptationLogic::LogicType::NearOptimal,
AbstractAdaptationLogic::LogicType::RateBased,
AbstractAdaptationLogic::LogicType::FixedRate,
AbstractAdaptationLogic::LogicType::AlwaysLowest,
AbstractAdaptationLogic::LogicType::AlwaysBest};
static const char *const ppsz_logics_values[] = {
"",
......@@ -129,7 +129,7 @@ vlc_module_begin ()
set_capability( "demux", 12 )
set_category( CAT_INPUT )
set_subcategory( SUBCAT_INPUT_DEMUX )
add_string( "adaptive-logic", "", ADAPT_LOGIC_TEXT, NULL, false )
add_string( "adaptive-logic", "", ADAPT_LOGIC_TEXT, nullptr, false )
change_string_list( ppsz_logics_values, ppsz_logics )
add_integer( "adaptive-maxwidth", 0,
ADAPT_WIDTH_TEXT, ADAPT_WIDTH_TEXT, false )
......@@ -142,7 +142,7 @@ vlc_module_begin ()
ADAPT_BUFFER_TEXT, ADAPT_BUFFER_LONGTEXT, true );
add_integer( "adaptive-maxbuffer",
AbstractBufferingLogic::DEFAULT_MAX_BUFFERING / 1000,
ADAPT_MAXBUFFER_TEXT, NULL, true );
ADAPT_MAXBUFFER_TEXT, nullptr, true );
add_integer( "adaptive-lowlatency", -1, ADAPT_LOWLATENCY_TEXT, ADAPT_LOWLATENCY_LONGTEXT, true );
change_integer_list(rgi_latency, ppsz_latency)
set_callbacks( Open, Close )
......@@ -165,7 +165,7 @@ static int Open(vlc_object_t *p_obj)
{
demux_t *p_demux = (demux_t*) p_obj;
if(!p_demux->s->psz_url || p_demux->s->b_preparsing)
if(!p_demux->s->psz_url)
return VLC_EGENERIC;
std::string mimeType;
......@@ -177,10 +177,10 @@ static int Open(vlc_object_t *p_obj)
free(psz_mime);
}
PlaylistManager *p_manager = NULL;
PlaylistManager *p_manager = nullptr;
char *psz_logic = var_InheritString(p_obj, "adaptive-logic");
AbstractAdaptationLogic::LogicType logic = AbstractAdaptationLogic::Default;
AbstractAdaptationLogic::LogicType logic = AbstractAdaptationLogic::LogicType::Default;
if( psz_logic )
{
bool b_found = false;
......@@ -246,7 +246,7 @@ static int Open(vlc_object_t *p_obj)
}
}
if(!p_manager || !p_manager->init())
if(!p_manager || !p_manager->init(p_demux->b_preparsing))
{
delete p_manager;
return VLC_EGENERIC;
......@@ -280,13 +280,6 @@ static void Close(vlc_object_t *p_obj)
/*****************************************************************************
*
*****************************************************************************/
static bool IsLocalResource(const std::string & url)
{
ConnectionParams params(url);
return params.isLocal();
}
static PlaylistManager * HandleDash(demux_t *p_demux, DOMParser &xmlParser,
const std::string & playlisturl,
AbstractAdaptationLogic::LogicType logic)
......@@ -294,21 +287,21 @@ static PlaylistManager * HandleDash(demux_t *p_demux, DOMParser &xmlParser,
if(!xmlParser.reset(p_demux->s) || !xmlParser.parse(true))
{
msg_Err(p_demux, "Cannot parse MPD");
return NULL;
return nullptr;
}
IsoffMainParser mpdparser(xmlParser.getRootNode(), VLC_OBJECT(p_demux),
p_demux->s, playlisturl);
MPD *p_playlist = mpdparser.parse();
if(p_playlist == NULL)
if(p_playlist == nullptr)
{
msg_Err( p_demux, "Cannot create/unknown MPD for profile");
return NULL;
return nullptr;
}
SharedResources *resources = new (std::nothrow) SharedResources(VLC_OBJECT(p_demux),
IsLocalResource(playlisturl));
SharedResources *resources =
SharedResources::createDefault(VLC_OBJECT(p_demux), playlisturl);
DASHStreamFactory *factory = new (std::nothrow) DASHStreamFactory;
DASHManager *manager = NULL;
DASHManager *manager = nullptr;
if(!resources || !factory ||
!(manager = new (std::nothrow) DASHManager(p_demux, resources,
p_playlist, factory, logic)))
......@@ -327,21 +320,21 @@ static PlaylistManager * HandleSmooth(demux_t *p_demux, DOMParser &xmlParser,
if(!xmlParser.reset(p_demux->s) || !xmlParser.parse(true))
{
msg_Err(p_demux, "Cannot parse Manifest");
return NULL;
return nullptr;
}
ManifestParser mparser(xmlParser.getRootNode(), VLC_OBJECT(p_demux),
p_demux->s, playlisturl);
Manifest *p_playlist = mparser.parse();
if(p_playlist == NULL)
if(p_playlist == nullptr)
{
msg_Err( p_demux, "Cannot create Manifest");
return NULL;
return nullptr;
}
SharedResources *resources = new (std::nothrow) SharedResources(VLC_OBJECT(p_demux),
IsLocalResource(playlisturl));
SharedResources *resources =
SharedResources::createDefault(VLC_OBJECT(p_demux), playlisturl);
SmoothStreamFactory *factory = new (std::nothrow) SmoothStreamFactory;
SmoothManager *manager = NULL;
SmoothManager *manager = nullptr;
if(!resources || !factory ||
!(manager = new (std::nothrow) SmoothManager(p_demux, resources,
p_playlist, factory, logic)))
......@@ -357,10 +350,10 @@ static PlaylistManager * HandleHLS(demux_t *p_demux,
const std::string & playlisturl,
AbstractAdaptationLogic::LogicType logic)
{
SharedResources *resources = new SharedResources(VLC_OBJECT(p_demux),
IsLocalResource(playlisturl));
SharedResources *resources =
SharedResources::createDefault(VLC_OBJECT(p_demux), playlisturl);
if(!resources)
return NULL;
return nullptr;
M3U8Parser parser(resources);
M3U8 *p_playlist = parser.parse(VLC_OBJECT(p_demux),p_demux->s, playlisturl);
......@@ -368,11 +361,11 @@ static PlaylistManager * HandleHLS(demux_t *p_demux,
{
msg_Err( p_demux, "Could not parse playlist" );
delete resources;
return NULL;
return nullptr;
}
HLSStreamFactory *factory = new (std::nothrow) HLSStreamFactory;
HLSManager *manager = NULL;
HLSManager *manager = nullptr;
if(!factory ||
!(manager = new (std::nothrow) HLSManager(p_demux, resources,
p_playlist, factory, logic)))
......
......@@ -38,13 +38,13 @@ using namespace adaptive::encryption;
CommonEncryption::CommonEncryption()
{
method = CommonEncryption::Method::NONE;
method = CommonEncryption::Method::None;
}
void CommonEncryption::mergeWith(const CommonEncryption &other)
{
if(method == CommonEncryption::Method::NONE &&
other.method != CommonEncryption::Method::NONE)
if(method == CommonEncryption::Method::None &&
other.method != CommonEncryption::Method::None)
method = other.method;
if(uri.empty() && !other.uri.empty())
uri = other.uri;
......@@ -54,7 +54,7 @@ void CommonEncryption::mergeWith(const CommonEncryption &other)
CommonEncryptionSession::CommonEncryptionSession()
{
ctx = NULL;
ctx = nullptr;
}
......@@ -86,7 +86,7 @@ bool CommonEncryptionSession::start(SharedResources *res, const CommonEncryption
gcry_cipher_setiv(handle, &encryption.iv[0], 16) )
{
gcry_cipher_close(handle);
ctx = NULL;
ctx = nullptr;
return false;
}
ctx = handle;
......@@ -101,7 +101,7 @@ void CommonEncryptionSession::close()
gcry_cipher_hd_t handle = reinterpret_cast<gcry_cipher_hd_t>(ctx);
if(ctx)
gcry_cipher_close(handle);
ctx = NULL;
ctx = nullptr;
#endif
}
......@@ -115,7 +115,7 @@ size_t CommonEncryptionSession::decrypt(void *inputdata, size_t inputbytes, bool
if(encryption.method == CommonEncryption::Method::AES_128 && ctx)
{
if ((inputbytes % 16) != 0 || inputbytes < 16 ||
gcry_cipher_decrypt(handle, inputdata, inputbytes, NULL, 0))
gcry_cipher_decrypt(handle, inputdata, inputbytes, nullptr, 0))
{
inputbytes = 0;
}
......@@ -135,7 +135,7 @@ size_t CommonEncryptionSession::decrypt(void *inputdata, size_t inputbytes, bool
}
else
#endif
if(encryption.method != CommonEncryption::Method::NONE)
if(encryption.method != CommonEncryption::Method::None)
{
inputbytes = 0;
}
......
......@@ -34,11 +34,11 @@ namespace adaptive
public:
CommonEncryption();
void mergeWith(const CommonEncryption &);
enum Method
enum class Method
{
NONE,
None,
AES_128,
AES_SAMPLE,
AES_Sample,
} method;
std::string uri;
std::vector<unsigned char> iv;
......
......@@ -24,6 +24,7 @@
#include "Keyring.hpp"
#include "../tools/Retrieve.hpp"
#include "../http/Chunk.h"
#include <vlc_block.h>
......@@ -52,7 +53,7 @@ KeyringKey Keyring::getKey(SharedResources *resources, const std::string &uri)
{
/* Pretty bad inside the lock */
msg_Dbg(obj, "Retrieving AES key %s", uri.c_str());
block_t *p_block = Retrieve::HTTP(resources, uri);
block_t *p_block = Retrieve::HTTP(resources, http::ChunkType::Key, uri);
if(p_block)
{
if(p_block->i_buffer == 16)
......
......@@ -33,7 +33,7 @@ namespace adaptive
namespace encryption
{
typedef std::vector<unsigned char> KeyringKey;
using KeyringKey = std::vector<unsigned char>;
class Keyring
{
......