[libcamera-devel,2/4] libcamera: v4l2_pixelformat: Move DRM/V4L2 format conversion

Message ID 20200430005226.18162-3-laurent.pinchart@ideasonboard.com
State Superseded
Headers show
Series
  • libcamera: Improve conversion between DRM and V4L2 formats
Related show

Commit Message

Laurent Pinchart April 30, 2020, 12:52 a.m. UTC
Move the DRM/V4L2 format conversion code from V4L2VideoDevice to
V4L2PixelFormat. This is a more natural home for the code.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 src/libcamera/include/v4l2_pixelformat.h     |   4 +
 src/libcamera/include/v4l2_videodevice.h     |   3 -
 src/libcamera/pipeline/uvcvideo/uvcvideo.cpp |   2 +-
 src/libcamera/v4l2_pixelformat.cpp           | 134 +++++++++++++++++
 src/libcamera/v4l2_videodevice.cpp           | 147 +------------------
 test/libtest/buffer_source.cpp               |   4 +-
 6 files changed, 143 insertions(+), 151 deletions(-)

Comments

Kieran Bingham April 30, 2020, 8:05 a.m. UTC | #1
Hi Laurent,

On 30/04/2020 01:52, Laurent Pinchart wrote:
> Move the DRM/V4L2 format conversion code from V4L2VideoDevice to
> V4L2PixelFormat. This is a more natural home for the code.
> 

On 30/04/2020 08:55, Kieran Bingham wrote:
> Quite happy to see this in fact.

- Particularly because of this.


> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

