[vlc-commits] demux: adaptive: add more unit tests

Francois Cartegnie git at videolan.org
Fri Jan 22 22:10:34 UTC 2021


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Mon Dec 21 14:17:35 2020 +0100| [fc25e9b42b2833f015b4b753c7a74061c88299e2] | committer: Francois Cartegnie

demux: adaptive: add more unit tests

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=fc25e9b42b2833f015b4b753c7a74061c88299e2
---

 modules/demux/Makefile.am                          |   9 +
 .../demux/adaptive/test/logic/BufferingLogic.cpp   | 171 ++++++++++
 .../demux/adaptive/test/playlist/Inheritables.cpp  | 126 ++++++++
 modules/demux/adaptive/test/playlist/M3U8.cpp      | 351 +++++++++++++++++++++
 .../demux/adaptive/test/playlist/SegmentBase.cpp   | 105 ++++++
 .../demux/adaptive/test/playlist/SegmentList.cpp   | 181 +++++++++++
 .../adaptive/test/playlist/SegmentTemplate.cpp     | 128 ++++++++
 .../adaptive/test/playlist/SegmentTimeline.cpp     | 184 +++++++++++
 .../demux/adaptive/test/plumbing/CommandsQueue.cpp | 327 +++++++++++++++++++
 modules/demux/adaptive/test/test.cpp               |  18 +-
 modules/demux/adaptive/test/test.hpp               |  10 +
 modules/demux/adaptive/test/tools/Conversions.cpp  |  67 ++++
 12 files changed, 1676 insertions(+), 1 deletion(-)

