/* * 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. */ #include "generic_codec_test.h" #include #include #include "tick_time.h" #include "../source/event.h" #include "rtp_rtcp.h" #include "module_common_types.h" #include "test_util.h" using namespace webrtc; int GenericCodecTest::RunTest(CmdArgs& args) { // Don't run this test with debug time #if !defined(TICK_TIME_DEBUG) || !defined(EVENT_DEBUG) return -1; #endif VideoCodingModule* vcm = VideoCodingModule::Create(1); GenericCodecTest* get = new GenericCodecTest(vcm); Trace::CreateTrace(); Trace::SetTraceFile("genericCodecTestTrace.txt"); Trace::SetLevelFilter(webrtc::kTraceAll); get->Perform(args); Trace::ReturnTrace(); delete get; VideoCodingModule::Destroy(vcm); return 0; } GenericCodecTest::GenericCodecTest(VideoCodingModule* vcm): _width(0), _height(0), _timeStamp(0), _lengthSourceFrame(0), _frameRate(0), vcmMacrosTests(0), vcmMacrosErrors(0), _vcm(vcm) { } GenericCodecTest::~GenericCodecTest() { } void GenericCodecTest::Setup(CmdArgs& args) { _timeStamp = 0; /* Test Sequence parameters */ _inname= args.inputFile; if (args.outputFile.compare("")) _outname = "GCTest_decoded.yuv"; else _outname = args.outputFile; _encodedName = "../GCTest_encoded.vp8"; _width = args.width; _height = args.height; _frameRate = args.frameRate; _lengthSourceFrame = 3*_width*_height/2; /* File settings */ 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 GenericCodecTest::Perform(CmdArgs& args) { WebRtc_Word32 ret; Setup(args); /* 1. sanity checks 2. encode/decoder individuality 3. API testing 4. Target bitrate (within a specific timespan) 5. Pipeline Delay */ /*******************************/ /* sanity checks on inputs */ /*****************************/ VideoCodec sendCodec, receiveCodec; sendCodec.maxBitrate = 8000; TEST(_vcm->NumberOfCodecs() > 0); // This works since we now initialize the list in the constructor TEST(_vcm->Codec(0, &sendCodec) == VCM_OK); _vcm->InitializeSender(); _vcm->InitializeReceiver(); WebRtc_Word32 NumberOfCodecs = _vcm->NumberOfCodecs(); // registration of first codec in the list int i = 0; _vcm->Codec(0, &_sendCodec); TEST(_vcm->RegisterSendCodec(&_sendCodec, 4, 1440) == VCM_OK); // sanity on encoder registration VideoFrame sourceFrame; sourceFrame.VerifyAndAllocate(_lengthSourceFrame); _vcm->InitializeSender(); TEST(_vcm->Codec(kVideoCodecVP8, &sendCodec) == 0); TEST(_vcm->RegisterSendCodec(&sendCodec, -1, 1440) < 0); // bad number of cores sendCodec.maxBitrate = 8000; _vcm->RegisterSendCodec(&sendCodec, 1, 1440); _vcm->InitializeSender(); _vcm->Codec(kVideoCodecVP8, &sendCodec); sendCodec.height = 0; TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) < 0); // bad height _vcm->Codec(kVideoCodecVP8, &sendCodec); sendCodec.startBitrate = -2; TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) < 0); // bad bit rate _vcm->Codec(kVideoCodecVP8, &sendCodec); _vcm->InitializeSender(); TEST(_vcm->SetChannelParameters(100, 0, 0) < 0);// setting rate when encoder uninitialized // register all availbale decoders -- need to have more for this test for (i=0; i< NumberOfCodecs; i++) { _vcm->Codec(i, &receiveCodec); _vcm->RegisterReceiveCodec(&receiveCodec, 1); } WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame]; fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); // building source frame sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); sourceFrame.SetHeight(_height); sourceFrame.SetWidth(_width); sourceFrame.SetTimeStamp(_timeStamp++); // encode/decode TEST(_vcm->AddVideoFrame(sourceFrame) < 0 ); // encoder uninitialized _vcm->InitializeReceiver(); TEST(_vcm->SetChannelParameters(100, 0, 0) < 0);// setting rtt when receiver uninitialized /**************************************/ /* encoder/decoder individuality test */ /**************************************/ //Register both encoder and decoder, reset decoder - encode, set up decoder, reset encoder - decode. rewind(_sourceFile); sourceFrame.Free(); _vcm->InitializeReceiver(); _vcm->InitializeSender(); NumberOfCodecs = _vcm->NumberOfCodecs(); // Register VP8 _vcm->Codec(kVideoCodecVP8, &_sendCodec); _vcm->RegisterSendCodec(&_sendCodec, 4, 1440); _vcm->SendCodec(&sendCodec); sendCodec.startBitrate = 2000; // Set target frame rate to half of the incoming frame rate // to test the frame rate control in the VCM sendCodec.maxFramerate = (WebRtc_UWord8)(_frameRate / 2); sendCodec.width = _width; sendCodec.height = _height; TEST(strncmp(_sendCodec.plName, "VP8", 3) == 0); // was VP8 _decodeCallback = new VCMDecodeCompleteCallback(_decodedFile); _encodeCompleteCallback = new VCMEncodeCompleteCallback(_encodedFile); _vcm->RegisterReceiveCallback(_decodeCallback); _vcm->RegisterTransportCallback(_encodeCompleteCallback); _encodeCompleteCallback->RegisterReceiverVCM(_vcm); _vcm->RegisterSendCodec(&sendCodec, 4, 1440); _encodeCompleteCallback->SetCodecType(ConvertCodecType(sendCodec.plName)); _vcm->InitializeReceiver(); _vcm->Process(); //encoding 1 second of video for (i = 0; i < _frameRate; i++) { fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); sourceFrame.SetHeight(_height); sourceFrame.SetWidth(_width); _timeStamp += (WebRtc_UWord32)(9e4 / static_cast(_frameRate)); sourceFrame.SetTimeStamp(_timeStamp); TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); IncrementDebugClock(_frameRate); _vcm->Process(); } sendCodec.maxFramerate = (WebRtc_UWord8)_frameRate; _vcm->InitializeSender(); TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK); // same codec for encode and decode ret = 0; i = 0; while ((i < 25) && (ret == 0) ) { ret = _vcm->Decode(); TEST(ret == VCM_OK); if (ret < 0) { printf("error in frame # %d \n", i); } IncrementDebugClock(_frameRate); i++; } //TEST((ret == 0) && (i = 50)); if (ret == 0) { printf("Encoder/Decoder individuality test complete - View output files \n"); } // last frame - not decoded _vcm->InitializeReceiver(); TEST(_vcm->Decode() < 0); // frame to be encoded exists, decoder uninitialized // Test key frame request on packet loss mode. // This a frame as a key frame and fooling the receiver // that the last packet was lost. The decoding will succeed, // but the VCM will see a packet loss and request a new key frame. VCMEncComplete_KeyReqTest keyReqTest_EncCompleteCallback(*_vcm); KeyFrameReqTest frameTypeCallback; _vcm->RegisterTransportCallback(&keyReqTest_EncCompleteCallback); _encodeCompleteCallback->RegisterReceiverVCM(_vcm); _vcm->RegisterSendCodec(&sendCodec, 4, 1440); _encodeCompleteCallback->SetCodecType(ConvertCodecType(sendCodec.plName)); TEST(_vcm->SetVideoProtection(kProtectionKeyOnKeyLoss, true) == VCM_OK); TEST(_vcm->RegisterFrameTypeCallback(&frameTypeCallback) == VCM_OK); TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK); TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); TEST(_vcm->Decode() == VCM_OK); printf("API tests complete \n"); /*******************/ /* Bit Rate Tests */ /*****************/ /* Requirements: * 1. OneSecReq = 15 % above/below target over a time period of 1s (_frameRate number of frames) * 3. FullReq = 10% for total seq. (for 300 frames/seq. coincides with #1) * 4. Test will go over all registered codecs //NOTE: time requirements are not part of the release tests */ double FullReq = 0.1; double OneSecReq = 0.15; printf("\n RATE CONTROL TEST\n"); // initializing.... _vcm->InitializeSender(); _vcm->InitializeReceiver(); rewind(_sourceFile); sourceFrame.Free(); sourceFrame.VerifyAndAllocate(_lengthSourceFrame); const float bitRate[] = {100, 400, 600, 1000, 2000, 3000}; const float nBitrates = sizeof(bitRate)/sizeof(*bitRate); float _bitRate; int _frameCnt = 0; WebRtc_Word64 startTime, currentTime, oneSecTime; float totalBytesOneSec;//, totalBytesTenSec; float totalBytes, actualBitrate; VCMFrameCount frameCount; // testing frame type counters // start test NumberOfCodecs = _vcm->NumberOfCodecs(); // going over all available codecs _encodeCompleteCallback->SetFrameDimensions(_width, _height); SendStatsTest sendStats; for (int k = 0; k < NumberOfCodecs; k++) //for (int k = NumberOfCodecs - 1; k >=0; k--) {// static list starts from 0 //just checking _vcm->InitializeSender(); _sendCodec.maxBitrate = 8000; TEST(_vcm->Codec(k, &_sendCodec)== VCM_OK); _vcm->RegisterSendCodec(&_sendCodec, 1, 1440); _vcm->RegisterTransportCallback(_encodeCompleteCallback); _encodeCompleteCallback->SetCodecType(ConvertCodecType(_sendCodec.plName)); printf (" \n\n Codec type = %s \n\n",_sendCodec.plName); for (i = 0; i < nBitrates; i++) { _bitRate = static_cast(bitRate[i]); // just testing _vcm->InitializeSender(); _sendCodec.startBitrate = (int)_bitRate; _sendCodec.maxBitrate = 8000; _vcm->RegisterSendCodec(&_sendCodec, 1, 1440); _vcm->RegisterTransportCallback(_encodeCompleteCallback); // up to here _vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 20); _frameCnt = 0; totalBytes = 0; startTime = VCMTickTime::MicrosecondTimestamp(); _encodeCompleteCallback->Initialize(); sendStats.SetTargetFrameRate(static_cast(_frameRate)); _vcm->RegisterSendStatisticsCallback(&sendStats); while (fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0) { _frameCnt++; sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); sourceFrame.SetHeight(_height); sourceFrame.SetWidth(_width); _timeStamp += (WebRtc_UWord32)(9e4 / static_cast(_frameRate)); sourceFrame.SetTimeStamp(_timeStamp); ret = _vcm->AddVideoFrame(sourceFrame); IncrementDebugClock(_frameRate); // The following should be uncommneted for timing tests. Release tests only include // compliance with full sequence bit rate. //totalBytes = WaitForEncodedFrame(); //currentTime = VCMTickTime::MillisecondTimestamp();//clock()/(double)CLOCKS_PER_SEC; if (_frameCnt == _frameRate)// @ 1sec { oneSecTime = VCMTickTime::MicrosecondTimestamp(); totalBytesOneSec = _encodeCompleteCallback->EncodedBytes();//totalBytes; } TEST(_vcm->TimeUntilNextProcess() >= 0); } // video seq. encode done TEST(_vcm->TimeUntilNextProcess() == 0); _vcm->Process(); // Let the module calculate its send bit rate estimate // estimating rates // complete sequence // bit rate assumes input frame rate is as specified currentTime = VCMTickTime::MicrosecondTimestamp(); totalBytes = _encodeCompleteCallback->EncodedBytes(); actualBitrate = (float)(8.0/1000)*(totalBytes / (_frameCnt / _frameRate)); WebRtc_Word64 timeDiff = (currentTime - startTime)/1000; //actualBitrate = (float)(8.0*totalBytes)/timeDiff; printf("Complete Seq.: target bitrate: %.0f kbps, actual bitrate: %.1f kbps\n", _bitRate, actualBitrate); TEST((fabs(actualBitrate - _bitRate) < FullReq * _bitRate) || (strncmp(_sendCodec.plName, "I420", 4) == 0)); // 1 Sec. actualBitrate = (float)(8.0/1000)*(totalBytesOneSec); //actualBitrate = (float)(8.0*totalBytesOneSec)/(oneSecTime - startTime); //printf("First 1Sec: target bitrate: %.0f kbps, actual bitrate: %.1f kbps\n", _bitRate, actualBitrate); //TEST(fabs(actualBitrate - _bitRate) < OneSecReq * _bitRate); rewind(_sourceFile); //checking key/delta frame count _vcm->SentFrameCount(frameCount); printf("frame count: %d delta, %d key\n", frameCount.numDeltaFrames, frameCount.numKeyFrames); }// end per codec } // end rate control test /********************************/ /* Encoder Pipeline Delay Test */ /******************************/ WebRtc_Word32 retVal; _vcm->InitializeSender(); sourceFrame.Free(); sourceFrame.VerifyAndAllocate(_lengthSourceFrame); NumberOfCodecs = _vcm->NumberOfCodecs(); bool encodeComplete = false; // going over all available codecs for (int k = 0; k < NumberOfCodecs; k++) { retVal = _vcm->Codec(k, &_sendCodec); retVal = _vcm->InitializeSender(); _sendCodec.maxBitrate = 8000; retVal = _vcm->RegisterSendCodec(&_sendCodec, 4, 1440); retVal = _vcm->RegisterTransportCallback(_encodeCompleteCallback); _frameCnt = 0; encodeComplete = false; while (encodeComplete == false) { fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); _frameCnt++; sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); sourceFrame.SetHeight(_height); sourceFrame.SetWidth(_width); _timeStamp += (WebRtc_UWord32)(9e4 / static_cast(_frameRate)); sourceFrame.SetTimeStamp(_timeStamp); retVal = _vcm->AddVideoFrame(sourceFrame); encodeComplete = _encodeCompleteCallback->EncodeComplete(); } // first frame encoded printf ("\n Codec type = %s \n", _sendCodec.plName); printf(" Encoder pipeline delay = %d frames\n", _frameCnt - 1); } // end for all codecs /********************************/ /* Encoder Packet Size Test */ /********************************/ RtpRtcp& rtpModule = *RtpRtcp::CreateRtpRtcp(1, false); TEST(rtpModule.InitSender() == 0); RTPSendCallback_SizeTest sendCallback; rtpModule.RegisterSendTransport(&sendCallback); VCMRTPEncodeCompleteCallback encCompleteCallback(&rtpModule); _vcm->InitializeSender(); // TEST DISABLED FOR NOW SINCE VP8 DOESN'T HAVE THIS FEATURE // sourceFrame.Free(); // sourceFrame.VerifyAndAllocate(_lengthSourceFrame); // NumberOfCodecs = _vcm->NumberOfCodecs(); // WebRtc_UWord32 targetPayloadSize = 500; // rtpModule.SetMaxTransferUnit(targetPayloadSize); // // going over all available codecs // for (int k = 0; k < NumberOfCodecs; k++) // { // _vcm->Codec(k, &_sendCodec); // if (strncmp(_sendCodec.plName, "VP8", 3) == 0) // { // // Only test with VP8 // continue; // } // rtpModule.RegisterSendPayload(_sendCodec.plName, _sendCodec.plType); // // Make sure we only get one NAL unit per packet // _vcm->InitializeSender(); // _vcm->RegisterSendCodec(&_sendCodec, 4, targetPayloadSize); // sendCallback.SetMaxPayloadSize(targetPayloadSize); // _vcm->RegisterTransportCallback(&encCompleteCallback); // sendCallback.Reset(); // _frameCnt = 0; // rewind(_sourceFile); // while (!feof(_sourceFile)) // { // fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); // _frameCnt++; // sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); // sourceFrame.SetHeight(_height); // sourceFrame.SetWidth(_width); // _timeStamp += (WebRtc_UWord32)(9e4 / static_cast(_frameRate)); // sourceFrame.SetTimeStamp(_timeStamp); // ret = _vcm->AddVideoFrame(sourceFrame); // } // first frame encoded // printf ("\n Codec type = %s \n",_sendCodec.plName); // printf(" Average payload size = %f bytes, target = %u bytes\n", sendCallback.AveragePayloadSize(), targetPayloadSize); // } // end for all codecs // Test temporal decimation settings for (int k = 0; k < NumberOfCodecs; k++) { _vcm->Codec(k, &_sendCodec); if (strncmp(_sendCodec.plName, "I420", 4) == 0) { // Only test with I420 break; } } TEST(strncmp(_sendCodec.plName, "I420", 4) == 0); _vcm->InitializeSender(); _sendCodec.maxFramerate = static_cast(_frameRate / 2.0 + 0.5f); _vcm->RegisterSendCodec(&_sendCodec, 4, 1440); _vcm->SetChannelParameters(2000, 0, 0); _vcm->RegisterTransportCallback(_encodeCompleteCallback); // up to here _vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 20); _encodeCompleteCallback->Initialize(); sendStats.SetTargetFrameRate(static_cast(_frameRate)); _vcm->RegisterSendStatisticsCallback(&sendStats); rewind(_sourceFile); while (!feof(_sourceFile)) { fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); sourceFrame.SetHeight(_height); sourceFrame.SetWidth(_width); _timeStamp += (WebRtc_UWord32)(9e4 / static_cast(_frameRate)); sourceFrame.SetTimeStamp(_timeStamp); ret = _vcm->AddVideoFrame(sourceFrame); if (_vcm->TimeUntilNextProcess() <= 0) { _vcm->Process(); } IncrementDebugClock(_frameRate); } // first frame encoded RtpRtcp::DestroyRtpRtcp(&rtpModule); Print(); delete tmpBuffer; delete _decodeCallback; delete _encodeCompleteCallback; return 0; } void GenericCodecTest::Print() { printf(" \n\n VCM Generic Encoder Test: \n\n%i tests completed\n", vcmMacrosTests); if (vcmMacrosErrors > 0) { printf("%i FAILED\n\n", vcmMacrosErrors); } else { printf("ALL PASSED\n\n"); } } float GenericCodecTest::WaitForEncodedFrame() const { WebRtc_Word64 startTime = TickTime::MillisecondTimestamp(); while (TickTime::MillisecondTimestamp() - startTime < kMaxWaitEncTimeMs*10) { if (_encodeCompleteCallback->EncodeComplete()) { return _encodeCompleteCallback->EncodedBytes(); } } return 0; } void GenericCodecTest::IncrementDebugClock(float frameRate) { for (int t= 0; t < 1000/frameRate; t++) { VCMTickTime::IncrementDebugClock(); } return; } int RTPSendCallback_SizeTest::SendPacket(int channel, const void *data, int len) { _nPackets++; _payloadSizeSum += len; // Make sure no payloads (len - header size) are larger than maxPayloadSize TEST(len > 0 && static_cast(len - 12) <= _maxPayloadSize); return 0; } void RTPSendCallback_SizeTest::SetMaxPayloadSize(WebRtc_UWord32 maxPayloadSize) { _maxPayloadSize = maxPayloadSize; } void RTPSendCallback_SizeTest::Reset() { _nPackets = 0; _payloadSizeSum = 0; } float RTPSendCallback_SizeTest::AveragePayloadSize() const { if (_nPackets > 0) { return _payloadSizeSum / static_cast(_nPackets); } return 0; } WebRtc_Word32 VCMEncComplete_KeyReqTest::SendData(const FrameType frameType, const WebRtc_UWord8 payloadType, const WebRtc_UWord32 timeStamp, const WebRtc_UWord8* payloadData, const WebRtc_UWord32 payloadSize, const RTPFragmentationHeader& fragmentationHeader) { WebRtcRTPHeader rtpInfo; rtpInfo.header.markerBit = true; // end of frame rtpInfo.type.Video.codec = kRTPVideoVP8; rtpInfo.header.payloadType = payloadType; rtpInfo.header.sequenceNumber = _seqNo; _seqNo += 2; rtpInfo.header.ssrc = 0; rtpInfo.header.timestamp = _timeStamp; _timeStamp += 3000; rtpInfo.type.Video.isFirstPacket = false; rtpInfo.frameType = kVideoFrameKey; return _vcm.IncomingPacket(payloadData, payloadSize, rtpInfo); }