Index: media/cast/framer/cast_message_builder_unittest.cc |
diff --git a/media/cast/framer/cast_message_builder_unittest.cc b/media/cast/framer/cast_message_builder_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..13c6a4639e62bc9aeecbdb6128af2af0e2d8b98d |
--- /dev/null |
+++ b/media/cast/framer/cast_message_builder_unittest.cc |
@@ -0,0 +1,513 @@ |
+// Copyright 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/memory/scoped_ptr.h" |
+#include "base/test/simple_test_tick_clock.h" |
+#include "media/cast/framer/cast_message_builder.h" |
+#include "media/cast/rtcp/rtcp.h" |
+#include "media/cast/rtp_common/rtp_defines.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace media { |
+namespace cast { |
+ |
+static const uint32 kSsrc = 0x1234; |
+static const uint32 kShortTimeIncrementMs = 10; |
+static const uint32 kLongTimeIncrementMs = 40; |
+static const int64 kStartMillisecond = 123456789; |
+ |
+typedef std::map<uint8, int> MissingPacketsMap; |
+ |
+class NackFeedbackVerification : public RtpPayloadFeedback { |
+ public: |
+ NackFeedbackVerification() |
+ : triggered_(false), |
+ missing_packets_(), |
+ last_frame_acked_(0) {} |
+ |
+ |
+ virtual void RequestKeyFrame() OVERRIDE { |
+ request_key_frame_ = true; |
+ } |
+ |
+ virtual void CastFeedback(const RtcpCastMessage& cast_feedback) OVERRIDE { |
+ EXPECT_EQ(kSsrc, cast_feedback.media_ssrc_); |
+ |
+ last_frame_acked_ = cast_feedback.ack_frame_id_; |
+ |
+ MissingFramesAndPacketsMap::const_iterator frame_it = |
+ cast_feedback.missing_frames_and_packets_.begin(); |
+ |
+ // Keep track of the number of missing packets per frame. |
+ missing_packets_.clear(); |
+ while (frame_it != cast_feedback.missing_frames_and_packets_.end()) { |
+ missing_packets_.insert( |
+ std::make_pair(frame_it->first, frame_it->second.size())); |
+ ++frame_it; |
+ } |
+ triggered_ = true; |
+ } |
+ |
+ int num_missing_packets(uint8 frame_id) { |
+ MissingPacketsMap::iterator it; |
+ it = missing_packets_.find(frame_id); |
+ if (it == missing_packets_.end()) return 0; |
+ |
+ return it->second; |
+ } |
+ |
+ // Holds value for one call. |
+ bool triggered() { |
+ bool ret_val = triggered_; |
+ triggered_ = false; |
+ return ret_val; |
+ } |
+ |
+ bool request_key_frame() { |
+ bool ret_val = request_key_frame_; |
+ request_key_frame_ = false; |
+ return ret_val; |
+ } |
+ uint8 last_frame_acked() { return last_frame_acked_; } |
+ |
+ private: |
+ bool triggered_; |
+ MissingPacketsMap missing_packets_; // Missing packets per frame. |
+ uint8 last_frame_acked_; |
+ bool request_key_frame_; |
+}; |
+ |
+class CastMessageBuilderTest : public ::testing::Test { |
+ protected: |
+ CastMessageBuilderTest() |
+ : cast_msg_builder_(new CastMessageBuilder(&feedback_, |
+ &frame_id_map_, |
+ kSsrc, |
+ true, |
+ 0)) { |
+ rtp_header_.webrtc.header.ssrc = kSsrc; |
+ rtp_header_.is_key_frame = false; |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kStartMillisecond)); |
+ cast_msg_builder_->set_clock(&testing_clock_); |
+ } |
+ |
+ ~CastMessageBuilderTest() {} |
+ |
+ void SetFrameId(uint8 frame_id) { |
+ rtp_header_.frame_id = frame_id; |
+ } |
+ |
+ void SetPacketId(uint16 packet_id) { |
+ rtp_header_.packet_id = packet_id; |
+ } |
+ |
+ void SetMaxPacketId(uint16 max_packet_id) { |
+ rtp_header_.max_packet_id = max_packet_id; |
+ } |
+ |
+ void SetKeyFrame(bool is_key) { |
+ rtp_header_.is_key_frame = is_key; |
+ } |
+ |
+ void SetReferenceFrameId(uint8 reference_frame_id) { |
+ rtp_header_.is_reference = true; |
+ rtp_header_.reference_frame_id = reference_frame_id; |
+ } |
+ |
+ void InsertPacket() { |
+ bool complete = false; |
+ frame_id_map_.InsertPacket(rtp_header_, &complete); |
+ if (complete) { |
+ cast_msg_builder_->CompleteFrameReceived(rtp_header_.frame_id, |
+ rtp_header_.is_key_frame); |
+ } |
+ cast_msg_builder_->UpdateCastMessage(); |
+ } |
+ |
+ void SetDecoderSlowerThanMaxFrameRate(int max_unacked_frames) { |
+ cast_msg_builder_.reset(new CastMessageBuilder(&feedback_, |
+ &frame_id_map_, |
+ kSsrc, |
+ false, |
+ max_unacked_frames)); |
+ } |
+ |
+ NackFeedbackVerification feedback_; |
+ scoped_ptr<CastMessageBuilder> cast_msg_builder_; |
+ RtpCastHeader rtp_header_; |
+ FrameIdMap frame_id_map_; |
+ base::SimpleTestTickClock testing_clock_; |
+}; |
+ |
+TEST_F(CastMessageBuilderTest, StartWithAKeyFrame) { |
+ SetFrameId(3); |
+ SetPacketId(0); |
+ SetMaxPacketId(0); |
+ InsertPacket(); |
+ // Should not trigger ack. |
+ EXPECT_FALSE(feedback_.triggered()); |
+ SetFrameId(5); |
+ SetPacketId(0); |
+ SetMaxPacketId(0); |
+ SetKeyFrame(true); |
+ InsertPacket(); |
+ frame_id_map_.RemoveOldFrames(5); // Simulate 5 being pulled for rendering. |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ cast_msg_builder_->UpdateCastMessage(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(5, feedback_.last_frame_acked()); |
+} |
+ |
+TEST_F(CastMessageBuilderTest, OneFrameNackList) { |
+ SetFrameId(0); |
+ SetPacketId(4); |
+ SetMaxPacketId(10); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs)); |
+ EXPECT_FALSE(feedback_.triggered()); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ SetPacketId(5); |
+ InsertPacket(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(4, feedback_.num_missing_packets(0)); |
+} |
+ |
+TEST_F(CastMessageBuilderTest, CompleteFrameMissing) { |
+ // TODO(mikhal): Add indication. |
+ SetFrameId(0); |
+ SetPacketId(2); |
+ SetMaxPacketId(5); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ SetFrameId(2); |
+ SetPacketId(2); |
+ SetMaxPacketId(5); |
+ InsertPacket(); |
+} |
+ |
+TEST_F(CastMessageBuilderTest, FastForwardAck) { |
+ SetFrameId(1); |
+ SetPacketId(0); |
+ SetMaxPacketId(0); |
+ InsertPacket(); |
+ EXPECT_FALSE(feedback_.triggered()); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ SetFrameId(2); |
+ SetPacketId(0); |
+ SetMaxPacketId(0); |
+ InsertPacket(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(255, feedback_.last_frame_acked()); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ SetFrameId(0); |
+ SetPacketId(0); |
+ SetMaxPacketId(0); |
+ SetKeyFrame(true); |
+ InsertPacket(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(2, feedback_.last_frame_acked()); |
+} |
+ |
+TEST_F(CastMessageBuilderTest, RemoveOldFrames) { |
+ SetFrameId(1); |
+ SetPacketId(0); |
+ SetMaxPacketId(1); |
+ InsertPacket(); |
+ EXPECT_FALSE(feedback_.triggered()); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ SetFrameId(2); |
+ SetPacketId(0); |
+ SetMaxPacketId(0); |
+ InsertPacket(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ SetFrameId(3); |
+ SetPacketId(0); |
+ SetMaxPacketId(5); |
+ InsertPacket(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(255, feedback_.last_frame_acked()); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ SetFrameId(5); |
+ SetPacketId(0); |
+ SetMaxPacketId(0); |
+ SetKeyFrame(true); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ frame_id_map_.RemoveOldFrames(5); // Simulate 5 being pulled for rendering. |
+ cast_msg_builder_->UpdateCastMessage(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(5, feedback_.last_frame_acked()); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs)); |
+ SetFrameId(1); |
+ SetPacketId(1); |
+ SetMaxPacketId(1); |
+ InsertPacket(); |
+ EXPECT_FALSE(feedback_.triggered()); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ InsertPacket(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(5, feedback_.last_frame_acked()); |
+} |
+ |
+TEST_F(CastMessageBuilderTest, WrapFastForward) { |
+ SetFrameId(254); |
+ SetPacketId(0); |
+ SetMaxPacketId(1); |
+ SetKeyFrame(true); |
+ InsertPacket(); |
+ EXPECT_FALSE(feedback_.triggered()); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ SetFrameId(255); |
+ SetPacketId(0); |
+ SetMaxPacketId(0); |
+ SetKeyFrame(false); |
+ InsertPacket(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(253, feedback_.last_frame_acked()); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ SetFrameId(0); |
+ SetPacketId(0); |
+ SetMaxPacketId(0); |
+ SetKeyFrame(false); |
+ InsertPacket(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(253, feedback_.last_frame_acked()); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ SetFrameId(254); |
+ SetPacketId(1); |
+ SetMaxPacketId(1); |
+ SetKeyFrame(true); |
+ InsertPacket(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(0, feedback_.last_frame_acked()); |
+} |
+ |
+TEST_F(CastMessageBuilderTest, NackUntilMaxReceivedPacket) { |
+ SetFrameId(0); |
+ SetPacketId(0); |
+ SetMaxPacketId(20); |
+ SetKeyFrame(true); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ SetPacketId(5); |
+ InsertPacket(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(4, feedback_.num_missing_packets(0)); |
+} |
+ |
+TEST_F(CastMessageBuilderTest, NackUntilMaxReceivedPacketNextFrame) { |
+ SetFrameId(0); |
+ SetPacketId(0); |
+ SetMaxPacketId(20); |
+ SetKeyFrame(true); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ SetPacketId(5); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(4, feedback_.num_missing_packets(0)); |
+ SetFrameId(1); |
+ SetMaxPacketId(2); |
+ SetPacketId(0); |
+ SetKeyFrame(false); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(21 - 2, feedback_.num_missing_packets(0)); |
+} |
+ |
+TEST_F(CastMessageBuilderTest, NackUntilMaxReceivedPacketNextKey) { |
+ SetFrameId(0); |
+ SetPacketId(0); |
+ SetMaxPacketId(20); |
+ SetKeyFrame(true); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ SetPacketId(5); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(4, feedback_.num_missing_packets(0)); |
+ SetFrameId(1); |
+ SetMaxPacketId(0); |
+ SetPacketId(0); |
+ SetKeyFrame(true); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(0, feedback_.num_missing_packets(0)); |
+} |
+ |
+TEST_F(CastMessageBuilderTest, Reset) { |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ cast_msg_builder_->Reset(); |
+ frame_id_map_.Clear(); |
+ // Should reset nack list state and request a key frame. |
+ cast_msg_builder_->UpdateCastMessage(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(0, feedback_.num_missing_packets(0)); |
+} |
+ |
+TEST_F(CastMessageBuilderTest, DeltaAfterReset) { |
+ SetFrameId(0); |
+ SetPacketId(0); |
+ SetMaxPacketId(0); |
+ SetKeyFrame(true); |
+ InsertPacket(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(0, feedback_.num_missing_packets(0)); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ cast_msg_builder_->Reset(); |
+ SetFrameId(1); |
+ SetPacketId(0); |
+ SetMaxPacketId(0); |
+ SetKeyFrame(true); |
+ EXPECT_FALSE(feedback_.triggered()); |
+} |
+ |
+TEST_F(CastMessageBuilderTest, BasicRps) { |
+ SetFrameId(0); |
+ SetPacketId(0); |
+ SetMaxPacketId(0); |
+ SetKeyFrame(true); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(0, feedback_.last_frame_acked()); |
+ SetFrameId(3); |
+ SetKeyFrame(false); |
+ SetReferenceFrameId(0); |
+ InsertPacket(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(0, feedback_.last_frame_acked()); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs)); |
+ frame_id_map_.RemoveOldFrames(3); // Simulate 3 being pulled for rendering. |
+ cast_msg_builder_->UpdateCastMessage(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(3, feedback_.last_frame_acked()); |
+} |
+ |
+TEST_F(CastMessageBuilderTest, InOrderRps) { |
+ // Create a pattern - skip to rps, and don't look back. |
+ SetFrameId(0); |
+ SetPacketId(0); |
+ SetMaxPacketId(0); |
+ SetKeyFrame(true); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs)); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(0, feedback_.last_frame_acked()); |
+ SetFrameId(1); |
+ SetPacketId(0); |
+ SetMaxPacketId(1); |
+ SetKeyFrame(false); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs)); |
+ EXPECT_FALSE(feedback_.triggered()); |
+ SetFrameId(3); |
+ SetPacketId(0); |
+ SetMaxPacketId(0); |
+ SetKeyFrame(false); |
+ SetReferenceFrameId(0); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs)); |
+ frame_id_map_.RemoveOldFrames(3); // Simulate 3 being pulled for rendering. |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs)); |
+ cast_msg_builder_->UpdateCastMessage(); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(3, feedback_.last_frame_acked()); |
+ // Make an old frame complete - should not trigger an ack. |
+ SetFrameId(1); |
+ SetPacketId(1); |
+ SetMaxPacketId(1); |
+ SetKeyFrame(false); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs)); |
+ EXPECT_FALSE(feedback_.triggered()); |
+ EXPECT_EQ(3, feedback_.last_frame_acked()); |
+} |
+ |
+TEST_F(CastMessageBuilderTest, SlowDownAck) { |
+ SetDecoderSlowerThanMaxFrameRate(3); |
+ SetFrameId(0); |
+ SetPacketId(0); |
+ SetMaxPacketId(0); |
+ SetKeyFrame(true); |
+ InsertPacket(); |
+ |
+ int frame_id; |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs)); |
+ SetKeyFrame(false); |
+ for (frame_id = 1; frame_id < 3; ++frame_id) { |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(frame_id - 1, feedback_.last_frame_acked()); |
+ SetFrameId(frame_id); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs)); |
+ } |
+ // We should now have entered the slowdown ACK state. |
+ uint8_t expected_frame_id = 1; |
+ for (; frame_id < 10; ++frame_id) { |
+ if (frame_id % 2) ++expected_frame_id; |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(expected_frame_id, feedback_.last_frame_acked()); |
+ SetFrameId(frame_id); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs)); |
+ } |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(expected_frame_id, feedback_.last_frame_acked()); |
+ |
+ // Simulate frame_id being pulled for rendering. |
+ frame_id_map_.RemoveOldFrames(frame_id); |
+ // We should now leave the slowdown ACK state. |
+ ++frame_id; |
+ SetFrameId(frame_id); |
+ InsertPacket(); |
+ testing_clock_.Advance( |
+ base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs)); |
+ EXPECT_TRUE(feedback_.triggered()); |
+ EXPECT_EQ(frame_id, feedback_.last_frame_acked()); |
+} |
+ |
+} // namespace cast |
+} // namespace media |