diff --git a/modules/demux/Makefile.am b/modules/demux/Makefile.am
index ca641e6670..6e2b5bfa00 100644
--- a/modules/demux/Makefile.am
+++ b/modules/demux/Makefile.am
@@ -505,7 +505,16 @@ 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
diff --git a/modules/demux/adaptive/test/logic/BufferingLogic.cpp b/modules/demux/adaptive/test/logic/BufferingLogic.cpp
new file mode 100644
index 0000000000..12bbdca744
--- /dev/null
+++ b/modules/demux/adaptive/test/logic/BufferingLogic.cpp
@@ -0,0 +1,171 @@
+/*****************************************************************************
+ *
+ *****************************************************************************
+ * Copyright (C) 2020 VideoLabs, VideoLAN and VLC Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "../../playlist/SegmentBase.h"
+#include "../../playlist/SegmentTimeline.h"
+#include "../../playlist/SegmentTemplate.h"
+#include "../../playlist/SegmentList.h"
+#include "../../playlist/BasePlaylist.hpp"
+#include "../../playlist/BasePeriod.h"
+#include "../../playlist/BaseAdaptationSet.h"
+#include "../../playlist/BaseRepresentation.h"
+#include "../../playlist/Segment.h"
+#include "../../logic/BufferingLogic.hpp"
+
+#include "../test.hpp"
+
+#include <limits>
+
+using namespace adaptive;
+using namespace adaptive::playlist;
+using namespace logic;
+
+class TestPlaylist : public BasePlaylist
+{
+    public:
+        TestPlaylist() : BasePlaylist(nullptr)
+        {
+            b_live = false;
+            b_lowlatency = false;
+        }
+
+        virtual ~TestPlaylist() {}
+
+        virtual bool isLive() const override
+        {
+            return b_live;
+        }
+
+        virtual bool isLowLatency() const override
+        {
+            return b_lowlatency;
+        }
+
+        bool b_live;
+        bool b_lowlatency;
+};
+
+int BufferingLogic_test()
+{
+    DefaultBufferingLogic bufferinglogic;
+    TestPlaylist *playlist = nullptr;
+    try
+    {
+        playlist = new TestPlaylist();
+        BasePeriod *period = nullptr;
+        BaseAdaptationSet *set = nullptr;
+        BaseRepresentation *rep = nullptr;
+        try
+        {
+            period = new BasePeriod(playlist);
+            set = new BaseAdaptationSet(period);
+            rep = new BaseRepresentation(set);
+        } catch(...) {
+            delete period;
+            delete set;
+            std::rethrow_exception(std::current_exception());
+        }
+        set->addRepresentation(rep);
+        period->addAdaptationSet(set);
+        playlist->addPeriod(period);
+
+        Timescale timescale(100);
+        set->addAttribute(new TimescaleAttr(timescale));
+
+        SegmentList *segmentList = new SegmentList(rep);
+        rep->addAttribute(segmentList);
+
+        Expect(bufferinglogic.getStartSegmentNumber(rep) == std::numeric_limits<uint64_t>::max());
+
+        stime_t segmentduration = timescale.ToScaled(DefaultBufferingLogic::BUFFERING_LOWEST_LIMIT / 2);
+        uint64_t number = 22;
+        Segment *seg = new Segment(rep);
+        seg->setSequenceNumber(number);
+        seg->duration.Set(segmentduration);
+        segmentList->addSegment(seg);
+
+        Expect(bufferinglogic.getStartSegmentNumber(rep) == number);
+        Expect(bufferinglogic.getMinBuffering(playlist) == DefaultBufferingLogic::DEFAULT_MIN_BUFFERING);
+        Expect(bufferinglogic.getMaxBuffering(playlist) == DefaultBufferingLogic::DEFAULT_MAX_BUFFERING);
+
+        bufferinglogic.setUserMinBuffering(DefaultBufferingLogic::DEFAULT_MIN_BUFFERING / 2);
+        Expect(bufferinglogic.getMinBuffering(playlist) == std::max(DefaultBufferingLogic::DEFAULT_MIN_BUFFERING / 2,
+                                                                    DefaultBufferingLogic::BUFFERING_LOWEST_LIMIT));
+
+        bufferinglogic.setUserMinBuffering(DefaultBufferingLogic::BUFFERING_LOWEST_LIMIT / 2);
+        Expect(bufferinglogic.getMinBuffering(playlist) == DefaultBufferingLogic::BUFFERING_LOWEST_LIMIT);
+
+        bufferinglogic.setUserMinBuffering(DefaultBufferingLogic::DEFAULT_MIN_BUFFERING);
+        bufferinglogic.setUserMaxBuffering(DefaultBufferingLogic::DEFAULT_MIN_BUFFERING / 2);
+        Expect(bufferinglogic.getMaxBuffering(playlist) == DefaultBufferingLogic::DEFAULT_MIN_BUFFERING);
+
+        bufferinglogic.setUserMinBuffering(DefaultBufferingLogic::BUFFERING_LOWEST_LIMIT / 2);
+        bufferinglogic.setUserMaxBuffering(DefaultBufferingLogic::BUFFERING_LOWEST_LIMIT / 2);
+        Expect(bufferinglogic.getMaxBuffering(playlist) == DefaultBufferingLogic::BUFFERING_LOWEST_LIMIT);
+
+        playlist->b_live = true;
+        bufferinglogic.setUserMinBuffering(0);
+        bufferinglogic.setUserMaxBuffering(0);
+        Expect(bufferinglogic.getMinBuffering(playlist) == DefaultBufferingLogic::DEFAULT_MIN_BUFFERING);
+        Expect(bufferinglogic.getMaxBuffering(playlist) <= DefaultBufferingLogic::DEFAULT_MAX_BUFFERING);
+        Expect(bufferinglogic.getLiveDelay(playlist) == DefaultBufferingLogic::DEFAULT_LIVE_BUFFERING);
+
+        bufferinglogic.setUserLiveDelay(DefaultBufferingLogic::DEFAULT_MIN_BUFFERING / 2);
+        Expect(bufferinglogic.getLiveDelay(playlist) ==std::max(DefaultBufferingLogic::DEFAULT_MIN_BUFFERING,
+                                                                DefaultBufferingLogic::BUFFERING_LOWEST_LIMIT));
+
+        playlist->b_lowlatency = true;
+        bufferinglogic.setUserLiveDelay(0);
+        if(DefaultBufferingLogic::DEFAULT_MIN_BUFFERING > DefaultBufferingLogic::BUFFERING_LOWEST_LIMIT)
+            Expect(bufferinglogic.getMinBuffering(playlist) < DefaultBufferingLogic::DEFAULT_MIN_BUFFERING);
+        Expect(bufferinglogic.getMaxBuffering(playlist) < DefaultBufferingLogic::DEFAULT_MAX_BUFFERING);
+        Expect(bufferinglogic.getMinBuffering(playlist) >= DefaultBufferingLogic::BUFFERING_LOWEST_LIMIT);
+        Expect(bufferinglogic.getLiveDelay(playlist) >= DefaultBufferingLogic::BUFFERING_LOWEST_LIMIT);
+
+        playlist->b_lowlatency = false;
+        Expect(bufferinglogic.getStartSegmentNumber(rep) == number);
+
+        while(segmentList->getTotalLength() <
+              timescale.ToScaled(DefaultBufferingLogic::DEFAULT_MAX_BUFFERING))
+        {
+            seg = new Segment(rep);
+            seg->setSequenceNumber(++number);
+            seg->duration.Set(segmentduration);
+            segmentList->addSegment(seg);
+        }
+
+        Expect(bufferinglogic.getStartSegmentNumber(rep) > 22);
+
+        Expect(bufferinglogic.getStartSegmentNumber(rep) <=
+               number - DefaultBufferingLogic::SAFETY_BUFFERING_EDGE_OFFSET);
+        Expect(bufferinglogic.getStartSegmentNumber(rep) >=
+               22 + DefaultBufferingLogic::SAFETY_EXPURGING_OFFSET);
+
+        delete playlist;
+    } catch(...) {
+        delete playlist;
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/modules/demux/adaptive/test/playlist/Inheritables.cpp b/modules/demux/adaptive/test/playlist/Inheritables.cpp
new file mode 100644
index 0000000000..4276b22cbe
--- /dev/null
+++ b/modules/demux/adaptive/test/playlist/Inheritables.cpp
@@ -0,0 +1,126 @@
+/*****************************************************************************
+ *
+ *****************************************************************************
+ * Copyright (C) 2020 VideoLabs, VideoLAN and VLC Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "../../playlist/SegmentBase.h"
+#include "../../playlist/SegmentTimeline.h"
+#include "../../playlist/SegmentTemplate.h"
+#include "../../playlist/SegmentList.h"
+#include "../../playlist/BasePlaylist.hpp"
+#include "../../playlist/BasePeriod.h"
+#include "../../playlist/BaseAdaptationSet.h"
+#include "../../playlist/BaseRepresentation.h"
+
+#include "../test.hpp"
+
+using namespace adaptive;
+using namespace adaptive::playlist;
+
+int Inheritables_test()
+{
+    BasePeriod *period = nullptr;
+    try
+    {
+        period = new BasePeriod(nullptr);
+        BaseAdaptationSet *set = nullptr;
+        BaseRepresentation *rep = nullptr;
+        try
+        {
+            set = new BaseAdaptationSet(period);
+            rep = new BaseRepresentation(set);
+        } catch(...) {
+            delete set;
+            std::rethrow_exception(std::current_exception());
+        }
+        set->addRepresentation(rep);
+        period->addAdaptationSet(set);
+
+        Expect(rep->inheritAttribute(AbstractAttr::Type::SegmentBase) == nullptr);
+        period->addAttribute(new TimescaleAttr(Timescale(123)));
+
+        const AbstractAttr *attr = rep->inheritAttribute(AbstractAttr::Type::Timescale);
+        Expect(attr != nullptr);
+        Expect(attr->isValid());
+        Timescale timescale = rep->inheritTimescale();
+        Expect(timescale == 123);
+
+        set->addAttribute(new TimescaleAttr(Timescale(1230)));
+        timescale = rep->inheritTimescale();
+        Expect(timescale == 1230);
+
+        set->replaceAttribute(new TimescaleAttr(Timescale()));
+        timescale = rep->inheritTimescale();
+        Expect(timescale == 123);
+
+        delete period;
+    } catch(...) {
+        delete period;
+        return 1;
+    }
+
+    try
+    {
+        /* Check inheritance from siblings */
+        period = new BasePeriod(nullptr);
+        BaseAdaptationSet *set = nullptr;
+        BaseRepresentation *rep = nullptr;
+        try
+        {
+            set = new BaseAdaptationSet(period);
+            rep = new BaseRepresentation(set);
+        } catch(...) {
+            delete set;
+            std::rethrow_exception(std::current_exception());
+        }
+        set->addRepresentation(rep);
+        period->addAdaptationSet(set);
+
+        SegmentTemplate *templ = new SegmentTemplate(new SegmentTemplateSegment(), rep);
+        rep->addAttribute(templ);
+        SegmentTimeline *timeline = new SegmentTimeline(templ);
+        templ->addAttribute(timeline);
+
+        templ = new SegmentTemplate(new SegmentTemplateSegment(), period);
+        period->addAttribute(templ);
+        templ->addAttribute(new TimescaleAttr(Timescale(123)));
+        timeline = new SegmentTimeline(templ);
+        timeline->addAttribute(new TimescaleAttr(Timescale(456)));
+        templ->addAttribute(timeline);
+
+        /* check inheritance from matched sibling */
+        const AbstractAttr *attr = rep->inheritAttribute(AbstractAttr::Type::Timescale);
+        Expect(attr == nullptr);
+        Timescale timescale = timeline->inheritTimescale();
+        Expect(timescale == 456);
+        timeline->replaceAttribute(new TimescaleAttr(Timescale())); /* invalidate on timeline */
+        /* should now inherit from sibling parent template */
+        timescale = timeline->inheritTimescale();
+        Expect(timescale == 123);
+
+        delete period;
+    } catch(...) {
+        delete period;
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/modules/demux/adaptive/test/playlist/M3U8.cpp b/modules/demux/adaptive/test/playlist/M3U8.cpp
new file mode 100644
index 0000000000..62e3f463a9
--- /dev/null
+++ b/modules/demux/adaptive/test/playlist/M3U8.cpp
@@ -0,0 +1,351 @@
+/*****************************************************************************
+ *
+ *****************************************************************************
+ * Copyright (C) 2020 VideoLabs, VideoLAN and VLC Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "../../playlist/Segment.h"
+#include "../../playlist/SegmentList.h"
+#include "../../playlist/BasePeriod.h"
+#include "../../playlist/BaseAdaptationSet.h"
+#include "../../playlist/BaseRepresentation.h"
+#include "../../logic/BufferingLogic.hpp"
+#include "../../SegmentTracker.hpp"
+#include "../../../hls/playlist/Parser.hpp"
+#include "../../../hls/playlist/M3U8.hpp"
+#include "../../../hls/playlist/HLSSegment.hpp"
+#include "../../../hls/playlist/HLSRepresentation.hpp"
+
+#include "../test.hpp"
+
+#include <vlc_stream.h>
+
+#include <limits>
+#include <algorithm>
+
+using namespace adaptive;
+using namespace adaptive::playlist;
+using namespace hls::playlist;
+
+static M3U8 * ParseM3U8(vlc_object_t *obj, const char *psz, size_t isz)
+{
+    M3U8Parser parser(nullptr);
+    stream_t *substream = vlc_stream_MemoryNew(obj, ((uint8_t *)psz), isz, true);
+    if(!substream)
+        return nullptr;
+    M3U8 *m3u = parser.parse(obj, substream, std::string("stdin://"));
+    vlc_stream_Delete(substream);
+    return m3u;
+}
+
+int M3U8MasterPlaylist_test()
+{
+    vlc_object_t *obj = static_cast<vlc_object_t*>(nullptr);
+
+    const char manifest0[] =
+    "#EXTM3U\n"
+    "#EXT-X-STREAM-INF:BANDWIDTH=1280000\n"
+    "http://example.com/low.m3u8\n"
+    "#EXT-X-STREAM-INF:BANDWIDTH=2560000\n"
+    "http://example.com/mid.m3u8\n"
+    "#EXT-X-STREAM-INF:BANDWIDTH=7680000\n"
+    "http://example.com/hi.m3u8\n"
+    "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n"
+    "http://example.com/audio-only.m3u8\n";
+
+    M3U8 *m3u = ParseM3U8(obj, manifest0, sizeof(manifest0));
+    try
+    {
+        Expect(m3u);
+        Expect(m3u->getUrlSegment().toString() == "stdin://");
+        Expect(m3u->getPeriods().size() == 1);
+        Expect(m3u->getFirstPeriod()->getAdaptationSets().size() == 1);
+        Expect(m3u->getFirstPeriod()->getAdaptationSets().front()->getRepresentations().size() == 4);
+        BaseAdaptationSet *set = m3u->getFirstPeriod()->getAdaptationSets().front();
+        std::vector<BaseRepresentation *> &reps = set->getRepresentations();
+        std::vector<BaseRepresentation *>::iterator it;
+        it = std::find_if(reps.begin(), reps.end(),
+                          [](BaseRepresentation *r) { return r->getBandwidth() == 1280000; });
+        Expect(it != reps.end());
+        Expect(static_cast<HLSRepresentation *>(*it)->getPlaylistUrl().toString() == "http://example.com/low.m3u8");
+        it = std::find_if(reps.begin(), reps.end(),
+                      [](BaseRepresentation *r) { return r->getBandwidth() == 65000; });
+        Expect(it != reps.end());
+        Expect(static_cast<HLSRepresentation *>(*it)->getPlaylistUrl().toString() == "http://example.com/audio-only.m3u8");
+        Expect((*it)->getUrlSegment().toString() == "http://example.com/");
+        const std::list<std::string> &codecs = (*it)->getCodecs();
+        Expect(!codecs.empty());
+        Expect(codecs.front() == "mp4a.40.5");
+        Expect((*it)->needsUpdate(1));
+        delete m3u;
+    }
+    catch(...)
+    {
+        delete m3u;
+        return 1;
+    }
+
+    const char manifest1[] =
+    "#EXTM3U\n"
+    "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aac\",NAME=\"English\", "
+    " DEFAULT=YES,AUTOSELECT=YES,LANGUAGE=\"en\", "
+    " URI=\"main/english-audio.m3u8\"\n"
+    "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aac\",NAME=\"Deutsch\", "
+    " DEFAULT=NO,AUTOSELECT=YES,LANGUAGE=\"de\", "
+    " URI=\"main/german-audio.m3u8\"\n"
+    "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aac\",NAME=\"Commentary\", "
+    " DEFAULT=NO,AUTOSELECT=NO,LANGUAGE=\"en\", "
+    " URI=\"commentary/audio-only.m3u8\"\n"
+    "#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS=\"avc1, mp4a.40.5\",AUDIO=\"aac\"\n"
+    "hi/video-only.m3u8\n"
+    "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\",AUDIO=\"aac\"\n"
+    "main/english-audio.m3u8\n";
+
+    m3u = ParseM3U8(obj, manifest1, sizeof(manifest1));
+    try
+    {
+        Expect(m3u);
+        Expect(m3u->getPeriods().size() == 1);
+        Expect(m3u->getFirstPeriod()->getAdaptationSets().size() == 4);
+        BaseAdaptationSet *set = m3u->getFirstPeriod()->getAdaptationSetByID(ID("aac English"));
+        Expect(set);
+        Expect(set->getRepresentations().size() == 1);
+        Expect(set->getLang() == "en");
+        Expect(set->getRole().autoSelectable());
+        Expect(set->getRole().isDefault());
+        delete m3u;
+    }
+    catch(...)
+    {
+        delete m3u;
+        return 1;
+    }
+
+    return 0;
+}
+
+int M3U8Playlist_test()
+{
+    vlc_object_t *obj = static_cast<vlc_object_t*>(nullptr);
+    DefaultBufferingLogic bufferingLogic;
+
+    /* Manifest 0 */
+    const char manifest0[] =
+    "#EXTM3U\n"
+    "#EXT-X-MEDIA-SEQUENCE:10\n"
+    "#EXTINF:8,\n"
+    "foobar.ts\n"
+    "#EXT-X-ENDLIST\n";
+
+    M3U8 *m3u = ParseM3U8(obj, manifest0, sizeof(manifest0));
+    try
+    {
+        Expect(m3u);
+        Expect(m3u->isLive() == false);
+        Expect(m3u->isLowLatency() == false);
+        Expect(m3u->getPeriods().size() == 1);
+        Expect(m3u->getFirstPeriod()->getAdaptationSets().size() == 1);
+        Expect(m3u->getFirstPeriod()->getAdaptationSets().front()->getRepresentations().size() == 1);
+        BaseRepresentation *rep = m3u->getFirstPeriod()->getAdaptationSets().front()->getRepresentations().front();
+        Expect(rep->getProfile()->getStartSegmentNumber() == 10);
+
+        uint64_t number = bufferingLogic.getStartSegmentNumber(rep);
+        Expect(number == 10);
+        vlc_tick_t mediatime, duration;
+        Expect(rep->getPlaybackTimeDurationBySegmentNumber(std::numeric_limits<uint64_t>::max(),
+                                                           &mediatime, &duration) == false);
+        Expect(rep->getPlaybackTimeDurationBySegmentNumber(number + 1, &mediatime, &duration) == false);
+        Expect(rep->getPlaybackTimeDurationBySegmentNumber(number - 1, &mediatime, &duration) == false);
+        Expect(rep->getPlaybackTimeDurationBySegmentNumber(number, &mediatime, &duration) == true);
+        Expect(mediatime == vlc_tick_from_sec(0));
+        Expect(duration == vlc_tick_from_sec(8));
+        Expect(rep->getSegmentNumberByTime(vlc_tick_from_sec(0), &number));
+        Expect(number == 10);
+        Segment *seg = rep->getMediaSegment(number);
+        Expect(seg);
+        Expect(seg->getSequenceNumber() == 10);
+        Expect(seg->startTime.Get() == (stime_t) 0);
+
+        vlc_tick_t begin, end;
+        Expect(rep->getMediaPlaybackRange(&begin, &end, &duration));
+        Expect(begin == vlc_tick_from_sec(0));
+        Expect(end == vlc_tick_from_sec(8));
+        Expect(duration == vlc_tick_from_sec(8));
+        delete m3u;
+    }
+    catch(...)
+    {
+        delete m3u;
+        return 1;
+    }
+
+    /* Manifest 1 */
+    const char manifest1[] =
+    "#EXTM3U\n"
+    "#EXT-X-MEDIA-SEQUENCE:10\n"
+    "#EXTINF:8,\n"
+    "foobar.ts\n"
+    "#EXTINF:12.0,\n" /* Broken format test: non integral notation */
+    "foobar2.ts\n"
+    "#EXTINF:10\n" /* Broken format test: no mandatory comma */
+    "foobar3.ts\n"
+    "#EXT-X-ENDLIST\n";
+
+    m3u = ParseM3U8(obj, manifest1, sizeof(manifest1));
+    try
+    {
+        Expect(m3u);
+        BaseRepresentation *rep = m3u->getFirstPeriod()->getAdaptationSets().front()->
+                                  getRepresentations().front();
+        Expect(rep->getProfile()->getStartSegmentNumber() == 10);
+        Expect(m3u->duration.Get());
+
+        Timescale timescale = rep->inheritTimescale();
+        Expect(timescale.isValid());
+
+        uint64_t number;
+        bool discont;
+        Segment *seg = rep->getNextMediaSegment(12, &number, &discont);
+        Expect(seg);
+        Expect(number == 12);
+        Expect(!discont);
+        Expect(seg->getSequenceNumber() == 12);
+        Expect(seg->startTime.Get() == timescale.ToScaled(vlc_tick_from_sec(20)));
+
+        vlc_tick_t begin, end, duration;
+        Expect(rep->getMediaPlaybackRange(&begin, &end, &duration));
+        Expect(begin == vlc_tick_from_sec(0));
+        Expect(end == vlc_tick_from_sec(30));
+        Expect(duration == vlc_tick_from_sec(30));
+
+        delete m3u;
+    }
+    catch (...)
+    {
+        delete m3u;
+        return 1;
+    }
+
+    /* Manifest 2 */
+    const char manifest2[] =
+    "#EXTM3U\n"
+    "#EXT-X-MEDIA-SEQUENCE:10\n"
+    "#EXTINF:8\n"
+    "foobar.ts\n"
+    "#EXTINF:5\n"
+    "foobar2.ts\n"
+    "#EXTINF:8\n"
+    "foobar3.ts\n"
+    "#EXTINF:5\n"
+    "foobar4.ts\n"
+    "#EXTINF:8\n"
+    "foobar5.ts\n";
+
+    m3u = ParseM3U8(obj, manifest2, sizeof(manifest2));
+    try
+    {
+        Expect(m3u);
+        Expect(m3u->isLive() == true);
+        BaseRepresentation *rep = m3u->getFirstPeriod()->getAdaptationSets().front()->
+                                  getRepresentations().front();
+        uint64_t number;
+        Expect(rep->getSegmentNumberByTime(vlc_tick_from_sec(2), &number));
+        Expect(number == 10);
+        Expect(rep->getSegmentNumberByTime(vlc_tick_from_sec(8), &number));
+        Expect(number == 11);
+        Expect(rep->getSegmentNumberByTime(vlc_tick_from_sec(100), &number));
+        Expect(number == 14);
+        Expect(bufferingLogic.getStartSegmentNumber(rep) < 13);
+
+        delete m3u;
+    }
+    catch (...)
+    {
+        delete m3u;
+        return 1;
+    }
+
+    /* Manifest 3 */
+    const char manifest3[] =
+    "#EXTM3U\n"
+    "#EXT-X-MEDIA-SEQUENCE:10\n"
+    "#EXT-X-PROGRAM-DATE-TIME:1970-01-01T00:00:10.000+00:00\n"
+    "#EXTINF:8\n"
+    "foobar.ts\n"
+    "#EXTINF:5\n"
+    "foobar2.ts\n"
+    "#EXTINF:8\n"
+    "foobar3.ts\n"
+    "#EXT-X-DISCONTINUITY\n"
+    "#EXT-X-PROGRAM-DATE-TIME:1970-01-01T02:00:00.000+00:00\n"
+    "#EXT-X-MEDIA-SEQUENCE:20\n"
+    "#EXTINF:5\n"
+    "foobar4.ts\n"
+    "#EXTINF:8\n"
+    "foobar5.ts\n";
+
+    m3u = ParseM3U8(obj, manifest3, sizeof(manifest3));
+    try
+    {
+        Expect(m3u);
+        BaseRepresentation *rep = m3u->getFirstPeriod()->getAdaptationSets().front()->
+                                  getRepresentations().front();
+        /* media sequence and discontinuity handling */
+        uint64_t number;
+        bool discont;
+        Segment *seg = rep->getNextMediaSegment(13, &number, &discont);
+        Expect(seg);
+        Expect(number == 20);
+        Expect(discont);
+
+        /* mapping past discontinuity */
+        Expect(rep->getSegmentNumberByTime(vlc_tick_from_sec(23), &number));
+        Expect(number == 20);
+
+        /* date set and incremented */
+        seg = rep->getMediaSegment(10);
+        Expect(seg);
+        Expect(static_cast<HLSSegment *>(seg)->getUTCTime() == VLC_TICK_0 + vlc_tick_from_sec(10));
+        seg = rep->getMediaSegment(11);
+        Expect(seg);
+        Expect(static_cast<HLSSegment *>(seg)->getUTCTime() == VLC_TICK_0 + vlc_tick_from_sec(10 + 8));
+
+        /* date change after discontinuity */
+        seg = rep->getMediaSegment(20);
+        Expect(seg);
+        Expect(static_cast<HLSSegment *>(seg)->getUTCTime() == VLC_TICK_0 + vlc_tick_from_sec(7200));
+
+        vlc_tick_t begin, end, duration;
+        Expect(rep->getMediaPlaybackRange(&begin, &end, &duration));
+        Expect(begin == vlc_tick_from_sec(0));
+        Expect(end == vlc_tick_from_sec(34));
+        Expect(duration == vlc_tick_from_sec(34));
+
+        delete m3u;
+    }
+    catch (...)
+    {
+        delete m3u;
+        return 1;
+    }
+
+
+    return 0;
+}
diff --git a/modules/demux/adaptive/test/playlist/SegmentBase.cpp b/modules/demux/adaptive/test/playlist/SegmentBase.cpp
new file mode 100644
index 0000000000..8b3083c19f
--- /dev/null
+++ b/modules/demux/adaptive/test/playlist/SegmentBase.cpp
@@ -0,0 +1,105 @@
+/*****************************************************************************
+ *
+ *****************************************************************************
+ * Copyright (C) 2020 VideoLabs, VideoLAN and VLC Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "../../playlist/Segment.h"
+#include "../../playlist/SegmentBase.h"
+#include "../../playlist/BasePeriod.h"
+#include "../../playlist/BaseAdaptationSet.h"
+#include "../../playlist/BaseRepresentation.h"
+#include "../../playlist/SegmentInformation.hpp"
+
+#include "../test.hpp"
+
+#include <limits>
+
+using namespace adaptive;
+using namespace adaptive::playlist;
+
+using SplitPoint = SegmentInformation::SplitPoint;
+
+int SegmentBase_test()
+{
+    BaseRepresentation *rep = new BaseRepresentation(nullptr);
+    try
+    {
+        SegmentBase *segmentBase = new SegmentBase(nullptr);
+        Timescale timescale(100);
+        segmentBase->addAttribute(new TimescaleAttr(timescale));
+        rep->addAttribute(segmentBase);
+        /* Check failures */
+
+        Expect(segmentBase->getMediaSegment(0) == nullptr);
+        uint64_t number; bool discont;
+        Expect(segmentBase->getSegmentNumberByTime(1, &number) == false);
+        Expect(segmentBase->getNextMediaSegment(0, &number, &discont) == nullptr);
+        vlc_tick_t time, duration;
+        Expect(segmentBase->getPlaybackTimeDurationBySegmentNumber(0, &time, &duration) == false);
+
+        /* Create a split range */
+        std::vector<SplitPoint> splitlist;
+        for(int i=0; i<10; i++)
+        {
+            SplitPoint point;
+            point.time = i * 100;
+            point.duration = 100;
+            point.offset = 123 + i * 100;
+            splitlist.push_back(point);
+        }
+        rep->SplitUsingIndex(splitlist);
+
+        /* For now we can't tell anything without main segment byte range */
+        Expect(segmentBase->getMediaSegment(0) == nullptr);
+
+        segmentBase->setByteRange(111, 2000);
+
+        segmentBase->duration.Set(100 * 10);
+        rep->SplitUsingIndex(splitlist);
+        Expect(segmentBase->subSegments().size());
+        Expect(segmentBase->getMediaSegment(0) != nullptr);
+        Expect(segmentBase->getMinAheadTime(0) == timescale.ToTime(9 * 100));
+        Expect(segmentBase->getSegmentNumberByTime(timescale.ToTime(9 * 100 - 1), &number));
+        Expect(number == 8);
+        Expect(segmentBase->getMinAheadTime(7) == timescale.ToTime(2 * 100));
+        Expect(segmentBase->getPlaybackTimeDurationBySegmentNumber(7, &time, &duration) == true);
+        Expect(time == timescale.ToTime(7 * 100));
+        Expect(duration == timescale.ToTime(100));
+        Segment *seg = segmentBase->getMediaSegment(7);
+        Expect(seg);
+        Expect(seg->getSequenceNumber() == 7);
+        Expect(seg->getOffset() == 123 + 7*100);
+
+        seg = segmentBase->getNextMediaSegment(7, &number, &discont);
+        Expect(seg);
+        Expect(seg->getSequenceNumber() == 7);
+        Expect(number == 7);
+        Expect(!discont);
+
+        delete rep;
+
+    } catch (...) {
+        delete rep;
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/modules/demux/adaptive/test/playlist/SegmentList.cpp b/modules/demux/adaptive/test/playlist/SegmentList.cpp
new file mode 100644
index 0000000000..b0e2ff7044
--- /dev/null
+++ b/modules/demux/adaptive/test/playlist/SegmentList.cpp
@@ -0,0 +1,181 @@
+/*****************************************************************************
+ *
+ *****************************************************************************
+ * Copyright (C) 2020 VideoLabs, VideoLAN and VLC Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "../../playlist/Segment.h"
+#include "../../playlist/SegmentList.h"
+#include "../../playlist/SegmentTimeline.h"
+#include "../../playlist/BasePeriod.h"
+#include "../../playlist/BaseAdaptationSet.h"
+#include "../../playlist/BaseRepresentation.h"
+
+#include "../test.hpp"
+
+#include <limits>
+
+using namespace adaptive;
+using namespace adaptive::playlist;
+
+int SegmentList_test()
+{
+    SegmentList *segmentList = nullptr;
+    SegmentList *segmentList2 = nullptr;
+    try
+    {
+        segmentList = new SegmentList(nullptr);
+        Timescale timescale(100);
+        segmentList->addAttribute(new TimescaleAttr(timescale));
+        /* Check failures */
+        Expect(segmentList->getStartSegmentNumber() == std::numeric_limits<uint64_t>::max());
+        Expect(segmentList->getTotalLength() == 0);
+        uint64_t number; bool discont;
+        Expect(segmentList->getSegmentNumberByTime(1, &number) == false);
+        Expect(segmentList->getMediaSegment(0) == nullptr);
+        Expect(segmentList->getNextMediaSegment(0, &number, &discont) == nullptr);
+        Expect(segmentList->getMinAheadTime(0) == 0);
+        vlc_tick_t time, duration;
+        Expect(segmentList->getPlaybackTimeDurationBySegmentNumber(0, &time, &duration) == false);
+
+        /* Simple elements list */
+        const stime_t START = 1337;
+        Segment *seg = new Segment(nullptr);
+        seg->setSequenceNumber(123);
+        seg->startTime.Set(START);
+        seg->duration.Set(100);
+        segmentList->addSegment(seg);
+
+        Expect(segmentList->getTotalLength() == 100);
+        Expect(segmentList->getSegmentNumberByTime(timescale.ToTime(0), &number) == false);
+        Expect(segmentList->getSegmentNumberByTime(timescale.ToTime(START), &number) == true);
+        Expect(number == 123);
+        Expect(segmentList->getPlaybackTimeDurationBySegmentNumber(123, &time, &duration) == true);
+        Expect(time == timescale.ToTime(START));
+        Expect(duration == timescale.ToTime(100));
+        seg = segmentList->getMediaSegment(123);
+        Expect(seg);
+        Expect(seg->getSequenceNumber() == 123);
+        Expect(seg->startTime.Get() == START);
+        seg = segmentList->getNextMediaSegment(123, &number, &discont);
+        Expect(seg);
+        Expect(number == 123);
+        Expect(!discont);
+        seg = segmentList->getNextMediaSegment(122, &number, &discont);
+        Expect(seg);
+        Expect(number == 123);
+        Expect(discont);
+        Expect(segmentList->getMinAheadTime(0) == timescale.ToTime(100));
+        Expect(segmentList->getMinAheadTime(123) == timescale.ToTime(0));
+
+        for(int i=1; i<10; i++)
+        {
+            seg = new Segment(nullptr);
+            seg->setSequenceNumber(123 + i);
+            seg->startTime.Set(START + 100 * i);
+            seg->duration.Set(100);
+            segmentList->addSegment(seg);
+        }
+
+        Expect(segmentList->getTotalLength() == 100 * 10);
+        Expect(segmentList->getMinAheadTime(123) == timescale.ToTime(100 * 9));
+        Expect(segmentList->getSegmentNumberByTime(timescale.ToTime(START + 100*9 - 1), &number) == true);
+        Expect(number == 123 + 8);
+        Expect(segmentList->getPlaybackTimeDurationBySegmentNumber(123 + 8, &time, &duration) == true);
+        Expect(time == timescale.ToTime(START + 100 * 8));
+        Expect(duration == timescale.ToTime(100));
+        Expect(segmentList->getMinAheadTime(123+8) == timescale.ToTime(100));
+
+        /* merge */
+        segmentList2 = new SegmentList(nullptr);
+        for(int i=5; i<20; i++)
+        {
+            seg = new Segment(nullptr);
+            seg->setSequenceNumber(123 + i);
+            seg->startTime.Set(START + 100 * i);
+            seg->duration.Set(100);
+            segmentList2->addSegment(seg);
+        }
+        segmentList->updateWith(segmentList2);
+        Expect(segmentList->getStartSegmentNumber() == 123 + 5);
+        Expect(segmentList->getTotalLength() == 100 * 15);
+
+        for(int i=5; i<20; i++)
+        {
+            seg = segmentList->getMediaSegment(123 + i);
+            Expect(seg);
+            Expect(seg->getSequenceNumber() == (uint64_t) 123 + i);
+            Expect(seg->startTime.Get() == START + 100 * i);
+            Expect(seg->duration.Get() == 100);
+        }
+
+        /* prune */
+        segmentList->pruneByPlaybackTime(timescale.ToTime(START+100*6));
+        Expect(segmentList->getStartSegmentNumber() == 123 + 6);
+        Expect(segmentList->getTotalLength() == 100 * 14);
+
+        segmentList->pruneBySegmentNumber(123+10);
+        Expect(segmentList->getStartSegmentNumber() == 123 + 10);
+        Expect(segmentList->getTotalLength() == 100 * 10);
+
+        delete segmentList;
+        delete segmentList2;
+        segmentList2 = nullptr;
+
+        /* Tricky now, check timelined */
+        segmentList = new SegmentList(nullptr);
+        segmentList->addAttribute(new TimescaleAttr(timescale));
+        for(int i=0; i<10; i++)
+        {
+            seg = new Segment(nullptr);
+            seg->setSequenceNumber(123 + i);
+            seg->startTime.Set(START + 100 * i);
+            seg->duration.Set(100);
+            segmentList->addSegment(seg);
+        }
+        const std::vector<Segment*>&allsegments = segmentList->getSegments();
+
+        SegmentTimeline *timeline = new SegmentTimeline(nullptr);
+        segmentList->addAttribute(timeline);
+        timeline->addElement(44, 100, 4, START);
+        Expect(timeline->getTotalLength() == 5 * 100);
+        Expect(segmentList->getStartSegmentNumber() == 44);
+        Expect(segmentList->getTotalLength() == timeline->getTotalLength());
+        seg = segmentList->getMediaSegment(44 + 2);
+        Expect(seg);
+        Expect(seg == allsegments.at(0));
+        Expect(segmentList->getMediaSegment(44 + 6) == nullptr); /* restricted window */
+
+        timeline->addElement(44 + 5, 100, 1, START + 5*100);
+        Expect(timeline->getTotalLength() == 7 * 100);
+        seg = segmentList->getMediaSegment(44 + 6);
+        Expect(seg);
+        Expect(seg == allsegments.at(1));
+
+        delete segmentList;
+
+    } catch (...) {
+        delete segmentList;
+        delete segmentList2;
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/modules/demux/adaptive/test/playlist/SegmentTemplate.cpp b/modules/demux/adaptive/test/playlist/SegmentTemplate.cpp
new file mode 100644
index 0000000000..9f3d8b8c51
--- /dev/null
+++ b/modules/demux/adaptive/test/playlist/SegmentTemplate.cpp
@@ -0,0 +1,128 @@
+/*****************************************************************************
+ *
+ *****************************************************************************
+ * Copyright (C) 2020 VideoLabs, VideoLAN and VLC Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "../../playlist/Segment.h"
+#include "../../playlist/SegmentTemplate.h"
+#include "../../playlist/SegmentTimeline.h"
+#include "../../playlist/BasePlaylist.hpp"
+#include "../../playlist/BasePeriod.h"
+#include "../../playlist/Inheritables.hpp"
+#include "../../../dash/mpd/AdaptationSet.h"
+#include "../../../dash/mpd/Representation.h"
+
+#include "../test.hpp"
+
+#include <limits>
+
+using namespace adaptive;
+using namespace adaptive::playlist;
+using namespace dash::mpd;
+
+int SegmentTemplate_test()
+{
+    BasePlaylist *pl = nullptr;
+    try
+    {
+        pl = new BasePlaylist(nullptr);
+        BasePeriod *period = nullptr;
+        AdaptationSet *set = nullptr;
+        Representation *rep = nullptr;
+        try
+        {
+            period = new BasePeriod(pl);
+            set = new AdaptationSet(period);
+            rep = new Representation(set);
+        } catch(...) {
+            delete period;
+            delete set;
+            std::rethrow_exception(std::current_exception());
+        }
+        set->addRepresentation(rep);
+        period->addAdaptationSet(set);
+        pl->addPeriod(period);
+
+        Timescale timescale(100);
+        rep->addAttribute(new TimescaleAttr(timescale));
+        SegmentTemplate *templ = new SegmentTemplate(new SegmentTemplateSegment(), rep);
+        rep->addAttribute(templ);
+        templ->addAttribute(new StartnumberAttr(11));
+        std::string res = rep->contextualize(0, "$Number$.m4v", templ);
+        Expect(res == "0.m4v");
+
+        /* No timeline, no start/end, no segment duration known */
+        Expect(templ->getStartSegmentNumber() == 11);
+        //Expect(templ->getMediaSegment(11) == nullptr);
+        uint64_t number;
+        Expect(templ->getSegmentNumberByTime(timescale.ToTime(0), &number) == false);
+        vlc_tick_t time, duration;
+        //Expect(templ->getPlaybackTimeDurationBySegmentNumber(11, &time, &duration) == false);
+        Expect(templ->getMinAheadTime(11) == 0);
+
+        /* No timeline, no start/end, duration known */
+        rep->addAttribute(new DurationAttr(100));
+        Expect(templ->getSegmentNumberByTime(timescale.ToTime(500), &number) == true);
+        Expect(number == 11 + 5);
+        Expect(templ->getPlaybackTimeDurationBySegmentNumber(11 + 2, &time, &duration) == true);
+        Expect(time == timescale.ToTime(2 * 100));
+        Expect(duration == timescale.ToTime(100));
+        Expect(templ->getMediaSegment(11 + 2) != nullptr);
+
+        /* start/end, duration known */
+        vlc_tick_t now = timescale.ToTime(1000000);
+        pl->availabilityStartTime.Set(now);
+        pl->availabilityEndTime.Set(now + timescale.ToTime(100 * 20));
+        Expect(templ->getLiveTemplateNumber(now, true) == templ->getStartSegmentNumber());
+        //Expect(templ->getLiveTemplateNumber(now / 2, true) == std::numeric_limits<uint64_t>::max());
+        Expect(templ->getLiveTemplateNumber(now + timescale.ToTime(100) * 2 + 1, true) ==
+               templ->getStartSegmentNumber() + 1);
+
+        /* reset */
+        pl->availabilityStartTime.Set(0);
+        pl->availabilityEndTime.Set(0);
+
+        /* timeline */
+        const stime_t START = 1337;
+        SegmentTimeline *timeline = new SegmentTimeline(nullptr);
+        templ->addAttribute(timeline);
+        timeline->addElement(44,     100, 4, START);
+        timeline->addElement(44 + 5,  33, 4, START + 5 * 100);
+        Expect(templ->getMediaSegment(44 - 1) == nullptr);
+        Expect(templ->getMediaSegment(44 + 5 + 4) != nullptr);
+        Expect(templ->getMediaSegment(44 + 5 + 5) == nullptr);
+        Expect(templ->getStartSegmentNumber() == 44);
+        Expect(templ->getSegmentNumberByTime(timescale.ToTime(START + 5 * 100 + 2), &number) == true);
+        Expect(number == 44 + 5);
+        Expect(templ->getPlaybackTimeDurationBySegmentNumber(44 + 6, &time, &duration) == true);
+        Expect(time == timescale.ToTime(START + 5 * 100 + 33));
+        Expect(duration == timescale.ToTime(33));
+        Expect(templ->getMinAheadTime(44+6) == timescale.ToTime(3 * 33));
+
+        delete pl;
+
+    } catch (...) {
+        delete pl;
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/modules/demux/adaptive/test/playlist/SegmentTimeline.cpp b/modules/demux/adaptive/test/playlist/SegmentTimeline.cpp
new file mode 100644
index 0000000000..706bd20420
--- /dev/null
+++ b/modules/demux/adaptive/test/playlist/SegmentTimeline.cpp
@@ -0,0 +1,184 @@
+/*****************************************************************************
+ *
+ *****************************************************************************
+ * Copyright (C) 2020 VideoLabs, VideoLAN and VLC Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "../../playlist/Segment.h"
+#include "../../playlist/SegmentTimeline.h"
+#include "../../playlist/BasePeriod.h"
+#include "../../playlist/BaseAdaptationSet.h"
+#include "../../playlist/BaseRepresentation.h"
+
+#include "../test.hpp"
+
+#include <limits>
+
+using namespace adaptive;
+using namespace adaptive::playlist;
+
+int Timeline_test()
+{
+    SegmentTimeline *timeline = nullptr;
+    SegmentTimeline *timeline2 = nullptr;
+    try
+    {
+        timeline = new SegmentTimeline(nullptr);
+        /* Check failures */
+        Expect(timeline->getTotalLength() == 0);
+        Expect(timeline->getElementIndexBySequence(123) == std::numeric_limits<uint64_t>::max());
+        Expect(timeline->getElementNumberByScaledPlaybackTime(123) == 0);
+        Expect(timeline->getMinAheadScaledTime(0) == 0);
+        Expect(timeline->minElementNumber() == 0);
+        Expect(timeline->maxElementNumber() == 0);
+
+        /* Simple elements list */
+        const stime_t START = 1337;
+        timeline->addElement(11, 100, 0, START);
+        timeline->addElement(12, 50, 0, 0);
+        timeline->addElement(13, 25, 0, 0);
+
+        Expect(timeline->minElementNumber() == 11);
+        Expect(timeline->maxElementNumber() == 13);
+        Expect(timeline->getTotalLength() == 175);
+        uint64_t idx = timeline->getElementIndexBySequence(0);
+        Expect(idx == std::numeric_limits<uint64_t>::max());
+        idx = timeline->getElementIndexBySequence(100);
+        Expect(idx == std::numeric_limits<uint64_t>::max());
+        idx = timeline->getElementIndexBySequence(11);
+        Expect(idx == 0);
+        idx = timeline->getElementIndexBySequence(13);
+        Expect(idx == 2);
+
+        Expect(timeline->getMinAheadScaledTime(11) == 75);
+        Expect(timeline->getMinAheadScaledTime(12) == 25);
+        Expect(timeline->getMinAheadScaledTime(14) == 0);
+        Expect(timeline->getElementIndexBySequence(13) == 2);
+        Expect(timeline->getScaledPlaybackTimeByElementNumber(11) == START+0);
+        Expect(timeline->getScaledPlaybackTimeByElementNumber(13) == START+150);
+        stime_t time, duration;
+        Expect(timeline->getScaledPlaybackTimeDurationBySegmentNumber(12, &time, &duration));
+        Expect(time == START+100);
+        Expect(duration == 50);
+        Expect(timeline->getElementNumberByScaledPlaybackTime(START-1) == 11);
+        Expect(timeline->getElementNumberByScaledPlaybackTime(START+9) == 11);
+        Expect(timeline->getElementNumberByScaledPlaybackTime(START+151) == 13);
+
+        /* Check repeats */
+        timeline->addElement(14, 100, 1, 0);
+        Expect(timeline->minElementNumber() == 11);
+        Expect(timeline->maxElementNumber() == 14 + 1);
+        Expect(timeline->getTotalLength() == 175 + 100*2);
+        Expect(timeline->getElementIndexBySequence(14) == 3);
+        Expect(timeline->getElementIndexBySequence(15) == 3);
+        timeline->addElement(16, 20, 9, 0);
+        Expect(timeline->maxElementNumber() == 16 + 9);
+        Expect(timeline->getTotalLength() == 175 + 100*2 + 20*10);
+
+        Expect(timeline->getMinAheadScaledTime(14) == 100 + 20 * 10);
+        Expect(timeline->getMinAheadScaledTime(15) == 20 * 10);
+        Expect(timeline->getMinAheadScaledTime(20) == 20 * 5);
+
+        Expect(timeline->getScaledPlaybackTimeByElementNumber(15) == START + 175 + 100);
+        Expect(timeline->getScaledPlaybackTimeByElementNumber(21) == START + 175 + 100*2 + 20*5);
+
+        Expect(timeline->getElementNumberByScaledPlaybackTime(START + 175 + 100 + 10) == 15);
+
+        /* Check discontinuity */
+        timeline->addElement(40, 33, 1, START + 1000);
+        Expect(timeline->maxElementNumber() == 41);
+        Expect(timeline->getTotalLength() == 175 + 100*2 + 20*10 + 66);
+        Expect(timeline->getElementIndexBySequence(39) == std::numeric_limits<uint64_t>::max());
+        Expect(timeline->getElementIndexBySequence(41) == 5);
+
+        /* Pruning */
+        Expect(timeline->pruneBySequenceNumber(24) == 5+8);
+        Expect(timeline->minElementNumber() == 24);
+        Expect(timeline->getTotalLength() == 20 * 2 + 33 * 2);
+
+        Timescale timescale(100);
+        timeline->addAttribute(new TimescaleAttr(timescale));
+        timeline->pruneByPlaybackTime(timescale.ToTime(START + 175 + 100*2 + 20*9 + 2));
+        Expect(timeline->minElementNumber() == 25);
+        Expect(timeline->getTotalLength() == 20 + 33 * 2);
+        timeline->pruneByPlaybackTime(timescale.ToTime(START + 175 + 100*2 + 20*11));
+        Expect(timeline->minElementNumber() == 25); /* tried to expurge before discontinuity time */
+        timeline->pruneByPlaybackTime(timescale.ToTime(START + 1000 + 2));
+        Expect(timeline->minElementNumber() == 40);
+        Expect(timeline->getTotalLength() == 33 * 2);
+        Expect(timeline->pruneBySequenceNumber(50) == 2);
+        Expect(timeline->getTotalLength() == 0);
+
+        /* Merging */
+        timeline->addElement(1, 1000, 0, START);
+        timeline->addElement(2, 2000, 1, 0);
+        Expect(timeline->minElementNumber() == 1);
+        Expect(timeline->maxElementNumber() == 2+1);
+        Expect(timeline->getTotalLength() == 1000 + 2000 * 2);
+
+        timeline2 = new SegmentTimeline(nullptr);
+        timeline2->addAttribute(new TimescaleAttr(timescale));
+        timeline2->addElement(1, 1000, 0, START);
+        timeline2->addElement(2, 2000, 1, 0);
+        Expect(timeline2->minElementNumber() == 1);
+        Expect(timeline->maxElementNumber() == 2+1);
+
+        timeline->updateWith(*timeline2); /* should do no change */
+        Expect(timeline->minElementNumber() == 1);
+        Expect(timeline->maxElementNumber() == 2+1);
+        Expect(timeline->getTotalLength() == 1000 + 2000 * 2);
+
+        delete timeline2;
+        timeline2 = new SegmentTimeline(nullptr);
+        timeline2->addElement(1, 1000, 0, START);
+        timeline2->addElement(2, 2000, 1, 0);
+        timeline2->addElement(4, 2, 99, 0);
+        Expect(timeline2->maxElementNumber() == 4+99);
+        Expect(timeline2->getTotalLength() == 1000 + 2000 * 2 + 2 * 100);
+
+        timeline->updateWith(*timeline2); /* should append missing content */
+        Expect(timeline->maxElementNumber() == 4+99);
+        Expect(timeline->getTotalLength() == 1000 + 2000 * 2 + 2 * 100);
+
+        delete timeline2;
+        timeline2 = new SegmentTimeline(nullptr);
+        /* add 0 more times at 1 inside offset, should not change anything */
+        timeline2->addElement(4+1, 2, 99-1, START+1000 + 2000 * 2 + 2 * 1);
+        timeline->updateWith(*timeline2);
+        Expect(timeline->maxElementNumber() == 4+99);
+
+        delete timeline2;
+        timeline2 = new SegmentTimeline(nullptr);
+        /* add 10 more times at 1 inside offset, should extend by 10 repeats */
+        timeline2->addElement(4+1, 2, 99-1+10, START+1000 + 2000 * 2 + 2 * 1);
+        timeline->updateWith(*timeline2);
+        Expect(timeline->maxElementNumber() == 4+99+10);
+
+        delete timeline;
+        delete timeline2;
+
+    } catch (...) {
+        delete timeline;
+        delete timeline2;
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/modules/demux/adaptive/test/plumbing/CommandsQueue.cpp b/modules/demux/adaptive/test/plumbing/CommandsQueue.cpp
new file mode 100644
index 0000000000..bba807e416
--- /dev/null
+++ b/modules/demux/adaptive/test/plumbing/CommandsQueue.cpp
@@ -0,0 +1,327 @@
+/*****************************************************************************
+ *
+ *****************************************************************************
+ * Copyright (C) 2020 VideoLabs, VideoLAN and VLC Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "../../plumbing/FakeESOut.hpp"
+#include "../../plumbing/FakeESOutID.hpp"
+#include "../../plumbing/CommandsQueue.hpp"
+
+#include "../test.hpp"
+
+#include <vlc_block.h>
+
+#include <limits>
+#include <list>
+#include <algorithm>
+
+using namespace adaptive;
+
+using OutputVal = std::pair<const AbstractFakeESOutID *, block_t *>;
+
+class TestEsOut : public AbstractFakeEsOut
+{
+    public:
+        TestEsOut() {}
+        virtual ~TestEsOut() { cleanup(); }
+        virtual void recycle(AbstractFakeESOutID *) override {}
+        virtual void createOrRecycleRealEsID(AbstractFakeESOutID *) override {}
+        virtual void setPriority(int) override {}
+        virtual void sendData(AbstractFakeESOutID *id, block_t *b) override
+        {
+            output.push_back(OutputVal(id, b));
+        }
+        virtual void sendMeta(int, const vlc_meta_t *) override {}
+
+        std::list<OutputVal> output;
+        void cleanup()
+        {
+            while(!output.empty())
+            {
+                block_Release(output.front().second);
+                output.pop_front();
+            }
+        }
+
+    private:
+        virtual es_out_id_t *esOutAdd(const es_format_t *) override { return nullptr; }
+        virtual int esOutSend(es_out_id_t *, block_t *) override { return VLC_SUCCESS; }
+        virtual void esOutDel(es_out_id_t *) override {}
+        virtual int esOutControl(int, va_list) override { return VLC_SUCCESS; }
+        virtual void esOutDestroy() override {}
+};
+
+class TestEsOutID : public AbstractFakeESOutID
+{
+    public:
+        TestEsOutID(TestEsOut *out) { this->out = out; }
+        virtual ~TestEsOutID() {}
+        virtual es_out_id_t * realESID() override { return nullptr; }
+        virtual void create() override {}
+        virtual void release() override {}
+        virtual void sendData(block_t *b) override
+        {
+            out->sendData(this, b);
+        }
+
+    private:
+        TestEsOut *out;
+};
+
+int CommandsQueue_test()
+{
+    TestEsOut esout;
+    TestEsOutID *id0 = nullptr;
+    TestEsOutID *id1 = nullptr;
+    try
+    {
+        CommandsFactory *factory = new CommandsFactory();
+        CommandsQueue queue(factory);
+
+        id0 = new TestEsOutID(&esout);
+        AbstractCommand *cmd = nullptr;
+
+        Expect(queue.isEOF() == false);
+        Expect(queue.isDraining() == false);
+        Expect(queue.isEmpty() == true);
+        Expect(queue.getDemuxedAmount(VLC_TICK_0) == 0);
+        Expect(queue.getBufferingLevel() == VLC_TICK_INVALID);
+        Expect(queue.getFirstDTS() == VLC_TICK_INVALID);
+        Expect(queue.getPCR() == VLC_TICK_INVALID);
+        cmd = queue.factory()->createEsOutAddCommand(id0);
+        queue.Schedule(cmd);
+        cmd = queue.factory()->createEsOutDelCommand(id0);
+        queue.Schedule(cmd);
+        for(size_t i=0; i<3; i++) /* Add / Del will return in between */
+            queue.Process(std::numeric_limits<vlc_tick_t>::max());
+        Expect(queue.isEmpty() == false); /* no PCR issued nor commit */
+        queue.Commit();
+        for(size_t i=0; i<3; i++)
+            queue.Process(std::numeric_limits<vlc_tick_t>::max());
+        Expect(queue.isEOF() == false);
+        Expect(queue.isDraining() == false);
+        Expect(queue.isEmpty() == true);
+        Expect(queue.getDemuxedAmount(VLC_TICK_0) == 0);
+        Expect(queue.getBufferingLevel() == VLC_TICK_INVALID);
+        Expect(queue.getPCR() == std::numeric_limits<vlc_tick_t>::max());
+
+        queue.Abort(true);
+        esout.cleanup();
+
+        /* Feed data in */
+        for(size_t i=0; i<10; i++)
+        {
+            block_t *data = block_Alloc(0);
+            Expect(data);
+            data->i_dts = VLC_TICK_0 + vlc_tick_from_sec(i);
+            cmd = queue.factory()->createEsOutSendCommand(id0, data);
+            queue.Schedule(cmd);
+        }
+        Expect(queue.getPCR() == VLC_TICK_INVALID);
+        Expect(queue.getFirstDTS() == VLC_TICK_INVALID);
+        Expect(queue.getDemuxedAmount(VLC_TICK_0) == 0);
+        Expect(queue.getBufferingLevel() == VLC_TICK_INVALID);
+        /* commit some */
+        cmd = queue.factory()->createEsOutControlPCRCommand(0, VLC_TICK_0 + vlc_tick_from_sec(8));
+        queue.Schedule(cmd);
+        Expect(queue.getDemuxedAmount(VLC_TICK_0) == vlc_tick_from_sec(8)); /* PCR committed data up to 8s */
+        Expect(queue.getBufferingLevel() == VLC_TICK_0 + vlc_tick_from_sec(8));
+        Expect(queue.getDemuxedAmount(VLC_TICK_0 + vlc_tick_from_sec(8)) == 0);
+        Expect(queue.getDemuxedAmount(VLC_TICK_0 + vlc_tick_from_sec(7)) == vlc_tick_from_sec(1));
+        Expect(queue.getPCR() == VLC_TICK_INVALID);
+        /* extend through PCR */
+        cmd = queue.factory()->createEsOutControlPCRCommand(0, VLC_TICK_0 + vlc_tick_from_sec(10));
+        queue.Schedule(cmd);
+        Expect(queue.getBufferingLevel() == VLC_TICK_0 + vlc_tick_from_sec(10));
+        Expect(queue.getDemuxedAmount(VLC_TICK_0) == vlc_tick_from_sec(10));
+
+        /* dequeue */
+        queue.Process(VLC_TICK_0 + vlc_tick_from_sec(3));
+        Expect(queue.getPCR() == VLC_TICK_0 + vlc_tick_from_sec(3));
+        Expect(queue.getFirstDTS() == VLC_TICK_0 + vlc_tick_from_sec(3));
+        Expect(queue.getDemuxedAmount(VLC_TICK_0 + vlc_tick_from_sec(3)) == vlc_tick_from_sec(7));
+        Expect(queue.getDemuxedAmount(VLC_TICK_0 + vlc_tick_from_sec(4)) == vlc_tick_from_sec(6));
+
+        /* drop */
+        queue.setDrop(true);
+        do
+        {
+            block_t *data = block_Alloc(0);
+            Expect(data);
+            data->i_dts = VLC_TICK_0 + vlc_tick_from_sec(11);
+            cmd = queue.factory()->createEsOutSendCommand(id0, data);
+            queue.Schedule(cmd);
+        } while(0);
+        cmd = queue.factory()->createEsOutControlPCRCommand(0, VLC_TICK_0 + vlc_tick_from_sec(11));
+        queue.Schedule(cmd);
+        Expect(queue.getPCR() == VLC_TICK_0 + vlc_tick_from_sec(3)); /* should be unchanged */
+        Expect(queue.getDemuxedAmount(VLC_TICK_0 + vlc_tick_from_sec(3)) == vlc_tick_from_sec(7));
+        queue.setDrop(false);
+
+        /* empty */
+        Expect(queue.getPCR() == VLC_TICK_0 + vlc_tick_from_sec(3));
+        queue.Process(VLC_TICK_0 + vlc_tick_from_sec(13));
+        Expect(queue.isEmpty());
+        Expect(queue.getPCR() == VLC_TICK_0 + vlc_tick_from_sec(9));
+
+        queue.Abort(true);
+        esout.cleanup();
+
+        /* reordering */
+        id1 = new TestEsOutID(&esout);
+        const vlc_tick_t OFFSET = vlc_tick_from_sec(100);
+        for(size_t j=0; j<2; j++)
+        {
+            TestEsOutID *id = (j % 2) ? id1 : id0;
+            for(size_t i=0; i<5; i++)
+            {
+                block_t *data = block_Alloc(0);
+                Expect(data);
+                data->i_dts = VLC_TICK_0 + OFFSET + vlc_tick_from_sec(i);
+                cmd = queue.factory()->createEsOutSendCommand(id, data);
+                queue.Schedule(cmd);
+            }
+        }
+
+        cmd = queue.factory()->createEsOutControlPCRCommand(0, VLC_TICK_0 + OFFSET + vlc_tick_from_sec(10));
+        queue.Schedule(cmd);
+        Expect(esout.output.empty());
+        queue.Process(VLC_TICK_0 + OFFSET - 1);
+        Expect(esout.output.empty());
+        queue.Process(VLC_TICK_0 + OFFSET + vlc_tick_from_sec(10));
+        Expect(esout.output.size() == 10);
+        for(size_t i=0; i<5; i++)
+        {
+            const vlc_tick_t now = VLC_TICK_0 + OFFSET + vlc_tick_from_sec(i);
+            OutputVal val = esout.output.front();
+            Expect(val.first == id0);
+            Expect(val.second->i_dts == now);
+            block_Release(val.second);
+            esout.output.pop_front();
+            val = esout.output.front();
+            Expect(val.first == id1);
+            Expect(val.second->i_dts == now);
+            block_Release(val.second);
+            esout.output.pop_front();
+        }
+        Expect(esout.output.empty());
+        queue.Abort(true);
+
+        /* reordering with PCR */
+        for(size_t k=0; k<3; k++)
+        {
+            for(size_t j=0; j<2; j++)
+            {
+                TestEsOutID *id = (j % 2) ? id1 : id0;
+                for(size_t i=0; i<2; i++)
+                {
+                    block_t *data = block_Alloc(0);
+                    Expect(data);
+                    data->i_dts = VLC_TICK_0 + OFFSET + vlc_tick_from_sec(k * 2 + i);
+                    cmd = queue.factory()->createEsOutSendCommand(id, data);
+                    queue.Schedule(cmd);
+                }
+            }
+            cmd = queue.factory()->createEsOutControlPCRCommand(0,
+                    VLC_TICK_0 + OFFSET + vlc_tick_from_sec( (k*2)+1 ));
+            queue.Schedule(cmd);
+        }
+        queue.Process(std::numeric_limits<vlc_tick_t>::max());
+        Expect(esout.output.size() == 12);
+        for(size_t i=0; i<12; i++)
+        {
+            TestEsOutID *id = (i % 2) ? id1 : id0;
+            const vlc_tick_t now = VLC_TICK_0 + OFFSET + vlc_tick_from_sec(i / 2);
+            OutputVal &val = esout.output.front();
+            Expect(val.first == id);
+            Expect(val.second->i_dts == now);
+            block_Release(val.second);
+            esout.output.pop_front();
+        }
+        Expect(esout.output.empty());
+        queue.Abort(true);
+
+        /* reordering with PCR & INVALID */
+        for(size_t k=0; k<3; k++)
+        {
+            for(size_t j=0; j<2; j++)
+            {
+                TestEsOutID *id = (j % 2) ? id1 : id0;
+                for(size_t i=0; i<2; i++)
+                {
+                    block_t *data = block_Alloc(0);
+                    Expect(data);
+                    if(i==0)
+                        data->i_dts = VLC_TICK_0 + OFFSET + vlc_tick_from_sec(k);
+                    cmd = queue.factory()->createEsOutSendCommand(id, data);
+                    queue.Schedule(cmd);
+                }
+            }
+            cmd = queue.factory()->createEsOutControlPCRCommand(0,
+                    VLC_TICK_0 + OFFSET + vlc_tick_from_sec(k));
+            queue.Schedule(cmd);
+        }
+        queue.Process(std::numeric_limits<vlc_tick_t>::max());
+        Expect(esout.output.size() == 12);
+        for(size_t i=0; i<6; i++)
+        {
+            TestEsOutID *id = (i % 2) ? id1 : id0;
+            const vlc_tick_t now = VLC_TICK_0 + OFFSET + vlc_tick_from_sec(i/2);
+            OutputVal val = esout.output.front();
+            Expect(val.first == id);
+            Expect(val.second->i_dts == now);
+            block_Release(val.second);
+            esout.output.pop_front();
+            val = esout.output.front();
+            Expect(val.first == id);
+            Expect(val.second->i_dts == VLC_TICK_INVALID);
+            block_Release(val.second);
+            esout.output.pop_front();
+        }
+        Expect(esout.output.empty());
+        queue.Abort(true);
+
+        /* reordering PCR before PTS */
+        for(size_t i=0; i<2; i++)
+        {
+            const vlc_tick_t now = VLC_TICK_0 + OFFSET + vlc_tick_from_sec(i);
+            cmd = queue.factory()->createEsOutControlPCRCommand(0, now);
+            queue.Schedule(cmd);
+            block_t *data = block_Alloc(0);
+            Expect(data);
+            data->i_dts = now;
+            cmd = queue.factory()->createEsOutSendCommand(id0, data);
+            queue.Schedule(cmd);
+        }
+        queue.Process(VLC_TICK_0 + OFFSET + vlc_tick_from_sec(0));
+        Expect(esout.output.size() == 1);
+
+    } catch(...) {
+        delete id0;
+        delete id1;
+        return 1;
+    }
+
+    delete id0;
+    delete id1;
+
+    return 0;
+}
diff --git a/modules/demux/adaptive/test/test.cpp b/modules/demux/adaptive/test/test.cpp
index 07ce55226a..68cf52c708 100644
--- a/modules/demux/adaptive/test/test.cpp
+++ b/modules/demux/adaptive/test/test.cpp
@@ -27,9 +27,25 @@
 
 #include "test.hpp"
 
