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 "webrtc/modules/video_coding/main/test/generic_codec_test.h"
12
13#include <math.h>
14#include <stdio.h>
15
16#include "webrtc/common_video/interface/i420_video_frame.h"
17#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
18#include "webrtc/modules/video_coding/main/interface/video_coding.h"
19#include "webrtc/modules/video_coding/main/test/test_macros.h"
20#include "webrtc/system_wrappers/interface/clock.h"
21#include "webrtc/test/testsupport/fileutils.h"
22
23using namespace webrtc;
24
25enum { kMaxWaitEncTimeMs = 100 };
26
27int GenericCodecTest::RunTest(CmdArgs& args)
28{
29    SimulatedClock clock(0);
30    NullEventFactory event_factory;
31    VideoCodingModule* vcm = VideoCodingModule::Create(&clock, &event_factory);
32    GenericCodecTest* get = new GenericCodecTest(vcm, &clock);
33    Trace::CreateTrace();
34    Trace::SetTraceFile(
35        (test::OutputPath() + "genericCodecTestTrace.txt").c_str());
36    Trace::set_level_filter(webrtc::kTraceAll);
37    get->Perform(args);
38    Trace::ReturnTrace();
39    delete get;
40    VideoCodingModule::Destroy(vcm);
41    return 0;
42}
43
44GenericCodecTest::GenericCodecTest(VideoCodingModule* vcm,
45                                   SimulatedClock* clock):
46_clock(clock),
47_vcm(vcm),
48_width(0),
49_height(0),
50_frameRate(0),
51_lengthSourceFrame(0),
52_timeStamp(0)
53{
54}
55
56GenericCodecTest::~GenericCodecTest()
57{
58}
59
60void
61GenericCodecTest::Setup(CmdArgs& args)
62{
63    _timeStamp = 0;
64
65    /* Test Sequence parameters */
66
67    _inname= args.inputFile;
68    if (args.outputFile.compare(""))
69        _outname = test::OutputPath() + "GCTest_decoded.yuv";
70    else
71        _outname = args.outputFile;
72    _encodedName = test::OutputPath() + "GCTest_encoded.vp8";
73    _width = args.width;
74    _height = args.height;
75    _frameRate = args.frameRate;
76    _lengthSourceFrame  = 3*_width*_height/2;
77
78    /* File settings */
79
80    if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL)
81    {
82        printf("Cannot read file %s.\n", _inname.c_str());
83        exit(1);
84    }
85    if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL)
86    {
87        printf("Cannot write encoded file.\n");
88        exit(1);
89    }
90    if ((_decodedFile = fopen(_outname.c_str(),  "wb")) == NULL)
91    {
92        printf("Cannot write file %s.\n", _outname.c_str());
93        exit(1);
94    }
95
96    return;
97}
98int32_t
99GenericCodecTest::Perform(CmdArgs& args)
100{
101    int32_t ret;
102    Setup(args);
103    /*
104    1. sanity checks
105    2. encode/decoder individuality
106    3. API testing
107    4. Target bitrate (within a specific timespan)
108    5. Pipeline Delay
109    */
110
111    /*******************************/
112    /* sanity checks on inputs    */
113    /*****************************/
114    VideoCodec sendCodec, receiveCodec;
115    sendCodec.maxBitrate = 8000;
116    TEST(_vcm->NumberOfCodecs() > 0); // This works since we now initialize the list in the constructor
117    TEST(_vcm->Codec(0, &sendCodec)  == VCM_OK);
118    _vcm->InitializeSender();
119    _vcm->InitializeReceiver();
120    int32_t NumberOfCodecs = _vcm->NumberOfCodecs();
121    // registration of first codec in the list
122    int i = 0;
123    _vcm->Codec(0, &_sendCodec);
124    TEST(_vcm->RegisterSendCodec(&_sendCodec, 4, 1440) == VCM_OK);
125    // sanity on encoder registration
126    I420VideoFrame sourceFrame;
127    _vcm->InitializeSender();
128    TEST(_vcm->Codec(kVideoCodecVP8, &sendCodec) == 0);
129    sendCodec.maxBitrate = 8000;
130    _vcm->RegisterSendCodec(&sendCodec, 1, 1440);
131    _vcm->InitializeSender();
132    _vcm->Codec(kVideoCodecVP8, &sendCodec);
133    sendCodec.height = 0;
134    TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) < 0); // bad height
135    _vcm->Codec(kVideoCodecVP8, &sendCodec);
136    _vcm->Codec(kVideoCodecVP8, &sendCodec);
137    _vcm->InitializeSender();
138    // Setting rate when encoder uninitialized.
139    TEST(_vcm->SetChannelParameters(100000, 0, 0) < 0);
140    // register all availbale decoders -- need to have more for this test
141    for (i=0; i< NumberOfCodecs; i++)
142    {
143        _vcm->Codec(i, &receiveCodec);
144        _vcm->RegisterReceiveCodec(&receiveCodec, 1);
145    }
146    uint8_t* tmpBuffer = new uint8_t[_lengthSourceFrame];
147    TEST(fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0);
148    int half_width = (_width + 1) / 2;
149    int half_height = (_height + 1) / 2;
150    int size_y = _width * _height;
151    int size_uv = half_width * half_height;
152    sourceFrame.CreateFrame(size_y, tmpBuffer,
153                            size_uv, tmpBuffer + size_y,
154                            size_uv, tmpBuffer + size_y + size_uv,
155                            _width, _height,
156                            _width, half_width, half_width);
157    sourceFrame.set_timestamp(_timeStamp++);
158    TEST(_vcm->AddVideoFrame(sourceFrame) < 0 ); // encoder uninitialized
159    _vcm->InitializeReceiver();
160    // Setting rtt when receiver uninitialized.
161    TEST(_vcm->SetChannelParameters(100000, 0, 0) < 0);
162
163      /**************************************/
164     /* encoder/decoder individuality test */
165    /**************************************/
166    //Register both encoder and decoder, reset decoder - encode, set up decoder, reset encoder - decode.
167    rewind(_sourceFile);
168    _vcm->InitializeReceiver();
169    _vcm->InitializeSender();
170    NumberOfCodecs = _vcm->NumberOfCodecs();
171    // Register VP8
172    _vcm->Codec(kVideoCodecVP8, &_sendCodec);
173    _vcm->RegisterSendCodec(&_sendCodec, 4, 1440);
174    _vcm->SendCodec(&sendCodec);
175    sendCodec.startBitrate = 2000;
176
177    // Set target frame rate to half of the incoming frame rate
178    // to test the frame rate control in the VCM
179    sendCodec.maxFramerate = (uint8_t)(_frameRate / 2);
180    sendCodec.width = _width;
181    sendCodec.height = _height;
182    TEST(strncmp(_sendCodec.plName, "VP8", 3) == 0); // was VP8
183
184    _decodeCallback = new VCMDecodeCompleteCallback(_decodedFile);
185    _encodeCompleteCallback = new VCMEncodeCompleteCallback(_encodedFile);
186    _vcm->RegisterReceiveCallback(_decodeCallback);
187    _vcm->RegisterTransportCallback(_encodeCompleteCallback);
188    _encodeCompleteCallback->RegisterReceiverVCM(_vcm);
189
190    _vcm->RegisterSendCodec(&sendCodec, 4, 1440);
191    _encodeCompleteCallback->SetCodecType(ConvertCodecType(sendCodec.plName));
192
193    _vcm->InitializeReceiver();
194    _vcm->Process();
195
196    //encoding 1 second of video
197    for (i = 0; i < _frameRate; i++)
198    {
199        TEST(fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0);
200        sourceFrame.CreateFrame(size_y, tmpBuffer,
201                                size_uv, tmpBuffer + size_y,
202                                size_uv, tmpBuffer + size_y + size_uv,
203                                _width, _height,
204                                _width, half_width, half_width);
205        _timeStamp += (uint32_t)(9e4 / static_cast<float>(_frameRate));
206        sourceFrame.set_timestamp(_timeStamp);
207        TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
208        IncrementDebugClock(_frameRate);
209        _vcm->Process();
210    }
211    sendCodec.maxFramerate = (uint8_t)_frameRate;
212    _vcm->InitializeSender();
213    TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK); // same codec for encode and decode
214    ret = 0;
215    i = 0;
216    while ((i < 25) && (ret == 0) )
217    {
218        ret = _vcm->Decode();
219        TEST(ret == VCM_OK);
220        if (ret < 0)
221        {
222            printf("error in frame # %d \n", i);
223        }
224        IncrementDebugClock(_frameRate);
225        i++;
226    }
227    //TEST((ret == 0) && (i = 50));
228    if (ret == 0)
229    {
230        printf("Encoder/Decoder individuality test complete - View output files \n");
231    }
232    // last frame - not decoded
233    _vcm->InitializeReceiver();
234    TEST(_vcm->Decode() < 0); // frame to be encoded exists, decoder uninitialized
235
236
237    // Test key frame request on packet loss mode.
238    // This a frame as a key frame and fooling the receiver
239    // that the last packet was lost. The decoding will succeed,
240    // but the VCM will see a packet loss and request a new key frame.
241    VCMEncComplete_KeyReqTest keyReqTest_EncCompleteCallback(*_vcm);
242    KeyFrameReqTest frameTypeCallback;
243    _vcm->RegisterTransportCallback(&keyReqTest_EncCompleteCallback);
244    _encodeCompleteCallback->RegisterReceiverVCM(_vcm);
245    _vcm->RegisterSendCodec(&sendCodec, 4, 1440);
246    _encodeCompleteCallback->SetCodecType(ConvertCodecType(sendCodec.plName));
247    TEST(_vcm->SetVideoProtection(kProtectionKeyOnKeyLoss, true) == VCM_OK);
248    TEST(_vcm->RegisterFrameTypeCallback(&frameTypeCallback) == VCM_OK);
249    TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK);
250    TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
251    _timeStamp += (uint32_t)(9e4 / static_cast<float>(_frameRate));
252    sourceFrame.set_timestamp(_timeStamp);
253    // First packet of a subsequent frame required before the jitter buffer
254    // will allow decoding an incomplete frame.
255    TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK);
256    TEST(_vcm->Decode() == VCM_OK);
257
258    printf("API tests complete \n");
259
260     /*******************/
261    /* Bit Rate Tests */
262    /*****************/
263    /* Requirements:
264    * 1. OneSecReq = 15 % above/below target over a time period of 1s (_frameRate number of frames)
265    * 3. FullReq  = 10% for total seq. (for 300 frames/seq. coincides with #1)
266    * 4. Test will go over all registered codecs
267    //NOTE: time requirements are not part of the release tests
268    */
269    double FullReq   =  0.1;
270    //double OneSecReq = 0.15;
271    printf("\n RATE CONTROL TEST\n");
272    // initializing....
273    _vcm->InitializeSender();
274    _vcm->InitializeReceiver();
275    rewind(_sourceFile);
276    sourceFrame.CreateEmptyFrame(_width, _height, _width,
277                                 (_width + 1) / 2, (_width + 1) / 2);
278    const float bitRate[] = {100, 400, 600, 1000, 2000};
279    const float nBitrates = sizeof(bitRate)/sizeof(*bitRate);
280    float _bitRate = 0;
281    int _frameCnt = 0;
282    float totalBytesOneSec = 0;//, totalBytesTenSec;
283    float totalBytes, actualBitrate;
284    VCMFrameCount frameCount; // testing frame type counters
285    // start test
286    NumberOfCodecs = _vcm->NumberOfCodecs();
287    // going over all available codecs
288    _encodeCompleteCallback->SetFrameDimensions(_width, _height);
289    SendStatsTest sendStats;
290    for (int k = 0; k < NumberOfCodecs; k++)
291    //for (int k = NumberOfCodecs - 1; k >=0; k--)
292    {// static list starts from 0
293        //just checking
294        _vcm->InitializeSender();
295        _sendCodec.maxBitrate = 8000;
296        TEST(_vcm->Codec(k, &_sendCodec)== VCM_OK);
297        _vcm->RegisterSendCodec(&_sendCodec, 1, 1440);
298        _vcm->RegisterTransportCallback(_encodeCompleteCallback);
299        _encodeCompleteCallback->SetCodecType(ConvertCodecType(_sendCodec.plName));
300        printf (" \n\n Codec type = %s \n\n",_sendCodec.plName);
301        for (i = 0; i < nBitrates; i++)
302        {
303             _bitRate = static_cast<float>(bitRate[i]);
304            // just testing
305            _vcm->InitializeSender();
306            _sendCodec.startBitrate = (int)_bitRate;
307            _sendCodec.maxBitrate = 8000;
308            _sendCodec.maxFramerate = _frameRate;
309            _vcm->RegisterSendCodec(&_sendCodec, 1, 1440);
310            _vcm->RegisterTransportCallback(_encodeCompleteCallback);
311            // up to here
312            _vcm->SetChannelParameters(static_cast<uint32_t>(1000 * _bitRate),
313                                       0, 20);
314            _frameCnt = 0;
315            totalBytes = 0;
316            _encodeCompleteCallback->Initialize();
317            sendStats.set_framerate(static_cast<uint32_t>(_frameRate));
318            sendStats.set_bitrate(1000 * _bitRate);
319            _vcm->RegisterSendStatisticsCallback(&sendStats);
320            while (fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) ==
321                _lengthSourceFrame)
322            {
323                _frameCnt++;
324                sourceFrame.CreateFrame(size_y, tmpBuffer,
325                                        size_uv, tmpBuffer + size_y,
326                                        size_uv, tmpBuffer + size_y + size_uv,
327                                        _width, _height,
328                                        _width, (_width + 1) / 2,
329                                        (_width + 1) / 2);
330                _timeStamp += (uint32_t)(9e4 / static_cast<float>(_frameRate));
331                sourceFrame.set_timestamp(_timeStamp);
332
333                ret = _vcm->AddVideoFrame(sourceFrame);
334                IncrementDebugClock(_frameRate);
335                // The following should be uncommneted for timing tests. Release tests only include
336                // compliance with full sequence bit rate.
337                if (_frameCnt == _frameRate)// @ 1sec
338                {
339                    totalBytesOneSec =  _encodeCompleteCallback->EncodedBytes();//totalBytes;
340                }
341                TEST(_vcm->TimeUntilNextProcess() >= 0);
342            }  // video seq. encode done
343            TEST(_vcm->TimeUntilNextProcess() == 0);
344            _vcm->Process(); // Let the module calculate its send bit rate estimate
345            // estimating rates
346            // complete sequence
347            // bit rate assumes input frame rate is as specified
348            totalBytes = _encodeCompleteCallback->EncodedBytes();
349            actualBitrate = (float)(8.0/1000)*(totalBytes / (_frameCnt / _frameRate));
350
351            printf("Complete Seq.: target bitrate: %.0f kbps, actual bitrate: %.1f kbps\n", _bitRate, actualBitrate);
352            TEST((fabs(actualBitrate - _bitRate) < FullReq * _bitRate) ||
353                 (strncmp(_sendCodec.plName, "I420", 4) == 0));
354
355            // 1 Sec.
356            actualBitrate = (float)(8.0/1000)*(totalBytesOneSec);
357            //actualBitrate = (float)(8.0*totalBytesOneSec)/(oneSecTime - startTime);
358            //printf("First 1Sec: target bitrate: %.0f kbps, actual bitrate: %.1f kbps\n", _bitRate, actualBitrate);
359            //TEST(fabs(actualBitrate - _bitRate) < OneSecReq * _bitRate);
360            rewind(_sourceFile);
361
362            //checking key/delta frame count
363            _vcm->SentFrameCount(frameCount);
364            printf("frame count: %d delta, %d key\n", frameCount.numDeltaFrames, frameCount.numKeyFrames);
365        }// end per codec
366
367    }  // end rate control test
368    /********************************/
369    /* Encoder Pipeline Delay Test */
370    /******************************/
371    _vcm->InitializeSender();
372    NumberOfCodecs = _vcm->NumberOfCodecs();
373    bool encodeComplete = false;
374    // going over all available codecs
375    for (int k = 0; k < NumberOfCodecs; k++)
376    {
377        _vcm->Codec(k, &_sendCodec);
378        _vcm->InitializeSender();
379        _sendCodec.maxBitrate = 8000;
380        _vcm->RegisterSendCodec(&_sendCodec, 4, 1440);
381        _vcm->RegisterTransportCallback(_encodeCompleteCallback);
382
383        _frameCnt = 0;
384        encodeComplete = false;
385        while (encodeComplete == false)
386        {
387            TEST(fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0);
388            _frameCnt++;
389            sourceFrame.CreateFrame(size_y, tmpBuffer,
390                                    size_uv, tmpBuffer + size_y,
391                                    size_uv, tmpBuffer + size_y + size_uv,
392                                    _width, _height,
393                                    _width, half_width, half_width);
394            _timeStamp += (uint32_t)(9e4 / static_cast<float>(_frameRate));
395            sourceFrame.set_timestamp(_timeStamp);
396            _vcm->AddVideoFrame(sourceFrame);
397            encodeComplete = _encodeCompleteCallback->EncodeComplete();
398        }  // first frame encoded
399        printf ("\n Codec type = %s \n", _sendCodec.plName);
400        printf(" Encoder pipeline delay = %d frames\n", _frameCnt - 1);
401    }  // end for all codecs
402
403    /********************************/
404    /* Encoder Packet Size Test     */
405    /********************************/
406    RTPSendCallback_SizeTest sendCallback;
407
408    RtpRtcp::Configuration configuration;
409    configuration.id = 1;
410    configuration.audio = false;
411    configuration.outgoing_transport = &sendCallback;
412
413    RtpRtcp& rtpModule = *RtpRtcp::CreateRtpRtcp(configuration);
414
415    VCMRTPEncodeCompleteCallback encCompleteCallback(&rtpModule);
416    _vcm->InitializeSender();
417
418    // Test temporal decimation settings
419    for (int k = 0; k < NumberOfCodecs; k++)
420    {
421        _vcm->Codec(k, &_sendCodec);
422        if (strncmp(_sendCodec.plName, "I420", 4) == 0)
423        {
424            // Only test with I420
425            break;
426        }
427    }
428    TEST(strncmp(_sendCodec.plName, "I420", 4) == 0);
429    _vcm->InitializeSender();
430    _sendCodec.maxFramerate = static_cast<uint8_t>(_frameRate / 2.0 + 0.5f);
431    _vcm->RegisterSendCodec(&_sendCodec, 4, 1440);
432    _vcm->SetChannelParameters(2000000, 0, 0);
433    _vcm->RegisterTransportCallback(_encodeCompleteCallback);
434    // up to here
435    _vcm->SetChannelParameters(static_cast<uint32_t>(1000 * _bitRate), 0, 20);
436    _encodeCompleteCallback->Initialize();
437    sendStats.set_framerate(static_cast<uint32_t>(_frameRate));
438    sendStats.set_bitrate(1000 * _bitRate);
439    _vcm->RegisterSendStatisticsCallback(&sendStats);
440    rewind(_sourceFile);
441    while (fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) ==
442        _lengthSourceFrame) {
443        sourceFrame.CreateFrame(size_y, tmpBuffer,
444                                size_uv, tmpBuffer + size_y,
445                                size_uv, tmpBuffer + size_y + size_uv,
446                                _width, _height,
447                                _width, half_width, half_width);
448        _timeStamp += (uint32_t)(9e4 / static_cast<float>(_frameRate));
449        sourceFrame.set_timestamp(_timeStamp);
450        ret = _vcm->AddVideoFrame(sourceFrame);
451        if (_vcm->TimeUntilNextProcess() <= 0)
452        {
453            _vcm->Process();
454        }
455        IncrementDebugClock(_frameRate);
456    }  // first frame encoded
457
458    delete &rtpModule;
459    Print();
460    delete tmpBuffer;
461    delete _decodeCallback;
462    delete _encodeCompleteCallback;
463    return 0;
464}
465
466
467void
468GenericCodecTest::Print()
469{
470    printf(" \n\n VCM Generic Encoder Test: \n\n%i tests completed\n", vcmMacrosTests);
471    if (vcmMacrosErrors > 0)
472    {
473        printf("%i FAILED\n\n", vcmMacrosErrors);
474    }
475    else
476    {
477        printf("ALL PASSED\n\n");
478    }
479}
480
481float
482GenericCodecTest::WaitForEncodedFrame() const
483{
484    int64_t startTime = _clock->TimeInMilliseconds();
485    while (_clock->TimeInMilliseconds() - startTime < kMaxWaitEncTimeMs*10)
486    {
487        if (_encodeCompleteCallback->EncodeComplete())
488        {
489            return _encodeCompleteCallback->EncodedBytes();
490        }
491    }
492    return 0;
493}
494
495void
496GenericCodecTest::IncrementDebugClock(float frameRate)
497{
498    _clock->AdvanceTimeMilliseconds(1000/frameRate);
499}
500
501int
502RTPSendCallback_SizeTest::SendPacket(int channel, const void *data, int len)
503{
504    _nPackets++;
505    _payloadSizeSum += len;
506    // Make sure no payloads (len - header size) are larger than maxPayloadSize
507    TEST(len > 0 && static_cast<uint32_t>(len - 12) <= _maxPayloadSize);
508    return 0;
509}
510
511void
512RTPSendCallback_SizeTest::SetMaxPayloadSize(uint32_t maxPayloadSize)
513{
514    _maxPayloadSize = maxPayloadSize;
515}
516
517void
518RTPSendCallback_SizeTest::Reset()
519{
520    _nPackets = 0;
521    _payloadSizeSum = 0;
522}
523
524float
525RTPSendCallback_SizeTest::AveragePayloadSize() const
526{
527    if (_nPackets > 0)
528    {
529        return _payloadSizeSum / static_cast<float>(_nPackets);
530    }
531    return 0;
532}
533
534int32_t
535VCMEncComplete_KeyReqTest::SendData(
536        const FrameType frameType,
537        const uint8_t payloadType,
538        const uint32_t timeStamp,
539        int64_t capture_time_ms,
540        const uint8_t* payloadData,
541        const uint32_t payloadSize,
542        const RTPFragmentationHeader& /*fragmentationHeader*/,
543        const webrtc::RTPVideoHeader* /*videoHdr*/)
544{
545    WebRtcRTPHeader rtpInfo;
546    rtpInfo.header.markerBit = true; // end of frame
547    rtpInfo.type.Video.codecHeader.VP8.InitRTPVideoHeaderVP8();
548    rtpInfo.type.Video.codec = kRtpVideoVp8;
549    rtpInfo.header.payloadType = payloadType;
550    rtpInfo.header.sequenceNumber = _seqNo;
551    _seqNo += 2;
552    rtpInfo.header.ssrc = 0;
553    rtpInfo.header.timestamp = _timeStamp;
554    _timeStamp += 3000;
555    rtpInfo.type.Video.isFirstPacket = false;
556    rtpInfo.frameType = kVideoFrameKey;
557    return _vcm.IncomingPacket(payloadData, payloadSize, rtpInfo);
558}
559