diff --git a/packager/hls/base/media_playlist.cc b/packager/hls/base/media_playlist.cc index cb26655034..c25c8845aa 100644 --- a/packager/hls/base/media_playlist.cc +++ b/packager/hls/base/media_playlist.cc @@ -305,19 +305,6 @@ std::string PlacementOpportunityEntry::ToString() { return "#EXT-X-PLACEMENT-OPPORTUNITY"; } -double LatestSegmentStartTime( - const std::list>& 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(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(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(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(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_++; } diff --git a/packager/hls/base/media_playlist.h b/packager/hls/base/media_playlist.h index 61306d2fb0..23458bd1d3 100644 --- a/packager/hls/base/media_playlist.h +++ b/packager/hls/base/media_playlist.h @@ -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> 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 segments_to_be_removed_; diff --git a/packager/mpd/base/representation.cc b/packager/mpd/base/representation.cc index c557f91616..820ccea49e 100644 --- a/packager/mpd/base/representation.cc +++ b/packager/mpd/base/representation.cc @@ -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& 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(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( + const int64_t time_shift_buffer_depth = static_cast( 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::iterator first = segment_infos_.begin(); std::list::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(); diff --git a/packager/mpd/base/representation.h b/packager/mpd/base/representation.h index 6e0309309d..6f336cf88f 100644 --- a/packager/mpd/base/representation.h +++ b/packager/mpd/base/representation.h @@ -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 content_protection_elements_; + + int64_t current_buffer_depth_ = 0; // TODO(kqyang): Address sliding window issue with multiple periods. std::list segment_infos_; // A list to hold the file names of the segments to be removed temporarily. diff --git a/packager/mpd/base/representation_unittest.cc b/packager/mpd/base/representation_unittest.cc index 073f7319cc..ae8a5552b6 100644 --- a/packager/mpd/base/representation_unittest.cc +++ b/packager/mpd/base/representation_unittest.cc @@ -410,7 +410,7 @@ namespace { // Any number for {AdaptationSet,Representation} ID. Required to create // either objects. Not checked in test. const char kSElementTemplate[] = - "\n"; + "\n"; const char kSElementTemplateWithoutR[] = "\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) { "" ""; } 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(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(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(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(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(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(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(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(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); diff --git a/packager/mpd/base/segment_info.h b/packager/mpd/base/segment_info.h index ecfba9f3ad..2590719da1 100644 --- a/packager/mpd/base/segment_info.h +++ b/packager/mpd/base/segment_info.h @@ -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