From patchwork Mon Aug 5 13:48:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Harvey Yang X-Patchwork-Id: 20781 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 A7EEEBE173 for ; Mon, 5 Aug 2024 13:51:19 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C684B63381; Mon, 5 Aug 2024 15:51:18 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="fN6oqAdE"; dkim-atps=neutral Received: from mail-lj1-x22a.google.com (mail-lj1-x22a.google.com [IPv6:2a00:1450:4864:20::22a]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DC52363384 for ; Mon, 5 Aug 2024 15:51:08 +0200 (CEST) Received: by mail-lj1-x22a.google.com with SMTP id 38308e7fff4ca-2ef1c12ae23so117510411fa.0 for ; Mon, 05 Aug 2024 06:51:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1722865868; x=1723470668; 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=C1NEI6Y0uTSrug/sZh2RvLVS7lS1QIMtdy3YzOmxR9k=; b=fN6oqAdESxW/vUfzXNt5nZcgvCLZfa/XuRFeWuGxxNtdKIwKtCTSbQmfZ9lVNYr5i/ ywm8y/vU5yBomqHw4bsy7fIr+KaksgKMLQV74fOh37p0ozGJuEl6+VjA5eh4KzFLceJC +4fqPwthq4FBNGx5t+58rNaaoGnjD3Eyz/ww8= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722865868; x=1723470668; 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=C1NEI6Y0uTSrug/sZh2RvLVS7lS1QIMtdy3YzOmxR9k=; b=QLAsb9bt48Ujh8S1go8sd+/QPUphOyA2z3ML+f4Eh1h9IdftA0gg9cJksb4c3hIL9X Wq7T/asN6VSsFXQ7x1FKSxgDOjnPZEn76NXWEnSWaUvLnHQFOIeM+ZSgsSFr/IFqn15k yn8XUphIWqFNUR7tE7JfC6RLMSnyZOGgo91fYu63dh6zbUicO3EZrTSTqbzOr/yf8KER ruM99JZwFM1BFwfNvbGTIaudgYYO7XCyRowVRDYRISaMyDgEaxH9cid3xeitJdNX/UOs xzjXjbRWBjjHJ7jhCJZ5OTuvcyyyeuE9uITFpSoaMMJE0c8B0whGUMTdYl8E8h+mjQuu TYew== X-Gm-Message-State: AOJu0YzGpsaYK5InDKYWbKDW0kDYGYoamBpjVjdaVd/ZY4KsYiICEZrV Ot+7drzoMzgIwmTTDL9Q/p18fDvCqQyqNjcrSaGOvvjRo0etbo7cfuyuXjjZtbKgERa5eGXUrhq INyQtRD4= X-Google-Smtp-Source: AGHT+IGEv0iJsAvQ7CCkt19oWYWKV+GMPWfDMtClFqJP+0J7PdiKlT4Oj2jGFEkmsc1a4cNZ4Rgr2w== X-Received: by 2002:a2e:b1c8:0:b0:2ef:564c:9b6b with SMTP id 38308e7fff4ca-2f15aaa848bmr89674301fa.23.1722865867656; Mon, 05 Aug 2024 06:51:07 -0700 (PDT) Received: from chenghaoyang-germany.c.googlers.com.com (49.222.77.34.bc.googleusercontent.com. [34.77.222.49]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-428e8d6555fsm137146675e9.26.2024.08.05.06.51.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 05 Aug 2024 06:51:07 -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 v8 4/8] libcamera: pipeline: Add test pattern for VirtualPipelineHandler Date: Mon, 5 Aug 2024 13:48:35 +0000 Message-ID: <20240805135104.139932-5-chenghaoyang@google.com> X-Mailer: git-send-email 2.46.0.rc2.264.g509ed76dc8-goog In-Reply-To: <20240805135104.139932-1-chenghaoyang@google.com> References: <20240805135104.139932-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 74eb8c7a..357fdd03 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -192,10 +192,14 @@ int PipelineHandlerVirtual::exportFrameBuffers( return dmaBufAllocator_.exportBuffers(config.bufferCount, planeSizes, 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; } @@ -207,9 +211,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); @@ -241,11 +250,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_; };