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