Integrate send-side BWE into simulation framework.
BUG=4173 R=mflodman@webrtc.org, tommi@webrtc.org Review URL: https://webrtc-codereview.appspot.com/34699004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@8123 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
cfd82dfc11
commit
e5251ad63c
@ -10,9 +10,11 @@
|
||||
|
||||
#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test.h"
|
||||
|
||||
#include "webrtc/modules/interface/module_common_types.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_baselinefile.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/rtp_rtcp/interface/receive_statistics.h"
|
||||
#include "webrtc/system_wrappers/interface/clock.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
|
||||
@ -23,13 +25,13 @@ namespace webrtc {
|
||||
namespace testing {
|
||||
namespace bwe {
|
||||
|
||||
class TestedEstimator : public RemoteBitrateObserver {
|
||||
class PacketReceiver : public RemoteBitrateObserver {
|
||||
public:
|
||||
static const uint32_t kRemoteBitrateEstimatorMinBitrateBps = 30000;
|
||||
static const int kDelayPlotIntervalMs = 100;
|
||||
|
||||
TestedEstimator(const string& test_name,
|
||||
const BweTestConfig::EstimatorConfig& config)
|
||||
PacketReceiver(const string& test_name,
|
||||
const BweTestConfig::EstimatorConfig& config)
|
||||
: debug_name_(config.debug_name),
|
||||
delay_log_prefix_(),
|
||||
estimate_log_prefix_(),
|
||||
@ -38,9 +40,12 @@ class TestedEstimator : public RemoteBitrateObserver {
|
||||
plot_estimate_(config.plot_estimate),
|
||||
clock_(0),
|
||||
stats_(),
|
||||
recv_stats_(ReceiveStatistics::Create(&clock_)),
|
||||
latest_estimate_bps_(-1),
|
||||
estimator_(config.estimator_factory->Create(
|
||||
this, &clock_, config.control_type,
|
||||
this,
|
||||
&clock_,
|
||||
config.control_type,
|
||||
kRemoteBitrateEstimatorMinBitrateBps)),
|
||||
baseline_(BaseLineFileInterface::Create(test_name + "_" + debug_name_,
|
||||
config.update_baseline)) {
|
||||
@ -60,6 +65,8 @@ class TestedEstimator : public RemoteBitrateObserver {
|
||||
void EatPacket(const Packet& packet) {
|
||||
BWE_TEST_LOGGING_CONTEXT(debug_name_);
|
||||
|
||||
recv_stats_->IncomingPacket(packet.header(), packet.payload_size(), false);
|
||||
|
||||
latest_estimate_bps_ = -1;
|
||||
|
||||
// We're treating the send time (from previous filter) as the arrival
|
||||
@ -89,12 +96,19 @@ class TestedEstimator : public RemoteBitrateObserver {
|
||||
ASSERT_TRUE(packet_time_ms == clock_.TimeInMilliseconds());
|
||||
}
|
||||
|
||||
bool CheckEstimate(PacketSender::Feedback* feedback) {
|
||||
bool GetFeedback(PacketSender::Feedback* feedback) {
|
||||
assert(feedback);
|
||||
BWE_TEST_LOGGING_CONTEXT(debug_name_);
|
||||
uint32_t estimated_bps = 0;
|
||||
if (LatestEstimate(&estimated_bps)) {
|
||||
feedback->estimated_bps = estimated_bps;
|
||||
StatisticianMap statisticians = recv_stats_->GetActiveStatisticians();
|
||||
if (statisticians.empty()) {
|
||||
feedback->report_block = RTCPReportBlock();
|
||||
} else {
|
||||
feedback->report_block =
|
||||
BuildReportBlock(statisticians.begin()->second);
|
||||
}
|
||||
baseline_->Estimate(clock_.TimeInMilliseconds(), estimated_bps);
|
||||
|
||||
double estimated_kbps = static_cast<double>(estimated_bps) / 1000.0;
|
||||
@ -123,6 +137,18 @@ class TestedEstimator : public RemoteBitrateObserver {
|
||||
}
|
||||
|
||||
private:
|
||||
static RTCPReportBlock BuildReportBlock(StreamStatistician* statistician) {
|
||||
RTCPReportBlock report_block;
|
||||
RtcpStatistics stats;
|
||||
if (!statistician->GetStatistics(&stats, true))
|
||||
return report_block;
|
||||
report_block.fractionLost = stats.fraction_lost;
|
||||
report_block.cumulativeLost = stats.cumulative_lost;
|
||||
report_block.extendedHighSeqNum = stats.extended_max_sequence_number;
|
||||
report_block.jitter = stats.jitter;
|
||||
return report_block;
|
||||
}
|
||||
|
||||
bool LatestEstimate(uint32_t* estimate_bps) {
|
||||
if (latest_estimate_bps_ < 0) {
|
||||
vector<unsigned int> ssrcs;
|
||||
@ -144,11 +170,12 @@ class TestedEstimator : public RemoteBitrateObserver {
|
||||
bool plot_estimate_;
|
||||
SimulatedClock clock_;
|
||||
Stats<double> stats_;
|
||||
scoped_ptr<ReceiveStatistics> recv_stats_;
|
||||
int64_t latest_estimate_bps_;
|
||||
scoped_ptr<RemoteBitrateEstimator> estimator_;
|
||||
scoped_ptr<BaseLineFileInterface> baseline_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(TestedEstimator);
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(PacketReceiver);
|
||||
};
|
||||
|
||||
class PacketProcessorRunner {
|
||||
@ -242,8 +269,8 @@ void BweTest::SetupTestFromConfig(const BweTestConfig& config) {
|
||||
for (vector<BweTestConfig::EstimatorConfig>::const_iterator it =
|
||||
config.estimator_configs.begin(); it != config.estimator_configs.end();
|
||||
++it) {
|
||||
estimators_.insert(std::make_pair(it->flow_id, new TestedEstimator(
|
||||
test_name, *it)));
|
||||
estimators_.insert(
|
||||
std::make_pair(it->flow_id, new PacketReceiver(test_name, *it)));
|
||||
}
|
||||
BWE_TEST_LOGGING_GLOBAL_ENABLE(false);
|
||||
}
|
||||
@ -277,7 +304,7 @@ void BweTest::VerboseLogging(bool enable) {
|
||||
}
|
||||
|
||||
void BweTest::GiveFeedbackToAffectedSenders(int flow_id,
|
||||
TestedEstimator* estimator) {
|
||||
PacketReceiver* estimator) {
|
||||
std::list<PacketSender*> affected_senders;
|
||||
for (std::vector<PacketSender*>::iterator psit =
|
||||
senders_.begin(); psit != senders_.end(); ++psit) {
|
||||
@ -287,7 +314,7 @@ void BweTest::GiveFeedbackToAffectedSenders(int flow_id,
|
||||
}
|
||||
}
|
||||
PacketSender::Feedback feedback = {0};
|
||||
if (estimator->CheckEstimate(&feedback) && !affected_senders.empty()) {
|
||||
if (estimator->GetFeedback(&feedback) && !affected_senders.empty()) {
|
||||
// Allocate the bitrate evenly between the senders.
|
||||
feedback.estimated_bps /= affected_senders.size();
|
||||
for (std::list<PacketSender*>::iterator psit = affected_senders.begin();
|
||||
|
@ -86,7 +86,7 @@ struct BweTestConfig {
|
||||
std::vector<EstimatorConfig> estimator_configs;
|
||||
};
|
||||
|
||||
class TestedEstimator;
|
||||
class PacketReceiver;
|
||||
class PacketProcessorRunner;
|
||||
|
||||
class BweTest : public PacketProcessorListener {
|
||||
@ -104,11 +104,11 @@ class BweTest : public PacketProcessorListener {
|
||||
std::string GetTestName() const;
|
||||
|
||||
private:
|
||||
typedef std::map<int, TestedEstimator*> EstimatorMap;
|
||||
typedef std::map<int, PacketReceiver*> EstimatorMap;
|
||||
|
||||
void FindPacketsToProcess(const FlowIds& flow_ids, Packets* in,
|
||||
Packets* out);
|
||||
void GiveFeedbackToAffectedSenders(int flow_id, TestedEstimator* estimator);
|
||||
void GiveFeedbackToAffectedSenders(int flow_id, PacketReceiver* estimator);
|
||||
|
||||
int64_t run_time_ms_;
|
||||
int64_t time_now_ms_;
|
||||
|
@ -538,10 +538,8 @@ void TraceBasedDeliveryFilter::ProceedToNextSlot() {
|
||||
PacketSender::PacketSender(PacketProcessorListener* listener)
|
||||
: PacketProcessor(listener, true) {}
|
||||
|
||||
PacketSender::PacketSender(PacketProcessorListener* listener,
|
||||
const FlowIds& flow_ids)
|
||||
: PacketProcessor(listener, flow_ids, true) {
|
||||
|
||||
PacketSender::PacketSender(PacketProcessorListener* listener, int flow_id)
|
||||
: PacketProcessor(listener, FlowIds(1, flow_id), true) {
|
||||
}
|
||||
|
||||
VideoSender::VideoSender(int flow_id,
|
||||
@ -550,7 +548,7 @@ VideoSender::VideoSender(int flow_id,
|
||||
uint32_t kbps,
|
||||
uint32_t ssrc,
|
||||
int64_t first_frame_offset_ms)
|
||||
: PacketSender(listener, FlowIds(1, flow_id)),
|
||||
: PacketSender(listener, flow_id),
|
||||
kMaxPayloadSizeBytes(1200),
|
||||
kTimestampBase(0xff80ff00ul),
|
||||
frame_period_ms_(1000.0 / fps),
|
||||
@ -677,17 +675,73 @@ uint32_t PeriodicKeyFrameSender::NextPacketSize(uint32_t frame_size,
|
||||
return std::min(avg_size, remaining_payload);
|
||||
}
|
||||
|
||||
RegularVideoSender::RegularVideoSender(PacketProcessorListener* listener,
|
||||
uint32_t kbps,
|
||||
AdaptiveVideoSender* source)
|
||||
// It is important that the first_frame_offset and the initial time of
|
||||
// clock_ are both zero, otherwise we can't have absolute time in this
|
||||
// class.
|
||||
: PacketSender(listener, source->flow_ids()[0]),
|
||||
clock_(0),
|
||||
start_of_run_ms_(0),
|
||||
bitrate_controller_(BitrateController::CreateBitrateController(&clock_,
|
||||
false)),
|
||||
feedback_observer_(bitrate_controller_->CreateRtcpBandwidthObserver()),
|
||||
source_(source),
|
||||
modules_() {
|
||||
const int kMinBitrateBps = 10000;
|
||||
const int kMaxBitrateBps = 20000000;
|
||||
bitrate_controller_->SetBitrateObserver(this, 1000 * kbps, kMinBitrateBps,
|
||||
kMaxBitrateBps);
|
||||
modules_.push_back(bitrate_controller_.get());
|
||||
}
|
||||
|
||||
RegularVideoSender::~RegularVideoSender() {
|
||||
}
|
||||
|
||||
void RegularVideoSender::GiveFeedback(const Feedback& feedback) {
|
||||
feedback_observer_->OnReceivedEstimatedBitrate(feedback.estimated_bps);
|
||||
ReportBlockList report_blocks;
|
||||
report_blocks.push_back(feedback.report_block);
|
||||
feedback_observer_->OnReceivedRtcpReceiverReport(report_blocks, 0,
|
||||
clock_.TimeInMilliseconds());
|
||||
bitrate_controller_->Process();
|
||||
}
|
||||
|
||||
void RegularVideoSender::RunFor(int64_t time_ms, Packets* in_out) {
|
||||
start_of_run_ms_ = clock_.TimeInMilliseconds();
|
||||
source_->RunFor(time_ms, in_out);
|
||||
clock_.AdvanceTimeMilliseconds(time_ms);
|
||||
}
|
||||
|
||||
void RegularVideoSender::OnNetworkChanged(uint32_t target_bitrate_bps,
|
||||
uint8_t fraction_lost,
|
||||
int64_t rtt) {
|
||||
PacketSender::Feedback feedback;
|
||||
feedback.estimated_bps = target_bitrate_bps;
|
||||
source_->GiveFeedback(feedback);
|
||||
std::stringstream ss;
|
||||
ss << "SendEstimate_" << flow_ids()[0] << "#1";
|
||||
BWE_TEST_LOGGING_PLOT(ss.str(), clock_.TimeInMilliseconds(),
|
||||
target_bitrate_bps / 1000);
|
||||
}
|
||||
|
||||
PacedVideoSender::PacedVideoSender(PacketProcessorListener* listener,
|
||||
uint32_t kbps,
|
||||
AdaptiveVideoSender* source)
|
||||
// It is important that the first_frame_offset and the initial time of
|
||||
// clock_ are both zero, otherwise we can't have absolute time in this
|
||||
// class.
|
||||
: PacketSender(listener, source->flow_ids()),
|
||||
clock_(0),
|
||||
start_of_run_ms_(0),
|
||||
pacer_(&clock_, this, kbps, PacedSender::kDefaultPaceMultiplier* kbps, 0),
|
||||
source_(source) {
|
||||
: RegularVideoSender(listener, kbps, source),
|
||||
pacer_(&clock_,
|
||||
this,
|
||||
kbps,
|
||||
PacedSender::kDefaultPaceMultiplier* kbps,
|
||||
0) {
|
||||
modules_.push_back(&pacer_);
|
||||
}
|
||||
|
||||
PacedVideoSender::~PacedVideoSender() {
|
||||
}
|
||||
|
||||
void PacedVideoSender::RunFor(int64_t time_ms, Packets* in_out) {
|
||||
@ -698,18 +752,18 @@ void PacedVideoSender::RunFor(int64_t time_ms, Packets* in_out) {
|
||||
int64_t end_time_ms = clock_.TimeInMilliseconds() + time_ms;
|
||||
Packets::iterator it = generated_packets.begin();
|
||||
while (clock_.TimeInMilliseconds() <= end_time_ms) {
|
||||
int64_t time_until_process_ms = pacer_.TimeUntilNextProcess();
|
||||
if (time_until_process_ms < 0)
|
||||
time_until_process_ms = 0;
|
||||
int64_t time_until_process_ms = TimeUntilNextProcess(modules_);
|
||||
|
||||
int time_until_packet_ms = time_ms;
|
||||
if (it != generated_packets.end())
|
||||
time_until_packet_ms =
|
||||
(it->send_time_us() + 500) / 1000 - clock_.TimeInMilliseconds();
|
||||
assert(time_until_packet_ms >= 0);
|
||||
|
||||
int time_until_next_event_ms = time_until_packet_ms;
|
||||
if (time_until_process_ms < time_until_packet_ms &&
|
||||
pacer_.QueueSizePackets() > 0)
|
||||
if (time_until_process_ms < time_until_packet_ms) {
|
||||
time_until_next_event_ms = time_until_process_ms;
|
||||
}
|
||||
|
||||
if (clock_.TimeInMilliseconds() + time_until_next_event_ms > end_time_ms) {
|
||||
clock_.AdvanceTimeMilliseconds(end_time_ms - clock_.TimeInMilliseconds());
|
||||
@ -718,7 +772,7 @@ void PacedVideoSender::RunFor(int64_t time_ms, Packets* in_out) {
|
||||
clock_.AdvanceTimeMilliseconds(time_until_next_event_ms);
|
||||
if (time_until_process_ms < time_until_packet_ms) {
|
||||
// Time to process.
|
||||
pacer_.Process();
|
||||
CallProcess(modules_);
|
||||
} else {
|
||||
// Time to send next packet to pacer.
|
||||
pacer_.SendPacket(PacedSender::kNormalPriority,
|
||||
@ -738,6 +792,27 @@ void PacedVideoSender::RunFor(int64_t time_ms, Packets* in_out) {
|
||||
QueuePackets(in_out, end_time_ms * 1000);
|
||||
}
|
||||
|
||||
int64_t PacedVideoSender::TimeUntilNextProcess(
|
||||
const std::list<Module*>& modules) {
|
||||
int64_t time_until_next_process_ms = 10;
|
||||
for (auto* module : modules) {
|
||||
int64_t next_process_ms = module->TimeUntilNextProcess();
|
||||
if (next_process_ms < time_until_next_process_ms)
|
||||
time_until_next_process_ms = next_process_ms;
|
||||
}
|
||||
if (time_until_next_process_ms < 0)
|
||||
time_until_next_process_ms = 0;
|
||||
return time_until_next_process_ms;
|
||||
}
|
||||
|
||||
void PacedVideoSender::CallProcess(const std::list<Module*>& modules) {
|
||||
for (auto* module : modules) {
|
||||
if (module->TimeUntilNextProcess() <= 0) {
|
||||
module->Process();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PacedVideoSender::QueuePackets(Packets* batch,
|
||||
int64_t end_of_batch_time_us) {
|
||||
queue_.merge(*batch);
|
||||
@ -755,14 +830,6 @@ void PacedVideoSender::QueuePackets(Packets* batch,
|
||||
batch->merge(to_transfer);
|
||||
}
|
||||
|
||||
void PacedVideoSender::GiveFeedback(const PacketSender::Feedback& feedback) {
|
||||
source_->GiveFeedback(feedback);
|
||||
pacer_.UpdateBitrate(
|
||||
feedback.estimated_bps / 1000,
|
||||
PacedSender::kDefaultPaceMultiplier * feedback.estimated_bps / 1000,
|
||||
0);
|
||||
}
|
||||
|
||||
bool PacedVideoSender::TimeToSendPacket(uint32_t ssrc,
|
||||
uint16_t sequence_number,
|
||||
int64_t capture_time_ms,
|
||||
@ -786,6 +853,15 @@ bool PacedVideoSender::TimeToSendPacket(uint32_t ssrc,
|
||||
size_t PacedVideoSender::TimeToSendPadding(size_t bytes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PacedVideoSender::OnNetworkChanged(uint32_t target_bitrate_bps,
|
||||
uint8_t fraction_lost,
|
||||
int64_t rtt) {
|
||||
RegularVideoSender::OnNetworkChanged(target_bitrate_bps, fraction_lost, rtt);
|
||||
pacer_.UpdateBitrate(
|
||||
target_bitrate_bps / 1000,
|
||||
PacedSender::kDefaultPaceMultiplier * target_bitrate_bps / 1000, 0);
|
||||
}
|
||||
} // namespace bwe
|
||||
} // namespace testing
|
||||
} // namespace webrtc
|
||||
|
@ -21,13 +21,18 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/modules/bitrate_controller/include/bitrate_controller.h"
|
||||
#include "webrtc/modules/interface/module_common_types.h"
|
||||
#include "webrtc/modules/pacing/include/paced_sender.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h"
|
||||
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
|
||||
#include "webrtc/system_wrappers/interface/clock.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RtcpBandwidthObserver;
|
||||
|
||||
namespace testing {
|
||||
namespace bwe {
|
||||
|
||||
@ -365,10 +370,11 @@ class PacketSender : public PacketProcessor {
|
||||
public:
|
||||
struct Feedback {
|
||||
uint32_t estimated_bps;
|
||||
RTCPReportBlock report_block;
|
||||
};
|
||||
|
||||
explicit PacketSender(PacketProcessorListener* listener);
|
||||
PacketSender(PacketProcessorListener* listener, const FlowIds& flow_ids);
|
||||
PacketSender(PacketProcessorListener* listener, int flow_id);
|
||||
virtual ~PacketSender() {}
|
||||
|
||||
virtual uint32_t GetCapacityKbps() const { return 0; }
|
||||
@ -462,16 +468,45 @@ class PeriodicKeyFrameSender : public AdaptiveVideoSender {
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(PeriodicKeyFrameSender);
|
||||
};
|
||||
|
||||
class PacedVideoSender : public PacketSender, public PacedSender::Callback {
|
||||
class RegularVideoSender : public PacketSender, public BitrateObserver {
|
||||
public:
|
||||
PacedVideoSender(PacketProcessorListener* listener,
|
||||
uint32_t kbps, AdaptiveVideoSender* source);
|
||||
virtual ~PacedVideoSender() {}
|
||||
RegularVideoSender(PacketProcessorListener* listener,
|
||||
uint32_t kbps,
|
||||
AdaptiveVideoSender* source);
|
||||
virtual ~RegularVideoSender();
|
||||
|
||||
virtual int GetFeedbackIntervalMs() const OVERRIDE { return 100; }
|
||||
virtual void GiveFeedback(const Feedback& feedback) OVERRIDE;
|
||||
virtual void RunFor(int64_t time_ms, Packets* in_out) OVERRIDE;
|
||||
|
||||
// Implements BitrateObserver.
|
||||
virtual void OnNetworkChanged(uint32_t target_bitrate_bps,
|
||||
uint8_t fraction_lost,
|
||||
int64_t rtt) OVERRIDE;
|
||||
|
||||
protected:
|
||||
static const int64_t kInitialTimeMs = 0;
|
||||
SimulatedClock clock_;
|
||||
int64_t start_of_run_ms_;
|
||||
scoped_ptr<BitrateController> bitrate_controller_;
|
||||
scoped_ptr<RtcpBandwidthObserver> feedback_observer_;
|
||||
AdaptiveVideoSender* source_;
|
||||
std::list<Module*> modules_;
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(RegularVideoSender);
|
||||
};
|
||||
|
||||
class PacedVideoSender : public RegularVideoSender,
|
||||
public PacedSender::Callback {
|
||||
public:
|
||||
PacedVideoSender(PacketProcessorListener* listener,
|
||||
uint32_t kbps,
|
||||
AdaptiveVideoSender* source);
|
||||
virtual ~PacedVideoSender();
|
||||
|
||||
virtual void RunFor(int64_t time_ms, Packets* in_out) OVERRIDE;
|
||||
|
||||
// Implements PacedSender::Callback.
|
||||
virtual bool TimeToSendPacket(uint32_t ssrc,
|
||||
uint16_t sequence_number,
|
||||
@ -479,6 +514,11 @@ class PacedVideoSender : public PacketSender, public PacedSender::Callback {
|
||||
bool retransmission) OVERRIDE;
|
||||
virtual size_t TimeToSendPadding(size_t bytes) OVERRIDE;
|
||||
|
||||
// Implements BitrateObserver.
|
||||
virtual void OnNetworkChanged(uint32_t target_bitrate_bps,
|
||||
uint8_t fraction_lost,
|
||||
int64_t rtt) OVERRIDE;
|
||||
|
||||
private:
|
||||
class ProbingPacedSender : public PacedSender {
|
||||
public:
|
||||
@ -496,15 +536,13 @@ class PacedVideoSender : public PacketSender, public PacedSender::Callback {
|
||||
virtual bool ProbingExperimentIsEnabled() const OVERRIDE { return true; }
|
||||
};
|
||||
|
||||
int64_t TimeUntilNextProcess(const std::list<Module*>& modules);
|
||||
void CallProcess(const std::list<Module*>& modules);
|
||||
void QueuePackets(Packets* batch, int64_t end_of_batch_time_us);
|
||||
|
||||
static const int64_t kInitialTimeMs = 0;
|
||||
SimulatedClock clock_;
|
||||
int64_t start_of_run_ms_;
|
||||
ProbingPacedSender pacer_;
|
||||
Packets pacer_queue_;
|
||||
Packets queue_;
|
||||
AdaptiveVideoSender* source_;
|
||||
Packets pacer_queue_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(PacedVideoSender);
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user