diff --git a/src/gstreamer/gstlibcamera-controls.cpp.in b/src/gstreamer/gstlibcamera-controls.cpp.in
index 1bc781cc..6faf3ee7 100644
--- a/src/gstreamer/gstlibcamera-controls.cpp.in
+++ b/src/gstreamer/gstlibcamera-controls.cpp.in
@@ -14,56 +14,10 @@
 #include <libcamera/geometry.h>
 
 #include "gstlibcamera-controls.h"
+#include "gstlibcamera-utils.h"
 
 using namespace libcamera;
 
-static void value_set_point(GValue *value, const Point &point)
-{
-	GValue x = G_VALUE_INIT;
-	g_value_init(&x, G_TYPE_INT);
-	g_value_set_int(&x, point.x);
-	gst_value_array_append_and_take_value(value, &x);
-
-	GValue y = G_VALUE_INIT;
-	g_value_init(&y, G_TYPE_INT);
-	g_value_set_int(&y, point.y);
-	gst_value_array_append_and_take_value(value, &y);
-}
-
-static void value_set_size(GValue *value, const Size &size)
-{
-	GValue width = G_VALUE_INIT;
-	g_value_init(&width, G_TYPE_INT);
-	g_value_set_int(&width, size.width);
-	gst_value_array_append_and_take_value(value, &width);
-
-	GValue height = G_VALUE_INIT;
-	g_value_init(&height, G_TYPE_INT);
-	g_value_set_int(&height, size.height);
-	gst_value_array_append_and_take_value(value, &height);
-}
-
-static void value_set_rectangle(GValue *value, const Rectangle &rect)
-{
-	value_set_point(value, rect.topLeft());
-	value_set_size(value, rect.size());
-}
-
-static Rectangle value_get_rectangle(const GValue *value)
-{
-	const GValue *r;
-	r = gst_value_array_get_value(value, 0);
-	int x = g_value_get_int(r);
-	r = gst_value_array_get_value(value, 1);
-	int y = g_value_get_int(r);
-	r = gst_value_array_get_value(value, 2);
-	int w = g_value_get_int(r);
-	r = gst_value_array_get_value(value, 3);
-	int h = g_value_get_int(r);
-
-	return Rectangle(x, y, w, h);
-}
-
 {% for vendor, ctrls in controls %}
 {%- for ctrl in ctrls if ctrl.is_enum %}
 static const GEnumValue {{ ctrl.name|snake_case }}_types[] = {
@@ -179,7 +133,7 @@ bool GstCameraControls::getProperty(guint propId, GValue *value,
 			GValue element = G_VALUE_INIT;
 {%- if ctrl.is_rectangle %}
 			g_value_init(&element, GST_TYPE_PARAM_ARRAY_LIST);
-			value_set_rectangle(&element, control[i]);
+			gst_libcamera_gvalue_set_rectangle(&element, control[i]);
 {%- else %}
 			g_value_init(&element, G_TYPE_{{ ctrl.gtype|upper }});
 			g_value_set_{{ ctrl.gtype }}(&element, control[i]);
@@ -188,7 +142,7 @@ bool GstCameraControls::getProperty(guint propId, GValue *value,
 		}
 {%- else %}
 {%- if ctrl.is_rectangle %}
-		value_set_rectangle(value, control);
+		gst_libcamera_gvalue_set_rectangle(value, control);
 {%- else %}
 		g_value_set_{{ ctrl.gtype }}(value, control);
 {%- endif %}
@@ -252,7 +206,7 @@ bool GstCameraControls::setProperty(guint propId, const GValue *value,
 					  i);
 				return true;
 			}
-			values[i] = value_get_rectangle(element);
+			values[i] = gst_libcamera_gvalue_get_rectangle(element);
 {%- else %}
 			values[i] = g_value_get_{{ ctrl.gtype }}(element);
 {%- endif %}
@@ -271,7 +225,7 @@ bool GstCameraControls::setProperty(guint propId, const GValue *value,
 				  "array of size 4");
 			return true;
 		}
