diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2016-03-08 10:20:46 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2016-03-08 09:30:54 +0000 |
commit | d26d06ed332166d6f61a5c06ab85aee5d987b2b6 (patch) | |
tree | 101729a45ae70b06e2586f5c66730f9e2524f3b5 /chromium/media | |
parent | 47c928e8a4986fa683c5763762fa0069c9b3debe (diff) | |
download | qtwebengine-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.cc | 114 | ||||
-rw-r--r-- | chromium/media/cast/net/pacing/paced_sender.h | 15 | ||||
-rw-r--r-- | chromium/media/cast/net/pacing/paced_sender_unittest.cc | 179 | ||||
-rw-r--r-- | chromium/media/filters/audio_clock.cc | 42 | ||||
-rw-r--r-- | chromium/media/filters/audio_clock.h | 23 | ||||
-rw-r--r-- | chromium/media/filters/audio_clock_unittest.cc | 59 | ||||
-rw-r--r-- | chromium/media/filters/source_buffer_range.cc | 4 | ||||
-rw-r--r-- | chromium/media/filters/source_buffer_range.h | 4 | ||||
-rw-r--r-- | chromium/media/filters/source_buffer_stream.cc | 17 | ||||
-rw-r--r-- | chromium/media/filters/source_buffer_stream_unittest.cc | 26 |
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); |