[libcamera-devel] [PATCH RFC 6/7] android: yuv: add YUYV -> NV12 conversion

Mattijs Korpershoek mkorpershoek at baylibre.com
Fri Sep 15 09:57:30 CEST 2023


On am62x platforms, the receiver driver (j721e-csi2rx) only
supports packed YUV422 formats such as YUYV, YVYU, UYVY and VYUY.

The receiver and the sensor (ov5640) hardware are both capable of
YUV420, however:

* we are not aware of OV5640 being tested with YUV420 formats on any
  vendor tree.
* NV12 has different line lines (even lines are twice as long as odd
  lines) Different line-sized DMA transfers have not been tested on
  the TI CSI-RX SHIM IP.

On the other hand, the graphics allocator (gralloc) cannot allocate
YUV422 buffers directly. It mainly allocated NV12 buffers when
userspace requests HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED.

Because of the above, we need a pixel conversion from YUYV to NV12,
which is handled in the yuv processor via libyuv.

Signed-off-by: Mattijs Korpershoek <mkorpershoek at baylibre.com>
---
 src/android/yuv/post_processor_yuv.cpp | 91 +++++++++++++++++++++++++---------
 1 file changed, 68 insertions(+), 23 deletions(-)

diff --git a/src/android/yuv/post_processor_yuv.cpp b/src/android/yuv/post_processor_yuv.cpp
index 734bb85b7351..b680b833dafb 100644
--- a/src/android/yuv/post_processor_yuv.cpp
+++ b/src/android/yuv/post_processor_yuv.cpp
@@ -7,6 +7,9 @@
 
 #include "post_processor_yuv.h"
 
+#include <utility>
+
+#include <libyuv/convert.h>
 #include <libyuv/scale.h>
 
 #include <libcamera/base/log.h>
@@ -22,14 +25,42 @@ using namespace libcamera;
 
 LOG_DEFINE_CATEGORY(YUV)
 
+namespace {
+
+/**
+ * \var supportedConversions
+ * \brief list of supported output pixel formats for an input pixel format
+ */
+const std::map<PixelFormat, const std::vector<PixelFormat>> supportedConversions = {
+	{ formats::YUYV, { formats::NV12 } },
+};
+
+} /* namespace */
+
 int PostProcessorYuv::configure(const StreamConfiguration &inCfg,
 				const StreamConfiguration &outCfg)
 {
 	if (inCfg.pixelFormat != outCfg.pixelFormat) {
-		LOG(YUV, Error) << "Pixel format conversion is not supported"
-				<< " (from " << inCfg.pixelFormat
-				<< " to " << outCfg.pixelFormat << ")";
-		return -EINVAL;
+		const auto it = supportedConversions.find(inCfg.pixelFormat);
+		if (it == supportedConversions.end()) {
+			LOG(YUV, Error) << "Unsupported source format " << inCfg.pixelFormat;
+			return -EINVAL;
+		}
+
+		std::vector<PixelFormat> outFormats = it->second;
+		const auto &match = std::find(outFormats.begin(), outFormats.end(), outCfg.pixelFormat);
+		if (match == outFormats.end()) {
+			LOG(YUV, Error) << "Requested pixel format conversion is not supported"
+					<< " (from " << inCfg.pixelFormat
+					<< " to " << outCfg.pixelFormat << ")";
+			return -EINVAL;
+		}
+	} else {
+		if (inCfg.pixelFormat != formats::NV12) {
+			LOG(YUV, Error) << "Unsupported format " << inCfg.pixelFormat
+					<< " (only NV12 is supported for scaling)";
+			return -EINVAL;
+		}
 	}
 
 	if (inCfg.size < outCfg.size) {
@@ -39,12 +70,6 @@ int PostProcessorYuv::configure(const StreamConfiguration &inCfg,
 		return -EINVAL;
 	}
 
-	if (inCfg.pixelFormat != formats::NV12) {
-		LOG(YUV, Error) << "Unsupported format " << inCfg.pixelFormat
-				<< " (only NV12 is supported)";
-		return -EINVAL;
-	}
-
 	calculateLengths(inCfg, outCfg);
 	return 0;
 }
@@ -66,20 +91,40 @@ void PostProcessorYuv::process(Camera3RequestDescriptor::StreamBuffer *streamBuf
 		return;
 	}
 
-	int ret = libyuv::NV12Scale(sourceMapped.planes()[0].data(),
-				    sourceStride_[0],
-				    sourceMapped.planes()[1].data(),
-				    sourceStride_[1],
-				    sourceSize_.width, sourceSize_.height,
-				    destination->plane(0).data(),
-				    destinationStride_[0],
-				    destination->plane(1).data(),
-				    destinationStride_[1],
-				    destinationSize_.width,
-				    destinationSize_.height,
-				    libyuv::FilterMode::kFilterBilinear);
+	int ret = 0;
+	switch (sourceFormat_) {
+	case formats::NV12:
+		ret = libyuv::NV12Scale(sourceMapped.planes()[0].data(),
+					sourceStride_[0],
+					sourceMapped.planes()[1].data(),
+					sourceStride_[1],
+					sourceSize_.width, sourceSize_.height,
+					destination->plane(0).data(),
+					destinationStride_[0],
+					destination->plane(1).data(),
+					destinationStride_[1],
+					destinationSize_.width,
+					destinationSize_.height,
+					libyuv::FilterMode::kFilterBilinear);
+		break;
+	case formats::YUYV:
+		ret = libyuv::YUY2ToNV12(sourceMapped.planes()[0].data(),
+					 sourceStride_[0],
+					 destination->plane(0).data(),
+					 destinationStride_[0],
+					 destination->plane(1).data(),
+					 destinationStride_[1],
+					 destinationSize_.width,
+					 destinationSize_.height);
+		break;
+	default:
+		LOG(YUV, Error) << "Unsupported source format " << sourceFormat_;
+		processComplete.emit(streamBuffer, PostProcessor::Status::Error);
+		break;
+	}
+
 	if (ret) {
-		LOG(YUV, Error) << "Failed NV12 scaling: " << ret;
+		LOG(YUV, Error) << "Libyuv operation failure: " << ret;
 		processComplete.emit(streamBuffer, PostProcessor::Status::Error);
 		return;
 	}

-- 
2.41.0



More information about the libcamera-devel mailing list