1 /*
2  *  Copyright (c) 2012 The WebM 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 #ifndef TEST_VIDEO_SOURCE_H_
11 #define TEST_VIDEO_SOURCE_H_
12 
13 #if defined(_WIN32)
14 #undef NOMINMAX
15 #define NOMINMAX
16 #ifndef WIN32_LEAN_AND_MEAN
17 #define WIN32_LEAN_AND_MEAN
18 #endif
19 #include <windows.h>
20 #endif
21 #include <cstdio>
22 #include <cstdlib>
23 #include <string>
24 #include "test/acm_random.h"
25 #include "vpx/vpx_encoder.h"
26 
27 namespace libvpx_test {
28 
29 // Helper macros to ensure LIBVPX_TEST_DATA_PATH is a quoted string.
30 // These are undefined right below GetDataPath
31 // NOTE: LIBVPX_TEST_DATA_PATH MUST NOT be a quoted string before
32 // Stringification or the GetDataPath will fail at runtime
33 #define TO_STRING(S) #S
34 #define STRINGIFY(S) TO_STRING(S)
35 
36 // A simple function to encapsulate cross platform retrieval of test data path
GetDataPath()37 static std::string GetDataPath() {
38   const char *const data_path = getenv("LIBVPX_TEST_DATA_PATH");
39   if (data_path == NULL) {
40 #ifdef LIBVPX_TEST_DATA_PATH
41     // In some environments, we cannot set environment variables
42     // Instead, we set the data path by using a preprocessor symbol
43     // which can be set from make files
44     return STRINGIFY(LIBVPX_TEST_DATA_PATH);
45 #else
46     return ".";
47 #endif
48   }
49   return data_path;
50 }
51 
52 // Undefining stringification macros because they are not used elsewhere
53 #undef TO_STRING
54 #undef STRINGIFY
55 
OpenTestDataFile(const std::string & file_name)56 inline FILE *OpenTestDataFile(const std::string &file_name) {
57   const std::string path_to_source = GetDataPath() + "/" + file_name;
58   return fopen(path_to_source.c_str(), "rb");
59 }
60 
GetTempOutFile(std::string * file_name)61 static FILE *GetTempOutFile(std::string *file_name) {
62   file_name->clear();
63 #if defined(_WIN32)
64   char fname[MAX_PATH];
65   char tmppath[MAX_PATH];
66   if (GetTempPathA(MAX_PATH, tmppath)) {
67     // Assume for now that the filename generated is unique per process
68     if (GetTempFileNameA(tmppath, "lvx", 0, fname)) {
69       file_name->assign(fname);
70       return fopen(fname, "wb+");
71     }
72   }
73   return NULL;
74 #else
75   return tmpfile();
76 #endif
77 }
78 
79 class TempOutFile {
80  public:
TempOutFile()81   TempOutFile() { file_ = GetTempOutFile(&file_name_); }
~TempOutFile()82   ~TempOutFile() {
83     CloseFile();
84     if (!file_name_.empty()) {
85       EXPECT_EQ(0, remove(file_name_.c_str()));
86     }
87   }
file()88   FILE *file() { return file_; }
file_name()89   const std::string &file_name() { return file_name_; }
90 
91  protected:
CloseFile()92   void CloseFile() {
93     if (file_) {
94       fclose(file_);
95       file_ = NULL;
96     }
97   }
98   FILE *file_;
99   std::string file_name_;
100 };
101 
102 // Abstract base class for test video sources, which provide a stream of
103 // vpx_image_t images with associated timestamps and duration.
104 class VideoSource {
105  public:
~VideoSource()106   virtual ~VideoSource() {}
107 
108   // Prepare the stream for reading, rewind/open as necessary.
109   virtual void Begin() = 0;
110 
111   // Advance the cursor to the next frame
112   virtual void Next() = 0;
113 
114   // Get the current video frame, or NULL on End-Of-Stream.
115   virtual vpx_image_t *img() const = 0;
116 
117   // Get the presentation timestamp of the current frame.
118   virtual vpx_codec_pts_t pts() const = 0;
119 
120   // Get the current frame's duration
121   virtual unsigned long duration() const = 0;
122 
123   // Get the timebase for the stream
124   virtual vpx_rational_t timebase() const = 0;
125 
126   // Get the current frame counter, starting at 0.
127   virtual unsigned int frame() const = 0;
128 
129   // Get the current file limit.
130   virtual unsigned int limit() const = 0;
131 };
132 
133 class DummyVideoSource : public VideoSource {
134  public:
DummyVideoSource()135   DummyVideoSource()
136       : img_(NULL), limit_(100), width_(80), height_(64),
137         format_(VPX_IMG_FMT_I420) {
138     ReallocImage();
139   }
140 
~DummyVideoSource()141   virtual ~DummyVideoSource() { vpx_img_free(img_); }
142 
Begin()143   virtual void Begin() {
144     frame_ = 0;
145     FillFrame();
146   }
147 
Next()148   virtual void Next() {
149     ++frame_;
150     FillFrame();
151   }
152 
img()153   virtual vpx_image_t *img() const { return (frame_ < limit_) ? img_ : NULL; }
154 
155   // Models a stream where Timebase = 1/FPS, so pts == frame.
pts()156   virtual vpx_codec_pts_t pts() const { return frame_; }
157 
duration()158   virtual unsigned long duration() const { return 1; }
159 
timebase()160   virtual vpx_rational_t timebase() const {
161     const vpx_rational_t t = { 1, 30 };
162     return t;
163   }
164 
frame()165   virtual unsigned int frame() const { return frame_; }
166 
limit()167   virtual unsigned int limit() const { return limit_; }
168 
set_limit(unsigned int limit)169   void set_limit(unsigned int limit) { limit_ = limit; }
170 
SetSize(unsigned int width,unsigned int height)171   void SetSize(unsigned int width, unsigned int height) {
172     if (width != width_ || height != height_) {
173       width_ = width;
174       height_ = height;
175       ReallocImage();
176     }
177   }
178 
SetImageFormat(vpx_img_fmt_t format)179   void SetImageFormat(vpx_img_fmt_t format) {
180     if (format_ != format) {
181       format_ = format;
182       ReallocImage();
183     }
184   }
185 
186  protected:
FillFrame()187   virtual void FillFrame() {
188     if (img_) memset(img_->img_data, 0, raw_sz_);
189   }
190 
ReallocImage()191   void ReallocImage() {
192     vpx_img_free(img_);
193     img_ = vpx_img_alloc(NULL, format_, width_, height_, 32);
194     raw_sz_ = ((img_->w + 31) & ~31) * img_->h * img_->bps / 8;
195   }
196 
197   vpx_image_t *img_;
198   size_t raw_sz_;
199   unsigned int limit_;
200   unsigned int frame_;
201   unsigned int width_;
202   unsigned int height_;
203   vpx_img_fmt_t format_;
204 };
205 
206 class RandomVideoSource : public DummyVideoSource {
207  public:
208   RandomVideoSource(int seed = ACMRandom::DeterministicSeed())
rnd_(seed)209       : rnd_(seed), seed_(seed) {}
210 
211  protected:
212   // Reset the RNG to get a matching stream for the second pass
Begin()213   virtual void Begin() {
214     frame_ = 0;
215     rnd_.Reset(seed_);
216     FillFrame();
217   }
218 
219   // 15 frames of noise, followed by 15 static frames. Reset to 0 rather
220   // than holding previous frames to encourage keyframes to be thrown.
FillFrame()221   virtual void FillFrame() {
222     if (img_) {
223       if (frame_ % 30 < 15) {
224         for (size_t i = 0; i < raw_sz_; ++i) img_->img_data[i] = rnd_.Rand8();
225       } else {
226         memset(img_->img_data, 0, raw_sz_);
227       }
228     }
229   }
230 
231   ACMRandom rnd_;
232   int seed_;
233 };
234 
235 // Abstract base class for test video sources, which provide a stream of
236 // decompressed images to the decoder.
237 class CompressedVideoSource {
238  public:
~CompressedVideoSource()239   virtual ~CompressedVideoSource() {}
240 
241   virtual void Init() = 0;
242 
243   // Prepare the stream for reading, rewind/open as necessary.
244   virtual void Begin() = 0;
245 
246   // Advance the cursor to the next frame
247   virtual void Next() = 0;
248 
249   virtual const uint8_t *cxdata() const = 0;
250 
251   virtual size_t frame_size() const = 0;
252 
253   virtual unsigned int frame_number() const = 0;
254 };
255 
256 }  // namespace libvpx_test
257 
258 #endif  // TEST_VIDEO_SOURCE_H_
259