1/* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#include <assert.h> 12#include <string.h> 13 14#include <sstream> 15 16#include "webrtc/modules/video_coding/codecs/test_framework/packet_loss_test.h" 17#include "webrtc/modules/video_coding/codecs/test_framework/video_source.h" 18 19using namespace webrtc; 20 21PacketLossTest::PacketLossTest() 22: 23NormalAsyncTest("PacketLossTest", "Encode, remove lost packets, decode", 300, 24 5), 25_lossRate(0.1), 26_lossProbability(0.1), 27_lastFrame(NULL), 28_lastFrameLength(0) 29{ 30} 31 32PacketLossTest::PacketLossTest(std::string name, std::string description) 33: 34NormalAsyncTest(name, description, 300, 5), 35_lossRate(0.1), 36_lossProbability(0.1), 37_lastFrame(NULL), 38_lastFrameLength(0) 39{ 40} 41 42PacketLossTest::PacketLossTest(std::string name, std::string description, double lossRate, bool useNack, unsigned int rttFrames /* = 0*/) 43: 44NormalAsyncTest(name, description, 300, 5, rttFrames), 45_lossRate(lossRate), 46_lastFrame(NULL), 47_lastFrameLength(0) 48{ 49 assert(lossRate >= 0 && lossRate <= 1); 50 if (useNack) 51 { 52 _lossProbability = 0; 53 } 54 else 55 { 56 _lossProbability = lossRate; 57 } 58} 59 60void 61PacketLossTest::Encoded(const EncodedImage& encodedImage) 62{ 63 // push timestamp to queue 64 _frameQueue.push_back(encodedImage._timeStamp); 65 NormalAsyncTest::Encoded(encodedImage); 66} 67 68void 69PacketLossTest::Decoded(const I420VideoFrame& decodedImage) 70{ 71 // check the frame queue if any frames have gone missing 72 assert(!_frameQueue.empty()); // decoded frame is not in the queue 73 while(_frameQueue.front() < decodedImage.timestamp()) 74 { 75 // this frame is missing 76 // write previous decoded frame again (frame freeze) 77 if (_decodedFile && _lastFrame) 78 { 79 if (fwrite(_lastFrame, 1, _lastFrameLength, 80 _decodedFile) != _lastFrameLength) { 81 return; 82 } 83 } 84 85 // remove frame from queue 86 _frameQueue.pop_front(); 87 } 88 // Decoded frame is not in the queue. 89 assert(_frameQueue.front() == decodedImage.timestamp()); 90 91 // pop the current frame 92 _frameQueue.pop_front(); 93 94 // save image for future freeze-frame 95 unsigned int length = CalcBufferSize(kI420, decodedImage.width(), 96 decodedImage.height()); 97 if (_lastFrameLength < length) 98 { 99 if (_lastFrame) delete [] _lastFrame; 100 101 _lastFrame = new uint8_t[length]; 102 } 103 // TODO(mikhal): Can't the last frame be a I420VideoFrame? 104 ExtractBuffer(decodedImage, length, _lastFrame); 105 _lastFrameLength = length; 106 107 NormalAsyncTest::Decoded(decodedImage); 108} 109 110void 111PacketLossTest::Teardown() 112{ 113 if (_totalKept + _totalThrown > 0) 114 { 115 printf("Target packet loss rate: %.4f\n", _lossProbability); 116 printf("Actual packet loss rate: %.4f\n", (_totalThrown * 1.0f) / (_totalKept + _totalThrown)); 117 printf("Channel rate: %.2f kbps\n", 118 0.001 * 8.0 * _sumChannelBytes / ((_framecnt * 1.0f) / _inst.maxFramerate)); 119 } 120 else 121 { 122 printf("No packet losses inflicted\n"); 123 } 124 125 NormalAsyncTest::Teardown(); 126} 127 128void 129PacketLossTest::Setup() 130{ 131 const VideoSource source(_inname, _inst.width, _inst.height, _inst.maxFramerate); 132 133 std::stringstream ss; 134 std::string lossRateStr; 135 ss << _lossRate; 136 ss >> lossRateStr; 137 _encodedName = source.GetName() + "-" + lossRateStr; 138 _outname = "out-" + source.GetName() + "-" + lossRateStr; 139 140 if (_lossProbability != _lossRate) 141 { 142 _encodedName += "-nack"; 143 _outname += "-nack"; 144 } 145 _encodedName += ".vp8"; 146 _outname += ".yuv"; 147 148 _totalKept = 0; 149 _totalThrown = 0; 150 _sumChannelBytes = 0; 151 152 NormalAsyncTest::Setup(); 153} 154 155void 156PacketLossTest::CodecSpecific_InitBitrate() 157{ 158 assert(_bitRate > 0); 159 uint32_t simulatedBitRate; 160 if (_lossProbability != _lossRate) 161 { 162 // Simulating NACK 163 simulatedBitRate = uint32_t(_bitRate / (1 + _lossRate)); 164 } 165 else 166 { 167 simulatedBitRate = _bitRate; 168 } 169 int rtt = 0; 170 if (_inst.maxFramerate > 0) 171 rtt = _rttFrames * (1000 / _inst.maxFramerate); 172 _encoder->SetChannelParameters((uint32_t)(_lossProbability * 255.0), 173 rtt); 174 _encoder->SetRates(simulatedBitRate, _inst.maxFramerate); 175} 176 177int PacketLossTest::DoPacketLoss() 178{ 179 // Only packet loss for delta frames 180 // TODO(mikhal): Identify delta frames 181 // First frame so never a delta frame. 182 if (_frameToDecode->_frame->Length() == 0 || _sumChannelBytes == 0) 183 { 184 _sumChannelBytes += _frameToDecode->_frame->Length(); 185 return 0; 186 } 187 unsigned char *packet = NULL; 188 VideoFrame newEncBuf; 189 newEncBuf.VerifyAndAllocate(_lengthSourceFrame); 190 _inBufIdx = 0; 191 _outBufIdx = 0; 192 int size = 1; 193 int kept = 0; 194 int thrown = 0; 195 while ((size = NextPacket(1500, &packet)) > 0) 196 { 197 if (!PacketLoss(_lossProbability, thrown)) 198 { 199 InsertPacket(&newEncBuf, packet, size); 200 kept++; 201 } 202 else 203 { 204 // Use the ByteLoss function if you want to lose only 205 // parts of a packet, and not the whole packet. 206 207 //int size2 = ByteLoss(size, packet, 15); 208 thrown++; 209 //if (size2 != size) 210 //{ 211 // InsertPacket(&newEncBuf, packet, size2); 212 //} 213 } 214 } 215 int lossResult = (thrown!=0); // 0 = no loss 1 = loss(es) 216 if (lossResult) 217 { 218 lossResult += (kept==0); // 2 = all lost = full frame 219 } 220 _frameToDecode->_frame->CopyFrame(newEncBuf.Length(), newEncBuf.Buffer()); 221 _sumChannelBytes += newEncBuf.Length(); 222 _totalKept += kept; 223 _totalThrown += thrown; 224 225 return lossResult; 226 //printf("Threw away: %d out of %d packets\n", thrown, thrown + kept); 227 //printf("Encoded left: %d bytes\n", _encodedVideoBuffer.Length()); 228} 229 230int PacketLossTest::NextPacket(int mtu, unsigned char **pkg) 231{ 232 unsigned char *buf = _frameToDecode->_frame->Buffer(); 233 *pkg = buf + _inBufIdx; 234 if (static_cast<long>(_frameToDecode->_frame->Length()) - _inBufIdx <= mtu) 235 { 236 int size = _frameToDecode->_frame->Length() - _inBufIdx; 237 _inBufIdx = _frameToDecode->_frame->Length(); 238 return size; 239 } 240 _inBufIdx += mtu; 241 return mtu; 242} 243 244int PacketLossTest::ByteLoss(int size, unsigned char *pkg, int bytesToLose) 245{ 246 return size; 247} 248 249void PacketLossTest::InsertPacket(VideoFrame *buf, unsigned char *pkg, int size) 250{ 251 if (static_cast<long>(buf->Size()) - _outBufIdx < size) 252 { 253 printf("InsertPacket error!\n"); 254 return; 255 } 256 memcpy(buf->Buffer() + _outBufIdx, pkg, size); 257 buf->SetLength(buf->Length() + size); 258 _outBufIdx += size; 259} 260