1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 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. 9 */ 10 11 #include "webrtc/modules/video_coding/main/test/normal_test.h" 12 13 #include <assert.h> 14 #include <iostream> 15 #include <sstream> 16 #include <time.h> 17 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" 28 29 using namespace webrtc; 30 31 int NormalTest::RunTest(const CmdArgs& args) 32 { 33 SimulatedClock sim_clock(0); 34 SimulatedClock* clock = &sim_clock; 35 NullEventFactory event_factory; 36 Trace::CreateTrace(); 37 Trace::SetTraceFile( 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); 44 Trace::ReturnTrace(); 45 return 0; 46 } 47 48 //////////////// 49 // Callback Implementation 50 ////////////// 51 52 VCMNTEncodeCompleteCallback::VCMNTEncodeCompleteCallback(FILE* encodedFile, 53 NormalTest& test): 54 _encodedFile(encodedFile), 55 _encodedBytes(0), 56 _skipCnt(0), 57 _VCMReceiver(NULL), 58 _seqNo(0), 59 _test(test) 60 { 61 // 62 } 63 VCMNTEncodeCompleteCallback::~VCMNTEncodeCompleteCallback() 64 { 65 } 66 67 void VCMNTEncodeCompleteCallback::RegisterTransportCallback( 68 VCMPacketizationCallback* transport) 69 { 70 } 71 72 int32_t 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) 82 83 { 84 // will call the VCMReceiver input packet 85 _frameType = frameType; 86 // writing encodedData into file 87 if (fwrite(payloadData, 1, payloadSize, _encodedFile) != payloadSize) { 88 return -1; 89 } 90 WebRtcRTPHeader rtpInfo; 91 rtpInfo.header.markerBit = true; 92 rtpInfo.type.Video.width = 0; 93 rtpInfo.type.Video.height = 0; 94 switch (_test.VideoType()) 95 { 96 case kVideoCodecVP8: 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; 103 break; 104 default: 105 assert(false); 106 return -1; 107 } 108 rtpInfo.header.payloadType = payloadType; 109 rtpInfo.header.sequenceNumber = _seqNo++; 110 rtpInfo.header.ssrc = 0; 111 rtpInfo.header.timestamp = timeStamp; 112 rtpInfo.frameType = frameType; 113 rtpInfo.type.Video.isFirstPacket = true; 114 // Size should also be received from that table, since the payload type 115 // defines the size. 116 117 _encodedBytes += payloadSize; 118 if (payloadSize < 20) 119 { 120 _skipCnt++; 121 } 122 _VCMReceiver->IncomingPacket(payloadData, payloadSize, rtpInfo); 123 return 0; 124 } 125 void 126 VCMNTEncodeCompleteCallback::RegisterReceiverVCM(VideoCodingModule *vcm) 127 { 128 _VCMReceiver = vcm; 129 return; 130 } 131 int32_t 132 VCMNTEncodeCompleteCallback::EncodedBytes() 133 { 134 return _encodedBytes; 135 } 136 137 uint32_t 138 VCMNTEncodeCompleteCallback::SkipCnt() 139 { 140 return _skipCnt; 141 } 142 143 // Decoded Frame Callback Implementation 144 VCMNTDecodeCompleCallback::~VCMNTDecodeCompleCallback() 145 { 146 if (_decodedFile) 147 fclose(_decodedFile); 148 } 149 int32_t 150 VCMNTDecodeCompleCallback::FrameToRender(webrtc::I420VideoFrame& videoFrame) 151 { 152 if (videoFrame.width() != _currentWidth || 153 videoFrame.height() != _currentHeight) 154 { 155 _currentWidth = videoFrame.width(); 156 _currentHeight = videoFrame.height(); 157 if (_decodedFile != NULL) 158 { 159 fclose(_decodedFile); 160 _decodedFile = NULL; 161 } 162 _decodedFile = fopen(_outname.c_str(), "wb"); 163 } 164 if (PrintI420VideoFrame(videoFrame, _decodedFile) < 0) { 165 return -1; 166 } 167 _decodedBytes+= webrtc::CalcBufferSize(webrtc::kI420, 168 videoFrame.width(), videoFrame.height()); 169 return VCM_OK; 170 } 171 172 int32_t 173 VCMNTDecodeCompleCallback::DecodedBytes() 174 { 175 return _decodedBytes; 176 } 177 178 //VCM Normal Test Class implementation 179 180 NormalTest::NormalTest(VideoCodingModule* vcm, Clock* clock) 181 : 182 _clock(clock), 183 _vcm(vcm), 184 _sumEncBytes(0), 185 _timeStamp(0), 186 _totalEncodeTime(0), 187 _totalDecodeTime(0), 188 _decodeCompleteTime(0), 189 _encodeCompleteTime(0), 190 _totalEncodePipeTime(0), 191 _totalDecodePipeTime(0), 192 _frameCnt(0), 193 _encFrameCnt(0), 194 _decFrameCnt(0) 195 { 196 // 197 } 198 199 NormalTest::~NormalTest() 200 { 201 // 202 } 203 void 204 NormalTest::Setup(const CmdArgs& args) 205 { 206 _inname = args.inputFile; 207 _encodedName = test::OutputPath() + "encoded_normaltest.yuv"; 208 _width = args.width; 209 _height = args.height; 210 _frameRate = args.frameRate; 211 _bitRate = args.bitRate; 212 if (args.outputFile == "") 213 { 214 std::ostringstream filename; 215 filename << test::OutputPath() << "NormalTest_" << 216 _width << "x" << _height << "_" << _frameRate << "Hz_P420.yuv"; 217 _outname = filename.str(); 218 } 219 else 220 { 221 _outname = args.outputFile; 222 } 223 _lengthSourceFrame = 3*_width*_height/2; 224 _videoType = args.codecType; 225 226 if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL) 227 { 228 printf("Cannot read file %s.\n", _inname.c_str()); 229 exit(1); 230 } 231 if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL) 232 { 233 printf("Cannot write encoded file.\n"); 234 exit(1); 235 } 236 237 _log.open((test::OutputPath() + "TestLog.txt").c_str(), 238 std::fstream::out | std::fstream::app); 239 } 240 241 int32_t 242 NormalTest::Perform(const CmdArgs& args) 243 { 244 Setup(args); 245 EventWrapper* waitEvent = EventWrapper::Create(); 246 VideoCodec _sendCodec; 247 _vcm->InitializeReceiver(); 248 _vcm->InitializeSender(); 249 TEST(VideoCodingModule::Codec(_videoType, &_sendCodec) == VCM_OK); 250 // should be later on changed via the API 251 _sendCodec.startBitrate = (int)_bitRate; 252 _sendCodec.width = static_cast<uint16_t>(_width); 253 _sendCodec.height = static_cast<uint16_t>(_height); 254 _sendCodec.maxFramerate = _frameRate; 255 // will also set and init the desired codec 256 TEST(_vcm->RegisterSendCodec(&_sendCodec, 4, 1400) == VCM_OK); 257 // register a decoder (same codec for decoder and encoder ) 258 TEST(_vcm->RegisterReceiveCodec(&_sendCodec, 1) == VCM_OK); 259 /* Callback Settings */ 260 VCMNTDecodeCompleCallback _decodeCallback(_outname); 261 _vcm->RegisterReceiveCallback(&_decodeCallback); 262 VCMNTEncodeCompleteCallback _encodeCompleteCallback(_encodedFile, *this); 263 _vcm->RegisterTransportCallback(&_encodeCompleteCallback); 264 // encode and decode with the same vcm 265 _encodeCompleteCallback.RegisterReceiverVCM(_vcm); 266 /////////////////////// 267 /// Start Test 268 /////////////////////// 269 I420VideoFrame sourceFrame; 270 int size_y = _width * _height; 271 int half_width = (_width + 1) / 2; 272 int half_height = (_height + 1) / 2; 273 int size_uv = half_width * half_height; 274 sourceFrame.CreateEmptyFrame(_width, _height, 275 _width, half_width, half_width); 276 uint8_t* tmpBuffer = new uint8_t[_lengthSourceFrame]; 277 double startTime = clock()/(double)CLOCKS_PER_SEC; 278 _vcm->SetChannelParameters(static_cast<uint32_t>(1000 * _bitRate), 0, 0); 279 280 SendStatsTest sendStats; 281 sendStats.set_framerate(static_cast<uint32_t>(_frameRate)); 282 sendStats.set_bitrate(1000 * _bitRate); 283 _vcm->RegisterSendStatisticsCallback(&sendStats); 284 285 while (feof(_sourceFile) == 0) { 286 TEST(fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0 || 287 feof(_sourceFile)); 288 _frameCnt++; 289 sourceFrame.CreateFrame(size_y, tmpBuffer, 290 size_uv, tmpBuffer + size_y, 291 size_uv, tmpBuffer + size_y + size_uv, 292 _width, _height, 293 _width, half_width, half_width); 294 _timeStamp += 295 (uint32_t)(9e4 / static_cast<float>(_sendCodec.maxFramerate)); 296 sourceFrame.set_timestamp(_timeStamp); 297 _encodeTimes[int(sourceFrame.timestamp())] = 298 clock()/(double)CLOCKS_PER_SEC; 299 int32_t ret = _vcm->AddVideoFrame(sourceFrame); 300 double encodeTime = clock()/(double)CLOCKS_PER_SEC - 301 _encodeTimes[int(sourceFrame.timestamp())]; 302 _totalEncodeTime += encodeTime; 303 if (ret < 0) 304 { 305 printf("Error in AddFrame: %d\n", ret); 306 //exit(1); 307 } 308 _decodeTimes[int(sourceFrame.timestamp())] = 309 clock()/(double)CLOCKS_PER_SEC; 310 ret = _vcm->Decode(); 311 _totalDecodeTime += clock()/(double)CLOCKS_PER_SEC - 312 _decodeTimes[int(sourceFrame.timestamp())]; 313 if (ret < 0) 314 { 315 printf("Error in Decode: %d\n", ret); 316 //exit(1); 317 } 318 if (_vcm->TimeUntilNextProcess() <= 0) 319 { 320 _vcm->Process(); 321 } 322 uint32_t framePeriod = 323 static_cast<uint32_t>( 324 1000.0f / static_cast<float>(_sendCodec.maxFramerate) + 0.5f); 325 static_cast<SimulatedClock*>(_clock)->AdvanceTimeMilliseconds(framePeriod); 326 } 327 double endTime = clock()/(double)CLOCKS_PER_SEC; 328 _testTotalTime = endTime - startTime; 329 _sumEncBytes = _encodeCompleteCallback.EncodedBytes(); 330 331 delete [] tmpBuffer; 332 delete waitEvent; 333 Teardown(); 334 Print(); 335 return 0; 336 } 337 338 void 339 NormalTest::FrameEncoded(uint32_t timeStamp) 340 { 341 _encodeCompleteTime = clock()/(double)CLOCKS_PER_SEC; 342 _encFrameCnt++; 343 _totalEncodePipeTime += _encodeCompleteTime - _encodeTimes[int(timeStamp)]; 344 345 } 346 347 void 348 NormalTest::FrameDecoded(uint32_t timeStamp) 349 { 350 _decodeCompleteTime = clock()/(double)CLOCKS_PER_SEC; 351 _decFrameCnt++; 352 _totalDecodePipeTime += _decodeCompleteTime - _decodeTimes[timeStamp]; 353 } 354 355 void 356 NormalTest::Print() 357 { 358 std::cout << "Normal Test Completed!" << std::endl; 359 (_log) << "Normal Test Completed!" << std::endl; 360 (_log) << "Input file: " << _inname << std::endl; 361 (_log) << "Output file: " << _outname << std::endl; 362 (_log) << "Total run time: " << _testTotalTime << std::endl; 363 printf("Total run time: %f s \n", _testTotalTime); 364 double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _frameRate)); 365 double actualBitRate = ActualBitRate / 1000.0; 366 double avgEncTime = _totalEncodeTime / _frameCnt; 367 double avgDecTime = _totalDecodeTime / _frameCnt; 368 webrtc::test::QualityMetricsResult psnr, ssim; 369 I420PSNRFromFiles(_inname.c_str(), _outname.c_str(), _width, _height, 370 &psnr); 371 I420SSIMFromFiles(_inname.c_str(), _outname.c_str(), _width, _height, 372 &ssim); 373 printf("Actual bitrate: %f kbps\n", actualBitRate); 374 printf("Target bitrate: %f kbps\n", _bitRate); 375 ( _log) << "Actual bitrate: " << actualBitRate << 376 " kbps\tTarget: " << _bitRate << " kbps" << std::endl; 377 printf("Average encode time: %f s\n", avgEncTime); 378 ( _log) << "Average encode time: " << avgEncTime << " s" << std::endl; 379 printf("Average decode time: %f s\n", avgDecTime); 380 ( _log) << "Average decode time: " << avgDecTime << " s" << std::endl; 381 printf("PSNR: %f \n", psnr.average); 382 ( _log) << "PSNR: " << psnr.average << std::endl; 383 printf("SSIM: %f \n", ssim.average); 384 ( _log) << "SSIM: " << ssim.average << std::endl; 385 (_log) << std::endl; 386 387 printf("\nVCM Normal Test: \n\n%i tests completed\n", vcmMacrosTests); 388 if (vcmMacrosErrors > 0) 389 { 390 printf("%i FAILED\n\n", vcmMacrosErrors); 391 } 392 else 393 { 394 printf("ALL PASSED\n\n"); 395 } 396 } 397 void 398 NormalTest::Teardown() 399 { 400 //_log.close(); 401 fclose(_sourceFile); 402 fclose(_encodedFile); 403 return; 404 } 405