From patchwork Sat Jan 23 05:16:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10966 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 3B5D5C0F2B for ; Sat, 23 Jan 2021 05:17:18 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0740968261; Sat, 23 Jan 2021 06:17: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="dj9up+Cx"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 19AE368261 for ; Sat, 23 Jan 2021 06:17:16 +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 64B64813; Sat, 23 Jan 2021 06:17:14 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611379035; bh=9WkXGyOtEF2dbo92v7IcbVD/EwSZEIh7QEoB2XMX8Dc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dj9up+Cx4nyvKIZ4LtWBYj3yLxrI+VwZMMfEggYFwyLVPHzORL//mqSsccWyaFh8E 5F9Bd4iblpgGSyeTHOYaIO9W54JvQerzfjdwPMEaZD+n7rOBfXbFOzLIJ8rwPqcean yayZvkJfrZ2mxaRSeUcgxLrKpFHeS5RlquNeUjeA= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Sat, 23 Jan 2021 14:16:57 +0900 Message-Id: <20210123051704.188117-2-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210123051704.188117-1-paul.elder@ideasonboard.com> References: <20210123051704.188117-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 1/8] 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 Reviewed-by: Laurent Pinchart --- Changes in v3: - use array to contain string encoding codes - drop JIS and Undefined - clean up setString a bit - use the endianness of the exif Changes in v2: - moved from utils into exif - support no-encoding --- src/android/jpeg/exif.cpp | 88 +++++++++++++++++++++++++++++++++++---- src/android/jpeg/exif.h | 10 ++++- 2 files changed, 89 insertions(+), 9 deletions(-) diff --git a/src/android/jpeg/exif.cpp b/src/android/jpeg/exif.cpp index 33b3fa7f..5d9492fb 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" @@ -178,11 +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::Unicode, { 0x55, 0x4e, 0x49, 0x43, 0x4f, 0x44, 0x45, 0x00 } }, +}; + +void Exif::setString(ExifIfd ifd, ExifTag tag, ExifFormat format, + const std::string &item, StringEncoding encoding) { std::string ascii; size_t length; const char *str; + std::vector buf; if (format == EXIF_FORMAT_ASCII) { ascii = utils::toAscii(item); @@ -191,13 +201,47 @@ 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(); + std::u16string u16str; + + auto encodingString = stringEncodingCodes.find(encoding); + if (encodingString != stringEncodingCodes.end()) { + buf = { + encodingString->second.begin(), + encodingString->second.end() + }; + } + + switch (encoding) { + case Unicode: + u16str = utf8ToUtf16(item); + + buf.resize(8 + u16str.size() * 2); + for (size_t i = 0; i < u16str.size(); i++) { + if (order_ == EXIF_BYTE_ORDER_INTEL) { + buf[8 + 2 * i] = (u16str[i] & 0xff); + buf[8 + 2 * i + 1] = ((u16str[i] >> 8) & 0xff); + } else { + buf[8 + 2 * i] = ((u16str[i] >> 8) & 0xff); + buf[8 + 2 * i + 1] = (u16str[i] & 0xff); + } + } + + str = reinterpret_cast(buf.data()); + length = buf.size(); + + break; + case ASCII: + case NoEncoding: + buf.insert(buf.end(), item.begin(), item.end()); + str = reinterpret_cast(buf.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); @@ -290,6 +334,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) +{ + 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..73f231b2 100644 --- a/src/android/jpeg/exif.h +++ b/src/android/jpeg/exif.h @@ -26,6 +26,12 @@ public: JPEG = 6, }; + enum StringEncoding { + NoEncoding = 0, + ASCII = 1, + Unicode = 2, + }; + void setMake(const std::string &make); void setModel(const std::string &model); @@ -46,9 +52,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); + const std::string &item, StringEncoding encoding = NoEncoding); void setRational(ExifIfd ifd, ExifTag tag, ExifRational item); + std::u16string utf8ToUtf16(const std::string &str); + bool valid_; ExifData *data_; From patchwork Sat Jan 23 05:16:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10967 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 943AFC0F2B for ; Sat, 23 Jan 2021 05:17:19 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5EF6F6828F; Sat, 23 Jan 2021 06:17:19 +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="FWXpv93r"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6E11F6827C for ; Sat, 23 Jan 2021 06:17:18 +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 8C4F78AE; Sat, 23 Jan 2021 06:17:16 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611379038; bh=jHTXVB+m5+o+aUCtFb/59jK9ktOvjGcW2WdLzFFTgbA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FWXpv93r1ot8IwbrCE7XI1P5PUmHTXdMYx8ljsTSeRjKOS4bKLVkpwC5lm5LFzoTo 1H/wouBX8JBQ29GK9n1CwoxJjutmwam9BmZiLB4ISe+Mw+ZPlc7jipaFEKw3J/kzN6 LV/m9Q8IbjyOLW7amvApbaTFzzWXwuzAzyMDscWw= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Sat, 23 Jan 2021 14:16:58 +0900 Message-Id: <20210123051704.188117-3-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210123051704.188117-1-paul.elder@ideasonboard.com> References: <20210123051704.188117-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 2/8] 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 --- No change in v3 Changes in v2 - expand commit message --- 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 5d9492fb..d4f86006 100644 --- a/src/android/jpeg/exif.cpp +++ b/src/android/jpeg/exif.cpp @@ -307,13 +307,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 Sat Jan 23 05:16:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10968 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 0A147C0F2B for ; Sat, 23 Jan 2021 05:17:22 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C221F68295; Sat, 23 Jan 2021 06:17:21 +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="H8doAEnR"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9E5E96827C for ; Sat, 23 Jan 2021 06:17:20 +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 E262F813; Sat, 23 Jan 2021 06:17:18 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611379040; bh=I8NnV13AVZAQZMyF+hNBZ2sAh2pCMvJktxKjQio76s4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=H8doAEnR5NIq8VnbDfBspED3o+Gi8HT4bmpCL5C1XRdPxQu76JsKjoThZGFkHZJKS Vc+SHVVXQIfaGMYJs4Vm/xs0e9bkxU2Vty/dvI6T8yodNMYUuyBi1QqSEKaN8rFsrH Q8cupgzOjge0Mnm2e0ZN3kVc6sgLc/Ko075UEs4o= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Sat, 23 Jan 2021 14:16:59 +0900 Message-Id: <20210123051704.188117-4-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210123051704.188117-1-paul.elder@ideasonboard.com> References: <20210123051704.188117-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 3/8] 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 v3: - merge setting subsec into setting the main timestamp Changes in v2: - some cosmetic and precision changes - use the new setString --- src/android/jpeg/exif.cpp | 175 +++++++++++++++++++++++++++++++++++--- src/android/jpeg/exif.h | 38 ++++++++- 2 files changed, 198 insertions(+), 15 deletions(-) diff --git a/src/android/jpeg/exif.cpp b/src/android/jpeg/exif.cpp index d4f86006..06cb4808 100644 --- a/src/android/jpeg/exif.cpp +++ b/src/android/jpeg/exif.cpp @@ -7,7 +7,9 @@ #include "exif.h" +#include #include +#include #include #include "libcamera/internal/log.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); @@ -268,7 +280,7 @@ void Exif::setSize(const Size &size) setLong(EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION, size.width); } -void Exif::setTimestamp(time_t timestamp) +void Exif::setTimestamp(time_t timestamp, uint64_t subsec) { struct tm tm; localtime_r(×tamp, &tm); @@ -283,19 +295,121 @@ void Exif::setTimestamp(time_t timestamp) /* Query and set timezone information if available. */ int r = strftime(str, sizeof(str), "%z", &tm); - if (r > 0) { - std::string tz(str); - tz.insert(3, 1, ':'); - setString(EXIF_IFD_EXIF, - static_cast(_ExifTag::OFFSET_TIME), - EXIF_FORMAT_ASCII, tz); - setString(EXIF_IFD_EXIF, - static_cast(_ExifTag::OFFSET_TIME_ORIGINAL), - EXIF_FORMAT_ASCII, tz); - setString(EXIF_IFD_EXIF, - static_cast(_ExifTag::OFFSET_TIME_DIGITIZED), - EXIF_FORMAT_ASCII, tz); - } + if (r <= 0) + return; + + std::string tz(str); + tz.insert(3, 1, ':'); + setString(EXIF_IFD_EXIF, + static_cast(_ExifTag::OFFSET_TIME), + EXIF_FORMAT_ASCII, tz); + setString(EXIF_IFD_EXIF, + static_cast(_ExifTag::OFFSET_TIME_ORIGINAL), + EXIF_FORMAT_ASCII, tz); + setString(EXIF_IFD_EXIF, + static_cast(_ExifTag::OFFSET_TIME_DIGITIZED), + EXIF_FORMAT_ASCII, tz); + + setString(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME, + EXIF_FORMAT_ASCII, std::to_string(subsec)); + setString(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_ORIGINAL, + EXIF_FORMAT_ASCII, std::to_string(subsec)); + setString(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_DIGITIZED, + EXIF_FORMAT_ASCII, std::to_string(subsec)); +} + +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, 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 + */ +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, 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, 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, method, Unicode); } void Exif::setOrientation(int orientation) @@ -334,6 +448,39 @@ 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)); +} + /** * \brief Convert UTF-8 string to UTF-16 string * \param[in] str String to convert diff --git a/src/android/jpeg/exif.h b/src/android/jpeg/exif.h index 73f231b2..7d8727ad 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, @@ -39,7 +60,18 @@ public: void setSize(const libcamera::Size &size); void setThumbnail(libcamera::Span thumbnail, Compression compression); - void setTimestamp(time_t timestamp); + void setTimestamp(time_t timestamp, uint64_t subsec); + + 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); libcamera::Span data() const { return { exifData_, size_ }; } [[nodiscard]] int generate(); @@ -49,12 +81,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, const std::string &item, StringEncoding encoding = NoEncoding); 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 Sat Jan 23 05:17:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10969 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 815FCC0F2B for ; Sat, 23 Jan 2021 05:17:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4EC7768299; Sat, 23 Jan 2021 06:17:25 +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="EyxFgFZH"; 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 0AF0B68261 for ; Sat, 23 Jan 2021 06:17:24 +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 12F90813; Sat, 23 Jan 2021 06:17:20 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611379042; bh=cGMm4a8BNd/BSZwtFQp+QkkR5Coh5AJ2kWJs8uISCKY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EyxFgFZH0rSvO6z3AJw01bJ1Q3LbRGc3/8g+dELH/rOV7yVYS6V3hbEiWt6DSDVxB mpv2zmyFGl61NYhObegBWLLpNKVhnAJIQcZC7UnxFQjoSXDiP2Qhu/sMBzHtqaf3Gm sWvtZpDl8eeSFI1O76JX0s7gorFQsbZLiei4HFXw= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Sat, 23 Jan 2021 14:17:00 +0900 Message-Id: <20210123051704.188117-5-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210123051704.188117-1-paul.elder@ideasonboard.com> References: <20210123051704.188117-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 4/8] 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 v3: - rename cameraMake_ and cameraModel_ to maker_ and model_ 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 3983c6dc..592e2d43 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 @@ -351,6 +352,28 @@ CameraDevice::CameraDevice(unsigned int id, const std::shared_ptr &camer * streamConfiguration. */ maxJpegBufferSize_ = 13 << 20; /* 13631488 from USB HAL */ + + maker_ = "libcamera"; + model_ = "cameraModel"; + + /* \todo Support getting properties on Android */ + std::ifstream fstream("/var/cache/camera/camera.prop"); + if (!fstream.is_open()) + return; + + std::string line; + while (std::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")) + model_ = val; + else if (!key.compare("ro.product.manufacturer")) + maker_ = val; + } } CameraDevice::~CameraDevice() diff --git a/src/android/camera_device.h b/src/android/camera_device.h index 597d11fc..058a3f9a 100644 --- a/src/android/camera_device.h +++ b/src/android/camera_device.h @@ -55,6 +55,8 @@ public: return config_.get(); } + const std::string &cameraMake() const { return maker_; } + const std::string &cameraModel() const { return model_; } int facing() const { return facing_; } int orientation() const { return orientation_; } unsigned int maxJpegBufferSize() const { return maxJpegBufferSize_; } @@ -125,6 +127,9 @@ private: std::map formatsMap_; std::vector streams_; + std::string maker_; + std::string model_; + int facing_; int orientation_; From patchwork Sat Jan 23 05:17:01 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10970 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 E772FC0F2B for ; Sat, 23 Jan 2021 05:17:26 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B2C1F68283; Sat, 23 Jan 2021 06:17:26 +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="Mcw5ifQ6"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id AF7A168274 for ; Sat, 23 Jan 2021 06:17:25 +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 56537813; Sat, 23 Jan 2021 06:17:24 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611379045; bh=/j868jIPNmYE5/IOGJ3WqYqkeuYNJ8xnBhryD532yE4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Mcw5ifQ64E4TYatV8FTVz/ZBjkHsjrVPTPt+0BmExCffZb3Jy0uAggtWEZFhK/OI/ RAmb3qvDCm5nCpmSiRe1lj7cb9nPlQr6R69huLi427MgNg+KPccnSa9/YSjvXep5XH 5Yoz0QoFzOKKH7EfDjOH7GWsV5qpgquvzFjlC1Do= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Sat, 23 Jan 2021 14:17:01 +0900 Message-Id: <20210123051704.188117-6-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210123051704.188117-1-paul.elder@ideasonboard.com> References: <20210123051704.188117-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 5/8] 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_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 Reviewed-by: Laurent Pinchart --- Changes in v3: - fix metadata entries and byte count - fix gps processing method string truncation - move thumbnail handling to a different patch 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 | 22 +++++++- src/android/camera_stream.cpp | 7 ++- src/android/camera_stream.h | 4 +- src/android/jpeg/post_processor_jpeg.cpp | 64 +++++++++++++++++++----- src/android/jpeg/post_processor_jpeg.h | 3 +- src/android/post_processor.h | 3 +- 6 files changed, 84 insertions(+), 19 deletions(-) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 592e2d43..d0922db7 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -1688,6 +1688,8 @@ 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); @@ -1817,6 +1819,7 @@ void CameraDevice::requestComplete(Request *request) } int ret = cameraStream->process(*buffer, &mapped, + descriptor->settings_, resultMetadata.get()); if (ret) { status = CAMERA3_BUFFER_STATUS_ERROR; @@ -1913,14 +1916,21 @@ CameraDevice::getResultMetadata(Camera3RequestDescriptor *descriptor, /* * \todo Keep this in sync with the actual number of entries. - * Currently: 33 entries, 75 bytes + * Currently: 40 entries, 156 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 (byte) = 1 byte + * 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, 156); if (!resultMetadata->isValid()) { LOG(HAL, Error) << "Failed to allocate static metadata"; return nullptr; @@ -1985,6 +1995,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_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 436a50f8..b325a06a 100644 --- a/src/android/jpeg/post_processor_jpeg.cpp +++ b/src/android/jpeg/post_processor_jpeg.cpp @@ -80,30 +80,71 @@ 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 jpegOrientation = ret ? *entry.data.i32 : 0; + resultMetadata->addEntry(ANDROID_JPEG_ORIENTATION, &jpegOrientation, 1); + exif.setOrientation(jpegOrientation); + exif.setSize(streamSize_); /* * We set the frame's EXIF timestamp as the time of encode. * Since the precision we need for EXIF timestamp is only one * second, it is good enough. */ - exif.setTimestamp(std::time(nullptr)); + exif.setTimestamp(std::time(nullptr), 0); + + 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.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); + } std::vector thumbnail; generateThumbnail(source, &thumbnail); if (!thumbnail.empty()) exif.setThumbnail(thumbnail, Exif::Compression::JPEG); + 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 + entry.count); + exif.setGPSMethod(method); + resultMetadata->addEntry(ANDROID_JPEG_GPS_PROCESSING_METHOD, + entry.data.u8, entry.count); + } + if (exif.generate() != 0) LOG(JPEG, Error) << "Failed to generate valid EXIF data"; @@ -133,13 +174,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 5afa831c..d721d1b9 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 Sat Jan 23 05:17:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10971 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 55176C0F2B for ; Sat, 23 Jan 2021 05:17:29 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1F6F968277; Sat, 23 Jan 2021 06:17:29 +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="lmlnKElu"; 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 89AF66030D for ; Sat, 23 Jan 2021 06:17:27 +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 17123813; Sat, 23 Jan 2021 06:17:25 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611379047; bh=q9Uu+V7CSqF7jZM5ayXtIdkotoiQdam0sPajR8Q9J74=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lmlnKEluCNdi9lsCLplQgGKFrrpN1adom8aN+E5VJCgeIxNZeoiflngoUcecevdbr jsnTXZ0drxIeXwzdQXMlLIpN76WVL+6uKVOniBUmW+Wf9mFzZX/Z4WMBOuTrodtp+1 wnX1fV59a0oCk4J/dSbJQN1e689ONrNaBYSzk+Ks= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Sat, 23 Jan 2021 14:17:02 +0900 Message-Id: <20210123051704.188117-7-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210123051704.188117-1-paul.elder@ideasonboard.com> References: <20210123051704.188117-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 6/8] android: jpeg: Configure thumbnailer 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" Configure the thumbnailer based on the thumbnail parameters given by the android request metadata. Only the thumbnail encoder needs to be configured, and since it is only used at post-processing time, move the configuration out of the post-processor constructor and into the processing step. Also set the following android result metadata tags: - ANDROID_JPEG_THUMBNAIL_SIZE - ANDROID_JPEG_THUMBNAIL_QUALITY Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- New in v3 - split from "android: Set result metadata and EXIF fields based on request" --- src/android/jpeg/post_processor_jpeg.cpp | 40 +++++++++++++++++------- src/android/jpeg/post_processor_jpeg.h | 1 + src/android/jpeg/thumbnailer.cpp | 25 ++------------- src/android/jpeg/thumbnailer.h | 4 +-- 4 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp index b325a06a..e5e42d39 100644 --- a/src/android/jpeg/post_processor_jpeg.cpp +++ b/src/android/jpeg/post_processor_jpeg.cpp @@ -41,12 +41,6 @@ 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; - } encoder_ = std::make_unique(); @@ -54,14 +48,20 @@ int PostProcessorJpeg::configure(const StreamConfiguration &inCfg, } void PostProcessorJpeg::generateThumbnail(const FrameBuffer &source, + const Size &targetSize, std::vector *thumbnail) { /* Stores the raw scaled-down thumbnail bytes. */ std::vector rawThumbnail; - thumbnailer_.createThumbnail(source, &rawThumbnail); + thumbnailer_.createThumbnail(source, targetSize, &rawThumbnail); + + StreamConfiguration thCfg; + thCfg.size = targetSize; + thCfg.pixelFormat = thumbnailer_.pixelFormat(); + int ret = thumbnailEncoder_.configure(thCfg); - if (!rawThumbnail.empty()) { + if (!rawThumbnail.empty() && !ret) { /* * \todo Avoid value-initialization of all elements of the * vector. @@ -125,10 +125,26 @@ int PostProcessorJpeg::process(const FrameBuffer &source, entry.data.i64, 1); } - std::vector thumbnail; - generateThumbnail(source, &thumbnail); - if (!thumbnail.empty()) - exif.setThumbnail(thumbnail, Exif::Compression::JPEG); + 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]) }; + + if (thumbnailSize != Size(0, 0)) { + std::vector thumbnail; + generateThumbnail(source, thumbnailSize, &thumbnail); + if (!thumbnail.empty()) + 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) { diff --git a/src/android/jpeg/post_processor_jpeg.h b/src/android/jpeg/post_processor_jpeg.h index d721d1b9..660b79b4 100644 --- a/src/android/jpeg/post_processor_jpeg.h +++ b/src/android/jpeg/post_processor_jpeg.h @@ -31,6 +31,7 @@ public: private: void generateThumbnail(const libcamera::FrameBuffer &source, + const libcamera::Size &targetSize, std::vector *thumbnail); CameraDevice *const cameraDevice_; diff --git a/src/android/jpeg/thumbnailer.cpp b/src/android/jpeg/thumbnailer.cpp index 5374538a..f709d343 100644 --- a/src/android/jpeg/thumbnailer.cpp +++ b/src/android/jpeg/thumbnailer.cpp @@ -32,30 +32,11 @@ void Thumbnailer::configure(const Size &sourceSize, PixelFormat pixelFormat) return; } - targetSize_ = computeThumbnailSize(); - valid_ = true; } -/* - * The Exif specification recommends the width of the thumbnail to be a - * multiple of 16 (section 4.8.1). Hence, compute the corresponding height - * keeping the aspect ratio same as of the source. - */ -Size Thumbnailer::computeThumbnailSize() const -{ - unsigned int targetHeight; - constexpr unsigned int kTargetWidth = 160; - - targetHeight = kTargetWidth * sourceSize_.height / sourceSize_.width; - - if (targetHeight & 1) - targetHeight++; - - return Size(kTargetWidth, targetHeight); -} - void Thumbnailer::createThumbnail(const FrameBuffer &source, + const Size &targetSize, std::vector *destination) { MappedFrameBuffer frame(&source, PROT_READ); @@ -73,8 +54,8 @@ void Thumbnailer::createThumbnail(const FrameBuffer &source, const unsigned int sw = sourceSize_.width; const unsigned int sh = sourceSize_.height; - const unsigned int tw = targetSize_.width; - const unsigned int th = targetSize_.height; + const unsigned int tw = targetSize.width; + const unsigned int th = targetSize.height; ASSERT(tw % 2 == 0 && th % 2 == 0); diff --git a/src/android/jpeg/thumbnailer.h b/src/android/jpeg/thumbnailer.h index 98f11833..bbd9832d 100644 --- a/src/android/jpeg/thumbnailer.h +++ b/src/android/jpeg/thumbnailer.h @@ -20,15 +20,15 @@ public: void configure(const libcamera::Size &sourceSize, libcamera::PixelFormat pixelFormat); void createThumbnail(const libcamera::FrameBuffer &source, + const libcamera::Size &targetSize, std::vector *dest); - const libcamera::Size &size() const { return targetSize_; } + const libcamera::PixelFormat &pixelFormat() const { return pixelFormat_; } private: libcamera::Size computeThumbnailSize() const; libcamera::PixelFormat pixelFormat_; libcamera::Size sourceSize_; - libcamera::Size targetSize_; bool valid_; }; From patchwork Sat Jan 23 05:17:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10972 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 A5149C0F2B for ; Sat, 23 Jan 2021 05:17:30 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7188B68293; Sat, 23 Jan 2021 06:17:30 +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="lUPJd02V"; 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 61F9B68274 for ; Sat, 23 Jan 2021 06:17:29 +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 E4E53813; Sat, 23 Jan 2021 06:17:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611379049; bh=bjAMJMHw0pf2CPqW4NrUeO2uoPeGXlYDjheFYUHFoTs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lUPJd02VTVYyLbov9FwsgW1+BVehQRzAIkzg/h02MXd0hfuQfjfVy53gMbDSV8rwC dxSq0E+jz30EGxbdiym0rRlxYFOgHSn396v/ndzfHd941hAM0Xk9fWi2KlrV+GfeU1 KAN1Tuds8lcfxbfyO9F2ahREZu4IZYazcTUhJZwg= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Sat, 23 Jan 2021 14:17:03 +0900 Message-Id: <20210123051704.188117-8-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210123051704.188117-1-paul.elder@ideasonboard.com> References: <20210123051704.188117-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 7/8] 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 Reviewed-by: Laurent Pinchart --- Changes in v3: - rebase on "android: camera device and metatada improvements", so it's a bit cleaner New in v2 --- src/android/camera_device.cpp | 9 ++++++--- src/android/camera_device.h | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index d0922db7..a2484fdd 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -343,7 +343,8 @@ 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), + lastSettings_(CameraMetadata(0, 0)) { camera_->requestCompleted.connect(this, &CameraDevice::requestComplete); @@ -1688,10 +1689,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); + if (camera3Request->settings) + lastSettings_ = camera3Request->settings; + else + descriptor->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 058a3f9a..fa4fb544 100644 --- a/src/android/camera_device.h +++ b/src/android/camera_device.h @@ -134,6 +134,8 @@ private: int orientation_; unsigned int maxJpegBufferSize_; + + CameraMetadata lastSettings_; }; #endif /* __ANDROID_CAMERA_DEVICE_H__ */ From patchwork Sat Jan 23 05:17:04 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10973 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 1D3A6C0F2B for ; Sat, 23 Jan 2021 05:17:33 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DC20668299; Sat, 23 Jan 2021 06:17:32 +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="NYjY3wlL"; 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 3E0716030D for ; Sat, 23 Jan 2021 06:17:31 +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 BA2248AE; Sat, 23 Jan 2021 06:17:29 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611379051; bh=K/tNt6KCTzoXKlUi/gogGUCIiPV4o0yos9fdSEz8YUs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NYjY3wlLCdKZGEyyYdgcfGWMJYXgSvbT6KF02VYH7QUSoeZ08whx++TGTy7FAAHTP C/x/5gpsqq/PCT9eeCy8hDCiHiQRupEMbIhTYspdDKWh/1eEjcUEmgyJSieQu3wHd9 OYnzsawoaPMLyKY/HANogmPCqAAvDxtbS3JnZ/jY= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Sat, 23 Jan 2021 14:17:04 +0900 Message-Id: <20210123051704.188117-9-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210123051704.188117-1-paul.elder@ideasonboard.com> References: <20210123051704.188117-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 8/8] android: jpeg: Set thumbnail and JPEG quality based on request 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 thumbnail quality and the JPEG quality based on the android request metadata. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- New in v3 --- src/android/jpeg/encoder.h | 3 ++- src/android/jpeg/encoder_libjpeg.cpp | 10 +++++----- src/android/jpeg/encoder_libjpeg.h | 8 ++++---- src/android/jpeg/post_processor_jpeg.cpp | 25 ++++++++++++------------ src/android/jpeg/post_processor_jpeg.h | 1 + 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/android/jpeg/encoder.h b/src/android/jpeg/encoder.h index 027233dc..8d449369 100644 --- a/src/android/jpeg/encoder.h +++ b/src/android/jpeg/encoder.h @@ -19,7 +19,8 @@ public: virtual int configure(const libcamera::StreamConfiguration &cfg) = 0; virtual int encode(const libcamera::FrameBuffer &source, libcamera::Span destination, - libcamera::Span exifData) = 0; + libcamera::Span exifData, + unsigned int quality) = 0; }; #endif /* __ANDROID_JPEG_ENCODER_H__ */ diff --git a/src/android/jpeg/encoder_libjpeg.cpp b/src/android/jpeg/encoder_libjpeg.cpp index aed919b9..f006e1d1 100644 --- a/src/android/jpeg/encoder_libjpeg.cpp +++ b/src/android/jpeg/encoder_libjpeg.cpp @@ -68,7 +68,6 @@ const struct JPEGPixelFormatInfo &findPixelInfo(const PixelFormat &format) } /* namespace */ EncoderLibJpeg::EncoderLibJpeg() - : quality_(95) { /* \todo Expand error handling coverage with a custom handler. */ compress_.err = jpeg_std_error(&jerr_); @@ -94,7 +93,6 @@ int EncoderLibJpeg::configure(const StreamConfiguration &cfg) compress_.input_components = info.colorSpace == JCS_GRAYSCALE ? 1 : 3; jpeg_set_defaults(&compress_); - jpeg_set_quality(&compress_, quality_, TRUE); pixelFormatInfo_ = &info.pixelFormatInfo; @@ -180,7 +178,7 @@ void EncoderLibJpeg::compressNV(Span frame) } int EncoderLibJpeg::encode(const FrameBuffer &source, Span dest, - Span exifData) + Span exifData, unsigned int quality) { MappedFrameBuffer frame(&source, PROT_READ); if (!frame.isValid()) { @@ -189,15 +187,17 @@ int EncoderLibJpeg::encode(const FrameBuffer &source, Span dest, return frame.error(); } - return encode(frame.maps()[0], dest, exifData); + return encode(frame.maps()[0], dest, exifData, quality); } int EncoderLibJpeg::encode(Span src, Span dest, - Span exifData) + Span exifData, unsigned int quality) { unsigned char *destination = dest.data(); unsigned long size = dest.size(); + jpeg_set_quality(&compress_, quality, TRUE); + /* * The jpeg_mem_dest will reallocate if the required size is not * sufficient. That means the output won't be written to the correct diff --git a/src/android/jpeg/encoder_libjpeg.h b/src/android/jpeg/encoder_libjpeg.h index 070f56f8..838da772 100644 --- a/src/android/jpeg/encoder_libjpeg.h +++ b/src/android/jpeg/encoder_libjpeg.h @@ -23,10 +23,12 @@ public: int configure(const libcamera::StreamConfiguration &cfg) override; int encode(const libcamera::FrameBuffer &source, libcamera::Span destination, - libcamera::Span exifData) override; + libcamera::Span exifData, + unsigned int quality) override; int encode(libcamera::Span source, libcamera::Span destination, - libcamera::Span exifData); + libcamera::Span exifData, + unsigned int quality); private: void compressRGB(libcamera::Span frame); @@ -35,8 +37,6 @@ private: struct jpeg_compress_struct compress_; struct jpeg_error_mgr jerr_; - unsigned int quality_; - const libcamera::PixelFormatInfo *pixelFormatInfo_; bool nv_; diff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp index e5e42d39..8b1801b9 100644 --- a/src/android/jpeg/post_processor_jpeg.cpp +++ b/src/android/jpeg/post_processor_jpeg.cpp @@ -49,6 +49,7 @@ int PostProcessorJpeg::configure(const StreamConfiguration &inCfg, void PostProcessorJpeg::generateThumbnail(const FrameBuffer &source, const Size &targetSize, + unsigned int quality, std::vector *thumbnail) { /* Stores the raw scaled-down thumbnail bytes. */ @@ -69,7 +70,7 @@ void PostProcessorJpeg::generateThumbnail(const FrameBuffer &source, thumbnail->resize(rawThumbnail.size()); int jpeg_size = thumbnailEncoder_.encode(rawThumbnail, - *thumbnail, {}); + *thumbnail, {}, quality); thumbnail->resize(jpeg_size); LOG(JPEG, Debug) @@ -131,19 +132,20 @@ int PostProcessorJpeg::process(const FrameBuffer &source, Size thumbnailSize = { static_cast(data[0]), static_cast(data[1]) }; + ret = requestMetadata.getEntry(ANDROID_JPEG_THUMBNAIL_QUALITY, &entry); + if (ret) + resultMetadata->addEntry(ANDROID_JPEG_THUMBNAIL_QUALITY, entry.data.u8, 1); + if (thumbnailSize != Size(0, 0)) { std::vector thumbnail; - generateThumbnail(source, thumbnailSize, &thumbnail); + generateThumbnail(source, thumbnailSize, + ret ? *entry.data.u8 : 95, &thumbnail); if (!thumbnail.empty()) 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); @@ -164,7 +166,11 @@ int PostProcessorJpeg::process(const FrameBuffer &source, if (exif.generate() != 0) LOG(JPEG, Error) << "Failed to generate valid EXIF data"; - int jpeg_size = encoder_->encode(source, destination, exif.data()); + 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); + + int jpeg_size = encoder_->encode(source, destination, exif.data(), jpeg_quality); if (jpeg_size < 0) { LOG(JPEG, Error) << "Failed to encode stream image"; return jpeg_size; @@ -192,10 +198,5 @@ int PostProcessorJpeg::process(const FrameBuffer &source, /* Update the JPEG result Metadata. */ resultMetadata->addEntry(ANDROID_JPEG_SIZE, &jpeg_size, 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 660b79b4..d2dfa450 100644 --- a/src/android/jpeg/post_processor_jpeg.h +++ b/src/android/jpeg/post_processor_jpeg.h @@ -32,6 +32,7 @@ public: private: void generateThumbnail(const libcamera::FrameBuffer &source, const libcamera::Size &targetSize, + unsigned int quality, std::vector *thumbnail); CameraDevice *const cameraDevice_;