Fixed standard PSNR/SSIM test.

BUG=1103

Review URL: https://webrtc-codereview.appspot.com/971005

git-svn-id: http://webrtc.googlecode.com/svn/trunk@3197 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/common_video/libyuv/include/webrtc_libyuv.h b/webrtc/common_video/libyuv/include/webrtc_libyuv.h
index c6a0046..777e7c7 100644
--- a/webrtc/common_video/libyuv/include/webrtc_libyuv.h
+++ b/webrtc/common_video/libyuv/include/webrtc_libyuv.h
@@ -43,6 +43,9 @@
   kBGRA,
 };
 
+// This is the max PSNR value our algorithms can return.
+const double kInfinitePSNR = 48.0f;
+
 // Conversion between the RawVideoType and the LibYuv videoType.
 // TODO(wu): Consolidate types into one type throughout WebRtc.
 VideoType RawVideoTypeToCommonVideoVideoType(RawVideoType type);
@@ -163,6 +166,7 @@
                      I420VideoFrame* dst_frame);
 
 // Compute PSNR for an I420 frame (all planes).
+// Returns the PSNR in decibel, to a maximum of kInfinitePSNR.
 double I420PSNR(const I420VideoFrame* ref_frame,
                 const I420VideoFrame* test_frame);
 // Compute SSIM for an I420 frame (all planes).
@@ -171,6 +175,7 @@
 
 // TODO(mikhal): Remove these functions and keep only the above functionality.
 // Compute PSNR for an I420 buffer (all planes).
+// Returns the PSNR in decibel, to a maximum of kInfinitePSNR.
 double I420PSNR(const uint8_t* ref_frame,
                 const uint8_t* test_frame,
                 int width, int height);
diff --git a/webrtc/common_video/libyuv/webrtc_libyuv.cc b/webrtc/common_video/libyuv/webrtc_libyuv.cc
index 004de53..fdef255 100644
--- a/webrtc/common_video/libyuv/webrtc_libyuv.cc
+++ b/webrtc/common_video/libyuv/webrtc_libyuv.cc
@@ -352,9 +352,9 @@
                                  test_frame->buffer(kVPlane),
                                  test_frame->stride(kVPlane),
                                  test_frame->width(), test_frame->height());
-  // LibYuv sets the max psnr value to 128, we restrict it to 48.
+  // LibYuv sets the max psnr value to 128, we restrict it here.
   // In case of 0 mse in one frame, 128 can skew the results significantly.
-  return (psnr > 48.0) ? 48.0 : psnr;
+  return (psnr > kInfinitePSNR) ? kInfinitePSNR : psnr;
 }
 
 // Compute SSIM for an I420 frame (all planes)
@@ -407,9 +407,9 @@
                                  src_u_b, half_width,
                                  src_v_b, half_width,
                                  width, height);
-  // LibYuv sets the max psnr value to 128, we restrict it to 48.
+  // LibYuv sets the max psnr value to 128, we restrict it here.
   // In case of 0 mse in one frame, 128 can skew the results significantly.
