[libcamera-devel,v1,05/23] gst: Add pads to the source

Message ID 20200129033210.278800-6-nicolas@ndufresne.ca
State Superseded
Headers show
Series
  • GStreamer Element for libcamera
Related show

Commit Message

Nicolas Dufresne Jan. 29, 2020, 3:31 a.m. UTC
From: Nicolas Dufresne <nicolas.dufresne@collabora.com>

This simply add the boiler plate for pads on the source element. The
design is that we have one pad, called "src", that will always be
present, and then more pads can be requested prior in READY or less
state. Initially pads have one property "stream-role" that let you
decide which role this pad will have.

Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
---
 src/gstreamer/gstlibcamerapad.cpp | 101 ++++++++++++++++++++++++++++++
 src/gstreamer/gstlibcamerapad.h   |  19 ++++++
 src/gstreamer/gstlibcamerasrc.cpp |  19 ++++++
 src/gstreamer/meson.build         |   1 +
 4 files changed, 140 insertions(+)
 create mode 100644 src/gstreamer/gstlibcamerapad.cpp
 create mode 100644 src/gstreamer/gstlibcamerapad.h

Comments

Laurent Pinchart Feb. 11, 2020, 7:21 p.m. UTC | #1
Hi Nicolas,

Thank you for the patch.

On Tue, Jan 28, 2020 at 10:31:52PM -0500, Nicolas Dufresne wrote:
> From: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> 
> This simply add the boiler plate for pads on the source element. The

s/add/adds/

