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 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 41 CodecDataBaseTest::CodecDataBaseTest(VideoCodingModule* vcm): 42 _vcm(vcm), 43 _width(0), 44 _height(0), 45 _lengthSourceFrame(0), 46 _timeStamp(0) 47 { 48 // 49 } 50 CodecDataBaseTest::~CodecDataBaseTest() 51 { 52 // 53 } 54 void 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 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 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 406 CodecDataBaseTest::TearDown() 407 { 408 fclose(_sourceFile); 409 fclose(_decodedFile); 410 fclose(_encodedFile); 411 return; 412 } 413