Use segment duration in buffer depth calculation
Segment start,end time was used previously, which could result in problems if there are discontinuity in the streams. E.g. if the stream has timestamp, 10000, 10001, 10002 and then next segment comes in with timestamp 1. With the previous logic, all the segments would remain in the time shift buffer until after 10000 segments even with a small time shift buffer depth of 10. This could also happen when timestamp wraps around, which could happen during long time of live streaming. This change will also be useful to support multi-period live DASH. Fixes #563. Change-Id: Ie078d76c6e4af13ade9ad46191c8e3529069ed4d
This commit is contained in:
parent
fa2c4409a6
commit
b85e5c9368
|
@ -305,19 +305,6 @@ std::string PlacementOpportunityEntry::ToString() {
|
|||
return "#EXT-X-PLACEMENT-OPPORTUNITY";
|
||||
}
|
||||
|
||||
double LatestSegmentStartTime(
|
||||
const std::list<std::unique_ptr<HlsEntry>>& entries) {
|
||||
DCHECK(!entries.empty());
|
||||
for (auto iter = entries.rbegin(); iter != entries.rend(); ++iter) {
|
||||
if (iter->get()->type() == HlsEntry::EntryType::kExtInf) {
|
||||
const SegmentInfoEntry* segment_info =
|
||||
reinterpret_cast<SegmentInfoEntry*>(iter->get());
|
||||
return segment_info->start_time();
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
HlsEntry::HlsEntry(HlsEntry::EntryType type) : type_(type) {}
|
||||
|
@ -530,6 +517,13 @@ void MediaPlaylist::AddSegmentInfoEntry(const std::string& segment_file_name,
|
|||
return;
|
||||
}
|
||||
|
||||
// In order for the oldest segment to be accessible for at least
|
||||
// |time_shift_buffer_depth| seconds, the latest segment should not be in the
|
||||
// sliding window since the player could be playing any part of the latest
|
||||
// segment. So the current segment duration is added to the sum of segment
|
||||
// durations (in the manifest/playlist) after sliding the window.
|
||||
SlideWindow();
|
||||
|
||||
const double start_time_seconds =
|
||||
static_cast<double>(start_time) / time_scale_;
|
||||
const double segment_duration_seconds =
|
||||
|
@ -537,12 +531,25 @@ void MediaPlaylist::AddSegmentInfoEntry(const std::string& segment_file_name,
|
|||
longest_segment_duration_ =
|
||||
std::max(longest_segment_duration_, segment_duration_seconds);
|
||||
bandwidth_estimator_.AddBlock(size, segment_duration_seconds);
|
||||
current_buffer_depth_ += segment_duration_seconds;
|
||||
|
||||
if (!entries_.empty() &&
|
||||
entries_.back()->type() == HlsEntry::EntryType::kExtInf) {
|
||||
const SegmentInfoEntry* segment_info =
|
||||
static_cast<SegmentInfoEntry*>(entries_.back().get());
|
||||
if (segment_info->start_time() > start_time_seconds) {
|
||||
LOG(WARNING)
|
||||
<< "Insert a discontinuity tag after the segment with start time "
|
||||
<< segment_info->start_time() << " as the next segment starts at "
|
||||
<< start_time_seconds << ".";
|
||||
entries_.emplace_back(new DiscontinuityEntry());
|
||||
}
|
||||
}
|
||||
|
||||
entries_.emplace_back(new SegmentInfoEntry(
|
||||
segment_file_name, start_time_seconds, segment_duration_seconds,
|
||||
use_byte_range_, start_byte_offset, size, previous_segment_end_offset_));
|
||||
previous_segment_end_offset_ = start_byte_offset + size - 1;
|
||||
SlideWindow();
|
||||
}
|
||||
|
||||
void MediaPlaylist::AdjustLastSegmentInfoEntryDuration(int64_t next_timestamp) {
|
||||
|
@ -559,7 +566,9 @@ void MediaPlaylist::AdjustLastSegmentInfoEntryDuration(int64_t next_timestamp) {
|
|||
|
||||
const double segment_duration_seconds =
|
||||
next_timestamp_seconds - segment_info->start_time();
|
||||
segment_info->set_duration(segment_duration_seconds);
|
||||
// It could be negative if timestamp messed up.
|
||||
if (segment_duration_seconds > 0)
|
||||
segment_info->set_duration(segment_duration_seconds);
|
||||
longest_segment_duration_ =
|
||||
std::max(longest_segment_duration_, segment_duration_seconds);
|
||||
break;
|
||||
|
@ -568,22 +577,15 @@ void MediaPlaylist::AdjustLastSegmentInfoEntryDuration(int64_t next_timestamp) {
|
|||
}
|
||||
|
||||
void MediaPlaylist::SlideWindow() {
|
||||
DCHECK(!entries_.empty());
|
||||
if (hls_params_.time_shift_buffer_depth <= 0.0 ||
|
||||
hls_params_.playlist_type != HlsPlaylistType::kLive) {
|
||||
return;
|
||||
}
|
||||
DCHECK_GT(time_scale_, 0u);
|
||||
|
||||
// The start time of the latest segment is considered the current_play_time,
|
||||
// and this should guarantee that the latest segment will stay in the list.
|
||||
const double current_play_time = LatestSegmentStartTime(entries_);
|
||||
if (current_play_time <= hls_params_.time_shift_buffer_depth)
|
||||
if (current_buffer_depth_ <= hls_params_.time_shift_buffer_depth)
|
||||
return;
|
||||
|
||||
const double timeshift_limit =
|
||||
current_play_time - hls_params_.time_shift_buffer_depth;
|
||||
|
||||
// Temporary list to hold the EXT-X-KEYs. For example, this allows us to
|
||||
// remove <3> without removing <1> and <2> below (<1> and <2> are moved to the
|
||||
// temporary list and added back later).
|
||||
|
@ -607,12 +609,17 @@ void MediaPlaylist::SlideWindow() {
|
|||
++discontinuity_sequence_number_;
|
||||
} else {
|
||||
DCHECK_EQ(entry_type, HlsEntry::EntryType::kExtInf);
|
||||
|
||||
const SegmentInfoEntry& segment_info =
|
||||
*reinterpret_cast<SegmentInfoEntry*>(last->get());
|
||||
const double last_segment_end_time =
|
||||
segment_info.start_time() + segment_info.duration();
|
||||
if (timeshift_limit < last_segment_end_time)
|
||||
// Remove the current segment only if it falls completely out of time
|
||||
// shift buffer range.
|
||||
const bool segment_within_time_shift_buffer =
|
||||
current_buffer_depth_ - segment_info.duration() <
|
||||
hls_params_.time_shift_buffer_depth;
|
||||
if (segment_within_time_shift_buffer)
|
||||
break;
|
||||
current_buffer_depth_ -= segment_info.duration();
|
||||
RemoveOldSegment(segment_info.start_time());
|
||||
media_sequence_number_++;
|
||||
}
|
||||
|
|
|
@ -240,7 +240,10 @@ class MediaPlaylist {
|
|||
bool target_duration_set_ = false;
|
||||
uint32_t target_duration_ = 0;
|
||||
|
||||
// TODO(kqyang): This could be managed better by a separate class, than having
|
||||
// all them managed in MediaPlaylist.
|
||||
std::list<std::unique_ptr<HlsEntry>> entries_;
|
||||
double current_buffer_depth_ = 0;
|
||||
// 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_;
|
||||
|
|
|
@ -79,33 +79,6 @@ uint32_t GetTimeScale(const MediaInfo& media_info) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
int64_t LastSegmentStartTime(const SegmentInfo& segment_info) {
|
||||
return segment_info.start_time + segment_info.duration * segment_info.repeat;
|
||||
}
|
||||
|
||||
// This is equal to |segment_info| end time
|
||||
int64_t LastSegmentEndTime(const SegmentInfo& segment_info) {
|
||||
return segment_info.start_time +
|
||||
segment_info.duration * (segment_info.repeat + 1);
|
||||
}
|
||||
|
||||
int64_t LatestSegmentStartTime(const std::list<SegmentInfo>& segments) {
|
||||
DCHECK(!segments.empty());
|
||||
const SegmentInfo& latest_segment = segments.back();
|
||||
return LastSegmentStartTime(latest_segment);
|
||||
}
|
||||
|
||||
// Given |timeshift_limit|, finds out the number of segments that are no longer
|
||||
// valid and should be removed from |segment_info|.
|
||||
uint64_t SearchTimedOutRepeatIndex(int64_t timeshift_limit,
|
||||
const SegmentInfo& segment_info) {
|
||||
DCHECK_LE(timeshift_limit, LastSegmentEndTime(segment_info));
|
||||
if (timeshift_limit < segment_info.start_time)
|
||||
return 0;
|
||||
|
||||
return (timeshift_limit - segment_info.start_time) / segment_info.duration;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Representation::Representation(
|
||||
|
@ -203,16 +176,21 @@ void Representation::AddNewSegment(int64_t start_time,
|
|||
return;
|
||||
}
|
||||
|
||||
// In order for the oldest segment to be accessible for at least
|
||||
// |time_shift_buffer_depth| seconds, the latest segment should not be in the
|
||||
// sliding window since the player could be playing any part of the latest
|
||||
// segment. So the current segment duration is added to the sum of segment
|
||||
// durations (in the manifest/playlist) after sliding the window.
|
||||
SlideWindow();
|
||||
|
||||
if (state_change_listener_)
|
||||
state_change_listener_->OnNewSegmentForRepresentation(start_time, duration);
|
||||
|
||||
AddSegmentInfo(start_time, duration);
|
||||
current_buffer_depth_ += segment_infos_.back().duration;
|
||||
|
||||
bandwidth_estimator_.AddBlock(
|
||||
size, static_cast<double>(duration) / media_info_.reference_time_scale());
|
||||
|
||||
SlideWindow();
|
||||
DCHECK_GE(segment_infos_.size(), 1u);
|
||||
}
|
||||
|
||||
void Representation::SetSampleDuration(uint32_t frame_duration) {
|
||||
|
@ -435,7 +413,6 @@ int64_t Representation::AdjustDuration(int64_t duration) const {
|
|||
}
|
||||
|
||||
void Representation::SlideWindow() {
|
||||
DCHECK(!segment_infos_.empty());
|
||||
if (mpd_options_.mpd_params.time_shift_buffer_depth <= 0.0 ||
|
||||
mpd_options_.mpd_type == MpdType::kStatic)
|
||||
return;
|
||||
|
@ -443,60 +420,40 @@ void Representation::SlideWindow() {
|
|||
const uint32_t time_scale = GetTimeScale(media_info_);
|
||||
DCHECK_GT(time_scale, 0u);
|
||||
|
||||
int64_t time_shift_buffer_depth = static_cast<int64_t>(
|
||||
const int64_t time_shift_buffer_depth = static_cast<int64_t>(
|
||||
mpd_options_.mpd_params.time_shift_buffer_depth * time_scale);
|
||||
|
||||
// The start time of the latest segment is considered the current_play_time,
|
||||
// and this should guarantee that the latest segment will stay in the list.
|
||||
const int64_t current_play_time = LatestSegmentStartTime(segment_infos_);
|
||||
if (current_play_time <= time_shift_buffer_depth)
|
||||
if (current_buffer_depth_ <= time_shift_buffer_depth)
|
||||
return;
|
||||
|
||||
const int64_t timeshift_limit = current_play_time - time_shift_buffer_depth;
|
||||
|
||||
// First remove all the SegmentInfos that are completely out of range, by
|
||||
// looking at the very last segment's end time.
|
||||
std::list<SegmentInfo>::iterator first = segment_infos_.begin();
|
||||
std::list<SegmentInfo>::iterator last = first;
|
||||
for (; last != segment_infos_.end(); ++last) {
|
||||
const int64_t last_segment_end_time = LastSegmentEndTime(*last);
|
||||
if (timeshift_limit < last_segment_end_time)
|
||||
// Remove the current segment only if it falls completely out of time shift
|
||||
// buffer range.
|
||||
while (last->repeat >= 0 &&
|
||||
current_buffer_depth_ - last->duration >= time_shift_buffer_depth) {
|
||||
current_buffer_depth_ -= last->duration;
|
||||
RemoveOldSegment(&*last);
|
||||
start_number_++;
|
||||
}
|
||||
if (last->repeat >= 0)
|
||||
break;
|
||||
RemoveSegments(last->start_time, last->duration, last->repeat + 1);
|
||||
start_number_ += last->repeat + 1;
|
||||
}
|
||||
segment_infos_.erase(first, last);
|
||||
|
||||
// Now some segment in the first SegmentInfo should be left in the list.
|
||||
SegmentInfo* first_segment_info = &segment_infos_.front();
|
||||
DCHECK_LE(timeshift_limit, LastSegmentEndTime(*first_segment_info));
|
||||
|
||||
// Identify which segments should still be in the SegmentInfo.
|
||||
const uint64_t repeat_index =
|
||||
SearchTimedOutRepeatIndex(timeshift_limit, *first_segment_info);
|
||||
if (repeat_index == 0)
|
||||
return;
|
||||
|
||||
RemoveSegments(first_segment_info->start_time, first_segment_info->duration,
|
||||
repeat_index);
|
||||
|
||||
first_segment_info->start_time = first_segment_info->start_time +
|
||||
first_segment_info->duration * repeat_index;
|
||||
first_segment_info->repeat = first_segment_info->repeat - repeat_index;
|
||||
start_number_ += repeat_index;
|
||||
}
|
||||
|
||||
void Representation::RemoveSegments(int64_t start_time,
|
||||
int64_t duration,
|
||||
uint64_t num_segments) {
|
||||
void Representation::RemoveOldSegment(SegmentInfo* segment_info) {
|
||||
int64_t segment_start_time = segment_info->start_time;
|
||||
segment_info->start_time += segment_info->duration;
|
||||
segment_info->repeat--;
|
||||
|
||||
if (mpd_options_.mpd_params.preserved_segments_outside_live_window == 0)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < num_segments; ++i) {
|
||||
segments_to_be_removed_.push_back(media::GetSegmentName(
|
||||
media_info_.segment_template(), start_time + i * duration,
|
||||
start_number_ - 1 + i, media_info_.bandwidth()));
|
||||
}
|
||||
segments_to_be_removed_.push_back(
|
||||
media::GetSegmentName(media_info_.segment_template(), segment_start_time,
|
||||
start_number_ - 1, media_info_.bandwidth()));
|
||||
while (segments_to_be_removed_.size() >
|
||||
mpd_options_.mpd_params.preserved_segments_outside_live_window) {
|
||||
VLOG(2) << "Deleting " << segments_to_be_removed_.front();
|
||||
|
|
|
@ -197,10 +197,8 @@ class Representation {
|
|||
// |start_number_| by the number of segments removed.
|
||||
void SlideWindow();
|
||||
|
||||
// Remove |num_segments| starting from |start_time| with |duration|.
|
||||
void RemoveSegments(int64_t start_time,
|
||||
int64_t duration,
|
||||
uint64_t num_segments);
|
||||
// Remove the first segment in |segment_info|.
|
||||
void RemoveOldSegment(SegmentInfo* segment_info);
|
||||
|
||||
// Note: Because 'mimeType' is a required field for a valid MPD, these return
|
||||
// strings.
|
||||
|
@ -212,6 +210,8 @@ class Representation {
|
|||
// any logic using this can assume only one set.
|
||||
MediaInfo media_info_;
|
||||
std::list<ContentProtectionElement> content_protection_elements_;
|
||||
|
||||
int64_t current_buffer_depth_ = 0;
|
||||
// TODO(kqyang): Address sliding window issue with multiple periods.
|
||||
std::list<SegmentInfo> segment_infos_;
|
||||
// A list to hold the file names of the segments to be removed temporarily.
|
||||
|
|
|
@ -410,7 +410,7 @@ namespace {
|
|||
// Any number for {AdaptationSet,Representation} ID. Required to create
|
||||
// either objects. Not checked in test.
|
||||
const char kSElementTemplate[] =
|
||||
"<S t=\"%" PRIu64 "\" d=\"%" PRIu64 "\" r=\"%" PRIu64 "\"/>\n";
|
||||
"<S t=\"%" PRIu64 "\" d=\"%" PRIu64 "\" r=\"%d\"/>\n";
|
||||
const char kSElementTemplateWithoutR[] =
|
||||
"<S t=\"%" PRIu64 "\" d=\"%" PRIu64 "\"/>\n";
|
||||
const int kDefaultStartNumber = 1;
|
||||
|
@ -457,7 +457,7 @@ class SegmentTemplateTest : public RepresentationTest {
|
|||
void AddSegments(int64_t start_time,
|
||||
int64_t duration,
|
||||
uint64_t size,
|
||||
uint64_t repeat) {
|
||||
int repeat) {
|
||||
DCHECK(representation_);
|
||||
|
||||
SegmentInfo s = {start_time, duration, repeat};
|
||||
|
@ -470,7 +470,7 @@ class SegmentTemplateTest : public RepresentationTest {
|
|||
base::StringPrintf(kSElementTemplate, start_time, duration, repeat);
|
||||
}
|
||||
|
||||
for (uint64_t i = 0; i < repeat + 1; ++i) {
|
||||
for (int i = 0; i < repeat + 1; ++i) {
|
||||
representation_->AddNewSegment(start_time, duration, size);
|
||||
start_time += duration;
|
||||
bandwidth_estimator_.AddBlock(
|
||||
|
@ -587,7 +587,7 @@ TEST_F(SegmentTemplateTest, NormalRepeatedSegmentDuration) {
|
|||
const uint64_t kSize = 256;
|
||||
int64_t start_time = 0;
|
||||
int64_t duration = 40000;
|
||||
uint64_t repeat = 2;
|
||||
int repeat = 2;
|
||||
AddSegments(start_time, duration, kSize, repeat);
|
||||
|
||||
start_time += duration * (repeat + 1);
|
||||
|
@ -607,7 +607,7 @@ TEST_F(SegmentTemplateTest, RepeatedSegmentsFromNonZeroStartTime) {
|
|||
const uint64_t kSize = 100000;
|
||||
int64_t start_time = 0;
|
||||
int64_t duration = 100000;
|
||||
uint64_t repeat = 2;
|
||||
int repeat = 2;
|
||||
AddSegments(start_time, duration, kSize, repeat);
|
||||
|
||||
start_time += duration * (repeat + 1);
|
||||
|
@ -628,8 +628,8 @@ TEST_F(SegmentTemplateTest, RepeatedSegmentsFromNonZeroStartTime) {
|
|||
TEST_F(SegmentTemplateTest, NonZeroStartTime) {
|
||||
const int64_t kStartTime = 10;
|
||||
const int64_t kDuration = 22000;
|
||||
const uint64_t kSize = 123456;
|
||||
const uint64_t kRepeat = 1;
|
||||
const int kSize = 123456;
|
||||
const int kRepeat = 1;
|
||||
AddSegments(kStartTime, kDuration, kSize, kRepeat);
|
||||
|
||||
EXPECT_THAT(representation_->GetXml().get(), XmlNodeEqual(ExpectedXml()));
|
||||
|
@ -639,8 +639,8 @@ TEST_F(SegmentTemplateTest, NonZeroStartTime) {
|
|||
TEST_F(SegmentTemplateTest, NonContiguousLiveInfo) {
|
||||
const int64_t kStartTime = 10;
|
||||
const int64_t kDuration = 22000;
|
||||
const uint64_t kSize = 123456;
|
||||
const uint64_t kRepeat = 0;
|
||||
const int kSize = 123456;
|
||||
const int kRepeat = 0;
|
||||
AddSegments(kStartTime, kDuration, kSize, kRepeat);
|
||||
|
||||
const int64_t kStartTimeOffset = 100;
|
||||
|
@ -655,8 +655,8 @@ TEST_F(SegmentTemplateTest, OutOfOrder) {
|
|||
const int64_t kEarlierStartTime = 0;
|
||||
const int64_t kLaterStartTime = 1000;
|
||||
const int64_t kDuration = 1000;
|
||||
const uint64_t kSize = 123456;
|
||||
const uint64_t kRepeat = 0;
|
||||
const int kSize = 123456;
|
||||
const int kRepeat = 0;
|
||||
|
||||
AddSegments(kLaterStartTime, kDuration, kSize, kRepeat);
|
||||
AddSegments(kEarlierStartTime, kDuration, kSize, kRepeat);
|
||||
|
@ -668,8 +668,8 @@ TEST_F(SegmentTemplateTest, OutOfOrder) {
|
|||
TEST_F(SegmentTemplateTest, OverlappingSegments) {
|
||||
const int64_t kEarlierStartTime = 0;
|
||||
const int64_t kDuration = 1000;
|
||||
const uint64_t kSize = 123456;
|
||||
const uint64_t kRepeat = 0;
|
||||
const int kSize = 123456;
|
||||
const int kRepeat = 0;
|
||||
|
||||
const int64_t kOverlappingSegmentStartTime = kDuration / 2;
|
||||
CHECK_GT(kDuration, kOverlappingSegmentStartTime);
|
||||
|
@ -686,8 +686,8 @@ TEST_F(SegmentTemplateTest, OverlappingSegments) {
|
|||
TEST_F(SegmentTemplateTest, OverlappingSegmentsWithinErrorRange) {
|
||||
const int64_t kEarlierStartTime = 0;
|
||||
const int64_t kDuration = 1000;
|
||||
const uint64_t kSize = 123456;
|
||||
const uint64_t kRepeat = 0;
|
||||
const int kSize = 123456;
|
||||
const int kRepeat = 0;
|
||||
|
||||
const int64_t kOverlappingSegmentStartTime = kDuration - 1;
|
||||
CHECK_GT(kDuration, kOverlappingSegmentStartTime);
|
||||
|
@ -824,7 +824,7 @@ TEST_P(ApproximateSegmentTimelineTest, SegmentsWithSimilarDurations) {
|
|||
|
||||
std::string expected_s_elements;
|
||||
if (allow_approximate_segment_timeline_) {
|
||||
uint64_t kNumSegments = 3;
|
||||
int kNumSegments = 3;
|
||||
expected_s_elements =
|
||||
base::StringPrintf(kSElementTemplate, kStartTime,
|
||||
kScaledTargetSegmentDuration, kNumSegments - 1);
|
||||
|
@ -860,7 +860,7 @@ TEST_P(ApproximateSegmentTimelineTest, SegmentsWithSimilarDurations2) {
|
|||
"<S t=\"0\" d=\"10\" r=\"1\"/>"
|
||||
"<S t=\"20\" d=\"13\"/>";
|
||||
} else {
|
||||
uint64_t kNumSegments = 3;
|
||||
int kNumSegments = 3;
|
||||
expected_s_elements = base::StringPrintf(kSElementTemplate, kStartTime,
|
||||
kDurationLarger, kNumSegments - 1);
|
||||
}
|
||||
|
@ -879,14 +879,14 @@ TEST_P(ApproximateSegmentTimelineTest, FillSmallGap) {
|
|||
|
||||
std::string expected_s_elements;
|
||||
if (allow_approximate_segment_timeline_) {
|
||||
uint64_t kNumSegments = 3;
|
||||
int kNumSegments = 3;
|
||||
expected_s_elements = base::StringPrintf(kSElementTemplate, kStartTime,
|
||||
kDuration, kNumSegments - 1);
|
||||
} else {
|
||||
expected_s_elements =
|
||||
base::StringPrintf(kSElementTemplateWithoutR, kStartTime, kDuration) +
|
||||
base::StringPrintf(kSElementTemplate, kStartTime + kDuration + kGap,
|
||||
kDuration, static_cast<uint64_t>(1) /* repeat */);
|
||||
kDuration, 1 /* repeat */);
|
||||
}
|
||||
EXPECT_THAT(representation_->GetXml().get(),
|
||||
XmlNodeEqual(ExpectedXml(expected_s_elements)));
|
||||
|
@ -903,14 +903,14 @@ TEST_P(ApproximateSegmentTimelineTest, FillSmallOverlap) {
|
|||
|
||||
std::string expected_s_elements;
|
||||
if (allow_approximate_segment_timeline_) {
|
||||
uint64_t kNumSegments = 3;
|
||||
int kNumSegments = 3;
|
||||
expected_s_elements = base::StringPrintf(kSElementTemplate, kStartTime,
|
||||
kDuration, kNumSegments - 1);
|
||||
} else {
|
||||
expected_s_elements =
|
||||
base::StringPrintf(kSElementTemplateWithoutR, kStartTime, kDuration) +
|
||||
base::StringPrintf(kSElementTemplate, kStartTime + kDuration - kOverlap,
|
||||
kDuration, static_cast<uint64_t>(1) /* repeat */);
|
||||
kDuration, 1 /* repeat */);
|
||||
}
|
||||
EXPECT_THAT(representation_->GetXml().get(),
|
||||
XmlNodeEqual(ExpectedXml(expected_s_elements)));
|
||||
|
@ -979,8 +979,8 @@ TEST_P(TimeShiftBufferDepthTest, Normal) {
|
|||
// Trick to make every segment 1 second long.
|
||||
const int64_t kDuration = kDefaultTimeScale;
|
||||
const uint64_t kSize = 10000;
|
||||
const uint64_t kRepeat = 1234;
|
||||
const uint64_t kLength = kRepeat;
|
||||
const int kRepeat = 1234;
|
||||
const int kLength = kRepeat;
|
||||
|
||||
CHECK_EQ(kDuration / kDefaultTimeScale * kRepeat, kLength);
|
||||
|
||||
|
@ -997,7 +997,7 @@ TEST_P(TimeShiftBufferDepthTest, Normal) {
|
|||
const std::string expected_s_element = base::StringPrintf(
|
||||
kSElementTemplate,
|
||||
initial_start_time_ + kDuration * (kRepeat - kExpectedRepeatsLeft),
|
||||
kDuration, static_cast<uint64_t>(kExpectedRepeatsLeft));
|
||||
kDuration, kExpectedRepeatsLeft);
|
||||
EXPECT_THAT(
|
||||
representation_->GetXml().get(),
|
||||
XmlNodeEqual(ExpectedXml(expected_s_element, kExpectedStartNumber)));
|
||||
|
@ -1015,8 +1015,8 @@ TEST_P(TimeShiftBufferDepthTest, TimeShiftBufferDepthShorterThanSegmentLength) {
|
|||
|
||||
// Each duration is a second longer than timeShiftBufferDepth.
|
||||
const int64_t kDuration = kDefaultTimeScale * (kTimeShiftBufferDepth + 1);
|
||||
const uint64_t kSize = 10000;
|
||||
const uint64_t kRepeat = 1;
|
||||
const int kSize = 10000;
|
||||
const int kRepeat = 1;
|
||||
|
||||
AddSegments(initial_start_time_, kDuration, kSize, kRepeat);
|
||||
|
||||
|
@ -1034,8 +1034,8 @@ TEST_P(TimeShiftBufferDepthTest, Generic) {
|
|||
kTimeShiftBufferDepth;
|
||||
|
||||
const int64_t kDuration = kDefaultTimeScale;
|
||||
const uint64_t kSize = 10000;
|
||||
const uint64_t kRepeat = 1000;
|
||||
const int kSize = 10000;
|
||||
const int kRepeat = 1000;
|
||||
|
||||
AddSegments(initial_start_time_, kDuration, kSize, kRepeat);
|
||||
const int64_t first_s_element_end_time =
|
||||
|
@ -1052,8 +1052,7 @@ TEST_P(TimeShiftBufferDepthTest, Generic) {
|
|||
// Expect only the latest S element with 2 segments.
|
||||
const std::string expected_s_element =
|
||||
base::StringPrintf(kSElementTemplate, first_s_element_end_time,
|
||||
kTimeShiftBufferDepthDuration,
|
||||
static_cast<uint64_t>(kMoreSegmentsRepeat));
|
||||
kTimeShiftBufferDepthDuration, kMoreSegmentsRepeat);
|
||||
|
||||
const int kExpectedRemovedSegments = kRepeat + 1;
|
||||
EXPECT_THAT(
|
||||
|
@ -1072,21 +1071,21 @@ TEST_P(TimeShiftBufferDepthTest, MoreThanOneS) {
|
|||
mutable_mpd_options()->mpd_params.time_shift_buffer_depth =
|
||||
kTimeShiftBufferDepth;
|
||||
|
||||
const uint64_t kSize = 20000;
|
||||
const int kSize = 20000;
|
||||
|
||||
const int64_t kOneSecondDuration = kDefaultTimeScale;
|
||||
const uint64_t kOneSecondSegmentRepeat = 99;
|
||||
const int kOneSecondSegmentRepeat = 99;
|
||||
AddSegments(initial_start_time_, kOneSecondDuration, kSize,
|
||||
kOneSecondSegmentRepeat);
|
||||
const int64_t first_s_element_end_time =
|
||||
initial_start_time_ + kOneSecondDuration * (kOneSecondSegmentRepeat + 1);
|
||||
|
||||
const int64_t kTwoSecondDuration = 2 * kDefaultTimeScale;
|
||||
const uint64_t kTwoSecondSegmentRepeat = 20;
|
||||
const int kTwoSecondSegmentRepeat = 20;
|
||||
AddSegments(first_s_element_end_time, kTwoSecondDuration, kSize,
|
||||
kTwoSecondSegmentRepeat);
|
||||
|
||||
const uint64_t kExpectedRemovedSegments =
|
||||
const int kExpectedRemovedSegments =
|
||||
(kOneSecondSegmentRepeat + 1 + kTwoSecondSegmentRepeat * 2) -
|
||||
kTimeShiftBufferDepth;
|
||||
|
||||
|
@ -1118,8 +1117,8 @@ TEST_P(TimeShiftBufferDepthTest, UseLastSegmentInS) {
|
|||
kTimeShiftBufferDepth;
|
||||
|
||||
const int64_t kDuration1 = static_cast<int64_t>(kDefaultTimeScale * 1.5);
|
||||
const uint64_t kSize = 20000;
|
||||
const uint64_t kRepeat1 = 1;
|
||||
const int kSize = 20000;
|
||||
const int kRepeat1 = 1;
|
||||
|
||||
AddSegments(initial_start_time_, kDuration1, kSize, kRepeat1);
|
||||
|
||||
|
@ -1127,7 +1126,7 @@ TEST_P(TimeShiftBufferDepthTest, UseLastSegmentInS) {
|
|||
initial_start_time_ + kDuration1 * (kRepeat1 + 1);
|
||||
|
||||
const int64_t kTwoSecondDuration = 2 * kDefaultTimeScale;
|
||||
const uint64_t kTwoSecondSegmentRepeat = 4;
|
||||
const int kTwoSecondSegmentRepeat = 4;
|
||||
|
||||
AddSegments(first_s_element_end_time, kTwoSecondDuration, kSize,
|
||||
kTwoSecondSegmentRepeat);
|
||||
|
@ -1151,8 +1150,8 @@ TEST_P(TimeShiftBufferDepthTest, NormalGap) {
|
|||
kTimeShiftBufferDepth;
|
||||
|
||||
const int64_t kDuration = kDefaultTimeScale;
|
||||
const uint64_t kSize = 20000;
|
||||
const uint64_t kRepeat = 6;
|
||||
const int kSize = 20000;
|
||||
const int kRepeat = 6;
|
||||
// CHECK here so that the when next S element is added with 1 segment, this S
|
||||
// element doesn't go away.
|
||||
CHECK_LT(kRepeat - 1u, static_cast<uint64_t>(kTimeShiftBufferDepth));
|
||||
|
@ -1176,15 +1175,15 @@ TEST_P(TimeShiftBufferDepthTest, NormalGap) {
|
|||
XmlNodeEqual(ExpectedXml(expected_s_element, kDefaultStartNumber)));
|
||||
}
|
||||
|
||||
// Case where there is a huge gap so the first S element is removed.
|
||||
// Timeshift is based on segment duration not on segment time.
|
||||
TEST_P(TimeShiftBufferDepthTest, HugeGap) {
|
||||
const int kTimeShiftBufferDepth = 10;
|
||||
mutable_mpd_options()->mpd_params.time_shift_buffer_depth =
|
||||
kTimeShiftBufferDepth;
|
||||
|
||||
const int64_t kDuration = kDefaultTimeScale;
|
||||
const uint64_t kSize = 20000;
|
||||
const uint64_t kRepeat = 6;
|
||||
const int kSize = 20000;
|
||||
const int kRepeat = 6;
|
||||
AddSegments(initial_start_time_, kDuration, kSize, kRepeat);
|
||||
|
||||
const int64_t first_s_element_end_time =
|
||||
|
@ -1194,7 +1193,7 @@ TEST_P(TimeShiftBufferDepthTest, HugeGap) {
|
|||
const int64_t gap_s_element_start_time =
|
||||
first_s_element_end_time +
|
||||
(kTimeShiftBufferDepth + 1) * kDefaultTimeScale;
|
||||
const uint64_t kSecondSElementRepeat = 9;
|
||||
const int kSecondSElementRepeat = 9;
|
||||
static_assert(
|
||||
kSecondSElementRepeat < static_cast<int64_t>(kTimeShiftBufferDepth),
|
||||
"second_s_element_repeat_must_be_less_than_time_shift_buffer_depth");
|
||||
|
@ -1202,9 +1201,11 @@ TEST_P(TimeShiftBufferDepthTest, HugeGap) {
|
|||
kSecondSElementRepeat);
|
||||
|
||||
std::string expected_s_element =
|
||||
base::StringPrintf(kSElementTemplateWithoutR,
|
||||
initial_start_time_ + kRepeat * kDuration, kDuration) +
|
||||
base::StringPrintf(kSElementTemplate, gap_s_element_start_time, kDuration,
|
||||
kSecondSElementRepeat);
|
||||
const int kExpectedRemovedSegments = kRepeat + 1;
|
||||
const int kExpectedRemovedSegments = kRepeat;
|
||||
EXPECT_THAT(
|
||||
representation_->GetXml().get(),
|
||||
XmlNodeEqual(ExpectedXml(
|
||||
|
@ -1218,9 +1219,9 @@ TEST_P(TimeShiftBufferDepthTest, ManySegments) {
|
|||
kTimeShiftBufferDepth;
|
||||
|
||||
const int64_t kDuration = kDefaultTimeScale;
|
||||
const uint64_t kSize = 20000;
|
||||
const uint64_t kRepeat = 10000;
|
||||
const uint64_t kTotalNumSegments = kRepeat + 1;
|
||||
const int kSize = 20000;
|
||||
const int kRepeat = 10000;
|
||||
const int kTotalNumSegments = kRepeat + 1;
|
||||
AddSegments(initial_start_time_, kDuration, kSize, kRepeat);
|
||||
|
||||
const int kExpectedSegmentsLeft = kTimeShiftBufferDepth + 1;
|
||||
|
@ -1233,7 +1234,7 @@ TEST_P(TimeShiftBufferDepthTest, ManySegments) {
|
|||
std::string expected_s_element = base::StringPrintf(
|
||||
kSElementTemplate,
|
||||
initial_start_time_ + kExpectedRemovedSegments * kDuration, kDuration,
|
||||
static_cast<uint64_t>(kExpectedSegmentsRepeat));
|
||||
kExpectedSegmentsRepeat);
|
||||
EXPECT_THAT(
|
||||
representation_->GetXml().get(),
|
||||
XmlNodeEqual(ExpectedXml(expected_s_element, kExpectedStartNumber)));
|
||||
|
@ -1327,7 +1328,7 @@ TEST_F(RepresentationDeleteSegmentsTest, ManyNonRepeatingSegments) {
|
|||
// Verify that segments are deleted as expected with many repeating segments.
|
||||
TEST_F(RepresentationDeleteSegmentsTest, ManyRepeatingSegments) {
|
||||
const int kLoops = 4;
|
||||
const uint64_t kRepeat = 10;
|
||||
const int kRepeat = 10;
|
||||
for (int i = 0; i < kLoops; ++i) {
|
||||
AddSegments(kInitialStartTime + i * kDuration * (kRepeat + 1), kDuration,
|
||||
kSize, kRepeat);
|
||||
|
|
|
@ -19,7 +19,7 @@ struct SegmentInfo {
|
|||
// |start_time| and has |duration| but none others have |start_time| * N and
|
||||
// |duration|, then this should be set to 0. The semantics is the same as S@r
|
||||
// in the DASH MPD spec.
|
||||
uint64_t repeat;
|
||||
int repeat;
|
||||
};
|
||||
} // namespace shaka
|
||||
|
||||
|
|
Loading…
Reference in New Issue