[{"id":3595,"web_url":"https://patchwork.libcamera.org/comment/3595/","msgid":"<aebebe27-0132-ff2b-3a6b-f65bc023e96d@ideasonboard.com>","date":"2020-01-29T12:17:50","subject":"Re: [libcamera-devel] [PATCH v1 22/23] gst: libcamerasrc: Implement\n\tinitial streaming","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Hi Nicolas,\n\nOn 29/01/2020 03:32, Nicolas Dufresne wrote:\n> From: Nicolas Dufresne <nicolas.dufresne@collabora.com>\n> \n> With this patch, the element is not able to push buffers to the next\n> element in the graph. The buffers are currently missing any metadata\n> like timestamp, sequence number. The handling of the GstFlowReturn\n> for multiple pads isn't using a GstFlowCombiner as it should.\n\nAha, so perhaps this patch is not intended to be built/used yet and is\njust for example purposes?\n\n\n> Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>\n> ---\n>  src/gstreamer/gstlibcamerapad.cpp |  18 ++-\n>  src/gstreamer/gstlibcamerapad.h   |   2 +\n>  src/gstreamer/gstlibcamerasrc.cpp | 191 +++++++++++++++++++++++++++++-\n>  3 files changed, 208 insertions(+), 3 deletions(-)\n> \n> diff --git a/src/gstreamer/gstlibcamerapad.cpp b/src/gstreamer/gstlibcamerapad.cpp\n> index 4a775e4..7bc44c1 100644\n> --- a/src/gstreamer/gstlibcamerapad.cpp\n> +++ b/src/gstreamer/gstlibcamerapad.cpp\n> @@ -152,7 +152,7 @@ gst_libcamera_pad_push_pending(GstPad *pad)\n>  {\n>  \tauto *self = GST_LIBCAMERA_PAD(pad);\n>  \tGstBuffer *buffer;\n> -\tGstFlowReturn ret = GST_FLOW_CUSTOM_SUCCESS;\n> +\tGstFlowReturn ret = GST_FLOW_OK;\n>  \n>  \t{\n>  \t\tGST_OBJECT_LOCKER(self);\n> @@ -164,3 +164,19 @@ gst_libcamera_pad_push_pending(GstPad *pad)\n>  \n>  \treturn ret;\n>  }\n> +\n> +bool\n> +gst_libcamera_pad_has_pending(GstPad *pad)\n> +{\n> +\tauto *self = GST_LIBCAMERA_PAD(pad);\n> +\tGST_OBJECT_LOCKER(self);\n> +\treturn (self->pending_buffers.length > 0);\n> +}\n> +\n> +void\n> +gst_libcamera_pad_set_latency(GstPad *pad, GstClockTime latency)\n> +{\n> +\tauto *self = GST_LIBCAMERA_PAD(pad);\n> +\tGST_OBJECT_LOCKER(self);\n> +\tself->latency = latency;\n\nself->latency isn't yet defined, and I don't think this function is used\n- so probably needs to be moved to the timestamp patch you are working on.\n\n> +}\n> diff --git a/src/gstreamer/gstlibcamerapad.h b/src/gstreamer/gstlibcamerapad.h\n> index 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__ */\n> diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp\n> index 5fc4393..947a8bf 100644\n> --- a/src/gstreamer/gstlibcamerasrc.cpp\n> +++ b/src/gstreamer/gstlibcamerasrc.cpp\n> @@ -12,8 +12,10 @@\n>  #include \"gstlibcamerapool.h\"\n>  #include \"gstlibcamera-utils.h\"\n>  \n> +#include <queue>\n>  #include <libcamera/camera.h>\n>  #include <libcamera/camera_manager.h>\n> +#include <gst/base/base.h>\n>  \n>  using namespace libcamera;\n>  \n> @@ -22,12 +24,73 @@ GST_DEBUG_CATEGORY_STATIC(source_debug);\n>  \n>  #define STREAM_LOCKER(obj) g_autoptr(GRecMutexLocker) stream_locker = g_rec_mutex_locker_new(&GST_LIBCAMERA_SRC(obj)->stream_lock)\n>  \n> -/* Used for C++ object with destructors */\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\n> +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 *\n> +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 and callbacks */\n>  struct GstLibcameraSrcState {\n> +\tGstLibcameraSrc *src;\n> +\n>  \tstd::shared_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> @@ -40,6 +103,7 @@ struct _GstLibcameraSrc {\n>  \n>  \tGstLibcameraSrcState *state;\n>  \tGstLibcameraAllocator *allocator;\n> +\tGstFlowCombiner *flow_combiner;\n>  };\n>  \n>  enum {\n> @@ -63,6 +127,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> +\tGST_OBJECT_LOCKER(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\tGST_OBJECT_LOCKER(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> @@ -115,6 +214,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 */\n>  \tself->state->cm = cm;\n>  \tself->state->cam = cam;\n> @@ -126,8 +227,74 @@ 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\tGST_OBJECT_LOCKER(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> +\t{\n> +\t\t/* Here we need to decide if we want to pause or stop the task. This\n> +\t\t * needs to happend in lock step with the callback thread which may want\n> +\t\t * to resume the task.\n> +\t\t */\n> +\t\tGST_OBJECT_LOCKER(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> -\tGST_DEBUG_OBJECT(self, \"Streaming thread it now capturing\");\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> @@ -137,6 +304,7 @@ gst_libcamera_src_task_enter(GstTask *task, GThread *thread, gpointer user_data)\n>  \tGstLibcameraSrc *self = GST_LIBCAMERA_SRC(user_data);\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> @@ -219,12 +387,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> @@ -254,6 +433,8 @@ gst_libcamera_src_task_leave(GstTask *task, GThread *thread, gpointer user_data)\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> @@ -333,6 +514,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> @@ -378,6 +562,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>","headers":{"Return-Path":"<kieran.bingham@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4507A608C6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 29 Jan 2020 13:17:54 +0100 (CET)","from [192.168.0.20]\n\t(cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 8B709516;\n\tWed, 29 Jan 2020 13:17:53 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1580300273;\n\tbh=c4NcHFtGVI5SdXgIH6VgQDRR1+beSZNuVHMEnoXFKNg=;\n\th=Reply-To:Subject:To:Cc:References:From:Date:In-Reply-To:From;\n\tb=mBUoi3ifb5dSfwGs07FyB9e4KRzxI3y2vWMV2Kpg4TPHdeMtZkRt/SHLl7vPLstPD\n\t41YF5DnnexWVRRzHxN3qGCiXYcB5ee1FcEF7LmQPrXM5DHwG6zTBLTFRm2MlQ54A9M\n\tEwE6TAQxiiI566f2cdzhVa5dy+eQxC9SxrzMHpEA=","Reply-To":"kieran.bingham@ideasonboard.com","To":"Nicolas Dufresne <nicolas@ndufresne.ca>,\n\tlibcamera-devel@lists.libcamera.org","Cc":"Nicolas Dufresne <nicolas.dufresne@collabora.com>","References":"<20200129033210.278800-1-nicolas@ndufresne.ca>\n\t<20200129033210.278800-23-nicolas@ndufresne.ca>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Openpgp":"preference=signencrypt","Autocrypt":"addr=kieran.bingham@ideasonboard.com; keydata=\n\t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\n\tWZw01QYHU/GUV/zHJSFk","Organization":"Ideas on Board","Message-ID":"<aebebe27-0132-ff2b-3a6b-f65bc023e96d@ideasonboard.com>","Date":"Wed, 29 Jan 2020 12:17:50 +0000","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101\n\tThunderbird/60.9.0","MIME-Version":"1.0","In-Reply-To":"<20200129033210.278800-23-nicolas@ndufresne.ca>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [PATCH v1 22/23] 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":"Wed, 29 Jan 2020 12:17:54 -0000"}},{"id":3599,"web_url":"https://patchwork.libcamera.org/comment/3599/","msgid":"<d85dd6b09fc23cbeffaf7254e3493ad44a043c68.camel@ndufresne.ca>","date":"2020-01-29T18:16:20","subject":"Re: [libcamera-devel] [PATCH v1 22/23] gst: libcamerasrc: Implement\n\tinitial streaming","submitter":{"id":30,"url":"https://patchwork.libcamera.org/api/people/30/","name":"Nicolas Dufresne","email":"nicolas@ndufresne.ca"},"content":"Le mercredi 29 janvier 2020 à 12:17 +0000, Kieran Bingham a écrit :\n> Hi Nicolas,\n> \n> On 29/01/2020 03:32, Nicolas Dufresne wrote:\n> > From: Nicolas Dufresne <nicolas.dufresne@collabora.com>\n> > \n> > With this patch, the element is not able to push buffers to the next\n> > element in the graph. The buffers are currently missing any metadata\n> > like timestamp, sequence number. The handling of the GstFlowReturn\n> > for multiple pads isn't using a GstFlowCombiner as it should.\n> \n> Aha, so perhaps this patch is not intended to be built/used yet and is\n> just for example purposes?\n\nI forgot to drop the comment about flow combiner desopite me implementing it\nyesterday.\n\nFor the timestamp, I'll cleanup and include my patch that assumes the timestamp\nare from CLOCK_MONOTONIC. Currently we only have V4L2 Capture nodes, which are\nrequired to use that clock, so it's fine tempory assumption. Note that it will\njust render as fast as possible without timestamp. What it breaks is RTP \nstreaming, fpsdisplaysink, QoS and muxing, which I agree is a lot.\n\n> \n> \n> > Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>\n> > ---\n> >  src/gstreamer/gstlibcamerapad.cpp |  18 ++-\n> >  src/gstreamer/gstlibcamerapad.h   |   2 +\n> >  src/gstreamer/gstlibcamerasrc.cpp | 191 +++++++++++++++++++++++++++++-\n> >  3 files changed, 208 insertions(+), 3 deletions(-)\n> > \n> > diff --git a/src/gstreamer/gstlibcamerapad.cpp\n> > b/src/gstreamer/gstlibcamerapad.cpp\n> > index 4a775e4..7bc44c1 100644\n> > --- a/src/gstreamer/gstlibcamerapad.cpp\n> > +++ b/src/gstreamer/gstlibcamerapad.cpp\n> > @@ -152,7 +152,7 @@ gst_libcamera_pad_push_pending(GstPad *pad)\n> >  {\n> >  \tauto *self = GST_LIBCAMERA_PAD(pad);\n> >  \tGstBuffer *buffer;\n> > -\tGstFlowReturn ret = GST_FLOW_CUSTOM_SUCCESS;\n> > +\tGstFlowReturn ret = GST_FLOW_OK;\n> >  \n> >  \t{\n> >  \t\tGST_OBJECT_LOCKER(self);\n> > @@ -164,3 +164,19 @@ gst_libcamera_pad_push_pending(GstPad *pad)\n> >  \n> >  \treturn ret;\n> >  }\n> > +\n> > +bool\n> > +gst_libcamera_pad_has_pending(GstPad *pad)\n> > +{\n> > +\tauto *self = GST_LIBCAMERA_PAD(pad);\n> > +\tGST_OBJECT_LOCKER(self);\n> > +\treturn (self->pending_buffers.length > 0);\n> > +}\n> > +\n> > +void\n> > +gst_libcamera_pad_set_latency(GstPad *pad, GstClockTime latency)\n> > +{\n> > +\tauto *self = GST_LIBCAMERA_PAD(pad);\n> > +\tGST_OBJECT_LOCKER(self);\n> > +\tself->latency = latency;\n> \n> self->latency isn't yet defined, and I don't think this function is used\n> - so probably needs to be moved to the timestamp patch you are working on.\n> \n> > +}\n> > diff --git a/src/gstreamer/gstlibcamerapad.h\n> > b/src/gstreamer/gstlibcamerapad.h\n> > index 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\n> > *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__ */\n> > diff --git a/src/gstreamer/gstlibcamerasrc.cpp\n> > b/src/gstreamer/gstlibcamerasrc.cpp\n> > index 5fc4393..947a8bf 100644\n> > --- a/src/gstreamer/gstlibcamerasrc.cpp\n> > +++ b/src/gstreamer/gstlibcamerasrc.cpp\n> > @@ -12,8 +12,10 @@\n> >  #include \"gstlibcamerapool.h\"\n> >  #include \"gstlibcamera-utils.h\"\n> >  \n> > +#include <queue>\n> >  #include <libcamera/camera.h>\n> >  #include <libcamera/camera_manager.h>\n> > +#include <gst/base/base.h>\n> >  \n> >  using namespace libcamera;\n> >  \n> > @@ -22,12 +24,73 @@ GST_DEBUG_CATEGORY_STATIC(source_debug);\n> >  \n> >  #define STREAM_LOCKER(obj) g_autoptr(GRecMutexLocker) stream_locker =\n> > g_rec_mutex_locker_new(&GST_LIBCAMERA_SRC(obj)->stream_lock)\n> >  \n> > -/* Used for C++ object with destructors */\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\n> > +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 *\n> > +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 and callbacks */\n> >  struct GstLibcameraSrcState {\n> > +\tGstLibcameraSrc *src;\n> > +\n> >  \tstd::shared_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> > @@ -40,6 +103,7 @@ struct _GstLibcameraSrc {\n> >  \n> >  \tGstLibcameraSrcState *state;\n> >  \tGstLibcameraAllocator *allocator;\n> > +\tGstFlowCombiner *flow_combiner;\n> >  };\n> >  \n> >  enum {\n> > @@ -63,6 +127,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> > +\tGST_OBJECT_LOCKER(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\tGST_OBJECT_LOCKER(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> > @@ -115,6 +214,8 @@ gst_libcamera_src_open(GstLibcameraSrc *self)\n> >  \t\treturn false;\n> >  \t}\n> >  \n> > +\tcam->requestCompleted.connect(self->state,\n> > &GstLibcameraSrcState::requestCompleted);\n> > +\n> >  \t/* no need to lock here we didn't start our threads */\n> >  \tself->state->cm = cm;\n> >  \tself->state->cam = cam;\n> > @@ -126,8 +227,74 @@ 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\tGST_OBJECT_LOCKER(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> > +\t{\n> > +\t\t/* Here we need to decide if we want to pause or stop the task.\n> > This\n> > +\t\t * needs to happend in lock step with the callback thread which\n> > may want\n> > +\t\t * to resume the task.\n> > +\t\t */\n> > +\t\tGST_OBJECT_LOCKER(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,\n> > 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> > -\tGST_DEBUG_OBJECT(self, \"Streaming thread it now capturing\");\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> > @@ -137,6 +304,7 @@ gst_libcamera_src_task_enter(GstTask *task, GThread\n> > *thread, gpointer user_data)\n> >  \tGstLibcameraSrc *self = GST_LIBCAMERA_SRC(user_data);\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> > @@ -219,12 +387,23 @@ gst_libcamera_src_task_enter(GstTask *task, GThread\n> > *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.strea\n> > m());\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\",\n> > g_strerror(-ret)),\n> > +\t\t\t\t  (\"Camera.start() failed with error code %i\",\n> > ret));\n> > +\t\tgst_task_stop(task);\n> > +\t\treturn;\n> >  \t}\n> >  \n> >  done:\n> > @@ -254,6 +433,8 @@ gst_libcamera_src_task_leave(GstTask *task, GThread\n> > *thread, gpointer user_data)\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> > @@ -333,6 +514,9 @@ gst_libcamera_src_change_state(GstElement *element,\n> > 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> > @@ -378,6 +562,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> >","headers":{"Return-Path":"<nicolas@ndufresne.ca>","Received":["from mail-qk1-x732.google.com (mail-qk1-x732.google.com\n\t[IPv6:2607:f8b0:4864:20::732])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C5FD6608C6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 29 Jan 2020 19:16:23 +0100 (CET)","by mail-qk1-x732.google.com with SMTP id x1so127887qkl.12\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 29 Jan 2020 10:16:23 -0800 (PST)","from nicolas-tpx395.localdomain ([2610:98:8005::527])\n\tby smtp.gmail.com with ESMTPSA id\n\t11sm1310801qko.76.2020.01.29.10.16.21\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tWed, 29 Jan 2020 10:16:21 -0800 (PST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ndufresne-ca.20150623.gappssmtp.com; s=20150623;\n\th=message-id:subject:from:to:date:in-reply-to:references:user-agent\n\t:mime-version:content-transfer-encoding;\n\tbh=vXXIENpr25zSANkXAPEam1nP/orx7YYbBjmmZPkuYtk=;\n\tb=AQZi5R4qejHgs8e2Jbw7UbI6GKhgP0XCWO38Sle4zvqCKs5T2lH060e8NudFFnHMqV\n\twAYmHhsLi8IwuPUzKNiFhkFCtUaC0btvJ17d+eJuUWmSyHpllQXQV6Db4nRvK1Iaf4Dx\n\tb1+k5qioxMJLkbuTLT94Ag7GSiY5q9rVteutZq3b0l0naN0k+DMgvce5HqX04SeuX2nr\n\tVoSiq17cwet3nwEIqm9xVyyH3kqT3hFCP+y/BWP1J2NsY4ZWWgchWvn7JspW7btgbhl3\n\tFTUiR+ahT3Wnqy+aNwijyPzfZAnTPHjMKQAOZTzCLJ7Z4b7qq0p8+ZDpbPL/ijLhIGwe\n\tfbTw==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:message-id:subject:from:to:date:in-reply-to\n\t:references:user-agent:mime-version:content-transfer-encoding;\n\tbh=vXXIENpr25zSANkXAPEam1nP/orx7YYbBjmmZPkuYtk=;\n\tb=QgUt57W5CUY880I3L7lquGmBHfK0pIsRbOzogeDmzBa+R/DpL0TWGMvBVCJ2WbJ2Fr\n\tfoYatHGlip/7iiuCZuuwX1tGwjt22NeBzm4UmmaiYiZyVCJdmabVsLI0ny3Ho7YW7ILp\n\tajF1vnsHaMtRC/Zt2AJyj5Ad/gT/Etth9DpTmZViuuGn/5MbZi8ssT83jekzV+vICfe3\n\tQJQsaAFyjtdFb+cXJm9sHaFIP9ysCb5BU1jQPS/rS/l8q5F5ZQid/ZzI70eAKfKaTzmu\n\tNlnX9K0eGqWQswX35emhtrkrFpu/CRLAotoyzrofS4uWweTIiHjkLV75zaqNKMNSQY5P\n\t0deQ==","X-Gm-Message-State":"APjAAAXOkPt+EC/7TFcRVfySBfpadWP6te3ba0UiM7K/c5K8Aj3/XJNE\n\txWoXLM2sZGjJ8l6S41ylh0nRIw==","X-Google-Smtp-Source":"APXvYqzdci9svTdu3ZOSK51Q6iY9tDon20RxjtZelCzqEomeqp8KWFkEFWmzSuJTLBd5vLRukZOPYw==","X-Received":"by 2002:a37:f518:: with SMTP id l24mr932843qkk.441.1580321782511;\n\tWed, 29 Jan 2020 10:16:22 -0800 (PST)","Message-ID":"<d85dd6b09fc23cbeffaf7254e3493ad44a043c68.camel@ndufresne.ca>","From":"Nicolas Dufresne <nicolas@ndufresne.ca>","To":"kieran.bingham@ideasonboard.com, libcamera-devel@lists.libcamera.org","Date":"Wed, 29 Jan 2020 13:16:20 -0500","In-Reply-To":"<aebebe27-0132-ff2b-3a6b-f65bc023e96d@ideasonboard.com>","References":"<20200129033210.278800-1-nicolas@ndufresne.ca>\n\t<20200129033210.278800-23-nicolas@ndufresne.ca>\n\t<aebebe27-0132-ff2b-3a6b-f65bc023e96d@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","User-Agent":"Evolution 3.34.3 (3.34.3-1.fc31) ","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [PATCH v1 22/23] 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":"Wed, 29 Jan 2020 18:16:24 -0000"}}]