{"id":9383,"url":"https://patchwork.libcamera.org/api/1.1/patches/9383/?format=json","web_url":"https://patchwork.libcamera.org/patch/9383/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20200825201048.13608-1-email@uajain.com>","date":"2020-08-25T20:10:53","name":"[libcamera-devel,v2,2/2] android: jpeg: Support a initial set of EXIF metadata tags","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"babfa12c40b772b1f17cae002350f819b69b480b","submitter":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/people/1/?format=json","name":"Umang Jain","email":"email@uajain.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/9383/mbox/","series":[{"id":1242,"url":"https://patchwork.libcamera.org/api/1.1/series/1242/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=1242","date":"2020-08-25T20:10:31","name":"Initial EXIF metadata support","version":2,"mbox":"https://patchwork.libcamera.org/series/1242/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/9383/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/9383/checks/","tags":{},"headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 5D123BD87E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 25 Aug 2020 20:10:56 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 29CD162882;\n\tTue, 25 Aug 2020 22:10:56 +0200 (CEST)","from o1.f.az.sendgrid.net (o1.f.az.sendgrid.net [208.117.55.132])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 794336037C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 25 Aug 2020 22:10:54 +0200 (CEST)","by filterdrecv-p3iad2-cbd967498-57mkd with SMTP id\n\tfilterdrecv-p3iad2-cbd967498-57mkd-20-5F45704D-A\n\t2020-08-25 20:10:53.258262099 +0000 UTC m=+2436.898476489","from mail.uajain.com (unknown)\n\tby ismtpd0002p1hnd1.sendgrid.net (SG) with ESMTP\n\tid qR2JujhPS86VIwUg2eFLUQ Tue, 25 Aug 2020 20:10:52.756 +0000 (UTC)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=uajain.com header.i=@uajain.com\n\theader.b=\"Wp+ZF9vO\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=uajain.com;\n\th=from:subject:mime-version:to:cc:content-transfer-encoding:\n\tcontent-type;\n\ts=s1; bh=rGAUH7EYnfYdfa2hbFd6w7VGRyGBf9q3ChbWN7Qr+vM=;\n\tb=Wp+ZF9vOAQsppBrjRS6u/a4Jj2bI2lJzQbSrm20KaSDTop4VzLnX2gsbYZrgIEeBvUnL\n\tVHAByih52XFnKPUlkm3xEdr9j8UyX3gYeHPSJ7Vt6bt0bRZirmd4uKOow/mEkGMdaoJny3\n\t8kNy1/cNz6g8ergwG8d15jm2TMJ/rlSW4=","From":"Umang Jain <email@uajain.com>","Date":"Tue, 25 Aug 2020 20:10:53 +0000 (UTC)","Message-Id":"<20200825201048.13608-1-email@uajain.com>","Mime-Version":"1.0","X-SG-EID":"1Q40EQ7YGir8a9gjSIAdTjhngY657NMk9ckeo4dbHZDiOpywc/L3L9rFqlwE4KPc4om/Oij3lMEQBxKPi/30Oy8rTdfU1FlVAi+/uEVNk+vYA3+jQRD58kreR/qA6YDT9CdrnbX9bygO6jjBE6KxqLMOl16UaRpzZFudm6N2zSVFEOHYgl2rbu4eNmr2p94Xm4VHDXMz2UdEY2Tt0UZK9eLqaaRH9bHTj7ENl8YtvR6GDbOodboE+HXbkZUjDcgRUAArBamtJyBTheXVMcbMXw==","To":"libcamera-devel@lists.libcamera.org","Subject":"[libcamera-devel] [PATCH v2 2/2] android: jpeg: Support a initial\n\tset of EXIF metadata tags","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"From: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\nAdd a Exif data placeholder inside CameraStream, to populate it\naccordingly to the stream properties (size, orientation etc).\nStatic EXIF properties (such as make, model, size, orientation)\ncan be configured once when the stream is being configured.\nPer-frame related metadata (such as timestamp) needs to set\njust before encoding preferably in CameraDevice::requestComplete().\n\nSigned-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\nSigned-off-by: Umang Jain <email@uajain.com>\n---\n src/android/camera_device.cpp        | 22 +++++++++-\n src/android/camera_device.h          |  2 +\n src/android/jpeg/encoder.h           |  5 ++-\n src/android/jpeg/encoder_libjpeg.cpp |  9 +++-\n src/android/jpeg/encoder_libjpeg.h   |  3 +-\n src/android/jpeg/exif.cpp            | 66 ++++++++++++++++++++++++++++\n src/android/jpeg/exif.h              |  9 ++++\n 7 files changed, 112 insertions(+), 4 deletions(-)","diff":"diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\nindex de6f86f..8789a9e 100644\n--- a/src/android/camera_device.cpp\n+++ b/src/android/camera_device.cpp\n@@ -1245,6 +1245,17 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)\n \t\t\t\t\t<< \"Failed to configure encoder\";\n \t\t\t\treturn ret;\n \t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * Set EXIF metadata for various tags.\n+\t\t\t * \\todo Discuss setMake String, maybe vendor ID?\n+\t\t\t * KB suggested to leave it to \"libcamera\" for now.\n+\t\t\t * setModel should use the 'model' property of the camera.\n+\t\t\t */\n+\t\t\tcameraStream->exif.setMake(\"libcamera\");\n+\t\t\tcameraStream->exif.setModel(\"cameraModel\");\n+\t\t\tcameraStream->exif.setOrientation(orientation_);\n+\t\t\tcameraStream->exif.setSize(cfg.size);\n \t\t}\n \t}\n \n@@ -1434,7 +1445,16 @@ void CameraDevice::requestComplete(Request *request)\n \t\t\tcontinue;\n \t\t}\n \n-\t\tint jpeg_size = encoder->encode(buffer, mapped.maps()[0]);\n+\t\t/*\n+\t\t * We set the frame's EXIF timestamp as the time of encode. Since the\n+\t\t * we need for EXIF is only one second, it is good enough.\n+\t\t */\n+\t\tstruct timespec tp;\n+\t\tclock_gettime(CLOCK_REALTIME, &tp);\n+\t\tcameraStream->exif.setTimestamp(tp.tv_sec);\n+\t\tSpan<uint8_t> exif_data = cameraStream->exif.generate();\n+\n+\t\tint jpeg_size = encoder->encode(buffer, mapped.maps()[0], exif_data);\n \t\tif (jpeg_size < 0) {\n \t\t\tLOG(HAL, Error) << \"Failed to encode stream image\";\n \t\t\tstatus = CAMERA3_BUFFER_STATUS_ERROR;\ndiff --git a/src/android/camera_device.h b/src/android/camera_device.h\nindex 3934f19..3e8a119 100644\n--- a/src/android/camera_device.h\n+++ b/src/android/camera_device.h\n@@ -42,6 +42,8 @@ struct CameraStream {\n \tlibcamera::Size size;\n \n \tEncoder *jpeg;\n+\n+\tExif exif;\n };\n \n class CameraDevice : protected libcamera::Loggable\ndiff --git a/src/android/jpeg/encoder.h b/src/android/jpeg/encoder.h\nindex f9eb88e..969f291 100644\n--- a/src/android/jpeg/encoder.h\n+++ b/src/android/jpeg/encoder.h\n@@ -7,6 +7,8 @@\n #ifndef __ANDROID_JPEG_ENCODER_H__\n #define __ANDROID_JPEG_ENCODER_H__\n \n+#include \"exif.h\"\n+\n #include <libcamera/buffer.h>\n #include <libcamera/span.h>\n #include <libcamera/stream.h>\n@@ -18,7 +20,8 @@ public:\n \n \tvirtual int configure(const libcamera::StreamConfiguration &cfg) = 0;\n \tvirtual int encode(const libcamera::FrameBuffer *source,\n-\t\t\t   const libcamera::Span<uint8_t> &destination) = 0;\n+\t\t\t   const libcamera::Span<uint8_t> &destination,\n+\t\t\t   const libcamera::Span<uint8_t> &exif_data) = 0;\n };\n \n #endif /* __ANDROID_JPEG_ENCODER_H__ */\ndiff --git a/src/android/jpeg/encoder_libjpeg.cpp b/src/android/jpeg/encoder_libjpeg.cpp\nindex 977538c..87464a4 100644\n--- a/src/android/jpeg/encoder_libjpeg.cpp\n+++ b/src/android/jpeg/encoder_libjpeg.cpp\n@@ -180,7 +180,8 @@ void EncoderLibJpeg::compressNV(const libcamera::MappedBuffer *frame)\n }\n \n int EncoderLibJpeg::encode(const FrameBuffer *source,\n-\t\t\t   const libcamera::Span<uint8_t> &dest)\n+\t\t\t   const libcamera::Span<uint8_t> &dest,\n+\t\t\t   const libcamera::Span<uint8_t> &exif_data)\n {\n \tMappedFrameBuffer frame(source, PROT_READ);\n \tif (!frame.isValid()) {\n@@ -214,5 +215,11 @@ int EncoderLibJpeg::encode(const FrameBuffer *source,\n \n \tjpeg_finish_compress(&compress_);\n \n+\tif (exif_data.size())\n+\t\t/* Store Exif data in the JPEG_APP1 data block. */\n+\t\tjpeg_write_marker(&compress_, JPEG_APP0 + 1,\n+\t\t\t\t  static_cast<const JOCTET *>(exif_data.data()),\n+\t\t\t\t  exif_data.size());\n+\n \treturn size;\n }\ndiff --git a/src/android/jpeg/encoder_libjpeg.h b/src/android/jpeg/encoder_libjpeg.h\nindex aed081a..88bc6e0 100644\n--- a/src/android/jpeg/encoder_libjpeg.h\n+++ b/src/android/jpeg/encoder_libjpeg.h\n@@ -22,7 +22,8 @@ public:\n \n \tint configure(const libcamera::StreamConfiguration &cfg) override;\n \tint encode(const libcamera::FrameBuffer *source,\n-\t\t   const libcamera::Span<uint8_t> &destination) override;\n+\t\t   const libcamera::Span<uint8_t> &destination,\n+\t\t   const libcamera::Span<uint8_t> &exif_data) override;\n \n private:\n \tvoid compressRGB(const libcamera::MappedBuffer *frame);\ndiff --git a/src/android/jpeg/exif.cpp b/src/android/jpeg/exif.cpp\nindex f6a9f5c..d2ef14e 100644\n--- a/src/android/jpeg/exif.cpp\n+++ b/src/android/jpeg/exif.cpp\n@@ -160,6 +160,72 @@ int Exif::setString(ExifIfd ifd, ExifTag tag, ExifFormat format, const std::stri\n \treturn 0;\n }\n \n+int Exif::setMake(const std::string &make)\n+{\n+\treturn setString(EXIF_IFD_0, EXIF_TAG_MAKE, EXIF_FORMAT_ASCII, make);\n+}\n+\n+int Exif::setModel(const std::string &model)\n+{\n+\treturn setString(EXIF_IFD_0, EXIF_TAG_MODEL, EXIF_FORMAT_ASCII, model);\n+}\n+\n+int Exif::setSize(Size size)\n+{\n+\tsetShort(EXIF_IFD_0, EXIF_TAG_IMAGE_LENGTH, size.height);\n+\tsetLong(EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION, size.height);\n+\n+\tsetShort(EXIF_IFD_0, EXIF_TAG_IMAGE_WIDTH, size.width);\n+\tsetLong(EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION, size.width);\n+\n+\treturn 0;\n+}\n+\n+int Exif::setTimestamp(const time_t timestamp)\n+{\n+\tchar str[40];\n+\tsize_t len = std::strftime(str, sizeof(str), \"%c %Z\",\n+\t\t\t\t   std::localtime(&timestamp));\n+\tstd::string ts(str);\n+\tint ret = -1;\n+\n+\tif (len > 0) {\n+\t\tret = setString(EXIF_IFD_0, EXIF_TAG_DATE_TIME, EXIF_FORMAT_ASCII, ts);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\n+\t\tret = setString(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_ORIGINAL, EXIF_FORMAT_ASCII, ts);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\n+\t\tret = setString(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_DIGITIZED, EXIF_FORMAT_ASCII, ts);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+\n+int Exif::setOrientation(int orientation)\n+{\n+\t/* Orientation's tag value computed similar to Chrome HAL. */\n+\tint value = 1;\n+\tswitch (orientation) {\n+\tcase 90:\n+\t\tvalue = 6;\n+\t\tbreak;\n+\tcase 180:\n+\t\tvalue = 3;\n+\t\tbreak;\n+\tcase 270:\n+\t\tvalue = 8;\n+\t\tbreak;\n+\t}\n+\n+\treturn setShort(EXIF_IFD_0, EXIF_TAG_ORIENTATION, value);\n+}\n+\n Span<uint8_t> Exif::generate()\n {\n \tif (exif_data_) {\ndiff --git a/src/android/jpeg/exif.h b/src/android/jpeg/exif.h\nindex 7df83c7..6cca578 100644\n--- a/src/android/jpeg/exif.h\n+++ b/src/android/jpeg/exif.h\n@@ -9,8 +9,10 @@\n \n #include <libexif/exif-data.h>\n \n+#include <libcamera/geometry.h>\n #include <libcamera/span.h>\n \n+#include <ctime>\n #include <string>\n \n class Exif\n@@ -24,6 +26,13 @@ public:\n \tint setString(ExifIfd ifd, ExifTag tag, ExifFormat format, const std::string &item);\n \tint setRational(ExifIfd ifd, ExifTag tag, uint32_t numerator, uint32_t denominator);\n \n+\tint setMake(const std::string &make);\n+\tint setModel(const std::string &model);\n+\n+\tint setOrientation(int orientation);\n+\tint setSize(libcamera::Size size);\n+\tint setTimestamp(const time_t timestamp);\n+\n \tlibcamera::Span<uint8_t> generate();\n \tunsigned char *data() const { return exif_data_; }\n \tunsigned int size() const { return size_; }\n","prefixes":["libcamera-devel","v2","2/2"]}