+#include <iostream>
+
 extern const char vlc_module_name[] = "foobar";
 
+#define TEST(func) []() { std::cerr << "Testing "#func << std::endl;\
+                          return func##_test(); }()
+
 int main()
 {
-    return TemplatedUri_test();
+    return
+    TEST(Inheritables) ||
+    TEST(SegmentBase) ||
+    TEST(SegmentList) ||
+    TEST(SegmentTemplate) ||
+    TEST(Timeline) ||
+    TEST(Conversions) ||
+    TEST(TemplatedUri) ||
+    TEST(BufferingLogic) ||
+    TEST(CommandsQueue) ||
+    TEST(M3U8MasterPlaylist) ||
+    TEST(M3U8Playlist);
 }
diff --git a/modules/demux/adaptive/test/test.hpp b/modules/demux/adaptive/test/test.hpp
index 3f39ae15dd..a2c27f57a7 100644
--- a/modules/demux/adaptive/test/test.hpp
+++ b/modules/demux/adaptive/test/test.hpp
@@ -36,6 +36,16 @@
 
 #define Expect(testcond) DoExpect((testcond), __FUNCTION__, __LINE__)
 
+int Inheritables_test();
 int TemplatedUri_test();
+int SegmentBase_test();
+int SegmentList_test();
+int SegmentTemplate_test();
+int Timeline_test();
+int Conversions_test();
+int M3U8MasterPlaylist_test();
+int M3U8Playlist_test();
+int CommandsQueue_test();
+int BufferingLogic_test();
 
 #endif
