blob: 58d3d639f58c56133c5aa3f154b3215452ade5db [file] [log] [blame]
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "camera3_test/camera3_still_capture_fixture.h"
#include <algorithm>
namespace camera3_test {
void Camera3StillCaptureFixture::SetUp() {
ASSERT_EQ(
0, cam_service_.Initialize(
base::Bind(&Camera3StillCaptureFixture::ProcessStillCaptureResult,
base::Unretained(this)),
Camera3Service::ProcessRecordingResultCallback()))
<< "Failed to initialize camera service";
for (const auto& it : cam_ids_) {
jpeg_max_sizes_[it] = cam_service_.GetStaticInfo(it)->GetJpegMaxSize();
}
}
void Camera3StillCaptureFixture::ProcessStillCaptureResult(
int cam_id,
uint32_t frame_number,
CameraMetadataUniquePtr metadata,
BufferHandleUniquePtr buffer) {
VLOGF_ENTER();
StillCaptureResult* result = &still_capture_results_[cam_id];
result->result_metadatas.emplace_back(std::move(metadata));
result->buffer_handles.emplace_back(std::move(buffer));
time_t cur_time;
time(&cur_time);
result->result_date_time.push_back(cur_time);
sem_post(&result->capture_result_sem);
}
int Camera3StillCaptureFixture::WaitStillCaptureResult(
int cam_id,
const struct timespec& timeout) {
// Wait for capture result callback
return sem_timedwait(&still_capture_results_[cam_id].capture_result_sem,
&timeout);
}
Camera3StillCaptureFixture::StillCaptureResult::StillCaptureResult() {
sem_init(&capture_result_sem, 0, 0);
}
Camera3StillCaptureFixture::StillCaptureResult::~StillCaptureResult() {
sem_destroy(&capture_result_sem);
}
static int32_t GetJpegInfo(uint8_t* data,
size_t size,
ResolutionInfo* resolution,
ExifData** exif_data) {
#define JPEG_APP1 (JPEG_APP0 + 1)
const unsigned int kMaxMarkerLength = 0xffff;
struct jpeg_decompress_struct jpeg_info;
struct jpeg_error_mgr jpeg_error;
jpeg_info.err = jpeg_std_error(&jpeg_error);
jpeg_create_decompress(&jpeg_info);
jpeg_mem_src(&jpeg_info, data, size);
jpeg_save_markers(&jpeg_info, JPEG_APP1, kMaxMarkerLength);
if (jpeg_read_header(&jpeg_info, TRUE) != JPEG_HEADER_OK) {
jpeg_destroy_decompress(&jpeg_info);
return -EINVAL;
}
if (resolution) {
*resolution = ResolutionInfo(jpeg_info.image_width, jpeg_info.image_height);
}
if (exif_data) {
for (auto marker = jpeg_info.marker_list; marker != nullptr;
marker = marker->next) {
if (marker->marker == JPEG_APP1) {
*exif_data = exif_data_new_from_data(marker->data, marker->data_length);
break;
}
}
}
jpeg_destroy_decompress(&jpeg_info);
return 0;
}
Camera3StillCaptureFixture::JpegExifInfo::JpegExifInfo(
const BufferHandleUniquePtr& buffer,
size_t size)
: buffer_handle(buffer),
buffer_size(size),
buffer_addr(nullptr),
jpeg_resolution(ResolutionInfo(0, 0)),
exif_data(nullptr) {
// Get JPEG image address and size
if (Camera3TestGralloc::GetInstance()->Lock(*buffer_handle, 0, 0, 0, size, 1,
&buffer_addr) != 0 ||
!buffer_addr) {
ADD_FAILURE() << "Failed to map buffer";
}
}
Camera3StillCaptureFixture::JpegExifInfo::~JpegExifInfo() {
if (exif_data) {
exif_data_unref(exif_data);
}
Camera3TestGralloc::GetInstance()->Unlock(*buffer_handle);
}
bool Camera3StillCaptureFixture::JpegExifInfo::Initialize() {
if (!buffer_addr) {
return false;
}
auto jpeg_blob = reinterpret_cast<camera3_jpeg_blob_t*>(
static_cast<uint8_t*>(buffer_addr) + buffer_size -
sizeof(camera3_jpeg_blob_t));
if (static_cast<void*>(jpeg_blob) < buffer_addr ||
jpeg_blob->jpeg_blob_id != CAMERA3_JPEG_BLOB_ID) {
ADD_FAILURE() << "Invalid JPEG BLOB ID";
return false;
}
if (GetJpegInfo(static_cast<uint8_t*>(buffer_addr), jpeg_blob->jpeg_size,
&jpeg_resolution, &exif_data) != 0) {
ADD_FAILURE() << "No valid JPEG image found in the buffer";
return false;
}
return true;
}
// Test parameters:
// - Camera ID
class Camera3SimpleStillCaptureTest
: public Camera3StillCaptureFixture,
public ::testing::WithParamInterface<int32_t> {
public:
Camera3SimpleStillCaptureTest()
: Camera3StillCaptureFixture(std::vector<int>(1, GetParam())),
cam_id_(GetParam()) {}
protected:
int cam_id_;
void TakePictureTest(uint32_t num_still_pictures);
struct ExifTestData {
ResolutionInfo thumbnail_resolution;
int32_t orientation;
uint8_t jpeg_quality;
uint8_t thumbnail_quality;
};
void ValidateExifKeys(const ResolutionInfo& jpeg_resolution,
const ExifTestData& exif_test_data,
const BufferHandleUniquePtr& buffer,
size_t buffer_size,
const camera_metadata_t& metadata,
const time_t& date_time);
};
static int32_t GetExifTagInteger(const ExifData& exif_data,
const ExifIfd& ifd,
const ExifTag& tag,
const ExifByteOrder& byte_order) {
ExifEntry* entry = exif_data_get_entry((&exif_data), tag);
if (!entry) {
ADD_FAILURE() << "Failed to get EXIF tag "
<< exif_tag_get_name_in_ifd(tag, ifd);
return -EINVAL;
}
switch (entry->format) {
case EXIF_FORMAT_SHORT:
return exif_get_short(entry->data, byte_order);
case EXIF_FORMAT_LONG:
return exif_get_long(entry->data, byte_order);
case EXIF_FORMAT_SLONG:
return exif_get_slong(entry->data, byte_order);
default:
ADD_FAILURE() << "Invalid EXIF entry format " << entry->format;
return -EINVAL;
}
}
static const char* GetExifTagString(const ExifData& exif_data,
const ExifIfd& ifd,
const ExifTag& tag) {
ExifEntry* entry = exif_data_get_entry((&exif_data), tag);
if (!entry) {
ADD_FAILURE() << "Failed to get EXIF tag "
<< exif_tag_get_name_in_ifd(tag, ifd);
return nullptr;
}
EXPECT_EQ(EXIF_FORMAT_ASCII, entry->format)
<< "Invalid EXIF entry string format " << entry->format;
return reinterpret_cast<char*>(entry->data);
}
static float GetExifTagFloat(const ExifData& exif_data,
const ExifIfd& ifd,
const ExifTag& tag,
const ExifByteOrder& byte_order) {
ExifEntry* entry = exif_data_get_entry((&exif_data), tag);
if (!entry) {
ADD_FAILURE() << "Failed to get EXIF tag "
<< exif_tag_get_name_in_ifd(tag, ifd);
return -EINVAL;
}
switch (entry->format) {
case EXIF_FORMAT_RATIONAL: {
ExifRational rational = exif_get_rational(entry->data, byte_order);
return static_cast<float>(rational.numerator) /
static_cast<float>(rational.denominator);
}
case EXIF_FORMAT_SRATIONAL: {
ExifSRational srational = exif_get_srational(entry->data, byte_order);
return static_cast<float>(srational.numerator) /
static_cast<float>(srational.denominator);
}
default:
ADD_FAILURE() << "Invalid EXIF entry format " << entry->format;
return -EINVAL;
}
}
static int64_t GetMetadataInteger(const camera_metadata_t& metadata,
int32_t key) {
camera_metadata_ro_entry_t entry;
if (find_camera_metadata_ro_entry(&metadata, key, &entry)) {
ADD_FAILURE() << "Cannot find the metadata "
<< get_camera_metadata_tag_name(key);
return -EINVAL;
}
switch (entry.type) {
case TYPE_BYTE:
return entry.data.u8[0];
case TYPE_INT32:
return entry.data.i32[0];
case TYPE_INT64:
return entry.data.i64[0];
default:
ADD_FAILURE() << "Unexpected metadata entry type " << entry.type;
return -EINVAL;
}
}
static float GetMetadataKeyValueFloat(const camera_metadata_t& metadata,
int32_t key) {
camera_metadata_ro_entry_t entry;
if (find_camera_metadata_ro_entry(&metadata, key, &entry)) {
ADD_FAILURE() << "Cannot find the metadata "
<< get_camera_metadata_tag_name(key);
return -EINVAL;
}
if (entry.type != TYPE_FLOAT) {
ADD_FAILURE() << "Unexpected metadata entry type " << entry.type;
return -EINVAL;
}
return entry.data.f[0];
}
static ResolutionInfo GetMetadataThumbnailSize(
const camera_metadata_t& metadata) {
const size_t kNumOfElementsInSizeEntry = 2;
enum { SIZE_ENTRY_WIDTH_INDEX, SIZE_ENTRY_HEIGHT_INDEX };
camera_metadata_ro_entry_t entry;
if (find_camera_metadata_ro_entry(&metadata, ANDROID_JPEG_THUMBNAIL_SIZE,
&entry)) {
ADD_FAILURE() << "Cannot find the metadata ANDROID_JPEG_THUMBNAIL_SIZE";
return ResolutionInfo(-1, -1);
}
if (entry.count != kNumOfElementsInSizeEntry) {
ADD_FAILURE()
<< "Invalid entry count of metadata ANDROID_JPEG_THUMBNAIL_SIZE ("
<< entry.count << ")";
return ResolutionInfo(-1, -1);
}
return ResolutionInfo(entry.data.i32[SIZE_ENTRY_WIDTH_INDEX],
entry.data.i32[SIZE_ENTRY_HEIGHT_INDEX]);
}
enum {
ORIENTATION_UNDEFINED,
ORIENTATION_NORMAL,
ORIENTATION_FLIP_HORIZONTAL,
ORIENTATION_ROTATE_180,
ORIENTATION_FLIP_VERTICAL,
ORIENTATION_TRANSPOSE,
ORIENTATION_ROTATE_90,
ORIENTATION_TRANSVERSE,
ORIENTATION_ROTATE_270,
};
static int32_t GetExifOrientationInDegree(int32_t exif_orientation) {
switch (exif_orientation) {
case ORIENTATION_NORMAL:
return 0;
case ORIENTATION_ROTATE_90:
return 90;
case ORIENTATION_ROTATE_180:
return 180;
case ORIENTATION_ROTATE_270:
return 270;
default:
ADD_FAILURE() << "It is impossible to get non 0, 90, 180, 270 degress "
"exif info based on the request orientation range";
return 0;
}
}
void Camera3SimpleStillCaptureTest::ValidateExifKeys(
const ResolutionInfo& jpeg_resolution,
const ExifTestData& exif_test_data,
const BufferHandleUniquePtr& buffer,
size_t buffer_size,
const camera_metadata_t& metadata,
const time_t& date_time) {
const int32_t kExifDateTimeStringLength = 19;
const double kExifDateTimeErrorMarginSeconds = 60;
const float kExifFocalLengthErrorMargin = 0.001f;
const float kExifExposureTimeErrorMarginRation = 0.05f;
const float kExifExposureTimeMinErrorMarginSeconds = 0.002f;
const float kOneNanoSecond = 1e-9;
const float kExifApertureErrorMargin = 0.001f;
auto GetClosestValueInArray = [](std::vector<float> values, float target) {
int min_idx = -1;
float min_distance = std::numeric_limits<float>::max();
for (size_t i = 0; i < values.size(); i++) {
float distance = std::abs(values[i] - target);
if (min_distance > distance) {
min_distance = distance;
min_idx = i;
}
}
return (min_idx >= 0) ? values[min_idx]
: -std::numeric_limits<float>::max();
};
auto SwapWidthAndHeight = [](ResolutionInfo* resolution) {
*resolution = ResolutionInfo(resolution->Height(), resolution->Width());
};
JpegExifInfo jpeg_exif_info(buffer, buffer_size);
ASSERT_TRUE(jpeg_exif_info.Initialize());
ExifByteOrder byte_order = exif_data_get_byte_order(jpeg_exif_info.exif_data);
const ExifData& exif_data = *jpeg_exif_info.exif_data;
int32_t exif_orientation = GetExifTagInteger(
exif_data, EXIF_IFD_0, EXIF_TAG_ORIENTATION, byte_order);
EXPECT_LE(ORIENTATION_UNDEFINED, exif_orientation)
<< "Invalid EXIF orientation value";
EXPECT_GE(ORIENTATION_ROTATE_270, exif_orientation)
<< "Invalid EXIF orientation value";
EXPECT_TRUE(exif_orientation == ORIENTATION_UNDEFINED ||
exif_test_data.orientation ==
GetExifOrientationInDegree(exif_orientation))
<< "EXIF orientaiton should match requested orientation";
if (exif_test_data.thumbnail_resolution == ResolutionInfo(0, 0)) {
EXPECT_EQ(nullptr, exif_data.data)
<< "JPEG shouldn't have thumbnail when thumbnail size is (0, 0)";
} else {
EXPECT_NE(nullptr, exif_data.data) << "No thumbnail found in JPEG image";
ResolutionInfo expected_thumbnail_resolution(
exif_test_data.thumbnail_resolution);
if (exif_test_data.orientation % 180 == 90 &&
exif_orientation == ORIENTATION_UNDEFINED) {
// Device physically rotated image+thumbnail data
// Expect thumbnail size to be also rotated
SwapWidthAndHeight(&expected_thumbnail_resolution);
}
EXPECT_EQ(expected_thumbnail_resolution, GetMetadataThumbnailSize(metadata))
<< "JPEG thumbnail size result and request should match";
if (exif_data.data) {
ResolutionInfo actual_thumbnail_resolution(0, 0);
if (GetJpegInfo(exif_data.data, exif_data.size,
&actual_thumbnail_resolution, nullptr) != 0) {
ADD_FAILURE() << "No valid thumbnail image found in the buffer";
} else {
EXPECT_EQ(exif_test_data.thumbnail_resolution,
actual_thumbnail_resolution)
<< "EXIF thumbnail image size should match requested size";
}
}
}
EXPECT_EQ(exif_test_data.orientation,
GetMetadataInteger(metadata, ANDROID_JPEG_ORIENTATION))
<< "JPEG orientation result and request should match";
EXPECT_EQ(exif_test_data.jpeg_quality,
GetMetadataInteger(metadata, ANDROID_JPEG_QUALITY))
<< "JPEG quality result and request should match";
EXPECT_EQ(exif_test_data.thumbnail_quality,
GetMetadataInteger(metadata, ANDROID_JPEG_THUMBNAIL_QUALITY))
<< "JPEG thumbnail quality result and request should match";
// TAG_IMAGE_WIDTH and TAG_IMAGE_LENGTH and TAG_ORIENTATION.
// Orientation and exif width/height need to be tested carefully, two cases:
// 1. Device rotates the image buffer physically, then exif width/height may
// not match the requested still capture size, we need swap them to check.
// 2. Device uses the exif tag to record the image orientation, it doesn't
// rotate the jpeg image buffer itself. In this case, the exif width/height
// should always match the requested still capture size, and the exif
// orientation should always match the requested orientation.
ResolutionInfo expected_jpeg_size(jpeg_resolution);
if (exif_test_data.orientation % 180 == 90 &&
exif_orientation == ORIENTATION_UNDEFINED) {
// Device captured image doesn't respect the requested orientation, which
// means it rotates the image buffer physically. Then we should swap the
// exif width/height accordingly to compare.
SwapWidthAndHeight(&expected_jpeg_size);
}
EXPECT_EQ(
expected_jpeg_size,
ResolutionInfo(GetExifTagInteger(exif_data, EXIF_IFD_0,
EXIF_TAG_IMAGE_WIDTH, byte_order),
GetExifTagInteger(exif_data, EXIF_IFD_0,
EXIF_TAG_IMAGE_LENGTH, byte_order)))
<< "EXIF JPEG size should match requested size";
EXPECT_EQ(
expected_jpeg_size,
ResolutionInfo(GetExifTagInteger(exif_data, EXIF_IFD_EXIF,
EXIF_TAG_PIXEL_X_DIMENSION, byte_order),
GetExifTagInteger(exif_data, EXIF_IFD_EXIF,
EXIF_TAG_PIXEL_Y_DIMENSION, byte_order)))
<< "EXIF JPEG pixel dimension should match requested size";
EXPECT_EQ(jpeg_resolution, jpeg_exif_info.jpeg_resolution)
<< "JPEG size result and request should match";
// Validate date time between EXIF data and current time
const char* exif_datetime_string =
GetExifTagString(exif_data, EXIF_IFD_0, EXIF_TAG_DATE_TIME);
if (exif_datetime_string) {
EXPECT_EQ(kExifDateTimeStringLength, strlen(exif_datetime_string))
<< "EXIF dateTime is in wrong format";
struct tm exif_tm = {};
strptime(exif_datetime_string, "%Y:%m:%d %H:%M:%S", &exif_tm);
time_t exif_time = mktime(&exif_tm);
EXPECT_GT(kExifDateTimeErrorMarginSeconds, difftime(date_time, exif_time))
<< "Capture time deviates too much from the current time";
const char* exif_datetime_digitized = GetExifTagString(
exif_data, EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_DIGITIZED);
if (exif_datetime_digitized) {
EXPECT_EQ(kExifDateTimeStringLength, strlen(exif_datetime_digitized))
<< "EXIF digitizedTime is in wrong format";
EXPECT_EQ(0, strncmp(exif_datetime_string, exif_datetime_digitized,
kExifDateTimeStringLength))
<< "EXIF dataTime should match digitizedTime";
}
}
// Validate focal length between EXIF data and metadata
std::vector<float> focal_lengths;
if (cam_service_.GetStaticInfo(cam_id_)->GetAvailableFocalLengths(
&focal_lengths) != 0 ||
focal_lengths.empty()) {
ADD_FAILURE() << "Failed to get available focal lengths";
} else {
float exif_focal_length = GetExifTagFloat(
exif_data, EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH, byte_order);
EXPECT_GT(
kExifFocalLengthErrorMargin,
std::abs(GetClosestValueInArray(focal_lengths, exif_focal_length) -
exif_focal_length))
<< "EXIF focal length should be one of the available focal lengths";
float result_focal_length =
GetMetadataKeyValueFloat(metadata, ANDROID_LENS_FOCAL_LENGTH);
EXPECT_LT(0.0f, result_focal_length)
<< "Result Focal length " << result_focal_length
<< " should be positive";
EXPECT_NE(focal_lengths.end(),
std::find(focal_lengths.begin(), focal_lengths.end(),
result_focal_length))
<< "Result Focal length should be one of the available focal lengths";
EXPECT_GT(kExifFocalLengthErrorMargin,
std::abs(result_focal_length - exif_focal_length))
<< "EXIF focal length should match capture result";
}
// Validate exposure time between EXIF data and metadata
float exif_exposure_time = GetExifTagFloat(
exif_data, EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_TIME, byte_order);
if (exif_exposure_time > 0 &&
cam_service_.GetStaticInfo(cam_id_)->IsKeyAvailable(
ANDROID_SENSOR_EXPOSURE_TIME)) {
float result_exposure_time = static_cast<float>(GetMetadataInteger(
metadata, ANDROID_SENSOR_EXPOSURE_TIME)) *
kOneNanoSecond;
float tolerance =
std::max(result_exposure_time * kExifExposureTimeErrorMarginRation,
kExifExposureTimeMinErrorMarginSeconds);
EXPECT_GT(tolerance, std::abs(result_exposure_time - exif_exposure_time))
<< "Exif exposure time doesn't match";
}
// Validate aperture between EXIF data and metadata
float exif_aperture =
GetExifTagFloat(exif_data, EXIF_IFD_EXIF, EXIF_TAG_FNUMBER, byte_order);
if (exif_aperture > 0 && cam_service_.GetStaticInfo(cam_id_)->IsKeyAvailable(
ANDROID_LENS_INFO_AVAILABLE_APERTURES)) {
std::vector<float> apertures;
if (cam_service_.GetStaticInfo(cam_id_)->GetAvailableApertures(
&apertures) != 0 ||
apertures.empty()) {
ADD_FAILURE() << "Failed to get available apertures";
} else {
float result_aperture =
GetMetadataKeyValueFloat(metadata, ANDROID_LENS_APERTURE);
EXPECT_GT(kExifApertureErrorMargin,
std::abs(GetClosestValueInArray(apertures, exif_aperture) -
exif_aperture))
<< "Aperture value should be one of the available apertures";
EXPECT_GT(kExifApertureErrorMargin,
std::abs(result_aperture - exif_aperture))
<< "Exif aperture length should match capture result";
}
}
// Verify EXIF TAG_FLASH is available
GetExifTagInteger(exif_data, EXIF_IFD_EXIF, EXIF_TAG_FLASH, byte_order);
// Verify EXIF TAG_WHITE_BALANCE is available
GetExifTagInteger(exif_data, EXIF_IFD_EXIF, EXIF_TAG_WHITE_BALANCE,
byte_order);
// TODO(hywu): For full devices, validate flash and AWB between EXIF and
// metadata
// Validate ISO between EXIF and metadata
if (cam_service_.GetStaticInfo(cam_id_)->IsKeyAvailable(
ANDROID_SENSOR_SENSITIVITY)) {
int32_t iso = GetExifTagInteger(exif_data, EXIF_IFD_EXIF,
EXIF_TAG_ISO_SPEED_RATINGS, byte_order);
EXPECT_EQ(GetMetadataInteger(metadata, ANDROID_SENSOR_SENSITIVITY), iso)
<< "EXIF TAG_ISO is incorrect";
}
auto is_number = [&](const ExifIfd& ifd, const ExifTag& tag) {
const char* c = GetExifTagString(exif_data, ifd, tag);
const std::string s(c ? c : "");
return !s.empty() && std::find_if(s.begin(), s.end(), [](char c) {
return !std::isdigit(c);
}) == s.end();
};
EXPECT_TRUE(is_number(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME));
EXPECT_TRUE(is_number(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_ORIGINAL));
EXPECT_TRUE(is_number(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_DIGITIZED));
}
TEST_P(Camera3SimpleStillCaptureTest, JpegExifTest) {
// Reference:
// camera2/cts/StillCaptureTest.java.java#testJpegExif
ResolutionInfo jpeg_resolution =
cam_service_.GetStaticInfo(cam_id_)
->GetSortedOutputResolutions(HAL_PIXEL_FORMAT_BLOB)
.back();
std::vector<ResolutionInfo> thumbnail_resolutions;
EXPECT_TRUE(cam_service_.GetStaticInfo(cam_id_)->GetAvailableThumbnailSizes(
&thumbnail_resolutions) == 0 &&
!thumbnail_resolutions.empty())
<< "JPEG thumbnail sizes are not available";
// Size must contain (0, 0).
EXPECT_TRUE(std::find(thumbnail_resolutions.begin(),
thumbnail_resolutions.end(),
ResolutionInfo(0, 0)) != thumbnail_resolutions.end())
<< "JPEG thumbnail sizes should contain (0, 0)";
// Each size must be distinct.
EXPECT_EQ(thumbnail_resolutions.size(),
std::set<ResolutionInfo>(thumbnail_resolutions.begin(),
thumbnail_resolutions.end())
.size())
<< "JPEG thumbnail sizes contain duplicate items";
// Must be sorted in ascending order by area, by width if areas are same.
EXPECT_TRUE(std::is_sorted(thumbnail_resolutions.begin(),
thumbnail_resolutions.end()))
<< "JPEG thumbnail sizes are not in ascending order";
ResolutionInfo preview_resolution =
cam_service_.GetStaticInfo(cam_id_)
->GetSortedOutputResolutions(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED)
.back();
ResolutionInfo recording_resolution(0, 0);
cam_service_.StartPreview(cam_id_, preview_resolution, jpeg_resolution,
recording_resolution);
ExifTestData exif_test_data[] = {
{thumbnail_resolutions.front(), 90, 80, 75},
{thumbnail_resolutions.front(), 180, 90, 85},
{thumbnail_resolutions.back(), 270, 100, 100},
};
CameraMetadataUniquePtr metadata(
clone_camera_metadata(cam_service_.ConstructDefaultRequestSettings(
cam_id_, CAMERA3_TEMPLATE_STILL_CAPTURE)));
ASSERT_NE(nullptr, metadata.get())
<< "Failed to create still capture metadata";
for (const auto& it : exif_test_data) {
#define ARRAY_SIZE(A) (sizeof(A) / sizeof(*(A)))
int32_t thumbnail_resolution[] = {it.thumbnail_resolution.Width(),
it.thumbnail_resolution.Height()};
EXPECT_EQ(0,
UpdateMetadata(ANDROID_JPEG_THUMBNAIL_SIZE, thumbnail_resolution,
ARRAY_SIZE(thumbnail_resolution), &metadata));
EXPECT_EQ(0, UpdateMetadata(ANDROID_JPEG_ORIENTATION, &it.orientation, 1,
&metadata));
EXPECT_EQ(0, UpdateMetadata(ANDROID_JPEG_QUALITY, &it.jpeg_quality, 1,
&metadata));
EXPECT_EQ(0, UpdateMetadata(ANDROID_JPEG_THUMBNAIL_QUALITY,
&it.thumbnail_quality, 1, &metadata));
cam_service_.TakeStillCapture(cam_id_, metadata.get());
}
struct timespec timeout;
clock_gettime(CLOCK_REALTIME, &timeout);
timeout.tv_sec += ARRAY_SIZE(exif_test_data); // 1 second per capture
for (size_t i = 0; i < arraysize(exif_test_data); i++) {
ASSERT_EQ(0, WaitStillCaptureResult(cam_id_, timeout))
<< "Waiting for still capture result timeout";
}
for (size_t i = 0;
i < still_capture_results_[cam_id_].result_metadatas.size(); i++) {
ValidateExifKeys(jpeg_resolution, exif_test_data[i],
still_capture_results_[cam_id_].buffer_handles[i],
jpeg_max_sizes_[cam_id_],
*still_capture_results_[cam_id_].result_metadatas[i].get(),
still_capture_results_[cam_id_].result_date_time[i]);
}
cam_service_.StopPreview(cam_id_);
}
void Camera3SimpleStillCaptureTest::TakePictureTest(
uint32_t num_still_pictures) {
auto IsAFSupported = [this]() {
std::vector<int32_t> available_af_modes;
cam_service_.GetStaticInfo(cam_id_)->GetAvailableAFModes(
&available_af_modes);
int32_t af_modes[] = {ANDROID_CONTROL_AF_MODE_AUTO,
ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE,
ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO,
ANDROID_CONTROL_AF_MODE_MACRO};
for (const auto& it : af_modes) {
if (std::find(available_af_modes.begin(), available_af_modes.end(), it) !=
available_af_modes.end()) {
return true;
}
}
return false;
};
ResolutionInfo jpeg_resolution =
cam_service_.GetStaticInfo(cam_id_)
->GetSortedOutputResolutions(HAL_PIXEL_FORMAT_BLOB)
.back();
ResolutionInfo preview_resolution =
cam_service_.GetStaticInfo(cam_id_)
->GetSortedOutputResolutions(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED)
.back();
ResolutionInfo recording_resolution(0, 0);
cam_service_.StartPreview(cam_id_, preview_resolution, jpeg_resolution,
recording_resolution);
// Trigger an auto focus run, and wait for AF locked.
if (IsAFSupported()) {
cam_service_.StartAutoFocus(cam_id_);
ASSERT_EQ(0, cam_service_.WaitForAutoFocusDone(cam_id_))
<< "Wait for auto focus done timed out";
}
// Wait for AWB converged, then lock it.
ASSERT_EQ(0, cam_service_.WaitForAWBConvergedAndLock(cam_id_))
<< "Wait for AWB converged timed out";
// Trigger an AE precapture metering sequence and wait for AE converged.
cam_service_.StartAEPrecapture(cam_id_);
ASSERT_EQ(0, cam_service_.WaitForAEStable(cam_id_))
<< "Wait for AE stable timed out";
const camera_metadata_t* metadata =
cam_service_.ConstructDefaultRequestSettings(
cam_id_, CAMERA3_TEMPLATE_STILL_CAPTURE);
for (uint32_t i = 0; i < num_still_pictures; i++) {
cam_service_.TakeStillCapture(cam_id_, metadata);
}
cam_service_.StopPreview(cam_id_);
}
TEST_P(Camera3SimpleStillCaptureTest, TakePictureTest) {
TakePictureTest(1);
}
TEST_P(Camera3SimpleStillCaptureTest, PerformanceTest) {
TakePictureTest(2);
}
// Test parameters:
// - Camera ID, preview resolution, JPEG resolution
class Camera3JpegResolutionTest
: public Camera3StillCaptureFixture,
public ::testing::WithParamInterface<
std::tuple<int32_t, ResolutionInfo, ResolutionInfo>> {
public:
Camera3JpegResolutionTest()
: Camera3StillCaptureFixture(
std::vector<int>(1, std::get<0>(GetParam()))),
cam_id_(std::get<0>(GetParam())) {}
protected:
int cam_id_;
};
TEST_P(Camera3JpegResolutionTest, JpegResolutionTest) {
ResolutionInfo preview_resolution(std::get<1>(GetParam()));
ResolutionInfo jpeg_resolution(std::get<2>(GetParam()));
VLOGF(1) << "Device " << cam_id_;
VLOGF(1) << "Preview resolution " << preview_resolution.Width() << "x"
<< preview_resolution.Height();
VLOGF(1) << "JPEG resolution " << jpeg_resolution.Width() << "x"
<< jpeg_resolution.Height();
ResolutionInfo recording_resolution(0, 0);
cam_service_.StartPreview(cam_id_, preview_resolution, jpeg_resolution,
recording_resolution);
CameraMetadataUniquePtr metadata(
clone_camera_metadata(cam_service_.ConstructDefaultRequestSettings(
cam_id_, CAMERA3_TEMPLATE_STILL_CAPTURE)));
ASSERT_NE(nullptr, metadata.get())
<< "Failed to create still capture metadata";
cam_service_.TakeStillCapture(cam_id_, metadata.get());
cam_service_.StopPreview(cam_id_);
ASSERT_EQ(1, still_capture_results_[cam_id_].buffer_handles.size())
<< "Incorrect number of still captures received";
JpegExifInfo jpeg_exif_info(
still_capture_results_[cam_id_].buffer_handles[0],
jpeg_max_sizes_[cam_id_]);
ASSERT_TRUE(jpeg_exif_info.Initialize());
EXPECT_EQ(jpeg_resolution, jpeg_exif_info.jpeg_resolution)
<< "JPEG size result and request should match";
}
INSTANTIATE_TEST_CASE_P(Camera3StillCaptureTest,
Camera3SimpleStillCaptureTest,
::testing::ValuesIn(Camera3Module().GetCameraIds()));
static std::vector<std::tuple<int, ResolutionInfo, ResolutionInfo>>
IterateCameraIdPreviewJpegResolution() {
std::vector<std::tuple<int, ResolutionInfo, ResolutionInfo>> result;
auto cam_ids = Camera3Module().GetCameraIds();
for (const auto& cam_id : cam_ids) {
auto preview_resolutions = Camera3Module().GetSortedOutputResolutions(
cam_id, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
auto jpeg_resolutions = Camera3Module().GetSortedOutputResolutions(
cam_id, HAL_PIXEL_FORMAT_BLOB);
for (const auto& preview_resolution : preview_resolutions) {
for (const auto& jpeg_resolution : jpeg_resolutions) {
result.emplace_back(cam_id, preview_resolution, jpeg_resolution);
}
}
}
return result;
}
INSTANTIATE_TEST_CASE_P(
Camera3StillCaptureTest,
Camera3JpegResolutionTest,
::testing::ValuesIn(IterateCameraIdPreviewJpegResolution()));
} // namespace camera3_test