| Index: webrtc/modules/rtp_rtcp/source/rtcp_packet/feedback_packet_unittest.cc
 | 
| diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/feedback_packet_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/feedback_packet_unittest.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..e96ee0f42d5e88385cfff1214313e997a7572437
 | 
| --- /dev/null
 | 
| +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/feedback_packet_unittest.cc
 | 
| @@ -0,0 +1,394 @@
 | 
| +/*
 | 
| + *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
 | 
| + *
 | 
| + *  Use of this source code is governed by a BSD-style license
 | 
| + *  that can be found in the LICENSE file in the root of the source
 | 
| + *  tree. An additional intellectual property rights grant can be found
 | 
| + *  in the file PATENTS.  All contributing project authors may
 | 
| + *  be found in the AUTHORS file in the root of the source tree.
 | 
| + */
 | 
| +
 | 
| +#include <limits>
 | 
| +
 | 
| +#include "testing/gtest/include/gtest/gtest.h"
 | 
| +
 | 
| +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/feedback_packet.h"
 | 
| +
 | 
| +using webrtc::rtcp::FeedbackPacket;
 | 
| +
 | 
| +namespace webrtc {
 | 
| +namespace {
 | 
| +
 | 
| +static const int kHeaderSize = 8;
 | 
| +static const int kStatusChunkSize = 2;
 | 
| +static const int kSmallDeltaSize = 1;
 | 
| +static const int kLargeDeltaSize = 2;
 | 
| +
 | 
| +static const int64_t kDeltaLimit = 0xFF * FeedbackPacket::kDeltaScaleFactor;
 | 
| +
 | 
| +class FeedbackTester {
 | 
| + public:
 | 
| +  FeedbackTester()
 | 
| +      : base_time_(-1),
 | 
| +        expected_size_(kAnySize),
 | 
| +        default_delta_(FeedbackPacket::kDeltaScaleFactor * 4) {}
 | 
| +
 | 
| +  void WithBaseTime(int64_t base_time) { base_time_ = base_time; }
 | 
| +
 | 
| +  void WithExpectedSize(size_t expected_size) {
 | 
| +    expected_size_ = expected_size;
 | 
| +  }
 | 
| +
 | 
| +  void WithDefaultDelta(int64_t delta) { default_delta_ = delta; }
 | 
| +
 | 
| +  void WithInput(const uint16_t received_seq[],
 | 
| +                 const int64_t received_ts[],
 | 
| +                 uint16_t length) {
 | 
| +    rtc::scoped_ptr<int64_t[]> temp_deltas;
 | 
| +    if (received_ts == nullptr) {
 | 
| +      temp_deltas.reset(new int64_t[length]);
 | 
| +      GenerateDeltas(received_seq, length, temp_deltas.get());
 | 
| +      received_ts = temp_deltas.get();
 | 
| +    }
 | 
| +
 | 
| +    if (base_time_ == -1)
 | 
| +      base_time_ = received_ts[0] - (FeedbackPacket::kDeltaScaleFactor * 300);
 | 
| +
 | 
| +    expected_seq_.clear();
 | 
| +    expected_deltas_.clear();
 | 
| +    feedback_.reset(new FeedbackPacket());
 | 
| +
 | 
| +    feedback_->WithBase(received_seq[0], base_time_, received_ts[0]);
 | 
| +    int64_t last_time = base_time_;
 | 
| +    for (int i = 0; i < length; ++i) {
 | 
| +      int64_t time = received_ts[i];
 | 
| +      EXPECT_TRUE(feedback_->WithReceivedPacket(received_seq[i], time));
 | 
| +
 | 
| +      if (last_time != -1) {
 | 
| +        int64_t delta = time - last_time;
 | 
| +        expected_deltas_.push_back(delta);
 | 
| +      }
 | 
| +      last_time = time;
 | 
| +    }
 | 
| +    expected_seq_.insert(expected_seq_.begin(), &received_seq[0],
 | 
| +                         &received_seq[length]);
 | 
| +  }
 | 
| +
 | 
| +  void VerifyPacket() {
 | 
| +    serialized_ = feedback_->Build();
 | 
| +    VerifyInternal();
 | 
| +    feedback_ =
 | 
| +        FeedbackPacket::ParseFrom(serialized_->Buffer(), serialized_->Length());
 | 
| +    ASSERT_NE(nullptr, feedback_.get());
 | 
| +    VerifyInternal();
 | 
| +  }
 | 
| +
 | 
| +  static const size_t kAnySize = static_cast<size_t>(0) - 1;
 | 
| +
 | 
| + private:
 | 
| +  void VerifyInternal() {
 | 
| +    if (expected_size_ != kAnySize) {
 | 
| +      // Round up to whole 32-bit words.
 | 
| +      size_t expected_size_words = (expected_size_ + 3) / 4;
 | 
| +      size_t expected_size_bytes = expected_size_words * 4;
 | 
| +      EXPECT_EQ(expected_size_bytes, serialized_->Length());
 | 
| +    }
 | 
| +
 | 
| +    std::vector<FeedbackPacket::StatusSymbol> symbols =
 | 
| +        feedback_->GetStatusVector();
 | 
| +    uint16_t seq = feedback_->GetBaseSequence();
 | 
| +    auto seq_it = expected_seq_.begin();
 | 
| +    for (FeedbackPacket::StatusSymbol symbol : symbols) {
 | 
| +      bool received =
 | 
| +          (symbol == FeedbackPacket::StatusSymbol::kReceivedSmallDelta ||
 | 
| +           symbol == FeedbackPacket::StatusSymbol::kReceivedLargeDelta);
 | 
| +      if (seq_it != expected_seq_.end()) {
 | 
| +        if (seq == *seq_it) {
 | 
| +          ASSERT_NE(expected_seq_.end(), seq_it);
 | 
| +          ASSERT_TRUE(received) << "Expected received packet @ " << seq;
 | 
| +          ++seq_it;
 | 
| +        } else {
 | 
| +          ASSERT_FALSE(received) << "Did not expect received packet @ " << seq;
 | 
| +        }
 | 
| +      }
 | 
| +      ++seq;
 | 
| +    }
 | 
| +    ASSERT_EQ(expected_seq_.end(), seq_it);
 | 
| +
 | 
| +    std::vector<int64_t> deltas = feedback_->GetReceiveDeltasUs();
 | 
| +    ASSERT_EQ(expected_deltas_.size(), deltas.size());
 | 
| +    for (size_t i = 0; i < expected_deltas_.size(); ++i)
 | 
| +      EXPECT_EQ(expected_deltas_[i], deltas[i]) << "Delta mismatch @ " << i;
 | 
| +  }
 | 
| +
 | 
| +  void GenerateDeltas(const uint16_t seq[],
 | 
| +                      const size_t length,
 | 
| +                      int64_t* deltas) {
 | 
| +    uint16_t last_seq = seq[0];
 | 
| +    int64_t offset = 0;
 | 
| +
 | 
| +    for (size_t i = 0; i < length; ++i) {
 | 
| +      if (seq[i] < last_seq)
 | 
| +        offset += 0x10000 * default_delta_;
 | 
| +      last_seq = seq[i];
 | 
| +
 | 
| +      deltas[i] = offset + (last_seq * default_delta_);
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  int64_t base_time_;
 | 
| +  std::vector<uint16_t> expected_seq_;
 | 
| +  std::vector<int64_t> expected_deltas_;
 | 
| +  size_t expected_size_;
 | 
| +  int64_t default_delta_;
 | 
| +  rtc::scoped_ptr<FeedbackPacket> feedback_;
 | 
| +  rtc::scoped_ptr<rtcp::RawPacket> serialized_;
 | 
| +};
 | 
| +
 | 
| +TEST(RtcpPacketTest, FeedbackPacket_OneBitVector) {
 | 
| +  const uint16_t kReceived[] = {1, 2, 7, 8, 9, 10, 13};
 | 
| +  const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
 | 
| +  const size_t kExpectedSizeBytes =
 | 
| +      kHeaderSize + kStatusChunkSize + (kLength * kSmallDeltaSize);
 | 
| +
 | 
| +  FeedbackTester test;
 | 
| +  test.WithExpectedSize(kExpectedSizeBytes);
 | 
| +  test.WithInput(kReceived, nullptr, kLength);
 | 
| +  test.VerifyPacket();
 | 
| +}
 | 
| +
 | 
| +TEST(RtcpPacketTest, FeedbackPacket_FullOneBitVector) {
 | 
| +  const uint16_t kReceived[] = {1, 2, 7, 8, 9, 10, 13, 14};
 | 
| +  const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
 | 
| +  const size_t kExpectedSizeBytes =
 | 
| +      kHeaderSize + kStatusChunkSize + (kLength * kSmallDeltaSize);
 | 
| +
 | 
| +  FeedbackTester test;
 | 
| +  test.WithExpectedSize(kExpectedSizeBytes);
 | 
| +  test.WithInput(kReceived, nullptr, kLength);
 | 
| +  test.VerifyPacket();
 | 
| +}
 | 
| +
 | 
| +TEST(RtcpPacketTest, FeedbackPacket_OneBitVector_WrapReceived) {
 | 
| +  const uint16_t kMax = 0xFFFF;
 | 
| +  const uint16_t kReceived[] = {kMax - 2, kMax - 1, kMax, 0, 1, 2};
 | 
| +  const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
 | 
| +  const size_t kExpectedSizeBytes =
 | 
| +      kHeaderSize + kStatusChunkSize + (kLength * kSmallDeltaSize);
 | 
| +
 | 
| +  FeedbackTester test;
 | 
| +  test.WithExpectedSize(kExpectedSizeBytes);
 | 
| +  test.WithInput(kReceived, nullptr, kLength);
 | 
| +  test.VerifyPacket();
 | 
| +}
 | 
| +
 | 
| +TEST(RtcpPacketTest, FeedbackPacket_OneBitVector_WrapMissing) {
 | 
| +  const uint16_t kMax = 0xFFFF;
 | 
| +  const uint16_t kReceived[] = {kMax - 2, kMax - 1, 1, 2};
 | 
| +  const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
 | 
| +  const size_t kExpectedSizeBytes =
 | 
| +      kHeaderSize + kStatusChunkSize + (kLength * kSmallDeltaSize);
 | 
| +
 | 
| +  FeedbackTester test;
 | 
| +  test.WithExpectedSize(kExpectedSizeBytes);
 | 
| +  test.WithInput(kReceived, nullptr, kLength);
 | 
| +  test.VerifyPacket();
 | 
| +}
 | 
| +
 | 
| +TEST(RtcpPacketTest, FeedbackPacket_TwoBitVector) {
 | 
| +  const uint16_t kReceived[] = {1, 2, 6, 7};
 | 
| +  const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
 | 
| +  const size_t kExpectedSizeBytes =
 | 
| +      kHeaderSize + kStatusChunkSize + (kLength * kLargeDeltaSize);
 | 
| +
 | 
| +  FeedbackTester test;
 | 
| +  test.WithExpectedSize(kExpectedSizeBytes);
 | 
| +  test.WithDefaultDelta(kDeltaLimit + FeedbackPacket::kDeltaScaleFactor);
 | 
| +  test.WithInput(kReceived, nullptr, kLength);
 | 
| +  test.VerifyPacket();
 | 
| +}
 | 
| +
 | 
| +TEST(RtcpPacketTest, FeedbackPacket_TwoBitVectorFull) {
 | 
| +  const uint16_t kReceived[] = {1, 2, 6, 7, 8};
 | 
| +  const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
 | 
| +  const size_t kExpectedSizeBytes =
 | 
| +      kHeaderSize + (2 * kStatusChunkSize) + (kLength * kLargeDeltaSize);
 | 
| +
 | 
| +  FeedbackTester test;
 | 
| +  test.WithExpectedSize(kExpectedSizeBytes);
 | 
| +  test.WithDefaultDelta(kDeltaLimit + FeedbackPacket::kDeltaScaleFactor);
 | 
| +  test.WithInput(kReceived, nullptr, kLength);
 | 
| +  test.VerifyPacket();
 | 
| +}
 | 
| +
 | 
| +TEST(RtcpPacketTest, FeedbackPacket_LargeAndNegativeDeltas) {
 | 
| +  const uint16_t kReceived[] = {1, 2, 6, 7, 8};
 | 
| +  const int64_t kReceiveTimes[] = {
 | 
| +      2000, 1000, 4000, 3000,
 | 
| +      3000 + FeedbackPacket::kDeltaScaleFactor * (1 << 8)};
 | 
| +  const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
 | 
| +  const size_t kExpectedSizeBytes =
 | 
| +      kHeaderSize + kStatusChunkSize + (3 * kLargeDeltaSize) + kSmallDeltaSize;
 | 
| +
 | 
| +  FeedbackTester test;
 | 
| +  test.WithExpectedSize(kExpectedSizeBytes);
 | 
| +  test.WithInput(kReceived, kReceiveTimes, kLength);
 | 
| +  test.VerifyPacket();
 | 
| +}
 | 
| +
 | 
| +TEST(RtcpPacketTest, FeedbackPacket_MaxRle) {
 | 
| +  // Expected chunks created:
 | 
| +  // * 1-bit vector chunk (1xreceived + 13xdropped)
 | 
| +  // * RLE chunk of max length for dropped symbol
 | 
| +  // * 1-bit vector chunk (1xreceived + 13xdropped)
 | 
| +
 | 
| +  const size_t kPacketCount = (1 << 13) - 1 + 14;
 | 
| +  const uint16_t kReceived[] = {0, kPacketCount};
 | 
| +  const int64_t kReceiveTimes[] = {1000, 2000};
 | 
| +  const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
 | 
| +  const size_t kExpectedSizeBytes =
 | 
| +      kHeaderSize + (3 * kStatusChunkSize) + (kLength * kSmallDeltaSize);
 | 
| +
 | 
| +  FeedbackTester test;
 | 
| +  test.WithExpectedSize(kExpectedSizeBytes);
 | 
| +  test.WithInput(kReceived, kReceiveTimes, kLength);
 | 
| +  test.VerifyPacket();
 | 
| +}
 | 
| +
 | 
| +TEST(RtcpPacketTest, FeedbackPacket_MinRle) {
 | 
| +  // Expected chunks created:
 | 
| +  // * 1-bit vector chunk (1xreceived + 13xdropped)
 | 
| +  // * RLE chunk of length 15 for dropped symbol
 | 
| +  // * 1-bit vector chunk (1xreceived + 13xdropped)
 | 
| +
 | 
| +  const uint16_t kReceived[] = {0, (14 * 2) + 1};
 | 
| +  const int64_t kReceiveTimes[] = {1000, 2000};
 | 
| +  const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
 | 
| +  const size_t kExpectedSizeBytes =
 | 
| +      kHeaderSize + (3 * kStatusChunkSize) + (kLength * kSmallDeltaSize);
 | 
| +
 | 
| +  FeedbackTester test;
 | 
| +  test.WithExpectedSize(kExpectedSizeBytes);
 | 
| +  test.WithInput(kReceived, kReceiveTimes, kLength);
 | 
| +  test.VerifyPacket();
 | 
| +}
 | 
| +
 | 
| +TEST(RtcpPacketTest, FeedbackPacket_OneToTwoBitVector) {
 | 
| +  const size_t kTwoBitVectorCapacity = 7;
 | 
| +
 | 
| +  FeedbackPacket feedback;
 | 
| +  feedback.WithBase(0, 0, 0);
 | 
| +
 | 
| +  EXPECT_TRUE(feedback.WithReceivedPacket(0, 0));
 | 
| +  EXPECT_TRUE(feedback.WithUntimedPacket(kTwoBitVectorCapacity - 1));
 | 
| +
 | 
| +  rtc::scoped_ptr<rtcp::RawPacket> packet(feedback.Build());
 | 
| +  const size_t kExpectedSizeBytes =
 | 
| +      kHeaderSize + kStatusChunkSize + kSmallDeltaSize;
 | 
| +  const size_t kExpectedSizeWords = (kExpectedSizeBytes + 3) / 4;
 | 
| +
 | 
| +  EXPECT_EQ(kExpectedSizeWords * 4, packet->Length());
 | 
| +  EXPECT_EQ(kTwoBitVectorCapacity, feedback.GetStatusVector().size());
 | 
| +}
 | 
| +
 | 
| +TEST(RtcpPacketTest, FeedbackPacket_OneToTwoBitVectorSplit) {
 | 
| +  const size_t kTwoBitVectorCapacity = 7;
 | 
| +
 | 
| +  FeedbackPacket feedback;
 | 
| +  feedback.WithBase(0, 0, 0);
 | 
| +
 | 
| +  EXPECT_TRUE(feedback.WithReceivedPacket(0, 0));
 | 
| +  EXPECT_TRUE(feedback.WithUntimedPacket(kTwoBitVectorCapacity));
 | 
| +
 | 
| +  rtc::scoped_ptr<rtcp::RawPacket> packet(feedback.Build());
 | 
| +  const size_t kExpectedSizeBytes =
 | 
| +      kHeaderSize + (kStatusChunkSize * 2) + kSmallDeltaSize;
 | 
| +  const size_t kExpectedSizeWords = (kExpectedSizeBytes + 3) / 4;
 | 
| +
 | 
| +  EXPECT_EQ(kExpectedSizeWords * 4, packet->Length());
 | 
| +  EXPECT_EQ(kTwoBitVectorCapacity + 1, feedback.GetStatusVector().size());
 | 
| +}
 | 
| +
 | 
| +TEST(RtcpPacketTest, FeedbackPacket_Aliasing) {
 | 
| +  FeedbackPacket feedback;
 | 
| +  feedback.WithBase(0, 0, 0);
 | 
| +
 | 
| +  const int kSamples = 100;
 | 
| +  const int64_t kTooSmallDelta = FeedbackPacket::kDeltaScaleFactor / 3;
 | 
| +
 | 
| +  for (int i = 0; i < kSamples; ++i)
 | 
| +    feedback.WithReceivedPacket(i, i * kTooSmallDelta);
 | 
| +
 | 
| +  feedback.Build();
 | 
| +  std::vector<int64_t> deltas = feedback.GetReceiveDeltasUs();
 | 
| +
 | 
| +  int64_t accumulated_delta = 0;
 | 
| +  int num_samples = 0;
 | 
| +  for (int64_t delta : deltas) {
 | 
| +    accumulated_delta += delta;
 | 
| +    int64_t expected_time = num_samples * kTooSmallDelta;
 | 
| +    ++num_samples;
 | 
| +
 | 
| +    EXPECT_NEAR(expected_time, accumulated_delta,
 | 
| +                FeedbackPacket::kDeltaScaleFactor / 2);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST(RtcpPacketTest, FeedbackPacket_Limits) {
 | 
| +  // Sequence number wrap above 0x8FFF.
 | 
| +  rtc::scoped_ptr<FeedbackPacket> packet(new FeedbackPacket());
 | 
| +  packet->WithBase(0, 0, 0);
 | 
| +  EXPECT_TRUE(packet->WithReceivedPacket(0x8FFF, 1000));
 | 
| +
 | 
| +  packet.reset(new FeedbackPacket());
 | 
| +  packet->WithBase(0, 0, 0);
 | 
| +  EXPECT_FALSE(packet->WithReceivedPacket(0x8FFF + 1, 1000));
 | 
| +
 | 
| +  // Packet status count max 0xFFFF.
 | 
| +  packet.reset(new FeedbackPacket());
 | 
| +  packet->WithBase(0, 0, 0);
 | 
| +  EXPECT_TRUE(packet->WithReceivedPacket(0x8FFF, 1000));
 | 
| +  EXPECT_TRUE(packet->WithReceivedPacket(0xFFFF, 2000));
 | 
| +  EXPECT_FALSE(packet->WithReceivedPacket(0, 3000));
 | 
| +
 | 
| +  // Reference delta within range(int16_t) * 2 ^ 8 * 250us.
 | 
| +  const int64_t kMaxBaseDelta =
 | 
| +      (static_cast<int64_t>(std::numeric_limits<int16_t>::max()) << 8) *
 | 
| +      FeedbackPacket::kDeltaScaleFactor;
 | 
| +  packet.reset(new FeedbackPacket());
 | 
| +  packet->WithBase(0, 0, kMaxBaseDelta);
 | 
| +
 | 
| +  const int64_t kMinBaseDelta =
 | 
| +      (static_cast<int64_t>(std::numeric_limits<int16_t>::min()) << 8) *
 | 
| +      FeedbackPacket::kDeltaScaleFactor;
 | 
| +  packet.reset(new FeedbackPacket());
 | 
| +  packet->WithBase(0, 0, kMinBaseDelta);
 | 
| +
 | 
| +  // Packet size max 0xFF 32-bit words.
 | 
| +  const size_t kMaxDeltas = (0xFF * 4) - kStatusChunkSize;
 | 
| +  packet.reset(new FeedbackPacket());
 | 
| +  packet->WithBase(0, 0, 0);
 | 
| +  for (size_t i = 0; i < kMaxDeltas; ++i)
 | 
| +    EXPECT_TRUE(packet->WithReceivedPacket(i, i * 1000));
 | 
| +
 | 
| +  EXPECT_FALSE(packet->WithReceivedPacket(kMaxDeltas, kMaxDeltas * 1000));
 | 
| +}
 | 
| +
 | 
| +TEST(RtcpPacketTest, FeedbackPacket_Padding) {
 | 
| +  const size_t kExpectedSizeBytes =
 | 
| +      kHeaderSize + kStatusChunkSize + kSmallDeltaSize;
 | 
| +  const size_t kExpectedSizeWords = (kExpectedSizeBytes + 3) / 4;
 | 
| +
 | 
| +  FeedbackPacket feedback;
 | 
| +  feedback.WithBase(0, 0, 0);
 | 
| +  EXPECT_TRUE(feedback.WithReceivedPacket(0, 0));
 | 
| +
 | 
| +  rtc::scoped_ptr<rtcp::RawPacket> packet(feedback.Build());
 | 
| +  EXPECT_EQ(kExpectedSizeWords * 4, packet->Length());
 | 
| +  ASSERT_GT(kExpectedSizeWords * 4, kExpectedSizeBytes);
 | 
| +  for (size_t i = kExpectedSizeBytes; i < kExpectedSizeWords * 4; ++i)
 | 
| +    EXPECT_EQ(0u, packet->Buffer()[i]);
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| +}  // namespace webrtc
 | 
| 
 |