> design is that we have one pad, called "src", that will always be
> present, and then more pads can be requested prior in READY or less
> state. Initially pads have one property "stream-role" that let you
> decide which role this pad will have.
> 
> Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> ---
>  src/gstreamer/gstlibcamerapad.cpp | 101 ++++++++++++++++++++++++++++++
>  src/gstreamer/gstlibcamerapad.h   |  19 ++++++
>  src/gstreamer/gstlibcamerasrc.cpp |  19 ++++++
>  src/gstreamer/meson.build         |   1 +
>  4 files changed, 140 insertions(+)
>  create mode 100644 src/gstreamer/gstlibcamerapad.cpp
>  create mode 100644 src/gstreamer/gstlibcamerapad.h
> 
> diff --git a/src/gstreamer/gstlibcamerapad.cpp b/src/gstreamer/gstlibcamerapad.cpp
> new file mode 100644
> index 0000000..c6c22e4
> --- /dev/null
> +++ b/src/gstreamer/gstlibcamerapad.cpp
> @@ -0,0 +1,101 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Collabora Ltd.
> + *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> + *
> + * gstlibcamerapad.cpp - GStreamer Capture Pad
> + */
> +
> +#include "gstlibcamerapad.h"
> +#include "gstlibcamera-utils.h"
> +
> +#include <libcamera/stream.h>
> +
> +using namespace libcamera;
> +
> +struct _GstLibcameraPad {
> +	GstPad parent;
> +	StreamRole role;
> +};
> +
> +enum {
> +	PROP_0,
> +	PROP_STREAM_ROLE
> +};
> +
> +G_DEFINE_TYPE(GstLibcameraPad, gst_libcamera_pad, GST_TYPE_PAD);
> +
> +static void
> +gst_libcamera_pad_set_property(GObject *object, guint prop_id,
> +			       const GValue *value, GParamSpec *pspec)
> +{
> +	auto *self = GST_LIBCAMERA_PAD(object);
> +	GST_OBJECT_LOCKER(self);
> +
> +	switch (prop_id) {
> +	case PROP_STREAM_ROLE:
> +		self->role = (StreamRole)g_value_get_enum(value);
> +		break;
> +	default:
> +		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
> +		break;
> +	}
> +}
> +
> +static void
> +gst_libcamera_pad_get_property(GObject *object, guint prop_id, GValue *value,
> +			       GParamSpec *pspec)
> +{
> +	auto *self = GST_LIBCAMERA_PAD(object);
> +	GST_OBJECT_LOCKER(self);
> +
> +	switch (prop_id) {
> +	case PROP_STREAM_ROLE:
> +		g_value_set_enum(value, self->role);
> +		break;
> +	default:
> +		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
> +		break;
> +	}
> +}
> +
> +static void
> +gst_libcamera_pad_init(GstLibcameraPad *self)
> +{
> +}
> +
> +static GType
> +gst_libcamera_stream_role_get_type(void)
> +{
> +	static GType type = 0;
> +	static const GEnumValue values[] = {
> +		{ StillCapture, "libcamera::StillCapture", "still-capture" },
> +		{ VideoRecording, "libcamera::VideoRecording", "video-recording" },
> +		{ Viewfinder, "libcamera::Viewfinder", "view-finder" },
> +		{ 0, NULL, NULL }
> +	};
> +
> +	if (!type)
> +		type = g_enum_register_static("GstLibcameraStreamRole", values);
> +
> +	return type;
> +}
> +
> +static void
> +gst_libcamera_pad_class_init(GstLibcameraPadClass *klass)
> +{
> +	auto *object_class = G_OBJECT_CLASS(klass);
> +
> +	object_class->set_property = gst_libcamera_pad_set_property;
> +	object_class->get_property = gst_libcamera_pad_get_property;
> +
> +	auto *spec = g_param_spec_enum("stream-role", "Stream Role",
> +				       "The selected stream role",
> +				       gst_libcamera_stream_role_get_type(),
> +				       VideoRecording,
> +				       (GParamFlags)(GST_PARAM_MUTABLE_READY
> +					             | G_PARAM_CONSTRUCT
> +						     | G_PARAM_READWRITE
> +						     | G_PARAM_STATIC_STRINGS));
> +	g_object_class_install_property(object_class, PROP_STREAM_ROLE, spec);
> +}
> diff --git a/src/gstreamer/gstlibcamerapad.h b/src/gstreamer/gstlibcamerapad.h
> new file mode 100644
> index 0000000..2e745f1
> --- /dev/null
> +++ b/src/gstreamer/gstlibcamerapad.h
> @@ -0,0 +1,19 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Collabora Ltd.
> + *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> + *
> + * gstlibcamerapad.h - GStreamer Capture Element
> + */
> +
> +#include <gst/gst.h>
> +
> +#ifndef __GST_LIBCAMERA_PAD_H__
> +#define __GST_LIBCAMERA_PAD_H__
> +
> +#define GST_TYPE_LIBCAMERA_PAD gst_libcamera_pad_get_type()
> +G_DECLARE_FINAL_TYPE(GstLibcameraPad, gst_libcamera_pad,
> +		     GST_LIBCAMERA, PAD, GstPad)
> +
> +
> +#endif /* __GST_LIBCAMERA_PAD_H__ */
> diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
> index 39a06a4..a8bcede 100644
> --- a/src/gstreamer/gstlibcamerasrc.cpp
> +++ b/src/gstreamer/gstlibcamerasrc.cpp
> @@ -7,6 +7,7 @@
>   */
>  
>  #include "gstlibcamerasrc.h"
> +#include "gstlibcamerapad.h"

