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 11 #include <string> 12 13 #include "third_party/googletest/src/include/gtest/gtest.h" 14 15 #include "./vpx_config.h" 16 #include "test/codec_factory.h" 17 #include "test/decode_test_driver.h" 18 #include "test/encode_test_driver.h" 19 #include "test/register_state_check.h" 20 #include "test/video_source.h" 21 22 namespace libvpx_test { 23 void Encoder::InitEncoder(VideoSource *video) { 24 vpx_codec_err_t res; 25 const vpx_image_t *img = video->img(); 26 27 if (video->img() && !encoder_.priv) { 28 cfg_.g_w = img->d_w; 29 cfg_.g_h = img->d_h; 30 cfg_.g_timebase = video->timebase(); 31 cfg_.rc_twopass_stats_in = stats_->buf(); 32 33 res = vpx_codec_enc_init(&encoder_, CodecInterface(), &cfg_, init_flags_); 34 ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError(); 35 36 #if CONFIG_VP9_ENCODER 37 if (CodecInterface() == &vpx_codec_vp9_cx_algo) { 38 // Default to 1 tile column for VP9. 39 const int log2_tile_columns = 0; 40 res = vpx_codec_control_(&encoder_, VP9E_SET_TILE_COLUMNS, 41 log2_tile_columns); 42 ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError(); 43 } else 44 #endif 45 { 46 #if CONFIG_VP8_ENCODER 47 ASSERT_EQ(&vpx_codec_vp8_cx_algo, CodecInterface()) 48 << "Unknown Codec Interface"; 49 #endif 50 } 51 } 52 } 53 54 void Encoder::EncodeFrame(VideoSource *video, const unsigned long frame_flags) { 55 if (video->img()) { 56 EncodeFrameInternal(*video, frame_flags); 57 } else { 58 Flush(); 59 } 60 61 // Handle twopass stats 62 CxDataIterator iter = GetCxData(); 63 64 while (const vpx_codec_cx_pkt_t *pkt = iter.Next()) { 65 if (pkt->kind != VPX_CODEC_STATS_PKT) continue; 66 67 stats_->Append(*pkt); 68 } 69 } 70 71 void Encoder::EncodeFrameInternal(const VideoSource &video, 72 const unsigned long frame_flags) { 73 vpx_codec_err_t res; 74 const vpx_image_t *img = video.img(); 75 76 // Handle frame resizing 77 if (cfg_.g_w != img->d_w || cfg_.g_h != img->d_h) { 78 cfg_.g_w = img->d_w; 79 cfg_.g_h = img->d_h; 80 res = vpx_codec_enc_config_set(&encoder_, &cfg_); 81 ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError(); 82 } 83 84 // Encode the frame 85 API_REGISTER_STATE_CHECK(res = vpx_codec_encode(&encoder_, img, video.pts(), 86 video.duration(), frame_flags, 87 deadline_)); 88 ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError(); 89 } 90 91 void Encoder::Flush() { 92 const vpx_codec_err_t res = 93 vpx_codec_encode(&encoder_, NULL, 0, 0, 0, deadline_); 94 if (!encoder_.priv) 95 ASSERT_EQ(VPX_CODEC_ERROR, res) << EncoderError(); 96 else 97 ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError(); 98 } 99 100 void EncoderTest::InitializeConfig() { 101 const vpx_codec_err_t res = codec_->DefaultEncoderConfig(&cfg_, 0); 102 dec_cfg_ = vpx_codec_dec_cfg_t(); 103 ASSERT_EQ(VPX_CODEC_OK, res); 104 } 105 106 void EncoderTest::SetMode(TestMode mode) { 107 switch (mode) { 108 case kRealTime: deadline_ = VPX_DL_REALTIME; break; 109 110 case kOnePassGood: 111 case kTwoPassGood: deadline_ = VPX_DL_GOOD_QUALITY; break; 112 113 case kOnePassBest: 114 case kTwoPassBest: deadline_ = VPX_DL_BEST_QUALITY; break; 115 116 default: ASSERT_TRUE(false) << "Unexpected mode " << mode; 117 } 118 119 if (mode == kTwoPassGood || mode == kTwoPassBest) { 120 passes_ = 2; 121 } else { 122 passes_ = 1; 123 } 124 } 125 // The function should return "true" most of the time, therefore no early 126 // break-out is implemented within the match checking process. 127 static bool compare_img(const vpx_image_t *img1, const vpx_image_t *img2) { 128 bool match = (img1->fmt == img2->fmt) && (img1->cs == img2->cs) && 129 (img1->d_w == img2->d_w) && (img1->d_h == img2->d_h); 130 131 const unsigned int width_y = img1->d_w; 132 const unsigned int height_y = img1->d_h; 133 unsigned int i; 134 for (i = 0; i < height_y; ++i) { 135 match = (memcmp(img1->planes[VPX_PLANE_Y] + i * img1->stride[VPX_PLANE_Y], 136 img2->planes[VPX_PLANE_Y] + i * img2->stride[VPX_PLANE_Y], 137 width_y) == 0) && 138 match; 139 } 140 const unsigned int width_uv = (img1->d_w + 1) >> 1; 141 const unsigned int height_uv = (img1->d_h + 1) >> 1; 142 for (i = 0; i < height_uv; ++i) { 143 match = (memcmp(img1->planes[VPX_PLANE_U] + i * img1->stride[VPX_PLANE_U], 144 img2->planes[VPX_PLANE_U] + i * img2->stride[VPX_PLANE_U], 145 width_uv) == 0) && 146 match; 147 } 148 for (i = 0; i < height_uv; ++i) { 149 match = (memcmp(img1->planes[VPX_PLANE_V] + i * img1->stride[VPX_PLANE_V], 150 img2->planes[VPX_PLANE_V] + i * img2->stride[VPX_PLANE_V], 151 width_uv) == 0) && 152 match; 153 } 154 return match; 155 } 156 157 void EncoderTest::MismatchHook(const vpx_image_t * /*img1*/, 158 const vpx_image_t * /*img2*/) { 159 ASSERT_TRUE(0) << "Encode/Decode mismatch found"; 160 } 161 162 void EncoderTest::RunLoop(VideoSource *video) { 163 vpx_codec_dec_cfg_t dec_cfg = vpx_codec_dec_cfg_t(); 164 165 stats_.Reset(); 166 167 ASSERT_TRUE(passes_ == 1 || passes_ == 2); 168 for (unsigned int pass = 0; pass < passes_; pass++) { 169 last_pts_ = 0; 170 171 if (passes_ == 1) { 172 cfg_.g_pass = VPX_RC_ONE_PASS; 173 } else if (pass == 0) { 174 cfg_.g_pass = VPX_RC_FIRST_PASS; 175 } else { 176 cfg_.g_pass = VPX_RC_LAST_PASS; 177 } 178 179 BeginPassHook(pass); 180 testing::internal::scoped_ptr<Encoder> encoder( 181 codec_->CreateEncoder(cfg_, deadline_, init_flags_, &stats_)); 182 ASSERT_TRUE(encoder.get() != NULL); 183 184 ASSERT_NO_FATAL_FAILURE(video->Begin()); 185 encoder->InitEncoder(video); 186 ASSERT_FALSE(::testing::Test::HasFatalFailure()); 187 188 unsigned long dec_init_flags = 0; // NOLINT 189 // Use fragment decoder if encoder outputs partitions. 190 // NOTE: fragment decoder and partition encoder are only supported by VP8. 191 if (init_flags_ & VPX_CODEC_USE_OUTPUT_PARTITION) { 192 dec_init_flags |= VPX_CODEC_USE_INPUT_FRAGMENTS; 193 } 194 testing::internal::scoped_ptr<Decoder> decoder( 195 codec_->CreateDecoder(dec_cfg, dec_init_flags)); 196 bool again; 197 for (again = true; again; video->Next()) { 198 again = (video->img() != NULL); 199 200 PreEncodeFrameHook(video); 201 PreEncodeFrameHook(video, encoder.get()); 202 encoder->EncodeFrame(video, frame_flags_); 203 204 PostEncodeFrameHook(encoder.get()); 205 206 CxDataIterator iter = encoder->GetCxData(); 207 208 bool has_cxdata = false; 209 bool has_dxdata = false; 210 while (const vpx_codec_cx_pkt_t *pkt = iter.Next()) { 211 pkt = MutateEncoderOutputHook(pkt); 212 again = true; 213 switch (pkt->kind) { 214 case VPX_CODEC_CX_FRAME_PKT: 215 has_cxdata = true; 216 if (decoder.get() != NULL && DoDecode()) { 217 vpx_codec_err_t res_dec = decoder->DecodeFrame( 218 (const uint8_t *)pkt->data.frame.buf, pkt->data.frame.sz); 219 220 if (!HandleDecodeResult(res_dec, *video, decoder.get())) break; 221 222 has_dxdata = true; 223 } 224 ASSERT_GE(pkt->data.frame.pts, last_pts_); 225 last_pts_ = pkt->data.frame.pts; 226 FramePktHook(pkt); 227 break; 228 229 case VPX_CODEC_PSNR_PKT: PSNRPktHook(pkt); break; 230 231 case VPX_CODEC_STATS_PKT: StatsPktHook(pkt); break; 232 233 default: break; 234 } 235 } 236 237 // Flush the decoder when there are no more fragments. 238 if ((init_flags_ & VPX_CODEC_USE_OUTPUT_PARTITION) && has_dxdata) { 239 const vpx_codec_err_t res_dec = decoder->DecodeFrame(NULL, 0); 240 if (!HandleDecodeResult(res_dec, *video, decoder.get())) break; 241 } 242 243 if (has_dxdata && has_cxdata) { 244 const vpx_image_t *img_enc = encoder->GetPreviewFrame(); 245 DxDataIterator dec_iter = decoder->GetDxData(); 246 const vpx_image_t *img_dec = dec_iter.Next(); 247 if (img_enc && img_dec) { 248 const bool res = compare_img(img_enc, img_dec); 249 if (!res) { // Mismatch 250 MismatchHook(img_enc, img_dec); 251 } 252 } 253 if (img_dec) DecompressedFrameHook(*img_dec, video->pts()); 254 } 255 if (!Continue()) break; 256 } 257 258 EndPassHook(); 259 260 if (!Continue()) break; 261 } 262 } 263 264 } // namespace libvpx_test 265