|  | /* | 
|  | *  Copyright (c) 2012 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. | 
|  | */ | 
|  |  | 
|  |  | 
|  | /* | 
|  | * This file includes unit tests for the RTCPSender. | 
|  | */ | 
|  |  | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | #include "webrtc/common_types.h" | 
|  | #include "webrtc/modules/remote_bitrate_estimator/include/mock/mock_remote_bitrate_observer.h" | 
|  | #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" | 
|  | #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" | 
|  | #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" | 
|  | #include "webrtc/modules/rtp_rtcp/interface/rtp_receiver.h" | 
|  | #include "webrtc/modules/rtp_rtcp/source/rtcp_receiver.h" | 
|  | #include "webrtc/modules/rtp_rtcp/source/rtcp_sender.h" | 
|  | #include "webrtc/modules/rtp_rtcp/source/rtp_receiver_video.h" | 
|  | #include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h" | 
|  | #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | TEST(NACKStringBuilderTest, TestCase1) { | 
|  | NACKStringBuilder builder; | 
|  | builder.PushNACK(5); | 
|  | builder.PushNACK(7); | 
|  | builder.PushNACK(9); | 
|  | builder.PushNACK(10); | 
|  | builder.PushNACK(11); | 
|  | builder.PushNACK(12); | 
|  | builder.PushNACK(15); | 
|  | builder.PushNACK(18); | 
|  | builder.PushNACK(19); | 
|  | EXPECT_EQ(std::string("5,7,9-12,15,18-19"), builder.GetResult()); | 
|  | } | 
|  |  | 
|  | TEST(NACKStringBuilderTest, TestCase2) { | 
|  | NACKStringBuilder builder; | 
|  | builder.PushNACK(5); | 
|  | builder.PushNACK(6); | 
|  | builder.PushNACK(7); | 
|  | builder.PushNACK(9); | 
|  | builder.PushNACK(10); | 
|  | builder.PushNACK(11); | 
|  | builder.PushNACK(12); | 
|  | builder.PushNACK(15); | 
|  | builder.PushNACK(18); | 
|  | builder.PushNACK(19); | 
|  | EXPECT_EQ(std::string("5-7,9-12,15,18-19"), builder.GetResult()); | 
|  | } | 
|  |  | 
|  | TEST(NACKStringBuilderTest, TestCase3) { | 
|  | NACKStringBuilder builder; | 
|  | builder.PushNACK(5); | 
|  | builder.PushNACK(7); | 
|  | builder.PushNACK(9); | 
|  | builder.PushNACK(10); | 
|  | builder.PushNACK(11); | 
|  | builder.PushNACK(12); | 
|  | builder.PushNACK(15); | 
|  | builder.PushNACK(18); | 
|  | builder.PushNACK(19); | 
|  | builder.PushNACK(21); | 
|  | EXPECT_EQ(std::string("5,7,9-12,15,18-19,21"), builder.GetResult()); | 
|  | } | 
|  |  | 
|  | TEST(NACKStringBuilderTest, TestCase4) { | 
|  | NACKStringBuilder builder; | 
|  | builder.PushNACK(5); | 
|  | builder.PushNACK(7); | 
|  | builder.PushNACK(8); | 
|  | builder.PushNACK(9); | 
|  | builder.PushNACK(10); | 
|  | builder.PushNACK(11); | 
|  | builder.PushNACK(12); | 
|  | builder.PushNACK(15); | 
|  | builder.PushNACK(18); | 
|  | builder.PushNACK(19); | 
|  | EXPECT_EQ(std::string("5,7-12,15,18-19"), builder.GetResult()); | 
|  | } | 
|  |  | 
|  | TEST(NACKStringBuilderTest, TestCase5) { | 
|  | NACKStringBuilder builder; | 
|  | builder.PushNACK(5); | 
|  | builder.PushNACK(7); | 
|  | builder.PushNACK(9); | 
|  | builder.PushNACK(10); | 
|  | builder.PushNACK(11); | 
|  | builder.PushNACK(12); | 
|  | builder.PushNACK(15); | 
|  | builder.PushNACK(16); | 
|  | builder.PushNACK(18); | 
|  | builder.PushNACK(19); | 
|  | EXPECT_EQ(std::string("5,7,9-12,15-16,18-19"), builder.GetResult()); | 
|  | } | 
|  |  | 
|  | TEST(NACKStringBuilderTest, TestCase6) { | 
|  | NACKStringBuilder builder; | 
|  | builder.PushNACK(5); | 
|  | builder.PushNACK(7); | 
|  | builder.PushNACK(9); | 
|  | builder.PushNACK(10); | 
|  | builder.PushNACK(11); | 
|  | builder.PushNACK(12); | 
|  | builder.PushNACK(15); | 
|  | builder.PushNACK(16); | 
|  | builder.PushNACK(17); | 
|  | builder.PushNACK(18); | 
|  | builder.PushNACK(19); | 
|  | EXPECT_EQ(std::string("5,7,9-12,15-19"), builder.GetResult()); | 
|  | } | 
|  |  | 
|  | TEST(NACKStringBuilderTest, TestCase7) { | 
|  | NACKStringBuilder builder; | 
|  | builder.PushNACK(5); | 
|  | builder.PushNACK(6); | 
|  | builder.PushNACK(7); | 
|  | builder.PushNACK(8); | 
|  | builder.PushNACK(11); | 
|  | builder.PushNACK(12); | 
|  | builder.PushNACK(13); | 
|  | builder.PushNACK(14); | 
|  | builder.PushNACK(15); | 
|  | EXPECT_EQ(std::string("5-8,11-15"), builder.GetResult()); | 
|  | } | 
|  |  | 
|  | TEST(NACKStringBuilderTest, TestCase8) { | 
|  | NACKStringBuilder builder; | 
|  | builder.PushNACK(5); | 
|  | builder.PushNACK(7); | 
|  | builder.PushNACK(9); | 
|  | builder.PushNACK(11); | 
|  | builder.PushNACK(15); | 
|  | builder.PushNACK(17); | 
|  | builder.PushNACK(19); | 
|  | EXPECT_EQ(std::string("5,7,9,11,15,17,19"), builder.GetResult()); | 
|  | } | 
|  |  | 
|  | TEST(NACKStringBuilderTest, TestCase9) { | 
|  | NACKStringBuilder builder; | 
|  | builder.PushNACK(5); | 
|  | builder.PushNACK(6); | 
|  | builder.PushNACK(7); | 
|  | builder.PushNACK(8); | 
|  | builder.PushNACK(9); | 
|  | builder.PushNACK(10); | 
|  | builder.PushNACK(11); | 
|  | builder.PushNACK(12); | 
|  | EXPECT_EQ(std::string("5-12"), builder.GetResult()); | 
|  | } | 
|  |  | 
|  | TEST(NACKStringBuilderTest, TestCase10) { | 
|  | NACKStringBuilder builder; | 
|  | builder.PushNACK(5); | 
|  | EXPECT_EQ(std::string("5"), builder.GetResult()); | 
|  | } | 
|  |  | 
|  | TEST(NACKStringBuilderTest, TestCase11) { | 
|  | NACKStringBuilder builder; | 
|  | EXPECT_EQ(std::string(""), builder.GetResult()); | 
|  | } | 
|  |  | 
|  | TEST(NACKStringBuilderTest, TestCase12) { | 
|  | NACKStringBuilder builder; | 
|  | builder.PushNACK(5); | 
|  | builder.PushNACK(6); | 
|  | EXPECT_EQ(std::string("5-6"), builder.GetResult()); | 
|  | } | 
|  |  | 
|  | TEST(NACKStringBuilderTest, TestCase13) { | 
|  | NACKStringBuilder builder; | 
|  | builder.PushNACK(5); | 
|  | builder.PushNACK(6); | 
|  | builder.PushNACK(9); | 
|  | EXPECT_EQ(std::string("5-6,9"), builder.GetResult()); | 
|  | } | 
|  |  | 
|  | void CreateRtpPacket(const bool marker_bit, const uint8_t payload, | 
|  | const uint16_t seq_num, const uint32_t timestamp, | 
|  | const uint32_t ssrc, uint8_t* array, | 
|  | uint16_t* cur_pos) { | 
|  | ASSERT_TRUE(payload <= 127); | 
|  | array[(*cur_pos)++] = 0x80; | 
|  | array[(*cur_pos)++] = payload | (marker_bit ? 0x80 : 0); | 
|  | array[(*cur_pos)++] = seq_num >> 8; | 
|  | array[(*cur_pos)++] = seq_num; | 
|  | array[(*cur_pos)++] = timestamp >> 24; | 
|  | array[(*cur_pos)++] = timestamp >> 16; | 
|  | array[(*cur_pos)++] = timestamp >> 8; | 
|  | array[(*cur_pos)++] = timestamp; | 
|  | array[(*cur_pos)++] = ssrc >> 24; | 
|  | array[(*cur_pos)++] = ssrc >> 16; | 
|  | array[(*cur_pos)++] = ssrc >> 8; | 
|  | array[(*cur_pos)++] = ssrc; | 
|  | // VP8 payload header | 
|  | array[(*cur_pos)++] = 0x90;  // X bit = 1 | 
|  | array[(*cur_pos)++] = 0x20;  // T bit = 1 | 
|  | array[(*cur_pos)++] = 0x00;  // TID = 0 | 
|  | array[(*cur_pos)++] = 0x00;  // Key frame | 
|  | array[(*cur_pos)++] = 0x00; | 
|  | array[(*cur_pos)++] = 0x00; | 
|  | array[(*cur_pos)++] = 0x9d; | 
|  | array[(*cur_pos)++] = 0x01; | 
|  | array[(*cur_pos)++] = 0x2a; | 
|  | array[(*cur_pos)++] = 128; | 
|  | array[(*cur_pos)++] = 0; | 
|  | array[(*cur_pos)++] = 96; | 
|  | array[(*cur_pos)++] = 0; | 
|  | } | 
|  |  | 
|  | class TestTransport : public Transport, | 
|  | public NullRtpData { | 
|  | public: | 
|  | TestTransport() | 
|  | : rtcp_receiver_(NULL) { | 
|  | } | 
|  | void SetRTCPReceiver(RTCPReceiver* rtcp_receiver) { | 
|  | rtcp_receiver_ = rtcp_receiver; | 
|  | } | 
|  | virtual int SendPacket(int /*ch*/, const void* /*data*/, int /*len*/) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | virtual int SendRTCPPacket(int /*ch*/, const void *packet, int packet_len) { | 
|  | RTCPUtility::RTCPParserV2 rtcpParser((uint8_t*)packet, | 
|  | (int32_t)packet_len, | 
|  | true); // Allow non-compound RTCP | 
|  |  | 
|  | EXPECT_TRUE(rtcpParser.IsValid()); | 
|  | RTCPHelp::RTCPPacketInformation rtcpPacketInformation; | 
|  | EXPECT_EQ(0, rtcp_receiver_->IncomingRTCPPacket(rtcpPacketInformation, | 
|  | &rtcpParser)); | 
|  | rtcp_packet_info_.rtcpPacketTypeFlags = | 
|  | rtcpPacketInformation.rtcpPacketTypeFlags; | 
|  | rtcp_packet_info_.remoteSSRC = rtcpPacketInformation.remoteSSRC; | 
|  | rtcp_packet_info_.applicationSubType = | 
|  | rtcpPacketInformation.applicationSubType; | 
|  | rtcp_packet_info_.applicationName = rtcpPacketInformation.applicationName; | 
|  | rtcp_packet_info_.report_blocks = rtcpPacketInformation.report_blocks; | 
|  | rtcp_packet_info_.rtt = rtcpPacketInformation.rtt; | 
|  | rtcp_packet_info_.interArrivalJitter = | 
|  | rtcpPacketInformation.interArrivalJitter; | 
|  | rtcp_packet_info_.sliPictureId = rtcpPacketInformation.sliPictureId; | 
|  | rtcp_packet_info_.rpsiPictureId = rtcpPacketInformation.rpsiPictureId; | 
|  | rtcp_packet_info_.receiverEstimatedMaxBitrate = | 
|  | rtcpPacketInformation.receiverEstimatedMaxBitrate; | 
|  | rtcp_packet_info_.ntp_secs = rtcpPacketInformation.ntp_secs; | 
|  | rtcp_packet_info_.ntp_frac = rtcpPacketInformation.ntp_frac; | 
|  | rtcp_packet_info_.rtp_timestamp = rtcpPacketInformation.rtp_timestamp; | 
|  |  | 
|  | return packet_len; | 
|  | } | 
|  |  | 
|  | virtual int OnReceivedPayloadData(const uint8_t* payloadData, | 
|  | const uint16_t payloadSize, | 
|  | const WebRtcRTPHeader* rtpHeader) { | 
|  | return 0; | 
|  | } | 
|  | RTCPReceiver* rtcp_receiver_; | 
|  | RTCPHelp::RTCPPacketInformation rtcp_packet_info_; | 
|  | }; | 
|  |  | 
|  | class RtcpSenderTest : public ::testing::Test { | 
|  | protected: | 
|  | static const uint32_t kRemoteBitrateEstimatorMinBitrateBps = 30000; | 
|  |  | 
|  | RtcpSenderTest() | 
|  | : over_use_detector_options_(), | 
|  | clock_(1335900000), | 
|  | rtp_payload_registry_(new RTPPayloadRegistry( | 
|  | RTPPayloadStrategy::CreateStrategy(false))), | 
|  | remote_bitrate_observer_(), | 
|  | remote_bitrate_estimator_( | 
|  | RemoteBitrateEstimatorFactory().Create( | 
|  | &remote_bitrate_observer_, | 
|  | &clock_, | 
|  | kMimdControl, | 
|  | kRemoteBitrateEstimatorMinBitrateBps)), | 
|  | receive_statistics_(ReceiveStatistics::Create(&clock_)) { | 
|  | test_transport_ = new TestTransport(); | 
|  |  | 
|  | RtpRtcp::Configuration configuration; | 
|  | configuration.id = 0; | 
|  | configuration.audio = false; | 
|  | configuration.clock = &clock_; | 
|  | configuration.outgoing_transport = test_transport_; | 
|  | configuration.remote_bitrate_estimator = remote_bitrate_estimator_.get(); | 
|  |  | 
|  | rtp_rtcp_impl_ = new ModuleRtpRtcpImpl(configuration); | 
|  | rtp_receiver_.reset(RtpReceiver::CreateVideoReceiver( | 
|  | 0, &clock_, test_transport_, NULL, rtp_payload_registry_.get())); | 
|  | rtcp_sender_ = | 
|  | new RTCPSender(0, false, &clock_, receive_statistics_.get()); | 
|  | rtcp_receiver_ = new RTCPReceiver(0, &clock_, rtp_rtcp_impl_); | 
|  | test_transport_->SetRTCPReceiver(rtcp_receiver_); | 
|  | // Initialize | 
|  | EXPECT_EQ(0, rtcp_sender_->Init()); | 
|  | EXPECT_EQ(0, rtcp_sender_->RegisterSendTransport(test_transport_)); | 
|  | } | 
|  | ~RtcpSenderTest() { | 
|  | delete rtcp_sender_; | 
|  | delete rtcp_receiver_; | 
|  | delete rtp_rtcp_impl_; | 
|  | delete test_transport_; | 
|  | } | 
|  |  | 
|  | // Helper function: Incoming RTCP has a specific packet type. | 
|  | bool gotPacketType(RTCPPacketType packet_type) { | 
|  | return ((test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags) & | 
|  | packet_type) != 0U; | 
|  | } | 
|  |  | 
|  | OverUseDetectorOptions over_use_detector_options_; | 
|  | SimulatedClock clock_; | 
|  | scoped_ptr<RTPPayloadRegistry> rtp_payload_registry_; | 
|  | scoped_ptr<RtpReceiver> rtp_receiver_; | 
|  | ModuleRtpRtcpImpl* rtp_rtcp_impl_; | 
|  | RTCPSender* rtcp_sender_; | 
|  | RTCPReceiver* rtcp_receiver_; | 
|  | TestTransport* test_transport_; | 
|  | MockRemoteBitrateObserver remote_bitrate_observer_; | 
|  | scoped_ptr<RemoteBitrateEstimator> remote_bitrate_estimator_; | 
|  | scoped_ptr<ReceiveStatistics> receive_statistics_; | 
|  |  | 
|  | enum {kMaxPacketLength = 1500}; | 
|  | uint8_t packet_[kMaxPacketLength]; | 
|  | }; | 
|  |  | 
|  | TEST_F(RtcpSenderTest, RtcpOff) { | 
|  | EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpOff)); | 
|  | RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); | 
|  | EXPECT_EQ(-1, rtcp_sender_->SendRTCP(feedback_state, kRtcpSr)); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, IJStatus) { | 
|  | ASSERT_FALSE(rtcp_sender_->IJ()); | 
|  | EXPECT_EQ(0, rtcp_sender_->SetIJStatus(true)); | 
|  | ASSERT_TRUE(rtcp_sender_->IJ()); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, TestCompound) { | 
|  | const bool marker_bit = false; | 
|  | const uint8_t payload = 100; | 
|  | const uint16_t seq_num = 11111; | 
|  | const uint32_t timestamp = 1234567; | 
|  | const uint32_t ssrc = 0x11111111; | 
|  | uint16_t packet_length = 0; | 
|  | CreateRtpPacket(marker_bit, payload, seq_num, timestamp, ssrc, packet_, | 
|  | &packet_length); | 
|  | EXPECT_EQ(25, packet_length); | 
|  |  | 
|  | VideoCodec codec_inst; | 
|  | strncpy(codec_inst.plName, "VP8", webrtc::kPayloadNameSize - 1); | 
|  | codec_inst.codecType = webrtc::kVideoCodecVP8; | 
|  | codec_inst.plType = payload; | 
|  | EXPECT_EQ(0, rtp_receiver_->RegisterReceivePayload(codec_inst.plName, | 
|  | codec_inst.plType, | 
|  | 90000, | 
|  | 0, | 
|  | codec_inst.maxBitrate)); | 
|  |  | 
|  | // Make sure RTP packet has been received. | 
|  | scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create()); | 
|  | RTPHeader header; | 
|  | EXPECT_TRUE(parser->Parse(packet_, packet_length, &header)); | 
|  | PayloadUnion payload_specific; | 
|  | EXPECT_TRUE(rtp_payload_registry_->GetPayloadSpecifics(header.payloadType, | 
|  | &payload_specific)); | 
|  | receive_statistics_->IncomingPacket(header, packet_length, false); | 
|  | EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(header, packet_, packet_length, | 
|  | payload_specific, true)); | 
|  |  | 
|  | EXPECT_EQ(0, rtcp_sender_->SetIJStatus(true)); | 
|  | EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound)); | 
|  | RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); | 
|  | EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpRr)); | 
|  |  | 
|  | // Transmission time offset packet should be received. | 
|  | ASSERT_TRUE(test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags & | 
|  | kRtcpTransmissionTimeOffset); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, TestCompound_NoRtpReceived) { | 
|  | EXPECT_EQ(0, rtcp_sender_->SetIJStatus(true)); | 
|  | EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound)); | 
|  | RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); | 
|  | EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpRr)); | 
|  |  | 
|  | // Transmission time offset packet should not be received. | 
|  | ASSERT_FALSE(test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags & | 
|  | kRtcpTransmissionTimeOffset); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, TestXrReceiverReferenceTime) { | 
|  | EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound)); | 
|  | RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); | 
|  | EXPECT_EQ(0, rtcp_sender_->SetSendingStatus(feedback_state, false)); | 
|  | rtcp_sender_->SendRtcpXrReceiverReferenceTime(true); | 
|  | EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpReport)); | 
|  |  | 
|  | EXPECT_TRUE(test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags & | 
|  | kRtcpXrReceiverReferenceTime); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, TestNoXrReceiverReferenceTimeIfSending) { | 
|  | EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound)); | 
|  | RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); | 
|  | EXPECT_EQ(0, rtcp_sender_->SetSendingStatus(feedback_state, true)); | 
|  | rtcp_sender_->SendRtcpXrReceiverReferenceTime(true); | 
|  | EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpReport)); | 
|  |  | 
|  | EXPECT_FALSE(test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags & | 
|  | kRtcpXrReceiverReferenceTime); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, TestNoXrReceiverReferenceTimeIfNotEnabled) { | 
|  | EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound)); | 
|  | RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); | 
|  | EXPECT_EQ(0, rtcp_sender_->SetSendingStatus(feedback_state, false)); | 
|  | rtcp_sender_->SendRtcpXrReceiverReferenceTime(false); | 
|  | EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpReport)); | 
|  |  | 
|  | EXPECT_FALSE(test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags & | 
|  | kRtcpXrReceiverReferenceTime); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, TestSendTimeOfXrRrReport) { | 
|  | EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound)); | 
|  | RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); | 
|  | EXPECT_EQ(0, rtcp_sender_->SetSendingStatus(feedback_state, false)); | 
|  | rtcp_sender_->SendRtcpXrReceiverReferenceTime(true); | 
|  | uint32_t ntp_sec; | 
|  | uint32_t ntp_frac; | 
|  | clock_.CurrentNtp(ntp_sec, ntp_frac); | 
|  | uint32_t initial_mid_ntp = RTCPUtility::MidNtp(ntp_sec, ntp_frac); | 
|  |  | 
|  | // No packet sent. | 
|  | int64_t time_ms; | 
|  | EXPECT_FALSE(rtcp_sender_->SendTimeOfXrRrReport(initial_mid_ntp, &time_ms)); | 
|  |  | 
|  | // Send XR RR packets. | 
|  | for (int i = 0; i <= RTCP_NUMBER_OF_SR; ++i) { | 
|  | EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpReport)); | 
|  | EXPECT_TRUE(test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags & | 
|  | kRtcpXrReceiverReferenceTime); | 
|  |  | 
|  | clock_.CurrentNtp(ntp_sec, ntp_frac); | 
|  | uint32_t mid_ntp = RTCPUtility::MidNtp(ntp_sec, ntp_frac); | 
|  | EXPECT_TRUE(rtcp_sender_->SendTimeOfXrRrReport(mid_ntp, &time_ms)); | 
|  | EXPECT_EQ(clock_.CurrentNtpInMilliseconds(), time_ms); | 
|  | clock_.AdvanceTimeMilliseconds(1000); | 
|  | } | 
|  |  | 
|  | // The first report should no longer be stored. | 
|  | EXPECT_FALSE(rtcp_sender_->SendTimeOfXrRrReport(initial_mid_ntp, &time_ms)); | 
|  | } | 
|  |  | 
|  | // This test is written to verify actual behaviour. It does not seem | 
|  | // to make much sense to send an empty TMMBN, since there is no place | 
|  | // to put an actual limit here. It's just information that no limit | 
|  | // is set, which is kind of the starting assumption. | 
|  | // See http://code.google.com/p/webrtc/issues/detail?id=468 for one | 
|  | // situation where this caused confusion. | 
|  | TEST_F(RtcpSenderTest, SendsTmmbnIfSetAndEmpty) { | 
|  | EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound)); | 
|  | TMMBRSet bounding_set; | 
|  | EXPECT_EQ(0, rtcp_sender_->SetTMMBN(&bounding_set, 3)); | 
|  | ASSERT_EQ(0U, test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags); | 
|  | RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); | 
|  | EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state,kRtcpSr)); | 
|  | // We now expect the packet to show up in the rtcp_packet_info_ of | 
|  | // test_transport_. | 
|  | ASSERT_NE(0U, test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags); | 
|  | EXPECT_TRUE(gotPacketType(kRtcpTmmbn)); | 
|  | TMMBRSet* incoming_set = NULL; | 
|  | bool owner = false; | 
|  | // The BoundingSet function returns the number of members of the | 
|  | // bounding set, and touches the incoming set only if there's > 1. | 
|  | EXPECT_EQ(0, test_transport_->rtcp_receiver_->BoundingSet(owner, | 
|  | incoming_set)); | 
|  | } | 
|  |  | 
|  | TEST_F(RtcpSenderTest, SendsTmmbnIfSetAndValid) { | 
|  | EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound)); | 
|  | TMMBRSet bounding_set; | 
|  | bounding_set.VerifyAndAllocateSet(1); | 
|  | const uint32_t kSourceSsrc = 12345; | 
|  | bounding_set.AddEntry(32768, 0, kSourceSsrc); | 
|  |  | 
|  | EXPECT_EQ(0, rtcp_sender_->SetTMMBN(&bounding_set, 3)); | 
|  | ASSERT_EQ(0U, test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags); | 
|  | RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_); | 
|  | EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpSr)); | 
|  | // We now expect the packet to show up in the rtcp_packet_info_ of | 
|  | // test_transport_. | 
|  | ASSERT_NE(0U, test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags); | 
|  | EXPECT_TRUE(gotPacketType(kRtcpTmmbn)); | 
|  | TMMBRSet incoming_set; | 
|  | bool owner = false; | 
|  | // We expect 1 member of the incoming set. | 
|  | EXPECT_EQ(1, test_transport_->rtcp_receiver_->BoundingSet(owner, | 
|  | &incoming_set)); | 
|  | EXPECT_EQ(kSourceSsrc, incoming_set.Ssrc(0)); | 
|  | } | 
|  | }  // namespace webrtc |