[v3] gstreamer: Add GstVideoMeta support
diff mbox series

Message ID 20241113072947.1579075-1-qi.hou@nxp.com
State New
Headers show
Series
  • [v3] gstreamer: Add GstVideoMeta support
Related show

Commit Message

Qi Hou Nov. 13, 2024, 7:29 a.m. UTC
GStreamer video-info calculated stride and offset may differ from
those used by the camera.

This patch enhances downstream plugin's support for videometa by
adding videometa support for libcamerasrc. It ensures that when
downstream plugin supports videometa by allocation query, libcamerasrc
also attaches videometa to buffer, preventing discrepancies
between the stride and offset calculated by video-info and camera.

Signed-off-by: Hou Qi <qi.hou@nxp.com>
---
 src/gstreamer/gstlibcamera-utils.cpp | 28 ++++++++++++++++++++++++
 src/gstreamer/gstlibcamera-utils.h   |  7 ++++++
 src/gstreamer/gstlibcamerapool.cpp   | 32 ++++++++++++++++++++++++----
 src/gstreamer/gstlibcamerapool.h     |  2 +-
 src/gstreamer/gstlibcamerasrc.cpp    | 14 +++++++++++-
 5 files changed, 77 insertions(+), 6 deletions(-)

Comments

Nicolas Dufresne Nov. 13, 2024, 7:19 p.m. UTC | #1
Hi,

Le mercredi 13 novembre 2024 à 16:29 +0900, Hou Qi a écrit :
> GStreamer video-info calculated stride and offset may differ from
> those used by the camera.
> 
> This patch enhances downstream plugin's support for videometa by
> adding videometa support for libcamerasrc. It ensures that when
> downstream plugin supports videometa by allocation query, libcamerasrc
> also attaches videometa to buffer, preventing discrepancies
> between the stride and offset calculated by video-info and camera.
> 
> Signed-off-by: Hou Qi <qi.hou@nxp.com>
> ---
>  src/gstreamer/gstlibcamera-utils.cpp | 28 ++++++++++++++++++++++++
>  src/gstreamer/gstlibcamera-utils.h   |  7 ++++++
>  src/gstreamer/gstlibcamerapool.cpp   | 32 ++++++++++++++++++++++++----
>  src/gstreamer/gstlibcamerapool.h     |  2 +-
>  src/gstreamer/gstlibcamerasrc.cpp    | 14 +++++++++++-
>  5 files changed, 77 insertions(+), 6 deletions(-)
> 
> diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp
> index 732987ef..91de1d44 100644
> --- a/src/gstreamer/gstlibcamera-utils.cpp
> +++ b/src/gstreamer/gstlibcamera-utils.cpp
> @@ -2,6 +2,9 @@
>  /*
>   * Copyright (C) 2020, Collabora Ltd.
>   *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
> + * Library       <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
> + * Copyright (C) <2007> David A. Schleef <ds@schleef.org>

I'll leave that to the upper maintainers to evaluate, but it would be nice to
identify that these are only needed as long as
gst_libcamera_extrapolate_stride() copy is there. As soon as Trixie becomes
stable, we'll bump to GStreamer 1.22 and remove it, would be nice to remove the
spurious copyright.

>   *
>   * GStreamer libcamera Utility Function
>   */
> @@ -591,6 +594,31 @@ gst_task_resume(GstTask *task)
>  }
>  #endif
>  
> +#if !GST_CHECK_VERSION(1, 22, 0)
> +gint gst_libcamera_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride)

Because its under an ifdef, it is guarantied not to clash at built time, and the
linker will always resolve it locally. Keep the mainline namespace, it will
simplify the code later.

> +{
> +	gint estride;
> +	gint comp[GST_VIDEO_MAX_COMPONENTS];
> +	gint i;
> +
> +	/* there is nothing to extrapolate on first plane */
> +	if (plane == 0)
> +		return stride;
> +
> +	gst_video_format_info_component(finfo, plane, comp);
> +
> +	/* For now, all planar formats have a single component on first plane, but
> +	* if there was a planar format with more, we'd have to make a ratio of the
> +	* number of component on the first plane against the number of component on
> +	* the current plane. */
> +	estride = 0;
> +	for (i = 0; i < GST_VIDEO_MAX_COMPONENTS && comp[i] >= 0; i++)
> +		estride += GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(finfo, comp[i], stride);
> +
> +	return estride;
> +}
> +#endif
> +
>  G_LOCK_DEFINE_STATIC(cm_singleton_lock);
>  static std::weak_ptr<CameraManager> cm_singleton_ptr;
>  
> diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h
> index cab1c814..14bf6664 100644
> --- a/src/gstreamer/gstlibcamera-utils.h
> +++ b/src/gstreamer/gstlibcamera-utils.h
> @@ -2,6 +2,9 @@
>  /*
>   * Copyright (C) 2020, Collabora Ltd.
>   *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
> + * Library       <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
> + * Copyright (C) <2007> David A. Schleef <ds@schleef.org>

Not needed.

>   *
>   * GStreamer libcamera Utility Functions
>   */
> @@ -35,6 +38,10 @@ static inline void gst_clear_event(GstEvent **event_ptr)
>  #if !GST_CHECK_VERSION(1, 17, 1)
>  gboolean gst_task_resume(GstTask *task);
>  #endif
> +
> +#if !GST_CHECK_VERSION(1, 22, 0)
> +gint gst_libcamera_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride);

Apply namespace comment here too.

