Patch Detail
Show a patch.
GET /api/1.1/patches/17370/?format=api
{ "id": 17370, "url": "https://patchwork.libcamera.org/api/1.1/patches/17370/?format=api", "web_url": "https://patchwork.libcamera.org/patch/17370/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/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": "<20220915114734.115572-3-rishikeshdonadkar@gmail.com>", "date": "2022-09-15T11:47:34", "name": "[libcamera-devel,v5,2/2] gstreamer: Provide framerate support for libcamerasrc.", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "d713bacc3cadb27a4e8ed650bb6e397c70b8234b", "submitter": { "id": 118, "url": "https://patchwork.libcamera.org/api/1.1/people/118/?format=api", "name": "Rishikesh Donadkar", "email": "rishikeshdonadkar@gmail.com" }, "delegate": { "id": 12, "url": "https://patchwork.libcamera.org/api/1.1/users/12/?format=api", "username": "uajain", "first_name": "Umang", "last_name": "Jain", "email": "umang.jain@ideasonboard.com" }, "mbox": "https://patchwork.libcamera.org/patch/17370/mbox/", "series": [ { "id": 3487, "url": "https://patchwork.libcamera.org/api/1.1/series/3487/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3487", "date": "2022-09-15T11:47:32", "name": "Provide framerate support for libcamerasrc", "version": 5, "mbox": "https://patchwork.libcamera.org/series/3487/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/17370/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/17370/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 B6C56C3272\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 15 Sep 2022 11:47:59 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 71A7161FB6;\n\tThu, 15 Sep 2022 13:47:59 +0200 (CEST)", "from mail-pg1-x532.google.com (mail-pg1-x532.google.com\n\t[IPv6:2607:f8b0:4864:20::532])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 60D7A61FB0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 15 Sep 2022 13:47:58 +0200 (CEST)", "by mail-pg1-x532.google.com with SMTP id 207so8685912pgc.7\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 15 Sep 2022 04:47:58 -0700 (PDT)", "from localhost.localdomain ([49.36.99.120])\n\tby smtp.googlemail.com with ESMTPSA id\n\tx15-20020a170902a38f00b001785dddc703sm3718994pla.120.2022.09.15.04.47.54\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tThu, 15 Sep 2022 04:47:56 -0700 (PDT)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1663242479;\n\tbh=2yW/T08lgkNZWJF/Wz917bnmL36SVgvJ/9PffarehGo=;\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:Cc:\n\tFrom;\n\tb=vLdVuKBXvmOp9YQ9uNRyqdiUul8VBX3EEnp2UaUSqiJpyIoTr3OrHOTqcyMPzuInP\n\tSkvDpRkQYSzMsGiT3GskY4frGxbY4guxZBvuO3TUeyMNpGo/VbHMql3wYdgJd64wj2\n\tN93dJ5P2LcETvymxGtX7dMQe4j30FD/kHntQwWc/vEvsuuVLvpwXJFSC178/RCM661\n\t+YTN/3B6NYkfHaIDsN3xd1AX/k5Ge36Rtq4sZ/R6u4zcQW70MdATBRsJidLBx3B7lK\n\t//MS4GdBOlyEgbvseL15wEq3/5bjzqkJwBOaqYuhr1WBY9ABVftpeykSre43Hz8FSj\n\turivNa+MONT8g==", "v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:from:to:cc:subject:date;\n\tbh=snGuuKLWj8mTNv76jKgQ92n9lMhXdXR/s2vS5Dmao1o=;\n\tb=NJ875NRQcRTB+Z9lr3Cr5defWU5+dF9f6dawdujJBvrztpULVXa3wYiwdGjVb3Cnxb\n\twVk67umzvCE+k+8bxS/ZqQ+2x8lFTTv+BTyWJDDo5LdhyErPjbW+s1Zxg/e9lO1Abv2L\n\tU6NMP2Uh+BezsEBxBY9gHa3swCCwxSRCDbCtWGBnx9SKjKCo1vruG18z+sIZg6qb8Zxs\n\t2yrNZnTRWp6m6K6v2PbooRITAyS1fPH4//FQncn4HOTrdX/rvUxi5VtKYs30cY1+qJDM\n\t/PR+mkCNzUFKumZBMwhbyInZxMqsyxr9pGYBh8dv7FQ+y63/JGyk3b8XH/1g0sGZORYa\n\t45hQ==" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"NJ875NRQ\"; dkim-atps=neutral", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n\t:subject:date;\n\tbh=snGuuKLWj8mTNv76jKgQ92n9lMhXdXR/s2vS5Dmao1o=;\n\tb=2v2o9usczUOcU2gMGK5U0woTcsFNi36ATSciGk6IJTCeU6IbnD/eTrlCV3BNxKAv6T\n\tR+TcsEX+Dw9bgOzP5F0jp8bpXmuHrNdYHQgjPtL0BJOqGH1dC51xCSqlUKF3IksUo/GH\n\tf71gedQLEWmddP1cyn5e0KBAypUGO70+FTV7pk1dynru4D6RnO4wdk8uIglnsD/x7UW2\n\ts4A0X6MOoRKN9TA9C6G4wDeqjvUMtQ8bxoFd61RcdlKHOeS8aq/Hrxha0wMB6aB73Yvg\n\t85UqZvfnWbH4wDwKuDK2+fagkI2HzvycI+EwE9GEHVN/TQrlbWVH8FHjNtSFbdEfZhCR\n\tflOw==", "X-Gm-Message-State": "ACgBeo0we/s6eBQkur0xYvfdS4yB9scWDvk8RwqyQMvY+/RQ71qMPn2M\n\tfoA3wTBtbNBeNaLH6uhnVAYaYiKqTe1H8g==", "X-Google-Smtp-Source": "AA6agR5zO6U/jvrDUOBl//idXH226F0QfXwhyGvbVaqj4jg1fwM7zZaQk18ObSHKxQPf3AdUs61vaA==", "X-Received": "by 2002:a05:6a00:804:b0:544:4e98:3ff3 with SMTP id\n\tm4-20020a056a00080400b005444e983ff3mr18031048pfk.36.1663242476668; \n\tThu, 15 Sep 2022 04:47:56 -0700 (PDT)", "To": "libcamera-devel@lists.libcamera.org", "Date": "Thu, 15 Sep 2022 17:17:34 +0530", "Message-Id": "<20220915114734.115572-3-rishikeshdonadkar@gmail.com>", "X-Mailer": "git-send-email 2.25.1", "In-Reply-To": "<20220915114734.115572-1-rishikeshdonadkar@gmail.com>", "References": "<20220915114734.115572-1-rishikeshdonadkar@gmail.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v5 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": "Rishikesh Donadkar via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>", "Reply-To": "Rishikesh Donadkar <rishikeshdonadkar@gmail.com>", "Cc": "nicolas.dufresne@collabora.com, vedantparanjape160201@gmail.com", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "Control the framerate by setting the controls::FrameDurationLimits\nin an ControlList and passing the control list at camera::start(). This\nrequires converting the framerate which of the type\nGST_TYPE_FRACTION into FrameDuration which of the type int64_t. This\nconversion causes loss of precision and the precise framerate cannot be\nobtained by Inverting the control::FrameDurationLimits value.\n\nExample -\n\n* Suppose the framerate requested is 35/1. The framerate is read form\n the caps in the from of fraction that has a numerator and\n denominator.\n\n* Converting to FrameDuration (in microseconds)\n (1 * 10^6) / 35 = 28571.4286\n int64_t frame_duration = 28571\n (the precision here is lost.)\n* To expose the framerate in caps, Inverting the frame_duration to get\n back the framerate and converting to Seconds.\n double framerate = 10^6 / 28571\n and\n 10^6/28571 which is close but not equal to 35/1 will fail the negotiation.\n\nGet the framerate from the caps and convert it to FrameDuration.\n\nClamp the frame_duration between the min/max FrameDuration supported\nby the camera. Set the FrameDurationLimits control in the initControls_.\nStore the clamped/unclamped framerate in the container to be retrieved later.\n\nSet the bound checked framerate from the container into the caps.\n\nPass the ControlList initControls_ at camera->start().\n\nSigned-off-by: Rishikesh Donadkar <rishikeshdonadkar@gmail.com>\n---\n src/gstreamer/gstlibcamera-utils.cpp | 70 ++++++++++++++++++++++++++++\n src/gstreamer/gstlibcamera-utils.h | 6 +++\n src/gstreamer/gstlibcamerasrc.cpp | 16 ++++++-\n 3 files changed, 91 insertions(+), 1 deletion(-)", "diff": "diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp\nindex 244a4a79..89f116ba 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,75 @@ gst_libcamera_configure_stream_from_caps(StreamConfiguration &stream_cfg,\n \t}\n }\n \n+void gst_libcamera_get_framerate_from_caps([[maybe_unused]] GstCaps *caps,\n+\t\t\t\t\t GstStructure *container)\n+{\n+\tGstStructure *s = gst_caps_get_structure(caps, 0);\n+\tgint fps_n = 0, 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_structure_set(container, \"framerate\", GST_TYPE_FRACTION, fps_n, fps_d, nullptr);\n+\t\telse\n+\t\t\tGST_WARNING(\"invalid framerate in the caps.\");\n+\t}\n+}\n+\n+void gst_libcamera_clamp_and_set_frameduration(ControlList &controls, const ControlInfoMap &camera_controls,\n+\t\t\t\t\t GstStructure *container)\n+{\n+\tgboolean in_bounds = false;\n+\tgint fps_caps_n, fps_caps_d, fps_n, fps_d;\n+\n+\tif (!gst_structure_has_field_typed(container, \"framerate\", GST_TYPE_FRACTION))\n+\t\treturn;\n+\n+\tauto iterFrameDuration = camera_controls.find(controls::FrameDurationLimits.id());\n+\tif (iterFrameDuration == camera_controls.end()) {\n+\t\tGST_WARNING(\"Valid bounds for FrameDuration not found\");\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\tdouble framerate_clamped = 1000000.0 / frame_duration;\n+\t\tgst_util_double_to_fraction(framerate_clamped, &fps_n, &fps_d);\n+\t\tgst_structure_set(container, \"framerate\", GST_TYPE_FRACTION, fps_n, fps_d, nullptr);\n+\t}\n+\n+\tcontrols.set(controls::FrameDurationLimits,\n+\t\t Span<const int64_t, 2>({ 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..37f07676 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@@ -496,6 +499,7 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n \t\t/* Retrieve the supported caps. */\n \t\tg_autoptr(GstCaps) filter = gst_libcamera_stream_formats_to_caps(stream_cfg.formats());\n \t\tg_autoptr(GstCaps) caps = gst_pad_peer_query_caps(srcpad, filter);\n+\n \t\tif (gst_caps_is_empty(caps)) {\n \t\t\tflow_ret = GST_FLOW_NOT_NEGOTIATED;\n \t\t\tbreak;\n@@ -504,6 +508,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 +530,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 +541,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 +579,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 +589,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", "v5", "2/2" ] }