374 lines
11 KiB
C++
374 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 "normal_test.h"
|
|
#include "../source/event.h"
|
|
#include "tick_time.h"
|
|
#include "common_types.h"
|
|
#include "trace.h"
|
|
#include "test_util.h"
|
|
#include <assert.h>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <time.h>
|
|
|
|
using namespace webrtc;
|
|
|
|
int NormalTest::RunTest(CmdArgs& args)
|
|
{
|
|
// Don't run this test with debug time
|
|
#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG)
|
|
printf("SIMULATION TIME\n");
|
|
#else
|
|
printf("REAL-TIME\n");
|
|
#endif
|
|
Trace::CreateTrace();
|
|
Trace::SetTraceFile("VCMNormalTestTrace.txt");
|
|
Trace::SetLevelFilter(webrtc::kTraceAll);
|
|
VideoCodingModule* vcm = VideoCodingModule::Create(1);
|
|
NormalTest VCMNTest(vcm);
|
|
VCMNTest.Perform(args);
|
|
VideoCodingModule::Destroy(vcm);
|
|
Trace::ReturnTrace();
|
|
return 0;
|
|
}
|
|
|
|
////////////////
|
|
// Callback Implementation
|
|
//////////////
|
|
|
|
VCMNTEncodeCompleteCallback::VCMNTEncodeCompleteCallback(FILE* encodedFile, NormalTest& test):
|
|
_seqNo(0),
|
|
_layerPacketId(1),
|
|
_encodedFile(encodedFile),
|
|
_encodedBytes(0),
|
|
_skipCnt(0),
|
|
_VCMReceiver(NULL),
|
|
_test(test)
|
|
{
|
|
//
|
|
}
|
|
VCMNTEncodeCompleteCallback::~VCMNTEncodeCompleteCallback()
|
|
{
|
|
}
|
|
|
|
void
|
|
VCMNTEncodeCompleteCallback::RegisterTransportCallback(VCMPacketizationCallback* transport)
|
|
{
|
|
}
|
|
|
|
WebRtc_Word32
|
|
VCMNTEncodeCompleteCallback::SendData(const FrameType frameType,
|
|
const WebRtc_UWord8 payloadType,
|
|
const WebRtc_UWord32 timeStamp,
|
|
const WebRtc_UWord8* payloadData,
|
|
const WebRtc_UWord32 payloadSize,
|
|
const RTPFragmentationHeader& fragmentationHeader)
|
|
|
|
{
|
|
// will call the VCMReceiver input packet
|
|
_frameType = frameType;
|
|
// writing encodedData into file
|
|
fwrite(payloadData, 1, payloadSize, _encodedFile);
|
|
WebRtcRTPHeader rtpInfo;
|
|
rtpInfo.header.markerBit = true;
|
|
rtpInfo.type.Video.width = 0;
|
|
rtpInfo.type.Video.height = 0;
|
|
switch (_test.VideoType())
|
|
{
|
|
case kVideoCodecH263:
|
|
rtpInfo.type.Video.codec = kRTPVideoH263;
|
|
rtpInfo.type.Video.codecHeader.H263.bits = false;
|
|
rtpInfo.type.Video.codecHeader.H263.independentlyDecodable = false;
|
|
rtpInfo.type.Video.height = (WebRtc_UWord16)_test.Height();
|
|
rtpInfo.type.Video.width = (WebRtc_UWord16)_test.Width();
|
|
break;
|
|
case kVideoCodecVP8:
|
|
rtpInfo.type.Video.codec = kRTPVideoVP8;
|
|
break;
|
|
case kVideoCodecI420:
|
|
rtpInfo.type.Video.codec = kRTPVideoI420;
|
|
break;
|
|
}
|
|
rtpInfo.header.payloadType = payloadType;
|
|
rtpInfo.header.sequenceNumber = _seqNo++;
|
|
rtpInfo.header.ssrc = 0;
|
|
rtpInfo.header.timestamp = timeStamp;
|
|
rtpInfo.frameType = frameType;
|
|
// Size should also be received from that table, since the payload type
|
|
// defines the size.
|
|
|
|
_encodedBytes += payloadSize;
|
|
if (payloadSize < 20)
|
|
{
|
|
_skipCnt++;
|
|
}
|
|
_VCMReceiver->IncomingPacket(payloadData, payloadSize, rtpInfo);
|
|
return 0;
|
|
}
|
|
void
|
|
VCMNTEncodeCompleteCallback::RegisterReceiverVCM(VideoCodingModule *vcm)
|
|
{
|
|
_VCMReceiver = vcm;
|
|
return;
|
|
}
|
|
WebRtc_Word32
|
|
VCMNTEncodeCompleteCallback::EncodedBytes()
|
|
{
|
|
return _encodedBytes;
|
|
}
|
|
|
|
WebRtc_UWord32
|
|
VCMNTEncodeCompleteCallback::SkipCnt()
|
|
{
|
|
return _skipCnt;
|
|
}
|
|
|
|
// Decoded Frame Callback Implmentation
|
|
VCMNTDecodeCompleCallback::~VCMNTDecodeCompleCallback()
|
|
{
|
|
//
|
|
}
|
|
WebRtc_Word32
|
|
VCMNTDecodeCompleCallback::FrameToRender(webrtc::VideoFrame& videoFrame)
|
|
{
|
|
if (videoFrame.Width() != _currentWidth ||
|
|
videoFrame.Height() != _currentHeight)
|
|
{
|
|
_currentWidth = videoFrame.Width();
|
|
_currentHeight = videoFrame.Height();
|
|
if (_decodedFile != NULL)
|
|
{
|
|
fclose(_decodedFile);
|
|
_decodedFile = NULL;
|
|
}
|
|
_decodedFile = fopen(_outname.c_str(), "wb");
|
|
}
|
|
fwrite(videoFrame.Buffer(), 1, videoFrame.Length(), _decodedFile);
|
|
_decodedBytes+= videoFrame.Length();
|
|
return VCM_OK;
|
|
}
|
|
|
|
WebRtc_Word32
|
|
VCMNTDecodeCompleCallback::DecodedBytes()
|
|
{
|
|
return _decodedBytes;
|
|
}
|
|
|
|
//VCM Normal Test Class implementation
|
|
|
|
NormalTest::NormalTest(VideoCodingModule* vcm)
|
|
:
|
|
_vcm(vcm),
|
|
_totalEncodeTime(0),
|
|
_totalDecodeTime(0),
|
|
_decodeCompleteTime(0),
|
|
_encodeCompleteTime(0),
|
|
_totalEncodePipeTime(0),
|
|
_totalDecodePipeTime(0),
|
|
_frameCnt(0),
|
|
_timeStamp(0),
|
|
_encFrameCnt(0),
|
|
_decFrameCnt(0),
|
|
_sumEncBytes(0)
|
|
|
|
{
|
|
//
|
|
}
|
|
|
|
NormalTest::~NormalTest()
|
|
{
|
|
//
|
|
}
|
|
void
|
|
NormalTest::Setup(CmdArgs& args)
|
|
{
|
|
_inname = args.inputFile;
|
|
_encodedName = "encoded_normaltest.yuv";
|
|
_width = args.width;
|
|
_height = args.height;
|
|
_frameRate = args.frameRate;
|
|
_bitRate = args.bitRate;
|
|
if (args.outputFile == "")
|
|
{
|
|
std::ostringstream filename;
|
|
filename << "../NormalTest_" << _width << "x" << _height << "_" << _frameRate << "Hz_P420.yuv";
|
|
_outname = filename.str();
|
|
}
|
|
else
|
|
{
|
|
_outname = args.outputFile;
|
|
}
|
|
_lengthSourceFrame = 3*_width*_height/2;
|
|
_videoType = args.codecType;
|
|
|
|
if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL)
|
|
{
|
|
printf("Cannot read file %s.\n", _inname.c_str());
|
|
exit(1);
|
|
}
|
|
if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL)
|
|
{
|
|
printf("Cannot write encoded file.\n");
|
|
exit(1);
|
|
}
|
|
|
|
_log.open("../TestLog.txt", std::fstream::out | std::fstream::app);
|
|
return;
|
|
}
|
|
|
|
WebRtc_Word32
|
|
NormalTest::Perform(CmdArgs& args)
|
|
{
|
|
Setup(args);
|
|
EventWrapper* waitEvent = EventWrapper::Create();
|
|
VideoCodec _sendCodec;//, _receiveCodec; // tmp - sendCodecd used as receive codec
|
|
_vcm->InitializeReceiver();
|
|
_vcm->InitializeSender();
|
|
TEST(VideoCodingModule::Codec(_videoType, &_sendCodec) == VCM_OK);
|
|
_sendCodec.startBitrate = (int)_bitRate; // should be later on changed via the API
|
|
_sendCodec.width = static_cast<WebRtc_UWord16>(_width);
|
|
_sendCodec.height = static_cast<WebRtc_UWord16>(_height);
|
|
_sendCodec.maxFramerate = _frameRate;
|
|
TEST(_vcm->RegisterSendCodec(&_sendCodec, 4, 1400) == VCM_OK);// will also set and init the desired codec
|
|
// register a decoder (same codec for decoder and encoder )
|
|
TEST(_vcm->RegisterReceiveCodec(&_sendCodec, 1) == VCM_OK);
|
|
/* Callback Settings */
|
|
VCMNTDecodeCompleCallback _decodeCallback(_outname);
|
|
_vcm->RegisterReceiveCallback(&_decodeCallback);
|
|
VCMNTEncodeCompleteCallback _encodeCompleteCallback(_encodedFile, *this);
|
|
_vcm->RegisterTransportCallback(&_encodeCompleteCallback);
|
|
// encode and decode with the same vcm
|
|
_encodeCompleteCallback.RegisterReceiverVCM(_vcm);
|
|
///////////////////////
|
|
/// Start Test
|
|
///////////////////////
|
|
VideoFrame sourceFrame;
|
|
sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
|
|
WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame];
|
|
double startTime = clock()/(double)CLOCKS_PER_SEC;
|
|
_vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 0);
|
|
|
|
SendStatsTest sendStats;
|
|
sendStats.SetTargetFrameRate(static_cast<WebRtc_UWord32>(_frameRate));
|
|
_vcm->RegisterSendStatisticsCallback(&sendStats);
|
|
|
|
while (feof(_sourceFile)== 0)
|
|
{
|
|
WebRtc_Word64 processStartTime = VCMTickTime::MillisecondTimestamp();
|
|
fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile);
|
|
_frameCnt++;
|
|
sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer);
|
|
sourceFrame.SetHeight(_height);
|
|
sourceFrame.SetWidth(_width);
|
|
_timeStamp += (WebRtc_UWord32)(9e4 / static_cast<float>(_sendCodec.maxFramerate));
|
|
sourceFrame.SetTimeStamp(_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; // same timestamp value for encode and decode
|
|
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(TICK_TIME_DEBUG) || defined(EVENT_DEBUG)
|
|
for (int i=0; i < framePeriod; i++)
|
|
{
|
|
VCMTickTime::IncrementDebugClock();
|
|
}
|
|
#else
|
|
WebRtc_Word64 timeSpent = VCMTickTime::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
|
|
NormalTest::FrameEncoded(WebRtc_UWord32 timeStamp)
|
|
{
|
|
_encodeCompleteTime = clock()/(double)CLOCKS_PER_SEC;
|
|
_encFrameCnt++;
|
|
_totalEncodePipeTime += _encodeCompleteTime - _encodeTimes[int(timeStamp)];
|
|
|
|
}
|
|
|
|
void
|
|
NormalTest::FrameDecoded(WebRtc_UWord32 timeStamp)
|
|
{
|
|
_decodeCompleteTime = clock()/(double)CLOCKS_PER_SEC;
|
|
_decFrameCnt++;
|
|
_totalDecodePipeTime += _decodeCompleteTime - _decodeTimes[timeStamp];
|
|
}
|
|
|
|
void
|
|
NormalTest::Print()
|
|
{
|
|
std::cout << "Normal Test Completed!" << std::endl;
|
|
(_log) << "Normal Test Completed!" << std::endl;
|
|
(_log) << "Input file: " << _inname << std::endl;
|
|
(_log) << "Output file: " << _outname << std::endl;
|
|
(_log) << "Total run time: " << _testTotalTime << std::endl;
|
|
printf("Total run time: %f s \n", _testTotalTime);
|
|
double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _frameRate));
|
|
double actualBitRate = ActualBitRate / 1000.0;
|
|
double avgEncTime = _totalEncodeTime / _frameCnt;
|
|
double avgDecTime = _totalDecodeTime / _frameCnt;
|
|
double psnr;
|
|
PSNRfromFiles(_inname.c_str(), _outname.c_str(), _width, _height, &psnr);
|
|
printf("Actual bitrate: %f kbps\n", actualBitRate);
|
|
printf("Target bitrate: %f kbps\n", _bitRate);
|
|
( _log) << "Actual bitrate: " << actualBitRate<< " kbps\tTarget: " << _bitRate << " kbps" << std::endl;
|
|
printf("Average encode time: %f s\n", avgEncTime);
|
|
( _log) << "Average encode time: " << avgEncTime << " s" << std::endl;
|
|
printf("Average decode time: %f s\n", avgDecTime);
|
|
( _log) << "Average decode time: " << avgDecTime << " s" << std::endl;
|
|
printf("PSNR: %f \n", psnr);
|
|
( _log) << "PSNR: " << psnr << std::endl;
|
|
(_log) << std::endl;
|
|
}
|
|
void
|
|
NormalTest::Teardown()
|
|
{
|
|
//_log.close();
|
|
fclose(_sourceFile);
|
|
fclose(_encodedFile);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|