439 lines
11 KiB
C++
439 lines
11 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 <algorithm> // for max function
|
|
#include <stdio.h>
|
|
|
|
#include "TestLoadGenerator.h"
|
|
#include "TestSenderReceiver.h"
|
|
#include "event_wrapper.h"
|
|
#include "thread_wrapper.h"
|
|
#include "critical_section_wrapper.h"
|
|
#include "tick_util.h"
|
|
|
|
|
|
bool SenderThreadFunction(void *obj)
|
|
{
|
|
if (obj == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
TestLoadGenerator *_genObj = static_cast<TestLoadGenerator *>(obj);
|
|
|
|
return _genObj->GeneratorLoop();
|
|
}
|
|
|
|
|
|
TestLoadGenerator::TestLoadGenerator(TestSenderReceiver *sender, WebRtc_Word32 rtpSampleRate)
|
|
:
|
|
_critSect(*CriticalSectionWrapper::CreateCriticalSection()),
|
|
_eventPtr(NULL),
|
|
_genThread(NULL),
|
|
_bitrateKbps(0),
|
|
_sender(sender),
|
|
_running(false),
|
|
_rtpSampleRate(rtpSampleRate)
|
|
{
|
|
}
|
|
|
|
TestLoadGenerator::~TestLoadGenerator ()
|
|
{
|
|
if (_running)
|
|
{
|
|
Stop();
|
|
}
|
|
|
|
delete &_critSect;
|
|
}
|
|
|
|
WebRtc_Word32 TestLoadGenerator::SetBitrate (WebRtc_Word32 newBitrateKbps)
|
|
{
|
|
CriticalSectionScoped cs(_critSect);
|
|
|
|
if (newBitrateKbps < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
_bitrateKbps = newBitrateKbps;
|
|
|
|
printf("New bitrate = %i kbps\n", _bitrateKbps);
|
|
|
|
return _bitrateKbps;
|
|
}
|
|
|
|
|
|
WebRtc_Word32 TestLoadGenerator::Start (const char *threadName)
|
|
{
|
|
CriticalSectionScoped cs(_critSect);
|
|
|
|
_eventPtr = EventWrapper::Create();
|
|
|
|
_genThread = ThreadWrapper::CreateThread(SenderThreadFunction, this, kRealtimePriority, threadName);
|
|
if (_genThread == NULL)
|
|
{
|
|
throw "Unable to start generator thread";
|
|
exit(1);
|
|
}
|
|
|
|
_running = true;
|
|
|
|
unsigned int tid;
|
|
_genThread->Start(tid);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
WebRtc_Word32 TestLoadGenerator::Stop ()
|
|
{
|
|
_critSect.Enter();
|
|
|
|
if (_genThread)
|
|
{
|
|
_genThread->SetNotAlive();
|
|
_running = false;
|
|
_eventPtr->Set();
|
|
|
|
while (!_genThread->Stop())
|
|
{
|
|
_critSect.Leave();
|
|
_critSect.Enter();
|
|
}
|
|
|
|
delete _genThread;
|
|
_genThread = NULL;
|
|
|
|
delete _eventPtr;
|
|
_eventPtr = NULL;
|
|
}
|
|
|
|
_genThread = NULL;
|
|
_critSect.Leave();
|
|
return (0);
|
|
}
|
|
|
|
|
|
int TestLoadGenerator::generatePayload ()
|
|
{
|
|
return(generatePayload( static_cast<WebRtc_UWord32>( TickTime::MillisecondTimestamp() * _rtpSampleRate / 1000 )));
|
|
}
|
|
|
|
|
|
int TestLoadGenerator::sendPayload (const WebRtc_UWord32 timeStamp,
|
|
const WebRtc_UWord8* payloadData,
|
|
const WebRtc_UWord32 payloadSize,
|
|
const webrtc::FrameType frameType /*= webrtc::kVideoFrameDelta*/)
|
|
{
|
|
|
|
return (_sender->SendOutgoingData(timeStamp, payloadData, payloadSize, frameType));
|
|
}
|
|
|
|
|
|
CBRGenerator::CBRGenerator (TestSenderReceiver *sender, WebRtc_Word32 payloadSizeBytes, WebRtc_Word32 bitrateKbps, WebRtc_Word32 rtpSampleRate)
|
|
:
|
|
//_eventPtr(NULL),
|
|
_payloadSizeBytes(payloadSizeBytes),
|
|
_payload(new WebRtc_UWord8[payloadSizeBytes]),
|
|
TestLoadGenerator(sender, rtpSampleRate)
|
|
{
|
|
SetBitrate (bitrateKbps);
|
|
}
|
|
|
|
CBRGenerator::~CBRGenerator ()
|
|
{
|
|
if (_running)
|
|
{
|
|
Stop();
|
|
}
|
|
|
|
if (_payload)
|
|
{
|
|
delete [] _payload;
|
|
}
|
|
|
|
}
|
|
|
|
bool CBRGenerator::GeneratorLoop ()
|
|
{
|
|
double periodMs;
|
|
WebRtc_Word64 nextSendTime = TickTime::MillisecondTimestamp();
|
|
|
|
|
|
// no critSect
|
|
while (_running)
|
|
{
|
|
// send data (critSect inside)
|
|
generatePayload( static_cast<WebRtc_UWord32>(nextSendTime * _rtpSampleRate / 1000) );
|
|
|
|
// calculate wait time
|
|
periodMs = 8.0 * _payloadSizeBytes / ( _bitrateKbps );
|
|
|
|
nextSendTime = static_cast<WebRtc_Word64>(nextSendTime + periodMs);
|
|
|
|
WebRtc_Word32 waitTime = static_cast<WebRtc_Word32>(nextSendTime - TickTime::MillisecondTimestamp());
|
|
if (waitTime < 0)
|
|
{
|
|
waitTime = 0;
|
|
}
|
|
// wait
|
|
_eventPtr->Wait(static_cast<WebRtc_Word32>(waitTime));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int CBRGenerator::generatePayload ( WebRtc_UWord32 timestamp )
|
|
{
|
|
CriticalSectionScoped cs(_critSect);
|
|
|
|
//WebRtc_UWord8 *payload = new WebRtc_UWord8[_payloadSizeBytes];
|
|
|
|
int ret = sendPayload(timestamp, _payload, _payloadSizeBytes);
|
|
|
|
//delete [] payload;
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////////
|
|
|
|
CBRFixFRGenerator::CBRFixFRGenerator (TestSenderReceiver *sender, WebRtc_Word32 bitrateKbps,
|
|
WebRtc_Word32 rtpSampleRate, WebRtc_Word32 frameRateFps /*= 30*/,
|
|
double spread /*= 0.0*/)
|
|
:
|
|
//_eventPtr(NULL),
|
|
_payloadSizeBytes(0),
|
|
_payload(NULL),
|
|
_payloadAllocLen(0),
|
|
_frameRateFps(frameRateFps),
|
|
_spreadFactor(spread),
|
|
TestLoadGenerator(sender, rtpSampleRate)
|
|
{
|
|
SetBitrate (bitrateKbps);
|
|
}
|
|
|
|
CBRFixFRGenerator::~CBRFixFRGenerator ()
|
|
{
|
|
if (_running)
|
|
{
|
|
Stop();
|
|
}
|
|
|
|
if (_payload)
|
|
{
|
|
delete [] _payload;
|
|
_payloadAllocLen = 0;
|
|
}
|
|
|
|
}
|
|
|
|
bool CBRFixFRGenerator::GeneratorLoop ()
|
|
{
|
|
double periodMs;
|
|
WebRtc_Word64 nextSendTime = TickTime::MillisecondTimestamp();
|
|
|
|
_critSect.Enter();
|
|
|
|
if (_frameRateFps <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_critSect.Leave();
|
|
|
|
// no critSect
|
|
while (_running)
|
|
{
|
|
_critSect.Enter();
|
|
|
|
// calculate payload size
|
|
_payloadSizeBytes = nextPayloadSize();
|
|
|
|
if (_payloadSizeBytes > 0)
|
|
{
|
|
|
|
if (_payloadAllocLen < _payloadSizeBytes * (1 + _spreadFactor))
|
|
{
|
|
// re-allocate _payload
|
|
if (_payload)
|
|
{
|
|
delete [] _payload;
|
|
_payload = NULL;
|
|
}
|
|
|
|
_payloadAllocLen = static_cast<WebRtc_Word32>((_payloadSizeBytes * (1 + _spreadFactor) * 3) / 2 + .5); // 50% extra to avoid frequent re-alloc
|
|
_payload = new WebRtc_UWord8[_payloadAllocLen];
|
|
}
|
|
|
|
|
|
// send data (critSect inside)
|
|
generatePayload( static_cast<WebRtc_UWord32>(nextSendTime * _rtpSampleRate / 1000) );
|
|
}
|
|
|
|
_critSect.Leave();
|
|
|
|
// calculate wait time
|
|
periodMs = 1000.0 / _frameRateFps;
|
|
nextSendTime = static_cast<WebRtc_Word64>(nextSendTime + periodMs + 0.5);
|
|
|
|
WebRtc_Word32 waitTime = static_cast<WebRtc_Word32>(nextSendTime - TickTime::MillisecondTimestamp());
|
|
if (waitTime < 0)
|
|
{
|
|
waitTime = 0;
|
|
}
|
|
// wait
|
|
_eventPtr->Wait(waitTime);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
WebRtc_Word32 CBRFixFRGenerator::nextPayloadSize()
|
|
{
|
|
const double periodMs = 1000.0 / _frameRateFps;
|
|
return static_cast<WebRtc_Word32>(_bitrateKbps * periodMs / 8 + 0.5);
|
|
}
|
|
|
|
int CBRFixFRGenerator::generatePayload ( WebRtc_UWord32 timestamp )
|
|
{
|
|
CriticalSectionScoped cs(_critSect);
|
|
|
|
double factor = ((double) rand() - RAND_MAX/2) / RAND_MAX; // [-0.5; 0.5]
|
|
factor = 1 + 2 * _spreadFactor * factor; // [1 - _spreadFactor ; 1 + _spreadFactor]
|
|
|
|
WebRtc_Word32 thisPayloadBytes = static_cast<WebRtc_Word32>(_payloadSizeBytes * factor);
|
|
// sanity
|
|
if (thisPayloadBytes > _payloadAllocLen)
|
|
{
|
|
thisPayloadBytes = _payloadAllocLen;
|
|
}
|
|
|
|
int ret = sendPayload(timestamp, _payload, thisPayloadBytes);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/////////////////////
|
|
|
|
PeriodicKeyFixFRGenerator::PeriodicKeyFixFRGenerator (TestSenderReceiver *sender, WebRtc_Word32 bitrateKbps,
|
|
WebRtc_Word32 rtpSampleRate, WebRtc_Word32 frameRateFps /*= 30*/,
|
|
double spread /*= 0.0*/, double keyFactor /*= 4.0*/, WebRtc_UWord32 keyPeriod /*= 300*/)
|
|
:
|
|
_keyFactor(keyFactor),
|
|
_keyPeriod(keyPeriod),
|
|
_frameCount(0),
|
|
CBRFixFRGenerator(sender, bitrateKbps, rtpSampleRate, frameRateFps, spread)
|
|
{
|
|
}
|
|
|
|
WebRtc_Word32 PeriodicKeyFixFRGenerator::nextPayloadSize()
|
|
{
|
|
// calculate payload size for a delta frame
|
|
WebRtc_Word32 payloadSizeBytes = static_cast<WebRtc_Word32>(1000 * _bitrateKbps / (8.0 * _frameRateFps * (1.0 + (_keyFactor - 1.0) / _keyPeriod)) + 0.5);
|
|
|
|
if (_frameCount % _keyPeriod == 0)
|
|
{
|
|
// this is a key frame, scale the payload size
|
|
payloadSizeBytes = static_cast<WebRtc_Word32>(_keyFactor * _payloadSizeBytes + 0.5);
|
|
}
|
|
_frameCount++;
|
|
|
|
return payloadSizeBytes;
|
|
}
|
|
|
|
////////////////////
|
|
|
|
CBRVarFRGenerator::CBRVarFRGenerator(TestSenderReceiver *sender, WebRtc_Word32 bitrateKbps, const WebRtc_UWord8* frameRates,
|
|
WebRtc_UWord16 numFrameRates, WebRtc_Word32 rtpSampleRate, double avgFrPeriodMs,
|
|
double frSpreadFactor, double spreadFactor)
|
|
:
|
|
_avgFrPeriodMs(avgFrPeriodMs),
|
|
_frSpreadFactor(frSpreadFactor),
|
|
_frameRates(NULL),
|
|
_numFrameRates(numFrameRates),
|
|
_frChangeTimeMs(TickTime::MillisecondTimestamp() + _avgFrPeriodMs),
|
|
CBRFixFRGenerator(sender, bitrateKbps, rtpSampleRate, frameRates[0], spreadFactor)
|
|
{
|
|
_frameRates = new WebRtc_UWord8[_numFrameRates];
|
|
memcpy(_frameRates, frameRates, _numFrameRates);
|
|
}
|
|
|
|
CBRVarFRGenerator::~CBRVarFRGenerator()
|
|
{
|
|
delete [] _frameRates;
|
|
}
|
|
|
|
void CBRVarFRGenerator::ChangeFrameRate()
|
|
{
|
|
const WebRtc_Word64 nowMs = TickTime::MillisecondTimestamp();
|
|
if (nowMs < _frChangeTimeMs)
|
|
{
|
|
return;
|
|
}
|
|
// Time to change frame rate
|
|
WebRtc_UWord16 frIndex = static_cast<WebRtc_UWord16>(static_cast<double>(rand()) / RAND_MAX
|
|
* (_numFrameRates - 1) + 0.5) ;
|
|
assert(frIndex < _numFrameRates);
|
|
_frameRateFps = _frameRates[frIndex];
|
|
// Update the next frame rate change time
|
|
double factor = ((double) rand() - RAND_MAX/2) / RAND_MAX; // [-0.5; 0.5]
|
|
factor = 1 + 2 * _frSpreadFactor * factor; // [1 - _frSpreadFactor ; 1 + _frSpreadFactor]
|
|
_frChangeTimeMs = nowMs + static_cast<WebRtc_Word64>(1000.0 * factor *
|
|
_avgFrPeriodMs + 0.5);
|
|
|
|
printf("New frame rate: %d\n", _frameRateFps);
|
|
}
|
|
|
|
WebRtc_Word32 CBRVarFRGenerator::nextPayloadSize()
|
|
{
|
|
ChangeFrameRate();
|
|
return CBRFixFRGenerator::nextPayloadSize();
|
|
}
|
|
|
|
////////////////////
|
|
|
|
CBRFrameDropGenerator::CBRFrameDropGenerator(TestSenderReceiver *sender, WebRtc_Word32 bitrateKbps,
|
|
WebRtc_Word32 rtpSampleRate, double spreadFactor)
|
|
:
|
|
_accBits(0),
|
|
CBRFixFRGenerator(sender, bitrateKbps, rtpSampleRate, 30, spreadFactor)
|
|
{
|
|
}
|
|
|
|
CBRFrameDropGenerator::~CBRFrameDropGenerator()
|
|
{
|
|
}
|
|
|
|
WebRtc_Word32 CBRFrameDropGenerator::nextPayloadSize()
|
|
{
|
|
_accBits -= 1000 * _bitrateKbps / _frameRateFps;
|
|
if (_accBits < 0)
|
|
{
|
|
_accBits = 0;
|
|
}
|
|
if (_accBits > 0.3 * _bitrateKbps * 1000)
|
|
{
|
|
//printf("drop\n");
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
//printf("keep\n");
|
|
const double periodMs = 1000.0 / _frameRateFps;
|
|
WebRtc_Word32 frameSize = static_cast<WebRtc_Word32>(_bitrateKbps * periodMs / 8 + 0.5);
|
|
frameSize = std::max(frameSize, static_cast<WebRtc_Word32>(300 * periodMs / 8 + 0.5));
|
|
_accBits += frameSize * 8;
|
|
return frameSize;
|
|
}
|
|
}
|