Patch Detail
Show a patch.
GET /api/1.1/patches/17363/?format=api
{ "id": 17363, "url": "https://patchwork.libcamera.org/api/1.1/patches/17363/?format=api", "web_url": "https://patchwork.libcamera.org/patch/17363/", "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": "<20220912095656.19013-3-rishikeshdonadkar@gmail.com>", "date": "2022-09-12T09:56:56", "name": "[libcamera-devel,v4,2/2] gstreamer: Provide framerate support for libcamerasrc.", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "430f7813d8ff1ee7300ce68299c2b88b1e216f76", "submitter": { "id": 118, "url": "https://patchwork.libcamera.org/api/1.1/people/118/?format=api", "name": "Rishikesh Donadkar", "email": "rishikeshdonadkar@gmail.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/17363/mbox/", "series": [ { "id": 3482, "url": "https://patchwork.libcamera.org/api/1.1/series/3482/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3482", "date": "2022-09-12T09:56:54", "name": "Provide framerate support for libcamerasrc", "version": 4, "mbox": "https://patchwork.libcamera.org/series/3482/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/17363/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/17363/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 48BA3C3272\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 12 Sep 2022 09:57:36 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 077C761F95;\n\tMon, 12 Sep 2022 11:57:36 +0200 (CEST)", "from mail-pl1-x62d.google.com (mail-pl1-x62d.google.com\n\t[IPv6:2607:f8b0:4864:20::62d])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1924161F8F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 12 Sep 2022 11:57:35 +0200 (CEST)", "by mail-pl1-x62d.google.com with SMTP id iw17so8109713plb.0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 12 Sep 2022 02:57:35 -0700 (PDT)", "from localhost.localdomain ([49.36.99.4])\n\tby smtp.googlemail.com with ESMTPSA id\n\tt14-20020aa7946e000000b00540dbae6272sm4959495pfq.213.2022.09.12.02.57.30\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tMon, 12 Sep 2022 02:57:33 -0700 (PDT)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1662976656;\n\tbh=TRIx0JdiGmmpv3T8gBGWMGXbcFCU+NDnhoqZd9KjXZY=;\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=j3kkto63G+5qNhw1rUpOZgtfm47vLSlvKP3YA1RuDtC1CxtLw4yUzAbkbB3amsHgN\n\tRwCOdBNlQ7La2tqeUM8wax97XdKozAlkXSchRmNKZf/RbEmHL7pbInzuyslR9elCY9\n\tfQNT/xuekiIgFl0kqlT/gxjNi+Ty49ufcIjM4nSo61SMYcmIKypeie2O9bDNFApF7E\n\tQ4gCTHw48x73rD16mCjh3H7FGo2SkSZi4P/lkgSdMhM705oIqRPkoyNCVy9rDB3LMQ\n\tLmOZ5hXnNag+9IDKzSXNMxtM+i9A6ZJO8TUBhhVkVDZmGb/uILJRmvFWbHvXM9wKrw\n\tIlY1YTMQXJWPQ==", "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=UcrUIUCtr/KO4vo4yPcHd7mFPsvV2uUA56Wbnl+hvpE=;\n\tb=WFlX8kQjzdQDI/P05ElsXHGQ6g7FEf9OYdDKYCFVVaAuFottYtrPL6+y7iY6eVzW1R\n\tYCNvAFWLHbPoeCwx2dKULbUZLSSUeRudoiubrJbNO5iVMMH/7A80Btpa779y8Wktr5nc\n\tHitsJ578Wq/cAZDToh97dSF46BzDLcP3VYIJKmJ0dryRl8st55PsoCG2poRkPQv1gPrm\n\tgy3n2Y+IXJpVL9WJjvGFBCmtrwnG3RAHtTvWcHJ7yg3SMjbmfi2Rf0qS1ROdTVSzkq4a\n\teZ10jZIocAFAY+6Px4dDGI9lGTmYi5sqop/MgHXlV93yA6ay5SAROrAQj3Jns/aOdHWz\n\t1+Ug==" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"WFlX8kQj\"; 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=UcrUIUCtr/KO4vo4yPcHd7mFPsvV2uUA56Wbnl+hvpE=;\n\tb=pze5g7/vSU/EDH9nmOaWozD6Ec/m1lDspKLRXOsoOhxWe5piUGPVYapBST15euk5If\n\tGXyrQDvZkboloRsuHyq5hpJk0m9I1s27vDg3nZzY0qWdWCjmkdM1P/EcJNPFbY0hEaUi\n\twyaKfU4R0cStRVolLAFDZgCU8aIEo3PHqOBsJisrVbrLNaSnjolhMbilmFRifLyilcKY\n\t8aV8qRz9NXzlrTy1bgJodkjeDAiQcNc8UZcaVJ4AHimFmXzDZACpEWzzdqwaLoxtcjtE\n\tqefVAuqMxN3nfe0iBy7KG76E4nOyT9uqrP3aNBwMkJjchPKIEYrToKo0cLDNPrPl5Yxc\n\t4FOw==", "X-Gm-Message-State": "ACgBeo0TVIMT0BiO1N73F/ZzpbtlmSFDr5Dh2sv+TjXIa5u/1gq4MJi4\n\tqAEmZDErW0N1m6zswQArbMnWRttpXCnJpg==", "X-Google-Smtp-Source": "AA6agR7ZsLfPSb/hs+K9ig3ErlyBGfDBd8LXHMIqKJ6evgBfd9MVhH5Jjg2hMi+VcpVPCwactfd+sg==", "X-Received": "by 2002:a17:903:54:b0:176:cdf8:b791 with SMTP id\n\tl20-20020a170903005400b00176cdf8b791mr24903668pla.24.1662976653388; \n\tMon, 12 Sep 2022 02:57:33 -0700 (PDT)", "To": "libcamera-devel@lists.libcamera.org", "Date": "Mon, 12 Sep 2022 15:26:56 +0530", "Message-Id": "<20220912095656.19013-3-rishikeshdonadkar@gmail.com>", "X-Mailer": "git-send-email 2.25.1", "In-Reply-To": "<20220912095656.19013-1-rishikeshdonadkar@gmail.com>", "References": "<20220912095656.19013-1-rishikeshdonadkar@gmail.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v4 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": "Add a field of the type ControlList to GstLibcameraSrcState.\n\nGet the framerate from the caps and convert it to FrameDuration.\nClamp the frame_duration between the min/max FrameDuration supported\nby the camera and set the FrameDurationLimits control in the initControls_\n\nSolve the complications in exposing the correct framerate due to loss in\nprecision as a result of casting the frameduration to int64_t(which is\nrequired in libcamera to set the FrameDurationLimits control).\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\nTo solve the above problem, Store the framerate requested in the peer\nelement caps in a container GstStructure, to be retrieved later.\n\nGet the control::FrameDurationLimits value that was set previously,\nconvert to framerate and if it is equal to the one requested, set the\nframerate in the caps, else set the framerate to whatever is obtained\nfrom inverting the controls::FrameDurationLimits.\n\nPass the initControls_ at camera->start().\n\nSigned-off-by: Rishikesh Donadkar <rishikeshdonadkar@gmail.com>\n---\n src/gstreamer/gstlibcamera-utils.cpp | 69 ++++++++++++++++++++++++++++\n src/gstreamer/gstlibcamera-utils.h | 7 +++\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 4df5dd6c..907ceab9 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@@ -405,6 +406,74 @@ 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 = -1, 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\t\treturn;\n+\t\t}\n+\t}\n+\n+\tif (fps_n < 0 || fps_d < 0)\n+\t\treturn;\n+\n+\tgst_structure_set(container, \"framerate\", GST_TYPE_FRACTION, fps_n, fps_d, nullptr);\n+}\n+\n+void gst_libcamera_clamp_and_set_frameduration(ControlList &controls, const ControlInfoMap &camera_controls,\n+\t\t\t\t\t const GstStructure *container)\n+{\n+\tgint fps_caps_n, fps_caps_d;\n+\tconst GValue *framerate = gst_structure_get_value(container, \"framerate\");\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\treturn;\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}\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(const ControlList &controls, 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_n, fps_d, 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+\tdouble framerate = 1000000 / controls.get(controls::FrameDurationLimits).value()[0];\n+\tgst_util_double_to_fraction(framerate, &fps_n, &fps_d);\n+\n+\tif (fps_caps_n / fps_caps_d == fps_n / fps_d)\n+\t\tgst_structure_set(s, \"framerate\", GST_TYPE_FRACTION, fps_caps_n, fps_caps_d, nullptr);\n+\telse\n+\t\tgst_structure_set(s, \"framerate\", GST_TYPE_FRACTION, fps_n, fps_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..bc04b073 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,12 @@ 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, const GstStructure *container);\n+void gst_libcamera_framerate_to_caps(const libcamera::ControlList &controls, GstCaps *caps,\n+\t\t\t\t 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 1ee1d08a..5cdf23a1 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@@ -524,6 +529,10 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n \t\treturn;\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@@ -531,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(state->initControls_, 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@@ -566,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@@ -576,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", "v4", "2/2" ] }