From bf39d4db34d5800ac329b1d4ff227b8b2f2957ba Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Fri, 3 Dec 2021 12:20:07 -0800 Subject: [PATCH] VTS test for previewStabilization Bug: 200197440 Test: VtsHalCameraProviderV2_4TargetTest --gtest_filter=PerInstance/CameraHidlTest.processCaptureRequestPreviewStab* Change-Id: Iba71983a69d20af605924fa6f1c0697e49d911f8 Signed-off-by: Jayant Chowdhary --- .../VtsHalCameraProviderV2_4TargetTest.cpp | 275 +++++++++++++++++- 1 file changed, 265 insertions(+), 10 deletions(-) diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp index 77974fcbe4..36b3d728d0 100644 --- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp +++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp @@ -666,7 +666,7 @@ public: void waitForBuffersReturned(); - private: + private: bool processCaptureResultLocked(const CaptureResult& results, hidl_vec physicalCameraMetadata); Return notifyHelper(const hidl_vec& msgs, @@ -906,7 +906,7 @@ public: static Status getMandatoryConcurrentStreams(const camera_metadata_t* staticMeta, std::vector* outputStreams); - + static bool supportsPreviewStabilization(const std::string& name, sp provider); static Status getJpegBufferSize(camera_metadata_t *staticMeta, uint32_t* outBufSize); static Status isConstrainedModeAvailable(camera_metadata_t *staticMeta); @@ -953,6 +953,9 @@ public: void processCaptureRequestInternal(uint64_t bufferusage, RequestTemplate reqTemplate, bool useSecureOnlyCameras); + void processPreviewStabilizationCaptureRequestInternal( + bool previewStabilizationOn, + /*inout*/ std::unordered_map& cameraDeviceToTimeLag); // Used by switchToOffline where a new result queue is created for offline reqs void updateInflightResultQueue(std::shared_ptr resultQueue); @@ -1006,7 +1009,11 @@ protected: // Buffers are added by process_capture_result when output buffers // return from HAL but framework. - ::android::Vector resultOutputBuffers; + struct StreamBufferAndTimestamp { + StreamBuffer buffer; + nsecs_t timeStamp; + }; + ::android::Vector resultOutputBuffers; std::unordered_set expectedPhysicalResults; @@ -1421,8 +1428,25 @@ bool CameraHidlTest::DeviceCb::processCaptureResultLocked(const CaptureResult& r return notify; } - request->resultOutputBuffers.appendArray(results.outputBuffers.data(), - results.outputBuffers.size()); + for (const auto& buffer : results.outputBuffers) { + // wait for the fence timestamp and store it along with the buffer + // TODO: Check if we really need the dup here + sp releaseFence = nullptr; + if (buffer.releaseFence && (buffer.releaseFence->numFds == 1) && + buffer.releaseFence->data[0] >= 0) { + releaseFence = new android::Fence(dup(buffer.releaseFence->data[0])); + } + InFlightRequest::StreamBufferAndTimestamp streamBufferAndTimestamp; + streamBufferAndTimestamp.buffer = buffer; + streamBufferAndTimestamp.timeStamp = systemTime(); + if (releaseFence && releaseFence->isValid()) { + releaseFence->wait(/*ms*/ 300); + nsecs_t releaseTime = releaseFence->getSignalTime(); + if (streamBufferAndTimestamp.timeStamp < releaseTime) + streamBufferAndTimestamp.timeStamp = releaseTime; + } + request->resultOutputBuffers.push_back(streamBufferAndTimestamp); + } // If shutter event is received notify the pending threads. if (request->shutterTimestamp != 0) { notify = true; @@ -4793,7 +4817,7 @@ void CameraHidlTest::processCaptureRequestInternal(uint64_t bufferUsage, ASSERT_FALSE(inflightReq.errorCodeValid); ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u); - ASSERT_EQ(testStream.id, inflightReq.resultOutputBuffers[0].streamId); + ASSERT_EQ(testStream.id, inflightReq.resultOutputBuffers[0].buffer.streamId); // For camera device 3.8 or newer, shutterReadoutTimestamp must be // available, and it must be >= shutterTimestamp + exposureTime, and @@ -4853,7 +4877,197 @@ void CameraHidlTest::processCaptureRequestInternal(uint64_t bufferUsage, ASSERT_FALSE(inflightReq.errorCodeValid); ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u); - ASSERT_EQ(testStream.id, inflightReq.resultOutputBuffers[0].streamId); + ASSERT_EQ(testStream.id, inflightReq.resultOutputBuffers[0].buffer.streamId); + } + + if (useHalBufManager) { + verifyBuffersReturned(session, deviceVersion, testStream.id, cb); + } + + ret = session->close(); + ASSERT_TRUE(ret.isOk()); + } +} + +TEST_P(CameraHidlTest, processCaptureRequestPreviewStabilization) { + std::unordered_map cameraDeviceToTimeLag; + processPreviewStabilizationCaptureRequestInternal(/*previewStabilizationOn*/ false, + cameraDeviceToTimeLag); + processPreviewStabilizationCaptureRequestInternal(/*previewStabilizationOn*/ true, + cameraDeviceToTimeLag); +} + +void CameraHidlTest::processPreviewStabilizationCaptureRequestInternal( + bool previewStabilizationOn, + // Used as output when preview stabilization is off, as output when its + // on. + std::unordered_map& cameraDeviceToTimeLag) { + hidl_vec cameraDeviceNames = getCameraDeviceNames(mProvider); + AvailableStream streamThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, + static_cast(PixelFormat::IMPLEMENTATION_DEFINED)}; + uint64_t bufferId = 1; + uint32_t frameNumber = 1; + ::android::hardware::hidl_vec settings; + + for (const auto& name : cameraDeviceNames) { + int deviceVersion = getCameraDeviceVersion(name, mProviderType); + if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) { + continue; + } else if (deviceVersion <= 0) { + ALOGE("%s: Unsupported device version %d", __func__, deviceVersion); + ADD_FAILURE(); + return; + } + + if (!supportsPreviewStabilization(name, mProvider)) { + ALOGI(" %s Camera device %s doesn't support preview stabilization, skipping", __func__, + name.c_str()); + continue; + } + + if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_8) { + ALOGE("%s: device version < 3.8 must not advertise preview stabilization," + " camera metadata validation will fail", + __func__); + ADD_FAILURE(); + } + + V3_2::Stream testStream; + HalStreamConfiguration halStreamConfig; + sp session; + sp cb; + bool supportsPartialResults = false; + bool useHalBufManager = false; + uint32_t partialResultCount = 0; + configureSingleStream(name, deviceVersion, mProvider, &streamThreshold, + GRALLOC1_CONSUMER_USAGE_HWCOMPOSER, RequestTemplate::PREVIEW, + &session /*out*/, &testStream /*out*/, &halStreamConfig /*out*/, + &supportsPartialResults /*out*/, &partialResultCount /*out*/, + &useHalBufManager /*out*/, &cb /*out*/); + + std::shared_ptr resultQueue; + auto resultQueueRet = + session->getCaptureResultMetadataQueue([&resultQueue](const auto& descriptor) { + resultQueue = std::make_shared(descriptor); + if (!resultQueue->isValid() || resultQueue->availableToWrite() <= 0) { + ALOGE("%s: HAL returns empty result metadata fmq," + " not use it", + __func__); + resultQueue = nullptr; + // Don't use the queue onwards. + } + }); + ASSERT_TRUE(resultQueueRet.isOk()); + + InFlightRequest inflightReq = {1, false, supportsPartialResults, partialResultCount, + resultQueue}; + + Return ret; + android::hardware::camera::common::V1_0::helper::CameraMetadata defaultSettings; + ret = session->constructDefaultRequestSettings( + RequestTemplate::PREVIEW, [&](auto status, const auto& req) { + ASSERT_EQ(Status::OK, status); + const camera_metadata_t* metadata = + reinterpret_cast(req.data()); + defaultSettings = metadata; + settings = req; + }); + ASSERT_TRUE(ret.isOk()); + android::status_t metadataRet = ::android::OK; + uint8_t videoStabilizationMode = ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF; + if (previewStabilizationOn) { + videoStabilizationMode = ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION; + metadataRet = defaultSettings.update(ANDROID_CONTROL_VIDEO_STABILIZATION_MODE, + &videoStabilizationMode, 1); + } else { + metadataRet = defaultSettings.update(ANDROID_CONTROL_VIDEO_STABILIZATION_MODE, + &videoStabilizationMode, 1); + } + ASSERT_EQ(metadataRet, ::android::OK); + hidl_handle buffer_handle; + StreamBuffer outputBuffer; + if (useHalBufManager) { + outputBuffer = {halStreamConfig.streams[0].id, + /*bufferId*/ 0, + buffer_handle, + BufferStatus::OK, + nullptr, + nullptr}; + } else { + allocateGraphicBuffer( + testStream.width, testStream.height, + /* We don't look at halStreamConfig.streams[0].consumerUsage + * since that is 0 for output streams + */ + android_convertGralloc1To0Usage(halStreamConfig.streams[0].producerUsage, + GRALLOC1_CONSUMER_USAGE_HWCOMPOSER), + halStreamConfig.streams[0].overrideFormat, &buffer_handle); + outputBuffer = {halStreamConfig.streams[0].id, + bufferId, + buffer_handle, + BufferStatus::OK, + nullptr, + nullptr}; + } + ::android::hardware::hidl_vec outputBuffers = {outputBuffer}; + StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr}; + CaptureRequest request = {frameNumber, 0 /* fmqSettingsSize */, settings, emptyInputBuffer, + outputBuffers}; + + { + std::unique_lock l(mLock); + mInflightMap.clear(); + mInflightMap.add(frameNumber, &inflightReq); + } + + Status status = Status::INTERNAL_ERROR; + uint32_t numRequestProcessed = 0; + hidl_vec cachesToRemove; + Return returnStatus = session->processCaptureRequest( + {request}, cachesToRemove, [&status, &numRequestProcessed](auto s, uint32_t n) { + status = s; + numRequestProcessed = n; + }); + ASSERT_TRUE(returnStatus.isOk()); + ASSERT_EQ(Status::OK, status); + ASSERT_EQ(numRequestProcessed, 1u); + + { + std::unique_lock l(mLock); + while (!inflightReq.errorCodeValid && + ((0 < inflightReq.numBuffersLeft) || (!inflightReq.haveResultMetadata))) { + auto timeout = std::chrono::system_clock::now() + + std::chrono::seconds(kStreamBufferTimeoutSec); + ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout)); + } + + ASSERT_FALSE(inflightReq.errorCodeValid); + ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u); + ASSERT_EQ(testStream.id, inflightReq.resultOutputBuffers[0].buffer.streamId); + ASSERT_TRUE(inflightReq.shutterReadoutTimestampValid); + nsecs_t readoutTimestamp = inflightReq.shutterReadoutTimestamp; + + if (previewStabilizationOn) { + // Here we collect the time difference between the buffer ready + // timestamp - notify readout timestamp. + // timeLag = buffer ready timestamp - notify readout timestamp. + // timeLag(previewStabilization) must be <= + // timeLag(stabilization off) + 1 frame duration. + auto it = cameraDeviceToTimeLag.find(name.c_str()); + camera_metadata_entry e; + e = inflightReq.collectedResult.find(ANDROID_SENSOR_FRAME_DURATION); + ASSERT_TRUE(e.count > 0); + nsecs_t frameDuration = e.data.i64[0]; + ASSERT_TRUE(it != cameraDeviceToTimeLag.end()); + + nsecs_t previewStabOnLagTime = + inflightReq.resultOutputBuffers[0].timeStamp - readoutTimestamp; + ASSERT_TRUE(previewStabOnLagTime <= (it->second + frameDuration)); + } else { + // Fill in the buffer ready timestamp - notify timestamp; + cameraDeviceToTimeLag[std::string(name.c_str())] = + inflightReq.resultOutputBuffers[0].timeStamp - readoutTimestamp; + } } if (useHalBufManager) { @@ -5434,7 +5648,7 @@ TEST_P(CameraHidlTest, processCaptureRequestBurstISO) { ASSERT_FALSE(inflightReqs[i].errorCodeValid); ASSERT_NE(inflightReqs[i].resultOutputBuffers.size(), 0u); - ASSERT_EQ(previewStream.id, inflightReqs[i].resultOutputBuffers[0].streamId); + ASSERT_EQ(previewStream.id, inflightReqs[i].resultOutputBuffers[0].buffer.streamId); ASSERT_FALSE(inflightReqs[i].collectedResult.isEmpty()); ASSERT_TRUE(inflightReqs[i].collectedResult.exists(ANDROID_SENSOR_SENSITIVITY)); camera_metadata_entry_t isoResult = inflightReqs[i].collectedResult.find( @@ -5718,7 +5932,7 @@ TEST_P(CameraHidlTest, switchToOffline) { ASSERT_FALSE(inflightReqs[i].errorCodeValid); ASSERT_NE(inflightReqs[i].resultOutputBuffers.size(), 0u); - ASSERT_EQ(stream.id, inflightReqs[i].resultOutputBuffers[0].streamId); + ASSERT_EQ(stream.id, inflightReqs[i].resultOutputBuffers[0].buffer.streamId); ASSERT_FALSE(inflightReqs[i].collectedResult.isEmpty()); } @@ -5914,7 +6128,7 @@ TEST_P(CameraHidlTest, flushPreviewRequest) { if (!inflightReq.errorCodeValid) { ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u); - ASSERT_EQ(previewStream.id, inflightReq.resultOutputBuffers[0].streamId); + ASSERT_EQ(previewStream.id, inflightReq.resultOutputBuffers[0].buffer.streamId); } else { switch (inflightReq.errorCode) { case ErrorCode::ERROR_REQUEST: @@ -7536,6 +7750,47 @@ void CameraHidlTest::configurePreviewStream(const std::string &name, int32_t dev previewStream, halStreamConfig, supportsPartialResults, partialResultCount, useHalBufManager, outCb, streamConfigCounter); } + +bool CameraHidlTest::supportsPreviewStabilization(const std::string& name, + sp provider) { + Return ret; + sp device3_x = nullptr; + ret = provider->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_x = device; + }); + if (!(ret.isOk())) { + ADD_FAILURE() << "Failed to get camera device interface for " << name; + } + + camera_metadata_t* staticMeta = nullptr; + ret = device3_x->getCameraCharacteristics([&](Status s, CameraMetadata metadata) { + ASSERT_EQ(Status::OK, s); + staticMeta = + clone_camera_metadata(reinterpret_cast(metadata.data())); + }); + if (!(ret.isOk())) { + ADD_FAILURE() << "Failed to get camera characteristics for " << name; + } + // Go through the characteristics and see if video stabilization modes have + // preview stabilization + camera_metadata_ro_entry entry; + + int retcode = find_camera_metadata_ro_entry( + staticMeta, ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, &entry); + if ((0 == retcode) && (entry.count > 0)) { + for (auto i = 0; i < entry.count; i++) { + if (entry.data.u8[i] == + ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION) { + return true; + } + } + } + return false; +} + // Open a device session and configure a preview stream. void CameraHidlTest::configureSingleStream( const std::string& name, int32_t deviceVersion, sp provider,