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