blob: 08b0a72c507181008175f7445f9c42810fa036d8 [file] [log] [blame]
/*
* 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.
*/
#ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_BWE_TEST_FRAMEWORK_H_
#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_BWE_TEST_FRAMEWORK_H_
#include <assert.h>
#include <math.h>
#include <algorithm>
#include <list>
#include <memory>
#include <numeric>
#include <set>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "webrtc/modules/bitrate_controller/include/bitrate_controller.h"
#include "webrtc/modules/include/module_common_types.h"
#include "webrtc/modules/pacing/paced_sender.h"
#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h"
#include "webrtc/modules/remote_bitrate_estimator/test/packet.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "webrtc/rtc_base/constructormagic.h"
#include "webrtc/rtc_base/random.h"
#include "webrtc/system_wrappers/include/clock.h"
#include "webrtc/typedefs.h"
namespace webrtc {
class RtcpBandwidthObserver;
namespace testing {
namespace bwe {
class DelayCapHelper;
class RateCounter {
public:
explicit RateCounter(int64_t window_size_ms)
: window_size_us_(1000 * window_size_ms),
recently_received_packets_(0),
recently_received_bytes_(0),
last_accumulated_us_(0),
window_() {}
RateCounter() : RateCounter(1000) {}
void UpdateRates(int64_t send_time_us, uint32_t payload_size);
int64_t window_size_ms() const { return (window_size_us_ + 500) / 1000; }
uint32_t packets_per_second() const;
uint32_t bits_per_second() const;
double BitrateWindowS() const;
private:
typedef std::pair<int64_t, uint32_t> TimeSizePair;
int64_t window_size_us_;
uint32_t recently_received_packets_;
uint32_t recently_received_bytes_;
int64_t last_accumulated_us_;
std::list<TimeSizePair> window_;
};
typedef std::set<int> FlowIds;
const FlowIds CreateFlowIds(const int *flow_ids_array, size_t num_flow_ids);
const FlowIds CreateFlowIdRange(int initial_value, int last_value);
template <typename T>
bool DereferencingComparator(const T* const& a, const T* const& b) {
assert(a != NULL);
assert(b != NULL);
return *a < *b;
}
template<typename T> class Stats {
public:
Stats()
: data_(),
last_mean_count_(0),
last_variance_count_(0),
last_minmax_count_(0),
mean_(0),
variance_(0),
min_(0),
max_(0) {
}
void Push(T data_point) {
data_.push_back(data_point);
}
T GetMean() {
if (last_mean_count_ != data_.size()) {
last_mean_count_ = data_.size();
mean_ = std::accumulate(data_.begin(), data_.end(), static_cast<T>(0));
assert(last_mean_count_ != 0);
mean_ /= static_cast<T>(last_mean_count_);
}
return mean_;
}
T GetVariance() {
if (last_variance_count_ != data_.size()) {
last_variance_count_ = data_.size();
T mean = GetMean();
variance_ = 0;
for (const auto& sample : data_) {
T diff = (sample - mean);
variance_ += diff * diff;
}
assert(last_variance_count_ != 0);
variance_ /= static_cast<T>(last_variance_count_);
}
return variance_;
}
T GetStdDev() {
return sqrt(static_cast<double>(GetVariance()));
}
T GetMin() {
RefreshMinMax();
return min_;
}
T GetMax() {
RefreshMinMax();
return max_;
}
std::string AsString() {
std::stringstream ss;
ss << (GetMean() >= 0 ? GetMean() : -1) << ", " <<
(GetStdDev() >= 0 ? GetStdDev() : -1);
return ss.str();
}
void Log(const std::string& units) {
BWE_TEST_LOGGING_LOG5("", "%f %s\t+/-%f\t[%f,%f]",
GetMean(), units.c_str(), GetStdDev(), GetMin(), GetMax());
}
private:
void RefreshMinMax() {
if (last_minmax_count_ != data_.size()) {
last_minmax_count_ = data_.size();
min_ = max_ = 0;
if (data_.empty()) {
return;
}
typename std::vector<T>::const_iterator it = data_.begin();
min_ = max_ = *it;
while (++it != data_.end()) {
min_ = std::min(min_, *it);
max_ = std::max(max_, *it);
}
}
}
std::vector<T> data_;
typename std::vector<T>::size_type last_mean_count_;
typename std::vector<T>::size_type last_variance_count_;
typename std::vector<T>::size_type last_minmax_count_;
T mean_;
T variance_;
T min_;
T max_;
};
bool IsTimeSorted(const Packets& packets);
class PacketProcessor;
enum ProcessorType { kSender, kReceiver, kRegular };
class PacketProcessorListener {
public:
virtual ~PacketProcessorListener() {}
virtual void AddPacketProcessor(PacketProcessor* processor,
ProcessorType type) = 0;
virtual void RemovePacketProcessor(PacketProcessor* processor) = 0;
};
class PacketProcessor {
public:
PacketProcessor(PacketProcessorListener* listener,
int flow_id,
ProcessorType type);
PacketProcessor(PacketProcessorListener* listener,
const FlowIds& flow_ids,
ProcessorType type);
virtual ~PacketProcessor();
// Called after each simulation batch to allow the processor to plot any
// internal data.
virtual void Plot(int64_t timestamp_ms) {}
// Run simulation for |time_ms| milliseconds, consuming packets from, and
// producing packets into in_out. The outgoing packet list must be sorted on
// |send_time_us_|. The simulation time |time_ms| is optional to use.
virtual void RunFor(int64_t time_ms, Packets* in_out) = 0;
const FlowIds& flow_ids() const { return flow_ids_; }
uint32_t packets_per_second() const;
uint32_t bits_per_second() const;
protected:
RateCounter rate_counter_;
private:
PacketProcessorListener* listener_;
const FlowIds flow_ids_;
RTC_DISALLOW_COPY_AND_ASSIGN(PacketProcessor);
};
class RateCounterFilter : public PacketProcessor {
public:
RateCounterFilter(PacketProcessorListener* listener,
int flow_id,
const char* name,
const std::string& algorithm_name);
RateCounterFilter(PacketProcessorListener* listener,
const FlowIds& flow_ids,
const char* name,
const std::string& algorithm_name);
RateCounterFilter(PacketProcessorListener* listener,
const FlowIds& flow_ids,
const char* name,
int64_t start_plotting_time_ms,
const std::string& algorithm_name);
virtual ~RateCounterFilter();
void LogStats();
Stats<double> GetBitrateStats() const;
virtual void Plot(int64_t timestamp_ms);
virtual void RunFor(int64_t time_ms, Packets* in_out);
private:
Stats<double> packets_per_second_stats_;
Stats<double> kbps_stats_;
int64_t start_plotting_time_ms_;
int flow_id_ = 0;
std::string name_;
// Algorithm name if single flow, Total link utilization if all flows.
std::string algorithm_name_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RateCounterFilter);
};
class LossFilter : public PacketProcessor {
public:
LossFilter(PacketProcessorListener* listener, int flow_id);
LossFilter(PacketProcessorListener* listener, const FlowIds& flow_ids);
virtual ~LossFilter() {}
void SetLoss(float loss_percent);
virtual void RunFor(int64_t time_ms, Packets* in_out);
private:
Random random_;
float loss_fraction_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(LossFilter);
};
class DelayFilter : public PacketProcessor {
public:
DelayFilter(PacketProcessorListener* listener, int flow_id);
DelayFilter(PacketProcessorListener* listener, const FlowIds& flow_ids);
virtual ~DelayFilter() {}
void SetOneWayDelayMs(int64_t one_way_delay_ms);
virtual void RunFor(int64_t time_ms, Packets* in_out);
private:
int64_t one_way_delay_us_;
int64_t last_send_time_us_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(DelayFilter);
};
class JitterFilter : public PacketProcessor {
public:
JitterFilter(PacketProcessorListener* listener, int flow_id);
JitterFilter(PacketProcessorListener* listener, const FlowIds& flow_ids);
virtual ~JitterFilter() {}
void SetMaxJitter(int64_t stddev_jitter_ms);
virtual void RunFor(int64_t time_ms, Packets* in_out);
void set_reorderdering(bool reordering) { reordering_ = reordering; }
int64_t MeanUs();
private:
Random random_;
int64_t stddev_jitter_us_;
int64_t last_send_time_us_;
bool reordering_; // False by default.
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(JitterFilter);
};
// Reorders two consecutive packets with a probability of reorder_percent.
class ReorderFilter : public PacketProcessor {
public:
ReorderFilter(PacketProcessorListener* listener, int flow_id);
ReorderFilter(PacketProcessorListener* listener, const FlowIds& flow_ids);
virtual ~ReorderFilter() {}
void SetReorder(float reorder_percent);
virtual void RunFor(int64_t time_ms, Packets* in_out);
private:
Random random_;
float reorder_fraction_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(ReorderFilter);
};
// Apply a bitrate choke with an infinite queue on the packet stream.
class ChokeFilter : public PacketProcessor {
public:
ChokeFilter(PacketProcessorListener* listener, int flow_id);
ChokeFilter(PacketProcessorListener* listener, const FlowIds& flow_ids);
virtual ~ChokeFilter();
void set_capacity_kbps(uint32_t kbps);
void set_max_delay_ms(int64_t max_queueing_delay_ms);
uint32_t capacity_kbps();
virtual void RunFor(int64_t time_ms, Packets* in_out);
Stats<double> GetDelayStats() const;
private:
uint32_t capacity_kbps_;
int64_t last_send_time_us_;
std::unique_ptr<DelayCapHelper> delay_cap_helper_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(ChokeFilter);
};
class TraceBasedDeliveryFilter : public PacketProcessor {
public:
TraceBasedDeliveryFilter(PacketProcessorListener* listener, int flow_id);
TraceBasedDeliveryFilter(PacketProcessorListener* listener,
const FlowIds& flow_ids);
TraceBasedDeliveryFilter(PacketProcessorListener* listener,
int flow_id,
const char* name);
virtual ~TraceBasedDeliveryFilter();
// The file should contain nanosecond timestamps corresponding to the time
// when the network can accept another packet. The timestamps should be
// separated by new lines, e.g., "100000000\n125000000\n321000000\n..."
bool Init(const std::string& filename);
virtual void Plot(int64_t timestamp_ms);
virtual void RunFor(int64_t time_ms, Packets* in_out);
void set_max_delay_ms(int64_t max_delay_ms);
Stats<double> GetDelayStats() const;
Stats<double> GetBitrateStats() const;
private:
void ProceedToNextSlot();
typedef std::vector<int64_t> TimeList;
int64_t current_offset_us_;
TimeList delivery_times_us_;
TimeList::const_iterator next_delivery_it_;
int64_t local_time_us_;
std::unique_ptr<RateCounter> rate_counter_;
std::string name_;
std::unique_ptr<DelayCapHelper> delay_cap_helper_;
Stats<double> packets_per_second_stats_;
Stats<double> kbps_stats_;
RTC_DISALLOW_COPY_AND_ASSIGN(TraceBasedDeliveryFilter);
};
class VideoSource {
public:
VideoSource(int flow_id,
float fps,
uint32_t kbps,
uint32_t ssrc,
int64_t first_frame_offset_ms);
virtual ~VideoSource() {}
virtual void RunFor(int64_t time_ms, Packets* in_out);
virtual int flow_id() const { return flow_id_; }
virtual void SetBitrateBps(int bitrate_bps) {}
uint32_t bits_per_second() const { return bits_per_second_; }
uint32_t max_payload_size_bytes() const { return kMaxPayloadSizeBytes; }
int64_t GetTimeUntilNextFrameMs() const;
protected:
virtual uint32_t NextFrameSize();
virtual uint32_t NextPacketSize(uint32_t frame_size,
uint32_t remaining_payload);
const uint32_t kMaxPayloadSizeBytes;
const uint32_t kTimestampBase;
const double frame_period_ms_;
uint32_t bits_per_second_;
uint32_t frame_size_bytes_;
private:
Random random_;
const int flow_id_;
int64_t next_frame_ms_;
int64_t next_frame_rand_ms_;
int64_t now_ms_;
RTPHeader prototype_header_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(VideoSource);
};
class AdaptiveVideoSource : public VideoSource {
public:
AdaptiveVideoSource(int flow_id,
float fps,
uint32_t kbps,
uint32_t ssrc,
int64_t first_frame_offset_ms);
virtual ~AdaptiveVideoSource() {}
void SetBitrateBps(int bitrate_bps) override;
private:
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(AdaptiveVideoSource);
};
class PeriodicKeyFrameSource : public AdaptiveVideoSource {
public:
PeriodicKeyFrameSource(int flow_id,
float fps,
uint32_t kbps,
uint32_t ssrc,
int64_t first_frame_offset_ms,
int key_frame_interval);
virtual ~PeriodicKeyFrameSource() {}
protected:
uint32_t NextFrameSize() override;
uint32_t NextPacketSize(uint32_t frame_size,
uint32_t remaining_payload) override;
private:
int key_frame_interval_;
uint32_t frame_counter_;
int compensation_bytes_;
int compensation_per_frame_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(PeriodicKeyFrameSource);
};
} // namespace bwe
} // namespace testing
} // namespace webrtc
#endif // WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_BWE_TEST_FRAMEWORK_H_