blob: c94fc9a86f76fb72fc7a4916ddf352bb57e81954 [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 "packet_loss_test.h"
#include "video_source.h"
#include <sstream>
#include <cassert>
#include <string.h>
using namespace webrtc;
PacketLossTest::PacketLossTest()
:
NormalAsyncTest("PacketLossTest", "Encode, remove lost packets, decode", 300,
5),
_lossRate(0.1),
_lossProbability(0.1),
_lastFrame(NULL),
_lastFrameLength(0)
{
}
PacketLossTest::PacketLossTest(std::string name, std::string description)
:
NormalAsyncTest(name, description, 300, 5),
_lossRate(0.1),
_lossProbability(0.1),
_lastFrame(NULL),
_lastFrameLength(0)
{
}
PacketLossTest::PacketLossTest(std::string name, std::string description, double lossRate, bool useNack, unsigned int rttFrames /* = 0*/)
:
NormalAsyncTest(name, description, 300, 5, rttFrames),
_lossRate(lossRate),
_lastFrame(NULL),
_lastFrameLength(0)
{
assert(lossRate >= 0 && lossRate <= 1);
if (useNack)
{
_lossProbability = 0;
}
else
{
_lossProbability = lossRate;
}
}
void
PacketLossTest::Encoded(const EncodedImage& encodedImage)
{
// push timestamp to queue
_frameQueue.push_back(encodedImage._timeStamp);
NormalAsyncTest::Encoded(encodedImage);
}
void
PacketLossTest::Decoded(const RawImage& decodedImage)
{
// check the frame queue if any frames have gone missing
assert(!_frameQueue.empty()); // decoded frame is not in the queue
while(_frameQueue.front() < decodedImage._timeStamp)
{
// this frame is missing
// write previous decoded frame again (frame freeze)
if (_decodedFile && _lastFrame)
{
fwrite(_lastFrame, 1, _lastFrameLength, _decodedFile);
}
// remove frame from queue
_frameQueue.pop_front();
}
assert(_frameQueue.front() == decodedImage._timeStamp); // decoded frame is not in the queue
// pop the current frame
_frameQueue.pop_front();
// save image for future freeze-frame
if (_lastFrameLength < decodedImage._length)
{
if (_lastFrame) delete [] _lastFrame;
_lastFrame = new WebRtc_UWord8[decodedImage._length];
}
memcpy(_lastFrame, decodedImage._buffer, decodedImage._length);
_lastFrameLength = decodedImage._length;
NormalAsyncTest::Decoded(decodedImage);
}
void
PacketLossTest::Teardown()
{
if (_totalKept + _totalThrown > 0)
{
printf("Target packet loss rate: %.4f\n", _lossProbability);
printf("Actual packet loss rate: %.4f\n", (_totalThrown * 1.0f) / (_totalKept + _totalThrown));
printf("Channel rate: %.2f kbps\n",
0.001 * 8.0 * _sumChannelBytes / ((_framecnt * 1.0f) / _inst.maxFramerate));
}
else
{
printf("No packet losses inflicted\n");
}
NormalAsyncTest::Teardown();
}
void
PacketLossTest::Setup()
{
const VideoSource source(_inname, _inst.width, _inst.height, _inst.maxFramerate);
std::stringstream ss;
std::string lossRateStr;
ss << _lossRate;
ss >> lossRateStr;
_encodedName = source.GetName() + "-" + lossRateStr;
_outname = "out-" + source.GetName() + "-" + lossRateStr;
if (_lossProbability != _lossRate)
{
_encodedName += "-nack";
_outname += "-nack";
}
_encodedName += ".vp8";
_outname += ".yuv";
_totalKept = 0;
_totalThrown = 0;
_sumChannelBytes = 0;
NormalAsyncTest::Setup();
}
void
PacketLossTest::CodecSpecific_InitBitrate()
{
assert(_bitRate > 0);
WebRtc_UWord32 simulatedBitRate;
if (_lossProbability != _lossRate)
{
// Simulating NACK
simulatedBitRate = WebRtc_UWord32(_bitRate / (1 + _lossRate));
}
else
{
simulatedBitRate = _bitRate;
}
int rtt = 0;
if (_inst.maxFramerate > 0)
rtt = _rttFrames * (1000 / _inst.maxFramerate);
_encoder->SetChannelParameters((WebRtc_UWord32)(_lossProbability * 255.0),
rtt);
_encoder->SetRates(simulatedBitRate, _inst.maxFramerate);
}
int PacketLossTest::DoPacketLoss()
{
// Only packet loss for delta frames
if (_frameToDecode->_frame->GetLength() == 0 || _frameToDecode->_frame->GetFrameType() != kDeltaFrame)
{
_sumChannelBytes += _frameToDecode->_frame->GetLength();
return 0;
}
unsigned char *packet = NULL;
TestVideoEncodedBuffer newEncBuf;
newEncBuf.VerifyAndAllocate(_lengthSourceFrame);
_inBufIdx = 0;
_outBufIdx = 0;
int size = 1;
int kept = 0;
int thrown = 0;
while ((size = NextPacket(1500, &packet)) > 0)
{
if (!PacketLoss(_lossProbability, thrown))
{
InsertPacket(&newEncBuf, packet, size);
kept++;
}
else
{
// Use the ByteLoss function if you want to lose only
// parts of a packet, and not the whole packet.
//int size2 = ByteLoss(size, packet, 15);
thrown++;
//if (size2 != size)
//{
// InsertPacket(&newEncBuf, packet, size2);
//}
}
}
int lossResult = (thrown!=0); // 0 = no loss 1 = loss(es)
if (lossResult)
{
lossResult += (kept==0); // 2 = all lost = full frame
}
_frameToDecode->_frame->CopyBuffer(newEncBuf.GetLength(), newEncBuf.GetBuffer());
_sumChannelBytes += newEncBuf.GetLength();
_totalKept += kept;
_totalThrown += thrown;
return lossResult;
//printf("Threw away: %d out of %d packets\n", thrown, thrown + kept);
//printf("Encoded left: %d bytes\n", _encodedVideoBuffer.GetLength());
}
int PacketLossTest::NextPacket(int mtu, unsigned char **pkg)
{
unsigned char *buf = _frameToDecode->_frame->GetBuffer();
*pkg = buf + _inBufIdx;
if (static_cast<long>(_frameToDecode->_frame->GetLength()) - _inBufIdx <= mtu)
{
int size = _frameToDecode->_frame->GetLength() - _inBufIdx;
_inBufIdx = _frameToDecode->_frame->GetLength();
return size;
}
_inBufIdx += mtu;
return mtu;
}
int PacketLossTest::ByteLoss(int size, unsigned char *pkg, int bytesToLose)
{
return size;
}
void PacketLossTest::InsertPacket(TestVideoEncodedBuffer *buf, unsigned char *pkg, int size)
{
if (static_cast<long>(buf->GetSize()) - _outBufIdx < size)
{
printf("InsertPacket error!\n");
return;
}
memcpy(buf->GetBuffer() + _outBufIdx, pkg, size);
buf->UpdateLength(buf->GetLength() + size);
_outBufIdx += size;
}