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:
parent
c3d1329b93
commit
6f09a619cc
6 changed files with 88 additions and 4 deletions
|
@ -24,6 +24,7 @@
|
|||
#include "system/graphics.h"
|
||||
|
||||
#include "jpeg/encoder_libjpeg.h"
|
||||
#include "jpeg/exif.h"
|
||||
|
||||
using namespace libcamera;
|
||||
|
||||
|
@ -1439,7 +1440,23 @@ void CameraDevice::requestComplete(Request *request)
|
|||
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) {
|
||||
LOG(HAL, Error) << "Failed to encode stream image";
|
||||
status = CAMERA3_BUFFER_STATUS_ERROR;
|
||||
|
|
|
@ -18,7 +18,8 @@ public:
|
|||
|
||||
virtual int configure(const libcamera::StreamConfiguration &cfg) = 0;
|
||||
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__ */
|
||||
|
|
|
@ -180,7 +180,8 @@ void EncoderLibJpeg::compressNV(const libcamera::MappedBuffer *frame)
|
|||
}
|
||||
|
||||
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);
|
||||
if (!frame.isValid()) {
|
||||
|
@ -204,6 +205,12 @@ int EncoderLibJpeg::encode(const FrameBuffer *source,
|
|||
|
||||
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
|
||||
<< "x" << compress_.image_height;
|
||||
|
||||
|
|
|
@ -22,7 +22,8 @@ public:
|
|||
|
||||
int configure(const libcamera::StreamConfiguration &cfg) override;
|
||||
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:
|
||||
void compressRGB(const libcamera::MappedBuffer *frame);
|
||||
|
|
|
@ -168,6 +168,56 @@ void Exif::setString(ExifIfd ifd, ExifTag tag, ExifFormat format, const std::str
|
|||
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(×tamp));
|
||||
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()
|
||||
{
|
||||
if (exifData_) {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <libexif/exif-data.h>
|
||||
|
||||
#include <libcamera/geometry.h>
|
||||
#include <libcamera/span.h>
|
||||
|
||||
class Exif
|
||||
|
@ -20,6 +21,13 @@ public:
|
|||
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_ }; }
|
||||
[[nodiscard]] int generate();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue