From patchwork Fri Oct 9 19:40:04 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Robert Mader X-Patchwork-Id: 28430 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 D46B2BDCB2 for ; Fri, 9 Oct 2026 19:40:35 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8A74568A21; Fri, 9 Oct 2026 21:40:35 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=collabora.com header.i=robert.mader@collabora.com header.b="jyPmbLXq"; dkim-atps=neutral Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CAE2C68A13 for ; Fri, 9 Oct 2026 21:40:33 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; t=1791574830; cv=none; d=zohomail.com; s=zohoarc; b=bQlKonPIpjE9syHXQdruqmhZlTU8QMUJdgAwyFJCO4kRTUvR+7SrZQNMECDq6RkOMW6cx1D4mNOFGtyi1OjCQ9+93nQlMtuK+570qYK7s9a3C8IKOMuGyc3LvKxp6vZzd1mH5BD2xGKVgdi7m3vUjLTaGHh413ZLeeQizOv5lOk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1791574830; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:Subject:Subject:To:To:Message-Id:Reply-To; bh=0SCss57sCwknN2/GCYARPpt4ehPzBJOLo6rMcAnPDbQ=; b=ESr9mf6uTFEhqBBwk4SytgjpXmPrkc7BBN0//kIXyDE0zwXryCCHVBVZQVx9RENRAtOMjYt3YVzCFalzuC4RkfLiYb8ZFHJ+90zREtePjDvTvdzHWnXx7HnYaHLt6a+IxcF+11n4Sb/OB/FpuVm0qPmH7alHWQaQ4PvjCQya8tE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=robert.mader@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1791574830; s=zohomail; d=collabora.com; i=robert.mader@collabora.com; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Reply-To; bh=0SCss57sCwknN2/GCYARPpt4ehPzBJOLo6rMcAnPDbQ=; b=jyPmbLXq0SeIYq5vOUCcWp0cMi7bhS8B0en5aR2l2GakOU9zyam4k1V0N/1Z6FO7 a3Fgenn+hqJ0pfQoRJayLA2wWCMXIAk/IC12ARcua8B9nxHi7JV3+buZTSyrW9opPmc /o0ni9SI9f4GVv3R1ccsgkXvZ+9zC4EhizN4xu5Y= Received: by smtp.zohomail.com with SMTPS id 1791574829997535.7908148987516; Fri, 9 Oct 2026 12:40:29 -0700 (PDT) From: Robert Mader To: libcamera-devel@lists.libcamera.org Cc: Robert Mader , =?utf-8?q?Barnab=C3=A1s_P?= =?utf-8?b?xZFjemU=?= Subject: [PATCH v2 1/4] virtual: Fill alpha channels Date: Fri, 9 Oct 2026 21:40:04 +0200 Message-ID: <20261009194007.23175-2-robert.mader@collabora.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261009194007.23175-1-robert.mader@collabora.com> References: <20261009194007.23175-1-robert.mader@collabora.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" While libyuv::ARGBToNV12() seems to ignore the alpha values, let's set them to fully opaque to avoid potential glitches going forward. Signed-off-by: Robert Mader Reviewed-by: Barnabás Pőcze --- src/libcamera/pipeline/virtual/test_pattern_generator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcamera/pipeline/virtual/test_pattern_generator.cpp b/src/libcamera/pipeline/virtual/test_pattern_generator.cpp index 1be3847d63a4..7b53e8b32327 100644 --- a/src/libcamera/pipeline/virtual/test_pattern_generator.cpp +++ b/src/libcamera/pipeline/virtual/test_pattern_generator.cpp @@ -100,7 +100,7 @@ void ColorBarsGenerator::configure(const Size &size) *buf++ = kColorBar[index][2]; /* B */ *buf++ = kColorBar[index][1]; /* G */ *buf++ = kColorBar[index][0]; /* R */ - *buf++ = 0x00; /* A */ + *buf++ = 0xff; /* A */ } } } @@ -127,7 +127,7 @@ void DiagonalLinesGenerator::configure(const Size &size) *buf++ = kColorBar[index][2]; /* B */ *buf++ = kColorBar[index][1]; /* G */ *buf++ = kColorBar[index][0]; /* R */ - *buf++ = 0x00; /* A */ + *buf++ = 0xff; /* A */ } } } From patchwork Fri Oct 9 19:40:05 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Robert Mader X-Patchwork-Id: 28431 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 67F7ABDCB2 for ; Fri, 9 Oct 2026 19:40:39 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 21D2568A28; Fri, 9 Oct 2026 21:40:39 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=collabora.com header.i=robert.mader@collabora.com header.b="Pk6D2XQX"; dkim-atps=neutral Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3BC1C68A20 for ; Fri, 9 Oct 2026 21:40:37 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; t=1791574834; cv=none; d=zohomail.com; s=zohoarc; b=hvHG7Yw+r5GC2hD+s+LBU2LYm6LrF1rs7AirxorolosQV2FMPZt4Etz96v1ynaOOEtEcGqJQhaJP3i5POS4YBpx+iWaBB7sUhYvDR/GxmBX1vJNEBFT3r+b/Y7e1Prmj8o+j2VnCv68CKAfERkCih1PSvlNGdOgAmbF/va8FAE4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1791574834; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:Subject:Subject:To:To:Message-Id:Reply-To; bh=gIGwKI7cqzWMX9ablC8ueAgEoPs+lGro/fqgPBIVGNM=; b=Wen6+tgnN3kwupi7sfrORh6RzC0PZ8QkJdP1WskIIDDaJV3DPIKo5dhXCy/NOmpTsHKNItk1U0Wl03DLmgIVCBe/rNX2SNyAZIjIm+zQxvg5Zmy3ocPgBOXpZEplPVFepW+w5jqlDClD0GkGrEUCevM/zHnjO2dDddpG30nm/6M= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=robert.mader@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1791574834; s=zohomail; d=collabora.com; i=robert.mader@collabora.com; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=gIGwKI7cqzWMX9ablC8ueAgEoPs+lGro/fqgPBIVGNM=; b=Pk6D2XQXAJEcCZE/TtIzfb8wV22tkM7i+WKqHWCPKJfe4K53mcTn0/U5EVhsBKc+ DgQB/fS5rRb9lEalWWtJuiur6Wta5XTJrympToSPXCxIATKTPqmrHlMsB9755QYMBrO ErIVEalt0qhPJLeALfkmQhRgfaThh3RMWl8mYNvI= Received: by smtp.zohomail.com with SMTPS id 1791574833057103.8861709594621; Fri, 9 Oct 2026 12:40:33 -0700 (PDT) From: Robert Mader To: libcamera-devel@lists.libcamera.org Cc: Robert Mader Subject: [PATCH v2 2/4] virtual: Support more common pixel formats Date: Fri, 9 Oct 2026 21:40:05 +0200 Message-ID: <20261009194007.23175-3-robert.mader@collabora.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261009194007.23175-1-robert.mader@collabora.com> References: <20261009194007.23175-1-robert.mader@collabora.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" While NV12 is the most common native format for hardware video encoders, YUV420 is the equivalent for most software video encoders. Thus supporting both is great for testing. Similarly having some RGB formats with and without alpha can be very useful, especially as the swISP is currently limited to such formats. This is a polished version of a hack that was used to help debugging and testing https://webrtc-review.googlesource.com/c/src/+/491680 - and so helpful with that that I'd like to see it upstream. Signed-off-by: Robert Mader --- .../pipeline/virtual/frame_generator.h | 3 +- .../virtual/image_frame_generator.cpp | 55 ++++++++++++++++--- .../pipeline/virtual/image_frame_generator.h | 2 +- .../virtual/test_pattern_generator.cpp | 46 ++++++++++++++-- .../pipeline/virtual/test_pattern_generator.h | 2 +- src/libcamera/pipeline/virtual/virtual.cpp | 41 ++++++++++---- 6 files changed, 123 insertions(+), 26 deletions(-) diff --git a/src/libcamera/pipeline/virtual/frame_generator.h b/src/libcamera/pipeline/virtual/frame_generator.h index a0658c45cd82..fd34a91a5e50 100644 --- a/src/libcamera/pipeline/virtual/frame_generator.h +++ b/src/libcamera/pipeline/virtual/frame_generator.h @@ -9,6 +9,7 @@ #include #include +#include namespace libcamera { @@ -19,7 +20,7 @@ public: virtual void configure(const Size &size) = 0; - virtual int generateFrame(const Size &size, + virtual int generateFrame(const StreamConfiguration &cfg, const FrameBuffer *buffer) = 0; protected: diff --git a/src/libcamera/pipeline/virtual/image_frame_generator.cpp b/src/libcamera/pipeline/virtual/image_frame_generator.cpp index 1853bddeca27..5c56db96d8e2 100644 --- a/src/libcamera/pipeline/virtual/image_frame_generator.cpp +++ b/src/libcamera/pipeline/virtual/image_frame_generator.cpp @@ -14,7 +14,9 @@ #include +#include #include "libcamera/internal/dma_buf_allocator.h" +#include "libcamera/internal/formats.h" #include "libcamera/internal/mapped_framebuffer.h" #include "libyuv/convert.h" @@ -124,7 +126,7 @@ void ImageFrameGenerator::configure(const Size &size) } } -int ImageFrameGenerator::generateFrame(const Size &size, const FrameBuffer *buffer) +int ImageFrameGenerator::generateFrame(const StreamConfiguration &cfg, const FrameBuffer *buffer) { ASSERT(!scaledFrameDatas_.empty()); @@ -141,16 +143,55 @@ int ImageFrameGenerator::generateFrame(const Size &size, const FrameBuffer *buff MappedFrameBuffer::MapFlag::Write); const auto &planes = mappedFrameBuffer.planes(); + auto size = cfg.size; + const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); /* Loop only around the number of images available */ frameIndex_ %= imageFrameDatas_.size(); /* Write the scaledY and scaledUV to the mapped frame buffer */ - libyuv::NV12Copy(scaledFrameDatas_[frameIndex_].Y.get(), size.width, - scaledFrameDatas_[frameIndex_].UV.get(), size.width, - planes[0].data(), size.width, - planes[1].data(), size.width, - size.width, size.height); + int ret; + switch (cfg.pixelFormat) { + case formats::NV12: + ret = libyuv::NV12Copy(scaledFrameDatas_[frameIndex_].Y.get(), + size.width, + scaledFrameDatas_[frameIndex_].UV.get(), + size.width, + planes[0].data(), + info.stride(size.width, 0), + planes[1].data(), + info.stride(size.width, 1), + size.width, size.height); + break; + case formats::YUV420: + ret = libyuv::NV12ToI420(scaledFrameDatas_[frameIndex_].Y.get(), + size.width, + scaledFrameDatas_[frameIndex_].UV.get(), + size.width, + planes[0].data(), + info.stride(size.width, 0), + planes[1].data(), + info.stride(size.width, 1), + planes[2].data(), + info.stride(size.width, 2), + size.width, size.height); + break; + case formats::ABGR8888: + case formats::XBGR8888: + ret = libyuv::NV12ToARGB(scaledFrameDatas_[frameIndex_].Y.get(), + size.width, + scaledFrameDatas_[frameIndex_].UV.get(), + size.width, + planes[0].data(), + info.stride(size.width, 0), + size.width, size.height); + break; + default: + LOG(Virtual, Error) << "Unsupported format"; + return -1; + } + if (ret != 0) + LOG(Virtual, Error) << "Conversation failed with " << ret; /* Proceed to the next image every 4 frames */ /* \todo Consider setting the frameRepeat in the config file */ @@ -158,7 +199,7 @@ int ImageFrameGenerator::generateFrame(const Size &size, const FrameBuffer *buff if (parameter_ % frameRepeat == 0) frameIndex_++; - return 0; + return ret; } /* diff --git a/src/libcamera/pipeline/virtual/image_frame_generator.h b/src/libcamera/pipeline/virtual/image_frame_generator.h index 42a077ba803c..19b6fe38654e 100644 --- a/src/libcamera/pipeline/virtual/image_frame_generator.h +++ b/src/libcamera/pipeline/virtual/image_frame_generator.h @@ -37,7 +37,7 @@ private: }; void configure(const Size &size) override; - int generateFrame(const Size &size, const FrameBuffer *buffer) override; + int generateFrame(const StreamConfiguration &cfg, const FrameBuffer *buffer) override; std::vector imageFrameDatas_; std::vector scaledFrameDatas_; diff --git a/src/libcamera/pipeline/virtual/test_pattern_generator.cpp b/src/libcamera/pipeline/virtual/test_pattern_generator.cpp index 7b53e8b32327..4cde32176661 100644 --- a/src/libcamera/pipeline/virtual/test_pattern_generator.cpp +++ b/src/libcamera/pipeline/virtual/test_pattern_generator.cpp @@ -11,7 +11,9 @@ #include +#include #include "libcamera/internal/dma_buf_allocator.h" +#include "libcamera/internal/formats.h" #include "libcamera/internal/mapped_framebuffer.h" #include @@ -42,7 +44,7 @@ LOG_DECLARE_CATEGORY(Virtual) static const unsigned int kARGBSize = 4; -int TestPatternGenerator::generateFrame(const Size &size, +int TestPatternGenerator::generateFrame(const StreamConfiguration &cfg, const FrameBuffer *buffer) { /* @@ -58,16 +60,48 @@ int TestPatternGenerator::generateFrame(const Size &size, MappedFrameBuffer::MapFlag::Write); const auto &planes = mappedFrameBuffer.planes(); + const auto size = cfg.size; + const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); rotateLeft1Column(size, template_.get()); /* Convert the template_ to the frame buffer */ - int ret = libyuv::ARGBToNV12(template_.get(), size.width * kARGBSize, - planes[0].data(), size.width, - planes[1].data(), size.width, - size.width, size.height); + int ret; + switch (cfg.pixelFormat) { + case formats::NV12: + ret = libyuv::ARGBToNV12(template_.get(), + size.width * kARGBSize, + planes[0].data(), + info.stride(size.width, 0), + planes[1].data(), + info.stride(size.width, 1), + size.width, size.height); + break; + case formats::YUV420: + ret = libyuv::ARGBToI420(template_.get(), + size.width * kARGBSize, + planes[0].data(), + info.stride(size.width, 0), + planes[1].data(), + info.stride(size.width, 1), + planes[2].data(), + info.stride(size.width, 2), + size.width, size.height); + break; + case formats::ABGR8888: + case formats::XBGR8888: + ret = libyuv::ARGBCopy(template_.get(), + size.width * kARGBSize, + planes[0].data(), + info.stride(size.width, 0), + size.width, size.height); + break; + default: + LOG(Virtual, Error) << "Unsupported format"; + return -1; + } if (ret != 0) - LOG(Virtual, Error) << "ARGBToNV12() failed with " << ret; + LOG(Virtual, Error) << "Conversation failed with " << ret; return ret; } diff --git a/src/libcamera/pipeline/virtual/test_pattern_generator.h b/src/libcamera/pipeline/virtual/test_pattern_generator.h index 2a51bd31abdf..9f72d652ab2c 100644 --- a/src/libcamera/pipeline/virtual/test_pattern_generator.h +++ b/src/libcamera/pipeline/virtual/test_pattern_generator.h @@ -24,7 +24,7 @@ enum class TestPattern : char { class TestPatternGenerator : public FrameGenerator { public: - int generateFrame(const Size &size, const FrameBuffer *buffer) override; + int generateFrame(const StreamConfiguration &cfg, const FrameBuffer *buffer) override; protected: /* Buffer of test pattern template */ diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp index e25dd85a7391..7ed2d0b7f596 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -47,6 +47,13 @@ LOG_DEFINE_CATEGORY(Virtual) namespace { +constexpr PixelFormat outputFormats[] = { + formats::NV12, + formats::YUV420, + formats::ABGR8888, + formats::XBGR8888, +}; + uint64_t currentTimestamp() { const auto now = std::chrono::steady_clock::now(); @@ -148,7 +155,7 @@ void VirtualCameraData::processRequest(Request *request) found = true; if (streamConfig.frameGenerator->generateFrame( - stream->configuration().size, buffer)) + stream->configuration(), buffer)) fmd.status = FrameMetadata::Status::FrameError; bufferCompleted.emit(buffer); @@ -202,17 +209,31 @@ CameraConfiguration::Status VirtualCameraConfiguration::validate() adjusted = true; } - if (cfg.pixelFormat != formats::NV12) { - cfg.pixelFormat = formats::NV12; + if (std::find(std::begin(outputFormats), + std::end(outputFormats), + cfg.pixelFormat) == std::end(outputFormats)) { + cfg.pixelFormat = outputFormats[0]; status = Adjusted; adjusted = true; } - if (cfg.colorSpace != ColorSpace::Smpte170m) { - cfg.colorSpace = ColorSpace::Smpte170m; + const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); + std::optional colorSpace; + switch (info.colourEncoding) { + case PixelFormatInfo::ColourEncodingRGB: + colorSpace = ColorSpace::Srgb; + break; + case PixelFormatInfo::ColourEncodingYUV: + colorSpace = ColorSpace::Smpte170m; + break; + default: + ASSERT(false); + } + if (cfg.colorSpace && cfg.colorSpace != colorSpace) { status = Adjusted; adjusted = true; } + cfg.colorSpace = colorSpace; if (validateColorSpaces() == Adjusted) { status = Adjusted; @@ -223,7 +244,6 @@ CameraConfiguration::Status VirtualCameraConfiguration::validate() LOG(Virtual, Info) << "Stream configuration adjusted to " << cfg.toString(); - const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); cfg.stride = info.stride(cfg.size.width, 0, 1); cfg.frameSize = info.frameSize(cfg.size, 1); @@ -275,12 +295,13 @@ PipelineHandlerVirtual::generateConfiguration(Camera *camera, } std::map> streamFormats; - PixelFormat pixelFormat = formats::NV12; - streamFormats[pixelFormat] = { { data->config_.minResolutionSize, - data->config_.maxResolutionSize } }; + for (auto pixelFormat : outputFormats) { + streamFormats[pixelFormat] = { { data->config_.minResolutionSize, + data->config_.maxResolutionSize } }; + } StreamFormats formats(streamFormats); StreamConfiguration cfg(formats); - cfg.pixelFormat = pixelFormat; + cfg.pixelFormat = outputFormats[0]; cfg.size = data->config_.maxResolutionSize; cfg.bufferCount = VirtualCameraConfiguration::kBufferCount; cfg.colorSpace = ColorSpace::Smpte170m; From patchwork Fri Oct 9 19:40:06 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Robert Mader X-Patchwork-Id: 28432 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 241C1BDCB2 for ; Fri, 9 Oct 2026 19:40:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DF32868A3C; Fri, 9 Oct 2026 21:40:40 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=collabora.com header.i=robert.mader@collabora.com header.b="Px1yT7lY"; dkim-atps=neutral Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6BC1168A36 for ; Fri, 9 Oct 2026 21:40:38 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; t=1791574834; cv=none; d=zohomail.com; s=zohoarc; b=iuWtFgudK4m5mKpQQhpchWOb8cQBXJR/nalYSNFAPf1wGXJIOzwIfAXx2s9oCU39mYeRrpfUJVVL7pSfe0iSd/LnAcJKVVNPcbOmcuBJZSD+jApDsxGoYuYSGji/KhFAHHnsBCJ4aFgaYuqy6Vx9V6s0AG71plME/W/cNqvk6QI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1791574834; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:Subject:Subject:To:To:Message-Id:Reply-To; bh=AWRDyyHQrD707ihL5DnV8Cawmazt7IE9MJNG9Kln4Qs=; b=MbC+M3RJyqbRiiQMV8kgyFR3Pp4fWMHJaWQtRXdaiuTQwOWQQZqzjDD5Xr4NGgLKZMEC2YjgGB5UpIzeoToHLbehrj7taBJa1udVL14ngPchIssemO+CR1l8n0U1rD3scygs4swLEqrEVqDD/CaY1UP1IiLQflpd0+YNrzbvshs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=robert.mader@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1791574834; s=zohomail; d=collabora.com; i=robert.mader@collabora.com; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=AWRDyyHQrD707ihL5DnV8Cawmazt7IE9MJNG9Kln4Qs=; b=Px1yT7lYZ3k0VS9AE8lFpZtZXlTkpbEOu+99aNBJo++hIs/SuGCFXTjhOpZng4fg usmWJBeXvrTMiOL+dHojdg2jdAU0d7WRLSOzVzfGOk6X5KznKEJWWsD70Le+q3wSsgk COIFoHwhKXct2s7fJGGzhdSok7U4mxq0J9oy5Fxs= Received: by smtp.zohomail.com with SMTPS id 179157483448942.570832211052675; Fri, 9 Oct 2026 12:40:34 -0700 (PDT) From: Robert Mader To: libcamera-devel@lists.libcamera.org Cc: Robert Mader Subject: [PATCH v2 3/4] virtual: Validate stride and frameSize Date: Fri, 9 Oct 2026 21:40:06 +0200 Message-ID: <20261009194007.23175-4-robert.mader@collabora.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261009194007.23175-1-robert.mader@collabora.com> References: <20261009194007.23175-1-robert.mader@collabora.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" If a client requested a specific stride that's incompatible with what we'll produce, let the client know. Signed-off-by: Robert Mader --- src/libcamera/pipeline/virtual/virtual.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp index 7ed2d0b7f596..6e97981b0f40 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -240,13 +240,20 @@ CameraConfiguration::Status VirtualCameraConfiguration::validate() adjusted = true; } + auto stride = info.stride(cfg.size.width, 0); + auto frameSize = info.frameSize(cfg.size); + if ((cfg.stride && cfg.stride != stride) || + (cfg.frameSize && cfg.frameSize != frameSize)) { + status = Adjusted; + adjusted = true; + } + cfg.stride = stride; + cfg.frameSize = frameSize; + if (adjusted) LOG(Virtual, Info) << "Stream configuration adjusted to " << cfg.toString(); - cfg.stride = info.stride(cfg.size.width, 0, 1); - cfg.frameSize = info.frameSize(cfg.size, 1); - cfg.bufferCount = VirtualCameraConfiguration::kBufferCount; } From patchwork Fri Oct 9 19:40:07 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Robert Mader X-Patchwork-Id: 28433 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 822ACC3305 for ; Fri, 9 Oct 2026 19:40:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7A1B868A36; Fri, 9 Oct 2026 21:40:41 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=collabora.com header.i=robert.mader@collabora.com header.b="WNpTXbXy"; dkim-atps=neutral Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3853468A2B for ; Fri, 9 Oct 2026 21:40:39 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; t=1791574836; cv=none; d=zohomail.com; s=zohoarc; b=ETpS4wxCugm3Rl6pMZ54rNKp8is30svkz3AQdHA3ItfNuOOTJ8QLQFt7ber56tmnrM01zolGXH1Qirc1Biii7ypItdJIrpLFaol6fQ+CbAKAsK33YDDm8YFtN+7mA9kOMHzI2YSXxFzC8kn97U0hL0hRskbdg0SS99VQ5ImBnDI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1791574836; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:Subject:Subject:To:To:Message-Id:Reply-To; bh=mUgINCyECrWqZtk3yx6T4m8p+rnHi01f5hgL1xkhNAk=; b=TvrNQux28rCH8PHnP8mNlmHirnXb0xB5WO290yYw9SemnCfjRFB+L/bvtMwhw6P7id+FJAJx4xC+961eKRS/awbXu9ko13N4E2LsvFRcd/VM95pbc8HPKGwD08xC0woFLpqJSbH1ztiZVag8uQLWtO0mBzpzPpH7Bxz+I8woOjU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=robert.mader@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1791574836; s=zohomail; d=collabora.com; i=robert.mader@collabora.com; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=mUgINCyECrWqZtk3yx6T4m8p+rnHi01f5hgL1xkhNAk=; b=WNpTXbXyvv3VSQl5rv1NfF9dC4TtAk+rXDN6HbcCWwwvlRfsYPPn1apJCqFIuX1Q 7TIx/BBlLcOpzWEhKszrSp8LiRrTFPMNUVG1VhpluGlkjAKz3SosIbyH5Ejofn5Osdv LgSGF+NTgVGA3RhTeU5d7QIFscpyEAp0NOSMY7pI= Received: by smtp.zohomail.com with SMTPS id 1791574835930936.4345989161416; Fri, 9 Oct 2026 12:40:35 -0700 (PDT) From: Robert Mader To: libcamera-devel@lists.libcamera.org Cc: Robert Mader Subject: [PATCH v2 4/4] virtual: Align strides to 256 bytes Date: Fri, 9 Oct 2026 21:40:07 +0200 Message-ID: <20261009194007.23175-5-robert.mader@collabora.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261009194007.23175-1-robert.mader@collabora.com> References: <20261009194007.23175-1-robert.mader@collabora.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" This patch was originally developed to help debugging and testing https://webrtc-review.googlesource.com/c/src/+/491680, however I see some benefits in always doing it: 1. It is common for hardware ISPs to have some stride alignment - even though usually somewhat lower. Using the given alignment should thus help ensure that clients implement stride handling correctly, preventing cases like the one linked above where a major implemention went without that - unnocited - for quite a long time. 2. It makes it more likely that output buffers can be imported by GPUs and display engines. 2. It paves the way for GPU-accelerated frame generators (similar to DebayerEGL), which may become desirable going forward in order to provide high resolution / high framerate / high bit-depth / HDR content. Signed-off-by: Robert Mader --- .../pipeline/virtual/frame_generator.h | 1 + .../virtual/image_frame_generator.cpp | 23 ++++++++++--------- .../virtual/test_pattern_generator.cpp | 18 ++++++++++----- src/libcamera/pipeline/virtual/virtual.cpp | 9 +++++--- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/libcamera/pipeline/virtual/frame_generator.h b/src/libcamera/pipeline/virtual/frame_generator.h index fd34a91a5e50..d575c28a3ce0 100644 --- a/src/libcamera/pipeline/virtual/frame_generator.h +++ b/src/libcamera/pipeline/virtual/frame_generator.h @@ -23,6 +23,7 @@ public: virtual int generateFrame(const StreamConfiguration &cfg, const FrameBuffer *buffer) = 0; + static constexpr unsigned int kStrideAlignment = 256; protected: FrameGenerator() {} }; diff --git a/src/libcamera/pipeline/virtual/image_frame_generator.cpp b/src/libcamera/pipeline/virtual/image_frame_generator.cpp index 5c56db96d8e2..be5633050a11 100644 --- a/src/libcamera/pipeline/virtual/image_frame_generator.cpp +++ b/src/libcamera/pipeline/virtual/image_frame_generator.cpp @@ -110,11 +110,6 @@ void ImageFrameGenerator::configure(const Size &size) std::make_unique(halfSizeWidth * halfSizeHeight * 2); auto &src = imageFrameDatas_[i]; - /* - * \todo Some platforms might enforce stride due to GPU. - * The width needs to be a multiple of the stride to work - * properly for now. - */ libyuv::NV12Scale(src.Y.get(), src.size.width, src.UV.get(), src.size.width, src.size.width, src.size.height, @@ -158,9 +153,11 @@ int ImageFrameGenerator::generateFrame(const StreamConfiguration &cfg, const Fra scaledFrameDatas_[frameIndex_].UV.get(), size.width, planes[0].data(), - info.stride(size.width, 0), + info.stride(size.width, 0, + kStrideAlignment), planes[1].data(), - info.stride(size.width, 1), + info.stride(size.width, 1, + kStrideAlignment), size.width, size.height); break; case formats::YUV420: @@ -169,11 +166,14 @@ int ImageFrameGenerator::generateFrame(const StreamConfiguration &cfg, const Fra scaledFrameDatas_[frameIndex_].UV.get(), size.width, planes[0].data(), - info.stride(size.width, 0), + info.stride(size.width, 0, + kStrideAlignment), planes[1].data(), - info.stride(size.width, 1), + info.stride(size.width, 1, + kStrideAlignment), planes[2].data(), - info.stride(size.width, 2), + info.stride(size.width, 2, + kStrideAlignment), size.width, size.height); break; case formats::ABGR8888: @@ -183,7 +183,8 @@ int ImageFrameGenerator::generateFrame(const StreamConfiguration &cfg, const Fra scaledFrameDatas_[frameIndex_].UV.get(), size.width, planes[0].data(), - info.stride(size.width, 0), + info.stride(size.width, 0, + kStrideAlignment), size.width, size.height); break; default: diff --git a/src/libcamera/pipeline/virtual/test_pattern_generator.cpp b/src/libcamera/pipeline/virtual/test_pattern_generator.cpp index 4cde32176661..9ed5f6e74f84 100644 --- a/src/libcamera/pipeline/virtual/test_pattern_generator.cpp +++ b/src/libcamera/pipeline/virtual/test_pattern_generator.cpp @@ -72,20 +72,25 @@ int TestPatternGenerator::generateFrame(const StreamConfiguration &cfg, ret = libyuv::ARGBToNV12(template_.get(), size.width * kARGBSize, planes[0].data(), - info.stride(size.width, 0), + info.stride(size.width, 0, + kStrideAlignment), planes[1].data(), - info.stride(size.width, 1), + info.stride(size.width, 1, + kStrideAlignment), size.width, size.height); break; case formats::YUV420: ret = libyuv::ARGBToI420(template_.get(), size.width * kARGBSize, planes[0].data(), - info.stride(size.width, 0), + info.stride(size.width, 0, + kStrideAlignment), planes[1].data(), - info.stride(size.width, 1), + info.stride(size.width, 1, + kStrideAlignment), planes[2].data(), - info.stride(size.width, 2), + info.stride(size.width, 2, + kStrideAlignment), size.width, size.height); break; case formats::ABGR8888: @@ -93,7 +98,8 @@ int TestPatternGenerator::generateFrame(const StreamConfiguration &cfg, ret = libyuv::ARGBCopy(template_.get(), size.width * kARGBSize, planes[0].data(), - info.stride(size.width, 0), + info.stride(size.width, 0, + kStrideAlignment), size.width, size.height); break; default: diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp index 6e97981b0f40..a9c1a5a85fc1 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -240,8 +240,10 @@ CameraConfiguration::Status VirtualCameraConfiguration::validate() adjusted = true; } - auto stride = info.stride(cfg.size.width, 0); - auto frameSize = info.frameSize(cfg.size); + auto stride = info.stride(cfg.size.width, 0, + FrameGenerator::kStrideAlignment); + auto frameSize = info.frameSize(cfg.size, + FrameGenerator::kStrideAlignment); if ((cfg.stride && cfg.stride != stride) || (cfg.frameSize && cfg.frameSize != frameSize)) { status = Adjusted; @@ -346,7 +348,8 @@ int PipelineHandlerVirtual::exportFrameBuffers([[maybe_unused]] Camera *camera, std::vector planeSizes; for (size_t i = 0; i < info.numPlanes(); ++i) - planeSizes.push_back(info.planeSize(config.size, i)); + planeSizes.push_back(info.planeSize(config.size, i, + FrameGenerator::kStrideAlignment)); return dmaBufAllocator_.exportBuffers(config.bufferCount, planeSizes, buffers); }