diff --git a/.gitlab-image-tags.yml b/.gitlab-image-tags.yml
index 87c5d4726a..20dcaf5cfe 100644
--- a/.gitlab-image-tags.yml
+++ b/.gitlab-image-tags.yml
@@ -11,6 +11,6 @@ variables:
CHECKS_TAG: '2025-02-04.0'
- ABI_CHECK_TAG: '2025-03-13.0'
+ ABI_CHECK_TAG: '2025-04-01.0'
WINDOWS_TAG: '2025-02-23.0'
diff --git a/girs/Gst-1.0.gir b/girs/Gst-1.0.gir
index 6b5482e9ed..22cc9724ef 100644
--- a/girs/Gst-1.0.gir
+++ b/girs/Gst-1.0.gir
@@ -51890,6 +51890,24 @@ or GST_VALUE_UNORDERED
+
+ Used by gst_value_hash() to calculate a hash of @value.
+
+
+ %TRUE, or %FALSE if @value cannot be hashed.
+
+
+
+
+ a #GValue
+
+
+
+ a location to store the hash value
+
+
+
+
A fundamental type that describes an unordered list of #GValue
@@ -52083,8 +52101,12 @@ Free-function: g_free
a #GstValueDeserializeWithPSpecFunc
+
+ a #GstValueHashFunc
+
+
-
+
@@ -58598,6 +58620,25 @@ a reference to @v.
+
+ Compute a hash value of @value.
+#GValue considered as equals by gst_value_compare() will have the same hash value.
+
+
+ %TRUE, or %FALSE if @value cannot be hashed.
+
+
+
+
+ a #GValue to hash
+
+
+
+ a location to store the hash value
+
+
+
+
Initialises the target value to be of the same type as source and then copies
the contents from source to target.
diff --git a/subprojects/gstreamer/gst/gstvalue.c b/subprojects/gstreamer/gst/gstvalue.c
index 1aeb012c16..d8e94a72ec 100644
--- a/subprojects/gstreamer/gst/gstvalue.c
+++ b/subprojects/gstreamer/gst/gstvalue.c
@@ -3219,6 +3219,7 @@ gst_value_serialize_tag_list (const GValue * value)
static gint
compare_buffer (GstBuffer * buf1, GstBuffer * buf2)
{
+ /* keep this implementation synced with gst_hash_buffer() */
gsize size1, size2;
GstMapInfo info1, info2;
gint result, mret;
@@ -3355,6 +3356,7 @@ wrong_char:
static gint
gst_value_compare_sample (const GValue * value1, const GValue * value2)
{
+ /* keep this implementation synced with gst_value_hash_sample() */
GstBuffer *buf1 = gst_sample_get_buffer (gst_value_get_sample (value1));
GstBuffer *buf2 = gst_sample_get_buffer (gst_value_get_sample (value2));
@@ -6139,7 +6141,8 @@ gst_value_compare (const GValue * value1, const GValue * value2)
value2_is_list = G_VALUE_TYPE (value2) == GST_TYPE_LIST;
/* Special cases: lists and scalar values ("{ 1 }" and "1" are equal),
- as well as lists and ranges ("{ 1, 2 }" and "[ 1, 2 ]" are equal) */
+ as well as lists and ranges ("{ 1, 2 }" and "[ 1, 2 ]" are equal).
+ Make sure to update gst_value_hash() when adding new special cases. */
if (value1_is_list && !value2_is_list) {
gint i, n, ret;
@@ -8059,6 +8062,548 @@ gst_value_compare_flagset (const GValue * value1, const GValue * value2)
return GST_VALUE_UNORDERED;
}
+static gboolean
+gst_value_hash_int (const GValue * v, guint * res)
+{
+ gint i = g_value_get_int (v);
+ *res = g_int_hash ((gconstpointer) & i);
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_int64 (const GValue * v, guint * res)
+{
+ gint64 i = g_value_get_int64 (v);
+ *res = g_int64_hash ((gconstpointer) & i);
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_long (const GValue * v, guint * res)
+{
+ glong l = g_value_get_long (v);
+
+#if GLIB_SIZEOF_LONG == 4
+ *res = g_int_hash ((gconstpointer) & l);
+#else
+ *res = g_int64_hash ((gconstpointer) & l);
+#endif
+
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_uint (const GValue * v, guint * res)
+{
+ guint u = g_value_get_uint (v);
+ *res = g_int_hash ((gconstpointer) & u);
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_uint64 (const GValue * v, guint * res)
+{
+ guint64 u = g_value_get_uint64 (v);
+ *res = g_int64_hash ((gconstpointer) & u);
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_ulong (const GValue * v, guint * res)
+{
+ gulong u = g_value_get_ulong (v);
+ *res = g_double_hash ((gconstpointer) & u);
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_uchar (const GValue * v, guint * res)
+{
+ guchar u = g_value_get_uchar (v);
+ gint i = u;
+ *res = g_int_hash ((gconstpointer) & i);
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_gtype (const GValue * v, guint * res)
+{
+ GType t = g_value_get_gtype (v);
+
+#if GLIB_SIZEOF_SIZE_T == 4
+ *res = g_int_hash ((gconstpointer) & t);
+#else
+ *res = g_int64_hash ((gconstpointer) & t);
+#endif
+
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_double (const GValue * v, guint * res)
+{
+ gdouble d = g_value_get_double (v);
+ *res = g_double_hash ((gconstpointer) & d);
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_float (const GValue * v, guint * res)
+{
+ gfloat f = g_value_get_float (v);
+ *res = g_int_hash ((gconstpointer) & f);
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_string (const GValue * v, guint * res)
+{
+ const gchar *s = g_value_get_string (v);
+ *res = g_str_hash (s);
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_boolean (const GValue * v, guint * res)
+{
+ gboolean b = g_value_get_boolean (v);
+ *res = b ? 1 : 0;
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_enum (const GValue * v, guint * res)
+{
+ gint e = g_value_get_enum (v);
+ *res = g_int_hash ((gconstpointer) & e);
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_gflags (const GValue * v, guint * res)
+{
+ guint f = g_value_get_flags (v);
+ *res = g_int_hash ((gconstpointer) & f);
+ return TRUE;
+}
+
+/* helper to combine hash when hashing ordered container */
+static void
+combine_hash_ordered (guint * hash, guint value_hash)
+{
+ /* perform a rotating shift to account for the position in the container */
+ *hash = ((*hash << 5) | (*hash >> 27)) ^ value_hash;
+}
+
+static gboolean
+gst_value_hash_int_range (const GValue * v, guint * res)
+{
+ gint min = gst_value_get_int_range_min (v);
+ gint max = gst_value_get_int_range_max (v);
+ gint step = gst_value_get_int_range_step (v);
+ gint i;
+
+ *res = 0;
+ /* range and list are considered as equal if they contain the same elements,
+ so they should have the same hash. Lists are not ordered so do not account
+ for element position when hashing. */
+ for (i = min; i <= max; i += step) {
+ *res ^= g_int_hash ((gconstpointer) & i);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_int64_range (const GValue * v, guint * res)
+{
+ gint64 min = gst_value_get_int64_range_min (v);
+ gint64 max = gst_value_get_int64_range_max (v);
+ gint64 step = gst_value_get_int64_range_step (v);
+ gint64 i;
+
+ *res = 0;
+ for (i = min; i <= max; i += step) {
+ *res ^= g_int64_hash ((gconstpointer) & i);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_double_range (const GValue * v, guint * res)
+{
+ gdouble min = gst_value_get_double_range_min (v);
+ gdouble max = gst_value_get_double_range_max (v);
+
+ *res = g_double_hash ((gconstpointer) & min) ^ g_double_hash ((gconstpointer)
+ & max);
+
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_g_value_array (const GValue * v, guint * res)
+{
+ guint i = 0;
+ GValueArray *array = g_value_get_boxed (v);
+
+ *res = 0;
+ for (i = 0; i < array->n_values; i++) {
+ guint value_hash;
+ if (!gst_value_hash (&array->values[i], &value_hash))
+ return FALSE;
+ combine_hash_ordered (res, value_hash);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_hash_buffer (GstBuffer * buf, guint * res)
+{
+ GstBufferMapInfo map;
+ guint i;
+
+ *res = 0;
+
+ if (!gst_buffer_map (buf, &map, GST_MAP_READ)) {
+ return FALSE;
+ }
+
+ for (i = 0; i < map.size; i++) {
+ combine_hash_ordered (res, map.data[i]);
+ }
+
+ gst_buffer_unmap (buf, &map);
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_buffer (const GValue * v, guint * res)
+{
+ GstBuffer *buf = gst_value_get_buffer (v);
+
+ /* compare_buffer() only compares the buffer content */
+ return gst_hash_buffer (buf, res);
+}
+
+static gboolean
+gst_value_hash_sample (const GValue * v, guint * res)
+{
+ GstSample *sample = gst_value_get_sample (v);
+ GstBuffer *buf = gst_sample_get_buffer (sample);
+
+ /* gst_value_compare_sample() only compares buffers */
+ return gst_hash_buffer (buf, res);
+}
+
+static gboolean
+gst_value_hash_fraction (const GValue * v, guint * res)
+{
+ gint num = gst_value_get_fraction_numerator (v);
+ gint den = gst_value_get_fraction_denominator (v);
+ gfloat div = ((gfloat) num) / ((gfloat) den);
+
+ *res = g_int_hash ((gconstpointer) & div);
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_fraction_range (const GValue * v, guint * res)
+{
+ const GValue *min = gst_value_get_fraction_range_min (v);
+ const GValue *max = gst_value_get_fraction_range_max (v);
+ gint num, den;
+ gfloat div;
+
+ num = gst_value_get_fraction_numerator (min);
+ den = gst_value_get_fraction_denominator (min);
+ div = ((gfloat) num) / ((gfloat) den);
+ *res = g_int_hash ((gconstpointer) & div);
+
+ num = gst_value_get_fraction_numerator (max);
+ den = gst_value_get_fraction_denominator (max);
+ div = ((gfloat) num) / ((gfloat) den);
+ *res ^= g_int_hash ((gconstpointer) & div);
+
+ return TRUE;
+}
+
+static gboolean
+hash_structure_foreach (const GstIdStr * fieldname,
+ const GValue * value, gpointer user_data)
+{
+ gint *hash = user_data;
+ guint value_hash;
+
+ *hash ^= g_str_hash (gst_id_str_as_str (fieldname));
+ if (!gst_value_hash (value, &value_hash))
+ return FALSE;
+ *hash ^= value_hash;
+
+ return TRUE;
+}
+
+static gboolean
+gst_hash_structure (const GstStructure * s, guint * res)
+{
+ *res = g_str_hash (gst_structure_get_name (s));
+ return gst_structure_foreach_id_str (s, hash_structure_foreach, res);
+}
+
+static gboolean
+gst_value_hash_structure (const GValue * v, guint * res)
+{
+ const GstStructure *s = gst_value_get_structure (v);
+
+ return gst_hash_structure (s, res);
+}
+
+static gboolean
+gst_hash_caps_features (const GstCapsFeatures * features, guint * res)
+{
+ guint i;
+
+ *res = 0;
+
+ for (i = 0; i < gst_caps_features_get_size (features); i++) {
+ const gchar *feature = gst_caps_features_get_nth (features, i);
+ *res ^= g_str_hash (feature);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_caps_features (const GValue * v, guint * res)
+{
+ const GstCapsFeatures *features = gst_value_get_caps_features (v);
+ return gst_hash_caps_features (features, res);
+}
+
+static gboolean
+gst_value_hash_caps (const GValue * v, guint * res)
+{
+ const GstCaps *caps = gst_value_get_caps (v);
+ guint i;
+ guint n = gst_caps_get_size (caps);
+
+ *res = 0;
+ for (i = 0; i < n; i++) {
+ GstCapsFeatures *features = gst_caps_get_features (caps, i);
+ guint hash;
+
+ if (!gst_hash_structure (gst_caps_get_structure (caps, i), &hash))
+ return FALSE;
+ *res ^= hash;
+
+ if (!gst_hash_caps_features (features, &hash))
+ return FALSE;
+ *res ^= hash;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_tag_list (const GValue * v, guint * res)
+{
+ const GstTagList *tags = g_value_get_boxed (v);
+ const GstStructure *s;
+
+ s = _gst_tag_list_structure (tags);
+ return gst_hash_structure (s, res);
+}
+
+static gboolean
+gst_value_hash_date (const GValue * v, guint * res)
+{
+ const GDate *date = g_value_get_boxed (v);
+ guint32 julian, year, month, day;
+
+ julian = g_date_get_julian (date);
+ if (julian) {
+ *res = g_int_hash ((gconstpointer) & julian);
+ return TRUE;
+ }
+
+ year = g_date_get_year (date);
+ month = g_date_get_month (date);
+ day = g_date_get_day (date);
+
+ *res = g_int_hash ((gconstpointer) & year);
+ combine_hash_ordered (res, g_int_hash ((gconstpointer) & month));
+ combine_hash_ordered (res, g_int_hash ((gconstpointer) & day));
+
+ return TRUE;
+}
+
+static gboolean
+gst_hash_g_date_time (const GDateTime * dt, guint * res)
+{
+ gint64 unix_time;
+
+ unix_time = g_date_time_to_unix ((GDateTime *) dt);
+ *res = g_int64_hash ((gconstpointer) & unix_time);
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_g_date_time (const GValue * v, guint * res)
+{
+ GDateTime *dt = g_value_get_boxed (v);
+ return gst_hash_g_date_time (dt, res);
+}
+
+static gboolean
+gst_value_hash_date_time (const GValue * v, guint * res)
+{
+ GstDateTime *dt = g_value_get_boxed (v);
+ GDateTime *gdt;
+ gboolean ret_val;
+
+ gdt = gst_date_time_to_g_date_time (dt);
+ ret_val = gst_hash_g_date_time (gdt, res);
+
+ g_date_time_unref (gdt);
+ return ret_val;
+}
+
+static gboolean
+gst_value_hash_bytes (const GValue * v, guint * res)
+{
+ GBytes *bytes = g_value_get_boxed (v);
+ const guint *data;
+ gsize size, i;
+
+ *res = 0;
+ data = g_bytes_get_data (bytes, &size);
+
+ for (i = 0; i < size; i++) {
+ combine_hash_ordered (res, data[i]);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_bitmask (const GValue * v, guint * res)
+{
+ guint64 mask = gst_value_get_bitmask (v);
+ *res = g_int64_hash ((gconstpointer) & mask);
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_flagset (const GValue * v, guint * res)
+{
+ guint flags = gst_value_get_flagset_flags (v);
+ guint mask = gst_value_get_flagset_mask (v);
+ guint value = flags & mask;
+ *res = g_int_hash ((gconstpointer) & value);
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_segment (const GValue * v, guint * res)
+{
+ GstSegment *segment = g_value_get_boxed (v);
+
+ *res = g_int_hash ((gconstpointer) & segment->flags);
+ combine_hash_ordered (res, g_double_hash ((gconstpointer) & segment->rate));
+ combine_hash_ordered (res,
+ g_double_hash ((gconstpointer) & segment->applied_rate));
+ combine_hash_ordered (res, g_int_hash ((gconstpointer) & segment->format));
+ combine_hash_ordered (res, g_int64_hash ((gconstpointer) & segment->base));
+ combine_hash_ordered (res, g_int64_hash ((gconstpointer) & segment->offset));
+ combine_hash_ordered (res, g_int64_hash ((gconstpointer) & segment->start));
+ combine_hash_ordered (res, g_int64_hash ((gconstpointer) & segment->stop));
+ combine_hash_ordered (res, g_int64_hash ((gconstpointer) & segment->time));
+ combine_hash_ordered (res,
+ g_int64_hash ((gconstpointer) & segment->position));
+ combine_hash_ordered (res,
+ g_int64_hash ((gconstpointer) & segment->duration));
+
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_allocation_params (const GValue * v, guint * res)
+{
+ GstAllocationParams *params = g_value_get_boxed (v);
+
+ *res = g_int_hash ((gconstpointer) & params->flags);
+ combine_hash_ordered (res, g_int64_hash ((gconstpointer) & params->align));
+ combine_hash_ordered (res, g_int64_hash ((gconstpointer) & params->prefix));
+ combine_hash_ordered (res, g_int64_hash ((gconstpointer) & params->padding));
+
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_object (const GValue * v, guint * res)
+{
+ GObject *obj = g_value_get_object (v);
+ *res = g_direct_hash (obj);
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_value_list (const GValue * v, guint * res)
+{
+ guint i, n;
+
+ *res = 0;
+ /* value list not ordered */
+ n = gst_value_list_get_size (v);
+ for (i = 0; i < n; i++) {
+ guint value_hash;
+
+ if (!gst_value_hash (gst_value_list_get_value (v, i), &value_hash))
+ return FALSE;
+ *res ^= value_hash;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_value_array (const GValue * v, guint * res)
+{
+ guint i, n;
+
+ *res = 0;
+ /* value array is ordered */
+ n = gst_value_array_get_size (v);
+ for (i = 0; i < n; i++) {
+ guint value_hash;
+
+ if (!gst_value_hash (gst_value_array_get_value (v, i), &value_hash))
+ return FALSE;
+ combine_hash_ordered (res, value_hash);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_value_hash_strv (const GValue * v, guint * res)
+{
+ GStrv strv = g_value_get_boxed (v);
+ guint i, n;
+
+ *res = 0;
+
+ /* GStrv is ordered */
+ n = g_strv_length (strv);
+ for (i = 0; i < n; i++) {
+ combine_hash_ordered (res, g_str_hash (strv[i]));
+ }
+
+ return TRUE;
+}
+
/***********************
* GstAllocationParams *
***********************/
@@ -8411,14 +8956,15 @@ gst_g_thread_get_type (void)
return G_TYPE_THREAD;
}
-#define SERIAL_VTABLE(t,c,s,d) { t, c, s, d, NULL }
-#define SERIAL_VTABLE_PSPEC(t,c,s,d) { t, c, s, NULL, d }
+#define SERIAL_VTABLE(t,c,s,d,h) { t, c, s, d, NULL, h }
+#define SERIAL_VTABLE_PSPEC(t,c,s,d,h) { t, c, s, NULL, d, h }
#define REGISTER_SERIALIZATION_CONST(_gtype, _type) \
G_STMT_START { \
static const GstValueTable gst_value = \
SERIAL_VTABLE (_gtype, gst_value_compare_ ## _type, \
- gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type); \
+ gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type, \
+ gst_value_hash_ ## _type); \
gst_value_register (&gst_value); \
} G_STMT_END
@@ -8426,7 +8972,8 @@ G_STMT_START { \
G_STMT_START { \
static GstValueTable gst_value = \
SERIAL_VTABLE (0, gst_value_compare_ ## _type, \
- gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type); \
+ gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type, \
+ gst_value_hash_ ## _type); \
gst_value.type = _gtype; \
gst_value_register (&gst_value); \
} G_STMT_END
@@ -8435,7 +8982,8 @@ G_STMT_START { \
G_STMT_START { \
static GstValueTable gst_value = \
SERIAL_VTABLE_PSPEC (0, gst_value_compare_ ## _type, \
- gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type); \
+ gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type, \
+ gst_value_hash_ ## _type); \
gst_value.type = _gtype; \
gst_value_register (&gst_value); \
} G_STMT_END
@@ -8444,7 +8992,8 @@ G_STMT_START { \
G_STMT_START { \
static GstValueTable gst_value = \
SERIAL_VTABLE (0, NULL, \
- gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type); \
+ gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type, \
+ gst_value_hash_ ## _type); \
gst_value.type = _gtype; \
gst_value_register (&gst_value); \
} G_STMT_END
@@ -8453,7 +9002,7 @@ G_STMT_START { \
G_STMT_START { \
static GstValueTable gst_value = \
SERIAL_VTABLE (0, gst_value_compare_ ## _type, \
- NULL, NULL); \
+ NULL, NULL, gst_value_hash_ ## _type); \
gst_value.type = _gtype; \
gst_value_register (&gst_value); \
} G_STMT_END
@@ -8703,3 +9252,58 @@ gst_flagset_register (GType flags_type)
return t;
}
+
+/**
+ * gst_value_hash:
+ * @value: a #GValue to hash
+ * @res: (out): a location to store the hash value
+ *
+ * Compute a hash value of @value.
+ * #GValue considered as equals by gst_value_compare() will have the same hash value.
+ *
+ * Returns: %TRUE, or %FALSE if @value cannot be hashed.
+ *
+ * Since: 1.28
+ */
+gboolean
+gst_value_hash (const GValue * value, guint * res)
+{
+ GType type;
+ GstValueTable *table;
+
+ g_return_val_if_fail (res, FALSE);
+ *res = 0;
+
+ type = G_VALUE_TYPE (value);
+
+ table = gst_value_hash_lookup_type (type);
+
+ if (G_LIKELY (table && table->hash)) {
+ if (type == GST_TYPE_LIST && gst_value_list_get_size (value) == 1) {
+ /* { 1 } and 1 are considered as equals so hash the single element contained in the list */
+ const GValue *v = gst_value_list_get_value (value, 0);
+ return gst_value_hash (v, res);
+ } else if (type == GST_TYPE_INT_RANGE || type == GST_TYPE_INT64_RANGE) {
+ /* hash the list type as list and ranges of the same values are considered as equals */
+ type = GST_TYPE_LIST;
+ }
+ guint hash;
+
+ *res = g_int_hash ((gconstpointer) & type);
+ if (!table->hash (value, &hash))
+ return FALSE;
+ *res ^= hash;
+ return TRUE;
+ } else {
+ /* custom enums and flags are not registered through gst_value_register() and so don't have a table entry */
+ if G_VALUE_HOLDS_ENUM
+ (value) {
+ return gst_value_hash_enum (value, res);
+ } else if G_VALUE_HOLDS_FLAGS
+ (value) {
+ return gst_value_hash_gflags (value, res);
+ }
+ }
+
+ return FALSE;
+}
diff --git a/subprojects/gstreamer/gst/gstvalue.h b/subprojects/gstreamer/gst/gstvalue.h
index 1802904f3d..13caf6451e 100644
--- a/subprojects/gstreamer/gst/gstvalue.h
+++ b/subprojects/gstreamer/gst/gstvalue.h
@@ -487,6 +487,19 @@ typedef gboolean (* GstValueDeserializeWithPSpecFunc) (GValue *dest,
const gchar *s,
GParamSpec *pspec);
+/**
+ * GstValueHashFunc:
+ * @value: a #GValue
+ * @res: (out): a location to store the hash value
+ *
+ * Used by gst_value_hash() to calculate a hash of @value.
+ *
+ * Returns: %TRUE, or %FALSE if @value cannot be hashed.
+ *
+ * Since: 1.28
+ */
+typedef gboolean (* GstValueHashFunc) (const GValue *value,
+ guint *res);
typedef struct _GstValueTable GstValueTable;
/**
@@ -514,8 +527,17 @@ struct _GstValueTable {
*/
GstValueDeserializeWithPSpecFunc deserialize_with_pspec;
+ /**
+ * GstValueTable.hash:
+ *
+ * a #GstValueHashFunc
+ *
+ * Since: 1.28
+ */
+ GstValueHashFunc hash;
+
/*< private >*/
- gpointer _gst_reserved [GST_PADDING - 1];
+ gpointer _gst_reserved [GST_PADDING - 2];
};
GST_API
@@ -569,6 +591,10 @@ gboolean gst_value_deserialize_with_pspec (GValue *dest,
const gchar *src,
GParamSpec *pspec);
+GST_API
+gboolean gst_value_hash (const GValue * value,
+ guint * res);
+
/* list */
GST_API
diff --git a/subprojects/gstreamer/tests/check/gst/gstvalue.c b/subprojects/gstreamer/tests/check/gst/gstvalue.c
index 72bc4848a9..9e6e8d0abf 100644
--- a/subprojects/gstreamer/tests/check/gst/gstvalue.c
+++ b/subprojects/gstreamer/tests/check/gst/gstvalue.c
@@ -4063,6 +4063,435 @@ GST_START_TEST (test_serialize_deserialize_strv)
GST_END_TEST;
+/* take ownership of @a and @b */
+static void
+test_hash_value_diff (GValue * a, GValue * b, gboolean compare)
+{
+ guint hash_a, hash_b;
+
+ if (compare) {
+ fail_unless (gst_value_compare (a, a) == GST_VALUE_EQUAL,
+ "[%s] value should be equal: \"%s\" \"%s\"",
+ G_VALUE_TYPE_NAME (a), gst_value_serialize (a),
+ gst_value_serialize (a));
+ fail_unless (gst_value_compare (b, b) == GST_VALUE_EQUAL,
+ "[%s] value should be equal: \"%s\" \"%s\"", G_VALUE_TYPE_NAME (a),
+ gst_value_serialize (b), gst_value_serialize (b));
+ fail_unless (gst_value_compare (a, b) != GST_VALUE_EQUAL,
+ "[%s] value should be different: \"%s\" \"%s\"", G_VALUE_TYPE_NAME (a),
+ gst_value_serialize (a), gst_value_serialize (b));
+ }
+
+ fail_unless (gst_value_hash (a, &hash_a),
+ "[%s] failed to hash: \"%s\"",
+ G_VALUE_TYPE_NAME (a), gst_value_serialize (a));
+ fail_unless (gst_value_hash (b, &hash_b),
+ "[%s] failed to hash: \"%s\"",
+ G_VALUE_TYPE_NAME (b), gst_value_serialize (b));
+ fail_unless (hash_a != hash_b,
+ "[%s] hash should be different: \"%s\" \"%s\"",
+ G_VALUE_TYPE_NAME (a), gst_value_serialize (a), gst_value_serialize (b));
+
+ g_value_unset (a);
+ g_value_unset (b);
+}
+
+/* take ownership of @a and @b */
+static void
+test_hash_value_equal (GValue * a, GValue * b, gboolean compare)
+{
+ guint hash_a, hash_b;
+
+ if (compare) {
+ fail_unless (gst_value_compare (a, a) == GST_VALUE_EQUAL,
+ "[%s] value should be equal: \"%s\" \"%s\"",
+ G_VALUE_TYPE_NAME (a), gst_value_serialize (a),
+ gst_value_serialize (a));
+ fail_unless (gst_value_compare (b, b) == GST_VALUE_EQUAL,
+ "[%s] value should be equal: \"%s\" \"%s\"", G_VALUE_TYPE_NAME (a),
+ gst_value_serialize (b), gst_value_serialize (b));
+ fail_unless (gst_value_compare (a, b) == GST_VALUE_EQUAL,
+ "[%s] value should be equal: \"%s\" \"%s\"", G_VALUE_TYPE_NAME (a),
+ gst_value_serialize (a), gst_value_serialize (b));
+ }
+
+ fail_unless (gst_value_hash (a, &hash_a),
+ "[%s] failed to hash: \"%s\"",
+ G_VALUE_TYPE_NAME (a), gst_value_serialize (a));
+ fail_unless (gst_value_hash (b, &hash_b),
+ "[%s] failed to hash: \"%s\"",
+ G_VALUE_TYPE_NAME (b), gst_value_serialize (b));
+ fail_unless (hash_a == hash_b,
+ "[%s] hash should be equal: \"%s\" \"%s\"",
+ G_VALUE_TYPE_NAME (a), gst_value_serialize (a), gst_value_serialize (b));
+
+ g_value_unset (a);
+ g_value_unset (b);
+}
+
+GST_START_TEST (test_hash)
+{
+ guint i;
+ /* tests where we can rely on gst_value_deserialize() to set GValue */
+ struct HashTest
+ {
+ GType gtype;
+ const gchar *a;
+ const gchar *b;
+ gboolean compare;
+ };
+
+ /* check that a and b are different */
+ struct HashTest tests_diff[] = {
+ {G_TYPE_INT, "1", "2", TRUE},
+ {G_TYPE_INT64, "1", "2", TRUE},
+ {G_TYPE_LONG, "1", "2", TRUE},
+ {G_TYPE_UINT, "1", "2", TRUE},
+ {G_TYPE_UINT64, "1", "2", TRUE},
+ {G_TYPE_ULONG, "1", "2", TRUE},
+ {G_TYPE_UCHAR, "1", "2", TRUE},
+ {G_TYPE_GTYPE, "gint", "gint64", TRUE},
+ {G_TYPE_DOUBLE, "1.0", "2.0", TRUE},
+ {G_TYPE_FLOAT, "1.0", "2.0", TRUE},
+ {G_TYPE_STRING, "a", "b", TRUE},
+ {G_TYPE_BOOLEAN, "TRUE", "FALSE", TRUE},
+ {GST_TYPE_BUFFER, "ab", "abcd", TRUE},
+ /* only difference is the buffer content */
+ {GST_TYPE_SAMPLE,
+ "6275660a64617400:Y2FwcywgSW50PShpbnQpMjAsIFN0cmluZz0oc3RyaW5nKSJhXCBzdHJpbmciAA__:c2VnbWVudCwgZmxhZ3M9KEdzdFNlZ21lbnRGbGFncylHU1RfU0VHTUVOVF9GTEFHX1JFU0VULCByYXRlPShkb3VibGUpMS4yLCBhcHBsaWVkLXJhdGU9KGRvdWJsZSkxLCBmb3JtYXQ9KEdzdEZvcm1hdClkZWZhdWx0LCBiYXNlPShndWludDY0KTAsIG9mZnNldD0oZ3VpbnQ2NCkwLCBzdGFydD0oZ3VpbnQ2NCkyMCwgc3RvcD0oZ3VpbnQ2NCkzMCwgdGltZT0oZ3VpbnQ2NCkyMCwgcG9zaXRpb249KGd1aW50NjQpMjAsIGR1cmF0aW9uPShndWludDY0KTE4NDQ2NzQ0MDczNzA5NTUxNjE1OwA_:c3RydWN0dXJlLCBGbG9hdD0oZmxvYXQpLTIuNTsA",
+ "4255460a44415400:Y2FwcywgSW50PShpbnQpMjAsIFN0cmluZz0oc3RyaW5nKSJhXCBzdHJpbmciAA__:c2VnbWVudCwgZmxhZ3M9KEdzdFNlZ21lbnRGbGFncylHU1RfU0VHTUVOVF9GTEFHX1JFU0VULCByYXRlPShkb3VibGUpMS4yLCBhcHBsaWVkLXJhdGU9KGRvdWJsZSkxLCBmb3JtYXQ9KEdzdEZvcm1hdClkZWZhdWx0LCBiYXNlPShndWludDY0KTAsIG9mZnNldD0oZ3VpbnQ2NCkwLCBzdGFydD0oZ3VpbnQ2NCkyMCwgc3RvcD0oZ3VpbnQ2NCkzMCwgdGltZT0oZ3VpbnQ2NCkyMCwgcG9zaXRpb249KGd1aW50NjQpMjAsIGR1cmF0aW9uPShndWludDY0KTE4NDQ2NzQ0MDczNzA5NTUxNjE1OwA_:c3RydWN0dXJlLCBGbG9hdD0oZmxvYXQpLTIuNTsA",
+ TRUE},
+ {GST_TYPE_FRACTION, "1/3", "3/1", TRUE},
+ {GST_TYPE_STRUCTURE, "video/x-raw", "audio/x-raw", TRUE},
+ {GST_TYPE_STRUCTURE, "video/x-raw,width=1920", "video/x-raw,width=800",
+ TRUE},
+ {GST_TYPE_CAPS, "video/x-raw", "audio/x-raw", TRUE},
+ {GST_TYPE_CAPS, "video/x-raw,width=1920", "video/x-raw,width=800", TRUE},
+ {GST_TYPE_CAPS, "video/x-raw", "video/x-raw(memory:DMABuf)", TRUE},
+ {GST_TYPE_TAG_LIST, "taglist, title=(string)Title, artist=(string)Artist",
+ "taglist, title=(string)Title2, artist=(string)Artist", TRUE},
+ {G_TYPE_DATE, "1984-11-30", "1985-11-30", TRUE},
+ {G_TYPE_DATE_TIME, "2011-06-23T07:40:10Z", "2011-06-23T07:40:11Z", TRUE},
+ {GST_TYPE_DATE_TIME, "2011-06-23T07:40:10Z", "2011-06-23T07:40:11Z",
+ TRUE},
+ {G_TYPE_BYTES, "YQ==", "Yg==", TRUE},
+ {GST_TYPE_BITMASK, "0xaa", "0xbb", TRUE},
+ {GST_TYPE_SEEK_FLAGS, "+flush", "+flush+trickmode/trickmode-no-audio",
+ TRUE},
+ {GST_TYPE_LIST, "{1, 2}", "{1, 3}", TRUE},
+ {GST_TYPE_CAPS_FEATURES, "memory:DMABuf", "memory:GLMemory", FALSE},
+ };
+
+ /* check that a and b are equal */
+ struct HashTest tests_equal[] = {
+ /* structures fields are not ordered */
+ {GST_TYPE_STRUCTURE, "video/x-raw,width=1920,height=1080",
+ "video/x-raw,height=1080,width=1920", TRUE},
+ /* fixed and unfixed caps are not ordered */
+ {GST_TYPE_CAPS, "video/x-raw; audio/x-raw", "audio/x-raw; video/x-raw",
+ TRUE},
+ {GST_TYPE_CAPS,
+ "video/x-raw,format=NV12,width=[1, 100]; video/x-raw,format=GREY8,width=[1, 100]",
+ "video/x-raw,format=GREY8,width=[1, 100]; video/x-raw,format=NV12,width=[1, 100]",
+ TRUE},
+ {GST_TYPE_LIST, "{1, 2}", "{2, 1}", TRUE},
+ {GST_TYPE_LIST, "{1, 2}", "{2, 1}", TRUE},
+ /* caps features are not ordered */
+ {GST_TYPE_CAPS_FEATURES, "memory:DMABuf, memory:GLMemory",
+ "memory:GLMemory, memory:DMABuf", FALSE},
+ /* same actual fraction have the same hash */
+ {GST_TYPE_FRACTION, "1/2", "2/4", TRUE},
+ };
+
+ for (i = 0; i < G_N_ELEMENTS (tests_diff); i++) {
+ GValue a = G_VALUE_INIT;
+ GValue b = G_VALUE_INIT;
+
+ g_value_init (&a, tests_diff[i].gtype);
+ fail_unless (gst_value_deserialize (&a, tests_diff[i].a),
+ "[%s] could not deserialize %s", g_type_name (tests_diff[i].gtype),
+ tests_diff[i].a);
+ g_value_init (&b, tests_diff[i].gtype);
+ fail_unless (gst_value_deserialize (&b, tests_diff[i].b),
+ "[%s] could not deserialize %s", g_type_name (tests_diff[i].gtype),
+ tests_diff[i].b);
+
+ test_hash_value_diff (&a, &b, tests_diff[i].compare);
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (tests_equal); i++) {
+ GValue a = G_VALUE_INIT;
+ GValue b = G_VALUE_INIT;
+
+ g_value_init (&a, tests_equal[i].gtype);
+ fail_unless (gst_value_deserialize (&a, tests_equal[i].a),
+ "[%s] could not deserialize %s", g_type_name (tests_equal[i].gtype),
+ tests_equal[i].a);
+ g_value_init (&b, tests_equal[i].gtype);
+ fail_unless (gst_value_deserialize (&b, tests_equal[i].b),
+ "[%s] could not deserialize %s", g_type_name (tests_equal[i].gtype),
+ tests_equal[i].b);
+
+ test_hash_value_equal (&a, &b, tests_equal[i].compare);
+ }
+
+ /* tests for types not implementing deserialize */
+ {
+ GValue a = G_VALUE_INIT;
+ GValue b = G_VALUE_INIT;
+ GValue v = G_VALUE_INIT;
+ GValueArray *array;
+
+ /* int range */
+ g_value_init (&a, GST_TYPE_INT_RANGE);
+ g_value_init (&b, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range (&a, 0, 2);
+ gst_value_set_int_range (&b, 0, 3);
+ test_hash_value_diff (&a, &b, TRUE);
+
+ /* int range with step */
+ g_value_init (&a, GST_TYPE_INT_RANGE);
+ g_value_init (&b, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range_step (&a, 0, 12, 2);
+ gst_value_set_int_range_step (&b, 0, 12, 4);
+ test_hash_value_diff (&a, &b, TRUE);
+
+ /* int64 range */
+ g_value_init (&a, GST_TYPE_INT64_RANGE);
+ g_value_init (&b, GST_TYPE_INT64_RANGE);
+ gst_value_set_int64_range (&a, 0, 2);
+ gst_value_set_int64_range (&b, 0, 3);
+ test_hash_value_diff (&a, &b, TRUE);
+
+ /* int64 range with step */
+ g_value_init (&a, GST_TYPE_INT64_RANGE);
+ g_value_init (&b, GST_TYPE_INT64_RANGE);
+ gst_value_set_int64_range_step (&a, 0, 12, 2);
+ gst_value_set_int64_range_step (&b, 0, 12, 4);
+ test_hash_value_diff (&a, &b, TRUE);
+
+ /* double range */
+ g_value_init (&a, GST_TYPE_DOUBLE_RANGE);
+ g_value_init (&b, GST_TYPE_DOUBLE_RANGE);
+ gst_value_set_double_range (&a, 0, 12);
+ gst_value_set_double_range (&b, 0, 13);
+ test_hash_value_diff (&a, &b, TRUE);
+
+ /* enum */
+ static const GEnumValue test_enum_vals[] = {
+ {0, "Badger", "badger"},
+ {1, "Mushroom", "mushroom"},
+ {0, NULL, NULL},
+ };
+ GType test_enum_type =
+ g_enum_register_static ("GstTestEnum", test_enum_vals);
+ g_value_init (&a, test_enum_type);
+ g_value_set_enum (&a, 0);
+ g_value_init (&b, test_enum_type);
+ g_value_set_enum (&b, 1);
+ test_hash_value_diff (&a, &b, TRUE);
+
+ /* flags */
+ static const GFlagsValue test_flags_vals[] = {
+ {0, "Badger", "badger"},
+ {1, "Mushroom", "mushroom"},
+ {0, NULL, NULL},
+ };
+ GType test_flags_type =
+ g_flags_register_static ("GstTestFlags", test_flags_vals);
+ g_value_init (&a, test_flags_type);
+ g_value_set_flags (&a, 0);
+ g_value_init (&b, test_flags_type);
+ g_value_set_flags (&b, 1);
+ test_hash_value_diff (&a, &b, TRUE);
+
+ /* value array */
+ setup_test_value_array (&a);
+ setup_test_value_array (&b);
+ GValueArray *arr = g_value_get_boxed (&b);
+ g_value_init (&v, G_TYPE_INT);
+ g_value_set_int (&v, 4);
+ g_value_array_append (arr, &v);
+ test_hash_value_diff (&a, &b, TRUE);
+ g_value_unset (&v);
+
+ g_value_init (&v, G_TYPE_INT);
+ g_value_init (&a, G_TYPE_VALUE_ARRAY);
+ array = g_value_array_new (2);
+ g_value_set_int (&v, 1);
+ g_value_array_append (array, &v);
+ g_value_set_int (&v, 2);
+ g_value_array_append (array, &v);
+ g_value_take_boxed (&a, array);
+ array = g_value_array_new (2);
+ g_value_init (&b, G_TYPE_VALUE_ARRAY);
+ g_value_set_int (&v, 2);
+ g_value_array_append (array, &v);
+ g_value_set_int (&v, 1);
+ g_value_array_append (array, &v);
+ g_value_take_boxed (&b, array);
+ test_hash_value_diff (&a, &b, TRUE);
+ g_value_unset (&v);
+
+ /* fraction range */
+ g_value_init (&a, GST_TYPE_FRACTION_RANGE);
+ g_value_init (&b, GST_TYPE_FRACTION_RANGE);
+ gst_value_set_fraction_range_full (&a, 1, 5, 4, 5);
+ gst_value_set_fraction_range_full (&a, 2, 5, 4, 5);
+ test_hash_value_diff (&a, &b, TRUE);
+
+ /* objet */
+ GstBufferPool *obj_a = gst_buffer_pool_new ();
+ GstBufferPool *obj_b = gst_buffer_pool_new ();
+ g_value_init (&a, G_TYPE_OBJECT);
+ g_value_take_object (&a, obj_a);
+ g_value_init (&b, G_TYPE_OBJECT);
+ g_value_take_object (&b, obj_b);
+ test_hash_value_diff (&a, &b, TRUE);
+
+ /* value array */
+ g_value_init (&a, GST_TYPE_ARRAY);
+ g_value_init (&v, G_TYPE_INT);
+ g_value_set_int (&v, 1);
+ gst_value_array_append_value (&a, &v);
+ g_value_set_int (&v, 2);
+ gst_value_array_append_value (&a, &v);
+ g_value_init (&b, GST_TYPE_ARRAY);
+ g_value_set_int (&v, 2);
+ gst_value_array_append_value (&b, &v);
+ g_value_set_int (&v, 2);
+ gst_value_array_append_value (&b, &v);
+ test_hash_value_diff (&a, &b, TRUE);
+ g_value_unset (&v);
+
+ /* AllocationParams */
+ GstAllocationParams params_a = { 0 }, params_b;
+ params_a.flags = 0;
+ params_a.align = 1;
+ params_a.prefix = 2;
+ params_a.padding = 3;
+ params_b = params_a;
+ params_b.padding = 4;
+ g_value_init (&a, GST_TYPE_ALLOCATION_PARAMS);
+ g_value_set_boxed (&a, ¶ms_a);
+ g_value_init (&b, GST_TYPE_ALLOCATION_PARAMS);
+ g_value_set_boxed (&b, ¶ms_b);
+ test_hash_value_diff (&a, &b, TRUE);
+
+ /* float NaN */
+ g_value_init (&a, G_TYPE_FLOAT);
+ g_value_set_float (&a, NAN);
+ g_value_init (&b, G_TYPE_FLOAT);
+ g_value_set_float (&b, 0);
+ test_hash_value_diff (&a, &b, FALSE);
+ }
+
+ /* types with specific compare function */
+ {
+ GValue a = G_VALUE_INIT;
+ GValue b = G_VALUE_INIT;
+
+ GstSegment seg_a, seg_b;
+ gst_segment_init (&seg_a, GST_FORMAT_DEFAULT);
+ fail_unless (gst_segment_do_seek (&seg_a, 1.2, GST_FORMAT_DEFAULT,
+ GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, 20, GST_SEEK_TYPE_SET, 30,
+ NULL));
+ seg_b = seg_a;
+ seg_b.rate = 1.0;
+ fail_unless (!gst_segment_is_equal (&seg_a, &seg_b));
+ g_value_init (&a, GST_TYPE_SEGMENT);
+ g_value_set_boxed (&a, &seg_a);
+ g_value_init (&b, GST_TYPE_SEGMENT);
+ g_value_set_boxed (&b, &seg_b);
+ test_hash_value_diff (&a, &b, FALSE);
+
+ g_value_init (&a, G_TYPE_STRV);
+ fail_unless (gst_value_deserialize (&a, "<\"foo\",\"bar\">"));
+ g_value_init (&b, G_TYPE_STRV);
+ fail_unless (gst_value_deserialize (&b, "<\"bar\",\"foo\">"));
+ fail_unless (!g_strv_equal (g_value_get_boxed (&a),
+ g_value_get_boxed (&b)));
+ test_hash_value_diff (&a, &b, FALSE);
+ }
+
+ // gst_value_compare() special cases
+ {
+ GValue a = G_VALUE_INIT;
+ GValue b = G_VALUE_INIT;
+ GValue v = G_VALUE_INIT;
+
+ /* { 1 } and 1 are considered as equals */
+ g_value_init (&a, GST_TYPE_LIST);
+ fail_unless (gst_value_deserialize (&a, "{ 1 }"));
+ g_value_init (&b, G_TYPE_INT);
+ fail_unless (gst_value_deserialize (&b, "1"));
+ test_hash_value_equal (&a, &b, TRUE);
+
+ /* list and range are considered as equal if they contain the same set of elements */
+ g_value_init (&a, GST_TYPE_LIST);
+ fail_unless (gst_value_deserialize (&a, "{ 1, 2 }"));
+ g_value_init (&b, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range (&b, 1, 2);
+ test_hash_value_equal (&a, &b, TRUE);
+
+ /* same set with steps */
+ g_value_init (&a, GST_TYPE_LIST);
+ fail_unless (gst_value_deserialize (&a, "{ 2, 4, 6, 8 }"));
+ g_value_init (&b, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range_step (&b, 2, 8, 2);
+ test_hash_value_equal (&a, &b, TRUE);
+
+ /* different step */
+ g_value_init (&a, GST_TYPE_LIST);
+ fail_unless (gst_value_deserialize (&a, "{ 1, 3 }"));
+ g_value_init (&b, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range (&b, 1, 3);
+ test_hash_value_diff (&a, &b, TRUE);
+
+ /* same for int64 range */
+ g_value_init (&a, GST_TYPE_LIST);
+ g_value_init (&v, G_TYPE_INT64);
+ g_value_set_int64 (&v, 1);
+ gst_value_list_append_and_take_value (&a, &v);
+ g_value_init (&v, G_TYPE_INT64);
+ g_value_set_int64 (&v, 2);
+ gst_value_list_append_and_take_value (&a, &v);
+ g_value_init (&b, GST_TYPE_INT64_RANGE);
+ gst_value_set_int64_range (&b, 1, 2);
+ test_hash_value_equal (&a, &b, TRUE);
+
+ g_value_init (&a, GST_TYPE_LIST);
+ g_value_init (&v, G_TYPE_INT64);
+ g_value_set_int64 (&v, 2);
+ gst_value_list_append_and_take_value (&a, &v);
+ g_value_init (&v, G_TYPE_INT64);
+ g_value_set_int64 (&v, 4);
+ gst_value_list_append_and_take_value (&a, &v);
+ g_value_init (&v, G_TYPE_INT64);
+ g_value_set_int64 (&v, 6);
+ gst_value_list_append_and_take_value (&a, &v);
+ g_value_init (&v, G_TYPE_INT64);
+ g_value_set_int64 (&v, 8);
+ gst_value_list_append_and_take_value (&a, &v);
+ g_value_init (&b, GST_TYPE_INT64_RANGE);
+ gst_value_set_int64_range_step (&b, 2, 8, 2);
+ test_hash_value_equal (&a, &b, TRUE);
+
+ g_value_init (&a, GST_TYPE_LIST);
+ g_value_init (&v, G_TYPE_INT64);
+ g_value_set_int64 (&v, 1);
+ gst_value_list_append_and_take_value (&a, &v);
+ g_value_init (&v, G_TYPE_INT64);
+ g_value_set_int64 (&v, 3);
+ gst_value_list_append_and_take_value (&a, &v);
+ g_value_init (&b, GST_TYPE_INT64_RANGE);
+ gst_value_set_int64_range (&b, 1, 3);
+ test_hash_value_diff (&a, &b, TRUE);
+ }
+}
+
+GST_END_TEST;
+
static Suite *
gst_value_suite (void)
{
@@ -4126,6 +4555,7 @@ gst_value_suite (void)
tcase_add_test (tc_chain, test_serialize_deserialize_tag_list);
tcase_add_test (tc_chain, test_serialize_deserialize_sample);
tcase_add_test (tc_chain, test_serialize_deserialize_strv);
+ tcase_add_test (tc_chain, test_hash);
return s;
}