{"id":23380,"url":"https://patchwork.libcamera.org/api/1.1/patches/23380/?format=json","web_url":"https://patchwork.libcamera.org/patch/23380/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20250516073044.426489-1-qi.hou@nxp.com>","date":"2025-05-16T07:30:44","name":"[v8] gstreamer: Add GstVideoMeta support","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"a7869d0f37c5859a6f0e2aee7e16009cf0efcdb1","submitter":{"id":195,"url":"https://patchwork.libcamera.org/api/1.1/people/195/?format=json","name":"Qi Hou","email":"qi.hou@nxp.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/23380/mbox/","series":[{"id":5178,"url":"https://patchwork.libcamera.org/api/1.1/series/5178/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=5178","date":"2025-05-16T07:30:44","name":"[v8] gstreamer: Add GstVideoMeta support","version":8,"mbox":"https://patchwork.libcamera.org/series/5178/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/23380/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/23380/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 65F1CBD78E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 16 May 2025 07:31:40 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6BF1668B6F;\n\tFri, 16 May 2025 09:31:39 +0200 (CEST)","from EUR02-DB5-obe.outbound.protection.outlook.com\n\t(mail-db5eur02on2043.outbound.protection.outlook.com [40.107.249.43])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9B8CD68B55\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 16 May 2025 09:31:38 +0200 (CEST)","from PAXPR04MB8285.eurprd04.prod.outlook.com\n\t(2603:10a6:102:1ca::15)\n\tby AM0PR04MB7123.eurprd04.prod.outlook.com (2603:10a6:208:197::13)\n\twith Microsoft SMTP Server (version=TLS1_2,\n\tcipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8722.31;\n\tFri, 16 May 2025 07:31:07 +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%3]) with mapi id 15.20.8722.031;\n\tFri, 16 May 2025 07:31:07 +0000"],"Authentication-Results":["lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=nxp.com header.i=@nxp.com header.b=\"ZBx9qr+3\";\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=t3H1c971Jdow9+UMAPGEMqiO2gIRvIWBIKiDU8pG91CyEQemuM9mj+TA8H5D+g5E2fArgHcB1IiK7PX3i/Bb9VRDPngBNoqEOVrK95iF2zoJAnIMVOw7/OiqB21cPaOxJKNyv/oI3ll1qNwL9ZFJGAoJjNjCoASbF21NxYbBHq7glfhXX+U2R6Tl6N/U5cr1BpWraxcwQmd1ibIMHDojgt7c+brz+DH86Rv47pj3iHjDsE1QqxvbGvS+Myi84X6RBD3bRit/JUK0Tf3GtHUDFKgCA/cZPx4QQs7dijK5re6CKK/yJd1icOWUzH94hbFQeaHK1mjMKlJIHE6BY8uOGw==","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=h/JkaxoPKaxFemfXU2hXYtYW4lWMtRnFBwnPWN/v11k=;\n\tb=XlzJ1m2g3HqXw6PRvxIoyE8g5ClDn5oIan7NE43cI5sMY3m5dkqF3Tr2jKrtnZoSJkhoRCjbviti+CmXdyVK53C+YH1TWU69KJjduYpSBWW8R1T9ZjoWA6iLtrQme02uhRWcDpcH7VTZZhttKOhuAXNq3hehBbmISVQSihNM12eDXpgKmnPSY5mFpKLkWhFT4oQoP81Ewv70Do0E69Dx6/q3xbaFUHwHFGROF2Y30X89ELbdMShfdi5UnWNhMm0VVA6CZfujl5NRhug2KO7t/dyk1SV6KiBk7uoc8qmc9SkBaQkhReZAMsTqMDdwQKg6M9V6/X2U66Ymnt+3YQVPQw==","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=h/JkaxoPKaxFemfXU2hXYtYW4lWMtRnFBwnPWN/v11k=;\n\tb=ZBx9qr+3SXx8vyY7RS1rVVxda4/gSnqiSzSj3wDCqPrCCDYU7UVZvuPoWhf2bpJegzM1YQHIW4SmQuoqAWWxSXLx4XwKR0kPpD2jS5LRi+hZ1Fjnk6beSFOKbH1J2a8LqSbDevTTpWPPjJ+T42cSwNZbjAKyIGGmkVyhwY3NDgYWbgl5cxmZYpKPO+6gHQgsL3Q2E0baq6lUTq2AY0vDmK/1XsUUUMXuM5QmVz1xdPX3fPyPdtyFy3gK+fEpRxKksrOTQGKzpKy9IncR/gqlvKQBGHFxa+xJZHq7TRsDUk5WvOVc1vG53bVnBQC6FTImq2ecZFWc9xeNCBkKLTT8rw==","From":"Hou Qi <qi.hou@nxp.com>","To":"libcamera-devel@lists.libcamera.org","Cc":"nicolas@ndufresne.ca, kieran.bingham@ideasonboard.com, jared.hu@nxp.com, \n\tqi.hou@nxp.com, julien.vuillaumier@nxp.com","Subject":"[PATCH v8] gstreamer: Add GstVideoMeta support","Date":"Fri, 16 May 2025 16:30:44 +0900","Message-Id":"<20250516073044.426489-1-qi.hou@nxp.com>","X-Mailer":"git-send-email 2.34.1","Content-Transfer-Encoding":"8bit","Content-Type":"text/plain","X-ClientProxiedBy":"SI2PR04CA0018.apcprd04.prod.outlook.com\n\t(2603:1096:4:197::9) 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_|AM0PR04MB7123:EE_","X-MS-Office365-Filtering-Correlation-Id":"1ca31102-5259-43fb-c8d7-08dd944b9ece","X-MS-Exchange-SenderADCheck":"1","X-MS-Exchange-AntiSpam-Relay":"0","X-Microsoft-Antispam":"BCL:0;\n\tARA:13230040|376014|1800799024|52116014|366016|38350700014; ","X-Microsoft-Antispam-Message-Info":"H+mEm3COidAPUDUTQRIj+O3Oxp5xC2DHjbjH2TIc54hjnm1fZ8gVeHekUPC6bjKkK+JkVHBu+dLmjQoE6NUA2hZqwzv+lZfhp5Od/u/zFbTn/gt4lvSyAlWtLwBEk/KNN1gOJXGohMtuyVFlQY1uxNn2Y3G6QBPFd6akt3f3N8sPeMheHW/pwC4SLQ4+jmTcbgjKzuWrrABrpvXzqajapibx+Dm3jjxyEMJgxhuj97DcsYcvg/fV98cdpdZ+eKPZBLRn4jU44RIYIUzRiqgxqKn1PymMaNXaPOoDAaSG7G5EIh+IYyXbNi6EjUvQLWQel7Av8Yq+y5+inLPzAGZD0TEtUVkFkXcnk/At/ku1GdwYg+jCgNWFKFDOnRxM/BDfF0We26+u6hWSq14y4b8vIWIj4zjkU+9FJ0F9l0imowSY3OWVH7vO88gW8/VUVWaXdApA6t0GpyAuAjJTBawYHSHRnxTnyut2gFy20oQPyDXceXgQf+uX6iJ214/ImTSVMgHQcCJVrojrSdCMFHMBvJaMk6F66UyEqDqkqNoC957orNd79UdCaOFU+GutkBPS5VZqSBWUBKAXSOooGjVlBmkwlIz8gN7WjXtPsSfl8zAaqMgAgEyEjpXV/H0E9Nz2EMGpEsgb5MAKLPM92IbL15x3uV7p8CWe4dwbp0TvjrZ33Zqevaj2SPUG3AxjbjzrOCtZJ0VboZgfCWeRWxNgaL7KHoSVcxjivDGf/L/cBQ1YRS4u/cirxRvL2iH/GNiofJT6x6E034DPNCSGRIBi6o0VdjyxiOzS9/M8mktMijjBe0xliUk0e5ZM+0XYS6zhFLI1CfwoNzxEOe1H05NZz5+M5/SUC9k97x2b9sFQ1Py6L0hW1H+qbNkORx+JYR0cnoNwFcakVD+RxrqUVDXpjDjJku5DeVfN5Uq3C5YvNoz3dWv9mTZty8JKzSN3vgqkJi5dWnDYWf0+aAYtCXllOubN4KrPsAagn2cHjPGVBEgn2rQBdGOnrkqZB/C3XeVUNnBBsnOQl1JPKLVT3ElDV4AklUbuUf9Yn79ApfpSf13CgdlBmSVqqls0Jk+W6IglK6vz1iiIVuKfKddgWvmNfhywa9CZuQqW6Y8d1eKiPHHlSg5n5czNd8XEgVscknfVnc4Rh/ThjG8jkLOJRJoVW3taurSybHhWKcvsOkMGLAIN15JsQJAayLQWm1GgsyWx9/hoJ82y9p9Ncy6DM9xKCsPyg8a1m3UdOw2Oz+ibJ868yH93UBbxcxLKvl6nvUVeEnaTBrAPWBBO0hQL5nQDuQf+3DWO2pcb2QymDnFGAvbqY5DuCSb/wflYtDI5CIzNFdrdWm06ObzZwzv42HAMDvfWCnNoLILPie1YJ1l9uNjREKt1CxK6dJ1fU+/I+o4qLp8yNZjKGlxBs67SiEgbg0oCMh38U8pVvRw8eWHID7gPAZm1MPtf3mhqkN0SCyTs5XIu6dIMD0C9GuP/kV/iIg==","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)(376014)(1800799024)(52116014)(366016)(38350700014);\n\tDIR:OUT; SFP:1101; ","X-MS-Exchange-AntiSpam-MessageData-ChunkCount":"1","X-MS-Exchange-AntiSpam-MessageData-0":"DyYDBtb5mXLGoij1d2gGWeteCyc4ehDFEnLvDhLA8ohbD7L8vC3OjXVpxN+4NnMVEAeKd6VzYLgSBSIZNU62ijmHUCDEdoT3VVfqNLf6eZRIbbBzKrGaBm8SQB1b94PlPCS0j1QyAxezJM9kZ3IaaJAc5Va+fMNpDehM/wORRn7znuJB1+/4aizuWsJ9LIBEn1YQ+nb59rG5VFd5blNYjISAHQyecMxP9QgMxrQTSs+WcUsUYLBqUOi8jSO0Q/k0d+BBsjQkRb5uvo8Tu7XP5EQyTehi0YUTm8X10T+TjElZl+WoLnKctQy0+DjtkwNwrRSqY9B0hYJid0qFyN66ZZR1i+h2+mRlDeVpwH8kgDAuL7/QW601EzlOdmpLGU0Mu+q/u/3/m6dcWTEF6Yk5iu4cVrkc2LcJ+EKHj5gqnkxVgnTAd+Cir5ysCZGGeWQQPONeyFr/S/UwLztbjAWpcMYNNZvFizHe9AXc1W9ErktI//vb6/n3fqclpE8rK4ae2qmdHyK3qHwUMSkQ/sV1QoD3NPo1DZS5Pua2/U5rO/6gCEec6pgq3HiakxbVBhsR6IwCTjzt6rB9SYHwhJ3MK9sLSd86JzThlG2rfwksgG0ZBSpiTu/XOTPtZMfrdqAiAjQN78tRtOZKlysEWzdQsAKurA1AlPG8qprZ9bebEo9/0WSkKbRQYWoBXC7oqSkag5sB4y//OuO4HQWcm42X84IS5h8LzruE/l8kWnP6MNI04SbOJDUl3QLCGWqW0LA+Bnu0i1ocYvLIhV/UZFgO0AKfHf4sXZS0Qw1Ds818una0Acyaf62sQdwM2/jmttwHYpFMwbPKzeqANs2xhZRhM9Q1y3cHJS2lWV9EatqYnLKfPctrBT+U6VpRRKwcxQKSgTtfN9oQqnBoHAcA7dxl1j8RigRx2UoVvgE9UimZgLPEkZm+lNjcFY+VDyg8IQ06bQAvKwNXQ3vhhRNZRHctR0alnkQ0tWW9eLIf48jf7h76tLQMCD74Pg8u+dcSs9/2bUJm9sGIK8Uu1Kl7OCBJy6WI8ZQQXW/VjZiV4NoVCsshtfwhDuIdV9JY/r1dohrTD2MJ/UCTn+zAqcbJ03ujsIn/C9aJg263IhsEp8TBnJUOekQprrvtUh60okI2OpOhrLMxCLJi5VymU4PVQkclXmtva5vI5tULbAA0jCWxoZ3FPRbgBM9nwoxMIsh9bqrjQjht+N/c9yoUWf0cEc+m4FWys+sPu5IFC7VqMv8ZBFyjMVq3kPHiYj1g3i5T14e+KIvf/c8Pgr25SHQvczoU/oLp9B5OBfsrhGPHeXKHkmI/r7nJAbqOUGEmfkC2oVlla8/kfzZYKoDSqptW1ZAgJmVEYQvQMcSId1kMQrv2a//Sp7XmoeK8n2xZveiieT3A9DHB2Q7WOb0tiGxf/mnLrPe4LVX2Iz+2JhDcYSCnRQRu+q7FSLbG7hTG7WIwm9MGOeADj/Ze2NngUJM3E9WyMl7AborDR/DrlTBbquRlli/+okB6GAA5t5Qf4Ujz+Bki14FUbUkY5CtRVp+T2doZHl4RecIhNRuXZi3+kUJsGK4d0iDuh5LY0ZMxp2fFVyKx","X-OriginatorOrg":"nxp.com","X-MS-Exchange-CrossTenant-Network-Message-Id":"1ca31102-5259-43fb-c8d7-08dd944b9ece","X-MS-Exchange-CrossTenant-AuthSource":"PAXPR04MB8285.eurprd04.prod.outlook.com","X-MS-Exchange-CrossTenant-AuthAs":"Internal","X-MS-Exchange-CrossTenant-OriginalArrivalTime":"16 May 2025 07:31:07.2841\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":"WHzYLDI8Enn8cndC0QusXmUsM+ACGil0i4jh+jg4nltTHBu/8X0la3zHqGamO0v0","X-MS-Exchange-Transport-CrossTenantHeadersStamped":"AM0PR04MB7123","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>\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n---\n src/gstreamer/gstlibcamera-utils.cpp |  33 ++++++\n src/gstreamer/gstlibcamera-utils.h   |   5 +\n src/gstreamer/gstlibcamerapad.cpp    |  31 ++++++\n src/gstreamer/gstlibcamerapad.h      |   8 ++\n src/gstreamer/gstlibcamerapool.cpp   |  15 ++-\n src/gstreamer/gstlibcamerapool.h     |   3 +-\n src/gstreamer/gstlibcamerasrc.cpp    | 147 ++++++++++++++++++++++++++-\n 7 files changed, 238 insertions(+), 4 deletions(-)","diff":"diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp\nindex 2edebba0..bf12cb07 100644\n--- a/src/gstreamer/gstlibcamera-utils.cpp\n+++ b/src/gstreamer/gstlibcamera-utils.cpp\n@@ -599,6 +599,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 4978987c..5f4e8a0f 100644\n--- a/src/gstreamer/gstlibcamera-utils.h\n+++ b/src/gstreamer/gstlibcamera-utils.h\n@@ -36,6 +36,11 @@ 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+\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..3bc2bc87 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, const 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..f98b8a7f 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, const 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..8278144f 100644\n--- a/src/gstreamer/gstlibcamerapool.cpp\n+++ b/src/gstreamer/gstlibcamerapool.cpp\n@@ -134,8 +134,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 +157,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 5e9e843d..4b81c94e 100644\n--- a/src/gstreamer/gstlibcamerasrc.cpp\n+++ b/src/gstreamer/gstlibcamerasrc.cpp\n@@ -268,6 +268,58 @@ 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, const 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\tGST_ERROR(\"Could not map buffer\");\n+\t\tgoto error;\n+\t}\n+\n+\tif (!gst_video_frame_map(&dest_frame, dest_info, dest, GST_MAP_WRITE)) {\n+\t\tGST_ERROR(\"Could not map buffer\");\n+\t\tgst_video_frame_unmap(&src_frame);\n+\t\tgoto error;\n+\t}\n+\n+\tif (!gst_video_frame_copy(&dest_frame, &src_frame)) {\n+\t\tGST_ERROR(\"Could not copy frame\");\n+\t\tgst_video_frame_unmap(&src_frame);\n+\t\tgst_video_frame_unmap(&dest_frame);\n+\t\tgoto error;\n+\t}\n+\n+\tgst_video_frame_unmap(&src_frame);\n+\tgst_video_frame_unmap(&dest_frame);\n+\n+\treturn GST_FLOW_OK;\n+\n+error:\n+\treturn GST_FLOW_ERROR;\n+}\n+\n /* Must be called with stream_lock held. */\n int GstLibcameraSrcState::processRequest()\n {\n@@ -292,11 +344,41 @@ 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 < srcpads_.size(); i++) {\n+\t\tGstPad *srcpad = 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 = config_->at(i);\n+\t\tGstBufferPool *video_pool = gst_libcamera_pad_get_video_pool(srcpad);\n+\n+\t\tif (video_pool) {\n+\t\t\t/* Only set video pool when a copy is needed */\n+\t\t\tGstBuffer *copy = NULL;\n+\t\t\tconst GstVideoInfo 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_buffer_unref(buffer);\n+\t\t\t\tGST_ELEMENT_ERROR(src_, RESOURCE, SETTINGS,\n+\t\t\t\t\t\t(\"Failed to acquire buffer\"),\n+\t\t\t\t\t\t(\"GstLibcameraSrcState::processRequest() failed: %s\", g_strerror(-ret)));\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_buffer_unref(copy);\n+\t\t\t\tGST_ELEMENT_ERROR(src_, RESOURCE, SETTINGS,\n+\t\t\t\t\t\t(\"Failed to copy buffer\"),\n+\t\t\t\t\t\t(\"GstLibcameraSrcState::processRequest() failed: %s\", g_strerror(-ret)));\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@@ -499,13 +581,68 @@ 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, transfer[i]);\n+\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\tconst gboolean 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+\n+\t\t\t\t\tGST_DEBUG_OBJECT(self, \"Own pool config is %\" GST_PTR_FORMAT, config);\n+\n+\t\t\t\t\tgst_buffer_pool_set_config(GST_BUFFER_POOL_CAST(video_pool), 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_caps_unref(caps);\n+\t\t\t\t\tGST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,\n+\t\t\t\t\t\t\t(\"Failed to active buffer pool\"),\n+\t\t\t\t\t\t\t(\"gst_libcamera_src_negotiate() failed\"));\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@@ -922,6 +1059,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":["v8"]}