Patch Detail
Show a patch.
GET /api/patches/2904/?format=api
{ "id": 2904, "url": "https://patchwork.libcamera.org/api/patches/2904/?format=api", "web_url": "https://patchwork.libcamera.org/patch/2904/", "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": "<20200227200407.490616-23-nicolas.dufresne@collabora.com>", "date": "2020-02-27T20:04:02", "name": "[libcamera-devel,v2,22/27] gst: libcamerasrc: Implement initial streaming", "commit_ref": null, "pull_url": null, "state": "accepted", "archived": false, "hash": "402c9822483d252e98038f4f5977f58566fde69f", "submitter": { "id": 31, "url": "https://patchwork.libcamera.org/api/people/31/?format=api", "name": "Nicolas Dufresne", "email": "nicolas.dufresne@collabora.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/2904/mbox/", "series": [ { "id": 693, "url": "https://patchwork.libcamera.org/api/series/693/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=693", "date": "2020-02-27T20:03:40", "name": "GStreamer Element for libcamera", "version": 2, "mbox": "https://patchwork.libcamera.org/series/693/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/2904/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/2904/checks/", "tags": {}, "headers": { "Return-Path": "<nicolas.dufresne@collabora.com>", "Received": [ "from bhuna.collabora.co.uk (bhuna.collabora.co.uk [46.235.227.227])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 7541F6274B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 27 Feb 2020 21:04:28 +0100 (CET)", "from [127.0.0.1] (localhost [127.0.0.1])\n\t(Authenticated sender: nicolas) with ESMTPSA id 074DE29654A" ], "From": "Nicolas Dufresne <nicolas.dufresne@collabora.com>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Thu, 27 Feb 2020 15:04:02 -0500", "Message-Id": "<20200227200407.490616-23-nicolas.dufresne@collabora.com>", "X-Mailer": "git-send-email 2.24.1", "In-Reply-To": "<20200227200407.490616-1-nicolas.dufresne@collabora.com>", "References": "<20200227200407.490616-1-nicolas.dufresne@collabora.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v2 22/27] gst: libcamerasrc: Implement\n\tinitial streaming", "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>", "X-List-Received-Date": "Thu, 27 Feb 2020 20:04:31 -0000" }, "content": "With this patch, the element is now able to push buffers to the next\nelement in the graph. The buffers are currently missing any metadata\nlike timestamp, sequence number. This will be added in the next commit.\n\nSigned-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>\n---\n src/gstreamer/gstlibcamerapad.cpp | 11 +-\n src/gstreamer/gstlibcamerapad.h | 2 +\n src/gstreamer/gstlibcamerasrc.cpp | 192 +++++++++++++++++++++++++++++-\n 3 files changed, 202 insertions(+), 3 deletions(-)", "diff": "diff --git a/src/gstreamer/gstlibcamerapad.cpp b/src/gstreamer/gstlibcamerapad.cpp\nindex 8662c92..2cf1630 100644\n--- a/src/gstreamer/gstlibcamerapad.cpp\n+++ b/src/gstreamer/gstlibcamerapad.cpp\n@@ -18,6 +18,7 @@ struct _GstLibcameraPad {\n \tStreamRole role;\n \tGstLibcameraPool *pool;\n \tGQueue pending_buffers;\n+\tGstClockTime latency;\n };\n \n enum {\n@@ -159,7 +160,15 @@ gst_libcamera_pad_push_pending(GstPad *pad)\n \t}\n \n \tif (!buffer)\n-\t\treturn GST_FLOW_CUSTOM_SUCCESS;\n+\t\treturn GST_FLOW_OK;\n \n \treturn gst_pad_push(pad, buffer);\n }\n+\n+bool\n+gst_libcamera_pad_has_pending(GstPad *pad)\n+{\n+\tauto *self = GST_LIBCAMERA_PAD(pad);\n+\tGLibLocker lock(GST_OBJECT(self));\n+\treturn (self->pending_buffers.length > 0);\n+}\ndiff --git a/src/gstreamer/gstlibcamerapad.h b/src/gstreamer/gstlibcamerapad.h\nindex d928570..eb24000 100644\n--- a/src/gstreamer/gstlibcamerapad.h\n+++ b/src/gstreamer/gstlibcamerapad.h\n@@ -30,4 +30,6 @@ void gst_libcamera_pad_queue_buffer(GstPad *pad, GstBuffer *buffer);\n \n GstFlowReturn gst_libcamera_pad_push_pending(GstPad *pad);\n \n+bool gst_libcamera_pad_has_pending(GstPad *pad);\n+\n #endif /* __GST_LIBCAMERA_PAD_H__ */\ndiff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp\nindex 83f93cc..70f2048 100644\n--- a/src/gstreamer/gstlibcamerasrc.cpp\n+++ b/src/gstreamer/gstlibcamerasrc.cpp\n@@ -18,8 +18,10 @@\n #include \"gstlibcamerapool.h\"\n #include \"gstlibcamerasrc.h\"\n \n+#include <gst/base/base.h>\n #include <libcamera/camera.h>\n #include <libcamera/camera_manager.h>\n+#include <queue>\n #include <vector>\n \n using namespace libcamera;\n@@ -27,12 +29,71 @@ using namespace libcamera;\n GST_DEBUG_CATEGORY_STATIC(source_debug);\n #define GST_CAT_DEFAULT source_debug\n \n+struct RequestWrap {\n+\tRequestWrap(Request *request);\n+\t~RequestWrap();\n+\n+\tvoid attachBuffer(GstBuffer *buffer);\n+\tGstBuffer *detachBuffer(Stream *stream);\n+\n+\t/* For ptr comparision only. */\n+\tRequest *request_;\n+\tstd::map<Stream *, GstBuffer *> buffers_;\n+};\n+\n+RequestWrap::RequestWrap(Request *request)\n+\t: request_(request)\n+{\n+}\n+\n+RequestWrap::~RequestWrap()\n+{\n+\tfor (std::pair<Stream *const, GstBuffer *> &item : buffers_) {\n+\t\tif (item.second)\n+\t\t\tgst_buffer_unref(item.second);\n+\t}\n+}\n+\n+void RequestWrap::attachBuffer(GstBuffer *buffer)\n+{\n+\tFrameBuffer *fb = gst_libcamera_buffer_get_frame_buffer(buffer);\n+\tStream *stream = gst_libcamera_buffer_get_stream(buffer);\n+\n+\trequest_->addBuffer(stream, fb);\n+\n+\tauto item = buffers_.find(stream);\n+\tif (item != buffers_.end()) {\n+\t\tgst_buffer_unref(item->second);\n+\t\titem->second = buffer;\n+\t} else {\n+\t\tbuffers_[stream] = buffer;\n+\t}\n+}\n+\n+GstBuffer *RequestWrap::detachBuffer(Stream *stream)\n+{\n+\tGstBuffer *buffer = nullptr;\n+\n+\tauto item = buffers_.find(stream);\n+\tif (item != buffers_.end()) {\n+\t\tbuffer = item->second;\n+\t\titem->second = nullptr;\n+\t}\n+\n+\treturn buffer;\n+}\n+\n /* Used for C++ object with destructors. */\n struct GstLibcameraSrcState {\n+\tGstLibcameraSrc *src;\n+\n \tstd::unique_ptr<CameraManager> cm;\n \tstd::shared_ptr<Camera> cam;\n \tstd::unique_ptr<CameraConfiguration> config;\n \tstd::vector<GstPad *> srcpads;\n+\tstd::queue<std::unique_ptr<RequestWrap>> requests;\n+\n+\tvoid requestCompleted(Request *request);\n };\n \n struct _GstLibcameraSrc {\n@@ -45,6 +106,7 @@ struct _GstLibcameraSrc {\n \n \tGstLibcameraSrcState *state;\n \tGstLibcameraAllocator *allocator;\n+\tGstFlowCombiner *flow_combiner;\n };\n \n enum {\n@@ -68,6 +130,41 @@ GstStaticPadTemplate request_src_template = {\n \t\"src_%s\", GST_PAD_SRC, GST_PAD_REQUEST, TEMPLATE_CAPS\n };\n \n+void\n+GstLibcameraSrcState::requestCompleted(Request *request)\n+{\n+\tGLibLocker lock(GST_OBJECT(this->src));\n+\n+\tGST_DEBUG_OBJECT(this->src, \"buffers are ready\");\n+\n+\tstd::unique_ptr<RequestWrap> wrap = std::move(this->requests.front());\n+\tthis->requests.pop();\n+\n+\tg_return_if_fail(wrap->request_ == request);\n+\n+\tif ((request->status() == Request::RequestCancelled)) {\n+\t\tGST_DEBUG_OBJECT(this->src, \"Request was cancelled\");\n+\t\treturn;\n+\t}\n+\n+\tGstBuffer *buffer;\n+\tfor (GstPad *srcpad : this->srcpads) {\n+\t\tStream *stream = gst_libcamera_pad_get_stream(srcpad);\n+\t\tbuffer = wrap->detachBuffer(stream);\n+\t\tgst_libcamera_pad_queue_buffer(srcpad, buffer);\n+\t}\n+\n+\t{\n+\t\t/* We only want to resume the task if it's paused. */\n+\t\tGstTask *task = this->src->task;\n+\t\tGLibLocker lock(GST_OBJECT(task));\n+\t\tif (GST_TASK_STATE(task) == GST_TASK_PAUSED) {\n+\t\t\tGST_TASK_STATE(task) = GST_TASK_STARTED;\n+\t\t\tGST_TASK_SIGNAL(task);\n+\t\t}\n+\t}\n+}\n+\n static bool\n gst_libcamera_src_open(GstLibcameraSrc *self)\n {\n@@ -120,6 +217,8 @@ gst_libcamera_src_open(GstLibcameraSrc *self)\n \t\treturn false;\n \t}\n \n+\tcam->requestCompleted.connect(self->state, &GstLibcameraSrcState::requestCompleted);\n+\n \t/* No need to lock here, we didn't start our threads yet. */\n \tself->state->cm = std::move(cm);\n \tself->state->cam = cam;\n@@ -131,17 +230,85 @@ static void\n gst_libcamera_src_task_run(gpointer user_data)\n {\n \tGstLibcameraSrc *self = GST_LIBCAMERA_SRC(user_data);\n+\tGstLibcameraSrcState *state = self->state;\n+\n+\tRequest *request = new Request(state->cam.get());\n+\tauto wrap = std::make_unique<RequestWrap>(request);\n+\tfor (GstPad *srcpad : state->srcpads) {\n+\t\tGstLibcameraPool *pool = gst_libcamera_pad_get_pool(srcpad);\n+\t\tGstBuffer *buffer;\n+\t\tGstFlowReturn ret;\n+\n+\t\tret = gst_buffer_pool_acquire_buffer(GST_BUFFER_POOL(pool),\n+\t\t\t\t\t\t &buffer, nullptr);\n+\t\tif (ret != GST_FLOW_OK) {\n+\t\t\t/* RequestWrap does not take ownership, and we won't be\n+\t\t\t * queueing this one due to lack of buffers. */\n+\t\t\tdelete request;\n+\t\t\trequest = NULL;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\twrap->attachBuffer(buffer);\n+\t}\n+\n+\tif (request) {\n+\t\tGLibLocker lock(GST_OBJECT(self));\n+\t\tGST_TRACE_OBJECT(self, \"Requesting buffers\");\n+\t\tstate->cam->queueRequest(request);\n+\t\tstate->requests.push(std::move(wrap));\n+\t}\n+\n+\tGstFlowReturn ret = GST_FLOW_OK;\n+\tgst_flow_combiner_reset(self->flow_combiner);\n+\tfor (GstPad *srcpad : state->srcpads) {\n+\t\tret = gst_libcamera_pad_push_pending(srcpad);\n+\t\tret = gst_flow_combiner_update_pad_flow(self->flow_combiner,\n+\t\t\t\t\t\t\tsrcpad, ret);\n+\t}\n \n-\tGST_DEBUG_OBJECT(self, \"Streaming thread is now capturing\");\n+\t{\n+\t\t/*\n+\t\t * Here we need to decide if we want to pause or stop the task. This\n+\t\t * needs to happen in lock step with the callback thread which may want\n+\t\t * to resume the task.\n+\t\t */\n+\t\tGLibLocker lock(GST_OBJECT(self));\n+\t\tif (ret != GST_FLOW_OK) {\n+\t\t\tif (ret == GST_FLOW_EOS) {\n+\t\t\t\tg_autoptr(GstEvent) eos = gst_event_new_eos();\n+\t\t\t\tguint32 seqnum = gst_util_seqnum_next();\n+\t\t\t\tgst_event_set_seqnum(eos, seqnum);\n+\t\t\t\tfor (GstPad *srcpad : state->srcpads)\n+\t\t\t\t\tgst_pad_push_event(srcpad, gst_event_ref(eos));\n+\t\t\t} else if (ret != GST_FLOW_FLUSHING) {\n+\t\t\t\tGST_ELEMENT_FLOW_ERROR(self, ret);\n+\t\t\t}\n+\t\t\tgst_task_stop(self->task);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tgboolean do_pause = true;\n+\t\tfor (GstPad *srcpad : state->srcpads) {\n+\t\t\tif (gst_libcamera_pad_has_pending(srcpad)) {\n+\t\t\t\tdo_pause = false;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (do_pause)\n+\t\t\tgst_task_pause(self->task);\n+\t}\n }\n \n static void\n gst_libcamera_src_task_enter(GstTask *task, GThread *thread, gpointer user_data)\n {\n \tGstLibcameraSrc *self = GST_LIBCAMERA_SRC(user_data);\n-\tGLibRecLocker(&self->stream_lock);\n+\tGLibRecLocker lock(&self->stream_lock);\n \tGstLibcameraSrcState *state = self->state;\n \tGstFlowReturn flow_ret = GST_FLOW_OK;\n+\tgint ret = 0;\n \n \tGST_DEBUG_OBJECT(self, \"Streaming thread has started\");\n \n@@ -230,12 +397,23 @@ gst_libcamera_src_task_enter(GstTask *task, GThread *thread, gpointer user_data)\n \t\treturn;\n \t}\n \n+\tself->flow_combiner = gst_flow_combiner_new();\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\tGstLibcameraPool *pool = gst_libcamera_pool_new(self->allocator,\n \t\t\t\t\t\t\t\tstream_cfg.stream());\n \t\tgst_libcamera_pad_set_pool(srcpad, pool);\n+\t\tgst_flow_combiner_add_pad(self->flow_combiner, srcpad);\n+\t}\n+\n+\tret = state->cam->start();\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+\t\t\t\t (\"Camera.start() failed with error code %i\", ret));\n+\t\tgst_task_stop(task);\n+\t\treturn;\n \t}\n \n done:\n@@ -257,10 +435,14 @@ gst_libcamera_src_task_leave(GstTask *task, GThread *thread, gpointer user_data)\n \n \tGST_DEBUG_OBJECT(self, \"Streaming thread is about to stop\");\n \n+\tstate->cam->stop();\n+\n \tfor (GstPad *srcpad : state->srcpads)\n \t\tgst_libcamera_pad_set_pool(srcpad, NULL);\n \n \tg_clear_object(&self->allocator);\n+\tg_clear_pointer(&self->flow_combiner,\n+\t\t\t(GDestroyNotify)gst_flow_combiner_free);\n }\n \n static void\n@@ -340,6 +522,9 @@ gst_libcamera_src_change_state(GstElement *element, GstStateChange transition)\n \t\t\treturn GST_STATE_CHANGE_FAILURE;\n \t\tret = GST_STATE_CHANGE_NO_PREROLL;\n \t\tbreak;\n+\tcase GST_STATE_CHANGE_PAUSED_TO_PLAYING:\n+\t\tgst_task_start(self->task);\n+\t\tbreak;\n \tcase GST_STATE_CHANGE_PLAYING_TO_PAUSED:\n \t\tret = GST_STATE_CHANGE_NO_PREROLL;\n \t\tbreak;\n@@ -389,6 +574,9 @@ gst_libcamera_src_init(GstLibcameraSrc *self)\n \n \tstate->srcpads.push_back(gst_pad_new_from_template(templ, \"src\"));\n \tgst_element_add_pad(GST_ELEMENT(self), state->srcpads[0]);\n+\n+\t/* C-style friend. */\n+\tstate->src = self;\n \tself->state = state;\n }\n \n", "prefixes": [ "libcamera-devel", "v2", "22/27" ] }