| /* 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 |