Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c40bae7d authored by Jayant Chowdhary's avatar Jayant Chowdhary Committed by Android (Google) Code Review
Browse files

Merge "VTS test for previewStabilization"

parents d1d52fac bf39d4db
Loading
Loading
Loading
Loading
+265 −10
Original line number Diff line number Diff line
@@ -907,7 +907,7 @@ public:

    static Status getMandatoryConcurrentStreams(const camera_metadata_t* staticMeta,
                                                std::vector<AvailableStream>* outputStreams);

    static bool supportsPreviewStabilization(const std::string& name, sp<ICameraProvider> provider);
    static Status getJpegBufferSize(camera_metadata_t *staticMeta,
            uint32_t* outBufSize);
    static Status isConstrainedModeAvailable(camera_metadata_t *staticMeta);
@@ -955,6 +955,9 @@ public:

    void processCaptureRequestInternal(uint64_t bufferusage, RequestTemplate reqTemplate,
                                       bool useSecureOnlyCameras);
    void processPreviewStabilizationCaptureRequestInternal(
            bool previewStabilizationOn,
            /*inout*/ std::unordered_map<std::string, nsecs_t>& cameraDeviceToTimeLag);

    // Used by switchToOffline where a new result queue is created for offline reqs
    void updateInflightResultQueue(std::shared_ptr<ResultMetadataQueue> resultQueue);
@@ -1008,7 +1011,11 @@ protected:

        // Buffers are added by process_capture_result when output buffers
        // return from HAL but framework.
        ::android::Vector<StreamBuffer> resultOutputBuffers;
        struct StreamBufferAndTimestamp {
            StreamBuffer buffer;
            nsecs_t timeStamp;
        };
        ::android::Vector<StreamBufferAndTimestamp> resultOutputBuffers;

        std::unordered_set<std::string> expectedPhysicalResults;

@@ -1423,8 +1430,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<android::Fence> 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;
@@ -4926,7 +4950,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
@@ -4986,7 +5010,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<std::string, nsecs_t> 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<std::string, nsecs_t>& cameraDeviceToTimeLag) {
    hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
    AvailableStream streamThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
                                       static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
    uint64_t bufferId = 1;
    uint32_t frameNumber = 1;
    ::android::hardware::hidl_vec<uint8_t> 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<ICameraDeviceSession> session;
        sp<DeviceCb> 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<ResultMetadataQueue> resultQueue;
        auto resultQueueRet =
                session->getCaptureResultMetadataQueue([&resultQueue](const auto& descriptor) {
                    resultQueue = std::make_shared<ResultMetadataQueue>(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<void> 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<const camera_metadata_t*>(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<StreamBuffer> outputBuffers = {outputBuffer};
        StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr};
        CaptureRequest request = {frameNumber, 0 /* fmqSettingsSize */, settings, emptyInputBuffer,
                                  outputBuffers};

        {
            std::unique_lock<std::mutex> l(mLock);
            mInflightMap.clear();
            mInflightMap.add(frameNumber, &inflightReq);
        }

        Status status = Status::INTERNAL_ERROR;
        uint32_t numRequestProcessed = 0;
        hidl_vec<BufferCache> cachesToRemove;
        Return<void> 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<std::mutex> 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) {
@@ -5567,7 +5781,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(
@@ -5851,7 +6065,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());
        }

@@ -6047,7 +6261,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:
@@ -7685,6 +7899,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<ICameraProvider> provider) {
    Return<void> ret;
    sp<ICameraDevice> 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<const camera_metadata_t*>(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<ICameraProvider> provider,