2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
11 #include "webrtc/modules/video_coding/main/test/normal_test.h"
18 #include "webrtc/common_types.h"
19 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
20 #include "webrtc/modules/video_coding/main/interface/video_coding.h"
21 #include "webrtc/modules/video_coding/main/test/test_callbacks.h"
22 #include "webrtc/modules/video_coding/main/test/test_macros.h"
23 #include "webrtc/modules/video_coding/main/test/test_util.h"
24 #include "webrtc/system_wrappers/interface/clock.h"
25 #include "webrtc/system_wrappers/interface/trace.h"
26 #include "webrtc/test/testsupport/fileutils.h"
27 #include "webrtc/test/testsupport/metrics/video_metrics.h"
29 using namespace webrtc;
31 int NormalTest::RunTest(const CmdArgs& args)
33 SimulatedClock sim_clock(0);
34 SimulatedClock* clock = &sim_clock;
35 NullEventFactory event_factory;
38 (test::OutputPath() + "VCMNormalTestTrace.txt").c_str());
39 Trace::set_level_filter(webrtc::kTraceAll);
40 VideoCodingModule* vcm = VideoCodingModule::Create(clock, &event_factory);
41 NormalTest VCMNTest(vcm, clock);
42 VCMNTest.Perform(args);
43 VideoCodingModule::Destroy(vcm);
49 // Callback Implementation
52 VCMNTEncodeCompleteCallback::VCMNTEncodeCompleteCallback(FILE* encodedFile,
54 _encodedFile(encodedFile),
63 VCMNTEncodeCompleteCallback::~VCMNTEncodeCompleteCallback()
67 void VCMNTEncodeCompleteCallback::RegisterTransportCallback(
68 VCMPacketizationCallback* transport)
73 VCMNTEncodeCompleteCallback::SendData(
74 const FrameType frameType,
75 const uint8_t payloadType,
76 const uint32_t timeStamp,
77 int64_t capture_time_ms,
78 const uint8_t* payloadData,
79 const uint32_t payloadSize,
80 const RTPFragmentationHeader& /*fragmentationHeader*/,
81 const webrtc::RTPVideoHeader* videoHdr)
84 // will call the VCMReceiver input packet
85 _frameType = frameType;
86 // writing encodedData into file
87 if (fwrite(payloadData, 1, payloadSize, _encodedFile) != payloadSize) {
90 WebRtcRTPHeader rtpInfo;
91 rtpInfo.header.markerBit = true;
92 rtpInfo.type.Video.width = 0;
93 rtpInfo.type.Video.height = 0;
94 switch (_test.VideoType())
97 rtpInfo.type.Video.codec = kRtpVideoVp8;
98 rtpInfo.type.Video.codecHeader.VP8.InitRTPVideoHeaderVP8();
99 rtpInfo.type.Video.codecHeader.VP8.nonReference =
100 videoHdr->codecHeader.VP8.nonReference;
101 rtpInfo.type.Video.codecHeader.VP8.pictureId =
102 videoHdr->codecHeader.VP8.pictureId;
105 // Leave for now, until we add kRtpVideoVp9 to RTP.
111 rtpInfo.header.payloadType = payloadType;
112 rtpInfo.header.sequenceNumber = _seqNo++;
113 rtpInfo.header.ssrc = 0;
114 rtpInfo.header.timestamp = timeStamp;
115 rtpInfo.frameType = frameType;
116 rtpInfo.type.Video.isFirstPacket = true;
117 // Size should also be received from that table, since the payload type
120 _encodedBytes += payloadSize;
121 if (payloadSize < 20)
125 _VCMReceiver->IncomingPacket(payloadData, payloadSize, rtpInfo);
129 VCMNTEncodeCompleteCallback::RegisterReceiverVCM(VideoCodingModule *vcm)
135 VCMNTEncodeCompleteCallback::EncodedBytes()
137 return _encodedBytes;
141 VCMNTEncodeCompleteCallback::SkipCnt()
146 // Decoded Frame Callback Implementation
147 VCMNTDecodeCompleCallback::~VCMNTDecodeCompleCallback()
150 fclose(_decodedFile);
153 VCMNTDecodeCompleCallback::FrameToRender(webrtc::I420VideoFrame& videoFrame)
155 if (videoFrame.width() != _currentWidth ||
156 videoFrame.height() != _currentHeight)
158 _currentWidth = videoFrame.width();
159 _currentHeight = videoFrame.height();
160 if (_decodedFile != NULL)
162 fclose(_decodedFile);
165 _decodedFile = fopen(_outname.c_str(), "wb");
167 if (PrintI420VideoFrame(videoFrame, _decodedFile) < 0) {
170 _decodedBytes+= webrtc::CalcBufferSize(webrtc::kI420,
171 videoFrame.width(), videoFrame.height());
176 VCMNTDecodeCompleCallback::DecodedBytes()
178 return _decodedBytes;
181 //VCM Normal Test Class implementation
183 NormalTest::NormalTest(VideoCodingModule* vcm, Clock* clock)
191 _decodeCompleteTime(0),
192 _encodeCompleteTime(0),
193 _totalEncodePipeTime(0),
194 _totalDecodePipeTime(0),
202 NormalTest::~NormalTest()
207 NormalTest::Setup(const CmdArgs& args)
209 _inname = args.inputFile;
210 _encodedName = test::OutputPath() + "encoded_normaltest.yuv";
212 _height = args.height;
213 _frameRate = args.frameRate;
214 _bitRate = args.bitRate;
215 if (args.outputFile == "")
217 std::ostringstream filename;
218 filename << test::OutputPath() << "NormalTest_" <<
219 _width << "x" << _height << "_" << _frameRate << "Hz_P420.yuv";
220 _outname = filename.str();
224 _outname = args.outputFile;
226 _lengthSourceFrame = 3*_width*_height/2;
227 _videoType = args.codecType;
229 if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL)
231 printf("Cannot read file %s.\n", _inname.c_str());
234 if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL)
236 printf("Cannot write encoded file.\n");
240 _log.open((test::OutputPath() + "TestLog.txt").c_str(),
241 std::fstream::out | std::fstream::app);
245 NormalTest::Perform(const CmdArgs& args)
248 EventWrapper* waitEvent = EventWrapper::Create();
249 VideoCodec _sendCodec;
250 _vcm->InitializeReceiver();
251 _vcm->InitializeSender();
252 TEST(VideoCodingModule::Codec(_videoType, &_sendCodec) == VCM_OK);
253 // should be later on changed via the API
254 _sendCodec.startBitrate = (int)_bitRate;
255 _sendCodec.width = static_cast<uint16_t>(_width);
256 _sendCodec.height = static_cast<uint16_t>(_height);
257 _sendCodec.maxFramerate = _frameRate;
258 // will also set and init the desired codec
259 TEST(_vcm->RegisterSendCodec(&_sendCodec, 4, 1400) == VCM_OK);
260 // register a decoder (same codec for decoder and encoder )
261 TEST(_vcm->RegisterReceiveCodec(&_sendCodec, 1) == VCM_OK);
262 /* Callback Settings */
263 VCMNTDecodeCompleCallback _decodeCallback(_outname);
264 _vcm->RegisterReceiveCallback(&_decodeCallback);
265 VCMNTEncodeCompleteCallback _encodeCompleteCallback(_encodedFile, *this);
266 _vcm->RegisterTransportCallback(&_encodeCompleteCallback);
267 // encode and decode with the same vcm
268 _encodeCompleteCallback.RegisterReceiverVCM(_vcm);
269 ///////////////////////
271 ///////////////////////
272 I420VideoFrame sourceFrame;
273 int size_y = _width * _height;
274 int half_width = (_width + 1) / 2;
275 int half_height = (_height + 1) / 2;
276 int size_uv = half_width * half_height;
277 sourceFrame.CreateEmptyFrame(_width, _height,
278 _width, half_width, half_width);
279 uint8_t* tmpBuffer = new uint8_t[_lengthSourceFrame];
280 double startTime = clock()/(double)CLOCKS_PER_SEC;
281 _vcm->SetChannelParameters(static_cast<uint32_t>(1000 * _bitRate), 0, 0);
283 SendStatsTest sendStats;
284 sendStats.set_framerate(static_cast<uint32_t>(_frameRate));
285 sendStats.set_bitrate(1000 * _bitRate);
286 _vcm->RegisterSendStatisticsCallback(&sendStats);
288 while (feof(_sourceFile) == 0) {
289 TEST(fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0 ||
292 sourceFrame.CreateFrame(size_y, tmpBuffer,
293 size_uv, tmpBuffer + size_y,
294 size_uv, tmpBuffer + size_y + size_uv,
296 _width, half_width, half_width);
298 (uint32_t)(9e4 / static_cast<float>(_sendCodec.maxFramerate));
299 sourceFrame.set_timestamp(_timeStamp);
300 _encodeTimes[int(sourceFrame.timestamp())] =
301 clock()/(double)CLOCKS_PER_SEC;
302 int32_t ret = _vcm->AddVideoFrame(sourceFrame);
303 double encodeTime = clock()/(double)CLOCKS_PER_SEC -
304 _encodeTimes[int(sourceFrame.timestamp())];
305 _totalEncodeTime += encodeTime;
308 printf("Error in AddFrame: %d\n", ret);
311 _decodeTimes[int(sourceFrame.timestamp())] =
312 clock()/(double)CLOCKS_PER_SEC;
313 ret = _vcm->Decode();
314 _totalDecodeTime += clock()/(double)CLOCKS_PER_SEC -
315 _decodeTimes[int(sourceFrame.timestamp())];
318 printf("Error in Decode: %d\n", ret);
321 if (_vcm->TimeUntilNextProcess() <= 0)
325 uint32_t framePeriod =
326 static_cast<uint32_t>(
327 1000.0f / static_cast<float>(_sendCodec.maxFramerate) + 0.5f);
328 static_cast<SimulatedClock*>(_clock)->AdvanceTimeMilliseconds(framePeriod);
330 double endTime = clock()/(double)CLOCKS_PER_SEC;
331 _testTotalTime = endTime - startTime;
332 _sumEncBytes = _encodeCompleteCallback.EncodedBytes();
342 NormalTest::FrameEncoded(uint32_t timeStamp)
344 _encodeCompleteTime = clock()/(double)CLOCKS_PER_SEC;
346 _totalEncodePipeTime += _encodeCompleteTime - _encodeTimes[int(timeStamp)];
351 NormalTest::FrameDecoded(uint32_t timeStamp)
353 _decodeCompleteTime = clock()/(double)CLOCKS_PER_SEC;
355 _totalDecodePipeTime += _decodeCompleteTime - _decodeTimes[timeStamp];
361 std::cout << "Normal Test Completed!" << std::endl;
362 (_log) << "Normal Test Completed!" << std::endl;
363 (_log) << "Input file: " << _inname << std::endl;
364 (_log) << "Output file: " << _outname << std::endl;
365 (_log) << "Total run time: " << _testTotalTime << std::endl;
366 printf("Total run time: %f s \n", _testTotalTime);
367 double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _frameRate));
368 double actualBitRate = ActualBitRate / 1000.0;
369 double avgEncTime = _totalEncodeTime / _frameCnt;
370 double avgDecTime = _totalDecodeTime / _frameCnt;
371 webrtc::test::QualityMetricsResult psnr, ssim;
372 I420PSNRFromFiles(_inname.c_str(), _outname.c_str(), _width, _height,
374 I420SSIMFromFiles(_inname.c_str(), _outname.c_str(), _width, _height,
376 printf("Actual bitrate: %f kbps\n", actualBitRate);
377 printf("Target bitrate: %f kbps\n", _bitRate);
378 ( _log) << "Actual bitrate: " << actualBitRate <<
379 " kbps\tTarget: " << _bitRate << " kbps" << std::endl;
380 printf("Average encode time: %f s\n", avgEncTime);
381 ( _log) << "Average encode time: " << avgEncTime << " s" << std::endl;
382 printf("Average decode time: %f s\n", avgDecTime);
383 ( _log) << "Average decode time: " << avgDecTime << " s" << std::endl;
384 printf("PSNR: %f \n", psnr.average);
385 ( _log) << "PSNR: " << psnr.average << std::endl;
386 printf("SSIM: %f \n", ssim.average);
387 ( _log) << "SSIM: " << ssim.average << std::endl;
390 printf("\nVCM Normal Test: \n\n%i tests completed\n", vcmMacrosTests);
391 if (vcmMacrosErrors > 0)
393 printf("%i FAILED\n\n", vcmMacrosErrors);
397 printf("ALL PASSED\n\n");
401 NormalTest::Teardown()
405 fclose(_encodedFile);