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
PacketLossTest()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
PacketLossTest(std::string name,std::string description)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
PacketLossTest(std::string name,std::string description,double lossRate,bool useNack,unsigned int rttFrames)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
Encoded(const EncodedImage & encodedImage)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
Decoded(const I420VideoFrame & decodedImage)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
Teardown()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
Setup()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
CodecSpecific_InitBitrate()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
DoPacketLoss()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
NextPacket(int mtu,unsigned char ** pkg)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
ByteLoss(int size,unsigned char * pkg,int bytesToLose)244 int PacketLossTest::ByteLoss(int size, unsigned char *pkg, int bytesToLose)
245 {
246 return size;
247 }
248
InsertPacket(VideoFrame * buf,unsigned char * pkg,int size)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