summaryrefslogtreecommitdiff
path: root/src/gstreamer/gstlibcamerasrc.cpp
diff options
context:
space:
mode:
authorJaslo Ziska <jaslo@ziska.de>2023-11-30 16:43:10 +0100
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2023-12-07 01:39:41 +0200
commitb06ebdedba52a31e8ba21ae293dfb6476b6ca117 (patch)
tree8add9062d318772bac90311d39bc69d9aeec0d4e /src/gstreamer/gstlibcamerasrc.cpp
parentbf231ead1c38d9a8f4a874e404e23b716612d030 (diff)
gstreamer: Implement renegotiation
This commit implements renegotiation of the camera configuration and source pad caps. A renegotiation can happen when a downstream element decides to change caps or the pipeline is dynamically changed. To handle a renegotiation the GST_FLOW_NOT_NEGOTIATED return value has to be handled in GstLibcameraSrcState::processRequest(). Otherwise the default would be to print an error and stop streaming. To archive this in a clean way the if statement is altered into a switch statement which now also has a case for GST_FLOW_NOT_NEGOTIATED. In the case of GST_FLOW_NOT_NEGOTIATED every source pad is checked for the reconfiguration flag with gst_pad_needs_reconfigure() which does not clear this flag. If at least one pad requested a reconfiguration the function returns without an error and the renegotiation will happen later in the running task. If no pad requested a reconfiguration then the function will return with an error. In gst_libcamera_src_task_run() the source pads are checked for the reconfigure flag by calling gst_pad_check_reconfigure() and if one pad returns true and the caps are not sufficient anymore then the negotiation is triggered. It is fine to trigger the negotiation after only a single pad returns true for gst_pad_check_reconfigure() because the reconfigure flags are cleared in the gst_libcamera_src_negotiate() function. If any pad requested a reconfiguration the following will happen: 1. The camera is stopped because changing the configuration may not happen while running. 2. The completedRequests queue will be cleared by calling GstLibcameraSrcState::clearRequests() because the completed buffers have the wrong configuration. 3. The new caps are negotiated by calling gst_libcamera_src_negotiate(). When the negotiation fails streaming will stop. 4. The camera is started again. Signed-off-by: Jaslo Ziska <jaslo@ziska.de> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Tested-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Diffstat (limited to 'src/gstreamer/gstlibcamerasrc.cpp')
-rw-r--r--src/gstreamer/gstlibcamerasrc.cpp77
1 files changed, 66 insertions, 11 deletions
diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
index d1d610c5..f015c6d2 100644
--- a/src/gstreamer/gstlibcamerasrc.cpp
+++ b/src/gstreamer/gstlibcamerasrc.cpp
@@ -11,7 +11,6 @@
* - Implement GstElement::send_event
* + Allowing application to use FLUSH/FLUSH_STOP
* + Prevent the main thread from accessing streaming thread
- * - Implement renegotiation (even if slow)
* - Implement GstElement::request-new-pad (multi stream)
* + Evaluate if a single streaming thread is fine
* - Add application driven request (snapshot)
@@ -302,18 +301,46 @@ int GstLibcameraSrcState::processRequest()
srcpad, ret);
}
- if (ret != GST_FLOW_OK) {
- if (ret == GST_FLOW_EOS) {
- g_autoptr(GstEvent) eos = gst_event_new_eos();
- guint32 seqnum = gst_util_seqnum_next();
- gst_event_set_seqnum(eos, seqnum);
- for (GstPad *srcpad : srcpads_)
- gst_pad_push_event(srcpad, gst_event_ref(eos));
- } else if (ret != GST_FLOW_FLUSHING) {
- GST_ELEMENT_FLOW_ERROR(src_, ret);
+ switch (ret) {
+ case GST_FLOW_OK:
+ break;
+
+ case GST_FLOW_NOT_NEGOTIATED: {
+ bool reconfigure = false;
+ for (GstPad *srcpad : srcpads_) {
+ if (gst_pad_needs_reconfigure(srcpad)) {
+ reconfigure = true;
+ break;
+ }
}
- return -EPIPE;
+ /* If no pads need a reconfiguration something went wrong. */
+ if (!reconfigure)
+ err = -EPIPE;
+
+ break;
+ }
+
+ case GST_FLOW_EOS: {
+ g_autoptr(GstEvent) eos = gst_event_new_eos();
+ guint32 seqnum = gst_util_seqnum_next();
+ gst_event_set_seqnum(eos, seqnum);
+ for (GstPad *srcpad : srcpads_)
+ gst_pad_push_event(srcpad, gst_event_ref(eos));
+
+ err = -EPIPE;
+ break;
+ }
+
+ case GST_FLOW_FLUSHING:
+ err = -EPIPE;
+ break;
+
+ default:
+ GST_ELEMENT_FLOW_ERROR(src_, ret);
+
+ err = -EPIPE;
+ break;
}
return err;
@@ -462,6 +489,9 @@ gst_libcamera_src_negotiate(GstLibcameraSrc *self)
G_CALLBACK(gst_task_resume), self->task);
gst_libcamera_pad_set_pool(srcpad, pool);
+
+ /* Clear all reconfigure flags. */
+ gst_pad_check_reconfigure(srcpad);
}
return true;
@@ -495,6 +525,31 @@ gst_libcamera_src_task_run(gpointer user_data)
return;
}
+ /* Check if a srcpad requested a renegotiation. */
+ bool reconfigure = false;
+ for (GstPad *srcpad : state->srcpads_) {
+ if (gst_pad_check_reconfigure(srcpad)) {
+ /* Check if the caps even need changing. */
+ g_autoptr(GstCaps) caps = gst_pad_get_current_caps(srcpad);
+ if (!gst_pad_peer_query_accept_caps(srcpad, caps)) {
+ reconfigure = true;
+ break;
+ }
+ }
+ }
+
+ if (reconfigure) {
+ state->cam_->stop();
+ state->clearRequests();
+
+ if (!gst_libcamera_src_negotiate(self)) {
+ GST_ELEMENT_FLOW_ERROR(self, GST_FLOW_NOT_NEGOTIATED);
+ gst_task_stop(self->task);
+ }
+
+ state->cam_->start(&state->initControls_);
+ }
+
/*
* Create and queue one request. If no buffers are available the
* function returns -ENOBUFS, which we ignore here as that's not a