• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RunTest(const CmdArgs & args)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 
VCMNTEncodeCompleteCallback(FILE * encodedFile,NormalTest & test)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 }
~VCMNTEncodeCompleteCallback()63 VCMNTEncodeCompleteCallback::~VCMNTEncodeCompleteCallback()
64 {
65 }
66 
RegisterTransportCallback(VCMPacketizationCallback * transport)67 void VCMNTEncodeCompleteCallback::RegisterTransportCallback(
68     VCMPacketizationCallback* transport)
69 {
70 }
71 
72 int32_t
SendData(const FrameType frameType,const uint8_t payloadType,const uint32_t timeStamp,int64_t capture_time_ms,const uint8_t * payloadData,const uint32_t payloadSize,const RTPFragmentationHeader &,const webrtc::RTPVideoHeader * videoHdr)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
RegisterReceiverVCM(VideoCodingModule * vcm)126 VCMNTEncodeCompleteCallback::RegisterReceiverVCM(VideoCodingModule *vcm)
127 {
128   _VCMReceiver = vcm;
129   return;
130 }
131  int32_t
EncodedBytes()132 VCMNTEncodeCompleteCallback::EncodedBytes()
133 {
134   return _encodedBytes;
135 }
136 
137 uint32_t
SkipCnt()138 VCMNTEncodeCompleteCallback::SkipCnt()
139 {
140   return _skipCnt;
141 }
142 
143 // Decoded Frame Callback Implementation
~VCMNTDecodeCompleCallback()144 VCMNTDecodeCompleCallback::~VCMNTDecodeCompleCallback()
145 {
146   if (_decodedFile)
147   fclose(_decodedFile);
148 }
149  int32_t
FrameToRender(webrtc::I420VideoFrame & videoFrame)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
DecodedBytes()173 VCMNTDecodeCompleCallback::DecodedBytes()
174 {
175   return _decodedBytes;
176 }
177 
178  //VCM Normal Test Class implementation
179 
NormalTest(VideoCodingModule * vcm,Clock * clock)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 
~NormalTest()199 NormalTest::~NormalTest()
200 {
201     //
202 }
203 void
Setup(const CmdArgs & args)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
Perform(const CmdArgs & args)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
FrameEncoded(uint32_t timeStamp)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
FrameDecoded(uint32_t timeStamp)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
Print()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
Teardown()398 NormalTest::Teardown()
399 {
400   //_log.close();
401   fclose(_sourceFile);
402   fclose(_encodedFile);
403   return;
404 }
405