> +#endif
>  std::shared_ptr<libcamera::CameraManager> gst_libcamera_get_camera_manager(int &ret);
>  
>  /**
> diff --git a/src/gstreamer/gstlibcamerapool.cpp b/src/gstreamer/gstlibcamerapool.cpp
> index 9cd7eccb..315f08ac 100644
> --- a/src/gstreamer/gstlibcamerapool.cpp
> +++ b/src/gstreamer/gstlibcamerapool.cpp
> @@ -135,16 +135,40 @@ gst_libcamera_pool_class_init(GstLibcameraPoolClass *klass)
>  }
>  
>  GstLibcameraPool *
> -gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream)
> +gst_libcamera_pool_new(GstLibcameraAllocator *allocator, const StreamConfiguration &stream_cfg,
> +		       GstCaps *caps, gboolean add_video_meta)
>  {
>  	auto *pool = GST_LIBCAMERA_POOL(g_object_new(GST_TYPE_LIBCAMERA_POOL, nullptr));
> +	GstVideoInfo info;
>  
>  	pool->allocator = GST_LIBCAMERA_ALLOCATOR(g_object_ref(allocator));
> -	pool->stream = stream;
> -
> -	gsize pool_size = gst_libcamera_allocator_get_pool_size(allocator, stream);
> +	pool->stream = stream_cfg.stream();

nit: Is pool->stream const ? C++ and C are pretty permissive, but storing a non-
const  pointer obtained from a const object is a bit of a stretch.

> +
> +	if (caps && gst_video_info_from_caps(&info, caps)) {

This needs some work, caps will never be null. Now, gst_video_info_from_caps()
will work for image/ and video/ type, in that case the format is set to ENCODED
and a video meta must not be appended. That includes bayer format, for which we
only support matching display and storage dimensions (since arbitrary cropping
of bayer is a bad idea).

In short, don't get caps, and be verbose if gst_video_info_from_caps() fails,
that is not expected.

> +		guint k, stride;
> +		gsize offset = 0;
> +		for (k = 0; k < GST_VIDEO_INFO_N_PLANES(&info); k++) {
> +#if !GST_CHECK_VERSION(1, 22, 0)

Not needed with the suggestion I just made.

> +			stride = gst_libcamera_extrapolate_stride(info.finfo, k, stream_cfg.stride);
> +#else
> +			stride = gst_video_format_info_extrapolate_stride(info.finfo, k, stream_cfg.stride);
> +#endif
> +			info.stride[k] = stride;
> +			info.offset[k] = offset;
> +			offset += stride * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info.finfo, k, GST_VIDEO_INFO_HEIGHT(&info));

Please add a comment that this should be updated if tiled formats get added in
the future. For anything single plane we don't care of course.

> +		}
> +	} else
> +		add_video_meta = false;

Not sure yet why sometimes you don't want to add it.

> +
> +	gsize pool_size = gst_libcamera_allocator_get_pool_size(allocator, stream_cfg.stream());
>  	for (gsize i = 0; i < pool_size; i++) {
>  		GstBuffer *buffer = gst_buffer_new();
> +		if (add_video_meta) {
> +			gst_buffer_add_video_meta_full(buffer, GST_VIDEO_FRAME_FLAG_NONE,
> +						       GST_VIDEO_INFO_FORMAT(&info), GST_VIDEO_INFO_WIDTH(&info),
> +						       GST_VIDEO_INFO_HEIGHT(&info), GST_VIDEO_INFO_N_PLANES(&info),
> +						       info.offset, info.stride);
> +		}
>  		pool->queue->push_back(buffer);
>  	}
>  
> diff --git a/src/gstreamer/gstlibcamerapool.h b/src/gstreamer/gstlibcamerapool.h
> index 2a7a9c77..8ad87cab 100644
> --- a/src/gstreamer/gstlibcamerapool.h
> +++ b/src/gstreamer/gstlibcamerapool.h
> @@ -21,7 +21,7 @@
>  G_DECLARE_FINAL_TYPE(GstLibcameraPool, gst_libcamera_pool, GST_LIBCAMERA, POOL, GstBufferPool)
>  
>  GstLibcameraPool *gst_libcamera_pool_new(GstLibcameraAllocator *allocator,
> -					 libcamera::Stream *stream);
> +					 const libcamera::StreamConfiguration &stream_cfg, GstCaps *caps, gboolean add_video_meta);
>  
>  libcamera::Stream *gst_libcamera_pool_get_stream(GstLibcameraPool *self);
>  
> diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
> index 8efa25f4..c05a31e7 100644
> --- a/src/gstreamer/gstlibcamerasrc.cpp
> +++ b/src/gstreamer/gstlibcamerasrc.cpp
> @@ -497,9 +497,21 @@ gst_libcamera_src_negotiate(GstLibcameraSrc *self)
>  	for (gsize i = 0; i < state->srcpads_.size(); i++) {
>  		GstPad *srcpad = state->srcpads_[i];
>  		const StreamConfiguration &stream_cfg = state->config_->at(i);
> +		GstQuery *query = NULL;
> +		gboolean add_video_meta = false;
> +
> +		g_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg);
> +		gst_libcamera_framerate_to_caps(caps, element_caps);
> +
> +		query = gst_query_new_allocation(caps, false);

My hate for boolean parameters is strong enough that I'd like to suggest:

		bool need_pool = false;
		query = gst_query_new_allocation(caps, need_pool);

> +		if (!gst_pad_peer_query(srcpad, query))
> +			GST_DEBUG_OBJECT(self, "didn't get downstream ALLOCATION hints");
> +		else
> +			add_video_meta = gst_query_find_allocation_meta(query, GST_VIDEO_META_API_TYPE, 
> NULL);

Its not sufficient to not add video meta. If downstream does not support
VideoMeta and the stride does not match what you from just turning the caps into
VideoInfo, you must bounce these buffers to system memory. For that reason, you
likely want to handle the stride extrapolation here and pass VideoInfo to the
pool instead. If the stride miss-match, you can then create a generic VideoPool
using the caps, and later own you will bounce the memory using
gst_video_frame_copy() (which can handle stride miss-match).

> +		gst_query_unref(query);
>  
>  		GstLibcameraPool *pool = gst_libcamera_pool_new(self->allocator,
> -								stream_cfg.stream());
> +								stream_cfg, caps, add_video_meta);
>  		g_signal_connect_swapped(pool, "buffer-notify",
>  					 G_CALLBACK(gst_task_resume), self->task);
>
Kieran Bingham Nov. 14, 2024, 9:21 a.m. UTC | #2
Quoting Nicolas Dufresne (2024-11-13 19:19:51)
> Hi,
> 
> Le mercredi 13 novembre 2024 à 16:29 +0900, Hou Qi a écrit :
> > GStreamer video-info calculated stride and offset may differ from
> > those used by the camera.
> > 
> > This patch enhances downstream plugin's support for videometa by
> > adding videometa support for libcamerasrc. It ensures that when
> > downstream plugin supports videometa by allocation query, libcamerasrc
> > also attaches videometa to buffer, preventing discrepancies
> > between the stride and offset calculated by video-info and camera.
> > 
> > Signed-off-by: Hou Qi <qi.hou@nxp.com>
> > ---
> >  src/gstreamer/gstlibcamera-utils.cpp | 28 ++++++++++++++++++++++++
> >  src/gstreamer/gstlibcamera-utils.h   |  7 ++++++
> >  src/gstreamer/gstlibcamerapool.cpp   | 32 ++++++++++++++++++++++++----
> >  src/gstreamer/gstlibcamerapool.h     |  2 +-
> >  src/gstreamer/gstlibcamerasrc.cpp    | 14 +++++++++++-
> >  5 files changed, 77 insertions(+), 6 deletions(-)
> > 
> > diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp
> > index 732987ef..91de1d44 100644
> > --- a/src/gstreamer/gstlibcamera-utils.cpp
> > +++ b/src/gstreamer/gstlibcamera-utils.cpp
> > @@ -2,6 +2,9 @@
> >  /*
> >   * Copyright (C) 2020, Collabora Ltd.
> >   *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> > + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
> > + * Library       <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
> > + * Copyright (C) <2007> David A. Schleef <ds@schleef.org>
> 
> I'll leave that to the upper maintainers to evaluate, but it would be nice to
> identify that these are only needed as long as
> gst_libcamera_extrapolate_stride() copy is there. As soon as Trixie becomes
> stable, we'll bump to GStreamer 1.22 and remove it, would be nice to remove the
> spurious copyright.

What about putting it in a comment banner directly adjacent to the
function with an explanation that the function has been imported
directly from the gstreamer project to support backwards compatibility
and will be removed (the function and the comment banner) when the the
older version is no longer supported ?

Then the copyright notice is directly adjacent to the code it relates
to, and it's easy to see and remove it all in one go.

Another alternative would be to create a new
gstlibcamera-compatibility.cpp and put it there on it's own - but I
think that's overkill, so keeping it in -utils is fine with me.

> 
> >   *
> >   * GStreamer libcamera Utility Function
> >   */
> > @@ -591,6 +594,31 @@ gst_task_resume(GstTask *task)
> >  }
> >  #endif
> >  
> > +#if !GST_CHECK_VERSION(1, 22, 0)
> > +gint gst_libcamera_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride)
> 
> Because its under an ifdef, it is guarantied not to clash at built time, and the
> linker will always resolve it locally. Keep the mainline namespace, it will
> simplify the code later.

