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