From patchwork Fri May 16 01:43:15 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qi Hou X-Patchwork-Id: 23379 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id CFED2C3200 for ; Fri, 16 May 2025 01:44:12 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 76D8B68B6C; Fri, 16 May 2025 03:44:11 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=nxp.com header.i=@nxp.com header.b="iIG6gqzc"; dkim-atps=neutral Received: from EUR02-VI1-obe.outbound.protection.outlook.com (mail-vi1eur02on20605.outbound.protection.outlook.com [IPv6:2a01:111:f403:2607::605]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 10E21616AF for ; Fri, 16 May 2025 03:44:10 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=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; s=arcselector10001; h=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; bh=I3wsP6MIZTaFaVHy2aI9n8l/+QiHW4Qxz4YG15asXUI=; b=Ox6BCdlptjRrlW2hMtg01F/mcua1xsGKW7qdO77MAXjav8oMGvG5KdTQfvr0oFNlHniW9jtXGikX4SW5u65F4mGnDRNgK3ghRniS37GqD45z7qRN3eZAS5XroN+3QI55aFjnp0ln0JU+aqI+VXfim/ZVyDN8AvvscIhxenspeVOZmFUCF+abjPZJo/jp57KsrkISmO4VK1E9iKzpIXxLyjQ0M7BSc/DoEDHmyeT1UUIMjJS670sb0iFytQxNwo2QY0fzbk2Q2AvMZABSqN2D12b0zQIyEpfigufpqYeKE+yEY8uZs6QQNQiuqcI/6gfyEL3rDyurPt7ktqjStx5a3Q== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=I3wsP6MIZTaFaVHy2aI9n8l/+QiHW4Qxz4YG15asXUI=; b=iIG6gqzczP59NC+3qP7ZO4py+IfmlrKZ9I8S7djtKA8NlREiSlmZU1i8LZq0Gr4jxRbidMEoklQx/WMsVgc3P16jAcLd7PMqPVOEEJFhSwa0BKUw73I8sWTCwJrfI1WjM6LWWKaTQ3QG5Uxoa7qBZBqFpNpofFQ2iJwJbLHaQJbYhmNXXuMXnXZGy1KlfUA6bFz0+cp8rgODZFZLBdLATQyDl1E8KxZAkBWjpqobx/GBJeYtUyMGXEESPCCQVwlKrODrj27u6np5Bol8MaknBRI08VzMuIzp9zVu+Z35h19ZRkLF1a99Gb/7FP9HnzFegSShzIuxWVDIdBvFN/lHRA== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB8285.eurprd04.prod.outlook.com (2603:10a6:102:1ca::15) by DU0PR04MB9348.eurprd04.prod.outlook.com (2603:10a6:10:358::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8722.29; Fri, 16 May 2025 01:43:50 +0000 Received: from PAXPR04MB8285.eurprd04.prod.outlook.com ([fe80::e003:8fb:64ea:acfd]) by PAXPR04MB8285.eurprd04.prod.outlook.com ([fe80::e003:8fb:64ea:acfd%3]) with mapi id 15.20.8722.031; Fri, 16 May 2025 01:43:50 +0000 From: Hou Qi To: libcamera-devel@lists.libcamera.org Cc: nicolas@ndufresne.ca, kieran.bingham@ideasonboard.com, jared.hu@nxp.com, qi.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 X-ClientProxiedBy: SI2PR02CA0039.apcprd02.prod.outlook.com (2603:1096:4:196::9) To PAXPR04MB8285.eurprd04.prod.outlook.com (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; ARA: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:; IPV:NLI; SFV:NSPM; H:PAXPR04MB8285.eurprd04.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230040)(376014)(366016)(52116014)(1800799024)(38350700014); DIR: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 (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: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" GStreamer video-info calculated stride and offset may differ from those used by the camera. For stride and offset mismatch, this patch adds video meta to buffer if downstream supports VideoMeta through allocation query. Otherwise, create a internal VideoPool using the caps, and copy video frame to this system memory. Signed-off-by: Hou Qi Reviewed-by: Kieran Bingham Signed-off-by: Hou Qi Reviewed-by: Kieran Bingham --- src/gstreamer/gstlibcamera-utils.cpp | 33 ++++++ src/gstreamer/gstlibcamera-utils.h | 5 + src/gstreamer/gstlibcamerapad.cpp | 31 ++++++ src/gstreamer/gstlibcamerapad.h | 8 ++ src/gstreamer/gstlibcamerapool.cpp | 15 ++- src/gstreamer/gstlibcamerapool.h | 3 +- src/gstreamer/gstlibcamerasrc.cpp | 147 ++++++++++++++++++++++++++- 7 files changed, 238 insertions(+), 4 deletions(-) diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp index 2edebba0..bf12cb07 100644 --- a/src/gstreamer/gstlibcamera-utils.cpp +++ b/src/gstreamer/gstlibcamera-utils.cpp @@ -599,6 +599,39 @@ gst_task_resume(GstTask *task) } #endif +#if !GST_CHECK_VERSION(1, 22, 0) +/* + * Copyright (C) <1999> Erik Walthinsen + * Library <2002> Ronald Bultje + * Copyright (C) <2007> David A. Schleef + */ +/* This function has been imported directly from the gstreamer project to + * support backwards compatibility and should be removed when the older + * version is no longer supported. */ +gint gst_video_format_info_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride) +{ + gint estride; + gint comp[GST_VIDEO_MAX_COMPONENTS]; + gint i; + + /* there is nothing to extrapolate on first plane */ + if (plane == 0) + return stride; + + gst_video_format_info_component(finfo, plane, comp); + + /* For now, all planar formats have a single component on first plane, but + * if there was a planar format with more, we'd have to make a ratio of the + * number of component on the first plane against the number of component on + * the current plane. */ + estride = 0; + for (i = 0; i < GST_VIDEO_MAX_COMPONENTS && comp[i] >= 0; i++) + estride += GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(finfo, comp[i], stride); + + return estride; +} +#endif + G_LOCK_DEFINE_STATIC(cm_singleton_lock); static std::weak_ptr cm_singleton_ptr; diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h index 4978987c..5f4e8a0f 100644 --- a/src/gstreamer/gstlibcamera-utils.h +++ b/src/gstreamer/gstlibcamera-utils.h @@ -36,6 +36,11 @@ static inline void gst_clear_event(GstEvent **event_ptr) #if !GST_CHECK_VERSION(1, 17, 1) gboolean gst_task_resume(GstTask *task); #endif + +#if !GST_CHECK_VERSION(1, 22, 0) +gint gst_video_format_info_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride); +#endif + std::shared_ptr gst_libcamera_get_camera_manager(int &ret); /** diff --git a/src/gstreamer/gstlibcamerapad.cpp b/src/gstreamer/gstlibcamerapad.cpp index 7b22aebe..3bc2bc87 100644 --- a/src/gstreamer/gstlibcamerapad.cpp +++ b/src/gstreamer/gstlibcamerapad.cpp @@ -18,6 +18,8 @@ struct _GstLibcameraPad { GstPad parent; StreamRole role; GstLibcameraPool *pool; + GstBufferPool *video_pool; + GstVideoInfo info; GstClockTime latency; }; @@ -153,6 +155,35 @@ gst_libcamera_pad_set_pool(GstPad *pad, GstLibcameraPool *pool) self->pool = pool; } +GstBufferPool * +gst_libcamera_pad_get_video_pool(GstPad *pad) +{ + auto *self = GST_LIBCAMERA_PAD(pad); + return self->video_pool; +} + +void gst_libcamera_pad_set_video_pool(GstPad *pad, GstBufferPool *video_pool) +{ + auto *self = GST_LIBCAMERA_PAD(pad); + + if (self->video_pool) + g_object_unref(self->video_pool); + self->video_pool = video_pool; +} + +GstVideoInfo gst_libcamera_pad_get_video_info(GstPad *pad) +{ + auto *self = GST_LIBCAMERA_PAD(pad); + return self->info; +} + +void gst_libcamera_pad_set_video_info(GstPad *pad, const GstVideoInfo *info) +{ + auto *self = GST_LIBCAMERA_PAD(pad); + + self->info = *info; +} + Stream * gst_libcamera_pad_get_stream(GstPad *pad) { diff --git a/src/gstreamer/gstlibcamerapad.h b/src/gstreamer/gstlibcamerapad.h index 630c168a..f98b8a7f 100644 --- a/src/gstreamer/gstlibcamerapad.h +++ b/src/gstreamer/gstlibcamerapad.h @@ -23,6 +23,14 @@ GstLibcameraPool *gst_libcamera_pad_get_pool(GstPad *pad); void gst_libcamera_pad_set_pool(GstPad *pad, GstLibcameraPool *pool); +GstBufferPool *gst_libcamera_pad_get_video_pool(GstPad *pad); + +void gst_libcamera_pad_set_video_pool(GstPad *pad, GstBufferPool *video_pool); + +GstVideoInfo gst_libcamera_pad_get_video_info(GstPad *pad); + +void gst_libcamera_pad_set_video_info(GstPad *pad, const GstVideoInfo *info); + libcamera::Stream *gst_libcamera_pad_get_stream(GstPad *pad); void gst_libcamera_pad_set_latency(GstPad *pad, GstClockTime latency); diff --git a/src/gstreamer/gstlibcamerapool.cpp b/src/gstreamer/gstlibcamerapool.cpp index 9cd7eccb..8278144f 100644 --- a/src/gstreamer/gstlibcamerapool.cpp +++ b/src/gstreamer/gstlibcamerapool.cpp @@ -134,8 +134,20 @@ gst_libcamera_pool_class_init(GstLibcameraPoolClass *klass) G_TYPE_NONE, 0); } +static void +gst_libcamera_buffer_add_video_meta(GstBuffer *buffer, GstVideoInfo *info) +{ + GstVideoMeta *vmeta; + vmeta = gst_buffer_add_video_meta_full(buffer, GST_VIDEO_FRAME_FLAG_NONE, + GST_VIDEO_INFO_FORMAT(info), GST_VIDEO_INFO_WIDTH(info), + GST_VIDEO_INFO_HEIGHT(info), GST_VIDEO_INFO_N_PLANES(info), + info->offset, info->stride); + GST_META_FLAGS(vmeta) = (GstMetaFlags)(GST_META_FLAGS(vmeta) | GST_META_FLAG_POOLED); +} + GstLibcameraPool * -gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream) +gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream, + GstVideoInfo *info) { auto *pool = GST_LIBCAMERA_POOL(g_object_new(GST_TYPE_LIBCAMERA_POOL, nullptr)); @@ -145,6 +157,7 @@ gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream) gsize pool_size = gst_libcamera_allocator_get_pool_size(allocator, stream); for (gsize i = 0; i < pool_size; i++) { GstBuffer *buffer = gst_buffer_new(); + gst_libcamera_buffer_add_video_meta(buffer, info); pool->queue->push_back(buffer); } diff --git a/src/gstreamer/gstlibcamerapool.h b/src/gstreamer/gstlibcamerapool.h index 2a7a9c77..02ee4dd4 100644 --- a/src/gstreamer/gstlibcamerapool.h +++ b/src/gstreamer/gstlibcamerapool.h @@ -14,6 +14,7 @@ #include "gstlibcameraallocator.h" #include +#include #include @@ -21,7 +22,7 @@ G_DECLARE_FINAL_TYPE(GstLibcameraPool, gst_libcamera_pool, GST_LIBCAMERA, POOL, GstBufferPool) GstLibcameraPool *gst_libcamera_pool_new(GstLibcameraAllocator *allocator, - libcamera::Stream *stream); + libcamera::Stream *stream, GstVideoInfo *info); libcamera::Stream *gst_libcamera_pool_get_stream(GstLibcameraPool *self); diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp index 5e9e843d..7275855a 100644 --- a/src/gstreamer/gstlibcamerasrc.cpp +++ b/src/gstreamer/gstlibcamerasrc.cpp @@ -268,6 +268,58 @@ GstLibcameraSrcState::requestCompleted(Request *request) gst_task_resume(src_->task); } +static void +gst_libcamera_extrapolate_info(GstVideoInfo *info, guint32 stride) +{ + guint i, estride; + gsize offset = 0; + + /* this should be updated if tiled formats get added in the future. */ + for (i = 0; i < GST_VIDEO_INFO_N_PLANES(info); i++) { + estride = gst_video_format_info_extrapolate_stride(info->finfo, i, stride); + info->stride[i] = estride; + info->offset[i] = offset; + offset += estride * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info->finfo, i, + GST_VIDEO_INFO_HEIGHT(info)); + } +} + +static GstFlowReturn +gst_libcamera_video_frame_copy(GstBuffer *src, GstBuffer *dest, const GstVideoInfo *dest_info, guint32 stride) +{ + GstVideoInfo src_info = *dest_info; + GstVideoFrame src_frame, dest_frame; + + gst_libcamera_extrapolate_info(&src_info, stride); + src_info.size = gst_buffer_get_size(src); + + if (!gst_video_frame_map(&src_frame, &src_info, src, GST_MAP_READ)) { + GST_ERROR("Could not map buffer"); + goto error; + } + + if (!gst_video_frame_map(&dest_frame, dest_info, dest, GST_MAP_WRITE)) { + GST_ERROR("Could not map buffer"); + gst_video_frame_unmap(&src_frame); + goto error; + } + + if (!gst_video_frame_copy(&dest_frame, &src_frame)) { + GST_ERROR("Could not copy frame"); + gst_video_frame_unmap(&src_frame); + gst_video_frame_unmap(&dest_frame); + goto error; + } + + gst_video_frame_unmap(&src_frame); + gst_video_frame_unmap(&dest_frame); + + return GST_FLOW_OK; + +error: + return GST_FLOW_ERROR; +} + /* Must be called with stream_lock held. */ int GstLibcameraSrcState::processRequest() { @@ -292,11 +344,41 @@ int GstLibcameraSrcState::processRequest() GstFlowReturn ret = GST_FLOW_OK; gst_flow_combiner_reset(src_->flow_combiner); - for (GstPad *srcpad : srcpads_) { + for (gsize i = 0; i < srcpads_.size(); i++) { + GstPad *srcpad = srcpads_[i]; Stream *stream = gst_libcamera_pad_get_stream(srcpad); GstBuffer *buffer = wrap->detachBuffer(stream); FrameBuffer *fb = gst_libcamera_buffer_get_frame_buffer(buffer); + const StreamConfiguration &stream_cfg = config_->at(i); + GstBufferPool *video_pool = gst_libcamera_pad_get_video_pool(srcpad); + + if (video_pool) { + /* Only set video pool when a copy is needed */ + GstBuffer *copy = NULL; + const GstVideoInfo info = gst_libcamera_pad_get_video_info(srcpad); + + ret = gst_buffer_pool_acquire_buffer(video_pool, ©, NULL); + if (ret != GST_FLOW_OK) { + gst_buffer_unref(buffer); + GST_ELEMENT_ERROR(src_, RESOURCE, SETTINGS, + ("Failed to acquire buffer"), + ("GstLibcameraSrcState::processRequest() failed: %s", g_strerror(-ret))); + return -EPIPE; + } + + ret = gst_libcamera_video_frame_copy(buffer, copy, &info, stream_cfg.stride); + gst_buffer_unref(buffer); + if (ret != GST_FLOW_OK) { + gst_buffer_unref(copy); + GST_ELEMENT_ERROR(src_, RESOURCE, SETTINGS, + ("Failed to copy buffer"), + ("GstLibcameraSrcState::processRequest() failed: %s", g_strerror(-ret))); + return -EPIPE; + } + + buffer = copy; + } if (GST_CLOCK_TIME_IS_VALID(wrap->pts_)) { GST_BUFFER_PTS(buffer) = wrap->pts_; @@ -499,13 +581,68 @@ gst_libcamera_src_negotiate(GstLibcameraSrc *self) for (gsize i = 0; i < state->srcpads_.size(); i++) { GstPad *srcpad = state->srcpads_[i]; const StreamConfiguration &stream_cfg = state->config_->at(i); + GstBufferPool *video_pool = NULL; + GstVideoInfo info; + + g_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg); + + gst_video_info_from_caps(&info, caps); + gst_libcamera_pad_set_video_info(srcpad, &info); + + /* stride mismatch between camera stride and that calculated by video-info */ + if (static_cast(info.stride[0]) != stream_cfg.stride && + GST_VIDEO_INFO_FORMAT(&info) != GST_VIDEO_FORMAT_ENCODED) { + GstQuery *query = NULL; + const gboolean need_pool = true; + gboolean has_video_meta = false; + + gst_libcamera_extrapolate_info(&info, stream_cfg.stride); + + query = gst_query_new_allocation(caps, need_pool); + if (!gst_pad_peer_query(srcpad, query)) + GST_DEBUG_OBJECT(self, "Didn't get downstream ALLOCATION hints"); + else + has_video_meta = gst_query_find_allocation_meta(query, GST_VIDEO_META_API_TYPE, NULL); + + if (!has_video_meta) { + GstBufferPool *pool = NULL; + + if (gst_query_get_n_allocation_pools(query) > 0) + gst_query_parse_nth_allocation_pool(query, 0, &pool, NULL, NULL, NULL); + + if (pool) + video_pool = pool; + else { + GstStructure *config; + guint min_buffers = 3; + video_pool = gst_video_buffer_pool_new(); + + config = gst_buffer_pool_get_config(video_pool); + gst_buffer_pool_config_set_params(config, caps, info.size, min_buffers, 0); + + GST_DEBUG_OBJECT(self, "Own pool config is %" GST_PTR_FORMAT, config); + + gst_buffer_pool_set_config(GST_BUFFER_POOL_CAST(video_pool), config); + } + + if (!gst_buffer_pool_set_active(video_pool, true)) { + gst_caps_unref(caps); + GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS, + ("Failed to active buffer pool"), + ("gst_libcamera_src_negotiate() failed")); + return false; + } + } + gst_query_unref(query); + } GstLibcameraPool *pool = gst_libcamera_pool_new(self->allocator, - stream_cfg.stream()); + stream_cfg.stream(), &info); g_signal_connect_swapped(pool, "buffer-notify", G_CALLBACK(gst_task_resume), self->task); gst_libcamera_pad_set_pool(srcpad, pool); + gst_libcamera_pad_set_video_pool(srcpad, video_pool); /* Clear all reconfigure flags. */ gst_pad_check_reconfigure(srcpad); @@ -922,6 +1059,12 @@ gst_libcamera_src_release_pad(GstElement *element, GstPad *pad) auto end_iterator = pads.end(); auto pad_iterator = std::find(begin_iterator, end_iterator, pad); + GstBufferPool *video_pool = gst_libcamera_pad_get_video_pool(pad); + if (video_pool) { + gst_buffer_pool_set_active(video_pool, false); + gst_object_unref(video_pool); + } + if (pad_iterator != end_iterator) { g_object_unref(*pad_iterator); pads.erase(pad_iterator);