Merge branch 'CR_8516_fix_qt_window_zejian.su' into 'jh7110-master'

CR_8516: Fix the qt window size and simplify the qt ping-pang buffer

See merge request sdk/buildroot!158
This commit is contained in:
andy.hu
2023-12-21 13:56:16 +00:00
11 changed files with 3 additions and 10095 deletions
@@ -1,284 +0,0 @@
From 2982813d58c13077c06d1c3c44cd60b0b93d1f4e Mon Sep 17 00:00:00 2001
From: "zejian.su" <zejian.su@starfivetech.com>
Date: Thu, 12 Oct 2023 11:54:09 +0800
Subject: [PATCH 1/4] Make libcamera-hello run normally
---
core/libcamera_app.cpp | 53 ++++++++++++++------------
preview/drm_preview.cpp | 84 +++++++++++++++++++++++++++++++++++------
2 files changed, 100 insertions(+), 37 deletions(-)
diff --git a/core/libcamera_app.cpp b/core/libcamera_app.cpp
index d3a6b63..cc76e9b 100644
--- a/core/libcamera_app.cpp
+++ b/core/libcamera_app.cpp
@@ -247,23 +247,24 @@ void LibcameraApp::ConfigureViewfinder()
if (!configuration_)
throw std::runtime_error("failed to generate viewfinder configuration");
- Size size(1280, 960);
- auto area = camera_->properties().get(properties::PixelArrayActiveAreas);
+ //Size size(1280, 960);
+ Size size = configuration_->at(0).size;
+ //auto area = camera_->properties().get(properties::PixelArrayActiveAreas);
if (options_->viewfinder_width && options_->viewfinder_height)
size = Size(options_->viewfinder_width, options_->viewfinder_height);
- else if (area)
- {
- // The idea here is that most sensors will have a 2x2 binned mode that
- // we can pick up. If it doesn't, well, you can always specify the size
- // you want exactly with the viewfinder_width/height options_->
- size = (*area)[0].size() / 2;
- // If width and height were given, we might be switching to capture
- // afterwards - so try to match the field of view.
- if (options_->width && options_->height)
- size = size.boundedToAspectRatio(Size(options_->width, options_->height));
- size.alignDownTo(2, 2); // YUV420 will want to be even
- LOG(2, "Viewfinder size chosen is " << size.toString());
- }
+ //else if (area)
+ //{
+ // // The idea here is that most sensors will have a 2x2 binned mode that
+ // // we can pick up. If it doesn't, well, you can always specify the size
+ // // you want exactly with the viewfinder_width/height options_->
+ // size = (*area)[0].size() / 2;
+ // // If width and height were given, we might be switching to capture
+ // // afterwards - so try to match the field of view.
+ // if (options_->width && options_->height)
+ // size = size.boundedToAspectRatio(Size(options_->width, options_->height));
+ // size.alignDownTo(2, 2); // YUV420 will want to be even
+ // LOG(2, "Viewfinder size chosen is " << size.toString());
+ //}
// Finally trim the image size to the largest that the preview can handle.
Size max_size;
@@ -275,7 +276,7 @@ void LibcameraApp::ConfigureViewfinder()
}
// Now we get to override any of the default settings from the options_->
- configuration_->at(0).pixelFormat = libcamera::formats::YUV420;
+ //configuration_->at(0).pixelFormat = libcamera::formats::YUV420;
configuration_->at(0).size = size;
if (options_->viewfinder_buffer_count > 0)
configuration_->at(0).bufferCount = options_->viewfinder_buffer_count;
@@ -286,7 +287,7 @@ void LibcameraApp::ConfigureViewfinder()
lores_size.alignDownTo(2, 2);
if (lores_size.width > size.width || lores_size.height > size.height)
throw std::runtime_error("Low res image larger than viewfinder");
- configuration_->at(lores_stream_num).pixelFormat = libcamera::formats::YUV420;
+ //configuration_->at(lores_stream_num).pixelFormat = libcamera::formats::YUV420;
configuration_->at(lores_stream_num).size = lores_size;
configuration_->at(lores_stream_num).bufferCount = configuration_->at(0).bufferCount;
}
@@ -297,7 +298,7 @@ void LibcameraApp::ConfigureViewfinder()
if (have_raw_stream)
{
configuration_->at(raw_stream_num).size = options_->viewfinder_mode.Size();
- configuration_->at(raw_stream_num).pixelFormat = mode_to_pixel_format(options_->viewfinder_mode);
+ //configuration_->at(raw_stream_num).pixelFormat = mode_to_pixel_format(options_->viewfinder_mode);
configuration_->at(raw_stream_num).bufferCount = configuration_->at(0).bufferCount;
}
@@ -335,8 +336,9 @@ void LibcameraApp::ConfigureStill(unsigned int flags)
configuration_->at(0).pixelFormat = libcamera::formats::BGR888;
else if (flags & FLAG_STILL_RGB)
configuration_->at(0).pixelFormat = libcamera::formats::RGB888;
- else
- configuration_->at(0).pixelFormat = libcamera::formats::YUV420;
+ else {
+ //configuration_->at(0).pixelFormat = libcamera::formats::YUV420;
+ }
if ((flags & FLAG_STILL_BUFFER_MASK) == FLAG_STILL_DOUBLE_BUFFER)
configuration_->at(0).bufferCount = 2;
else if ((flags & FLAG_STILL_BUFFER_MASK) == FLAG_STILL_TRIPLE_BUFFER)
@@ -358,8 +360,8 @@ void LibcameraApp::ConfigureStill(unsigned int flags)
configuration_->at(1).pixelFormat = mode_to_pixel_format(options_->mode);
}
configuration_->at(1).bufferCount = configuration_->at(0).bufferCount;
-
configureDenoise(options_->denoise == "auto" ? "cdn_hq" : options_->denoise);
+
setupCapture();
streams_["still"] = configuration_->at(0).stream();
@@ -392,7 +394,7 @@ void LibcameraApp::ConfigureVideo(unsigned int flags)
// Now we get to override any of the default settings from the options_->
StreamConfiguration &cfg = configuration_->at(0);
- cfg.pixelFormat = libcamera::formats::YUV420;
+ //cfg.pixelFormat = libcamera::formats::YUV420;
cfg.bufferCount = 6; // 6 buffers is better than 4
if (options_->buffer_count > 0)
cfg.bufferCount = options_->buffer_count;
@@ -431,7 +433,7 @@ void LibcameraApp::ConfigureVideo(unsigned int flags)
if (lores_size.width > configuration_->at(0).size.width ||
lores_size.height > configuration_->at(0).size.height)
throw std::runtime_error("Low res image larger than video");
- configuration_->at(lores_index).pixelFormat = libcamera::formats::YUV420;
+ //configuration_->at(lores_index).pixelFormat = libcamera::formats::YUV420;
configuration_->at(lores_index).size = lores_size;
configuration_->at(lores_index).bufferCount = configuration_->at(0).bufferCount;
}
@@ -959,8 +961,9 @@ void LibcameraApp::previewThread()
preview_cond_var_.wait(lock);
}
- if (item.stream->configuration().pixelFormat != libcamera::formats::YUV420)
- throw std::runtime_error("Preview windows only support YUV420");
+ if (item.stream->configuration().pixelFormat != libcamera::formats::YUV420 &&
+ item.stream->configuration().pixelFormat != libcamera::formats::NV12)
+ throw std::runtime_error("Preview windows only support YUV420 and NV12");
StreamInfo info = GetStreamInfo(item.stream);
FrameBuffer *buffer = item.completed_request->buffers[item.stream];
diff --git a/preview/drm_preview.cpp b/preview/drm_preview.cpp
index 4d6cf0d..38fc6a2 100644
--- a/preview/drm_preview.cpp
+++ b/preview/drm_preview.cpp
@@ -10,6 +10,8 @@
#include <drm_mode.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
+#include <fcntl.h>
+#include <libcamera/formats.h>
#include "core/options.hpp"
@@ -48,6 +50,7 @@ private:
void findPlane();
int drmfd_;
int conId_;
+ drmModeConnector *con_;
uint32_t crtcId_;
int crtcIdx_;
uint32_t planeId_;
@@ -93,10 +96,21 @@ void DrmPreview::findCrtc()
if (con->encoder_id)
{
enc = drmModeGetEncoder(drmfd_, con->encoder_id);
- if (enc->crtc_id)
- {
- crtc = drmModeGetCrtc(drmfd_, enc->crtc_id);
- }
+ //if (enc->crtc_id)
+ //{
+ // crtc = drmModeGetCrtc(drmfd_, enc->crtc_id);
+ //}
+ } else
+ {
+ enc = drmModeGetEncoder(drmfd_, con->encoders[0]);
+ }
+
+ if (enc->crtc_id)
+ {
+ crtc = drmModeGetCrtc(drmfd_, enc->crtc_id);
+ } else
+ {
+ crtc = drmModeGetCrtc(drmfd_, res->crtcs[0]);
}
if (!conId_ && crtc)
@@ -158,6 +172,8 @@ void DrmPreview::findCrtc()
throw std::runtime_error("connector supports no mode");
}
+ con_ = c;
+
if (options_->fullscreen || width_ == 0 || height_ == 0)
{
drmModeCrtc *crtc = drmModeGetCrtc(drmfd_, crtcId_);
@@ -225,10 +241,13 @@ void DrmPreview::findPlane()
DrmPreview::DrmPreview(Options const *options) : Preview(options), last_fd_(-1), first_time_(true)
{
- drmfd_ = drmOpen("vc4", NULL);
+ //drmfd_ = drmOpen("vc4", NULL);
+ drmfd_ = drmOpen("starfive", NULL);
if (drmfd_ < 0)
throw std::runtime_error("drmOpen failed: " + std::string(ERRSTR));
+ drmSetClientCap(drmfd_, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+
x_ = options_->preview_x;
y_ = options_->preview_y;
width_ = options_->preview_width;
@@ -243,7 +262,8 @@ DrmPreview::DrmPreview(Options const *options) : Preview(options), last_fd_(-1),
conId_ = 0;
findCrtc();
- out_fourcc_ = DRM_FORMAT_YUV420;
+ //out_fourcc_ = DRM_FORMAT_YUV420;
+ out_fourcc_ = DRM_FORMAT_NV12;
findPlane();
}
catch (std::exception const &e)
@@ -347,6 +367,7 @@ static void setup_colour_space(int fd, int plane_id, std::optional<libcamera::Co
void DrmPreview::makeBuffer(int fd, size_t size, StreamInfo const &info, Buffer &buffer)
{
+ unsigned int width = info.width, height = info.height, stride = info.stride;
if (first_time_)
{
first_time_ = false;
@@ -361,13 +382,52 @@ void DrmPreview::makeBuffer(int fd, size_t size, StreamInfo const &info, Buffer
if (drmPrimeFDToHandle(drmfd_, fd, &buffer.bo_handle))
throw std::runtime_error("drmPrimeFDToHandle failed for fd " + std::to_string(fd));
- uint32_t offsets[4] =
- { 0, info.stride * info.height, info.stride * info.height + (info.stride / 2) * (info.height / 2) };
- uint32_t pitches[4] = { info.stride, info.stride / 2, info.stride / 2 };
- uint32_t bo_handles[4] = { buffer.bo_handle, buffer.bo_handle, buffer.bo_handle };
-
- if (drmModeAddFB2(drmfd_, info.width, info.height, out_fourcc_, bo_handles, pitches, offsets, &buffer.fb_handle, 0))
+ if (out_fourcc_ == DRM_FORMAT_YUV420) {
+ uint32_t offsets[4] = { 0, stride * height, stride * height + (stride / 2) * (height / 2) };
+ uint32_t pitches[4] = { stride, stride / 2, stride / 2 };
+ uint32_t bo_handles[4] = { buffer.bo_handle, buffer.bo_handle, buffer.bo_handle };
+
+ if (drmModeAddFB2(drmfd_, width, height, out_fourcc_, bo_handles, pitches, offsets, &buffer.fb_handle, 0))
+ throw std::runtime_error("YUV420 drmModeAddFB2 failed: " + std::string(ERRSTR));
+ } else if (out_fourcc_ == DRM_FORMAT_NV12 || out_fourcc_ == DRM_FORMAT_NV21) {
+ uint32_t offsets[4] = { 0, stride * height};
+ uint32_t pitches[4] = { stride, stride};
+ uint32_t bo_handles[4] = { buffer.bo_handle, buffer.bo_handle};
+
+ if (drmModeAddFB2(drmfd_, width, height, out_fourcc_, bo_handles, pitches, offsets, &buffer.fb_handle, 0))
+ throw std::runtime_error("NV12/21 drmModeAddFB2 failed: " + std::string(ERRSTR));
+ } else
throw std::runtime_error("drmModeAddFB2 failed: " + std::string(ERRSTR));
+
+ /* find preferred mode */
+ drmModeModeInfo *modeptr = NULL, *preferred = NULL;
+ for (int m = 0; m < con_->count_modes; m++) {
+ modeptr = &con_->modes[m];
+ if (modeptr->hdisplay == width && modeptr->vdisplay == height) {
+ 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;
}
void DrmPreview::Show(int fd, libcamera::Span<uint8_t> span, StreamInfo const &info)
--
2.34.1
@@ -1,222 +0,0 @@
From d02d3b3a6c0abdfd8a89ee92447df7d8d48639d5 Mon Sep 17 00:00:00 2001
From: "zejian.su" <zejian.su@starfivetech.com>
Date: Thu, 12 Oct 2023 11:55:47 +0800
Subject: [PATCH 2/4] Make libcamera-jpeg run normally
---
apps/libcamera_jpeg.cpp | 28 +++++----
core/CMakeLists.txt | 2 +-
core/cvt_color_format.cpp | 119 ++++++++++++++++++++++++++++++++++++++
image/jpeg.cpp | 1 +
4 files changed, 134 insertions(+), 16 deletions(-)
create mode 100755 core/cvt_color_format.cpp
diff --git a/apps/libcamera_jpeg.cpp b/apps/libcamera_jpeg.cpp
index 05a632b..b791960 100644
--- a/apps/libcamera_jpeg.cpp
+++ b/apps/libcamera_jpeg.cpp
@@ -15,6 +15,8 @@
using namespace std::placeholders;
using libcamera::Stream;
+void NV12ToYUV420(const libcamera::Span<uint8_t> &mem, std::vector<libcamera::Span<uint8_t>> &yuv420, StreamInfo &info);
+
class LibcameraJpegApp : public LibcameraApp
{
public:
@@ -61,10 +63,19 @@ static void event_loop(LibcameraJpegApp &app)
auto now = std::chrono::high_resolution_clock::now();
if (options->timeout && now - start_time > std::chrono::milliseconds(options->timeout))
{
+ Stream *stream = app.ViewfinderStream();
+ StreamInfo info = app.GetStreamInfo(stream);
+ CompletedRequestPtr &payload = std::get<CompletedRequestPtr>(msg.payload);
+ const std::vector<libcamera::Span<uint8_t>> mem = app.Mmap(payload->buffers[stream]);
+ std::vector<libcamera::Span<uint8_t>> yuv420;
+
+ NV12ToYUV420(mem[0], yuv420, info);
app.StopCamera();
app.Teardown();
- app.ConfigureStill();
- app.StartCamera();
+
+ jpeg_save(yuv420, info, payload->metadata, options->output, app.CameraModel(), options);
+ delete[] yuv420[0].data();
+ return;
}
else
{
@@ -72,19 +83,6 @@ static void event_loop(LibcameraJpegApp &app)
app.ShowPreview(completed_request, app.ViewfinderStream());
}
}
- // In still capture mode, save a jpeg and quit.
- else if (app.StillStream())
- {
- app.StopCamera();
- LOG(1, "Still capture image received");
-
- Stream *stream = app.StillStream();
- StreamInfo info = app.GetStreamInfo(stream);
- CompletedRequestPtr &payload = std::get<CompletedRequestPtr>(msg.payload);
- const std::vector<libcamera::Span<uint8_t>> mem = app.Mmap(payload->buffers[stream]);
- jpeg_save(mem, info, payload->metadata, options->output, app.CameraModel(), options);
- return;
- }
}
}
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index b601538..5d9ee30 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -7,7 +7,7 @@ find_package(Boost REQUIRED COMPONENTS program_options)
add_custom_target(VersionCpp ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_SOURCE_DIR} -P ${CMAKE_CURRENT_LIST_DIR}/version.cmake)
set_source_files_properties(version.cpp PROPERTIES GENERATED 1)
-add_library(libcamera_app libcamera_app.cpp post_processor.cpp version.cpp options.cpp)
+add_library(libcamera_app libcamera_app.cpp post_processor.cpp version.cpp options.cpp cvt_color_format.cpp)
add_dependencies(libcamera_app VersionCpp)
set_target_properties(libcamera_app PROPERTIES PREFIX "" IMPORT_PREFIX "" VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR})
diff --git a/core/cvt_color_format.cpp b/core/cvt_color_format.cpp
new file mode 100755
index 0000000..9ff2b6b
--- /dev/null
+++ b/core/cvt_color_format.cpp
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2023, Starfive Technology Co., Ltd.
+ *
+ * cvt_color_format.cpp - convert other format to yuv420.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "core/still_options.hpp"
+
+void NV12ToYUV420(const uint8_t *mem, uint8_t *dst, const StreamInfo &info)
+{
+ const uint8_t *src = mem;
+ uint32_t width = info.width, height = info.height, stride = info.stride;
+ uint8_t *u = dst + height * stride;
+ uint8_t *v = u + ((height * width) >> 2);
+ uint32_t i;
+
+ memcpy(dst, src, height * stride);
+
+ src += height * stride;
+ for(i = 0; i < (height * width) >> 2; i++, src += 2)
+ u[i] = src[0], v[i] = src[1];
+}
+
+void NV12ToYUV420(const libcamera::Span<uint8_t> &mem, uint8_t *dst, const StreamInfo &info)
+{
+ NV12ToYUV420((const uint8_t *)(mem.data()), dst, info);
+/*
+ const uint8_t *src = mem.data();
+ uint32_t width = info.width, height = info.height, stride = info.stride;
+ uint8_t *u = dst + height * stride;
+ uint8_t *v = u + ((height * width) >> 2);
+ uint32_t i;
+
+ memcpy(dst, src, height * stride);
+
+ src += height * stride;
+ for(i = 0; i < (height * width) >> 2; i++, src += 2)
+ u[i] = src[0], v[i] = src[1];
+*/
+}
+
+void NV12ToYUV420(const libcamera::Span<uint8_t> &mem, std::vector<libcamera::Span<uint8_t>> &yuv420, StreamInfo &info)
+{
+ uint32_t imgBufSize = info.height * info.stride + ((info.height * info.width) >> 1);
+ uint8_t * yuv420Buf = new uint8_t[imgBufSize];
+
+ if(!yuv420Buf)
+ throw std::runtime_error("fail to apply memory");
+
+ yuv420.push_back(libcamera::Span<uint8_t>(yuv420Buf, imgBufSize));
+
+ NV12ToYUV420(mem, yuv420Buf, info);
+ info.pixel_format = libcamera::formats::YUV420;
+}
+
+template<int R>
+void NV12ToRGB888(const libcamera::Span<uint8_t> &mem, uint8_t *dst, const StreamInfo &info)
+{
+ uint32_t width = info.width, height = info.height, stride = info.stride;
+ const uint8_t *yRow = mem.data();
+ const uint8_t *uv = yRow + height * stride;
+ int ri = R ? 2 : 0;
+
+ for(uint32_t i = 0; i < height; i++, yRow += stride, dst += 3 * width)
+ {
+ const uint8_t * curUV = uv;
+ uint8_t * curDst = dst;
+ for(uint32_t j = 0; j < width; j++, curDst += 3)
+ {
+ int32_t y = (int32_t)yRow[j] - 16;
+ int32_t u = (int32_t)curUV[0] - 128;
+ int32_t v = (int32_t)curUV[1] - 128;
+ int32_t val;
+
+ val = (1192 * y + 2066 * u - v) >> 10;
+ curDst[ri] = val < 0 ? 0 : (val > 255 ? 255 : (uint8_t)val);
+ val = (1192 * y - 401 * u - 833 * v) >> 10;
+ curDst[1] = val < 0 ? 0 : (val > 255 ? 255 : (uint8_t)val);
+ val = (1192 * y - 2 * u + 1634 * v) >> 10;
+ curDst[2 - ri] = val < 0 ? 0 : (val > 255 ? 255 : (uint8_t)val);
+
+ if(j & 1)
+ curUV += 2;
+ }
+ if(i & 1)
+ uv += width;
+ }
+}
+
+template<int R>
+void RGB888FromNV12(const libcamera::Span<uint8_t> &mem, std::vector<libcamera::Span<uint8_t>> &rgb888, StreamInfo &info)
+{
+ uint32_t imgBufSize = info.height * info.width * 3;
+ uint8_t * rgb888Buf = new uint8_t[imgBufSize];
+
+ if(!rgb888Buf)
+ throw std::runtime_error("fail to apply memory");
+
+ rgb888.push_back(libcamera::Span<uint8_t>(rgb888Buf, imgBufSize));
+
+ NV12ToRGB888<R>(mem, rgb888Buf, info);
+ info.stride = 3 * info.width;
+}
+
+void NV12ToRGB888(const libcamera::Span<uint8_t> &mem, std::vector<libcamera::Span<uint8_t>> &rgb888, StreamInfo &info)
+{
+ RGB888FromNV12<0>(mem, rgb888, info);
+ info.pixel_format = libcamera::formats::RGB888;
+}
+
+void NV12ToBGR888(const libcamera::Span<uint8_t> &mem, std::vector<libcamera::Span<uint8_t>> &rgb888, StreamInfo &info)
+{
+ RGB888FromNV12<2>(mem, rgb888, info);
+ info.pixel_format = libcamera::formats::BGR888;
+}
\ No newline at end of file
diff --git a/image/jpeg.cpp b/image/jpeg.cpp
index e3516ef..872f5c6 100644
--- a/image/jpeg.cpp
+++ b/image/jpeg.cpp
@@ -447,6 +447,7 @@ static void create_exif_data(std::vector<libcamera::Span<uint8_t>> const &mem, S
exif = exif_data_new();
if (!exif)
throw std::runtime_error("failed to allocate EXIF data");
+
exif_data_set_byte_order(exif, exif_byte_order);
// First add some fixed EXIF tags.
--
2.34.1
@@ -1,204 +0,0 @@
From f79f0914c3f8e740cf653da0dd14e5664760188d Mon Sep 17 00:00:00 2001
From: "zejian.su" <zejian.su@starfivetech.com>
Date: Thu, 12 Oct 2023 11:56:48 +0800
Subject: [PATCH 3/4] Make libcamera-still run normally
---
apps/libcamera_still.cpp | 137 +++++++++++++++++++++++++--------------
image/dng.cpp | 1 -
2 files changed, 88 insertions(+), 50 deletions(-)
diff --git a/apps/libcamera_still.cpp b/apps/libcamera_still.cpp
index 856f1be..bd44818 100644
--- a/apps/libcamera_still.cpp
+++ b/apps/libcamera_still.cpp
@@ -74,22 +74,79 @@ static void update_latest_link(std::string const &filename, StillOptions const *
}
}
+void cvtRawData(const libcamera::Span<uint8_t> &mem, std::vector<libcamera::Span<uint8_t>> &packRaw, StreamInfo &info)
+{
+ const uint8_t *src = (uint8_t *)mem.data();
+ uint32_t width = info.width, height = info.height;
+ uint32_t bufSize = (width * height) + ((width * height) >> 1);
+ uint8_t * dstBuf = new uint8_t[bufSize];
+ uint8_t * dst = dstBuf;
+ uint32_t srcStride = ((width * 12 / 8 + 8 * 16 - 1) / (8 * 16)) * 128;
+
+ if(!dst)
+ throw std::runtime_error("fail to apply memory");
+
+ for(uint32_t i = 0; i < height; i++, src += srcStride, dst += (3 * width) >> 1) {
+ const uint8_t *srcRow = src;
+ uint8_t * dstRow = dst;
+ for(uint32_t j = 0; j < width; j += 2, srcRow += 3, dstRow += 3) {
+ dstRow[0] = ((srcRow[1] & 0xf) << 4) | (srcRow[0] >> 4);
+ dstRow[1] = srcRow[2];
+ dstRow[2] = (srcRow[1] & 0xf0) | (srcRow[0] & 0xf);
+ }
+ }
+
+ packRaw.push_back(libcamera::Span<uint8_t>(dstBuf, bufSize));
+
+ if(libcamera::formats::SRGGB12 == info.pixel_format)
+ info.pixel_format = libcamera::formats::SRGGB12_CSI2P;
+ else if(libcamera::formats::SGRBG12 == info.pixel_format)
+ info.pixel_format = libcamera::formats::SGRBG12_CSI2P;
+ else if(libcamera::formats::SBGGR12 == info.pixel_format)
+ info.pixel_format = libcamera::formats::SBGGR12_CSI2P;
+ else if(libcamera::formats::SGBRG12 == info.pixel_format)
+ info.pixel_format = libcamera::formats::SGBRG12_CSI2P;
+ else
+ throw std::runtime_error("unsupported Bayer format");
+ info.stride = (3 * width) >> 1;
+}
+
+void NV12ToYUV420(const libcamera::Span<uint8_t> &mem, std::vector<libcamera::Span<uint8_t>> &yuv420, StreamInfo &info);
+void NV12ToRGB888(const libcamera::Span<uint8_t> &mem, std::vector<libcamera::Span<uint8_t>> &rgb888, StreamInfo &info);
+void NV12ToBGR888(const libcamera::Span<uint8_t> &mem, std::vector<libcamera::Span<uint8_t>> &rgb888, StreamInfo &info);
+
static void save_image(LibcameraStillApp &app, CompletedRequestPtr &payload, Stream *stream,
std::string const &filename)
{
StillOptions const *options = app.GetOptions();
StreamInfo info = app.GetStreamInfo(stream);
const std::vector<libcamera::Span<uint8_t>> mem = app.Mmap(payload->buffers[stream]);
- if (stream == app.RawStream())
- dng_save(mem, info, payload->metadata, filename, app.CameraModel(), options);
- else if (options->encoding == "jpg")
- jpeg_save(mem, info, payload->metadata, filename, app.CameraModel(), options);
- else if (options->encoding == "png")
- png_save(mem, info, filename, options);
- else if (options->encoding == "bmp")
- bmp_save(mem, info, filename, options);
- else
- yuv_save(mem, info, filename, options);
+ if (stream == app.RawStream()) {
+ std::vector<libcamera::Span<uint8_t>> packRaw;
+ cvtRawData(mem[0], packRaw, info);
+ dng_save(packRaw, info, payload->metadata, filename, app.CameraModel(), options);
+ delete[] packRaw[0].data();
+ } else if (options->encoding == "jpg") {
+ std::vector<libcamera::Span<uint8_t>> yuv420;
+ NV12ToYUV420(mem[0], yuv420, info);
+ jpeg_save(yuv420, info, payload->metadata, filename, app.CameraModel(), options);
+ delete[] yuv420[0].data();
+ } else if (options->encoding == "png") {
+ std::vector<libcamera::Span<uint8_t>> rgb888;
+ NV12ToBGR888(mem[0], rgb888, info);
+ png_save(rgb888, info, filename, options);
+ delete[] rgb888[0].data();
+ } else if (options->encoding == "bmp") {
+ std::vector<libcamera::Span<uint8_t>> rgb888;
+ NV12ToRGB888(mem[0], rgb888, info);
+ bmp_save(rgb888, info, filename, options);
+ delete[] rgb888[0].data();
+ } else {
+ std::vector<libcamera::Span<uint8_t>> yuv420;
+ NV12ToYUV420(mem[0], yuv420, info);
+ yuv_save(yuv420, info, filename, options);
+ delete[] yuv420[0].data();
+ }
LOG(2, "Saved image " << info.width << " x " << info.height << " to file " << filename);
}
@@ -193,7 +250,7 @@ static void event_loop(LibcameraStillApp &app)
}
}
else
- app.ConfigureViewfinder();
+ app.ConfigureStill();
app.StartCamera();
auto start_time = std::chrono::high_resolution_clock::now();
auto timelapse_time = start_time;
@@ -234,7 +291,7 @@ static void event_loop(LibcameraStillApp &app)
// In viewfinder mode, run until the timeout or keypress. When that happens,
// if the "--autofocus-on-capture" option was set, trigger an AF scan and wait
// for it to complete. Then switch to capture mode if an output was requested.
- if (app.ViewfinderStream())
+ if (app.StillStream())
{
LOG(2, "Viewfinder frame " << count);
timelapse_frames++;
@@ -280,47 +337,29 @@ static void event_loop(LibcameraStillApp &app)
keypressed = false;
af_wait_state = AF_WAIT_NONE;
timelapse_time = std::chrono::high_resolution_clock::now();
+
+ save_images(app, completed_request);
+ if (!options->metadata.empty())
+ save_metadata(options, completed_request->metadata);
+
app.StopCamera();
app.Teardown();
- app.ConfigureStill(still_flags);
- if (options->af_on_capture)
- {
- libcamera::ControlList cl;
- cl.set(libcamera::controls::AfMode, libcamera::controls::AfModeAuto);
- cl.set(libcamera::controls::AfTrigger, libcamera::controls::AfTriggerCancel);
- app.SetControls(cl);
- }
- app.StartCamera();
- }
- else
- app.ShowPreview(completed_request, app.ViewfinderStream());
- }
- // In still capture mode, save a jpeg. Go back to viewfinder if in timelapse mode,
- // otherwise quit.
- else if (app.StillStream())
- {
- app.StopCamera();
- LOG(1, "Still capture image received");
- save_images(app, completed_request);
- if (!options->metadata.empty())
- save_metadata(options, completed_request->metadata);
- timelapse_frames = 0;
- if (!options->immediate && (options->timelapse || options->signal || options->keypress))
- {
- app.Teardown();
- app.ConfigureViewfinder();
- if (options->af_on_capture && options->afMode_index == -1)
- {
- libcamera::ControlList cl;
- cl.set(libcamera::controls::AfMode, libcamera::controls::AfModeAuto);
- cl.set(libcamera::controls::AfTrigger, libcamera::controls::AfTriggerCancel);
- app.SetControls(cl);
- }
- app.StartCamera();
- af_wait_state = AF_WAIT_NONE;
+ if (!options->immediate && (options->timelapse || options->signal || options->keypress)){
+ app.ConfigureStill(still_flags);
+ if (options->af_on_capture)
+ {
+ libcamera::ControlList cl;
+ cl.set(libcamera::controls::AfMode, libcamera::controls::AfModeAuto);
+ cl.set(libcamera::controls::AfTrigger, libcamera::controls::AfTriggerCancel);
+ app.SetControls(cl);
+ }
+ app.StartCamera();
+ }
+ else
+ return;
}
else
- return;
+ app.ShowPreview(completed_request, app.StillStream());
}
}
}
diff --git a/image/dng.cpp b/image/dng.cpp
index fbc02bb..98cb5e7 100644
--- a/image/dng.cpp
+++ b/image/dng.cpp
@@ -151,7 +151,6 @@ void dng_save(std::vector<libcamera::Span<uint8_t>> const &mem, StreamInfo const
std::string const &filename, std::string const &cam_model, StillOptions const *options)
{
// Check the Bayer format and unpack it to u16.
-
auto it = bayer_formats.find(info.pixel_format);
if (it == bayer_formats.end())
throw std::runtime_error("unsupported Bayer format");
--
2.34.1
File diff suppressed because it is too large Load Diff
@@ -1,28 +0,0 @@
From 2faf0e97fa4f1fdf8a2d22407966ea8747581eff Mon Sep 17 00:00:00 2001
From: "zejian.su" <zejian.su@starfivetech.com>
Date: Mon, 30 Oct 2023 17:35:05 +0800
Subject: [PATCH] Let the option: width, height work again.
Signed-off-by: zejian.su <zejian.su@starfivetech.com>
---
core/libcamera_app.cpp | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/core/libcamera_app.cpp b/core/libcamera_app.cpp
index cc76e9b..a49e31d 100644
--- a/core/libcamera_app.cpp
+++ b/core/libcamera_app.cpp
@@ -252,6 +252,10 @@ void LibcameraApp::ConfigureViewfinder()
//auto area = camera_->properties().get(properties::PixelArrayActiveAreas);
if (options_->viewfinder_width && options_->viewfinder_height)
size = Size(options_->viewfinder_width, options_->viewfinder_height);
+ else if (options_->width && options_->height)
+ {
+ size = Size(options_->width, options_->height);
+ }
//else if (area)
//{
// // The idea here is that most sensors will have a 2x2 binned mode that
--
2.34.1
@@ -1,148 +0,0 @@
From 0327a137e069e0cfbdd775f7c40f03e088b765d6 Mon Sep 17 00:00:00 2001
From: "zejian.su" <zejian.su@starfivetech.com>
Date: Wed, 8 Nov 2023 09:23:01 +0800
Subject: [PATCH 2/2] Make the qt preview work in debian. 1. Fill the NV12 data
into the QT buffer. 2. For the low performance of QWidget, reduce the FPS to
15.
Signed-off-by: zejian.su <zejian.su@starfivetech.com>
---
preview/preview.cpp | 37 ++++++++++++++-----------------------
preview/qt_preview.cpp | 20 ++++++++++++++++----
2 files changed, 30 insertions(+), 27 deletions(-)
diff --git a/preview/preview.cpp b/preview/preview.cpp
index b67edb3..d2c511a 100644
--- a/preview/preview.cpp
+++ b/preview/preview.cpp
@@ -19,47 +19,38 @@ Preview *make_preview(Options const *options)
if (options->nopreview)
return make_null_preview(options);
#if QT_PRESENT
- else if (options->qt_preview)
+ else
{
Preview *p = make_qt_preview(options);
if (p)
LOG(1, "Made QT preview window");
return p;
}
-#endif
- else
+#else
+ try
+ {
+ throw std::runtime_error("qt5 libraries unavailable.");
+ }
+ catch(std::exception const &e)
{
try
{
-#if LIBEGL_PRESENT
- Preview *p = make_egl_preview(options);
+#if LIBDRM_PRESENT
+ Preview *p = make_drm_preview(options);
if (p)
- LOG(1, "Made X/EGL preview window");
+ LOG(1, "Made DRM preview window");
return p;
#else
- throw std::runtime_error("egl libraries unavailable.");
+ throw std::runtime_error("drm libraries unavailable.");
#endif
}
catch (std::exception const &e)
{
- try
- {
-#if LIBDRM_PRESENT
- Preview *p = make_drm_preview(options);
- if (p)
- LOG(1, "Made DRM preview window");
- return p;
-#else
- throw std::runtime_error("drm libraries unavailable.");
-#endif
- }
- catch (std::exception const &e)
- {
- LOG(1, "Preview window unavailable");
- return make_null_preview(options);
- }
+ LOG(1, "Preview window unavailable");
+ return make_null_preview(options);
}
}
+#endif
return nullptr; // prevents compiler warning in debug builds
}
diff --git a/preview/qt_preview.cpp b/preview/qt_preview.cpp
index f595db7..8196a8e 100644
--- a/preview/qt_preview.cpp
+++ b/preview/qt_preview.cpp
@@ -58,7 +58,7 @@ protected:
class QtPreview : public Preview
{
public:
- QtPreview(Options const *options) : Preview(options)
+ QtPreview(Options const *options) : Preview(options), frame_counter_(0)
{
window_width_ = options->preview_width;
window_height_ = options->preview_height;
@@ -83,6 +83,12 @@ public:
void SetInfoText(const std::string &text) override { main_window_->setWindowTitle(QString::fromStdString(text)); }
virtual void Show(int fd, libcamera::Span<uint8_t> span, StreamInfo const &info) override
{
+ if((frame_counter_++) & 1) {
+ // Return the buffer to the camera system.
+ done_callback_(fd);
+ return;
+ }
+
// Quick and simple nearest-neighbour-ish resampling is used here.
// We further share U,V samples between adjacent output pixel pairs
// (even when downscaling) to speed up the conversion.
@@ -131,6 +137,7 @@ public:
// take a copy of each row used. This is a speedup provided memcpy() is vectorized.
tmp_stripe_.resize(2 * info.stride);
uint8_t const *Y_start = span.data();
+ uint8_t const *UV_start = Y_start + info.height * info.stride;
uint8_t *Y_row = &tmp_stripe_[0];
uint8_t *U_row = Y_row + info.stride;
uint8_t *V_row = U_row + (info.stride >> 1);
@@ -146,8 +153,11 @@ public:
unsigned x_pos = x_step >> 1;
memcpy(Y_row, Y_start + row * info.stride, info.stride);
- memcpy(U_row, Y_start + ((4 * info.height + row) >> 1) * (info.stride >> 1), info.stride >> 1);
- memcpy(V_row, Y_start + ((5 * info.height + row) >> 1) * (info.stride >> 1), info.stride >> 1);
+ //memcpy(U_row, Y_start + ((4 * info.height + row) >> 1) * (info.stride >> 1), info.stride >> 1);
+ //memcpy(V_row, Y_start + ((5 * info.height + row) >> 1) * (info.stride >> 1), info.stride >> 1);
+ uint8_t const *cur_uv = UV_start + (row >> 1) * info.width;
+ for(unsigned int uv_idx = 0; uv_idx < info.width >> 1; uv_idx++, cur_uv += 2)
+ U_row[uv_idx] = cur_uv[0], V_row[uv_idx] = cur_uv[1];
for (unsigned int x = 0; x < window_width_; x += 2)
{
@@ -183,7 +193,7 @@ public:
}
// Reset the preview window, clearing the current buffers and being ready to
// show new ones.
- void Reset() override {}
+ void Reset() override {frame_counter_ = 0;}
// Check if preview window has been shut down.
bool Quit() override { return main_window_->quit; }
// There is no particular limit to image sizes, though large images will be very slow.
@@ -218,6 +228,8 @@ private:
std::mutex mutex_;
std::condition_variable cond_var_;
std::vector<uint8_t> tmp_stripe_;
+
+ unsigned int frame_counter_;
};
Preview *make_qt_preview(Options const *options)
--
2.34.1
@@ -1,37 +0,0 @@
From de29362501b331f640a3d1d1f18722b229044873 Mon Sep 17 00:00:00 2001
From: "zejian.su" <zejian.su@starfivetech.com>
Date: Wed, 15 Nov 2023 12:00:25 +0800
Subject: [PATCH] Fix the libcamera-still bug at low resolution (redmine
#8306).
Set the raw stream's resolution too.
Signed-off-by: zejian.su <zejian.su@starfivetech.com>
---
core/libcamera_app.cpp | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/core/libcamera_app.cpp b/core/libcamera_app.cpp
index a49e31d..27d5c44 100644
--- a/core/libcamera_app.cpp
+++ b/core/libcamera_app.cpp
@@ -349,10 +349,14 @@ void LibcameraApp::ConfigureStill(unsigned int flags)
configuration_->at(0).bufferCount = 3;
else if (options_->buffer_count > 0)
configuration_->at(0).bufferCount = options_->buffer_count;
- if (options_->width)
+ if (options_->width) {
configuration_->at(0).size.width = options_->width;
- if (options_->height)
+ configuration_->at(1).size.width = options_->width;
+ }
+ if (options_->height) {
configuration_->at(0).size.height = options_->height;
+ configuration_->at(1).size.height = options_->height;
+ }
configuration_->at(0).colorSpace = libcamera::ColorSpace::Sycc;
configuration_->transform = options_->transform;
--
2.34.1
@@ -1,300 +0,0 @@
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
@@ -1,291 +0,0 @@
From e9e9555cac3c11c70277824d82d5b3fbd5e4afd2 Mon Sep 17 00:00:00 2001
From: "zejian.su" <zejian.su@starfivetech.com>
Date: Fri, 1 Dec 2023 16:30:27 +0800
Subject: [PATCH 2/2] Fix the sync issue between the Preview.Show() and the
QWidget.update() 1. Add a mutex between the Preview.Show() and the
QWidget.update(). 2. Speed up the Preview.Show() -- change the floating point
to integer, merge some plus and multiplying operation and remove some data
copy.
Signed-off-by: zejian.su <zejian.su@starfivetech.com>
---
preview/qt_preview.cpp | 183 +++++++++++++++++++++++++----------------
1 file changed, 113 insertions(+), 70 deletions(-)
diff --git a/preview/qt_preview.cpp b/preview/qt_preview.cpp
index 8196a8e..ef3c8a4 100644
--- a/preview/qt_preview.cpp
+++ b/preview/qt_preview.cpp
@@ -41,16 +41,47 @@ class MyWidget : public QWidget
public:
MyWidget(QWidget *parent, int w, int h) : QWidget(parent), size(w, h)
{
- image = QImage(size, QImage::Format_RGB888);
- image.fill(0);
+ for(int i = 0; i < 2; i++) {
+ buffers_[i].image = QImage(size, QImage::Format_RGB888);
+ buffers_[i].image.fill(0);
+ availableBuffers_.push_back(&buffers_[i]);
+ }
}
QSize size;
- QImage image;
+
+ struct ImageBuffer {
+ QImage image;
+ uint8_t frameCounter;
+
+ ImageBuffer() : frameCounter(0) {};
+ } buffers_[2];
+
+ std::list<ImageBuffer *> freeBuffers_;
+ std::list<ImageBuffer *> availableBuffers_;
+
+ std::mutex buffers_available_mutex_;
+ std::mutex buffers_free_mutex_;
protected:
void paintEvent(QPaintEvent *) override
{
QPainter painter(this);
- painter.drawImage(rect(), image, image.rect());
+ {
+ ImageBuffer *buffer = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(buffers_available_mutex_);
+ if(!availableBuffers_.size())
+ return;
+ buffer = availableBuffers_.back();
+ availableBuffers_.pop_back();
+ }
+
+ painter.drawImage(rect(), buffer->image, buffer->image.rect());
+
+ {
+ std::lock_guard<std::mutex> lock(buffers_free_mutex_);
+ freeBuffers_.push_back(buffer);
+ }
+ }
}
QSize sizeHint() const override { return size; }
};
@@ -58,7 +89,7 @@ protected:
class QtPreview : public Preview
{
public:
- QtPreview(Options const *options) : Preview(options), frame_counter_(0)
+ QtPreview(Options const *options) : Preview(options)
{
window_width_ = options->preview_width;
window_height_ = options->preview_height;
@@ -67,6 +98,8 @@ public:
// This preview window is expensive, so make it small by default.
if (window_width_ == 0 || window_height_ == 0)
window_width_ = 512, window_height_ = 384;
+
+ frameCounter_ = 0;
// As a hint, reserve twice the binned width for our widest current camera (V3)
tmp_stripe_.reserve(4608);
thread_ = std::thread(&QtPreview::threadFunc, this, options);
@@ -83,100 +116,105 @@ public:
void SetInfoText(const std::string &text) override { main_window_->setWindowTitle(QString::fromStdString(text)); }
virtual void Show(int fd, libcamera::Span<uint8_t> span, StreamInfo const &info) override
{
- if((frame_counter_++) & 1) {
- // Return the buffer to the camera system.
- done_callback_(fd);
- return;
+ MyWidget::ImageBuffer *buffer = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(pane_->buffers_free_mutex_);
+ if(pane_->freeBuffers_.size() > 0) {
+ buffer = pane_->freeBuffers_.front();
+ pane_->freeBuffers_.pop_front();
+ }
+ }
+
+ if(!buffer) {
+ std::lock_guard<std::mutex> lock(pane_->buffers_available_mutex_);
+ if(!pane_->availableBuffers_.size()) {
+ done_callback_(fd);
+ return;
+ }
+ buffer = pane_->availableBuffers_.front();
+ pane_->availableBuffers_.pop_front();
}
-
// Quick and simple nearest-neighbour-ish resampling is used here.
// We further share U,V samples between adjacent output pixel pairs
// (even when downscaling) to speed up the conversion.
unsigned x_step = (info.width << 16) / window_width_;
unsigned y_step = (info.height << 16) / window_height_;
- // Choose the right matrix to convert YUV back to RGB.
- static const float YUV2RGB[3][9] = {
- { 1.0, 0.0, 1.402, 1.0, -0.344, -0.714, 1.0, 1.772, 0.0 }, // JPEG
- { 1.164, 0.0, 1.596, 1.164, -0.392, -0.813, 1.164, 2.017, 0.0 }, // SMPTE170M
- { 1.164, 0.0, 1.793, 1.164, -0.213, -0.533, 1.164, 2.112, 0.0 }, // Rec709
+ static const uint32_t YUV2RGB[3][9] = {
+ { 128, 0, 179, 128, 44, 91, 128, 227, 0 }, // JPEG
+ { 149, 0, 204, 149, 50, 104, 149, 258, 0 }, // SMPTE170M
+ { 149, 0, 230, 149, 27, 68, 149, 270, 0 }, // Rec709
};
- int offsetY;
- float coeffY, coeffVR, coeffUG, coeffVG, coeffUB;
- if (info.colour_space == libcamera::ColorSpace::Smpte170m)
+ static const int RGBOFFSET[3][3] = {
+ {24960, -15232, 31104}, {28496, -17328, 35408}, {31824, -9776, 36944}
+ };
+
+ uint32_t coeffY, coeffVR, coeffUG, coeffVG, coeffUB;
+ const int * rgbOffset = nullptr;
+ if(info.colour_space == libcamera::ColorSpace::Smpte170m)
{
- offsetY = 16;
coeffY = YUV2RGB[1][0];
coeffVR = YUV2RGB[1][2];
coeffUG = YUV2RGB[1][4];
coeffVG = YUV2RGB[1][5];
coeffUB = YUV2RGB[1][7];
- }
- else if (info.colour_space == libcamera::ColorSpace::Rec709)
+ rgbOffset = RGBOFFSET[1];
+ } else if(info.colour_space == libcamera::ColorSpace::Rec709)
{
- offsetY = 16;
coeffY = YUV2RGB[2][0];
coeffVR = YUV2RGB[2][2];
coeffUG = YUV2RGB[2][4];
coeffVG = YUV2RGB[2][5];
coeffUB = YUV2RGB[2][7];
- }
- else
+ rgbOffset = RGBOFFSET[2];
+ } else
{
- offsetY = 0;
coeffY = YUV2RGB[0][0];
coeffVR = YUV2RGB[0][2];
coeffUG = YUV2RGB[0][4];
coeffVG = YUV2RGB[0][5];
coeffUB = YUV2RGB[0][7];
- if (info.colour_space != libcamera::ColorSpace::Sycc)
- LOG(1, "QtPreview: unexpected colour space " << libcamera::ColorSpace::toString(info.colour_space));
+ rgbOffset = RGBOFFSET[0];
}
- // Because the source buffer is uncached, and we want to read it a byte at a time,
- // take a copy of each row used. This is a speedup provided memcpy() is vectorized.
- tmp_stripe_.resize(2 * info.stride);
- uint8_t const *Y_start = span.data();
- uint8_t const *UV_start = Y_start + info.height * info.stride;
- uint8_t *Y_row = &tmp_stripe_[0];
- uint8_t *U_row = Y_row + info.stride;
- uint8_t *V_row = U_row + (info.stride >> 1);
-
- // Possibly this should be locked in case a repaint is happening? In practice the risk
- // is only that there might be some tearing, so I don't think we worry. We could speed
- // it up by getting the ISP to supply RGB, but I'm not sure I want to handle that extra
- // possibility in our main application code, so we'll put up with the slow conversion.
- for (unsigned int y = 0; y < window_height_; y++)
+ uint8_t const * Y_start = span.data();
+ uint8_t const * UV_start = Y_start + info.height * info.stride;
+ int src_ypos = y_step >> 1;
+ uint32_t Y2 = 0;
+ uint32_t U2 = coeffUG | (coeffUB << 16);
+ uint32_t V2 = coeffVR | (coeffVG << 16);
+ uint16_t * y2 = (uint16_t *)&Y2;
+ uint32_t U;
+ uint32_t V;
+ uint16_t * u2 = (uint16_t *)&U;
+ uint16_t * v2 = (uint16_t *)&V;
+
+ for(unsigned int y = 0; y < window_height_; y++, src_ypos += y_step)
{
- unsigned row = (y * y_step) >> 16;
- uint8_t *dest = pane_->image.scanLine(y);
- unsigned x_pos = x_step >> 1;
-
- memcpy(Y_row, Y_start + row * info.stride, info.stride);
- //memcpy(U_row, Y_start + ((4 * info.height + row) >> 1) * (info.stride >> 1), info.stride >> 1);
- //memcpy(V_row, Y_start + ((5 * info.height + row) >> 1) * (info.stride >> 1), info.stride >> 1);
- uint8_t const *cur_uv = UV_start + (row >> 1) * info.width;
- for(unsigned int uv_idx = 0; uv_idx < info.width >> 1; uv_idx++, cur_uv += 2)
- U_row[uv_idx] = cur_uv[0], V_row[uv_idx] = cur_uv[1];
-
- for (unsigned int x = 0; x < window_width_; x += 2)
+ const uint8_t * src_y = Y_start + (src_ypos >> 16) * info.width;
+ const uint8_t * src_uv = UV_start + (src_ypos >> 17) * info.width;
+ uint8_t * dest = buffer->image.scanLine(y);
+ uint32_t x_pos = x_step >> 1;
+
+ for(unsigned int x = 0; x < window_width_; x += 2)
{
- int Y0 = Y_row[x_pos >> 16];
+ y2[0] = src_y[x_pos >> 16];
x_pos += x_step;
- int Y1 = Y_row[x_pos >> 16];
- int U = U_row[x_pos >> 17];
- int V = V_row[x_pos >> 17];
+ y2[1] = src_y[x_pos >> 16];
+ U = src_uv[(x_pos >> 16) & 0xfffffffe];
+ V = src_uv[(x_pos >> 16) | 1];
x_pos += x_step;
- Y0 -= offsetY;
- Y1 -= offsetY;
- U -= 128;
- V -= 128;
- int R0 = coeffY * Y0 + coeffVR * V;
- int G0 = coeffY * Y0 + coeffUG * U + coeffVG * V;
- int B0 = coeffY * Y0 + coeffUB * U;
- int R1 = coeffY * Y1 + coeffVR * V;
- int G1 = coeffY * Y1 + coeffUG * U + coeffVG * V;
- int B1 = coeffY * Y1 + coeffUB * U;
+
+ Y2 *= coeffY;
+ U *= U2;
+ V *= V2;
+
+ int R0 = ((int)y2[0] + (int)v2[0] - rgbOffset[0]) >> 7;
+ int G0 = ((int)y2[0] - (int)u2[0] - (int)v2[1] - rgbOffset[1]) >> 7;
+ int B0 = ((int)y2[0] + (int)u2[1] - rgbOffset[2]) >> 7;
+ int R1 = ((int)y2[1] + (int)v2[0] - rgbOffset[0]) >> 7;
+ int G1 = ((int)y2[1] - (int)u2[0] - (int)v2[1] - rgbOffset[1]) >> 7;
+ int B1 = ((int)y2[1] + (int)u2[1] - rgbOffset[2]) >> 7;
*(dest++) = std::clamp(R0, 0, 255);
*(dest++) = std::clamp(G0, 0, 255);
*(dest++) = std::clamp(B0, 0, 255);
@@ -186,6 +224,12 @@ public:
}
}
+ {
+ std::lock_guard<std::mutex> lock(pane_->buffers_available_mutex_);
+ buffer->frameCounter = ++frameCounter_;
+ pane_->availableBuffers_.push_back(buffer);
+ }
+
pane_->update();
// Return the buffer to the camera system.
@@ -193,7 +237,7 @@ public:
}
// Reset the preview window, clearing the current buffers and being ready to
// show new ones.
- void Reset() override {frame_counter_ = 0;}
+ void Reset() override {}
// Check if preview window has been shut down.
bool Quit() override { return main_window_->quit; }
// There is no particular limit to image sizes, though large images will be very slow.
@@ -228,8 +272,7 @@ private:
std::mutex mutex_;
std::condition_variable cond_var_;
std::vector<uint8_t> tmp_stripe_;
-
- unsigned int frame_counter_;
+ uint8_t frameCounter_;
};
Preview *make_qt_preview(Options const *options)
--
2.34.1
@@ -1 +1 @@
sha256 add2d80450f3b61de275c6505689b0984d29c9dde94f0cf381c4209620c96263 libcamera-apps-54a781dffdd101954dcfa6acd0bd80006f67da83-br1.tar.gz
sha256 774e913f4b71ddf51a6f2adf00f4c48866018d32a0ba57569206e31ada339f59 libcamera-apps-31377be38defcfbd6bc70bd12895c6f145fc8171-br1.tar.gz
@@ -4,8 +4,8 @@
#
################################################################################
LIBCAMERA_APPS_SITE = https://github.com/raspberrypi/libcamera-apps.git
LIBCAMERA_APPS_VERSION = 54a781dffdd101954dcfa6acd0bd80006f67da83
LIBCAMERA_APPS_SITE = https://github.com/starfive-tech/libcamera-apps.git
LIBCAMERA_APPS_VERSION = 31377be38defcfbd6bc70bd12895c6f145fc8171
LIBCAMERA_APPS_SITE_METHOD = git
LIBCAMERA_APPS_INSTALL_STAGING = YES