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