android: jpeg: Support an initial set of EXIF metadata tags

Create a Exif object with various metadata tags set, just before
the encoder starts to encode the frame. The object is passed
directly as libcamera::Span<> to make sure EXIF tags can be set
in a single place i.e. in CameraDevice and the encoder only has
the job to write the data in the final output.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Umang Jain <email@uajain.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
Umang Jain 2020-09-09 16:44:45 +05:30 committed by Laurent Pinchart
parent c3d1329b93
commit 6f09a619cc
6 changed files with 88 additions and 4 deletions

View file

@ -24,6 +24,7 @@
#include "system/graphics.h" #include "system/graphics.h"
#include "jpeg/encoder_libjpeg.h" #include "jpeg/encoder_libjpeg.h"
#include "jpeg/exif.h"
using namespace libcamera; using namespace libcamera;
@ -1439,7 +1440,23 @@ void CameraDevice::requestComplete(Request *request)
continue; continue;
} }
int jpeg_size = encoder->encode(buffer, mapped.maps()[0]); /* 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(orientation_);
exif.setSize(cameraStream->size);
/*
* 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));
if (exif.generate() != 0)
LOG(HAL, Error) << "Failed to generate valid EXIF data";
int jpeg_size = encoder->encode(buffer, mapped.maps()[0], exif.data());
if (jpeg_size < 0) { if (jpeg_size < 0) {
LOG(HAL, Error) << "Failed to encode stream image"; LOG(HAL, Error) << "Failed to encode stream image";
status = CAMERA3_BUFFER_STATUS_ERROR; status = CAMERA3_BUFFER_STATUS_ERROR;

View file

@ -18,7 +18,8 @@ public:
virtual int configure(const libcamera::StreamConfiguration &cfg) = 0; virtual int configure(const libcamera::StreamConfiguration &cfg) = 0;
virtual int encode(const libcamera::FrameBuffer *source, virtual int encode(const libcamera::FrameBuffer *source,
const libcamera::Span<uint8_t> &destination) = 0; const libcamera::Span<uint8_t> &destination,
const libcamera::Span<const uint8_t> &exifData) = 0;
}; };
#endif /* __ANDROID_JPEG_ENCODER_H__ */ #endif /* __ANDROID_JPEG_ENCODER_H__ */

View file

@ -180,7 +180,8 @@ void EncoderLibJpeg::compressNV(const libcamera::MappedBuffer *frame)
} }
int EncoderLibJpeg::encode(const FrameBuffer *source, int EncoderLibJpeg::encode(const FrameBuffer *source,
const libcamera::Span<uint8_t> &dest) const libcamera::Span<uint8_t> &dest,
const libcamera::Span<const uint8_t> &exifData)
{ {
MappedFrameBuffer frame(source, PROT_READ); MappedFrameBuffer frame(source, PROT_READ);
if (!frame.isValid()) { if (!frame.isValid()) {
@ -204,6 +205,12 @@ int EncoderLibJpeg::encode(const FrameBuffer *source,
jpeg_start_compress(&compress_, TRUE); jpeg_start_compress(&compress_, TRUE);
if (exifData.size())
/* Store Exif data in the JPEG_APP1 data block. */
jpeg_write_marker(&compress_, JPEG_APP0 + 1,
static_cast<const JOCTET *>(exifData.data()),
exifData.size());
LOG(JPEG, Debug) << "JPEG Encode Starting:" << compress_.image_width LOG(JPEG, Debug) << "JPEG Encode Starting:" << compress_.image_width
<< "x" << compress_.image_height; << "x" << compress_.image_height;

View file

@ -22,7 +22,8 @@ public:
int configure(const libcamera::StreamConfiguration &cfg) override; int configure(const libcamera::StreamConfiguration &cfg) override;
int encode(const libcamera::FrameBuffer *source, int encode(const libcamera::FrameBuffer *source,
const libcamera::Span<uint8_t> &destination) override; const libcamera::Span<uint8_t> &destination,
const libcamera::Span<const uint8_t> &exifData) override;
private: private:
void compressRGB(const libcamera::MappedBuffer *frame); void compressRGB(const libcamera::MappedBuffer *frame);

View file

@ -168,6 +168,56 @@ void Exif::setString(ExifIfd ifd, ExifTag tag, ExifFormat format, const std::str
exif_entry_unref(entry); exif_entry_unref(entry);
} }
void Exif::setMake(const std::string &make)
{
setString(EXIF_IFD_0, EXIF_TAG_MAKE, EXIF_FORMAT_ASCII, make);
}
void Exif::setModel(const std::string &model)
{
setString(EXIF_IFD_0, EXIF_TAG_MODEL, EXIF_FORMAT_ASCII, model);
}
void Exif::setSize(const Size &size)
{
setLong(EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION, size.height);
setLong(EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION, size.width);
}
void Exif::setTimestamp(time_t timestamp)
{
char str[20];
std::strftime(str, sizeof(str), "%Y:%m:%d %H:%M:%S",
std::localtime(&timestamp));
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);
}
void Exif::setOrientation(int orientation)
{
int value;
switch (orientation) {
case 0:
default:
value = 1;
break;
case 90:
value = 8;
break;
case 180:
value = 3;
break;
case 270:
value = 6;
break;
}
setShort(EXIF_IFD_0, EXIF_TAG_ORIENTATION, value);
}
[[nodiscard]] int Exif::generate() [[nodiscard]] int Exif::generate()
{ {
if (exifData_) { if (exifData_) {

View file

@ -12,6 +12,7 @@
#include <libexif/exif-data.h> #include <libexif/exif-data.h>
#include <libcamera/geometry.h>
#include <libcamera/span.h> #include <libcamera/span.h>
class Exif class Exif
@ -20,6 +21,13 @@ public:
Exif(); Exif();
~Exif(); ~Exif();
void setMake(const std::string &make);
void setModel(const std::string &model);
void setOrientation(int orientation);
void setSize(const libcamera::Size &size);
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_ }; }
[[nodiscard]] int generate(); [[nodiscard]] int generate();