Patch Detail
Show a patch.
GET /api/patches/23379/?format=api
{ "id": 23379, "url": "https://patchwork.libcamera.org/api/patches/23379/?format=api", "web_url": "https://patchwork.libcamera.org/patch/23379/", "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": "<20250516014315.4015034-1-qi.hou@nxp.com>", "date": "2025-05-16T01:43:15", "name": "[v7] gstreamer: Add GstVideoMeta support", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "33093f74dbf16104df8b9966941712047e96f327", "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/23379/mbox/", "series": [ { "id": 5177, "url": "https://patchwork.libcamera.org/api/series/5177/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5177", "date": "2025-05-16T01:43:15", "name": "[v7] gstreamer: Add GstVideoMeta support", "version": 7, "mbox": "https://patchwork.libcamera.org/series/5177/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/23379/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/23379/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 CFED2C3200\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 16 May 2025 01:44:12 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 76D8B68B6C;\n\tFri, 16 May 2025 03:44:11 +0200 (CEST)", "from EUR02-VI1-obe.outbound.protection.outlook.com\n\t(mail-vi1eur02on20605.outbound.protection.outlook.com\n\t[IPv6:2a01:111:f403:2607::605])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 10E21616AF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 16 May 2025 03:44:10 +0200 (CEST)", "from PAXPR04MB8285.eurprd04.prod.outlook.com\n\t(2603:10a6:102:1ca::15)\n\tby DU0PR04MB9348.eurprd04.prod.outlook.com (2603:10a6:10:358::5) with\n\tMicrosoft SMTP Server (version=TLS1_2,\n\tcipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8722.29;\n\tFri, 16 May 2025 01:43:50 +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 01:43:50 +0000" ], "Authentication-Results": [ "lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=nxp.com header.i=@nxp.com header.b=\"iIG6gqzc\";\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=Srz76LJsWwyrKYNhQLnWBJqLVv6u+iq2Rrkix+mRvQHiN/ByR7+CIBAb6crTw9yjqYpIKZtRhiwaMkh9Qs4UhmKKHgdalK0VkYwNLyia32zZeXToN3N5aThBqgWT9YsxqN30AsKc5omfpDKG8zhadTAFJJWio75IST7i42o2cIzyPqVhE6bM7/FFEUhINWI1t1Uu/JH+d0UX1qhsClxyKpZfM/mdjls5FbnRntw4fx7cTvei9XdGHwnmLqd2nHVZO4B9YwnnHUOnUaqTO285EStf8ToolhA91mEwljuRXg+HTTYnw2nONR2uRKWycUT/hfezlPyZTS3bJS7xyxmrQQ==", "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=I3wsP6MIZTaFaVHy2aI9n8l/+QiHW4Qxz4YG15asXUI=;\n\tb=Ox6BCdlptjRrlW2hMtg01F/mcua1xsGKW7qdO77MAXjav8oMGvG5KdTQfvr0oFNlHniW9jtXGikX4SW5u65F4mGnDRNgK3ghRniS37GqD45z7qRN3eZAS5XroN+3QI55aFjnp0ln0JU+aqI+VXfim/ZVyDN8AvvscIhxenspeVOZmFUCF+abjPZJo/jp57KsrkISmO4VK1E9iKzpIXxLyjQ0M7BSc/DoEDHmyeT1UUIMjJS670sb0iFytQxNwo2QY0fzbk2Q2AvMZABSqN2D12b0zQIyEpfigufpqYeKE+yEY8uZs6QQNQiuqcI/6gfyEL3rDyurPt7ktqjStx5a3Q==", "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=I3wsP6MIZTaFaVHy2aI9n8l/+QiHW4Qxz4YG15asXUI=;\n\tb=iIG6gqzczP59NC+3qP7ZO4py+IfmlrKZ9I8S7djtKA8NlREiSlmZU1i8LZq0Gr4jxRbidMEoklQx/WMsVgc3P16jAcLd7PMqPVOEEJFhSwa0BKUw73I8sWTCwJrfI1WjM6LWWKaTQ3QG5Uxoa7qBZBqFpNpofFQ2iJwJbLHaQJbYhmNXXuMXnXZGy1KlfUA6bFz0+cp8rgODZFZLBdLATQyDl1E8KxZAkBWjpqobx/GBJeYtUyMGXEESPCCQVwlKrODrj27u6np5Bol8MaknBRI08VzMuIzp9zVu+Z35h19ZRkLF1a99Gb/7FP9HnzFegSShzIuxWVDIdBvFN/lHRA==", "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 v7] gstreamer: Add GstVideoMeta support", "Date": "Fri, 16 May 2025 10:43:15 +0900", "Message-Id": "<20250516014315.4015034-1-qi.hou@nxp.com>", "X-Mailer": "git-send-email 2.34.1", "Content-Transfer-Encoding": "8bit", "Content-Type": "text/plain", "X-ClientProxiedBy": "SI2PR02CA0039.apcprd02.prod.outlook.com\n\t(2603:1096:4:196::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_|DU0PR04MB9348:EE_", "X-MS-Office365-Filtering-Correlation-Id": "8a846a55-aa3a-483d-22c8-08dd941b1b40", "X-MS-Exchange-SenderADCheck": "1", "X-MS-Exchange-AntiSpam-Relay": "0", "X-Microsoft-Antispam": "BCL:0;\n\tARA:13230040|376014|366016|52116014|1800799024|38350700014; ", "X-Microsoft-Antispam-Message-Info": "+BaWQobPZKBrNzqB2yOUxQK1K9DVClbi8HNdRq8mxT1/DoV6BvWqsbzVDwN2CyKflyerl797gavcv5ibKL7wQmXCkcmCnzjMn7MueSDS7I6qKHgcwx5DU6jigoRz9mmpOdh4bce34M+J6fZJoGzmbTLzgb8TYhReu0xb9dd8hvym/rN2Q7fxeEaJPEuyZx/3rjebGHm57T6Xl12niX9OrbdXWMi7wxxLAzkgMp7CIqj2dRw3fEtRpIe5UntgOpwM0x5+GT8G87MvOF8AfIeTECVyHTRxaWnK2GTv9iZmB2gPQ+tCnOuZ6/KCXUGNq6u7cZQClMFdFdAI0OgoSrZN3SdXDnL/7efDG0WZI8ic0xkoFbCQHftYxxripm0TqoAbUR26yI80q5z33h0Jw95mPty+D9BiQ+4I4psHVAdaNYYLogEIiOjwip2K2XRGKTRwPgXRIxY4Ky9dWdhagysrTzUtz+PLThs0bwUnTc3bB+OKM0Kymd9sS4xZ7kcKDckI8yvhGP5TNNrTgG+Tqt94US6Nr9Q2laYO9Cx17hqzlSRMDWKdyMOkXqaOLfN9PEFeNzFBxknqX1NxwbK9/q8CPGQvLgCEVuNzHVtoXTVXddDbTRpg6X3A0HkiJREA5cDl6kf4dHhM8GCH00f0Qvw/bRuFb4X+o0ulqhq64gZJyeFq9elyKiPmnBYpouLjg5OtZeCyCb99Yc936gLxAEPtZhCDF6iPAFdSygZTaEba0mKHk5IRpUxN19dLp16o/nXDn893NQkKNJGL/M5GsRoF+1O/X9JrGf3b2GspCCbAyp/eNiQ0UulPF5k7iUDvbhI+YGs1xhQkqos6h60rt6zQwEpvpSEsmxSQHyqYDOV1sp6mPfXj/QJY3gzwSxI/nZgrDWXM1FNIM+ddQfNqcXvD5t8DYK8zaraMnkJFJC+9ElRV2v5PsGEkbk2JmLaToH1adJUI/xRhBmfEZaO5cpyuE0liOsjANKFxdF15G5AltdErXZNSiEdpj7VUlgue96aKkSB3/VpAvyhv2qbHXQ13ITfvzA3Hs2ktWjZNIFHAJ9fDtFEKTjJcQdsRG9NJZFzkEs2GhbiP7/b2wk+CPdduj/K6SJ4eNpf9eI0JRP9IKq4TfJhgaUX+Owvb/N1JyqGZSDQVZtJRqk+BXbVoW1Xvsk4S/eFCfiaSw7oDfbuL9VqIMQiwSX3lsZMRMwlOYUfvQc1UsG6wT5GvkD0rK57QgVoCJa4zjTnRUbK1Y7H7kOy3iQbTTRh+6sqPWj5uXAU+Z/zKwpxxpQTYcvKDr5zaGbWodilfwuIbgPfDCU3oGTpB6rhov+hqZ++4AgY9FALySFVPmdAPssVHM1qLsts50rfELnxYQQ1XCH8pB6+m5+Niurd4jukxDFKbCoiNRd9E0dZqvRNGF2vSvh19PC8E/Yg2qdhLITFv5amQzXmWPjIQnUiI1tqE4G+wHH6m0rsjmdHd8i8pXD8I+O0cKig1Jg==", "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)(366016)(52116014)(1800799024)(38350700014);\n\tDIR:OUT; SFP:1101; ", "X-MS-Exchange-AntiSpam-MessageData-ChunkCount": "1", "X-MS-Exchange-AntiSpam-MessageData-0": "05PjfsjHFylLC8o37f0ikf+yQ3LqBIJWjlJqH5F1wRQfiXU3Y0qKB+AJURZniSqNLENNRUHxX7qJobLKSd1Ixt4BYo2CkZr7kGO8DnrmGAGTcywiVOXDHLisWnN5FPuBvzzK4sBTJYkBVhuHUJ9+47iEA9zMvv/9omP407azor6vZjqMv7s172a5lSHvzu3tuRtD+Ez0GprllcncWUxNX9Hn+tOCt8dyhVWydPu4PK+k74IfqRMvZC293feq9VFbapBTgUuRQ3iWR93GHlAbHpexOTxKiN2KyOhqEaGL29Mz678zNYKhLibM1hQ1IhAjeETB8qkUegMZe1xWX2u9VMlum3FWBRQn/ZUuYEb7RkfHQx8JOj9z5yvTUvns4e+ZfxTFubRgc7t1wnLtAg/BWSGhQNoKuihUjZPQggAcZloVdJfEWuXt+L0XMWov/dnM3sfkMTAmHJMpafems1JyMjzDijAFWl8PtRXGBZxPLyRnP+n9ZOK8f+S0SG5vu8pYiCCRptpIjGBkOhVh8GsTsGZ0E7TdSUkif+PS6k3Q5IU0snAop4qinj9mNjbS/z2u+B6ESVRB6zgOFDtWFRAtWm3vKcKvA3LAY3Ib5L09gzSiT39ou0V47ccrt6Z9ZRhwkFOre/PFQzSr9XX2hLNBfU8wtrBEYGO8fHwELgd5bWRTHXAGCRUvJlLKWhytDUFDqNOXxXu73R7KwFQLIfdh8pP83DzxSKXHE3eslEhpXhHU2/Wem+He+3XPBcv1Ou8rP3unxRdw1PiG8s4bNHhcpth8PT2D3IRZzlm1p8Velke+IR1NHdNgGDDmGqescFOtIhT2TXqoOroma1E9gpmZ9wtej5yKAUOJ+cKKVip+Rdkuphn2HVJOiNheiGjZc8xd41ofMeZbzLLPely5Y1P2opqA+Wghi+VJtlT9QWhtVOwJS6StN5/3jdlkkAICkH5SoMCLTTq0Tlo+tYbtkQJ1ee3Nz4UUt/hrLcmJ5ncBtc9wJg/klenzvDcf0PflEKS7SMruvxcGjp0w+GgPlLEmfFw8kAIYDbjKyAfkjER6GLz1ltB1nX1p1uJZcA4NqV8U4c6FYBJOX/d0jzf7Cr5UhEvg4RX0NDFKDPDU5L9qCrPer+Lg+DBVtqb1DNqh6Hg4XkO1nrGQRZpr5bo65JQ8qaD9h2Zg98PUmfk2W/+csNSlEkkQyWthY4cJZ2KXey1CUAV6MFeMUHtweS+3n+jofZ0ayKtc73Q/5tF+M3DwViBmS6DQ2XZLbs+UscYaFw2C4k6LcgbYmRakFmQLLgy44g0L70rudahnilz3ZMtioau1yivhj/j2HDtPhW32d9UapzBE0TLGow8chGmsvzhDN79+ti/AZW2lWh5WoX3siD7q+/DyZZ/cxpjxEm97rRBqpwKuIsd+HSBA17/6n8mj9Rol5E35uclb/lE5+3n70Hz9LNoobW+yu15KVCgjeu70wiR3lRm1J/r3lGgn8sglLJhJZ4GVPlwcJfVLKKJAqPbAboY00q9/H25A7v0++fl9EympW15EHBzam9/iYbnfYsvbkKkMG2tIapArmeywZT8=", "X-OriginatorOrg": "nxp.com", "X-MS-Exchange-CrossTenant-Network-Message-Id": "8a846a55-aa3a-483d-22c8-08dd941b1b40", "X-MS-Exchange-CrossTenant-AuthSource": "PAXPR04MB8285.eurprd04.prod.outlook.com", "X-MS-Exchange-CrossTenant-AuthAs": "Internal", "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "16 May 2025 01:43:50.2297\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": "3SChhL/4Syx79fd4HY1b8lHFC0z4pgheA8jzbs4CCFSvdW4jwL3XBYUgIX/gBeT7", "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "DU0PR04MB9348", "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..7275855a 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, ©, 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);\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": [ "v7" ] }