(Auto)update libjingle 72682155-> 72785180

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6841 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/libjingle.gyp b/talk/libjingle.gyp
index 8ad4c11..f14854c 100755
--- a/talk/libjingle.gyp
+++ b/talk/libjingle.gyp
@@ -489,8 +489,6 @@
         'media/base/mediacommon.h',
         'media/base/mediaengine.cc',
         'media/base/mediaengine.h',
-        'media/base/mutedvideocapturer.cc',
-        'media/base/mutedvideocapturer.h',
         'media/base/rtpdataengine.cc',
         'media/base/rtpdataengine.h',
         'media/base/rtpdump.cc',
@@ -508,6 +506,7 @@
         'media/base/videocommon.h',
         'media/base/videoframe.cc',
         'media/base/videoframe.h',
+        'media/base/videoframefactory.h',
         'media/base/videoprocessor.h',
         'media/base/videorenderer.h',
         'media/base/voiceprocessor.h',
@@ -543,6 +542,8 @@
         'media/webrtc/webrtcvideoengine2.h',
         'media/webrtc/webrtcvideoframe.cc',
         'media/webrtc/webrtcvideoframe.h',
+        'media/webrtc/webrtcvideoframefactory.cc',
+        'media/webrtc/webrtcvideoframefactory.h',
         'media/webrtc/webrtcvie.h',
         'media/webrtc/webrtcvoe.h',
         'media/webrtc/webrtcvoiceengine.cc',
diff --git a/talk/libjingle_tests.gyp b/talk/libjingle_tests.gyp
index 9a00fed..76f22ea 100755
--- a/talk/libjingle_tests.gyp
+++ b/talk/libjingle_tests.gyp
@@ -148,6 +148,7 @@
         'media/base/streamparams_unittest.cc',
         'media/base/testutils.cc',
         'media/base/testutils.h',
+        'media/base/videoadapter_unittest.cc',
         'media/base/videocapturer_unittest.cc',
         'media/base/videocommon_unittest.cc',
         'media/base/videoengine_unittest.h',
diff --git a/talk/media/base/fakevideocapturer.h b/talk/media/base/fakevideocapturer.h
index 089151f..b61ac5d 100644
--- a/talk/media/base/fakevideocapturer.h
+++ b/talk/media/base/fakevideocapturer.h
@@ -36,6 +36,9 @@
 #include "talk/media/base/videocapturer.h"
 #include "talk/media/base/videocommon.h"
 #include "talk/media/base/videoframe.h"
