Add SwapFrame() to VideoSendStreamInput.

Optionally prevents doing a frame copy when putting frames into a
VideoSendStream. PutFrame() is still there, which copies the frame.

Also removes time_since_capture_ms as a parameter, since
I420VideoFrame::render_time_ms() denotes when the frame was captured.

BUG=2657
R=mflodman@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@5265 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/common_video/common_video.gyp b/common_video/common_video.gyp
index f4d9ceb..97cb941 100644
--- a/common_video/common_video.gyp
+++ b/common_video/common_video.gyp
@@ -49,84 +49,4 @@
       ],
     },
   ],  # targets
-  'conditions': [
-    ['include_tests==1', {
-      'targets': [
-        {
-          'target_name': 'frame_generator',
-          'type': 'static_library',
-          'sources': [
-            'test/frame_generator.h',
-            'test/frame_generator.cc',
-          ],
-          'dependencies': [
-            'common_video',
-          ],
-        },
-        {
-          'target_name': 'common_video_unittests',
-          'type': '<(gtest_target_type)',
-          'dependencies': [
-             'common_video',
-             '<(DEPTH)/testing/gtest.gyp:gtest',
-             '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
-             '<(webrtc_root)/test/test.gyp:test_support_main',
-          ],
-          'sources': [
-            'i420_video_frame_unittest.cc',
-            'libyuv/libyuv_unittest.cc',
-            'libyuv/scaler_unittest.cc',
-            'plane_unittest.cc',
-            'texture_video_frame_unittest.cc'
-          ],
-          # Disable warnings to enable Win64 build, issue 1323.
-          'msvs_disabled_warnings': [
-            4267,  # size_t to int truncation.
-          ],
-          'conditions': [
-            # TODO(henrike): remove build_with_chromium==1 when the bots are
-            # using Chromium's buildbots.
-            ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', {
-              'dependencies': [
-                '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code',
-              ],
-            }],
-          ],
-        },
-      ],  # targets
-      'conditions': [
-        # TODO(henrike): remove build_with_chromium==1 when the bots are using
-        # Chromium's buildbots.
-        ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', {
-          'targets': [
-            {
-              'target_name': 'common_video_unittests_apk_target',
-              'type': 'none',
-              'dependencies': [
-                '<(apk_tests_path):common_video_unittests_apk',
-              ],
-            },
-          ],
-        }],
-        ['test_isolation_mode != "noop"', {
-          'targets': [
-            {
-              'target_name': 'common_video_unittests_run',
-              'type': 'none',
-              'dependencies': [
-                'common_video_unittests',
-              ],
-              'includes': [
-                '../build/isolate.gypi',
-                'common_video_unittests.isolate',
-              ],
-              'sources': [
-                'common_video_unittests.isolate',
-              ],
-            },
-          ],
-        }],
-      ],
-    }],  # include_tests
-  ],
 }
