| /* |
| * 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 <assert.h> |
| #include <iostream> |
| #include <sstream> |
| #include <time.h> |
| |
| #include "../source/event.h" |
| #include "common_types.h" |
| #include "modules/video_coding/main/source/mock/fake_tick_time.h" |
| #include "test_callbacks.h" |
| #include "test_macros.h" |
| #include "test_util.h" |
| #include "trace.h" |
| #include "testsupport/metrics/video_metrics.h" |
| |
| using namespace webrtc; |
| |
| int NormalTest::RunTest(CmdArgs& args) |
| { |
| #if defined(EVENT_DEBUG) |
| printf("SIMULATION TIME\n"); |
| FakeTickTime clock(0); |
| #else |
| printf("REAL-TIME\n"); |
| TickTimeBase clock; |
| #endif |
| Trace::CreateTrace(); |
| Trace::SetTraceFile( |
| (test::OutputPath() + "VCMNormalTestTrace.txt").c_str()); |
| Trace::SetLevelFilter(webrtc::kTraceAll); |
| VideoCodingModule* vcm = VideoCodingModule::Create(1, &clock); |
| NormalTest VCMNTest(vcm, &clock); |
| VCMNTest.Perform(args); |
| VideoCodingModule::Destroy(vcm); |
| Trace::ReturnTrace(); |
| return 0; |
| } |
| |
| //////////////// |
| // Callback Implementation |
| ////////////// |
| |
| VCMNTEncodeCompleteCallback::VCMNTEncodeCompleteCallback(FILE* encodedFile, |
| NormalTest& test): |
| _encodedFile(encodedFile), |
| _encodedBytes(0), |
| _skipCnt(0), |
| _VCMReceiver(NULL), |
| _seqNo(0), |
| _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*/, |
| const webrtc::RTPVideoHeader* videoHdr) |
| |
| { |
| // 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 kVideoCodecVP8: |
| rtpInfo.type.Video.codec = kRTPVideoVP8; |
| rtpInfo.type.Video.codecHeader.VP8.InitRTPVideoHeaderVP8(); |
| rtpInfo.type.Video.codecHeader.VP8.nonReference = |
| videoHdr->codecHeader.VP8.nonReference; |
| rtpInfo.type.Video.codecHeader.VP8.pictureId = |
| videoHdr->codecHeader.VP8.pictureId; |
| break; |
| case kVideoCodecI420: |
| rtpInfo.type.Video.codec = kRTPVideoI420; |
| break; |
| default: |
| assert(false); |
| return -1; |
| } |
| 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 Implementation |
| VCMNTDecodeCompleCallback::~VCMNTDecodeCompleCallback() |
| { |
| if (_decodedFile) |
| fclose(_decodedFile); |
| } |
| 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, TickTimeBase* clock) |
| : |
| _clock(clock), |
| _vcm(vcm), |
| _sumEncBytes(0), |
| _timeStamp(0), |
| _totalEncodeTime(0), |
| _totalDecodeTime(0), |
| _decodeCompleteTime(0), |
| _encodeCompleteTime(0), |
| _totalEncodePipeTime(0), |
| _totalDecodePipeTime(0), |
| _frameCnt(0), |
| _encFrameCnt(0), |
| _decFrameCnt(0) |
| { |
| // |
| } |
| |
| NormalTest::~NormalTest() |
| { |
| // |
| } |
| void |
| NormalTest::Setup(CmdArgs& args) |
| { |
| _inname = args.inputFile; |
| _encodedName = test::OutputPath() + "encoded_normaltest.yuv"; |
| _width = args.width; |
| _height = args.height; |
| _frameRate = args.frameRate; |
| _bitRate = args.bitRate; |
| if (args.outputFile == "") |
| { |
| std::ostringstream filename; |
| filename << test::OutputPath() << "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((test::OutputPath() + "TestLog.txt").c_str(), |
| 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) |
| { |
| #if !defined(EVENT_DEBUG) |
| WebRtc_Word64 processStartTime = _clock->MillisecondTimestamp(); |
| #endif |
| TEST(fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0 || |
| feof(_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(EVENT_DEBUG) |
| static_cast<FakeTickTime*>(_clock)->IncrementDebugClock(framePeriod); |
| #else |
| WebRtc_Word64 timeSpent = _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 |
| 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; |
| webrtc::test::QualityMetricsResult psnr, ssim; |
| I420PSNRFromFiles(_inname.c_str(), _outname.c_str(), _width, _height, |
| &psnr); |
| I420SSIMFromFiles(_inname.c_str(), _outname.c_str(), _width, _height, |
| &ssim); |
| 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.average); |
| ( _log) << "PSNR: " << psnr.average << std::endl; |
| printf("SSIM: %f \n", ssim.average); |
| ( _log) << "SSIM: " << ssim.average << std::endl; |
| (_log) << std::endl; |
| |
| printf("\nVCM Normal Test: \n\n%i tests completed\n", vcmMacrosTests); |
| if (vcmMacrosErrors > 0) |
| { |
| printf("%i FAILED\n\n", vcmMacrosErrors); |
| } |
| else |
| { |
| printf("ALL PASSED\n\n"); |
| } |
| } |
| void |
| NormalTest::Teardown() |
| { |
| //_log.close(); |
| fclose(_sourceFile); |
| fclose(_encodedFile); |
| return; |
| } |
| |
| |
| |
| |