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.cpp557
1 files changed, 411 insertions, 146 deletions
diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
index 63d99571..b34f0897 100644
--- a/src/gstreamer/gstlibcamerasrc.cpp
+++ b/src/gstreamer/gstlibcamerasrc.cpp
@@ -3,16 +3,14 @@
* Copyright (C) 2019, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamerasrc.cpp - GStreamer Capture Element
+ * GStreamer Capture Element
*/
/**
* \todo The following is a list of items that needs implementation in the GStreamer plugin
* - Implement GstElement::send_event
- * + Allowing application to send EOS
* + Allowing application to use FLUSH/FLUSH_STOP
* + Prevent the main thread from accessing streaming thread
- * - Implement renegotiation (even if slow)
* - Implement GstElement::request-new-pad (multi stream)
* + Evaluate if a single streaming thread is fine
* - Add application driven request (snapshot)
@@ -29,6 +27,7 @@
#include "gstlibcamerasrc.h"
+#include <atomic>
#include <queue>
#include <vector>
@@ -38,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;
@@ -129,10 +129,12 @@ struct GstLibcameraSrcState {
ControlList initControls_;
guint group_id_;
+ GstCameraControls controls_;
int queueRequest();
void requestCompleted(Request *request);
int processRequest();
+ void clearRequests();
};
struct _GstLibcameraSrc {
@@ -142,7 +144,8 @@ struct _GstLibcameraSrc {
GstTask *task;
gchar *camera_name;
- controls::AfModeEnum auto_focus_mode = controls::AfModeManual;
+
+ std::atomic<GstEvent *> pending_eos;
GstLibcameraSrcState *state;
GstLibcameraAllocator *allocator;
@@ -152,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"))
@@ -178,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));
@@ -221,6 +232,9 @@ GstLibcameraSrcState::requestCompleted(Request *request)
{
GLibLocker locker(&lock_);
+
+ controls_.readMetadata(request);
+
wrap = std::move(queuedRequests_.front());
queuedRequests_.pop();
}
@@ -254,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()
{
@@ -278,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_;
@@ -299,23 +392,57 @@ int GstLibcameraSrcState::processRequest()
srcpad, ret);
}
- if (ret != GST_FLOW_OK) {
- if (ret == GST_FLOW_EOS) {
- g_autoptr(GstEvent) eos = gst_event_new_eos();
- guint32 seqnum = gst_util_seqnum_next();
- gst_event_set_seqnum(eos, seqnum);
- for (GstPad *srcpad : srcpads_)
- gst_pad_push_event(srcpad, gst_event_ref(eos));
- } else if (ret != GST_FLOW_FLUSHING) {
- GST_ELEMENT_FLOW_ERROR(src_, ret);
+ switch (ret) {
+ case GST_FLOW_OK:
+ break;
+
+ case GST_FLOW_NOT_NEGOTIATED: {
+ bool reconfigure = false;
+ for (GstPad *srcpad : srcpads_) {
+ if (gst_pad_needs_reconfigure(srcpad)) {
+ reconfigure = true;
+ break;
+ }
}
- return -EPIPE;
+ /* If no pads need a reconfiguration something went wrong. */
+ if (!reconfigure)
+ err = -EPIPE;
+
+ break;
+ }
+
+ case GST_FLOW_EOS: {
+ g_autoptr(GstEvent) eos = gst_event_new_eos();
+ guint32 seqnum = gst_util_seqnum_next();
+ gst_event_set_seqnum(eos, seqnum);
+ for (GstPad *srcpad : srcpads_)
+ gst_pad_push_event(srcpad, gst_event_ref(eos));
+
+ err = -EPIPE;
+ break;
+ }
+
+ case GST_FLOW_FLUSHING:
+ err = -EPIPE;
+ break;
+
+ default:
+ GST_ELEMENT_FLOW_ERROR(src_, ret);
+
+ err = -EPIPE;
+ break;
}
return err;
}
+void GstLibcameraSrcState::clearRequests()
+{
+ GLibLocker locker(&lock_);
+ completedRequests_ = {};
+}
+
static bool
gst_libcamera_src_open(GstLibcameraSrc *self)
{
@@ -341,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());
@@ -368,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. */
@@ -377,6 +507,149 @@ gst_libcamera_src_open(GstLibcameraSrc *self)
return true;
}
+/* Must be called with stream_lock held. */
+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");
+
+ for (gsize i = 0; i < state->srcpads_.size(); i++) {
+ GstPad *srcpad = state->srcpads_[i];
+ StreamConfiguration &stream_cfg = state->config_->at(i);
+
+ /* Retrieve the supported caps. */
+ g_autoptr(GstCaps) filter = gst_libcamera_stream_formats_to_caps(stream_cfg.formats());
+ g_autoptr(GstCaps) caps = gst_pad_peer_query_caps(srcpad, filter);
+ if (gst_caps_is_empty(caps))
+ return false;
+
+ /* Fixate caps and configure the stream. */
+ caps = gst_caps_make_writable(caps);
+ gst_libcamera_configure_stream_from_caps(stream_cfg, caps, &transfer[i]);
+ gst_libcamera_get_framerate_from_caps(caps, element_caps);
+ }
+
+ /* Validate the configuration. */
+ if (state->config_->validate() == CameraConfiguration::Invalid)
+ return false;
+
+ int ret = state->cam_->configure(state->config_.get());
+ if (ret) {
+ GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,
+ ("Failed to configure camera: %s", g_strerror(-ret)),
+ ("Camera::configure() failed with error code %i", ret));
+ return false;
+ }
+
+ /* Check frame duration bounds within controls::FrameDurationLimits */
+ gst_libcamera_clamp_and_set_frameduration(state->initControls_,
+ state->cam_->controls(), element_caps);
+
+ /*
+ * Regardless if it has been modified, create clean caps and push the
+ * caps event. Downstream will decide if the caps are acceptable.
+ */
+ for (gsize i = 0; i < state->srcpads_.size(); i++) {
+ 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, transfer[i]);
+ gst_libcamera_framerate_to_caps(caps, element_caps);
+
+ if (!gst_pad_push_event(srcpad, gst_event_new_caps(caps)))
+ return false;
+ }
+
+ if (self->allocator)
+ g_clear_object(&self->allocator);
+
+ self->allocator = gst_libcamera_allocator_new(state->cam_, state->config_.get());
+ if (!self->allocator) {
+ GST_ELEMENT_ERROR(self, RESOURCE, NO_SPACE_LEFT,
+ ("Failed to allocate memory"),
+ ("gst_libcamera_allocator_new() failed."));
+ return false;
+ }
+
+ 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(), &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);
+ }
+
+ return true;
+}
+
static void
gst_libcamera_src_task_run(gpointer user_data)
{
@@ -397,6 +670,39 @@ gst_libcamera_src_task_run(gpointer user_data)
bool doResume = false;
+ g_autoptr(GstEvent) event = self->pending_eos.exchange(nullptr);
+ if (event) {
+ for (GstPad *srcpad : state->srcpads_)
+ gst_pad_push_event(srcpad, gst_event_ref(event));
+
+ return;
+ }
+
+ /* Check if a srcpad requested a renegotiation. */
+ bool reconfigure = false;
+ for (GstPad *srcpad : state->srcpads_) {
+ if (gst_pad_check_reconfigure(srcpad)) {
+ /* Check if the caps even need changing. */
+ g_autoptr(GstCaps) caps = gst_pad_get_current_caps(srcpad);
+ if (!gst_pad_peer_query_accept_caps(srcpad, caps)) {
+ reconfigure = true;
+ break;
+ }
+ }
+ }
+
+ if (reconfigure) {
+ state->cam_->stop();
+ state->clearRequests();
+
+ if (!gst_libcamera_src_negotiate(self)) {
+ GST_ELEMENT_FLOW_ERROR(self, GST_FLOW_NOT_NEGOTIATED);
+ gst_task_stop(self->task);
+ }
+
+ state->cam_->start(&state->initControls_);
+ }
+
/*
* Create and queue one request. If no buffers are available the
* function returns -ENOBUFS, which we ignore here as that's not a
@@ -458,11 +764,8 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,
GstLibcameraSrc *self = GST_LIBCAMERA_SRC(user_data);
GLibRecLocker lock(&self->stream_lock);
GstLibcameraSrcState *state = self->state;
- GstFlowReturn flow_ret = GST_FLOW_OK;
gint ret;
- g_autoptr(GstStructure) element_caps = gst_structure_new_empty("caps");
-
GST_DEBUG_OBJECT(self, "Streaming thread has started");
gint stream_id_num = 0;
@@ -490,62 +793,16 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,
}
g_assert(state->config_->size() == state->srcpads_.size());
- for (gsize i = 0; i < state->srcpads_.size(); i++) {
- GstPad *srcpad = state->srcpads_[i];
- StreamConfiguration &stream_cfg = state->config_->at(i);
-
- /* Retrieve the supported caps. */
- g_autoptr(GstCaps) filter = gst_libcamera_stream_formats_to_caps(stream_cfg.formats());
- g_autoptr(GstCaps) caps = gst_pad_peer_query_caps(srcpad, filter);
- if (gst_caps_is_empty(caps)) {
- flow_ret = GST_FLOW_NOT_NEGOTIATED;
- break;
- }
-
- /* Fixate caps and configure the stream. */
- caps = gst_caps_make_writable(caps);
- gst_libcamera_configure_stream_from_caps(stream_cfg, caps);
- gst_libcamera_get_framerate_from_caps(caps, element_caps);
- }
-
- if (flow_ret != GST_FLOW_OK)
- goto done;
-
- /* Validate the configuration. */
- if (state->config_->validate() == CameraConfiguration::Invalid) {
- flow_ret = GST_FLOW_NOT_NEGOTIATED;
- goto done;
- }
-
- ret = state->cam_->configure(state->config_.get());
- if (ret) {
- GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,
- ("Failed to configure camera: %s", g_strerror(-ret)),
- ("Camera::configure() failed with error code %i", ret));
+ if (!gst_libcamera_src_negotiate(self)) {
+ state->initControls_.clear();
+ GST_ELEMENT_FLOW_ERROR(self, GST_FLOW_NOT_NEGOTIATED);
gst_task_stop(task);
- flow_ret = GST_FLOW_NOT_NEGOTIATED;
- goto done;
+ return;
}
- /* Check frame duration bounds within controls::FrameDurationLimits */
- gst_libcamera_clamp_and_set_frameduration(state->initControls_,
- state->cam_->controls(), element_caps);
-
- /*
- * Regardless if it has been modified, create clean caps and push the
- * caps event. Downstream will decide if the caps are acceptable.
- */
- for (gsize i = 0; i < state->srcpads_.size(); i++) {
- 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);
- gst_libcamera_framerate_to_caps(caps, element_caps);
-
- if (!gst_pad_push_event(srcpad, gst_event_new_caps(caps))) {
- flow_ret = GST_FLOW_NOT_NEGOTIATED;
- break;
- }
+ self->flow_combiner = gst_flow_combiner_new();
+ for (GstPad *srcpad : state->srcpads_) {
+ gst_flow_combiner_add_pad(self->flow_combiner, srcpad);
/* Send an open segment event with time format. */
GstSegment segment;
@@ -553,40 +810,6 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,
gst_pad_push_event(srcpad, gst_event_new_segment(&segment));
}
- self->allocator = gst_libcamera_allocator_new(state->cam_, state->config_.get());
- if (!self->allocator) {
- GST_ELEMENT_ERROR(self, RESOURCE, NO_SPACE_LEFT,
- ("Failed to allocate memory"),
- ("gst_libcamera_allocator_new() failed."));
- gst_task_stop(task);
- return;
- }
-
- self->flow_combiner = gst_flow_combiner_new();
- for (gsize i = 0; i < state->srcpads_.size(); i++) {
- GstPad *srcpad = state->srcpads_[i];
- const StreamConfiguration &stream_cfg = state->config_->at(i);
- GstLibcameraPool *pool = gst_libcamera_pool_new(self->allocator,
- stream_cfg.stream());
- g_signal_connect_swapped(pool, "buffer-notify",
- G_CALLBACK(gst_task_resume), task);
-
- gst_libcamera_pad_set_pool(srcpad, pool);
- gst_flow_combiner_add_pad(self->flow_combiner, srcpad);
- }
-
- 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,
@@ -595,17 +818,6 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,
gst_task_stop(task);
return;
}
-
-done:
- state->initControls_.clear();
- switch (flow_ret) {
- case GST_FLOW_NOT_NEGOTIATED:
- GST_ELEMENT_FLOW_ERROR(self, flow_ret);
- gst_task_stop(task);
- break;
- default:
- break;
- }
}
static void
@@ -619,11 +831,7 @@ gst_libcamera_src_task_leave([[maybe_unused]] GstTask *task,
GST_DEBUG_OBJECT(self, "Streaming thread is about to stop");
state->cam_->stop();
-
- {
- GLibLocker locker(&state->lock_);
- state->completedRequests_ = {};
- }
+ state->clearRequests();
{
GLibRecLocker locker(&self->stream_lock);
@@ -663,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;
}
}
@@ -684,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;
}
}
@@ -747,6 +953,27 @@ gst_libcamera_src_change_state(GstElement *element, GstStateChange transition)
return ret;
}
+static gboolean
+gst_libcamera_src_send_event(GstElement *element, GstEvent *event)
+{
+ GstLibcameraSrc *self = GST_LIBCAMERA_SRC(element);
+ gboolean ret = FALSE;
+
+ switch (GST_EVENT_TYPE(event)) {
+ case GST_EVENT_EOS: {
+ GstEvent *oldEvent = self->pending_eos.exchange(event);
+ gst_clear_event(&oldEvent);
+ ret = TRUE;
+ break;
+ }
+ default:
+ gst_event_unref(event);
+ break;
+ }
+
+ return ret;
+}
+
static void
gst_libcamera_src_finalize(GObject *object)
{
@@ -776,8 +1003,12 @@ 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);
/* C-style friend. */
state->src_ = self;
@@ -806,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));
}
@@ -814,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);
{
@@ -823,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);
@@ -844,11 +1085,12 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)
element_class->request_new_pad = gst_libcamera_src_request_new_pad;
element_class->release_pad = gst_libcamera_src_release_pad;
element_class->change_state = gst_libcamera_src_change_state;
+ element_class->send_event = gst_libcamera_src_send_event;
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);
@@ -864,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;
}