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 // Implementation of codec data base test
12 // testing is done via the VCM module, no specific CodecDataBase module functionality.
13
14 #include "webrtc/modules/video_coding/main/test/codec_database_test.h"
15
16 #include <assert.h>
17 #include <stdio.h>
18
19 #include "webrtc/engine_configurations.h"
20 #include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h"
21 #include "webrtc/modules/video_coding/main/interface/video_coding.h"
22 #include "webrtc/modules/video_coding/main/test/test_callbacks.h"
23 #include "webrtc/modules/video_coding/main/test/test_macros.h"
24 #include "webrtc/modules/video_coding/main/test/test_util.h"
25 #include "webrtc/test/testsupport/fileutils.h"
26 #include "webrtc/test/testsupport/metrics/video_metrics.h"
27
28 using namespace webrtc;
29
RunTest(CmdArgs & args)30 int CodecDataBaseTest::RunTest(CmdArgs& args)
31 {
32 VideoCodingModule* vcm = VideoCodingModule::Create();
33 CodecDataBaseTest* cdbt = new CodecDataBaseTest(vcm);
34 cdbt->Perform(args);
35 VideoCodingModule::Destroy(vcm);
36 delete cdbt;
37 return 0;
38
39 }
40
CodecDataBaseTest(VideoCodingModule * vcm)41 CodecDataBaseTest::CodecDataBaseTest(VideoCodingModule* vcm):
42 _vcm(vcm),
43 _width(0),
44 _height(0),
45 _lengthSourceFrame(0),
46 _timeStamp(0)
47 {
48 //
49 }
~CodecDataBaseTest()50 CodecDataBaseTest::~CodecDataBaseTest()
51 {
52 //
53 }
54 void
Setup(CmdArgs & args)55 CodecDataBaseTest::Setup(CmdArgs& args)
56 {
57 _inname= args.inputFile;
58 _width = args.width;
59 _height = args.height;
60 _frameRate = args.frameRate;
61 _lengthSourceFrame = 3*_width*_height/2;
62 if (args.outputFile.compare(""))
63 _outname = test::OutputPath() + "CDBtest_decoded.yuv";
64 else
65 _outname = args.outputFile;
66 _outname = args.outputFile;
67 _encodedName = test::OutputPath() + "CDBtest_encoded.vp8";
68
69 if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL)
70 {
71 printf("Cannot read file %s.\n", _inname.c_str());
72 exit(1);
73 }
74
75 if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL)
76 {
77 printf("Cannot write encoded file.\n");
78 exit(1);
79 }
80
81 if ((_decodedFile = fopen(_outname.c_str(), "wb")) == NULL)
82 {
83 printf("Cannot write file %s.\n", _outname.c_str());
84 exit(1);
85 }
86
87 return;
88 }
89
90
91
92 int32_t
Perform(CmdArgs & args)93 CodecDataBaseTest::Perform(CmdArgs& args)
94 {
95 #ifndef VIDEOCODEC_VP8
96 assert(false);
97 #endif
98 Setup(args);
99 EventWrapper* waitEvent = EventWrapper::Create();
100
101 /**************************/
102 /* General Sanity Checks */
103 /************************/
104 VideoCodec sendCodec, receiveCodec;
105 TEST(VideoCodingModule::NumberOfCodecs() > 0);
106 _vcm->InitializeReceiver();
107 _vcm->InitializeSender();
108 VCMDecodeCompleteCallback *_decodeCallback = new VCMDecodeCompleteCallback(_decodedFile);
109 VCMEncodeCompleteCallback *_encodeCompleteCallback = new VCMEncodeCompleteCallback(_encodedFile);
110 _vcm->RegisterReceiveCallback(_decodeCallback);
111 _vcm->RegisterTransportCallback(_encodeCompleteCallback);
112 _encodeCompleteCallback->SetFrameDimensions(_width, _height);
113 // registering the callback - encode and decode with the same vcm (could be later changed)
114 _encodeCompleteCallback->RegisterReceiverVCM(_vcm);
115 // preparing a frame to be encoded
116 uint8_t* tmpBuffer = new uint8_t[_lengthSourceFrame];
117 TEST(fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0);
118 I420VideoFrame sourceFrame;
119 int half_width = (_width + 1) / 2;
120 int half_height = (_height + 1) / 2;
121 int size_y = _width * _height;
122 int size_uv = half_width * half_height;
123 sourceFrame.CreateFrame(size_y, tmpBuffer,
124 size_uv, tmpBuffer + size_y,
125 size_uv, tmpBuffer + size_y + size_uv,
126 _width, _height,
127 _width, half_width, half_width);
128 _timeStamp += (uint32_t)(9e4 / _frameRate);
129 sourceFrame.set_timestamp(_timeStamp);
130 // Encoder registration
131 TEST (VideoCodingModule::NumberOfCodecs() > 0);
132 TEST(VideoCodingModule::Codec(-1, &sendCodec) < 0);
133 TEST(VideoCodingModule::Codec(VideoCodingModule::NumberOfCodecs() + 1,
134 &sendCodec) < 0);
135 VideoCodingModule::Codec(1, &sendCodec);
136 sendCodec.plType = 0; // random value
137 TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) < 0);
138 _vcm->InitializeReceiver();
139 _vcm->InitializeSender();
140 _vcm->RegisterReceiveCallback(_decodeCallback);
141 _vcm->RegisterTransportCallback(_encodeCompleteCallback);
142 printf(" \nNumber of Registered Codecs: %d \n\n", VideoCodingModule::NumberOfCodecs());
143 printf("Registered codec names: ");
144 for (int i=0; i < VideoCodingModule::NumberOfCodecs(); i++)
145 {
146 VideoCodingModule::Codec(i, &sendCodec);
147 printf("%s ", sendCodec.plName);
148 }
149 printf("\n\nVerify that all requested codecs are used\n \n \n");
150
151 // Testing with VP8.
152 VideoCodingModule::Codec(kVideoCodecVP8, &sendCodec);
153 _vcm->RegisterSendCodec(&sendCodec, 1, 1440);
154 _encodeCompleteCallback->SetCodecType(kRtpVideoVp8);
155 _vcm->InitializeReceiver();
156 TEST (_vcm->AddVideoFrame(sourceFrame) == VCM_OK );
157 _vcm->InitializeSender();
158 TEST (_vcm->AddVideoFrame(sourceFrame) < 0 );
159
160 // Test changing frame size while keeping the same payload type
161 VideoCodingModule::Codec(0, &sendCodec);
162 sendCodec.width = 352;
163 sendCodec.height = 288;
164 VideoCodec currentSendCodec;
165 _vcm->RegisterSendCodec(&sendCodec, 1, 1440);
166 _vcm->SendCodec(¤tSendCodec);
167 TEST(currentSendCodec.width == sendCodec.width &&
168 currentSendCodec.height == sendCodec.height);
169 sendCodec.width = 352/2;
170 sendCodec.height = 288/2;
171 _vcm->RegisterSendCodec(&sendCodec, 1, 1440);
172 _vcm->SendCodec(¤tSendCodec);
173 TEST(currentSendCodec.width == sendCodec.width &&
174 currentSendCodec.height == sendCodec.height);
175
176 delete _decodeCallback;
177 _decodeCallback = NULL;
178 delete _encodeCompleteCallback;
179 _encodeCompleteCallback = NULL;
180
181 VCMEncodeCompleteCallback *_encodeCallback = new VCMEncodeCompleteCallback(_encodedFile);
182
183 /*************************/
184 /* External codecs */
185 /*************************/
186
187
188 _vcm->InitializeReceiver();
189 VP8Decoder* decoder = VP8Decoder::Create();
190 VideoCodec vp8DecSettings;
191 VideoCodingModule::Codec(kVideoCodecVP8, &vp8DecSettings);
192 TEST(_vcm->RegisterExternalDecoder(decoder, vp8DecSettings.plType, false) == VCM_OK);
193 TEST(_vcm->RegisterReceiveCodec(&vp8DecSettings, 1, false) == VCM_OK);
194 VP8Encoder* encoder = VP8Encoder::Create();
195 VideoCodec vp8EncSettings;
196 VideoCodingModule::Codec(kVideoCodecVP8, &vp8EncSettings);
197 _vcm->RegisterTransportCallback(_encodeCallback); // encode returns error if callback uninitialized
198 _encodeCallback->RegisterReceiverVCM(_vcm);
199 _encodeCallback->SetCodecType(kRtpVideoVp8);
200 TEST(_vcm->RegisterExternalEncoder(encoder, vp8EncSettings.plType) == VCM_OK);
201 TEST(_vcm->RegisterSendCodec(&vp8EncSettings, 4, 1440) == VCM_OK);
202 TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
203 TEST(_vcm->Decode() == VCM_OK);
204 waitEvent->Wait(33);
205 _timeStamp += (uint32_t)(9e4 / _frameRate);
206 sourceFrame.set_timestamp(_timeStamp);
207 TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
208 TEST(_vcm->Decode() == VCM_OK);
209
210 // De-register and try again.
211 TEST(_vcm->RegisterExternalDecoder(NULL, vp8DecSettings.plType, false) == VCM_OK);
212 TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
213 TEST(_vcm->Decode() < 0); // Expect an error since we have de-registered the decoder
214 TEST(_vcm->RegisterExternalEncoder(NULL, vp8DecSettings.plType) == VCM_OK);
215 TEST(_vcm->AddVideoFrame(sourceFrame) < 0); // No send codec registered
216
217 delete decoder;
218 decoder = NULL;
219 delete encoder;
220 encoder = NULL;
221
222 /***************************************
223 * Test the "require key frame" setting*
224 ***************************************/
225
226 TEST(_vcm->InitializeSender() == VCM_OK);
227 TEST(_vcm->InitializeReceiver() == VCM_OK);
228 VideoCodingModule::Codec(kVideoCodecVP8, &receiveCodec);
229 receiveCodec.height = _height;
230 receiveCodec.width = _width;
231 TEST(_vcm->RegisterSendCodec(&receiveCodec, 4, 1440) == VCM_OK);
232 TEST(_vcm->RegisterReceiveCodec(&receiveCodec, 1, true) == VCM_OK); // Require key frame
233 _vcm->RegisterTransportCallback(_encodeCallback); // encode returns error if callback uninitialized
234 _encodeCallback->RegisterReceiverVCM(_vcm);
235 _encodeCallback->SetCodecType(kRtpVideoVp8);
236 TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
237 TEST(_vcm->Decode() == VCM_OK);
238 TEST(_vcm->ResetDecoder() == VCM_OK);
239 waitEvent->Wait(33);
240 _timeStamp += (uint32_t)(9e4 / _frameRate);
241 sourceFrame.set_timestamp(_timeStamp);
242 TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
243 // Try to decode a delta frame. Should get a warning since we have enabled the "require key frame" setting
244 // and because no frame type request callback has been registered.
245 TEST(_vcm->Decode() == VCM_MISSING_CALLBACK);
246 TEST(_vcm->IntraFrameRequest(0) == VCM_OK);
247 _timeStamp += (uint32_t)(9e4 / _frameRate);
248 sourceFrame.set_timestamp(_timeStamp);
249 TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
250 TEST(_vcm->Decode() == VCM_OK);
251
252 // Make sure we can register another codec with the same
253 // payload type without crash.
254 _vcm->InitializeReceiver();
255 sendCodec.width = _width;
256 sendCodec.height = _height;
257 TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK);
258 TEST(_vcm->IntraFrameRequest(0) == VCM_OK);
259 waitEvent->Wait(33);
260 _timeStamp += (uint32_t)(9e4 / _frameRate);
261 sourceFrame.set_timestamp(_timeStamp);
262 TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
263 TEST(_vcm->Decode() == VCM_OK);
264 TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK);
265 waitEvent->Wait(33);
266 _timeStamp += (uint32_t)(9e4 / _frameRate);
267 sourceFrame.set_timestamp(_timeStamp);
268 TEST(_vcm->IntraFrameRequest(0) == VCM_OK);
269 TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
270 TEST(_vcm->Decode() == VCM_OK);
271 TEST(_vcm->ResetDecoder() == VCM_OK);
272
273 delete _encodeCallback;
274
275 /*************************/
276 /* Send/Receive Control */
277 /***********************/
278 /*
279 1. check available codecs (N)
280 2. register all corresponding decoders
281 3. encode 300/N frames with each encoder, and hope to properly decode
282 4. encode without a matching decoder - expect an error
283 */
284 rewind(_sourceFile);
285 _vcm->InitializeReceiver();
286 _vcm->InitializeSender();
287 VCMDecodeCompleteCallback* decodeCallCDT = new VCMDecodeCompleteCallback(_decodedFile);
288 VCMEncodeCompleteCallback* encodeCallCDT = new VCMEncodeCompleteCallback(_encodedFile);
289 _vcm->RegisterReceiveCallback(decodeCallCDT);
290 _vcm->RegisterTransportCallback(encodeCallCDT);
291 encodeCallCDT->RegisterReceiverVCM(_vcm);
292 if (VideoCodingModule::NumberOfCodecs() > 0)
293 {
294 // Register all available decoders.
295 int i, j;
296 sourceFrame.CreateEmptyFrame(_width, _height, _width,
297 (_width + 1) / 2, (_width + 1) / 2);
298 _vcm->RegisterReceiveCallback(decodeCallCDT);
299 for (i=0; i < VideoCodingModule::NumberOfCodecs(); i++)
300 {
301 VideoCodingModule::Codec(i, &receiveCodec);
302 if (strcmp(receiveCodec.plName, "I420") == 0)
303 {
304 receiveCodec.height = _height;
305 receiveCodec.width = _width;
306 }
307 _vcm->RegisterReceiveCodec(&receiveCodec, 1);
308 }
309 // start encoding - iterating over available encoders
310 _vcm->RegisterTransportCallback(encodeCallCDT);
311 encodeCallCDT->RegisterReceiverVCM(_vcm);
312 encodeCallCDT->Initialize();
313 int frameCnt = 0;
314 for (i=0; i < VideoCodingModule::NumberOfCodecs(); i++)
315 {
316 encodeCallCDT->ResetByteCount();
317 VideoCodingModule::Codec(i, &sendCodec);
318 sendCodec.height = _height;
319 sendCodec.width = _width;
320 sendCodec.startBitrate = 1000;
321 sendCodec.maxBitrate = 8000;
322 encodeCallCDT->SetFrameDimensions(_width, _height);
323 encodeCallCDT->SetCodecType(ConvertCodecType(sendCodec.plName));
324 TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) == VCM_OK);
325
326 // We disable the frame dropper to avoid dropping frames due to
327 // bad rate control. This isn't a codec performance test, and the
328 // I420 codec is expected to produce too many bits.
329 _vcm->EnableFrameDropper(false);
330
331 printf("Encoding with %s \n\n", sendCodec.plName);
332 // Assuming 300 frames, NumberOfCodecs <= 10.
333 for (j=0; j < int(300/VideoCodingModule::NumberOfCodecs()); j++)
334 {
335 frameCnt++;
336 TEST(fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0);
337 sourceFrame.CreateFrame(size_y, tmpBuffer,
338 size_uv, tmpBuffer + size_y,
339 size_uv, tmpBuffer + size_y + size_uv,
340 _width, _height,
341 _width, half_width, half_width);
342 _timeStamp += (uint32_t)(9e4 / _frameRate);
343 sourceFrame.set_timestamp(_timeStamp);
344 // send frame to the encoder
345 TEST (_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
346 waitEvent->Wait(33); // was 100
347
348 int ret =_vcm->Decode();
349 TEST(ret >= 0);
350 if (ret < 0)
351 {
352 printf("Error #%d in frame number %d \n",ret, frameCnt);
353 }
354 // verifying matching payload types:
355 _vcm->SendCodec(&sendCodec);
356 _vcm->ReceiveCodec(&receiveCodec);
357 TEST(sendCodec.plType == receiveCodec.plType);
358 if (sendCodec.plType != receiveCodec.plType)
359 {
360 printf("frame number:%d\n",frameCnt);
361 }
362 } // end for:encode-decode
363 // byte count for codec specific
364
365 printf("Total bytes encoded: %f \n\n",(8.0/1000)*(encodeCallCDT->EncodedBytes()/((int)10/VideoCodingModule::NumberOfCodecs())));
366 // decode what's left in the buffer....
367 _vcm->Decode();
368 _vcm->Decode();
369 // Don't measure PSNR for I420 since it will be perfect.
370 if (sendCodec.codecType != kVideoCodecI420) {
371 webrtc::test::QualityMetricsResult psnr;
372 I420PSNRFromFiles(_inname.c_str(), _outname.c_str(), _width,
373 _height, &psnr);
374 printf("\n @ %d KBPS: ", sendCodec.startBitrate);
375 printf("PSNR from encoder-decoder send-receive control test"
376 "is %f\n\n", psnr.average);
377 }
378 } // end: iterate codecs
379 rewind(_sourceFile);
380 delete [] tmpBuffer;
381 delete decodeCallCDT;
382 delete encodeCallCDT;
383 // closing and calculating PSNR for prior encoder-decoder test
384 TearDown(); // closing open files
385 } // end of #codecs >1
386
387 delete waitEvent;
388 Print();
389 return 0;
390 }
391 void
Print()392 CodecDataBaseTest::Print()
393 {
394 printf("\nVCM Codec DataBase Test: \n\n%i tests completed\n", vcmMacrosTests);
395 if (vcmMacrosErrors > 0)
396 {
397 printf("%i FAILED\n\n", vcmMacrosErrors);
398 }
399 else
400 {
401 printf("ALL PASSED\n\n");
402 }
403 }
404
405 void
TearDown()406 CodecDataBaseTest::TearDown()
407 {
408 fclose(_sourceFile);
409 fclose(_decodedFile);
410 fclose(_encodedFile);
411 return;
412 }
413