From 58feb69f8522892620825b691b4669e7c37e74b2 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Thu, 16 Jan 2020 10:31:45 -0500 Subject: gst: libcamerasrc: Implement selection and acquisition 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 Signed-off-by: Laurent Pinchart --- src/gstreamer/gstlibcamerasrc.cpp | 125 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) (limited to 'src/gstreamer') diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp index 3e13a000..fd0eaf5a 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", -- cgit v1.2.1