Home | History | Annotate | Download | only in test
      1 /*
      2  * Copyright (c) 2016, Alliance for Open Media. All rights reserved
      3  *
      4  * This source code is subject to the terms of the BSD 2 Clause License and
      5  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
      6  * was not distributed with this source code in the LICENSE file, you can
      7  * obtain it at www.aomedia.org/license/software. If the Alliance for Open
      8  * Media Patent License 1.0 was not distributed with this source code in the
      9  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
     10  */
     11 #ifndef AOM_TEST_ENCODE_TEST_DRIVER_H_
     12 #define AOM_TEST_ENCODE_TEST_DRIVER_H_
     13 
     14 #include <string>
     15 #include <vector>
     16 
     17 #include "third_party/googletest/src/googletest/include/gtest/gtest.h"
     18 
     19 #include "config/aom_config.h"
     20 
     21 #if CONFIG_AV1_ENCODER
     22 #include "aom/aomcx.h"
     23 #endif
     24 #include "aom/aom_encoder.h"
     25 
     26 namespace libaom_test {
     27 
     28 class CodecFactory;
     29 class VideoSource;
     30 
     31 enum TestMode { kRealTime, kOnePassGood, kTwoPassGood };
     32 #define ALL_TEST_MODES                                                     \
     33   ::testing::Values(::libaom_test::kRealTime, ::libaom_test::kOnePassGood, \
     34                     ::libaom_test::kTwoPassGood)
     35 
     36 #define ONE_PASS_TEST_MODES \
     37   ::testing::Values(::libaom_test::kRealTime, ::libaom_test::kOnePassGood)
     38 
     39 #define TWO_PASS_TEST_MODES ::testing::Values(::libaom_test::kTwoPassGood)
     40 
     41 #define NONREALTIME_TEST_MODES \
     42   ::testing::Values(::libaom_test::kOnePassGood, ::libaom_test::kTwoPassGood)
     43 
     44 // Provides an object to handle the libaom get_cx_data() iteration pattern
     45 class CxDataIterator {
     46  public:
     47   explicit CxDataIterator(aom_codec_ctx_t *encoder)
     48       : encoder_(encoder), iter_(NULL) {}
     49 
     50   const aom_codec_cx_pkt_t *Next() {
     51     return aom_codec_get_cx_data(encoder_, &iter_);
     52   }
     53 
     54  private:
     55   aom_codec_ctx_t *encoder_;
     56   aom_codec_iter_t iter_;
     57 };
     58 
     59 // Implements an in-memory store for libaom twopass statistics
     60 class TwopassStatsStore {
     61  public:
     62   void Append(const aom_codec_cx_pkt_t &pkt) {
     63     buffer_.append(reinterpret_cast<char *>(pkt.data.twopass_stats.buf),
     64                    pkt.data.twopass_stats.sz);
     65   }
     66 
     67   aom_fixed_buf_t buf() {
     68     const aom_fixed_buf_t buf = { &buffer_[0], buffer_.size() };
     69     return buf;
     70   }
     71 
     72   void Reset() { buffer_.clear(); }
     73 
     74  protected:
     75   std::string buffer_;
     76 };
     77 
     78 // Provides a simplified interface to manage one video encoding pass, given
     79 // a configuration and video source.
     80 //
     81 // TODO(jkoleszar): The exact services it provides and the appropriate
     82 // level of abstraction will be fleshed out as more tests are written.
     83 class Encoder {
     84  public:
     85   Encoder(aom_codec_enc_cfg_t cfg, const uint32_t init_flags,
     86           TwopassStatsStore *stats)
     87       : cfg_(cfg), init_flags_(init_flags), stats_(stats) {
     88     memset(&encoder_, 0, sizeof(encoder_));
     89   }
     90 
     91   virtual ~Encoder() { aom_codec_destroy(&encoder_); }
     92 
     93   CxDataIterator GetCxData() { return CxDataIterator(&encoder_); }
     94 
     95   void InitEncoder(VideoSource *video);
     96 
     97   const aom_image_t *GetPreviewFrame() {
     98     return aom_codec_get_preview_frame(&encoder_);
     99   }
    100   // This is a thin wrapper around aom_codec_encode(), so refer to
    101   // aom_encoder.h for its semantics.
    102   void EncodeFrame(VideoSource *video, const unsigned long frame_flags);
    103 
    104   // Convenience wrapper for EncodeFrame()
    105   void EncodeFrame(VideoSource *video) { EncodeFrame(video, 0); }
    106 
    107   void Control(int ctrl_id, int arg) {
    108     const aom_codec_err_t res = aom_codec_control_(&encoder_, ctrl_id, arg);
    109     ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError();
    110   }
    111 
    112   void Control(int ctrl_id, int *arg) {
    113     const aom_codec_err_t res = aom_codec_control_(&encoder_, ctrl_id, arg);
    114     ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError();
    115   }
    116 
    117   void Control(int ctrl_id, struct aom_scaling_mode *arg) {
    118     const aom_codec_err_t res = aom_codec_control_(&encoder_, ctrl_id, arg);
    119     ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError();
    120   }
    121 
    122 #if CONFIG_AV1_ENCODER
    123   void Control(int ctrl_id, aom_active_map_t *arg) {
    124     const aom_codec_err_t res = aom_codec_control_(&encoder_, ctrl_id, arg);
    125     ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError();
    126   }
    127 #endif
    128 
    129   void Config(const aom_codec_enc_cfg_t *cfg) {
    130     const aom_codec_err_t res = aom_codec_enc_config_set(&encoder_, cfg);
    131     ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError();
    132     cfg_ = *cfg;
    133   }
    134 
    135  protected:
    136   virtual aom_codec_iface_t *CodecInterface() const = 0;
    137 
    138   const char *EncoderError() {
    139     const char *detail = aom_codec_error_detail(&encoder_);
    140     return detail ? detail : aom_codec_error(&encoder_);
    141   }
    142 
    143   // Encode an image
    144   void EncodeFrameInternal(const VideoSource &video,
    145                            const unsigned long frame_flags);
    146 
    147   // Flush the encoder on EOS
    148   void Flush();
    149 
    150   aom_codec_ctx_t encoder_;
    151   aom_codec_enc_cfg_t cfg_;
    152   unsigned long init_flags_;
    153   TwopassStatsStore *stats_;
    154 };
    155 
    156 // Common test functionality for all Encoder tests.
    157 //
    158 // This class is a mixin which provides the main loop common to all
    159 // encoder tests. It provides hooks which can be overridden by subclasses
    160 // to implement each test's specific behavior, while centralizing the bulk
    161 // of the boilerplate. Note that it doesn't inherit the gtest testing
    162 // classes directly, so that tests can be parameterized differently.
    163 class EncoderTest {
    164  protected:
    165   explicit EncoderTest(const CodecFactory *codec)
    166       : codec_(codec), abort_(false), init_flags_(0), frame_flags_(0),
    167         last_pts_(0), mode_(kRealTime) {
    168     // Default to 1 thread.
    169     cfg_.g_threads = 1;
    170   }
    171 
    172   virtual ~EncoderTest() {}
    173 
    174   // Initialize the cfg_ member with the default configuration.
    175   void InitializeConfig();
    176 
    177   // Map the TestMode enum to the passes_ variables.
    178   void SetMode(TestMode mode);
    179 
    180   // Set encoder flag.
    181   void set_init_flags(unsigned long flag) {  // NOLINT(runtime/int)
    182     init_flags_ = flag;
    183   }
    184 
    185   // Main loop
    186   virtual void RunLoop(VideoSource *video);
    187 
    188   // Hook to be called at the beginning of a pass.
    189   virtual void BeginPassHook(unsigned int /*pass*/) {}
    190 
    191   // Hook to be called at the end of a pass.
    192   virtual void EndPassHook() {}
    193 
    194   // Hook to be called before encoding a frame.
    195   virtual void PreEncodeFrameHook(VideoSource * /*video*/) {}
    196   virtual void PreEncodeFrameHook(VideoSource * /*video*/,
    197                                   Encoder * /*encoder*/) {}
    198 
    199   // Hook to be called on every compressed data packet.
    200   virtual void FramePktHook(const aom_codec_cx_pkt_t * /*pkt*/) {}
    201 
    202   // Hook to be called on every PSNR packet.
    203   virtual void PSNRPktHook(const aom_codec_cx_pkt_t * /*pkt*/) {}
    204 
    205   // Hook to determine whether the encode loop should continue.
    206   virtual bool Continue() const {
    207     return !(::testing::Test::HasFatalFailure() || abort_);
    208   }
    209 
    210   // Hook to determine whether to decode frame after encoding
    211   virtual bool DoDecode() const { return true; }
    212 
    213   // Hook to determine whether to decode invisible frames after encoding
    214   virtual bool DoDecodeInvisible() const { return true; }
    215 
    216   // Hook to handle encode/decode mismatch
    217   virtual void MismatchHook(const aom_image_t *img1, const aom_image_t *img2);
    218 
    219   // Hook to be called on every decompressed frame.
    220   virtual void DecompressedFrameHook(const aom_image_t & /*img*/,
    221                                      aom_codec_pts_t /*pts*/) {}
    222 
    223   // Hook to be called to handle decode result. Return true to continue.
    224   virtual bool HandleDecodeResult(const aom_codec_err_t res_dec,
    225                                   Decoder *decoder) {
    226     EXPECT_EQ(AOM_CODEC_OK, res_dec) << decoder->DecodeError();
    227     return AOM_CODEC_OK == res_dec;
    228   }
    229 
    230   // Hook that can modify the encoder's output data
    231   virtual const aom_codec_cx_pkt_t *MutateEncoderOutputHook(
    232       const aom_codec_cx_pkt_t *pkt) {
    233     return pkt;
    234   }
    235 
    236   const CodecFactory *codec_;
    237   bool abort_;
    238   aom_codec_enc_cfg_t cfg_;
    239   unsigned int passes_;
    240   TwopassStatsStore stats_;
    241   unsigned long init_flags_;
    242   unsigned long frame_flags_;
    243   aom_codec_pts_t last_pts_;
    244   TestMode mode_;
    245 };
    246 
    247 }  // namespace libaom_test
    248 
    249 #endif  // AOM_TEST_ENCODE_TEST_DRIVER_H_
    250