blob: e4e89bdede468f44b8d51c2b42479efc90744465 [file] [log] [blame]
// Copyright 2016 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_frame_fixture.h"
#include <linux/videodev2.h>
#include <semaphore.h>
#include <stdio.h>
#include <limits>
#include <list>
#include <unordered_map>
#include <unordered_set>
#include <base/command_line.h>
#include <base/files/file_util.h>
#include <base/macros.h>
#include <base/stl_util.h>
#include <base/strings/string_split.h>
#include <jpeglib.h>
#include <libyuv.h>
#include "camera3_test/camera3_perf_log.h"
#include "cros-camera/common.h"
namespace camera3_test {
int32_t Camera3FrameFixture::CreateCaptureRequest(
const camera_metadata_t& metadata, uint32_t* frame_number) {
// Allocate output buffers
std::vector<camera3_stream_buffer_t> output_buffers;
if (cam_device_.AllocateOutputStreamBuffers(&output_buffers)) {
ADD_FAILURE() << "Failed to allocate buffers for capture request";
return -EINVAL;
}
camera3_capture_request_t capture_request = {
.frame_number = UINT32_MAX,
.settings = &metadata,
.input_buffer = NULL,
.num_output_buffers = static_cast<uint32_t>(output_buffers.size()),
.output_buffers = output_buffers.data(),
.num_physcam_settings = 0};
// Process capture request
int32_t ret = cam_device_.ProcessCaptureRequest(&capture_request);
if (ret == 0 && frame_number) {
*frame_number = capture_request.frame_number;
}
return ret;
}
int32_t Camera3FrameFixture::CreateCaptureRequestByMetadata(
const ScopedCameraMetadata& metadata, uint32_t* frame_number) {
return CreateCaptureRequest(*metadata, frame_number);
}
int32_t Camera3FrameFixture::CreateCaptureRequestByTemplate(
int32_t type, uint32_t* frame_number) {
const camera_metadata_t* default_settings;
default_settings = cam_device_.ConstructDefaultRequestSettings(type);
if (!default_settings) {
ADD_FAILURE() << "Camera default settings are NULL";
return -EINVAL;
}
return CreateCaptureRequest(*default_settings, frame_number);
}
void Camera3FrameFixture::WaitShutterAndCaptureResult(
const struct timespec& timeout) {
ASSERT_EQ(0, cam_device_.WaitShutter(timeout))
<< "Timeout waiting for shutter callback";
ASSERT_EQ(0, cam_device_.WaitCaptureResult(timeout))
<< "Timeout waiting for capture result callback";
}
std::vector<int32_t>
Camera3FrameFixture::GetAvailableColorBarsTestPatternModes() {
std::vector<int32_t> test_pattern_modes;
if (cam_device_.GetStaticInfo()->GetAvailableTestPatternModes(
&test_pattern_modes) != 0) {
ADD_FAILURE() << "Failed to get sensor available test pattern modes";
return std::vector<int32_t>();
}
std::vector<int32_t> result;
for (const auto& it : supported_color_bars_test_pattern_modes_) {
if (std::find(test_pattern_modes.begin(), test_pattern_modes.end(), it) !=
test_pattern_modes.end()) {
result.push_back(it);
}
}
return result;
}
Camera3FrameFixture::ImagePlane::ImagePlane(uint32_t stride,
uint32_t size,
uint8_t* addr)
: stride(stride), size(size), addr(addr) {}
#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d))
Camera3FrameFixture::Image::Image(uint32_t w, uint32_t h, ImageFormat f)
: width(w), height(h), format(f) {
if (format == ImageFormat::IMAGE_FORMAT_ARGB) {
size = w * h * kARGBPixelWidth;
data.resize(size);
planes.emplace_back(w * kARGBPixelWidth, size, data.data());
} else if (format == ImageFormat::IMAGE_FORMAT_I420) {
uint32_t cstride = DIV_ROUND_UP(w, 2);
size = w * h + cstride * DIV_ROUND_UP(h, 2) * 2;
uint32_t uv_plane_size = cstride * DIV_ROUND_UP(h, 2);
data.resize(size);
planes.emplace_back(w, w * h, data.data()); // y
planes.emplace_back(cstride, uv_plane_size,
planes.back().addr + planes.back().size); // u
planes.emplace_back(cstride, uv_plane_size,
planes.back().addr + planes.back().size); // v
}
}
int Camera3FrameFixture::Image::SaveToFile(const std::string filename) const {
const char* suffix =
(format == ImageFormat::IMAGE_FORMAT_ARGB) ? ".argb" : ".i420";
base::FilePath file_path(filename + suffix);
if (base::WriteFile(file_path, reinterpret_cast<const char*>(data.data()),
size) != size) {
LOGF(ERROR) << "Failed to write file " << filename << suffix;
return -EINVAL;
}
return 0;
}
Camera3FrameFixture::ScopedImage Camera3FrameFixture::ConvertToImage(
cros::ScopedBufferHandle buffer,
uint32_t width,
uint32_t height,
ImageFormat format) {
if (!buffer || format >= ImageFormat::IMAGE_FORMAT_END) {
LOGF(ERROR) << "Invalid input buffer or format";
return ScopedImage(nullptr);
}
buffer_handle_t handle = *buffer;
auto hnd = camera_buffer_handle_t::FromBufferHandle(handle);
if (!hnd || hnd->buffer_id == 0) {
LOGF(ERROR) << "Invalid input buffer handle";
return ScopedImage(nullptr);
}
ScopedImage out_buffer(new Image(width, height, format));
auto gralloc = Camera3TestGralloc::GetInstance();
if (gralloc->GetFormat(handle) == HAL_PIXEL_FORMAT_BLOB) {
size_t jpeg_max_size = cam_device_.GetStaticInfo()->GetJpegMaxSize();
void* buf_addr = nullptr;
if (gralloc->Lock(handle, 0, 0, 0, jpeg_max_size, 1, &buf_addr) != 0 ||
!buf_addr) {
LOGF(ERROR) << "Failed to lock input buffer";
return ScopedImage(nullptr);
}
auto jpeg_blob = reinterpret_cast<camera3_jpeg_blob_t*>(
static_cast<uint8_t*>(buf_addr) + jpeg_max_size -
sizeof(camera3_jpeg_blob_t));
if (static_cast<void*>(jpeg_blob) < buf_addr ||
jpeg_blob->jpeg_blob_id != CAMERA3_JPEG_BLOB_ID) {
gralloc->Unlock(handle);
LOGF(ERROR) << "Invalid JPEG BLOB ID";
return ScopedImage(nullptr);
}
if ((format == ImageFormat::IMAGE_FORMAT_I420 &&
libyuv::MJPGToI420(
static_cast<uint8_t*>(buf_addr), jpeg_blob->jpeg_size,
out_buffer->planes[0].addr, out_buffer->planes[0].stride,
out_buffer->planes[1].addr, out_buffer->planes[1].stride,
out_buffer->planes[2].addr, out_buffer->planes[2].stride, width,
height, width, height) != 0) ||
(format == ImageFormat::IMAGE_FORMAT_ARGB &&
libyuv::MJPGToARGB(static_cast<uint8_t*>(buf_addr),
jpeg_blob->jpeg_size, out_buffer->planes[0].addr,
out_buffer->planes[0].stride, width, height, width,
height) != 0)) {
LOGF(ERROR) << "Failed to convert image from JPEG";
out_buffer.reset();
}
gralloc->Unlock(handle);
} else {
struct android_ycbcr in_ycbcr_info;
if (gralloc->LockYCbCr(handle, 0, 0, 0, width, height, &in_ycbcr_info) !=
0) {
LOGF(ERROR) << "Failed to lock input buffer";
return ScopedImage(nullptr);
}
uint32_t v4l2_format =
cros::CameraBufferManager::GetV4L2PixelFormat(handle);
switch (v4l2_format) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV12M:
if ((format == ImageFormat::IMAGE_FORMAT_I420 &&
libyuv::NV12ToI420(
static_cast<uint8_t*>(in_ycbcr_info.y), in_ycbcr_info.ystride,
static_cast<uint8_t*>(in_ycbcr_info.cb), in_ycbcr_info.cstride,
out_buffer->planes[0].addr, out_buffer->planes[0].stride,
out_buffer->planes[1].addr, out_buffer->planes[1].stride,
out_buffer->planes[2].addr, out_buffer->planes[2].stride,
width, height) != 0) ||
(format == ImageFormat::IMAGE_FORMAT_ARGB &&
libyuv::NV12ToARGB(
static_cast<uint8_t*>(in_ycbcr_info.y), in_ycbcr_info.ystride,
static_cast<uint8_t*>(in_ycbcr_info.cb), in_ycbcr_info.cstride,
out_buffer->planes[0].addr, out_buffer->planes[0].stride,
width, height) != 0)) {
LOGF(ERROR) << "Failed to convert image from NV12";
out_buffer.reset();
}
break;
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV21M:
if ((format == ImageFormat::IMAGE_FORMAT_I420 &&
libyuv::NV21ToI420(
static_cast<uint8_t*>(in_ycbcr_info.y), in_ycbcr_info.ystride,
static_cast<uint8_t*>(in_ycbcr_info.cr), in_ycbcr_info.cstride,
out_buffer->planes[0].addr, out_buffer->planes[0].stride,
out_buffer->planes[1].addr, out_buffer->planes[1].stride,
out_buffer->planes[2].addr, out_buffer->planes[2].stride,
width, height) != 0) ||
(format == ImageFormat::IMAGE_FORMAT_ARGB &&
libyuv::NV21ToARGB(
static_cast<uint8_t*>(in_ycbcr_info.y), in_ycbcr_info.ystride,
static_cast<uint8_t*>(in_ycbcr_info.cr), in_ycbcr_info.cstride,
out_buffer->planes[0].addr, out_buffer->planes[0].stride,
width, height) != 0)) {
LOGF(ERROR) << "Failed to convert image from NV21";
out_buffer.reset();
}
break;
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YUV420M:
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_YVU420M:
if ((format == ImageFormat::IMAGE_FORMAT_I420 &&
libyuv::I420Copy(
static_cast<uint8_t*>(in_ycbcr_info.y), in_ycbcr_info.ystride,
static_cast<uint8_t*>(in_ycbcr_info.cb), in_ycbcr_info.cstride,
static_cast<uint8_t*>(in_ycbcr_info.cr), in_ycbcr_info.cstride,
out_buffer->planes[0].addr, out_buffer->planes[0].stride,
out_buffer->planes[1].addr, out_buffer->planes[1].stride,
out_buffer->planes[2].addr, out_buffer->planes[2].stride,
width, height) != 0) ||
(format == ImageFormat::IMAGE_FORMAT_ARGB &&
libyuv::I420ToARGB(
static_cast<uint8_t*>(in_ycbcr_info.y), in_ycbcr_info.ystride,
static_cast<uint8_t*>(in_ycbcr_info.cb), in_ycbcr_info.cstride,
static_cast<uint8_t*>(in_ycbcr_info.cr), in_ycbcr_info.cstride,
out_buffer->planes[0].addr, out_buffer->planes[0].stride,
width, height) != 0)) {
LOGF(ERROR) << "Failed to convert image from YUV420 or YVU420";
out_buffer.reset();
}
break;
default:
LOGF(ERROR) << "Unsupported format " << FormatToString(v4l2_format);
out_buffer.reset();
}
gralloc->Unlock(handle);
}
return out_buffer;
}
Camera3FrameFixture::ScopedImage Camera3FrameFixture::ConvertToImageAndRotate(
cros::ScopedBufferHandle buffer,
uint32_t width,
uint32_t height,
ImageFormat format,
int32_t rotation) {
ScopedImage image = ConvertToImage(std::move(buffer), width, height, format);
if (image == nullptr) {
LOGF(ERROR) << "Failed to convert image before rotate";
return image;
}
if (format != ImageFormat::IMAGE_FORMAT_I420) {
LOGF(ERROR) << "Do not support rotate image with format: "
<< static_cast<int>(format);
return image;
}
int new_width = width;
int new_height = height;
libyuv::RotationMode rotationMode;
if (rotation % 180 == 90) {
std::swap(new_width, new_height);
}
switch (rotation) {
case 90:
rotationMode = libyuv::kRotate90;
break;
case 180:
rotationMode = libyuv::kRotate180;
break;
case 270:
rotationMode = libyuv::kRotate270;
break;
default:
rotationMode = libyuv::kRotate0;
break;
}
ScopedImage rotated_image(
new Image(new_width, new_height, ImageFormat::IMAGE_FORMAT_I420));
libyuv::I420Rotate(
image->planes[0].addr, image->planes[0].stride, image->planes[1].addr,
image->planes[1].stride, image->planes[2].addr, image->planes[2].stride,
rotated_image->planes[0].addr, rotated_image->planes[0].stride,
rotated_image->planes[1].addr, rotated_image->planes[1].stride,
rotated_image->planes[2].addr, rotated_image->planes[2].stride, width,
height, rotationMode);
return rotated_image;
}
Camera3FrameFixture::ScopedImage Camera3FrameFixture::GenerateColorBarsPattern(
uint32_t width,
uint32_t height,
const std::vector<std::tuple<uint8_t, uint8_t, uint8_t, float>>&
color_bars_pattern,
int32_t color_bars_pattern_mode,
uint32_t sensor_pixel_array_width,
uint32_t sensor_pixel_array_height) {
if (std::find(supported_color_bars_test_pattern_modes_.begin(),
supported_color_bars_test_pattern_modes_.end(),
color_bars_pattern_mode) ==
supported_color_bars_test_pattern_modes_.end()) {
return nullptr;
}
ScopedImage argb_image(new Image(sensor_pixel_array_width,
sensor_pixel_array_height,
ImageFormat::IMAGE_FORMAT_ARGB));
uint8_t* pdata = argb_image->planes[0].addr;
int color_bar_height = sensor_pixel_array_height / 128 * 128;
if (color_bar_height == 0) {
color_bar_height = sensor_pixel_array_height;
}
for (size_t h = 0; h < sensor_pixel_array_height; h++) {
float gray_factor =
static_cast<float>(color_bar_height - (h % color_bar_height)) /
color_bar_height;
int index = 0;
for (size_t w = 0; w < sensor_pixel_array_width; w++) {
if (index + 1 < color_bars_pattern.size() &&
w > sensor_pixel_array_width *
std::get<3>(color_bars_pattern[index + 1])) {
index++;
}
auto get_fade_color = [&](uint8_t base_color) {
if (color_bars_pattern_mode ==
ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS) {
return base_color;
}
uint8_t color = base_color * gray_factor;
const int start = std::get<3>(color_bars_pattern[index]);
const int end = (index + 1 == color_bars_pattern.size())
? 1.0f
: std::get<3>(color_bars_pattern[index + 1]);
if (w > (start + end) / 2 * sensor_pixel_array_width) {
color = (color & 0xF0) | (color >> 4);
}
return color;
};
*pdata++ = get_fade_color(std::get<2>(color_bars_pattern[index])); // B
*pdata++ = get_fade_color(std::get<1>(color_bars_pattern[index])); // G
*pdata++ = get_fade_color(std::get<0>(color_bars_pattern[index])); // R
*pdata++ = 0x00;
}
}
return CropRotateScale(std::move(argb_image), 0, width, height);
}
Camera3FrameFixture::ScopedImage Camera3FrameFixture::CropRotateScale(
ScopedImage input_image,
int32_t rotation_degrees,
uint32_t width,
uint32_t height) {
if (input_image->format != ImageFormat::IMAGE_FORMAT_ARGB &&
input_image->format != ImageFormat::IMAGE_FORMAT_I420) {
ADD_FAILURE() << "Unsupported image format";
return nullptr;
}
libyuv::RotationMode rotation_mode = libyuv::RotationMode::kRotate0;
switch (rotation_degrees) {
case 0:
break;
case 90:
rotation_mode = libyuv::RotationMode::kRotate90;
break;
case 270:
rotation_mode = libyuv::RotationMode::kRotate270;
break;
default:
LOGF(ERROR) << "Invalid rotation degree: " << rotation_degrees;
return nullptr;
}
int cropped_width;
int cropped_height;
int crop_x = 0;
int crop_y = 0;
if (rotation_mode == libyuv::RotationMode::kRotate0) {
if (input_image->width * height > width * input_image->height) {
cropped_width = input_image->height * width / height;
cropped_height = input_image->height;
if (cropped_width % 2 == 1) {
// Make cropped_width to the closest even number.
cropped_width++;
}
crop_x = (input_image->width - cropped_width) / 2;
} else {
cropped_width = input_image->width;
cropped_height = input_image->width * height / width;
if (cropped_height % 2 == 1) {
// Make cropped_height to the closest even number.
cropped_height++;
}
crop_y = (input_image->height - cropped_height) / 2;
}
} else {
if (input_image->width * width > input_image->height * height) {
cropped_width = input_image->height * height / width;
cropped_height = input_image->height;
if (cropped_width % 2 == 1) {
// Make cropped_width to the closest even number.
cropped_width++;
}
crop_x = (input_image->width - cropped_width) / 2;
} else {
cropped_width = input_image->width;
cropped_height = input_image->width * width / height;
if (cropped_height % 2 == 1) {
// Make cropped_height to the closest even number.
cropped_height++;
}
crop_y = (input_image->height - cropped_height) / 2;
}
}
ScopedImage cropped_image(new Image(
rotation_mode == libyuv::RotationMode::kRotate0 ? cropped_width
: cropped_height,
rotation_mode == libyuv::RotationMode::kRotate0 ? cropped_height
: cropped_width,
ImageFormat::IMAGE_FORMAT_I420));
int res = libyuv::ConvertToI420(
input_image->planes[0].addr, input_image->size,
cropped_image->planes[0].addr, cropped_image->planes[0].stride,
cropped_image->planes[1].addr, cropped_image->planes[1].stride,
cropped_image->planes[2].addr, cropped_image->planes[2].stride, crop_x,
crop_y, input_image->width, input_image->height, cropped_width,
cropped_height, rotation_mode,
input_image->format == ImageFormat::IMAGE_FORMAT_ARGB
? libyuv::FourCC::FOURCC_ARGB
: libyuv::FourCC::FOURCC_I420);
if (res) {
ADD_FAILURE() << "ConvertToI420 failed: " << res;
return nullptr;
}
ScopedImage i420_image(
new Image(width, height, ImageFormat::IMAGE_FORMAT_I420));
res = libyuv::I420Scale(
cropped_image->planes[0].addr, cropped_image->planes[0].stride,
cropped_image->planes[1].addr, cropped_image->planes[1].stride,
cropped_image->planes[2].addr, cropped_image->planes[2].stride,
cropped_image->width, cropped_image->height, i420_image->planes[0].addr,
i420_image->planes[0].stride, i420_image->planes[1].addr,
i420_image->planes[1].stride, i420_image->planes[2].addr,
i420_image->planes[2].stride, width, height,
libyuv::FilterMode::kFilterNone);
if (res) {
ADD_FAILURE() << "I420Scale failed: " << res;
return nullptr;
}
return i420_image;
}
double Camera3FrameFixture::ComputeSsim(const Image& buffer_a,
const Image& buffer_b) {
if (buffer_a.format != ImageFormat::IMAGE_FORMAT_I420 ||
buffer_b.format != ImageFormat::IMAGE_FORMAT_I420 ||
buffer_a.width != buffer_b.width || buffer_a.height != buffer_b.height) {
LOGF(ERROR) << "Images are not of I420 format or resolutions do not match";
return 0.0;
}
return libyuv::I420Ssim(buffer_a.planes[0].addr, buffer_a.planes[0].stride,
buffer_a.planes[1].addr, buffer_a.planes[1].stride,
buffer_a.planes[2].addr, buffer_a.planes[2].stride,
buffer_b.planes[0].addr, buffer_b.planes[0].stride,
buffer_b.planes[1].addr, buffer_b.planes[1].stride,
buffer_b.planes[2].addr, buffer_b.planes[2].stride,
buffer_a.width, buffer_a.height);
}
// Get real-time clock time after waiting for given timeout
void GetTimeOfTimeout(int32_t ms, struct timespec* ts) {
memset(ts, 0, sizeof(*ts));
if (clock_gettime(CLOCK_REALTIME, ts)) {
LOGF(ERROR) << "Failed to get clock time";
}
ts->tv_sec += ms / 1000;
ts->tv_nsec += (ms % 1000) * 1000;
}
// Test parameters:
// - Camera ID
// - Template ID
// - Frame format
// - If true, capture with the maximum resolution supported for this format;
// otherwise, capture the minimum one.
class Camera3SingleFrameTest
: public Camera3FrameFixture,
public ::testing::WithParamInterface<
std::tuple<int32_t, int32_t, int32_t, bool>> {
public:
Camera3SingleFrameTest() : Camera3FrameFixture(std::get<0>(GetParam())) {}
};
TEST_P(Camera3SingleFrameTest, GetFrame) {
int32_t format = std::get<1>(GetParam());
int32_t type = std::get<2>(GetParam());
if (!cam_device_.IsTemplateSupported(type)) {
return;
}
if (cam_device_.GetStaticInfo()->IsFormatAvailable(format)) {
ResolutionInfo resolution(0, 0);
if (std::get<3>(GetParam())) {
ASSERT_EQ(0, GetMaxResolution(format, &resolution, true))
<< "Failed to get max resolution for format " << format;
} else {
ASSERT_EQ(0, GetMinResolution(format, &resolution, true))
<< "Failed to get min resolution for format " << format;
}
VLOGF(1) << "Device " << cam_id_;
VLOGF(1) << "Format 0x" << std::hex << format;
VLOGF(1) << "Resolution " << resolution.Width() << "x"
<< resolution.Height();
cam_device_.AddOutputStream(format, resolution.Width(), resolution.Height(),
CAMERA3_STREAM_ROTATION_0);
ASSERT_EQ(0, cam_device_.ConfigureStreams(nullptr))
<< "Configuring stream fails";
ASSERT_EQ(0, CreateCaptureRequestByTemplate(type, nullptr))
<< "Creating capture request fails";
struct timespec timeout;
GetTimeOfTimeout(kDefaultTimeoutMs, &timeout);
WaitShutterAndCaptureResult(timeout);
}
}
// Test parameters:
// - Camera ID
// - Template ID
// - Number of frames to capture
class Camera3MultiFrameTest : public Camera3FrameFixture,
public ::testing::WithParamInterface<
std::tuple<int32_t, int32_t, int32_t>> {
public:
Camera3MultiFrameTest() : Camera3FrameFixture(std::get<0>(GetParam())) {}
};
TEST_P(Camera3MultiFrameTest, GetFrame) {
cam_device_.AddOutputStream(default_format_, default_width_, default_height_,
CAMERA3_STREAM_ROTATION_0);
std::vector<const camera3_stream_t*> streams;
ASSERT_EQ(0, cam_device_.ConfigureStreams(&streams))
<< "Configuring stream fails";
ASSERT_EQ(1, streams.size());
int32_t stream_queue_depth = static_cast<int32_t>(streams[0]->max_buffers);
int32_t type = std::get<1>(GetParam());
if (!cam_device_.IsTemplateSupported(type)) {
return;
}
int32_t num_frames = std::get<2>(GetParam());
struct timespec timeout;
for (int32_t i = 0; i < num_frames; i++) {
GetTimeOfTimeout(kDefaultTimeoutMs, &timeout);
if (i >= stream_queue_depth) {
WaitShutterAndCaptureResult(timeout);
}
EXPECT_EQ(0, CreateCaptureRequestByTemplate(type, nullptr))
<< "Creating capture request fails";
}
for (int32_t i = 0; i < std::min(num_frames, stream_queue_depth); i++) {
WaitShutterAndCaptureResult(timeout);
}
}
// Test parameters:
// - Camera ID
class Camera3MixedTemplateMultiFrameTest
: public Camera3FrameFixture,
public ::testing::WithParamInterface<int32_t> {
public:
Camera3MixedTemplateMultiFrameTest() : Camera3FrameFixture(GetParam()) {}
};
TEST_P(Camera3MixedTemplateMultiFrameTest, GetFrame) {
cam_device_.AddOutputStream(default_format_, default_width_, default_height_,
CAMERA3_STREAM_ROTATION_0);
ASSERT_EQ(0, cam_device_.ConfigureStreams(nullptr))
<< "Configuring stream fails";
int32_t types[] = {CAMERA3_TEMPLATE_PREVIEW, CAMERA3_TEMPLATE_STILL_CAPTURE,
CAMERA3_TEMPLATE_VIDEO_RECORD,
CAMERA3_TEMPLATE_VIDEO_SNAPSHOT};
for (size_t i = 0; i < base::size(types); ++i) {
EXPECT_EQ(0, CreateCaptureRequestByTemplate(types[i], nullptr))
<< "Creating capture request fails";
}
struct timespec timeout;
for (size_t i = 0; i < base::size(types); ++i) {
GetTimeOfTimeout(kDefaultTimeoutMs, &timeout);
WaitShutterAndCaptureResult(timeout);
}
}
// Test parameters:
// - Camera ID
// - Template ID
// - Number of frames to capture
class Camera3FlushRequestsTest : public Camera3FrameFixture,
public ::testing::WithParamInterface<
std::tuple<int32_t, int32_t, int32_t>> {
public:
Camera3FlushRequestsTest()
: Camera3FrameFixture(std::get<0>(GetParam())), num_capture_results_(0) {}
void SetUp() override;
protected:
// Callback functions from HAL device
virtual void ProcessCaptureResult(const camera3_capture_result* result);
// Callback functions from HAL device
virtual void Notify(const camera3_notify_msg* msg);
// Number of received capture results with all output buffers returned
int32_t num_capture_results_;
sem_t flush_result_sem_;
private:
void CheckAllResultReceived(uint32_t frame_number);
// Number of configured streams
static const int32_t kNumberOfConfiguredStreams;
// Store number of output buffers returned in capture results with frame
// number as the key
std::unordered_map<uint32_t, int32_t> num_capture_result_buffers_;
// Store frame numbers of which all partial results are received
std::unordered_set<uint32_t> metadata_complete_frame_numbers_;
// Store the frames numbers that had been notified with
// CAMERA3_MSG_ERROR_REQUEST.
std::unordered_set<uint32_t> notified_error_frames_;
};
const int32_t Camera3FlushRequestsTest::kNumberOfConfiguredStreams = 1;
void Camera3FlushRequestsTest::SetUp() {
Camera3FrameFixture::SetUp();
cam_device_.RegisterProcessCaptureResultCallback(base::Bind(
&Camera3FlushRequestsTest::ProcessCaptureResult, base::Unretained(this)));
cam_device_.RegisterNotifyCallback(
base::Bind(&Camera3FlushRequestsTest::Notify, base::Unretained(this)));
sem_init(&flush_result_sem_, 0, 0);
}
void Camera3FlushRequestsTest::CheckAllResultReceived(uint32_t frame_number) {
if (num_capture_result_buffers_[frame_number] == kNumberOfConfiguredStreams &&
(notified_error_frames_.find(frame_number) !=
notified_error_frames_.end() ||
metadata_complete_frame_numbers_.find(frame_number) !=
metadata_complete_frame_numbers_.end())) {
num_capture_results_++;
sem_post(&flush_result_sem_);
}
}
void Camera3FlushRequestsTest::ProcessCaptureResult(
const camera3_capture_result* result) {
VLOGF_ENTER();
ASSERT_NE(nullptr, result) << "Capture result is null";
EXPECT_EQ(result->result != nullptr, result->partial_result != 0)
<< "Inconsistent partial metadata";
if (result->result &&
result->partial_result ==
cam_device_.GetStaticInfo()->GetPartialResultCount()) {
metadata_complete_frame_numbers_.insert(result->frame_number);
}
num_capture_result_buffers_[result->frame_number] +=
result->num_output_buffers;
CheckAllResultReceived(result->frame_number);
}
void Camera3FlushRequestsTest::Notify(const camera3_notify_msg* msg) {
// TODO(shik): support the partial failure cases
VLOGF_ENTER();
if (msg->type == CAMERA3_MSG_ERROR) {
const camera3_error_msg_t& error = msg->message.error;
if (error.error_code == CAMERA3_MSG_ERROR_REQUEST ||
error.error_code == CAMERA3_MSG_ERROR_RESULT) {
notified_error_frames_.insert(error.frame_number);
}
CheckAllResultReceived(error.frame_number);
}
}
TEST_P(Camera3FlushRequestsTest, GetFrame) {
// TODO(hywu): spawn a thread to test simultaneous process_capture_request
// and flush
// The number of configured streams must match the value of
// |kNumberOfConfiguredStreams|.
cam_device_.AddOutputStream(default_format_, default_width_, default_height_,
CAMERA3_STREAM_ROTATION_0);
ASSERT_EQ(0, cam_device_.ConfigureStreams(nullptr))
<< "Configuring stream fails";
int32_t type = std::get<1>(GetParam());
if (!cam_device_.IsTemplateSupported(type)) {
return;
}
const int32_t num_frames = std::get<2>(GetParam());
for (int32_t i = 0; i < num_frames; i++) {
EXPECT_EQ(0, CreateCaptureRequestByTemplate(type, nullptr))
<< "Creating capture request fails";
}
ASSERT_EQ(0, cam_device_.Flush()) << "Flushing capture requests fails";
// flush() should only return when there are no more outstanding buffers or
// requests left in the HAL
EXPECT_EQ(num_frames, num_capture_results_)
<< "There are requests left in the HAL after flushing";
struct timespec timeout;
for (int32_t i = 0; i < num_frames; i++) {
GetTimeOfTimeout(kDefaultTimeoutMs, &timeout);
ASSERT_EQ(0, sem_timedwait(&flush_result_sem_, &timeout));
}
}
// Test parameters:
// - Camera ID
class Camera3MultiStreamFrameTest
: public Camera3FrameFixture,
public ::testing::WithParamInterface<int32_t> {
public:
Camera3MultiStreamFrameTest() : Camera3FrameFixture(GetParam()) {}
};
TEST_P(Camera3MultiStreamFrameTest, GetFrame) {
// Preview stream with large size no bigger than 1080p
ResolutionInfo limit_resolution(1920, 1080);
ResolutionInfo preview_resolution(0, 0);
ASSERT_EQ(0, GetMaxResolution(HAL_PIXEL_FORMAT_YCbCr_420_888,
&preview_resolution, true))
<< "Failed to get max resolution for implementation defined format";
preview_resolution = CapResolution(preview_resolution, limit_resolution);
cam_device_.AddOutputStream(
HAL_PIXEL_FORMAT_YCbCr_420_888, preview_resolution.Width(),
preview_resolution.Height(), CAMERA3_STREAM_ROTATION_0);
// Second preview stream
cam_device_.AddOutputStream(
HAL_PIXEL_FORMAT_YCbCr_420_888, preview_resolution.Width(),
preview_resolution.Height(), CAMERA3_STREAM_ROTATION_0);
// Capture stream with largest size
ResolutionInfo capture_resolution(0, 0);
ASSERT_EQ(0,
GetMaxResolution(HAL_PIXEL_FORMAT_BLOB, &capture_resolution, true))
<< "Failed to get max resolution for YCbCr 420 format";
cam_device_.AddOutputStream(HAL_PIXEL_FORMAT_BLOB, capture_resolution.Width(),
capture_resolution.Height(),
CAMERA3_STREAM_ROTATION_0);
ASSERT_EQ(0, cam_device_.ConfigureStreams(nullptr))
<< "Configuring stream fails";
ASSERT_EQ(0,
CreateCaptureRequestByTemplate(CAMERA3_TEMPLATE_PREVIEW, nullptr))
<< "Creating capture request fails";
struct timespec timeout;
GetTimeOfTimeout(kDefaultTimeoutMs, &timeout);
WaitShutterAndCaptureResult(timeout);
}
// Test parameters:
// - Camera ID
class Camera3InvalidRequestTest : public Camera3FrameFixture,
public ::testing::WithParamInterface<int> {
public:
Camera3InvalidRequestTest() : Camera3FrameFixture(GetParam()) {}
};
TEST_P(Camera3InvalidRequestTest, NullOrUnconfiguredRequest) {
// Reference:
// camera2/cts/CameraDeviceTest.java#testInvalidCapture
EXPECT_NE(0, cam_device_.ProcessCaptureRequest(nullptr))
<< "Capturing with null request should fail";
const camera_metadata_t* default_settings;
default_settings =
cam_device_.ConstructDefaultRequestSettings(CAMERA3_TEMPLATE_PREVIEW);
std::vector<camera3_stream_buffer_t> output_buffers;
std::vector<camera3_stream_t> streams(1);
streams[0].stream_type = CAMERA3_STREAM_OUTPUT;
streams[0].width = static_cast<uint32_t>(default_width_);
streams[0].height = static_cast<uint32_t>(default_height_);
streams[0].format = default_format_;
std::vector<const camera3_stream_t*> stream_ptrs(1, &streams[0]);
ASSERT_EQ(0, cam_device_.AllocateOutputBuffersByStreams(stream_ptrs,
&output_buffers))
<< "Failed to allocate buffers for capture request";
camera3_capture_request_t capture_request = {
.frame_number = 0,
.settings = default_settings,
.input_buffer = NULL,
.num_output_buffers = static_cast<uint32_t>(output_buffers.size()),
.output_buffers = output_buffers.data(),
.num_physcam_settings = 0};
EXPECT_NE(0, cam_device_.ProcessCaptureRequest(&capture_request))
<< "Capturing with stream unconfigured should fail";
}
// Test parameters:
// - Camera ID
// - Number of frames to capture
class Camera3SimpleCaptureFrames
: public Camera3FrameFixture,
public ::testing::WithParamInterface<std::tuple<int32_t, int32_t>> {
public:
Camera3SimpleCaptureFrames()
: Camera3FrameFixture(std::get<0>(GetParam())),
num_frames_(std::get<1>(GetParam())) {}
protected:
// Process result metadata and/or output buffers
void ProcessResultMetadataOutputBuffers(
uint32_t frame_number,
ScopedCameraMetadata metadata,
std::vector<cros::ScopedBufferHandle> buffers) override;
// Validate capture result keys
void ValidateCaptureResultKeys(const ScopedCameraMetadata& request_metadata);
// Get waiver keys per camera device hardware level and capability
void GetWaiverKeys(std::set<int32_t>* waiver_keys) const;
// Process partial metadata
void ProcessPartialMetadata(
std::vector<ScopedCameraMetadata>* partial_metadata) override;
// Validate partial results
void ValidatePartialMetadata();
const int32_t num_frames_;
// Store result metadata in the first-in-first-out order
std::list<ScopedCameraMetadata> result_metadata_;
// Store partial metadata in the first-in-first-out order
std::list<std::vector<ScopedCameraMetadata>> partial_metadata_list_;
static const int32_t kCaptureResultKeys[69];
};
void Camera3SimpleCaptureFrames::ProcessResultMetadataOutputBuffers(
uint32_t frame_number,
ScopedCameraMetadata metadata,
std::vector<cros::ScopedBufferHandle> buffers) {
result_metadata_.push_back(std::move(metadata));
}
void Camera3SimpleCaptureFrames::ValidateCaptureResultKeys(
const ScopedCameraMetadata& request_metadata) {
std::set<int32_t> waiver_keys;
GetWaiverKeys(&waiver_keys);
while (!result_metadata_.empty()) {
camera_metadata_t* metadata = result_metadata_.front().get();
for (auto key : kCaptureResultKeys) {
if (waiver_keys.find(key) != waiver_keys.end()) {
continue;
}
// Check the critical tags here.
switch (key) {
case ANDROID_CONTROL_AE_MODE:
case ANDROID_CONTROL_AF_MODE:
case ANDROID_CONTROL_AWB_MODE:
case ANDROID_CONTROL_MODE:
case ANDROID_STATISTICS_FACE_DETECT_MODE:
case ANDROID_NOISE_REDUCTION_MODE:
camera_metadata_ro_entry_t request_entry;
if (find_camera_metadata_ro_entry(request_metadata.get(), key,
&request_entry)) {
ADD_FAILURE() << "Metadata " << get_camera_metadata_tag_name(key)
<< " is unavailable in capture request";
continue;
}
camera_metadata_ro_entry_t result_entry;
if (find_camera_metadata_ro_entry(metadata, key, &result_entry)) {
ADD_FAILURE() << "Metadata " << get_camera_metadata_tag_name(key)
<< " is not present in capture result";
continue;
}
EXPECT_EQ(request_entry.data.i32[0], result_entry.data.i32[0])
<< "Wrong value of metadata " << get_camera_metadata_tag_name(key)
<< " in capture result";
break;
case ANDROID_REQUEST_PIPELINE_DEPTH:
break;
default:
// Only do non-null check for the rest of keys.
camera_metadata_ro_entry_t entry;
EXPECT_EQ(0, find_camera_metadata_ro_entry(metadata, key, &entry))
<< "Metadata " << get_camera_metadata_tag_name(key)
<< " is unavailable in capture result";
break;
}
}
result_metadata_.pop_front();
}
}
const int32_t Camera3SimpleCaptureFrames::kCaptureResultKeys[] = {
ANDROID_COLOR_CORRECTION_MODE,
ANDROID_COLOR_CORRECTION_TRANSFORM,
ANDROID_COLOR_CORRECTION_GAINS,
ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
ANDROID_CONTROL_AE_ANTIBANDING_MODE,
ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
ANDROID_CONTROL_AE_LOCK,
ANDROID_CONTROL_AE_MODE,
ANDROID_CONTROL_AE_REGIONS,
ANDROID_CONTROL_AF_REGIONS,
ANDROID_CONTROL_AWB_REGIONS,
ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
ANDROID_CONTROL_AF_MODE,
ANDROID_CONTROL_AF_TRIGGER,
ANDROID_CONTROL_AWB_LOCK,
ANDROID_CONTROL_AWB_MODE,
ANDROID_CONTROL_CAPTURE_INTENT,
ANDROID_CONTROL_EFFECT_MODE,
ANDROID_CONTROL_MODE,
ANDROID_CONTROL_SCENE_MODE,
ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
ANDROID_CONTROL_AE_STATE,
ANDROID_CONTROL_AF_STATE,
ANDROID_CONTROL_AWB_STATE,
ANDROID_EDGE_MODE,
ANDROID_FLASH_MODE,
ANDROID_FLASH_STATE,
ANDROID_HOT_PIXEL_MODE,
ANDROID_JPEG_ORIENTATION,
ANDROID_JPEG_QUALITY,
ANDROID_JPEG_THUMBNAIL_QUALITY,
ANDROID_JPEG_THUMBNAIL_SIZE,
ANDROID_LENS_APERTURE,
ANDROID_LENS_FILTER_DENSITY,
ANDROID_LENS_FOCAL_LENGTH,
ANDROID_LENS_FOCUS_DISTANCE,
ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
ANDROID_LENS_POSE_ROTATION,
ANDROID_LENS_POSE_TRANSLATION,
ANDROID_LENS_FOCUS_RANGE,
ANDROID_LENS_STATE,
ANDROID_LENS_INTRINSIC_CALIBRATION,
ANDROID_LENS_RADIAL_DISTORTION,
ANDROID_NOISE_REDUCTION_MODE,
ANDROID_REQUEST_PIPELINE_DEPTH,
ANDROID_SCALER_CROP_REGION,
ANDROID_SENSOR_EXPOSURE_TIME,
ANDROID_SENSOR_FRAME_DURATION,
ANDROID_SENSOR_SENSITIVITY,
ANDROID_SENSOR_TIMESTAMP,
ANDROID_SENSOR_NEUTRAL_COLOR_POINT,
ANDROID_SENSOR_NOISE_PROFILE,
ANDROID_SENSOR_GREEN_SPLIT,
ANDROID_SENSOR_TEST_PATTERN_DATA,
ANDROID_SENSOR_TEST_PATTERN_MODE,
ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,
ANDROID_SHADING_MODE,
ANDROID_STATISTICS_FACE_DETECT_MODE,
ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
ANDROID_STATISTICS_LENS_SHADING_CORRECTION_MAP,
ANDROID_STATISTICS_SCENE_FLICKER,
ANDROID_STATISTICS_HOT_PIXEL_MAP,
ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
ANDROID_TONEMAP_MODE,
ANDROID_TONEMAP_GAMMA,
ANDROID_TONEMAP_PRESET_CURVE,
ANDROID_BLACK_LEVEL_LOCK,
ANDROID_REPROCESS_EFFECTIVE_EXPOSURE_FACTOR};
void Camera3SimpleCaptureFrames::GetWaiverKeys(
std::set<int32_t>* waiver_keys) const {
// Global waiver keys
waiver_keys->insert(ANDROID_JPEG_ORIENTATION);
waiver_keys->insert(ANDROID_JPEG_QUALITY);
waiver_keys->insert(ANDROID_JPEG_THUMBNAIL_QUALITY);
waiver_keys->insert(ANDROID_JPEG_THUMBNAIL_SIZE);
// Keys only present when corresponding control is on are being verified in
// its own functional test
// Only present in certain tonemap mode
waiver_keys->insert(ANDROID_TONEMAP_CURVE_BLUE);
waiver_keys->insert(ANDROID_TONEMAP_CURVE_GREEN);
waiver_keys->insert(ANDROID_TONEMAP_CURVE_RED);
waiver_keys->insert(ANDROID_TONEMAP_GAMMA);
waiver_keys->insert(ANDROID_TONEMAP_PRESET_CURVE);
// Only present when test pattern mode is SOLID_COLOR.
waiver_keys->insert(ANDROID_SENSOR_TEST_PATTERN_DATA);
// Only present when STATISTICS_LENS_SHADING_MAP_MODE is ON
waiver_keys->insert(ANDROID_STATISTICS_LENS_SHADING_CORRECTION_MAP);
// Only present when STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES is ON
waiver_keys->insert(ANDROID_STATISTICS_HOT_PIXEL_MAP);
// Only present when face detection is on
waiver_keys->insert(ANDROID_STATISTICS_FACE_IDS);
waiver_keys->insert(ANDROID_STATISTICS_FACE_LANDMARKS);
waiver_keys->insert(ANDROID_STATISTICS_FACE_RECTANGLES);
waiver_keys->insert(ANDROID_STATISTICS_FACE_SCORES);
// Only present in reprocessing capture result.
waiver_keys->insert(ANDROID_REPROCESS_EFFECTIVE_EXPOSURE_FACTOR);
// Keys not required if RAW is not supported
if (!cam_device_.GetStaticInfo()->IsCapabilitySupported(
ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
waiver_keys->insert(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
waiver_keys->insert(ANDROID_SENSOR_GREEN_SPLIT);
waiver_keys->insert(ANDROID_SENSOR_NOISE_PROFILE);
}
// Keys for depth output capability
if (!cam_device_.GetStaticInfo()->IsCapabilitySupported(
ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT)) {
waiver_keys->insert(ANDROID_LENS_POSE_ROTATION);
waiver_keys->insert(ANDROID_LENS_POSE_TRANSLATION);
waiver_keys->insert(ANDROID_LENS_INTRINSIC_CALIBRATION);
waiver_keys->insert(ANDROID_LENS_RADIAL_DISTORTION);
}
if (cam_device_.GetStaticInfo()->GetAeMaxRegions() == 0) {
waiver_keys->insert(ANDROID_CONTROL_AE_REGIONS);
}
if (cam_device_.GetStaticInfo()->GetAwbMaxRegions() == 0) {
waiver_keys->insert(ANDROID_CONTROL_AWB_REGIONS);
}
if (cam_device_.GetStaticInfo()->GetAfMaxRegions() == 0) {
waiver_keys->insert(ANDROID_CONTROL_AF_REGIONS);
}
if (cam_device_.GetStaticInfo()->IsHardwareLevelAtLeastFull()) {
return;
}
// Keys to waive for limited devices
if (!cam_device_.GetStaticInfo()->IsKeyAvailable(
ANDROID_COLOR_CORRECTION_MODE)) {
waiver_keys->insert(ANDROID_COLOR_CORRECTION_GAINS);
waiver_keys->insert(ANDROID_COLOR_CORRECTION_MODE);
waiver_keys->insert(ANDROID_COLOR_CORRECTION_TRANSFORM);
}
if (!cam_device_.GetStaticInfo()->IsKeyAvailable(
ANDROID_COLOR_CORRECTION_ABERRATION_MODE)) {
waiver_keys->insert(ANDROID_COLOR_CORRECTION_ABERRATION_MODE);
}
if (!cam_device_.GetStaticInfo()->IsKeyAvailable(ANDROID_TONEMAP_MODE)) {
waiver_keys->insert(ANDROID_TONEMAP_MODE);
}
if (!cam_device_.GetStaticInfo()->IsKeyAvailable(ANDROID_EDGE_MODE)) {
waiver_keys->insert(ANDROID_EDGE_MODE);
}
if (!cam_device_.GetStaticInfo()->IsKeyAvailable(ANDROID_HOT_PIXEL_MODE)) {
waiver_keys->insert(ANDROID_HOT_PIXEL_MODE);
}
if (!cam_device_.GetStaticInfo()->IsKeyAvailable(
ANDROID_NOISE_REDUCTION_MODE)) {
waiver_keys->insert(ANDROID_NOISE_REDUCTION_MODE);
}
if (!cam_device_.GetStaticInfo()->IsKeyAvailable(ANDROID_SHADING_MODE)) {
waiver_keys->insert(ANDROID_SHADING_MODE);
}
// Keys not required if neither MANUAL_SENSOR nor READ_SENSOR_SETTINGS is
// supported
if (!cam_device_.GetStaticInfo()->IsCapabilitySupported(
ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
waiver_keys->insert(ANDROID_SENSOR_EXPOSURE_TIME);
waiver_keys->insert(ANDROID_SENSOR_FRAME_DURATION);
waiver_keys->insert(ANDROID_SENSOR_SENSITIVITY);
waiver_keys->insert(ANDROID_BLACK_LEVEL_LOCK);
waiver_keys->insert(ANDROID_LENS_FOCUS_RANGE);
waiver_keys->insert(ANDROID_LENS_FOCUS_DISTANCE);
waiver_keys->insert(ANDROID_LENS_STATE);
waiver_keys->insert(ANDROID_LENS_APERTURE);
waiver_keys->insert(ANDROID_LENS_FILTER_DENSITY);
}
}
void Camera3SimpleCaptureFrames::ProcessPartialMetadata(
std::vector<ScopedCameraMetadata>* partial_metadata) {
partial_metadata_list_.resize(partial_metadata_list_.size() + 1);
for (auto& it : *partial_metadata) {
partial_metadata_list_.back().push_back(std::move(it));
}
}
void Camera3SimpleCaptureFrames::ValidatePartialMetadata() {
for (const auto& it : partial_metadata_list_) {
// Number of partial results is less than or equal to
// REQUEST_PARTIAL_RESULT_COUNT
EXPECT_GE(cam_device_.GetStaticInfo()->GetPartialResultCount(), it.size())
<< "Number of received partial results is wrong";
// Each key appeared in partial results must be unique across all partial
// results
for (size_t i = 0; i < it.size(); i++) {
size_t entry_count = get_camera_metadata_entry_count(it[i].get());
for (size_t entry_index = 0; entry_index < entry_count; entry_index++) {
camera_metadata_ro_entry_t entry;
ASSERT_EQ(
0, get_camera_metadata_ro_entry(it[i].get(), entry_index, &entry));
int32_t key = entry.tag;
for (size_t j = i + 1; j < it.size(); j++) {
EXPECT_NE(0, find_camera_metadata_ro_entry(it[j].get(), key, &entry))
<< "Key " << get_camera_metadata_tag_name(key)
<< " appears in multiple partial results";
}
}
}
}
}
TEST_P(Camera3SimpleCaptureFrames, Camera3ResultAllKeysTest) {
// Reference:
// camera2/cts/CaptureResultTest.java#testCameraCaptureResultAllKeys
cam_device_.AddOutputStream(default_format_, default_width_, default_height_,
CAMERA3_STREAM_ROTATION_0);
ASSERT_EQ(0, cam_device_.ConfigureStreams(nullptr))
<< "Configuring stream fails";
ScopedCameraMetadata metadata(clone_camera_metadata(
cam_device_.ConstructDefaultRequestSettings(CAMERA3_TEMPLATE_PREVIEW)));
for (int32_t i = 0; i < num_frames_; i++) {
ASSERT_EQ(0, CreateCaptureRequestByMetadata(metadata, nullptr))
<< "Creating capture request fails";
}
struct timespec timeout;
for (int32_t i = 0; i < num_frames_; i++) {
GetTimeOfTimeout(kDefaultTimeoutMs, &timeout);
WaitShutterAndCaptureResult(timeout);
}
ValidateCaptureResultKeys(metadata);
}
TEST_P(Camera3SimpleCaptureFrames, Camera3PartialResultTest) {
// Reference:
// camera2/cts/CaptureResultTest.java#testPartialResult
// Skip the test if partial result is not supported
if (cam_device_.GetStaticInfo()->GetPartialResultCount() == 1) {
return;
}
cam_device_.AddOutputStream(default_format_, default_width_, default_height_,
CAMERA3_STREAM_ROTATION_0);
ASSERT_EQ(0, cam_device_.ConfigureStreams(nullptr))
<< "Configuring stream fails";
for (int32_t i = 0; i < num_frames_; i++) {
ASSERT_EQ(0,
CreateCaptureRequestByTemplate(CAMERA3_TEMPLATE_PREVIEW, nullptr))
<< "Creating capture request fails";
}
struct timespec timeout;
for (int32_t i = 0; i < num_frames_; i++) {
GetTimeOfTimeout(kDefaultTimeoutMs, &timeout);
WaitShutterAndCaptureResult(timeout);
}
ValidatePartialMetadata();
}
// Test parameters:
// - Camera ID
class Camera3ResultTimestampsTest
: public Camera3FrameFixture,
public ::testing::WithParamInterface<int32_t> {
public:
Camera3ResultTimestampsTest() : Camera3FrameFixture(GetParam()) {}
void SetUp() override;
protected:
virtual void Notify(const camera3_notify_msg* msg);
// Process result metadata and/or output buffers
void ProcessResultMetadataOutputBuffers(
uint32_t frame_number,
ScopedCameraMetadata metadata,
std::vector<cros::ScopedBufferHandle> buffers) override;
// Validate and get one timestamp
void ValidateAndGetTimestamp(int64_t* timestamp);
private:
// Store timestamps of shutter callback in the first-in-first-out order
std::list<uint64_t> capture_timestamps_;
// Store result metadata in the first-in-first-out order
std::list<ScopedCameraMetadata> result_metadata_;
};
void Camera3ResultTimestampsTest::SetUp() {
Camera3FrameFixture::SetUp();
cam_device_.RegisterNotifyCallback(
base::Bind(&Camera3ResultTimestampsTest::Notify, base::Unretained(this)));
}
void Camera3ResultTimestampsTest::Notify(const camera3_notify_msg* msg) {
VLOGF_ENTER();
EXPECT_EQ(CAMERA3_MSG_SHUTTER, msg->type)
<< "Shutter error = " << msg->message.error.error_code;
if (msg->type == CAMERA3_MSG_SHUTTER) {
capture_timestamps_.push_back(msg->message.shutter.timestamp);
}
}
void Camera3ResultTimestampsTest::ProcessResultMetadataOutputBuffers(
uint32_t frame_number,
ScopedCameraMetadata metadata,
std::vector<cros::ScopedBufferHandle> buffers) {
VLOGF_ENTER();
result_metadata_.push_back(std::move(metadata));
}
void Camera3ResultTimestampsTest::ValidateAndGetTimestamp(int64_t* timestamp) {
ASSERT_FALSE(capture_timestamps_.empty())
<< "Capture timestamp is unavailable";
ASSERT_FALSE(result_metadata_.empty()) << "Result metadata is unavailable";
camera_metadata_ro_entry_t entry;
ASSERT_EQ(0, find_camera_metadata_ro_entry(result_metadata_.front().get(),
ANDROID_SENSOR_TIMESTAMP, &entry))
<< "Metadata key ANDROID_SENSOR_TIMESTAMP not found";
*timestamp = entry.data.i64[0];
EXPECT_EQ(capture_timestamps_.front(), *timestamp)
<< "Shutter notification timestamp must be same as capture result"
" timestamp";
capture_timestamps_.pop_front();
result_metadata_.pop_front();
}
TEST_P(Camera3ResultTimestampsTest, GetFrame) {
// Reference:
// camera2/cts/CaptureResultTest.java#testResultTimestamps
cam_device_.AddOutputStream(default_format_, default_width_, default_height_,
CAMERA3_STREAM_ROTATION_0);
ASSERT_EQ(0, cam_device_.ConfigureStreams(nullptr))
<< "Configuring stream fails";
ASSERT_EQ(0,
CreateCaptureRequestByTemplate(CAMERA3_TEMPLATE_PREVIEW, nullptr))
<< "Creating capture request fails";
struct timespec timeout;
GetTimeOfTimeout(kDefaultTimeoutMs, &timeout);
ASSERT_EQ(0, cam_device_.WaitCaptureResult(timeout));
int64_t timestamp1 = 0;
ValidateAndGetTimestamp(&timestamp1);
ASSERT_EQ(0,
CreateCaptureRequestByTemplate(CAMERA3_TEMPLATE_PREVIEW, nullptr))
<< "Creating capture request fails";
GetTimeOfTimeout(kDefaultTimeoutMs, &timeout);
ASSERT_EQ(0, cam_device_.WaitCaptureResult(timeout));
int64_t timestamp2 = 0;
ValidateAndGetTimestamp(&timestamp2);
ASSERT_LT(timestamp1, timestamp2) << "Timestamps must be increasing";
}
// Test parameters:
// - Camera ID
class Camera3InvalidBufferTest : public Camera3FrameFixture,
public ::testing::WithParamInterface<int32_t> {
public:
Camera3InvalidBufferTest() : Camera3FrameFixture(GetParam()) {}
void SetUp() override;
protected:
// Callback functions from HAL device
virtual void ProcessCaptureResult(const camera3_capture_result* result);
void RunInvalidBufferTest(buffer_handle_t* handle);
// Callback functions from HAL device
// Do nothing.
virtual void Notify(const camera3_notify_msg* msg) {}
sem_t capture_result_sem_;
private:
// Number of configured streams
static const int32_t kNumberOfConfiguredStreams;
};
const int32_t Camera3InvalidBufferTest::kNumberOfConfiguredStreams = 1;
void Camera3InvalidBufferTest::SetUp() {
Camera3FrameFixture::SetUp();
cam_device_.RegisterProcessCaptureResultCallback(base::Bind(
&Camera3InvalidBufferTest::ProcessCaptureResult, base::Unretained(this)));
cam_device_.RegisterNotifyCallback(
base::Bind(&Camera3InvalidBufferTest::Notify, base::Unretained(this)));
sem_init(&capture_result_sem_, 0, 0);
}
void Camera3InvalidBufferTest::ProcessCaptureResult(
const camera3_capture_result* result) {
VLOGF_ENTER();
ASSERT_NE(nullptr, result) << "Capture result is null";
for (uint32_t i = 0; i < result->num_output_buffers; i++) {
EXPECT_EQ(CAMERA3_BUFFER_STATUS_ERROR, result->output_buffers[i].status)
<< "Capture result should return error on invalid buffer";
}
if (result->num_output_buffers > 0) {
sem_post(&capture_result_sem_);
}
}
void Camera3InvalidBufferTest::RunInvalidBufferTest(buffer_handle_t* handle) {
cam_device_.AddOutputStream(default_format_, default_width_, default_height_,
CAMERA3_STREAM_ROTATION_0);
std::vector<const camera3_stream_t*> streams;
ASSERT_EQ(0, cam_device_.ConfigureStreams(&streams))
<< "Configuring stream fails";
const camera_metadata_t* default_settings;
default_settings =
cam_device_.ConstructDefaultRequestSettings(CAMERA3_TEMPLATE_PREVIEW);
ASSERT_NE(nullptr, default_settings) << "Camera default settings are NULL";
camera3_stream_buffer_t stream_buffer = {
.stream = const_cast<camera3_stream_t*>(streams.front()),
.buffer = handle,
.status = CAMERA3_BUFFER_STATUS_OK,
.acquire_fence = -1,
.release_fence = -1};
std::vector<camera3_stream_buffer_t> stream_buffers(1, stream_buffer);
camera3_capture_request_t capture_request = {
.frame_number = UINT32_MAX,
.settings = default_settings,
.input_buffer = NULL,
.num_output_buffers = static_cast<uint32_t>(stream_buffers.size()),
.output_buffers = stream_buffers.data(),
.num_physcam_settings = 0};
int ret = cam_device_.ProcessCaptureRequest(&capture_request);
if (ret == -EINVAL) {
return;
}
ASSERT_EQ(0, ret);
struct timespec timeout;
GetTimeOfTimeout(kDefaultTimeoutMs, &timeout);
ASSERT_EQ(0, sem_timedwait(&capture_result_sem_, &timeout));
}
TEST_P(Camera3InvalidBufferTest, NullBufferHandle) {
buffer_handle_t handle = nullptr;
RunInvalidBufferTest(&handle);
}
// Test parameters:
// - Camera ID, frame format, resolution width, resolution height
class Camera3FrameContentTest
: public Camera3FrameFixture,
public ::testing::WithParamInterface<
std::tuple<int32_t, int32_t, int32_t, int32_t>> {
public:
const double kContentTestSsimThreshold = 0.75;
Camera3FrameContentTest()
: Camera3FrameFixture(std::get<0>(GetParam())),
format_(std::get<1>(GetParam())),
width_(std::get<2>(GetParam())),
height_(std::get<3>(GetParam())) {}
protected:
void ProcessResultMetadataOutputBuffers(
uint32_t frame_number,
ScopedCameraMetadata metadata,
std::vector<cros::ScopedBufferHandle> buffers) override;
int32_t format_;
int32_t width_;
int32_t height_;
cros::ScopedBufferHandle buffer_handle_;
};
void Camera3FrameContentTest::ProcessResultMetadataOutputBuffers(
uint32_t frame_number,
ScopedCameraMetadata metadata,
std::vector<cros::ScopedBufferHandle> buffers) {
ASSERT_EQ(nullptr, buffer_handle_);
buffer_handle_ = std::move(buffers.front());
}
TEST_P(Camera3FrameContentTest, CorruptionDetection) {
auto test_pattern_modes = GetAvailableColorBarsTestPatternModes();
ASSERT_FALSE(test_pattern_modes.empty())
<< "Failed to get sensor available test pattern modes";
cam_device_.AddOutputStream(format_, width_, height_,
CAMERA3_STREAM_ROTATION_0);
ASSERT_EQ(0, cam_device_.ConfigureStreams(nullptr))
<< "Configuring stream fails";
ScopedCameraMetadata metadata(clone_camera_metadata(
cam_device_.ConstructDefaultRequestSettings(CAMERA3_TEMPLATE_PREVIEW)));
UpdateMetadata(ANDROID_SENSOR_TEST_PATTERN_MODE, test_pattern_modes.data(), 1,
&metadata);
ASSERT_EQ(0, CreateCaptureRequestByMetadata(metadata, nullptr))
<< "Creating capture request fails";
struct timespec timeout;
GetTimeOfTimeout(kDefaultTimeoutMs, &timeout);
WaitShutterAndCaptureResult(timeout);
ASSERT_NE(nullptr, buffer_handle_) << "Failed to get frame buffer";
auto capture_image = ConvertToImage(std::move(buffer_handle_), width_,
height_, ImageFormat::IMAGE_FORMAT_I420);
ASSERT_NE(nullptr, capture_image);
uint32_t sensor_pixel_array_width;
uint32_t sensor_pixel_array_height;
ASSERT_EQ(0, cam_device_.GetStaticInfo()->GetSensorPixelArraySize(
&sensor_pixel_array_width, &sensor_pixel_array_height));
for (const auto& it : color_bars_test_patterns_) {
auto pattern_image = GenerateColorBarsPattern(
width_, height_, it, test_pattern_modes.front(),
sensor_pixel_array_width, sensor_pixel_array_height);
ASSERT_NE(nullptr, pattern_image);
if (ComputeSsim(*capture_image, *pattern_image) >
kContentTestSsimThreshold) {
return;
}
}
std::stringstream ss;
ss << "/tmp/corruption_test_0x" << std::hex << format_ << "_" << std::dec
<< width_ << "x" << height_;
capture_image->SaveToFile(ss.str());
ADD_FAILURE() << "The frame content is corrupted";
}
TEST_P(Camera3FrameContentTest, DetectGreenLine) {
auto test_pattern_modes = GetAvailableColorBarsTestPatternModes();
ASSERT_FALSE(test_pattern_modes.empty())
<< "Failed to get sensor available test pattern modes";
cam_device_.AddOutputStream(format_, width_, height_,
CAMERA3_STREAM_ROTATION_0);
ASSERT_EQ(0, cam_device_.ConfigureStreams(nullptr))
<< "Configuring stream fails";
ScopedCameraMetadata metadata(clone_camera_metadata(
cam_device_.ConstructDefaultRequestSettings(CAMERA3_TEMPLATE_PREVIEW)));
UpdateMetadata(ANDROID_SENSOR_TEST_PATTERN_MODE, test_pattern_modes.data(), 1,
&metadata);
ASSERT_EQ(0, CreateCaptureRequestByMetadata(metadata, nullptr))
<< "Creating capture request fails";
struct timespec timeout;
GetTimeOfTimeout(kDefaultTimeoutMs, &timeout);
WaitShutterAndCaptureResult(timeout);
ASSERT_NE(nullptr, buffer_handle_) << "Failed to get frame buffer";
auto argb_image = ConvertToImage(std::move(buffer_handle_), width_, height_,
ImageFormat::IMAGE_FORMAT_ARGB);
ASSERT_NE(nullptr, argb_image);
auto IsGreenPixel = [](const uint8_t* pixel) {
const uint8_t kRedOrBlueUpperLimit = 50;
const uint8_t kGreenLowerLimit = 100;
const uint32_t kRedOffset = 0;
const uint32_t kGreenOffset = 1;
const uint32_t kBlueOffset = 2;
return *(pixel + kRedOffset)<kRedOrBlueUpperLimit&&*(pixel + kGreenOffset)>
kGreenLowerLimit &&
*(pixel + kBlueOffset) < kRedOrBlueUpperLimit;
};
auto IsBottomLineGreen = [&](const ScopedImage& argb_image) {
uint8_t* pixel_of_last_line =
argb_image->planes[0].addr +
argb_image->planes[0].stride * (argb_image->height - 1);
for (size_t i = 0; i < argb_image->planes[0].stride;
i += kARGBPixelWidth, pixel_of_last_line += kARGBPixelWidth) {
if (!IsGreenPixel(pixel_of_last_line)) {
return false;
}
}
return true;
};
EXPECT_FALSE(IsBottomLineGreen(argb_image))
<< "Green line at the bottom of captured frame";
auto IsRightMostLineGreen = [&](const ScopedImage& argb_image) {
uint8_t* last_pixel_of_line = argb_image->planes[0].addr +
argb_image->planes[0].stride -
kARGBPixelWidth;
for (size_t i = 0; i < argb_image->height;
i++, last_pixel_of_line += argb_image->planes[0].stride) {
if (!IsGreenPixel(last_pixel_of_line)) {
return false;
}
}
return true;
};
EXPECT_FALSE(IsRightMostLineGreen(argb_image))
<< "Green line at the rightmost of captured frame";
}
// Test parameters:
// - Camera ID, frame format, resolution width, resolution height
// - Rotation degrees
class Camera3PortraitRotationTest
: public Camera3FrameFixture,
public ::testing::WithParamInterface<
std::tuple<std::tuple<int32_t, int32_t, int32_t, int32_t>, int32_t>> {
public:
const double kPortraitTestSsimThreshold = 0.75;
Camera3PortraitRotationTest()
: Camera3FrameFixture(std::get<0>(std::get<0>(GetParam()))),
format_(std::get<1>(std::get<0>(GetParam()))),
width_(std::get<2>(std::get<0>(GetParam()))),
height_(std::get<3>(std::get<0>(GetParam()))),
rotation_degrees_(std::get<1>(GetParam())),
save_images_(base::CommandLine::ForCurrentProcess()->HasSwitch(
"save_portrait_test_images")) {}
protected:
void ProcessResultMetadataOutputBuffers(
uint32_t frame_number,
ScopedCameraMetadata metadata,
std::vector<cros::ScopedBufferHandle> buffers) override;
// Rotate |in_buffer| 180 degrees to |out_buffer|.
int Rotate180(const Image& in_buffer, Image* out_buffer);
int32_t format_;
int32_t width_;
int32_t height_;
int32_t rotation_degrees_;
bool save_images_;
cros::ScopedBufferHandle buffer_handle_;
};
void Camera3PortraitRotationTest::ProcessResultMetadataOutputBuffers(
uint32_t frame_number,
ScopedCameraMetadata metadata,
std::vector<cros::ScopedBufferHandle> buffers) {
ASSERT_EQ(nullptr, buffer_handle_);
buffer_handle_ = std::move(buffers.front());
}
int Camera3PortraitRotationTest::Rotate180(const Image& in_buffer,
Image* out_buffer) {
if (in_buffer.format != ImageFormat::IMAGE_FORMAT_I420 ||
out_buffer->format != ImageFormat::IMAGE_FORMAT_I420 ||
in_buffer.width != out_buffer->width ||
in_buffer.height != out_buffer->height) {
return -EINVAL;
}
return libyuv::I420Rotate(
in_buffer.planes[0].addr, in_buffer.planes[0].stride,
in_buffer.planes[1].addr, in_buffer.planes[1].stride,
in_buffer.planes[2].addr, in_buffer.planes[2].stride,
out_buffer->planes[0].addr, out_buffer->planes[0].stride,
out_buffer->planes[1].addr, out_buffer->planes[1].stride,
out_buffer->planes[2].addr, out_buffer->planes[2].stride, in_buffer.width,
in_buffer.height, libyuv::RotationMode::kRotate180);
}
TEST_P(Camera3PortraitRotationTest, GetFrame) {
auto test_pattern_modes = GetAvailableColorBarsTestPatternModes();
ASSERT_FALSE(test_pattern_modes.empty())
<< "Failed to get sensor available test pattern modes";
if (cam_device_.GetStaticInfo()->IsFormatAvailable(format_)) {
VLOGF(1) << "Device " << cam_id_;
VLOGF(1) << "Format 0x" << std::hex << format_;
VLOGF(1) << "Resolution " << width_ << "x" << height_;
VLOGF(1) << "Rotation " << rotation_degrees_;
cam_device_.AddOutputStream(format_, width_, height_,
CAMERA3_STREAM_ROTATION_0);
ASSERT_EQ(0, cam_device_.ConfigureStreams(nullptr))
<< "Configuring stream fails";
// Get original pattern
ScopedCameraMetadata metadata(clone_camera_metadata(
cam_device_.ConstructDefaultRequestSettings(CAMERA3_TEMPLATE_PREVIEW)));
UpdateMetadata(ANDROID_SENSOR_TEST_PATTERN_MODE, test_pattern_modes.data(),
1, &metadata);
ASSERT_EQ(0, CreateCaptureRequestByMetadata(metadata, nullptr))
<< "Creating capture request fails";
struct timespec timeout;
GetTimeOfTimeout(kDefaultTimeoutMs, &timeout);
WaitShutterAndCaptureResult(timeout);
ASSERT_NE(nullptr, buffer_handle_) << "Failed to get original frame buffer";
auto orig_i420_image =
ConvertToImage(std::move(buffer_handle_), width_, height_,
ImageFormat::IMAGE_FORMAT_I420);
ASSERT_NE(nullptr, orig_i420_image);
auto SaveImage = [this](const Image& image, const std::string suffix) {
std::stringstream ss;
ss << "/tmp/portrait_test_0x" << std::hex << format_ << "_" << std::dec
<< width_ << "x" << height_ << "_" << rotation_degrees_ << suffix;
EXPECT_EQ(0, image.SaveToFile(ss.str()));
};
if (save_images_) {
SaveImage(*orig_i420_image, "_orig");
}
// Re-configure streams with rotation
camera3_stream_rotation_t crop_rotate_scale_degrees =
CAMERA3_STREAM_ROTATION_0;
switch (rotation_degrees_) {
case 90:
crop_rotate_scale_degrees = CAMERA3_STREAM_ROTATION_90;
break;
case 270:
crop_rotate_scale_degrees = CAMERA3_STREAM_ROTATION_270;
break;
default:
FAIL() << "Invalid rotation degree: " << rotation_degrees_;
}
cam_device_.AddOutputStream(format_, width_, height_,
crop_rotate_scale_degrees);
ASSERT_EQ(0, cam_device_.ConfigureStreams(nullptr))
<< "Configuring stream fails";
ASSERT_EQ(0, CreateCaptureRequestByMetadata(metadata, nullptr))
<< "Creating capture request fails";
// Verify the original pattern is asymmetric
ScopedImage orig_rotated_i420_image(
new Image(width_, height_, ImageFormat::IMAGE_FORMAT_I420));
ASSERT_EQ(0, Rotate180(*orig_i420_image, orig_rotated_i420_image.get()));
ASSERT_LE(ComputeSsim(*orig_i420_image, *orig_rotated_i420_image),
kPortraitTestSsimThreshold)
<< "Test pattern appears to be symmetric";
// Generate software crop-rotate-scaled pattern
ScopedImage sw_portrait_i420_image = CropRotateScale(
std::move(orig_i420_image), rotation_degrees_, width_, height_);
ASSERT_NE(nullptr, sw_portrait_i420_image);
if (save_images_) {
SaveImage(*sw_portrait_i420_image, "_swconv");
}
GetTimeOfTimeout(kDefaultTimeoutMs, &timeout);
WaitShutterAndCaptureResult(timeout);
ASSERT_NE(nullptr, buffer_handle_) << "Failed to get portrait frame buffer";
auto portrait_i420_image =
ConvertToImage(std::move(buffer_handle_), width_, height_,
ImageFormat::IMAGE_FORMAT_I420);
ASSERT_NE(nullptr, portrait_i420_image);
if (save_images_) {
SaveImage(*portrait_i420_image, "_conv");
}
// Compare similarity of crop-rotate-scaled patterns
ASSERT_GT(ComputeSsim(*sw_portrait_i420_image, *portrait_i420_image),
kPortraitTestSsimThreshold)
<< "SSIM value is lower than threshold";
}
}
// Test parameters:
// - Camera ID
class Camera3PortraitModeTest : public Camera3FrameFixture,
public ::testing::WithParamInterface<int32_t> {
public:
const uint32_t kPortraitModeTimeoutMs = 10000;
Camera3PortraitModeTest() : Camera3FrameFixture(GetParam()) {}
protected:
void ProcessResultMetadataOutputBuffers(
uint32_t frame_number,
ScopedCameraMetadata metadata,
std::vector<cros::ScopedBufferHandle> buffers) override;
// Get portrait mode vendor tags; return false if the tag is not listed in
// available request keys.
bool GetPortraitModeVendorTags(uint32_t* portrait_mode_vendor_tag,
uint32_t* segmentation_result_vendor_tag);
bool LoadTestImage();
void TakePortraitModePictureTest(bool has_face);
ScopedCameraMetadata result_metadata_;
cros::ScopedBufferHandle yuv_buffer_handle_;
cros::ScopedBufferHandle blob_buffer_handle_;
};
void Camera3PortraitModeTest::ProcessResultMetadataOutputBuffers(
uint32_t frame_number,
ScopedCameraMetadata metadata,
std::vector<cros::ScopedBufferHandle> buffers) {
result_metadata_ = std::move(metadata);
for (auto& buffer : buffers) {
auto* native_handle = camera_buffer_handle_t::FromBufferHandle(*buffer);
if (native_handle->hal_pixel_format == HAL_PIXEL_FORMAT_BLOB) {
blob_buffer_handle_ = std::move(buffer);
} else {
yuv_buffer_handle_ = std::move(buffer);
}
}
}
bool Camera3PortraitModeTest::GetPortraitModeVendorTags(
uint32_t* portrait_mode_vendor_tag,
uint32_t* segmentation_result_vendor_tag) {
if (!cam_module_.GetVendorTagByName("com.google.effect.portraitMode",
portrait_mode_vendor_tag) ||
!cam_module_.GetVendorTagByName(
"com.google.effect.portraitModeSegmentationResult",
segmentation_result_vendor_tag)) {
return false;
}
auto available_request_keys =
cam_device_.GetStaticInfo()->GetAvailableRequestKeys();
if (available_request_keys.find(*portrait_mode_vendor_tag) ==
available_request_keys.end()) {
return false;
}
return true;
}
bool Camera3PortraitModeTest::LoadTestImage() {
auto* gralloc = Camera3TestGralloc::GetInstance();
uint32_t width = gralloc->GetWidth(*yuv_buffer_handle_);
uint32_t height = gralloc->GetHeight(*yuv_buffer_handle_);
base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
base::FilePath portrait_mode_test_data_path =
cmd_line->GetSwitchValuePath("portrait_mode_test_data");
if (portrait_mode_test_data_path.empty()) {
LOGF(ERROR) << "Failed to find test data. Did you specify "
"'--portrait_mode_test_data='?";
return false;
}
int64_t file_size = 0;
if (!base::GetFileSize(portrait_mode_test_data_path, &file_size) ||
file_size <= 0) {
LOGF(ERROR) << "Failed get file size";
return false;
}
auto test_image = std::vector<char>(file_size);
int test_image_size = base::ReadFile(portrait_mode_test_data_path,
test_image.data(), file_size);
if (test_image_size < file_size) {
LOGF(ERROR) << "Failed to read test image "
<< portrait_mode_test_data_path.value();
return false;
}
libyuv::MJpegDecoder decoder;
if (!decoder.LoadFrame(reinterpret_cast<uint8_t*>(test_image.data()),
test_image_size)) {
LOGF(ERROR) << "Failed to parse test image";
return false;
}
ScopedImage i420_image(new Image(decoder.GetWidth(), decoder.GetHeight(),
ImageFormat::IMAGE_FORMAT_I420));
if (libyuv::MJPGToI420(
reinterpret_cast<uint8_t*>(test_image.data()), test_image_size,
i420_image->planes[0].addr, i420_image->planes[0].stride,
i420_image->planes[1].addr, i420_image->planes[1].stride,
i420_image->planes[2].addr, i420_image->planes[2].stride,
decoder.GetWidth(), decoder.GetHeight(), decoder.GetWidth(),
decoder.GetHeight()) != 0) {
LOGF(ERROR) << "Failed to convert test image to I420";
return false;
}
i420_image = CropRotateScale(std::move(i420_image), 0, width, height);
if (!i420_image) {
LOGF(ERROR) << "Failed to crop, rotate and scale test image";
return false;
}
struct android_ycbcr ycbcr_info;
if (gralloc->LockYCbCr(*yuv_buffer_handle_, 0, 0, 0, width, height,
&ycbcr_info) != 0) {
LOGF(ERROR) << "Failed to lock YUV buffer";
return false;
}
uint32_t v4l2_format =
cros::CameraBufferManager::GetV4L2PixelFormat(*yuv_buffer_handle_);
bool res = false;
switch (v4l2_format) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV12M:
if (libyuv::I420ToNV12(
i420_image->planes[0].addr, i420_image->planes[0].stride,
i420_image->planes[1].addr, i420_image->planes[1].stride,
i420_image->planes[2].addr, i420_image->planes[2].stride,
static_cast<uint8_t*>(ycbcr_info.y), ycbcr_info.ystride,
static_cast<uint8_t*>(ycbcr_info.cb), ycbcr_info.cstride, width,
height) == 0) {
res = true;
} else {
LOGF(ERROR) << "Failed to convert test image to NV12";
}
break;
default:
LOGF(ERROR) << "Unsupported format " << FormatToString(v4l2_format);
}
gralloc->Unlock(*yuv_buffer_handle_);
return res;
}
static bool FillImageWithBlackColor(buffer_handle_t buffer) {
auto* gralloc = Camera3TestGralloc::GetInstance();
uint32_t width = gralloc->GetWidth(buffer);
uint32_t height = gralloc->GetHeight(buffer);
struct android_ycbcr ycbcr_info;
if (gralloc->LockYCbCr(buffer, 0, 0, 0, width, height, &ycbcr_info) != 0) {
LOGF(ERROR) << "Failed to lock YUV buffer";
return false;
}
uint32_t v4l2_format = cros::CameraBufferManager::GetV4L2PixelFormat(buffer);
bool res = false;
switch (v4l2_format) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV12M:
std::fill(
static_cast<uint8_t*>(ycbcr_info.y),
static_cast<uint8_t*>(ycbcr_info.y) + ycbcr_info.ystride * height, 0);
std::fill(static_cast<uint8_t*>(ycbcr_info.cb),
static_cast<uint8_t*>(ycbcr_info.cb) +
ycbcr_info.cstride * height / 2,
0x80U);
res = true;
break;
default:
LOGF(ERROR) << "Unsupported format " << FormatToString(v4l2_format);
}
gralloc->Unlock(buffer);
return res;
}
void Camera3PortraitModeTest::TakePortraitModePictureTest(bool has_face) {
uint32_t portrait_mode_vendor_tag;
uint32_t segmentation_result_vendor_tag;
if (!GetPortraitModeVendorTags(&portrait_mode_vendor_tag,
&segmentation_result_vendor_tag)) {
GTEST_SKIP();
}
auto out_resolutions =
cam_device_.GetStaticInfo()->GetSortedOutputResolutions(
HAL_PIXEL_FORMAT_BLOB);
ASSERT_FALSE(out_resolutions.empty())
<< "Failed to get JPEG format output resolutions";
ResolutionInfo resolution = out_resolutions.back();
auto in_resolutions = cam_device_.GetStaticInfo()->GetSortedOutputResolutions(
HAL_PIXEL_FORMAT_YCbCr_420_888);
ASSERT_TRUE(std::binary_search(in_resolutions.begin(), in_resolutions.end(),
resolution))
<< "Failed to find " << resolution << " in input YUV resolutions";
cam_device_.AddInputStream(HAL_PIXEL_FORMAT_YCbCr_420_888, resolution.Width(),
resolution.Height());
cam_device_.AddOutputStream(HAL_PIXEL_FORMAT_YCbCr_420_888,
resolution.Width(), resolution.Height(),
CAMERA3_STREAM_ROTATION_0);
cam_device_.AddOutputStream(HAL_PIXEL_FORMAT_BLOB, resolution.Width(),
resolution.Height(), CAMERA3_STREAM_ROTATION_0);
std::vector<const camera3_stream_t*> streams;
ASSERT_EQ(0, cam_device_.ConfigureStreams(&streams))
<< "Configuring stream fails";
ASSERT_EQ(0, CreateCaptureRequestByTemplate(CAMERA3_TEMPLATE_STILL_CAPTURE,
nullptr))
<< "Creating capture request fails";
struct timespec timeout;
GetTimeOfTimeout(kDefaultTimeoutMs, &timeout);
WaitShutterAndCaptureResult(timeout);
ASSERT_NE(nullptr, yuv_buffer_handle_) << "Failed to get YUV output buffer";
ASSERT_NE(nullptr, blob_buffer_handle_) << "Failed to get BLOB output buffer";
std::vector<uint8_t> enable_portrait_mode(1, 1);
UpdateMetadata(portrait_mode_vendor_tag, enable_portrait_mode.data(), 1,
&result_metadata_);
auto GetStream = [&streams](int32_t format, bool is_output) {
auto dir = is_output ? CAMERA3_STREAM_OUTPUT : CAMERA3_STREAM_INPUT;
auto it = std::find_if(
streams.begin(), streams.end(), [&](const camera3_stream_t* stream) {
return stream->format == format && stream->stream_type == dir;
});
return it == streams.end() ? nullptr : *it;
};
// prepare input_buffer
if (has_face) {
ASSERT_TRUE(LoadTestImage());
} else {
ASSERT_TRUE(FillImageWithBlackColor(*yuv_buffer_handle_));
}
auto in_buffer = std::move(yuv_buffer_handle_);
auto in_stream = GetStream(HAL_PIXEL_FORMAT_YCbCr_420_888, false);
ASSERT_NE(in_stream, nullptr);
camera3_stream_buffer_t input_buffer = {
.stream = const_cast<camera3_stream_t*>(in_stream),
.buffer = in_buffer.get(),
.status = CAMERA3_BUFFER_STATUS_OK,
.acquire_fence = -1,
.release_fence = -1};
// prepare output_buffer
std::vector<camera3_stream_buffer_t> output_buffers;
auto out_stream = GetStream(HAL_PIXEL_FORMAT_BLOB, true);
ASSERT_NE(out_stream, nullptr);
ASSERT_EQ(0, cam_device_.AllocateOutputBuffersByStreams({out_stream},
&output_buffers))
<< "Failed to allocate buffers for capture request";
camera3_capture_request_t capture_request = {
.frame_number = UINT32_MAX,
.settings = result_metadata_.get(),
.input_buffer = &input_buffer,
.num_output_buffers = static_cast<uint32_t>(output_buffers.size()),
.output_buffers = output_buffers.data(),
.num_physcam_settings = 0};
// Process capture request
ASSERT_EQ(0, cam_device_.ProcessCaptureRequest(&capture_request))
<< "Creating capture request fails";
Camera3PerfLog::GetInstance()->UpdateFrameEvent(
cam_id_, capture_request.frame_number, FrameEvent::PORTRAIT_MODE_STARTED,
base::TimeTicks::Now());
GetTimeOfTimeout(kPortraitModeTimeoutMs, &timeout);
WaitShutterAndCaptureResult(timeout);
ASSERT_NE(nullptr, blob_buffer_handle_) << "Failed to get BLOB output buffer";
Camera3PerfLog::GetInstance()->UpdateFrameEvent(
cam_id_, capture_request.frame_number, FrameEvent::PORTRAIT_MODE_ENDED,
base::TimeTicks::Now());
camera_metadata_ro_entry_t entry = {};
ASSERT_EQ(
0, find_camera_metadata_ro_entry(result_metadata_.get(),
segmentation_result_vendor_tag, &entry))
<< "Fail to find "
<< get_camera_metadata_tag_name(segmentation_result_vendor_tag)
<< " in result metadata";
ASSERT_EQ(1, entry.count);
if (has_face) {
ASSERT_EQ(0, entry.data.u8[0]) << "Portrait mode failed";
} else {
ASSERT_EQ(1, entry.data.u8[0])
<< "Portrait mode should have failed with no face in the picture";
}
}
TEST_P(Camera3PortraitModeTest, BasicOperation) {
TakePortraitModePictureTest(true);
}
TEST_P(Camera3PortraitModeTest, NoFace) {
TakePortraitModePictureTest(false);
}
INSTANTIATE_TEST_SUITE_P(
Camera3FrameTest,
Camera3SingleFrameTest,
::testing::Combine(
::testing::ValuesIn(Camera3Module().GetTestCameraIds()),
::testing::Values(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
HAL_PIXEL_FORMAT_YCbCr_420_888,
HAL_PIXEL_FORMAT_YCrCb_420_SP,
HAL_PIXEL_FORMAT_BLOB,
HAL_PIXEL_FORMAT_YV12,
HAL_PIXEL_FORMAT_Y8,
HAL_PIXEL_FORMAT_Y16,
HAL_PIXEL_FORMAT_RAW16),
::testing::Values(CAMERA3_TEMPLATE_PREVIEW,
CAMERA3_TEMPLATE_STILL_CAPTURE,
CAMERA3_TEMPLATE_VIDEO_RECORD,
CAMERA3_TEMPLATE_VIDEO_SNAPSHOT,
CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG,
CAMERA3_TEMPLATE_MANUAL),
::testing::Bool()));
INSTANTIATE_TEST_SUITE_P(
Camera3FrameTest,
Camera3MultiFrameTest,
::testing::Combine(::testing::ValuesIn(Camera3Module().GetTestCameraIds()),
::testing::Values(CAMERA3_TEMPLATE_PREVIEW,
CAMERA3_TEMPLATE_STILL_CAPTURE,
CAMERA3_TEMPLATE_VIDEO_RECORD,
CAMERA3_TEMPLATE_VIDEO_SNAPSHOT,
CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG,
CAMERA3_TEMPLATE_MANUAL),
::testing::Range(1, 10)));
INSTANTIATE_TEST_SUITE_P(
Camera3FrameTest,
Camera3MixedTemplateMultiFrameTest,
::testing::ValuesIn(Camera3Module().GetTestCameraIds()));
INSTANTIATE_TEST_SUITE_P(
Camera3FrameTest,
Camera3FlushRequestsTest,
::testing::Combine(::testing::ValuesIn(Camera3Module().GetTestCameraIds()),
::testing::Values(CAMERA3_TEMPLATE_PREVIEW,
CAMERA3_TEMPLATE_STILL_CAPTURE,
CAMERA3_TEMPLATE_VIDEO_RECORD,
CAMERA3_TEMPLATE_VIDEO_SNAPSHOT,
CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG,
CAMERA3_TEMPLATE_MANUAL),
::testing::Values(10)));
INSTANTIATE_TEST_SUITE_P(
Camera3FrameTest,
Camera3MultiStreamFrameTest,
::testing::ValuesIn(Camera3Module().GetTestCameraIds()));
INSTANTIATE_TEST_SUITE_P(
Camera3FrameTest,
Camera3InvalidRequestTest,
::testing::ValuesIn(Camera3Module().GetTestCameraIds()));
INSTANTIATE_TEST_SUITE_P(
Camera3FrameTest,
Camera3SimpleCaptureFrames,
::testing::Combine(::testing::ValuesIn(Camera3Module().GetTestCameraIds()),
::testing::Values(10)));
INSTANTIATE_TEST_SUITE_P(
Camera3FrameTest,
Camera3ResultTimestampsTest,
::testing::ValuesIn(Camera3Module().GetTestCameraIds()));
INSTANTIATE_TEST_SUITE_P(
Camera3FrameTest,
Camera3InvalidBufferTest,
::testing::ValuesIn(Camera3Module().GetTestCameraIds()));
static std::vector<std::tuple<int32_t, int32_t, int32_t, int32_t>>
IterateCameraIdFormatResolution() {
std::vector<std::tuple<int32_t, int32_t, int32_t, int32_t>> result;
auto cam_ids = Camera3Module().GetTestCameraIds();
auto formats =
std::vector<int>({HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
HAL_PIXEL_FORMAT_YCbCr_420_888, HAL_PIXEL_FORMAT_BLOB});
for (const auto& cam_id : cam_ids) {
for (const auto& format : formats) {
auto resolutions =
Camera3Module().GetSortedOutputResolutions(cam_id, format);
for (const auto& resolution : resolutions) {
result.emplace_back(cam_id, format, resolution.Width(),
resolution.Height());
}
}
}
return result;
}
INSTANTIATE_TEST_SUITE_P(
Camera3FrameTest,
Camera3FrameContentTest,
::testing::ValuesIn(IterateCameraIdFormatResolution()));
INSTANTIATE_TEST_SUITE_P(
Camera3FrameTest,
Camera3PortraitRotationTest,
::testing::Combine(::testing::ValuesIn(IterateCameraIdFormatResolution()),
::testing::Values(90, 270)));
INSTANTIATE_TEST_SUITE_P(
Camera3FrameTest,
Camera3PortraitModeTest,
::testing::ValuesIn(Camera3Module().GetTestCameraIds()));
} // namespace camera3_test