Yes, I agree - lets keep this as
gst_video_format_info_extrapolate_stride please.

> 
> > +{
> > +     gint estride;
> > +     gint comp[GST_VIDEO_MAX_COMPONENTS];
> > +     gint i;
> > +
> > +     /* there is nothing to extrapolate on first plane */
> > +     if (plane == 0)
> > +             return stride;
> > +
> > +     gst_video_format_info_component(finfo, plane, comp);
> > +
> > +     /* For now, all planar formats have a single component on first plane, but
> > +     * if there was a planar format with more, we'd have to make a ratio of the
> > +     * number of component on the first plane against the number of component on
> > +     * the current plane. */
> > +     estride = 0;
> > +     for (i = 0; i < GST_VIDEO_MAX_COMPONENTS && comp[i] >= 0; i++)
> > +             estride += GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(finfo, comp[i], stride);
> > +
> > +     return estride;
> > +}
> > +#endif
> > +
> >  G_LOCK_DEFINE_STATIC(cm_singleton_lock);
> >  static std::weak_ptr<CameraManager> cm_singleton_ptr;
> >  
> > diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h
> > index cab1c814..14bf6664 100644
> > --- a/src/gstreamer/gstlibcamera-utils.h
> > +++ b/src/gstreamer/gstlibcamera-utils.h
> > @@ -2,6 +2,9 @@
> >  /*
> >   * Copyright (C) 2020, Collabora Ltd.
> >   *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> > + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
> > + * Library       <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
> > + * Copyright (C) <2007> David A. Schleef <ds@schleef.org>
> 
> Not needed.
> 
> >   *
> >   * GStreamer libcamera Utility Functions
> >   */
> > @@ -35,6 +38,10 @@ static inline void gst_clear_event(GstEvent **event_ptr)
> >  #if !GST_CHECK_VERSION(1, 17, 1)
> >  gboolean gst_task_resume(GstTask *task);
> >  #endif
> > +
> > +#if !GST_CHECK_VERSION(1, 22, 0)
> > +gint gst_libcamera_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride);
> 
> Apply namespace comment here too.
> 
> > +#endif
> >  std::shared_ptr<libcamera::CameraManager> gst_libcamera_get_camera_manager(int &ret);
> >  
> >  /**
> > diff --git a/src/gstreamer/gstlibcamerapool.cpp b/src/gstreamer/gstlibcamerapool.cpp
> > index 9cd7eccb..315f08ac 100644
> > --- a/src/gstreamer/gstlibcamerapool.cpp
> > +++ b/src/gstreamer/gstlibcamerapool.cpp
> > @@ -135,16 +135,40 @@ gst_libcamera_pool_class_init(GstLibcameraPoolClass *klass)
> >  }
> >  
> >  GstLibcameraPool *
> > -gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream)
> > +gst_libcamera_pool_new(GstLibcameraAllocator *allocator, const StreamConfiguration &stream_cfg,
> > +                    GstCaps *caps, gboolean add_video_meta)
> >  {
> >       auto *pool = GST_LIBCAMERA_POOL(g_object_new(GST_TYPE_LIBCAMERA_POOL, nullptr));
> > +     GstVideoInfo info;
> >  
> >       pool->allocator = GST_LIBCAMERA_ALLOCATOR(g_object_ref(allocator));
> > -     pool->stream = stream;
> > -
> > -     gsize pool_size = gst_libcamera_allocator_get_pool_size(allocator, stream);
> > +     pool->stream = stream_cfg.stream();
> 
> nit: Is pool->stream const ? C++ and C are pretty permissive, but storing a non-
> const  pointer obtained from a const object is a bit of a stretch.
> 
> > +
> > +     if (caps && gst_video_info_from_caps(&info, caps)) {
> 
> This needs some work, caps will never be null. Now, gst_video_info_from_caps()
> will work for image/ and video/ type, in that case the format is set to ENCODED
> and a video meta must not be appended. That includes bayer format, for which we
> only support matching display and storage dimensions (since arbitrary cropping
> of bayer is a bad idea).
> 
> In short, don't get caps, and be verbose if gst_video_info_from_caps() fails,
> that is not expected.
> 
> > +             guint k, stride;
> > +             gsize offset = 0;
> > +             for (k = 0; k < GST_VIDEO_INFO_N_PLANES(&info); k++) {
> > +#if !GST_CHECK_VERSION(1, 22, 0)
> 
> Not needed with the suggestion I just made.
> 
> > +                     stride = gst_libcamera_extrapolate_stride(info.finfo, k, stream_cfg.stride);
> > +#else
> > +                     stride = gst_video_format_info_extrapolate_stride(info.finfo, k, stream_cfg.stride);
> > +#endif
> > +                     info.stride[k] = stride;
> > +                     info.offset[k] = offset;
> > +                     offset += stride * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info.finfo, k, GST_VIDEO_INFO_HEIGHT(&info));
> 
> Please add a comment that this should be updated if tiled formats get added in
> the future. For anything single plane we don't care of course.
> 
> > +             }
> > +     } else
> > +             add_video_meta = false;
> 
> Not sure yet why sometimes you don't want to add it.
> 
> > +
> > +     gsize pool_size = gst_libcamera_allocator_get_pool_size(allocator, stream_cfg.stream());
> >       for (gsize i = 0; i < pool_size; i++) {
> >               GstBuffer *buffer = gst_buffer_new();
> > +             if (add_video_meta) {
> > +                     gst_buffer_add_video_meta_full(buffer, GST_VIDEO_FRAME_FLAG_NONE,
> > +                                                    GST_VIDEO_INFO_FORMAT(&info), GST_VIDEO_INFO_WIDTH(&info),
> > +                                                    GST_VIDEO_INFO_HEIGHT(&info), GST_VIDEO_INFO_N_PLANES(&info),
> > +                                                    info.offset, info.stride);
> > +             }
> >               pool->queue->push_back(buffer);
> >       }
> >  
> > diff --git a/src/gstreamer/gstlibcamerapool.h b/src/gstreamer/gstlibcamerapool.h
> > index 2a7a9c77..8ad87cab 100644
> > --- a/src/gstreamer/gstlibcamerapool.h
> > +++ b/src/gstreamer/gstlibcamerapool.h
> > @@ -21,7 +21,7 @@
> >  G_DECLARE_FINAL_TYPE(GstLibcameraPool, gst_libcamera_pool, GST_LIBCAMERA, POOL, GstBufferPool)
> >  
> >  GstLibcameraPool *gst_libcamera_pool_new(GstLibcameraAllocator *allocator,
> > -                                      libcamera::Stream *stream);
> > +                                      const libcamera::StreamConfiguration &stream_cfg, GstCaps *caps, gboolean add_video_meta);
> >  
> >  libcamera::Stream *gst_libcamera_pool_get_stream(GstLibcameraPool *self);
> >  
> > diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
> > index 8efa25f4..c05a31e7 100644
> > --- a/src/gstreamer/gstlibcamerasrc.cpp
> > +++ b/src/gstreamer/gstlibcamerasrc.cpp
> > @@ -497,9 +497,21 @@ gst_libcamera_src_negotiate(GstLibcameraSrc *self)
> >       for (gsize i = 0; i < state->srcpads_.size(); i++) {
> >               GstPad *srcpad = state->srcpads_[i];
> >               const StreamConfiguration &stream_cfg = state->config_->at(i);
> > +             GstQuery *query = NULL;
> > +             gboolean add_video_meta = false;
> > +
> > +             g_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg);
> > +             gst_libcamera_framerate_to_caps(caps, element_caps);
> > +
> > +             query = gst_query_new_allocation(caps, false);
> 
> My hate for boolean parameters is strong enough that I'd like to suggest:
> 
>                 bool need_pool = false;
>                 query = gst_query_new_allocation(caps, need_pool);

	bool kieran_agrees = true;
	ret = send_response(kieran_agrees);


> > +             if (!gst_pad_peer_query(srcpad, query))
> > +                     GST_DEBUG_OBJECT(self, "didn't get downstream ALLOCATION hints");
> > +             else
> > +                     add_video_meta = gst_query_find_allocation_meta(query, GST_VIDEO_META_API_TYPE, 
> > NULL);
> 
> Its not sufficient to not add video meta. If downstream does not support
> VideoMeta and the stride does not match what you from just turning the caps into
> VideoInfo, you must bounce these buffers to system memory. For that reason, you
> likely want to handle the stride extrapolation here and pass VideoInfo to the
> pool instead. If the stride miss-match, you can then create a generic VideoPool
> using the caps, and later own you will bounce the memory using
> gst_video_frame_copy() (which can handle stride miss-match).
> 
> > +             gst_query_unref(query);
> >  
> >               GstLibcameraPool *pool = gst_libcamera_pool_new(self->allocator,
> > -                                                             stream_cfg.stream());
> > +                                                             stream_cfg, caps, add_video_meta);
> >               g_signal_connect_swapped(pool, "buffer-notify",
> >                                        G_CALLBACK(gst_task_resume), self->task);
> >  
>
Laurent Pinchart Nov. 14, 2024, 10:56 a.m. UTC | #3
On Thu, Nov 14, 2024 at 09:21:59AM +0000, Kieran Bingham wrote:
> Quoting Nicolas Dufresne (2024-11-13 19:19:51)
> > Hi,
> > 
> > Le mercredi 13 novembre 2024 à 16:29 +0900, Hou Qi a écrit :
> > > GStreamer video-info calculated stride and offset may differ from
> > > those used by the camera.
> > > 
> > > This patch enhances downstream plugin's support for videometa by
> > > adding videometa support for libcamerasrc. It ensures that when
> > > downstream plugin supports videometa by allocation query, libcamerasrc
> > > also attaches videometa to buffer, preventing discrepancies
> > > between the stride and offset calculated by video-info and camera.
> > > 
> > > Signed-off-by: Hou Qi <qi.hou@nxp.com>
> > > ---
> > >  src/gstreamer/gstlibcamera-utils.cpp | 28 ++++++++++++++++++++++++
> > >  src/gstreamer/gstlibcamera-utils.h   |  7 ++++++
> > >  src/gstreamer/gstlibcamerapool.cpp   | 32 ++++++++++++++++++++++++----
> > >  src/gstreamer/gstlibcamerapool.h     |  2 +-
> > >  src/gstreamer/gstlibcamerasrc.cpp    | 14 +++++++++++-
> > >  5 files changed, 77 insertions(+), 6 deletions(-)
> > > 
> > > diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp
> > > index 732987ef..91de1d44 100644
> > > --- a/src/gstreamer/gstlibcamera-utils.cpp
> > > +++ b/src/gstreamer/gstlibcamera-utils.cpp
> > > @@ -2,6 +2,9 @@
> > >  /*
> > >   * Copyright (C) 2020, Collabora Ltd.
> > >   *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> > > + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
> > > + * Library       <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
> > > + * Copyright (C) <2007> David A. Schleef <ds@schleef.org>
> > 
> > I'll leave that to the upper maintainers to evaluate, but it would be nice to
> > identify that these are only needed as long as
> > gst_libcamera_extrapolate_stride() copy is there. As soon as Trixie becomes
> > stable, we'll bump to GStreamer 1.22 and remove it, would be nice to remove the
> > spurious copyright.
> 
> What about putting it in a comment banner directly adjacent to the
> function with an explanation that the function has been imported
> directly from the gstreamer project to support backwards compatibility
> and will be removed (the function and the comment banner) when the the
> older version is no longer supported ?
> 
> Then the copyright notice is directly adjacent to the code it relates
> to, and it's easy to see and remove it all in one go.
> 
> Another alternative would be to create a new
> gstlibcamera-compatibility.cpp and put it there on it's own - but I
> think that's overkill, so keeping it in -utils is fine with me.

Agreed, a new file is overkill, we can just add the copyright comment
directly above the function, within the #ifdef version guard.

> > >   *
> > >   * GStreamer libcamera Utility Function
> > >   */
> > > @@ -591,6 +594,31 @@ gst_task_resume(GstTask *task)
> > >  }
> > >  #endif
> > >  
> > > +#if !GST_CHECK_VERSION(1, 22, 0)
> > > +gint gst_libcamera_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride)
> > 
> > Because its under an ifdef, it is guarantied not to clash at built time, and the
> > linker will always resolve it locally. Keep the mainline namespace, it will
> > simplify the code later.
> 
> Yes, I agree - lets keep this as
> gst_video_format_info_extrapolate_stride please.
> 
> > 
> > > +{
> > > +     gint estride;
> > > +     gint comp[GST_VIDEO_MAX_COMPONENTS];
> > > +     gint i;
> > > +
> > > +     /* there is nothing to extrapolate on first plane */
> > > +     if (plane == 0)
> > > +             return stride;
> > > +
> > > +     gst_video_format_info_component(finfo, plane, comp);
> > > +
> > > +     /* For now, all planar formats have a single component on first plane, but
> > > +     * if there was a planar format with more, we'd have to make a ratio of the
> > > +     * number of component on the first plane against the number of component on
> > > +     * the current plane. */
> > > +     estride = 0;
> > > +     for (i = 0; i < GST_VIDEO_MAX_COMPONENTS && comp[i] >= 0; i++)
> > > +             estride += GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(finfo, comp[i], stride);
> > > +
> > > +     return estride;
> > > +}
> > > +#endif
> > > +
> > >  G_LOCK_DEFINE_STATIC(cm_singleton_lock);
> > >  static std::weak_ptr<CameraManager> cm_singleton_ptr;
> > >  
> > > diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h
> > > index cab1c814..14bf6664 100644
> > > --- a/src/gstreamer/gstlibcamera-utils.h
> > > +++ b/src/gstreamer/gstlibcamera-utils.h
> > > @@ -2,6 +2,9 @@
> > >  /*
> > >   * Copyright (C) 2020, Collabora Ltd.
> > >   *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> > > + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
> > > + * Library       <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
> > > + * Copyright (C) <2007> David A. Schleef <ds@schleef.org>
> > 
> > Not needed.
> > 
> > >   *
> > >   * GStreamer libcamera Utility Functions
> > >   */
> > > @@ -35,6 +38,10 @@ static inline void gst_clear_event(GstEvent **event_ptr)
> > >  #if !GST_CHECK_VERSION(1, 17, 1)
> > >  gboolean gst_task_resume(GstTask *task);
> > >  #endif
> > > +
> > > +#if !GST_CHECK_VERSION(1, 22, 0)
> > > +gint gst_libcamera_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride);
> > 
> > Apply namespace comment here too.
> > 
> > > +#endif
> > >  std::shared_ptr<libcamera::CameraManager> gst_libcamera_get_camera_manager(int &ret);
> > >  
> > >  /**
> > > diff --git a/src/gstreamer/gstlibcamerapool.cpp b/src/gstreamer/gstlibcamerapool.cpp
> > > index 9cd7eccb..315f08ac 100644
> > > --- a/src/gstreamer/gstlibcamerapool.cpp
> > > +++ b/src/gstreamer/gstlibcamerapool.cpp
> > > @@ -135,16 +135,40 @@ gst_libcamera_pool_class_init(GstLibcameraPoolClass *klass)
> > >  }
> > >  
> > >  GstLibcameraPool *
> > > -gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream)
> > > +gst_libcamera_pool_new(GstLibcameraAllocator *allocator, const StreamConfiguration &stream_cfg,
> > > +                    GstCaps *caps, gboolean add_video_meta)
> > >  {
> > >       auto *pool = GST_LIBCAMERA_POOL(g_object_new(GST_TYPE_LIBCAMERA_POOL, nullptr));
> > > +     GstVideoInfo info;
> > >  
> > >       pool->allocator = GST_LIBCAMERA_ALLOCATOR(g_object_ref(allocator));
> > > -     pool->stream = stream;
> > > -
> > > -     gsize pool_size = gst_libcamera_allocator_get_pool_size(allocator, stream);
> > > +     pool->stream = stream_cfg.stream();
> > 
> > nit: Is pool->stream const ? C++ and C are pretty permissive, but storing a non-
> > const  pointer obtained from a const object is a bit of a stretch.
> > 
> > > +
> > > +     if (caps && gst_video_info_from_caps(&info, caps)) {
> > 
> > This needs some work, caps will never be null. Now, gst_video_info_from_caps()
> > will work for image/ and video/ type, in that case the format is set to ENCODED
> > and a video meta must not be appended. That includes bayer format, for which we
> > only support matching display and storage dimensions (since arbitrary cropping
> > of bayer is a bad idea).
> > 
> > In short, don't get caps, and be verbose if gst_video_info_from_caps() fails,
> > that is not expected.
> > 
> > > +             guint k, stride;
> > > +             gsize offset = 0;
> > > +             for (k = 0; k < GST_VIDEO_INFO_N_PLANES(&info); k++) {
> > > +#if !GST_CHECK_VERSION(1, 22, 0)
> > 
> > Not needed with the suggestion I just made.
> > 
> > > +                     stride = gst_libcamera_extrapolate_stride(info.finfo, k, stream_cfg.stride);
> > > +#else
> > > +                     stride = gst_video_format_info_extrapolate_stride(info.finfo, k, stream_cfg.stride);
> > > +#endif
> > > +                     info.stride[k] = stride;
> > > +                     info.offset[k] = offset;
> > > +                     offset += stride * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info.finfo, k, GST_VIDEO_INFO_HEIGHT(&info));
> > 
> > Please add a comment that this should be updated if tiled formats get added in
> > the future. For anything single plane we don't care of course.
> > 
> > > +             }
> > > +     } else
> > > +             add_video_meta = false;
> > 
> > Not sure yet why sometimes you don't want to add it.
> > 
> > > +
> > > +     gsize pool_size = gst_libcamera_allocator_get_pool_size(allocator, stream_cfg.stream());
> > >       for (gsize i = 0; i < pool_size; i++) {
> > >               GstBuffer *buffer = gst_buffer_new();
> > > +             if (add_video_meta) {
> > > +                     gst_buffer_add_video_meta_full(buffer, GST_VIDEO_FRAME_FLAG_NONE,
> > > +                                                    GST_VIDEO_INFO_FORMAT(&info), GST_VIDEO_INFO_WIDTH(&info),
> > > +                                                    GST_VIDEO_INFO_HEIGHT(&info), GST_VIDEO_INFO_N_PLANES(&info),
> > > +                                                    info.offset, info.stride);
> > > +             }
> > >               pool->queue->push_back(buffer);
> > >       }
> > >  
> > > diff --git a/src/gstreamer/gstlibcamerapool.h b/src/gstreamer/gstlibcamerapool.h
> > > index 2a7a9c77..8ad87cab 100644
> > > --- a/src/gstreamer/gstlibcamerapool.h
> > > +++ b/src/gstreamer/gstlibcamerapool.h
> > > @@ -21,7 +21,7 @@
> > >  G_DECLARE_FINAL_TYPE(GstLibcameraPool, gst_libcamera_pool, GST_LIBCAMERA, POOL, GstBufferPool)
> > >  
> > >  GstLibcameraPool *gst_libcamera_pool_new(GstLibcameraAllocator *allocator,
> > > -                                      libcamera::Stream *stream);
> > > +                                      const libcamera::StreamConfiguration &stream_cfg, GstCaps *caps, gboolean add_video_meta);
> > >  
> > >  libcamera::Stream *gst_libcamera_pool_get_stream(GstLibcameraPool *self);
> > >  
> > > diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
> > > index 8efa25f4..c05a31e7 100644
> > > --- a/src/gstreamer/gstlibcamerasrc.cpp
> > > +++ b/src/gstreamer/gstlibcamerasrc.cpp
> > > @@ -497,9 +497,21 @@ gst_libcamera_src_negotiate(GstLibcameraSrc *self)
> > >       for (gsize i = 0; i < state->srcpads_.size(); i++) {
> > >               GstPad *srcpad = state->srcpads_[i];
> > >               const StreamConfiguration &stream_cfg = state->config_->at(i);
> > > +             GstQuery *query = NULL;
> > > +             gboolean add_video_meta = false;
> > > +
> > > +             g_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg);
> > > +             gst_libcamera_framerate_to_caps(caps, element_caps);
> > > +
> > > +             query = gst_query_new_allocation(caps, false);
> > 
> > My hate for boolean parameters is strong enough that I'd like to suggest:
> > 
> >                 bool need_pool = false;
> >                 query = gst_query_new_allocation(caps, need_pool);
> 
> 	bool kieran_agrees = true;
> 	ret = send_response(kieran_agrees);
> 
> 
> > > +             if (!gst_pad_peer_query(srcpad, query))
> > > +                     GST_DEBUG_OBJECT(self, "didn't get downstream ALLOCATION hints");
> > > +             else
> > > +                     add_video_meta = gst_query_find_allocation_meta(query, GST_VIDEO_META_API_TYPE, 
> > > NULL);
> > 
> > Its not sufficient to not add video meta. If downstream does not support
> > VideoMeta and the stride does not match what you from just turning the caps into
> > VideoInfo, you must bounce these buffers to system memory. For that reason, you
> > likely want to handle the stride extrapolation here and pass VideoInfo to the
> > pool instead. If the stride miss-match, you can then create a generic VideoPool
> > using the caps, and later own you will bounce the memory using
> > gst_video_frame_copy() (which can handle stride miss-match).
> > 
> > > +             gst_query_unref(query);
> > >  
> > >               GstLibcameraPool *pool = gst_libcamera_pool_new(self->allocator,
> > > -                                                             stream_cfg.stream());
> > > +                                                             stream_cfg, caps, add_video_meta);
> > >               g_signal_connect_swapped(pool, "buffer-notify",
> > >                                        G_CALLBACK(gst_task_resume), self->task);
> > >
Qi Hou Nov. 20, 2024, 10:04 a.m. UTC | #4
Hi,

Thanks for all of your review. Suggestions are useful. I make a v4 patch. Please help review that version. I am also doing more local test.

Regards,
Qi Hou

-----Original Message-----
From: Nicolas Dufresne <nicolas@ndufresne.ca> 
Sent: 2024年11月14日 3:20
To: Qi Hou <qi.hou@nxp.com>; libcamera-devel@lists.libcamera.org
Cc: Jared Hu <jared.hu@nxp.com>; Julien Vuillaumier <julien.vuillaumier@nxp.com>
Subject: [EXT] Re: [PATCH v3] gstreamer: Add GstVideoMeta support

Caution: This is an external email. Please take care when clicking links or opening attachments. When in doubt, report the message using the 'Report this email' button


Hi,

Le mercredi 13 novembre 2024 à 16:29 +0900, Hou Qi a écrit :
> GStreamer video-info calculated stride and offset may differ from 
> those used by the camera.
>
> This patch enhances downstream plugin's support for videometa by 
> adding videometa support for libcamerasrc. It ensures that when 
> downstream plugin supports videometa by allocation query, libcamerasrc 
> also attaches videometa to buffer, preventing discrepancies between 
> the stride and offset calculated by video-info and camera.
>
> Signed-off-by: Hou Qi <qi.hou@nxp.com>
> ---
>  src/gstreamer/gstlibcamera-utils.cpp | 28 ++++++++++++++++++++++++
>  src/gstreamer/gstlibcamera-utils.h   |  7 ++++++
>  src/gstreamer/gstlibcamerapool.cpp   | 32 ++++++++++++++++++++++++----
>  src/gstreamer/gstlibcamerapool.h     |  2 +-
>  src/gstreamer/gstlibcamerasrc.cpp    | 14 +++++++++++-
>  5 files changed, 77 insertions(+), 6 deletions(-)
>
> diff --git a/src/gstreamer/gstlibcamera-utils.cpp 
> b/src/gstreamer/gstlibcamera-utils.cpp
> index 732987ef..91de1d44 100644
> --- a/src/gstreamer/gstlibcamera-utils.cpp
> +++ b/src/gstreamer/gstlibcamera-utils.cpp
> @@ -2,6 +2,9 @@
>  /*
>   * Copyright (C) 2020, Collabora Ltd.
>   *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
> + * Library       <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
> + * Copyright (C) <2007> David A. Schleef <ds@schleef.org>

I'll leave that to the upper maintainers to evaluate, but it would be nice to identify that these are only needed as long as
gst_libcamera_extrapolate_stride() copy is there. As soon as Trixie becomes stable, we'll bump to GStreamer 1.22 and remove it, would be nice to remove the spurious copyright.

>   *
>   * GStreamer libcamera Utility Function
>   */
> @@ -591,6 +594,31 @@ gst_task_resume(GstTask *task)  }  #endif
>
> +#if !GST_CHECK_VERSION(1, 22, 0)
> +gint gst_libcamera_extrapolate_stride(const GstVideoFormatInfo 
> +*finfo, gint plane, gint stride)

