Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2012 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include <gtest/gtest.h> |
Sasha Levitskiy | 0ab4c96 | 2014-04-21 14:49:12 -0700 | [diff] [blame] | 18 | #include <inttypes.h> |
Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 19 | |
| 20 | #define LOG_TAG "CameraBurstTest" |
| 21 | //#define LOG_NDEBUG 0 |
| 22 | #include <utils/Log.h> |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 23 | #include <utils/Timers.h> |
Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 24 | |
| 25 | #include <cmath> |
| 26 | |
| 27 | #include "CameraStreamFixture.h" |
| 28 | #include "TestExtensions.h" |
| 29 | |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 30 | #define CAMERA_FRAME_TIMEOUT 1000000000LL //nsecs (1 secs) |
Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 31 | #define CAMERA_HEAP_COUNT 2 //HALBUG: 1 means registerBuffers fails |
| 32 | #define CAMERA_BURST_DEBUGGING 0 |
| 33 | #define CAMERA_FRAME_BURST_COUNT 10 |
| 34 | |
| 35 | /* constants for the exposure test */ |
| 36 | #define CAMERA_EXPOSURE_DOUBLE 2 |
| 37 | #define CAMERA_EXPOSURE_DOUBLING_THRESHOLD 1.0f |
| 38 | #define CAMERA_EXPOSURE_DOUBLING_COUNT 4 |
Eino-Ville Talvala | 4c543a1 | 2013-06-25 18:12:19 -0700 | [diff] [blame] | 39 | #define CAMERA_EXPOSURE_FORMAT CAMERA_STREAM_AUTO_CPU_FORMAT |
Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 40 | #define CAMERA_EXPOSURE_STARTING 100000 // 1/10ms, up to 51.2ms with 10 steps |
| 41 | |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 42 | #define USEC 1000LL // in ns |
| 43 | #define MSEC 1000000LL // in ns |
| 44 | #define SEC 1000000000LL // in ns |
| 45 | |
Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 46 | #if CAMERA_BURST_DEBUGGING |
| 47 | #define dout std::cout |
| 48 | #else |
| 49 | #define dout if (0) std::cout |
| 50 | #endif |
| 51 | |
Bernhard Rosenkränzer | 1f1d797 | 2014-12-12 22:16:55 +0100 | [diff] [blame] | 52 | #define WARN_UNLESS(condition) if(!(condition)) std::cerr << "Warning: " |
Zhijun He | 60cbb52 | 2013-09-18 09:44:19 -0700 | [diff] [blame] | 53 | #define WARN_LE(exp, act) WARN_UNLESS((exp) <= (act)) |
| 54 | #define WARN_LT(exp, act) WARN_UNLESS((exp) < (act)) |
| 55 | #define WARN_GT(exp, act) WARN_UNLESS((exp) > (act)) |
| 56 | |
Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 57 | using namespace android; |
| 58 | using namespace android::camera2; |
| 59 | |
| 60 | namespace android { |
| 61 | namespace camera2 { |
| 62 | namespace tests { |
| 63 | |
| 64 | static CameraStreamParams STREAM_PARAMETERS = { |
Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 65 | /*mFormat*/ CAMERA_EXPOSURE_FORMAT, |
| 66 | /*mHeapCount*/ CAMERA_HEAP_COUNT |
| 67 | }; |
| 68 | |
| 69 | class CameraBurstTest |
| 70 | : public ::testing::Test, |
| 71 | public CameraStreamFixture { |
| 72 | |
| 73 | public: |
| 74 | CameraBurstTest() : CameraStreamFixture(STREAM_PARAMETERS) { |
| 75 | TEST_EXTENSION_FORKING_CONSTRUCTOR; |
| 76 | |
| 77 | if (HasFatalFailure()) { |
| 78 | return; |
| 79 | } |
| 80 | |
| 81 | CreateStream(); |
| 82 | } |
| 83 | |
| 84 | ~CameraBurstTest() { |
| 85 | TEST_EXTENSION_FORKING_DESTRUCTOR; |
| 86 | |
| 87 | if (mDevice.get()) { |
| 88 | mDevice->waitUntilDrained(); |
| 89 | } |
| 90 | DeleteStream(); |
| 91 | } |
| 92 | |
| 93 | virtual void SetUp() { |
| 94 | TEST_EXTENSION_FORKING_SET_UP; |
| 95 | } |
| 96 | virtual void TearDown() { |
| 97 | TEST_EXTENSION_FORKING_TEAR_DOWN; |
| 98 | } |
| 99 | |
Eino-Ville Talvala | 4c543a1 | 2013-06-25 18:12:19 -0700 | [diff] [blame] | 100 | /* this assumes the format is YUV420sp or flexible YUV */ |
Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 101 | long long TotalBrightness(const CpuConsumer::LockedBuffer& imgBuffer, |
| 102 | int *underexposed, |
| 103 | int *overexposed) const { |
| 104 | |
| 105 | const uint8_t* buf = imgBuffer.data; |
| 106 | size_t stride = imgBuffer.stride; |
| 107 | |
| 108 | /* iterate over the Y plane only */ |
| 109 | long long acc = 0; |
| 110 | |
| 111 | *underexposed = 0; |
| 112 | *overexposed = 0; |
| 113 | |
| 114 | for (size_t y = 0; y < imgBuffer.height; ++y) { |
| 115 | for (size_t x = 0; x < imgBuffer.width; ++x) { |
| 116 | const uint8_t p = buf[y * stride + x]; |
| 117 | |
| 118 | if (p == 0) { |
| 119 | if (underexposed) { |
| 120 | ++*underexposed; |
| 121 | } |
| 122 | continue; |
| 123 | } else if (p == 255) { |
| 124 | if (overexposed) { |
| 125 | ++*overexposed; |
| 126 | } |
| 127 | continue; |
| 128 | } |
| 129 | |
| 130 | acc += p; |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | return acc; |
| 135 | } |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 136 | |
| 137 | // Parses a comma-separated string list into a Vector |
| 138 | template<typename T> |
| 139 | void ParseList(const char *src, Vector<T> &list) { |
| 140 | std::istringstream s(src); |
| 141 | while (!s.eof()) { |
| 142 | char c = s.peek(); |
| 143 | if (c == ',' || c == ' ') { |
| 144 | s.ignore(1, EOF); |
| 145 | continue; |
| 146 | } |
| 147 | T val; |
| 148 | s >> val; |
| 149 | list.push_back(val); |
| 150 | } |
| 151 | } |
| 152 | |
Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 153 | }; |
| 154 | |
| 155 | TEST_F(CameraBurstTest, ManualExposureControl) { |
| 156 | |
| 157 | TEST_EXTENSION_FORKING_INIT; |
| 158 | |
| 159 | // Range of valid exposure times, in nanoseconds |
| 160 | int64_t minExp, maxExp; |
| 161 | { |
| 162 | camera_metadata_ro_entry exposureTimeRange = |
| 163 | GetStaticEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE); |
| 164 | |
| 165 | ASSERT_EQ(2u, exposureTimeRange.count); |
| 166 | minExp = exposureTimeRange.data.i64[0]; |
| 167 | maxExp = exposureTimeRange.data.i64[1]; |
| 168 | } |
| 169 | |
| 170 | dout << "Min exposure is " << minExp; |
| 171 | dout << " max exposure is " << maxExp << std::endl; |
| 172 | |
| 173 | // Calculate some set of valid exposure times for each request |
| 174 | int64_t exposures[CAMERA_FRAME_BURST_COUNT]; |
| 175 | exposures[0] = CAMERA_EXPOSURE_STARTING; |
| 176 | for (int i = 1; i < CAMERA_FRAME_BURST_COUNT; ++i) { |
| 177 | exposures[i] = exposures[i-1] * CAMERA_EXPOSURE_DOUBLE; |
| 178 | } |
| 179 | // Our calculated exposure times should be in [minExp, maxExp] |
| 180 | EXPECT_LE(minExp, exposures[0]) |
| 181 | << "Minimum exposure range is too high, wanted at most " |
| 182 | << exposures[0] << "ns"; |
| 183 | EXPECT_GE(maxExp, exposures[CAMERA_FRAME_BURST_COUNT-1]) |
| 184 | << "Maximum exposure range is too low, wanted at least " |
| 185 | << exposures[CAMERA_FRAME_BURST_COUNT-1] << "ns"; |
| 186 | |
| 187 | // Create a preview request, turning off all 3A |
| 188 | CameraMetadata previewRequest; |
| 189 | ASSERT_EQ(OK, mDevice->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, |
| 190 | &previewRequest)); |
| 191 | { |
Zhijun He | a159417 | 2013-09-06 15:35:09 -0700 | [diff] [blame] | 192 | Vector<int32_t> outputStreamIds; |
Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 193 | outputStreamIds.push(mStreamId); |
| 194 | ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_OUTPUT_STREAMS, |
| 195 | outputStreamIds)); |
| 196 | |
| 197 | // Disable all 3A routines |
| 198 | uint8_t cmOff = static_cast<uint8_t>(ANDROID_CONTROL_MODE_OFF); |
| 199 | ASSERT_EQ(OK, previewRequest.update(ANDROID_CONTROL_MODE, |
| 200 | &cmOff, 1)); |
Eino-Ville Talvala | 4c543a1 | 2013-06-25 18:12:19 -0700 | [diff] [blame] | 201 | |
| 202 | int requestId = 1; |
| 203 | ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_ID, |
| 204 | &requestId, 1)); |
| 205 | |
Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 206 | if (CAMERA_BURST_DEBUGGING) { |
| 207 | int frameCount = 0; |
| 208 | ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_FRAME_COUNT, |
| 209 | &frameCount, 1)); |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | if (CAMERA_BURST_DEBUGGING) { |
| 214 | previewRequest.dump(STDOUT_FILENO); |
| 215 | } |
| 216 | |
| 217 | // Submit capture requests |
| 218 | for (int i = 0; i < CAMERA_FRAME_BURST_COUNT; ++i) { |
| 219 | CameraMetadata tmpRequest = previewRequest; |
| 220 | ASSERT_EQ(OK, tmpRequest.update(ANDROID_SENSOR_EXPOSURE_TIME, |
| 221 | &exposures[i], 1)); |
Bernhard Rosenkränzer | 1f1d797 | 2014-12-12 22:16:55 +0100 | [diff] [blame] | 222 | ALOGV("Submitting capture request %d with exposure %" PRId64, i, |
Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 223 | exposures[i]); |
| 224 | dout << "Capture request " << i << " exposure is " |
| 225 | << (exposures[i]/1e6f) << std::endl; |
| 226 | ASSERT_EQ(OK, mDevice->capture(tmpRequest)); |
| 227 | } |
| 228 | |
| 229 | dout << "Buffer dimensions " << mWidth << "x" << mHeight << std::endl; |
| 230 | |
| 231 | float brightnesses[CAMERA_FRAME_BURST_COUNT]; |
| 232 | // Get each frame (metadata) and then the buffer. Calculate brightness. |
| 233 | for (int i = 0; i < CAMERA_FRAME_BURST_COUNT; ++i) { |
Bernhard Rosenkränzer | 1f1d797 | 2014-12-12 22:16:55 +0100 | [diff] [blame] | 234 | ALOGV("Reading capture request %d with exposure %" PRId64, i, exposures[i]); |
Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 235 | ASSERT_EQ(OK, mDevice->waitForNextFrame(CAMERA_FRAME_TIMEOUT)); |
| 236 | ALOGV("Reading capture request-1 %d", i); |
Jianing Wei | f816eea | 2014-04-10 14:17:57 -0700 | [diff] [blame] | 237 | CaptureResult result; |
| 238 | ASSERT_EQ(OK, mDevice->getNextResult(&result)); |
Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 239 | ALOGV("Reading capture request-2 %d", i); |
| 240 | |
| 241 | ASSERT_EQ(OK, mFrameListener->waitForFrame(CAMERA_FRAME_TIMEOUT)); |
| 242 | ALOGV("We got the frame now"); |
| 243 | |
| 244 | CpuConsumer::LockedBuffer imgBuffer; |
| 245 | ASSERT_EQ(OK, mCpuConsumer->lockNextBuffer(&imgBuffer)); |
| 246 | |
| 247 | int underexposed, overexposed; |
| 248 | long long brightness = TotalBrightness(imgBuffer, &underexposed, |
| 249 | &overexposed); |
| 250 | float avgBrightness = brightness * 1.0f / |
| 251 | (mWidth * mHeight - (underexposed + overexposed)); |
| 252 | ALOGV("Total brightness for frame %d was %lld (underexposed %d, " |
| 253 | "overexposed %d), avg %f", i, brightness, underexposed, |
| 254 | overexposed, avgBrightness); |
| 255 | dout << "Average brightness (frame " << i << ") was " << avgBrightness |
| 256 | << " (underexposed " << underexposed << ", overexposed " |
| 257 | << overexposed << ")" << std::endl; |
| 258 | |
| 259 | ASSERT_EQ(OK, mCpuConsumer->unlockBuffer(imgBuffer)); |
| 260 | |
| 261 | brightnesses[i] = avgBrightness; |
| 262 | } |
| 263 | |
| 264 | // Calculate max consecutive frame exposure doubling |
| 265 | float prev = brightnesses[0]; |
| 266 | int doubling_count = 1; |
| 267 | int max_doubling_count = 0; |
| 268 | for (int i = 1; i < CAMERA_FRAME_BURST_COUNT; ++i) { |
| 269 | if (fabs(brightnesses[i] - prev*CAMERA_EXPOSURE_DOUBLE) |
| 270 | <= CAMERA_EXPOSURE_DOUBLING_THRESHOLD) { |
| 271 | doubling_count++; |
| 272 | } |
| 273 | else { |
| 274 | max_doubling_count = std::max(max_doubling_count, doubling_count); |
| 275 | doubling_count = 1; |
| 276 | } |
| 277 | prev = brightnesses[i]; |
| 278 | } |
| 279 | |
| 280 | dout << "max doubling count: " << max_doubling_count << std::endl; |
| 281 | |
Zhijun He | 60cbb52 | 2013-09-18 09:44:19 -0700 | [diff] [blame] | 282 | /** |
| 283 | * Make this check warning only, since the brightness calculation is not reliable |
| 284 | * and we have separate test to cover this case. Plus it is pretty subtle to make |
| 285 | * it right without complicating the test too much. |
| 286 | */ |
| 287 | WARN_LE(CAMERA_EXPOSURE_DOUBLING_COUNT, max_doubling_count) |
| 288 | << "average brightness should double at least " |
| 289 | << CAMERA_EXPOSURE_DOUBLING_COUNT |
| 290 | << " times over each consecutive frame as the exposure is doubled" |
| 291 | << std::endl; |
Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 292 | } |
| 293 | |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 294 | /** |
| 295 | * This test varies exposure time, frame duration, and sensitivity for a |
| 296 | * burst of captures. It picks values by default, but the selection can be |
| 297 | * overridden with the environment variables |
| 298 | * CAMERA2_TEST_VARIABLE_BURST_EXPOSURE_TIMES |
| 299 | * CAMERA2_TEST_VARIABLE_BURST_FRAME_DURATIONS |
| 300 | * CAMERA2_TEST_VARIABLE_BURST_SENSITIVITIES |
| 301 | * which must all be a list of comma-separated values, and each list must be |
| 302 | * the same length. In addition, if the environment variable |
| 303 | * CAMERA2_TEST_VARIABLE_BURST_DUMP_FRAMES |
| 304 | * is set to 1, then the YUV buffers are dumped into files named |
| 305 | * "camera2_test_variable_burst_frame_NNN.yuv" |
| 306 | * |
| 307 | * For example: |
| 308 | * $ setenv CAMERA2_TEST_VARIABLE_BURST_EXPOSURE_TIMES 10000000,20000000 |
| 309 | * $ setenv CAMERA2_TEST_VARIABLE_BURST_FRAME_DURATIONS 40000000,40000000 |
| 310 | * $ setenv CAMERA2_TEST_VARIABLE_BURST_SENSITIVITIES 200,100 |
| 311 | * $ setenv CAMERA2_TEST_VARIABLE_BURST_DUMP_FRAMES 1 |
| 312 | * $ /data/nativetest/camera2_test/camera2_test --gtest_filter="*VariableBurst" |
| 313 | */ |
Yin-Chia Yeh | 8df990b | 2014-10-31 15:10:11 -0700 | [diff] [blame] | 314 | TEST_F(CameraBurstTest, VariableBurst) { |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 315 | |
| 316 | TEST_EXTENSION_FORKING_INIT; |
| 317 | |
| 318 | // Bounds for checking frame duration is within range |
| 319 | const nsecs_t DURATION_UPPER_BOUND = 10 * MSEC; |
| 320 | const nsecs_t DURATION_LOWER_BOUND = 20 * MSEC; |
| 321 | |
| 322 | // Threshold for considering two captures to have equivalent exposure value, |
| 323 | // as a ratio of the smaller EV to the larger EV. |
| 324 | const float EV_MATCH_BOUND = 0.95; |
| 325 | // Bound for two captures with equivalent exp values to have the same |
| 326 | // measured brightness, in 0-255 luminance. |
| 327 | const float BRIGHTNESS_MATCH_BOUND = 5; |
| 328 | |
| 329 | // Environment variables to look for to override test settings |
| 330 | const char *expEnv = "CAMERA2_TEST_VARIABLE_BURST_EXPOSURE_TIMES"; |
| 331 | const char *durationEnv = "CAMERA2_TEST_VARIABLE_BURST_FRAME_DURATIONS"; |
| 332 | const char *sensitivityEnv = "CAMERA2_TEST_VARIABLE_BURST_SENSITIVITIES"; |
| 333 | const char *dumpFrameEnv = "CAMERA2_TEST_VARIABLE_BURST_DUMP_FRAMES"; |
| 334 | |
| 335 | // Range of valid exposure times, in nanoseconds |
| 336 | int64_t minExp = 0, maxExp = 0; |
| 337 | // List of valid sensor sensitivities |
| 338 | Vector<int32_t> sensitivities; |
| 339 | // Range of valid frame durations, in nanoseconds |
| 340 | int64_t minDuration = 0, maxDuration = 0; |
| 341 | |
| 342 | { |
| 343 | camera_metadata_ro_entry exposureTimeRange = |
| 344 | GetStaticEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE); |
| 345 | |
| 346 | EXPECT_EQ(2u, exposureTimeRange.count) << "Bad exposure time range tag." |
| 347 | "Using default values"; |
| 348 | if (exposureTimeRange.count == 2) { |
| 349 | minExp = exposureTimeRange.data.i64[0]; |
| 350 | maxExp = exposureTimeRange.data.i64[1]; |
| 351 | } |
| 352 | |
| 353 | EXPECT_LT(0, minExp) << "Minimum exposure time is 0"; |
| 354 | EXPECT_LT(0, maxExp) << "Maximum exposure time is 0"; |
| 355 | EXPECT_LE(minExp, maxExp) << "Minimum exposure is greater than maximum"; |
| 356 | |
| 357 | if (minExp == 0) { |
| 358 | minExp = 1 * MSEC; // Fallback minimum exposure time |
| 359 | } |
| 360 | |
| 361 | if (maxExp == 0) { |
| 362 | maxExp = 10 * SEC; // Fallback maximum exposure time |
| 363 | } |
| 364 | } |
| 365 | |
Zhijun He | 3bf3b45 | 2013-09-18 23:42:12 -0700 | [diff] [blame] | 366 | camera_metadata_ro_entry hardwareLevel = |
| 367 | GetStaticEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL); |
| 368 | ASSERT_EQ(1u, hardwareLevel.count); |
| 369 | uint8_t level = hardwareLevel.data.u8[0]; |
| 370 | ASSERT_GE(level, ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED); |
| 371 | ASSERT_LE(level, ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL); |
| 372 | if (level == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED) { |
| 373 | const ::testing::TestInfo* const test_info = |
| 374 | ::testing::UnitTest::GetInstance()->current_test_info(); |
| 375 | std::cerr << "Skipping test " |
| 376 | << test_info->test_case_name() << "." |
| 377 | << test_info->name() |
| 378 | << " because HAL hardware supported level is limited " |
| 379 | << std::endl; |
| 380 | return; |
| 381 | } |
| 382 | |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 383 | dout << "Stream size is " << mWidth << " x " << mHeight << std::endl; |
| 384 | dout << "Valid exposure range is: " << |
| 385 | minExp << " - " << maxExp << " ns " << std::endl; |
| 386 | |
| 387 | { |
Zhijun He | 7f3ce00 | 2013-07-18 17:01:57 -0700 | [diff] [blame] | 388 | camera_metadata_ro_entry sensivityRange = |
| 389 | GetStaticEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE); |
| 390 | EXPECT_EQ(2u, sensivityRange.count) << "No sensitivity range listed." |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 391 | "Falling back to default set."; |
Zhijun He | 7f3ce00 | 2013-07-18 17:01:57 -0700 | [diff] [blame] | 392 | int32_t minSensitivity = 100; |
| 393 | int32_t maxSensitivity = 800; |
Zhijun He | 3bf3b45 | 2013-09-18 23:42:12 -0700 | [diff] [blame] | 394 | if (sensivityRange.count == 2) { |
| 395 | ASSERT_GT(sensivityRange.data.i32[0], 0); |
| 396 | ASSERT_GT(sensivityRange.data.i32[1], 0); |
Zhijun He | 7f3ce00 | 2013-07-18 17:01:57 -0700 | [diff] [blame] | 397 | minSensitivity = sensivityRange.data.i32[0]; |
| 398 | maxSensitivity = sensivityRange.data.i32[1]; |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 399 | } |
Zhijun He | 7f3ce00 | 2013-07-18 17:01:57 -0700 | [diff] [blame] | 400 | int32_t count = (maxSensitivity - minSensitivity + 99) / 100; |
| 401 | sensitivities.push_back(minSensitivity); |
| 402 | for (int i = 1; i < count; i++) { |
| 403 | sensitivities.push_back(minSensitivity + i * 100); |
| 404 | } |
| 405 | sensitivities.push_back(maxSensitivity); |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 406 | } |
| 407 | |
| 408 | dout << "Available sensitivities: "; |
| 409 | for (size_t i = 0; i < sensitivities.size(); i++) { |
| 410 | dout << sensitivities[i] << " "; |
| 411 | } |
| 412 | dout << std::endl; |
| 413 | |
| 414 | { |
Yin-Chia Yeh | 8df990b | 2014-10-31 15:10:11 -0700 | [diff] [blame] | 415 | if (getDeviceVersion() < CAMERA_DEVICE_API_VERSION_3_2) { |
| 416 | camera_metadata_ro_entry availableProcessedSizes = |
| 417 | GetStaticEntry(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES); |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 418 | |
Yin-Chia Yeh | 8df990b | 2014-10-31 15:10:11 -0700 | [diff] [blame] | 419 | camera_metadata_ro_entry availableProcessedMinFrameDurations = |
| 420 | GetStaticEntry(ANDROID_SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS); |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 421 | |
Yin-Chia Yeh | 8df990b | 2014-10-31 15:10:11 -0700 | [diff] [blame] | 422 | EXPECT_EQ(availableProcessedSizes.count, |
| 423 | availableProcessedMinFrameDurations.count * 2) << |
| 424 | "The number of minimum frame durations doesn't match the number of " |
| 425 | "available sizes. Using fallback values"; |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 426 | |
Yin-Chia Yeh | 8df990b | 2014-10-31 15:10:11 -0700 | [diff] [blame] | 427 | if (availableProcessedSizes.count == |
| 428 | availableProcessedMinFrameDurations.count * 2) { |
| 429 | bool gotSize = false; |
| 430 | for (size_t i = 0; i < availableProcessedSizes.count; i += 2) { |
| 431 | if (availableProcessedSizes.data.i32[i] == mWidth && |
| 432 | availableProcessedSizes.data.i32[i+1] == mHeight) { |
| 433 | gotSize = true; |
| 434 | minDuration = availableProcessedMinFrameDurations.data.i64[i/2]; |
| 435 | } |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 436 | } |
Yin-Chia Yeh | 8df990b | 2014-10-31 15:10:11 -0700 | [diff] [blame] | 437 | EXPECT_TRUE(gotSize) << "Can't find stream size in list of " |
| 438 | "available sizes: " << mWidth << ", " << mHeight; |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 439 | } |
Yin-Chia Yeh | 8df990b | 2014-10-31 15:10:11 -0700 | [diff] [blame] | 440 | if (minDuration == 0) { |
| 441 | minDuration = 1 * SEC / 30; // Fall back to 30 fps as minimum duration |
| 442 | } |
| 443 | } else { |
| 444 | minDuration = getMinFrameDurationFor( |
| 445 | HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, mWidth, mHeight); |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 446 | } |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 447 | ASSERT_LT(0, minDuration); |
| 448 | |
| 449 | camera_metadata_ro_entry maxFrameDuration = |
| 450 | GetStaticEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION); |
| 451 | |
| 452 | EXPECT_EQ(1u, maxFrameDuration.count) << "No valid maximum frame duration"; |
| 453 | |
| 454 | if (maxFrameDuration.count == 1) { |
| 455 | maxDuration = maxFrameDuration.data.i64[0]; |
| 456 | } |
| 457 | |
Zhijun He | 6e548cf | 2013-08-08 19:43:24 -0700 | [diff] [blame] | 458 | EXPECT_GT(maxDuration, 0) << "Max duration is 0 or not given, using fallback"; |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 459 | |
| 460 | if (maxDuration == 0) { |
| 461 | maxDuration = 10 * SEC; // Fall back to 10 seconds as max duration |
| 462 | } |
| 463 | |
| 464 | } |
| 465 | dout << "Available frame duration range for configured stream size: " |
| 466 | << minDuration << " - " << maxDuration << " ns" << std::endl; |
| 467 | |
| 468 | // Get environment variables if set |
| 469 | const char *expVal = getenv(expEnv); |
| 470 | const char *durationVal = getenv(durationEnv); |
| 471 | const char *sensitivityVal = getenv(sensitivityEnv); |
| 472 | |
| 473 | bool gotExp = (expVal != NULL); |
| 474 | bool gotDuration = (durationVal != NULL); |
| 475 | bool gotSensitivity = (sensitivityVal != NULL); |
| 476 | |
| 477 | // All or none must be provided if using override envs |
| 478 | ASSERT_TRUE( (gotDuration && gotExp && gotSensitivity) || |
| 479 | (!gotDuration && !gotExp && !gotSensitivity) ) << |
| 480 | "Incomplete set of environment variable overrides provided"; |
| 481 | |
| 482 | Vector<int64_t> expList, durationList; |
| 483 | Vector<int32_t> sensitivityList; |
| 484 | if (gotExp) { |
| 485 | ParseList(expVal, expList); |
| 486 | ParseList(durationVal, durationList); |
| 487 | ParseList(sensitivityVal, sensitivityList); |
| 488 | |
| 489 | ASSERT_TRUE( |
| 490 | (expList.size() == durationList.size()) && |
| 491 | (durationList.size() == sensitivityList.size())) << |
| 492 | "Mismatched sizes in env lists, or parse error"; |
| 493 | |
| 494 | dout << "Using burst list from environment with " << expList.size() << |
| 495 | " captures" << std::endl; |
| 496 | } else { |
| 497 | // Create a default set of controls based on the available ranges |
| 498 | |
| 499 | int64_t e; |
| 500 | int64_t d; |
| 501 | int32_t s; |
| 502 | |
| 503 | // Exposure ramp |
| 504 | |
| 505 | e = minExp; |
| 506 | d = minDuration; |
| 507 | s = sensitivities[0]; |
| 508 | while (e < maxExp) { |
| 509 | expList.push_back(e); |
| 510 | durationList.push_back(d); |
| 511 | sensitivityList.push_back(s); |
| 512 | e = e * 2; |
| 513 | } |
| 514 | e = maxExp; |
| 515 | expList.push_back(e); |
| 516 | durationList.push_back(d); |
| 517 | sensitivityList.push_back(s); |
| 518 | |
| 519 | // Duration ramp |
| 520 | |
| 521 | e = 30 * MSEC; |
| 522 | d = minDuration; |
| 523 | s = sensitivities[0]; |
| 524 | while (d < maxDuration) { |
| 525 | // make sure exposure <= frame duration |
| 526 | expList.push_back(e > d ? d : e); |
| 527 | durationList.push_back(d); |
| 528 | sensitivityList.push_back(s); |
| 529 | d = d * 2; |
| 530 | } |
| 531 | |
| 532 | // Sensitivity ramp |
| 533 | |
| 534 | e = 30 * MSEC; |
| 535 | d = 30 * MSEC; |
| 536 | d = d > minDuration ? d : minDuration; |
| 537 | for (size_t i = 0; i < sensitivities.size(); i++) { |
| 538 | expList.push_back(e); |
| 539 | durationList.push_back(d); |
| 540 | sensitivityList.push_back(sensitivities[i]); |
| 541 | } |
| 542 | |
| 543 | // Constant-EV ramp, duration == exposure |
| 544 | |
| 545 | e = 30 * MSEC; // at ISO 100 |
| 546 | for (size_t i = 0; i < sensitivities.size(); i++) { |
| 547 | int64_t e_adj = e * 100 / sensitivities[i]; |
| 548 | expList.push_back(e_adj); |
| 549 | durationList.push_back(e_adj > minDuration ? e_adj : minDuration); |
| 550 | sensitivityList.push_back(sensitivities[i]); |
| 551 | } |
| 552 | |
| 553 | dout << "Default burst sequence created with " << expList.size() << |
| 554 | " entries" << std::endl; |
| 555 | } |
| 556 | |
| 557 | // Validate the list, but warn only |
| 558 | for (size_t i = 0; i < expList.size(); i++) { |
| 559 | EXPECT_GE(maxExp, expList[i]) |
| 560 | << "Capture " << i << " exposure too long: " << expList[i]; |
| 561 | EXPECT_LE(minExp, expList[i]) |
| 562 | << "Capture " << i << " exposure too short: " << expList[i]; |
| 563 | EXPECT_GE(maxDuration, durationList[i]) |
| 564 | << "Capture " << i << " duration too long: " << durationList[i]; |
| 565 | EXPECT_LE(minDuration, durationList[i]) |
| 566 | << "Capture " << i << " duration too short: " << durationList[i]; |
| 567 | bool validSensitivity = false; |
| 568 | for (size_t j = 0; j < sensitivities.size(); j++) { |
| 569 | if (sensitivityList[i] == sensitivities[j]) { |
| 570 | validSensitivity = true; |
| 571 | break; |
| 572 | } |
| 573 | } |
| 574 | EXPECT_TRUE(validSensitivity) |
| 575 | << "Capture " << i << " sensitivity not in list: " << sensitivityList[i]; |
| 576 | } |
| 577 | |
| 578 | // Check if debug yuv dumps are requested |
| 579 | |
| 580 | bool dumpFrames = false; |
| 581 | { |
| 582 | const char *frameDumpVal = getenv(dumpFrameEnv); |
| 583 | if (frameDumpVal != NULL) { |
| 584 | if (frameDumpVal[0] == '1') dumpFrames = true; |
| 585 | } |
| 586 | } |
| 587 | |
| 588 | dout << "Dumping YUV frames " << |
| 589 | (dumpFrames ? "enabled, not checking timing" : "disabled") << std::endl; |
| 590 | |
| 591 | // Create a base preview request, turning off all 3A |
| 592 | CameraMetadata previewRequest; |
| 593 | ASSERT_EQ(OK, mDevice->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, |
| 594 | &previewRequest)); |
| 595 | { |
Zhijun He | a159417 | 2013-09-06 15:35:09 -0700 | [diff] [blame] | 596 | Vector<int32_t> outputStreamIds; |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 597 | outputStreamIds.push(mStreamId); |
| 598 | ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_OUTPUT_STREAMS, |
| 599 | outputStreamIds)); |
| 600 | |
| 601 | // Disable all 3A routines |
| 602 | uint8_t cmOff = static_cast<uint8_t>(ANDROID_CONTROL_MODE_OFF); |
| 603 | ASSERT_EQ(OK, previewRequest.update(ANDROID_CONTROL_MODE, |
| 604 | &cmOff, 1)); |
| 605 | |
| 606 | int requestId = 1; |
| 607 | ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_ID, |
| 608 | &requestId, 1)); |
| 609 | } |
| 610 | |
| 611 | // Submit capture requests |
| 612 | |
| 613 | for (size_t i = 0; i < expList.size(); ++i) { |
| 614 | CameraMetadata tmpRequest = previewRequest; |
| 615 | ASSERT_EQ(OK, tmpRequest.update(ANDROID_SENSOR_EXPOSURE_TIME, |
| 616 | &expList[i], 1)); |
| 617 | ASSERT_EQ(OK, tmpRequest.update(ANDROID_SENSOR_FRAME_DURATION, |
| 618 | &durationList[i], 1)); |
| 619 | ASSERT_EQ(OK, tmpRequest.update(ANDROID_SENSOR_SENSITIVITY, |
| 620 | &sensitivityList[i], 1)); |
Bernhard Rosenkränzer | 1f1d797 | 2014-12-12 22:16:55 +0100 | [diff] [blame] | 621 | ALOGV("Submitting capture %zu with exposure %" PRId64 ", frame duration %" PRId64 ", sensitivity %d", |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 622 | i, expList[i], durationList[i], sensitivityList[i]); |
| 623 | dout << "Capture request " << i << |
| 624 | ": exposure is " << (expList[i]/1e6f) << " ms" << |
| 625 | ", frame duration is " << (durationList[i]/1e6f) << " ms" << |
| 626 | ", sensitivity is " << sensitivityList[i] << |
| 627 | std::endl; |
| 628 | ASSERT_EQ(OK, mDevice->capture(tmpRequest)); |
| 629 | } |
| 630 | |
| 631 | Vector<float> brightnesses; |
| 632 | Vector<nsecs_t> captureTimes; |
| 633 | brightnesses.setCapacity(expList.size()); |
| 634 | captureTimes.setCapacity(expList.size()); |
| 635 | |
| 636 | // Get each frame (metadata) and then the buffer. Calculate brightness. |
| 637 | for (size_t i = 0; i < expList.size(); ++i) { |
| 638 | |
Sasha Levitskiy | 0ab4c96 | 2014-04-21 14:49:12 -0700 | [diff] [blame] | 639 | ALOGV("Reading request %zu", i); |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 640 | dout << "Waiting for capture " << i << ": " << |
| 641 | " exposure " << (expList[i]/1e6f) << " ms," << |
| 642 | " frame duration " << (durationList[i]/1e6f) << " ms," << |
| 643 | " sensitivity " << sensitivityList[i] << |
| 644 | std::endl; |
| 645 | |
| 646 | // Set wait limit based on expected frame duration, or minimum timeout |
| 647 | int64_t waitLimit = CAMERA_FRAME_TIMEOUT; |
| 648 | if (expList[i] * 2 > waitLimit) waitLimit = expList[i] * 2; |
| 649 | if (durationList[i] * 2 > waitLimit) waitLimit = durationList[i] * 2; |
| 650 | |
| 651 | ASSERT_EQ(OK, mDevice->waitForNextFrame(waitLimit)); |
Sasha Levitskiy | 0ab4c96 | 2014-04-21 14:49:12 -0700 | [diff] [blame] | 652 | ALOGV("Reading capture request-1 %zu", i); |
Jianing Wei | f816eea | 2014-04-10 14:17:57 -0700 | [diff] [blame] | 653 | CaptureResult result; |
| 654 | ASSERT_EQ(OK, mDevice->getNextResult(&result)); |
Sasha Levitskiy | 0ab4c96 | 2014-04-21 14:49:12 -0700 | [diff] [blame] | 655 | ALOGV("Reading capture request-2 %zu", i); |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 656 | |
| 657 | ASSERT_EQ(OK, mFrameListener->waitForFrame(CAMERA_FRAME_TIMEOUT)); |
| 658 | ALOGV("We got the frame now"); |
| 659 | |
| 660 | captureTimes.push_back(systemTime()); |
| 661 | |
| 662 | CpuConsumer::LockedBuffer imgBuffer; |
| 663 | ASSERT_EQ(OK, mCpuConsumer->lockNextBuffer(&imgBuffer)); |
| 664 | |
| 665 | int underexposed, overexposed; |
| 666 | float avgBrightness = 0; |
| 667 | long long brightness = TotalBrightness(imgBuffer, &underexposed, |
| 668 | &overexposed); |
| 669 | int numValidPixels = mWidth * mHeight - (underexposed + overexposed); |
| 670 | if (numValidPixels != 0) { |
| 671 | avgBrightness = brightness * 1.0f / numValidPixels; |
| 672 | } else if (underexposed < overexposed) { |
| 673 | avgBrightness = 255; |
| 674 | } |
| 675 | |
Sasha Levitskiy | 0ab4c96 | 2014-04-21 14:49:12 -0700 | [diff] [blame] | 676 | ALOGV("Total brightness for frame %zu was %lld (underexposed %d, " |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 677 | "overexposed %d), avg %f", i, brightness, underexposed, |
| 678 | overexposed, avgBrightness); |
| 679 | dout << "Average brightness (frame " << i << ") was " << avgBrightness |
| 680 | << " (underexposed " << underexposed << ", overexposed " |
| 681 | << overexposed << ")" << std::endl; |
| 682 | brightnesses.push_back(avgBrightness); |
| 683 | |
| 684 | if (i != 0) { |
| 685 | float prevEv = static_cast<float>(expList[i - 1]) * sensitivityList[i - 1]; |
| 686 | float currentEv = static_cast<float>(expList[i]) * sensitivityList[i]; |
| 687 | float evRatio = (prevEv > currentEv) ? (currentEv / prevEv) : |
| 688 | (prevEv / currentEv); |
| 689 | if ( evRatio > EV_MATCH_BOUND ) { |
Zhijun He | 60cbb52 | 2013-09-18 09:44:19 -0700 | [diff] [blame] | 690 | WARN_LT(fabs(brightnesses[i] - brightnesses[i - 1]), |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 691 | BRIGHTNESS_MATCH_BOUND) << |
| 692 | "Capture brightness different from previous, even though " |
| 693 | "they have the same EV value. Ev now: " << currentEv << |
| 694 | ", previous: " << prevEv << ". Brightness now: " << |
Zhijun He | 60cbb52 | 2013-09-18 09:44:19 -0700 | [diff] [blame] | 695 | brightnesses[i] << ", previous: " << brightnesses[i-1] << |
| 696 | std::endl; |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 697 | } |
| 698 | // Only check timing if not saving to disk, since that slows things |
| 699 | // down substantially |
| 700 | if (!dumpFrames) { |
| 701 | nsecs_t timeDelta = captureTimes[i] - captureTimes[i-1]; |
| 702 | nsecs_t expectedDelta = expList[i] > durationList[i] ? |
| 703 | expList[i] : durationList[i]; |
Zhijun He | 60cbb52 | 2013-09-18 09:44:19 -0700 | [diff] [blame] | 704 | WARN_LT(timeDelta, expectedDelta + DURATION_UPPER_BOUND) << |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 705 | "Capture took " << timeDelta << " ns to receive, but expected" |
Zhijun He | 60cbb52 | 2013-09-18 09:44:19 -0700 | [diff] [blame] | 706 | " frame duration was " << expectedDelta << " ns." << |
| 707 | std::endl; |
| 708 | WARN_GT(timeDelta, expectedDelta - DURATION_LOWER_BOUND) << |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 709 | "Capture took " << timeDelta << " ns to receive, but expected" |
Zhijun He | 60cbb52 | 2013-09-18 09:44:19 -0700 | [diff] [blame] | 710 | " frame duration was " << expectedDelta << " ns." << |
| 711 | std::endl; |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 712 | dout << "Time delta from previous frame: " << timeDelta / 1e6 << |
| 713 | " ms. Expected " << expectedDelta / 1e6 << " ms" << std::endl; |
| 714 | } |
| 715 | } |
| 716 | |
| 717 | if (dumpFrames) { |
| 718 | String8 dumpName = |
Sasha Levitskiy | 0ab4c96 | 2014-04-21 14:49:12 -0700 | [diff] [blame] | 719 | String8::format("/data/local/tmp/camera2_test_variable_burst_frame_%03zu.yuv", i); |
Eino-Ville Talvala | 7d83171 | 2013-07-01 18:47:09 -0700 | [diff] [blame] | 720 | dout << " Writing YUV dump to " << dumpName << std::endl; |
| 721 | DumpYuvToFile(dumpName, imgBuffer); |
| 722 | } |
| 723 | |
| 724 | ASSERT_EQ(OK, mCpuConsumer->unlockBuffer(imgBuffer)); |
| 725 | } |
| 726 | |
| 727 | } |
| 728 | |
Igor Murashkin | f1b9ae7 | 2012-12-07 15:08:35 -0800 | [diff] [blame] | 729 | } |
| 730 | } |
| 731 | } |