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/codecs/test_framework/video_source.h"
12
13#include <stdio.h>
14
15#include "testing/gtest/include/gtest/gtest.h"
16#include "webrtc/test/testsupport/fileutils.h"
17
18VideoSource::VideoSource()
19:
20_fileName(webrtc::test::ProjectRootPath() + "resources/foreman_cif.yuv"),
21_width(352),
22_height(288),
23_type(webrtc::kI420),
24_frameRate(30)
25{
26}
27
28VideoSource::VideoSource(std::string fileName, VideoSize size,
29    int frameRate /*= 30*/, webrtc::VideoType type /*=  webrtc::kI420*/)
30:
31_fileName(fileName),
32_type(type),
33_frameRate(frameRate)
34{
35    assert(size != kUndefined && size != kNumberOfVideoSizes);
36    assert(type != webrtc::kUnknown);
37    assert(frameRate > 0);
38    if (GetWidthHeight(size, _width, _height) != 0) {
39        assert(false);
40    }
41}
42
43VideoSource::VideoSource(std::string fileName, int width, int height,
44    int frameRate /*= 30*/,  webrtc::VideoType type /*=  webrtc::kI420*/)
45:
46_fileName(fileName),
47_width(width),
48_height(height),
49_type(type),
50_frameRate(frameRate)
51{
52    assert(width > 0);
53    assert(height > 0);
54    assert(type != webrtc::kUnknown);
55    assert(frameRate > 0);
56}
57
58VideoSize
59VideoSource::GetSize() const
60{
61    return GetSize(_width, _height);
62}
63
64VideoSize
65VideoSource::GetSize(uint16_t width, uint16_t height)
66{
67    if(width == 128 && height == 96)
68    {
69        return kSQCIF;
70    }else if(width == 160 && height == 120)
71    {
72        return kQQVGA;
73    }else if(width == 176 && height == 144)
74    {
75        return kQCIF;
76    }else if(width == 320 && height == 240)
77    {
78        return kQVGA;
79    }else if(width == 352 && height == 288)
80    {
81        return kCIF;
82    }else if(width == 640 && height == 480)
83    {
84        return kVGA;
85    }else if(width == 720 && height == 480)
86    {
87        return kNTSC;
88    }else if(width == 704 && height == 576)
89    {
90        return k4CIF;
91    }else if(width == 800 && height == 600)
92    {
93        return kSVGA;
94    }else if(width == 960 && height == 720)
95    {
96        return kHD;
97    }else if(width == 1024 && height == 768)
98    {
99        return kXGA;
100    }else if(width == 1440 && height == 1080)
101    {
102        return kFullHD;
103    }else if(width == 400 && height == 240)
104    {
105        return kWQVGA;
106    }else if(width == 800 && height == 480)
107    {
108        return kWVGA;
109    }else if(width == 1280 && height == 720)
110    {
111        return kWHD;
112    }else if(width == 1920 && height == 1080)
113    {
114        return kWFullHD;
115    }
116    return kUndefined;
117}
118
119unsigned int
120VideoSource::GetFrameLength() const
121{
122    return webrtc::CalcBufferSize(_type, _width, _height);
123}
124
125const char*
126VideoSource::GetMySizeString() const
127{
128    return VideoSource::GetSizeString(GetSize());
129}
130
131const char*
132VideoSource::GetSizeString(VideoSize size)
133{
134    switch (size)
135    {
136        case kSQCIF:
137            return "SQCIF";
138        case kQQVGA:
139            return "QQVGA";
140        case kQCIF:
141            return "QCIF";
142        case kQVGA:
143            return "QVGA";
144        case kCIF:
145            return "CIF";
146        case kVGA:
147            return "VGA";
148        case kNTSC:
149            return "NTSC";
150        case k4CIF:
151            return "4CIF";
152        case kSVGA:
153            return "SVGA";
154        case kHD:
155            return "HD";
156        case kXGA:
157            return "XGA";
158        case kFullHD:
159            return "Full_HD";
160        case kWQVGA:
161            return "WQVGA";
162        case kWHD:
163            return "WHD";
164        case kWFullHD:
165            return "WFull_HD";
166        default:
167            return "Undefined";
168    }
169}
170
171std::string
172VideoSource::GetFilePath() const
173{
174    size_t slashPos = _fileName.find_last_of("/\\");
175    if (slashPos == std::string::npos)
176    {
177        return ".";
178    }
179
180    return _fileName.substr(0, slashPos);
181}
182
183std::string
184VideoSource::GetName() const
185{
186    // Remove path.
187    size_t slashPos = _fileName.find_last_of("/\\");
188    if (slashPos == std::string::npos)
189    {
190        slashPos = 0;
191    }
192    else
193    {
194        slashPos++;
195    }
196
197    // Remove extension and underscored suffix if it exists.
198    return _fileName.substr(slashPos, std::min(_fileName.find_last_of("_"),
199        _fileName.find_last_of(".")) - slashPos);
200}
201
202void
203VideoSource::Convert(const VideoSource &target, bool force /* = false */) const
204{
205    // Ensure target rate is less than or equal to source
206    // (i.e. we are only temporally downsampling).
207    ASSERT_TRUE(target.GetFrameRate() <= _frameRate);
208    // Only supports YUV420 currently.
209    ASSERT_TRUE(_type == webrtc::kI420 && target.GetType() == webrtc::kI420);
210    if (!force && (FileExists(target.GetFileName().c_str()) ||
211        (target.GetWidth() == _width && target.GetHeight() == _height && target.GetFrameRate() == _frameRate)))
212    {
213        // Assume that the filename uniquely defines the content.
214        // If the file already exists, it is the correct file.
215        return;
216    }
217    FILE *inFile = NULL;
218    FILE *outFile = NULL;
219
220    inFile = fopen(_fileName.c_str(), "rb");
221    ASSERT_TRUE(inFile != NULL);
222
223    outFile = fopen(target.GetFileName().c_str(), "wb");
224    ASSERT_TRUE(outFile != NULL);
225
226    FrameDropper fd;
227    fd.SetFrameRate(target.GetFrameRate(), _frameRate);
228
229    const size_t lengthOutFrame = webrtc::CalcBufferSize(target.GetType(),
230        target.GetWidth(), target.GetHeight());
231    ASSERT_TRUE(lengthOutFrame > 0);
232    unsigned char *outFrame = new unsigned char[lengthOutFrame];
233
234    const size_t lengthInFrame = webrtc::CalcBufferSize(_type, _width, _height);
235    ASSERT_TRUE(lengthInFrame > 0);
236    unsigned char *inFrame = new unsigned char[lengthInFrame];
237
238    while (fread(inFrame, 1, lengthInFrame, inFile) == lengthInFrame)
239    {
240        if (!fd.DropFrame())
241        {
242            ASSERT_TRUE(target.GetWidth() == _width &&
243                   target.GetHeight() == _height);
244            // Add video interpolator here!
245            if (fwrite(outFrame, 1, lengthOutFrame,
246                       outFile) !=  lengthOutFrame) {
247              return;
248            }
249        }
250    }
251
252    delete inFrame;
253    delete outFrame;
254    fclose(inFile);
255    fclose(outFile);
256}
257
258bool VideoSource::FileExists(const char* fileName)
259{
260    FILE* fp = NULL;
261    fp = fopen(fileName, "rb");
262    if(fp != NULL)
263    {
264        fclose(fp);
265        return true;
266    }
267    return false;
268}
269
270
271int
272VideoSource::GetWidthHeight( VideoSize size, int & width, int& height)
273{
274    switch(size)
275    {
276    case kSQCIF:
277        width = 128;
278        height = 96;
279        return 0;
280    case kQQVGA:
281        width = 160;
282        height = 120;
283        return 0;
284    case kQCIF:
285        width = 176;
286        height = 144;
287        return 0;
288    case kCGA:
289        width = 320;
290        height = 200;
291        return 0;
292    case kQVGA:
293        width = 320;
294        height = 240;
295        return 0;
296    case kSIF:
297        width = 352;
298        height = 240;
299        return 0;
300    case kWQVGA:
301        width = 400;
302        height = 240;
303        return 0;
304    case kCIF:
305        width = 352;
306        height = 288;
307        return 0;
308    case kW288p:
309        width = 512;
310        height = 288;
311        return 0;
312    case k448p:
313        width = 576;
314        height = 448;
315        return 0;
316    case kVGA:
317        width = 640;
318        height = 480;
319        return 0;
320    case k432p:
321        width = 720;
322        height = 432;
323        return 0;
324    case kW432p:
325        width = 768;
326        height = 432;
327        return 0;
328    case k4SIF:
329        width = 704;
330        height = 480;
331        return 0;
332    case kW448p:
333        width = 768;
334        height = 448;
335        return 0;
336    case kNTSC:
337        width = 720;
338        height = 480;
339        return 0;
340    case kFW448p:
341        width = 800;
342        height = 448;
343        return 0;
344    case kWVGA:
345        width = 800;
346        height = 480;
347        return 0;
348    case k4CIF:
349        width = 704;
350        height = 576;
351        return 0;
352    case kSVGA:
353        width = 800;
354        height = 600;
355        return 0;
356    case kW544p:
357        width = 960;
358        height = 544;
359        return 0;
360    case kW576p:
361        width = 1024;
362        height = 576;
363        return 0;
364    case kHD:
365        width = 960;
366        height = 720;
367        return 0;
368    case kXGA:
369        width = 1024;
370        height = 768;
371        return 0;
372    case kFullHD:
373        width = 1440;
374        height = 1080;
375        return 0;
376    case kWHD:
377        width = 1280;
378        height = 720;
379        return 0;
380    case kWFullHD:
381        width = 1920;
382        height = 1080;
383        return 0;
384    default:
385        return -1;
386    }
387}
388
389FrameDropper::FrameDropper()
390:
391_dropsBetweenRenders(0),
392_frameCounter(0)
393{
394}
395
396bool
397FrameDropper::DropFrame()
398{
399    _frameCounter++;
400    if (_frameCounter > _dropsBetweenRenders)
401    {
402        _frameCounter = 0;
403        return false;
404    }
405    return true;
406}
407
408unsigned int
409FrameDropper::DropsBetweenRenders()
410{
411    return _dropsBetweenRenders;
412}
413
414void
415FrameDropper::SetFrameRate(double frameRate, double maxFrameRate)
416{
417    if (frameRate >= 1.0)
418    {
419        _dropsBetweenRenders = static_cast<unsigned int>(maxFrameRate / frameRate + 0.5) - 1;
420    }
421    else
422    {
423        _dropsBetweenRenders = 0;
424    }
425}
426