Camera: Add camera capture request VTS tests

Use single camera capture requests to verify basic
'processCaptureRequest' functionality:
- 'processCaptureRequestSinglePreview' will generate
a valid preview capture request. The result needs to
include both valid stream Id and frame number.
- 'processCaptureRequestInvalidSinglePreview' will
omit the settings from the first capture request. Hal
should handle this by returning an appropriate error.
- 'processCaptureRequestInvalidSingleSnapshot' will
have a valid blob request but no valid output buffers.
Hal should again return appropriate error in this case.

BUG: 32022758
Test: compile and run the gtest binary on device
Change-Id: I021dd150b12d4be39fae47e13ba82d3db105bfa3
This commit is contained in:
Emilian Peev
2017-02-09 16:09:36 +00:00
parent 378f8aaec6
commit 56c2e79dd1
2 changed files with 370 additions and 1 deletions

View File

@@ -26,7 +26,8 @@ cc_test {
"libutils",
"android.hardware.camera.provider@2.4",
"android.hardware.camera.device@3.2",
"libcamera_metadata"
"libcamera_metadata",
"libui"
],
static_libs: ["libgtest"],
cflags: [

View File

@@ -18,11 +18,17 @@
#include <android/hardware/camera/provider/2.4/ICameraProvider.h>
#include <android/hardware/camera/device/3.2/ICameraDevice.h>
#include <android/log.h>
#include <ui/GraphicBuffer.h>
#include <gtest/gtest.h>
#include <regex>
#include "system/camera_metadata.h"
#include <hardware/gralloc.h>
#include <hardware/gralloc1.h>
#include <unordered_map>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <inttypes.h>
using ::android::hardware::Return;
using ::android::hardware::Void;
@@ -30,6 +36,7 @@ using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::sp;
using ::android::GraphicBuffer;
using ::android::hardware::graphics::common::V1_0::PixelFormat;
using ::android::hardware::camera::common::V1_0::Status;
using ::android::hardware::camera::common::V1_0::CameraDeviceStatus;
@@ -50,12 +57,15 @@ using ::android::hardware::camera::device::V3_2::StreamConfiguration;
using ::android::hardware::camera::device::V3_2::StreamConfigurationMode;
using ::android::hardware::camera::device::V3_2::CameraMetadata;
using ::android::hardware::camera::device::V3_2::HalStreamConfiguration;
using ::android::hardware::camera::device::V3_2::BufferStatus;
using ::android::hardware::camera::device::V3_2::StreamBuffer;
#define CAMERA_PASSTHROUGH_SERVICE_NAME "legacy/0"
#define MAX_PREVIEW_WIDTH 1920
#define MAX_PREVIEW_HEIGHT 1080
#define MAX_VIDEO_WIDTH 4096
#define MAX_VIDEO_HEIGHT 2160
#define STREAM_BUFFER_TIMEOUT 3 // sec.
struct AvailableStream {
int32_t width;
@@ -152,6 +162,15 @@ public:
}
};
struct DeviceCb : public ICameraDeviceCallback {
DeviceCb(CameraHidlTest *parent) : mParent(parent) {}
Return<void> processCaptureResult(const CaptureResult& result) override;
Return<void> notify(const NotifyMsg& msg) override;
private:
CameraHidlTest *mParent; // Parent object
};
static Status getAvailableOutputStreams(camera_metadata_t *staticMeta,
std::vector<AvailableStream> &outputStreams,
AvailableStream *threshold = nullptr);
@@ -164,8 +183,49 @@ public:
static Status findLargestSize(
const std::vector<AvailableStream> &streamSizes,
int32_t format, AvailableStream &result);
protected:
std::mutex mLock; // Synchronize access to member variables
std::condition_variable mResultCondition; // Condition variable for incoming results
uint32_t mResultFrameNumber; // Expected result frame number
std::vector<StreamBuffer> mResultBuffers; // Holds stream buffers from capture result
};
Return<void> CameraHidlTest::DeviceCb::processCaptureResult(
const CaptureResult& result) {
if (nullptr == mParent) {
return Void();
}
std::unique_lock<std::mutex> l(mParent->mLock);
if(mParent->mResultFrameNumber != result.frameNumber) {
ALOGE("%s: Unexpected frame number! Expected: %u received: %u",
__func__, mParent->mResultFrameNumber, result.frameNumber);
ADD_FAILURE();
}
size_t resultLength = result.outputBuffers.size();
for (size_t i = 0; i < resultLength; i++) {
mParent->mResultBuffers.push_back(result.outputBuffers[i]);
}
// TODO(epeev): Handle partial results in case client supports them and
// verify the result against request settings.
l.unlock();
mParent->mResultCondition.notify_one();
return Void();
}
Return<void> CameraHidlTest::DeviceCb::notify(
const NotifyMsg& /*msg*/) {
// TODO(epeev): Pending implementation.
ALOGI("notify callback");
return Void();
}
hidl_vec<hidl_string> CameraHidlTest::getCameraDeviceNames() {
CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
hidl_vec<hidl_string> cameraDeviceNames;
@@ -1066,6 +1126,314 @@ TEST_F(CameraHidlTest, configureStreamsVideoStillOutputs) {
}
}
// Generate and verify a camera capture request
TEST_F(CameraHidlTest, processCaptureRequestPreview) {
CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
std::vector<AvailableStream> outputPreviewStreams;
AvailableStream previewThreshold = {MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT,
static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
int32_t streamId = 0;
uint64_t bufferId = 1;
uint32_t frameNumber = 1;
::android::hardware::hidl_vec<uint8_t> settings;
for (const auto& name : cameraDeviceNames) {
if (getCameraDeviceVersion(name) == CAMERA_DEVICE_API_VERSION_3_2) {
::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_2;
ALOGI("configureStreams: Testing camera device %s", name.c_str());
env->mProvider->getCameraDeviceInterface_V3_x(
name,
[&](auto status, const auto& device) {
ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
ASSERT_EQ(Status::OK, status);
ASSERT_NE(device, nullptr);
device3_2 = device;
});
sp<DeviceCb> cb = new DeviceCb(this);
sp<ICameraDeviceSession> session;
device3_2->open(
cb,
[&](auto status, const auto& newSession) {
ALOGI("device::open returns status:%d", (int)status);
ASSERT_EQ(Status::OK, status);
ASSERT_NE(newSession, nullptr);
session = newSession;
});
camera_metadata_t *staticMeta;
device3_2->getCameraCharacteristics([&] (Status s,
CameraMetadata metadata) {
ASSERT_EQ(Status::OK, s);
staticMeta =
reinterpret_cast<camera_metadata_t*>(metadata.data());
});
outputPreviewStreams.clear();
ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta,
outputPreviewStreams, &previewThreshold));
ASSERT_NE(0u, outputPreviewStreams.size());
HalStreamConfiguration halStreamConfig;
Stream previewStream = {streamId, StreamType::OUTPUT,
static_cast<uint32_t> (outputPreviewStreams[0].width),
static_cast<uint32_t> (outputPreviewStreams[0].height),
static_cast<PixelFormat> (outputPreviewStreams[0].format),
0, 0, StreamRotation::ROTATION_0};
::android::hardware::hidl_vec<Stream> streams = {previewStream};
StreamConfiguration config = {streams,
StreamConfigurationMode::NORMAL_MODE};
session->configureStreams(config, [&] (Status s,
HalStreamConfiguration halConfig) {
ASSERT_EQ(Status::OK, s);
ASSERT_EQ(1u, halConfig.streams.size());
halStreamConfig = halConfig;
});
RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
session->constructDefaultRequestSettings(reqTemplate,
[&](auto status, const auto& req) {
ASSERT_EQ(Status::OK, status);
settings = req; });
sp<GraphicBuffer> gb = new GraphicBuffer(previewStream.width,
previewStream.height,
static_cast<int32_t> (halStreamConfig.streams[0].overrideFormat),
1, halStreamConfig.streams[0].producerUsage,
halStreamConfig.streams[0].consumerUsage);
ASSERT_NE(nullptr, gb.get());
StreamBuffer outputBuffer = {halStreamConfig.streams[0].id,
bufferId, hidl_handle(gb->getNativeBuffer()->handle),
BufferStatus::OK, nullptr, nullptr};
::android::hardware::hidl_vec<StreamBuffer> outputBuffers = {
outputBuffer};
CaptureRequest request = {frameNumber, settings,
{-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr},
outputBuffers};
std::unique_lock<std::mutex> l(mLock);
mResultBuffers.clear();
mResultFrameNumber = frameNumber;
l.unlock();
ASSERT_EQ(Status::OK, session->processCaptureRequest(request));
l.lock();
while (0 == mResultBuffers.size()) {
auto timeout = std::chrono::system_clock::now() +
std::chrono::seconds(STREAM_BUFFER_TIMEOUT);
ASSERT_NE(std::cv_status::timeout,
mResultCondition.wait_until(l, timeout));
}
ASSERT_EQ(BufferStatus::OK, mResultBuffers[0].status);
ASSERT_EQ(previewStream.id, mResultBuffers[0].streamId);
request.frameNumber++;
//Empty settings should be supported after the first call
//for repeating requests.
request.settings.setToExternal(nullptr, 0, true);
mResultBuffers.clear();
mResultFrameNumber++;
l.unlock();
ASSERT_EQ(Status::OK, session->processCaptureRequest(request));
l.lock();
while (0 == mResultBuffers.size()) {
auto timeout = std::chrono::system_clock::now() +
std::chrono::seconds(STREAM_BUFFER_TIMEOUT);
ASSERT_NE(std::cv_status::timeout,
mResultCondition.wait_until(l, timeout));
}
ASSERT_EQ(BufferStatus::OK, mResultBuffers[0].status);
ASSERT_EQ(previewStream.id, mResultBuffers[0].streamId);
session->close();
}
}
}
// Test whether an incorrect capture request with missing settings will
// be reported correctly.
TEST_F(CameraHidlTest, processCaptureRequestInvalidSinglePreview) {
CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
std::vector<AvailableStream> outputPreviewStreams;
AvailableStream previewThreshold = {MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT,
static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
int32_t streamId = 0;
uint64_t bufferId = 1;
uint32_t frameNumber = 1;
::android::hardware::hidl_vec<uint8_t> settings;
for (const auto& name : cameraDeviceNames) {
if (getCameraDeviceVersion(name) == CAMERA_DEVICE_API_VERSION_3_2) {
::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_2;
ALOGI("configureStreams: Testing camera device %s", name.c_str());
env->mProvider->getCameraDeviceInterface_V3_x(
name,
[&](auto status, const auto& device) {
ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
ASSERT_EQ(Status::OK, status);
ASSERT_NE(device, nullptr);
device3_2 = device;
});
sp<DeviceCb> cb = new DeviceCb(this);
sp<ICameraDeviceSession> session;
device3_2->open(
cb,
[&](auto status, const auto& newSession) {
ALOGI("device::open returns status:%d", (int)status);
ASSERT_EQ(Status::OK, status);
ASSERT_NE(newSession, nullptr);
session = newSession;
});
camera_metadata_t *staticMeta;
device3_2->getCameraCharacteristics([&] (Status s,
CameraMetadata metadata) {
ASSERT_EQ(Status::OK, s);
staticMeta =
reinterpret_cast<camera_metadata_t*>(metadata.data());
});
outputPreviewStreams.clear();
ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta,
outputPreviewStreams, &previewThreshold));
ASSERT_NE(0u, outputPreviewStreams.size());
HalStreamConfiguration halStreamConfig;
Stream previewStream = {streamId, StreamType::OUTPUT,
static_cast<uint32_t> (outputPreviewStreams[0].width),
static_cast<uint32_t> (outputPreviewStreams[0].height),
static_cast<PixelFormat> (outputPreviewStreams[0].format),
0, 0, StreamRotation::ROTATION_0};
::android::hardware::hidl_vec<Stream> streams = {previewStream};
StreamConfiguration config = {streams,
StreamConfigurationMode::NORMAL_MODE};
session->configureStreams(config, [&] (Status s,
HalStreamConfiguration halConfig) {
ASSERT_EQ(Status::OK, s);
ASSERT_EQ(1u, halConfig.streams.size());
halStreamConfig = halConfig;
});
sp<GraphicBuffer> gb = new GraphicBuffer(previewStream.width,
previewStream.height,
static_cast<int32_t> (halStreamConfig.streams[0].overrideFormat),
1, halStreamConfig.streams[0].producerUsage,
halStreamConfig.streams[0].consumerUsage);
StreamBuffer outputBuffer = {halStreamConfig.streams[0].id,
bufferId, hidl_handle(gb->getNativeBuffer()->handle),
BufferStatus::OK, nullptr, nullptr};
::android::hardware::hidl_vec<StreamBuffer> outputBuffers = {
outputBuffer};
CaptureRequest request = {frameNumber, settings,
{-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr},
outputBuffers};
//Settings were not correctly initialized, we should fail here
ASSERT_EQ(Status::INTERNAL_ERROR,
session->processCaptureRequest(request));
session->close();
}
}
}
// Check whether an invalid capture request with missing output buffers
// will be reported correctly.
TEST_F(CameraHidlTest, processCaptureRequestInvalidSingleSnapshot) {
CameraHidlEnvironment* env = CameraHidlEnvironment::Instance();
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames();
std::vector<AvailableStream> outputBlobStreams;
AvailableStream blobThreshold = {INT32_MAX, INT32_MAX,
static_cast<int32_t>(PixelFormat::BLOB)};
int32_t streamId = 0;
uint64_t bufferId = 1;
uint32_t frameNumber = 1;
::android::hardware::hidl_vec<uint8_t> settings;
for (const auto& name : cameraDeviceNames) {
if (getCameraDeviceVersion(name) == CAMERA_DEVICE_API_VERSION_3_2) {
::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_2;
ALOGI("configureStreams: Testing camera device %s", name.c_str());
env->mProvider->getCameraDeviceInterface_V3_x(
name,
[&](auto status, const auto& device) {
ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
ASSERT_EQ(Status::OK, status);
ASSERT_NE(device, nullptr);
device3_2 = device;
});
sp<DeviceCb> cb = new DeviceCb(this);
sp<ICameraDeviceSession> session;
device3_2->open(
cb,
[&](auto status, const auto& newSession) {
ALOGI("device::open returns status:%d", (int)status);
ASSERT_EQ(Status::OK, status);
ASSERT_NE(newSession, nullptr);
session = newSession;
});
camera_metadata_t *staticMeta;
device3_2->getCameraCharacteristics([&] (Status s,
CameraMetadata metadata) {
ASSERT_EQ(Status::OK, s);
staticMeta =
reinterpret_cast<camera_metadata_t*>(metadata.data());
});
outputBlobStreams.clear();
ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta,
outputBlobStreams, &blobThreshold));
ASSERT_NE(0u, outputBlobStreams.size());
HalStreamConfiguration halStreamConfig;
Stream previewStream = {streamId, StreamType::OUTPUT,
static_cast<uint32_t> (outputBlobStreams[0].width),
static_cast<uint32_t> (outputBlobStreams[0].height),
static_cast<PixelFormat> (outputBlobStreams[0].format),
0, 0, StreamRotation::ROTATION_0};
::android::hardware::hidl_vec<Stream> streams = {previewStream};
StreamConfiguration config = {streams,
StreamConfigurationMode::NORMAL_MODE};
session->configureStreams(config, [&] (Status s,
HalStreamConfiguration halConfig) {
ASSERT_EQ(Status::OK, s);
ASSERT_EQ(1u, halConfig.streams.size());
halStreamConfig = halConfig;
});
RequestTemplate reqTemplate = RequestTemplate::STILL_CAPTURE;
session->constructDefaultRequestSettings(reqTemplate,
[&](auto status, const auto& req) {
ASSERT_EQ(Status::OK, status);
settings = req; });
StreamBuffer outputBuffer = {halStreamConfig.streams[0].id,
bufferId, hidl_handle(nullptr), BufferStatus::OK,
nullptr, nullptr};
::android::hardware::hidl_vec<StreamBuffer> outputBuffers;
CaptureRequest request = {frameNumber, settings,
{-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr},
outputBuffers};
//Output buffers are missing, we should fail here
ASSERT_EQ(Status::INTERNAL_ERROR,
session->processCaptureRequest(request));
session->close();
}
}
}
// Retrieve all valid output stream resolutions from the camera
// static characteristics.
Status CameraHidlTest::getAvailableOutputStreams(camera_metadata_t *staticMeta,