encode_test_driver.h (9814B)
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 "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 { 32 kRealTime, 33 kOnePassGood, 34 kTwoPassGood, 35 kAllIntra, 36 kLowComplexityDecode 37 }; 38 #define ALL_TEST_MODES \ 39 ::testing::Values(::libaom_test::kRealTime, ::libaom_test::kOnePassGood, \ 40 ::libaom_test::kTwoPassGood) 41 42 #define ONE_PASS_TEST_MODES \ 43 ::testing::Values(::libaom_test::kRealTime, ::libaom_test::kOnePassGood) 44 45 #define TWO_PASS_TEST_MODES ::testing::Values(::libaom_test::kTwoPassGood) 46 47 #define NONREALTIME_TEST_MODES \ 48 ::testing::Values(::libaom_test::kOnePassGood, ::libaom_test::kTwoPassGood) 49 50 // Provides an object to handle the libaom get_cx_data() iteration pattern 51 class CxDataIterator { 52 public: 53 explicit CxDataIterator(aom_codec_ctx_t *encoder) 54 : encoder_(encoder), iter_(nullptr) {} 55 56 const aom_codec_cx_pkt_t *Next() { 57 return aom_codec_get_cx_data(encoder_, &iter_); 58 } 59 60 private: 61 aom_codec_ctx_t *encoder_; 62 aom_codec_iter_t iter_; 63 }; 64 65 // Implements an in-memory store for libaom twopass statistics 66 class TwopassStatsStore { 67 public: 68 void Append(const aom_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 aom_fixed_buf_t buf() { 74 const aom_fixed_buf_t buf = { &buffer_[0], buffer_.size() }; 75 return buf; 76 } 77 78 void Reset() { buffer_.clear(); } 79 80 protected: 81 std::string buffer_; 82 }; 83 84 // Provides a simplified interface to manage one video encoding pass, given 85 // a configuration and video source. 86 // 87 // TODO(jkoleszar): The exact services it provides and the appropriate 88 // level of abstraction will be fleshed out as more tests are written. 89 class Encoder { 90 public: 91 Encoder(aom_codec_enc_cfg_t cfg, const aom_codec_flags_t init_flags, 92 TwopassStatsStore *stats) 93 : cfg_(cfg), init_flags_(init_flags), stats_(stats) { 94 memset(&encoder_, 0, sizeof(encoder_)); 95 } 96 97 virtual ~Encoder() { aom_codec_destroy(&encoder_); } 98 99 CxDataIterator GetCxData() { return CxDataIterator(&encoder_); } 100 101 void InitEncoder(VideoSource *video); 102 103 const aom_image_t *GetPreviewFrame() { 104 return aom_codec_get_preview_frame(&encoder_); 105 } 106 // This is a thin wrapper around aom_codec_encode(), so refer to 107 // aom_encoder.h for its semantics. 108 void EncodeFrame(VideoSource *video, aom_enc_frame_flags_t frame_flags); 109 110 // Convenience wrapper for EncodeFrame() 111 void EncodeFrame(VideoSource *video) { EncodeFrame(video, 0); } 112 113 void Control(int ctrl_id, int arg) { 114 const aom_codec_err_t res = aom_codec_control(&encoder_, ctrl_id, arg); 115 ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); 116 } 117 118 void Control(int ctrl_id, int *arg) { 119 const aom_codec_err_t res = aom_codec_control(&encoder_, ctrl_id, arg); 120 ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); 121 } 122 123 void Control(int ctrl_id, struct aom_scaling_mode *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 128 void Control(int ctrl_id, struct aom_svc_layer_id *arg) { 129 const aom_codec_err_t res = aom_codec_control(&encoder_, ctrl_id, arg); 130 ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); 131 } 132 133 void Control(int ctrl_id, struct aom_svc_ref_frame_config *arg) { 134 const aom_codec_err_t res = aom_codec_control(&encoder_, ctrl_id, arg); 135 ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); 136 } 137 138 void Control(int ctrl_id, struct aom_svc_ref_frame_comp_pred *arg) { 139 const aom_codec_err_t res = aom_codec_control(&encoder_, ctrl_id, arg); 140 ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); 141 } 142 143 void Control(int ctrl_id, struct aom_svc_params *arg) { 144 const aom_codec_err_t res = aom_codec_control(&encoder_, ctrl_id, arg); 145 ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); 146 } 147 148 void Control(int ctrl_id, struct aom_ext_part_funcs *arg) { 149 const aom_codec_err_t res = aom_codec_control(&encoder_, ctrl_id, arg); 150 ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); 151 } 152 153 #if CONFIG_AV1_ENCODER 154 void Control(int ctrl_id, aom_active_map_t *arg) { 155 const aom_codec_err_t res = aom_codec_control(&encoder_, ctrl_id, arg); 156 ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); 157 } 158 void Control(int ctrl_id, aom_roi_map_t *arg) { 159 const aom_codec_err_t res = aom_codec_control(&encoder_, ctrl_id, arg); 160 ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); 161 } 162 #endif 163 164 void SetOption(const char *name, const char *value) { 165 const aom_codec_err_t res = aom_codec_set_option(&encoder_, name, value); 166 ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); 167 } 168 169 void Config(const aom_codec_enc_cfg_t *cfg) { 170 const aom_codec_err_t res = aom_codec_enc_config_set(&encoder_, cfg); 171 ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); 172 cfg_ = *cfg; 173 } 174 175 protected: 176 virtual aom_codec_iface_t *CodecInterface() const = 0; 177 178 const char *EncoderError() { 179 const char *detail = aom_codec_error_detail(&encoder_); 180 return detail ? detail : aom_codec_error(&encoder_); 181 } 182 183 // Encode an image 184 void EncodeFrameInternal(const VideoSource &video, 185 aom_enc_frame_flags_t frame_flags); 186 187 // Flush the encoder on EOS 188 void Flush(); 189 190 aom_codec_ctx_t encoder_; 191 aom_codec_enc_cfg_t cfg_; 192 aom_codec_flags_t init_flags_; 193 TwopassStatsStore *stats_; 194 }; 195 196 // Common test functionality for all Encoder tests. 197 // 198 // This class is a mixin which provides the main loop common to all 199 // encoder tests. It provides hooks which can be overridden by subclasses 200 // to implement each test's specific behavior, while centralizing the bulk 201 // of the boilerplate. Note that it doesn't inherit the gtest testing 202 // classes directly, so that tests can be parameterized differently. 203 class EncoderTest { 204 protected: 205 explicit EncoderTest(const CodecFactory *codec) 206 : codec_(codec), abort_(false), init_flags_(0), frame_flags_(0), 207 mode_(kRealTime) { 208 // Default to 1 thread. 209 cfg_.g_threads = 1; 210 } 211 212 virtual ~EncoderTest() = default; 213 214 // Initialize the cfg_ member with the default configuration for the 215 // TestMode enum and maps the TestMode enum to the passes_ variable. 216 void InitializeConfig(TestMode mode); 217 218 // Set encoder flag. 219 void set_init_flags(aom_codec_flags_t flag) { init_flags_ = flag; } 220 221 // Main loop 222 virtual void RunLoop(VideoSource *video); 223 224 // Hook to be called at the beginning of a pass. 225 virtual void BeginPassHook(unsigned int /*pass*/) {} 226 227 // Hook to be called at the end of a pass. 228 virtual void EndPassHook() {} 229 230 // Hook to be called before encoding a frame. 231 virtual void PreEncodeFrameHook(VideoSource * /*video*/, 232 Encoder * /*encoder*/) {} 233 234 virtual void PostEncodeFrameHook(Encoder * /*encoder*/) {} 235 236 // Hook to be called on every compressed data packet. 237 virtual void FramePktHook(const aom_codec_cx_pkt_t * /*pkt*/) {} 238 239 // Hook to be called on every PSNR packet. 240 virtual void PSNRPktHook(const aom_codec_cx_pkt_t * /*pkt*/) {} 241 242 // Hook to be called on every first pass stats packet. 243 virtual void StatsPktHook(const aom_codec_cx_pkt_t * /*pkt*/) {} 244 245 // Calculates SSIM at frame level. 246 virtual void CalculateFrameLevelSSIM(const aom_image_t * /*img_src*/, 247 const aom_image_t * /*img_enc*/, 248 aom_bit_depth_t /*bit_depth*/, 249 unsigned int /*input_bit_depth*/) {} 250 251 // Hook to determine whether the encode loop should continue. 252 virtual bool Continue() const { 253 return !(::testing::Test::HasFatalFailure() || abort_); 254 } 255 256 // Hook to determine whether to decode frame after encoding 257 virtual bool DoDecode() const { return true; } 258 259 // Hook to determine whether to decode invisible frames after encoding 260 virtual bool DoDecodeInvisible() const { return true; } 261 262 // Hook to handle encode/decode mismatch 263 virtual void MismatchHook(const aom_image_t *img1, const aom_image_t *img2); 264 265 // Hook to be called on every decompressed frame. 266 virtual void DecompressedFrameHook(const aom_image_t & /*img*/, 267 aom_codec_pts_t /*pts*/) {} 268 269 // Hook to be called to handle decode result. Return true to continue. 270 virtual bool HandleDecodeResult(const aom_codec_err_t res_dec, 271 Decoder *decoder) { 272 EXPECT_EQ(AOM_CODEC_OK, res_dec) << decoder->DecodeError(); 273 return AOM_CODEC_OK == res_dec; 274 } 275 276 virtual int GetNumSpatialLayers() { return 1; } 277 278 // Hook that can modify the encoder's output data 279 virtual const aom_codec_cx_pkt_t *MutateEncoderOutputHook( 280 const aom_codec_cx_pkt_t *pkt) { 281 return pkt; 282 } 283 284 const CodecFactory *codec_; 285 bool abort_; 286 aom_codec_enc_cfg_t cfg_; 287 unsigned int passes_; 288 TwopassStatsStore stats_; 289 aom_codec_flags_t init_flags_; 290 aom_enc_frame_flags_t frame_flags_; 291 TestMode mode_; 292 }; 293 294 } // namespace libaom_test 295 296 #endif // AOM_TEST_ENCODE_TEST_DRIVER_H_