From patchwork Tue Jul 21 22:01:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 8910 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 498F6C2E68 for ; Tue, 21 Jul 2020 22:01:36 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 16BBA6099C; Wed, 22 Jul 2020 00:01:36 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ME8STVyK"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A540160923 for ; Wed, 22 Jul 2020 00:01:32 +0200 (CEST) Received: from localhost.localdomain (cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 07B77585; Wed, 22 Jul 2020 00:01:30 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1595368891; bh=4EDcEmP0yH0t+8WP9R39WQ6pcFZhIjGrKnzfKWFNVpc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ME8STVyKogGi8p7xtmzTnMkdmn74wlbBgxXss3czPs6WUtp9KWQIbfW7uF44i8GJi WHo8JNIUP9wQuBH9QTSHQRliWorZOVMowyvhT/ApGyAzwzyon3f5vSg8XOexe/ybnM +eMYHbo5U8p0yfBjahiL7HfFzqo8fAqr6EGsIapE= From: Kieran Bingham To: libcamera devel Date: Tue, 21 Jul 2020 23:01:21 +0100 Message-Id: <20200721220126.202065-2-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200721220126.202065-1-kieran.bingham@ideasonboard.com> References: <20200721220126.202065-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH 1/6] android: Introduce JPEG compression 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" Provide a compressor interface and implement a JPEG compressor using libjpeg. Signed-off-by: Kieran Bingham --- src/android/jpeg/compressor.h | 28 +++ src/android/jpeg/compressor_jpeg.cpp | 279 +++++++++++++++++++++++++++ src/android/jpeg/compressor_jpeg.h | 44 +++++ src/android/meson.build | 1 + src/libcamera/meson.build | 2 + 5 files changed, 354 insertions(+) create mode 100644 src/android/jpeg/compressor.h create mode 100644 src/android/jpeg/compressor_jpeg.cpp create mode 100644 src/android/jpeg/compressor_jpeg.h diff --git a/src/android/jpeg/compressor.h b/src/android/jpeg/compressor.h new file mode 100644 index 000000000000..f95e4a4539cb --- /dev/null +++ b/src/android/jpeg/compressor.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * compressor.h - Image compression interface + */ +#ifndef __LIBCAMERA_COMPRESSOR_H__ +#define __LIBCAMERA_COMPRESSOR_H__ + +#include +#include + +struct CompressedImage { + unsigned char *data; + unsigned long length; +}; + +class Compressor +{ +public: + virtual ~Compressor() { }; + + virtual int configure(const libcamera::StreamConfiguration &cfg) = 0; + virtual int compress(const libcamera::FrameBuffer *source, CompressedImage *image) = 0; + virtual void free(CompressedImage *image) = 0; +}; + +#endif /* __LIBCAMERA_COMPRESSOR_H__ */ diff --git a/src/android/jpeg/compressor_jpeg.cpp b/src/android/jpeg/compressor_jpeg.cpp new file mode 100644 index 000000000000..78fa5e399d99 --- /dev/null +++ b/src/android/jpeg/compressor_jpeg.cpp @@ -0,0 +1,279 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * compressor_jpeg.cpp - JPEG compression using libjpeg native API + */ + +#include "compressor_jpeg.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "libcamera/internal/log.h" + +using namespace libcamera; + +LOG_DEFINE_CATEGORY(JPEG) + +struct PixelFormatPlaneInfo { + unsigned int bitsPerPixel; + unsigned int hSubSampling; + unsigned int vSubSampling; +}; + +struct PixelFormatInfo { + J_COLOR_SPACE colorSpace; + unsigned int numPlanes; + bool nvSwap; + PixelFormatPlaneInfo planes[3]; +}; + +namespace { + +static const std::map pixelInfo{ + { formats::R8, { JCS_GRAYSCALE, 1, false, { { 8, 1, 1 }, { 0, 0, 0 }, { 0, 0, 0 } } } }, + + /* RGB formats. */ + { formats::RGB888, { JCS_EXT_BGR, 1, false, { { 24, 1, 1 }, { 0, 0, 0 }, { 0, 0, 0 } } } }, + { formats::BGR888, { JCS_EXT_RGB, 1, false, { { 24, 1, 1 }, { 0, 0, 0 }, { 0, 0, 0 } } } }, + + /* YUV packed formats. */ + { formats::UYVY, { JCS_YCbCr, 1, false, { { 16, 1, 1 }, { 0, 0, 0 }, { 0, 0, 0 } } } }, + { formats::VYUY, { JCS_YCbCr, 1, false, { { 16, 1, 1 }, { 0, 0, 0 }, { 0, 0, 0 } } } }, + { formats::YUYV, { JCS_YCbCr, 1, false, { { 16, 1, 1 }, { 0, 0, 0 }, { 0, 0, 0 } } } }, + { formats::YVYU, { JCS_YCbCr, 1, false, { { 16, 1, 1 }, { 0, 0, 0 }, { 0, 0, 0 } } } }, + + /* YUY planar formats. */ + { formats::NV12, { JCS_YCbCr, 2, false, { { 8, 1, 1 }, { 16, 2, 2 }, { 0, 0, 0 } } } }, + { formats::NV21, { JCS_YCbCr, 2, true, { { 8, 1, 1 }, { 16, 2, 2 }, { 0, 0, 0 } } } }, + { formats::NV16, { JCS_YCbCr, 2, false, { { 8, 1, 1 }, { 16, 2, 1 }, { 0, 0, 0 } } } }, + { formats::NV61, { JCS_YCbCr, 2, true, { { 8, 1, 1 }, { 16, 2, 1 }, { 0, 0, 0 } } } }, + { formats::NV24, { JCS_YCbCr, 2, false, { { 8, 1, 1 }, { 16, 1, 1 }, { 0, 0, 0 } } } }, + { formats::NV42, { JCS_YCbCr, 2, true, { { 8, 1, 1 }, { 16, 1, 1 }, { 0, 0, 0 } } } }, +}; + +} + +const struct PixelFormatInfo &findPixelInfo(const PixelFormat &format) +{ + static const struct PixelFormatInfo invalidPixelFormat { + JCS_UNKNOWN, 0, false, {} + }; + + const auto iter = pixelInfo.find(format); + if (iter == pixelInfo.end()) { + LOG(JPEG, Error) << "Unsupported pixel format for JPEG compressor: " + << format.toString(); + return invalidPixelFormat; + } + + return iter->second; +} + +CompressorJPEG::CompressorJPEG() + : quality_(95) +{ + /* \todo: Expand error handling coverage. */ + compress_.err = jpeg_std_error(&jerr_); + + jpeg_create_compress(&compress_); +} + +CompressorJPEG::~CompressorJPEG() +{ + jpeg_destroy_compress(&compress_); +} + +int CompressorJPEG::configure(const StreamConfiguration &cfg) +{ + { + LOG(JPEG, Warning) << "Configuring pixelformat as : " + << cfg.pixelFormat.toString(); + LOG(JPEG, Warning) << " : " << cfg.toString(); + + std::vector formats = cfg.formats().pixelformats(); + LOG(JPEG, Warning) << "StreamConfiguration supports " << formats.size() << " formats:"; + for (const PixelFormat &format : formats) + LOG(JPEG, Warning) << " - " << format.toString(); + } + + const struct PixelFormatInfo info = findPixelInfo(cfg.pixelFormat); + if (info.colorSpace == JCS_UNKNOWN) + return -ENOTSUP; + + /* + * Todo: The stride given by the stream configuration has caused issues. + * Validate it and also handle per-plane strides. + */ + stride_ = cfg.stride; + stride_ = cfg.size.width * info.planes[0].bitsPerPixel / 8; + /* Saw some errors with strides, so this is a debug/develop check */ + if (cfg.stride != stride_) + LOG(JPEG, Error) << "*** StreamConfigure provided stride of " + << cfg.stride << " rather than " << stride_; + + compress_.image_width = cfg.size.width; + compress_.image_height = cfg.size.height; + compress_.in_color_space = info.colorSpace; + + compress_.input_components = info.colorSpace == JCS_GRAYSCALE ? 1 : 3; + + jpeg_set_defaults(&compress_); + jpeg_set_quality(&compress_, quality_, TRUE); + + nv_ = info.numPlanes == 2; + nvSwap_ = info.nvSwap; + + /* Use the 'last' plane for subsampling of component info. */ + unsigned int p = info.numPlanes - 1; + horzSubSample_ = info.planes[p].hSubSampling; + vertSubSample_ = info.planes[p].vSubSampling; + + return 0; +} + +void CompressorJPEG::compressRGB(const libcamera::MappedFrameBuffer *frame) +{ + unsigned char *src = static_cast(frame->maps()[0].address); + + JSAMPROW row_pointer[1]; + + while (compress_.next_scanline < compress_.image_height) { + row_pointer[0] = &src[compress_.next_scanline * stride_]; + jpeg_write_scanlines(&compress_, row_pointer, 1); + } +} + +/* + * A very dull implementation to compress YUYV. + * To be converted to a generic algorithm akin to NV12. + * If it can be shared with NV12 great, but we might be able to further + * optimisze the NV layouts by only depacking the CrCb pixels. + */ +void CompressorJPEG::compressYUV(const libcamera::MappedFrameBuffer *frame) +{ + std::vector tmprowbuf(compress_.image_width * 3); + unsigned char *input = static_cast(frame->maps()[0].address); + + JSAMPROW row_pointer[1]; + row_pointer[0] = &tmprowbuf[0]; + while (compress_.next_scanline < compress_.image_height) { + unsigned i, j; + unsigned offset = compress_.next_scanline * compress_.image_width * 2; //offset to the correct row + for (i = 0, j = 0; i < compress_.image_width * 2; i += 4, j += 6) { //input strides by 4 bytes, output strides by 6 (2 pixels) + tmprowbuf[j + 0] = input[offset + i + 0]; // Y (unique to this pixel) + tmprowbuf[j + 1] = input[offset + i + 1]; // U (shared between pixels) + tmprowbuf[j + 2] = input[offset + i + 3]; // V (shared between pixels) + tmprowbuf[j + 3] = input[offset + i + 2]; // Y (unique to this pixel) + tmprowbuf[j + 4] = input[offset + i + 1]; // U (shared between pixels) + tmprowbuf[j + 5] = input[offset + i + 3]; // V (shared between pixels) + } + jpeg_write_scanlines(&compress_, row_pointer, 1); + } +} + +/* + * Really inefficient NV unpacking to YUV888 for JPEG compress. + * This /could/ be improved (drastically I hope) ;-) + */ +void CompressorJPEG::compressNV(const libcamera::MappedFrameBuffer *frame) +{ + std::vector tmprowbuf(compress_.image_width * 3); + + /* + * Todo: Use the raw api, and only unpack the cb/cr samples to new line buffers. + * If possible, see if we can set appropriate pixel strides too to save even that copy. + * + * Possible hints at: + * https://sourceforge.net/p/libjpeg/mailman/message/30815123/ + */ + unsigned int c_stride = compress_.image_width * (2 / horzSubSample_); + unsigned int c_inc = horzSubSample_ == 1 ? 2 : 0; + unsigned int cb_pos = nvSwap_ ? 1 : 0; + unsigned int cr_pos = nvSwap_ ? 0 : 1; + + const unsigned char *src = static_cast(frame->maps()[0].address); + const unsigned char *src_c = src + compress_.image_width * compress_.image_height; // * stride[0] surely + + JSAMPROW row_pointer[1]; + row_pointer[0] = &tmprowbuf[0]; + + for (unsigned int y = 0; y < compress_.image_height; y++) { + unsigned char *dst = &tmprowbuf[0]; + + const unsigned char *src_y = src + y * compress_.image_width; + const unsigned char *src_cb = src_c + (y / vertSubSample_) * + c_stride + cb_pos; + const unsigned char *src_cr = src_c + (y / vertSubSample_) * + c_stride + cr_pos; + + for (unsigned int x = 0; x < compress_.image_width; x += 2) { + dst[0] = *src_y; + dst[1] = *src_cb; + dst[2] = *src_cr; + src_y++; + src_cb += c_inc; + src_cr += c_inc; + dst += 3; + + dst[0] = *src_y; + dst[1] = *src_cb; + dst[2] = *src_cr; + src_y++; + src_cb += 2; + src_cr += 2; + dst += 3; + } + + jpeg_write_scanlines(&compress_, row_pointer, 1); + } +} + +int CompressorJPEG::compress(const FrameBuffer *source, CompressedImage *jpeg) +{ + MappedFrameBuffer frame(source, PROT_READ); + if (!frame.isValid()) { + LOG(JPEG, Error) << "Failed to map FrameBuffer : " + << strerror(frame.error()); + return -frame.error(); + } + + jpeg_mem_dest(&compress_, &jpeg->data, &jpeg->length); + + jpeg_start_compress(&compress_, TRUE); + + LOG(JPEG, Debug) << "JPEG Compress Starting"; + LOG(JPEG, Debug) << "Width: " << compress_.image_width + << " height: " << compress_.image_height + << " stride: " << stride_; + + if (nv_) + compressNV(&frame); + else if (compress_.in_color_space == JCS_YCbCr) + compressYUV(&frame); + else + compressRGB(&frame); + + LOG(JPEG, Debug) << "JPEG Compress Completed"; + + jpeg_finish_compress(&compress_); + + return 0; +} + +void CompressorJPEG::free(CompressedImage *jpeg) +{ + ::free(jpeg->data); + jpeg->data = nullptr; +} diff --git a/src/android/jpeg/compressor_jpeg.h b/src/android/jpeg/compressor_jpeg.h new file mode 100644 index 000000000000..11009481a2fe --- /dev/null +++ b/src/android/jpeg/compressor_jpeg.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * compressor_jpeg.h - JPEG compression using libjpeg + */ +#ifndef __COMPRESSOR_JPEG_H__ +#define __COMPRESSOR_JPEG_H__ + +#include "compressor.h" + +#include +#include + +#include + +class CompressorJPEG : public Compressor +{ +public: + CompressorJPEG(); + ~CompressorJPEG(); + + int configure(const libcamera::StreamConfiguration &cfg); + int compress(const libcamera::FrameBuffer *source, CompressedImage *jpeg); + void free(CompressedImage *jpeg); + +private: + void compressRGB(const libcamera::MappedFrameBuffer *frame); + void compressYUV(const libcamera::MappedFrameBuffer *frame); + void compressNV(const libcamera::MappedFrameBuffer *frame); + + struct jpeg_compress_struct compress_; + struct jpeg_error_mgr jerr_; + + unsigned int quality_; + unsigned int stride_; + + bool nv_; + bool nvSwap_; + unsigned int horzSubSample_; + unsigned int vertSubSample_; +}; + +#endif /* __COMPRESSOR_JPEG_H__ */ diff --git a/src/android/meson.build b/src/android/meson.build index 822cad621f01..51dcd99ee62f 100644 --- a/src/android/meson.build +++ b/src/android/meson.build @@ -6,6 +6,7 @@ android_hal_sources = files([ 'camera_device.cpp', 'camera_metadata.cpp', 'camera_ops.cpp', + 'jpeg/compressor_jpeg.cpp', ]) android_camera_metadata_sources = files([ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 3aad4386ffc2..d78e2c1f6eb8 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -124,6 +124,8 @@ if get_option('android') libcamera_sources += android_hal_sources includes += android_includes libcamera_link_with += android_camera_metadata + + libcamera_deps += dependency('libjpeg') endif # We add '/' to the build_rpath as a 'safe' path to act as a boolean flag. From patchwork Tue Jul 21 22:01:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 8911 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 6EAB0C2E68 for ; Tue, 21 Jul 2020 22:01:37 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8FB67609A2; Wed, 22 Jul 2020 00:01:36 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="a62ZyhAF"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B586B6093B for ; Wed, 22 Jul 2020 00:01:32 +0200 (CEST) Received: from localhost.localdomain (cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 613ECA58; Wed, 22 Jul 2020 00:01:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1595368891; bh=WDrrFmfz091GJHNh2ME1Ki/sACWbRrIOaIjhNnjAkto=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=a62ZyhAFrUgbAV0tc8kttgfW5Um2f7IaSQy2EKoxzS5RPabLWKvOZHPnHf5N+MmWD wH0N7WpL2TY3QT0IQsU/Nv3G4X3r+Jv/M6odRHEr4DR7WKZscJxdjNpe+QQ5RmqI6d zZPOjJTl5nRl3q5qqeUwrejhPdiAobD90JlkiVsk= From: Kieran Bingham To: libcamera devel Date: Tue, 21 Jul 2020 23:01:22 +0100 Message-Id: <20200721220126.202065-3-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200721220126.202065-1-kieran.bingham@ideasonboard.com> References: <20200721220126.202065-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH 2/6] android: camera_device: Report configuration changes from validate() 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" When we call validate on a configuration, if there are any adjustments on the configuration, we fail without showing why. Display the stream configuration after the validate stage to aid debugging stream startup failures. Signed-off-by: Kieran Bingham --- src/android/camera_device.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index f51542b282d5..3f3d7857f0ab 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -1032,6 +1032,14 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list) break; case CameraConfiguration::Adjusted: LOG(HAL, Info) << "Camera configuration adjusted"; + + for (unsigned int i = 0; i < stream_list->num_streams; ++i) { + CameraStream *cameraStream = &streams_[i]; + StreamConfiguration &cfg = config_->at(cameraStream->index); + + LOG(HAL, Info) << i << " : " << cfg.toString(); + } + config_.reset(); return -EINVAL; case CameraConfiguration::Invalid: From patchwork Tue Jul 21 22:01:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 8912 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 DAC48C2E68 for ; Tue, 21 Jul 2020 22:01:37 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4FD0560943; Wed, 22 Jul 2020 00:01:37 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="HYzu6UXY"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E585C6093F for ; Wed, 22 Jul 2020 00:01:32 +0200 (CEST) Received: from localhost.localdomain (cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id BAD281229; Wed, 22 Jul 2020 00:01:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1595368891; bh=aR6pbl4HFuMWZ97vbpuQwd2fSxu2mCwmBotV6PbSAf4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HYzu6UXYO3bbiXXxsELF4itXM1xsUzL8R1dLSM8OeR5g3BA4I69GmuI5XxSVMzB2r HSEbGsOOuO3pE2bZcmAJSsN7Pv0EdYroMzvSKMLN+x0TSVfxXuCDW9PAiRcce281Xf IHS5HjagRbrGSoZlJ6DDj4zoSNhDUW6ZDlvXX8FQ= From: Kieran Bingham To: libcamera devel Date: Tue, 21 Jul 2020 23:01:23 +0100 Message-Id: <20200721220126.202065-4-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200721220126.202065-1-kieran.bingham@ideasonboard.com> References: <20200721220126.202065-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH 3/6] android: camera_device: Add ANDROID_JPEG_MAX_SIZE 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" Provide a key for ANDROID_JPEG_MAX_SIZE. The value of 13MB is currently arbitrary, but taken from the USB Camera HAL in CrOS platform2: // android.jpeg update_static(ANDROID_JPEG_MAX_SIZE, int32_t{13 << 20}); update_request(ANDROID_JPEG_QUALITY, uint8_t{90}); update_request(ANDROID_JPEG_THUMBNAIL_QUALITY, uint8_t{90}); update_request(ANDROID_JPEG_ORIENTATION, int32_t{0}); Note: The metadata entries/size updates are probably wrong, and should be automated. Signed-off-by: Kieran Bingham --- src/android/camera_device.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 3f3d7857f0ab..56652ac57676 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -407,10 +407,10 @@ std::tuple CameraDevice::calculateStaticMetadataSize() { /* * \todo Keep this in sync with the actual number of entries. - * Currently: 50 entries, 647 bytes of static metadata + * Currently: 51 entries, 651 bytes of static metadata */ - uint32_t numEntries = 50; - uint32_t byteSize = 647; + uint32_t numEntries = 51; + uint32_t byteSize = 651; /* * Calculate space occupation in bytes for dynamically built metadata @@ -567,6 +567,9 @@ const camera_metadata_t *CameraDevice::getStaticMetadata() availableThumbnailSizes.data(), availableThumbnailSizes.size()); + int32_t jpegMaxSize = 13 << 20; /* 13631488 from USB HAL */ + staticMetadata_->addEntry(ANDROID_JPEG_MAX_SIZE, &jpegMaxSize, 1); + /* Sensor static metadata. */ int32_t pixelArraySize[] = { 2592, 1944, @@ -773,6 +776,7 @@ const camera_metadata_t *CameraDevice::getStaticMetadata() ANDROID_CONTROL_AWB_LOCK_AVAILABLE, ANDROID_CONTROL_AVAILABLE_MODES, ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, + ANDROID_JPEG_MAX_SIZE, ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, ANDROID_SENSOR_INFO_SENSITIVITY_RANGE, From patchwork Tue Jul 21 22:01:24 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 8913 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 4CB11C2E68 for ; Tue, 21 Jul 2020 22:01:38 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 06613609A9; Wed, 22 Jul 2020 00:01:38 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="WWbxNmv2"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id EB4DB60943 for ; Wed, 22 Jul 2020 00:01:32 +0200 (CEST) Received: from localhost.localdomain (cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 2049D51A; Wed, 22 Jul 2020 00:01:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1595368892; bh=8FCCc/j7kEX8ddGZY6kMswMJEatvfKkxggXUodGUX+o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WWbxNmv223Tx06jMQsqLvhbhXbRIvzRGYwtSvZpLT8eFTPIhkvDxw1Iw6qac9toaS nRJ9tzt0siSW9QP2aatxxYYae/mAwhtdCYk7N9JkjVLBJDef8Mo6jxJDw8JJ0YD9Up attsMZyLPRwvnKktVwxGASO1Rt0AXAxV66lE5FRE= From: Kieran Bingham To: libcamera devel Date: Tue, 21 Jul 2020 23:01:24 +0100 Message-Id: <20200721220126.202065-5-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200721220126.202065-1-kieran.bingham@ideasonboard.com> References: <20200721220126.202065-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH 4/6] android: camera_device: Support MJPEG stream construction 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" MJPEG streams must be created referencing a libcamera stream. This stream may already be provided by the request configuration, in which case the existing stream is utilised. If no compatible stream is available to encode, a new stream is requested from the libcamera configuration. Signed-off-by: Kieran Bingham --- src/android/camera_device.cpp | 158 +++++++++++++++++++++++++++++++++- src/android/camera_device.h | 8 ++ 2 files changed, 162 insertions(+), 4 deletions(-) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 56652ac57676..7323d4e58f68 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -8,6 +8,7 @@ #include "camera_device.h" #include "camera_ops.h" +#include #include #include @@ -21,6 +22,8 @@ #include "camera_metadata.h" #include "system/graphics.h" +#include "jpeg/compressor_jpeg.h" + using namespace libcamera; namespace { @@ -1004,6 +1007,7 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list) */ unsigned int streamIndex = 0; + /* First handle all non-MJPEG streams */ for (unsigned int i = 0; i < stream_list->num_streams; ++i) { camera3_stream_t *stream = stream_list->streams[i]; @@ -1019,6 +1023,18 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list) if (!format.isValid()) return -EINVAL; + stream->priv = static_cast(&streams_[i]); + streams_[i].format = format; + streams_[i].size = Size(stream->width, stream->height); + + /* Defer handling of MJPEG streams until all others are known. */ + if (format == formats::MJPEG) { + LOG(HAL, Info) << "Handling MJPEG stream"; + + streams_[i].index = -1; + continue; + } + StreamConfiguration streamConfiguration; streamConfiguration.size.width = stream->width; @@ -1028,7 +1044,61 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list) config_->addConfiguration(streamConfiguration); streams_[i].index = streamIndex++; - stream->priv = static_cast(&streams_[i]); + } + + /* Now handle MJPEG streams, adding a new stream if required. */ + for (unsigned int i = 0; i < stream_list->num_streams; ++i) { + camera3_stream_t *stream = stream_list->streams[i]; + CameraStream *cameraStream = &streams_[i]; + + /* Only process MJPEG streams */ + if (cameraStream->format != formats::MJPEG) + continue; + + bool match = false; + + /* Search for a compatible stream */ + for (unsigned int j = 0; j < config_->size(); j++) { + StreamConfiguration &cfg = config_->at(j); + + /* + * The PixelFormat must also be compatible with the + * encoder. + */ + if (cfg.size == cameraStream->size) { + LOG(HAL, Info) + << "Stream " << i + << " using libcamera stream " + << j; + + match = true; + cameraStream->index = j; + } + } + + /* + * Without a compatible match for JPEG encoding we must + * introduce a new stream to satisfy the request requirements. + */ + if (!match) { + StreamConfiguration streamConfiguration; + + /* + * \todo: The pixelFormat should be a 'best-fit' choice + * and may require a validation cycle. This is not yet + * handled, and should be considered as part of any + * stream configuration reworks. + */ + streamConfiguration.size.width = stream->width; + streamConfiguration.size.height = stream->height; + streamConfiguration.pixelFormat = formats::NV12; + + LOG(HAL, Info) << "Adding " << streamConfiguration.toString() + << " for MJPEG support"; + + config_->addConfiguration(streamConfiguration); + streams_[i].index = streamIndex++; + } } switch (config_->validate()) { @@ -1059,6 +1129,18 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list) /* Use the bufferCount confirmed by the validation process. */ stream->max_buffers = cfg.bufferCount; + + /* + * Construct a software compressor for MJPEG streams from the + * chosen libcamera source stream. + */ + if (cameraStream->format == formats::MJPEG) { + cameraStream->jpeg = new CompressorJPEG(); + cameraStream->jpeg->configure(cfg); + } else { + /* Either construct this correctly, or use a better interface */ + cameraStream->jpeg = nullptr; + } } /* @@ -1112,6 +1194,9 @@ FrameBuffer *CameraDevice::createFrameBuffer(const buffer_handle_t camera3buffer int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Request) { + LOG(HAL, Error) << "Received request " << camera3Request->frame_number + << " with " << camera3Request->num_output_buffers << " buffers"; + if (!camera3Request->num_output_buffers) { LOG(HAL, Error) << "No output buffers provided"; return -EINVAL; @@ -1158,6 +1243,10 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques descriptor->buffers[i].stream = camera3Buffers[i].stream; descriptor->buffers[i].buffer = camera3Buffers[i].buffer; + /* Software streams are handled after hardware streams complete. */ + if (cameraStream->format == formats::MJPEG) + continue; + /* * Create a libcamera buffer using the dmabuf descriptors of * the camera3Buffer for each stream. The FrameBuffer is @@ -1190,11 +1279,40 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques return 0; } +static CompressedImage mapAndroidBlobBuffer(const buffer_handle_t camera3buffer) +{ + CompressedImage img; + + /* ANDROID_JPEG_MAX_SIZE */ + unsigned int length = int32_t{13 << 20}; + + /* Take only the first plane */ + void *memory = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, + camera3buffer->data[0], 0); + + img.length = length; + img.data = static_cast(memory); + + return img; +} + +static void unmapAndroidBlobBuffer(CompressedImage *img) +{ + munmap(img->data, img->length); + img->data = nullptr; + img->length = 0; +} + void CameraDevice::requestComplete(Request *request) { const std::map &buffers = request->buffers(); camera3_buffer_status status = CAMERA3_BUFFER_STATUS_OK; std::unique_ptr resultMetadata; + Camera3RequestDescriptor *descriptor = + reinterpret_cast(request->cookie()); + + + LOG(HAL, Error) << "Request completed:..."; if (request->status() != Request::RequestComplete) { LOG(HAL, Error) << "Request not succesfully completed: " @@ -1202,10 +1320,41 @@ void CameraDevice::requestComplete(Request *request) status = CAMERA3_BUFFER_STATUS_ERROR; } - /* Prepare to call back the Android camera stack. */ - Camera3RequestDescriptor *descriptor = - reinterpret_cast(request->cookie()); + /* Handle any JPEG compression */ + for (unsigned int i = 0; i < descriptor->numBuffers; ++i) { + CameraStream *cameraStream = + static_cast(descriptor->buffers[i].stream->priv); + Compressor *compressor = cameraStream->jpeg; + /* Only handle streams with compression enabled. */ + if (!compressor) + continue; + + /* \todo: Optimise this dance routine, just to get the stream/buffer ... */ + StreamConfiguration *streamConfiguration = &config_->at(cameraStream->index); + Stream *stream = streamConfiguration->stream(); + FrameBuffer *buffer = request->findBuffer(stream); + if (!buffer) { + LOG(HAL, Error) << "Failed to find a source stream buffer"; + continue; + } + + CompressedImage output = mapAndroidBlobBuffer(*descriptor->buffers[i].buffer); + if (output.data == MAP_FAILED) { + LOG(HAL, Error) << "Failed to mmap android blob buffer of length " << output.length; + continue; + } + + int ret = compressor->compress(buffer, &output); + if (ret) { + LOG(HAL, Error) << "Failed to compress stream image"; + status = CAMERA3_BUFFER_STATUS_ERROR; + } + + unmapAndroidBlobBuffer(&output); + } + + /* Prepare to call back the Android camera stack. */ camera3_capture_result_t captureResult = {}; captureResult.frame_number = descriptor->frameNumber; captureResult.num_output_buffers = descriptor->numBuffers; @@ -1246,6 +1395,7 @@ void CameraDevice::requestComplete(Request *request) descriptor->buffers[0].stream); } + callbacks_->process_capture_result(callbacks_, &captureResult); delete descriptor; diff --git a/src/android/camera_device.h b/src/android/camera_device.h index 5b8b9c3e26e2..1973adaa2b4b 100644 --- a/src/android/camera_device.h +++ b/src/android/camera_device.h @@ -23,6 +23,8 @@ #include "libcamera/internal/log.h" #include "libcamera/internal/message.h" +#include "jpeg/compressor_jpeg.h" + class CameraMetadata; struct CameraStream { @@ -32,6 +34,12 @@ struct CameraStream { * one or more streams to the Android framework. */ unsigned int index; + + libcamera::PixelFormat format; + libcamera::Size size; + + /* Make sure this gets destructed correctly */ + CompressorJPEG *jpeg; }; class CameraDevice : protected libcamera::Loggable From patchwork Tue Jul 21 22:01:25 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 8914 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 ACE21C2E68 for ; Tue, 21 Jul 2020 22:01:38 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7A48C609C6; Wed, 22 Jul 2020 00:01:38 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Svj7XbPq"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2724160948 for ; Wed, 22 Jul 2020 00:01:33 +0200 (CEST) Received: from localhost.localdomain (cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 897831299; Wed, 22 Jul 2020 00:01:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1595368892; bh=LCrMLvsUn9JLcZDYM8uiorYJgASmezU9eYP0VlSs79E=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Svj7XbPqhnOf7TKXA6xMAW9nzHYA97vNTbsbbho/UFqL3W4eardw0uw0ZI5lrWsdt mIGBlTg/i3URs1FrDqSTs+KAODlBt7fbyyqwroz93OBfMLW1HTFyN9Npg7NWmjyFJH nbyb7BY51pAilNPMaqCsSucU39eY1kAcRCTpkdKw= From: Kieran Bingham To: libcamera devel Date: Tue, 21 Jul 2020 23:01:25 +0100 Message-Id: <20200721220126.202065-6-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200721220126.202065-1-kieran.bingham@ideasonboard.com> References: <20200721220126.202065-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH 5/6] [HACK]: android: camera_device: Define JPEG size and quality 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" Providing these two keys allows the camera application to capture without crashing. It does not /succeed/ but it doesn't crash. The camera application and cros-camera-test still need exif information to be able to successfully capture or pass the tests. These metadata values are hardcoded, as there is not yet an easy way to add dynamic data to the request metadata. I would like to see all of the infrastructure around adding request metadata support a more dynamic means of addition, which blocks easily adding and updating key value pairs otherwise. But as all of that is a lot of work, here's a hack-ity work around for the time being. Otherwise no form of the JPEG series will ever be posted. Please don't review this. ;-) Signed-off-by: Kieran Bingham --- src/android/camera_device.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 7323d4e58f68..23dde1cfaf98 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -846,6 +846,8 @@ const camera_metadata_t *CameraDevice::getStaticMetadata() ANDROID_SENSOR_EXPOSURE_TIME, ANDROID_STATISTICS_LENS_SHADING_MAP_MODE, ANDROID_STATISTICS_SCENE_FLICKER, + ANDROID_JPEG_SIZE, + ANDROID_JPEG_QUALITY, }; staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, availableResultKeys.data(), @@ -1444,10 +1446,10 @@ std::unique_ptr CameraDevice::getResultMetadata(int frame_number { /* * \todo Keep this in sync with the actual number of entries. - * Currently: 12 entries, 36 bytes + * Currently: 17 entries, 58 bytes */ std::unique_ptr resultMetadata = - std::make_unique(15, 50); + std::make_unique(17, 58); if (!resultMetadata->isValid()) { LOG(HAL, Error) << "Failed to allocate static metadata"; return nullptr; @@ -1497,6 +1499,14 @@ std::unique_ptr CameraDevice::getResultMetadata(int frame_number resultMetadata->addEntry(ANDROID_STATISTICS_SCENE_FLICKER, &scene_flicker, 1); + const uint32_t jpeg_size = 1 << 13; + resultMetadata->addEntry(ANDROID_JPEG_SIZE, + &jpeg_size, 1); + + const uint32_t jpeg_quality = 95; + resultMetadata->addEntry(ANDROID_JPEG_QUALITY, + &jpeg_quality, 1); + /* * Return the result metadata pack even is not valid: get() will return * nullptr. From patchwork Tue Jul 21 22:01:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 8915 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 1CC23C2E68 for ; Tue, 21 Jul 2020 22:01:39 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E20D160998; Wed, 22 Jul 2020 00:01:38 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="nzOYwopN"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 61D7760987 for ; Wed, 22 Jul 2020 00:01:33 +0200 (CEST) Received: from localhost.localdomain (cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E4D521414; Wed, 22 Jul 2020 00:01:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1595368893; bh=eW+PNbRr/LnRbFeT+LVAIiVoD7/UNRFIwYJMGKGYfKY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nzOYwopN2QoeKr64kP1TE/94KLP+NRxscTBKY75s/Axqvf59m6ntKwEVr0iuzHlly E1u+s1zYrMMlpL2lEFPF2650SKzQ5TuUyvshxTOJEZ8tBT+8JlXEGVohVegGfH9RD7 /sw6TNueF/so/pF9t+RYTMjzi7dtsp6DUTY/M9O8= From: Kieran Bingham To: libcamera devel Date: Tue, 21 Jul 2020 23:01:26 +0100 Message-Id: <20200721220126.202065-7-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200721220126.202065-1-kieran.bingham@ideasonboard.com> References: <20200721220126.202065-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH 6/6] android: camera_device: Provide a MappedCamera3Buffer 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" Utilise the MappedBuffer interface to map each of the planes provided in the Camera3 buffer to facilitate use in software streams. The buffers will be automatically unmapped when the object goes out of scope or is deleted. Signed-off-by: Kieran Bingham --- This shows how the MappedCamera3Buffer which was posted as part of the MappedBuffer series can get used. Originally my aim was to pass a MappedBuffer in place of creating a CompressedImage object, but passing in a MappedBuffer alone restricts passing back how many bytes were consumed, and I expect other meta-data might be needed, so I anticipate needing more than just a function return value soon. (i.e. so I don't yet think I can just return the compressed image size in the return value, but maybe it might end up being possible/cleaner to do that...) src/android/camera_device.cpp | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 23dde1cfaf98..34c60556e5fa 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -1281,30 +1281,6 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques return 0; } -static CompressedImage mapAndroidBlobBuffer(const buffer_handle_t camera3buffer) -{ - CompressedImage img; - - /* ANDROID_JPEG_MAX_SIZE */ - unsigned int length = int32_t{13 << 20}; - - /* Take only the first plane */ - void *memory = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, - camera3buffer->data[0], 0); - - img.length = length; - img.data = static_cast(memory); - - return img; -} - -static void unmapAndroidBlobBuffer(CompressedImage *img) -{ - munmap(img->data, img->length); - img->data = nullptr; - img->length = 0; -} - void CameraDevice::requestComplete(Request *request) { const std::map &buffers = request->buffers(); @@ -1341,19 +1317,22 @@ void CameraDevice::requestComplete(Request *request) continue; } - CompressedImage output = mapAndroidBlobBuffer(*descriptor->buffers[i].buffer); - if (output.data == MAP_FAILED) { - LOG(HAL, Error) << "Failed to mmap android blob buffer of length " << output.length; + MappedCamera3Buffer mapped(*descriptor->buffers[i].buffer, PROT_READ|PROT_WRITE); + if (!mapped.isValid()) { + LOG(HAL, Error) << "Failed to mmap android blob buffer"; continue; } + CompressedImage output; + output.data = static_cast(mapped.maps()[0].address); + output.length = mapped.maps()[0].length; + int ret = compressor->compress(buffer, &output); if (ret) { LOG(HAL, Error) << "Failed to compress stream image"; status = CAMERA3_BUFFER_STATUS_ERROR; } - unmapAndroidBlobBuffer(&output); } /* Prepare to call back the Android camera stack. */