diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index 0888263331..65c4827564 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -184,6 +184,8 @@ 'remote_bitrate_estimator/test/bwe_test_framework_unittest.cc', 'remote_bitrate_estimator/test/bwe_test_logging.cc', 'remote_bitrate_estimator/test/bwe_test_logging.h', + 'remote_bitrate_estimator/test/bwe_test.cc', + 'remote_bitrate_estimator/test/bwe_test.h', 'rtp_rtcp/source/mock/mock_rtp_payload_strategy.h', 'rtp_rtcp/source/fec_receiver_unittest.cc', 'rtp_rtcp/source/fec_test_helper.cc', diff --git a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimators_test.cc b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimators_test.cc index deeeb9013b..ed8e5c555e 100644 --- a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimators_test.cc +++ b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimators_test.cc @@ -8,444 +8,225 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "gtest/gtest.h" -#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h" #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" -#include "webrtc/system_wrappers/interface/clock.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" - -#define ENABLE_1_SENDER 1 -#define ENABLE_3_SENDERS 1 -#define ENABLE_10_SENDERS 1 -#define ENABLE_BASIC_TESTS 1 -#define ENABLE_LOSS_TESTS 0 -#define ENABLE_DELAY_TESTS 0 -#define ENABLE_JITTER_TESTS 0 -#define ENABLE_REORDER_TESTS 0 -#define ENABLE_CHOKE_TESTS 0 -#define ENABLE_MULTI_TESTS 0 - -#define ENABLE_TOF_ESTIMATOR 1 -#define ENABLE_AST_ESTIMATOR 1 - -using std::vector; +#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test.h" namespace webrtc { namespace testing { namespace bwe { -const int64_t kSimulationIntervalMs = 1000; +std::vector VideoSenderFactories(uint32_t count) { + class VideoPacketSenderFactory : public PacketSenderFactory { + public: + VideoPacketSenderFactory(float fps, uint32_t kbps, uint32_t ssrc, + float frame_offset) + : fps_(fps), + kbps_(kbps), + ssrc_(ssrc), + frame_offset_(frame_offset) { + } + virtual ~VideoPacketSenderFactory() {} + virtual PacketSender* Create() const { + return new VideoSender(NULL, fps_, kbps_, ssrc_, frame_offset_); + } + private: + float fps_; + uint32_t kbps_; + uint32_t ssrc_; + float frame_offset_; + }; -namespace stl_helpers { -template void DeleteElements(T* container) { - if (!container) return; - for (typename T::iterator it = container->begin(); it != container->end(); - ++it) { - delete *it; + static const VideoPacketSenderFactory factories[] = { + VideoPacketSenderFactory(30.00f, 150, 0x1234, 0.13f), + VideoPacketSenderFactory(15.00f, 500, 0x2345, 0.16f), + VideoPacketSenderFactory(30.00f, 1200, 0x3456, 0.26f), + VideoPacketSenderFactory(7.49f, 150, 0x4567, 0.05f), + VideoPacketSenderFactory(7.50f, 150, 0x5678, 0.15f), + VideoPacketSenderFactory(7.51f, 150, 0x6789, 0.25f), + VideoPacketSenderFactory(15.02f, 150, 0x7890, 0.27f), + VideoPacketSenderFactory(15.03f, 150, 0x8901, 0.38f), + VideoPacketSenderFactory(30.02f, 150, 0x9012, 0.39f), + VideoPacketSenderFactory(30.03f, 150, 0x0123, 0.52f) + }; + assert(count <= sizeof(factories) / sizeof(factories[0])); + + std::vector result; + for (uint32_t i = 0; i < count; ++i) { + result.push_back(&factories[i]); } - container->clear(); + return result; } -} // namespace stl_helpers -class TestedEstimator : public RemoteBitrateObserver { - public: - TestedEstimator(const std::string& debug_name, - const RemoteBitrateEstimatorFactory& factory) - : debug_name_(debug_name), - clock_(0), - stats_(), - relative_estimator_stats_(), - latest_estimate_kbps_(-1.0), - estimator_(factory.Create(this, &clock_)), - relative_estimator_(NULL) { - assert(estimator_.get()); - // Default RTT in RemoteRateControl is 200 ms ; 50 ms is more realistic. - estimator_->OnRttUpdate(50); +std::vector EstimatorConfigs() { + static const RemoteBitrateEstimatorFactory factories[] = { + RemoteBitrateEstimatorFactory(), + AbsoluteSendTimeRemoteBitrateEstimatorFactory() + }; + + std::vector result; + result.push_back(BweTestConfig::EstimatorConfig("TOF", &factories[0])); + result.push_back(BweTestConfig::EstimatorConfig("AST", &factories[1])); + return result; +} + +BweTestConfig MakeBweTestConfig(uint32_t sender_count) { + BweTestConfig result = { + VideoSenderFactories(sender_count), EstimatorConfigs() + }; + return result; +} + +INSTANTIATE_TEST_CASE_P(VideoSendersTest, BweTest, + ::testing::Values(MakeBweTestConfig(1), + MakeBweTestConfig(3))); + +TEST_P(BweTest, UnlimitedSpeed) { + VerboseLogging(false); + RunFor(10 * 60 * 1000); +} + +TEST_P(BweTest, SteadyLoss) { + LossFilter loss(this); + loss.SetLoss(20.0); + RunFor(10 * 60 * 1000); +} + +TEST_P(BweTest, IncreasingLoss1) { + LossFilter loss(this); + for (int i = 0; i < 76; ++i) { + loss.SetLoss(i); + RunFor(5000); } +} - void SetRelativeEstimator(TestedEstimator* relative_estimator) { - relative_estimator_ = relative_estimator; +TEST_P(BweTest, SteadyDelay) { + DelayFilter delay(this); + delay.SetDelay(1000); + RunFor(10 * 60 * 1000); +} + +TEST_P(BweTest, IncreasingDelay1) { + DelayFilter delay(this); + RunFor(10 * 60 * 1000); + for (int i = 0; i < 30 * 2; ++i) { + delay.SetDelay(i); + RunFor(10 * 1000); } + RunFor(10 * 60 * 1000); +} - void EatPacket(const BwePacket& packet) { - BWE_TEST_LOGGING_CONTEXT(debug_name_); - - latest_estimate_kbps_ = -1.0; - - // We're treating the send time (from previous filter) as the arrival - // time once packet reaches the estimator. - int64_t packet_time_ms = (packet.send_time_us() + 500) / 1000; - BWE_TEST_LOGGING_TIME(packet_time_ms); - - int64_t step_ms = estimator_->TimeUntilNextProcess(); - while ((clock_.TimeInMilliseconds() + step_ms) < packet_time_ms) { - clock_.AdvanceTimeMilliseconds(step_ms); - estimator_->Process(); - step_ms = estimator_->TimeUntilNextProcess(); - } - - estimator_->IncomingPacket(packet_time_ms, packet.payload_size(), - packet.header()); - clock_.AdvanceTimeMilliseconds(packet_time_ms - - clock_.TimeInMilliseconds()); - ASSERT_TRUE(packet_time_ms == clock_.TimeInMilliseconds()); +TEST_P(BweTest, IncreasingDelay2) { + DelayFilter delay(this); + RateCounterFilter counter(this); + RunFor(1 * 60 * 1000); + for (int i = 1; i < 51; ++i) { + delay.SetDelay(10.0f * i); + RunFor(10 * 1000); } + delay.SetDelay(0.0f); + RunFor(10 * 60 * 1000); +} - void CheckEstimate() { - BWE_TEST_LOGGING_CONTEXT(debug_name_); - double estimated_kbps = 0.0; - if (LatestEstimate(&estimated_kbps)) { - stats_.Push(estimated_kbps); - BWE_TEST_LOGGING_PLOT("Estimate", clock_.TimeInMilliseconds(), - estimated_kbps / 100); - double relative_estimate_kbps = 0.0; - if (relative_estimator_ && - relative_estimator_->LatestEstimate(&relative_estimate_kbps)) { - relative_estimator_stats_.Push(estimated_kbps - relative_estimate_kbps); - } - } +TEST_P(BweTest, JumpyDelay1) { + DelayFilter delay(this); + RunFor(10 * 60 * 1000); + for (int i = 1; i < 200; ++i) { + delay.SetDelay((10 * i) % 500); + RunFor(1000); + delay.SetDelay(1.0f); + RunFor(1000); } + delay.SetDelay(0.0f); + RunFor(10 * 60 * 1000); +} - void LogStats() { - BWE_TEST_LOGGING_CONTEXT(debug_name_); - BWE_TEST_LOGGING_CONTEXT("Mean"); - stats_.Log("kbps"); - if (relative_estimator_) { - BWE_TEST_LOGGING_CONTEXT("Diff"); - relative_estimator_stats_.Log("kbps"); - } +TEST_P(BweTest, SteadyJitter) { + JitterFilter jitter(this); + RateCounterFilter counter(this); + jitter.SetJitter(20); + RunFor(2 * 60 * 1000); +} + +TEST_P(BweTest, IncreasingJitter1) { + JitterFilter jitter(this); + for (int i = 0; i < 2 * 60 * 2; ++i) { + jitter.SetJitter(i); + RunFor(10 * 1000); } + RunFor(10 * 60 * 1000); +} - virtual void OnReceiveBitrateChanged(const vector& ssrcs, - unsigned int bitrate) { +TEST_P(BweTest, IncreasingJitter2) { + JitterFilter jitter(this); + RunFor(30 * 1000); + for (int i = 1; i < 51; ++i) { + jitter.SetJitter(10.0f * i); + RunFor(10 * 1000); } + jitter.SetJitter(0.0f); + RunFor(10 * 60 * 1000); +} - private: - bool LatestEstimate(double* estimate_kbps) { - if (latest_estimate_kbps_ < 0.0) { - vector ssrcs; - unsigned int bps = 0; - if (!estimator_->LatestEstimate(&ssrcs, &bps)) { - return false; - } - latest_estimate_kbps_ = bps / 1000.0; - } - *estimate_kbps = latest_estimate_kbps_; - return true; +TEST_P(BweTest, SteadyReorder) { + ReorderFilter reorder(this); + reorder.SetReorder(20.0); + RunFor(10 * 60 * 1000); +} + +TEST_P(BweTest, IncreasingReorder1) { + ReorderFilter reorder(this); + for (int i = 0; i < 76; ++i) { + reorder.SetReorder(i); + RunFor(5000); } +} - std::string debug_name_; - bool log_estimates_; - SimulatedClock clock_; - Stats stats_; - Stats relative_estimator_stats_; - double latest_estimate_kbps_; - scoped_ptr estimator_; - TestedEstimator* relative_estimator_; +TEST_P(BweTest, SteadyChoke) { + ChokeFilter choke(this); + choke.SetCapacity(140); + RunFor(10 * 60 * 1000); +} - DISALLOW_IMPLICIT_CONSTRUCTORS(TestedEstimator); -}; - -class RemoteBitrateEstimatorsTest : public ::testing::Test { - public: - RemoteBitrateEstimatorsTest() - : run_time_ms_(0), - estimators_(), - previous_packets_(), - processors_(), - video_senders_() { - } - virtual ~RemoteBitrateEstimatorsTest() { - stl_helpers::DeleteElements(&estimators_); - stl_helpers::DeleteElements(&video_senders_); +TEST_P(BweTest, IncreasingChoke1) { + ChokeFilter choke(this); + for (int i = 1200; i >= 100; i -= 100) { + choke.SetCapacity(i); + RunFor(5000); } +} - virtual void SetUp() { -#if ENABLE_TOF_ESTIMATOR - estimators_.push_back(new TestedEstimator("TOF", - RemoteBitrateEstimatorFactory())); -#endif -#if ENABLE_AST_ESTIMATOR - estimators_.push_back(new TestedEstimator("AST", - AbsoluteSendTimeRemoteBitrateEstimatorFactory())); -#endif - // Set all estimators as relative to the first one. - for (uint32_t i = 1; i < estimators_.size(); ++i) { - estimators_[i]->SetRelativeEstimator(estimators_[0]); - } +TEST_P(BweTest, IncreasingChoke2) { + ChokeFilter choke(this); + RunFor(60 * 1000); + for (int i = 1200; i >= 100; i -= 20) { + choke.SetCapacity(i); + RunFor(1000); } +} - protected: - void RunFor(int64_t time_ms) { - for (run_time_ms_ += time_ms; run_time_ms_ >= kSimulationIntervalMs; - run_time_ms_ -= kSimulationIntervalMs) { - Packets packets; - for (vector::const_iterator it = - processors_.begin(); it != processors_.end(); ++it) { - (*it)->RunFor(kSimulationIntervalMs, &packets); - } - - // Verify packets are in order between batches. - if (!packets.empty() && !previous_packets_.empty()) { - packets.splice(packets.begin(), previous_packets_, - --previous_packets_.end()); - ASSERT_TRUE(IsTimeSorted(packets)); - packets.erase(packets.begin()); - } else { - ASSERT_TRUE(IsTimeSorted(packets)); - } - - for (PacketsConstIt pit = packets.begin(); pit != packets.end(); ++pit) { - for (vector::iterator eit = estimators_.begin(); - eit != estimators_.end(); ++eit) { - (*eit)->EatPacket(*pit); - } - } - - previous_packets_.swap(packets); - - for (vector::iterator eit = estimators_.begin(); - eit != estimators_.end(); ++eit) { - (*eit)->CheckEstimate(); - } - } - } - - void AddVideoSenders(uint32_t count) { - struct { float fps; uint32_t kbps; uint32_t ssrc; float frame_offset; } - configs[] = { - { 30.00f, 150, 0x1234, 0.13f }, - { 15.00f, 500, 0x2345, 0.16f }, - { 30.00f, 1200, 0x3456, 0.26f }, - { 7.49f, 150, 0x4567, 0.05f }, - { 7.50f, 150, 0x5678, 0.15f }, - { 7.51f, 150, 0x6789, 0.25f }, - { 15.02f, 150, 0x7890, 0.27f }, - { 15.03f, 150, 0x8901, 0.38f }, - { 30.02f, 150, 0x9012, 0.39f }, - { 30.03f, 150, 0x0123, 0.52f } - }; - assert(count <= sizeof(configs) / sizeof(configs[0])); - uint32_t total_capacity = 0; - for (uint32_t i = 0; i < count; ++i) { - video_senders_.push_back(new VideoSender(configs[i].fps, configs[i].kbps, - configs[i].ssrc, configs[i].frame_offset)); - processors_.push_back(video_senders_.back()); - total_capacity += configs[i].kbps; - } - BWE_TEST_LOGGING_LOG1("RequiredLinkCapacity", "%d kbps", total_capacity) - } - - void LogStats() { - for (vector::iterator eit = estimators_.begin(); - eit != estimators_.end(); ++eit) { - (*eit)->LogStats(); - } - } - - void UnlimitedSpeedTest() { - RunFor(10 * 60 * 1000); - } - - void SteadyLossTest() { - LossFilter loss; - processors_.push_back(&loss); - loss.SetLoss(20.0); - RunFor(10 * 60 * 1000); - } - void IncreasingLoss1Test() { - LossFilter loss; - processors_.push_back(&loss); - for (int i = 0; i < 76; ++i) { - loss.SetLoss(i); - RunFor(5000); - } - } - - void SteadyDelayTest() { - DelayFilter delay; - processors_.push_back(&delay); - delay.SetDelay(1000); - RunFor(10 * 60 * 1000); - } - void IncreasingDelay1Test() { - DelayFilter delay; - processors_.push_back(&delay); - RunFor(10 * 60 * 1000); - for (int i = 0; i < 30 * 2; ++i) { - delay.SetDelay(i); - RunFor(10 * 1000); - } - RunFor(10 * 60 * 1000); - } - void IncreasingDelay2Test() { - DelayFilter delay; - RateCounterFilter counter; - processors_.push_back(&delay); - processors_.push_back(&counter); - RunFor(1 * 60 * 1000); - for (int i = 1; i < 51; ++i) { - delay.SetDelay(10.0f * i); - RunFor(10 * 1000); - } - delay.SetDelay(0.0f); - RunFor(10 * 60 * 1000); - } - void JumpyDelay1Test() { - DelayFilter delay; - processors_.push_back(&delay); - RunFor(10 * 60 * 1000); - for (int i = 1; i < 200; ++i) { - delay.SetDelay((10 * i) % 500); - RunFor(1000); - delay.SetDelay(1.0f); - RunFor(1000); - } - delay.SetDelay(0.0f); - RunFor(10 * 60 * 1000); - } - - void SteadyJitterTest() { - JitterFilter jitter; - RateCounterFilter counter; - processors_.push_back(&jitter); - processors_.push_back(&counter); - jitter.SetJitter(20); - RunFor(2 * 60 * 1000); - } - void IncreasingJitter1Test() { - JitterFilter jitter; - processors_.push_back(&jitter); - for (int i = 0; i < 2 * 60 * 2; ++i) { - jitter.SetJitter(i); - RunFor(10 * 1000); - } - RunFor(10 * 60 * 1000); - } - void IncreasingJitter2Test() { - JitterFilter jitter; - processors_.push_back(&jitter); - RunFor(30 * 1000); - for (int i = 1; i < 51; ++i) { - jitter.SetJitter(10.0f * i); - RunFor(10 * 1000); - } - jitter.SetJitter(0.0f); - RunFor(10 * 60 * 1000); - } - - void SteadyReorderTest() { - ReorderFilter reorder; - processors_.push_back(&reorder); - reorder.SetReorder(20.0); - RunFor(10 * 60 * 1000); - } - void IncreasingReorder1Test() { - ReorderFilter reorder; - processors_.push_back(&reorder); - for (int i = 0; i < 76; ++i) { - reorder.SetReorder(i); - RunFor(5000); - } - } - - void SteadyChokeTest() { - ChokeFilter choke; - processors_.push_back(&choke); - choke.SetCapacity(140); - RunFor(10 * 60 * 1000); - } - void IncreasingChoke1Test() { - ChokeFilter choke; - processors_.push_back(&choke); - for (int i = 1200; i >= 100; i -= 100) { - choke.SetCapacity(i); - RunFor(5000); - } - } - void IncreasingChoke2Test() { - ChokeFilter choke; - processors_.push_back(&choke); - RunFor(60 * 1000); - for (int i = 1200; i >= 100; i -= 20) { - choke.SetCapacity(i); - RunFor(1000); - } - } - - void Multi1Test() { - DelayFilter delay; - ChokeFilter choke; - RateCounterFilter counter; - processors_.push_back(&delay); - processors_.push_back(&choke); - processors_.push_back(&counter); - choke.SetCapacity(1000); - RunFor(1 * 60 * 1000); - for (int i = 1; i < 51; ++i) { - delay.SetDelay(100.0f * i); - RunFor(10 * 1000); - } - delay.SetDelay(0.0f); - RunFor(5 * 60 * 1000); - } - void Multi2Test() { - ChokeFilter choke; - JitterFilter jitter; - RateCounterFilter counter; - processors_.push_back(&choke); - processors_.push_back(&jitter); - processors_.push_back(&counter); - choke.SetCapacity(2000); - jitter.SetJitter(120); - RunFor(5 * 60 * 1000); - } - - private: - int64_t run_time_ms_; - vector estimators_; - Packets previous_packets_; - vector processors_; - vector video_senders_; - - DISALLOW_COPY_AND_ASSIGN(RemoteBitrateEstimatorsTest); -}; - -#define SINGLE_TEST(enabled, test_name, video_senders, log)\ - TEST_F(RemoteBitrateEstimatorsTest, test_name##_##video_senders##Sender) {\ - BWE_TEST_LOGGING_ENABLE(log);\ - if (enabled) {\ - BWE_TEST_LOGGING_CONTEXT(#test_name);\ - AddVideoSenders(video_senders);\ - test_name##Test();\ - LogStats();\ - }\ - } - -#define MULTI_TEST(enabled, test_name, log)\ - SINGLE_TEST((enabled) && ENABLE_1_SENDER, test_name, 1, log)\ - SINGLE_TEST((enabled) && ENABLE_3_SENDERS, test_name, 3, log)\ - SINGLE_TEST((enabled) && ENABLE_10_SENDERS, test_name, 10, log) - -MULTI_TEST(ENABLE_BASIC_TESTS, UnlimitedSpeed, true) -MULTI_TEST(ENABLE_LOSS_TESTS, SteadyLoss, true) -MULTI_TEST(ENABLE_LOSS_TESTS, IncreasingLoss1, true) -MULTI_TEST(ENABLE_DELAY_TESTS, SteadyDelay, true) -MULTI_TEST(ENABLE_DELAY_TESTS, IncreasingDelay1, true) -MULTI_TEST(ENABLE_DELAY_TESTS, IncreasingDelay2, true) -MULTI_TEST(ENABLE_DELAY_TESTS, JumpyDelay1, true) -MULTI_TEST(ENABLE_JITTER_TESTS, SteadyJitter, true) -MULTI_TEST(ENABLE_JITTER_TESTS, IncreasingJitter1, true) -MULTI_TEST(ENABLE_JITTER_TESTS, IncreasingJitter2, true) -MULTI_TEST(ENABLE_REORDER_TESTS, SteadyReorder, true) -MULTI_TEST(ENABLE_REORDER_TESTS, IncreasingReorder1, true) -MULTI_TEST(ENABLE_CHOKE_TESTS, SteadyChoke, true) -MULTI_TEST(ENABLE_CHOKE_TESTS, IncreasingChoke1, true) -MULTI_TEST(ENABLE_CHOKE_TESTS, IncreasingChoke2, true) -MULTI_TEST(ENABLE_MULTI_TESTS, Multi1, true) -MULTI_TEST(ENABLE_MULTI_TESTS, Multi2, true) +TEST_P(BweTest, Multi1) { + DelayFilter delay(this); + ChokeFilter choke(this); + RateCounterFilter counter(this); + choke.SetCapacity(1000); + RunFor(1 * 60 * 1000); + for (int i = 1; i < 51; ++i) { + delay.SetDelay(100.0f * i); + RunFor(10 * 1000); + } + RunFor(500 * 1000); + delay.SetDelay(0.0f); + RunFor(5 * 60 * 1000); +} +TEST_P(BweTest, Multi2) { + ChokeFilter choke(this); + JitterFilter jitter(this); + RateCounterFilter counter(this); + choke.SetCapacity(2000); + jitter.SetJitter(120); + RunFor(5 * 60 * 1000); +} } // namespace bwe } // namespace testing } // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/test/bwe_test.cc b/webrtc/modules/remote_bitrate_estimator/test/bwe_test.cc new file mode 100644 index 0000000000..3046a5bdf9 --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/test/bwe_test.cc @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2013 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/modules/remote_bitrate_estimator/test/bwe_test.h" + +#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h" +#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +using std::string; +using std::vector; + +namespace webrtc { +namespace testing { +namespace bwe { + +namespace stl_helpers { +template void DeleteElements(T* container) { + if (!container) return; + for (typename T::iterator it = container->begin(); it != container->end(); + ++it) { + delete *it; + } + container->clear(); +} +} // namespace stl_helpers + +class BweTest::TestedEstimator : public RemoteBitrateObserver { + public: + explicit TestedEstimator(const BweTestConfig::EstimatorConfig& config) + : debug_name_(config.debug_name), + clock_(0), + stats_(), + relative_estimator_stats_(), + latest_estimate_kbps_(-1.0), + estimator_(config.estimator_factory->Create(this, &clock_)), + relative_estimator_(NULL) { + assert(estimator_.get()); + // Default RTT in RemoteRateControl is 200 ms ; 50 ms is more realistic. + estimator_->OnRttUpdate(50); + } + + void SetRelativeEstimator(TestedEstimator* relative_estimator) { + relative_estimator_ = relative_estimator; + } + + void EatPacket(const Packet& packet) { + BWE_TEST_LOGGING_CONTEXT(debug_name_); + + latest_estimate_kbps_ = -1.0; + + // We're treating the send time (from previous filter) as the arrival + // time once packet reaches the estimator. + int64_t packet_time_ms = (packet.send_time_us() + 500) / 1000; + BWE_TEST_LOGGING_TIME(packet_time_ms); + + int64_t step_ms = estimator_->TimeUntilNextProcess(); + while ((clock_.TimeInMilliseconds() + step_ms) < packet_time_ms) { + clock_.AdvanceTimeMilliseconds(step_ms); + estimator_->Process(); + step_ms = estimator_->TimeUntilNextProcess(); + } + + estimator_->IncomingPacket(packet_time_ms, packet.payload_size(), + packet.header()); + clock_.AdvanceTimeMilliseconds(packet_time_ms - + clock_.TimeInMilliseconds()); + ASSERT_TRUE(packet_time_ms == clock_.TimeInMilliseconds()); + } + + bool CheckEstimate(PacketSender::Feedback* feedback) { + assert(feedback); + BWE_TEST_LOGGING_CONTEXT(debug_name_); + double estimated_kbps = 0.0; + if (LatestEstimate(&estimated_kbps)) { + stats_.Push(estimated_kbps); + BWE_TEST_LOGGING_PLOT("Estimate", clock_.TimeInMilliseconds(), + estimated_kbps / 100); + double relative_estimate_kbps = 0.0; + if (relative_estimator_ && + relative_estimator_->LatestEstimate(&relative_estimate_kbps)) { + relative_estimator_stats_.Push(estimated_kbps - relative_estimate_kbps); + } + feedback->estimated_kbps = estimated_kbps; + return true; + } + return false; + } + + void LogStats() { + BWE_TEST_LOGGING_CONTEXT(debug_name_); + BWE_TEST_LOGGING_CONTEXT("Mean"); + stats_.Log("kbps"); + if (relative_estimator_) { + BWE_TEST_LOGGING_CONTEXT("Diff"); + relative_estimator_stats_.Log("kbps"); + } + } + + virtual void OnReceiveBitrateChanged(const vector& ssrcs, + unsigned int bitrate) { + } + + private: + bool LatestEstimate(double* estimate_kbps) { + if (latest_estimate_kbps_ < 0.0) { + vector ssrcs; + unsigned int bps = 0; + if (!estimator_->LatestEstimate(&ssrcs, &bps)) { + return false; + } + latest_estimate_kbps_ = bps / 1000.0; + } + *estimate_kbps = latest_estimate_kbps_; + return true; + } + + string debug_name_; + bool log_estimates_; + SimulatedClock clock_; + Stats stats_; + Stats relative_estimator_stats_; + double latest_estimate_kbps_; + scoped_ptr estimator_; + TestedEstimator* relative_estimator_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(TestedEstimator); +}; + +BweTest::BweTest() + : run_time_ms_(0), + simulation_interval_ms_(-1), + previous_packets_(), + packet_senders_(), + estimators_(), + processors_() { +} + +BweTest::~BweTest() { + stl_helpers::DeleteElements(&estimators_); + stl_helpers::DeleteElements(&packet_senders_); +} + +void BweTest::SetUp() { + BWE_TEST_LOGGING_GLOBAL_CONTEXT(::testing::UnitTest::GetInstance()-> + current_test_info()->test_case_name()); + + const BweTestConfig& config = GetParam(); + + uint32_t total_capacity = 0; + for (vector::const_iterator it = + config.sender_factories.begin(); it != config.sender_factories.end(); + ++it) { + PacketSender* sender = (*it)->Create(); + assert(sender); + total_capacity += sender->GetCapacityKbps(); + packet_senders_.push_back(sender); + processors_.push_back(sender); + } + BWE_TEST_LOGGING_LOG1("RequiredLinkCapacity", "%d kbps", total_capacity) + + // Set simulation interval from first packet sender. + if (packet_senders_.size() > 0) { + simulation_interval_ms_ = packet_senders_[0]->GetFeedbackIntervalMs(); + } + + for (vector:: const_iterator it = + config.estimator_configs.begin(); it != config.estimator_configs.end(); + ++it) { + estimators_.push_back(new TestedEstimator(*it)); + } + if (estimators_.size() > 1) { + // Set all estimators as relative to the first one. + for (uint32_t i = 1; i < estimators_.size(); ++i) { + estimators_[i]->SetRelativeEstimator(estimators_[0]); + } + } + + BWE_TEST_LOGGING_GLOBAL_ENABLE(false); +} + +void BweTest::TearDown() { + BWE_TEST_LOGGING_GLOBAL_ENABLE(true); + LogStats(); + BWE_TEST_LOGGING_GLOBAL_CONTEXT(""); +} + +void BweTest::AddPacketProcessor( + PacketProcessor* processor) { + assert(processor); + processors_.push_back(processor); +} + +void BweTest::RemovePacketProcessor( + PacketProcessor* processor) { + vector::iterator it = + std::find(processors_.begin(), processors_.end(), processor); + assert(it != processors_.end()); + processors_.erase(it); +} + +void BweTest::VerboseLogging(bool enable) { + BWE_TEST_LOGGING_GLOBAL_ENABLE(enable); +} + +void BweTest::RunFor(int64_t time_ms) { + for (run_time_ms_ += time_ms; run_time_ms_ >= simulation_interval_ms_; + run_time_ms_ -= simulation_interval_ms_) { + Packets packets; + for (vector::const_iterator it = + processors_.begin(); it != processors_.end(); ++it) { + (*it)->RunFor(simulation_interval_ms_, &packets); + } + + // Verify packets are in order between batches. + if (!packets.empty() && !previous_packets_.empty()) { + packets.splice(packets.begin(), previous_packets_, + --previous_packets_.end()); + ASSERT_TRUE(IsTimeSorted(packets)); + packets.erase(packets.begin()); + } else { + ASSERT_TRUE(IsTimeSorted(packets)); + } + + for (PacketsConstIt pit = packets.begin(); pit != packets.end(); ++pit) { + for (vector::iterator eit = estimators_.begin(); + eit != estimators_.end(); ++eit) { + (*eit)->EatPacket(*pit); + } + } + + previous_packets_.swap(packets); + + for (vector::iterator eit = estimators_.begin(); + eit != estimators_.end(); ++eit) { + PacketSender::Feedback feedback = {0}; + if ((*eit)->CheckEstimate(&feedback)) { + for (vector::iterator psit = packet_senders_.begin(); + psit != packet_senders_.end(); ++psit) { + (*psit)->GiveFeedback(feedback); + } + } + } + } +} + +void BweTest::LogStats() { + for (vector::iterator eit = estimators_.begin(); + eit != estimators_.end(); ++eit) { + (*eit)->LogStats(); + } +} +} // namespace bwe +} // namespace testing +} // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/test/bwe_test.h b/webrtc/modules/remote_bitrate_estimator/test/bwe_test.h new file mode 100644 index 0000000000..0945ea6a12 --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/test/bwe_test.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2013 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 +#include +#include "gtest/gtest.h" +#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h" +#include "webrtc/system_wrappers/interface/constructor_magic.h" + +namespace webrtc { + +struct RemoteBitrateEstimatorFactory; + +namespace testing { +namespace bwe { + +struct BweTestConfig { + struct EstimatorConfig { + EstimatorConfig() : debug_name(), estimator_factory(NULL) {} + EstimatorConfig(std::string debug_name, + const RemoteBitrateEstimatorFactory* estimator_factory) + : debug_name(debug_name), + estimator_factory(estimator_factory) { + } + std::string debug_name; + const RemoteBitrateEstimatorFactory* estimator_factory; + }; + + std::vector sender_factories; + std::vector estimator_configs; +}; + +class BweTest : public ::testing::TestWithParam, + public PacketProcessorListener { + public: + BweTest(); + virtual ~BweTest(); + + virtual void SetUp(); + virtual void TearDown(); + virtual void AddPacketProcessor(PacketProcessor* processor); + virtual void RemovePacketProcessor(PacketProcessor* processor); + + protected: + void VerboseLogging(bool enable); + void RunFor(int64_t time_ms); + void LogStats(); + + private: + class TestedEstimator; + + int64_t run_time_ms_; + int64_t simulation_interval_ms_; + Packets previous_packets_; + std::vector packet_senders_; + std::vector estimators_; + std::vector processors_; + + DISALLOW_COPY_AND_ASSIGN(BweTest); +}; +} // namespace bwe +} // namespace testing +} // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.cc b/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.cc index b90a7f6554..9b64eaf02a 100644 --- a/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.cc +++ b/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.cc @@ -14,6 +14,62 @@ namespace webrtc { namespace testing { namespace bwe { +Random::Random(uint32_t seed) + : a_(0x531FDB97 ^ seed), + b_(0x6420ECA8 + seed) { +} + +float Random::Rand() { + const float kScale = 1.0f / 0xffffffff; + float result = kScale * b_; + a_ ^= b_; + b_ += a_; + return result; +} + +int Random::Gaussian(int mean, int standard_deviation) { + // Creating a Normal distribution variable from two independent uniform + // variables based on the Box-Muller transform, which is defined on the + // interval (0, 1], hence the mask+add below. + const double kPi = 3.14159265358979323846; + const double kScale = 1.0 / 0x80000000ul; + double u1 = kScale * ((a_ & 0x7ffffffful) + 1); + double u2 = kScale * ((b_ & 0x7ffffffful) + 1); + a_ ^= b_; + b_ += a_; + return static_cast(mean + standard_deviation * + std::sqrt(-2 * std::log(u1)) * std::cos(2 * kPi * u2)); +} + +Packet::Packet() + : send_time_us_(0), + payload_size_(0) { + memset(&header_, 0, sizeof(header_)); +} + +Packet::Packet(int64_t send_time_us, uint32_t payload_size, + const RTPHeader& header) + : send_time_us_(send_time_us), + payload_size_(payload_size), + header_(header) { +} + +Packet::Packet(int64_t send_time_us, uint32_t sequence_number) + : send_time_us_(send_time_us), + payload_size_(0) { + memset(&header_, 0, sizeof(header_)); + header_.sequenceNumber = sequence_number; +} + +bool Packet::operator<(const Packet& rhs) const { + return send_time_us_ < rhs.send_time_us_; +} + +void Packet::set_send_time_us(int64_t send_time_us) { + assert(send_time_us >= 0); + send_time_us_ = send_time_us; +} + bool IsTimeSorted(const Packets& packets) { PacketsConstIt last_it = packets.begin(); for (PacketsConstIt it = last_it; it != packets.end(); ++it) { @@ -24,6 +80,261 @@ bool IsTimeSorted(const Packets& packets) { } return true; } + +PacketProcessor::PacketProcessor(PacketProcessorListener* listener) + : listener_(listener) { + if (listener_) { + listener_->AddPacketProcessor(this); + } +} + +PacketProcessor::~PacketProcessor() { + if (listener_) { + listener_->RemovePacketProcessor(this); + } +} + +RateCounterFilter::RateCounterFilter(PacketProcessorListener* listener) + : PacketProcessor(listener), + kWindowSizeUs(1000000), + packets_per_second_(0), + bytes_per_second_(0), + last_accumulated_us_(0), + window_(), + pps_stats_(), + kbps_stats_() { +} + +RateCounterFilter::~RateCounterFilter() { + LogStats(); +} + +void RateCounterFilter::LogStats() { + BWE_TEST_LOGGING_CONTEXT("RateCounterFilter"); + pps_stats_.Log("pps"); + kbps_stats_.Log("kbps"); +} + +void RateCounterFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) { + assert(in_out); + for (PacketsConstIt it = in_out->begin(); it != in_out->end(); ++it) { + packets_per_second_++; + bytes_per_second_ += it->payload_size(); + last_accumulated_us_ = it->send_time_us(); + } + window_.insert(window_.end(), in_out->begin(), in_out->end()); + while (!window_.empty()) { + const Packet& packet = window_.front(); + if (packet.send_time_us() > (last_accumulated_us_ - kWindowSizeUs)) { + break; + } + assert(packets_per_second_ >= 1); + assert(bytes_per_second_ >= packet.payload_size()); + packets_per_second_--; + bytes_per_second_ -= packet.payload_size(); + window_.pop_front(); + } + pps_stats_.Push(packets_per_second_); + kbps_stats_.Push((bytes_per_second_ * 8) / 1000.0); +} + +LossFilter::LossFilter(PacketProcessorListener* listener) + : PacketProcessor(listener), + random_(0x12345678), + loss_fraction_(0.0f) { +} + +void LossFilter::SetLoss(float loss_percent) { + BWE_TEST_LOGGING_ENABLE(false); + BWE_TEST_LOGGING_LOG1("Loss", "%f%%", loss_percent); + assert(loss_percent >= 0.0f); + assert(loss_percent <= 100.0f); + loss_fraction_ = loss_percent * 0.01f; +} + +void LossFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) { + assert(in_out); + for (PacketsIt it = in_out->begin(); it != in_out->end(); ) { + if (random_.Rand() < loss_fraction_) { + it = in_out->erase(it); + } else { + ++it; + } + } +} + +DelayFilter::DelayFilter(PacketProcessorListener* listener) + : PacketProcessor(listener), + delay_us_(0), + last_send_time_us_(0) { +} + +void DelayFilter::SetDelay(int64_t delay_ms) { + BWE_TEST_LOGGING_ENABLE(false); + BWE_TEST_LOGGING_LOG1("Delay", "%d ms", static_cast(delay_ms)); + assert(delay_ms >= 0); + delay_us_ = delay_ms * 1000; +} + +void DelayFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) { + assert(in_out); + for (PacketsIt it = in_out->begin(); it != in_out->end(); ++it) { + int64_t new_send_time_us = it->send_time_us() + delay_us_; + last_send_time_us_ = std::max(last_send_time_us_, new_send_time_us); + it->set_send_time_us(last_send_time_us_); + } +} + +JitterFilter::JitterFilter(PacketProcessorListener* listener) + : PacketProcessor(listener), + random_(0x89674523), + stddev_jitter_us_(0), + last_send_time_us_(0) { +} + +void JitterFilter::SetJitter(int64_t stddev_jitter_ms) { + BWE_TEST_LOGGING_ENABLE(false); + BWE_TEST_LOGGING_LOG1("Jitter", "%d ms", + static_cast(stddev_jitter_ms)); + assert(stddev_jitter_ms >= 0); + stddev_jitter_us_ = stddev_jitter_ms * 1000; +} + +void JitterFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) { + assert(in_out); + for (PacketsIt it = in_out->begin(); it != in_out->end(); ++it) { + int64_t new_send_time_us = it->send_time_us(); + new_send_time_us += random_.Gaussian(0, stddev_jitter_us_); + last_send_time_us_ = std::max(last_send_time_us_, new_send_time_us); + it->set_send_time_us(last_send_time_us_); + } +} + +ReorderFilter::ReorderFilter(PacketProcessorListener* listener) + : PacketProcessor(listener), + random_(0x27452389), + reorder_fraction_(0.0f) { +} + +void ReorderFilter::SetReorder(float reorder_percent) { + BWE_TEST_LOGGING_ENABLE(false); + BWE_TEST_LOGGING_LOG1("Reordering", "%f%%", reorder_percent); + assert(reorder_percent >= 0.0f); + assert(reorder_percent <= 100.0f); + reorder_fraction_ = reorder_percent * 0.01f; +} + +void ReorderFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) { + assert(in_out); + if (in_out->size() >= 2) { + PacketsIt last_it = in_out->begin(); + PacketsIt it = last_it; + while (++it != in_out->end()) { + if (random_.Rand() < reorder_fraction_) { + int64_t t1 = last_it->send_time_us(); + int64_t t2 = it->send_time_us(); + std::swap(*last_it, *it); + last_it->set_send_time_us(t1); + it->set_send_time_us(t2); + } + last_it = it; + } + } +} + +ChokeFilter::ChokeFilter(PacketProcessorListener* listener) + : PacketProcessor(listener), + kbps_(1200), + max_delay_us_(0), + last_send_time_us_(0) { +} + +void ChokeFilter::SetCapacity(uint32_t kbps) { + BWE_TEST_LOGGING_ENABLE(false); + BWE_TEST_LOGGING_LOG1("BitrateChoke", "%d kbps", kbps); + kbps_ = kbps; +} + +void ChokeFilter::SetMaxDelay(int64_t max_delay_ms) { + BWE_TEST_LOGGING_ENABLE(false); + BWE_TEST_LOGGING_LOG1("Max Delay", "%d ms", static_cast(max_delay_ms)); + assert(max_delay_ms >= 0); + max_delay_us_ = max_delay_ms * 1000; +} + +void ChokeFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) { + assert(in_out); + for (PacketsIt it = in_out->begin(); it != in_out->end(); ) { + int64_t earliest_send_time_us = last_send_time_us_ + + (it->payload_size() * 8 * 1000 + kbps_ / 2) / kbps_; + int64_t new_send_time_us = std::max(it->send_time_us(), + earliest_send_time_us); + if (max_delay_us_ == 0 || + max_delay_us_ >= (new_send_time_us - it->send_time_us())) { + it->set_send_time_us(new_send_time_us); + last_send_time_us_ = new_send_time_us; + ++it; + } else { + it = in_out->erase(it); + } + } +} + +PacketSender::PacketSender(PacketProcessorListener* listener) + : PacketProcessor(listener) { +} + +VideoSender::VideoSender(PacketProcessorListener* listener, float fps, + uint32_t kbps, uint32_t ssrc, float first_frame_offset) + : PacketSender(listener), + kMaxPayloadSizeBytes(1000), + kTimestampBase(0xff80ff00ul), + frame_period_ms_(1000.0 / fps), + next_frame_ms_(frame_period_ms_ * first_frame_offset), + now_ms_(0.0), + bytes_per_second_((1000 * kbps) / 8), + frame_size_bytes_(bytes_per_second_ / fps), + prototype_header_() { + assert(first_frame_offset >= 0.0f); + assert(first_frame_offset < 1.0f); + memset(&prototype_header_, 0, sizeof(prototype_header_)); + prototype_header_.ssrc = ssrc; + prototype_header_.sequenceNumber = 0xf000u; +} + +uint32_t VideoSender::GetCapacityKbps() const { + return (bytes_per_second_ * 8) / 1000; +} + +void VideoSender::RunFor(int64_t time_ms, Packets* in_out) { + assert(in_out); + now_ms_ += time_ms; + Packets newPackets; + while (now_ms_ >= next_frame_ms_) { + prototype_header_.sequenceNumber++; + prototype_header_.timestamp = kTimestampBase + + static_cast(next_frame_ms_ * 90.0); + prototype_header_.extension.absoluteSendTime = (kTimestampBase + + ((static_cast(next_frame_ms_ * (1 << 18)) + 500) / 1000)) & + 0x00fffffful; + prototype_header_.extension.transmissionTimeOffset = 0; + + // Generate new packets for this frame, all with the same timestamp, + // but the payload size is capped, so if the whole frame doesn't fit in + // one packet, we will see a number of equally sized packets followed by + // one smaller at the tail. + int64_t send_time_us = next_frame_ms_ * 1000.0; + uint32_t payload_size = frame_size_bytes_; + while (payload_size > 0) { + uint32_t size = std::min(kMaxPayloadSizeBytes, payload_size); + newPackets.push_back(Packet(send_time_us, size, prototype_header_)); + payload_size -= size; + } + + next_frame_ms_ += frame_period_ms_; + } + in_out->merge(newPackets); +} } // namespace bwe } // namespace testing } // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h b/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h index 3b340bc201..c19cb2b803 100644 --- a/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h +++ b/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h @@ -26,44 +26,6 @@ namespace webrtc { namespace testing { namespace bwe { -class Random { - public: - explicit Random(uint32_t seed) - : a_(0x531FDB97 ^ seed), - b_(0x6420ECA8 + seed) { - } - - // Return semi-random number in the interval [0.0, 1.0]. - float Rand() { - const float kScale = 1.0f / 0xffffffff; - float result = kScale * b_; - a_ ^= b_; - b_ += a_; - return result; - } - - // Normal Distribution. - int Gaussian(int mean, int standard_deviation) { - // Creating a Normal distribution variable from two independent uniform - // variables based on the Box-Muller transform, which is defined on the - // interval (0, 1], hence the mask+add below. - const double kPi = 3.14159265358979323846; - const double kScale = 1.0 / 0x80000000ul; - double u1 = kScale * ((a_ & 0x7ffffffful) + 1); - double u2 = kScale * ((b_ & 0x7ffffffful) + 1); - a_ ^= b_; - b_ += a_; - return static_cast(mean + standard_deviation * - std::sqrt(-2 * std::log(u1)) * std::cos(2 * kPi * u2)); - } - - private: - uint32_t a_; - uint32_t b_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(Random); -}; - template class Stats { public: Stats() @@ -149,36 +111,36 @@ template class Stats { T max_; }; -class BwePacket { +class Random { public: - BwePacket() - : send_time_us_(0), - payload_size_(0) { - memset(&header_, 0, sizeof(header_)); - } + explicit Random(uint32_t seed); - BwePacket(int64_t send_time_us, uint32_t payload_size, - const RTPHeader& header) - : send_time_us_(send_time_us), - payload_size_(payload_size), - header_(header) { - } + // Return pseudo random number in the interval [0.0, 1.0]. + float Rand(); - BwePacket(int64_t send_time_us, uint32_t sequence_number) - : send_time_us_(send_time_us), - payload_size_(0) { - memset(&header_, 0, sizeof(header_)); - header_.sequenceNumber = sequence_number; - } + // Normal Distribution. + int Gaussian(int mean, int standard_deviation); - bool operator<(const BwePacket& rhs) const { - return send_time_us_ < rhs.send_time_us_; - } + // TODO(solenberg): Random from histogram. + // template int Distribution(const std::vector histogram) { - void set_send_time_us(int64_t send_time_us) { - assert(send_time_us >= 0); - send_time_us_ = send_time_us; - } + private: + uint32_t a_; + uint32_t b_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(Random); +}; + +class Packet { + public: + Packet(); + Packet(int64_t send_time_us, uint32_t payload_size, + const RTPHeader& header); + Packet(int64_t send_time_us, uint32_t sequence_number); + + bool operator<(const Packet& rhs) const; + + void set_send_time_us(int64_t send_time_us); int64_t send_time_us() const { return send_time_us_; } uint32_t payload_size() const { return payload_size_; } const RTPHeader& header() const { return header_; } @@ -189,73 +151,180 @@ class BwePacket { RTPHeader header_; // Actual contents. }; -typedef std::list Packets; -typedef std::list::iterator PacketsIt; -typedef std::list::const_iterator PacketsConstIt; +typedef std::list Packets; +typedef std::list::iterator PacketsIt; +typedef std::list::const_iterator PacketsConstIt; bool IsTimeSorted(const Packets& packets); -class PacketProcessorInterface { +class PacketProcessor; + +class PacketProcessorListener { public: - virtual ~PacketProcessorInterface() {} + virtual ~PacketProcessorListener() {} + + virtual void AddPacketProcessor(PacketProcessor* processor) = 0; + virtual void RemovePacketProcessor(PacketProcessor* processor) = 0; +}; + +class PacketProcessor { + public: + explicit PacketProcessor(PacketProcessorListener* listener); + virtual ~PacketProcessor(); // Run simulation for |time_ms| micro seconds, consuming packets from, and // producing packets into in_out. The outgoing packet list must be sorted on // |send_time_us_|. The simulation time |time_ms| is optional to use. virtual void RunFor(int64_t time_ms, Packets* in_out) = 0; + + private: + PacketProcessorListener* listener_; + + DISALLOW_COPY_AND_ASSIGN(PacketProcessor); }; -class VideoSender : public PacketProcessorInterface { +class RateCounterFilter : public PacketProcessor { public: - VideoSender(float fps, uint32_t kbps, uint32_t ssrc, float first_frame_offset) - : kMaxPayloadSizeBytes(1000), - kTimestampBase(0xff80ff00ul), - frame_period_ms_(1000.0 / fps), - next_frame_ms_(frame_period_ms_ * first_frame_offset), - now_ms_(0.0), - bytes_per_second_(1000 * kbps / 8), - frame_size_bytes_(bytes_per_second_ / fps), - prototype_header_() { - assert(first_frame_offset >= 0.0f); - assert(first_frame_offset < 1.0f); - memset(&prototype_header_, 0, sizeof(prototype_header_)); - prototype_header_.ssrc = ssrc; - prototype_header_.sequenceNumber = 0xf000u; - } + explicit RateCounterFilter(PacketProcessorListener* listener); + virtual ~RateCounterFilter(); + + uint32_t packets_per_second() const { return packets_per_second_; } + uint32_t bits_per_second() const { return bytes_per_second_ * 8; } + + void LogStats(); + virtual void RunFor(int64_t time_ms, Packets* in_out); + + private: + const int64_t kWindowSizeUs; + uint32_t packets_per_second_; + uint32_t bytes_per_second_; + int64_t last_accumulated_us_; + Packets window_; + Stats pps_stats_; + Stats kbps_stats_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(RateCounterFilter); +}; + +class LossFilter : public PacketProcessor { + public: + explicit LossFilter(PacketProcessorListener* listener); + virtual ~LossFilter() {} + + void SetLoss(float loss_percent); + virtual void RunFor(int64_t time_ms, Packets* in_out); + + private: + Random random_; + float loss_fraction_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(LossFilter); +}; + +class DelayFilter : public PacketProcessor { + public: + explicit DelayFilter(PacketProcessorListener* listener); + virtual ~DelayFilter() {} + + void SetDelay(int64_t delay_ms); + virtual void RunFor(int64_t time_ms, Packets* in_out); + + private: + int64_t delay_us_; + int64_t last_send_time_us_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(DelayFilter); +}; + +class JitterFilter : public PacketProcessor { + public: + explicit JitterFilter(PacketProcessorListener* listener); + virtual ~JitterFilter() {} + + void SetJitter(int64_t stddev_jitter_ms); + virtual void RunFor(int64_t time_ms, Packets* in_out); + + private: + Random random_; + int64_t stddev_jitter_us_; + int64_t last_send_time_us_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(JitterFilter); +}; + +class ReorderFilter : public PacketProcessor { + public: + explicit ReorderFilter(PacketProcessorListener* listener); + virtual ~ReorderFilter() {} + + void SetReorder(float reorder_percent); + virtual void RunFor(int64_t time_ms, Packets* in_out); + + private: + Random random_; + float reorder_fraction_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ReorderFilter); +}; + +// Apply a bitrate choke with an infinite queue on the packet stream. +class ChokeFilter : public PacketProcessor { + public: + explicit ChokeFilter(PacketProcessorListener* listener); + virtual ~ChokeFilter() {} + + void SetCapacity(uint32_t kbps); + void SetMaxDelay(int64_t max_delay_ms); + virtual void RunFor(int64_t time_ms, Packets* in_out); + + private: + uint32_t kbps_; + int64_t max_delay_us_; + int64_t last_send_time_us_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ChokeFilter); +}; + +class PacketSender : public PacketProcessor { + public: + struct Feedback { + double estimated_kbps; + }; + + explicit PacketSender(PacketProcessorListener* listener); + virtual ~PacketSender() {} + + virtual uint32_t GetCapacityKbps() const { return 0; } + + // Call GiveFeedback() with the returned interval in milliseconds, provided + // there is a new estimate available. + virtual int64_t GetFeedbackIntervalMs() const { return 1000; } + virtual void GiveFeedback(const Feedback& feedback) {} + + private: + DISALLOW_COPY_AND_ASSIGN(PacketSender); +}; + +struct PacketSenderFactory { + PacketSenderFactory() {} + virtual ~PacketSenderFactory() {} + virtual PacketSender* Create() const = 0; +}; + +class VideoSender : public PacketSender { + public: + VideoSender(PacketProcessorListener* listener, float fps, uint32_t kbps, + uint32_t ssrc, float first_frame_offset); virtual ~VideoSender() {} uint32_t max_payload_size_bytes() const { return kMaxPayloadSizeBytes; } uint32_t bytes_per_second() const { return bytes_per_second_; } - virtual void RunFor(int64_t time_ms, Packets* in_out) { - assert(in_out); - now_ms_ += time_ms; - Packets newPackets; - while (now_ms_ >= next_frame_ms_) { - prototype_header_.sequenceNumber++; - prototype_header_.timestamp = kTimestampBase + - static_cast(next_frame_ms_ * 90.0); - prototype_header_.extension.absoluteSendTime = (kTimestampBase + - ((static_cast(next_frame_ms_ * (1 << 18)) + 500) / 1000)) & - 0x00fffffful; - prototype_header_.extension.transmissionTimeOffset = 0; + virtual uint32_t GetCapacityKbps() const; - // Generate new packets for this frame, all with the same timestamp, - // but the payload size is capped, so if the whole frame doesn't fit in - // one packet, we will see a number of equally sized packets followed by - // one smaller at the tail. - int64_t send_time_us = next_frame_ms_ * 1000.0; - uint32_t payload_size = frame_size_bytes_; - while (payload_size > 0) { - uint32_t size = std::min(kMaxPayloadSizeBytes, payload_size); - newPackets.push_back(BwePacket(send_time_us, size, prototype_header_)); - payload_size -= size; - } - - next_frame_ms_ += frame_period_ms_; - } - in_out->merge(newPackets); - } + // TODO(solenberg): void SetFrameRate(float fps); + // TODO(solenberg): void SetRate(uint32_t kbps); + virtual void RunFor(int64_t time_ms, Packets* in_out); private: const uint32_t kMaxPayloadSizeBytes; @@ -269,242 +338,6 @@ class VideoSender : public PacketProcessorInterface { DISALLOW_IMPLICIT_CONSTRUCTORS(VideoSender); }; - -class RateCounterFilter : public PacketProcessorInterface { - public: - RateCounterFilter() - : kWindowSizeUs(1000000), - packets_per_second_(0), - bytes_per_second_(0), - last_accumulated_us_(0), - window_(), - pps_stats_(), - kbps_stats_() { - } - virtual ~RateCounterFilter() { - LogStats(); - } - - uint32_t packets_per_second() const { return packets_per_second_; } - uint32_t bits_per_second() const { return bytes_per_second_ * 8; } - - void LogStats() { - BWE_TEST_LOGGING_CONTEXT("RateCounterFilter"); - pps_stats_.Log("pps"); - kbps_stats_.Log("kbps"); - } - - virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) { - assert(in_out); - for (PacketsConstIt it = in_out->begin(); it != in_out->end(); ++it) { - packets_per_second_++; - bytes_per_second_ += it->payload_size(); - last_accumulated_us_ = it->send_time_us(); - } - window_.insert(window_.end(), in_out->begin(), in_out->end()); - while (!window_.empty()) { - const BwePacket& packet = window_.front(); - if (packet.send_time_us() > (last_accumulated_us_ - kWindowSizeUs)) { - break; - } - assert(packets_per_second_ >= 1); - assert(bytes_per_second_ >= packet.payload_size()); - packets_per_second_--; - bytes_per_second_ -= packet.payload_size(); - window_.pop_front(); - } - pps_stats_.Push(packets_per_second_); - kbps_stats_.Push((bytes_per_second_ * 8) / 1000.0); - } - - private: - const int64_t kWindowSizeUs; - uint32_t packets_per_second_; - uint32_t bytes_per_second_; - int64_t last_accumulated_us_; - Packets window_; - Stats pps_stats_; - Stats kbps_stats_; - - DISALLOW_COPY_AND_ASSIGN(RateCounterFilter); -}; - -class LossFilter : public PacketProcessorInterface { - public: - LossFilter() : random_(0x12345678), loss_fraction_(0.0f) {} - virtual ~LossFilter() {} - - void SetLoss(float loss_percent) { - BWE_TEST_LOGGING_ENABLE(false); - BWE_TEST_LOGGING_LOG1("Loss", "%f%%", loss_percent); - assert(loss_percent >= 0.0f); - assert(loss_percent <= 100.0f); - loss_fraction_ = loss_percent * 0.01f; - } - - virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) { - assert(in_out); - for (PacketsIt it = in_out->begin(); it != in_out->end(); ) { - if (random_.Rand() < loss_fraction_) { - it = in_out->erase(it); - } else { - ++it; - } - } - } - - private: - Random random_; - float loss_fraction_; - - DISALLOW_COPY_AND_ASSIGN(LossFilter); -}; - -class DelayFilter : public PacketProcessorInterface { - public: - DelayFilter() : delay_us_(0), last_send_time_us_(0) {} - virtual ~DelayFilter() {} - - void SetDelay(int64_t delay_ms) { - BWE_TEST_LOGGING_ENABLE(false); - BWE_TEST_LOGGING_LOG1("Delay", "%d ms", static_cast(delay_ms)); - assert(delay_ms >= 0); - delay_us_ = delay_ms * 1000; - } - - virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) { - assert(in_out); - for (PacketsIt it = in_out->begin(); it != in_out->end(); ++it) { - int64_t new_send_time_us = it->send_time_us() + delay_us_; - last_send_time_us_ = std::max(last_send_time_us_, new_send_time_us); - it->set_send_time_us(last_send_time_us_); - } - } - - private: - int64_t delay_us_; - int64_t last_send_time_us_; - - DISALLOW_COPY_AND_ASSIGN(DelayFilter); -}; - -class JitterFilter : public PacketProcessorInterface { - public: - JitterFilter() - : random_(0x89674523), - stddev_jitter_us_(0), - last_send_time_us_(0) { - } - virtual ~JitterFilter() {} - - void SetJitter(int64_t stddev_jitter_ms) { - BWE_TEST_LOGGING_ENABLE(false); - BWE_TEST_LOGGING_LOG1("Jitter", "%d ms", - static_cast(stddev_jitter_ms)); - assert(stddev_jitter_ms >= 0); - stddev_jitter_us_ = stddev_jitter_ms * 1000; - } - - virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) { - assert(in_out); - for (PacketsIt it = in_out->begin(); it != in_out->end(); ++it) { - int64_t new_send_time_us = it->send_time_us(); - new_send_time_us += random_.Gaussian(0, stddev_jitter_us_); - last_send_time_us_ = std::max(last_send_time_us_, new_send_time_us); - it->set_send_time_us(last_send_time_us_); - } - } - - private: - Random random_; - int64_t stddev_jitter_us_; - int64_t last_send_time_us_; - - DISALLOW_COPY_AND_ASSIGN(JitterFilter); -}; - -class ReorderFilter : public PacketProcessorInterface { - public: - ReorderFilter() : random_(0x27452389), reorder_fraction_(0.0f) {} - virtual ~ReorderFilter() {} - - void SetReorder(float reorder_percent) { - BWE_TEST_LOGGING_ENABLE(false); - BWE_TEST_LOGGING_LOG1("Reordering", "%f%%", reorder_percent); - assert(reorder_percent >= 0.0f); - assert(reorder_percent <= 100.0f); - reorder_fraction_ = reorder_percent * 0.01f; - } - - virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) { - assert(in_out); - if (in_out->size() >= 2) { - PacketsIt last_it = in_out->begin(); - PacketsIt it = last_it; - while (++it != in_out->end()) { - if (random_.Rand() < reorder_fraction_) { - int64_t t1 = last_it->send_time_us(); - int64_t t2 = it->send_time_us(); - std::swap(*last_it, *it); - last_it->set_send_time_us(t1); - it->set_send_time_us(t2); - } - last_it = it; - } - } - } - - private: - Random random_; - float reorder_fraction_; - - DISALLOW_COPY_AND_ASSIGN(ReorderFilter); -}; - -// Apply a bitrate choke with an infinite queue on the packet stream. -class ChokeFilter : public PacketProcessorInterface { - public: - ChokeFilter() : kbps_(1200), max_delay_us_(0), last_send_time_us_(0) {} - virtual ~ChokeFilter() {} - - void SetCapacity(uint32_t kbps) { - BWE_TEST_LOGGING_ENABLE(false); - BWE_TEST_LOGGING_LOG1("BitrateChoke", "%d kbps", kbps); - kbps_ = kbps; - } - - void SetMaxDelay(int64_t max_delay_ms) { - BWE_TEST_LOGGING_ENABLE(false); - BWE_TEST_LOGGING_LOG1("Max Delay", "%d ms", static_cast(max_delay_ms)); - assert(max_delay_ms >= 0); - max_delay_us_ = max_delay_ms * 1000; - } - - virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) { - assert(in_out); - for (PacketsIt it = in_out->begin(); it != in_out->end(); ) { - int64_t earliest_send_time_us = last_send_time_us_ + - (it->payload_size() * 8 * 1000 + kbps_ / 2) / kbps_; - int64_t new_send_time_us = std::max(it->send_time_us(), - earliest_send_time_us); - if (max_delay_us_ == 0 || - max_delay_us_ >= (new_send_time_us - it->send_time_us())) { - it->set_send_time_us(new_send_time_us); - last_send_time_us_ = new_send_time_us; - ++it; - } else { - it = in_out->erase(it); - } - } - } - - private: - uint32_t kbps_; - int64_t max_delay_us_; - int64_t last_send_time_us_; - - DISALLOW_COPY_AND_ASSIGN(ChokeFilter); -}; } // namespace bwe } // namespace testing } // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework_unittest.cc b/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework_unittest.cc index b6a7123ccd..440044a7d2 100644 --- a/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework_unittest.cc +++ b/webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework_unittest.cc @@ -66,45 +66,45 @@ static bool IsSequenceNumberSorted(const Packets& packets) { return true; } -TEST(BweTestFramework_BwePacketTest, IsTimeSorted) { +TEST(BweTestFramework_PacketTest, IsTimeSorted) { Packets packets; // Insert some packets in order... EXPECT_TRUE(IsTimeSorted(packets)); - packets.push_back(BwePacket(100, 0)); + packets.push_back(Packet(100, 0)); EXPECT_TRUE(IsTimeSorted(packets)); - packets.push_back(BwePacket(110, 0)); + packets.push_back(Packet(110, 0)); EXPECT_TRUE(IsTimeSorted(packets)); // ...and one out-of-order... - packets.push_back(BwePacket(100, 0)); + packets.push_back(Packet(100, 0)); EXPECT_FALSE(IsTimeSorted(packets)); // ...remove the out-of-order packet, insert another in-order packet. packets.pop_back(); - packets.push_back(BwePacket(120, 0)); + packets.push_back(Packet(120, 0)); EXPECT_TRUE(IsTimeSorted(packets)); } -TEST(BweTestFramework_BwePacketTest, IsSequenceNumberSorted) { +TEST(BweTestFramework_PacketTest, IsSequenceNumberSorted) { Packets packets; // Insert some packets in order... EXPECT_TRUE(IsSequenceNumberSorted(packets)); - packets.push_back(BwePacket(0, 100)); + packets.push_back(Packet(0, 100)); EXPECT_TRUE(IsSequenceNumberSorted(packets)); - packets.push_back(BwePacket(0, 110)); + packets.push_back(Packet(0, 110)); EXPECT_TRUE(IsSequenceNumberSorted(packets)); // ...and one out-of-order... - packets.push_back(BwePacket(0, 100)); + packets.push_back(Packet(0, 100)); EXPECT_FALSE(IsSequenceNumberSorted(packets)); // ...remove the out-of-order packet, insert another in-order packet. packets.pop_back(); - packets.push_back(BwePacket(0, 120)); + packets.push_back(Packet(0, 120)); EXPECT_TRUE(IsSequenceNumberSorted(packets)); } @@ -168,167 +168,10 @@ TEST(BweTestFramework_StatsTest, MinMax) { EXPECT_EQ(3, stats.GetMax()); } -void TestVideoSender(VideoSender* sender, int64_t run_for_ms, - uint32_t expected_packets, - uint32_t expected_payload_size, - uint32_t expected_total_payload_size) { - assert(sender); - Packets packets; - sender->RunFor(run_for_ms, &packets); - ASSERT_TRUE(IsTimeSorted(packets)); - ASSERT_TRUE(IsSequenceNumberSorted(packets)); - EXPECT_EQ(expected_packets, packets.size()); - int64_t send_time_us = -1; - uint32_t total_payload_size = 0; - uint32_t absolute_send_time = 0; - uint32_t absolute_send_time_wraps = 0; - uint32_t rtp_timestamp = 0; - uint32_t rtp_timestamp_wraps = 0; - for (PacketsIt it = packets.begin(); it != packets.end(); ++it) { - EXPECT_LE(send_time_us, it->send_time_us()); - send_time_us = it->send_time_us(); - if (sender->max_payload_size_bytes() != it->payload_size()) { - EXPECT_EQ(expected_payload_size, it->payload_size()); - } - total_payload_size += it->payload_size(); - if (absolute_send_time > it->header().extension.absoluteSendTime) { - absolute_send_time_wraps++; - } - absolute_send_time = it->header().extension.absoluteSendTime; - if (rtp_timestamp > it->header().timestamp) { - rtp_timestamp_wraps++; - } - rtp_timestamp = it->header().timestamp; - } - EXPECT_EQ(expected_total_payload_size, total_payload_size); - EXPECT_GE(1u, absolute_send_time_wraps); - EXPECT_GE(1u, rtp_timestamp_wraps); -} - -TEST(BweTestFramework_VideoSenderTest, Fps1Kpbs80_1s) { - // 1 fps, 80 kbps - VideoSender sender(1.0f, 80, 0x1234, 0); - EXPECT_EQ(10000u, sender.bytes_per_second()); - // We're at 1 fps, so all packets should be generated on first call, giving 10 - // packets of each 1000 bytes, total 10000 bytes. - TestVideoSender(&sender, 1, 10, 1000, 10000); - // 999ms, should see no output here. - TestVideoSender(&sender, 998, 0, 0, 0); - // 1999ms, should get data for one more frame. - TestVideoSender(&sender, 1000, 10, 1000, 10000); - // 2000ms, one more frame. - TestVideoSender(&sender, 1, 10, 1000, 10000); - // 2999ms, should see nothing. - TestVideoSender(&sender, 999, 0, 0, 0); -} - -TEST(BweTestFramework_VideoSenderTest, Fps1Kpbs80_1s_Offset) { - // 1 fps, 80 kbps, offset 0.5 of a frame period, ==0.5s in this case. - VideoSender sender(1.0f, 80, 0x1234, 0.5f); - EXPECT_EQ(10000u, sender.bytes_per_second()); - // 499ms, no output. - TestVideoSender(&sender, 499, 0, 0, 0); - // 500ms, first frame (this is the offset we set), 10 packets of 1000 bytes. - TestVideoSender(&sender, 1, 10, 1000, 10000); - // 1499ms, nothing. - TestVideoSender(&sender, 999, 0, 0, 0); - // 1999ms, second frame. - TestVideoSender(&sender, 500, 10, 1000, 10000); - // 2499ms, nothing. - TestVideoSender(&sender, 500, 0, 0, 0); - // 2500ms, third frame. - TestVideoSender(&sender, 1, 10, 1000, 10000); - // 3499ms, nothing. - TestVideoSender(&sender, 999, 0, 0, 0); -} - -TEST(BweTestFramework_VideoSenderTest, Fps50Kpbs80_11s) { - // 50 fps, 80 kbps. - VideoSender sender(50.0f, 80, 0x1234, 0); - EXPECT_EQ(10000u, sender.bytes_per_second()); - // 9998ms, should see 500 frames, 200 byte payloads, total 100000 bytes. - TestVideoSender(&sender, 9998, 500, 200, 100000); - // 9999ms, nothing. - TestVideoSender(&sender, 1, 0, 0, 0); - // 10000ms, 501st frame as a single packet. - TestVideoSender(&sender, 1, 1, 200, 200); - // 10998ms, 49 more frames. - TestVideoSender(&sender, 998, 49, 200, 9800); - // 10999ms, nothing. - TestVideoSender(&sender, 1, 0, 0, 0); -} - -TEST(BweTestFramework_VideoSenderTest, Fps10Kpbs120_1s) { - // 20 fps, 120 kbps. - VideoSender sender(20.0f, 120, 0x1234, 0); - EXPECT_EQ(15000u, sender.bytes_per_second()); - // 498ms, 10 frames with 750 byte payloads, total 7500 bytes. - TestVideoSender(&sender, 498, 10, 750, 7500); - // 499ms, nothing. - TestVideoSender(&sender, 1, 0, 0, 0); - // 500ms, one more frame. - TestVideoSender(&sender, 1, 1, 750, 750); - // 998ms, 9 more frames. - TestVideoSender(&sender, 498, 9, 750, 6750); - // 999ms, nothing. - TestVideoSender(&sender, 1, 0, 0, 0); -} - -TEST(BweTestFramework_VideoSenderTest, Fps30Kpbs800_20s) { - // 20 fps, 820 kbps. - VideoSender sender(25.0f, 820, 0x1234, 0); - EXPECT_EQ(102500u, sender.bytes_per_second()); - // 9998ms, 250 frames. 820 kbps = 102500 bytes/s, so total should be 1025000. - // Each frame is 102500/25=4100 bytes, or 5 packets (4 @1000 bytes, 1 @100), - // so packet count should be 5*250=1250 and last packet of each frame has - // 100 bytes of payload. - TestVideoSender(&sender, 9998, 1250, 100, 1025000); - // 9999ms, nothing. - TestVideoSender(&sender, 1, 0, 0, 0); - // 19998ms, 250 more frames. - TestVideoSender(&sender, 9999, 1250, 100, 1025000); - // 19999ms, nothing. - TestVideoSender(&sender, 1, 0, 0, 0); - // 20038ms, one more frame, as described above (25fps == 40ms/frame). - TestVideoSender(&sender, 39, 5, 100, 4100); - // 20039ms, nothing. - TestVideoSender(&sender, 1, 0, 0, 0); -} - -TEST(BweTestFramework_VideoSenderTest, TestAppendInOrder) { - // 1 fps, 80 kbps, 250ms offset. - VideoSender sender1(1.0f, 80, 0x1234, 0.25f); - EXPECT_EQ(10000u, sender1.bytes_per_second()); - Packets packets; - // Generate some packets, verify they are sorted. - sender1.RunFor(999, &packets); - ASSERT_TRUE(IsTimeSorted(packets)); - ASSERT_TRUE(IsSequenceNumberSorted(packets)); - EXPECT_EQ(10u, packets.size()); - // Generate some more packets and verify they are appended to end of list. - sender1.RunFor(1000, &packets); - ASSERT_TRUE(IsTimeSorted(packets)); - ASSERT_TRUE(IsSequenceNumberSorted(packets)); - EXPECT_EQ(20u, packets.size()); - - // Another sender, 2 fps, 160 kpbs, 150ms offset - VideoSender sender2(2.0f, 160, 0x2234, 0.30f); - EXPECT_EQ(20000u, sender2.bytes_per_second()); - // Generate some packets, verify that they are merged with the packets already - // on the list. - sender2.RunFor(999, &packets); - ASSERT_TRUE(IsTimeSorted(packets)); - EXPECT_EQ(40u, packets.size()); - // Generate some more. - sender2.RunFor(1000, &packets); - ASSERT_TRUE(IsTimeSorted(packets)); - EXPECT_EQ(60u, packets.size()); -} - class BweTestFramework_RateCounterFilterTest : public ::testing::Test { public: BweTestFramework_RateCounterFilterTest() - : filter_(), + : filter_(NULL), now_ms_(0) { } virtual ~BweTestFramework_RateCounterFilterTest() {} @@ -340,7 +183,7 @@ class BweTestFramework_RateCounterFilterTest : public ::testing::Test { RTPHeader header = {0}; // "Send" a packet every 10 ms. for (int64_t i = 0; i < run_for_ms; i += 10, now_ms_ += 10) { - packets.push_back(BwePacket(now_ms_ * 1000, payload_bits / 8, header)); + packets.push_back(Packet(now_ms_ * 1000, payload_bits / 8, header)); } filter_.RunFor(run_for_ms, &packets); ASSERT_TRUE(IsTimeSorted(packets)); @@ -386,7 +229,7 @@ TEST_F(BweTestFramework_RateCounterFilterTest, Long) { } static void TestLossFilter(float loss_percent, bool zero_tolerance) { - LossFilter filter; + LossFilter filter(NULL); filter.SetLoss(loss_percent); Packets::size_type sent_packets = 0; Packets::size_type remaining_packets = 0; @@ -406,7 +249,7 @@ static void TestLossFilter(float loss_percent, bool zero_tolerance) { // Generate and process 10000 packets in different batch sizes (some empty) for (int i = 0; i < 2225; ++i) { Packets packets; - packets.insert(packets.end(), i % 10, BwePacket()); + packets.insert(packets.end(), i % 10, Packet()); sent_packets += packets.size(); filter.RunFor(0, &packets); ASSERT_TRUE(IsTimeSorted(packets)); @@ -445,7 +288,7 @@ TEST(BweTestFramework_LossFilterTest, Loss100) { class BweTestFramework_DelayFilterTest : public ::testing::Test { public: BweTestFramework_DelayFilterTest() - : filter_(), + : filter_(NULL), now_ms_(0), sequence_number_(0) { } @@ -456,7 +299,7 @@ class BweTestFramework_DelayFilterTest : public ::testing::Test { uint32_t out_packets) { Packets packets; for (uint32_t i = 0; i < in_packets; ++i) { - packets.push_back(BwePacket(now_ms_ * 1000 + (sequence_number_ >> 4), + packets.push_back(Packet(now_ms_ * 1000 + (sequence_number_ >> 4), sequence_number_)); sequence_number_++; } @@ -550,14 +393,14 @@ TEST_F(BweTestFramework_DelayFilterTest, Delay100) { } TEST_F(BweTestFramework_DelayFilterTest, JumpToZeroDelay) { - DelayFilter delay; + DelayFilter delay(NULL); Packets acc; Packets packets; // Delay a bunch of packets, accumulate them to the 'acc' list. delay.SetDelay(100.0f); for (uint32_t i = 0; i < 10; ++i) { - packets.push_back(BwePacket(i * 100, i)); + packets.push_back(Packet(i * 100, i)); } delay.RunFor(1000, &packets); acc.splice(acc.end(), packets); @@ -568,7 +411,7 @@ TEST_F(BweTestFramework_DelayFilterTest, JumpToZeroDelay) { // to the 'acc' list and verify that it is all sorted. delay.SetDelay(0.0f); for (uint32_t i = 10; i < 50; ++i) { - packets.push_back(BwePacket(i * 100, i)); + packets.push_back(Packet(i * 100, i)); } delay.RunFor(1000, &packets); acc.splice(acc.end(), packets); @@ -595,7 +438,7 @@ TEST_F(BweTestFramework_DelayFilterTest, IncreasingDelay) { } static void TestJitterFilter(int64_t stddev_jitter_ms) { - JitterFilter filter; + JitterFilter filter(NULL); filter.SetJitter(stddev_jitter_ms); int64_t now_ms = 0; @@ -607,7 +450,7 @@ static void TestJitterFilter(int64_t stddev_jitter_ms) { for (uint32_t i = 0; i < 1000; ++i) { Packets packets; for (uint32_t j = 0; j < i % 100; ++j) { - packets.push_back(BwePacket(now_ms * 1000, sequence_number++)); + packets.push_back(Packet(now_ms * 1000, sequence_number++)); now_ms += 5 * stddev_jitter_ms; } original.insert(original.end(), packets.begin(), packets.end()); @@ -664,13 +507,13 @@ static void TestReorderFilter(uint32_t reorder_percent, uint32_t near) { int64_t now_ms = 0; uint32_t sequence_number = 1; for (uint32_t i = 0; i < kPacketCount; ++i, now_ms += 10) { - packets.push_back(BwePacket(now_ms * 1000, sequence_number++)); + packets.push_back(Packet(now_ms * 1000, sequence_number++)); } ASSERT_TRUE(IsTimeSorted(packets)); ASSERT_TRUE(IsSequenceNumberSorted(packets)); // Reorder packets, verify that send times are still in order. - ReorderFilter filter; + ReorderFilter filter(NULL); filter.SetReorder(reorder_percent); filter.RunFor(now_ms, &packets); ASSERT_TRUE(IsTimeSorted(packets)); @@ -724,7 +567,7 @@ TEST(BweTestFramework_ReorderFilterTest, Reorder100) { class BweTestFramework_ChokeFilterTest : public ::testing::Test { public: BweTestFramework_ChokeFilterTest() - : filter_(), + : filter_(NULL), now_ms_(0), sequence_number_(0), output_packets_(), @@ -744,7 +587,7 @@ class BweTestFramework_ChokeFilterTest : public ::testing::Test { int64_t send_time_ms = now_ms_ + (i * run_for_ms) / packets_to_generate; header.sequenceNumber = sequence_number_++; // Payload is 1000 bits. - packets.push_back(BwePacket(send_time_ms * 1000, 125, header)); + packets.push_back(Packet(send_time_ms * 1000, 125, header)); send_times_us_.push_back(send_time_ms * 1000); } ASSERT_TRUE(IsTimeSorted(packets)); @@ -757,7 +600,7 @@ class BweTestFramework_ChokeFilterTest : public ::testing::Test { // Sum up the transmitted bytes up until the current time. uint32_t bytes_transmitted = 0; while (!output_packets_.empty()) { - const BwePacket& packet = output_packets_.front(); + const Packet& packet = output_packets_.front(); if (packet.send_time_us() > now_ms_ * 1000) { break; } @@ -770,7 +613,7 @@ class BweTestFramework_ChokeFilterTest : public ::testing::Test { void CheckMaxDelay(int64_t max_delay_ms) { for (PacketsIt it = output_packets_.begin(); it != output_packets_.end(); ++it) { - const BwePacket& packet = *it; + const Packet& packet = *it; int64_t delay_us = packet.send_time_us() - send_times_us_[packet.header().sequenceNumber]; EXPECT_GE(max_delay_ms * 1000, delay_us); @@ -853,6 +696,163 @@ TEST_F(BweTestFramework_ChokeFilterTest, MaxDelay) { TestChoke(100, 100, 2); TestChoke(9900, 0, 98); } + +void TestVideoSender(VideoSender* sender, int64_t run_for_ms, + uint32_t expected_packets, + uint32_t expected_payload_size, + uint32_t expected_total_payload_size) { + assert(sender); + Packets packets; + sender->RunFor(run_for_ms, &packets); + ASSERT_TRUE(IsTimeSorted(packets)); + ASSERT_TRUE(IsSequenceNumberSorted(packets)); + EXPECT_EQ(expected_packets, packets.size()); + int64_t send_time_us = -1; + uint32_t total_payload_size = 0; + uint32_t absolute_send_time = 0; + uint32_t absolute_send_time_wraps = 0; + uint32_t rtp_timestamp = 0; + uint32_t rtp_timestamp_wraps = 0; + for (PacketsIt it = packets.begin(); it != packets.end(); ++it) { + EXPECT_LE(send_time_us, it->send_time_us()); + send_time_us = it->send_time_us(); + if (sender->max_payload_size_bytes() != it->payload_size()) { + EXPECT_EQ(expected_payload_size, it->payload_size()); + } + total_payload_size += it->payload_size(); + if (absolute_send_time > it->header().extension.absoluteSendTime) { + absolute_send_time_wraps++; + } + absolute_send_time = it->header().extension.absoluteSendTime; + if (rtp_timestamp > it->header().timestamp) { + rtp_timestamp_wraps++; + } + rtp_timestamp = it->header().timestamp; + } + EXPECT_EQ(expected_total_payload_size, total_payload_size); + EXPECT_GE(1u, absolute_send_time_wraps); + EXPECT_GE(1u, rtp_timestamp_wraps); +} + +TEST(BweTestFramework_VideoSenderTest, Fps1Kpbs80_1s) { + // 1 fps, 80 kbps + VideoSender sender(NULL, 1.0f, 80, 0x1234, 0); + EXPECT_EQ(10000u, sender.bytes_per_second()); + // We're at 1 fps, so all packets should be generated on first call, giving 10 + // packets of each 1000 bytes, total 10000 bytes. + TestVideoSender(&sender, 1, 10, 1000, 10000); + // 999ms, should see no output here. + TestVideoSender(&sender, 998, 0, 0, 0); + // 1999ms, should get data for one more frame. + TestVideoSender(&sender, 1000, 10, 1000, 10000); + // 2000ms, one more frame. + TestVideoSender(&sender, 1, 10, 1000, 10000); + // 2999ms, should see nothing. + TestVideoSender(&sender, 999, 0, 0, 0); +} + +TEST(BweTestFramework_VideoSenderTest, Fps1Kpbs80_1s_Offset) { + // 1 fps, 80 kbps, offset 0.5 of a frame period, ==0.5s in this case. + VideoSender sender(NULL, 1.0f, 80, 0x1234, 0.5f); + EXPECT_EQ(10000u, sender.bytes_per_second()); + // 499ms, no output. + TestVideoSender(&sender, 499, 0, 0, 0); + // 500ms, first frame (this is the offset we set), 10 packets of 1000 bytes. + TestVideoSender(&sender, 1, 10, 1000, 10000); + // 1499ms, nothing. + TestVideoSender(&sender, 999, 0, 0, 0); + // 1999ms, second frame. + TestVideoSender(&sender, 500, 10, 1000, 10000); + // 2499ms, nothing. + TestVideoSender(&sender, 500, 0, 0, 0); + // 2500ms, third frame. + TestVideoSender(&sender, 1, 10, 1000, 10000); + // 3499ms, nothing. + TestVideoSender(&sender, 999, 0, 0, 0); +} + +TEST(BweTestFramework_VideoSenderTest, Fps50Kpbs80_11s) { + // 50 fps, 80 kbps. + VideoSender sender(NULL, 50.0f, 80, 0x1234, 0); + EXPECT_EQ(10000u, sender.bytes_per_second()); + // 9998ms, should see 500 frames, 200 byte payloads, total 100000 bytes. + TestVideoSender(&sender, 9998, 500, 200, 100000); + // 9999ms, nothing. + TestVideoSender(&sender, 1, 0, 0, 0); + // 10000ms, 501st frame as a single packet. + TestVideoSender(&sender, 1, 1, 200, 200); + // 10998ms, 49 more frames. + TestVideoSender(&sender, 998, 49, 200, 9800); + // 10999ms, nothing. + TestVideoSender(&sender, 1, 0, 0, 0); +} + +TEST(BweTestFramework_VideoSenderTest, Fps10Kpbs120_1s) { + // 20 fps, 120 kbps. + VideoSender sender(NULL, 20.0f, 120, 0x1234, 0); + EXPECT_EQ(15000u, sender.bytes_per_second()); + // 498ms, 10 frames with 750 byte payloads, total 7500 bytes. + TestVideoSender(&sender, 498, 10, 750, 7500); + // 499ms, nothing. + TestVideoSender(&sender, 1, 0, 0, 0); + // 500ms, one more frame. + TestVideoSender(&sender, 1, 1, 750, 750); + // 998ms, 9 more frames. + TestVideoSender(&sender, 498, 9, 750, 6750); + // 999ms, nothing. + TestVideoSender(&sender, 1, 0, 0, 0); +} + +TEST(BweTestFramework_VideoSenderTest, Fps30Kpbs800_20s) { + // 20 fps, 820 kbps. + VideoSender sender(NULL, 25.0f, 820, 0x1234, 0); + EXPECT_EQ(102500u, sender.bytes_per_second()); + // 9998ms, 250 frames. 820 kbps = 102500 bytes/s, so total should be 1025000. + // Each frame is 102500/25=4100 bytes, or 5 packets (4 @1000 bytes, 1 @100), + // so packet count should be 5*250=1250 and last packet of each frame has + // 100 bytes of payload. + TestVideoSender(&sender, 9998, 1250, 100, 1025000); + // 9999ms, nothing. + TestVideoSender(&sender, 1, 0, 0, 0); + // 19998ms, 250 more frames. + TestVideoSender(&sender, 9999, 1250, 100, 1025000); + // 19999ms, nothing. + TestVideoSender(&sender, 1, 0, 0, 0); + // 20038ms, one more frame, as described above (25fps == 40ms/frame). + TestVideoSender(&sender, 39, 5, 100, 4100); + // 20039ms, nothing. + TestVideoSender(&sender, 1, 0, 0, 0); +} + +TEST(BweTestFramework_VideoSenderTest, TestAppendInOrder) { + // 1 fps, 80 kbps, 250ms offset. + VideoSender sender1(NULL, 1.0f, 80, 0x1234, 0.25f); + EXPECT_EQ(10000u, sender1.bytes_per_second()); + Packets packets; + // Generate some packets, verify they are sorted. + sender1.RunFor(999, &packets); + ASSERT_TRUE(IsTimeSorted(packets)); + ASSERT_TRUE(IsSequenceNumberSorted(packets)); + EXPECT_EQ(10u, packets.size()); + // Generate some more packets and verify they are appended to end of list. + sender1.RunFor(1000, &packets); + ASSERT_TRUE(IsTimeSorted(packets)); + ASSERT_TRUE(IsSequenceNumberSorted(packets)); + EXPECT_EQ(20u, packets.size()); + + // Another sender, 2 fps, 160 kpbs, 150ms offset + VideoSender sender2(NULL, 2.0f, 160, 0x2234, 0.30f); + EXPECT_EQ(20000u, sender2.bytes_per_second()); + // Generate some packets, verify that they are merged with the packets already + // on the list. + sender2.RunFor(999, &packets); + ASSERT_TRUE(IsTimeSorted(packets)); + EXPECT_EQ(40u, packets.size()); + // Generate some more. + sender2.RunFor(1000, &packets); + ASSERT_TRUE(IsTimeSorted(packets)); + EXPECT_EQ(60u, packets.size()); +} } // namespace bwe } // namespace testing } // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.cc b/webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.cc index ae1d42b87d..1a43f09c21 100644 --- a/webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.cc +++ b/webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.cc @@ -25,20 +25,24 @@ namespace bwe { Logging Logging::g_Logging; -Logging::Context::Context(uint32_t name, int64_t timestamp_ms, bool enabled) { +static std::string ToString(uint32_t v) { const size_t kBufferSize = 16; char string_buffer[kBufferSize] = {0}; #if defined(_MSC_VER) && defined(_WIN32) - _snprintf(string_buffer, kBufferSize - 1, "%08x", name); + _snprintf(string_buffer, kBufferSize - 1, "%08x", v); #else - snprintf(string_buffer, kBufferSize, "%08x", name); + snprintf(string_buffer, kBufferSize, "%08x", v); #endif - Logging::GetInstance()->PushState(string_buffer, timestamp_ms, enabled); + return string_buffer; +} + +Logging::Context::Context(uint32_t name, int64_t timestamp_ms, bool enabled) { + Logging::GetInstance()->PushState(ToString(name), timestamp_ms, enabled); } Logging::Context::Context(const std::string& name, int64_t timestamp_ms, bool enabled) { - Logging::GetInstance()->PushState(name.c_str(), timestamp_ms, enabled); + Logging::GetInstance()->PushState(name, timestamp_ms, enabled); } Logging::Context::Context(const char* name, int64_t timestamp_ms, @@ -54,11 +58,31 @@ Logging* Logging::GetInstance() { return &g_Logging; } +void Logging::SetGlobalContext(uint32_t name) { + CriticalSectionScoped cs(crit_sect_.get()); + thread_map_[ThreadWrapper::GetThreadId()].global_state.tag = ToString(name); +} + +void Logging::SetGlobalContext(const std::string& name) { + CriticalSectionScoped cs(crit_sect_.get()); + thread_map_[ThreadWrapper::GetThreadId()].global_state.tag = name; +} + +void Logging::SetGlobalContext(const char* name) { + CriticalSectionScoped cs(crit_sect_.get()); + thread_map_[ThreadWrapper::GetThreadId()].global_state.tag = name; +} + +void Logging::SetGlobalEnable(bool enabled) { + CriticalSectionScoped cs(crit_sect_.get()); + thread_map_[ThreadWrapper::GetThreadId()].global_state.enabled = enabled; +} + void Logging::Log(const char format[], ...) { CriticalSectionScoped cs(crit_sect_.get()); ThreadMap::iterator it = thread_map_.find(ThreadWrapper::GetThreadId()); assert(it != thread_map_.end()); - const State& state = it->second.top(); + const State& state = it->second.stack.top(); if (state.enabled) { printf("%s\t", state.tag.c_str()); va_list args; @@ -73,7 +97,7 @@ void Logging::Plot(double value) { CriticalSectionScoped cs(crit_sect_.get()); ThreadMap::iterator it = thread_map_.find(ThreadWrapper::GetThreadId()); assert(it != thread_map_.end()); - const State& state = it->second.top(); + const State& state = it->second.stack.top(); if (state.enabled) { printf("PLOT\t%s\t%f\t%f\n", state.tag.c_str(), state.timestamp_ms * 0.001, value); @@ -85,37 +109,48 @@ Logging::Logging() thread_map_() { } -void Logging::PushState(const char append_to_tag[], int64_t timestamp_ms, - bool enabled) { - assert(append_to_tag); - CriticalSectionScoped cs(crit_sect_.get()); - std::stack* stack = &thread_map_[ThreadWrapper::GetThreadId()]; - if (stack->empty()) { - State new_state(append_to_tag, std::max(static_cast(0), - timestamp_ms), enabled); - stack->push(new_state); - } else { - stack->push(stack->top()); - State* state = &stack->top(); - if (state->tag != "" && std::string(append_to_tag) != "") { - state->tag.append("_"); - } - state->tag.append(append_to_tag); - state->timestamp_ms = std::max(timestamp_ms, state->timestamp_ms); - state->enabled = enabled && state->enabled; +Logging::State::State() : tag(""), timestamp_ms(0), enabled(true) {} + +Logging::State::State(const std::string& tag, int64_t timestamp_ms, + bool enabled) + : tag(tag), + timestamp_ms(timestamp_ms), + enabled(enabled) { +} + +void Logging::State::MergePrevious(const State& previous) { + if (tag == "") { + tag = previous.tag; + } else if (previous.tag != "") { + tag = previous.tag + "_" + tag; } + timestamp_ms = std::max(previous.timestamp_ms, timestamp_ms); + enabled = previous.enabled && enabled; +} + +void Logging::PushState(const std::string& append_to_tag, int64_t timestamp_ms, + bool enabled) { + CriticalSectionScoped cs(crit_sect_.get()); + State new_state(append_to_tag, timestamp_ms, enabled); + ThreadState* thread_state = &thread_map_[ThreadWrapper::GetThreadId()]; + std::stack* stack = &thread_state->stack; + if (stack->empty()) { + new_state.MergePrevious(thread_state->global_state); + } else { + new_state.MergePrevious(stack->top()); + } + stack->push(new_state); } void Logging::PopState() { CriticalSectionScoped cs(crit_sect_.get()); ThreadMap::iterator it = thread_map_.find(ThreadWrapper::GetThreadId()); assert(it != thread_map_.end()); - int64_t newest_timestamp_ms = it->second.top().timestamp_ms; - it->second.pop(); - if (it->second.empty()) { - thread_map_.erase(it); - } else { - State* state = &it->second.top(); + std::stack* stack = &it->second.stack; + int64_t newest_timestamp_ms = stack->top().timestamp_ms; + stack->pop(); + if (!stack->empty()) { + State* state = &stack->top(); // Update time so that next log/plot will use the latest time seen so far // in this call tree. state->timestamp_ms = std::max(state->timestamp_ms, newest_timestamp_ms); diff --git a/webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h b/webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h index 0ff582582c..c949763555 100644 --- a/webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h +++ b/webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h @@ -51,6 +51,15 @@ #if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE) +// Set a thread-global base logging context. This name will be prepended to all +// hierarchical contexts. +// |name| is a char*, std::string or uint32_t to name the context. +#define BWE_TEST_LOGGING_GLOBAL_CONTEXT(name) + +// Thread-globally allow/disallow logging. +// |enable| is expected to be a bool. +#define BWE_TEST_LOGGING_GLOBAL_ENABLE(enabled) + // Insert a (hierarchical) logging context. // |name| is a char*, std::string or uint32_t to name the context. #define BWE_TEST_LOGGING_CONTEXT(name) @@ -93,6 +102,16 @@ #include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" +#define BWE_TEST_LOGGING_GLOBAL_CONTEXT(name) \ + do { \ + webrtc::testing::bwe::Logging::GetInstance()->SetGlobalContext(name); \ + } while (0); + +#define BWE_TEST_LOGGING_GLOBAL_ENABLE(enabled) \ + do { \ + webrtc::testing::bwe::Logging::GetInstance()->SetGlobalEnable(enabled); \ + } while (0); + #define __BWE_TEST_LOGGING_CONTEXT_NAME(ctx, line) ctx ## line #define __BWE_TEST_LOGGING_CONTEXT_DECLARE(ctx, line, name, time, enabled) \ webrtc::testing::bwe::Logging::Context \ @@ -162,24 +181,33 @@ class Logging { }; static Logging* GetInstance(); + + void SetGlobalContext(uint32_t name); + void SetGlobalContext(const std::string& name); + void SetGlobalContext(const char* name); + void SetGlobalEnable(bool enabled); + void Log(const char format[], ...); void Plot(double value); private: struct State { - State(const char new_tag[], int64_t timestamp_ms, bool enabled) - : tag(new_tag), - timestamp_ms(timestamp_ms), - enabled(enabled) { - } + State(); + State(const std::string& new_tag, int64_t timestamp_ms, bool enabled); + void MergePrevious(const State& previous); + std::string tag; int64_t timestamp_ms; bool enabled; }; - typedef std::map > ThreadMap; + struct ThreadState { + State global_state; + std::stack stack; + }; + typedef std::map ThreadMap; Logging(); - void PushState(const char append_to_tag[], int64_t timestamp_ms, + void PushState(const std::string& append_to_tag, int64_t timestamp_ms, bool enabled); void PopState();