summaryrefslogtreecommitdiff
path: root/src/gstreamer/gstlibcamerasrc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gstreamer/gstlibcamerasrc.cpp')
-rw-r--r--src/gstreamer/gstlibcamerasrc.cpp260
1 files changed, 217 insertions, 43 deletions
diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
index f015c6d2..b34f0897 100644
--- a/src/gstreamer/gstlibcamerasrc.cpp
+++ b/src/gstreamer/gstlibcamerasrc.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2019, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamerasrc.cpp - GStreamer Capture Element
+ * GStreamer Capture Element
*/
/**
@@ -37,10 +37,11 @@
#include <gst/base/base.h>
+#include "gstlibcamera-controls.h"
+#include "gstlibcamera-utils.h"
#include "gstlibcameraallocator.h"
#include "gstlibcamerapad.h"
#include "gstlibcamerapool.h"
-#include "gstlibcamera-utils.h"
using namespace libcamera;
@@ -128,6 +129,7 @@ struct GstLibcameraSrcState {
ControlList initControls_;
guint group_id_;
+ GstCameraControls controls_;
int queueRequest();
void requestCompleted(Request *request);
@@ -142,7 +144,6 @@ struct _GstLibcameraSrc {
GstTask *task;
gchar *camera_name;
- controls::AfModeEnum auto_focus_mode = controls::AfModeManual;
std::atomic<GstEvent *> pending_eos;
@@ -154,10 +155,15 @@ struct _GstLibcameraSrc {
enum {
PROP_0,
PROP_CAMERA_NAME,
- PROP_AUTO_FOCUS_MODE,
+ PROP_LAST
};
+static void gst_libcamera_src_child_proxy_init(gpointer g_iface,
+ gpointer iface_data);
+
G_DEFINE_TYPE_WITH_CODE(GstLibcameraSrc, gst_libcamera_src, GST_TYPE_ELEMENT,
+ G_IMPLEMENT_INTERFACE(GST_TYPE_CHILD_PROXY,
+ gst_libcamera_src_child_proxy_init)
GST_DEBUG_CATEGORY_INIT(source_debug, "libcamerasrc", 0,
"libcamera Source"))
@@ -180,6 +186,9 @@ int GstLibcameraSrcState::queueRequest()
if (!request)
return -ENOMEM;
+ /* Apply controls */
+ controls_.applyControls(request);
+
std::unique_ptr<RequestWrap> wrap =
std::make_unique<RequestWrap>(std::move(request));
@@ -223,6 +232,9 @@ GstLibcameraSrcState::requestCompleted(Request *request)
{
GLibLocker locker(&lock_);
+
+ controls_.readMetadata(request);
+
wrap = std::move(queuedRequests_.front());
queuedRequests_.pop();
}
@@ -256,6 +268,55 @@ GstLibcameraSrcState::requestCompleted(Request *request)
gst_task_resume(src_->task);
}
+static void
+gst_libcamera_extrapolate_info(GstVideoInfo *info, guint32 stride)
+{
+ guint i, estride;
+ gsize offset = 0;
+
+ /* This should be updated if tiled formats get added in the future. */
+ for (i = 0; i < GST_VIDEO_INFO_N_PLANES(info); i++) {
+ estride = gst_video_format_info_extrapolate_stride(info->finfo, i, stride);
+ info->stride[i] = estride;
+ info->offset[i] = offset;
+ offset += estride * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info->finfo, i,
+ GST_VIDEO_INFO_HEIGHT(info));
+ }
+}
+
+static GstFlowReturn
+gst_libcamera_video_frame_copy(GstBuffer *src, GstBuffer *dest, const GstVideoInfo *dest_info, guint32 stride)
+{
+ GstVideoInfo src_info = *dest_info;
+ GstVideoFrame src_frame, dest_frame;
+
+ gst_libcamera_extrapolate_info(&src_info, stride);
+ src_info.size = gst_buffer_get_size(src);
+
+ if (!gst_video_frame_map(&src_frame, &src_info, src, GST_MAP_READ)) {
+ GST_ERROR("Could not map src buffer");
+ return GST_FLOW_ERROR;
+ }
+
+ if (!gst_video_frame_map(&dest_frame, const_cast<GstVideoInfo *>(dest_info), dest, GST_MAP_WRITE)) {
+ GST_ERROR("Could not map dest buffer");
+ gst_video_frame_unmap(&src_frame);
+ return GST_FLOW_ERROR;
+ }
+
+ if (!gst_video_frame_copy(&dest_frame, &src_frame)) {
+ GST_ERROR("Could not copy frame");
+ gst_video_frame_unmap(&src_frame);
+ gst_video_frame_unmap(&dest_frame);
+ return GST_FLOW_ERROR;
+ }
+
+ gst_video_frame_unmap(&src_frame);
+ gst_video_frame_unmap(&dest_frame);
+
+ return GST_FLOW_OK;
+}
+
/* Must be called with stream_lock held. */
int GstLibcameraSrcState::processRequest()
{
@@ -280,11 +341,41 @@ int GstLibcameraSrcState::processRequest()
GstFlowReturn ret = GST_FLOW_OK;
gst_flow_combiner_reset(src_->flow_combiner);
- for (GstPad *srcpad : srcpads_) {
+ for (gsize i = 0; i < srcpads_.size(); i++) {
+ GstPad *srcpad = srcpads_[i];
Stream *stream = gst_libcamera_pad_get_stream(srcpad);
GstBuffer *buffer = wrap->detachBuffer(stream);
FrameBuffer *fb = gst_libcamera_buffer_get_frame_buffer(buffer);
+ const StreamConfiguration &stream_cfg = config_->at(i);
+ GstBufferPool *video_pool = gst_libcamera_pad_get_video_pool(srcpad);
+
+ if (video_pool) {
+ /* Only set video pool when a copy is needed. */
+ GstBuffer *copy = NULL;
+ const GstVideoInfo info = gst_libcamera_pad_get_video_info(srcpad);
+
+ ret = gst_buffer_pool_acquire_buffer(video_pool, &copy, NULL);
+ if (ret != GST_FLOW_OK) {
+ gst_buffer_unref(buffer);
+ GST_ELEMENT_ERROR(src_, RESOURCE, SETTINGS,
+ ("Failed to acquire buffer"),
+ ("GstLibcameraSrcState::processRequest() failed: %s", g_strerror(-ret)));
+ return -EPIPE;
+ }
+
+ ret = gst_libcamera_video_frame_copy(buffer, copy, &info, stream_cfg.stride);
+ gst_buffer_unref(buffer);
+ if (ret != GST_FLOW_OK) {
+ gst_buffer_unref(copy);
+ GST_ELEMENT_ERROR(src_, RESOURCE, SETTINGS,
+ ("Failed to copy buffer"),
+ ("GstLibcameraSrcState::processRequest() failed: %s", g_strerror(-ret)));
+ return -EPIPE;
+ }
+
+ buffer = copy;
+ }
if (GST_CLOCK_TIME_IS_VALID(wrap->pts_)) {
GST_BUFFER_PTS(buffer) = wrap->pts_;
@@ -377,21 +468,22 @@ gst_libcamera_src_open(GstLibcameraSrc *self)
}
if (camera_name) {
- cam = cm->get(self->camera_name);
+ cam = cm->get(camera_name);
if (!cam) {
GST_ELEMENT_ERROR(self, RESOURCE, NOT_FOUND,
- ("Could not find a camera named '%s'.", self->camera_name),
+ ("Could not find a camera named '%s'.", camera_name),
("libcamera::CameraMananger::get() returned nullptr"));
return false;
}
} else {
- if (cm->cameras().empty()) {
+ auto cameras = cm->cameras();
+ if (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];
+ cam = cameras[0];
}
GST_INFO_OBJECT(self, "Using camera '%s'", cam->id().c_str());
@@ -404,6 +496,8 @@ gst_libcamera_src_open(GstLibcameraSrc *self)
return false;
}
+ self->state->controls_.setCamera(cam);
+
cam->requestCompleted.connect(self->state, &GstLibcameraSrcState::requestCompleted);
/* No need to lock here, we didn't start our threads yet. */
@@ -418,6 +512,8 @@ static bool
gst_libcamera_src_negotiate(GstLibcameraSrc *self)
{
GstLibcameraSrcState *state = self->state;
+ std::vector<GstVideoTransferFunction> transfer(state->srcpads_.size(),
+ GST_VIDEO_TRANSFER_UNKNOWN);
g_autoptr(GstStructure) element_caps = gst_structure_new_empty("caps");
@@ -433,7 +529,7 @@ gst_libcamera_src_negotiate(GstLibcameraSrc *self)
/* Fixate caps and configure the stream. */
caps = gst_caps_make_writable(caps);
- gst_libcamera_configure_stream_from_caps(stream_cfg, caps);
+ gst_libcamera_configure_stream_from_caps(stream_cfg, caps, &transfer[i]);
gst_libcamera_get_framerate_from_caps(caps, element_caps);
}
@@ -461,7 +557,7 @@ gst_libcamera_src_negotiate(GstLibcameraSrc *self)
GstPad *srcpad = state->srcpads_[i];
const StreamConfiguration &stream_cfg = state->config_->at(i);
- g_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg);
+ g_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg, transfer[i]);
gst_libcamera_framerate_to_caps(caps, element_caps);
if (!gst_pad_push_event(srcpad, gst_event_new_caps(caps)))
@@ -482,13 +578,70 @@ 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);
+ GstBufferPool *video_pool = NULL;
+ GstVideoInfo info;
+
+ g_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg, transfer[i]);
+
+ gst_video_info_from_caps(&info, caps);
+ gst_libcamera_pad_set_video_info(srcpad, &info);
+
+ /* Stride mismatch between camera stride and that calculated by video-info. */
+ if (static_cast<unsigned int>(info.stride[0]) != stream_cfg.stride &&
+ GST_VIDEO_INFO_FORMAT(&info) != GST_VIDEO_FORMAT_ENCODED) {
+ GstQuery *query = NULL;
+ const gboolean need_pool = true;
+ gboolean has_video_meta = false;
+
+ gst_libcamera_extrapolate_info(&info, stream_cfg.stride);
+
+ 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
+ has_video_meta = gst_query_find_allocation_meta(query, GST_VIDEO_META_API_TYPE, NULL);
+
+ if (!has_video_meta) {
+ GstBufferPool *pool = NULL;
+
+ if (gst_query_get_n_allocation_pools(query) > 0)
+ gst_query_parse_nth_allocation_pool(query, 0, &pool, NULL, NULL, NULL);
+
+ if (pool)
+ video_pool = pool;
+ else {
+ GstStructure *config;
+ guint min_buffers = 3;
+ video_pool = gst_video_buffer_pool_new();
+
+ config = gst_buffer_pool_get_config(video_pool);
+ gst_buffer_pool_config_set_params(config, caps, info.size, min_buffers, 0);
+
+ GST_DEBUG_OBJECT(self, "Own pool config is %" GST_PTR_FORMAT, config);
+
+ gst_buffer_pool_set_config(GST_BUFFER_POOL_CAST(video_pool), config);
+ }
+
+ GST_WARNING_OBJECT(self, "Downstream doesn't support video meta, need to copy frame.");
+
+ if (!gst_buffer_pool_set_active(video_pool, true)) {
+ gst_caps_unref(caps);
+ GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,
+ ("Failed to active buffer pool"),
+ ("gst_libcamera_src_negotiate() failed."));
+ return false;
+ }
+ }
+ gst_query_unref(query);
+ }
GstLibcameraPool *pool = gst_libcamera_pool_new(self->allocator,
- stream_cfg.stream());
+ stream_cfg.stream(), &info);
g_signal_connect_swapped(pool, "buffer-notify",
G_CALLBACK(gst_task_resume), self->task);
gst_libcamera_pad_set_pool(srcpad, pool);
+ gst_libcamera_pad_set_video_pool(srcpad, video_pool);
/* Clear all reconfigure flags. */
gst_pad_check_reconfigure(srcpad);
@@ -657,18 +810,6 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,
gst_pad_push_event(srcpad, gst_event_new_segment(&segment));
}
- if (self->auto_focus_mode != controls::AfModeManual) {
- const ControlInfoMap &infoMap = state->cam_->controls();
- if (infoMap.find(&controls::AfMode) != infoMap.end()) {
- state->initControls_.set(controls::AfMode, self->auto_focus_mode);
- } else {
- GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,
- ("Failed to enable auto focus"),
- ("AfMode not supported by this camera, "
- "please retry with 'auto-focus-mode=AfModeManual'"));
- }
- }
-
ret = state->cam_->start(&state->initControls_);
if (ret) {
GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,
@@ -730,17 +871,16 @@ gst_libcamera_src_set_property(GObject *object, guint prop_id,
{
GLibLocker lock(GST_OBJECT(object));
GstLibcameraSrc *self = GST_LIBCAMERA_SRC(object);
+ GstLibcameraSrcState *state = self->state;
switch (prop_id) {
case PROP_CAMERA_NAME:
g_free(self->camera_name);
self->camera_name = g_value_dup_string(value);
break;
- case PROP_AUTO_FOCUS_MODE:
- self->auto_focus_mode = static_cast<controls::AfModeEnum>(g_value_get_enum(value));
- break;
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ if (!state->controls_.setProperty(prop_id - PROP_LAST, value, pspec))
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
@@ -751,16 +891,15 @@ gst_libcamera_src_get_property(GObject *object, guint prop_id, GValue *value,
{
GLibLocker lock(GST_OBJECT(object));
GstLibcameraSrc *self = GST_LIBCAMERA_SRC(object);
+ GstLibcameraSrcState *state = self->state;
switch (prop_id) {
case PROP_CAMERA_NAME:
g_value_set_string(value, self->camera_name);
break;
- case PROP_AUTO_FOCUS_MODE:
- g_value_set_enum(value, static_cast<gint>(self->auto_focus_mode));
- break;
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ if (!state->controls_.getProperty(prop_id - PROP_LAST, value, pspec))
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
@@ -864,8 +1003,10 @@ gst_libcamera_src_init(GstLibcameraSrc *self)
g_mutex_init(&state->lock_);
- state->srcpads_.push_back(gst_pad_new_from_template(templ, "src"));
- gst_element_add_pad(GST_ELEMENT(self), state->srcpads_.back());
+ GstPad *pad = gst_pad_new_from_template(templ, "src");
+ state->srcpads_.push_back(pad);
+ gst_element_add_pad(GST_ELEMENT(self), pad);
+ gst_child_proxy_child_added(GST_CHILD_PROXY(self), G_OBJECT(pad), GST_OBJECT_NAME(pad));
GST_OBJECT_FLAG_SET(self, GST_ELEMENT_FLAG_SOURCE);
@@ -896,6 +1037,8 @@ gst_libcamera_src_request_new_pad(GstElement *element, GstPadTemplate *templ,
return NULL;
}
+ gst_child_proxy_child_added(GST_CHILD_PROXY(self), G_OBJECT(pad), GST_OBJECT_NAME(pad));
+
return reinterpret_cast<GstPad *>(g_steal_pointer(&pad));
}
@@ -904,6 +1047,8 @@ gst_libcamera_src_release_pad(GstElement *element, GstPad *pad)
{
GstLibcameraSrc *self = GST_LIBCAMERA_SRC(element);
+ gst_child_proxy_child_removed(GST_CHILD_PROXY(self), G_OBJECT(pad), GST_OBJECT_NAME(pad));
+
GST_DEBUG_OBJECT(self, "Pad %" GST_PTR_FORMAT " being released", pad);
{
@@ -913,6 +1058,12 @@ gst_libcamera_src_release_pad(GstElement *element, GstPad *pad)
auto end_iterator = pads.end();
auto pad_iterator = std::find(begin_iterator, end_iterator, pad);
+ GstBufferPool *video_pool = gst_libcamera_pad_get_video_pool(pad);
+ if (video_pool) {
+ gst_buffer_pool_set_active(video_pool, false);
+ gst_object_unref(video_pool);
+ }
+
if (pad_iterator != end_iterator) {
g_object_unref(*pad_iterator);
pads.erase(pad_iterator);
@@ -939,7 +1090,7 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)
gst_element_class_set_metadata(element_class,
"libcamera Source", "Source/Video",
"Linux Camera source using libcamera",
- "Nicolas Dufresne <nicolas.dufresne@collabora.com");
+ "Nicolas Dufresne <nicolas.dufresne@collabora.com>");
gst_element_class_add_static_pad_template_with_gtype(element_class,
&src_template,
GST_TYPE_LIBCAMERA_PAD);
@@ -955,12 +1106,35 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)
| G_PARAM_STATIC_STRINGS));
g_object_class_install_property(object_class, PROP_CAMERA_NAME, spec);
- spec = g_param_spec_enum("auto-focus-mode",
- "Set auto-focus mode",
- "Available options: AfModeManual, "
- "AfModeAuto or AfModeContinuous.",
- gst_libcamera_auto_focus_get_type(),
- static_cast<gint>(controls::AfModeManual),
- G_PARAM_WRITABLE);
- g_object_class_install_property(object_class, PROP_AUTO_FOCUS_MODE, spec);
+ GstCameraControls::installProperties(object_class, PROP_LAST);
+}
+
+/* GstChildProxy implementation */
+static GObject *
+gst_libcamera_src_child_proxy_get_child_by_index(GstChildProxy *child_proxy,
+ guint index)
+{
+ GLibLocker lock(GST_OBJECT(child_proxy));
+ GObject *obj = nullptr;
+
+ obj = reinterpret_cast<GObject *>(g_list_nth_data(GST_ELEMENT(child_proxy)->srcpads, index));
+ if (obj)
+ gst_object_ref(obj);
+
+ return obj;
+}
+
+static guint
+gst_libcamera_src_child_proxy_get_children_count(GstChildProxy *child_proxy)
+{
+ GLibLocker lock(GST_OBJECT(child_proxy));
+ return GST_ELEMENT_CAST(child_proxy)->numsrcpads;
+}
+
+static void
+gst_libcamera_src_child_proxy_init(gpointer g_iface, [[maybe_unused]] gpointer iface_data)
+{
+ GstChildProxyInterface *iface = reinterpret_cast<GstChildProxyInterface *>(g_iface);
+ iface->get_child_by_index = gst_libcamera_src_child_proxy_get_child_by_index;
+ iface->get_children_count = gst_libcamera_src_child_proxy_get_children_count;
}