/* * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ // Implementation of codec data base test // testing is done via the VCM module, no specific CodecDataBase module functionality. #include "codec_database_test.h" #include "vp8.h" // for external codecs test #include "../source/event.h" #include "test_util.h" #include "../../../../engine_configurations.h" #include #include using namespace webrtc; int CodecDataBaseTest::RunTest(CmdArgs& args) { VideoCodingModule* vcm = VideoCodingModule::Create(1); CodecDataBaseTest* cdbt = new CodecDataBaseTest(vcm); cdbt->Perform(args); VideoCodingModule::Destroy(vcm); delete cdbt; return 0; } CodecDataBaseTest::CodecDataBaseTest(VideoCodingModule* vcm): _width(0), _height(0), _timeStamp(0), _lengthSourceFrame(0), vcmMacrosTests(0), vcmMacrosErrors(0), _vcm(vcm) { // } CodecDataBaseTest::~CodecDataBaseTest() { // } void CodecDataBaseTest::Setup(CmdArgs& args) { _inname= args.inputFile; _width = args.width; _height = args.height; _frameRate = args.frameRate; _lengthSourceFrame = 3*_width*_height/2; if (args.outputFile.compare("")) _outname = "CDBtest_decoded.yuv"; else _outname = args.outputFile; _outname = args.outputFile; _encodedName = "../CDBtest_encoded.vp8"; if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL) { printf("Cannot read file %s.\n", _inname.c_str()); exit(1); } if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL) { printf("Cannot write encoded file.\n"); exit(1); } if ((_decodedFile = fopen(_outname.c_str(), "wb")) == NULL) { printf("Cannot write file %s.\n", _outname.c_str()); exit(1); } return; } WebRtc_Word32 CodecDataBaseTest::Perform(CmdArgs& args) { #ifndef VIDEOCODEC_VP8 assert(false); #endif Setup(args); EventWrapper* waitEvent = EventWrapper::Create(); /**************************/ /* General Sanity Checks */ /************************/ VideoCodec sendCodec, receiveCodec; TEST(VideoCodingModule::NumberOfCodecs() > 0); WebRtc_Word8 version[512]; WebRtc_UWord32 length = 512; WebRtc_UWord32 position = 0; TEST(_vcm->Version(version, length, position) == VCM_OK); printf("%s", version); _vcm->InitializeReceiver(); _vcm->InitializeSender(); VCMDecodeCompleteCallback *_decodeCallback = new VCMDecodeCompleteCallback(_decodedFile); VCMEncodeCompleteCallback *_encodeCompleteCallback = new VCMEncodeCompleteCallback(_encodedFile); _vcm->RegisterReceiveCallback(_decodeCallback); _vcm->RegisterTransportCallback(_encodeCompleteCallback); _encodeCompleteCallback->SetFrameDimensions(_width, _height); // registering the callback - encode and decode with the same vcm (could be later changed) _encodeCompleteCallback->RegisterReceiverVCM(_vcm); // preparing a frame to be encoded VideoFrame sourceFrame; sourceFrame.VerifyAndAllocate(_lengthSourceFrame); WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame]; fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); sourceFrame.SetHeight(_height); sourceFrame.SetWidth(_width); _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate); sourceFrame.SetTimeStamp(_timeStamp); // Encoder registration TEST (VideoCodingModule::NumberOfCodecs() > 0); TEST(VideoCodingModule::Codec(-1, &sendCodec) == VCM_PARAMETER_ERROR); TEST(VideoCodingModule::Codec(VideoCodingModule::NumberOfCodecs() + 1, &sendCodec) == VCM_PARAMETER_ERROR); VideoCodingModule::Codec(1, &sendCodec); sendCodec.plType = 0; // random value TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) < 0); _vcm->InitializeReceiver(); _vcm->InitializeSender(); _vcm->RegisterReceiveCallback(_decodeCallback); _vcm->RegisterTransportCallback(_encodeCompleteCallback); printf(" \nNumber of Registered Codecs: %d \n\n", VideoCodingModule::NumberOfCodecs()); printf("Registered codec names: "); for (int i=0; i < VideoCodingModule::NumberOfCodecs(); i++) { VideoCodingModule::Codec(i, &sendCodec); printf("%s ", sendCodec.plName); } printf("\n\nVerify that all requested codecs are used\n \n \n"); // testing with first codec registered VideoCodingModule::Codec(0, &sendCodec); _vcm->RegisterSendCodec(&sendCodec, 1, 1440); _vcm->InitializeReceiver(); TEST (_vcm->AddVideoFrame(sourceFrame) == VCM_OK ); _vcm->InitializeSender(); TEST (_vcm->AddVideoFrame(sourceFrame) < 0 ); // Test changing frame size while keeping the same payload type VideoCodingModule::Codec(0, &sendCodec); sendCodec.width = 352; sendCodec.height = 288; VideoCodec currentSendCodec; _vcm->RegisterSendCodec(&sendCodec, 1, 1440); _vcm->SendCodec(¤tSendCodec); TEST(currentSendCodec.width == sendCodec.width && currentSendCodec.height == sendCodec.height); sendCodec.width = 352/2; sendCodec.height = 288/2; _vcm->RegisterSendCodec(&sendCodec, 1, 1440); _vcm->SendCodec(¤tSendCodec); TEST(currentSendCodec.width == sendCodec.width && currentSendCodec.height == sendCodec.height); delete _decodeCallback; _decodeCallback = NULL; delete _encodeCompleteCallback; _encodeCompleteCallback = NULL; VCMEncodeCompleteCallback *_encodeCallback = new VCMEncodeCompleteCallback(_encodedFile); /*************************/ /* External codecs */ /*************************/ _vcm->InitializeReceiver(); VP8Decoder* decoder = new VP8Decoder; VideoCodec vp8DecSettings; VideoCodingModule::Codec(kVideoCodecVP8, &vp8DecSettings); TEST(_vcm->RegisterExternalDecoder(decoder, vp8DecSettings.plType, false) == VCM_OK); TEST(_vcm->RegisterReceiveCodec(&vp8DecSettings, 1, false) == VCM_OK); VP8Encoder* encoder = new VP8Encoder; VideoCodec vp8EncSettings; VideoCodingModule::Codec(kVideoCodecVP8, &vp8EncSettings); _vcm->RegisterTransportCallback(_encodeCallback); // encode returns error if callback uninitialized _encodeCallback->RegisterReceiverVCM(_vcm); _encodeCallback->SetCodecType(kRTPVideoVP8); TEST(_vcm->RegisterExternalEncoder(encoder, vp8EncSettings.plType) == VCM_OK); TEST(_vcm->RegisterSendCodec(&vp8EncSettings, 4, 1440) == VCM_OK); TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); TEST(_vcm->Decode() == VCM_OK); waitEvent->Wait(33); _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate); sourceFrame.SetTimeStamp(_timeStamp); TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); TEST(_vcm->Decode() == VCM_OK); // De-register and try again. TEST(_vcm->RegisterExternalDecoder(NULL, vp8DecSettings.plType, false) == VCM_OK); TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); TEST(_vcm->Decode() < 0); // Expect an error since we have de-registered the decoder TEST(_vcm->RegisterExternalEncoder(NULL, vp8DecSettings.plType) == VCM_OK); TEST(_vcm->AddVideoFrame(sourceFrame) < 0); // No send codec registered delete decoder; decoder = NULL; delete encoder; encoder = NULL; /*************************************** * Test the "require key frame" setting* ***************************************/ TEST(_vcm->InitializeSender() == VCM_OK); TEST(_vcm->InitializeReceiver() == VCM_OK); VideoCodingModule::Codec(kVideoCodecVP8, &receiveCodec); receiveCodec.height = _height; receiveCodec.width = _width; TEST(_vcm->RegisterSendCodec(&receiveCodec, 4, 1440) == VCM_OK); TEST(_vcm->RegisterReceiveCodec(&receiveCodec, 1, true) == VCM_OK); // Require key frame _vcm->RegisterTransportCallback(_encodeCallback); // encode returns error if callback uninitialized _encodeCallback->RegisterReceiverVCM(_vcm); _encodeCallback->SetCodecType(kRTPVideoVP8); TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); TEST(_vcm->Decode() == VCM_OK); TEST(_vcm->ResetDecoder() == VCM_OK); waitEvent->Wait(33); _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate); sourceFrame.SetTimeStamp(_timeStamp); TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); // Try to decode a delta frame. Should get a warning since we have enabled the "require key frame" setting // and because no frame type request callback has been registered. TEST(_vcm->Decode() == VCM_MISSING_CALLBACK); TEST(_vcm->FrameTypeRequest(kVideoFrameKey) == VCM_OK); TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); TEST(_vcm->Decode() == VCM_OK); // Make sure we can register another codec with the same // payload type without crash. _vcm->InitializeReceiver(); sendCodec.width = _width; sendCodec.height = _height; TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK); TEST(_vcm->FrameTypeRequest(kVideoFrameKey) == VCM_OK); waitEvent->Wait(33); _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate); sourceFrame.SetTimeStamp(_timeStamp); TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); TEST(_vcm->Decode() == VCM_OK); TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK); waitEvent->Wait(33); _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate); sourceFrame.SetTimeStamp(_timeStamp); TEST(_vcm->FrameTypeRequest(kVideoFrameKey) == VCM_OK); TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); TEST(_vcm->Decode() == VCM_OK); TEST(_vcm->ResetDecoder() == VCM_OK); delete _encodeCallback; /*************************/ /* Send/Receive Control */ /***********************/ /* 1. check available codecs (N) 2. register all corresponding decoders 3. encode 300/N frames with each encoder, and hope to properly decode 4. encode without a matching decoder - expect an error */ rewind(_sourceFile); _vcm->InitializeReceiver(); _vcm->InitializeSender(); sourceFrame.Free(); VCMDecodeCompleteCallback* decodeCallCDT = new VCMDecodeCompleteCallback(_decodedFile); VCMEncodeCompleteCallback* encodeCallCDT = new VCMEncodeCompleteCallback(_encodedFile); _vcm->RegisterReceiveCallback(decodeCallCDT); _vcm->RegisterTransportCallback(encodeCallCDT); encodeCallCDT->RegisterReceiverVCM(_vcm); if (VideoCodingModule::NumberOfCodecs() > 0) { // registrating all available decoders int i, j; //double psnr; sourceFrame.VerifyAndAllocate(_lengthSourceFrame); _vcm->RegisterReceiveCallback(decodeCallCDT); for (i=0; i < VideoCodingModule::NumberOfCodecs(); i++) { VideoCodingModule::Codec(i, &receiveCodec); if (strcmp(receiveCodec.plName, "I420") == 0) { receiveCodec.height = _height; receiveCodec.width = _width; } _vcm->RegisterReceiveCodec(&receiveCodec, 1); } // start encoding - iterating over available encoders _vcm->RegisterTransportCallback(encodeCallCDT); encodeCallCDT->RegisterReceiverVCM(_vcm); encodeCallCDT->Initialize(); int frameCnt = 0; for (i=0; i < VideoCodingModule::NumberOfCodecs(); i++) { encodeCallCDT->ResetByteCount(); VideoCodingModule::Codec(i, &sendCodec); sendCodec.height = _height; sendCodec.width = _width; sendCodec.startBitrate = 1000; sendCodec.maxBitrate = 8000; encodeCallCDT->SetFrameDimensions(_width, _height); encodeCallCDT->SetCodecType(ConvertCodecType(sendCodec.plName)); TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) == VCM_OK); printf("Encoding with %s \n\n", sendCodec.plName); for (j=0; j < int(300/VideoCodingModule::NumberOfCodecs()); j++)// assuming 300 frames, NumberOfCodecs <= 10 { frameCnt++; fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); // building source frame sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); sourceFrame.SetHeight(_height); sourceFrame.SetWidth(_width); sourceFrame.SetLength(_lengthSourceFrame); _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate); sourceFrame.SetTimeStamp(_timeStamp); // send frame to the encoder TEST (_vcm->AddVideoFrame(sourceFrame) == VCM_OK); waitEvent->Wait(33); // was 100 int ret =_vcm->Decode(); TEST(ret == 0); if (ret < 0) { printf("Error #%d in frame number %d \n",ret, frameCnt); } // verifying matching payload types: _vcm->SendCodec(&sendCodec); _vcm->ReceiveCodec(&receiveCodec); TEST(sendCodec.plType == receiveCodec.plType); if (sendCodec.plType != receiveCodec.plType) { printf("frame number:%d\n",frameCnt); } } // end for:encode-decode // byte count for codec specific printf("Total bytes encoded: %f \n\n",(8.0/1000)*(encodeCallCDT->EncodedBytes()/((int)10/VideoCodingModule::NumberOfCodecs()))); // decode what's left in the buffer.... _vcm->Decode(); _vcm->Decode(); } // end: iterate codecs rewind(_sourceFile); sourceFrame.Free(); delete tmpBuffer; delete decodeCallCDT; delete encodeCallCDT; // closing and calculating PSNR for prior encoder-decoder test TearDown(); // closing open files double psnr = 0; PSNRfromFiles(_inname.c_str(), _outname.c_str(), _width, _height, &psnr); printf(" \n @ %d KBPS: ", sendCodec.startBitrate); printf("PSNR from encoder-decoder send-receive control test is %f \n \n", psnr); } // end of #codecs >1 delete waitEvent; Print(); return 0; } void CodecDataBaseTest::Print() { printf("\nVCM Codec DataBase Test: \n\n%i tests completed\n", vcmMacrosTests); if (vcmMacrosErrors > 0) { printf("%i FAILED\n\n", vcmMacrosErrors); } else { printf("ALL PASSED\n\n"); } } void CodecDataBaseTest::TearDown() { fclose(_sourceFile); fclose(_decodedFile); fclose(_encodedFile); return; }