Added possibility to run quality modes test. Added possibility to input arguments to the test. The test will (for each frame) log the values in contentMetrics to a txt-file. The txt-file can optionally be saved in a specific place. Fixed an issue where video_coding_test crashed if there weren't any parameter submitted to an input argument.

BUG=

Review URL: https://webrtc-codereview.appspot.com/772005

git-svn-id: http://webrtc.googlecode.com/svn/trunk@3068 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
brykt@google.com
2012-11-09 16:16:41 +00:00
parent 9cb9fc17b1
commit e8ef807a2d
7 changed files with 886 additions and 749 deletions

View File

@@ -27,7 +27,7 @@
using namespace webrtc; using namespace webrtc;
int NormalTest::RunTest(CmdArgs& args) int NormalTest::RunTest(const CmdArgs& args)
{ {
#if defined(EVENT_DEBUG) #if defined(EVENT_DEBUG)
printf("SIMULATION TIME\n"); printf("SIMULATION TIME\n");
@@ -67,8 +67,8 @@ VCMNTEncodeCompleteCallback::~VCMNTEncodeCompleteCallback()
{ {
} }
void void VCMNTEncodeCompleteCallback::RegisterTransportCallback(
VCMNTEncodeCompleteCallback::RegisterTransportCallback(VCMPacketizationCallback* transport) VCMPacketizationCallback* transport)
{ {
} }
@@ -84,73 +84,73 @@ VCMNTEncodeCompleteCallback::SendData(
const webrtc::RTPVideoHeader* videoHdr) const webrtc::RTPVideoHeader* videoHdr)
{ {
// will call the VCMReceiver input packet // will call the VCMReceiver input packet
_frameType = frameType; _frameType = frameType;
// writing encodedData into file // writing encodedData into file
if (fwrite(payloadData, 1, payloadSize, _encodedFile) != payloadSize) { if (fwrite(payloadData, 1, payloadSize, _encodedFile) != payloadSize) {
return -1; return -1;
} }
WebRtcRTPHeader rtpInfo; WebRtcRTPHeader rtpInfo;
rtpInfo.header.markerBit = true; rtpInfo.header.markerBit = true;
rtpInfo.type.Video.width = 0; rtpInfo.type.Video.width = 0;
rtpInfo.type.Video.height = 0; rtpInfo.type.Video.height = 0;
switch (_test.VideoType()) switch (_test.VideoType())
{ {
case kVideoCodecVP8: case kVideoCodecVP8:
rtpInfo.type.Video.codec = kRTPVideoVP8; rtpInfo.type.Video.codec = kRTPVideoVP8;
rtpInfo.type.Video.codecHeader.VP8.InitRTPVideoHeaderVP8(); rtpInfo.type.Video.codecHeader.VP8.InitRTPVideoHeaderVP8();
rtpInfo.type.Video.codecHeader.VP8.nonReference = rtpInfo.type.Video.codecHeader.VP8.nonReference =
videoHdr->codecHeader.VP8.nonReference; videoHdr->codecHeader.VP8.nonReference;
rtpInfo.type.Video.codecHeader.VP8.pictureId = rtpInfo.type.Video.codecHeader.VP8.pictureId =
videoHdr->codecHeader.VP8.pictureId; videoHdr->codecHeader.VP8.pictureId;
break; break;
case kVideoCodecI420: case kVideoCodecI420:
rtpInfo.type.Video.codec = kRTPVideoI420; rtpInfo.type.Video.codec = kRTPVideoI420;
break; break;
default: default:
assert(false); assert(false);
return -1; return -1;
} }
rtpInfo.header.payloadType = payloadType; rtpInfo.header.payloadType = payloadType;
rtpInfo.header.sequenceNumber = _seqNo++; rtpInfo.header.sequenceNumber = _seqNo++;
rtpInfo.header.ssrc = 0; rtpInfo.header.ssrc = 0;
rtpInfo.header.timestamp = timeStamp; rtpInfo.header.timestamp = timeStamp;
rtpInfo.frameType = frameType; rtpInfo.frameType = frameType;
rtpInfo.type.Video.isFirstPacket = true; rtpInfo.type.Video.isFirstPacket = true;
// Size should also be received from that table, since the payload type // Size should also be received from that table, since the payload type
// defines the size. // defines the size.
_encodedBytes += payloadSize; _encodedBytes += payloadSize;
if (payloadSize < 20) if (payloadSize < 20)
{ {
_skipCnt++; _skipCnt++;
} }
_VCMReceiver->IncomingPacket(payloadData, payloadSize, rtpInfo); _VCMReceiver->IncomingPacket(payloadData, payloadSize, rtpInfo);
return 0; return 0;
} }
void void
VCMNTEncodeCompleteCallback::RegisterReceiverVCM(VideoCodingModule *vcm) VCMNTEncodeCompleteCallback::RegisterReceiverVCM(VideoCodingModule *vcm)
{ {
_VCMReceiver = vcm; _VCMReceiver = vcm;
return; return;
} }
WebRtc_Word32 WebRtc_Word32
VCMNTEncodeCompleteCallback::EncodedBytes() VCMNTEncodeCompleteCallback::EncodedBytes()
{ {
return _encodedBytes; return _encodedBytes;
} }
WebRtc_UWord32 WebRtc_UWord32
VCMNTEncodeCompleteCallback::SkipCnt() VCMNTEncodeCompleteCallback::SkipCnt()
{ {
return _skipCnt; return _skipCnt;
} }
// Decoded Frame Callback Implementation // Decoded Frame Callback Implementation
VCMNTDecodeCompleCallback::~VCMNTDecodeCompleCallback() VCMNTDecodeCompleCallback::~VCMNTDecodeCompleCallback()
{ {
if (_decodedFile) if (_decodedFile)
fclose(_decodedFile); fclose(_decodedFile);
} }
WebRtc_Word32 WebRtc_Word32
VCMNTDecodeCompleCallback::FrameToRender(webrtc::I420VideoFrame& videoFrame) VCMNTDecodeCompleCallback::FrameToRender(webrtc::I420VideoFrame& videoFrame)
@@ -178,7 +178,7 @@ VCMNTDecodeCompleCallback::FrameToRender(webrtc::I420VideoFrame& videoFrame)
WebRtc_Word32 WebRtc_Word32
VCMNTDecodeCompleCallback::DecodedBytes() VCMNTDecodeCompleCallback::DecodedBytes()
{ {
return _decodedBytes; return _decodedBytes;
} }
//VCM Normal Test Class implementation //VCM Normal Test Class implementation
@@ -207,210 +207,216 @@ NormalTest::~NormalTest()
// //
} }
void void
NormalTest::Setup(CmdArgs& args) NormalTest::Setup(const CmdArgs& args)
{ {
_inname = args.inputFile; _inname = args.inputFile;
_encodedName = test::OutputPath() + "encoded_normaltest.yuv"; _encodedName = test::OutputPath() + "encoded_normaltest.yuv";
_width = args.width; _width = args.width;
_height = args.height; _height = args.height;
_frameRate = args.frameRate; _frameRate = args.frameRate;
_bitRate = args.bitRate; _bitRate = args.bitRate;
if (args.outputFile == "") if (args.outputFile == "")
{ {
std::ostringstream filename; std::ostringstream filename;
filename << test::OutputPath() << "NormalTest_" << filename << test::OutputPath() << "NormalTest_" <<
_width << "x" << _height << "_" << _frameRate << "Hz_P420.yuv"; _width << "x" << _height << "_" << _frameRate << "Hz_P420.yuv";
_outname = filename.str(); _outname = filename.str();
} }
else else
{ {
_outname = args.outputFile; _outname = args.outputFile;
} }
_lengthSourceFrame = 3*_width*_height/2; _lengthSourceFrame = 3*_width*_height/2;
_videoType = args.codecType; _videoType = args.codecType;
if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL) if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL)
{ {
printf("Cannot read file %s.\n", _inname.c_str()); printf("Cannot read file %s.\n", _inname.c_str());
exit(1); exit(1);
} }
if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL) if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL)
{ {
printf("Cannot write encoded file.\n"); printf("Cannot write encoded file.\n");
exit(1); exit(1);
} }
_log.open((test::OutputPath() + "TestLog.txt").c_str(), _log.open((test::OutputPath() + "TestLog.txt").c_str(),
std::fstream::out | std::fstream::app); std::fstream::out | std::fstream::app);
return;
} }
WebRtc_Word32 WebRtc_Word32
NormalTest::Perform(CmdArgs& args) NormalTest::Perform(const CmdArgs& args)
{ {
Setup(args); Setup(args);
EventWrapper* waitEvent = EventWrapper::Create(); EventWrapper* waitEvent = EventWrapper::Create();
VideoCodec _sendCodec;//, _receiveCodec; // tmp - sendCodecd used as receive codec VideoCodec _sendCodec;
_vcm->InitializeReceiver(); _vcm->InitializeReceiver();
_vcm->InitializeSender(); _vcm->InitializeSender();
TEST(VideoCodingModule::Codec(_videoType, &_sendCodec) == VCM_OK); TEST(VideoCodingModule::Codec(_videoType, &_sendCodec) == VCM_OK);
_sendCodec.startBitrate = (int)_bitRate; // should be later on changed via the API // should be later on changed via the API
_sendCodec.width = static_cast<WebRtc_UWord16>(_width); _sendCodec.startBitrate = (int)_bitRate;
_sendCodec.height = static_cast<WebRtc_UWord16>(_height); _sendCodec.width = static_cast<WebRtc_UWord16>(_width);
_sendCodec.maxFramerate = _frameRate; _sendCodec.height = static_cast<WebRtc_UWord16>(_height);
TEST(_vcm->RegisterSendCodec(&_sendCodec, 4, 1400) == VCM_OK);// will also set and init the desired codec _sendCodec.maxFramerate = _frameRate;
// register a decoder (same codec for decoder and encoder ) // will also set and init the desired codec
TEST(_vcm->RegisterReceiveCodec(&_sendCodec, 1) == VCM_OK); TEST(_vcm->RegisterSendCodec(&_sendCodec, 4, 1400) == VCM_OK);
/* Callback Settings */ // register a decoder (same codec for decoder and encoder )
VCMNTDecodeCompleCallback _decodeCallback(_outname); TEST(_vcm->RegisterReceiveCodec(&_sendCodec, 1) == VCM_OK);
_vcm->RegisterReceiveCallback(&_decodeCallback); /* Callback Settings */
VCMNTEncodeCompleteCallback _encodeCompleteCallback(_encodedFile, *this); VCMNTDecodeCompleCallback _decodeCallback(_outname);
_vcm->RegisterTransportCallback(&_encodeCompleteCallback); _vcm->RegisterReceiveCallback(&_decodeCallback);
// encode and decode with the same vcm VCMNTEncodeCompleteCallback _encodeCompleteCallback(_encodedFile, *this);
_encodeCompleteCallback.RegisterReceiverVCM(_vcm); _vcm->RegisterTransportCallback(&_encodeCompleteCallback);
/////////////////////// // encode and decode with the same vcm
/// Start Test _encodeCompleteCallback.RegisterReceiverVCM(_vcm);
/////////////////////// ///////////////////////
I420VideoFrame sourceFrame; /// Start Test
int size_y = _width * _height; ///////////////////////
int half_width = (_width + 1) / 2; I420VideoFrame sourceFrame;
int half_height = (_height + 1) / 2; int size_y = _width * _height;
int size_uv = half_width * half_height; int half_width = (_width + 1) / 2;
sourceFrame.CreateEmptyFrame(_width, _height, int half_height = (_height + 1) / 2;
_width, half_width, half_width); int size_uv = half_width * half_height;
WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame]; sourceFrame.CreateEmptyFrame(_width, _height,
double startTime = clock()/(double)CLOCKS_PER_SEC; _width, half_width, half_width);
_vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 0); WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame];
double startTime = clock()/(double)CLOCKS_PER_SEC;
_vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 0);
SendStatsTest sendStats; SendStatsTest sendStats;
sendStats.SetTargetFrameRate(static_cast<WebRtc_UWord32>(_frameRate)); sendStats.SetTargetFrameRate(static_cast<WebRtc_UWord32>(_frameRate));
_vcm->RegisterSendStatisticsCallback(&sendStats); _vcm->RegisterSendStatisticsCallback(&sendStats);
while (feof(_sourceFile) == 0) while (feof(_sourceFile) == 0) {
{
#if !defined(EVENT_DEBUG) #if !defined(EVENT_DEBUG)
WebRtc_Word64 processStartTime = _clock->MillisecondTimestamp(); WebRtc_Word64 processStartTime = _clock->MillisecondTimestamp();
#endif
TEST(fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0 ||
feof(_sourceFile));
_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 += (WebRtc_UWord32)(9e4 / static_cast<float>(_sendCodec.maxFramerate));
sourceFrame.set_timestamp(_timeStamp);
_encodeTimes[int(sourceFrame.timestamp())] =
clock()/(double)CLOCKS_PER_SEC;
WebRtc_Word32 ret = _vcm->AddVideoFrame(sourceFrame);
double encodeTime = clock()/(double)CLOCKS_PER_SEC -
_encodeTimes[int(sourceFrame.timestamp())];
_totalEncodeTime += encodeTime;
if (ret < 0)
{
printf("Error in AddFrame: %d\n", ret);
//exit(1);
}
_decodeTimes[int(sourceFrame.timestamp())] =
clock()/(double)CLOCKS_PER_SEC;
ret = _vcm->Decode();
_totalDecodeTime += clock()/(double)CLOCKS_PER_SEC -
_decodeTimes[int(sourceFrame.timestamp())];
if (ret < 0)
{
printf("Error in Decode: %d\n", ret);
//exit(1);
}
if (_vcm->TimeUntilNextProcess() <= 0)
{
_vcm->Process();
}
WebRtc_UWord32 framePeriod = static_cast<WebRtc_UWord32>(1000.0f/static_cast<float>(_sendCodec.maxFramerate) + 0.5f);
#if defined(EVENT_DEBUG)
static_cast<FakeTickTime*>(_clock)->IncrementDebugClock(framePeriod);
#else
WebRtc_Word64 timeSpent = _clock->MillisecondTimestamp() - processStartTime;
if (timeSpent < framePeriod)
{
waitEvent->Wait(framePeriod - timeSpent);
}
#endif #endif
TEST(fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0 ||
feof(_sourceFile));
_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 +=
(WebRtc_UWord32)(9e4 / static_cast<float>(_sendCodec.maxFramerate));
sourceFrame.set_timestamp(_timeStamp);
_encodeTimes[int(sourceFrame.timestamp())] =
clock()/(double)CLOCKS_PER_SEC;
WebRtc_Word32 ret = _vcm->AddVideoFrame(sourceFrame);
double encodeTime = clock()/(double)CLOCKS_PER_SEC -
_encodeTimes[int(sourceFrame.timestamp())];
_totalEncodeTime += encodeTime;
if (ret < 0)
{
printf("Error in AddFrame: %d\n", ret);
//exit(1);
} }
double endTime = clock()/(double)CLOCKS_PER_SEC; _decodeTimes[int(sourceFrame.timestamp())] =
_testTotalTime = endTime - startTime; clock()/(double)CLOCKS_PER_SEC;
_sumEncBytes = _encodeCompleteCallback.EncodedBytes(); ret = _vcm->Decode();
_totalDecodeTime += clock()/(double)CLOCKS_PER_SEC -
_decodeTimes[int(sourceFrame.timestamp())];
if (ret < 0)
{
printf("Error in Decode: %d\n", ret);
//exit(1);
}
if (_vcm->TimeUntilNextProcess() <= 0)
{
_vcm->Process();
}
WebRtc_UWord32 framePeriod =
static_cast<WebRtc_UWord32>(
1000.0f / static_cast<float>(_sendCodec.maxFramerate) + 0.5f);
delete [] tmpBuffer; #if defined(EVENT_DEBUG)
delete waitEvent; static_cast<FakeTickTime*>(_clock)->IncrementDebugClock(framePeriod);
Teardown(); #else
Print(); WebRtc_Word64 timeSpent =
return 0; _clock->MillisecondTimestamp() - processStartTime;
if (timeSpent < framePeriod)
{
waitEvent->Wait(framePeriod - timeSpent);
}
#endif
}
double endTime = clock()/(double)CLOCKS_PER_SEC;
_testTotalTime = endTime - startTime;
_sumEncBytes = _encodeCompleteCallback.EncodedBytes();
delete [] tmpBuffer;
delete waitEvent;
Teardown();
Print();
return 0;
} }
void void
NormalTest::FrameEncoded(WebRtc_UWord32 timeStamp) NormalTest::FrameEncoded(WebRtc_UWord32 timeStamp)
{ {
_encodeCompleteTime = clock()/(double)CLOCKS_PER_SEC; _encodeCompleteTime = clock()/(double)CLOCKS_PER_SEC;
_encFrameCnt++; _encFrameCnt++;
_totalEncodePipeTime += _encodeCompleteTime - _encodeTimes[int(timeStamp)]; _totalEncodePipeTime += _encodeCompleteTime - _encodeTimes[int(timeStamp)];
} }
void void
NormalTest::FrameDecoded(WebRtc_UWord32 timeStamp) NormalTest::FrameDecoded(WebRtc_UWord32 timeStamp)
{ {
_decodeCompleteTime = clock()/(double)CLOCKS_PER_SEC; _decodeCompleteTime = clock()/(double)CLOCKS_PER_SEC;
_decFrameCnt++; _decFrameCnt++;
_totalDecodePipeTime += _decodeCompleteTime - _decodeTimes[timeStamp]; _totalDecodePipeTime += _decodeCompleteTime - _decodeTimes[timeStamp];
} }
void void
NormalTest::Print() NormalTest::Print()
{ {
std::cout << "Normal Test Completed!" << std::endl; std::cout << "Normal Test Completed!" << std::endl;
(_log) << "Normal Test Completed!" << std::endl; (_log) << "Normal Test Completed!" << std::endl;
(_log) << "Input file: " << _inname << std::endl; (_log) << "Input file: " << _inname << std::endl;
(_log) << "Output file: " << _outname << std::endl; (_log) << "Output file: " << _outname << std::endl;
(_log) << "Total run time: " << _testTotalTime << std::endl; (_log) << "Total run time: " << _testTotalTime << std::endl;
printf("Total run time: %f s \n", _testTotalTime); printf("Total run time: %f s \n", _testTotalTime);
double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _frameRate)); double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _frameRate));
double actualBitRate = ActualBitRate / 1000.0; double actualBitRate = ActualBitRate / 1000.0;
double avgEncTime = _totalEncodeTime / _frameCnt; double avgEncTime = _totalEncodeTime / _frameCnt;
double avgDecTime = _totalDecodeTime / _frameCnt; double avgDecTime = _totalDecodeTime / _frameCnt;
webrtc::test::QualityMetricsResult psnr, ssim; webrtc::test::QualityMetricsResult psnr, ssim;
I420PSNRFromFiles(_inname.c_str(), _outname.c_str(), _width, _height, I420PSNRFromFiles(_inname.c_str(), _outname.c_str(), _width, _height,
&psnr); &psnr);
I420SSIMFromFiles(_inname.c_str(), _outname.c_str(), _width, _height, I420SSIMFromFiles(_inname.c_str(), _outname.c_str(), _width, _height,
&ssim); &ssim);
printf("Actual bitrate: %f kbps\n", actualBitRate); printf("Actual bitrate: %f kbps\n", actualBitRate);
printf("Target bitrate: %f kbps\n", _bitRate); printf("Target bitrate: %f kbps\n", _bitRate);
( _log) << "Actual bitrate: " << actualBitRate<< " kbps\tTarget: " << _bitRate << " kbps" << std::endl; ( _log) << "Actual bitrate: " << actualBitRate <<
printf("Average encode time: %f s\n", avgEncTime); " kbps\tTarget: " << _bitRate << " kbps" << std::endl;
( _log) << "Average encode time: " << avgEncTime << " s" << std::endl; printf("Average encode time: %f s\n", avgEncTime);
printf("Average decode time: %f s\n", avgDecTime); ( _log) << "Average encode time: " << avgEncTime << " s" << std::endl;
( _log) << "Average decode time: " << avgDecTime << " s" << std::endl; printf("Average decode time: %f s\n", avgDecTime);
printf("PSNR: %f \n", psnr.average); ( _log) << "Average decode time: " << avgDecTime << " s" << std::endl;
( _log) << "PSNR: " << psnr.average << std::endl; printf("PSNR: %f \n", psnr.average);
printf("SSIM: %f \n", ssim.average); ( _log) << "PSNR: " << psnr.average << std::endl;
( _log) << "SSIM: " << ssim.average << std::endl; printf("SSIM: %f \n", ssim.average);
(_log) << std::endl; ( _log) << "SSIM: " << ssim.average << std::endl;
(_log) << std::endl;
printf("\nVCM Normal Test: \n\n%i tests completed\n", vcmMacrosTests); printf("\nVCM Normal Test: \n\n%i tests completed\n", vcmMacrosTests);
if (vcmMacrosErrors > 0) if (vcmMacrosErrors > 0)
{ {
printf("%i FAILED\n\n", vcmMacrosErrors); printf("%i FAILED\n\n", vcmMacrosErrors);
} }
else else
{ {
printf("ALL PASSED\n\n"); printf("ALL PASSED\n\n");
} }
} }
void void
NormalTest::Teardown() NormalTest::Teardown()
{ {
//_log.close(); //_log.close();
fclose(_sourceFile); fclose(_sourceFile);
fclose(_encodedFile); fclose(_encodedFile);
return; return;
} }

