| /* |
| * 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<ANativeWindow>& 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<ANativeWindow> 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")); |
| mNativeWindow = new Surface(producer); |
| } else { |
| // Render the stream to screen. |
| mCpuConsumer = NULL; |
| mNativeWindow = 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(mNativeWindow, |
| 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. |
| mNativeWindow.clear(); |
| mCpuConsumer.clear(); |
| } |
| |
| private: |
| sp<FrameListener> mFrameListener; |
| sp<CpuConsumer> mCpuConsumer; |
| sp<ANativeWindow> mNativeWindow; |
| 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, |
| sp<ANativeWindow> 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 { |
| const int32_t *implDefResolutions; |
| size_t implDefResolutionsCount; |
| |
| 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, processedMaxSize, jpegMaxSize; |
| |
| 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<ANativeWindow> 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()); |
| } |
| |
| } |
| } |
| } |