Remove extra copy in VideoCaptureImpl::IncomingFrameI420

Add support for aliasing a I420VideoFrame (and internally, a Plane) to an
existing memory buffer without taking ownership.  Use this to remove an extra
copy in VideoCaptureImpl::IncomingFrameI420.

BUG=1128
BUG=chromium:310271
TEST=local build, run Chromium on ARM, build, run Chromium/unittests on Linux
TBR=fischman@webrtc.org, mflodman@webrtc.org, mikhal@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5070 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/common_video/i420_video_frame.cc b/webrtc/common_video/i420_video_frame.cc
index e369ffe..332a1e8 100644
--- a/webrtc/common_video/i420_video_frame.cc
+++ b/webrtc/common_video/i420_video_frame.cc
@@ -58,6 +58,23 @@
   return 0;
 }
 
+int I420VideoFrame::AliasBuffers(int size_y, uint8_t* buffer_y,
+                                 int size_u, uint8_t* buffer_u,
+                                 int size_v, uint8_t* buffer_v,
+                                 int width, int height,
+                                 int stride_y, int stride_u, int stride_v) {
+  if (size_y < 1 || size_u < 1 || size_v < 1)
+    return -1;
+  if (CheckDimensions(width, height, stride_y, stride_u, stride_v) < 0)
+    return -1;
+  y_plane_.Alias(size_y, stride_y, buffer_y);
+  u_plane_.Alias(size_u, stride_u, buffer_u);
+  v_plane_.Alias(size_v, stride_v, buffer_v);
+  width_ = width;
+  height_ = height;
+  return 0;
+}
+
 int I420VideoFrame::CopyFrame(const I420VideoFrame& videoFrame) {
   int ret = CreateFrame(videoFrame.allocated_size(kYPlane),
                         videoFrame.buffer(kYPlane),
diff --git a/webrtc/common_video/i420_video_frame_unittest.cc b/webrtc/common_video/i420_video_frame_unittest.cc
index 5c738bd7..9fb8c04 100644
--- a/webrtc/common_video/i420_video_frame_unittest.cc
+++ b/webrtc/common_video/i420_video_frame_unittest.cc
@@ -118,7 +118,7 @@
   EXPECT_TRUE(EqualFrames(frame1, frame2));
 }
 
-TEST(TestI420VideoFrame, CopyBuffer) {
+TEST(TestI420VideoFrame, CreateFrame) {
   I420VideoFrame frame1, frame2;
   int width = 15;
   int height = 15;
@@ -148,6 +148,34 @@
   EXPECT_LE(kSizeUv, frame2.allocated_size(kVPlane));
 }
 
+TEST(TestI420VideoFrame, AliasBuffers) {
+  I420VideoFrame frame;
+  int width = 15;
+  int height = 15;
+  int stride_y = 15;
+  int stride_uv = 10;
+  const int kSizeY = 225;
+  const int kSizeUv = 80;
+  EXPECT_EQ(
+      0, frame.CreateEmptyFrame(width, height, stride_y, stride_uv, stride_uv));
+  uint8_t buffer_y[kSizeY];
+  uint8_t buffer_u[kSizeUv];
+  uint8_t buffer_v[kSizeUv];
+  memset(buffer_y, 16, kSizeY);
+  memset(buffer_u, 8, kSizeUv);
+  memset(buffer_v, 4, kSizeUv);
+  frame.AliasBuffers(kSizeY, buffer_y,
+                     kSizeUv, buffer_u,
+                     kSizeUv, buffer_v,
+                     width, height, stride_y, stride_uv, stride_uv);
+  EXPECT_EQ(buffer_y, frame.buffer(kYPlane));
+  EXPECT_EQ(buffer_u, frame.buffer(kUPlane));
+  EXPECT_EQ(buffer_v, frame.buffer(kVPlane));
+  EXPECT_EQ(0, frame.allocated_size(kYPlane));
+  EXPECT_EQ(0, frame.allocated_size(kUPlane));
+  EXPECT_EQ(0, frame.allocated_size(kVPlane));
+}
+
 TEST(TestI420VideoFrame, FrameSwap) {
   I420VideoFrame frame1, frame2;
   uint32_t timestamp1 = 1;
diff --git a/webrtc/common_video/interface/i420_video_frame.h b/webrtc/common_video/interface/i420_video_frame.h
index 45f2ec3..a9e1b35 100644
--- a/webrtc/common_video/interface/i420_video_frame.h
+++ b/webrtc/common_video/interface/i420_video_frame.h
@@ -62,6 +62,15 @@
                           int width, int height,
                           int stride_y, int stride_u, int stride_v);
 
+  // AliasBuffers: Sets the frame's members and buffers from a set of buffers.
+  // The buffers are not copied, only the pointers, and the frame does not take
+  // ownership of the buffers (i.e.: the buffers must outlive the frame).
+  virtual int AliasBuffers(int size_y, uint8_t* buffer_y,
+                           int size_u, uint8_t* buffer_u,
+                           int size_v, uint8_t* buffer_v,
+                           int width, int height,
+                           int stride_y, int stride_u, int stride_v);
+
   // Copy frame: If required size is bigger than allocated one, new buffers of
   // adequate size will be allocated.
   // Return value: 0 on success ,-1 on error.
diff --git a/webrtc/common_video/interface/texture_video_frame.h b/webrtc/common_video/interface/texture_video_frame.h
index e905ea7..8023200 100644
--- a/webrtc/common_video/interface/texture_video_frame.h
+++ b/webrtc/common_video/interface/texture_video_frame.h
@@ -48,6 +48,17 @@
                           int stride_y,
                           int stride_u,
                           int stride_v) OVERRIDE;
+  virtual int AliasBuffers(int size_y,
+                           uint8_t* buffer_y,
+                           int size_u,
+                           uint8_t* buffer_u,
+                           int size_v,
+                           uint8_t* buffer_v,
+                           int width,
+                           int height,
+                           int stride_y,
+                           int stride_u,
+                           int stride_v) OVERRIDE;
   virtual int CopyFrame(const I420VideoFrame& videoFrame) OVERRIDE;
   virtual void SwapFrame(I420VideoFrame* videoFrame) OVERRIDE;
   virtual uint8_t* buffer(PlaneType type) OVERRIDE;
diff --git a/webrtc/common_video/plane.cc b/webrtc/common_video/plane.cc
index 68d32cd..9782917 100644
--- a/webrtc/common_video/plane.cc
+++ b/webrtc/common_video/plane.cc
@@ -17,65 +17,75 @@
 namespace webrtc {
 
 // Aligning pointer to 64 bytes for improved performance, e.g. use SIMD.
-static const int kBufferAlignment =  64;
+static const int kBufferAlignment = 64;
 
 Plane::Plane()
-    : buffer_(NULL),
+    : pointer_(NULL),
+      allocation_(NULL),
       allocated_size_(0),
       plane_size_(0),
       stride_(0) {}
 
 Plane::~Plane() {}
 
-int Plane::CreateEmptyPlane(int allocated_size, int stride, int plane_size) {
+int Plane::CreateEmptyPlane(int allocated_size,
+                            int stride,
+                            int plane_size) {
   if (allocated_size < 1 || stride < 1 || plane_size < 1)
     return -1;
   stride_ = stride;
-  if (MaybeResize(allocated_size) < 0)
+  if (Reallocate(allocated_size) < 0)
     return -1;
   plane_size_ = plane_size;
   return 0;
 }
 
-int Plane::MaybeResize(int new_size) {
+int Plane::Reallocate(int new_size) {
   if (new_size <= 0)
     return -1;
   if (new_size <= allocated_size_)
     return 0;
   Allocator<uint8_t>::scoped_ptr_aligned new_buffer(
     AlignedMalloc<uint8_t>(new_size, kBufferAlignment));
-  if (buffer_.get()) {
-    memcpy(new_buffer.get(), buffer_.get(), plane_size_);
-  }
-  buffer_.reset(new_buffer.release());
+  allocation_.reset(new_buffer.release());
+  pointer_ = allocation_.get();
   allocated_size_ = new_size;
   return 0;
 }
 
 int Plane::Copy(const Plane& plane) {
-  if (MaybeResize(plane.allocated_size_) < 0)
+  if (Reallocate(plane.allocated_size_) < 0)
     return -1;
-  if (plane.buffer_.get())
-    memcpy(buffer_.get(), plane.buffer_.get(), plane.plane_size_);
+  if (plane.pointer_)
+    memcpy(pointer_, plane.pointer_, plane.plane_size_);
   stride_ = plane.stride_;
   plane_size_ = plane.plane_size_;
   return 0;
 }
 
 int Plane::Copy(int size, int stride, const uint8_t* buffer) {
-  if (MaybeResize(size) < 0)
+  if (Reallocate(size) < 0)
     return -1;
-  memcpy(buffer_.get(), buffer, size);
+  memcpy(pointer_, buffer, size);
   plane_size_ = size;
   stride_ = stride;
   return 0;
 }
 
+void Plane::Alias(int size, int stride, uint8_t* buffer) {
+  allocation_.reset();
+  allocated_size_ = 0;
+  pointer_ = buffer;
+  stride_ = stride;
+  plane_size_ = size;
+}
+
 void Plane::Swap(Plane& plane) {
-  std::swap(stride_, plane.stride_);
+  std::swap(pointer_, plane.pointer_);
+  allocation_.swap(plane.allocation_);
   std::swap(allocated_size_, plane.allocated_size_);
   std::swap(plane_size_, plane.plane_size_);
-  buffer_.swap(plane.buffer_);
+  std::swap(stride_, plane.stride_);
 }
 
 }  // namespace webrtc
diff --git a/webrtc/common_video/plane.h b/webrtc/common_video/plane.h
index 1b74f37..612429f 100644
--- a/webrtc/common_video/plane.h
+++ b/webrtc/common_video/plane.h
@@ -25,23 +25,26 @@
   // CreateEmptyPlane - set allocated size, actual plane size and stride:
   // If current size is smaller than current size, then a buffer of sufficient
   // size will be allocated.
-  // Return value: 0 on success ,-1 on error.
+  // Return value: 0 on success, -1 on error.
   int CreateEmptyPlane(int allocated_size, int stride, int plane_size);
 
   // Copy the entire plane data.
-  // Return value: 0 on success ,-1 on error.
+  // Return value: 0 on success, -1 on error.
   int Copy(const Plane& plane);
 
   // Copy buffer: If current size is smaller
   // than current size, then a buffer of sufficient size will be allocated.
-  // Return value: 0 on success ,-1 on error.
+  // Return value: 0 on success, -1 on error.
   int Copy(int size, int stride, const uint8_t* buffer);
 
+  // Make this plane refer to a memory buffer. Plane will not own buffer.
+  void Alias(int size, int stride, uint8_t* buffer);
+
   // Swap plane data.
   void Swap(Plane& plane);
 
   // Get allocated size.
-  int allocated_size() const {return allocated_size_;}
+  int allocated_size() const { return allocated_size_; }
 
   // Set actual size.
   void ResetSize() {plane_size_ = 0;}
@@ -50,20 +53,21 @@
   bool IsZeroSize() const {return plane_size_ == 0;}
 
   // Get stride value.
-  int stride() const {return stride_;}
+  int stride() const { return stride_; }
 
   // Return data pointer.
-  const uint8_t* buffer() const {return buffer_.get();}
+  const uint8_t* buffer() const { return pointer_; }
   // Overloading with non-const.
-  uint8_t* buffer() {return buffer_.get();}
+  uint8_t* buffer() { return pointer_; }
 
  private:
-  // Resize when needed: If current allocated size is less than new_size, buffer
-  // will be updated. Old data will be copied to new buffer.
-  // Return value: 0 on success ,-1 on error.
-  int MaybeResize(int new_size);
+  // Reallocate when needed: If current allocated size is less than new_size,
+  // buffer will be updated. In any case, old data becomes undefined.
+  // Return value: 0 on success, -1 on error.
+  int Reallocate(int new_size);
 
-  Allocator<uint8_t>::scoped_ptr_aligned buffer_;
+  uint8_t* pointer_;
+  Allocator<uint8_t>::scoped_ptr_aligned allocation_;
   int allocated_size_;
   int plane_size_;
   int stride_;
diff --git a/webrtc/common_video/plane_unittest.cc b/webrtc/common_video/plane_unittest.cc
index d165598..6738f51 100644
--- a/webrtc/common_video/plane_unittest.cc
+++ b/webrtc/common_video/plane_unittest.cc
@@ -78,6 +78,15 @@
   EXPECT_EQ(0, memcmp(buffer1, plane2.buffer(), size1));
 }
 
+TEST(TestPlane, PlaneAlias) {
+  Plane plane;
+  plane.CreateEmptyPlane(100, 10, 100);
+  uint8_t buffer[256];
+  plane.Alias(sizeof(buffer), sizeof(buffer) / 16, buffer);
+  EXPECT_EQ(buffer, plane.buffer());
+  EXPECT_EQ(0, plane.allocated_size());
+}
+
 TEST(TestPlane, PlaneSwap) {
   Plane plane1, plane2;
   int size1, size2, stride1, stride2;
diff --git a/webrtc/common_video/texture_video_frame.cc b/webrtc/common_video/texture_video_frame.cc
index ea53dc2..9bd6671 100644
--- a/webrtc/common_video/texture_video_frame.cc
+++ b/webrtc/common_video/texture_video_frame.cc
@@ -60,6 +60,21 @@
   return -1;
 }
 
+int TextureVideoFrame::AliasBuffers(int size_y,
+                                    uint8_t* buffer_y,
+                                    int size_u,
+                                    uint8_t* buffer_u,
+                                    int size_v,
+                                    uint8_t* buffer_v,
+                                    int width,
+                                    int height,
+                                    int stride_y,
+                                    int stride_u,
+                                    int stride_v) {
+  NOTREACHED();
+  return -1;
+}
+
 int TextureVideoFrame::CopyFrame(const I420VideoFrame& videoFrame) {
   NOTREACHED();
   return -1;
diff --git a/webrtc/modules/video_capture/video_capture_impl.cc b/webrtc/modules/video_capture/video_capture_impl.cc
index a23d22b..592d523 100644
--- a/webrtc/modules/video_capture/video_capture_impl.cc
+++ b/webrtc/modules/video_capture/video_capture_impl.cc
@@ -314,10 +314,9 @@
         // Setting absolute height (in case it was negative).
         // In Windows, the image starts bottom left, instead of top left.
         // Setting a negative source height, inverts the image (within LibYuv).
-        int ret = _captureFrame.CreateEmptyFrame(target_width,
-                                                 abs(target_height),
-                                                 stride_y,
-                                                 stride_uv, stride_uv);
+        I420VideoFrame captureFrame;
+        int ret = captureFrame.CreateEmptyFrame(
+            target_width, abs(target_height), stride_y, stride_uv, stride_uv);
         if (ret < 0)
         {
             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
@@ -330,7 +329,7 @@
                                                    width, height,
                                                    videoFrameLength,
                                                    _rotateFrame,
-                                                   &_captureFrame);
+                                                   &captureFrame);
         if (conversionResult < 0)
         {
             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
@@ -338,7 +337,7 @@
                        frameInfo.rawType);
             return -1;
         }
-        DeliverCapturedFrame(_captureFrame, captureTime);
+        DeliverCapturedFrame(captureFrame, captureTime);
     }
     else // Encoded format
     {
@@ -365,8 +364,8 @@
   int size_y = video_frame.height * video_frame.y_pitch;
   int size_u = video_frame.u_pitch * ((video_frame.height + 1) / 2);
   int size_v =  video_frame.v_pitch * ((video_frame.height + 1) / 2);
-  // TODO(mikhal): Can we use Swap here? This will do a memcpy.
-  int ret = _captureFrame.CreateFrame(size_y, video_frame.y_plane,
+  I420VideoFrame captureFrame;
+  int ret = captureFrame.AliasBuffers(size_y, video_frame.y_plane,
                                       size_u, video_frame.u_plane,
                                       size_v, video_frame.v_plane,
                                       video_frame.width, video_frame.height,
@@ -374,11 +373,11 @@
                                       video_frame.v_pitch);
   if (ret < 0) {
     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
-                 "Failed to create I420VideoFrame");
+                 "Failed to alias I420VideoFrame");
     return -1;
   }
 