diff --git a/modules/demux/adaptive/test/tools/Conversions.cpp b/modules/demux/adaptive/test/tools/Conversions.cpp
new file mode 100644
index 0000000000..e54bf0d833
--- /dev/null
+++ b/modules/demux/adaptive/test/tools/Conversions.cpp
@@ -0,0 +1,67 @@
+/*****************************************************************************
+ *
+ *****************************************************************************
+ * Copyright (C) 2020 VideoLabs, VideoLAN and VLC Authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "../../tools/Conversions.hpp"
+
+#include "../test.hpp"
+
+#include <limits>
+
+int Conversions_test()
+{
+    UTCTime utc("1970-01-01T00:00:00.000+00:00");
+    Expect(utc.mtime() == 0);
+    utc = UTCTime("1970-01-01T10:00:00.000+10:00");
+    Expect(utc.mtime() == 0);
+    utc = UTCTime("1970-01-01T02:00:00.000+01:02");
+    Expect(utc.mtime() == vlc_tick_from_sec(7200 - 3720));
+    utc = UTCTime("1970-01-01T02:00:00.000+0102");
+    Expect(utc.mtime() == vlc_tick_from_sec(7200 - 3720));
+    utc = UTCTime("1970-01-01T01:10:00.000+1");
+    Expect(utc.mtime() == vlc_tick_from_sec(10*60));
+    utc = UTCTime("2019-06-11Z");
+    Expect(utc.mtime() == vlc_tick_from_sec(1560211200));
+    utc = UTCTime("2019-06-11T16:52:05.100Z");
+    Expect(utc.mtime() == vlc_tick_from_sec(1560271925) + VLC_TICK_FROM_MS(100));
+    utc = UTCTime("2019-06-11T16:52:05.012Z");
+    Expect(utc.mtime() == vlc_tick_from_sec(1560271925) + VLC_TICK_FROM_MS(12));
+    utc = UTCTime("T16:52:05.012Z");
+
+
+    IsoTime isotime("PT0H9M56.46S");
+    Expect(isotime == (vlc_tick_from_sec(9*60+56)+VLC_TICK_FROM_MS(460)));
+    isotime = IsoTime("HELLO");
+    Expect(isotime == -1);
+    isotime = IsoTime("P1D");
+    Expect(isotime == vlc_tick_from_sec(86400));
+    isotime = IsoTime("PT2.5M");
+    Expect(isotime == vlc_tick_from_sec(150));
+    isotime = IsoTime("PT");
+    Expect(isotime == 0);
+    isotime = IsoTime("PT.5S");
+    Expect(isotime == VLC_TICK_FROM_MS(500));
+    isotime = IsoTime("PT.010S");
+    Expect(isotime == VLC_TICK_FROM_MS(10));
+
+    return 0;
+}



More information about the vlc-commits mailing list