
This is unfortunately causing build problems in Chrome on Windows. > Unify underlying frame buffer in I420VideoFrame and WebRtcVideoFrame > > Currently, I420VideoFrame uses three webrtc::Plane to store pixel data, and WebRtcVideoFrame uses WebRtcVideoFrame::FrameBuffer/webrtc::VideoFrame. The two subclasses WebRtcTextureVideoFrame and TextureVideoFrame use a NativeHandle to store pixel data, and there is also a class WebRtcVideoRenderFrame that wraps an I420VideoFrame. > > This CL replaces these classes with a new interface VideoFrameBuffer that provides the common functionality. This makes it possible to remove deep frame copies between cricket::VideoFrame and I420VideoFrame. > > Some additional minor changes are: > * Disallow creation of 0x0 texture frames. > * Remove the half-implemented ref count functions in I420VideoFrame. > * Remove the Alias functionality in WebRtcVideoFrame > > The final goal is to eliminate all frame copies, but to limit the scope of this CL, some planned changes are postponed to follow-up CL:s (see planned changes in https://webrtc-codereview.appspot.com/38879004, or https://docs.google.com/document/d/1bxoJZNmlo-Z9GnQwIaWpEG6hDlL_W-bzka8Zb_K2NbA/preview). Specifically, this CL: > * Keeps empty subclasses WebRtcTextureVideoFrame and TextureVideoFrame, and just delegates the construction to the superclass. > * Keeps the deep copies from cricket::VideoFrame to I420VideoFrame. > > BUG=1128 > R=mflodman@webrtc.org, pbos@webrtc.org, perkj@webrtc.org, tommi@webrtc.org > > Review URL: https://webrtc-codereview.appspot.com/42469004 TBR=magjed@webrtc.org Review URL: https://webrtc-codereview.appspot.com/42199005 Cr-Commit-Position: refs/heads/master@{#8599} git-svn-id: http://webrtc.googlecode.com/svn/trunk@8599 4adac7df-926f-26a2-2b94-8c16560cd09d
299 lines
10 KiB
C++
299 lines
10 KiB
C++
/*
|
|
* 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.
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
#include "webrtc/base/scoped_ptr.h"
|
|
#include "webrtc/common_video/interface/i420_video_frame.h"
|
|
#include "webrtc/system_wrappers/interface/ref_count.h"
|
|
#include "webrtc/system_wrappers/interface/scoped_refptr.h"
|
|
|
|
namespace webrtc {
|
|
|
|
bool EqualPlane(const uint8_t* data1,
|
|
const uint8_t* data2,
|
|
int stride,
|
|
int width,
|
|
int height);
|
|
bool EqualFrames(const I420VideoFrame& frame1, const I420VideoFrame& frame2);
|
|
int ExpectedSize(int plane_stride, int image_height, PlaneType type);
|
|
|
|
TEST(TestI420VideoFrame, InitialValues) {
|
|
I420VideoFrame frame;
|
|
// Invalid arguments - one call for each variable.
|
|
EXPECT_TRUE(frame.IsZeroSize());
|
|
EXPECT_EQ(kVideoRotation_0, frame.rotation());
|
|
EXPECT_EQ(-1, frame.CreateEmptyFrame(0, 10, 10, 14, 14));
|
|
EXPECT_EQ(-1, frame.CreateEmptyFrame(10, -1, 10, 90, 14));
|
|
EXPECT_EQ(-1, frame.CreateEmptyFrame(10, 10, 0, 14, 18));
|
|
EXPECT_EQ(-1, frame.CreateEmptyFrame(10, 10, 10, -2, 13));
|
|
EXPECT_EQ(-1, frame.CreateEmptyFrame(10, 10, 10, 14, 0));
|
|
EXPECT_EQ(0, frame.CreateEmptyFrame(10, 10, 10, 14, 90));
|
|
EXPECT_FALSE(frame.IsZeroSize());
|
|
}
|
|
|
|
TEST(TestI420VideoFrame, WidthHeightValues) {
|
|
I420VideoFrame frame;
|
|
const int valid_value = 10;
|
|
EXPECT_EQ(0, frame.CreateEmptyFrame(10, 10, 10, 14, 90));
|
|
EXPECT_EQ(valid_value, frame.width());
|
|
EXPECT_EQ(valid_value, frame.height());
|
|
frame.set_timestamp(123u);
|
|
EXPECT_EQ(123u, frame.timestamp());
|
|
frame.set_ntp_time_ms(456);
|
|
EXPECT_EQ(456, frame.ntp_time_ms());
|
|
frame.set_render_time_ms(789);
|
|
EXPECT_EQ(789, frame.render_time_ms());
|
|
}
|
|
|
|
TEST(TestI420VideoFrame, SizeAllocation) {
|
|
I420VideoFrame frame;
|
|
EXPECT_EQ(0, frame. CreateEmptyFrame(10, 10, 12, 14, 220));
|
|
int height = frame.height();
|
|
int stride_y = frame.stride(kYPlane);
|
|
int stride_u = frame.stride(kUPlane);
|
|
int stride_v = frame.stride(kVPlane);
|
|
// Verify that allocated size was computed correctly.
|
|
EXPECT_EQ(ExpectedSize(stride_y, height, kYPlane),
|
|
frame.allocated_size(kYPlane));
|
|
EXPECT_EQ(ExpectedSize(stride_u, height, kUPlane),
|
|
frame.allocated_size(kUPlane));
|
|
EXPECT_EQ(ExpectedSize(stride_v, height, kVPlane),
|
|
frame.allocated_size(kVPlane));
|
|
}
|
|
|
|
TEST(TestI420VideoFrame, CopyFrame) {
|
|
uint32_t timestamp = 1;
|
|
int64_t ntp_time_ms = 2;
|
|
int64_t render_time_ms = 3;
|
|
int stride_y = 15;
|
|
int stride_u = 10;
|
|
int stride_v = 10;
|
|
int width = 15;
|
|
int height = 15;
|
|
// Copy frame.
|
|
I420VideoFrame small_frame;
|
|
EXPECT_EQ(0, small_frame.CreateEmptyFrame(width, height,
|
|
stride_y, stride_u, stride_v));
|
|
small_frame.set_timestamp(timestamp);
|
|
small_frame.set_ntp_time_ms(ntp_time_ms);
|
|
small_frame.set_render_time_ms(render_time_ms);
|
|
const int kSizeY = 400;
|
|
const int kSizeU = 100;
|
|
const int kSizeV = 100;
|
|
const VideoRotation kRotation = kVideoRotation_270;
|
|
uint8_t buffer_y[kSizeY];
|
|
uint8_t buffer_u[kSizeU];
|
|
uint8_t buffer_v[kSizeV];
|
|
memset(buffer_y, 16, kSizeY);
|
|
memset(buffer_u, 8, kSizeU);
|
|
memset(buffer_v, 4, kSizeV);
|
|
I420VideoFrame big_frame;
|
|
EXPECT_EQ(0,
|
|
big_frame.CreateFrame(kSizeY, buffer_y, kSizeU, buffer_u, kSizeV,
|
|
buffer_v, width + 5, height + 5, stride_y + 5,
|
|
stride_u, stride_v, kRotation));
|
|
// Frame of smaller dimensions.
|
|
EXPECT_EQ(0, small_frame.CopyFrame(big_frame));
|
|
EXPECT_TRUE(EqualFrames(small_frame, big_frame));
|
|
EXPECT_EQ(kRotation, small_frame.rotation());
|
|
|
|
// Frame of larger dimensions.
|
|
EXPECT_EQ(0, small_frame.CreateEmptyFrame(width, height,
|
|
stride_y, stride_u, stride_v));
|
|
memset(small_frame.buffer(kYPlane), 1, small_frame.allocated_size(kYPlane));
|
|
memset(small_frame.buffer(kUPlane), 2, small_frame.allocated_size(kUPlane));
|
|
memset(small_frame.buffer(kVPlane), 3, small_frame.allocated_size(kVPlane));
|
|
EXPECT_EQ(0, big_frame.CopyFrame(small_frame));
|
|
EXPECT_TRUE(EqualFrames(small_frame, big_frame));
|
|
}
|
|
|
|
TEST(TestI420VideoFrame, CloneFrame) {
|
|
I420VideoFrame frame1;
|
|
rtc::scoped_ptr<I420VideoFrame> frame2;
|
|
const int kSizeY = 400;
|
|
const int kSizeU = 100;
|
|
const int kSizeV = 100;
|
|
uint8_t buffer_y[kSizeY];
|
|
uint8_t buffer_u[kSizeU];
|
|
uint8_t buffer_v[kSizeV];
|
|
memset(buffer_y, 16, kSizeY);
|
|
memset(buffer_u, 8, kSizeU);
|
|
memset(buffer_v, 4, kSizeV);
|
|
frame1.CreateFrame(
|
|
kSizeY, buffer_y, kSizeU, buffer_u, kSizeV, buffer_v, 20, 20, 20, 10, 10);
|
|
frame1.set_timestamp(1);
|
|
frame1.set_ntp_time_ms(2);
|
|
frame1.set_render_time_ms(3);
|
|
|
|
frame2.reset(frame1.CloneFrame());
|
|
EXPECT_TRUE(frame2.get() != NULL);
|
|
EXPECT_TRUE(EqualFrames(frame1, *frame2));
|
|
}
|
|
|
|
TEST(TestI420VideoFrame, CopyBuffer) {
|
|
I420VideoFrame frame1, frame2;
|
|
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, frame2.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);
|
|
frame2.CreateFrame(kSizeY, buffer_y,
|
|
kSizeUv, buffer_u,
|
|
kSizeUv, buffer_v,
|
|
width, height, stride_y, stride_uv, stride_uv);
|
|
// Expect exactly the same pixel data.
|
|
EXPECT_TRUE(EqualPlane(buffer_y, frame2.buffer(kYPlane), stride_y, 15, 15));
|
|
EXPECT_TRUE(EqualPlane(buffer_u, frame2.buffer(kUPlane), stride_uv, 8, 8));
|
|
EXPECT_TRUE(EqualPlane(buffer_v, frame2.buffer(kVPlane), stride_uv, 8, 8));
|
|
|
|
// Compare size.
|
|
EXPECT_LE(kSizeY, frame2.allocated_size(kYPlane));
|
|
EXPECT_LE(kSizeUv, frame2.allocated_size(kUPlane));
|
|
EXPECT_LE(kSizeUv, frame2.allocated_size(kVPlane));
|
|
}
|
|
|
|
TEST(TestI420VideoFrame, FrameSwap) {
|
|
I420VideoFrame frame1, frame2;
|
|
uint32_t timestamp1 = 1;
|
|
int64_t ntp_time_ms1 = 2;
|
|
int64_t render_time_ms1 = 3;
|
|
int stride_y1 = 15;
|
|
int stride_u1 = 10;
|
|
int stride_v1 = 10;
|
|
int width1 = 15;
|
|
int height1 = 15;
|
|
const int kSizeY1 = 225;
|
|
const int kSizeU1 = 80;
|
|
const int kSizeV1 = 80;
|
|
uint32_t timestamp2 = 4;
|
|
int64_t ntp_time_ms2 = 5;
|
|
int64_t render_time_ms2 = 6;
|
|
int stride_y2 = 30;
|
|
int stride_u2 = 20;
|
|
int stride_v2 = 20;
|
|
int width2 = 30;
|
|
int height2 = 30;
|
|
const int kSizeY2 = 900;
|
|
const int kSizeU2 = 300;
|
|
const int kSizeV2 = 300;
|
|
// Initialize frame1 values.
|
|
EXPECT_EQ(0, frame1.CreateEmptyFrame(width1, height1,
|
|
stride_y1, stride_u1, stride_v1));
|
|
frame1.set_timestamp(timestamp1);
|
|
frame1.set_ntp_time_ms(ntp_time_ms1);
|
|
frame1.set_render_time_ms(render_time_ms1);
|
|
// Set memory for frame1.
|
|
uint8_t buffer_y1[kSizeY1];
|
|
uint8_t buffer_u1[kSizeU1];
|
|
uint8_t buffer_v1[kSizeV1];
|
|
memset(buffer_y1, 2, kSizeY1);
|
|
memset(buffer_u1, 4, kSizeU1);
|
|
memset(buffer_v1, 8, kSizeV1);
|
|
frame1.CreateFrame(kSizeY1, buffer_y1,
|
|
kSizeU1, buffer_u1,
|
|
kSizeV1, buffer_v1,
|
|
width1, height1, stride_y1, stride_u1, stride_v1);
|
|
// Initialize frame2 values.
|
|
EXPECT_EQ(0, frame2.CreateEmptyFrame(width2, height2,
|
|
stride_y2, stride_u2, stride_v2));
|
|
frame2.set_timestamp(timestamp2);
|
|
frame1.set_ntp_time_ms(ntp_time_ms2);
|
|
frame2.set_render_time_ms(render_time_ms2);
|
|
// Set memory for frame2.
|
|
uint8_t buffer_y2[kSizeY2];
|
|
uint8_t buffer_u2[kSizeU2];
|
|
uint8_t buffer_v2[kSizeV2];
|
|
memset(buffer_y2, 0, kSizeY2);
|
|
memset(buffer_u2, 1, kSizeU2);
|
|
memset(buffer_v2, 2, kSizeV2);
|
|
frame2.CreateFrame(kSizeY2, buffer_y2,
|
|
kSizeU2, buffer_u2,
|
|
kSizeV2, buffer_v2,
|
|
width2, height2, stride_y2, stride_u2, stride_v2);
|
|
// Copy frames for subsequent comparison.
|
|
I420VideoFrame frame1_copy, frame2_copy;
|
|
frame1_copy.CopyFrame(frame1);
|
|
frame2_copy.CopyFrame(frame2);
|
|
// Swap frames.
|
|
frame1.SwapFrame(&frame2);
|
|
// Verify swap.
|
|
EXPECT_TRUE(EqualFrames(frame1_copy, frame2));
|
|
EXPECT_TRUE(EqualFrames(frame2_copy, frame1));
|
|
}
|
|
|
|
TEST(TestI420VideoFrame, RefCountedInstantiation) {
|
|
// Refcounted instantiation - ref_count should correspond to the number of
|
|
// instances.
|
|
scoped_refptr<I420VideoFrame> ref_count_frame(
|
|
new RefCountImpl<I420VideoFrame>());
|
|
EXPECT_EQ(2, ref_count_frame->AddRef());
|
|
EXPECT_EQ(3, ref_count_frame->AddRef());
|
|
EXPECT_EQ(2, ref_count_frame->Release());
|
|
EXPECT_EQ(1, ref_count_frame->Release());
|
|
}
|
|
|
|
bool EqualPlane(const uint8_t* data1,
|
|
const uint8_t* data2,
|
|
int stride,
|
|
int width,
|
|
int height) {
|
|
for (int y = 0; y < height; ++y) {
|
|
if (memcmp(data1, data2, width) != 0)
|
|
return false;
|
|
data1 += stride;
|
|
data2 += stride;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool EqualFrames(const I420VideoFrame& frame1, const I420VideoFrame& frame2) {
|
|
if ((frame1.width() != frame2.width()) ||
|
|
(frame1.height() != frame2.height()) ||
|
|
(frame1.stride(kYPlane) != frame2.stride(kYPlane)) ||
|
|
(frame1.stride(kUPlane) != frame2.stride(kUPlane)) ||
|
|
(frame1.stride(kVPlane) != frame2.stride(kVPlane)) ||
|
|
(frame1.timestamp() != frame2.timestamp()) ||
|
|
(frame1.ntp_time_ms() != frame2.ntp_time_ms()) ||
|
|
(frame1.render_time_ms() != frame2.render_time_ms())) {
|
|
return false;
|
|
}
|
|
const int half_width = (frame1.width() + 1) / 2;
|
|
const int half_height = (frame1.height() + 1) / 2;
|
|
return EqualPlane(frame1.buffer(kYPlane), frame2.buffer(kYPlane),
|
|
frame1.stride(kYPlane), frame1.width(), frame1.height()) &&
|
|
EqualPlane(frame1.buffer(kUPlane), frame2.buffer(kUPlane),
|
|
frame1.stride(kUPlane), half_width, half_height) &&
|
|
EqualPlane(frame1.buffer(kVPlane), frame2.buffer(kVPlane),
|
|
frame1.stride(kVPlane), half_width, half_height);
|
|
}
|
|
|
|
int ExpectedSize(int plane_stride, int image_height, PlaneType type) {
|
|
if (type == kYPlane) {
|
|
return (plane_stride * image_height);
|
|
} else {
|
|
int half_height = (image_height + 1) / 2;
|
|
return (plane_stride * half_height);
|
|
}
|
|
}
|
|
|
|
} // namespace webrtc
|