blob: 4d4ff4c0838300132e3ae6a9621c286a3dc3ca60 [file] [log] [blame]
/*
* Copyright (c) 2012 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.
*/
// Implementation of Media Optimization Test
// testing is done via the VCM module, no specific Media opt functionality.
#include "webrtc/modules/video_coding/main/test/media_opt_test.h"
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <vector>
#include "webrtc/modules/rtp_rtcp/interface/rtp_receiver.h"
#include "webrtc/modules/video_coding/main/interface/video_coding.h"
#include "webrtc/modules/video_coding/main/test/test_macros.h"
#include "webrtc/modules/video_coding/main/test/test_util.h"
#include "webrtc/test/testsupport/fileutils.h"
#include "webrtc/test/testsupport/metrics/video_metrics.h"
using namespace webrtc;
int MediaOptTest::RunTest(int testNum, CmdArgs& args)
{
Trace::CreateTrace();
Trace::SetTraceFile((test::OutputPath() + "mediaOptTestTrace.txt").c_str());
Trace::set_level_filter(webrtc::kTraceAll);
VideoCodingModule* vcm = VideoCodingModule::Create(nullptr);
Clock* clock = Clock::GetRealTimeClock();
MediaOptTest* mot = new MediaOptTest(vcm, clock);
if (testNum == 0)
{ // regular
mot->Setup(0, args);
mot->GeneralSetup();
mot->Perform();
mot->Print(1);// print to screen
mot->TearDown();
}
if (testNum == 1)
{ // release test
mot->Setup(0, args);
mot->RTTest();
}
if (testNum == 2)
{ // release test, running from script
mot->Setup(1, args);
mot->GeneralSetup();
mot->Perform();
mot->Print(1);// print to screen
mot->TearDown();
}
VideoCodingModule::Destroy(vcm);
delete mot;
Trace::ReturnTrace();
return 0;
}
MediaOptTest::MediaOptTest(VideoCodingModule* vcm, Clock* clock)
: _vcm(vcm),
_rtp(NULL),
_outgoingTransport(NULL),
_dataCallback(NULL),
_clock(clock),
_width(0),
_height(0),
_lengthSourceFrame(0),
_timeStamp(0),
_frameRate(30.0f),
_nackEnabled(false),
_fecEnabled(false),
_rttMS(0),
_bitRate(300.0f),
_lossRate(0.0f),
_renderDelayMs(0),
_frameCnt(0),
_sumEncBytes(0),
_numFramesDropped(0),
_numberOfCores(4) {
}
MediaOptTest::~MediaOptTest() {
delete _rtp;
}
void MediaOptTest::Setup(int testType, CmdArgs& args) {
/*TEST USER SETTINGS*/
// test parameters
_inname = args.inputFile;
if (args.outputFile == "")
_outname = test::OutputPath() + "MOTest_out.vp8";
else
_outname = args.outputFile;
// actual source after frame dropping
_actualSourcename = test::OutputPath() + "MOTestSource.yuv";
_codecName = args.codecName;
_sendCodecType = args.codecType;
_width = args.width;
_height = args.height;
_frameRate = args.frameRate;
_bitRate = args.bitRate;
_numberOfCores = 4;
// error resilience
_nackEnabled = false;
_fecEnabled = true;
_nackFecEnabled = false;
_rttMS = 100;
_lossRate = 0.00*255; // no packet loss
_testType = testType;
//For multiple runs with script
if (_testType == 1)
{
float rateTest,lossTest;
int numRuns;
_fpinp = fopen("dat_inp","rb");
_fpout = fopen("test_runs/dat_out","ab");
_fpout2 = fopen("test_runs/dat_out2","ab");
TEST(fscanf(_fpinp,"%f %f %d \n",&rateTest,&lossTest,&numRuns) > 0);
_bitRate = rateTest;
_lossRate = lossTest;
_testNum = 0;
// for bit rates: 500, 1000, 2000, 3000,4000
// for loss rates: 0, 1, 3, 5, 10%
_numParRuns = 25;
_testNum = numRuns + 1;
if (rateTest == 0.0) _lossRate = 0.0;
else
{
if (rateTest == 4000) //final bit rate
{
if (lossTest == 0.1*255) _lossRate = 0.0; //start at 1%
else
if (lossTest == 0.05*255) _lossRate = 0.1*255; //final loss rate
else
if (lossTest == 0.0) _lossRate = 0.01*255;
else _lossRate = lossTest + 0.02*255;
}
}
if (rateTest == 0.0 || rateTest == 4000) _bitRate = 500; //starting bit rate
else
if (rateTest == 500) _bitRate = 1000;
else _bitRate = rateTest + 1000;
}
//
_renderDelayMs = 0;
/* test settings end*/
_lengthSourceFrame = 3*_width*_height/2;
_log.open((test::OutputPath() + "VCM_MediaOptLog.txt").c_str(),
std::fstream::out | std::fstream::app);
}
void
MediaOptTest::GeneralSetup()
{
uint32_t minPlayoutDelayMs = 0;
if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL)
{
printf("Cannot read file %s.\n", _inname.c_str());
exit(1);
}
if ((_decodedFile = fopen(_outname.c_str(), "wb")) == NULL)
{
printf("Cannot read file %s.\n", _outname.c_str());
exit(1);
}
if ((_actualSourceFile = fopen(_actualSourcename.c_str(), "wb")) == NULL)
{
printf("Cannot read file %s.\n", _actualSourcename.c_str());
exit(1);
}
if (_vcm->InitializeReceiver() < 0)
{
exit(1);
}
if (_vcm->InitializeSender())
{
exit(1);
}
_outgoingTransport = new RTPSendCompleteCallback(_clock);
_dataCallback = new RtpDataCallback(_vcm);
RtpRtcp::Configuration configuration;
configuration.id = 1;
configuration.audio = false;
configuration.outgoing_transport = _outgoingTransport;
_rtp = RtpRtcp::CreateRtpRtcp(configuration);
_outgoingTransport->SetRtpModule(_rtp);
// Registering codecs for the RTP module
// Register receive and send payload
VideoCodec video_codec;
strncpy(video_codec.plName, "VP8", 32);
video_codec.plType = VCM_VP8_PAYLOAD_TYPE;
rtp_receiver_->RegisterReceivePayload(video_codec.plName,
video_codec.plType,
90000,
0,
video_codec.maxBitrate);
_rtp->RegisterSendPayload(video_codec);
strncpy(video_codec.plName, "ULPFEC", 32);
video_codec.plType = VCM_ULPFEC_PAYLOAD_TYPE;
rtp_receiver_->RegisterReceivePayload(video_codec.plName,
video_codec.plType,
90000,
0,
video_codec.maxBitrate);
_rtp->RegisterSendPayload(video_codec);
strncpy(video_codec.plName, "RED", 32);
video_codec.plType = VCM_RED_PAYLOAD_TYPE;
rtp_receiver_->RegisterReceivePayload(video_codec.plName,
video_codec.plType,
90000,
0,
video_codec.maxBitrate);
_rtp->RegisterSendPayload(video_codec);
if (_nackFecEnabled == 1)
_rtp->SetGenericFECStatus(_nackFecEnabled, VCM_RED_PAYLOAD_TYPE,
VCM_ULPFEC_PAYLOAD_TYPE);
else
_rtp->SetGenericFECStatus(_fecEnabled, VCM_RED_PAYLOAD_TYPE,
VCM_ULPFEC_PAYLOAD_TYPE);
// VCM: Registering codecs
VideoCodec sendCodec;
_vcm->InitializeSender();
_vcm->InitializeReceiver();
int32_t numberOfCodecs = _vcm->NumberOfCodecs();
if (numberOfCodecs < 1)
{
exit(1);
}
if (_vcm->Codec(_sendCodecType, &sendCodec) != 0)
{
printf("Unknown codec\n");
exit(1);
}
// register codec
sendCodec.startBitrate = (int) _bitRate;
sendCodec.height = _height;
sendCodec.width = _width;
sendCodec.maxFramerate = (uint8_t)_frameRate;
_vcm->RegisterSendCodec(&sendCodec, _numberOfCores, 1440);
_vcm->RegisterReceiveCodec(&sendCodec, _numberOfCores); // same settings for encode and decode
_vcm->SetRenderDelay(_renderDelayMs);
_vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs);
}
// The following test shall be conducted under release tests
int32_t
MediaOptTest::Perform()
{
VCMDecodeCompleteCallback receiveCallback(_decodedFile);
VCMRTPEncodeCompleteCallback* encodeCompleteCallback = new VCMRTPEncodeCompleteCallback(_rtp);
_vcm->RegisterTransportCallback(encodeCompleteCallback);
encodeCompleteCallback->SetCodecType(ConvertCodecType(_codecName.c_str()));
encodeCompleteCallback->SetFrameDimensions(_width, _height);
// callback settings
VideoProtectionCallback protectionCallback;
protectionCallback.RegisterRtpModule(_rtp);
_vcm->RegisterProtectionCallback(&protectionCallback);
// set error resilience / test parameters:
_outgoingTransport->SetLossPct(_lossRate);
if (_nackFecEnabled == 1) {
_vcm->SetVideoProtection(kProtectionNackFEC, _nackFecEnabled);
} else {
_vcm->SetVideoProtection(kProtectionNack, _nackEnabled);
_vcm->SetVideoProtection(kProtectionFEC, _fecEnabled);
}
// START TEST
I420VideoFrame sourceFrame;
uint8_t* tmpBuffer = new uint8_t[_lengthSourceFrame];
_vcm->SetChannelParameters(static_cast<uint32_t>(1000 * _bitRate),
(uint8_t)_lossRate, _rttMS);
_vcm->RegisterReceiveCallback(&receiveCallback);
_frameCnt = 0;
_sumEncBytes = 0;
_numFramesDropped = 0;
int half_width = (_width + 1) / 2;
int half_height = (_height + 1) / 2;
int size_y = _width * _height;
int size_uv = half_width * half_height;
while (feof(_sourceFile)== 0)
{
TEST(fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0);
_frameCnt++;
sourceFrame.CreateFrame(size_y, tmpBuffer,
size_uv, tmpBuffer + size_y,
size_uv, tmpBuffer + size_y + size_uv,
_width, _height,
_width, half_width, half_width);
_timeStamp += (uint32_t)(9e4 / static_cast<float>(_frameRate));
sourceFrame.set_timestamp(_timeStamp);
TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
// inform RTP Module of error resilience features
//_rtp->SetFECCodeRate(protectionCallback.FECKeyRate(),protectionCallback.FECDeltaRate());
//_rtp->SetNACKStatus(protectionCallback.NACKMethod());
int32_t ret = _vcm->Decode();
if (ret < 0 )
{
TEST(ret == 0);
printf ("Decode error in frame # %d",_frameCnt);
}
size_t encBytes = encodeCompleteCallback->EncodedBytes();
if (encBytes == 0)
{
_numFramesDropped += 1;
//printf("frame #%d dropped \n", _frameCnt );
}
else
{
// write frame to file
if (PrintI420VideoFrame(sourceFrame, _actualSourceFile) < 0) {
return -1;
}
}
_sumEncBytes += encBytes;
}
//END TEST
delete encodeCompleteCallback;
delete tmpBuffer;
return 0;
}
void
MediaOptTest::RTTest()
{
// will only calculate PSNR - not create output files for all
// SET UP
// Set bit rates
const float bitRateVec[] = {500, 1000, 2000,3000, 4000};
//const float bitRateVec[] = {1000};
// Set Packet loss values ([0,255])
const double lossPctVec[] = {0.0*255, 0.0*255, 0.01*255, 0.01*255, 0.03*255, 0.03*255, 0.05*255, 0.05*255, 0.1*255, 0.1*255};
const bool nackEnabledVec[] = {false , false, false, false, false, false, false, false , false, false};
const bool fecEnabledVec[] = {false , true, false, true , false, true , false, true , false, true};
// fec and nack are set according to the packet loss values
const float nBitrates = sizeof(bitRateVec)/sizeof(*bitRateVec);
const float nlossPct = sizeof(lossPctVec)/sizeof(*lossPctVec);
std::vector<const VideoSource*> sources;
std::vector<const VideoSource*>::iterator it;
sources.push_back(new const VideoSource(_inname, _width, _height));
int numOfSrc = 1;
// constant settings (valid for entire run time)
_rttMS = 20;
_renderDelayMs = 0;
// same out name for all
_outname = test::OutputPath() + "RTMOTest_out.yuv";
// actual source after frame dropping
_actualSourcename = test::OutputPath() + "RTMOTestSource.yuv";
_codecName = "VP8"; // for now just this one - later iterate over all codec types
_log.open((test::OutputPath() + "/VCM_RTMediaOptLog.txt").c_str(),
std::fstream::out | std::fstream::app);
_outputRes=fopen((test::OutputPath() + "VCM_MediaOptResults.txt").c_str(),
"ab");
//char filename[128];
/* test settings end*/
// START TEST
// iterate over test sequences
printf("\n****START TEST OVER ALL RUNS ****\n");
int runCnt = 0;
for (it = sources.begin() ; it < sources.end(); it++)
{
// test set up
_inname = (*it)->GetFileName();
_width = (*it)->GetWidth();
_height = (*it)->GetHeight();
_lengthSourceFrame = 3*_width*_height/2;
_frameRate = (*it)->GetFrameRate();
//GeneralSetup();
// iterate over all bit rates
for (int i = 0; i < nBitrates; i++)
{
_bitRate = static_cast<float>(bitRateVec[i]);
// iterate over all packet loss values
for (int j = 0; j < nlossPct; j++)
{
_lossRate = static_cast<float>(lossPctVec[j]);
_nackEnabled = static_cast<bool>(nackEnabledVec[j]);
_fecEnabled = static_cast<bool>(fecEnabledVec[j]);
runCnt++;
printf("run #%d out of %d \n", runCnt,(int)(nlossPct*nBitrates*numOfSrc));
//printf("**FOR RUN: **%d %d %d %d \n",_nackEnabled,_fecEnabled,int(lossPctVec[j]),int(_bitRate));
/*
int ch = sprintf(filename,"../test_mediaOpt/RTMOTest_%d_%d_%d_%d.yuv",_nackEnabled,_fecEnabled,int(lossPctVec[j]),int(_bitRate));
_outname = filename;
printf("**FOR RUN: **%d %d %d %d \n",_nackEnabled,_fecEnabled,int(lossPctVec[j]),int(_bitRate));
*/
GeneralSetup();
Perform();
Print(1);
TearDown();
printf("\n");
//printf("**DONE WITH RUN: **%d %d %f %d \n",_nackEnabled,_fecEnabled,lossPctVec[j],int(_bitRate));
//
}// end of packet loss loop
}// end of bit rate loop
delete *it;
}// end of video sequence loop
// at end of sequence
fclose(_outputRes);
printf("\nVCM Media Optimization Test: \n\n%i tests completed\n", vcmMacrosTests);
if (vcmMacrosErrors > 0)
{
printf("%i FAILED\n\n", vcmMacrosErrors);
}
else
{
printf("ALL PASSED\n\n");
}
}
void
MediaOptTest::Print(int mode)
{
double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _frameRate));
double actualBitRate = ActualBitRate / 1000.0;
webrtc::test::QualityMetricsResult psnr;
I420PSNRFromFiles(_actualSourcename.c_str(), _outname.c_str(), _width,
_height, &psnr);
(_log) << "VCM: Media Optimization Test Cycle Completed!" << std::endl;
(_log) << "Input file: " << _inname << std::endl;
(_log) << "Output file:" << _outname << std::endl;
( _log) << "Actual bitrate: " << actualBitRate<< " kbps\tTarget: " << _bitRate << " kbps" << std::endl;
(_log) << "Error Reslience: NACK:" << _nackEnabled << "; FEC: " << _fecEnabled << std::endl;
(_log) << "Packet Loss applied= %f " << _lossRate << std::endl;
(_log) << _numFramesDropped << " FRames were dropped" << std::endl;
( _log) << "PSNR: " << psnr.average << std::endl;
(_log) << std::endl;
if (_testType == 2)
{
fprintf(_outputRes,"************\n");
fprintf(_outputRes,"\n\n\n");
fprintf(_outputRes,"Actual bitrate: %f kbps\n", actualBitRate);
fprintf(_outputRes,"Target bitrate: %f kbps\n", _bitRate);
fprintf(_outputRes,"NACK: %s ",(_nackEnabled)?"true":"false");
fprintf(_outputRes,"FEC: %s \n ",(_fecEnabled)?"true":"false");
fprintf(_outputRes,"Packet loss applied = %f\n", _lossRate);
fprintf(_outputRes,"%d frames were dropped, and total number of frames processed %d \n",_numFramesDropped,_frameCnt);
fprintf(_outputRes,"PSNR: %f \n", psnr.average);
fprintf(_outputRes,"************\n");
}
//
if (_testType == 1)
{
fprintf(_fpout,"************\n");
fprintf(_fpout,"\n\n\n");
fprintf(_fpout,"Actual bitrate: %f kbps\n", actualBitRate);
fprintf(_fpout,"Target bitrate: %f kbps\n", _bitRate);
fprintf(_fpout,"NACK: %s ",(_nackEnabled)?"true":"false");
fprintf(_fpout,"FEC: %s \n ",(_fecEnabled)?"true":"false");
fprintf(_fpout,"Packet loss applied = %f\n", _lossRate);
fprintf(_fpout,"%d frames were dropped, and total number of frames processed %d \n",_numFramesDropped,_frameCnt);
fprintf(_fpout,"PSNR: %f \n", psnr.average);
fprintf(_fpout,"************\n");
int testNum1 = _testNum/(_numParRuns +1) + 1;
int testNum2 = _testNum%_numParRuns;
if (testNum2 == 0) testNum2 = _numParRuns;
fprintf(_fpout2,"%d %d %f %f %f %f \n",testNum1,testNum2,_bitRate,actualBitRate,_lossRate,psnr.average);
fclose(_fpinp);
_fpinp = fopen("dat_inp","wb");
fprintf(_fpinp,"%f %f %d \n",_bitRate,_lossRate,_testNum);
}
//
if (mode == 1)
{
// print to screen
printf("\n\n\n");
printf("Actual bitrate: %f kbps\n", actualBitRate);
printf("Target bitrate: %f kbps\n", _bitRate);
printf("NACK: %s ",(_nackEnabled)?"true":"false");
printf("FEC: %s \n",(_fecEnabled)?"true":"false");
printf("Packet loss applied = %f\n", _lossRate);
printf("%d frames were dropped, and total number of frames processed %d \n",_numFramesDropped,_frameCnt);
printf("PSNR: %f \n", psnr.average);
}
TEST(psnr.average > 10); // low becuase of possible frame dropping (need to verify that OK for all packet loss values/ rates)
}
void MediaOptTest::TearDown() {
delete _rtp;
_rtp = NULL;
delete _outgoingTransport;
_outgoingTransport = NULL;
delete _dataCallback;
_dataCallback = NULL;
_log.close();
fclose(_sourceFile);
fclose(_decodedFile);
fclose(_actualSourceFile);
}