blob: f9bb0668d82b9861ff7a955eea683f3bae210f34 [file] [log] [blame]
// 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 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;
}
uint8 last_frame_acked() { return last_frame_acked_; }
private:
bool triggered_;
MissingPacketsMap missing_packets_; // Missing packets per frame.
uint8 last_frame_acked_;
};
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