From faada6e604e04e0765f93829cd29782667a1f235 Mon Sep 17 00:00:00 2001 From: "stefan@webrtc.org" Date: Wed, 18 Dec 2013 20:28:25 +0000 Subject: [PATCH] Integrate fake_network_pipe into direct_transport. TEST=trybots R=mflodman@webrtc.org, pbos@webrtc.org Review URL: https://webrtc-codereview.appspot.com/5529004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5321 4adac7df-926f-26a2-2b94-8c16560cd09d --- webrtc/test/direct_transport.cc | 81 ++----- webrtc/test/direct_transport.h | 21 +- webrtc/test/fake_network_pipe.cc | 203 ++++++++++++++++++ .../include => test}/fake_network_pipe.h | 56 +++-- .../fake_network_pipe_unittest.cc | 65 +++--- webrtc/test/rtp_rtcp_observer.h | 15 +- webrtc/test/webrtc_test_common.gyp | 21 ++ webrtc/video/call_perf_tests.cc | 11 +- .../test/libvietest/libvietest.gypi | 21 -- .../libvietest/testbed/fake_network_pipe.cc | 173 --------------- webrtc/webrtc.gyp | 1 + 11 files changed, 318 insertions(+), 350 deletions(-) create mode 100644 webrtc/test/fake_network_pipe.cc rename webrtc/{video_engine/test/libvietest/include => test}/fake_network_pipe.h (60%) rename webrtc/{video_engine/test/libvietest/testbed => test}/fake_network_pipe_unittest.cc (79%) delete mode 100644 webrtc/video_engine/test/libvietest/testbed/fake_network_pipe.cc diff --git a/webrtc/test/direct_transport.cc b/webrtc/test/direct_transport.cc index 7b3ff22570..96852ed9c0 100644 --- a/webrtc/test/direct_transport.cc +++ b/webrtc/test/direct_transport.cc @@ -23,20 +23,19 @@ DirectTransport::DirectTransport() thread_(ThreadWrapper::CreateThread(NetworkProcess, this)), clock_(Clock::GetRealTimeClock()), shutting_down_(false), - receiver_(NULL), - delay_ms_(0) { + fake_network_(FakeNetworkPipe::Config()) { unsigned int thread_id; EXPECT_TRUE(thread_->Start(thread_id)); } -DirectTransport::DirectTransport(int delay_ms) +DirectTransport::DirectTransport( + const FakeNetworkPipe::Config& config) : lock_(CriticalSectionWrapper::CreateCriticalSection()), packet_event_(EventWrapper::Create()), thread_(ThreadWrapper::CreateThread(NetworkProcess, this)), clock_(Clock::GetRealTimeClock()), shutting_down_(false), - receiver_(NULL), - delay_ms_(delay_ms) { + fake_network_(config) { unsigned int thread_id; EXPECT_TRUE(thread_->Start(thread_id)); } @@ -54,37 +53,19 @@ void DirectTransport::StopSending() { } void DirectTransport::SetReceiver(PacketReceiver* receiver) { - receiver_ = receiver; + fake_network_.SetReceiver(receiver); } bool DirectTransport::SendRtp(const uint8_t* data, size_t length) { - QueuePacket(data, length, clock_->TimeInMilliseconds() + delay_ms_); + fake_network_.SendPacket(data, length); + packet_event_->Set(); return true; } bool DirectTransport::SendRtcp(const uint8_t* data, size_t length) { - QueuePacket(data, length, clock_->TimeInMilliseconds() + delay_ms_); - return true; -} - -DirectTransport::Packet::Packet() : length(0), delivery_time_ms(0) {} - -DirectTransport::Packet::Packet(const uint8_t* data, - size_t length, - int64_t delivery_time_ms) - : length(length), delivery_time_ms(delivery_time_ms) { - EXPECT_LE(length, sizeof(this->data)); - memcpy(this->data, data, length); -} - -void DirectTransport::QueuePacket(const uint8_t* data, - size_t length, - int64_t delivery_time_ms) { - CriticalSectionScoped crit(lock_.get()); - if (receiver_ == NULL) - return; - packet_queue_.push_back(Packet(data, length, delivery_time_ms)); + fake_network_.SendPacket(data, length); packet_event_->Set(); + return true; } bool DirectTransport::NetworkProcess(void* transport) { @@ -92,44 +73,20 @@ bool DirectTransport::NetworkProcess(void* transport) { } bool DirectTransport::SendPackets() { - while (true) { - Packet p; - { - CriticalSectionScoped crit(lock_.get()); - if (packet_queue_.empty()) + fake_network_.Process(); + int wait_time_ms = fake_network_.TimeUntilNextProcess(); + if (wait_time_ms > 0) { + switch (packet_event_->Wait(wait_time_ms)) { + case kEventSignaled: + packet_event_->Reset(); break; - p = packet_queue_.front(); - if (p.delivery_time_ms > clock_->TimeInMilliseconds()) + case kEventTimeout: break; - packet_queue_.pop_front(); - } - receiver_->DeliverPacket(p.data, p.length); - } - uint32_t time_until_next_delivery = WEBRTC_EVENT_INFINITE; - { - CriticalSectionScoped crit(lock_.get()); - if (!packet_queue_.empty()) { - int64_t now_ms = clock_->TimeInMilliseconds(); - const int64_t delivery_time_ms = packet_queue_.front().delivery_time_ms; - if (delivery_time_ms > now_ms) { - time_until_next_delivery = delivery_time_ms - now_ms; - } else { - time_until_next_delivery = 0; - } + case kEventError: + // TODO(pbos): Log a warning here? + return true; } } - - switch (packet_event_->Wait(time_until_next_delivery)) { - case kEventSignaled: - packet_event_->Reset(); - break; - case kEventTimeout: - break; - case kEventError: - // TODO(pbos): Log a warning here? - return true; - } - CriticalSectionScoped crit(lock_.get()); return shutting_down_ ? false : true; } diff --git a/webrtc/test/direct_transport.h b/webrtc/test/direct_transport.h index b6021cbae5..660ffecc2e 100644 --- a/webrtc/test/direct_transport.h +++ b/webrtc/test/direct_transport.h @@ -18,6 +18,7 @@ #include "webrtc/system_wrappers/interface/event_wrapper.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/system_wrappers/interface/thread_wrapper.h" +#include "webrtc/test/fake_network_pipe.h" #include "webrtc/transport.h" namespace webrtc { @@ -30,7 +31,7 @@ namespace test { class DirectTransport : public newapi::Transport { public: DirectTransport(); - explicit DirectTransport(int delay_ms); + explicit DirectTransport(const FakeNetworkPipe::Config& config); ~DirectTransport(); virtual void StopSending(); @@ -40,19 +41,6 @@ class DirectTransport : public newapi::Transport { virtual bool SendRtcp(const uint8_t* data, size_t length) OVERRIDE; private: - struct Packet { - Packet(); - Packet(const uint8_t* data, size_t length, int64_t delivery_time_ms); - - uint8_t data[1500]; - size_t length; - int64_t delivery_time_ms; - }; - - void QueuePacket(const uint8_t* data, - size_t length, - int64_t delivery_time_ms); - static bool NetworkProcess(void* transport); bool SendPackets(); @@ -63,10 +51,7 @@ class DirectTransport : public newapi::Transport { bool shutting_down_; - std::deque packet_queue_; - PacketReceiver* receiver_; - // TODO(stefan): Replace this with FakeNetworkPipe. - const int delay_ms_; + FakeNetworkPipe fake_network_; }; } // namespace test } // namespace webrtc diff --git a/webrtc/test/fake_network_pipe.cc b/webrtc/test/fake_network_pipe.cc new file mode 100644 index 0000000000..5a2424e75c --- /dev/null +++ b/webrtc/test/fake_network_pipe.cc @@ -0,0 +1,203 @@ +/* + * 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. + */ + +#include "webrtc/test/fake_network_pipe.h" + +#include +#include +#include +#include + +#include "webrtc/call.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/tick_util.h" + +namespace webrtc { + +const double kPi = 3.14159265; +const int kDefaultProcessIntervalMs = 30; + +static int GaussianRandom(int mean_delay_ms, int standard_deviation_ms) { + // Creating a Normal distribution variable from two independent uniform + // variables based on the Box-Muller transform. + double uniform1 = (rand() + 1.0) / (RAND_MAX + 1.0); // NOLINT + double uniform2 = (rand() + 1.0) / (RAND_MAX + 1.0); // NOLINT + return static_cast(mean_delay_ms + standard_deviation_ms * + sqrt(-2 * log(uniform1)) * cos(2 * kPi * uniform2)); +} + +class NetworkPacket { + public: + NetworkPacket(const uint8_t* data, size_t length, int64_t send_time, + int64_t arrival_time) + : data_(NULL), + data_length_(length), + send_time_(send_time), + arrival_time_(arrival_time) { + data_ = new uint8_t[length]; + memcpy(data_, data, length); + } + ~NetworkPacket() { + delete [] data_; + } + + uint8_t* data() const { return data_; } + size_t data_length() const { return data_length_; } + int64_t send_time() const { return send_time_; } + int64_t arrival_time() const { return arrival_time_; } + void IncrementArrivalTime(int64_t extra_delay) { + arrival_time_+= extra_delay; + } + + private: + // The packet data. + uint8_t* data_; + // Length of data_. + size_t data_length_; + // The time the packet was sent out on the network. + const int64_t send_time_; + // The time the packet should arrive at the reciver. + int64_t arrival_time_; +}; + +FakeNetworkPipe::FakeNetworkPipe( + const FakeNetworkPipe::Config& config) + : lock_(CriticalSectionWrapper::CreateCriticalSection()), + packet_receiver_(NULL), + config_(config), + dropped_packets_(0), + sent_packets_(0), + total_packet_delay_(0), + next_process_time_(TickTime::MillisecondTimestamp()) { +} + +FakeNetworkPipe::~FakeNetworkPipe() { + while (!capacity_link_.empty()) { + delete capacity_link_.front(); + capacity_link_.pop(); + } + while (!delay_link_.empty()) { + delete delay_link_.front(); + delay_link_.pop(); + } +} + +void FakeNetworkPipe::SetReceiver(PacketReceiver* receiver) { + packet_receiver_ = receiver; +} + +void FakeNetworkPipe::SendPacket(const uint8_t* data, size_t data_length) { + // A NULL packet_receiver_ means that this pipe will terminate the flow of + // packets. + if (packet_receiver_ == NULL) + return; + CriticalSectionScoped crit(lock_.get()); + if (config_.queue_length > 0 && + capacity_link_.size() >= config_.queue_length) { + // Too many packet on the link, drop this one. + ++dropped_packets_; + return; + } + + int64_t time_now = TickTime::MillisecondTimestamp(); + + // Delay introduced by the link capacity. + int64_t capacity_delay_ms = 0; + if (config_.link_capacity_kbps > 0) + capacity_delay_ms = data_length / (config_.link_capacity_kbps / 8); + int64_t network_start_time = time_now; + + // Check if there already are packets on the link and change network start + // time if there is. + if (capacity_link_.size() > 0) + network_start_time = capacity_link_.back()->arrival_time(); + + int64_t arrival_time = network_start_time + capacity_delay_ms; + NetworkPacket* packet = new NetworkPacket(data, data_length, time_now, + arrival_time); + capacity_link_.push(packet); +} + +float FakeNetworkPipe::PercentageLoss() { + CriticalSectionScoped crit(lock_.get()); + if (sent_packets_ == 0) + return 0; + + return static_cast(dropped_packets_) / + (sent_packets_ + dropped_packets_); +} + +int FakeNetworkPipe::AverageDelay() { + CriticalSectionScoped crit(lock_.get()); + if (sent_packets_ == 0) + return 0; + + return total_packet_delay_ / static_cast(sent_packets_); +} + +void FakeNetworkPipe::Process() { + int64_t time_now = TickTime::MillisecondTimestamp(); + std::queue packets_to_deliver; + { + CriticalSectionScoped crit(lock_.get()); + // Check the capacity link first. + while (capacity_link_.size() > 0 && + time_now >= capacity_link_.front()->arrival_time()) { + // Time to get this packet. + NetworkPacket* packet = capacity_link_.front(); + capacity_link_.pop(); + + // Add extra delay and jitter, but make sure the arrival time is not + // earlier than the last packet in the queue. + int extra_delay = GaussianRandom(config_.queue_delay_ms, + config_.delay_standard_deviation_ms); + if (delay_link_.size() > 0 && + packet->arrival_time() + extra_delay < + delay_link_.back()->arrival_time()) { + extra_delay = delay_link_.back()->arrival_time() - + packet->arrival_time(); + } + packet->IncrementArrivalTime(extra_delay); + if (packet->arrival_time() < next_process_time_) + next_process_time_ = packet->arrival_time(); + delay_link_.push(packet); + } + + // Check the extra delay queue. + while (delay_link_.size() > 0 && + time_now >= delay_link_.front()->arrival_time()) { + // Deliver this packet. + NetworkPacket* packet = delay_link_.front(); + packets_to_deliver.push(packet); + delay_link_.pop(); + // |time_now| might be later than when the packet should have arrived, due + // to NetworkProcess being called too late. For stats, use the time it + // should have been on the link. + total_packet_delay_ += packet->arrival_time() - packet->send_time(); + } + sent_packets_ += packets_to_deliver.size(); + } + while (!packets_to_deliver.empty()) { + NetworkPacket* packet = packets_to_deliver.front(); + packets_to_deliver.pop(); + packet_receiver_->DeliverPacket(packet->data(), packet->data_length()); + delete packet; + } +} + +int FakeNetworkPipe::TimeUntilNextProcess() const { + CriticalSectionScoped crit(lock_.get()); + if (capacity_link_.size() == 0 || delay_link_.size() == 0) + return kDefaultProcessIntervalMs; + return std::max(static_cast(next_process_time_ - + TickTime::MillisecondTimestamp()), 0); +} + +} // namespace webrtc diff --git a/webrtc/video_engine/test/libvietest/include/fake_network_pipe.h b/webrtc/test/fake_network_pipe.h similarity index 60% rename from webrtc/video_engine/test/libvietest/include/fake_network_pipe.h rename to webrtc/test/fake_network_pipe.h index ece1472d32..e75045701d 100644 --- a/webrtc/video_engine/test/libvietest/include/fake_network_pipe.h +++ b/webrtc/test/fake_network_pipe.h @@ -8,12 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_VIDEO_ENGINE_TEST_LIBVIETEST_INCLUDE_FAKE_NETWORK_PIPE_H_ -#define WEBRTC_VIDEO_ENGINE_TEST_LIBVIETEST_INCLUDE_FAKE_NETWORK_PIPE_H_ +#ifndef WEBRTC_TEST_FAKE_NETWORK_PIPE_H_ +#define WEBRTC_TEST_FAKE_NETWORK_PIPE_H_ #include #include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/system_wrappers/interface/event_wrapper.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/typedefs.h" @@ -21,14 +22,7 @@ namespace webrtc { class CriticalSectionWrapper; class NetworkPacket; - -class PacketReceiver { - public: - // Delivers a new packet to the receive side of the network pipe. The - // implementor of PacketReceiver now owns the memory. - virtual void IncomingPacket(uint8_t* packet, int length) = 0; - virtual ~PacketReceiver() {} -}; +class PacketReceiver; // Class faking a network link. This is a simple and naive solution just faking // capacity and adding an extra transport delay in addition to the capacity @@ -37,18 +31,15 @@ class PacketReceiver { // TODO(mflodman) Add random and bursty packet loss. class FakeNetworkPipe { public: - struct Configuration { - Configuration() - : packet_receiver(NULL), - queue_length(0), + struct Config { + Config() + : queue_length(0), queue_delay_ms(0), delay_standard_deviation_ms(0), link_capacity_kbps(0), loss_percent(0) { } - // Callback to deliver received packets. - PacketReceiver* packet_receiver; - // Queue lenght in number of packets. + // Queue length in number of packets. size_t queue_length; // Delay in addition to capacity induced delay. int queue_delay_ms; @@ -60,44 +51,45 @@ class FakeNetworkPipe { int loss_percent; }; - explicit FakeNetworkPipe(const FakeNetworkPipe::Configuration& configuration); + explicit FakeNetworkPipe(const FakeNetworkPipe::Config& config); ~FakeNetworkPipe(); + // Must not be called in parallel with SendPacket or Process. + void SetReceiver(PacketReceiver* receiver); + // Sends a new packet to the link. - void SendPacket(void* packet, int packet_length); + void SendPacket(const uint8_t* packet, size_t packet_length); // Processes the network queues and trigger PacketReceiver::IncomingPacket for // packets ready to be delivered. - void NetworkProcess(); + void Process(); + int TimeUntilNextProcess() const; // Get statistics. float PercentageLoss(); int AverageDelay(); - int dropped_packets() { return dropped_packets_; } - int sent_packets() { return sent_packets_; } + size_t dropped_packets() { return dropped_packets_; } + size_t sent_packets() { return sent_packets_; } private: + scoped_ptr lock_; PacketReceiver* packet_receiver_; - scoped_ptr link_cs_; std::queue capacity_link_; std::queue delay_link_; // Link configuration. - const size_t queue_length_; - const int queue_delay_ms_; - const int queue_delay_deviation_ms_; - const int link_capacity_bytes_ms_; // In bytes per ms. - - const int loss_percent_; + Config config_; // Statistics. - int dropped_packets_; - int sent_packets_; + size_t dropped_packets_; + size_t sent_packets_; int total_packet_delay_; + int64_t next_process_time_; + DISALLOW_COPY_AND_ASSIGN(FakeNetworkPipe); }; } // namespace webrtc -#endif // WEBRTC_VIDEO_ENGINE_TEST_LIBVIETEST_INCLUDE_FAKE_NETWORK_PIPE_H_ +#endif // WEBRTC_TEST_FAKE_NETWORK_PIPE_H_ diff --git a/webrtc/video_engine/test/libvietest/testbed/fake_network_pipe_unittest.cc b/webrtc/test/fake_network_pipe_unittest.cc similarity index 79% rename from webrtc/video_engine/test/libvietest/testbed/fake_network_pipe_unittest.cc rename to webrtc/test/fake_network_pipe_unittest.cc index 7747302d0f..1245f618f1 100644 --- a/webrtc/video_engine/test/libvietest/testbed/fake_network_pipe_unittest.cc +++ b/webrtc/test/fake_network_pipe_unittest.cc @@ -11,9 +11,10 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/call.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/system_wrappers/interface/tick_util.h" -#include "webrtc/video_engine/test/libvietest/include/fake_network_pipe.h" +#include "webrtc/test/fake_network_pipe.h" using ::testing::_; using ::testing::AnyNumber; @@ -27,12 +28,12 @@ class MockReceiver : public PacketReceiver { MockReceiver() {} virtual ~MockReceiver() {} - void IncomingPacket(uint8_t* data, int length) { - IncomingData(data, length); + void IncomingPacket(const uint8_t* data, size_t length) { + DeliverPacket(data, length); delete [] data; } - MOCK_METHOD2(IncomingData, void(uint8_t*, int)); + MOCK_METHOD2(DeliverPacket, bool(const uint8_t*, size_t)); }; class FakeNetworkPipeTest : public ::testing::Test { @@ -63,11 +64,11 @@ void DeleteMemory(uint8_t* data, int length) { delete [] data; } // Test the capacity link and verify we get as many packets as we expect. TEST_F(FakeNetworkPipeTest, CapacityTest) { - FakeNetworkPipe::Configuration config; - config.packet_receiver = receiver_.get(); + FakeNetworkPipe::Config config; config.queue_length = 20; config.link_capacity_kbps = 80; scoped_ptr pipe(new FakeNetworkPipe(config)); + pipe->SetReceiver(receiver_.get()); // Add 10 packets of 1000 bytes, = 80 kb, and verify it takes one second to // get through the pipe. @@ -80,37 +81,37 @@ TEST_F(FakeNetworkPipeTest, CapacityTest) { kPacketSize); // Time haven't increased yet, so we souldn't get any packets. - EXPECT_CALL(*receiver_, IncomingData(_, _)) + EXPECT_CALL(*receiver_, DeliverPacket(_, _)) .Times(0); - pipe->NetworkProcess(); + pipe->Process(); // Advance enough time to release one packet. TickTime::AdvanceFakeClock(kPacketTimeMs); - EXPECT_CALL(*receiver_, IncomingData(_, _)) + EXPECT_CALL(*receiver_, DeliverPacket(_, _)) .Times(1); - pipe->NetworkProcess(); + pipe->Process(); // Release all but one packet TickTime::AdvanceFakeClock(9 * kPacketTimeMs - 1); - EXPECT_CALL(*receiver_, IncomingData(_, _)) + EXPECT_CALL(*receiver_, DeliverPacket(_, _)) .Times(8); - pipe->NetworkProcess(); + pipe->Process(); // And the last one. TickTime::AdvanceFakeClock(1); - EXPECT_CALL(*receiver_, IncomingData(_, _)) + EXPECT_CALL(*receiver_, DeliverPacket(_, _)) .Times(1); - pipe->NetworkProcess(); + pipe->Process(); } // Test the extra network delay. TEST_F(FakeNetworkPipeTest, ExtraDelayTest) { - FakeNetworkPipe::Configuration config; - config.packet_receiver = receiver_.get(); + FakeNetworkPipe::Config config; config.queue_length = 20; config.queue_delay_ms = 100; config.link_capacity_kbps = 80; scoped_ptr pipe(new FakeNetworkPipe(config)); + pipe->SetReceiver(receiver_.get()); const int kNumPackets = 2; const int kPacketSize = 1000; @@ -122,31 +123,31 @@ TEST_F(FakeNetworkPipeTest, ExtraDelayTest) { // Increase more than kPacketTimeMs, but not more than the extra delay. TickTime::AdvanceFakeClock(kPacketTimeMs); - EXPECT_CALL(*receiver_, IncomingData(_, _)) + EXPECT_CALL(*receiver_, DeliverPacket(_, _)) .Times(0); - pipe->NetworkProcess(); + pipe->Process(); // Advance the network delay to get the first packet. TickTime::AdvanceFakeClock(config.queue_delay_ms); - EXPECT_CALL(*receiver_, IncomingData(_, _)) + EXPECT_CALL(*receiver_, DeliverPacket(_, _)) .Times(1); - pipe->NetworkProcess(); + pipe->Process(); // Advance one more kPacketTimeMs to get the last packet. TickTime::AdvanceFakeClock(kPacketTimeMs); - EXPECT_CALL(*receiver_, IncomingData(_, _)) + EXPECT_CALL(*receiver_, DeliverPacket(_, _)) .Times(1); - pipe->NetworkProcess(); + pipe->Process(); } // Test the number of buffers and packets are dropped when sending too many // packets too quickly. TEST_F(FakeNetworkPipeTest, QueueLengthTest) { - FakeNetworkPipe::Configuration config; - config.packet_receiver = receiver_.get(); + FakeNetworkPipe::Config config; config.queue_length = 2; config.link_capacity_kbps = 80; scoped_ptr pipe(new FakeNetworkPipe(config)); + pipe->SetReceiver(receiver_.get()); const int kPacketSize = 1000; const int kPacketTimeMs = PacketTimeMs(config.link_capacity_kbps, @@ -158,19 +159,19 @@ TEST_F(FakeNetworkPipeTest, QueueLengthTest) { // Increase time enough to deliver all three packets, verify only two are // delivered. TickTime::AdvanceFakeClock(3 * kPacketTimeMs); - EXPECT_CALL(*receiver_, IncomingData(_, _)) + EXPECT_CALL(*receiver_, DeliverPacket(_, _)) .Times(2); - pipe->NetworkProcess(); + pipe->Process(); } // Test we get statistics as expected. TEST_F(FakeNetworkPipeTest, StatisticsTest) { - FakeNetworkPipe::Configuration config; - config.packet_receiver = receiver_.get(); + FakeNetworkPipe::Config config; config.queue_length = 2; config.queue_delay_ms = 20; config.link_capacity_kbps = 80; scoped_ptr pipe(new FakeNetworkPipe(config)); + pipe->SetReceiver(receiver_.get()); const int kPacketSize = 1000; const int kPacketTimeMs = PacketTimeMs(config.link_capacity_kbps, @@ -180,15 +181,15 @@ TEST_F(FakeNetworkPipeTest, StatisticsTest) { SendPackets(pipe.get(), 3, kPacketSize); TickTime::AdvanceFakeClock(3 * kPacketTimeMs + config.queue_delay_ms); - EXPECT_CALL(*receiver_, IncomingData(_, _)) + EXPECT_CALL(*receiver_, DeliverPacket(_, _)) .Times(2); - pipe->NetworkProcess(); + pipe->Process(); // Packet 1: kPacketTimeMs + config.queue_delay_ms, // packet 2: 2 * kPacketTimeMs + config.queue_delay_ms => 170 ms average. EXPECT_EQ(pipe->AverageDelay(), 170); - EXPECT_EQ(pipe->sent_packets(), 2); - EXPECT_EQ(pipe->dropped_packets(), 1); + EXPECT_EQ(pipe->sent_packets(), 2u); + EXPECT_EQ(pipe->dropped_packets(), 1u); EXPECT_EQ(pipe->PercentageLoss(), 1/3.f); } diff --git a/webrtc/test/rtp_rtcp_observer.h b/webrtc/test/rtp_rtcp_observer.h index 56c96fae7e..3b4ad7b145 100644 --- a/webrtc/test/rtp_rtcp_observer.h +++ b/webrtc/test/rtp_rtcp_observer.h @@ -49,7 +49,8 @@ class RtpRtcpObserver { } protected: - RtpRtcpObserver(unsigned int event_timeout_ms, int delay_ms) + RtpRtcpObserver(unsigned int event_timeout_ms, + const FakeNetworkPipe::Config& configuration) : lock_(CriticalSectionWrapper::CreateCriticalSection()), observation_complete_(EventWrapper::Create()), parser_(RtpHeaderParser::Create()), @@ -57,12 +58,12 @@ class RtpRtcpObserver { this, &RtpRtcpObserver::OnSendRtp, &RtpRtcpObserver::OnSendRtcp, - delay_ms), + configuration), receive_transport_(lock_.get(), this, &RtpRtcpObserver::OnReceiveRtp, &RtpRtcpObserver::OnReceiveRtcp, - delay_ms), + configuration), timeout_ms_(event_timeout_ms) {} explicit RtpRtcpObserver(unsigned int event_timeout_ms) @@ -73,12 +74,12 @@ class RtpRtcpObserver { this, &RtpRtcpObserver::OnSendRtp, &RtpRtcpObserver::OnSendRtcp, - 0), + FakeNetworkPipe::Config()), receive_transport_(lock_.get(), this, &RtpRtcpObserver::OnReceiveRtp, &RtpRtcpObserver::OnReceiveRtcp, - 0), + FakeNetworkPipe::Config()), timeout_ms_(event_timeout_ms) {} enum Action { @@ -113,8 +114,8 @@ class RtpRtcpObserver { RtpRtcpObserver* observer, PacketTransportAction on_rtp, PacketTransportAction on_rtcp, - int delay_ms) - : test::DirectTransport(delay_ms), + const FakeNetworkPipe::Config& configuration) + : test::DirectTransport(configuration), lock_(lock), observer_(observer), on_rtp_(on_rtp), diff --git a/webrtc/test/webrtc_test_common.gyp b/webrtc/test/webrtc_test_common.gyp index eae66a0440..eac8b97ec6 100644 --- a/webrtc/test/webrtc_test_common.gyp +++ b/webrtc/test/webrtc_test_common.gyp @@ -24,6 +24,8 @@ 'fake_decoder.h', 'fake_encoder.cc', 'fake_encoder.h', + 'fake_network_pipe.cc', + 'fake_network_pipe.h', 'flags.cc', 'flags.h', 'frame_generator_capturer.cc', @@ -124,4 +126,23 @@ ], }, ], + 'conditions': [ + ['include_tests==1', { + 'targets': [ + { + 'target_name': 'webrtc_test_common_unittests', + 'type': '<(gtest_target_type)', + 'dependencies': [ + 'webrtc_test_common', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/testing/gmock.gyp:gmock', + '<(webrtc_root)/test/test.gyp:test_support_main', + ], + 'sources': [ + 'fake_network_pipe_unittest.cc', + ], + }, + ], #targets + }], # include_tests + ], # conditions } diff --git a/webrtc/video/call_perf_tests.cc b/webrtc/video/call_perf_tests.cc index 49c648877c..0637ec3ee7 100644 --- a/webrtc/video/call_perf_tests.cc +++ b/webrtc/video/call_perf_tests.cc @@ -49,8 +49,8 @@ class CallPerfTest : public ::testing::Test { class SyncRtcpObserver : public test::RtpRtcpObserver { public: - explicit SyncRtcpObserver(int delay_ms) - : test::RtpRtcpObserver(kLongTimeoutMs, delay_ms), + explicit SyncRtcpObserver(const FakeNetworkPipe::Config& config) + : test::RtpRtcpObserver(kLongTimeoutMs, config), critical_section_(CriticalSectionWrapper::CreateCriticalSection()) {} virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE { @@ -119,7 +119,7 @@ class VideoRtcpAndSyncObserver : public SyncRtcpObserver, public VideoRenderer { int voe_channel, VoEVideoSync* voe_sync, SyncRtcpObserver* audio_observer) - : SyncRtcpObserver(0), + : SyncRtcpObserver(FakeNetworkPipe::Config()), clock_(clock), voe_channel_(voe_channel), voe_sync_(voe_sync), @@ -189,8 +189,9 @@ TEST_F(CallPerfTest, PlaysOutAudioAndVideoInSync) { EXPECT_EQ(0, voe_base->Init(&fake_audio_device, NULL)); int channel = voe_base->CreateChannel(); - const int kVoiceDelayMs = 500; - SyncRtcpObserver audio_observer(kVoiceDelayMs); + FakeNetworkPipe::Config net_config; + net_config.queue_delay_ms = 500; + SyncRtcpObserver audio_observer(net_config); VideoRtcpAndSyncObserver observer( Clock::GetRealTimeClock(), channel, voe_sync, &audio_observer); diff --git a/webrtc/video_engine/test/libvietest/libvietest.gypi b/webrtc/video_engine/test/libvietest/libvietest.gypi index a3ba22ec16..33d0b0f734 100644 --- a/webrtc/video_engine/test/libvietest/libvietest.gypi +++ b/webrtc/video_engine/test/libvietest/libvietest.gypi @@ -28,14 +28,12 @@ 'helpers/vie_to_file_renderer.cc', # Testbed classes - 'include/fake_network_pipe.h', 'include/tb_capture_device.h', 'include/tb_external_transport.h', 'include/tb_I420_codec.h', 'include/tb_interfaces.h', 'include/tb_video_channel.h', - 'testbed/fake_network_pipe.cc', 'testbed/tb_capture_device.cc', 'testbed/tb_external_transport.cc', 'testbed/tb_I420_codec.cc', @@ -48,23 +46,4 @@ ], }, ], - 'conditions': [ - ['include_tests==1', { - 'targets': [ - { - 'target_name': 'libvietest_unittests', - 'type': 'executable', - 'dependencies': [ - 'libvietest', - '<(DEPTH)/testing/gtest.gyp:gtest', - '<(DEPTH)/testing/gmock.gyp:gmock', - '<(webrtc_root)/test/test.gyp:test_support_main', - ], - 'sources': [ - 'testbed/fake_network_pipe_unittest.cc', - ], - }, - ], #targets - }], # include_tests - ], # conditions } diff --git a/webrtc/video_engine/test/libvietest/testbed/fake_network_pipe.cc b/webrtc/video_engine/test/libvietest/testbed/fake_network_pipe.cc deleted file mode 100644 index b1c8eb2920..0000000000 --- a/webrtc/video_engine/test/libvietest/testbed/fake_network_pipe.cc +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 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. - */ - -#include "webrtc/video_engine/test/libvietest/include/fake_network_pipe.h" - -#include -#include -#include - -#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" -#include "webrtc/system_wrappers/interface/tick_util.h" - -namespace webrtc { - -const double kPi = 3.14159265; - -static int GaussianRandom(int mean_delay_ms, int standard_deviation_ms) { - // Creating a Normal distribution variable from two independent uniform - // variables based on the Box-Muller transform. - double uniform1 = (rand() + 1.0) / (RAND_MAX + 1.0); // NOLINT - double uniform2 = (rand() + 1.0) / (RAND_MAX + 1.0); // NOLINT - return static_cast(mean_delay_ms + standard_deviation_ms * - sqrt(-2 * log(uniform1)) * cos(2 * kPi * uniform2)); -} - -class NetworkPacket { - public: - NetworkPacket(void* data, int length, int64_t send_time, int64_t arrival_time) - : data_(NULL), - data_length_(length), - send_time_(send_time), - arrival_time_(arrival_time) { - data_ = new uint8_t[length]; - memcpy(data_, data, length); - } - ~NetworkPacket() {} - - void ReleaseData() { - delete [] data_; - data_ = NULL; - } - uint8_t* data() const { return data_; } - int data_length() const { return data_length_; } - int64_t send_time() const { return send_time_; } - int64_t arrival_time() const { return arrival_time_; } - void IncrementArrivalTime(int64_t extra_delay) { - arrival_time_+= extra_delay; - } - - private: - // The packet data. - uint8_t* data_; - // Length of data_. - int data_length_; - // The time the packet was sent out on the network. - const int64_t send_time_; - // The time the packet should arrive at the reciver. - int64_t arrival_time_; -}; - -FakeNetworkPipe::FakeNetworkPipe( - const FakeNetworkPipe::Configuration& configuration) - : packet_receiver_(configuration.packet_receiver), - link_cs_(CriticalSectionWrapper::CreateCriticalSection()), - queue_length_(configuration.queue_length), - queue_delay_ms_(configuration.queue_delay_ms), - queue_delay_deviation_ms_(configuration.delay_standard_deviation_ms), - link_capacity_bytes_ms_(configuration.link_capacity_kbps / 8), - loss_percent_(configuration.loss_percent), - dropped_packets_(0), - sent_packets_(0), - total_packet_delay_(0) { - assert(link_capacity_bytes_ms_ > 0); - assert(packet_receiver_ != NULL); -} - -FakeNetworkPipe::~FakeNetworkPipe() { -} - -void FakeNetworkPipe::SendPacket(void* data, int data_length) { - CriticalSectionScoped cs(link_cs_.get()); - if (capacity_link_.size() >= queue_length_) { - // Too many packet on the link, drop this one. - ++dropped_packets_; - return; - } - - int64_t time_now = TickTime::MillisecondTimestamp(); - - // Delay introduced by the link capacity. - int64_t capacity_delay_ms = data_length / link_capacity_bytes_ms_; - int64_t network_start_time = time_now; - - // Check if there already are packets on the link and change network start - // time if there is. - if (capacity_link_.size() > 0) - network_start_time = capacity_link_.back()->arrival_time(); - - int64_t arrival_time = network_start_time + capacity_delay_ms; - NetworkPacket* packet = new NetworkPacket(data, data_length, time_now, - arrival_time); - capacity_link_.push(packet); -} - -float FakeNetworkPipe::PercentageLoss() { - CriticalSectionScoped cs(link_cs_.get()); - if (sent_packets_ == 0) - return 0; - - return static_cast(dropped_packets_) / - (sent_packets_ + dropped_packets_); -} - -int FakeNetworkPipe::AverageDelay() { - CriticalSectionScoped cs(link_cs_.get()); - if (sent_packets_ == 0) - return 0; - - return total_packet_delay_ / sent_packets_; -} - -void FakeNetworkPipe::NetworkProcess() { - CriticalSectionScoped cs(link_cs_.get()); - if (capacity_link_.size() == 0 && delay_link_.size() == 0) - return; - - int64_t time_now = TickTime::MillisecondTimestamp(); - - // Check the capacity link first. - while (capacity_link_.size() > 0 && - time_now >= capacity_link_.front()->arrival_time()) { - // Time to get this packet. - NetworkPacket* packet = capacity_link_.front(); - capacity_link_.pop(); - - // Add extra delay and jitter, but make sure the arrival time is not earlier - // than the last packet in the queue. - int extra_delay = GaussianRandom(queue_delay_ms_, - queue_delay_deviation_ms_); - if (delay_link_.size() > 0 && - packet->arrival_time() + extra_delay < - delay_link_.back()->arrival_time()) { - extra_delay = delay_link_.back()->arrival_time() - packet->arrival_time(); - } - packet->IncrementArrivalTime(extra_delay); - delay_link_.push(packet); - } - - // Check the extra delay queue. - while (delay_link_.size() > 0 && - time_now >= delay_link_.front()->arrival_time()) { - // Deliver this packet. - NetworkPacket* packet = delay_link_.front(); - delay_link_.pop(); - packet_receiver_->IncomingPacket(packet->data(), packet->data_length()); - ++sent_packets_; - - // |time_now| might be later than when the packet should have arrived, due - // to NetworkProcess being called too late. For stats, use the time it - // should have been on the link. - total_packet_delay_ += packet->arrival_time() - packet->send_time(); - delete packet; - } -} - -} // namespace webrtc diff --git a/webrtc/webrtc.gyp b/webrtc/webrtc.gyp index ed101b6624..e78dba6fa2 100644 --- a/webrtc/webrtc.gyp +++ b/webrtc/webrtc.gyp @@ -43,6 +43,7 @@ 'system_wrappers/source/system_wrappers_tests.gyp:*', 'test/metrics.gyp:*', 'test/test.gyp:*', + 'test/webrtc_test_common.gyp:webrtc_test_common_unittests', 'tools/tools.gyp:*', 'webrtc_tests', ],