webrtc_m130/webrtc/api/video/i420_buffer.cc
magjed eaf4a1e103 Add separate base classes for I420 and I444 buffers
Previously, the base class PlanarYuvBuffer was used directly. Having
separate base classes will allow us to improve type safety in some
places.

BUG=webrtc:7632
TBR=stefan@webrtc.org

Review-Url: https://codereview.webrtc.org/2914463002
Cr-Commit-Position: refs/heads/master@{#18317}
2017-05-30 08:21:59 +00:00

241 lines
7.4 KiB
C++

/*
* Copyright (c) 2015 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 "webrtc/api/video/i420_buffer.h"
#include <string.h>
#include <algorithm>
#include <utility>
#include "webrtc/base/checks.h"
#include "webrtc/base/keep_ref_until_done.h"
#include "libyuv/convert.h"
#include "libyuv/planar_functions.h"
#include "libyuv/scale.h"
// Aligning pointer to 64 bytes for improved performance, e.g. use SIMD.
static const int kBufferAlignment = 64;
namespace webrtc {
namespace {
int I420DataSize(int height, int stride_y, int stride_u, int stride_v) {
return stride_y * height + (stride_u + stride_v) * ((height + 1) / 2);
}
} // namespace
I420Buffer::I420Buffer(int width, int height)
: I420Buffer(width, height, width, (width + 1) / 2, (width + 1) / 2) {
}
I420Buffer::I420Buffer(int width,
int height,
int stride_y,
int stride_u,
int stride_v)
: width_(width),
height_(height),
stride_y_(stride_y),
stride_u_(stride_u),
stride_v_(stride_v),
data_(static_cast<uint8_t*>(AlignedMalloc(
I420DataSize(height, stride_y, stride_u, stride_v),
kBufferAlignment))) {
RTC_DCHECK_GT(width, 0);
RTC_DCHECK_GT(height, 0);
RTC_DCHECK_GE(stride_y, width);
RTC_DCHECK_GE(stride_u, (width + 1) / 2);
RTC_DCHECK_GE(stride_v, (width + 1) / 2);
}
I420Buffer::~I420Buffer() {
}
// static
rtc::scoped_refptr<I420Buffer> I420Buffer::Create(int width, int height) {
return new rtc::RefCountedObject<I420Buffer>(width, height);
}
// static
rtc::scoped_refptr<I420Buffer> I420Buffer::Create(int width,
int height,
int stride_y,
int stride_u,
int stride_v) {
return new rtc::RefCountedObject<I420Buffer>(
width, height, stride_y, stride_u, stride_v);
}
// static
rtc::scoped_refptr<I420Buffer> I420Buffer::Copy(
const VideoFrameBuffer& source) {
return Copy(source.width(), source.height(),
source.DataY(), source.StrideY(),
source.DataU(), source.StrideU(),
source.DataV(), source.StrideV());
}
// static
rtc::scoped_refptr<I420Buffer> I420Buffer::Copy(
int width, int height,
const uint8_t* data_y, int stride_y,
const uint8_t* data_u, int stride_u,
const uint8_t* data_v, int stride_v) {
// Note: May use different strides than the input data.
rtc::scoped_refptr<I420Buffer> buffer = Create(width, height);
RTC_CHECK_EQ(0, libyuv::I420Copy(data_y, stride_y,
data_u, stride_u,
data_v, stride_v,
buffer->MutableDataY(), buffer->StrideY(),
buffer->MutableDataU(), buffer->StrideU(),
buffer->MutableDataV(), buffer->StrideV(),
width, height));
return buffer;
}
// static
rtc::scoped_refptr<I420Buffer> I420Buffer::Rotate(
const VideoFrameBuffer& src, VideoRotation rotation) {
RTC_CHECK(src.DataY());
RTC_CHECK(src.DataU());
RTC_CHECK(src.DataV());
int rotated_width = src.width();
int rotated_height = src.height();
if (rotation == webrtc::kVideoRotation_90 ||
rotation == webrtc::kVideoRotation_270) {
std::swap(rotated_width, rotated_height);
}
rtc::scoped_refptr<webrtc::I420Buffer> buffer =
I420Buffer::Create(rotated_width, rotated_height);
RTC_CHECK_EQ(0, libyuv::I420Rotate(
src.DataY(), src.StrideY(),
src.DataU(), src.StrideU(),
src.DataV(), src.StrideV(),
buffer->MutableDataY(), buffer->StrideY(), buffer->MutableDataU(),
buffer->StrideU(), buffer->MutableDataV(), buffer->StrideV(),
src.width(), src.height(),
static_cast<libyuv::RotationMode>(rotation)));
return buffer;
}
void I420Buffer::InitializeData() {
memset(data_.get(), 0,
I420DataSize(height_, stride_y_, stride_u_, stride_v_));
}
int I420Buffer::width() const {
return width_;
}
int I420Buffer::height() const {
return height_;
}
const uint8_t* I420Buffer::DataY() const {
return data_.get();
}
const uint8_t* I420Buffer::DataU() const {
return data_.get() + stride_y_ * height_;
}
const uint8_t* I420Buffer::DataV() const {
return data_.get() + stride_y_ * height_ + stride_u_ * ((height_ + 1) / 2);
}
int I420Buffer::StrideY() const {
return stride_y_;
}
int I420Buffer::StrideU() const {
return stride_u_;
}
int I420Buffer::StrideV() const {
return stride_v_;
}
uint8_t* I420Buffer::MutableDataY() {
return const_cast<uint8_t*>(DataY());
}
uint8_t* I420Buffer::MutableDataU() {
return const_cast<uint8_t*>(DataU());
}
uint8_t* I420Buffer::MutableDataV() {
return const_cast<uint8_t*>(DataV());
}
// static
void I420Buffer::SetBlack(I420Buffer* buffer) {
RTC_CHECK(libyuv::I420Rect(buffer->MutableDataY(), buffer->StrideY(),
buffer->MutableDataU(), buffer->StrideU(),
buffer->MutableDataV(), buffer->StrideV(),
0, 0, buffer->width(), buffer->height(),
0, 128, 128) == 0);
}
void I420Buffer::CropAndScaleFrom(
const VideoFrameBuffer& src,
int offset_x,
int offset_y,
int crop_width,
int crop_height) {
RTC_CHECK_LE(crop_width, src.width());
RTC_CHECK_LE(crop_height, src.height());
RTC_CHECK_LE(crop_width + offset_x, src.width());
RTC_CHECK_LE(crop_height + offset_y, src.height());
RTC_CHECK_GE(offset_x, 0);
RTC_CHECK_GE(offset_y, 0);
// Make sure offset is even so that u/v plane becomes aligned.
const int uv_offset_x = offset_x / 2;
const int uv_offset_y = offset_y / 2;
offset_x = uv_offset_x * 2;
offset_y = uv_offset_y * 2;
const uint8_t* y_plane =
src.DataY() + src.StrideY() * offset_y + offset_x;
const uint8_t* u_plane =
src.DataU() + src.StrideU() * uv_offset_y + uv_offset_x;
const uint8_t* v_plane =
src.DataV() + src.StrideV() * uv_offset_y + uv_offset_x;
int res = libyuv::I420Scale(y_plane, src.StrideY(),
u_plane, src.StrideU(),
v_plane, src.StrideV(),
crop_width, crop_height,
MutableDataY(), StrideY(),
MutableDataU(), StrideU(),
MutableDataV(), StrideV(),
width(), height(), libyuv::kFilterBox);
RTC_DCHECK_EQ(res, 0);
}
void I420Buffer::CropAndScaleFrom(
const VideoFrameBuffer& src) {
const int crop_width =
std::min(src.width(), width() * src.height() / height());
const int crop_height =
std::min(src.height(), height() * src.width() / width());
CropAndScaleFrom(
src,
(src.width() - crop_width) / 2, (src.height() - crop_height) / 2,
crop_width, crop_height);
}
void I420Buffer::ScaleFrom(const VideoFrameBuffer& src) {
CropAndScaleFrom(src, 0, 0, src.width(), src.height());
}
} // namespace webrtc