Show a patch.

GET /api/1.1/patches/19013/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 19013,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/19013/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/19013/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20230915-libyuv-convert-v1-7-1e5bcf68adac@baylibre.com>",
    "date": "2023-09-15T07:57:31",
    "name": "[libcamera-devel,RFC,7/7] WIP: android: add YUYV->NV12 format conversion via libyuv",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "f7470ef2f9441e1677d3402ccb48bd6b48db349b",
    "submitter": {
        "id": 153,
        "url": "https://patchwork.libcamera.org/api/1.1/people/153/?format=api",
        "name": "Mattijs Korpershoek",
        "email": "mkorpershoek@baylibre.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/19013/mbox/",
    "series": [
        {
            "id": 4023,
            "url": "https://patchwork.libcamera.org/api/1.1/series/4023/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4023",
            "date": "2023-09-15T07:57:24",
            "name": "android: add YUYV->NV12 conversion via libyuv",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/4023/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/19013/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/19013/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>",
        "X-Original-To": "parsemail@patchwork.libcamera.org",
        "Delivered-To": "parsemail@patchwork.libcamera.org",
        "Received": [
            "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 161AEC32B2\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 15 Sep 2023 07:57:38 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 89BFF62921;\n\tFri, 15 Sep 2023 09:57:37 +0200 (CEST)",
            "from mail-wr1-x433.google.com (mail-wr1-x433.google.com\n\t[IPv6:2a00:1450:4864:20::433])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 75BF862925\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 15 Sep 2023 09:57:32 +0200 (CEST)",
            "by mail-wr1-x433.google.com with SMTP id\n\tffacd0b85a97d-31ad9155414so1650692f8f.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 15 Sep 2023 00:57:32 -0700 (PDT)",
            "from [192.168.1.20] ([2a01:cb19:8704:be00:4f55:bd9d:611a:6c8e])\n\tby smtp.gmail.com with ESMTPSA id\n\tm2-20020a056000174200b0031fe9a47a87sm2506942wrf.112.2023.09.15.00.57.31\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tFri, 15 Sep 2023 00:57:31 -0700 (PDT)"
        ],
        "DKIM-Signature": [
            "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1694764657;\n\tbh=YPidGSqcc2kQoHQvTM7e/QJD48dRt0kqNOJQpbXRc/k=;\n\th=Date:References:In-Reply-To:To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=Y7KDstVR+OqRHZof2Ot2U9a6wAh9TYjObg9caIQPiiyyrr+D1j00q220YRpTTIsHS\n\tHLS3+xgz9cmUoCBaiKrnsXociKy41qXwS4LypAIkz3Rzwg+tZLM25gjbFiu7mfDWpP\n\tuTIV+832jBZYjDLyZNTSMIbBNPvotd5f2azy0KfNOB2Do1H+qxXRv+XoGs4XZwf9cw\n\tu8JwQgDcBr/1K3d7Xb70lIj3Yj4ZwSq5944RYUB+mJCPCVBAQLJJ8vPz/0bwVw+O8o\n\tIGNG0WFi3a0Jmfi0tZ70Pogt6iheKIHlqSBXclYS7bDX3+l0P2UUyLhsV8ngmGOCjq\n\t+FGlyO1yBvP6Q==",
            "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1694764652;\n\tx=1695369452; darn=lists.libcamera.org; \n\th=cc:to:in-reply-to:references:message-id:content-transfer-encoding\n\t:mime-version:subject:date:from:from:to:cc:subject:date:message-id\n\t:reply-to; bh=wDS1MohSUECROpdzIgju5oIQIUyzBwhqYjp/QXcKlUY=;\n\tb=xIJ0+ULC+Q7PajvxI7j2hnZo1RK3zmDHY2nicxgD/8LfynkLzIEbdRD+bBYgNICrrH\n\tQsZxzDP1UuEni51Y4ZitQdO5SzzdulQPrA6iwR78MkN1hSBSrEmZSiXUmjvGpTQajBUv\n\tfYpLYhu/D8j7I8Y3Jy524eaRh62guFw1xmUz+qBSKKlIbwtqmpexY7/UOe6+8EfaS8Sw\n\tq2Qyu078MMkmqxJid8cwtnrbwOVtieP83Xcrk8mUgRRhJrFc/uia+r93T48xKc4M40Zc\n\tAWZ2y3TvMvtKqMf9MWhNEm/cPymKxl8saYBYGV6XX1u3Elz765t+VeKXRLBbaGEh4Cp0\n\t7EyA=="
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected)\n\theader.d=baylibre-com.20230601.gappssmtp.com\n\theader.i=@baylibre-com.20230601.gappssmtp.com header.b=\"xIJ0+ULC\"; \n\tdkim-atps=neutral",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1694764652; x=1695369452;\n\th=cc:to:in-reply-to:references:message-id:content-transfer-encoding\n\t:mime-version:subject:date:from:x-gm-message-state:from:to:cc\n\t:subject:date:message-id:reply-to;\n\tbh=wDS1MohSUECROpdzIgju5oIQIUyzBwhqYjp/QXcKlUY=;\n\tb=xNGjasd7eW35GD/0b1ua6swqisxp9fBbP37n+669davFwSHFup2R8jFG4CuiUUjGVS\n\te+OIZLVxJCl5lKzX5mErI5fdYi92rJsg8+Es9uOuYmoq6j0dXL2UX25G86f5iQxtJUqs\n\tQOVYuc4aMkHxqitpKoHpEO8nJYiZlibXZ2z6VxzBhGMVrtGMqX69Zdgdzgw6ZQ75UDT1\n\ts6P0Q+iKd/eaLymFNt/tf21h2pTMHfq0KWycGvt9xT7WzMHVz0LMbetrf2osLIR+jTRR\n\tTW/RCO2OGn8oRzT3WYLPI0gPqiaPh5w4sezZhHDutxh7B/z8GYcjLe8VmwMbpIoY0rd6\n\tHJUg==",
        "X-Gm-Message-State": "AOJu0YwRsyAyT+7jcnPEqHLlu32WuOxoFdaxKHjCg+hoedetW1oty/iN\n\tE/0tvW1AfU1CLG5ubY4/nFqbhAqqrKDMk43tTvo=",
        "X-Google-Smtp-Source": "AGHT+IEn5r1+/swhrQCGIePp8ScDtHHUkkwAordKLV11fE6EDDzxnWDlL48LeypmU89FnNaLWR1hRQ==",
        "X-Received": "by 2002:adf:d4c7:0:b0:31a:ed75:75d6 with SMTP id\n\tw7-20020adfd4c7000000b0031aed7575d6mr696501wrk.15.1694764651971; \n\tFri, 15 Sep 2023 00:57:31 -0700 (PDT)",
        "Date": "Fri, 15 Sep 2023 09:57:31 +0200",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"utf-8\"",
        "Content-Transfer-Encoding": "7bit",
        "Message-Id": "<20230915-libyuv-convert-v1-7-1e5bcf68adac@baylibre.com>",
        "References": "<20230915-libyuv-convert-v1-0-1e5bcf68adac@baylibre.com>",
        "In-Reply-To": "<20230915-libyuv-convert-v1-0-1e5bcf68adac@baylibre.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "X-Mailer": "b4 0.12.4-dev-6aa5d",
        "Subject": "[libcamera-devel] [PATCH RFC 7/7] WIP: android: add YUYV->NV12\n\tformat conversion via libyuv",
        "X-BeenThere": "libcamera-devel@lists.libcamera.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "<libcamera-devel.lists.libcamera.org>",
        "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>",
        "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>",
        "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>",
        "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>",
        "From": "Mattijs Korpershoek via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>",
        "Reply-To": "Mattijs Korpershoek <mkorpershoek@baylibre.com>",
        "Cc": "Guillaume La Roque <glaroque@baylibre.com>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "For some platforms, it's possible that the gralloc implementation\nand the CSI receiver cannot agree on a pixel format. When that happens,\nthere is usually a m2m converter in the pipeline which handles pixel format\nconversion.\n\nOn platforms without pixel format converters, such as the AM62x, we need to do\nsoftware conversion.\n\nThe AM62x platform:\n* uses a CSI receiver (j721e-csi2rx), that only supports\n  packed YUV422 formats such as YUYV, YVYU, UYVY and VYUY.\n* Has a gralloc implementation that only supports of semi-planar\n  YUV420 formats such as NV12.\n\nImplement YUYV->NV12 format conversion using libyuv.\n\nThis is mainly done by transforming the first stream from Type::Direct into\nType::Internal so that it goes through the post-processor loop.\n\n```\nThe WIP: part is mainly around computeYUYVSize():\n\nSince gralloc and j721e-csi2rx are incompatible, we need a way to get\ngralloc to allocate (NV12) the kernel-requested buffer length (YUYV).\nIn other words, we should make sure that the first plane of the NV12\nallocated buffer is long enough to fit a YUYV image.\n\nAccording to [1], NV12 has 8 bits (one byte) per component, and the\nfirst plane is the Y component.\nSo a 1920x1080 image in NV12 has plane[0].length=1920*1080=2073600\n\nAccording to [2], YUYV stores 2 pixels per container of 32 bits, which\ngives us 16 bits (2 bytes for one pixel).\nSo a 1920x1080 image in YUYV has plane[0].length=1920*1080*2=4147200\n\nSo apply a *2 factor to make the kernel believe it's receiving a YUYV buffer.\n\nNote: this also means that we are wasting NV12's plane[1] buffer with\neach allocation.\n\n[1] https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/pixfmt-yuv-planar.html\n[2] https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/pixfmt-packed-yuv.html\n```\n\nSigned-off-by: Mattijs Korpershoek <mkorpershoek@baylibre.com>\n---\n src/android/camera_capabilities.cpp | 90 ++++++++++++++++++++++++++++++++++++-\n src/android/camera_capabilities.h   |  4 ++\n src/android/camera_device.cpp       |  6 ++-\n src/android/camera_stream.cpp       | 54 +++++++++++++++++++++-\n src/android/camera_stream.h         |  5 +++\n 5 files changed, 154 insertions(+), 5 deletions(-)",
    "diff": "diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp\nindex 1bfeaea4b121..e2e0f7409e94 100644\n--- a/src/android/camera_capabilities.cpp\n+++ b/src/android/camera_capabilities.cpp\n@@ -124,6 +124,16 @@ const std::map<int, const Camera3Format> camera3FormatsMap = {\n \t},\n };\n \n+/**\n+ * \\var yuvConversions\n+ * \\brief list of supported pixel formats for an input pixel format\n+ *\n+ * \\todo This should be retrieved statically from yuv/post_processor_yuv instead\n+ */\n+const std::map<PixelFormat, const std::vector<PixelFormat>> yuvConversions = {\n+\t{ formats::YUYV, { formats::NV12 } },\n+};\n+\n const std::map<camera_metadata_enum_android_info_supported_hardware_level, std::string>\n hwLevelStrings = {\n \t{ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,  \"LIMITED\" },\n@@ -582,8 +592,10 @@ int CameraCapabilities::initializeStreamConfigurations()\n \t\t\tLOG(HAL, Debug) << \"Testing \" << pixelFormat;\n \n \t\t\t/*\n-\t\t\t * The stream configuration size can be adjusted,\n-\t\t\t * not the pixel format.\n+\t\t\t * The stream configuration size can be adjusted.\n+\t\t\t * The pixel format might be converted via libyuv.\n+\t\t\t * Conversion check is done in another loop after\n+\t\t\t * testing native supported formats.\n \t\t\t *\n \t\t\t * \\todo This could be simplified once all pipeline\n \t\t\t * handlers will report the StreamFormats list of\n@@ -603,7 +615,46 @@ int CameraCapabilities::initializeStreamConfigurations()\n \t\t\t/* If the format is not mandatory, skip it. */\n \t\t\tif (!camera3Format.mandatory)\n \t\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/*\n+\t\t * Test if we can map the format via a software conversion.\n+\t\t * This means that the converter can produce an \"output\" that is\n+\t\t * compatible with the format defined in Android.\n+\t\t */\n+\t\tbool needConversion = false;\n+\t\tfor (const PixelFormat &pixelFormat : libcameraFormats) {\n \n+\t\t\tLOG(HAL, Debug) << \"Testing \" << pixelFormat << \" using conversion\";\n+\n+\t\t\t/* \\todo move this into a separate function */\n+\t\t\tfor (const auto &[inputFormat, outputFormats] : yuvConversions) {\n+\t\t\t\t/* check if the converter can produce pixelFormat */\n+\t\t\t\tauto it = std::find(outputFormats.begin(), outputFormats.end(), pixelFormat);\n+\t\t\t\tif (it == outputFormats.end())\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\t/*\n+\t\t\t\t * The converter can produce output pixelFormat, see if we can configure\n+\t\t\t\t * the camera with the associated input pixelFormat.\n+\t\t\t\t */\n+\t\t\t\tcfg.pixelFormat = inputFormat;\n+\t\t\t\tCameraConfiguration::Status status = cameraConfig->validate();\n+\n+\t\t\t\tif (status != CameraConfiguration::Invalid && cfg.pixelFormat == inputFormat) {\n+\t\t\t\t\tmappedFormat = inputFormat;\n+\t\t\t\t\tconversionMap_[androidFormat] = std::make_pair(inputFormat, *it);\n+\t\t\t\t\tneedConversion = true;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\t/* We found a valid conversion format, so bail out */\n+\t\t\tif (mappedFormat.isValid())\n+\t\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (!mappedFormat.isValid()) {\n \t\t\tLOG(HAL, Error)\n \t\t\t\t<< \"Failed to map mandatory Android format \"\n \t\t\t\t<< camera3Format.name << \" (\"\n@@ -619,6 +670,11 @@ int CameraCapabilities::initializeStreamConfigurations()\n \t\tLOG(HAL, Debug) << \"Mapped Android format \"\n \t\t\t\t<< camera3Format.name << \" to \"\n \t\t\t\t<< mappedFormat;\n+\t\tif (needConversion) {\n+\t\t\tLOG(HAL, Debug) << mappedFormat\n+\t\t\t\t\t<< \" will be converted into \"\n+\t\t\t\t\t<< conversionMap_[androidFormat].second;\n+\t\t}\n \n \t\tstd::vector<Size> resolutions;\n \t\tconst PixelFormatInfo &info = PixelFormatInfo::info(mappedFormat);\n@@ -1457,6 +1513,36 @@ PixelFormat CameraCapabilities::toPixelFormat(int format) const\n \treturn it->second;\n }\n \n+/*\n+ * Check if we need to do software conversion via a post-processor\n+ * for an Android format code\n+ */\n+bool CameraCapabilities::needConversion(int format) const\n+{\n+\tauto it = conversionMap_.find(format);\n+\tif (it == conversionMap_.end()) {\n+\t\tLOG(HAL, Error) << \"Requested format \" << utils::hex(format)\n+\t\t\t\t<< \" not supported for conversion\";\n+\t\treturn false;\n+\t}\n+\n+\treturn true;\n+}\n+\n+/*\n+ * Returns a conversion (input,output) pair for a given Android format code\n+ */\n+std::pair<PixelFormat, PixelFormat> CameraCapabilities::conversionFormats(int format) const\n+{\n+\tauto it = conversionMap_.find(format);\n+\tif (it == conversionMap_.end()) {\n+\t\tLOG(HAL, Error) << \"Requested format \" << utils::hex(format)\n+\t\t\t\t<< \" not supported for conversion\";\n+\t}\n+\n+\treturn it->second;\n+}\n+\n std::unique_ptr<CameraMetadata> CameraCapabilities::requestTemplateManual() const\n {\n \tif (!capabilities_.count(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {\ndiff --git a/src/android/camera_capabilities.h b/src/android/camera_capabilities.h\nindex 6f66f221d33f..c3e6b48ab91d 100644\n--- a/src/android/camera_capabilities.h\n+++ b/src/android/camera_capabilities.h\n@@ -30,6 +30,9 @@ public:\n \n \tCameraMetadata *staticMetadata() const { return staticMetadata_.get(); }\n \tlibcamera::PixelFormat toPixelFormat(int format) const;\n+\tbool needConversion(int format) const;\n+\tstd::pair<libcamera::PixelFormat, libcamera::PixelFormat>\n+\tconversionFormats(int format) const;\n \tunsigned int maxJpegBufferSize() const { return maxJpegBufferSize_; }\n \n \tstd::unique_ptr<CameraMetadata> requestTemplateManual() const;\n@@ -77,6 +80,7 @@ private:\n \n \tstd::vector<Camera3StreamConfiguration> streamConfigurations_;\n \tstd::map<int, libcamera::PixelFormat> formatsMap_;\n+\tstd::map<int, std::pair<libcamera::PixelFormat, libcamera::PixelFormat>> conversionMap_;\n \tstd::unique_ptr<CameraMetadata> staticMetadata_;\n \tunsigned int maxJpegBufferSize_;\n \ndiff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\nindex d34bae715a47..842cbb06d345 100644\n--- a/src/android/camera_device.cpp\n+++ b/src/android/camera_device.cpp\n@@ -635,8 +635,12 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)\n \t\t\tcontinue;\n \t\t}\n \n+\t\tCameraStream::Type type = CameraStream::Type::Direct;\n+\t\tif (capabilities_.needConversion(stream->format))\n+\t\t\ttype = CameraStream::Type::Internal;\n+\n \t\tCamera3StreamConfig streamConfig;\n-\t\tstreamConfig.streams = { { stream, CameraStream::Type::Direct } };\n+\t\tstreamConfig.streams = { { stream, type } };\n \t\tstreamConfig.config.size = size;\n \t\tstreamConfig.config.pixelFormat = format;\n \t\tstreamConfigs.push_back(std::move(streamConfig));\ndiff --git a/src/android/camera_stream.cpp b/src/android/camera_stream.cpp\nindex 4fd05dda5ed3..961ee40017f1 100644\n--- a/src/android/camera_stream.cpp\n+++ b/src/android/camera_stream.cpp\n@@ -95,6 +95,7 @@ int CameraStream::configure()\n \n \t\tswitch (outFormat) {\n \t\tcase formats::NV12:\n+\t\tcase formats::YUYV:\n \t\t\tpostProcessor_ = std::make_unique<PostProcessorYuv>();\n \t\t\tbreak;\n \n@@ -107,6 +108,16 @@ int CameraStream::configure()\n \t\t\treturn -EINVAL;\n \t\t}\n \n+\t\tneedConversion_ =\n+\t\t\tcameraDevice_->capabilities()->needConversion(camera3Stream_->format);\n+\n+\t\tif (needConversion_) {\n+\t\t\tauto conv = cameraDevice_->capabilities()->conversionFormats(camera3Stream_->format);\n+\t\t\tLOG(HAL, Debug) << \"Configuring the post processor to convert \"\n+\t\t\t\t\t<< conv.first << \" -> \" << conv.second;\n+\t\t\toutput.pixelFormat = conv.second;\n+\t\t}\n+\n \t\tint ret = postProcessor_->configure(input, output);\n \t\tif (ret)\n \t\t\treturn ret;\n@@ -183,7 +194,12 @@ int CameraStream::process(Camera3RequestDescriptor::StreamBuffer *streamBuffer)\n \t\tstreamBuffer->fence.reset();\n \t}\n \n-\tconst StreamConfiguration &output = configuration();\n+\tStreamConfiguration output = configuration();\n+\tif (needConversion_) {\n+\t\toutput.pixelFormat =\n+\t\t\tcameraDevice_->capabilities()->conversionFormats(camera3Stream_->format).second;\n+\t}\n+\n \tstreamBuffer->dstBuffer = std::make_unique<CameraBuffer>(\n \t\t*streamBuffer->camera3Buffer, output.pixelFormat, output.size,\n \t\tPROT_READ | PROT_WRITE);\n@@ -205,6 +221,39 @@ void CameraStream::flush()\n \tworker_->flush();\n }\n \n+Size CameraStream::computeYUYVSize(const Size &nv12Size)\n+{\n+\t/*\n+\t * On am62x platforms, the receiver driver (j721e-csi2rx) only\n+\t * supports packed YUV422 formats such as YUYV, YVYU, UYVY and VYUY.\n+\t *\n+\t * However, the gralloc implementation is only capable of semiplanar\n+\t * YUV420 such as NV12.\n+\t *\n+\t * To trick the kernel into believing it's receiving a YUYV buffer, we adjust the\n+\t * size we request to gralloc so that plane(0) of the NV12 buffer is long enough to\n+\t * match the length of a YUYV plane.\n+\t *\n+\t * for NV12, one pixel is encoded on 1.5 bytes, but plane 0 has 1 byte per pixel.\n+\t * for YUYV, one pixel is encoded on 2 bytes.\n+\t *\n+\t * So apply a *2 factor.\n+\t *\n+\t * See:\n+\t * https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/pixfmt-packed-yuv.html\n+\t * https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/pixfmt-yuv-planar.html\n+\t */\n+\tconstexpr unsigned int YUYVfactor = 2;\n+\n+\tunsigned int width = nv12Size.width;\n+\tunsigned int height = nv12Size.height;\n+\n+\tif (needConversion_)\n+\t\twidth = width * YUYVfactor;\n+\n+\treturn Size{ width, height };\n+}\n+\n FrameBuffer *CameraStream::getBuffer()\n {\n \tif (!allocator_)\n@@ -222,8 +271,9 @@ FrameBuffer *CameraStream::getBuffer()\n \t\t * \\todo Store a reference to the format of the source stream\n \t\t * instead of hardcoding.\n \t\t */\n+\t\tconst Size hackedSize = computeYUYVSize(configuration().size);\n \t\tauto frameBuffer = allocator_->allocate(HAL_PIXEL_FORMAT_YCBCR_420_888,\n-\t\t\t\t\t\t\tconfiguration().size,\n+\t\t\t\t\t\t\thackedSize,\n \t\t\t\t\t\t\tcamera3Stream_->usage);\n \t\tallocatedBuffers_.push_back(std::move(frameBuffer));\n \t\tbuffers_.emplace_back(allocatedBuffers_.back().get());\ndiff --git a/src/android/camera_stream.h b/src/android/camera_stream.h\nindex 4c5078b2c26d..52a5606399c5 100644\n--- a/src/android/camera_stream.h\n+++ b/src/android/camera_stream.h\n@@ -128,10 +128,13 @@ public:\n \n \tint configure();\n \tint process(Camera3RequestDescriptor::StreamBuffer *streamBuffer);\n+\tlibcamera::Size computeYUYVSize(const libcamera::Size &nv12Size);\n \tlibcamera::FrameBuffer *getBuffer();\n \tvoid putBuffer(libcamera::FrameBuffer *buffer);\n \tvoid flush();\n \n+\tbool needConversion() const { return needConversion_; }\n+\n private:\n \tclass PostProcessorWorker : public libcamera::Thread\n \t{\n@@ -184,4 +187,6 @@ private:\n \tstd::unique_ptr<PostProcessor> postProcessor_;\n \n \tstd::unique_ptr<PostProcessorWorker> worker_;\n+\n+\tbool needConversion_;\n };\n",
    "prefixes": [
        "libcamera-devel",
        "RFC",
        "7/7"
    ]
}