Message ID | 20200227200407.490616-10-nicolas.dufresne@collabora.com |
---|---|
State | Accepted |
Headers | show |
Series |
|
Related | show |
Hi Nicolas, Thank you for the patch. On Thu, Feb 27, 2020 at 03:03:49PM -0500, Nicolas Dufresne wrote: > This add code to select and acquire a camera. With this, it is now s/add/adds/ > possible to run pipeline like: s/pipeline/a pipeline/ or s/pipeline/pipelines/ > > 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 <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > --- > src/gstreamer/gstlibcamerasrc.cpp | 125 ++++++++++++++++++++++++++++++ > 1 file changed, 125 insertions(+) > > diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp > index c993143..0c60478 100644 > --- a/src/gstreamer/gstlibcamerasrc.cpp > +++ b/src/gstreamer/gstlibcamerasrc.cpp > @@ -10,13 +10,26 @@ > #include "gstlibcamerapad.h" > #include "gstlibcamerasrc.h" > > +#include <libcamera/camera.h> > +#include <libcamera/camera_manager.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<CameraManager> cm; > + std::shared_ptr<Camera> cam; > +}; > + > struct _GstLibcameraSrc { > GstElement parent; > GstPad *srcpad; > gchar *camera_name; > + > + GstLibcameraSrcState *state; > }; > > enum { > @@ -40,6 +53,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<CameraManager> cm = std::make_unique<CameraManager>(); > + std::shared_ptr<Camera> 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) > @@ -75,6 +165,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) > { > @@ -82,6 +202,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); > } > @@ -89,10 +210,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 > @@ -105,6 +228,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",
diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp index c993143..0c60478 100644 --- a/src/gstreamer/gstlibcamerasrc.cpp +++ b/src/gstreamer/gstlibcamerasrc.cpp @@ -10,13 +10,26 @@ #include "gstlibcamerapad.h" #include "gstlibcamerasrc.h" +#include <libcamera/camera.h> +#include <libcamera/camera_manager.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<CameraManager> cm; + std::shared_ptr<Camera> cam; +}; + struct _GstLibcameraSrc { GstElement parent; GstPad *srcpad; gchar *camera_name; + + GstLibcameraSrcState *state; }; enum { @@ -40,6 +53,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<CameraManager> cm = std::make_unique<CameraManager>(); + std::shared_ptr<Camera> 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) @@ -75,6 +165,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) { @@ -82,6 +202,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); } @@ -89,10 +210,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 @@ -105,6 +228,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",
This add code to select and acquire a camera. With this, it is now possible to run 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 <nicolas.dufresne@collabora.com> --- src/gstreamer/gstlibcamerasrc.cpp | 125 ++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+)