From patchwork Wed Jan 29 03:32:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicolas Dufresne X-Patchwork-Id: 2749 Return-Path: Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [IPv6:2a00:1098:0:82:1000:25:2eeb:e3e3]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 141F4608CA for ; Wed, 29 Jan 2020 04:35:36 +0100 (CET) Received: from nicolas-tpx395.localdomain (unknown [IPv6:2002:c0de:c115:0:66fc:8b:2a38:8313]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: nicolas) by bhuna.collabora.co.uk (Postfix) with ESMTPSA id 8568028EA9F; Wed, 29 Jan 2020 03:35:34 +0000 (GMT) From: Nicolas Dufresne To: libcamera-devel@lists.libcamera.org Cc: Nicolas Dufresne Date: Tue, 28 Jan 2020 22:32:02 -0500 Message-Id: <20200129033210.278800-16-nicolas@ndufresne.ca> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200129033210.278800-1-nicolas@ndufresne.ca> References: <20200129033210.278800-1-nicolas@ndufresne.ca> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v1 15/23] gst: libcamerasrc: Implement minimal caps negotiation 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: , X-List-Received-Date: Wed, 29 Jan 2020 03:35:36 -0000 From: Nicolas Dufresne This is not expected to work in every possible cases, but should be sufficient as an initial implementation. What is does it that it turns the StreamFormats into caps and query downstream caps with that as a filter. The result is the subset of caps that can be used. We then keep the first structure in that result and fixate using the default values found in StreamConfiguration as a default in case a range is available. We then validate this configuration and turn the potentially modified configuration into caps that we push downstream. Note that we strust the order in StreamFormats as being sorted best first, but this is not currently in libcamera. Signed-off-by: Nicolas Dufresne --- src/gstreamer/gstlibcamerasrc.cpp | 71 +++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp index b1a21dc..7b478fa 100644 --- a/src/gstreamer/gstlibcamerasrc.cpp +++ b/src/gstreamer/gstlibcamerasrc.cpp @@ -24,6 +24,7 @@ GST_DEBUG_CATEGORY_STATIC(source_debug); struct GstLibcameraSrcState { std::shared_ptr cm; std::shared_ptr cam; + std::unique_ptr config; std::vector srcpads; }; @@ -132,16 +133,86 @@ gst_libcamera_src_task_enter(GstTask *task, GThread *thread, gpointer user_data) STREAM_LOCKER(user_data); GstLibcameraSrc *self = GST_LIBCAMERA_SRC(user_data); GstLibcameraSrcState *state = self->state; + GstFlowReturn flow_ret = GST_FLOW_OK; GST_DEBUG_OBJECT(self, "Streaming thread has started"); guint group_id = gst_util_group_id_next(); + StreamRoles roles; for (GstPad *srcpad : state->srcpads) { /* Create stream-id and push stream-start */ g_autofree gchar *stream_id = gst_pad_create_stream_id(srcpad, GST_ELEMENT(self), nullptr); GstEvent *event = gst_event_new_stream_start(stream_id); gst_event_set_group_id(event, group_id); gst_pad_push_event(srcpad, event); + + /* Collect the streams roles for the next iteration */ + roles.push_back(gst_libcamera_pad_get_role(srcpad)); + } + + /* Generate the stream configurations, there should be one per pad */ + state->config = state->cam->generateConfiguration(roles); + g_assert(state->config->size() == state->srcpads.size()); + + for (gsize i = 0; i < state->srcpads.size(); i++) { + GstPad *srcpad = state->srcpads[i]; + StreamConfiguration &stream_cfg = state->config->at(i); + + /* Retreive the supported caps */ + g_autoptr(GstCaps) filter = gst_libcamera_stream_formats_to_caps(stream_cfg.formats()); + g_autoptr(GstCaps) caps = gst_pad_peer_query_caps(srcpad, filter); + if (gst_caps_is_empty(caps)) { + flow_ret = GST_FLOW_NOT_NEGOTIATED; + break; + } + + /* Fixate caps and configure the stream */ + caps = gst_caps_make_writable(caps); + gst_libcamera_configure_stream_from_caps(stream_cfg, caps); + } + + if (flow_ret != GST_FLOW_OK) + goto done; + + /* Validate the configuration */ + if (state->config->validate() == CameraConfiguration::Invalid) { + flow_ret = GST_FLOW_NOT_NEGOTIATED; + goto done; + } + + /* Regardless if it has been modified, create clean caps and push the + * caps event, downstream will decide if hte caps are acceptable */ + for (gsize i = 0; i < state->srcpads.size(); i++) { + GstPad *srcpad = state->srcpads[i]; + const StreamConfiguration &stream_cfg = state->config->at(i); + + g_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg); + if (!gst_pad_push_event(srcpad, gst_event_new_caps(caps))) { + flow_ret = GST_FLOW_NOT_NEGOTIATED; + break; + } + } + + ret = state->cam->configure(state->config.get()); + if (ret) { + GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS, + ("Failed to configure camera: %s", g_strerror(-ret)), + ("Camera.configure() failed with error code %i", ret)); + gst_task_stop(task); + return; + } + +done: + switch (flow_ret) { + case GST_FLOW_NOT_NEGOTIATED: + for (GstPad *srcpad : state->srcpads) { + gst_pad_push_event(srcpad, gst_event_new_eos()); + } + GST_ELEMENT_FLOW_ERROR(self, flow_ret); + gst_task_stop(task); + return; + default: + break; } }