Skip to content

Commit

Permalink
Fix tests
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
glennguy committed Aug 15, 2021
1 parent 8e51edc commit 8d89ff4
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 85 deletions.
23 changes: 12 additions & 11 deletions src/common/AdaptiveStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::chrono::system_clock> lastUpdated_;
virtual bool download_segment();
std::string download_url_;
std::map<std::string, std::string> media_headers_, download_headers_;
struct SEGMENTBUFFER
{
std::string buffer;
AdaptiveTree::Segment segment;
unsigned int segment_number;
AdaptiveTree::Representation* rep;
};
std::vector<SEGMENTBUFFER> segment_buffers_;


private:
enum STATE
Expand All @@ -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,
Expand Down Expand Up @@ -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<SEGMENTBUFFER> 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
Expand All @@ -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<std::string, std::string> media_headers_, download_headers_;
std::size_t segment_read_pos_;
uint64_t absolute_position_;
uint64_t currentPTSOffset_, absolutePTSOffset_;
Expand Down
1 change: 1 addition & 0 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
143 changes: 77 additions & 66 deletions src/test/TestDASHTree.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "TestHelper.h"

#include <chrono>
#include <thread>
#include <gtest/gtest.h>


Expand Down Expand Up @@ -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
Expand All @@ -52,36 +56,49 @@ 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
// amount of bytes needed to supply the next sample until the segment is
// 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<std::string> downloadedUrls;
std::map<std::string, std::string> mediaHeaders;
unsigned char buf[16];
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);

Expand Down
17 changes: 13 additions & 4 deletions src/test/TestHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

std::string testHelper::testFile;
std::string testHelper::effectiveUrl;
std::string testHelper::lastDownloadUrl;

void Log(const LogLevel loglevel, const char* format, ...){}

Expand Down Expand Up @@ -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<std::string, std::string>& mediaHeaders)
const std::map<std::string, std::string>& 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)
Expand Down
23 changes: 19 additions & 4 deletions src/test/TestHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string, std::string>& 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<std::string> download_list_;
virtual bool download_segment() override;

protected:
virtual bool download(const char* url,
const std::map<std::string, std::string>& mediaHeaders) override;
const std::map<std::string, std::string>& mediaHeaders,
std::string* lockfreeBuffer) override;

private:
DefaultRepresentationChooser* chooser_ = nullptr;
};

class AESDecrypter : public IAESDecrypter
Expand Down

0 comments on commit 8d89ff4

Please sign in to comment.