Home | History | Annotate | Download | only in test
      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