Camera: Add external camera provider
Bug: 64874137 63873538
Change-Id: I4309874a7dedd3dd71d4bd0c2004d460421db679
diff --git a/camera/device/3.4/default/ExternalCameraDevice.cpp b/camera/device/3.4/default/ExternalCameraDevice.cpp
new file mode 100644
index 0000000..4ad1768
--- /dev/null
+++ b/camera/device/3.4/default/ExternalCameraDevice.cpp
@@ -0,0 +1,793 @@
+/*
+ * Copyright (C) 2018 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@3.4"
+#define LOG_NDEBUG 0
+#include <log/log.h>
+
+#include <array>
+#include <linux/videodev2.h>
+#include "android-base/macros.h"
+#include "CameraMetadata.h"
+#include "../../3.2/default/include/convert.h"
+#include "ExternalCameraDevice_3_4.h"
+
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V3_4 {
+namespace implementation {
+
+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*/1> kSupportedFourCCs {{
+ V4L2_PIX_FMT_MJPEG
+}}; // double braces required in C++11
+
+// TODO: b/72261897
+// Define max size/fps this Android device can advertise (and streaming at reasonable speed)
+// Also make sure that can be done without editing source code
+
+// TODO: b/72261675: make it dynamic since this affects memory usage
+const int kMaxJpegSize = {13 * 1024 * 1024}; // 13MB
+} // anonymous namespace
+
+ExternalCameraDevice::ExternalCameraDevice(const std::string& cameraId) :
+ mCameraId(cameraId) {
+ status_t ret = initCameraCharacteristics();
+ if (ret != OK) {
+ ALOGE("%s: init camera characteristics failed: errorno %d", __FUNCTION__, ret);
+ mInitFailed = true;
+ }
+}
+
+ExternalCameraDevice::~ExternalCameraDevice() {}
+
+bool ExternalCameraDevice::isInitFailed() {
+ return mInitFailed;
+}
+
+Return<void> ExternalCameraDevice::getResourceCost(getResourceCost_cb _hidl_cb) {
+ CameraResourceCost resCost;
+ resCost.resourceCost = 100;
+ _hidl_cb(Status::OK, resCost);
+ return Void();
+}
+
+Return<void> ExternalCameraDevice::getCameraCharacteristics(
+ getCameraCharacteristics_cb _hidl_cb) {
+ Mutex::Autolock _l(mLock);
+ V3_2::CameraMetadata hidlChars;
+
+ if (isInitFailed()) {
+ _hidl_cb(Status::INTERNAL_ERROR, hidlChars);
+ return Void();
+ }
+
+ const camera_metadata_t* rawMetadata = mCameraCharacteristics.getAndLock();
+ V3_2::implementation::convertToHidl(rawMetadata, &hidlChars);
+ _hidl_cb(Status::OK, hidlChars);
+ mCameraCharacteristics.unlock(rawMetadata);
+ return Void();
+}
+
+Return<Status> ExternalCameraDevice::setTorchMode(TorchMode) {
+ return Status::METHOD_NOT_SUPPORTED;
+}
+
+Return<void> ExternalCameraDevice::open(
+ const sp<ICameraDeviceCallback>& callback, open_cb _hidl_cb) {
+ Status status = Status::OK;
+ sp<ExternalCameraDeviceSession> session = nullptr;
+
+ if (callback == nullptr) {
+ ALOGE("%s: cannot open camera %s. callback is null!",
+ __FUNCTION__, mCameraId.c_str());
+ _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
+ return Void();
+ }
+
+ if (isInitFailed()) {
+ ALOGE("%s: cannot open camera %s. camera init failed!",
+ __FUNCTION__, mCameraId.c_str());
+ _hidl_cb(Status::INTERNAL_ERROR, nullptr);
+ return Void();
+ }
+
+ mLock.lock();
+
+ ALOGV("%s: Initializing device for camera %s", __FUNCTION__, mCameraId.c_str());
+ session = mSession.promote();
+ if (session != nullptr && !session->isClosed()) {
+ ALOGE("%s: cannot open an already opened camera!", __FUNCTION__);
+ mLock.unlock();
+ _hidl_cb(Status::CAMERA_IN_USE, nullptr);
+ return Void();
+ }
+
+ unique_fd fd(::open(mCameraId.c_str(), O_RDWR));
+ if (fd.get() < 0) {
+ ALOGE("%s: v4l2 device open %s failed: %s",
+ __FUNCTION__, mCameraId.c_str(), strerror(errno));
+ mLock.unlock();
+ _hidl_cb(Status::INTERNAL_ERROR, nullptr);
+ return Void();
+ }
+
+ session = new ExternalCameraDeviceSession(
+ callback, mSupportedFormats, mCameraCharacteristics, std::move(fd));
+ if (session == nullptr) {
+ ALOGE("%s: camera device session allocation failed", __FUNCTION__);
+ mLock.unlock();
+ _hidl_cb(Status::INTERNAL_ERROR, nullptr);
+ return Void();
+ }
+ if (session->isInitFailed()) {
+ ALOGE("%s: camera device session init failed", __FUNCTION__);
+ session = nullptr;
+ mLock.unlock();
+ _hidl_cb(Status::INTERNAL_ERROR, nullptr);
+ return Void();
+ }
+ mSession = session;
+
+ mLock.unlock();
+
+ _hidl_cb(status, session->getInterface());
+ return Void();
+}
+
+Return<void> ExternalCameraDevice::dumpState(const ::android::hardware::hidl_handle& handle) {
+ Mutex::Autolock _l(mLock);
+ if (handle.getNativeHandle() == nullptr) {
+ ALOGE("%s: handle must not be null", __FUNCTION__);
+ return Void();
+ }
+ if (handle->numFds != 1 || handle->numInts != 0) {
+ ALOGE("%s: handle must contain 1 FD and 0 integers! Got %d FDs and %d ints",
+ __FUNCTION__, handle->numFds, handle->numInts);
+ return Void();
+ }
+ int fd = handle->data[0];
+ if (mSession == nullptr) {
+ dprintf(fd, "No active camera device session instance\n");
+ return Void();
+ }
+ auto session = mSession.promote();
+ if (session == nullptr) {
+ dprintf(fd, "No active camera device session instance\n");
+ return Void();
+ }
+ // Call into active session to dump states
+ session->dumpState(handle);
+ return Void();
+}
+
+
+status_t ExternalCameraDevice::initCameraCharacteristics() {
+ if (mCameraCharacteristics.isEmpty()) {
+ // init camera characteristics
+ unique_fd fd(::open(mCameraId.c_str(), O_RDWR));
+ if (fd.get() < 0) {
+ ALOGE("%s: v4l2 device open %s failed", __FUNCTION__, mCameraId.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;
+ }
+ }
+ 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::initDefaultCharsKeys(
+ ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) {
+ // TODO: changed to HARDWARELEVEL_EXTERNAL later
+ const uint8_t hardware_level = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
+ 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.jpeg
+ // TODO: b/72261675 See if we can provide thumbnail size for all jpeg aspect ratios
+ const int32_t jpegAvailableThumbnailSizes[] = {0, 0, 240, 180};
+ UPDATE(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, jpegAvailableThumbnailSizes,
+ ARRAY_SIZE(jpegAvailableThumbnailSizes));
+
+ const int32_t jpegMaxSize = kMaxJpegSize;
+ UPDATE(ANDROID_JPEG_MAX_SIZE, &jpegMaxSize, 1);
+
+ const uint8_t jpegQuality = 90;
+ UPDATE(ANDROID_JPEG_QUALITY, &jpegQuality, 1);
+ UPDATE(ANDROID_JPEG_THUMBNAIL_QUALITY, &jpegQuality, 1);
+
+ const int32_t jpegOrientation = 0;
+ UPDATE(ANDROID_JPEG_ORIENTATION, &jpegOrientation, 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);
+
+ // android.request
+ const uint8_t availableCapabilities[] = {
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE};
+ UPDATE(ANDROID_REQUEST_AVAILABLE_CAPABILITIES, availableCapabilities,
+ ARRAY_SIZE(availableCapabilities));
+
+ 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);
+ UPDATE(ANDROID_REQUEST_PIPELINE_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};
+ UPDATE(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES, testPatternModes,
+ ARRAY_SIZE(testPatternModes));
+ UPDATE(ANDROID_SENSOR_TEST_PATTERN_MODE, &testPatternModes[0], 1);
+
+ const uint8_t timestampSource = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
+ UPDATE(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE, ×tampSource, 1);
+
+ // Orientation probably isn't useful for external facing camera?
+ const int32_t orientation = 0;
+ UPDATE(ANDROID_SENSOR_ORIENTATION, &orientation, 1);
+
+ // android.shading
+ const uint8_t availabeMode = ANDROID_SHADING_MODE_OFF;
+ UPDATE(ANDROID_SHADING_AVAILABLE_MODES, &availabeMode, 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);
+
+ /* Other sensor/RAW realted 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));
+
+ const int32_t availableCharacteristicsKeys[] = {
+ ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
+ ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
+ ANDROID_CONTROL_AE_AVAILABLE_MODES,
+ ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
+ ANDROID_CONTROL_AE_COMPENSATION_RANGE,
+ ANDROID_CONTROL_AE_COMPENSATION_STEP,
+ ANDROID_CONTROL_AE_LOCK_AVAILABLE,
+ ANDROID_CONTROL_AF_AVAILABLE_MODES,
+ ANDROID_CONTROL_AVAILABLE_EFFECTS,
+ ANDROID_CONTROL_AVAILABLE_MODES,
+ ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
+ ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
+ ANDROID_CONTROL_AWB_AVAILABLE_MODES,
+ ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+ ANDROID_CONTROL_MAX_REGIONS,
+ ANDROID_FLASH_INFO_AVAILABLE,
+ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
+ ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
+ ANDROID_LENS_FACING,
+ ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
+ ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
+ ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
+ ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
+ ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
+ ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
+ ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
+ ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+ ANDROID_SCALER_CROPPING_TYPE,
+ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+ ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
+ ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
+ ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
+ ANDROID_SENSOR_ORIENTATION,
+ ANDROID_SHADING_AVAILABLE_MODES,
+ ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
+ ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES,
+ ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
+ ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
+ ANDROID_SYNC_MAX_LATENCY};
+ UPDATE(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
+ availableCharacteristicsKeys,
+ ARRAY_SIZE(availableCharacteristicsKeys));
+
+ 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;
+ }
+
+ std::vector<int32_t> streamConfigurations;
+ std::vector<int64_t> minFrameDurations;
+ std::vector<int64_t> stallDurations;
+ int64_t maxFrameDuration = 0;
+ int32_t maxFps = std::numeric_limits<int32_t>::min();
+ int32_t minFps = std::numeric_limits<int32_t>::max();
+ std::set<int32_t> framerates;
+
+ 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) {
+ for (const auto& format : halFormats) {
+ streamConfigurations.push_back(format);
+ streamConfigurations.push_back(supportedFormat.width);
+ streamConfigurations.push_back(supportedFormat.height);
+ streamConfigurations.push_back(
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
+ }
+
+ int64_t min_frame_duration = std::numeric_limits<int64_t>::max();
+ for (const auto& frameRate : supportedFormat.frameRates) {
+ int64_t frame_duration = 1000000000LL / frameRate;
+ if (frame_duration < min_frame_duration) {
+ min_frame_duration = frame_duration;
+ }
+ if (frame_duration > maxFrameDuration) {
+ maxFrameDuration = frame_duration;
+ }
+ int32_t frameRateInt = static_cast<int32_t>(frameRate);
+ if (minFps > frameRateInt) {
+ minFps = frameRateInt;
+ }
+ if (maxFps < frameRateInt) {
+ maxFps = frameRateInt;
+ }
+ framerates.insert(frameRateInt);
+ }
+
+ for (const auto& format : halFormats) {
+ minFrameDurations.push_back(format);
+ minFrameDurations.push_back(supportedFormat.width);
+ minFrameDurations.push_back(supportedFormat.height);
+ minFrameDurations.push_back(min_frame_duration);
+ }
+
+ // 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 = 1000000000;
+ 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);
+ }
+ }
+
+ // The document in aeAvailableTargetFpsRanges section says the minFps should
+ // not be larger than 15.
+ // We cannot support fixed 30fps but Android requires (min, max) and
+ // (max, max) ranges.
+ // TODO: populate more, right now this does not support 30,30 if the device
+ // has higher than 30 fps modes
+ std::vector<int32_t> fpsRanges;
+ // Variable range
+ fpsRanges.push_back(minFps);
+ fpsRanges.push_back(maxFps);
+ // Fixed ranges
+ for (const auto& framerate : framerates) {
+ fpsRanges.push_back(framerate);
+ fpsRanges.push_back(framerate);
+ }
+ UPDATE(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, fpsRanges.data(),
+ fpsRanges.size());
+
+ UPDATE(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+ streamConfigurations.data(), streamConfigurations.size());
+
+ UPDATE(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
+ minFrameDurations.data(), minFrameDurations.size());
+
+ UPDATE(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS, stallDurations.data(),
+ stallDurations.size());
+
+ UPDATE(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION, &maxFrameDuration, 1);
+
+ 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;
+}
+
+#undef ARRAY_SIZE
+#undef UPDATE
+
+void ExternalCameraDevice::getFrameRateList(
+ int fd, SupportedV4L2Format* format) {
+ format->frameRates.clear();
+
+ v4l2_frmivalenum frameInterval {
+ .pixel_format = format->fourcc,
+ .width = format->width,
+ .height = format->height,
+ .index = 0
+ };
+
+ 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) {
+ float framerate = frameInterval.discrete.denominator /
+ static_cast<float>(frameInterval.discrete.numerator);
+ 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(framerate);
+ }
+ }
+ }
+
+ 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::initSupportedFormatsLocked(int fd) {
+ 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));
+ ALOGD("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)) {
+ auto it = std::find (
+ kSupportedFourCCs.begin(), kSupportedFourCCs.end(), fmtdesc.pixelformat);
+ if (it != kSupportedFourCCs.end()) {
+ // 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) {
+ ALOGD("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;
+ }
+ SupportedV4L2Format format {
+ .width = frameSize.discrete.width,
+ .height = frameSize.discrete.height,
+ .fourcc = fmtdesc.pixelformat
+ };
+ getFrameRateList(fd, &format);
+ if (!format.frameRates.empty()) {
+ mSupportedFormats.push_back(format);
+ }
+ }
+ }
+ }
+ }
+ fmtdesc.index++;
+ }
+}
+
+} // namespace implementation
+} // namespace V3_4
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
+