#include "benchmark.h"
#include <cassert>
#include <iostream>
#include <sstream>
#include <vector>
#if defined(_WIN32)
#include <windows.h>
#endif
#include "api/video/i420_buffer.h"
#include "common_video/libyuv/include/webrtc_libyuv.h"
#include "fileutils.h"
#include "modules/video_coding/utility/simulcast_rate_allocator.h"
#include "rtc_base/event.h"
#include "video_source.h"
#define SSIM_CALC 0
using namespace webrtc;
#define EXPECT_EQ (a, b)
FrameQueueTuple::~FrameQueueTuple() {
if (_codecSpecificInfo != NULL) {
delete _codecSpecificInfo;
}
if (_frame != NULL) {
delete _frame;
}
}
void FrameQueue::PushFrame(VideoFrame* frame,
webrtc::CodecSpecificInfo* codecSpecificInfo) {
webrtc::MutexLock cs(&_queueRWLock);
_frameBufferQueue.push(new FrameQueueTuple(frame, codecSpecificInfo));
}
FrameQueueTuple* FrameQueue::PopFrame() {
webrtc::MutexLock cs(&_queueRWLock);
if (_frameBufferQueue.empty()) {
return NULL;
}
FrameQueueTuple* tuple = _frameBufferQueue.front();
_frameBufferQueue.pop();
return tuple;
}
bool FrameQueue::Empty() {
webrtc::MutexLock cs(&_queueRWLock);
return _frameBufferQueue.empty();
}
uint32_t VideoEncodeCompleteCallback::EncodedBytes() {
return _encodedBytes;
}
webrtc::EncodedImageCallback::Result
VideoEncodeCompleteCallback::OnEncodedImage(
const webrtc::EncodedImage& encodedImage,
const webrtc::CodecSpecificInfo* codecSpecificInfo) {
_test.UpdateEncodedBytes(encodedImage.GetEncodedData()->size());
_encodedBytes += encodedImage.GetEncodedData()->size();
if (_encodedFile != NULL) {
if (fwrite(encodedImage.GetEncodedData()->data(), 1,
encodedImage.GetEncodedData()->size(),
_encodedFile) != encodedImage.GetEncodedData()->size()) {
fprintf(stderr, "Error writing to encoded file %s\n",
_test._outname.c_str());
}
}
return webrtc::EncodedImageCallback::Result(
webrtc::EncodedImageCallback::Result::OK);
}
Benchmark::Benchmark()
: _resultsFileName(webrtc::test::OutputPath() + "benchmark.txt"),
_codecName("Default"),
_env(webrtc::CreateEnvironment()) {}
Benchmark::Benchmark(std::string name, std::string description)
: _name(name),
_description(description),
_resultsFileName(webrtc::test::OutputPath() + "benchmark.txt"),
_codecName("Default"),
_env(webrtc::CreateEnvironment()) {}
Benchmark::Benchmark(std::string name,
std::string description,
std::string resultsFileName,
std::string codecName)
: _name(name),
_description(description),
_resultsFileName(resultsFileName),
_codecName(codecName),
_cpu(webrtc::CpuWrapper::CreateCpu()),
_env(webrtc::CreateEnvironment()) {}
void Benchmark::Perform() {
std::vector<const VideoSource*> sources;
std::vector<const VideoSource*>::iterator it;
sources.push_back(new const VideoSource(
webrtc::test::ProjectRootPath() + "resources/FourPeople_1280x720_30.yuv",
kWHD));
const VideoSize size[] = {kWHD};
const int frameRate[] = {30};
const bool speedTestMask[] = {true};
const int bitRate[] = {500, 1000, 2000, 3000, 4000};
enum { kSpeedTestIterations = 8 };
const int nFrameRates = sizeof(frameRate) / sizeof(*frameRate);
assert(sizeof(speedTestMask) / sizeof(*speedTestMask) == nFrameRates);
const int nBitrates = sizeof(bitRate) / sizeof(*bitRate);
int testIterations = 1;
double fps[nBitrates];
uint32_t cpuUsage[nBitrates];
double totalEncodeTime[nBitrates];
double totalDecodeTime[nBitrates];
_results.open(_resultsFileName.c_str(), std::fstream::out);
_results << GetMagicStr() << std::endl;
_results << _codecName << std::endl;
for (it = sources.begin(); it < sources.end(); it++) {
int i = 0;
for (int j = 0; j < nFrameRates; j++) {
_target = *it;
_inname = (*it)->GetFileName();
std::cout << (*it)->GetName() << ", "
<< VideoSource::GetSizeString(size[i]) << ", " << frameRate[j]
<< " fps" << ", " << _name << std::endl;
_results << (*it)->GetName() << "," << VideoSource::GetSizeString(size[i])
<< "," << frameRate[j] << " fps" << ", " << _name << std::endl
<< "Bitrate [kbps]";
if (speedTestMask[j]) {
testIterations = kSpeedTestIterations;
} else {
testIterations = 1;
}
for (int k = 0; k < nBitrates; k++) {
_bitRate = (bitRate[k]);
double avgFps = 0.0;
uint32_t currCpuUsage = 0;
totalEncodeTime[k] = 0;
std::cout << "TargetBitrate [kbps]:" << " " << _bitRate << std::endl;
for (int l = 0; l < testIterations; l++) {
PerformNormalTest();
uint32_t cpuUsage = _cpu->CpuUsage();
if (cpuUsage > 0) {
currCpuUsage += cpuUsage;
int coreCount = _cpu->GetNumCores();
std::string str = "CPU Usage[%]: cores ";
str += std::to_string(coreCount);
str += ", usage " + std::to_string(cpuUsage) + "%" +
", Test Iteration: " + std::to_string(l + 1) + "/" +
std::to_string(testIterations);
std::cout << str << std::flush;
for (int i = 0; i < str.length(); ++i) {
std::cout << "\b";
}
}
_appendNext = false;
avgFps += _framecnt / (_totalEncodeTime);
totalEncodeTime[k] += _totalEncodeTime;
}
avgFps /= testIterations;
totalEncodeTime[k] /= testIterations;
currCpuUsage /= testIterations;
double actualBitRate = ActualBitRate(_framecnt) / 1000.0;
std::cout << "ActualBitRate [kbps]:" << " " << actualBitRate
<< std::endl;
_results << "," << actualBitRate;
fps[k] = avgFps;
cpuUsage[k] = currCpuUsage;
}
std::cout << std::endl << "CpuUsage [%]:";
_results << std::endl << "CpuUsage [%]";
for (int k = 0; k < nBitrates; k++) {
std::cout << " " << cpuUsage[k] << "%";
_results << "," << cpuUsage[k] << "%";
}
std::cout << std::endl << "Encode Time[ms]:";
_results << std::endl << "Encode Time[ms]";
for (int k = 0; k < nBitrates; k++) {
std::cout << " " << totalEncodeTime[k];
_results << "," << totalEncodeTime[k];
}
if (speedTestMask[j]) {
std::cout << std::endl << "Speed [fps]:";
_results << std::endl << "Speed [fps]";
for (int k = 0; k < nBitrates; k++) {
std::cout << " " << static_cast<int>(fps[k] + 0.5);
_results << "," << static_cast<int>(fps[k] + 0.5);
}
}
std::cout << std::endl << std::endl;
_results << std::endl << std::endl;
}
i++;
delete *it;
}
_results.close();
}
void Benchmark::PerformNormalTest() {
_encoder = GetNewEncoder(_env);
_lengthSourceFrame = _target->GetFrameLength();
CodecSettings(_target->GetWidth(), _target->GetHeight(),
_target->GetFrameRate(), _bitRate);
Setup();
std::unique_ptr<webrtc::Event> waitEvent = std::make_unique<webrtc::Event>();
_encoder->InitEncode(&_inst, 4, 1440);
CodecSpecific_InitBitrate();
FrameQueue frameQueue;
VideoEncodeCompleteCallback encCallback(_encodedFile, &frameQueue, *this);
_encoder->RegisterEncodeCompleteCallback(&encCallback);
_totalEncodeTime = _totalDecodeTime = 0;
_totalEncodePipeTime = _totalDecodePipeTime = 0;
bool complete = false;
_framecnt = 0;
_encFrameCnt = 0;
_sumEncBytes = 0;
_lengthEncFrame = 0;
while (!complete) {
complete = Encode();
_framecnt++;
_encFrameCnt++;
}
Teardown();
}
void Benchmark::Teardown() {
if (_sourceFile == NULL) {
return;
}
_encoder->Release();
fclose(_sourceFile);
_sourceFile = NULL;
delete[] _sourceBuffer;
_sourceBuffer = NULL;
}
void Benchmark::CodecSpecific_InitBitrate() {
webrtc::SimulcastRateAllocator init_allocator(_env,_inst);
if (_bitRate == 0) {
VideoBitrateAllocation allocation =
init_allocator.Allocate(VideoBitrateAllocationParameters(
DataRate::KilobitsPerSec(600), _inst.maxFramerate));
_encoder->SetRates(webrtc::VideoEncoder::RateControlParameters(
allocation, _inst.maxFramerate));
} else {
VideoBitrateAllocation allocation =
init_allocator.Allocate(VideoBitrateAllocationParameters(
DataRate::BitsPerSec(_bitRate), _inst.maxFramerate));
_encoder->SetRates(webrtc::VideoEncoder::RateControlParameters(
allocation, _inst.maxFramerate));
}
}
bool Benchmark::Encode() {
_lengthEncFrame = 0;
if (_sourceBuffer == NULL) {
_sourceBuffer = new unsigned char[_lengthSourceFrame];
}
auto size = fread(_sourceBuffer, 1, _lengthSourceFrame, _sourceFile);
if (size <= 0) {
return true;
}
webrtc::scoped_refptr<webrtc::I420Buffer> buffer(
webrtc::I420Buffer::Create(_inst.width, _inst.height));
buffer->InitializeData();
memcpy(buffer->MutableDataY(), _sourceBuffer, _lengthSourceFrame);
webrtc::VideoFrame inputVideoBuffer =
webrtc::VideoFrame::Builder()
.set_video_frame_buffer(buffer)
.set_rtp_timestamp(
(unsigned int)(_encFrameCnt * 9e4 / _inst.maxFramerate))
.build();
if (feof(_sourceFile) != 0) {
return true;
}
_encodeCompleteTime = 0;
_encodeTimes[inputVideoBuffer.rtp_timestamp()] = tGetTime();
std::vector<VideoFrameType> frame_types(1, VideoFrameType::kVideoFrameDelta);
_hasReceivedSLI = false;
while (!_signalSLI.empty() && _signalSLI.front().delay == 0) {
_hasReceivedSLI = true;
_pictureIdSLI = _signalSLI.front().id;
_signalSLI.pop_front();
}
for (std::list<fbSignal>::iterator it = _signalSLI.begin();
it != _signalSLI.end(); it++) {
(*it).delay--;
}
_hasReceivedPLI = false;
while (!_signalPLI.empty() && _signalPLI.front().delay == 0) {
_hasReceivedPLI = true;
_signalPLI.pop_front();
}
for (std::list<fbSignal>::iterator it = _signalPLI.begin();
it != _signalPLI.end(); it++) {
(*it).delay--;
}
if (_hasReceivedPLI) {
frame_types[0] = VideoFrameType::kVideoFrameKey;
_hasReceivedPLI = false;
_hasReceivedSLI = false; }
int ret = _encoder->Encode(inputVideoBuffer, &frame_types);
if (_encodeCompleteTime > 0) {
_totalEncodeTime +=
_encodeCompleteTime - _encodeTimes[inputVideoBuffer.rtp_timestamp()];
} else {
_totalEncodeTime += tGetTime() - _encodeTimes[inputVideoBuffer.rtp_timestamp()];
}
assert(ret >= 0);
return false;
}
webrtc::CodecSpecificInfo* Benchmark::CopyCodecSpecificInfo(
const webrtc::CodecSpecificInfo* codecSpecificInfo) const {
webrtc::CodecSpecificInfo* info = new webrtc::CodecSpecificInfo;
*info = *codecSpecificInfo;
return info;
}
void Benchmark::Setup() {
if (_sourceFile != NULL) {
return;
}
std::stringstream ss;
std::string strTestNo;
ss << "0";
ss >> strTestNo;
if (_outname == "") {
_outname =
webrtc::test::OutputPath() + "out_normaltest" + strTestNo + ".yuv";
}
if (_codecName == "") {
_codecName =
webrtc::test::OutputPath() + "encoded_normaltest" + strTestNo + ".yuv";
}
if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL) {
printf("Cannot read file %s.\n", _inname.c_str());
exit(1);
}
if ((_encodedFile = fopen(_codecName.c_str(), "wb")) == NULL) {
printf("Cannot write encoded file.\n");
exit(1);
}
char mode[3] = "wb";
if (_appendNext) {
strncpy(mode, "ab", 3);
}
_appendNext = true;
}