summaryrefslogtreecommitdiff
path: root/src/gstreamer
diff options
context:
space:
mode:
Diffstat (limited to 'src/gstreamer')
-rw-r--r--src/gstreamer/gstlibcamera-controls.cpp.in327
-rw-r--r--src/gstreamer/gstlibcamera-controls.h43
-rw-r--r--src/gstreamer/gstlibcamera-utils.cpp172
-rw-r--r--src/gstreamer/gstlibcamera-utils.h12
-rw-r--r--src/gstreamer/gstlibcamera.cpp2
-rw-r--r--src/gstreamer/gstlibcameraallocator.cpp26
-rw-r--r--src/gstreamer/gstlibcameraallocator.h2
-rw-r--r--src/gstreamer/gstlibcamerapad.cpp33
-rw-r--r--src/gstreamer/gstlibcamerapad.h10
-rw-r--r--src/gstreamer/gstlibcamerapool.cpp58
-rw-r--r--src/gstreamer/gstlibcamerapool.h5
-rw-r--r--src/gstreamer/gstlibcameraprovider.cpp17
-rw-r--r--src/gstreamer/gstlibcameraprovider.h2
-rw-r--r--src/gstreamer/gstlibcamerasrc.cpp260
-rw-r--r--src/gstreamer/gstlibcamerasrc.h33
-rw-r--r--src/gstreamer/meson.build11
16 files changed, 836 insertions, 177 deletions
diff --git a/src/gstreamer/gstlibcamera-controls.cpp.in b/src/gstreamer/gstlibcamera-controls.cpp.in
new file mode 100644
index 00000000..89c530da
--- /dev/null
+++ b/src/gstreamer/gstlibcamera-controls.cpp.in
@@ -0,0 +1,327 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Jaslo Ziska
+ *
+ * GStreamer Camera Controls
+ *
+ * This file is auto-generated. Do not edit.
+ */
+
+#include <vector>
+
+#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
+#include <libcamera/geometry.h>
+
+#include "gstlibcamera-controls.h"
+
+using namespace libcamera;
+
+static void value_set_rectangle(GValue *value, const Rectangle &rect)
+{
+ Point top_left = rect.topLeft();
+ Size size = rect.size();
+
+ GValue x = G_VALUE_INIT;
+ g_value_init(&x, G_TYPE_INT);
+ g_value_set_int(&x, top_left.x);
+ gst_value_array_append_and_take_value(value, &x);
+
+ GValue y = G_VALUE_INIT;
+ g_value_init(&y, G_TYPE_INT);
+ g_value_set_int(&y, top_left.y);
+ gst_value_array_append_and_take_value(value, &y);
+
+ GValue width = G_VALUE_INIT;
+ g_value_init(&width, G_TYPE_INT);
+ g_value_set_int(&width, size.width);
+ gst_value_array_append_and_take_value(value, &width);
+
+ GValue height = G_VALUE_INIT;
+ g_value_init(&height, G_TYPE_INT);
+ g_value_set_int(&height, size.height);
+ gst_value_array_append_and_take_value(value, &height);
+}
+
+static Rectangle value_get_rectangle(const GValue *value)
+{
+ const GValue *r;
+ r = gst_value_array_get_value(value, 0);
+ int x = g_value_get_int(r);
+ r = gst_value_array_get_value(value, 1);
+ int y = g_value_get_int(r);
+ r = gst_value_array_get_value(value, 2);
+ int w = g_value_get_int(r);
+ r = gst_value_array_get_value(value, 3);
+ int h = g_value_get_int(r);
+
+ return Rectangle(x, y, w, h);
+}
+
+{% for vendor, ctrls in controls %}
+{%- for ctrl in ctrls if ctrl.is_enum %}
+static const GEnumValue {{ ctrl.name|snake_case }}_types[] = {
+{%- for enum in ctrl.enum_values %}
+ {
+ controls::{{ ctrl.namespace }}{{ enum.name }},
+ {{ enum.description|format_description|indent_str('\t\t') }},
+ "{{ enum.gst_name }}"
+ },
+{%- endfor %}
+ {0, NULL, NULL}
+};
+
+#define TYPE_{{ ctrl.name|snake_case|upper }} \
+ ({{ ctrl.name|snake_case }}_get_type())
+static GType {{ ctrl.name|snake_case }}_get_type()
+{
+ static GType {{ ctrl.name|snake_case }}_type = 0;
+
+ if (!{{ ctrl.name|snake_case }}_type)
+ {{ ctrl.name|snake_case }}_type =
+ g_enum_register_static("{{ ctrl.name }}",
+ {{ ctrl.name|snake_case }}_types);
+
+ return {{ ctrl.name|snake_case }}_type;
+}
+{% endfor %}
+{%- endfor %}
+
+void GstCameraControls::installProperties(GObjectClass *klass, int lastPropId)
+{
+{%- for vendor, ctrls in controls %}
+{%- for ctrl in ctrls %}
+
+{%- set spec %}
+{%- if ctrl.is_rectangle -%}
+gst_param_spec_array(
+{%- else -%}
+g_param_spec_{{ ctrl.gtype }}(
+{%- endif -%}
+{%- if ctrl.is_array %}
+ "{{ ctrl.vendor_prefix }}{{ ctrl.name|kebab_case }}-value",
+ "{{ ctrl.name }} Value",
+ "One {{ ctrl.name }} element value",
+{%- else %}
+ "{{ ctrl.vendor_prefix }}{{ ctrl.name|kebab_case }}",
+ "{{ ctrl.name }}",
+ {{ ctrl.description|format_description|indent_str('\t') }},
+{%- endif %}
+{%- if ctrl.is_enum %}
+ TYPE_{{ ctrl.name|snake_case|upper }},
+ {{ ctrl.default }},
+{%- elif ctrl.is_rectangle %}
+ g_param_spec_int(
+ "rectangle-value",
+ "Rectangle Value",
+ "One rectangle value, either x, y, width or height.",
+ {{ ctrl.min }}, {{ ctrl.max }}, {{ ctrl.default }},
+ (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS)
+ ),
+{%- elif ctrl.gtype == 'boolean' %}
+ {{ ctrl.default }},
+{%- elif ctrl.gtype in ['float', 'int', 'int64', 'uchar'] %}
+ {{ ctrl.min }}, {{ ctrl.max }}, {{ ctrl.default }},
+{%- endif %}
+ (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS)
+)
+{%- endset %}
+
+ g_object_class_install_property(
+ klass,
+ lastPropId + controls::{{ ctrl.namespace }}{{ ctrl.name|snake_case|upper }},
+{%- if ctrl.is_array %}
+ gst_param_spec_array(
+ "{{ ctrl.vendor_prefix }}{{ ctrl.name|kebab_case }}",
+ "{{ ctrl.name }}",
+ {{ ctrl.description|format_description|indent_str('\t\t\t') }},
+ {{ spec|indent_str('\t\t\t') }},
+ (GParamFlags) (GST_PARAM_CONTROLLABLE |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS)
+ )
+{%- else %}
+ {{ spec|indent_str('\t\t') }}
+{%- endif %}
+ );
+{%- endfor %}
+{%- endfor %}
+}
+
+bool GstCameraControls::getProperty(guint propId, GValue *value,
+ [[maybe_unused]] GParamSpec *pspec)
+{
+ if (!controls_acc_.contains(propId)) {
+ GST_WARNING("Control '%s' is not available, default value will "
+ "be returned",
+ controls::controls.at(propId)->name().c_str());
+ return true;
+ }
+ const ControlValue &cv = controls_acc_.get(propId);
+
+ switch (propId) {
+{%- for vendor, ctrls in controls %}
+{%- for ctrl in ctrls %}
+
+ case controls::{{ ctrl.namespace }}{{ ctrl.name|snake_case|upper }}: {
+ auto control = cv.get<{{ ctrl.type }}>();
+
+{%- if ctrl.is_array %}
+ for (size_t i = 0; i < control.size(); ++i) {
+ GValue element = G_VALUE_INIT;
+{%- if ctrl.is_rectangle %}
+ g_value_init(&element, GST_TYPE_PARAM_ARRAY_LIST);
+ value_set_rectangle(&element, control[i]);
+{%- else %}
+ g_value_init(&element, G_TYPE_{{ ctrl.gtype|upper }});
+ g_value_set_{{ ctrl.gtype }}(&element, control[i]);
+{%- endif %}
+ gst_value_array_append_and_take_value(value, &element);
+ }
+{%- else %}
+{%- if ctrl.is_rectangle %}
+ value_set_rectangle(value, control);
+{%- else %}
+ g_value_set_{{ ctrl.gtype }}(value, control);
+{%- endif %}
+{%- endif %}
+
+ return true;
+ }
+{%- endfor %}
+{%- endfor %}
+
+ default:
+ return false;
+ }
+}
+
+bool GstCameraControls::setProperty(guint propId, const GValue *value,
+ [[maybe_unused]] GParamSpec *pspec)
+{
+ /*
+ * Check whether the camera capabilities are already available.
+ * They might not be available if the pipeline has not started yet.
+ */
+ if (!capabilities_.empty()) {
+ /* If so, check that the control is supported by the camera. */
+ const ControlId *cid = capabilities_.idmap().at(propId);
+ auto info = capabilities_.find(cid);
+
+ if (info == capabilities_.end()) {
+ GST_WARNING("Control '%s' is not supported by the "
+ "camera and will be ignored",
+ cid->name().c_str());
+ return true;
+ }
+ }
+
+ switch (propId) {
+{%- for vendor, ctrls in controls %}
+{%- for ctrl in ctrls %}
+
+ case controls::{{ ctrl.namespace }}{{ ctrl.name|snake_case|upper }}: {
+{%- if ctrl.is_array %}
+ size_t size = gst_value_array_get_size(value);
+{%- if ctrl.size != 0 %}
+ if (size != {{ ctrl.size }}) {
+ GST_ERROR("Incorrect array size for control "
+ "'{{ ctrl.name|kebab_case }}', must be of "
+ "size {{ ctrl.size }}");
+ return true;
+ }
+{%- endif %}
+
+ std::vector<{{ ctrl.element_type }}> values(size);
+ for (size_t i = 0; i < size; ++i) {
+ const GValue *element =
+ gst_value_array_get_value(value, i);
+{%- if ctrl.is_rectangle %}
+ if (gst_value_array_get_size(element) != 4) {
+ GST_ERROR("Rectangle in control "
+ "'{{ ctrl.name|kebab_case }}' at"
+ "index %zu must be an array of size 4",
+ i);
+ return true;
+ }
+ values[i] = value_get_rectangle(element);
+{%- else %}
+ values[i] = g_value_get_{{ ctrl.gtype }}(element);
+{%- endif %}
+ }
+
+{%- if ctrl.size == 0 %}
+ Span<const {{ ctrl.element_type }}> val(values.data(), size);
+{%- else %}
+ Span<const {{ ctrl.element_type }}, {{ ctrl.size }}> val(values.data(), size);
+{%- endif %}
+{%- else %}
+{%- if ctrl.is_rectangle %}
+ if (gst_value_array_get_size(value) != 4) {
+ GST_ERROR("Rectangle in control "
+ "'{{ ctrl.name|kebab_case }}' must be an "
+ "array of size 4");
+ return true;
+ }
+ Rectangle val = value_get_rectangle(value);
+{%- else %}
+ auto val = g_value_get_{{ ctrl.gtype }}(value);
+{%- endif %}
+{%- endif %}
+ controls_.set(controls::{{ ctrl.namespace }}{{ ctrl.name }}, val);
+ controls_acc_.set(controls::{{ ctrl.namespace }}{{ ctrl.name }}, val);
+ return true;
+ }
+{%- endfor %}
+{%- endfor %}
+
+ default:
+ return false;
+ }
+}
+
+void GstCameraControls::setCamera(const std::shared_ptr<libcamera::Camera> &cam)
+{
+ capabilities_ = cam->controls();
+
+ /*
+ * Check the controls which were set before the camera capabilities were
+ * known. This is required because GStreamer may set properties before
+ * the pipeline has started and thus before the camera was known.
+ */
+ ControlList new_controls;
+ for (auto control = controls_acc_.begin();
+ control != controls_acc_.end();
+ ++control) {
+ unsigned int id = control->first;
+ ControlValue value = control->second;
+
+ const ControlId *cid = capabilities_.idmap().at(id);
+ auto info = capabilities_.find(cid);
+
+ /* Only add controls which are supported. */
+ if (info != capabilities_.end())
+ new_controls.set(id, value);
+ else
+ GST_WARNING("Control '%s' is not supported by the "
+ "camera and will be ignored",
+ cid->name().c_str());
+ }
+
+ controls_acc_ = new_controls;
+ controls_ = new_controls;
+}
+
+void GstCameraControls::applyControls(std::unique_ptr<libcamera::Request> &request)
+{
+ request->controls().merge(controls_);
+ controls_.clear();
+}
+
+void GstCameraControls::readMetadata(libcamera::Request *request)
+{
+ controls_acc_.merge(request->metadata(),
+ ControlList::MergePolicy::OverwriteExisting);
+}
diff --git a/src/gstreamer/gstlibcamera-controls.h b/src/gstreamer/gstlibcamera-controls.h
new file mode 100644
index 00000000..749220b5
--- /dev/null
+++ b/src/gstreamer/gstlibcamera-controls.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Collabora Ltd.
+ * Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
+ *
+ * GStreamer Camera Controls
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <libcamera/camera.h>
+#include <libcamera/controls.h>
+#include <libcamera/request.h>
+
+#include "gstlibcamerasrc.h"
+
+namespace libcamera {
+
+class GstCameraControls
+{
+public:
+ static void installProperties(GObjectClass *klass, int lastProp);
+
+ bool getProperty(guint propId, GValue *value, GParamSpec *pspec);
+ bool setProperty(guint propId, const GValue *value, GParamSpec *pspec);
+
+ void setCamera(const std::shared_ptr<libcamera::Camera> &cam);
+
+ void applyControls(std::unique_ptr<libcamera::Request> &request);
+ void readMetadata(libcamera::Request *request);
+
+private:
+ /* Supported controls and limits of camera. */
+ ControlInfoMap capabilities_;
+ /* Set of user modified controls. */
+ ControlList controls_;
+ /* Accumulator of all controls ever set and metadata returned by camera */
+ ControlList controls_acc_;
+};
+
+} /* namespace libcamera */
diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp
index 469ac810..a548b0c1 100644
--- a/src/gstreamer/gstlibcamera-utils.cpp
+++ b/src/gstreamer/gstlibcamera-utils.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamera-utils.c - GStreamer libcamera Utility Function
+ * GStreamer libcamera Utility Function
*/
#include "gstlibcamera-utils.h"
@@ -74,6 +74,7 @@ static struct {
{ GST_VIDEO_FORMAT_I420, formats::YUV420 },
{ GST_VIDEO_FORMAT_YV12, formats::YVU420 },
{ GST_VIDEO_FORMAT_Y42B, formats::YUV422 },
+ { GST_VIDEO_FORMAT_Y444, formats::YUV444 },
/* YUV Packed */
{ GST_VIDEO_FORMAT_UYVY, formats::UYVY },
@@ -85,7 +86,7 @@ static struct {
};
static GstVideoColorimetry
-colorimetry_from_colorspace(const ColorSpace &colorSpace)
+colorimetry_from_colorspace(const ColorSpace &colorSpace, GstVideoTransferFunction transfer)
{
GstVideoColorimetry colorimetry;
@@ -113,6 +114,8 @@ colorimetry_from_colorspace(const ColorSpace &colorSpace)
break;
case ColorSpace::TransferFunction::Rec709:
colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
+ if (transfer != GST_VIDEO_TRANSFER_UNKNOWN)
+ colorimetry.transfer = transfer;
break;
}
@@ -144,7 +147,8 @@ colorimetry_from_colorspace(const ColorSpace &colorSpace)
}
static std::optional<ColorSpace>
-colorspace_from_colorimetry(const GstVideoColorimetry &colorimetry)
+colorspace_from_colorimetry(const GstVideoColorimetry &colorimetry,
+ GstVideoTransferFunction *transfer)
{
std::optional<ColorSpace> colorspace = ColorSpace::Raw;
@@ -188,6 +192,7 @@ colorspace_from_colorimetry(const GstVideoColorimetry &colorimetry)
case GST_VIDEO_TRANSFER_BT2020_12:
case GST_VIDEO_TRANSFER_BT709:
colorspace->transferFunction = ColorSpace::TransferFunction::Rec709;
+ *transfer = colorimetry.transfer;
break;
default:
GST_WARNING("Colorimetry transfer function %d not mapped in gstlibcamera",
@@ -254,52 +259,50 @@ gst_format_to_pixel_format(GstVideoFormat gst_format)
return PixelFormat{};
}
+static const struct {
+ PixelFormat format;
+ const gchar *name;
+} bayer_map[]{
+ { formats::SBGGR8, "bggr" },
+ { formats::SGBRG8, "gbrg" },
+ { formats::SGRBG8, "grbg" },
+ { formats::SRGGB8, "rggb" },
+ { formats::SBGGR10, "bggr10le" },
+ { formats::SGBRG10, "gbrg10le" },
+ { formats::SGRBG10, "grbg10le" },
+ { formats::SRGGB10, "rggb10le" },
+ { formats::SBGGR12, "bggr12le" },
+ { formats::SGBRG12, "gbrg12le" },
+ { formats::SGRBG12, "grbg12le" },
+ { formats::SRGGB12, "rggb12le" },
+ { formats::SBGGR14, "bggr14le" },
+ { formats::SGBRG14, "gbrg14le" },
+ { formats::SGRBG14, "grbg14le" },
+ { formats::SRGGB14, "rggb14le" },
+ { formats::SBGGR16, "bggr16le" },
+ { formats::SGBRG16, "gbrg16le" },
+ { formats::SGRBG16, "grbg16le" },
+ { formats::SRGGB16, "rggb16le" },
+};
+
static const gchar *
-bayer_format_to_string(int format)
+bayer_format_to_string(PixelFormat format)
{
- switch (format) {
- case formats::SBGGR8:
- return "bggr";
- case formats::SGBRG8:
- return "gbrg";
- case formats::SGRBG8:
- return "grbg";
- case formats::SRGGB8:
- return "rggb";
- case formats::SBGGR10:
- return "bggr10le";
- case formats::SGBRG10:
- return "gbrg10le";
- case formats::SGRBG10:
- return "grbg10le";
- case formats::SRGGB10:
- return "rggb10le";
- case formats::SBGGR12:
- return "bggr12le";
- case formats::SGBRG12:
- return "gbrg12le";
- case formats::SGRBG12:
- return "grbg12le";
- case formats::SRGGB12:
- return "rggb12le";
- case formats::SBGGR14:
- return "bggr14le";
- case formats::SGBRG14:
- return "gbrg14le";
- case formats::SGRBG14:
- return "grbg14le";
- case formats::SRGGB14:
- return "rggb14le";
- case formats::SBGGR16:
- return "bggr16le";
- case formats::SGBRG16:
- return "gbrg16le";
- case formats::SGRBG16:
- return "grbg16le";
- case formats::SRGGB16:
- return "rggb16le";
+ for (auto &b : bayer_map) {
+ if (b.format == format)
+ return b.name;
}
- return NULL;
+ return nullptr;
+}
+
+static PixelFormat
+bayer_format_from_string(const gchar *name)
+{
+ for (auto &b : bayer_map) {
+ if (strcmp(b.name, name) == 0)
+ return b.format;
+ }
+ return PixelFormat{};
}
static GstStructure *
@@ -359,13 +362,21 @@ gst_libcamera_stream_formats_to_caps(const StreamFormats &formats)
GValue val = G_VALUE_INIT;
g_value_init(&val, GST_TYPE_INT_RANGE);
- gst_value_set_int_range_step(&val, range.min.width, range.max.width, range.hStep);
- gst_structure_set_value(s, "width", &val);
- gst_value_set_int_range_step(&val, range.min.height, range.max.height, range.vStep);
- gst_structure_set_value(s, "height", &val);
+ if (range.min.width == range.max.width) {
+ gst_structure_set(s, "width", G_TYPE_INT, range.min.width, nullptr);
+ } else {
+ gst_value_set_int_range_step(&val, range.min.width, range.max.width, range.hStep);
+ gst_structure_set_value(s, "width", &val);
+ }
+ if (range.min.height == range.max.height) {
+ gst_structure_set(s, "height", G_TYPE_INT, range.min.height, nullptr);
+ } else {
+ gst_value_set_int_range_step(&val, range.min.height, range.max.height, range.vStep);
+ gst_structure_set_value(s, "height", &val);
+ }
g_value_unset(&val);
- gst_caps_append_structure(caps, s);
+ caps = gst_caps_merge_structure(caps, s);
}
}
@@ -373,7 +384,8 @@ gst_libcamera_stream_formats_to_caps(const StreamFormats &formats)
}
GstCaps *
-gst_libcamera_stream_configuration_to_caps(const StreamConfiguration &stream_cfg)
+gst_libcamera_stream_configuration_to_caps(const StreamConfiguration &stream_cfg,
+ GstVideoTransferFunction transfer)
{
GstCaps *caps = gst_caps_new_empty();
GstStructure *s = bare_structure_from_format(stream_cfg.pixelFormat);
@@ -384,8 +396,8 @@ gst_libcamera_stream_configuration_to_caps(const StreamConfiguration &stream_cfg
nullptr);
if (stream_cfg.colorSpace) {
- GstVideoColorimetry colorimetry = colorimetry_from_colorspace(stream_cfg.colorSpace.value());
- gchar *colorimetry_str = gst_video_colorimetry_to_string(&colorimetry);
+ GstVideoColorimetry colorimetry = colorimetry_from_colorspace(stream_cfg.colorSpace.value(), transfer);
+ g_autofree gchar *colorimetry_str = gst_video_colorimetry_to_string(&colorimetry);
if (colorimetry_str)
gst_structure_set(s, "colorimetry", G_TYPE_STRING, colorimetry_str, nullptr);
@@ -399,9 +411,8 @@ gst_libcamera_stream_configuration_to_caps(const StreamConfiguration &stream_cfg
return caps;
}
-void
-gst_libcamera_configure_stream_from_caps(StreamConfiguration &stream_cfg,
- GstCaps *caps)
+void gst_libcamera_configure_stream_from_caps(StreamConfiguration &stream_cfg,
+ GstCaps *caps, GstVideoTransferFunction *transfer)
{
GstVideoFormat gst_format = pixel_format_to_gst_format(stream_cfg.pixelFormat);
guint i;
@@ -466,6 +477,9 @@ gst_libcamera_configure_stream_from_caps(StreamConfiguration &stream_cfg,
const gchar *format = gst_structure_get_string(s, "format");
gst_format = gst_video_format_from_string(format);
stream_cfg.pixelFormat = gst_format_to_pixel_format(gst_format);
+ } else if (gst_structure_has_name(s, "video/x-bayer")) {
+ const gchar *format = gst_structure_get_string(s, "format");
+ stream_cfg.pixelFormat = bayer_format_from_string(format);
} else if (gst_structure_has_name(s, "image/jpeg")) {
stream_cfg.pixelFormat = formats::MJPEG;
} else {
@@ -480,13 +494,16 @@ gst_libcamera_configure_stream_from_caps(StreamConfiguration &stream_cfg,
/* Configure colorimetry */
if (gst_structure_has_field(s, "colorimetry")) {
- const gchar *colorimetry_str = gst_structure_get_string(s, "colorimetry");
+ const gchar *colorimetry_str;
GstVideoColorimetry colorimetry;
+ gst_structure_fixate_field(s, "colorimetry");
+ colorimetry_str = gst_structure_get_string(s, "colorimetry");
+
if (!gst_video_colorimetry_from_string(&colorimetry, colorimetry_str))
g_critical("Invalid colorimetry %s", colorimetry_str);
- stream_cfg.colorSpace = colorspace_from_colorimetry(colorimetry);
+ stream_cfg.colorSpace = colorspace_from_colorimetry(colorimetry, transfer);
}
}
@@ -582,6 +599,43 @@ gst_task_resume(GstTask *task)
}
#endif
+#if !GST_CHECK_VERSION(1, 22, 0)
+/*
+ * 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>
+ */
+/*
+ * This function has been imported directly from the gstreamer project to
+ * support backwards compatibility and should be removed when the older version
+ * is no longer supported.
+ */
+gint gst_video_format_info_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 6adeab0e..5f4e8a0f 100644
--- a/src/gstreamer/gstlibcamera-utils.h
+++ b/src/gstreamer/gstlibcamera-utils.h
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamera-utils.h - GStreamer libcamera Utility Functions
+ * GStreamer libcamera Utility Functions
*/
#pragma once
@@ -16,9 +16,10 @@
#include <gst/video/video.h>
GstCaps *gst_libcamera_stream_formats_to_caps(const libcamera::StreamFormats &formats);
-GstCaps *gst_libcamera_stream_configuration_to_caps(const libcamera::StreamConfiguration &stream_cfg);
+GstCaps *gst_libcamera_stream_configuration_to_caps(const libcamera::StreamConfiguration &stream_cfg,
+ GstVideoTransferFunction transfer);
void gst_libcamera_configure_stream_from_caps(libcamera::StreamConfiguration &stream_cfg,
- GstCaps *caps);
+ GstCaps *caps, GstVideoTransferFunction *transfer);
void gst_libcamera_get_framerate_from_caps(GstCaps *caps, GstStructure *element_caps);
void gst_libcamera_clamp_and_set_frameduration(libcamera::ControlList &controls,
const libcamera::ControlInfoMap &camera_controls,
@@ -35,6 +36,11 @@ 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_video_format_info_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/gstlibcamera.cpp b/src/gstreamer/gstlibcamera.cpp
index 52388b5e..bff98979 100644
--- a/src/gstreamer/gstlibcamera.cpp
+++ b/src/gstreamer/gstlibcamera.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2019, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamera.c - GStreamer plugin
+ * GStreamer plugin
*/
#include "gstlibcameraprovider.h"
diff --git a/src/gstreamer/gstlibcameraallocator.cpp b/src/gstreamer/gstlibcameraallocator.cpp
index c740b8fc..d4492d99 100644
--- a/src/gstreamer/gstlibcameraallocator.cpp
+++ b/src/gstreamer/gstlibcameraallocator.cpp
@@ -3,11 +3,13 @@
* Copyright (C) 2020, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcameraallocator.cpp - GStreamer Custom Allocator
+ * GStreamer Custom Allocator
*/
#include "gstlibcameraallocator.h"
+#include <utility>
+
#include <libcamera/camera.h>
#include <libcamera/framebuffer_allocator.h>
#include <libcamera/stream.h>
@@ -100,6 +102,11 @@ struct _GstLibcameraAllocator {
* FrameWrap.
*/
GHashTable *pools;
+ /*
+ * The camera manager represents the library, which needs to be kept
+ * alive until all the memory has been released.
+ */
+ std::shared_ptr<CameraManager> *cm_ptr;
};
G_DEFINE_TYPE(GstLibcameraAllocator, gst_libcamera_allocator,
@@ -173,6 +180,9 @@ gst_libcamera_allocator_finalize(GObject *object)
delete self->fb_allocator;
+ /* Keep last. */
+ delete self->cm_ptr;
+
G_OBJECT_CLASS(gst_libcamera_allocator_parent_class)->finalize(object);
}
@@ -191,16 +201,20 @@ GstLibcameraAllocator *
gst_libcamera_allocator_new(std::shared_ptr<Camera> camera,
CameraConfiguration *config_)
{
- auto *self = GST_LIBCAMERA_ALLOCATOR(g_object_new(GST_TYPE_LIBCAMERA_ALLOCATOR,
- nullptr));
+ g_autoptr(GstLibcameraAllocator) self = GST_LIBCAMERA_ALLOCATOR(g_object_new(GST_TYPE_LIBCAMERA_ALLOCATOR,
+ nullptr));
+ gint ret;
+
+ self->cm_ptr = new std::shared_ptr<CameraManager>(gst_libcamera_get_camera_manager(ret));
+ if (ret)
+ return nullptr;
self->fb_allocator = new FrameBufferAllocator(camera);
for (StreamConfiguration &streamCfg : *config_) {
Stream *stream = streamCfg.stream();
- gint ret;
ret = self->fb_allocator->allocate(stream);
- if (ret == 0)
+ if (ret <= 0)
return nullptr;
GQueue *pool = g_queue_new();
@@ -214,7 +228,7 @@ gst_libcamera_allocator_new(std::shared_ptr<Camera> camera,
g_hash_table_insert(self->pools, stream, pool);
}
- return self;
+ return std::exchange(self, nullptr);
}
bool
diff --git a/src/gstreamer/gstlibcameraallocator.h b/src/gstreamer/gstlibcameraallocator.h
index 0a08c3bb..1a6ba346 100644
--- a/src/gstreamer/gstlibcameraallocator.h
+++ b/src/gstreamer/gstlibcameraallocator.h
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcameraallocator.h - GStreamer Custom Allocator
+ * GStreamer Custom Allocator
*/
#pragma once
diff --git a/src/gstreamer/gstlibcamerapad.cpp b/src/gstreamer/gstlibcamerapad.cpp
index 9e710a47..3bc2bc87 100644
--- a/src/gstreamer/gstlibcamerapad.cpp
+++ b/src/gstreamer/gstlibcamerapad.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2019, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamerapad.cpp - GStreamer Capture Pad
+ * GStreamer Capture Pad
*/
#include "gstlibcamerapad.h"
@@ -18,6 +18,8 @@ struct _GstLibcameraPad {
GstPad parent;
StreamRole role;
GstLibcameraPool *pool;
+ GstBufferPool *video_pool;
+ GstVideoInfo info;
GstClockTime latency;
};
@@ -153,6 +155,35 @@ gst_libcamera_pad_set_pool(GstPad *pad, GstLibcameraPool *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)
{
diff --git a/src/gstreamer/gstlibcamerapad.h b/src/gstreamer/gstlibcamerapad.h
index 103ee57a..f98b8a7f 100644
--- a/src/gstreamer/gstlibcamerapad.h
+++ b/src/gstreamer/gstlibcamerapad.h
@@ -3,7 +3,7 @@
* Copyright (C) 2019, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamerapad.h - GStreamer Capture Element
+ * GStreamer Capture Element
*/
#pragma once
@@ -23,6 +23,14 @@ GstLibcameraPool *gst_libcamera_pad_get_pool(GstPad *pad);
void gst_libcamera_pad_set_pool(GstPad *pad, GstLibcameraPool *pool);
+GstBufferPool *gst_libcamera_pad_get_video_pool(GstPad *pad);
+
+void gst_libcamera_pad_set_video_pool(GstPad *pad, GstBufferPool *video_pool);
+
+GstVideoInfo gst_libcamera_pad_get_video_info(GstPad *pad);
+
+void gst_libcamera_pad_set_video_info(GstPad *pad, const GstVideoInfo *info);
+
libcamera::Stream *gst_libcamera_pad_get_stream(GstPad *pad);
void gst_libcamera_pad_set_latency(GstPad *pad, GstClockTime latency);
diff --git a/src/gstreamer/gstlibcamerapool.cpp b/src/gstreamer/gstlibcamerapool.cpp
index 0c2be43c..8278144f 100644
--- a/src/gstreamer/gstlibcamerapool.cpp
+++ b/src/gstreamer/gstlibcamerapool.cpp
@@ -3,11 +3,13 @@
* Copyright (C) 2020, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamerapool.cpp - GStreamer Buffer Pool
+ * GStreamer Buffer Pool
*/
#include "gstlibcamerapool.h"
+#include <deque>
+
#include <libcamera/stream.h>
#include "gstlibcamera-utils.h"
@@ -24,24 +26,41 @@ static guint signals[N_SIGNALS];
struct _GstLibcameraPool {
GstBufferPool parent;
- GstAtomicQueue *queue;
+ std::deque<GstBuffer *> *queue;
GstLibcameraAllocator *allocator;
Stream *stream;
};
G_DEFINE_TYPE(GstLibcameraPool, gst_libcamera_pool, GST_TYPE_BUFFER_POOL)
+static GstBuffer *
+gst_libcamera_pool_pop_buffer(GstLibcameraPool *self)
+{
+ GLibLocker lock(GST_OBJECT(self));
+ GstBuffer *buf;
+
+ if (self->queue->empty())
+ return nullptr;
+
+ buf = self->queue->front();
+ self->queue->pop_front();
+
+ return buf;
+}
+
static GstFlowReturn
gst_libcamera_pool_acquire_buffer(GstBufferPool *pool, GstBuffer **buffer,
[[maybe_unused]] GstBufferPoolAcquireParams *params)
{
GstLibcameraPool *self = GST_LIBCAMERA_POOL(pool);
- GstBuffer *buf = GST_BUFFER(gst_atomic_queue_pop(self->queue));
+ GstBuffer *buf = gst_libcamera_pool_pop_buffer(self);
+
if (!buf)
return GST_FLOW_ERROR;
if (!gst_libcamera_allocator_prepare_buffer(self->allocator, self->stream, buf)) {
- gst_atomic_queue_push(self->queue, buf);
+ GLibLocker lock(GST_OBJECT(self));
+ self->queue->push_back(buf);
return GST_FLOW_ERROR;
}
@@ -64,9 +83,13 @@ static void
gst_libcamera_pool_release_buffer(GstBufferPool *pool, GstBuffer *buffer)
{
GstLibcameraPool *self = GST_LIBCAMERA_POOL(pool);
- bool do_notify = gst_atomic_queue_length(self->queue) == 0;
+ bool do_notify;
- gst_atomic_queue_push(self->queue, buffer);
+ {
+ GLibLocker lock(GST_OBJECT(self));
+ do_notify = self->queue->empty();
+ self->queue->push_back(buffer);
+ }
if (do_notify)
g_signal_emit(self, signals[SIGNAL_BUFFER_NOTIFY], 0);
@@ -75,7 +98,7 @@ gst_libcamera_pool_release_buffer(GstBufferPool *pool, GstBuffer *buffer)
static void
gst_libcamera_pool_init(GstLibcameraPool *self)
{
- self->queue = gst_atomic_queue_new(4);
+ self->queue = new std::deque<GstBuffer *>();
}
static void
@@ -84,10 +107,10 @@ gst_libcamera_pool_finalize(GObject *object)
GstLibcameraPool *self = GST_LIBCAMERA_POOL(object);
GstBuffer *buf;
- while ((buf = GST_BUFFER(gst_atomic_queue_pop(self->queue))))
+ while ((buf = gst_libcamera_pool_pop_buffer(self)))
gst_buffer_unref(buf);
- gst_atomic_queue_unref(self->queue);
+ delete self->queue;
g_object_unref(self->allocator);
G_OBJECT_CLASS(gst_libcamera_pool_parent_class)->finalize(object);
@@ -111,8 +134,20 @@ gst_libcamera_pool_class_init(GstLibcameraPoolClass *klass)
G_TYPE_NONE, 0);
}
+static void
+gst_libcamera_buffer_add_video_meta(GstBuffer *buffer, GstVideoInfo *info)
+{
+ GstVideoMeta *vmeta;
+ vmeta = 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);
+ GST_META_FLAGS(vmeta) = (GstMetaFlags)(GST_META_FLAGS(vmeta) | GST_META_FLAG_POOLED);
+}
+
GstLibcameraPool *
-gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream)
+gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream,
+ GstVideoInfo *info)
{
auto *pool = GST_LIBCAMERA_POOL(g_object_new(GST_TYPE_LIBCAMERA_POOL, nullptr));
@@ -122,7 +157,8 @@ gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream)
gsize pool_size = gst_libcamera_allocator_get_pool_size(allocator, stream);
for (gsize i = 0; i < pool_size; i++) {
GstBuffer *buffer = gst_buffer_new();
- gst_atomic_queue_push(pool->queue, buffer);
+ gst_libcamera_buffer_add_video_meta(buffer, info);
+ pool->queue->push_back(buffer);
}
return pool;
diff --git a/src/gstreamer/gstlibcamerapool.h b/src/gstreamer/gstlibcamerapool.h
index ce3bf60b..02ee4dd4 100644
--- a/src/gstreamer/gstlibcamerapool.h
+++ b/src/gstreamer/gstlibcamerapool.h
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamerapool.h - GStreamer Buffer Pool
+ * GStreamer Buffer Pool
*
* This is a partial implementation of GstBufferPool intended for internal use
* only. This pool cannot be configured or activated.
@@ -14,6 +14,7 @@
#include "gstlibcameraallocator.h"
#include <gst/gst.h>
+#include <gst/video/video.h>
#include <libcamera/stream.h>
@@ -21,7 +22,7 @@
G_DECLARE_FINAL_TYPE(GstLibcameraPool, gst_libcamera_pool, GST_LIBCAMERA, POOL, GstBufferPool)
GstLibcameraPool *gst_libcamera_pool_new(GstLibcameraAllocator *allocator,
- libcamera::Stream *stream);
+ libcamera::Stream *stream, GstVideoInfo *info);
libcamera::Stream *gst_libcamera_pool_get_stream(GstLibcameraPool *self);
diff --git a/src/gstreamer/gstlibcameraprovider.cpp b/src/gstreamer/gstlibcameraprovider.cpp
index ce3e0a08..5da96ea3 100644
--- a/src/gstreamer/gstlibcameraprovider.cpp
+++ b/src/gstreamer/gstlibcameraprovider.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcameraprovider.c - GStreamer Device Provider
+ * GStreamer Device Provider
*/
#include <array>
@@ -33,7 +33,6 @@ GST_DEBUG_CATEGORY_STATIC(provider_debug);
enum {
PROP_DEVICE_NAME = 1,
- PROP_AUTO_FOCUS_MODE = 2,
};
#define GST_TYPE_LIBCAMERA_DEVICE gst_libcamera_device_get_type()
@@ -43,7 +42,6 @@ G_DECLARE_FINAL_TYPE(GstLibcameraDevice, gst_libcamera_device,
struct _GstLibcameraDevice {
GstDevice parent;
gchar *name;
- controls::AfModeEnum auto_focus_mode = controls::AfModeManual;
};
G_DEFINE_TYPE(GstLibcameraDevice, gst_libcamera_device, GST_TYPE_DEVICE)
@@ -60,7 +58,6 @@ gst_libcamera_device_create_element(GstDevice *device, const gchar *name)
g_assert(source);
g_object_set(source, "camera-name", GST_LIBCAMERA_DEVICE(device)->name, nullptr);
- g_object_set(source, "auto-focus-mode", GST_LIBCAMERA_DEVICE(device)->auto_focus_mode, nullptr);
return source;
}
@@ -87,9 +84,6 @@ gst_libcamera_device_set_property(GObject *object, guint prop_id,
case PROP_DEVICE_NAME:
device->name = g_value_dup_string(value);
break;
- case PROP_AUTO_FOCUS_MODE:
- device->auto_focus_mode = static_cast<controls::AfModeEnum>(g_value_get_enum(value));
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@@ -129,15 +123,6 @@ gst_libcamera_device_class_init(GstLibcameraDeviceClass *klass)
(GParamFlags)(G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property(object_class, PROP_DEVICE_NAME, pspec);
-
- pspec = 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, pspec);
}
static GstDevice *
diff --git a/src/gstreamer/gstlibcameraprovider.h b/src/gstreamer/gstlibcameraprovider.h
index aaceabf5..19708b9d 100644
--- a/src/gstreamer/gstlibcameraprovider.h
+++ b/src/gstreamer/gstlibcameraprovider.h
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcameraprovider.h - GStreamer Device Provider
+ * GStreamer Device Provider
*/
#pragma once
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;
}
diff --git a/src/gstreamer/gstlibcamerasrc.h b/src/gstreamer/gstlibcamerasrc.h
index 0a88ba02..a27db9ca 100644
--- a/src/gstreamer/gstlibcamerasrc.h
+++ b/src/gstreamer/gstlibcamerasrc.h
@@ -3,13 +3,11 @@
* Copyright (C) 2019, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamerasrc.h - GStreamer Capture Element
+ * GStreamer Capture Element
*/
#pragma once
-#include <libcamera/control_ids.h>
-
#include <gst/gst.h>
G_BEGIN_DECLS
@@ -19,32 +17,3 @@ G_DECLARE_FINAL_TYPE(GstLibcameraSrc, gst_libcamera_src,
GST_LIBCAMERA, SRC, GstElement)
G_END_DECLS
-
-inline GType
-gst_libcamera_auto_focus_get_type()
-{
- static GType type = 0;
- static const GEnumValue values[] = {
- {
- static_cast<gint>(libcamera::controls::AfModeManual),
- "AfModeManual",
- "manual-focus",
- },
- {
- static_cast<gint>(libcamera::controls::AfModeAuto),
- "AfModeAuto",
- "automatic-auto-focus",
- },
- {
- static_cast<gint>(libcamera::controls::AfModeContinuous),
- "AfModeContinuous",
- "continuous-auto-focus",
- },
- { 0, NULL, NULL }
- };
-
- if (!type)
- type = g_enum_register_static("GstLibcameraAutoFocus", values);
-
- return type;
-}
diff --git a/src/gstreamer/meson.build b/src/gstreamer/meson.build
index c2a01e7b..fd83e073 100644
--- a/src/gstreamer/meson.build
+++ b/src/gstreamer/meson.build
@@ -25,6 +25,17 @@ libcamera_gst_sources = [
'gstlibcamerasrc.cpp',
]
+# Generate gstreamer control properties
+
+gen_gst_controls_template = files('gstlibcamera-controls.cpp.in')
+libcamera_gst_sources += custom_target('gstlibcamera-controls.cpp',
+ input : controls_files,
+ output : 'gstlibcamera-controls.cpp',
+ command : [gen_gst_controls, '-o', '@OUTPUT@',
+ '-t', gen_gst_controls_template, '@INPUT@'],
+ depend_files : [py_mod_controls],
+ env : py_build_env)
+
libcamera_gst_cpp_args = [
'-DVERSION="@0@"'.format(libcamera_git_version),
'-DPACKAGE="@0@"'.format(meson.project_name()),