]> granicus.if.org Git - libvpx/commitdiff
Merge "multi-res: work around reference mismatch"
authorJohn Koleszar <jkoleszar@google.com>
Mon, 8 Oct 2012 22:48:25 +0000 (15:48 -0700)
committerGerrit Code Review <gerrit@gerrit.golo.chromium.org>
Mon, 8 Oct 2012 22:48:25 +0000 (15:48 -0700)
test/cq_test.cc [new file with mode: 0644]
test/encode_test_driver.cc
test/encode_test_driver.h
test/error_resilience_test.cc [new file with mode: 0644]
test/keyframe_test.cc
test/test.mk

diff --git a/test/cq_test.cc b/test/cq_test.cc
new file mode 100644 (file)
index 0000000..42ee2a2
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ *  Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#include <cmath>
+#include "third_party/googletest/src/include/gtest/gtest.h"
+#include "test/encode_test_driver.h"
+#include "test/i420_video_source.h"
+
+// CQ level range: [kCQLevelMin, kCQLevelMax).
+const int kCQLevelMin = 4;
+const int kCQLevelMax = 63;
+const int kCQLevelStep = 8;
+const int kCQTargetBitrate = 2000;
+
+namespace {
+
+class CQTest : public libvpx_test::EncoderTest,
+    public ::testing::TestWithParam<int> {
+ protected:
+  CQTest() : cq_level_(GetParam()) { init_flags_ = VPX_CODEC_USE_PSNR; }
+  virtual ~CQTest() {}
+
+  virtual void SetUp() {
+    InitializeConfig();
+    SetMode(libvpx_test::kTwoPassGood);
+  }
+
+  virtual void BeginPassHook(unsigned int /*pass*/) {
+    file_size_ = 0;
+    psnr_ = 0.0;
+    n_frames_ = 0;
+  }
+
+  virtual bool Continue() const {
+    return !HasFatalFailure() && !abort_;
+  }
+
+  virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video,
+                                  libvpx_test::Encoder *encoder) {
+    if (video->frame() == 1) {
+      if (cfg_.rc_end_usage == VPX_CQ) {
+        encoder->Control(VP8E_SET_CQ_LEVEL, cq_level_);
+      }
+      encoder->Control(VP8E_SET_CPUUSED, 3);
+    }
+  }
+
+  virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {
+    psnr_ += pow(10.0, pkt->data.psnr.psnr[0] / 10.0);
+    n_frames_++;
+  }
+
+  virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
+    file_size_ += pkt->data.frame.sz;
+  }
+
+  double GetLinearPSNROverBitrate() const {
+    double avg_psnr = log10(psnr_ / n_frames_) * 10.0;
+    return pow(10.0, avg_psnr / 10.0) / file_size_;
+  }
+
+  int file_size() const { return file_size_; }
+  int n_frames() const { return n_frames_; }
+
+ private:
+  int cq_level_;
+  int file_size_;
+  double psnr_;
+  int n_frames_;
+};
+
+int prev_actual_bitrate = kCQTargetBitrate;
+TEST_P(CQTest, LinearPSNRIsHigherForCQLevel) {
+  const vpx_rational timebase = { 33333333, 1000000000 };
+  cfg_.g_timebase = timebase;
+  cfg_.rc_target_bitrate = kCQTargetBitrate;
+  cfg_.g_lag_in_frames = 25;
+
+  cfg_.rc_end_usage = VPX_CQ;
+  libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
+                                     timebase.den, timebase.num, 0, 30);
+  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+  const double cq_psnr_lin = GetLinearPSNROverBitrate();
+  const int cq_actual_bitrate = file_size() * 8 * 30 / (n_frames() * 1000);
+  EXPECT_LE(cq_actual_bitrate, kCQTargetBitrate);
+  EXPECT_LE(cq_actual_bitrate, prev_actual_bitrate);
+  prev_actual_bitrate = cq_actual_bitrate;
+
+  // try targeting the approximate same bitrate with VBR mode
+  cfg_.rc_end_usage = VPX_VBR;
+  cfg_.rc_target_bitrate = cq_actual_bitrate;
+  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+  const double vbr_psnr_lin = GetLinearPSNROverBitrate();
+  EXPECT_GE(cq_psnr_lin, vbr_psnr_lin);
+}
+
+INSTANTIATE_TEST_CASE_P(CQLevelRange, CQTest,
+                        ::testing::Range(kCQLevelMin, kCQLevelMax,
+                                         kCQLevelStep));
+}  // namespace
index 43e99187d54f34c3c750f1e884acba7b9a884388..8dbc88be144dc6c09d16fb1eaad087d789c60b7c 100644 (file)
@@ -16,9 +16,9 @@
 #include "third_party/googletest/src/include/gtest/gtest.h"
 
 namespace libvpx_test {
-void Encoder::EncodeFrame(VideoSource *video, unsigned long flags) {
+void Encoder::EncodeFrame(VideoSource *video, const unsigned long frame_flags) {
   if (video->img())
-    EncodeFrameInternal(*video, flags);
+    EncodeFrameInternal(*video, frame_flags);
   else
     Flush();
 
@@ -34,7 +34,7 @@ void Encoder::EncodeFrame(VideoSource *video, unsigned long flags) {
 }
 
 void Encoder::EncodeFrameInternal(const VideoSource &video,
-                                  unsigned long flags) {
+                                  const unsigned long frame_flags) {
   vpx_codec_err_t res;
   const vpx_image_t *img = video.img();
 
@@ -44,7 +44,8 @@ void Encoder::EncodeFrameInternal(const VideoSource &video,
     cfg_.g_h = img->d_h;
     cfg_.g_timebase = video.timebase();
     cfg_.rc_twopass_stats_in = stats_->buf();
-    res = vpx_codec_enc_init(&encoder_, &vpx_codec_vp8_cx_algo, &cfg_, 0);
+    res = vpx_codec_enc_init(&encoder_, &vpx_codec_vp8_cx_algo, &cfg_,
+                             init_flags_);
     ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError();
   }
 
@@ -59,7 +60,7 @@ void Encoder::EncodeFrameInternal(const VideoSource &video,
   // Encode the frame
   res = vpx_codec_encode(&encoder_,
                          video.img(), video.pts(), video.duration(),
-                         flags, deadline_);
+                         frame_flags, deadline_);
   ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError();
 }
 
@@ -140,7 +141,7 @@ void EncoderTest::RunLoop(VideoSource *video) {
       cfg_.g_pass = VPX_RC_LAST_PASS;
 
     BeginPassHook(pass);
-    Encoder encoder(cfg_, deadline_, &stats_);
+    Encoder encoder(cfg_, deadline_, init_flags_, &stats_);
 #if CONFIG_VP8_DECODER
     Decoder decoder(dec_cfg);
     bool has_cxdata = false;
@@ -151,23 +152,32 @@ void EncoderTest::RunLoop(VideoSource *video) {
 
       PreEncodeFrameHook(video);
       PreEncodeFrameHook(video, &encoder);
-      encoder.EncodeFrame(video, flags_);
+      encoder.EncodeFrame(video, frame_flags_);
 
       CxDataIterator iter = encoder.GetCxData();
 
       while (const vpx_codec_cx_pkt_t *pkt = iter.Next()) {
         again = true;
 
-        if (pkt->kind != VPX_CODEC_CX_FRAME_PKT)
-          continue;
+        switch (pkt->kind) {
+          case VPX_CODEC_CX_FRAME_PKT:
 #if CONFIG_VP8_DECODER
-        has_cxdata = true;
-        decoder.DecodeFrame((const uint8_t*)pkt->data.frame.buf,
-                            pkt->data.frame.sz);
+            has_cxdata = true;
+            decoder.DecodeFrame((const uint8_t*)pkt->data.frame.buf,
+                                pkt->data.frame.sz);
 #endif
-        ASSERT_GE(pkt->data.frame.pts, last_pts_);
-        last_pts_ = pkt->data.frame.pts;
-        FramePktHook(pkt);
+            ASSERT_GE(pkt->data.frame.pts, last_pts_);
+            last_pts_ = pkt->data.frame.pts;
+            FramePktHook(pkt);
+            break;
+
+          case VPX_CODEC_PSNR_PKT:
+            PSNRPktHook(pkt);
+            break;
+
+          default:
+            break;
+        }
       }
 
 #if CONFIG_VP8_DECODER
index f0654926845eca5ba1bfcd26080eea7bffe9bbc7..0141fa9107c2c2a6604643cbe0ccb88819b0c702 100644 (file)
@@ -82,8 +82,8 @@ class TwopassStatsStore {
 class Encoder {
  public:
   Encoder(vpx_codec_enc_cfg_t cfg, unsigned long deadline,
-          TwopassStatsStore *stats)
-    : cfg_(cfg), deadline_(deadline), stats_(stats) {
+          const unsigned long init_flags, TwopassStatsStore *stats)
+    : cfg_(cfg), deadline_(deadline), init_flags_(init_flags), stats_(stats) {
     memset(&encoder_, 0, sizeof(encoder_));
   }
 
@@ -100,7 +100,7 @@ class Encoder {
   }
   // This is a thin wrapper around vpx_codec_encode(), so refer to
   // vpx_encoder.h for its semantics.
-  void EncodeFrame(VideoSource *video, unsigned long flags);
+  void EncodeFrame(VideoSource *video, const unsigned long frame_flags);
 
   // Convenience wrapper for EncodeFrame()
   void EncodeFrame(VideoSource *video) {
@@ -123,7 +123,8 @@ class Encoder {
   }
 
   // Encode an image
-  void EncodeFrameInternal(const VideoSource &video, unsigned long flags);
+  void EncodeFrameInternal(const VideoSource &video,
+                           const unsigned long frame_flags);
 
   // Flush the encoder on EOS
   void Flush();
@@ -131,6 +132,7 @@ class Encoder {
   vpx_codec_ctx_t      encoder_;
   vpx_codec_enc_cfg_t  cfg_;
   unsigned long        deadline_;
+  unsigned long        init_flags_;
   TwopassStatsStore   *stats_;
 };
 
@@ -143,7 +145,8 @@ class Encoder {
 // classes directly, so that tests can be parameterized differently.
 class EncoderTest {
  protected:
-  EncoderTest() : abort_(false), flags_(0), last_pts_(0) {}
+  EncoderTest() : abort_(false), init_flags_(0), frame_flags_(0),
+                  last_pts_(0) {}
 
   virtual ~EncoderTest() {}
 
@@ -173,6 +176,9 @@ class EncoderTest {
   // Hook to be called on every compressed data packet.
   virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {}
 
+  // Hook to be called on every PSNR packet.
+  virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {}
+
   // Hook to determine whether the encode loop should continue.
   virtual bool Continue() const { return !abort_; }
 
@@ -181,7 +187,8 @@ class EncoderTest {
   unsigned int         passes_;
   unsigned long        deadline_;
   TwopassStatsStore    stats_;
-  unsigned long        flags_;
+  unsigned long        init_flags_;
+  unsigned long        frame_flags_;
   vpx_codec_pts_t      last_pts_;
 };
 
diff --git a/test/error_resilience_test.cc b/test/error_resilience_test.cc
new file mode 100644 (file)
index 0000000..25c6731
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+  Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+
+  Use of this source code is governed by a BSD-style license
+  that can be found in the LICENSE file in the root of the source
+  tree. An additional intellectual property rights grant can be found
+  in the file PATENTS.  All contributing project authors may
+  be found in the AUTHORS file in the root of the source tree.
+*/
+#include "third_party/googletest/src/include/gtest/gtest.h"
+#include "test/encode_test_driver.h"
+#include "test/i420_video_source.h"
+
+namespace {
+
+class ErrorResilienceTest : public libvpx_test::EncoderTest,
+    public ::testing::TestWithParam<int> {
+ protected:
+  ErrorResilienceTest() {
+    psnr_ = 0.0;
+    nframes_ = 0;
+    encoding_mode_ = static_cast<libvpx_test::TestMode>(GetParam());
+  }
+  virtual ~ErrorResilienceTest() {}
+
+  virtual void SetUp() {
+    InitializeConfig();
+    SetMode(encoding_mode_);
+  }
+
+  virtual void BeginPassHook(unsigned int /*pass*/) {
+    psnr_ = 0.0;
+    nframes_ = 0;
+  }
+
+  virtual bool Continue() const {
+    return !HasFatalFailure() && !abort_;
+  }
+
+  virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {
+    psnr_ += pkt->data.psnr.psnr[0];
+    nframes_++;
+  }
+
+  double GetAveragePsnr() const {
+    if (nframes_)
+      return psnr_ / nframes_;
+    return 0.0;
+  }
+
+ private:
+  double psnr_;
+  unsigned int nframes_;
+  libvpx_test::TestMode encoding_mode_;
+};
+
+TEST_P(ErrorResilienceTest, OnVersusOff) {
+  const vpx_rational timebase = { 33333333, 1000000000 };
+  cfg_.g_timebase = timebase;
+  cfg_.rc_target_bitrate = 2000;
+  cfg_.g_lag_in_frames = 25;
+
+  init_flags_ = VPX_CODEC_USE_PSNR;
+
+  libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
+                                     timebase.den, timebase.num, 0, 30);
+
+  // Error resilient mode OFF.
+  cfg_.g_error_resilient = 0;
+  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+  const double psnr_resilience_off = GetAveragePsnr();
+  EXPECT_GT(psnr_resilience_off, 25.0);
+
+  // Error resilient mode ON.
+  cfg_.g_error_resilient = 1;
+  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+  const double psnr_resilience_on = GetAveragePsnr();
+  EXPECT_GT(psnr_resilience_on, 25.0);
+
+  // Test that turning on error resilient mode hurts by 10% at most.
+  if (psnr_resilience_off > 0.0) {
+    const double psnr_ratio = psnr_resilience_on / psnr_resilience_off;
+    EXPECT_GE(psnr_ratio, 0.9);
+    EXPECT_LE(psnr_ratio, 1.1);
+  }
+}
+
+INSTANTIATE_TEST_CASE_P(OnOffTest, ErrorResilienceTest,
+                        ONE_PASS_TEST_MODES);
+}  // namespace
index 9925daea73c0671b1d2f4b8ea71be2d0e8d50ace..d0c81df99825a8e1a5f50505ec477b39ed89a438 100644 (file)
@@ -34,7 +34,7 @@ class KeyframeTest : public ::libvpx_test::EncoderTest,
   virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
                                   ::libvpx_test::Encoder *encoder) {
     if (kf_do_force_kf_)
-      flags_ = (video->frame() % 3) ? 0 : VPX_EFLAG_FORCE_KF;
+      frame_flags_ = (video->frame() % 3) ? 0 : VPX_EFLAG_FORCE_KF;
     if (set_cpu_used_ && video->frame() == 1)
       encoder->Control(VP8E_SET_CPUUSED, set_cpu_used_);
   }
index 0dfbdbea6410a7f549f5bd283fcd728e08a0b968..7e0732023e24ef59c0c8a358f065520e290e9e68 100644 (file)
@@ -10,8 +10,10 @@ LIBVPX_TEST_SRCS-yes += util.h
 ##
 LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += altref_test.cc
 LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += config_test.cc
+LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += cq_test.cc
 LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += encode_test_driver.cc
 LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += encode_test_driver.h
+LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += error_resilience_test.cc
 LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += i420_video_source.h
 LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += keyframe_test.cc
 LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += resize_test.cc