/* * 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> #include <inttypes.h> #define LOG_TAG "CameraBurstTest" //#define LOG_NDEBUG 0 #include <utils/Log.h> #include <utils/Timers.h> #include <cmath> #include "CameraStreamFixture.h" #include "TestExtensions.h" #define CAMERA_FRAME_TIMEOUT 1000000000LL //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 CAMERA_STREAM_AUTO_CPU_FORMAT #define CAMERA_EXPOSURE_STARTING 100000 // 1/10ms, up to 51.2ms with 10 steps #define USEC 1000LL // in ns #define MSEC 1000000LL // in ns #define SEC 1000000000LL // in ns #if CAMERA_BURST_DEBUGGING #define dout std::cout #else #define dout if (0) std::cout #endif #define WARN_UNLESS(condition) if(!(condition)) std::cerr << "Warning: " #define WARN_LE(exp, act) WARN_UNLESS((exp) <= (act)) #define WARN_LT(exp, act) WARN_UNLESS((exp) < (act)) #define WARN_GT(exp, act) WARN_UNLESS((exp) > (act)) using namespace android; using namespace android::camera2; namespace android { namespace camera2 { namespace tests { static CameraStreamParams STREAM_PARAMETERS = { /*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 or flexible YUV */ 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; } // Parses a comma-separated string list into a Vector template<typename T> void ParseList(const char *src, Vector<T> &list) { std::istringstream s(src); while (!s.eof()) { char c = s.peek(); if (c == ',' || c == ' ') { s.ignore(1, EOF); continue; } T val; s >> val; list.push_back(val); } } }; 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<int32_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)); int requestId = 1; ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_ID, &requestId, 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 %" PRId64, 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 %" PRId64, i, exposures[i]); ASSERT_EQ(OK, mDevice->waitForNextFrame(CAMERA_FRAME_TIMEOUT)); ALOGV("Reading capture request-1 %d", i); CaptureResult result; ASSERT_EQ(OK, mDevice->getNextResult(&result)); 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; /** * Make this check warning only, since the brightness calculation is not reliable * and we have separate test to cover this case. Plus it is pretty subtle to make * it right without complicating the test too much. */ WARN_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" << std::endl; } /** * This test varies exposure time, frame duration, and sensitivity for a * burst of captures. It picks values by default, but the selection can be * overridden with the environment variables * CAMERA2_TEST_VARIABLE_BURST_EXPOSURE_TIMES * CAMERA2_TEST_VARIABLE_BURST_FRAME_DURATIONS * CAMERA2_TEST_VARIABLE_BURST_SENSITIVITIES * which must all be a list of comma-separated values, and each list must be * the same length. In addition, if the environment variable * CAMERA2_TEST_VARIABLE_BURST_DUMP_FRAMES * is set to 1, then the YUV buffers are dumped into files named * "camera2_test_variable_burst_frame_NNN.yuv" * * For example: * $ setenv CAMERA2_TEST_VARIABLE_BURST_EXPOSURE_TIMES 10000000,20000000 * $ setenv CAMERA2_TEST_VARIABLE_BURST_FRAME_DURATIONS 40000000,40000000 * $ setenv CAMERA2_TEST_VARIABLE_BURST_SENSITIVITIES 200,100 * $ setenv CAMERA2_TEST_VARIABLE_BURST_DUMP_FRAMES 1 * $ /data/nativetest/camera2_test/camera2_test --gtest_filter="*VariableBurst" */ TEST_F(CameraBurstTest, VariableBurst) { TEST_EXTENSION_FORKING_INIT; // Bounds for checking frame duration is within range const nsecs_t DURATION_UPPER_BOUND = 10 * MSEC; const nsecs_t DURATION_LOWER_BOUND = 20 * MSEC; // Threshold for considering two captures to have equivalent exposure value, // as a ratio of the smaller EV to the larger EV. const float EV_MATCH_BOUND = 0.95; // Bound for two captures with equivalent exp values to have the same // measured brightness, in 0-255 luminance. const float BRIGHTNESS_MATCH_BOUND = 5; // Environment variables to look for to override test settings const char *expEnv = "CAMERA2_TEST_VARIABLE_BURST_EXPOSURE_TIMES"; const char *durationEnv = "CAMERA2_TEST_VARIABLE_BURST_FRAME_DURATIONS"; const char *sensitivityEnv = "CAMERA2_TEST_VARIABLE_BURST_SENSITIVITIES"; const char *dumpFrameEnv = "CAMERA2_TEST_VARIABLE_BURST_DUMP_FRAMES"; // Range of valid exposure times, in nanoseconds int64_t minExp = 0, maxExp = 0; // List of valid sensor sensitivities Vector<int32_t> sensitivities; // Range of valid frame durations, in nanoseconds int64_t minDuration = 0, maxDuration = 0; { camera_metadata_ro_entry exposureTimeRange = GetStaticEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE); EXPECT_EQ(2u, exposureTimeRange.count) << "Bad exposure time range tag." "Using default values"; if (exposureTimeRange.count == 2) { minExp = exposureTimeRange.data.i64[0]; maxExp = exposureTimeRange.data.i64[1]; } EXPECT_LT(0, minExp) << "Minimum exposure time is 0"; EXPECT_LT(0, maxExp) << "Maximum exposure time is 0"; EXPECT_LE(minExp, maxExp) << "Minimum exposure is greater than maximum"; if (minExp == 0) { minExp = 1 * MSEC; // Fallback minimum exposure time } if (maxExp == 0) { maxExp = 10 * SEC; // Fallback maximum exposure time } } camera_metadata_ro_entry hardwareLevel = GetStaticEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL); ASSERT_EQ(1u, hardwareLevel.count); uint8_t level = hardwareLevel.data.u8[0]; ASSERT_GE(level, ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED); ASSERT_LE(level, ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL); if (level == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED) { const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); std::cerr << "Skipping test " << test_info->test_case_name() << "." << test_info->name() << " because HAL hardware supported level is limited " << std::endl; return; } dout << "Stream size is " << mWidth << " x " << mHeight << std::endl; dout << "Valid exposure range is: " << minExp << " - " << maxExp << " ns " << std::endl; { camera_metadata_ro_entry sensivityRange = GetStaticEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE); EXPECT_EQ(2u, sensivityRange.count) << "No sensitivity range listed." "Falling back to default set."; int32_t minSensitivity = 100; int32_t maxSensitivity = 800; if (sensivityRange.count == 2) { ASSERT_GT(sensivityRange.data.i32[0], 0); ASSERT_GT(sensivityRange.data.i32[1], 0); minSensitivity = sensivityRange.data.i32[0]; maxSensitivity = sensivityRange.data.i32[1]; } int32_t count = (maxSensitivity - minSensitivity + 99) / 100; sensitivities.push_back(minSensitivity); for (int i = 1; i < count; i++) { sensitivities.push_back(minSensitivity + i * 100); } sensitivities.push_back(maxSensitivity); } dout << "Available sensitivities: "; for (size_t i = 0; i < sensitivities.size(); i++) { dout << sensitivities[i] << " "; } dout << std::endl; { if (getDeviceVersion() < CAMERA_DEVICE_API_VERSION_3_2) { camera_metadata_ro_entry availableProcessedSizes = GetStaticEntry(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES); camera_metadata_ro_entry availableProcessedMinFrameDurations = GetStaticEntry(ANDROID_SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS); EXPECT_EQ(availableProcessedSizes.count, availableProcessedMinFrameDurations.count * 2) << "The number of minimum frame durations doesn't match the number of " "available sizes. Using fallback values"; if (availableProcessedSizes.count == availableProcessedMinFrameDurations.count * 2) { bool gotSize = false; for (size_t i = 0; i < availableProcessedSizes.count; i += 2) { if (availableProcessedSizes.data.i32[i] == mWidth && availableProcessedSizes.data.i32[i+1] == mHeight) { gotSize = true; minDuration = availableProcessedMinFrameDurations.data.i64[i/2]; } } EXPECT_TRUE(gotSize) << "Can't find stream size in list of " "available sizes: " << mWidth << ", " << mHeight; } if (minDuration == 0) { minDuration = 1 * SEC / 30; // Fall back to 30 fps as minimum duration } } else { minDuration = getMinFrameDurationFor( HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, mWidth, mHeight); } ASSERT_LT(0, minDuration); camera_metadata_ro_entry maxFrameDuration = GetStaticEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION); EXPECT_EQ(1u, maxFrameDuration.count) << "No valid maximum frame duration"; if (maxFrameDuration.count == 1) { maxDuration = maxFrameDuration.data.i64[0]; } EXPECT_GT(maxDuration, 0) << "Max duration is 0 or not given, using fallback"; if (maxDuration == 0) { maxDuration = 10 * SEC; // Fall back to 10 seconds as max duration } } dout << "Available frame duration range for configured stream size: " << minDuration << " - " << maxDuration << " ns" << std::endl; // Get environment variables if set const char *expVal = getenv(expEnv); const char *durationVal = getenv(durationEnv); const char *sensitivityVal = getenv(sensitivityEnv); bool gotExp = (expVal != NULL); bool gotDuration = (durationVal != NULL); bool gotSensitivity = (sensitivityVal != NULL); // All or none must be provided if using override envs ASSERT_TRUE( (gotDuration && gotExp && gotSensitivity) || (!gotDuration && !gotExp && !gotSensitivity) ) << "Incomplete set of environment variable overrides provided"; Vector<int64_t> expList, durationList; Vector<int32_t> sensitivityList; if (gotExp) { ParseList(expVal, expList); ParseList(durationVal, durationList); ParseList(sensitivityVal, sensitivityList); ASSERT_TRUE( (expList.size() == durationList.size()) && (durationList.size() == sensitivityList.size())) << "Mismatched sizes in env lists, or parse error"; dout << "Using burst list from environment with " << expList.size() << " captures" << std::endl; } else { // Create a default set of controls based on the available ranges int64_t e; int64_t d; int32_t s; // Exposure ramp e = minExp; d = minDuration; s = sensitivities[0]; while (e < maxExp) { expList.push_back(e); durationList.push_back(d); sensitivityList.push_back(s); e = e * 2; } e = maxExp; expList.push_back(e); durationList.push_back(d); sensitivityList.push_back(s); // Duration ramp e = 30 * MSEC; d = minDuration; s = sensitivities[0]; while (d < maxDuration) { // make sure exposure <= frame duration expList.push_back(e > d ? d : e); durationList.push_back(d); sensitivityList.push_back(s); d = d * 2; } // Sensitivity ramp e = 30 * MSEC; d = 30 * MSEC; d = d > minDuration ? d : minDuration; for (size_t i = 0; i < sensitivities.size(); i++) { expList.push_back(e); durationList.push_back(d); sensitivityList.push_back(sensitivities[i]); } // Constant-EV ramp, duration == exposure e = 30 * MSEC; // at ISO 100 for (size_t i = 0; i < sensitivities.size(); i++) { int64_t e_adj = e * 100 / sensitivities[i]; expList.push_back(e_adj); durationList.push_back(e_adj > minDuration ? e_adj : minDuration); sensitivityList.push_back(sensitivities[i]); } dout << "Default burst sequence created with " << expList.size() << " entries" << std::endl; } // Validate the list, but warn only for (size_t i = 0; i < expList.size(); i++) { EXPECT_GE(maxExp, expList[i]) << "Capture " << i << " exposure too long: " << expList[i]; EXPECT_LE(minExp, expList[i]) << "Capture " << i << " exposure too short: " << expList[i]; EXPECT_GE(maxDuration, durationList[i]) << "Capture " << i << " duration too long: " << durationList[i]; EXPECT_LE(minDuration, durationList[i]) << "Capture " << i << " duration too short: " << durationList[i]; bool validSensitivity = false; for (size_t j = 0; j < sensitivities.size(); j++) { if (sensitivityList[i] == sensitivities[j]) { validSensitivity = true; break; } } EXPECT_TRUE(validSensitivity) << "Capture " << i << " sensitivity not in list: " << sensitivityList[i]; } // Check if debug yuv dumps are requested bool dumpFrames = false; { const char *frameDumpVal = getenv(dumpFrameEnv); if (frameDumpVal != NULL) { if (frameDumpVal[0] == '1') dumpFrames = true; } } dout << "Dumping YUV frames " << (dumpFrames ? "enabled, not checking timing" : "disabled") << std::endl; // Create a base preview request, turning off all 3A CameraMetadata previewRequest; ASSERT_EQ(OK, mDevice->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, &previewRequest)); { Vector<int32_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)); int requestId = 1; ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_ID, &requestId, 1)); } // Submit capture requests for (size_t i = 0; i < expList.size(); ++i) { CameraMetadata tmpRequest = previewRequest; ASSERT_EQ(OK, tmpRequest.update(ANDROID_SENSOR_EXPOSURE_TIME, &expList[i], 1)); ASSERT_EQ(OK, tmpRequest.update(ANDROID_SENSOR_FRAME_DURATION, &durationList[i], 1)); ASSERT_EQ(OK, tmpRequest.update(ANDROID_SENSOR_SENSITIVITY, &sensitivityList[i], 1)); ALOGV("Submitting capture %zu with exposure %" PRId64 ", frame duration %" PRId64 ", sensitivity %d", i, expList[i], durationList[i], sensitivityList[i]); dout << "Capture request " << i << ": exposure is " << (expList[i]/1e6f) << " ms" << ", frame duration is " << (durationList[i]/1e6f) << " ms" << ", sensitivity is " << sensitivityList[i] << std::endl; ASSERT_EQ(OK, mDevice->capture(tmpRequest)); } Vector<float> brightnesses; Vector<nsecs_t> captureTimes; brightnesses.setCapacity(expList.size()); captureTimes.setCapacity(expList.size()); // Get each frame (metadata) and then the buffer. Calculate brightness. for (size_t i = 0; i < expList.size(); ++i) { ALOGV("Reading request %zu", i); dout << "Waiting for capture " << i << ": " << " exposure " << (expList[i]/1e6f) << " ms," << " frame duration " << (durationList[i]/1e6f) << " ms," << " sensitivity " << sensitivityList[i] << std::endl; // Set wait limit based on expected frame duration, or minimum timeout int64_t waitLimit = CAMERA_FRAME_TIMEOUT; if (expList[i] * 2 > waitLimit) waitLimit = expList[i] * 2; if (durationList[i] * 2 > waitLimit) waitLimit = durationList[i] * 2; ASSERT_EQ(OK, mDevice->waitForNextFrame(waitLimit)); ALOGV("Reading capture request-1 %zu", i); CaptureResult result; ASSERT_EQ(OK, mDevice->getNextResult(&result)); ALOGV("Reading capture request-2 %zu", i); ASSERT_EQ(OK, mFrameListener->waitForFrame(CAMERA_FRAME_TIMEOUT)); ALOGV("We got the frame now"); captureTimes.push_back(systemTime()); CpuConsumer::LockedBuffer imgBuffer; ASSERT_EQ(OK, mCpuConsumer->lockNextBuffer(&imgBuffer)); int underexposed, overexposed; float avgBrightness = 0; long long brightness = TotalBrightness(imgBuffer, &underexposed, &overexposed); int numValidPixels = mWidth * mHeight - (underexposed + overexposed); if (numValidPixels != 0) { avgBrightness = brightness * 1.0f / numValidPixels; } else if (underexposed < overexposed) { avgBrightness = 255; } ALOGV("Total brightness for frame %zu 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; brightnesses.push_back(avgBrightness); if (i != 0) { float prevEv = static_cast<float>(expList[i - 1]) * sensitivityList[i - 1]; float currentEv = static_cast<float>(expList[i]) * sensitivityList[i]; float evRatio = (prevEv > currentEv) ? (currentEv / prevEv) : (prevEv / currentEv); if ( evRatio > EV_MATCH_BOUND ) { WARN_LT(fabs(brightnesses[i] - brightnesses[i - 1]), BRIGHTNESS_MATCH_BOUND) << "Capture brightness different from previous, even though " "they have the same EV value. Ev now: " << currentEv << ", previous: " << prevEv << ". Brightness now: " << brightnesses[i] << ", previous: " << brightnesses[i-1] << std::endl; } // Only check timing if not saving to disk, since that slows things // down substantially if (!dumpFrames) { nsecs_t timeDelta = captureTimes[i] - captureTimes[i-1]; nsecs_t expectedDelta = expList[i] > durationList[i] ? expList[i] : durationList[i]; WARN_LT(timeDelta, expectedDelta + DURATION_UPPER_BOUND) << "Capture took " << timeDelta << " ns to receive, but expected" " frame duration was " << expectedDelta << " ns." << std::endl; WARN_GT(timeDelta, expectedDelta - DURATION_LOWER_BOUND) << "Capture took " << timeDelta << " ns to receive, but expected" " frame duration was " << expectedDelta << " ns." << std::endl; dout << "Time delta from previous frame: " << timeDelta / 1e6 << " ms. Expected " << expectedDelta / 1e6 << " ms" << std::endl; } } if (dumpFrames) { String8 dumpName = String8::format("/data/local/tmp/camera2_test_variable_burst_frame_%03zu.yuv", i); dout << " Writing YUV dump to " << dumpName << std::endl; DumpYuvToFile(dumpName, imgBuffer); } ASSERT_EQ(OK, mCpuConsumer->unlockBuffer(imgBuffer)); } } } } }