| Message ID | 20251106130133.130946-1-uajain@igalia.com |
|---|---|
| State | New |
| Headers | show |
| Series |
|
| Related | show |
Hi Umang, I noticed this patch -really- fell into the cracks.. Is it still valid, still something that hasn't been addressed otherwise, right ? On Thu, Nov 06, 2025 at 01:01:33PM +0000, Umang Jain wrote: > 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(+) > > diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp > index bfb094c9..79fb606b 100644 > --- a/src/gstreamer/gstlibcamera-utils.cpp > +++ b/src/gstreamer/gstlibcamera-utils.cpp > @@ -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"; > + } > +} > diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h > index 35df56fb..b7983264 100644 > --- a/src/gstreamer/gstlibcamera-utils.h > +++ b/src/gstreamer/gstlibcamera-utils.h > @@ -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) > diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp > index 391b228d..77648e6a 100644 > --- a/src/gstreamer/gstlibcamerasrc.cpp > +++ b/src/gstreamer/gstlibcamerasrc.cpp > @@ -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); > } > > -- > 2.51.0 >
Hi Jacopo, On 2026-08-06 08:40, Jacopo Mondi wrote: > Hi Umang, > I noticed this patch -really- fell into the cracks.. > > Is it still valid, still something that hasn't been addressed > otherwise, right ? I believe this is still valid and needs review. Perhaps needs rebasing as well. > > On Thu, Nov 06, 2025 at 01:01:33PM +0000, Umang Jain wrote: >> 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(+) >> >> diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp >> index bfb094c9..79fb606b 100644 >> --- a/src/gstreamer/gstlibcamera-utils.cpp >> +++ b/src/gstreamer/gstlibcamera-utils.cpp >> @@ -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"; >> + } >> +} >> diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h >> index 35df56fb..b7983264 100644 >> --- a/src/gstreamer/gstlibcamera-utils.h >> +++ b/src/gstreamer/gstlibcamera-utils.h >> @@ -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) >> diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp >> index 391b228d..77648e6a 100644 >> --- a/src/gstreamer/gstlibcamerasrc.cpp >> +++ b/src/gstreamer/gstlibcamerasrc.cpp >> @@ -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); >> } >> >> -- >> 2.51.0 >>
Hi 2025. 11. 06. 14:01 keltezéssel, Umang Jain írta: > 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(+) > > diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp > index bfb094c9..79fb606b 100644 > --- a/src/gstreamer/gstlibcamera-utils.cpp > +++ b/src/gstreamer/gstlibcamera-utils.cpp > @@ -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"; > + } I'd remove the `default` branch and move the return here so that the compiler can recognize the incomplete switch if `Transform` changes. > +} > diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h > index 35df56fb..b7983264 100644 > --- a/src/gstreamer/gstlibcamera-utils.h > +++ b/src/gstreamer/gstlibcamera-utils.h > @@ -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) > diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp > index 391b228d..77648e6a 100644 > --- a/src/gstreamer/gstlibcamerasrc.cpp > +++ b/src/gstreamer/gstlibcamerasrc.cpp > @@ -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]; If you're creating this, it'd probably be better to initialize all members properly, not just "orientation". > + > 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; Why is the locking needed? `state->config_` is "freely" accessed in this function in other places? Maybe `self->orientation` is what needs to be guarded? > + } > + > /* 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); Is `g_value_unset()` / `g_auto(GValue)` not needed here? I suppose the value is just an integer, but for the sake of completeness, I'd add it. > + > + 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); > } >
diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp index bfb094c9..79fb606b 100644 --- a/src/gstreamer/gstlibcamera-utils.cpp +++ b/src/gstreamer/gstlibcamera-utils.cpp @@ -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"; + } +} diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h index 35df56fb..b7983264 100644 --- a/src/gstreamer/gstlibcamera-utils.h +++ b/src/gstreamer/gstlibcamera-utils.h @@ -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) diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp index 391b228d..77648e6a 100644 --- a/src/gstreamer/gstlibcamerasrc.cpp +++ b/src/gstreamer/gstlibcamerasrc.cpp @@ -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(+)