-  return (psnr > 48.0) ? 48.0 : psnr;
+  return (psnr > kInfinitePSNR) ? kInfinitePSNR : psnr;
 }
 // Compute SSIM for an I420 frame (all planes)
 double I420SSIM(const uint8_t* ref_frame,
diff --git a/webrtc/modules/video_coding/main/source/media_optimization.cc b/webrtc/modules/video_coding/main/source/media_optimization.cc
index 48b7b47..8dd47fc 100644
--- a/webrtc/modules/video_coding/main/source/media_optimization.cc
+++ b/webrtc/modules/video_coding/main/source/media_optimization.cc
@@ -249,6 +249,7 @@
 {
     // leak appropriate number of bytes
     _frameDropper->Leak((WebRtc_UWord32)(InputFrameRate() + 0.5f));
+
     return _frameDropper->DropFrame();
 }
 
diff --git a/webrtc/test/testsupport/metrics/video_metrics.cc b/webrtc/test/testsupport/metrics/video_metrics.cc
index ddd31c4..c9d6d9d 100644
--- a/webrtc/test/testsupport/metrics/video_metrics.cc
+++ b/webrtc/test/testsupport/metrics/video_metrics.cc
@@ -20,6 +20,9 @@
 namespace webrtc {
 namespace test {
 
+// Copy here so our callers won't need to include libyuv for this constant.
+double kMetricsInfinitePSNR = kInfinitePSNR;
+
 // Used for calculating min and max values.
 static bool LessForFrameResultValue (const FrameResult& s1,
                                      const FrameResult& s2) {
diff --git a/webrtc/test/testsupport/metrics/video_metrics.h b/webrtc/test/testsupport/metrics/video_metrics.h
index df11a49..36e62a0 100644
--- a/webrtc/test/testsupport/metrics/video_metrics.h
+++ b/webrtc/test/testsupport/metrics/video_metrics.h
@@ -17,6 +17,9 @@
 namespace webrtc {
 namespace test {
 
+// The highest PSNR value our algorithms will return.
+extern double kMetricsInfinitePSNR;
+
 // Contains video quality metrics result for a single frame.
 struct FrameResult {
   int frame_number;
@@ -43,10 +46,13 @@
 
 // Calculates PSNR and SSIM values for the reference and test video files
 // (must be in I420 format). All calculated values are filled into the
-// QualityMetricsResult stucts.
+// QualityMetricsResult structs.
+//
 // PSNR values have the unit decibel (dB) where a high value means the test file
-// is similar to the reference file. The higher value, the more similar.
-// For more info about PSNR, see http://en.wikipedia.org/wiki/PSNR
+// is similar to the reference file. The higher value, the more similar. The
+// maximum PSNR value is kMetricsInfinitePSNR. For more info about PSNR, see
+// http://en.wikipedia.org/wiki/PSNR.
+//
 // SSIM values range between -1.0 and 1.0, where 1.0 means the files are
 // identical. For more info about SSIM, see http://en.wikipedia.org/wiki/SSIM
 // This function only compares video frames up to the point when the shortest
@@ -67,11 +73,14 @@
 // Calculates PSNR values for the reference and test video files (must be in
 // I420 format). All calculated values are filled into the QualityMetricsResult
 // struct.
+//
 // PSNR values have the unit decibel (dB) where a high value means the test file
-// is similar to the reference file. The higher value, the more similar.
+// is similar to the reference file. The higher value, the more similar. The
+// maximum PSNR value is kMetricsInfinitePSNR. For more info about PSNR, see
+// http://en.wikipedia.org/wiki/PSNR.
+//
 // This function only compares video frames up to the point when the shortest
 // video ends.
-// For more info about PSNR, see http://en.wikipedia.org/wiki/PSNR
 //
 // Return value:
 //  0 if successful, negative on errors:
diff --git a/webrtc/video_engine/test/auto_test/automated/vie_video_verification_test.cc b/webrtc/video_engine/test/auto_test/automated/vie_video_verification_test.cc
index f5237c0..fbe97aa 100644
--- a/webrtc/video_engine/test/auto_test/automated/vie_video_verification_test.cc
+++ b/webrtc/video_engine/test/auto_test/automated/vie_video_verification_test.cc
@@ -25,7 +25,6 @@
 // wouldn't like scaling, so this will work when we compare with the original.
 const int kInputWidth = 176;
 const int kInputHeight = 144;
-const int kVerifyingTestMaxNumAttempts = 3;
 
 class ViEVideoVerificationTest : public testing::Test {
  protected:
@@ -72,7 +71,8 @@
     EXPECT_EQ(0, error) << "SSIM routine failed - output files missing?";
     *ssim_result = ssim.average;
 
-    ViETest::Log("Results: PSNR is %f (dB), SSIM is %f (1 is perfect)",
+    ViETest::Log("Results: PSNR is %f (dB; 48 is max), "
+                 "SSIM is %f (1 is perfect)",
                  psnr.average, ssim.average);
   }
 
@@ -140,41 +140,40 @@
   TestParameters parameter_table_[2];
 };
 
-// TODO(phoglund): Needs to be rewritten to use external transport. Currently
-// the new memory-safe decoder is too slow with I420 with the default packet
-// engine. See http://code.google.com/p/webrtc/issues/detail?id=1103.
-TEST_F(ViEVideoVerificationTest, DISABLED_RunsBaseStandardTestWithoutErrors) {
-  // The I420 test should give pretty good values since it's a lossless codec
-  // running on the default bitrate. It should average about 30 dB but there
-  // may be cases where it dips as low as 26 under adverse conditions. That's
-  // why we have a retrying mechanism in place for this test.
-  const double kExpectedMinimumPSNR = 30;
-  const double kExpectedMinimumSSIM = 0.95;
-
-  for (int attempt = 0; attempt < kVerifyingTestMaxNumAttempts; attempt++) {
+TEST_F(ViEVideoVerificationTest, RunsBaseStandardTestWithoutErrors) {
+  // I420 is lossless, so the I420 test should obviously get perfect results -
+  // the local preview and remote output files should be bit-exact. This test
+  // runs on external transport to ensure we do not drop packets.
+  // However, it's hard to make 100% stringent requirements on the video engine
+  // since for instance the jitter buffer has non-deterministic elements. If it
+  // breaks five times in a row though, you probably introduced a bug.
+  const int kNumAttempts = 5;
+  for (int attempt = 0; attempt < kNumAttempts; ++attempt) {
     InitializeFileRenderers();
     ASSERT_TRUE(tests_.TestCallSetup(input_file_, kInputWidth, kInputHeight,
                                      local_file_renderer_,
                                      remote_file_renderer_));
-    std::string output_file = remote_file_renderer_->GetFullOutputPath();
+    std::string remote_file = remote_file_renderer_->GetFullOutputPath();
+    std::string local_preview = local_file_renderer_->GetFullOutputPath();
     StopRenderers();
 
     double actual_psnr = 0;
     double actual_ssim = 0;
-    CompareFiles(input_file_, output_file, &actual_psnr, &actual_ssim);
+    CompareFiles(local_preview, remote_file, &actual_psnr, &actual_ssim);
 
     TearDownFileRenderers();
 
-    if (actual_psnr >= kExpectedMinimumPSNR &&
-        actual_ssim >= kExpectedMinimumSSIM) {
-      // Test succeeded!
+    if (actual_psnr == webrtc::test::kMetricsInfinitePSNR &&
+        actual_ssim == 1.0f) {
+      // Test successful.
       return;
+    } else {
+      ViETest::Log("Retrying; attempt %d of %d.", attempt + 1, kNumAttempts);
     }
   }
 
-  ADD_FAILURE() << "Failed to achieve PSNR " << kExpectedMinimumPSNR <<
-      " and SSIM " << kExpectedMinimumSSIM << " after " <<
-      kVerifyingTestMaxNumAttempts << " attempts.";
+  FAIL() << "Failed to achieve perfect PSNR and SSIM results after " <<
+      kNumAttempts << " attempts.";
 }
 
 // Runs a whole stack processing with tracking of which frames are dropped
diff --git a/webrtc/video_engine/test/auto_test/primitives/base_primitives.cc b/webrtc/video_engine/test/auto_test/primitives/base_primitives.cc
index a8e2da3..317a850 100644
--- a/webrtc/video_engine/test/auto_test/primitives/base_primitives.cc
+++ b/webrtc/video_engine/test/auto_test/primitives/base_primitives.cc
@@ -8,11 +8,36 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "base_primitives.h"
+#include "webrtc/video_engine/test/auto_test/primitives/base_primitives.h"
 
-#include "vie_autotest.h"
-#include "vie_autotest_defines.h"
 #include "webrtc/modules/video_capture/include/video_capture_factory.h"
+#include "webrtc/video_engine/test/auto_test/interface/vie_autotest.h"
+#include "webrtc/video_engine/test/auto_test/interface/vie_autotest_defines.h"
+#include "webrtc/video_engine/test/libvietest/include/tb_external_transport.h"
+
+static void ConfigureCodecsToI420(int video_channel,
+                                  webrtc::VideoCodec video_codec,
+                                  webrtc::ViECodec* codec_interface) {
+  // Set up the codec interface with all known receive codecs and with
+  // I420 as the send codec.
+  for (int i = 0; i < codec_interface->NumberOfCodecs(); i++) {
+    EXPECT_EQ(0, codec_interface->GetCodec(i, video_codec));
+
+    // Try to keep the test frame size small and bit rate generous when I420.
+    if (video_codec.codecType == webrtc::kVideoCodecI420) {
+      video_codec.width = 176;
+      video_codec.height = 144;
+      video_codec.maxBitrate = 32000;
+      video_codec.startBitrate = 32000;
+      EXPECT_EQ(0, codec_interface->SetSendCodec(video_channel, video_codec));
+    }
+
+    EXPECT_EQ(0, codec_interface->SetReceiveCodec(video_channel, video_codec));
+  }
+  // Verify that we really found the I420 codec.
+  EXPECT_EQ(0, codec_interface->GetSendCodec(video_channel, video_codec));
+  EXPECT_EQ(webrtc::kVideoCodecI420, video_codec.codecType);
+}
 
 void TestI420CallSetup(webrtc::ViECodec* codec_interface,
                        webrtc::VideoEngine* video_engine,
@@ -23,45 +48,26 @@
   webrtc::VideoCodec video_codec;
   memset(&video_codec, 0, sizeof(webrtc::VideoCodec));
 
-  // Set up the codec interface with all known receive codecs and with
-  // I420 as the send codec.
-  for (int i = 0; i < codec_interface->NumberOfCodecs(); i++) {
-    EXPECT_EQ(0, codec_interface->GetCodec(i, video_codec));
+  ConfigureCodecsToI420(video_channel, video_codec, codec_interface);
 
-    // Try to keep the test frame size small when I420.
-    if (video_codec.codecType == webrtc::kVideoCodecI420) {
-      video_codec.width = 176;
-      video_codec.height = 144;
-      EXPECT_EQ(0, codec_interface->SetSendCodec(video_channel, video_codec));
-    }
-
-    EXPECT_EQ(0, codec_interface->SetReceiveCodec(video_channel, video_codec));
-  }
-
-  // Verify that we really found the I420 codec.
-  EXPECT_EQ(0, codec_interface->GetSendCodec(video_channel, video_codec));
-  EXPECT_EQ(webrtc::kVideoCodecI420, video_codec.codecType);
-
-  // Set up senders and receivers.
-  char version[1024] = "";
-  EXPECT_EQ(0, base_interface->GetVersion(version));
-  ViETest::Log("\nUsing WebRTC Video Engine version: %s", version);
-
-  const char *ipAddress = "127.0.0.1";
-  WebRtc_UWord16 rtpPortListen = 6100;
-  WebRtc_UWord16 rtpPortSend = 6100;
-  EXPECT_EQ(0, network_interface->SetLocalReceiver(video_channel,
-                                                   rtpPortListen));
+  TbExternalTransport external_transport(
+      *network_interface, video_channel, NULL);
+  EXPECT_EQ(0, network_interface->RegisterSendTransport(
+      video_channel, external_transport));
   EXPECT_EQ(0, base_interface->StartReceive(video_channel));
-  EXPECT_EQ(0, network_interface->SetSendDestination(video_channel, ipAddress,
-                                                     rtpPortSend));
   EXPECT_EQ(0, base_interface->StartSend(video_channel));
 
-  // Call started.
+  // Let the call run for a while.
   ViETest::Log("Call started");
-
   AutoTestSleep(KAutoTestSleepTimeMs);
 
-  // Done.
+  // Stop the call.
+  ViETest::Log("Stopping call.");
   EXPECT_EQ(0, base_interface->StopSend(video_channel));
+
+  // Make sure we receive all packets.
+  AutoTestSleep(1000);
+
+  EXPECT_EQ(0, base_interface->StopReceive(video_channel));
+  EXPECT_EQ(0, network_interface->DeregisterSendTransport(video_channel));
 }
diff --git a/webrtc/video_engine/test/auto_test/primitives/framedrop_primitives.cc b/webrtc/video_engine/test/auto_test/primitives/framedrop_primitives.cc
index f759118..321c74c 100644
--- a/webrtc/video_engine/test/auto_test/primitives/framedrop_primitives.cc
+++ b/webrtc/video_engine/test/auto_test/primitives/framedrop_primitives.cc
@@ -22,6 +22,7 @@
 #include "video_engine/test/auto_test/primitives/general_primitives.h"
 #include "video_engine/test/libvietest/include/tb_interfaces.h"
 #include "video_engine/test/libvietest/include/tb_external_transport.h"
+#include "video_engine/test/libvietest/include/vie_external_render_filter.h"
 #include "video_engine/test/libvietest/include/vie_to_file_renderer.h"
 
 enum { kWaitTimeForFinalDecodeMs = 100 };
@@ -29,34 +30,22 @@
 // Writes the frames to be encoded to file and tracks which frames are sent in
 // external transport on the local side and reports them to the
 // FrameDropDetector class.
-class LocalRendererEffectFilter : public webrtc::ViEEffectFilter {
+class LocalRendererEffectFilter : public webrtc::ExternalRendererEffectFilter {
  public:
-  explicit LocalRendererEffectFilter(FrameDropDetector* frame_drop_detector,
-                                     webrtc::ExternalRenderer* renderer)
-      : width_(0), height_(0), frame_drop_detector_(frame_drop_detector),
-        renderer_(renderer) {}
-  virtual ~LocalRendererEffectFilter() {}
-  virtual int Transform(int size, unsigned char* frameBuffer,
-                        unsigned int timeStamp90KHz, unsigned int width,
-                        unsigned int height) {
-    if (width != width_ || height_ != height) {
-      renderer_->FrameSizeChange(width, height, 1);
-      width_ = width;
-      height_ = height;
-    }
+  LocalRendererEffectFilter(webrtc::ExternalRenderer* renderer,
+                            FrameDropDetector* frame_drop_detector)
+      : ExternalRendererEffectFilter(renderer),
+        frame_drop_detector_(frame_drop_detector) {}
+  int Transform(int size, unsigned char* frameBuffer,
+                unsigned int timeStamp90KHz, unsigned int width,
+                unsigned int height) {
     frame_drop_detector_->ReportFrameState(FrameDropDetector::kCreated,
                                            timeStamp90KHz);
-    return renderer_->DeliverFrame(frameBuffer,
-                                   size,
-                                   timeStamp90KHz,
-                                   webrtc::TickTime::MillisecondTimestamp());
+    return webrtc::ExternalRendererEffectFilter::Transform(
+        size, frameBuffer, timeStamp90KHz, width, height);
   }
-
  private:
-  unsigned int width_;
-  unsigned int height_;
   FrameDropDetector* frame_drop_detector_;
-  webrtc::ExternalRenderer* renderer_;
 };
 
 // Tracks which frames are sent in external transport on the local side
@@ -175,8 +164,8 @@
   // Setup the effect filters.
   // Local rendering at the send-side is done in an effect filter to avoid
   // synchronization issues with the remote renderer.
-  LocalRendererEffectFilter local_renderer_filter(frame_drop_detector,
-                                                  local_file_renderer);
+  LocalRendererEffectFilter local_renderer_filter(local_file_renderer,
+                                                  frame_drop_detector);
   EXPECT_EQ(0, image_process->RegisterSendEffectFilter(video_channel,
                                                        local_renderer_filter));
   DecodedTimestampEffectFilter decode_filter(frame_drop_detector);
diff --git a/webrtc/video_engine/test/auto_test/primitives/general_primitives.cc b/webrtc/video_engine/test/auto_test/primitives/general_primitives.cc
index 5ef2aa6..8d6089f 100644
--- a/webrtc/video_engine/test/auto_test/primitives/general_primitives.cc
+++ b/webrtc/video_engine/test/auto_test/primitives/general_primitives.cc
@@ -73,15 +73,6 @@
   EXPECT_EQ(0, renderer_interface->StartRender(frame_provider_id));
 }
 
-void StopAndRemoveRenderers(webrtc::ViEBase* base_interface,
-                            webrtc::ViERender* render_interface,
-                            int channel_id,
-                            int capture_id) {
-  EXPECT_EQ(0, render_interface->StopRender(channel_id));
-  EXPECT_EQ(0, render_interface->RemoveRenderer(channel_id));
-  EXPECT_EQ(0, render_interface->RemoveRenderer(capture_id));
-}
-
 void ConfigureRtpRtcp(webrtc::ViERTP_RTCP* rtcp_interface,
                       int video_channel) {
   EXPECT_EQ(0, rtcp_interface->SetRTCPStatus(video_channel,
diff --git a/webrtc/video_engine/test/auto_test/primitives/general_primitives.h b/webrtc/video_engine/test/auto_test/primitives/general_primitives.h
index 7c1740d..3ef8d85 100644
--- a/webrtc/video_engine/test/auto_test/primitives/general_primitives.h
+++ b/webrtc/video_engine/test/auto_test/primitives/general_primitives.h
@@ -55,13 +55,6 @@
                   int frame_provider_id,
                   ViEToFileRenderer* to_file_renderer);
 
-// Stops all rendering given the normal case that we have a capture device
-// and a video channel set up for rendering.
-void StopAndRemoveRenderers(webrtc::ViEBase* base_interface,
-                            webrtc::ViERender* render_interface,
-                            int channel_id,
-                            int capture_id);
-
 // Configures RTP-RTCP.
 void ConfigureRtpRtcp(webrtc::ViERTP_RTCP* rtcp_interface,
                       int video_channel);
diff --git a/webrtc/video_engine/test/auto_test/source/vie_autotest_base.cc b/webrtc/video_engine/test/auto_test/source/vie_autotest_base.cc
index 4a7437a..28a2029 100644
--- a/webrtc/video_engine/test/auto_test/source/vie_autotest_base.cc
+++ b/webrtc/video_engine/test/auto_test/source/vie_autotest_base.cc
@@ -75,8 +75,9 @@
   EXPECT_EQ(0, capture_interface->StopCapture(capture_id));
   EXPECT_EQ(0, base_interface->StopReceive(video_channel));
 
-  StopAndRemoveRenderers(base_interface, render_interface, video_channel,
-                         capture_id);
+  EXPECT_EQ(0, render_interface->StopRender(video_channel));
+  EXPECT_EQ(0, render_interface->RemoveRenderer(video_channel));
+  EXPECT_EQ(0, render_interface->RemoveRenderer(capture_id));
 
   EXPECT_EQ(0, render_interface->DeRegisterVideoRenderModule(*_vrm1));
   EXPECT_EQ(0, render_interface->DeRegisterVideoRenderModule(*_vrm2));
diff --git a/webrtc/video_engine/test/auto_test/source/vie_file_based_comparison_tests.cc b/webrtc/video_engine/test/auto_test/source/vie_file_based_comparison_tests.cc
index d322236..7fcc693 100644
--- a/webrtc/video_engine/test/auto_test/source/vie_file_based_comparison_tests.cc
+++ b/webrtc/video_engine/test/auto_test/source/vie_file_based_comparison_tests.cc
@@ -15,6 +15,7 @@
 #include "video_engine/test/auto_test/primitives/framedrop_primitives.h"
 #include "video_engine/test/auto_test/primitives/general_primitives.h"
 #include "video_engine/test/libvietest/include/tb_interfaces.h"
+#include "video_engine/test/libvietest/include/vie_external_render_filter.h"
 #include "video_engine/test/libvietest/include/vie_fake_camera.h"
 #include "video_engine/test/libvietest/include/vie_to_file_renderer.h"
 
@@ -48,11 +49,18 @@
 
   ConfigureRtpRtcp(interfaces.rtp_rtcp, video_channel);
 
-  webrtc::ViERender *render_interface = interfaces.render;
+  webrtc::ViERender* render_interface = interfaces.render;
+  webrtc::ViEImageProcess* image_process = interfaces.image_process;
 
-  RenderToFile(render_interface, capture_id, local_file_renderer);
   RenderToFile(render_interface, video_channel, remote_file_renderer);
 
+  // We make a special hookup of the local renderer to use an effect filter
+  // instead of using the render interface for the capture device. This way
+  // we will only render frames that actually get sent.
+  webrtc::ExternalRendererEffectFilter renderer_filter(local_file_renderer);
+  EXPECT_EQ(0, image_process->RegisterSendEffectFilter(video_channel,
+                                                       renderer_filter));
+
   // Run the test itself:
   const char* device_name = "Fake Capture Device";
 
@@ -60,12 +68,8 @@
                       interfaces.base, interfaces.network, video_channel,
                       device_name);
 
-  AutoTestSleep(KAutoTestSleepTimeMs);
-
-  EXPECT_EQ(0, interfaces.base->StopReceive(video_channel));
-
-  StopAndRemoveRenderers(interfaces.base, render_interface, video_channel,
-                         capture_id);
+  EXPECT_EQ(0, render_interface->StopRender(video_channel));
+  EXPECT_EQ(0, render_interface->RemoveRenderer(video_channel));
 
   interfaces.capture->DisconnectCaptureDevice(video_channel);
 
@@ -74,6 +78,7 @@
   // tests that the system doesn't mind that the external capture device sends
   // data after rendering has been stopped.
   fake_camera.StopCamera();
+  EXPECT_EQ(0, image_process->DeregisterSendEffectFilter(video_channel));
 
   EXPECT_EQ(0, interfaces.base->DeleteChannel(video_channel));
   return true;
diff --git a/webrtc/video_engine/test/libvietest/include/vie_external_render_filter.h b/webrtc/video_engine/test/libvietest/include/vie_external_render_filter.h
new file mode 100644
index 0000000..a1aeb4c
--- /dev/null
+++ b/webrtc/video_engine/test/libvietest/include/vie_external_render_filter.h
@@ -0,0 +1,49 @@
+/*
+ *  Copyright (c) 2012 The WebRTC 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.
+ */
+#ifndef WEBRTC_VIDEO_ENGINE_TEST_LIBVIETEST_INCLUDE_VIE_EXTERNAL_RENDER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_LIBVIETEST_INCLUDE_VIE_EXTERNAL_RENDER_H_
+
+#include "webrtc/system_wrappers/interface/tick_util.h"
+#include "webrtc/video_engine/include/vie_render.h"
+
+namespace webrtc {
+
+// A render filter which passes frames directly to an external renderer. This
+// is different from plugging the external renderer directly into the sending
+// side since this will only run on frames that actually get sent and not on
+// frames that only get captured.
+class ExternalRendererEffectFilter : public webrtc::ViEEffectFilter {
+ public:
+  explicit ExternalRendererEffectFilter(webrtc::ExternalRenderer* renderer)
+      : width_(0), height_(0), renderer_(renderer) {}
+  virtual ~ExternalRendererEffectFilter() {}
+  virtual int Transform(int size, unsigned char* frame_buffer,
+                        unsigned int time_stamp90KHz, unsigned int width,
+                        unsigned int height) {
+    if (width != width_ || height_ != height) {
+      renderer_->FrameSizeChange(width, height, 1);
+      width_ = width;
+      height_ = height;
+    }
+    return renderer_->DeliverFrame(frame_buffer,
+                                   size,
+                                   time_stamp90KHz,
+                                   webrtc::TickTime::MillisecondTimestamp());
+  }
+
+ private:
+  unsigned int width_;
+  unsigned int height_;
+  webrtc::ExternalRenderer* renderer_;
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_VIDEO_ENGINE_TEST_LIBVIETEST_INCLUDE_VIE_EXTERNAL_RENDER_H_
diff --git a/webrtc/video_engine/test/libvietest/libvietest.gypi b/webrtc/video_engine/test/libvietest/libvietest.gypi
index 562bdbe..c8a84ad 100644
--- a/webrtc/video_engine/test/libvietest/libvietest.gypi
+++ b/webrtc/video_engine/test/libvietest/libvietest.gypi
@@ -27,6 +27,7 @@
       ],
       'sources': [
         # Helper classes
+        'include/vie_external_effect_filter.h',
         'include/vie_fake_camera.h',
         'include/vie_file_capture_device.h',
         'include/vie_to_file_renderer.h',