From 26d90af082d1d810a75ba73472cce2950fc94284 Mon Sep 17 00:00:00 2001
From: Hirokazu Honda <hiroh@chromium.org>
Date: Fri, 11 Dec 2020 09:53:36 +0000
Subject: android: camera_device: Reorder configurations before requesting

This reorders Camera3Configs before executing
CameraConfiguration::validate() to make it easier for the Camera
to satisfy the Android framework request.

Signed-off-by: Hirokazu Honda <hiroh@chromium.org>
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Umang Jain <email@uajain.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Add blank line and fix compilation on gcc 7.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 src/android/camera_device.cpp | 113 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 111 insertions(+), 2 deletions(-)

diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
index 4a19aa03..38689bdc 100644
--- a/src/android/camera_device.cpp
+++ b/src/android/camera_device.cpp
@@ -27,6 +27,8 @@
 
 using namespace libcamera;
 
+LOG_DECLARE_CATEGORY(HAL)
+
 namespace {
 
 /*
@@ -145,9 +147,115 @@ struct Camera3StreamConfig {
 	StreamConfiguration config;
 };
 
-} /* namespace */
+/*
+ * Reorder the configurations so that libcamera::Camera can accept them as much
+ * as possible. The sort rule is as follows.
+ * 1.) The configuration for NV12 request whose resolution is the largest.
+ * 2.) The configuration for JPEG request.
+ * 3.) Others. Larger resolutions and different formats are put earlier.
+ */
+void sortCamera3StreamConfigs(std::vector<Camera3StreamConfig> &unsortedConfigs,
+			      const camera3_stream_t *jpegStream)
+{
+	const Camera3StreamConfig *jpegConfig = nullptr;
+
+	std::map<PixelFormat, std::vector<const Camera3StreamConfig *>> formatToConfigs;
+	for (const auto &streamConfig : unsortedConfigs) {
+		if (jpegStream && !jpegConfig) {
+			const auto &streams = streamConfig.streams;
+			if (std::find_if(streams.begin(), streams.end(),
+					 [jpegStream](const auto &stream) {
+						 return stream.stream == jpegStream;
+					 }) != streams.end()) {
+				jpegConfig = &streamConfig;
+				continue;
+			}
+		}
+		formatToConfigs[streamConfig.config.pixelFormat].push_back(&streamConfig);
+	}
 
-LOG_DECLARE_CATEGORY(HAL)
+	if (jpegStream && !jpegConfig)
+		LOG(HAL, Fatal) << "No Camera3StreamConfig is found for JPEG";
+
+	for (auto &fmt : formatToConfigs) {
+		auto &streamConfigs = fmt.second;
+
+		/* Sorted by resolution. Smaller is put first. */
+		std::sort(streamConfigs.begin(), streamConfigs.end(),
+			  [](const auto *streamConfigA, const auto *streamConfigB) {
+				  const Size &sizeA = streamConfigA->config.size;
+				  const Size &sizeB = streamConfigB->config.size;
+				  return sizeA < sizeB;
+			  });
+	}
+
+	std::vector<Camera3StreamConfig> sortedConfigs;
+	sortedConfigs.reserve(unsortedConfigs.size());
+
+	/*
+	 * NV12 is the most prioritized format. Put the configuration with NV12
+	 * and the largest resolution first.
+	 */
+	const auto nv12It = formatToConfigs.find(formats::NV12);
+	if (nv12It != formatToConfigs.end()) {
+		auto &nv12Configs = nv12It->second;
+		const auto &nv12Largest = nv12Configs.back();
+
+		/*
+		 * If JPEG will be created from NV12 and the size is larger than
+		 * the largest NV12 configurations, then put the NV12
+		 * configuration for JPEG first.
+		 */
+		if (jpegConfig && jpegConfig->config.pixelFormat == formats::NV12) {
+			const Size &nv12SizeForJpeg = jpegConfig->config.size;
+			const Size &nv12LargestSize = nv12Largest->config.size;
+
+			if (nv12LargestSize < nv12SizeForJpeg) {
+				LOG(HAL, Debug) << "Insert " << jpegConfig->config.toString();
+				sortedConfigs.push_back(std::move(*jpegConfig));
+				jpegConfig = nullptr;
+			}
+		}
+
+		LOG(HAL, Debug) << "Insert " << nv12Largest->config.toString();
+		sortedConfigs.push_back(*nv12Largest);
+		nv12Configs.pop_back();
+
+		if (nv12Configs.empty())
+			formatToConfigs.erase(nv12It);
+	}
+
+	/* If the configuration for JPEG is there, then put it. */
+	if (jpegConfig) {
+		LOG(HAL, Debug) << "Insert " << jpegConfig->config.toString();
+		sortedConfigs.push_back(std::move(*jpegConfig));
+		jpegConfig = nullptr;
+	}
+
+	/*
+	 * Put configurations with different formats and larger resolutions
+	 * earlier.
+	 */
+	while (!formatToConfigs.empty()) {
+		for (auto it = formatToConfigs.begin(); it != formatToConfigs.end();) {
+			auto &configs = it->second;
+			LOG(HAL, Debug) << "Insert " << configs.back()->config.toString();
+			sortedConfigs.push_back(*configs.back());
+			configs.pop_back();
+
+			if (configs.empty())
+				it = formatToConfigs.erase(it);
+			else
+				it++;
+		}
+	}
+
+	ASSERT(sortedConfigs.size() == unsortedConfigs.size());
+
+	unsortedConfigs = sortedConfigs;
+}
+
+} /* namespace */
 
 MappedCamera3Buffer::MappedCamera3Buffer(const buffer_handle_t camera3buffer,
 					 int flags)
@@ -1357,6 +1465,7 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
 		streamConfigs[index].streams.push_back({ jpegStream, type });
 	}
 
+	sortCamera3StreamConfigs(streamConfigs, jpegStream);
 	for (const auto &streamConfig : streamConfigs) {
 		config_->addConfiguration(streamConfig.config);
 
-- 
cgit v1.2.1