From patchwork Thu Jan 21 10:15:41 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10913 X-Patchwork-Delegate: paul.elder@ideasonboard.com 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 452B5C0F2A for ; Thu, 21 Jan 2021 10:16:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 12594681DB; Thu, 21 Jan 2021 11:16:05 +0100 (CET) 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="X1OKmaqk"; 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 C5C23681A0 for ; Thu, 21 Jan 2021 11:16:03 +0100 (CET) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3C57550E; Thu, 21 Jan 2021 11:16:01 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611224163; bh=VVKWPshAHlzwGDCJA+HhMPLk7FTlPwYWPVztdK4UFIc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=X1OKmaqkVA7fMGW260A/0135OrAq9AsHenW6/JiAnb308j+qc6STuWg8zZiQEEuVF wCvvqOQVI4+5W/B22+n0PZWWUKqp9kSn581/n6oPQ4Oz54jZ8IQQo+qXs/LNpVPDML FMar7BsKlkzv+BwKAhzF7aPshTJpJ5fo5Lhguq1g= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Thu, 21 Jan 2021 19:15:41 +0900 Message-Id: <20210121101549.134574-2-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210121101549.134574-1-paul.elder@ideasonboard.com> References: <20210121101549.134574-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 1/9] android: jpeg: exif: Expand setString to support different encodings 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" GPSProcessingMethod and UserComment in EXIF tags can be in UTF-16. Expand setString to take an encoding when the field type is undefined. Update callers accordingly. Signed-off-by: Paul Elder --- Changes in v2: - moved from utils into exif - support no-encoding --- src/android/jpeg/exif.cpp | 107 +++++++++++++++++++++++++++++++------- src/android/jpeg/exif.h | 12 ++++- 2 files changed, 99 insertions(+), 20 deletions(-) diff --git a/src/android/jpeg/exif.cpp b/src/android/jpeg/exif.cpp index 33b3fa7f..cff366a4 100644 --- a/src/android/jpeg/exif.cpp +++ b/src/android/jpeg/exif.cpp @@ -7,6 +7,9 @@ #include "exif.h" +#include +#include + #include "libcamera/internal/log.h" #include "libcamera/internal/utils.h" @@ -64,7 +67,7 @@ Exif::Exif() exif_data_set_byte_order(data_, order_); setString(EXIF_IFD_EXIF, EXIF_TAG_EXIF_VERSION, - EXIF_FORMAT_UNDEFINED, "0231"); + EXIF_FORMAT_UNDEFINED, NoEncoding, "0231"); /* Create the mandatory EXIF fields with default data. */ exif_data_fix(data_); @@ -178,10 +181,18 @@ void Exif::setRational(ExifIfd ifd, ExifTag tag, ExifRational item) exif_entry_unref(entry); } -void Exif::setString(ExifIfd ifd, ExifTag tag, ExifFormat format, const std::string &item) +static const std::map> StringEncodingCodes = { + { Exif::ASCII, { 0x41, 0x53, 0x43, 0x49, 0x49, 0x00, 0x00, 0x00 } }, + { Exif::JIS, { 0x4a, 0x49, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00 } }, + { Exif::Unicode, { 0x55, 0x4e, 0x49, 0x43, 0x4f, 0x44, 0x45, 0x00 } }, + { Exif::Undefined, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, +}; + +void Exif::setString(ExifIfd ifd, ExifTag tag, ExifFormat format, + StringEncoding enc, const std::string &item) { std::string ascii; - size_t length; + size_t length = 0; const char *str; if (format == EXIF_FORMAT_ASCII) { @@ -190,14 +201,44 @@ void Exif::setString(ExifIfd ifd, ExifTag tag, ExifFormat format, const std::str /* Pad 1 extra byte to null-terminate the ASCII string. */ length = ascii.length() + 1; - } else { - str = item.c_str(); - - /* - * Strings stored in different formats (EXIF_FORMAT_UNDEFINED) - * are not null-terminated. - */ - length = item.length(); + } else if (format == EXIF_FORMAT_UNDEFINED) { + std::vector buf; + const std::vector &constbuf = buf; + std::u16string u16str; + + switch (enc) { + case Unicode: + buf = StringEncodingCodes.find(enc)->second; + u16str = utf8ToUtf16(item); + + /* Little-endian */ + buf.resize(8 + u16str.size() * 2); + for (size_t i = 0; i < u16str.size(); i++) { + buf[8 + 2 * i] = (u16str[i] & 0xff); + buf[8 + 2 * i + 1] = ((u16str[i] >> 8) & 0xff); + } + + str = reinterpret_cast(constbuf.data()); + length = buf.size(); + + break; + /* \todo Support JIS */ + case JIS: + case ASCII: + case Undefined: + buf = StringEncodingCodes.find(enc)->second; + [[fallthrough]]; + default: + buf.insert(buf.end(), item.begin(), item.end()); + str = reinterpret_cast(constbuf.data()); + + /* + * Strings stored in different formats (EXIF_FORMAT_UNDEFINED) + * are not null-terminated. + */ + length = buf.size(); + break; + } } ExifEntry *entry = createEntry(ifd, tag, format, length, length); @@ -210,12 +251,12 @@ void Exif::setString(ExifIfd ifd, ExifTag tag, ExifFormat format, const std::str void Exif::setMake(const std::string &make) { - setString(EXIF_IFD_0, EXIF_TAG_MAKE, EXIF_FORMAT_ASCII, make); + setString(EXIF_IFD_0, EXIF_TAG_MAKE, EXIF_FORMAT_ASCII, NoEncoding, make); } void Exif::setModel(const std::string &model) { - setString(EXIF_IFD_0, EXIF_TAG_MODEL, EXIF_FORMAT_ASCII, model); + setString(EXIF_IFD_0, EXIF_TAG_MODEL, EXIF_FORMAT_ASCII, NoEncoding, model); } void Exif::setSize(const Size &size) @@ -233,9 +274,9 @@ void Exif::setTimestamp(time_t timestamp) strftime(str, sizeof(str), "%Y:%m:%d %H:%M:%S", &tm); std::string ts(str); - setString(EXIF_IFD_0, EXIF_TAG_DATE_TIME, EXIF_FORMAT_ASCII, ts); - setString(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_ORIGINAL, EXIF_FORMAT_ASCII, ts); - setString(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_DIGITIZED, EXIF_FORMAT_ASCII, ts); + setString(EXIF_IFD_0, EXIF_TAG_DATE_TIME, EXIF_FORMAT_ASCII, NoEncoding, ts); + setString(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_ORIGINAL, EXIF_FORMAT_ASCII, NoEncoding, ts); + setString(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_DIGITIZED, EXIF_FORMAT_ASCII, NoEncoding, ts); /* Query and set timezone information if available. */ int r = strftime(str, sizeof(str), "%z", &tm); @@ -244,13 +285,13 @@ void Exif::setTimestamp(time_t timestamp) tz.insert(3, 1, ':'); setString(EXIF_IFD_EXIF, static_cast(_ExifTag::OFFSET_TIME), - EXIF_FORMAT_ASCII, tz); + EXIF_FORMAT_ASCII, NoEncoding, tz); setString(EXIF_IFD_EXIF, static_cast(_ExifTag::OFFSET_TIME_ORIGINAL), - EXIF_FORMAT_ASCII, tz); + EXIF_FORMAT_ASCII, NoEncoding, tz); setString(EXIF_IFD_EXIF, static_cast(_ExifTag::OFFSET_TIME_DIGITIZED), - EXIF_FORMAT_ASCII, tz); + EXIF_FORMAT_ASCII, NoEncoding, tz); } } @@ -290,6 +331,34 @@ void Exif::setThumbnail(Span thumbnail, setShort(EXIF_IFD_0, EXIF_TAG_COMPRESSION, compression); } +/** + * \brief Convert UTF-8 string to UTF-16 string + * \param[in] str String to convert + * + * \return \a str in UTF-16 + */ +std::u16string Exif::utf8ToUtf16(const std::string &str) +{ + std::mbstate_t state{}; + char16_t c16; + const char *ptr = str.data(); + const char *end = ptr + str.size(); + + std::u16string ret; + while (size_t rc = mbrtoc16(&c16, ptr, end - ptr + 1, &state)) { + if (rc == static_cast(-2) || + rc == static_cast(-1)) + break; + + ret.push_back(c16); + + if (rc > 0) + ptr += rc; + } + + return ret; +} + [[nodiscard]] int Exif::generate() { if (exifData_) { diff --git a/src/android/jpeg/exif.h b/src/android/jpeg/exif.h index 5cab4559..db98ba63 100644 --- a/src/android/jpeg/exif.h +++ b/src/android/jpeg/exif.h @@ -26,6 +26,14 @@ public: JPEG = 6, }; + enum StringEncoding { + NoEncoding = 0, + ASCII = 1, + JIS = 2, + Unicode = 3, + Undefined = 4, + }; + void setMake(const std::string &make); void setModel(const std::string &model); @@ -46,9 +54,11 @@ private: void setShort(ExifIfd ifd, ExifTag tag, uint16_t item); void setLong(ExifIfd ifd, ExifTag tag, uint32_t item); void setString(ExifIfd ifd, ExifTag tag, ExifFormat format, - const std::string &item); + StringEncoding enc, const std::string &item); void setRational(ExifIfd ifd, ExifTag tag, ExifRational item); + std::u16string utf8ToUtf16(const std::string &str); + bool valid_; ExifData *data_; From patchwork Thu Jan 21 10:15:42 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10914 X-Patchwork-Delegate: paul.elder@ideasonboard.com 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 D6CD4C0F2A for ; Thu, 21 Jan 2021 10:16:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A1D01681D9; Thu, 21 Jan 2021 11:16:06 +0100 (CET) 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="MDxesJ56"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A1C14681BF for ; Thu, 21 Jan 2021 11:16:05 +0100 (CET) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 2F3E98AE; Thu, 21 Jan 2021 11:16:03 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611224165; bh=xSfX1fkIg5O+/ViRLQ/Kg1CST+vVOjtbFrsOuoTHvVU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=MDxesJ56zvXFV2cNE1N3BQlf0/v1ZvpTrF4L7BMiUD4gDKoT5oBc5OYhmYkXmc1WF q2FZjk6YH5mGOzbhCEbs2tpmmITKfDJS2ZdR7Fe7hb1tAPzccuF9wvdYdnuoYGtikr y8UKagCDBF71cFAP0lQVguZWxRWi0NQgGOMY31AQ= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Thu, 21 Jan 2021 19:15:42 +0900 Message-Id: <20210121101549.134574-3-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210121101549.134574-1-paul.elder@ideasonboard.com> References: <20210121101549.134574-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 2/9] android: CameraMetadata: Add copy constructor and getEntry 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" Add a copy constructor and assigment operator to CameraMetadata, as well a constructor from camera_metadata_t. Also add a function getEntry to allow getting metadata entries from CameraMetadata. This allows us to use CameraMetadata for reading from camera_metadata_t. Signed-off-by: Paul Elder Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- Changes in v2: - getEntry returns bool instead of int --- src/android/camera_metadata.cpp | 34 +++++++++++++++++++++++++++++++++ src/android/camera_metadata.h | 5 +++++ 2 files changed, 39 insertions(+) diff --git a/src/android/camera_metadata.cpp b/src/android/camera_metadata.cpp index edea48fe..3b98d25c 100644 --- a/src/android/camera_metadata.cpp +++ b/src/android/camera_metadata.cpp @@ -19,12 +19,46 @@ CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity) valid_ = metadata_ != nullptr; } +CameraMetadata::CameraMetadata(const camera_metadata_t *metadata) +{ + metadata_ = clone_camera_metadata(metadata); + valid_ = metadata_ != nullptr; +} + +CameraMetadata::CameraMetadata(const CameraMetadata &other) +{ + metadata_ = clone_camera_metadata(other.get()); + valid_ = metadata_ != nullptr; +} + CameraMetadata::~CameraMetadata() { if (metadata_) free_camera_metadata(metadata_); } +CameraMetadata &CameraMetadata::operator=(const CameraMetadata &other) +{ + if (this == &other) + return *this; + + if (metadata_) + free_camera_metadata(metadata_); + + metadata_ = clone_camera_metadata(other.get()); + valid_ = metadata_ != nullptr; + + return *this; +} + +bool CameraMetadata::getEntry(uint32_t tag, camera_metadata_ro_entry_t *entry) const +{ + if (find_camera_metadata_ro_entry(metadata_, tag, entry)) + return false; + + return true; +} + bool CameraMetadata::addEntry(uint32_t tag, const void *data, size_t count) { if (!valid_) diff --git a/src/android/camera_metadata.h b/src/android/camera_metadata.h index 9d047b1b..720b760d 100644 --- a/src/android/camera_metadata.h +++ b/src/android/camera_metadata.h @@ -15,9 +15,14 @@ class CameraMetadata { public: CameraMetadata(size_t entryCapacity, size_t dataCapacity); + CameraMetadata(const camera_metadata_t *metadata); + CameraMetadata(const CameraMetadata &other); ~CameraMetadata(); + CameraMetadata &operator=(const CameraMetadata &other); + bool isValid() const { return valid_; } + bool getEntry(uint32_t tag, camera_metadata_ro_entry_t *entry) const; bool addEntry(uint32_t tag, const void *data, size_t data_count); bool updateEntry(uint32_t tag, const void *data, size_t data_count); From patchwork Thu Jan 21 10:15:43 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10915 X-Patchwork-Delegate: paul.elder@ideasonboard.com 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 36C34C0F2A for ; Thu, 21 Jan 2021 10:16:09 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 033C8681DB; Thu, 21 Jan 2021 11:16:09 +0100 (CET) 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="Gp6XcMQ9"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C4BDD681BF for ; Thu, 21 Jan 2021 11:16:07 +0100 (CET) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 0A64F50E; Thu, 21 Jan 2021 11:16:05 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611224167; bh=Al01nFAdXc6G8Ra8bF6+uUJL/4nlGQ4gx82U2aPPjIE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Gp6XcMQ9bizJTPR6oiPxcgId2WFiqFcDowk3nv0g5z2Lv38zcVNAhhJdaWzVAuX5A 0sPRjPl2ngsEXNQkMOT8KXEA+rSwulHbiuCKqrgNOgWkG9zn40hh7zJebVsxHPAOEB 8aJ8gQNPRUAr9Y7pcJgH6bupNokNb5Zt0a8QmNbQ= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Thu, 21 Jan 2021 19:15:43 +0900 Message-Id: <20210121101549.134574-4-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210121101549.134574-1-paul.elder@ideasonboard.com> References: <20210121101549.134574-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 3/9] android: jpeg: exif: Fix setOrientation EXIF values 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" The input to setOrientation is angle clockwise from the sensor orientation, while the EXIF output values were swapped for 90 and 270 degrees. From the EXIF spec: 6 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual top. 8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom. 6 should be 90 degrees clockwise, while 8 should 270 degrees clockwise. Fix this. As Android defines the rotation as the clockwise angle by which the image needs to be rotated to appear in the correct orientation on the device screen, the previous values would be correct if the input angle was from the camera orientation. Since the correct input should be the requested JPEG orientation, these new values are the correct ones. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- src/android/jpeg/exif.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/android/jpeg/exif.cpp b/src/android/jpeg/exif.cpp index cff366a4..8a185130 100644 --- a/src/android/jpeg/exif.cpp +++ b/src/android/jpeg/exif.cpp @@ -304,13 +304,13 @@ void Exif::setOrientation(int orientation) value = 1; break; case 90: - value = 8; + value = 6; break; case 180: value = 3; break; case 270: - value = 6; + value = 8; break; } From patchwork Thu Jan 21 10:15:44 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10916 X-Patchwork-Delegate: paul.elder@ideasonboard.com 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 90045C0F2A for ; Thu, 21 Jan 2021 10:16:11 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5CBFD681DB; Thu, 21 Jan 2021 11:16:11 +0100 (CET) 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="mQgXPyhh"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B3DC2681BF for ; Thu, 21 Jan 2021 11:16:09 +0100 (CET) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 46CE250E; Thu, 21 Jan 2021 11:16:07 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611224169; bh=NNI9ffkcOFepRrlIwGq2JQLmOJ7m2dwGw+dIQpLrKgM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mQgXPyhhw7d9KMxlb74DnEcwfMGBwuGI3jRxhY2PI6rUwa1yLgBWVp2EccUG25n6W +9FhdxXch/uZMbEQy0HMVgIe/NIYpsHxsDqMS0a4ay2z0S9RRRtM+DeKQ0qnj20Y2S dtZoIVcx80XYv24+C9GbFDp5FXU+ajvuU8iAkMTo= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Thu, 21 Jan 2021 19:15:44 +0900 Message-Id: <20210121101549.134574-5-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210121101549.134574-1-paul.elder@ideasonboard.com> References: <20210121101549.134574-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 4/9] android: jpeg: exif: Add functions for setting various values 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" Add functions for setting the following EXIF fields: - GPSDatestamp - GPSTimestamp - GPSLocation - GPSLatitudeRef - GPSLatitude - GPSLongitudeRef - GPSLongitude - GPSAltitudeRef - GPSAltitude - GPSProcessingMethod - FocalLength - ExposureTime - FNumber - ISO - Flash - WhiteBalance - SubsecTime - SubsecTimeOriginal - SubsecTimeDigitized These are in preparation for fixing the following CTS tests: - android.hardware.camera2.cts.StillCaptureTest#testFocalLengths - android.hardware.camera2.cts.StillCaptureTest#testJpegExif Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- Changes in v2: - some cosmetic and precision changes - use the new setString --- src/android/jpeg/exif.cpp | 159 ++++++++++++++++++++++++++++++++++++++ src/android/jpeg/exif.h | 40 ++++++++++ 2 files changed, 199 insertions(+) diff --git a/src/android/jpeg/exif.cpp b/src/android/jpeg/exif.cpp index 8a185130..d3d0026a 100644 --- a/src/android/jpeg/exif.cpp +++ b/src/android/jpeg/exif.cpp @@ -7,8 +7,10 @@ #include "exif.h" +#include #include #include +#include #include "libcamera/internal/log.h" #include "libcamera/internal/utils.h" @@ -151,6 +153,16 @@ ExifEntry *Exif::createEntry(ExifIfd ifd, ExifTag tag, ExifFormat format, return entry; } +void Exif::setByte(ExifIfd ifd, ExifTag tag, uint8_t item) +{ + ExifEntry *entry = createEntry(ifd, tag, EXIF_FORMAT_BYTE, 1, 1); + if (!entry) + return; + + entry->data[0] = item; + exif_entry_unref(entry); +} + void Exif::setShort(ExifIfd ifd, ExifTag tag, uint16_t item) { ExifEntry *entry = createEntry(ifd, tag); @@ -295,6 +307,102 @@ void Exif::setTimestamp(time_t timestamp) } } +void Exif::setGPSDateTimestamp(time_t timestamp) +{ + struct tm tm; + gmtime_r(×tamp, &tm); + + char str[11]; + strftime(str, sizeof(str), "%Y:%m:%d", &tm); + std::string tsStr(str); + + setString(EXIF_IFD_GPS, static_cast(EXIF_TAG_GPS_DATE_STAMP), + EXIF_FORMAT_ASCII, NoEncoding, tsStr); + + /* Set GPS_TIME_STAMP */ + ExifEntry *entry = + createEntry(EXIF_IFD_GPS, + static_cast(EXIF_TAG_GPS_TIME_STAMP), + EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational)); + if (!entry) + return; + + ExifRational ts[] = { + { static_cast(tm.tm_hour), 1 }, + { static_cast(tm.tm_min), 1 }, + { static_cast(tm.tm_sec), 1 }, + }; + + for (int i = 0; i < 3; i++) + exif_set_rational(entry->data + i * sizeof(ExifRational), + order_, ts[i]); + + exif_entry_unref(entry); +} + +std::tuple Exif::degreesToDMS(double decimalDegrees) +{ + int degrees = std::trunc(decimalDegrees); + double minutes = std::abs((decimalDegrees - degrees) * 60); + double seconds = (minutes - std::trunc(minutes)) * 60; + + return { degrees, std::trunc(minutes), std::round(seconds) }; +} + +void Exif::setGPSDMS(ExifIfd ifd, ExifTag tag, int deg, int min, int sec) +{ + ExifEntry *entry = createEntry(ifd, tag, EXIF_FORMAT_RATIONAL, 3, + 3 * sizeof(ExifRational)); + if (!entry) + return; + + ExifRational coords[] = { + { static_cast(deg), 1 }, + { static_cast(min), 1 }, + { static_cast(sec), 1 }, + }; + + for (int i = 0; i < 3; i++) + exif_set_rational(entry->data + i * sizeof(ExifRational), + order_, coords[i]); + exif_entry_unref(entry); +} + +/* + * \brief Set GPS location (lat, long, alt) + * \param[in] coords Pointer to coordinates latitude, longitude, and altitude, + * first two in degrees, the third in meters + * + * \todo use std::array + */ +void Exif::setGPSLocation(const double *coords) +{ + int deg, min, sec; + + std::tie(deg, min, sec) = degreesToDMS(coords[0]); + setString(EXIF_IFD_GPS, static_cast(EXIF_TAG_GPS_LATITUDE_REF), + EXIF_FORMAT_ASCII, NoEncoding, deg >= 0 ? "N" : "S"); + setGPSDMS(EXIF_IFD_GPS, static_cast(EXIF_TAG_GPS_LATITUDE), + std::abs(deg), min, sec); + + std::tie(deg, min, sec) = degreesToDMS(coords[1]); + setString(EXIF_IFD_GPS, static_cast(EXIF_TAG_GPS_LONGITUDE_REF), + EXIF_FORMAT_ASCII, NoEncoding, deg >= 0 ? "E" : "W"); + setGPSDMS(EXIF_IFD_GPS, static_cast(EXIF_TAG_GPS_LATITUDE), + std::abs(deg), min, sec); + + setByte(EXIF_IFD_GPS, static_cast(EXIF_TAG_GPS_ALTITUDE_REF), + coords[2] >= 0 ? 0 : 1); + setRational(EXIF_IFD_GPS, static_cast(EXIF_TAG_GPS_ALTITUDE), + ExifRational{ static_cast(std::abs(coords[2])), 1 }); +} + +void Exif::setGPSMethod(const std::string &method) +{ + setString(EXIF_IFD_GPS, static_cast(EXIF_TAG_GPS_PROCESSING_METHOD), + EXIF_FORMAT_UNDEFINED, Unicode, method); +} + void Exif::setOrientation(int orientation) { int value; @@ -331,6 +439,45 @@ void Exif::setThumbnail(Span thumbnail, setShort(EXIF_IFD_0, EXIF_TAG_COMPRESSION, compression); } +void Exif::setFocalLength(float length) +{ + ExifRational rational = { static_cast(length * 1000), 1000 }; + setRational(EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH, rational); +} + +void Exif::setExposureTime(uint64_t nsec) +{ + ExifRational rational = { static_cast(nsec), 1000000000 }; + setRational(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_TIME, rational); +} + +void Exif::setAperture(float size) +{ + ExifRational rational = { static_cast(size * 10000), 10000 }; + setRational(EXIF_IFD_EXIF, EXIF_TAG_FNUMBER, rational); +} + +void Exif::setISO(uint16_t iso) +{ + setShort(EXIF_IFD_EXIF, EXIF_TAG_ISO_SPEED_RATINGS, iso); +} + +void Exif::setFlash(Flash flash) +{ + setShort(EXIF_IFD_EXIF, EXIF_TAG_FLASH, static_cast(flash)); +} + +void Exif::setWhiteBalance(WhiteBalance wb) +{ + setShort(EXIF_IFD_EXIF, EXIF_TAG_WHITE_BALANCE, static_cast(wb)); +} + +void Exif::setSubsecTime(uint64_t subsec) +{ + setString(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME, + EXIF_FORMAT_ASCII, NoEncoding, std::to_string(subsec)); +} + /** * \brief Convert UTF-8 string to UTF-16 string * \param[in] str String to convert @@ -359,6 +506,18 @@ std::u16string Exif::utf8ToUtf16(const std::string &str) return ret; } +void Exif::setSubsecTimeOriginal(uint64_t subsec) +{ + setString(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_ORIGINAL, + EXIF_FORMAT_ASCII, NoEncoding, std::to_string(subsec)); +} + +void Exif::setSubsecTimeDigitized(uint64_t subsec) +{ + setString(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_DIGITIZED, + EXIF_FORMAT_ASCII, NoEncoding, std::to_string(subsec)); +} + [[nodiscard]] int Exif::generate() { if (exifData_) { diff --git a/src/android/jpeg/exif.h b/src/android/jpeg/exif.h index db98ba63..baab96ed 100644 --- a/src/android/jpeg/exif.h +++ b/src/android/jpeg/exif.h @@ -26,6 +26,27 @@ public: JPEG = 6, }; + enum Flash { + /* bit 0 */ + Fired = 0x01, + /* bits 1 and 2 */ + StrobeDetected = 0x04, + StrobeNotDetected = 0x06, + /* bits 3 and 4 */ + ModeCompulsoryFiring = 0x08, + ModeCompulsorySuppression = 0x10, + ModeAuto = 0x18, + /* bit 5 */ + FlashNotPresent = 0x20, + /* bit 6 */ + RedEye = 0x40, + }; + + enum WhiteBalance { + Auto = 0, + Manual = 1, + }; + enum StringEncoding { NoEncoding = 0, ASCII = 1, @@ -43,6 +64,21 @@ public: Compression compression); void setTimestamp(time_t timestamp); + void setGPSDateTimestamp(time_t timestamp); + void setGPSLocation(const double *coords); + void setGPSMethod(const std::string &method); + + void setFocalLength(float length); + void setExposureTime(uint64_t nsec); + void setAperture(float size); + void setISO(uint16_t iso); + void setFlash(Flash flash); + void setWhiteBalance(WhiteBalance wb); + + void setSubsecTime(uint64_t subsec); + void setSubsecTimeOriginal(uint64_t subsec); + void setSubsecTimeDigitized(uint64_t subsec); + libcamera::Span data() const { return { exifData_, size_ }; } [[nodiscard]] int generate(); @@ -51,12 +87,16 @@ private: ExifEntry *createEntry(ExifIfd ifd, ExifTag tag, ExifFormat format, unsigned long components, unsigned int size); + void setByte(ExifIfd ifd, ExifTag tag, uint8_t item); void setShort(ExifIfd ifd, ExifTag tag, uint16_t item); void setLong(ExifIfd ifd, ExifTag tag, uint32_t item); void setString(ExifIfd ifd, ExifTag tag, ExifFormat format, StringEncoding enc, const std::string &item); void setRational(ExifIfd ifd, ExifTag tag, ExifRational item); + std::tuple degreesToDMS(double decimalDegrees); + void setGPSDMS(ExifIfd ifd, ExifTag tag, int deg, int min, int sec); + std::u16string utf8ToUtf16(const std::string &str); bool valid_; From patchwork Thu Jan 21 10:15:45 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10917 X-Patchwork-Delegate: paul.elder@ideasonboard.com 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 E589FC0F2A for ; Thu, 21 Jan 2021 10:16:12 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B251F681DE; Thu, 21 Jan 2021 11:16:12 +0100 (CET) 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="OM0qR/rw"; 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 B416B681E1 for ; Thu, 21 Jan 2021 11:16:11 +0100 (CET) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 39A7F50E; Thu, 21 Jan 2021 11:16:09 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611224171; bh=e2LS7OxkgVSQ4usA6jGOUmqGy42jH5Gfw9noQG3d4WY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OM0qR/rwI9hY7+004isdEOGO1VoAb09vVfzVAbUrT9PEhhCv+Kub8b4G8b7hFRR57 hXtrErKDdJWskBoPBvW8jcmlxBDRkbIoarwr5WwgaFnZFOUHV813KdS9HV9E6ZKB3g 3rUW0ovWNuCuVmryNlx/XIgIMSW1ltJjscsmllNE= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Thu, 21 Jan 2021 19:15:45 +0900 Message-Id: <20210121101549.134574-6-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210121101549.134574-1-paul.elder@ideasonboard.com> References: <20210121101549.134574-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 5/9] android: camera_device: Load make and model from platform settings 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" In ChromeOS the camera make and model is saved in /var/cache/camera/camera.prop. Load and save these values at construction time, if available. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- Changes in v2: - use fstream instead of File and split --- src/android/camera_device.cpp | 23 +++++++++++++++++++++++ src/android/camera_device.h | 5 +++++ 2 files changed, 28 insertions(+) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index d2a8e876..16d5b472 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -9,6 +9,7 @@ #include "camera_ops.h" #include "post_processor.h" +#include #include #include #include @@ -344,6 +345,28 @@ CameraDevice::CameraDevice(unsigned int id, const std::shared_ptr &camer * streamConfiguration. */ maxJpegBufferSize_ = 13 << 20; /* 13631488 from USB HAL */ + + cameraMake_ = "libcamera"; + cameraModel_ = "cameraModel"; + + /* \todo Support getting properties on Android */ + std::ifstream fstream("/var/cache/camera/camera.prop"); + if (!fstream.is_open()) + return; + + std::string line; + while (getline(fstream, line)) { + std::string::size_type delimPos = line.find("="); + if (delimPos == std::string::npos) + continue; + std::string key = line.substr(0, delimPos); + std::string val = line.substr(delimPos + 1); + + if (!key.compare("ro.product.model")) + cameraModel_ = val; + if (!key.compare("ro.product.manufacturer")) + cameraMake_ = val; + } } CameraDevice::~CameraDevice() diff --git a/src/android/camera_device.h b/src/android/camera_device.h index 0874c80f..a285d0a1 100644 --- a/src/android/camera_device.h +++ b/src/android/camera_device.h @@ -56,6 +56,8 @@ public: return config_.get(); } + const std::string &cameraMake() const { return cameraMake_; } + const std::string &cameraModel() const { return cameraModel_; } int facing() const { return facing_; } int orientation() const { return orientation_; } unsigned int maxJpegBufferSize() const { return maxJpegBufferSize_; } @@ -127,6 +129,9 @@ private: std::map formatsMap_; std::vector streams_; + std::string cameraMake_; + std::string cameraModel_; + int facing_; int orientation_; From patchwork Thu Jan 21 10:15:46 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10918 X-Patchwork-Delegate: paul.elder@ideasonboard.com 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 4614AC0F2A for ; Thu, 21 Jan 2021 10:16:15 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0E3F1681DB; Thu, 21 Jan 2021 11:16:15 +0100 (CET) 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="f1kl0s6Z"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 86728681BF for ; Thu, 21 Jan 2021 11:16:13 +0100 (CET) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1317250E; Thu, 21 Jan 2021 11:16:11 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611224173; bh=F/tN+HFLOCrpsc36gpPO2GSxl8oIkGUu1Uzx1eMt0PY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=f1kl0s6Zy4ubzYKguFA0mr5PrcncH/gh3n86GvjdwI+t1MN+ZVeBIeTADVVlM14oc pxGKCytpozv1ADx1L7gxXaX+f8DQ685bCyR+x7JYfCWirdz7korpButL+OylA1Qv+5 0ogRSVp7/1RrquzXIfmhDNC++M4fzVsUjz4ooGdc= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Thu, 21 Jan 2021 19:15:46 +0900 Message-Id: <20210121101549.134574-7-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210121101549.134574-1-paul.elder@ideasonboard.com> References: <20210121101549.134574-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 6/9] android: jpeg: Factor out thumbnailer configuration 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" Move configuration of the thumbnailer into a function so that it can be called later in post-processing if a different size thumbnail is requested. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- New in v2 --- src/android/jpeg/post_processor_jpeg.cpp | 26 ++++++++++++++++-------- src/android/jpeg/post_processor_jpeg.h | 2 ++ src/android/jpeg/thumbnailer.h | 1 + 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp index 436a50f8..0c1226ad 100644 --- a/src/android/jpeg/post_processor_jpeg.cpp +++ b/src/android/jpeg/post_processor_jpeg.cpp @@ -25,6 +25,21 @@ PostProcessorJpeg::PostProcessorJpeg(CameraDevice *const device) { } +int PostProcessorJpeg::configureThumbnailer(const Size &size, + const PixelFormat &pixelFormat) +{ + thumbnailer_.configure(size, pixelFormat); + StreamConfiguration thCfg; + thCfg.size = thumbnailer_.size(); + thCfg.pixelFormat = pixelFormat; + if (thumbnailEncoder_.configure(thCfg) != 0) { + LOG(JPEG, Error) << "Failed to configure thumbnail encoder"; + return -EINVAL; + } + + return 0; +} + int PostProcessorJpeg::configure(const StreamConfiguration &inCfg, const StreamConfiguration &outCfg) { @@ -40,16 +55,11 @@ int PostProcessorJpeg::configure(const StreamConfiguration &inCfg, streamSize_ = outCfg.size; - thumbnailer_.configure(inCfg.size, inCfg.pixelFormat); - StreamConfiguration thCfg = inCfg; - thCfg.size = thumbnailer_.size(); - if (thumbnailEncoder_.configure(thCfg) != 0) { - LOG(JPEG, Error) << "Failed to configure thumbnail encoder"; - return -EINVAL; - } + int ret = configureThumbnailer(inCfg.size, inCfg.pixelFormat); + if (ret) + return ret; encoder_ = std::make_unique(); - return encoder_->configure(inCfg); } diff --git a/src/android/jpeg/post_processor_jpeg.h b/src/android/jpeg/post_processor_jpeg.h index 5afa831c..c545c29c 100644 --- a/src/android/jpeg/post_processor_jpeg.h +++ b/src/android/jpeg/post_processor_jpeg.h @@ -31,6 +31,8 @@ public: private: void generateThumbnail(const libcamera::FrameBuffer &source, std::vector *thumbnail); + int configureThumbnailer(const libcamera::Size &size, + const libcamera::PixelFormat &pixelFormat); CameraDevice *const cameraDevice_; std::unique_ptr encoder_; diff --git a/src/android/jpeg/thumbnailer.h b/src/android/jpeg/thumbnailer.h index 98f11833..f393db47 100644 --- a/src/android/jpeg/thumbnailer.h +++ b/src/android/jpeg/thumbnailer.h @@ -22,6 +22,7 @@ public: void createThumbnail(const libcamera::FrameBuffer &source, std::vector *dest); const libcamera::Size &size() const { return targetSize_; } + const libcamera::PixelFormat &pixelFormat() const { return pixelFormat_; } private: libcamera::Size computeThumbnailSize() const; From patchwork Thu Jan 21 10:15:47 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10919 X-Patchwork-Delegate: paul.elder@ideasonboard.com 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 ABD66C0F2A for ; Thu, 21 Jan 2021 10:16:17 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 77AC6681EB; Thu, 21 Jan 2021 11:16:17 +0100 (CET) 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="g5ABu5q/"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 704A3681C3 for ; Thu, 21 Jan 2021 11:16:15 +0100 (CET) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 01F4A50E; Thu, 21 Jan 2021 11:16:13 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611224175; bh=zjFaEcw7z5dM5BSS+bjbQTTQ3ZLxS/0UdvDtOuf2rUo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=g5ABu5q/59wy/N95w88VuT69ZPsIQUptNuhHiclr/NodCKJebG8ot+83lS716BIYP o+auJ+2auK8EhHHPTwN0VrBxO9uqxhVSojf1kPHaJ5QHfdtawGO0gxQ2cI4d/Y6p3i NfrM0LTrKLWdMNw8Em7TIhOgeQCS7ZXqHnpBOWHU= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Thu, 21 Jan 2021 19:15:47 +0900 Message-Id: <20210121101549.134574-8-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210121101549.134574-1-paul.elder@ideasonboard.com> References: <20210121101549.134574-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 7/9] android: camera_device: Use CameraMetadata wrapper in processControls 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" Use the CameraMetadata wrapper to access android request settings instead of accessing camera_metadata_t directly. Signed-off-by: Paul Elder --- New in v2 --- src/android/camera_device.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 16d5b472..49af221b 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -1639,12 +1639,10 @@ int CameraDevice::processControls(camera3_capture_request_t *camera3Request, * \todo As soon as more controls are handled, this part should be * broke out to a dedicated function. */ - const camera_metadata_t *camera3Settings = camera3Request->settings; + CameraMetadata camera3Settings(camera3Request->settings); camera_metadata_ro_entry_t entry; - int ret = find_camera_metadata_ro_entry(camera3Settings, - ANDROID_SCALER_CROP_REGION, - &entry); - if (!ret) { + int ret = camera3Settings.getEntry(ANDROID_SCALER_CROP_REGION, &entry); + if (ret) { const int32_t *data = entry.data.i32; Rectangle cropRegion = { data[0], data[1], static_cast(data[2]), From patchwork Thu Jan 21 10:15:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10920 X-Patchwork-Delegate: paul.elder@ideasonboard.com 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 2BE0FC0F2A for ; Thu, 21 Jan 2021 10:16:19 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id EC2B9681E7; Thu, 21 Jan 2021 11:16:18 +0100 (CET) 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="rm2MVdSC"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 50BAB681C9 for ; Thu, 21 Jan 2021 11:16:17 +0100 (CET) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D3B1250E; Thu, 21 Jan 2021 11:16:15 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611224177; bh=m1f0bhz1MabYYf58IEyW1yMiikIN5k4O18t2OgCZIV0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rm2MVdSC1xjb0PCjDmEsppOB7P566KsOiTapMau3II4vw8h7JRV6utg//zaumV8yJ OVlskxaIfPImP5Ap1MR+exYU4wKIhO68vmgz3oaX/VpQQnK0LV5ypGsti9mkB6K9OV RyrfPdqmN2FNCLgn7/xA9YNajv3N1AB/6KKa34aw= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Thu, 21 Jan 2021 19:15:48 +0900 Message-Id: <20210121101549.134574-9-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210121101549.134574-1-paul.elder@ideasonboard.com> References: <20210121101549.134574-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 8/9] android: Set result metadata and EXIF fields based on request metadata 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" Set the following android result metadata: - ANDROID_LENS_FOCAL_LENGTH - ANDROID_LENS_APERTURE - ANDROID_JPEG_GPS_TIMESTAMP - ANDROID_JPEG_THUMBNAIL_SIZE - ANDROID_JPEG_THUMBNAIL_QUALITY - ANDROID_JPEG_GPS_COORDINATES - ANDROID_JPEG_GPS_PROCESSING_METHOD - ANDROID_JPEG_SIZE - ANDROID_JPEG_QUALITY - ANDROID_JPEG_ORIENTATION And the following EXIF fields: - GPSDatestamp - GPSTimestamp - GPSLocation - GPSLatitudeRef - GPSLatitude - GPSLongitudeRef - GPSLongitude - GPSAltitudeRef - GPSAltitude - GPSProcessingMethod - FocalLength - ExposureTime - FNumber - ISO - Flash - WhiteBalance - SubsecTime - SubsecTimeOriginal - SubsecTimeDigitized Based on android request metadata. This allows the following CTS tests to pass: - android.hardware.camera2.cts.StillCaptureTest#testFocalLengths - android.hardware.camera2.cts.StillCaptureTest#testJpegExif Signed-off-by: Paul Elder --- Changes in v2: - break out processControls and thumbnailer configuration into different patches - fix android metadata entry number and size - other small changes --- src/android/camera_device.cpp | 30 ++++++-- src/android/camera_device.h | 5 +- src/android/camera_stream.cpp | 7 +- src/android/camera_stream.h | 4 +- src/android/jpeg/post_processor_jpeg.cpp | 90 ++++++++++++++++++++---- src/android/jpeg/post_processor_jpeg.h | 3 +- src/android/post_processor.h | 3 +- 7 files changed, 116 insertions(+), 26 deletions(-) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 49af221b..1b803c92 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -296,8 +296,9 @@ MappedCamera3Buffer::MappedCamera3Buffer(const buffer_handle_t camera3buffer, */ CameraDevice::Camera3RequestDescriptor::Camera3RequestDescriptor( - Camera *camera, unsigned int frameNumber, unsigned int numBuffers) - : frameNumber_(frameNumber), numBuffers_(numBuffers) + Camera *camera, unsigned int frameNumber, unsigned int numBuffers, + CameraMetadata &settings) + : frameNumber_(frameNumber), numBuffers_(numBuffers), settings_(settings) { buffers_ = new camera3_stream_buffer_t[numBuffers]; @@ -1691,9 +1692,12 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques * The descriptor and the associated memory reserved here are freed * at request complete time. */ + /* \todo Handle null request settings */ + CameraMetadata settings(camera3Request->settings); Camera3RequestDescriptor *descriptor = new Camera3RequestDescriptor(camera_.get(), camera3Request->frame_number, - camera3Request->num_output_buffers); + camera3Request->num_output_buffers, + settings); LOG(HAL, Debug) << "Queueing Request to libcamera with " << descriptor->numBuffers_ << " HAL streams"; @@ -1828,6 +1832,7 @@ void CameraDevice::requestComplete(Request *request) } int ret = cameraStream->process(*buffer, &mapped, + descriptor->settings_, resultMetadata.get()); if (ret) { status = CAMERA3_BUFFER_STATUS_ERROR; @@ -1924,14 +1929,21 @@ CameraDevice::getResultMetadata(Camera3RequestDescriptor *descriptor, /* * \todo Keep this in sync with the actual number of entries. - * Currently: 33 entries, 75 bytes + * Currently: 40 entries, 163 bytes * * Reserve more space for the JPEG metadata set by the post-processor. * Currently: ANDROID_JPEG_SIZE (int32_t), ANDROID_JPEG_QUALITY (byte), + * ANDROID_JPEG_GPS_COORDINATES (double x 3) = 24 bytes + * ANDROID_JPEG_GPS_PROCESSING_METHOD (byte x 32) = 32 bytes + * ANDROID_JPEG_GPS_TIMESTAMP (int64) = 8 bytes * ANDROID_JPEG_ORIENTATION (int32_t) = 3 entries, 9 bytes. + * ANDROID_JPEG_THUMBNAIL_QUALITY (int32 x 2) = 8 bytes + * ANDROID_JPEG_THUMBNAIL_SIZE (int32 x 2) = 8 bytes + * ANDROID_LENS_APERTURE (float) = 4 bytes + * ANDROID_LENS_FOCAL_LENGTH (float) = 4 bytes */ std::unique_ptr resultMetadata = - std::make_unique(33, 75); + std::make_unique(40, 163); if (!resultMetadata->isValid()) { LOG(HAL, Error) << "Failed to allocate static metadata"; return nullptr; @@ -1996,6 +2008,14 @@ CameraDevice::getResultMetadata(Camera3RequestDescriptor *descriptor, value = ANDROID_FLASH_STATE_UNAVAILABLE; resultMetadata->addEntry(ANDROID_FLASH_STATE, &value, 1); + camera_metadata_ro_entry_t entry; + int ret = descriptor->settings_.getEntry(ANDROID_LENS_APERTURE, &entry); + if (ret) + resultMetadata->addEntry(ANDROID_LENS_APERTURE, entry.data.f, 1); + + float focal_length = 1.0; + resultMetadata->addEntry(ANDROID_LENS_FOCAL_LENGTH, &focal_length, 1); + value = ANDROID_LENS_STATE_STATIONARY; resultMetadata->addEntry(ANDROID_LENS_STATE, &value, 1); diff --git a/src/android/camera_device.h b/src/android/camera_device.h index a285d0a1..111a7d8f 100644 --- a/src/android/camera_device.h +++ b/src/android/camera_device.h @@ -24,6 +24,7 @@ #include "libcamera/internal/log.h" #include "libcamera/internal/message.h" +#include "camera_metadata.h" #include "camera_stream.h" #include "camera_worker.h" #include "jpeg/encoder.h" @@ -78,7 +79,8 @@ private: struct Camera3RequestDescriptor { Camera3RequestDescriptor(libcamera::Camera *camera, unsigned int frameNumber, - unsigned int numBuffers); + unsigned int numBuffers, + CameraMetadata &settings); ~Camera3RequestDescriptor(); uint32_t frameNumber_; @@ -86,6 +88,7 @@ private: camera3_stream_buffer_t *buffers_; std::vector> frameBuffers_; std::unique_ptr request_; + CameraMetadata settings_; }; struct Camera3StreamConfiguration { diff --git a/src/android/camera_stream.cpp b/src/android/camera_stream.cpp index dba351a4..4c8020e5 100644 --- a/src/android/camera_stream.cpp +++ b/src/android/camera_stream.cpp @@ -96,12 +96,15 @@ int CameraStream::configure() } int CameraStream::process(const libcamera::FrameBuffer &source, - MappedCamera3Buffer *dest, CameraMetadata *metadata) + MappedCamera3Buffer *dest, + const CameraMetadata &requestMetadata, + CameraMetadata *resultMetadata) { if (!postProcessor_) return 0; - return postProcessor_->process(source, dest->maps()[0], metadata); + return postProcessor_->process(source, dest->maps()[0], + requestMetadata, resultMetadata); } FrameBuffer *CameraStream::getBuffer() diff --git a/src/android/camera_stream.h b/src/android/camera_stream.h index cc9d5470..298ffbf6 100644 --- a/src/android/camera_stream.h +++ b/src/android/camera_stream.h @@ -119,7 +119,9 @@ public: int configure(); int process(const libcamera::FrameBuffer &source, - MappedCamera3Buffer *dest, CameraMetadata *metadata); + MappedCamera3Buffer *dest, + const CameraMetadata &requestMetadata, + CameraMetadata *resultMetadata); libcamera::FrameBuffer *getBuffer(); void putBuffer(libcamera::FrameBuffer *buffer); diff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp index 0c1226ad..ef5e011e 100644 --- a/src/android/jpeg/post_processor_jpeg.cpp +++ b/src/android/jpeg/post_processor_jpeg.cpp @@ -90,17 +90,26 @@ void PostProcessorJpeg::generateThumbnail(const FrameBuffer &source, int PostProcessorJpeg::process(const FrameBuffer &source, Span destination, - CameraMetadata *metadata) + const CameraMetadata &requestMetadata, + CameraMetadata *resultMetadata) { if (!encoder_) return 0; + camera_metadata_ro_entry_t entry; + int ret; + /* Set EXIF metadata for various tags. */ Exif exif; - /* \todo Set Make and Model from external vendor tags. */ - exif.setMake("libcamera"); - exif.setModel("cameraModel"); - exif.setOrientation(cameraDevice_->orientation()); + exif.setMake(cameraDevice_->cameraMake()); + exif.setModel(cameraDevice_->cameraModel()); + + ret = requestMetadata.getEntry(ANDROID_JPEG_ORIENTATION, &entry); + + const uint32_t jpeg_orientation = ret ? *entry.data.i32 : 0; + resultMetadata->addEntry(ANDROID_JPEG_ORIENTATION, &jpeg_orientation, 1); + exif.setOrientation(ret ? *entry.data.i32 : jpeg_orientation); + exif.setSize(streamSize_); /* * We set the frame's EXIF timestamp as the time of encode. @@ -109,10 +118,62 @@ int PostProcessorJpeg::process(const FrameBuffer &source, */ exif.setTimestamp(std::time(nullptr)); - std::vector thumbnail; - generateThumbnail(source, &thumbnail); - if (!thumbnail.empty()) - exif.setThumbnail(thumbnail, Exif::Compression::JPEG); + exif.setExposureTime(0); + ret = requestMetadata.getEntry(ANDROID_LENS_APERTURE, &entry); + if (ret) + exif.setAperture(*entry.data.f); + exif.setISO(100); + exif.setFlash(Exif::Flash::FlashNotPresent); + exif.setWhiteBalance(Exif::WhiteBalance::Auto); + + exif.setSubsecTime(0); + exif.setSubsecTimeOriginal(0); + exif.setSubsecTimeDigitized(0); + + exif.setFocalLength(1.0); + + ret = requestMetadata.getEntry(ANDROID_JPEG_GPS_TIMESTAMP, &entry); + if (ret) { + exif.setGPSDateTimestamp(*entry.data.i64); + resultMetadata->addEntry(ANDROID_JPEG_GPS_TIMESTAMP, + entry.data.i64, 1); + } + + ret = requestMetadata.getEntry(ANDROID_JPEG_THUMBNAIL_SIZE, &entry); + if (ret) { + const int32_t *data = entry.data.i32; + Size thumbnailSize = { static_cast(data[0]), + static_cast(data[1]) }; + + std::vector thumbnail; + if (thumbnailSize != Size(0, 0)) { + configureThumbnailer(thumbnailSize, thumbnailer_.pixelFormat()); + generateThumbnail(source, &thumbnail); + exif.setThumbnail(thumbnail, Exif::Compression::JPEG); + } + + resultMetadata->addEntry(ANDROID_JPEG_THUMBNAIL_SIZE, data, 2); + + /* \todo Use this quality as a parameter to the encoder */ + ret = requestMetadata.getEntry(ANDROID_JPEG_THUMBNAIL_QUALITY, &entry); + if (ret) + resultMetadata->addEntry(ANDROID_JPEG_THUMBNAIL_QUALITY, entry.data.u8, 1); + } + + ret = requestMetadata.getEntry(ANDROID_JPEG_GPS_COORDINATES, &entry); + if (ret) { + exif.setGPSLocation(entry.data.d); + resultMetadata->addEntry(ANDROID_JPEG_GPS_COORDINATES, + entry.data.d, 3); + } + + ret = requestMetadata.getEntry(ANDROID_JPEG_GPS_PROCESSING_METHOD, &entry); + if (ret) { + std::string method(entry.data.u8, entry.data.u8 + 32); + exif.setGPSMethod(method); + resultMetadata->addEntry(ANDROID_JPEG_GPS_PROCESSING_METHOD, + entry.data.u8, 32); + } if (exif.generate() != 0) LOG(JPEG, Error) << "Failed to generate valid EXIF data"; @@ -143,13 +204,12 @@ int PostProcessorJpeg::process(const FrameBuffer &source, blob->jpeg_size = jpeg_size; /* Update the JPEG result Metadata. */ - metadata->addEntry(ANDROID_JPEG_SIZE, &jpeg_size, 1); - - const uint32_t jpeg_quality = 95; - metadata->addEntry(ANDROID_JPEG_QUALITY, &jpeg_quality, 1); + resultMetadata->addEntry(ANDROID_JPEG_SIZE, &jpeg_size, 1); - const uint32_t jpeg_orientation = 0; - metadata->addEntry(ANDROID_JPEG_ORIENTATION, &jpeg_orientation, 1); + /* \todo Configure JPEG encoder with this */ + ret = requestMetadata.getEntry(ANDROID_JPEG_QUALITY, &entry); + const uint32_t jpeg_quality = ret ? *entry.data.u8 : 95; + resultMetadata->addEntry(ANDROID_JPEG_QUALITY, &jpeg_quality, 1); return 0; } diff --git a/src/android/jpeg/post_processor_jpeg.h b/src/android/jpeg/post_processor_jpeg.h index c545c29c..d07c9fe5 100644 --- a/src/android/jpeg/post_processor_jpeg.h +++ b/src/android/jpeg/post_processor_jpeg.h @@ -26,7 +26,8 @@ public: const libcamera::StreamConfiguration &outcfg) override; int process(const libcamera::FrameBuffer &source, libcamera::Span destination, - CameraMetadata *metadata) override; + const CameraMetadata &requestMetadata, + CameraMetadata *resultMetadata) override; private: void generateThumbnail(const libcamera::FrameBuffer &source, diff --git a/src/android/post_processor.h b/src/android/post_processor.h index e0f91880..bda93bb4 100644 --- a/src/android/post_processor.h +++ b/src/android/post_processor.h @@ -22,7 +22,8 @@ public: const libcamera::StreamConfiguration &outCfg) = 0; virtual int process(const libcamera::FrameBuffer &source, libcamera::Span destination, - CameraMetadata *metadata) = 0; + const CameraMetadata &requestMetadata, + CameraMetadata *resultMetadata) = 0; }; #endif /* __ANDROID_POST_PROCESSOR_H__ */ From patchwork Thu Jan 21 10:15:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10921 X-Patchwork-Delegate: paul.elder@ideasonboard.com 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 83F30C0F2A for ; Thu, 21 Jan 2021 10:16:20 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 51C95681EF; Thu, 21 Jan 2021 11:16:20 +0100 (CET) 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="lflWmg1O"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 19B78681EF for ; Thu, 21 Jan 2021 11:16:19 +0100 (CET) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id AD66550E; Thu, 21 Jan 2021 11:16:17 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611224178; bh=mjONAGbRA+tc5z85O+RqaqpU94ZOA1u56IAhNeFbSbQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lflWmg1O0iG+AE9WCFzEou2aXO2c/XenEc/qNEdMi7nCBv6xZbK0vF8Rvb3X77l6r rX8VIiSKJvRSpQ6sF6IIHEXJE3FxiQD/lBEkQntoozB96Sq9KL8hJV9q+eUTEc/RLy rw0vUjAOdKG5paXu/8WPlNhbCUiacAU4qbl1gu2c= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Thu, 21 Jan 2021 19:15:49 +0900 Message-Id: <20210121101549.134574-10-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210121101549.134574-1-paul.elder@ideasonboard.com> References: <20210121101549.134574-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 9/9] android: camera_device: Cache request metadata 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" The settings in an android capture request may be null, in which case the settings from the most recently submitted capture request should be used. Cache the request settings to achieve this. Signed-off-by: Paul Elder --- New in v2 --- src/android/camera_device.cpp | 10 ++++++---- src/android/camera_device.h | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 1b803c92..4f92cecc 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -337,7 +337,9 @@ CameraDevice::Camera3RequestDescriptor::~Camera3RequestDescriptor() CameraDevice::CameraDevice(unsigned int id, const std::shared_ptr &camera) : id_(id), running_(false), camera_(camera), staticMetadata_(nullptr), - facing_(CAMERA_FACING_FRONT), orientation_(0) + facing_(CAMERA_FACING_FRONT), orientation_(0), + /* \todo Keep this in sync with the actual number of entries. */ + lastSettings_(CameraMetadata(40, 163)) { camera_->requestCompleted.connect(this, &CameraDevice::requestComplete); @@ -1692,12 +1694,12 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques * The descriptor and the associated memory reserved here are freed * at request complete time. */ - /* \todo Handle null request settings */ - CameraMetadata settings(camera3Request->settings); + if (camera3Request->settings) + lastSettings_ = CameraMetadata(camera3Request->settings); Camera3RequestDescriptor *descriptor = new Camera3RequestDescriptor(camera_.get(), camera3Request->frame_number, camera3Request->num_output_buffers, - settings); + lastSettings_); LOG(HAL, Debug) << "Queueing Request to libcamera with " << descriptor->numBuffers_ << " HAL streams"; diff --git a/src/android/camera_device.h b/src/android/camera_device.h index 111a7d8f..d3a9f777 100644 --- a/src/android/camera_device.h +++ b/src/android/camera_device.h @@ -139,6 +139,8 @@ private: int orientation_; unsigned int maxJpegBufferSize_; + + CameraMetadata lastSettings_; }; #endif /* __ANDROID_CAMERA_DEVICE_H__ */