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