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 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 ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError(); 73} 74 75void EncoderTest::InitializeConfig() { 76 const vpx_codec_err_t res = codec_->DefaultEncoderConfig(&cfg_, 0); 77 ASSERT_EQ(VPX_CODEC_OK, res); 78} 79 80void EncoderTest::SetMode(TestMode mode) { 81 switch (mode) { 82 case kRealTime: 83 deadline_ = VPX_DL_REALTIME; 84 break; 85 86 case kOnePassGood: 87 case kTwoPassGood: 88 deadline_ = VPX_DL_GOOD_QUALITY; 89 break; 90 91 case kOnePassBest: 92 case kTwoPassBest: 93 deadline_ = VPX_DL_BEST_QUALITY; 94 break; 95 96 default: 97 ASSERT_TRUE(false) << "Unexpected mode " << mode; 98 } 99 100 if (mode == kTwoPassGood || mode == kTwoPassBest) 101 passes_ = 2; 102 else 103 passes_ = 1; 104} 105// The function should return "true" most of the time, therefore no early 106// break-out is implemented within the match checking process. 107static bool compare_img(const vpx_image_t *img1, 108 const vpx_image_t *img2) { 109 bool match = (img1->fmt == img2->fmt) && 110 (img1->d_w == img2->d_w) && 111 (img1->d_h == img2->d_h); 112 113 const unsigned int width_y = img1->d_w; 114 const unsigned int height_y = img1->d_h; 115 unsigned int i; 116 for (i = 0; i < height_y; ++i) 117 match = (memcmp(img1->planes[VPX_PLANE_Y] + i * img1->stride[VPX_PLANE_Y], 118 img2->planes[VPX_PLANE_Y] + i * img2->stride[VPX_PLANE_Y], 119 width_y) == 0) && match; 120 const unsigned int width_uv = (img1->d_w + 1) >> 1; 121 const unsigned int height_uv = (img1->d_h + 1) >> 1; 122 for (i = 0; i < height_uv; ++i) 123 match = (memcmp(img1->planes[VPX_PLANE_U] + i * img1->stride[VPX_PLANE_U], 124 img2->planes[VPX_PLANE_U] + i * img2->stride[VPX_PLANE_U], 125 width_uv) == 0) && match; 126 for (i = 0; i < height_uv; ++i) 127 match = (memcmp(img1->planes[VPX_PLANE_V] + i * img1->stride[VPX_PLANE_V], 128 img2->planes[VPX_PLANE_V] + i * img2->stride[VPX_PLANE_V], 129 width_uv) == 0) && match; 130 return match; 131} 132 133void EncoderTest::MismatchHook(const vpx_image_t *img1, 134 const vpx_image_t *img2) { 135 ASSERT_TRUE(0) << "Encode/Decode mismatch found"; 136} 137 138void EncoderTest::RunLoop(VideoSource *video) { 139 vpx_codec_dec_cfg_t dec_cfg = {0}; 140 141 stats_.Reset(); 142 143 ASSERT_TRUE(passes_ == 1 || passes_ == 2); 144 for (unsigned int pass = 0; pass < passes_; pass++) { 145 last_pts_ = 0; 146 147 if (passes_ == 1) 148 cfg_.g_pass = VPX_RC_ONE_PASS; 149 else if (pass == 0) 150 cfg_.g_pass = VPX_RC_FIRST_PASS; 151 else 152 cfg_.g_pass = VPX_RC_LAST_PASS; 153 154 BeginPassHook(pass); 155 Encoder* const encoder = codec_->CreateEncoder(cfg_, deadline_, init_flags_, 156 &stats_); 157 ASSERT_TRUE(encoder != NULL); 158 Decoder* const decoder = codec_->CreateDecoder(dec_cfg, 0); 159 bool again; 160 for (again = true, video->Begin(); again; video->Next()) { 161 again = (video->img() != NULL); 162 163 PreEncodeFrameHook(video); 164 PreEncodeFrameHook(video, encoder); 165 encoder->EncodeFrame(video, frame_flags_); 166 167 CxDataIterator iter = encoder->GetCxData(); 168 169 bool has_cxdata = false; 170 bool has_dxdata = false; 171 while (const vpx_codec_cx_pkt_t *pkt = iter.Next()) { 172 pkt = MutateEncoderOutputHook(pkt); 173 again = true; 174 switch (pkt->kind) { 175 case VPX_CODEC_CX_FRAME_PKT: 176 has_cxdata = true; 177 if (decoder && DoDecode()) { 178 vpx_codec_err_t res_dec = decoder->DecodeFrame( 179 (const uint8_t*)pkt->data.frame.buf, pkt->data.frame.sz); 180 ASSERT_EQ(VPX_CODEC_OK, res_dec) << decoder->DecodeError(); 181 has_dxdata = true; 182 } 183 ASSERT_GE(pkt->data.frame.pts, last_pts_); 184 last_pts_ = pkt->data.frame.pts; 185 FramePktHook(pkt); 186 break; 187 188 case VPX_CODEC_PSNR_PKT: 189 PSNRPktHook(pkt); 190 break; 191 192 default: 193 break; 194 } 195 } 196 197 if (has_dxdata && has_cxdata) { 198 const vpx_image_t *img_enc = encoder->GetPreviewFrame(); 199 DxDataIterator dec_iter = decoder->GetDxData(); 200 const vpx_image_t *img_dec = dec_iter.Next(); 201 if (img_enc && img_dec) { 202 const bool res = compare_img(img_enc, img_dec); 203 if (!res) { // Mismatch 204 MismatchHook(img_enc, img_dec); 205 } 206 } 207 if (img_dec) 208 DecompressedFrameHook(*img_dec, video->pts()); 209 } 210 if (!Continue()) 211 break; 212 } 213 214 EndPassHook(); 215 216 if (decoder) 217 delete decoder; 218 delete encoder; 219 220 if (!Continue()) 221 break; 222 } 223} 224 225} // namespace libvpx_test 226