Because its under an ifdef, it is guarantied not to clash at built time, and the linker will always resolve it locally. Keep the mainline namespace, it will simplify the code later.

> +{
> +     gint estride;
> +     gint comp[GST_VIDEO_MAX_COMPONENTS];
> +     gint i;
> +
> +     /* there is nothing to extrapolate on first plane */
> +     if (plane == 0)
> +             return stride;
> +
> +     gst_video_format_info_component(finfo, plane, comp);
> +
> +     /* For now, all planar formats have a single component on first plane, but
> +     * if there was a planar format with more, we'd have to make a ratio of the
> +     * number of component on the first plane against the number of component on
> +     * the current plane. */
> +     estride = 0;
> +     for (i = 0; i < GST_VIDEO_MAX_COMPONENTS && comp[i] >= 0; i++)
> +             estride += GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(finfo, 
> + comp[i], stride);
> +
> +     return estride;
> +}
> +#endif
> +
>  G_LOCK_DEFINE_STATIC(cm_singleton_lock);
>  static std::weak_ptr<CameraManager> cm_singleton_ptr;
>
> diff --git a/src/gstreamer/gstlibcamera-utils.h 
> b/src/gstreamer/gstlibcamera-utils.h
> index cab1c814..14bf6664 100644
> --- a/src/gstreamer/gstlibcamera-utils.h
> +++ b/src/gstreamer/gstlibcamera-utils.h
> @@ -2,6 +2,9 @@
>  /*
>   * Copyright (C) 2020, Collabora Ltd.
>   *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
> + * Library       <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
> + * Copyright (C) <2007> David A. Schleef <ds@schleef.org>

Not needed.

>   *
>   * GStreamer libcamera Utility Functions
>   */
> @@ -35,6 +38,10 @@ static inline void gst_clear_event(GstEvent 
> **event_ptr)  #if !GST_CHECK_VERSION(1, 17, 1)  gboolean 
> gst_task_resume(GstTask *task);  #endif
> +
> +#if !GST_CHECK_VERSION(1, 22, 0)
> +gint gst_libcamera_extrapolate_stride(const GstVideoFormatInfo 
> +*finfo, gint plane, gint stride);