View File

@@ -18,42 +18,46 @@
class NormalTest; class NormalTest;
//Send Side - Packetization callback - will create and send a packet to the VCMReceiver //Send Side - Packetization callback -
// will create and send a packet to the VCMReceiver
class VCMNTEncodeCompleteCallback : public webrtc::VCMPacketizationCallback class VCMNTEncodeCompleteCallback : public webrtc::VCMPacketizationCallback
{ {
public: public:
// constructor input: file in which encoded data will be written // constructor input: file in which encoded data will be written
VCMNTEncodeCompleteCallback(FILE* encodedFile, NormalTest& test); VCMNTEncodeCompleteCallback(FILE* encodedFile, NormalTest& test);
virtual ~VCMNTEncodeCompleteCallback(); virtual ~VCMNTEncodeCompleteCallback();
// Register transport callback // Register transport callback
void RegisterTransportCallback(webrtc::VCMPacketizationCallback* transport); void RegisterTransportCallback(webrtc::VCMPacketizationCallback* transport);
// process encoded data received from the encoder, pass stream to the VCMReceiver module // process encoded data received from the encoder,
WebRtc_Word32 SendData(const webrtc::FrameType frameType, // pass stream to the VCMReceiver module
const WebRtc_UWord8 payloadType, WebRtc_Word32
const WebRtc_UWord32 timeStamp, SendData(const webrtc::FrameType frameType,
int64_t capture_time_ms, const WebRtc_UWord8 payloadType,
const WebRtc_UWord8* payloadData, const WebRtc_UWord32 timeStamp,
const WebRtc_UWord32 payloadSize, int64_t capture_time_ms,
const webrtc::RTPFragmentationHeader& fragmentationHeader, const WebRtc_UWord8* payloadData,
const webrtc::RTPVideoHeader* videoHdr); const WebRtc_UWord32 payloadSize,
const webrtc::RTPFragmentationHeader& fragmentationHeader,
const webrtc::RTPVideoHeader* videoHdr);
// Register exisitng VCM. Currently - encode and decode with the same vcm module. // Register exisitng VCM.
void RegisterReceiverVCM(webrtc::VideoCodingModule *vcm); // Currently - encode and decode with the same vcm module.
// Return sum of encoded data (all frames in the sequence) void RegisterReceiverVCM(webrtc::VideoCodingModule *vcm);
WebRtc_Word32 EncodedBytes(); // Return sum of encoded data (all frames in the sequence)
// return number of encoder-skipped frames WebRtc_Word32 EncodedBytes();
WebRtc_UWord32 SkipCnt();; // return number of encoder-skipped frames
// conversion function for payload type (needed for the callback function) WebRtc_UWord32 SkipCnt();;
// conversion function for payload type (needed for the callback function)
// RTPVideoVideoCodecTypes ConvertPayloadType(WebRtc_UWord8 payloadType); // RTPVideoVideoCodecTypes ConvertPayloadType(WebRtc_UWord8 payloadType);
private: private:
FILE* _encodedFile; FILE* _encodedFile;
WebRtc_UWord32 _encodedBytes; WebRtc_UWord32 _encodedBytes;
WebRtc_UWord32 _skipCnt; WebRtc_UWord32 _skipCnt;
webrtc::VideoCodingModule* _VCMReceiver; webrtc::VideoCodingModule* _VCMReceiver;
webrtc::FrameType _frameType; webrtc::FrameType _frameType;
WebRtc_UWord16 _seqNo; WebRtc_UWord16 _seqNo;
NormalTest& _test; NormalTest& _test;
}; // end of VCMEncodeCompleteCallback }; // end of VCMEncodeCompleteCallback
class VCMNTDecodeCompleCallback: public webrtc::VCMReceiveCallback class VCMNTDecodeCompleCallback: public webrtc::VCMReceiveCallback
@@ -76,18 +80,16 @@ private:
int _decodedBytes; int _decodedBytes;
int _currentWidth; int _currentWidth;
int _currentHeight; int _currentHeight;
}; // end of VCMDecodeCompleCallback class }; // end of VCMDecodeCompleCallback class
class NormalTest class NormalTest
{ {
public: public:
NormalTest(webrtc::VideoCodingModule* vcm, NormalTest(webrtc::VideoCodingModule* vcm,
webrtc::TickTimeBase* clock); webrtc::TickTimeBase* clock);
~NormalTest(); ~NormalTest();
static int RunTest(CmdArgs& args); static int RunTest(const CmdArgs& args);
WebRtc_Word32 Perform(CmdArgs& args); WebRtc_Word32 Perform(const CmdArgs& args);
// option:: turn into private and call from perform // option:: turn into private and call from perform
int Width() const { return _width; }; int Width() const { return _width; };
int Height() const { return _height; }; int Height() const { return _height; };
@@ -96,7 +98,7 @@ public:
protected: protected:
// test setup - open files, general initializations // test setup - open files, general initializations
void Setup(CmdArgs& args); void Setup(const CmdArgs& args);
// close open files, delete used memory // close open files, delete used memory
void Teardown(); void Teardown();
// print results to std output and to log file // print results to std output and to log file

View File

@@ -10,34 +10,34 @@
#include "quality_modes_test.h" #include "quality_modes_test.h"
#include <time.h>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <time.h> #include <sstream>
#include "../source/event.h"
#include "common_video/libyuv/include/webrtc_libyuv.h" #include "common_video/libyuv/include/webrtc_libyuv.h"
#include "modules/video_coding/main/source/event.h"
#include "modules/video_coding/main/source/mock/fake_tick_time.h"
#include "modules/video_coding/main/source/tick_time_base.h" #include "modules/video_coding/main/source/tick_time_base.h"
#include "test_callbacks.h" #include "modules/video_coding/main/test/test_callbacks.h"
#include "test_macros.h" #include "modules/video_coding/main/test/test_macros.h"
#include "modules/video_coding/main/test/test_util.h"
#include "system_wrappers/interface/data_log.h"
#include "system_wrappers/interface/data_log.h"
#include "testsupport/metrics/video_metrics.h" #include "testsupport/metrics/video_metrics.h"
using namespace webrtc; using namespace webrtc;
int qualityModeTest() int qualityModeTest(const CmdArgs& args)
{ {
// Don't run this test with debug events. FakeTickTime clock(0);
#if defined(EVENT_DEBUG) VideoCodingModule* vcm = VideoCodingModule::Create(1, &clock);
return -1; QualityModesTest QMTest(vcm, &clock);
#endif QMTest.Perform(args);
TickTimeBase clock; VideoCodingModule::Destroy(vcm);
VideoCodingModule* vcm = VideoCodingModule::Create(1, &clock); return 0;
QualityModesTest QMTest(vcm, &clock);
QMTest.Perform();
VideoCodingModule::Destroy(vcm);
return 0;
} }
QualityModesTest::QualityModesTest(VideoCodingModule* vcm, QualityModesTest::QualityModesTest(VideoCodingModule* vcm,
TickTimeBase* clock): TickTimeBase* clock):
NormalTest(vcm, clock), NormalTest(vcm, clock),
@@ -46,320 +46,377 @@ _vpm()
// //
} }
QualityModesTest::~QualityModesTest() QualityModesTest::~QualityModesTest()
{ {
// //
} }
void void
QualityModesTest::Setup() QualityModesTest::Setup(const CmdArgs& args)
{ {
NormalTest::Setup(args);
_inname = args.inputFile;
_outname = args.outputFile;
fv_outfilename_ = args.fv_outputfile;
filename_testvideo_ =
_inname.substr(_inname.find_last_of("\\/") + 1,_inname.length());
_inname= test::ProjectRootPath() + "resources/crew_30f_4CIF.yuv"; _encodedName = test::OutputPath() + "encoded_qmtest.yuv";
_outname = test::OutputPath() + "out_qmtest.yuv";
_encodedName = test::OutputPath() + "encoded_qmtest.yuv";
//NATIVE/SOURCE VALUES //NATIVE/SOURCE VALUES
_nativeWidth = 2*352; _nativeWidth = args.width;
_nativeHeight = 2*288; _nativeHeight = args.height;
_nativeFrameRate = 30; _nativeFrameRate =args.frameRate;
//TARGET/ENCODER VALUES
_width = args.width;
_height = args.height;
_frameRate = args.frameRate;
//TARGET/ENCODER VALUES _bitRate = args.bitRate;
_width = 2*352;
_height = 2*288;
_frameRate = 30;
//
_bitRate = 400;
_flagSSIM = false; _flagSSIM = true;
_lengthSourceFrame = 3*_nativeWidth*_nativeHeight/2; _lengthSourceFrame = 3*_nativeWidth*_nativeHeight/2;
if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL) if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL)
{ {
printf("Cannot read file %s.\n", _inname.c_str()); printf("Cannot read file %s.\n", _inname.c_str());
exit(1); exit(1);
} }
if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL) if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL)
{ {
printf("Cannot write encoded file.\n"); printf("Cannot write encoded file.\n");
exit(1); exit(1);
} }
if ((_decodedFile = fopen(_outname.c_str(), "wb")) == NULL) if ((_decodedFile = fopen(_outname.c_str(), "wb")) == NULL)
{ {
printf("Cannot write file %s.\n", _outname.c_str()); printf("Cannot write file %s.\n", _outname.c_str());
exit(1); exit(1);
} }
_log.open((test::OutputPath() + "TestLog.txt").c_str(), DataLog::CreateLog();
std::fstream::out | std::fstream::app);
return; feature_table_name_ = fv_outfilename_;
DataLog::AddTable(feature_table_name_);
DataLog::AddColumn(feature_table_name_, "motion magnitude", 1);
DataLog::AddColumn(feature_table_name_, "spatial prediction error", 1);
DataLog::AddColumn(feature_table_name_, "spatial pred err horizontal", 1);
DataLog::AddColumn(feature_table_name_, "spatial pred err vertical", 1);
DataLog::AddColumn(feature_table_name_, "width", 1);
DataLog::AddColumn(feature_table_name_, "height", 1);
DataLog::AddColumn(feature_table_name_, "num pixels", 1);
DataLog::AddColumn(feature_table_name_, "frame rate", 1);
DataLog::AddColumn(feature_table_name_, "num frames since drop", 1);
_log.open((test::OutputPath() + "TestLog.txt").c_str(),
std::fstream::out | std::fstream::app);
} }
void void
QualityModesTest::Print() QualityModesTest::Print()
{ {
std::cout << "Quality Modes Test Completed!" << std::endl; std::cout << "Quality Modes Test Completed!" << std::endl;
(_log) << "Quality Modes Test Completed!" << std::endl; (_log) << "Quality Modes Test Completed!" << std::endl;
(_log) << "Input file: " << _inname << std::endl; (_log) << "Input file: " << _inname << std::endl;
(_log) << "Output file: " << _outname << std::endl; (_log) << "Output file: " << _outname << std::endl;
(_log) << "Total run time: " << _testTotalTime << std::endl; (_log) << "Total run time: " << _testTotalTime << std::endl;
printf("Total run time: %f s \n", _testTotalTime); printf("Total run time: %f s \n", _testTotalTime);
double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _nativeFrameRate)); double ActualBitRate = 8.0*( _sumEncBytes / (_frameCnt / _nativeFrameRate));
double actualBitRate = ActualBitRate / 1000.0; double actualBitRate = ActualBitRate / 1000.0;
double avgEncTime = _totalEncodeTime / _frameCnt; double avgEncTime = _totalEncodeTime / _frameCnt;
double avgDecTime = _totalDecodeTime / _frameCnt; double avgDecTime = _totalDecodeTime / _frameCnt;
webrtc::test::QualityMetricsResult psnr,ssim; webrtc::test::QualityMetricsResult psnr,ssim;
I420PSNRFromFiles(_inname.c_str(), _outname.c_str(), _nativeWidth, I420PSNRFromFiles(_inname.c_str(), _outname.c_str(), _nativeWidth,
_nativeHeight, &psnr); _nativeHeight, &psnr);
printf("Actual bitrate: %f kbps\n", actualBitRate); printf("Actual bitrate: %f kbps\n", actualBitRate);
printf("Target bitrate: %f kbps\n", _bitRate); printf("Target bitrate: %f kbps\n", _bitRate);
( _log) << "Actual bitrate: " << actualBitRate<< " kbps\tTarget: " << _bitRate << " kbps" << std::endl; ( _log) << "Actual bitrate: " << actualBitRate<< " kbps\tTarget: " <<
printf("Average encode time: %f s\n", avgEncTime); _bitRate << " kbps" << std::endl;
( _log) << "Average encode time: " << avgEncTime << " s" << std::endl; printf("Average encode time: %f s\n", avgEncTime);
printf("Average decode time: %f s\n", avgDecTime); ( _log) << "Average encode time: " << avgEncTime << " s" << std::endl;
( _log) << "Average decode time: " << avgDecTime << " s" << std::endl; printf("Average decode time: %f s\n", avgDecTime);
printf("PSNR: %f \n", psnr.average); ( _log) << "Average decode time: " << avgDecTime << " s" << std::endl;
printf("**Number of frames dropped in VPM***%d \n",_numFramesDroppedVPM); printf("PSNR: %f \n", psnr.average);
( _log) << "PSNR: " << psnr.average << std::endl; printf("**Number of frames dropped in VPM***%d \n",_numFramesDroppedVPM);
if (_flagSSIM == 1) ( _log) << "PSNR: " << psnr.average << std::endl;
{ if (_flagSSIM == 1)
printf("***computing SSIM***\n"); {
I420SSIMFromFiles(_inname.c_str(), _outname.c_str(), _nativeWidth, printf("***computing SSIM***\n");
_nativeHeight, &ssim); I420SSIMFromFiles(_inname.c_str(), _outname.c_str(), _nativeWidth,
printf("SSIM: %f \n", ssim.average); _nativeHeight, &ssim);
} printf("SSIM: %f \n", ssim.average);
(_log) << std::endl; }
(_log) << std::endl;
printf("\nVCM Qualit Modes Test: \n\n%i tests completed\n", vcmMacrosTests); printf("\nVCM Quality Modes Test: \n\n%i tests completed\n", vcmMacrosTests);
if (vcmMacrosErrors > 0) if (vcmMacrosErrors > 0)
{ {
printf("%i FAILED\n\n", vcmMacrosErrors); printf("%i FAILED\n\n", vcmMacrosErrors);
} }
else else
{ {
printf("ALL PASSED\n\n"); printf("ALL PASSED\n\n");
} }
} }
void void
QualityModesTest::Teardown() QualityModesTest::Teardown()
{ {
_log.close(); _log.close();
fclose(_sourceFile); fclose(_sourceFile);
fclose(_decodedFile); fclose(_decodedFile);
fclose(_encodedFile); fclose(_encodedFile);
return; return;
} }
WebRtc_Word32 WebRtc_Word32
QualityModesTest::Perform() QualityModesTest::Perform(const CmdArgs& args)
{ {
Setup(); Setup(args);
// changing bit/frame rate during the test // changing bit/frame rate during the test
const float bitRateUpdate[] = {1000}; const float bitRateUpdate[] = {1000};
const float frameRateUpdate[] = {30}; const float frameRateUpdate[] = {30};
const int updateFrameNum[] = {10000}; // frame numbers at which an update will occur // frame num at which an update will occur
const int updateFrameNum[] = {10000};
WebRtc_UWord32 numChanges = sizeof(updateFrameNum)/sizeof(*updateFrameNum); WebRtc_UWord32 numChanges = sizeof(updateFrameNum)/sizeof(*updateFrameNum);
WebRtc_UWord8 change = 0;// change counter WebRtc_UWord8 change = 0;// change counter
_vpm = VideoProcessingModule::Create(1); _vpm = VideoProcessingModule::Create(1);
EventWrapper* waitEvent = EventWrapper::Create();
EventWrapper* waitEvent = EventWrapper::Create(); VideoCodec codec;//both send and receive
VideoCodec codec;//both send and receive _vcm->InitializeReceiver();
_vcm->InitializeReceiver(); _vcm->InitializeSender();
_vcm->InitializeSender(); WebRtc_Word32 NumberOfCodecs = _vcm->NumberOfCodecs();
WebRtc_Word32 NumberOfCodecs = _vcm->NumberOfCodecs(); for (int i = 0; i < NumberOfCodecs; i++)
for (int i = 0; i < NumberOfCodecs; i++) {
_vcm->Codec(i, &codec);
if(strncmp(codec.plName,"VP8" , 5) == 0)
{ {
_vcm->Codec(i, &codec); codec.startBitrate = (int)_bitRate;
if(strncmp(codec.plName,"VP8" , 5) == 0) codec.maxFramerate = (WebRtc_UWord8) _frameRate;
{ codec.width = (WebRtc_UWord16)_width;
codec.startBitrate = (int)_bitRate; codec.height = (WebRtc_UWord16)_height;
codec.maxFramerate = (WebRtc_UWord8) _frameRate; codec.codecSpecific.VP8.frameDroppingOn = false;
codec.width = (WebRtc_UWord16)_width;
codec.height = (WebRtc_UWord16)_height; // Will also set and init the desired codec
TEST(_vcm->RegisterSendCodec(&codec, 2, 1440) == VCM_OK);// will also set and init the desired codec TEST(_vcm->RegisterSendCodec(&codec, 2, 1440) == VCM_OK);
i = NumberOfCodecs; i = NumberOfCodecs;
}
} }
}
// register a decoder (same codec for decoder and encoder ) // register a decoder (same codec for decoder and encoder )
TEST(_vcm->RegisterReceiveCodec(&codec, 2) == VCM_OK); TEST(_vcm->RegisterReceiveCodec(&codec, 2) == VCM_OK);
/* Callback Settings */ /* Callback Settings */
VCMQMDecodeCompleCallback _decodeCallback(_decodedFile); VCMQMDecodeCompleCallback _decodeCallback(
_vcm->RegisterReceiveCallback(&_decodeCallback); _decodedFile, _nativeFrameRate, feature_table_name_);
VCMNTEncodeCompleteCallback _encodeCompleteCallback(_encodedFile, *this); _vcm->RegisterReceiveCallback(&_decodeCallback);
_vcm->RegisterTransportCallback(&_encodeCompleteCallback); VCMNTEncodeCompleteCallback _encodeCompleteCallback(_encodedFile, *this);
// encode and decode with the same vcm _vcm->RegisterTransportCallback(&_encodeCompleteCallback);
_encodeCompleteCallback.RegisterReceiverVCM(_vcm); // encode and decode with the same vcm
_encodeCompleteCallback.RegisterReceiverVCM(_vcm);
//quality modes callback //quality modes callback
QMTestVideoSettingsCallback QMCallback; QMTestVideoSettingsCallback QMCallback;
QMCallback.RegisterVCM(_vcm); QMCallback.RegisterVCM(_vcm);
QMCallback.RegisterVPM(_vpm); QMCallback.RegisterVPM(_vpm);
_vcm->RegisterVideoQMCallback(&QMCallback); //_vcm->RegisterVideoQMCallback(&QMCallback);
/////////////////////// ///////////////////////
/// Start Test /// Start Test
/////////////////////// ///////////////////////
_vpm->EnableTemporalDecimation(true); _vpm->EnableTemporalDecimation(true);
_vpm->EnableContentAnalysis(true); _vpm->EnableContentAnalysis(true);
_vpm->SetInputFrameResampleMode(kFastRescaling); _vpm->SetInputFrameResampleMode(kFastRescaling);
// disabling internal VCM frame dropper // disabling internal VCM frame dropper
_vcm->EnableFrameDropper(false); _vcm->EnableFrameDropper(false);
I420VideoFrame sourceFrame; I420VideoFrame sourceFrame;
I420VideoFrame *decimatedFrame = NULL; I420VideoFrame *decimatedFrame = NULL;
WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame]; WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame];
double startTime = clock()/(double)CLOCKS_PER_SEC; double startTime = clock()/(double)CLOCKS_PER_SEC;
_vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 0); _vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 0);
SendStatsTest sendStats; SendStatsTest sendStats;
sendStats.SetTargetFrameRate(static_cast<WebRtc_UWord32>(_frameRate)); sendStats.SetTargetFrameRate(static_cast<WebRtc_UWord32>(_frameRate));
_vcm->RegisterSendStatisticsCallback(&sendStats); _vcm->RegisterSendStatisticsCallback(&sendStats);
VideoContentMetrics* contentMetrics = NULL; VideoContentMetrics* contentMetrics = NULL;
// setting user frame rate // setting user frame rate
_vpm->SetMaxFrameRate((WebRtc_UWord32)(_nativeFrameRate+ 0.5f)); _vpm->SetMaxFrameRate((WebRtc_UWord32)(_nativeFrameRate+ 0.5f));
// for starters: keeping native values: // for starters: keeping native values:
_vpm->SetTargetResolution(_width, _height, (WebRtc_UWord32)(_frameRate+ 0.5f)); _vpm->SetTargetResolution(_width, _height,
_decodeCallback.SetOriginalFrameDimensions(_nativeWidth, _nativeHeight); (WebRtc_UWord32)(_frameRate+ 0.5f));
_decodeCallback.SetOriginalFrameDimensions(_nativeWidth, _nativeHeight);
//tmp - disabling VPM frame dropping //tmp - disabling VPM frame dropping
_vpm->EnableTemporalDecimation(false); _vpm->EnableTemporalDecimation(false);
WebRtc_Word32 ret = 0;
_numFramesDroppedVPM = 0;
do {
if (fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0) {
_frameCnt++;
int size_y = _nativeWidth * _nativeHeight;
int size_uv = ((_nativeWidth + 1) / 2) * ((_nativeHeight + 1) / 2);
sourceFrame.CreateFrame(size_y, tmpBuffer,
size_uv, tmpBuffer + size_y,
size_uv, tmpBuffer + size_y + size_uv,
_nativeWidth, _nativeHeight,
_nativeWidth, (_nativeWidth + 1) / 2,
(_nativeWidth + 1) / 2);
_timeStamp +=
(WebRtc_UWord32)(9e4 / static_cast<float>(codec.maxFramerate));
sourceFrame.set_timestamp(_timeStamp);
ret = _vpm->PreprocessFrame(sourceFrame, &decimatedFrame);
if (ret == 1)
{
printf("VD: frame drop %d \n",_frameCnt);
_numFramesDroppedVPM += 1;
continue; // frame drop
}
else if (ret < 0)
{
printf("Error in PreprocessFrame: %d\n", ret);
//exit(1);
}
// Frame was not re-sampled => use original.
if (decimatedFrame == NULL)
{
decimatedFrame = &sourceFrame;
}
contentMetrics = _vpm->ContentMetrics();
if (contentMetrics == NULL)
{
printf("error: contentMetrics = NULL\n");
}
// counting only encoding time
_encodeTimes[int(sourceFrame.timestamp())] =
clock()/(double)CLOCKS_PER_SEC;
WebRtc_Word32 ret = _vcm->AddVideoFrame(*decimatedFrame, contentMetrics);
_totalEncodeTime += clock()/(double)CLOCKS_PER_SEC -
_encodeTimes[int(sourceFrame.timestamp())];
if (ret < 0)
{
printf("Error in AddFrame: %d\n", ret);
//exit(1);
}
// Same timestamp value for encode and decode
_decodeTimes[int(sourceFrame.timestamp())] =
clock()/(double)CLOCKS_PER_SEC;
ret = _vcm->Decode();
_totalDecodeTime += clock()/(double)CLOCKS_PER_SEC -
_decodeTimes[int(sourceFrame.timestamp())];
if (ret < 0)
{
printf("Error in Decode: %d\n", ret);
//exit(1);
}
if (_vcm->TimeUntilNextProcess() <= 0)
{
_vcm->Process();
}
// mimicking setTargetRates - update every 1 sec
// this will trigger QMSelect
if (_frameCnt%((int)_frameRate) == 0)
{
_vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 1);
}
// check for bit rate update
if (change < numChanges && _frameCnt == updateFrameNum[change])
{
_bitRate = bitRateUpdate[change];
_frameRate = frameRateUpdate[change];
codec.startBitrate = (int)_bitRate;
codec.maxFramerate = (WebRtc_UWord8) _frameRate;
// Will also set and init the desired codec
TEST(_vcm->RegisterSendCodec(&codec, 2, 1440) == VCM_OK);
change++;
}
DataLog::InsertCell(feature_table_name_, "motion magnitude",
contentMetrics->motion_magnitude);
DataLog::InsertCell(feature_table_name_, "spatial prediction error",
contentMetrics->spatial_pred_err);
DataLog::InsertCell(feature_table_name_, "spatial pred err horizontal",
contentMetrics->spatial_pred_err_h);
DataLog::InsertCell(feature_table_name_, "spatial pred err vertical",
contentMetrics->spatial_pred_err_v);
DataLog::InsertCell(feature_table_name_, "width", _nativeHeight);
DataLog::InsertCell(feature_table_name_, "height", _nativeWidth);
DataLog::InsertCell(feature_table_name_, "num pixels",
_nativeHeight * _nativeWidth);
DataLog::InsertCell(feature_table_name_, "frame rate", _nativeFrameRate);
DataLog::NextRow(feature_table_name_);
static_cast<FakeTickTime*>(
_clock)->IncrementDebugClock(1000 / _nativeFrameRate);
}
} while (feof(_sourceFile) == 0);
_decodeCallback.WriteEnd(_frameCnt);
WebRtc_Word32 ret = 0; double endTime = clock()/(double)CLOCKS_PER_SEC;
_numFramesDroppedVPM = 0; _testTotalTime = endTime - startTime;
while (feof(_sourceFile)== 0) _sumEncBytes = _encodeCompleteCallback.EncodedBytes();
{
TEST(fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0);
_frameCnt++;
int size_y = _nativeWidth * _nativeHeight;
int size_uv = ((_nativeWidth + 1) / 2) * ((_nativeHeight + 1) / 2);
sourceFrame.CreateFrame(size_y, tmpBuffer,
size_uv, tmpBuffer + size_y,
size_uv, tmpBuffer + size_y + size_uv,
_nativeWidth, _nativeHeight,
_nativeWidth, (_nativeWidth + 1) / 2,
(_nativeWidth + 1) / 2);
_timeStamp += (WebRtc_UWord32)(9e4 / static_cast<float>(codec.maxFramerate)); delete tmpBuffer;
sourceFrame.set_timestamp(_timeStamp); delete waitEvent;
_vpm->Reset();
ret = _vpm->PreprocessFrame(sourceFrame, &decimatedFrame); Teardown();
if (ret == 1) Print();
{ VideoProcessingModule::Destroy(_vpm);
printf("VD: frame drop %d \n",_frameCnt); return 0;
_numFramesDroppedVPM += 1;
continue; // frame drop
}
else if (ret < 0)
{
printf("Error in PreprocessFrame: %d\n", ret);
//exit(1);
}
contentMetrics = _vpm->ContentMetrics();
if (contentMetrics == NULL)
{
printf("error: contentMetrics = NULL\n");
}
// counting only encoding time
_encodeTimes[int(sourceFrame.timestamp())] =
clock()/(double)CLOCKS_PER_SEC;
WebRtc_Word32 ret = _vcm->AddVideoFrame(*decimatedFrame, contentMetrics);
_totalEncodeTime += clock()/(double)CLOCKS_PER_SEC -
_encodeTimes[int(sourceFrame.timestamp())];
if (ret < 0)
{
printf("Error in AddFrame: %d\n", ret);
//exit(1);
}
_decodeTimes[int(sourceFrame.timestamp())] = clock() /
(double)CLOCKS_PER_SEC - _decodeTimes[int(sourceFrame.timestamp())];
ret = _vcm->Decode();
_totalDecodeTime += clock()/(double)CLOCKS_PER_SEC -
_decodeTimes[int(sourceFrame.timestamp())];
if (ret < 0)
{
printf("Error in Decode: %d\n", ret);
//exit(1);
}
if (_vcm->TimeUntilNextProcess() <= 0)
{
_vcm->Process();
}
// mimicking setTargetRates - update every 1 sec
// this will trigger QMSelect
if (_frameCnt%((int)_frameRate) == 0)
{
_vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 1);
waitEvent->Wait(33);
}
waitEvent->Wait(33);
// check for bit rate update
if (change < numChanges && _frameCnt == updateFrameNum[change])
{
_bitRate = bitRateUpdate[change];
_frameRate = frameRateUpdate[change];
codec.startBitrate = (int)_bitRate;
codec.maxFramerate = (WebRtc_UWord8) _frameRate;
TEST(_vcm->RegisterSendCodec(&codec, 2, 1440) == VCM_OK);
change++;
}
}
double endTime = clock()/(double)CLOCKS_PER_SEC;
_testTotalTime = endTime - startTime;
_sumEncBytes = _encodeCompleteCallback.EncodedBytes();
delete tmpBuffer;
delete waitEvent;
_vpm->Reset();
Teardown();
Print();
VideoProcessingModule::Destroy(_vpm);
return 0;
} }
// implementing callback to be called from
// VCM to update VPM of frame rate and size
QMTestVideoSettingsCallback::QMTestVideoSettingsCallback(): QMTestVideoSettingsCallback::QMTestVideoSettingsCallback():
_vpm(NULL), _vpm(NULL),
_vcm(NULL) _vcm(NULL)
{ {
// //
} }
void void
QMTestVideoSettingsCallback::RegisterVPM(VideoProcessingModule *vpm) QMTestVideoSettingsCallback::RegisterVPM(VideoProcessingModule *vpm)
{ {
_vpm = vpm; _vpm = vpm;
} }
void void
QMTestVideoSettingsCallback::RegisterVCM(VideoCodingModule *vcm) QMTestVideoSettingsCallback::RegisterVCM(VideoCodingModule *vcm)
{ {
_vcm = vcm; _vcm = vcm;
} }
bool bool
QMTestVideoSettingsCallback::Updated() QMTestVideoSettingsCallback::Updated()
{ {
if (_updated) if (_updated)
{ {
_updated = false; _updated = false;
return true; return true;
} }
return false; return false;
} }
WebRtc_Word32 WebRtc_Word32
@@ -367,31 +424,31 @@ QMTestVideoSettingsCallback::SetVideoQMSettings(const WebRtc_UWord32 frameRate,
const WebRtc_UWord32 width, const WebRtc_UWord32 width,
const WebRtc_UWord32 height) const WebRtc_UWord32 height)
{ {
WebRtc_Word32 retVal = 0; WebRtc_Word32 retVal = 0;
printf("QM updates: W = %d, H = %d, FR = %d, \n", width, height, frameRate); printf("QM updates: W = %d, H = %d, FR = %d, \n", width, height, frameRate);
retVal = _vpm->SetTargetResolution(width, height, frameRate); retVal = _vpm->SetTargetResolution(width, height, frameRate);
//Initialize codec with new values - is this the best place to do it? //Initialize codec with new values - is this the best place to do it?
if (!retVal) if (!retVal)
{ {
// first get current settings // first get current settings
VideoCodec currentCodec; VideoCodec currentCodec;
_vcm->SendCodec(&currentCodec); _vcm->SendCodec(&currentCodec);
// now set new values: // now set new values:
currentCodec.height = (WebRtc_UWord16)height; currentCodec.height = (WebRtc_UWord16)height;
currentCodec.width = (WebRtc_UWord16)width; currentCodec.width = (WebRtc_UWord16)width;
currentCodec.maxFramerate = (WebRtc_UWord8)frameRate; currentCodec.maxFramerate = (WebRtc_UWord8)frameRate;
// re-register encoder // re-register encoder
retVal = _vcm->RegisterSendCodec(&currentCodec, 2, 1440); retVal = _vcm->RegisterSendCodec(&currentCodec, 2, 1440);
_updated = true; _updated = true;
} }
return retVal; return retVal;
} }
// Decoded Frame Callback Implementation
// Decoded Frame Callback Implmentation VCMQMDecodeCompleCallback::VCMQMDecodeCompleCallback(
VCMQMDecodeCompleCallback::VCMQMDecodeCompleCallback(FILE* decodedFile): FILE* decodedFile, int frame_rate, std::string feature_table_name):
_decodedFile(decodedFile), _decodedFile(decodedFile),
_decodedBytes(0), _decodedBytes(0),
//_test(test), //_test(test),
@@ -401,7 +458,10 @@ _decWidth(0),
_decHeight(0), _decHeight(0),
//_interpolator(NULL), //_interpolator(NULL),
_decBuffer(NULL), _decBuffer(NULL),
_frameCnt(0) _frameCnt(0),
frame_rate_(frame_rate),
frames_cnt_since_drop_(0),
feature_table_name_(feature_table_name)
{ {
// //
} }
@@ -413,69 +473,106 @@ VCMQMDecodeCompleCallback::~VCMQMDecodeCompleCallback()
// deleteInterpolator(_interpolator); // deleteInterpolator(_interpolator);
// _interpolator = NULL; // _interpolator = NULL;
// } // }
if (_decBuffer != NULL) if (_decBuffer != NULL)
{ {
delete [] _decBuffer; delete [] _decBuffer;
_decBuffer = NULL; _decBuffer = NULL;
} }
} }
WebRtc_Word32 WebRtc_Word32
VCMQMDecodeCompleCallback::FrameToRender(I420VideoFrame& videoFrame) VCMQMDecodeCompleCallback::FrameToRender(I420VideoFrame& videoFrame)
{ {
if ((_origWidth == videoFrame.width()) && ++frames_cnt_since_drop_;
(_origHeight == videoFrame.height()))
{ // When receiving the first coded frame the last_frame variable is not set
if (PrintI420VideoFrame(videoFrame, _decodedFile) < 0) { if (last_frame_.IsZeroSize()) {
return -1; last_frame_.CopyFrame(videoFrame);
} }
_frameCnt++;
// no need for interpolator and decBuffer // Check if there were frames skipped.
if (_decBuffer != NULL) int num_frames_skipped = static_cast<int>( 0.5f +
{ (videoFrame.timestamp() - (last_frame_.timestamp() + (9e4 / frame_rate_))) /
delete [] _decBuffer; (9e4 / frame_rate_));
_decBuffer = NULL;
} // If so...put the last frames into the encoded stream to make up for the
_decWidth = 0; // skipped frame(s)
_decHeight = 0; while (num_frames_skipped > 0) {
} PrintI420VideoFrame(last_frame_, _decodedFile);
else _frameCnt++;
{ --num_frames_skipped;
// TODO(mikhal): Add support for scaling. frames_cnt_since_drop_ = 1; // Reset counter
}
DataLog::InsertCell(
feature_table_name_,"num frames since drop",frames_cnt_since_drop_);
if (_origWidth == videoFrame.width() && _origHeight == videoFrame.height())
{
if (PrintI420VideoFrame(videoFrame, _decodedFile) < 0) {
return -1; return -1;
} }
_frameCnt++;
_decodedBytes += CalcBufferSize(kI420, videoFrame.width(), // no need for interpolator and decBuffer
videoFrame.height());
return VCM_OK;
}
WebRtc_Word32
VCMQMDecodeCompleCallback::DecodedBytes()
{
return _decodedBytes;
}
void
VCMQMDecodeCompleCallback::SetOriginalFrameDimensions(WebRtc_Word32 width,
WebRtc_Word32 height)
{
_origWidth = width;
_origHeight = height;
}
WebRtc_Word32
VCMQMDecodeCompleCallback::buildInterpolator()
{
WebRtc_UWord32 decFrameLength = _origWidth*_origHeight*3 >> 1;
if (_decBuffer != NULL) if (_decBuffer != NULL)
{ {
delete [] _decBuffer; delete [] _decBuffer;
_decBuffer = NULL;
} }
_decBuffer = new WebRtc_UWord8[decFrameLength]; _decWidth = 0;
if (_decBuffer == NULL) _decHeight = 0;
{ }
return -1; else
} {
// TODO(mikhal): Add support for scaling.
return -1;
}
return 0; _decodedBytes += CalcBufferSize(kI420, videoFrame.width(),
videoFrame.height());
videoFrame.SwapFrame(&last_frame_);
return VCM_OK;
}
WebRtc_Word32 VCMQMDecodeCompleCallback::DecodedBytes()
{
return _decodedBytes;
}
void VCMQMDecodeCompleCallback::SetOriginalFrameDimensions(WebRtc_Word32 width,
WebRtc_Word32 height)
{
_origWidth = width;
_origHeight = height;
}
WebRtc_Word32 VCMQMDecodeCompleCallback::buildInterpolator()
{
WebRtc_UWord32 decFrameLength = _origWidth*_origHeight*3 >> 1;
if (_decBuffer != NULL)
{
delete [] _decBuffer;
}
_decBuffer = new WebRtc_UWord8[decFrameLength];
if (_decBuffer == NULL)
{
return -1;
}
return 0;
}
// This function checks if the total number of frames processed in the encoding
// process is the same as the number of frames rendered. If not, the last
// frame (or several consecutive frames from the end) must have been dropped. If
// this is the case, the last frame is repeated so that there are as many
// frames rendered as there are number of frames encoded.
void VCMQMDecodeCompleCallback::WriteEnd(int input_frame_count)
{
int num_missing_frames = input_frame_count - _frameCnt;
for (int n = num_missing_frames; n > 0; --n) {
PrintI420VideoFrame(last_frame_, _decodedFile);
_frameCnt++;
}
} }

