Patch Detail
Show a patch.
GET /api/patches/17747/?format=api
{ "id": 17747, "url": "https://patchwork.libcamera.org/api/patches/17747/?format=api", "web_url": "https://patchwork.libcamera.org/patch/17747/", "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": "<20221102135614.657444-3-umang.jain@ideasonboard.com>", "date": "2022-11-02T13:56:14", "name": "[libcamera-devel,v6,2/2] gstreamer: Provide framerate support for libcamerasrc", "commit_ref": null, "pull_url": null, "state": "accepted", "archived": false, "hash": "df95c8ee3cf0c7f483f4c7d1219ad07cbe8e04ca", "submitter": { "id": 86, "url": "https://patchwork.libcamera.org/api/people/86/?format=api", "name": "Umang Jain", "email": "umang.jain@ideasonboard.com" }, "delegate": { "id": 12, "url": "https://patchwork.libcamera.org/api/users/12/?format=api", "username": "uajain", "first_name": "Umang", "last_name": "Jain", "email": "umang.jain@ideasonboard.com" }, "mbox": "https://patchwork.libcamera.org/patch/17747/mbox/", "series": [ { "id": 3593, "url": "https://patchwork.libcamera.org/api/series/3593/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3593", "date": "2022-11-02T13:56:12", "name": "Provide framerate support for libcamerasrc", "version": 6, "mbox": "https://patchwork.libcamera.org/series/3593/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/17747/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/17747/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 2857DBD16B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 2 Nov 2022 13:56:32 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C101063080;\n\tWed, 2 Nov 2022 14:56:31 +0100 (CET)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 11C2A63078\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 2 Nov 2022 14:56:30 +0100 (CET)", "from umang.jainideasonboard.com (unknown [103.251.226.107])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 3265F1226;\n\tWed, 2 Nov 2022 14:56:27 +0100 (CET)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1667397391;\n\tbh=Pn7J48k/yYKWXnlUrI9OnY6j4RworjZkje1VK59bdfc=;\n\th=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=zmidIl6Zm/k+yXWq8f75fFGfXo559sm1uPyBH2E4hq1rceKuhtWOxEgTp6lAMnSV+\n\tu+j8bhKjFMyM4atzkd+oeDXOyqQgvaHG+ZtwUYsujy6awcPKt04jpYYLogPCYJFfrN\n\tPAWh69sUWVskL/Rn3OoyxaUISwvONloivqI/8Oxnav0U7kwcaSrSNeo7Vn3aI+JQ2i\n\tNqWKW/SMq2GYfCiziAt7vmXbCUhsqjXBVX7Na7jIDwMaXhm0+7wzeyPkt5ms4VjW+7\n\tNEtMaf21FwFuhB95OfYxoiSx0PTBCQn8mYJjAt330w2klNyx+kWdFj7ir1Sa9iWiR9\n\t1GD1u/8q5Zq9A==", "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1667397389;\n\tbh=Pn7J48k/yYKWXnlUrI9OnY6j4RworjZkje1VK59bdfc=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=XJa/G79PK6JnC/olrQg5gCvdL7/xZiix5t6kczEfSQ8+bc20WLFNDGHfy0sxsHyQr\n\twBMtz1kq3WWFVFG/YcBSWx4HM2/wUcrXVPPyRAhxNTJq/SoHz3QyCmPnp/vjxsjk8o\n\tp0q5lr3/klZWWZ6FpVOLl/6o5NnKwqaat1/ku5NU=" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"XJa/G79P\"; dkim-atps=neutral", "To": "libcamera-devel@lists.libcamera.org", "Date": "Wed, 2 Nov 2022 19:26:14 +0530", "Message-Id": "<20221102135614.657444-3-umang.jain@ideasonboard.com>", "X-Mailer": "git-send-email 2.37.3", "In-Reply-To": "<20221102135614.657444-1-umang.jain@ideasonboard.com>", "References": "<20221102135614.657444-1-umang.jain@ideasonboard.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v6 2/2] gstreamer: Provide framerate\n\tsupport for libcamerasrc", "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>", "From": "Umang Jain via libcamera-devel <libcamera-devel@lists.libcamera.org>", "Reply-To": "Umang Jain <umang.jain@ideasonboard.com>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "From: Rishikesh Donadkar <rishikeshdonadkar@gmail.com>\n\nControl the framerate by passing the controls::FrameDurationLimits\nduring Camera::start(). Framerate in gstreamer is expressed as\nGST_TYPE_FRACTION so we maximise on maintaining it as a fraction\nthroughout and only do arithematic computations as and when required\n(to compute frame-duration and vice-versa).\n\nTo weed out abritrary framerate as input, place the clamping via the\ncontrols::FrameDurationLimits provided after camera::configure() phase.\nThis is handled by a helper function\ngst_libcamera_clamp_and_set_frameduration().\n\nSet the bound checked framerate (done in the above mentioned helper)\ninto the caps and pass the ControlList containing the frame-duration\nto Camera::start(ctrls).\n\nSigned-off-by: Rishikesh Donadkar <rishikeshdonadkar@gmail.com>\nSigned-off-by: Umang Jain <umang.jain@ideasonboard.com>\nTested-by: Umang Jain <umang.jain@ideasonboard.com>\n---\n src/gstreamer/gstlibcamera-utils.cpp | 78 ++++++++++++++++++++++++++++\n src/gstreamer/gstlibcamera-utils.h | 6 +++\n src/gstreamer/gstlibcamerasrc.cpp | 15 +++++-\n 3 files changed, 98 insertions(+), 1 deletion(-)", "diff": "diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp\nindex 244a4a79..c14f72ec 100644\n--- a/src/gstreamer/gstlibcamera-utils.cpp\n+++ b/src/gstreamer/gstlibcamera-utils.cpp\n@@ -8,6 +8,7 @@\n \n #include \"gstlibcamera-utils.h\"\n \n+#include <libcamera/control_ids.h>\n #include <libcamera/formats.h>\n \n using namespace libcamera;\n@@ -407,6 +408,83 @@ gst_libcamera_configure_stream_from_caps(StreamConfiguration &stream_cfg,\n \t}\n }\n \n+void gst_libcamera_get_framerate_from_caps(GstCaps *caps,\n+\t\t\t\t\t GstStructure *container)\n+{\n+\tGstStructure *s = gst_caps_get_structure(caps, 0);\n+\t/*\n+\t * Default to 30 fps. If the \"framerate\" fraction is invalid below,\n+\t * libcamerasrc will set 30fps as the framerate.\n+\t */\n+\tgint fps_n = 30, fps_d = 1;\n+\n+\tif (gst_structure_has_field_typed(s, \"framerate\", GST_TYPE_FRACTION)) {\n+\t\tif (!gst_structure_get_fraction(s, \"framerate\", &fps_n, &fps_d))\n+\t\t\tGST_WARNING(\"Invalid framerate in the caps\");\n+\t}\n+\n+\tgst_structure_set(container, \"framerate\", GST_TYPE_FRACTION,\n+\t\t\t fps_n, fps_d, nullptr);\n+}\n+\n+void gst_libcamera_clamp_and_set_frameduration(ControlList &initCtrls,\n+\t\t\t\t\t const ControlInfoMap &cam_ctrls,\n+\t\t\t\t\t GstStructure *container)\n+{\n+\tgboolean in_bounds = false;\n+\tgint fps_caps_n, fps_caps_d;\n+\n+\tif (!gst_structure_has_field_typed(container, \"framerate\", GST_TYPE_FRACTION))\n+\t\treturn;\n+\n+\tauto iterFrameDuration = cam_ctrls.find(controls::FrameDurationLimits.id());\n+\tif (iterFrameDuration == cam_ctrls.end()) {\n+\t\tGST_WARNING(\"FrameDurationLimits not found in camera controls.\");\n+\t\treturn;\n+\t}\n+\n+\tconst GValue *framerate = gst_structure_get_value(container, \"framerate\");\n+\n+\tfps_caps_n = gst_value_get_fraction_numerator(framerate);\n+\tfps_caps_d = gst_value_get_fraction_denominator(framerate);\n+\n+\tint64_t frame_duration = (fps_caps_d * 1000000.0) / fps_caps_n;\n+\tint64_t min_frame_duration = iterFrameDuration->second.min().get<int64_t>();\n+\tint64_t max_frame_duration = iterFrameDuration->second.max().get<int64_t>();\n+\n+\tif (frame_duration < min_frame_duration) {\n+\t\tframe_duration = min_frame_duration;\n+\t} else if (frame_duration > max_frame_duration) {\n+\t\tframe_duration = max_frame_duration;\n+\t} else {\n+\t\tin_bounds = true;\n+\t}\n+\n+\tif (!in_bounds) {\n+\t\tgint framerate_clamped = 1000000 / frame_duration;\n+\n+\t\t/* Update the framerate which then will be exposed in caps. */\n+\t\tgst_structure_set(container, \"framerate\", GST_TYPE_FRACTION,\n+\t\t\t\t framerate_clamped, 1, nullptr);\n+\t}\n+\n+\tinitCtrls.set(controls::FrameDurationLimits,\n+\t\t { frame_duration, frame_duration });\n+}\n+\n+void gst_libcamera_framerate_to_caps(GstCaps *caps, const GValue *container)\n+{\n+\tif (!GST_VALUE_HOLDS_FRACTION(container))\n+\t\treturn;\n+\n+\tGstStructure *s = gst_caps_get_structure(caps, 0);\n+\tgint fps_caps_n, fps_caps_d;\n+\n+\tfps_caps_n = gst_value_get_fraction_numerator(container);\n+\tfps_caps_d = gst_value_get_fraction_denominator(container);\n+\tgst_structure_set(s, \"framerate\", GST_TYPE_FRACTION, fps_caps_n, fps_caps_d, nullptr);\n+}\n+\n #if !GST_CHECK_VERSION(1, 17, 1)\n gboolean\n gst_task_resume(GstTask *task)\ndiff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h\nindex 164189a2..3d217fcf 100644\n--- a/src/gstreamer/gstlibcamera-utils.h\n+++ b/src/gstreamer/gstlibcamera-utils.h\n@@ -9,6 +9,7 @@\n #pragma once\n \n #include <libcamera/camera_manager.h>\n+#include <libcamera/controls.h>\n #include <libcamera/stream.h>\n \n #include <gst/gst.h>\n@@ -18,6 +19,11 @@ GstCaps *gst_libcamera_stream_formats_to_caps(const libcamera::StreamFormats &fo\n GstCaps *gst_libcamera_stream_configuration_to_caps(const libcamera::StreamConfiguration &stream_cfg);\n void gst_libcamera_configure_stream_from_caps(libcamera::StreamConfiguration &stream_cfg,\n \t\t\t\t\t GstCaps *caps);\n+void gst_libcamera_get_framerate_from_caps(GstCaps *caps, GstStructure *container);\n+void gst_libcamera_clamp_and_set_frameduration(libcamera::ControlList &controls,\n+\t\t\t\t\t const libcamera::ControlInfoMap &camera_controls, GstStructure *container);\n+void gst_libcamera_framerate_to_caps(GstCaps *caps, const GValue *container);\n+\n #if !GST_CHECK_VERSION(1, 17, 1)\n gboolean gst_task_resume(GstTask *task);\n #endif\ndiff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp\nindex 60032236..955d6eac 100644\n--- a/src/gstreamer/gstlibcamerasrc.cpp\n+++ b/src/gstreamer/gstlibcamerasrc.cpp\n@@ -131,6 +131,7 @@ struct GstLibcameraSrcState {\n \tstd::queue<std::unique_ptr<RequestWrap>> completedRequests_\n \t\tLIBCAMERA_TSA_GUARDED_BY(lock_);\n \n+\tControlList initControls_;\n \tguint group_id_;\n \n \tint queueRequest();\n@@ -462,6 +463,8 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n \tGstFlowReturn flow_ret = GST_FLOW_OK;\n \tgint ret;\n \n+\tGstStructure *container = gst_structure_new_empty(\"container\");\n+\n \tGST_DEBUG_OBJECT(self, \"Streaming thread has started\");\n \n \tgint stream_id_num = 0;\n@@ -504,6 +507,7 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n \t\t/* Fixate caps and configure the stream. */\n \t\tcaps = gst_caps_make_writable(caps);\n \t\tgst_libcamera_configure_stream_from_caps(stream_cfg, caps);\n+\t\tgst_libcamera_get_framerate_from_caps(caps, container);\n \t}\n \n \tif (flow_ret != GST_FLOW_OK)\n@@ -525,6 +529,10 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n \t\tgoto done;\n \t}\n \n+\t/* Check frame duration bounds within controls::FrameDurationLimits */\n+\tgst_libcamera_clamp_and_set_frameduration(state->initControls_,\n+\t\t\t\t\t\t state->cam_->controls(), container);\n+\n \t/*\n \t * Regardless if it has been modified, create clean caps and push the\n \t * caps event. Downstream will decide if the caps are acceptable.\n@@ -532,8 +540,11 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\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\tconst GValue *framerate = gst_structure_get_value(container, \"framerate\");\n \n \t\tg_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg);\n+\t\tgst_libcamera_framerate_to_caps(caps, framerate);\n+\n \t\tif (!gst_pad_push_event(srcpad, gst_event_new_caps(caps))) {\n \t\t\tflow_ret = GST_FLOW_NOT_NEGOTIATED;\n \t\t\tbreak;\n@@ -567,7 +578,7 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n \t\tgst_flow_combiner_add_pad(self->flow_combiner, srcpad);\n \t}\n \n-\tret = state->cam_->start();\n+\tret = state->cam_->start(&state->initControls_);\n \tif (ret) {\n \t\tGST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,\n \t\t\t\t (\"Failed to start the camera: %s\", g_strerror(-ret)),\n@@ -577,6 +588,8 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n \t}\n \n done:\n+\tstate->initControls_.clear();\n+\tg_free(container);\n \tswitch (flow_ret) {\n \tcase GST_FLOW_NOT_NEGOTIATED:\n \t\tGST_ELEMENT_FLOW_ERROR(self, flow_ret);\n", "prefixes": [ "libcamera-devel", "v6", "2/2" ] }