datarate_test.cc revision 91037db265ecdd914a26e056cf69207b4f50924e
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
16namespace {
17
18class DatarateTest : public ::libvpx_test::EncoderTest,
19    public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
20 public:
21  DatarateTest() : EncoderTest(GET_PARAM(0)) {}
22
23 protected:
24  virtual void SetUp() {
25    InitializeConfig();
26    SetMode(GET_PARAM(1));
27    ResetModel();
28  }
29
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
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
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
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
120TEST_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
151TEST_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
179VP8_INSTANTIATE_TEST_CASE(DatarateTest, ALL_TEST_MODES);
180
181}  // namespace
182