Support removing segments outside of live window in HLS
Issue: #223 Change-Id: Ib91c60268d8df9adbaf5f6cac77eaebd6a3edb6e
This commit is contained in:
parent
224b597b48
commit
37d4ff017f
|
@ -18,6 +18,7 @@
|
||||||
#include "packager/file/file.h"
|
#include "packager/file/file.h"
|
||||||
#include "packager/hls/base/tag.h"
|
#include "packager/hls/base/tag.h"
|
||||||
#include "packager/media/base/language_utils.h"
|
#include "packager/media/base/language_utils.h"
|
||||||
|
#include "packager/media/base/muxer_util.h"
|
||||||
#include "packager/version/version.h"
|
#include "packager/version/version.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
@ -577,7 +578,6 @@ void MediaPlaylist::SlideWindow() {
|
||||||
HlsEntry::EntryType prev_entry_type = HlsEntry::EntryType::kExtInf;
|
HlsEntry::EntryType prev_entry_type = HlsEntry::EntryType::kExtInf;
|
||||||
|
|
||||||
std::list<std::unique_ptr<HlsEntry>>::iterator last = entries_.begin();
|
std::list<std::unique_ptr<HlsEntry>>::iterator last = entries_.begin();
|
||||||
size_t num_segments_removed = 0;
|
|
||||||
for (; last != entries_.end(); ++last) {
|
for (; last != entries_.end(); ++last) {
|
||||||
HlsEntry::EntryType entry_type = last->get()->type();
|
HlsEntry::EntryType entry_type = last->get()->type();
|
||||||
if (entry_type == HlsEntry::EntryType::kExtKey) {
|
if (entry_type == HlsEntry::EntryType::kExtKey) {
|
||||||
|
@ -588,13 +588,14 @@ void MediaPlaylist::SlideWindow() {
|
||||||
++discontinuity_sequence_number_;
|
++discontinuity_sequence_number_;
|
||||||
} else {
|
} else {
|
||||||
DCHECK_EQ(entry_type, HlsEntry::EntryType::kExtInf);
|
DCHECK_EQ(entry_type, HlsEntry::EntryType::kExtInf);
|
||||||
const SegmentInfoEntry* segment_info =
|
const SegmentInfoEntry& segment_info =
|
||||||
reinterpret_cast<SegmentInfoEntry*>(last->get());
|
*reinterpret_cast<SegmentInfoEntry*>(last->get());
|
||||||
const double last_segment_end_time =
|
const double last_segment_end_time =
|
||||||
segment_info->start_time() + segment_info->duration();
|
segment_info.start_time() + segment_info.duration();
|
||||||
if (timeshift_limit < last_segment_end_time)
|
if (timeshift_limit < last_segment_end_time)
|
||||||
break;
|
break;
|
||||||
++num_segments_removed;
|
RemoveOldSegment(segment_info.start_time());
|
||||||
|
media_sequence_number_++;
|
||||||
}
|
}
|
||||||
prev_entry_type = entry_type;
|
prev_entry_type = entry_type;
|
||||||
}
|
}
|
||||||
|
@ -602,7 +603,23 @@ void MediaPlaylist::SlideWindow() {
|
||||||
// Add key entries back.
|
// Add key entries back.
|
||||||
entries_.insert(entries_.begin(), std::make_move_iterator(ext_x_keys.begin()),
|
entries_.insert(entries_.begin(), std::make_move_iterator(ext_x_keys.begin()),
|
||||||
std::make_move_iterator(ext_x_keys.end()));
|
std::make_move_iterator(ext_x_keys.end()));
|
||||||
media_sequence_number_ += num_segments_removed;
|
}
|
||||||
|
|
||||||
|
void MediaPlaylist::RemoveOldSegment(uint64_t start_time) {
|
||||||
|
if (hls_params_.preserved_segments_outside_live_window == 0)
|
||||||
|
return;
|
||||||
|
if (stream_type_ == MediaPlaylistStreamType::kVideoIFramesOnly)
|
||||||
|
return;
|
||||||
|
|
||||||
|
segments_to_be_removed_.push_back(
|
||||||
|
media::GetSegmentName(media_info_.segment_template(), start_time,
|
||||||
|
media_sequence_number_, media_info_.bandwidth()));
|
||||||
|
while (segments_to_be_removed_.size() >
|
||||||
|
hls_params_.preserved_segments_outside_live_window) {
|
||||||
|
VLOG(2) << "Deleting " << segments_to_be_removed_.front();
|
||||||
|
File::Delete(segments_to_be_removed_.front().c_str());
|
||||||
|
segments_to_be_removed_.pop_front();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace hls
|
} // namespace hls
|
||||||
|
|
|
@ -188,8 +188,12 @@ class MediaPlaylist {
|
||||||
// Remove elements from |entries_| for live profile. Increments
|
// Remove elements from |entries_| for live profile. Increments
|
||||||
// |sequence_number_| by the number of segments removed.
|
// |sequence_number_| by the number of segments removed.
|
||||||
void SlideWindow();
|
void SlideWindow();
|
||||||
|
// Remove the segment specified by |start_time|. The actual deletion can
|
||||||
|
// happen at a later time depending on the value of
|
||||||
|
// |preserved_segment_outside_live_window| in |hls_params_|.
|
||||||
|
void RemoveOldSegment(uint64_t start_time);
|
||||||
|
|
||||||
const HlsParams hls_params_;
|
const HlsParams& hls_params_;
|
||||||
// Mainly for MasterPlaylist to use these values.
|
// Mainly for MasterPlaylist to use these values.
|
||||||
const std::string file_name_;
|
const std::string file_name_;
|
||||||
const std::string name_;
|
const std::string name_;
|
||||||
|
@ -218,6 +222,9 @@ class MediaPlaylist {
|
||||||
uint32_t target_duration_ = 0;
|
uint32_t target_duration_ = 0;
|
||||||
|
|
||||||
std::list<std::unique_ptr<HlsEntry>> entries_;
|
std::list<std::unique_ptr<HlsEntry>> entries_;
|
||||||
|
// A list to hold the file names of the segments to be removed temporarily.
|
||||||
|
// Once a file is actually removed, it is removed from the list.
|
||||||
|
std::list<std::string> segments_to_be_removed_;
|
||||||
|
|
||||||
// Used by kVideoIFrameOnly playlists to track the i-frames (key frames).
|
// Used by kVideoIFrameOnly playlists to track the i-frames (key frames).
|
||||||
struct KeyFrameInfo {
|
struct KeyFrameInfo {
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "packager/base/strings/stringprintf.h"
|
||||||
|
#include "packager/file/file.h"
|
||||||
|
#include "packager/file/file_closer.h"
|
||||||
#include "packager/file/file_test_util.h"
|
#include "packager/file/file_test_util.h"
|
||||||
#include "packager/hls/base/media_playlist.h"
|
#include "packager/hls/base/media_playlist.h"
|
||||||
#include "packager/version/version.h"
|
#include "packager/version/version.h"
|
||||||
|
@ -42,10 +45,9 @@ class MediaPlaylistTest : public ::testing::Test {
|
||||||
: default_file_name_(kDefaultPlaylistFileName),
|
: default_file_name_(kDefaultPlaylistFileName),
|
||||||
default_name_("default_name"),
|
default_name_("default_name"),
|
||||||
default_group_id_("default_group_id") {
|
default_group_id_("default_group_id") {
|
||||||
HlsParams hls_params;
|
hls_params_.playlist_type = type;
|
||||||
hls_params.playlist_type = type;
|
hls_params_.time_shift_buffer_depth = kTimeShiftBufferDepth;
|
||||||
hls_params.time_shift_buffer_depth = kTimeShiftBufferDepth;
|
media_playlist_.reset(new MediaPlaylist(hls_params_, default_file_name_,
|
||||||
media_playlist_.reset(new MediaPlaylist(hls_params, default_file_name_,
|
|
||||||
default_name_, default_group_id_));
|
default_name_, default_group_id_));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,9 +67,12 @@ class MediaPlaylistTest : public ::testing::Test {
|
||||||
valid_video_media_info_.set_reference_time_scale(kTimeScale);
|
valid_video_media_info_.set_reference_time_scale(kTimeScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HlsParams* mutable_hls_params() { return &hls_params_; }
|
||||||
|
|
||||||
const std::string default_file_name_;
|
const std::string default_file_name_;
|
||||||
const std::string default_name_;
|
const std::string default_name_;
|
||||||
const std::string default_group_id_;
|
const std::string default_group_id_;
|
||||||
|
HlsParams hls_params_;
|
||||||
std::unique_ptr<MediaPlaylist> media_playlist_;
|
std::unique_ptr<MediaPlaylist> media_playlist_;
|
||||||
|
|
||||||
MediaInfo valid_video_media_info_;
|
MediaInfo valid_video_media_info_;
|
||||||
|
@ -870,5 +875,84 @@ TEST_F(IFrameMediaPlaylistTest, MultiSegment) {
|
||||||
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const int kNumPreservedSegmentsOutsideLiveWindow = 3;
|
||||||
|
const int kMaxNumSegmentsAvailable =
|
||||||
|
kTimeShiftBufferDepth + 1 + kNumPreservedSegmentsOutsideLiveWindow;
|
||||||
|
|
||||||
|
const char kSegmentTemplate[] = "memory://$Number$.mp4";
|
||||||
|
const char kSegmentTemplateUrl[] = "video/$Number$.mp4";
|
||||||
|
const char kStringPrintTemplate[] = "memory://%d.mp4";
|
||||||
|
const char kIgnoredSegmentName[] = "ignored_segment_name";
|
||||||
|
|
||||||
|
const uint64_t kInitialStartTime = 0;
|
||||||
|
const uint64_t kDuration = kTimeScale;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class MediaPlaylistDeleteSegmentsTest : public LiveMediaPlaylistTest {
|
||||||
|
public:
|
||||||
|
void SetUp() override {
|
||||||
|
LiveMediaPlaylistTest::SetUp();
|
||||||
|
|
||||||
|
// Create 100 files with the template.
|
||||||
|
for (int i = 1; i <= 100; ++i) {
|
||||||
|
File::WriteStringToFile(
|
||||||
|
base::StringPrintf(kStringPrintTemplate, i).c_str(), "dummy content");
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_video_media_info_.set_segment_template(kSegmentTemplate);
|
||||||
|
valid_video_media_info_.set_segment_template_url(kSegmentTemplateUrl);
|
||||||
|
ASSERT_TRUE(media_playlist_->SetMediaInfo(valid_video_media_info_));
|
||||||
|
|
||||||
|
mutable_hls_params()->preserved_segments_outside_live_window =
|
||||||
|
kNumPreservedSegmentsOutsideLiveWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SegmentDeleted(const std::string& segment_name) {
|
||||||
|
std::unique_ptr<File, FileCloser> file_closer(
|
||||||
|
File::Open(segment_name.c_str(), "r"));
|
||||||
|
return file_closer.get() == nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify that no segments are deleted initially until there are more than
|
||||||
|
// |kMaxNumSegmentsAvailable| segments.
|
||||||
|
TEST_F(MediaPlaylistDeleteSegmentsTest, NoSegmentsDeletedInitially) {
|
||||||
|
for (int i = 0; i < kMaxNumSegmentsAvailable; ++i) {
|
||||||
|
media_playlist_->AddSegment(kIgnoredSegmentName,
|
||||||
|
kInitialStartTime + i * kDuration, kDuration,
|
||||||
|
kZeroByteOffset, kMBytes);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < kMaxNumSegmentsAvailable; ++i) {
|
||||||
|
EXPECT_FALSE(
|
||||||
|
SegmentDeleted(base::StringPrintf(kStringPrintTemplate, i + 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MediaPlaylistDeleteSegmentsTest, OneSegmentDeleted) {
|
||||||
|
for (int i = 0; i <= kMaxNumSegmentsAvailable; ++i) {
|
||||||
|
media_playlist_->AddSegment(kIgnoredSegmentName,
|
||||||
|
kInitialStartTime + i * kDuration, kDuration,
|
||||||
|
kZeroByteOffset, kMBytes);
|
||||||
|
}
|
||||||
|
EXPECT_FALSE(SegmentDeleted(base::StringPrintf(kStringPrintTemplate, 2)));
|
||||||
|
EXPECT_TRUE(SegmentDeleted(base::StringPrintf(kStringPrintTemplate, 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MediaPlaylistDeleteSegmentsTest, ManySegments) {
|
||||||
|
int many_segments = 50;
|
||||||
|
for (int i = 0; i < many_segments; ++i) {
|
||||||
|
media_playlist_->AddSegment(kIgnoredSegmentName,
|
||||||
|
kInitialStartTime + i * kDuration, kDuration,
|
||||||
|
kZeroByteOffset, kMBytes);
|
||||||
|
}
|
||||||
|
const int last_available_segment_index =
|
||||||
|
many_segments - kMaxNumSegmentsAvailable + 1;
|
||||||
|
EXPECT_FALSE(SegmentDeleted(
|
||||||
|
base::StringPrintf(kStringPrintTemplate, last_available_segment_index)));
|
||||||
|
EXPECT_TRUE(SegmentDeleted(base::StringPrintf(
|
||||||
|
kStringPrintTemplate, last_available_segment_index - 1)));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace hls
|
} // namespace hls
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -31,6 +31,13 @@ struct HlsParams {
|
||||||
/// Defines the live window, or the guaranteed duration of the time shifting
|
/// Defines the live window, or the guaranteed duration of the time shifting
|
||||||
/// buffer for 'live' playlists.
|
/// buffer for 'live' playlists.
|
||||||
double time_shift_buffer_depth = 0;
|
double time_shift_buffer_depth = 0;
|
||||||
|
/// Segments outside live window (defined by 'time_shift_buffer_depth' above)
|
||||||
|
/// are automatically removed except the latest number of segments defined by
|
||||||
|
/// this parameter. This is needed to accommodate latencies in various stages
|
||||||
|
/// of content serving pipeline, so that the segments stay accessible as they
|
||||||
|
/// may still be accessed by the player.
|
||||||
|
/// The segments are not removed if the value is zero.
|
||||||
|
size_t preserved_segments_outside_live_window = 0;
|
||||||
/// Defines the key uri for "identity" and "com.apple.streamingkeydelivery"
|
/// Defines the key uri for "identity" and "com.apple.streamingkeydelivery"
|
||||||
/// key formats. Ignored if the playlist is not encrypted or not using the
|
/// key formats. Ignored if the playlist is not encrypted or not using the
|
||||||
/// above key formats.
|
/// above key formats.
|
||||||
|
|
|
@ -498,6 +498,7 @@ void Representation::RemoveSegments(uint64_t start_time,
|
||||||
}
|
}
|
||||||
while (segments_to_be_removed_.size() >
|
while (segments_to_be_removed_.size() >
|
||||||
mpd_options_.mpd_params.preserved_segments_outside_live_window) {
|
mpd_options_.mpd_params.preserved_segments_outside_live_window) {
|
||||||
|
VLOG(2) << "Deleting " << segments_to_be_removed_.front();
|
||||||
File::Delete(segments_to_be_removed_.front().c_str());
|
File::Delete(segments_to_be_removed_.front().c_str());
|
||||||
segments_to_be_removed_.pop_front();
|
segments_to_be_removed_.pop_front();
|
||||||
}
|
}
|
||||||
|
|
|
@ -212,7 +212,8 @@ class Representation {
|
||||||
std::list<ContentProtectionElement> content_protection_elements_;
|
std::list<ContentProtectionElement> content_protection_elements_;
|
||||||
// TODO(kqyang): Address sliding window issue with multiple periods.
|
// TODO(kqyang): Address sliding window issue with multiple periods.
|
||||||
std::list<SegmentInfo> segment_infos_;
|
std::list<SegmentInfo> segment_infos_;
|
||||||
// A temporary list to hold the file names of segments to be removed.
|
// A list to hold the file names of the segments to be removed temporarily.
|
||||||
|
// Once a file is actually removed, it is removed from the list.
|
||||||
std::list<std::string> segments_to_be_removed_;
|
std::list<std::string> segments_to_be_removed_;
|
||||||
|
|
||||||
const uint32_t id_;
|
const uint32_t id_;
|
||||||
|
|
|
@ -27,12 +27,6 @@ namespace {
|
||||||
|
|
||||||
const uint32_t kAnyRepresentationId = 1;
|
const uint32_t kAnyRepresentationId = 1;
|
||||||
|
|
||||||
bool SegmentDeleted(const std::string& segment_name) {
|
|
||||||
std::unique_ptr<File, FileCloser> file_closer(
|
|
||||||
File::Open(segment_name.c_str(), "r"));
|
|
||||||
return file_closer.get() == nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MockRepresentationStateChangeListener
|
class MockRepresentationStateChangeListener
|
||||||
: public RepresentationStateChangeListener {
|
: public RepresentationStateChangeListener {
|
||||||
public:
|
public:
|
||||||
|
@ -1185,86 +1179,106 @@ TEST_P(TimeShiftBufferDepthTest, ManySegments) {
|
||||||
XmlNodeEqual(ExpectedXml(expected_s_element, kExpectedStartNumber)));
|
XmlNodeEqual(ExpectedXml(expected_s_element, kExpectedStartNumber)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(TimeShiftBufferDepthTest, DeleteSegmentsOutsideOfLiveWindow) {
|
INSTANTIATE_TEST_CASE_P(InitialStartTime,
|
||||||
const char kSegmentTemplate[] = "memory://$Number$.mp4";
|
TimeShiftBufferDepthTest,
|
||||||
const char kStringPrintTemplate[] = "memory://%d.mp4";
|
Values(0, 1000));
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const int kTimeShiftBufferDepth = 2;
|
||||||
|
const int kNumPreservedSegmentsOutsideLiveWindow = 3;
|
||||||
|
const int kMaxNumSegmentsAvailable =
|
||||||
|
kTimeShiftBufferDepth + 1 + kNumPreservedSegmentsOutsideLiveWindow;
|
||||||
|
|
||||||
|
const char kSegmentTemplate[] = "memory://$Number$.mp4";
|
||||||
|
const char kSegmentTemplateUrl[] = "video/$Number$.mp4";
|
||||||
|
const char kStringPrintTemplate[] = "memory://%d.mp4";
|
||||||
|
|
||||||
|
const uint64_t kInitialStartTime = 0;
|
||||||
|
const uint64_t kDuration = kDefaultTimeScale;
|
||||||
|
const uint64_t kSize = 10;
|
||||||
|
const uint64_t kNoRepeat = 0;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class RepresentationDeleteSegmentsTest : public SegmentTimelineTestBase {
|
||||||
|
public:
|
||||||
|
void SetUp() override {
|
||||||
|
SegmentTimelineTestBase::SetUp();
|
||||||
|
|
||||||
// Create 100 files with the template.
|
// Create 100 files with the template.
|
||||||
for (int i = 1; i <= 100; ++i) {
|
for (int i = 1; i <= 100; ++i) {
|
||||||
File::WriteStringToFile(base::StringPrintf(kStringPrintTemplate, i).c_str(),
|
File::WriteStringToFile(
|
||||||
"dummy content");
|
base::StringPrintf(kStringPrintTemplate, i).c_str(), "dummy content");
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaInfo media_info = ConvertToMediaInfo(GetDefaultMediaInfo());
|
MediaInfo media_info = ConvertToMediaInfo(GetDefaultMediaInfo());
|
||||||
media_info.set_segment_template(kSegmentTemplate);
|
media_info.set_segment_template(kSegmentTemplate);
|
||||||
|
media_info.set_segment_template_url(kSegmentTemplateUrl);
|
||||||
representation_ =
|
representation_ =
|
||||||
CreateRepresentation(media_info, kAnyRepresentationId, NoListener());
|
CreateRepresentation(media_info, kAnyRepresentationId, NoListener());
|
||||||
ASSERT_TRUE(representation_->Init());
|
ASSERT_TRUE(representation_->Init());
|
||||||
|
|
||||||
const int kTimeShiftBufferDepth = 2;
|
mpd_options_.mpd_params.time_shift_buffer_depth = kTimeShiftBufferDepth;
|
||||||
const int kNumPreservedSegmentsOutsideLiveWindow = 3;
|
mpd_options_.mpd_params.preserved_segments_outside_live_window =
|
||||||
const int kMaxNumSegmentsAvailable =
|
|
||||||
kTimeShiftBufferDepth + 1 + kNumPreservedSegmentsOutsideLiveWindow;
|
|
||||||
|
|
||||||
mutable_mpd_options()->mpd_params.time_shift_buffer_depth =
|
|
||||||
kTimeShiftBufferDepth;
|
|
||||||
mutable_mpd_options()->mpd_params.preserved_segments_outside_live_window =
|
|
||||||
kNumPreservedSegmentsOutsideLiveWindow;
|
kNumPreservedSegmentsOutsideLiveWindow;
|
||||||
|
|
||||||
const uint64_t kInitialStartTime = 0;
|
|
||||||
const uint64_t kDuration = kDefaultTimeScale;
|
|
||||||
const uint64_t kSize = 10;
|
|
||||||
const uint64_t kNoRepeat = 0;
|
|
||||||
|
|
||||||
// Verify that no segments are deleted initially until there are more than
|
|
||||||
// |kMaxNumSegmentsAvailable| segments.
|
|
||||||
uint64_t next_start_time = kInitialStartTime;
|
|
||||||
int num_total_segments = 0;
|
|
||||||
int last_available_segment_index = 1; // No segments are deleted.
|
|
||||||
for (int i = 0; i < kMaxNumSegmentsAvailable; ++i) {
|
|
||||||
AddSegments(next_start_time, kDuration, kSize, kNoRepeat);
|
|
||||||
next_start_time += kDuration;
|
|
||||||
++num_total_segments;
|
|
||||||
|
|
||||||
EXPECT_FALSE(SegmentDeleted(base::StringPrintf(
|
|
||||||
kStringPrintTemplate, last_available_segment_index)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AddSegments(next_start_time, kDuration, kSize, kNoRepeat);
|
bool SegmentDeleted(const std::string& segment_name) {
|
||||||
next_start_time += kDuration;
|
std::unique_ptr<File, FileCloser> file_closer(
|
||||||
++num_total_segments;
|
File::Open(segment_name.c_str(), "r"));
|
||||||
last_available_segment_index = 2;
|
return file_closer.get() == nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
EXPECT_FALSE(SegmentDeleted(
|
// Verify that no segments are deleted initially until there are more than
|
||||||
base::StringPrintf(kStringPrintTemplate, last_available_segment_index)));
|
// |kMaxNumSegmentsAvailable| segments.
|
||||||
EXPECT_TRUE(SegmentDeleted(base::StringPrintf(
|
TEST_F(RepresentationDeleteSegmentsTest, NoSegmentsDeletedInitially) {
|
||||||
kStringPrintTemplate, last_available_segment_index - 1)));
|
for (int i = 0; i < kMaxNumSegmentsAvailable; ++i) {
|
||||||
|
AddSegments(kInitialStartTime + i * kDuration, kDuration, kSize, kNoRepeat);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < kMaxNumSegmentsAvailable; ++i) {
|
||||||
|
EXPECT_FALSE(
|
||||||
|
SegmentDeleted(base::StringPrintf(kStringPrintTemplate, i + 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Verify that there are exactly |kMaxNumSegmentsAvailable| segments
|
TEST_F(RepresentationDeleteSegmentsTest, OneSegmentDeleted) {
|
||||||
// remaining.
|
for (int i = 0; i <= kMaxNumSegmentsAvailable; ++i) {
|
||||||
const uint64_t kRepeat = 10;
|
AddSegments(kInitialStartTime + i * kDuration, kDuration, kSize, kNoRepeat);
|
||||||
AddSegments(next_start_time, kDuration, kSize, kRepeat);
|
}
|
||||||
next_start_time += (kRepeat + 1) * kDuration;
|
EXPECT_FALSE(SegmentDeleted(base::StringPrintf(kStringPrintTemplate, 2)));
|
||||||
num_total_segments += kRepeat + 1;
|
EXPECT_TRUE(SegmentDeleted(base::StringPrintf(kStringPrintTemplate, 1)));
|
||||||
last_available_segment_index =
|
}
|
||||||
num_total_segments - kMaxNumSegmentsAvailable + 1;
|
|
||||||
|
|
||||||
EXPECT_FALSE(SegmentDeleted(
|
|
||||||
base::StringPrintf(kStringPrintTemplate, last_available_segment_index)));
|
|
||||||
EXPECT_TRUE(SegmentDeleted(base::StringPrintf(
|
|
||||||
kStringPrintTemplate, last_available_segment_index - 1)));
|
|
||||||
|
|
||||||
AddSegments(next_start_time, kDuration, kSize, kNoRepeat);
|
|
||||||
++last_available_segment_index;
|
|
||||||
|
|
||||||
|
// Verify that segments are deleted as expected with many non-repeating
|
||||||
|
// segments.
|
||||||
|
TEST_F(RepresentationDeleteSegmentsTest, ManyNonRepeatingSegments) {
|
||||||
|
int many_segments = 50;
|
||||||
|
for (int i = 0; i < many_segments; ++i) {
|
||||||
|
AddSegments(kInitialStartTime + i * kDuration, kDuration, kSize, kNoRepeat);
|
||||||
|
}
|
||||||
|
const int last_available_segment_index =
|
||||||
|
many_segments - kMaxNumSegmentsAvailable + 1;
|
||||||
EXPECT_FALSE(SegmentDeleted(
|
EXPECT_FALSE(SegmentDeleted(
|
||||||
base::StringPrintf(kStringPrintTemplate, last_available_segment_index)));
|
base::StringPrintf(kStringPrintTemplate, last_available_segment_index)));
|
||||||
EXPECT_TRUE(SegmentDeleted(base::StringPrintf(
|
EXPECT_TRUE(SegmentDeleted(base::StringPrintf(
|
||||||
kStringPrintTemplate, last_available_segment_index - 1)));
|
kStringPrintTemplate, last_available_segment_index - 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(InitialStartTime,
|
// Verify that segments are deleted as expected with many repeating segments.
|
||||||
TimeShiftBufferDepthTest,
|
TEST_F(RepresentationDeleteSegmentsTest, ManyRepeatingSegments) {
|
||||||
Values(0, 1000));
|
const int kLoops = 4;
|
||||||
|
const uint64_t kRepeat = 10;
|
||||||
|
for (int i = 0; i < kLoops; ++i) {
|
||||||
|
AddSegments(kInitialStartTime + i * kDuration * (kRepeat + 1), kDuration,
|
||||||
|
kSize, kRepeat);
|
||||||
|
}
|
||||||
|
const int kNumSegments = kLoops * (kRepeat + 1);
|
||||||
|
const int last_available_segment_index =
|
||||||
|
kNumSegments - kMaxNumSegmentsAvailable + 1;
|
||||||
|
EXPECT_FALSE(SegmentDeleted(
|
||||||
|
base::StringPrintf(kStringPrintTemplate, last_available_segment_index)));
|
||||||
|
EXPECT_TRUE(SegmentDeleted(base::StringPrintf(
|
||||||
|
kStringPrintTemplate, last_available_segment_index - 1)));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
Loading…
Reference in New Issue