From patchwork Fri Jul 26 09:54:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Harvey Yang X-Patchwork-Id: 20705 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 47E1FBDC71 for ; Fri, 26 Jul 2024 09:56:47 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DF5C86337F; Fri, 26 Jul 2024 11:56:46 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="NbW4IHSu"; 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 91B9761994 for ; Fri, 26 Jul 2024 11:56:38 +0200 (CEST) Received: by mail-pl1-x629.google.com with SMTP id d9443c01a7336-1fc658b6b2eso4032825ad.0 for ; Fri, 26 Jul 2024 02:56:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1721987797; x=1722592597; 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=LnScMZl9xDDlUx3VD4jbBUFtWNG/mFUavpE5NoHjgHU=; b=NbW4IHSu7q9kkl5ltQxHga6Sbi7gAJxH/Hy+CCakIkl0DeYMtQEZu9vkKGIYLnSVhE KAYST/+Hq9ugdXniWQE9QfX0Bo/Jm/gPw5oevI+SrsZYa7O/6mfTk+82ZI0NqWexLe5h UynxwMf27AqlZQf1AZuRGRE3HZv98uc3GWKck= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721987797; x=1722592597; 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=LnScMZl9xDDlUx3VD4jbBUFtWNG/mFUavpE5NoHjgHU=; b=XqLokYgdGpGUegKqPfJB2ACpW16nY8JJ8gtCvtlupyiFxLr1dbNuuREDKwhDzPOMRT ZNtt/OAY2PQGDcIzuT6ZGG4rJMd4qaTmfqpBm7l7hqSpLWGZADPxWS8Ox3ir9xPVzkQi 7CzLbLXDvbi13epOZMy3iF2iOJvX21SsGvUk6ZwcoMZks4MB/bsLXxdXA0xn7kpsACuK A6HeRzW12foMNZPa7cLlcKpx3y9JWD9GiG4Ikwfqj5mpvTOkYjLNTAP6xgx3i8UVM92C o3b6kc+xu8W/lAd9SgXW44QluRBKgkCS0iHAv5DXBfzBj1KzFSaFxEspNyeyojE3TGo5 Tb8Q== X-Gm-Message-State: AOJu0YzJ2Mw/jOEBEVFS/TXZ/T9pI4HBhnM61QkkQxJCMIiG1JkJXPkl BwEJut7nIrHhfceUc61nw4fHPE1+XnxbiyUOKc6pEWWqT7J6zT0xS4D9BCHZSG1Fn+1sJArvcSH 8lw== X-Google-Smtp-Source: AGHT+IEg0MKYW+UUsafBW7/mkc6rKt+AUXxKQl8rRyYKNXEjIrrslufmXFm+ggEZ0hT6+DRHECdTcw== X-Received: by 2002:a17:903:22c4:b0:1f7:37f:728d with SMTP id d9443c01a7336-1fed3870eb3mr75232585ad.10.1721987796761; Fri, 26 Jul 2024 02:56:36 -0700 (PDT) Received: from chenghaoyang-low.c.googlers.com.com (148.175.199.104.bc.googleusercontent.com. [104.199.175.148]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-1fed7c7f63asm28396665ad.46.2024.07.26.02.56.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Jul 2024 02:56:36 -0700 (PDT) From: Harvey Yang To: libcamera-devel@lists.libcamera.org Cc: Konami Shu , Harvey Yang , Yunke Cao , Tomasz Figa Subject: [PATCH v6 4/7] libcamera: pipeline: Add test pattern for VirtualPipelineHandler Date: Fri, 26 Jul 2024 09:54:25 +0000 Message-ID: <20240726095626.3632402-5-chenghaoyang@chromium.org> X-Mailer: git-send-email 2.46.0.rc1.232.g9752f9e123-goog In-Reply-To: <20240726095626.3632402-1-chenghaoyang@chromium.org> References: <20240726095626.3632402-1-chenghaoyang@chromium.org> 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 Change-Id: I744706c9b86539594751fcdd0b57412238f027ef --- .../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 000000000..9699af7a4 --- /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 ba7ff754e..e1e65e68d 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 000000000..b142b522c --- /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 000000000..ed8d4e43b --- /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 8abb74b48..a36ffd42b 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 6fc6b34d8..fecd9fa6f 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_; };