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