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/generic_codec_test.h" 12 13#include <math.h> 14#include <stdio.h> 15 16#include "webrtc/common_video/interface/i420_video_frame.h" 17#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" 18#include "webrtc/modules/video_coding/main/interface/video_coding.h" 19#include "webrtc/modules/video_coding/main/test/test_macros.h" 20#include "webrtc/system_wrappers/interface/clock.h" 21#include "webrtc/test/testsupport/fileutils.h" 22 23using namespace webrtc; 24 25enum { kMaxWaitEncTimeMs = 100 }; 26 27int GenericCodecTest::RunTest(CmdArgs& args) 28{ 29 SimulatedClock clock(0); 30 NullEventFactory event_factory; 31 VideoCodingModule* vcm = VideoCodingModule::Create(&clock, &event_factory); 32 GenericCodecTest* get = new GenericCodecTest(vcm, &clock); 33 Trace::CreateTrace(); 34 Trace::SetTraceFile( 35 (test::OutputPath() + "genericCodecTestTrace.txt").c_str()); 36 Trace::set_level_filter(webrtc::kTraceAll); 37 get->Perform(args); 38 Trace::ReturnTrace(); 39 delete get; 40 VideoCodingModule::Destroy(vcm); 41 return 0; 42} 43 44GenericCodecTest::GenericCodecTest(VideoCodingModule* vcm, 45 SimulatedClock* clock): 46_clock(clock), 47_vcm(vcm), 48_width(0), 49_height(0), 50_frameRate(0), 51_lengthSourceFrame(0), 52_timeStamp(0) 53{ 54} 55 56GenericCodecTest::~GenericCodecTest() 57{ 58} 59 60void 61GenericCodecTest::Setup(CmdArgs& args) 62{ 63 _timeStamp = 0; 64 65 /* Test Sequence parameters */ 66 67 _inname= args.inputFile; 68 if (args.outputFile.compare("")) 69 _outname = test::OutputPath() + "GCTest_decoded.yuv"; 70 else 71 _outname = args.outputFile; 72 _encodedName = test::OutputPath() + "GCTest_encoded.vp8"; 73 _width = args.width; 74 _height = args.height; 75 _frameRate = args.frameRate; 76 _lengthSourceFrame = 3*_width*_height/2; 77 78 /* File settings */ 79 80 if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL) 81 { 82 printf("Cannot read file %s.\n", _inname.c_str()); 83 exit(1); 84 } 85 if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL) 86 { 87 printf("Cannot write encoded file.\n"); 88 exit(1); 89 } 90 if ((_decodedFile = fopen(_outname.c_str(), "wb")) == NULL) 91 { 92 printf("Cannot write file %s.\n", _outname.c_str()); 93 exit(1); 94 } 95 96 return; 97} 98int32_t 99GenericCodecTest::Perform(CmdArgs& args) 100{ 101 int32_t ret; 102 Setup(args); 103 /* 104 1. sanity checks 105 2. encode/decoder individuality 106 3. API testing 107 4. Target bitrate (within a specific timespan) 108 5. Pipeline Delay 109 */ 110 111 /*******************************/ 112 /* sanity checks on inputs */ 113 /*****************************/ 114 VideoCodec sendCodec, receiveCodec; 115 sendCodec.maxBitrate = 8000; 116 TEST(_vcm->NumberOfCodecs() > 0); // This works since we now initialize the list in the constructor 117 TEST(_vcm->Codec(0, &sendCodec) == VCM_OK); 118 _vcm->InitializeSender(); 119 _vcm->InitializeReceiver(); 120 int32_t NumberOfCodecs = _vcm->NumberOfCodecs(); 121 // registration of first codec in the list 122 int i = 0; 123 _vcm->Codec(0, &_sendCodec); 124 TEST(_vcm->RegisterSendCodec(&_sendCodec, 4, 1440) == VCM_OK); 125 // sanity on encoder registration 126 I420VideoFrame sourceFrame; 127 _vcm->InitializeSender(); 128 TEST(_vcm->Codec(kVideoCodecVP8, &sendCodec) == 0); 129 sendCodec.maxBitrate = 8000; 130 _vcm->RegisterSendCodec(&sendCodec, 1, 1440); 131 _vcm->InitializeSender(); 132 _vcm->Codec(kVideoCodecVP8, &sendCodec); 133 sendCodec.height = 0; 134 TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) < 0); // bad height 135 _vcm->Codec(kVideoCodecVP8, &sendCodec); 136 _vcm->Codec(kVideoCodecVP8, &sendCodec); 137 _vcm->InitializeSender(); 138 // Setting rate when encoder uninitialized. 139 TEST(_vcm->SetChannelParameters(100000, 0, 0) < 0); 140 // register all availbale decoders -- need to have more for this test 141 for (i=0; i< NumberOfCodecs; i++) 142 { 143 _vcm->Codec(i, &receiveCodec); 144 _vcm->RegisterReceiveCodec(&receiveCodec, 1); 145 } 146 uint8_t* tmpBuffer = new uint8_t[_lengthSourceFrame]; 147 TEST(fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0); 148 int half_width = (_width + 1) / 2; 149 int half_height = (_height + 1) / 2; 150 int size_y = _width * _height; 151 int size_uv = half_width * half_height; 152 sourceFrame.CreateFrame(size_y, tmpBuffer, 153 size_uv, tmpBuffer + size_y, 154 size_uv, tmpBuffer + size_y + size_uv, 155 _width, _height, 156 _width, half_width, half_width); 157 sourceFrame.set_timestamp(_timeStamp++); 158 TEST(_vcm->AddVideoFrame(sourceFrame) < 0 ); // encoder uninitialized 159 _vcm->InitializeReceiver(); 160 // Setting rtt when receiver uninitialized. 161 TEST(_vcm->SetChannelParameters(100000, 0, 0) < 0); 162 163 /**************************************/ 164 /* encoder/decoder individuality test */ 165 /**************************************/ 166 //Register both encoder and decoder, reset decoder - encode, set up decoder, reset encoder - decode. 167 rewind(_sourceFile); 168 _vcm->InitializeReceiver(); 169 _vcm->InitializeSender(); 170 NumberOfCodecs = _vcm->NumberOfCodecs(); 171 // Register VP8 172 _vcm->Codec(kVideoCodecVP8, &_sendCodec); 173 _vcm->RegisterSendCodec(&_sendCodec, 4, 1440); 174 _vcm->SendCodec(&sendCodec); 175 sendCodec.startBitrate = 2000; 176 177 // Set target frame rate to half of the incoming frame rate 178 // to test the frame rate control in the VCM 179 sendCodec.maxFramerate = (uint8_t)(_frameRate / 2); 180 sendCodec.width = _width; 181 sendCodec.height = _height; 182 TEST(strncmp(_sendCodec.plName, "VP8", 3) == 0); // was VP8 183 184 _decodeCallback = new VCMDecodeCompleteCallback(_decodedFile); 185 _encodeCompleteCallback = new VCMEncodeCompleteCallback(_encodedFile); 186 _vcm->RegisterReceiveCallback(_decodeCallback); 187 _vcm->RegisterTransportCallback(_encodeCompleteCallback); 188 _encodeCompleteCallback->RegisterReceiverVCM(_vcm); 189 190 _vcm->RegisterSendCodec(&sendCodec, 4, 1440); 191 _encodeCompleteCallback->SetCodecType(ConvertCodecType(sendCodec.plName)); 192 193 _vcm->InitializeReceiver(); 194 _vcm->Process(); 195 196 //encoding 1 second of video 197 for (i = 0; i < _frameRate; i++) 198 { 199 TEST(fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0); 200 sourceFrame.CreateFrame(size_y, tmpBuffer, 201 size_uv, tmpBuffer + size_y, 202 size_uv, tmpBuffer + size_y + size_uv, 203 _width, _height, 204 _width, half_width, half_width); 205 _timeStamp += (uint32_t)(9e4 / static_cast<float>(_frameRate)); 206 sourceFrame.set_timestamp(_timeStamp); 207 TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); 208 IncrementDebugClock(_frameRate); 209 _vcm->Process(); 210 } 211 sendCodec.maxFramerate = (uint8_t)_frameRate; 212 _vcm->InitializeSender(); 213 TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK); // same codec for encode and decode 214 ret = 0; 215 i = 0; 216 while ((i < 25) && (ret == 0) ) 217 { 218 ret = _vcm->Decode(); 219 TEST(ret == VCM_OK); 220 if (ret < 0) 221 { 222 printf("error in frame # %d \n", i); 223 } 224 IncrementDebugClock(_frameRate); 225 i++; 226 } 227 //TEST((ret == 0) && (i = 50)); 228 if (ret == 0) 229 { 230 printf("Encoder/Decoder individuality test complete - View output files \n"); 231 } 232 // last frame - not decoded 233 _vcm->InitializeReceiver(); 234 TEST(_vcm->Decode() < 0); // frame to be encoded exists, decoder uninitialized 235 236 237 // Test key frame request on packet loss mode. 238 // This a frame as a key frame and fooling the receiver 239 // that the last packet was lost. The decoding will succeed, 240 // but the VCM will see a packet loss and request a new key frame. 241 VCMEncComplete_KeyReqTest keyReqTest_EncCompleteCallback(*_vcm); 242 KeyFrameReqTest frameTypeCallback; 243 _vcm->RegisterTransportCallback(&keyReqTest_EncCompleteCallback); 244 _encodeCompleteCallback->RegisterReceiverVCM(_vcm); 245 _vcm->RegisterSendCodec(&sendCodec, 4, 1440); 246 _encodeCompleteCallback->SetCodecType(ConvertCodecType(sendCodec.plName)); 247 TEST(_vcm->SetVideoProtection(kProtectionKeyOnKeyLoss, true) == VCM_OK); 248 TEST(_vcm->RegisterFrameTypeCallback(&frameTypeCallback) == VCM_OK); 249 TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK); 250 TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); 251 _timeStamp += (uint32_t)(9e4 / static_cast<float>(_frameRate)); 252 sourceFrame.set_timestamp(_timeStamp); 253 // First packet of a subsequent frame required before the jitter buffer 254 // will allow decoding an incomplete frame. 255 TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); 256 TEST(_vcm->Decode() == VCM_OK); 257 258 printf("API tests complete \n"); 259 260 /*******************/ 261 /* Bit Rate Tests */ 262 /*****************/ 263 /* Requirements: 264 * 1. OneSecReq = 15 % above/below target over a time period of 1s (_frameRate number of frames) 265 * 3. FullReq = 10% for total seq. (for 300 frames/seq. coincides with #1) 266 * 4. Test will go over all registered codecs 267 //NOTE: time requirements are not part of the release tests 268 */ 269 double FullReq = 0.1; 270 //double OneSecReq = 0.15; 271 printf("\n RATE CONTROL TEST\n"); 272 // initializing.... 273 _vcm->InitializeSender(); 274 _vcm->InitializeReceiver(); 275 rewind(_sourceFile); 276 sourceFrame.CreateEmptyFrame(_width, _height, _width, 277 (_width + 1) / 2, (_width + 1) / 2); 278 const float bitRate[] = {100, 400, 600, 1000, 2000}; 279 const float nBitrates = sizeof(bitRate)/sizeof(*bitRate); 280 float _bitRate = 0; 281 int _frameCnt = 0; 282 float totalBytesOneSec = 0;//, totalBytesTenSec; 283 float totalBytes, actualBitrate; 284 VCMFrameCount frameCount; // testing frame type counters 285 // start test 286 NumberOfCodecs = _vcm->NumberOfCodecs(); 287 // going over all available codecs 288 _encodeCompleteCallback->SetFrameDimensions(_width, _height); 289 SendStatsTest sendStats; 290 for (int k = 0; k < NumberOfCodecs; k++) 291 //for (int k = NumberOfCodecs - 1; k >=0; k--) 292 {// static list starts from 0 293 //just checking 294 _vcm->InitializeSender(); 295 _sendCodec.maxBitrate = 8000; 296 TEST(_vcm->Codec(k, &_sendCodec)== VCM_OK); 297 _vcm->RegisterSendCodec(&_sendCodec, 1, 1440); 298 _vcm->RegisterTransportCallback(_encodeCompleteCallback); 299 _encodeCompleteCallback->SetCodecType(ConvertCodecType(_sendCodec.plName)); 300 printf (" \n\n Codec type = %s \n\n",_sendCodec.plName); 301 for (i = 0; i < nBitrates; i++) 302 { 303 _bitRate = static_cast<float>(bitRate[i]); 304 // just testing 305 _vcm->InitializeSender(); 306 _sendCodec.startBitrate = (int)_bitRate; 307 _sendCodec.maxBitrate = 8000; 308 _sendCodec.maxFramerate = _frameRate; 309 _vcm->RegisterSendCodec(&_sendCodec, 1, 1440); 310 _vcm->RegisterTransportCallback(_encodeCompleteCallback); 311 // up to here 312 _vcm->SetChannelParameters(static_cast<uint32_t>(1000 * _bitRate), 313 0, 20); 314 _frameCnt = 0; 315 totalBytes = 0; 316 _encodeCompleteCallback->Initialize(); 317 sendStats.set_framerate(static_cast<uint32_t>(_frameRate)); 318 sendStats.set_bitrate(1000 * _bitRate); 319 _vcm->RegisterSendStatisticsCallback(&sendStats); 320 while (fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) == 321 _lengthSourceFrame) 322 { 323 _frameCnt++; 324 sourceFrame.CreateFrame(size_y, tmpBuffer, 325 size_uv, tmpBuffer + size_y, 326 size_uv, tmpBuffer + size_y + size_uv, 327 _width, _height, 328 _width, (_width + 1) / 2, 329 (_width + 1) / 2); 330 _timeStamp += (uint32_t)(9e4 / static_cast<float>(_frameRate)); 331 sourceFrame.set_timestamp(_timeStamp); 332 333 ret = _vcm->AddVideoFrame(sourceFrame); 334 IncrementDebugClock(_frameRate); 335 // The following should be uncommneted for timing tests. Release tests only include 336 // compliance with full sequence bit rate. 337 if (_frameCnt == _frameRate)// @ 1sec 338 { 339 totalBytesOneSec = _encodeCompleteCallback->EncodedBytes();//totalBytes; 340 } 341 TEST(_vcm->TimeUntilNextProcess() >= 0); 342 } // video seq. encode done 343 TEST(_vcm->TimeUntilNextProcess() == 0); 344 _vcm->Process(); // Let the module calculate its send bit rate estimate 345 // estimating rates 346 // complete sequence 347 // bit rate assumes input frame rate is as specified 348 totalBytes = _encodeCompleteCallback->EncodedBytes(); 349 actualBitrate = (float)(8.0/1000)*(totalBytes / (_frameCnt / _frameRate)); 350 351 printf("Complete Seq.: target bitrate: %.0f kbps, actual bitrate: %.1f kbps\n", _bitRate, actualBitrate); 352 TEST((fabs(actualBitrate - _bitRate) < FullReq * _bitRate) || 353 (strncmp(_sendCodec.plName, "I420", 4) == 0)); 354 355 // 1 Sec. 356 actualBitrate = (float)(8.0/1000)*(totalBytesOneSec); 357 //actualBitrate = (float)(8.0*totalBytesOneSec)/(oneSecTime - startTime); 358 //printf("First 1Sec: target bitrate: %.0f kbps, actual bitrate: %.1f kbps\n", _bitRate, actualBitrate); 359 //TEST(fabs(actualBitrate - _bitRate) < OneSecReq * _bitRate); 360 rewind(_sourceFile); 361 362 //checking key/delta frame count 363 _vcm->SentFrameCount(frameCount); 364 printf("frame count: %d delta, %d key\n", frameCount.numDeltaFrames, frameCount.numKeyFrames); 365 }// end per codec 366 367 } // end rate control test 368 /********************************/ 369 /* Encoder Pipeline Delay Test */ 370 /******************************/ 371 _vcm->InitializeSender(); 372 NumberOfCodecs = _vcm->NumberOfCodecs(); 373 bool encodeComplete = false; 374 // going over all available codecs 375 for (int k = 0; k < NumberOfCodecs; k++) 376 { 377 _vcm->Codec(k, &_sendCodec); 378 _vcm->InitializeSender(); 379 _sendCodec.maxBitrate = 8000; 380 _vcm->RegisterSendCodec(&_sendCodec, 4, 1440); 381 _vcm->RegisterTransportCallback(_encodeCompleteCallback); 382 383 _frameCnt = 0; 384 encodeComplete = false; 385 while (encodeComplete == false) 386 { 387 TEST(fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0); 388 _frameCnt++; 389 sourceFrame.CreateFrame(size_y, tmpBuffer, 390 size_uv, tmpBuffer + size_y, 391 size_uv, tmpBuffer + size_y + size_uv, 392 _width, _height, 393 _width, half_width, half_width); 394 _timeStamp += (uint32_t)(9e4 / static_cast<float>(_frameRate)); 395 sourceFrame.set_timestamp(_timeStamp); 396 _vcm->AddVideoFrame(sourceFrame); 397 encodeComplete = _encodeCompleteCallback->EncodeComplete(); 398 } // first frame encoded 399 printf ("\n Codec type = %s \n", _sendCodec.plName); 400 printf(" Encoder pipeline delay = %d frames\n", _frameCnt - 1); 401 } // end for all codecs 402 403 /********************************/ 404 /* Encoder Packet Size Test */ 405 /********************************/ 406 RTPSendCallback_SizeTest sendCallback; 407 408 RtpRtcp::Configuration configuration; 409 configuration.id = 1; 410 configuration.audio = false; 411 configuration.outgoing_transport = &sendCallback; 412 413 RtpRtcp& rtpModule = *RtpRtcp::CreateRtpRtcp(configuration); 414 415 VCMRTPEncodeCompleteCallback encCompleteCallback(&rtpModule); 416 _vcm->InitializeSender(); 417 418 // Test temporal decimation settings 419 for (int k = 0; k < NumberOfCodecs; k++) 420 { 421 _vcm->Codec(k, &_sendCodec); 422 if (strncmp(_sendCodec.plName, "I420", 4) == 0) 423 { 424 // Only test with I420 425 break; 426 } 427 } 428 TEST(strncmp(_sendCodec.plName, "I420", 4) == 0); 429 _vcm->InitializeSender(); 430 _sendCodec.maxFramerate = static_cast<uint8_t>(_frameRate / 2.0 + 0.5f); 431 _vcm->RegisterSendCodec(&_sendCodec, 4, 1440); 432 _vcm->SetChannelParameters(2000000, 0, 0); 433 _vcm->RegisterTransportCallback(_encodeCompleteCallback); 434 // up to here 435 _vcm->SetChannelParameters(static_cast<uint32_t>(1000 * _bitRate), 0, 20); 436 _encodeCompleteCallback->Initialize(); 437 sendStats.set_framerate(static_cast<uint32_t>(_frameRate)); 438 sendStats.set_bitrate(1000 * _bitRate); 439 _vcm->RegisterSendStatisticsCallback(&sendStats); 440 rewind(_sourceFile); 441 while (fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) == 442 _lengthSourceFrame) { 443 sourceFrame.CreateFrame(size_y, tmpBuffer, 444 size_uv, tmpBuffer + size_y, 445 size_uv, tmpBuffer + size_y + size_uv, 446 _width, _height, 447 _width, half_width, half_width); 448 _timeStamp += (uint32_t)(9e4 / static_cast<float>(_frameRate)); 449 sourceFrame.set_timestamp(_timeStamp); 450 ret = _vcm->AddVideoFrame(sourceFrame); 451 if (_vcm->TimeUntilNextProcess() <= 0) 452 { 453 _vcm->Process(); 454 } 455 IncrementDebugClock(_frameRate); 456 } // first frame encoded 457 458 delete &rtpModule; 459 Print(); 460 delete tmpBuffer; 461 delete _decodeCallback; 462 delete _encodeCompleteCallback; 463 return 0; 464} 465 466 467void 468GenericCodecTest::Print() 469{ 470 printf(" \n\n VCM Generic Encoder Test: \n\n%i tests completed\n", vcmMacrosTests); 471 if (vcmMacrosErrors > 0) 472 { 473 printf("%i FAILED\n\n", vcmMacrosErrors); 474 } 475 else 476 { 477 printf("ALL PASSED\n\n"); 478 } 479} 480 481float 482GenericCodecTest::WaitForEncodedFrame() const 483{ 484 int64_t startTime = _clock->TimeInMilliseconds(); 485 while (_clock->TimeInMilliseconds() - startTime < kMaxWaitEncTimeMs*10) 486 { 487 if (_encodeCompleteCallback->EncodeComplete()) 488 { 489 return _encodeCompleteCallback->EncodedBytes(); 490 } 491 } 492 return 0; 493} 494 495void 496GenericCodecTest::IncrementDebugClock(float frameRate) 497{ 498 _clock->AdvanceTimeMilliseconds(1000/frameRate); 499} 500 501int 502RTPSendCallback_SizeTest::SendPacket(int channel, const void *data, int len) 503{ 504 _nPackets++; 505 _payloadSizeSum += len; 506 // Make sure no payloads (len - header size) are larger than maxPayloadSize 507 TEST(len > 0 && static_cast<uint32_t>(len - 12) <= _maxPayloadSize); 508 return 0; 509} 510 511void 512RTPSendCallback_SizeTest::SetMaxPayloadSize(uint32_t maxPayloadSize) 513{ 514 _maxPayloadSize = maxPayloadSize; 515} 516 517void 518RTPSendCallback_SizeTest::Reset() 519{ 520 _nPackets = 0; 521 _payloadSizeSum = 0; 522} 523 524float 525RTPSendCallback_SizeTest::AveragePayloadSize() const 526{ 527 if (_nPackets > 0) 528 { 529 return _payloadSizeSum / static_cast<float>(_nPackets); 530 } 531 return 0; 532} 533 534int32_t 535VCMEncComplete_KeyReqTest::SendData( 536 const FrameType frameType, 537 const uint8_t payloadType, 538 const uint32_t timeStamp, 539 int64_t capture_time_ms, 540 const uint8_t* payloadData, 541 const uint32_t payloadSize, 542 const RTPFragmentationHeader& /*fragmentationHeader*/, 543 const webrtc::RTPVideoHeader* /*videoHdr*/) 544{ 545 WebRtcRTPHeader rtpInfo; 546 rtpInfo.header.markerBit = true; // end of frame 547 rtpInfo.type.Video.codecHeader.VP8.InitRTPVideoHeaderVP8(); 548 rtpInfo.type.Video.codec = kRtpVideoVp8; 549 rtpInfo.header.payloadType = payloadType; 550 rtpInfo.header.sequenceNumber = _seqNo; 551 _seqNo += 2; 552 rtpInfo.header.ssrc = 0; 553 rtpInfo.header.timestamp = _timeStamp; 554 _timeStamp += 3000; 555 rtpInfo.type.Video.isFirstPacket = false; 556 rtpInfo.frameType = kVideoFrameKey; 557 return _vcm.IncomingPacket(payloadData, payloadSize, rtpInfo); 558} 559