| /* | |
| * Copyright (C) 2022 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_TAG "ExtCamDev" | |
| // #define LOG_NDEBUG 0 | |
| #include <log/log.h> | |
| #include "ExternalCameraDevice.h" | |
| #include <aidl/android/hardware/camera/common/Status.h> | |
| #include <convert.h> | |
| #include <linux/videodev2.h> | |
| #include <regex> | |
| #include <set> | |
| namespace android { | |
| namespace hardware { | |
| namespace camera { | |
| namespace device { | |
| namespace implementation { | |
| using ::aidl::android::hardware::camera::common::Status; | |
| namespace { | |
| // Only support MJPEG for now as it seems to be the one supports higher fps | |
| // Other formats to consider in the future: | |
| // * V4L2_PIX_FMT_YVU420 (== YV12) | |
| // * V4L2_PIX_FMT_YVYU (YVYU: can be converted to YV12 or other YUV420_888 formats) | |
| const std::array<uint32_t, /*size*/ 2> kSupportedFourCCs{ | |
| {V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_Z16}}; // double braces required in C++11 | |
| constexpr int MAX_RETRY = 5; // Allow retry v4l2 open failures a few times. | |
| constexpr int OPEN_RETRY_SLEEP_US = 100'000; // 100ms * MAX_RETRY = 0.5 seconds | |
| const std::regex kDevicePathRE("/dev/video([0-9]+)"); | |
| } // namespace | |
| std::string ExternalCameraDevice::kDeviceVersion = "1.1"; | |
| ExternalCameraDevice::ExternalCameraDevice(const std::string& devicePath, | |
| const ExternalCameraConfig& config) | |
| : mCameraId("-1"), mDevicePath(devicePath), mCfg(config) { | |
| std::smatch sm; | |
| if (std::regex_match(mDevicePath, sm, kDevicePathRE)) { | |
| mCameraId = std::to_string(mCfg.cameraIdOffset + std::stoi(sm[1])); | |
| } else { | |
| ALOGE("%s: device path match failed for %s", __FUNCTION__, mDevicePath.c_str()); | |
| } | |
| } | |
| ExternalCameraDevice::~ExternalCameraDevice() {} | |
| ndk::ScopedAStatus ExternalCameraDevice::getCameraCharacteristics(CameraMetadata* _aidl_return) { | |
| Mutex::Autolock _l(mLock); | |
| if (_aidl_return == nullptr) { | |
| return fromStatus(Status::ILLEGAL_ARGUMENT); | |
| } | |
| if (isInitFailedLocked()) { | |
| return fromStatus(Status::INTERNAL_ERROR); | |
| } | |
| const camera_metadata_t* rawMetadata = mCameraCharacteristics.getAndLock(); | |
| convertToAidl(rawMetadata, _aidl_return); | |
| mCameraCharacteristics.unlock(rawMetadata); | |
| return fromStatus(Status::OK); | |
| } | |
| ndk::ScopedAStatus ExternalCameraDevice::getPhysicalCameraCharacteristics(const std::string&, | |
| CameraMetadata*) { | |
| ALOGE("%s: Physical camera functions are not supported for external cameras.", __FUNCTION__); | |
| return fromStatus(Status::ILLEGAL_ARGUMENT); | |
| } | |
| ndk::ScopedAStatus ExternalCameraDevice::getResourceCost(CameraResourceCost* _aidl_return) { | |
| if (_aidl_return == nullptr) { | |
| return fromStatus(Status::ILLEGAL_ARGUMENT); | |
| } | |
| _aidl_return->resourceCost = 100; | |
| return fromStatus(Status::OK); | |
| } | |
| ndk::ScopedAStatus ExternalCameraDevice::isStreamCombinationSupported( | |
| const StreamConfiguration& in_streams, bool* _aidl_return) { | |
| if (isInitFailed()) { | |
| ALOGE("%s: camera %s. camera init failed!", __FUNCTION__, mCameraId.c_str()); | |
| return fromStatus(Status::INTERNAL_ERROR); | |
| } | |
| Status s = ExternalCameraDeviceSession::isStreamCombinationSupported(in_streams, | |
| mSupportedFormats, mCfg); | |
| *_aidl_return = s == Status::OK; | |
| return fromStatus(Status::OK); | |
| } | |
| ndk::ScopedAStatus ExternalCameraDevice::open( | |
| const std::shared_ptr<ICameraDeviceCallback>& in_callback, | |
| std::shared_ptr<ICameraDeviceSession>* _aidl_return) { | |
| if (_aidl_return == nullptr) { | |
| ALOGE("%s: cannot open camera %s. return session ptr is null!", __FUNCTION__, | |
| mCameraId.c_str()); | |
| return fromStatus(Status::ILLEGAL_ARGUMENT); | |
| } | |
| Mutex::Autolock _l(mLock); | |
| if (isInitFailedLocked()) { | |
| ALOGE("%s: cannot open camera %s. camera init failed!", __FUNCTION__, mCameraId.c_str()); | |
| return fromStatus(Status::INTERNAL_ERROR); | |
| } | |
| std::shared_ptr<ExternalCameraDeviceSession> session; | |
| ALOGV("%s: Initializing device for camera %s", __FUNCTION__, mCameraId.c_str()); | |
| session = mSession.lock(); | |
| if (session != nullptr && !session->isClosed()) { | |
| ALOGE("%s: cannot open an already opened camera!", __FUNCTION__); | |
| return fromStatus(Status::CAMERA_IN_USE); | |
| } | |
| int numAttempt = 0; | |
| unique_fd fd(::open(mDevicePath.c_str(), O_RDWR)); | |
| while (fd.get() < 0 && numAttempt < MAX_RETRY) { | |
| // Previous retry attempts failed. Retry opening the device at most MAX_RETRY times | |
| ALOGW("%s: v4l2 device %s open failed, wait 33ms and try again", __FUNCTION__, | |
| mDevicePath.c_str()); | |
| usleep(OPEN_RETRY_SLEEP_US); // sleep and try again | |
| fd.reset(::open(mDevicePath.c_str(), O_RDWR)); | |
| numAttempt++; | |
| } | |
| if (fd.get() < 0) { | |
| ALOGE("%s: v4l2 device open %s failed: %s", __FUNCTION__, mDevicePath.c_str(), | |
| strerror(errno)); | |
| return fromStatus(Status::INTERNAL_ERROR); | |
| } | |
| session = createSession(in_callback, mCfg, mSupportedFormats, mCroppingType, | |
| mCameraCharacteristics, mCameraId, std::move(fd)); | |
| if (session == nullptr) { | |
| ALOGE("%s: camera device session allocation failed", __FUNCTION__); | |
| return fromStatus(Status::INTERNAL_ERROR); | |
| } | |
| if (session->isInitFailed()) { | |
| ALOGE("%s: camera device session init failed", __FUNCTION__); | |
| return fromStatus(Status::INTERNAL_ERROR); | |
| } | |
| mSession = session; | |
| *_aidl_return = session; | |
| return fromStatus(Status::OK); | |
| } | |
| ndk::ScopedAStatus ExternalCameraDevice::openInjectionSession( | |
| const std::shared_ptr<ICameraDeviceCallback>&, std::shared_ptr<ICameraInjectionSession>*) { | |
| return fromStatus(Status::OPERATION_NOT_SUPPORTED); | |
| } | |
| ndk::ScopedAStatus ExternalCameraDevice::setTorchMode(bool) { | |
| return fromStatus(Status::OPERATION_NOT_SUPPORTED); | |
| } | |
| ndk::ScopedAStatus ExternalCameraDevice::turnOnTorchWithStrengthLevel(int32_t) { | |
| return fromStatus(Status::OPERATION_NOT_SUPPORTED); | |
| } | |
| ndk::ScopedAStatus ExternalCameraDevice::getTorchStrengthLevel(int32_t*) { | |
| return fromStatus(Status::OPERATION_NOT_SUPPORTED); | |
| } | |
| std::shared_ptr<ExternalCameraDeviceSession> ExternalCameraDevice::createSession( | |
| const std::shared_ptr<ICameraDeviceCallback>& cb, const ExternalCameraConfig& cfg, | |
| const std::vector<SupportedV4L2Format>& sortedFormats, const CroppingType& croppingType, | |
| const common::V1_0::helper::CameraMetadata& chars, const std::string& cameraId, | |
| unique_fd v4l2Fd) { | |
| return ndk::SharedRefBase::make<ExternalCameraDeviceSession>( | |
| cb, cfg, sortedFormats, croppingType, chars, cameraId, std::move(v4l2Fd)); | |
| } | |
| bool ExternalCameraDevice::isInitFailed() { | |
| Mutex::Autolock _l(mLock); | |
| return isInitFailedLocked(); | |
| } | |
| bool ExternalCameraDevice::isInitFailedLocked() { | |
| if (!mInitialized) { | |
| status_t ret = initCameraCharacteristics(); | |
| if (ret != OK) { | |
| ALOGE("%s: init camera characteristics failed: errorno %d", __FUNCTION__, ret); | |
| mInitFailed = true; | |
| } | |
| mInitialized = true; | |
| } | |
| return mInitFailed; | |
| } | |
| void ExternalCameraDevice::initSupportedFormatsLocked(int fd) { | |
| std::vector<SupportedV4L2Format> horizontalFmts = | |
| getCandidateSupportedFormatsLocked(fd, HORIZONTAL, mCfg.fpsLimits, mCfg.depthFpsLimits, | |
| mCfg.minStreamSize, mCfg.depthEnabled); | |
| std::vector<SupportedV4L2Format> verticalFmts = | |
| getCandidateSupportedFormatsLocked(fd, VERTICAL, mCfg.fpsLimits, mCfg.depthFpsLimits, | |
| mCfg.minStreamSize, mCfg.depthEnabled); | |
| size_t horiSize = horizontalFmts.size(); | |
| size_t vertSize = verticalFmts.size(); | |
| if (horiSize == 0 && vertSize == 0) { | |
| ALOGE("%s: cannot find suitable cropping type!", __FUNCTION__); | |
| return; | |
| } | |
| if (horiSize == 0) { | |
| mSupportedFormats = verticalFmts; | |
| mCroppingType = VERTICAL; | |
| return; | |
| } else if (vertSize == 0) { | |
| mSupportedFormats = horizontalFmts; | |
| mCroppingType = HORIZONTAL; | |
| return; | |
| } | |
| const auto& maxHoriSize = horizontalFmts[horizontalFmts.size() - 1]; | |
| const auto& maxVertSize = verticalFmts[verticalFmts.size() - 1]; | |
| // Try to keep the largest possible output size | |
| // When they are the same or ambiguous, pick the one support more sizes | |
| if (maxHoriSize.width == maxVertSize.width && maxHoriSize.height == maxVertSize.height) { | |
| if (horiSize > vertSize) { | |
| mSupportedFormats = horizontalFmts; | |
| mCroppingType = HORIZONTAL; | |
| } else { | |
| mSupportedFormats = verticalFmts; | |
| mCroppingType = VERTICAL; | |
| } | |
| } else if (maxHoriSize.width >= maxVertSize.width && maxHoriSize.height >= maxVertSize.height) { | |
| mSupportedFormats = horizontalFmts; | |
| mCroppingType = HORIZONTAL; | |
| } else if (maxHoriSize.width <= maxVertSize.width && maxHoriSize.height <= maxVertSize.height) { | |
| mSupportedFormats = verticalFmts; | |
| mCroppingType = VERTICAL; | |
| } else { | |
| if (horiSize > vertSize) { | |
| mSupportedFormats = horizontalFmts; | |
| mCroppingType = HORIZONTAL; | |
| } else { | |
| mSupportedFormats = verticalFmts; | |
| mCroppingType = VERTICAL; | |
| } | |
| } | |
| } | |
| status_t ExternalCameraDevice::initCameraCharacteristics() { | |
| if (!mCameraCharacteristics.isEmpty()) { | |
| // Camera Characteristics previously initialized. Skip. | |
| return OK; | |
| } | |
| // init camera characteristics | |
| unique_fd fd(::open(mDevicePath.c_str(), O_RDWR)); | |
| if (fd.get() < 0) { | |
| ALOGE("%s: v4l2 device open %s failed", __FUNCTION__, mDevicePath.c_str()); | |
| return DEAD_OBJECT; | |
| } | |
| status_t ret; | |
| ret = initDefaultCharsKeys(&mCameraCharacteristics); | |
| if (ret != OK) { | |
| ALOGE("%s: init default characteristics key failed: errorno %d", __FUNCTION__, ret); | |
| mCameraCharacteristics.clear(); | |
| return ret; | |
| } | |
| ret = initCameraControlsCharsKeys(fd.get(), &mCameraCharacteristics); | |
| if (ret != OK) { | |
| ALOGE("%s: init camera control characteristics key failed: errorno %d", __FUNCTION__, ret); | |
| mCameraCharacteristics.clear(); | |
| return ret; | |
| } | |
| ret = initOutputCharsKeys(fd.get(), &mCameraCharacteristics); | |
| if (ret != OK) { | |
| ALOGE("%s: init output characteristics key failed: errorno %d", __FUNCTION__, ret); | |
| mCameraCharacteristics.clear(); | |
| return ret; | |
| } | |
| ret = initAvailableCapabilities(&mCameraCharacteristics); | |
| if (ret != OK) { | |
| ALOGE("%s: init available capabilities key failed: errorno %d", __FUNCTION__, ret); | |
| mCameraCharacteristics.clear(); | |
| return ret; | |
| } | |
| return OK; | |
| } | |
| #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) | |
| #define UPDATE(tag, data, size) \ | |
| do { \ | |
| if (metadata->update((tag), (data), (size))) { \ | |
| ALOGE("Update " #tag " failed!"); \ | |
| return -EINVAL; \ | |
| } \ | |
| } while (0) | |
| status_t ExternalCameraDevice::initAvailableCapabilities( | |
| ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) { | |
| if (mSupportedFormats.empty()) { | |
| ALOGE("%s: Supported formats list is empty", __FUNCTION__); | |
| return UNKNOWN_ERROR; | |
| } | |
| bool hasDepth = false; | |
| bool hasColor = false; | |
| for (const auto& fmt : mSupportedFormats) { | |
| switch (fmt.fourcc) { | |
| case V4L2_PIX_FMT_Z16: | |
| hasDepth = true; | |
| break; | |
| case V4L2_PIX_FMT_MJPEG: | |
| hasColor = true; | |
| break; | |
| default: | |
| ALOGW("%s: Unsupported format found", __FUNCTION__); | |
| } | |
| } | |
| std::vector<uint8_t> availableCapabilities; | |
| if (hasDepth) { | |
| availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT); | |
| } | |
| if (hasColor) { | |
| availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); | |
| } | |
| if (!availableCapabilities.empty()) { | |
| UPDATE(ANDROID_REQUEST_AVAILABLE_CAPABILITIES, availableCapabilities.data(), | |
| availableCapabilities.size()); | |
| } | |
| return OK; | |
| } | |
| status_t ExternalCameraDevice::initDefaultCharsKeys( | |
| ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) { | |
| const uint8_t hardware_level = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL; | |
| UPDATE(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL, &hardware_level, 1); | |
| // android.colorCorrection | |
| const uint8_t availableAberrationModes[] = {ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF}; | |
| UPDATE(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES, availableAberrationModes, | |
| ARRAY_SIZE(availableAberrationModes)); | |
| // android.control | |
| const uint8_t antibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO; | |
| UPDATE(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, &antibandingMode, 1); | |
| const int32_t controlMaxRegions[] = {/*AE*/ 0, /*AWB*/ 0, /*AF*/ 0}; | |
| UPDATE(ANDROID_CONTROL_MAX_REGIONS, controlMaxRegions, ARRAY_SIZE(controlMaxRegions)); | |
| const uint8_t videoStabilizationMode = ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF; | |
| UPDATE(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, &videoStabilizationMode, 1); | |
| const uint8_t awbAvailableMode = ANDROID_CONTROL_AWB_MODE_AUTO; | |
| UPDATE(ANDROID_CONTROL_AWB_AVAILABLE_MODES, &awbAvailableMode, 1); | |
| const uint8_t aeAvailableMode = ANDROID_CONTROL_AE_MODE_ON; | |
| UPDATE(ANDROID_CONTROL_AE_AVAILABLE_MODES, &aeAvailableMode, 1); | |
| const uint8_t availableFffect = ANDROID_CONTROL_EFFECT_MODE_OFF; | |
| UPDATE(ANDROID_CONTROL_AVAILABLE_EFFECTS, &availableFffect, 1); | |
| const uint8_t controlAvailableModes[] = {ANDROID_CONTROL_MODE_OFF, ANDROID_CONTROL_MODE_AUTO}; | |
| UPDATE(ANDROID_CONTROL_AVAILABLE_MODES, controlAvailableModes, | |
| ARRAY_SIZE(controlAvailableModes)); | |
| // android.edge | |
| const uint8_t edgeMode = ANDROID_EDGE_MODE_OFF; | |
| UPDATE(ANDROID_EDGE_AVAILABLE_EDGE_MODES, &edgeMode, 1); | |
| // android.flash | |
| const uint8_t flashInfo = ANDROID_FLASH_INFO_AVAILABLE_FALSE; | |
| UPDATE(ANDROID_FLASH_INFO_AVAILABLE, &flashInfo, 1); | |
| // android.hotPixel | |
| const uint8_t hotPixelMode = ANDROID_HOT_PIXEL_MODE_OFF; | |
| UPDATE(ANDROID_HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES, &hotPixelMode, 1); | |
| // android.info | |
| const uint8_t bufMgrVer = ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5; | |
| UPDATE(ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, &bufMgrVer, 1); | |
| // android.jpeg | |
| const int32_t jpegAvailableThumbnailSizes[] = {0, 0, 176, 144, 240, 144, 256, | |
| 144, 240, 160, 256, 154, 240, 180}; | |
| UPDATE(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, jpegAvailableThumbnailSizes, | |
| ARRAY_SIZE(jpegAvailableThumbnailSizes)); | |
| const int32_t jpegMaxSize = mCfg.maxJpegBufSize; | |
| UPDATE(ANDROID_JPEG_MAX_SIZE, &jpegMaxSize, 1); | |
| // android.lens | |
| const uint8_t focusDistanceCalibration = | |
| ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED; | |
| UPDATE(ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION, &focusDistanceCalibration, 1); | |
| const uint8_t opticalStabilizationMode = ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF; | |
| UPDATE(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION, &opticalStabilizationMode, 1); | |
| const uint8_t facing = ANDROID_LENS_FACING_EXTERNAL; | |
| UPDATE(ANDROID_LENS_FACING, &facing, 1); | |
| // android.noiseReduction | |
| const uint8_t noiseReductionMode = ANDROID_NOISE_REDUCTION_MODE_OFF; | |
| UPDATE(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES, &noiseReductionMode, 1); | |
| UPDATE(ANDROID_NOISE_REDUCTION_MODE, &noiseReductionMode, 1); | |
| const int32_t partialResultCount = 1; | |
| UPDATE(ANDROID_REQUEST_PARTIAL_RESULT_COUNT, &partialResultCount, 1); | |
| // This means pipeline latency of X frame intervals. The maximum number is 4. | |
| const uint8_t requestPipelineMaxDepth = 4; | |
| UPDATE(ANDROID_REQUEST_PIPELINE_MAX_DEPTH, &requestPipelineMaxDepth, 1); | |
| // Three numbers represent the maximum numbers of different types of output | |
| // streams simultaneously. The types are raw sensor, processed (but not | |
| // stalling), and processed (but stalling). For usb limited mode, raw sensor | |
| // is not supported. Stalling stream is JPEG. Non-stalling streams are | |
| // YUV_420_888 or YV12. | |
| const int32_t requestMaxNumOutputStreams[] = { | |
| /*RAW*/ 0, | |
| /*Processed*/ ExternalCameraDeviceSession::kMaxProcessedStream, | |
| /*Stall*/ ExternalCameraDeviceSession::kMaxStallStream}; | |
| UPDATE(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS, requestMaxNumOutputStreams, | |
| ARRAY_SIZE(requestMaxNumOutputStreams)); | |
| // Limited mode doesn't support reprocessing. | |
| const int32_t requestMaxNumInputStreams = 0; | |
| UPDATE(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS, &requestMaxNumInputStreams, 1); | |
| // android.scaler | |
| // TODO: b/72263447 V4L2_CID_ZOOM_* | |
| const float scalerAvailableMaxDigitalZoom[] = {1}; | |
| UPDATE(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, scalerAvailableMaxDigitalZoom, | |
| ARRAY_SIZE(scalerAvailableMaxDigitalZoom)); | |
| const uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY; | |
| UPDATE(ANDROID_SCALER_CROPPING_TYPE, &croppingType, 1); | |
| const int32_t testPatternModes[] = {ANDROID_SENSOR_TEST_PATTERN_MODE_OFF, | |
| ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR}; | |
| UPDATE(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES, testPatternModes, | |
| ARRAY_SIZE(testPatternModes)); | |
| const uint8_t timestampSource = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN; | |
| UPDATE(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE, ×tampSource, 1); | |
| // Orientation is a bit odd for external camera, but consider it as the orientation | |
| // between the external camera sensor (which is usually landscape) and the device's | |
| // natural display orientation. For devices with natural landscape display (ex: tablet/TV), the | |
| // orientation should be 0. For devices with natural portrait display (phone), the orientation | |
| // should be 270. | |
| const int32_t orientation = mCfg.orientation; | |
| UPDATE(ANDROID_SENSOR_ORIENTATION, &orientation, 1); | |
| // android.shading | |
| const uint8_t availableMode = ANDROID_SHADING_MODE_OFF; | |
| UPDATE(ANDROID_SHADING_AVAILABLE_MODES, &availableMode, 1); | |
| // android.statistics | |
| const uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF; | |
| UPDATE(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES, &faceDetectMode, 1); | |
| const int32_t maxFaceCount = 0; | |
| UPDATE(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT, &maxFaceCount, 1); | |
| const uint8_t availableHotpixelMode = ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE_OFF; | |
| UPDATE(ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES, &availableHotpixelMode, 1); | |
| const uint8_t lensShadingMapMode = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF; | |
| UPDATE(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES, &lensShadingMapMode, 1); | |
| // android.sync | |
| const int32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN; | |
| UPDATE(ANDROID_SYNC_MAX_LATENCY, &maxLatency, 1); | |
| const uint8_t sensorReadoutTimestamp = ANDROID_SENSOR_READOUT_TIMESTAMP_NOT_SUPPORTED; | |
| UPDATE(ANDROID_SENSOR_READOUT_TIMESTAMP, &sensorReadoutTimestamp, 1); | |
| /* Other sensor/RAW related keys: | |
| * android.sensor.info.colorFilterArrangement -> no need if we don't do RAW | |
| * android.sensor.info.physicalSize -> not available | |
| * android.sensor.info.whiteLevel -> not available/not needed | |
| * android.sensor.info.lensShadingApplied -> not needed | |
| * android.sensor.info.preCorrectionActiveArraySize -> not available/not needed | |
| * android.sensor.blackLevelPattern -> not available/not needed | |
| */ | |
| const int32_t availableRequestKeys[] = {ANDROID_COLOR_CORRECTION_ABERRATION_MODE, | |
| ANDROID_CONTROL_AE_ANTIBANDING_MODE, | |
| ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, | |
| ANDROID_CONTROL_AE_LOCK, | |
| ANDROID_CONTROL_AE_MODE, | |
| ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER, | |
| ANDROID_CONTROL_AE_TARGET_FPS_RANGE, | |
| ANDROID_CONTROL_AF_MODE, | |
| ANDROID_CONTROL_AF_TRIGGER, | |
| ANDROID_CONTROL_AWB_LOCK, | |
| ANDROID_CONTROL_AWB_MODE, | |
| ANDROID_CONTROL_CAPTURE_INTENT, | |
| ANDROID_CONTROL_EFFECT_MODE, | |
| ANDROID_CONTROL_MODE, | |
| ANDROID_CONTROL_SCENE_MODE, | |
| ANDROID_CONTROL_VIDEO_STABILIZATION_MODE, | |
| ANDROID_FLASH_MODE, | |
| ANDROID_JPEG_ORIENTATION, | |
| ANDROID_JPEG_QUALITY, | |
| ANDROID_JPEG_THUMBNAIL_QUALITY, | |
| ANDROID_JPEG_THUMBNAIL_SIZE, | |
| ANDROID_LENS_OPTICAL_STABILIZATION_MODE, | |
| ANDROID_NOISE_REDUCTION_MODE, | |
| ANDROID_SCALER_CROP_REGION, | |
| ANDROID_SENSOR_TEST_PATTERN_MODE, | |
| ANDROID_STATISTICS_FACE_DETECT_MODE, | |
| ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE}; | |
| UPDATE(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, availableRequestKeys, | |
| ARRAY_SIZE(availableRequestKeys)); | |
| const int32_t availableResultKeys[] = {ANDROID_COLOR_CORRECTION_ABERRATION_MODE, | |
| ANDROID_CONTROL_AE_ANTIBANDING_MODE, | |
| ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, | |
| ANDROID_CONTROL_AE_LOCK, | |
| ANDROID_CONTROL_AE_MODE, | |
| ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER, | |
| ANDROID_CONTROL_AE_STATE, | |
| ANDROID_CONTROL_AE_TARGET_FPS_RANGE, | |
| ANDROID_CONTROL_AF_MODE, | |
| ANDROID_CONTROL_AF_STATE, | |
| ANDROID_CONTROL_AF_TRIGGER, | |
| ANDROID_CONTROL_AWB_LOCK, | |
| ANDROID_CONTROL_AWB_MODE, | |
| ANDROID_CONTROL_AWB_STATE, | |
| ANDROID_CONTROL_CAPTURE_INTENT, | |
| ANDROID_CONTROL_EFFECT_MODE, | |
| ANDROID_CONTROL_MODE, | |
| ANDROID_CONTROL_SCENE_MODE, | |
| ANDROID_CONTROL_VIDEO_STABILIZATION_MODE, | |
| ANDROID_FLASH_MODE, | |
| ANDROID_FLASH_STATE, | |
| ANDROID_JPEG_ORIENTATION, | |
| ANDROID_JPEG_QUALITY, | |
| ANDROID_JPEG_THUMBNAIL_QUALITY, | |
| ANDROID_JPEG_THUMBNAIL_SIZE, | |
| ANDROID_LENS_OPTICAL_STABILIZATION_MODE, | |
| ANDROID_NOISE_REDUCTION_MODE, | |
| ANDROID_REQUEST_PIPELINE_DEPTH, | |
| ANDROID_SCALER_CROP_REGION, | |
| ANDROID_SENSOR_TIMESTAMP, | |
| ANDROID_STATISTICS_FACE_DETECT_MODE, | |
| ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE, | |
| ANDROID_STATISTICS_LENS_SHADING_MAP_MODE, | |
| ANDROID_STATISTICS_SCENE_FLICKER}; | |
| UPDATE(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, availableResultKeys, | |
| ARRAY_SIZE(availableResultKeys)); | |
| UPDATE(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, AVAILABLE_CHARACTERISTICS_KEYS.data(), | |
| AVAILABLE_CHARACTERISTICS_KEYS.size()); | |
| return OK; | |
| } | |
| status_t ExternalCameraDevice::initCameraControlsCharsKeys( | |
| int, ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) { | |
| // android.sensor.info.sensitivityRange -> V4L2_CID_ISO_SENSITIVITY | |
| // android.sensor.info.exposureTimeRange -> V4L2_CID_EXPOSURE_ABSOLUTE | |
| // android.sensor.info.maxFrameDuration -> TBD | |
| // android.lens.info.minimumFocusDistance -> V4L2_CID_FOCUS_ABSOLUTE | |
| // android.lens.info.hyperfocalDistance | |
| // android.lens.info.availableFocalLengths -> not available? | |
| // android.control | |
| // No AE compensation support for now. | |
| // TODO: V4L2_CID_EXPOSURE_BIAS | |
| const int32_t controlAeCompensationRange[] = {0, 0}; | |
| UPDATE(ANDROID_CONTROL_AE_COMPENSATION_RANGE, controlAeCompensationRange, | |
| ARRAY_SIZE(controlAeCompensationRange)); | |
| const camera_metadata_rational_t controlAeCompensationStep[] = {{0, 1}}; | |
| UPDATE(ANDROID_CONTROL_AE_COMPENSATION_STEP, controlAeCompensationStep, | |
| ARRAY_SIZE(controlAeCompensationStep)); | |
| // TODO: Check V4L2_CID_AUTO_FOCUS_*. | |
| const uint8_t afAvailableModes[] = {ANDROID_CONTROL_AF_MODE_AUTO, ANDROID_CONTROL_AF_MODE_OFF}; | |
| UPDATE(ANDROID_CONTROL_AF_AVAILABLE_MODES, afAvailableModes, ARRAY_SIZE(afAvailableModes)); | |
| // TODO: V4L2_CID_SCENE_MODE | |
| const uint8_t availableSceneMode = ANDROID_CONTROL_SCENE_MODE_DISABLED; | |
| UPDATE(ANDROID_CONTROL_AVAILABLE_SCENE_MODES, &availableSceneMode, 1); | |
| // TODO: V4L2_CID_3A_LOCK | |
| const uint8_t aeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE; | |
| UPDATE(ANDROID_CONTROL_AE_LOCK_AVAILABLE, &aeLockAvailable, 1); | |
| const uint8_t awbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE; | |
| UPDATE(ANDROID_CONTROL_AWB_LOCK_AVAILABLE, &awbLockAvailable, 1); | |
| // TODO: V4L2_CID_ZOOM_* | |
| const float scalerAvailableMaxDigitalZoom[] = {1}; | |
| UPDATE(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, scalerAvailableMaxDigitalZoom, | |
| ARRAY_SIZE(scalerAvailableMaxDigitalZoom)); | |
| return OK; | |
| } | |
| status_t ExternalCameraDevice::initOutputCharsKeys( | |
| int fd, ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) { | |
| initSupportedFormatsLocked(fd); | |
| if (mSupportedFormats.empty()) { | |
| ALOGE("%s: Init supported format list failed", __FUNCTION__); | |
| return UNKNOWN_ERROR; | |
| } | |
| bool hasDepth = false; | |
| bool hasColor = false; | |
| // For V4L2_PIX_FMT_Z16 | |
| std::array<int, /*size*/ 1> halDepthFormats{{HAL_PIXEL_FORMAT_Y16}}; | |
| // For V4L2_PIX_FMT_MJPEG | |
| std::array<int, /*size*/ 3> halFormats{{HAL_PIXEL_FORMAT_BLOB, HAL_PIXEL_FORMAT_YCbCr_420_888, | |
| HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED}}; | |
| for (const auto& supportedFormat : mSupportedFormats) { | |
| switch (supportedFormat.fourcc) { | |
| case V4L2_PIX_FMT_Z16: | |
| hasDepth = true; | |
| break; | |
| case V4L2_PIX_FMT_MJPEG: | |
| hasColor = true; | |
| break; | |
| default: | |
| ALOGW("%s: format %c%c%c%c is not supported!", __FUNCTION__, | |
| supportedFormat.fourcc & 0xFF, (supportedFormat.fourcc >> 8) & 0xFF, | |
| (supportedFormat.fourcc >> 16) & 0xFF, (supportedFormat.fourcc >> 24) & 0xFF); | |
| } | |
| } | |
| if (hasDepth) { | |
| status_t ret = initOutputCharsKeysByFormat( | |
| metadata, V4L2_PIX_FMT_Z16, halDepthFormats, | |
| ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_OUTPUT, | |
| ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, | |
| ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS, | |
| ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS); | |
| if (ret != OK) { | |
| ALOGE("%s: Unable to initialize depth format keys: %s", __FUNCTION__, | |
| statusToString(ret).c_str()); | |
| return ret; | |
| } | |
| } | |
| if (hasColor) { | |
| status_t ret = | |
| initOutputCharsKeysByFormat(metadata, V4L2_PIX_FMT_MJPEG, halFormats, | |
| ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT, | |
| ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, | |
| ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, | |
| ANDROID_SCALER_AVAILABLE_STALL_DURATIONS); | |
| if (ret != OK) { | |
| ALOGE("%s: Unable to initialize color format keys: %s", __FUNCTION__, | |
| statusToString(ret).c_str()); | |
| return ret; | |
| } | |
| } | |
| status_t ret = calculateMinFps(metadata); | |
| if (ret != OK) { | |
| ALOGE("%s: Unable to update fps metadata: %s", __FUNCTION__, statusToString(ret).c_str()); | |
| return ret; | |
| } | |
| SupportedV4L2Format maximumFormat{.width = 0, .height = 0}; | |
| for (const auto& supportedFormat : mSupportedFormats) { | |
| if (supportedFormat.width >= maximumFormat.width && | |
| supportedFormat.height >= maximumFormat.height) { | |
| maximumFormat = supportedFormat; | |
| } | |
| } | |
| int32_t activeArraySize[] = {0, 0, static_cast<int32_t>(maximumFormat.width), | |
| static_cast<int32_t>(maximumFormat.height)}; | |
| UPDATE(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, activeArraySize, | |
| ARRAY_SIZE(activeArraySize)); | |
| UPDATE(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, activeArraySize, ARRAY_SIZE(activeArraySize)); | |
| int32_t pixelArraySize[] = {static_cast<int32_t>(maximumFormat.width), | |
| static_cast<int32_t>(maximumFormat.height)}; | |
| UPDATE(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, pixelArraySize, ARRAY_SIZE(pixelArraySize)); | |
| return OK; | |
| } | |
| template <size_t SIZE> | |
| status_t ExternalCameraDevice::initOutputCharsKeysByFormat( | |
| ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata, | |
| uint32_t fourcc, const std::array<int, SIZE>& halFormats, int streamConfigTag, | |
| int streamConfigurationKey, int minFrameDurationKey, int stallDurationKey) { | |
| if (mSupportedFormats.empty()) { | |
| ALOGE("%s: Init supported format list failed", __FUNCTION__); | |
| return UNKNOWN_ERROR; | |
| } | |
| std::vector<int32_t> streamConfigurations; | |
| std::vector<int64_t> minFrameDurations; | |
| std::vector<int64_t> stallDurations; | |
| for (const auto& supportedFormat : mSupportedFormats) { | |
| if (supportedFormat.fourcc != fourcc) { | |
| // Skip 4CCs not meant for the halFormats | |
| continue; | |
| } | |
| for (const auto& format : halFormats) { | |
| streamConfigurations.push_back(format); | |
| streamConfigurations.push_back(supportedFormat.width); | |
| streamConfigurations.push_back(supportedFormat.height); | |
| streamConfigurations.push_back(streamConfigTag); | |
| } | |
| int64_t minFrameDuration = std::numeric_limits<int64_t>::max(); | |
| for (const auto& fr : supportedFormat.frameRates) { | |
| // 1000000000LL < (2^32 - 1) and | |
| // fr.durationNumerator is uint32_t, so no overflow here | |
| int64_t frameDuration = 1000000000LL * fr.durationNumerator / fr.durationDenominator; | |
| if (frameDuration < minFrameDuration) { | |
| minFrameDuration = frameDuration; | |
| } | |
| } | |
| for (const auto& format : halFormats) { | |
| minFrameDurations.push_back(format); | |
| minFrameDurations.push_back(supportedFormat.width); | |
| minFrameDurations.push_back(supportedFormat.height); | |
| minFrameDurations.push_back(minFrameDuration); | |
| } | |
| // The stall duration is 0 for non-jpeg formats. For JPEG format, stall | |
| // duration can be 0 if JPEG is small. Here we choose 1 sec for JPEG. | |
| // TODO: b/72261675. Maybe set this dynamically | |
| for (const auto& format : halFormats) { | |
| const int64_t NS_TO_SECOND = 1E9; | |
| int64_t stall_duration = (format == HAL_PIXEL_FORMAT_BLOB) ? NS_TO_SECOND : 0; | |
| stallDurations.push_back(format); | |
| stallDurations.push_back(supportedFormat.width); | |
| stallDurations.push_back(supportedFormat.height); | |
| stallDurations.push_back(stall_duration); | |
| } | |
| } | |
| UPDATE(streamConfigurationKey, streamConfigurations.data(), streamConfigurations.size()); | |
| UPDATE(minFrameDurationKey, minFrameDurations.data(), minFrameDurations.size()); | |
| UPDATE(stallDurationKey, stallDurations.data(), stallDurations.size()); | |
| return OK; | |
| } | |
| status_t ExternalCameraDevice::calculateMinFps( | |
| ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) { | |
| std::set<int32_t> framerates; | |
| int32_t minFps = std::numeric_limits<int32_t>::max(); | |
| for (const auto& supportedFormat : mSupportedFormats) { | |
| for (const auto& fr : supportedFormat.frameRates) { | |
| int32_t frameRateInt = static_cast<int32_t>(fr.getFramesPerSecond()); | |
| if (minFps > frameRateInt) { | |
| minFps = frameRateInt; | |
| } | |
| framerates.insert(frameRateInt); | |
| } | |
| } | |
| std::vector<int32_t> fpsRanges; | |
| // FPS ranges | |
| for (const auto& framerate : framerates) { | |
| // Empirical: webcams often have close to 2x fps error and cannot support fixed fps range | |
| fpsRanges.push_back(framerate / 2); | |
| fpsRanges.push_back(framerate); | |
| } | |
| minFps /= 2; | |
| int64_t maxFrameDuration = 1000000000LL / minFps; | |
| UPDATE(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, fpsRanges.data(), fpsRanges.size()); | |
| UPDATE(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION, &maxFrameDuration, 1); | |
| return OK; | |
| } | |
| #undef ARRAY_SIZE | |
| #undef UPDATE | |
| void ExternalCameraDevice::getFrameRateList(int fd, double fpsUpperBound, | |
| SupportedV4L2Format* format) { | |
| format->frameRates.clear(); | |
| v4l2_frmivalenum frameInterval{ | |
| .index = 0, | |
| .pixel_format = format->fourcc, | |
| .width = static_cast<__u32>(format->width), | |
| .height = static_cast<__u32>(format->height), | |
| }; | |
| for (frameInterval.index = 0; | |
| TEMP_FAILURE_RETRY(ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frameInterval)) == 0; | |
| ++frameInterval.index) { | |
| if (frameInterval.type == V4L2_FRMIVAL_TYPE_DISCRETE) { | |
| if (frameInterval.discrete.numerator != 0) { | |
| SupportedV4L2Format::FrameRate fr = {frameInterval.discrete.numerator, | |
| frameInterval.discrete.denominator}; | |
| double framerate = fr.getFramesPerSecond(); | |
| if (framerate > fpsUpperBound) { | |
| continue; | |
| } | |
| ALOGV("index:%d, format:%c%c%c%c, w %d, h %d, framerate %f", frameInterval.index, | |
| frameInterval.pixel_format & 0xFF, (frameInterval.pixel_format >> 8) & 0xFF, | |
| (frameInterval.pixel_format >> 16) & 0xFF, | |
| (frameInterval.pixel_format >> 24) & 0xFF, frameInterval.width, | |
| frameInterval.height, framerate); | |
| format->frameRates.push_back(fr); | |
| } | |
| } | |
| } | |
| if (format->frameRates.empty()) { | |
| ALOGE("%s: failed to get supported frame rates for format:%c%c%c%c w %d h %d", __FUNCTION__, | |
| frameInterval.pixel_format & 0xFF, (frameInterval.pixel_format >> 8) & 0xFF, | |
| (frameInterval.pixel_format >> 16) & 0xFF, (frameInterval.pixel_format >> 24) & 0xFF, | |
| frameInterval.width, frameInterval.height); | |
| } | |
| } | |
| void ExternalCameraDevice::updateFpsBounds( | |
| int fd, CroppingType cropType, | |
| const std::vector<ExternalCameraConfig::FpsLimitation>& fpsLimits, | |
| SupportedV4L2Format format, std::vector<SupportedV4L2Format>& outFmts) { | |
| double fpsUpperBound = -1.0; | |
| for (const auto& limit : fpsLimits) { | |
| if (cropType == VERTICAL) { | |
| if (format.width <= limit.size.width) { | |
| fpsUpperBound = limit.fpsUpperBound; | |
| break; | |
| } | |
| } else { // HORIZONTAL | |
| if (format.height <= limit.size.height) { | |
| fpsUpperBound = limit.fpsUpperBound; | |
| break; | |
| } | |
| } | |
| } | |
| if (fpsUpperBound < 0.f) { | |
| return; | |
| } | |
| getFrameRateList(fd, fpsUpperBound, &format); | |
| if (!format.frameRates.empty()) { | |
| outFmts.push_back(format); | |
| } | |
| } | |
| std::vector<SupportedV4L2Format> ExternalCameraDevice::getCandidateSupportedFormatsLocked( | |
| int fd, CroppingType cropType, | |
| const std::vector<ExternalCameraConfig::FpsLimitation>& fpsLimits, | |
| const std::vector<ExternalCameraConfig::FpsLimitation>& depthFpsLimits, | |
| const Size& minStreamSize, bool depthEnabled) { | |
| std::vector<SupportedV4L2Format> outFmts; | |
| struct v4l2_fmtdesc fmtdesc { | |
| .index = 0, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE | |
| }; | |
| int ret = 0; | |
| while (ret == 0) { | |
| ret = TEMP_FAILURE_RETRY(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc)); | |
| ALOGV("index:%d,ret:%d, format:%c%c%c%c", fmtdesc.index, ret, fmtdesc.pixelformat & 0xFF, | |
| (fmtdesc.pixelformat >> 8) & 0xFF, (fmtdesc.pixelformat >> 16) & 0xFF, | |
| (fmtdesc.pixelformat >> 24) & 0xFF); | |
| if (ret != 0 || (fmtdesc.flags & V4L2_FMT_FLAG_EMULATED)) { | |
| // Skip if IOCTL failed, or if the format is emulated | |
| fmtdesc.index++; | |
| continue; | |
| } | |
| auto it = | |
| std::find(kSupportedFourCCs.begin(), kSupportedFourCCs.end(), fmtdesc.pixelformat); | |
| if (it == kSupportedFourCCs.end()) { | |
| fmtdesc.index++; | |
| continue; | |
| } | |
| // Found supported format | |
| v4l2_frmsizeenum frameSize{.index = 0, .pixel_format = fmtdesc.pixelformat}; | |
| for (; TEMP_FAILURE_RETRY(ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frameSize)) == 0; | |
| ++frameSize.index) { | |
| if (frameSize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { | |
| ALOGV("index:%d, format:%c%c%c%c, w %d, h %d", frameSize.index, | |
| fmtdesc.pixelformat & 0xFF, (fmtdesc.pixelformat >> 8) & 0xFF, | |
| (fmtdesc.pixelformat >> 16) & 0xFF, (fmtdesc.pixelformat >> 24) & 0xFF, | |
| frameSize.discrete.width, frameSize.discrete.height); | |
| // Disregard h > w formats so all aspect ratio (h/w) <= 1.0 | |
| // This will simplify the crop/scaling logic down the road | |
| if (frameSize.discrete.height > frameSize.discrete.width) { | |
| continue; | |
| } | |
| // Discard all formats which is smaller than minStreamSize | |
| if (frameSize.discrete.width < minStreamSize.width || | |
| frameSize.discrete.height < minStreamSize.height) { | |
| continue; | |
| } | |
| SupportedV4L2Format format{ | |
| .width = static_cast<int32_t>(frameSize.discrete.width), | |
| .height = static_cast<int32_t>(frameSize.discrete.height), | |
| .fourcc = fmtdesc.pixelformat}; | |
| if (format.fourcc == V4L2_PIX_FMT_Z16 && depthEnabled) { | |
| updateFpsBounds(fd, cropType, depthFpsLimits, format, outFmts); | |
| } else { | |
| updateFpsBounds(fd, cropType, fpsLimits, format, outFmts); | |
| } | |
| } | |
| } | |
| fmtdesc.index++; | |
| } | |
| trimSupportedFormats(cropType, &outFmts); | |
| return outFmts; | |
| } | |
| void ExternalCameraDevice::trimSupportedFormats(CroppingType cropType, | |
| std::vector<SupportedV4L2Format>* pFmts) { | |
| std::vector<SupportedV4L2Format>& sortedFmts = *pFmts; | |
| if (cropType == VERTICAL) { | |
| std::sort(sortedFmts.begin(), sortedFmts.end(), | |
| [](const SupportedV4L2Format& a, const SupportedV4L2Format& b) -> bool { | |
| if (a.width == b.width) { | |
| return a.height < b.height; | |
| } | |
| return a.width < b.width; | |
| }); | |
| } else { | |
| std::sort(sortedFmts.begin(), sortedFmts.end(), | |
| [](const SupportedV4L2Format& a, const SupportedV4L2Format& b) -> bool { | |
| if (a.height == b.height) { | |
| return a.width < b.width; | |
| } | |
| return a.height < b.height; | |
| }); | |
| } | |
| if (sortedFmts.empty()) { | |
| ALOGE("%s: input format list is empty!", __FUNCTION__); | |
| return; | |
| } | |
| const auto& maxSize = sortedFmts[sortedFmts.size() - 1]; | |
| float maxSizeAr = ASPECT_RATIO(maxSize); | |
| // Remove formats that has aspect ratio not croppable from largest size | |
| std::vector<SupportedV4L2Format> out; | |
| for (const auto& fmt : sortedFmts) { | |
| float ar = ASPECT_RATIO(fmt); | |
| if (isAspectRatioClose(ar, maxSizeAr)) { | |
| out.push_back(fmt); | |
| } else if (cropType == HORIZONTAL && ar < maxSizeAr) { | |
| out.push_back(fmt); | |
| } else if (cropType == VERTICAL && ar > maxSizeAr) { | |
| out.push_back(fmt); | |
| } else { | |
| ALOGV("%s: size (%d,%d) is removed due to unable to crop %s from (%d,%d)", __FUNCTION__, | |
| fmt.width, fmt.height, cropType == VERTICAL ? "vertically" : "horizontally", | |
| maxSize.width, maxSize.height); | |
| } | |
| } | |
| sortedFmts = out; | |
| } | |
| binder_status_t ExternalCameraDevice::dump(int fd, const char** args, uint32_t numArgs) { | |
| std::shared_ptr<ExternalCameraDeviceSession> session = mSession.lock(); | |
| if (session == nullptr) { | |
| dprintf(fd, "No active camera device session instance\n"); | |
| return STATUS_OK; | |
| } | |
| return session->dump(fd, args, numArgs); | |
| } | |
| } // namespace implementation | |
| } // namespace device | |
| } // namespace camera | |
| } // namespace hardware | |
| } // namespace android |