diff --git a/common_video/common_video_unittests.gyp b/common_video/common_video_unittests.gyp
new file mode 100644
index 0000000..9523361
--- /dev/null
+++ b/common_video/common_video_unittests.gyp
@@ -0,0 +1,76 @@
+# Copyright (c) 2013 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.
+
+{
+  'includes': ['../build/common.gypi'],
+  'targets': [
+    {
+      'target_name': 'common_video_unittests',
+      'type': '<(gtest_target_type)',
+      'dependencies': [
+         '<(webrtc_root)/common_video/common_video.gyp:common_video',
+         '<(DEPTH)/testing/gtest.gyp:gtest',
+         '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
+         '<(webrtc_root)/test/test.gyp:test_support_main',
+      ],
+      'sources': [
+        'i420_video_frame_unittest.cc',
+        'libyuv/libyuv_unittest.cc',
+        'libyuv/scaler_unittest.cc',
+        'plane_unittest.cc',
+        'texture_video_frame_unittest.cc'
+      ],
+      # Disable warnings to enable Win64 build, issue 1323.
+      'msvs_disabled_warnings': [
+        4267,  # size_t to int truncation.
+      ],
+      'conditions': [
+        # TODO(henrike): remove build_with_chromium==1 when the bots are
+        # using Chromium's buildbots.
+        ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', {
+          'dependencies': [
+            '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code',
+          ],
+        }],
+      ],
+    },
+  ],  # targets
+  'conditions': [
+    # TODO(henrike): remove build_with_chromium==1 when the bots are using
+    # Chromium's buildbots.
+    ['build_with_chromium==1 and OS=="android" and gtest_target_type=="shared_library"', {
+      'targets': [
+        {
+          'target_name': 'common_video_unittests_apk_target',
+          'type': 'none',
+          'dependencies': [
+            '<(apk_tests_path):common_video_unittests_apk',
+          ],
+        },
+      ],
+    }],
+    ['test_isolation_mode != "noop"', {
+      'targets': [
+        {
+          'target_name': 'common_video_unittests_run',
+          'type': 'none',
+          'dependencies': [
+            'common_video_unittests',
+          ],
+          'includes': [
+            '../build/isolate.gypi',
+            'common_video_unittests.isolate',
+          ],
+          'sources': [
+            'common_video_unittests.isolate',
+          ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/modules/modules.gyp b/modules/modules.gyp
index 53e7b31..e33816c 100644
--- a/modules/modules.gyp
+++ b/modules/modules.gyp
@@ -98,7 +98,7 @@
             '<(webrtc_root)/modules/video_coding/codecs/vp8/vp8.gyp:webrtc_vp8',
             '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
             '<(webrtc_root)/test/test.gyp:test_support_main',
-            '<(webrtc_root)/common_video/common_video.gyp:frame_generator',
+            '<(webrtc_root)/test/test.gyp:frame_generator',
           ],
           'sources': [
             'audio_coding/main/acm2/acm_receiver_unittest.cc',
diff --git a/modules/video_coding/main/source/video_sender_unittest.cc b/modules/video_coding/main/source/video_sender_unittest.cc
index bdf2947..513a99e 100644
--- a/modules/video_coding/main/source/video_sender_unittest.cc
+++ b/modules/video_coding/main/source/video_sender_unittest.cc
@@ -12,7 +12,6 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "webrtc/common.h"
-#include "webrtc/common_video/test/frame_generator.h"
 #include "webrtc/modules/video_coding/codecs/interface/mock/mock_video_codec_interface.h"
 #include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"
 #include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
@@ -22,6 +21,7 @@
 #include "webrtc/modules/video_coding/main/test/test_util.h"
 #include "webrtc/system_wrappers/interface/clock.h"
 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
+#include "webrtc/test/frame_generator.h"
 #include "webrtc/test/testsupport/fileutils.h"
 #include "webrtc/test/testsupport/gtest_disable.h"
 
@@ -70,7 +70,7 @@
 
 class EmptyFrameGenerator : public FrameGenerator {
  public:
-  virtual I420VideoFrame& NextFrame() OVERRIDE { return frame_; }
+  I420VideoFrame* NextFrame() OVERRIDE { frame_.ResetSize(); return &frame_; }
 
  private:
   I420VideoFrame frame_;
@@ -180,7 +180,7 @@
 
   void AddFrame() {
     assert(generator_.get());
-    sender_->AddVideoFrame(generator_->NextFrame(), NULL, NULL);
+    sender_->AddVideoFrame(*generator_->NextFrame(), NULL, NULL);
   }
 
   SimulatedClock clock_;
diff --git a/common_video/test/frame_generator.cc b/test/frame_generator.cc
similarity index 74%
rename from common_video/test/frame_generator.cc
rename to test/frame_generator.cc
index 062bd17..4dd76ae 100644
--- a/common_video/test/frame_generator.cc
+++ b/test/frame_generator.cc
@@ -7,7 +7,7 @@
  *  in the file PATENTS.  All contributing project authors may
  *  be found in the AUTHORS file in the root of the source tree.
  */
-#include "webrtc/common_video/test/frame_generator.h"
+#include "webrtc/test/frame_generator.h"
 
 #include <math.h>
 #include <stdio.h>
@@ -21,29 +21,32 @@
 
 class ChromaGenerator : public FrameGenerator {
  public:
-  ChromaGenerator(size_t width, size_t height) : angle_(0.0) {
+  ChromaGenerator(size_t width, size_t height)
+      : angle_(0.0), width_(width), height_(height) {
     assert(width > 0);
     assert(height > 0);
-    frame_.CreateEmptyFrame(static_cast<int>(width),
-                            static_cast<int>(height),
-                            static_cast<int>(width),
-                            static_cast<int>((width + 1) / 2),
-                            static_cast<int>((width + 1) / 2));
-    memset(frame_.buffer(kYPlane), 0x80, frame_.allocated_size(kYPlane));
   }
 
-  virtual I420VideoFrame& NextFrame() OVERRIDE {
+  virtual I420VideoFrame* NextFrame() OVERRIDE {
+    frame_.CreateEmptyFrame(static_cast<int>(width_),
+                            static_cast<int>(height_),
+                            static_cast<int>(width_),
+                            static_cast<int>((width_ + 1) / 2),
+                            static_cast<int>((width_ + 1) / 2));
     angle_ += 30.0;
     uint8_t u = fabs(sin(angle_)) * 0xFF;
     uint8_t v = fabs(cos(angle_)) * 0xFF;
 
+    memset(frame_.buffer(kYPlane), 0x80, frame_.allocated_size(kYPlane));
     memset(frame_.buffer(kUPlane), u, frame_.allocated_size(kUPlane));
     memset(frame_.buffer(kVPlane), v, frame_.allocated_size(kVPlane));
-    return frame_;
+    return &frame_;
   }
 
  private:
   double angle_;
+  size_t width_;
+  size_t height_;
   I420VideoFrame frame_;
 };
 
@@ -57,11 +60,6 @@
     frame_size_ = CalcBufferSize(
         kI420, static_cast<int>(width_), static_cast<int>(height_));
     frame_buffer_ = new uint8_t[frame_size_];
-    frame_.CreateEmptyFrame(static_cast<int>(width),
-                            static_cast<int>(height),
-                            static_cast<int>(width),
-                            static_cast<int>((width + 1) / 2),
-                            static_cast<int>((width + 1) / 2));
   }
 
   virtual ~YuvFileGenerator() {
@@ -69,13 +67,19 @@
     delete[] frame_buffer_;
   }
 
-  virtual I420VideoFrame& NextFrame() OVERRIDE {
+  virtual I420VideoFrame* NextFrame() OVERRIDE {
     size_t count = fread(frame_buffer_, 1, frame_size_, file_);
     if (count < frame_size_) {
       rewind(file_);
       return NextFrame();
     }
 
+    frame_.CreateEmptyFrame(static_cast<int>(width_),
+                            static_cast<int>(height_),
+                            static_cast<int>(width_),
+                            static_cast<int>((width_ + 1) / 2),
+                            static_cast<int>((width_ + 1) / 2));
+
     ConvertToI420(kI420,
                   frame_buffer_,
                   0,
@@ -85,7 +89,7 @@
                   0,
                   kRotateNone,
                   &frame_);
-    return frame_;
+    return &frame_;
   }
 
  private:
diff --git a/common_video/test/frame_generator.h b/test/frame_generator.h
similarity index 95%
rename from common_video/test/frame_generator.h
rename to test/frame_generator.h
index 8233700..fe10612 100644
--- a/common_video/test/frame_generator.h
+++ b/test/frame_generator.h
@@ -22,7 +22,7 @@
   virtual ~FrameGenerator() {}
 
   // Returns video frame that remains valid until next call.
-  virtual I420VideoFrame& NextFrame() = 0;
+  virtual I420VideoFrame* NextFrame() = 0;
 
   static FrameGenerator* Create(size_t width, size_t height);
   static FrameGenerator* CreateFromYuvFile(const char* file,
diff --git a/test/frame_generator_capturer.cc b/test/frame_generator_capturer.cc
index e04961d..6570c6f 100644
--- a/test/frame_generator_capturer.cc
+++ b/test/frame_generator_capturer.cc
@@ -10,7 +10,7 @@
 
 #include "webrtc/test/frame_generator_capturer.h"
 
-#include "webrtc/common_video/test/frame_generator.h"
+#include "webrtc/test/frame_generator.h"
 #include "webrtc/system_wrappers/interface/clock.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 #include "webrtc/system_wrappers/interface/event_wrapper.h"
@@ -111,11 +111,9 @@
   {
     CriticalSectionScoped cs(lock_.get());
     if (sending_) {
-      int64_t time_before = clock_->CurrentNtpInMilliseconds();
-      I420VideoFrame& frame = frame_generator_->NextFrame();
-      frame.set_render_time_ms(time_before);
-      int64_t time_after = clock_->CurrentNtpInMilliseconds();
-      input_->PutFrame(frame, static_cast<uint32_t>(time_after - time_before));
+      I420VideoFrame* frame = frame_generator_->NextFrame();
+      frame->set_render_time_ms(clock_->CurrentNtpInMilliseconds());
+      input_->SwapFrame(frame);
     }
   }
   tick_->Wait(WEBRTC_EVENT_INFINITE);
diff --git a/test/test.gyp b/test/test.gyp
index 09851f5..0051bce 100644
--- a/test/test.gyp
+++ b/test/test.gyp
@@ -42,6 +42,17 @@
       ],
     },
     {
+      'target_name': 'frame_generator',
+      'type': 'static_library',
+      'sources': [
+        'frame_generator.cc',
+        'frame_generator.h',
+      ],
+      'dependencies': [
+        '<(webrtc_root)/common_video/common_video.gyp:common_video',
+      ],
+    },
+    {
       'target_name': 'test_support',
       'type': 'static_library',
       'dependencies': [
diff --git a/test/vcm_capturer.cc b/test/vcm_capturer.cc
index cbf7b1f..a5820bf 100644
--- a/test/vcm_capturer.cc
+++ b/test/vcm_capturer.cc
@@ -17,7 +17,7 @@
 namespace test {
 
 VcmCapturer::VcmCapturer(webrtc::VideoSendStreamInput* input)
-    : VideoCapturer(input), started_(false), vcm_(NULL), last_timestamp_(0) {}
+    : VideoCapturer(input), started_(false), vcm_(NULL) {}
 
 bool VcmCapturer::Init(size_t width, size_t height, size_t target_fps) {
   VideoCaptureModule::DeviceInfo* device_info =
@@ -88,14 +88,8 @@
 
 void VcmCapturer::OnIncomingCapturedFrame(const int32_t id,
                                           I420VideoFrame& frame) {
-  if (last_timestamp_ == 0 || frame.timestamp() < last_timestamp_) {
-    last_timestamp_ = frame.timestamp();
-  }
-
-  if (started_) {
-    input_->PutFrame(frame, frame.timestamp() - last_timestamp_);
-  }
-  last_timestamp_ = frame.timestamp();
+  if (started_)
+    input_->SwapFrame(&frame);
 }
 
 void VcmCapturer::OnCaptureDelayChanged(const int32_t id, const int32_t delay) {
diff --git a/test/vcm_capturer.h b/test/vcm_capturer.h
index 40befe8..dde3edc 100644
--- a/test/vcm_capturer.h
+++ b/test/vcm_capturer.h
@@ -40,8 +40,6 @@
   bool started_;
   VideoCaptureModule* vcm_;
   VideoCaptureCapability capability_;
-
-  uint32_t last_timestamp_;
 };
 }  // test
 }  // webrtc
diff --git a/test/webrtc_test_common.gyp b/test/webrtc_test_common.gyp
index c7f6df5..eae66a0 100644
--- a/test/webrtc_test_common.gyp
+++ b/test/webrtc_test_common.gyp
@@ -119,8 +119,8 @@
         '<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
         '<(webrtc_root)/modules/modules.gyp:video_capture_module',
         '<(webrtc_root)/modules/modules.gyp:media_file',
+        '<(webrtc_root)/test/test.gyp:frame_generator',
         '<(webrtc_root)/test/test.gyp:test_support',
-        '<(webrtc_root)/common_video/common_video.gyp:frame_generator',
       ],
     },
   ],
diff --git a/video/call_tests.cc b/video/call_tests.cc
index 6c247e6..5e4d682 100644
--- a/video/call_tests.cc
+++ b/video/call_tests.cc
@@ -17,7 +17,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 #include "webrtc/call.h"
-#include "webrtc/common_video/test/frame_generator.h"
 #include "webrtc/frame_callback.h"
 #include "webrtc/modules/remote_bitrate_estimator/include/rtp_to_ntp.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
@@ -25,20 +24,21 @@
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 #include "webrtc/system_wrappers/interface/event_wrapper.h"
 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
+#include "webrtc/test/direct_transport.h"
+#include "webrtc/test/fake_audio_device.h"
+#include "webrtc/test/fake_decoder.h"
+#include "webrtc/test/fake_encoder.h"
+#include "webrtc/test/frame_generator.h"
+#include "webrtc/test/frame_generator_capturer.h"
+#include "webrtc/test/rtp_rtcp_observer.h"
+#include "webrtc/test/testsupport/fileutils.h"
+#include "webrtc/test/testsupport/perf_test.h"
 #include "webrtc/video/transport_adapter.h"
 #include "webrtc/voice_engine/include/voe_base.h"
 #include "webrtc/voice_engine/include/voe_codec.h"
 #include "webrtc/voice_engine/include/voe_network.h"
 #include "webrtc/voice_engine/include/voe_rtp_rtcp.h"
 #include "webrtc/voice_engine/include/voe_video_sync.h"
-#include "webrtc/test/direct_transport.h"
-#include "webrtc/test/fake_audio_device.h"
-#include "webrtc/test/fake_decoder.h"
-#include "webrtc/test/fake_encoder.h"
-#include "webrtc/test/frame_generator_capturer.h"
-#include "webrtc/test/rtp_rtcp_observer.h"
-#include "webrtc/test/testsupport/fileutils.h"
-#include "webrtc/test/testsupport/perf_test.h"
 
 namespace webrtc {
 
@@ -336,7 +336,7 @@
 
   scoped_ptr<test::FrameGenerator> frame_generator(test::FrameGenerator::Create(
       send_config_.codec.width, send_config_.codec.height));
-  send_stream_->Input()->PutFrame(frame_generator->NextFrame(), 0);
+  send_stream_->Input()->SwapFrame(frame_generator->NextFrame());
 
   EXPECT_EQ(kEventSignaled, renderer.Wait())
       << "Timed out while waiting for the frame to render.";
@@ -499,7 +499,7 @@
   // check that the callbacks are done after processing video.
   scoped_ptr<test::FrameGenerator> frame_generator(
       test::FrameGenerator::Create(kWidth / 2, kHeight / 2));
-  send_stream_->Input()->PutFrame(frame_generator->NextFrame(), 0);
+  send_stream_->Input()->SwapFrame(frame_generator->NextFrame());
 
   EXPECT_EQ(kEventSignaled, pre_encode_callback.Wait())
       << "Timed out while waiting for pre-encode callback.";
@@ -1147,7 +1147,7 @@
 
   scoped_ptr<test::FrameGenerator> frame_generator(test::FrameGenerator::Create(
       send_config_.codec.width, send_config_.codec.height));
-  send_stream_->Input()->PutFrame(frame_generator->NextFrame(), 0);
+  send_stream_->Input()->SwapFrame(frame_generator->NextFrame());
 
   EXPECT_EQ(kEventSignaled, post_encode_observer.Wait())
       << "Timed out while waiting for send-side encoded-frame callback.";
diff --git a/video/full_stack.cc b/video/full_stack.cc
index f4e07e0..1181bfe 100644
--- a/video/full_stack.cc
+++ b/video/full_stack.cc
@@ -123,8 +123,11 @@
     return receiver_->DeliverPacket(packet, length);
   }
 
-  virtual void PutFrame(const I420VideoFrame& video_frame,
-                        uint32_t delta_capture_ms) OVERRIDE {
+  virtual void PutFrame(const I420VideoFrame& video_frame) OVERRIDE {
+    ADD_FAILURE() << "PutFrame() should not have been called in this test.";
+  }
+
+  virtual void SwapFrame(I420VideoFrame* video_frame) OVERRIDE {
     I420VideoFrame* copy = NULL;
     {
       CriticalSectionScoped cs(crit_.get());
@@ -136,7 +139,7 @@
     if (copy == NULL)
       copy = new I420VideoFrame();
 
-    copy->CopyFrame(video_frame);
+    copy->CopyFrame(*video_frame);
     copy->set_timestamp(copy->render_time_ms() * 90);
 
     {
@@ -147,7 +150,7 @@
       frames_.push_back(copy);
     }
 
-    input_->PutFrame(video_frame, delta_capture_ms);
+    input_->SwapFrame(video_frame);
   }
 
   virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE {
diff --git a/video/video_send_stream.cc b/video/video_send_stream.cc
index d90c733..2b83a48 100644
--- a/video/video_send_stream.cc
+++ b/video/video_send_stream.cc
@@ -211,31 +211,24 @@
   rtp_rtcp_->Release();
 }
 
-void VideoSendStream::PutFrame(const I420VideoFrame& frame,
-                               uint32_t time_since_capture_ms) {
-  // TODO(pbos): frame_copy should happen after the VideoProcessingModule has
-  //             resized the frame.
-  I420VideoFrame frame_copy;
-  frame_copy.CopyFrame(frame);
+void VideoSendStream::PutFrame(const I420VideoFrame& frame) {
+  input_frame_.CopyFrame(frame);
+  SwapFrame(&input_frame_);
+}
 
-  ViEVideoFrameI420 vf;
+void VideoSendStream::SwapFrame(I420VideoFrame* frame) {
+  // TODO(pbos): Warn if frame is "too far" into the future, or too old. This
+  //             would help detect if frame's being used without NTP.
+  //             TO REVIEWER: Is there any good check for this? Should it be
+  //             skipped?
+  if (frame != &input_frame_)
+    input_frame_.SwapFrame(frame);
 
-  // TODO(pbos): This represents a memcpy step and is only required because
-  //             external_capture_ only takes ViEVideoFrameI420s.
-  vf.y_plane = frame_copy.buffer(kYPlane);
-  vf.u_plane = frame_copy.buffer(kUPlane);
-  vf.v_plane = frame_copy.buffer(kVPlane);
-  vf.y_pitch = frame.stride(kYPlane);
-  vf.u_pitch = frame.stride(kUPlane);
-  vf.v_pitch = frame.stride(kVPlane);
-  vf.width = frame.width();
-  vf.height = frame.height();
+  // TODO(pbos): Local rendering should not be done on the capture thread.
+  if (config_.local_renderer != NULL)
+    config_.local_renderer->RenderFrame(input_frame_, 0);
 
-  external_capture_->IncomingFrameI420(vf, frame.render_time_ms());
-
-  if (config_.local_renderer != NULL) {
-    config_.local_renderer->RenderFrame(frame, 0);
-  }
+  external_capture_->SwapFrame(&input_frame_);
 }
 
 VideoSendStreamInput* VideoSendStream::Input() { return this; }
diff --git a/video/video_send_stream.h b/video/video_send_stream.h
index 0881d91..addb045 100644
--- a/video/video_send_stream.h
+++ b/video/video_send_stream.h
@@ -46,8 +46,9 @@
 
   virtual ~VideoSendStream();
 
-  virtual void PutFrame(const I420VideoFrame& frame,
-                        uint32_t time_since_capture_ms) OVERRIDE;
+  virtual void PutFrame(const I420VideoFrame& frame) OVERRIDE;
+
+  virtual void SwapFrame(I420VideoFrame* frame) OVERRIDE;
 
   virtual VideoSendStreamInput* Input() OVERRIDE;
 
@@ -62,6 +63,7 @@
   bool DeliverRtcp(const uint8_t* packet, size_t length);
 
  private:
+  I420VideoFrame input_frame_;
   TransportAdapter transport_adapter_;
   EncodedFrameCallbackAdapter encoded_frame_proxy_;
   scoped_ptr<CriticalSectionWrapper> codec_lock_;
diff --git a/video/video_send_stream_tests.cc b/video/video_send_stream_tests.cc
index d45a9c3..ae33e81 100644
--- a/video/video_send_stream_tests.cc
+++ b/video/video_send_stream_tests.cc
@@ -368,6 +368,30 @@
   StatisticianMap stats_map_;
 };
 
+TEST_F(VideoSendStreamTest, SwapsI420VideoFrames) {
+  static const size_t kWidth = 320;
+  static const size_t kHeight = 240;
+
+  test::NullTransport transport;
+  Call::Config call_config(&transport);
+  scoped_ptr<Call> call(Call::Create(call_config));
+
+  VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
+  VideoSendStream* video_send_stream = call->CreateVideoSendStream(send_config);
+  video_send_stream->StartSending();
+
+  I420VideoFrame frame;
+  frame.CreateEmptyFrame(
+      kWidth, kHeight, kWidth, (kWidth + 1) / 2, (kWidth + 1) / 2);
+  uint8_t* old_y_buffer = frame.buffer(kYPlane);
+
+  video_send_stream->Input()->SwapFrame(&frame);
+
+  EXPECT_NE(frame.buffer(kYPlane), old_y_buffer);
+
+  call->DestroyVideoSendStream(video_send_stream);
+}
+
 TEST_F(VideoSendStreamTest, SupportsFec) {
   static const int kRedPayloadType = 118;
   static const int kUlpfecPayloadType = 119;
diff --git a/video_engine/include/vie_capture.h b/video_engine/include/vie_capture.h
index 2174d5d..cee3626 100644
--- a/video_engine/include/vie_capture.h
+++ b/video_engine/include/vie_capture.h
@@ -19,6 +19,7 @@
 #define WEBRTC_VIDEO_ENGINE_INCLUDE_VIE_CAPTURE_H_
 
 #include "webrtc/common_types.h"
+#include "webrtc/common_video/interface/i420_video_frame.h"
 
 namespace webrtc {
 
@@ -117,6 +118,8 @@
   virtual int IncomingFrameI420(
       const ViEVideoFrameI420& video_frame,
       unsigned long long capture_time = 0) = 0;
+
+  virtual void SwapFrame(I420VideoFrame* frame) {}
 };
 
 // This class declares an abstract interface for a user defined observer. It is
diff --git a/video_engine/vie_capturer.cc b/video_engine/vie_capturer.cc
index 9a2e3fc..aec5b39 100644
--- a/video_engine/vie_capturer.cc
+++ b/video_engine/vie_capturer.cc
@@ -363,6 +363,11 @@
                                                           capture_time);
 }
 
+void ViECapturer::SwapFrame(I420VideoFrame* frame) {
+  external_capture_module_->IncomingI420VideoFrame(frame,
+                                                   frame->render_time_ms());
+}
+
 void ViECapturer::OnIncomingCapturedFrame(const int32_t capture_id,
                                           I420VideoFrame& video_frame) {
   WEBRTC_TRACE(kTraceStream, kTraceVideo, ViEId(engine_id_, capture_id_),
diff --git a/video_engine/vie_capturer.h b/video_engine/vie_capturer.h
index 9da187a..1fa2b53 100644
--- a/video_engine/vie_capturer.h
+++ b/video_engine/vie_capturer.h
@@ -76,6 +76,8 @@
   virtual int IncomingFrameI420(const ViEVideoFrameI420& video_frame,
                                 unsigned long long capture_time = 0);  // NOLINT
 
+  virtual void SwapFrame(I420VideoFrame* frame) OVERRIDE;
+
   // Start/Stop.
   int32_t Start(
       const CaptureCapability& capture_capability = CaptureCapability());
diff --git a/video_send_stream.h b/video_send_stream.h
index 2358042..9d4695d 100644
--- a/video_send_stream.h
+++ b/video_send_stream.h
@@ -26,10 +26,11 @@
 // Class to deliver captured frame to the video send stream.
 class VideoSendStreamInput {
  public:
-  // TODO(mflodman) Replace time_since_capture_ms when I420VideoFrame uses NTP
-  // time.
-  virtual void PutFrame(const I420VideoFrame& video_frame,
-                        uint32_t time_since_capture_ms) = 0;
+  // These methods do not lock internally and must be called sequentially.
+  // If your application switches input sources synchronization must be done
+  // externally to make sure that any old frames are not delivered concurrently.
+  virtual void PutFrame(const I420VideoFrame& video_frame) = 0;
+  virtual void SwapFrame(I420VideoFrame* video_frame) = 0;
 
  protected:
   virtual ~VideoSendStreamInput() {}
diff --git a/webrtc.gyp b/webrtc.gyp
index 7da4433..ed101b6 100644
--- a/webrtc.gyp
+++ b/webrtc.gyp
@@ -39,6 +39,7 @@
       'conditions': [
         ['include_tests==1', {
           'dependencies': [
+            'common_video/common_video_unittests.gyp:*',
             'system_wrappers/source/system_wrappers_tests.gyp:*',
             'test/metrics.gyp:*',
             'test/test.gyp:*',