[libcamera-devel] [RFC 1/1] android: Introduce CameraCapabilties class

paul.elder at ideasonboard.com paul.elder at ideasonboard.com
Mon Jun 21 12:54:45 CEST 2021


Hi Jacopo,

Thank you for the patch.

On Sat, Jun 19, 2021 at 12:51:51PM +0200, Jacopo Mondi wrote:
> The camera_device.cpp has grown a little too much, and it has quickly
> become hard to maintain. Break out the handling of the static
> information collected at camera initialization time to a new
> CameraCapabilities class.
> 
> Break out from the camera_device.cpp file all the functions relative to:
> - Initialization of supported stream configurations
> - Initialization of static metadata
> - Initialization of request templates
> 
> Signed-off-by: Jacopo Mondi <jacopo at jmondi.org>

I like how the CameraDevice uses the CameraCapabilities, it makes
CameraDevice a lot cleaner. I was expecting a bit more to reduce
duplication in the functions that CameraCapabilities has, but that can
be done on top. I think this is a good start, and we can build on top of
this.

I'm not going to confirm the copy-and-paste :D

Acked-by: Paul Elder <paul.elder at ideasonboard.com>

It also doesn't regress CTS.

Tested-by: Paul Elder <paul.elder at ideasonboard.com>

> ---
>  src/android/camera_capabilities.cpp | 1165 +++++++++++++++++++++++++++
>  src/android/camera_capabilities.h   |   64 ++
>  src/android/camera_device.cpp       | 1147 +-------------------------
>  src/android/camera_device.h         |   27 +-
>  src/android/meson.build             |    1 +
>  5 files changed, 1245 insertions(+), 1159 deletions(-)
>  create mode 100644 src/android/camera_capabilities.cpp
>  create mode 100644 src/android/camera_capabilities.h
> 
> diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp
> new file mode 100644
> index 000000000000..20df9a6f1abb
> --- /dev/null
> +++ b/src/android/camera_capabilities.cpp
> @@ -0,0 +1,1165 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2021, Google Inc.
> + *
> + * camera_capabilities.cpp - Camera static properties manager
> + */
> +
> +#include "camera_capabilities.h"
> +
> +#include <array>
> +#include <cmath>
> +
> +#include <hardware/camera3.h>
> +
> +#include <libcamera/control_ids.h>
> +#include <libcamera/controls.h>
> +#include <libcamera/formats.h>
> +#include <libcamera/property_ids.h>
> +
> +#include "libcamera/internal/formats.h"
> +#include "libcamera/internal/log.h"
> +
> +using namespace libcamera;
> +
> +LOG_DECLARE_CATEGORY(HAL)
> +
> +namespace {
> +
> +/*
> + * \var camera3Resolutions
> + * \brief The list of image resolutions defined as mandatory to be supported by
> + * the Android Camera3 specification
> + */
> +const std::vector<Size> camera3Resolutions = {
> +	{ 320, 240 },
> +	{ 640, 480 },
> +	{ 1280, 720 },
> +	{ 1920, 1080 }
> +};
> +
> +/*
> + * \struct Camera3Format
> + * \brief Data associated with an Android format identifier
> + * \var libcameraFormats List of libcamera pixel formats compatible with the
> + * Android format
> + * \var name The human-readable representation of the Android format code
> + */
> +struct Camera3Format {
> +	std::vector<PixelFormat> libcameraFormats;
> +	bool mandatory;
> +	const char *name;
> +};
> +
> +/*
> + * \var camera3FormatsMap
> + * \brief Associate Android format code with ancillary data
> + */
> +const std::map<int, const Camera3Format> camera3FormatsMap = {
> +	{
> +		HAL_PIXEL_FORMAT_BLOB, {
> +			{ formats::MJPEG },
> +			true,
> +			"BLOB"
> +		}
> +	}, {
> +		HAL_PIXEL_FORMAT_YCbCr_420_888, {
> +			{ formats::NV12, formats::NV21 },
> +			true,
> +			"YCbCr_420_888"
> +		}
> +	}, {
> +		/*
> +		 * \todo Translate IMPLEMENTATION_DEFINED inspecting the gralloc
> +		 * usage flag. For now, copy the YCbCr_420 configuration.
> +		 */
> +		HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, {
> +			{ formats::NV12, formats::NV21 },
> +			true,
> +			"IMPLEMENTATION_DEFINED"
> +		}
> +	}, {
> +		HAL_PIXEL_FORMAT_RAW10, {
> +			{
> +				formats::SBGGR10_CSI2P,
> +				formats::SGBRG10_CSI2P,
> +				formats::SGRBG10_CSI2P,
> +				formats::SRGGB10_CSI2P
> +			},
> +			false,
> +			"RAW10"
> +		}
> +	}, {
> +		HAL_PIXEL_FORMAT_RAW12, {
> +			{
> +				formats::SBGGR12_CSI2P,
> +				formats::SGBRG12_CSI2P,
> +				formats::SGRBG12_CSI2P,
> +				formats::SRGGB12_CSI2P
> +			},
> +			false,
> +			"RAW12"
> +		}
> +	}, {
> +		HAL_PIXEL_FORMAT_RAW16, {
> +			{
> +				formats::SBGGR16,
> +				formats::SGBRG16,
> +				formats::SGRBG16,
> +				formats::SRGGB16
> +			},
> +			false,
> +			"RAW16"
> +		}
> +	},
> +};
> +
> +} /* namespace */
> +
> +int CameraCapabilities::initialize(std::shared_ptr<libcamera::Camera> camera,
> +				   int orientation, int facing)
> +{
> +	camera_ = camera;
> +	orientation_ = orientation;
> +	facing_ = facing;
> +
> +	/* Acquire the camera and initialize available stream configurations. */
> +	int ret = camera_->acquire();
> +	if (ret) {
> +		LOG(HAL, Error) << "Failed to temporarily acquire the camera";
> +		return ret;
> +	}
> +
> +	ret = initializeStreamConfigurations();
> +	camera_->release();
> +	if (ret)
> +		return ret;
> +
> +	return initializeStaticMetadata();
> +}
> +
> +std::vector<Size> CameraCapabilities::getYUVResolutions(CameraConfiguration *cameraConfig,
> +							const PixelFormat &pixelFormat,
> +							const std::vector<Size> &resolutions)
> +{
> +	std::vector<Size> supportedResolutions;
> +
> +	StreamConfiguration &cfg = cameraConfig->at(0);
> +	for (const Size &res : resolutions) {
> +		cfg.pixelFormat = pixelFormat;
> +		cfg.size = res;
> +
> +		CameraConfiguration::Status status = cameraConfig->validate();
> +		if (status != CameraConfiguration::Valid) {
> +			LOG(HAL, Debug) << cfg.toString() << " not supported";
> +			continue;
> +		}
> +
> +		LOG(HAL, Debug) << cfg.toString() << " supported";
> +
> +		supportedResolutions.push_back(res);
> +	}
> +
> +	return supportedResolutions;
> +}
> +
> +std::vector<Size> CameraCapabilities::getRawResolutions(const libcamera::PixelFormat &pixelFormat)
> +{
> +	std::unique_ptr<CameraConfiguration> cameraConfig =
> +		camera_->generateConfiguration({ StreamRole::Raw });
> +	StreamConfiguration &cfg = cameraConfig->at(0);
> +	const StreamFormats &formats = cfg.formats();
> +	std::vector<Size> supportedResolutions = formats.sizes(pixelFormat);
> +
> +	return supportedResolutions;
> +}
> +
> +/*
> + * Initialize the format conversion map to translate from Android format
> + * identifier to libcamera pixel formats and fill in the list of supported
> + * stream configurations to be reported to the Android camera framework through
> + * the Camera static metadata.
> + */
> +int CameraCapabilities::initializeStreamConfigurations()
> +{
> +	/*
> +	 * Get the maximum output resolutions
> +	 * \todo Get this from the camera properties once defined
> +	 */
> +	std::unique_ptr<CameraConfiguration> cameraConfig =
> +		camera_->generateConfiguration({ StillCapture });
> +	if (!cameraConfig) {
> +		LOG(HAL, Error) << "Failed to get maximum resolution";
> +		return -EINVAL;
> +	}
> +	StreamConfiguration &cfg = cameraConfig->at(0);
> +
> +	/*
> +	 * \todo JPEG - Adjust the maximum available resolution by taking the
> +	 * JPEG encoder requirements into account (alignment and aspect ratio).
> +	 */
> +	const Size maxRes = cfg.size;
> +	LOG(HAL, Debug) << "Maximum supported resolution: " << maxRes.toString();
> +
> +	/*
> +	 * Build the list of supported image resolutions.
> +	 *
> +	 * The resolutions listed in camera3Resolution are mandatory to be
> +	 * supported, up to the camera maximum resolution.
> +	 *
> +	 * Augment the list by adding resolutions calculated from the camera
> +	 * maximum one.
> +	 */
> +	std::vector<Size> cameraResolutions;
> +	std::copy_if(camera3Resolutions.begin(), camera3Resolutions.end(),
> +		     std::back_inserter(cameraResolutions),
> +		     [&](const Size &res) { return res < maxRes; });
> +
> +	/*
> +	 * The Camera3 specification suggests adding 1/2 and 1/4 of the maximum
> +	 * resolution.
> +	 */
> +	for (unsigned int divider = 2;; divider <<= 1) {
> +		Size derivedSize{
> +			maxRes.width / divider,
> +			maxRes.height / divider,
> +		};
> +
> +		if (derivedSize.width < 320 ||
> +		    derivedSize.height < 240)
> +			break;
> +
> +		cameraResolutions.push_back(derivedSize);
> +	}
> +	cameraResolutions.push_back(maxRes);
> +
> +	/* Remove duplicated entries from the list of supported resolutions. */
> +	std::sort(cameraResolutions.begin(), cameraResolutions.end());
> +	auto last = std::unique(cameraResolutions.begin(), cameraResolutions.end());
> +	cameraResolutions.erase(last, cameraResolutions.end());
> +
> +	/*
> +	 * Build the list of supported camera formats.
> +	 *
> +	 * To each Android format a list of compatible libcamera formats is
> +	 * associated. The first libcamera format that tests successful is added
> +	 * to the format translation map used when configuring the streams.
> +	 * It is then tested against the list of supported camera resolutions to
> +	 * build the stream configuration map reported through the camera static
> +	 * metadata.
> +	 */
> +	Size maxJpegSize;
> +	for (const auto &format : camera3FormatsMap) {
> +		int androidFormat = format.first;
> +		const Camera3Format &camera3Format = format.second;
> +		const std::vector<PixelFormat> &libcameraFormats =
> +			camera3Format.libcameraFormats;
> +
> +		LOG(HAL, Debug) << "Trying to map Android format "
> +				<< camera3Format.name;
> +
> +		/*
> +		 * JPEG is always supported, either produced directly by the
> +		 * camera, or encoded in the HAL.
> +		 */
> +		if (androidFormat == HAL_PIXEL_FORMAT_BLOB) {
> +			formatsMap_[androidFormat] = formats::MJPEG;
> +			LOG(HAL, Debug) << "Mapped Android format "
> +					<< camera3Format.name << " to "
> +					<< formats::MJPEG.toString()
> +					<< " (fixed mapping)";
> +			continue;
> +		}
> +
> +		/*
> +		 * Test the libcamera formats that can produce images
> +		 * compatible with the format defined by Android.
> +		 */
> +		PixelFormat mappedFormat;
> +		for (const PixelFormat &pixelFormat : libcameraFormats) {
> +
> +			LOG(HAL, Debug) << "Testing " << pixelFormat.toString();
> +
> +			/*
> +			 * The stream configuration size can be adjusted,
> +			 * not the pixel format.
> +			 *
> +			 * \todo This could be simplified once all pipeline
> +			 * handlers will report the StreamFormats list of
> +			 * supported formats.
> +			 */
> +			cfg.pixelFormat = pixelFormat;
> +
> +			CameraConfiguration::Status status = cameraConfig->validate();
> +			if (status != CameraConfiguration::Invalid &&
> +			    cfg.pixelFormat == pixelFormat) {
> +				mappedFormat = pixelFormat;
> +				break;
> +			}
> +		}
> +
> +		if (!mappedFormat.isValid()) {
> +			/* If the format is not mandatory, skip it. */
> +			if (!camera3Format.mandatory)
> +				continue;
> +
> +			LOG(HAL, Error)
> +				<< "Failed to map mandatory Android format "
> +				<< camera3Format.name << " ("
> +				<< utils::hex(androidFormat) << "): aborting";
> +			return -EINVAL;
> +		}
> +
> +		/*
> +		 * Record the mapping and then proceed to generate the
> +		 * stream configurations map, by testing the image resolutions.
> +		 */
> +		formatsMap_[androidFormat] = mappedFormat;
> +		LOG(HAL, Debug) << "Mapped Android format "
> +				<< camera3Format.name << " to "
> +				<< mappedFormat.toString();
> +
> +		std::vector<Size> resolutions;
> +		const PixelFormatInfo &info = PixelFormatInfo::info(mappedFormat);
> +		if (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW)
> +			resolutions = getRawResolutions(mappedFormat);
> +		else
> +			resolutions = getYUVResolutions(cameraConfig.get(),
> +							mappedFormat,
> +							cameraResolutions);
> +
> +		for (const Size &res : resolutions) {
> +			streamConfigurations_.push_back({ res, androidFormat });
> +
> +			/*
> +			 * If the format is HAL_PIXEL_FORMAT_YCbCr_420_888
> +			 * from which JPEG is produced, add an entry for
> +			 * the JPEG stream.
> +			 *
> +			 * \todo Wire the JPEG encoder to query the supported
> +			 * sizes provided a list of formats it can encode.
> +			 *
> +			 * \todo Support JPEG streams produced by the Camera
> +			 * natively.
> +			 */
> +			if (androidFormat == HAL_PIXEL_FORMAT_YCbCr_420_888) {
> +				streamConfigurations_.push_back(
> +					{ res, HAL_PIXEL_FORMAT_BLOB });
> +				maxJpegSize = std::max(maxJpegSize, res);
> +			}
> +		}
> +
> +		/*
> +		 * \todo Calculate the maximum JPEG buffer size by asking the
> +		 * encoder giving the maximum frame size required.
> +		 */
> +		maxJpegBufferSize_ = maxJpegSize.width * maxJpegSize.height * 1.5;
> +	}
> +
> +	LOG(HAL, Debug) << "Collected stream configuration map: ";
> +	for (const auto &entry : streamConfigurations_)
> +		LOG(HAL, Debug) << "{ " << entry.resolution.toString() << " - "
> +				<< utils::hex(entry.androidFormat) << " }";
> +
> +	return 0;
> +}
> +
> +int CameraCapabilities::initializeStaticMetadata()
> +{
> +	staticMetadata_ = std::make_unique<CameraMetadata>(64, 1024);
> +	if (!staticMetadata_->isValid()) {
> +		LOG(HAL, Error) << "Failed to allocate static metadata";
> +		staticMetadata_.reset();
> +		return -EINVAL;
> +	}
> +
> +	const ControlInfoMap &controlsInfo = camera_->controls();
> +	const ControlList &properties = camera_->properties();
> +
> +	/* Color correction static metadata. */
> +	{
> +		std::vector<uint8_t> data;
> +		data.reserve(3);
> +		const auto &infoMap = controlsInfo.find(&controls::draft::ColorCorrectionAberrationMode);
> +		if (infoMap != controlsInfo.end()) {
> +			for (const auto &value : infoMap->second.values())
> +				data.push_back(value.get<int32_t>());
> +		} else {
> +			data.push_back(ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF);
> +		}
> +		staticMetadata_->addEntry(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> +					  data);
> +	}
> +
> +	/* Control static metadata. */
> +	std::vector<uint8_t> aeAvailableAntiBandingModes = {
> +		ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,
> +		ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ,
> +		ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ,
> +		ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> +				  aeAvailableAntiBandingModes);
> +
> +	std::vector<uint8_t> aeAvailableModes = {
> +		ANDROID_CONTROL_AE_MODE_ON,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,
> +				  aeAvailableModes);
> +
> +	int64_t minFrameDurationNsec = -1;
> +	int64_t maxFrameDurationNsec = -1;
> +	const auto frameDurationsInfo = controlsInfo.find(&controls::FrameDurationLimits);
> +	if (frameDurationsInfo != controlsInfo.end()) {
> +		minFrameDurationNsec = frameDurationsInfo->second.min().get<int64_t>() * 1000;
> +		maxFrameDurationNsec = frameDurationsInfo->second.max().get<int64_t>() * 1000;
> +
> +		/*
> +		 * Adjust the minimum frame duration to comply with Android
> +		 * requirements. The camera service mandates all preview/record
> +		 * streams to have a minimum frame duration < 33,366 milliseconds
> +		 * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera service
> +		 * implementation).
> +		 *
> +		 * If we're close enough (+ 500 useconds) to that value, round
> +		 * the minimum frame duration of the camera to an accepted
> +		 * value.
> +		 */
> +		static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS = 1e9 / 29.97;
> +		if (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS &&
> +		    minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS + 500000)
> +			minFrameDurationNsec = MAX_PREVIEW_RECORD_DURATION_NS - 1000;
> +
> +		/*
> +		 * The AE routine frame rate limits are computed using the frame
> +		 * duration limits, as libcamera clips the AE routine to the
> +		 * frame durations.
> +		 */
> +		int32_t maxFps = std::round(1e9 / minFrameDurationNsec);
> +		int32_t minFps = std::round(1e9 / maxFrameDurationNsec);
> +		minFps = std::max(1, minFps);
> +
> +		/*
> +		 * Force rounding errors so that we have the proper frame
> +		 * durations for when we reuse these variables later
> +		 */
> +		minFrameDurationNsec = 1e9 / maxFps;
> +		maxFrameDurationNsec = 1e9 / minFps;
> +
> +		/*
> +		 * Register to the camera service {min, max} and {max, max}
> +		 * intervals as requested by the metadata documentation.
> +		 */
> +		int32_t availableAeFpsTarget[] = {
> +			minFps, maxFps, maxFps, maxFps
> +		};
> +		staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> +					  availableAeFpsTarget);
> +	}
> +
> +	std::vector<int32_t> aeCompensationRange = {
> +		0, 0,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> +				  aeCompensationRange);
> +
> +	const camera_metadata_rational_t aeCompensationStep[] = {
> +		{ 0, 1 }
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_STEP,
> +				  aeCompensationStep);
> +
> +	std::vector<uint8_t> availableAfModes = {
> +		ANDROID_CONTROL_AF_MODE_OFF,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AF_AVAILABLE_MODES,
> +				  availableAfModes);
> +
> +	std::vector<uint8_t> availableEffects = {
> +		ANDROID_CONTROL_EFFECT_MODE_OFF,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_EFFECTS,
> +				  availableEffects);
> +
> +	std::vector<uint8_t> availableSceneModes = {
> +		ANDROID_CONTROL_SCENE_MODE_DISABLED,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> +				  availableSceneModes);
> +
> +	std::vector<uint8_t> availableStabilizationModes = {
> +		ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> +				  availableStabilizationModes);
> +
> +	/*
> +	 * \todo Inspect the Camera capabilities to report the available
> +	 * AWB modes. Default to AUTO as CTS tests require it.
> +	 */
> +	std::vector<uint8_t> availableAwbModes = {
> +		ANDROID_CONTROL_AWB_MODE_AUTO,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> +				  availableAwbModes);
> +
> +	std::vector<int32_t> availableMaxRegions = {
> +		0, 0, 0,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_MAX_REGIONS,
> +				  availableMaxRegions);
> +
> +	std::vector<uint8_t> sceneModesOverride = {
> +		ANDROID_CONTROL_AE_MODE_ON,
> +		ANDROID_CONTROL_AWB_MODE_AUTO,
> +		ANDROID_CONTROL_AF_MODE_OFF,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> +				  sceneModesOverride);
> +
> +	uint8_t aeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> +				  aeLockAvailable);
> +
> +	uint8_t awbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> +				  awbLockAvailable);
> +
> +	char availableControlModes = ANDROID_CONTROL_MODE_AUTO;
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_MODES,
> +				  availableControlModes);
> +
> +	/* JPEG static metadata. */
> +
> +	/*
> +	 * Create the list of supported thumbnail sizes by inspecting the
> +	 * available JPEG resolutions collected in streamConfigurations_ and
> +	 * generate one entry for each aspect ratio.
> +	 *
> +	 * The JPEG thumbnailer can freely scale, so pick an arbitrary
> +	 * (160, 160) size as the bounding rectangle, which is then cropped to
> +	 * the different supported aspect ratios.
> +	 */
> +	constexpr Size maxJpegThumbnail(160, 160);
> +	std::vector<Size> thumbnailSizes;
> +	thumbnailSizes.push_back({ 0, 0 });
> +	for (const auto &entry : streamConfigurations_) {
> +		if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)
> +			continue;
> +
> +		Size thumbnailSize = maxJpegThumbnail
> +				     .boundedToAspectRatio({ entry.resolution.width,
> +							     entry.resolution.height });
> +		thumbnailSizes.push_back(thumbnailSize);
> +	}
> +
> +	std::sort(thumbnailSizes.begin(), thumbnailSizes.end());
> +	auto last = std::unique(thumbnailSizes.begin(), thumbnailSizes.end());
> +	thumbnailSizes.erase(last, thumbnailSizes.end());
> +
> +	/* Transform sizes in to a list of integers that can be consumed. */
> +	std::vector<int32_t> thumbnailEntries;
> +	thumbnailEntries.reserve(thumbnailSizes.size() * 2);
> +	for (const auto &size : thumbnailSizes) {
> +		thumbnailEntries.push_back(size.width);
> +		thumbnailEntries.push_back(size.height);
> +	}
> +	staticMetadata_->addEntry(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> +				  thumbnailEntries);
> +
> +	staticMetadata_->addEntry(ANDROID_JPEG_MAX_SIZE, maxJpegBufferSize_);
> +
> +	/* Sensor static metadata. */
> +	std::array<int32_t, 2> pixelArraySize;
> +	{
> +		const Size &size = properties.get(properties::PixelArraySize);
> +		pixelArraySize[0] = size.width;
> +		pixelArraySize[1] = size.height;
> +		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> +					  pixelArraySize);
> +	}
> +
> +	if (properties.contains(properties::UnitCellSize)) {
> +		const Size &cellSize = properties.get<Size>(properties::UnitCellSize);
> +		std::array<float, 2> physicalSize{
> +			cellSize.width * pixelArraySize[0] / 1e6f,
> +			cellSize.height * pixelArraySize[1] / 1e6f
> +		};
> +		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> +					  physicalSize);
> +	}
> +
> +	{
> +		const Span<const Rectangle> &rects =
> +			properties.get(properties::PixelArrayActiveAreas);
> +		std::vector<int32_t> data{
> +			static_cast<int32_t>(rects[0].x),
> +			static_cast<int32_t>(rects[0].y),
> +			static_cast<int32_t>(rects[0].width),
> +			static_cast<int32_t>(rects[0].height),
> +		};
> +		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> +					  data);
> +	}
> +
> +	int32_t sensitivityRange[] = {
> +		32, 2400,
> +	};
> +	staticMetadata_->addEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> +				  sensitivityRange);
> +
> +	/* Report the color filter arrangement if the camera reports it. */
> +	if (properties.contains(properties::draft::ColorFilterArrangement)) {
> +		uint8_t filterArr = properties.get(properties::draft::ColorFilterArrangement);
> +		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> +					  filterArr);
> +	}
> +
> +	const auto &exposureInfo = controlsInfo.find(&controls::ExposureTime);
> +	if (exposureInfo != controlsInfo.end()) {
> +		int64_t exposureTimeRange[2] = {
> +			exposureInfo->second.min().get<int32_t>() * 1000LL,
> +			exposureInfo->second.max().get<int32_t>() * 1000LL,
> +		};
> +		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> +					  exposureTimeRange, 2);
> +	}
> +
> +	staticMetadata_->addEntry(ANDROID_SENSOR_ORIENTATION, orientation_);
> +
> +	std::vector<int32_t> testPatternModes = {
> +		ANDROID_SENSOR_TEST_PATTERN_MODE_OFF
> +	};
> +	const auto &testPatternsInfo =
> +		controlsInfo.find(&controls::draft::TestPatternMode);
> +	if (testPatternsInfo != controlsInfo.end()) {
> +		const auto &values = testPatternsInfo->second.values();
> +		ASSERT(!values.empty());
> +		for (const auto &value : values) {
> +			switch (value.get<int32_t>()) {
> +			case controls::draft::TestPatternModeOff:
> +				/*
> +				 * ANDROID_SENSOR_TEST_PATTERN_MODE_OFF is
> +				 * already in testPatternModes.
> +				 */
> +				break;
> +
> +			case controls::draft::TestPatternModeSolidColor:
> +				testPatternModes.push_back(
> +					ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR);
> +				break;
> +
> +			case controls::draft::TestPatternModeColorBars:
> +				testPatternModes.push_back(
> +					ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS);
> +				break;
> +
> +			case controls::draft::TestPatternModeColorBarsFadeToGray:
> +				testPatternModes.push_back(
> +					ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS_FADE_TO_GRAY);
> +				break;
> +
> +			case controls::draft::TestPatternModePn9:
> +				testPatternModes.push_back(
> +					ANDROID_SENSOR_TEST_PATTERN_MODE_PN9);
> +				break;
> +
> +			case controls::draft::TestPatternModeCustom1:
> +				/* We don't support this yet. */
> +				break;
> +
> +			default:
> +				LOG(HAL, Error) << "Unknown test pattern mode: "
> +						<< value.get<int32_t>();
> +				continue;
> +			}
> +		}
> +	}
> +	staticMetadata_->addEntry(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> +				  testPatternModes);
> +
> +	uint8_t timestampSource = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
> +	staticMetadata_->addEntry(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> +				  timestampSource);
> +
> +	if (maxFrameDurationNsec > 0)
> +		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> +					  maxFrameDurationNsec);
> +
> +	/* Statistics static metadata. */
> +	uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> +	staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> +				  faceDetectMode);
> +
> +	int32_t maxFaceCount = 0;
> +	staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> +				  maxFaceCount);
> +
> +	{
> +		std::vector<uint8_t> data;
> +		data.reserve(2);
> +		const auto &infoMap = controlsInfo.find(&controls::draft::LensShadingMapMode);
> +		if (infoMap != controlsInfo.end()) {
> +			for (const auto &value : infoMap->second.values())
> +				data.push_back(value.get<int32_t>());
> +		} else {
> +			data.push_back(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF);
> +		}
> +		staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
> +					  data);
> +	}
> +
> +	/* Sync static metadata. */
> +	int32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
> +	staticMetadata_->addEntry(ANDROID_SYNC_MAX_LATENCY, maxLatency);
> +
> +	/* Flash static metadata. */
> +	char flashAvailable = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
> +	staticMetadata_->addEntry(ANDROID_FLASH_INFO_AVAILABLE,
> +				  flashAvailable);
> +
> +	/* Lens static metadata. */
> +	std::vector<float> lensApertures = {
> +		2.53 / 100,
> +	};
> +	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> +				  lensApertures);
> +
> +	uint8_t lensFacing;
> +	switch (facing_) {
> +	default:
> +	case CAMERA_FACING_FRONT:
> +		lensFacing = ANDROID_LENS_FACING_FRONT;
> +		break;
> +	case CAMERA_FACING_BACK:
> +		lensFacing = ANDROID_LENS_FACING_BACK;
> +		break;
> +	case CAMERA_FACING_EXTERNAL:
> +		lensFacing = ANDROID_LENS_FACING_EXTERNAL;
> +		break;
> +	}
> +	staticMetadata_->addEntry(ANDROID_LENS_FACING, lensFacing);
> +
> +	std::vector<float> lensFocalLengths = {
> +		1,
> +	};
> +	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> +				  lensFocalLengths);
> +
> +	std::vector<uint8_t> opticalStabilizations = {
> +		ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF,
> +	};
> +	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> +				  opticalStabilizations);
> +
> +	float hypeFocalDistance = 0;
> +	staticMetadata_->addEntry(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> +				  hypeFocalDistance);
> +
> +	float minFocusDistance = 0;
> +	staticMetadata_->addEntry(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> +				  minFocusDistance);
> +
> +	/* Noise reduction modes. */
> +	{
> +		std::vector<uint8_t> data;
> +		data.reserve(5);
> +		const auto &infoMap = controlsInfo.find(&controls::draft::NoiseReductionMode);
> +		if (infoMap != controlsInfo.end()) {
> +			for (const auto &value : infoMap->second.values())
> +				data.push_back(value.get<int32_t>());
> +		} else {
> +			data.push_back(ANDROID_NOISE_REDUCTION_MODE_OFF);
> +		}
> +		staticMetadata_->addEntry(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> +					  data);
> +	}
> +
> +	/* Scaler static metadata. */
> +
> +	/*
> +	 * \todo The digital zoom factor is a property that depends on the
> +	 * desired output configuration and the sensor frame size input to the
> +	 * ISP. This information is not available to the Android HAL, not at
> +	 * initialization time at least.
> +	 *
> +	 * As a workaround rely on pipeline handlers initializing the
> +	 * ScalerCrop control with the camera default configuration and use the
> +	 * maximum and minimum crop rectangles to calculate the digital zoom
> +	 * factor.
> +	 */
> +	float maxZoom = 1.0f;
> +	const auto scalerCrop = controlsInfo.find(&controls::ScalerCrop);
> +	if (scalerCrop != controlsInfo.end()) {
> +		Rectangle min = scalerCrop->second.min().get<Rectangle>();
> +		Rectangle max = scalerCrop->second.max().get<Rectangle>();
> +		maxZoom = std::min(1.0f * max.width / min.width,
> +				   1.0f * max.height / min.height);
> +	}
> +	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> +				  maxZoom);
> +
> +	std::vector<uint32_t> availableStreamConfigurations;
> +	availableStreamConfigurations.reserve(streamConfigurations_.size() * 4);
> +	for (const auto &entry : streamConfigurations_) {
> +		availableStreamConfigurations.push_back(entry.androidFormat);
> +		availableStreamConfigurations.push_back(entry.resolution.width);
> +		availableStreamConfigurations.push_back(entry.resolution.height);
> +		availableStreamConfigurations.push_back(
> +			ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
> +	}
> +	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> +				  availableStreamConfigurations);
> +
> +	std::vector<int64_t> availableStallDurations = {
> +		ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920, 33333333,
> +	};
> +	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> +				  availableStallDurations);
> +
> +	/* Use the minimum frame duration for all the YUV/RGB formats. */
> +	if (minFrameDurationNsec > 0) {
> +		std::vector<int64_t> minFrameDurations;
> +		minFrameDurations.reserve(streamConfigurations_.size() * 4);
> +		for (const auto &entry : streamConfigurations_) {
> +			minFrameDurations.push_back(entry.androidFormat);
> +			minFrameDurations.push_back(entry.resolution.width);
> +			minFrameDurations.push_back(entry.resolution.height);
> +			minFrameDurations.push_back(minFrameDurationNsec);
> +		}
> +		staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> +					  minFrameDurations);
> +	}
> +
> +	uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
> +	staticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE, croppingType);
> +
> +	/* Info static metadata. */
> +	uint8_t supportedHWLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
> +	staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> +				  supportedHWLevel);
> +
> +	/* Request static metadata. */
> +	int32_t partialResultCount = 1;
> +	staticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> +				  partialResultCount);
> +
> +	{
> +		/* Default the value to 2 if not reported by the camera. */
> +		uint8_t maxPipelineDepth = 2;
> +		const auto &infoMap = controlsInfo.find(&controls::draft::PipelineDepth);
> +		if (infoMap != controlsInfo.end())
> +			maxPipelineDepth = infoMap->second.max().get<int32_t>();
> +		staticMetadata_->addEntry(ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> +					  maxPipelineDepth);
> +	}
> +
> +	/* LIMITED does not support reprocessing. */
> +	uint32_t maxNumInputStreams = 0;
> +	staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> +				  maxNumInputStreams);
> +
> +	std::vector<uint8_t> availableCapabilities = {
> +		ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
> +	};
> +
> +	/* Report if camera supports RAW. */
> +	bool rawStreamAvailable = false;
> +	std::unique_ptr<CameraConfiguration> cameraConfig =
> +		camera_->generateConfiguration({ StreamRole::Raw });
> +	if (cameraConfig && !cameraConfig->empty()) {
> +		const PixelFormatInfo &info =
> +			PixelFormatInfo::info(cameraConfig->at(0).pixelFormat);
> +		/* Only advertise RAW support if RAW16 is possible. */
> +		if (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW &&
> +		    info.bitsPerPixel == 16) {
> +			rawStreamAvailable = true;
> +			availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
> +		}
> +	}
> +
> +	/* Number of { RAW, YUV, JPEG } supported output streams */
> +	int32_t numOutStreams[] = { rawStreamAvailable, 2, 1 };
> +	staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> +				  numOutStreams);
> +
> +	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> +				  availableCapabilities);
> +
> +	std::vector<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_CONTROL_SCENE_MODE_OVERRIDES,
> +		ANDROID_FLASH_INFO_AVAILABLE,
> +		ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> +		ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> +		ANDROID_JPEG_MAX_SIZE,
> +		ANDROID_LENS_FACING,
> +		ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> +		ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> +		ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> +		ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> +		ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> +		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_MIN_FRAME_DURATIONS,
> +		ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> +		ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> +		ANDROID_SCALER_CROPPING_TYPE,
> +		ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> +		ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> +		ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> +		ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> +		ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> +		ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> +		ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> +		ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> +		ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> +		ANDROID_SENSOR_ORIENTATION,
> +		ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> +		ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> +		ANDROID_SYNC_MAX_LATENCY,
> +	};
> +	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
> +				  availableCharacteristicsKeys);
> +
> +	std::vector<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_APERTURE,
> +		ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> +		ANDROID_NOISE_REDUCTION_MODE,
> +		ANDROID_SCALER_CROP_REGION,
> +		ANDROID_STATISTICS_FACE_DETECT_MODE
> +	};
> +	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
> +				  availableRequestKeys);
> +
> +	std::vector<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_GPS_COORDINATES,
> +		ANDROID_JPEG_GPS_PROCESSING_METHOD,
> +		ANDROID_JPEG_GPS_TIMESTAMP,
> +		ANDROID_JPEG_ORIENTATION,
> +		ANDROID_JPEG_QUALITY,
> +		ANDROID_JPEG_SIZE,
> +		ANDROID_JPEG_THUMBNAIL_QUALITY,
> +		ANDROID_JPEG_THUMBNAIL_SIZE,
> +		ANDROID_LENS_APERTURE,
> +		ANDROID_LENS_FOCAL_LENGTH,
> +		ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> +		ANDROID_LENS_STATE,
> +		ANDROID_NOISE_REDUCTION_MODE,
> +		ANDROID_REQUEST_PIPELINE_DEPTH,
> +		ANDROID_SCALER_CROP_REGION,
> +		ANDROID_SENSOR_EXPOSURE_TIME,
> +		ANDROID_SENSOR_FRAME_DURATION,
> +		ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,
> +		ANDROID_SENSOR_TEST_PATTERN_MODE,
> +		ANDROID_SENSOR_TIMESTAMP,
> +		ANDROID_STATISTICS_FACE_DETECT_MODE,
> +		ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
> +		ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
> +		ANDROID_STATISTICS_SCENE_FLICKER,
> +	};
> +	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
> +				  availableResultKeys);
> +
> +	if (!staticMetadata_->isValid()) {
> +		LOG(HAL, Error) << "Failed to construct static metadata";
> +		staticMetadata_.reset();
> +		return -EINVAL;
> +	}
> +
> +	if (staticMetadata_->resized()) {
> +		auto [entryCount, dataCount] = staticMetadata_->usage();
> +		LOG(HAL, Info)
> +			<< "Static metadata resized: " << entryCount
> +			<< " entries and " << dataCount << " bytes used";
> +	}
> +
> +	return 0;
> +}
> +
> +/* Translate Android format code to libcamera pixel format. */
> +PixelFormat CameraCapabilities::toPixelFormat(int format) const
> +{
> +	auto it = formatsMap_.find(format);
> +	if (it == formatsMap_.end()) {
> +		LOG(HAL, Error) << "Requested format " << utils::hex(format)
> +				<< " not supported";
> +		return PixelFormat();
> +	}
> +
> +	return it->second;
> +}
> +
> +std::unique_ptr<CameraMetadata> CameraCapabilities::requestTemplatePreview() const
> +{
> +	/*
> +	 * \todo Keep this in sync with the actual number of entries.
> +	 * Currently: 20 entries, 35 bytes
> +	 */
> +	auto requestTemplate = std::make_unique<CameraMetadata>(21, 36);
> +	if (!requestTemplate->isValid()) {
> +		return nullptr;
> +	}
> +
> +	/* Get the FPS range registered in the static metadata. */
> +	camera_metadata_ro_entry_t entry;
> +	bool found = staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> +					       &entry);
> +	if (!found) {
> +		LOG(HAL, Error) << "Cannot create capture template without FPS range";
> +		return nullptr;
> +	}
> +
> +	/*
> +	 * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> +	 * has been assembled as {{min, max} {max, max}}.
> +	 */
> +	requestTemplate->addEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> +				  entry.data.i32, 2);
> +
> +	uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AE_MODE, aeMode);
> +
> +	int32_t aeExposureCompensation = 0;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> +				  aeExposureCompensation);
> +
> +	uint8_t aePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> +				  aePrecaptureTrigger);
> +
> +	uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AE_LOCK, aeLock);
> +
> +	uint8_t aeAntibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> +				  aeAntibandingMode);
> +
> +	uint8_t afMode = ANDROID_CONTROL_AF_MODE_OFF;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AF_MODE, afMode);
> +
> +	uint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AF_TRIGGER, afTrigger);
> +
> +	uint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AWB_MODE, awbMode);
> +
> +	uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
> +	requestTemplate->addEntry(ANDROID_CONTROL_AWB_LOCK, awbLock);
> +
> +	uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
> +	requestTemplate->addEntry(ANDROID_FLASH_MODE, flashMode);
> +
> +	uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> +	requestTemplate->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,
> +				  faceDetectMode);
> +
> +	uint8_t noiseReduction = ANDROID_NOISE_REDUCTION_MODE_OFF;
> +	requestTemplate->addEntry(ANDROID_NOISE_REDUCTION_MODE,
> +				  noiseReduction);
> +
> +	uint8_t aberrationMode = ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;
> +	requestTemplate->addEntry(ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> +				  aberrationMode);
> +
> +	uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;
> +	requestTemplate->addEntry(ANDROID_CONTROL_MODE, controlMode);
> +
> +	float lensAperture = 2.53 / 100;
> +	requestTemplate->addEntry(ANDROID_LENS_APERTURE, lensAperture);
> +
> +	uint8_t opticalStabilization = ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
> +	requestTemplate->addEntry(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> +				  opticalStabilization);
> +
> +	uint8_t captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
> +	requestTemplate->addEntry(ANDROID_CONTROL_CAPTURE_INTENT,
> +				  captureIntent);
> +
> +	return requestTemplate;
> +}
> +
> +std::unique_ptr<CameraMetadata> CameraCapabilities::requestTemplateVideo() const
> +{
> +	std::unique_ptr<CameraMetadata> previewTemplate = requestTemplatePreview();
> +	if (!previewTemplate)
> +		return nullptr;
> +
> +	/*
> +	 * The video template requires a fixed FPS range. Everything else
> +	 * stays the same as the preview template.
> +	 */
> +	camera_metadata_ro_entry_t entry;
> +	staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> +				  &entry);
> +
> +	/*
> +	 * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> +	 * has been assembled as {{min, max} {max, max}}.
> +	 */
> +	previewTemplate->updateEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> +				     entry.data.i32 + 2, 2);
> +
> +	return previewTemplate;
> +}
> diff --git a/src/android/camera_capabilities.h b/src/android/camera_capabilities.h
> new file mode 100644
> index 000000000000..3a427e768aff
> --- /dev/null
> +++ b/src/android/camera_capabilities.h
> @@ -0,0 +1,64 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2021, Google Inc.
> + *
> + * camera_capabilities.h - Camera static properties manager
> + */
> +#ifndef __ANDROID_CAMERA_CAPABILITIES_H__
> +#define __ANDROID_CAMERA_CAPABILITIES_H__
> +
> +#include <map>
> +#include <memory>
> +#include <vector>
> +
> +#include <libcamera/camera.h>
> +#include <libcamera/class.h>
> +#include <libcamera/geometry.h>
> +
> +#include "camera_metadata.h"
> +
> +class CameraCapabilities
> +{
> +public:
> +	CameraCapabilities() = default;
> +
> +	int initialize(std::shared_ptr<libcamera::Camera> camera,
> +		       int orientation, int facing);
> +
> +	CameraMetadata *staticMetadata() const { return staticMetadata_.get(); }
> +	libcamera::PixelFormat toPixelFormat(int format) const;
> +	unsigned int maxJpegBufferSize() const { return maxJpegBufferSize_; }
> +
> +	std::unique_ptr<CameraMetadata> requestTemplatePreview() const;
> +	std::unique_ptr<CameraMetadata> requestTemplateVideo() const;
> +
> +private:
> +	LIBCAMERA_DISABLE_COPY_AND_MOVE(CameraCapabilities)
> +
> +	struct Camera3StreamConfiguration {
> +		libcamera::Size resolution;
> +		int androidFormat;
> +	};
> +
> +	std::vector<libcamera::Size>
> +	getYUVResolutions(libcamera::CameraConfiguration *cameraConfig,
> +			  const libcamera::PixelFormat &pixelFormat,
> +			  const std::vector<libcamera::Size> &resolutions);
> +	std::vector<libcamera::Size>
> +	getRawResolutions(const libcamera::PixelFormat &pixelFormat);
> +	int initializeStreamConfigurations();
> +
> +	int initializeStaticMetadata();
> +
> +	std::shared_ptr<libcamera::Camera> camera_;
> +
> +	int facing_;
> +	int orientation_;
> +
> +	std::vector<Camera3StreamConfiguration> streamConfigurations_;
> +	std::map<int, libcamera::PixelFormat> formatsMap_;
> +	std::unique_ptr<CameraMetadata> staticMetadata_;
> +	unsigned int maxJpegBufferSize_;
> +};
> +
> +#endif /* __ANDROID_CAMERA_CAPABILITIES_H__ */
> diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> index 8c71fd0675d3..4bd125d7020a 100644
> --- a/src/android/camera_device.cpp
> +++ b/src/android/camera_device.cpp
> @@ -10,11 +10,8 @@
>  #include "camera_ops.h"
>  #include "post_processor.h"
>  
> -#include <array>
> -#include <cmath>
>  #include <fstream>
>  #include <sys/mman.h>
> -#include <tuple>
>  #include <unistd.h>
>  #include <vector>
>  
> @@ -23,7 +20,6 @@
>  #include <libcamera/formats.h>
>  #include <libcamera/property_ids.h>
>  
> -#include "libcamera/internal/formats.h"
>  #include "libcamera/internal/log.h"
>  #include "libcamera/internal/thread.h"
>  #include "libcamera/internal/utils.h"
> @@ -36,94 +32,6 @@ LOG_DECLARE_CATEGORY(HAL)
>  
>  namespace {
>  
> -/*
> - * \var camera3Resolutions
> - * \brief The list of image resolutions defined as mandatory to be supported by
> - * the Android Camera3 specification
> - */
> -const std::vector<Size> camera3Resolutions = {
> -	{ 320, 240 },
> -	{ 640, 480 },
> -	{ 1280, 720 },
> -	{ 1920, 1080 }
> -};
> -
> -/*
> - * \struct Camera3Format
> - * \brief Data associated with an Android format identifier
> - * \var libcameraFormats List of libcamera pixel formats compatible with the
> - * Android format
> - * \var name The human-readable representation of the Android format code
> - */
> -struct Camera3Format {
> -	std::vector<PixelFormat> libcameraFormats;
> -	bool mandatory;
> -	const char *name;
> -};
> -
> -/*
> - * \var camera3FormatsMap
> - * \brief Associate Android format code with ancillary data
> - */
> -const std::map<int, const Camera3Format> camera3FormatsMap = {
> -	{
> -		HAL_PIXEL_FORMAT_BLOB, {
> -			{ formats::MJPEG },
> -			true,
> -			"BLOB"
> -		}
> -	}, {
> -		HAL_PIXEL_FORMAT_YCbCr_420_888, {
> -			{ formats::NV12, formats::NV21 },
> -			true,
> -			"YCbCr_420_888"
> -		}
> -	}, {
> -		/*
> -		 * \todo Translate IMPLEMENTATION_DEFINED inspecting the gralloc
> -		 * usage flag. For now, copy the YCbCr_420 configuration.
> -		 */
> -		HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, {
> -			{ formats::NV12, formats::NV21 },
> -			true,
> -			"IMPLEMENTATION_DEFINED"
> -		}
> -	}, {
> -		HAL_PIXEL_FORMAT_RAW10, {
> -			{
> -				formats::SBGGR10_CSI2P,
> -				formats::SGBRG10_CSI2P,
> -				formats::SGRBG10_CSI2P,
> -				formats::SRGGB10_CSI2P
> -			},
> -			false,
> -			"RAW10"
> -		}
> -	}, {
> -		HAL_PIXEL_FORMAT_RAW12, {
> -			{
> -				formats::SBGGR12_CSI2P,
> -				formats::SGBRG12_CSI2P,
> -				formats::SGRBG12_CSI2P,
> -				formats::SRGGB12_CSI2P
> -			},
> -			false,
> -			"RAW12"
> -		}
> -	}, {
> -		HAL_PIXEL_FORMAT_RAW16, {
> -			{
> -				formats::SBGGR16,
> -				formats::SGBRG16,
> -				formats::SGRBG16,
> -				formats::SRGGB16
> -			},
> -			false,
> -			"RAW16"
> -		}
> -	},
> -};
> -
>  /*
>   * \struct Camera3StreamConfig
>   * \brief Data to store StreamConfiguration associated with camera3_stream(s)
> @@ -512,242 +420,7 @@ int CameraDevice::initialize(const CameraConfigData *cameraConfigData)
>  		orientation_ = 0;
>  	}
>  
> -	/* Acquire the camera and initialize available stream configurations. */
> -	int ret = camera_->acquire();
> -	if (ret) {
> -		LOG(HAL, Error) << "Failed to temporarily acquire the camera";
> -		return ret;
> -	}
> -
> -	ret = initializeStreamConfigurations();
> -	camera_->release();
> -	return ret;
> -}
> -
> -std::vector<Size> CameraDevice::getYUVResolutions(CameraConfiguration *cameraConfig,
> -						  const PixelFormat &pixelFormat,
> -						  const std::vector<Size> &resolutions)
> -{
> -	std::vector<Size> supportedResolutions;
> -
> -	StreamConfiguration &cfg = cameraConfig->at(0);
> -	for (const Size &res : resolutions) {
> -		cfg.pixelFormat = pixelFormat;
> -		cfg.size = res;
> -
> -		CameraConfiguration::Status status = cameraConfig->validate();
> -		if (status != CameraConfiguration::Valid) {
> -			LOG(HAL, Debug) << cfg.toString() << " not supported";
> -			continue;
> -		}
> -
> -		LOG(HAL, Debug) << cfg.toString() << " supported";
> -
> -		supportedResolutions.push_back(res);
> -	}
> -
> -	return supportedResolutions;
> -}
> -
> -std::vector<Size> CameraDevice::getRawResolutions(const libcamera::PixelFormat &pixelFormat)
> -{
> -	std::unique_ptr<CameraConfiguration> cameraConfig =
> -		camera_->generateConfiguration({ StreamRole::Raw });
> -	StreamConfiguration &cfg = cameraConfig->at(0);
> -	const StreamFormats &formats = cfg.formats();
> -	std::vector<Size> supportedResolutions = formats.sizes(pixelFormat);
> -
> -	return supportedResolutions;
> -}
> -
> -/*
> - * Initialize the format conversion map to translate from Android format
> - * identifier to libcamera pixel formats and fill in the list of supported
> - * stream configurations to be reported to the Android camera framework through
> - * the static stream configuration metadata.
> - */
> -int CameraDevice::initializeStreamConfigurations()
> -{
> -	/*
> -	 * Get the maximum output resolutions
> -	 * \todo Get this from the camera properties once defined
> -	 */
> -	std::unique_ptr<CameraConfiguration> cameraConfig =
> -		camera_->generateConfiguration({ StillCapture });
> -	if (!cameraConfig) {
> -		LOG(HAL, Error) << "Failed to get maximum resolution";
> -		return -EINVAL;
> -	}
> -	StreamConfiguration &cfg = cameraConfig->at(0);
> -
> -	/*
> -	 * \todo JPEG - Adjust the maximum available resolution by taking the
> -	 * JPEG encoder requirements into account (alignment and aspect ratio).
> -	 */
> -	const Size maxRes = cfg.size;
> -	LOG(HAL, Debug) << "Maximum supported resolution: " << maxRes.toString();
> -
> -	/*
> -	 * Build the list of supported image resolutions.
> -	 *
> -	 * The resolutions listed in camera3Resolution are mandatory to be
> -	 * supported, up to the camera maximum resolution.
> -	 *
> -	 * Augment the list by adding resolutions calculated from the camera
> -	 * maximum one.
> -	 */
> -	std::vector<Size> cameraResolutions;
> -	std::copy_if(camera3Resolutions.begin(), camera3Resolutions.end(),
> -		     std::back_inserter(cameraResolutions),
> -		     [&](const Size &res) { return res < maxRes; });
> -
> -	/*
> -	 * The Camera3 specification suggests adding 1/2 and 1/4 of the maximum
> -	 * resolution.
> -	 */
> -	for (unsigned int divider = 2;; divider <<= 1) {
> -		Size derivedSize{
> -			maxRes.width / divider,
> -			maxRes.height / divider,
> -		};
> -
> -		if (derivedSize.width < 320 ||
> -		    derivedSize.height < 240)
> -			break;
> -
> -		cameraResolutions.push_back(derivedSize);
> -	}
> -	cameraResolutions.push_back(maxRes);
> -
> -	/* Remove duplicated entries from the list of supported resolutions. */
> -	std::sort(cameraResolutions.begin(), cameraResolutions.end());
> -	auto last = std::unique(cameraResolutions.begin(), cameraResolutions.end());
> -	cameraResolutions.erase(last, cameraResolutions.end());
> -
> -	/*
> -	 * Build the list of supported camera formats.
> -	 *
> -	 * To each Android format a list of compatible libcamera formats is
> -	 * associated. The first libcamera format that tests successful is added
> -	 * to the format translation map used when configuring the streams.
> -	 * It is then tested against the list of supported camera resolutions to
> -	 * build the stream configuration map reported through the camera static
> -	 * metadata.
> -	 */
> -	Size maxJpegSize;
> -	for (const auto &format : camera3FormatsMap) {
> -		int androidFormat = format.first;
> -		const Camera3Format &camera3Format = format.second;
> -		const std::vector<PixelFormat> &libcameraFormats =
> -			camera3Format.libcameraFormats;
> -
> -		LOG(HAL, Debug) << "Trying to map Android format "
> -				<< camera3Format.name;
> -
> -		/*
> -		 * JPEG is always supported, either produced directly by the
> -		 * camera, or encoded in the HAL.
> -		 */
> -		if (androidFormat == HAL_PIXEL_FORMAT_BLOB) {
> -			formatsMap_[androidFormat] = formats::MJPEG;
> -			LOG(HAL, Debug) << "Mapped Android format "
> -					<< camera3Format.name << " to "
> -					<< formats::MJPEG.toString()
> -					<< " (fixed mapping)";
> -			continue;
> -		}
> -
> -		/*
> -		 * Test the libcamera formats that can produce images
> -		 * compatible with the format defined by Android.
> -		 */
> -		PixelFormat mappedFormat;
> -		for (const PixelFormat &pixelFormat : libcameraFormats) {
> -
> -			LOG(HAL, Debug) << "Testing " << pixelFormat.toString();
> -
> -			/*
> -			 * The stream configuration size can be adjusted,
> -			 * not the pixel format.
> -			 *
> -			 * \todo This could be simplified once all pipeline
> -			 * handlers will report the StreamFormats list of
> -			 * supported formats.
> -			 */
> -			cfg.pixelFormat = pixelFormat;
> -
> -			CameraConfiguration::Status status = cameraConfig->validate();
> -			if (status != CameraConfiguration::Invalid &&
> -			    cfg.pixelFormat == pixelFormat) {
> -				mappedFormat = pixelFormat;
> -				break;
> -			}
> -		}
> -
> -		if (!mappedFormat.isValid()) {
> -			/* If the format is not mandatory, skip it. */
> -			if (!camera3Format.mandatory)
> -				continue;
> -
> -			LOG(HAL, Error)
> -				<< "Failed to map mandatory Android format "
> -				<< camera3Format.name << " ("
> -				<< utils::hex(androidFormat) << "): aborting";
> -			return -EINVAL;
> -		}
> -
> -		/*
> -		 * Record the mapping and then proceed to generate the
> -		 * stream configurations map, by testing the image resolutions.
> -		 */
> -		formatsMap_[androidFormat] = mappedFormat;
> -		LOG(HAL, Debug) << "Mapped Android format "
> -				<< camera3Format.name << " to "
> -				<< mappedFormat.toString();
> -
> -		std::vector<Size> resolutions;
> -		const PixelFormatInfo &info = PixelFormatInfo::info(mappedFormat);
> -		if (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW)
> -			resolutions = getRawResolutions(mappedFormat);
> -		else
> -			resolutions = getYUVResolutions(cameraConfig.get(),
> -							mappedFormat,
> -							cameraResolutions);
> -
> -		for (const Size &res : resolutions) {
> -			streamConfigurations_.push_back({ res, androidFormat });
> -
> -			/*
> -			 * If the format is HAL_PIXEL_FORMAT_YCbCr_420_888
> -			 * from which JPEG is produced, add an entry for
> -			 * the JPEG stream.
> -			 *
> -			 * \todo Wire the JPEG encoder to query the supported
> -			 * sizes provided a list of formats it can encode.
> -			 *
> -			 * \todo Support JPEG streams produced by the Camera
> -			 * natively.
> -			 */
> -			if (androidFormat == HAL_PIXEL_FORMAT_YCbCr_420_888) {
> -				streamConfigurations_.push_back(
> -					{ res, HAL_PIXEL_FORMAT_BLOB });
> -				maxJpegSize = std::max(maxJpegSize, res);
> -			}
> -		}
> -
> -		/*
> -		 * \todo Calculate the maximum JPEG buffer size by asking the
> -		 * encoder giving the maximum frame size required.
> -		 */
> -		maxJpegBufferSize_ = maxJpegSize.width * maxJpegSize.height * 1.5;
> -	}
> -
> -	LOG(HAL, Debug) << "Collected stream configuration map: ";
> -	for (const auto &entry : streamConfigurations_)
> -		LOG(HAL, Debug) << "{ " << entry.resolution.toString() << " - "
> -				<< utils::hex(entry.androidFormat) << " }";
> -
> -	return 0;
> +	return capabilities_.initialize(camera_, orientation_, facing_);
>  }
>  
>  /*
> @@ -817,802 +490,19 @@ void CameraDevice::stop()
>  	state_ = State::Stopped;
>  }
>  
> -void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks)
> +unsigned int CameraDevice::maxJpegBufferSize() const
>  {
> -	callbacks_ = callbacks;
> +	return capabilities_.maxJpegBufferSize();
>  }
>  
> -/*
> - * Return static information for the camera.
> - */
> -const camera_metadata_t *CameraDevice::getStaticMetadata()
> -{
> -	if (staticMetadata_)
> -		return staticMetadata_->get();
> -
> -	staticMetadata_ = std::make_unique<CameraMetadata>(64, 1024);
> -	if (!staticMetadata_->isValid()) {
> -		LOG(HAL, Error) << "Failed to allocate static metadata";
> -		staticMetadata_.reset();
> -		return nullptr;
> -	}
> -
> -	const ControlInfoMap &controlsInfo = camera_->controls();
> -	const ControlList &properties = camera_->properties();
> -
> -	/* Color correction static metadata. */
> -	{
> -		std::vector<uint8_t> data;
> -		data.reserve(3);
> -		const auto &infoMap = controlsInfo.find(&controls::draft::ColorCorrectionAberrationMode);
> -		if (infoMap != controlsInfo.end()) {
> -			for (const auto &value : infoMap->second.values())
> -				data.push_back(value.get<int32_t>());
> -		} else {
> -			data.push_back(ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF);
> -		}
> -		staticMetadata_->addEntry(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
> -					  data);
> -	}
> -
> -	/* Control static metadata. */
> -	std::vector<uint8_t> aeAvailableAntiBandingModes = {
> -		ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF,
> -		ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ,
> -		ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ,
> -		ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
> -				  aeAvailableAntiBandingModes);
> -
> -	std::vector<uint8_t> aeAvailableModes = {
> -		ANDROID_CONTROL_AE_MODE_ON,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,
> -				  aeAvailableModes);
> -
> -	int64_t minFrameDurationNsec = -1;
> -	int64_t maxFrameDurationNsec = -1;
> -	const auto frameDurationsInfo = controlsInfo.find(&controls::FrameDurationLimits);
> -	if (frameDurationsInfo != controlsInfo.end()) {
> -		minFrameDurationNsec = frameDurationsInfo->second.min().get<int64_t>() * 1000;
> -		maxFrameDurationNsec = frameDurationsInfo->second.max().get<int64_t>() * 1000;
> -
> -		/*
> -		 * Adjust the minimum frame duration to comply with Android
> -		 * requirements. The camera service mandates all preview/record
> -		 * streams to have a minimum frame duration < 33,366 milliseconds
> -		 * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera service
> -		 * implementation).
> -		 *
> -		 * If we're close enough (+ 500 useconds) to that value, round
> -		 * the minimum frame duration of the camera to an accepted
> -		 * value.
> -		 */
> -		static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS = 1e9 / 29.97;
> -		if (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS &&
> -		    minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS + 500000)
> -			minFrameDurationNsec = MAX_PREVIEW_RECORD_DURATION_NS - 1000;
> -
> -		/*
> -		 * The AE routine frame rate limits are computed using the frame
> -		 * duration limits, as libcamera clips the AE routine to the
> -		 * frame durations.
> -		 */
> -		int32_t maxFps = std::round(1e9 / minFrameDurationNsec);
> -		int32_t minFps = std::round(1e9 / maxFrameDurationNsec);
> -		minFps = std::max(1, minFps);
> -
> -		/*
> -		 * Force rounding errors so that we have the proper frame
> -		 * durations for when we reuse these variables later
> -		 */
> -		minFrameDurationNsec = 1e9 / maxFps;
> -		maxFrameDurationNsec = 1e9 / minFps;
> -
> -		/*
> -		 * Register to the camera service {min, max} and {max, max}
> -		 * intervals as requested by the metadata documentation.
> -		 */
> -		int32_t availableAeFpsTarget[] = {
> -			minFps, maxFps, maxFps, maxFps
> -		};
> -		staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> -					  availableAeFpsTarget);
> -	}
> -
> -	std::vector<int32_t> aeCompensationRange = {
> -		0, 0,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_RANGE,
> -				  aeCompensationRange);
> -
> -	const camera_metadata_rational_t aeCompensationStep[] = {
> -		{ 0, 1 }
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AE_COMPENSATION_STEP,
> -				  aeCompensationStep);
> -
> -	std::vector<uint8_t> availableAfModes = {
> -		ANDROID_CONTROL_AF_MODE_OFF,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AF_AVAILABLE_MODES,
> -				  availableAfModes);
> -
> -	std::vector<uint8_t> availableEffects = {
> -		ANDROID_CONTROL_EFFECT_MODE_OFF,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_EFFECTS,
> -				  availableEffects);
> -
> -	std::vector<uint8_t> availableSceneModes = {
> -		ANDROID_CONTROL_SCENE_MODE_DISABLED,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
> -				  availableSceneModes);
> -
> -	std::vector<uint8_t> availableStabilizationModes = {
> -		ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
> -				  availableStabilizationModes);
> -
> -	/*
> -	 * \todo Inspect the Camera capabilities to report the available
> -	 * AWB modes. Default to AUTO as CTS tests require it.
> -	 */
> -	std::vector<uint8_t> availableAwbModes = {
> -		ANDROID_CONTROL_AWB_MODE_AUTO,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
> -				  availableAwbModes);
> -
> -	std::vector<int32_t> availableMaxRegions = {
> -		0, 0, 0,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_MAX_REGIONS,
> -				  availableMaxRegions);
> -
> -	std::vector<uint8_t> sceneModesOverride = {
> -		ANDROID_CONTROL_AE_MODE_ON,
> -		ANDROID_CONTROL_AWB_MODE_AUTO,
> -		ANDROID_CONTROL_AF_MODE_OFF,
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_SCENE_MODE_OVERRIDES,
> -				  sceneModesOverride);
> -
> -	uint8_t aeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AE_LOCK_AVAILABLE,
> -				  aeLockAvailable);
> -
> -	uint8_t awbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
> -				  awbLockAvailable);
> -
> -	char availableControlModes = ANDROID_CONTROL_MODE_AUTO;
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AVAILABLE_MODES,
> -				  availableControlModes);
> -
> -	/* JPEG static metadata. */
> -
> -	/*
> -	 * Create the list of supported thumbnail sizes by inspecting the
> -	 * available JPEG resolutions collected in streamConfigurations_ and
> -	 * generate one entry for each aspect ratio.
> -	 *
> -	 * The JPEG thumbnailer can freely scale, so pick an arbitrary
> -	 * (160, 160) size as the bounding rectangle, which is then cropped to
> -	 * the different supported aspect ratios.
> -	 */
> -	constexpr Size maxJpegThumbnail(160, 160);
> -	std::vector<Size> thumbnailSizes;
> -	thumbnailSizes.push_back({ 0, 0 });
> -	for (const auto &entry : streamConfigurations_) {
> -		if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)
> -			continue;
> -
> -		Size thumbnailSize = maxJpegThumbnail
> -				     .boundedToAspectRatio({ entry.resolution.width,
> -							     entry.resolution.height });
> -		thumbnailSizes.push_back(thumbnailSize);
> -	}
> -
> -	std::sort(thumbnailSizes.begin(), thumbnailSizes.end());
> -	auto last = std::unique(thumbnailSizes.begin(), thumbnailSizes.end());
> -	thumbnailSizes.erase(last, thumbnailSizes.end());
> -
> -	/* Transform sizes in to a list of integers that can be consumed. */
> -	std::vector<int32_t> thumbnailEntries;
> -	thumbnailEntries.reserve(thumbnailSizes.size() * 2);
> -	for (const auto &size : thumbnailSizes) {
> -		thumbnailEntries.push_back(size.width);
> -		thumbnailEntries.push_back(size.height);
> -	}
> -	staticMetadata_->addEntry(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> -				  thumbnailEntries);
> -
> -	staticMetadata_->addEntry(ANDROID_JPEG_MAX_SIZE, maxJpegBufferSize_);
> -
> -	/* Sensor static metadata. */
> -	std::array<int32_t, 2> pixelArraySize;
> -	{
> -		const Size &size = properties.get(properties::PixelArraySize);
> -		pixelArraySize[0] = size.width;
> -		pixelArraySize[1] = size.height;
> -		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> -					  pixelArraySize);
> -	}
> -
> -	if (properties.contains(properties::UnitCellSize)) {
> -		const Size &cellSize = properties.get<Size>(properties::UnitCellSize);
> -		std::array<float, 2> physicalSize{
> -			cellSize.width * pixelArraySize[0] / 1e6f,
> -			cellSize.height * pixelArraySize[1] / 1e6f
> -		};
> -		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> -					  physicalSize);
> -	}
> -
> -	{
> -		const Span<const Rectangle> &rects =
> -			properties.get(properties::PixelArrayActiveAreas);
> -		std::vector<int32_t> data{
> -			static_cast<int32_t>(rects[0].x),
> -			static_cast<int32_t>(rects[0].y),
> -			static_cast<int32_t>(rects[0].width),
> -			static_cast<int32_t>(rects[0].height),
> -		};
> -		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> -					  data);
> -	}
> -
> -	int32_t sensitivityRange[] = {
> -		32, 2400,
> -	};
> -	staticMetadata_->addEntry(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> -				  sensitivityRange);
> -
> -	/* Report the color filter arrangement if the camera reports it. */
> -	if (properties.contains(properties::draft::ColorFilterArrangement)) {
> -		uint8_t filterArr = properties.get(properties::draft::ColorFilterArrangement);
> -		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> -					  filterArr);
> -	}
> -
> -	const auto &exposureInfo = controlsInfo.find(&controls::ExposureTime);
> -	if (exposureInfo != controlsInfo.end()) {
> -		int64_t exposureTimeRange[2] = {
> -			exposureInfo->second.min().get<int32_t>() * 1000LL,
> -			exposureInfo->second.max().get<int32_t>() * 1000LL,
> -		};
> -		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> -					  exposureTimeRange, 2);
> -	}
> -
> -	staticMetadata_->addEntry(ANDROID_SENSOR_ORIENTATION, orientation_);
> -
> -	std::vector<int32_t> testPatternModes = {
> -		ANDROID_SENSOR_TEST_PATTERN_MODE_OFF
> -	};
> -	const auto &testPatternsInfo =
> -		controlsInfo.find(&controls::draft::TestPatternMode);
> -	if (testPatternsInfo != controlsInfo.end()) {
> -		const auto &values = testPatternsInfo->second.values();
> -		ASSERT(!values.empty());
> -		for (const auto &value : values) {
> -			switch (value.get<int32_t>()) {
> -			case controls::draft::TestPatternModeOff:
> -				/*
> -				 * ANDROID_SENSOR_TEST_PATTERN_MODE_OFF is
> -				 * already in testPatternModes.
> -				 */
> -				break;
> -
> -			case controls::draft::TestPatternModeSolidColor:
> -				testPatternModes.push_back(
> -					ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR);
> -				break;
> -
> -			case controls::draft::TestPatternModeColorBars:
> -				testPatternModes.push_back(
> -					ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS);
> -				break;
> -
> -			case controls::draft::TestPatternModeColorBarsFadeToGray:
> -				testPatternModes.push_back(
> -					ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS_FADE_TO_GRAY);
> -				break;
> -
> -			case controls::draft::TestPatternModePn9:
> -				testPatternModes.push_back(
> -					ANDROID_SENSOR_TEST_PATTERN_MODE_PN9);
> -				break;
> -
> -			case controls::draft::TestPatternModeCustom1:
> -				/* We don't support this yet. */
> -				break;
> -
> -			default:
> -				LOG(HAL, Error) << "Unknown test pattern mode: "
> -						<< value.get<int32_t>();
> -				continue;
> -			}
> -		}
> -	}
> -	staticMetadata_->addEntry(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> -				  testPatternModes);
> -
> -	uint8_t timestampSource = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
> -	staticMetadata_->addEntry(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> -				  timestampSource);
> -
> -	if (maxFrameDurationNsec > 0)
> -		staticMetadata_->addEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> -					  maxFrameDurationNsec);
> -
> -	/* Statistics static metadata. */
> -	uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> -	staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> -				  faceDetectMode);
> -
> -	int32_t maxFaceCount = 0;
> -	staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> -				  maxFaceCount);
> -
> -	{
> -		std::vector<uint8_t> data;
> -		data.reserve(2);
> -		const auto &infoMap = controlsInfo.find(&controls::draft::LensShadingMapMode);
> -		if (infoMap != controlsInfo.end()) {
> -			for (const auto &value : infoMap->second.values())
> -				data.push_back(value.get<int32_t>());
> -		} else {
> -			data.push_back(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF);
> -		}
> -		staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
> -					  data);
> -	}
> -
> -	/* Sync static metadata. */
> -	int32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
> -	staticMetadata_->addEntry(ANDROID_SYNC_MAX_LATENCY, maxLatency);
> -
> -	/* Flash static metadata. */
> -	char flashAvailable = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
> -	staticMetadata_->addEntry(ANDROID_FLASH_INFO_AVAILABLE,
> -				  flashAvailable);
> -
> -	/* Lens static metadata. */
> -	std::vector<float> lensApertures = {
> -		2.53 / 100,
> -	};
> -	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> -				  lensApertures);
> -
> -	uint8_t lensFacing;
> -	switch (facing_) {
> -	default:
> -	case CAMERA_FACING_FRONT:
> -		lensFacing = ANDROID_LENS_FACING_FRONT;
> -		break;
> -	case CAMERA_FACING_BACK:
> -		lensFacing = ANDROID_LENS_FACING_BACK;
> -		break;
> -	case CAMERA_FACING_EXTERNAL:
> -		lensFacing = ANDROID_LENS_FACING_EXTERNAL;
> -		break;
> -	}
> -	staticMetadata_->addEntry(ANDROID_LENS_FACING, lensFacing);
> -
> -	std::vector<float> lensFocalLengths = {
> -		1,
> -	};
> -	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> -				  lensFocalLengths);
> -
> -	std::vector<uint8_t> opticalStabilizations = {
> -		ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF,
> -	};
> -	staticMetadata_->addEntry(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> -				  opticalStabilizations);
> -
> -	float hypeFocalDistance = 0;
> -	staticMetadata_->addEntry(ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> -				  hypeFocalDistance);
> -
> -	float minFocusDistance = 0;
> -	staticMetadata_->addEntry(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> -				  minFocusDistance);
> -
> -	/* Noise reduction modes. */
> -	{
> -		std::vector<uint8_t> data;
> -		data.reserve(5);
> -		const auto &infoMap = controlsInfo.find(&controls::draft::NoiseReductionMode);
> -		if (infoMap != controlsInfo.end()) {
> -			for (const auto &value : infoMap->second.values())
> -				data.push_back(value.get<int32_t>());
> -		} else {
> -			data.push_back(ANDROID_NOISE_REDUCTION_MODE_OFF);
> -		}
> -		staticMetadata_->addEntry(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
> -					  data);
> -	}
> -
> -	/* Scaler static metadata. */
> -
> -	/*
> -	 * \todo The digital zoom factor is a property that depends on the
> -	 * desired output configuration and the sensor frame size input to the
> -	 * ISP. This information is not available to the Android HAL, not at
> -	 * initialization time at least.
> -	 *
> -	 * As a workaround rely on pipeline handlers initializing the
> -	 * ScalerCrop control with the camera default configuration and use the
> -	 * maximum and minimum crop rectangles to calculate the digital zoom
> -	 * factor.
> -	 */
> -	float maxZoom = 1.0f;
> -	const auto scalerCrop = controlsInfo.find(&controls::ScalerCrop);
> -	if (scalerCrop != controlsInfo.end()) {
> -		Rectangle min = scalerCrop->second.min().get<Rectangle>();
> -		Rectangle max = scalerCrop->second.max().get<Rectangle>();
> -		maxZoom = std::min(1.0f * max.width / min.width,
> -				   1.0f * max.height / min.height);
> -	}
> -	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
> -				  maxZoom);
> -
> -	std::vector<uint32_t> availableStreamConfigurations;
> -	availableStreamConfigurations.reserve(streamConfigurations_.size() * 4);
> -	for (const auto &entry : streamConfigurations_) {
> -		availableStreamConfigurations.push_back(entry.androidFormat);
> -		availableStreamConfigurations.push_back(entry.resolution.width);
> -		availableStreamConfigurations.push_back(entry.resolution.height);
> -		availableStreamConfigurations.push_back(
> -			ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
> -	}
> -	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> -				  availableStreamConfigurations);
> -
> -	std::vector<int64_t> availableStallDurations = {
> -		ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, 2560, 1920, 33333333,
> -	};
> -	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> -				  availableStallDurations);
> -
> -	/* Use the minimum frame duration for all the YUV/RGB formats. */
> -	if (minFrameDurationNsec > 0) {
> -		std::vector<int64_t> minFrameDurations;
> -		minFrameDurations.reserve(streamConfigurations_.size() * 4);
> -		for (const auto &entry : streamConfigurations_) {
> -			minFrameDurations.push_back(entry.androidFormat);
> -			minFrameDurations.push_back(entry.resolution.width);
> -			minFrameDurations.push_back(entry.resolution.height);
> -			minFrameDurations.push_back(minFrameDurationNsec);
> -		}
> -		staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> -					  minFrameDurations);
> -	}
> -
> -	uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
> -	staticMetadata_->addEntry(ANDROID_SCALER_CROPPING_TYPE, croppingType);
> -
> -	/* Info static metadata. */
> -	uint8_t supportedHWLevel = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
> -	staticMetadata_->addEntry(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> -				  supportedHWLevel);
> -
> -	/* Request static metadata. */
> -	int32_t partialResultCount = 1;
> -	staticMetadata_->addEntry(ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
> -				  partialResultCount);
> -
> -	{
> -		/* Default the value to 2 if not reported by the camera. */
> -		uint8_t maxPipelineDepth = 2;
> -		const auto &infoMap = controlsInfo.find(&controls::draft::PipelineDepth);
> -		if (infoMap != controlsInfo.end())
> -			maxPipelineDepth = infoMap->second.max().get<int32_t>();
> -		staticMetadata_->addEntry(ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
> -					  maxPipelineDepth);
> -	}
> -
> -	/* LIMITED does not support reprocessing. */
> -	uint32_t maxNumInputStreams = 0;
> -	staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
> -				  maxNumInputStreams);
> -
> -	std::vector<uint8_t> availableCapabilities = {
> -		ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
> -	};
> -
> -	/* Report if camera supports RAW. */
> -	bool rawStreamAvailable = false;
> -	std::unique_ptr<CameraConfiguration> cameraConfig =
> -		camera_->generateConfiguration({ StreamRole::Raw });
> -	if (cameraConfig && !cameraConfig->empty()) {
> -		const PixelFormatInfo &info =
> -			PixelFormatInfo::info(cameraConfig->at(0).pixelFormat);
> -		/* Only advertise RAW support if RAW16 is possible. */
> -		if (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW &&
> -		    info.bitsPerPixel == 16) {
> -			rawStreamAvailable = true;
> -			availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW);
> -		}
> -	}
> -
> -	/* Number of { RAW, YUV, JPEG } supported output streams */
> -	int32_t numOutStreams[] = { rawStreamAvailable, 2, 1 };
> -	staticMetadata_->addEntry(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
> -				  numOutStreams);
> -
> -	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
> -				  availableCapabilities);
> -
> -	std::vector<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_CONTROL_SCENE_MODE_OVERRIDES,
> -		ANDROID_FLASH_INFO_AVAILABLE,
> -		ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
> -		ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
> -		ANDROID_JPEG_MAX_SIZE,
> -		ANDROID_LENS_FACING,
> -		ANDROID_LENS_INFO_AVAILABLE_APERTURES,
> -		ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
> -		ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
> -		ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
> -		ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
> -		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_MIN_FRAME_DURATIONS,
> -		ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
> -		ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
> -		ANDROID_SCALER_CROPPING_TYPE,
> -		ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES,
> -		ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
> -		ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
> -		ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE,
> -		ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
> -		ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
> -		ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
> -		ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
> -		ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
> -		ANDROID_SENSOR_ORIENTATION,
> -		ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
> -		ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
> -		ANDROID_SYNC_MAX_LATENCY,
> -	};
> -	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
> -				  availableCharacteristicsKeys);
> -
> -	std::vector<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_APERTURE,
> -		ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> -		ANDROID_NOISE_REDUCTION_MODE,
> -		ANDROID_SCALER_CROP_REGION,
> -		ANDROID_STATISTICS_FACE_DETECT_MODE
> -	};
> -	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
> -				  availableRequestKeys);
> -
> -	std::vector<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_GPS_COORDINATES,
> -		ANDROID_JPEG_GPS_PROCESSING_METHOD,
> -		ANDROID_JPEG_GPS_TIMESTAMP,
> -		ANDROID_JPEG_ORIENTATION,
> -		ANDROID_JPEG_QUALITY,
> -		ANDROID_JPEG_SIZE,
> -		ANDROID_JPEG_THUMBNAIL_QUALITY,
> -		ANDROID_JPEG_THUMBNAIL_SIZE,
> -		ANDROID_LENS_APERTURE,
> -		ANDROID_LENS_FOCAL_LENGTH,
> -		ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> -		ANDROID_LENS_STATE,
> -		ANDROID_NOISE_REDUCTION_MODE,
> -		ANDROID_REQUEST_PIPELINE_DEPTH,
> -		ANDROID_SCALER_CROP_REGION,
> -		ANDROID_SENSOR_EXPOSURE_TIME,
> -		ANDROID_SENSOR_FRAME_DURATION,
> -		ANDROID_SENSOR_ROLLING_SHUTTER_SKEW,
> -		ANDROID_SENSOR_TEST_PATTERN_MODE,
> -		ANDROID_SENSOR_TIMESTAMP,
> -		ANDROID_STATISTICS_FACE_DETECT_MODE,
> -		ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
> -		ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
> -		ANDROID_STATISTICS_SCENE_FLICKER,
> -	};
> -	staticMetadata_->addEntry(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
> -				  availableResultKeys);
> -
> -	if (!staticMetadata_->isValid()) {
> -		LOG(HAL, Error) << "Failed to construct static metadata";
> -		staticMetadata_.reset();
> -		return nullptr;
> -	}
> -
> -	if (staticMetadata_->resized()) {
> -		auto [entryCount, dataCount] = staticMetadata_->usage();
> -		LOG(HAL, Info)
> -			<< "Static metadata resized: " << entryCount
> -			<< " entries and " << dataCount << " bytes used";
> -	}
> -
> -	return staticMetadata_->get();
> -}
> -
> -std::unique_ptr<CameraMetadata> CameraDevice::requestTemplatePreview()
> +void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks)
>  {
> -	/*
> -	 * \todo Keep this in sync with the actual number of entries.
> -	 * Currently: 20 entries, 35 bytes
> -	 */
> -	auto requestTemplate = std::make_unique<CameraMetadata>(21, 36);
> -	if (!requestTemplate->isValid()) {
> -		return nullptr;
> -	}
> -
> -	/* Get the FPS range registered in the static metadata. */
> -	camera_metadata_ro_entry_t entry;
> -	bool found = staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> -					       &entry);
> -	if (!found) {
> -		LOG(HAL, Error) << "Cannot create capture template without FPS range";
> -		return nullptr;
> -	}
> -
> -	/*
> -	 * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> -	 * has been assembled as {{min, max} {max, max}}.
> -	 */
> -	requestTemplate->addEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> -				  entry.data.i32, 2);
> -
> -	uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AE_MODE, aeMode);
> -
> -	int32_t aeExposureCompensation = 0;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
> -				  aeExposureCompensation);
> -
> -	uint8_t aePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
> -				  aePrecaptureTrigger);
> -
> -	uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AE_LOCK, aeLock);
> -
> -	uint8_t aeAntibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
> -				  aeAntibandingMode);
> -
> -	uint8_t afMode = ANDROID_CONTROL_AF_MODE_OFF;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AF_MODE, afMode);
> -
> -	uint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AF_TRIGGER, afTrigger);
> -
> -	uint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AWB_MODE, awbMode);
> -
> -	uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
> -	requestTemplate->addEntry(ANDROID_CONTROL_AWB_LOCK, awbLock);
> -
> -	uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
> -	requestTemplate->addEntry(ANDROID_FLASH_MODE, flashMode);
> -
> -	uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
> -	requestTemplate->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,
> -				  faceDetectMode);
> -
> -	uint8_t noiseReduction = ANDROID_NOISE_REDUCTION_MODE_OFF;
> -	requestTemplate->addEntry(ANDROID_NOISE_REDUCTION_MODE,
> -				  noiseReduction);
> -
> -	uint8_t aberrationMode = ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;
> -	requestTemplate->addEntry(ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
> -				  aberrationMode);
> -
> -	uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;
> -	requestTemplate->addEntry(ANDROID_CONTROL_MODE, controlMode);
> -
> -	float lensAperture = 2.53 / 100;
> -	requestTemplate->addEntry(ANDROID_LENS_APERTURE, lensAperture);
> -
> -	uint8_t opticalStabilization = ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
> -	requestTemplate->addEntry(ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
> -				  opticalStabilization);
> -
> -	uint8_t captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
> -	requestTemplate->addEntry(ANDROID_CONTROL_CAPTURE_INTENT,
> -				  captureIntent);
> -
> -	return requestTemplate;
> +	callbacks_ = callbacks;
>  }
>  
> -std::unique_ptr<CameraMetadata> CameraDevice::requestTemplateVideo()
> +const camera_metadata_t *CameraDevice::getStaticMetadata()
>  {
> -	std::unique_ptr<CameraMetadata> previewTemplate = requestTemplatePreview();
> -	if (!previewTemplate)
> -		return nullptr;
> -
> -	/*
> -	 * The video template requires a fixed FPS range. Everything else
> -	 * stays the same as the preview template.
> -	 */
> -	camera_metadata_ro_entry_t entry;
> -	staticMetadata_->getEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> -				  &entry);
> -
> -	/*
> -	 * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
> -	 * has been assembled as {{min, max} {max, max}}.
> -	 */
> -	previewTemplate->updateEntry(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
> -				     entry.data.i32 + 2, 2);
> -
> -	return previewTemplate;
> +	return capabilities_.staticMetadata()->get();
>  }
>  
>  /*
> @@ -1630,7 +520,7 @@ const camera_metadata_t *CameraDevice::constructDefaultRequestSettings(int type)
>  	switch (type) {
>  	case CAMERA3_TEMPLATE_PREVIEW:
>  		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
> -		requestTemplate = requestTemplatePreview();
> +		requestTemplate = capabilities_.requestTemplatePreview();
>  		break;
>  	case CAMERA3_TEMPLATE_STILL_CAPTURE:
>  		/*
> @@ -1638,15 +528,15 @@ const camera_metadata_t *CameraDevice::constructDefaultRequestSettings(int type)
>  		 * for the torch mode we currently do not support.
>  		 */
>  		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE;
> -		requestTemplate = requestTemplatePreview();
> +		requestTemplate = capabilities_.requestTemplatePreview();
>  		break;
>  	case CAMERA3_TEMPLATE_VIDEO_RECORD:
>  		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD;
> -		requestTemplate = requestTemplateVideo();
> +		requestTemplate = capabilities_.requestTemplateVideo();
>  		break;
>  	case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
>  		captureIntent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT;
> -		requestTemplate = requestTemplateVideo();
> +		requestTemplate = capabilities_.requestTemplateVideo();
>  		break;
>  	/* \todo Implement templates generation for the remaining use cases. */
>  	case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
> @@ -1668,19 +558,6 @@ const camera_metadata_t *CameraDevice::constructDefaultRequestSettings(int type)
>  	return requestTemplates_[type]->get();
>  }
>  
> -PixelFormat CameraDevice::toPixelFormat(int format) const
> -{
> -	/* Translate Android format code to libcamera pixel format. */
> -	auto it = formatsMap_.find(format);
> -	if (it == formatsMap_.end()) {
> -		LOG(HAL, Error) << "Requested format " << utils::hex(format)
> -				<< " not supported";
> -		return PixelFormat();
> -	}
> -
> -	return it->second;
> -}
> -
>  /*
>   * Inspect the stream_list to produce a list of StreamConfiguration to
>   * be use to configure the Camera.
> @@ -1727,7 +604,7 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
>  		camera3_stream_t *stream = stream_list->streams[i];
>  		Size size(stream->width, stream->height);
>  
> -		PixelFormat format = toPixelFormat(stream->format);
> +		PixelFormat format = capabilities_.toPixelFormat(stream->format);
>  
>  		LOG(HAL, Info) << "Stream #" << i
>  			       << ", direction: " << stream->stream_type
> diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> index 4aadb27c562c..090fe28a551e 100644
> --- a/src/android/camera_device.h
> +++ b/src/android/camera_device.h
> @@ -10,14 +10,12 @@
>  #include <map>
>  #include <memory>
>  #include <mutex>
> -#include <tuple>
>  #include <vector>
>  
>  #include <hardware/camera3.h>
>  
>  #include <libcamera/buffer.h>
>  #include <libcamera/camera.h>
> -#include <libcamera/geometry.h>
>  #include <libcamera/request.h>
>  #include <libcamera/stream.h>
>  
> @@ -26,6 +24,7 @@
>  #include "libcamera/internal/message.h"
>  #include "libcamera/internal/thread.h"
>  
> +#include "camera_capabilities.h"
>  #include "camera_metadata.h"
>  #include "camera_stream.h"
>  #include "camera_worker.h"
> @@ -57,7 +56,7 @@ public:
>  	const std::string &model() const { return model_; }
>  	int facing() const { return facing_; }
>  	int orientation() const { return orientation_; }
> -	unsigned int maxJpegBufferSize() const { return maxJpegBufferSize_; }
> +	unsigned int maxJpegBufferSize() const;
>  
>  	void setCallbacks(const camera3_callback_ops_t *callbacks);
>  	const camera_metadata_t *getStaticMetadata();
> @@ -86,11 +85,6 @@ private:
>  		std::unique_ptr<CaptureRequest> request_;
>  	};
>  
> -	struct Camera3StreamConfiguration {
> -		libcamera::Size resolution;
> -		int androidFormat;
> -	};
> -
>  	enum class State {
>  		Stopped,
>  		Flushing,
> @@ -99,22 +93,11 @@ private:
>  
>  	void stop();
>  
> -	int initializeStreamConfigurations();
> -	std::vector<libcamera::Size>
> -	getYUVResolutions(libcamera::CameraConfiguration *cameraConfig,
> -			  const libcamera::PixelFormat &pixelFormat,
> -			  const std::vector<libcamera::Size> &resolutions);
> -	std::vector<libcamera::Size>
> -	getRawResolutions(const libcamera::PixelFormat &pixelFormat);
> -
>  	libcamera::FrameBuffer *createFrameBuffer(const buffer_handle_t camera3buffer);
>  	void abortRequest(camera3_capture_request_t *request);
>  	void notifyShutter(uint32_t frameNumber, uint64_t timestamp);
>  	void notifyError(uint32_t frameNumber, camera3_stream_t *stream,
>  			 camera3_error_msg_code code);
> -	std::unique_ptr<CameraMetadata> requestTemplatePreview();
> -	std::unique_ptr<CameraMetadata> requestTemplateVideo();
> -	libcamera::PixelFormat toPixelFormat(int format) const;
>  	int processControls(Camera3RequestDescriptor *descriptor);
>  	std::unique_ptr<CameraMetadata> getResultMetadata(
>  		const Camera3RequestDescriptor &descriptor) const;
> @@ -129,13 +112,11 @@ private:
>  
>  	std::shared_ptr<libcamera::Camera> camera_;
>  	std::unique_ptr<libcamera::CameraConfiguration> config_;
> +	CameraCapabilities capabilities_;
>  
> -	std::unique_ptr<CameraMetadata> staticMetadata_;
>  	std::map<unsigned int, std::unique_ptr<CameraMetadata>> requestTemplates_;
>  	const camera3_callback_ops_t *callbacks_;
>  
> -	std::vector<Camera3StreamConfiguration> streamConfigurations_;
> -	std::map<int, libcamera::PixelFormat> formatsMap_;
>  	std::vector<CameraStream> streams_;
>  
>  	libcamera::Mutex descriptorsMutex_; /* Protects descriptors_. */
> @@ -147,8 +128,6 @@ private:
>  	int facing_;
>  	int orientation_;
>  
> -	unsigned int maxJpegBufferSize_;
> -
>  	CameraMetadata lastSettings_;
>  };
>  
> diff --git a/src/android/meson.build b/src/android/meson.build
> index 3893e5b5b832..e093aa2ec565 100644
> --- a/src/android/meson.build
> +++ b/src/android/meson.build
> @@ -45,6 +45,7 @@ subdir('cros')
>  android_hal_sources = files([
>      'camera3_hal.cpp',
>      'camera_hal_manager.cpp',
> +    'camera_capabilities.cpp',
>      'camera_device.cpp',
>      'camera_hal_config.cpp',
>      'camera_metadata.cpp',
> -- 
> 2.31.1
> 


More information about the libcamera-devel mailing list