[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