From 9e675a54e34ac3fd1d8ed74e13c8b24d58931f9e Mon Sep 17 00:00:00 2001 From: "zejian.su" Date: Tue, 5 Dec 2023 10:41:46 +0800 Subject: [PATCH] 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 --- ...oom-the-input-image-to-fit-the-scree.patch | 300 ++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 package/starfive/libcamera-apps/0008-In-DRM-preview-zoom-the-input-image-to-fit-the-scree.patch diff --git a/package/starfive/libcamera-apps/0008-In-DRM-preview-zoom-the-input-image-to-fit-the-scree.patch b/package/starfive/libcamera-apps/0008-In-DRM-preview-zoom-the-input-image-to-fit-the-scree.patch new file mode 100644 index 00000000..c487d9d1 --- /dev/null +++ b/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" +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 +--- + 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 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 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 connector_; ++ std::unique_ptr 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(drmfd_, conId_, DRM_MODE_OBJECT_CONNECTOR); ++ crtc_ = std::make_unique(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 (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 span, StreamInfo const &info) +@@ -436,14 +553,7 @@ void DrmPreview::Show(int fd, libcamera::Span 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 +