Merge "Unit Test for Error Resilience Mode"
diff --git a/test/cq_test.cc b/test/cq_test.cc
new file mode 100644
index 0000000..42ee2a2
--- /dev/null
+++ b/test/cq_test.cc
@@ -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
diff --git a/test/keyframe_test.cc b/test/keyframe_test.cc
index b003aa3..d0c81df 100644
--- a/test/keyframe_test.cc
+++ b/test/keyframe_test.cc
@@ -62,7 +62,10 @@
   ::libvpx_test::RandomVideoSource video;
   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
 
-  EXPECT_GT(kf_count_, 1);
+  // In realtime mode - auto placed keyframes are exceedingly rare,  don't
+  // bother with this check   if(GetParam() > 0)
+  if(GetParam() > 0)
+    EXPECT_GT(kf_count_, 1);
 }
 
 TEST_P(KeyframeTest, TestDisableKeyframes) {
@@ -121,7 +124,10 @@
 
   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
 
-  EXPECT_EQ(2u, kf_pts_list_.size()) << " Not the right number of keyframes ";
+  // In realtime mode - auto placed keyframes are exceedingly rare,  don't
+  // bother with this check
+  if(GetParam() > 0)
+    EXPECT_EQ(2u, kf_pts_list_.size()) << " Not the right number of keyframes ";
 
   // Verify that keyframes match the file keyframes in the file.
   for (std::vector<vpx_codec_pts_t>::const_iterator iter = kf_pts_list_.begin();
diff --git a/test/subtract_test.cc b/test/subtract_test.cc
new file mode 100644
index 0000000..99363de
--- /dev/null
+++ b/test/subtract_test.cc
@@ -0,0 +1,113 @@
+/*
+ *  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/acm_random.h"
+extern "C" {
+#include "vpx_config.h"
+#include "vpx_rtcd.h"
+#include "vp8/common/blockd.h"
+#include "vp8/encoder/block.h"
+#include "vpx_mem/vpx_mem.h"
+}
+
+typedef void (*subtract_b_fn_t)(BLOCK *be, BLOCKD *bd, int pitch);
+
+namespace {
+
+class SubtractBlockTest : public ::testing::TestWithParam<subtract_b_fn_t> {};
+
+using libvpx_test::ACMRandom;
+
+TEST_P(SubtractBlockTest, SimpleSubtract) {
+  ACMRandom rnd(ACMRandom::DeterministicSeed());
+  BLOCK be;
+  BLOCKD bd;
+  // in libvpx, this stride is always 16
+  const int kDiffPredStride = 16;
+  const int kSrcStride[] = {32, 16, 8, 4, 0};
+  const int kBlockWidth = 4;
+  const int kBlockHeight = 4;
+
+  // Allocate... align to 16 for mmx/sse tests
+  uint8_t *source = reinterpret_cast<uint8_t*>(
+      vpx_memalign(16, kBlockHeight * kSrcStride[0] * sizeof(*source)));
+  be.src_diff = reinterpret_cast<int16_t*>(
+      vpx_memalign(16, kBlockHeight * kDiffPredStride * sizeof(*be.src_diff)));
+  bd.predictor = reinterpret_cast<unsigned char*>(
+      vpx_memalign(16, kBlockHeight * kDiffPredStride * sizeof(*bd.predictor)));
+
+  for(int i = 0; kSrcStride[i] > 0; ++i) {
+    // start at block0
+    be.src = 0;
+    be.base_src = &source;
+    be.src_stride = kSrcStride[i];
+
+    // set difference
+    int16_t *src_diff = be.src_diff;
+    for (int r = 0; r < kBlockHeight; ++r) {
+      for (int c = 0; c < kBlockWidth; ++c) {
+        src_diff[c] = 0xa5a5;
+      }
+      src_diff += kDiffPredStride;
+    }
+
+    // set destination
+    uint8_t *base_src = *be.base_src;
+    for (int r = 0; r < kBlockHeight; ++r) {
+      for (int c = 0; c < kBlockWidth; ++c) {
+        base_src[c] = rnd.Rand8();
+      }
+      base_src += be.src_stride;
+    }
+
+    // set predictor
+    uint8_t *predictor = bd.predictor;
+    for (int r = 0; r < kBlockHeight; ++r) {
+      for (int c = 0; c < kBlockWidth; ++c) {
+        predictor[c] = rnd.Rand8();
+      }
+      predictor += kDiffPredStride;
+    }
+
+    GetParam()(&be, &bd, kDiffPredStride);
+
+    base_src = *be.base_src;
+    src_diff = be.src_diff;
+    predictor = bd.predictor;
+    for (int r = 0; r < kBlockHeight; ++r) {
+      for (int c = 0; c < kBlockWidth; ++c) {
+        EXPECT_EQ(base_src[c], (src_diff[c] + predictor[c])) << "r = " << r
+                                                             << ", c = " << c;
+      }
+      src_diff += kDiffPredStride;
+      predictor += kDiffPredStride;
+      base_src += be.src_stride;
+    }
+  }
+  vpx_free(be.src_diff);
+  vpx_free(source);
+  vpx_free(bd.predictor);
+}
+
+INSTANTIATE_TEST_CASE_P(C, SubtractBlockTest,
+                        ::testing::Values(vp8_subtract_b_c));
+
+#if HAVE_MMX
+INSTANTIATE_TEST_CASE_P(MMX, SubtractBlockTest,
+                        ::testing::Values(vp8_subtract_b_mmx));
+#endif
+
+#if HAVE_SSE2
+INSTANTIATE_TEST_CASE_P(SSE2, SubtractBlockTest,
+                        ::testing::Values(vp8_subtract_b_sse2));
+#endif
+
+}  // namespace
diff --git a/test/test.mk b/test/test.mk
index bf49bd2..7e07320 100644
--- a/test/test.mk
+++ b/test/test.mk
@@ -10,6 +10,7 @@
 ##
 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
@@ -40,6 +41,7 @@
 LIBVPX_TEST_SRCS-yes                   += sad_test.cc
 LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += set_roi.cc
 LIBVPX_TEST_SRCS-yes                   += sixtap_predict_test.cc
+LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += subtract_test.cc
 
 endif
 
diff --git a/vp8/common/rtcd.c b/vp8/common/rtcd.c
index 3150fff..01dad46 100644
--- a/vp8/common/rtcd.c
+++ b/vp8/common/rtcd.c
@@ -13,17 +13,43 @@
 
 #if CONFIG_MULTITHREAD && defined(_WIN32)
 #include <windows.h>
+#include <stdlib.h>
 static void once(void (*func)(void))
 {
-    /* Using a static initializer here rather than InitializeCriticalSection()
-     * since there's no race-free context in which to execute it. Protecting
-     * it with an atomic op like InterlockedCompareExchangePointer introduces
-     * an x86 dependency, and InitOnceExecuteOnce requires Vista.
-     */
-    static CRITICAL_SECTION lock = {(void *)-1, -1, 0, 0, 0, 0};
+    static CRITICAL_SECTION *lock;
+    static LONG waiters;
     static int done;
+    void *lock_ptr = &lock;
 
-    EnterCriticalSection(&lock);
+    /* If the initialization is complete, return early. This isn't just an
+     * optimization, it prevents races on the destruction of the global
+     * lock.
+     */
+    if(done)
+        return;
+
+    InterlockedIncrement(&waiters);
+
+    /* Get a lock. We create one and try to make it the one-true-lock,
+     * throwing it away if we lost the race.
+     */
+
+    {
+        /* Scope to protect access to new_lock */
+        CRITICAL_SECTION *new_lock = malloc(sizeof(CRITICAL_SECTION));
+        InitializeCriticalSection(new_lock);
+        if (InterlockedCompareExchangePointer(lock_ptr, new_lock, NULL) != NULL)
+        {
+            DeleteCriticalSection(new_lock);
+            free(new_lock);
+        }
+    }
+
+    /* At this point, we have a lock that can be synchronized on. We don't
+     * care which thread actually performed the allocation.
+     */
+
+    EnterCriticalSection(lock);
 
     if (!done)
     {
@@ -31,7 +57,17 @@
         done = 1;
     }
 
-    LeaveCriticalSection(&lock);
+    LeaveCriticalSection(lock);
+
+    /* Last one out should free resources. The destructed objects are
+     * protected by checking if(done) above.
+     */
+    if(!InterlockedDecrement(&waiters))
+    {
+        DeleteCriticalSection(lock);
+        free(lock);
+        lock = NULL;
+    }
 }
 
 
diff --git a/vp8/encoder/onyx_if.c b/vp8/encoder/onyx_if.c
index b4e02e2..3ed36d3 100644
--- a/vp8/encoder/onyx_if.c
+++ b/vp8/encoder/onyx_if.c
@@ -3970,18 +3970,12 @@
         /* Test to see if the stats generated for this frame indicate that
          * we should have coded a key frame (assuming that we didn't)!
          */
-        if (cpi->pass != 2 && cpi->oxcf.auto_key && cm->frame_type != KEY_FRAME)
-        {
-            int key_frame_decision = decide_key_frame(cpi);
 
-            if (cpi->compressor_speed == 2)
-            {
-                /* we don't do re-encoding in realtime mode
-                 * if key frame is decided then we force it on next frame */
-                cpi->force_next_frame_intra = key_frame_decision;
-            }
+        if (cpi->pass != 2 && cpi->oxcf.auto_key && cm->frame_type != KEY_FRAME
+            && cpi->compressor_speed != 2)
+        {
 #if !(CONFIG_REALTIME_ONLY)
-            else if (key_frame_decision)
+            if (decide_key_frame(cpi))
             {
                 /* Reset all our sizing numbers and recode */
                 cm->frame_type = KEY_FRAME;
diff --git a/vp8/encoder/pickinter.c b/vp8/encoder/pickinter.c
index 3cad8bf..6050d15 100644
--- a/vp8/encoder/pickinter.c
+++ b/vp8/encoder/pickinter.c
@@ -865,7 +865,8 @@
             if (cpi->oxcf.mr_encoder_id && !cpi->mr_low_res_mv_avail)
                 cpi->sf.improved_mv_pred = 0;
 
-            if (cpi->oxcf.mr_encoder_id && cpi->mr_low_res_mv_avail)
+            if (cpi->oxcf.mr_encoder_id && cpi->mr_low_res_mv_avail
+                && parent_ref_frame)
             {
                 /* Use parent MV as predictor. Adjust search range
                  * accordingly.
@@ -911,6 +912,7 @@
 #if CONFIG_MULTI_RES_ENCODING
             if (cpi->oxcf.mr_encoder_id && cpi->mr_low_res_mv_avail &&
                 dissim <= 2 &&
+                parent_ref_frame &&
                 MAX(abs(best_ref_mv.as_mv.row - parent_ref_mv.as_mv.row),
                     abs(best_ref_mv.as_mv.col - parent_ref_mv.as_mv.col)) <= 4)
             {