blob: a9e86a7843578696399ae067c86eec585159b99d [file] [log] [blame]
/* Copyright 2017 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "hal/usb/frame_buffer.h"
#include <sys/mman.h>
#include <utility>
#include <hardware/gralloc.h>
#include "cros-camera/common.h"
#include "hal/usb/image_processor.h"
namespace cros {
FrameBuffer::FrameBuffer()
: data_size_(0),
buffer_size_(0),
width_(0),
height_(0),
fourcc_(0),
num_planes_(0) {}
FrameBuffer::~FrameBuffer() {}
uint8_t* FrameBuffer::GetData(size_t plane) const {
if (plane >= num_planes_ || plane >= data_.size()) {
LOGF(ERROR) << "Invalid plane " << plane;
return nullptr;
}
return data_[plane];
}
size_t FrameBuffer::GetStride(size_t plane) const {
if (plane >= num_planes_) {
LOGF(ERROR) << "Invalid plane " << plane;
return 0;
}
return stride_[plane];
}
void FrameBuffer::SetFourcc(uint32_t fourcc) {
fourcc_ = fourcc;
}
int FrameBuffer::SetDataSize(size_t data_size) {
if (data_size > buffer_size_) {
LOGF(ERROR) << "Buffer overflow: Buffer only has " << buffer_size_
<< ", but data needs " << data_size;
return -EINVAL;
}
data_size_ = data_size;
return 0;
}
// static
bool SharedFrameBuffer::Reallocate(uint32_t width,
uint32_t height,
uint32_t fourcc,
std::unique_ptr<SharedFrameBuffer>* frame) {
if (!(*frame)) {
*frame = std::make_unique<SharedFrameBuffer>(0);
}
(*frame)->SetFourcc(fourcc);
(*frame)->SetWidth(width);
(*frame)->SetHeight(height);
size_t data_size = ImageProcessor::GetConvertedSize(**frame);
if (data_size == 0 || (*frame)->SetDataSize(data_size) != 0) {
LOG(ERROR) << "Set data size failed: " << width << "x" << height << " "
<< FormatToString(fourcc) << ", " << data_size;
return false;
}
return true;
}
SharedFrameBuffer::SharedFrameBuffer(int buffer_size) {
shm_region_ = base::UnsafeSharedMemoryRegion::Create(buffer_size);
shm_mapping_ = shm_region_.Map();
buffer_size_ = buffer_size;
num_planes_ = 1;
data_.resize(num_planes_, nullptr);
data_[0] = shm_mapping_.GetMemoryAs<uint8_t>();
stride_.resize(num_planes_, 0);
}
SharedFrameBuffer::~SharedFrameBuffer() {}
void SharedFrameBuffer::SetWidth(uint32_t width) {
width_ = width;
if (fourcc_ && height_) {
SetStride();
}
}
void SharedFrameBuffer::SetHeight(uint32_t height) {
height_ = height;
if (fourcc_ && width_) {
SetStride();
}
}
void SharedFrameBuffer::SetFourcc(uint32_t fourcc) {
fourcc_ = fourcc;
if (width_ && height_) {
SetStride();
}
}
int SharedFrameBuffer::SetDataSize(size_t data_size) {
if (data_size > buffer_size_) {
shm_region_ = base::UnsafeSharedMemoryRegion::Create(data_size);
shm_mapping_ = shm_region_.Map();
if (!shm_mapping_.IsValid()) {
LOGF(ERROR) << "Created Shared Memory Fail";
return -ENOMEM;
}
buffer_size_ = data_size;
}
data_size_ = data_size;
SetData();
return 0;
}
void SharedFrameBuffer::SetData() {
switch (fourcc_) {
case V4L2_PIX_FMT_YUV420: // YU12
case V4L2_PIX_FMT_YUV420M: // YM12, multiple planes YU12
if (num_planes_ != 3) {
LOGF(ERROR) << "Stride is not set correctly";
return;
}
data_.resize(num_planes_, 0);
data_[YPLANE] = shm_mapping_.GetMemoryAs<uint8_t>();
data_[UPLANE] = data_[YPLANE] + stride_[YPLANE] * height_;
data_[VPLANE] = data_[UPLANE] + stride_[UPLANE] * height_ / 2;
break;
default:
data_.resize(num_planes_, 0);
data_[0] = shm_mapping_.GetMemoryAs<uint8_t>();
break;
}
}
void SharedFrameBuffer::SetStride() {
if (!width_ || !height_ || !fourcc_) {
LOGF(ERROR) << "Invalid width (" << width_ << ") or height (" << height_
<< ") or fourcc (" << FormatToString(fourcc_) << ")";
return;
}
switch (fourcc_) {
case V4L2_PIX_FMT_YUV420: // YU12
case V4L2_PIX_FMT_YUV420M: // YM12, multiple planes YU12
num_planes_ = 3;
stride_.resize(num_planes_, 0);
stride_[YPLANE] = width_;
stride_[UPLANE] = stride_[VPLANE] = width_ / 2;
break;
default:
LOGF(ERROR) << "Pixel format " << FormatToString(fourcc_)
<< " is unsupported.";
break;
}
}
V4L2FrameBuffer::V4L2FrameBuffer(base::ScopedFD fd,
int buffer_size,
uint32_t width,
uint32_t height,
uint32_t fourcc)
: fd_(std::move(fd)), is_mapped_(false) {
buffer_size_ = buffer_size;
width_ = width;
height_ = height;
fourcc_ = fourcc;
switch (fourcc_) {
case V4L2_PIX_FMT_YUV420:
num_planes_ = 3;
break;
default:
num_planes_ = 1;
break;
}
data_.resize(num_planes_, nullptr);
stride_.resize(num_planes_, 0);
}
V4L2FrameBuffer::~V4L2FrameBuffer() {
if (Unmap()) {
LOGF(ERROR) << "Unmap failed";
}
}
int V4L2FrameBuffer::Map() {
base::AutoLock l(lock_);
if (is_mapped_)
return 0;
// TODO(b/141517606): We should tweak the mapping implementation to:
// 1. Mapped with PROT_READ | PROT_WRITE (Due to: crbug.com/178582)
// 2. Support non-zero offset
void* addr = mmap(NULL, buffer_size_, PROT_READ, MAP_SHARED, fd_.get(), 0);
if (addr == MAP_FAILED) {
PLOGF(ERROR) << "mmap() failed";
return -EINVAL;
}
data_[0] = static_cast<uint8_t*>(addr);
is_mapped_ = true;
switch (fourcc_) {
case V4L2_PIX_FMT_Y16:
case V4L2_PIX_FMT_Z16:
stride_[0] = width_;
break;
case V4L2_PIX_FMT_RGB24:
stride_[0] = width_ * 3;
break;
case V4L2_PIX_FMT_MJPEG:
stride_[0] = data_size_;
break;
case V4L2_PIX_FMT_YUYV:
stride_[0] = width_ * 2;
break;
case V4L2_PIX_FMT_YUV420:
stride_[0] = width_;
stride_[1] = (width_ + 1) / 2;
stride_[2] = (width_ + 1) / 2;
data_[1] = data_[0] + stride_[0] * height_;
data_[2] = data_[1] + stride_[1] * (height_ + 1) / 2;
break;
default:
LOGF(WARNING) << "The strides for pixel format "
<< FormatToString(fourcc_) << " are not given.";
break;
}
return 0;
}
int V4L2FrameBuffer::Unmap() {
base::AutoLock l(lock_);
if (!is_mapped_)
return 0;
if (munmap(data_[0], buffer_size_)) {
PLOGF(ERROR) << "mummap() failed";
return -EINVAL;
}
is_mapped_ = false;
return 0;
}
// static
bool GrallocFrameBuffer::Reallocate(
uint32_t width,
uint32_t height,
uint32_t fourcc,
std::unique_ptr<GrallocFrameBuffer>* frame) {
if (!(*frame) || (*frame)->GetWidth() != width ||
(*frame)->GetHeight() != height || (*frame)->GetFourcc() != fourcc) {
*frame = std::make_unique<GrallocFrameBuffer>(width, height, fourcc);
}
return true;
}
GrallocFrameBuffer::GrallocFrameBuffer(buffer_handle_t buffer,
uint32_t width,
uint32_t height)
: buffer_(buffer),
buffer_manager_(CameraBufferManager::GetInstance()),
is_buffer_owner_(false),
is_mapped_(false) {
int ret = buffer_manager_->Register(buffer_);
if (ret) {
LOGF(ERROR) << "Failed to register buffer";
return;
}
width_ = width;
height_ = height;
fourcc_ = buffer_manager_->GetV4L2PixelFormat(buffer);
num_planes_ = buffer_manager_->GetNumPlanes(buffer);
data_.resize(num_planes_, nullptr);
stride_.resize(num_planes_, 0);
}
GrallocFrameBuffer::GrallocFrameBuffer(uint32_t width,
uint32_t height,
uint32_t fourcc)
: buffer_(nullptr),
buffer_manager_(CameraBufferManager::GetInstance()),
is_buffer_owner_(true),
is_mapped_(false) {
if (fourcc != V4L2_PIX_FMT_NV12 && fourcc != V4L2_PIX_FMT_NV12M &&
fourcc != V4L2_PIX_FMT_YUV420 && fourcc != V4L2_PIX_FMT_YUV420M) {
LOGF(ERROR) << "Unsupported format: " << FormatToString(fourcc);
return;
}
uint32_t hal_format = HAL_PIXEL_FORMAT_YCbCr_420_888;
uint32_t hal_usage =
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
if (fourcc == V4L2_PIX_FMT_YUV420 || fourcc == V4L2_PIX_FMT_YUV420M) {
hal_usage |= GRALLOC_USAGE_FORCE_I420;
}
uint32_t stride;
int ret = buffer_manager_->Allocate(width, height, hal_format, hal_usage,
GRALLOC, &buffer_, &stride);
if (ret) {
LOGF(ERROR) << "Failed to allocate buffer";
return;
}
width_ = buffer_manager_->GetWidth(buffer_);
height_ = buffer_manager_->GetHeight(buffer_);
fourcc_ = buffer_manager_->GetV4L2PixelFormat(buffer_);
num_planes_ = buffer_manager_->GetNumPlanes(buffer_);
data_.resize(num_planes_, nullptr);
stride_.resize(num_planes_, 0);
}
GrallocFrameBuffer::~GrallocFrameBuffer() {
if (Unmap()) {
LOGF(ERROR) << "Unmap failed";
}
if (is_buffer_owner_) {
int ret = buffer_manager_->Free(buffer_);
if (ret) {
LOGF(ERROR) << "Failed to free buffer";
}
} else {
int ret = buffer_manager_->Deregister(buffer_);
if (ret) {
LOGF(ERROR) << "Failed to unregister buffer";
}
}
}
int GrallocFrameBuffer::Map() {
base::AutoLock l(lock_);
if (is_mapped_)
return 0;
buffer_size_ = 0;
for (size_t i = 0; i < num_planes_; i++) {
buffer_size_ += buffer_manager_->GetPlaneSize(buffer_, i);
}
void* addr;
int ret;
switch (fourcc_) {
case V4L2_PIX_FMT_JPEG:
ret = buffer_manager_->Lock(buffer_, 0, 0, 0, buffer_size_, 1, &addr);
if (!ret) {
data_[0] = static_cast<uint8_t*>(addr);
}
break;
case V4L2_PIX_FMT_RGBX32: {
ret = buffer_manager_->Lock(buffer_, 0, 0, 0, width_, height_, &addr);
if (!ret) {
data_[0] = static_cast<uint8_t*>(addr);
stride_[0] = width_ * 4;
}
break;
}
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV12M: {
struct android_ycbcr ycbcr;
ret =
buffer_manager_->LockYCbCr(buffer_, 0, 0, 0, width_, height_, &ycbcr);
if (!ret) {
data_[YPLANE] = static_cast<uint8_t*>(ycbcr.y);
data_[UPLANE] = static_cast<uint8_t*>(ycbcr.cb);
stride_[YPLANE] = ycbcr.ystride;
stride_[UPLANE] = ycbcr.cstride;
}
break;
}
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_YVU420M: {
struct android_ycbcr ycbcr;
ret =
buffer_manager_->LockYCbCr(buffer_, 0, 0, 0, width_, height_, &ycbcr);
if (!ret) {
data_[YPLANE] = static_cast<uint8_t*>(ycbcr.y);
data_[UPLANE] = static_cast<uint8_t*>(ycbcr.cb);
data_[VPLANE] = static_cast<uint8_t*>(ycbcr.cr);
stride_[YPLANE] = ycbcr.ystride;
stride_[UPLANE] = ycbcr.cstride;
stride_[VPLANE] = ycbcr.cstride;
}
break;
}
default:
LOGF(ERROR) << "Format " << FormatToString(fourcc_) << " is unsupported";
return -EINVAL;
}
if (ret) {
LOGF(ERROR) << "Failed to map buffer";
return -EINVAL;
}
is_mapped_ = true;
return 0;
}
int GrallocFrameBuffer::Unmap() {
base::AutoLock l(lock_);
if (!is_mapped_)
return 0;
if (buffer_manager_->Unlock(buffer_)) {
LOGF(ERROR) << "Failed to unmap buffer";
return -EINVAL;
}
is_mapped_ = false;
return 0;
}
} // namespace cros