> ---
>  src/libcamera/include/v4l2_pixelformat.h     |   4 +
>  src/libcamera/include/v4l2_videodevice.h     |   3 -
>  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp |   2 +-
>  src/libcamera/v4l2_pixelformat.cpp           | 134 +++++++++++++++++
>  src/libcamera/v4l2_videodevice.cpp           | 147 +------------------
>  test/libtest/buffer_source.cpp               |   4 +-
>  6 files changed, 143 insertions(+), 151 deletions(-)
> 
> diff --git a/src/libcamera/include/v4l2_pixelformat.h b/src/libcamera/include/v4l2_pixelformat.h
> index 4d277569cb8c..0fe8a017de34 100644
> --- a/src/libcamera/include/v4l2_pixelformat.h
> +++ b/src/libcamera/include/v4l2_pixelformat.h
> @@ -36,6 +36,10 @@ public:
>  
>  	std::string toString() const;
>  
> +	PixelFormat toPixelFormat() const;
> +	static V4L2PixelFormat fromPixelFormat(const PixelFormat &pixelFormat,
> +					       bool multiplanar);
> +
>  private:
>  	uint32_t fourcc_;
>  };
> diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h
> index ff64bb357c7e..982fee6bf740 100644
> --- a/src/libcamera/include/v4l2_videodevice.h
> +++ b/src/libcamera/include/v4l2_videodevice.h
> @@ -210,10 +210,7 @@ public:
>  	static V4L2VideoDevice *fromEntityName(const MediaDevice *media,
>  					       const std::string &entity);
>  
> -	static PixelFormat toPixelFormat(V4L2PixelFormat v4l2Fourcc);
>  	V4L2PixelFormat toV4L2PixelFormat(const PixelFormat &pixelFormat);
> -	static V4L2PixelFormat toV4L2PixelFormat(const PixelFormat &pixelFormat,
> -						 bool multiplanar);
>  
>  protected:
>  	std::string logPrefix() const;
> diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> index 030ac6864752..455693264c2e 100644
> --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> @@ -166,7 +166,7 @@ CameraConfiguration *PipelineHandlerUVC::generateConfiguration(Camera *camera,
>  		       std::inserter(deviceFormats, deviceFormats.begin()),
>  		       [&](const decltype(v4l2Formats)::value_type &format) {
>  			       return decltype(deviceFormats)::value_type{
> -				       data->video_->toPixelFormat(format.first),
> +				       format.first.toPixelFormat(),
>  				       format.second
>  			       };
>  		       });
> diff --git a/src/libcamera/v4l2_pixelformat.cpp b/src/libcamera/v4l2_pixelformat.cpp
> index 57d65c380b0d..876f5de321f6 100644
> --- a/src/libcamera/v4l2_pixelformat.cpp
> +++ b/src/libcamera/v4l2_pixelformat.cpp
> @@ -103,4 +103,138 @@ std::string V4L2PixelFormat::toString() const
>  	return ss;
>  }
>  
> +/**
> + * \brief Convert the V4L2 pixel format to the corresponding PixelFormat
> + * \return The PixelFormat corresponding to the V4L2 pixel format
> + */
> +PixelFormat V4L2PixelFormat::toPixelFormat() const
> +{
> +	switch (fourcc_) {
> +	/* RGB formats. */
> +	case V4L2_PIX_FMT_RGB24:
> +		return PixelFormat(DRM_FORMAT_BGR888);
> +	case V4L2_PIX_FMT_BGR24:
> +		return PixelFormat(DRM_FORMAT_RGB888);
> +	case V4L2_PIX_FMT_RGBA32:
> +		return PixelFormat(DRM_FORMAT_ABGR8888);
> +	case V4L2_PIX_FMT_ABGR32:
> +		return PixelFormat(DRM_FORMAT_ARGB8888);
> +	case V4L2_PIX_FMT_ARGB32:
> +		return PixelFormat(DRM_FORMAT_BGRA8888);
> +	case V4L2_PIX_FMT_BGRA32:
> +		return PixelFormat(DRM_FORMAT_RGBA8888);
> +
> +	/* YUV packed formats. */
> +	case V4L2_PIX_FMT_YUYV:
> +		return PixelFormat(DRM_FORMAT_YUYV);
> +	case V4L2_PIX_FMT_YVYU:
> +		return PixelFormat(DRM_FORMAT_YVYU);
> +	case V4L2_PIX_FMT_UYVY:
> +		return PixelFormat(DRM_FORMAT_UYVY);
> +	case V4L2_PIX_FMT_VYUY:
> +		return PixelFormat(DRM_FORMAT_VYUY);
> +
> +	/* YUY planar formats. */
> +	case V4L2_PIX_FMT_NV16:
> +	case V4L2_PIX_FMT_NV16M:
> +		return PixelFormat(DRM_FORMAT_NV16);
> +	case V4L2_PIX_FMT_NV61:
> +	case V4L2_PIX_FMT_NV61M:
> +		return PixelFormat(DRM_FORMAT_NV61);
> +	case V4L2_PIX_FMT_NV12:
> +	case V4L2_PIX_FMT_NV12M:
> +		return PixelFormat(DRM_FORMAT_NV12);
> +	case V4L2_PIX_FMT_NV21:
> +	case V4L2_PIX_FMT_NV21M:
> +		return PixelFormat(DRM_FORMAT_NV21);
> +
> +	/* Greyscale formats. */
> +	case V4L2_PIX_FMT_GREY:
> +		return PixelFormat(DRM_FORMAT_R8);
> +
> +	/* Compressed formats. */
> +	case V4L2_PIX_FMT_MJPEG:
> +		return PixelFormat(DRM_FORMAT_MJPEG);
> +
> +	/* V4L2 formats not yet supported by DRM. */
> +	default:
> +		LOG(V4L2, Warning)
> +			<< "Unsupported V4L2 pixel format "
> +			<< toString();
> +		return PixelFormat();
> +	}
> +}
> +
> +/**
> + * \brief Convert \a pixelFormat to its corresponding V4L2PixelFormat
> + * \param[in] pixelFormat The PixelFormat to convert
> + * \param[in] multiplanar V4L2 Multiplanar API support flag
> + *
> + * Multiple V4L2 formats may exist for one PixelFormat when the format uses
> + * multiple planes, as V4L2 defines separate 4CCs for contiguous and separate
> + * planes formats. Set the \a multiplanar parameter to false to select a format
> + * with contiguous planes, or to true to select a format with non-contiguous
> + * planes.
> + *
> + * \return The V4L2PixelFormat corresponding to \a pixelFormat
> + */
> +V4L2PixelFormat V4L2PixelFormat::fromPixelFormat(const PixelFormat &pixelFormat,
> +						 bool multiplanar)
> +{
> +	switch (pixelFormat) {
> +	/* RGB formats. */
> +	case DRM_FORMAT_BGR888:
> +		return V4L2PixelFormat(V4L2_PIX_FMT_RGB24);
> +	case DRM_FORMAT_RGB888:
> +		return V4L2PixelFormat(V4L2_PIX_FMT_BGR24);
> +	case DRM_FORMAT_ABGR8888:
> +		return V4L2PixelFormat(V4L2_PIX_FMT_RGBA32);
> +	case DRM_FORMAT_ARGB8888:
> +		return V4L2PixelFormat(V4L2_PIX_FMT_ABGR32);
> +	case DRM_FORMAT_BGRA8888:
> +		return V4L2PixelFormat(V4L2_PIX_FMT_ARGB32);
> +	case DRM_FORMAT_RGBA8888:
> +		return V4L2PixelFormat(V4L2_PIX_FMT_BGRA32);
> +
> +	/* YUV packed formats. */
> +	case DRM_FORMAT_YUYV:
> +		return V4L2PixelFormat(V4L2_PIX_FMT_YUYV);
> +	case DRM_FORMAT_YVYU:
> +		return V4L2PixelFormat(V4L2_PIX_FMT_YVYU);
> +	case DRM_FORMAT_UYVY:
> +		return V4L2PixelFormat(V4L2_PIX_FMT_UYVY);
> +	case DRM_FORMAT_VYUY:
> +		return V4L2PixelFormat(V4L2_PIX_FMT_VYUY);
> +
> +	/*
> +	 * YUY planar formats.
> +	 * \todo Add support for non-contiguous memory planes
> +	 * \todo Select the format variant not only based on \a multiplanar but
> +	 * also take into account the formats supported by the device.
> +	 */
> +	case DRM_FORMAT_NV16:
> +		return V4L2PixelFormat(V4L2_PIX_FMT_NV16);
> +	case DRM_FORMAT_NV61:
> +		return V4L2PixelFormat(V4L2_PIX_FMT_NV61);
> +	case DRM_FORMAT_NV12:
> +		return V4L2PixelFormat(V4L2_PIX_FMT_NV12);
> +	case DRM_FORMAT_NV21:
> +		return V4L2PixelFormat(V4L2_PIX_FMT_NV21);
> +
> +	/* Greyscale formats. */
> +	case DRM_FORMAT_R8:
> +		return V4L2PixelFormat(V4L2_PIX_FMT_GREY);
> +
> +	/* Compressed formats. */
> +	case DRM_FORMAT_MJPEG:
> +		return V4L2PixelFormat(V4L2_PIX_FMT_MJPEG);
> +
> +	default:
> +		LOG(V4L2, Warning)
> +			<< "Unsupported pixel format "
> +			<< pixelFormat.toString();
> +		return V4L2PixelFormat();
> +	}
> +}
> +
>  } /* namespace libcamera */
> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
> index 21df4f524212..e95b0c01cc85 100644
> --- a/src/libcamera/v4l2_videodevice.cpp
> +++ b/src/libcamera/v4l2_videodevice.cpp
> @@ -18,7 +18,6 @@
>  #include <unistd.h>
>  #include <vector>
>  
> -#include <linux/drm_fourcc.h>
>  #include <linux/version.h>
>  
>  #include <libcamera/event_notifier.h>
> @@ -1634,74 +1633,6 @@ V4L2VideoDevice *V4L2VideoDevice::fromEntityName(const MediaDevice *media,
>  	return new V4L2VideoDevice(mediaEntity);
>  }
>  
> -/**
> - * \brief Convert a \a v4l2Fourcc to the corresponding PixelFormat
> - * \param[in] v4l2Fourcc The V4L2 pixel format (V4L2_PIX_FORMAT_*)
> - * \return The PixelFormat corresponding to \a v4l2Fourcc
> - */
> -PixelFormat V4L2VideoDevice::toPixelFormat(V4L2PixelFormat v4l2Fourcc)
> -{
> -	switch (v4l2Fourcc) {
> -	/* RGB formats. */
> -	case V4L2_PIX_FMT_RGB24:
> -		return PixelFormat(DRM_FORMAT_BGR888);
> -	case V4L2_PIX_FMT_BGR24:
> -		return PixelFormat(DRM_FORMAT_RGB888);
> -	case V4L2_PIX_FMT_RGBA32:
> -		return PixelFormat(DRM_FORMAT_ABGR8888);
> -	case V4L2_PIX_FMT_ABGR32:
> -		return PixelFormat(DRM_FORMAT_ARGB8888);
> -	case V4L2_PIX_FMT_ARGB32:
> -		return PixelFormat(DRM_FORMAT_BGRA8888);
> -	case V4L2_PIX_FMT_BGRA32:
> -		return PixelFormat(DRM_FORMAT_RGBA8888);
> -
> -	/* YUV packed formats. */
> -	case V4L2_PIX_FMT_YUYV:
> -		return PixelFormat(DRM_FORMAT_YUYV);
> -	case V4L2_PIX_FMT_YVYU:
> -		return PixelFormat(DRM_FORMAT_YVYU);
> -	case V4L2_PIX_FMT_UYVY:
> -		return PixelFormat(DRM_FORMAT_UYVY);
> -	case V4L2_PIX_FMT_VYUY:
> -		return PixelFormat(DRM_FORMAT_VYUY);
> -
> -	/* YUY planar formats. */
> -	case V4L2_PIX_FMT_NV16:
> -	case V4L2_PIX_FMT_NV16M:
> -		return PixelFormat(DRM_FORMAT_NV16);
> -	case V4L2_PIX_FMT_NV61:
> -	case V4L2_PIX_FMT_NV61M:
> -		return PixelFormat(DRM_FORMAT_NV61);
> -	case V4L2_PIX_FMT_NV12:
> -	case V4L2_PIX_FMT_NV12M:
> -		return PixelFormat(DRM_FORMAT_NV12);
> -	case V4L2_PIX_FMT_NV21:
> -	case V4L2_PIX_FMT_NV21M:
> -		return PixelFormat(DRM_FORMAT_NV21);
> -
> -	/* Greyscale formats. */
> -	case V4L2_PIX_FMT_GREY:
> -		return PixelFormat(DRM_FORMAT_R8);
> -
> -	/* Compressed formats. */
> -	case V4L2_PIX_FMT_MJPEG:
> -		return PixelFormat(DRM_FORMAT_MJPEG);
> -
> -	/* V4L2 formats not yet supported by DRM. */
> -	default:
> -		/*
> -		 * \todo We can't use LOG() in a static method of a Loggable
> -		 * class. Until we fix the logger, work around it.
> -		 */
> -		libcamera::_log(__FILE__, __LINE__, _LOG_CATEGORY(V4L2)(),
> -				LogWarning).stream()

Aha, nice this all gets tidied up.

> -			<< "Unsupported V4L2 pixel format "
> -			<< v4l2Fourcc.toString();
> -		return PixelFormat();
> -	}
> -}
> -
>  /**
>   * \brief Convert \a PixelFormat to its corresponding V4L2 FourCC
>   * \param[in] pixelFormat The PixelFormat to convert
> @@ -1715,82 +1646,8 @@ PixelFormat V4L2VideoDevice::toPixelFormat(V4L2PixelFormat v4l2Fourcc)
>   */
>  V4L2PixelFormat V4L2VideoDevice::toV4L2PixelFormat(const PixelFormat &pixelFormat)
>  {
> -	return toV4L2PixelFormat(pixelFormat, caps_.isMultiplanar());
> -}
> -
> -/**
> - * \brief Convert \a pixelFormat to its corresponding V4L2 FourCC
> - * \param[in] pixelFormat The PixelFormat to convert
> - * \param[in] multiplanar V4L2 Multiplanar API support flag
> - *
> - * Multiple V4L2 formats may exist for one PixelFormat when the format uses
> - * multiple planes, as V4L2 defines separate 4CCs for contiguous and separate
> - * planes formats. Set the \a multiplanar parameter to false to select a format
> - * with contiguous planes, or to true to select a format with non-contiguous
> - * planes.
> - *
> - * \return The V4L2_PIX_FMT_* pixel format code corresponding to \a pixelFormat
> - */
> -V4L2PixelFormat V4L2VideoDevice::toV4L2PixelFormat(const PixelFormat &pixelFormat,
> -						   bool multiplanar)
> -{
> -	switch (pixelFormat) {
> -	/* RGB formats. */
> -	case DRM_FORMAT_BGR888:
> -		return V4L2PixelFormat(V4L2_PIX_FMT_RGB24);
> -	case DRM_FORMAT_RGB888:
> -		return V4L2PixelFormat(V4L2_PIX_FMT_BGR24);
> -	case DRM_FORMAT_ABGR8888:
> -		return V4L2PixelFormat(V4L2_PIX_FMT_RGBA32);
> -	case DRM_FORMAT_ARGB8888:
> -		return V4L2PixelFormat(V4L2_PIX_FMT_ABGR32);
> -	case DRM_FORMAT_BGRA8888:
> -		return V4L2PixelFormat(V4L2_PIX_FMT_ARGB32);
> -	case DRM_FORMAT_RGBA8888:
> -		return V4L2PixelFormat(V4L2_PIX_FMT_BGRA32);
> -
> -	/* YUV packed formats. */
> -	case DRM_FORMAT_YUYV:
> -		return V4L2PixelFormat(V4L2_PIX_FMT_YUYV);
> -	case DRM_FORMAT_YVYU:
> -		return V4L2PixelFormat(V4L2_PIX_FMT_YVYU);
> -	case DRM_FORMAT_UYVY:
> -		return V4L2PixelFormat(V4L2_PIX_FMT_UYVY);
> -	case DRM_FORMAT_VYUY:
> -		return V4L2PixelFormat(V4L2_PIX_FMT_VYUY);
> -
> -	/*
> -	 * YUY planar formats.
> -	 * \todo Add support for non-contiguous memory planes
> -	 * \todo Select the format variant not only based on \a multiplanar but
> -	 * also take into account the formats supported by the device.
> -	 */
> -	case DRM_FORMAT_NV16:
> -		return V4L2PixelFormat(V4L2_PIX_FMT_NV16);
> -	case DRM_FORMAT_NV61:
> -		return V4L2PixelFormat(V4L2_PIX_FMT_NV61);
> -	case DRM_FORMAT_NV12:
> -		return V4L2PixelFormat(V4L2_PIX_FMT_NV12);
> -	case DRM_FORMAT_NV21:
> -		return V4L2PixelFormat(V4L2_PIX_FMT_NV21);
> -
> -	/* Greyscale formats. */
> -	case DRM_FORMAT_R8:
> -		return V4L2PixelFormat(V4L2_PIX_FMT_GREY);
> -
> -	/* Compressed formats. */
> -	case DRM_FORMAT_MJPEG:
> -		return V4L2PixelFormat(V4L2_PIX_FMT_MJPEG);
> -	}
> -
> -	/*
> -	 * \todo We can't use LOG() in a static method of a Loggable
> -	 * class. Until we fix the logger, work around it.
> -	 */
> -	libcamera::_log(__FILE__, __LINE__, _LOG_CATEGORY(V4L2)(),
> -			LogWarning).stream()
> -		<< "Unsupported V4L2 pixel format " << pixelFormat.toString();
> -	return {};
> +	return V4L2PixelFormat::fromPixelFormat(pixelFormat,
> +						caps_.isMultiplanar());
>  }
>  
>  /**
> diff --git a/test/libtest/buffer_source.cpp b/test/libtest/buffer_source.cpp
> index dae3cb9f7a6c..d1dad2a0f8cf 100644
> --- a/test/libtest/buffer_source.cpp
> +++ b/test/libtest/buffer_source.cpp
> @@ -70,8 +70,8 @@ int BufferSource::allocate(const StreamConfiguration &config)
>  	}
>  
>  	format.size = config.size;
> -	format.fourcc = V4L2VideoDevice::toV4L2PixelFormat(config.pixelFormat,
> -							   false);
> +	format.fourcc = V4L2PixelFormat::fromPixelFormat(config.pixelFormat,
> +							 false);
>  	if (video->setFormat(&format)) {
>  		std::cout << "Failed to set format on output device" << std::endl;
>  		return TestFail;
>

Patch

diff --git a/src/libcamera/include/v4l2_pixelformat.h b/src/libcamera/include/v4l2_pixelformat.h
index 4d277569cb8c..0fe8a017de34 100644
--- a/src/libcamera/include/v4l2_pixelformat.h
+++ b/src/libcamera/include/v4l2_pixelformat.h
@@ -36,6 +36,10 @@  public:
 
 	std::string toString() const;
 
+	PixelFormat toPixelFormat() const;
+	static V4L2PixelFormat fromPixelFormat(const PixelFormat &pixelFormat,
+					       bool multiplanar);
+
 private:
 	uint32_t fourcc_;
 };
diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h
index ff64bb357c7e..982fee6bf740 100644
--- a/src/libcamera/include/v4l2_videodevice.h
+++ b/src/libcamera/include/v4l2_videodevice.h
@@ -210,10 +210,7 @@  public:
 	static V4L2VideoDevice *fromEntityName(const MediaDevice *media,
 					       const std::string &entity);
 
-	static PixelFormat toPixelFormat(V4L2PixelFormat v4l2Fourcc);
 	V4L2PixelFormat toV4L2PixelFormat(const PixelFormat &pixelFormat);
-	static V4L2PixelFormat toV4L2PixelFormat(const PixelFormat &pixelFormat,
-						 bool multiplanar);
 
 protected:
 	std::string logPrefix() const;
diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
index 030ac6864752..455693264c2e 100644
--- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
+++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
@@ -166,7 +166,7 @@  CameraConfiguration *PipelineHandlerUVC::generateConfiguration(Camera *camera,
 		       std::inserter(deviceFormats, deviceFormats.begin()),
 		       [&](const decltype(v4l2Formats)::value_type &format) {
 			       return decltype(deviceFormats)::value_type{
-				       data->video_->toPixelFormat(format.first),
+				       format.first.toPixelFormat(),
 				       format.second
 			       };
 		       });
diff --git a/src/libcamera/v4l2_pixelformat.cpp b/src/libcamera/v4l2_pixelformat.cpp
index 57d65c380b0d..876f5de321f6 100644
--- a/src/libcamera/v4l2_pixelformat.cpp
+++ b/src/libcamera/v4l2_pixelformat.cpp
@@ -103,4 +103,138 @@  std::string V4L2PixelFormat::toString() const
 	return ss;
 }
 
+/**
+ * \brief Convert the V4L2 pixel format to the corresponding PixelFormat
+ * \return The PixelFormat corresponding to the V4L2 pixel format
+ */
+PixelFormat V4L2PixelFormat::toPixelFormat() const
+{
+	switch (fourcc_) {
+	/* RGB formats. */
+	case V4L2_PIX_FMT_RGB24:
+		return PixelFormat(DRM_FORMAT_BGR888);
+	case V4L2_PIX_FMT_BGR24:
+		return PixelFormat(DRM_FORMAT_RGB888);
+	case V4L2_PIX_FMT_RGBA32:
+		return PixelFormat(DRM_FORMAT_ABGR8888);
+	case V4L2_PIX_FMT_ABGR32:
+		return PixelFormat(DRM_FORMAT_ARGB8888);
+	case V4L2_PIX_FMT_ARGB32:
+		return PixelFormat(DRM_FORMAT_BGRA8888);
+	case V4L2_PIX_FMT_BGRA32:
+		return PixelFormat(DRM_FORMAT_RGBA8888);
+
+	/* YUV packed formats. */
+	case V4L2_PIX_FMT_YUYV:
+		return PixelFormat(DRM_FORMAT_YUYV);
+	case V4L2_PIX_FMT_YVYU:
+		return PixelFormat(DRM_FORMAT_YVYU);
+	case V4L2_PIX_FMT_UYVY:
+		return PixelFormat(DRM_FORMAT_UYVY);
+	case V4L2_PIX_FMT_VYUY:
+		return PixelFormat(DRM_FORMAT_VYUY);
+
+	/* YUY planar formats. */
+	case V4L2_PIX_FMT_NV16:
+	case V4L2_PIX_FMT_NV16M:
+		return PixelFormat(DRM_FORMAT_NV16);
+	case V4L2_PIX_FMT_NV61:
+	case V4L2_PIX_FMT_NV61M:
+		return PixelFormat(DRM_FORMAT_NV61);
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV12M:
+		return PixelFormat(DRM_FORMAT_NV12);
+	case V4L2_PIX_FMT_NV21:
+	case V4L2_PIX_FMT_NV21M:
+		return PixelFormat(DRM_FORMAT_NV21);
+
+	/* Greyscale formats. */
+	case V4L2_PIX_FMT_GREY:
+		return PixelFormat(DRM_FORMAT_R8);
+
+	/* Compressed formats. */
+	case V4L2_PIX_FMT_MJPEG:
+		return PixelFormat(DRM_FORMAT_MJPEG);
+
+	/* V4L2 formats not yet supported by DRM. */
+	default:
+		LOG(V4L2, Warning)
+			<< "Unsupported V4L2 pixel format "
+			<< toString();
+		return PixelFormat();
+	}
+}
+
+/**
+ * \brief Convert \a pixelFormat to its corresponding V4L2PixelFormat
+ * \param[in] pixelFormat The PixelFormat to convert
+ * \param[in] multiplanar V4L2 Multiplanar API support flag
+ *
+ * Multiple V4L2 formats may exist for one PixelFormat when the format uses
+ * multiple planes, as V4L2 defines separate 4CCs for contiguous and separate
+ * planes formats. Set the \a multiplanar parameter to false to select a format
+ * with contiguous planes, or to true to select a format with non-contiguous
+ * planes.
+ *
+ * \return The V4L2PixelFormat corresponding to \a pixelFormat
+ */
+V4L2PixelFormat V4L2PixelFormat::fromPixelFormat(const PixelFormat &pixelFormat,
+						 bool multiplanar)
+{
+	switch (pixelFormat) {
+	/* RGB formats. */
+	case DRM_FORMAT_BGR888:
+		return V4L2PixelFormat(V4L2_PIX_FMT_RGB24);
+	case DRM_FORMAT_RGB888:
+		return V4L2PixelFormat(V4L2_PIX_FMT_BGR24);
+	case DRM_FORMAT_ABGR8888:
+		return V4L2PixelFormat(V4L2_PIX_FMT_RGBA32);
+	case DRM_FORMAT_ARGB8888:
+		return V4L2PixelFormat(V4L2_PIX_FMT_ABGR32);
+	case DRM_FORMAT_BGRA8888:
+		return V4L2PixelFormat(V4L2_PIX_FMT_ARGB32);
+	case DRM_FORMAT_RGBA8888:
+		return V4L2PixelFormat(V4L2_PIX_FMT_BGRA32);
+
+	/* YUV packed formats. */
+	case DRM_FORMAT_YUYV:
+		return V4L2PixelFormat(V4L2_PIX_FMT_YUYV);
+	case DRM_FORMAT_YVYU:
+		return V4L2PixelFormat(V4L2_PIX_FMT_YVYU);
+	case DRM_FORMAT_UYVY:
+		return V4L2PixelFormat(V4L2_PIX_FMT_UYVY);
+	case DRM_FORMAT_VYUY:
+		return V4L2PixelFormat(V4L2_PIX_FMT_VYUY);
+
+	/*
+	 * YUY planar formats.
+	 * \todo Add support for non-contiguous memory planes
+	 * \todo Select the format variant not only based on \a multiplanar but
+	 * also take into account the formats supported by the device.
+	 */
+	case DRM_FORMAT_NV16:
+		return V4L2PixelFormat(V4L2_PIX_FMT_NV16);
+	case DRM_FORMAT_NV61:
+		return V4L2PixelFormat(V4L2_PIX_FMT_NV61);
+	case DRM_FORMAT_NV12:
+		return V4L2PixelFormat(V4L2_PIX_FMT_NV12);
+	case DRM_FORMAT_NV21:
+		return V4L2PixelFormat(V4L2_PIX_FMT_NV21);
+
+	/* Greyscale formats. */
+	case DRM_FORMAT_R8:
+		return V4L2PixelFormat(V4L2_PIX_FMT_GREY);
+
+	/* Compressed formats. */
+	case DRM_FORMAT_MJPEG:
+		return V4L2PixelFormat(V4L2_PIX_FMT_MJPEG);
+
+	default:
+		LOG(V4L2, Warning)
+			<< "Unsupported pixel format "
+			<< pixelFormat.toString();
+		return V4L2PixelFormat();
+	}
+}
+
 } /* namespace libcamera */
diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
index 21df4f524212..e95b0c01cc85 100644
--- a/src/libcamera/v4l2_videodevice.cpp
+++ b/src/libcamera/v4l2_videodevice.cpp
@@ -18,7 +18,6 @@ 
 #include <unistd.h>
 #include <vector>
 
-#include <linux/drm_fourcc.h>
 #include <linux/version.h>
 
 #include <libcamera/event_notifier.h>
@@ -1634,74 +1633,6 @@  V4L2VideoDevice *V4L2VideoDevice::fromEntityName(const MediaDevice *media,
 	return new V4L2VideoDevice(mediaEntity);
 }
 
-/**
- * \brief Convert a \a v4l2Fourcc to the corresponding PixelFormat
- * \param[in] v4l2Fourcc The V4L2 pixel format (V4L2_PIX_FORMAT_*)
- * \return The PixelFormat corresponding to \a v4l2Fourcc
- */
-PixelFormat V4L2VideoDevice::toPixelFormat(V4L2PixelFormat v4l2Fourcc)
-{
-	switch (v4l2Fourcc) {
-	/* RGB formats. */
-	case V4L2_PIX_FMT_RGB24:
-		return PixelFormat(DRM_FORMAT_BGR888);
-	case V4L2_PIX_FMT_BGR24:
-		return PixelFormat(DRM_FORMAT_RGB888);
-	case V4L2_PIX_FMT_RGBA32:
-		return PixelFormat(DRM_FORMAT_ABGR8888);
-	case V4L2_PIX_FMT_ABGR32:
-		return PixelFormat(DRM_FORMAT_ARGB8888);
-	case V4L2_PIX_FMT_ARGB32:
-		return PixelFormat(DRM_FORMAT_BGRA8888);
-	case V4L2_PIX_FMT_BGRA32:
-		return PixelFormat(DRM_FORMAT_RGBA8888);
-
-	/* YUV packed formats. */
-	case V4L2_PIX_FMT_YUYV:
-		return PixelFormat(DRM_FORMAT_YUYV);
-	case V4L2_PIX_FMT_YVYU:
-		return PixelFormat(DRM_FORMAT_YVYU);
-	case V4L2_PIX_FMT_UYVY:
-		return PixelFormat(DRM_FORMAT_UYVY);
-	case V4L2_PIX_FMT_VYUY:
-		return PixelFormat(DRM_FORMAT_VYUY);
-
-	/* YUY planar formats. */
-	case V4L2_PIX_FMT_NV16:
-	case V4L2_PIX_FMT_NV16M:
-		return PixelFormat(DRM_FORMAT_NV16);
-	case V4L2_PIX_FMT_NV61:
-	case V4L2_PIX_FMT_NV61M:
-		return PixelFormat(DRM_FORMAT_NV61);
-	case V4L2_PIX_FMT_NV12:
-	case V4L2_PIX_FMT_NV12M:
-		return PixelFormat(DRM_FORMAT_NV12);
-	case V4L2_PIX_FMT_NV21:
-	case V4L2_PIX_FMT_NV21M:
-		return PixelFormat(DRM_FORMAT_NV21);
-
-	/* Greyscale formats. */
-	case V4L2_PIX_FMT_GREY:
-		return PixelFormat(DRM_FORMAT_R8);
-
-	/* Compressed formats. */
-	case V4L2_PIX_FMT_MJPEG:
-		return PixelFormat(DRM_FORMAT_MJPEG);
-
-	/* V4L2 formats not yet supported by DRM. */
-	default:
-		/*
-		 * \todo We can't use LOG() in a static method of a Loggable
-		 * class. Until we fix the logger, work around it.
-		 */
-		libcamera::_log(__FILE__, __LINE__, _LOG_CATEGORY(V4L2)(),
-				LogWarning).stream()
-			<< "Unsupported V4L2 pixel format "
-			<< v4l2Fourcc.toString();
-		return PixelFormat();
-	}
-}
-
 /**
  * \brief Convert \a PixelFormat to its corresponding V4L2 FourCC
  * \param[in] pixelFormat The PixelFormat to convert
@@ -1715,82 +1646,8 @@  PixelFormat V4L2VideoDevice::toPixelFormat(V4L2PixelFormat v4l2Fourcc)
  */
 V4L2PixelFormat V4L2VideoDevice::toV4L2PixelFormat(const PixelFormat &pixelFormat)
 {
-	return toV4L2PixelFormat(pixelFormat, caps_.isMultiplanar());
-}
-
-/**
- * \brief Convert \a pixelFormat to its corresponding V4L2 FourCC
- * \param[in] pixelFormat The PixelFormat to convert
- * \param[in] multiplanar V4L2 Multiplanar API support flag
- *
- * Multiple V4L2 formats may exist for one PixelFormat when the format uses
- * multiple planes, as V4L2 defines separate 4CCs for contiguous and separate
- * planes formats. Set the \a multiplanar parameter to false to select a format
- * with contiguous planes, or to true to select a format with non-contiguous
- * planes.
- *
- * \return The V4L2_PIX_FMT_* pixel format code corresponding to \a pixelFormat
- */
-V4L2PixelFormat V4L2VideoDevice::toV4L2PixelFormat(const PixelFormat &pixelFormat,
-						   bool multiplanar)
-{
-	switch (pixelFormat) {
-	/* RGB formats. */
-	case DRM_FORMAT_BGR888:
-		return V4L2PixelFormat(V4L2_PIX_FMT_RGB24);
-	case DRM_FORMAT_RGB888:
-		return V4L2PixelFormat(V4L2_PIX_FMT_BGR24);
-	case DRM_FORMAT_ABGR8888:
-		return V4L2PixelFormat(V4L2_PIX_FMT_RGBA32);
-	case DRM_FORMAT_ARGB8888:
-		return V4L2PixelFormat(V4L2_PIX_FMT_ABGR32);
-	case DRM_FORMAT_BGRA8888:
-		return V4L2PixelFormat(V4L2_PIX_FMT_ARGB32);
-	case DRM_FORMAT_RGBA8888:
-		return V4L2PixelFormat(V4L2_PIX_FMT_BGRA32);
-
-	/* YUV packed formats. */
-	case DRM_FORMAT_YUYV:
-		return V4L2PixelFormat(V4L2_PIX_FMT_YUYV);
-	case DRM_FORMAT_YVYU:
-		return V4L2PixelFormat(V4L2_PIX_FMT_YVYU);
-	case DRM_FORMAT_UYVY:
-		return V4L2PixelFormat(V4L2_PIX_FMT_UYVY);
-	case DRM_FORMAT_VYUY:
-		return V4L2PixelFormat(V4L2_PIX_FMT_VYUY);
-
-	/*
-	 * YUY planar formats.
-	 * \todo Add support for non-contiguous memory planes
-	 * \todo Select the format variant not only based on \a multiplanar but
-	 * also take into account the formats supported by the device.
-	 */
-	case DRM_FORMAT_NV16:
-		return V4L2PixelFormat(V4L2_PIX_FMT_NV16);
-	case DRM_FORMAT_NV61:
-		return V4L2PixelFormat(V4L2_PIX_FMT_NV61);
-	case DRM_FORMAT_NV12:
-		return V4L2PixelFormat(V4L2_PIX_FMT_NV12);
-	case DRM_FORMAT_NV21:
-		return V4L2PixelFormat(V4L2_PIX_FMT_NV21);
-
-	/* Greyscale formats. */
-	case DRM_FORMAT_R8:
-		return V4L2PixelFormat(V4L2_PIX_FMT_GREY);
-
-	/* Compressed formats. */
-	case DRM_FORMAT_MJPEG:
-		return V4L2PixelFormat(V4L2_PIX_FMT_MJPEG);
-	}
-
-	/*
-	 * \todo We can't use LOG() in a static method of a Loggable
-	 * class. Until we fix the logger, work around it.
-	 */
-	libcamera::_log(__FILE__, __LINE__, _LOG_CATEGORY(V4L2)(),
-			LogWarning).stream()
-		<< "Unsupported V4L2 pixel format " << pixelFormat.toString();
-	return {};
+	return V4L2PixelFormat::fromPixelFormat(pixelFormat,
+						caps_.isMultiplanar());
 }
 
 /**
diff --git a/test/libtest/buffer_source.cpp b/test/libtest/buffer_source.cpp
index dae3cb9f7a6c..d1dad2a0f8cf 100644
--- a/test/libtest/buffer_source.cpp
+++ b/test/libtest/buffer_source.cpp
@@ -70,8 +70,8 @@  int BufferSource::allocate(const StreamConfiguration &config)
 	}
 
 	format.size = config.size;
-	format.fourcc = V4L2VideoDevice::toV4L2PixelFormat(config.pixelFormat,
-							   false);
+	format.fourcc = V4L2PixelFormat::fromPixelFormat(config.pixelFormat,
+							 false);
 	if (video->setFormat(&format)) {
 		std::cout << "Failed to set format on output device" << std::endl;
 		return TestFail;