• 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 #include "third_party/googletest/src/include/gtest/gtest.h"
11 #include "test/codec_factory.h"
12 #include "test/encode_test_driver.h"
13 #include "test/i420_video_source.h"
14 #include "test/util.h"
15 
16 namespace {
17 
18 class DatarateTest : public ::libvpx_test::EncoderTest,
19     public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
20  public:
DatarateTest()21   DatarateTest() : EncoderTest(GET_PARAM(0)) {}
22 
23  protected:
SetUp()24   virtual void SetUp() {
25     InitializeConfig();
26     SetMode(GET_PARAM(1));
27     ResetModel();
28   }
29 
ResetModel()30   virtual void ResetModel() {
31     last_pts_ = 0;
32     bits_in_buffer_model_ = cfg_.rc_target_bitrate * cfg_.rc_buf_initial_sz;
33     frame_number_ = 0;
34     first_drop_ = 0;
35     bits_total_ = 0;
36     duration_ = 0.0;
37   }
38 
PreEncodeFrameHook(::libvpx_test::VideoSource * video,::libvpx_test::Encoder * encoder)39   virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
40                                   ::libvpx_test::Encoder *encoder) {
41     const vpx_rational_t tb = video->timebase();
42     timebase_ = static_cast<double>(tb.num) / tb.den;
43     duration_ = 0;
44   }
45 
FramePktHook(const vpx_codec_cx_pkt_t * pkt)46   virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
47     // Time since last timestamp = duration.
48     vpx_codec_pts_t duration = pkt->data.frame.pts - last_pts_;
49 
50     // TODO(jimbankoski): Remove these lines when the issue:
51     // http://code.google.com/p/webm/issues/detail?id=496 is fixed.
52     // For now the codec assumes buffer starts at starting buffer rate
53     // plus one frame's time.
54     if (last_pts_ == 0)
55       duration = 1;
56 
57     // Add to the buffer the bits we'd expect from a constant bitrate server.
58     bits_in_buffer_model_ += duration * timebase_ * cfg_.rc_target_bitrate
59         * 1000;
60 
61     /* Test the buffer model here before subtracting the frame. Do so because
62      * the way the leaky bucket model works in libvpx is to allow the buffer to
63      * empty - and then stop showing frames until we've got enough bits to
64      * show one. As noted in comment below (issue 495), this does not currently
65      * apply to key frames. For now exclude key frames in condition below. */
66     bool key_frame = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) ? true: false;
67     if (!key_frame) {
68       ASSERT_GE(bits_in_buffer_model_, 0) << "Buffer Underrun at frame "
69           << pkt->data.frame.pts;
70     }
71 
72     const int frame_size_in_bits = pkt->data.frame.sz * 8;
73 
74     // Subtract from the buffer the bits associated with a played back frame.
75     bits_in_buffer_model_ -= frame_size_in_bits;
76 
77     // Update the running total of bits for end of test datarate checks.
78     bits_total_ += frame_size_in_bits;
79 
80     // If first drop not set and we have a drop set it to this time.
81     if (!first_drop_ && duration > 1)
82       first_drop_ = last_pts_ + 1;
83 
84     // Update the most recent pts.
85     last_pts_ = pkt->data.frame.pts;
86 
87     // We update this so that we can calculate the datarate minus the last
88     // frame encoded in the file.
89     bits_in_last_frame_ = frame_size_in_bits;
90 
91     ++frame_number_;
92   }
93 
EndPassHook(void)94   virtual void EndPassHook(void) {
95     if (bits_total_) {
96       const double file_size_in_kb = bits_total_ / 1000;  /* bits per kilobit */
97 
98       duration_ = (last_pts_ + 1) * timebase_;
99 
100       // Effective file datarate includes the time spent prebuffering.
101       effective_datarate_ = (bits_total_ - bits_in_last_frame_) / 1000.0
102           / (cfg_.rc_buf_initial_sz / 1000.0 + duration_);
103 
104       file_datarate_ = file_size_in_kb / duration_;
105     }
106   }
107 
108   vpx_codec_pts_t last_pts_;
109   int bits_in_buffer_model_;
110   double timebase_;
111   int frame_number_;
112   vpx_codec_pts_t first_drop_;
113   int64_t bits_total_;
114   double duration_;
115   double file_datarate_;
116   double effective_datarate_;
117   int bits_in_last_frame_;
118 };
119 
TEST_P(DatarateTest,BasicBufferModel)120 TEST_P(DatarateTest, BasicBufferModel) {
121   cfg_.rc_buf_initial_sz = 500;
122   cfg_.rc_dropframe_thresh = 1;
123   cfg_.rc_max_quantizer = 56;
124   cfg_.rc_end_usage = VPX_CBR;
125   // 2 pass cbr datarate control has a bug hidden by the small # of
126   // frames selected in this encode. The problem is that even if the buffer is
127   // negative we produce a keyframe on a cutscene. Ignoring datarate
128   // constraints
129   // TODO(jimbankoski): ( Fix when issue
130   // http://code.google.com/p/webm/issues/detail?id=495 is addressed. )
131   ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
132                                        30, 1, 0, 140);
133 
134   // There is an issue for low bitrates in real-time mode, where the
135   // effective_datarate slightly overshoots the target bitrate.
136   // This is same the issue as noted about (#495).
137   // TODO(jimbankoski/marpan): Update test to run for lower bitrates (< 100),
138   // when the issue is resolved.
139   for (int i = 100; i < 800; i += 200) {
140     cfg_.rc_target_bitrate = i;
141     ResetModel();
142     ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
143     ASSERT_GE(cfg_.rc_target_bitrate, effective_datarate_)
144         << " The datarate for the file exceeds the target!";
145 
146     ASSERT_LE(cfg_.rc_target_bitrate, file_datarate_ * 1.3)
147         << " The datarate for the file missed the target!";
148   }
149 }
150 
TEST_P(DatarateTest,ChangingDropFrameThresh)151 TEST_P(DatarateTest, ChangingDropFrameThresh) {
152   cfg_.rc_buf_initial_sz = 500;
153   cfg_.rc_max_quantizer = 36;
154   cfg_.rc_end_usage = VPX_CBR;
155   cfg_.rc_target_bitrate = 200;
156   cfg_.kf_mode = VPX_KF_DISABLED;
157 
158   const int frame_count = 40;
159   ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
160                                        30, 1, 0, frame_count);
161 
162   // Here we check that the first dropped frame gets earlier and earlier
163   // as the drop frame threshold is increased.
164 
165   const int kDropFrameThreshTestStep = 30;
166   vpx_codec_pts_t last_drop = frame_count;
167   for (int i = 1; i < 91; i += kDropFrameThreshTestStep) {
168     cfg_.rc_dropframe_thresh = i;
169     ResetModel();
170     ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
171     ASSERT_LE(first_drop_, last_drop)
172         << " The first dropped frame for drop_thresh " << i
173         << " > first dropped frame for drop_thresh "
174         << i - kDropFrameThreshTestStep;
175     last_drop = first_drop_;
176   }
177 }
178 
179 class DatarateTestVP9 : public ::libvpx_test::EncoderTest,
180     public ::libvpx_test::CodecTestWith2Params<libvpx_test::TestMode, int> {
181  public:
DatarateTestVP9()182   DatarateTestVP9() : EncoderTest(GET_PARAM(0)) {}
183 
184  protected:
~DatarateTestVP9()185   virtual ~DatarateTestVP9() {}
186 
SetUp()187   virtual void SetUp() {
188     InitializeConfig();
189     SetMode(GET_PARAM(1));
190     set_cpu_used_ = GET_PARAM(2);
191     ResetModel();
192   }
193 
ResetModel()194   virtual void ResetModel() {
195     last_pts_ = 0;
196     frame_number_ = 0;
197     bits_total_ = 0;
198     duration_ = 0.0;
199   }
200 
PreEncodeFrameHook(::libvpx_test::VideoSource * video,::libvpx_test::Encoder * encoder)201   virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
202                                     ::libvpx_test::Encoder *encoder) {
203     if (video->frame() == 1) {
204       encoder->Control(VP8E_SET_CPUUSED, set_cpu_used_);
205     }
206     const vpx_rational_t tb = video->timebase();
207     timebase_ = static_cast<double>(tb.num) / tb.den;
208     duration_ = 0;
209   }
210 
FramePktHook(const vpx_codec_cx_pkt_t * pkt)211   virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
212     const int frame_size_in_bits = pkt->data.frame.sz * 8;
213     bits_total_ += frame_size_in_bits;
214     // Update the most recent pts.
215     last_pts_ = pkt->data.frame.pts;
216     ++frame_number_;
217   }
218 
EndPassHook(void)219   virtual void EndPassHook(void) {
220     if (bits_total_) {
221       duration_ = (last_pts_ + 1) * timebase_;
222       // Effective file datarate:
223       effective_datarate_ = ((bits_total_) / 1000.0) / duration_;
224     }
225   }
226 
227   vpx_codec_pts_t last_pts_;
228   double timebase_;
229   int frame_number_;
230   int64_t bits_total_;
231   double duration_;
232   double effective_datarate_;
233   int set_cpu_used_;
234 };
235 
236 // There is no buffer model/frame dropper in VP9 currently, so for now we
237 // have separate test for VP9 rate targeting for 1-pass CBR. We only check
238 // that effective datarate is within some range of target bitrate.
239 // No frame dropper, so we can't go to low bitrates.
TEST_P(DatarateTestVP9,BasicRateTargeting)240 TEST_P(DatarateTestVP9, BasicRateTargeting) {
241   cfg_.rc_min_quantizer = 0;
242   cfg_.rc_max_quantizer = 63;
243   cfg_.rc_end_usage = VPX_CBR;
244 
245   ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
246                                        30, 1, 0, 140);
247   for (int i = 150; i < 800; i += 200) {
248     cfg_.rc_target_bitrate = i;
249     ResetModel();
250     ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
251     ASSERT_GE(cfg_.rc_target_bitrate, effective_datarate_ * 0.9)
252         << " The datarate for the file exceeds the target by too much!";
253     ASSERT_LE(cfg_.rc_target_bitrate, effective_datarate_ * 1.1)
254         << " The datarate for the file missed the target!";
255   }
256 }
257 
258 VP8_INSTANTIATE_TEST_CASE(DatarateTest, ALL_TEST_MODES);
259 VP9_INSTANTIATE_TEST_CASE(DatarateTestVP9,
260                           ::testing::Values(::libvpx_test::kOnePassGood),
261                           ::testing::Range(1, 5));
262 }  // namespace
263