View File

@@ -13,9 +13,10 @@
#include "video_processing.h" #include "video_processing.h"
#include "normal_test.h" #include "normal_test.h"
#include "system_wrappers/interface/data_log.h"
#include "video_coding_defines.h" #include "video_coding_defines.h"
int qualityModeTest(); int qualityModeTest(const CmdArgs& args);
class QualityModesTest : public NormalTest class QualityModesTest : public NormalTest
{ {
@@ -23,11 +24,11 @@ public:
QualityModesTest(webrtc::VideoCodingModule* vcm, QualityModesTest(webrtc::VideoCodingModule* vcm,
webrtc::TickTimeBase* clock); webrtc::TickTimeBase* clock);
virtual ~QualityModesTest(); virtual ~QualityModesTest();
WebRtc_Word32 Perform(); WebRtc_Word32 Perform(const CmdArgs& args);
private: private:
void Setup(); void Setup(const CmdArgs& args);
void Print(); void Print();
void Teardown(); void Teardown();
void SsimComp(); void SsimComp();
@@ -43,14 +44,20 @@ private:
WebRtc_UWord32 _numFramesDroppedVPM; WebRtc_UWord32 _numFramesDroppedVPM;
bool _flagSSIM; bool _flagSSIM;
std::string filename_testvideo_;
std::string fv_outfilename_;
std::string feature_table_name_;
}; // end of QualityModesTest class }; // end of QualityModesTest class
class VCMQMDecodeCompleCallback: public webrtc::VCMReceiveCallback class VCMQMDecodeCompleCallback: public webrtc::VCMReceiveCallback
{ {
public: public:
VCMQMDecodeCompleCallback(FILE* decodedFile); VCMQMDecodeCompleCallback(
FILE* decodedFile,
int frame_rate,
std::string feature_table_name);
virtual ~VCMQMDecodeCompleCallback(); virtual ~VCMQMDecodeCompleCallback();
void SetUserReceiveCallback(webrtc::VCMReceiveCallback* receiveCallback); void SetUserReceiveCallback(webrtc::VCMReceiveCallback* receiveCallback);
// will write decoded frame into file // will write decoded frame into file
@@ -58,6 +65,9 @@ public:
WebRtc_Word32 DecodedBytes(); WebRtc_Word32 DecodedBytes();
void SetOriginalFrameDimensions(WebRtc_Word32 width, WebRtc_Word32 height); void SetOriginalFrameDimensions(WebRtc_Word32 width, WebRtc_Word32 height);
WebRtc_Word32 buildInterpolator(); WebRtc_Word32 buildInterpolator();
// Check if last frame is dropped, if so, repeat the last rendered frame.
void WriteEnd(int input_tot_frame_count);
private: private:
FILE* _decodedFile; FILE* _decodedFile;
WebRtc_UWord32 _decodedBytes; WebRtc_UWord32 _decodedBytes;
@@ -69,6 +79,12 @@ private:
// VideoInterpolator* _interpolator; // VideoInterpolator* _interpolator;
WebRtc_UWord8* _decBuffer; WebRtc_UWord8* _decBuffer;
WebRtc_UWord32 _frameCnt; // debug WebRtc_UWord32 _frameCnt; // debug
webrtc::I420VideoFrame last_frame_;
int frame_rate_;
int frames_cnt_since_drop_;
std::string feature_table_name_;
}; // end of VCMQMDecodeCompleCallback class }; // end of VCMQMDecodeCompleCallback class

View File

@@ -41,7 +41,8 @@ class CmdArgs
"/resources/foreman_cif.yuv"), "/resources/foreman_cif.yuv"),
outputFile(webrtc::test::OutputPath() + outputFile(webrtc::test::OutputPath() +
"video_coding_test_output_352x288.yuv"), "video_coding_test_output_352x288.yuv"),
testNum(11) {} fv_outputfile(webrtc::test::OutputPath() + "features.txt"),
testNum(0) {}
std::string codecName; std::string codecName;
webrtc::VideoCodecType codecType; webrtc::VideoCodecType codecType;
int width; int width;
@@ -54,6 +55,7 @@ class CmdArgs
int camaEnable; int camaEnable;
std::string inputFile; std::string inputFile;
std::string outputFile; std::string outputFile;
std::string fv_outputfile;
int testNum; int testNum;
}; };