+#ifdef HAVE_WEBRTC_VIDEO
+#include "talk/media/webrtc/webrtcvideoframefactory.h"
+#endif
 
 namespace cricket {
 
@@ -47,6 +50,9 @@
         initial_unix_timestamp_(time(NULL) * rtc::kNumNanosecsPerSec),
         next_timestamp_(rtc::kNumNanosecsPerMillisec),
         is_screencast_(false) {
+#ifdef HAVE_WEBRTC_VIDEO
+    set_frame_factory(new cricket::WebRtcVideoFrameFactory());
+#endif
     // Default supported formats. Use ResetSupportedFormats to over write.
     std::vector<cricket::VideoFormat> formats;
     formats.push_back(cricket::VideoFormat(1280, 720,
diff --git a/talk/media/base/mutedvideocapturer.cc b/talk/media/base/mutedvideocapturer.cc
index 6ff60a0..f122c30 100644
--- a/talk/media/base/mutedvideocapturer.cc
+++ b/talk/media/base/mutedvideocapturer.cc
@@ -1,135 +1,2 @@
-/*
- * libjingle
- * Copyright 2012 Google Inc.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *  3. The name of the author may not be used to endorse or promote products
- *     derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "webrtc/base/logging.h"
-#include "webrtc/base/thread.h"
-#include "talk/media/base/mutedvideocapturer.h"
-#include "talk/media/base/videoframe.h"
-
-#if defined(HAVE_WEBRTC_VIDEO)
-#include "talk/media/webrtc/webrtcvideoframe.h"
-#endif  // HAVE_WEBRTC_VIDEO
-
-
-namespace cricket {
-
-const char MutedVideoCapturer::kCapturerId[] = "muted_camera";
-
-class MutedFramesGenerator : public rtc::MessageHandler {
- public:
-  explicit MutedFramesGenerator(const VideoFormat& format);
-  virtual ~MutedFramesGenerator();
-
-  // Called every |interval| ms. From |format|.interval given in the
-  // constructor.
-  sigslot::signal1<VideoFrame*> SignalFrame;
-
- protected:
-  virtual void OnMessage(rtc::Message* message);
-
- private:
-  rtc::Thread capture_thread_;
-  rtc::scoped_ptr<VideoFrame> muted_frame_;
-  const VideoFormat format_;
-  const int interval_;
-  uint32 create_time_;
-};
-
-MutedFramesGenerator::MutedFramesGenerator(const VideoFormat& format)
-    : format_(format),
-      interval_(static_cast<int>(format.interval /
-                                 rtc::kNumNanosecsPerMillisec)),
-      create_time_(rtc::Time()) {
-  capture_thread_.Start();
-  capture_thread_.PostDelayed(interval_, this);
-}
-
-MutedFramesGenerator::~MutedFramesGenerator() { capture_thread_.Clear(this); }
-
-void MutedFramesGenerator::OnMessage(rtc::Message* message) {
-  // Queue a new frame as soon as possible to minimize drift.
-  capture_thread_.PostDelayed(interval_, this);
-  if (!muted_frame_) {
-#if defined(HAVE_WEBRTC_VIDEO)
-#define VIDEO_FRAME_NAME WebRtcVideoFrame
-#endif
-#if defined(VIDEO_FRAME_NAME)
-    muted_frame_.reset(new VIDEO_FRAME_NAME());
-#else
-    return;
-#endif
-  }
-  uint32 current_timestamp = rtc::Time();
-  // Delta between create time and current time will be correct even if there is
-  // a wraparound since they are unsigned integers.
-  uint32 elapsed_time = current_timestamp - create_time_;
-  if (!muted_frame_->InitToBlack(format_.width, format_.height, 1, 1,
-                                 elapsed_time, current_timestamp)) {
-    LOG(LS_ERROR) << "Failed to create a black frame.";
-  }
-  SignalFrame(muted_frame_.get());
-}
-
-MutedVideoCapturer::MutedVideoCapturer() { SetId(kCapturerId); }
-
-MutedVideoCapturer::~MutedVideoCapturer() { Stop(); }
-
-bool MutedVideoCapturer::GetBestCaptureFormat(const VideoFormat& desired,
-                                              VideoFormat* best_format) {
-  *best_format = desired;
-  return true;
-}
-
-CaptureState MutedVideoCapturer::Start(const VideoFormat& capture_format) {
-  if (frame_generator_.get()) {
-    return CS_RUNNING;
-  }
-  frame_generator_.reset(new MutedFramesGenerator(capture_format));
-  frame_generator_->SignalFrame
-      .connect(this, &MutedVideoCapturer::OnMutedFrame);
-  SetCaptureFormat(&capture_format);
-  return CS_RUNNING;
-}
-
-void MutedVideoCapturer::Stop() {
-  frame_generator_.reset();
-  SetCaptureFormat(NULL);
-}
-
-bool MutedVideoCapturer::IsRunning() { return frame_generator_.get() != NULL; }
-
-bool MutedVideoCapturer::GetPreferredFourccs(std::vector<uint32>* fourccs) {
-  fourccs->clear();
-  fourccs->push_back(cricket::FOURCC_I420);
-  return true;
-}
-
-void MutedVideoCapturer::OnMutedFrame(VideoFrame* muted_frame) {
-  SignalVideoFrame(this, muted_frame);
-}
-
-}  // namespace cricket
+// TODO(pthatcher): Delete this file.  Pulse won't work without it for
+// some reason.
diff --git a/talk/media/base/mutedvideocapturer_unittest.cc b/talk/media/base/mutedvideocapturer_unittest.cc
deleted file mode 100755
index 739874f..0000000
--- a/talk/media/base/mutedvideocapturer_unittest.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * libjingle
- * Copyright 2012 Google Inc.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *  3. The name of the author may not be used to endorse or promote products
- *     derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "talk/media/base/mutedvideocapturer.h"
-
-#include "webrtc/base/gunit.h"
-#include "talk/media/base/videoframe.h"
-
-class MutedVideoCapturerTest : public sigslot::has_slots<>,
-                               public testing::Test {
- protected:
-  void SetUp() {
-    frames_received_ = 0;
-    capturer_.SignalVideoFrame
-        .connect(this, &MutedVideoCapturerTest::OnVideoFrame);
-  }
-  void OnVideoFrame(cricket::VideoCapturer* capturer,
-                    const cricket::VideoFrame* muted_frame) {
-    EXPECT_EQ(capturer, &capturer_);
-    ++frames_received_;
-    received_width_ = muted_frame->GetWidth();
-    received_height_ = muted_frame->GetHeight();
-  }
-  int frames_received() { return frames_received_; }
-  bool ReceivedCorrectFormat() {
-    return (received_width_ == capturer_.GetCaptureFormat()->width) &&
-           (received_height_ == capturer_.GetCaptureFormat()->height);
-  }
-
-  cricket::MutedVideoCapturer capturer_;
-  int frames_received_;
-  cricket::VideoFormat capture_format_;
-  int received_width_;
-  int received_height_;
-};
-
-TEST_F(MutedVideoCapturerTest, GetBestCaptureFormat) {
-  cricket::VideoFormat format(640, 360, cricket::VideoFormat::FpsToInterval(30),
-                              cricket::FOURCC_I420);
-  cricket::VideoFormat best_format;
-  EXPECT_TRUE(capturer_.GetBestCaptureFormat(format, &best_format));
-  EXPECT_EQ(format.width, best_format.width);
-  EXPECT_EQ(format.height, best_format.height);
-  EXPECT_EQ(format.interval, best_format.interval);
-  EXPECT_EQ(format.fourcc, best_format.fourcc);
-}
-
-TEST_F(MutedVideoCapturerTest, IsScreencast) {
-  EXPECT_FALSE(capturer_.IsScreencast());
-}
-
-TEST_F(MutedVideoCapturerTest, GetPreferredFourccs) {
-  std::vector<uint32> fourccs;
-  EXPECT_TRUE(capturer_.GetPreferredFourccs(&fourccs));
-  EXPECT_EQ(fourccs.size(), 1u);
-  EXPECT_TRUE(capturer_.GetPreferredFourccs(&fourccs));
-  EXPECT_EQ(fourccs.size(), 1u);
-  EXPECT_EQ(fourccs[0], cricket::FOURCC_I420);
-}
-
-TEST_F(MutedVideoCapturerTest, Capturing) {
-  cricket::VideoFormat format(640, 360, cricket::VideoFormat::FpsToInterval(30),
-                              cricket::FOURCC_I420);
-  EXPECT_EQ(capturer_.Start(format), cricket::CS_RUNNING);
-  EXPECT_EQ(capturer_.Start(format), cricket::CS_RUNNING);
-  EXPECT_TRUE(capturer_.IsRunning());
-  // 100 ms should be enough to receive 3 frames at FPS of 30.
-  EXPECT_EQ_WAIT(frames_received(), 1, 100);
-  EXPECT_TRUE(ReceivedCorrectFormat());
-  capturer_.Stop();
-  EXPECT_FALSE(capturer_.IsRunning());
-}
diff --git a/talk/media/base/videoadapter_unittest.cc b/talk/media/base/videoadapter_unittest.cc
new file mode 100755
index 0000000..df4125d
--- /dev/null
+++ b/talk/media/base/videoadapter_unittest.cc
@@ -0,0 +1,1285 @@
+/*
+ * libjingle
+ * Copyright 2010 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// If we don't have a WebRtcVideoFrame, just skip all of these tests.
+#if defined(HAVE_WEBRTC_VIDEO)
+#include <limits.h>  // For INT_MAX
+#include <string>
+#include <vector>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/sigslot.h"
+#include "talk/media/base/mediachannel.h"
+#include "talk/media/base/testutils.h"
+#include "talk/media/base/videoadapter.h"
+#include "talk/media/devices/filevideocapturer.h"
+#include "talk/media/webrtc/webrtcvideoframe.h"
+
+namespace cricket {
+
+namespace {
+  static const uint32 kWaitTimeout = 3000U;  // 3 seconds.
+  static const uint32 kShortWaitTimeout = 1000U;  // 1 second.
+  void UpdateCpuLoad(CoordinatedVideoAdapter* adapter,
+    int current_cpus, int max_cpus, float process_load, float system_load) {
+    adapter->set_cpu_load_min_samples(1);
+    adapter->OnCpuLoadUpdated(current_cpus, max_cpus,
+                              process_load, system_load);
+  }
+}
+
+class VideoAdapterTest : public testing::Test {
+ public:
+  virtual void SetUp() {
+    capturer_.reset(new FileVideoCapturer);
+    EXPECT_TRUE(capturer_->Init(GetTestFilePath(
+        "captured-320x240-2s-48.frames")));
+    capture_format_ = capturer_->GetSupportedFormats()->at(0);
+    capture_format_.interval = VideoFormat::FpsToInterval(50);
+    adapter_.reset(new VideoAdapter());
+    adapter_->SetInputFormat(capture_format_);
+
+    listener_.reset(new VideoCapturerListener(adapter_.get()));
+    capturer_->SignalFrameCaptured.connect(
+        listener_.get(), &VideoCapturerListener::OnFrameCaptured);
+  }
+
+  void VerifyAdaptedResolution(int width, int height) {
+    EXPECT_TRUE(NULL != listener_->adapted_frame());
+    EXPECT_EQ(static_cast<size_t>(width),
+              listener_->adapted_frame()->GetWidth());
+    EXPECT_EQ(static_cast<size_t>(height),
+              listener_->adapted_frame()->GetHeight());
+  }
+
+ protected:
+  class VideoCapturerListener: public sigslot::has_slots<> {
+   public:
+    explicit VideoCapturerListener(VideoAdapter* adapter)
+        : video_adapter_(adapter),
+          adapted_frame_(NULL),
+          copied_output_frame_(),
+          captured_frames_(0),
+          dropped_frames_(0),
+          last_adapt_was_no_op_(false) {
+    }
+
+    void OnFrameCaptured(VideoCapturer* capturer,
+                         const CapturedFrame* captured_frame) {
+      WebRtcVideoFrame temp_i420;
+      EXPECT_TRUE(temp_i420.Init(captured_frame,
+          captured_frame->width, abs(captured_frame->height)));
+      VideoFrame* out_frame = NULL;
+      EXPECT_TRUE(video_adapter_->AdaptFrame(&temp_i420, &out_frame));
+      if (out_frame) {
+        if (out_frame == &temp_i420) {
+          last_adapt_was_no_op_ = true;
+          copied_output_frame_.reset(temp_i420.Copy());
+          adapted_frame_ = copied_output_frame_.get();
+        } else {
+          last_adapt_was_no_op_ = false;
+          adapted_frame_ = out_frame;
+          copied_output_frame_.reset();
+        }
+      } else {
+        ++dropped_frames_;
+      }
+      ++captured_frames_;
+    }
+
+    const VideoFrame* adapted_frame() const { return adapted_frame_; }
+    int captured_frames() const { return captured_frames_; }
+    int dropped_frames() const { return dropped_frames_; }
+    bool last_adapt_was_no_op() const { return last_adapt_was_no_op_; }
+
+   private:
+    VideoAdapter* video_adapter_;
+    const VideoFrame* adapted_frame_;
+    rtc::scoped_ptr<VideoFrame> copied_output_frame_;
+    int captured_frames_;
+    int dropped_frames_;
+    bool last_adapt_was_no_op_;
+  };
+
+  class CpuAdapterListener: public sigslot::has_slots<> {
+   public:
+    CpuAdapterListener() : received_cpu_signal_(false) {}
+    void OnCpuAdaptationSignalled() { received_cpu_signal_ = true; }
+    bool received_cpu_signal() { return received_cpu_signal_; }
+   private:
+    bool received_cpu_signal_;
+  };
+
+  rtc::scoped_ptr<FileVideoCapturer> capturer_;
+  rtc::scoped_ptr<VideoAdapter> adapter_;
+  rtc::scoped_ptr<VideoCapturerListener> listener_;
+  VideoFormat capture_format_;
+};
+
+
+// Test adapter remembers exact pixel count
+TEST_F(VideoAdapterTest, AdaptNumPixels) {
+  adapter_->SetOutputNumPixels(123456);
+  EXPECT_EQ(123456, adapter_->GetOutputNumPixels());
+}
+
+// Test adapter is constructed but not activated. Expect no frame drop and no
+// resolution change.
+TEST_F(VideoAdapterTest, AdaptInactive) {
+  // Output resolution is not set.
+  EXPECT_EQ(INT_MAX, adapter_->GetOutputNumPixels());
+
+  // Call Adapter with some frames.
+  EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_));
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 10, kWaitTimeout);
+
+  // Verify no frame drop and no resolution change.
+  EXPECT_GE(listener_->captured_frames(), 10);
+  EXPECT_EQ(0, listener_->dropped_frames());
+  VerifyAdaptedResolution(capture_format_.width, capture_format_.height);
+}
+
+// Do not adapt the frame rate or the resolution. Expect no frame drop and no
+// resolution change.
+TEST_F(VideoAdapterTest, AdaptNothing) {
+  adapter_->SetOutputFormat(capture_format_);
+  EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_));
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 10, kWaitTimeout);
+
+  // Verify no frame drop and no resolution change.
+  EXPECT_GE(listener_->captured_frames(), 10);
+  EXPECT_EQ(0, listener_->dropped_frames());
+  VerifyAdaptedResolution(capture_format_.width, capture_format_.height);
+  EXPECT_TRUE(listener_->last_adapt_was_no_op());
+}
+
+TEST_F(VideoAdapterTest, AdaptZeroInterval) {
+  VideoFormat format = capturer_->GetSupportedFormats()->at(0);
+  format.interval = 0;
+  adapter_->SetInputFormat(format);
+  adapter_->SetOutputFormat(format);
+  EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_));
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 10, kWaitTimeout);
+
+  // Verify no crash and that frames aren't dropped.
+  EXPECT_GE(listener_->captured_frames(), 10);
+  EXPECT_EQ(0, listener_->dropped_frames());
+  VerifyAdaptedResolution(capture_format_.width, capture_format_.height);
+}
+
+// Adapt the frame rate to be half of the capture rate at the beginning. Expect
+// the number of dropped frames to be half of the number the captured frames.
+TEST_F(VideoAdapterTest, AdaptFramerate) {
+  VideoFormat request_format = capture_format_;
+  request_format.interval *= 2;
+  adapter_->SetOutputFormat(request_format);
+  EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_));
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 10, kWaitTimeout);
+
+  // Verify frame drop and no resolution change.
+  EXPECT_GE(listener_->captured_frames(), 10);
+  EXPECT_EQ(listener_->captured_frames() / 2, listener_->dropped_frames());
+  VerifyAdaptedResolution(capture_format_.width, capture_format_.height);
+}
+
+// Adapt the frame rate to be half of the capture rate at the beginning. Expect
+// the number of dropped frames to be half of the number the captured frames.
+TEST_F(VideoAdapterTest, AdaptFramerateVariable) {
+  VideoFormat request_format = capture_format_;
+  request_format.interval = request_format.interval * 3 / 2;
+  adapter_->SetOutputFormat(request_format);
+  EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_));
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 30, kWaitTimeout);
+
+  // Verify frame drop and no resolution change.
+  EXPECT_GE(listener_->captured_frames(), 30);
+  // Verify 2 / 3 kept (20) and 1 / 3 dropped (10).
+  EXPECT_EQ(listener_->captured_frames() * 1 / 3, listener_->dropped_frames());
+  VerifyAdaptedResolution(capture_format_.width, capture_format_.height);
+}
+
+// Adapt the frame rate to be half of the capture rate after capturing no less
+// than 10 frames. Expect no frame dropped before adaptation and frame dropped
+// after adaptation.
+TEST_F(VideoAdapterTest, AdaptFramerateOntheFly) {
+  VideoFormat request_format = capture_format_;
+  adapter_->SetOutputFormat(request_format);
+  EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_));
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 10, kWaitTimeout);
+
+  // Verify no frame drop before adaptation.
+  EXPECT_EQ(0, listener_->dropped_frames());
+
+  // Adapat the frame rate.
+  request_format.interval *= 2;
+  adapter_->SetOutputFormat(request_format);
+
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 20, kWaitTimeout);
+
+  // Verify frame drop after adaptation.
+  EXPECT_GT(listener_->dropped_frames(), 0);
+}
+
+// Adapt the frame resolution to be a quarter of the capture resolution at the
+// beginning. Expect resolution change.
+TEST_F(VideoAdapterTest, AdaptResolution) {
+  VideoFormat request_format = capture_format_;
+  request_format.width /= 2;
+  request_format.height /= 2;
+  adapter_->SetOutputFormat(request_format);
+  EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_));
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 10, kWaitTimeout);
+
+  // Verify no frame drop and resolution change.
+  EXPECT_EQ(0, listener_->dropped_frames());
+  VerifyAdaptedResolution(request_format.width, request_format.height);
+}
+
+// Adapt the frame resolution to half width. Expect resolution change.
+TEST_F(VideoAdapterTest, AdaptResolutionNarrow) {
+  VideoFormat request_format = capture_format_;
+  request_format.width /= 2;
+  adapter_->set_scale_third(true);
+  adapter_->SetOutputFormat(request_format);
+  EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_));
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 10, kWaitTimeout);
+
+  // Verify resolution change.
+  VerifyAdaptedResolution(213, 160);
+}
+
+// Adapt the frame resolution to half height. Expect resolution change.
+TEST_F(VideoAdapterTest, AdaptResolutionWide) {
+  VideoFormat request_format = capture_format_;
+  request_format.height /= 2;
+  adapter_->set_scale_third(true);
+  adapter_->SetOutputFormat(request_format);
+  EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_));
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 10, kWaitTimeout);
+
+  // Verify resolution change.
+  VerifyAdaptedResolution(213, 160);
+}
+
+// Adapt the frame resolution to be a quarter of the capture resolution after
+// capturing no less than 10 frames. Expect no resolution change before
+// adaptation and resolution change after adaptation.
+TEST_F(VideoAdapterTest, AdaptResolutionOnTheFly) {
+  VideoFormat request_format = capture_format_;
+  adapter_->SetOutputFormat(request_format);
+  EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_));
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 10, kWaitTimeout);
+
+  // Verify no resolution change before adaptation.
+  VerifyAdaptedResolution(request_format.width, request_format.height);
+
+  // Adapt the frame resolution.
+  request_format.width /= 2;
+  request_format.height /= 2;
+  adapter_->SetOutputFormat(request_format);
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 20, kWaitTimeout);
+
+
+  // Verify resolution change after adaptation.
+  VerifyAdaptedResolution(request_format.width, request_format.height);
+}
+
+// Black the output frame.
+TEST_F(VideoAdapterTest, BlackOutput) {
+  adapter_->SetOutputFormat(capture_format_);
+  EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_));
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 10, kWaitTimeout);
+  // Verify that the output frame is not black.
+  EXPECT_NE(16, *listener_->adapted_frame()->GetYPlane());
+  EXPECT_NE(128, *listener_->adapted_frame()->GetUPlane());
+  EXPECT_NE(128, *listener_->adapted_frame()->GetVPlane());
+
+  adapter_->SetBlackOutput(true);
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 20, kWaitTimeout);
+  // Verify that the output frame is black.
+  EXPECT_EQ(16, *listener_->adapted_frame()->GetYPlane());
+  EXPECT_EQ(128, *listener_->adapted_frame()->GetUPlane());
+  EXPECT_EQ(128, *listener_->adapted_frame()->GetVPlane());
+
+  // Verify that the elapsed time and timestamp of the black frame increase.
+  int64 elapsed_time = listener_->adapted_frame()->GetElapsedTime();
+  int64 timestamp = listener_->adapted_frame()->GetTimeStamp();
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 22, kWaitTimeout);
+  EXPECT_GT(listener_->adapted_frame()->GetElapsedTime(), elapsed_time);
+  EXPECT_GT(listener_->adapted_frame()->GetTimeStamp(), timestamp);
+
+  // Change the output size
+  VideoFormat request_format = capture_format_;
+  request_format.width /= 2;
+  request_format.height /= 2;
+  adapter_->SetOutputFormat(request_format);
+
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 40, kWaitTimeout);
+  // Verify resolution change after adaptation.
+  VerifyAdaptedResolution(request_format.width, request_format.height);
+  // Verify that the output frame is black.
+  EXPECT_EQ(16, *listener_->adapted_frame()->GetYPlane());
+  EXPECT_EQ(128, *listener_->adapted_frame()->GetUPlane());
+  EXPECT_EQ(128, *listener_->adapted_frame()->GetVPlane());
+}
+
+// Drop all frames.
+TEST_F(VideoAdapterTest, DropAllFrames) {
+  VideoFormat format;  // with resolution 0x0.
+  adapter_->SetOutputFormat(format);
+  EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_));
+  EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+                   listener_->captured_frames() >= 10, kWaitTimeout);
+
+  // Verify all frames are dropped.
+  EXPECT_GE(listener_->captured_frames(), 10);
+  EXPECT_EQ(listener_->captured_frames(), listener_->dropped_frames());
+}
+
+TEST(CoordinatedVideoAdapterTest, TestCoordinatedWithoutCpuAdaptation) {
+  CoordinatedVideoAdapter adapter;
+  adapter.set_cpu_adaptation(false);
+
+  VideoFormat format(640, 400, VideoFormat::FpsToInterval(30), FOURCC_I420);
+  adapter.SetInputFormat(format);
+  adapter.set_scale_third(true);
+  EXPECT_EQ(format, adapter.input_format());
+  EXPECT_TRUE(adapter.output_format().IsSize0x0());
+
+  // Server format request 640x400.
+  format.height = 400;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(400, adapter.output_format().height);
+
+  // Server format request 1280x720, higher than input. Adapt nothing.
+  format.width = 1280;
+  format.height = 720;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(400, adapter.output_format().height);
+
+  // Cpu load is high, but cpu adaptation is disabled. Adapt nothing.
+  adapter.OnCpuLoadUpdated(1, 1, 0.99f, 0.99f);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(400, adapter.output_format().height);
+
+  // Encoder resolution request: downgrade with different size. Adapt nothing.
+  adapter.OnEncoderResolutionRequest(320, 200,
+                                     CoordinatedVideoAdapter::DOWNGRADE);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(400, adapter.output_format().height);
+
+  // Encoder resolution request: downgrade.
+  adapter.OnEncoderResolutionRequest(640, 400,
+                                     CoordinatedVideoAdapter::DOWNGRADE);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // Encoder resolution request: downgrade. But GD off. Adapt nothing.
+  adapter.set_gd_adaptation(false);
+  adapter.OnEncoderResolutionRequest(480, 300,
+                                     CoordinatedVideoAdapter::DOWNGRADE);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+  adapter.set_gd_adaptation(true);
+
+  // Encoder resolution request: downgrade.
+  adapter.OnEncoderResolutionRequest(480, 300,
+                                     CoordinatedVideoAdapter::DOWNGRADE);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Encoder resolution request: keep. Adapt nothing.
+  adapter.OnEncoderResolutionRequest(320, 200,
+                                     CoordinatedVideoAdapter::KEEP);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Encoder resolution request: upgrade.
+  adapter.OnEncoderResolutionRequest(320, 200,
+                                     CoordinatedVideoAdapter::UPGRADE);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // Server format request 0x0.
+  format.width = 0;
+  format.height = 0;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_TRUE(adapter.output_format().IsSize0x0());
+
+  // Server format request 320x200.
+  format.width = 320;
+  format.height = 200;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Server format request 160x100. But view disabled. Adapt nothing.
+  adapter.set_view_adaptation(false);
+  format.width = 160;
+  format.height = 100;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+  adapter.set_view_adaptation(true);
+
+  // Enable View Switch. Expect adapt down.
+  adapter.set_view_switch(true);
+  format.width = 160;
+  format.height = 100;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(160, adapter.output_format().width);
+  EXPECT_EQ(100, adapter.output_format().height);
+
+  // Encoder resolution request: upgrade. Adapt nothing.
+  adapter.OnEncoderResolutionRequest(160, 100,
+                                     CoordinatedVideoAdapter::UPGRADE);
+  EXPECT_EQ(160, adapter.output_format().width);
+  EXPECT_EQ(100, adapter.output_format().height);
+
+  // Request View of 2 / 3. Expect adapt down.
+  adapter.set_view_switch(true);
+  format.width = (640 * 2 + 1) / 3;
+  format.height = (400 * 2 + 1) / 3;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ((640 * 2 + 1) / 3, adapter.output_format().width);
+  EXPECT_EQ((400 * 2 + 1) / 3, adapter.output_format().height);
+
+
+  // Request View of 3 / 8. Expect adapt down.
+  adapter.set_view_switch(true);
+  format.width = 640 * 3 / 8;
+  format.height = 400 * 3 / 8;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(640 * 3 / 8, adapter.output_format().width);
+  EXPECT_EQ(400 * 3 / 8, adapter.output_format().height);
+
+  // View Switch back up. Expect adapt.
+  format.width = 320;
+  format.height = 200;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  adapter.set_view_switch(false);
+
+  // Encoder resolution request: upgrade. Constrained by server request.
+  adapter.OnEncoderResolutionRequest(320, 200,
+                                     CoordinatedVideoAdapter::UPGRADE);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Server format request 480x300.
+  format.width = 480;
+  format.height = 300;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+}
+
+TEST(CoordinatedVideoAdapterTest, TestCoordinatedWithCpuAdaptation) {
+  CoordinatedVideoAdapter adapter;
+  adapter.set_cpu_adaptation(true);
+  EXPECT_FALSE(adapter.cpu_smoothing());
+  VideoFormat format(640, 400, VideoFormat::FpsToInterval(30), FOURCC_I420);
+  adapter.SetInputFormat(format);
+
+  // Server format request 640x400.
+  format.height = 400;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(400, adapter.output_format().height);
+
+  // Process load is medium, but system load is high. Downgrade.
+  UpdateCpuLoad(&adapter, 1, 1, 0.55f, 0.98f);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // CPU high, but cpu adaptation disabled. Adapt nothing.
+  adapter.set_cpu_adaptation(false);
+  adapter.OnCpuLoadUpdated(1, 1, 0.55f, 0.98f);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+  adapter.set_cpu_adaptation(true);
+
+  // System load is high, but time has not elaspsed. Adapt nothing.
+  adapter.set_cpu_load_min_samples(2);
+  adapter.OnCpuLoadUpdated(1, 1, 0.55f, 0.98f);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // Process load is medium, but system load is high. Downgrade.
+  UpdateCpuLoad(&adapter, 1, 1, 0.55f, 0.98f);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Test reason for adapting is CPU.
+  EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_CPU,
+            adapter.adapt_reason());
+
+  // Server format request 320x200. Same as CPU. Do nothing.
+  format.width = 320;
+  format.height = 200;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Test reason for adapting is CPU and VIEW.
+  EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_CPU +
+            CoordinatedVideoAdapter::ADAPTREASON_VIEW,
+            adapter.adapt_reason());
+
+  // Process load and system load are normal. Adapt nothing.
+  UpdateCpuLoad(&adapter, 1, 1, 0.5f, 0.8f);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Process load and system load are low, but view is still low. Adapt nothing.
+  UpdateCpuLoad(&adapter, 1, 1, 0.2f, 0.3f);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Test reason for adapting is VIEW.
+  EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_VIEW,
+            adapter.adapt_reason());
+
+  // Server format request 640x400. Cpu is still low.  Upgrade.
+  format.width = 640;
+  format.height = 400;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // Test reason for adapting is CPU.
+  EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_CPU,
+            adapter.adapt_reason());
+
+  // Encoder resolution request: downgrade.
+  adapter.OnEncoderResolutionRequest(480, 300,
+                                     CoordinatedVideoAdapter::DOWNGRADE);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Test reason for adapting is BANDWIDTH.
+  EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_BANDWIDTH,
+            adapter.adapt_reason());
+
+  // Process load and system load are low. Constrained by GD. Adapt nothing
+  adapter.OnCpuLoadUpdated(1, 1, 0.2f, 0.3f);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Encoder resolution request: upgrade.
+  adapter.OnEncoderResolutionRequest(320, 200,
+                                     CoordinatedVideoAdapter::UPGRADE);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // Encoder resolution request: upgrade. Constrained by CPU.
+  adapter.OnEncoderResolutionRequest(480, 300,
+                                     CoordinatedVideoAdapter::UPGRADE);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // Server format request 640x400. Constrained by CPU.
+  format.width = 640;
+  format.height = 400;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+}
+
+TEST(CoordinatedVideoAdapterTest, TestCoordinatedWithCpuRequest) {
+  CoordinatedVideoAdapter adapter;
+  adapter.set_cpu_adaptation(true);
+  EXPECT_FALSE(adapter.cpu_smoothing());
+  VideoFormat format(640, 400, VideoFormat::FpsToInterval(30), FOURCC_I420);
+  adapter.SetInputFormat(format);
+
+  // Server format request 640x400.
+  format.height = 400;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(400, adapter.output_format().height);
+
+  // CPU resolution request: downgrade.  Adapt down.
+  adapter.OnCpuResolutionRequest(CoordinatedVideoAdapter::DOWNGRADE);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // CPU resolution request: keep. Do nothing.
+  adapter.OnCpuResolutionRequest(CoordinatedVideoAdapter::KEEP);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // CPU resolution request: downgrade, but cpu adaptation disabled.
+  // Adapt nothing.
+  adapter.set_cpu_adaptation(false);
+  adapter.OnCpuResolutionRequest(CoordinatedVideoAdapter::DOWNGRADE);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // CPU resolution request: downgrade.  Adapt down.
+  adapter.set_cpu_adaptation(true);
+  adapter.OnCpuResolutionRequest(CoordinatedVideoAdapter::DOWNGRADE);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Test reason for adapting is CPU.
+  EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_CPU,
+            adapter.adapt_reason());
+
+  // CPU resolution request: downgrade, but already at minimum.  Do nothing.
+  adapter.OnCpuResolutionRequest(CoordinatedVideoAdapter::DOWNGRADE);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Server format request 320x200. Same as CPU. Do nothing.
+  format.width = 320;
+  format.height = 200;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Test reason for adapting is CPU and VIEW.
+  EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_CPU +
+            CoordinatedVideoAdapter::ADAPTREASON_VIEW,
+            adapter.adapt_reason());
+
+  // CPU resolution request: upgrade, but view request still low. Do nothing.
+  adapter.OnCpuResolutionRequest(CoordinatedVideoAdapter::UPGRADE);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Test reason for adapting is VIEW.
+  EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_VIEW,
+            adapter.adapt_reason());
+
+  // Server format request 640x400. Cpu is still low.  Upgrade.
+  format.width = 640;
+  format.height = 400;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // Test reason for adapting is CPU.
+  EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_CPU,
+            adapter.adapt_reason());
+
+  // Encoder resolution request: downgrade.
+  adapter.OnEncoderResolutionRequest(480, 300,
+                                     CoordinatedVideoAdapter::DOWNGRADE);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Test reason for adapting is BANDWIDTH.
+  EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_BANDWIDTH,
+            adapter.adapt_reason());
+
+  // Process load and system load are low. Constrained by GD. Adapt nothing
+  adapter.OnCpuLoadUpdated(1, 1, 0.2f, 0.3f);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Encoder resolution request: upgrade.
+  adapter.OnEncoderResolutionRequest(320, 200,
+                                     CoordinatedVideoAdapter::UPGRADE);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // Encoder resolution request: upgrade. Constrained by CPU.
+  adapter.OnEncoderResolutionRequest(480, 300,
+                                     CoordinatedVideoAdapter::UPGRADE);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // Server format request 640x400. Constrained by CPU.
+  format.width = 640;
+  format.height = 400;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+}
+
+TEST(CoordinatedVideoAdapterTest, TestViewRequestPlusCameraSwitch) {
+  CoordinatedVideoAdapter adapter;
+  adapter.set_view_switch(true);
+
+  // Start at HD.
+  VideoFormat format(1280, 720, VideoFormat::FpsToInterval(30), FOURCC_I420);
+  adapter.SetInputFormat(format);
+  EXPECT_EQ(format, adapter.input_format());
+  EXPECT_TRUE(adapter.output_format().IsSize0x0());
+
+  // View request for VGA.
+  format.width = 640;
+  format.height = 360;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(360, adapter.output_format().height);
+  EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_VIEW, adapter.adapt_reason());
+
+  // Now, the camera reopens at VGA.
+  // Both the frame and the output format should be 640x360.
+  WebRtcVideoFrame in_frame;
+  in_frame.InitToBlack(640, 360, 1, 1, 33, 33);
+  VideoFrame* out_frame;
+  adapter.AdaptFrame(&in_frame, &out_frame);
+  EXPECT_EQ(640u, out_frame->GetWidth());
+  EXPECT_EQ(360u, out_frame->GetHeight());
+  // At this point, the view is no longer adapted, since the input has resized
+  // small enough to fit the last view request.
+  EXPECT_EQ(0, adapter.adapt_reason());
+
+  // And another view request comes in for 640x360, which should have no
+  // real impact.
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(360, adapter.output_format().height);
+  EXPECT_EQ(0, adapter.adapt_reason());
+}
+
+TEST(CoordinatedVideoAdapterTest, TestVGAWidth) {
+  CoordinatedVideoAdapter adapter;
+  adapter.set_view_switch(true);
+
+  // Start at 640x480, for cameras that don't support 640x360.
+  VideoFormat format(640, 480, VideoFormat::FpsToInterval(30), FOURCC_I420);
+  adapter.SetInputFormat(format);
+  EXPECT_EQ(format, adapter.input_format());
+  EXPECT_TRUE(adapter.output_format().IsSize0x0());
+
+  // Output format is 640x360, though.
+  format.width = 640;
+  format.height = 360;
+  adapter.SetOutputFormat(format);
+
+  // And also a view request comes for 640x360.
+  adapter.OnOutputFormatRequest(format);
+  // At this point, we have to adapt down to something lower.
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(360, adapter.output_format().height);
+
+  // But if frames come in at 640x360, we shouldn't adapt them down.
+  // Fake a 640x360 frame.
+  WebRtcVideoFrame in_frame;
+  in_frame.InitToBlack(640, 360, 1, 1, 33, 33);
+  VideoFrame* out_frame;
+  adapter.AdaptFrame(&in_frame, &out_frame);
+
+  EXPECT_EQ(640u, out_frame->GetWidth());
+  EXPECT_EQ(360u, out_frame->GetHeight());
+
+  // Similarly, no-op adapt requests for other reasons shouldn't change
+  // adaptation state (before a previous bug, the previous EXPECTs would
+  // fail and the following would succeed, as the no-op CPU request would
+  // fix the adaptation state).
+  adapter.set_cpu_adaptation(true);
+  UpdateCpuLoad(&adapter, 1, 1, 0.7f, 0.7f);
+  adapter.AdaptFrame(&in_frame, &out_frame);
+
+  EXPECT_EQ(640u, out_frame->GetWidth());
+  EXPECT_EQ(360u, out_frame->GetHeight());
+}
+
+// When adapting resolution for CPU or GD, the quantity of pixels that the
+// request is based on is reduced to half or double, and then an actual
+// resolution is snapped to, rounding to the closest actual resolution.
+// This works well for some tolerance to 3/4, odd widths and aspect ratios
+// that dont exactly match, but is not best behavior for ViewRequests which
+// need to be be strictly respected to avoid going over the resolution budget
+// given to the codec - 854x480 total pixels.
+// ViewRequest must find a lower resolution.
+TEST(CoordinatedVideoAdapterTest, TestCoordinatedViewRequestDown) {
+  CoordinatedVideoAdapter adapter;
+  adapter.set_cpu_adaptation(false);
+
+  VideoFormat format(960, 540, VideoFormat::FpsToInterval(30), FOURCC_I420);
+  adapter.SetInputFormat(format);
+  adapter.set_scale_third(true);
+  EXPECT_EQ(format, adapter.input_format());
+  EXPECT_TRUE(adapter.output_format().IsSize0x0());
+
+  // Server format request 640x400. Expect HVGA.
+  format.width = 640;
+  format.height = 400;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(360, adapter.output_format().height);
+
+  // Test reason for adapting is VIEW.
+  EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_VIEW, adapter.adapt_reason());
+}
+
+// Test that we downgrade video for cpu up to two times.
+TEST(CoordinatedVideoAdapterTest, TestCpuDowngradeTimes) {
+  CoordinatedVideoAdapter adapter;
+  adapter.set_cpu_adaptation(true);
+  EXPECT_FALSE(adapter.cpu_smoothing());
+  VideoFormat format(640, 400, VideoFormat::FpsToInterval(30), FOURCC_I420);
+  adapter.SetInputFormat(format);
+
+  // Server format request 640x400.
+  format.height = 400;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(400, adapter.output_format().height);
+
+  // Process load and system load are low. Do not change the cpu desired format
+  // and do not adapt.
+  adapter.OnCpuLoadUpdated(1, 1, 0.2f, 0.3f);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(400, adapter.output_format().height);
+
+  // System load is high. Downgrade.
+  UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // System load is high. Downgrade again.
+  UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // System load is still high. Do not downgrade any more.
+  UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Process load and system load are low. Upgrade.
+  UpdateCpuLoad(&adapter, 1, 1, 0.2f, 0.3f);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // System load is high. Downgrade.
+  UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // System load is still high. Do not downgrade any more.
+  UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+}
+
+// Test that we respect CPU adapter threshold values.
+TEST(CoordinatedVideoAdapterTest, TestAdapterCpuThreshold) {
+  CoordinatedVideoAdapter adapter;
+  adapter.set_cpu_adaptation(true);
+  EXPECT_FALSE(adapter.cpu_smoothing());
+  VideoFormat format(640, 400, VideoFormat::FpsToInterval(30), FOURCC_I420);
+  adapter.SetInputFormat(format);
+
+  // Server format request 640x400.
+  format.height = 400;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(400, adapter.output_format().height);
+
+  // Process load and system load are low. Do not change the cpu desired format
+  // and do not adapt.
+  adapter.OnCpuLoadUpdated(1, 1, 0.2f, 0.3f);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(400, adapter.output_format().height);
+
+  // System load is high. Downgrade.
+  UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // Test reason for adapting is CPU.
+  EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_CPU, adapter.adapt_reason());
+
+  // System load is high. Normally downgrade but threshold is high. Do nothing.
+  adapter.set_high_system_threshold(0.98f);  // Set threshold high.
+  UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // System load is medium. Normally do nothing, threshold is low. Adapt down.
+  adapter.set_high_system_threshold(0.75f);  // Set threshold low.
+  UpdateCpuLoad(&adapter, 1, 1, 0.8f, 0.8f);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+}
+
+
+// Test that for an upgrade cpu request, we actually upgrade the desired format;
+// for a downgrade request, we downgrade from the output format.
+TEST(CoordinatedVideoAdapterTest, TestRealCpuUpgrade) {
+  CoordinatedVideoAdapter adapter;
+  adapter.set_cpu_adaptation(true);
+  adapter.set_cpu_smoothing(true);
+  VideoFormat format(640, 400, VideoFormat::FpsToInterval(30), FOURCC_I420);
+  adapter.SetInputFormat(format);
+
+  // Server format request 640x400.
+  format.width = 640;
+  format.height = 400;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(400, adapter.output_format().height);
+
+  // Process load and system load are low. Do not change the cpu desired format
+  // and do not adapt.
+  UpdateCpuLoad(&adapter, 1, 1, 0.2f, 0.3f);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(400, adapter.output_format().height);
+
+  // Server format request 320x200.
+  format.width = 320;
+  format.height = 200;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Process load and system load are low. Do not change the cpu desired format
+  // and do not adapt.
+  UpdateCpuLoad(&adapter, 1, 1, 0.2f, 0.3f);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Server format request 640x400. Set to 640x400 immediately.
+  format.width = 640;
+  format.height = 400;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(400, adapter.output_format().height);
+
+  // Server format request 320x200.
+  format.width = 320;
+  format.height = 200;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Process load is high, but system is not. Do not change the cpu desired
+  // format and do not adapt.
+  for (size_t i = 0; i < 10; ++i) {
+    UpdateCpuLoad(&adapter, 1, 1, 0.75f, 0.8f);
+  }
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+}
+
+// Test that for an upgrade encoder request, we actually upgrade the desired
+//  format; for a downgrade request, we downgrade from the output format.
+TEST(CoordinatedVideoAdapterTest, TestRealEncoderUpgrade) {
+  CoordinatedVideoAdapter adapter;
+  adapter.set_cpu_adaptation(true);
+  adapter.set_cpu_smoothing(true);
+  VideoFormat format(640, 400, VideoFormat::FpsToInterval(30), FOURCC_I420);
+  adapter.SetInputFormat(format);
+
+  // Server format request 640x400.
+  format.width = 640;
+  format.height = 400;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(400, adapter.output_format().height);
+
+  // Encoder resolution request. Do not change the encoder desired format and
+  // do not adapt.
+  adapter.OnEncoderResolutionRequest(640, 400,
+                                     CoordinatedVideoAdapter::UPGRADE);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(400, adapter.output_format().height);
+
+  // Server format request 320x200.
+  format.width = 320;
+  format.height = 200;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Encoder resolution request. Do not change the encoder desired format and
+  // do not adapt.
+  adapter.OnEncoderResolutionRequest(320, 200,
+                                     CoordinatedVideoAdapter::UPGRADE);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Server format request 640x400. Set to 640x400 immediately.
+  format.width = 640;
+  format.height = 400;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(300, adapter.output_format().height);
+
+  // Test reason for adapting is BANDWIDTH.
+  EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_BANDWIDTH,
+            adapter.adapt_reason());
+
+  // Server format request 320x200.
+  format.width = 320;
+  format.height = 200;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(200, adapter.output_format().height);
+
+  // Encoder resolution request. Downgrade from 320x200.
+  adapter.OnEncoderResolutionRequest(320, 200,
+                                     CoordinatedVideoAdapter::DOWNGRADE);
+  EXPECT_EQ(240, adapter.output_format().width);
+  EXPECT_EQ(150, adapter.output_format().height);
+}
+
+TEST(CoordinatedVideoAdapterTest, TestNormalizeOutputFormat) {
+  CoordinatedVideoAdapter adapter;
+  // The input format is 640x360 and the output is limited to 16:9.
+  VideoFormat format(640, 360, VideoFormat::FpsToInterval(30), FOURCC_I420);
+  adapter.SetInputFormat(format);
+
+  format.width = 320;
+  format.height = 180;
+  format.interval = VideoFormat::FpsToInterval(15);
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(180, adapter.output_format().height);
+  EXPECT_EQ(VideoFormat::FpsToInterval(15), adapter.output_format().interval);
+
+  format.width = 320;
+  format.height = 200;
+  format.interval = VideoFormat::FpsToInterval(40);
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(180, adapter.output_format().height);
+  EXPECT_EQ(VideoFormat::FpsToInterval(30), adapter.output_format().interval);
+
+  // Test reason for adapting is VIEW. Should work even with normalization.
+  EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_VIEW,
+            adapter.adapt_reason());
+
+  format.width = 320;
+  format.height = 240;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(180, adapter.output_format().height);
+
+  // The input format is 640x480 and the output will be 4:3.
+  format.width = 640;
+  format.height = 480;
+  adapter.SetInputFormat(format);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(240, adapter.output_format().height);
+
+  format.width = 320;
+  format.height = 240;
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(240, adapter.output_format().height);
+
+  // The input format is initialized after the output. At that time, the output
+  // height is adjusted.
+  format.width = 0;
+  format.height = 0;
+  adapter.SetInputFormat(format);
+
+  format.width = 320;
+  format.height = 240;
+  format.interval = VideoFormat::FpsToInterval(30);
+  adapter.OnOutputFormatRequest(format);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(240, adapter.output_format().height);
+  EXPECT_EQ(VideoFormat::FpsToInterval(30), adapter.output_format().interval);
+
+  format.width = 640;
+  format.height = 480;
+  format.interval = VideoFormat::FpsToInterval(15);
+  adapter.SetInputFormat(format);
+  EXPECT_EQ(320, adapter.output_format().width);
+  EXPECT_EQ(240, adapter.output_format().height);
+  EXPECT_EQ(VideoFormat::FpsToInterval(15), adapter.output_format().interval);
+}
+
+// Test that we downgrade video for cpu up to two times.
+TEST_F(VideoAdapterTest, CpuDowngradeAndSignal) {
+  CoordinatedVideoAdapter adapter;
+  CpuAdapterListener cpu_listener;
+  adapter.SignalCpuAdaptationUnable.connect(
+      &cpu_listener, &CpuAdapterListener::OnCpuAdaptationSignalled);
+
+  adapter.set_cpu_adaptation(true);
+  EXPECT_FALSE(adapter.cpu_smoothing());
+  VideoFormat format(640, 360, VideoFormat::FpsToInterval(30), FOURCC_I420);
+  adapter.SetInputFormat(format);
+  adapter.OnOutputFormatRequest(format);
+
+  // System load is high. Downgrade.
+  UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f);
+
+  // System load is high. Downgrade again.
+  UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f);
+
+  // System load is still high. Do not downgrade any more. Ensure we have not
+  // signalled until after the cpu warning though.
+  EXPECT_TRUE(!cpu_listener.received_cpu_signal());
+  UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f);
+  EXPECT_TRUE_WAIT(cpu_listener.received_cpu_signal(), kWaitTimeout);
+}
+
+// Test that we downgrade video for cpu up to two times.
+TEST_F(VideoAdapterTest, CpuDowngradeAndDontSignal) {
+  CoordinatedVideoAdapter adapter;
+  CpuAdapterListener cpu_listener;
+  adapter.SignalCpuAdaptationUnable.connect(
+      &cpu_listener, &CpuAdapterListener::OnCpuAdaptationSignalled);
+
+  adapter.set_cpu_adaptation(true);
+  adapter.set_cpu_smoothing(true);
+  VideoFormat format(640, 360, VideoFormat::FpsToInterval(30), FOURCC_I420);
+  adapter.SetInputFormat(format);
+  adapter.OnOutputFormatRequest(format);
+
+  // System load is high. Downgrade.
+  UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f);
+
+  // System load is high, process is not, Do not downgrade again.
+  UpdateCpuLoad(&adapter, 1, 1, 0.25f, 0.95f);
+
+  // System load is high, process is not, Do not downgrade again and do not
+  // signal.
+  adapter.set_cpu_adaptation(false);
+  UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f);
+  rtc::Thread::Current()->ProcessMessages(kShortWaitTimeout);
+  EXPECT_TRUE(!cpu_listener.received_cpu_signal());
+  adapter.set_cpu_adaptation(true);
+}
+
+// Test that we require enough time before we downgrade.
+TEST_F(VideoAdapterTest, CpuMinTimeRequirement) {
+  CoordinatedVideoAdapter adapter;
+  CpuAdapterListener cpu_listener;
+  adapter.SignalCpuAdaptationUnable.connect(
+      &cpu_listener, &CpuAdapterListener::OnCpuAdaptationSignalled);
+
+  adapter.set_cpu_adaptation(true);
+  adapter.set_cpu_smoothing(true);
+  VideoFormat format(640, 360, VideoFormat::FpsToInterval(30), FOURCC_I420);
+  adapter.SetInputFormat(format);
+  adapter.OnOutputFormatRequest(format);
+
+  EXPECT_EQ(3, adapter.cpu_load_min_samples());
+  adapter.set_cpu_load_min_samples(5);
+
+  for (size_t i = 0; i < 4; ++i) {
+    adapter.OnCpuLoadUpdated(1, 1, 1.0f, 1.0f);
+    EXPECT_EQ(640, adapter.output_format().width);
+    EXPECT_EQ(360, adapter.output_format().height);
+  }
+  // The computed cpu load should now be around 93.5%, with the coefficient of
+  // 0.4 and a seed value of 0.5. That should be high enough to adapt, but it
+  // isn't enough samples, so we shouldn't have adapted on any of the previous
+  // samples.
+
+  // One more sample is enough, though, once enough time has passed.
+  adapter.OnCpuLoadUpdated(1, 1, 1.0f, 1.0f);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(270, adapter.output_format().height);
+
+  // Now the cpu is lower, but we still need enough samples to upgrade.
+  for (size_t i = 0; i < 4; ++i) {
+    adapter.OnCpuLoadUpdated(1, 1, 0.1f, 0.1f);
+    EXPECT_EQ(480, adapter.output_format().width);
+    EXPECT_EQ(270, adapter.output_format().height);
+  }
+
+  // One more sample is enough, once time has elapsed.
+  adapter.OnCpuLoadUpdated(1, 1, 1.0f, 1.0f);
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(360, adapter.output_format().height);
+}
+
+TEST_F(VideoAdapterTest, CpuIgnoresSpikes) {
+  CoordinatedVideoAdapter adapter;
+  CpuAdapterListener cpu_listener;
+  adapter.SignalCpuAdaptationUnable.connect(
+      &cpu_listener, &CpuAdapterListener::OnCpuAdaptationSignalled);
+
+  adapter.set_cpu_adaptation(true);
+  adapter.set_cpu_smoothing(true);
+  VideoFormat format(640, 360, VideoFormat::FpsToInterval(30), FOURCC_I420);
+  adapter.SetInputFormat(format);
+  adapter.OnOutputFormatRequest(format);
+
+  // System load is high. Downgrade.
+  for (size_t i = 0; i < 5; ++i) {
+    UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f);
+  }
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(270, adapter.output_format().height);
+
+  // Now we're in a state where we could upgrade or downgrade, so get to a
+  // steady state of about 75% cpu usage.
+  for (size_t i = 0; i < 5; ++i) {
+    UpdateCpuLoad(&adapter, 1, 1, 0.75f, 0.75f);
+    EXPECT_EQ(480, adapter.output_format().width);
+    EXPECT_EQ(270, adapter.output_format().height);
+  }
+
+  // Now, the cpu spikes for two samples, but then goes back to
+  // normal. This shouldn't cause adaptation.
+  UpdateCpuLoad(&adapter, 1, 1, 0.90f, 0.90f);
+  UpdateCpuLoad(&adapter, 1, 1, 0.90f, 0.90f);
+  EXPECT_EQ(480, adapter.output_format().width);
+  EXPECT_EQ(270, adapter.output_format().height);
+  // Back to the steady state for awhile.
+  for (size_t i = 0; i < 5; ++i) {
+    UpdateCpuLoad(&adapter, 1, 1, 0.75, 0.75);
+    EXPECT_EQ(480, adapter.output_format().width);
+    EXPECT_EQ(270, adapter.output_format().height);
+  }
+
+  // Now, system cpu usage is starting to drop down. But it takes a bit before
+  // it gets all the way there.
+  for (size_t i = 0; i < 10; ++i) {
+    UpdateCpuLoad(&adapter, 1, 1, 0.5f, 0.5f);
+  }
+  EXPECT_EQ(640, adapter.output_format().width);
+  EXPECT_EQ(360, adapter.output_format().height);
+}
+
+}  // namespace cricket
+#endif  // HAVE_WEBRTC_VIDEO
diff --git a/talk/media/base/videocapturer.cc b/talk/media/base/videocapturer.cc
index 139fd09..f20e6fb 100644
--- a/talk/media/base/videocapturer.cc
+++ b/talk/media/base/videocapturer.cc
@@ -35,13 +35,14 @@
 #include "webrtc/base/common.h"
 #include "webrtc/base/logging.h"
 #include "webrtc/base/systeminfo.h"
+#include "talk/media/base/videoframefactory.h"
 #include "talk/media/base/videoprocessor.h"
 
 #if defined(HAVE_WEBRTC_VIDEO)
 #include "talk/media/webrtc/webrtcvideoframe.h"
+#include "talk/media/webrtc/webrtcvideoframefactory.h"
 #endif  // HAVE_WEBRTC_VIDEO
 
-
 namespace cricket {
 
 namespace {
@@ -352,10 +353,6 @@
   if (SignalVideoFrame.is_empty()) {
     return;
   }
-#if defined(HAVE_WEBRTC_VIDEO)
-#define VIDEO_FRAME_NAME WebRtcVideoFrame
-#endif
-#if defined(VIDEO_FRAME_NAME)
 #if !defined(DISABLE_YUV)
   if (IsScreencast()) {
     int scaled_width, scaled_height;
@@ -501,8 +498,15 @@
                 &desired_width, &desired_height);
   }
 
-  VIDEO_FRAME_NAME i420_frame;
-  if (!i420_frame.Alias(captured_frame, desired_width, desired_height)) {
+  if (!frame_factory_) {
+    LOG(LS_ERROR) << "No video frame factory.";
+    return;
+  }
+
+  rtc::scoped_ptr<VideoFrame> i420_frame(
+      frame_factory_->CreateAliasedFrame(
+          captured_frame, desired_width, desired_height));
+  if (!i420_frame) {
     // TODO(fbarchard): LOG more information about captured frame attributes.
     LOG(LS_ERROR) << "Couldn't convert to I420! "
                   << "From " << ToString(captured_frame) << " To "
@@ -510,7 +514,7 @@
     return;
   }
 
-  VideoFrame* adapted_frame = &i420_frame;
+  VideoFrame* adapted_frame = i420_frame.get();
   if (enable_video_adapter_ && !IsScreencast()) {
     VideoFrame* out_frame = NULL;
     video_adapter_.AdaptFrame(adapted_frame, &out_frame);
@@ -528,13 +532,12 @@
     return;
   }
   if (muted_) {
+    // TODO(pthatcher): Use frame_factory_->CreateBlackFrame() instead.
     adapted_frame->SetToBlack();
   }
   SignalVideoFrame(this, adapted_frame);
 
   UpdateStats(captured_frame);
-
-#endif  // VIDEO_FRAME_NAME
 }
 
 void VideoCapturer::SetCaptureState(CaptureState state) {
diff --git a/talk/media/base/videocapturer.h b/talk/media/base/videocapturer.h
index d4192be..c60221a 100644
--- a/talk/media/base/videocapturer.h
+++ b/talk/media/base/videocapturer.h
@@ -42,6 +42,7 @@
 #include "talk/media/base/mediachannel.h"
 #include "talk/media/base/videoadapter.h"
 #include "talk/media/base/videocommon.h"
+#include "talk/media/base/videoframefactory.h"
 #include "talk/media/devices/devicemanager.h"
 
 
@@ -289,6 +290,11 @@
     return &video_adapter_;
   }
 
+  // Takes ownership.
+  void set_frame_factory(VideoFrameFactory* frame_factory) {
+    frame_factory_.reset(frame_factory);
+  }
+
   // Gets statistics for tracked variables recorded since the last call to
   // GetStats.  Note that calling GetStats resets any gathered data so it
   // should be called only periodically to log statistics.
@@ -326,6 +332,7 @@
   }
 
   void SetSupportedFormats(const std::vector<VideoFormat>& formats);
+  VideoFrameFactory* frame_factory() { return frame_factory_.get(); }
 
  private:
   void Construct();
@@ -361,6 +368,7 @@
   rtc::Thread* thread_;
   std::string id_;
   CaptureState capture_state_;
+  rtc::scoped_ptr<VideoFrameFactory> frame_factory_;
   rtc::scoped_ptr<VideoFormat> capture_format_;
   std::vector<VideoFormat> supported_formats_;
   rtc::scoped_ptr<VideoFormat> max_format_;
diff --git a/talk/media/base/videocapturer_unittest.cc b/talk/media/base/videocapturer_unittest.cc
index b70280f..37f6bbd 100644
--- a/talk/media/base/videocapturer_unittest.cc
+++ b/talk/media/base/videocapturer_unittest.cc
@@ -1,4 +1,29 @@
-// Copyright 2008 Google Inc.
+/*
+ * libjingle
+ * Copyright 2008 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
 
 #include <stdio.h>
 #include <vector>
@@ -13,15 +38,6 @@
 #include "talk/media/base/videocapturer.h"
 #include "talk/media/base/videoprocessor.h"
 
-// If HAS_I420_FRAME is not defined the video capturer will not be able to
-// provide OnVideoFrame-callbacks since they require cricket::CapturedFrame to
-// be decoded as a cricket::VideoFrame (i.e. an I420 frame). This functionality
-// only exist if HAS_I420_FRAME is defined below. I420 frames are also a
-// requirement for the VideoProcessors so they will not be called either.
-#if defined(HAVE_WEBRTC_VIDEO)
-#define HAS_I420_FRAME
-#endif
-
 using cricket::FakeVideoCapturer;
 
 namespace {
@@ -682,7 +698,12 @@
   EXPECT_EQ(360, best.height);
 }
 
-#if defined(HAS_I420_FRAME)
+// If HAVE_WEBRTC_VIDEO is not defined the video capturer will not be able to
+// provide OnVideoFrame-callbacks since they require cricket::CapturedFrame to
+// be decoded as a cricket::VideoFrame (i.e. an I420 frame). This functionality
+// only exist if HAVE_WEBRTC_VIDEO is defined below. I420 frames are also a
+// requirement for the VideoProcessors so they will not be called either.
+#if defined(HAVE_WEBRTC_VIDEO)
 TEST_F(VideoCapturerTest, VideoFrame) {
   EXPECT_EQ(cricket::CS_RUNNING, capturer_.Start(cricket::VideoFormat(
       640,
@@ -735,7 +756,7 @@
   EXPECT_TRUE(capturer_.CaptureFrame());
   EXPECT_EQ(0, video_frames_received());
 }
-#endif  // HAS_I420_FRAME
+#endif  // HAVE_WEBRTC_VIDEO
 
 bool HdFormatInList(const std::vector<cricket::VideoFormat>& formats) {
   for (std::vector<cricket::VideoFormat>::const_iterator found =
diff --git a/talk/media/base/videoengine_unittest.h b/talk/media/base/videoengine_unittest.h
index 25811ba..502cf47 100644
--- a/talk/media/base/videoengine_unittest.h
+++ b/talk/media/base/videoengine_unittest.h
@@ -503,14 +503,18 @@
     media_error_ = cricket::VideoMediaChannel::ERROR_NONE;
     channel_->SetRecvCodecs(engine_.codecs());
     EXPECT_TRUE(channel_->AddSendStream(DefaultSendStreamParams()));
-
-    video_capturer_.reset(new cricket::FakeVideoCapturer);
+    video_capturer_.reset(CreateFakeVideoCapturer());
     cricket::VideoFormat format(640, 480,
                                 cricket::VideoFormat::FpsToInterval(30),
                                 cricket::FOURCC_I420);
     EXPECT_EQ(cricket::CS_RUNNING, video_capturer_->Start(format));
     EXPECT_TRUE(channel_->SetCapturer(kSsrc, video_capturer_.get()));
   }
+
+  virtual cricket::FakeVideoCapturer* CreateFakeVideoCapturer() {
+    return new cricket::FakeVideoCapturer();
+  }
+
   // Utility method to setup an additional stream to send and receive video.
   // Used to test send and recv between two streams.
   void SetUpSecondStream() {
@@ -535,7 +539,7 @@
     // We dont add recv for the second stream.
 
     // Setup the receive and renderer for second stream after send.
-    video_capturer_2_.reset(new cricket::FakeVideoCapturer());
+    video_capturer_2_.reset(CreateFakeVideoCapturer());
     cricket::VideoFormat format(640, 480,
                                 cricket::VideoFormat::FpsToInterval(30),
                                 cricket::FOURCC_I420);
@@ -967,7 +971,7 @@
     // Add an additional capturer, and hook up a renderer to receive it.
     cricket::FakeVideoRenderer renderer1;
     rtc::scoped_ptr<cricket::FakeVideoCapturer> capturer(
-      new cricket::FakeVideoCapturer);
+        CreateFakeVideoCapturer());
     capturer->SetScreencast(true);
     const int kTestWidth = 160;
     const int kTestHeight = 120;
@@ -1317,7 +1321,7 @@
     EXPECT_TRUE(SendFrame());
     EXPECT_FRAME_WAIT(1, codec.width, codec.height, kTimeout);
     rtc::scoped_ptr<cricket::FakeVideoCapturer> capturer(
-        new cricket::FakeVideoCapturer);
+        CreateFakeVideoCapturer());
     capturer->SetScreencast(true);
     cricket::VideoFormat format(480, 360,
                                 cricket::VideoFormat::FpsToInterval(30),
@@ -1411,7 +1415,7 @@
     EXPECT_TRUE(channel_->AddSendStream(
         cricket::StreamParams::CreateLegacy(1)));
     rtc::scoped_ptr<cricket::FakeVideoCapturer> capturer1(
-        new cricket::FakeVideoCapturer);
+        CreateFakeVideoCapturer());
     capturer1->SetScreencast(true);
     EXPECT_EQ(cricket::CS_RUNNING, capturer1->Start(capture_format));
     // Set up additional stream 2.
@@ -1423,7 +1427,7 @@
     EXPECT_TRUE(channel_->AddSendStream(
         cricket::StreamParams::CreateLegacy(2)));
     rtc::scoped_ptr<cricket::FakeVideoCapturer> capturer2(
-        new cricket::FakeVideoCapturer);
+        CreateFakeVideoCapturer());
     capturer2->SetScreencast(true);
     EXPECT_EQ(cricket::CS_RUNNING, capturer2->Start(capture_format));
     // State for all the streams.
@@ -1481,7 +1485,7 @@
     // Registering an external capturer is currently the same as screen casting
     // (update the test when this changes).
     rtc::scoped_ptr<cricket::FakeVideoCapturer> capturer(
-        new cricket::FakeVideoCapturer);
+        CreateFakeVideoCapturer());
     capturer->SetScreencast(true);
     const std::vector<cricket::VideoFormat>* formats =
         capturer->GetSupportedFormats();
diff --git a/talk/media/base/videoframefactory.h b/talk/media/base/videoframefactory.h
new file mode 100755
index 0000000..301ec1c
--- /dev/null
+++ b/talk/media/base/videoframefactory.h
@@ -0,0 +1,52 @@
+// libjingle
+// Copyright 2014 Google Inc.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//  1. Redistributions of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//  2. Redistributions in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//  3. The name of the author may not be used to endorse or promote products
+//     derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef TALK_MEDIA_BASE_VIDEOFRAMEFACTORY_H_
+#define TALK_MEDIA_BASE_VIDEOFRAMEFACTORY_H_
+
+namespace cricket {
+
+struct CapturedFrame;
+class VideoFrame;
+
+// Creates cricket::VideoFrames, or a subclass of cricket::VideoFrame
+// depending on the subclass of VideoFrameFactory.
+class VideoFrameFactory {
+ public:
+  VideoFrameFactory() {}
+  virtual ~VideoFrameFactory() {}
+
+  // The returned frame aliases the aliased_frame if the input color
+  // space allows for aliasing, otherwise a color conversion will
+  // occur.  For safety, |input_frame| must outlive the returned
+  // frame.  Returns NULL if conversion fails.
+  virtual VideoFrame* CreateAliasedFrame(
+      const CapturedFrame* input_frame, int width, int height) const = 0;
+};
+
+}  // namespace cricket
+
+#endif  // TALK_MEDIA_BASE_VIDEOFRAMEFACTORY_H_
diff --git a/talk/media/webrtc/webrtcvideocapturer.cc b/talk/media/webrtc/webrtcvideocapturer.cc
index d341d12..38df9ce 100644
--- a/talk/media/webrtc/webrtcvideocapturer.cc
+++ b/talk/media/webrtc/webrtcvideocapturer.cc
@@ -37,6 +37,7 @@
 #include "webrtc/base/thread.h"
 #include "webrtc/base/timeutils.h"
 #include "talk/media/webrtc/webrtcvideoframe.h"
+#include "talk/media/webrtc/webrtcvideoframefactory.h"
 
 #include "webrtc/base/win32.h"  // Need this to #include the impl files.
 #include "webrtc/modules/video_capture/include/video_capture_factory.h"
@@ -126,12 +127,14 @@
     : factory_(new WebRtcVcmFactory),
       module_(NULL),
       captured_frames_(0) {
+  set_frame_factory(new WebRtcVideoFrameFactory());
 }
 
 WebRtcVideoCapturer::WebRtcVideoCapturer(WebRtcVcmFactoryInterface* factory)
     : factory_(factory),
       module_(NULL),
       captured_frames_(0) {
+  set_frame_factory(new WebRtcVideoFrameFactory());
 }
 
 WebRtcVideoCapturer::~WebRtcVideoCapturer() {
diff --git a/talk/media/base/mutedvideocapturer.h b/talk/media/webrtc/webrtcvideoframefactory.cc
similarity index 61%
rename from talk/media/base/mutedvideocapturer.h
rename to talk/media/webrtc/webrtcvideoframefactory.cc
index 11512bc..07b6663 100755
--- a/talk/media/base/mutedvideocapturer.h
+++ b/talk/media/webrtc/webrtcvideoframefactory.cc
@@ -1,6 +1,6 @@
 /*
  * libjingle
- * Copyright 2012 Google Inc.
+ * Copyright 2014 Google Inc.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -25,36 +25,23 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef TALK_MEDIA_BASE_MUTEDVIDEOCAPTURER_H_
-#define TALK_MEDIA_BASE_MUTEDVIDEOCAPTURER_H_
-
-#include "webrtc/base/thread.h"
-#include "talk/media/base/videocapturer.h"
+#include "talk/media/webrtc/webrtcvideoframe.h"
+#include "talk/media/webrtc/webrtcvideoframefactory.h"
+#include "webrtc/base/logging.h"
 
 namespace cricket {
 
-class MutedFramesGenerator;
-
-class MutedVideoCapturer : public VideoCapturer {
- public:
-  static const char kCapturerId[];
-
-  MutedVideoCapturer();
-  virtual ~MutedVideoCapturer();
-  virtual bool GetBestCaptureFormat(const VideoFormat& desired,
-                                    VideoFormat* best_format);
-  virtual CaptureState Start(const VideoFormat& capture_format);
-  virtual void Stop();
-  virtual bool IsRunning();
-  virtual bool IsScreencast() const { return false; }
-  virtual bool GetPreferredFourccs(std::vector<uint32>* fourccs);
-
- protected:
-  void OnMutedFrame(VideoFrame* muted_frame);
-
-  rtc::scoped_ptr<MutedFramesGenerator> frame_generator_;
-};
+VideoFrame* WebRtcVideoFrameFactory::CreateAliasedFrame(
+    const CapturedFrame* aliased_frame, int width, int height) const {
+  // TODO(pthatcher): Move Alias logic into the VideoFrameFactory and
+  // out of the VideoFrame.
+  rtc::scoped_ptr<WebRtcVideoFrame> frame(new WebRtcVideoFrame());
+  if (!frame->Alias(aliased_frame, width, height)) {
+    LOG(LS_ERROR) <<
+        "Failed to create WebRtcVideoFrame in CreateAliasedFrame.";
+    return NULL;
+  }
+  return frame.release();
+}
 
 }  // namespace cricket
-
-#endif  // TALK_MEDIA_BASE_MUTEDVIDEOCAPTURER_H_
diff --git a/talk/media/webrtc/webrtcvideoframefactory.h b/talk/media/webrtc/webrtcvideoframefactory.h
new file mode 100755
index 0000000..448cdcf
--- /dev/null
+++ b/talk/media/webrtc/webrtcvideoframefactory.h
@@ -0,0 +1,45 @@
+// libjingle
+// Copyright 2014 Google Inc.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//  1. Redistributions of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//  2. Redistributions in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//  3. The name of the author may not be used to endorse or promote products
+//     derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef TALK_MEDIA_WEBRTC_WEBRTCVIDEOFRAMEFACTORY_H_
+#define TALK_MEDIA_WEBRTC_WEBRTCVIDEOFRAMEFACTORY_H_
+
+#include "talk/media/base/videoframefactory.h"
+
+namespace cricket {
+
+struct CapturedFrame;
+
+// Creates instances of cricket::WebRtcVideoFrame.
+class WebRtcVideoFrameFactory : public VideoFrameFactory {
+ public:
+  virtual VideoFrame* CreateAliasedFrame(
+      const CapturedFrame* aliased_frame, int width, int height) const OVERRIDE;
+};
+
+}  // namespace cricket
+
+#endif  // TALK_MEDIA_WEBRTC_WEBRTCVIDEOFRAMEFACTORY_H_