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 19 using namespace webrtc; 20 21 PacketLossTest::PacketLossTest() 22 : 23 NormalAsyncTest("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 32 PacketLossTest::PacketLossTest(std::string name, std::string description) 33 : 34 NormalAsyncTest(name, description, 300, 5), 35 _lossRate(0.1), 36 _lossProbability(0.1), 37 _lastFrame(NULL), 38 _lastFrameLength(0) 39 { 40 } 41 42 PacketLossTest::PacketLossTest(std::string name, std::string description, double lossRate, bool useNack, unsigned int rttFrames /* = 0*/) 43 : 44 NormalAsyncTest(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 60 void 61 PacketLossTest::Encoded(const EncodedImage& encodedImage) 62 { 63 // push timestamp to queue 64 _frameQueue.push_back(encodedImage._timeStamp); 65 NormalAsyncTest::Encoded(encodedImage); 66 } 67 68 void 69 PacketLossTest::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 110 void 111 PacketLossTest::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 128 void 129 PacketLossTest::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 155 void 156 PacketLossTest::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 177 int 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 230 int 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 244 int PacketLossTest::ByteLoss(int size, unsigned char *pkg, int bytesToLose) 245 { 246 return size; 247 } 248 249 void 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