From patchwork Fri Mar 6 20:26:19 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicolas Dufresne X-Patchwork-Id: 3015 Return-Path: Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [46.235.227.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6914C628CC for ; Fri, 6 Mar 2020 21:27:04 +0100 (CET) Received: from nicolas-tpx395.localdomain (unknown [IPv6:2610:98:8005::527]) (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 D6D432972F1; Fri, 6 Mar 2020 20:27:03 +0000 (GMT) From: Nicolas Dufresne To: libcamera-devel@lists.libcamera.org Cc: Nicolas Dufresne , Laurent Pinchart Date: Fri, 6 Mar 2020 15:26:19 -0500 Message-Id: <20200306202637.525587-10-nicolas@ndufresne.ca> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200306202637.525587-1-nicolas@ndufresne.ca> References: <20200306202637.525587-1-nicolas@ndufresne.ca> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 09/27] gst: libcamerasrc: Implement selection and acquisition 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: Fri, 06 Mar 2020 20:27:04 -0000 From: Nicolas Dufresne This adds code to select and acquire a camera. With this, it is now possible to run a pipeline like: gst-launch-1.0 libcamerasrc ! fakesink Though no buffer will be streamed yet. In this function, we implement the change_state() virtual method to trigger actions on specific state transitions. Note that we also return GST_STATE_CHANGE_NO_PREROLL in GST_STATE_CHANGE_READY_TO_PAUSED and GST_STATE_CHANGE_PLAYING_TO_PAUSED transitions as this is required for all live sources. Signed-off-by: Nicolas Dufresne Reviewed-by: Laurent Pinchart --- src/gstreamer/gstlibcamerasrc.cpp | 125 ++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp index 3e13a00..fd0eaf5 100644 --- a/src/gstreamer/gstlibcamerasrc.cpp +++ b/src/gstreamer/gstlibcamerasrc.cpp @@ -8,16 +8,29 @@ #include "gstlibcamerasrc.h" +#include +#include + #include "gstlibcamerapad.h" #include "gstlibcamera-utils.h" +using namespace libcamera; + GST_DEBUG_CATEGORY_STATIC(source_debug); #define GST_CAT_DEFAULT source_debug +/* Used for C++ object with destructors. */ +struct GstLibcameraSrcState { + std::unique_ptr cm_; + std::shared_ptr cam_; +}; + struct _GstLibcameraSrc { GstElement parent; GstPad *srcpad; gchar *camera_name; + + GstLibcameraSrcState *state; }; enum { @@ -41,6 +54,83 @@ GstStaticPadTemplate request_src_template = { "src_%s", GST_PAD_SRC, GST_PAD_REQUEST, TEMPLATE_CAPS }; +static bool +gst_libcamera_src_open(GstLibcameraSrc *self) +{ + std::unique_ptr cm = std::make_unique(); + std::shared_ptr cam; + gint ret = 0; + + GST_DEBUG_OBJECT(self, "Opening camera device ..."); + + ret = cm->start(); + if (ret) { + GST_ELEMENT_ERROR(self, LIBRARY, INIT, + ("Failed listing cameras."), + ("libcamera::CameraMananger::start() failed: %s", g_strerror(-ret))); + return false; + } + + g_autofree gchar *camera_name = nullptr; + { + GLibLocker lock(GST_OBJECT(self)); + if (self->camera_name) + camera_name = g_strdup(self->camera_name); + } + + if (camera_name) { + cam = cm->get(self->camera_name); + if (!cam) { + GST_ELEMENT_ERROR(self, RESOURCE, NOT_FOUND, + ("Could not find a camera named '%s'.", self->camera_name), + ("libcamera::CameraMananger::get() returned nullptr")); + return false; + } + } else { + if (cm->cameras().empty()) { + GST_ELEMENT_ERROR(self, RESOURCE, NOT_FOUND, + ("Could not find any supported camera on this system."), + ("libcamera::CameraMananger::cameras() is empty")); + return false; + } + cam = cm->cameras()[0]; + } + + GST_INFO_OBJECT(self, "Using camera named '%s'", cam->name().c_str()); + + ret = cam->acquire(); + if (ret) { + GST_ELEMENT_ERROR(self, RESOURCE, BUSY, + ("Camera name '%s' is already in use.", cam->name().c_str()), + ("libcamera::Camera::acquire() failed: %s", g_strerror(ret))); + return false; + } + + /* No need to lock here, we didn't start our threads yet. */ + self->state->cm_ = std::move(cm); + self->state->cam_ = cam; + + return true; +} + +static void +gst_libcamera_src_close(GstLibcameraSrc *self) +{ + GstLibcameraSrcState *state = self->state; + gint ret; + + ret = state->cam_->release(); + if (ret) { + GST_ELEMENT_WARNING(self, RESOURCE, BUSY, + ("Camera name '%s' is still in use.", state->cam_->name().c_str()), + ("libcamera::Camera.release() failed: %s", g_strerror(-ret))); + } + + state->cam_.reset(); + state->cm_->stop(); + state->cm_.reset(); +} + static void gst_libcamera_src_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) @@ -76,6 +166,36 @@ gst_libcamera_src_get_property(GObject *object, guint prop_id, GValue *value, } } +static GstStateChangeReturn +gst_libcamera_src_change_state(GstElement *element, GstStateChange transition) +{ + GstLibcameraSrc *self = GST_LIBCAMERA_SRC(element); + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GstElementClass *klass = GST_ELEMENT_CLASS(gst_libcamera_src_parent_class); + + ret = klass->change_state(element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (!gst_libcamera_src_open(self)) + return GST_STATE_CHANGE_FAILURE; + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + ret = GST_STATE_CHANGE_NO_PREROLL; + break; + case GST_STATE_CHANGE_READY_TO_NULL: + gst_libcamera_src_close(self); + break; + default: + break; + } + + return ret; +} + static void gst_libcamera_src_finalize(GObject *object) { @@ -83,6 +203,7 @@ gst_libcamera_src_finalize(GObject *object) GstLibcameraSrc *self = GST_LIBCAMERA_SRC(object); g_free(self->camera_name); + delete self->state; return klass->finalize(object); } @@ -90,10 +211,12 @@ gst_libcamera_src_finalize(GObject *object) static void gst_libcamera_src_init(GstLibcameraSrc *self) { + GstLibcameraSrcState *state = new GstLibcameraSrcState(); GstPadTemplate *templ = gst_element_get_pad_template(GST_ELEMENT(self), "src"); self->srcpad = gst_pad_new_from_template(templ, "src"); gst_element_add_pad(GST_ELEMENT(self), self->srcpad); + self->state = state; } static void @@ -106,6 +229,8 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass) object_class->get_property = gst_libcamera_src_get_property; object_class->finalize = gst_libcamera_src_finalize; + element_class->change_state = gst_libcamera_src_change_state; + gst_element_class_set_metadata(element_class, "libcamera Source", "Source/Video", "Linux Camera source using libcamera",