Files
fml13v01-buildroot/package/starfive/libcamera-apps/0008-In-DRM-preview-zoom-the-input-image-to-fit-the-scree.patch
T
zejian.su 9e675a54e3 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>
2023-12-06 16:04:11 +08:00

301 lines
8.9 KiB
Diff

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