[{"id":27330,"web_url":"https://patchwork.libcamera.org/comment/27330/","msgid":"<168665716815.2889415.14346463454221033138@Monstersaurus>","date":"2023-06-13T11:52:48","subject":"Re: [libcamera-devel] [PATCH 2/2] gstreamer: src: Add transform\n\tproperty","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Robert Mader via libcamera-devel (2023-06-13 11:34:50)\n> This allows users to request a transform using the Gstreamer\n> equivalent. If the combined transform of the requested one and a\n> possible rotation from the camera properties is not fully supported by\n> the sensor, the remaining transform will be passed down to downstream\n> elements as tag.\n\n'as a tag' ? (minor could be fixed when applying)\n\n> \n> The later is common for 90/270 degree rotations. Thus, a side effect of\n> this feature is that libcamerasrc now behaves similar to pipewiresrc in\n> regards to rotated cameras in e.g. phones, allowing apps to compensate\n> accordingly.\n\nSounds helpful!\n\n> \n> Signed-off-by: Robert Mader <robert.mader@collabora.com>\n> ---\n>  src/gstreamer/gstlibcamera-utils.cpp | 72 ++++++++++++++++++++++++++++\n>  src/gstreamer/gstlibcamera-utils.h   |  5 ++\n>  src/gstreamer/gstlibcamerasrc.cpp    | 41 +++++++++++++++-\n>  3 files changed, 117 insertions(+), 1 deletion(-)\n> \n> diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp\n> index 750ec351..43ce75cb 100644\n> --- a/src/gstreamer/gstlibcamera-utils.cpp\n> +++ b/src/gstreamer/gstlibcamera-utils.cpp\n> @@ -553,3 +553,75 @@ gst_libcamera_get_camera_manager(int &ret)\n>  \n>         return cm;\n>  }\n> +\n> +libcamera::Transform\n> +gst_libcamera_orientation_to_transform(GstVideoOrientationMethod orientation)\n> +{\n> +       switch (orientation) {\n> +       case GST_VIDEO_ORIENTATION_90R:\n> +               return Transform::Rot90;\n> +       case GST_VIDEO_ORIENTATION_180:\n> +               return Transform::Rot180;\n> +       case GST_VIDEO_ORIENTATION_90L:\n> +               return Transform::Rot270;\n> +       case GST_VIDEO_ORIENTATION_HORIZ:\n> +               return Transform::HFlip;\n> +       case GST_VIDEO_ORIENTATION_VERT:\n> +               return Transform::VFlip;\n> +       case GST_VIDEO_ORIENTATION_UL_LR:\n> +               return Transform::Transpose;\n> +       case GST_VIDEO_ORIENTATION_UR_LL:\n> +               return Transform::Rot180Transpose;\n> +       case GST_VIDEO_ORIENTATION_IDENTITY:\n> +       default:\n> +               return Transform::Identity;\n> +       }\n> +}\n> +\n> +GstVideoOrientationMethod\n> +gst_libcamera_transform_to_orientation(libcamera::Transform transform)\n> +{\n> +       switch (transform) {\n> +       case Transform::Rot90:\n> +               return GST_VIDEO_ORIENTATION_90R;\n> +       case Transform::Rot180:\n> +               return GST_VIDEO_ORIENTATION_180;\n> +       case Transform::Rot270:\n> +               return GST_VIDEO_ORIENTATION_90L;\n> +       case Transform::HFlip:\n> +               return GST_VIDEO_ORIENTATION_HORIZ;\n> +       case Transform::VFlip:\n> +               return GST_VIDEO_ORIENTATION_VERT;\n> +       case Transform::Transpose:\n> +               return GST_VIDEO_ORIENTATION_UL_LR;\n> +       case Transform::Rot180Transpose:\n> +               return GST_VIDEO_ORIENTATION_UR_LL;\n> +       case Transform::Identity:\n> +       default:\n> +               return GST_VIDEO_ORIENTATION_IDENTITY;\n> +       }\n> +}\n> +\n> +const char *\n> +gst_libcamera_transform_to_tag_string(libcamera::Transform transform)\n> +{\n> +       switch (transform) {\n> +       case Transform::Rot90:\n> +               return \"rotate-90\";\n> +       case Transform::Rot180:\n> +               return \"rotate-180\";\n> +       case Transform::Rot270:\n> +               return \"rotate-270\";\n> +       case Transform::HFlip:\n> +               return \"flip-rotate-0\";\n> +       case Transform::VFlip:\n> +               return \"flip-rotate-180\";\n> +       case Transform::Transpose:\n> +               return \"flip-rotate-270\";\n> +       case Transform::Rot180Transpose:\n> +               return \"flip-rotate-90\";\n> +       case Transform::Identity:\n> +       default:\n> +               return \"rotate-0\";\n> +       }\n> +}\n> diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h\n> index fd304a8b..84d26c47 100644\n> --- a/src/gstreamer/gstlibcamera-utils.h\n> +++ b/src/gstreamer/gstlibcamera-utils.h\n> @@ -11,6 +11,7 @@\n>  #include <libcamera/camera_manager.h>\n>  #include <libcamera/controls.h>\n>  #include <libcamera/stream.h>\n> +#include <libcamera/transform.h>\n>  \n>  #include <gst/gst.h>\n>  #include <gst/video/video.h>\n> @@ -30,6 +31,10 @@ gboolean gst_task_resume(GstTask *task);\n>  #endif\n>  std::shared_ptr<libcamera::CameraManager> gst_libcamera_get_camera_manager(int &ret);\n>  \n> +libcamera::Transform gst_libcamera_orientation_to_transform(GstVideoOrientationMethod orientation);\n> +GstVideoOrientationMethod gst_libcamera_transform_to_orientation(libcamera::Transform transform);\n> +const char *gst_libcamera_transform_to_tag_string(libcamera::Transform transform);\n> +\n>  /**\n>   * \\class GLibLocker\n>   * \\brief A simple scoped mutex locker for GMutex\n> diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp\n> index 721b35c2..9d9437d0 100644\n> --- a/src/gstreamer/gstlibcamerasrc.cpp\n> +++ b/src/gstreamer/gstlibcamerasrc.cpp\n> @@ -147,6 +147,8 @@ struct _GstLibcameraSrc {\n>  \n>         gchar *camera_name;\n>  \n> +       GstVideoOrientationMethod transform;\n> +\n>         GstLibcameraSrcState *state;\n>         GstLibcameraAllocator *allocator;\n>         GstFlowCombiner *flow_combiner;\n> @@ -154,7 +156,9 @@ struct _GstLibcameraSrc {\n>  \n>  enum {\n>         PROP_0,\n> -       PROP_CAMERA_NAME\n> +       PROP_CAMERA_NAME,\n> +       PROP_TRANSFORM,\n> +       PROP_LAST\n>  };\n>  \n>  G_DEFINE_TYPE_WITH_CODE(GstLibcameraSrc, gst_libcamera_src, GST_TYPE_ELEMENT,\n> @@ -461,6 +465,8 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n>         GLibRecLocker lock(&self->stream_lock);\n>         GstLibcameraSrcState *state = self->state;\n>         GstFlowReturn flow_ret = GST_FLOW_OK;\n> +       libcamera::Transform tag_transform;\n> +       const char* tag_string;\n>         gint ret;\n>  \n>         g_autoptr(GstStructure) element_caps = gst_structure_new_empty(\"caps\");\n> @@ -513,12 +519,27 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n>         if (flow_ret != GST_FLOW_OK)\n>                 goto done;\n>  \n> +       state->config_->transform =\n> +               gst_libcamera_orientation_to_transform (self->transform);\n> +\n\nOoof I misred the function as 'gst (libcamera_orientation) to transform'\ninstead of ' gst libcamera \"orientation\" to \"transform\" '\n\nSo I think this sounds right. I just need to remember that the\ngst_libcamera is the whole prefix.\n\n\n>         /* Validate the configuration. */\n>         if (state->config_->validate() == CameraConfiguration::Invalid) {\n>                 flow_ret = GST_FLOW_NOT_NEGOTIATED;\n>                 goto done;\n>         }\n>  \n\nGiven the use of the ^ operator here, which is a little bit of C++\nmagic, it's probably worth a comment to future readers to say that this\nblock identifies the delta between what was requested and what was\npossible to expose the actions that still need to be taken. (As I\nunderstand it).\n\nIs it clear that the 'tag' is an action still needed to take rather than a\ndescription of what the image contains in normal gstreamer elements? \n\n(Unless I've got it backwards of course, which is entirely possible).\n\n\n> +       tag_transform = (gst_libcamera_orientation_to_transform (self->transform) ^\n> +                        state->config_->transform);\n> +       tag_string = gst_libcamera_transform_to_tag_string(tag_transform);\n> +       for (gsize i = 0; i < state->srcpads_.size(); i++) {\n> +               GstPad *srcpad = state->srcpads_[i];\n> +               GstEvent *tag_event;\n> +\n> +               tag_event = gst_event_new_tag(gst_tag_list_new(GST_TAG_IMAGE_ORIENTATION,\n> +                                                              tag_string, NULL));\n> +               gst_pad_push_event (srcpad, tag_event);\n> +       }\n> +\n>         ret = state->cam_->configure(state->config_.get());\n>         if (ret) {\n>                 GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,\n> @@ -659,6 +680,11 @@ gst_libcamera_src_set_property(GObject *object, guint prop_id,\n>                 g_free(self->camera_name);\n>                 self->camera_name = g_value_dup_string(value);\n>                 break;\n> +       case PROP_TRANSFORM:\n> +               self->transform =\n> +                       static_cast<GstVideoOrientationMethod>(\n> +                               g_value_get_enum(value));\n> +               break;\n>         default:\n>                 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);\n>                 break;\n> @@ -676,6 +702,9 @@ gst_libcamera_src_get_property(GObject *object, guint prop_id, GValue *value,\n>         case PROP_CAMERA_NAME:\n>                 g_value_set_string(value, self->camera_name);\n>                 break;\n> +        case PROP_TRANSFORM:\n> +                g_value_set_enum(value, self->transform);\n> +                break;\n\nIndentation issues in this one ^.\n\n>         default:\n>                 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);\n>                 break;\n> @@ -845,4 +874,14 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)\n>                                                   | G_PARAM_CONSTRUCT\n>                                                   | G_PARAM_READWRITE\n>                                                   | G_PARAM_STATIC_STRINGS)));\n> +\n\nI know the commit describes that transforms that couldn't be applied\nwill be passed in a tag, but it is probably worth documenting that here\ntoo where the property is registered for future developers/readers..\n(Unless that's just standard, expected and documented gstreamer\nbehaviour?)\n\n\n\n> +       g_object_class_install_property (object_class, PROP_TRANSFORM,\n> +               g_param_spec_enum (\"transform\", \"Transform\",\n> +                                  \"Request a transform (rotation and/or flip).\",\n> +                                  GST_TYPE_VIDEO_ORIENTATION_METHOD,\n> +                                  GST_VIDEO_ORIENTATION_IDENTITY,\n> +                                  (GParamFlags)(GST_PARAM_MUTABLE_READY\n> +                                                | G_PARAM_CONSTRUCT\n> +                                                | G_PARAM_READWRITE\n> +                                                | G_PARAM_STATIC_STRINGS)));\n>  }\n> -- \n> 2.41.0\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 9252EBD78E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 13 Jun 2023 11:52:52 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 179ED61E50;\n\tTue, 13 Jun 2023 13:52:52 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3860B61E49\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 13 Jun 2023 13:52:51 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(aztw-30-b2-v4wan-166917-cust845.vm26.cable.virginm.net\n\t[82.37.23.78])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 0A84F1AE;\n\tTue, 13 Jun 2023 13:52:20 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1686657172;\n\tbh=7GfTBc9CycXCYgaKVbdv/BEK7sTmh+Ch2MLeeR8T1Oc=;\n\th=In-Reply-To:References:To:Date:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=SyPaSgnE4fZ0Sjz/kESf8lU1lnne3TV/I8arPuCYioDI0Yq+q9QEZKYmuh/j9gdwp\n\teAIhPdv6xYFeUTguxJR4p6dBw8Pmq6RLn47lYCxIKQmjvMgWrMN+B7jHNwSkgq9Ze+\n\tYgLwuJNoXNnxUIQQ+zM4bvNckMTu/gL/F5mooBHmUrxhAEdmviJaIQPQsX46xrgkhP\n\tjx2XohqtszGtqCuq7wXTO0b+6cz3T7LUtbrus6UeCh5nR8f9dGpBdLcvBeSnEuz4rb\n\t53j/RbnN0ArulO2+04JzNuebg+l7kd0qoBvrS9Em4rFSoXCHfx6Qi1HTMMmBMHxP9d\n\tnLVZRED4F+FJA==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1686657141;\n\tbh=7GfTBc9CycXCYgaKVbdv/BEK7sTmh+Ch2MLeeR8T1Oc=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=TXlqcrBczBKAcgCNRlcDAx/yZVu7FZns5RLBDY4ckdJVZZjhKzBGsyfwH/Deg1zI7\n\tM8k18vA+xxjjTozfSViWQrDJuDHZyW5/1zMU2mw6x76jSAi75DUiMXGEyDnnalDQyM\n\t8CRyBb8iHtc0O5ilFOqnd6h6ci2EA2GkQzqjsESI="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"TXlqcrBc\"; dkim-atps=neutral","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20230613103519.91370-3-robert.mader@collabora.com>","References":"<20230613103519.91370-1-robert.mader@collabora.com>\n\t<20230613103519.91370-3-robert.mader@collabora.com>","To":"Robert Mader <robert.mader@collabora.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Tue, 13 Jun 2023 12:52:48 +0100","Message-ID":"<168665716815.2889415.14346463454221033138@Monstersaurus>","User-Agent":"alot/0.10","Subject":"Re: [libcamera-devel] [PATCH 2/2] gstreamer: src: Add transform\n\tproperty","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Kieran Bingham via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":27333,"web_url":"https://patchwork.libcamera.org/comment/27333/","msgid":"<8ad4816d4be8c65ab4ef4f556f5ea4dfa4ea0286.camel@ndufresne.ca>","date":"2023-06-13T14:39:33","subject":"Re: [libcamera-devel] [PATCH 2/2] gstreamer: src: Add transform\n\tproperty","submitter":{"id":30,"url":"https://patchwork.libcamera.org/api/people/30/","name":"Nicolas Dufresne","email":"nicolas@ndufresne.ca"},"content":"Le mardi 13 juin 2023 à 12:34 +0200, Robert Mader via libcamera-devel a écrit :\n> This allows users to request a transform using the Gstreamer\n> equivalent. If the combined transform of the requested one and a\n> possible rotation from the camera properties is not fully supported by\n> the sensor, the remaining transform will be passed down to downstream\n> elements as tag.\n> \n> The later is common for 90/270 degree rotations. Thus, a side effect of\n> this feature is that libcamerasrc now behaves similar to pipewiresrc in\n> regards to rotated cameras in e.g. phones, allowing apps to compensate\n> accordingly.\n> \n> Signed-off-by: Robert Mader <robert.mader@collabora.com>\n> ---\n>  src/gstreamer/gstlibcamera-utils.cpp | 72 ++++++++++++++++++++++++++++\n>  src/gstreamer/gstlibcamera-utils.h   |  5 ++\n>  src/gstreamer/gstlibcamerasrc.cpp    | 41 +++++++++++++++-\n>  3 files changed, 117 insertions(+), 1 deletion(-)\n> \n> diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp\n> index 750ec351..43ce75cb 100644\n> --- a/src/gstreamer/gstlibcamera-utils.cpp\n> +++ b/src/gstreamer/gstlibcamera-utils.cpp\n> @@ -553,3 +553,75 @@ gst_libcamera_get_camera_manager(int &ret)\n>  \n>  \treturn cm;\n>  }\n> +\n> +libcamera::Transform\n> +gst_libcamera_orientation_to_transform(GstVideoOrientationMethod orientation)\n> +{\n> +\tswitch (orientation) {\n> +\tcase GST_VIDEO_ORIENTATION_90R:\n> +\t\treturn Transform::Rot90;\n> +\tcase GST_VIDEO_ORIENTATION_180:\n> +\t\treturn Transform::Rot180;\n> +\tcase GST_VIDEO_ORIENTATION_90L:\n> +\t\treturn Transform::Rot270;\n> +\tcase GST_VIDEO_ORIENTATION_HORIZ:\n> +\t\treturn Transform::HFlip;\n> +\tcase GST_VIDEO_ORIENTATION_VERT:\n> +\t\treturn Transform::VFlip;\n> +\tcase GST_VIDEO_ORIENTATION_UL_LR:\n> +\t\treturn Transform::Transpose;\n> +\tcase GST_VIDEO_ORIENTATION_UR_LL:\n> +\t\treturn Transform::Rot180Transpose;\n> +\tcase GST_VIDEO_ORIENTATION_IDENTITY:\n> +\tdefault:\n> +\t\treturn Transform::Identity;\n> +\t}\n> +}\n> +\n> +GstVideoOrientationMethod\n> +gst_libcamera_transform_to_orientation(libcamera::Transform transform)\n> +{\n> +\tswitch (transform) {\n> +\tcase Transform::Rot90:\n> +\t\treturn GST_VIDEO_ORIENTATION_90R;\n> +\tcase Transform::Rot180:\n> +\t\treturn GST_VIDEO_ORIENTATION_180;\n> +\tcase Transform::Rot270:\n> +\t\treturn GST_VIDEO_ORIENTATION_90L;\n> +\tcase Transform::HFlip:\n> +\t\treturn GST_VIDEO_ORIENTATION_HORIZ;\n> +\tcase Transform::VFlip:\n> +\t\treturn GST_VIDEO_ORIENTATION_VERT;\n> +\tcase Transform::Transpose:\n> +\t\treturn GST_VIDEO_ORIENTATION_UL_LR;\n> +\tcase Transform::Rot180Transpose:\n> +\t\treturn GST_VIDEO_ORIENTATION_UR_LL;\n> +\tcase Transform::Identity:\n> +\tdefault:\n> +\t\treturn GST_VIDEO_ORIENTATION_IDENTITY;\n> +\t}\n> +}\n> +\n> +const char *\n> +gst_libcamera_transform_to_tag_string(libcamera::Transform transform)\n> +{\n> +\tswitch (transform) {\n> +\tcase Transform::Rot90:\n> +\t\treturn \"rotate-90\";\n> +\tcase Transform::Rot180:\n> +\t\treturn \"rotate-180\";\n> +\tcase Transform::Rot270:\n> +\t\treturn \"rotate-270\";\n> +\tcase Transform::HFlip:\n> +\t\treturn \"flip-rotate-0\";\n> +\tcase Transform::VFlip:\n> +\t\treturn \"flip-rotate-180\";\n> +\tcase Transform::Transpose:\n> +\t\treturn \"flip-rotate-270\";\n> +\tcase Transform::Rot180Transpose:\n> +\t\treturn \"flip-rotate-90\";\n> +\tcase Transform::Identity:\n> +\tdefault:\n> +\t\treturn \"rotate-0\";\n> +\t}\n> +}\n> diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h\n> index fd304a8b..84d26c47 100644\n> --- a/src/gstreamer/gstlibcamera-utils.h\n> +++ b/src/gstreamer/gstlibcamera-utils.h\n> @@ -11,6 +11,7 @@\n>  #include <libcamera/camera_manager.h>\n>  #include <libcamera/controls.h>\n>  #include <libcamera/stream.h>\n> +#include <libcamera/transform.h>\n>  \n>  #include <gst/gst.h>\n>  #include <gst/video/video.h>\n> @@ -30,6 +31,10 @@ gboolean gst_task_resume(GstTask *task);\n>  #endif\n>  std::shared_ptr<libcamera::CameraManager> gst_libcamera_get_camera_manager(int &ret);\n>  \n> +libcamera::Transform gst_libcamera_orientation_to_transform(GstVideoOrientationMethod orientation);\n> +GstVideoOrientationMethod gst_libcamera_transform_to_orientation(libcamera::Transform transform);\n> +const char *gst_libcamera_transform_to_tag_string(libcamera::Transform transform);\n\nnit: Just to follow the style, I'd been adding empty line between each\ndeclaration to ease the readability. The style checker didn't allow to indent it\nfurther though.\n\n> +\n>  /**\n>   * \\class GLibLocker\n>   * \\brief A simple scoped mutex locker for GMutex\n> diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp\n> index 721b35c2..9d9437d0 100644\n> --- a/src/gstreamer/gstlibcamerasrc.cpp\n> +++ b/src/gstreamer/gstlibcamerasrc.cpp\n> @@ -147,6 +147,8 @@ struct _GstLibcameraSrc {\n>  \n>  \tgchar *camera_name;\n>  \n> +\tGstVideoOrientationMethod transform;\n> +\n>  \tGstLibcameraSrcState *state;\n>  \tGstLibcameraAllocator *allocator;\n>  \tGstFlowCombiner *flow_combiner;\n> @@ -154,7 +156,9 @@ struct _GstLibcameraSrc {\n>  \n>  enum {\n>  \tPROP_0,\n> -\tPROP_CAMERA_NAME\n> +\tPROP_CAMERA_NAME,\n> +\tPROP_TRANSFORM,\n> +\tPROP_LAST\n>  };\n>  \n>  G_DEFINE_TYPE_WITH_CODE(GstLibcameraSrc, gst_libcamera_src, GST_TYPE_ELEMENT,\n> @@ -461,6 +465,8 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n>  \tGLibRecLocker lock(&self->stream_lock);\n>  \tGstLibcameraSrcState *state = self->state;\n>  \tGstFlowReturn flow_ret = GST_FLOW_OK;\n> +\tlibcamera::Transform tag_transform;\n> +\tconst char* tag_string;\n>  \tgint ret;\n>  \n>  \tg_autoptr(GstStructure) element_caps = gst_structure_new_empty(\"caps\");\n> @@ -513,12 +519,27 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n>  \tif (flow_ret != GST_FLOW_OK)\n>  \t\tgoto done;\n>  \n> +\tstate->config_->transform =\n> +\t\tgst_libcamera_orientation_to_transform (self->transform);\n> +\n>  \t/* Validate the configuration. */\n>  \tif (state->config_->validate() == CameraConfiguration::Invalid) {\n>  \t\tflow_ret = GST_FLOW_NOT_NEGOTIATED;\n>  \t\tgoto done;\n>  \t}\n>  \n> +\ttag_transform = (gst_libcamera_orientation_to_transform (self->transform) ^\n> +\t\t\t state->config_->transform);\n> +\ttag_string = gst_libcamera_transform_to_tag_string(tag_transform);\n> +\tfor (gsize i = 0; i < state->srcpads_.size(); i++) {\n> +\t\tGstPad *srcpad = state->srcpads_[i];\n> +\t\tGstEvent *tag_event;\n> +\n> +\t\ttag_event = gst_event_new_tag(gst_tag_list_new(GST_TAG_IMAGE_ORIENTATION,\n> +\t\t\t\t\t\t\t       tag_string, NULL));\n> +\t\tgst_pad_push_event (srcpad, tag_event);\n> +\t}\n> +\n>  \tret = state->cam_->configure(state->config_.get());\n>  \tif (ret) {\n>  \t\tGST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,\n> @@ -659,6 +680,11 @@ gst_libcamera_src_set_property(GObject *object, guint prop_id,\n>  \t\tg_free(self->camera_name);\n>  \t\tself->camera_name = g_value_dup_string(value);\n>  \t\tbreak;\n> +\tcase PROP_TRANSFORM:\n> +\t\tself->transform =\n> +\t\t\tstatic_cast<GstVideoOrientationMethod>(\n> +\t\t\t\tg_value_get_enum(value));\n> +\t\tbreak;\n>  \tdefault:\n>  \t\tG_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);\n>  \t\tbreak;\n> @@ -676,6 +702,9 @@ gst_libcamera_src_get_property(GObject *object, guint prop_id, GValue *value,\n>  \tcase PROP_CAMERA_NAME:\n>  \t\tg_value_set_string(value, self->camera_name);\n>  \t\tbreak;\n> +        case PROP_TRANSFORM:\n> +                g_value_set_enum(value, self->transform);\n> +                break;\n>  \tdefault:\n>  \t\tG_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);\n>  \t\tbreak;\n> @@ -845,4 +874,14 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)\n>  \t\t\t\t\t\t  | G_PARAM_CONSTRUCT\n>  \t\t\t\t\t\t  | G_PARAM_READWRITE\n>  \t\t\t\t\t\t  | G_PARAM_STATIC_STRINGS)));\n> +\n> +\tg_object_class_install_property (object_class, PROP_TRANSFORM,\n> +\t\tg_param_spec_enum (\"transform\", \"Transform\",\n\nShouldn't this be \"video-direction\" and this patch should implement the\nGstVideoDirection interface ? Every other element in gstreamer do that.\n\n> +\t\t\t\t   \"Request a transform (rotation and/or flip).\",\n> +\t\t\t\t   GST_TYPE_VIDEO_ORIENTATION_METHOD,\n> +\t\t\t\t   GST_VIDEO_ORIENTATION_IDENTITY,\n> +\t\t\t\t   (GParamFlags)(GST_PARAM_MUTABLE_READY\n> +\t\t\t\t\t\t | G_PARAM_CONSTRUCT\n> +\t\t\t\t\t\t | G_PARAM_READWRITE\n> +\t\t\t\t\t\t | G_PARAM_STATIC_STRINGS)));\n>  }","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 6A3E5C31E9\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 13 Jun 2023 14:39:38 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A68ED61E50;\n\tTue, 13 Jun 2023 16:39:37 +0200 (CEST)","from mail-vk1-xa2d.google.com (mail-vk1-xa2d.google.com\n\t[IPv6:2607:f8b0:4864:20::a2d])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C34EC61E49\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 13 Jun 2023 16:39:35 +0200 (CEST)","by mail-vk1-xa2d.google.com with SMTP id\n\t71dfb90a1353d-46e20ab6045so210957e0c.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 13 Jun 2023 07:39:35 -0700 (PDT)","from nicolas-tpx395.localdomain ([2606:6d00:15:c623::7a9])\n\tby smtp.gmail.com with ESMTPSA id\n\tmg9-20020a056214560900b006238b6bd191sm3942644qvb.145.2023.06.13.07.39.33\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tTue, 13 Jun 2023 07:39:34 -0700 (PDT)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1686667177;\n\tbh=sWoDXQ1WBWJUUmnsFpFuoIJbdfekyjtiByMlK8QOCSU=;\n\th=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=OiZ4a8AVQNi9nDRhtGWUDb/CHqMGErtUenzEoFc3pC7fwN8t6Od1OaD8eMF1DQ0IA\n\tDhDU2T0n44tc9jYAs8R8zyHbQqkdZTO4SfbmpxV4LbqxyEWsUdQXO8/3asLc096Gml\n\tyLcSq0W9GbrNy14JXbTNYNk915zXPFXZS/7i4vYYfgWrrcG4CgGyO6RXxXxdsEnExz\n\toKO9OY7HDBHBuzE41PleWIWz1TCnhkel8QEVkb0VDk43qoWi8JfAGOnzqeB5wyvN5L\n\tqAW/22oixXJb96QaC0kx3hVuvrnUNsWGiuU6LYbHn8HsBiITg/EV7T96IL0kEbiXcU\n\t/ycQPIOzMesPQ==","v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ndufresne-ca.20221208.gappssmtp.com; s=20221208; t=1686667174;\n\tx=1689259174; \n\th=mime-version:user-agent:content-transfer-encoding:references\n\t:in-reply-to:date:to:from:subject:message-id:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=qndRzyrb6hhjyIOAJ+Bs9VpEVI9g2ARIajGD4HDYpDU=;\n\tb=YZF/CiW+SCBJQmmpcraxiAghzsZ5Kf4GhydrSLnD3w7t9NkhCz6oDUPLHGMGvfvq1c\n\t+Ec0lFtEr7jP6rIVZcTSnjiSnKKBPkXeCdO48D05jCsYXQ3Cv3DZ9vqiK+91dZK2eZI2\n\t6fnfBIArInmOXblEiDyViNeG3B6MbxFQIal6SgBjOlXZ/NcA9vpjNIJ5Zjc8rm4ix1q4\n\ttxD4owsbjGlplvFDGpwUn8yugurPFWH72A7cdY8M24n44/hxMTF1CJJVOR9dTScx8RP4\n\tV34rniv9T9ALskAsDh8JVc5ncl0T9GPLa8UQvtb6LecQAL7YB9BXux9v0NaKecGBWcMJ\n\thBLg=="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected)\n\theader.d=ndufresne-ca.20221208.gappssmtp.com\n\theader.i=@ndufresne-ca.20221208.gappssmtp.com header.b=\"YZF/CiW+\"; \n\tdkim-atps=neutral","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20221208; t=1686667174; x=1689259174;\n\th=mime-version:user-agent:content-transfer-encoding:references\n\t:in-reply-to:date:to:from:subject:message-id:x-gm-message-state:from\n\t:to:cc:subject:date:message-id:reply-to;\n\tbh=qndRzyrb6hhjyIOAJ+Bs9VpEVI9g2ARIajGD4HDYpDU=;\n\tb=UsgPggO/cSv+WmGLjDHdPoXakjJQs1XCVti65Fv7DsQCzTYa8b7cN85GIxcTtIjLrN\n\tjW6SeeAD2oK92f4GTIYKvbUW4T2oq5n9SRQvKs5ZtYo+ByBDbFljWnQ50Qatqj8fgUyb\n\tVMqBCg97GoNDhgn09kTRXvlS5Kg8FR+V3S6xvjWEa4un2K/GYRQ0x4yfWwIWBlSliWx6\n\tFCaVED8NGIUZdjd5SfNq8zIfXesmFk8AT6yYSEEAsg69stw+9uk5p941q4Yo72XsHSY6\n\tt16wWQPMVYZn3rVGXGAyyEJvPew88bvv9CbE1JM79niqkGEo3Xfj0h3X76H8/WR6yLsJ\n\tcmdg==","X-Gm-Message-State":"AC+VfDyYHipiwuKl4vuVGeSpgN/3zqh0Gl1YFkL+nsbVbPNVMO3RwSMD\n\t9QaWtCcD4DGlQXS97tVbjqvC+RhAF7Jxt6gT4u4=","X-Google-Smtp-Source":"ACHHUZ7wrkEDRSK4luoe9drnoX09guQ5UxoIiw8IQJf7Rvul6pagLFDzSL1+PKrnGlLCmqOJEkDr1A==","X-Received":"by 2002:a1f:56c1:0:b0:45e:892b:d436 with SMTP id\n\tk184-20020a1f56c1000000b0045e892bd436mr3847065vkb.12.1686667174483; \n\tTue, 13 Jun 2023 07:39:34 -0700 (PDT)","Message-ID":"<8ad4816d4be8c65ab4ef4f556f5ea4dfa4ea0286.camel@ndufresne.ca>","To":"Robert Mader <robert.mader@collabora.com>, \n\tlibcamera-devel@lists.libcamera.org","Date":"Tue, 13 Jun 2023 10:39:33 -0400","In-Reply-To":"<20230613103519.91370-3-robert.mader@collabora.com>","References":"<20230613103519.91370-1-robert.mader@collabora.com>\n\t<20230613103519.91370-3-robert.mader@collabora.com>","Content-Type":"text/plain; charset=\"UTF-8\"","Content-Transfer-Encoding":"quoted-printable","User-Agent":"Evolution 3.48.3 (3.48.3-1.fc38) ","MIME-Version":"1.0","Subject":"Re: [libcamera-devel] [PATCH 2/2] gstreamer: src: Add transform\n\tproperty","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Nicolas Dufresne via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Nicolas Dufresne <nicolas@ndufresne.ca>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":27334,"web_url":"https://patchwork.libcamera.org/comment/27334/","msgid":"<46e76b77b0d42aefaaa4c39d81dac91eb7ef0727.camel@ndufresne.ca>","date":"2023-06-13T14:58:58","subject":"Re: [libcamera-devel] [PATCH 2/2] gstreamer: src: Add transform\n\tproperty","submitter":{"id":30,"url":"https://patchwork.libcamera.org/api/people/30/","name":"Nicolas Dufresne","email":"nicolas@ndufresne.ca"},"content":"Le mardi 13 juin 2023 à 12:52 +0100, Kieran Bingham via libcamera-devel a\nécrit :\n> Quoting Robert Mader via libcamera-devel (2023-06-13 11:34:50)\n> > This allows users to request a transform using the Gstreamer\n> > equivalent. If the combined transform of the requested one and a\n> > possible rotation from the camera properties is not fully supported by\n> > the sensor, the remaining transform will be passed down to downstream\n> > elements as tag.\n> \n> 'as a tag' ? (minor could be fixed when applying)\n> \n> > \n> > The later is common for 90/270 degree rotations. Thus, a side effect of\n> > this feature is that libcamerasrc now behaves similar to pipewiresrc in\n> > regards to rotated cameras in e.g. phones, allowing apps to compensate\n> > accordingly.\n> \n> Sounds helpful!\n> \n> > \n> > Signed-off-by: Robert Mader <robert.mader@collabora.com>\n> > ---\n> >  src/gstreamer/gstlibcamera-utils.cpp | 72 ++++++++++++++++++++++++++++\n> >  src/gstreamer/gstlibcamera-utils.h   |  5 ++\n> >  src/gstreamer/gstlibcamerasrc.cpp    | 41 +++++++++++++++-\n> >  3 files changed, 117 insertions(+), 1 deletion(-)\n> > \n> > diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp\n> > index 750ec351..43ce75cb 100644\n> > --- a/src/gstreamer/gstlibcamera-utils.cpp\n> > +++ b/src/gstreamer/gstlibcamera-utils.cpp\n> > @@ -553,3 +553,75 @@ gst_libcamera_get_camera_manager(int &ret)\n> >  \n> >         return cm;\n> >  }\n> > +\n> > +libcamera::Transform\n> > +gst_libcamera_orientation_to_transform(GstVideoOrientationMethod orientation)\n> > +{\n> > +       switch (orientation) {\n> > +       case GST_VIDEO_ORIENTATION_90R:\n> > +               return Transform::Rot90;\n> > +       case GST_VIDEO_ORIENTATION_180:\n> > +               return Transform::Rot180;\n> > +       case GST_VIDEO_ORIENTATION_90L:\n> > +               return Transform::Rot270;\n> > +       case GST_VIDEO_ORIENTATION_HORIZ:\n> > +               return Transform::HFlip;\n> > +       case GST_VIDEO_ORIENTATION_VERT:\n> > +               return Transform::VFlip;\n> > +       case GST_VIDEO_ORIENTATION_UL_LR:\n> > +               return Transform::Transpose;\n> > +       case GST_VIDEO_ORIENTATION_UR_LL:\n> > +               return Transform::Rot180Transpose;\n> > +       case GST_VIDEO_ORIENTATION_IDENTITY:\n> > +       default:\n> > +               return Transform::Identity;\n> > +       }\n> > +}\n> > +\n> > +GstVideoOrientationMethod\n> > +gst_libcamera_transform_to_orientation(libcamera::Transform transform)\n> > +{\n> > +       switch (transform) {\n> > +       case Transform::Rot90:\n> > +               return GST_VIDEO_ORIENTATION_90R;\n> > +       case Transform::Rot180:\n> > +               return GST_VIDEO_ORIENTATION_180;\n> > +       case Transform::Rot270:\n> > +               return GST_VIDEO_ORIENTATION_90L;\n> > +       case Transform::HFlip:\n> > +               return GST_VIDEO_ORIENTATION_HORIZ;\n> > +       case Transform::VFlip:\n> > +               return GST_VIDEO_ORIENTATION_VERT;\n> > +       case Transform::Transpose:\n> > +               return GST_VIDEO_ORIENTATION_UL_LR;\n> > +       case Transform::Rot180Transpose:\n> > +               return GST_VIDEO_ORIENTATION_UR_LL;\n> > +       case Transform::Identity:\n> > +       default:\n> > +               return GST_VIDEO_ORIENTATION_IDENTITY;\n> > +       }\n> > +}\n> > +\n> > +const char *\n> > +gst_libcamera_transform_to_tag_string(libcamera::Transform transform)\n> > +{\n> > +       switch (transform) {\n> > +       case Transform::Rot90:\n> > +               return \"rotate-90\";\n> > +       case Transform::Rot180:\n> > +               return \"rotate-180\";\n> > +       case Transform::Rot270:\n> > +               return \"rotate-270\";\n> > +       case Transform::HFlip:\n> > +               return \"flip-rotate-0\";\n> > +       case Transform::VFlip:\n> > +               return \"flip-rotate-180\";\n> > +       case Transform::Transpose:\n> > +               return \"flip-rotate-270\";\n> > +       case Transform::Rot180Transpose:\n> > +               return \"flip-rotate-90\";\n> > +       case Transform::Identity:\n> > +       default:\n> > +               return \"rotate-0\";\n> > +       }\n> > +}\n> > diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h\n> > index fd304a8b..84d26c47 100644\n> > --- a/src/gstreamer/gstlibcamera-utils.h\n> > +++ b/src/gstreamer/gstlibcamera-utils.h\n> > @@ -11,6 +11,7 @@\n> >  #include <libcamera/camera_manager.h>\n> >  #include <libcamera/controls.h>\n> >  #include <libcamera/stream.h>\n> > +#include <libcamera/transform.h>\n> >  \n> >  #include <gst/gst.h>\n> >  #include <gst/video/video.h>\n> > @@ -30,6 +31,10 @@ gboolean gst_task_resume(GstTask *task);\n> >  #endif\n> >  std::shared_ptr<libcamera::CameraManager> gst_libcamera_get_camera_manager(int &ret);\n> >  \n> > +libcamera::Transform gst_libcamera_orientation_to_transform(GstVideoOrientationMethod orientation);\n> > +GstVideoOrientationMethod gst_libcamera_transform_to_orientation(libcamera::Transform transform);\n> > +const char *gst_libcamera_transform_to_tag_string(libcamera::Transform transform);\n> > +\n> >  /**\n> >   * \\class GLibLocker\n> >   * \\brief A simple scoped mutex locker for GMutex\n> > diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp\n> > index 721b35c2..9d9437d0 100644\n> > --- a/src/gstreamer/gstlibcamerasrc.cpp\n> > +++ b/src/gstreamer/gstlibcamerasrc.cpp\n> > @@ -147,6 +147,8 @@ struct _GstLibcameraSrc {\n> >  \n> >         gchar *camera_name;\n> >  \n> > +       GstVideoOrientationMethod transform;\n> > +\n> >         GstLibcameraSrcState *state;\n> >         GstLibcameraAllocator *allocator;\n> >         GstFlowCombiner *flow_combiner;\n> > @@ -154,7 +156,9 @@ struct _GstLibcameraSrc {\n> >  \n> >  enum {\n> >         PROP_0,\n> > -       PROP_CAMERA_NAME\n> > +       PROP_CAMERA_NAME,\n> > +       PROP_TRANSFORM,\n> > +       PROP_LAST\n> >  };\n> >  \n> >  G_DEFINE_TYPE_WITH_CODE(GstLibcameraSrc, gst_libcamera_src, GST_TYPE_ELEMENT,\n> > @@ -461,6 +465,8 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n> >         GLibRecLocker lock(&self->stream_lock);\n> >         GstLibcameraSrcState *state = self->state;\n> >         GstFlowReturn flow_ret = GST_FLOW_OK;\n> > +       libcamera::Transform tag_transform;\n> > +       const char* tag_string;\n> >         gint ret;\n> >  \n> >         g_autoptr(GstStructure) element_caps = gst_structure_new_empty(\"caps\");\n> > @@ -513,12 +519,27 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n> >         if (flow_ret != GST_FLOW_OK)\n> >                 goto done;\n> >  \n> > +       state->config_->transform =\n> > +               gst_libcamera_orientation_to_transform (self->transform);\n> > +\n> \n> Ooof I misred the function as 'gst (libcamera_orientation) to transform'\n> instead of ' gst libcamera \"orientation\" to \"transform\" '\n> \n> So I think this sounds right. I just need to remember that the\n> gst_libcamera is the whole prefix.\n\ns/prefix/namespace\n\n> \n> \n> >         /* Validate the configuration. */\n> >         if (state->config_->validate() == CameraConfiguration::Invalid) {\n> >                 flow_ret = GST_FLOW_NOT_NEGOTIATED;\n> >                 goto done;\n> >         }\n> >  \n> \n> Given the use of the ^ operator here, which is a little bit of C++\n> magic, it's probably worth a comment to future readers to say that this\n> block identifies the delta between what was requested and what was\n> possible to expose the actions that still need to be taken. (As I\n> understand it).\n> \n> Is it clear that the 'tag' is an action still needed to take rather than a\n> description of what the image contains in normal gstreamer elements? \n> \n> (Unless I've got it backwards of course, which is entirely possible).\n\nIts seems a little complicated, I guess it worth digging further. We have:\n\n1. libcamera transform: no idea, is that the transform needed to make the image\nstraight ?\n2. Gst orientation-tag: This is what the video will look like if you render it\nas-is without any transformation.\n3. video-direction property: This is a transform the element will apply to the\nimage before delivering it\n\nMy thinking is that in normal condition you expect the image to come out\nstraight. So if the IPA can correct it, we should by default (video-\norientation=auto) just make it happen. No tags, nothing, just a up straight\nimage.\n\nIf we can't transform in the IPA, then having video-direction=default should\nprobably lead to having one of image-orientation tag or\nGstVideoAffineTransformationMeta attached to buffers (can't use both at the same\ntime). This allows a downstream element to correct it.\n\nNow, if we explicitly ask for a video-direction, that's our call. It cold be\nthat a) its not supported. But if its supported, we need to make a choice. If we\nask for video-direction=90r as an example, and the video from the IPA is already\nthat way, maybe it would be logical to just leave it like this. But in this\ncase, there should be no image-orientation tag, since this is the deliberate\ndirection the user wanted to display.\n\nWith the logic, case a) can be compensated by calculating the oriention the\nvideo will look like if 90r was wanted but could be be applied.\n\nThat also brings me to, maybe you should make two patches. First patch to\nintroduce image-orientation, and second patch to add the video-direction\nproperty (using the interface I mention of course) ?\n> \n> \n> > +       tag_transform = (gst_libcamera_orientation_to_transform (self->transform) ^\n> > +                        state->config_->transform);\n> > +       tag_string = gst_libcamera_transform_to_tag_string(tag_transform);\n> > +       for (gsize i = 0; i < state->srcpads_.size(); i++) {\n> > +               GstPad *srcpad = state->srcpads_[i];\n> > +               GstEvent *tag_event;\n> > +\n> > +               tag_event = gst_event_new_tag(gst_tag_list_new(GST_TAG_IMAGE_ORIENTATION,\n> > +                                                              tag_string, NULL));\n> > +               gst_pad_push_event (srcpad, tag_event);\n> > +       }\n> > +\n> >         ret = state->cam_->configure(state->config_.get());\n> >         if (ret) {\n> >                 GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,\n> > @@ -659,6 +680,11 @@ gst_libcamera_src_set_property(GObject *object, guint prop_id,\n> >                 g_free(self->camera_name);\n> >                 self->camera_name = g_value_dup_string(value);\n> >                 break;\n> > +       case PROP_TRANSFORM:\n> > +               self->transform =\n> > +                       static_cast<GstVideoOrientationMethod>(\n> > +                               g_value_get_enum(value));\n> > +               break;\n> >         default:\n> >                 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);\n> >                 break;\n> > @@ -676,6 +702,9 @@ gst_libcamera_src_get_property(GObject *object, guint prop_id, GValue *value,\n> >         case PROP_CAMERA_NAME:\n> >                 g_value_set_string(value, self->camera_name);\n> >                 break;\n> > +        case PROP_TRANSFORM:\n> > +                g_value_set_enum(value, self->transform);\n> > +                break;\n> \n> Indentation issues in this one ^.\n> \n> >         default:\n> >                 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);\n> >                 break;\n> > @@ -845,4 +874,14 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)\n> >                                                   | G_PARAM_CONSTRUCT\n> >                                                   | G_PARAM_READWRITE\n> >                                                   | G_PARAM_STATIC_STRINGS)));\n> > +\n> \n> I know the commit describes that transforms that couldn't be applied\n> will be passed in a tag, but it is probably worth documenting that here\n> too where the property is registered for future developers/readers..\n> (Unless that's just standard, expected and documented gstreamer\n> behaviour?)\n> \n> \n> \n> > +       g_object_class_install_property (object_class, PROP_TRANSFORM,\n> > +               g_param_spec_enum (\"transform\", \"Transform\",\n> > +                                  \"Request a transform (rotation and/or flip).\",\n> > +                                  GST_TYPE_VIDEO_ORIENTATION_METHOD,\n> > +                                  GST_VIDEO_ORIENTATION_IDENTITY,\n> > +                                  (GParamFlags)(GST_PARAM_MUTABLE_READY\n> > +                                                | G_PARAM_CONSTRUCT\n> > +                                                | G_PARAM_READWRITE\n> > +                                                | G_PARAM_STATIC_STRINGS)));\n> >  }\n> > -- \n> > 2.41.0\n> >","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 5426CBD78E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 13 Jun 2023 14:59:03 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C628E61E4B;\n\tTue, 13 Jun 2023 16:59:02 +0200 (CEST)","from mail-qk1-x733.google.com (mail-qk1-x733.google.com\n\t[IPv6:2607:f8b0:4864:20::733])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 05F7361E49\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 13 Jun 2023 16:59:00 +0200 (CEST)","by mail-qk1-x733.google.com with SMTP id\n\taf79cd13be357-75d5051fad3so101190185a.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 13 Jun 2023 07:59:00 -0700 (PDT)","from nicolas-tpx395.localdomain ([2606:6d00:15:c623::7a9])\n\tby smtp.gmail.com with ESMTPSA id\n\td22-20020a05620a159600b0075902dffce7sm3646930qkk.100.2023.06.13.07.58.59\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tTue, 13 Jun 2023 07:58:59 -0700 (PDT)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1686668342;\n\tbh=fWwBl9eQkMwiauFX6Bz08ZPFqGerSIiRdDT3STXvjfw=;\n\th=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=HQNd84Jl/Oi4qnIZ0a5Kf1bP7+RLWz/713ZRJVjozORa9T78Qs4UWS7N6HKeXT6SS\n\tKcDIgsCcRSgjNeAz3T6yHVHow70MhcMQ5ypjuIaCh+Sb8SFnA5Es1Yx+jtl+Bpvbxz\n\tWFESX8c1K6H0OHP1twocZFHSZvzZ1vcdEvSlLcVG1zbiTymekfsdvvhr97QcCI5U3K\n\tVipEX7W+puwU8hrPVbnf7K042DI98OmaxhGwIIW4M2Mw9YbpG9TjQ0vAxroeXUCMqQ\n\tbVFR1hX3Nrya/DgGUqMeVN5kcClFUhohDZEg8+RPsvV/6zJGrFo2OIzCB2N69oLAmP\n\t2TbtYYkZc93fA==","v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ndufresne-ca.20221208.gappssmtp.com; s=20221208; t=1686668340;\n\tx=1689260340; \n\th=mime-version:user-agent:content-transfer-encoding:references\n\t:in-reply-to:date:to:from:subject:message-id:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=z62GwIW4FmTIo2JxQCQMQx3nizaKXiPoZNQdp6YiJ0M=;\n\tb=vt3tTMjvE29ZAXUuVMFN/9SF5JglSJlF+ZNpcEAe80omcUqNSNAJlRYLuwxEtSMhW+\n\te9asRRzd+fFkMa8j92zY3aLKWbylYRtXEkYgP8j/FqAmZcG5oH+63txFAdIQa4+2BWEJ\n\tEPFLcZ0ZIaj9Y2yRPB0gegoTg9x8SPrcbwbhG8cbtQfogh4A6UAC1KACowlei0FaV2Pz\n\tPy/f2pT/kgNINGwsE0pxU1dmP5x4WSmsTKJvb3eyKrry5SZN3sLWSWqcu3zwwumsWymi\n\tVleInFYjHv/lGxoem1aAdeqKKMcNkgIjXB8sEGlaXOyxr7+s/GKZLEfJh176OsY1K/aT\n\tNCoQ=="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected)\n\theader.d=ndufresne-ca.20221208.gappssmtp.com\n\theader.i=@ndufresne-ca.20221208.gappssmtp.com header.b=\"vt3tTMjv\"; \n\tdkim-atps=neutral","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20221208; t=1686668340; x=1689260340;\n\th=mime-version:user-agent:content-transfer-encoding:references\n\t:in-reply-to:date:to:from:subject:message-id:x-gm-message-state:from\n\t:to:cc:subject:date:message-id:reply-to;\n\tbh=z62GwIW4FmTIo2JxQCQMQx3nizaKXiPoZNQdp6YiJ0M=;\n\tb=ZyjTMURIYsMn+JDLq+8s/zBY0rmaFKHjcjHtfDMk1piohFWCc3chsUP4Y8VFd9oYk4\n\tSsek+0/FWqE+2hOD+NSm2IPi7B8/PNLGWu/lkBOeg+UeWq0rQ1+irIsLjmbxpHXNmohz\n\tvDXNtybTfBKpU2/dKjmI/uJohko1NkmZk8PnVixMVVCID2QpQjMraRCQUdd7ZtO26olg\n\tU46lemJE+3uuPClMFFE0LMOU9xG3pxS/kPYsNojzh5+v1k/AR1tO7DDlHKCYVnShrWDR\n\tGYZmf7YMXJ45e8M4AFSAhEf7SQLOVWe5NEwcKGEwvzFH/cucwXBF3qkw1UmOulbFtO8p\n\tn5Vw==","X-Gm-Message-State":"AC+VfDyPs2F11Mo2txEU+D8I8ZtnIrCqMo+R1hLEaAXCYDZ69/4vSpfA\n\tc8j1HGb3H1LioMCn8AUsutaCuw==","X-Google-Smtp-Source":"ACHHUZ7V7po2fvLqG7Dgdao/nF/xrau7iSGbd9JvuBVm2maZgwSPzwqfqN/OmQV2YLzmR3RU1erlnA==","X-Received":"by 2002:a05:620a:f0c:b0:75b:23a0:deca with SMTP id\n\tv12-20020a05620a0f0c00b0075b23a0decamr12969832qkl.72.1686668339784; \n\tTue, 13 Jun 2023 07:58:59 -0700 (PDT)","Message-ID":"<46e76b77b0d42aefaaa4c39d81dac91eb7ef0727.camel@ndufresne.ca>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>, Robert Mader\n\t<robert.mader@collabora.com>, libcamera-devel@lists.libcamera.org","Date":"Tue, 13 Jun 2023 10:58:58 -0400","In-Reply-To":"<168665716815.2889415.14346463454221033138@Monstersaurus>","References":"<20230613103519.91370-1-robert.mader@collabora.com>\n\t<20230613103519.91370-3-robert.mader@collabora.com>\n\t<168665716815.2889415.14346463454221033138@Monstersaurus>","Content-Type":"text/plain; charset=\"UTF-8\"","Content-Transfer-Encoding":"quoted-printable","User-Agent":"Evolution 3.48.3 (3.48.3-1.fc38) ","MIME-Version":"1.0","Subject":"Re: [libcamera-devel] [PATCH 2/2] gstreamer: src: Add transform\n\tproperty","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Nicolas Dufresne via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Nicolas Dufresne <nicolas@ndufresne.ca>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":27338,"web_url":"https://patchwork.libcamera.org/comment/27338/","msgid":"<131657df-eab4-6760-90d1-408794db89b5@ideasonboard.com>","date":"2023-06-13T17:37:36","subject":"Re: [libcamera-devel] [PATCH 2/2] gstreamer: src: Add transform\n\tproperty","submitter":{"id":86,"url":"https://patchwork.libcamera.org/api/people/86/","name":"Umang Jain","email":"umang.jain@ideasonboard.com"},"content":"Hi Robert,\n\nOn 6/13/23 4:04 PM, Robert Mader via libcamera-devel wrote:\n> This allows users to request a transform using the Gstreamer\n> equivalent. If the combined transform of the requested one and a\n> possible rotation from the camera properties is not fully supported by\n> the sensor, the remaining transform will be passed down to downstream\n> elements as tag.\n>\n> The later is common for 90/270 degree rotations. Thus, a side effect of\n> this feature is that libcamerasrc now behaves similar to pipewiresrc in\n> regards to rotated cameras in e.g. phones, allowing apps to compensate\n> accordingly.\n>\n> Signed-off-by: Robert Mader <robert.mader@collabora.com>\n> ---\n>   src/gstreamer/gstlibcamera-utils.cpp | 72 ++++++++++++++++++++++++++++\n>   src/gstreamer/gstlibcamera-utils.h   |  5 ++\n>   src/gstreamer/gstlibcamerasrc.cpp    | 41 +++++++++++++++-\n>   3 files changed, 117 insertions(+), 1 deletion(-)\n>\n> diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp\n> index 750ec351..43ce75cb 100644\n> --- a/src/gstreamer/gstlibcamera-utils.cpp\n> +++ b/src/gstreamer/gstlibcamera-utils.cpp\n> @@ -553,3 +553,75 @@ gst_libcamera_get_camera_manager(int &ret)\n>   \n>   \treturn cm;\n>   }\n> +\n> +libcamera::Transform\n> +gst_libcamera_orientation_to_transform(GstVideoOrientationMethod orientation)\n> +{\n> +\tswitch (orientation) {\n> +\tcase GST_VIDEO_ORIENTATION_90R:\n> +\t\treturn Transform::Rot90;\n> +\tcase GST_VIDEO_ORIENTATION_180:\n> +\t\treturn Transform::Rot180;\n> +\tcase GST_VIDEO_ORIENTATION_90L:\n> +\t\treturn Transform::Rot270;\n> +\tcase GST_VIDEO_ORIENTATION_HORIZ:\n> +\t\treturn Transform::HFlip;\n> +\tcase GST_VIDEO_ORIENTATION_VERT:\n> +\t\treturn Transform::VFlip;\n> +\tcase GST_VIDEO_ORIENTATION_UL_LR:\n> +\t\treturn Transform::Transpose;\n> +\tcase GST_VIDEO_ORIENTATION_UR_LL:\n> +\t\treturn Transform::Rot180Transpose;\n> +\tcase GST_VIDEO_ORIENTATION_IDENTITY:\n> +\tdefault:\n> +\t\treturn Transform::Identity;\n> +\t}\n> +}\n> +\n> +GstVideoOrientationMethod\n> +gst_libcamera_transform_to_orientation(libcamera::Transform transform)\n> +{\n> +\tswitch (transform) {\n> +\tcase Transform::Rot90:\n> +\t\treturn GST_VIDEO_ORIENTATION_90R;\n> +\tcase Transform::Rot180:\n> +\t\treturn GST_VIDEO_ORIENTATION_180;\n> +\tcase Transform::Rot270:\n> +\t\treturn GST_VIDEO_ORIENTATION_90L;\n> +\tcase Transform::HFlip:\n> +\t\treturn GST_VIDEO_ORIENTATION_HORIZ;\n> +\tcase Transform::VFlip:\n> +\t\treturn GST_VIDEO_ORIENTATION_VERT;\n> +\tcase Transform::Transpose:\n> +\t\treturn GST_VIDEO_ORIENTATION_UL_LR;\n> +\tcase Transform::Rot180Transpose:\n> +\t\treturn GST_VIDEO_ORIENTATION_UR_LL;\n> +\tcase Transform::Identity:\n> +\tdefault:\n> +\t\treturn GST_VIDEO_ORIENTATION_IDENTITY;\n> +\t}\n> +}\n> +\n> +const char *\n> +gst_libcamera_transform_to_tag_string(libcamera::Transform transform)\n> +{\n> +\tswitch (transform) {\n> +\tcase Transform::Rot90:\n> +\t\treturn \"rotate-90\";\n> +\tcase Transform::Rot180:\n> +\t\treturn \"rotate-180\";\n> +\tcase Transform::Rot270:\n> +\t\treturn \"rotate-270\";\n> +\tcase Transform::HFlip:\n> +\t\treturn \"flip-rotate-0\";\n> +\tcase Transform::VFlip:\n> +\t\treturn \"flip-rotate-180\";\n> +\tcase Transform::Transpose:\n> +\t\treturn \"flip-rotate-270\";\n> +\tcase Transform::Rot180Transpose:\n> +\t\treturn \"flip-rotate-90\";\n> +\tcase Transform::Identity:\n> +\tdefault:\n> +\t\treturn \"rotate-0\";\n> +\t}\n> +}\n> diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h\n> index fd304a8b..84d26c47 100644\n> --- a/src/gstreamer/gstlibcamera-utils.h\n> +++ b/src/gstreamer/gstlibcamera-utils.h\n> @@ -11,6 +11,7 @@\n>   #include <libcamera/camera_manager.h>\n>   #include <libcamera/controls.h>\n>   #include <libcamera/stream.h>\n> +#include <libcamera/transform.h>\n>   \n>   #include <gst/gst.h>\n>   #include <gst/video/video.h>\n> @@ -30,6 +31,10 @@ gboolean gst_task_resume(GstTask *task);\n>   #endif\n>   std::shared_ptr<libcamera::CameraManager> gst_libcamera_get_camera_manager(int &ret);\n>   \n> +libcamera::Transform gst_libcamera_orientation_to_transform(GstVideoOrientationMethod orientation);\n> +GstVideoOrientationMethod gst_libcamera_transform_to_orientation(libcamera::Transform transform);\n> +const char *gst_libcamera_transform_to_tag_string(libcamera::Transform transform);\n> +\n>   /**\n>    * \\class GLibLocker\n>    * \\brief A simple scoped mutex locker for GMutex\n> diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp\n> index 721b35c2..9d9437d0 100644\n> --- a/src/gstreamer/gstlibcamerasrc.cpp\n> +++ b/src/gstreamer/gstlibcamerasrc.cpp\n> @@ -147,6 +147,8 @@ struct _GstLibcameraSrc {\n>   \n>   \tgchar *camera_name;\n>   \n> +\tGstVideoOrientationMethod transform;\n> +\n>   \tGstLibcameraSrcState *state;\n>   \tGstLibcameraAllocator *allocator;\n>   \tGstFlowCombiner *flow_combiner;\n> @@ -154,7 +156,9 @@ struct _GstLibcameraSrc {\n>   \n>   enum {\n>   \tPROP_0,\n> -\tPROP_CAMERA_NAME\n> +\tPROP_CAMERA_NAME,\n> +\tPROP_TRANSFORM,\n> +\tPROP_LAST\n>   };\n>   \n>   G_DEFINE_TYPE_WITH_CODE(GstLibcameraSrc, gst_libcamera_src, GST_TYPE_ELEMENT,\n> @@ -461,6 +465,8 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n>   \tGLibRecLocker lock(&self->stream_lock);\n>   \tGstLibcameraSrcState *state = self->state;\n>   \tGstFlowReturn flow_ret = GST_FLOW_OK;\n> +\tlibcamera::Transform tag_transform;\n> +\tconst char* tag_string;\n>   \tgint ret;\n>   \n>   \tg_autoptr(GstStructure) element_caps = gst_structure_new_empty(\"caps\");\n> @@ -513,12 +519,27 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,\n>   \tif (flow_ret != GST_FLOW_OK)\n>   \t\tgoto done;\n>   \n> +\tstate->config_->transform =\n> +\t\tgst_libcamera_orientation_to_transform (self->transform);\n> +\n>   \t/* Validate the configuration. */\n>   \tif (state->config_->validate() == CameraConfiguration::Invalid) {\n>   \t\tflow_ret = GST_FLOW_NOT_NEGOTIATED;\n>   \t\tgoto done;\n>   \t}\n>   \n> +\ttag_transform = (gst_libcamera_orientation_to_transform (self->transform) ^\n> +\t\t\t state->config_->transform);\n> +\ttag_string = gst_libcamera_transform_to_tag_string(tag_transform);\n> +\tfor (gsize i = 0; i < state->srcpads_.size(); i++) {\n> +\t\tGstPad *srcpad = state->srcpads_[i];\n> +\t\tGstEvent *tag_event;\n> +\n> +\t\ttag_event = gst_event_new_tag(gst_tag_list_new(GST_TAG_IMAGE_ORIENTATION,\n> +\t\t\t\t\t\t\t       tag_string, NULL));\n\nHow about separating this to a dedicated function with ability to add \nmore new tags easily for the downstream pipeline?\n\n> +\t\tgst_pad_push_event (srcpad, tag_event);\n> +\t}\n> +\n>   \tret = state->cam_->configure(state->config_.get());\n>   \tif (ret) {\n>   \t\tGST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,\n> @@ -659,6 +680,11 @@ gst_libcamera_src_set_property(GObject *object, guint prop_id,\n>   \t\tg_free(self->camera_name);\n>   \t\tself->camera_name = g_value_dup_string(value);\n>   \t\tbreak;\n> +\tcase PROP_TRANSFORM:\n> +\t\tself->transform =\n> +\t\t\tstatic_cast<GstVideoOrientationMethod>(\n> +\t\t\t\tg_value_get_enum(value));\n> +\t\tbreak;\n>   \tdefault:\n>   \t\tG_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);\n>   \t\tbreak;\n> @@ -676,6 +702,9 @@ gst_libcamera_src_get_property(GObject *object, guint prop_id, GValue *value,\n>   \tcase PROP_CAMERA_NAME:\n>   \t\tg_value_set_string(value, self->camera_name);\n>   \t\tbreak;\n> +        case PROP_TRANSFORM:\n> +                g_value_set_enum(value, self->transform);\n> +                break;\n>   \tdefault:\n>   \t\tG_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);\n>   \t\tbreak;\n> @@ -845,4 +874,14 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)\n>   \t\t\t\t\t\t  | G_PARAM_CONSTRUCT\n>   \t\t\t\t\t\t  | G_PARAM_READWRITE\n>   \t\t\t\t\t\t  | G_PARAM_STATIC_STRINGS)));\n> +\n> +\tg_object_class_install_property (object_class, PROP_TRANSFORM,\n> +\t\tg_param_spec_enum (\"transform\", \"Transform\",\n> +\t\t\t\t   \"Request a transform (rotation and/or flip).\",\n> +\t\t\t\t   GST_TYPE_VIDEO_ORIENTATION_METHOD,\n> +\t\t\t\t   GST_VIDEO_ORIENTATION_IDENTITY,\n> +\t\t\t\t   (GParamFlags)(GST_PARAM_MUTABLE_READY\n> +\t\t\t\t\t\t | G_PARAM_CONSTRUCT\n> +\t\t\t\t\t\t | G_PARAM_READWRITE\n> +\t\t\t\t\t\t | G_PARAM_STATIC_STRINGS)));\n>   }","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id A529CBD78E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 13 Jun 2023 17:37:43 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1C5B361E50;\n\tTue, 13 Jun 2023 19:37:43 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3FF2161E49\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 13 Jun 2023 19:37:42 +0200 (CEST)","from [192.168.1.108] (unknown [103.86.18.143])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 51E3C1AE;\n\tTue, 13 Jun 2023 19:37:11 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1686677863;\n\tbh=47hjwQLcpcDoe4v/hrpnaUQ3sMwvf38h+v2GUaQvRvI=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=bHBvuWClfLv5VFIeN581gItZvj0dAZPYb4r1qtD6bMz4AmUkvI3wyMjuzf1NAkrEo\n\tLXPgXYvgjJZulYnzXbO3nfg65GOSFabptfbVe9rCO9OzSV/Kgr/BX9ky9Vuq9qsR/7\n\t8Odz+ygBiJy/EXnDkDSjAcGnVXMy7xYXgAA1hZvkFrZbJzaLQaNL0StdQRWOGXdWLD\n\t891ocRe3XyXhtWfURBnxronpqYDpaVLfVj3rzuB3eiutforhQGcbZlkOZiV+hrW9/s\n\tlwY2DKxukX62dNQPEZLezuUfOjMEBe8YrMqtXqalowclKUQtf8Dfn8AKOZHRGaOUxd\n\tJPhe0FroKE7Gg==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1686677831;\n\tbh=47hjwQLcpcDoe4v/hrpnaUQ3sMwvf38h+v2GUaQvRvI=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=vNfdfg+qcKXUSrCUSZL6sG7hSjIFWn8bYwZ39Rubuwq9dvkDYAOR6EHEij13C7FIz\n\tR5Fr1A1XhOZDguGnIXVXgifb8dU2uPOEup6FPAHBrBE2pea2UKcgAdnwATCDWE8oIi\n\tyODPKJ8Dtb7LLmdO5ZPxgxTd3gbXLxAf4jpgFT/g="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"vNfdfg+q\"; dkim-atps=neutral","Message-ID":"<131657df-eab4-6760-90d1-408794db89b5@ideasonboard.com>","Date":"Tue, 13 Jun 2023 23:07:36 +0530","MIME-Version":"1.0","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101\n\tThunderbird/102.7.1","Content-Language":"en-US","To":"Robert Mader <robert.mader@collabora.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20230613103519.91370-1-robert.mader@collabora.com>\n\t<20230613103519.91370-3-robert.mader@collabora.com>","In-Reply-To":"<20230613103519.91370-3-robert.mader@collabora.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [PATCH 2/2] gstreamer: src: Add transform\n\tproperty","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Umang Jain via libcamera-devel <libcamera-devel@lists.libcamera.org>","Reply-To":"Umang Jain <umang.jain@ideasonboard.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]