Alphabetical order here too (I'll stop saying it now :-)).

>  
>  struct _GstLibcameraSrc {
>  	GstElement parent;
> @@ -14,6 +15,18 @@ struct _GstLibcameraSrc {
>  
>  G_DEFINE_TYPE(GstLibcameraSrc, gst_libcamera_src, GST_TYPE_ELEMENT);
>  
> +#define TEMPLATE_CAPS GST_STATIC_CAPS("video/x-raw;image/jpeg")

JPEG is only supported by UVC in libcamera. Should we default to a YUV
format ?

> +
> +/* For the simple case, we have a src pad that is always present. */
> +GstStaticPadTemplate src_template = {
> +	"src", GST_PAD_SRC, GST_PAD_ALWAYS, TEMPLATE_CAPS
> +};
> +
> +/* More pads can be requested in state < PAUSED */
> +GstStaticPadTemplate request_src_template = {
> +	"src_%s", GST_PAD_SRC, GST_PAD_REQUEST, TEMPLATE_CAPS
> +};
> +
>  static void
>  gst_libcamera_src_init(GstLibcameraSrc *self)
>  {
> @@ -28,4 +41,10 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)
>  				       "LibCamera Source", "Source/Video",
>  				       "Linux Camera source using libcamera",
>  				       "Nicolas Dufresne <nicolas.dufresne@collabora.com");
> +	gst_element_class_add_static_pad_template_with_gtype(element_class,
> +							     &src_template,
> +							     GST_TYPE_LIBCAMERA_PAD);
> +	gst_element_class_add_static_pad_template_with_gtype(element_class,
> +							     &request_src_template,
> +							     GST_TYPE_LIBCAMERA_PAD);
>  }
> diff --git a/src/gstreamer/meson.build b/src/gstreamer/meson.build
> index 7769b78..e497bf4 100644
> --- a/src/gstreamer/meson.build
> +++ b/src/gstreamer/meson.build
> @@ -3,6 +3,7 @@ libcamera_gst_sources = [
>      'gstlibcamera-utils.cpp',
>      'gstlibcamerasrc.cpp',
>      'gstlibcameraprovider.cpp',
> +    'gstlibcamerapad.cpp',
>  ]
>  
>  libcamera_gst_c_args = [
Nicolas Dufresne Feb. 11, 2020, 9:54 p.m. UTC | #2
On mar, 2020-02-11 at 21:21 +0200, Laurent Pinchart wrote:
> Hi Nicolas,
> 
> Thank you for the patch.
> 
> On Tue, Jan 28, 2020 at 10:31:52PM -0500, Nicolas Dufresne wrote:
> > From: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> > 
> > This simply add the boiler plate for pads on the source element. The
> 
> s/add/adds/
> 
> > design is that we have one pad, called "src", that will always be
> > present, and then more pads can be requested prior in READY or less
> > state. Initially pads have one property "stream-role" that let you
> > decide which role this pad will have.
> > 
> > Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> > ---
> >  src/gstreamer/gstlibcamerapad.cpp | 101 ++++++++++++++++++++++++++++++
> >  src/gstreamer/gstlibcamerapad.h   |  19 ++++++
> >  src/gstreamer/gstlibcamerasrc.cpp |  19 ++++++
> >  src/gstreamer/meson.build         |   1 +
> >  4 files changed, 140 insertions(+)
> >  create mode 100644 src/gstreamer/gstlibcamerapad.cpp
> >  create mode 100644 src/gstreamer/gstlibcamerapad.h
> > 
> > diff --git a/src/gstreamer/gstlibcamerapad.cpp
> > b/src/gstreamer/gstlibcamerapad.cpp
> > new file mode 100644
> > index 0000000..c6c22e4
> > --- /dev/null
> > +++ b/src/gstreamer/gstlibcamerapad.cpp
> > @@ -0,0 +1,101 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2019, Collabora Ltd.
> > + *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> > + *
> > + * gstlibcamerapad.cpp - GStreamer Capture Pad
> > + */
> > +
> > +#include "gstlibcamerapad.h"
> > +#include "gstlibcamera-utils.h"
> > +
> > +#include <libcamera/stream.h>
> > +
> > +using namespace libcamera;
> > +
> > +struct _GstLibcameraPad {
> > +	GstPad parent;
> > +	StreamRole role;
> > +};
> > +
> > +enum {
> > +	PROP_0,
> > +	PROP_STREAM_ROLE
> > +};
> > +
> > +G_DEFINE_TYPE(GstLibcameraPad, gst_libcamera_pad, GST_TYPE_PAD);
> > +
> > +static void
> > +gst_libcamera_pad_set_property(GObject *object, guint prop_id,
> > +			       const GValue *value, GParamSpec *pspec)
> > +{
> > +	auto *self = GST_LIBCAMERA_PAD(object);
> > +	GST_OBJECT_LOCKER(self);
> > +
> > +	switch (prop_id) {
> > +	case PROP_STREAM_ROLE:
> > +		self->role = (StreamRole)g_value_get_enum(value);
> > +		break;
> > +	default:
> > +		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
> > +		break;
> > +	}
> > +}
> > +
> > +static void
> > +gst_libcamera_pad_get_property(GObject *object, guint prop_id, GValue
> > *value,
> > +			       GParamSpec *pspec)
> > +{
> > +	auto *self = GST_LIBCAMERA_PAD(object);
> > +	GST_OBJECT_LOCKER(self);
> > +
> > +	switch (prop_id) {
> > +	case PROP_STREAM_ROLE:
> > +		g_value_set_enum(value, self->role);
> > +		break;
> > +	default:
> > +		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
> > +		break;
> > +	}
> > +}
> > +
> > +static void
> > +gst_libcamera_pad_init(GstLibcameraPad *self)
> > +{
> > +}
> > +
> > +static GType
> > +gst_libcamera_stream_role_get_type(void)
> > +{
> > +	static GType type = 0;
> > +	static const GEnumValue values[] = {
> > +		{ StillCapture, "libcamera::StillCapture", "still-capture" },
> > +		{ VideoRecording, "libcamera::VideoRecording", "video-recording" 
> > },
> > +		{ Viewfinder, "libcamera::Viewfinder", "view-finder" },
> > +		{ 0, NULL, NULL }
> > +	};
> > +
> > +	if (!type)
> > +		type = g_enum_register_static("GstLibcameraStreamRole", values);
> > +
> > +	return type;
> > +}
> > +
> > +static void
> > +gst_libcamera_pad_class_init(GstLibcameraPadClass *klass)
> > +{
> > +	auto *object_class = G_OBJECT_CLASS(klass);
> > +
> > +	object_class->set_property = gst_libcamera_pad_set_property;
> > +	object_class->get_property = gst_libcamera_pad_get_property;
> > +
> > +	auto *spec = g_param_spec_enum("stream-role", "Stream Role",
> > +				       "The selected stream role",
> > +				       gst_libcamera_stream_role_get_type(),
> > +				       VideoRecording,
> > +				       (GParamFlags)(GST_PARAM_MUTABLE_READY
> > +					             | G_PARAM_CONSTRUCT
> > +						     | G_PARAM_READWRITE
> > +						     | G_PARAM_STATIC_STRINGS));
> > +	g_object_class_install_property(object_class, PROP_STREAM_ROLE, spec);
> > +}
> > diff --git a/src/gstreamer/gstlibcamerapad.h
> > b/src/gstreamer/gstlibcamerapad.h
> > new file mode 100644
> > index 0000000..2e745f1
> > --- /dev/null
> > +++ b/src/gstreamer/gstlibcamerapad.h
> > @@ -0,0 +1,19 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2019, Collabora Ltd.
> > + *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> > + *
> > + * gstlibcamerapad.h - GStreamer Capture Element
> > + */
> > +
> > +#include <gst/gst.h>
> > +
> > +#ifndef __GST_LIBCAMERA_PAD_H__
> > +#define __GST_LIBCAMERA_PAD_H__
> > +
> > +#define GST_TYPE_LIBCAMERA_PAD gst_libcamera_pad_get_type()
> > +G_DECLARE_FINAL_TYPE(GstLibcameraPad, gst_libcamera_pad,
> > +		     GST_LIBCAMERA, PAD, GstPad)
> > +
> > +
> > +#endif /* __GST_LIBCAMERA_PAD_H__ */
> > diff --git a/src/gstreamer/gstlibcamerasrc.cpp
> > b/src/gstreamer/gstlibcamerasrc.cpp
> > index 39a06a4..a8bcede 100644
> > --- a/src/gstreamer/gstlibcamerasrc.cpp
> > +++ b/src/gstreamer/gstlibcamerasrc.cpp
> > @@ -7,6 +7,7 @@
> >   */
> >  
> >  #include "gstlibcamerasrc.h"
> > +#include "gstlibcamerapad.h"
> 
> Alphabetical order here too (I'll stop saying it now :-)).
> 
> >  
> >  struct _GstLibcameraSrc {
> >  	GstElement parent;
> > @@ -14,6 +15,18 @@ struct _GstLibcameraSrc {
> >  
> >  G_DEFINE_TYPE(GstLibcameraSrc, gst_libcamera_src, GST_TYPE_ELEMENT);
> >  
> > +#define TEMPLATE_CAPS GST_STATIC_CAPS("video/x-raw;image/jpeg")
> 
> JPEG is only supported by UVC in libcamera. Should we default to a YUV
> format ?

It uses video/x-raw first followed by jpeg, which include all video formats that
are not compressed. This could benefit from a space after ";" in the caps
string. It's a bit hard to read this way.

> 
> > +
> > +/* For the simple case, we have a src pad that is always present. */
> > +GstStaticPadTemplate src_template = {
> > +	"src", GST_PAD_SRC, GST_PAD_ALWAYS, TEMPLATE_CAPS
> > +};
> > +
> > +/* More pads can be requested in state < PAUSED */
> > +GstStaticPadTemplate request_src_template = {
> > +	"src_%s", GST_PAD_SRC, GST_PAD_REQUEST, TEMPLATE_CAPS
> > +};
> > +
> >  static void
> >  gst_libcamera_src_init(GstLibcameraSrc *self)
> >  {
> > @@ -28,4 +41,10 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)
> >  				       "LibCamera Source", "Source/Video",
> >  				       "Linux Camera source using libcamera",
> >  				       "Nicolas Dufresne <
> > nicolas.dufresne@collabora.com");
> > +	gst_element_class_add_static_pad_template_with_gtype(element_class,
> > +							     &src_template,
> > +							     GST_TYPE_LIBCAMERA_
> > PAD);
> > +	gst_element_class_add_static_pad_template_with_gtype(element_class,
> > +							     &request_src_templa
> > te,
> > +							     GST_TYPE_LIBCAMERA_
> > PAD);
> >  }
> > diff --git a/src/gstreamer/meson.build b/src/gstreamer/meson.build
> > index 7769b78..e497bf4 100644
> > --- a/src/gstreamer/meson.build
> > +++ b/src/gstreamer/meson.build
> > @@ -3,6 +3,7 @@ libcamera_gst_sources = [
> >      'gstlibcamera-utils.cpp',
> >      'gstlibcamerasrc.cpp',
> >      'gstlibcameraprovider.cpp',
> > +    'gstlibcamerapad.cpp',
> >  ]
> >  
> >  libcamera_gst_c_args = [

Patch

diff --git a/src/gstreamer/gstlibcamerapad.cpp b/src/gstreamer/gstlibcamerapad.cpp
new file mode 100644
index 0000000..c6c22e4
--- /dev/null
+++ b/src/gstreamer/gstlibcamerapad.cpp
@@ -0,0 +1,101 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Collabora Ltd.
+ *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
+ *
+ * gstlibcamerapad.cpp - GStreamer Capture Pad
+ */
+
+#include "gstlibcamerapad.h"
+#include "gstlibcamera-utils.h"
+
+#include <libcamera/stream.h>
+
+using namespace libcamera;
+
+struct _GstLibcameraPad {
+	GstPad parent;
+	StreamRole role;
+};
+
+enum {
+	PROP_0,
+	PROP_STREAM_ROLE
+};
+
+G_DEFINE_TYPE(GstLibcameraPad, gst_libcamera_pad, GST_TYPE_PAD);
+
+static void
+gst_libcamera_pad_set_property(GObject *object, guint prop_id,
+			       const GValue *value, GParamSpec *pspec)
+{
+	auto *self = GST_LIBCAMERA_PAD(object);
+	GST_OBJECT_LOCKER(self);
+
+	switch (prop_id) {
+	case PROP_STREAM_ROLE:
+		self->role = (StreamRole)g_value_get_enum(value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gst_libcamera_pad_get_property(GObject *object, guint prop_id, GValue *value,
+			       GParamSpec *pspec)
+{
+	auto *self = GST_LIBCAMERA_PAD(object);
+	GST_OBJECT_LOCKER(self);
+
+	switch (prop_id) {
+	case PROP_STREAM_ROLE:
+		g_value_set_enum(value, self->role);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gst_libcamera_pad_init(GstLibcameraPad *self)
+{
+}
+
+static GType
+gst_libcamera_stream_role_get_type(void)
+{
+	static GType type = 0;
+	static const GEnumValue values[] = {
+		{ StillCapture, "libcamera::StillCapture", "still-capture" },
+		{ VideoRecording, "libcamera::VideoRecording", "video-recording" },
+		{ Viewfinder, "libcamera::Viewfinder", "view-finder" },
+		{ 0, NULL, NULL }
+	};
+
+	if (!type)
+		type = g_enum_register_static("GstLibcameraStreamRole", values);
+
+	return type;
+}
+
+static void
+gst_libcamera_pad_class_init(GstLibcameraPadClass *klass)
+{
+	auto *object_class = G_OBJECT_CLASS(klass);
+
+	object_class->set_property = gst_libcamera_pad_set_property;
+	object_class->get_property = gst_libcamera_pad_get_property;
+
+	auto *spec = g_param_spec_enum("stream-role", "Stream Role",
+				       "The selected stream role",
+				       gst_libcamera_stream_role_get_type(),
+				       VideoRecording,
+				       (GParamFlags)(GST_PARAM_MUTABLE_READY
+					             | G_PARAM_CONSTRUCT
+						     | G_PARAM_READWRITE
+						     | G_PARAM_STATIC_STRINGS));
+	g_object_class_install_property(object_class, PROP_STREAM_ROLE, spec);
+}
diff --git a/src/gstreamer/gstlibcamerapad.h b/src/gstreamer/gstlibcamerapad.h
new file mode 100644
index 0000000..2e745f1
--- /dev/null
+++ b/src/gstreamer/gstlibcamerapad.h
@@ -0,0 +1,19 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Collabora Ltd.
+ *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
+ *
+ * gstlibcamerapad.h - GStreamer Capture Element
+ */
+
+#include <gst/gst.h>
+
+#ifndef __GST_LIBCAMERA_PAD_H__
+#define __GST_LIBCAMERA_PAD_H__
+
+#define GST_TYPE_LIBCAMERA_PAD gst_libcamera_pad_get_type()
+G_DECLARE_FINAL_TYPE(GstLibcameraPad, gst_libcamera_pad,
+		     GST_LIBCAMERA, PAD, GstPad)
+
+
+#endif /* __GST_LIBCAMERA_PAD_H__ */
diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
index 39a06a4..a8bcede 100644
--- a/src/gstreamer/gstlibcamerasrc.cpp
+++ b/src/gstreamer/gstlibcamerasrc.cpp
@@ -7,6 +7,7 @@ 
  */
 
 #include "gstlibcamerasrc.h"
+#include "gstlibcamerapad.h"
 
 struct _GstLibcameraSrc {
 	GstElement parent;
@@ -14,6 +15,18 @@  struct _GstLibcameraSrc {
 
 G_DEFINE_TYPE(GstLibcameraSrc, gst_libcamera_src, GST_TYPE_ELEMENT);
 
+#define TEMPLATE_CAPS GST_STATIC_CAPS("video/x-raw;image/jpeg")
+
+/* For the simple case, we have a src pad that is always present. */
+GstStaticPadTemplate src_template = {
+	"src", GST_PAD_SRC, GST_PAD_ALWAYS, TEMPLATE_CAPS
+};
+
+/* More pads can be requested in state < PAUSED */
+GstStaticPadTemplate request_src_template = {
+	"src_%s", GST_PAD_SRC, GST_PAD_REQUEST, TEMPLATE_CAPS
+};
+
 static void
 gst_libcamera_src_init(GstLibcameraSrc *self)
 {
@@ -28,4 +41,10 @@  gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)
 				       "LibCamera Source", "Source/Video",
 				       "Linux Camera source using libcamera",
 				       "Nicolas Dufresne <nicolas.dufresne@collabora.com");
+	gst_element_class_add_static_pad_template_with_gtype(element_class,
+							     &src_template,
+							     GST_TYPE_LIBCAMERA_PAD);
+	gst_element_class_add_static_pad_template_with_gtype(element_class,
+							     &request_src_template,
+							     GST_TYPE_LIBCAMERA_PAD);
 }
diff --git a/src/gstreamer/meson.build b/src/gstreamer/meson.build
index 7769b78..e497bf4 100644
--- a/src/gstreamer/meson.build
+++ b/src/gstreamer/meson.build
@@ -3,6 +3,7 @@  libcamera_gst_sources = [
     'gstlibcamera-utils.cpp',
     'gstlibcamerasrc.cpp',
     'gstlibcameraprovider.cpp',
+    'gstlibcamerapad.cpp',
 ]
 
 libcamera_gst_c_args = [