Camera: Remove Camera2 native tests
The native camera tests will no longer be supported by 'CameraService' and will fail during compilation. VTS already has most of these covered except for the burst test cases, which need to be rewritten. Merged-In: I28dc3dd6e922fc7f4b21108de227d4d42695e2e0 Bug: 34392075 Test: Succesful build of hardware library Change-Id: I28dc3dd6e922fc7f4b21108de227d4d42695e2e0
This commit is contained in:
parent
7ac3e70649
commit
65217c24b0
18 changed files with 0 additions and 4111 deletions
|
@ -1,45 +0,0 @@
|
||||||
LOCAL_PATH:= $(call my-dir)
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
|
|
||||||
LOCAL_SRC_FILES:= \
|
|
||||||
camera2_utils.cpp \
|
|
||||||
main.cpp \
|
|
||||||
CameraMetadataTests.cpp \
|
|
||||||
CameraModuleTests.cpp \
|
|
||||||
CameraStreamTests.cpp \
|
|
||||||
CameraFrameTests.cpp \
|
|
||||||
CameraBurstTests.cpp \
|
|
||||||
CameraMultiStreamTests.cpp\
|
|
||||||
ForkedTests.cpp \
|
|
||||||
TestForkerEventListener.cpp \
|
|
||||||
TestSettings.cpp \
|
|
||||||
|
|
||||||
LOCAL_SHARED_LIBRARIES := \
|
|
||||||
liblog \
|
|
||||||
libutils \
|
|
||||||
libcutils \
|
|
||||||
libhardware \
|
|
||||||
libcamera_metadata \
|
|
||||||
libcameraservice \
|
|
||||||
libcamera_client \
|
|
||||||
libgui \
|
|
||||||
libsync \
|
|
||||||
libui \
|
|
||||||
libdl \
|
|
||||||
android.hardware.camera.device@3.2
|
|
||||||
|
|
||||||
LOCAL_C_INCLUDES += \
|
|
||||||
system/media/camera/include \
|
|
||||||
system/media/private/camera/include \
|
|
||||||
frameworks/av/include/ \
|
|
||||||
frameworks/av/services/camera/libcameraservice \
|
|
||||||
frameworks/native/include \
|
|
||||||
|
|
||||||
LOCAL_CFLAGS += -Wall -Wextra
|
|
||||||
LOCAL_MODULE:= camera2_test
|
|
||||||
LOCAL_MODULE_STEM_32 := camera2_test
|
|
||||||
LOCAL_MODULE_STEM_64 := camera2_test64
|
|
||||||
LOCAL_MULTILIB := both
|
|
||||||
LOCAL_MODULE_TAGS := tests
|
|
||||||
|
|
||||||
include $(BUILD_NATIVE_TEST)
|
|
|
@ -1,731 +0,0 @@
|
||||||
/*
|
|
||||||
* 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,140 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 "CameraFrameTest"
|
|
||||||
//#define LOG_NDEBUG 0
|
|
||||||
#include <utils/Log.h>
|
|
||||||
|
|
||||||
#include "hardware/hardware.h"
|
|
||||||
#include "hardware/camera2.h"
|
|
||||||
|
|
||||||
#include <common/CameraDeviceBase.h>
|
|
||||||
#include <utils/StrongPointer.h>
|
|
||||||
#include <gui/CpuConsumer.h>
|
|
||||||
#include <gui/Surface.h>
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#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_FRAME_DEBUGGING 0
|
|
||||||
|
|
||||||
using namespace android;
|
|
||||||
using namespace android::camera2;
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace camera2 {
|
|
||||||
namespace tests {
|
|
||||||
|
|
||||||
static CameraStreamParams STREAM_PARAMETERS = {
|
|
||||||
/*mFormat*/ CAMERA_STREAM_AUTO_CPU_FORMAT,
|
|
||||||
/*mHeapCount*/ CAMERA_HEAP_COUNT
|
|
||||||
};
|
|
||||||
|
|
||||||
class CameraFrameTest
|
|
||||||
: public ::testing::TestWithParam<int>,
|
|
||||||
public CameraStreamFixture {
|
|
||||||
|
|
||||||
public:
|
|
||||||
CameraFrameTest() : CameraStreamFixture(STREAM_PARAMETERS) {
|
|
||||||
TEST_EXTENSION_FORKING_CONSTRUCTOR;
|
|
||||||
|
|
||||||
if (!HasFatalFailure()) {
|
|
||||||
CreateStream();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~CameraFrameTest() {
|
|
||||||
TEST_EXTENSION_FORKING_DESTRUCTOR;
|
|
||||||
|
|
||||||
if (mDevice.get()) {
|
|
||||||
mDevice->waitUntilDrained();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void SetUp() {
|
|
||||||
TEST_EXTENSION_FORKING_SET_UP;
|
|
||||||
}
|
|
||||||
virtual void TearDown() {
|
|
||||||
TEST_EXTENSION_FORKING_TEAR_DOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_P(CameraFrameTest, GetFrame) {
|
|
||||||
|
|
||||||
TEST_EXTENSION_FORKING_INIT;
|
|
||||||
|
|
||||||
/* Submit a PREVIEW type request, then wait until we get the frame back */
|
|
||||||
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));
|
|
||||||
if (CAMERA_FRAME_DEBUGGING) {
|
|
||||||
int frameCount = 0;
|
|
||||||
ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_FRAME_COUNT,
|
|
||||||
&frameCount, 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CAMERA_FRAME_DEBUGGING) {
|
|
||||||
previewRequest.dump(STDOUT_FILENO);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < GetParam(); ++i) {
|
|
||||||
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));
|
|
||||||
|
|
||||||
CaptureResult result;
|
|
||||||
ASSERT_EQ(OK, mDevice->getNextResult(&result));
|
|
||||||
|
|
||||||
// wait for buffer to be available
|
|
||||||
ASSERT_EQ(OK, mFrameListener->waitForFrame(CAMERA_FRAME_TIMEOUT));
|
|
||||||
ALOGV("We got the frame now");
|
|
||||||
|
|
||||||
// mark buffer consumed so producer can re-dequeue it
|
|
||||||
CpuConsumer::LockedBuffer imgBuffer;
|
|
||||||
ASSERT_EQ(OK, mCpuConsumer->lockNextBuffer(&imgBuffer));
|
|
||||||
ASSERT_EQ(OK, mCpuConsumer->unlockBuffer(imgBuffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//FIXME: dont hardcode stream params, and also test multistream
|
|
||||||
INSTANTIATE_TEST_CASE_P(FrameParameterCombinations, CameraFrameTest,
|
|
||||||
testing::Range(1, 10));
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,200 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LOG_NDEBUG 0
|
|
||||||
#define LOG_TAG "CameraMetadataTestFunctional"
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "cutils/properties.h"
|
|
||||||
#include "log/log.h"
|
|
||||||
#include "utils/Errors.h"
|
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#include "system/camera_metadata.h"
|
|
||||||
#include "hardware/hardware.h"
|
|
||||||
#include "hardware/camera2.h"
|
|
||||||
|
|
||||||
#include "common/CameraDeviceBase.h"
|
|
||||||
#include "utils/StrongPointer.h"
|
|
||||||
|
|
||||||
#include <gui/CpuConsumer.h>
|
|
||||||
#include <gui/Surface.h>
|
|
||||||
|
|
||||||
#include "CameraStreamFixture.h"
|
|
||||||
#include "TestExtensions.h"
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace camera2 {
|
|
||||||
namespace tests {
|
|
||||||
|
|
||||||
//FIXME: dont hardcode
|
|
||||||
static CameraStreamParams METADATA_STREAM_PARAMETERS = {
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_YCrCb_420_SP,
|
|
||||||
/*mHeapCount*/ 2
|
|
||||||
};
|
|
||||||
|
|
||||||
class CameraMetadataTest
|
|
||||||
: public ::testing::Test,
|
|
||||||
public CameraStreamFixture {
|
|
||||||
|
|
||||||
public:
|
|
||||||
CameraMetadataTest()
|
|
||||||
: CameraStreamFixture(METADATA_STREAM_PARAMETERS) {
|
|
||||||
TEST_EXTENSION_FORKING_CONSTRUCTOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
~CameraMetadataTest() {
|
|
||||||
TEST_EXTENSION_FORKING_DESTRUCTOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetTypeFromTag(uint32_t tag) const {
|
|
||||||
return get_camera_metadata_tag_type(tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetTypeFromStaticTag(uint32_t tag) const {
|
|
||||||
const CameraMetadata& staticInfo = mDevice->info();
|
|
||||||
camera_metadata_ro_entry entry = staticInfo.find(tag);
|
|
||||||
return entry.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetEntryCountFromStaticTag(uint32_t tag) const {
|
|
||||||
const CameraMetadata& staticInfo = mDevice->info();
|
|
||||||
camera_metadata_ro_entry entry = staticInfo.find(tag);
|
|
||||||
return entry.count;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasElementInArrayFromStaticTag(uint32_t tag, int32_t element) const {
|
|
||||||
const CameraMetadata& staticInfo = mDevice->info();
|
|
||||||
camera_metadata_ro_entry entry = staticInfo.find(tag);
|
|
||||||
for (size_t i = 0; i < entry.count; ++i) {
|
|
||||||
if (entry.data.i32[i] == element)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_F(CameraMetadataTest, types) {
|
|
||||||
|
|
||||||
TEST_EXTENSION_FORKING_INIT;
|
|
||||||
|
|
||||||
//FIXME: set this up in an external file of some sort (xml?)
|
|
||||||
{
|
|
||||||
char value[PROPERTY_VALUE_MAX];
|
|
||||||
property_get("ro.build.id", value, "");
|
|
||||||
std::string str_value(value);
|
|
||||||
|
|
||||||
if (str_value == "manta")
|
|
||||||
{
|
|
||||||
EXPECT_EQ(TYPE_BYTE,
|
|
||||||
GetTypeFromStaticTag(ANDROID_QUIRKS_TRIGGER_AF_WITH_AUTO));
|
|
||||||
EXPECT_EQ(TYPE_BYTE,
|
|
||||||
GetTypeFromStaticTag(ANDROID_QUIRKS_USE_ZSL_FORMAT));
|
|
||||||
EXPECT_EQ(TYPE_BYTE,
|
|
||||||
GetTypeFromStaticTag(ANDROID_QUIRKS_METERING_CROP_REGION));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
TODO:
|
|
||||||
go through all static metadata and make sure all fields we expect
|
|
||||||
that are there, ARE there.
|
|
||||||
|
|
||||||
dont worry about the type as its enforced by the metadata api
|
|
||||||
we can probably check the range validity though
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (0) {
|
|
||||||
camera_metadata_ro_entry entry;
|
|
||||||
EXPECT_EQ(TYPE_BYTE, entry.type);
|
|
||||||
EXPECT_EQ(TYPE_INT32, entry.type);
|
|
||||||
EXPECT_EQ(TYPE_FLOAT, entry.type);
|
|
||||||
EXPECT_EQ(TYPE_INT64, entry.type);
|
|
||||||
EXPECT_EQ(TYPE_DOUBLE, entry.type);
|
|
||||||
EXPECT_EQ(TYPE_RATIONAL, entry.type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(CameraMetadataTest, RequiredFormats) {
|
|
||||||
TEST_EXTENSION_FORKING_INIT;
|
|
||||||
|
|
||||||
EXPECT_TRUE(
|
|
||||||
HasElementInArrayFromStaticTag(ANDROID_SCALER_AVAILABLE_FORMATS,
|
|
||||||
HAL_PIXEL_FORMAT_BLOB)); // JPEG
|
|
||||||
|
|
||||||
if (getDeviceVersion() < CAMERA_DEVICE_API_VERSION_3_0) {
|
|
||||||
// HAL2 can support either flexible YUV or YV12 + NV21
|
|
||||||
if (!HasElementInArrayFromStaticTag(ANDROID_SCALER_AVAILABLE_FORMATS,
|
|
||||||
HAL_PIXEL_FORMAT_YCbCr_420_888)) {
|
|
||||||
|
|
||||||
EXPECT_TRUE(
|
|
||||||
HasElementInArrayFromStaticTag(ANDROID_SCALER_AVAILABLE_FORMATS,
|
|
||||||
HAL_PIXEL_FORMAT_YCrCb_420_SP)); // NV21
|
|
||||||
|
|
||||||
EXPECT_TRUE(
|
|
||||||
HasElementInArrayFromStaticTag(ANDROID_SCALER_AVAILABLE_FORMATS,
|
|
||||||
HAL_PIXEL_FORMAT_YV12));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// HAL3 must support flexible YUV
|
|
||||||
EXPECT_TRUE(HasElementInArrayFromStaticTag(ANDROID_SCALER_AVAILABLE_FORMATS,
|
|
||||||
HAL_PIXEL_FORMAT_YCbCr_420_888));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(CameraMetadataTest, SaneResolutions) {
|
|
||||||
TEST_EXTENSION_FORKING_INIT;
|
|
||||||
|
|
||||||
if (getDeviceVersion() < CAMERA_DEVICE_API_VERSION_3_2) {
|
|
||||||
// Iff there are listed raw resolutions, the format should be available
|
|
||||||
int rawResolutionsCount =
|
|
||||||
GetEntryCountFromStaticTag(ANDROID_SCALER_AVAILABLE_RAW_SIZES);
|
|
||||||
if (rawResolutionsCount > 0) {
|
|
||||||
EXPECT_TRUE(
|
|
||||||
HasElementInArrayFromStaticTag(ANDROID_SCALER_AVAILABLE_FORMATS,
|
|
||||||
HAL_PIXEL_FORMAT_RAW16));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Required processed sizes.
|
|
||||||
int processedSizeCount =
|
|
||||||
GetEntryCountFromStaticTag(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES);
|
|
||||||
EXPECT_NE(0, processedSizeCount);
|
|
||||||
EXPECT_EQ(0, processedSizeCount % 2); // multiple of 2 (w,h)
|
|
||||||
|
|
||||||
// Required JPEG sizes
|
|
||||||
int jpegSizeCount =
|
|
||||||
GetEntryCountFromStaticTag(ANDROID_SCALER_AVAILABLE_JPEG_SIZES);
|
|
||||||
EXPECT_NE(0, jpegSizeCount);
|
|
||||||
EXPECT_EQ(0, jpegSizeCount % 2); // multiple of 2 (w,h)
|
|
||||||
} else {
|
|
||||||
int strmConfigCount =
|
|
||||||
GetEntryCountFromStaticTag(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
|
|
||||||
EXPECT_NE(0, strmConfigCount);
|
|
||||||
EXPECT_EQ(0, strmConfigCount % 4); // multiple of 4 (format,w,h,output?)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,161 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __ANDROID_HAL_CAMERA2_TESTS_MODULE_FIXTURE__
|
|
||||||
#define __ANDROID_HAL_CAMERA2_TESTS_MODULE_FIXTURE__
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include "hardware/hardware.h"
|
|
||||||
#include "hardware/camera2.h"
|
|
||||||
|
|
||||||
#include <common/CameraModule.h>
|
|
||||||
#include <device3/Camera3Device.h>
|
|
||||||
|
|
||||||
#include "camera2_utils.h"
|
|
||||||
#include "TestExtensions.h"
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace camera2 {
|
|
||||||
namespace tests {
|
|
||||||
|
|
||||||
template <bool InfoQuirk = false>
|
|
||||||
struct CameraModuleFixture {
|
|
||||||
|
|
||||||
explicit CameraModuleFixture(int CameraID = -1) {
|
|
||||||
TEST_EXTENSION_FORKING_CONSTRUCTOR;
|
|
||||||
|
|
||||||
mCameraID = CameraID;
|
|
||||||
}
|
|
||||||
|
|
||||||
~CameraModuleFixture() {
|
|
||||||
TEST_EXTENSION_FORKING_DESTRUCTOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
camera_metadata_ro_entry GetStaticEntry(uint32_t tag) const {
|
|
||||||
const CameraMetadata& staticInfo = mDevice->info();
|
|
||||||
camera_metadata_ro_entry entry = staticInfo.find(tag);
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetUp() {
|
|
||||||
TEST_EXTENSION_FORKING_SET_UP;
|
|
||||||
|
|
||||||
camera_module_t *rawModule;
|
|
||||||
ASSERT_LE(0, hw_get_module(CAMERA_HARDWARE_MODULE_ID,
|
|
||||||
(const hw_module_t **)&rawModule)) << "Could not load camera module";
|
|
||||||
ASSERT_NE((void*)0, rawModule);
|
|
||||||
mModule = new CameraModule(rawModule);
|
|
||||||
|
|
||||||
mNumberOfCameras = mModule->getNumberOfCameras();
|
|
||||||
ASSERT_LE(0, mNumberOfCameras);
|
|
||||||
|
|
||||||
ASSERT_LE(
|
|
||||||
CAMERA_MODULE_API_VERSION_2_0, mModule->getModuleApiVersion())
|
|
||||||
<< "Wrong module API version";
|
|
||||||
|
|
||||||
/* For using this fixture in other tests only */
|
|
||||||
SetUpMixin();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TearDown() {
|
|
||||||
TEST_EXTENSION_FORKING_TEAR_DOWN;
|
|
||||||
|
|
||||||
TearDownMixin();
|
|
||||||
|
|
||||||
/* important: device must be destructed before closing module,
|
|
||||||
since it calls back into HAL */
|
|
||||||
mDevice.clear();
|
|
||||||
|
|
||||||
if (!TEST_EXTENSION_FORKING_ENABLED) {
|
|
||||||
ASSERT_EQ(0, HWModuleHelpers::closeModule(mModule->getDso()))
|
|
||||||
<< "Failed to close camera HAL module";
|
|
||||||
}
|
|
||||||
delete mModule;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreateCamera(int cameraID, /*out*/ sp<CameraDeviceBase> *device) {
|
|
||||||
struct camera_info info;
|
|
||||||
ASSERT_EQ(OK, mModule->getCameraInfo(cameraID, &info));
|
|
||||||
|
|
||||||
ASSERT_GE((int)info.device_version, CAMERA_DEVICE_API_VERSION_3_0) <<
|
|
||||||
"Device version too old for camera " << cameraID << ". Version: " <<
|
|
||||||
info.device_version;
|
|
||||||
switch(info.device_version) {
|
|
||||||
case CAMERA_DEVICE_API_VERSION_3_0:
|
|
||||||
case CAMERA_DEVICE_API_VERSION_3_1:
|
|
||||||
case CAMERA_DEVICE_API_VERSION_3_2:
|
|
||||||
*device = new Camera3Device(String8::format("%d", cameraID));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
device->clear();
|
|
||||||
FAIL() << "Device version unknown for camera " << cameraID << ". Version: " <<
|
|
||||||
info.device_version;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int getDeviceVersion() {
|
|
||||||
return getDeviceVersion(mCameraID);
|
|
||||||
}
|
|
||||||
|
|
||||||
int getDeviceVersion(int cameraId, status_t* status = NULL) {
|
|
||||||
camera_info info;
|
|
||||||
status_t res;
|
|
||||||
res = mModule->getCameraInfo(cameraId, &info);
|
|
||||||
if (status != NULL) *status = res;
|
|
||||||
|
|
||||||
return info.device_version;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void SetUpMixin() {
|
|
||||||
/* For using this fixture in other tests only */
|
|
||||||
if (mCameraID != -1) {
|
|
||||||
EXPECT_LE(0, mCameraID);
|
|
||||||
EXPECT_LT(mCameraID, mNumberOfCameras);
|
|
||||||
|
|
||||||
/* HALBUG (Exynos5); crashes if we skip calling get_camera_info
|
|
||||||
before initializing. Need info anyway now. */
|
|
||||||
|
|
||||||
CreateCamera(mCameraID, &mDevice);
|
|
||||||
|
|
||||||
ASSERT_TRUE(mDevice != NULL) << "Failed to open device " << mCameraID;
|
|
||||||
ASSERT_EQ(OK, mDevice->initialize(mModule))
|
|
||||||
<< "Failed to initialize device " << mCameraID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TearDownMixin() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
int mNumberOfCameras;
|
|
||||||
CameraModule *mModule;
|
|
||||||
sp<CameraDeviceBase> mDevice;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int mCameraID;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,141 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 "CameraModuleTest"
|
|
||||||
#define LOG_NDEBUG 0
|
|
||||||
#include <utils/Log.h>
|
|
||||||
#include <utils/StrongPointer.h>
|
|
||||||
#include <common/CameraDeviceBase.h>
|
|
||||||
|
|
||||||
#include "hardware/hardware.h"
|
|
||||||
#include "hardware/camera2.h"
|
|
||||||
|
|
||||||
#include "CameraModuleFixture.h"
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace camera2 {
|
|
||||||
namespace tests {
|
|
||||||
|
|
||||||
class CameraModuleTest : public ::testing::Test,
|
|
||||||
public CameraModuleFixture<> {
|
|
||||||
|
|
||||||
public:
|
|
||||||
CameraModuleTest() {
|
|
||||||
CameraModuleFixture::SetUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
~CameraModuleTest() {
|
|
||||||
CameraModuleFixture::TearDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t initializeDevice(int cameraId) {
|
|
||||||
|
|
||||||
// ignore HAL1s. count as test pass
|
|
||||||
status_t stat;
|
|
||||||
if (isDeviceVersionHal2(cameraId, &stat) && stat == OK) {
|
|
||||||
stat = mDevice->initialize(mModule);
|
|
||||||
}
|
|
||||||
|
|
||||||
return stat;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isDeviceVersionHal2(int cameraId, status_t* status) {
|
|
||||||
return getDeviceVersion(cameraId, status)
|
|
||||||
>= CAMERA_DEVICE_API_VERSION_2_0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_F(CameraModuleTest, LoadModule) {
|
|
||||||
|
|
||||||
TEST_EXTENSION_FORKING_INIT;
|
|
||||||
|
|
||||||
status_t stat;
|
|
||||||
for (int i = 0; i < mNumberOfCameras; ++i) {
|
|
||||||
if (isDeviceVersionHal2(i, &stat) && stat == OK) {
|
|
||||||
CreateCamera(i, &mDevice);
|
|
||||||
ASSERT_EQ(OK, initializeDevice(i))
|
|
||||||
<< "Failed to initialize device " << i;
|
|
||||||
mDevice.clear();
|
|
||||||
} else {
|
|
||||||
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 device version is V1"
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(CameraModuleTest, LoadModuleBadIndices) {
|
|
||||||
|
|
||||||
TEST_EXTENSION_FORKING_INIT;
|
|
||||||
|
|
||||||
int idx[] = { -1, mNumberOfCameras, mNumberOfCameras + 1 };
|
|
||||||
hw_device_t *device = NULL;
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < sizeof(idx)/sizeof(idx[0]); ++i) {
|
|
||||||
String8 deviceName = String8::format("%d", idx[i]);
|
|
||||||
status_t res = mModule->open(deviceName, &device);
|
|
||||||
EXPECT_NE(OK, res);
|
|
||||||
EXPECT_EQ(-ENODEV, res)
|
|
||||||
<< "Incorrect error code when trying to open camera with invalid id "
|
|
||||||
<< deviceName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(CameraModuleTest, GetCameraInfo) {
|
|
||||||
|
|
||||||
TEST_EXTENSION_FORKING_INIT;
|
|
||||||
|
|
||||||
for (int i = 0; i < mNumberOfCameras; ++i) {
|
|
||||||
struct camera_info info;
|
|
||||||
ASSERT_EQ(OK, mModule->getCameraInfo(i, &info));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(CameraModuleTest, GetCameraInfoBadIndices) {
|
|
||||||
|
|
||||||
TEST_EXTENSION_FORKING_INIT;
|
|
||||||
|
|
||||||
int idx[] = { -1, mNumberOfCameras, mNumberOfCameras + 1 };
|
|
||||||
for (unsigned i = 0; i < sizeof(idx)/sizeof(idx[0]); ++i) {
|
|
||||||
struct camera_info info;
|
|
||||||
EXPECT_NE(OK, mModule->getCameraInfo(idx[i], &info));
|
|
||||||
EXPECT_EQ(-EINVAL, mModule->getCameraInfo(idx[i], &info))
|
|
||||||
<< "Incorrect error code for get_camera_info idx= "
|
|
||||||
<< idx[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: Additional test to add: open two cameras at once.
|
|
||||||
* (is allowed to fail, at least for now, but should not blow up)
|
|
||||||
* - open same device multiple times
|
|
||||||
* - close same device multiple times
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,713 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2013 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 <inttypes.h>
|
|
||||||
#define LOG_TAG "CameraMultiStreamTest"
|
|
||||||
//#define LOG_NDEBUG 0
|
|
||||||
#include "CameraStreamFixture.h"
|
|
||||||
#include "TestExtensions.h"
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
#include <utils/Log.h>
|
|
||||||
#include <utils/StrongPointer.h>
|
|
||||||
#include <common/CameraDeviceBase.h>
|
|
||||||
#include <hardware/hardware.h>
|
|
||||||
#include <hardware/camera2.h>
|
|
||||||
#include <gui/SurfaceComposerClient.h>
|
|
||||||
#include <gui/Surface.h>
|
|
||||||
|
|
||||||
#define DEFAULT_FRAME_DURATION 33000000LL // 33ms
|
|
||||||
#define CAMERA_HEAP_COUNT 1
|
|
||||||
#define CAMERA_EXPOSURE_FORMAT CAMERA_STREAM_AUTO_CPU_FORMAT
|
|
||||||
#define CAMERA_DISPLAY_FORMAT HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED
|
|
||||||
#define CAMERA_MULTI_STREAM_DEBUGGING 0
|
|
||||||
#define CAMERA_FRAME_TIMEOUT 1000000000LL // nsecs (1 secs)
|
|
||||||
#define PREVIEW_RENDERING_TIME_INTERVAL 200000 // in unit of us, 200ms
|
|
||||||
// 1% tolerance margin for exposure sanity check against metadata
|
|
||||||
#define TOLERANCE_MARGIN_METADATA 0.01
|
|
||||||
// 5% tolerance margin for exposure sanity check against capture times
|
|
||||||
#define TOLERANCE_MARGIN_CAPTURE 0.05
|
|
||||||
/* constants for display */
|
|
||||||
#define DISPLAY_BUFFER_HEIGHT 1024
|
|
||||||
#define DISPLAY_BUFFER_WIDTH 1024
|
|
||||||
#define DISPLAY_BUFFER_FORMAT PIXEL_FORMAT_RGB_888
|
|
||||||
|
|
||||||
// This test intends to test large preview size but less than 1080p.
|
|
||||||
#define PREVIEW_WIDTH_CAP 1920
|
|
||||||
#define PREVIEW_HEIGHT_CAP 1080
|
|
||||||
// This test intends to test small metering burst size that is less than 640x480
|
|
||||||
#define METERING_WIDTH_CAP 640
|
|
||||||
#define METERING_HEIGHT_CAP 480
|
|
||||||
|
|
||||||
#define EXP_WAIT_MULTIPLIER 2
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace camera2 {
|
|
||||||
namespace tests {
|
|
||||||
|
|
||||||
static const CameraStreamParams DEFAULT_STREAM_PARAMETERS = {
|
|
||||||
/*mFormat*/ CAMERA_EXPOSURE_FORMAT,
|
|
||||||
/*mHeapCount*/ CAMERA_HEAP_COUNT
|
|
||||||
};
|
|
||||||
|
|
||||||
static const CameraStreamParams DISPLAY_STREAM_PARAMETERS = {
|
|
||||||
/*mFormat*/ CAMERA_DISPLAY_FORMAT,
|
|
||||||
/*mHeapCount*/ CAMERA_HEAP_COUNT
|
|
||||||
};
|
|
||||||
|
|
||||||
class CameraMultiStreamTest
|
|
||||||
: public ::testing::Test,
|
|
||||||
public CameraStreamFixture {
|
|
||||||
|
|
||||||
public:
|
|
||||||
CameraMultiStreamTest() : CameraStreamFixture(DEFAULT_STREAM_PARAMETERS) {
|
|
||||||
TEST_EXTENSION_FORKING_CONSTRUCTOR;
|
|
||||||
|
|
||||||
if (HasFatalFailure()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Don't create default stream, each test is in charge of creating
|
|
||||||
* its own streams.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
~CameraMultiStreamTest() {
|
|
||||||
TEST_EXTENSION_FORKING_DESTRUCTOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
sp<SurfaceComposerClient> mComposerClient;
|
|
||||||
sp<SurfaceControl> mSurfaceControl;
|
|
||||||
|
|
||||||
void CreateOnScreenSurface(sp<Surface>& surface) {
|
|
||||||
mComposerClient = new SurfaceComposerClient;
|
|
||||||
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
|
|
||||||
|
|
||||||
mSurfaceControl = mComposerClient->createSurface(
|
|
||||||
String8("CameraMultiStreamTest StreamingImage Surface"),
|
|
||||||
DISPLAY_BUFFER_HEIGHT, DISPLAY_BUFFER_WIDTH,
|
|
||||||
DISPLAY_BUFFER_FORMAT, 0);
|
|
||||||
|
|
||||||
ASSERT_NE((void*)NULL, mSurfaceControl.get());
|
|
||||||
ASSERT_TRUE(mSurfaceControl->isValid());
|
|
||||||
|
|
||||||
SurfaceComposerClient::openGlobalTransaction();
|
|
||||||
ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF));
|
|
||||||
ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
|
|
||||||
SurfaceComposerClient::closeGlobalTransaction();
|
|
||||||
|
|
||||||
surface = mSurfaceControl->getSurface();
|
|
||||||
|
|
||||||
ASSERT_NE((void*)NULL, surface.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Size {
|
|
||||||
int32_t width;
|
|
||||||
int32_t height;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Select minimal size by number of pixels.
|
|
||||||
void GetMinSize(const int32_t* data, size_t count,
|
|
||||||
Size* min, int32_t* idx) {
|
|
||||||
ASSERT_NE((int32_t*)NULL, data);
|
|
||||||
int32_t minIdx = 0;
|
|
||||||
int32_t minSize = INT_MAX, tempSize;
|
|
||||||
for (size_t i = 0; i < count; i+=2) {
|
|
||||||
tempSize = data[i] * data[i+1];
|
|
||||||
if (minSize > tempSize) {
|
|
||||||
minSize = tempSize;
|
|
||||||
minIdx = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
min->width = data[minIdx];
|
|
||||||
min->height = data[minIdx + 1];
|
|
||||||
*idx = minIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select maximal size by number of pixels.
|
|
||||||
void GetMaxSize(const int32_t* data, size_t count,
|
|
||||||
Size* max, int32_t* idx) {
|
|
||||||
ASSERT_NE((int32_t*)NULL, data);
|
|
||||||
int32_t maxIdx = 0;
|
|
||||||
int32_t maxSize = INT_MIN, tempSize;
|
|
||||||
for (size_t i = 0; i < count; i+=2) {
|
|
||||||
tempSize = data[i] * data[i+1];
|
|
||||||
if (maxSize < tempSize) {
|
|
||||||
maxSize = tempSize;
|
|
||||||
maxIdx = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
max->width = data[maxIdx];
|
|
||||||
max->height = data[maxIdx + 1];
|
|
||||||
*idx = maxIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cap size by number of pixels.
|
|
||||||
Size CapSize(Size cap, Size input) {
|
|
||||||
if (input.width * input.height > cap.width * cap.height) {
|
|
||||||
return cap;
|
|
||||||
}
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CameraStream : public RefBase {
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Only initialize the variables here, do the ASSERT check in
|
|
||||||
* SetUp function. To make this stream useful, the SetUp must
|
|
||||||
* be called before using it.
|
|
||||||
*/
|
|
||||||
CameraStream(
|
|
||||||
int width,
|
|
||||||
int height,
|
|
||||||
const sp<CameraDeviceBase>& device,
|
|
||||||
CameraStreamParams param, sp<Surface> surface,
|
|
||||||
bool useCpuConsumer)
|
|
||||||
: mDevice(device),
|
|
||||||
mWidth(width),
|
|
||||||
mHeight(height) {
|
|
||||||
mFormat = param.mFormat;
|
|
||||||
if (useCpuConsumer) {
|
|
||||||
sp<IGraphicBufferProducer> producer;
|
|
||||||
sp<IGraphicBufferConsumer> consumer;
|
|
||||||
BufferQueue::createBufferQueue(&producer, &consumer);
|
|
||||||
mCpuConsumer = new CpuConsumer(consumer, param.mHeapCount);
|
|
||||||
mCpuConsumer->setName(String8(
|
|
||||||
"CameraMultiStreamTest::mCpuConsumer"));
|
|
||||||
mSurface = new Surface(producer);
|
|
||||||
} else {
|
|
||||||
// Render the stream to screen.
|
|
||||||
mCpuConsumer = NULL;
|
|
||||||
mSurface = surface;
|
|
||||||
}
|
|
||||||
|
|
||||||
mFrameListener = new FrameListener();
|
|
||||||
if (mCpuConsumer != 0) {
|
|
||||||
mCpuConsumer->setFrameAvailableListener(mFrameListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finally create camera stream, and do the ASSERT check, since we
|
|
||||||
* can not do it in ctor.
|
|
||||||
*/
|
|
||||||
void SetUp() {
|
|
||||||
ASSERT_EQ(OK,
|
|
||||||
mDevice->createStream(mSurface,
|
|
||||||
mWidth, mHeight, mFormat, HAL_DATASPACE_UNKNOWN,
|
|
||||||
CAMERA3_STREAM_ROTATION_0, &mStreamId));
|
|
||||||
|
|
||||||
ASSERT_NE(-1, mStreamId);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetStreamId() { return mStreamId; }
|
|
||||||
sp<CpuConsumer> GetConsumer() { return mCpuConsumer; }
|
|
||||||
sp<FrameListener> GetFrameListener() { return mFrameListener; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
~CameraStream() {
|
|
||||||
if (mDevice.get()) {
|
|
||||||
mDevice->waitUntilDrained();
|
|
||||||
mDevice->deleteStream(mStreamId);
|
|
||||||
}
|
|
||||||
// Clear producer before consumer.
|
|
||||||
mSurface.clear();
|
|
||||||
mCpuConsumer.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
sp<FrameListener> mFrameListener;
|
|
||||||
sp<CpuConsumer> mCpuConsumer;
|
|
||||||
sp<Surface> mSurface;
|
|
||||||
sp<CameraDeviceBase> mDevice;
|
|
||||||
int mStreamId;
|
|
||||||
int mWidth;
|
|
||||||
int mHeight;
|
|
||||||
int mFormat;
|
|
||||||
};
|
|
||||||
|
|
||||||
int64_t GetExposureValue(const CameraMetadata& metaData) {
|
|
||||||
camera_metadata_ro_entry_t entry =
|
|
||||||
metaData.find(ANDROID_SENSOR_EXPOSURE_TIME);
|
|
||||||
EXPECT_EQ(1u, entry.count);
|
|
||||||
if (entry.count == 1) {
|
|
||||||
return entry.data.i64[0];
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t GetSensitivity(const CameraMetadata& metaData) {
|
|
||||||
camera_metadata_ro_entry_t entry =
|
|
||||||
metaData.find(ANDROID_SENSOR_SENSITIVITY);
|
|
||||||
EXPECT_EQ(1u, entry.count);
|
|
||||||
if (entry.count == 1) {
|
|
||||||
return entry.data.i32[0];
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t GetFrameDuration(const CameraMetadata& metaData) {
|
|
||||||
camera_metadata_ro_entry_t entry =
|
|
||||||
metaData.find(ANDROID_SENSOR_FRAME_DURATION);
|
|
||||||
EXPECT_EQ(1u, entry.count);
|
|
||||||
if (entry.count == 1) {
|
|
||||||
return entry.data.i64[0];
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreateRequests(CameraMetadata& previewRequest,
|
|
||||||
CameraMetadata& meteringRequest,
|
|
||||||
CameraMetadata& captureRequest,
|
|
||||||
int previewStreamId,
|
|
||||||
int meteringStreamId,
|
|
||||||
int captureStreamId) {
|
|
||||||
int32_t requestId = 0;
|
|
||||||
Vector<int32_t> previewStreamIds;
|
|
||||||
previewStreamIds.push(previewStreamId);
|
|
||||||
ASSERT_EQ(OK, mDevice->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
|
|
||||||
&previewRequest));
|
|
||||||
ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_OUTPUT_STREAMS,
|
|
||||||
previewStreamIds));
|
|
||||||
ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_ID,
|
|
||||||
&requestId, 1));
|
|
||||||
|
|
||||||
// Create metering request, manual settings
|
|
||||||
// Manual control: Disable 3A, noise reduction, edge sharping
|
|
||||||
uint8_t cmOff = static_cast<uint8_t>(ANDROID_CONTROL_MODE_OFF);
|
|
||||||
uint8_t nrOff = static_cast<uint8_t>(ANDROID_NOISE_REDUCTION_MODE_OFF);
|
|
||||||
uint8_t sharpOff = static_cast<uint8_t>(ANDROID_EDGE_MODE_OFF);
|
|
||||||
Vector<int32_t> meteringStreamIds;
|
|
||||||
meteringStreamIds.push(meteringStreamId);
|
|
||||||
ASSERT_EQ(OK, mDevice->createDefaultRequest(
|
|
||||||
CAMERA2_TEMPLATE_PREVIEW,
|
|
||||||
&meteringRequest));
|
|
||||||
ASSERT_EQ(OK, meteringRequest.update(
|
|
||||||
ANDROID_REQUEST_OUTPUT_STREAMS,
|
|
||||||
meteringStreamIds));
|
|
||||||
ASSERT_EQ(OK, meteringRequest.update(
|
|
||||||
ANDROID_CONTROL_MODE,
|
|
||||||
&cmOff, 1));
|
|
||||||
ASSERT_EQ(OK, meteringRequest.update(
|
|
||||||
ANDROID_NOISE_REDUCTION_MODE,
|
|
||||||
&nrOff, 1));
|
|
||||||
ASSERT_EQ(OK, meteringRequest.update(
|
|
||||||
ANDROID_EDGE_MODE,
|
|
||||||
&sharpOff, 1));
|
|
||||||
|
|
||||||
// Create capture request, manual settings
|
|
||||||
Vector<int32_t> captureStreamIds;
|
|
||||||
captureStreamIds.push(captureStreamId);
|
|
||||||
ASSERT_EQ(OK, mDevice->createDefaultRequest(
|
|
||||||
CAMERA2_TEMPLATE_PREVIEW,
|
|
||||||
&captureRequest));
|
|
||||||
ASSERT_EQ(OK, captureRequest.update(
|
|
||||||
ANDROID_REQUEST_OUTPUT_STREAMS,
|
|
||||||
captureStreamIds));
|
|
||||||
ASSERT_EQ(OK, captureRequest.update(
|
|
||||||
ANDROID_CONTROL_MODE,
|
|
||||||
&cmOff, 1));
|
|
||||||
ASSERT_EQ(OK, captureRequest.update(
|
|
||||||
ANDROID_NOISE_REDUCTION_MODE,
|
|
||||||
&nrOff, 1));
|
|
||||||
ASSERT_EQ(OK, captureRequest.update(
|
|
||||||
ANDROID_EDGE_MODE,
|
|
||||||
&sharpOff, 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
sp<CameraStream> CreateStream(
|
|
||||||
int width,
|
|
||||||
int height,
|
|
||||||
const sp<CameraDeviceBase>& device,
|
|
||||||
CameraStreamParams param = DEFAULT_STREAM_PARAMETERS,
|
|
||||||
const sp<Surface>& surface = NULL,
|
|
||||||
bool useCpuConsumer = true) {
|
|
||||||
param.mFormat = MapAutoFormat(param.mFormat);
|
|
||||||
return new CameraStream(width, height, device,
|
|
||||||
param, surface, useCpuConsumer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureBurst(CameraMetadata& request, size_t requestCount,
|
|
||||||
const Vector<int64_t>& exposures,
|
|
||||||
const Vector<int32_t>& sensitivities,
|
|
||||||
const sp<CameraStream>& stream,
|
|
||||||
int64_t minFrameDuration,
|
|
||||||
int32_t* requestIdStart) {
|
|
||||||
ASSERT_EQ(OK, request.update(ANDROID_SENSOR_FRAME_DURATION,
|
|
||||||
&minFrameDuration, 1));
|
|
||||||
// Submit a series of requests with the specified exposure/gain values.
|
|
||||||
int32_t targetRequestId = *requestIdStart;
|
|
||||||
for (size_t i = 0; i < requestCount; i++) {
|
|
||||||
ASSERT_EQ(OK, request.update(ANDROID_REQUEST_ID, requestIdStart, 1));
|
|
||||||
ASSERT_EQ(OK, request.update(ANDROID_SENSOR_EXPOSURE_TIME, &exposures[i], 1));
|
|
||||||
ASSERT_EQ(OK, request.update(ANDROID_SENSOR_SENSITIVITY, &sensitivities[i], 1));
|
|
||||||
ASSERT_EQ(OK, mDevice->capture(request));
|
|
||||||
ALOGV("Submitting request with: id %d with exposure %" PRId64 ", sensitivity %d",
|
|
||||||
*requestIdStart, exposures[i], sensitivities[i]);
|
|
||||||
if (CAMERA_MULTI_STREAM_DEBUGGING) {
|
|
||||||
request.dump(STDOUT_FILENO);
|
|
||||||
}
|
|
||||||
(*requestIdStart)++;
|
|
||||||
}
|
|
||||||
// Get capture burst results.
|
|
||||||
Vector<nsecs_t> captureBurstTimes;
|
|
||||||
sp<CpuConsumer> consumer = stream->GetConsumer();
|
|
||||||
sp<FrameListener> listener = stream->GetFrameListener();
|
|
||||||
|
|
||||||
// Set wait limit based on expected frame duration.
|
|
||||||
int64_t waitLimit = CAMERA_FRAME_TIMEOUT;
|
|
||||||
for (size_t i = 0; i < requestCount; i++) {
|
|
||||||
ALOGV("Reading request result %zu", i);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Raise the timeout to be at least twice as long as the exposure
|
|
||||||
* time. to avoid a false positive when the timeout is too short.
|
|
||||||
*/
|
|
||||||
if ((exposures[i] * EXP_WAIT_MULTIPLIER) > waitLimit) {
|
|
||||||
waitLimit = exposures[i] * EXP_WAIT_MULTIPLIER;
|
|
||||||
}
|
|
||||||
|
|
||||||
CaptureResult result;
|
|
||||||
CameraMetadata frameMetadata;
|
|
||||||
int32_t resultRequestId;
|
|
||||||
do {
|
|
||||||
ASSERT_EQ(OK, mDevice->waitForNextFrame(waitLimit));
|
|
||||||
ASSERT_EQ(OK, mDevice->getNextResult(&result));
|
|
||||||
frameMetadata = result.mMetadata;
|
|
||||||
|
|
||||||
camera_metadata_entry_t resultEntry = frameMetadata.find(ANDROID_REQUEST_ID);
|
|
||||||
ASSERT_EQ(1u, resultEntry.count);
|
|
||||||
resultRequestId = resultEntry.data.i32[0];
|
|
||||||
if (CAMERA_MULTI_STREAM_DEBUGGING) {
|
|
||||||
std::cout << "capture result req id: " << resultRequestId << std::endl;
|
|
||||||
}
|
|
||||||
} while (resultRequestId != targetRequestId);
|
|
||||||
targetRequestId++;
|
|
||||||
ALOGV("Got capture burst result for request %zu", i);
|
|
||||||
|
|
||||||
// Validate capture result
|
|
||||||
if (CAMERA_MULTI_STREAM_DEBUGGING) {
|
|
||||||
frameMetadata.dump(STDOUT_FILENO);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Need revisit it to figure out an accurate margin.
|
|
||||||
int64_t resultExposure = GetExposureValue(frameMetadata);
|
|
||||||
int32_t resultSensitivity = GetSensitivity(frameMetadata);
|
|
||||||
EXPECT_LE(sensitivities[i] * (1.0 - TOLERANCE_MARGIN_METADATA), resultSensitivity);
|
|
||||||
EXPECT_GE(sensitivities[i] * (1.0 + TOLERANCE_MARGIN_METADATA), resultSensitivity);
|
|
||||||
EXPECT_LE(exposures[i] * (1.0 - TOLERANCE_MARGIN_METADATA), resultExposure);
|
|
||||||
EXPECT_GE(exposures[i] * (1.0 + TOLERANCE_MARGIN_METADATA), resultExposure);
|
|
||||||
|
|
||||||
ASSERT_EQ(OK, listener->waitForFrame(waitLimit));
|
|
||||||
captureBurstTimes.push_back(systemTime());
|
|
||||||
CpuConsumer::LockedBuffer imgBuffer;
|
|
||||||
ASSERT_EQ(OK, consumer->lockNextBuffer(&imgBuffer));
|
|
||||||
ALOGV("Got capture buffer for request %zu", i);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: Validate capture buffer. Current brightness calculation
|
|
||||||
* is too slow, it also doesn't account for saturation effects,
|
|
||||||
* which is quite common since we are going over a significant
|
|
||||||
* range of EVs. we need figure out some reliable way to validate
|
|
||||||
* buffer data.
|
|
||||||
*/
|
|
||||||
|
|
||||||
ASSERT_EQ(OK, consumer->unlockBuffer(imgBuffer));
|
|
||||||
if (i > 0) {
|
|
||||||
nsecs_t timeDelta =
|
|
||||||
captureBurstTimes[i] - captureBurstTimes[i-1];
|
|
||||||
EXPECT_GE(timeDelta * ( 1 + TOLERANCE_MARGIN_CAPTURE), exposures[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Intentionally shadow default CreateStream function from base class,
|
|
||||||
* because we don't want any test in this class to use the default
|
|
||||||
* stream creation function.
|
|
||||||
*/
|
|
||||||
void CreateStream() {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This test adds multiple stream use case test, basically, test 3
|
|
||||||
* streams:
|
|
||||||
*
|
|
||||||
* 1. Preview stream, with large size that is no bigger than 1080p
|
|
||||||
* we render this stream to display and vary the exposure time for
|
|
||||||
* for certain amount of time for visualization purpose.
|
|
||||||
*
|
|
||||||
* 2. Metering stream, with small size that is no bigger than VGA size.
|
|
||||||
* a burst is issued for different exposure times and analog gains
|
|
||||||
* (or analog gain implemented sensitivities) then check if the capture
|
|
||||||
* result metadata matches the request.
|
|
||||||
*
|
|
||||||
* 3. Capture stream, this is basically similar as meterting stream, but
|
|
||||||
* has large size, which is the largest supported JPEG capture size.
|
|
||||||
*
|
|
||||||
* This multiple stream test is to test if HAL supports:
|
|
||||||
*
|
|
||||||
* 1. Multiple streams like above, HAL should support at least 3 streams
|
|
||||||
* concurrently: one preview stream, 2 other YUV stream.
|
|
||||||
*
|
|
||||||
* 2. Manual control(gain/exposure) of mutiple burst capture.
|
|
||||||
*/
|
|
||||||
// Disable this test for now, as we need cleanup the usage of the deprecated tag quite a bit.
|
|
||||||
TEST_F(CameraMultiStreamTest, DISABLED_MultiBurst) {
|
|
||||||
|
|
||||||
TEST_EXTENSION_FORKING_INIT;
|
|
||||||
|
|
||||||
const int32_t* implDefData;
|
|
||||||
size_t implDefCount;
|
|
||||||
const int32_t* jpegData;
|
|
||||||
size_t jpegCount;
|
|
||||||
if (getDeviceVersion() < CAMERA_DEVICE_API_VERSION_3_2) {
|
|
||||||
camera_metadata_ro_entry availableProcessedSizes =
|
|
||||||
GetStaticEntry(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES);
|
|
||||||
ASSERT_EQ(0u, availableProcessedSizes.count % 2);
|
|
||||||
ASSERT_GE(availableProcessedSizes.count, 2u);
|
|
||||||
camera_metadata_ro_entry availableProcessedMinFrameDurations =
|
|
||||||
GetStaticEntry(ANDROID_SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS);
|
|
||||||
EXPECT_EQ(availableProcessedSizes.count,
|
|
||||||
availableProcessedMinFrameDurations.count * 2);
|
|
||||||
|
|
||||||
camera_metadata_ro_entry availableJpegSizes =
|
|
||||||
GetStaticEntry(ANDROID_SCALER_AVAILABLE_JPEG_SIZES);
|
|
||||||
ASSERT_EQ(0u, availableJpegSizes.count % 2);
|
|
||||||
ASSERT_GE(availableJpegSizes.count, 2u);
|
|
||||||
implDefData = availableProcessedSizes.data.i32;
|
|
||||||
implDefCount = availableProcessedSizes.count;
|
|
||||||
jpegData = availableJpegSizes.data.i32;
|
|
||||||
jpegCount = availableJpegSizes.count;
|
|
||||||
} else {
|
|
||||||
getResolutionList(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, &implDefData, &implDefCount);
|
|
||||||
ASSERT_NE(0u, implDefCount)
|
|
||||||
<< "Missing implementation defined sizes";
|
|
||||||
ASSERT_EQ(0u, implDefCount % 2);
|
|
||||||
ASSERT_GE(implDefCount, 2u);
|
|
||||||
|
|
||||||
getResolutionList(HAL_PIXEL_FORMAT_BLOB, &jpegData, &jpegCount);
|
|
||||||
ASSERT_EQ(0u, jpegCount % 2);
|
|
||||||
ASSERT_GE(jpegCount, 2u);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the right sizes for preview, metering, and capture streams
|
|
||||||
int64_t minFrameDuration = DEFAULT_FRAME_DURATION;
|
|
||||||
Size processedMinSize = {0, 0}, processedMaxSize = {0, 0};
|
|
||||||
Size jpegMaxSize = {0, 0};
|
|
||||||
|
|
||||||
int32_t minIdx, maxIdx;
|
|
||||||
GetMinSize(implDefData, implDefCount, &processedMinSize, &minIdx);
|
|
||||||
GetMaxSize(implDefData, implDefCount, &processedMaxSize, &maxIdx);
|
|
||||||
ALOGV("Found processed max size: %dx%d, min size = %dx%d",
|
|
||||||
processedMaxSize.width, processedMaxSize.height,
|
|
||||||
processedMinSize.width, processedMinSize.height);
|
|
||||||
|
|
||||||
if (getDeviceVersion() < CAMERA_DEVICE_API_VERSION_3_2) {
|
|
||||||
camera_metadata_ro_entry availableProcessedMinFrameDurations =
|
|
||||||
GetStaticEntry(ANDROID_SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS);
|
|
||||||
minFrameDuration =
|
|
||||||
availableProcessedMinFrameDurations.data.i64[maxIdx / 2];
|
|
||||||
} else {
|
|
||||||
minFrameDuration = getMinFrameDurationFor(
|
|
||||||
HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
|
|
||||||
processedMaxSize.width, processedMaxSize.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_GT(minFrameDuration, 0);
|
|
||||||
|
|
||||||
if (minFrameDuration <= 0) {
|
|
||||||
minFrameDuration = DEFAULT_FRAME_DURATION;
|
|
||||||
}
|
|
||||||
|
|
||||||
ALOGV("targeted minimal frame duration is: %" PRId64 "ns", minFrameDuration);
|
|
||||||
|
|
||||||
GetMaxSize(jpegData, jpegCount, &jpegMaxSize, &maxIdx);
|
|
||||||
ALOGV("Found Jpeg size max idx = %d", maxIdx);
|
|
||||||
|
|
||||||
// Max Jpeg size should be available in processed sizes. Use it for
|
|
||||||
// YUV capture anyway.
|
|
||||||
EXPECT_EQ(processedMaxSize.width, jpegMaxSize.width);
|
|
||||||
EXPECT_EQ(processedMaxSize.height, jpegMaxSize.height);
|
|
||||||
|
|
||||||
// Cap preview size.
|
|
||||||
Size previewLimit = { PREVIEW_WIDTH_CAP, PREVIEW_HEIGHT_CAP };
|
|
||||||
// FIXME: need make sure the previewLimit is supported by HAL.
|
|
||||||
Size previewSize = CapSize(previewLimit, processedMaxSize);
|
|
||||||
// Cap Metering size.
|
|
||||||
Size meteringLimit = { METERING_WIDTH_CAP, METERING_HEIGHT_CAP };
|
|
||||||
// Cap metering size to VGA (VGA is mandatory by CDD)
|
|
||||||
Size meteringSize = CapSize(meteringLimit, processedMinSize);
|
|
||||||
// Capture stream should be the max size of jpeg sizes.
|
|
||||||
ALOGV("preview size: %dx%d, metering size: %dx%d, capture size: %dx%d",
|
|
||||||
previewSize.width, previewSize.height,
|
|
||||||
meteringSize.width, meteringSize.height,
|
|
||||||
jpegMaxSize.width, jpegMaxSize.height);
|
|
||||||
|
|
||||||
// Create streams
|
|
||||||
// Preview stream: small resolution, render on the screen.
|
|
||||||
sp<CameraStream> previewStream;
|
|
||||||
{
|
|
||||||
sp<Surface> surface;
|
|
||||||
ASSERT_NO_FATAL_FAILURE(CreateOnScreenSurface(/*out*/surface));
|
|
||||||
previewStream = CreateStream(
|
|
||||||
previewSize.width,
|
|
||||||
previewSize.height,
|
|
||||||
mDevice,
|
|
||||||
DISPLAY_STREAM_PARAMETERS,
|
|
||||||
surface,
|
|
||||||
false);
|
|
||||||
ASSERT_NE((void*)NULL, previewStream.get());
|
|
||||||
ASSERT_NO_FATAL_FAILURE(previewStream->SetUp());
|
|
||||||
}
|
|
||||||
// Metering burst stream: small resolution yuv stream
|
|
||||||
sp<CameraStream> meteringStream =
|
|
||||||
CreateStream(
|
|
||||||
meteringSize.width,
|
|
||||||
meteringSize.height,
|
|
||||||
mDevice);
|
|
||||||
ASSERT_NE((void*)NULL, meteringStream.get());
|
|
||||||
ASSERT_NO_FATAL_FAILURE(meteringStream->SetUp());
|
|
||||||
// Capture burst stream: full resolution yuv stream
|
|
||||||
sp<CameraStream> captureStream =
|
|
||||||
CreateStream(
|
|
||||||
jpegMaxSize.width,
|
|
||||||
jpegMaxSize.height,
|
|
||||||
mDevice);
|
|
||||||
ASSERT_NE((void*)NULL, captureStream.get());
|
|
||||||
ASSERT_NO_FATAL_FAILURE(captureStream->SetUp());
|
|
||||||
|
|
||||||
// Create Preview request.
|
|
||||||
CameraMetadata previewRequest, meteringRequest, captureRequest;
|
|
||||||
ASSERT_NO_FATAL_FAILURE(CreateRequests(previewRequest, meteringRequest,
|
|
||||||
captureRequest, previewStream->GetStreamId(),
|
|
||||||
meteringStream->GetStreamId(), captureStream->GetStreamId()));
|
|
||||||
|
|
||||||
// Start preview
|
|
||||||
if (CAMERA_MULTI_STREAM_DEBUGGING) {
|
|
||||||
previewRequest.dump(STDOUT_FILENO);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate exposure and sensitivity lists
|
|
||||||
camera_metadata_ro_entry exposureTimeRange =
|
|
||||||
GetStaticEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE);
|
|
||||||
ASSERT_EQ(exposureTimeRange.count, 2u);
|
|
||||||
int64_t minExp = exposureTimeRange.data.i64[0];
|
|
||||||
int64_t maxExp = exposureTimeRange.data.i64[1];
|
|
||||||
ASSERT_GT(maxExp, minExp);
|
|
||||||
|
|
||||||
camera_metadata_ro_entry sensivityRange =
|
|
||||||
GetStaticEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE);
|
|
||||||
ASSERT_EQ(2u, sensivityRange.count);
|
|
||||||
int32_t minSensitivity = sensivityRange.data.i32[0];
|
|
||||||
int32_t maxSensitivity = sensivityRange.data.i32[1];
|
|
||||||
camera_metadata_ro_entry maxAnalogSenEntry =
|
|
||||||
GetStaticEntry(ANDROID_SENSOR_MAX_ANALOG_SENSITIVITY);
|
|
||||||
EXPECT_EQ(1u, maxAnalogSenEntry.count);
|
|
||||||
int32_t maxAnalogSensitivity = maxAnalogSenEntry.data.i32[0];
|
|
||||||
EXPECT_LE(maxAnalogSensitivity, maxSensitivity);
|
|
||||||
// Only test the sensitivity implemented by analog gain.
|
|
||||||
if (maxAnalogSensitivity > maxSensitivity) {
|
|
||||||
// Fallback to maxSensitity
|
|
||||||
maxAnalogSensitivity = maxSensitivity;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sensitivity list, only include the sensitivities that are implemented
|
|
||||||
// purely by analog gain if possible.
|
|
||||||
Vector<int32_t> sensitivities;
|
|
||||||
Vector<int64_t> exposures;
|
|
||||||
size_t count = (maxAnalogSensitivity - minSensitivity + 99) / 100;
|
|
||||||
sensitivities.push_back(minSensitivity);
|
|
||||||
for (size_t i = 1; i < count; i++) {
|
|
||||||
sensitivities.push_back(minSensitivity + i * 100);
|
|
||||||
}
|
|
||||||
sensitivities.push_back(maxAnalogSensitivity);
|
|
||||||
ALOGV("Sensitivity Range: min=%d, max=%d", minSensitivity,
|
|
||||||
maxAnalogSensitivity);
|
|
||||||
int64_t exp = minExp;
|
|
||||||
while (exp < maxExp) {
|
|
||||||
exposures.push_back(exp);
|
|
||||||
exp *= 2;
|
|
||||||
}
|
|
||||||
// Sweep the exposure value for preview, just for visual inspection purpose.
|
|
||||||
uint8_t cmOff = static_cast<uint8_t>(ANDROID_CONTROL_MODE_OFF);
|
|
||||||
for (size_t i = 0; i < exposures.size(); i++) {
|
|
||||||
ASSERT_EQ(OK, previewRequest.update(
|
|
||||||
ANDROID_CONTROL_MODE,
|
|
||||||
&cmOff, 1));
|
|
||||||
ASSERT_EQ(OK, previewRequest.update(
|
|
||||||
ANDROID_SENSOR_EXPOSURE_TIME,
|
|
||||||
&exposures[i], 1));
|
|
||||||
ALOGV("Submitting preview request %zu with exposure %" PRId64,
|
|
||||||
i, exposures[i]);
|
|
||||||
|
|
||||||
ASSERT_EQ(OK, mDevice->setStreamingRequest(previewRequest));
|
|
||||||
|
|
||||||
// Let preview run 200ms on screen for each exposure time.
|
|
||||||
usleep(PREVIEW_RENDERING_TIME_INTERVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t requestCount = sensitivities.size();
|
|
||||||
if (requestCount > exposures.size()) {
|
|
||||||
requestCount = exposures.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
// To maintain the request id uniqueness (preview request id is 0), make burst capture start
|
|
||||||
// request id 1 here.
|
|
||||||
int32_t requestIdStart = 1;
|
|
||||||
/**
|
|
||||||
* Submit metering request, set default frame duration to minimal possible
|
|
||||||
* value, we want the capture to run as fast as possible. HAL should adjust
|
|
||||||
* the frame duration to minimal necessary value to support the requested
|
|
||||||
* exposure value if exposure is larger than frame duration.
|
|
||||||
*/
|
|
||||||
CaptureBurst(meteringRequest, requestCount, exposures, sensitivities,
|
|
||||||
meteringStream, minFrameDuration, &requestIdStart);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Submit capture request, set default frame duration to minimal possible
|
|
||||||
* value, we want the capture to run as fast as possible. HAL should adjust
|
|
||||||
* the frame duration to minimal necessary value to support the requested
|
|
||||||
* exposure value if exposure is larger than frame duration.
|
|
||||||
*/
|
|
||||||
CaptureBurst(captureRequest, requestCount, exposures, sensitivities,
|
|
||||||
captureStream, minFrameDuration, &requestIdStart);
|
|
||||||
|
|
||||||
ASSERT_EQ(OK, mDevice->clearStreamingRequest());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,377 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __ANDROID_HAL_CAMERA2_TESTS_STREAM_FIXTURE__
|
|
||||||
#define __ANDROID_HAL_CAMERA2_TESTS_STREAM_FIXTURE__
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#include <gui/CpuConsumer.h>
|
|
||||||
#include <gui/Surface.h>
|
|
||||||
#include <utils/Condition.h>
|
|
||||||
#include <utils/Mutex.h>
|
|
||||||
#include <system/camera_metadata.h>
|
|
||||||
|
|
||||||
#include "CameraModuleFixture.h"
|
|
||||||
#include "TestExtensions.h"
|
|
||||||
|
|
||||||
#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace camera2 {
|
|
||||||
namespace tests {
|
|
||||||
|
|
||||||
// Format specifier for picking the best format for CPU reading the given device
|
|
||||||
// version
|
|
||||||
#define CAMERA_STREAM_AUTO_CPU_FORMAT (-1)
|
|
||||||
|
|
||||||
struct CameraStreamParams;
|
|
||||||
|
|
||||||
void PrintTo(const CameraStreamParams& p, ::std::ostream* os);
|
|
||||||
|
|
||||||
struct CameraStreamParams {
|
|
||||||
int mFormat;
|
|
||||||
int mHeapCount;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
inline ::std::ostream& operator<<(::std::ostream& os, const CameraStreamParams &p) {
|
|
||||||
PrintTo(p, &os);
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void PrintTo(const CameraStreamParams& p, ::std::ostream* os) {
|
|
||||||
char fmt[100];
|
|
||||||
camera_metadata_enum_snprint(
|
|
||||||
ANDROID_SCALER_AVAILABLE_FORMATS, p.mFormat, fmt, sizeof(fmt));
|
|
||||||
|
|
||||||
*os << "{ ";
|
|
||||||
*os << "Format: 0x" << std::hex << p.mFormat << ", ";
|
|
||||||
*os << "Format name: " << fmt << ", ";
|
|
||||||
*os << "HeapCount: " << p.mHeapCount;
|
|
||||||
*os << " }";
|
|
||||||
}
|
|
||||||
|
|
||||||
class CameraStreamFixture
|
|
||||||
: public CameraModuleFixture</*InfoQuirk*/true> {
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit CameraStreamFixture(CameraStreamParams p)
|
|
||||||
: CameraModuleFixture(TestSettings::DeviceId()) {
|
|
||||||
TEST_EXTENSION_FORKING_CONSTRUCTOR;
|
|
||||||
|
|
||||||
mParam = p;
|
|
||||||
|
|
||||||
SetUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
~CameraStreamFixture() {
|
|
||||||
TEST_EXTENSION_FORKING_DESTRUCTOR;
|
|
||||||
|
|
||||||
TearDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void SetUp() {
|
|
||||||
TEST_EXTENSION_FORKING_SET_UP;
|
|
||||||
|
|
||||||
CameraModuleFixture::SetUp();
|
|
||||||
|
|
||||||
sp<CameraDeviceBase> device = mDevice;
|
|
||||||
|
|
||||||
/* use an arbitrary w,h */
|
|
||||||
if (getDeviceVersion() < CAMERA_DEVICE_API_VERSION_3_2) {
|
|
||||||
const int tag = ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES;
|
|
||||||
|
|
||||||
const CameraMetadata& staticInfo = device->info();
|
|
||||||
camera_metadata_ro_entry entry = staticInfo.find(tag);
|
|
||||||
ASSERT_NE(0u, entry.count)
|
|
||||||
<< "Missing tag android.scaler.availableProcessedSizes";
|
|
||||||
|
|
||||||
ASSERT_LE(2u, entry.count);
|
|
||||||
/* this seems like it would always be the smallest w,h
|
|
||||||
but we actually make no contract that it's sorted asc */
|
|
||||||
mWidth = entry.data.i32[0];
|
|
||||||
mHeight = entry.data.i32[1];
|
|
||||||
} else {
|
|
||||||
buildOutputResolutions();
|
|
||||||
const int32_t *implDefResolutions = NULL;
|
|
||||||
size_t implDefResolutionsCount;
|
|
||||||
|
|
||||||
int format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
|
|
||||||
|
|
||||||
getResolutionList(format,
|
|
||||||
&implDefResolutions, &implDefResolutionsCount);
|
|
||||||
ASSERT_NE(0u, implDefResolutionsCount)
|
|
||||||
<< "Missing implementation defined sizes";
|
|
||||||
mWidth = implDefResolutions[0];
|
|
||||||
mHeight = implDefResolutions[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void TearDown() {
|
|
||||||
TEST_EXTENSION_FORKING_TEAR_DOWN;
|
|
||||||
|
|
||||||
// important: shut down HAL before releasing streams
|
|
||||||
CameraModuleFixture::TearDown();
|
|
||||||
|
|
||||||
deleteOutputResolutions();
|
|
||||||
mSurface.clear();
|
|
||||||
mCpuConsumer.clear();
|
|
||||||
mFrameListener.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
int64_t getMinFrameDurationFor(int32_t format, int32_t width, int32_t height) {
|
|
||||||
int64_t minFrameDuration = -1L;
|
|
||||||
const int tag = ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS;
|
|
||||||
sp<CameraDeviceBase> device = mDevice;
|
|
||||||
const CameraMetadata& staticInfo = device->info();
|
|
||||||
camera_metadata_ro_entry_t availableMinDurations = staticInfo.find(tag);
|
|
||||||
for (uint32_t i = 0; i < availableMinDurations.count; i += 4) {
|
|
||||||
if (format == availableMinDurations.data.i64[i] &&
|
|
||||||
width == availableMinDurations.data.i64[i + 1] &&
|
|
||||||
height == availableMinDurations.data.i64[i + 2]) {
|
|
||||||
minFrameDuration = availableMinDurations.data.i64[i + 3];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return minFrameDuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
void buildOutputResolutions() {
|
|
||||||
if (getDeviceVersion() < CAMERA_DEVICE_API_VERSION_3_2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mOutputResolutions.isEmpty()) {
|
|
||||||
const int tag = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
|
|
||||||
const CameraMetadata& staticInfo = mDevice->info();
|
|
||||||
camera_metadata_ro_entry_t availableStrmConfigs = staticInfo.find(tag);
|
|
||||||
ASSERT_EQ(0u, availableStrmConfigs.count % 4);
|
|
||||||
for (uint32_t i = 0; i < availableStrmConfigs.count; i += 4) {
|
|
||||||
int32_t format = availableStrmConfigs.data.i32[i];
|
|
||||||
int32_t width = availableStrmConfigs.data.i32[i + 1];
|
|
||||||
int32_t height = availableStrmConfigs.data.i32[i + 2];
|
|
||||||
int32_t inOrOut = availableStrmConfigs.data.i32[i + 3];
|
|
||||||
if (inOrOut == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) {
|
|
||||||
int index = mOutputResolutions.indexOfKey(format);
|
|
||||||
if (index < 0) {
|
|
||||||
index = mOutputResolutions.add(format, new Vector<int32_t>());
|
|
||||||
ASSERT_TRUE(index >= 0);
|
|
||||||
}
|
|
||||||
Vector<int32_t> *resolutions = mOutputResolutions.editValueAt(index);
|
|
||||||
resolutions->add(width);
|
|
||||||
resolutions->add(height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void getResolutionList(int32_t format,
|
|
||||||
const int32_t **list,
|
|
||||||
size_t *count) {
|
|
||||||
ALOGV("Getting resolutions for format %x", format);
|
|
||||||
if (getDeviceVersion() < CAMERA_DEVICE_API_VERSION_3_2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int index = mOutputResolutions.indexOfKey(format);
|
|
||||||
ASSERT_TRUE(index >= 0);
|
|
||||||
Vector<int32_t>* resolutions = mOutputResolutions.valueAt(index);
|
|
||||||
*list = resolutions->array();
|
|
||||||
*count = resolutions->size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void deleteOutputResolutions() {
|
|
||||||
for (uint32_t i = 0; i < mOutputResolutions.size(); i++) {
|
|
||||||
Vector<int32_t>* resolutions = mOutputResolutions.editValueAt(i);
|
|
||||||
delete resolutions;
|
|
||||||
}
|
|
||||||
mOutputResolutions.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FrameListener : public ConsumerBase::FrameAvailableListener {
|
|
||||||
|
|
||||||
FrameListener() {
|
|
||||||
mPendingFrames = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// CpuConsumer::FrameAvailableListener implementation
|
|
||||||
virtual void onFrameAvailable(const BufferItem& /* item */) {
|
|
||||||
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<CameraDeviceBase> device = mDevice;
|
|
||||||
CameraStreamParams p = mParam;
|
|
||||||
|
|
||||||
sp<IGraphicBufferProducer> producer;
|
|
||||||
sp<IGraphicBufferConsumer> consumer;
|
|
||||||
BufferQueue::createBufferQueue(&producer, &consumer);
|
|
||||||
mCpuConsumer = new CpuConsumer(consumer, p.mHeapCount);
|
|
||||||
mCpuConsumer->setName(String8("CameraStreamTest::mCpuConsumer"));
|
|
||||||
|
|
||||||
mSurface = new Surface(producer);
|
|
||||||
|
|
||||||
int format = MapAutoFormat(p.mFormat);
|
|
||||||
|
|
||||||
ASSERT_EQ(OK,
|
|
||||||
device->createStream(mSurface,
|
|
||||||
mWidth, mHeight, format,
|
|
||||||
HAL_DATASPACE_UNKNOWN,
|
|
||||||
CAMERA3_STREAM_ROTATION_0,
|
|
||||||
&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() {
|
|
||||||
ASSERT_EQ(OK, mDevice->deleteStream(mStreamId));
|
|
||||||
}
|
|
||||||
|
|
||||||
int MapAutoFormat(int format) {
|
|
||||||
if (format == CAMERA_STREAM_AUTO_CPU_FORMAT) {
|
|
||||||
if (getDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_0) {
|
|
||||||
format = HAL_PIXEL_FORMAT_YCbCr_420_888;
|
|
||||||
} else {
|
|
||||||
format = HAL_PIXEL_FORMAT_YCrCb_420_SP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DumpYuvToFile(const String8 &fileName, const CpuConsumer::LockedBuffer &img) {
|
|
||||||
uint8_t *dataCb, *dataCr;
|
|
||||||
uint32_t stride;
|
|
||||||
uint32_t chromaStride;
|
|
||||||
uint32_t chromaStep;
|
|
||||||
|
|
||||||
switch (img.format) {
|
|
||||||
case HAL_PIXEL_FORMAT_YCbCr_420_888:
|
|
||||||
stride = img.stride;
|
|
||||||
chromaStride = img.chromaStride;
|
|
||||||
chromaStep = img.chromaStep;
|
|
||||||
dataCb = img.dataCb;
|
|
||||||
dataCr = img.dataCr;
|
|
||||||
break;
|
|
||||||
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
|
|
||||||
stride = img.width;
|
|
||||||
chromaStride = img.width;
|
|
||||||
chromaStep = 2;
|
|
||||||
dataCr = img.data + img.width * img.height;
|
|
||||||
dataCb = dataCr + 1;
|
|
||||||
break;
|
|
||||||
case HAL_PIXEL_FORMAT_YV12:
|
|
||||||
stride = img.stride;
|
|
||||||
chromaStride = ALIGN(img.width / 2, 16);
|
|
||||||
chromaStep = 1;
|
|
||||||
dataCr = img.data + img.stride * img.height;
|
|
||||||
dataCb = dataCr + chromaStride * img.height/2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ALOGE("Unknown format %d, not dumping", img.format);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write Y
|
|
||||||
FILE *yuvFile = fopen(fileName.string(), "w");
|
|
||||||
|
|
||||||
size_t bytes;
|
|
||||||
|
|
||||||
for (size_t y = 0; y < img.height; ++y) {
|
|
||||||
bytes = fwrite(
|
|
||||||
reinterpret_cast<const char*>(img.data + stride * y),
|
|
||||||
1, img.width, yuvFile);
|
|
||||||
if (bytes != img.width) {
|
|
||||||
ALOGE("Unable to write to file %s", fileName.string());
|
|
||||||
fclose(yuvFile);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write Cb/Cr
|
|
||||||
uint8_t *src = dataCb;
|
|
||||||
for (int c = 0; c < 2; ++c) {
|
|
||||||
for (size_t y = 0; y < img.height / 2; ++y) {
|
|
||||||
uint8_t *px = src + y * chromaStride;
|
|
||||||
if (chromaStep != 1) {
|
|
||||||
for (size_t x = 0; x < img.width / 2; ++x) {
|
|
||||||
fputc(*px, yuvFile);
|
|
||||||
px += chromaStep;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bytes = fwrite(reinterpret_cast<const char*>(px),
|
|
||||||
1, img.width / 2, yuvFile);
|
|
||||||
if (bytes != img.width / 2) {
|
|
||||||
ALOGE("Unable to write to file %s", fileName.string());
|
|
||||||
fclose(yuvFile);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
src = dataCr;
|
|
||||||
}
|
|
||||||
fclose(yuvFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mWidth;
|
|
||||||
int mHeight;
|
|
||||||
|
|
||||||
int mStreamId;
|
|
||||||
|
|
||||||
android::sp<FrameListener> mFrameListener;
|
|
||||||
android::sp<CpuConsumer> mCpuConsumer;
|
|
||||||
android::sp<Surface> mSurface;
|
|
||||||
KeyedVector<int32_t, Vector<int32_t>* > mOutputResolutions;
|
|
||||||
|
|
||||||
private:
|
|
||||||
CameraStreamParams mParam;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,186 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 <iostream>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#define LOG_TAG "CameraStreamTest"
|
|
||||||
#define LOG_NDEBUG 0
|
|
||||||
#include <utils/Log.h>
|
|
||||||
|
|
||||||
#include "hardware/hardware.h"
|
|
||||||
#include "hardware/camera2.h"
|
|
||||||
|
|
||||||
#include <utils/StrongPointer.h>
|
|
||||||
#include <gui/CpuConsumer.h>
|
|
||||||
#include <gui/Surface.h>
|
|
||||||
|
|
||||||
#include "CameraStreamFixture.h"
|
|
||||||
#include "TestExtensions.h"
|
|
||||||
|
|
||||||
using namespace android;
|
|
||||||
using namespace android::camera2;
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace camera2 {
|
|
||||||
namespace tests {
|
|
||||||
|
|
||||||
class CameraStreamTest
|
|
||||||
: public ::testing::TestWithParam<CameraStreamParams>,
|
|
||||||
public CameraStreamFixture {
|
|
||||||
|
|
||||||
public:
|
|
||||||
CameraStreamTest() : CameraStreamFixture(GetParam()) {
|
|
||||||
TEST_EXTENSION_FORKING_CONSTRUCTOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
~CameraStreamTest() {
|
|
||||||
TEST_EXTENSION_FORKING_DESTRUCTOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void SetUp() {
|
|
||||||
TEST_EXTENSION_FORKING_SET_UP;
|
|
||||||
}
|
|
||||||
virtual void TearDown() {
|
|
||||||
TEST_EXTENSION_FORKING_TEAR_DOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_P(CameraStreamTest, CreateStream) {
|
|
||||||
|
|
||||||
TEST_EXTENSION_FORKING_INIT;
|
|
||||||
|
|
||||||
/** Make sure the format requested is supported. PASS this test if it's not
|
|
||||||
* not supported.
|
|
||||||
*
|
|
||||||
* TODO: would be nice of not running this test in the first place
|
|
||||||
* somehow.
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
camera_metadata_ro_entry availableFormats =
|
|
||||||
GetStaticEntry(ANDROID_SCALER_AVAILABLE_FORMATS);
|
|
||||||
|
|
||||||
bool hasFormat = false;
|
|
||||||
for (size_t i = 0; i < availableFormats.count; ++i) {
|
|
||||||
if (availableFormats.data.i32[i] == GetParam().mFormat) {
|
|
||||||
hasFormat = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasFormat) {
|
|
||||||
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 the format was not available: "
|
|
||||||
<< GetParam() << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_NO_FATAL_FAILURE(CreateStream());
|
|
||||||
ASSERT_NO_FATAL_FAILURE(DeleteStream());
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: use a combinatoric generator
|
|
||||||
static CameraStreamParams TestParameters[] = {
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
|
|
||||||
/*mHeapCount*/ 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
|
|
||||||
/*mHeapCount*/ 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
|
|
||||||
/*mHeapCount*/ 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_YCrCb_420_SP, // NV21
|
|
||||||
/*mHeapCount*/ 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_YCrCb_420_SP,
|
|
||||||
/*mHeapCount*/ 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_YCrCb_420_SP,
|
|
||||||
/*mHeapCount*/ 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_YV12,
|
|
||||||
/*mHeapCount*/ 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_YV12,
|
|
||||||
/*mHeapCount*/ 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_YV12,
|
|
||||||
/*mHeapCount*/ 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_Y8,
|
|
||||||
/*mHeapCount*/ 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_Y8,
|
|
||||||
/*mHeapCount*/ 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_Y8,
|
|
||||||
/*mHeapCount*/ 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_Y16,
|
|
||||||
/*mHeapCount*/ 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_Y16,
|
|
||||||
/*mHeapCount*/ 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_Y16,
|
|
||||||
/*mHeapCount*/ 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_RAW16,
|
|
||||||
/*mHeapCount*/ 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_RAW16,
|
|
||||||
/*mHeapCount*/ 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/*mFormat*/ HAL_PIXEL_FORMAT_RAW16,
|
|
||||||
/*mHeapCount*/ 3
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(StreamParameterCombinations, CameraStreamTest,
|
|
||||||
testing::ValuesIn(TestParameters));
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 <stdlib.h>
|
|
||||||
|
|
||||||
#include "TestExtensions.h"
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace camera2 {
|
|
||||||
namespace tests {
|
|
||||||
|
|
||||||
// Intentionally disabled since 2 of these tests are supposed to fail
|
|
||||||
class DISABLED_ForkedTest : public ::testing::Test {
|
|
||||||
|
|
||||||
virtual void SetUp() {
|
|
||||||
TEST_EXTENSION_FORKING_SET_UP;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void TearDown() {
|
|
||||||
TEST_EXTENSION_FORKING_TEAR_DOWN;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// intentionally fail
|
|
||||||
TEST_F(DISABLED_ForkedTest, FailCrash) {
|
|
||||||
TEST_EXTENSION_FORKING_INIT;
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(DISABLED_ForkedTest, SucceedNormal) {
|
|
||||||
TEST_EXTENSION_FORKING_INIT;
|
|
||||||
|
|
||||||
EXPECT_TRUE(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// intentionally fail
|
|
||||||
TEST_F(DISABLED_ForkedTest, FailNormal) {
|
|
||||||
TEST_EXTENSION_FORKING_INIT;
|
|
||||||
|
|
||||||
EXPECT_TRUE(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __ANDROID_HAL_CAMERA2_TESTS_EXTENSIONS__
|
|
||||||
#define __ANDROID_HAL_CAMERA2_TESTS_EXTENSIONS__
|
|
||||||
|
|
||||||
#include "TestForkerEventListener.h"
|
|
||||||
#include "TestSettings.h"
|
|
||||||
|
|
||||||
// Use at the beginning of each Test::SetUp() impl
|
|
||||||
#define TEST_EXTENSION_FORKING_SET_UP \
|
|
||||||
do { \
|
|
||||||
if (TEST_EXTENSION_FORKING_ENABLED) { \
|
|
||||||
if (!TestForkerEventListener::mIsForked) { \
|
|
||||||
return; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
} while (false) \
|
|
||||||
|
|
||||||
// Use at the beginning of each Test::TearDown() impl
|
|
||||||
#define TEST_EXTENSION_FORKING_TEAR_DOWN TEST_EXTENSION_FORKING_SET_UP
|
|
||||||
|
|
||||||
// Use at the beginning of each Test::Test constructor
|
|
||||||
#define TEST_EXTENSION_FORKING_CONSTRUCTOR TEST_EXTENSION_FORKING_SET_UP
|
|
||||||
|
|
||||||
// Use at the beginning of each Test::~Test destructor
|
|
||||||
#define TEST_EXTENSION_FORKING_DESTRUCTOR TEST_EXTENSION_FORKING_TEAR_DOWN
|
|
||||||
|
|
||||||
// Use at the beginning of each test body, e.g. TEST(x,y), TEST_F(x,y), etc
|
|
||||||
#define TEST_EXTENSION_FORKING_INIT \
|
|
||||||
do { \
|
|
||||||
TEST_EXTENSION_FORKING_SET_UP; \
|
|
||||||
if (HasFatalFailure()) return; \
|
|
||||||
} while(false) \
|
|
||||||
|
|
||||||
// Are we running each test by forking it?
|
|
||||||
#define TEST_EXTENSION_FORKING_ENABLED \
|
|
||||||
(android::camera2::tests::TestSettings::ForkingEnabled())
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,117 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 <unistd.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include "TestForkerEventListener.h"
|
|
||||||
#include "TestExtensions.h"
|
|
||||||
|
|
||||||
#define DEBUG_TEST_FORKER_EVENT_LISTENER 0
|
|
||||||
|
|
||||||
#define RETURN_CODE_PASSED 0
|
|
||||||
#define RETURN_CODE_FAILED 1
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace camera2 {
|
|
||||||
namespace tests {
|
|
||||||
|
|
||||||
bool TestForkerEventListener::mIsForked = false;
|
|
||||||
|
|
||||||
TestForkerEventListener::TestForkerEventListener() {
|
|
||||||
mIsForked = false;
|
|
||||||
mHasSucceeded = true;
|
|
||||||
mTermSignal = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called before a test starts.
|
|
||||||
void TestForkerEventListener::OnTestStart(const ::testing::TestInfo&) {
|
|
||||||
|
|
||||||
if (!TEST_EXTENSION_FORKING_ENABLED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pid_t childPid = fork();
|
|
||||||
if (childPid != 0) {
|
|
||||||
int status;
|
|
||||||
waitpid(childPid, &status, /*options*/0);
|
|
||||||
|
|
||||||
// terminated normally?
|
|
||||||
mHasSucceeded = WIFEXITED(status);
|
|
||||||
// terminate with return code 0 = test passed, 1 = test failed
|
|
||||||
if (mHasSucceeded) {
|
|
||||||
mHasSucceeded = WEXITSTATUS(status) == RETURN_CODE_PASSED;
|
|
||||||
} else if (WIFSIGNALED(status)) {
|
|
||||||
mTermSignal = WTERMSIG(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* the test is then skipped by inserting the various
|
|
||||||
TEST_EXTENSION_ macros in TestExtensions.h */
|
|
||||||
|
|
||||||
} else {
|
|
||||||
mIsForked = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called after a failed assertion or a SUCCEED() invocation.
|
|
||||||
void TestForkerEventListener::OnTestPartResult(
|
|
||||||
const ::testing::TestPartResult& test_part_result) {
|
|
||||||
|
|
||||||
if (DEBUG_TEST_FORKER_EVENT_LISTENER) {
|
|
||||||
printf("%s in %s:%d\n%s\n",
|
|
||||||
test_part_result.failed() ? "*** Failure" : "Success",
|
|
||||||
test_part_result.file_name(),
|
|
||||||
test_part_result.line_number(),
|
|
||||||
test_part_result.summary());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called after a test ends.
|
|
||||||
void TestForkerEventListener::OnTestEnd(const ::testing::TestInfo& test_info) {
|
|
||||||
|
|
||||||
if (!TEST_EXTENSION_FORKING_ENABLED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mIsForked) {
|
|
||||||
exit(test_info.result()->Passed()
|
|
||||||
? RETURN_CODE_PASSED : RETURN_CODE_FAILED);
|
|
||||||
} else if (!mHasSucceeded && mTermSignal != 0) {
|
|
||||||
|
|
||||||
printf("*** Test %s.%s crashed with signal = %s\n",
|
|
||||||
test_info.test_case_name(), test_info.name(),
|
|
||||||
strsignal(mTermSignal));
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: overload the default event listener to suppress this message
|
|
||||||
// dynamically (e.g. by skipping OnTestPartResult after OnTestEnd )
|
|
||||||
|
|
||||||
// trigger a test failure if the child has failed
|
|
||||||
if (!mHasSucceeded) {
|
|
||||||
ADD_FAILURE();
|
|
||||||
}
|
|
||||||
mTermSignal = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __ANDROID_HAL_CAMERA2_TESTS_FORKER_EVENT_LISTENER__
|
|
||||||
#define __ANDROID_HAL_CAMERA2_TESTS_FORKER_EVENT_LISTENER__
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace camera2 {
|
|
||||||
namespace tests {
|
|
||||||
|
|
||||||
// Fork before each test runs.
|
|
||||||
class TestForkerEventListener : public ::testing::EmptyTestEventListener {
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
TestForkerEventListener();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
// Called before a test starts.
|
|
||||||
virtual void OnTestStart(const ::testing::TestInfo& test_info);
|
|
||||||
|
|
||||||
// Called after a failed assertion or a SUCCEED() invocation.
|
|
||||||
virtual void OnTestPartResult(
|
|
||||||
const ::testing::TestPartResult& test_part_result);
|
|
||||||
|
|
||||||
// Called after a test ends.
|
|
||||||
virtual void OnTestEnd(const ::testing::TestInfo& test_info);
|
|
||||||
|
|
||||||
bool mHasSucceeded;
|
|
||||||
int mTermSignal;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// do not read directly. use TEST_EXTENSION macros instead
|
|
||||||
static bool mIsForked;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,167 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 <cstdlib>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <cstring>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "TestSettings.h"
|
|
||||||
|
|
||||||
#include "TestForkerEventListener.h"
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace camera2 {
|
|
||||||
namespace tests {
|
|
||||||
|
|
||||||
bool TestSettings::mForkingDisabled = false;
|
|
||||||
int TestSettings::mDeviceId = 0;
|
|
||||||
char* const* TestSettings::mArgv;
|
|
||||||
|
|
||||||
// --forking-disabled, false by default
|
|
||||||
bool TestSettings::ForkingDisabled() {
|
|
||||||
return mForkingDisabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reverse of --forking-disabled (not a flag), true by default
|
|
||||||
bool TestSettings::ForkingEnabled() {
|
|
||||||
return !ForkingDisabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --device-id, 0 by default
|
|
||||||
int TestSettings::DeviceId() {
|
|
||||||
return mDeviceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns false if usage should be printed and we should exit early
|
|
||||||
bool TestSettings::ParseArgs(int argc, char* const argv[])
|
|
||||||
{
|
|
||||||
{
|
|
||||||
char *env = getenv("CAMERA2_TEST_FORKING_DISABLED");
|
|
||||||
if (env) {
|
|
||||||
mForkingDisabled = atoi(env);
|
|
||||||
}
|
|
||||||
|
|
||||||
env = getenv("CAMERA2_TEST_DEVICE_ID");
|
|
||||||
if (env) {
|
|
||||||
mDeviceId = atoi(env);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool printHelp = false;
|
|
||||||
bool unknownArgs = false;
|
|
||||||
|
|
||||||
opterr = 0; // do not print errors for unknown arguments
|
|
||||||
while (true) {
|
|
||||||
int c;
|
|
||||||
int option_index = 0;
|
|
||||||
|
|
||||||
static struct option long_options[] = {
|
|
||||||
/* name has_arg flag val */
|
|
||||||
{"forking-disabled", optional_argument, 0, 0 },
|
|
||||||
{"device-id", required_argument, 0, 0 },
|
|
||||||
{"help", no_argument, 0, 'h' },
|
|
||||||
{0, 0, 0, 0 }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Note: '+' in optstring means do not mutate argv
|
|
||||||
c = getopt_long(argc, argv, "+h", long_options, &option_index);
|
|
||||||
|
|
||||||
if (c == -1) { // All arguments exhausted
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (c == '?') { // Argument not in option lists
|
|
||||||
const char *arg = argv[optind-1];
|
|
||||||
// Anything beginning with gtest_ will get handled by gtest
|
|
||||||
if (strstr(arg, "--gtest_") != arg) {
|
|
||||||
std::cerr << "Unknown argument: " << arg << std::endl;
|
|
||||||
unknownArgs = true;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (c) {
|
|
||||||
case 0: // long option
|
|
||||||
switch (option_index) {
|
|
||||||
case 0: {
|
|
||||||
const char *arg = optarg ?: "1";
|
|
||||||
mForkingDisabled = atoi(arg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 1: {
|
|
||||||
mDeviceId = atoi(optarg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
std::cerr << "Unknown long option: " << option_index << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break; // case 0
|
|
||||||
case 'h': // help
|
|
||||||
printHelp = true;
|
|
||||||
break;
|
|
||||||
default: // case '?'
|
|
||||||
std::cerr << "Unknown option: " << optarg << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unknownArgs) {
|
|
||||||
std::cerr << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
mArgv = argv;
|
|
||||||
|
|
||||||
if (printHelp || unknownArgs) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cerr << "Forking Disabled: "
|
|
||||||
<< (mForkingDisabled ? "yes" : "no") << std::endl;
|
|
||||||
|
|
||||||
std::cerr << "Device ID: " << mDeviceId << std::endl;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// print usage/help list of commands (non-gtest)
|
|
||||||
void TestSettings::PrintUsage() {
|
|
||||||
std::cerr << "Usage: " << mArgv[0] << " [OPTIONS]" << std::endl;
|
|
||||||
std::cerr << std::endl;
|
|
||||||
|
|
||||||
std::cerr << "Main modes of operation:"
|
|
||||||
<< std::endl;
|
|
||||||
std::cerr << " --forking-disabled[=1] don't fork process before "
|
|
||||||
<< std::endl
|
|
||||||
<< " running a new test."
|
|
||||||
<< std::endl
|
|
||||||
<< " (default enabled)"
|
|
||||||
<< std::endl;
|
|
||||||
std::cerr << " --device-id=ID specify a different camera ID"
|
|
||||||
<< std::endl
|
|
||||||
<< " (default 0)"
|
|
||||||
<< std::endl;
|
|
||||||
|
|
||||||
std::cerr << " -h, --help print this help listing"
|
|
||||||
<< std::endl;
|
|
||||||
|
|
||||||
|
|
||||||
std::cerr << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
:qa
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __ANDROID_HAL_CAMERA2_TESTS_SETTINGS__
|
|
||||||
#define __ANDROID_HAL_CAMERA2_TESTS_SETTINGS__
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace camera2 {
|
|
||||||
namespace tests {
|
|
||||||
|
|
||||||
class TestSettings {
|
|
||||||
|
|
||||||
public:
|
|
||||||
// --forking-disabled, false by default
|
|
||||||
static bool ForkingDisabled();
|
|
||||||
|
|
||||||
// reverse of --forking-disabled (not a flag), true by default
|
|
||||||
static bool ForkingEnabled();
|
|
||||||
|
|
||||||
// --device-id, 0 by default
|
|
||||||
static int DeviceId();
|
|
||||||
|
|
||||||
// returns false if usage should be printed and we should exit early
|
|
||||||
static bool ParseArgs(int argc, char* const argv[]);
|
|
||||||
|
|
||||||
// print usage/help list of commands (non-gtest)
|
|
||||||
static void PrintUsage();
|
|
||||||
|
|
||||||
private:
|
|
||||||
TestSettings();
|
|
||||||
~TestSettings();
|
|
||||||
|
|
||||||
static bool mForkingDisabled;
|
|
||||||
static int mDeviceId;
|
|
||||||
static char* const* mArgv;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,609 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Utility classes for camera2 HAL testing
|
|
||||||
|
|
||||||
#define LOG_TAG "Camera2_test_utils"
|
|
||||||
#define LOG_NDEBUG 0
|
|
||||||
|
|
||||||
#include "utils/Log.h"
|
|
||||||
#include "camera2_utils.h"
|
|
||||||
#include <dlfcn.h>
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace camera2 {
|
|
||||||
namespace tests {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MetadataQueue
|
|
||||||
*/
|
|
||||||
|
|
||||||
MetadataQueue::MetadataQueue():
|
|
||||||
mDevice(NULL),
|
|
||||||
mFrameCount(0),
|
|
||||||
mCount(0),
|
|
||||||
mStreamSlotCount(0),
|
|
||||||
mSignalConsumer(true)
|
|
||||||
{
|
|
||||||
camera2_request_queue_src_ops::dequeue_request = consumer_dequeue;
|
|
||||||
camera2_request_queue_src_ops::request_count = consumer_buffer_count;
|
|
||||||
camera2_request_queue_src_ops::free_request = consumer_free;
|
|
||||||
|
|
||||||
camera2_frame_queue_dst_ops::dequeue_frame = producer_dequeue;
|
|
||||||
camera2_frame_queue_dst_ops::cancel_frame = producer_cancel;
|
|
||||||
camera2_frame_queue_dst_ops::enqueue_frame = producer_enqueue;
|
|
||||||
}
|
|
||||||
|
|
||||||
MetadataQueue::~MetadataQueue() {
|
|
||||||
freeBuffers(mEntries.begin(), mEntries.end());
|
|
||||||
freeBuffers(mStreamSlot.begin(), mStreamSlot.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface to camera2 HAL as consumer (input requests/reprocessing)
|
|
||||||
const camera2_request_queue_src_ops_t* MetadataQueue::getToConsumerInterface() {
|
|
||||||
return static_cast<camera2_request_queue_src_ops_t*>(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MetadataQueue::setFromConsumerInterface(camera2_device_t *d) {
|
|
||||||
mDevice = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
const camera2_frame_queue_dst_ops_t* MetadataQueue::getToProducerInterface() {
|
|
||||||
return static_cast<camera2_frame_queue_dst_ops_t*>(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Real interfaces
|
|
||||||
status_t MetadataQueue::enqueue(camera_metadata_t *buf) {
|
|
||||||
Mutex::Autolock l(mMutex);
|
|
||||||
|
|
||||||
mCount++;
|
|
||||||
mEntries.push_back(buf);
|
|
||||||
notEmpty.signal();
|
|
||||||
|
|
||||||
if (mSignalConsumer && mDevice != NULL) {
|
|
||||||
mSignalConsumer = false;
|
|
||||||
|
|
||||||
mMutex.unlock();
|
|
||||||
ALOGV("%s: Signaling consumer", __FUNCTION__);
|
|
||||||
mDevice->ops->notify_request_queue_not_empty(mDevice);
|
|
||||||
mMutex.lock();
|
|
||||||
}
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int MetadataQueue::getBufferCount() {
|
|
||||||
Mutex::Autolock l(mMutex);
|
|
||||||
if (mStreamSlotCount > 0) {
|
|
||||||
return CAMERA2_REQUEST_QUEUE_IS_BOTTOMLESS;
|
|
||||||
}
|
|
||||||
return mCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t MetadataQueue::dequeue(camera_metadata_t **buf, bool incrementCount) {
|
|
||||||
Mutex::Autolock l(mMutex);
|
|
||||||
|
|
||||||
if (mCount == 0) {
|
|
||||||
if (mStreamSlotCount == 0) {
|
|
||||||
ALOGV("%s: Empty", __FUNCTION__);
|
|
||||||
*buf = NULL;
|
|
||||||
mSignalConsumer = true;
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
ALOGV("%s: Streaming %d frames to queue", __FUNCTION__,
|
|
||||||
mStreamSlotCount);
|
|
||||||
|
|
||||||
for (List<camera_metadata_t*>::iterator slotEntry = mStreamSlot.begin();
|
|
||||||
slotEntry != mStreamSlot.end();
|
|
||||||
slotEntry++ ) {
|
|
||||||
size_t entries = get_camera_metadata_entry_count(*slotEntry);
|
|
||||||
size_t dataBytes = get_camera_metadata_data_count(*slotEntry);
|
|
||||||
|
|
||||||
camera_metadata_t *copy = allocate_camera_metadata(entries, dataBytes);
|
|
||||||
append_camera_metadata(copy, *slotEntry);
|
|
||||||
mEntries.push_back(copy);
|
|
||||||
}
|
|
||||||
mCount = mStreamSlotCount;
|
|
||||||
}
|
|
||||||
ALOGV("MetadataQueue: deque (%d buffers)", mCount);
|
|
||||||
camera_metadata_t *b = *(mEntries.begin());
|
|
||||||
mEntries.erase(mEntries.begin());
|
|
||||||
|
|
||||||
if (incrementCount) {
|
|
||||||
add_camera_metadata_entry(b,
|
|
||||||
ANDROID_REQUEST_FRAME_COUNT,
|
|
||||||
(void**)&mFrameCount, 1);
|
|
||||||
mFrameCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
*buf = b;
|
|
||||||
mCount--;
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t MetadataQueue::waitForBuffer(nsecs_t timeout) {
|
|
||||||
Mutex::Autolock l(mMutex);
|
|
||||||
status_t res;
|
|
||||||
while (mCount == 0) {
|
|
||||||
res = notEmpty.waitRelative(mMutex,timeout);
|
|
||||||
if (res != OK) return res;
|
|
||||||
}
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t MetadataQueue::setStreamSlot(camera_metadata_t *buf) {
|
|
||||||
if (buf == NULL) {
|
|
||||||
freeBuffers(mStreamSlot.begin(), mStreamSlot.end());
|
|
||||||
mStreamSlotCount = 0;
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
if (mStreamSlotCount > 1) {
|
|
||||||
List<camera_metadata_t*>::iterator deleter = ++mStreamSlot.begin();
|
|
||||||
freeBuffers(++mStreamSlot.begin(), mStreamSlot.end());
|
|
||||||
mStreamSlotCount = 1;
|
|
||||||
}
|
|
||||||
if (mStreamSlotCount == 1) {
|
|
||||||
free_camera_metadata( *(mStreamSlot.begin()) );
|
|
||||||
*(mStreamSlot.begin()) = buf;
|
|
||||||
} else {
|
|
||||||
mStreamSlot.push_front(buf);
|
|
||||||
mStreamSlotCount = 1;
|
|
||||||
}
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t MetadataQueue::setStreamSlot(const List<camera_metadata_t*> &bufs) {
|
|
||||||
if (mStreamSlotCount > 0) {
|
|
||||||
freeBuffers(mStreamSlot.begin(), mStreamSlot.end());
|
|
||||||
}
|
|
||||||
mStreamSlot = bufs;
|
|
||||||
mStreamSlotCount = mStreamSlot.size();
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t MetadataQueue::freeBuffers(List<camera_metadata_t*>::iterator start,
|
|
||||||
const List<camera_metadata_t*>::iterator& end) {
|
|
||||||
while (start != end) {
|
|
||||||
free_camera_metadata(*start);
|
|
||||||
start = mStreamSlot.erase(start);
|
|
||||||
}
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
MetadataQueue* MetadataQueue::getInstance(
|
|
||||||
const camera2_request_queue_src_ops_t *q) {
|
|
||||||
const MetadataQueue* cmq = static_cast<const MetadataQueue*>(q);
|
|
||||||
return const_cast<MetadataQueue*>(cmq);
|
|
||||||
}
|
|
||||||
|
|
||||||
MetadataQueue* MetadataQueue::getInstance(
|
|
||||||
const camera2_frame_queue_dst_ops_t *q) {
|
|
||||||
const MetadataQueue* cmq = static_cast<const MetadataQueue*>(q);
|
|
||||||
return const_cast<MetadataQueue*>(cmq);
|
|
||||||
}
|
|
||||||
|
|
||||||
int MetadataQueue::consumer_buffer_count(
|
|
||||||
const camera2_request_queue_src_ops_t *q) {
|
|
||||||
MetadataQueue *queue = getInstance(q);
|
|
||||||
return queue->getBufferCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
int MetadataQueue::consumer_dequeue(const camera2_request_queue_src_ops_t *q,
|
|
||||||
camera_metadata_t **buffer) {
|
|
||||||
MetadataQueue *queue = getInstance(q);
|
|
||||||
return queue->dequeue(buffer, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int MetadataQueue::consumer_free(const camera2_request_queue_src_ops_t * /* q */,
|
|
||||||
camera_metadata_t *old_buffer) {
|
|
||||||
free_camera_metadata(old_buffer);
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int MetadataQueue::producer_dequeue(const camera2_frame_queue_dst_ops_t * /* q */,
|
|
||||||
size_t entries, size_t bytes,
|
|
||||||
camera_metadata_t **buffer) {
|
|
||||||
camera_metadata_t *new_buffer =
|
|
||||||
allocate_camera_metadata(entries, bytes);
|
|
||||||
if (new_buffer == NULL) return NO_MEMORY;
|
|
||||||
*buffer = new_buffer;
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int MetadataQueue::producer_cancel(const camera2_frame_queue_dst_ops_t * /* q */,
|
|
||||||
camera_metadata_t *old_buffer) {
|
|
||||||
free_camera_metadata(old_buffer);
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int MetadataQueue::producer_enqueue(const camera2_frame_queue_dst_ops_t *q,
|
|
||||||
camera_metadata_t *filled_buffer) {
|
|
||||||
MetadataQueue *queue = getInstance(q);
|
|
||||||
return queue->enqueue(filled_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* NotifierListener
|
|
||||||
*/
|
|
||||||
|
|
||||||
NotifierListener::NotifierListener() {
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t NotifierListener::getNotificationsFrom(camera2_device *dev) {
|
|
||||||
if (!dev) return BAD_VALUE;
|
|
||||||
status_t err;
|
|
||||||
err = dev->ops->set_notify_callback(dev,
|
|
||||||
notify_callback_dispatch,
|
|
||||||
(void*)this);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t NotifierListener::getNextNotification(int32_t *msg_type,
|
|
||||||
int32_t *ext1,
|
|
||||||
int32_t *ext2,
|
|
||||||
int32_t *ext3) {
|
|
||||||
Mutex::Autolock l(mMutex);
|
|
||||||
if (mNotifications.size() == 0) return BAD_VALUE;
|
|
||||||
return getNextNotificationLocked(msg_type, ext1, ext2, ext3);
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t NotifierListener::waitForNotification(int32_t *msg_type,
|
|
||||||
int32_t *ext1,
|
|
||||||
int32_t *ext2,
|
|
||||||
int32_t *ext3) {
|
|
||||||
Mutex::Autolock l(mMutex);
|
|
||||||
while (mNotifications.size() == 0) {
|
|
||||||
mNewNotification.wait(mMutex);
|
|
||||||
}
|
|
||||||
return getNextNotificationLocked(msg_type, ext1, ext2, ext3);
|
|
||||||
}
|
|
||||||
|
|
||||||
int NotifierListener::numNotifications() {
|
|
||||||
Mutex::Autolock l(mMutex);
|
|
||||||
return mNotifications.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t NotifierListener::getNextNotificationLocked(int32_t *msg_type,
|
|
||||||
int32_t *ext1,
|
|
||||||
int32_t *ext2,
|
|
||||||
int32_t *ext3) {
|
|
||||||
*msg_type = mNotifications.begin()->msg_type;
|
|
||||||
*ext1 = mNotifications.begin()->ext1;
|
|
||||||
*ext2 = mNotifications.begin()->ext2;
|
|
||||||
*ext3 = mNotifications.begin()->ext3;
|
|
||||||
mNotifications.erase(mNotifications.begin());
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifierListener::onNotify(int32_t msg_type,
|
|
||||||
int32_t ext1,
|
|
||||||
int32_t ext2,
|
|
||||||
int32_t ext3) {
|
|
||||||
Mutex::Autolock l(mMutex);
|
|
||||||
mNotifications.push_back(Notification(msg_type, ext1, ext2, ext3));
|
|
||||||
mNewNotification.signal();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifierListener::notify_callback_dispatch(int32_t msg_type,
|
|
||||||
int32_t ext1,
|
|
||||||
int32_t ext2,
|
|
||||||
int32_t ext3,
|
|
||||||
void *user) {
|
|
||||||
NotifierListener *me = reinterpret_cast<NotifierListener*>(user);
|
|
||||||
me->onNotify(msg_type, ext1, ext2, ext3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* StreamAdapter
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef container_of
|
|
||||||
#define container_of(ptr, type, member) \
|
|
||||||
(type *)((char*)(ptr) - offsetof(type, member))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
StreamAdapter::StreamAdapter(sp<IGraphicBufferProducer> consumer):
|
|
||||||
mState(UNINITIALIZED), mDevice(NULL),
|
|
||||||
mId(-1),
|
|
||||||
mWidth(0), mHeight(0), mFormat(0)
|
|
||||||
{
|
|
||||||
mConsumerInterface = new Surface(consumer);
|
|
||||||
camera2_stream_ops::dequeue_buffer = dequeue_buffer;
|
|
||||||
camera2_stream_ops::enqueue_buffer = enqueue_buffer;
|
|
||||||
camera2_stream_ops::cancel_buffer = cancel_buffer;
|
|
||||||
camera2_stream_ops::set_crop = set_crop;
|
|
||||||
}
|
|
||||||
|
|
||||||
StreamAdapter::~StreamAdapter() {
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t StreamAdapter::connectToDevice(camera2_device_t *d,
|
|
||||||
uint32_t width, uint32_t height, int format) {
|
|
||||||
if (mState != UNINITIALIZED) return INVALID_OPERATION;
|
|
||||||
if (d == NULL) {
|
|
||||||
ALOGE("%s: Null device passed to stream adapter", __FUNCTION__);
|
|
||||||
return BAD_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t res;
|
|
||||||
|
|
||||||
mWidth = width;
|
|
||||||
mHeight = height;
|
|
||||||
mFormat = format;
|
|
||||||
|
|
||||||
// Allocate device-side stream interface
|
|
||||||
|
|
||||||
uint32_t id;
|
|
||||||
uint32_t formatActual; // ignored
|
|
||||||
uint32_t usage;
|
|
||||||
uint32_t maxBuffers = 2;
|
|
||||||
res = d->ops->allocate_stream(d,
|
|
||||||
mWidth, mHeight, mFormat, getStreamOps(),
|
|
||||||
&id, &formatActual, &usage, &maxBuffers);
|
|
||||||
if (res != OK) {
|
|
||||||
ALOGE("%s: Device stream allocation failed: %s (%d)",
|
|
||||||
__FUNCTION__, strerror(-res), res);
|
|
||||||
mState = UNINITIALIZED;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
mDevice = d;
|
|
||||||
|
|
||||||
mId = id;
|
|
||||||
mUsage = usage;
|
|
||||||
mMaxProducerBuffers = maxBuffers;
|
|
||||||
|
|
||||||
// Configure consumer-side ANativeWindow interface
|
|
||||||
|
|
||||||
res = native_window_api_connect(mConsumerInterface.get(),
|
|
||||||
NATIVE_WINDOW_API_CAMERA);
|
|
||||||
if (res != OK) {
|
|
||||||
ALOGE("%s: Unable to connect to native window for stream %d",
|
|
||||||
__FUNCTION__, mId);
|
|
||||||
mState = ALLOCATED;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = native_window_set_usage(mConsumerInterface.get(), mUsage);
|
|
||||||
if (res != OK) {
|
|
||||||
ALOGE("%s: Unable to configure usage %08x for stream %d",
|
|
||||||
__FUNCTION__, mUsage, mId);
|
|
||||||
mState = CONNECTED;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = native_window_set_buffers_dimensions(mConsumerInterface.get(),
|
|
||||||
mWidth, mHeight);
|
|
||||||
if (res != OK) {
|
|
||||||
ALOGE("%s: Unable to configure buffer dimensions"
|
|
||||||
" %d x %d for stream %d",
|
|
||||||
__FUNCTION__, mWidth, mHeight, mId);
|
|
||||||
mState = CONNECTED;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
res = native_window_set_buffers_format(mConsumerInterface.get(),
|
|
||||||
mFormat);
|
|
||||||
if (res != OK) {
|
|
||||||
ALOGE("%s: Unable to configure buffer format"
|
|
||||||
" 0x%x for stream %d",
|
|
||||||
__FUNCTION__, mFormat, mId);
|
|
||||||
mState = CONNECTED;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int maxConsumerBuffers;
|
|
||||||
res = mConsumerInterface->query(mConsumerInterface.get(),
|
|
||||||
NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxConsumerBuffers);
|
|
||||||
if (res != OK) {
|
|
||||||
ALOGE("%s: Unable to query consumer undequeued"
|
|
||||||
" buffer count for stream %d", __FUNCTION__, mId);
|
|
||||||
mState = CONNECTED;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
mMaxConsumerBuffers = maxConsumerBuffers;
|
|
||||||
|
|
||||||
ALOGV("%s: Producer wants %d buffers, consumer wants %d", __FUNCTION__,
|
|
||||||
mMaxProducerBuffers, mMaxConsumerBuffers);
|
|
||||||
|
|
||||||
int totalBuffers = mMaxConsumerBuffers + mMaxProducerBuffers;
|
|
||||||
|
|
||||||
res = native_window_set_buffer_count(mConsumerInterface.get(),
|
|
||||||
totalBuffers);
|
|
||||||
if (res != OK) {
|
|
||||||
ALOGE("%s: Unable to set buffer count for stream %d",
|
|
||||||
__FUNCTION__, mId);
|
|
||||||
mState = CONNECTED;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register allocated buffers with HAL device
|
|
||||||
buffer_handle_t *buffers = new buffer_handle_t[totalBuffers];
|
|
||||||
ANativeWindowBuffer **anwBuffers = new ANativeWindowBuffer*[totalBuffers];
|
|
||||||
int bufferIdx = 0;
|
|
||||||
for (; bufferIdx < totalBuffers; bufferIdx++) {
|
|
||||||
res = native_window_dequeue_buffer_and_wait(mConsumerInterface.get(),
|
|
||||||
&anwBuffers[bufferIdx]);
|
|
||||||
if (res != OK) {
|
|
||||||
ALOGE("%s: Unable to dequeue buffer %d for initial registration for"
|
|
||||||
"stream %d", __FUNCTION__, bufferIdx, mId);
|
|
||||||
mState = CONNECTED;
|
|
||||||
goto cleanUpBuffers;
|
|
||||||
}
|
|
||||||
buffers[bufferIdx] = anwBuffers[bufferIdx]->handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = mDevice->ops->register_stream_buffers(mDevice,
|
|
||||||
mId,
|
|
||||||
totalBuffers,
|
|
||||||
buffers);
|
|
||||||
if (res != OK) {
|
|
||||||
ALOGE("%s: Unable to register buffers with HAL device for stream %d",
|
|
||||||
__FUNCTION__, mId);
|
|
||||||
mState = CONNECTED;
|
|
||||||
} else {
|
|
||||||
mState = ACTIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanUpBuffers:
|
|
||||||
for (int i = 0; i < bufferIdx; i++) {
|
|
||||||
res = mConsumerInterface->cancelBuffer(mConsumerInterface.get(),
|
|
||||||
anwBuffers[i], -1);
|
|
||||||
}
|
|
||||||
delete[] anwBuffers;
|
|
||||||
delete[] buffers;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t StreamAdapter::disconnect() {
|
|
||||||
status_t res;
|
|
||||||
if (mState >= ALLOCATED) {
|
|
||||||
res = mDevice->ops->release_stream(mDevice, mId);
|
|
||||||
if (res != OK) {
|
|
||||||
ALOGE("%s: Unable to release stream %d",
|
|
||||||
__FUNCTION__, mId);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mState >= CONNECTED) {
|
|
||||||
res = native_window_api_disconnect(mConsumerInterface.get(),
|
|
||||||
NATIVE_WINDOW_API_CAMERA);
|
|
||||||
if (res != OK) {
|
|
||||||
ALOGE("%s: Unable to disconnect stream %d from native window",
|
|
||||||
__FUNCTION__, mId);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mId = -1;
|
|
||||||
mState = DISCONNECTED;
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int StreamAdapter::getId() {
|
|
||||||
return mId;
|
|
||||||
}
|
|
||||||
|
|
||||||
const camera2_stream_ops *StreamAdapter::getStreamOps() {
|
|
||||||
return static_cast<camera2_stream_ops *>(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
ANativeWindow* StreamAdapter::toANW(const camera2_stream_ops_t *w) {
|
|
||||||
return static_cast<const StreamAdapter*>(w)->mConsumerInterface.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
int StreamAdapter::dequeue_buffer(const camera2_stream_ops_t *w,
|
|
||||||
buffer_handle_t** buffer) {
|
|
||||||
int res;
|
|
||||||
int state = static_cast<const StreamAdapter*>(w)->mState;
|
|
||||||
if (state != ACTIVE) {
|
|
||||||
ALOGE("%s: Called when in bad state: %d", __FUNCTION__, state);
|
|
||||||
return INVALID_OPERATION;
|
|
||||||
}
|
|
||||||
|
|
||||||
ANativeWindow *a = toANW(w);
|
|
||||||
ANativeWindowBuffer* anb;
|
|
||||||
res = native_window_dequeue_buffer_and_wait(a, &anb);
|
|
||||||
if (res != OK) return res;
|
|
||||||
|
|
||||||
*buffer = &(anb->handle);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int StreamAdapter::enqueue_buffer(const camera2_stream_ops_t* w,
|
|
||||||
int64_t timestamp,
|
|
||||||
buffer_handle_t* buffer) {
|
|
||||||
int state = static_cast<const StreamAdapter*>(w)->mState;
|
|
||||||
if (state != ACTIVE) {
|
|
||||||
ALOGE("%s: Called when in bad state: %d", __FUNCTION__, state);
|
|
||||||
return INVALID_OPERATION;
|
|
||||||
}
|
|
||||||
ANativeWindow *a = toANW(w);
|
|
||||||
status_t err;
|
|
||||||
err = native_window_set_buffers_timestamp(a, timestamp);
|
|
||||||
if (err != OK) return err;
|
|
||||||
return a->queueBuffer(a,
|
|
||||||
container_of(buffer, ANativeWindowBuffer, handle), -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int StreamAdapter::cancel_buffer(const camera2_stream_ops_t* w,
|
|
||||||
buffer_handle_t* buffer) {
|
|
||||||
int state = static_cast<const StreamAdapter*>(w)->mState;
|
|
||||||
if (state != ACTIVE) {
|
|
||||||
ALOGE("%s: Called when in bad state: %d", __FUNCTION__, state);
|
|
||||||
return INVALID_OPERATION;
|
|
||||||
}
|
|
||||||
ANativeWindow *a = toANW(w);
|
|
||||||
return a->cancelBuffer(a,
|
|
||||||
container_of(buffer, ANativeWindowBuffer, handle), -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int StreamAdapter::set_crop(const camera2_stream_ops_t* w,
|
|
||||||
int left, int top, int right, int bottom) {
|
|
||||||
int state = static_cast<const StreamAdapter*>(w)->mState;
|
|
||||||
if (state != ACTIVE) {
|
|
||||||
ALOGE("%s: Called when in bad state: %d", __FUNCTION__, state);
|
|
||||||
return INVALID_OPERATION;
|
|
||||||
}
|
|
||||||
ANativeWindow *a = toANW(w);
|
|
||||||
android_native_rect_t crop = { left, top, right, bottom };
|
|
||||||
return native_window_set_crop(a, &crop);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FrameWaiter
|
|
||||||
*/
|
|
||||||
|
|
||||||
FrameWaiter::FrameWaiter():
|
|
||||||
mPendingFrames(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t FrameWaiter::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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FrameWaiter::onFrameAvailable(const BufferItem& /* item */) {
|
|
||||||
Mutex::Autolock lock(mMutex);
|
|
||||||
mPendingFrames++;
|
|
||||||
mCondition.signal();
|
|
||||||
}
|
|
||||||
|
|
||||||
int HWModuleHelpers::closeModule(void *dso) {
|
|
||||||
int status;
|
|
||||||
if (!dso) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = dlclose(dso);
|
|
||||||
if (status != 0) {
|
|
||||||
char const *err_str = dlerror();
|
|
||||||
ALOGE("%s dlclose failed, error: %s", __func__, err_str ?: "unknown");
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace tests
|
|
||||||
} // namespace camera2
|
|
||||||
} // namespace android
|
|
|
@ -1,250 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __ANDROID_HAL_CAMERA2_TESTS_UTILS__
|
|
||||||
#define __ANDROID_HAL_CAMERA2_TESTS_UTILS__
|
|
||||||
|
|
||||||
// Utility classes for camera2 HAL testing
|
|
||||||
|
|
||||||
#include <system/camera_metadata.h>
|
|
||||||
#include <hardware/camera2.h>
|
|
||||||
|
|
||||||
#include <gui/Surface.h>
|
|
||||||
#include <gui/CpuConsumer.h>
|
|
||||||
|
|
||||||
#include <utils/List.h>
|
|
||||||
#include <utils/Mutex.h>
|
|
||||||
#include <utils/Condition.h>
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace camera2 {
|
|
||||||
namespace tests {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Queue class for both sending requests to a camera2 device, and for receiving
|
|
||||||
* frames from a camera2 device.
|
|
||||||
*/
|
|
||||||
class MetadataQueue: public camera2_request_queue_src_ops_t,
|
|
||||||
public camera2_frame_queue_dst_ops_t {
|
|
||||||
public:
|
|
||||||
MetadataQueue();
|
|
||||||
~MetadataQueue();
|
|
||||||
|
|
||||||
// Interface to camera2 HAL device, either for requests (device is consumer)
|
|
||||||
// or for frames (device is producer)
|
|
||||||
const camera2_request_queue_src_ops_t* getToConsumerInterface();
|
|
||||||
void setFromConsumerInterface(camera2_device_t *d);
|
|
||||||
|
|
||||||
const camera2_frame_queue_dst_ops_t* getToProducerInterface();
|
|
||||||
|
|
||||||
// Real interfaces. On enqueue, queue takes ownership of buffer pointer
|
|
||||||
// On dequeue, user takes ownership of buffer pointer.
|
|
||||||
status_t enqueue(camera_metadata_t *buf);
|
|
||||||
status_t dequeue(camera_metadata_t **buf, bool incrementCount = true);
|
|
||||||
int getBufferCount();
|
|
||||||
status_t waitForBuffer(nsecs_t timeout);
|
|
||||||
|
|
||||||
// Set repeating buffer(s); if the queue is empty on a dequeue call, the
|
|
||||||
// queue copies the contents of the stream slot into the queue, and then
|
|
||||||
// dequeues the first new entry.
|
|
||||||
status_t setStreamSlot(camera_metadata_t *buf);
|
|
||||||
status_t setStreamSlot(const List<camera_metadata_t*> &bufs);
|
|
||||||
|
|
||||||
private:
|
|
||||||
status_t freeBuffers(List<camera_metadata_t*>::iterator start,
|
|
||||||
const List<camera_metadata_t*>::iterator& end);
|
|
||||||
|
|
||||||
camera2_device_t *mDevice;
|
|
||||||
|
|
||||||
Mutex mMutex;
|
|
||||||
Condition notEmpty;
|
|
||||||
|
|
||||||
int mFrameCount;
|
|
||||||
|
|
||||||
int mCount;
|
|
||||||
List<camera_metadata_t*> mEntries;
|
|
||||||
int mStreamSlotCount;
|
|
||||||
List<camera_metadata_t*> mStreamSlot;
|
|
||||||
|
|
||||||
bool mSignalConsumer;
|
|
||||||
|
|
||||||
static MetadataQueue* getInstance(const camera2_frame_queue_dst_ops_t *q);
|
|
||||||
static MetadataQueue* getInstance(const camera2_request_queue_src_ops_t *q);
|
|
||||||
|
|
||||||
static int consumer_buffer_count(const camera2_request_queue_src_ops_t *q);
|
|
||||||
|
|
||||||
static int consumer_dequeue(const camera2_request_queue_src_ops_t *q,
|
|
||||||
camera_metadata_t **buffer);
|
|
||||||
|
|
||||||
static int consumer_free(const camera2_request_queue_src_ops_t *q,
|
|
||||||
camera_metadata_t *old_buffer);
|
|
||||||
|
|
||||||
static int producer_dequeue(const camera2_frame_queue_dst_ops_t *q,
|
|
||||||
size_t entries, size_t bytes,
|
|
||||||
camera_metadata_t **buffer);
|
|
||||||
|
|
||||||
static int producer_cancel(const camera2_frame_queue_dst_ops_t *q,
|
|
||||||
camera_metadata_t *old_buffer);
|
|
||||||
|
|
||||||
static int producer_enqueue(const camera2_frame_queue_dst_ops_t *q,
|
|
||||||
camera_metadata_t *filled_buffer);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic class to receive and queue up notifications from the camera device
|
|
||||||
*/
|
|
||||||
|
|
||||||
class NotifierListener {
|
|
||||||
public:
|
|
||||||
|
|
||||||
NotifierListener();
|
|
||||||
|
|
||||||
status_t getNotificationsFrom(camera2_device *dev);
|
|
||||||
|
|
||||||
status_t getNextNotification(int32_t *msg_type, int32_t *ext1,
|
|
||||||
int32_t *ext2, int32_t *ext3);
|
|
||||||
|
|
||||||
status_t waitForNotification(int32_t *msg_type, int32_t *ext1,
|
|
||||||
int32_t *ext2, int32_t *ext3);
|
|
||||||
|
|
||||||
int numNotifications();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
status_t getNextNotificationLocked(int32_t *msg_type,
|
|
||||||
int32_t *ext1, int32_t *ext2, int32_t *ext3);
|
|
||||||
|
|
||||||
struct Notification {
|
|
||||||
Notification(int32_t type, int32_t e1, int32_t e2, int32_t e3):
|
|
||||||
msg_type(type),
|
|
||||||
ext1(e1),
|
|
||||||
ext2(e2),
|
|
||||||
ext3(e3)
|
|
||||||
{}
|
|
||||||
|
|
||||||
int32_t msg_type;
|
|
||||||
int32_t ext1;
|
|
||||||
int32_t ext2;
|
|
||||||
int32_t ext3;
|
|
||||||
};
|
|
||||||
|
|
||||||
List<Notification> mNotifications;
|
|
||||||
|
|
||||||
Mutex mMutex;
|
|
||||||
Condition mNewNotification;
|
|
||||||
|
|
||||||
void onNotify(int32_t msg_type,
|
|
||||||
int32_t ext1,
|
|
||||||
int32_t ext2,
|
|
||||||
int32_t ext3);
|
|
||||||
|
|
||||||
static void notify_callback_dispatch(int32_t msg_type,
|
|
||||||
int32_t ext1,
|
|
||||||
int32_t ext2,
|
|
||||||
int32_t ext3,
|
|
||||||
void *user);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter from an IGraphicBufferProducer interface to camera2 device stream ops.
|
|
||||||
* Also takes care of allocating/deallocating stream in device interface
|
|
||||||
*/
|
|
||||||
class StreamAdapter: public camera2_stream_ops {
|
|
||||||
public:
|
|
||||||
explicit StreamAdapter(sp<IGraphicBufferProducer> consumer);
|
|
||||||
|
|
||||||
~StreamAdapter();
|
|
||||||
|
|
||||||
status_t connectToDevice(camera2_device_t *d,
|
|
||||||
uint32_t width, uint32_t height, int format);
|
|
||||||
|
|
||||||
status_t disconnect();
|
|
||||||
|
|
||||||
// Get stream ID. Only valid after a successful connectToDevice call.
|
|
||||||
int getId();
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum {
|
|
||||||
ERROR = -1,
|
|
||||||
DISCONNECTED = 0,
|
|
||||||
UNINITIALIZED,
|
|
||||||
ALLOCATED,
|
|
||||||
CONNECTED,
|
|
||||||
ACTIVE
|
|
||||||
} mState;
|
|
||||||
|
|
||||||
sp<ANativeWindow> mConsumerInterface;
|
|
||||||
camera2_device_t *mDevice;
|
|
||||||
|
|
||||||
uint32_t mId;
|
|
||||||
uint32_t mWidth;
|
|
||||||
uint32_t mHeight;
|
|
||||||
uint32_t mFormat;
|
|
||||||
uint32_t mUsage;
|
|
||||||
uint32_t mMaxProducerBuffers;
|
|
||||||
uint32_t mMaxConsumerBuffers;
|
|
||||||
|
|
||||||
const camera2_stream_ops *getStreamOps();
|
|
||||||
|
|
||||||
static ANativeWindow* toANW(const camera2_stream_ops_t *w);
|
|
||||||
|
|
||||||
static int dequeue_buffer(const camera2_stream_ops_t *w,
|
|
||||||
buffer_handle_t** buffer);
|
|
||||||
|
|
||||||
static int enqueue_buffer(const camera2_stream_ops_t* w,
|
|
||||||
int64_t timestamp,
|
|
||||||
buffer_handle_t* buffer);
|
|
||||||
|
|
||||||
static int cancel_buffer(const camera2_stream_ops_t* w,
|
|
||||||
buffer_handle_t* buffer);
|
|
||||||
|
|
||||||
static int set_crop(const camera2_stream_ops_t* w,
|
|
||||||
int left, int top, int right, int bottom);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple class to wait on the CpuConsumer to have a frame available
|
|
||||||
*/
|
|
||||||
class FrameWaiter : public CpuConsumer::FrameAvailableListener {
|
|
||||||
public:
|
|
||||||
FrameWaiter();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for max timeout nanoseconds for a new frame. Returns
|
|
||||||
* OK if a frame is available, TIMED_OUT if the timeout was reached.
|
|
||||||
*/
|
|
||||||
status_t waitForFrame(nsecs_t timeout);
|
|
||||||
|
|
||||||
virtual void onFrameAvailable(const BufferItem& item);
|
|
||||||
|
|
||||||
int mPendingFrames;
|
|
||||||
Mutex mMutex;
|
|
||||||
Condition mCondition;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HWModuleHelpers {
|
|
||||||
/* attempt to unload the library with dlclose */
|
|
||||||
static int closeModule(void* dso);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 "TestForkerEventListener.h"
|
|
||||||
#include "TestSettings.h"
|
|
||||||
|
|
||||||
using android::camera2::tests::TestForkerEventListener;
|
|
||||||
using android::camera2::tests::TestSettings;
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
|
|
||||||
bool printUsage = !TestSettings::ParseArgs(argc, argv);
|
|
||||||
|
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
|
||||||
|
|
||||||
if (printUsage) {
|
|
||||||
TestSettings::PrintUsage();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets hold of the event listener list.
|
|
||||||
::testing::TestEventListeners& listeners =
|
|
||||||
::testing::UnitTest::GetInstance()->listeners();
|
|
||||||
// Adds a listener to the end. Google Test takes the ownership.
|
|
||||||
listeners.Append(new TestForkerEventListener());
|
|
||||||
|
|
||||||
int ret = RUN_ALL_TESTS();
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
Loading…
Reference in a new issue