Show a patch.

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

{
    "id": 22157,
    "url": "https://patchwork.libcamera.org/api/patches/22157/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/22157/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/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": "<20241204085511.1733434-1-qi.hou@nxp.com>",
    "date": "2024-12-04T08:55:11",
    "name": "[v6] gstreamer: Add GstVideoMeta support",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "6fc144c4f96f3a85f096c11a4506de78c60bf8f7",
    "submitter": {
        "id": 195,
        "url": "https://patchwork.libcamera.org/api/people/195/?format=api",
        "name": "Qi Hou",
        "email": "qi.hou@nxp.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/22157/mbox/",
    "series": [
        {
            "id": 4842,
            "url": "https://patchwork.libcamera.org/api/series/4842/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4842",
            "date": "2024-12-04T08:55:11",
            "name": "[v6] gstreamer: Add GstVideoMeta support",
            "version": 6,
            "mbox": "https://patchwork.libcamera.org/series/4842/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/22157/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/22157/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 5BE16BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  4 Dec 2024 08:55:41 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id EE1C9618B5;\n\tWed,  4 Dec 2024 09:55:39 +0100 (CET)",
            "from EUR02-VI1-obe.outbound.protection.outlook.com\n\t(mail-vi1eur02on20615.outbound.protection.outlook.com\n\t[IPv6:2a01:111:f403:2607::615])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id DB50A618B5\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  4 Dec 2024 09:55:36 +0100 (CET)",
            "from PAXPR04MB8285.eurprd04.prod.outlook.com\n\t(2603:10a6:102:1ca::15)\n\tby PA4PR04MB7789.eurprd04.prod.outlook.com (2603:10a6:102:c3::10)\n\twith Microsoft SMTP Server (version=TLS1_2,\n\tcipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8230.11;\n\tWed, 4 Dec 2024 08:55:34 +0000",
            "from PAXPR04MB8285.eurprd04.prod.outlook.com\n\t([fe80::e003:8fb:64ea:acfd]) by\n\tPAXPR04MB8285.eurprd04.prod.outlook.com\n\t([fe80::e003:8fb:64ea:acfd%6]) with mapi id 15.20.8207.017;\n\tWed, 4 Dec 2024 08:55:34 +0000"
        ],
        "Authentication-Results": [
            "lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=nxp.com header.i=@nxp.com header.b=\"W0xcb6oH\";\n\tdkim-atps=neutral",
            "dkim=none (message not signed)\n\theader.d=none;dmarc=none action=none header.from=nxp.com;"
        ],
        "ARC-Seal": "i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none;\n\tb=NIcUC4ZzNUHKUdpg2fLkfEDADBhWA93oxgf4NHnL5BE1AZyk/QMHOUbHDEIJEMgq0kAZfqaGmwM6TPUQ2xSapEoYD8ORBR0yqtbj+hQgariJvAINTeUh72yPZ0LpLjjA2sJX3FkAsJVGiaK/6XNlFfQhMNG5CDaWxta8TvSI9lI5NXR4uagFhfstFsi6/aNRy45Uu3LS4+xLuKsD66HOyKTnlHvyhx2HHyYfGs5UOyDU5QBZeOtLZm7lNmD4xIKlfFFeYTFngPLD/29/zQ2qM81jFMa8hm1XPLUsyoE/wv7i5m+hrAvV2xtf5iUiRvOedeQ9z7ikUxccMaulVRUu6g==",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;\n\ts=arcselector10001;\n\th=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1;\n\tbh=ZrhxcvAAtbeOuWH8etRpfy0yezP8dYMj1efEcUa4DQg=;\n\tb=Aa3Bpnx64BjSxv6Z2e5F4loy3PnNGZmtz7Ca/FwzS6KMlT8JzxZ5KnYCIcbvJB07md88ykdFCCiooa4v4rsWHCAWerKi5PQ+OpSdImVNnSRrvPaxear252H1owvPdaT9w4HagxbKUY8t2frbPoy3pgvqdkIjr//aHMVFJ3J5sN4JPsBixpz4vFt93fmV1fv5rSx4CdJ7+G1Ikw1DcQQBiyidQjFa+7S/J6KfXSWxcAx+soe+nMmbfvOqBQMyizHrILRbW71md+nOhemGpsg1KyfWIHCv7nI6kdnvVwZxJ/pf0l748Hv6iJ7Pqg3uB/7ZanEvWHlC00e0z6HKUSTfnw==",
        "ARC-Authentication-Results": "i=1; mx.microsoft.com 1; spf=pass\n\tsmtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com;\n\tdkim=pass header.d=nxp.com; arc=none",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1;\n\th=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n\tbh=ZrhxcvAAtbeOuWH8etRpfy0yezP8dYMj1efEcUa4DQg=;\n\tb=W0xcb6oHc6K7dNzLStYCh9x064UG0daC/yOZN0Kt4opY6mR5rbtqebiO6oDx3I5encfU6aWWH0PUlpxGiLxmEC/o/4qh1dokFwFdc+TYOwARkw9AQLcWUtHBscR2cwKy0k9z6wdJYLUEo4SefJtLdiMLoMNMt2mpcdIvR9awPcvkmwiaowpNgVPBgqeNK+UsG1pqrgj0bfJDn8AdPrW+/8IDsVQ5Md66GQ/cDMpulbj3LO6dt6WTDU72t6biUM4SbuKBahnjOSV0iYgrvwEc/SayH9v8xbFM9b6VxjMziUbRaFVuVVi2gZs+EeiG6nwXDPHgWOy4WfhzNJjAmgFtww==",
        "From": "Hou Qi <qi.hou@nxp.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "jared.hu@nxp.com,\n\tqi.hou@nxp.com,\n\tjulien.vuillaumier@nxp.com",
        "Subject": "[PATCH v6] gstreamer: Add GstVideoMeta support",
        "Date": "Wed,  4 Dec 2024 17:55:11 +0900",
        "Message-Id": "<20241204085511.1733434-1-qi.hou@nxp.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "Content-Transfer-Encoding": "8bit",
        "Content-Type": "text/plain",
        "X-ClientProxiedBy": "SI2PR01CA0004.apcprd01.prod.exchangelabs.com\n\t(2603:1096:4:191::22) To PAXPR04MB8285.eurprd04.prod.outlook.com\n\t(2603:10a6:102:1ca::15)",
        "MIME-Version": "1.0",
        "X-MS-PublicTrafficType": "Email",
        "X-MS-TrafficTypeDiagnostic": "PAXPR04MB8285:EE_|PA4PR04MB7789:EE_",
        "X-MS-Office365-Filtering-Correlation-Id": "de1c2ae9-60df-4d5d-5142-08dd1441694b",
        "X-MS-Exchange-SenderADCheck": "1",
        "X-MS-Exchange-AntiSpam-Relay": "0",
        "X-Microsoft-Antispam": "BCL:0;\n\tARA:13230040|1800799024|366016|376014|52116014|38350700014; ",
        "X-Microsoft-Antispam-Message-Info": "tFp1VFQ6G2pp2yflTKZmdPgBIY74xRFdHJ3Y/wrA5O8qDG1xcUvdEOEsBRzZMJuXlzIRA8pf7OldT/Kg5y8HOL5f3TBLHCHhYNPFsBnskgagBaAQSCbdQzABBayYIWC42Y/RuA/hnJYsR+ryTtI7pbVzw6yiwTmcLxyj/kgKddbgKBuSClmS0axdvkTnyOKIcMyEJyCDvn9p7KVPHDiH3J5mCgn59XeEohl7yUiIZvEmt854Fmgkp5MWoUosTp/I7e+Ye1uijLtzU3Dw+pnx0qpYwngr8/qry4FgLshXWS1NTIV+N4GPPu3LKfzG3SiGhvdv1wsAqVQBdjlfROBfyrFPJoR5TBZ5BcwIlzeZu4q448Vb0ZDrJnoMRIrnhwQ7mnIWHXJSaPjckbq9U84wl57wnJQzoqxzpCkdSm5Gp0xe0WvGbGz17as8E9tl/ExonMMZoCGAO0AWNHkD6Cs8gXYfGsVmDx+IFKYz1A24sVBNFeBq/05qW9Tp42eUR0t85yTZhNAAasZwxkZ1dr+dZBS0x3mDVYjuSHi0zcEbkE12hyEGLMG1vnruXQe8RsDMoELSbvNBo8lwo0rnJnsx8d6avPjLPXNVVF9pToqT8SUXN57fKHXqztv4hm4L/2EfEOO7BTzsI32nz7iOlNCsxR7MzZaJ3yR51ZUR8/dd3AhLnhFkVraIVyEZc9jINLFJwfMkoayHPeKgBGOypMTbLy8sE+SimDPRaoszd4c/B9amYXGpeA7Sg+9mqUC7dBaP7S0Z4lRdE1AYR53F6C4kDLTk8vFSazI2/CnQZKxekhD7FbOL8ncdVMACPKHIGBg24w37rkWyf1h+lVuxFwubjTXpLYgB6mCPyj5borPulhSnv6BJFb9Cm0w9ebdtBXNjAkbIoOfuyXTMJ51Wn7MvKAaK9POizCZ2kOirJWjGnKuKJvs9ncgS7GZ1YhCKOUg/X7Nfu45LihUZyMPUHR7ECcZ+EOetu26E4djRrEe7tgx9GYTEnq6n6L7DJjx7LH7mx/P6Tk200u38oh3rXo+aOBBhk6OI31GlKt+ZbjlimXXTjrpSCcpOjlowZtl6mUTac7mcVW+WGh+PzBHIQNAyj40AfJ+o+Qg3qQx2TONptXKb7bhrjW6J01r+DLdLHjzDyqXdNYuw9NsWHtAYyR1/aMvgEm+/6nEv5Is+jPZO3Z60EXlojWXjJYwK45MtABUqo+E/DlGSaA6AaLY0ehen+BfDMQxfDMD5PHABaZYksrDm/w1rsRrn9hxysRAEtppzAJD4fv3EvKZS0CpigZay+jDN1ErjELkyJFhiVGzCIXr+4jCYDAdRxaUoRB3v9gxsax7F3uKflLE9DwmpDEKLcj0i25Dj2wy7PFoQCiMMnnNWOJRR4E3w4kyZK+vaPi3htb7s697QQ+NsZnStD4NRCg==",
        "X-Forefront-Antispam-Report": "CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:;\n\tIPV:NLI; SFV:NSPM; H:PAXPR04MB8285.eurprd04.prod.outlook.com; PTR:;\n\tCAT:NONE; \n\tSFS:(13230040)(1800799024)(366016)(376014)(52116014)(38350700014);\n\tDIR:OUT; SFP:1101; ",
        "X-MS-Exchange-AntiSpam-MessageData-ChunkCount": "1",
        "X-MS-Exchange-AntiSpam-MessageData-0": "zlWPt76T8zcfppqIg7Q8URvn0jF4OhFt1+In19+YcfM2VWMWhJVbM9vWEXmNSe3zcPC4lsoIKwCm/26iI2MzFGEzeUilEhpYU9i+GyO1APdlE1/tssmap0Vb40HNZCrEmmXMfwy5zjtYMeocP3LXHFoiwdPDDx0aD73weJSCcNNm0CsixryeCQHSCs9cxbzXExt0ePPpCa5jcRfotcVJvSjII0chNYtMzN6cAMluYaCzYLW72tlIcGaIjE18wJz6UDFsnFeAAYgvkWIm40ZqWGa53ffe6zkZFr9a5mhp66OEXtis2PVi8xQMJALS6Y2FFJ1xbEkOZW1sJq6jk1A/qjJamBszAdcjHZQnc+x5I7qwwdxObWOCU67UnMNR2GVj+WLZVRQ9yyfZ23N8xgqqXq2zgcczW0jCH0txDLCeMJhUcKxSx3UmwpvbbYaDFtLYFG4GHSg3CcTh4mtRHW8BLdic2VDV6VT1ucA6+EhvFdphVf2U3Dvt1v5H6fWJSVKBbKr0jDfyAwRV3WRmxYmA2s4CU4k/7z1Y/FjGjOLVtz6SOJ/K304yGUCbJY6H5G9xOuA8MqpVVckRg9U3N9Xpuqu/eJCbNbxvHiH3nZd6zBE3AhahS0DWfzJGdSW4rHiQhLPm5osb71nJAhU7tWkradgoMFmAZGCEwj1MTDelmAB12S4yU1IMbAnQPwHS3iLccXlGmjd2ytvKmidN87140ytphs2y4YLWah+T56mC3b0YjyoyabkD6SuJ1VNufCtYicn8LxVdZK6odB3Zr8+OuykB565kgU7xUMrVeUvX2cnmazxxApUOufW9FI28ACIr7h0M7NY1Mofhr/L1s7UNNnAbsSP3nqigQmKbmMk1LYheOTHlBJ2+EJS77jaLVuIieUs3z+fh2K8FGFEqJzftBKtTRhXMUFX8O0GsjEXQFXiifYkrIQMuSiRMO7U6qGfKIotsKlVcjCgAepF5XyCXm7qy+/CeWkROLvYI/lAKfbyu1bq+SFgZ5RI+mSy7MGNWZ0Vj4phjgIivANlmpWkfa7wRD3EzbFkNuO6hCwD3zMJGtExd9m0ixsfBFs651Vk2Rl7cwP39d4c4TL013EctrZjBSPN2mIUKGOV48AEwnWxQ59rgTjA8PBk9r5DXOOYG4a0MQ7MwsvHw7kYYx3CGSDwvVA4DUJrD9PTV24EpbmGyEEqUYyFKhmb5eUPTxB+QRRJadJ1iFLNP6jp7kXP9UvdYILbNOHPiFDbGPe0/3a8n3otxoWfkkNEJnMOeX3wL29otboB30/vqES5n29IRSyADPdltnA6pcJeBlwo0UqXnppKacIExQWMup5aDTa46nwNtTN/pYG+22pnMceT5ThQjDR3aQ6hkQaTknfVG5DLQUTcLAfBAlMiyQsjeZVYBU6lK+C57umOSaPY+jcpcQ5zeIwawbx8x9rlVQ77netc5atpm5qt7944LE1nDKh1OH2xNgMJrPLx0OD+Dr8bPg/vpJIlxVyNiBVYAiQxPDU5eTOhlgn6KWSFEu/zE89uG96NVFd9fBI0dtTGTrukI3yPukaZariUXc736LXq5PEU=",
        "X-OriginatorOrg": "nxp.com",
        "X-MS-Exchange-CrossTenant-Network-Message-Id": "de1c2ae9-60df-4d5d-5142-08dd1441694b",
        "X-MS-Exchange-CrossTenant-AuthSource": "PAXPR04MB8285.eurprd04.prod.outlook.com",
        "X-MS-Exchange-CrossTenant-AuthAs": "Internal",
        "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "04 Dec 2024 08:55:34.2112\n\t(UTC)",
        "X-MS-Exchange-CrossTenant-FromEntityHeader": "Hosted",
        "X-MS-Exchange-CrossTenant-Id": "686ea1d3-bc2b-4c6f-a92c-d99c5c301635",
        "X-MS-Exchange-CrossTenant-MailboxType": "HOSTED",
        "X-MS-Exchange-CrossTenant-UserPrincipalName": "O7dLKNiAheKjEyzaobHL0Fb4ikSyevg4D4clp7Cxig2dt/EhN35nQFvdzFRDLmm5",
        "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "PA4PR04MB7789",
        "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>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "GStreamer video-info calculated stride and offset may differ from\nthose used by the camera.\n\nFor stride and offset mismatch, this patch adds video meta to buffer\nif downstream supports VideoMeta through allocation query. Otherwise,\ncreate a internal VideoPool using the caps, and copy video frame to\nthis system memory.\n\nSigned-off-by: Hou Qi <qi.hou@nxp.com>\n---\n src/gstreamer/gstlibcamera-utils.cpp |  33 +++++++\n src/gstreamer/gstlibcamera-utils.h   |   4 +\n src/gstreamer/gstlibcamerapad.cpp    |  31 +++++++\n src/gstreamer/gstlibcamerapad.h      |   8 ++\n src/gstreamer/gstlibcamerapool.cpp   |  18 +++-\n src/gstreamer/gstlibcamerapool.h     |   3 +-\n src/gstreamer/gstlibcamerasrc.cpp    | 133 ++++++++++++++++++++++++++-\n 7 files changed, 226 insertions(+), 4 deletions(-)",
    "diff": "diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp\nindex 732987ef..09af9204 100644\n--- a/src/gstreamer/gstlibcamera-utils.cpp\n+++ b/src/gstreamer/gstlibcamera-utils.cpp\n@@ -591,6 +591,39 @@ gst_task_resume(GstTask *task)\n }\n #endif\n \n+#if !GST_CHECK_VERSION(1, 22, 0)\n+/*\n+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>\n+ * Library       <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>\n+ * Copyright (C) <2007> David A. Schleef <ds@schleef.org>\n+ */\n+/* This function has been imported directly from the gstreamer project to\n+ * support backwards compatibility and should be removed when the older\n+ * version is no longer supported. */\n+gint gst_video_format_info_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride)\n+{\n+\tgint estride;\n+\tgint comp[GST_VIDEO_MAX_COMPONENTS];\n+\tgint i;\n+\n+\t/* there is nothing to extrapolate on first plane */\n+\tif (plane == 0)\n+\t\treturn stride;\n+\n+\tgst_video_format_info_component(finfo, plane, comp);\n+\n+\t/* For now, all planar formats have a single component on first plane, but\n+\t* if there was a planar format with more, we'd have to make a ratio of the\n+\t* number of component on the first plane against the number of component on\n+\t* the current plane. */\n+\testride = 0;\n+\tfor (i = 0; i < GST_VIDEO_MAX_COMPONENTS && comp[i] >= 0; i++)\n+\t\testride += GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(finfo, comp[i], stride);\n+\n+\treturn estride;\n+}\n+#endif\n+\n G_LOCK_DEFINE_STATIC(cm_singleton_lock);\n static std::weak_ptr<CameraManager> cm_singleton_ptr;\n \ndiff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h\nindex cab1c814..81149280 100644\n--- a/src/gstreamer/gstlibcamera-utils.h\n+++ b/src/gstreamer/gstlibcamera-utils.h\n@@ -35,6 +35,10 @@ static inline void gst_clear_event(GstEvent **event_ptr)\n #if !GST_CHECK_VERSION(1, 17, 1)\n gboolean gst_task_resume(GstTask *task);\n #endif\n+\n+#if !GST_CHECK_VERSION(1, 22, 0)\n+gint gst_video_format_info_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride);\n+#endif\n std::shared_ptr<libcamera::CameraManager> gst_libcamera_get_camera_manager(int &ret);\n \n /**\ndiff --git a/src/gstreamer/gstlibcamerapad.cpp b/src/gstreamer/gstlibcamerapad.cpp\nindex 7b22aebe..7fd11a8f 100644\n--- a/src/gstreamer/gstlibcamerapad.cpp\n+++ b/src/gstreamer/gstlibcamerapad.cpp\n@@ -18,6 +18,8 @@ struct _GstLibcameraPad {\n \tGstPad parent;\n \tStreamRole role;\n \tGstLibcameraPool *pool;\n+\tGstBufferPool *video_pool;\n+\tGstVideoInfo info;\n \tGstClockTime latency;\n };\n \n@@ -153,6 +155,35 @@ gst_libcamera_pad_set_pool(GstPad *pad, GstLibcameraPool *pool)\n \tself->pool = pool;\n }\n \n+GstBufferPool *\n+gst_libcamera_pad_get_video_pool(GstPad *pad)\n+{\n+\tauto *self = GST_LIBCAMERA_PAD(pad);\n+\treturn self->video_pool;\n+}\n+\n+void gst_libcamera_pad_set_video_pool(GstPad *pad, GstBufferPool *video_pool)\n+{\n+\tauto *self = GST_LIBCAMERA_PAD(pad);\n+\n+\tif (self->video_pool)\n+\t\tg_object_unref(self->video_pool);\n+\tself->video_pool = video_pool;\n+}\n+\n+GstVideoInfo gst_libcamera_pad_get_video_info(GstPad *pad)\n+{\n+\tauto *self = GST_LIBCAMERA_PAD(pad);\n+\treturn self->info;\n+}\n+\n+void gst_libcamera_pad_set_video_info(GstPad *pad, GstVideoInfo info)\n+{\n+\tauto *self = GST_LIBCAMERA_PAD(pad);\n+\n+\tself->info = info;\n+}\n+\n Stream *\n gst_libcamera_pad_get_stream(GstPad *pad)\n {\ndiff --git a/src/gstreamer/gstlibcamerapad.h b/src/gstreamer/gstlibcamerapad.h\nindex 630c168a..ed0beb1f 100644\n--- a/src/gstreamer/gstlibcamerapad.h\n+++ b/src/gstreamer/gstlibcamerapad.h\n@@ -23,6 +23,14 @@ GstLibcameraPool *gst_libcamera_pad_get_pool(GstPad *pad);\n \n void gst_libcamera_pad_set_pool(GstPad *pad, GstLibcameraPool *pool);\n \n+GstBufferPool *gst_libcamera_pad_get_video_pool(GstPad *pad);\n+\n+void gst_libcamera_pad_set_video_pool(GstPad *pad, GstBufferPool *video_pool);\n+\n+GstVideoInfo gst_libcamera_pad_get_video_info(GstPad *pad);\n+\n+void gst_libcamera_pad_set_video_info(GstPad *pad, GstVideoInfo info);\n+\n libcamera::Stream *gst_libcamera_pad_get_stream(GstPad *pad);\n \n void gst_libcamera_pad_set_latency(GstPad *pad, GstClockTime latency);\ndiff --git a/src/gstreamer/gstlibcamerapool.cpp b/src/gstreamer/gstlibcamerapool.cpp\nindex 9cd7eccb..471f71da 100644\n--- a/src/gstreamer/gstlibcamerapool.cpp\n+++ b/src/gstreamer/gstlibcamerapool.cpp\n@@ -14,6 +14,9 @@\n \n #include \"gstlibcamera-utils.h\"\n \n+#include <gst/gstmeta.h>\n+\n+\n using namespace libcamera;\n \n enum {\n@@ -134,8 +137,20 @@ gst_libcamera_pool_class_init(GstLibcameraPoolClass *klass)\n \t\t\t\t\t\t     G_TYPE_NONE, 0);\n }\n \n+static void\n+gst_libcamera_buffer_add_video_meta(GstBuffer *buffer, GstVideoInfo *info)\n+{\n+\tGstVideoMeta *vmeta;\n+\tvmeta = gst_buffer_add_video_meta_full(buffer, GST_VIDEO_FRAME_FLAG_NONE,\n+\t\t\t\t\t       GST_VIDEO_INFO_FORMAT(info), GST_VIDEO_INFO_WIDTH(info),\n+\t\t\t\t\t       GST_VIDEO_INFO_HEIGHT(info), GST_VIDEO_INFO_N_PLANES(info),\n+\t\t\t\t\t       info->offset, info->stride);\n+\tGST_META_FLAGS(vmeta) = (GstMetaFlags)(GST_META_FLAGS(vmeta) | GST_META_FLAG_POOLED);\n+}\n+\n GstLibcameraPool *\n-gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream)\n+gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream,\n+\t\t       GstVideoInfo *info)\n {\n \tauto *pool = GST_LIBCAMERA_POOL(g_object_new(GST_TYPE_LIBCAMERA_POOL, nullptr));\n \n@@ -145,6 +160,7 @@ gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream)\n \tgsize pool_size = gst_libcamera_allocator_get_pool_size(allocator, stream);\n \tfor (gsize i = 0; i < pool_size; i++) {\n \t\tGstBuffer *buffer = gst_buffer_new();\n+\t\tgst_libcamera_buffer_add_video_meta(buffer, info);\n \t\tpool->queue->push_back(buffer);\n \t}\n \ndiff --git a/src/gstreamer/gstlibcamerapool.h b/src/gstreamer/gstlibcamerapool.h\nindex 2a7a9c77..02ee4dd4 100644\n--- a/src/gstreamer/gstlibcamerapool.h\n+++ b/src/gstreamer/gstlibcamerapool.h\n@@ -14,6 +14,7 @@\n #include \"gstlibcameraallocator.h\"\n \n #include <gst/gst.h>\n+#include <gst/video/video.h>\n \n #include <libcamera/stream.h>\n \n@@ -21,7 +22,7 @@\n G_DECLARE_FINAL_TYPE(GstLibcameraPool, gst_libcamera_pool, GST_LIBCAMERA, POOL, GstBufferPool)\n \n GstLibcameraPool *gst_libcamera_pool_new(GstLibcameraAllocator *allocator,\n-\t\t\t\t\t libcamera::Stream *stream);\n+\t\t\t\t\t libcamera::Stream *stream, GstVideoInfo *info);\n \n libcamera::Stream *gst_libcamera_pool_get_stream(GstLibcameraPool *self);\n \ndiff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp\nindex 8efa25f4..82b06eb4 100644\n--- a/src/gstreamer/gstlibcamerasrc.cpp\n+++ b/src/gstreamer/gstlibcamerasrc.cpp\n@@ -268,6 +268,52 @@ GstLibcameraSrcState::requestCompleted(Request *request)\n \tgst_task_resume(src_->task);\n }\n \n+static void\n+gst_libcamera_extrapolate_info(GstVideoInfo *info, guint32 stride)\n+{\n+\tguint i, estride;\n+\tgsize offset = 0;\n+\n+\t/* this should be updated if tiled formats get added in the future. */\n+\tfor (i = 0; i < GST_VIDEO_INFO_N_PLANES(info); i++) {\n+\t\testride = gst_video_format_info_extrapolate_stride(info->finfo, i, stride);\n+\t\tinfo->stride[i] = estride;\n+\t\tinfo->offset[i] = offset;\n+\t\toffset += estride * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info->finfo, i,\n+\t\t\t\t\t\t\t\t       GST_VIDEO_INFO_HEIGHT(info));\n+\t}\n+}\n+\n+static GstFlowReturn\n+gst_libcamera_video_frame_copy(GstBuffer *src, GstBuffer *dest, GstVideoInfo dest_info, guint32 stride)\n+{\n+\tGstVideoInfo src_info = dest_info;\n+\tGstVideoFrame src_frame, dest_frame;\n+\n+\tgst_libcamera_extrapolate_info(&src_info, stride);\n+\tsrc_info.size = gst_buffer_get_size(src);\n+\n+\tif (!gst_video_frame_map(&src_frame, &src_info, src, GST_MAP_READ))\n+\t\tgoto invalid_buffer;\n+\n+\tif (!gst_video_frame_map(&dest_frame, &dest_info, dest, GST_MAP_WRITE)) {\n+\t\tgst_video_frame_unmap(&src_frame);\n+\t\tgoto invalid_buffer;\n+\t}\n+\n+\tgst_video_frame_copy(&dest_frame, &src_frame);\n+\n+\tgst_video_frame_unmap(&src_frame);\n+\tgst_video_frame_unmap(&dest_frame);\n+\n+\treturn GST_FLOW_OK;\n+\n+invalid_buffer : {\n+\tGST_ERROR(\"Could not map buffer\");\n+\treturn GST_FLOW_ERROR;\n+}\n+}\n+\n /* Must be called with stream_lock held. */\n int GstLibcameraSrcState::processRequest()\n {\n@@ -292,11 +338,36 @@ int GstLibcameraSrcState::processRequest()\n \tGstFlowReturn ret = GST_FLOW_OK;\n \tgst_flow_combiner_reset(src_->flow_combiner);\n \n-\tfor (GstPad *srcpad : srcpads_) {\n+\tfor (gsize i = 0; i < src_->state->srcpads_.size(); i++) {\n+\t\tGstPad *srcpad = src_->state->srcpads_[i];\n \t\tStream *stream = gst_libcamera_pad_get_stream(srcpad);\n \t\tGstBuffer *buffer = wrap->detachBuffer(stream);\n \n \t\tFrameBuffer *fb = gst_libcamera_buffer_get_frame_buffer(buffer);\n+\t\tconst StreamConfiguration &stream_cfg = src_->state->config_->at(i);\n+\t\tGstBufferPool *video_pool = gst_libcamera_pad_get_video_pool(srcpad);\n+\n+\t\tif (video_pool) {\n+\t\t\tGstBuffer *copy = NULL;\n+\t\t\tGstVideoInfo info = gst_libcamera_pad_get_video_info(srcpad);\n+\n+\t\t\tret = gst_buffer_pool_acquire_buffer(video_pool, &copy, NULL);\n+\t\t\tif (ret != GST_FLOW_OK) {\n+\t\t\t\tGST_ERROR(\"Failed to acquire buffer\");\n+\t\t\t\tgst_buffer_unref(buffer);\n+\t\t\t\treturn -EPIPE;\n+\t\t\t}\n+\n+\t\t\tret = gst_libcamera_video_frame_copy(buffer, copy, info, stream_cfg.stride);\n+\t\t\tgst_buffer_unref(buffer);\n+\t\t\tif (ret != GST_FLOW_OK) {\n+\t\t\t\tGST_ERROR(\"Failed to copy buffer\");\n+\t\t\t\tgst_buffer_unref(copy);\n+\t\t\t\treturn -EPIPE;\n+\t\t\t}\n+\n+\t\t\tbuffer = copy;\n+\t\t}\n \n \t\tif (GST_CLOCK_TIME_IS_VALID(wrap->pts_)) {\n \t\t\tGST_BUFFER_PTS(buffer) = wrap->pts_;\n@@ -497,13 +568,65 @@ gst_libcamera_src_negotiate(GstLibcameraSrc *self)\n \tfor (gsize i = 0; i < state->srcpads_.size(); i++) {\n \t\tGstPad *srcpad = state->srcpads_[i];\n \t\tconst StreamConfiguration &stream_cfg = state->config_->at(i);\n+\t\tGstBufferPool *video_pool = NULL;\n+\t\tGstVideoInfo info;\n+\n+\t\tg_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg);\n+\n+\t\tgst_video_info_init(&info);\n+\t\tgst_video_info_from_caps(&info, caps);\n+\t\tgst_libcamera_pad_set_video_info(srcpad, info);\n+\n+\t\t/* stride mismatch between camera stride and that calculated by video-info */\n+\t\tif (static_cast<unsigned int>(info.stride[0]) != stream_cfg.stride &&\n+\t\t    GST_VIDEO_INFO_FORMAT(&info) != GST_VIDEO_FORMAT_ENCODED) {\n+\t\t\tGstQuery *query = NULL;\n+\t\t\tgboolean need_pool = true;\n+\t\t\tgboolean has_video_meta = false;\n+\n+\t\t\tgst_libcamera_extrapolate_info(&info, stream_cfg.stride);\n+\n+\t\t\tquery = gst_query_new_allocation(caps, need_pool);\n+\t\t\tif (!gst_pad_peer_query(srcpad, query))\n+\t\t\t\tGST_DEBUG_OBJECT(self, \"Didn't get downstream ALLOCATION hints\");\n+\t\t\telse\n+\t\t\t\thas_video_meta = gst_query_find_allocation_meta(query, GST_VIDEO_META_API_TYPE, NULL);\n+\n+\t\t\tif (!has_video_meta) {\n+\t\t\t\tGstBufferPool *pool = NULL;\n+\n+\t\t\t\tif (gst_query_get_n_allocation_pools(query) > 0)\n+\t\t\t\t\tgst_query_parse_nth_allocation_pool(query, 0, &pool, NULL, NULL, NULL);\n+\n+\t\t\t\tif (pool)\n+\t\t\t\t\tvideo_pool = pool;\n+\t\t\t\telse {\n+\t\t\t\t\tGstStructure *config;\n+\t\t\t\t\tguint min_buffers = 3;\n+\t\t\t\t\tvideo_pool = gst_video_buffer_pool_new();\n+\n+\t\t\t\t\tconfig = gst_buffer_pool_get_config(video_pool);\n+\t\t\t\t\tgst_buffer_pool_config_set_params(config, caps, info.size, min_buffers, 0);\n+\t\t\t\t\tgst_buffer_pool_set_config(GST_BUFFER_POOL_CAST(video_pool), config);\n+\t\t\t\t\tGST_DEBUG_OBJECT(self, \"Own pool config is %\" GST_PTR_FORMAT, config);\n+\t\t\t\t}\n+\n+\t\t\t\tif (!gst_buffer_pool_set_active(video_pool, true)) {\n+\t\t\t\t\tGST_ERROR_OBJECT(self, \"Failed to active buffer pool\");\n+\t\t\t\t\tgst_caps_unref(caps);\n+\t\t\t\t\treturn false;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tgst_query_unref(query);\n+\t\t}\n \n \t\tGstLibcameraPool *pool = gst_libcamera_pool_new(self->allocator,\n-\t\t\t\t\t\t\t\tstream_cfg.stream());\n+\t\t\t\t\t\t\t\tstream_cfg.stream(), &info);\n \t\tg_signal_connect_swapped(pool, \"buffer-notify\",\n \t\t\t\t\t G_CALLBACK(gst_task_resume), self->task);\n \n \t\tgst_libcamera_pad_set_pool(srcpad, pool);\n+\t\tgst_libcamera_pad_set_video_pool(srcpad, video_pool);\n \n \t\t/* Clear all reconfigure flags. */\n \t\tgst_pad_check_reconfigure(srcpad);\n@@ -920,6 +1043,12 @@ gst_libcamera_src_release_pad(GstElement *element, GstPad *pad)\n \t\tauto end_iterator = pads.end();\n \t\tauto pad_iterator = std::find(begin_iterator, end_iterator, pad);\n \n+\t\tGstBufferPool *video_pool = gst_libcamera_pad_get_video_pool(pad);\n+\t\tif (video_pool) {\n+\t\t\tgst_buffer_pool_set_active(video_pool, false);\n+\t\t\tgst_object_unref(video_pool);\n+\t\t}\n+\n \t\tif (pad_iterator != end_iterator) {\n \t\t\tg_object_unref(*pad_iterator);\n \t\t\tpads.erase(pad_iterator);\n",
    "prefixes": [
        "v6"
    ]
}