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