Bläddra i källkod

In DRM preview, zoom the input image to fit the screen.

1. Use the atomic drm mode.
2. Find a mode in the connector with most closed area with the input image.
3. Set the plane property to scale the input image.

Signed-off-by: zejian.su <zejian.su@starfivetech.com>
zejian.su 5 månader sedan
förälder
incheckning
9e675a54e3

+ 300 - 0
package/starfive/libcamera-apps/0008-In-DRM-preview-zoom-the-input-image-to-fit-the-scree.patch

@@ -0,0 +1,300 @@
+From 526cbca855ea6310dc846cc05158d403aea456e1 Mon Sep 17 00:00:00 2001
+From: "zejian.su" <zejian.su@starfivetech.com>
+Date: Mon, 27 Nov 2023 18:02:17 +0800
+Subject: [PATCH 1/2] In DRM preview, zoom the input image to fit the screen.
+
+1. Use the atomic drm mode.
+2. Find a mode in the connector with most closed area with the input image.
+3. Set the plane property to scale the input image.
+
+Signed-off-by: zejian.su <zejian.su@starfivetech.com>
+---
+ preview/drm_preview.cpp | 184 ++++++++++++++++++++++++++++++++--------
+ 1 file changed, 147 insertions(+), 37 deletions(-)
+
+diff --git a/preview/drm_preview.cpp b/preview/drm_preview.cpp
+index 38fc6a2..405bdca 100644
+--- a/preview/drm_preview.cpp
++++ b/preview/drm_preview.cpp
+@@ -17,6 +17,92 @@
+ 
+ #include "preview.hpp"
+ 
++class DRMObject
++{
++public:
++	DRMObject(int drmfd, uint32_t id, uint32_t type) 
++		: id_(id), type_(type){
++		drmModeObjectProperties *properties = drmModeObjectGetProperties(drmfd, id, type);
++		if (!properties)
++			return;
++
++		drmModePropertyPtr property;
++		for (uint32_t i = 0; i < properties->count_props; i++) {
++			property = drmModeGetProperty(drmfd, properties->props[i]);
++			property_[std::string(property->name)] = property->prop_id;
++			drmModeFreeProperty(property);
++		}
++
++		drmModeFreeObjectProperties(properties);
++	}
++
++	uint32_t getID() const {return id_;};
++
++public:
++	std::unordered_map<std::string, uint32_t> property_;
++
++private:
++	uint32_t id_;
++	uint32_t type_;
++};
++
++class AtomicRequest
++{
++public:
++	enum Flags {
++		FlagAllowModeset = (1 << 0),
++		FlagAsync = (1 << 1),
++		FlagTestOnly = (1 << 2),
++	};
++
++	AtomicRequest() : valid_(true) {
++		request_ = drmModeAtomicAlloc();
++		if (!request_)
++			valid_ = false;
++	}
++
++	~AtomicRequest() {
++		if (request_)
++			drmModeAtomicFree(request_);
++	}
++
++	bool isValid() const { return valid_; }
++
++	int addProperty(int drmfd, const DRMObject *object, const std::string &propertyName,
++			uint64_t value) {
++
++		if (!valid_)
++			return -EINVAL;
++		
++		auto it = object->property_.find(propertyName);
++		if(it == object->property_.end())
++			return -EINVAL;
++
++		if(drmModeAtomicAddProperty(request_, object->getID(), it->second, value) < 0) {
++			std::string errMsg = "drmModeAtomicAddProperty() failed -- object: " +
++				std::to_string(object->getID()) + ", property: " + std::to_string(it->second);
++			throw std::runtime_error(errMsg);
++		}
++
++		return 0;
++	}
++	
++	int commit(int drmfd, unsigned int flags = 0) {
++		if(drmModeAtomicCommit(drmfd, request_, flags, NULL) < 0)
++			throw std::runtime_error("drmModeAtomicCommit() failed");
++		return 0;
++	}
++
++private:
++	AtomicRequest(const AtomicRequest &) = delete;
++	AtomicRequest(const AtomicRequest &&) = delete;
++	AtomicRequest &operator=(const AtomicRequest &) = delete;
++	AtomicRequest &operator=(const AtomicRequest &&) = delete;
++
++	bool valid_;
++	drmModeAtomicReq *request_;
++};
++
+ class DrmPreview : public Preview
+ {
+ public:
+@@ -48,6 +134,7 @@ private:
+ 	void makeBuffer(int fd, size_t size, StreamInfo const &info, Buffer &buffer);
+ 	void findCrtc();
+ 	void findPlane();
++	void fitDisplaySize();
+ 	int drmfd_;
+ 	int conId_;
+ 	drmModeConnector *con_;
+@@ -61,11 +148,18 @@ private:
+ 	unsigned int height_;
+ 	unsigned int screen_width_;
+ 	unsigned int screen_height_;
++	unsigned int display_x_;
++	unsigned int display_y_;
++	unsigned int display_w_;
++	unsigned int display_h_;
+ 	std::map<int, Buffer> buffers_; // map the DMABUF's fd to the Buffer
+ 	int last_fd_;
+ 	unsigned int max_image_width_;
+ 	unsigned int max_image_height_;
+ 	bool first_time_;
++
++	std::unique_ptr<DRMObject> connector_;
++	std::unique_ptr<DRMObject> crtc_;
+ };
+ 
+ #define ERRSTR strerror(errno)
+@@ -96,10 +190,6 @@ void DrmPreview::findCrtc()
+ 			if (con->encoder_id)
+ 			{
+ 				enc = drmModeGetEncoder(drmfd_, con->encoder_id);
+-				//if (enc->crtc_id)
+-				//{
+-				//	crtc = drmModeGetCrtc(drmfd_, enc->crtc_id);
+-				//}
+ 			} else
+ 			{
+ 				enc = drmModeGetEncoder(drmfd_, con->encoders[0]);
+@@ -229,6 +319,7 @@ void DrmPreview::findPlane()
+ 			drmModeFreePlane(plane);
+ 			break;
+ 		}
++		planeId_ = planes->planes[0];
+ 	}
+ 	catch (std::exception const &e)
+ 	{
+@@ -265,21 +356,16 @@ DrmPreview::DrmPreview(Options const *options) : Preview(options), last_fd_(-1),
+ 		//out_fourcc_ = DRM_FORMAT_YUV420;
+ 		out_fourcc_ = DRM_FORMAT_NV12;
+ 		findPlane();
++
++		drmSetClientCap(drmfd_, DRM_CLIENT_CAP_ATOMIC, 1);
++		connector_ = std::make_unique<DRMObject>(drmfd_, conId_, DRM_MODE_OBJECT_CONNECTOR);
++		crtc_ = std::make_unique<DRMObject>(drmfd_, crtcId_, DRM_MODE_OBJECT_CRTC);
+ 	}
+ 	catch (std::exception const &e)
+ 	{
+ 		close(drmfd_);
+ 		throw;
+ 	}
+-
+-	// Default behaviour here is to go fullscreen.
+-	if (options_->fullscreen || width_ == 0 || height_ == 0 || x_ + width_ > screen_width_ ||
+-		y_ + height_ > screen_height_)
+-	{
+-		x_ = y_ = 0;
+-		width_ = screen_width_;
+-		height_ = screen_height_;
+-	}
+ }
+ 
+ DrmPreview::~DrmPreview()
+@@ -365,9 +451,23 @@ static void setup_colour_space(int fd, int plane_id, std::optional<libcamera::Co
+ 	drm_set_property(fd, plane_id, "COLOR_RANGE", range);
+ }
+ 
++void DrmPreview::fitDisplaySize()
++{
++	if((int64_t)width_ * screen_height_ > (int64_t)screen_width_ * height_) {
++		display_x_ = 0, display_w_ = screen_width_;
++		display_h_ = ((int64_t)screen_width_ * height_) / (int64_t)width_;
++		display_y_ = (screen_height_ - display_h_) >> 1;
++	} else {
++		display_y_ = 0, display_h_ = screen_height_;
++		display_w_ = ((int64_t)width_ * screen_height_) / (int64_t)height_;
++		display_x_ = (screen_width_ - display_w_) >> 1;
++	}
++}
++
+ void DrmPreview::makeBuffer(int fd, size_t size, StreamInfo const &info, Buffer &buffer)
+ {
+ 	unsigned int width = info.width, height = info.height, stride = info.stride;
++	int64_t targetArea = 0;
+ 	if (first_time_)
+ 	{
+ 		first_time_ = false;
+@@ -379,6 +479,10 @@ void DrmPreview::makeBuffer(int fd, size_t size, StreamInfo const &info, Buffer
+ 	buffer.size = size;
+ 	buffer.info = info;
+ 
++	if(!width_ || !height)
++		width_ = info.width, height_ = info.height;
++	targetArea = (int64_t)width_ * height_;
++
+ 	if (drmPrimeFDToHandle(drmfd_, fd, &buffer.bo_handle))
+ 		throw std::runtime_error("drmPrimeFDToHandle failed for fd " + std::to_string(fd));
+ 
+@@ -401,33 +505,46 @@ void DrmPreview::makeBuffer(int fd, size_t size, StreamInfo const &info, Buffer
+ 
+ 	/* find preferred mode */
+ 	drmModeModeInfo *modeptr = NULL, *preferred = NULL;
++	int64_t minDiff = -1;
+ 	for (int m = 0; m < con_->count_modes; m++) {
++		int64_t area, areaDiff;
+ 		modeptr = &con_->modes[m];
+-		if (modeptr->hdisplay == width && modeptr->vdisplay == height) {
++
++		area = (int64_t)modeptr->hdisplay * modeptr->vdisplay;
++		areaDiff = abs(targetArea - area);
++		if(-1 == minDiff)
++			minDiff = abs(targetArea - area), preferred = modeptr;
++		else {
++			if(areaDiff < minDiff)
++				minDiff = areaDiff, preferred = modeptr;
++		}
++
++		if (!minDiff) {
+ 			preferred = modeptr;
+ 			std::cout << "find the matched mode, modes index= "
+ 				<< m << ", " << width << "x" << height << std::endl;
+ 			break;
+ 		}
+-		if (modeptr->type & DRM_MODE_TYPE_PREFERRED) {
+-			preferred = modeptr;
+-			std::cout << "find perferred mode, modes index= " << m << std::endl;
+-		}
+-	}
+-
+-	if (!preferred)
+-		preferred = &con_->modes[0];
+-
+-	// set default
+-	if (drmModeSetCrtc(drmfd_, crtcId_, buffer.fb_handle, 0, 0,
+-	        (uint32_t *)&conId_, 1, preferred)) {
+-		throw std::runtime_error("drmModeSetCrtc() failed");
+ 	}
+ 
+ 	screen_width_ = preferred->hdisplay;
+ 	screen_height_ = preferred->vdisplay;
+-	width_ = width;
+-	height_ = height;
++	fitDisplaySize();
++
++	AtomicRequest ar;
++	uint32_t blob_id;
++
++	drmModeCreatePropertyBlob(drmfd_, preferred, sizeof(con_->modes[0]), &blob_id);
++	ar.addProperty(drmfd_, connector_.get(), "CRTC_ID", crtcId_);
++	ar.addProperty(drmfd_, crtc_.get(), "ACTIVE", 1);
++	ar.addProperty(drmfd_, crtc_.get(), "MODE_ID", blob_id);
++	ar.commit(drmfd_, DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_ATOMIC_ALLOW_MODESET);
++
++	// set default
++	//if (drmModeSetCrtc(drmfd_, crtcId_, buffer.fb_handle, 0, 0,
++	//        (uint32_t *)&conId_, 1, preferred)) {
++	//	throw std::runtime_error("drmModeSetCrtc() failed");
++	//}
+ }
+ 
+ void DrmPreview::Show(int fd, libcamera::Span<uint8_t> span, StreamInfo const &info)
+@@ -436,14 +553,7 @@ void DrmPreview::Show(int fd, libcamera::Span<uint8_t> span, StreamInfo const &i
+ 	if (buffer.fd == -1)
+ 		makeBuffer(fd, span.size(), info, buffer);
+ 
+-	unsigned int x_off = 0, y_off = 0;
+-	unsigned int w = width_, h = height_;
+-	if (info.width * height_ > width_ * info.height)
+-		h = width_ * info.height / info.width, y_off = (height_ - h) / 2;
+-	else
+-		w = height_ * info.width / info.height, x_off = (width_ - w) / 2;
+-
+-	if (drmModeSetPlane(drmfd_, planeId_, crtcId_, buffer.fb_handle, 0, x_off + x_, y_off + y_, w, h, 0, 0,
++	if (drmModeSetPlane(drmfd_, planeId_, crtcId_, buffer.fb_handle, 0, display_x_, display_y_, display_w_, display_h_, 0, 0,
+ 						buffer.info.width << 16, buffer.info.height << 16))
+ 		throw std::runtime_error("drmModeSetPlane failed: " + std::string(ERRSTR));
+ 	if (last_fd_ >= 0)
+-- 
+2.34.1
+