@@ -357,6 +357,42 @@ control_type_to_gtype(const ControlType &type)
return G_TYPE_INVALID;
}
+static const struct {
+ Orientation orientation;
+ GstVideoOrientationMethod method;
+} orientation_map[]{
+ { Orientation::Rotate0, GST_VIDEO_ORIENTATION_IDENTITY },
+ { Orientation::Rotate90, GST_VIDEO_ORIENTATION_90R },
+ { Orientation::Rotate180, GST_VIDEO_ORIENTATION_180 },
+ { Orientation::Rotate270, GST_VIDEO_ORIENTATION_90L },
+ { Orientation::Rotate0Mirror, GST_VIDEO_ORIENTATION_HORIZ },
+ { Orientation::Rotate180Mirror, GST_VIDEO_ORIENTATION_VERT },
+ { Orientation::Rotate90Mirror, GST_VIDEO_ORIENTATION_UL_LR },
+ { Orientation::Rotate270Mirror, GST_VIDEO_ORIENTATION_UR_LL },
+};
+
+Orientation
+gst_video_orientation_to_libcamera_orientation(GstVideoOrientationMethod method)
+{
+ for (auto &b : orientation_map) {
+ if (b.method == method)
+ return b.orientation;
+ }
+
+ return Orientation::Rotate0;
+}
+
+GstVideoOrientationMethod
+libcamera_orientation_to_gst_video_orientation(Orientation orientation)
+{
+ for (auto &a : orientation_map) {
+ if (a.orientation == orientation)
+ return a.method;
+ }
+
+ return GST_VIDEO_ORIENTATION_IDENTITY;
+}
+
GstCaps *
gst_libcamera_stream_formats_to_caps(const StreamFormats &formats)
{
@@ -898,3 +934,27 @@ int gst_libcamera_set_structure_field(GstStructure *structure, const ControlId *
return 0;
}
+
+const gchar *
+gst_libcamera_transform_to_tag_string(libcamera::Transform transform)
+{
+ switch (transform) {
+ case Transform::Rot90:
+ return "rotate-90";
+ case Transform::Rot180:
+ return "rotate-180";
+ case Transform::Rot270:
+ return "rotate-270";
+ case Transform::HFlip:
+ return "flip-rotate-0";
+ case Transform::VFlip:
+ return "flip-rotate-180";
+ case Transform::Transpose:
+ return "flip-rotate-270";
+ case Transform::Rot180Transpose:
+ return "flip-rotate-90";
+ case Transform::Identity:
+ default:
+ return "rotate-0";
+ }
+}
@@ -10,7 +10,9 @@
#include <libcamera/camera_manager.h>
#include <libcamera/controls.h>
+#include <libcamera/orientation.h>
#include <libcamera/stream.h>
+#include <libcamera/transform.h>
#include <gst/gst.h>
#include <gst/video/video.h>
@@ -32,6 +34,9 @@ libcamera::Rectangle gst_libcamera_gvalue_get_rectangle(const GValue *value);
int gst_libcamera_set_structure_field(GstStructure *structure,
const libcamera::ControlId *id,
const libcamera::ControlValue &value);
+libcamera::Orientation gst_video_orientation_to_libcamera_orientation(GstVideoOrientationMethod method);
+GstVideoOrientationMethod libcamera_orientation_to_gst_video_orientation(libcamera::Orientation orientation);
+const gchar *gst_libcamera_transform_to_tag_string(libcamera::Transform transform);
#if !GST_CHECK_VERSION(1, 16, 0)
static inline void gst_clear_event(GstEvent **event_ptr)
@@ -141,6 +141,7 @@ struct _GstLibcameraSrc {
GstTask *task;
gchar *camera_name;
+ GstVideoOrientationMethod orientation;
std::atomic<GstEvent *> pending_eos;
@@ -152,9 +153,12 @@ struct _GstLibcameraSrc {
enum {
PROP_0,
PROP_CAMERA_NAME,
+ PROP_ORIENTATION,
PROP_LAST
};
+static GParamSpec *properties[PROP_LAST];
+
static void gst_libcamera_src_child_proxy_init(gpointer g_iface,
gpointer iface_data);
@@ -606,6 +610,13 @@ gst_libcamera_src_negotiate(GstLibcameraSrc *self)
gst_libcamera_get_framerate_from_caps(caps, element_caps);
}
+ /* Set orientation in libcamera camera configuration. */
+ Orientation requestedLibcameraOrientation = gst_video_orientation_to_libcamera_orientation(self->orientation);
+ {
+ GLibLocker lock(GST_OBJECT(self));
+ state->config_->orientation = requestedLibcameraOrientation;
+ }
+
/* Validate the configuration. */
CameraConfiguration::Status status = state->config_->validate();
if (status == CameraConfiguration::Invalid)
@@ -627,6 +638,33 @@ gst_libcamera_src_negotiate(GstLibcameraSrc *self)
gst_libcamera_clamp_and_set_frameduration(state->initControls_,
state->cam_->controls(), element_caps);
+ /*
+ * If the requested orientation isn't possible, report the libcamera-provided orientation
+ * on GstBus and send the "image-orientation" tag downstream for elements to apply
+ * the final requested orientation.
+ */
+ if (state->config_->orientation != requestedLibcameraOrientation) {
+ {
+ GLibLocker lock(GST_OBJECT(self));
+ self->orientation = libcamera_orientation_to_gst_video_orientation(state->config_->orientation);
+ }
+
+ GValue v = G_VALUE_INIT;
+ g_value_init(&v, properties[PROP_ORIENTATION]->value_type);
+ g_object_get_property(G_OBJECT(self), properties[PROP_ORIENTATION]->name, &v);
+ GstMessage *msg = gst_message_new_property_notify(GST_OBJECT(self), properties[PROP_ORIENTATION]->name, &v);
+ gst_element_post_message(GST_ELEMENT(self), msg);
+
+ Transform toApply = requestedLibcameraOrientation / state->config_->orientation;
+ const gchar *transform_tag = gst_libcamera_transform_to_tag_string(toApply);
+
+ g_autoptr(GstEvent) tag_event =
+ gst_event_new_tag(gst_tag_list_new(GST_TAG_IMAGE_ORIENTATION, transform_tag, NULL));
+
+ for (gsize i = 0; i < state->srcpads_.size(); i++)
+ gst_pad_push_event(state->srcpads_[i], gst_event_ref(tag_event));
+ }
+
/*
* Regardless if it has been modified, create clean caps and push the
* caps event. Downstream will decide if the caps are acceptable.
@@ -934,6 +972,9 @@ gst_libcamera_src_set_property(GObject *object, guint prop_id,
g_free(self->camera_name);
self->camera_name = g_value_dup_string(value);
break;
+ case PROP_ORIENTATION:
+ self->orientation = static_cast<GstVideoOrientationMethod>(g_value_get_enum(value));
+ break;
default:
if (!state->controls_.setProperty(prop_id - PROP_LAST, value, pspec))
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -953,6 +994,9 @@ gst_libcamera_src_get_property(GObject *object, guint prop_id, GValue *value,
case PROP_CAMERA_NAME:
g_value_set_string(value, self->camera_name);
break;
+ case PROP_ORIENTATION:
+ g_value_set_enum(value, static_cast<gint>(self->orientation));
+ break;
default:
if (!state->controls_.getProperty(prop_id - PROP_LAST, value, pspec))
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -1162,6 +1206,20 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)
| G_PARAM_STATIC_STRINGS));
g_object_class_install_property(object_class, PROP_CAMERA_NAME, spec);
+
+ properties[PROP_ORIENTATION] = g_param_spec_enum("orientation", "orientation",
+ "Property to control flipping and rotation operations of the camera. "
+ "If the orientation cannot be satisfied, libcamerasrc will send 'image-orientation' "
+ "tag downstream to assist with the orientation that was requested. Sinks that "
+ "implement the GST_VIDEO_ORIENTATION_AUTO should rotate the stream accordingly. ",
+ GST_TYPE_VIDEO_ORIENTATION_METHOD,
+ GST_VIDEO_ORIENTATION_IDENTITY,
+ (GParamFlags)(GST_PARAM_MUTABLE_READY
+ | G_PARAM_CONSTRUCT
+ | G_PARAM_READWRITE
+ | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property(object_class, PROP_ORIENTATION, properties[PROP_ORIENTATION]);
+
GstCameraControls::installProperties(object_class, PROP_LAST);
}
Plumb the support for CameraConfiguration::orientation in libcamerasrc. A new "orientation" property is introduced and mappings for libcamera::Orientation <> GstVideoOrientationMethod are provided with helpers. If the orientation is changed after the CameraConfiguration::validate(), a GstMessage is sent on the GstBus to report the new orientation property value. In case the orientation that was requested cannot be satisfied, a 'image-orientation' tag is pushed downstream for sinks to rotate the stream accordingly (those implementing GSTGST_VIDEO_ORIENTATION_AUTO). Signed-off-by: Umang Jain <uajain@igalia.com> --- Changes in v6: - Use GstMessage to send new property changes if any (like orientation after validate()) - send 'image-orientation' tag orientation downstream if orientation cannot be satisfied - Drop 'video-orientation' and restore 'orientation' as property name - change authorship to myself as patch is reworked significantly Changes in v5: - patch scrubbing and cleanup - If different orientation is returned than requested, update the property - Minor string fixes, append appropriate tags Link to v4: https://patchwork.libcamera.org/patch/23965/ Note: Checkstyle will complain on this patch on a hunk, but the existing format is quite read-able IMO, hence ignored. --- src/gstreamer/gstlibcamera-utils.cpp | 60 ++++++++++++++++++++++++++++ src/gstreamer/gstlibcamera-utils.h | 5 +++ src/gstreamer/gstlibcamerasrc.cpp | 58 +++++++++++++++++++++++++++ 3 files changed, 123 insertions(+)