From patchwork Thu Aug 1 07:31:00 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cheng-Hao Yang X-Patchwork-Id: 20742 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id A809EBDC71 for ; Thu, 1 Aug 2024 07:34:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4325C633B1; Thu, 1 Aug 2024 09:34:05 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="gxRUuV1c"; dkim-atps=neutral Received: from mail-pl1-x629.google.com (mail-pl1-x629.google.com [IPv6:2607:f8b0:4864:20::629]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7B4D063386 for ; Thu, 1 Aug 2024 09:33:54 +0200 (CEST) Received: by mail-pl1-x629.google.com with SMTP id d9443c01a7336-1fd65aaac27so13325005ad.1 for ; Thu, 01 Aug 2024 00:33:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1722497632; x=1723102432; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=S8UX4QggDFljYgu85ci7m0NZAN/PUJn4EJZCAtz70yg=; b=gxRUuV1cseduZuHnSretrjdU8Dts64IpNRFlmZ0FB6oZhuIe40NRWK7Wrq1+2wfRUO C5O3jG/ndzeJtFmELaCmom01iueQnDO+/cNvbRkqt8PIIER2v93GhC7n2UgP1CGztZnU agWL9pfJNGNQV1+8x9d4xBr0MpgJP78Th5Vtg= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722497632; x=1723102432; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=S8UX4QggDFljYgu85ci7m0NZAN/PUJn4EJZCAtz70yg=; b=IHNmt41ir2sXLlFdzXQ/2wIWgTiGMxArtUaNHwjDpQ3HZpk7qM6BOrRzPgps+AfCcU paYcuxweZvcxR3kNtFH1Msn6QfEU2DTO/3cPvlf8TarfWeoiJEO36u6GO0MvZCMOR2gO wKBRibO2f5CqPDWmAdbSABf755TMhOqhbPmQjtzDjoVNAH9JRweL2NBNW+DKPCH41yog LGZY9wAOs+374vPIKuXTrcl2Z4rcDBVagXmxmgkXhZ8SLkXn+K0KBPX/oF5Gab0Gz75P FCMVLw925rc4J4FefXr3VjDLe5byx9/gmsW7vMjXxISZEdsR/Mk2h7kgMjOHYoMHKAdV 1cZQ== X-Gm-Message-State: AOJu0YyskyQSZ+Y7KC+ZubTi3dwKX/p5TMGWN3DeyPb90AfgLmQKD1EA SFvXsXi89pZ4a9efeFPtYjMyT1GWXAwUQ++EhQGj17nqAV/26B5I7hnqfd+oy/F9groQ/1oAXAX Hyg== X-Google-Smtp-Source: AGHT+IED67Dl/7b10wZcVdY7Er6tgxrXRNw8QFywWPGhmlPyYUlodbC91pFHQayTYT4Gnw7lxpZeZg== X-Received: by 2002:a17:902:c40c:b0:1fd:a3c4:25eb with SMTP id d9443c01a7336-1ff52441c36mr6629825ad.17.1722497632007; Thu, 01 Aug 2024 00:33:52 -0700 (PDT) Received: from chenghaoyang-low.c.googlers.com.com (237.198.80.34.bc.googleusercontent.com. [34.80.198.237]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-1fed7cd3dcbsm131839165ad.73.2024.08.01.00.33.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 01 Aug 2024 00:33:51 -0700 (PDT) From: Harvey Yang X-Google-Original-From: Harvey Yang To: libcamera-devel@lists.libcamera.org Cc: Konami Shu , Harvey Yang , Yunke Cao , Tomasz Figa Subject: [PATCH v7 4/7] libcamera: pipeline: Add test pattern for VirtualPipelineHandler Date: Thu, 1 Aug 2024 07:31:00 +0000 Message-ID: <20240801073339.4061027-5-chenghaoyang@google.com> X-Mailer: git-send-email 2.46.0.rc1.232.g9752f9e123-goog In-Reply-To: <20240801073339.4061027-1-chenghaoyang@google.com> References: <20240801073339.4061027-1-chenghaoyang@google.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" From: Konami Shu - There are two test patterns: color bars and diagonal lines - Add class for generating test patterns - Add libyuv to build dependencies - Make VirtualPipelineHandler show the test pattern - Format the code - Rename test_pattern to frame_generator - reflect comment - Fix const variable name - Use #pragma once - Make configure() private Signed-off-by: Konami Shu Co-developed-by: Harvey Yang Co-developed-by: Yunke Cao Co-developed-by: Tomasz Figa --- .../pipeline/virtual/frame_generator.h | 33 ++++++ src/libcamera/pipeline/virtual/meson.build | 22 ++++ .../virtual/test_pattern_generator.cpp | 112 ++++++++++++++++++ .../pipeline/virtual/test_pattern_generator.h | 58 +++++++++ src/libcamera/pipeline/virtual/virtual.cpp | 28 ++++- src/libcamera/pipeline/virtual/virtual.h | 8 ++ 6 files changed, 258 insertions(+), 3 deletions(-) create mode 100644 src/libcamera/pipeline/virtual/frame_generator.h create mode 100644 src/libcamera/pipeline/virtual/test_pattern_generator.cpp create mode 100644 src/libcamera/pipeline/virtual/test_pattern_generator.h diff --git a/src/libcamera/pipeline/virtual/frame_generator.h b/src/libcamera/pipeline/virtual/frame_generator.h new file mode 100644 index 00000000..9699af7a --- /dev/null +++ b/src/libcamera/pipeline/virtual/frame_generator.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2023, Google Inc. + * + * frame_generator.h - Virtual cameras helper to generate frames + */ + +#pragma once + +#include +#include + +namespace libcamera { + +class FrameGenerator +{ +public: + virtual ~FrameGenerator() = default; + + /* Create buffers for using them in `generateFrame` */ + virtual void configure(const Size &size) = 0; + + /** Fill the output frame buffer. + * Use the frame at the frameCount of image frames + */ + virtual void generateFrame(const Size &size, + const FrameBuffer *buffer) = 0; + +protected: + FrameGenerator() {} +}; + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/virtual/meson.build b/src/libcamera/pipeline/virtual/meson.build index ba7ff754..e1e65e68 100644 --- a/src/libcamera/pipeline/virtual/meson.build +++ b/src/libcamera/pipeline/virtual/meson.build @@ -2,4 +2,26 @@ libcamera_sources += files([ 'virtual.cpp', + 'test_pattern_generator.cpp', ]) + +libyuv_dep = dependency('libyuv', required : false) + +# Fallback to a subproject if libyuv isn't found, as it's typically not +# provided by distributions. +if not libyuv_dep.found() + cmake = import('cmake') + + libyuv_vars = cmake.subproject_options() + libyuv_vars.add_cmake_defines({'CMAKE_POSITION_INDEPENDENT_CODE': 'ON'}) + libyuv_vars.set_override_option('cpp_std', 'c++17') + libyuv_vars.append_compile_args('cpp', + '-Wno-sign-compare', + '-Wno-unused-variable', + '-Wno-unused-parameter') + libyuv_vars.append_link_args('-ljpeg') + libyuv = cmake.subproject('libyuv', options : libyuv_vars) + libyuv_dep = libyuv.dependency('yuv') +endif + +libcamera_deps += [libyuv_dep] diff --git a/src/libcamera/pipeline/virtual/test_pattern_generator.cpp b/src/libcamera/pipeline/virtual/test_pattern_generator.cpp new file mode 100644 index 00000000..8dfe626e --- /dev/null +++ b/src/libcamera/pipeline/virtual/test_pattern_generator.cpp @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2023, Google Inc. + * + * test_pattern_generator.cpp - Derived class of FrameGenerator for + * generating test patterns + */ + +#include "test_pattern_generator.h" + +#include + +#include "libcamera/internal/mapped_framebuffer.h" + +#include "libyuv/convert_from_argb.h" +namespace libcamera { + +LOG_DECLARE_CATEGORY(Virtual) + +static const unsigned int kARGBSize = 4; + +void TestPatternGenerator::generateFrame( + const Size &size, + const FrameBuffer *buffer) +{ + MappedFrameBuffer mappedFrameBuffer(buffer, + MappedFrameBuffer::MapFlag::Write); + + auto planes = mappedFrameBuffer.planes(); + + /* Convert the template_ to the frame buffer */ + int ret = libyuv::ARGBToNV12( + template_.get(), /*src_stride_argb=*/size.width * kARGBSize, + planes[0].begin(), size.width, + planes[1].begin(), size.width, + size.width, size.height); + if (ret != 0) { + LOG(Virtual, Error) << "ARGBToNV12() failed with " << ret; + } +} + +std::unique_ptr ColorBarsGenerator::create() +{ + return std::make_unique(); +} + +void ColorBarsGenerator::configure(const Size &size) +{ + constexpr uint8_t kColorBar[8][3] = { + // R, G, B + { 0xff, 0xff, 0xff }, // White + { 0xff, 0xff, 0x00 }, // Yellow + { 0x00, 0xff, 0xff }, // Cyan + { 0x00, 0xff, 0x00 }, // Green + { 0xff, 0x00, 0xff }, // Magenta + { 0xff, 0x00, 0x00 }, // Red + { 0x00, 0x00, 0xff }, // Blue + { 0x00, 0x00, 0x00 }, // Black + }; + + template_ = std::make_unique( + size.width * size.height * kARGBSize); + + unsigned int colorBarWidth = size.width / std::size(kColorBar); + + uint8_t *buf = template_.get(); + for (size_t h = 0; h < size.height; h++) { + for (size_t w = 0; w < size.width; w++) { + // repeat when the width is exceed + int index = (w / colorBarWidth) % std::size(kColorBar); + + *buf++ = kColorBar[index][2]; // B + *buf++ = kColorBar[index][1]; // G + *buf++ = kColorBar[index][0]; // R + *buf++ = 0x00; // A + } + } +} + +std::unique_ptr DiagonalLinesGenerator::create() +{ + return std::make_unique(); +} + +void DiagonalLinesGenerator::configure(const Size &size) +{ + constexpr uint8_t kColorBar[8][3] = { + // R, G, B + { 0xff, 0xff, 0xff }, // White + { 0x00, 0x00, 0x00 }, // Black + }; + + template_ = std::make_unique( + size.width * size.height * kARGBSize); + + unsigned int lineWidth = size.width / 10; + + uint8_t *buf = template_.get(); + for (size_t h = 0; h < size.height; h++) { + for (size_t w = 0; w < size.width; w++) { + // repeat when the width is exceed + int index = ((w + h) / lineWidth) % 2; + + *buf++ = kColorBar[index][2]; // B + *buf++ = kColorBar[index][1]; // G + *buf++ = kColorBar[index][0]; // R + *buf++ = 0x00; // A + } + } +} + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/virtual/test_pattern_generator.h b/src/libcamera/pipeline/virtual/test_pattern_generator.h new file mode 100644 index 00000000..ed8d4e43 --- /dev/null +++ b/src/libcamera/pipeline/virtual/test_pattern_generator.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2023, Google Inc. + * + * test_pattern_generator.h - Derived class of FrameGenerator for + * generating test patterns + */ + +#pragma once + +#include + +#include +#include + +#include "frame_generator.h" + +namespace libcamera { + +enum class TestPattern : char { + ColorBars = 0, + DiagonalLines = 1, +}; + +class TestPatternGenerator : public FrameGenerator +{ +private: + void generateFrame(const Size &size, + const FrameBuffer *buffer) override; + +protected: + /* Shift the buffer by 1 pixel left each frame */ + void shiftLeft(const Size &size); + /* Buffer of test pattern template */ + std::unique_ptr template_; +}; + +class ColorBarsGenerator : public TestPatternGenerator +{ +public: + static std::unique_ptr create(); + +private: + /* Generate a template buffer of the color bar test pattern. */ + void configure(const Size &size) override; +}; + +class DiagonalLinesGenerator : public TestPatternGenerator +{ +public: + static std::unique_ptr create(); + +private: + /* Generate a template buffer of the diagonal lines test pattern. */ + void configure(const Size &size) override; +}; + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp index 8abb74b4..a36ffd42 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -184,10 +184,14 @@ int PipelineHandlerVirtual::exportFrameBuffers( return dmaBufAllocator_.exportFrameBuffers(stream->configuration(), buffers); } -int PipelineHandlerVirtual::start([[maybe_unused]] Camera *camera, +int PipelineHandlerVirtual::start(Camera *camera, [[maybe_unused]] const ControlList *controls) { /* \todo Start reading the virtual video if any. */ + VirtualCameraData *data = cameraData(camera); + + data->frameGenerator_->configure(data->stream_.configuration().size); + return 0; } @@ -199,9 +203,14 @@ void PipelineHandlerVirtual::stopDevice([[maybe_unused]] Camera *camera) int PipelineHandlerVirtual::queueRequestDevice([[maybe_unused]] Camera *camera, Request *request) { + VirtualCameraData *data = cameraData(camera); + /* \todo Read from the virtual video if any. */ - for (auto it : request->buffers()) - completeBuffer(request, it.second); + for (auto const &[stream, buffer] : request->buffers()) { + /* map buffer and fill test patterns */ + data->frameGenerator_->generateFrame(stream->configuration().size, buffer); + completeBuffer(request, buffer); + } request->metadata().set(controls::SensorTimestamp, currentTimestamp()); completeRequest(request); @@ -233,11 +242,24 @@ bool PipelineHandlerVirtual::match([[maybe_unused]] DeviceEnumerator *enumerator std::set streams{ &data->stream_ }; const std::string id = "Virtual0"; std::shared_ptr camera = Camera::create(std::move(data), id, streams); + + initFrameGenerator(camera.get()); + registerCamera(std::move(camera)); return false; // Prevent infinite loops for now } +void PipelineHandlerVirtual::initFrameGenerator(Camera *camera) +{ + auto data = cameraData(camera); + if (data->testPattern_ == TestPattern::DiagonalLines) { + data->frameGenerator_ = DiagonalLinesGenerator::create(); + } else { + data->frameGenerator_ = ColorBarsGenerator::create(); + } +} + REGISTER_PIPELINE_HANDLER(PipelineHandlerVirtual, "virtual") } /* namespace libcamera */ diff --git a/src/libcamera/pipeline/virtual/virtual.h b/src/libcamera/pipeline/virtual/virtual.h index 6fc6b34d..fecd9fa6 100644 --- a/src/libcamera/pipeline/virtual/virtual.h +++ b/src/libcamera/pipeline/virtual/virtual.h @@ -13,6 +13,8 @@ #include "libcamera/internal/dma_buf_allocator.h" #include "libcamera/internal/pipeline_handler.h" +#include "test_pattern_generator.h" + namespace libcamera { class VirtualCameraData : public Camera::Private @@ -29,9 +31,13 @@ public: ~VirtualCameraData() = default; + TestPattern testPattern_; + std::vector supportedResolutions_; Stream stream_; + + std::unique_ptr frameGenerator_; }; class VirtualCameraConfiguration : public CameraConfiguration @@ -72,6 +78,8 @@ private: return static_cast(camera->_d()); } + void initFrameGenerator(Camera *camera); + DmaBufAllocator dmaBufAllocator_; };