Apply namespace comment here too.

> +#endif
>  std::shared_ptr<libcamera::CameraManager> 
> gst_libcamera_get_camera_manager(int &ret);
>
>  /**
> diff --git a/src/gstreamer/gstlibcamerapool.cpp 
> b/src/gstreamer/gstlibcamerapool.cpp
> index 9cd7eccb..315f08ac 100644
> --- a/src/gstreamer/gstlibcamerapool.cpp
> +++ b/src/gstreamer/gstlibcamerapool.cpp
> @@ -135,16 +135,40 @@ 
> gst_libcamera_pool_class_init(GstLibcameraPoolClass *klass)  }
>
>  GstLibcameraPool *
> -gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream 
> *stream)
> +gst_libcamera_pool_new(GstLibcameraAllocator *allocator, const StreamConfiguration &stream_cfg,
> +                    GstCaps *caps, gboolean add_video_meta)
>  {
>       auto *pool = 
> GST_LIBCAMERA_POOL(g_object_new(GST_TYPE_LIBCAMERA_POOL, nullptr));
> +     GstVideoInfo info;
>
>       pool->allocator = GST_LIBCAMERA_ALLOCATOR(g_object_ref(allocator));
> -     pool->stream = stream;
> -
> -     gsize pool_size = gst_libcamera_allocator_get_pool_size(allocator, stream);
> +     pool->stream = stream_cfg.stream();

nit: Is pool->stream const ? C++ and C are pretty permissive, but storing a non- const  pointer obtained from a const object is a bit of a stretch.

> +
> +     if (caps && gst_video_info_from_caps(&info, caps)) {

This needs some work, caps will never be null. Now, gst_video_info_from_caps() will work for image/ and video/ type, in that case the format is set to ENCODED and a video meta must not be appended. That includes bayer format, for which we only support matching display and storage dimensions (since arbitrary cropping of bayer is a bad idea).

In short, don't get caps, and be verbose if gst_video_info_from_caps() fails, that is not expected.

> +             guint k, stride;
> +             gsize offset = 0;
> +             for (k = 0; k < GST_VIDEO_INFO_N_PLANES(&info); k++) { 
> +#if !GST_CHECK_VERSION(1, 22, 0)

Not needed with the suggestion I just made.

> +                     stride = 
> +gst_libcamera_extrapolate_stride(info.finfo, k, stream_cfg.stride); #else
> +                     stride = 
> +gst_video_format_info_extrapolate_stride(info.finfo, k, stream_cfg.stride); #endif
> +                     info.stride[k] = stride;
> +                     info.offset[k] = offset;
> +                     offset += stride * 
> +GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info.finfo, k, 
> +GST_VIDEO_INFO_HEIGHT(&info));

Please add a comment that this should be updated if tiled formats get added in the future. For anything single plane we don't care of course.

> +             }
> +     } else
> +             add_video_meta = false;

Not sure yet why sometimes you don't want to add it.

> +
> +     gsize pool_size = 
> + gst_libcamera_allocator_get_pool_size(allocator, 
> + stream_cfg.stream());
>       for (gsize i = 0; i < pool_size; i++) {
>               GstBuffer *buffer = gst_buffer_new();
> +             if (add_video_meta) {
> +                     gst_buffer_add_video_meta_full(buffer, GST_VIDEO_FRAME_FLAG_NONE,
> +                                                    GST_VIDEO_INFO_FORMAT(&info), GST_VIDEO_INFO_WIDTH(&info),
> +                                                    GST_VIDEO_INFO_HEIGHT(&info), GST_VIDEO_INFO_N_PLANES(&info),
> +                                                    info.offset, info.stride);
> +             }
>               pool->queue->push_back(buffer);
>       }
>
> diff --git a/src/gstreamer/gstlibcamerapool.h 
> b/src/gstreamer/gstlibcamerapool.h
> index 2a7a9c77..8ad87cab 100644
> --- a/src/gstreamer/gstlibcamerapool.h
> +++ b/src/gstreamer/gstlibcamerapool.h
> @@ -21,7 +21,7 @@
>  G_DECLARE_FINAL_TYPE(GstLibcameraPool, gst_libcamera_pool, 
> GST_LIBCAMERA, POOL, GstBufferPool)
>
>  GstLibcameraPool *gst_libcamera_pool_new(GstLibcameraAllocator *allocator,
> -                                      libcamera::Stream *stream);
> +                                      const 
> + libcamera::StreamConfiguration &stream_cfg, GstCaps *caps, gboolean 
> + add_video_meta);
>
>  libcamera::Stream *gst_libcamera_pool_get_stream(GstLibcameraPool 
> *self);
>
> diff --git a/src/gstreamer/gstlibcamerasrc.cpp 
> b/src/gstreamer/gstlibcamerasrc.cpp
> index 8efa25f4..c05a31e7 100644
> --- a/src/gstreamer/gstlibcamerasrc.cpp
> +++ b/src/gstreamer/gstlibcamerasrc.cpp
> @@ -497,9 +497,21 @@ gst_libcamera_src_negotiate(GstLibcameraSrc *self)
>       for (gsize i = 0; i < state->srcpads_.size(); i++) {
>               GstPad *srcpad = state->srcpads_[i];
>               const StreamConfiguration &stream_cfg = 
> state->config_->at(i);
> +             GstQuery *query = NULL;
> +             gboolean add_video_meta = false;
> +
> +             g_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg);
> +             gst_libcamera_framerate_to_caps(caps, element_caps);
> +
> +             query = gst_query_new_allocation(caps, false);

My hate for boolean parameters is strong enough that I'd like to suggest:

                bool need_pool = false;
                query = gst_query_new_allocation(caps, need_pool);

> +             if (!gst_pad_peer_query(srcpad, query))
> +                     GST_DEBUG_OBJECT(self, "didn't get downstream ALLOCATION hints");
> +             else
> +                     add_video_meta = 
> + gst_query_find_allocation_meta(query, GST_VIDEO_META_API_TYPE,
> NULL);

Its not sufficient to not add video meta. If downstream does not support VideoMeta and the stride does not match what you from just turning the caps into VideoInfo, you must bounce these buffers to system memory. For that reason, you likely want to handle the stride extrapolation here and pass VideoInfo to the pool instead. If the stride miss-match, you can then create a generic VideoPool using the caps, and later own you will bounce the memory using
gst_video_frame_copy() (which can handle stride miss-match).

> +             gst_query_unref(query);
>
>               GstLibcameraPool *pool = gst_libcamera_pool_new(self->allocator,
> -                                                             stream_cfg.stream());
> +                                                             
> + stream_cfg, caps, add_video_meta);
>               g_signal_connect_swapped(pool, "buffer-notify",
>                                        G_CALLBACK(gst_task_resume), 
> self->task);
>

Patch
diff mbox series

diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp
index 732987ef..91de1d44 100644
--- a/src/gstreamer/gstlibcamera-utils.cpp
+++ b/src/gstreamer/gstlibcamera-utils.cpp
@@ -2,6 +2,9 @@ 
 /*
  * Copyright (C) 2020, Collabora Ltd.
  *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Library       <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Copyright (C) <2007> David A. Schleef <ds@schleef.org>
  *
  * GStreamer libcamera Utility Function
  */
