/* * 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 #include #include #include 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; rtpInfo.type.Video.isFirstPacket = true; // 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(_width); _sendCodec.height = static_cast(_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(_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(_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(1000.0f/static_cast(_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; }