Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c0aff13c authored by Vadim Caen's avatar Vadim Caen
Browse files

Generate correct Exif data from capture metadata

The GPS metadata are supposed to by piped from request to result and
included in the jpeg Exif.

Additionnaly, flash, make and model are added in the exif metadata with
some default value since they are required.

Test: atest 'android.hardware.camera2.cts.StillCaptureTest#testJpegExif[1]'
Fixes: 324383963
Change-Id: Iaf161a2d9816115a0daba19dc2d9a911bc879330
parent bd683bef
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -122,6 +122,12 @@ class VirtualCameraDevice
  // Default JPEG compression quality.
  static constexpr uint8_t kDefaultJpegQuality = 80;

  // Default JPEG orientation.
  static constexpr uint8_t kDefaultJpegOrientation = 0;

  // Default Make and Model for Exif
  static constexpr char kDefaultMakeAndModel[] = "Android Virtual Camera";

  static constexpr camera_metadata_enum_android_control_capture_intent_t
      kDefaultCaptureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;

+27 −8
Original line number Diff line number Diff line
@@ -14,7 +14,6 @@
 * limitations under the License.
 */

#include "system/camera_metadata.h"
#define LOG_TAG "VirtualCameraRenderThread"
#include "VirtualCameraRenderThread.h"

@@ -45,6 +44,7 @@
#include "android-base/thread_annotations.h"
#include "android/binder_auto_utils.h"
#include "android/hardware_buffer.h"
#include "system/camera_metadata.h"
#include "ui/GraphicBuffer.h"
#include "util/EglFramebuffer.h"
#include "util/JpegUtil.h"
@@ -128,6 +128,7 @@ CameraMetadata createCaptureResultMetadata(
          .setFlashMode(ANDROID_FLASH_MODE_OFF)
          .setFocalLength(VirtualCameraDevice::kFocalLength)
          .setJpegQuality(requestSettings.jpegQuality)
          .setJpegOrientation(requestSettings.jpegOrientation)
          .setJpegThumbnailSize(requestSettings.thumbnailResolution.width,
                                requestSettings.thumbnailResolution.height)
          .setJpegThumbnailQuality(requestSettings.thumbnailJpegQuality)
@@ -146,6 +147,11 @@ CameraMetadata createCaptureResultMetadata(
    builder.setControlAeTargetFpsRange(requestSettings.fpsRange.value());
  }

  if (requestSettings.gpsCoordinates.has_value()) {
    const GpsCoordinates& coordinates = requestSettings.gpsCoordinates.value();
    builder.setJpegGpsCoordinates(coordinates);
  }

  std::unique_ptr<CameraMetadata> metadata = builder.build();

  if (metadata == nullptr) {
@@ -224,12 +230,24 @@ bool isYuvFormat(const PixelFormat pixelFormat) {
}

std::vector<uint8_t> createExif(
    Resolution imageSize, const std::vector<uint8_t>& compressedThumbnail = {}) {
    Resolution imageSize, const CameraMetadata resultMetadata,
    const std::vector<uint8_t>& compressedThumbnail = {}) {
  std::unique_ptr<ExifUtils> exifUtils(ExifUtils::create());
  exifUtils->initialize();
  exifUtils->setImageWidth(imageSize.width);
  exifUtils->setImageHeight(imageSize.height);
  // TODO(b/324383963) Set Make/Model and orientation.

  // Make a copy of the metadata in order to converting it the HAL metadata
  // format (as opposed to the AIDL class) and use the setFromMetadata method
  // from ExifUtil
  camera_metadata_t* rawSettings =
      clone_camera_metadata((camera_metadata_t*)resultMetadata.metadata.data());
  if (rawSettings != nullptr) {
    android::hardware::camera::common::helper::CameraMetadata halMetadata(
        rawSettings);
    exifUtils->setFromMetadata(halMetadata, imageSize.width, imageSize.height);
  }
  exifUtils->setMake(VirtualCameraDevice::kDefaultMakeAndModel);
  exifUtils->setModel(VirtualCameraDevice::kDefaultMakeAndModel);
  exifUtils->setFlash(0);

  std::vector<uint8_t> app1Data;

@@ -427,7 +445,8 @@ void VirtualCameraRenderThread::processCaptureRequest(
    auto status = streamConfig->format == PixelFormat::BLOB
                      ? renderIntoBlobStreamBuffer(
                            reqBuffer.getStreamId(), reqBuffer.getBufferId(),
                            request.getRequestSettings(), reqBuffer.getFence())
                            captureResult.result, request.getRequestSettings(),
                            reqBuffer.getFence())
                      : renderIntoImageStreamBuffer(reqBuffer.getStreamId(),
                                                    reqBuffer.getBufferId(),
                                                    reqBuffer.getFence());
@@ -569,7 +588,7 @@ std::vector<uint8_t> VirtualCameraRenderThread::createThumbnail(
}

ndk::ScopedAStatus VirtualCameraRenderThread::renderIntoBlobStreamBuffer(
    const int streamId, const int bufferId,
    const int streamId, const int bufferId, const CameraMetadata& resultMetadata,
    const RequestSettings& requestSettings, sp<Fence> fence) {
  std::shared_ptr<AHardwareBuffer> hwBuffer =
      mSessionContext.fetchHardwareBuffer(streamId, bufferId);
@@ -635,7 +654,7 @@ ndk::ScopedAStatus VirtualCameraRenderThread::renderIntoBlobStreamBuffer(
  }

  std::vector<uint8_t> app1ExifData =
      createExif(Resolution(stream->width, stream->height),
      createExif(Resolution(stream->width, stream->height), resultMetadata,
                 createThumbnail(requestSettings.thumbnailResolution,
                                 requestSettings.thumbnailJpegQuality));
  std::optional<size_t> compressedSize = compressJpeg(
+5 −1
Original line number Diff line number Diff line
@@ -57,11 +57,13 @@ class CaptureRequestBuffer {

struct RequestSettings {
  int jpegQuality = VirtualCameraDevice::kDefaultJpegQuality;
  int jpegOrientation = VirtualCameraDevice::kDefaultJpegOrientation;
  Resolution thumbnailResolution = Resolution(0, 0);
  int thumbnailJpegQuality = VirtualCameraDevice::kDefaultJpegQuality;
  std::optional<FpsRange> fpsRange = {};
  std::optional<FpsRange> fpsRange;
  camera_metadata_enum_android_control_capture_intent_t captureIntent =
      VirtualCameraDevice::kDefaultCaptureIntent;
  std::optional<GpsCoordinates> gpsCoordinates;
};

// Represents single capture request to fill set of buffers.
@@ -153,6 +155,8 @@ class VirtualCameraRenderThread {
  // Always called on render thread.
  ndk::ScopedAStatus renderIntoBlobStreamBuffer(
      const int streamId, const int bufferId,
      const ::aidl::android::hardware::camera::device::CameraMetadata&
          resultMetadata,
      const RequestSettings& requestSettings, sp<Fence> fence = nullptr);

  // Render current image to the YCbCr buffer.
+3 −1
Original line number Diff line number Diff line
@@ -257,13 +257,15 @@ RequestSettings createSettingsFromMetadata(const CameraMetadata& metadata) {
  return RequestSettings{
      .jpegQuality = getJpegQuality(metadata).value_or(
          VirtualCameraDevice::kDefaultJpegQuality),
      .jpegOrientation = getJpegOrientation(metadata),
      .thumbnailResolution =
          getJpegThumbnailSize(metadata).value_or(Resolution(0, 0)),
      .thumbnailJpegQuality = getJpegThumbnailQuality(metadata).value_or(
          VirtualCameraDevice::kDefaultJpegQuality),
      .fpsRange = getFpsRange(metadata),
      .captureIntent = getCaptureIntent(metadata).value_or(
          ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW)};
          ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW),
      .gpsCoordinates = getGpsCoordinates(metadata)};
}

}  // namespace
+71 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <iterator>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <variant>
#include <vector>
@@ -435,6 +436,29 @@ MetadataBuilder& MetadataBuilder::setJpegAvailableThumbnailSizes(
  return *this;
}

MetadataBuilder& MetadataBuilder::setJpegGpsCoordinates(
    const GpsCoordinates& gpsCoordinates) {
  mEntryMap[ANDROID_JPEG_GPS_COORDINATES] =
      std::vector<double>({gpsCoordinates.latitude, gpsCoordinates.longitude,
                           gpsCoordinates.altitude});

  if (!gpsCoordinates.provider.empty()) {
    mEntryMap[ANDROID_JPEG_GPS_PROCESSING_METHOD] = std::vector<uint8_t>{
        gpsCoordinates.provider.begin(), gpsCoordinates.provider.end()};
  }

  if (gpsCoordinates.timestamp.has_value()) {
    mEntryMap[ANDROID_JPEG_GPS_TIMESTAMP] =
        asVectorOf<int64_t>(gpsCoordinates.timestamp.value());
  }
  return *this;
}

MetadataBuilder& MetadataBuilder::setJpegOrientation(const int32_t orientation) {
  mEntryMap[ANDROID_JPEG_ORIENTATION] = asVectorOf<int32_t>(orientation);
  return *this;
}

MetadataBuilder& MetadataBuilder::setJpegQuality(const uint8_t quality) {
  mEntryMap[ANDROID_JPEG_QUALITY] = asVectorOf<uint8_t>(quality);
  return *this;
@@ -753,6 +777,20 @@ std::optional<int32_t> getJpegQuality(
  return *entry.data.i32;
}

int32_t getJpegOrientation(
    const aidl::android::hardware::camera::device::CameraMetadata& cameraMetadata) {
  auto metadata =
      reinterpret_cast<const camera_metadata_t*>(cameraMetadata.metadata.data());

  camera_metadata_ro_entry_t entry;
  if (find_camera_metadata_ro_entry(metadata, ANDROID_JPEG_ORIENTATION,
                                    &entry) != OK) {
    return 0;
  }

  return *entry.data.i32;
}

std::optional<Resolution> getJpegThumbnailSize(
    const aidl::android::hardware::camera::device::CameraMetadata& cameraMetadata) {
  auto metadata =
@@ -832,6 +870,39 @@ getCaptureIntent(const aidl::android::hardware::camera::device::CameraMetadata&
      entry.data.u8[0]);
}

std::optional<GpsCoordinates> getGpsCoordinates(
    const aidl::android::hardware::camera::device::CameraMetadata& cameraMetadata) {
  auto metadata =
      reinterpret_cast<const camera_metadata_t*>(cameraMetadata.metadata.data());

  camera_metadata_ro_entry_t entry;
  if (find_camera_metadata_ro_entry(metadata, ANDROID_JPEG_GPS_COORDINATES,
                                    &entry) != OK) {
    return std::nullopt;
  }

  GpsCoordinates coordinates{.latitude = entry.data.d[0],
                             .longitude = entry.data.d[1],
                             .altitude = entry.data.d[2]};

  if (find_camera_metadata_ro_entry(metadata, ANDROID_JPEG_GPS_TIMESTAMP,
                                    &entry) == OK) {
    coordinates.timestamp = entry.data.i64[0];
  }

  // According to types.hal, the string describing the GPS processing method has
  // a 32 characters size
  static constexpr float kGpsProviderStringLength = 32;
  if (find_camera_metadata_ro_entry(
          metadata, ANDROID_JPEG_GPS_PROCESSING_METHOD, &entry) == OK) {
    coordinates.provider.assign(
        reinterpret_cast<const char*>(entry.data.u8),
        std::min(entry.count, static_cast<size_t>(kGpsProviderStringLength)));
  }

  return coordinates;
}

}  // namespace virtualcamera
}  // namespace companion
}  // namespace android
Loading