webrtc/modules/video_coding/main/test/codec_database_test.cc

397 lines
15 KiB
C++

/*
* 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 <assert.h>
#include <stdio.h>
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(&currentSendCodec);
TEST(currentSendCodec.width == sendCodec.width &&
currentSendCodec.height == sendCodec.height);
sendCodec.width = 352/2;
sendCodec.height = 288/2;
_vcm->RegisterSendCodec(&sendCodec, 1, 1440);
_vcm->SendCodec(&currentSendCodec);
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;
}