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