summaryrefslogtreecommitdiff
path: root/chromium/media
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@theqtcompany.com>2016-03-08 10:20:46 +0100
committerAllan Sandfeld Jensen <allan.jensen@theqtcompany.com>2016-03-08 09:30:54 +0000
commitd26d06ed332166d6f61a5c06ab85aee5d987b2b6 (patch)
tree101729a45ae70b06e2586f5c66730f9e2524f3b5 /chromium/media
parent47c928e8a4986fa683c5763762fa0069c9b3debe (diff)
downloadqtwebengine-chromium-d26d06ed332166d6f61a5c06ab85aee5d987b2b6.tar.gz
BASELINE: Update Chromium to 49.0.2623.91
And adds sources hunspell dictionary convertion tool. Change-Id: Ifb016b018e7b7cc5a2c56d8e4c1b1b563bd5eaed Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
Diffstat (limited to 'chromium/media')
-rw-r--r--chromium/media/cast/net/pacing/paced_sender.cc114
-rw-r--r--chromium/media/cast/net/pacing/paced_sender.h15
-rw-r--r--chromium/media/cast/net/pacing/paced_sender_unittest.cc179
-rw-r--r--chromium/media/filters/audio_clock.cc42
-rw-r--r--chromium/media/filters/audio_clock.h23
-rw-r--r--chromium/media/filters/audio_clock_unittest.cc59
-rw-r--r--chromium/media/filters/source_buffer_range.cc4
-rw-r--r--chromium/media/filters/source_buffer_range.h4
-rw-r--r--chromium/media/filters/source_buffer_stream.cc17
-rw-r--r--chromium/media/filters/source_buffer_stream_unittest.cc26
10 files changed, 369 insertions, 114 deletions
diff --git a/chromium/media/cast/net/pacing/paced_sender.cc b/chromium/media/cast/net/pacing/paced_sender.cc
index 10849015025..b5e5c0ee766 100644
--- a/chromium/media/cast/net/pacing/paced_sender.cc
+++ b/chromium/media/cast/net/pacing/paced_sender.cc
@@ -40,7 +40,7 @@ PacketKey PacedPacketSender::MakePacketKey(base::TimeTicks capture_time,
}
PacedSender::PacketSendRecord::PacketSendRecord()
- : last_byte_sent(0), last_byte_sent_for_audio(0) {}
+ : last_byte_sent(0), last_byte_sent_for_audio(0), cancel_count(0) {}
PacedSender::PacedSender(
size_t target_burst_size,
@@ -99,6 +99,19 @@ bool PacedSender::SendPackets(const SendPacketVector& packets) {
}
const bool high_priority = IsHighPriority(packets.begin()->first);
for (size_t i = 0; i < packets.size(); i++) {
+ if (VLOG_IS_ON(2)) {
+ PacketSendHistory::const_iterator history_it =
+ send_history_.find(packets[i].first);
+ if (history_it != send_history_.end() &&
+ history_it->second.cancel_count > 0) {
+ VLOG(2) << "PacedSender::SendPackets() called for packet CANCELED "
+ << history_it->second.cancel_count << " times: "
+ << "ssrc=" << packets[i].first.ssrc
+ << ", frame_id=" << packets[i].first.frame_id
+ << ", packet_id=" << packets[i].first.packet_id;
+ }
+ }
+
DCHECK(IsHighPriority(packets[i].first) == high_priority);
if (high_priority) {
priority_packet_list_[packets[i].first] =
@@ -127,6 +140,9 @@ bool PacedSender::ShouldResend(const PacketKey& packet_key,
// packet Y sent just before X. Reject retransmission of X if ACK for
// Y has not been received.
// Only do this for video packets.
+ //
+ // TODO(miu): This sounds wrong. Audio packets are always transmitted first
+ // (because they are put in |priority_packet_list_|, see PopNextPacket()).
if (packet_key.ssrc == video_ssrc_) {
if (dedup_info.last_byte_acked_for_audio &&
it->second.last_byte_sent_for_audio &&
@@ -149,6 +165,19 @@ bool PacedSender::ResendPackets(const SendPacketVector& packets,
const bool high_priority = IsHighPriority(packets.begin()->first);
const base::TimeTicks now = clock_->NowTicks();
for (size_t i = 0; i < packets.size(); i++) {
+ if (VLOG_IS_ON(2)) {
+ PacketSendHistory::const_iterator history_it =
+ send_history_.find(packets[i].first);
+ if (history_it != send_history_.end() &&
+ history_it->second.cancel_count > 0) {
+ VLOG(2) << "PacedSender::ReendPackets() called for packet CANCELED "
+ << history_it->second.cancel_count << " times: "
+ << "ssrc=" << packets[i].first.ssrc
+ << ", frame_id=" << packets[i].first.frame_id
+ << ", packet_id=" << packets[i].first.packet_id;
+ }
+ }
+
if (!ShouldResend(packets[i].first, dedup_info, now)) {
LogPacketEvent(packets[i].second->data, PACKET_RTX_REJECTED);
continue;
@@ -189,18 +218,67 @@ bool PacedSender::SendRtcpPacket(uint32_t ssrc, PacketRef packet) {
void PacedSender::CancelSendingPacket(const PacketKey& packet_key) {
packet_list_.erase(packet_key);
priority_packet_list_.erase(packet_key);
+
+ if (VLOG_IS_ON(2)) {
+ PacketSendHistory::iterator history_it = send_history_.find(packet_key);
+ if (history_it != send_history_.end())
+ ++history_it->second.cancel_count;
+ }
}
PacketRef PacedSender::PopNextPacket(PacketType* packet_type,
PacketKey* packet_key) {
+ // Always pop from the priority list first.
PacketList* list = !priority_packet_list_.empty() ?
&priority_packet_list_ : &packet_list_;
DCHECK(!list->empty());
- PacketList::iterator i = list->begin();
- *packet_type = i->second.first;
- *packet_key = i->first;
- PacketRef ret = i->second.second;
- list->erase(i);
+
+ // Determine which packet in the frame should be popped by examining the
+ // |send_history_| for prior transmission attempts. Packets that have never
+ // been transmitted will be popped first. If all packets have transmitted
+ // before, pop the one that has not been re-attempted for the longest time.
+ PacketList::iterator it = list->begin();
+ PacketKey last_key = it->first;
+ last_key.packet_id = UINT16_C(0xffff);
+ PacketSendHistory::const_iterator history_it =
+ send_history_.lower_bound(it->first);
+ base::TimeTicks earliest_send_time =
+ base::TimeTicks() + base::TimeDelta::Max();
+ PacketList::iterator found_it = it;
+ while (true) {
+ if (history_it == send_history_.end() || it->first < history_it->first) {
+ // There is no send history for this packet, which means it has not been
+ // transmitted yet.
+ found_it = it;
+ break;
+ }
+
+ DCHECK(it->first == history_it->first);
+ if (history_it->second.time < earliest_send_time) {
+ earliest_send_time = history_it->second.time;
+ found_it = it;
+ }
+
+ // Advance to next packet for the current frame, or break if there are no
+ // more.
+ ++it;
+ if (it == list->end() || last_key < it->first)
+ break;
+
+ // Advance to next history entry. Since there may be "holes" in the packet
+ // list (e.g., due to packets canceled for retransmission), it's possible
+ // |history_it| will have to be advanced more than once even though |it| was
+ // only advanced once.
+ do {
+ ++history_it;
+ } while (history_it != send_history_.end() &&
+ history_it->first < it->first);
+ }
+
+ *packet_type = found_it->second.first;
+ *packet_key = found_it->first;
+ PacketRef ret = found_it->second.second;
+ list->erase(found_it);
return ret;
}
@@ -279,8 +357,16 @@ void PacedSender::SendStoredPackets() {
PacketType packet_type;
PacketKey packet_key;
PacketRef packet = PopNextPacket(&packet_type, &packet_key);
- PacketSendRecord send_record;
- send_record.time = now;
+ PacketSendRecord* const send_record = &(send_history_[packet_key]);
+ send_record->time = now;
+
+ if (send_record->cancel_count > 0 && packet_type != PacketType_RTCP) {
+ VLOG(2) << "PacedSender is sending a packet known to have been CANCELED "
+ << send_record->cancel_count << " times: "
+ << "ssrc=" << packet_key.ssrc
+ << ", frame_id=" << packet_key.frame_id
+ << ", packet_id=" << packet_key.packet_id;
+ }
switch (packet_type) {
case PacketType_Resend:
@@ -296,11 +382,10 @@ void PacedSender::SendStoredPackets() {
const bool socket_blocked = !transport_->SendPacket(packet, cb);
// Save the send record.
- send_record.last_byte_sent = transport_->GetBytesSent();
- send_record.last_byte_sent_for_audio = GetLastByteSentForSsrc(audio_ssrc_);
- send_history_[packet_key] = send_record;
- send_history_buffer_[packet_key] = send_record;
- last_byte_sent_[packet_key.ssrc] = send_record.last_byte_sent;
+ send_record->last_byte_sent = transport_->GetBytesSent();
+ send_record->last_byte_sent_for_audio = GetLastByteSentForSsrc(audio_ssrc_);
+ send_history_buffer_[packet_key] = *send_record;
+ last_byte_sent_[packet_key.ssrc] = send_record->last_byte_sent;
if (socket_blocked) {
state_ = State_TransportBlocked;
@@ -310,6 +395,9 @@ void PacedSender::SendStoredPackets() {
}
// Keep ~0.5 seconds of data (1000 packets).
+ //
+ // TODO(miu): This has no relation to the actual size of the frames, and so
+ // there's no way to reason whether 1000 is enough or too much, or whatever.
if (send_history_buffer_.size() >=
max_burst_size_ * kMaxDedupeWindowMs / kPacingIntervalMs) {
send_history_.swap(send_history_buffer_);
diff --git a/chromium/media/cast/net/pacing/paced_sender.h b/chromium/media/cast/net/pacing/paced_sender.h
index 714fe9dcb10..41609e79b17 100644
--- a/chromium/media/cast/net/pacing/paced_sender.h
+++ b/chromium/media/cast/net/pacing/paced_sender.h
@@ -42,6 +42,11 @@ struct PacketKey {
uint32_t frame_id;
uint16_t packet_id;
+ bool operator==(const PacketKey& key) const {
+ return std::tie(capture_time, ssrc, frame_id, packet_id) ==
+ std::tie(key.capture_time, key.ssrc, key.frame_id, key.packet_id);
+ }
+
bool operator<(const PacketKey& key) const {
return std::tie(capture_time, ssrc, frame_id, packet_id) <
std::tie(key.capture_time, key.ssrc, key.frame_id, key.packet_id);
@@ -171,9 +176,12 @@ class PacedSender : public PacedPacketSender,
bool empty() const;
size_t size() const;
- // Returns the next packet to send. RTCP packets have highest priority,
- // resend packets have second highest priority and then comes everything
- // else.
+ // Returns the next packet to send. RTCP packets have highest priority, then
+ // high-priority RTP packets, then normal-priority RTP packets. Packets
+ // within a frame are selected based on fairness to ensure all have an equal
+ // chance of being sent. Therefore, it is up to client code to ensure that
+ // packets acknowledged in NACK messages are removed from PacedSender (see
+ // CancelSendingPacket()), to avoid wasteful retransmission.
PacketRef PopNextPacket(PacketType* packet_type,
PacketKey* packet_key);
@@ -203,6 +211,7 @@ class PacedSender : public PacedPacketSender,
// packet was sent.
int64_t last_byte_sent_for_audio; // Number of bytes sent to network from
// audio stream just before this packet.
+ int cancel_count; // Number of times the packet was canceled (debugging).
};
typedef std::map<PacketKey, PacketSendRecord> PacketSendHistory;
PacketSendHistory send_history_;
diff --git a/chromium/media/cast/net/pacing/paced_sender_unittest.cc b/chromium/media/cast/net/pacing/paced_sender_unittest.cc
index fec5c5f8620..148b0fb6038 100644
--- a/chromium/media/cast/net/pacing/paced_sender_unittest.cc
+++ b/chromium/media/cast/net/pacing/paced_sender_unittest.cc
@@ -5,7 +5,8 @@
#include <stddef.h>
#include <stdint.h>
-#include <list>
+#include <algorithm>
+#include <deque>
#include "base/big_endian.h"
#include "base/macros.h"
@@ -20,41 +21,65 @@ namespace media {
namespace cast {
namespace {
-static const uint8_t kValue = 123;
-static const size_t kSize1 = 101;
-static const size_t kSize2 = 102;
-static const size_t kSize3 = 103;
-static const size_t kSize4 = 104;
-static const size_t kNackSize = 105;
-static const int64_t kStartMillisecond = INT64_C(12345678900000);
-static const uint32_t kVideoSsrc = 0x1234;
-static const uint32_t kAudioSsrc = 0x5678;
-static const uint32_t kVideoFrameRtpTimestamp = 12345;
-static const uint32_t kAudioFrameRtpTimestamp = 23456;
+const uint8_t kValue = 123;
+const size_t kSize1 = 101;
+const size_t kSize2 = 102;
+const size_t kSize3 = 103;
+const size_t kSize4 = 104;
+const size_t kNackSize = 105;
+const int64_t kStartMillisecond = INT64_C(12345678900000);
+const uint32_t kVideoSsrc = 0x1234;
+const uint32_t kAudioSsrc = 0x5678;
+const uint32_t kVideoFrameRtpTimestamp = 12345;
+const uint32_t kAudioFrameRtpTimestamp = 23456;
+
+// RTCP packets don't really have a packet ID. However, the bytes where
+// TestPacketSender checks for the ID should be set to 31611, so we'll just
+// check that.
+const uint16_t kRtcpPacketIdMagic = UINT16_C(31611);
class TestPacketSender : public PacketSender {
public:
TestPacketSender() : bytes_sent_(0) {}
bool SendPacket(PacketRef packet, const base::Closure& cb) final {
- EXPECT_FALSE(expected_packet_size_.empty());
- size_t expected_packet_size = expected_packet_size_.front();
- expected_packet_size_.pop_front();
+ EXPECT_FALSE(expected_packet_sizes_.empty());
+ size_t expected_packet_size = expected_packet_sizes_.front();
+ expected_packet_sizes_.pop_front();
EXPECT_EQ(expected_packet_size, packet->data.size());
bytes_sent_ += packet->data.size();
+
+ // Parse for the packet ID and confirm it is the next one we expect.
+ EXPECT_LE(kSize1, packet->data.size());
+ base::BigEndianReader reader(reinterpret_cast<char*>(&packet->data[0]),
+ packet->data.size());
+ bool success = reader.Skip(14);
+ uint16_t packet_id = 0xffff;
+ success &= reader.ReadU16(&packet_id);
+ EXPECT_TRUE(success);
+ const uint16_t expected_packet_id = expected_packet_ids_.front();
+ expected_packet_ids_.pop_front();
+ EXPECT_EQ(expected_packet_id, packet_id);
+
return true;
}
int64_t GetBytesSent() final { return bytes_sent_; }
- void AddExpectedSize(int expected_packet_size, int repeat_count) {
- for (int i = 0; i < repeat_count; ++i) {
- expected_packet_size_.push_back(expected_packet_size);
+ void AddExpectedSizesAndPacketIds(int packet_size,
+ uint16_t first_packet_id,
+ int sequence_length) {
+ for (int i = 0; i < sequence_length; ++i) {
+ expected_packet_sizes_.push_back(packet_size);
+ expected_packet_ids_.push_back(first_packet_id++);
}
}
- public:
- std::list<int> expected_packet_size_;
+ bool expecting_nothing_else() const { return expected_packet_sizes_.empty(); }
+
+ private:
+ std::deque<int> expected_packet_sizes_;
+ std::deque<uint16_t> expected_packet_ids_;
int64_t bytes_sent_;
DISALLOW_COPY_AND_ASSIGN(TestPacketSender);
@@ -111,17 +136,29 @@ class PacedSenderTest : public ::testing::Test {
return packets;
}
+ void SendWithoutBursting(const SendPacketVector& packets) {
+ const size_t kBatchSize = 10;
+ for (size_t i = 0; i < packets.size(); i += kBatchSize) {
+ const SendPacketVector next_batch(
+ packets.begin() + i,
+ packets.begin() + i + std::min(packets.size() - i, kBatchSize));
+ ASSERT_TRUE(paced_sender_->SendPackets(next_batch));
+ testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+ task_runner_->RunTasks();
+ }
+ }
+
// Use this function to drain the packet list in PacedSender without having
// to test the pacing implementation details.
bool RunUntilEmpty(int max_tries) {
for (int i = 0; i < max_tries; i++) {
testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
task_runner_->RunTasks();
- if (mock_transport_.expected_packet_size_.empty())
+ if (mock_transport_.expecting_nothing_else())
return true;
}
- return mock_transport_.expected_packet_size_.empty();
+ return mock_transport_.expecting_nothing_else();
}
std::vector<PacketEvent> packet_events_;
@@ -136,13 +173,14 @@ class PacedSenderTest : public ::testing::Test {
} // namespace
TEST_F(PacedSenderTest, PassThroughRtcp) {
- mock_transport_.AddExpectedSize(kSize1, 2);
SendPacketVector packets = CreateSendPacketVector(kSize1, 1, true);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(0), 1);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(0), 1);
EXPECT_TRUE(paced_sender_->SendPackets(packets));
EXPECT_TRUE(paced_sender_->ResendPackets(packets, DedupInfo()));
- mock_transport_.AddExpectedSize(kSize2, 1);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize2, kRtcpPacketIdMagic, 1);
Packet tmp(kSize2, kValue);
EXPECT_TRUE(paced_sender_->SendRtcpPacket(
1,
@@ -156,11 +194,11 @@ TEST_F(PacedSenderTest, BasicPace) {
false);
const base::TimeTicks earliest_event_timestamp = testing_clock_.NowTicks();
- mock_transport_.AddExpectedSize(kSize1, 10);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(0), 10);
EXPECT_TRUE(paced_sender_->SendPackets(packets));
// Check that we get the next burst.
- mock_transport_.AddExpectedSize(kSize1, 10);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(10), 10);
base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(10);
testing_clock_.Advance(timeout);
@@ -172,7 +210,7 @@ TEST_F(PacedSenderTest, BasicPace) {
task_runner_->RunTasks();
// Check that we get the next burst.
- mock_transport_.AddExpectedSize(kSize1, 7);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(20), 7);
testing_clock_.Advance(timeout);
task_runner_->RunTasks();
@@ -211,14 +249,14 @@ TEST_F(PacedSenderTest, PaceWithNack) {
CreateSendPacketVector(kSize2, num_of_packets_in_frame, true);
// Check that the first burst of the frame go out on the wire.
- mock_transport_.AddExpectedSize(kSize1, 10);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(0), 10);
EXPECT_TRUE(paced_sender_->SendPackets(first_frame_packets));
// Add first NACK request.
EXPECT_TRUE(paced_sender_->ResendPackets(nack_packets, DedupInfo()));
// Check that we get the first NACK burst.
- mock_transport_.AddExpectedSize(kNackSize, 10);
+ mock_transport_.AddExpectedSizesAndPacketIds(kNackSize, UINT16_C(0), 10);
base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(10);
testing_clock_.Advance(timeout);
task_runner_->RunTasks();
@@ -227,24 +265,25 @@ TEST_F(PacedSenderTest, PaceWithNack) {
EXPECT_TRUE(paced_sender_->ResendPackets(nack_packets, DedupInfo()));
// Check that we get the next NACK burst.
- mock_transport_.AddExpectedSize(kNackSize, 10);
+ mock_transport_.AddExpectedSizesAndPacketIds(kNackSize, UINT16_C(10), 2);
+ mock_transport_.AddExpectedSizesAndPacketIds(kNackSize, UINT16_C(0), 8);
testing_clock_.Advance(timeout);
task_runner_->RunTasks();
// End of NACK plus two packets from the oldest frame.
// Note that two of the NACKs have been de-duped.
- mock_transport_.AddExpectedSize(kNackSize, 2);
- mock_transport_.AddExpectedSize(kSize1, 2);
+ mock_transport_.AddExpectedSizesAndPacketIds(kNackSize, UINT16_C(8), 2);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(10), 2);
testing_clock_.Advance(timeout);
task_runner_->RunTasks();
// Add second frame.
// Make sure we don't delay the second frame due to the previous packets.
- mock_transport_.AddExpectedSize(kSize2, 10);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize2, UINT16_C(0), 10);
EXPECT_TRUE(paced_sender_->SendPackets(second_frame_packets));
// Last packets of frame 2.
- mock_transport_.AddExpectedSize(kSize2, 2);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize2, UINT16_C(10), 2);
testing_clock_.Advance(timeout);
task_runner_->RunTasks();
@@ -301,47 +340,47 @@ TEST_F(PacedSenderTest, PaceWith60fps) {
base::TimeDelta timeout_10ms = base::TimeDelta::FromMilliseconds(10);
// Check that the first burst of the frame go out on the wire.
- mock_transport_.AddExpectedSize(kSize1, 10);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(0), 10);
EXPECT_TRUE(paced_sender_->SendPackets(first_frame_packets));
- mock_transport_.AddExpectedSize(kSize1, 7);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(10), 7);
testing_clock_.Advance(timeout_10ms);
task_runner_->RunTasks();
testing_clock_.Advance(base::TimeDelta::FromMilliseconds(6));
// Add second frame, after 16 ms.
- mock_transport_.AddExpectedSize(kSize2, 3);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize2, UINT16_C(0), 3);
EXPECT_TRUE(paced_sender_->SendPackets(second_frame_packets));
testing_clock_.Advance(base::TimeDelta::FromMilliseconds(4));
- mock_transport_.AddExpectedSize(kSize2, 10);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize2, UINT16_C(3), 10);
testing_clock_.Advance(timeout_10ms);
task_runner_->RunTasks();
- mock_transport_.AddExpectedSize(kSize2, 4);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize2, UINT16_C(13), 4);
testing_clock_.Advance(timeout_10ms);
task_runner_->RunTasks();
testing_clock_.Advance(base::TimeDelta::FromMilliseconds(3));
// Add third frame, after 33 ms.
- mock_transport_.AddExpectedSize(kSize3, 6);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize3, UINT16_C(0), 6);
EXPECT_TRUE(paced_sender_->SendPackets(third_frame_packets));
- mock_transport_.AddExpectedSize(kSize3, 10);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize3, UINT16_C(6), 10);
testing_clock_.Advance(base::TimeDelta::FromMilliseconds(7));
task_runner_->RunTasks();
// Add fourth frame, after 50 ms.
EXPECT_TRUE(paced_sender_->SendPackets(fourth_frame_packets));
- mock_transport_.AddExpectedSize(kSize3, 1);
- mock_transport_.AddExpectedSize(kSize4, 9);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize3, UINT16_C(16), 1);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize4, UINT16_C(0), 9);
testing_clock_.Advance(timeout_10ms);
task_runner_->RunTasks();
- mock_transport_.AddExpectedSize(kSize4, 8);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize4, UINT16_C(9), 8);
testing_clock_.Advance(timeout_10ms);
task_runner_->RunTasks();
@@ -362,11 +401,11 @@ TEST_F(PacedSenderTest, SendPriority) {
// 3. Audio packet x 1.
// 4. Video retransmission packet x 10.
// 5. Video packet x 10.
- mock_transport_.AddExpectedSize(kSize2, 10); // Normal video packets.
- mock_transport_.AddExpectedSize(kSize3, 1); // RTCP packet.
- mock_transport_.AddExpectedSize(kSize1, 1); // Audio packet.
- mock_transport_.AddExpectedSize(kSize4, 10); // Resend video packets.
- mock_transport_.AddExpectedSize(kSize2, 10); // Normal video packets.
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize2, UINT16_C(0), 10);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize3, kRtcpPacketIdMagic, 1);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(0), 1);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize4, UINT16_C(0), 10);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize2, UINT16_C(10), 10);
paced_sender_->RegisterPrioritySsrc(kAudioSsrc);
@@ -406,11 +445,10 @@ TEST_F(PacedSenderTest, SendPriority) {
}
TEST_F(PacedSenderTest, GetLastByteSent) {
- mock_transport_.AddExpectedSize(kSize1, 4);
-
SendPacketVector packets1 = CreateSendPacketVector(kSize1, 1, true);
SendPacketVector packets2 = CreateSendPacketVector(kSize1, 1, false);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(0), 1);
EXPECT_TRUE(paced_sender_->SendPackets(packets1));
EXPECT_EQ(static_cast<int64_t>(kSize1),
paced_sender_->GetLastByteSentForPacket(packets1[0].first));
@@ -418,6 +456,7 @@ TEST_F(PacedSenderTest, GetLastByteSent) {
paced_sender_->GetLastByteSentForSsrc(kAudioSsrc));
EXPECT_EQ(0, paced_sender_->GetLastByteSentForSsrc(kVideoSsrc));
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(0), 1);
EXPECT_TRUE(paced_sender_->SendPackets(packets2));
EXPECT_EQ(static_cast<int64_t>(2 * kSize1),
paced_sender_->GetLastByteSentForPacket(packets2[0].first));
@@ -426,6 +465,7 @@ TEST_F(PacedSenderTest, GetLastByteSent) {
EXPECT_EQ(static_cast<int64_t>(2 * kSize1),
paced_sender_->GetLastByteSentForSsrc(kVideoSsrc));
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(0), 1);
EXPECT_TRUE(paced_sender_->ResendPackets(packets1, DedupInfo()));
EXPECT_EQ(static_cast<int64_t>(3 * kSize1),
paced_sender_->GetLastByteSentForPacket(packets1[0].first));
@@ -434,6 +474,7 @@ TEST_F(PacedSenderTest, GetLastByteSent) {
EXPECT_EQ(static_cast<int64_t>(2 * kSize1),
paced_sender_->GetLastByteSentForSsrc(kVideoSsrc));
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(0), 1);
EXPECT_TRUE(paced_sender_->ResendPackets(packets2, DedupInfo()));
EXPECT_EQ(static_cast<int64_t>(4 * kSize1),
paced_sender_->GetLastByteSentForPacket(packets2[0].first));
@@ -444,9 +485,8 @@ TEST_F(PacedSenderTest, GetLastByteSent) {
}
TEST_F(PacedSenderTest, DedupWithResendInterval) {
- mock_transport_.AddExpectedSize(kSize1, 2);
-
SendPacketVector packets = CreateSendPacketVector(kSize1, 1, true);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(0), 1);
EXPECT_TRUE(paced_sender_->SendPackets(packets));
testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
@@ -458,9 +498,42 @@ TEST_F(PacedSenderTest, DedupWithResendInterval) {
EXPECT_EQ(static_cast<int64_t>(kSize1), mock_transport_.GetBytesSent());
dedup_info.resend_interval = base::TimeDelta::FromMilliseconds(5);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(0), 1);
EXPECT_TRUE(paced_sender_->ResendPackets(packets, dedup_info));
EXPECT_EQ(static_cast<int64_t>(2 * kSize1), mock_transport_.GetBytesSent());
}
+TEST_F(PacedSenderTest, AllPacketsInSameFrameAreResentFairly) {
+ const int kNumPackets = 400;
+ SendPacketVector packets = CreateSendPacketVector(kSize1, kNumPackets, false);
+
+ // Send a large frame (400 packets, yeah!). Confirm that the paced sender
+ // sends each packet in the frame exactly once.
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(0),
+ kNumPackets);
+ SendWithoutBursting(packets);
+ ASSERT_TRUE(mock_transport_.expecting_nothing_else());
+
+ // Resend packets 2 and 3. Confirm that the paced sender sends them. Then,
+ // resend all of the first 10 packets. The paced sender should send packets
+ // 0, 1, and 4 through 9 first, and then 2 and 3.
+ SendPacketVector couple_of_packets;
+ couple_of_packets.push_back(packets[2]);
+ couple_of_packets.push_back(packets[3]);
+
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(2), 2);
+ SendWithoutBursting(couple_of_packets);
+ ASSERT_TRUE(mock_transport_.expecting_nothing_else());
+
+ SendPacketVector first_ten_packets;
+ for (size_t i = 0; i < 10; ++i)
+ first_ten_packets.push_back(packets[i]);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(0), 2);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(4), 6);
+ mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(2), 2);
+ SendWithoutBursting(first_ten_packets);
+ ASSERT_TRUE(mock_transport_.expecting_nothing_else());
+}
+
} // namespace cast
} // namespace media
diff --git a/chromium/media/filters/audio_clock.cc b/chromium/media/filters/audio_clock.cc
index 19c5c2c28ab..0bd0a6c4ac9 100644
--- a/chromium/media/filters/audio_clock.cc
+++ b/chromium/media/filters/audio_clock.cc
@@ -8,6 +8,7 @@
#include <stddef.h>
#include <algorithm>
+#include <cmath>
#include "base/logging.h"
@@ -19,9 +20,8 @@ AudioClock::AudioClock(base::TimeDelta start_timestamp, int sample_rate)
static_cast<double>(base::Time::kMicrosecondsPerSecond) /
sample_rate),
total_buffered_frames_(0),
- front_timestamp_(start_timestamp),
- back_timestamp_(start_timestamp) {
-}
+ front_timestamp_micros_(start_timestamp.InMicroseconds()),
+ back_timestamp_micros_(start_timestamp.InMicroseconds()) {}
AudioClock::~AudioClock() {
}
@@ -36,8 +36,10 @@ void AudioClock::WroteAudio(int frames_written,
DCHECK_GE(playback_rate, 0);
// First write: initialize buffer with silence.
- if (start_timestamp_ == front_timestamp_ && buffered_.empty())
+ if (start_timestamp_.InMicroseconds() == front_timestamp_micros_ &&
+ buffered_.empty()) {
PushBufferedAudioData(delay_frames, 0.0);
+ }
// Move frames from |buffered_| into the computed timestamp based on
// |delay_frames|.
@@ -53,14 +55,16 @@ void AudioClock::WroteAudio(int frames_written,
// Update our front and back timestamps. The back timestamp is considered the
// authoritative source of truth, so base the front timestamp on range of data
// buffered. Doing so avoids accumulation errors on the front timestamp.
- back_timestamp_ += base::TimeDelta::FromMicroseconds(
- frames_written * playback_rate * microseconds_per_frame_);
+ back_timestamp_micros_ +=
+ frames_written * playback_rate * microseconds_per_frame_;
+
// Don't let front timestamp move earlier in time, as could occur due to delay
// frames pushed in the first write, above.
- front_timestamp_ = std::max(front_timestamp_,
- back_timestamp_ - ComputeBufferedMediaDuration());
- DCHECK_GE(front_timestamp_, start_timestamp_);
- DCHECK_LE(front_timestamp_, back_timestamp_);
+ front_timestamp_micros_ =
+ std::max(front_timestamp_micros_,
+ back_timestamp_micros_ - ComputeBufferedMediaDurationMicros());
+ DCHECK_GE(front_timestamp_micros_, start_timestamp_.InMicroseconds());
+ DCHECK_LE(front_timestamp_micros_, back_timestamp_micros_);
}
void AudioClock::CompensateForSuspendedWrites(base::TimeDelta elapsed,
@@ -81,12 +85,15 @@ void AudioClock::CompensateForSuspendedWrites(base::TimeDelta elapsed,
}
base::TimeDelta AudioClock::TimeUntilPlayback(base::TimeDelta timestamp) const {
- DCHECK_GE(timestamp, front_timestamp_);
- DCHECK_LE(timestamp, back_timestamp_);
+ // Use front/back_timestamp() methods rather than internal members. The public
+ // methods round to the nearest microsecond for conversion to TimeDelta and
+ // the rounded value will likely be used by the caller.
+ DCHECK_GE(timestamp, front_timestamp());
+ DCHECK_LE(timestamp, back_timestamp());
int64_t frames_until_timestamp = 0;
double timestamp_us = timestamp.InMicroseconds();
- double media_time_us = front_timestamp_.InMicroseconds();
+ double media_time_us = front_timestamp().InMicroseconds();
for (size_t i = 0; i < buffered_.size(); ++i) {
// Leading silence is always accounted prior to anything else.
@@ -113,8 +120,8 @@ base::TimeDelta AudioClock::TimeUntilPlayback(base::TimeDelta timestamp) const {
frames_until_timestamp += buffered_[i].frames;
}
- return base::TimeDelta::FromMicroseconds(frames_until_timestamp *
- microseconds_per_frame_);
+ return base::TimeDelta::FromMicroseconds(
+ std::round(frames_until_timestamp * microseconds_per_frame_));
}
void AudioClock::ContiguousAudioDataBufferedForTesting(
@@ -179,12 +186,11 @@ void AudioClock::PopBufferedAudioData(int64_t frames) {
}
}
-base::TimeDelta AudioClock::ComputeBufferedMediaDuration() const {
+double AudioClock::ComputeBufferedMediaDurationMicros() const {
double scaled_frames = 0;
for (const auto& buffer : buffered_)
scaled_frames += buffer.frames * buffer.playback_rate;
- return base::TimeDelta::FromMicroseconds(scaled_frames *
- microseconds_per_frame_);
+ return scaled_frames * microseconds_per_frame_;
}
} // namespace media
diff --git a/chromium/media/filters/audio_clock.h b/chromium/media/filters/audio_clock.h
index 024c7915006..42ee2b660c2 100644
--- a/chromium/media/filters/audio_clock.h
+++ b/chromium/media/filters/audio_clock.h
@@ -7,6 +7,7 @@
#include <stdint.h>
+#include <cmath>
#include <deque>
#include "base/macros.h"
@@ -89,8 +90,14 @@ class MEDIA_EXPORT AudioClock {
// |start_timestamp| since no amount of media frames tracked
// media data has been played yet. by AudioClock, which would be
// 1000 + 500 + 250 = 1750 ms.
- base::TimeDelta front_timestamp() const { return front_timestamp_; }
- base::TimeDelta back_timestamp() const { return back_timestamp_; }
+ base::TimeDelta front_timestamp() const {
+ return base::TimeDelta::FromMicroseconds(
+ std::round(front_timestamp_micros_));
+ }
+ base::TimeDelta back_timestamp() const {
+ return base::TimeDelta::FromMicroseconds(
+ std::round(back_timestamp_micros_));
+ }
// Returns the amount of wall time until |timestamp| will be played by the
// audio hardware.
@@ -117,7 +124,7 @@ class MEDIA_EXPORT AudioClock {
// Helpers for operating on |buffered_|.
void PushBufferedAudioData(int64_t frames, double playback_rate);
void PopBufferedAudioData(int64_t frames);
- base::TimeDelta ComputeBufferedMediaDuration() const;
+ double ComputeBufferedMediaDurationMicros() const;
const base::TimeDelta start_timestamp_;
const double microseconds_per_frame_;
@@ -125,8 +132,14 @@ class MEDIA_EXPORT AudioClock {
std::deque<AudioData> buffered_;
int64_t total_buffered_frames_;
- base::TimeDelta front_timestamp_;
- base::TimeDelta back_timestamp_;
+ // Use double rather than TimeDelta to avoid loss of partial microseconds when
+ // converting between frames-written/delayed and time-passed (see conversion
+ // in WroteAudio()). Particularly for |back_timestamp|, which accumulates more
+ // time with each call to WroteAudio(), the loss of precision can accumulate
+ // to create noticeable audio/video sync drift for longer (2-3 hr) videos.
+ // See http://crbug.com/564604.
+ double front_timestamp_micros_;
+ double back_timestamp_micros_;
DISALLOW_COPY_AND_ASSIGN(AudioClock);
};
diff --git a/chromium/media/filters/audio_clock_unittest.cc b/chromium/media/filters/audio_clock_unittest.cc
index 303e8e34573..312ba61d73d 100644
--- a/chromium/media/filters/audio_clock_unittest.cc
+++ b/chromium/media/filters/audio_clock_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/filters/audio_clock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -11,8 +12,7 @@ namespace media {
class AudioClockTest : public testing::Test {
public:
- AudioClockTest()
- : sample_rate_(10), clock_(base::TimeDelta(), sample_rate_) {}
+ AudioClockTest() { SetupClock(base::TimeDelta(), 10); }
~AudioClockTest() override {}
@@ -20,45 +20,51 @@ class AudioClockTest : public testing::Test {
int frames_requested,
int delay_frames,
double playback_rate) {
- clock_.WroteAudio(
- frames_written, frames_requested, delay_frames, playback_rate);
+ clock_->WroteAudio(frames_written, frames_requested, delay_frames,
+ playback_rate);
}
- int FrontTimestampInDays() { return clock_.front_timestamp().InDays(); }
+ void SetupClock(base::TimeDelta start_time, int sample_rate) {
+ sample_rate_ = sample_rate;
+ clock_.reset(new AudioClock(start_time, sample_rate_));
+ }
+
+ int FrontTimestampInDays() { return clock_->front_timestamp().InDays(); }
int FrontTimestampInMilliseconds() {
- return clock_.front_timestamp().InMilliseconds();
+ return clock_->front_timestamp().InMilliseconds();
}
int BackTimestampInMilliseconds() {
- return clock_.back_timestamp().InMilliseconds();
+ return clock_->back_timestamp().InMilliseconds();
}
int TimeUntilPlaybackInMilliseconds(int timestamp_ms) {
- return clock_.TimeUntilPlayback(base::TimeDelta::FromMilliseconds(
- timestamp_ms)).InMilliseconds();
+ return clock_
+ ->TimeUntilPlayback(base::TimeDelta::FromMilliseconds(timestamp_ms))
+ .InMilliseconds();
}
int ContiguousAudioDataBufferedInDays() {
base::TimeDelta total, same_rate_total;
- clock_.ContiguousAudioDataBufferedForTesting(&total, &same_rate_total);
+ clock_->ContiguousAudioDataBufferedForTesting(&total, &same_rate_total);
return total.InDays();
}
int ContiguousAudioDataBufferedInMilliseconds() {
base::TimeDelta total, same_rate_total;
- clock_.ContiguousAudioDataBufferedForTesting(&total, &same_rate_total);
+ clock_->ContiguousAudioDataBufferedForTesting(&total, &same_rate_total);
return total.InMilliseconds();
}
int ContiguousAudioDataBufferedAtSameRateInMilliseconds() {
base::TimeDelta total, same_rate_total;
- clock_.ContiguousAudioDataBufferedForTesting(&total, &same_rate_total);
+ clock_->ContiguousAudioDataBufferedForTesting(&total, &same_rate_total);
return same_rate_total.InMilliseconds();
}
- const int sample_rate_;
- AudioClock clock_;
+ int sample_rate_;
+ scoped_ptr<AudioClock> clock_;
private:
DISALLOW_COPY_AND_ASSIGN(AudioClockTest);
@@ -337,8 +343,8 @@ TEST_F(AudioClockTest, CompensateForSuspendedWrites) {
// Elapsing frames less than we have buffered should do nothing.
const int kDelayFrames = 2;
for (int i = 1000; i <= kBaseTimeMs; i += 1000) {
- clock_.CompensateForSuspendedWrites(base::TimeDelta::FromMilliseconds(i),
- kDelayFrames);
+ clock_->CompensateForSuspendedWrites(base::TimeDelta::FromMilliseconds(i),
+ kDelayFrames);
EXPECT_EQ(kBaseTimeMs - (i - 1000), TimeUntilPlaybackInMilliseconds(0));
// Write silence to simulate maintaining a 7s output buffer.
@@ -347,9 +353,26 @@ TEST_F(AudioClockTest, CompensateForSuspendedWrites) {
// Exhausting all frames should advance timestamps and prime the buffer with
// our delay frames value.
- clock_.CompensateForSuspendedWrites(base::TimeDelta::FromMilliseconds(7000),
- kDelayFrames);
+ clock_->CompensateForSuspendedWrites(base::TimeDelta::FromMilliseconds(7000),
+ kDelayFrames);
EXPECT_EQ(kDelayFrames * 100, TimeUntilPlaybackInMilliseconds(1000));
}
+TEST_F(AudioClockTest, FramesToTimePrecision) {
+ SetupClock(base::TimeDelta(), 48000);
+ double micros_per_frame = base::Time::kMicrosecondsPerSecond / 48000.0;
+ int frames_written = 0;
+
+ // Write ~2 hours of data to clock to give any error a significant chance to
+ // accumulate.
+ while (clock_->back_timestamp() <= base::TimeDelta::FromHours(2)) {
+ frames_written += 1024;
+ WroteAudio(1024, 1024, 0, 1);
+ }
+
+ // Verify no error accumulated.
+ EXPECT_EQ(std::round(frames_written * micros_per_frame),
+ clock_->back_timestamp().InMicroseconds());
+}
+
} // namespace media
diff --git a/chromium/media/filters/source_buffer_range.cc b/chromium/media/filters/source_buffer_range.cc
index 562e14e0964..fdb600a3173 100644
--- a/chromium/media/filters/source_buffer_range.cc
+++ b/chromium/media/filters/source_buffer_range.cc
@@ -228,6 +228,7 @@ bool SourceBufferRange::TruncateAt(
}
size_t SourceBufferRange::DeleteGOPFromFront(BufferQueue* deleted_buffers) {
+ DCHECK(!buffers_.empty());
DCHECK(!FirstGOPContainsNextBufferPosition());
DCHECK(deleted_buffers);
@@ -275,6 +276,7 @@ size_t SourceBufferRange::DeleteGOPFromFront(BufferQueue* deleted_buffers) {
}
size_t SourceBufferRange::DeleteGOPFromBack(BufferQueue* deleted_buffers) {
+ DCHECK(!buffers_.empty());
DCHECK(!LastGOPContainsNextBufferPosition());
DCHECK(deleted_buffers);
@@ -347,7 +349,7 @@ size_t SourceBufferRange::GetRemovalGOP(
bool SourceBufferRange::FirstGOPEarlierThanMediaTime(
DecodeTimestamp media_time) const {
if (keyframe_map_.size() == 1u)
- return (GetEndTimestamp() < media_time);
+ return (GetBufferedEndTimestamp() <= media_time);
KeyframeMap::const_iterator second_gop = keyframe_map_.begin();
++second_gop;
diff --git a/chromium/media/filters/source_buffer_range.h b/chromium/media/filters/source_buffer_range.h
index c29ed4f29ac..f387ad83a96 100644
--- a/chromium/media/filters/source_buffer_range.h
+++ b/chromium/media/filters/source_buffer_range.h
@@ -114,6 +114,8 @@ class SourceBufferRange {
// Deletes a GOP from the front or back of the range and moves these
// buffers into |deleted_buffers|. Returns the number of bytes deleted from
// the range (i.e. the size in bytes of |deleted_buffers|).
+ // This range must NOT be empty when these methods are called.
+ // The GOP being deleted must NOT contain the next buffer position.
size_t DeleteGOPFromFront(BufferQueue* deleted_buffers);
size_t DeleteGOPFromBack(BufferQueue* deleted_buffers);
@@ -126,6 +128,8 @@ class SourceBufferRange {
DecodeTimestamp start_timestamp, DecodeTimestamp end_timestamp,
size_t bytes_to_free, DecodeTimestamp* end_removal_timestamp);
+ // Returns true iff the buffered end time of the first GOP in this range is
+ // at or before |media_time|.
bool FirstGOPEarlierThanMediaTime(DecodeTimestamp media_time) const;
// Indicates whether the GOP at the beginning or end of the range contains the
diff --git a/chromium/media/filters/source_buffer_stream.cc b/chromium/media/filters/source_buffer_stream.cc
index 619ff47ab7d..d2b4fae6e5f 100644
--- a/chromium/media/filters/source_buffer_stream.cc
+++ b/chromium/media/filters/source_buffer_stream.cc
@@ -730,7 +730,7 @@ bool SourceBufferStream::GarbageCollectIfNeeded(DecodeTimestamp media_time,
if (bytes_freed < bytes_to_free) {
size_t front2 = FreeBuffers(bytes_to_free - bytes_freed,
ranges_.back()->GetEndTimestamp(), false);
- DVLOG(3) << __FUNCTION__ << " Removed " << front << " bytes from the"
+ DVLOG(3) << __FUNCTION__ << " Removed " << front2 << " bytes from the"
<< " front. ranges_=" << RangesToString(ranges_);
bytes_freed += front2;
}
@@ -855,13 +855,24 @@ size_t SourceBufferStream::FreeBuffers(size_t total_bytes_to_free,
} else {
current_range = ranges_.front();
DVLOG(5) << "current_range=" << RangeToString(*current_range);
- if (!current_range->FirstGOPEarlierThanMediaTime(media_time)) {
+
+ // FirstGOPEarlierThanMediaTime() is useful here especially if
+ // |seek_pending_| (such that no range contains next buffer
+ // position).
+ // FirstGOPContainsNextBufferPosition() is useful here especially if
+ // |!seek_pending_| to protect against DeleteGOPFromFront() if
+ // FirstGOPEarlierThanMediaTime() was insufficient alone.
+ if (!current_range->FirstGOPEarlierThanMediaTime(media_time) ||
+ current_range->FirstGOPContainsNextBufferPosition()) {
// We have removed all data up to the GOP that contains current playback
// position, we can't delete any further.
DVLOG(5) << "current_range contains playback position, stopping GC";
break;
}
- DVLOG(4) << "Deleting GOP from front: " << RangeToString(*current_range);
+ DVLOG(4) << "Deleting GOP from front: " << RangeToString(*current_range)
+ << ", media_time: " << media_time.InMicroseconds()
+ << ", current_range->HasNextBufferPosition(): "
+ << current_range->HasNextBufferPosition();
bytes_deleted = current_range->DeleteGOPFromFront(&buffers);
}
diff --git a/chromium/media/filters/source_buffer_stream_unittest.cc b/chromium/media/filters/source_buffer_stream_unittest.cc
index fd807083862..cb8d2174f5e 100644
--- a/chromium/media/filters/source_buffer_stream_unittest.cc
+++ b/chromium/media/filters/source_buffer_stream_unittest.cc
@@ -2451,6 +2451,32 @@ TEST_F(SourceBufferStreamTest, GarbageCollection_DeleteFront) {
CheckExpectedBuffers(5, 9, &kDataA);
}
+TEST_F(SourceBufferStreamTest,
+ GarbageCollection_DeleteFront_PreserveSeekedGOP) {
+ // Set memory limit to 15 buffers.
+ SetMemoryLimit(15);
+
+ NewSegmentAppend("0K 10 20 30 40 50K 60 70 80 90");
+ NewSegmentAppend("1000K 1010 1020 1030 1040");
+
+ // GC should be a no-op, since we are just under memory limit.
+ EXPECT_TRUE(stream_->GarbageCollectIfNeeded(DecodeTimestamp(), 0));
+ CheckExpectedRangesByTimestamp("{ [0,100) [1000,1050) }");
+
+ // Seek to the near the end of the first range
+ SeekToTimestampMs(95);
+
+ // We are about to append 7 new buffers and current playback position is at
+ // the end of the last GOP in the first range, so the GC algorithm should be
+ // able to delete some old data from the front, but must not collect the last
+ // GOP in that first range. Neither can it collect the last appended GOP
+ // (which is the entire second range), so GC should return false since it
+ // couldn't collect enough.
+ EXPECT_FALSE(stream_->GarbageCollectIfNeeded(
+ DecodeTimestamp::FromMilliseconds(95), 7));
+ CheckExpectedRangesByTimestamp("{ [50,100) [1000,1050) }");
+}
+
TEST_F(SourceBufferStreamTest, GarbageCollection_DeleteFrontGOPsAtATime) {
// Set memory limit to 20 buffers.
SetMemoryLimit(20);