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