From patchwork Thu Oct 8 21:11:46 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Robert Mader X-Patchwork-Id: 28419 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 BC483C3305 for ; Thu, 8 Oct 2026 21:12:11 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5473E68DEF; Thu, 8 Oct 2026 23:12:11 +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="USpOhsn5"; 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 0C17D68A07 for ; Thu, 8 Oct 2026 23:12:08 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; t=1791493925; cv=none; d=zohomail.com; s=zohoarc; b=Y8P0GMn6sHcbJo794kYCnIwUB6eX64dGWeERZ7hMvcT9XmCSKKQHhDmdC9Nzws0k8gMmSd12/aSY+n3cJ2b4NlDXJyn7N15eDv84zjfxKOHNN9CFhNYOTtXZpXmtRMhuQSXCAcPT4KxwawefIZVRbMA56hOWHCbR1VI/20YEhFA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1791493925; 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=HlwzqNZxV8BeLYIzrgOvzGwRneQuQKJbnngjNwC0/8g=; b=FMqCwl91Yg2dmLVDvP4pKy7z1w5zBxfk2K6tAZLP25+tZoct3Xp361T9JBE4JZ0Hdz+P6PLgnnICPIb/b1DQSyPkRTA6hWwXxheL9Wn9HjO1f7YoR///AIY39lfXK9PmX/dMk8/QGFTVBbtsjzCjn3KOQ43b2IJetfoEDgpszd4= 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=1791493925; 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=HlwzqNZxV8BeLYIzrgOvzGwRneQuQKJbnngjNwC0/8g=; b=USpOhsn5TWudUOc43kLDPQWDrqzfRzY3iq8T8LYCRP/wU6gq04zabzzJ+UoAZ5Sj Uer3ohitIAEsLpnNQKafRRH9qGd5rvF0h9zO1bUFav8FNdLXZFCd/qcD00fHgFFbTZU j6/eRPJQxGk2MUSojm2lwDz0lLPrAE9wcaSEO4Gw= Received: by smtp.zohomail.com with SMTPS id 1791493924150713.6893504032488; Thu, 8 Oct 2026 14:12:04 -0700 (PDT) From: Robert Mader To: libcamera-devel@lists.libcamera.org Cc: Robert Mader Subject: [PATCH 1/4] virtual: Fill alpha channels Date: Thu, 8 Oct 2026 23:11:46 +0200 Message-ID: <20261008211149.51765-2-robert.mader@collabora.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261008211149.51765-1-robert.mader@collabora.com> References: <20261008211149.51765-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 --- 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 Thu Oct 8 21:11:47 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Robert Mader X-Patchwork-Id: 28421 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 5B8D1C3307 for ; Thu, 8 Oct 2026 21:12:17 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D70B168A90; Thu, 8 Oct 2026 23:12:16 +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="EbLkUkUH"; 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 0319A68A07 for ; Thu, 8 Oct 2026 23:12:10 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; t=1791493927; cv=none; d=zohomail.com; s=zohoarc; b=WG3KhkeeLwFS3UCS8PyZlVQFOYJQzGBJqwsGWkQIRvkoVp7SU+/aQ4BKojvVfnPmDnfbd5OjMPbHWBb6Q3datuEch5doXgti/LbIjTVn5jaAAUg7iAx8VSFCxIAgWBMspg4s2mtWxjMHIkOPeeUQmyUIUwZPr7HsObvDvdrC6Xk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1791493927; 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=+RLRb18km12zerm7N7Yf39BosKMbo0w6Mv3N8wuENOo=; b=VisYs7Iv0MPVo1w4gZHx1i0hj48YHFmzyEIaSYWRS2kMv3unUiwzgZW3b8T+7t6eMBxC+T4cuWrIXRE1T1Jp3v2M1WSx6MqeuAxVEBM/sZdNFv/kyBmCbyl9LW5L3OGDwsw335cDoLcqvdeBIefei9KH9zgyL+n9HIdjthLPUuI= 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=1791493927; 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=+RLRb18km12zerm7N7Yf39BosKMbo0w6Mv3N8wuENOo=; b=EbLkUkUHMUaT+3zxwLVO0YRKKkBqLo8h8HndPS80smAX0SclfParXgo5fyp6W6kR LuTDEjEIgDWwZCDyiSXRIY+AGhlBpo7DPxHQ1v32MFTve5e4xuZ5HIQIxqaJApi+Wh3 x+mL5EY6xCw8MQocd+ycWh0MLsJAUgAoAa0SGZFQ= Received: by smtp.zohomail.com with SMTPS id 1791493925584769.6358655228726; Thu, 8 Oct 2026 14:12:05 -0700 (PDT) From: Robert Mader To: libcamera-devel@lists.libcamera.org Cc: Robert Mader Subject: [PATCH 2/4] virtual: Support more common pixel formats Date: Thu, 8 Oct 2026 23:11:47 +0200 Message-ID: <20261008211149.51765-3-robert.mader@collabora.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261008211149.51765-1-robert.mader@collabora.com> References: <20261008211149.51765-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 | 56 ++++++++++++++++--- .../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 | 39 +++++++++---- 6 files changed, 121 insertions(+), 27 deletions(-) diff --git a/src/libcamera/pipeline/virtual/frame_generator.h b/src/libcamera/pipeline/virtual/frame_generator.h index a0658c45cd82..70cd92907b63 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..dcef90702e18 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,8 +126,10 @@ void ImageFrameGenerator::configure(const Size &size) } } -int ImageFrameGenerator::generateFrame(const Size &size, const FrameBuffer *buffer) +int ImageFrameGenerator::generateFrame(const StreamConfiguration *cfg, const FrameBuffer *buffer) { + auto size = cfg->size; + ASSERT(!scaledFrameDatas_.empty()); /* @@ -146,11 +150,49 @@ int ImageFrameGenerator::generateFrame(const Size &size, const FrameBuffer *buff 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; + const PixelFormatInfo &info = PixelFormatInfo::info(cfg->pixelFormat); + 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) << "NV12 conversation failed with " << ret; /* Proceed to the next image every 4 frames */ /* \todo Consider setting the frameRepeat in the config file */ @@ -158,7 +200,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..575900815d9e 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..d8cdc1c91c24 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; 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; + const PixelFormatInfo &info = PixelFormatInfo::info(cfg->pixelFormat); + 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) << "ARGB 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..5022eaff3d30 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..565b66477363 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -148,7 +148,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,16 +202,28 @@ CameraConfiguration::Status VirtualCameraConfiguration::validate() adjusted = true; } - if (cfg.pixelFormat != formats::NV12) { + if (cfg.pixelFormat != formats::NV12 && + cfg.pixelFormat != formats::YUV420 && + cfg.pixelFormat != formats::ABGR8888 && + cfg.pixelFormat != formats::XBGR8888) { cfg.pixelFormat = formats::NV12; status = Adjusted; adjusted = true; } - if (cfg.colorSpace != ColorSpace::Smpte170m) { - cfg.colorSpace = ColorSpace::Smpte170m; - status = Adjusted; - adjusted = true; + const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); + if (info.colourEncoding == PixelFormatInfo::ColourEncodingRGB) { + if (cfg.colorSpace != ColorSpace::Srgb) { + cfg.colorSpace = ColorSpace::Srgb; + status = Adjusted; + adjusted = true; + } + } else if (info.colourEncoding == PixelFormatInfo::ColourEncodingYUV) { + if (cfg.colorSpace != ColorSpace::Smpte170m) { + cfg.colorSpace = ColorSpace::Smpte170m; + status = Adjusted; + adjusted = true; + } } if (validateColorSpaces() == Adjusted) { @@ -223,7 +235,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); @@ -274,13 +285,19 @@ PipelineHandlerVirtual::generateConfiguration(Camera *camera, return {}; } + std::vector outputFormats = { formats::NV12, + formats::YUV420, + formats::ABGR8888, + formats::XBGR8888 }; + 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.front(); cfg.size = data->config_.maxResolutionSize; cfg.bufferCount = VirtualCameraConfiguration::kBufferCount; cfg.colorSpace = ColorSpace::Smpte170m; From patchwork Thu Oct 8 21:11:48 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Robert Mader X-Patchwork-Id: 28420 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 8103CC32F4 for ; Thu, 8 Oct 2026 21:12:14 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0EA9568E2F; Thu, 8 Oct 2026 23:12:14 +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="Xdr2LYVj"; 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 B303168A06 for ; Thu, 8 Oct 2026 23:12:10 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; t=1791493927; cv=none; d=zohomail.com; s=zohoarc; b=I0kKrL0+WPBGtVWMV4/IbdHlZWp0a5YWs6sAAEODQhFYIiSubC/hgZ/EEJIpXMCQrBN8Q+oQ+Q7lLDat4LmBG/0PgqThHg/qANSPZMDN3klh2GT7c0LNjWvyoK2ufS/xaqFF24vjTZnQBZ4VtV4UNs8qL7iswRuGYpATKNiDFvQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1791493927; 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=RjJBswJ9RZit1pUO1U9lj4r0088h21tYO9crdxHAI9o=; b=dK95ZmQABK6ArDjjLu9mfNt6Zd479Yf1CAFvh2RhhmC/sxZYyurnsJDeE4lg65J2hHxEOGjgg2OGJvqmzPYMvUSb3W+XB7cvtgtoHIVRSkRGE9KZwPx9TSNm+Uclfc1DGrtgElqmUaTTC2gDbShx1mEnMOA8JyPD+NFGmvjiUVg= 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=1791493927; 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=RjJBswJ9RZit1pUO1U9lj4r0088h21tYO9crdxHAI9o=; b=Xdr2LYVj/kuFtcCYW0oCWawEh/OAaL1Oq62xAkPawovRlmek8Ltcz/h3TIGRNStE EpWtfErp9IVthfXPh5E6PcKEiyA8O5hw+Mn3NBduUwHissuHecZ91i5dZ+v3Nznv/cQ BWMrt5D0YKwfshMlsCZ1kJsZ7hF7rcc9+ockhS1Y= Received: by smtp.zohomail.com with SMTPS id 1791493926789660.7263372283858; Thu, 8 Oct 2026 14:12:06 -0700 (PDT) From: Robert Mader To: libcamera-devel@lists.libcamera.org Cc: Robert Mader Subject: [PATCH 3/4] virtual: Validate stride and frameSize Date: Thu, 8 Oct 2026 23:11:48 +0200 Message-ID: <20261008211149.51765-4-robert.mader@collabora.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261008211149.51765-1-robert.mader@collabora.com> References: <20261008211149.51765-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 565b66477363..349cb88a3f05 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -231,13 +231,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 Thu Oct 8 21:11:49 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Robert Mader X-Patchwork-Id: 28422 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 1F2D9C3305 for ; Thu, 8 Oct 2026 21:12:18 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6C72868E2B; Thu, 8 Oct 2026 23:12:17 +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="N9jMyC+k"; 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 F3B0C68A06 for ; Thu, 8 Oct 2026 23:12:12 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; t=1791493929; cv=none; d=zohomail.com; s=zohoarc; b=L2/TVVQ/pjQQ2F2TXi6si6T45EuG7gUtg0eHocwjcbYmo4v/f1NC2kZ9QbpYNrdEPIb9u6uTGnQNmuluQf0nfx8QOPPyGT65LGXdcT7wSWfRDP4X/8aBg+ppM2RE2z3hKg9+cXbfmqJGb+zFR4PrFUGg2XP40BfAKEntc4k5cwQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1791493929; 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=PSx4y6rU1EVAHoxVbejfaVjLOgCmkloCi4cIdxvkrlo=; b=OLRb6Cz3fmFS483KkH0KOgltv4PgfJJxh7gT3CHFfFdn12nPymncODH0BmZuOwC/cjdIeiYXDAuCnHGnODzejqB61pPL1e/U2HDvcY3T9sw8Ghqv0Xu97TDprIMnUA/PdCY8Dv6RvF+0QZqZLMbAgu2Fhmi+lPVMuLfD8mJBuHw= 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=1791493929; 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=PSx4y6rU1EVAHoxVbejfaVjLOgCmkloCi4cIdxvkrlo=; b=N9jMyC+k/cWU2CL6/xYVfnxMbVObQojsOO+DIXPzGBx0sF4hcZ0MGdqKTHcoy+CP Lyu52lsNvQFdU9jc9UOiLXQJ0WJNeESXHDt5SkV9Qq3OZz74MGujUD0H0t/lk4mfFr7 f0XYjIDY4Yqu571vfXeQ+9QIHE1fYVanh5hxtvYg= Received: by smtp.zohomail.com with SMTPS id 1791493928248228.8795225111628; Thu, 8 Oct 2026 14:12:08 -0700 (PDT) From: Robert Mader To: libcamera-devel@lists.libcamera.org Cc: Robert Mader Subject: [PATCH 4/4] virtual: Align strides to 256 bytes Date: Thu, 8 Oct 2026 23:11:49 +0200 Message-ID: <20261008211149.51765-5-robert.mader@collabora.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261008211149.51765-1-robert.mader@collabora.com> References: <20261008211149.51765-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 70cd92907b63..31eea6392008 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 dcef90702e18..685c11a61029 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, @@ -159,9 +154,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: @@ -170,11 +167,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: @@ -184,7 +184,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 d8cdc1c91c24..1892854d63d3 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 349cb88a3f05..ac6c5ca67aa8 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -231,8 +231,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; @@ -342,7 +344,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); }