View File

@@ -37,171 +37,185 @@ int vcmMacrosErrors = 0;
int ParseArguments(int argc, char **argv, CmdArgs& args) int ParseArguments(int argc, char **argv, CmdArgs& args)
{ {
int i = 1; int i = 1;
while (i < argc) while (i < argc)
{ {
if (argv[i][0] != '-') if (argv[i+1] == '\0')
{ {
return -1; printf( "You did not supply a parameter value\n." );
} return -1;
switch (argv[i][1]) }
{
case 'w':
{
int w = atoi(argv[i+1]);
if (w < 1)
return -1;
args.width = w;
break;
}
case 'h':
{
int h = atoi(argv[i+1]);
if (h < 1)
return -1;
args.height = h;
break;
}
case 'b':
{
int b = atoi(argv[i+1]);
if (b < 1)
return -1;
args.bitRate = b;
break;
}
case 'f':
{
int f = atoi(argv[i+1]);
if (f < 1)
return -1;
args.frameRate = f;
break;
}
case 'c':
{
// TODO(holmer): This should be replaced with a map if more codecs
// are added
args.codecName = argv[i+1];
if (strncmp(argv[i+1], "VP8", 3) == 0)
{
args.codecType = kVideoCodecVP8;
}
else if (strncmp(argv[i+1], "I420", 4) == 0)
{
args.codecType = kVideoCodecI420;
}
else
return -1;
break; if (argv[i][0] != '-')
} {
case 'i': return -1;
{ }
args.inputFile = argv[i+1]; switch (argv[i][1])
break; {
} case 'w':
case 'o': {
args.outputFile = argv[i+1]; int w = atoi(argv[i+1]);
break; if (w < 1)
case 'n': return -1;
{ args.width = w;
int n = atoi(argv[i+1]); break;
if (n < 1) }
return -1; case 'h':
args.testNum = n; {
break; int h = atoi(argv[i+1]);
} if (h < 1)
case 'p': return -1;
{ args.height = h;
args.packetLoss = atoi(argv[i+1]); break;
break; }
} case 'b':
case 'r': {
{ int b = atoi(argv[i+1]);
args.rtt = atoi(argv[i+1]); if (b < 1)
break; return -1;
} args.bitRate = b;
case 'm': break;
{ }
args.protectionMode = atoi(argv[i+1]); case 'f':
break; {
} int f = atoi(argv[i+1]);
case 'e': if (f < 1)
{ return -1;
args.camaEnable = atoi(argv[i+1]); args.frameRate = f;
break; break;
} }
default: case 'c':
return -1; {
} // TODO(holmer): This should be replaced with a map if more codecs
i += 2; // are added
args.codecName = argv[i+1];
if (strncmp(argv[i+1], "VP8", 3) == 0)
{
args.codecType = kVideoCodecVP8;
}
else if (strncmp(argv[i+1], "I420", 4) == 0)
{
args.codecType = kVideoCodecI420;
}
else
return -1;
break;
}
case 'i':
{
args.inputFile = argv[i+1];
break;
}
case 'o':
args.outputFile = argv[i+1];
break;
case 'n':
{
int n = atoi(argv[i+1]);
if (n < 1)
return -1;
args.testNum = n;
break;
}
case 'p':
{
args.packetLoss = atoi(argv[i+1]);
break;
}
case 'r':
{
args.rtt = atoi(argv[i+1]);
break;
}
case 'm':
{
args.protectionMode = atoi(argv[i+1]);
break;
}
case 'e':
{
args.camaEnable = atoi(argv[i+1]);
break;
}
case 'v':
{
args.fv_outputfile = argv[i+1];
break;
}
default:
return -1;
}
i += 2;
} }
return 0; return 0;
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
CmdArgs args; CmdArgs args;
if (ParseArguments(argc, argv, args) != 0) if (ParseArguments(argc, argv, args) != 0)
{ {
printf("Unable to parse input arguments\n"); printf("Unable to parse input arguments\n");
printf("args: -n <test #> -w <width> -h <height> -f <fps> -b <bps> " printf("args: -n <test #> -w <width> -h <height> -f <fps> -b <bps> "
"-c <codec> -i <input file> -o <output file> -p <packet loss> " "-c <codec> -i <input file> -o <output file> -p <packet loss> "
"-r <round-trip-time> -e <cama enable> -m <protection mode> \n"); "-r <round-trip-time> -e <cama enable> -m <protection mode> \n");
return -1; return -1;
} }
int ret = 0; int ret = 0;
switch (args.testNum) switch (args.testNum) {
{ case 0:
ret = NormalTest::RunTest(args);
ret |= CodecDataBaseTest::RunTest(args);
ret |= ReceiverTimingTests(args);
ret |= JitterBufferTest(args);
break;
case 1: case 1:
ret = NormalTest::RunTest(args); ret = NormalTest::RunTest(args);
break; break;
case 2: case 2:
ret = MTRxTxTest(args); ret = MTRxTxTest(args);
break; break;
case 3: case 3:
ret = GenericCodecTest::RunTest(args); ret = GenericCodecTest::RunTest(args);
break; break;
case 4: case 4:
ret = CodecDataBaseTest::RunTest(args); ret = CodecDataBaseTest::RunTest(args);
break; break;
case 5: case 5:
// 0- normal, 1-Release test(50 runs) 2- from file // 0- normal, 1-Release test(50 runs) 2- from file
ret = MediaOptTest::RunTest(0, args); ret = MediaOptTest::RunTest(0, args);
break; break;
case 6: case 6:
ret = ReceiverTimingTests(args); ret = ReceiverTimingTests(args);
break; break;
case 7: case 7:
ret = RtpPlay(args); ret = RtpPlay(args);
break; break;
case 8: case 8:
ret = RtpPlayMT(args); ret = RtpPlayMT(args);
break; break;
case 9: case 9:
ret = JitterBufferTest(args); ret = JitterBufferTest(args);
break; break;
case 10: case 10:
ret = DecodeFromStorageTest(args); ret = DecodeFromStorageTest(args);
break; break;
case 11: case 11:
ret = NormalTest::RunTest(args); qualityModeTest(args);
ret |= CodecDataBaseTest::RunTest(args); break;
ret |= ReceiverTimingTests(args);
ret |= JitterBufferTest(args);
break;
default: default:
ret = -1; ret = -1;
break; break;
} }
if (ret != 0) if (ret != 0)
{ {
printf("Test failed!\n"); printf("Test failed!\n");
return -1; return -1;
} }
return 0; return 0;
} }

View File

@@ -23,7 +23,7 @@ using ::webrtc::DataLog;
struct ExpectedValues { struct ExpectedValues {
public: public:
ExpectedValues() ExpectedValues()
: values(NULL), : values(),
multi_value_length(1) { multi_value_length(1) {
} }