git-svn-id: http://webrtc.googlecode.com/svn/trunk@4 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
396
modules/video_coding/main/test/codec_database_test.cc
Normal file
396
modules/video_coding/main/test/codec_database_test.cc
Normal file
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* 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(¤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;
|
||||
}
|
||||
54
modules/video_coding/main/test/codec_database_test.h
Normal file
54
modules/video_coding/main/test/codec_database_test.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_CODEC_DATABASE_TEST_H_
|
||||
#define WEBRTC_MODULES_VIDEO_CODING_TEST_CODEC_DATABASE_TEST_H_
|
||||
|
||||
#include "video_coding.h"
|
||||
#include "test_macros.h"
|
||||
#include "test_util.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
Test consists of:
|
||||
1. Sanity chacks: Send and Receive side (bad input, etc. )
|
||||
2. Send-side control (encoder registration etc.)
|
||||
3. Decoder-side control - encode with various encoders, and verify correct decoding
|
||||
*/
|
||||
|
||||
class CodecDataBaseTest
|
||||
{
|
||||
public:
|
||||
CodecDataBaseTest(webrtc::VideoCodingModule* vcm);
|
||||
~CodecDataBaseTest();
|
||||
static int RunTest(CmdArgs& args);
|
||||
WebRtc_Word32 Perform(CmdArgs& args);
|
||||
private:
|
||||
void TearDown();
|
||||
void Setup(CmdArgs& args);
|
||||
void Print();
|
||||
webrtc::VideoCodingModule* _vcm;
|
||||
std::string _inname;
|
||||
std::string _outname;
|
||||
std::string _encodedName;
|
||||
FILE* _sourceFile;
|
||||
FILE* _decodedFile;
|
||||
FILE* _encodedFile;
|
||||
WebRtc_UWord16 _width;
|
||||
WebRtc_UWord16 _height;
|
||||
WebRtc_UWord32 _lengthSourceFrame;
|
||||
WebRtc_UWord32 _timeStamp;
|
||||
int vcmMacrosTests;
|
||||
int vcmMacrosErrors;
|
||||
float _frameRate;
|
||||
}; // end of codecDBTest class definition
|
||||
|
||||
#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_CODEC_DATABASE_TEST_H_
|
||||
183
modules/video_coding/main/test/decode_from_storage_test.cc
Normal file
183
modules/video_coding/main/test/decode_from_storage_test.cc
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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 "receiver_tests.h"
|
||||
#include "video_coding.h"
|
||||
#include "rtp_rtcp.h"
|
||||
#include "trace.h"
|
||||
#include "tick_time.h"
|
||||
#include "../source/event.h"
|
||||
#include "test_macros.h"
|
||||
#include "rtp_player.h"
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
class FrameStorageCallback : public VCMFrameStorageCallback
|
||||
{
|
||||
public:
|
||||
FrameStorageCallback(VideoCodingModule* vcm) : _vcm(vcm) {}
|
||||
|
||||
WebRtc_Word32 StoreReceivedFrame(const EncodedVideoData& frameToStore)
|
||||
{
|
||||
_vcm->DecodeFromStorage(frameToStore);
|
||||
return VCM_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
VideoCodingModule* _vcm;
|
||||
};
|
||||
|
||||
int DecodeFromStorageTest(CmdArgs& args)
|
||||
{
|
||||
// Make sure this test isn't executed without simulated clocks
|
||||
#if !defined(TICK_TIME_DEBUG) || !defined(EVENT_DEBUG)
|
||||
return -1;
|
||||
#endif
|
||||
// BEGIN Settings
|
||||
|
||||
bool protectionEnabled = false;
|
||||
VCMVideoProtection protectionMethod = kProtectionNack;
|
||||
WebRtc_UWord32 rttMS = 100;
|
||||
float lossRate = 0.00f;
|
||||
bool reordering = false;
|
||||
WebRtc_UWord32 renderDelayMs = 0;
|
||||
WebRtc_UWord32 minPlayoutDelayMs = 0;
|
||||
const WebRtc_Word64 MAX_RUNTIME_MS = -1;
|
||||
std::string rtpFilename = args.inputFile;
|
||||
std::string outFilename = args.outputFile;
|
||||
if (outFilename == "")
|
||||
outFilename = "DecodeFromStorage.yuv";
|
||||
|
||||
FrameReceiveCallback receiveCallback(outFilename.c_str());
|
||||
|
||||
// END Settings
|
||||
|
||||
Trace::CreateTrace();
|
||||
Trace::SetTraceFile("decodeFromStorageTestTrace.txt");
|
||||
Trace::SetLevelFilter(webrtc::kTraceAll);
|
||||
|
||||
|
||||
VideoCodingModule* vcm = VideoCodingModule::Create(1);
|
||||
VideoCodingModule* vcmPlayback = VideoCodingModule::Create(2);
|
||||
FrameStorageCallback storageCallback(vcmPlayback);
|
||||
RtpDataCallback dataCallback(vcm);
|
||||
WebRtc_Word32 ret = vcm->InitializeReceiver();
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
ret = vcmPlayback->InitializeReceiver();
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
vcm->RegisterFrameStorageCallback(&storageCallback);
|
||||
vcmPlayback->RegisterReceiveCallback(&receiveCallback);
|
||||
RTPPlayer rtpStream(rtpFilename.c_str(), &dataCallback);
|
||||
ListWrapper payloadTypes;
|
||||
payloadTypes.PushFront(new PayloadCodecTuple(VCM_VP8_PAYLOAD_TYPE, "VP8", kVideoCodecVP8));
|
||||
|
||||
// Register receive codecs in VCM
|
||||
ListItem* item = payloadTypes.First();
|
||||
while (item != NULL)
|
||||
{
|
||||
PayloadCodecTuple* payloadType = static_cast<PayloadCodecTuple*>(item->GetItem());
|
||||
if (payloadType != NULL)
|
||||
{
|
||||
VideoCodec codec;
|
||||
memset(&codec, 0, sizeof(codec));
|
||||
strncpy(codec.plName, payloadType->name.c_str(), payloadType->name.length());
|
||||
codec.plName[payloadType->name.length()] = '\0';
|
||||
codec.plType = payloadType->payloadType;
|
||||
codec.codecType = payloadType->codecType;
|
||||
if (vcm->RegisterReceiveCodec(&codec, 1) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (vcmPlayback->RegisterReceiveCodec(&codec, 1) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
item = payloadTypes.Next(item);
|
||||
}
|
||||
if (rtpStream.Initialize(payloadTypes) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
bool nackEnabled = protectionEnabled && (protectionMethod == kProtectionNack ||
|
||||
protectionMethod == kProtectionDualDecoder);
|
||||
rtpStream.SimulatePacketLoss(lossRate, nackEnabled, rttMS);
|
||||
rtpStream.SetReordering(reordering);
|
||||
vcm->SetChannelParameters(0, 0, rttMS);
|
||||
vcm->SetVideoProtection(protectionMethod, protectionEnabled);
|
||||
vcm->SetRenderDelay(renderDelayMs);
|
||||
vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs);
|
||||
|
||||
ret = 0;
|
||||
|
||||
// RTP stream main loop
|
||||
while ((ret = rtpStream.NextPacket(VCMTickTime::MillisecondTimestamp())) == 0)
|
||||
{
|
||||
if (VCMTickTime::MillisecondTimestamp() % 5 == 0)
|
||||
{
|
||||
ret = vcm->Decode();
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (vcm->TimeUntilNextProcess() <= 0)
|
||||
{
|
||||
vcm->Process();
|
||||
}
|
||||
if (MAX_RUNTIME_MS > -1 && VCMTickTime::MillisecondTimestamp() >= MAX_RUNTIME_MS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
VCMTickTime::IncrementDebugClock();
|
||||
}
|
||||
|
||||
switch (ret)
|
||||
{
|
||||
case 1:
|
||||
printf("Success\n");
|
||||
break;
|
||||
case -1:
|
||||
printf("Failed\n");
|
||||
break;
|
||||
case 0:
|
||||
printf("Timeout\n");
|
||||
break;
|
||||
}
|
||||
|
||||
rtpStream.Print();
|
||||
|
||||
// Tear down
|
||||
item = payloadTypes.First();
|
||||
while (item != NULL)
|
||||
{
|
||||
PayloadCodecTuple* payloadType = static_cast<PayloadCodecTuple*>(item->GetItem());
|
||||
if (payloadType != NULL)
|
||||
{
|
||||
delete payloadType;
|
||||
}
|
||||
ListItem* itemToRemove = item;
|
||||
item = payloadTypes.Next(item);
|
||||
payloadTypes.Erase(itemToRemove);
|
||||
}
|
||||
VideoCodingModule::Destroy(vcm);
|
||||
vcm = NULL;
|
||||
VideoCodingModule::Destroy(vcmPlayback);
|
||||
vcmPlayback = NULL;
|
||||
Trace::ReturnTrace();
|
||||
|
||||
return 0;
|
||||
}
|
||||
584
modules/video_coding/main/test/generic_codec_test.cc
Normal file
584
modules/video_coding/main/test/generic_codec_test.cc
Normal file
@@ -0,0 +1,584 @@
|
||||
/*
|
||||
* 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 <cmath>
|
||||
#include <stdio.h>
|
||||
#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<float>(_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<float>(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<WebRtc_UWord32>(_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<float>(_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<float>(_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<float>(_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<WebRtc_UWord8>(_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<WebRtc_UWord32>(_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<float>(_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<WebRtc_UWord32>(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<float>(_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);
|
||||
}
|
||||
100
modules/video_coding/main/test/generic_codec_test.h
Normal file
100
modules/video_coding/main/test/generic_codec_test.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_GENERIC_CODEC_TEST_H_
|
||||
#define WEBRTC_MODULES_VIDEO_CODING_TEST_GENERIC_CODEC_TEST_H_
|
||||
|
||||
#include "video_coding.h"
|
||||
#include "test_macros.h"
|
||||
#include "test_util.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <fstream>
|
||||
|
||||
/*
|
||||
Test consists of:
|
||||
1. Sanity checks
|
||||
2. Bit rate validation
|
||||
3. Encoder control test / General API functionality
|
||||
4. Decoder control test / General API functionality
|
||||
|
||||
*/
|
||||
int VCMGenericCodecTest(CmdArgs& args);
|
||||
|
||||
class GenericCodecTest
|
||||
{
|
||||
public:
|
||||
GenericCodecTest(webrtc::VideoCodingModule* vcm);
|
||||
~GenericCodecTest();
|
||||
static int RunTest(CmdArgs& args);
|
||||
WebRtc_Word32 Perform(CmdArgs& args);
|
||||
float WaitForEncodedFrame() const;
|
||||
|
||||
private:
|
||||
void Setup(CmdArgs& args);
|
||||
void Print();
|
||||
WebRtc_Word32 TearDown();
|
||||
void IncrementDebugClock(float frameRate);
|
||||
|
||||
webrtc::VideoCodingModule* _vcm;
|
||||
webrtc::VideoCodec _sendCodec;
|
||||
webrtc::VideoCodec _receiveCodec;
|
||||
std::string _inname;
|
||||
std::string _outname;
|
||||
std::string _encodedName;
|
||||
WebRtc_Word32 _sumEncBytes;
|
||||
FILE* _sourceFile;
|
||||
FILE* _decodedFile;
|
||||
FILE* _encodedFile;
|
||||
WebRtc_UWord16 _width;
|
||||
WebRtc_UWord16 _height;
|
||||
float _frameRate;
|
||||
WebRtc_UWord32 _lengthSourceFrame;
|
||||
WebRtc_UWord32 _timeStamp;
|
||||
int vcmMacrosTests;
|
||||
int vcmMacrosErrors;
|
||||
VCMDecodeCompleteCallback* _decodeCallback;
|
||||
VCMEncodeCompleteCallback* _encodeCompleteCallback;
|
||||
|
||||
}; // end of GenericCodecTest class definition
|
||||
|
||||
class RTPSendCallback_SizeTest : public webrtc::Transport
|
||||
{
|
||||
public:
|
||||
// constructor input: (receive side) rtp module to send encoded data to
|
||||
RTPSendCallback_SizeTest() : _maxPayloadSize(0), _payloadSizeSum(0), _nPackets(0) {}
|
||||
virtual int SendPacket(int channel, const void *data, int len);
|
||||
virtual int SendRTCPPacket(int channel, const void *data, int len) {return 0;}
|
||||
void SetMaxPayloadSize(WebRtc_UWord32 maxPayloadSize);
|
||||
void Reset();
|
||||
float AveragePayloadSize() const;
|
||||
private:
|
||||
WebRtc_UWord32 _maxPayloadSize;
|
||||
WebRtc_UWord32 _payloadSizeSum;
|
||||
WebRtc_UWord32 _nPackets;
|
||||
};
|
||||
|
||||
class VCMEncComplete_KeyReqTest : public webrtc::VCMPacketizationCallback
|
||||
{
|
||||
public:
|
||||
VCMEncComplete_KeyReqTest(webrtc::VideoCodingModule &vcm) : _vcm(vcm), _seqNo(0), _timeStamp(0) {}
|
||||
WebRtc_Word32 SendData(const webrtc::FrameType frameType,
|
||||
const WebRtc_UWord8 payloadType,
|
||||
const WebRtc_UWord32 timeStamp,
|
||||
const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord32 payloadSize,
|
||||
const webrtc::RTPFragmentationHeader& fragmentationHeader);
|
||||
private:
|
||||
webrtc::VideoCodingModule& _vcm;
|
||||
WebRtc_UWord16 _seqNo;
|
||||
WebRtc_UWord32 _timeStamp;
|
||||
}; // end of VCMEncodeCompleteCallback
|
||||
|
||||
#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_GENERIC_CODEC_TEST_H_
|
||||
2156
modules/video_coding/main/test/jitter_buffer_test.cc
Normal file
2156
modules/video_coding/main/test/jitter_buffer_test.cc
Normal file
File diff suppressed because it is too large
Load Diff
110
modules/video_coding/main/test/jitter_estimate_test.cc
Normal file
110
modules/video_coding/main/test/jitter_estimate_test.cc
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <ctime>
|
||||
#include "JitterEstimateTest.h"
|
||||
#include "tick_time.h"
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
JitterEstimateTest::JitterEstimateTest(unsigned int frameRate) :
|
||||
_frameRate(frameRate),
|
||||
_capacity(2000),
|
||||
_rate(500),
|
||||
_jitter(5, 0),
|
||||
_keyFrameRate(1.0),
|
||||
_deltaFrameSize(10000, 1e6),
|
||||
_counter(0),
|
||||
_lossrate(0.0)
|
||||
{
|
||||
// Assign to random value between 0 and max of unsigned int
|
||||
_seed = static_cast<unsigned>(std::time(0));
|
||||
std::srand(_seed);
|
||||
_prevTimestamp = static_cast<unsigned int>((std::rand() + 1.0)/(RAND_MAX + 1.0)*(pow((float) 2, (long) sizeof(unsigned int)*8)-1));
|
||||
_prevWallClock = VCMTickTime::MillisecondTimestamp();
|
||||
}
|
||||
|
||||
FrameSample
|
||||
JitterEstimateTest::GenerateFrameSample()
|
||||
{
|
||||
double increment = 1.0/_frameRate;
|
||||
unsigned int frameSize = static_cast<unsigned int>(_deltaFrameSize.RandValue());
|
||||
bool keyFrame = false;
|
||||
bool resent = false;
|
||||
_prevTimestamp += static_cast<unsigned int>(90000*increment + 0.5);
|
||||
double deltaFrameRate = _frameRate - _keyFrameRate;
|
||||
double ratio = deltaFrameRate/static_cast<double>(_keyFrameRate);
|
||||
if (ratio < 1.0)
|
||||
{
|
||||
ratio = 1.0/ratio;
|
||||
if (_counter >= ratio)
|
||||
_counter = 0;
|
||||
else
|
||||
{
|
||||
_counter++;
|
||||
frameSize += static_cast<unsigned int>(3*_deltaFrameSize.GetAverage());
|
||||
keyFrame = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_counter >= ratio)
|
||||
{
|
||||
frameSize += static_cast<unsigned int>(3*_deltaFrameSize.GetAverage());
|
||||
_counter = 0;
|
||||
keyFrame = true;
|
||||
}
|
||||
else
|
||||
_counter++;
|
||||
}
|
||||
WebRtc_Word64 jitter = static_cast<WebRtc_Word64>(_jitter.RandValue() + 1.0/_capacity * frameSize + 0.5);
|
||||
_prevWallClock += static_cast<WebRtc_Word64>(1000*increment + 0.5);
|
||||
double rndValue = RandUniform();
|
||||
resent = (rndValue < _lossrate);
|
||||
//printf("rndValue = %f\n", rndValue);
|
||||
return FrameSample(_prevTimestamp, _prevWallClock + jitter, frameSize, keyFrame, resent);
|
||||
}
|
||||
|
||||
void
|
||||
JitterEstimateTest::SetCapacity(unsigned int c)
|
||||
{
|
||||
_capacity = c;
|
||||
}
|
||||
|
||||
void
|
||||
JitterEstimateTest::SetRate(unsigned int r)
|
||||
{
|
||||
_rate = r;
|
||||
}
|
||||
|
||||
void
|
||||
JitterEstimateTest::SetJitter(double m, double v)
|
||||
{
|
||||
_jitter.SetParams(m, v);
|
||||
}
|
||||
|
||||
void
|
||||
JitterEstimateTest::SetFrameSizeStats(double m, double v)
|
||||
{
|
||||
_deltaFrameSize.SetParams(m, v);
|
||||
}
|
||||
|
||||
void
|
||||
JitterEstimateTest::SetKeyFrameRate(int rate)
|
||||
{
|
||||
_keyFrameRate = rate;
|
||||
}
|
||||
|
||||
void
|
||||
JitterEstimateTest::SetLossRate(double rate)
|
||||
{
|
||||
_lossrate = rate;
|
||||
}
|
||||
105
modules/video_coding/main/test/jitter_estimate_test.h
Normal file
105
modules/video_coding/main/test/jitter_estimate_test.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_JITTER_ESTIMATE_TEST_H_
|
||||
#define WEBRTC_MODULES_VIDEO_CODING_TEST_JITTER_ESTIMATE_TEST_H_
|
||||
|
||||
#include "typedefs.h"
|
||||
#include "jitter_buffer.h"
|
||||
#include "jitter_estimator.h"
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
|
||||
double const pi = 4*std::atan(1.0);
|
||||
|
||||
class GaussDist
|
||||
{
|
||||
public:
|
||||
GaussDist(double m, double v): _mu(m), _sigma(sqrt(v)) {}
|
||||
|
||||
double RandValue() // returns a single normally distributed number
|
||||
{
|
||||
double r1 = (std::rand() + 1.0)/(RAND_MAX + 1.0); // gives equal distribution in (0, 1]
|
||||
double r2 = (std::rand() + 1.0)/(RAND_MAX + 1.0);
|
||||
return _mu + _sigma * std::sqrt(-2*std::log(r1))*std::cos(2*pi*r2);
|
||||
}
|
||||
|
||||
double GetAverage()
|
||||
{
|
||||
return _mu;
|
||||
}
|
||||
|
||||
double GetVariance()
|
||||
{
|
||||
return _sigma*_sigma;
|
||||
}
|
||||
|
||||
void SetParams(double m, double v)
|
||||
{
|
||||
_mu = m;
|
||||
_sigma = sqrt(v);
|
||||
}
|
||||
|
||||
private:
|
||||
double _mu, _sigma;
|
||||
};
|
||||
|
||||
class JitterEstimateTestWrapper : public webrtc::VCMJitterEstimator
|
||||
{
|
||||
public:
|
||||
JitterEstimateTestWrapper() : VCMJitterEstimator() {}
|
||||
double GetTheta() { return _theta[0]; }
|
||||
double GetVarNoise() { return _varNoise; }
|
||||
};
|
||||
|
||||
class FrameSample
|
||||
{
|
||||
public:
|
||||
FrameSample() {FrameSample(0, 0, 0, false, false);}
|
||||
FrameSample(unsigned int ts, WebRtc_Word64 wallClk, unsigned int fs, bool _keyFrame, bool _resent):
|
||||
timestamp90Khz(ts), wallClockMs(wallClk), frameSize(fs), keyFrame(_keyFrame), resent(_resent) {}
|
||||
|
||||
unsigned int timestamp90Khz;
|
||||
WebRtc_Word64 wallClockMs;
|
||||
unsigned int frameSize;
|
||||
bool keyFrame;
|
||||
bool resent;
|
||||
};
|
||||
|
||||
class JitterEstimateTest
|
||||
{
|
||||
public:
|
||||
JitterEstimateTest(unsigned int frameRate);
|
||||
FrameSample GenerateFrameSample();
|
||||
void SetCapacity(unsigned int c);
|
||||
void SetRate(unsigned int r);
|
||||
void SetJitter(double m, double v);
|
||||
void SetFrameSizeStats(double m, double v);
|
||||
void SetKeyFrameRate(int rate);
|
||||
void SetLossRate(double rate);
|
||||
|
||||
private:
|
||||
double RandUniform() { return (std::rand() + 1.0)/(RAND_MAX + 1.0); }
|
||||
unsigned int _frameRate;
|
||||
unsigned int _capacity;
|
||||
unsigned int _rate;
|
||||
GaussDist _jitter;
|
||||
//GaussDist _noResend;
|
||||
GaussDist _deltaFrameSize;
|
||||
unsigned int _prevTimestamp;
|
||||
WebRtc_Word64 _prevWallClock;
|
||||
unsigned int _nextDelay;
|
||||
double _keyFrameRate;
|
||||
unsigned int _counter;
|
||||
unsigned int _seed;
|
||||
double _lossrate;
|
||||
};
|
||||
|
||||
#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_JITTER_ESTIMATE_TEST_H_
|
||||
614
modules/video_coding/main/test/media_opt_test.cc
Normal file
614
modules/video_coding/main/test/media_opt_test.cc
Normal file
@@ -0,0 +1,614 @@
|
||||
/*
|
||||
* 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 Media Optimization Test
|
||||
// testing is done via the VCM module, no specific Media opt functionality.
|
||||
|
||||
#include "receiver_tests.h" // receive side callbacks
|
||||
#include "video_coding.h"
|
||||
#include "rtp_rtcp.h"
|
||||
#include "test_util.h" // send side callback
|
||||
#include "media_opt_test.h"
|
||||
#include "../source/event.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
//#include <Windows.h>
|
||||
#include <time.h>
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
int MediaOptTest::RunTest(int testNum, CmdArgs& args)
|
||||
{
|
||||
Trace::CreateTrace();
|
||||
Trace::SetTraceFile("mediaOptTestTrace.txt");
|
||||
Trace::SetLevelFilter(webrtc::kTraceAll);
|
||||
VideoCodingModule* vcm = VideoCodingModule::Create(1);
|
||||
MediaOptTest* mot = new MediaOptTest(vcm);
|
||||
if (testNum == 0)
|
||||
{ // regular
|
||||
mot->Setup(0, args);
|
||||
mot->GeneralSetup();
|
||||
mot->Perform();
|
||||
mot->Print(1);// print to screen
|
||||
mot->TearDown();
|
||||
}
|
||||
if (testNum == 1)
|
||||
{ // release test
|
||||
mot->Setup(0, args);
|
||||
mot->RTTest();
|
||||
}
|
||||
if (testNum == 2)
|
||||
{ // release test, running from script
|
||||
mot->Setup(1, args);
|
||||
mot->GeneralSetup();
|
||||
mot->Perform();
|
||||
mot->Print(1);// print to screen
|
||||
mot->TearDown();
|
||||
}
|
||||
|
||||
VideoCodingModule::Destroy(vcm);
|
||||
delete mot;
|
||||
Trace::ReturnTrace();
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
MediaOptTest::MediaOptTest(VideoCodingModule* vcm):
|
||||
_vcm(vcm),
|
||||
_width(0),
|
||||
_height(0),
|
||||
_lengthSourceFrame(0),
|
||||
_timeStamp(0),
|
||||
_frameRate(30.0f),
|
||||
_nackEnabled(false),
|
||||
_fecEnabled(false),
|
||||
_rttMS(0),
|
||||
_renderDelayMs(0),
|
||||
_bitRate(300.0f),
|
||||
_lossRate(0.0f),
|
||||
_frameCnt(0),
|
||||
_sumEncBytes(0),
|
||||
_numFramesDropped(0),
|
||||
_numberOfCores(4),
|
||||
vcmMacrosTests(0),
|
||||
vcmMacrosErrors(0)
|
||||
{
|
||||
_rtp = RtpRtcp::CreateRtpRtcp(1, false);
|
||||
}
|
||||
|
||||
MediaOptTest::~MediaOptTest()
|
||||
{
|
||||
RtpRtcp::DestroyRtpRtcp(_rtp);
|
||||
}
|
||||
void
|
||||
MediaOptTest::Setup(int testType, CmdArgs& args)
|
||||
{
|
||||
/*TEST USER SETTINGS*/
|
||||
// test parameters
|
||||
_inname = args.inputFile;
|
||||
if (args.outputFile == "")
|
||||
_outname = "../MOTest_out.vp8";
|
||||
else
|
||||
_outname = args.outputFile;
|
||||
_actualSourcename = "../MOTestSource.yuv"; // actual source after frame dropping
|
||||
_codecName = args.codecName;
|
||||
_sendCodecType = args.codecType;
|
||||
_width = args.width;
|
||||
_height = args.height;
|
||||
_frameRate = args.frameRate;
|
||||
_bitRate = args.bitRate;
|
||||
_numberOfCores = 4;
|
||||
|
||||
// error resilience
|
||||
_nackEnabled = false;
|
||||
_fecEnabled = true;
|
||||
_nackFecEnabled = false;
|
||||
|
||||
_rttMS = 100;
|
||||
_lossRate = 0.00*255; // no packet loss
|
||||
|
||||
_testType = testType;
|
||||
|
||||
//For multiple runs with script
|
||||
if (_testType == 1)
|
||||
{
|
||||
float rateTest,lossTest;
|
||||
int numRuns;
|
||||
_fpinp = fopen("dat_inp","rb");
|
||||
_fpout = fopen("test_runs/dat_out","ab");
|
||||
_fpout2 = fopen("test_runs/dat_out2","ab");
|
||||
fscanf(_fpinp,"%f %f %d \n",&rateTest,&lossTest,&numRuns);
|
||||
_bitRate = rateTest;
|
||||
_lossRate = lossTest;
|
||||
_testNum = 0;
|
||||
|
||||
// for bit rates: 500, 1000, 2000, 3000,4000
|
||||
// for loss rates: 0, 1, 3, 5, 10%
|
||||
_numParRuns = 25;
|
||||
|
||||
_testNum = numRuns + 1;
|
||||
if (rateTest == 0.0) _lossRate = 0.0;
|
||||
else
|
||||
{
|
||||
if (rateTest == 4000) //final bit rate
|
||||
{
|
||||
if (lossTest == 0.1*255) _lossRate = 0.0; //start at 1%
|
||||
else
|
||||
if (lossTest == 0.05*255) _lossRate = 0.1*255; //final loss rate
|
||||
else
|
||||
if (lossTest == 0.0) _lossRate = 0.01*255;
|
||||
else _lossRate = lossTest + 0.02*255;
|
||||
}
|
||||
}
|
||||
|
||||
if (rateTest == 0.0 || rateTest == 4000) _bitRate = 500; //starting bit rate
|
||||
else
|
||||
if (rateTest == 500) _bitRate = 1000;
|
||||
else _bitRate = rateTest + 1000;
|
||||
}
|
||||
//
|
||||
|
||||
_renderDelayMs = 0;
|
||||
WebRtc_UWord32 minPlayoutDelayMs = 0;
|
||||
/* test settings end*/
|
||||
|
||||
_lengthSourceFrame = 3*_width*_height/2;
|
||||
_log.open("../VCM_MediaOptLog.txt", std::fstream::out | std::fstream::app);
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
MediaOptTest::GeneralSetup()
|
||||
{
|
||||
|
||||
WebRtc_UWord8 deltaFECRate = 0;
|
||||
WebRtc_UWord8 keyFECRate = 0;
|
||||
WebRtc_UWord32 minPlayoutDelayMs = 0;
|
||||
|
||||
if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL)
|
||||
{
|
||||
printf("Cannot read file %s.\n", _inname.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((_decodedFile = fopen(_outname.c_str(), "wb")) == NULL)
|
||||
{
|
||||
printf("Cannot read file %s.\n", _outname.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((_actualSourceFile = fopen(_actualSourcename.c_str(), "wb")) == NULL)
|
||||
{
|
||||
printf("Cannot read file %s.\n", _actualSourcename.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (_rtp->InitReceiver() < 0)
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
if (_rtp->InitSender() < 0)
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
if (_vcm->InitializeReceiver() < 0)
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
if (_vcm->InitializeSender())
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Registering codecs for the RTP module
|
||||
|
||||
// Register receive payload
|
||||
_rtp->RegisterReceivePayload("VP8", VCM_VP8_PAYLOAD_TYPE);
|
||||
_rtp->RegisterReceivePayload("ULPFEC", VCM_ULPFEC_PAYLOAD_TYPE);
|
||||
_rtp->RegisterReceivePayload("RED", VCM_RED_PAYLOAD_TYPE);
|
||||
|
||||
// Register send payload
|
||||
_rtp->RegisterSendPayload("VP8", VCM_VP8_PAYLOAD_TYPE);
|
||||
_rtp->RegisterSendPayload("ULPFEC", VCM_ULPFEC_PAYLOAD_TYPE);
|
||||
_rtp->RegisterSendPayload("RED", VCM_RED_PAYLOAD_TYPE);
|
||||
|
||||
if (_nackFecEnabled == 1)
|
||||
_rtp->SetGenericFECStatus(_nackFecEnabled, VCM_RED_PAYLOAD_TYPE,
|
||||
VCM_ULPFEC_PAYLOAD_TYPE);
|
||||
else
|
||||
_rtp->SetGenericFECStatus(_fecEnabled, VCM_RED_PAYLOAD_TYPE,
|
||||
VCM_ULPFEC_PAYLOAD_TYPE);
|
||||
|
||||
// VCM: Registering codecs
|
||||
VideoCodec sendCodec;
|
||||
_vcm->InitializeSender();
|
||||
_vcm->InitializeReceiver();
|
||||
WebRtc_Word32 numberOfCodecs = _vcm->NumberOfCodecs();
|
||||
if (numberOfCodecs < 1)
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
|
||||
WebRtc_UWord8 i= 0;
|
||||
if (_vcm->Codec(_sendCodecType, &sendCodec) != 0)
|
||||
{
|
||||
printf("Unknown codec\n");
|
||||
exit(1);
|
||||
}
|
||||
// register codec
|
||||
sendCodec.startBitrate = (int) _bitRate;
|
||||
sendCodec.height = _height;
|
||||
sendCodec.width = _width;
|
||||
sendCodec.maxFramerate = (WebRtc_UWord8)_frameRate;
|
||||
_vcm->RegisterSendCodec(&sendCodec, _numberOfCores, 1440);
|
||||
_vcm->RegisterReceiveCodec(&sendCodec, _numberOfCores); // same settings for encode and decode
|
||||
|
||||
_vcm->SetRenderDelay(_renderDelayMs);
|
||||
_vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs);
|
||||
|
||||
return;
|
||||
}
|
||||
// The following test shall be conducted under release tests
|
||||
|
||||
|
||||
|
||||
WebRtc_Word32
|
||||
MediaOptTest::Perform()
|
||||
{
|
||||
//Setup();
|
||||
EventWrapper* waitEvent = EventWrapper::Create();
|
||||
|
||||
// callback settings
|
||||
VCMRTPEncodeCompleteCallback* encodeCompleteCallback = new VCMRTPEncodeCompleteCallback(_rtp);
|
||||
_vcm->RegisterTransportCallback(encodeCompleteCallback);
|
||||
encodeCompleteCallback->SetCodecType(ConvertCodecType(_codecName.c_str()));
|
||||
encodeCompleteCallback->SetFrameDimensions(_width, _height);
|
||||
// frame ready to be sent to network
|
||||
RTPSendCompleteCallback* outgoingTransport = new RTPSendCompleteCallback(_rtp);
|
||||
_rtp->RegisterSendTransport(outgoingTransport);
|
||||
//FrameReceiveCallback
|
||||
VCMDecodeCompleteCallback receiveCallback(_decodedFile);
|
||||
RtpDataCallback dataCallback(_vcm);
|
||||
_rtp->RegisterIncomingDataCallback(&dataCallback);
|
||||
|
||||
VCMTestProtectionCallback protectionCallback;
|
||||
_vcm->RegisterProtectionCallback(&protectionCallback);
|
||||
|
||||
// set error resilience / test parameters:
|
||||
outgoingTransport->SetLossPct(_lossRate);
|
||||
if (_nackFecEnabled == 1)
|
||||
_vcm->SetVideoProtection(kProtectionNackFEC, _nackFecEnabled);
|
||||
else
|
||||
{
|
||||
_vcm->SetVideoProtection(kProtectionNack, _nackEnabled);
|
||||
_vcm->SetVideoProtection(kProtectionFEC, _fecEnabled);
|
||||
}
|
||||
|
||||
// START TEST
|
||||
VideoFrame sourceFrame;
|
||||
sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
|
||||
WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame];
|
||||
_vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, (WebRtc_UWord8)_lossRate, _rttMS);
|
||||
_vcm->RegisterReceiveCallback(&receiveCallback);
|
||||
|
||||
// inform RTP Module of error resilience features
|
||||
_rtp->SetFECCodeRate(protectionCallback.FECKeyRate(),protectionCallback.FECDeltaRate());
|
||||
_rtp->SetNACKStatus(protectionCallback.NACKMethod());
|
||||
|
||||
_frameCnt = 0;
|
||||
_sumEncBytes = 0.0;
|
||||
_numFramesDropped = 0;
|
||||
|
||||
while (feof(_sourceFile)== 0)
|
||||
{
|
||||
fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile);
|
||||
_frameCnt++;
|
||||
|
||||
sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer);
|
||||
sourceFrame.SetHeight(_height);
|
||||
sourceFrame.SetWidth(_width);
|
||||
_timeStamp += (WebRtc_UWord32)(9e4 / static_cast<float>(_frameRate));
|
||||
sourceFrame.SetTimeStamp(_timeStamp);
|
||||
TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
|
||||
// inform RTP Module of error resilience features
|
||||
//_rtp->SetFECCodeRate(protectionCallback.FECKeyRate(),protectionCallback.FECDeltaRate());
|
||||
//_rtp->SetNACKStatus(protectionCallback.NACKMethod());
|
||||
|
||||
WebRtc_Word32 ret = _vcm->Decode();
|
||||
if (ret < 0 )
|
||||
{
|
||||
TEST(ret == 0);
|
||||
printf ("Decode error in frame # %d",_frameCnt);
|
||||
}
|
||||
|
||||
float encBytes = encodeCompleteCallback->EncodedBytes();
|
||||
if (encBytes == 0)
|
||||
{
|
||||
_numFramesDropped += 1;
|
||||
//printf("frame #%d dropped \n", _frameCnt );
|
||||
}
|
||||
else
|
||||
{
|
||||
// write frame to file
|
||||
fwrite(sourceFrame.Buffer(), 1, sourceFrame.Length(), _actualSourceFile);
|
||||
}
|
||||
|
||||
_sumEncBytes += encBytes;
|
||||
//waitEvent->Wait(33);
|
||||
}
|
||||
|
||||
//END TEST
|
||||
delete waitEvent;
|
||||
delete encodeCompleteCallback;
|
||||
delete outgoingTransport;
|
||||
delete tmpBuffer;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
MediaOptTest::RTTest()
|
||||
{
|
||||
// will only calculate PSNR - not create output files for all
|
||||
// SET UP
|
||||
// Set bit rates
|
||||
const float bitRateVec[] = {500, 1000, 2000,3000, 4000};
|
||||
//const float bitRateVec[] = {1000};
|
||||
// Set Packet loss values ([0,255])
|
||||
const double lossPctVec[] = {0.0*255, 0.0*255, 0.01*255, 0.01*255, 0.03*255, 0.03*255, 0.05*255, 0.05*255, 0.1*255, 0.1*255};
|
||||
const bool nackEnabledVec[] = {false , false, false, false, false, false, false, false , false, false};
|
||||
const bool fecEnabledVec[] = {false , true, false, true , false, true , false, true , false, true};
|
||||
// fec and nack are set according to the packet loss values
|
||||
|
||||
const float nBitrates = sizeof(bitRateVec)/sizeof(*bitRateVec);
|
||||
const float nlossPct = sizeof(lossPctVec)/sizeof(*lossPctVec);
|
||||
|
||||
std::vector<const VideoSource*> sources;
|
||||
std::vector<const VideoSource*>::iterator it;
|
||||
|
||||
sources.push_back(new const VideoSource(_inname, _width, _height));
|
||||
int numOfSrc = 1;
|
||||
|
||||
// constant settings (valid for entire run time)
|
||||
_rttMS = 20;
|
||||
_renderDelayMs = 0;
|
||||
WebRtc_UWord32 minPlayoutDelayMs = 0;
|
||||
|
||||
_outname = "../RTMOTest_out.yuv"; // same out name for all
|
||||
_actualSourcename = "../RTMOTestSource.yuv"; // actual source after frame dropping
|
||||
|
||||
_codecName = "VP8"; // for now just this one - later iterate over all codec types
|
||||
_log.open("../VCM_RTMediaOptLog.txt", std::fstream::out | std::fstream::app);
|
||||
_outputRes=fopen("../VCM_MediaOpt","ab");
|
||||
|
||||
//char filename[128];
|
||||
/* test settings end*/
|
||||
|
||||
// START TEST
|
||||
// iterate over test sequences
|
||||
printf("\n****START TEST OVER ALL RUNS ****\n");
|
||||
int runCnt = 0;
|
||||
for (it = sources.begin() ; it < sources.end(); it++)
|
||||
{
|
||||
|
||||
// test set up
|
||||
_inname = (*it)->GetFileName();
|
||||
_width = (*it)->GetWidth();
|
||||
_height = (*it)->GetHeight();
|
||||
_lengthSourceFrame = 3*_width*_height/2;
|
||||
_frameRate = (*it)->GetFrameRate();
|
||||
//GeneralSetup();
|
||||
|
||||
|
||||
// iterate over all bit rates
|
||||
for (int i = 0; i < nBitrates; i++)
|
||||
{
|
||||
_bitRate = static_cast<float>(bitRateVec[i]);
|
||||
// iterate over all packet loss values
|
||||
for (int j = 0; j < nlossPct; j++)
|
||||
{
|
||||
_lossRate = static_cast<float>(lossPctVec[j]);
|
||||
_nackEnabled = static_cast<bool>(nackEnabledVec[j]);
|
||||
_fecEnabled = static_cast<bool>(fecEnabledVec[j]);
|
||||
|
||||
runCnt++;
|
||||
printf("run #%d out of %d \n", runCnt,(int)(nlossPct*nBitrates*numOfSrc));
|
||||
|
||||
//printf("**FOR RUN: **%d %d %d %d \n",_nackEnabled,_fecEnabled,int(lossPctVec[j]),int(_bitRate));
|
||||
|
||||
/*
|
||||
int ch = sprintf(filename,"../test_mediaOpt/RTMOTest_%d_%d_%d_%d.yuv",_nackEnabled,_fecEnabled,int(lossPctVec[j]),int(_bitRate));
|
||||
_outname = filename;
|
||||
|
||||
printf("**FOR RUN: **%d %d %d %d \n",_nackEnabled,_fecEnabled,int(lossPctVec[j]),int(_bitRate));
|
||||
*/
|
||||
if (_rtp != NULL)
|
||||
{
|
||||
RtpRtcp::DestroyRtpRtcp(_rtp);
|
||||
}
|
||||
_rtp = RtpRtcp::CreateRtpRtcp(1, false);
|
||||
GeneralSetup();
|
||||
Perform();
|
||||
Print(1);
|
||||
TearDown();
|
||||
RtpRtcp::DestroyRtpRtcp(_rtp);
|
||||
_rtp = NULL;
|
||||
|
||||
printf("\n");
|
||||
//printf("**DONE WITH RUN: **%d %d %f %d \n",_nackEnabled,_fecEnabled,lossPctVec[j],int(_bitRate));
|
||||
//
|
||||
|
||||
}// end of packet loss loop
|
||||
}// end of bit rate loop
|
||||
delete *it;
|
||||
}// end of video sequence loop
|
||||
// at end of sequence
|
||||
fclose(_outputRes);
|
||||
printf("\nVCM Media Optimization Test: \n\n%i tests completed\n", vcmMacrosTests);
|
||||
if (vcmMacrosErrors > 0)
|
||||
{
|
||||
printf("%i FAILED\n\n", vcmMacrosErrors);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("ALL PASSED\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MediaOptTest::Print(int mode)
|
||||
{
|
||||
double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _frameRate));
|
||||
double actualBitRate = ActualBitRate / 1000.0;
|
||||
double psnr;
|
||||
PSNRfromFiles(_actualSourcename.c_str(), _outname.c_str(), _width, _height, &psnr);
|
||||
|
||||
(_log) << "VCM: Media Optimization Test Cycle Completed!" << std::endl;
|
||||
(_log) << "Input file: " << _inname << std::endl;
|
||||
(_log) << "Output file:" << _outname << std::endl;
|
||||
( _log) << "Actual bitrate: " << actualBitRate<< " kbps\tTarget: " << _bitRate << " kbps" << std::endl;
|
||||
(_log) << "Error Reslience: NACK:" << _nackEnabled << "; FEC: " << _fecEnabled << std::endl;
|
||||
(_log) << "Packet Loss applied= %f " << _lossRate << std::endl;
|
||||
(_log) << _numFramesDropped << " FRames were dropped" << std::endl;
|
||||
( _log) << "PSNR: " << psnr << std::endl;
|
||||
(_log) << std::endl;
|
||||
|
||||
if (_testType == 2)
|
||||
{
|
||||
fprintf(_outputRes,"************\n");
|
||||
fprintf(_outputRes,"\n\n\n");
|
||||
fprintf(_outputRes,"Actual bitrate: %f kbps\n", actualBitRate);
|
||||
fprintf(_outputRes,"Target bitrate: %f kbps\n", _bitRate);
|
||||
fprintf(_outputRes,"NACK: %s ",(_nackEnabled)?"true":"false");
|
||||
fprintf(_outputRes,"FEC: %s \n ",(_fecEnabled)?"true":"false");
|
||||
fprintf(_outputRes,"Packet loss applied = %f\n", _lossRate);
|
||||
fprintf(_outputRes,"%d frames were dropped, and total number of frames processed %d \n",_numFramesDropped,_frameCnt);
|
||||
fprintf(_outputRes,"PSNR: %f \n", psnr);
|
||||
fprintf(_outputRes,"************\n");
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
if (_testType == 1)
|
||||
{
|
||||
fprintf(_fpout,"************\n");
|
||||
fprintf(_fpout,"\n\n\n");
|
||||
fprintf(_fpout,"Actual bitrate: %f kbps\n", actualBitRate);
|
||||
fprintf(_fpout,"Target bitrate: %f kbps\n", _bitRate);
|
||||
fprintf(_fpout,"NACK: %s ",(_nackEnabled)?"true":"false");
|
||||
fprintf(_fpout,"FEC: %s \n ",(_fecEnabled)?"true":"false");
|
||||
fprintf(_fpout,"Packet loss applied = %f\n", _lossRate);
|
||||
fprintf(_fpout,"%d frames were dropped, and total number of frames processed %d \n",_numFramesDropped,_frameCnt);
|
||||
fprintf(_fpout,"PSNR: %f \n", psnr);
|
||||
fprintf(_fpout,"************\n");
|
||||
|
||||
int testNum1 = _testNum/(_numParRuns +1) + 1;
|
||||
int testNum2 = _testNum%_numParRuns;
|
||||
if (testNum2 == 0) testNum2 = _numParRuns;
|
||||
fprintf(_fpout2,"%d %d %f %f %f %f \n",testNum1,testNum2,_bitRate,actualBitRate,_lossRate,psnr);
|
||||
fclose(_fpinp);
|
||||
_fpinp = fopen("dat_inp","wb");
|
||||
fprintf(_fpinp,"%f %f %d \n",_bitRate,_lossRate,_testNum);
|
||||
}
|
||||
//
|
||||
|
||||
|
||||
if (mode == 1)
|
||||
{
|
||||
// print to screen
|
||||
printf("\n\n\n");
|
||||
printf("Actual bitrate: %f kbps\n", actualBitRate);
|
||||
printf("Target bitrate: %f kbps\n", _bitRate);
|
||||
printf("NACK: %s ",(_nackEnabled)?"true":"false");
|
||||
printf("FEC: %s \n",(_fecEnabled)?"true":"false");
|
||||
printf("Packet loss applied = %f\n", _lossRate);
|
||||
printf("%d frames were dropped, and total number of frames processed %d \n",_numFramesDropped,_frameCnt);
|
||||
printf("PSNR: %f \n", psnr);
|
||||
}
|
||||
TEST(psnr > 10); // low becuase of possible frame dropping (need to verify that OK for all packet loss values/ rates)
|
||||
}
|
||||
|
||||
void
|
||||
MediaOptTest::TearDown()
|
||||
{
|
||||
_log.close();
|
||||
fclose(_sourceFile);
|
||||
fclose(_decodedFile);
|
||||
fclose(_actualSourceFile);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
VCMTestProtectionCallback::VCMTestProtectionCallback():
|
||||
_deltaFECRate(0),
|
||||
_keyFECRate(0),
|
||||
_nack(kNackOff)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
VCMTestProtectionCallback::~VCMTestProtectionCallback()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
VCMTestProtectionCallback::ProtectionRequest(const WebRtc_UWord8 deltaFECRate, const WebRtc_UWord8 keyFECRate, const bool nack)
|
||||
{
|
||||
_deltaFECRate = deltaFECRate;
|
||||
_keyFECRate = keyFECRate;
|
||||
if (nack == true)
|
||||
{
|
||||
_nack = kNackRtcp;
|
||||
}
|
||||
else
|
||||
{
|
||||
_nack = kNackOff;
|
||||
}
|
||||
return VCM_OK;
|
||||
|
||||
}
|
||||
NACKMethod
|
||||
VCMTestProtectionCallback::NACKMethod()
|
||||
{
|
||||
return _nack;
|
||||
}
|
||||
|
||||
WebRtc_UWord8
|
||||
VCMTestProtectionCallback::FECDeltaRate()
|
||||
{
|
||||
return _deltaFECRate;
|
||||
}
|
||||
|
||||
WebRtc_UWord8
|
||||
VCMTestProtectionCallback::FECKeyRate()
|
||||
{
|
||||
return _keyFECRate;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RTPFeedbackCallback::OnNetworkChanged(const WebRtc_Word32 id,
|
||||
const WebRtc_UWord16 bitrateTargetKbit,
|
||||
const WebRtc_UWord8 fractionLost,
|
||||
const WebRtc_UWord16 roundTripTimeMs,
|
||||
const WebRtc_UWord32 jitterMS,
|
||||
const WebRtc_UWord16 bwEstimateKbitMin,
|
||||
const WebRtc_UWord16 bwEstimateKbitMax)
|
||||
{
|
||||
|
||||
_vcm->SetChannelParameters(bitrateTargetKbit, fractionLost,(WebRtc_UWord8)roundTripTimeMs);
|
||||
}
|
||||
133
modules/video_coding/main/test/media_opt_test.h
Normal file
133
modules/video_coding/main/test/media_opt_test.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// VCM Media Optimization Test
|
||||
#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_MEDIA_OPT_TEST_H_
|
||||
#define WEBRTC_MODULES_VIDEO_CODING_TEST_MEDIA_OPT_TEST_H_
|
||||
|
||||
#include "video_coding.h"
|
||||
#include "test_macros.h"
|
||||
#include "test_util.h"
|
||||
#include "video_source.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
//
|
||||
|
||||
// media optimization test
|
||||
// This test simulates a complete encode-decode cycle via the RTP module.
|
||||
// allows error resilience tests, packet loss tests, etc.
|
||||
// Does not test the media optimization deirectly, but via the VCM API only.
|
||||
// The test allows two modes:
|
||||
// 1 - Standard, basic settings, one run
|
||||
// 2 - Release test - iterates over a number of video sequences, bit rates, packet loss values ,etc.
|
||||
|
||||
class VCMTestProtectionCallback: public webrtc::VCMProtectionCallback
|
||||
{
|
||||
public:
|
||||
VCMTestProtectionCallback();
|
||||
virtual ~VCMTestProtectionCallback();
|
||||
WebRtc_Word32 ProtectionRequest(const WebRtc_UWord8 deltaFECRate, const WebRtc_UWord8 keyFECRate, const bool nack);
|
||||
enum webrtc::NACKMethod NACKMethod();
|
||||
WebRtc_UWord8 FECDeltaRate();
|
||||
WebRtc_UWord8 FECKeyRate();
|
||||
private:
|
||||
WebRtc_UWord8 _deltaFECRate;
|
||||
WebRtc_UWord8 _keyFECRate;
|
||||
enum webrtc::NACKMethod _nack;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class MediaOptTest
|
||||
{
|
||||
public:
|
||||
MediaOptTest(webrtc::VideoCodingModule* vcm);
|
||||
~MediaOptTest();
|
||||
|
||||
static int RunTest(int testNum, CmdArgs& args);
|
||||
// perform encode-decode of an entire sequence
|
||||
WebRtc_Word32 Perform();
|
||||
// Set up for a single mode test
|
||||
void Setup(int testType, CmdArgs& args);
|
||||
// General set up - applicable for both modes
|
||||
void GeneralSetup();
|
||||
// Run release testing
|
||||
void RTTest();
|
||||
void TearDown();
|
||||
// mode = 1; will print to screen, otherwise only to log file
|
||||
void Print(int mode);
|
||||
|
||||
private:
|
||||
|
||||
webrtc::VideoCodingModule* _vcm;
|
||||
webrtc::RtpRtcp* _rtp;
|
||||
std::string _inname;
|
||||
std::string _outname;
|
||||
std::string _actualSourcename;
|
||||
std::fstream _log;
|
||||
FILE* _sourceFile;
|
||||
FILE* _decodedFile;
|
||||
FILE* _actualSourceFile;
|
||||
FILE* _outputRes;
|
||||
WebRtc_UWord16 _width;
|
||||
WebRtc_UWord16 _height;
|
||||
WebRtc_UWord32 _lengthSourceFrame;
|
||||
WebRtc_UWord32 _timeStamp;
|
||||
float _frameRate;
|
||||
bool _nackEnabled;
|
||||
bool _fecEnabled;
|
||||
bool _nackFecEnabled;
|
||||
WebRtc_UWord8 _rttMS;
|
||||
float _bitRate;
|
||||
double _lossRate;
|
||||
WebRtc_UWord32 _renderDelayMs;
|
||||
WebRtc_Word32 _frameCnt;
|
||||
float _sumEncBytes;
|
||||
WebRtc_Word32 _numFramesDropped;
|
||||
string _codecName;
|
||||
webrtc::VideoCodecType _sendCodecType;
|
||||
WebRtc_Word32 _numberOfCores;
|
||||
int vcmMacrosTests;
|
||||
int vcmMacrosErrors;
|
||||
|
||||
//for release test#2
|
||||
FILE* _fpinp;
|
||||
FILE* _fpout;
|
||||
FILE* _fpout2;
|
||||
int _testType;
|
||||
int _testNum;
|
||||
int _numParRuns;
|
||||
|
||||
}; // end of MediaOptTest class definition
|
||||
|
||||
|
||||
// Feed back from the RTP Module callback
|
||||
class RTPFeedbackCallback: public webrtc::RtpVideoFeedback
|
||||
{
|
||||
public:
|
||||
RTPFeedbackCallback(webrtc::VideoCodingModule* vcm) {_vcm = vcm;};
|
||||
void OnReceivedIntraFrameRequest(const WebRtc_Word32 id,
|
||||
const WebRtc_UWord8 message = 0){};
|
||||
|
||||
void OnNetworkChanged(const WebRtc_Word32 id,
|
||||
const WebRtc_UWord16 bitrateTargetKbit,
|
||||
const WebRtc_UWord8 fractionLost,
|
||||
const WebRtc_UWord16 roundTripTimeMs,
|
||||
const WebRtc_UWord32 jitterMS,
|
||||
const WebRtc_UWord16 bwEstimateKbitMin,
|
||||
const WebRtc_UWord16 bwEstimateKbitMax);
|
||||
|
||||
private:
|
||||
webrtc::VideoCodingModule* _vcm;
|
||||
|
||||
};
|
||||
#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_MEDIA_OPT_TEST_H_
|
||||
337
modules/video_coding/main/test/mt_rx_tx_test.cc
Normal file
337
modules/video_coding/main/test/mt_rx_tx_test.cc
Normal file
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*************************************************
|
||||
*
|
||||
* Testing multi thread - receive and send sides
|
||||
*
|
||||
**************************************************/
|
||||
|
||||
#include "receiver_tests.h" // shared RTP state and receive side threads
|
||||
#include "video_coding.h"
|
||||
#include "rtp_rtcp.h"
|
||||
#include "thread_wrapper.h"
|
||||
#include "../source/event.h"
|
||||
#include "test_util.h" // send side callback
|
||||
#include "media_opt_test.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
bool
|
||||
MainSenderThread(void* obj)
|
||||
{
|
||||
SendSharedState* state = static_cast<SendSharedState*>(obj);
|
||||
EventWrapper& waitEvent = *EventWrapper::Create();
|
||||
// preparing a frame for encoding
|
||||
VideoFrame sourceFrame;
|
||||
WebRtc_Word32 width = state->_args.width;
|
||||
WebRtc_Word32 height = state->_args.height;
|
||||
float frameRate = state->_args.frameRate;
|
||||
WebRtc_Word32 lengthSourceFrame = 3*width*height/2;
|
||||
sourceFrame.VerifyAndAllocate(lengthSourceFrame);
|
||||
WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[lengthSourceFrame];
|
||||
|
||||
if (state->_sourceFile == NULL)
|
||||
{
|
||||
state->_sourceFile = fopen(state->_args.inputFile.c_str(), "rb");
|
||||
if (state->_sourceFile == NULL)
|
||||
{
|
||||
printf ("Error when opening file \n");
|
||||
delete &waitEvent;
|
||||
delete tmpBuffer;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (feof(state->_sourceFile) == 0)
|
||||
{
|
||||
fread(tmpBuffer, 1, lengthSourceFrame,state->_sourceFile);
|
||||
state->_frameCnt++;
|
||||
sourceFrame.CopyFrame(lengthSourceFrame, tmpBuffer);
|
||||
sourceFrame.SetHeight(height);
|
||||
sourceFrame.SetWidth(width);
|
||||
state->_timestamp += (WebRtc_UWord32)(9e4 / frameRate);
|
||||
sourceFrame.SetTimeStamp(state->_timestamp);
|
||||
|
||||
WebRtc_Word32 ret = state->_vcm.AddVideoFrame(sourceFrame);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("Add Frame error: %d\n", ret);
|
||||
delete &waitEvent;
|
||||
delete tmpBuffer;
|
||||
return false;
|
||||
}
|
||||
waitEvent.Wait(33);
|
||||
}
|
||||
|
||||
delete &waitEvent;
|
||||
delete tmpBuffer;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IntSenderThread(void* obj)
|
||||
{
|
||||
SendSharedState* state = static_cast<SendSharedState*>(obj);
|
||||
state->_vcm.SetChannelParameters(1000,30,0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int MTRxTxTest(CmdArgs& args)
|
||||
{
|
||||
/* TEST SETTINGS */
|
||||
std::string inname = args.inputFile;
|
||||
std::string outname;
|
||||
if (args.outputFile == "")
|
||||
outname = "../MTRxTxTest_decoded.yuv";
|
||||
else
|
||||
outname = args.outputFile;
|
||||
|
||||
WebRtc_UWord16 width = args.width;
|
||||
WebRtc_UWord16 height = args.height;
|
||||
WebRtc_UWord32 lengthSourceFrame = 3*width*height/2;
|
||||
|
||||
float frameRate = args.frameRate;
|
||||
float bitRate = args.bitRate;
|
||||
WebRtc_Word32 numberOfCores = 1;
|
||||
|
||||
// error resilience/network
|
||||
bool nackEnabled = false;
|
||||
bool fecEnabled = false;
|
||||
WebRtc_UWord8 rttMS = 20;
|
||||
float lossRate = 0.0*255; // no packet loss
|
||||
WebRtc_UWord32 renderDelayMs = 0;
|
||||
WebRtc_UWord32 minPlayoutDelayMs = 0;
|
||||
WebRtc_UWord8 deltaFECRate = 0;
|
||||
WebRtc_UWord8 keyFECRate = 0;
|
||||
|
||||
/* TEST SET-UP */
|
||||
|
||||
// Set up trace
|
||||
Trace::CreateTrace();
|
||||
Trace::SetTraceFile("MTRxTxTestTrace.txt");
|
||||
Trace::SetLevelFilter(webrtc::kTraceAll);
|
||||
|
||||
FILE* sourceFile;
|
||||
FILE* decodedFile;
|
||||
|
||||
if ((sourceFile = fopen(inname.c_str(), "rb")) == NULL)
|
||||
{
|
||||
printf("Cannot read file %s.\n", inname.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((decodedFile = fopen(outname.c_str(), "wb")) == NULL)
|
||||
{
|
||||
printf("Cannot read file %s.\n", outname.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
//RTP
|
||||
RtpRtcp* rtp = RtpRtcp::CreateRtpRtcp(1, false);
|
||||
if (rtp->InitReceiver() < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (rtp->InitSender() < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// registering codecs for the RTP module
|
||||
TEST(rtp->RegisterReceivePayload("ULPFEC", VCM_ULPFEC_PAYLOAD_TYPE) == 0);
|
||||
TEST(rtp->RegisterReceivePayload("RED", VCM_RED_PAYLOAD_TYPE) == 0);
|
||||
TEST(rtp->RegisterReceivePayload(args.codecName.c_str(), VCM_VP8_PAYLOAD_TYPE) == 0);
|
||||
|
||||
// inform RTP Module of error resilience features
|
||||
TEST(rtp->SetGenericFECStatus(fecEnabled, VCM_RED_PAYLOAD_TYPE, VCM_ULPFEC_PAYLOAD_TYPE) == 0);
|
||||
|
||||
TEST(rtp->RegisterSendPayload(args.codecName.c_str(), VCM_VP8_PAYLOAD_TYPE, 90000, 1, 10000) == 0);
|
||||
|
||||
//VCM
|
||||
VideoCodingModule* vcm = VideoCodingModule::Create(1);
|
||||
if (vcm->InitializeReceiver() < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (vcm->InitializeSender())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// registering codecs for the VCM module
|
||||
VideoCodec sendCodec;
|
||||
vcm->InitializeSender();
|
||||
WebRtc_Word32 numberOfCodecs = vcm->NumberOfCodecs();
|
||||
if (numberOfCodecs < 1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vcm->Codec(args.codecType, &sendCodec) != 0)
|
||||
{
|
||||
// desired codec unavailable
|
||||
printf("Codec not registered\n");
|
||||
return -1;
|
||||
}
|
||||
// register codec
|
||||
sendCodec.startBitrate = (int) bitRate;
|
||||
sendCodec.height = height;
|
||||
sendCodec.width = width;
|
||||
sendCodec.maxFramerate = (WebRtc_UWord8)frameRate;
|
||||
vcm->RegisterSendCodec(&sendCodec, numberOfCores, 1440);
|
||||
vcm->RegisterReceiveCodec(&sendCodec, numberOfCores); // same settings for encode and decode
|
||||
|
||||
vcm->SetRenderDelay(renderDelayMs);
|
||||
vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs);
|
||||
|
||||
// Callback Settings
|
||||
|
||||
PacketRequester packetRequester(*rtp);
|
||||
vcm->RegisterPacketRequestCallback(&packetRequester);
|
||||
|
||||
VCMRTPEncodeCompleteCallback* encodeCompleteCallback = new VCMRTPEncodeCompleteCallback(rtp);
|
||||
vcm->RegisterTransportCallback(encodeCompleteCallback);
|
||||
encodeCompleteCallback->SetCodecType(ConvertCodecType(args.codecName.c_str()));
|
||||
encodeCompleteCallback->SetFrameDimensions(width, height);
|
||||
// frame ready to be sent to network
|
||||
RTPSendCompleteCallback* outgoingTransport = new RTPSendCompleteCallback(rtp, "dump.rtp");
|
||||
rtp->RegisterSendTransport(outgoingTransport);
|
||||
// FrameReceiveCallback
|
||||
VCMDecodeCompleteCallback receiveCallback(decodedFile);
|
||||
RtpDataCallback dataCallback(vcm);
|
||||
rtp->RegisterIncomingDataCallback(&dataCallback);
|
||||
vcm->RegisterReceiveCallback(&receiveCallback);
|
||||
|
||||
VCMTestProtectionCallback protectionCallback;
|
||||
vcm->RegisterProtectionCallback(&protectionCallback);
|
||||
|
||||
outgoingTransport->SetLossPct(lossRate);
|
||||
vcm->SetVideoProtection(kProtectionNack, nackEnabled);
|
||||
vcm->SetVideoProtection(kProtectionFEC, fecEnabled);
|
||||
|
||||
// inform RTP Module of error resilience features
|
||||
rtp->SetFECCodeRate(protectionCallback.FECKeyRate(),
|
||||
protectionCallback.FECDeltaRate());
|
||||
rtp->SetNACKStatus(protectionCallback.NACKMethod());
|
||||
|
||||
vcm->SetChannelParameters((WebRtc_UWord32) bitRate,
|
||||
(WebRtc_UWord8) lossRate, rttMS);
|
||||
|
||||
SharedRTPState mtState(*vcm, *rtp); // receive side
|
||||
SendSharedState mtSendState(*vcm, *rtp, args); // send side
|
||||
|
||||
/*START TEST*/
|
||||
|
||||
// Create and start all threads
|
||||
// send side threads
|
||||
ThreadWrapper* mainSenderThread = ThreadWrapper::CreateThread(MainSenderThread,
|
||||
&mtSendState, kNormalPriority, "MainSenderThread");
|
||||
ThreadWrapper* intSenderThread = ThreadWrapper::CreateThread(IntSenderThread,
|
||||
&mtSendState, kNormalPriority, "IntThread");
|
||||
|
||||
if (MainSenderThread != NULL)
|
||||
{
|
||||
unsigned int tid;
|
||||
mainSenderThread->Start(tid);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Unable to start main sender thread\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (IntSenderThread != NULL)
|
||||
{
|
||||
unsigned int tid;
|
||||
intSenderThread->Start(tid);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Unable to start sender interference thread\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Receive side threads
|
||||
ThreadWrapper* processingThread = ThreadWrapper::CreateThread(ProcessingThread,
|
||||
&mtState, kNormalPriority, "ProcessingThread");
|
||||
ThreadWrapper* decodeThread = ThreadWrapper::CreateThread(DecodeThread,
|
||||
&mtState, kNormalPriority, "DecodeThread");
|
||||
|
||||
if (processingThread != NULL)
|
||||
{
|
||||
unsigned int tid;
|
||||
processingThread->Start(tid);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Unable to start processing thread\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (decodeThread != NULL)
|
||||
{
|
||||
unsigned int tid;
|
||||
decodeThread->Start(tid);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Unable to start decode thread\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
EventWrapper& waitEvent = *EventWrapper::Create();
|
||||
|
||||
// Decode for 10 seconds and then tear down and exit.
|
||||
waitEvent.Wait(30000);
|
||||
|
||||
// Tear down
|
||||
|
||||
while (!mainSenderThread->Stop())
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
while (!intSenderThread->Stop())
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
while (!processingThread->Stop())
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
while (!decodeThread->Stop())
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
delete &waitEvent;
|
||||
delete mainSenderThread;
|
||||
delete intSenderThread;
|
||||
delete processingThread;
|
||||
delete decodeThread;
|
||||
delete encodeCompleteCallback;
|
||||
delete outgoingTransport;
|
||||
VideoCodingModule::Destroy(vcm);
|
||||
RtpRtcp::DestroyRtpRtcp(rtp);
|
||||
rtp = NULL;
|
||||
vcm = NULL;
|
||||
Trace::ReturnTrace();
|
||||
fclose(decodedFile);
|
||||
printf("Multi-Thread test Done: View output file \n");
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
373
modules/video_coding/main/test/normal_test.cc
Normal file
373
modules/video_coding/main/test/normal_test.cc
Normal file
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* 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 "normal_test.h"
|
||||
#include "../source/event.h"
|
||||
#include "tick_time.h"
|
||||
#include "common_types.h"
|
||||
#include "trace.h"
|
||||
#include "test_util.h"
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <time.h>
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
int NormalTest::RunTest(CmdArgs& args)
|
||||
{
|
||||
// Don't run this test with debug time
|
||||
#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG)
|
||||
printf("SIMULATION TIME\n");
|
||||
#else
|
||||
printf("REAL-TIME\n");
|
||||
#endif
|
||||
Trace::CreateTrace();
|
||||
Trace::SetTraceFile("VCMNormalTestTrace.txt");
|
||||
Trace::SetLevelFilter(webrtc::kTraceAll);
|
||||
VideoCodingModule* vcm = VideoCodingModule::Create(1);
|
||||
NormalTest VCMNTest(vcm);
|
||||
VCMNTest.Perform(args);
|
||||
VideoCodingModule::Destroy(vcm);
|
||||
Trace::ReturnTrace();
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////
|
||||
// Callback Implementation
|
||||
//////////////
|
||||
|
||||
VCMNTEncodeCompleteCallback::VCMNTEncodeCompleteCallback(FILE* encodedFile, NormalTest& test):
|
||||
_seqNo(0),
|
||||
_layerPacketId(1),
|
||||
_encodedFile(encodedFile),
|
||||
_encodedBytes(0),
|
||||
_skipCnt(0),
|
||||
_VCMReceiver(NULL),
|
||||
_test(test)
|
||||
{
|
||||
//
|
||||
}
|
||||
VCMNTEncodeCompleteCallback::~VCMNTEncodeCompleteCallback()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
VCMNTEncodeCompleteCallback::RegisterTransportCallback(VCMPacketizationCallback* transport)
|
||||
{
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
VCMNTEncodeCompleteCallback::SendData(const FrameType frameType,
|
||||
const WebRtc_UWord8 payloadType,
|
||||
const WebRtc_UWord32 timeStamp,
|
||||
const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord32 payloadSize,
|
||||
const RTPFragmentationHeader& fragmentationHeader)
|
||||
|
||||
{
|
||||
// will call the VCMReceiver input packet
|
||||
_frameType = frameType;
|
||||
// writing encodedData into file
|
||||
fwrite(payloadData, 1, payloadSize, _encodedFile);
|
||||
WebRtcRTPHeader rtpInfo;
|
||||
rtpInfo.header.markerBit = true;
|
||||
rtpInfo.type.Video.width = 0;
|
||||
rtpInfo.type.Video.height = 0;
|
||||
switch (_test.VideoType())
|
||||
{
|
||||
case kVideoCodecH263:
|
||||
rtpInfo.type.Video.codec = kRTPVideoH263;
|
||||
rtpInfo.type.Video.codecHeader.H263.bits = false;
|
||||
rtpInfo.type.Video.codecHeader.H263.independentlyDecodable = false;
|
||||
rtpInfo.type.Video.height = (WebRtc_UWord16)_test.Height();
|
||||
rtpInfo.type.Video.width = (WebRtc_UWord16)_test.Width();
|
||||
break;
|
||||
case kVideoCodecVP8:
|
||||
rtpInfo.type.Video.codec = kRTPVideoVP8;
|
||||
break;
|
||||
case kVideoCodecI420:
|
||||
rtpInfo.type.Video.codec = kRTPVideoI420;
|
||||
break;
|
||||
}
|
||||
rtpInfo.header.payloadType = payloadType;
|
||||
rtpInfo.header.sequenceNumber = _seqNo++;
|
||||
rtpInfo.header.ssrc = 0;
|
||||
rtpInfo.header.timestamp = timeStamp;
|
||||
rtpInfo.frameType = frameType;
|
||||
// Size should also be received from that table, since the payload type
|
||||
// defines the size.
|
||||
|
||||
_encodedBytes += payloadSize;
|
||||
if (payloadSize < 20)
|
||||
{
|
||||
_skipCnt++;
|
||||
}
|
||||
_VCMReceiver->IncomingPacket(payloadData, payloadSize, rtpInfo);
|
||||
return 0;
|
||||
}
|
||||
void
|
||||
VCMNTEncodeCompleteCallback::RegisterReceiverVCM(VideoCodingModule *vcm)
|
||||
{
|
||||
_VCMReceiver = vcm;
|
||||
return;
|
||||
}
|
||||
WebRtc_Word32
|
||||
VCMNTEncodeCompleteCallback::EncodedBytes()
|
||||
{
|
||||
return _encodedBytes;
|
||||
}
|
||||
|
||||
WebRtc_UWord32
|
||||
VCMNTEncodeCompleteCallback::SkipCnt()
|
||||
{
|
||||
return _skipCnt;
|
||||
}
|
||||
|
||||
// Decoded Frame Callback Implmentation
|
||||
VCMNTDecodeCompleCallback::~VCMNTDecodeCompleCallback()
|
||||
{
|
||||
//
|
||||
}
|
||||
WebRtc_Word32
|
||||
VCMNTDecodeCompleCallback::FrameToRender(webrtc::VideoFrame& videoFrame)
|
||||
{
|
||||
if (videoFrame.Width() != _currentWidth ||
|
||||
videoFrame.Height() != _currentHeight)
|
||||
{
|
||||
_currentWidth = videoFrame.Width();
|
||||
_currentHeight = videoFrame.Height();
|
||||
if (_decodedFile != NULL)
|
||||
{
|
||||
fclose(_decodedFile);
|
||||
_decodedFile = NULL;
|
||||
}
|
||||
_decodedFile = fopen(_outname.c_str(), "wb");
|
||||
}
|
||||
fwrite(videoFrame.Buffer(), 1, videoFrame.Length(), _decodedFile);
|
||||
_decodedBytes+= videoFrame.Length();
|
||||
return VCM_OK;
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
VCMNTDecodeCompleCallback::DecodedBytes()
|
||||
{
|
||||
return _decodedBytes;
|
||||
}
|
||||
|
||||
//VCM Normal Test Class implementation
|
||||
|
||||
NormalTest::NormalTest(VideoCodingModule* vcm)
|
||||
:
|
||||
_vcm(vcm),
|
||||
_totalEncodeTime(0),
|
||||
_totalDecodeTime(0),
|
||||
_decodeCompleteTime(0),
|
||||
_encodeCompleteTime(0),
|
||||
_totalEncodePipeTime(0),
|
||||
_totalDecodePipeTime(0),
|
||||
_frameCnt(0),
|
||||
_timeStamp(0),
|
||||
_encFrameCnt(0),
|
||||
_decFrameCnt(0),
|
||||
_sumEncBytes(0)
|
||||
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
NormalTest::~NormalTest()
|
||||
{
|
||||
//
|
||||
}
|
||||
void
|
||||
NormalTest::Setup(CmdArgs& args)
|
||||
{
|
||||
_inname = args.inputFile;
|
||||
_encodedName = "encoded_normaltest.yuv";
|
||||
_width = args.width;
|
||||
_height = args.height;
|
||||
_frameRate = args.frameRate;
|
||||
_bitRate = args.bitRate;
|
||||
if (args.outputFile == "")
|
||||
{
|
||||
std::ostringstream filename;
|
||||
filename << "../NormalTest_" << _width << "x" << _height << "_" << _frameRate << "Hz_P420.yuv";
|
||||
_outname = filename.str();
|
||||
}
|
||||
else
|
||||
{
|
||||
_outname = args.outputFile;
|
||||
}
|
||||
_lengthSourceFrame = 3*_width*_height/2;
|
||||
_videoType = args.codecType;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
_log.open("../TestLog.txt", std::fstream::out | std::fstream::app);
|
||||
return;
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
NormalTest::Perform(CmdArgs& args)
|
||||
{
|
||||
Setup(args);
|
||||
EventWrapper* waitEvent = EventWrapper::Create();
|
||||
VideoCodec _sendCodec;//, _receiveCodec; // tmp - sendCodecd used as receive codec
|
||||
_vcm->InitializeReceiver();
|
||||
_vcm->InitializeSender();
|
||||
TEST(VideoCodingModule::Codec(_videoType, &_sendCodec) == VCM_OK);
|
||||
_sendCodec.startBitrate = (int)_bitRate; // should be later on changed via the API
|
||||
_sendCodec.width = static_cast<WebRtc_UWord16>(_width);
|
||||
_sendCodec.height = static_cast<WebRtc_UWord16>(_height);
|
||||
_sendCodec.maxFramerate = _frameRate;
|
||||
TEST(_vcm->RegisterSendCodec(&_sendCodec, 4, 1400) == VCM_OK);// will also set and init the desired codec
|
||||
// register a decoder (same codec for decoder and encoder )
|
||||
TEST(_vcm->RegisterReceiveCodec(&_sendCodec, 1) == VCM_OK);
|
||||
/* Callback Settings */
|
||||
VCMNTDecodeCompleCallback _decodeCallback(_outname);
|
||||
_vcm->RegisterReceiveCallback(&_decodeCallback);
|
||||
VCMNTEncodeCompleteCallback _encodeCompleteCallback(_encodedFile, *this);
|
||||
_vcm->RegisterTransportCallback(&_encodeCompleteCallback);
|
||||
// encode and decode with the same vcm
|
||||
_encodeCompleteCallback.RegisterReceiverVCM(_vcm);
|
||||
///////////////////////
|
||||
/// Start Test
|
||||
///////////////////////
|
||||
VideoFrame sourceFrame;
|
||||
sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
|
||||
WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame];
|
||||
double startTime = clock()/(double)CLOCKS_PER_SEC;
|
||||
_vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 0);
|
||||
|
||||
SendStatsTest sendStats;
|
||||
sendStats.SetTargetFrameRate(static_cast<WebRtc_UWord32>(_frameRate));
|
||||
_vcm->RegisterSendStatisticsCallback(&sendStats);
|
||||
|
||||
while (feof(_sourceFile)== 0)
|
||||
{
|
||||
WebRtc_Word64 processStartTime = VCMTickTime::MillisecondTimestamp();
|
||||
fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile);
|
||||
_frameCnt++;
|
||||
sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer);
|
||||
sourceFrame.SetHeight(_height);
|
||||
sourceFrame.SetWidth(_width);
|
||||
_timeStamp += (WebRtc_UWord32)(9e4 / static_cast<float>(_sendCodec.maxFramerate));
|
||||
sourceFrame.SetTimeStamp(_timeStamp);
|
||||
_encodeTimes[int(sourceFrame.TimeStamp())] = clock()/(double)CLOCKS_PER_SEC;
|
||||
WebRtc_Word32 ret = _vcm->AddVideoFrame(sourceFrame);
|
||||
double encodeTime = clock()/(double)CLOCKS_PER_SEC - _encodeTimes[int(sourceFrame.TimeStamp())];
|
||||
_totalEncodeTime += encodeTime;
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("Error in AddFrame: %d\n", ret);
|
||||
//exit(1);
|
||||
}
|
||||
_decodeTimes[int(sourceFrame.TimeStamp())] = clock()/(double)CLOCKS_PER_SEC; // same timestamp value for encode and decode
|
||||
ret = _vcm->Decode();
|
||||
_totalDecodeTime += clock()/(double)CLOCKS_PER_SEC - _decodeTimes[int(sourceFrame.TimeStamp())];
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("Error in Decode: %d\n", ret);
|
||||
//exit(1);
|
||||
}
|
||||
if (_vcm->TimeUntilNextProcess() <= 0)
|
||||
{
|
||||
_vcm->Process();
|
||||
}
|
||||
WebRtc_UWord32 framePeriod = static_cast<WebRtc_UWord32>(1000.0f/static_cast<float>(_sendCodec.maxFramerate) + 0.5f);
|
||||
#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG)
|
||||
for (int i=0; i < framePeriod; i++)
|
||||
{
|
||||
VCMTickTime::IncrementDebugClock();
|
||||
}
|
||||
#else
|
||||
WebRtc_Word64 timeSpent = VCMTickTime::MillisecondTimestamp() - processStartTime;
|
||||
if (timeSpent < framePeriod)
|
||||
{
|
||||
waitEvent->Wait(framePeriod - timeSpent);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
double endTime = clock()/(double)CLOCKS_PER_SEC;
|
||||
_testTotalTime = endTime - startTime;
|
||||
_sumEncBytes = _encodeCompleteCallback.EncodedBytes();
|
||||
|
||||
delete tmpBuffer;
|
||||
delete waitEvent;
|
||||
Teardown();
|
||||
Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
NormalTest::FrameEncoded(WebRtc_UWord32 timeStamp)
|
||||
{
|
||||
_encodeCompleteTime = clock()/(double)CLOCKS_PER_SEC;
|
||||
_encFrameCnt++;
|
||||
_totalEncodePipeTime += _encodeCompleteTime - _encodeTimes[int(timeStamp)];
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
NormalTest::FrameDecoded(WebRtc_UWord32 timeStamp)
|
||||
{
|
||||
_decodeCompleteTime = clock()/(double)CLOCKS_PER_SEC;
|
||||
_decFrameCnt++;
|
||||
_totalDecodePipeTime += _decodeCompleteTime - _decodeTimes[timeStamp];
|
||||
}
|
||||
|
||||
void
|
||||
NormalTest::Print()
|
||||
{
|
||||
std::cout << "Normal Test Completed!" << std::endl;
|
||||
(_log) << "Normal Test Completed!" << std::endl;
|
||||
(_log) << "Input file: " << _inname << std::endl;
|
||||
(_log) << "Output file: " << _outname << std::endl;
|
||||
(_log) << "Total run time: " << _testTotalTime << std::endl;
|
||||
printf("Total run time: %f s \n", _testTotalTime);
|
||||
double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _frameRate));
|
||||
double actualBitRate = ActualBitRate / 1000.0;
|
||||
double avgEncTime = _totalEncodeTime / _frameCnt;
|
||||
double avgDecTime = _totalDecodeTime / _frameCnt;
|
||||
double psnr;
|
||||
PSNRfromFiles(_inname.c_str(), _outname.c_str(), _width, _height, &psnr);
|
||||
printf("Actual bitrate: %f kbps\n", actualBitRate);
|
||||
printf("Target bitrate: %f kbps\n", _bitRate);
|
||||
( _log) << "Actual bitrate: " << actualBitRate<< " kbps\tTarget: " << _bitRate << " kbps" << std::endl;
|
||||
printf("Average encode time: %f s\n", avgEncTime);
|
||||
( _log) << "Average encode time: " << avgEncTime << " s" << std::endl;
|
||||
printf("Average decode time: %f s\n", avgDecTime);
|
||||
( _log) << "Average decode time: " << avgDecTime << " s" << std::endl;
|
||||
printf("PSNR: %f \n", psnr);
|
||||
( _log) << "PSNR: " << psnr << std::endl;
|
||||
(_log) << std::endl;
|
||||
}
|
||||
void
|
||||
NormalTest::Teardown()
|
||||
{
|
||||
//_log.close();
|
||||
fclose(_sourceFile);
|
||||
fclose(_encodedFile);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
145
modules/video_coding/main/test/normal_test.h
Normal file
145
modules/video_coding/main/test/normal_test.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_NORMAL_TEST_H_
|
||||
#define WEBRTC_MODULES_VIDEO_CODING_TEST_NORMAL_TEST_H_
|
||||
|
||||
#include "video_coding.h"
|
||||
#include "test_macros.h"
|
||||
#include "test_util.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
class NormalTest;
|
||||
|
||||
//Send Side - Packetization callback - will create and send a packet to the VCMReceiver
|
||||
class VCMNTEncodeCompleteCallback : public webrtc::VCMPacketizationCallback
|
||||
{
|
||||
public:
|
||||
// constructor input: file in which encoded data will be written
|
||||
VCMNTEncodeCompleteCallback(FILE* encodedFile, NormalTest& test);
|
||||
virtual ~VCMNTEncodeCompleteCallback();
|
||||
// Register transport callback
|
||||
void RegisterTransportCallback(webrtc::VCMPacketizationCallback* transport);
|
||||
// process encoded data received from the encoder, pass stream to the VCMReceiver module
|
||||
WebRtc_Word32 SendData(const webrtc::FrameType frameType,
|
||||
const WebRtc_UWord8 payloadType,
|
||||
const WebRtc_UWord32 timeStamp,
|
||||
const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord32 payloadSize,
|
||||
const webrtc::RTPFragmentationHeader& fragmentationHeader);
|
||||
|
||||
// Register exisitng VCM. Currently - encode and decode with the same vcm module.
|
||||
void RegisterReceiverVCM(webrtc::VideoCodingModule *vcm);
|
||||
// Return sum of encoded data (all frames in the sequence)
|
||||
WebRtc_Word32 EncodedBytes();
|
||||
// return number of encoder-skipped frames
|
||||
WebRtc_UWord32 SkipCnt();;
|
||||
// conversion function for payload type (needed for the callback function)
|
||||
// RTPVideoVideoCodecTypes ConvertPayloadType(WebRtc_UWord8 payloadType);
|
||||
|
||||
private:
|
||||
FILE* _encodedFile;
|
||||
WebRtc_UWord32 _encodedBytes;
|
||||
WebRtc_UWord32 _skipCnt;
|
||||
webrtc::VideoCodingModule* _VCMReceiver;
|
||||
webrtc::FrameType _frameType;
|
||||
WebRtc_UWord8* _payloadData; // max payload size??
|
||||
WebRtc_UWord16 _seqNo;
|
||||
WebRtc_UWord8 _layerPacketId;
|
||||
NormalTest& _test;
|
||||
// int _vcmMacrosTests;
|
||||
// int _vcmMacrosErrors;
|
||||
|
||||
}; // end of VCMEncodeCompleteCallback
|
||||
|
||||
class VCMNTDecodeCompleCallback: public webrtc::VCMReceiveCallback
|
||||
{
|
||||
public:
|
||||
VCMNTDecodeCompleCallback(std::string outname): // or should it get a name?
|
||||
_outname(outname),
|
||||
_decodedFile(NULL),
|
||||
_decodedBytes(0),
|
||||
_currentWidth(0),
|
||||
_currentHeight(0) {}
|
||||
virtual ~VCMNTDecodeCompleCallback();
|
||||
void SetUserReceiveCallback(webrtc::VCMReceiveCallback* receiveCallback);
|
||||
// will write decoded frame into file
|
||||
WebRtc_Word32 FrameToRender(webrtc::VideoFrame& videoFrame);
|
||||
WebRtc_Word32 DecodedBytes();
|
||||
private:
|
||||
FILE* _decodedFile;
|
||||
std::string _outname;
|
||||
WebRtc_UWord32 _decodedBytes;
|
||||
WebRtc_UWord32 _currentWidth;
|
||||
WebRtc_UWord32 _currentHeight;
|
||||
|
||||
}; // end of VCMDecodeCompleCallback class
|
||||
|
||||
|
||||
class NormalTest
|
||||
{
|
||||
public:
|
||||
NormalTest(webrtc::VideoCodingModule* vcm);
|
||||
~NormalTest();
|
||||
static int RunTest(CmdArgs& args);
|
||||
WebRtc_Word32 Perform(CmdArgs& args);
|
||||
// option:: turn into private and call from perform
|
||||
WebRtc_UWord32 Width() const { return _width; };
|
||||
WebRtc_UWord32 Height() const { return _height; };
|
||||
webrtc::VideoCodecType VideoType() const { return _videoType; };
|
||||
|
||||
|
||||
protected:
|
||||
// test setup - open files, general initializations
|
||||
void Setup(CmdArgs& args);
|
||||
// close open files, delete used memory
|
||||
void Teardown();
|
||||
// print results to std output and to log file
|
||||
void Print();
|
||||
// calculating pipeline delay, and encoding time
|
||||
void FrameEncoded(WebRtc_UWord32 timeStamp);
|
||||
// calculating pipeline delay, and decoding time
|
||||
void FrameDecoded(WebRtc_UWord32 timeStamp);
|
||||
|
||||
webrtc::VideoCodingModule* _vcm;
|
||||
webrtc::VideoCodec _sendCodec;
|
||||
webrtc::VideoCodec _receiveCodec;
|
||||
std::string _inname;
|
||||
std::string _outname;
|
||||
std::string _encodedName;
|
||||
WebRtc_Word32 _sumEncBytes;
|
||||
FILE* _sourceFile;
|
||||
FILE* _decodedFile;
|
||||
FILE* _encodedFile;
|
||||
std::fstream _log;
|
||||
WebRtc_UWord32 _width;
|
||||
WebRtc_UWord32 _height;
|
||||
float _frameRate;
|
||||
float _bitRate;
|
||||
WebRtc_UWord32 _lengthSourceFrame;
|
||||
WebRtc_UWord32 _timeStamp;
|
||||
webrtc::VideoCodecType _videoType;
|
||||
double _totalEncodeTime;
|
||||
double _totalDecodeTime;
|
||||
double _decodeCompleteTime;
|
||||
double _encodeCompleteTime;
|
||||
double _totalEncodePipeTime;
|
||||
double _totalDecodePipeTime;
|
||||
double _testTotalTime;
|
||||
std::map<int, double> _encodeTimes;
|
||||
std::map<int, double> _decodeTimes;
|
||||
WebRtc_Word32 _frameCnt;
|
||||
WebRtc_Word32 _encFrameCnt;
|
||||
WebRtc_Word32 _decFrameCnt;
|
||||
|
||||
}; // end of VCMNormalTestClass
|
||||
|
||||
#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_NORMAL_TEST_H_
|
||||
52
modules/video_coding/main/test/plotJitterEstimate.m
Normal file
52
modules/video_coding/main/test/plotJitterEstimate.m
Normal file
@@ -0,0 +1,52 @@
|
||||
function plotJitterEstimate(filename)
|
||||
|
||||
[timestamps, framedata, slopes, randJitters, framestats, timetable, filtjitter, rtt, rttStatsVec] = jitterBufferTraceParser(filename);
|
||||
|
||||
x = 1:size(framestats, 1);
|
||||
%figure(2);
|
||||
subfigure(3, 2, 1);
|
||||
hold on;
|
||||
plot(x, slopes(x, 1).*(framestats(x, 1) - framestats(x, 2)) + 3*sqrt(randJitters(x,2)), 'b'); title('Estimate ms');
|
||||
plot(x, filtjitter, 'r');
|
||||
plot(x, slopes(x, 1).*(framestats(x, 1) - framestats(x, 2)), 'g');
|
||||
subfigure(3, 2, 2);
|
||||
%subplot(211);
|
||||
plot(x, slopes(x, 1)); title('Line slope');
|
||||
%subplot(212);
|
||||
%plot(x, slopes(x, 2)); title('Line offset');
|
||||
subfigure(3, 2, 3); hold on;
|
||||
plot(x, framestats); plot(x, framedata(x, 1)); title('frame size and average frame size');
|
||||
subfigure(3, 2, 4);
|
||||
plot(x, framedata(x, 2)); title('Delay');
|
||||
subfigure(3, 2, 5);
|
||||
hold on;
|
||||
plot(x, randJitters(x,1),'r');
|
||||
plot(x, randJitters(x,2)); title('Random jitter');
|
||||
|
||||
subfigure(3, 2, 6);
|
||||
delays = framedata(:,2);
|
||||
dL = [0; framedata(2:end, 1) - framedata(1:end-1, 1)];
|
||||
hold on;
|
||||
plot(dL, delays, '.');
|
||||
s = [min(dL) max(dL)];
|
||||
plot(s, slopes(end, 1)*s + slopes(end, 2), 'g');
|
||||
plot(s, slopes(end, 1)*s + slopes(end, 2) + 3*sqrt(randJitters(end,2)), 'r');
|
||||
plot(s, slopes(end, 1)*s + slopes(end, 2) - 3*sqrt(randJitters(end,2)), 'r');
|
||||
title('theta(1)*x+theta(2), (dT-dTS)/dL');
|
||||
if sum(size(rttStatsVec)) > 0
|
||||
figure; hold on;
|
||||
rttNstdDevsDrift = 3.5;
|
||||
rttNstdDevsJump = 2.5;
|
||||
rttSamples = rttStatsVec(:, 1);
|
||||
rttAvgs = rttStatsVec(:, 2);
|
||||
rttStdDevs = sqrt(rttStatsVec(:, 3));
|
||||
rttMax = rttStatsVec(:, 4);
|
||||
plot(rttSamples, 'ko-');
|
||||
plot(rttAvgs, 'g');
|
||||
plot(rttAvgs + rttNstdDevsDrift*rttStdDevs, 'b--');
|
||||
plot(rttAvgs + rttNstdDevsJump*rttStdDevs, 'b');
|
||||
plot(rttAvgs - rttNstdDevsJump*rttStdDevs, 'b');
|
||||
plot(rttMax, 'r');
|
||||
%plot(driftRestarts*max(maxRtts), '.');
|
||||
%plot(jumpRestarts*max(maxRtts), '.');
|
||||
end
|
||||
213
modules/video_coding/main/test/plotReceiveTrace.m
Normal file
213
modules/video_coding/main/test/plotReceiveTrace.m
Normal file
@@ -0,0 +1,213 @@
|
||||
function [t, TS] = plotReceiveTrace(filename, flat)
|
||||
fid=fopen(filename);
|
||||
%DEBUG ; ( 8:32:33:375 | 0) VIDEO:1 ; 5260; First packet of frame 1869537938
|
||||
%DEBUG ; ( 8:32:33:375 | 0) VIDEO CODING:1 ; 5260; Decoding timestamp 1869534934
|
||||
%DEBUG ; ( 8:32:33:375 | 0) VIDEO:1 ; 5260; Render frame 1869534934 at 20772610
|
||||
%DEBUG ; ( 8:32:33:375 | 0) VIDEO CODING:-1 ; 5260; Frame decoded: timeStamp=1870511259 decTime=0 maxDecTime=0, at 19965
|
||||
%DEBUG ; ( 7:59:42:500 | 0) VIDEO:-1 ; 2500; Received complete frame timestamp 1870514263 frame type 1 frame size 7862 at time 19965, jitter estimate was 130
|
||||
%DEBUG ; ( 8: 5:51:774 | 0) VIDEO:-1 ; 3968; ExtrapolateLocalTime(1870967878)=24971 ms
|
||||
|
||||
if nargin == 1
|
||||
flat = 0;
|
||||
end
|
||||
line = fgetl(fid);
|
||||
estimatedArrivalTime = [];
|
||||
packetTime = [];
|
||||
firstPacketTime = [];
|
||||
decodeTime = [];
|
||||
decodeCompleteTime = [];
|
||||
renderTime = [];
|
||||
completeTime = [];
|
||||
while ischar(line)%line ~= -1
|
||||
if length(line) == 0
|
||||
line = fgetl(fid);
|
||||
continue;
|
||||
end
|
||||
% Parse the trace line header
|
||||
[tempres, count] = sscanf(line, 'DEBUG ; (%u:%u:%u:%u |%*lu)%13c:');
|
||||
if count < 5
|
||||
line = fgetl(fid);
|
||||
continue;
|
||||
end
|
||||
hr=tempres(1);
|
||||
mn=tempres(2);
|
||||
sec=tempres(3);
|
||||
ms=tempres(4);
|
||||
timeInMs=hr*60*60*1000 + mn*60*1000 + sec*1000 + ms;
|
||||
label = tempres(5:end);
|
||||
I = find(label ~= 32);
|
||||
label = label(I(1):end); % remove white spaces
|
||||
if ~strncmp(char(label), 'VIDEO', 5) & ~strncmp(char(label), 'VIDEO CODING', 12)
|
||||
line = fgetl(fid);
|
||||
continue;
|
||||
end
|
||||
message = line(72:end);
|
||||
|
||||
% Parse message
|
||||
[p, count] = sscanf(message, 'ExtrapolateLocalTime(%lu)=%lu ms');
|
||||
if count == 2
|
||||
estimatedArrivalTime = [estimatedArrivalTime; p'];
|
||||
line = fgetl(fid);
|
||||
continue;
|
||||
end
|
||||
|
||||
[p, count] = sscanf(message, 'Packet seqNo %u of frame %lu at %lu');
|
||||
if count == 3
|
||||
packetTime = [packetTime; p'];
|
||||
line = fgetl(fid);
|
||||
continue;
|
||||
end
|
||||
|
||||
[p, count] = sscanf(message, 'First packet of frame %lu at %lu');
|
||||
if count == 2
|
||||
firstPacketTime = [firstPacketTime; p'];
|
||||
line = fgetl(fid);
|
||||
continue;
|
||||
end
|
||||
|
||||
[p, count] = sscanf(message, 'Decoding timestamp %lu at %lu');
|
||||
if count == 2
|
||||
decodeTime = [decodeTime; p'];
|
||||
line = fgetl(fid);
|
||||
continue;
|
||||
end
|
||||
|
||||
[p, count] = sscanf(message, 'Render frame %lu at %lu. Render delay %lu, required delay %lu, max decode time %lu, min total delay %lu');
|
||||
if count == 6
|
||||
renderTime = [renderTime; p'];
|
||||
line = fgetl(fid);
|
||||
continue;
|
||||
end
|
||||
|
||||
[p, count] = sscanf(message, 'Frame decoded: timeStamp=%lu decTime=%d maxDecTime=%lu, at %lu');
|
||||
if count == 4
|
||||
decodeCompleteTime = [decodeCompleteTime; p'];
|
||||
line = fgetl(fid);
|
||||
continue;
|
||||
end
|
||||
|
||||
[p, count] = sscanf(message, 'Received complete frame timestamp %lu frame type %u frame size %*u at time %lu, jitter estimate was %lu');
|
||||
if count == 4
|
||||
completeTime = [completeTime; p'];
|
||||
line = fgetl(fid);
|
||||
continue;
|
||||
end
|
||||
|
||||
line = fgetl(fid);
|
||||
end
|
||||
fclose(fid);
|
||||
|
||||
t = completeTime(:,3);
|
||||
TS = completeTime(:,1);
|
||||
|
||||
figure;
|
||||
subplot(211);
|
||||
hold on;
|
||||
slope = 0;
|
||||
|
||||
if sum(size(packetTime)) > 0
|
||||
% Plot the time when each packet arrives
|
||||
firstTimeStamp = packetTime(1,2);
|
||||
x = (packetTime(:,2) - firstTimeStamp)/90;
|
||||
if flat
|
||||
slope = x;
|
||||
end
|
||||
firstTime = packetTime(1,3);
|
||||
plot(x, packetTime(:,3) - firstTime - slope, 'b.');
|
||||
else
|
||||
% Plot the time when the first packet of a frame arrives
|
||||
firstTimeStamp = firstPacketTime(1,1);
|
||||
x = (firstPacketTime(:,1) - firstTimeStamp)/90;
|
||||
if flat
|
||||
slope = x;
|
||||
end
|
||||
firstTime = firstPacketTime(1,2);
|
||||
plot(x, firstPacketTime(:,2) - firstTime - slope, 'b.');
|
||||
end
|
||||
|
||||
% Plot the frame complete time
|
||||
if prod(size(completeTime)) > 0
|
||||
x = (completeTime(:,1) - firstTimeStamp)/90;
|
||||
if flat
|
||||
slope = x;
|
||||
end
|
||||
plot(x, completeTime(:,3) - firstTime - slope, 'ks');
|
||||
end
|
||||
|
||||
% Plot the time the decode starts
|
||||
if prod(size(decodeTime)) > 0
|
||||
x = (decodeTime(:,1) - firstTimeStamp)/90;
|
||||
if flat
|
||||
slope = x;
|
||||
end
|
||||
plot(x, decodeTime(:,2) - firstTime - slope, 'r.');
|
||||
end
|
||||
|
||||
% Plot the decode complete time
|
||||
if prod(size(decodeCompleteTime)) > 0
|
||||
x = (decodeCompleteTime(:,1) - firstTimeStamp)/90;
|
||||
if flat
|
||||
slope = x;
|
||||
end
|
||||
plot(x, decodeCompleteTime(:,4) - firstTime - slope, 'g.');
|
||||
end
|
||||
|
||||
if prod(size(renderTime)) > 0
|
||||
% Plot the wanted render time in ms
|
||||
x = (renderTime(:,1) - firstTimeStamp)/90;
|
||||
if flat
|
||||
slope = x;
|
||||
end
|
||||
plot(x, renderTime(:,2) - firstTime - slope, 'c-');
|
||||
|
||||
% Plot the render time if there were no render delay or decoding delay.
|
||||
x = (renderTime(:,1) - firstTimeStamp)/90;
|
||||
if flat
|
||||
slope = x;
|
||||
end
|
||||
plot(x, renderTime(:,2) - firstTime - slope - renderTime(:, 3) - renderTime(:, 5), 'c--');
|
||||
|
||||
% Plot the render time if there were no render delay.
|
||||
x = (renderTime(:,1) - firstTimeStamp)/90;
|
||||
if flat
|
||||
slope = x;
|
||||
end
|
||||
plot(x, renderTime(:,2) - firstTime - slope - renderTime(:, 3) - renderTime(:, 5), 'b-');
|
||||
end
|
||||
|
||||
%plot(x, 90*x, 'r-');
|
||||
|
||||
xlabel('RTP timestamp (in ms)');
|
||||
ylabel('Time (ms)');
|
||||
legend('Packet arrives', 'Frame complete', 'Decode', 'Decode complete', 'Time to render', 'Only jitter', 'Must decode');
|
||||
|
||||
% subplot(312);
|
||||
% hold on;
|
||||
% completeTs = completeTime(:, 1);
|
||||
% arrivalTs = estimatedArrivalTime(:, 1);
|
||||
% [c, completeIdx, arrivalIdx] = intersect(completeTs, arrivalTs);
|
||||
% %plot(completeTs(completeIdx), completeTime(completeIdx, 3) - estimatedArrivalTime(arrivalIdx, 2));
|
||||
% timeUntilComplete = completeTime(completeIdx, 3) - estimatedArrivalTime(arrivalIdx, 2);
|
||||
% devFromAvgCompleteTime = timeUntilComplete - mean(timeUntilComplete);
|
||||
% plot(completeTs(completeIdx) - completeTs(completeIdx(1)), devFromAvgCompleteTime);
|
||||
% plot(completeTime(:, 1) - completeTime(1, 1), completeTime(:, 4), 'r');
|
||||
% plot(decodeCompleteTime(:, 1) - decodeCompleteTime(1, 1), decodeCompleteTime(:, 2), 'g');
|
||||
% plot(decodeCompleteTime(:, 1) - decodeCompleteTime(1, 1), decodeCompleteTime(:, 3), 'k');
|
||||
% xlabel('RTP timestamp');
|
||||
% ylabel('Time (ms)');
|
||||
% legend('Complete time - Estimated arrival time', 'Desired jitter buffer level', 'Actual decode time', 'Max decode time', 0);
|
||||
|
||||
if prod(size(renderTime)) > 0
|
||||
subplot(212);
|
||||
hold on;
|
||||
firstTime = renderTime(1, 1);
|
||||
targetDelay = max(renderTime(:, 3) + renderTime(:, 4) + renderTime(:, 5), renderTime(:, 6));
|
||||
plot(renderTime(:, 1) - firstTime, renderTime(:, 3), 'r-');
|
||||
plot(renderTime(:, 1) - firstTime, renderTime(:, 4), 'b-');
|
||||
plot(renderTime(:, 1) - firstTime, renderTime(:, 5), 'g-');
|
||||
plot(renderTime(:, 1) - firstTime, renderTime(:, 6), 'k-');
|
||||
plot(renderTime(:, 1) - firstTime, targetDelay, 'c-');
|
||||
xlabel('RTP timestamp');
|
||||
ylabel('Time (ms)');
|
||||
legend('Render delay', 'Jitter delay', 'Decode delay', 'Extra delay', 'Min total delay');
|
||||
end
|
||||
62
modules/video_coding/main/test/plotTimingTest.m
Normal file
62
modules/video_coding/main/test/plotTimingTest.m
Normal file
@@ -0,0 +1,62 @@
|
||||
function plotTimingTest(filename)
|
||||
fid=fopen(filename);
|
||||
|
||||
%DEBUG ; ( 9:53:33:859 | 0) VIDEO:-1 ; 7132; Stochastic test 1
|
||||
%DEBUG ; ( 9:53:33:859 | 0) VIDEO CODING:-1 ; 7132; Frame decoded: timeStamp=3000 decTime=10 at 10012
|
||||
%DEBUG ; ( 9:53:33:859 | 0) VIDEO:-1 ; 7132; timeStamp=3000 clock=10037 maxWaitTime=0
|
||||
%DEBUG ; ( 9:53:33:859 | 0) VIDEO:-1 ; 7132; timeStampMs=33 renderTime=54
|
||||
line = fgetl(fid);
|
||||
decTime = [];
|
||||
waitTime = [];
|
||||
renderTime = [];
|
||||
foundStart = 0;
|
||||
testName = 'Stochastic test 1';
|
||||
while ischar(line)
|
||||
if length(line) == 0
|
||||
line = fgetl(fid);
|
||||
continue;
|
||||
end
|
||||
lineOrig = line;
|
||||
line = line(72:end);
|
||||
if ~foundStart
|
||||
if strncmp(line, testName, length(testName))
|
||||
foundStart = 1;
|
||||
end
|
||||
line = fgetl(fid);
|
||||
continue;
|
||||
end
|
||||
[p, count] = sscanf(line, 'Frame decoded: timeStamp=%lu decTime=%d maxDecTime=%d, at %lu');
|
||||
if count == 4
|
||||
decTime = [decTime; p'];
|
||||
line = fgetl(fid);
|
||||
continue;
|
||||
end
|
||||
[p, count] = sscanf(line, 'timeStamp=%u clock=%u maxWaitTime=%u');
|
||||
if count == 3
|
||||
waitTime = [waitTime; p'];
|
||||
line = fgetl(fid);
|
||||
continue;
|
||||
end
|
||||
[p, count] = sscanf(line, 'timeStamp=%u renderTime=%u');
|
||||
if count == 2
|
||||
renderTime = [renderTime; p'];
|
||||
line = fgetl(fid);
|
||||
continue;
|
||||
end
|
||||
line = fgetl(fid);
|
||||
end
|
||||
fclose(fid);
|
||||
|
||||
% Compensate for wrap arounds and start counting from zero.
|
||||
timeStamps = waitTime(:, 1);
|
||||
tsDiff = diff(timeStamps);
|
||||
wrapIdx = find(tsDiff < 0);
|
||||
timeStamps(wrapIdx+1:end) = hex2dec('ffffffff') + timeStamps(wrapIdx+1:end);
|
||||
timeStamps = timeStamps - timeStamps(1);
|
||||
|
||||
figure;
|
||||
hold on;
|
||||
plot(timeStamps, decTime(:, 2), 'r');
|
||||
plot(timeStamps, waitTime(:, 3), 'g');
|
||||
plot(timeStamps(2:end), diff(renderTime(:, 2)), 'b');
|
||||
legend('Decode time', 'Max wait time', 'Render time diff');
|
||||
499
modules/video_coding/main/test/quality_modes_test.cc
Normal file
499
modules/video_coding/main/test/quality_modes_test.cc
Normal file
@@ -0,0 +1,499 @@
|
||||
/*
|
||||
* 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 "quality_modes_test.h"
|
||||
#include "../source/event.h"
|
||||
#include "vplib.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <time.h>
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
int qualityModeTest()
|
||||
{
|
||||
// Don't run this test with debug time
|
||||
#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG)
|
||||
return -1;
|
||||
#endif
|
||||
VideoCodingModule* vcm = VideoCodingModule::Create(1);
|
||||
QualityModesTest QMTest(vcm);
|
||||
QMTest.Perform();
|
||||
VideoCodingModule::Destroy(vcm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
QualityModesTest::QualityModesTest(VideoCodingModule *vcm):
|
||||
NormalTest(vcm),
|
||||
_vpm()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
QualityModesTest::~QualityModesTest()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
void
|
||||
QualityModesTest::Setup()
|
||||
{
|
||||
|
||||
|
||||
_inname= "../codecs/testFiles/database/crew_30f_4CIF.yuv";
|
||||
_outname = "../out_qmtest.yuv";
|
||||
_encodedName = "../encoded_qmtest.yuv";
|
||||
|
||||
//NATIVE/SOURCE VALUES
|
||||
_nativeWidth = 2*352;
|
||||
_nativeHeight = 2*288;
|
||||
_nativeFrameRate = 30;
|
||||
|
||||
|
||||
//TARGET/ENCODER VALUES
|
||||
_width = 2*352;
|
||||
_height = 2*288;
|
||||
_frameRate = 30;
|
||||
//
|
||||
_bitRate = 400;
|
||||
|
||||
_flagSSIM = false;
|
||||
|
||||
_lengthSourceFrame = 3*_nativeWidth*_nativeHeight/2;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
_log.open("../TestLog.txt", std::fstream::out | std::fstream::app);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
QualityModesTest::Print()
|
||||
{
|
||||
std::cout << "Quality Modes Test Completed!" << std::endl;
|
||||
(_log) << "Quality Modes Test Completed!" << std::endl;
|
||||
(_log) << "Input file: " << _inname << std::endl;
|
||||
(_log) << "Output file: " << _outname << std::endl;
|
||||
(_log) << "Total run time: " << _testTotalTime << std::endl;
|
||||
printf("Total run time: %f s \n", _testTotalTime);
|
||||
double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _nativeFrameRate));
|
||||
double actualBitRate = ActualBitRate / 1000.0;
|
||||
double avgEncTime = _totalEncodeTime / _frameCnt;
|
||||
double avgDecTime = _totalDecodeTime / _frameCnt;
|
||||
double psnr,ssim;
|
||||
PSNRfromFiles(_inname.c_str(), _outname.c_str(), _nativeWidth, _nativeHeight, &psnr);
|
||||
printf("Actual bitrate: %f kbps\n", actualBitRate);
|
||||
printf("Target bitrate: %f kbps\n", _bitRate);
|
||||
( _log) << "Actual bitrate: " << actualBitRate<< " kbps\tTarget: " << _bitRate << " kbps" << std::endl;
|
||||
printf("Average encode time: %f s\n", avgEncTime);
|
||||
( _log) << "Average encode time: " << avgEncTime << " s" << std::endl;
|
||||
printf("Average decode time: %f s\n", avgDecTime);
|
||||
( _log) << "Average decode time: " << avgDecTime << " s" << std::endl;
|
||||
printf("PSNR: %f \n", psnr);
|
||||
printf("**Number of frames dropped in VPM***%d \n",_numFramesDroppedVPM);
|
||||
( _log) << "PSNR: " << psnr << std::endl;
|
||||
if (_flagSSIM == 1)
|
||||
{
|
||||
printf("***computing SSIM***\n");
|
||||
SSIMfromFiles(_inname.c_str(), _outname.c_str(), _nativeWidth, _nativeHeight, &ssim);
|
||||
printf("SSIM: %f \n", ssim);
|
||||
}
|
||||
(_log) << std::endl;
|
||||
}
|
||||
void
|
||||
QualityModesTest::Teardown()
|
||||
{
|
||||
_log.close();
|
||||
fclose(_sourceFile);
|
||||
fclose(_decodedFile);
|
||||
fclose(_encodedFile);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
WebRtc_Word32
|
||||
QualityModesTest::Perform()
|
||||
{
|
||||
Setup();
|
||||
// changing bit/frame rate during the test
|
||||
const float bitRateUpdate[] = {1000};
|
||||
const float frameRateUpdate[] = {30};
|
||||
const int updateFrameNum[] = {10000}; // frame numbers at which an update will occur
|
||||
|
||||
WebRtc_UWord32 numChanges = sizeof(updateFrameNum)/sizeof(*updateFrameNum);
|
||||
WebRtc_UWord8 change = 0;// change counter
|
||||
|
||||
_vpm = VideoProcessingModule::Create(1);
|
||||
|
||||
EventWrapper* waitEvent = EventWrapper::Create();
|
||||
VideoCodec codec;//both send and receive
|
||||
_vcm->InitializeReceiver();
|
||||
_vcm->InitializeSender();
|
||||
WebRtc_Word32 NumberOfCodecs = _vcm->NumberOfCodecs();
|
||||
for (int i = 0; i < NumberOfCodecs; i++)
|
||||
{
|
||||
_vcm->Codec(i, &codec);
|
||||
if(strncmp(codec.plName,"VP8" , 5) == 0)
|
||||
{
|
||||
codec.startBitrate = (int)_bitRate;
|
||||
codec.maxFramerate = (WebRtc_UWord8) _frameRate;
|
||||
codec.width = (WebRtc_UWord16)_width;
|
||||
codec.height = (WebRtc_UWord16)_height;
|
||||
TEST(_vcm->RegisterSendCodec(&codec, 2, 1440) == VCM_OK);// will also set and init the desired codec
|
||||
i = NumberOfCodecs;
|
||||
}
|
||||
}
|
||||
|
||||
// register a decoder (same codec for decoder and encoder )
|
||||
TEST(_vcm->RegisterReceiveCodec(&codec, 2) == VCM_OK);
|
||||
/* Callback Settings */
|
||||
VCMQMDecodeCompleCallback _decodeCallback(_decodedFile);
|
||||
_vcm->RegisterReceiveCallback(&_decodeCallback);
|
||||
VCMNTEncodeCompleteCallback _encodeCompleteCallback(_encodedFile, *this);
|
||||
_vcm->RegisterTransportCallback(&_encodeCompleteCallback);
|
||||
// encode and decode with the same vcm
|
||||
_encodeCompleteCallback.RegisterReceiverVCM(_vcm);
|
||||
|
||||
//quality modes callback
|
||||
QMTestVideoSettingsCallback QMCallback;
|
||||
QMCallback.RegisterVCM(_vcm);
|
||||
QMCallback.RegisterVPM(_vpm);
|
||||
_vcm->RegisterVideoQMCallback(&QMCallback);
|
||||
|
||||
///////////////////////
|
||||
/// Start Test
|
||||
///////////////////////
|
||||
_vpm->EnableTemporalDecimation(true);
|
||||
_vpm->EnableContentAnalysis(true);
|
||||
_vpm->SetInputFrameResampleMode(kFastRescaling);
|
||||
|
||||
// disabling internal VCM frame dropper
|
||||
_vcm->EnableFrameDropper(false);
|
||||
|
||||
VideoFrame sourceFrame;
|
||||
VideoFrame *decimatedFrame = NULL;
|
||||
sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
|
||||
WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame];
|
||||
double startTime = clock()/(double)CLOCKS_PER_SEC;
|
||||
_vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 0);
|
||||
|
||||
SendStatsTest sendStats;
|
||||
sendStats.SetTargetFrameRate(static_cast<WebRtc_UWord32>(_frameRate));
|
||||
_vcm->RegisterSendStatisticsCallback(&sendStats);
|
||||
|
||||
VideoContentMetrics* contentMetrics = NULL;
|
||||
// setting user frame rate
|
||||
_vpm->SetMaxFrameRate((WebRtc_UWord32)(_nativeFrameRate+ 0.5f));
|
||||
// for starters: keeping native values:
|
||||
_vpm->SetTargetResolution(_width, _height, (WebRtc_UWord32)(_frameRate+ 0.5f));
|
||||
_decodeCallback.SetOriginalFrameDimensions(_nativeWidth, _nativeHeight);
|
||||
|
||||
//tmp - disabling VPM frame dropping
|
||||
_vpm->EnableTemporalDecimation(false);
|
||||
|
||||
|
||||
WebRtc_Word32 ret = 0;
|
||||
_numFramesDroppedVPM = 0;
|
||||
|
||||
while (feof(_sourceFile)== 0)
|
||||
{
|
||||
fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile);
|
||||
_frameCnt++;
|
||||
sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer);
|
||||
sourceFrame.SetHeight(_nativeHeight);
|
||||
sourceFrame.SetWidth(_nativeWidth);
|
||||
|
||||
_timeStamp += (WebRtc_UWord32)(9e4 / static_cast<float>(codec.maxFramerate));
|
||||
sourceFrame.SetTimeStamp(_timeStamp);
|
||||
|
||||
ret = _vpm->PreprocessFrame(&sourceFrame, &decimatedFrame);
|
||||
if (ret == 1)
|
||||
{
|
||||
printf("VD: frame drop %d \n",_frameCnt);
|
||||
_numFramesDroppedVPM += 1;
|
||||
continue; // frame drop
|
||||
}
|
||||
else if (ret < 0)
|
||||
{
|
||||
printf("Error in PreprocessFrame: %d\n", ret);
|
||||
//exit(1);
|
||||
}
|
||||
contentMetrics = _vpm->ContentMetrics();
|
||||
if (contentMetrics == NULL)
|
||||
{
|
||||
printf("error: contentMetrics = NULL\n");
|
||||
}
|
||||
|
||||
// counting only encoding time
|
||||
_encodeTimes[int(sourceFrame.TimeStamp())] = clock()/(double)CLOCKS_PER_SEC;
|
||||
|
||||
WebRtc_Word32 ret = _vcm->AddVideoFrame(*decimatedFrame, contentMetrics);
|
||||
|
||||
_totalEncodeTime += clock()/(double)CLOCKS_PER_SEC - _encodeTimes[int(sourceFrame.TimeStamp())];
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("Error in AddFrame: %d\n", ret);
|
||||
//exit(1);
|
||||
}
|
||||
_decodeTimes[int(sourceFrame.TimeStamp())] = clock()/(double)CLOCKS_PER_SEC; // same timestamp value for encode and decode
|
||||
ret = _vcm->Decode();
|
||||
_totalDecodeTime += clock()/(double)CLOCKS_PER_SEC - _decodeTimes[int(sourceFrame.TimeStamp())];
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("Error in Decode: %d\n", ret);
|
||||
//exit(1);
|
||||
}
|
||||
if (_vcm->TimeUntilNextProcess() <= 0)
|
||||
{
|
||||
_vcm->Process();
|
||||
}
|
||||
// mimicking setTargetRates - update every 1 sec
|
||||
// this will trigger QMSelect
|
||||
if (_frameCnt%((int)_frameRate) == 0)
|
||||
{
|
||||
_vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 1);
|
||||
waitEvent->Wait(33);
|
||||
}
|
||||
waitEvent->Wait(33);
|
||||
// check for bit rate update
|
||||
if (change < numChanges && _frameCnt == updateFrameNum[change])
|
||||
{
|
||||
_bitRate = bitRateUpdate[change];
|
||||
_frameRate = frameRateUpdate[change];
|
||||
codec.startBitrate = (int)_bitRate;
|
||||
codec.maxFramerate = (WebRtc_UWord8) _frameRate;
|
||||
TEST(_vcm->RegisterSendCodec(&codec, 2, 1440) == VCM_OK);// will also set and init the desired codec
|
||||
change++;
|
||||
}
|
||||
}
|
||||
|
||||
double endTime = clock()/(double)CLOCKS_PER_SEC;
|
||||
_testTotalTime = endTime - startTime;
|
||||
_sumEncBytes = _encodeCompleteCallback.EncodedBytes();
|
||||
|
||||
delete tmpBuffer;
|
||||
delete waitEvent;
|
||||
_vpm->Reset();
|
||||
Teardown();
|
||||
Print();
|
||||
VideoProcessingModule::Destroy(_vpm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// implementing callback to be called from VCM to update VPM of frame rate and size
|
||||
QMTestVideoSettingsCallback::QMTestVideoSettingsCallback():
|
||||
_vpm(NULL),
|
||||
_vcm(NULL)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
void
|
||||
QMTestVideoSettingsCallback::RegisterVPM(VideoProcessingModule *vpm)
|
||||
{
|
||||
_vpm = vpm;
|
||||
}
|
||||
void
|
||||
QMTestVideoSettingsCallback::RegisterVCM(VideoCodingModule *vcm)
|
||||
{
|
||||
_vcm = vcm;
|
||||
}
|
||||
|
||||
bool
|
||||
QMTestVideoSettingsCallback::Updated()
|
||||
{
|
||||
if (_updated)
|
||||
{
|
||||
_updated = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
QMTestVideoSettingsCallback::SetVideoQMSettings(const WebRtc_UWord32 frameRate,
|
||||
const WebRtc_UWord32 width,
|
||||
const WebRtc_UWord32 height)
|
||||
{
|
||||
WebRtc_Word32 retVal = 0;
|
||||
printf("QM updates: W = %d, H = %d, FR = %d, \n", width, height, frameRate);
|
||||
retVal = _vpm->SetTargetResolution(width, height, frameRate);
|
||||
//Initialize codec with new values - is this the best place to do it?
|
||||
if (!retVal)
|
||||
{
|
||||
// first get current settings
|
||||
VideoCodec currentCodec;
|
||||
_vcm->SendCodec(¤tCodec);
|
||||
// now set new values:
|
||||
currentCodec.height = (WebRtc_UWord16)height;
|
||||
currentCodec.width = (WebRtc_UWord16)width;
|
||||
currentCodec.maxFramerate = (WebRtc_UWord8)frameRate;
|
||||
|
||||
// re-register encoder
|
||||
retVal = _vcm->RegisterSendCodec(¤tCodec, 2, 1440);
|
||||
_updated = true;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
// Decoded Frame Callback Implmentation
|
||||
VCMQMDecodeCompleCallback::VCMQMDecodeCompleCallback(FILE* decodedFile):
|
||||
_decodedFile(decodedFile),
|
||||
_decodedBytes(0),
|
||||
//_test(test),
|
||||
_origWidth(0),
|
||||
_origHeight(0),
|
||||
_decWidth(0),
|
||||
_decHeight(0),
|
||||
//_interpolator(NULL),
|
||||
_decBuffer(NULL),
|
||||
_frameCnt(0)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
VCMQMDecodeCompleCallback::~VCMQMDecodeCompleCallback()
|
||||
{
|
||||
// if (_interpolator != NULL)
|
||||
// {
|
||||
// deleteInterpolator(_interpolator);
|
||||
// _interpolator = NULL;
|
||||
// }
|
||||
if (_decBuffer != NULL)
|
||||
{
|
||||
delete [] _decBuffer;
|
||||
_decBuffer = NULL;
|
||||
}
|
||||
}
|
||||
WebRtc_Word32
|
||||
VCMQMDecodeCompleCallback::FrameToRender(VideoFrame& videoFrame)
|
||||
{
|
||||
if ((_origWidth == videoFrame.Width()) && (_origHeight == videoFrame.Height()))
|
||||
{
|
||||
fwrite(videoFrame.Buffer(), 1, videoFrame.Length(), _decodedFile);
|
||||
_frameCnt++;
|
||||
//printf("frame dec # %d", _frameCnt);
|
||||
// no need for interpolator and decBuffer
|
||||
if (_decBuffer != NULL)
|
||||
{
|
||||
delete [] _decBuffer;
|
||||
_decBuffer = NULL;
|
||||
}
|
||||
// if (_interpolator != NULL)
|
||||
// {
|
||||
// deleteInterpolator(_interpolator);
|
||||
// _interpolator = NULL;
|
||||
// }
|
||||
_decWidth = 0;
|
||||
_decHeight = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((_decWidth != videoFrame.Width()) || (_decHeight != videoFrame.Height()))
|
||||
{
|
||||
_decWidth = videoFrame.Width();
|
||||
_decHeight = videoFrame.Height();
|
||||
buildInterpolator();
|
||||
}
|
||||
|
||||
// interpolateFrame(_interpolator, videoFrame.Buffer(),_decBuffer);
|
||||
fwrite(_decBuffer, 1, _origWidth*_origHeight*3/2, _decodedFile);
|
||||
_frameCnt++;
|
||||
}
|
||||
|
||||
_decodedBytes += videoFrame.Length();
|
||||
return VCM_OK;
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
VCMQMDecodeCompleCallback::DecodedBytes()
|
||||
{
|
||||
return _decodedBytes;
|
||||
}
|
||||
|
||||
void
|
||||
VCMQMDecodeCompleCallback::SetOriginalFrameDimensions(WebRtc_Word32 width, WebRtc_Word32 height)
|
||||
{
|
||||
_origWidth = width;
|
||||
_origHeight = height;
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
VCMQMDecodeCompleCallback::buildInterpolator()
|
||||
{
|
||||
// if (_interpolator != NULL)
|
||||
// {
|
||||
// deleteInterpolator(_interpolator);
|
||||
// _interpolator = NULL;
|
||||
// }
|
||||
|
||||
// create decimator
|
||||
WebRtc_Word32 filterPar = 4; //Lanczos (assuming sampling ratio is 1, 1.5, 2, 4)
|
||||
|
||||
float HeightRatio = 0;
|
||||
float WidthRatio = 0;
|
||||
|
||||
WidthRatio = (float)_origWidth/(float)_decWidth;
|
||||
HeightRatio = (float)_origHeight/(float)_decHeight;
|
||||
|
||||
if ( (HeightRatio == 1.0 || HeightRatio == 1.5 || HeightRatio == 2 || HeightRatio == 4) &&
|
||||
(WidthRatio == 1.0 || WidthRatio == 1.5 || WidthRatio == 2 || WidthRatio == 4))
|
||||
{
|
||||
filterPar = 4; //Lanczos
|
||||
} else
|
||||
{
|
||||
filterPar = 0; //BiCubic
|
||||
}
|
||||
|
||||
|
||||
// define interpolator here
|
||||
|
||||
// create interpolator here
|
||||
|
||||
// if (_interpolator == NULL)
|
||||
// {
|
||||
// return -1;
|
||||
// }
|
||||
|
||||
WebRtc_UWord32 decFrameLength = _origWidth*_origHeight*3 >> 1;
|
||||
if (_decBuffer != NULL)
|
||||
{
|
||||
delete [] _decBuffer;
|
||||
}
|
||||
_decBuffer = new WebRtc_UWord8[decFrameLength];
|
||||
if (_decBuffer == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
94
modules/video_coding/main/test/quality_modes_test.h
Normal file
94
modules/video_coding/main/test/quality_modes_test.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_QUALITY_MODSE_TEST_H_
|
||||
#define WEBRTC_MODULES_VIDEO_CODING_TEST_QUALITY_MODSE_TEST_H_
|
||||
|
||||
#include "video_processing.h"
|
||||
#include "normal_test.h"
|
||||
#include "video_coding_defines.h"
|
||||
|
||||
int qualityModeTest();
|
||||
|
||||
class QualityModesTest : public NormalTest
|
||||
{
|
||||
public:
|
||||
QualityModesTest(webrtc::VideoCodingModule* vcm);
|
||||
virtual ~QualityModesTest();
|
||||
WebRtc_Word32 Perform();
|
||||
|
||||
private:
|
||||
|
||||
void Setup();
|
||||
void Print();
|
||||
void Teardown();
|
||||
void SsimComp();
|
||||
|
||||
webrtc::VideoProcessingModule* _vpm;
|
||||
|
||||
WebRtc_UWord32 _width;
|
||||
WebRtc_UWord32 _height;
|
||||
float _frameRate;
|
||||
WebRtc_UWord32 _nativeWidth;
|
||||
WebRtc_UWord32 _nativeHeight;
|
||||
float _nativeFrameRate;
|
||||
|
||||
WebRtc_UWord32 _numFramesDroppedVPM;
|
||||
bool _flagSSIM;
|
||||
|
||||
}; // end of QualityModesTest class
|
||||
|
||||
|
||||
class VCMQMDecodeCompleCallback: public webrtc::VCMReceiveCallback
|
||||
{
|
||||
public:
|
||||
VCMQMDecodeCompleCallback(FILE* decodedFile);
|
||||
virtual ~VCMQMDecodeCompleCallback();
|
||||
void SetUserReceiveCallback(webrtc::VCMReceiveCallback* receiveCallback);
|
||||
// will write decoded frame into file
|
||||
WebRtc_Word32 FrameToRender(webrtc::VideoFrame& videoFrame);
|
||||
WebRtc_Word32 DecodedBytes();
|
||||
void SetOriginalFrameDimensions(WebRtc_Word32 width, WebRtc_Word32 height);
|
||||
WebRtc_Word32 buildInterpolator();
|
||||
private:
|
||||
FILE* _decodedFile;
|
||||
WebRtc_UWord32 _decodedBytes;
|
||||
// QualityModesTest& _test;
|
||||
WebRtc_Word32 _origWidth;
|
||||
WebRtc_Word32 _origHeight;
|
||||
WebRtc_Word32 _decWidth;
|
||||
WebRtc_Word32 _decHeight;
|
||||
// VideoInterpolator* _interpolator;
|
||||
WebRtc_UWord8* _decBuffer;
|
||||
WebRtc_UWord32 _frameCnt; // debug
|
||||
|
||||
}; // end of VCMQMDecodeCompleCallback class
|
||||
|
||||
class QMTestVideoSettingsCallback : public webrtc::VCMQMSettingsCallback
|
||||
{
|
||||
public:
|
||||
QMTestVideoSettingsCallback();
|
||||
// update VPM with QM settings
|
||||
WebRtc_Word32 SetVideoQMSettings(const WebRtc_UWord32 frameRate,
|
||||
const WebRtc_UWord32 width,
|
||||
const WebRtc_UWord32 height);
|
||||
// register VPM used by test
|
||||
void RegisterVPM(webrtc::VideoProcessingModule* vpm);
|
||||
void RegisterVCM(webrtc::VideoCodingModule* vcm);
|
||||
bool Updated();
|
||||
|
||||
private:
|
||||
webrtc::VideoProcessingModule* _vpm;
|
||||
webrtc::VideoCodingModule* _vcm;
|
||||
bool _updated;
|
||||
};
|
||||
|
||||
|
||||
#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_QUALITY_MODSE_TEST_H_
|
||||
90
modules/video_coding/main/test/receiver_tests.h
Normal file
90
modules/video_coding/main/test/receiver_tests.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_RECEIVER_TESTS_H_
|
||||
#define WEBRTC_MODULES_VIDEO_CODING_TEST_RECEIVER_TESTS_H_
|
||||
|
||||
#include "video_coding.h"
|
||||
#include "module_common_types.h"
|
||||
#include "common_types.h"
|
||||
#include "rtp_rtcp.h"
|
||||
#include "typedefs.h"
|
||||
#include "rtp_player.h"
|
||||
#include "test_util.h"
|
||||
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
|
||||
class RtpDataCallback : public webrtc::RtpData
|
||||
{
|
||||
public:
|
||||
RtpDataCallback(webrtc::VideoCodingModule* vcm)
|
||||
: _vcm(vcm) {};
|
||||
|
||||
virtual WebRtc_Word32 OnReceivedPayloadData(const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord16 payloadSize,
|
||||
const webrtc::WebRtcRTPHeader* rtpHeader);
|
||||
private:
|
||||
webrtc::VideoCodingModule* _vcm;
|
||||
};
|
||||
|
||||
class FrameReceiveCallback : public webrtc::VCMReceiveCallback
|
||||
{
|
||||
public:
|
||||
FrameReceiveCallback(std::string outFilename) :
|
||||
_outFilename(outFilename),
|
||||
_outFile(NULL),
|
||||
_timingFile(NULL) {}
|
||||
|
||||
virtual ~FrameReceiveCallback();
|
||||
|
||||
WebRtc_Word32 FrameToRender(webrtc::VideoFrame& videoFrame);
|
||||
|
||||
private:
|
||||
std::string _outFilename;
|
||||
FILE* _outFile;
|
||||
FILE* _timingFile;
|
||||
};
|
||||
|
||||
class SharedState
|
||||
{
|
||||
public:
|
||||
SharedState(webrtc::VideoCodingModule& vcm, RTPPlayer& rtpPlayer) :
|
||||
_rtpPlayer(rtpPlayer),
|
||||
_vcm(vcm) {}
|
||||
webrtc::VideoCodingModule& _vcm;
|
||||
RTPPlayer& _rtpPlayer;
|
||||
};
|
||||
|
||||
class SharedRTPState
|
||||
{
|
||||
public:
|
||||
SharedRTPState(webrtc::VideoCodingModule& vcm, webrtc::RtpRtcp& rtp) :
|
||||
_rtp(rtp),
|
||||
_vcm(vcm) {}
|
||||
webrtc::VideoCodingModule& _vcm;
|
||||
webrtc::RtpRtcp& _rtp;
|
||||
};
|
||||
|
||||
int RtpPlay(CmdArgs& args);
|
||||
int RtpPlayMT(CmdArgs& args,
|
||||
int releaseTest = 0,
|
||||
webrtc::VideoCodecType releaseTestVideoType = webrtc::kVideoCodecVP8);
|
||||
int ReceiverTimingTests(CmdArgs& args);
|
||||
int JitterBufferTest(CmdArgs& args);
|
||||
int DecodeFromStorageTest(CmdArgs& args);
|
||||
|
||||
// Thread functions:
|
||||
bool ProcessingThread(void* obj);
|
||||
bool RtpReaderThread(void* obj);
|
||||
bool DecodeThread(void* obj);
|
||||
bool NackThread(void* obj);
|
||||
|
||||
#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_RECEIVER_TESTS_H_
|
||||
227
modules/video_coding/main/test/receiver_timing_tests.cc
Normal file
227
modules/video_coding/main/test/receiver_timing_tests.cc
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* 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 "receiver_tests.h"
|
||||
#include "video_coding.h"
|
||||
#include "trace.h"
|
||||
#include "tick_time.h"
|
||||
#include "../source/event.h"
|
||||
#include "../source/internal_defines.h"
|
||||
#include "timing.h"
|
||||
#include "test_macros.h"
|
||||
#include "test_util.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
float vcmFloatMax(float a, float b)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
float vcmFloatMin(float a, float b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
double const pi = 4*std::atan(1.0);
|
||||
|
||||
class GaussDist
|
||||
{
|
||||
public:
|
||||
static float RandValue(float m, float stdDev) // returns a single normally distributed number
|
||||
{
|
||||
float r1 = static_cast<float>((std::rand() + 1.0)/(RAND_MAX + 1.0)); // gives equal distribution in (0, 1]
|
||||
float r2 = static_cast<float>((std::rand() + 1.0)/(RAND_MAX + 1.0));
|
||||
return m + stdDev * static_cast<float>(std::sqrt(-2*std::log(r1))*std::cos(2*pi*r2));
|
||||
}
|
||||
};
|
||||
|
||||
int ReceiverTimingTests(CmdArgs& args)
|
||||
{
|
||||
// Make sure this test is never executed with simulated clocks
|
||||
#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
// Set up trace
|
||||
Trace::CreateTrace();
|
||||
Trace::SetTraceFile("receiverTestTrace.txt");
|
||||
Trace::SetLevelFilter(webrtc::kTraceAll);
|
||||
|
||||
// A static random seed
|
||||
srand(0);
|
||||
|
||||
VCMTiming timing;
|
||||
float clockInMs = 0.0;
|
||||
WebRtc_UWord32 waitTime = 0;
|
||||
WebRtc_Word32 jitterDelayMs = 0;
|
||||
WebRtc_Word32 maxDecodeTimeMs = 0;
|
||||
WebRtc_Word32 extraDelayMs = 0;
|
||||
WebRtc_UWord32 timeStamp = 0;
|
||||
|
||||
timing.Reset(static_cast<WebRtc_Word64>(clockInMs + 0.5));
|
||||
|
||||
timing.UpdateCurrentDelay(timeStamp);
|
||||
TEST(timing.MaxWaitingTime(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5)) >= 0);
|
||||
|
||||
timing.Reset(static_cast<WebRtc_Word64>(clockInMs + 0.5));
|
||||
|
||||
timing.IncomingTimestamp(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5));
|
||||
jitterDelayMs = 20;
|
||||
timing.SetRequiredDelay(jitterDelayMs);
|
||||
timing.UpdateCurrentDelay(timeStamp);
|
||||
waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5)),
|
||||
static_cast<WebRtc_Word64>(clockInMs + 0.5));
|
||||
// First update initializes the render time. Since we have no decode delay
|
||||
// we get waitTime = renderTime - now - renderDelay = jitter
|
||||
TEST(waitTime == jitterDelayMs);
|
||||
|
||||
jitterDelayMs += VCMTiming::kDelayMaxChangeMsPerS + 10;
|
||||
timeStamp += 90000;
|
||||
clockInMs += 1000.0f;
|
||||
timing.SetRequiredDelay(jitterDelayMs);
|
||||
timing.UpdateCurrentDelay(timeStamp);
|
||||
waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5)),
|
||||
static_cast<WebRtc_Word64>(clockInMs + 0.5));
|
||||
// Since we gradually increase the delay we only get
|
||||
// 100 ms every second.
|
||||
TEST(waitTime == jitterDelayMs - 10);
|
||||
|
||||
timeStamp += 90000;
|
||||
clockInMs += 1000.0;
|
||||
timing.UpdateCurrentDelay(timeStamp);
|
||||
waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5)),
|
||||
static_cast<WebRtc_Word64>(clockInMs + 0.5));
|
||||
TEST(waitTime == jitterDelayMs);
|
||||
|
||||
// 300 incoming frames without jitter, verify that this gives the exact wait time
|
||||
for (int i=0; i < 300; i++)
|
||||
{
|
||||
clockInMs += 1000.0f/30.0f;
|
||||
timeStamp += 3000;
|
||||
timing.IncomingTimestamp(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5));
|
||||
}
|
||||
timing.UpdateCurrentDelay(timeStamp);
|
||||
waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5)),
|
||||
static_cast<WebRtc_Word64>(clockInMs + 0.5));
|
||||
TEST(waitTime == jitterDelayMs);
|
||||
|
||||
// Add decode time estimates
|
||||
for (int i=0; i < 10; i++)
|
||||
{
|
||||
WebRtc_Word64 startTimeMs = static_cast<WebRtc_Word64>(clockInMs + 0.5);
|
||||
clockInMs += 10.0f;
|
||||
timing.StopDecodeTimer(timeStamp, startTimeMs, static_cast<WebRtc_Word64>(clockInMs + 0.5));
|
||||
timeStamp += 3000;
|
||||
clockInMs += 1000.0f/30.0f - 10.0f;
|
||||
timing.IncomingTimestamp(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5));
|
||||
}
|
||||
maxDecodeTimeMs = 10;
|
||||
timing.SetRequiredDelay(jitterDelayMs);
|
||||
clockInMs += 1000.0f;
|
||||
timeStamp += 90000;
|
||||
timing.UpdateCurrentDelay(timeStamp);
|
||||
waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5)),
|
||||
static_cast<WebRtc_Word64>(clockInMs + 0.5));
|
||||
TEST(waitTime == jitterDelayMs);
|
||||
|
||||
WebRtc_UWord32 totalDelay1 = timing.TargetVideoDelay();
|
||||
WebRtc_UWord32 minTotalDelayMs = 200;
|
||||
timing.SetMinimumTotalDelay(minTotalDelayMs);
|
||||
clockInMs += 5000.0f;
|
||||
timeStamp += 5*90000;
|
||||
timing.UpdateCurrentDelay(timeStamp);
|
||||
waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5)),
|
||||
static_cast<WebRtc_Word64>(clockInMs + 0.5));
|
||||
WebRtc_UWord32 totalDelay2 = timing.TargetVideoDelay();
|
||||
// We should at least have minTotalDelayMs - decodeTime (10) - renderTime (10) to wait
|
||||
TEST(waitTime == minTotalDelayMs - maxDecodeTimeMs - 10);
|
||||
// The total video delay should not increase with the extra delay,
|
||||
// the extra delay should be independent.
|
||||
TEST(totalDelay1 == totalDelay2);
|
||||
|
||||
// Reset min total delay
|
||||
timing.SetMinimumTotalDelay(0);
|
||||
clockInMs += 5000.0f;
|
||||
timeStamp += 5*90000;
|
||||
timing.UpdateCurrentDelay(timeStamp);
|
||||
|
||||
// A sudden increase in timestamp of 2.1 seconds
|
||||
clockInMs += 1000.0f/30.0f;
|
||||
timeStamp += static_cast<WebRtc_UWord32>(2.1*90000 + 0.5);
|
||||
WebRtc_Word64 ret = timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5));
|
||||
TEST(ret == -1);
|
||||
timing.Reset();
|
||||
|
||||
// This test produces a trace which can be parsed with plotTimingTest.m. The plot
|
||||
// can be used to see that the timing is reasonable under noise, and that the
|
||||
// gradual transition between delays works as expected.
|
||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, -1, "Stochastic test 1");
|
||||
|
||||
jitterDelayMs = 60;
|
||||
maxDecodeTimeMs = 10;
|
||||
extraDelayMs = 0;
|
||||
|
||||
timeStamp = static_cast<WebRtc_UWord32>(-10000); // To produce a wrap
|
||||
clockInMs = 10000.0f;
|
||||
timing.Reset(static_cast<WebRtc_Word64>(clockInMs + 0.5));
|
||||
|
||||
float noise = 0.0f;
|
||||
for (int i=0; i < 1400; i++)
|
||||
{
|
||||
if (i == 400)
|
||||
{
|
||||
jitterDelayMs = 30;
|
||||
}
|
||||
else if (i == 700)
|
||||
{
|
||||
jitterDelayMs = 100;
|
||||
}
|
||||
else if (i == 1000)
|
||||
{
|
||||
minTotalDelayMs = 200;
|
||||
timing.SetMinimumTotalDelay(minTotalDelayMs);
|
||||
}
|
||||
else if (i == 1200)
|
||||
{
|
||||
minTotalDelayMs = 0;
|
||||
timing.SetMinimumTotalDelay(minTotalDelayMs);
|
||||
}
|
||||
WebRtc_Word64 startTimeMs = static_cast<WebRtc_Word64>(clockInMs + 0.5);
|
||||
noise = vcmFloatMin(vcmFloatMax(GaussDist::RandValue(0, 2), -10.0f), 30.0f);
|
||||
clockInMs += 10.0f;
|
||||
timing.StopDecodeTimer(timeStamp, startTimeMs, static_cast<WebRtc_Word64>(clockInMs + noise + 0.5));
|
||||
timeStamp += 3000;
|
||||
clockInMs += 1000.0f/30.0f - 10.0f;
|
||||
noise = vcmFloatMin(vcmFloatMax(GaussDist::RandValue(0, 8), -15.0f), 15.0f);
|
||||
timing.IncomingTimestamp(timeStamp, static_cast<WebRtc_Word64>(clockInMs + noise + 0.5));
|
||||
timing.SetRequiredDelay(jitterDelayMs);
|
||||
timing.UpdateCurrentDelay(timeStamp);
|
||||
waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5)),
|
||||
static_cast<WebRtc_Word64>(clockInMs + 0.5));
|
||||
|
||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, -1, "timeStamp=%u clock=%u maxWaitTime=%u", timeStamp,
|
||||
static_cast<WebRtc_UWord32>(clockInMs + 0.5), waitTime);
|
||||
|
||||
WebRtc_Word64 renderTimeMs = timing.RenderTimeMs(timeStamp, static_cast<WebRtc_Word64>(clockInMs + 0.5));
|
||||
|
||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, -1,
|
||||
"timeStamp=%u renderTime=%u",
|
||||
timeStamp,
|
||||
MaskWord64ToUWord32(renderTimeMs));
|
||||
}
|
||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, -1, "End Stochastic test 1");
|
||||
Trace::ReturnTrace();
|
||||
return 0;
|
||||
}
|
||||
46
modules/video_coding/main/test/release_test.cc
Normal file
46
modules/video_coding/main/test/release_test.cc
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 "ReleaseTest.h"
|
||||
#include "ReceiverTests.h"
|
||||
#include "TestMacros.h"
|
||||
#include "MediaOptTest.h"
|
||||
#include "CodecDataBaseTest.h"
|
||||
#include "GenericCodecTest.h"
|
||||
|
||||
|
||||
|
||||
|
||||
int ReleaseTest()
|
||||
{
|
||||
printf("VCM RELEASE TESTS \n\n");
|
||||
|
||||
// Automatic tests
|
||||
|
||||
printf("Testing receive side timing...\n");
|
||||
TEST(ReceiverTimingTests() == 0);
|
||||
|
||||
printf("Testing jitter buffer...\n");
|
||||
TEST(JitterBufferTest() == 0);
|
||||
|
||||
printf("Testing Codec Data Base...\n");
|
||||
TEST(CodecDBTest() == 0);
|
||||
|
||||
printf("Testing Media Optimization....\n");
|
||||
TEST(VCMMediaOptTest(1) == 0);
|
||||
|
||||
// Tests requiring verification
|
||||
|
||||
printf("Testing Multi thread send-receive....\n");
|
||||
TEST(MTRxTxTest() == 0);
|
||||
printf("Verify by viewing output file MTRxTx_out.yuv \n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
17
modules/video_coding/main/test/release_test.h
Normal file
17
modules/video_coding/main/test/release_test.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef RELEASE_TEST_H
|
||||
#define RELEASE_TEST_H
|
||||
|
||||
int ReleaseTest();
|
||||
int ReleaseTestPart2();
|
||||
|
||||
#endif
|
||||
31
modules/video_coding/main/test/release_test_pt2.cc
Normal file
31
modules/video_coding/main/test/release_test_pt2.cc
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 "ReleaseTest.h"
|
||||
#include "ReceiverTests.h"
|
||||
#include "TestMacros.h"
|
||||
#include "MediaOptTest.h"
|
||||
#include "CodecDataBaseTest.h"
|
||||
#include "GenericCodecTest.h"
|
||||
|
||||
|
||||
|
||||
|
||||
int ReleaseTestPart2()
|
||||
{
|
||||
printf("Verify that TICK_TIME_DEBUG and EVENT_DEBUG are uncommented");
|
||||
// Tests requiring verification
|
||||
|
||||
printf("Testing Generic Codecs...\n");
|
||||
TEST(VCMGenericCodecTest() == 0);
|
||||
printf("Verify by viewing output file GCTest_out.yuv \n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
241
modules/video_coding/main/test/resampler_test.cc
Normal file
241
modules/video_coding/main/test/resampler_test.cc
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* 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 "ResamplerTest.h"
|
||||
#include "video_coding.h"
|
||||
#include "tick_time.h"
|
||||
#include "../source/event.h"
|
||||
#include "VCMSpatialResampler.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
int ResamplerTest()
|
||||
{
|
||||
VideoCodingModule* vcm = VideoCodingModule::Create(1);
|
||||
class ResamplerTest test(vcm);
|
||||
int ret = test.Perform();
|
||||
VideoCodingModule::Destroy(vcm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ResamplerTest::ResamplerTest(VideoCodingModule* vcm):
|
||||
_width(0),
|
||||
_height(0),
|
||||
_timeStamp(0),
|
||||
_lengthSourceFrame(0),
|
||||
_vcmMacrosTests(0),
|
||||
_vcmMacrosErrors(0),
|
||||
_vcm(vcm)
|
||||
{
|
||||
//
|
||||
}
|
||||
ResamplerTest::~ResamplerTest()
|
||||
{
|
||||
//
|
||||
}
|
||||
void
|
||||
ResamplerTest::Setup()
|
||||
{
|
||||
_inname= "../../../../../codecs_video/testFiles/foreman.yuv";
|
||||
_width = 352;
|
||||
_height = 288;
|
||||
_frameRate = 30;
|
||||
_lengthSourceFrame = 3*_width*_height/2;
|
||||
_encodedName = "../ResamplerTest_encoded.yuv";
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
WebRtc_Word32 ResamplerTest::Perform()
|
||||
{
|
||||
// Make sure this test isn't executed without simulated clocks
|
||||
#if !defined(TICK_TIME_DEBUG) || !defined(EVENT_DEBUG)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
// Setup test
|
||||
Setup();
|
||||
|
||||
ResamplerStandAloneTest();
|
||||
|
||||
ResamplerVCMTest();
|
||||
|
||||
TearDown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ResamplerTest::ResamplerVCMTest()
|
||||
{
|
||||
// Create the input frame and read a frame from file
|
||||
VideoFrame sourceFrame;
|
||||
sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
|
||||
fread(sourceFrame.Buffer(), 1, _lengthSourceFrame, _sourceFile);
|
||||
sourceFrame.SetLength(_lengthSourceFrame);
|
||||
sourceFrame.SetHeight(_height);
|
||||
sourceFrame.SetWidth(_width);
|
||||
|
||||
TEST_EXIT_ON_FAIL(_vcm->InitializeReceiver() == VCM_OK);
|
||||
TEST_EXIT_ON_FAIL(_vcm->InitializeSender() == VCM_OK);
|
||||
|
||||
TEST_EXIT_ON_FAIL(_vcm->EnableInputFrameInterpolation(true) == VCM_OK);
|
||||
|
||||
TestSizeVCM(sourceFrame, 128, 80); // Cut, decimation 1x, interpolate
|
||||
TestSizeVCM(sourceFrame, 352/2, 288/2); // Even decimation
|
||||
TestSizeVCM(sourceFrame, 352, 288); // No resampling
|
||||
TestSizeVCM(sourceFrame, 2*352, 2*288); // Upsampling 2x
|
||||
TestSizeVCM(sourceFrame, 400, 256); // Upsampling 1.5x and cut
|
||||
TestSizeVCM(sourceFrame, 960, 720); // Upsampling 3.5x and cut
|
||||
|
||||
TEST_EXIT_ON_FAIL(_vcm->EnableInputFrameInterpolation(false) == VCM_OK);
|
||||
|
||||
TestSizeVCM(sourceFrame, 320, 240); // Cropped
|
||||
TestSizeVCM(sourceFrame, 1280, 720); // Padded
|
||||
}
|
||||
|
||||
void
|
||||
ResamplerTest::TestSizeVCM(VideoFrame& sourceFrame, WebRtc_UWord32 targetWidth, WebRtc_UWord32 targetHeight)
|
||||
{
|
||||
assert(false);
|
||||
/*
|
||||
std::ostringstream filename;
|
||||
filename << "../VCM_Resampler_" << targetWidth << "x" << targetHeight << "_30Hz_P420.yuv";
|
||||
std::cout << "Watch " << filename.str() << " and verify that it is okay." << std::endl;
|
||||
FILE* decodedFile = fopen(filename.str().c_str(), "wb");
|
||||
|
||||
_timeStamp += (WebRtc_UWord32)(9e4 / _frameRate);
|
||||
sourceFrame.SetTimeStamp(_timeStamp);
|
||||
|
||||
VCMDecodeCompleteCallback decodeCallback(decodedFile);
|
||||
VCMEncodeCompleteCallback encodeCompleteCallback(_encodedFile);
|
||||
TEST_EXIT_ON_FAIL(_vcm->RegisterReceiveCallback(&decodeCallback) == VCM_OK);
|
||||
TEST_EXIT_ON_FAIL(_vcm->RegisterTransportCallback(&encodeCompleteCallback) == VCM_OK);
|
||||
encodeCompleteCallback.RegisterReceiverVCM(_vcm);
|
||||
encodeCompleteCallback.SetCodecType(webrtc::VideoCodecVP8);
|
||||
|
||||
RegisterCodec(targetWidth, targetHeight);
|
||||
encodeCompleteCallback.SetFrameDimensions(targetWidth, targetHeight);
|
||||
TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
|
||||
TEST(_vcm->Decode() == VCM_OK);
|
||||
|
||||
fclose(decodedFile);
|
||||
*/
|
||||
}
|
||||
|
||||
void
|
||||
ResamplerTest::RegisterCodec(WebRtc_UWord32 width, WebRtc_UWord32 height)
|
||||
{
|
||||
// Register codecs
|
||||
assert(false);
|
||||
/*
|
||||
VideoCodec codec;
|
||||
VideoCodingModule::Codec(webrtc::kVideoCodecVP8, &codec);
|
||||
codec.width = static_cast<WebRtc_Word16>(width);
|
||||
codec.height = static_cast<WebRtc_Word16>(height);
|
||||
TEST(_vcm->RegisterSendCodec(&codec, 1, 1440) == VCM_OK);
|
||||
TEST(_vcm->RegisterReceiveCodec(&codec, 1) == VCM_OK);
|
||||
TEST(_vcm->SetChannelParameters(2000, 0, 0) == VCM_OK);
|
||||
*/
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
ResamplerTest::ResamplerStandAloneTest()
|
||||
{
|
||||
// Create the input frame and read a frame from file
|
||||
VideoFrame sourceFrame;
|
||||
sourceFrame.VerifyAndAllocate(_lengthSourceFrame);
|
||||
fread(sourceFrame.Buffer(), 1, _lengthSourceFrame, _sourceFile);
|
||||
sourceFrame.SetLength(_lengthSourceFrame);
|
||||
sourceFrame.SetHeight(_height);
|
||||
sourceFrame.SetWidth(_width);
|
||||
|
||||
TestSize(sourceFrame, 100, 50); // Cut, decimation 1x, interpolate
|
||||
TestSize(sourceFrame, 352/2, 288/2); // Even decimation
|
||||
TestSize(sourceFrame, 352, 288); // No resampling
|
||||
TestSize(sourceFrame, 2*352, 2*288); // Even upsampling
|
||||
TestSize(sourceFrame, 400, 256); // Upsampling 1.5x and cut
|
||||
TestSize(sourceFrame, 960, 720); // Upsampling 3.5x and cut
|
||||
TestSize(sourceFrame, 1280, 720); // Upsampling 4x and cut
|
||||
|
||||
sourceFrame.Free();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ResamplerTest::TestSize(VideoFrame& sourceFrame, WebRtc_UWord32 targetWidth, WebRtc_UWord32 targetHeight)
|
||||
{
|
||||
VCMSimpleSpatialResampler resampler;
|
||||
VideoFrame outFrame;
|
||||
std::ostringstream filename;
|
||||
filename << "../Resampler_" << targetWidth << "x" << targetHeight << "_30Hz_P420.yuv";
|
||||
std::cout << "Watch " << filename.str() << " and verify that it is okay." << std::endl;
|
||||
FILE* standAloneFile = fopen(filename.str().c_str(), "wb");
|
||||
//resampler.EnableUpSampling(true);
|
||||
resampler.EnableInterpolation(true);
|
||||
TEST(resampler.SetTargetFrameSize(targetWidth, targetHeight) == VCM_OK);
|
||||
TEST(resampler.ResampleFrame(sourceFrame, outFrame) == VCM_OK);
|
||||
TEST(outFrame.Buffer() != NULL);
|
||||
TEST(outFrame.Length() == (targetWidth * targetHeight * 3 / 2));
|
||||
|
||||
// Write to file for visual inspection
|
||||
fwrite(outFrame.Buffer(), 1, outFrame.Length(), standAloneFile);
|
||||
|
||||
outFrame.Free();
|
||||
fclose(standAloneFile);
|
||||
}
|
||||
|
||||
void
|
||||
ResamplerTest::Print()
|
||||
{
|
||||
printf("\nVCM Resampler Test: \n\n%i tests completed\n", _vcmMacrosTests);
|
||||
if (_vcmMacrosErrors > 0)
|
||||
{
|
||||
printf("%i FAILED\n\n", _vcmMacrosErrors);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("ALL PASSED\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ResamplerTest::TearDown()
|
||||
{
|
||||
fclose(_sourceFile);
|
||||
fclose(_encodedFile);
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
ResamplerTest::IncrementDebugClock(float frameRate)
|
||||
{
|
||||
for (int t= 0; t < 1000/frameRate; t++)
|
||||
{
|
||||
VCMTickTime::IncrementDebugClock();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
466
modules/video_coding/main/test/rtp_player.cc
Normal file
466
modules/video_coding/main/test/rtp_player.cc
Normal file
@@ -0,0 +1,466 @@
|
||||
/*
|
||||
* 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 "rtp_player.h"
|
||||
#include "../source/internal_defines.h"
|
||||
#include "rtp_rtcp.h"
|
||||
#include "tick_time.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#include <Winsock2.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
RawRtpPacket::RawRtpPacket(WebRtc_UWord8* data, WebRtc_UWord16 len)
|
||||
:
|
||||
rtpData(), rtpLen(len), resendTimeMs(-1)
|
||||
{
|
||||
rtpData = new WebRtc_UWord8[rtpLen];
|
||||
memcpy(rtpData, data, rtpLen);
|
||||
}
|
||||
|
||||
RawRtpPacket::~RawRtpPacket()
|
||||
{
|
||||
delete [] rtpData;
|
||||
}
|
||||
|
||||
LostPackets::LostPackets()
|
||||
:
|
||||
_critSect(*CriticalSectionWrapper::CreateCriticalSection()),
|
||||
_lossCount(0),
|
||||
ListWrapper(),
|
||||
_debugFile(NULL)
|
||||
{
|
||||
_debugFile = fopen("PacketLossDebug.txt", "w");
|
||||
}
|
||||
|
||||
LostPackets::~LostPackets()
|
||||
{
|
||||
if (_debugFile)
|
||||
{
|
||||
fclose(_debugFile);
|
||||
}
|
||||
ListItem* item = First();
|
||||
while (item != NULL)
|
||||
{
|
||||
RawRtpPacket* packet = static_cast<RawRtpPacket*>(item->GetItem());
|
||||
if (packet != NULL)
|
||||
{
|
||||
delete packet;
|
||||
}
|
||||
Erase(item);
|
||||
item = First();
|
||||
}
|
||||
delete &_critSect;
|
||||
}
|
||||
|
||||
WebRtc_UWord32 LostPackets::AddPacket(WebRtc_UWord8* rtpData, WebRtc_UWord16 rtpLen)
|
||||
{
|
||||
CriticalSectionScoped cs(_critSect);
|
||||
RawRtpPacket* packet = new RawRtpPacket(rtpData, rtpLen);
|
||||
ListItem* newItem = new ListItem(packet);
|
||||
InsertBefore(First(), newItem);
|
||||
const WebRtc_UWord16 seqNo = (rtpData[2] << 8) + rtpData[3];
|
||||
if (_debugFile != NULL)
|
||||
{
|
||||
fprintf(_debugFile, "%u Lost packet: %u\n", _lossCount, seqNo);
|
||||
}
|
||||
_lossCount++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebRtc_UWord32 LostPackets::SetResendTime(WebRtc_UWord16 sequenceNumber, WebRtc_Word64 resendTime)
|
||||
{
|
||||
CriticalSectionScoped cs(_critSect);
|
||||
ListItem* item = First();
|
||||
while (item != NULL)
|
||||
{
|
||||
RawRtpPacket* packet = static_cast<RawRtpPacket*>(item->GetItem());
|
||||
const WebRtc_UWord16 seqNo = (packet->rtpData[2] << 8) + packet->rtpData[3];
|
||||
const WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp();
|
||||
if (sequenceNumber == seqNo && packet->resendTimeMs + 10 < nowMs)
|
||||
{
|
||||
if (_debugFile != NULL)
|
||||
{
|
||||
fprintf(_debugFile, "Resend %u at %u\n", seqNo, MaskWord64ToUWord32(resendTime));
|
||||
}
|
||||
packet->resendTimeMs = resendTime;
|
||||
return 0;
|
||||
}
|
||||
item = Next(item);
|
||||
}
|
||||
fprintf(_debugFile, "Packet not lost %u\n", sequenceNumber);
|
||||
return -1;
|
||||
}
|
||||
|
||||
WebRtc_UWord32 LostPackets::NumberOfPacketsToResend() const
|
||||
{
|
||||
CriticalSectionScoped cs(_critSect);
|
||||
WebRtc_UWord32 count = 0;
|
||||
ListItem* item = First();
|
||||
while (item != NULL)
|
||||
{
|
||||
RawRtpPacket* packet = static_cast<RawRtpPacket*>(item->GetItem());
|
||||
if (packet->resendTimeMs >= 0)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
item = Next(item);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void LostPackets::ResentPacket(WebRtc_UWord16 seqNo)
|
||||
{
|
||||
CriticalSectionScoped cs(_critSect);
|
||||
if (_debugFile != NULL)
|
||||
{
|
||||
fprintf(_debugFile, "Resent %u at %u\n", seqNo,
|
||||
MaskWord64ToUWord32(VCMTickTime::MillisecondTimestamp()));
|
||||
}
|
||||
}
|
||||
|
||||
RTPPlayer::RTPPlayer(const char* filename, RtpData* callback)
|
||||
:
|
||||
_rtpModule(*RtpRtcp::CreateRtpRtcp(1, false)),
|
||||
_nextRtpTime(0),
|
||||
_dataCallback(callback),
|
||||
_firstPacket(true),
|
||||
_lossRate(0.0f),
|
||||
_nackEnabled(false),
|
||||
_resendPacketCount(0),
|
||||
_noLossStartup(100),
|
||||
_endOfFile(false),
|
||||
_rttMs(0),
|
||||
_firstPacketRtpTime(0),
|
||||
_firstPacketTimeMs(0),
|
||||
_reorderBuffer(NULL),
|
||||
_reordering(false),
|
||||
_nextPacket(),
|
||||
_nextPacketLength(0),
|
||||
_randVec(),
|
||||
_randVecPos(0)
|
||||
{
|
||||
_rtpFile = fopen(filename, "rb");
|
||||
memset(_nextPacket, 0, sizeof(_nextPacket));
|
||||
}
|
||||
|
||||
RTPPlayer::~RTPPlayer()
|
||||
{
|
||||
RtpRtcp::DestroyRtpRtcp(&_rtpModule);
|
||||
if (_rtpFile != NULL)
|
||||
{
|
||||
fclose(_rtpFile);
|
||||
}
|
||||
if (_reorderBuffer != NULL)
|
||||
{
|
||||
delete _reorderBuffer;
|
||||
_reorderBuffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
WebRtc_Word32 RTPPlayer::Initialize(const ListWrapper& payloadList)
|
||||
{
|
||||
std::srand(321);
|
||||
for (int i=0; i < RAND_VEC_LENGTH; i++)
|
||||
{
|
||||
_randVec[i] = rand();
|
||||
}
|
||||
_randVecPos = 0;
|
||||
WebRtc_Word32 ret = _rtpModule.SetNACKStatus(kNackOff);
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
ret = _rtpModule.InitReceiver();
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
_rtpModule.InitSender();
|
||||
_rtpModule.SetRTCPStatus(kRtcpNonCompound);
|
||||
_rtpModule.SetTMMBRStatus(true);
|
||||
|
||||
ret = _rtpModule.RegisterIncomingDataCallback(_dataCallback);
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// Register payload types
|
||||
ListItem* item = payloadList.First();
|
||||
while (item != NULL)
|
||||
{
|
||||
PayloadCodecTuple* payloadType = static_cast<PayloadCodecTuple*>(item->GetItem());
|
||||
if (payloadType != NULL)
|
||||
{
|
||||
if (_rtpModule.RegisterReceivePayload(payloadType->name.c_str(), payloadType->payloadType) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
item = payloadList.Next(item);
|
||||
}
|
||||
if (ReadHeader() < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
memset(_nextPacket, 0, sizeof(_nextPacket));
|
||||
_nextPacketLength = ReadPacket(_nextPacket, &_nextRtpTime);
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebRtc_Word32 RTPPlayer::ReadHeader()
|
||||
{
|
||||
char firstline[FIRSTLINELEN];
|
||||
if (_rtpFile == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
fgets(firstline, FIRSTLINELEN, _rtpFile);
|
||||
if(strncmp(firstline,"#!rtpplay",9) == 0) {
|
||||
if(strncmp(firstline,"#!rtpplay1.0",12) != 0){
|
||||
printf("ERROR: wrong rtpplay version, must be 1.0\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (strncmp(firstline,"#!RTPencode",11) == 0) {
|
||||
if(strncmp(firstline,"#!RTPencode1.0",14) != 0){
|
||||
printf("ERROR: wrong RTPencode version, must be 1.0\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("ERROR: wrong file format of input file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
WebRtc_UWord32 start_sec;
|
||||
WebRtc_UWord32 start_usec;
|
||||
WebRtc_UWord32 source;
|
||||
WebRtc_UWord16 port;
|
||||
WebRtc_UWord16 padding;
|
||||
|
||||
fread(&start_sec, 4, 1, _rtpFile);
|
||||
start_sec=ntohl(start_sec);
|
||||
fread(&start_usec, 4, 1, _rtpFile);
|
||||
start_usec=ntohl(start_usec);
|
||||
fread(&source, 4, 1, _rtpFile);
|
||||
source=ntohl(source);
|
||||
fread(&port, 2, 1, _rtpFile);
|
||||
port=ntohs(port);
|
||||
fread(&padding, 2, 1, _rtpFile);
|
||||
padding=ntohs(padding);
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebRtc_UWord32 RTPPlayer::TimeUntilNextPacket() const
|
||||
{
|
||||
WebRtc_Word64 timeLeft = (_nextRtpTime - _firstPacketRtpTime) - (VCMTickTime::MillisecondTimestamp() - _firstPacketTimeMs);
|
||||
if (timeLeft < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return static_cast<WebRtc_UWord32>(timeLeft);
|
||||
}
|
||||
|
||||
WebRtc_Word32 RTPPlayer::NextPacket(const WebRtc_Word64 timeNow)
|
||||
{
|
||||
// Send any packets ready to be resent
|
||||
_lostPackets.Lock();
|
||||
ListItem* item = _lostPackets.First();
|
||||
_lostPackets.Unlock();
|
||||
while (item != NULL)
|
||||
{
|
||||
_lostPackets.Lock();
|
||||
RawRtpPacket* packet = static_cast<RawRtpPacket*>(item->GetItem());
|
||||
_lostPackets.Unlock();
|
||||
if (timeNow >= packet->resendTimeMs && packet->resendTimeMs != -1)
|
||||
{
|
||||
const WebRtc_UWord16 seqNo = (packet->rtpData[2] << 8) + packet->rtpData[3];
|
||||
printf("Resend: %u\n", seqNo);
|
||||
WebRtc_Word32 ret = SendPacket(packet->rtpData, packet->rtpLen);
|
||||
ListItem* itemToRemove = item;
|
||||
_lostPackets.Lock();
|
||||
item = _lostPackets.Next(item);
|
||||
_lostPackets.Erase(itemToRemove);
|
||||
delete packet;
|
||||
_lostPackets.Unlock();
|
||||
_resendPacketCount++;
|
||||
if (ret > 0)
|
||||
{
|
||||
_lostPackets.ResentPacket(seqNo);
|
||||
}
|
||||
else if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_lostPackets.Lock();
|
||||
item = _lostPackets.Next(item);
|
||||
_lostPackets.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// Send any packets from rtp file
|
||||
if (!_endOfFile && (TimeUntilNextPacket() == 0 || _firstPacket))
|
||||
{
|
||||
_rtpModule.Process();
|
||||
if (_firstPacket)
|
||||
{
|
||||
_firstPacketRtpTime = static_cast<WebRtc_Word64>(_nextRtpTime);
|
||||
_firstPacketTimeMs = VCMTickTime::MillisecondTimestamp();
|
||||
}
|
||||
if (_reordering && _reorderBuffer == NULL)
|
||||
{
|
||||
_reorderBuffer = new RawRtpPacket(reinterpret_cast<WebRtc_UWord8*>(_nextPacket), static_cast<WebRtc_UWord16>(_nextPacketLength));
|
||||
return 0;
|
||||
}
|
||||
WebRtc_Word32 ret = SendPacket(reinterpret_cast<WebRtc_UWord8*>(_nextPacket), static_cast<WebRtc_UWord16>(_nextPacketLength));
|
||||
if (_reordering && _reorderBuffer != NULL)
|
||||
{
|
||||
RawRtpPacket* rtpPacket = _reorderBuffer;
|
||||
_reorderBuffer = NULL;
|
||||
SendPacket(rtpPacket->rtpData, rtpPacket->rtpLen);
|
||||
delete rtpPacket;
|
||||
}
|
||||
_firstPacket = false;
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
_nextPacketLength = ReadPacket(_nextPacket, &_nextRtpTime);
|
||||
if (_nextPacketLength < 0)
|
||||
{
|
||||
_endOfFile = true;
|
||||
return 0;
|
||||
}
|
||||
else if (_nextPacketLength == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (_endOfFile && _lostPackets.NumberOfPacketsToResend() == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebRtc_Word32 RTPPlayer::SendPacket(WebRtc_UWord8* rtpData, WebRtc_UWord16 rtpLen)
|
||||
{
|
||||
if ((_randVec[(_randVecPos++) % RAND_VEC_LENGTH] + 1.0)/(RAND_MAX + 1.0) < _lossRate &&
|
||||
_noLossStartup < 0)
|
||||
{
|
||||
if (_nackEnabled)
|
||||
{
|
||||
const WebRtc_UWord16 seqNo = (rtpData[2] << 8) + rtpData[3];
|
||||
printf("Throw: %u\n", seqNo);
|
||||
_lostPackets.AddPacket(rtpData, rtpLen);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WebRtc_Word32 ret = _rtpModule.IncomingPacket(rtpData, rtpLen);
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (_noLossStartup >= 0)
|
||||
{
|
||||
_noLossStartup--;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
WebRtc_Word32 RTPPlayer::ReadPacket(WebRtc_Word16* rtpdata, WebRtc_UWord32* offset)
|
||||
{
|
||||
WebRtc_UWord16 length, plen;
|
||||
|
||||
if (fread(&length,2,1,_rtpFile)==0)
|
||||
return(-1);
|
||||
length=ntohs(length);
|
||||
|
||||
if (fread(&plen,2,1,_rtpFile)==0)
|
||||
return(-1);
|
||||
plen=ntohs(plen);
|
||||
|
||||
if (fread(offset,4,1,_rtpFile)==0)
|
||||
return(-1);
|
||||
*offset=ntohl(*offset);
|
||||
|
||||
// Use length here because a plen of 0 specifies rtcp
|
||||
length = (WebRtc_UWord16) (length - HDR_SIZE);
|
||||
if (fread((unsigned short *) rtpdata,1,length,_rtpFile) != length)
|
||||
return(-1);
|
||||
|
||||
#ifdef JUNK_DATA
|
||||
// destroy the RTP payload with random data
|
||||
if (plen > 12) { // ensure that we have more than just a header
|
||||
for ( int ix = 12; ix < plen; ix=ix+2 ) {
|
||||
rtpdata[ix>>1] = (short) (rtpdata[ix>>1] + (short) rand());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return plen;
|
||||
}
|
||||
|
||||
WebRtc_Word32 RTPPlayer::SimulatePacketLoss(float lossRate, bool enableNack, WebRtc_UWord32 rttMs)
|
||||
{
|
||||
_nackEnabled = enableNack;
|
||||
_lossRate = lossRate;
|
||||
_rttMs = rttMs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebRtc_Word32 RTPPlayer::SetReordering(bool enabled)
|
||||
{
|
||||
_reordering = enabled;
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebRtc_Word32 RTPPlayer::ResendPackets(const WebRtc_UWord16* sequenceNumbers, WebRtc_UWord16 length)
|
||||
{
|
||||
if (sequenceNumbers == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
for (int i=0; i < length; i++)
|
||||
{
|
||||
_lostPackets.SetResendTime(sequenceNumbers[i], VCMTickTime::MillisecondTimestamp() + _rttMs);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RTPPlayer::Print() const
|
||||
{
|
||||
printf("Lost packets: %u, resent packets: %u\n", _lostPackets.TotalNumberOfLosses(), _resendPacketCount);
|
||||
printf("Packets still lost: %u\n", _lostPackets.GetSize());
|
||||
printf("Packets waiting to be resent: %u\n", _lostPackets.NumberOfPacketsToResend());
|
||||
printf("Sequence numbers:\n");
|
||||
ListItem* item = _lostPackets.First();
|
||||
while (item != NULL)
|
||||
{
|
||||
RawRtpPacket* packet = static_cast<RawRtpPacket*>(item->GetItem());
|
||||
const WebRtc_UWord16 seqNo = (packet->rtpData[2] << 8) + packet->rtpData[3];
|
||||
printf("%u, ", seqNo);
|
||||
item = _lostPackets.Next(item);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
106
modules/video_coding/main/test/rtp_player.h
Normal file
106
modules/video_coding/main/test/rtp_player.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_RTP_PLAYER_H_
|
||||
#define WEBRTC_MODULES_VIDEO_CODING_TEST_RTP_PLAYER_H_
|
||||
|
||||
#include "typedefs.h"
|
||||
#include "rtp_rtcp.h"
|
||||
#include "list_wrapper.h"
|
||||
#include "critical_section_wrapper.h"
|
||||
#include "video_coding_defines.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#define HDR_SIZE 8 // rtpplay packet header size in bytes
|
||||
#define FIRSTLINELEN 40
|
||||
#define RAND_VEC_LENGTH 4096
|
||||
|
||||
struct RawRtpPacket
|
||||
{
|
||||
public:
|
||||
RawRtpPacket(WebRtc_UWord8* data, WebRtc_UWord16 len);
|
||||
~RawRtpPacket();
|
||||
|
||||
WebRtc_UWord8* rtpData;
|
||||
WebRtc_UWord16 rtpLen;
|
||||
WebRtc_Word64 resendTimeMs;
|
||||
};
|
||||
|
||||
class LostPackets : public webrtc::ListWrapper
|
||||
{
|
||||
public:
|
||||
LostPackets();
|
||||
~LostPackets();
|
||||
|
||||
WebRtc_UWord32 AddPacket(WebRtc_UWord8* rtpData, WebRtc_UWord16 rtpLen);
|
||||
WebRtc_UWord32 SetResendTime(WebRtc_UWord16 sequenceNumber, WebRtc_Word64 resendTime);
|
||||
WebRtc_UWord32 TotalNumberOfLosses() const { return _lossCount; };
|
||||
WebRtc_UWord32 NumberOfPacketsToResend() const;
|
||||
void ResentPacket(WebRtc_UWord16 seqNo);
|
||||
void Lock() {_critSect.Enter();};
|
||||
void Unlock() {_critSect.Leave();};
|
||||
private:
|
||||
webrtc::CriticalSectionWrapper& _critSect;
|
||||
WebRtc_UWord32 _lossCount;
|
||||
FILE* _debugFile;
|
||||
};
|
||||
|
||||
struct PayloadCodecTuple
|
||||
{
|
||||
PayloadCodecTuple(WebRtc_UWord8 plType, std::string codecName, webrtc::VideoCodecType type) :
|
||||
name(codecName), payloadType(plType), codecType(type) {};
|
||||
const std::string name;
|
||||
const WebRtc_UWord8 payloadType;
|
||||
const webrtc::VideoCodecType codecType;
|
||||
};
|
||||
|
||||
class RTPPlayer : public webrtc::VCMPacketRequestCallback
|
||||
{
|
||||
public:
|
||||
RTPPlayer(const char* filename, webrtc::RtpData* callback);
|
||||
virtual ~RTPPlayer();
|
||||
|
||||
WebRtc_Word32 Initialize(const webrtc::ListWrapper& payloadList);
|
||||
WebRtc_Word32 NextPacket(const WebRtc_Word64 timeNow);
|
||||
WebRtc_UWord32 TimeUntilNextPacket() const;
|
||||
WebRtc_Word32 SimulatePacketLoss(float lossRate, bool enableNack = false, WebRtc_UWord32 rttMs = 0);
|
||||
WebRtc_Word32 SetReordering(bool enabled);
|
||||
WebRtc_Word32 ResendPackets(const WebRtc_UWord16* sequenceNumbers, WebRtc_UWord16 length);
|
||||
void Print() const;
|
||||
|
||||
private:
|
||||
WebRtc_Word32 SendPacket(WebRtc_UWord8* rtpData, WebRtc_UWord16 rtpLen);
|
||||
WebRtc_Word32 ReadPacket(WebRtc_Word16* rtpdata, WebRtc_UWord32* offset);
|
||||
WebRtc_Word32 ReadHeader();
|
||||
FILE* _rtpFile;
|
||||
webrtc::RtpRtcp& _rtpModule;
|
||||
WebRtc_UWord32 _nextRtpTime;
|
||||
webrtc::RtpData* _dataCallback;
|
||||
bool _firstPacket;
|
||||
float _lossRate;
|
||||
bool _nackEnabled;
|
||||
LostPackets _lostPackets;
|
||||
WebRtc_UWord32 _resendPacketCount;
|
||||
WebRtc_Word32 _noLossStartup;
|
||||
bool _endOfFile;
|
||||
WebRtc_UWord32 _rttMs;
|
||||
WebRtc_Word64 _firstPacketRtpTime;
|
||||
WebRtc_Word64 _firstPacketTimeMs;
|
||||
RawRtpPacket* _reorderBuffer;
|
||||
bool _reordering;
|
||||
WebRtc_Word16 _nextPacket[8000];
|
||||
WebRtc_Word32 _nextPacketLength;
|
||||
int _randVec[RAND_VEC_LENGTH];
|
||||
int _randVecPos;
|
||||
};
|
||||
|
||||
#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_RTP_PLAYER_H_
|
||||
30
modules/video_coding/main/test/subfigure.m
Normal file
30
modules/video_coding/main/test/subfigure.m
Normal file
@@ -0,0 +1,30 @@
|
||||
function H = subfigure(m, n, p)
|
||||
%
|
||||
% H = SUBFIGURE(m, n, p)
|
||||
%
|
||||
% Create a new figure window and adjust position and size such that it will
|
||||
% become the p-th tile in an m-by-n matrix of windows. (The interpretation of
|
||||
% m, n, and p is the same as for SUBPLOT.
|
||||
%
|
||||
% Henrik Lundin, 2009-01-19
|
||||
%
|
||||
|
||||
|
||||
h = figure;
|
||||
|
||||
[j, i] = ind2sub([n m], p);
|
||||
scrsz = get(0,'ScreenSize'); % get screen size
|
||||
%scrsz = [1, 1, 1600, 1200];
|
||||
|
||||
taskbarSize = 58;
|
||||
windowbarSize = 68;
|
||||
windowBorder = 4;
|
||||
|
||||
scrsz(2) = scrsz(2) + taskbarSize;
|
||||
scrsz(4) = scrsz(4) - taskbarSize;
|
||||
|
||||
set(h, 'position', [(j-1)/n * scrsz(3) + scrsz(1) + windowBorder,...
|
||||
(m-i)/m * scrsz(4) + scrsz(2) + windowBorder, ...
|
||||
scrsz(3)/n - (windowBorder + windowBorder),...
|
||||
scrsz(4)/m - (windowbarSize + windowBorder + windowBorder)]);
|
||||
|
||||
45
modules/video_coding/main/test/test_macros.h
Normal file
45
modules/video_coding/main/test/test_macros.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef VCM_TEST_MACROS_H
|
||||
#define VCM_TEST_MACROS_H
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
static int vcmMacrosTests = 0;
|
||||
static int vcmMacrosErrors = 0;
|
||||
|
||||
#define PRINT_ERR_MSG(msg) \
|
||||
do { \
|
||||
fprintf(stderr, "Error at line %i of %s\n%s", \
|
||||
__LINE__, __FILE__, msg); \
|
||||
} while(0)
|
||||
|
||||
#define TEST(expr) \
|
||||
do { \
|
||||
vcmMacrosTests++; \
|
||||
if (!(expr)) { \
|
||||
PRINT_ERR_MSG("Assertion failed: " #expr "\n\n"); \
|
||||
vcmMacrosErrors++; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define TEST_EXIT_ON_FAIL(expr) \
|
||||
do { \
|
||||
vcmMacrosTests++; \
|
||||
if (!(expr)) { \
|
||||
PRINT_ERR_MSG("Assertion failed: " #expr "\nExiting...\n\n"); \
|
||||
vcmMacrosErrors++; \
|
||||
exit(EXIT_FAILURE); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
725
modules/video_coding/main/test/test_util.cc
Normal file
725
modules/video_coding/main/test/test_util.cc
Normal file
@@ -0,0 +1,725 @@
|
||||
/*
|
||||
* 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 "test_util.h"
|
||||
#include "rtp_dump.h"
|
||||
#include <cmath>
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
/******************************
|
||||
* VCMEncodeCompleteCallback
|
||||
*****************************/
|
||||
// Basic callback implementation
|
||||
// passes the encoded frame directly to the encoder
|
||||
// Packetization callback implmentation
|
||||
VCMEncodeCompleteCallback::VCMEncodeCompleteCallback(FILE* encodedFile):
|
||||
_seqNo(0),
|
||||
_encodedFile(encodedFile),
|
||||
_encodedBytes(0),
|
||||
_VCMReceiver(NULL),
|
||||
_encodeComplete(false),
|
||||
_width(0),
|
||||
_height(0),
|
||||
_codecType(kRTPVideoNoVideo),
|
||||
_layerPacketId(1)
|
||||
{
|
||||
//
|
||||
}
|
||||
VCMEncodeCompleteCallback::~VCMEncodeCompleteCallback()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
VCMEncodeCompleteCallback::RegisterTransportCallback(VCMPacketizationCallback* transport)
|
||||
{
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
VCMEncodeCompleteCallback::SendData(const FrameType frameType,
|
||||
const WebRtc_UWord8 payloadType,
|
||||
const WebRtc_UWord32 timeStamp,
|
||||
const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord32 payloadSize,
|
||||
const RTPFragmentationHeader& fragmentationHeader)
|
||||
{
|
||||
// will call the VCMReceiver input packet
|
||||
_frameType = frameType;
|
||||
// writing encodedData into file
|
||||
fwrite(payloadData, 1, payloadSize, _encodedFile);
|
||||
WebRtcRTPHeader rtpInfo;
|
||||
rtpInfo.header.markerBit = true; // end of frame
|
||||
rtpInfo.type.Video.isFirstPacket = true;
|
||||
rtpInfo.type.Video.codec = _codecType;
|
||||
switch (_codecType)
|
||||
{
|
||||
case webrtc::kRTPVideoH263:
|
||||
rtpInfo.type.Video.codecHeader.H263.bits = false;
|
||||
rtpInfo.type.Video.codecHeader.H263.independentlyDecodable = false;
|
||||
rtpInfo.type.Video.height = (WebRtc_UWord16)_height;
|
||||
rtpInfo.type.Video.width = (WebRtc_UWord16)_width;
|
||||
break;
|
||||
}
|
||||
|
||||
rtpInfo.header.payloadType = payloadType;
|
||||
rtpInfo.header.sequenceNumber = _seqNo++;
|
||||
rtpInfo.header.ssrc = 0;
|
||||
rtpInfo.header.timestamp = timeStamp;
|
||||
rtpInfo.frameType = frameType;
|
||||
// Size should also be received from that table, since the payload type
|
||||
// defines the size.
|
||||
|
||||
_encodedBytes += payloadSize;
|
||||
// directly to receiver
|
||||
_VCMReceiver->IncomingPacket(payloadData, payloadSize, rtpInfo);
|
||||
_encodeComplete = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
float
|
||||
VCMEncodeCompleteCallback::EncodedBytes()
|
||||
{
|
||||
return _encodedBytes;
|
||||
}
|
||||
|
||||
bool
|
||||
VCMEncodeCompleteCallback::EncodeComplete()
|
||||
{
|
||||
if (_encodeComplete)
|
||||
{
|
||||
_encodeComplete = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
VCMEncodeCompleteCallback::Initialize()
|
||||
{
|
||||
_encodeComplete = false;
|
||||
_encodedBytes = 0;
|
||||
_seqNo = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
VCMEncodeCompleteCallback::ResetByteCount()
|
||||
{
|
||||
_encodedBytes = 0;
|
||||
}
|
||||
|
||||
/**********************************/
|
||||
/* VCMRTPEncodeCompleteCallback /
|
||||
/********************************/
|
||||
// Encode Complete callback implementation
|
||||
// passes the encoded frame via the RTP module to the decoder
|
||||
// Packetization callback implmentation
|
||||
|
||||
WebRtc_Word32
|
||||
VCMRTPEncodeCompleteCallback::SendData(const FrameType frameType,
|
||||
const WebRtc_UWord8 payloadType,
|
||||
const WebRtc_UWord32 timeStamp,
|
||||
const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord32 payloadSize,
|
||||
const RTPFragmentationHeader& fragmentationHeader)
|
||||
{
|
||||
_frameType = frameType;
|
||||
_encodedBytes+= payloadSize;
|
||||
_encodeComplete = true;
|
||||
//printf("encoded = %d Bytes\n", payloadSize);
|
||||
return _RTPModule->SendOutgoingData(frameType, payloadType, timeStamp, payloadData, payloadSize, &fragmentationHeader);
|
||||
}
|
||||
|
||||
float
|
||||
VCMRTPEncodeCompleteCallback::EncodedBytes()
|
||||
{
|
||||
// only good for one call - after which will reset value;
|
||||
float tmp = _encodedBytes;
|
||||
_encodedBytes = 0;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool
|
||||
VCMRTPEncodeCompleteCallback::EncodeComplete()
|
||||
{
|
||||
if (_encodeComplete)
|
||||
{
|
||||
_encodeComplete = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Decoded Frame Callback Implmentation
|
||||
|
||||
WebRtc_Word32
|
||||
VCMDecodeCompleteCallback::FrameToRender(VideoFrame& videoFrame)
|
||||
{
|
||||
fwrite(videoFrame.Buffer(), 1, videoFrame.Length(), _decodedFile);
|
||||
_decodedBytes+= videoFrame.Length();
|
||||
// keeping last decoded frame
|
||||
_lastDecodedFrame.VerifyAndAllocate(videoFrame.Size());
|
||||
_lastDecodedFrame.CopyFrame(videoFrame.Size(), videoFrame.Buffer());
|
||||
_lastDecodedFrame.SetHeight(videoFrame.Height());
|
||||
_lastDecodedFrame.SetWidth(videoFrame.Width());
|
||||
_lastDecodedFrame.SetTimeStamp(videoFrame.TimeStamp());
|
||||
|
||||
return VCM_OK;
|
||||
}
|
||||
|
||||
int
|
||||
VCMDecodeCompleteCallback::PSNRLastFrame(const VideoFrame& sourceFrame, double *YPSNRptr)
|
||||
{
|
||||
double mse = 0.0;
|
||||
double mseLogSum = 0.0;
|
||||
|
||||
WebRtc_Word32 frameBytes = sourceFrame.Height() * sourceFrame.Width(); // only Y
|
||||
WebRtc_UWord8 *ref = sourceFrame.Buffer();
|
||||
if (_lastDecodedFrame.Height() == 0)
|
||||
{
|
||||
*YPSNRptr = 0;
|
||||
return 0; // no new decoded frames
|
||||
}
|
||||
WebRtc_UWord8 *test = _lastDecodedFrame.Buffer();
|
||||
for( int k = 0; k < frameBytes; k++ )
|
||||
{
|
||||
mse += (test[k] - ref[k]) * (test[k] - ref[k]);
|
||||
}
|
||||
|
||||
// divide by number of pixels
|
||||
mse /= (double) (frameBytes);
|
||||
|
||||
// accumulate for total average
|
||||
mseLogSum += std::log10( mse );
|
||||
|
||||
*YPSNRptr = 20.0 * std::log10(255.0) - 10.0 * mseLogSum; // for only 1 frame
|
||||
|
||||
_lastDecodedFrame.Free();
|
||||
_lastDecodedFrame.SetHeight(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
VCMDecodeCompleteCallback::DecodedBytes()
|
||||
{
|
||||
return _decodedBytes;
|
||||
}
|
||||
|
||||
RTPSendCompleteCallback::RTPSendCompleteCallback(RtpRtcp* rtp, const char* filename):
|
||||
_rtp(rtp),
|
||||
_sendCount(0),
|
||||
_lossPct(0),
|
||||
_rtpDump(NULL)
|
||||
{
|
||||
if (filename != NULL)
|
||||
{
|
||||
_rtpDump = RtpDump::CreateRtpDump();
|
||||
_rtpDump->Start(filename);
|
||||
}
|
||||
}
|
||||
RTPSendCompleteCallback::~RTPSendCompleteCallback()
|
||||
{
|
||||
if (_rtpDump != NULL)
|
||||
{
|
||||
_rtpDump->Stop();
|
||||
RtpDump::DestroyRtpDump(_rtpDump);
|
||||
}
|
||||
}
|
||||
int
|
||||
RTPSendCompleteCallback::SendPacket(int channel, const void *data, int len)
|
||||
{
|
||||
_sendCount++;
|
||||
// Packet Loss - randomly drop %loss packets
|
||||
// don't drop I-frame packets
|
||||
if(PacketLoss(_lossPct) && (_sendCount > 12))
|
||||
{
|
||||
// drop
|
||||
//printf("\tDrop packet, sendCount = %d\n", _sendCount);
|
||||
return len;
|
||||
}
|
||||
if (_rtpDump != NULL)
|
||||
{
|
||||
if (_rtpDump->DumpPacket((const WebRtc_UWord8*)data, len) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if(_rtp->IncomingPacket((const WebRtc_UWord8*)data, len) == 0)
|
||||
{
|
||||
return len;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
RTPSendCompleteCallback::SendRTCPPacket(int channel, const void *data, int len)
|
||||
{
|
||||
if(_rtp->IncomingPacket((const WebRtc_UWord8*)data, len) == 0)
|
||||
{
|
||||
return len;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
RTPSendCompleteCallback::SetLossPct(double lossPct)
|
||||
{
|
||||
_lossPct = lossPct;
|
||||
return;
|
||||
}
|
||||
|
||||
bool
|
||||
RTPSendCompleteCallback::PacketLoss(double lossPct)
|
||||
{
|
||||
double randVal = (std::rand() + 1.0)/(RAND_MAX + 1.0);
|
||||
return randVal < lossPct/100;
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
PacketRequester::ResendPackets(const WebRtc_UWord16* sequenceNumbers, WebRtc_UWord16 length)
|
||||
{
|
||||
return _rtp.SendNACK(sequenceNumbers, length);
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
PSNRfromFiles(const WebRtc_Word8 *refFileName, const WebRtc_Word8 *testFileName, WebRtc_Word32 width, WebRtc_Word32 height, double *YPSNRptr)
|
||||
{
|
||||
FILE *refFp = fopen(refFileName, "rb");
|
||||
if( refFp == NULL ) {
|
||||
// cannot open reference file
|
||||
fprintf(stderr, "Cannot open file %s\n", refFileName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *testFp = fopen(testFileName, "rb");
|
||||
if( testFp == NULL ) {
|
||||
// cannot open test file
|
||||
fprintf(stderr, "Cannot open file %s\n", testFileName);
|
||||
return -2;
|
||||
}
|
||||
|
||||
double mse = 0.0;
|
||||
double mseLogSum = 0.0;
|
||||
WebRtc_Word32 frames = 0;
|
||||
|
||||
WebRtc_Word32 frameBytes = 3*width*height/2; // bytes in one frame I420
|
||||
WebRtc_UWord8 *ref = new WebRtc_UWord8[frameBytes]; // space for one frame I420
|
||||
WebRtc_UWord8 *test = new WebRtc_UWord8[frameBytes]; // space for one frame I420
|
||||
|
||||
WebRtc_Word32 refBytes = (WebRtc_Word32) fread(ref, 1, frameBytes, refFp);
|
||||
WebRtc_Word32 testBytes = (WebRtc_Word32) fread(test, 1, frameBytes, testFp);
|
||||
|
||||
while( refBytes == frameBytes && testBytes == frameBytes )
|
||||
{
|
||||
mse = 0.0;
|
||||
|
||||
int sh = 8;//boundary offset
|
||||
for( int k2 = sh; k2 < height-sh;k2++)
|
||||
for( int k = sh; k < width-sh;k++)
|
||||
{
|
||||
int kk = k2*width + k;
|
||||
mse += (test[kk] - ref[kk]) * (test[kk] - ref[kk]);
|
||||
}
|
||||
|
||||
// divide by number of pixels
|
||||
mse /= (double) (width * height);
|
||||
|
||||
// accumulate for total average
|
||||
mseLogSum += std::log10( mse );
|
||||
frames++;
|
||||
|
||||
refBytes = (int) fread(ref, 1, frameBytes, refFp);
|
||||
testBytes = (int) fread(test, 1, frameBytes, testFp);
|
||||
}
|
||||
// for identical reproduction:
|
||||
if (mse == 0)
|
||||
{
|
||||
*YPSNRptr = 48;
|
||||
}
|
||||
else
|
||||
{
|
||||
*YPSNRptr = 20.0 * std::log10(255.0) - 10.0 * mseLogSum / frames;
|
||||
}
|
||||
|
||||
|
||||
delete [] ref;
|
||||
delete [] test;
|
||||
|
||||
fclose(refFp);
|
||||
fclose(testFp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
SSIMfromFiles(const WebRtc_Word8 *refFileName, const WebRtc_Word8 *testFileName, WebRtc_Word32 width, WebRtc_Word32 height, double *SSIMptr)
|
||||
{
|
||||
FILE *refFp = fopen(refFileName, "rb");
|
||||
if( refFp == NULL ) {
|
||||
// cannot open reference file
|
||||
fprintf(stderr, "Cannot open file %s\n", refFileName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *testFp = fopen(testFileName, "rb");
|
||||
if( testFp == NULL ) {
|
||||
// cannot open test file
|
||||
fprintf(stderr, "Cannot open file %s\n", testFileName);
|
||||
return -2;
|
||||
}
|
||||
|
||||
int frames = 0;
|
||||
|
||||
int frameBytes = 3*width*height/2; // bytes in one frame I420
|
||||
unsigned char *ref = new unsigned char[frameBytes]; // space for one frame I420
|
||||
unsigned char *test = new unsigned char[frameBytes]; // space for one frame I420
|
||||
|
||||
int refBytes = (int) fread(ref, 1, frameBytes, refFp);
|
||||
int testBytes = (int) fread(test, 1, frameBytes, testFp);
|
||||
|
||||
float *righMostColumnAvgTest = new float[width];
|
||||
float *righMostColumnAvgRef = new float[width];
|
||||
float *righMostColumnContrastTest = new float[width];
|
||||
float *righMostColumnContrastRef = new float[width];
|
||||
float *righMostColumnCrossCorr = new float[width];
|
||||
|
||||
float term1,term2,term3,term4,term5;
|
||||
|
||||
//
|
||||
// SSIM: variable definition, window function, initialization
|
||||
int window = 10;
|
||||
//
|
||||
int flag_window = 0; //0 and 1 for uniform window filter, 2 for gaussian window
|
||||
//
|
||||
float variance_window = 2.0; //variance for window function
|
||||
float ssimFilter[121]; //2d window filter: typically 11x11 = (window+1)*(window+1)
|
||||
//statistics per column of window (#columns = window+1), 0 element for avg over all columns
|
||||
float avgTest[12];
|
||||
float avgRef[12];
|
||||
float contrastTest[12];
|
||||
float contrastRef[12];
|
||||
float crossCorr[12];
|
||||
//
|
||||
//offsets for stability
|
||||
float offset1 = 1.0f; //0.1
|
||||
float offset2 = 1.0f; //0.1
|
||||
//for Guassian window: settings from paper:
|
||||
//float offset1 = 6.0f; // ~ (K1*L)^2 , K1 = 0.01
|
||||
//float offset2 = 58.0f; // ~ (K1*L)^2 , K2 = 0.03
|
||||
|
||||
|
||||
float offset3 = offset2/2;
|
||||
//
|
||||
//define window for SSIM: take uniform filter for now
|
||||
float sumfil = 0.0;
|
||||
int nn=-1;
|
||||
for(int j=-window/2;j<=window/2;j++)
|
||||
for(int i=-window/2;i<=window/2;i++)
|
||||
{
|
||||
nn+=1;
|
||||
if (flag_window != 2)
|
||||
ssimFilter[nn] = 1.0;
|
||||
else
|
||||
{
|
||||
float dist = (float)(i*i) + (float)(j*j);
|
||||
float tmp = 0.5f*dist/variance_window;
|
||||
ssimFilter[nn] = exp(-tmp);
|
||||
}
|
||||
sumfil +=ssimFilter[nn];
|
||||
}
|
||||
//normalize window
|
||||
nn=-1;
|
||||
for(int j=-window/2;j<=window/2;j++)
|
||||
for(int i=-window/2;i<=window/2;i++)
|
||||
{
|
||||
nn+=1;
|
||||
ssimFilter[nn] = ssimFilter[nn]/((float)sumfil);
|
||||
}
|
||||
//
|
||||
float ssimScene = 0.0; //avgerage SSIM for sequence
|
||||
//
|
||||
//SSIM: done with variables and defintion
|
||||
//
|
||||
|
||||
int sh = 8; //boundary offset
|
||||
|
||||
while( refBytes == frameBytes && testBytes == frameBytes )
|
||||
{
|
||||
float ssimFrame = 0.0;
|
||||
|
||||
int numPixels = 0;
|
||||
|
||||
//skip over pixels vertically and horizontally
|
||||
//for window cases 1 and 2
|
||||
int skipH = 2;
|
||||
int skipV = 2;
|
||||
|
||||
//uniform window case, with window computation updated for each pixel horiz and vert: can't skip pixels for this case
|
||||
if (flag_window == 0)
|
||||
{
|
||||
skipH = 1;
|
||||
skipV = 1;
|
||||
}
|
||||
for(int i=sh;i<height-sh;i+=skipV)
|
||||
for(int j=sh;j<width-sh;j+=skipH)
|
||||
{
|
||||
avgTest[0] = 0.0;
|
||||
avgRef[0] = 0.0;
|
||||
contrastTest[0] = 0.0;
|
||||
contrastRef[0] = 0.0;
|
||||
crossCorr[0] = 0.0;
|
||||
|
||||
numPixels +=1;
|
||||
|
||||
if (flag_window > 0 )
|
||||
{
|
||||
//initialize statistics
|
||||
avgTest[0] = 0.0;
|
||||
avgRef[0] = 0.0;
|
||||
contrastTest[0] = 0.0;
|
||||
contrastRef[0] = 0.0;
|
||||
crossCorr[0] = 0.0;
|
||||
|
||||
int nn=-1;
|
||||
//compute contrast and correlation
|
||||
//windows are symmetrics
|
||||
for(int jj=-window/2;jj<=window/2;jj++)
|
||||
for(int ii=-window/2;ii<=window/2;ii++)
|
||||
{
|
||||
nn+=1;
|
||||
int i2 = i+ii;
|
||||
int j2 = j+jj;
|
||||
float tmp1 = (float)test[i2*width+j2];
|
||||
float tmp2 = (float)ref[i2*width+j2];
|
||||
|
||||
term1 = tmp1;
|
||||
term2 = tmp2;
|
||||
term3 = tmp1*tmp1;
|
||||
term4 = tmp2*tmp2;
|
||||
term5 = tmp1*tmp2;
|
||||
|
||||
//local average of each signal
|
||||
avgTest[0] += ssimFilter[nn]*term1;
|
||||
avgRef[0] += ssimFilter[nn]*term2;
|
||||
//local correlation/contrast of each signal
|
||||
contrastTest[0] += ssimFilter[nn]*term3;
|
||||
contrastRef[0] += ssimFilter[nn]*term4;
|
||||
//local cross correlation
|
||||
crossCorr[0] += ssimFilter[nn]*term5;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
//for uniform window case == 0: only need to loop over whole window for first row and column, and then shift/update
|
||||
if (j == sh || i == sh)
|
||||
{
|
||||
//initialize statistics
|
||||
for(int k=0;k<window+2;k++)
|
||||
{
|
||||
avgTest[k] = 0.0;
|
||||
avgRef[k] = 0.0;
|
||||
contrastTest[k] = 0.0;
|
||||
contrastRef[k] = 0.0;
|
||||
crossCorr[k] = 0.0;
|
||||
}
|
||||
|
||||
int nn=-1;
|
||||
//compute contrast and correlation
|
||||
//windows are symmetrics
|
||||
for(int jj=-window/2;jj<=window/2;jj++)
|
||||
for(int ii=-window/2;ii<=window/2;ii++)
|
||||
{
|
||||
nn+=1;
|
||||
int i2 = i+ii;
|
||||
int j2 = j+jj;
|
||||
float tmp1 = (float)test[i2*width+j2];
|
||||
float tmp2 = (float)ref[i2*width+j2];
|
||||
|
||||
term1 = tmp1;
|
||||
term2 = tmp2;
|
||||
term3 = tmp1*tmp1;
|
||||
term4 = tmp2*tmp2;
|
||||
term5 = tmp1*tmp2;
|
||||
|
||||
//local average of each signal
|
||||
avgTest[jj+window/2+1] += term1;
|
||||
avgRef[jj+window/2+1] += term2;
|
||||
//local correlation/contrast of each signal
|
||||
contrastTest[jj+window/2+1] += term3;
|
||||
contrastRef[jj+window/2+1] += term4;
|
||||
//local cross correlation
|
||||
crossCorr[jj+window/2+1] += term5;
|
||||
|
||||
}
|
||||
|
||||
//normalize
|
||||
for(int k=1;k<window+2;k++)
|
||||
{
|
||||
avgTest[k] = ssimFilter[0]*avgTest[k];
|
||||
avgRef[k] = ssimFilter[0]*avgRef[k];
|
||||
contrastTest[k] = ssimFilter[0]*contrastTest[k];
|
||||
contrastRef[k] = ssimFilter[0]*contrastRef[k];
|
||||
crossCorr[k] = ssimFilter[0]*crossCorr[k];
|
||||
}
|
||||
|
||||
}
|
||||
//for all other pixels, update window filter computation
|
||||
else
|
||||
{
|
||||
//shift statistics horiz.
|
||||
for(int k=1;k<window+1;k++)
|
||||
{
|
||||
avgTest[k]=avgTest[k+1];
|
||||
avgRef[k]=avgRef[k+1];
|
||||
contrastTest[k] = contrastTest[k+1];
|
||||
contrastRef[k] = contrastRef[k+1];
|
||||
crossCorr[k] = crossCorr[k+1];
|
||||
}
|
||||
|
||||
//compute statistics for last column
|
||||
//update right-most column, by updating with bottom pixel contribution
|
||||
int j2 = j + window/2; //last column of window
|
||||
int i2 = i + window/2; //last window pixel of column
|
||||
int ix = i - window/2 - 1; //last window pixel of top neighboring pixel
|
||||
float tmp1 = (float)test[i2*width+j2];
|
||||
float tmp2 = (float)ref[i2*width+j2];
|
||||
float tmp1x = (float)test[ix*width+j2];
|
||||
float tmp2x = (float)ref[ix*width+j2];
|
||||
|
||||
avgTest[window+1] = righMostColumnAvgTest[j] + ssimFilter[0]*(tmp1 - tmp1x);
|
||||
avgRef[window+1] = righMostColumnAvgRef[j] + ssimFilter[0]*(tmp2 - tmp2x);
|
||||
contrastTest[window+1] = righMostColumnContrastTest[j] + ssimFilter[0]*(tmp1*tmp1 - tmp1x*tmp1x);
|
||||
contrastRef[window+1] = righMostColumnContrastRef[j] + ssimFilter[0]*(tmp2*tmp2 - tmp2x*tmp2x);
|
||||
crossCorr[window+1] = righMostColumnCrossCorr[j] + ssimFilter[0]*(tmp1*tmp2 - tmp1x*tmp2x);
|
||||
}
|
||||
|
||||
//sum over all columns
|
||||
for(int k=1;k<window+2;k++)
|
||||
{
|
||||
avgTest[0] += avgTest[k];
|
||||
avgRef[0] += avgRef[k];
|
||||
contrastTest[0] += contrastTest[k];
|
||||
contrastRef[0] += contrastRef[k];
|
||||
crossCorr[0] += crossCorr[k];
|
||||
}
|
||||
|
||||
//
|
||||
righMostColumnAvgTest[j] = avgTest[window+1];
|
||||
righMostColumnAvgRef[j] = avgRef[window+1];
|
||||
righMostColumnContrastTest[j] = contrastTest[window+1];
|
||||
righMostColumnContrastRef[j] = contrastRef[window+1];
|
||||
righMostColumnCrossCorr[j] = crossCorr[window+1];
|
||||
//
|
||||
|
||||
} //end of window = 0 case
|
||||
|
||||
float tmp1 = (contrastTest[0] - avgTest[0]*avgTest[0]);
|
||||
if (tmp1 < 0.0) tmp1 = 0.0;
|
||||
contrastTest[0] = sqrt(tmp1);
|
||||
float tmp2 = (contrastRef[0] - avgRef[0]*avgRef[0]);
|
||||
if (tmp2 < 0.0) tmp2 = 0.0;
|
||||
contrastRef[0] = sqrt(tmp2);
|
||||
crossCorr[0] = crossCorr[0] - avgTest[0]*avgRef[0];
|
||||
|
||||
float ssimCorrCoeff = (crossCorr[0]+offset3)/(contrastTest[0]*contrastRef[0] + offset3);
|
||||
float ssimLuminance = (2*avgTest[0]*avgRef[0]+offset1)/(avgTest[0]*avgTest[0] + avgRef[0]*avgRef[0] + offset1);
|
||||
float ssimContrast = (2*contrastTest[0]*contrastRef[0]+offset2)/(contrastTest[0]*contrastTest[0] + contrastRef[0]*contrastRef[0] + offset2);
|
||||
|
||||
float ssimPixel = ssimCorrCoeff * ssimLuminance * ssimContrast;
|
||||
ssimFrame += ssimPixel;
|
||||
|
||||
} //done with ssim computation
|
||||
|
||||
ssimFrame = ssimFrame / (numPixels);
|
||||
//printf("***SSIM for frame ***%f \n",ssimFrame);
|
||||
ssimScene += ssimFrame;
|
||||
//
|
||||
//SSIM: done with SSIM computation
|
||||
//
|
||||
|
||||
frames++;
|
||||
|
||||
refBytes = (int) fread(ref, 1, frameBytes, refFp);
|
||||
testBytes = (int) fread(test, 1, frameBytes, testFp);
|
||||
|
||||
}
|
||||
|
||||
//SSIM: normalize/average for sequence
|
||||
ssimScene = ssimScene / frames;
|
||||
*SSIMptr = ssimScene;
|
||||
|
||||
|
||||
delete [] ref;
|
||||
delete [] test;
|
||||
|
||||
delete [] righMostColumnAvgTest;
|
||||
delete [] righMostColumnAvgRef;
|
||||
delete [] righMostColumnContrastTest;
|
||||
delete [] righMostColumnContrastRef;
|
||||
delete [] righMostColumnCrossCorr;
|
||||
|
||||
|
||||
fclose(refFp);
|
||||
fclose(testFp);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
RTPVideoCodecTypes
|
||||
ConvertCodecType(const char* plname)
|
||||
{
|
||||
if (strncmp(plname,"VP8" , 3) == 0)
|
||||
{
|
||||
return kRTPVideoVP8;
|
||||
}else if (strncmp(plname,"H263" , 5) == 0)
|
||||
{
|
||||
return kRTPVideoH263;
|
||||
}else if (strncmp(plname, "H263-1998",10) == 0)
|
||||
{
|
||||
return kRTPVideoH263;
|
||||
}else if (strncmp(plname,"I420" , 5) == 0)
|
||||
{
|
||||
return kRTPVideoI420;
|
||||
}else
|
||||
{
|
||||
return kRTPVideoNoVideo; // defualt value
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
SendStatsTest::SendStatistics(const WebRtc_UWord32 bitRate, const WebRtc_UWord32 frameRate)
|
||||
{
|
||||
TEST(frameRate <= _frameRate);
|
||||
TEST(bitRate > 0 && bitRate < 100000);
|
||||
printf("VCM 1 sec: Bit rate: %u\tFrame rate: %u\n", bitRate, frameRate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
KeyFrameReqTest::FrameTypeRequest(const FrameType frameType)
|
||||
{
|
||||
TEST(frameType == kVideoFrameKey);
|
||||
if (frameType == kVideoFrameKey)
|
||||
{
|
||||
printf("Key frame requested\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Non-key frame requested: %d\n", frameType);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
257
modules/video_coding/main/test/test_util.h
Normal file
257
modules/video_coding/main/test/test_util.h
Normal file
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TEST_UTIL_H
|
||||
#define TEST_UTIL_H
|
||||
|
||||
#include "video_coding.h"
|
||||
#include "rtp_rtcp.h"
|
||||
#include "trace.h"
|
||||
#include "module_common_types.h"
|
||||
#include "tick_time.h"
|
||||
#include "test_macros.h"
|
||||
#include "test_util.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
|
||||
enum { kMaxWaitEncTimeMs = 100 };
|
||||
|
||||
// Class used for passing command line arguments to tests
|
||||
class CmdArgs
|
||||
{
|
||||
public:
|
||||
CmdArgs() : codecName(""), codecType(webrtc::kVideoCodecVP8), width(-1),
|
||||
height(-1), bitRate(-1), frameRate(-1),
|
||||
inputFile(""), outputFile(""), testNum(-1)
|
||||
{}
|
||||
std::string codecName;
|
||||
webrtc::VideoCodecType codecType;
|
||||
int width;
|
||||
int height;
|
||||
int bitRate;
|
||||
int frameRate;
|
||||
std::string inputFile;
|
||||
std::string outputFile;
|
||||
int testNum;
|
||||
};
|
||||
|
||||
// forward declaration
|
||||
int MTRxTxTest(CmdArgs& args);
|
||||
namespace webrtc
|
||||
{
|
||||
class RtpDump;
|
||||
}
|
||||
|
||||
// definition of general test function to be used by VCM tester (mainly send side)
|
||||
/*
|
||||
Includes the following:
|
||||
1. General Callback definition for VCM test functions - no RTP.
|
||||
2. EncodeComplete callback:
|
||||
2a. Transfer encoded data directly to the decoder
|
||||
2b. Pass encoded data via the RTP module
|
||||
3. Caluclate PSNR from file function (for now: does not deal with frame drops)
|
||||
*/
|
||||
|
||||
//Send Side - Packetization callback - send an encoded frame directly to the VCMReceiver
|
||||
class VCMEncodeCompleteCallback: public webrtc::VCMPacketizationCallback
|
||||
{
|
||||
public:
|
||||
// constructor input: file in which encoded data will be written, and test parameters
|
||||
VCMEncodeCompleteCallback(FILE* encodedFile);
|
||||
virtual ~VCMEncodeCompleteCallback();
|
||||
// Register transport callback
|
||||
void RegisterTransportCallback(webrtc::VCMPacketizationCallback* transport);
|
||||
// process encoded data received from the encoder, pass stream to the VCMReceiver module
|
||||
WebRtc_Word32 SendData(const webrtc::FrameType frameType,
|
||||
const WebRtc_UWord8 payloadType, const WebRtc_UWord32 timeStamp,
|
||||
const WebRtc_UWord8* payloadData, const WebRtc_UWord32 payloadSize,
|
||||
const webrtc::RTPFragmentationHeader& fragmentationHeader);
|
||||
// Register exisitng VCM. Currently - encode and decode with the same vcm module.
|
||||
void RegisterReceiverVCM(webrtc::VideoCodingModule *vcm) { _VCMReceiver = vcm; }
|
||||
// Return size of last encoded frame encoded data (all frames in the sequence)
|
||||
// Good for only one call - after which will reset value (to allow detection of frame drop)
|
||||
float EncodedBytes();
|
||||
// return encode complete (true/false)
|
||||
bool EncodeComplete();
|
||||
// Inform callback of codec used
|
||||
void SetCodecType(webrtc::RTPVideoCodecTypes codecType) { _codecType = codecType; }
|
||||
// inform callback of frame dimensions
|
||||
void SetFrameDimensions(WebRtc_Word32 width, WebRtc_Word32 height)
|
||||
{
|
||||
_width = width;
|
||||
_height = height;
|
||||
}
|
||||
//Initialize callback data
|
||||
void Initialize();
|
||||
void ResetByteCount();
|
||||
|
||||
// conversion function for payload type (needed for the callback function)
|
||||
// RTPVideoVideoCodecTypes ConvertPayloadType(WebRtc_UWord8 payloadType);
|
||||
|
||||
private:
|
||||
FILE* _encodedFile;
|
||||
float _encodedBytes;
|
||||
webrtc::VideoCodingModule* _VCMReceiver;
|
||||
webrtc::FrameType _frameType;
|
||||
WebRtc_UWord8* _payloadData;
|
||||
WebRtc_UWord8 _seqNo;
|
||||
bool _encodeComplete;
|
||||
WebRtc_Word32 _width;
|
||||
WebRtc_Word32 _height;
|
||||
webrtc::RTPVideoCodecTypes _codecType;
|
||||
WebRtc_UWord8 _layerPacketId;
|
||||
|
||||
}; // end of VCMEncodeCompleteCallback
|
||||
|
||||
//Send Side - Packetization callback - packetize an encoded frame via the RTP module
|
||||
class VCMRTPEncodeCompleteCallback: public webrtc::VCMPacketizationCallback
|
||||
{
|
||||
public:
|
||||
VCMRTPEncodeCompleteCallback(webrtc::RtpRtcp* rtp) :
|
||||
_seqNo(0), _encodedBytes(0), _RTPModule(rtp), _encodeComplete(false) {}
|
||||
virtual ~VCMRTPEncodeCompleteCallback() {}
|
||||
// process encoded data received from the encoder, pass stream to the RTP module
|
||||
WebRtc_Word32 SendData(const webrtc::FrameType frameType,
|
||||
const WebRtc_UWord8 payloadType, const WebRtc_UWord32 timeStamp,
|
||||
const WebRtc_UWord8* payloadData, const WebRtc_UWord32 payloadSize,
|
||||
const webrtc::RTPFragmentationHeader& fragmentationHeader);
|
||||
// Return size of last encoded frame. Value good for one call
|
||||
// (resets to zero after call to inform test of frame drop)
|
||||
float EncodedBytes();
|
||||
// return encode complete (true/false)
|
||||
bool EncodeComplete();
|
||||
// Inform callback of codec used
|
||||
void SetCodecType(webrtc::RTPVideoCodecTypes codecType) { _codecType = codecType; }
|
||||
|
||||
// inform callback of frame dimensions
|
||||
void SetFrameDimensions(WebRtc_Word16 width, WebRtc_Word16 height)
|
||||
{
|
||||
_width = width;
|
||||
_height = height;
|
||||
}
|
||||
|
||||
private:
|
||||
float _encodedBytes;
|
||||
webrtc::FrameType _frameType;
|
||||
WebRtc_UWord8* _payloadData;
|
||||
WebRtc_UWord16 _seqNo;
|
||||
bool _encodeComplete;
|
||||
webrtc::RtpRtcp* _RTPModule;
|
||||
WebRtc_Word16 _width;
|
||||
WebRtc_Word16 _height;
|
||||
webrtc::RTPVideoCodecTypes _codecType;
|
||||
}; // end of VCMEncodeCompleteCallback
|
||||
|
||||
class VCMDecodeCompleteCallback: public webrtc::VCMReceiveCallback
|
||||
{
|
||||
public:
|
||||
VCMDecodeCompleteCallback(FILE* decodedFile) :
|
||||
_decodedFile(decodedFile), _decodedBytes(0) {}
|
||||
virtual ~VCMDecodeCompleteCallback() {}
|
||||
// will write decoded frame into file
|
||||
WebRtc_Word32 FrameToRender(webrtc::VideoFrame& videoFrame);
|
||||
WebRtc_Word32 DecodedBytes();
|
||||
int PSNRLastFrame(const webrtc::VideoFrame& sourceFrame, double *YPSNRptr);
|
||||
private:
|
||||
FILE* _decodedFile;
|
||||
WebRtc_UWord32 _decodedBytes;
|
||||
webrtc::VideoFrame _lastDecodedFrame;
|
||||
}; // end of VCMDecodeCompleCallback class
|
||||
|
||||
///
|
||||
class RTPSendCompleteCallback: public webrtc::Transport
|
||||
{
|
||||
public:
|
||||
// constructor input: (reeive side) rtp module to send encoded data to
|
||||
RTPSendCompleteCallback(webrtc::RtpRtcp* rtp,
|
||||
const char* filename = NULL);
|
||||
virtual ~RTPSendCompleteCallback();
|
||||
// Send Packet to receive side RTP module
|
||||
virtual int SendPacket(int channel, const void *data, int len);
|
||||
// Send RTCP Packet to receive side RTP module
|
||||
virtual int SendRTCPPacket(int channel, const void *data, int len);
|
||||
// Set percentage of channel loss in the network
|
||||
void SetLossPct(double lossPct);
|
||||
// return send count
|
||||
int SendCount() { return _sendCount; }
|
||||
private:
|
||||
// randomly decide weather to drop a packet or not, based on the channel model
|
||||
bool PacketLoss(double lossPct);
|
||||
|
||||
WebRtc_UWord32 _sendCount;
|
||||
webrtc::RtpRtcp* _rtp;
|
||||
double _lossPct;
|
||||
webrtc::RtpDump* _rtpDump;
|
||||
};
|
||||
|
||||
// used in multi thread test
|
||||
class SendSharedState
|
||||
{
|
||||
public:
|
||||
SendSharedState(webrtc::VideoCodingModule& vcm, webrtc::RtpRtcp& rtp,
|
||||
CmdArgs args) :
|
||||
_rtp(rtp), _vcm(vcm), _args(args), _sourceFile(NULL), _frameCnt(0),
|
||||
_timestamp(0) {}
|
||||
|
||||
webrtc::VideoCodingModule& _vcm;
|
||||
webrtc::RtpRtcp& _rtp;
|
||||
CmdArgs _args;
|
||||
FILE* _sourceFile;
|
||||
WebRtc_Word32 _frameCnt;
|
||||
WebRtc_Word32 _timestamp;
|
||||
};
|
||||
|
||||
class PacketRequester: public webrtc::VCMPacketRequestCallback
|
||||
{
|
||||
public:
|
||||
PacketRequester(webrtc::RtpRtcp& rtp) :
|
||||
_rtp(rtp) {}
|
||||
WebRtc_Word32 ResendPackets(const WebRtc_UWord16* sequenceNumbers,
|
||||
WebRtc_UWord16 length);
|
||||
|
||||
private:
|
||||
webrtc::RtpRtcp& _rtp;
|
||||
};
|
||||
|
||||
// PSNR & SSIM calculations
|
||||
WebRtc_Word32
|
||||
PSNRfromFiles(const WebRtc_Word8 *refFileName,
|
||||
const WebRtc_Word8 *testFileName, WebRtc_Word32 width,
|
||||
WebRtc_Word32 height, double *YPSNRptr);
|
||||
|
||||
WebRtc_Word32
|
||||
SSIMfromFiles(const WebRtc_Word8 *refFileName,
|
||||
const WebRtc_Word8 *testFileName, WebRtc_Word32 width,
|
||||
WebRtc_Word32 height, double *SSIMptr);
|
||||
|
||||
// codec type conversion
|
||||
webrtc::RTPVideoCodecTypes
|
||||
ConvertCodecType(const char* plname);
|
||||
|
||||
class SendStatsTest: public webrtc::VCMSendStatisticsCallback
|
||||
{
|
||||
public:
|
||||
SendStatsTest() : _frameRate(15) {}
|
||||
WebRtc_Word32 SendStatistics(const WebRtc_UWord32 bitRate,
|
||||
const WebRtc_UWord32 frameRate);
|
||||
void SetTargetFrameRate(WebRtc_UWord32 frameRate) { _frameRate = frameRate; }
|
||||
private:
|
||||
WebRtc_UWord32 _frameRate;
|
||||
};
|
||||
|
||||
class KeyFrameReqTest: public webrtc::VCMFrameTypeCallback
|
||||
{
|
||||
public:
|
||||
WebRtc_Word32 FrameTypeRequest(const webrtc::FrameType frameType);
|
||||
};
|
||||
|
||||
#endif
|
||||
187
modules/video_coding/main/test/tester_main.cc
Normal file
187
modules/video_coding/main/test/tester_main.cc
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* 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 "receiver_tests.h"
|
||||
#include "normal_test.h"
|
||||
#include "codec_database_test.h"
|
||||
#include "generic_codec_test.h"
|
||||
#include "../source/event.h"
|
||||
#include "media_opt_test.h"
|
||||
#include "quality_modes_test.h"
|
||||
#include "test_util.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
//#include "vld.h"
|
||||
#endif
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
/*
|
||||
* Build with TICK_TIME_DEBUG and EVENT_DEBUG defined
|
||||
* to build the tests with simulated clock.
|
||||
*/
|
||||
|
||||
// TODO(holmer): How do we get debug time into the cmd line interface?
|
||||
/* Debug time */
|
||||
#if defined(TICK_TIME_DEBUG) && defined(EVENT_DEBUG)
|
||||
WebRtc_Word64 VCMTickTime::_timeNowDebug = 0; // current time in ms
|
||||
#endif
|
||||
|
||||
int ParseArguments(int argc, char **argv, CmdArgs& args)
|
||||
{
|
||||
int i = 1;
|
||||
while (i < argc)
|
||||
{
|
||||
if (argv[i][0] != '-')
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
switch (argv[i][1])
|
||||
{
|
||||
case 'w':
|
||||
{
|
||||
int w = atoi(argv[i+1]);
|
||||
if (w < 1)
|
||||
return -1;
|
||||
args.width = w;
|
||||
break;
|
||||
}
|
||||
case 'h':
|
||||
{
|
||||
int h = atoi(argv[i+1]);
|
||||
if (h < 1)
|
||||
return -1;
|
||||
args.height = h;
|
||||
break;
|
||||
}
|
||||
case 'b':
|
||||
{
|
||||
int b = atoi(argv[i+1]);
|
||||
if (b < 1)
|
||||
return -1;
|
||||
args.bitRate = b;
|
||||
break;
|
||||
}
|
||||
case 'f':
|
||||
{
|
||||
int f = atoi(argv[i+1]);
|
||||
if (f < 1)
|
||||
return -1;
|
||||
args.frameRate = f;
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
{
|
||||
// TODO(holmer): This should be replaced with a map if more codecs
|
||||
// are added
|
||||
args.codecName = argv[i+1];
|
||||
if (strncmp(argv[i+1], "VP8", 3) == 0)
|
||||
{
|
||||
args.codecType = kVideoCodecVP8;
|
||||
}
|
||||
else if (strncmp(argv[i+1], "I420", 4) == 0)
|
||||
{
|
||||
args.codecType = kVideoCodecI420;
|
||||
}
|
||||
else if (strncmp(argv[i+1], "H263", 4) == 0)
|
||||
{
|
||||
args.codecType = kVideoCodecH263;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
|
||||
break;
|
||||
}
|
||||
case 'i':
|
||||
{
|
||||
args.inputFile = argv[i+1];
|
||||
break;
|
||||
}
|
||||
case 'o':
|
||||
args.outputFile = argv[i+1];
|
||||
break;
|
||||
case 'n':
|
||||
{
|
||||
int n = atoi(argv[i+1]);
|
||||
if (n < 1)
|
||||
return -1;
|
||||
args.testNum = n;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
CmdArgs args;
|
||||
|
||||
if (ParseArguments(argc, argv, args) != 0)
|
||||
{
|
||||
printf("Unable to parse input arguments\n");
|
||||
printf("args: -n <test #> -w <width> -h <height> -f <fps> -b <bps> -c <codec>"
|
||||
" -i <input file> -o <output file>\n");
|
||||
return -1;
|
||||
}
|
||||
int ret = 0;
|
||||
switch (args.testNum)
|
||||
{
|
||||
case 1:
|
||||
ret = NormalTest::RunTest(args);
|
||||
break;
|
||||
case 2:
|
||||
ret = MTRxTxTest(args);
|
||||
break;
|
||||
case 3:
|
||||
ret = GenericCodecTest::RunTest(args);
|
||||
break;
|
||||
case 4:
|
||||
ret = CodecDataBaseTest::RunTest(args);
|
||||
break;
|
||||
case 5:
|
||||
// 0- normal, 1-Release test(50 runs) 2- from file
|
||||
ret = MediaOptTest::RunTest(0, args);
|
||||
break;
|
||||
case 6:
|
||||
ret = ReceiverTimingTests(args);
|
||||
break;
|
||||
case 7:
|
||||
ret = RtpPlay(args);
|
||||
break;
|
||||
case 8:
|
||||
ret = RtpPlayMT(args);
|
||||
break;
|
||||
case 9:
|
||||
ret = JitterBufferTest(args);
|
||||
break;
|
||||
case 10:
|
||||
ret = DecodeFromStorageTest(args);
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
if (ret != 0)
|
||||
{
|
||||
printf("Test failed!\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
206
modules/video_coding/main/test/video_rtp_play.cc
Normal file
206
modules/video_coding/main/test/video_rtp_play.cc
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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 "receiver_tests.h"
|
||||
#include "video_coding.h"
|
||||
#include "rtp_rtcp.h"
|
||||
#include "trace.h"
|
||||
#include "tick_time.h"
|
||||
#include "../source/event.h"
|
||||
#include "../source/internal_defines.h"
|
||||
#include "test_macros.h"
|
||||
#include "rtp_player.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
WebRtc_Word32
|
||||
RtpDataCallback::OnReceivedPayloadData(const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord16 payloadSize,
|
||||
const WebRtcRTPHeader* rtpHeader)
|
||||
{
|
||||
return _vcm->IncomingPacket(payloadData, payloadSize, *rtpHeader);
|
||||
}
|
||||
|
||||
FrameReceiveCallback::~FrameReceiveCallback()
|
||||
{
|
||||
if (_timingFile != NULL)
|
||||
{
|
||||
fclose(_timingFile);
|
||||
}
|
||||
if (_outFile != NULL)
|
||||
{
|
||||
fclose(_outFile);
|
||||
}
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
FrameReceiveCallback::FrameToRender(VideoFrame& videoFrame)
|
||||
{
|
||||
if (_timingFile == NULL)
|
||||
{
|
||||
_timingFile = fopen("renderTiming.txt", "w");
|
||||
if (_timingFile == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (_outFile == NULL)
|
||||
{
|
||||
_outFile = fopen(_outFilename.c_str(), "wb");
|
||||
if (_outFile == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
fprintf(_timingFile, "%u, %u\n",
|
||||
videoFrame.TimeStamp(),
|
||||
MaskWord64ToUWord32(videoFrame.RenderTimeMs()));
|
||||
fwrite(videoFrame.Buffer(), 1, videoFrame.Length(), _outFile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RtpPlay(CmdArgs& args)
|
||||
{
|
||||
// Make sure this test isn't executed without simulated clocks
|
||||
#if !defined(TICK_TIME_DEBUG) || !defined(EVENT_DEBUG)
|
||||
return -1;
|
||||
#endif
|
||||
// BEGIN Settings
|
||||
|
||||
bool protectionEnabled = false;
|
||||
VCMVideoProtection protectionMethod = kProtectionNack;
|
||||
WebRtc_UWord32 rttMS = 10;
|
||||
float lossRate = 0.0f;
|
||||
bool reordering = false;
|
||||
WebRtc_UWord32 renderDelayMs = 0;
|
||||
WebRtc_UWord32 minPlayoutDelayMs = 0;
|
||||
const WebRtc_Word64 MAX_RUNTIME_MS = -1;
|
||||
std::string outFile = args.outputFile;
|
||||
if (outFile == "")
|
||||
outFile = "RtpPlay_decoded.yuv";
|
||||
FrameReceiveCallback receiveCallback(outFile);
|
||||
VideoCodingModule* vcm = VideoCodingModule::Create(1);
|
||||
RtpDataCallback dataCallback(vcm);
|
||||
RTPPlayer rtpStream(args.inputFile.c_str(), &dataCallback);
|
||||
|
||||
|
||||
ListWrapper payloadTypes;
|
||||
payloadTypes.PushFront(new PayloadCodecTuple(VCM_VP8_PAYLOAD_TYPE, "VP8", kVideoCodecVP8));
|
||||
|
||||
Trace::CreateTrace();
|
||||
Trace::SetTraceFile("receiverTestTrace.txt");
|
||||
Trace::SetLevelFilter(webrtc::kTraceAll);
|
||||
// END Settings
|
||||
|
||||
// Set up
|
||||
|
||||
WebRtc_Word32 ret = vcm->InitializeReceiver();
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
vcm->RegisterReceiveCallback(&receiveCallback);
|
||||
vcm->RegisterPacketRequestCallback(&rtpStream);
|
||||
|
||||
// Register receive codecs in VCM
|
||||
ListItem* item = payloadTypes.First();
|
||||
while (item != NULL)
|
||||
{
|
||||
PayloadCodecTuple* payloadType = static_cast<PayloadCodecTuple*>(item->GetItem());
|
||||
if (payloadType != NULL)
|
||||
{
|
||||
VideoCodec codec;
|
||||
if (VideoCodingModule::Codec(payloadType->codecType, &codec) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
codec.plType = payloadType->payloadType;
|
||||
if (vcm->RegisterReceiveCodec(&codec, 1) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
item = payloadTypes.Next(item);
|
||||
}
|
||||
|
||||
if (rtpStream.Initialize(payloadTypes) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
bool nackEnabled = protectionEnabled && (protectionMethod == kProtectionNack ||
|
||||
protectionMethod == kProtectionDualDecoder);
|
||||
rtpStream.SimulatePacketLoss(lossRate, nackEnabled, rttMS);
|
||||
rtpStream.SetReordering(reordering);
|
||||
vcm->SetChannelParameters(0, 0, rttMS);
|
||||
vcm->SetVideoProtection(protectionMethod, protectionEnabled);
|
||||
vcm->SetRenderDelay(renderDelayMs);
|
||||
vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs);
|
||||
|
||||
ret = 0;
|
||||
|
||||
// RTP stream main loop
|
||||
while ((ret = rtpStream.NextPacket(VCMTickTime::MillisecondTimestamp())) == 0)
|
||||
{
|
||||
if (VCMTickTime::MillisecondTimestamp() % 5 == 0)
|
||||
{
|
||||
ret = vcm->Decode();
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
while (vcm->DecodeDualFrame(0) == 1);
|
||||
if (vcm->TimeUntilNextProcess() <= 0)
|
||||
{
|
||||
vcm->Process();
|
||||
}
|
||||
if (MAX_RUNTIME_MS > -1 && VCMTickTime::MillisecondTimestamp() >= MAX_RUNTIME_MS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
VCMTickTime::IncrementDebugClock();
|
||||
}
|
||||
|
||||
switch (ret)
|
||||
{
|
||||
case 1:
|
||||
printf("Success\n");
|
||||
break;
|
||||
case -1:
|
||||
printf("Failed\n");
|
||||
break;
|
||||
case 0:
|
||||
printf("Timeout\n");
|
||||
break;
|
||||
}
|
||||
|
||||
rtpStream.Print();
|
||||
|
||||
// Tear down
|
||||
item = payloadTypes.First();
|
||||
while (item != NULL)
|
||||
{
|
||||
PayloadCodecTuple* payloadType = static_cast<PayloadCodecTuple*>(item->GetItem());
|
||||
if (payloadType != NULL)
|
||||
{
|
||||
delete payloadType;
|
||||
}
|
||||
ListItem* itemToRemove = item;
|
||||
item = payloadTypes.Next(item);
|
||||
payloadTypes.Erase(itemToRemove);
|
||||
}
|
||||
delete vcm;
|
||||
vcm = NULL;
|
||||
Trace::ReturnTrace();
|
||||
return 0;
|
||||
}
|
||||
272
modules/video_coding/main/test/video_rtp_play_mt.cc
Normal file
272
modules/video_coding/main/test/video_rtp_play_mt.cc
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* 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 "receiver_tests.h"
|
||||
#include "video_coding.h"
|
||||
#include "rtp_rtcp.h"
|
||||
#include "trace.h"
|
||||
#include "thread_wrapper.h"
|
||||
#include "../source/event.h"
|
||||
#include "tick_time.h"
|
||||
#include "test_macros.h"
|
||||
#include "rtp_player.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
bool ProcessingThread(void* obj)
|
||||
{
|
||||
SharedState* state = static_cast<SharedState*>(obj);
|
||||
if (state->_vcm.TimeUntilNextProcess() <= 0)
|
||||
{
|
||||
if (state->_vcm.Process() < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RtpReaderThread(void* obj)
|
||||
{
|
||||
SharedState* state = static_cast<SharedState*>(obj);
|
||||
EventWrapper& waitEvent = *EventWrapper::Create();
|
||||
// RTP stream main loop
|
||||
WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp();
|
||||
if (state->_rtpPlayer.NextPacket(nowMs) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
waitEvent.Wait(state->_rtpPlayer.TimeUntilNextPacket());
|
||||
delete &waitEvent;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DecodeThread(void* obj)
|
||||
{
|
||||
SharedState* state = static_cast<SharedState*>(obj);
|
||||
WebRtc_Word32 ret = state->_vcm.Decode(10000);
|
||||
TEST(ret == VCM_OK || ret == VCM_UNINITIALIZED || ret == VCM_NO_CODEC_REGISTERED);
|
||||
while (state->_vcm.DecodeDualFrame(0) == 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
int RtpPlayMT(CmdArgs& args, int releaseTestNo, webrtc::VideoCodecType releaseTestVideoType)
|
||||
{
|
||||
// Don't run these tests with debug time
|
||||
#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
// BEGIN Settings
|
||||
|
||||
bool protectionEnabled = true;
|
||||
VCMVideoProtection protection = kProtectionDualDecoder;
|
||||
WebRtc_UWord8 rttMS = 50;
|
||||
float lossRate = 0.05f;
|
||||
WebRtc_UWord32 renderDelayMs = 0;
|
||||
WebRtc_UWord32 minPlayoutDelayMs = 0;
|
||||
const WebRtc_Word64 MAX_RUNTIME_MS = 10000;
|
||||
std::string outFilename = args.outputFile;
|
||||
if (outFilename == "")
|
||||
outFilename = "RtpPlayMT_decoded.yuv";
|
||||
|
||||
bool nackEnabled = (protectionEnabled &&
|
||||
(protection == kProtectionDualDecoder ||
|
||||
protection == kProtectionNack ||
|
||||
kProtectionNackFEC));
|
||||
VideoCodingModule* vcm =
|
||||
VideoCodingModule::Create(1);
|
||||
RtpDataCallback dataCallback(vcm);
|
||||
std::string rtpFilename;
|
||||
rtpFilename = args.inputFile;
|
||||
if (releaseTestNo > 0)
|
||||
{
|
||||
// Setup a release test
|
||||
switch (releaseTestVideoType)
|
||||
{
|
||||
case webrtc::kVideoCodecVP8:
|
||||
rtpFilename = args.inputFile;
|
||||
outFilename = "MTReceiveTest_VP8";
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
switch (releaseTestNo)
|
||||
{
|
||||
case 1:
|
||||
// Normal execution
|
||||
protectionEnabled = false;
|
||||
nackEnabled = false;
|
||||
rttMS = 0;
|
||||
lossRate = 0.0f;
|
||||
outFilename += "_Normal.yuv";
|
||||
break;
|
||||
case 2:
|
||||
// Packet loss
|
||||
protectionEnabled = false;
|
||||
nackEnabled = false;
|
||||
rttMS = 0;
|
||||
lossRate = 0.05f;
|
||||
outFilename += "_0.05.yuv";
|
||||
break;
|
||||
case 3:
|
||||
// Packet loss and NACK
|
||||
protection = kProtectionNack;
|
||||
nackEnabled = true;
|
||||
protectionEnabled = true;
|
||||
rttMS = 100;
|
||||
lossRate = 0.05f;
|
||||
outFilename += "_0.05_NACK_100ms.yuv";
|
||||
break;
|
||||
case 4:
|
||||
// Packet loss and dual decoder
|
||||
// Not implemented
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
printf("Watch %s to verify that the output is reasonable\n", outFilename.c_str());
|
||||
}
|
||||
RTPPlayer rtpStream(rtpFilename.c_str(), &dataCallback);
|
||||
ListWrapper payloadTypes;
|
||||
payloadTypes.PushFront(new PayloadCodecTuple(VCM_VP8_PAYLOAD_TYPE,
|
||||
"VP8", kVideoCodecVP8));
|
||||
|
||||
Trace::CreateTrace();
|
||||
Trace::SetTraceFile("receiverTestTrace.txt");
|
||||
Trace::SetLevelFilter(webrtc::kTraceAll);
|
||||
|
||||
// END Settings
|
||||
|
||||
// Set up
|
||||
|
||||
SharedState mtState(*vcm, rtpStream);
|
||||
|
||||
if (rtpStream.Initialize(payloadTypes) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
rtpStream.SimulatePacketLoss(lossRate, nackEnabled, rttMS);
|
||||
|
||||
WebRtc_Word32 ret = vcm->InitializeReceiver();
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create and start all threads
|
||||
ThreadWrapper* processingThread = ThreadWrapper::CreateThread(ProcessingThread,
|
||||
&mtState, kNormalPriority, "ProcessingThread");
|
||||
ThreadWrapper* rtpReaderThread = ThreadWrapper::CreateThread(RtpReaderThread,
|
||||
&mtState, kNormalPriority, "RtpReaderThread");
|
||||
ThreadWrapper* decodeThread = ThreadWrapper::CreateThread(DecodeThread,
|
||||
&mtState, kNormalPriority, "DecodeThread");
|
||||
|
||||
// Register receive codecs in VCM
|
||||
ListItem* item = payloadTypes.First();
|
||||
while (item != NULL)
|
||||
{
|
||||
PayloadCodecTuple* payloadType = static_cast<PayloadCodecTuple*>(item->GetItem());
|
||||
if (payloadType != NULL)
|
||||
{
|
||||
VideoCodec codec;
|
||||
VideoCodingModule::Codec(payloadType->codecType, &codec);
|
||||
codec.plType = payloadType->payloadType;
|
||||
if (vcm->RegisterReceiveCodec(&codec, 1) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
item = payloadTypes.Next(item);
|
||||
}
|
||||
|
||||
if (processingThread != NULL)
|
||||
{
|
||||
unsigned int tid;
|
||||
processingThread->Start(tid);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Unable to start processing thread\n");
|
||||
return -1;
|
||||
}
|
||||
if (rtpReaderThread != NULL)
|
||||
{
|
||||
unsigned int tid;
|
||||
rtpReaderThread->Start(tid);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Unable to start RTP reader thread\n");
|
||||
return -1;
|
||||
}
|
||||
if (decodeThread != NULL)
|
||||
{
|
||||
unsigned int tid;
|
||||
decodeThread->Start(tid);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Unable to start decode thread\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
FrameReceiveCallback receiveCallback(outFilename);
|
||||
vcm->RegisterReceiveCallback(&receiveCallback);
|
||||
vcm->RegisterPacketRequestCallback(&rtpStream);
|
||||
|
||||
vcm->SetChannelParameters(0, 0, rttMS);
|
||||
vcm->SetVideoProtection(protection, protectionEnabled);
|
||||
vcm->SetRenderDelay(renderDelayMs);
|
||||
vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs);
|
||||
|
||||
EventWrapper& waitEvent = *EventWrapper::Create();
|
||||
|
||||
// Decode for 10 seconds and then tear down and exit.
|
||||
waitEvent.Wait(MAX_RUNTIME_MS);
|
||||
|
||||
// Tear down
|
||||
item = payloadTypes.First();
|
||||
while (item != NULL)
|
||||
{
|
||||
PayloadCodecTuple* payloadType = static_cast<PayloadCodecTuple*>(item->GetItem());
|
||||
if (payloadType != NULL)
|
||||
{
|
||||
delete payloadType;
|
||||
}
|
||||
ListItem* itemToRemove = item;
|
||||
item = payloadTypes.Next(item);
|
||||
payloadTypes.Erase(itemToRemove);
|
||||
}
|
||||
while (!processingThread->Stop())
|
||||
{
|
||||
;
|
||||
}
|
||||
while (!rtpReaderThread->Stop())
|
||||
{
|
||||
;
|
||||
}
|
||||
while (!decodeThread->Stop())
|
||||
{
|
||||
;
|
||||
}
|
||||
VideoCodingModule::Destroy(vcm);
|
||||
vcm = NULL;
|
||||
delete &waitEvent;
|
||||
delete processingThread;
|
||||
delete decodeThread;
|
||||
delete rtpReaderThread;
|
||||
rtpStream.Print();
|
||||
Trace::ReturnTrace();
|
||||
return 0;
|
||||
}
|
||||
200
modules/video_coding/main/test/video_source.cc
Normal file
200
modules/video_coding/main/test/video_source.cc
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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 "vplib.h"
|
||||
#include "video_source.h"
|
||||
#include <cassert>
|
||||
|
||||
VideoSource::VideoSource()
|
||||
:
|
||||
_fileName("../../../../../codecs_video/testFiles/foreman.yuv"),
|
||||
_width(352),
|
||||
_height(288),
|
||||
_type(webrtc::kI420),
|
||||
_frameRate(30)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
VideoSource::VideoSource(std::string fileName, VideoSize size,
|
||||
float frameRate, webrtc::VideoType type /*= webrtc::kI420*/)
|
||||
:
|
||||
_fileName(fileName),
|
||||
_type(type),
|
||||
_frameRate(frameRate),
|
||||
_width(0),
|
||||
_height(0)
|
||||
{
|
||||
assert(size != kUndefined && size != kNumberOfVideoSizes);
|
||||
assert(type != webrtc::kUnknown);
|
||||
assert(frameRate > 0);
|
||||
GetWidthHeight(size);
|
||||
}
|
||||
|
||||
VideoSource::VideoSource(std::string fileName, WebRtc_UWord16 width, WebRtc_UWord16 height,
|
||||
float frameRate /*= 30*/, webrtc::VideoType type /*= webrtc::kI420*/)
|
||||
:
|
||||
_fileName(fileName),
|
||||
_width(width),
|
||||
_height(height),
|
||||
_type(type),
|
||||
_frameRate(frameRate)
|
||||
{
|
||||
assert(width > 0);
|
||||
assert(height > 0);
|
||||
assert(type != webrtc::kUnknown);
|
||||
assert(frameRate > 0);
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
VideoSource::GetFrameLength() const
|
||||
{
|
||||
return webrtc::CalcBufferSize(_type, _width, _height);
|
||||
}
|
||||
|
||||
std::string
|
||||
VideoSource::GetName() const
|
||||
{
|
||||
// Remove path.
|
||||
size_t slashPos = _fileName.find_last_of("/\\");
|
||||
if (slashPos == std::string::npos)
|
||||
{
|
||||
slashPos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
slashPos++;
|
||||
}
|
||||
|
||||
// Remove extension and underscored suffix if it exists.
|
||||
//return _fileName.substr(slashPos, std::min(_fileName.find_last_of("_"),
|
||||
// _fileName.find_last_of(".")) - slashPos);
|
||||
// MS: Removing suffix, not underscore....keeping full file name
|
||||
return _fileName.substr(slashPos, _fileName.find_last_of(".") - slashPos);
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
VideoSource::GetWidthHeight( VideoSize size)
|
||||
{
|
||||
switch(size)
|
||||
{
|
||||
case kSQCIF:
|
||||
_width = 128;
|
||||
_height = 96;
|
||||
return 0;
|
||||
case kQQVGA:
|
||||
_width = 160;
|
||||
_height = 120;
|
||||
return 0;
|
||||
case kQCIF:
|
||||
_width = 176;
|
||||
_height = 144;
|
||||
return 0;
|
||||
case kCGA:
|
||||
_width = 320;
|
||||
_height = 200;
|
||||
return 0;
|
||||
case kQVGA:
|
||||
_width = 320;
|
||||
_height = 240;
|
||||
return 0;
|
||||
case kSIF:
|
||||
_width = 352;
|
||||
_height = 240;
|
||||
return 0;
|
||||
case kWQVGA:
|
||||
_width = 400;
|
||||
_height = 240;
|
||||
return 0;
|
||||
case kCIF:
|
||||
_width = 352;
|
||||
_height = 288;
|
||||
return 0;
|
||||
case kW288p:
|
||||
_width = 512;
|
||||
_height = 288;
|
||||
return 0;
|
||||
case k448p:
|
||||
_width = 576;
|
||||
_height = 448;
|
||||
return 0;
|
||||
case kVGA:
|
||||
_width = 640;
|
||||
_height = 480;
|
||||
return 0;
|
||||
case k432p:
|
||||
_width = 720;
|
||||
_height = 432;
|
||||
return 0;
|
||||
case kW432p:
|
||||
_width = 768;
|
||||
_height = 432;
|
||||
return 0;
|
||||
case k4SIF:
|
||||
_width = 704;
|
||||
_height = 480;
|
||||
return 0;
|
||||
case kW448p:
|
||||
_width = 768;
|
||||
_height = 448;
|
||||
return 0;
|
||||
case kNTSC:
|
||||
_width = 720;
|
||||
_height = 480;
|
||||
return 0;
|
||||
case kFW448p:
|
||||
_width = 800;
|
||||
_height = 448;
|
||||
return 0;
|
||||
case kWVGA:
|
||||
_width = 800;
|
||||
_height = 480;
|
||||
return 0;
|
||||
case k4CIF:
|
||||
_width = 704;
|
||||
_height = 576;
|
||||
return 0;
|
||||
case kSVGA:
|
||||
_width = 800;
|
||||
_height = 600;
|
||||
return 0;
|
||||
case kW544p:
|
||||
_width = 960;
|
||||
_height = 544;
|
||||
return 0;
|
||||
case kW576p:
|
||||
_width = 1024;
|
||||
_height = 576;
|
||||
return 0;
|
||||
case kHD:
|
||||
_width = 960;
|
||||
_height = 720;
|
||||
return 0;
|
||||
case kXGA:
|
||||
_width = 1024;
|
||||
_height = 768;
|
||||
return 0;
|
||||
case kFullHD:
|
||||
_width = 1440;
|
||||
_height = 1080;
|
||||
return 0;
|
||||
case kWHD:
|
||||
_width = 1280;
|
||||
_height = 720;
|
||||
return 0;
|
||||
case kWFullHD:
|
||||
_width = 1920;
|
||||
_height = 1080;
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
83
modules/video_coding/main/test/video_source.h
Normal file
83
modules/video_coding/main/test/video_source.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_VIDEO_SOURCE_H_
|
||||
#define WEBRTC_MODULES_VIDEO_CODING_TEST_VIDEO_SOURCE_H_
|
||||
|
||||
#include "vplib.h"
|
||||
#include "typedefs.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
enum VideoSize
|
||||
{
|
||||
kUndefined,
|
||||
kSQCIF, // 128*96 = 12 288
|
||||
kQQVGA, // 160*120 = 19 200
|
||||
kQCIF, // 176*144 = 25 344
|
||||
kCGA, // 320*200 = 64 000
|
||||
kQVGA, // 320*240 = 76 800
|
||||
kSIF, // 352*240 = 84 480
|
||||
kWQVGA, // 400*240 = 96 000
|
||||
kCIF, // 352*288 = 101 376
|
||||
kW288p, // 512*288 = 147 456 (WCIF)
|
||||
k448p, // 576*448 = 281 088
|
||||
kVGA, // 640*480 = 307 200
|
||||
k432p, // 720*432 = 311 040
|
||||
kW432p, // 768*432 = 331 776
|
||||
k4SIF, // 704*480 = 337 920
|
||||
kW448p, // 768*448 = 344 064
|
||||
kNTSC, // 720*480 = 345 600
|
||||
kFW448p, // 800*448 = 358 400
|
||||
kWVGA, // 800*480 = 384 000
|
||||
k4CIF, // 704*576 = 405 504
|
||||
kSVGA, // 800*600 = 480 000
|
||||
kW544p, // 960*544 = 522 240
|
||||
kW576p, // 1024*576 = 589 824 (W4CIF)
|
||||
kHD, // 960*720 = 691 200
|
||||
kXGA, // 1024*768 = 786 432
|
||||
kWHD, // 1280*720 = 921 600
|
||||
kFullHD, // 1440*1080 = 1 555 200
|
||||
kWFullHD, // 1920*1080 = 2 073 600
|
||||
|
||||
kNumberOfVideoSizes
|
||||
};
|
||||
|
||||
|
||||
class VideoSource
|
||||
{
|
||||
public:
|
||||
VideoSource();
|
||||
VideoSource(std::string fileName, VideoSize size, float frameRate, webrtc::VideoType type = webrtc::kI420);
|
||||
VideoSource(std::string fileName, WebRtc_UWord16 width, WebRtc_UWord16 height,
|
||||
float frameRate = 30, webrtc::VideoType type = webrtc::kI420);
|
||||
|
||||
std::string GetFileName() const { return _fileName; }
|
||||
WebRtc_UWord16 GetWidth() const { return _width; }
|
||||
WebRtc_UWord16 GetHeight() const { return _height; }
|
||||
webrtc::VideoType GetType() const { return _type; }
|
||||
float GetFrameRate() const { return _frameRate; }
|
||||
int GetWidthHeight( VideoSize size);
|
||||
|
||||
// Returns the filename with the path (including the leading slash) removed.
|
||||
std::string GetName() const;
|
||||
|
||||
WebRtc_Word32 GetFrameLength() const;
|
||||
|
||||
private:
|
||||
std::string _fileName;
|
||||
WebRtc_UWord16 _width;
|
||||
WebRtc_UWord16 _height;
|
||||
webrtc::VideoType _type;
|
||||
float _frameRate;
|
||||
};
|
||||
|
||||
#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_VIDEO_SOURCE_H_
|
||||
|
||||
Reference in New Issue
Block a user