summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorUmang Jain <email@uajain.com>2020-10-28 02:54:47 +0530
committerKieran Bingham <kieran.bingham@ideasonboard.com>2020-10-28 15:52:51 +0000
commitb053384ffab467ed016019bbcf8121a6f9e48fed (patch)
treea2e1910dcdf2bd9fd6d24c21325971842d38916d /src
parentf0421988dc7d91fbd221e2282f839cb51e1a3f8c (diff)
android: jpeg: post_processor_jpeg: Embed thumbnail into Exif metadata
Embed a Jpeg-encoded thumbnail into Exif metadata using the Thumbnailer class that got introduced. Introduce a helper function in Exif class for setting the thumbnail data. Signed-off-by: Umang Jain <email@uajain.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> [Kieran: Add todo comment, and Compression enum] Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Diffstat (limited to 'src')
-rw-r--r--src/android/jpeg/exif.cpp24
-rw-r--r--src/android/jpeg/exif.h7
-rw-r--r--src/android/jpeg/post_processor_jpeg.cpp40
-rw-r--r--src/android/jpeg/post_processor_jpeg.h8
4 files changed, 77 insertions, 2 deletions
diff --git a/src/android/jpeg/exif.cpp b/src/android/jpeg/exif.cpp
index d21534a4..33b3fa7f 100644
--- a/src/android/jpeg/exif.cpp
+++ b/src/android/jpeg/exif.cpp
@@ -75,8 +75,16 @@ Exif::~Exif()
if (exifData_)
free(exifData_);
- if (data_)
+ if (data_) {
+ /*
+ * Reset thumbnail data to avoid getting double-freed by
+ * libexif. It is owned by the caller (i.e. PostProcessorJpeg).
+ */
+ data_->data = nullptr;
+ data_->size = 0;
+
exif_data_unref(data_);
+ }
if (mem_)
exif_mem_unref(mem_);
@@ -268,6 +276,20 @@ void Exif::setOrientation(int orientation)
setShort(EXIF_IFD_0, EXIF_TAG_ORIENTATION, value);
}
+/*
+ * The thumbnail data should remain valid until the Exif object is destroyed.
+ * Failing to do so, might result in no thumbnail data being set even after a
+ * call to Exif::setThumbnail().
+ */
+void Exif::setThumbnail(Span<const unsigned char> thumbnail,
+ Compression compression)
+{
+ data_->data = const_cast<unsigned char *>(thumbnail.data());
+ data_->size = thumbnail.size();
+
+ setShort(EXIF_IFD_0, EXIF_TAG_COMPRESSION, compression);
+}
+
[[nodiscard]] int Exif::generate()
{
if (exifData_) {
diff --git a/src/android/jpeg/exif.h b/src/android/jpeg/exif.h
index 12c27b6f..5cab4559 100644
--- a/src/android/jpeg/exif.h
+++ b/src/android/jpeg/exif.h
@@ -21,11 +21,18 @@ public:
Exif();
~Exif();
+ enum Compression {
+ None = 1,
+ JPEG = 6,
+ };
+
void setMake(const std::string &make);
void setModel(const std::string &model);
void setOrientation(int orientation);
void setSize(const libcamera::Size &size);
+ void setThumbnail(libcamera::Span<const unsigned char> thumbnail,
+ Compression compression);
void setTimestamp(time_t timestamp);
libcamera::Span<const uint8_t> data() const { return { exifData_, size_ }; }
diff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp
index 93acfe52..b9b9dc00 100644
--- a/src/android/jpeg/post_processor_jpeg.cpp
+++ b/src/android/jpeg/post_processor_jpeg.cpp
@@ -39,11 +39,45 @@ 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<EncoderLibJpeg>();
return encoder_->configure(inCfg);
}
+void PostProcessorJpeg::generateThumbnail(const FrameBuffer &source,
+ std::vector<unsigned char> *thumbnail)
+{
+ /* Stores the raw scaled-down thumbnail bytes. */
+ std::vector<unsigned char> rawThumbnail;
+
+ thumbnailer_.createThumbnail(source, &rawThumbnail);
+
+ if (!rawThumbnail.empty()) {
+ /*
+ * \todo Avoid value-initialization of all elements of the
+ * vector.
+ */
+ thumbnail->resize(rawThumbnail.size());
+
+ int jpeg_size = thumbnailEncoder_.encode(rawThumbnail,
+ *thumbnail, {});
+ thumbnail->resize(jpeg_size);
+
+ LOG(JPEG, Debug)
+ << "Thumbnail compress returned "
+ << jpeg_size << " bytes";
+ }
+}
+
int PostProcessorJpeg::process(const FrameBuffer &source,
Span<uint8_t> destination,
CameraMetadata *metadata)
@@ -64,6 +98,12 @@ int PostProcessorJpeg::process(const FrameBuffer &source,
* second, it is good enough.
*/
exif.setTimestamp(std::time(nullptr));
+
+ std::vector<unsigned char> thumbnail;
+ generateThumbnail(source, &thumbnail);
+ if (!thumbnail.empty())
+ exif.setThumbnail(thumbnail, Exif::Compression::JPEG);
+
if (exif.generate() != 0)
LOG(JPEG, Error) << "Failed to generate valid EXIF data";
diff --git a/src/android/jpeg/post_processor_jpeg.h b/src/android/jpeg/post_processor_jpeg.h
index 3706cec2..5afa831c 100644
--- a/src/android/jpeg/post_processor_jpeg.h
+++ b/src/android/jpeg/post_processor_jpeg.h
@@ -8,12 +8,13 @@
#define __ANDROID_POST_PROCESSOR_JPEG_H__
#include "../post_processor.h"
+#include "encoder_libjpeg.h"
+#include "thumbnailer.h"
#include <libcamera/geometry.h>
#include "libcamera/internal/buffer.h"
-class Encoder;
class CameraDevice;
class PostProcessorJpeg : public PostProcessor
@@ -28,9 +29,14 @@ public:
CameraMetadata *metadata) override;
private:
+ void generateThumbnail(const libcamera::FrameBuffer &source,
+ std::vector<unsigned char> *thumbnail);
+
CameraDevice *const cameraDevice_;
std::unique_ptr<Encoder> encoder_;
libcamera::Size streamSize_;
+ EncoderLibJpeg thumbnailEncoder_;
+ Thumbnailer thumbnailer_;
};
#endif /* __ANDROID_POST_PROCESSOR_JPEG_H__ */