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 {