| /* 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/image_processor.h" |
| |
| #include <errno.h> |
| #include <libyuv.h> |
| #include <time.h> |
| |
| #include <base/memory/ptr_util.h> |
| |
| #include "cros-camera/common.h" |
| #include "hal/usb/common_types.h" |
| |
| namespace cros { |
| |
| /* |
| * Formats have different names in different header files. Here is the mapping |
| * table: |
| * |
| * android_pixel_format_t videodev2.h FOURCC in libyuv |
| * ----------------------------------------------------------------------------- |
| * HAL_PIXEL_FORMAT_RGBA_8888 = V4L2_PIX_FMT_RGBX32 = FOURCC_ABGR |
| * HAL_PIXEL_FORMAT_YCbCr_422_I = V4L2_PIX_FMT_YUYV = FOURCC_YUYV |
| * = FOURCC_YUY2 |
| * V4L2_PIX_FMT_YUV420 = FOURCC_I420 |
| * = FOURCC_YU12 |
| * V4L2_PIX_FMT_MJPEG = FOURCC_MJPG |
| * |
| * HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED and HAL_PIXEL_FORMAT_YCbCr_420_888 |
| * may be backed by different types of buffers depending on the platform. |
| * |
| * HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED |
| * = V4L2_PIX_FMT_NV12 = FOURCC_NV12 |
| * = V4L2_PIX_FMT_RGBX32 = FOURCC_ABGR |
| * |
| * HAL_PIXEL_FORMAT_YCbCr_420_888 = V4L2_PIX_FMT_NV12 = FOURCC_NV12 |
| * = V4L2_PIX_FMT_YVU420 = FOURCC_YV12 |
| * |
| * Camera device generates FOURCC_YUYV and FOURCC_MJPG. |
| * At the Android side: |
| * - Camera preview uses HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED buffers. |
| * - Video recording uses HAL_PIXEL_FORMAT_YCbCr_420_888 buffers. |
| * - Still capture uses HAL_PIXEL_FORMAT_BLOB buffers. |
| * - CTS requires FOURCC_YV12 and FOURCC_NV21 for applications. |
| * |
| * Android stride requirement: |
| * YV12 horizontal stride should be a multiple of 16 pixels. See |
| * android.graphics.ImageFormat.YV12. |
| * The stride of ARGB, YU12, and NV21 are always equal to the width. |
| * |
| * Conversion Path: |
| * MJPG/YUYV (from camera) -> YU12 -> ARGB / NM12 (preview) |
| * -> NV21 (apps) |
| * -> YV12 (apps) |
| * -> NM12 / YV12 (video encoder) |
| */ |
| |
| size_t ImageProcessor::GetConvertedSize(const FrameBuffer& frame) { |
| if ((frame.GetWidth() % 2) || (frame.GetHeight() % 2)) { |
| LOGF(ERROR) << "Width or height is not even (" << frame.GetWidth() << " x " |
| << frame.GetHeight() << ")"; |
| return 0; |
| } |
| |
| switch (frame.GetFourcc()) { |
| case V4L2_PIX_FMT_YVU420: |
| case V4L2_PIX_FMT_YVU420M: // YM21, multiple planes YV12 |
| case V4L2_PIX_FMT_YUV420: |
| case V4L2_PIX_FMT_YUV420M: // YM12, multiple planes YU12 |
| if (frame.GetNumPlanes() != 3) { |
| LOGF(ERROR) << "Stride is not set correctly"; |
| return 0; |
| } |
| return frame.GetStride(FrameBuffer::YPLANE) * frame.GetHeight() + |
| frame.GetStride(FrameBuffer::UPLANE) * frame.GetHeight() / 2 + |
| frame.GetStride(FrameBuffer::VPLANE) * frame.GetHeight() / 2; |
| case V4L2_PIX_FMT_NV12: |
| case V4L2_PIX_FMT_NV12M: // NV12, multiple planes |
| if (frame.GetNumPlanes() != 2) { |
| LOGF(ERROR) << "Stride is not set correctly"; |
| return 0; |
| } |
| return frame.GetStride(FrameBuffer::YPLANE) * frame.GetHeight() + |
| frame.GetStride(FrameBuffer::UPLANE) * frame.GetHeight() / 2; |
| case V4L2_PIX_FMT_YUYV: |
| case V4L2_PIX_FMT_RGBX32: |
| case V4L2_PIX_FMT_RGB24: |
| return frame.GetStride() * frame.GetHeight(); |
| case V4L2_PIX_FMT_INVZ: |
| case V4L2_PIX_FMT_Y16: |
| case V4L2_PIX_FMT_Z16: |
| return 2 * frame.GetStride() * frame.GetHeight(); |
| default: |
| LOGF(ERROR) << "Pixel format " << FormatToString(frame.GetFourcc()) |
| << " is unsupported."; |
| return 0; |
| } |
| } |
| |
| int ImageProcessor::ConvertFormat(const FrameBuffer& in_frame, |
| FrameBuffer* out_frame) { |
| if ((in_frame.GetWidth() % 2) || (in_frame.GetHeight() % 2)) { |
| LOGF(ERROR) << "Width or height is not even (" << in_frame.GetWidth() |
| << " x " << in_frame.GetHeight() << ")"; |
| return -EINVAL; |
| } |
| |
| VLOGF(1) << "Convert format from " << FormatToString(in_frame.GetFourcc()) |
| << " to " << FormatToString(out_frame->GetFourcc()); |
| |
| if (in_frame.GetFourcc() == V4L2_PIX_FMT_YUYV) { |
| switch (out_frame->GetFourcc()) { |
| case V4L2_PIX_FMT_YUV420: // YU12 |
| case V4L2_PIX_FMT_YUV420M: // YM12, multiple planes YU12 |
| case V4L2_PIX_FMT_YVU420: // YV12 |
| case V4L2_PIX_FMT_YVU420M: { // YM21, multiple planes YV12 |
| int res = |
| libyuv::YUY2ToI420(in_frame.GetData(), in_frame.GetStride(), |
| out_frame->GetData(FrameBuffer::YPLANE), |
| out_frame->GetStride(FrameBuffer::YPLANE), |
| out_frame->GetData(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), |
| out_frame->GetData(FrameBuffer::VPLANE), |
| out_frame->GetStride(FrameBuffer::VPLANE), |
| out_frame->GetWidth(), out_frame->GetHeight()); |
| LOGF_IF(ERROR, res) << "YUY2ToI420() returns " << res; |
| return res ? -EINVAL : 0; |
| } |
| case V4L2_PIX_FMT_NV12: // NV12 |
| case V4L2_PIX_FMT_NV12M: { // NM12 |
| int res = |
| libyuv::YUY2ToNV12(in_frame.GetData(), in_frame.GetStride(), |
| out_frame->GetData(FrameBuffer::YPLANE), |
| out_frame->GetStride(FrameBuffer::YPLANE), |
| out_frame->GetData(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), |
| out_frame->GetWidth(), out_frame->GetHeight()); |
| LOGF_IF(ERROR, res) << "YUY2ToNV12() returns " << res; |
| return res ? -EINVAL : 0; |
| } |
| default: |
| LOGF(ERROR) << "Destination pixel format " |
| << FormatToString(out_frame->GetFourcc()) |
| << " is unsupported for YUYV source format."; |
| return -EINVAL; |
| } |
| } else if (in_frame.GetFourcc() == V4L2_PIX_FMT_YUV420 || |
| in_frame.GetFourcc() == V4L2_PIX_FMT_YUV420M) { |
| // V4L2_PIX_FMT_YVU420 is YV12. I420 is usually referred to YU12 |
| // (V4L2_PIX_FMT_YUV420), and YV12 is similar to YU12 except that U/V |
| // planes are swapped. |
| switch (out_frame->GetFourcc()) { |
| case V4L2_PIX_FMT_YUV420: // YU12 |
| case V4L2_PIX_FMT_YUV420M: // YM12, multiple planes YU12 |
| case V4L2_PIX_FMT_YVU420: // YV12 |
| case V4L2_PIX_FMT_YVU420M: { // YM21, multiple planes YV12 |
| int res = |
| libyuv::I420Copy(in_frame.GetData(FrameBuffer::YPLANE), |
| in_frame.GetStride(FrameBuffer::YPLANE), |
| in_frame.GetData(FrameBuffer::UPLANE), |
| in_frame.GetStride(FrameBuffer::UPLANE), |
| in_frame.GetData(FrameBuffer::VPLANE), |
| in_frame.GetStride(FrameBuffer::VPLANE), |
| out_frame->GetData(FrameBuffer::YPLANE), |
| out_frame->GetStride(FrameBuffer::YPLANE), |
| out_frame->GetData(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), |
| out_frame->GetData(FrameBuffer::VPLANE), |
| out_frame->GetStride(FrameBuffer::VPLANE), |
| out_frame->GetWidth(), out_frame->GetHeight()); |
| LOGF_IF(ERROR, res) << "I420Copy() returns " << res; |
| return res ? -EINVAL : 0; |
| } |
| case V4L2_PIX_FMT_NV12: // NV12 |
| case V4L2_PIX_FMT_NV12M: { // NM12 |
| int res = |
| libyuv::I420ToNV12(in_frame.GetData(FrameBuffer::YPLANE), |
| in_frame.GetStride(FrameBuffer::YPLANE), |
| in_frame.GetData(FrameBuffer::UPLANE), |
| in_frame.GetStride(FrameBuffer::UPLANE), |
| in_frame.GetData(FrameBuffer::VPLANE), |
| in_frame.GetStride(FrameBuffer::VPLANE), |
| out_frame->GetData(FrameBuffer::YPLANE), |
| out_frame->GetStride(FrameBuffer::YPLANE), |
| out_frame->GetData(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), |
| out_frame->GetWidth(), out_frame->GetHeight()); |
| LOGF_IF(ERROR, res) << "I420ToNV12() returns " << res; |
| return res ? -EINVAL : 0; |
| } |
| case V4L2_PIX_FMT_RGBX32: { |
| int res = |
| libyuv::I420ToABGR(in_frame.GetData(FrameBuffer::YPLANE), |
| in_frame.GetStride(FrameBuffer::YPLANE), |
| in_frame.GetData(FrameBuffer::UPLANE), |
| in_frame.GetStride(FrameBuffer::UPLANE), |
| in_frame.GetData(FrameBuffer::VPLANE), |
| in_frame.GetStride(FrameBuffer::VPLANE), |
| out_frame->GetData(), out_frame->GetStride(), |
| out_frame->GetWidth(), out_frame->GetHeight()); |
| LOGF_IF(ERROR, res) << "I420ToABGR() returns " << res; |
| return res ? -EINVAL : 0; |
| } |
| default: |
| LOGF(ERROR) << "Destination pixel format " |
| << FormatToString(out_frame->GetFourcc()) |
| << " is unsupported for YU12 source format."; |
| return -EINVAL; |
| } |
| } else if (in_frame.GetFourcc() == V4L2_PIX_FMT_NV12 || |
| in_frame.GetFourcc() == V4L2_PIX_FMT_NV12M) { |
| switch (out_frame->GetFourcc()) { |
| case V4L2_PIX_FMT_YUV420: // YU12 |
| case V4L2_PIX_FMT_YUV420M: // YM12, multiple planes YU12 |
| case V4L2_PIX_FMT_YVU420: // YV12 |
| case V4L2_PIX_FMT_YVU420M: { // YM21, multiple planes YV12 |
| int res = |
| libyuv::NV12ToI420(in_frame.GetData(FrameBuffer::YPLANE), |
| in_frame.GetStride(FrameBuffer::YPLANE), |
| in_frame.GetData(FrameBuffer::UPLANE), |
| in_frame.GetStride(FrameBuffer::UPLANE), |
| out_frame->GetData(FrameBuffer::YPLANE), |
| out_frame->GetStride(FrameBuffer::YPLANE), |
| out_frame->GetData(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), |
| out_frame->GetData(FrameBuffer::VPLANE), |
| out_frame->GetStride(FrameBuffer::VPLANE), |
| out_frame->GetWidth(), out_frame->GetHeight()); |
| LOGF_IF(ERROR, res) << "NV12ToI420() returns " << res; |
| return res ? -EINVAL : 0; |
| } |
| case V4L2_PIX_FMT_NV12: // NV12 |
| case V4L2_PIX_FMT_NV12M: { // NM12 |
| libyuv::CopyPlane(in_frame.GetData(FrameBuffer::YPLANE), |
| in_frame.GetStride(FrameBuffer::YPLANE), |
| out_frame->GetData(FrameBuffer::YPLANE), |
| out_frame->GetStride(FrameBuffer::YPLANE), |
| out_frame->GetWidth(), out_frame->GetHeight()); |
| libyuv::CopyPlane(in_frame.GetData(FrameBuffer::UPLANE), |
| in_frame.GetStride(FrameBuffer::UPLANE), |
| out_frame->GetData(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), |
| out_frame->GetWidth(), out_frame->GetHeight() / 2); |
| return 0; |
| } |
| case V4L2_PIX_FMT_RGBX32: { |
| int res = |
| libyuv::NV12ToABGR(in_frame.GetData(FrameBuffer::YPLANE), |
| in_frame.GetStride(FrameBuffer::YPLANE), |
| in_frame.GetData(FrameBuffer::UPLANE), |
| in_frame.GetStride(FrameBuffer::UPLANE), |
| out_frame->GetData(), out_frame->GetStride(), |
| out_frame->GetWidth(), out_frame->GetHeight()); |
| LOGF_IF(ERROR, res) << "NV12ToABGR() returns " << res; |
| return res ? -EINVAL : 0; |
| } |
| default: |
| LOGF(ERROR) << "Destination pixel format " |
| << FormatToString(out_frame->GetFourcc()) |
| << " is unsupported for NV12 source format."; |
| return -EINVAL; |
| } |
| } else if (in_frame.GetFourcc() == V4L2_PIX_FMT_JPEG || |
| in_frame.GetFourcc() == V4L2_PIX_FMT_MJPEG) { |
| switch (out_frame->GetFourcc()) { |
| case V4L2_PIX_FMT_YUV420: // YU12 |
| case V4L2_PIX_FMT_YUV420M: { // YM12, multiple planes YU12 |
| int res = |
| libyuv::MJPGToI420(in_frame.GetData(), in_frame.GetDataSize(), |
| out_frame->GetData(FrameBuffer::YPLANE), |
| out_frame->GetStride(FrameBuffer::YPLANE), |
| out_frame->GetData(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), |
| out_frame->GetData(FrameBuffer::VPLANE), |
| out_frame->GetStride(FrameBuffer::VPLANE), |
| in_frame.GetWidth(), in_frame.GetHeight(), |
| out_frame->GetWidth(), out_frame->GetHeight()); |
| LOGF_IF(ERROR, res) << "libyuv::MJPEGToI420() returns " << res; |
| return res ? -EINVAL : 0; |
| } |
| default: |
| LOGF(ERROR) << "Destination pixel format " |
| << FormatToString(out_frame->GetFourcc()) |
| << " is unsupported for MJPEG source format."; |
| return -EINVAL; |
| } |
| } else if (in_frame.GetFourcc() == V4L2_PIX_FMT_RGB24) { |
| switch (out_frame->GetFourcc()) { |
| case V4L2_PIX_FMT_YUV420: // YU12 |
| case V4L2_PIX_FMT_YUV420M: // YM12, multiple planes YU12 |
| case V4L2_PIX_FMT_YVU420: // YV12 |
| case V4L2_PIX_FMT_YVU420M: { // YM21, multiple planes YV12 |
| int res = |
| libyuv::RGB24ToI420(in_frame.GetData(), in_frame.GetStride(), |
| out_frame->GetData(FrameBuffer::YPLANE), |
| out_frame->GetStride(FrameBuffer::YPLANE), |
| out_frame->GetData(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), |
| out_frame->GetData(FrameBuffer::VPLANE), |
| out_frame->GetStride(FrameBuffer::VPLANE), |
| out_frame->GetWidth(), out_frame->GetHeight()); |
| LOGF_IF(ERROR, res) << "RGB24ToI420() returns " << res; |
| return res ? -EINVAL : 0; |
| } |
| case V4L2_PIX_FMT_NV12: // NV12 |
| case V4L2_PIX_FMT_NV12M: { // NM12 |
| if (!SharedFrameBuffer::Reallocate( |
| in_frame.GetWidth(), in_frame.GetHeight(), V4L2_PIX_FMT_YUV420, |
| &temp_i420_buffer_)) { |
| return -EINVAL; |
| } |
| // TODO(b/151201659): Currently we convert RGB24 to I420 and then |
| // convert I420 to NV12. We should find a way to convert it directly |
| // if the performance is not acceptable. |
| int res = libyuv::RGB24ToI420( |
| in_frame.GetData(), in_frame.GetStride(), |
| temp_i420_buffer_->GetData(FrameBuffer::YPLANE), |
| temp_i420_buffer_->GetStride(FrameBuffer::YPLANE), |
| temp_i420_buffer_->GetData(FrameBuffer::UPLANE), |
| temp_i420_buffer_->GetStride(FrameBuffer::UPLANE), |
| temp_i420_buffer_->GetData(FrameBuffer::VPLANE), |
| temp_i420_buffer_->GetStride(FrameBuffer::VPLANE), |
| temp_i420_buffer_->GetWidth(), temp_i420_buffer_->GetHeight()); |
| if (res != 0) { |
| LOGF(ERROR) << "RGB24ToNV12() returns " << res; |
| return -EINVAL; |
| } |
| res = libyuv::I420ToNV12( |
| temp_i420_buffer_->GetData(FrameBuffer::YPLANE), |
| temp_i420_buffer_->GetStride(FrameBuffer::YPLANE), |
| temp_i420_buffer_->GetData(FrameBuffer::UPLANE), |
| temp_i420_buffer_->GetStride(FrameBuffer::UPLANE), |
| temp_i420_buffer_->GetData(FrameBuffer::VPLANE), |
| temp_i420_buffer_->GetStride(FrameBuffer::VPLANE), |
| out_frame->GetData(FrameBuffer::YPLANE), |
| out_frame->GetStride(FrameBuffer::YPLANE), |
| out_frame->GetData(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), out_frame->GetWidth(), |
| out_frame->GetHeight()); |
| LOGF_IF(ERROR, res) << "RGB24ToNV12() returns " << res; |
| return res ? -EINVAL : 0; |
| } |
| default: { |
| LOGF(ERROR) << "Not implemented: RGB24 -> " << out_frame->GetFourcc(); |
| return -EINVAL; |
| } |
| } |
| } else if (in_frame.GetFourcc() == V4L2_PIX_FMT_INVZ || |
| in_frame.GetFourcc() == V4L2_PIX_FMT_Y16 || |
| in_frame.GetFourcc() == V4L2_PIX_FMT_Z16) { |
| if (!temp_i420_buffer_gray_ || |
| temp_i420_buffer_gray_->GetWidth() != in_frame.GetWidth() || |
| temp_i420_buffer_gray_->GetHeight() != in_frame.GetHeight()) { |
| if (!SharedFrameBuffer::Reallocate( |
| in_frame.GetWidth(), in_frame.GetHeight(), V4L2_PIX_FMT_YUV420, |
| &temp_i420_buffer_gray_)) { |
| return -EINVAL; |
| } |
| |
| // Fill UV plane with 0x80 to display in gray scale. |
| memset(temp_i420_buffer_gray_->GetData(FrameBuffer::UPLANE), 0x80, |
| temp_i420_buffer_gray_->GetStride(FrameBuffer::UPLANE) * |
| in_frame.GetHeight() / 2); |
| memset(temp_i420_buffer_gray_->GetData(FrameBuffer::VPLANE), 0x80, |
| temp_i420_buffer_gray_->GetStride(FrameBuffer::VPLANE) * |
| in_frame.GetHeight() / 2); |
| } |
| |
| switch (out_frame->GetFourcc()) { |
| case V4L2_PIX_FMT_YUV420: // YU12 |
| case V4L2_PIX_FMT_YUV420M: // YM12, multiple planes YU12 |
| case V4L2_PIX_FMT_YVU420: // YV12 |
| case V4L2_PIX_FMT_YVU420M: { // YM21, multiple planes YV12 |
| libyuv::Convert16To8Plane( |
| reinterpret_cast<const uint16_t*>(in_frame.GetData()), |
| in_frame.GetStride(), out_frame->GetData(FrameBuffer::YPLANE), |
| out_frame->GetStride(FrameBuffer::YPLANE), |
| 256, // scale |
| in_frame.GetWidth(), in_frame.GetHeight()); |
| libyuv::CopyPlane( |
| temp_i420_buffer_gray_->GetData(FrameBuffer::UPLANE), |
| temp_i420_buffer_gray_->GetStride(FrameBuffer::UPLANE), |
| out_frame->GetData(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), |
| out_frame->GetHeight() / 2); |
| libyuv::CopyPlane( |
| temp_i420_buffer_gray_->GetData(FrameBuffer::VPLANE), |
| temp_i420_buffer_gray_->GetStride(FrameBuffer::VPLANE), |
| out_frame->GetData(FrameBuffer::VPLANE), |
| out_frame->GetStride(FrameBuffer::VPLANE), |
| out_frame->GetStride(FrameBuffer::VPLANE), |
| out_frame->GetHeight() / 2); |
| return 0; |
| } |
| case V4L2_PIX_FMT_NV12: // NV12 |
| case V4L2_PIX_FMT_NV12M: { // NM12 |
| libyuv::Convert16To8Plane( |
| reinterpret_cast<const uint16_t*>(in_frame.GetData()), |
| in_frame.GetStride(), |
| temp_i420_buffer_gray_->GetData(FrameBuffer::YPLANE), |
| temp_i420_buffer_gray_->GetStride(FrameBuffer::YPLANE), |
| 256, // scale |
| in_frame.GetWidth(), in_frame.GetHeight()); |
| |
| int res = libyuv::I420ToNV12( |
| temp_i420_buffer_gray_->GetData(FrameBuffer::YPLANE), |
| temp_i420_buffer_gray_->GetStride(FrameBuffer::YPLANE), |
| temp_i420_buffer_gray_->GetData(FrameBuffer::UPLANE), |
| temp_i420_buffer_gray_->GetStride(FrameBuffer::UPLANE), |
| temp_i420_buffer_gray_->GetData(FrameBuffer::VPLANE), |
| temp_i420_buffer_gray_->GetStride(FrameBuffer::VPLANE), |
| out_frame->GetData(FrameBuffer::YPLANE), |
| out_frame->GetStride(FrameBuffer::YPLANE), |
| out_frame->GetData(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), out_frame->GetWidth(), |
| out_frame->GetHeight()); |
| LOGF_IF(ERROR, res) << "I420ToNV12() returns " << res; |
| return res ? -EINVAL : 0; |
| } |
| default: { |
| LOGF(ERROR) << "Not implemented: Y16/Z16 -> " << out_frame->GetFourcc(); |
| return -EINVAL; |
| } |
| } |
| } else { |
| LOGF(ERROR) << "Convert format doesn't support source format " |
| << FormatToString(in_frame.GetFourcc()); |
| return -EINVAL; |
| } |
| } |
| |
| int ImageProcessor::Scale(const FrameBuffer& in_frame, FrameBuffer* out_frame) { |
| if (in_frame.GetFourcc() != V4L2_PIX_FMT_YUV420 && |
| in_frame.GetFourcc() != V4L2_PIX_FMT_YUV420M) { |
| LOGF(ERROR) << "Pixel format " << FormatToString(in_frame.GetFourcc()) |
| << " is unsupported."; |
| return -EINVAL; |
| } |
| |
| VLOGF(1) << "Scale image from " << in_frame.GetWidth() << "x" |
| << in_frame.GetHeight() << " to " << out_frame->GetWidth() << "x" |
| << out_frame->GetHeight(); |
| |
| int ret = libyuv::I420Scale( |
| in_frame.GetData(FrameBuffer::YPLANE), |
| in_frame.GetStride(FrameBuffer::YPLANE), |
| in_frame.GetData(FrameBuffer::UPLANE), |
| in_frame.GetStride(FrameBuffer::UPLANE), |
| in_frame.GetData(FrameBuffer::VPLANE), |
| in_frame.GetStride(FrameBuffer::VPLANE), in_frame.GetWidth(), |
| in_frame.GetHeight(), out_frame->GetData(FrameBuffer::YPLANE), |
| out_frame->GetStride(FrameBuffer::YPLANE), |
| out_frame->GetData(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), |
| out_frame->GetData(FrameBuffer::VPLANE), |
| out_frame->GetStride(FrameBuffer::VPLANE), out_frame->GetWidth(), |
| out_frame->GetHeight(), libyuv::FilterMode::kFilterNone); |
| LOGF_IF(ERROR, ret) << "I420Scale failed: " << ret; |
| return ret; |
| } |
| |
| int ImageProcessor::ProcessForInsetPortraitMode(const FrameBuffer& in_frame, |
| FrameBuffer* out_frame, |
| int rotate_degree) { |
| libyuv::RotationMode rotation_mode = libyuv::RotationMode::kRotate90; |
| switch (rotate_degree) { |
| case 90: |
| rotation_mode = libyuv::RotationMode::kRotate90; |
| break; |
| case 270: |
| rotation_mode = libyuv::RotationMode::kRotate270; |
| break; |
| default: |
| LOGF(ERROR) << "Invalid rotation degree: " << rotate_degree; |
| return -EINVAL; |
| } |
| |
| VLOGF(1) << "Crop and rotate image, rotate degree: " << rotate_degree; |
| |
| int margin = (in_frame.GetWidth() - out_frame->GetHeight()) / 2; |
| // Crop from even pixels. |
| margin &= ~1; |
| |
| if (in_frame.GetFourcc() == V4L2_PIX_FMT_YUV420 || |
| in_frame.GetFourcc() == V4L2_PIX_FMT_YUV420M) { |
| int ret = |
| I420Rotate(in_frame.GetData(FrameBuffer::YPLANE) + margin, |
| in_frame.GetStride(FrameBuffer::YPLANE), |
| in_frame.GetData(FrameBuffer::UPLANE) + margin / 2, |
| in_frame.GetStride(FrameBuffer::UPLANE), |
| in_frame.GetData(FrameBuffer::VPLANE) + margin / 2, |
| in_frame.GetStride(FrameBuffer::VPLANE), |
| out_frame->GetData(FrameBuffer::YPLANE), |
| out_frame->GetStride(FrameBuffer::YPLANE), |
| out_frame->GetData(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), |
| out_frame->GetData(FrameBuffer::VPLANE), |
| out_frame->GetStride(FrameBuffer::VPLANE), |
| out_frame->GetHeight(), in_frame.GetHeight(), rotation_mode); |
| if (ret) { |
| LOGF(ERROR) << "I420Rotate failed: " << ret; |
| return ret; |
| } |
| } else if (in_frame.GetFourcc() == V4L2_PIX_FMT_NV12 || |
| in_frame.GetFourcc() == V4L2_PIX_FMT_NV12M) { |
| int ret = NV12ToI420Rotate(in_frame.GetData(FrameBuffer::YPLANE) + margin, |
| in_frame.GetStride(FrameBuffer::YPLANE), |
| in_frame.GetData(FrameBuffer::UPLANE) + margin, |
| in_frame.GetStride(FrameBuffer::UPLANE), |
| out_frame->GetData(FrameBuffer::YPLANE), |
| out_frame->GetStride(FrameBuffer::YPLANE), |
| out_frame->GetData(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), |
| out_frame->GetData(FrameBuffer::VPLANE), |
| out_frame->GetStride(FrameBuffer::VPLANE), |
| out_frame->GetHeight(), in_frame.GetHeight(), |
| rotation_mode); |
| if (ret) { |
| LOGF(ERROR) << "NV12ToI420Rotate failed: " << ret; |
| return ret; |
| } |
| } else { |
| LOGF(ERROR) << "Pixel format " << FormatToString(in_frame.GetFourcc()) |
| << " is unsupported."; |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| int ImageProcessor::Crop(const FrameBuffer& in_frame, FrameBuffer* out_frame) { |
| VLOGF(1) << "Crop from " << in_frame.GetWidth() << "x" << in_frame.GetHeight() |
| << "," << FormatToString(in_frame.GetFourcc()) << " to " |
| << out_frame->GetWidth() << "x" << out_frame->GetHeight() << "," |
| << FormatToString(out_frame->GetFourcc()); |
| if (out_frame->GetWidth() > in_frame.GetWidth() || |
| out_frame->GetHeight() > in_frame.GetHeight()) { |
| LOGF(ERROR) << "Crop to larger size"; |
| return -EINVAL; |
| } |
| |
| int crop_x = (in_frame.GetWidth() - out_frame->GetWidth()) / 2; |
| int crop_y = (in_frame.GetHeight() - out_frame->GetHeight()) / 2; |
| // Crop from even pixels for correct YUV image. |
| crop_x &= ~1; |
| crop_y &= ~1; |
| |
| if (in_frame.GetFourcc() == V4L2_PIX_FMT_YUV420 || |
| in_frame.GetFourcc() == V4L2_PIX_FMT_YUV420M) { |
| int ret = libyuv::I420Copy( |
| in_frame.GetData(FrameBuffer::YPLANE) + |
| in_frame.GetStride(FrameBuffer::YPLANE) * crop_y + crop_x, |
| in_frame.GetStride(FrameBuffer::YPLANE), |
| in_frame.GetData(FrameBuffer::UPLANE) + |
| in_frame.GetStride(FrameBuffer::UPLANE) * crop_y / 2 + crop_x / 2, |
| in_frame.GetStride(FrameBuffer::UPLANE), |
| in_frame.GetData(FrameBuffer::VPLANE) + |
| in_frame.GetStride(FrameBuffer::VPLANE) * crop_y / 2 + crop_x / 2, |
| in_frame.GetStride(FrameBuffer::VPLANE), |
| out_frame->GetData(FrameBuffer::YPLANE), |
| out_frame->GetStride(FrameBuffer::YPLANE), |
| out_frame->GetData(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), |
| out_frame->GetData(FrameBuffer::VPLANE), |
| out_frame->GetStride(FrameBuffer::VPLANE), out_frame->GetWidth(), |
| out_frame->GetHeight()); |
| if (ret) { |
| LOGF(ERROR) << "I420Copy failed: " << ret; |
| return ret; |
| } |
| } else if (in_frame.GetFourcc() == V4L2_PIX_FMT_NV12 || |
| in_frame.GetFourcc() == V4L2_PIX_FMT_NV12M) { |
| int ret = libyuv::NV12ToI420( |
| in_frame.GetData(FrameBuffer::YPLANE) + |
| in_frame.GetStride(FrameBuffer::YPLANE) * crop_y + crop_x, |
| in_frame.GetStride(FrameBuffer::YPLANE), |
| in_frame.GetData(FrameBuffer::UPLANE) + |
| in_frame.GetStride(FrameBuffer::UPLANE) * crop_y / 2 + crop_x, |
| in_frame.GetStride(FrameBuffer::UPLANE), |
| out_frame->GetData(FrameBuffer::YPLANE), |
| out_frame->GetStride(FrameBuffer::YPLANE), |
| out_frame->GetData(FrameBuffer::UPLANE), |
| out_frame->GetStride(FrameBuffer::UPLANE), |
| out_frame->GetData(FrameBuffer::VPLANE), |
| out_frame->GetStride(FrameBuffer::VPLANE), out_frame->GetWidth(), |
| out_frame->GetHeight()); |
| if (ret) { |
| LOGF(ERROR) << "NV12ToI420 failed: " << ret; |
| return ret; |
| } |
| } else { |
| LOGF(ERROR) << "Pixel format " << FormatToString(in_frame.GetFourcc()) |
| << " is unsupported."; |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| } // namespace cros |