From patchwork Thu Jan 28 01:13:50 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hirokazu Honda X-Patchwork-Id: 11035 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 27F27BD808 for ; Thu, 28 Jan 2021 01:14:03 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E9BBD6837B; Thu, 28 Jan 2021 02:14:02 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="IC1Fei2K"; dkim-atps=neutral Received: from mail-pg1-x52c.google.com (mail-pg1-x52c.google.com [IPv6:2607:f8b0:4864:20::52c]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D6A9468377 for ; Thu, 28 Jan 2021 02:14:00 +0100 (CET) Received: by mail-pg1-x52c.google.com with SMTP id g15so3171824pgu.9 for ; Wed, 27 Jan 2021 17:14:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=uIgQNllzvkJ3HX1gSkPuKm1T4RZDIXpZVpTGD8HE0l0=; b=IC1Fei2KKStTz3uQiMOSNjlTdG1B4JQCJTaEMrUelx0ukzMEOJSXLYHFTfslyn+rr2 /hHWnRdpd5rXD0QicCjCGPVJzfALGupNs2v1v7wh5E+xBk3oYWuPuUnQcywqo3WhS4bm W5tMYz7lTYP78pN8rnmUm8KoqvBqTZiv5Ue3A= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=uIgQNllzvkJ3HX1gSkPuKm1T4RZDIXpZVpTGD8HE0l0=; b=H6Domd0yNeuwIniVW1esb1gsaUl7ABfR+v6lM9Pk8XLo6isQuiDYfAHbhbXPuvmMNo I4XbGBm36J+ahh+dCVgtC1yd/f1oQTlO0tVWDWdnZw0zuEB5sDX1o1BVqJj+zqCYBA6S GOIejqknzr+5DIEaeehHgYtZC2nJm9Kq/2b5S8n0bVsNuXS0usImUA2GXG+SOyNIAnxO HMsNLs4Ni6BNhqFoL9RIB8HuZ8CP2TMECpGwgNL++8p3+mtrle5y2y3DmAJS3oZOrCEY Gh8wlb/wu5i1LWYnaV9tlEoLjVDTw87/Be3YTIXuQClcSrLKoHKzVWG50xmN/XhYno3X uYeQ== X-Gm-Message-State: AOAM530F90xmhQjaC0uEX9NjC8wG01cqYWCwPm1eq6H/3wXSXiAssz5Q bSCNWj9l1g94nQ4SmxqgHxBnpn5r063aDFBt X-Google-Smtp-Source: ABdhPJymjxP4FHUktXDkNSvIZ6q5SdzSPtnUankmf+WQUYv4c+cUf3vyUw8gTLTp1pA3ogJzfj0yXw== X-Received: by 2002:a63:710:: with SMTP id 16mr10023412pgh.73.1611796438869; Wed, 27 Jan 2021 17:13:58 -0800 (PST) Received: from hiroh.c.googlers.com.com (128.141.236.35.bc.googleusercontent.com. [35.236.141.128]) by smtp.gmail.com with ESMTPSA id br21sm3331103pjb.9.2021.01.27.17.13.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 27 Jan 2021 17:13:58 -0800 (PST) From: Hirokazu Honda To: libcamera-devel@lists.libcamera.org Date: Thu, 28 Jan 2021 01:13:50 +0000 Message-Id: <20210128011351.2495050-2-hiroh@chromium.org> X-Mailer: git-send-email 2.30.0.280.ga3ce27912f-goog In-Reply-To: <20210128011351.2495050-1-hiroh@chromium.org> References: <20210128011351.2495050-1-hiroh@chromium.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 2/2] android: libyuv: Introduce PostProcessorLibyuv X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" This adds PostProcessorLibyuv. It supports NV12 buffer scaling. Signed-off-by: Hirokazu Honda --- src/android/libyuv/post_processor_libyuv.cpp | 123 +++++++++++++++++++ src/android/libyuv/post_processor_libyuv.h | 44 +++++++ src/android/meson.build | 1 + 3 files changed, 168 insertions(+) create mode 100644 src/android/libyuv/post_processor_libyuv.cpp create mode 100644 src/android/libyuv/post_processor_libyuv.h -- 2.30.0.280.ga3ce27912f-goog diff --git a/src/android/libyuv/post_processor_libyuv.cpp b/src/android/libyuv/post_processor_libyuv.cpp new file mode 100644 index 00000000..81f237e0 --- /dev/null +++ b/src/android/libyuv/post_processor_libyuv.cpp @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Google Inc. + * + * post_processor_libyuv.cpp - Post Processor using libyuv + */ + +#include "post_processor_libyuv.h" + +#include + +#include +#include +#include +#include +#include + +using namespace libcamera; + +LOG_DEFINE_CATEGORY(LIBYUV) + +int PostProcessorLibyuv::configure(const StreamConfiguration &inCfg, + const StreamConfiguration &outCfg) +{ + if (inCfg.pixelFormat != outCfg.pixelFormat) { + LOG(LIBYUV, Error) + << "Pixel format conversion is not supported" + << " (from " << inCfg.toString() + << " to " << outCfg.toString() << ")"; + return -EINVAL; + } + + if (inCfg.size < outCfg.size) { + LOG(LIBYUV, Error) << "Up-scaling is not supported" + << " (from " << inCfg.toString() + << " to " << outCfg.toString() << ")"; + return -EINVAL; + } + + if (inCfg.pixelFormat == formats::NV12) { + LOG(LIBYUV, Error) << "Unsupported format " << inCfg.pixelFormat + << " (only NV12 is supported)"; + return -EINVAL; + } + + getNV12LengthAndStride(inCfg.size, sourceLength_, sourceStride_); + getNV12LengthAndStride(outCfg.size, destinationLength_, + destinationStride_); + return 0; +} + +int PostProcessorLibyuv::process(const FrameBuffer &source, + libcamera::MappedBuffer *destination, + [[maybe_unused]] const CameraMetadata &requestMetadata, + [[maybe_unused]] CameraMetadata *metadata) +{ + if (!isValidNV12Buffers(source, *destination)) + return -EINVAL; + + const MappedFrameBuffer sourceMapped(&source, PROT_READ); + if (!sourceMapped.isValid()) { + LOG(LIBYUV, Error) << "Failed to mmap camera frame buffer"; + return -EINVAL; + } + + return 0 == libyuv::NV12Scale(sourceMapped.maps()[0].data(), + sourceStride_[0], + sourceMapped.maps()[1].data(), + sourceStride_[1], + sourceSize_.width, sourceSize_.height, + destination->maps()[0].data(), + destinationStride_[0], + destination->maps()[1].data(), + destinationStride_[1], + destinationSize_.width, + destinationSize_.height, + libyuv::FilterMode::kFilterBilinear); +} + +bool PostProcessorLibyuv::isValidNV12Buffers( + const FrameBuffer &source, + const libcamera::MappedBuffer &destination) const +{ + if (source.planes().size() != 2u) { + LOG(LIBYUV, Error) << "The number of source planes is not 2"; + return false; + } + if (destination.maps().size() != 2u) { + LOG(LIBYUV, Error) + << "The number of destination planes is not 2"; + return false; + } + + if (source.planes()[0].length < sourceLength_[0] || + source.planes()[1].length < sourceLength_[1]) { + LOG(LIBYUV, Error) + << "The source planes lengths are too small"; + return false; + } + if (destination.maps()[0].size() < destinationLength_[0] || + destination.maps()[1].size() < destinationLength_[1]) { + LOG(LIBYUV, Error) + << "The destination planes lengths are too small"; + return false; + } + + return true; +} + +// static +void PostProcessorLibyuv::getNV12LengthAndStride(Size size, + unsigned int length[2], + unsigned int stride[2]) +{ + const PixelFormatInfo &nv12Info = PixelFormatInfo::info(formats::NV12); + for (unsigned int i = 0; i < 2; i++) { + const unsigned int vertSubSample = + nv12Info.planes[i].verticalSubSampling; + length[i] = nv12Info.stride(size.width, i, /*align=*/1) * + ((size.height + vertSubSample - 1) / vertSubSample); + stride[i] = nv12Info.stride(size.width, i, 1); + } +} diff --git a/src/android/libyuv/post_processor_libyuv.h b/src/android/libyuv/post_processor_libyuv.h new file mode 100644 index 00000000..a65a16b3 --- /dev/null +++ b/src/android/libyuv/post_processor_libyuv.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Google Inc. + * + * post_processor_libyuv.h - Post Processor using libyuv + */ +#ifndef __ANDROID_POST_PROCESSOR_LIBYUV_H__ +#define __ANDROID_POST_PROCESSOR_LIBYUV_H__ + +#include + +#include "../post_processor.h" + +class CameraDevice; + +class PostProcessorLibyuv : public PostProcessor +{ +public: + PostProcessorLibyuv() = default; + + int configure(const libcamera::StreamConfiguration &incfg, + const libcamera::StreamConfiguration &outcfg) override; + int process(const libcamera::FrameBuffer &source, + libcamera::MappedBuffer *destination, + const CameraMetadata & /*requestMetadata*/, + CameraMetadata *metadata) override; + +private: + bool isValidNV12Buffers(const libcamera::FrameBuffer &source, + const libcamera::MappedBuffer &destination) const; + + static void getNV12LengthAndStride(libcamera::Size size, + unsigned int length[2], + unsigned int stride[2]); + + libcamera::Size sourceSize_; + libcamera::Size destinationSize_; + unsigned int sourceLength_[2] = {}; + unsigned int destinationLength_[2] = {}; + unsigned int sourceStride_[2] = {}; + unsigned int destinationStride_[2] = {}; +}; + +#endif /* __ANDROID_POST_PROCESSOR_LIBYUV_H__ */ diff --git a/src/android/meson.build b/src/android/meson.build index e1533d7c..cbe09200 100644 --- a/src/android/meson.build +++ b/src/android/meson.build @@ -44,6 +44,7 @@ android_hal_sources = files([ 'jpeg/exif.cpp', 'jpeg/post_processor_jpeg.cpp', 'jpeg/thumbnailer.cpp', + 'libyuv/post_processor_libyuv.cpp' ]) android_camera_metadata_sources = files([