diff options
Diffstat (limited to 'webrtc/call')
-rw-r--r-- | webrtc/call/BUILD.gn | 1 | ||||
-rw-r--r-- | webrtc/call/bitrate_allocator.cc | 194 | ||||
-rw-r--r-- | webrtc/call/bitrate_allocator.h | 102 | ||||
-rw-r--r-- | webrtc/call/bitrate_allocator_unittest.cc | 212 | ||||
-rw-r--r-- | webrtc/call/bitrate_estimator_tests.cc | 201 | ||||
-rw-r--r-- | webrtc/call/call.cc | 209 | ||||
-rw-r--r-- | webrtc/call/call_perf_tests.cc | 208 | ||||
-rw-r--r-- | webrtc/call/call_unittest.cc | 11 | ||||
-rw-r--r-- | webrtc/call/congestion_controller.cc | 50 | ||||
-rw-r--r-- | webrtc/call/congestion_controller.h | 66 | ||||
-rw-r--r-- | webrtc/call/mock/mock_congestion_controller.h | 52 | ||||
-rw-r--r-- | webrtc/call/packet_injection_tests.cc | 12 | ||||
-rw-r--r-- | webrtc/call/rampup_tests.cc | 587 | ||||
-rw-r--r-- | webrtc/call/rampup_tests.h | 137 | ||||
-rw-r--r-- | webrtc/call/rtc_event_log.cc | 92 | ||||
-rw-r--r-- | webrtc/call/rtc_event_log.h | 5 | ||||
-rw-r--r-- | webrtc/call/rtc_event_log.proto | 62 | ||||
-rw-r--r-- | webrtc/call/rtc_event_log_unittest.cc | 237 | ||||
-rw-r--r-- | webrtc/call/webrtc_call.gypi | 1 |
19 files changed, 2006 insertions, 433 deletions
diff --git a/webrtc/call/BUILD.gn b/webrtc/call/BUILD.gn index 3abc762a77..498c724900 100644 --- a/webrtc/call/BUILD.gn +++ b/webrtc/call/BUILD.gn @@ -10,6 +10,7 @@ import("../build/webrtc.gni") source_set("call") { sources = [ + "bitrate_allocator.cc", "call.cc", "congestion_controller.cc", "transport_adapter.cc", diff --git a/webrtc/call/bitrate_allocator.cc b/webrtc/call/bitrate_allocator.cc new file mode 100644 index 0000000000..b3789d3bb6 --- /dev/null +++ b/webrtc/call/bitrate_allocator.cc @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#include "webrtc/call/bitrate_allocator.h" + +#include <algorithm> +#include <utility> + +#include "webrtc/modules/bitrate_controller/include/bitrate_controller.h" + +namespace webrtc { + +// Allow packets to be transmitted in up to 2 times max video bitrate if the +// bandwidth estimate allows it. +const int kTransmissionMaxBitrateMultiplier = 2; +const int kDefaultBitrateBps = 300000; + +BitrateAllocator::BitrateAllocator() + : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + bitrate_observers_(), + bitrate_observers_modified_(false), + enforce_min_bitrate_(true), + last_bitrate_bps_(kDefaultBitrateBps), + last_fraction_loss_(0), + last_rtt_(0) {} + +uint32_t BitrateAllocator::OnNetworkChanged(uint32_t bitrate, + uint8_t fraction_loss, + int64_t rtt) { + CriticalSectionScoped lock(crit_sect_.get()); + last_bitrate_bps_ = bitrate; + last_fraction_loss_ = fraction_loss; + last_rtt_ = rtt; + uint32_t allocated_bitrate_bps = 0; + ObserverBitrateMap allocation = AllocateBitrates(); + for (const auto& kv : allocation) { + kv.first->OnNetworkChanged(kv.second, last_fraction_loss_, last_rtt_); + allocated_bitrate_bps += kv.second; + } + return allocated_bitrate_bps; +} + +BitrateAllocator::ObserverBitrateMap BitrateAllocator::AllocateBitrates() { + if (bitrate_observers_.empty()) + return ObserverBitrateMap(); + + uint32_t sum_min_bitrates = 0; + for (const auto& observer : bitrate_observers_) + sum_min_bitrates += observer.second.min_bitrate; + if (last_bitrate_bps_ <= sum_min_bitrates) + return LowRateAllocation(last_bitrate_bps_); + else + return NormalRateAllocation(last_bitrate_bps_, sum_min_bitrates); +} + +int BitrateAllocator::AddBitrateObserver(BitrateObserver* observer, + uint32_t min_bitrate_bps, + uint32_t max_bitrate_bps) { + CriticalSectionScoped lock(crit_sect_.get()); + + BitrateObserverConfList::iterator it = + FindObserverConfigurationPair(observer); + + // Allow the max bitrate to be exceeded for FEC and retransmissions. + // TODO(holmer): We have to get rid of this hack as it makes it difficult to + // properly allocate bitrate. The allocator should instead distribute any + // extra bitrate after all streams have maxed out. + max_bitrate_bps *= kTransmissionMaxBitrateMultiplier; + if (it != bitrate_observers_.end()) { + // Update current configuration. + it->second.min_bitrate = min_bitrate_bps; + it->second.max_bitrate = max_bitrate_bps; + } else { + // Add new settings. + bitrate_observers_.push_back(BitrateObserverConfiguration( + observer, BitrateConfiguration(min_bitrate_bps, max_bitrate_bps))); + bitrate_observers_modified_ = true; + } + + ObserverBitrateMap allocation = AllocateBitrates(); + int new_observer_bitrate_bps = 0; + for (auto& kv : allocation) { + kv.first->OnNetworkChanged(kv.second, last_fraction_loss_, last_rtt_); + if (kv.first == observer) + new_observer_bitrate_bps = kv.second; + } + return new_observer_bitrate_bps; +} + +void BitrateAllocator::RemoveBitrateObserver(BitrateObserver* observer) { + CriticalSectionScoped lock(crit_sect_.get()); + BitrateObserverConfList::iterator it = + FindObserverConfigurationPair(observer); + if (it != bitrate_observers_.end()) { + bitrate_observers_.erase(it); + bitrate_observers_modified_ = true; + } +} + +void BitrateAllocator::GetMinMaxBitrateSumBps(int* min_bitrate_sum_bps, + int* max_bitrate_sum_bps) const { + *min_bitrate_sum_bps = 0; + *max_bitrate_sum_bps = 0; + + CriticalSectionScoped lock(crit_sect_.get()); + for (const auto& observer : bitrate_observers_) { + *min_bitrate_sum_bps += observer.second.min_bitrate; + *max_bitrate_sum_bps += observer.second.max_bitrate; + } +} + +BitrateAllocator::BitrateObserverConfList::iterator +BitrateAllocator::FindObserverConfigurationPair( + const BitrateObserver* observer) { + for (auto it = bitrate_observers_.begin(); it != bitrate_observers_.end(); + ++it) { + if (it->first == observer) + return it; + } + return bitrate_observers_.end(); +} + +void BitrateAllocator::EnforceMinBitrate(bool enforce_min_bitrate) { + CriticalSectionScoped lock(crit_sect_.get()); + enforce_min_bitrate_ = enforce_min_bitrate; +} + +BitrateAllocator::ObserverBitrateMap BitrateAllocator::NormalRateAllocation( + uint32_t bitrate, + uint32_t sum_min_bitrates) { + uint32_t number_of_observers = + static_cast<uint32_t>(bitrate_observers_.size()); + uint32_t bitrate_per_observer = + (bitrate - sum_min_bitrates) / number_of_observers; + // Use map to sort list based on max bitrate. + ObserverSortingMap list_max_bitrates; + for (const auto& observer : bitrate_observers_) { + list_max_bitrates.insert(std::pair<uint32_t, ObserverConfiguration>( + observer.second.max_bitrate, + ObserverConfiguration(observer.first, observer.second.min_bitrate))); + } + ObserverBitrateMap allocation; + ObserverSortingMap::iterator max_it = list_max_bitrates.begin(); + while (max_it != list_max_bitrates.end()) { + number_of_observers--; + uint32_t observer_allowance = + max_it->second.min_bitrate + bitrate_per_observer; + if (max_it->first < observer_allowance) { + // We have more than enough for this observer. + // Carry the remainder forward. + uint32_t remainder = observer_allowance - max_it->first; + if (number_of_observers != 0) { + bitrate_per_observer += remainder / number_of_observers; + } + allocation[max_it->second.observer] = max_it->first; + } else { + allocation[max_it->second.observer] = observer_allowance; + } + list_max_bitrates.erase(max_it); + // Prepare next iteration. + max_it = list_max_bitrates.begin(); + } + return allocation; +} + +BitrateAllocator::ObserverBitrateMap BitrateAllocator::LowRateAllocation( + uint32_t bitrate) { + ObserverBitrateMap allocation; + if (enforce_min_bitrate_) { + // Min bitrate to all observers. + for (const auto& observer : bitrate_observers_) + allocation[observer.first] = observer.second.min_bitrate; + } else { + // Allocate up to |min_bitrate| to one observer at a time, until + // |bitrate| is depleted. + uint32_t remainder = bitrate; + for (const auto& observer : bitrate_observers_) { + uint32_t allocated_bitrate = + std::min(remainder, observer.second.min_bitrate); + allocation[observer.first] = allocated_bitrate; + remainder -= allocated_bitrate; + } + } + return allocation; +} +} // namespace webrtc diff --git a/webrtc/call/bitrate_allocator.h b/webrtc/call/bitrate_allocator.h new file mode 100644 index 0000000000..4a3fd59d49 --- /dev/null +++ b/webrtc/call/bitrate_allocator.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + * Usage: this class will register multiple RtcpBitrateObserver's one at each + * RTCP module. It will aggregate the results and run one bandwidth estimation + * and push the result to the encoders via BitrateObserver(s). + */ + +#ifndef WEBRTC_CALL_BITRATE_ALLOCATOR_H_ +#define WEBRTC_CALL_BITRATE_ALLOCATOR_H_ + +#include <list> +#include <map> +#include <utility> + +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/thread_annotations.h" +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" + +namespace webrtc { + +class BitrateObserver; + +class BitrateAllocator { + public: + BitrateAllocator(); + + // Allocate target_bitrate across the registered BitrateObservers. + // Returns actual bitrate allocated (might be higher than target_bitrate if + // for instance EnforceMinBitrate() is enabled. + uint32_t OnNetworkChanged(uint32_t target_bitrate, + uint8_t fraction_loss, + int64_t rtt); + + // Set the start and max send bitrate used by the bandwidth management. + // + // |observer| updates bitrates if already in use. + // |min_bitrate_bps| = 0 equals no min bitrate. + // |max_bitrate_bps| = 0 equals no max bitrate. + // Returns bitrate allocated for the bitrate observer. + int AddBitrateObserver(BitrateObserver* observer, + uint32_t min_bitrate_bps, + uint32_t max_bitrate_bps); + + void RemoveBitrateObserver(BitrateObserver* observer); + + void GetMinMaxBitrateSumBps(int* min_bitrate_sum_bps, + int* max_bitrate_sum_bps) const; + + // This method controls the behavior when the available bitrate is lower than + // the minimum bitrate, or the sum of minimum bitrates. + // When true, the bitrate will never be set lower than the minimum bitrate(s). + // When false, the bitrate observers will be allocated rates up to their + // respective minimum bitrate, satisfying one observer after the other. + void EnforceMinBitrate(bool enforce_min_bitrate); + + private: + struct BitrateConfiguration { + BitrateConfiguration(uint32_t min_bitrate, uint32_t max_bitrate) + : min_bitrate(min_bitrate), max_bitrate(max_bitrate) {} + uint32_t min_bitrate; + uint32_t max_bitrate; + }; + struct ObserverConfiguration { + ObserverConfiguration(BitrateObserver* observer, uint32_t bitrate) + : observer(observer), min_bitrate(bitrate) {} + BitrateObserver* const observer; + uint32_t min_bitrate; + }; + typedef std::pair<BitrateObserver*, BitrateConfiguration> + BitrateObserverConfiguration; + typedef std::list<BitrateObserverConfiguration> BitrateObserverConfList; + typedef std::multimap<uint32_t, ObserverConfiguration> ObserverSortingMap; + typedef std::map<BitrateObserver*, int> ObserverBitrateMap; + + BitrateObserverConfList::iterator FindObserverConfigurationPair( + const BitrateObserver* observer) EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + ObserverBitrateMap AllocateBitrates() EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + ObserverBitrateMap NormalRateAllocation(uint32_t bitrate, + uint32_t sum_min_bitrates) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + ObserverBitrateMap LowRateAllocation(uint32_t bitrate) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_); + + rtc::scoped_ptr<CriticalSectionWrapper> crit_sect_; + // Stored in a list to keep track of the insertion order. + BitrateObserverConfList bitrate_observers_ GUARDED_BY(crit_sect_); + bool bitrate_observers_modified_ GUARDED_BY(crit_sect_); + bool enforce_min_bitrate_ GUARDED_BY(crit_sect_); + uint32_t last_bitrate_bps_ GUARDED_BY(crit_sect_); + uint8_t last_fraction_loss_ GUARDED_BY(crit_sect_); + int64_t last_rtt_ GUARDED_BY(crit_sect_); +}; +} // namespace webrtc +#endif // WEBRTC_CALL_BITRATE_ALLOCATOR_H_ diff --git a/webrtc/call/bitrate_allocator_unittest.cc b/webrtc/call/bitrate_allocator_unittest.cc new file mode 100644 index 0000000000..86f75a4380 --- /dev/null +++ b/webrtc/call/bitrate_allocator_unittest.cc @@ -0,0 +1,212 @@ +/* + * 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 <algorithm> +#include <vector> + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/call/bitrate_allocator.h" +#include "webrtc/modules/bitrate_controller/include/bitrate_controller.h" + +namespace webrtc { + +class TestBitrateObserver : public BitrateObserver { + public: + TestBitrateObserver() + : last_bitrate_(0), last_fraction_loss_(0), last_rtt_(0) {} + + virtual void OnNetworkChanged(uint32_t bitrate, + uint8_t fraction_loss, + int64_t rtt) { + last_bitrate_ = bitrate; + last_fraction_loss_ = fraction_loss; + last_rtt_ = rtt; + } + uint32_t last_bitrate_; + uint8_t last_fraction_loss_; + int64_t last_rtt_; +}; + +class BitrateAllocatorTest : public ::testing::Test { + protected: + BitrateAllocatorTest() : allocator_(new BitrateAllocator()) { + allocator_->OnNetworkChanged(300000u, 0, 0); + } + ~BitrateAllocatorTest() {} + + rtc::scoped_ptr<BitrateAllocator> allocator_; +}; + +TEST_F(BitrateAllocatorTest, UpdatingBitrateObserver) { + TestBitrateObserver bitrate_observer; + int start_bitrate = + allocator_->AddBitrateObserver(&bitrate_observer, 100000, 1500000); + EXPECT_EQ(300000, start_bitrate); + allocator_->OnNetworkChanged(200000, 0, 0); + EXPECT_EQ(200000u, bitrate_observer.last_bitrate_); + + // TODO(pbos): Expect capping to 1.5M instead of 3M when not boosting the max + // bitrate for FEC/retransmissions (see todo in BitrateAllocator). + allocator_->OnNetworkChanged(4000000, 0, 0); + EXPECT_EQ(3000000u, bitrate_observer.last_bitrate_); + start_bitrate = + allocator_->AddBitrateObserver(&bitrate_observer, 100000, 4000000); + EXPECT_EQ(4000000, start_bitrate); + + start_bitrate = + allocator_->AddBitrateObserver(&bitrate_observer, 100000, 1500000); + EXPECT_EQ(3000000, start_bitrate); + EXPECT_EQ(3000000u, bitrate_observer.last_bitrate_); + allocator_->OnNetworkChanged(1500000, 0, 0); + EXPECT_EQ(1500000u, bitrate_observer.last_bitrate_); +} + +TEST_F(BitrateAllocatorTest, TwoBitrateObserversOneRtcpObserver) { + TestBitrateObserver bitrate_observer_1; + TestBitrateObserver bitrate_observer_2; + int start_bitrate = + allocator_->AddBitrateObserver(&bitrate_observer_1, 100000, 300000); + EXPECT_EQ(300000, start_bitrate); + start_bitrate = + allocator_->AddBitrateObserver(&bitrate_observer_2, 200000, 300000); + EXPECT_EQ(200000, start_bitrate); + + // Test too low start bitrate, hence lower than sum of min. Min bitrates will + // be allocated to all observers. + allocator_->OnNetworkChanged(200000, 0, 50); + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_); + EXPECT_EQ(0, bitrate_observer_1.last_fraction_loss_); + EXPECT_EQ(50, bitrate_observer_1.last_rtt_); + EXPECT_EQ(200000u, bitrate_observer_2.last_bitrate_); + EXPECT_EQ(0, bitrate_observer_2.last_fraction_loss_); + EXPECT_EQ(50, bitrate_observer_2.last_rtt_); + + // Test a bitrate which should be distributed equally. + allocator_->OnNetworkChanged(500000, 0, 50); + const uint32_t kBitrateToShare = 500000 - 200000 - 100000; + EXPECT_EQ(100000u + kBitrateToShare / 2, bitrate_observer_1.last_bitrate_); + EXPECT_EQ(200000u + kBitrateToShare / 2, bitrate_observer_2.last_bitrate_); + + // Limited by 2x max bitrates since we leave room for FEC and retransmissions. + allocator_->OnNetworkChanged(1500000, 0, 50); + EXPECT_EQ(600000u, bitrate_observer_1.last_bitrate_); + EXPECT_EQ(600000u, bitrate_observer_2.last_bitrate_); +} + +class BitrateAllocatorTestNoEnforceMin : public ::testing::Test { + protected: + BitrateAllocatorTestNoEnforceMin() : allocator_(new BitrateAllocator()) { + allocator_->EnforceMinBitrate(false); + allocator_->OnNetworkChanged(300000u, 0, 0); + } + ~BitrateAllocatorTestNoEnforceMin() {} + + rtc::scoped_ptr<BitrateAllocator> allocator_; +}; + +// The following three tests verify that the EnforceMinBitrate() method works +// as intended. +TEST_F(BitrateAllocatorTestNoEnforceMin, OneBitrateObserver) { + TestBitrateObserver bitrate_observer_1; + int start_bitrate = + allocator_->AddBitrateObserver(&bitrate_observer_1, 100000, 400000); + EXPECT_EQ(300000, start_bitrate); + + // High REMB. + allocator_->OnNetworkChanged(150000, 0, 0); + EXPECT_EQ(150000u, bitrate_observer_1.last_bitrate_); + + // Low REMB. + allocator_->OnNetworkChanged(10000, 0, 0); + EXPECT_EQ(10000u, bitrate_observer_1.last_bitrate_); + + allocator_->RemoveBitrateObserver(&bitrate_observer_1); +} + +TEST_F(BitrateAllocatorTestNoEnforceMin, ThreeBitrateObservers) { + TestBitrateObserver bitrate_observer_1; + TestBitrateObserver bitrate_observer_2; + TestBitrateObserver bitrate_observer_3; + // Set up the observers with min bitrates at 100000, 200000, and 300000. + int start_bitrate = + allocator_->AddBitrateObserver(&bitrate_observer_1, 100000, 400000); + EXPECT_EQ(300000, start_bitrate); + + start_bitrate = + allocator_->AddBitrateObserver(&bitrate_observer_2, 200000, 400000); + EXPECT_EQ(200000, start_bitrate); + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_); + + start_bitrate = + allocator_->AddBitrateObserver(&bitrate_observer_3, 300000, 400000); + EXPECT_EQ(0, start_bitrate); + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_); + EXPECT_EQ(200000u, bitrate_observer_2.last_bitrate_); + + // High REMB. Make sure the controllers get a fair share of the surplus + // (i.e., what is left after each controller gets its min rate). + allocator_->OnNetworkChanged(690000, 0, 0); + // Verify that each observer gets its min rate (sum of min rates is 600000), + // and that the remaining 90000 is divided equally among the three. + uint32_t bitrate_to_share = 690000u - 100000u - 200000u - 300000u; + EXPECT_EQ(100000u + bitrate_to_share / 3, bitrate_observer_1.last_bitrate_); + EXPECT_EQ(200000u + bitrate_to_share / 3, bitrate_observer_2.last_bitrate_); + EXPECT_EQ(300000u + bitrate_to_share / 3, bitrate_observer_3.last_bitrate_); + + // High REMB, but below the sum of min bitrates. + allocator_->OnNetworkChanged(500000, 0, 0); + // Verify that the first and second observers get their min bitrates, and the + // third gets the remainder. + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_); // Min bitrate. + EXPECT_EQ(200000u, bitrate_observer_2.last_bitrate_); // Min bitrate. + EXPECT_EQ(200000u, bitrate_observer_3.last_bitrate_); // Remainder. + + // Low REMB. + allocator_->OnNetworkChanged(10000, 0, 0); + // Verify that the first observer gets all the rate, and the rest get zero. + EXPECT_EQ(10000u, bitrate_observer_1.last_bitrate_); + EXPECT_EQ(0u, bitrate_observer_2.last_bitrate_); + EXPECT_EQ(0u, bitrate_observer_3.last_bitrate_); + + allocator_->RemoveBitrateObserver(&bitrate_observer_1); + allocator_->RemoveBitrateObserver(&bitrate_observer_2); + allocator_->RemoveBitrateObserver(&bitrate_observer_3); +} + +TEST_F(BitrateAllocatorTest, ThreeBitrateObserversLowRembEnforceMin) { + TestBitrateObserver bitrate_observer_1; + TestBitrateObserver bitrate_observer_2; + TestBitrateObserver bitrate_observer_3; + int start_bitrate = + allocator_->AddBitrateObserver(&bitrate_observer_1, 100000, 400000); + EXPECT_EQ(300000, start_bitrate); + + start_bitrate = + allocator_->AddBitrateObserver(&bitrate_observer_2, 200000, 400000); + EXPECT_EQ(200000, start_bitrate); + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_); + + start_bitrate = + allocator_->AddBitrateObserver(&bitrate_observer_3, 300000, 400000); + EXPECT_EQ(300000, start_bitrate); + EXPECT_EQ(100000, static_cast<int>(bitrate_observer_1.last_bitrate_)); + EXPECT_EQ(200000, static_cast<int>(bitrate_observer_2.last_bitrate_)); + + // Low REMB. Verify that all observers still get their respective min bitrate. + allocator_->OnNetworkChanged(1000, 0, 0); + EXPECT_EQ(100000u, bitrate_observer_1.last_bitrate_); // Min cap. + EXPECT_EQ(200000u, bitrate_observer_2.last_bitrate_); // Min cap. + EXPECT_EQ(300000u, bitrate_observer_3.last_bitrate_); // Min cap. + + allocator_->RemoveBitrateObserver(&bitrate_observer_1); + allocator_->RemoveBitrateObserver(&bitrate_observer_2); + allocator_->RemoveBitrateObserver(&bitrate_observer_3); +} +} // namespace webrtc diff --git a/webrtc/call/bitrate_estimator_tests.cc b/webrtc/call/bitrate_estimator_tests.cc index 685f3fd665..4b24bbd5ef 100644 --- a/webrtc/call/bitrate_estimator_tests.cc +++ b/webrtc/call/bitrate_estimator_tests.cc @@ -13,66 +13,54 @@ #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/audio_state.h" #include "webrtc/base/checks.h" +#include "webrtc/base/event.h" +#include "webrtc/base/logging.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/base/thread_annotations.h" #include "webrtc/call.h" #include "webrtc/system_wrappers/include/critical_section_wrapper.h" -#include "webrtc/system_wrappers/include/event_wrapper.h" #include "webrtc/system_wrappers/include/trace.h" #include "webrtc/test/call_test.h" #include "webrtc/test/direct_transport.h" #include "webrtc/test/encoder_settings.h" #include "webrtc/test/fake_decoder.h" #include "webrtc/test/fake_encoder.h" -#include "webrtc/test/fake_voice_engine.h" +#include "webrtc/test/mock_voice_engine.h" #include "webrtc/test/frame_generator_capturer.h" namespace webrtc { namespace { // Note: If you consider to re-use this class, think twice and instead consider -// writing tests that don't depend on the trace system. -class TraceObserver { +// writing tests that don't depend on the logging system. +class LogObserver { public: - TraceObserver() { - Trace::set_level_filter(kTraceTerseInfo); - - Trace::CreateTrace(); - Trace::SetTraceCallback(&callback_); - - // Call webrtc trace to initialize the tracer that would otherwise trigger a - // data-race if left to be initialized by multiple threads (i.e. threads - // spawned by test::DirectTransport members in BitrateEstimatorTest). - WEBRTC_TRACE(kTraceStateInfo, - kTraceUtility, - -1, - "Instantiate without data races."); - } + LogObserver() { rtc::LogMessage::AddLogToStream(&callback_, rtc::LS_INFO); } - ~TraceObserver() { - Trace::SetTraceCallback(nullptr); - Trace::ReturnTrace(); - } + ~LogObserver() { rtc::LogMessage::RemoveLogToStream(&callback_); } void PushExpectedLogLine(const std::string& expected_log_line) { callback_.PushExpectedLogLine(expected_log_line); } - EventTypeWrapper Wait() { - return callback_.Wait(); - } + bool Wait() { return callback_.Wait(); } private: - class Callback : public TraceCallback { + class Callback : public rtc::LogSink { public: - Callback() : done_(EventWrapper::Create()) {} + Callback() : done_(false, false) {} - void Print(TraceLevel level, const char* message, int length) override { + void OnLogMessage(const std::string& message) override { rtc::CritScope lock(&crit_sect_); - std::string msg(message); - if (msg.find("BitrateEstimator") != std::string::npos) { - received_log_lines_.push_back(msg); + // Ignore log lines that are due to missing AST extensions, these are + // logged when we switch back from AST to TOF until the wrapping bitrate + // estimator gives up on using AST. + if (message.find("BitrateEstimator") != std::string::npos && + message.find("packet is missing") == std::string::npos) { + received_log_lines_.push_back(message); } + int num_popped = 0; while (!received_log_lines_.empty() && !expected_log_lines_.empty()) { std::string a = received_log_lines_.front(); @@ -80,19 +68,17 @@ class TraceObserver { received_log_lines_.pop_front(); expected_log_lines_.pop_front(); num_popped++; - EXPECT_TRUE(a.find(b) != std::string::npos); + EXPECT_TRUE(a.find(b) != std::string::npos) << a << " != " << b; } if (expected_log_lines_.size() <= 0) { if (num_popped > 0) { - done_->Set(); + done_.Set(); } return; } } - EventTypeWrapper Wait() { - return done_->Wait(test::CallTest::kDefaultTimeoutMs); - } + bool Wait() { return done_.Wait(test::CallTest::kDefaultTimeoutMs); } void PushExpectedLogLine(const std::string& expected_log_line) { rtc::CritScope lock(&crit_sect_); @@ -104,7 +90,7 @@ class TraceObserver { rtc::CriticalSection crit_sect_; Strings received_log_lines_ GUARDED_BY(crit_sect_); Strings expected_log_lines_ GUARDED_BY(crit_sect_); - rtc::scoped_ptr<EventWrapper> done_; + rtc::Event done_; }; Callback callback_; @@ -118,13 +104,13 @@ class BitrateEstimatorTest : public test::CallTest { public: BitrateEstimatorTest() : receive_config_(nullptr) {} - virtual ~BitrateEstimatorTest() { - EXPECT_TRUE(streams_.empty()); - } + virtual ~BitrateEstimatorTest() { EXPECT_TRUE(streams_.empty()); } virtual void SetUp() { + AudioState::Config audio_state_config; + audio_state_config.voice_engine = &mock_voice_engine_; Call::Config config; - config.voice_engine = &fake_voice_engine_; + config.audio_state = AudioState::Create(audio_state_config); receiver_call_.reset(Call::Create(config)); sender_call_.reset(Call::Create(config)); @@ -133,18 +119,19 @@ class BitrateEstimatorTest : public test::CallTest { receive_transport_.reset(new test::DirectTransport(receiver_call_.get())); receive_transport_->SetReceiver(sender_call_->Receiver()); - send_config_ = VideoSendStream::Config(send_transport_.get()); - send_config_.rtp.ssrcs.push_back(kSendSsrcs[0]); + video_send_config_ = VideoSendStream::Config(send_transport_.get()); + video_send_config_.rtp.ssrcs.push_back(kVideoSendSsrcs[0]); // Encoders will be set separately per stream. - send_config_.encoder_settings.encoder = nullptr; - send_config_.encoder_settings.payload_name = "FAKE"; - send_config_.encoder_settings.payload_type = kFakeSendPayloadType; - encoder_config_.streams = test::CreateVideoStreams(1); + video_send_config_.encoder_settings.encoder = nullptr; + video_send_config_.encoder_settings.payload_name = "FAKE"; + video_send_config_.encoder_settings.payload_type = + kFakeVideoSendPayloadType; + video_encoder_config_.streams = test::CreateVideoStreams(1); receive_config_ = VideoReceiveStream::Config(receive_transport_.get()); // receive_config_.decoders will be set by every stream separately. - receive_config_.rtp.remote_ssrc = send_config_.rtp.ssrcs[0]; - receive_config_.rtp.local_ssrc = kReceiverLocalSsrc; + receive_config_.rtp.remote_ssrc = video_send_config_.rtp.ssrcs[0]; + receive_config_.rtp.local_ssrc = kReceiverLocalVideoSsrc; receive_config_.rtp.remb = true; receive_config_.rtp.extensions.push_back( RtpExtension(RtpExtension::kTOffset, kTOFExtensionId)); @@ -154,7 +141,7 @@ class BitrateEstimatorTest : public test::CallTest { virtual void TearDown() { std::for_each(streams_.begin(), streams_.end(), - std::mem_fun(&Stream::StopSending)); + std::mem_fun(&Stream::StopSending)); send_transport_->StopSending(); receive_transport_->StopSending(); @@ -165,6 +152,7 @@ class BitrateEstimatorTest : public test::CallTest { } receiver_call_.reset(); + sender_call_.reset(); } protected: @@ -181,23 +169,21 @@ class BitrateEstimatorTest : public test::CallTest { frame_generator_capturer_(), fake_encoder_(Clock::GetRealTimeClock()), fake_decoder_() { - test_->send_config_.rtp.ssrcs[0]++; - test_->send_config_.encoder_settings.encoder = &fake_encoder_; + test_->video_send_config_.rtp.ssrcs[0]++; + test_->video_send_config_.encoder_settings.encoder = &fake_encoder_; send_stream_ = test_->sender_call_->CreateVideoSendStream( - test_->send_config_, test_->encoder_config_); - RTC_DCHECK_EQ(1u, test_->encoder_config_.streams.size()); + test_->video_send_config_, test_->video_encoder_config_); + RTC_DCHECK_EQ(1u, test_->video_encoder_config_.streams.size()); frame_generator_capturer_.reset(test::FrameGeneratorCapturer::Create( - send_stream_->Input(), - test_->encoder_config_.streams[0].width, - test_->encoder_config_.streams[0].height, - 30, + send_stream_->Input(), test_->video_encoder_config_.streams[0].width, + test_->video_encoder_config_.streams[0].height, 30, Clock::GetRealTimeClock())); send_stream_->Start(); frame_generator_capturer_->Start(); if (receive_audio) { AudioReceiveStream::Config receive_config; - receive_config.rtp.remote_ssrc = test_->send_config_.rtp.ssrcs[0]; + receive_config.rtp.remote_ssrc = test_->video_send_config_.rtp.ssrcs[0]; // Bogus non-default id to prevent hitting a RTC_DCHECK when creating // the AudioReceiveStream. Every receive stream has to correspond to // an underlying channel id. @@ -211,12 +197,13 @@ class BitrateEstimatorTest : public test::CallTest { VideoReceiveStream::Decoder decoder; decoder.decoder = &fake_decoder_; decoder.payload_type = - test_->send_config_.encoder_settings.payload_type; + test_->video_send_config_.encoder_settings.payload_type; decoder.payload_name = - test_->send_config_.encoder_settings.payload_name; + test_->video_send_config_.encoder_settings.payload_name; + test_->receive_config_.decoders.clear(); test_->receive_config_.decoders.push_back(decoder); test_->receive_config_.rtp.remote_ssrc = - test_->send_config_.rtp.ssrcs[0]; + test_->video_send_config_.rtp.ssrcs[0]; test_->receive_config_.rtp.local_ssrc++; video_receive_stream_ = test_->receiver_call_->CreateVideoReceiveStream( test_->receive_config_); @@ -262,8 +249,8 @@ class BitrateEstimatorTest : public test::CallTest { test::FakeDecoder fake_decoder_; }; - test::FakeVoiceEngine fake_voice_engine_; - TraceObserver receiver_trace_; + testing::NiceMock<test::MockVoiceEngine> mock_voice_engine_; + LogObserver receiver_log_; rtc::scoped_ptr<test::DirectTransport> send_transport_; rtc::scoped_ptr<test::DirectTransport> receive_transport_; rtc::scoped_ptr<Call> sender_call_; @@ -278,89 +265,89 @@ static const char* kSingleStreamLog = "RemoteBitrateEstimatorSingleStream: Instantiating."; TEST_F(BitrateEstimatorTest, InstantiatesTOFPerDefaultForVideo) { - send_config_.rtp.extensions.push_back( + video_send_config_.rtp.extensions.push_back( RtpExtension(RtpExtension::kTOffset, kTOFExtensionId)); - receiver_trace_.PushExpectedLogLine(kSingleStreamLog); - receiver_trace_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); streams_.push_back(new Stream(this, false)); - EXPECT_EQ(kEventSignaled, receiver_trace_.Wait()); + EXPECT_TRUE(receiver_log_.Wait()); } TEST_F(BitrateEstimatorTest, ImmediatelySwitchToASTForAudio) { - send_config_.rtp.extensions.push_back( + video_send_config_.rtp.extensions.push_back( RtpExtension(RtpExtension::kAbsSendTime, kASTExtensionId)); - receiver_trace_.PushExpectedLogLine(kSingleStreamLog); - receiver_trace_.PushExpectedLogLine(kSingleStreamLog); - receiver_trace_.PushExpectedLogLine("Switching to absolute send time RBE."); - receiver_trace_.PushExpectedLogLine(kAbsSendTimeLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine("Switching to absolute send time RBE."); + receiver_log_.PushExpectedLogLine(kAbsSendTimeLog); streams_.push_back(new Stream(this, true)); - EXPECT_EQ(kEventSignaled, receiver_trace_.Wait()); + EXPECT_TRUE(receiver_log_.Wait()); } TEST_F(BitrateEstimatorTest, ImmediatelySwitchToASTForVideo) { - send_config_.rtp.extensions.push_back( + video_send_config_.rtp.extensions.push_back( RtpExtension(RtpExtension::kAbsSendTime, kASTExtensionId)); - receiver_trace_.PushExpectedLogLine(kSingleStreamLog); - receiver_trace_.PushExpectedLogLine(kSingleStreamLog); - receiver_trace_.PushExpectedLogLine("Switching to absolute send time RBE."); - receiver_trace_.PushExpectedLogLine(kAbsSendTimeLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine("Switching to absolute send time RBE."); + receiver_log_.PushExpectedLogLine(kAbsSendTimeLog); streams_.push_back(new Stream(this, false)); - EXPECT_EQ(kEventSignaled, receiver_trace_.Wait()); + EXPECT_TRUE(receiver_log_.Wait()); } TEST_F(BitrateEstimatorTest, SwitchesToASTForAudio) { - receiver_trace_.PushExpectedLogLine(kSingleStreamLog); - receiver_trace_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); streams_.push_back(new Stream(this, true)); - EXPECT_EQ(kEventSignaled, receiver_trace_.Wait()); + EXPECT_TRUE(receiver_log_.Wait()); - send_config_.rtp.extensions.push_back( + video_send_config_.rtp.extensions.push_back( RtpExtension(RtpExtension::kAbsSendTime, kASTExtensionId)); - receiver_trace_.PushExpectedLogLine("Switching to absolute send time RBE."); - receiver_trace_.PushExpectedLogLine(kAbsSendTimeLog); + receiver_log_.PushExpectedLogLine("Switching to absolute send time RBE."); + receiver_log_.PushExpectedLogLine(kAbsSendTimeLog); streams_.push_back(new Stream(this, true)); - EXPECT_EQ(kEventSignaled, receiver_trace_.Wait()); + EXPECT_TRUE(receiver_log_.Wait()); } TEST_F(BitrateEstimatorTest, SwitchesToASTForVideo) { - send_config_.rtp.extensions.push_back( + video_send_config_.rtp.extensions.push_back( RtpExtension(RtpExtension::kTOffset, kTOFExtensionId)); - receiver_trace_.PushExpectedLogLine(kSingleStreamLog); - receiver_trace_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); streams_.push_back(new Stream(this, false)); - EXPECT_EQ(kEventSignaled, receiver_trace_.Wait()); + EXPECT_TRUE(receiver_log_.Wait()); - send_config_.rtp.extensions[0] = + video_send_config_.rtp.extensions[0] = RtpExtension(RtpExtension::kAbsSendTime, kASTExtensionId); - receiver_trace_.PushExpectedLogLine("Switching to absolute send time RBE."); - receiver_trace_.PushExpectedLogLine(kAbsSendTimeLog); + receiver_log_.PushExpectedLogLine("Switching to absolute send time RBE."); + receiver_log_.PushExpectedLogLine(kAbsSendTimeLog); streams_.push_back(new Stream(this, false)); - EXPECT_EQ(kEventSignaled, receiver_trace_.Wait()); + EXPECT_TRUE(receiver_log_.Wait()); } TEST_F(BitrateEstimatorTest, SwitchesToASTThenBackToTOFForVideo) { - send_config_.rtp.extensions.push_back( + video_send_config_.rtp.extensions.push_back( RtpExtension(RtpExtension::kTOffset, kTOFExtensionId)); - receiver_trace_.PushExpectedLogLine(kSingleStreamLog); - receiver_trace_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); streams_.push_back(new Stream(this, false)); - EXPECT_EQ(kEventSignaled, receiver_trace_.Wait()); + EXPECT_TRUE(receiver_log_.Wait()); - send_config_.rtp.extensions[0] = + video_send_config_.rtp.extensions[0] = RtpExtension(RtpExtension::kAbsSendTime, kASTExtensionId); - receiver_trace_.PushExpectedLogLine("Switching to absolute send time RBE."); - receiver_trace_.PushExpectedLogLine(kAbsSendTimeLog); + receiver_log_.PushExpectedLogLine("Switching to absolute send time RBE."); + receiver_log_.PushExpectedLogLine(kAbsSendTimeLog); streams_.push_back(new Stream(this, false)); - EXPECT_EQ(kEventSignaled, receiver_trace_.Wait()); + EXPECT_TRUE(receiver_log_.Wait()); - send_config_.rtp.extensions[0] = + video_send_config_.rtp.extensions[0] = RtpExtension(RtpExtension::kTOffset, kTOFExtensionId); - receiver_trace_.PushExpectedLogLine( + receiver_log_.PushExpectedLogLine( "WrappingBitrateEstimator: Switching to transmission time offset RBE."); - receiver_trace_.PushExpectedLogLine(kSingleStreamLog); + receiver_log_.PushExpectedLogLine(kSingleStreamLog); streams_.push_back(new Stream(this, false)); streams_[0]->StopSending(); streams_[1]->StopSending(); - EXPECT_EQ(kEventSignaled, receiver_trace_.Wait()); + EXPECT_TRUE(receiver_log_.Wait()); } } // namespace webrtc diff --git a/webrtc/call/call.cc b/webrtc/call/call.cc index 594ddf5c97..5c46a48f14 100644 --- a/webrtc/call/call.cc +++ b/webrtc/call/call.cc @@ -15,27 +15,33 @@ #include "webrtc/audio/audio_receive_stream.h" #include "webrtc/audio/audio_send_stream.h" +#include "webrtc/audio/audio_state.h" +#include "webrtc/audio/scoped_voe_interface.h" #include "webrtc/base/checks.h" +#include "webrtc/base/logging.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/base/thread_annotations.h" #include "webrtc/base/thread_checker.h" #include "webrtc/base/trace_event.h" #include "webrtc/call.h" +#include "webrtc/call/bitrate_allocator.h" #include "webrtc/call/congestion_controller.h" #include "webrtc/call/rtc_event_log.h" #include "webrtc/common.h" #include "webrtc/config.h" -#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" +#include "webrtc/modules/bitrate_controller/include/bitrate_controller.h" +#include "webrtc/modules/pacing/paced_sender.h" +#include "webrtc/modules/rtp_rtcp/include/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/source/byte_io.h" -#include "webrtc/modules/utility/interface/process_thread.h" +#include "webrtc/modules/utility/include/process_thread.h" #include "webrtc/system_wrappers/include/cpu_info.h" #include "webrtc/system_wrappers/include/critical_section_wrapper.h" -#include "webrtc/system_wrappers/include/logging.h" +#include "webrtc/system_wrappers/include/metrics.h" #include "webrtc/system_wrappers/include/rw_lock_wrapper.h" #include "webrtc/system_wrappers/include/trace.h" +#include "webrtc/video/call_stats.h" #include "webrtc/video/video_receive_stream.h" #include "webrtc/video/video_send_stream.h" -#include "webrtc/video_engine/call_stats.h" #include "webrtc/voice_engine/include/voe_codec.h" namespace webrtc { @@ -44,7 +50,8 @@ const int Call::Config::kDefaultStartBitrateBps = 300000; namespace internal { -class Call : public webrtc::Call, public PacketReceiver { +class Call : public webrtc::Call, public PacketReceiver, + public BitrateObserver { public: explicit Call(const Call::Config& config); virtual ~Call(); @@ -83,6 +90,10 @@ class Call : public webrtc::Call, public PacketReceiver { void OnSentPacket(const rtc::SentPacket& sent_packet) override; + // Implements BitrateObserver. + void OnNetworkChanged(uint32_t bitrate_bps, uint8_t fraction_loss, + int64_t rtt_ms) override; + private: DeliveryStatus DeliverRtcp(MediaType media_type, const uint8_t* packet, size_t length); @@ -94,14 +105,28 @@ class Call : public webrtc::Call, public PacketReceiver { void ConfigureSync(const std::string& sync_group) EXCLUSIVE_LOCKS_REQUIRED(receive_crit_); + VoiceEngine* voice_engine() { + internal::AudioState* audio_state = + static_cast<internal::AudioState*>(config_.audio_state.get()); + if (audio_state) + return audio_state->voice_engine(); + else + return nullptr; + } + + void UpdateSendHistograms() EXCLUSIVE_LOCKS_REQUIRED(&bitrate_crit_); + void UpdateReceiveHistograms(); + + Clock* const clock_; + const int num_cpu_cores_; const rtc::scoped_ptr<ProcessThread> module_process_thread_; const rtc::scoped_ptr<CallStats> call_stats_; - const rtc::scoped_ptr<CongestionController> congestion_controller_; + const rtc::scoped_ptr<BitrateAllocator> bitrate_allocator_; Call::Config config_; rtc::ThreadChecker configuration_thread_checker_; - bool network_enabled_; + bool network_enabled_; rtc::scoped_ptr<RWLockWrapper> receive_crit_; // Audio and Video receive streams are owned by the client that creates them. @@ -123,7 +148,25 @@ class Call : public webrtc::Call, public PacketReceiver { VideoSendStream::RtpStateMap suspended_video_send_ssrcs_; RtcEventLog* event_log_ = nullptr; - VoECodec* voe_codec_ = nullptr; + + // The following members are only accessed (exclusively) from one thread and + // from the destructor, and therefore doesn't need any explicit + // synchronization. + int64_t received_video_bytes_; + int64_t received_audio_bytes_; + int64_t received_rtcp_bytes_; + int64_t first_rtp_packet_received_ms_; + int64_t last_rtp_packet_received_ms_; + int64_t first_packet_sent_ms_; + + // TODO(holmer): Remove this lock once BitrateController no longer calls + // OnNetworkChanged from multiple threads. + rtc::CriticalSection bitrate_crit_; + int64_t estimated_send_bitrate_sum_kbits_ GUARDED_BY(&bitrate_crit_); + int64_t pacer_bitrate_sum_kbits_ GUARDED_BY(&bitrate_crit_); + int64_t num_bitrate_updates_ GUARDED_BY(&bitrate_crit_); + + const rtc::scoped_ptr<CongestionController> congestion_controller_; RTC_DISALLOW_COPY_AND_ASSIGN(Call); }; @@ -136,15 +179,29 @@ Call* Call::Create(const Call::Config& config) { namespace internal { Call::Call(const Call::Config& config) - : num_cpu_cores_(CpuInfo::DetectNumberOfCores()), + : clock_(Clock::GetRealTimeClock()), + num_cpu_cores_(CpuInfo::DetectNumberOfCores()), module_process_thread_(ProcessThread::Create("ModuleProcessThread")), - call_stats_(new CallStats()), - congestion_controller_(new CongestionController( - module_process_thread_.get(), call_stats_.get())), + call_stats_(new CallStats(clock_)), + bitrate_allocator_(new BitrateAllocator()), config_(config), network_enabled_(true), receive_crit_(RWLockWrapper::CreateRWLock()), - send_crit_(RWLockWrapper::CreateRWLock()) { + send_crit_(RWLockWrapper::CreateRWLock()), + received_video_bytes_(0), + received_audio_bytes_(0), + received_rtcp_bytes_(0), + first_rtp_packet_received_ms_(-1), + last_rtp_packet_received_ms_(-1), + first_packet_sent_ms_(-1), + estimated_send_bitrate_sum_kbits_(0), + pacer_bitrate_sum_kbits_(0), + num_bitrate_updates_(0), + congestion_controller_( + new CongestionController(module_process_thread_.get(), + call_stats_.get(), + this)) { + RTC_DCHECK(configuration_thread_checker_.CalledOnValidThread()); RTC_DCHECK_GE(config.bitrate_config.min_bitrate_bps, 0); RTC_DCHECK_GE(config.bitrate_config.start_bitrate_bps, config.bitrate_config.min_bitrate_bps); @@ -152,12 +209,9 @@ Call::Call(const Call::Config& config) RTC_DCHECK_GE(config.bitrate_config.max_bitrate_bps, config.bitrate_config.start_bitrate_bps); } - if (config.voice_engine) { - // Keep a reference to VoECodec, so we're sure the VoiceEngine lives for the - // duration of the call. - voe_codec_ = VoECodec::GetInterface(config.voice_engine); - if (voe_codec_) - event_log_ = voe_codec_->GetEventLog(); + if (config.audio_state.get()) { + ScopedVoEInterface<VoECodec> voe_codec(voice_engine()); + event_log_ = voe_codec->GetEventLog(); } Trace::CreateTrace(); @@ -168,10 +222,14 @@ Call::Call(const Call::Config& config) config_.bitrate_config.min_bitrate_bps, config_.bitrate_config.start_bitrate_bps, config_.bitrate_config.max_bitrate_bps); + + congestion_controller_->GetBitrateController()->SetEventLog(event_log_); } Call::~Call() { RTC_DCHECK(configuration_thread_checker_.CalledOnValidThread()); + UpdateSendHistograms(); + UpdateReceiveHistograms(); RTC_CHECK(audio_send_ssrcs_.empty()); RTC_CHECK(video_send_ssrcs_.empty()); RTC_CHECK(video_send_streams_.empty()); @@ -182,9 +240,53 @@ Call::~Call() { module_process_thread_->DeRegisterModule(call_stats_.get()); module_process_thread_->Stop(); Trace::ReturnTrace(); +} + +void Call::UpdateSendHistograms() { + if (num_bitrate_updates_ == 0 || first_packet_sent_ms_ == -1) + return; + int64_t elapsed_sec = + (clock_->TimeInMilliseconds() - first_packet_sent_ms_) / 1000; + if (elapsed_sec < metrics::kMinRunTimeInSeconds) + return; + int send_bitrate_kbps = + estimated_send_bitrate_sum_kbits_ / num_bitrate_updates_; + int pacer_bitrate_kbps = pacer_bitrate_sum_kbits_ / num_bitrate_updates_; + if (send_bitrate_kbps > 0) { + RTC_HISTOGRAM_COUNTS_SPARSE_100000("WebRTC.Call.EstimatedSendBitrateInKbps", + send_bitrate_kbps); + } + if (pacer_bitrate_kbps > 0) { + RTC_HISTOGRAM_COUNTS_SPARSE_100000("WebRTC.Call.PacerBitrateInKbps", + pacer_bitrate_kbps); + } +} - if (voe_codec_) - voe_codec_->Release(); +void Call::UpdateReceiveHistograms() { + if (first_rtp_packet_received_ms_ == -1) + return; + int64_t elapsed_sec = + (last_rtp_packet_received_ms_ - first_rtp_packet_received_ms_) / 1000; + if (elapsed_sec < metrics::kMinRunTimeInSeconds) + return; + int audio_bitrate_kbps = received_audio_bytes_ * 8 / elapsed_sec / 1000; + int video_bitrate_kbps = received_video_bytes_ * 8 / elapsed_sec / 1000; + int rtcp_bitrate_bps = received_rtcp_bytes_ * 8 / elapsed_sec; + if (video_bitrate_kbps > 0) { + RTC_HISTOGRAM_COUNTS_SPARSE_100000("WebRTC.Call.VideoBitrateReceivedInKbps", + video_bitrate_kbps); + } + if (audio_bitrate_kbps > 0) { + RTC_HISTOGRAM_COUNTS_SPARSE_100000("WebRTC.Call.AudioBitrateReceivedInKbps", + audio_bitrate_kbps); + } + if (rtcp_bitrate_bps > 0) { + RTC_HISTOGRAM_COUNTS_SPARSE_100000("WebRTC.Call.RtcpBitrateReceivedInBps", + rtcp_bitrate_bps); + } + RTC_HISTOGRAM_COUNTS_SPARSE_100000( + "WebRTC.Call.BitrateReceivedInKbps", + audio_bitrate_kbps + video_bitrate_kbps + rtcp_bitrate_bps / 1000); } PacketReceiver* Call::Receiver() { @@ -198,8 +300,8 @@ webrtc::AudioSendStream* Call::CreateAudioSendStream( const webrtc::AudioSendStream::Config& config) { TRACE_EVENT0("webrtc", "Call::CreateAudioSendStream"); RTC_DCHECK(configuration_thread_checker_.CalledOnValidThread()); - AudioSendStream* send_stream = - new AudioSendStream(config, config_.voice_engine); + AudioSendStream* send_stream = new AudioSendStream( + config, config_.audio_state, congestion_controller_.get()); if (!network_enabled_) send_stream->SignalNetworkState(kNetworkDown); { @@ -234,8 +336,7 @@ webrtc::AudioReceiveStream* Call::CreateAudioReceiveStream( TRACE_EVENT0("webrtc", "Call::CreateAudioReceiveStream"); RTC_DCHECK(configuration_thread_checker_.CalledOnValidThread()); AudioReceiveStream* receive_stream = new AudioReceiveStream( - congestion_controller_->GetRemoteBitrateEstimator(false), config, - config_.voice_engine); + congestion_controller_.get(), config, config_.audio_state); { WriteLockScoped write_lock(*receive_crit_); RTC_DCHECK(audio_receive_ssrcs_.find(config.rtp.remote_ssrc) == @@ -279,8 +380,8 @@ webrtc::VideoSendStream* Call::CreateVideoSendStream( // the call has already started. VideoSendStream* send_stream = new VideoSendStream( num_cpu_cores_, module_process_thread_.get(), call_stats_.get(), - congestion_controller_.get(), config, encoder_config, - suspended_video_send_ssrcs_); + congestion_controller_.get(), bitrate_allocator_.get(), config, + encoder_config, suspended_video_send_ssrcs_); if (!network_enabled_) send_stream->SignalNetworkState(kNetworkDown); @@ -338,7 +439,7 @@ webrtc::VideoReceiveStream* Call::CreateVideoReceiveStream( RTC_DCHECK(configuration_thread_checker_.CalledOnValidThread()); VideoReceiveStream* receive_stream = new VideoReceiveStream( num_cpu_cores_, congestion_controller_.get(), config, - config_.voice_engine, module_process_thread_.get(), call_stats_.get()); + voice_engine(), module_process_thread_.get(), call_stats_.get()); WriteLockScoped write_lock(*receive_crit_); RTC_DCHECK(video_receive_ssrcs_.find(config.rtp.remote_ssrc) == @@ -463,12 +564,48 @@ void Call::SignalNetworkState(NetworkState state) { } void Call::OnSentPacket(const rtc::SentPacket& sent_packet) { + if (first_packet_sent_ms_ == -1) + first_packet_sent_ms_ = clock_->TimeInMilliseconds(); congestion_controller_->OnSentPacket(sent_packet); } +void Call::OnNetworkChanged(uint32_t target_bitrate_bps, uint8_t fraction_loss, + int64_t rtt_ms) { + uint32_t allocated_bitrate_bps = bitrate_allocator_->OnNetworkChanged( + target_bitrate_bps, fraction_loss, rtt_ms); + + int pad_up_to_bitrate_bps = 0; + { + ReadLockScoped read_lock(*send_crit_); + // No need to update as long as we're not sending. + if (video_send_streams_.empty()) + return; + + for (VideoSendStream* stream : video_send_streams_) + pad_up_to_bitrate_bps += stream->GetPaddingNeededBps(); + } + // Allocated bitrate might be higher than bitrate estimate if enforcing min + // bitrate, or lower if estimate is higher than the sum of max bitrates, so + // set the pacer bitrate to the maximum of the two. + uint32_t pacer_bitrate_bps = + std::max(target_bitrate_bps, allocated_bitrate_bps); + { + rtc::CritScope lock(&bitrate_crit_); + // We only update these stats if we have send streams, and assume that + // OnNetworkChanged is called roughly with a fixed frequency. + estimated_send_bitrate_sum_kbits_ += target_bitrate_bps / 1000; + pacer_bitrate_sum_kbits_ += pacer_bitrate_bps / 1000; + ++num_bitrate_updates_; + } + congestion_controller_->UpdatePacerBitrate( + target_bitrate_bps / 1000, + PacedSender::kDefaultPaceMultiplier * pacer_bitrate_bps / 1000, + pad_up_to_bitrate_bps / 1000); +} + void Call::ConfigureSync(const std::string& sync_group) { // Set sync only if there was no previous one. - if (config_.voice_engine == nullptr || sync_group.empty()) + if (voice_engine() == nullptr || sync_group.empty()) return; AudioReceiveStream* sync_audio_stream = nullptr; @@ -506,10 +643,10 @@ void Call::ConfigureSync(const std::string& sync_group) { } // Only sync the first A/V pair within this sync group. if (sync_audio_stream != nullptr && num_synced_streams == 1) { - video_stream->SetSyncChannel(config_.voice_engine, + video_stream->SetSyncChannel(voice_engine(), sync_audio_stream->config().voe_channel_id); } else { - video_stream->SetSyncChannel(config_.voice_engine, -1); + video_stream->SetSyncChannel(voice_engine(), -1); } } } @@ -517,10 +654,12 @@ void Call::ConfigureSync(const std::string& sync_group) { PacketReceiver::DeliveryStatus Call::DeliverRtcp(MediaType media_type, const uint8_t* packet, size_t length) { + TRACE_EVENT0("webrtc", "Call::DeliverRtcp"); // TODO(pbos): Figure out what channel needs it actually. // Do NOT broadcast! Also make sure it's a valid packet. // Return DELIVERY_UNKNOWN_SSRC if it can be determined that // there's no receiver of the packet. + received_rtcp_bytes_ += length; bool rtcp_delivered = false; if (media_type == MediaType::ANY || media_type == MediaType::VIDEO) { ReadLockScoped read_lock(*receive_crit_); @@ -549,16 +688,21 @@ PacketReceiver::DeliveryStatus Call::DeliverRtp(MediaType media_type, const uint8_t* packet, size_t length, const PacketTime& packet_time) { + TRACE_EVENT0("webrtc", "Call::DeliverRtp"); // Minimum RTP header size. if (length < 12) return DELIVERY_PACKET_ERROR; - uint32_t ssrc = ByteReader<uint32_t>::ReadBigEndian(&packet[8]); + last_rtp_packet_received_ms_ = clock_->TimeInMilliseconds(); + if (first_rtp_packet_received_ms_ == -1) + first_rtp_packet_received_ms_ = last_rtp_packet_received_ms_; + uint32_t ssrc = ByteReader<uint32_t>::ReadBigEndian(&packet[8]); ReadLockScoped read_lock(*receive_crit_); if (media_type == MediaType::ANY || media_type == MediaType::AUDIO) { auto it = audio_receive_ssrcs_.find(ssrc); if (it != audio_receive_ssrcs_.end()) { + received_audio_bytes_ += length; auto status = it->second->DeliverRtp(packet, length, packet_time) ? DELIVERY_OK : DELIVERY_PACKET_ERROR; @@ -570,6 +714,7 @@ PacketReceiver::DeliveryStatus Call::DeliverRtp(MediaType media_type, if (media_type == MediaType::ANY || media_type == MediaType::VIDEO) { auto it = video_receive_ssrcs_.find(ssrc); if (it != video_receive_ssrcs_.end()) { + received_video_bytes_ += length; auto status = it->second->DeliverRtp(packet, length, packet_time) ? DELIVERY_OK : DELIVERY_PACKET_ERROR; diff --git a/webrtc/call/call_perf_tests.cc b/webrtc/call/call_perf_tests.cc index c37b83bab4..3adcb10b09 100644 --- a/webrtc/call/call_perf_tests.cc +++ b/webrtc/call/call_perf_tests.cc @@ -18,8 +18,10 @@ #include "webrtc/base/thread_annotations.h" #include "webrtc/call.h" #include "webrtc/call/transport_adapter.h" -#include "webrtc/modules/audio_coding/main/include/audio_coding_module.h" -#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" +#include "webrtc/common.h" +#include "webrtc/config.h" +#include "webrtc/modules/audio_coding/include/audio_coding_module.h" +#include "webrtc/modules/rtp_rtcp/include/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" #include "webrtc/system_wrappers/include/critical_section_wrapper.h" #include "webrtc/system_wrappers/include/rtp_to_ntp.h" @@ -172,7 +174,7 @@ class VideoRtcpAndSyncObserver : public SyncRtcpObserver, public VideoRenderer { false); } if (time_since_creation > kMinRunTimeMs) - observation_complete_->Set(); + observation_complete_.Set(); } } @@ -189,6 +191,8 @@ class VideoRtcpAndSyncObserver : public SyncRtcpObserver, public VideoRenderer { void CallPerfTest::TestAudioVideoSync(bool fec, bool create_audio_first) { const char* kSyncGroup = "av_sync"; + const uint32_t kAudioSendSsrc = 1234; + const uint32_t kAudioRecvSsrc = 5678; class AudioPacketReceiver : public PacketReceiver { public: AudioPacketReceiver(int channel, VoENetwork* voe_network) @@ -228,35 +232,45 @@ void CallPerfTest::TestAudioVideoSync(bool fec, bool create_audio_first) { test::FakeAudioDevice fake_audio_device(Clock::GetRealTimeClock(), audio_filename); EXPECT_EQ(0, voe_base->Init(&fake_audio_device, nullptr)); - int channel = voe_base->CreateChannel(); + Config voe_config; + voe_config.Set<VoicePacing>(new VoicePacing(true)); + int send_channel_id = voe_base->CreateChannel(voe_config); + int recv_channel_id = voe_base->CreateChannel(); SyncRtcpObserver audio_observer; + AudioState::Config send_audio_state_config; + send_audio_state_config.voice_engine = voice_engine; + Call::Config sender_config; + sender_config.audio_state = AudioState::Create(send_audio_state_config); Call::Config receiver_config; - receiver_config.voice_engine = voice_engine; - CreateCalls(Call::Config(), receiver_config); + receiver_config.audio_state = sender_config.audio_state; + CreateCalls(sender_config, receiver_config); - CodecInst isac = {103, "ISAC", 16000, 480, 1, 32000}; - EXPECT_EQ(0, voe_codec->SetSendCodec(channel, isac)); - - AudioPacketReceiver voe_packet_receiver(channel, voe_network); + AudioPacketReceiver voe_send_packet_receiver(send_channel_id, voe_network); + AudioPacketReceiver voe_recv_packet_receiver(recv_channel_id, voe_network); FakeNetworkPipe::Config net_config; net_config.queue_delay_ms = 500; net_config.loss_percent = 5; test::PacketTransport audio_send_transport( nullptr, &audio_observer, test::PacketTransport::kSender, net_config); - audio_send_transport.SetReceiver(&voe_packet_receiver); + audio_send_transport.SetReceiver(&voe_recv_packet_receiver); test::PacketTransport audio_receive_transport( nullptr, &audio_observer, test::PacketTransport::kReceiver, net_config); - audio_receive_transport.SetReceiver(&voe_packet_receiver); + audio_receive_transport.SetReceiver(&voe_send_packet_receiver); + + internal::TransportAdapter send_transport_adapter(&audio_send_transport); + send_transport_adapter.Enable(); + EXPECT_EQ(0, voe_network->RegisterExternalTransport(send_channel_id, + send_transport_adapter)); - internal::TransportAdapter transport_adapter(&audio_send_transport); - transport_adapter.Enable(); - EXPECT_EQ(0, - voe_network->RegisterExternalTransport(channel, transport_adapter)); + internal::TransportAdapter recv_transport_adapter(&audio_receive_transport); + recv_transport_adapter.Enable(); + EXPECT_EQ(0, voe_network->RegisterExternalTransport(recv_channel_id, + recv_transport_adapter)); - VideoRtcpAndSyncObserver observer(Clock::GetRealTimeClock(), channel, + VideoRtcpAndSyncObserver observer(Clock::GetRealTimeClock(), recv_channel_id, voe_sync, &audio_observer); test::PacketTransport sync_send_transport(sender_call_.get(), &observer, @@ -270,34 +284,45 @@ void CallPerfTest::TestAudioVideoSync(bool fec, bool create_audio_first) { test::FakeDecoder fake_decoder; - CreateSendConfig(1, &sync_send_transport); + CreateSendConfig(1, 0, &sync_send_transport); CreateMatchingReceiveConfigs(&sync_receive_transport); - send_config_.rtp.nack.rtp_history_ms = kNackRtpHistoryMs; + AudioSendStream::Config audio_send_config(&audio_send_transport); + audio_send_config.voe_channel_id = send_channel_id; + audio_send_config.rtp.ssrc = kAudioSendSsrc; + AudioSendStream* audio_send_stream = + sender_call_->CreateAudioSendStream(audio_send_config); + + CodecInst isac = {103, "ISAC", 16000, 480, 1, 32000}; + EXPECT_EQ(0, voe_codec->SetSendCodec(send_channel_id, isac)); + + video_send_config_.rtp.nack.rtp_history_ms = kNackRtpHistoryMs; if (fec) { - send_config_.rtp.fec.red_payload_type = kRedPayloadType; - send_config_.rtp.fec.ulpfec_payload_type = kUlpfecPayloadType; - receive_configs_[0].rtp.fec.red_payload_type = kRedPayloadType; - receive_configs_[0].rtp.fec.ulpfec_payload_type = kUlpfecPayloadType; + video_send_config_.rtp.fec.red_payload_type = kRedPayloadType; + video_send_config_.rtp.fec.ulpfec_payload_type = kUlpfecPayloadType; + video_receive_configs_[0].rtp.fec.red_payload_type = kRedPayloadType; + video_receive_configs_[0].rtp.fec.ulpfec_payload_type = kUlpfecPayloadType; } - receive_configs_[0].rtp.nack.rtp_history_ms = 1000; - receive_configs_[0].renderer = &observer; - receive_configs_[0].sync_group = kSyncGroup; + video_receive_configs_[0].rtp.nack.rtp_history_ms = 1000; + video_receive_configs_[0].renderer = &observer; + video_receive_configs_[0].sync_group = kSyncGroup; - AudioReceiveStream::Config audio_config; - audio_config.voe_channel_id = channel; - audio_config.sync_group = kSyncGroup; + AudioReceiveStream::Config audio_recv_config; + audio_recv_config.rtp.remote_ssrc = kAudioSendSsrc; + audio_recv_config.rtp.local_ssrc = kAudioRecvSsrc; + audio_recv_config.voe_channel_id = recv_channel_id; + audio_recv_config.sync_group = kSyncGroup; - AudioReceiveStream* audio_receive_stream = nullptr; + AudioReceiveStream* audio_receive_stream; if (create_audio_first) { audio_receive_stream = - receiver_call_->CreateAudioReceiveStream(audio_config); - CreateStreams(); + receiver_call_->CreateAudioReceiveStream(audio_recv_config); + CreateVideoStreams(); } else { - CreateStreams(); + CreateVideoStreams(); audio_receive_stream = - receiver_call_->CreateAudioReceiveStream(audio_config); + receiver_call_->CreateAudioReceiveStream(audio_recv_config); } CreateFrameGeneratorCapturer(); @@ -305,16 +330,16 @@ void CallPerfTest::TestAudioVideoSync(bool fec, bool create_audio_first) { Start(); fake_audio_device.Start(); - EXPECT_EQ(0, voe_base->StartPlayout(channel)); - EXPECT_EQ(0, voe_base->StartReceive(channel)); - EXPECT_EQ(0, voe_base->StartSend(channel)); + EXPECT_EQ(0, voe_base->StartPlayout(recv_channel_id)); + EXPECT_EQ(0, voe_base->StartReceive(recv_channel_id)); + EXPECT_EQ(0, voe_base->StartSend(send_channel_id)); - EXPECT_EQ(kEventSignaled, observer.Wait()) + EXPECT_TRUE(observer.Wait()) << "Timed out while waiting for audio and video to be synchronized."; - EXPECT_EQ(0, voe_base->StopSend(channel)); - EXPECT_EQ(0, voe_base->StopReceive(channel)); - EXPECT_EQ(0, voe_base->StopPlayout(channel)); + EXPECT_EQ(0, voe_base->StopSend(send_channel_id)); + EXPECT_EQ(0, voe_base->StopReceive(recv_channel_id)); + EXPECT_EQ(0, voe_base->StopPlayout(recv_channel_id)); fake_audio_device.Stop(); Stop(); @@ -323,16 +348,18 @@ void CallPerfTest::TestAudioVideoSync(bool fec, bool create_audio_first) { audio_send_transport.StopSending(); audio_receive_transport.StopSending(); - voe_base->DeleteChannel(channel); + DestroyStreams(); + + sender_call_->DestroyAudioSendStream(audio_send_stream); + receiver_call_->DestroyAudioReceiveStream(audio_receive_stream); + + voe_base->DeleteChannel(send_channel_id); + voe_base->DeleteChannel(recv_channel_id); voe_base->Release(); voe_codec->Release(); voe_network->Release(); voe_sync->Release(); - DestroyStreams(); - - receiver_call_->DestroyAudioReceiveStream(audio_receive_stream); - DestroyCalls(); VoiceEngine::Delete(voice_engine); @@ -357,8 +384,12 @@ void CallPerfTest::TestCaptureNtpTime(const FakeNetworkPipe::Config& net_config, class CaptureNtpTimeObserver : public test::EndToEndTest, public VideoRenderer { public: - CaptureNtpTimeObserver(int threshold_ms, int start_time_ms, int run_time_ms) + CaptureNtpTimeObserver(const FakeNetworkPipe::Config& net_config, + int threshold_ms, + int start_time_ms, + int run_time_ms) : EndToEndTest(kLongTimeoutMs), + net_config_(net_config), clock_(Clock::GetRealTimeClock()), threshold_ms_(threshold_ms), start_time_ms_(start_time_ms), @@ -369,6 +400,16 @@ void CallPerfTest::TestCaptureNtpTime(const FakeNetworkPipe::Config& net_config, rtp_start_timestamp_(0) {} private: + test::PacketTransport* CreateSendTransport(Call* sender_call) override { + return new test::PacketTransport( + sender_call, this, test::PacketTransport::kSender, net_config_); + } + + test::PacketTransport* CreateReceiveTransport() override { + return new test::PacketTransport( + nullptr, this, test::PacketTransport::kReceiver, net_config_); + } + void RenderFrame(const VideoFrame& video_frame, int time_to_render_ms) override { rtc::CritScope lock(&crit_); @@ -386,7 +427,7 @@ void CallPerfTest::TestCaptureNtpTime(const FakeNetworkPipe::Config& net_config, } if (time_since_creation > run_time_ms_) { - observation_complete_->Set(); + observation_complete_.Set(); } FrameCaptureTimeList::iterator iter = @@ -437,21 +478,23 @@ void CallPerfTest::TestCaptureNtpTime(const FakeNetworkPipe::Config& net_config, capturer_ = frame_generator_capturer; } - void ModifyConfigs(VideoSendStream::Config* send_config, - std::vector<VideoReceiveStream::Config>* receive_configs, - VideoEncoderConfig* encoder_config) override { + void ModifyVideoConfigs( + VideoSendStream::Config* send_config, + std::vector<VideoReceiveStream::Config>* receive_configs, + VideoEncoderConfig* encoder_config) override { (*receive_configs)[0].renderer = this; // Enable the receiver side rtt calculation. (*receive_configs)[0].rtp.rtcp_xr.receiver_reference_time_report = true; } void PerformTest() override { - EXPECT_EQ(kEventSignaled, Wait()) << "Timed out while waiting for " - "estimated capture NTP time to be " - "within bounds."; + EXPECT_TRUE(Wait()) << "Timed out while waiting for " + "estimated capture NTP time to be " + "within bounds."; } rtc::CriticalSection crit_; + const FakeNetworkPipe::Config net_config_; Clock* const clock_; int threshold_ms_; int start_time_ms_; @@ -462,9 +505,9 @@ void CallPerfTest::TestCaptureNtpTime(const FakeNetworkPipe::Config& net_config, uint32_t rtp_start_timestamp_; typedef std::map<uint32_t, uint32_t> FrameCaptureTimeList; FrameCaptureTimeList capture_time_list_ GUARDED_BY(&crit_); - } test(threshold_ms, start_time_ms, run_time_ms); + } test(net_config, threshold_ms, start_time_ms, run_time_ms); - RunBaseTest(&test, net_config); + RunBaseTest(&test); } TEST_F(CallPerfTest, CaptureNtpTimeWithNetworkDelay) { @@ -501,26 +544,26 @@ void CallPerfTest::TestCpuOveruse(LoadObserver::Load tested_load, void OnLoadUpdate(Load load) override { if (load == tested_load_) - observation_complete_->Set(); + observation_complete_.Set(); } - void ModifyConfigs(VideoSendStream::Config* send_config, - std::vector<VideoReceiveStream::Config>* receive_configs, - VideoEncoderConfig* encoder_config) override { + void ModifyVideoConfigs( + VideoSendStream::Config* send_config, + std::vector<VideoReceiveStream::Config>* receive_configs, + VideoEncoderConfig* encoder_config) override { send_config->overuse_callback = this; send_config->encoder_settings.encoder = &encoder_; } void PerformTest() override { - EXPECT_EQ(kEventSignaled, Wait()) - << "Timed out before receiving an overuse callback."; + EXPECT_TRUE(Wait()) << "Timed out before receiving an overuse callback."; } LoadObserver::Load tested_load_; test::DelayedEncoder encoder_; } test(tested_load, encode_delay_ms); - RunBaseTest(&test, FakeNetworkPipe::Config()); + RunBaseTest(&test); } TEST_F(CallPerfTest, ReceivesCpuUnderuse) { @@ -581,21 +624,22 @@ void CallPerfTest::TestMinTransmitBitrate(bool pad_to_min_bitrate) { } if (num_bitrate_observations_in_range_ == kNumBitrateObservationsInRange) - observation_complete_->Set(); + observation_complete_.Set(); } } return SEND_PACKET; } - void OnStreamsCreated( + void OnVideoStreamsCreated( VideoSendStream* send_stream, const std::vector<VideoReceiveStream*>& receive_streams) override { send_stream_ = send_stream; } - void ModifyConfigs(VideoSendStream::Config* send_config, - std::vector<VideoReceiveStream::Config>* receive_configs, - VideoEncoderConfig* encoder_config) override { + void ModifyVideoConfigs( + VideoSendStream::Config* send_config, + std::vector<VideoReceiveStream::Config>* receive_configs, + VideoEncoderConfig* encoder_config) override { if (pad_to_min_bitrate_) { encoder_config->min_transmit_bitrate_bps = kMinTransmitBitrateBps; } else { @@ -604,8 +648,7 @@ void CallPerfTest::TestMinTransmitBitrate(bool pad_to_min_bitrate) { } void PerformTest() override { - EXPECT_EQ(kEventSignaled, Wait()) - << "Timeout while waiting for send-bitrate stats."; + EXPECT_TRUE(Wait()) << "Timeout while waiting for send-bitrate stats."; } VideoSendStream* send_stream_; @@ -614,7 +657,7 @@ void CallPerfTest::TestMinTransmitBitrate(bool pad_to_min_bitrate) { } test(pad_to_min_bitrate); fake_encoder_.SetMaxBitrate(kMaxEncodeBitrateKbps); - RunBaseTest(&test, FakeNetworkPipe::Config()); + RunBaseTest(&test); } TEST_F(CallPerfTest, PadsToMinTransmitBitrate) { TestMinTransmitBitrate(true); } @@ -633,7 +676,7 @@ TEST_F(CallPerfTest, KeepsHighBitrateWhenReconfiguringSender) { BitrateObserver() : EndToEndTest(kDefaultTimeoutMs), FakeEncoder(Clock::GetRealTimeClock()), - time_to_reconfigure_(webrtc::EventWrapper::Create()), + time_to_reconfigure_(false, false), encoder_inits_(0), last_set_bitrate_(0), send_stream_(nullptr) {} @@ -652,7 +695,7 @@ TEST_F(CallPerfTest, KeepsHighBitrateWhenReconfiguringSender) { last_set_bitrate_, kPermittedReconfiguredBitrateDiffKbps) << "Encoder reconfigured with bitrate too far away from last set."; - observation_complete_->Set(); + observation_complete_.Set(); } return FakeEncoder::InitEncode(config, number_of_cores, max_payload_size); } @@ -662,7 +705,7 @@ TEST_F(CallPerfTest, KeepsHighBitrateWhenReconfiguringSender) { last_set_bitrate_ = new_target_bitrate_kbps; if (encoder_inits_ == 1 && new_target_bitrate_kbps > kReconfigureThresholdKbps) { - time_to_reconfigure_->Set(); + time_to_reconfigure_.Set(); } return FakeEncoder::SetRates(new_target_bitrate_kbps, framerate); } @@ -673,9 +716,10 @@ TEST_F(CallPerfTest, KeepsHighBitrateWhenReconfiguringSender) { return config; } - void ModifyConfigs(VideoSendStream::Config* send_config, - std::vector<VideoReceiveStream::Config>* receive_configs, - VideoEncoderConfig* encoder_config) override { + void ModifyVideoConfigs( + VideoSendStream::Config* send_config, + std::vector<VideoReceiveStream::Config>* receive_configs, + VideoEncoderConfig* encoder_config) override { send_config->encoder_settings.encoder = this; encoder_config->streams[0].min_bitrate_bps = 50000; encoder_config->streams[0].target_bitrate_bps = @@ -684,32 +728,32 @@ TEST_F(CallPerfTest, KeepsHighBitrateWhenReconfiguringSender) { encoder_config_ = *encoder_config; } - void OnStreamsCreated( + void OnVideoStreamsCreated( VideoSendStream* send_stream, const std::vector<VideoReceiveStream*>& receive_streams) override { send_stream_ = send_stream; } void PerformTest() override { - ASSERT_EQ(kEventSignaled, time_to_reconfigure_->Wait(kDefaultTimeoutMs)) + ASSERT_TRUE(time_to_reconfigure_.Wait(kDefaultTimeoutMs)) << "Timed out before receiving an initial high bitrate."; encoder_config_.streams[0].width *= 2; encoder_config_.streams[0].height *= 2; EXPECT_TRUE(send_stream_->ReconfigureVideoEncoder(encoder_config_)); - EXPECT_EQ(kEventSignaled, Wait()) + EXPECT_TRUE(Wait()) << "Timed out while waiting for a couple of high bitrate estimates " "after reconfiguring the send stream."; } private: - rtc::scoped_ptr<webrtc::EventWrapper> time_to_reconfigure_; + rtc::Event time_to_reconfigure_; int encoder_inits_; uint32_t last_set_bitrate_; VideoSendStream* send_stream_; VideoEncoderConfig encoder_config_; } test; - RunBaseTest(&test, FakeNetworkPipe::Config()); + RunBaseTest(&test); } } // namespace webrtc diff --git a/webrtc/call/call_unittest.cc b/webrtc/call/call_unittest.cc index 9819b538f8..75c8238a5b 100644 --- a/webrtc/call/call_unittest.cc +++ b/webrtc/call/call_unittest.cc @@ -12,22 +12,25 @@ #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/audio_state.h" #include "webrtc/call.h" -#include "webrtc/test/fake_voice_engine.h" +#include "webrtc/test/mock_voice_engine.h" namespace { struct CallHelper { - CallHelper() : voice_engine_(new webrtc::test::FakeVoiceEngine()) { + CallHelper() { + webrtc::AudioState::Config audio_state_config; + audio_state_config.voice_engine = &voice_engine_; webrtc::Call::Config config; - config.voice_engine = voice_engine_.get(); + config.audio_state = webrtc::AudioState::Create(audio_state_config); call_.reset(webrtc::Call::Create(config)); } webrtc::Call* operator->() { return call_.get(); } private: - rtc::scoped_ptr<webrtc::test::FakeVoiceEngine> voice_engine_; + testing::NiceMock<webrtc::test::MockVoiceEngine> voice_engine_; rtc::scoped_ptr<webrtc::Call> call_; }; } // namespace diff --git a/webrtc/call/congestion_controller.cc b/webrtc/call/congestion_controller.cc index 1ec361e898..c442667ae0 100644 --- a/webrtc/call/congestion_controller.cc +++ b/webrtc/call/congestion_controller.cc @@ -11,23 +11,24 @@ #include "webrtc/call/congestion_controller.h" #include "webrtc/base/checks.h" +#include "webrtc/base/logging.h" #include "webrtc/base/thread_annotations.h" #include "webrtc/common.h" -#include "webrtc/modules/pacing/include/paced_sender.h" -#include "webrtc/modules/pacing/include/packet_router.h" +#include "webrtc/modules/bitrate_controller/include/bitrate_controller.h" +#include "webrtc/modules/pacing/paced_sender.h" +#include "webrtc/modules/pacing/packet_router.h" #include "webrtc/modules/remote_bitrate_estimator/include/send_time_history.h" #include "webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.h" #include "webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.h" #include "webrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.h" #include "webrtc/modules/remote_bitrate_estimator/transport_feedback_adapter.h" -#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" -#include "webrtc/modules/utility/interface/process_thread.h" +#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp.h" +#include "webrtc/modules/utility/include/process_thread.h" #include "webrtc/system_wrappers/include/critical_section_wrapper.h" -#include "webrtc/system_wrappers/include/logging.h" -#include "webrtc/video_engine/call_stats.h" -#include "webrtc/video_engine/payload_router.h" -#include "webrtc/video_engine/vie_encoder.h" -#include "webrtc/video_engine/vie_remb.h" +#include "webrtc/video/call_stats.h" +#include "webrtc/video/payload_router.h" +#include "webrtc/video/vie_encoder.h" +#include "webrtc/video/vie_remb.h" #include "webrtc/voice_engine/include/voe_video_sync.h" namespace webrtc { @@ -144,9 +145,9 @@ class WrappingBitrateEstimator : public RemoteBitrateEstimator { } // namespace CongestionController::CongestionController(ProcessThread* process_thread, - CallStats* call_stats) - : remb_(new VieRemb()), - bitrate_allocator_(new BitrateAllocator()), + CallStats* call_stats, + BitrateObserver* bitrate_observer) + : remb_(new VieRemb(Clock::GetRealTimeClock())), packet_router_(new PacketRouter()), pacer_(new PacedSender(Clock::GetRealTimeClock(), packet_router_.get(), @@ -166,7 +167,7 @@ CongestionController::CongestionController(ProcessThread* process_thread, // construction. bitrate_controller_( BitrateController::CreateBitrateController(Clock::GetRealTimeClock(), - this)), + bitrate_observer)), min_bitrate_bps_(RemoteBitrateEstimator::kDefaultMinBitrateBps) { call_stats_->RegisterStatsObserver(remote_bitrate_estimator_.get()); @@ -249,6 +250,12 @@ CongestionController::GetTransportFeedbackObserver() { return transport_feedback_adapter_.get(); } +void CongestionController::UpdatePacerBitrate(int bitrate_kbps, + int max_bitrate_kbps, + int min_bitrate_kbps) { + pacer_->UpdateBitrate(bitrate_kbps, max_bitrate_kbps, min_bitrate_kbps); +} + int64_t CongestionController::GetPacerQueuingDelayMs() const { return pacer_->QueueInMs(); } @@ -278,23 +285,6 @@ void CongestionController::SignalNetworkState(NetworkState state) { } } -// TODO(mflodman): Move this logic out from CongestionController. -void CongestionController::OnNetworkChanged(uint32_t target_bitrate_bps, - uint8_t fraction_loss, - int64_t rtt) { - bitrate_allocator_->OnNetworkChanged(target_bitrate_bps, fraction_loss, rtt); - int pad_up_to_bitrate_bps = 0; - { - rtc::CritScope lock(&encoder_crit_); - for (const auto& encoder : encoders_) - pad_up_to_bitrate_bps += encoder->GetPaddingNeededBps(); - } - pacer_->UpdateBitrate( - target_bitrate_bps / 1000, - PacedSender::kDefaultPaceMultiplier * target_bitrate_bps / 1000, - pad_up_to_bitrate_bps / 1000); -} - void CongestionController::OnSentPacket(const rtc::SentPacket& sent_packet) { if (transport_feedback_adapter_) { transport_feedback_adapter_->OnSentPacket(sent_packet.packet_id, diff --git a/webrtc/call/congestion_controller.h b/webrtc/call/congestion_controller.h index b424234123..b77c46faa3 100644 --- a/webrtc/call/congestion_controller.h +++ b/webrtc/call/congestion_controller.h @@ -16,12 +16,12 @@ #include "webrtc/base/criticalsection.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/base/socket.h" -#include "webrtc/modules/bitrate_controller/include/bitrate_controller.h" #include "webrtc/stream.h" namespace webrtc { -class BitrateAllocator; +class BitrateController; +class BitrateObserver; class CallStats; class Config; class PacedSender; @@ -32,43 +32,43 @@ class RemoteEstimatorProxy; class RtpRtcp; class SendStatisticsProxy; class TransportFeedbackAdapter; +class TransportFeedbackObserver; class ViEEncoder; class VieRemb; -class CongestionController : public BitrateObserver { +class CongestionController { public: - CongestionController(ProcessThread* process_thread, CallStats* call_stats); - ~CongestionController(); - void AddEncoder(ViEEncoder* encoder); - void RemoveEncoder(ViEEncoder* encoder); - void SetBweBitrates(int min_bitrate_bps, - int start_bitrate_bps, - int max_bitrate_bps); - - void SetChannelRembStatus(bool sender, bool receiver, RtpRtcp* rtp_module); - - void SignalNetworkState(NetworkState state); - - BitrateController* GetBitrateController() const; - RemoteBitrateEstimator* GetRemoteBitrateEstimator(bool send_side_bwe) const; - int64_t GetPacerQueuingDelayMs() const; - PacedSender* pacer() const { return pacer_.get(); } - PacketRouter* packet_router() const { return packet_router_.get(); } - BitrateAllocator* bitrate_allocator() const { - return bitrate_allocator_.get(); } - TransportFeedbackObserver* GetTransportFeedbackObserver(); - - // Implements BitrateObserver. - void OnNetworkChanged(uint32_t target_bitrate_bps, - uint8_t fraction_loss, - int64_t rtt) override; - - void OnSentPacket(const rtc::SentPacket& sent_packet); + CongestionController(ProcessThread* process_thread, CallStats* call_stats, + BitrateObserver* bitrate_observer); + virtual ~CongestionController(); + virtual void AddEncoder(ViEEncoder* encoder); + virtual void RemoveEncoder(ViEEncoder* encoder); + virtual void SetBweBitrates(int min_bitrate_bps, + int start_bitrate_bps, + int max_bitrate_bps); + + virtual void SetChannelRembStatus(bool sender, + bool receiver, + RtpRtcp* rtp_module); + + virtual void SignalNetworkState(NetworkState state); + + virtual BitrateController* GetBitrateController() const; + virtual RemoteBitrateEstimator* GetRemoteBitrateEstimator( + bool send_side_bwe) const; + virtual int64_t GetPacerQueuingDelayMs() const; + virtual PacedSender* pacer() const { return pacer_.get(); } + virtual PacketRouter* packet_router() const { return packet_router_.get(); } + virtual TransportFeedbackObserver* GetTransportFeedbackObserver(); + + virtual void UpdatePacerBitrate(int bitrate_kbps, + int max_bitrate_kbps, + int min_bitrate_kbps); + + virtual void OnSentPacket(const rtc::SentPacket& sent_packet); private: rtc::scoped_ptr<VieRemb> remb_; - // TODO(mflodman): Move bitrate_allocator_ to Call. - rtc::scoped_ptr<BitrateAllocator> bitrate_allocator_; rtc::scoped_ptr<PacketRouter> packet_router_; rtc::scoped_ptr<PacedSender> pacer_; rtc::scoped_ptr<RemoteBitrateEstimator> remote_bitrate_estimator_; @@ -86,6 +86,8 @@ class CongestionController : public BitrateObserver { rtc::scoped_ptr<BitrateController> bitrate_controller_; rtc::scoped_ptr<TransportFeedbackAdapter> transport_feedback_adapter_; int min_bitrate_bps_; + + RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(CongestionController); }; } // namespace webrtc diff --git a/webrtc/call/mock/mock_congestion_controller.h b/webrtc/call/mock/mock_congestion_controller.h new file mode 100644 index 0000000000..54014da339 --- /dev/null +++ b/webrtc/call/mock/mock_congestion_controller.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_CALL_MOCK_MOCK_CONGESTION_CONTROLLER_H_ +#define WEBRTC_CALL_MOCK_MOCK_CONGESTION_CONTROLLER_H_ + +#include "testing/gmock/include/gmock/gmock.h" +#include "webrtc/call/congestion_controller.h" + +namespace webrtc { +namespace test { + +class MockCongestionController : public CongestionController { + public: + MockCongestionController(ProcessThread* process_thread, + CallStats* call_stats, + BitrateObserver* bitrate_observer) + : CongestionController(process_thread, call_stats, bitrate_observer) {} + MOCK_METHOD1(AddEncoder, void(ViEEncoder* encoder)); + MOCK_METHOD1(RemoveEncoder, void(ViEEncoder* encoder)); + MOCK_METHOD3(SetBweBitrates, + void(int min_bitrate_bps, + int start_bitrate_bps, + int max_bitrate_bps)); + MOCK_METHOD3(SetChannelRembStatus, + void(bool sender, bool receiver, RtpRtcp* rtp_module)); + MOCK_METHOD1(SignalNetworkState, void(NetworkState state)); + MOCK_CONST_METHOD0(GetBitrateController, BitrateController*()); + MOCK_CONST_METHOD1(GetRemoteBitrateEstimator, + RemoteBitrateEstimator*(bool send_side_bwe)); + MOCK_CONST_METHOD0(GetPacerQueuingDelayMs, int64_t()); + MOCK_CONST_METHOD0(pacer, PacedSender*()); + MOCK_CONST_METHOD0(packet_router, PacketRouter*()); + MOCK_METHOD0(GetTransportFeedbackObserver, TransportFeedbackObserver*()); + MOCK_METHOD3(UpdatePacerBitrate, + void(int bitrate_kbps, + int max_bitrate_kbps, + int min_bitrate_kbps)); + MOCK_METHOD1(OnSentPacket, void(const rtc::SentPacket& sent_packet)); + + RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(MockCongestionController); +}; +} // namespace test +} // namespace webrtc +#endif // WEBRTC_CALL_MOCK_MOCK_CONGESTION_CONTROLLER_H_ diff --git a/webrtc/call/packet_injection_tests.cc b/webrtc/call/packet_injection_tests.cc index 18ca0581d1..277cd3e4df 100644 --- a/webrtc/call/packet_injection_tests.cc +++ b/webrtc/call/packet_injection_tests.cc @@ -40,22 +40,22 @@ void PacketInjectionTest::InjectIncorrectPacket(CodecType codec_type, CreateReceiverCall(Call::Config()); test::NullTransport null_transport; - CreateSendConfig(1, &null_transport); + CreateSendConfig(1, 0, &null_transport); CreateMatchingReceiveConfigs(&null_transport); - receive_configs_[0].decoders[0].payload_type = payload_type; + video_receive_configs_[0].decoders[0].payload_type = payload_type; switch (codec_type) { case CodecType::kVp8: - receive_configs_[0].decoders[0].payload_name = "VP8"; + video_receive_configs_[0].decoders[0].payload_name = "VP8"; break; case CodecType::kH264: - receive_configs_[0].decoders[0].payload_name = "H264"; + video_receive_configs_[0].decoders[0].payload_name = "H264"; break; } - CreateStreams(); + CreateVideoStreams(); RTPHeader header; EXPECT_TRUE(rtp_header_parser_->Parse(packet, length, &header)); - EXPECT_EQ(kSendSsrcs[0], header.ssrc) + EXPECT_EQ(kVideoSendSsrcs[0], header.ssrc) << "Packet should have configured SSRC to not be dropped early."; EXPECT_EQ(payload_type, header.payloadType); Start(); diff --git a/webrtc/call/rampup_tests.cc b/webrtc/call/rampup_tests.cc new file mode 100644 index 0000000000..81f1e81c68 --- /dev/null +++ b/webrtc/call/rampup_tests.cc @@ -0,0 +1,587 @@ +/* + * 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/call/rampup_tests.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/checks.h" +#include "webrtc/base/platform_thread.h" +#include "webrtc/test/testsupport/perf_test.h" + +namespace webrtc { +namespace { + +static const int64_t kPollIntervalMs = 20; + +std::vector<uint32_t> GenerateSsrcs(size_t num_streams, uint32_t ssrc_offset) { + std::vector<uint32_t> ssrcs; + for (size_t i = 0; i != num_streams; ++i) + ssrcs.push_back(static_cast<uint32_t>(ssrc_offset + i)); + return ssrcs; +} +} // namespace + +RampUpTester::RampUpTester(size_t num_video_streams, + size_t num_audio_streams, + unsigned int start_bitrate_bps, + const std::string& extension_type, + bool rtx, + bool red) + : EndToEndTest(test::CallTest::kLongTimeoutMs), + event_(false, false), + clock_(Clock::GetRealTimeClock()), + num_video_streams_(num_video_streams), + num_audio_streams_(num_audio_streams), + rtx_(rtx), + red_(red), + send_stream_(nullptr), + start_bitrate_bps_(start_bitrate_bps), + start_bitrate_verified_(false), + expected_bitrate_bps_(0), + test_start_ms_(-1), + ramp_up_finished_ms_(-1), + extension_type_(extension_type), + video_ssrcs_(GenerateSsrcs(num_video_streams_, 100)), + video_rtx_ssrcs_(GenerateSsrcs(num_video_streams_, 200)), + audio_ssrcs_(GenerateSsrcs(num_audio_streams_, 300)), + poller_thread_(&BitrateStatsPollingThread, + this, + "BitrateStatsPollingThread"), + sender_call_(nullptr) { + EXPECT_LE(num_audio_streams_, 1u); + if (rtx_) { + for (size_t i = 0; i < video_ssrcs_.size(); ++i) + rtx_ssrc_map_[video_rtx_ssrcs_[i]] = video_ssrcs_[i]; + } +} + +RampUpTester::~RampUpTester() { + event_.Set(); +} + +Call::Config RampUpTester::GetSenderCallConfig() { + Call::Config call_config; + if (start_bitrate_bps_ != 0) { + call_config.bitrate_config.start_bitrate_bps = start_bitrate_bps_; + } + call_config.bitrate_config.min_bitrate_bps = 10000; + return call_config; +} + +void RampUpTester::OnVideoStreamsCreated( + VideoSendStream* send_stream, + const std::vector<VideoReceiveStream*>& receive_streams) { + send_stream_ = send_stream; +} + +test::PacketTransport* RampUpTester::CreateSendTransport(Call* sender_call) { + send_transport_ = new test::PacketTransport(sender_call, this, + test::PacketTransport::kSender, + forward_transport_config_); + return send_transport_; +} + +size_t RampUpTester::GetNumVideoStreams() const { + return num_video_streams_; +} + +size_t RampUpTester::GetNumAudioStreams() const { + return num_audio_streams_; +} + +void RampUpTester::ModifyVideoConfigs( + VideoSendStream::Config* send_config, + std::vector<VideoReceiveStream::Config>* receive_configs, + VideoEncoderConfig* encoder_config) { + send_config->suspend_below_min_bitrate = true; + + if (num_video_streams_ == 1) { + encoder_config->streams[0].target_bitrate_bps = + encoder_config->streams[0].max_bitrate_bps = 2000000; + // For single stream rampup until 1mbps + expected_bitrate_bps_ = kSingleStreamTargetBps; + } else { + // For multi stream rampup until all streams are being sent. That means + // enough birate to send all the target streams plus the min bitrate of + // the last one. + expected_bitrate_bps_ = encoder_config->streams.back().min_bitrate_bps; + for (size_t i = 0; i < encoder_config->streams.size() - 1; ++i) { + expected_bitrate_bps_ += encoder_config->streams[i].target_bitrate_bps; + } + } + + send_config->rtp.extensions.clear(); + + bool remb; + bool transport_cc; + if (extension_type_ == RtpExtension::kAbsSendTime) { + remb = true; + transport_cc = false; + send_config->rtp.extensions.push_back( + RtpExtension(extension_type_.c_str(), kAbsSendTimeExtensionId)); + } else if (extension_type_ == RtpExtension::kTransportSequenceNumber) { + remb = false; + transport_cc = true; + send_config->rtp.extensions.push_back(RtpExtension( + extension_type_.c_str(), kTransportSequenceNumberExtensionId)); + } else { + remb = true; + transport_cc = false; + send_config->rtp.extensions.push_back(RtpExtension( + extension_type_.c_str(), kTransmissionTimeOffsetExtensionId)); + } + + send_config->rtp.nack.rtp_history_ms = test::CallTest::kNackRtpHistoryMs; + send_config->rtp.ssrcs = video_ssrcs_; + if (rtx_) { + send_config->rtp.rtx.payload_type = test::CallTest::kSendRtxPayloadType; + send_config->rtp.rtx.ssrcs = video_rtx_ssrcs_; + } + if (red_) { + send_config->rtp.fec.ulpfec_payload_type = + test::CallTest::kUlpfecPayloadType; + send_config->rtp.fec.red_payload_type = test::CallTest::kRedPayloadType; + } + + size_t i = 0; + for (VideoReceiveStream::Config& recv_config : *receive_configs) { + recv_config.rtp.remb = remb; + recv_config.rtp.transport_cc = transport_cc; + recv_config.rtp.extensions = send_config->rtp.extensions; + + recv_config.rtp.remote_ssrc = video_ssrcs_[i]; + recv_config.rtp.nack.rtp_history_ms = send_config->rtp.nack.rtp_history_ms; + + if (red_) { + recv_config.rtp.fec.red_payload_type = + send_config->rtp.fec.red_payload_type; + recv_config.rtp.fec.ulpfec_payload_type = + send_config->rtp.fec.ulpfec_payload_type; + } + + if (rtx_) { + recv_config.rtp.rtx[send_config->encoder_settings.payload_type].ssrc = + video_rtx_ssrcs_[i]; + recv_config.rtp.rtx[send_config->encoder_settings.payload_type] + .payload_type = send_config->rtp.rtx.payload_type; + } + ++i; + } +} + +void RampUpTester::ModifyAudioConfigs( + AudioSendStream::Config* send_config, + std::vector<AudioReceiveStream::Config>* receive_configs) { + if (num_audio_streams_ == 0) + return; + + EXPECT_NE(RtpExtension::kTOffset, extension_type_) + << "Audio BWE not supported with toffset."; + + send_config->rtp.ssrc = audio_ssrcs_[0]; + send_config->rtp.extensions.clear(); + + bool transport_cc = false; + if (extension_type_ == RtpExtension::kAbsSendTime) { + transport_cc = false; + send_config->rtp.extensions.push_back( + RtpExtension(extension_type_.c_str(), kAbsSendTimeExtensionId)); + } else if (extension_type_ == RtpExtension::kTransportSequenceNumber) { + transport_cc = true; + send_config->rtp.extensions.push_back(RtpExtension( + extension_type_.c_str(), kTransportSequenceNumberExtensionId)); + } + + for (AudioReceiveStream::Config& recv_config : *receive_configs) { + recv_config.combined_audio_video_bwe = true; + recv_config.rtp.transport_cc = transport_cc; + recv_config.rtp.extensions = send_config->rtp.extensions; + recv_config.rtp.remote_ssrc = send_config->rtp.ssrc; + } +} + +void RampUpTester::OnCallsCreated(Call* sender_call, Call* receiver_call) { + sender_call_ = sender_call; +} + +bool RampUpTester::BitrateStatsPollingThread(void* obj) { + return static_cast<RampUpTester*>(obj)->PollStats(); +} + +bool RampUpTester::PollStats() { + if (sender_call_) { + Call::Stats stats = sender_call_->GetStats(); + + RTC_DCHECK_GT(expected_bitrate_bps_, 0); + if (!start_bitrate_verified_ && start_bitrate_bps_ != 0) { + // For tests with an explicitly set start bitrate, verify the first + // bitrate estimate is close to the start bitrate and lower than the + // test target bitrate. This is to verify a call respects the configured + // start bitrate, but due to the BWE implementation we can't guarantee the + // first estimate really is as high as the start bitrate. + EXPECT_GT(stats.send_bandwidth_bps, 0.9 * start_bitrate_bps_); + start_bitrate_verified_ = true; + } + if (stats.send_bandwidth_bps >= expected_bitrate_bps_) { + ramp_up_finished_ms_ = clock_->TimeInMilliseconds(); + observation_complete_.Set(); + } + } + + return !event_.Wait(kPollIntervalMs); +} + +void RampUpTester::ReportResult(const std::string& measurement, + size_t value, + const std::string& units) const { + webrtc::test::PrintResult( + measurement, "", + ::testing::UnitTest::GetInstance()->current_test_info()->name(), value, + units, false); +} + +void RampUpTester::AccumulateStats(const VideoSendStream::StreamStats& stream, + size_t* total_packets_sent, + size_t* total_sent, + size_t* padding_sent, + size_t* media_sent) const { + *total_packets_sent += stream.rtp_stats.transmitted.packets + + stream.rtp_stats.retransmitted.packets + + stream.rtp_stats.fec.packets; + *total_sent += stream.rtp_stats.transmitted.TotalBytes() + + stream.rtp_stats.retransmitted.TotalBytes() + + stream.rtp_stats.fec.TotalBytes(); + *padding_sent += stream.rtp_stats.transmitted.padding_bytes + + stream.rtp_stats.retransmitted.padding_bytes + + stream.rtp_stats.fec.padding_bytes; + *media_sent += stream.rtp_stats.MediaPayloadBytes(); +} + +void RampUpTester::TriggerTestDone() { + RTC_DCHECK_GE(test_start_ms_, 0); + + // TODO(holmer): Add audio send stats here too when those APIs are available. + VideoSendStream::Stats send_stats = send_stream_->GetStats(); + + size_t total_packets_sent = 0; + size_t total_sent = 0; + size_t padding_sent = 0; + size_t media_sent = 0; + for (uint32_t ssrc : video_ssrcs_) { + AccumulateStats(send_stats.substreams[ssrc], &total_packets_sent, + &total_sent, &padding_sent, &media_sent); + } + + size_t rtx_total_packets_sent = 0; + size_t rtx_total_sent = 0; + size_t rtx_padding_sent = 0; + size_t rtx_media_sent = 0; + for (uint32_t rtx_ssrc : video_rtx_ssrcs_) { + AccumulateStats(send_stats.substreams[rtx_ssrc], &rtx_total_packets_sent, + &rtx_total_sent, &rtx_padding_sent, &rtx_media_sent); + } + + ReportResult("ramp-up-total-packets-sent", total_packets_sent, "packets"); + ReportResult("ramp-up-total-sent", total_sent, "bytes"); + ReportResult("ramp-up-media-sent", media_sent, "bytes"); + ReportResult("ramp-up-padding-sent", padding_sent, "bytes"); + ReportResult("ramp-up-rtx-total-packets-sent", rtx_total_packets_sent, + "packets"); + ReportResult("ramp-up-rtx-total-sent", rtx_total_sent, "bytes"); + ReportResult("ramp-up-rtx-media-sent", rtx_media_sent, "bytes"); + ReportResult("ramp-up-rtx-padding-sent", rtx_padding_sent, "bytes"); + if (ramp_up_finished_ms_ >= 0) { + ReportResult("ramp-up-time", ramp_up_finished_ms_ - test_start_ms_, + "milliseconds"); + } + ReportResult("ramp-up-average-network-latency", + send_transport_->GetAverageDelayMs(), "milliseconds"); +} + +void RampUpTester::PerformTest() { + test_start_ms_ = clock_->TimeInMilliseconds(); + poller_thread_.Start(); + EXPECT_TRUE(Wait()) << "Timed out while waiting for ramp-up to complete."; + TriggerTestDone(); + poller_thread_.Stop(); +} + +RampUpDownUpTester::RampUpDownUpTester(size_t num_video_streams, + size_t num_audio_streams, + unsigned int start_bitrate_bps, + const std::string& extension_type, + bool rtx, + bool red) + : RampUpTester(num_video_streams, + num_audio_streams, + start_bitrate_bps, + extension_type, + rtx, + red), + test_state_(kFirstRampup), + state_start_ms_(clock_->TimeInMilliseconds()), + interval_start_ms_(clock_->TimeInMilliseconds()), + sent_bytes_(0) { + forward_transport_config_.link_capacity_kbps = kHighBandwidthLimitBps / 1000; +} + +RampUpDownUpTester::~RampUpDownUpTester() {} + +bool RampUpDownUpTester::PollStats() { + if (send_stream_) { + webrtc::VideoSendStream::Stats stats = send_stream_->GetStats(); + int transmit_bitrate_bps = 0; + for (auto it : stats.substreams) { + transmit_bitrate_bps += it.second.total_bitrate_bps; + } + + EvolveTestState(transmit_bitrate_bps, stats.suspended); + } + + return !event_.Wait(kPollIntervalMs); +} + +Call::Config RampUpDownUpTester::GetReceiverCallConfig() { + Call::Config config; + config.bitrate_config.min_bitrate_bps = 10000; + return config; +} + +std::string RampUpDownUpTester::GetModifierString() const { + std::string str("_"); + if (num_video_streams_ > 0) { + std::ostringstream s; + s << num_video_streams_; + str += s.str(); + str += "stream"; + str += (num_video_streams_ > 1 ? "s" : ""); + str += "_"; + } + if (num_audio_streams_ > 0) { + std::ostringstream s; + s << num_audio_streams_; + str += s.str(); + str += "stream"; + str += (num_audio_streams_ > 1 ? "s" : ""); + str += "_"; + } + str += (rtx_ ? "" : "no"); + str += "rtx"; + return str; +} + +void RampUpDownUpTester::EvolveTestState(int bitrate_bps, bool suspended) { + int64_t now = clock_->TimeInMilliseconds(); + switch (test_state_) { + case kFirstRampup: { + EXPECT_FALSE(suspended); + if (bitrate_bps > kExpectedHighBitrateBps) { + // The first ramp-up has reached the target bitrate. Change the + // channel limit, and move to the next test state. + forward_transport_config_.link_capacity_kbps = + kLowBandwidthLimitBps / 1000; + send_transport_->SetConfig(forward_transport_config_); + test_state_ = kLowRate; + webrtc::test::PrintResult("ramp_up_down_up", GetModifierString(), + "first_rampup", now - state_start_ms_, "ms", + false); + state_start_ms_ = now; + interval_start_ms_ = now; + sent_bytes_ = 0; + } + break; + } + case kLowRate: { + if (bitrate_bps < kExpectedLowBitrateBps && suspended) { + // The ramp-down was successful. Change the channel limit back to a + // high value, and move to the next test state. + forward_transport_config_.link_capacity_kbps = + kHighBandwidthLimitBps / 1000; + send_transport_->SetConfig(forward_transport_config_); + test_state_ = kSecondRampup; + webrtc::test::PrintResult("ramp_up_down_up", GetModifierString(), + "rampdown", now - state_start_ms_, "ms", + false); + state_start_ms_ = now; + interval_start_ms_ = now; + sent_bytes_ = 0; + } + break; + } + case kSecondRampup: { + if (bitrate_bps > kExpectedHighBitrateBps && !suspended) { + webrtc::test::PrintResult("ramp_up_down_up", GetModifierString(), + "second_rampup", now - state_start_ms_, "ms", + false); + ReportResult("ramp-up-down-up-average-network-latency", + send_transport_->GetAverageDelayMs(), "milliseconds"); + observation_complete_.Set(); + } + break; + } + } +} + +class RampUpTest : public test::CallTest { + public: + RampUpTest() {} + + virtual ~RampUpTest() { + EXPECT_EQ(nullptr, video_send_stream_); + EXPECT_TRUE(video_receive_streams_.empty()); + } +}; + +TEST_F(RampUpTest, SingleStream) { + RampUpTester test(1, 0, 0, RtpExtension::kTOffset, false, false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, Simulcast) { + RampUpTester test(3, 0, 0, RtpExtension::kTOffset, false, false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, SimulcastWithRtx) { + RampUpTester test(3, 0, 0, RtpExtension::kTOffset, true, false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, SimulcastByRedWithRtx) { + RampUpTester test(3, 0, 0, RtpExtension::kTOffset, true, true); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, SingleStreamWithHighStartBitrate) { + RampUpTester test(1, 0, 0.9 * kSingleStreamTargetBps, RtpExtension::kTOffset, + false, false); + RunBaseTest(&test); +} + +// Disabled on Mac due to flakiness, see +// https://bugs.chromium.org/p/webrtc/issues/detail?id=5407 +#ifndef WEBRTC_MAC + +static const uint32_t kStartBitrateBps = 60000; + +TEST_F(RampUpTest, UpDownUpOneStream) { + RampUpDownUpTester test(1, 0, kStartBitrateBps, RtpExtension::kAbsSendTime, + false, false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, UpDownUpThreeStreams) { + RampUpDownUpTester test(3, 0, kStartBitrateBps, RtpExtension::kAbsSendTime, + false, false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, UpDownUpOneStreamRtx) { + RampUpDownUpTester test(1, 0, kStartBitrateBps, RtpExtension::kAbsSendTime, + true, false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, UpDownUpThreeStreamsRtx) { + RampUpDownUpTester test(3, 0, kStartBitrateBps, RtpExtension::kAbsSendTime, + true, false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, UpDownUpOneStreamByRedRtx) { + RampUpDownUpTester test(1, 0, kStartBitrateBps, RtpExtension::kAbsSendTime, + true, true); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, UpDownUpThreeStreamsByRedRtx) { + RampUpDownUpTester test(3, 0, kStartBitrateBps, RtpExtension::kAbsSendTime, + true, true); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, SendSideVideoUpDownUpRtx) { + RampUpDownUpTester test(3, 0, kStartBitrateBps, + RtpExtension::kTransportSequenceNumber, true, false); + RunBaseTest(&test); +} + +// TODO(holmer): Enable when audio bitrates are included in the bitrate +// allocation. +TEST_F(RampUpTest, DISABLED_SendSideAudioVideoUpDownUpRtx) { + RampUpDownUpTester test(3, 1, kStartBitrateBps, + RtpExtension::kTransportSequenceNumber, true, false); + RunBaseTest(&test); +} + +#endif + +TEST_F(RampUpTest, AbsSendTimeSingleStream) { + RampUpTester test(1, 0, 0, RtpExtension::kAbsSendTime, false, false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, AbsSendTimeSimulcast) { + RampUpTester test(3, 0, 0, RtpExtension::kAbsSendTime, false, false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, AbsSendTimeSimulcastWithRtx) { + RampUpTester test(3, 0, 0, RtpExtension::kAbsSendTime, true, false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, AbsSendTimeSimulcastByRedWithRtx) { + RampUpTester test(3, 0, 0, RtpExtension::kAbsSendTime, true, true); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, AbsSendTimeSingleStreamWithHighStartBitrate) { + RampUpTester test(1, 0, 0.9 * kSingleStreamTargetBps, + RtpExtension::kAbsSendTime, false, false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, TransportSequenceNumberSingleStream) { + RampUpTester test(1, 0, 0, RtpExtension::kTransportSequenceNumber, false, + false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, TransportSequenceNumberSimulcast) { + RampUpTester test(3, 0, 0, RtpExtension::kTransportSequenceNumber, false, + false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, TransportSequenceNumberSimulcastWithRtx) { + RampUpTester test(3, 0, 0, RtpExtension::kTransportSequenceNumber, true, + false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, AudioVideoTransportSequenceNumberSimulcastWithRtx) { + RampUpTester test(3, 1, 0, RtpExtension::kTransportSequenceNumber, true, + false); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, TransportSequenceNumberSimulcastByRedWithRtx) { + RampUpTester test(3, 0, 0, RtpExtension::kTransportSequenceNumber, true, + true); + RunBaseTest(&test); +} + +TEST_F(RampUpTest, TransportSequenceNumberSingleStreamWithHighStartBitrate) { + RampUpTester test(1, 0, 0.9 * kSingleStreamTargetBps, + RtpExtension::kTransportSequenceNumber, false, false); + RunBaseTest(&test); +} +} // namespace webrtc diff --git a/webrtc/call/rampup_tests.h b/webrtc/call/rampup_tests.h new file mode 100644 index 0000000000..31a0a0296e --- /dev/null +++ b/webrtc/call/rampup_tests.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014 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. + */ + +#ifndef WEBRTC_CALL_RAMPUP_TESTS_H_ +#define WEBRTC_CALL_RAMPUP_TESTS_H_ + +#include <map> +#include <string> +#include <vector> + +#include "webrtc/base/event.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/call.h" +#include "webrtc/test/call_test.h" + +namespace webrtc { + +static const int kTransmissionTimeOffsetExtensionId = 6; +static const int kAbsSendTimeExtensionId = 7; +static const int kTransportSequenceNumberExtensionId = 8; +static const unsigned int kSingleStreamTargetBps = 1000000; + +class Clock; + +class RampUpTester : public test::EndToEndTest { + public: + RampUpTester(size_t num_video_streams, + size_t num_audio_streams, + unsigned int start_bitrate_bps, + const std::string& extension_type, + bool rtx, + bool red); + ~RampUpTester() override; + + size_t GetNumVideoStreams() const override; + size_t GetNumAudioStreams() const override; + + void PerformTest() override; + + protected: + virtual bool PollStats(); + + void AccumulateStats(const VideoSendStream::StreamStats& stream, + size_t* total_packets_sent, + size_t* total_sent, + size_t* padding_sent, + size_t* media_sent) const; + + void ReportResult(const std::string& measurement, + size_t value, + const std::string& units) const; + void TriggerTestDone(); + + rtc::Event event_; + Clock* const clock_; + FakeNetworkPipe::Config forward_transport_config_; + const size_t num_video_streams_; + const size_t num_audio_streams_; + const bool rtx_; + const bool red_; + VideoSendStream* send_stream_; + test::PacketTransport* send_transport_; + + private: + typedef std::map<uint32_t, uint32_t> SsrcMap; + + Call::Config GetSenderCallConfig() override; + void OnVideoStreamsCreated( + VideoSendStream* send_stream, + const std::vector<VideoReceiveStream*>& receive_streams) override; + test::PacketTransport* CreateSendTransport(Call* sender_call) override; + void ModifyVideoConfigs( + VideoSendStream::Config* send_config, + std::vector<VideoReceiveStream::Config>* receive_configs, + VideoEncoderConfig* encoder_config) override; + void ModifyAudioConfigs( + AudioSendStream::Config* send_config, + std::vector<AudioReceiveStream::Config>* receive_configs) override; + void OnCallsCreated(Call* sender_call, Call* receiver_call) override; + + static bool BitrateStatsPollingThread(void* obj); + + const int start_bitrate_bps_; + bool start_bitrate_verified_; + int expected_bitrate_bps_; + int64_t test_start_ms_; + int64_t ramp_up_finished_ms_; + + const std::string extension_type_; + std::vector<uint32_t> video_ssrcs_; + std::vector<uint32_t> video_rtx_ssrcs_; + std::vector<uint32_t> audio_ssrcs_; + SsrcMap rtx_ssrc_map_; + + rtc::PlatformThread poller_thread_; + Call* sender_call_; +}; + +class RampUpDownUpTester : public RampUpTester { + public: + RampUpDownUpTester(size_t num_video_streams, + size_t num_audio_streams, + unsigned int start_bitrate_bps, + const std::string& extension_type, + bool rtx, + bool red); + ~RampUpDownUpTester() override; + + protected: + bool PollStats() override; + + private: + static const int kHighBandwidthLimitBps = 80000; + static const int kExpectedHighBitrateBps = 60000; + static const int kLowBandwidthLimitBps = 20000; + static const int kExpectedLowBitrateBps = 20000; + enum TestStates { kFirstRampup, kLowRate, kSecondRampup }; + + Call::Config GetReceiverCallConfig() override; + + std::string GetModifierString() const; + void EvolveTestState(int bitrate_bps, bool suspended); + + TestStates test_state_; + int64_t state_start_ms_; + int64_t interval_start_ms_; + int sent_bytes_; +}; +} // namespace webrtc +#endif // WEBRTC_CALL_RAMPUP_TESTS_H_ diff --git a/webrtc/call/rtc_event_log.cc b/webrtc/call/rtc_event_log.cc index 550b556e80..9f592ce479 100644 --- a/webrtc/call/rtc_event_log.cc +++ b/webrtc/call/rtc_event_log.cc @@ -17,7 +17,9 @@ #include "webrtc/base/criticalsection.h" #include "webrtc/base/thread_annotations.h" #include "webrtc/call.h" +#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "webrtc/modules/rtp_rtcp/source/byte_io.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" #include "webrtc/system_wrappers/include/clock.h" #include "webrtc/system_wrappers/include/file_wrapper.h" @@ -54,6 +56,9 @@ class RtcEventLogImpl final : public RtcEventLog { const uint8_t* packet, size_t length) override {} void LogAudioPlayout(uint32_t ssrc) override {} + void LogBwePacketLossEvent(int32_t bitrate, + uint8_t fraction_loss, + int32_t total_packets) override {} }; #else // ENABLE_RTC_EVENT_LOG is defined @@ -78,6 +83,9 @@ class RtcEventLogImpl final : public RtcEventLog { const uint8_t* packet, size_t length) override; void LogAudioPlayout(uint32_t ssrc) override; + void LogBwePacketLossEvent(int32_t bitrate, + uint8_t fraction_loss, + int32_t total_packets) override; private: // Starts logging. This function assumes the file_ has been opened succesfully @@ -254,8 +262,7 @@ void RtcEventLogImpl::LogVideoReceiveStreamConfig( rtc::CritScope lock(&crit_); rtclog::Event event; - const int64_t timestamp = clock_->TimeInMicroseconds(); - event.set_timestamp_us(timestamp); + event.set_timestamp_us(clock_->TimeInMicroseconds()); event.set_type(rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT); rtclog::VideoReceiveConfig* receiver_config = @@ -264,9 +271,6 @@ void RtcEventLogImpl::LogVideoReceiveStreamConfig( receiver_config->set_local_ssrc(config.rtp.local_ssrc); receiver_config->set_rtcp_mode(ConvertRtcpMode(config.rtp.rtcp_mode)); - - receiver_config->set_receiver_reference_time_report( - config.rtp.rtcp_xr.receiver_reference_time_report); receiver_config->set_remb(config.rtp.remb); for (const auto& kv : config.rtp.rtx) { @@ -296,8 +300,7 @@ void RtcEventLogImpl::LogVideoSendStreamConfig( rtc::CritScope lock(&crit_); rtclog::Event event; - const int64_t timestamp = clock_->TimeInMicroseconds(); - event.set_timestamp_us(timestamp); + event.set_timestamp_us(clock_->TimeInMicroseconds()); event.set_type(rtclog::Event::VIDEO_SENDER_CONFIG_EVENT); rtclog::VideoSendConfig* sender_config = event.mutable_video_sender_config(); @@ -318,8 +321,6 @@ void RtcEventLogImpl::LogVideoSendStreamConfig( } sender_config->set_rtx_payload_type(config.rtp.rtx.payload_type); - sender_config->set_c_name(config.rtp.c_name); - rtclog::EncoderConfig* encoder = sender_config->mutable_encoder(); encoder->set_name(config.encoder_settings.payload_name); encoder->set_payload_type(config.encoder_settings.payload_type); @@ -348,8 +349,7 @@ void RtcEventLogImpl::LogRtpHeader(bool incoming, rtc::CritScope lock(&crit_); rtclog::Event rtp_event; - const int64_t timestamp = clock_->TimeInMicroseconds(); - rtp_event.set_timestamp_us(timestamp); + rtp_event.set_timestamp_us(clock_->TimeInMicroseconds()); rtp_event.set_type(rtclog::Event::RTP_EVENT); rtp_event.mutable_rtp_packet()->set_incoming(incoming); rtp_event.mutable_rtp_packet()->set_type(ConvertMediaType(media_type)); @@ -364,33 +364,89 @@ void RtcEventLogImpl::LogRtcpPacket(bool incoming, size_t length) { rtc::CritScope lock(&crit_); rtclog::Event rtcp_event; - const int64_t timestamp = clock_->TimeInMicroseconds(); - rtcp_event.set_timestamp_us(timestamp); + rtcp_event.set_timestamp_us(clock_->TimeInMicroseconds()); rtcp_event.set_type(rtclog::Event::RTCP_EVENT); rtcp_event.mutable_rtcp_packet()->set_incoming(incoming); rtcp_event.mutable_rtcp_packet()->set_type(ConvertMediaType(media_type)); - rtcp_event.mutable_rtcp_packet()->set_packet_data(packet, length); + + RTCPUtility::RtcpCommonHeader header; + const uint8_t* block_begin = packet; + const uint8_t* packet_end = packet + length; + RTC_DCHECK(length <= IP_PACKET_SIZE); + uint8_t buffer[IP_PACKET_SIZE]; + uint32_t buffer_length = 0; + while (block_begin < packet_end) { + if (!RtcpParseCommonHeader(block_begin, packet_end - block_begin, + &header)) { + break; // Incorrect message header. + } + uint32_t block_size = header.BlockSize(); + switch (header.packet_type) { + case RTCPUtility::PT_SR: + FALLTHROUGH(); + case RTCPUtility::PT_RR: + FALLTHROUGH(); + case RTCPUtility::PT_BYE: + FALLTHROUGH(); + case RTCPUtility::PT_IJ: + FALLTHROUGH(); + case RTCPUtility::PT_RTPFB: + FALLTHROUGH(); + case RTCPUtility::PT_PSFB: + FALLTHROUGH(); + case RTCPUtility::PT_XR: + // We log sender reports, receiver reports, bye messages + // inter-arrival jitter, third-party loss reports, payload-specific + // feedback and extended reports. + memcpy(buffer + buffer_length, block_begin, block_size); + buffer_length += block_size; + break; + case RTCPUtility::PT_SDES: + FALLTHROUGH(); + case RTCPUtility::PT_APP: + FALLTHROUGH(); + default: + // We don't log sender descriptions, application defined messages + // or message blocks of unknown type. + break; + } + + block_begin += block_size; + } + rtcp_event.mutable_rtcp_packet()->set_packet_data(buffer, buffer_length); HandleEvent(&rtcp_event); } void RtcEventLogImpl::LogAudioPlayout(uint32_t ssrc) { rtc::CritScope lock(&crit_); rtclog::Event event; - const int64_t timestamp = clock_->TimeInMicroseconds(); - event.set_timestamp_us(timestamp); + event.set_timestamp_us(clock_->TimeInMicroseconds()); event.set_type(rtclog::Event::AUDIO_PLAYOUT_EVENT); auto playout_event = event.mutable_audio_playout_event(); playout_event->set_local_ssrc(ssrc); HandleEvent(&event); } +void RtcEventLogImpl::LogBwePacketLossEvent(int32_t bitrate, + uint8_t fraction_loss, + int32_t total_packets) { + rtc::CritScope lock(&crit_); + rtclog::Event event; + event.set_timestamp_us(clock_->TimeInMicroseconds()); + event.set_type(rtclog::Event::BWE_PACKET_LOSS_EVENT); + auto bwe_event = event.mutable_bwe_packet_loss_event(); + bwe_event->set_bitrate(bitrate); + bwe_event->set_fraction_loss(fraction_loss); + bwe_event->set_total_packets(total_packets); + HandleEvent(&event); +} + void RtcEventLogImpl::StopLoggingLocked() { if (currently_logging_) { currently_logging_ = false; // Create a LogEnd event rtclog::Event event; - int64_t timestamp = clock_->TimeInMicroseconds(); - event.set_timestamp_us(timestamp); + event.set_timestamp_us(clock_->TimeInMicroseconds()); event.set_type(rtclog::Event::LOG_END); // Store the event and close the file RTC_DCHECK(file_->Open()); diff --git a/webrtc/call/rtc_event_log.h b/webrtc/call/rtc_event_log.h index 85d7525752..489687a195 100644 --- a/webrtc/call/rtc_event_log.h +++ b/webrtc/call/rtc_event_log.h @@ -77,6 +77,11 @@ class RtcEventLog { // Logs an audio playout event virtual void LogAudioPlayout(uint32_t ssrc) = 0; + // Logs a bitrate update from the bandwidth estimator based on packet loss. + virtual void LogBwePacketLossEvent(int32_t bitrate, + uint8_t fraction_loss, + int32_t total_packets) = 0; + // Reads an RtcEventLog file and returns true when reading was successful. // The result is stored in the given EventStream object. static bool ParseRtcEventLog(const std::string& file_name, diff --git a/webrtc/call/rtc_event_log.proto b/webrtc/call/rtc_event_log.proto index 6bdea7bd2f..b14306e362 100644 --- a/webrtc/call/rtc_event_log.proto +++ b/webrtc/call/rtc_event_log.proto @@ -34,10 +34,12 @@ message Event { RTP_EVENT = 3; RTCP_EVENT = 4; AUDIO_PLAYOUT_EVENT = 5; - VIDEO_RECEIVER_CONFIG_EVENT = 6; - VIDEO_SENDER_CONFIG_EVENT = 7; - AUDIO_RECEIVER_CONFIG_EVENT = 8; - AUDIO_SENDER_CONFIG_EVENT = 9; + BWE_PACKET_LOSS_EVENT = 6; + BWE_PACKET_DELAY_EVENT = 7; + VIDEO_RECEIVER_CONFIG_EVENT = 8; + VIDEO_SENDER_CONFIG_EVENT = 9; + AUDIO_RECEIVER_CONFIG_EVENT = 10; + AUDIO_SENDER_CONFIG_EVENT = 11; } // required - Indicates the type of this event @@ -52,17 +54,20 @@ message Event { // optional - but required if type == AUDIO_PLAYOUT_EVENT optional AudioPlayoutEvent audio_playout_event = 5; + // optional - but required if type == BWE_PACKET_LOSS_EVENT + optional BwePacketLossEvent bwe_packet_loss_event = 6; + // optional - but required if type == VIDEO_RECEIVER_CONFIG_EVENT - optional VideoReceiveConfig video_receiver_config = 6; + optional VideoReceiveConfig video_receiver_config = 8; // optional - but required if type == VIDEO_SENDER_CONFIG_EVENT - optional VideoSendConfig video_sender_config = 7; + optional VideoSendConfig video_sender_config = 9; // optional - but required if type == AUDIO_RECEIVER_CONFIG_EVENT - optional AudioReceiveConfig audio_receiver_config = 8; + optional AudioReceiveConfig audio_receiver_config = 10; // optional - but required if type == AUDIO_SENDER_CONFIG_EVENT - optional AudioSendConfig audio_sender_config = 9; + optional AudioSendConfig audio_sender_config = 11; } @@ -99,6 +104,19 @@ message AudioPlayoutEvent { optional uint32 local_ssrc = 2; } +message BwePacketLossEvent { + // required - Bandwidth estimate (in bps) after the update. + optional int32 bitrate = 1; + + // required - Fraction of lost packets since last receiver report + // computed as floor( 256 * (#lost_packets / #total_packets) ). + // The possible values range from 0 to 255. + optional uint32 fraction_loss = 2; + + // TODO(terelius): Is this really needed? Remove or make optional? + // required - Total number of packets that the BWE update is based on. + optional int32 total_packets = 3; +} // TODO(terelius): Video and audio streams could in principle share SSRC, // so identifying a stream based only on SSRC might not work. @@ -119,20 +137,17 @@ message VideoReceiveConfig { // required - RTCP mode to use. optional RtcpMode rtcp_mode = 3; - // required - Extended RTCP settings. - optional bool receiver_reference_time_report = 4; - // required - Receiver estimated maximum bandwidth. - optional bool remb = 5; + optional bool remb = 4; // Map from video RTP payload type -> RTX config. - repeated RtxMap rtx_map = 6; + repeated RtxMap rtx_map = 5; // RTP header extensions used for the received stream. - repeated RtpHeaderExtension header_extensions = 7; + repeated RtpHeaderExtension header_extensions = 6; // List of decoders associated with the stream. - repeated DecoderConfig decoders = 8; + repeated DecoderConfig decoders = 7; } @@ -142,7 +157,7 @@ message DecoderConfig { optional string name = 1; // required - optional sint32 payload_type = 2; + optional int32 payload_type = 2; } @@ -152,7 +167,7 @@ message RtpHeaderExtension { optional string name = 1; // required - optional sint32 id = 2; + optional int32 id = 2; } @@ -163,13 +178,13 @@ message RtxConfig { optional uint32 rtx_ssrc = 1; // required - Payload type to use for the RTX stream. - optional sint32 rtx_payload_type = 2; + optional int32 rtx_payload_type = 2; } message RtxMap { // required - optional sint32 payload_type = 1; + optional int32 payload_type = 1; // required optional RtxConfig config = 2; @@ -189,13 +204,10 @@ message VideoSendConfig { repeated uint32 rtx_ssrcs = 3; // required if rtx_ssrcs is used - Payload type for retransmitted packets. - optional sint32 rtx_payload_type = 4; - - // required - Canonical end-point identifier. - optional string c_name = 5; + optional int32 rtx_payload_type = 4; // required - Encoder associated with the stream. - optional EncoderConfig encoder = 6; + optional EncoderConfig encoder = 5; } @@ -205,7 +217,7 @@ message EncoderConfig { optional string name = 1; // required - optional sint32 payload_type = 2; + optional int32 payload_type = 2; } diff --git a/webrtc/call/rtc_event_log_unittest.cc b/webrtc/call/rtc_event_log_unittest.cc index a4fdd13512..f590f669a2 100644 --- a/webrtc/call/rtc_event_log_unittest.cc +++ b/webrtc/call/rtc_event_log_unittest.cc @@ -10,22 +10,23 @@ #ifdef ENABLE_RTC_EVENT_LOG -#include <stdio.h> #include <string> +#include <utility> #include <vector> #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/base/buffer.h" #include "webrtc/base/checks.h" +#include "webrtc/base/random.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/base/thread.h" #include "webrtc/call.h" #include "webrtc/call/rtc_event_log.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" #include "webrtc/modules/rtp_rtcp/source/rtp_sender.h" #include "webrtc/system_wrappers/include/clock.h" #include "webrtc/test/test_suite.h" #include "webrtc/test/testsupport/fileutils.h" -#include "webrtc/test/testsupport/gtest_disable.h" // Files generated at build-time by the protobuf compiler. #ifdef WEBRTC_ANDROID_PLATFORM_BUILD @@ -138,9 +139,6 @@ void VerifyReceiveStreamConfig(const rtclog::Event& event, else EXPECT_EQ(rtclog::VideoReceiveConfig::RTCP_REDUCEDSIZE, receiver_config.rtcp_mode()); - ASSERT_TRUE(receiver_config.has_receiver_reference_time_report()); - EXPECT_EQ(config.rtp.rtcp_xr.receiver_reference_time_report, - receiver_config.receiver_reference_time_report()); ASSERT_TRUE(receiver_config.has_remb()); EXPECT_EQ(config.rtp.remb, receiver_config.remb()); // Check RTX map. @@ -214,9 +212,6 @@ void VerifySendStreamConfig(const rtclog::Event& event, ASSERT_TRUE(sender_config.has_rtx_payload_type()); EXPECT_EQ(config.rtp.rtx.payload_type, sender_config.rtx_payload_type()); } - // Check CNAME. - ASSERT_TRUE(sender_config.has_c_name()); - EXPECT_EQ(config.rtp.c_name, sender_config.c_name()); // Check encoder. ASSERT_TRUE(sender_config.has_encoder()); ASSERT_TRUE(sender_config.encoder().has_name()); @@ -230,7 +225,7 @@ void VerifySendStreamConfig(const rtclog::Event& event, void VerifyRtpEvent(const rtclog::Event& event, bool incoming, MediaType media_type, - uint8_t* header, + const uint8_t* header, size_t header_size, size_t total_size) { ASSERT_TRUE(IsValidBasicEvent(event)); @@ -252,7 +247,7 @@ void VerifyRtpEvent(const rtclog::Event& event, void VerifyRtcpEvent(const rtclog::Event& event, bool incoming, MediaType media_type, - uint8_t* packet, + const uint8_t* packet, size_t total_size) { ASSERT_TRUE(IsValidBasicEvent(event)); ASSERT_EQ(rtclog::Event::RTCP_EVENT, event.type()); @@ -276,6 +271,21 @@ void VerifyPlayoutEvent(const rtclog::Event& event, uint32_t ssrc) { EXPECT_EQ(ssrc, playout_event.local_ssrc()); } +void VerifyBweLossEvent(const rtclog::Event& event, + int32_t bitrate, + uint8_t fraction_loss, + int32_t total_packets) { + ASSERT_TRUE(IsValidBasicEvent(event)); + ASSERT_EQ(rtclog::Event::BWE_PACKET_LOSS_EVENT, event.type()); + const rtclog::BwePacketLossEvent& bwe_event = event.bwe_packet_loss_event(); + ASSERT_TRUE(bwe_event.has_bitrate()); + EXPECT_EQ(bitrate, bwe_event.bitrate()); + ASSERT_TRUE(bwe_event.has_fraction_loss()); + EXPECT_EQ(fraction_loss, bwe_event.fraction_loss()); + ASSERT_TRUE(bwe_event.has_total_packets()); + EXPECT_EQ(total_packets, bwe_event.total_packets()); +} + void VerifyLogStartEvent(const rtclog::Event& event) { ASSERT_TRUE(IsValidBasicEvent(event)); EXPECT_EQ(rtclog::Event::LOG_START, event.type()); @@ -289,7 +299,8 @@ void VerifyLogStartEvent(const rtclog::Event& event) { size_t GenerateRtpPacket(uint32_t extensions_bitvector, uint32_t csrcs_count, uint8_t* packet, - size_t packet_size) { + size_t packet_size, + Random* prng) { RTC_CHECK_GE(packet_size, 16 + 4 * csrcs_count + 4 * kNumExtensions); Clock* clock = Clock::GetRealTimeClock(); @@ -306,12 +317,12 @@ size_t GenerateRtpPacket(uint32_t extensions_bitvector, std::vector<uint32_t> csrcs; for (unsigned i = 0; i < csrcs_count; i++) { - csrcs.push_back(rand()); + csrcs.push_back(prng->Rand<uint32_t>()); } rtp_sender.SetCsrcs(csrcs); - rtp_sender.SetSSRC(rand()); - rtp_sender.SetStartTimestamp(rand(), true); - rtp_sender.SetSequenceNumber(rand()); + rtp_sender.SetSSRC(prng->Rand<uint32_t>()); + rtp_sender.SetStartTimestamp(prng->Rand<uint32_t>(), true); + rtp_sender.SetSequenceNumber(prng->Rand<uint16_t>()); for (unsigned i = 0; i < kNumExtensions; i++) { if (extensions_bitvector & (1u << i)) { @@ -319,76 +330,84 @@ size_t GenerateRtpPacket(uint32_t extensions_bitvector, } } - int8_t payload_type = rand() % 128; - bool marker_bit = (rand() % 2 == 1); - uint32_t capture_timestamp = rand(); - int64_t capture_time_ms = rand(); - bool timestamp_provided = (rand() % 2 == 1); - bool inc_sequence_number = (rand() % 2 == 1); + int8_t payload_type = prng->Rand(0, 127); + bool marker_bit = prng->Rand<bool>(); + uint32_t capture_timestamp = prng->Rand<uint32_t>(); + int64_t capture_time_ms = prng->Rand<uint32_t>(); + bool timestamp_provided = prng->Rand<bool>(); + bool inc_sequence_number = prng->Rand<bool>(); size_t header_size = rtp_sender.BuildRTPheader( packet, payload_type, marker_bit, capture_timestamp, capture_time_ms, timestamp_provided, inc_sequence_number); for (size_t i = header_size; i < packet_size; i++) { - packet[i] = rand(); + packet[i] = prng->Rand<uint8_t>(); } return header_size; } -void GenerateRtcpPacket(uint8_t* packet, size_t packet_size) { - for (size_t i = 0; i < packet_size; i++) { - packet[i] = rand(); - } +rtc::scoped_ptr<rtcp::RawPacket> GenerateRtcpPacket(Random* prng) { + rtcp::ReportBlock report_block; + report_block.To(prng->Rand<uint32_t>()); // Remote SSRC. + report_block.WithFractionLost(prng->Rand(50)); + + rtcp::SenderReport sender_report; + sender_report.From(prng->Rand<uint32_t>()); // Sender SSRC. + sender_report.WithNtpSec(prng->Rand<uint32_t>()); + sender_report.WithNtpFrac(prng->Rand<uint32_t>()); + sender_report.WithPacketCount(prng->Rand<uint32_t>()); + sender_report.WithReportBlock(report_block); + + return sender_report.Build(); } void GenerateVideoReceiveConfig(uint32_t extensions_bitvector, - VideoReceiveStream::Config* config) { + VideoReceiveStream::Config* config, + Random* prng) { // Create a map from a payload type to an encoder name. VideoReceiveStream::Decoder decoder; - decoder.payload_type = rand(); - decoder.payload_name = (rand() % 2 ? "VP8" : "H264"); + decoder.payload_type = prng->Rand(0, 127); + decoder.payload_name = (prng->Rand<bool>() ? "VP8" : "H264"); config->decoders.push_back(decoder); // Add SSRCs for the stream. - config->rtp.remote_ssrc = rand(); - config->rtp.local_ssrc = rand(); + config->rtp.remote_ssrc = prng->Rand<uint32_t>(); + config->rtp.local_ssrc = prng->Rand<uint32_t>(); // Add extensions and settings for RTCP. config->rtp.rtcp_mode = - rand() % 2 ? RtcpMode::kCompound : RtcpMode::kReducedSize; - config->rtp.rtcp_xr.receiver_reference_time_report = (rand() % 2 == 1); - config->rtp.remb = (rand() % 2 == 1); + prng->Rand<bool>() ? RtcpMode::kCompound : RtcpMode::kReducedSize; + config->rtp.remb = prng->Rand<bool>(); // Add a map from a payload type to a new ssrc and a new payload type for RTX. VideoReceiveStream::Config::Rtp::Rtx rtx_pair; - rtx_pair.ssrc = rand(); - rtx_pair.payload_type = rand(); - config->rtp.rtx.insert(std::make_pair(rand(), rtx_pair)); + rtx_pair.ssrc = prng->Rand<uint32_t>(); + rtx_pair.payload_type = prng->Rand(0, 127); + config->rtp.rtx.insert(std::make_pair(prng->Rand(0, 127), rtx_pair)); // Add header extensions. for (unsigned i = 0; i < kNumExtensions; i++) { if (extensions_bitvector & (1u << i)) { config->rtp.extensions.push_back( - RtpExtension(kExtensionNames[i], rand())); + RtpExtension(kExtensionNames[i], prng->Rand<int>())); } } } void GenerateVideoSendConfig(uint32_t extensions_bitvector, - VideoSendStream::Config* config) { + VideoSendStream::Config* config, + Random* prng) { // Create a map from a payload type to an encoder name. - config->encoder_settings.payload_type = rand(); - config->encoder_settings.payload_name = (rand() % 2 ? "VP8" : "H264"); + config->encoder_settings.payload_type = prng->Rand(0, 127); + config->encoder_settings.payload_name = (prng->Rand<bool>() ? "VP8" : "H264"); // Add SSRCs for the stream. - config->rtp.ssrcs.push_back(rand()); + config->rtp.ssrcs.push_back(prng->Rand<uint32_t>()); // Add a map from a payload type to new ssrcs and a new payload type for RTX. - config->rtp.rtx.ssrcs.push_back(rand()); - config->rtp.rtx.payload_type = rand(); - // Add a CNAME. - config->rtp.c_name = "some.user@some.host"; + config->rtp.rtx.ssrcs.push_back(prng->Rand<uint32_t>()); + config->rtp.rtx.payload_type = prng->Rand(0, 127); // Add header extensions. for (unsigned i = 0; i < kNumExtensions; i++) { if (extensions_bitvector & (1u << i)) { config->rtp.extensions.push_back( - RtpExtension(kExtensionNames[i], rand())); + RtpExtension(kExtensionNames[i], prng->Rand<int>())); } } } @@ -398,42 +417,49 @@ void GenerateVideoSendConfig(uint32_t extensions_bitvector, void LogSessionAndReadBack(size_t rtp_count, size_t rtcp_count, size_t playout_count, + size_t bwe_loss_count, uint32_t extensions_bitvector, uint32_t csrcs_count, unsigned int random_seed) { ASSERT_LE(rtcp_count, rtp_count); ASSERT_LE(playout_count, rtp_count); + ASSERT_LE(bwe_loss_count, rtp_count); std::vector<rtc::Buffer> rtp_packets; - std::vector<rtc::Buffer> rtcp_packets; + std::vector<rtc::scoped_ptr<rtcp::RawPacket> > rtcp_packets; std::vector<size_t> rtp_header_sizes; std::vector<uint32_t> playout_ssrcs; + std::vector<std::pair<int32_t, uint8_t> > bwe_loss_updates; VideoReceiveStream::Config receiver_config(nullptr); VideoSendStream::Config sender_config(nullptr); - srand(random_seed); + Random prng(random_seed); // Create rtp_count RTP packets containing random data. for (size_t i = 0; i < rtp_count; i++) { - size_t packet_size = 1000 + rand() % 64; + size_t packet_size = prng.Rand(1000, 1100); rtp_packets.push_back(rtc::Buffer(packet_size)); - size_t header_size = GenerateRtpPacket(extensions_bitvector, csrcs_count, - rtp_packets[i].data(), packet_size); + size_t header_size = + GenerateRtpPacket(extensions_bitvector, csrcs_count, + rtp_packets[i].data(), packet_size, &prng); rtp_header_sizes.push_back(header_size); } // Create rtcp_count RTCP packets containing random data. for (size_t i = 0; i < rtcp_count; i++) { - size_t packet_size = 1000 + rand() % 64; - rtcp_packets.push_back(rtc::Buffer(packet_size)); - GenerateRtcpPacket(rtcp_packets[i].data(), packet_size); + rtcp_packets.push_back(GenerateRtcpPacket(&prng)); } // Create playout_count random SSRCs to use when logging AudioPlayout events. for (size_t i = 0; i < playout_count; i++) { - playout_ssrcs.push_back(static_cast<uint32_t>(rand())); + playout_ssrcs.push_back(prng.Rand<uint32_t>()); + } + // Create bwe_loss_count random bitrate updates for BwePacketLoss. + for (size_t i = 0; i < bwe_loss_count; i++) { + bwe_loss_updates.push_back( + std::make_pair(prng.Rand<int32_t>(), prng.Rand<uint8_t>())); } // Create configurations for the video streams. - GenerateVideoReceiveConfig(extensions_bitvector, &receiver_config); - GenerateVideoSendConfig(extensions_bitvector, &sender_config); + GenerateVideoReceiveConfig(extensions_bitvector, &receiver_config, &prng); + GenerateVideoSendConfig(extensions_bitvector, &sender_config, &prng); const int config_count = 2; // Find the name of the current test, in order to use it as a temporary @@ -448,7 +474,9 @@ void LogSessionAndReadBack(size_t rtp_count, rtc::scoped_ptr<RtcEventLog> log_dumper(RtcEventLog::Create()); log_dumper->LogVideoReceiveStreamConfig(receiver_config); log_dumper->LogVideoSendStreamConfig(sender_config); - size_t rtcp_index = 1, playout_index = 1; + size_t rtcp_index = 1; + size_t playout_index = 1; + size_t bwe_loss_index = 1; for (size_t i = 1; i <= rtp_count; i++) { log_dumper->LogRtpHeader( (i % 2 == 0), // Every second packet is incoming. @@ -458,14 +486,20 @@ void LogSessionAndReadBack(size_t rtp_count, log_dumper->LogRtcpPacket( rtcp_index % 2 == 0, // Every second packet is incoming rtcp_index % 3 == 0 ? MediaType::AUDIO : MediaType::VIDEO, - rtcp_packets[rtcp_index - 1].data(), - rtcp_packets[rtcp_index - 1].size()); + rtcp_packets[rtcp_index - 1]->Buffer(), + rtcp_packets[rtcp_index - 1]->Length()); rtcp_index++; } if (i * playout_count >= playout_index * rtp_count) { log_dumper->LogAudioPlayout(playout_ssrcs[playout_index - 1]); playout_index++; } + if (i * bwe_loss_count >= bwe_loss_index * rtp_count) { + log_dumper->LogBwePacketLossEvent( + bwe_loss_updates[bwe_loss_index - 1].first, + bwe_loss_updates[bwe_loss_index - 1].second, i); + bwe_loss_index++; + } if (i == rtp_count / 2) { log_dumper->StartLogging(temp_filename, 10000000); } @@ -480,12 +514,15 @@ void LogSessionAndReadBack(size_t rtp_count, // Verify that what we read back from the event log is the same as // what we wrote down. For RTCP we log the full packets, but for // RTP we should only log the header. - const int event_count = - config_count + playout_count + rtcp_count + rtp_count + 1; + const int event_count = config_count + playout_count + bwe_loss_count + + rtcp_count + rtp_count + 1; EXPECT_EQ(event_count, parsed_stream.stream_size()); VerifyReceiveStreamConfig(parsed_stream.stream(0), receiver_config); VerifySendStreamConfig(parsed_stream.stream(1), sender_config); - size_t event_index = config_count, rtcp_index = 1, playout_index = 1; + size_t event_index = config_count; + size_t rtcp_index = 1; + size_t playout_index = 1; + size_t bwe_loss_index = 1; for (size_t i = 1; i <= rtp_count; i++) { VerifyRtpEvent(parsed_stream.stream(event_index), (i % 2 == 0), // Every second packet is incoming. @@ -497,8 +534,8 @@ void LogSessionAndReadBack(size_t rtp_count, VerifyRtcpEvent(parsed_stream.stream(event_index), rtcp_index % 2 == 0, // Every second packet is incoming. rtcp_index % 3 == 0 ? MediaType::AUDIO : MediaType::VIDEO, - rtcp_packets[rtcp_index - 1].data(), - rtcp_packets[rtcp_index - 1].size()); + rtcp_packets[rtcp_index - 1]->Buffer(), + rtcp_packets[rtcp_index - 1]->Length()); event_index++; rtcp_index++; } @@ -508,6 +545,13 @@ void LogSessionAndReadBack(size_t rtp_count, event_index++; playout_index++; } + if (i * bwe_loss_count >= bwe_loss_index * rtp_count) { + VerifyBweLossEvent(parsed_stream.stream(event_index), + bwe_loss_updates[bwe_loss_index - 1].first, + bwe_loss_updates[bwe_loss_index - 1].second, i); + event_index++; + bwe_loss_index++; + } if (i == rtp_count / 2) { VerifyLogStartEvent(parsed_stream.stream(event_index)); event_index++; @@ -519,10 +563,11 @@ void LogSessionAndReadBack(size_t rtp_count, } TEST(RtcEventLogTest, LogSessionAndReadBack) { - // Log 5 RTP, 2 RTCP, and 0 playout events with no header extensions or CSRCS. - LogSessionAndReadBack(5, 2, 0, 0, 0, 321); + // Log 5 RTP, 2 RTCP, 0 playout events and 0 BWE events + // with no header extensions or CSRCS. + LogSessionAndReadBack(5, 2, 0, 0, 0, 0, 321); - // Enable AbsSendTime and TransportSequenceNumbers + // Enable AbsSendTime and TransportSequenceNumbers. uint32_t extensions = 0; for (uint32_t i = 0; i < kNumExtensions; i++) { if (kExtensionTypes[i] == RTPExtensionType::kRtpExtensionAbsoluteSendTime || @@ -531,20 +576,21 @@ TEST(RtcEventLogTest, LogSessionAndReadBack) { extensions |= 1u << i; } } - LogSessionAndReadBack(8, 2, 0, extensions, 0, 3141592653u); + LogSessionAndReadBack(8, 2, 0, 0, extensions, 0, 3141592653u); - extensions = (1u << kNumExtensions) - 1; // Enable all header extensions - LogSessionAndReadBack(9, 2, 3, extensions, 2, 2718281828u); + extensions = (1u << kNumExtensions) - 1; // Enable all header extensions. + LogSessionAndReadBack(9, 2, 3, 2, extensions, 2, 2718281828u); // Try all combinations of header extensions and up to 2 CSRCS. for (extensions = 0; extensions < (1u << kNumExtensions); extensions++) { for (uint32_t csrcs_count = 0; csrcs_count < 3; csrcs_count++) { LogSessionAndReadBack(5 + extensions, // Number of RTP packets. 2 + csrcs_count, // Number of RTCP packets. - 3 + csrcs_count, // Number of playout events - extensions, // Bit vector choosing extensions - csrcs_count, // Number of contributing sources - rand()); + 3 + csrcs_count, // Number of playout events. + 1 + csrcs_count, // Number of BWE loss events. + extensions, // Bit vector choosing extensions. + csrcs_count, // Number of contributing sources. + extensions * 3 + csrcs_count + 1); // Random seed. } } } @@ -556,35 +602,32 @@ void DropOldEvents(uint32_t extensions_bitvector, unsigned int random_seed) { rtc::Buffer old_rtp_packet; rtc::Buffer recent_rtp_packet; - rtc::Buffer old_rtcp_packet; - rtc::Buffer recent_rtcp_packet; + rtc::scoped_ptr<rtcp::RawPacket> old_rtcp_packet; + rtc::scoped_ptr<rtcp::RawPacket> recent_rtcp_packet; VideoReceiveStream::Config receiver_config(nullptr); VideoSendStream::Config sender_config(nullptr); - srand(random_seed); + Random prng(random_seed); // Create two RTP packets containing random data. - size_t packet_size = 1000 + rand() % 64; + size_t packet_size = prng.Rand(1000, 1100); old_rtp_packet.SetSize(packet_size); GenerateRtpPacket(extensions_bitvector, csrcs_count, old_rtp_packet.data(), - packet_size); - packet_size = 1000 + rand() % 64; + packet_size, &prng); + packet_size = prng.Rand(1000, 1100); recent_rtp_packet.SetSize(packet_size); - size_t recent_header_size = GenerateRtpPacket( - extensions_bitvector, csrcs_count, recent_rtp_packet.data(), packet_size); + size_t recent_header_size = + GenerateRtpPacket(extensions_bitvector, csrcs_count, + recent_rtp_packet.data(), packet_size, &prng); // Create two RTCP packets containing random data. - packet_size = 1000 + rand() % 64; - old_rtcp_packet.SetSize(packet_size); - GenerateRtcpPacket(old_rtcp_packet.data(), packet_size); - packet_size = 1000 + rand() % 64; - recent_rtcp_packet.SetSize(packet_size); - GenerateRtcpPacket(recent_rtcp_packet.data(), packet_size); + old_rtcp_packet = GenerateRtcpPacket(&prng); + recent_rtcp_packet = GenerateRtcpPacket(&prng); // Create configurations for the video streams. - GenerateVideoReceiveConfig(extensions_bitvector, &receiver_config); - GenerateVideoSendConfig(extensions_bitvector, &sender_config); + GenerateVideoReceiveConfig(extensions_bitvector, &receiver_config, &prng); + GenerateVideoSendConfig(extensions_bitvector, &sender_config, &prng); // Find the name of the current test, in order to use it as a temporary // filename. @@ -601,16 +644,16 @@ void DropOldEvents(uint32_t extensions_bitvector, log_dumper->LogVideoSendStreamConfig(sender_config); log_dumper->LogRtpHeader(false, MediaType::AUDIO, old_rtp_packet.data(), old_rtp_packet.size()); - log_dumper->LogRtcpPacket(true, MediaType::AUDIO, old_rtcp_packet.data(), - old_rtcp_packet.size()); + log_dumper->LogRtcpPacket(true, MediaType::AUDIO, old_rtcp_packet->Buffer(), + old_rtcp_packet->Length()); // Sleep 55 ms to let old events be removed from the queue. rtc::Thread::SleepMs(55); log_dumper->StartLogging(temp_filename, 10000000); log_dumper->LogRtpHeader(true, MediaType::VIDEO, recent_rtp_packet.data(), recent_rtp_packet.size()); log_dumper->LogRtcpPacket(false, MediaType::VIDEO, - recent_rtcp_packet.data(), - recent_rtcp_packet.size()); + recent_rtcp_packet->Buffer(), + recent_rtcp_packet->Length()); } // Read the generated file from disk. @@ -628,7 +671,7 @@ void DropOldEvents(uint32_t extensions_bitvector, recent_rtp_packet.data(), recent_header_size, recent_rtp_packet.size()); VerifyRtcpEvent(parsed_stream.stream(4), false, MediaType::VIDEO, - recent_rtcp_packet.data(), recent_rtcp_packet.size()); + recent_rtcp_packet->Buffer(), recent_rtcp_packet->Length()); // Clean up temporary file - can be pretty slow. remove(temp_filename.c_str()); diff --git a/webrtc/call/webrtc_call.gypi b/webrtc/call/webrtc_call.gypi index fd70ae81f4..0c3efff43a 100644 --- a/webrtc/call/webrtc_call.gypi +++ b/webrtc/call/webrtc_call.gypi @@ -14,6 +14,7 @@ '<(webrtc_root)/webrtc.gyp:rtc_event_log', ], 'webrtc_call_sources': [ + 'call/bitrate_allocator.cc', 'call/call.cc', 'call/congestion_controller.cc', 'call/transport_adapter.cc', |