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>
This commit is contained in:
Umang Jain 2020-10-28 02:54:47 +05:30 committed by Kieran Bingham
parent f0421988dc
commit b053384ffa
4 changed files with 77 additions and 2 deletions

View file

@ -75,8 +75,16 @@ Exif::~Exif()
if (exifData_) if (exifData_)
free(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_); exif_data_unref(data_);
}
if (mem_) if (mem_)
exif_mem_unref(mem_); exif_mem_unref(mem_);
@ -268,6 +276,20 @@ void Exif::setOrientation(int orientation)
setShort(EXIF_IFD_0, EXIF_TAG_ORIENTATION, value); 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() [[nodiscard]] int Exif::generate()
{ {
if (exifData_) { if (exifData_) {

View file

@ -21,11 +21,18 @@ public:
Exif(); Exif();
~Exif(); ~Exif();
enum Compression {
None = 1,
JPEG = 6,
};
void setMake(const std::string &make); void setMake(const std::string &make);
void setModel(const std::string &model); void setModel(const std::string &model);
void setOrientation(int orientation); void setOrientation(int orientation);
void setSize(const libcamera::Size &size); void setSize(const libcamera::Size &size);
void setThumbnail(libcamera::Span<const unsigned char> thumbnail,
Compression compression);
void setTimestamp(time_t timestamp); void setTimestamp(time_t timestamp);
libcamera::Span<const uint8_t> data() const { return { exifData_, size_ }; } libcamera::Span<const uint8_t> data() const { return { exifData_, size_ }; }

View file

@ -39,11 +39,45 @@ int PostProcessorJpeg::configure(const StreamConfiguration &inCfg,
} }
streamSize_ = outCfg.size; 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>(); encoder_ = std::make_unique<EncoderLibJpeg>();
return encoder_->configure(inCfg); 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, int PostProcessorJpeg::process(const FrameBuffer &source,
Span<uint8_t> destination, Span<uint8_t> destination,
CameraMetadata *metadata) CameraMetadata *metadata)
@ -64,6 +98,12 @@ int PostProcessorJpeg::process(const FrameBuffer &source,
* second, it is good enough. * second, it is good enough.
*/ */
exif.setTimestamp(std::time(nullptr)); 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) if (exif.generate() != 0)
LOG(JPEG, Error) << "Failed to generate valid EXIF data"; LOG(JPEG, Error) << "Failed to generate valid EXIF data";

View file

@ -8,12 +8,13 @@
#define __ANDROID_POST_PROCESSOR_JPEG_H__ #define __ANDROID_POST_PROCESSOR_JPEG_H__
#include "../post_processor.h" #include "../post_processor.h"
#include "encoder_libjpeg.h"
#include "thumbnailer.h"
#include <libcamera/geometry.h> #include <libcamera/geometry.h>
#include "libcamera/internal/buffer.h" #include "libcamera/internal/buffer.h"
class Encoder;
class CameraDevice; class CameraDevice;
class PostProcessorJpeg : public PostProcessor class PostProcessorJpeg : public PostProcessor
@ -28,9 +29,14 @@ public:
CameraMetadata *metadata) override; CameraMetadata *metadata) override;
private: private:
void generateThumbnail(const libcamera::FrameBuffer &source,
std::vector<unsigned char> *thumbnail);
CameraDevice *const cameraDevice_; CameraDevice *const cameraDevice_;
std::unique_ptr<Encoder> encoder_; std::unique_ptr<Encoder> encoder_;
libcamera::Size streamSize_; libcamera::Size streamSize_;
EncoderLibJpeg thumbnailEncoder_;
Thumbnailer thumbnailer_;
}; };
#endif /* __ANDROID_POST_PROCESSOR_JPEG_H__ */ #endif /* __ANDROID_POST_PROCESSOR_JPEG_H__ */