-		Rectangle val = value_get_rectangle(value);
+		Rectangle val = gst_libcamera_gvalue_get_rectangle(value);
 {%- else %}
 		auto val = g_value_get_{{ ctrl.gtype }}(value);
 {%- endif %}
diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp
index a548b0c1..63e00fe0 100644
--- a/src/gstreamer/gstlibcamera-utils.cpp
+++ b/src/gstreamer/gstlibcamera-utils.cpp
@@ -584,6 +584,53 @@ void gst_libcamera_framerate_to_caps(GstCaps *caps, const GstStructure *element_
 	gst_structure_set(s, "framerate", GST_TYPE_FRACTION, fps_caps_n, fps_caps_d, nullptr);
 }
 
+void gst_libcamera_gvalue_set_point(GValue *value, const Point &point)
+{
+	GValue x = G_VALUE_INIT;
+	g_value_init(&x, G_TYPE_INT);
+	g_value_set_int(&x, point.x);
+	gst_value_array_append_and_take_value(value, &x);
+
+	GValue y = G_VALUE_INIT;
+	g_value_init(&y, G_TYPE_INT);
+	g_value_set_int(&y, point.y);
+	gst_value_array_append_and_take_value(value, &y);
+}
+
+void gst_libcamera_gvalue_set_size(GValue *value, const Size &size)
+{
+	GValue width = G_VALUE_INIT;
+	g_value_init(&width, G_TYPE_INT);
+	g_value_set_int(&width, size.width);
+	gst_value_array_append_and_take_value(value, &width);
+
+	GValue height = G_VALUE_INIT;
+	g_value_init(&height, G_TYPE_INT);
+	g_value_set_int(&height, size.height);
+	gst_value_array_append_and_take_value(value, &height);
+}
+
+void gst_libcamera_gvalue_set_rectangle(GValue *value, const Rectangle &rect)
+{
+	gst_libcamera_gvalue_set_point(value, rect.topLeft());
+	gst_libcamera_gvalue_set_size(value, rect.size());
+}
+
+Rectangle gst_libcamera_gvalue_get_rectangle(const GValue *value)
+{
+	const GValue *r;
+	r = gst_value_array_get_value(value, 0);
+	int x = g_value_get_int(r);
+	r = gst_value_array_get_value(value, 1);
+	int y = g_value_get_int(r);
+	r = gst_value_array_get_value(value, 2);
+	int w = g_value_get_int(r);
+	r = gst_value_array_get_value(value, 3);
+	int h = g_value_get_int(r);
+
+	return Rectangle(x, y, w, h);
+}
+
 #if !GST_CHECK_VERSION(1, 17, 1)
 gboolean
 gst_task_resume(GstTask *task)
diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h
index 5f4e8a0f..1812be75 100644
--- a/src/gstreamer/gstlibcamera-utils.h
+++ b/src/gstreamer/gstlibcamera-utils.h
@@ -25,6 +25,10 @@ void gst_libcamera_clamp_and_set_frameduration(libcamera::ControlList &controls,
 					       const libcamera::ControlInfoMap &camera_controls,
 					       GstStructure *element_caps);
 void gst_libcamera_framerate_to_caps(GstCaps *caps, const GstStructure *element_caps);
+void gst_libcamera_gvalue_set_point(GValue *value, const libcamera::Point &point);
+void gst_libcamera_gvalue_set_size(GValue *value, const libcamera::Size &size);
+void gst_libcamera_gvalue_set_rectangle(GValue *value, const libcamera::Rectangle &rect);
+libcamera::Rectangle gst_libcamera_gvalue_get_rectangle(const GValue *value);
 
 #if !GST_CHECK_VERSION(1, 16, 0)
 static inline void gst_clear_event(GstEvent **event_ptr)