-  DeliverCapturedFrame(_captureFrame, captureTime);
+  DeliverCapturedFrame(captureFrame, captureTime);
 
   return 0;
 }
diff --git a/webrtc/modules/video_capture/video_capture_impl.h b/webrtc/modules/video_capture/video_capture_impl.h
index 41f555c..2fee61c 100644
--- a/webrtc/modules/video_capture/video_capture_impl.h
+++ b/webrtc/modules/video_capture/video_capture_impl.h
@@ -134,7 +134,6 @@
     TickTime _incomingFrameTimes[kFrameRateCountHistorySize];// timestamp for local captured frames
     VideoRotationMode _rotateFrame; //Set if the frame should be rotated by the capture module.
 
-    I420VideoFrame _captureFrame;
     VideoFrame _capture_encoded_frame;
 
     // Used to make sure incoming timestamp is increasing for every frame.
diff --git a/webrtc/video_engine/include/vie_image_process.h b/webrtc/video_engine/include/vie_image_process.h
index 9a12748..c1365f6 100644
--- a/webrtc/video_engine/include/vie_image_process.h
+++ b/webrtc/video_engine/include/vie_image_process.h
@@ -18,7 +18,6 @@
 #define WEBRTC_VIDEO_ENGINE_INCLUDE_VIE_IMAGE_PROCESS_H_
 
 #include "webrtc/common_types.h"
-#include "webrtc/common_video/interface/i420_video_frame.h"
 
 namespace webrtc {