diff options
author | Igor Murashkin <iam@google.com> | 2012-12-12 12:48:27 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-12-12 12:48:27 -0800 |
commit | 459afa7fb59b0a88d7e86d5544469b1c9aea7b73 (patch) | |
tree | 3cb71038b5d3494be299dc4bda21b219987ecd15 /tests/camera2 | |
parent | 1082c0b54c21c220e975f1c714e41f9183b366d1 (diff) | |
parent | f1b9ae703393ddb461f1e9b1023d6ab59eb51701 (diff) | |
download | hardware_libhardware-459afa7fb59b0a88d7e86d5544469b1c9aea7b73.zip hardware_libhardware-459afa7fb59b0a88d7e86d5544469b1c9aea7b73.tar.gz hardware_libhardware-459afa7fb59b0a88d7e86d5544469b1c9aea7b73.tar.bz2 |
Merge "Camera2: Add a test for bursting 10 frames at a time /w manual exp controls"
Diffstat (limited to 'tests/camera2')
-rw-r--r-- | tests/camera2/Android.mk | 1 | ||||
-rw-r--r-- | tests/camera2/CameraBurstTests.cpp | 269 | ||||
-rw-r--r-- | tests/camera2/CameraFrameTests.cpp | 4 | ||||
-rw-r--r-- | tests/camera2/CameraStreamFixture.h | 48 |
4 files changed, 317 insertions, 5 deletions
diff --git a/tests/camera2/Android.mk b/tests/camera2/Android.mk index 6b6a6c4..29a028b 100644 --- a/tests/camera2/Android.mk +++ b/tests/camera2/Android.mk @@ -9,6 +9,7 @@ LOCAL_SRC_FILES:= \ CameraModuleTests.cpp \ CameraStreamTests.cpp \ CameraFrameTests.cpp \ + CameraBurstTests.cpp \ ForkedTests.cpp \ TestForkerEventListener.cpp \ diff --git a/tests/camera2/CameraBurstTests.cpp b/tests/camera2/CameraBurstTests.cpp new file mode 100644 index 0000000..ea567f7 --- /dev/null +++ b/tests/camera2/CameraBurstTests.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#define LOG_TAG "CameraBurstTest" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> + +#include <cmath> + +#include "CameraStreamFixture.h" +#include "TestExtensions.h" + +#define CAMERA_FRAME_TIMEOUT 1000000000 //nsecs (1 secs) +#define CAMERA_HEAP_COUNT 2 //HALBUG: 1 means registerBuffers fails +#define CAMERA_BURST_DEBUGGING 0 +#define CAMERA_FRAME_BURST_COUNT 10 + +/* constants for the exposure test */ +#define CAMERA_EXPOSURE_DOUBLE 2 +#define CAMERA_EXPOSURE_DOUBLING_THRESHOLD 1.0f +#define CAMERA_EXPOSURE_DOUBLING_COUNT 4 +#define CAMERA_EXPOSURE_FORMAT HAL_PIXEL_FORMAT_YCrCb_420_SP +#define CAMERA_EXPOSURE_STARTING 100000 // 1/10ms, up to 51.2ms with 10 steps + +#if CAMERA_BURST_DEBUGGING +#define dout std::cout +#else +#define dout if (0) std::cout +#endif + +using namespace android; +using namespace android::camera2; + +namespace android { +namespace camera2 { +namespace tests { + +static CameraStreamParams STREAM_PARAMETERS = { + /*mCameraId*/ 0, + /*mFormat*/ CAMERA_EXPOSURE_FORMAT, + /*mHeapCount*/ CAMERA_HEAP_COUNT +}; + +class CameraBurstTest + : public ::testing::Test, + public CameraStreamFixture { + +public: + CameraBurstTest() : CameraStreamFixture(STREAM_PARAMETERS) { + TEST_EXTENSION_FORKING_CONSTRUCTOR; + + if (HasFatalFailure()) { + return; + } + + CreateStream(); + } + + ~CameraBurstTest() { + TEST_EXTENSION_FORKING_DESTRUCTOR; + + if (mDevice.get()) { + mDevice->waitUntilDrained(); + } + DeleteStream(); + } + + virtual void SetUp() { + TEST_EXTENSION_FORKING_SET_UP; + } + virtual void TearDown() { + TEST_EXTENSION_FORKING_TEAR_DOWN; + } + + /* this assumes the format is YUV420sp */ + long long TotalBrightness(const CpuConsumer::LockedBuffer& imgBuffer, + int *underexposed, + int *overexposed) const { + + const uint8_t* buf = imgBuffer.data; + size_t stride = imgBuffer.stride; + + /* iterate over the Y plane only */ + long long acc = 0; + + *underexposed = 0; + *overexposed = 0; + + for (size_t y = 0; y < imgBuffer.height; ++y) { + for (size_t x = 0; x < imgBuffer.width; ++x) { + const uint8_t p = buf[y * stride + x]; + + if (p == 0) { + if (underexposed) { + ++*underexposed; + } + continue; + } else if (p == 255) { + if (overexposed) { + ++*overexposed; + } + continue; + } + + acc += p; + } + } + + return acc; + } + +protected: + + camera_metadata_ro_entry GetStaticEntry(uint32_t tag) const { + const CameraMetadata& staticInfo = mDevice->info(); + camera_metadata_ro_entry entry = staticInfo.find(tag); + return entry; + } + + +}; + +TEST_F(CameraBurstTest, ManualExposureControl) { + + TEST_EXTENSION_FORKING_INIT; + + // Range of valid exposure times, in nanoseconds + int64_t minExp, maxExp; + { + camera_metadata_ro_entry exposureTimeRange = + GetStaticEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE); + + ASSERT_EQ(2u, exposureTimeRange.count); + minExp = exposureTimeRange.data.i64[0]; + maxExp = exposureTimeRange.data.i64[1]; + } + + dout << "Min exposure is " << minExp; + dout << " max exposure is " << maxExp << std::endl; + + // Calculate some set of valid exposure times for each request + int64_t exposures[CAMERA_FRAME_BURST_COUNT]; + exposures[0] = CAMERA_EXPOSURE_STARTING; + for (int i = 1; i < CAMERA_FRAME_BURST_COUNT; ++i) { + exposures[i] = exposures[i-1] * CAMERA_EXPOSURE_DOUBLE; + } + // Our calculated exposure times should be in [minExp, maxExp] + EXPECT_LE(minExp, exposures[0]) + << "Minimum exposure range is too high, wanted at most " + << exposures[0] << "ns"; + EXPECT_GE(maxExp, exposures[CAMERA_FRAME_BURST_COUNT-1]) + << "Maximum exposure range is too low, wanted at least " + << exposures[CAMERA_FRAME_BURST_COUNT-1] << "ns"; + + // Create a preview request, turning off all 3A + CameraMetadata previewRequest; + ASSERT_EQ(OK, mDevice->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, + &previewRequest)); + { + Vector<uint8_t> outputStreamIds; + outputStreamIds.push(mStreamId); + ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_OUTPUT_STREAMS, + outputStreamIds)); + + // Disable all 3A routines + uint8_t cmOff = static_cast<uint8_t>(ANDROID_CONTROL_MODE_OFF); + ASSERT_EQ(OK, previewRequest.update(ANDROID_CONTROL_MODE, + &cmOff, 1)); + if (CAMERA_BURST_DEBUGGING) { + int frameCount = 0; + ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_FRAME_COUNT, + &frameCount, 1)); + } + } + + if (CAMERA_BURST_DEBUGGING) { + previewRequest.dump(STDOUT_FILENO); + } + + // Submit capture requests + for (int i = 0; i < CAMERA_FRAME_BURST_COUNT; ++i) { + CameraMetadata tmpRequest = previewRequest; + ASSERT_EQ(OK, tmpRequest.update(ANDROID_SENSOR_EXPOSURE_TIME, + &exposures[i], 1)); + ALOGV("Submitting capture request %d with exposure %lld", i, + exposures[i]); + dout << "Capture request " << i << " exposure is " + << (exposures[i]/1e6f) << std::endl; + ASSERT_EQ(OK, mDevice->capture(tmpRequest)); + } + + dout << "Buffer dimensions " << mWidth << "x" << mHeight << std::endl; + + float brightnesses[CAMERA_FRAME_BURST_COUNT]; + // Get each frame (metadata) and then the buffer. Calculate brightness. + for (int i = 0; i < CAMERA_FRAME_BURST_COUNT; ++i) { + ALOGV("Reading capture request %d with exposure %lld", i, exposures[i]); + ASSERT_EQ(OK, mDevice->waitForNextFrame(CAMERA_FRAME_TIMEOUT)); + ALOGV("Reading capture request-1 %d", i); + CameraMetadata frameMetadata; + ASSERT_EQ(OK, mDevice->getNextFrame(&frameMetadata)); + ALOGV("Reading capture request-2 %d", i); + + ASSERT_EQ(OK, mFrameListener->waitForFrame(CAMERA_FRAME_TIMEOUT)); + ALOGV("We got the frame now"); + + CpuConsumer::LockedBuffer imgBuffer; + ASSERT_EQ(OK, mCpuConsumer->lockNextBuffer(&imgBuffer)); + + int underexposed, overexposed; + long long brightness = TotalBrightness(imgBuffer, &underexposed, + &overexposed); + float avgBrightness = brightness * 1.0f / + (mWidth * mHeight - (underexposed + overexposed)); + ALOGV("Total brightness for frame %d was %lld (underexposed %d, " + "overexposed %d), avg %f", i, brightness, underexposed, + overexposed, avgBrightness); + dout << "Average brightness (frame " << i << ") was " << avgBrightness + << " (underexposed " << underexposed << ", overexposed " + << overexposed << ")" << std::endl; + + ASSERT_EQ(OK, mCpuConsumer->unlockBuffer(imgBuffer)); + + brightnesses[i] = avgBrightness; + } + + // Calculate max consecutive frame exposure doubling + float prev = brightnesses[0]; + int doubling_count = 1; + int max_doubling_count = 0; + for (int i = 1; i < CAMERA_FRAME_BURST_COUNT; ++i) { + if (fabs(brightnesses[i] - prev*CAMERA_EXPOSURE_DOUBLE) + <= CAMERA_EXPOSURE_DOUBLING_THRESHOLD) { + doubling_count++; + } + else { + max_doubling_count = std::max(max_doubling_count, doubling_count); + doubling_count = 1; + } + prev = brightnesses[i]; + } + + dout << "max doubling count: " << max_doubling_count << std::endl; + + EXPECT_LE(CAMERA_EXPOSURE_DOUBLING_COUNT, max_doubling_count) + << "average brightness should double at least " + << CAMERA_EXPOSURE_DOUBLING_COUNT + << " times over each consecutive frame as the exposure is doubled"; +} + +} +} +} + diff --git a/tests/camera2/CameraFrameTests.cpp b/tests/camera2/CameraFrameTests.cpp index 6f88312..b24f4ac 100644 --- a/tests/camera2/CameraFrameTests.cpp +++ b/tests/camera2/CameraFrameTests.cpp @@ -70,7 +70,6 @@ public: if (mDevice.get()) { mDevice->waitUntilDrained(); } - DeleteStream(); } virtual void SetUp() { @@ -109,12 +108,13 @@ TEST_P(CameraFrameTest, GetFrame) { } for (int i = 0; i < GetParam(); ++i) { - ALOGV("Submitting capture request"); + ALOGV("Submitting capture request %d", i); CameraMetadata tmpRequest = previewRequest; ASSERT_EQ(OK, mDevice->capture(tmpRequest)); } for (int i = 0; i < GetParam(); ++i) { + ALOGV("Reading capture request %d", i); ASSERT_EQ(OK, mDevice->waitForNextFrame(CAMERA_FRAME_TIMEOUT)); CameraMetadata frameMetadata; ASSERT_EQ(OK, mDevice->getNextFrame(&frameMetadata)); diff --git a/tests/camera2/CameraStreamFixture.h b/tests/camera2/CameraStreamFixture.h index d4ba764..d93d26b 100644 --- a/tests/camera2/CameraStreamFixture.h +++ b/tests/camera2/CameraStreamFixture.h @@ -22,6 +22,8 @@ #include <gui/CpuConsumer.h> #include <gui/SurfaceTextureClient.h> +#include <utils/Condition.h> +#include <utils/Mutex.h> #include "CameraModuleFixture.h" #include "TestExtensions.h" @@ -38,9 +40,9 @@ struct CameraStreamParams { inline void PrintTo(const CameraStreamParams& p, ::std::ostream* os) { *os << "{ "; - *os << "CameraID: " << p.mCameraId << ", "; - *os << "Format: " << p.mCameraId << ", "; - *os << "HeapCount: " << p.mCameraId; + *os << "CameraID: " << p.mCameraId << ", "; + *os << "Format: " << p.mFormat << ", "; + *os << "HeapCount: " << p.mHeapCount; *os << " }"; } @@ -92,6 +94,39 @@ private: } protected: + struct FrameListener : public ConsumerBase::FrameAvailableListener { + + FrameListener() { + mPendingFrames = 0; + } + + // CpuConsumer::FrameAvailableListener implementation + virtual void onFrameAvailable() { + ALOGV("Frame now available (start)"); + + Mutex::Autolock lock(mMutex); + mPendingFrames++; + mCondition.signal(); + + ALOGV("Frame now available (end)"); + } + + status_t waitForFrame(nsecs_t timeout) { + status_t res; + Mutex::Autolock lock(mMutex); + while (mPendingFrames == 0) { + res = mCondition.waitRelative(mMutex, timeout); + if (res != OK) return res; + } + mPendingFrames--; + return OK; + } + + private: + Mutex mMutex; + Condition mCondition; + int mPendingFrames; + }; void CreateStream() { sp<Camera2Device> device = mDevice; @@ -109,6 +144,10 @@ protected: &mStreamId)); ASSERT_NE(-1, mStreamId); + + // do not make 'this' a FrameListener or the lifetime policy will clash + mFrameListener = new FrameListener(); + mCpuConsumer->setFrameAvailableListener(mFrameListener); } void DeleteStream() { @@ -119,9 +158,12 @@ protected: int mHeight; int mStreamId; + + android::sp<FrameListener> mFrameListener; android::sp<CpuConsumer> mCpuConsumer; android::sp<ANativeWindow> mNativeWindow; + private: CameraStreamParams mParam; }; |