454 lines
8.6 KiB
C++
454 lines
8.6 KiB
C++
|
/*
|
||
|
* Copyright (c) 2011 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 "BWETestBase.h"
|
||
|
|
||
|
#include <algorithm> // sort
|
||
|
#include <fstream>
|
||
|
#include <string>
|
||
|
#include <vector>
|
||
|
#include <math.h>
|
||
|
|
||
|
#include "TestSenderReceiver.h"
|
||
|
#include "TestLoadGenerator.h"
|
||
|
#include "event_wrapper.h"
|
||
|
#include "thread_wrapper.h"
|
||
|
#include "tick_util.h"
|
||
|
#include "critical_section_wrapper.h"
|
||
|
|
||
|
|
||
|
double StatVec::Mean()
|
||
|
{
|
||
|
double sum = 0;
|
||
|
|
||
|
// sanity
|
||
|
if (size() <= 0) return (0);
|
||
|
|
||
|
std::vector<double>::iterator it;
|
||
|
for (it = begin(); it < end(); ++it)
|
||
|
{
|
||
|
sum += (*it);
|
||
|
}
|
||
|
|
||
|
return (sum / size());
|
||
|
}
|
||
|
|
||
|
double StatVec::Variance()
|
||
|
{
|
||
|
double sumSqaure = 0;
|
||
|
double sum = 0;
|
||
|
|
||
|
std::vector<double>::iterator it;
|
||
|
for (it = begin(); it < end(); ++it)
|
||
|
{
|
||
|
sum += (*it);
|
||
|
sumSqaure += (*it) * (*it);
|
||
|
}
|
||
|
|
||
|
// Normalizes by N-1. This produces the best unbiased estimate of the
|
||
|
// variance if X is a sample from a normal distribution.
|
||
|
int M = static_cast<int> (size() - 1);
|
||
|
|
||
|
if (M > 0)
|
||
|
{
|
||
|
double var = (sumSqaure / M) - (sum / (M+1)) * (sum / M);
|
||
|
assert(var >= 0);
|
||
|
return (var);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return (0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
double StatVec::Std()
|
||
|
{
|
||
|
return (sqrt(Variance()));
|
||
|
}
|
||
|
|
||
|
double StatVec::Max()
|
||
|
{
|
||
|
// sanity
|
||
|
if (size() <= 0) return (0);
|
||
|
|
||
|
std::vector<double>::iterator it = begin();
|
||
|
double maxVal = (*it);
|
||
|
++it;
|
||
|
|
||
|
for (; it < end(); ++it)
|
||
|
{
|
||
|
if ((*it) > maxVal) maxVal = (*it);
|
||
|
}
|
||
|
|
||
|
return (maxVal);
|
||
|
}
|
||
|
|
||
|
double StatVec::Min()
|
||
|
{
|
||
|
// sanity
|
||
|
if (size() <= 0) return (0);
|
||
|
|
||
|
std::vector<double>::iterator it = begin();
|
||
|
double minVal = (*it);
|
||
|
++it;
|
||
|
|
||
|
for (; it < end(); ++it)
|
||
|
{
|
||
|
if ((*it) < minVal) minVal = (*it);
|
||
|
}
|
||
|
|
||
|
return (minVal);
|
||
|
}
|
||
|
|
||
|
double StatVec::Median()
|
||
|
{
|
||
|
double median;
|
||
|
|
||
|
// sanity
|
||
|
if (size() <= 0) return (0);
|
||
|
|
||
|
// sort the vector
|
||
|
sort(begin(), end());
|
||
|
|
||
|
if ((size() % 2) == 0)
|
||
|
{
|
||
|
// even size; use average of two center elements
|
||
|
median = (at(size()/2 - 1) + at(size()/2)) / 2.0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// odd size; take center element
|
||
|
median = at(size()/2);
|
||
|
}
|
||
|
|
||
|
return (median);
|
||
|
}
|
||
|
|
||
|
double StatVec::Percentile(double p)
|
||
|
{
|
||
|
// sanity
|
||
|
if (size() <= 0) return (0);
|
||
|
|
||
|
// sort the vector
|
||
|
sort(begin(), end());
|
||
|
|
||
|
int rank = static_cast<int> (((size() - 1) * p) / 100 + 0.5); // between 1 and size()
|
||
|
rank -= 1; // between 0 and size()-1
|
||
|
|
||
|
assert(rank >= 0);
|
||
|
assert(rank < static_cast<int>(size()));
|
||
|
|
||
|
return (at(rank));
|
||
|
}
|
||
|
|
||
|
void StatVec::Export(std::fstream &file, bool colVec /*= false*/)
|
||
|
{
|
||
|
// sanity
|
||
|
if (size() <= 0) return;
|
||
|
|
||
|
std::string separator;
|
||
|
if (colVec) separator = "\n";
|
||
|
else separator = ", ";
|
||
|
|
||
|
std::vector<double>::iterator it = begin();
|
||
|
file << (*it);
|
||
|
++it;
|
||
|
|
||
|
for (; it < end(); ++it)
|
||
|
{
|
||
|
file << separator << (*it);
|
||
|
}
|
||
|
|
||
|
file << std::endl;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool BWETestProcThreadFunction(void *obj)
|
||
|
{
|
||
|
if (obj == NULL)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
BWETest *theObj = static_cast<BWETest *>(obj);
|
||
|
|
||
|
theObj->ProcLoop();
|
||
|
|
||
|
theObj->Stop();
|
||
|
|
||
|
return(true);
|
||
|
}
|
||
|
|
||
|
|
||
|
BWETest::BWETest(std::string testName, int startRateKbps):
|
||
|
_testName(testName),
|
||
|
_startRateKbps(startRateKbps),
|
||
|
_master(false),
|
||
|
_sendrec(NULL),
|
||
|
_gen(NULL),
|
||
|
_initialized(false),
|
||
|
_started(false),
|
||
|
_running(false),
|
||
|
_eventPtr(NULL),
|
||
|
_procThread(NULL),
|
||
|
_startTimeMs(-1),
|
||
|
_stopTimeMs(-1),
|
||
|
_statCritSect(*CriticalSectionWrapper::CreateCriticalSection())
|
||
|
{
|
||
|
_sendrec = new TestSenderReceiver();
|
||
|
}
|
||
|
|
||
|
|
||
|
BWETest::~BWETest()
|
||
|
{
|
||
|
if (_running)
|
||
|
{
|
||
|
Stop();
|
||
|
}
|
||
|
|
||
|
_statCritSect.Enter();
|
||
|
delete &_statCritSect;
|
||
|
|
||
|
if (_sendrec)
|
||
|
{
|
||
|
delete _sendrec;
|
||
|
_sendrec = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
bool BWETest::SetMaster(bool isMaster /*= true*/)
|
||
|
{
|
||
|
if (!_initialized)
|
||
|
{
|
||
|
// Can only set status before initializing.
|
||
|
_master = isMaster;
|
||
|
}
|
||
|
|
||
|
return (_master);
|
||
|
}
|
||
|
|
||
|
|
||
|
int BWETest::Init(std::string ip, WebRtc_UWord16 port)
|
||
|
{
|
||
|
if (_initialized)
|
||
|
{
|
||
|
// cannot init twice
|
||
|
return (-1);
|
||
|
}
|
||
|
|
||
|
if (!_sendrec)
|
||
|
{
|
||
|
throw "SenderReceiver must be created";
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
if (_started)
|
||
|
{
|
||
|
// cannot init after start
|
||
|
return (-1);
|
||
|
}
|
||
|
|
||
|
// initialize receiver port (for feedback)
|
||
|
_sendrec->InitReceiver(port);
|
||
|
|
||
|
// initialize sender
|
||
|
_sendrec->SetLoadGenerator(_gen);
|
||
|
_sendrec->InitSender(_startRateKbps, ip.c_str(), port);
|
||
|
//_gen->Start();
|
||
|
|
||
|
_sendrec->SetCallback(this);
|
||
|
|
||
|
_initialized = true;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool BWETest::Start()
|
||
|
{
|
||
|
if (!_initialized)
|
||
|
{
|
||
|
// must init first
|
||
|
return (false);
|
||
|
}
|
||
|
if (_started)
|
||
|
{
|
||
|
// already started, do nothing
|
||
|
return (true);
|
||
|
}
|
||
|
|
||
|
if (_sendrec->Start() != 0)
|
||
|
{
|
||
|
// failed
|
||
|
return (false);
|
||
|
}
|
||
|
|
||
|
if (_gen)
|
||
|
{
|
||
|
if (_gen->Start() != 0)
|
||
|
{
|
||
|
// failed
|
||
|
return (false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_eventPtr = EventWrapper::Create();
|
||
|
|
||
|
_startTimeMs = TickTime::MillisecondTimestamp();
|
||
|
_started = true;
|
||
|
_running = true;
|
||
|
|
||
|
return (true);
|
||
|
}
|
||
|
|
||
|
|
||
|
bool BWETest::Stop()
|
||
|
{
|
||
|
if (_procThread)
|
||
|
{
|
||
|
_stopTimeMs = TickTime::MillisecondTimestamp();
|
||
|
_procThread->SetNotAlive();
|
||
|
_running = false;
|
||
|
_eventPtr->Set();
|
||
|
|
||
|
while (!_procThread->Stop())
|
||
|
{
|
||
|
;
|
||
|
}
|
||
|
|
||
|
delete _procThread;
|
||
|
_procThread = NULL;
|
||
|
|
||
|
}
|
||
|
|
||
|
if (_eventPtr)
|
||
|
{
|
||
|
delete _eventPtr;
|
||
|
_eventPtr = NULL;
|
||
|
}
|
||
|
|
||
|
_procThread = NULL;
|
||
|
|
||
|
if(_gen)
|
||
|
{
|
||
|
_gen->Stop();
|
||
|
}
|
||
|
|
||
|
return(true);
|
||
|
}
|
||
|
|
||
|
|
||
|
bool BWETest::ProcLoop(void)
|
||
|
{
|
||
|
bool receiving = false;
|
||
|
|
||
|
// no critSect
|
||
|
while (_running)
|
||
|
{
|
||
|
|
||
|
// check stopping criterions
|
||
|
if (_master && StoppingCriterionMaster())
|
||
|
{
|
||
|
printf("StoppingCriterionMaster()\n");
|
||
|
_stopTimeMs = TickTime::MillisecondTimestamp();
|
||
|
_running = false;
|
||
|
}
|
||
|
else if (!_master && StoppingCriterionSlave())
|
||
|
{
|
||
|
printf("StoppingCriterionSlave()\n");
|
||
|
_running = false;
|
||
|
}
|
||
|
|
||
|
// wait
|
||
|
_eventPtr->Wait(1000); // 1000 ms
|
||
|
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
void BWETest::Report(std::fstream &log)
|
||
|
{
|
||
|
// cannot report on a running test
|
||
|
if(_running) return;
|
||
|
|
||
|
CriticalSectionScoped cs(_statCritSect);
|
||
|
|
||
|
log << "\n\n*** Test name = " << _testName << "\n";
|
||
|
log << "Execution time = " << static_cast<double>(_stopTimeMs - _startTimeMs) / 1000 << " s\n";
|
||
|
log << "\n";
|
||
|
log << "RTT statistics\n";
|
||
|
log << "\tMin = " << _rttVecMs.Min() << " ms\n";
|
||
|
log << "\tMax = " << _rttVecMs.Max() << " ms\n";
|
||
|
log << "\n";
|
||
|
log << "Loss statistics\n";
|
||
|
log << "\tAverage = " << _lossVec.Mean() << "%\n";
|
||
|
log << "\tMax = " << _lossVec.Max() << "%\n";
|
||
|
|
||
|
log << "\n" << "Rates" << "\n";
|
||
|
_rateVecKbps.Export(log);
|
||
|
|
||
|
log << "\n" << "RTT" << "\n";
|
||
|
_rttVecMs.Export(log);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
// SenderReceiver callback
|
||
|
void BWETest::OnOnNetworkChanged(const WebRtc_UWord32 bitrateTargetBps,
|
||
|
const WebRtc_UWord8 fractionLost,
|
||
|
const WebRtc_UWord16 roundTripTimeMs,
|
||
|
const WebRtc_UWord32 jitterMS,
|
||
|
const WebRtc_UWord16 bwEstimateKbitMin,
|
||
|
const WebRtc_UWord16 bwEstimateKbitMax)
|
||
|
{
|
||
|
CriticalSectionScoped cs(_statCritSect);
|
||
|
|
||
|
// bitrate statistics
|
||
|
WebRtc_Word32 newBitrateKbps = bitrateTargetBps/1000;
|
||
|
|
||
|
_rateVecKbps.push_back(newBitrateKbps);
|
||
|
_rttVecMs.push_back(roundTripTimeMs);
|
||
|
_lossVec.push_back(static_cast<double>(fractionLost) / 255.0);
|
||
|
}
|
||
|
|
||
|
|
||
|
int BWEOneWayTest::Init(std::string ip, WebRtc_UWord16 port)
|
||
|
{
|
||
|
|
||
|
if (!_master)
|
||
|
{
|
||
|
// Use timeout stopping criterion by default for receiver
|
||
|
UseRecvTimeout();
|
||
|
}
|
||
|
|
||
|
return (BWETest::Init(ip, port));
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
bool BWEOneWayTest::Start()
|
||
|
{
|
||
|
bool ret = BWETest::Start();
|
||
|
|
||
|
if (!_master)
|
||
|
{
|
||
|
// send one dummy RTP packet to enable RTT measurements
|
||
|
const WebRtc_UWord8 dummy = 0;
|
||
|
//_gen->sendPayload(TickTime::MillisecondTimestamp(), &dummy, 0);
|
||
|
_sendrec->SendOutgoingData(
|
||
|
static_cast<WebRtc_UWord32>(TickTime::MillisecondTimestamp()*90),
|
||
|
&dummy, 1, webrtc::kVideoFrameDelta);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|