@@ -591,6 +594,31 @@  gst_task_resume(GstTask *task)
 }
 #endif
 
+#if !GST_CHECK_VERSION(1, 22, 0)
+gint gst_libcamera_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride)
+{
+	gint estride;
+	gint comp[GST_VIDEO_MAX_COMPONENTS];
+	gint i;
+
+	/* there is nothing to extrapolate on first plane */
+	if (plane == 0)
+		return stride;
+
+	gst_video_format_info_component(finfo, plane, comp);
+
+	/* For now, all planar formats have a single component on first plane, but
+	* if there was a planar format with more, we'd have to make a ratio of the
+	* number of component on the first plane against the number of component on
+	* the current plane. */
+	estride = 0;
+	for (i = 0; i < GST_VIDEO_MAX_COMPONENTS && comp[i] >= 0; i++)
+		estride += GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(finfo, comp[i], stride);
+
+	return estride;
+}
+#endif
+
 G_LOCK_DEFINE_STATIC(cm_singleton_lock);
 static std::weak_ptr<CameraManager> cm_singleton_ptr;
 
diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h
index cab1c814..14bf6664 100644
--- a/src/gstreamer/gstlibcamera-utils.h
+++ b/src/gstreamer/gstlibcamera-utils.h
@@ -2,6 +2,9 @@ 
 /*
  * Copyright (C) 2020, Collabora Ltd.
  *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Library       <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Copyright (C) <2007> David A. Schleef <ds@schleef.org>
  *
  * GStreamer libcamera Utility Functions
  */
