{"id":23382,"url":"https://patchwork.libcamera.org/api/1.1/patches/23382/?format=json","web_url":"https://patchwork.libcamera.org/patch/23382/","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":"<20250519075210.3765048-1-qi.hou@nxp.com>","date":"2025-05-19T07:52:10","name":"[v9] gstreamer: Add GstVideoMeta support","commit_ref":"848a3017b8eee31eb94add447850b2751f38aa62","pull_url":null,"state":"accepted","archived":false,"hash":"f8cfe0e3c96aa692ee381740571ef26eddd80cb6","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/23382/mbox/","series":[{"id":5180,"url":"https://patchwork.libcamera.org/api/1.1/series/5180/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=5180","date":"2025-05-19T07:52:10","name":"[v9] gstreamer: Add GstVideoMeta support","version":9,"mbox":"https://patchwork.libcamera.org/series/5180/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/23382/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/23382/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 8001BC31E9\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 19 May 2025 07:52:40 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 15A7F68B66;\n\tMon, 19 May 2025 09:52:39 +0200 (CEST)","from EUR02-VI1-obe.outbound.protection.outlook.com\n\t(mail-vi1eur02on2062a.outbound.protection.outlook.com\n\t[IPv6:2a01:111:f403:2607::62a])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 98676616A3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 19 May 2025 09:52:37 +0200 (CEST)","from PAXPR04MB8285.eurprd04.prod.outlook.com\n\t(2603:10a6:102:1ca::15)\n\tby VI0PR04MB10391.eurprd04.prod.outlook.com (2603:10a6:800:236::15)\n\twith Microsoft SMTP Server (version=TLS1_2,\n\tcipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8746.30;\n\tMon, 19 May 2025 07:52: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%3]) with mapi id 15.20.8746.030;\n\tMon, 19 May 2025 07:52:34 +0000"],"Authentication-Results":["lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=nxp.com header.i=@nxp.com header.b=\"AhVjIeEO\";\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=HLufjz6HwmbAQ1uDswFn2ttOC9b/1Zajae17wv9q0m5/C0WKRrCbzSuI15F/sXtCqVUuUafeF2Sr4acbPX7bTTRh590S1/nSORdlQ3QfqHxjDNPvA4+FYrNvJR0pWCWUCEwizODCtjwcCVXnYnUc9+Vnca/u+H6bpnYdY59BCv17OP/esmNbKj+Kq48GjO8BMpRH9jxu018pp39MdYO8YnOhtgdsFiIIKEApYjIR2+ZrmARrin7iZj1rGM3Zopb+YhtoLVKYsgZs3/8D09EbfLbeN95be3tVLszd/JYeDRL9oV8LB9oSMVaDCF7boSdRGWxTApcTqrBjKy+ryFv1ow==","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=75YyOYrx3rQonEtmjGCxidwn2LTPy5Wh4DJnm5lWOEU=;\n\tb=xvot/G6mVvDTZlGaNLqRDI54J3QO4l1cudIVTzGOBVS6JL1RPHHTSAu2fraN0CC2sTRz/PowG6zVw6BCo+XPgfg8vM0a9P8GllRiAxvLEr42rWXS7Spw7Ik9LCqx3uofpUda4RwiFXWbs+z6GVUIDmd0Z3st9K1fQPAlXJnCDpa3BHDEyNQh+BkQ/abl3wYL7NHwBRUNMoUtniFnue0Dya3p98HtUbxfu80uh2b0EMNVphn6/X4hL/jy13dqD41lXcEu1uQgO+C5cZh2cjjzhjcvjf2p90Zz2b1SsiWhudN+OtQkkDqy+ky5w7hpmu9XeXokZeAdeL8sDah5ZFPzPA==","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=75YyOYrx3rQonEtmjGCxidwn2LTPy5Wh4DJnm5lWOEU=;\n\tb=AhVjIeEOOc6dRkH/Ul2RPbGloTbubP2lfKvwc2yFyxMTieeufB+KiAN2zRODWzn4ZZfAQsXknSMB95WVv4kIrsD5HHlcNcZXPILKNcrQqhYEYTY7ZIk++svQ803gv3UZ78B+2uOwP0NseKjGUOsj4wUiwuoZC4TX4urw82rWECHIH22lGtT66HobIoDLgWK+xNR0S8zDoUnu2LJjjj9QbYdsEdt44I8NRRbyDPsbL5k98A6NtIK6sl3vhd/D/K5lKas0aSUNL5KDaKFUPz2uLTIZATirEHj2Zaht5fXHUE8I0yqe/yAfy+vT+O+8UMaHCEjIVscBseG3QAjD6J7YTA==","From":"Hou Qi <qi.hou@nxp.com>","To":"libcamera-devel@lists.libcamera.org","Cc":"nicolas@ndufresne.ca, kieran.bingham@ideasonboard.com,\n\tlaurent.pinchart@ideasonboard.com, jared.hu@nxp.com, qi.hou@nxp.com, \n\tjulien.vuillaumier@nxp.com","Subject":"[PATCH v9] gstreamer: Add GstVideoMeta support","Date":"Mon, 19 May 2025 16:52:10 +0900","Message-Id":"<20250519075210.3765048-1-qi.hou@nxp.com>","X-Mailer":"git-send-email 2.34.1","Content-Transfer-Encoding":"8bit","Content-Type":"text/plain","X-ClientProxiedBy":"SG2PR02CA0016.apcprd02.prod.outlook.com\n\t(2603:1096:3:17::28) 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_|VI0PR04MB10391:EE_","X-MS-Office365-Filtering-Correlation-Id":"ae822321-fe5a-43aa-b7c0-08dd96aa1da5","X-LD-Processed":"686ea1d3-bc2b-4c6f-a92c-d99c5c301635,ExtAddr","X-MS-Exchange-SenderADCheck":"1","X-MS-Exchange-AntiSpam-Relay":"0","X-Microsoft-Antispam":"BCL:0;\n\tARA:13230040|366016|1800799024|376014|52116014|38350700014; ","X-Microsoft-Antispam-Message-Info":"cAGtxxp7zmPCTbJAvejHOXR9uyKwlrrfsPWZNQFB7HGXnQ3OizfPNSZkZoX7JWt/P8+uOGT487zt5FYWEH6bAR4zKuetsnnFSMkxOup3ywz7kpf+rF9Zsb6nuiYtWEb89QUE3l1I6cll8x1zTd4x1OdTgGPgEzoN9iD/Evqo9oMRrDuHJhwTLKAMKk7dub03hjVFtZID6j8rvTRHgB5ew01uEI0EpWc4SSHu7/8/dVCBJPAp/YoOMP/3BwFi9GPYucHaaYAPJN98Ohe8lnmV6u1yinKEwbnfksXlXpQheY/9i8YqJQ+a638Yvw3slDAMl56+4A7tVTq8FjjHLlMS9N+BQbPxiD10bzjl3TS4GFmDTKLS9o7jbh6paiMqrMciZY32VvFQNMJol3UV4gyc0KnO2utfCIfT+gxMoBQKkalEsKJlFEOqBB+kcm6qwR9LMx6dObkYi+HKiahrbHu7ptjxp9/ro0RdlI42Zdr+O/SNvQvXlD+mr5axMUwPQ1GAmFE0IZws2o/tRKH71XmPyOUO72IUhMU/L5CHGML5+ZVLL1FoC9m0udnk4HI0jjfCJs80VWdJ+H/tHDVz9pbyxmaU3GpSP3gNdREIC2IadStNryiKhuP8hyYjYEm9w1WDOWpWIn9fK01jxnQNcP3kBI8S8JjAPEEI6VRv/XXysW4eXx7YPZLyix/UOXIysBIQXEMm/Z+P/0dkwjDPaOroqQQZV9cUTr/2gCbAOlzey3Jn86aOLNUCYba7Zf2Tgj0FXTaQ23nyMTWOytHflbHcKNgrn63iSoIyvOJtW5N7jSyK3GRRfYwN0knUIPcHHxXrw/c2xSnS7FNmO212T47bcEx7cxDFqYL43XFsWWjrhnxlhNmaS8G4kjMYdT+/yfJ2qzDCIi9JUNf1AkLNVZUE2eIJdDmlh8rY2Sf2xpX4Rp9sEDYevjSXmVXyYvX8JO8qKervK+0PTHcNM472MM+HuKCiZADIBflTu14IGhousPnveZlGmQTVVjFTjJ4G8cNJU1BY91y0eqbPNH7bmDsBCEseSu2Q0GMELHGuFcaOUXTPMa/Kb7SCt5tAxXdb/WimCmtQr8h5u3SDfQnTR8hq70BNUEORAb0agKZ6RFRWTUax9PliN+USHqif8152c2AoZ1OZqYq9a+Af1BYVyvJjdebeD49O4WQWQCesIqJ1xENewjgXnT6ihLGSlsoKjbaDp1Q9SYD73wksJQ9Az8auL1ILIzVwg6Y10yHhmr6owznWjxB2h/2OBoZi2VwYiA2/+bEvNugkyhgVOKDT8+jkg8e8/sdR0E4rB+uWsa0Kdg2Lx6uWE9tcg+fV9cMje7T78T5UCnBz8gBjpO0yjNBAVmSfQBEgHQ7ALq23MoiKrr0LU3qnzsm8IA5z6D5Bn2T90ZasoLpxIkhO/p6YW4ONjFS5Y89nJoZWtzDWMvlQRWZHxqqpCbW3EBlAnHGM1vGyV+FTdUb19wIhGvltB5uAwA==","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)(366016)(1800799024)(376014)(52116014)(38350700014);\n\tDIR:OUT; SFP:1101; ","X-MS-Exchange-AntiSpam-MessageData-ChunkCount":"1","X-MS-Exchange-AntiSpam-MessageData-0":"/IZvSp0ex24FQdDGPFbngmp8BIR5OWOu28Guw0emdWKgmdoYYurtqKuGQ0a2h3NXbBbzuxMVF9soZOVIR3asNvMvI71YDFF+oEReyX2Z6df+1YP4/Iu5Kb0BoaCkinScmuVro39ZBicwBYulsmj0F7DreI9PIFRu3GAHa5oRiZeQWyb4XcLE+gB5tbTKAz1gEvFviS1+y8YEEMK04CalF8P38PYYcdIVVpOQuJogqv765rVGUN1/oCgh3E5zaAV0pCP3s+XiuMeWAbmSssL4RJ095CIi2JwalA8tdtcm10NeFOLG0WXn+l7IMqHOECd5qGVlQ4UaWigV5ne2DAY7HkT5Ae3IGvQt/d90zLvXaA/rUPfdtF4Eiwu7du/kbM3hqe+So2miOM/QMNr9acr+EI4WlOCzEWe+LAmcAGZfz88Dl502o4SeDqOu022qnFvNRKBoNzijnz0JWtNhLW1vxJIkydFhsLteaHssmBWScWgXe38QytyhSwcZwM94wkyfY8faRZ61+l2ZyZmJM0HKwHfjwWnYP+WPCUPDgXsx/lWT5WTv1SlxQNHu6g9oZ1qvt+FWU0TaHiAW7d7IVDvDVAvG298T1nAta5txRc7cA3tLF2PVOcREiLDg15YDM2sWbTSiUXjE+bfBmdxbfFPpMVDxOsYOjnW39orljbUdDWUXwl/c6Pw6RTp4jMnaXkaz/49qfccGpb9JuVMJj0HXinAIXUV3OwjlDbGxD9IhMCDlblMmfgnX5MApM52rCxOJKTnidFfpSoXNGAq57GGWEqXZXoKZGTDYJcfvCFOHMfV30e2XWvDmKxwfoYRERyBjgGpjt9iUj7wZdUOD9HYpg2n9PcvTT/CQnuP0E5bNi97zLRv4DUqe+fwCDusMH39OqyKweDpFkeWI67JaeSNcjLw43ieHu+uRgxRKxUHD4p4e8gFd8Gfvxaxt/gKx50UQ388bxEuHn8/CiGgbbIThGyCPK4BDy33RKWpAHwNS3R4WogL8uxhSfVetp+hfQ+ElDtktSpLW5kHMquM2lo641vlvGk91AUWhpl+QVRfi6EQSUnHq40nSSnACWh+EndRc4CsRpoRN5494JjHPc2bw560k3fQrDaKHmxaxnVyrgNXXx2rL5mU5pOwE7291duMN8y1OXOibeqHiZ48CgDaqHELYpiOsCA9Wi0yyJlwzhxJWVFtwfuxGU+qZDCIkMDXepJcFRbaJin6Zui/QholX8i19zIgjv0btjuAbKx5gLmMaj9cO2wJn6ESddrvNVnHbHxjTdaElim1D7WlKcOhWB93I6i+SC0ArCXoSSGscf94IETWg1VQDBCSlq/sRVmjHbdBd+OdbBPr4TSexeUvUyODrfL5/JqDiNPY1QGSSHm7+3GLbTWA1TOykYbvx5sL7Cz8DOHzmCz+zl+W9M11rlknG6y9hlIuYngcvRWcy/V1CNc7nyLxAXjBf6HdD8uiKTJ0BQFIKMHk3mUZ9PA2V5UFfD7K+kiRJrzE3Mnrnr7X/FAM3IqoYidUKw0/ogBwLdeTXm0jDBcg0nHYcoguA9Gj6RISuPi+++LApi8K65vA=","X-OriginatorOrg":"nxp.com","X-MS-Exchange-CrossTenant-Network-Message-Id":"ae822321-fe5a-43aa-b7c0-08dd96aa1da5","X-MS-Exchange-CrossTenant-AuthSource":"PAXPR04MB8285.eurprd04.prod.outlook.com","X-MS-Exchange-CrossTenant-AuthAs":"Internal","X-MS-Exchange-CrossTenant-OriginalArrivalTime":"19 May 2025 07:52:34.5054\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":"IbX4BUa94A/pQkHjgOSWGfOiNaC1l69DOq7PX2WJrpN+W+EPmdveZ6KyiEkCdZYB","X-MS-Exchange-Transport-CrossTenantHeadersStamped":"VI0PR04MB10391","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>\nTested-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>\nReviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>\n---\n src/gstreamer/gstlibcamera-utils.cpp |  37 +++++++\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    | 146 ++++++++++++++++++++++++++-\n 7 files changed, 241 insertions(+), 4 deletions(-)","diff":"diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp\nindex 2edebba0..a548b0c1 100644\n--- a/src/gstreamer/gstlibcamera-utils.cpp\n+++ b/src/gstreamer/gstlibcamera-utils.cpp\n@@ -599,6 +599,43 @@ 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+/*\n+ * This function has been imported directly from the gstreamer project to\n+ * support backwards compatibility and should be removed when the older version\n+ * is no longer supported.\n+ */\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/*\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+\t */\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..b34f0897 100644\n--- a/src/gstreamer/gstlibcamerasrc.cpp\n+++ b/src/gstreamer/gstlibcamerasrc.cpp\n@@ -268,6 +268,55 @@ 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 src buffer\");\n+\t\treturn GST_FLOW_ERROR;\n+\t}\n+\n+\tif (!gst_video_frame_map(&dest_frame, const_cast<GstVideoInfo *>(dest_info), dest, GST_MAP_WRITE)) {\n+\t\tGST_ERROR(\"Could not map dest buffer\");\n+\t\tgst_video_frame_unmap(&src_frame);\n+\t\treturn GST_FLOW_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\treturn GST_FLOW_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+\n /* Must be called with stream_lock held. */\n int GstLibcameraSrcState::processRequest()\n {\n@@ -292,11 +341,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 +578,70 @@ 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\tGST_WARNING_OBJECT(self, \"Downstream doesn't support video meta, need to copy frame.\");\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 +1058,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":["v9"]}