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_ENCODE_TEST_DRIVER_H_
11#define TEST_ENCODE_TEST_DRIVER_H_
12
13#include <string>
14#include <vector>
15
16#include "third_party/googletest/src/include/gtest/gtest.h"
17
18#include "./vpx_config.h"
19#if CONFIG_VP8_ENCODER || CONFIG_VP9_ENCODER || CONFIG_VP10_ENCODER
20#include "vpx/vp8cx.h"
21#endif
22#include "vpx/vpx_encoder.h"
23
24namespace libvpx_test {
25
26class CodecFactory;
27class VideoSource;
28
29enum TestMode {
30  kRealTime,
31  kOnePassGood,
32  kOnePassBest,
33  kTwoPassGood,
34  kTwoPassBest
35};
36#define ALL_TEST_MODES ::testing::Values(::libvpx_test::kRealTime, \
37                                         ::libvpx_test::kOnePassGood, \
38                                         ::libvpx_test::kOnePassBest, \
39                                         ::libvpx_test::kTwoPassGood, \
40                                         ::libvpx_test::kTwoPassBest)
41
42#define ONE_PASS_TEST_MODES ::testing::Values(::libvpx_test::kRealTime, \
43                                              ::libvpx_test::kOnePassGood, \
44                                              ::libvpx_test::kOnePassBest)
45
46#define TWO_PASS_TEST_MODES ::testing::Values(::libvpx_test::kTwoPassGood, \
47                                              ::libvpx_test::kTwoPassBest)
48
49
50// Provides an object to handle the libvpx get_cx_data() iteration pattern
51class CxDataIterator {
52 public:
53  explicit CxDataIterator(vpx_codec_ctx_t *encoder)
54      : encoder_(encoder), iter_(NULL) {}
55
56  const vpx_codec_cx_pkt_t *Next() {
57    return vpx_codec_get_cx_data(encoder_, &iter_);
58  }
59
60 private:
61  vpx_codec_ctx_t  *encoder_;
62  vpx_codec_iter_t  iter_;
63};
64
65// Implements an in-memory store for libvpx twopass statistics
66class TwopassStatsStore {
67 public:
68  void Append(const vpx_codec_cx_pkt_t &pkt) {
69    buffer_.append(reinterpret_cast<char *>(pkt.data.twopass_stats.buf),
70                   pkt.data.twopass_stats.sz);
71  }
72
73  vpx_fixed_buf_t buf() {
74    const vpx_fixed_buf_t buf = { &buffer_[0], buffer_.size() };
75    return buf;
76  }
77
78  void Reset() {
79    buffer_.clear();
80  }
81
82 protected:
83  std::string  buffer_;
84};
85
86
87// Provides a simplified interface to manage one video encoding pass, given
88// a configuration and video source.
89//
90// TODO(jkoleszar): The exact services it provides and the appropriate
91// level of abstraction will be fleshed out as more tests are written.
92class Encoder {
93 public:
94  Encoder(vpx_codec_enc_cfg_t cfg, unsigned long deadline,
95          const unsigned long init_flags, TwopassStatsStore *stats)
96      : cfg_(cfg), deadline_(deadline), init_flags_(init_flags), stats_(stats) {
97    memset(&encoder_, 0, sizeof(encoder_));
98  }
99
100  virtual ~Encoder() {
101    vpx_codec_destroy(&encoder_);
102  }
103
104  CxDataIterator GetCxData() {
105    return CxDataIterator(&encoder_);
106  }
107
108  void InitEncoder(VideoSource *video);
109
110  const vpx_image_t *GetPreviewFrame() {
111    return vpx_codec_get_preview_frame(&encoder_);
112  }
113  // This is a thin wrapper around vpx_codec_encode(), so refer to
114  // vpx_encoder.h for its semantics.
115  void EncodeFrame(VideoSource *video, const unsigned long frame_flags);
116
117  // Convenience wrapper for EncodeFrame()
118  void EncodeFrame(VideoSource *video) {
119    EncodeFrame(video, 0);
120  }
121
122  void Control(int ctrl_id, int arg) {
123    const vpx_codec_err_t res = vpx_codec_control_(&encoder_, ctrl_id, arg);
124    ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError();
125  }
126
127  void Control(int ctrl_id, int *arg) {
128    const vpx_codec_err_t res = vpx_codec_control_(&encoder_, ctrl_id, arg);
129    ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError();
130  }
131
132  void Control(int ctrl_id, struct vpx_scaling_mode *arg) {
133    const vpx_codec_err_t res = vpx_codec_control_(&encoder_, ctrl_id, arg);
134    ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError();
135  }
136
137  void Control(int ctrl_id, struct vpx_svc_layer_id *arg) {
138    const vpx_codec_err_t res = vpx_codec_control_(&encoder_, ctrl_id, arg);
139    ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError();
140  }
141
142  void Control(int ctrl_id, struct vpx_svc_parameters *arg) {
143    const vpx_codec_err_t res = vpx_codec_control_(&encoder_, ctrl_id, arg);
144    ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError();
145  }
146#if CONFIG_VP8_ENCODER || CONFIG_VP9_ENCODER || CONFIG_VP10_ENCODER
147  void Control(int ctrl_id, vpx_active_map_t *arg) {
148    const vpx_codec_err_t res = vpx_codec_control_(&encoder_, ctrl_id, arg);
149    ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError();
150  }
151#endif
152
153  void Config(const vpx_codec_enc_cfg_t *cfg) {
154    const vpx_codec_err_t res = vpx_codec_enc_config_set(&encoder_, cfg);
155    ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError();
156    cfg_ = *cfg;
157  }
158
159  void set_deadline(unsigned long deadline) {
160    deadline_ = deadline;
161  }
162
163 protected:
164  virtual vpx_codec_iface_t* CodecInterface() const = 0;
165
166  const char *EncoderError() {
167    const char *detail = vpx_codec_error_detail(&encoder_);
168    return detail ? detail : vpx_codec_error(&encoder_);
169  }
170
171  // Encode an image
172  void EncodeFrameInternal(const VideoSource &video,
173                           const unsigned long frame_flags);
174
175  // Flush the encoder on EOS
176  void Flush();
177
178  vpx_codec_ctx_t      encoder_;
179  vpx_codec_enc_cfg_t  cfg_;
180  unsigned long        deadline_;
181  unsigned long        init_flags_;
182  TwopassStatsStore   *stats_;
183};
184
185// Common test functionality for all Encoder tests.
186//
187// This class is a mixin which provides the main loop common to all
188// encoder tests. It provides hooks which can be overridden by subclasses
189// to implement each test's specific behavior, while centralizing the bulk
190// of the boilerplate. Note that it doesn't inherit the gtest testing
191// classes directly, so that tests can be parameterized differently.
192class EncoderTest {
193 protected:
194  explicit EncoderTest(const CodecFactory *codec)
195      : codec_(codec), abort_(false), init_flags_(0), frame_flags_(0),
196        last_pts_(0) {
197    // Default to 1 thread.
198    cfg_.g_threads = 1;
199  }
200
201  virtual ~EncoderTest() {}
202
203  // Initialize the cfg_ member with the default configuration.
204  void InitializeConfig();
205
206  // Map the TestMode enum to the deadline_ and passes_ variables.
207  void SetMode(TestMode mode);
208
209  // Set encoder flag.
210  void set_init_flags(unsigned long flag) {  // NOLINT(runtime/int)
211    init_flags_ = flag;
212  }
213
214  // Main loop
215  virtual void RunLoop(VideoSource *video);
216
217  // Hook to be called at the beginning of a pass.
218  virtual void BeginPassHook(unsigned int /*pass*/) {}
219
220  // Hook to be called at the end of a pass.
221  virtual void EndPassHook() {}
222
223  // Hook to be called before encoding a frame.
224  virtual void PreEncodeFrameHook(VideoSource* /*video*/) {}
225  virtual void PreEncodeFrameHook(VideoSource* /*video*/,
226                                  Encoder* /*encoder*/) {}
227
228  // Hook to be called on every compressed data packet.
229  virtual void FramePktHook(const vpx_codec_cx_pkt_t* /*pkt*/) {}
230
231  // Hook to be called on every PSNR packet.
232  virtual void PSNRPktHook(const vpx_codec_cx_pkt_t* /*pkt*/) {}
233
234  // Hook to determine whether the encode loop should continue.
235  virtual bool Continue() const {
236    return !(::testing::Test::HasFatalFailure() || abort_);
237  }
238
239  const CodecFactory   *codec_;
240  // Hook to determine whether to decode frame after encoding
241  virtual bool DoDecode() const { return 1; }
242
243  // Hook to handle encode/decode mismatch
244  virtual void MismatchHook(const vpx_image_t *img1,
245                            const vpx_image_t *img2);
246
247  // Hook to be called on every decompressed frame.
248  virtual void DecompressedFrameHook(const vpx_image_t& /*img*/,
249                                     vpx_codec_pts_t /*pts*/) {}
250
251  // Hook to be called to handle decode result. Return true to continue.
252  virtual bool HandleDecodeResult(const vpx_codec_err_t res_dec,
253                                  const VideoSource& /*video*/,
254                                  Decoder *decoder) {
255    EXPECT_EQ(VPX_CODEC_OK, res_dec) << decoder->DecodeError();
256    return VPX_CODEC_OK == res_dec;
257  }
258
259  // Hook that can modify the encoder's output data
260  virtual const vpx_codec_cx_pkt_t *MutateEncoderOutputHook(
261      const vpx_codec_cx_pkt_t *pkt) {
262    return pkt;
263  }
264
265  bool                 abort_;
266  vpx_codec_enc_cfg_t  cfg_;
267  vpx_codec_dec_cfg_t  dec_cfg_;
268  unsigned int         passes_;
269  unsigned long        deadline_;
270  TwopassStatsStore    stats_;
271  unsigned long        init_flags_;
272  unsigned long        frame_flags_;
273  vpx_codec_pts_t      last_pts_;
274};
275
276}  // namespace libvpx_test
277
278#endif  // TEST_ENCODE_TEST_DRIVER_H_
279