From 56c2e79dd124b1d0b8668c6d35529404fa898c6f Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Thu, 9 Feb 2017 16:09:36 +0000 Subject: [PATCH] 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 --- camera/provider/2.4/vts/functional/Android.bp | 3 +- .../VtsHalCameraProviderV2_4TargetTest.cpp | 368 ++++++++++++++++++ 2 files changed, 370 insertions(+), 1 deletion(-) diff --git a/camera/provider/2.4/vts/functional/Android.bp b/camera/provider/2.4/vts/functional/Android.bp index 731a292fc4..defe3dcd64 100644 --- a/camera/provider/2.4/vts/functional/Android.bp +++ b/camera/provider/2.4/vts/functional/Android.bp @@ -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: [ diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp index 3423b26586..e1e10f8f17 100644 --- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp +++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp @@ -18,11 +18,17 @@ #include #include #include +#include #include #include #include "system/camera_metadata.h" #include +#include #include +#include +#include +#include +#include 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 processCaptureResult(const CaptureResult& result) override; + Return notify(const NotifyMsg& msg) override; + + private: + CameraHidlTest *mParent; // Parent object + }; + static Status getAvailableOutputStreams(camera_metadata_t *staticMeta, std::vector &outputStreams, AvailableStream *threshold = nullptr); @@ -164,8 +183,49 @@ public: static Status findLargestSize( const std::vector &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 mResultBuffers; // Holds stream buffers from capture result }; +Return CameraHidlTest::DeviceCb::processCaptureResult( + const CaptureResult& result) { + if (nullptr == mParent) { + return Void(); + } + + std::unique_lock 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 CameraHidlTest::DeviceCb::notify( + const NotifyMsg& /*msg*/) { + // TODO(epeev): Pending implementation. + ALOGI("notify callback"); + return Void(); +} + hidl_vec CameraHidlTest::getCameraDeviceNames() { CameraHidlEnvironment* env = CameraHidlEnvironment::Instance(); hidl_vec 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 cameraDeviceNames = getCameraDeviceNames(); + std::vector outputPreviewStreams; + AvailableStream previewThreshold = {MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT, + static_cast(PixelFormat::IMPLEMENTATION_DEFINED)}; + int32_t streamId = 0; + uint64_t bufferId = 1; + uint32_t frameNumber = 1; + ::android::hardware::hidl_vec 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 cb = new DeviceCb(this); + sp 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(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 (outputPreviewStreams[0].width), + static_cast (outputPreviewStreams[0].height), + static_cast (outputPreviewStreams[0].format), + 0, 0, StreamRotation::ROTATION_0}; + ::android::hardware::hidl_vec 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 gb = new GraphicBuffer(previewStream.width, + previewStream.height, + static_cast (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 outputBuffers = { + outputBuffer}; + CaptureRequest request = {frameNumber, settings, + {-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr}, + outputBuffers}; + + std::unique_lock 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 cameraDeviceNames = getCameraDeviceNames(); + std::vector outputPreviewStreams; + AvailableStream previewThreshold = {MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT, + static_cast(PixelFormat::IMPLEMENTATION_DEFINED)}; + int32_t streamId = 0; + uint64_t bufferId = 1; + uint32_t frameNumber = 1; + ::android::hardware::hidl_vec 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 cb = new DeviceCb(this); + sp 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(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 (outputPreviewStreams[0].width), + static_cast (outputPreviewStreams[0].height), + static_cast (outputPreviewStreams[0].format), + 0, 0, StreamRotation::ROTATION_0}; + ::android::hardware::hidl_vec 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 gb = new GraphicBuffer(previewStream.width, + previewStream.height, + static_cast (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 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 cameraDeviceNames = getCameraDeviceNames(); + std::vector outputBlobStreams; + AvailableStream blobThreshold = {INT32_MAX, INT32_MAX, + static_cast(PixelFormat::BLOB)}; + int32_t streamId = 0; + uint64_t bufferId = 1; + uint32_t frameNumber = 1; + ::android::hardware::hidl_vec 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 cb = new DeviceCb(this); + sp 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(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 (outputBlobStreams[0].width), + static_cast (outputBlobStreams[0].height), + static_cast (outputBlobStreams[0].format), + 0, 0, StreamRotation::ROTATION_0}; + ::android::hardware::hidl_vec 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 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,