blob: e37975849b15445b1eb524baefd859c0070dfb75 [file] [log] [blame]
/*
* 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_async_test.h"
#include <assert.h>
#include <string.h>
#include <sstream>
#include <queue>
#include "gtest/gtest.h"
#include "tick_util.h"
#include "testsupport/fileutils.h"
#include "typedefs.h"
using namespace webrtc;
NormalAsyncTest::NormalAsyncTest()
:
NormalTest("Async Normal Test 1", "A test of normal execution of the codec",
_testNo),
_decodeCompleteTime(0),
_encodeCompleteTime(0),
_encFrameCnt(0),
_decFrameCnt(0),
_requestKeyFrame(false),
_testNo(1),
_appendNext(false),
_missingFrames(false),
_rttFrames(0),
_hasReceivedSLI(false),
_hasReceivedRPSI(false),
_hasReceivedPLI(false),
_waitForKey(false)
{
}
NormalAsyncTest::NormalAsyncTest(WebRtc_UWord32 bitRate)
:
NormalTest("Async Normal Test 1", "A test of normal execution of the codec",
bitRate, _testNo),
_decodeCompleteTime(0),
_encodeCompleteTime(0),
_encFrameCnt(0),
_decFrameCnt(0),
_requestKeyFrame(false),
_testNo(1),
_appendNext(false),
_missingFrames(false),
_rttFrames(0),
_hasReceivedSLI(false),
_hasReceivedRPSI(false),
_hasReceivedPLI(false),
_waitForKey(false)
{
}
NormalAsyncTest::NormalAsyncTest(std::string name, std::string description,
unsigned int testNo)
:
NormalTest(name, description, _testNo),
_decodeCompleteTime(0),
_encodeCompleteTime(0),
_encFrameCnt(0),
_decFrameCnt(0),
_requestKeyFrame(false),
_testNo(testNo),
_lengthEncFrame(0),
_appendNext(false),
_missingFrames(false),
_rttFrames(0),
_hasReceivedSLI(false),
_hasReceivedRPSI(false),
_hasReceivedPLI(false),
_waitForKey(false)
{
}
NormalAsyncTest::NormalAsyncTest(std::string name, std::string description,
WebRtc_UWord32 bitRate, unsigned int testNo)
:
NormalTest(name, description, bitRate, _testNo),
_decodeCompleteTime(0),
_encodeCompleteTime(0),
_encFrameCnt(0),
_decFrameCnt(0),
_requestKeyFrame(false),
_testNo(testNo),
_lengthEncFrame(0),
_appendNext(false),
_missingFrames(false),
_rttFrames(0),
_hasReceivedSLI(false),
_hasReceivedRPSI(false),
_hasReceivedPLI(false),
_waitForKey(false)
{
}
NormalAsyncTest::NormalAsyncTest(std::string name, std::string description,
WebRtc_UWord32 bitRate, unsigned int testNo,
unsigned int rttFrames)
:
NormalTest(name, description, bitRate, _testNo),
_decodeCompleteTime(0),
_encodeCompleteTime(0),
_encFrameCnt(0),
_decFrameCnt(0),
_requestKeyFrame(false),
_testNo(testNo),
_lengthEncFrame(0),
_appendNext(false),
_missingFrames(false),
_rttFrames(rttFrames),
_hasReceivedSLI(false),
_hasReceivedRPSI(false),
_hasReceivedPLI(false),
_waitForKey(false)
{
}
void
NormalAsyncTest::Setup()
{
Test::Setup();
std::stringstream ss;
std::string strTestNo;
ss << _testNo;
ss >> strTestNo;
// Check if settings exist. Otherwise use defaults.
if (_outname == "")
{
_outname = webrtc::test::OutputPath() + "out_normaltest" + strTestNo +
".yuv";
}
if (_encodedName == "")
{
_encodedName = webrtc::test::OutputPath() + "encoded_normaltest" +
strTestNo + ".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);
}
char mode[3] = "wb";
if (_appendNext)
{
strncpy(mode, "ab", 3);
}
if ((_decodedFile = fopen(_outname.c_str(), mode)) == NULL)
{
printf("Cannot write file %s.\n", _outname.c_str());
exit(1);
}
_appendNext = true;
}
void
NormalAsyncTest::Teardown()
{
Test::Teardown();
fclose(_sourceFile);
fclose(_encodedFile);
fclose(_decodedFile);
}
FrameQueueTuple::~FrameQueueTuple()
{
if (_codecSpecificInfo != NULL)
{
delete _codecSpecificInfo;
}
if (_frame != NULL)
{
delete _frame;
}
}
void FrameQueue::PushFrame(TestVideoEncodedBuffer *frame,
webrtc::CodecSpecificInfo* codecSpecificInfo)
{
WriteLockScoped cs(_queueRWLock);
_frameBufferQueue.push(new FrameQueueTuple(frame, codecSpecificInfo));
}
FrameQueueTuple* FrameQueue::PopFrame()
{
WriteLockScoped cs(_queueRWLock);
if (_frameBufferQueue.empty())
{
return NULL;
}
FrameQueueTuple* tuple = _frameBufferQueue.front();
_frameBufferQueue.pop();
return tuple;
}
bool FrameQueue::Empty()
{
ReadLockScoped cs(_queueRWLock);
return _frameBufferQueue.empty();
}
WebRtc_UWord32 VideoEncodeCompleteCallback::EncodedBytes()
{
return _encodedBytes;
}
WebRtc_Word32
VideoEncodeCompleteCallback::Encoded(EncodedImage& encodedImage,
const webrtc::CodecSpecificInfo* codecSpecificInfo,
const webrtc::RTPFragmentationHeader*
fragmentation)
{
_test.Encoded(encodedImage);
TestVideoEncodedBuffer *newBuffer = new TestVideoEncodedBuffer();
//newBuffer->VerifyAndAllocate(encodedImage._length);
newBuffer->VerifyAndAllocate(encodedImage._size);
_encodedBytes += encodedImage._length;
// If _frameQueue would have been a fixed sized buffer we could have asked
// it for an empty frame and then just do:
// emptyFrame->SwapBuffers(encodedBuffer);
// This is how it should be done in Video Engine to save in on memcpys
webrtc::CodecSpecificInfo* codecSpecificInfoCopy =
_test.CopyCodecSpecificInfo(codecSpecificInfo);
_test.CopyEncodedImage(*newBuffer, encodedImage, codecSpecificInfoCopy);
if (_encodedFile != NULL)
{
fwrite(newBuffer->GetBuffer(), 1, newBuffer->GetLength(), _encodedFile);
}
_frameQueue->PushFrame(newBuffer, codecSpecificInfoCopy);
return 0;
}
WebRtc_UWord32 VideoDecodeCompleteCallback::DecodedBytes()
{
return _decodedBytes;
}
WebRtc_Word32
VideoDecodeCompleteCallback::Decoded(RawImage& image)
{
_test.Decoded(image);
_decodedBytes += image._length;
if (_decodedFile != NULL)
{
fwrite(image._buffer, 1, image._length, _decodedFile);
}
return 0;
}
WebRtc_Word32
VideoDecodeCompleteCallback::ReceivedDecodedReferenceFrame(
const WebRtc_UWord64 pictureId)
{
return _test.ReceivedDecodedReferenceFrame(pictureId);
}
WebRtc_Word32
VideoDecodeCompleteCallback::ReceivedDecodedFrame(
const WebRtc_UWord64 pictureId)
{
return _test.ReceivedDecodedFrame(pictureId);
}
void
NormalAsyncTest::Encoded(const EncodedImage& encodedImage)
{
_encodeCompleteTime = tGetTime();
_encFrameCnt++;
_totalEncodePipeTime += _encodeCompleteTime -
_encodeTimes[encodedImage._timeStamp];
}
void
NormalAsyncTest::Decoded(const RawImage& decodedImage)
{
_decodeCompleteTime = tGetTime();
_decFrameCnt++;
_totalDecodePipeTime += _decodeCompleteTime -
_decodeTimes[decodedImage._timeStamp];
_decodedWidth = decodedImage._width;
_decodedHeight = decodedImage._height;
}
void
NormalAsyncTest::Perform()
{
_inname = webrtc::test::ProjectRootPath() + "resources/foreman_cif.yuv";
CodecSettings(352, 288, 30, _bitRate);
Setup();
_inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
_decodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame);
if(_encoder->InitEncode(&_inst, 1, 1440) < 0)
{
exit(EXIT_FAILURE);
}
_decoder->InitDecode(&_inst, 1);
FrameQueue frameQueue;
VideoEncodeCompleteCallback encCallback(_encodedFile, &frameQueue, *this);
VideoDecodeCompleteCallback decCallback(_decodedFile, *this);
_encoder->RegisterEncodeCompleteCallback(&encCallback);
_decoder->RegisterDecodeCompleteCallback(&decCallback);
if (SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK)
{
exit(EXIT_FAILURE);
}
_totalEncodeTime = _totalDecodeTime = 0;
_totalEncodePipeTime = _totalDecodePipeTime = 0;
bool complete = false;
_framecnt = 0;
_encFrameCnt = 0;
_decFrameCnt = 0;
_sumEncBytes = 0;
_lengthEncFrame = 0;
double starttime = tGetTime();
while (!complete)
{
CodecSpecific_InitBitrate();
complete = Encode();
if (!frameQueue.Empty() || complete)
{
while (!frameQueue.Empty())
{
_frameToDecode =
static_cast<FrameQueueTuple *>(frameQueue.PopFrame());
int lost = DoPacketLoss();
if (lost == 2)
{
// Lost the whole frame, continue
_missingFrames = true;
delete _frameToDecode;
_frameToDecode = NULL;
continue;
}
int ret = Decode(lost);
delete _frameToDecode;
_frameToDecode = NULL;
if (ret < 0)
{
fprintf(stderr,"\n\nError in decoder: %d\n\n", ret);
exit(EXIT_FAILURE);
}
else if (ret == 0)
{
_framecnt++;
}
else
{
fprintf(stderr,
"\n\nPositive return value from decode!\n\n");
}
}
}
}
double endtime = tGetTime();
double totalExecutionTime = endtime - starttime;
printf("Total execution time: %.1f s\n", totalExecutionTime);
_sumEncBytes = encCallback.EncodedBytes();
double actualBitRate = ActualBitRate(_encFrameCnt) / 1000.0;
double avgEncTime = _totalEncodeTime / _encFrameCnt;
double avgDecTime = _totalDecodeTime / _decFrameCnt;
printf("Actual bitrate: %f kbps\n", actualBitRate);
printf("Average encode time: %.1f ms\n", 1000 * avgEncTime);
printf("Average decode time: %.1f ms\n", 1000 * avgDecTime);
printf("Average encode pipeline time: %.1f ms\n",
1000 * _totalEncodePipeTime / _encFrameCnt);
printf("Average decode pipeline time: %.1f ms\n",
1000 * _totalDecodePipeTime / _decFrameCnt);
printf("Number of encoded frames: %u\n", _encFrameCnt);
printf("Number of decoded frames: %u\n", _decFrameCnt);
(*_log) << "Actual bitrate: " << actualBitRate << " kbps\tTarget: " <<
_bitRate << " kbps" << std::endl;
(*_log) << "Average encode time: " << avgEncTime << " s" << std::endl;
(*_log) << "Average decode time: " << avgDecTime << " s" << std::endl;
_encoder->Release();
_decoder->Release();
Teardown();
}
bool
NormalAsyncTest::Encode()
{
_lengthEncFrame = 0;
EXPECT_GT(fread(_sourceBuffer, 1, _lengthSourceFrame, _sourceFile), 0u);
_inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _sourceBuffer);
_inputVideoBuffer.SetTimeStamp((unsigned int)
(_encFrameCnt * 9e4 / _inst.maxFramerate));
_inputVideoBuffer.SetWidth(_inst.width);
_inputVideoBuffer.SetHeight(_inst.height);
RawImage rawImage;
VideoBufferToRawImage(_inputVideoBuffer, rawImage);
if (feof(_sourceFile) != 0)
{
return true;
}
_encodeCompleteTime = 0;
_encodeTimes[rawImage._timeStamp] = tGetTime();
VideoFrameType frameType = kDeltaFrame;
// check SLI queue
_hasReceivedSLI = false;
while (!_signalSLI.empty() && _signalSLI.front().delay == 0)
{
// SLI message has arrived at sender side
_hasReceivedSLI = true;
_pictureIdSLI = _signalSLI.front().id;
_signalSLI.pop_front();
}
// decrement SLI queue times
for (std::list<fbSignal>::iterator it = _signalSLI.begin();
it !=_signalSLI.end(); it++)
{
(*it).delay--;
}
// check PLI queue
_hasReceivedPLI = false;
while (!_signalPLI.empty() && _signalPLI.front().delay == 0)
{
// PLI message has arrived at sender side
_hasReceivedPLI = true;
_signalPLI.pop_front();
}
// decrement PLI queue times
for (std::list<fbSignal>::iterator it = _signalPLI.begin();
it != _signalPLI.end(); it++)
{
(*it).delay--;
}
if (_hasReceivedPLI)
{
// respond to PLI by encoding a key frame
frameType = kKeyFrame;
_hasReceivedPLI = false;
_hasReceivedSLI = false; // don't trigger both at once
}
webrtc::CodecSpecificInfo* codecSpecificInfo = CreateEncoderSpecificInfo();
int ret = _encoder->Encode(rawImage, codecSpecificInfo, &frameType);
EXPECT_EQ(ret, WEBRTC_VIDEO_CODEC_OK);
if (codecSpecificInfo != NULL)
{
delete codecSpecificInfo;
codecSpecificInfo = NULL;
}
if (_encodeCompleteTime > 0)
{
_totalEncodeTime += _encodeCompleteTime -
_encodeTimes[rawImage._timeStamp];
}
else
{
_totalEncodeTime += tGetTime() - _encodeTimes[rawImage._timeStamp];
}
assert(ret >= 0);
return false;
}
int
NormalAsyncTest::Decode(int lossValue)
{
_sumEncBytes += _frameToDecode->_frame->GetLength();
EncodedImage encodedImage;
VideoEncodedBufferToEncodedImage(*(_frameToDecode->_frame), encodedImage);
encodedImage._completeFrame = !lossValue;
_decodeCompleteTime = 0;
_decodeTimes[encodedImage._timeStamp] = tGetTime();
int ret = WEBRTC_VIDEO_CODEC_OK;
if (!_waitForKey || encodedImage._frameType == kKeyFrame)
{
_waitForKey = false;
ret = _decoder->Decode(encodedImage, _missingFrames, NULL,
_frameToDecode->_codecSpecificInfo);
if (ret >= 0)
{
_missingFrames = false;
}
}
// check for SLI
if (ret == WEBRTC_VIDEO_CODEC_REQUEST_SLI)
{
// add an SLI feedback to the feedback "queue"
// to be delivered to encoder with _rttFrames delay
_signalSLI.push_back(fbSignal(_rttFrames,
static_cast<WebRtc_UWord8>((_lastDecPictureId) & 0x3f))); // 6 lsb
ret = WEBRTC_VIDEO_CODEC_OK;
}
else if (ret == WEBRTC_VIDEO_CODEC_ERR_REQUEST_SLI)
{
// add an SLI feedback to the feedback "queue"
// to be delivered to encoder with _rttFrames delay
_signalSLI.push_back(fbSignal(_rttFrames,
static_cast<WebRtc_UWord8>((_lastDecPictureId + 1) & 0x3f)));//6 lsb
ret = WEBRTC_VIDEO_CODEC_OK;
}
else if (ret == WEBRTC_VIDEO_CODEC_ERROR)
{
// wait for new key frame
// add an PLI feedback to the feedback "queue"
// to be delivered to encoder with _rttFrames delay
_signalPLI.push_back(fbSignal(_rttFrames, 0 /* picId not used*/));
_waitForKey = true;
ret = WEBRTC_VIDEO_CODEC_OK;
}
if (_decodeCompleteTime > 0)
{
_totalDecodeTime += _decodeCompleteTime -
_decodeTimes[encodedImage._timeStamp];
}
else
{
_totalDecodeTime += tGetTime() - _decodeTimes[encodedImage._timeStamp];
}
return ret;
}
webrtc::CodecSpecificInfo*
NormalAsyncTest::CopyCodecSpecificInfo(
const webrtc::CodecSpecificInfo* codecSpecificInfo) const
{
webrtc::CodecSpecificInfo* info = new webrtc::CodecSpecificInfo;
*info = *codecSpecificInfo;
return info;
}
void NormalAsyncTest::CodecSpecific_InitBitrate()
{
if (_bitRate == 0)
{
_encoder->SetRates(600, _inst.maxFramerate);
}
else
{
_encoder->SetRates(_bitRate, _inst.maxFramerate);
}
}
void NormalAsyncTest::CopyEncodedImage(TestVideoEncodedBuffer& dest,
EncodedImage& src,
void* /*codecSpecificInfo*/) const
{
dest.CopyBuffer(src._length, src._buffer);
dest.SetFrameType(src._frameType);
dest.SetCaptureWidth((WebRtc_UWord16)src._encodedWidth);
dest.SetCaptureHeight((WebRtc_UWord16)src._encodedHeight);
dest.SetTimeStamp(src._timeStamp);
}
WebRtc_Word32 NormalAsyncTest::ReceivedDecodedReferenceFrame(
const WebRtc_UWord64 pictureId) {
_lastDecRefPictureId = pictureId;
return 0;
}
WebRtc_Word32 NormalAsyncTest::ReceivedDecodedFrame(
const WebRtc_UWord64 pictureId) {
_lastDecPictureId = pictureId;
return 0;
}
double
NormalAsyncTest::tGetTime()
{// return time in sec
return ((double) (TickTime::MillisecondTimestamp())/1000);
}