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: CxDataIterator(aom_codec_ctx_t * encoder)47 explicit CxDataIterator(aom_codec_ctx_t *encoder) 48 : encoder_(encoder), iter_(NULL) {} 49 Next()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: Append(const aom_codec_cx_pkt_t & pkt)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 buf()67 aom_fixed_buf_t buf() { 68 const aom_fixed_buf_t buf = { &buffer_[0], buffer_.size() }; 69 return buf; 70 } 71 Reset()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: Encoder(aom_codec_enc_cfg_t cfg,const aom_codec_flags_t init_flags,TwopassStatsStore * stats)85 Encoder(aom_codec_enc_cfg_t cfg, const aom_codec_flags_t init_flags, 86 TwopassStatsStore *stats) 87 : cfg_(cfg), init_flags_(init_flags), stats_(stats) { 88 memset(&encoder_, 0, sizeof(encoder_)); 89 } 90 ~Encoder()91 virtual ~Encoder() { aom_codec_destroy(&encoder_); } 92 GetCxData()93 CxDataIterator GetCxData() { return CxDataIterator(&encoder_); } 94 95 void InitEncoder(VideoSource *video); 96 GetPreviewFrame()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() EncodeFrame(VideoSource * video)105 void EncodeFrame(VideoSource *video) { EncodeFrame(video, 0); } 106 Control(int ctrl_id,int arg)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 Control(int ctrl_id,int * arg)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 Control(int ctrl_id,struct aom_scaling_mode * arg)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 Control(int ctrl_id,struct aom_svc_layer_id * arg)122 void Control(int ctrl_id, struct aom_svc_layer_id *arg) { 123 const aom_codec_err_t res = aom_codec_control(&encoder_, ctrl_id, arg); 124 ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); 125 } 126 Control(int ctrl_id,struct aom_svc_ref_frame_config * arg)127 void Control(int ctrl_id, struct aom_svc_ref_frame_config *arg) { 128 const aom_codec_err_t res = aom_codec_control(&encoder_, ctrl_id, arg); 129 ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); 130 } 131 Control(int ctrl_id,struct aom_svc_params * arg)132 void Control(int ctrl_id, struct aom_svc_params *arg) { 133 const aom_codec_err_t res = aom_codec_control(&encoder_, ctrl_id, arg); 134 ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); 135 } 136 137 #if CONFIG_AV1_ENCODER Control(int ctrl_id,aom_active_map_t * arg)138 void Control(int ctrl_id, aom_active_map_t *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 #endif 143 Config(const aom_codec_enc_cfg_t * cfg)144 void Config(const aom_codec_enc_cfg_t *cfg) { 145 const aom_codec_err_t res = aom_codec_enc_config_set(&encoder_, cfg); 146 ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); 147 cfg_ = *cfg; 148 } 149 150 protected: 151 virtual aom_codec_iface_t *CodecInterface() const = 0; 152 EncoderError()153 const char *EncoderError() { 154 const char *detail = aom_codec_error_detail(&encoder_); 155 return detail ? detail : aom_codec_error(&encoder_); 156 } 157 158 // Encode an image 159 void EncodeFrameInternal(const VideoSource &video, 160 const unsigned long frame_flags); 161 162 // Flush the encoder on EOS 163 void Flush(); 164 165 aom_codec_ctx_t encoder_; 166 aom_codec_enc_cfg_t cfg_; 167 aom_codec_flags_t init_flags_; 168 TwopassStatsStore *stats_; 169 }; 170 171 // Common test functionality for all Encoder tests. 172 // 173 // This class is a mixin which provides the main loop common to all 174 // encoder tests. It provides hooks which can be overridden by subclasses 175 // to implement each test's specific behavior, while centralizing the bulk 176 // of the boilerplate. Note that it doesn't inherit the gtest testing 177 // classes directly, so that tests can be parameterized differently. 178 class EncoderTest { 179 protected: EncoderTest(const CodecFactory * codec)180 explicit EncoderTest(const CodecFactory *codec) 181 : codec_(codec), abort_(false), init_flags_(0), frame_flags_(0), 182 last_pts_(0), mode_(kRealTime), number_spatial_layers_(1) { 183 // Default to 1 thread. 184 cfg_.g_threads = 1; 185 } 186 ~EncoderTest()187 virtual ~EncoderTest() {} 188 189 // Initialize the cfg_ member with the default configuration. 190 void InitializeConfig(); 191 192 // Map the TestMode enum to the passes_ variables. 193 void SetMode(TestMode mode); 194 195 // Set encoder flag. set_init_flags(aom_codec_flags_t flag)196 void set_init_flags(aom_codec_flags_t flag) { init_flags_ = flag; } 197 198 // Main loop 199 virtual void RunLoop(VideoSource *video); 200 201 // Hook to be called at the beginning of a pass. BeginPassHook(unsigned int)202 virtual void BeginPassHook(unsigned int /*pass*/) {} 203 204 // Hook to be called at the end of a pass. EndPassHook()205 virtual void EndPassHook() {} 206 207 // Hook to be called before encoding a frame. PreEncodeFrameHook(VideoSource *)208 virtual void PreEncodeFrameHook(VideoSource * /*video*/) {} PreEncodeFrameHook(VideoSource *,Encoder *)209 virtual void PreEncodeFrameHook(VideoSource * /*video*/, 210 Encoder * /*encoder*/) {} 211 212 // Hook to be called on every compressed data packet. FramePktHook(const aom_codec_cx_pkt_t *)213 virtual void FramePktHook(const aom_codec_cx_pkt_t * /*pkt*/) {} 214 215 // Hook to be called on every PSNR packet. PSNRPktHook(const aom_codec_cx_pkt_t *)216 virtual void PSNRPktHook(const aom_codec_cx_pkt_t * /*pkt*/) {} 217 218 // Hook to be called on every first pass stats packet. StatsPktHook(const aom_codec_cx_pkt_t *)219 virtual void StatsPktHook(const aom_codec_cx_pkt_t * /*pkt*/) {} 220 221 // Hook to determine whether the encode loop should continue. Continue()222 virtual bool Continue() const { 223 return !(::testing::Test::HasFatalFailure() || abort_); 224 } 225 226 // Hook to determine whether to decode frame after encoding DoDecode()227 virtual bool DoDecode() const { return true; } 228 229 // Hook to determine whether to decode invisible frames after encoding DoDecodeInvisible()230 virtual bool DoDecodeInvisible() const { return true; } 231 232 // Hook to handle encode/decode mismatch 233 virtual void MismatchHook(const aom_image_t *img1, const aom_image_t *img2); 234 235 // Hook to be called on every decompressed frame. DecompressedFrameHook(const aom_image_t &,aom_codec_pts_t)236 virtual void DecompressedFrameHook(const aom_image_t & /*img*/, 237 aom_codec_pts_t /*pts*/) {} 238 239 // Hook to be called to handle decode result. Return true to continue. HandleDecodeResult(const aom_codec_err_t res_dec,Decoder * decoder)240 virtual bool HandleDecodeResult(const aom_codec_err_t res_dec, 241 Decoder *decoder) { 242 EXPECT_EQ(AOM_CODEC_OK, res_dec) << decoder->DecodeError(); 243 return AOM_CODEC_OK == res_dec; 244 } 245 GetNumSpatialLayers()246 virtual int GetNumSpatialLayers() { return 1; } 247 248 // Hook that can modify the encoder's output data MutateEncoderOutputHook(const aom_codec_cx_pkt_t * pkt)249 virtual const aom_codec_cx_pkt_t *MutateEncoderOutputHook( 250 const aom_codec_cx_pkt_t *pkt) { 251 return pkt; 252 } 253 254 const CodecFactory *codec_; 255 bool abort_; 256 aom_codec_enc_cfg_t cfg_; 257 unsigned int passes_; 258 TwopassStatsStore stats_; 259 aom_codec_flags_t init_flags_; 260 unsigned long frame_flags_; 261 aom_codec_pts_t last_pts_; 262 TestMode mode_; 263 int number_spatial_layers_; 264 }; 265 266 } // namespace libaom_test 267 268 #endif // AOM_TEST_ENCODE_TEST_DRIVER_H_ 269