encode_test_driver.cc 8.4 KB

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