Files
external_libcamera/src/gstreamer/gstlibcamerapad.cpp
Hou Qi 4a277906a4 gstreamer: Fix libcamerasrc responding latency before setting caps
Whenever a downstream element queries latency, libcamerasrc will always reply,
even though it has not yet determined the latency.

However some downstream elements (e.g. glvideomixer/aggregator) will query the
latency before libcamerasrc sets the caps. When these elements get the latency,
they will start the caps negotiation. Since libcamerasrc has not yet determined
caps, invalid negotiation is performed and workflow is disrupted.

So, set latency to 'GST_CLOCK_TIME_NONE' during initialization, and reply to the
query after libcamerasrc confirms the latency. At this time, libcamerasrc has also
completed caps negotiation and downstream elements work fine.

In addition, every time the src pad task stops, we reset the latency to
GST_CLOCK_TIME_NONE to ensure that when next time task starts, the downstream
elements can generate out buffers after receiving the effective latency.

Signed-off-by: Hou Qi <qi.hou@nxp.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2025-06-19 01:50:38 +01:00

210 lines
4.6 KiB
C++

/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2019, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
* GStreamer Capture Pad
*/
#include "gstlibcamerapad.h"
#include <libcamera/stream.h>
#include "gstlibcamera-utils.h"
using namespace libcamera;
struct _GstLibcameraPad {
GstPad parent;
StreamRole role;
GstLibcameraPool *pool;
GstBufferPool *video_pool;
GstVideoInfo info;
GstClockTime latency;
};
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);
GLibLocker lock(GST_OBJECT(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);
GLibLocker lock(GST_OBJECT(self));
switch (prop_id) {
case PROP_STREAM_ROLE:
g_value_set_enum(value, static_cast<gint>(self->role));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static gboolean
gst_libcamera_pad_query(GstPad *pad, GstObject *parent, GstQuery *query)
{
auto *self = GST_LIBCAMERA_PAD(pad);
if (query->type != GST_QUERY_LATENCY)
return gst_pad_query_default(pad, parent, query);
GLibLocker lock(GST_OBJECT(self));
if (self->latency == GST_CLOCK_TIME_NONE)
return FALSE;
/* TRUE here means live, we assumes that max latency is the same as min
* as we have no idea that duration of frames. */
gst_query_set_latency(query, TRUE, self->latency, self->latency);
return TRUE;
}
static void
gst_libcamera_pad_init(GstLibcameraPad *self)
{
self->latency = GST_CLOCK_TIME_NONE;
GST_PAD_QUERYFUNC(self) = gst_libcamera_pad_query;
}
static GType
gst_libcamera_stream_role_get_type()
{
static GType type = 0;
static const GEnumValue values[] = {
{
static_cast<gint>(StreamRole::StillCapture),
"libcamera::StillCapture",
"still-capture",
}, {
static_cast<gint>(StreamRole::VideoRecording),
"libcamera::VideoRecording",
"video-recording",
}, {
static_cast<gint>(StreamRole::Viewfinder),
"libcamera::Viewfinder",
"view-finder",
},
{ 0, nullptr, nullptr }
};
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(),
static_cast<gint>(StreamRole::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);
}
StreamRole
gst_libcamera_pad_get_role(GstPad *pad)
{
auto *self = GST_LIBCAMERA_PAD(pad);
GLibLocker lock(GST_OBJECT(self));
return self->role;
}
GstLibcameraPool *
gst_libcamera_pad_get_pool(GstPad *pad)
{
auto *self = GST_LIBCAMERA_PAD(pad);
return self->pool;
}
void
gst_libcamera_pad_set_pool(GstPad *pad, GstLibcameraPool *pool)
{
auto *self = GST_LIBCAMERA_PAD(pad);
if (self->pool)
g_object_unref(self->pool);
self->pool = pool;
}
GstBufferPool *
gst_libcamera_pad_get_video_pool(GstPad *pad)
{
auto *self = GST_LIBCAMERA_PAD(pad);
return self->video_pool;
}
void gst_libcamera_pad_set_video_pool(GstPad *pad, GstBufferPool *video_pool)
{
auto *self = GST_LIBCAMERA_PAD(pad);
if (self->video_pool)
g_object_unref(self->video_pool);
self->video_pool = video_pool;
}
GstVideoInfo gst_libcamera_pad_get_video_info(GstPad *pad)
{
auto *self = GST_LIBCAMERA_PAD(pad);
return self->info;
}
void gst_libcamera_pad_set_video_info(GstPad *pad, const GstVideoInfo *info)
{
auto *self = GST_LIBCAMERA_PAD(pad);
self->info = *info;
}
Stream *
gst_libcamera_pad_get_stream(GstPad *pad)
{
auto *self = GST_LIBCAMERA_PAD(pad);
if (self->pool)
return gst_libcamera_pool_get_stream(self->pool);
return nullptr;
}
void
gst_libcamera_pad_set_latency(GstPad *pad, GstClockTime latency)
{
auto *self = GST_LIBCAMERA_PAD(pad);
GLibLocker lock(GST_OBJECT(self));
self->latency = latency;
}