From 8d89ff4618d01150054cdc14a872a8d0f3fcc29d Mon Sep 17 00:00:00 2001 From: Glenn Guy Date: Sat, 14 Aug 2021 17:33:01 +1000 Subject: [PATCH] Fix tests The list of downloaded segment urls is now kept within the stream itself. This list also includes the initialization segment so tests are updated accordingly. The amount of segments that the worker thread downloads is not reliably predictable. Sometimes it will get only the amount of segments that we ask to 'read' but other times it will run away and start filling the available buffers on top of the reads requested, making the previous use of `downloadedUrls.back()` unreliable. --- src/common/AdaptiveStream.h | 23 +++--- src/main.h | 1 + src/test/TestDASHTree.cpp | 143 +++++++++++++++++++----------------- src/test/TestHelper.cpp | 17 ++++- src/test/TestHelper.h | 23 +++++- 5 files changed, 122 insertions(+), 85 deletions(-) diff --git a/src/common/AdaptiveStream.h b/src/common/AdaptiveStream.h index e2cc3a2da..ea0626f69 100644 --- a/src/common/AdaptiveStream.h +++ b/src/common/AdaptiveStream.h @@ -89,6 +89,18 @@ namespace adaptive bool write_data(const void* buffer, size_t buffer_size, std::string* lockfreeBuffer); virtual void SetLastUpdated(std::chrono::system_clock::time_point tm) {}; std::chrono::time_point lastUpdated_; + virtual bool download_segment(); + std::string download_url_; + std::map media_headers_, download_headers_; + struct SEGMENTBUFFER + { + std::string buffer; + AdaptiveTree::Segment segment; + unsigned int segment_number; + AdaptiveTree::Representation* rep; + }; + std::vector segment_buffers_; + private: enum STATE @@ -103,7 +115,6 @@ namespace adaptive void ResetSegment(const AdaptiveTree::Segment* segment); void ResetActiveBuffer(bool oneValid); void StopWorker(STATE state); - bool download_segment(); void worker(); bool prepareNextDownload(); bool prepareDownload(const AdaptiveTree::Representation* rep, @@ -146,17 +157,8 @@ namespace adaptive AdaptiveTree::Period* current_period_; AdaptiveTree::AdaptationSet* current_adp_; AdaptiveTree::Representation *current_rep_; - std::string download_url_; static const size_t MAXSEGMENTBUFFER; - struct SEGMENTBUFFER - { - std::string buffer; - AdaptiveTree::Segment segment; - unsigned int segment_number; - AdaptiveTree::Representation* rep; - }; - std::vector segment_buffers_; // number of segmentbuffers whith valid segment, always >= valid_segment_buffers_ size_t available_segment_buffers_; // number of segment_buffers which are downloaded / downloading @@ -167,7 +169,6 @@ namespace adaptive AdaptiveTree::Representation *prev_rep_; // used for rep_counter_ AdaptiveTree::Representation* last_rep_; // used to align new live rep with old - std::map media_headers_, download_headers_; std::size_t segment_read_pos_; uint64_t absolute_position_; uint64_t currentPTSOffset_, absolutePTSOffset_; diff --git a/src/main.h b/src/main.h index 52e22bb1d..f08b4680c 100644 --- a/src/main.h +++ b/src/main.h @@ -79,6 +79,7 @@ class ATTRIBUTE_HIDDEN KodiAdaptiveStream : public adaptive::AdaptiveStream std::string* lockfreeBuffer) override; bool parseIndexRange(adaptive::AdaptiveTree::Representation* rep, const std::string& buffer) override; + bool download_segment() override { return AdaptiveStream::download_segment(); } private: DefaultRepresentationChooser* chooser_ = nullptr; diff --git a/src/test/TestDASHTree.cpp b/src/test/TestDASHTree.cpp index c788ecb2f..f49ec9ecb 100644 --- a/src/test/TestDASHTree.cpp +++ b/src/test/TestDASHTree.cpp @@ -1,5 +1,6 @@ #include "TestHelper.h" - +#include +#include #include @@ -40,10 +41,13 @@ class DASHTreeAdaptiveStreamTest : public DASHTreeTest protected: void SetUp() override { - testHelper::lastDownloadUrl.clear(); DASHTreeTest::SetUp(); - videoStream = new TestAdaptiveStream(*tree, adaptive::AdaptiveTree::StreamType::VIDEO); - audioStream = new TestAdaptiveStream(*tree, adaptive::AdaptiveTree::StreamType::AUDIO); + m_chooser = new DefaultRepresentationChooser(); + m_chooser->hdcp_override_ = true; + m_chooser->assured_buffer_duration_ = 5; + m_chooser->max_buffer_duration_ = 5; + tree->representation_chooser_ = m_chooser; + } void TearDown() override @@ -52,13 +56,30 @@ class DASHTreeAdaptiveStreamTest : public DASHTreeTest delete audioStream; videoStream = nullptr; audioStream = nullptr; + newStream = nullptr; DASHTreeTest::TearDown(); } + void SetVideoStream(TestAdaptiveStream* newStream) + { + delete videoStream; + videoStream = newStream; + } + + void SetAudioStream(TestAdaptiveStream* newStream) + { + delete audioStream; + audioStream = newStream; + } + + TestAdaptiveStream* NewStream(adaptive::AdaptiveTree::AdaptationSet* adp, bool playTsb=true) + { + return new TestAdaptiveStream(*tree, adp, mediaHeaders, m_chooser, playTsb, 0, false); + } + void ReadSegments(TestAdaptiveStream* stream, uint32_t bytesToRead, - uint32_t reads, - bool clearUrls = true) + uint32_t reads) { // Rudimentary simulation of running a stream and consuming segment data. // Normally AdaptiveStream::read is called from a sample reader for the exact @@ -66,22 +87,18 @@ class DASHTreeAdaptiveStreamTest : public DASHTreeTest // exhausted. Here our segments are a fixed size (16 bytes) and for testing we can // optimally call to read 1 segment per AdaptiveStream::read - if (clearUrls) - downloadedUrls.clear(); - for (unsigned int i = 0; i < reads; i++) - if (stream->read(buf, bytesToRead)) - downloadedUrls.push_back(testHelper::lastDownloadUrl); - else + if (!stream->read(buf, bytesToRead)) break; // Decrement last updated time so live manifest will always refresh on each segment // in order to test manifest update changes tree->SetLastUpdated(std::chrono::system_clock::now() - std::chrono::seconds(2)); stream->SetLastUpdated(std::chrono::system_clock::now() - std::chrono::seconds(2)); } - - TestAdaptiveStream* videoStream; - TestAdaptiveStream* audioStream; + DefaultRepresentationChooser* m_chooser; + TestAdaptiveStream* videoStream = nullptr; + TestAdaptiveStream* audioStream = nullptr; + TestAdaptiveStream* newStream = nullptr; std::vector downloadedUrls; std::map mediaHeaders; unsigned char buf[16]; @@ -254,55 +271,50 @@ TEST_F(DASHTreeTest, CalculateCorrectFpsScaleFromAdaptionSet) TEST_F(DASHTreeAdaptiveStreamTest, replacePlaceHolders) { OpenTestFile("mpd/placeholders.mpd", "https://foo.bar/placeholders.mpd", ""); - - videoStream->prepare_stream(tree->current_period_->adaptationSets_[0], 0, 0, 0, 0, 0, 0, 0, - mediaHeaders); - videoStream->start_stream(~0, 0, 0, true); + tree->has_timeshift_buffer_ = false; + SetVideoStream(NewStream(tree->periods_[0]->adaptationSets_[0])); + + videoStream->start_stream(); ReadSegments(videoStream, 16, 5); - EXPECT_EQ(downloadedUrls[0], "https://foo.bar/videosd-400x224/segment_487050.m4s"); - EXPECT_EQ(downloadedUrls.back(), "https://foo.bar/videosd-400x224/segment_487054.m4s"); - - videoStream->prepare_stream(tree->current_period_->adaptationSets_[1], 0, 0, 0, 0, 0, 0, 0, - mediaHeaders); - videoStream->start_stream(~0, 0, 0, true); + EXPECT_EQ(videoStream->download_list_[0], "https://foo.bar/videosd-400x224/init.mp4"); + EXPECT_EQ(videoStream->download_list_[4], "https://foo.bar/videosd-400x224/segment_487053.m4s"); + + SetVideoStream(NewStream(tree->periods_[0]->adaptationSets_[1])); + videoStream->start_stream(); ReadSegments(videoStream, 16, 5); - EXPECT_EQ(downloadedUrls[0], "https://foo.bar/videosd-400x224/segment_00487050.m4s"); - EXPECT_EQ(downloadedUrls.back(), "https://foo.bar/videosd-400x224/segment_00487054.m4s"); - - videoStream->prepare_stream(tree->current_period_->adaptationSets_[2], 0, 0, 0, 0, 0, 0, 0, - mediaHeaders); - videoStream->start_stream(~0, 0, 0, true); + EXPECT_EQ(videoStream->download_list_[1], "https://foo.bar/videosd-400x224/segment_00487050.m4s"); + EXPECT_EQ(videoStream->download_list_[4], "https://foo.bar/videosd-400x224/segment_00487053.m4s"); + + + SetVideoStream(NewStream(tree->periods_[0]->adaptationSets_[2])); + videoStream->start_stream(); ReadSegments(videoStream, 16, 5); - EXPECT_EQ(downloadedUrls[0], "https://foo.bar/videosd-400x224/segment_263007000000.m4s"); - EXPECT_EQ(downloadedUrls.back(), "https://foo.bar/videosd-400x224/segment_263009160000.m4s"); + EXPECT_EQ(videoStream->download_list_[1], "https://foo.bar/videosd-400x224/segment_263007000000.m4s"); + EXPECT_EQ(videoStream->download_list_[4], "https://foo.bar/videosd-400x224/segment_263008620000.m4s"); - videoStream->prepare_stream(tree->current_period_->adaptationSets_[3], 0, 0, 0, 0, 0, 0, 0, - mediaHeaders); - videoStream->start_stream(~0, 0, 0, true); + SetVideoStream(NewStream(tree->periods_[0]->adaptationSets_[3])); + videoStream->start_stream(); ReadSegments(videoStream, 16, 5); - EXPECT_EQ(downloadedUrls[0], "https://foo.bar/videosd-400x224/segment_00263007000000"); - EXPECT_EQ(downloadedUrls.back(), "https://foo.bar/videosd-400x224/segment_00263009160000"); + EXPECT_EQ(videoStream->download_list_[1], "https://foo.bar/videosd-400x224/segment_00263007000000"); + EXPECT_EQ(videoStream->download_list_[4], "https://foo.bar/videosd-400x224/segment_00263008620000"); - videoStream->prepare_stream(tree->current_period_->adaptationSets_[4], 0, 0, 0, 0, 0, 0, 0, - mediaHeaders); - videoStream->start_stream(~0, 0, 0, true); + SetVideoStream(NewStream(tree->periods_[0]->adaptationSets_[4])); + videoStream->start_stream(); ReadSegments(videoStream, 16, 5); - EXPECT_EQ(downloadedUrls[0], "https://foo.bar/videosd-400x224/segment_487050.m4s?t=263007000000"); - EXPECT_EQ(downloadedUrls.back(), "https://foo.bar/videosd-400x224/segment_487054.m4s?t=263009160000"); + EXPECT_EQ(videoStream->download_list_[1], "https://foo.bar/videosd-400x224/segment_487050.m4s?t=263007000000"); + EXPECT_EQ(videoStream->download_list_[4], "https://foo.bar/videosd-400x224/segment_487053.m4s?t=263008620000"); - videoStream->prepare_stream(tree->current_period_->adaptationSets_[5], 0, 0, 0, 0, 0, 0, 0, - mediaHeaders); - videoStream->start_stream(~0, 0, 0, true); + SetVideoStream(NewStream(tree->periods_[0]->adaptationSets_[5])); + videoStream->start_stream(); ReadSegments(videoStream, 16, 5); - EXPECT_EQ(downloadedUrls[0], "https://foo.bar/videosd-400x224/segment_00487050.m4s?t=00263007000000"); - EXPECT_EQ(downloadedUrls.back(), "https://foo.bar/videosd-400x224/segment_00487054.m4s?t=00263009160000"); + EXPECT_EQ(videoStream->download_list_[1], "https://foo.bar/videosd-400x224/segment_00487050.m4s?t=00263007000000"); + EXPECT_EQ(videoStream->download_list_[4], "https://foo.bar/videosd-400x224/segment_00487053.m4s?t=00263008620000"); - videoStream->prepare_stream(tree->current_period_->adaptationSets_[6], 0, 0, 0, 0, 0, 0, 0, - mediaHeaders); - videoStream->start_stream(~0, 0, 0, true); + SetVideoStream(NewStream(tree->periods_[0]->adaptationSets_[6])); + videoStream->start_stream(); ReadSegments(videoStream, 16, 5); - EXPECT_EQ(downloadedUrls[0], "https://foo.bar/videosd-400x224/segment.m4s"); - EXPECT_EQ(downloadedUrls.back(), "https://foo.bar/videosd-400x224/segment.m4s"); + EXPECT_EQ(videoStream->download_list_[0], "https://foo.bar/videosd-400x224/init.mp4"); + EXPECT_EQ(videoStream->download_list_[4], "https://foo.bar/videosd-400x224/segment.m4s"); } TEST_F(DASHTreeTest, updateParameterLiveSegmentTimeline) @@ -405,22 +417,22 @@ TEST_F(DASHTreeAdaptiveStreamTest, subtitles) EXPECT_EQ(tree->periods_[0]->adaptationSets_[11]->type_, DASHTestTree::SUBTITLE); EXPECT_EQ(tree->periods_[0]->adaptationSets_[11]->mimeType_, "application/mp4"); EXPECT_EQ(tree->periods_[0]->adaptationSets_[11]->representations_[0]->codecs_, "stpp"); - videoStream->prepare_stream(tree->periods_[0]->adaptationSets_[11], 0, 0, 0, 0, 0, 0, 0, - mediaHeaders); - videoStream->start_stream(~0, 0, 0, true); + + SetVideoStream(NewStream(tree->periods_[0]->adaptationSets_[11])); + videoStream->start_stream(); ReadSegments(videoStream, 16, 5); - EXPECT_EQ(downloadedUrls[0], "https://foo.bar/11/0001.m4s"); - EXPECT_EQ(downloadedUrls.back(), "https://foo.bar/11/0005.m4s"); + EXPECT_EQ(videoStream->download_list_[0], "https://foo.bar/11/init.mp4"); + EXPECT_EQ(videoStream->download_list_[4], "https://foo.bar/11/0004.m4s"); EXPECT_EQ(tree->periods_[0]->adaptationSets_[12]->type_, DASHTestTree::SUBTITLE); EXPECT_EQ(tree->periods_[0]->adaptationSets_[12]->mimeType_, "application/mp4"); EXPECT_EQ(tree->periods_[0]->adaptationSets_[12]->representations_[0]->codecs_, "stpp.ttml.im1t"); - videoStream->prepare_stream(tree->periods_[0]->adaptationSets_[12], 0, 0, 0, 0, 0, 0, 0, - mediaHeaders); - videoStream->start_stream(~0, 0, 0, true); + + SetVideoStream(NewStream(tree->periods_[0]->adaptationSets_[12])); + videoStream->start_stream(); ReadSegments(videoStream, 16, 5); - EXPECT_EQ(downloadedUrls[0], "https://foo.bar/tears-of-steel-multiple-subtitles-12-0.dash"); - EXPECT_EQ(downloadedUrls.back(), "https://foo.bar/tears-of-steel-multiple-subtitles-12-16000.dash"); + EXPECT_EQ(videoStream->download_list_[0], "https://foo.bar/tears-of-steel-multiple-subtitles-12.dash"); + EXPECT_EQ(videoStream->download_list_[4], "https://foo.bar/tears-of-steel-multiple-subtitles-12-12000.dash"); } TEST_F(DASHTreeTest, CalculateMultipleSegTpl) @@ -482,9 +494,8 @@ TEST_F(DASHTreeTest, CalculateReprensentationBaseURL) TEST_F(DASHTreeAdaptiveStreamTest, MisalignedSegmentTimeline) { OpenTestFile("mpd/bad_segtimeline_1.mpd", "https://foo.bar/placeholders.mpd", ""); - audioStream->prepare_stream(tree->current_period_->adaptationSets_[1], 0, 0, 0, 0, 0, 0, 0, - mediaHeaders); - audioStream->start_stream(~0, 0, 0, false); + SetAudioStream(NewStream(tree->current_period_->adaptationSets_[1])); + audioStream->start_stream(); ReadSegments(audioStream, 16, 1); diff --git a/src/test/TestHelper.cpp b/src/test/TestHelper.cpp index 09470a024..cbc4a5c68 100644 --- a/src/test/TestHelper.cpp +++ b/src/test/TestHelper.cpp @@ -2,7 +2,6 @@ std::string testHelper::testFile; std::string testHelper::effectiveUrl; -std::string testHelper::lastDownloadUrl; void Log(const LogLevel loglevel, const char* format, ...){} @@ -54,16 +53,26 @@ bool adaptive::AdaptiveTree::download(const char* url, return nbRead == 0; } +bool TestAdaptiveStream::download_segment() +{ + if (download_url_.empty()) + return false; + bool ret = download(download_url_.c_str(), download_headers_, nullptr); + if (ret) + download_list_.push_back(download_url_); + return ret; +} + bool TestAdaptiveStream::download(const char* url, - const std::map& mediaHeaders) + const std::map& mediaHeaders, + std::string* lockfreeBuffer) { - testHelper::lastDownloadUrl = url; size_t nbRead = ~0UL; std::stringstream ss("Sixteen bytes!!!"); char buf[16]; size_t nbReadOverall = 0; - while ((nbRead = ss.readsome(buf, 16)) > 0 && ~nbRead && write_data(buf, nbRead)) + while ((nbRead = ss.readsome(buf, 16)) > 0 && ~nbRead && write_data(buf, nbRead, lockfreeBuffer)) nbReadOverall += nbRead; if (!nbReadOverall) diff --git a/src/test/TestHelper.h b/src/test/TestHelper.h index 460984739..fb434869e 100644 --- a/src/test/TestHelper.h +++ b/src/test/TestHelper.h @@ -3,30 +3,45 @@ #include "../common/AdaptiveStream.h" #include "../parser/DASHTree.h" #include "../parser/HLSTree.h" +#include "../common/RepresentationChooser.h" std::string GetEnv(const std::string& var); void SetFileName(std::string& file, const std::string name); void Log(const LogLevel loglevel, const char* format, ...); +struct DefaultRepresentationChooser; + class testHelper { public: static std::string testFile; static std::string effectiveUrl; - static std::string lastDownloadUrl; }; class TestAdaptiveStream : public adaptive::AdaptiveStream { public: - TestAdaptiveStream(adaptive::AdaptiveTree& tree, adaptive::AdaptiveTree::StreamType type) - : adaptive::AdaptiveStream(tree, type){}; + TestAdaptiveStream(adaptive::AdaptiveTree& tree, + adaptive::AdaptiveTree::AdaptationSet* adp, + const std::map& media_headers, + DefaultRepresentationChooser* chooser, + bool play_timeshift_buffer, + size_t repId, + bool choose_rep) + : adaptive::AdaptiveStream(tree, adp, media_headers, play_timeshift_buffer, repId, choose_rep), + chooser_(chooser) {}; std::chrono::system_clock::time_point mock_time_stream = std::chrono::system_clock::now(); void SetLastUpdated(std::chrono::system_clock::time_point tm) override { lastUpdated_ = tm; }; + std::vector download_list_; + virtual bool download_segment() override; protected: virtual bool download(const char* url, - const std::map& mediaHeaders) override; + const std::map& mediaHeaders, + std::string* lockfreeBuffer) override; + +private: + DefaultRepresentationChooser* chooser_ = nullptr; }; class AESDecrypter : public IAESDecrypter