[libcamera-devel] [PATCH v1 09/23] gst: libcamerasrc: Implement selection and acquisition
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Wed Feb 12 00:07:11 CET 2020
Hi Nicolas,
On Tue, Feb 11, 2020 at 05:23:28PM -0500, Nicolas Dufresne wrote:
> On mar, 2020-02-11 at 21:43 +0200, Laurent Pinchart wrote:
> > On Tue, Jan 28, 2020 at 10:31:56PM -0500, Nicolas Dufresne wrote:
> > > From: Nicolas Dufresne <nicolas.dufresne at collabora.com>
> > >
> > > 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 at collabora.com>
> > > ---
> > > src/gstreamer/gstlibcamerasrc.cpp | 124 ++++++++++++++++++++++++++++++
> > > 1 file changed, 124 insertions(+)
> > >
> > > diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
> > > index 2177a8d..abb376b 100644
> > > --- a/src/gstreamer/gstlibcamerasrc.cpp
> > > +++ b/src/gstreamer/gstlibcamerasrc.cpp
> > > @@ -10,13 +10,26 @@
> > > #include "gstlibcamerapad.h"
> > > #include "gstlibcamera-utils.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::shared_ptr<CameraManager> cm;
> >
> > This one should be a std::unique_ptr as there's no need to share
> > ownership.
> >
> > > + 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::shared_ptr<CameraManager> cm = std::make_shared<CameraManager>();
> > > + std::shared_ptr<Camera> cam = nullptr;
> >
> > No need to initialize cam to nullptr explicitly, this is automatic for
> > shared_ptr.
> >
> > > + gint ret = 0;
> > > + g_autofree gchar *camera_name = nullptr;
> > > +
> > > + 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)));
> >
> > Maybe ::start() instead of .start() ? Same comment for all the other
> > locations below (and in other patches if applicable).
> >
> > > + return false;
> > > + }
> > > +
> >
> > You can move the camera_name variable definition right here.
> >
> > > + {
> > > + GST_OBJECT_LOCKER(self);
> > > + if (self->camera_name)
> > > + camera_name = g_strdup(self->camera_name);
> >
> > So properties can change at any time, concurrently to the open call ?
>
> There is GST_PARAM_MUTABLE_* flag to describe the limtations on which GstState
> you are allowed to change the properties, but it's mostly informative. In this
> case I've set MUTABLE_READY, but it's actually a mistake, I should not set this
> flag, since it's only mutable in NULL state. With that pattern, it's safe and
> you can change it anytime, and then cycle through NULL state. I try to stay safe
> with nul-terminated string, specifically.
>
> In most application (or in sane application I should say), the camera-name
> property will be set, prior to state change, on the same thread we apply ready
> state. So I could remove that lock.
I don't mind keeping it to protect against some application
misbehaviours, the question was more for my information.
> > > + }
> > > +
> > > + 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 NULL"));
> > > + 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 */
> >
> > s/no/No/ and s/threads/threads./
> >
> > > + self->state->cm = cm;
> >
> > This is not an issue for now, but when we'll support hotplug, will it be
> > possible to create a single CameraManager instance that will be shared
> > by GstLibcameraSrc and GstLibcameraProvider ?
>
> These are totally independent things, not sure it's conveniant to share it. You
> can't rely on having a src when you have a provider and vis-versa. I'm not sure,
> I don't know enough about the intention with this manager. For sure I was under
> the impression that if the manager in the source is monitoring, it's mostly a
> waste of resource.
>
> Normally for hotplugin we would want the v4l2src to fail when the camera is
> unplugged and send an error message. Ideally with a specific error code, I could
> extend GstResourceError enum for that purpose. But what application do, is that
> if the camera pipeline fails (regardless of the error, they just report it to
> the user and then when it received a message from the provider (usually just
> after) it takes action to cleanup and possibly suggest another camera to the
> user.
The issue with decoupling the provider and the source is that a process
is supposed to have a single CameraManager instance active at a time. I
think it would work with multiple instances (as long as we don't try to
acquire the same camera twice), but that hasn't been tested. It's a
problem we an deal with later, when implementing hotplug support.
> > > + 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)
> > > @@ -73,7 +163,36 @@ gst_libcamera_src_get_property(GObject *object, guint prop_id, GValue *value,
> > > G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
> > > break;
> > > }
> > > +}
> > >
> > > +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
> > > @@ -83,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);
> > > }
> > > @@ -90,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;
> >
> > You can simply write
> >
> > self->state = new GstLibcameraSrcState();
> >
> > > }
> > >
> > > static void
> > > @@ -107,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",
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list