@@ -35,6 +38,10 @@  static inline void gst_clear_event(GstEvent **event_ptr)
 #if !GST_CHECK_VERSION(1, 17, 1)
 gboolean gst_task_resume(GstTask *task);
 #endif
+
+#if !GST_CHECK_VERSION(1, 22, 0)
+gint gst_libcamera_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride);
+#endif
 std::shared_ptr<libcamera::CameraManager> gst_libcamera_get_camera_manager(int &ret);
 
 /**
diff --git a/src/gstreamer/gstlibcamerapool.cpp b/src/gstreamer/gstlibcamerapool.cpp
index 9cd7eccb..315f08ac 100644
--- a/src/gstreamer/gstlibcamerapool.cpp
+++ b/src/gstreamer/gstlibcamerapool.cpp
@@ -135,16 +135,40 @@  gst_libcamera_pool_class_init(GstLibcameraPoolClass *klass)
 }
 
 GstLibcameraPool *
-gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream)
+gst_libcamera_pool_new(GstLibcameraAllocator *allocator, const StreamConfiguration &stream_cfg,
+		       GstCaps *caps, gboolean add_video_meta)
 {
 	auto *pool = GST_LIBCAMERA_POOL(g_object_new(GST_TYPE_LIBCAMERA_POOL, nullptr));
+	GstVideoInfo info;
 
 	pool->allocator = GST_LIBCAMERA_ALLOCATOR(g_object_ref(allocator));
-	pool->stream = stream;
-
-	gsize pool_size = gst_libcamera_allocator_get_pool_size(allocator, stream);
+	pool->stream = stream_cfg.stream();
+
+	if (caps && gst_video_info_from_caps(&info, caps)) {
+		guint k, stride;
+		gsize offset = 0;
+		for (k = 0; k < GST_VIDEO_INFO_N_PLANES(&info); k++) {
+#if !GST_CHECK_VERSION(1, 22, 0)
+			stride = gst_libcamera_extrapolate_stride(info.finfo, k, stream_cfg.stride);
+#else
+			stride = gst_video_format_info_extrapolate_stride(info.finfo, k, stream_cfg.stride);
+#endif
+			info.stride[k] = stride;
+			info.offset[k] = offset;
+			offset += stride * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info.finfo, k, GST_VIDEO_INFO_HEIGHT(&info));
+		}
+	} else
+		add_video_meta = false;
+
+	gsize pool_size = gst_libcamera_allocator_get_pool_size(allocator, stream_cfg.stream());
 	for (gsize i = 0; i < pool_size; i++) {
 		GstBuffer *buffer = gst_buffer_new();
+		if (add_video_meta) {
+			gst_buffer_add_video_meta_full(buffer, GST_VIDEO_FRAME_FLAG_NONE,
+						       GST_VIDEO_INFO_FORMAT(&info), GST_VIDEO_INFO_WIDTH(&info),
+						       GST_VIDEO_INFO_HEIGHT(&info), GST_VIDEO_INFO_N_PLANES(&info),
+						       info.offset, info.stride);
+		}
 		pool->queue->push_back(buffer);
 	}
 
diff --git a/src/gstreamer/gstlibcamerapool.h b/src/gstreamer/gstlibcamerapool.h
index 2a7a9c77..8ad87cab 100644
--- a/src/gstreamer/gstlibcamerapool.h
+++ b/src/gstreamer/gstlibcamerapool.h
@@ -21,7 +21,7 @@ 
 G_DECLARE_FINAL_TYPE(GstLibcameraPool, gst_libcamera_pool, GST_LIBCAMERA, POOL, GstBufferPool)
 
 GstLibcameraPool *gst_libcamera_pool_new(GstLibcameraAllocator *allocator,
-					 libcamera::Stream *stream);
+					 const libcamera::StreamConfiguration &stream_cfg, GstCaps *caps, gboolean add_video_meta);
 
 libcamera::Stream *gst_libcamera_pool_get_stream(GstLibcameraPool *self);
 
diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
index 8efa25f4..c05a31e7 100644
--- a/src/gstreamer/gstlibcamerasrc.cpp
+++ b/src/gstreamer/gstlibcamerasrc.cpp
@@ -497,9 +497,21 @@  gst_libcamera_src_negotiate(GstLibcameraSrc *self)
 	for (gsize i = 0; i < state->srcpads_.size(); i++) {
 		GstPad *srcpad = state->srcpads_[i];
 		const StreamConfiguration &stream_cfg = state->config_->at(i);
+		GstQuery *query = NULL;
+		gboolean add_video_meta = false;
+
+		g_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg);
+		gst_libcamera_framerate_to_caps(caps, element_caps);
+
+		query = gst_query_new_allocation(caps, false);
+		if (!gst_pad_peer_query(srcpad, query))
+			GST_DEBUG_OBJECT(self, "didn't get downstream ALLOCATION hints");
+		else
+			add_video_meta = gst_query_find_allocation_meta(query, GST_VIDEO_META_API_TYPE, NULL);
+		gst_query_unref(query);
 
 		GstLibcameraPool *pool = gst_libcamera_pool_new(self->allocator,
-								stream_cfg.stream());
+								stream_cfg, caps, add_video_meta);
 		g_signal_connect_swapped(pool, "buffer-notify",
 					 G_CALLBACK(gst_task_resume), self->task);