Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package gstreamer for openSUSE:Factory 
checked in at 2026-06-13 18:46:01
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/gstreamer (Old)
 and      /work/SRC/openSUSE:Factory/.gstreamer.new.1981 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "gstreamer"

Sat Jun 13 18:46:01 2026 rev:123 rq:1359027 version:1.28.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/gstreamer/gstreamer.changes      2026-05-13 
17:18:44.465807638 +0200
+++ /work/SRC/openSUSE:Factory/.gstreamer.new.1981/gstreamer.changes    
2026-06-13 18:46:56.120427022 +0200
@@ -1,0 +2,52 @@
+Fri Jun 12 16:00:13 UTC 2026 - Antonio Larrosa <[email protected]>
+
+- Update to version 1.28.4:
+  + Highlighted bugfixes:
+    - Various security fixes and playback fixes
+    - audioaggregator: fixes for conversion of in-progress buffers
+      when input caps change
+    - audioresample: more armv7 fixes
+    - camerabin: Fix caps negotiation failure when starting video
+      capture
+    - Debug logging performance improvements
+    - fmp4mux: Fix draining in chunk mode after partial GOPs were
+      drained
+    - gldownload: fix handling of directly imported dmabufs from
+      glupload
+    - matroskamux: Write ReferenceBlock for non-keyframe video in
+      BlockGroups
+    - rtp2: session: add "stats" property
+    - rtspsrc2: handle parse errors with TCP interleaved more
+      gracefully where the server just drops data
+    - rtspsrc2: implement support for SRTP, authentication, HTTP
+      tunnelling, keep alive, stream selection, TLS validation,
+      latency configuration
+    - st2038combiner: only forward video pad segment, fixing issues
+      for cases where the ST2038 segment differs
+    - Wavpack audio: Various channel and channel-mask related fixes
+    - webrtc, sdp: set level in negotiated caps only if level
+      asymmetry not allowed, fixing an H.264 negotiation regression
+      with higher resolutions
+    - androidmedia: add various new codec mime / profile mappings
+      (WMV, VC1, AC3/EAC3/AC4, AAC, H265) and support decoding FLAC
+    - d3d12decoder: Fix decoding on Qualcomm GPUs on ARM64 Windows
+    - wasapi2src: fix hang when using loopback-target-pid
+      (regression from 1.26)
+    - cerbero: update to Rust 1.96, plus glib-networking OpenSSL
+      backend fixes
+    - Various bug fixes, build fixes, memory leak fixes, and other
+      stability and reliability improvements
+  + gstreamer:
+    - bufferpool: avoid leaking partially preallocated buffers
+    - caps: fix multiple caps leaks
+    - datetime: Improve correctness of ISO-8601 string parsing
+    - info: Don't use fwrite() on Windows for debug logging
+    - info: Use stack allocation for messages smaller than 1kB
+    - task: Fix racy tests by making unref deterministic
+    - value: fix crash when converting NULL G_TYPE_VALUE_ARRAY to
+      G_TYPE_STRING
+    - registry: detect libgstreamer load from Android container
+      and skip canonicalization
+    - tests: Fix build with glib <= 2.67.2
+
+-------------------------------------------------------------------

Old:
----
  gstreamer-1.28.3.obscpio

New:
----
  gstreamer-1.28.4.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ gstreamer.spec ++++++
--- /var/tmp/diff_new_pack.W7AGuN/_old  2026-06-13 18:46:59.436564791 +0200
+++ /var/tmp/diff_new_pack.W7AGuN/_new  2026-06-13 18:46:59.456565622 +0200
@@ -19,7 +19,7 @@
 %define gst_branch 1.0
 
 Name:           gstreamer
-Version:        1.28.3
+Version:        1.28.4
 Release:        0
 Summary:        Streaming-Media Framework Runtime
 License:        LGPL-2.1-or-later

++++++ _service ++++++
--- /var/tmp/diff_new_pack.W7AGuN/_old  2026-06-13 18:46:59.848581908 +0200
+++ /var/tmp/diff_new_pack.W7AGuN/_new  2026-06-13 18:46:59.880583238 +0200
@@ -5,7 +5,7 @@
     <param 
name="url">https://gitlab.freedesktop.org/gstreamer/gstreamer.git</param>
     <param name="subdir">subprojects/gstreamer</param>
     <param name="filename">gstreamer</param>
-    <param name="revision">1.28.3</param>
+    <param name="revision">1.28.4</param>
     <param name="versionformat">@PARENT_TAG@+@TAG_OFFSET@</param>
     <param name="versionrewrite-pattern">v?(.*)\+0</param>
     <param name="versionrewrite-replacement">\1</param>

++++++ gstreamer-1.28.3.obscpio -> gstreamer-1.28.4.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/gst/gstbufferpool.c 
new/gstreamer-1.28.4/gst/gstbufferpool.c
--- old/gstreamer-1.28.3/gst/gstbufferpool.c    2026-05-11 19:28:12.000000000 
+0200
+++ new/gstreamer-1.28.4/gst/gstbufferpool.c    2026-06-12 14:19:43.000000000 
+0200
@@ -127,6 +127,7 @@
 static void default_reset_buffer (GstBufferPool * pool, GstBuffer * buffer);
 static void default_free_buffer (GstBufferPool * pool, GstBuffer * buffer);
 static void default_release_buffer (GstBufferPool * pool, GstBuffer * buffer);
+static void do_free_buffer (GstBufferPool * pool, GstBuffer * buffer);
 
 static void
 gst_buffer_pool_class_init (GstBufferPoolClass * klass)
@@ -326,9 +327,12 @@
   guint i;
   GstBufferPoolPrivate *priv = pool->priv;
   GstBufferPoolClass *pclass;
+  GPtrArray *buffers;
 
   pclass = GST_BUFFER_POOL_GET_CLASS (pool);
 
+  buffers = g_ptr_array_sized_new (priv->min_buffers);
+
   /* we need to prealloc buffers */
   for (i = 0; i < priv->min_buffers; i++) {
     GstBuffer *buffer;
@@ -336,17 +340,31 @@
     if (do_alloc_buffer (pool, &buffer, NULL) != GST_FLOW_OK)
       goto alloc_failed;
 
+    g_ptr_array_add (buffers, buffer);
+  }
+
+  for (i = 0; i < buffers->len; i++) {
+    GstBuffer *buffer = g_ptr_array_index (buffers, i);
+
     /* release to the queue, we call the vmethod directly, we don't need to do
      * the other refcount handling right now. */
     if (G_LIKELY (pclass->release_buffer))
       pclass->release_buffer (pool, buffer);
   }
+
+  g_ptr_array_unref (buffers);
   return TRUE;
 
   /* ERRORS */
 alloc_failed:
   {
     GST_WARNING_OBJECT (pool, "failed to allocate buffer");
+    for (i = 0; i < buffers->len; i++) {
+      GstBuffer *buffer = g_ptr_array_index (buffers, i);
+
+      do_free_buffer (pool, buffer);
+    }
+    g_ptr_array_unref (buffers);
     return FALSE;
   }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/gst/gstcaps.c 
new/gstreamer-1.28.4/gst/gstcaps.c
--- old/gstreamer-1.28.3/gst/gstcaps.c  2026-05-11 19:28:12.000000000 +0200
+++ new/gstreamer-1.28.4/gst/gstcaps.c  2026-06-12 14:19:43.000000000 +0200
@@ -130,6 +130,7 @@
     GValue * dest_value);
 static gboolean gst_caps_from_string_inplace (GstCaps * caps,
     const gchar * string);
+static void gst_value_set_may_be_leaked (const GValue * value);
 
 GType _gst_caps_type = 0;
 GstCaps *_gst_caps_any;
@@ -553,6 +554,60 @@
 
 G_DEFINE_POINTER_TYPE (GstStaticCaps, gst_static_caps);
 
+static gboolean
+gst_caps_field_set_may_be_leaked (const GstIdStr * fieldname,
+    const GValue * value, gpointer user_data)
+{
+  gst_value_set_may_be_leaked (value);
+  return TRUE;
+}
+
+static void
+gst_caps_set_nested_may_be_leaked (GstCaps * caps)
+{
+  guint i, n = gst_caps_get_size (caps);
+
+  GST_MINI_OBJECT_FLAG_SET (caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
+
+  for (i = 0; i < n; i++)
+    gst_structure_foreach_id_str (gst_caps_get_structure (caps, i),
+        gst_caps_field_set_may_be_leaked, NULL);
+}
+
+static void
+gst_value_set_may_be_leaked (const GValue * value)
+{
+  if (GST_VALUE_HOLDS_CAPS (value)) {
+    GstCaps *nested = (GstCaps *) gst_value_get_caps (value);
+
+    if (nested)
+      gst_caps_set_nested_may_be_leaked (nested);
+  } else if (GST_VALUE_HOLDS_LIST (value)) {
+    guint i, n = gst_value_list_get_size (value);
+
+    for (i = 0; i < n; i++)
+      gst_value_set_may_be_leaked (gst_value_list_get_value (value, i));
+  } else if (GST_VALUE_HOLDS_ARRAY (value)) {
+    guint i, n = gst_value_array_get_size (value);
+
+    for (i = 0; i < n; i++)
+      gst_value_set_may_be_leaked (gst_value_array_get_value (value, i));
+  } else if (GST_VALUE_HOLDS_UNIQUE_LIST (value)) {
+    guint i, n = gst_value_unique_list_get_size (value);
+
+    for (i = 0; i < n; i++)
+      gst_value_set_may_be_leaked (gst_value_unique_list_get_value (value, i));
+  } else if (GST_VALUE_HOLDS_STRUCTURE (value)) {
+    gst_structure_foreach_id_str (gst_value_get_structure (value),
+        gst_caps_field_set_may_be_leaked, NULL);
+  } else if (g_type_is_a (G_VALUE_TYPE (value), GST_TYPE_MINI_OBJECT)) {
+    GstMiniObject *obj = g_value_get_boxed (value);
+
+    if (obj)
+      GST_MINI_OBJECT_FLAG_SET (obj, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
+  }
+}
+
 /**
  * gst_static_caps_get:
  * @static_caps: the #GstStaticCaps to convert
@@ -594,8 +649,9 @@
       goto done;
     }
 
-    /* Caps generated from static caps are usually leaked */
-    GST_MINI_OBJECT_FLAG_SET (*caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
+    /* Caps generated from static caps are usually leaked, as are any caps
+     * nested inside them (e.g. a "...=(GstCaps)[...]" field). */
+    gst_caps_set_nested_may_be_leaked (*caps);
 
     GST_CAT_TRACE (GST_CAT_CAPS, "created %p from string %s", static_caps,
         string);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/gst/gstdatetime.c 
new/gstreamer-1.28.4/gst/gstdatetime.c
--- old/gstreamer-1.28.3/gst/gstdatetime.c      2026-05-11 19:28:12.000000000 
+0200
+++ new/gstreamer-1.28.4/gst/gstdatetime.c      2026-06-12 14:19:43.000000000 
+0200
@@ -883,7 +883,6 @@
   gint gmt_offset_hour = -99, gmt_offset_min = -99;
   gdouble second = -1.0;
   gfloat tzoffset = 0.0;
-  guint64 usecs;
   gint len, ret;
 
   g_return_val_if_fail (string != NULL, NULL);
@@ -899,25 +898,44 @@
     return NULL;
 
   if (g_ascii_isdigit (string[2]) && g_ascii_isdigit (string[3])) {
-    ret = sscanf (string, "%04d-%02d-%02d", &year, &month, &day);
+    guint year_u, month_u, day_u;
 
-    if (ret == 0)
+    /* Parse to temporary unsigned integers */
+    ret = sscanf (string, "%04u-%02u-%02u", &year_u, &month_u, &day_u);
+
+    /* First sanity-check parsing */
+    if (ret == 3 && day_u > 31)
+      return NULL;
+    if (ret >= 2 && month_u > 12)
+      return NULL;
+    if (ret >= 1 && (year_u == 0 || year_u > 9999))
+      return NULL;
+    if (ret <= 0)
       return NULL;
 
-    if (ret == 3 && day <= 0) {
-      ret = 2;
-      day = -1;
+    /* Then assign to signed integers, giving month/day 0 a special meaning of
+     * being unset by setting them to -1 */
+    if (ret == 3) {
+      if (day_u == 0) {
+        ret = 2;
+        day = -1;
+      } else {
+        day = day_u;
+      }
     }
 
-    if (ret >= 2 && month <= 0) {
-      ret = 1;
-      month = day = -1;
+    if (ret >= 2) {
+      if (month_u == 0) {
+        ret = 1;
+        month = day = -1;
+      } else {
+        month = month_u;
+      }
     }
 
-    if (ret >= 1 && (year <= 0 || year > 9999 || month > 12 || day > 31))
-      return NULL;
+    year = year_u;
 
-    else if (ret >= 1 && len < 16)
+    if (ret >= 1 && len < 16)
       /* YMD is 10 chars. XMD + HM will be 16 chars. if it is less,
        * it make no sense to continue. We will stay with YMD. */
       goto ymd;
@@ -929,43 +947,60 @@
 
     string += 1;
   }
-  /* if hour or minute fails, then we will use only ymd. */
-  hour = g_ascii_strtoull (string, (gchar **) & string, 10);
-  if (hour > 24 || *string != ':')
-    goto ymd;
-
-  /* minute */
-  minute = g_ascii_strtoull (string + 1, (gchar **) & string, 10);
-  if (minute > 59)
-    goto ymd;
-
-  /* second */
-  if (*string == ':') {
-    second = g_ascii_strtoull (string + 1, (gchar **) & string, 10);
-    /* if we fail here, we still can reuse hour and minute. We
-     * will still attempt to parse any timezone information */
-    if (second > 59) {
-      second = -1.0;
-    } else {
-      /* microseconds */
-      if (*string == '.' || *string == ',') {
-        const gchar *usec_start = string + 1;
-        guint digits;
-
-        usecs = g_ascii_strtoull (string + 1, (gchar **) & string, 10);
-        if (usecs != G_MAXUINT64 && string > usec_start) {
-          digits = (guint) (string - usec_start);
-          second += (gdouble) usecs / pow (10.0, digits);
+
+  {
+    /* Also here parse hour/minute/second/subseconds as unsigned integers for
+     * being able to detect errors correctly, then validate values, and only
+     * then assign to signed integers. -1 has a special meaning of the values
+     * being unset */
+    guint64 hour_u64, minute_u64;
+
+    /* if hour or minute fails, then we will use only ymd. */
+    hour_u64 = g_ascii_strtoull (string, (gchar **) & string, 10);
+    if (hour_u64 > 24 || *string != ':')
+      goto ymd;
+
+    /* minute */
+    minute_u64 = g_ascii_strtoull (string + 1, (gchar **) & string, 10);
+    if (minute_u64 > 59)
+      goto ymd;
+
+    hour = hour_u64;
+    minute = minute_u64;
+
+    /* second */
+    if (*string == ':') {
+      guint64 second_u64 =
+          g_ascii_strtoull (string + 1, (gchar **) & string, 10);
+
+      /* if we fail here, we still can reuse hour and minute. We
+       * will still attempt to parse any timezone information */
+      if (second_u64 > 59) {
+        second = -1.0;
+      } else {
+        guint64 usecs;
+
+        second = second_u64;
+
+        /* microseconds */
+        if (*string == '.' || *string == ',') {
+          const gchar *usec_start = string + 1;
+          gsize digits;
+
+          usecs = g_ascii_strtoull (string + 1, (gchar **) & string, 10);
+          if (usecs != G_MAXUINT64 && string > usec_start) {
+            digits = (gsize) (string - usec_start);
+            second += (gdouble) usecs / pow (10.0, digits);
+          }
         }
       }
     }
   }
 
-  if (*string == 'Z')
+  if (*string == 'Z') {
     goto ymd_hms;
-  else {
-    /* reuse some code from gst-plugins-base/gst-libs/gst/tag/gstxmptag.c */
-    gint gmt_offset = -1;
+  } else {
+    guint gmt_offset_hour_u32, gmt_offset_min_u32;
     const gchar *plus_pos = NULL;
     const gchar *neg_pos = NULL;
     const gchar *pos = NULL;
@@ -982,25 +1017,38 @@
 
     if (pos && strlen (pos) >= 3) {
       gint ret_tz;
+
+      GST_DEBUG ("Parsing timezone: %s", pos);
+
+      /* Also here for the timezone parse as unsigned integers, then validate
+       * and only then assign to the signed integers based on the existence of
+       * a literal `-` or `+` */
       if (pos[2] == ':')
-        ret_tz = sscanf (pos, "%d:%d", &gmt_offset_hour, &gmt_offset_min);
+        ret_tz =
+            sscanf (pos, "%u:%u", &gmt_offset_hour_u32, &gmt_offset_min_u32);
       else
-        ret_tz = sscanf (pos, "%02d%02d", &gmt_offset_hour, &gmt_offset_min);
+        ret_tz =
+            sscanf (pos, "%02u%02u", &gmt_offset_hour_u32, 
&gmt_offset_min_u32);
 
-      GST_DEBUG ("Parsing timezone: %s", pos);
+      if (ret_tz == 2 && gmt_offset_hour_u32 < 24 && gmt_offset_min_u32 < 60) {
+        gint gmt_offset = -1;
 
-      if (ret_tz == 2) {
         if (neg_pos != NULL && neg_pos + 1 == pos) {
-          gmt_offset_hour *= -1;
-          gmt_offset_min *= -1;
+          gmt_offset_hour = -gmt_offset_hour_u32;
+          gmt_offset_min = -gmt_offset_min_u32;
+        } else {
+          gmt_offset_hour = gmt_offset_hour_u32;
+          gmt_offset_min = gmt_offset_min_u32;
         }
+
         gmt_offset = gmt_offset_hour * 60 + gmt_offset_min;
 
         tzoffset = gmt_offset / 60.0;
 
         GST_LOG ("Timezone offset: %f (%d minutes)", tzoffset, gmt_offset);
-      } else
+      } else {
         GST_WARNING ("Failed to parse timezone information");
+      }
     }
   }
 
@@ -1013,10 +1061,8 @@
     if (!now_utc)
       return NULL;
 
-    if (tzoffset != 0.0) {
-      /* If a timezone offset was supplied, get the date of that timezone */
-      g_assert (gmt_offset_min != -99);
-      g_assert (gmt_offset_hour != -99);
+    /* Set to valid values above otherwise */
+    if (gmt_offset_hour != -99 && gmt_offset_min != -99) {
       now_in_given_tz =
           g_date_time_add_minutes (now_utc,
           (60 * gmt_offset_hour) + gmt_offset_min);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/gst/gstinfo.c 
new/gstreamer-1.28.4/gst/gstinfo.c
--- old/gstreamer-1.28.3/gst/gstinfo.c  2026-05-11 19:28:12.000000000 +0200
+++ new/gstreamer-1.28.4/gst/gstinfo.c  2026-06-12 14:19:43.000000000 +0200
@@ -344,6 +344,10 @@
 
   /* heap-allocated write area for short names */
   gchar tmp_id[32];
+
+  /* Inline buffer @message is formatted into; vasnprintf falls back to malloc
+   * only if the result doesn't fit. */
+  gchar inline_buf[1024];
 };
 
 struct _GstLogContext
@@ -443,6 +447,13 @@
   return name;
 }
 
+#ifdef G_OS_WIN32
+/* HANDLE for the configured log file. This needs to be in static storage
+ * because the address allows us to check when gst_debug_log_default() is
+ * called with a custom FILE* pointer by the user. */
+static HANDLE _gst_debug_log_handle = INVALID_HANDLE_VALUE;
+#endif
+
 /* Initialize the debugging system */
 void
 _priv_gst_debug_init (void)
@@ -457,13 +468,37 @@
         log_file = stdout;
       } else {
         gchar *name = _priv_gst_debug_file_name (env);
+#ifdef G_OS_WIN32
+        wchar_t *wname = g_utf8_to_utf16 (name, -1, NULL, NULL, NULL);
+        if (wname) {
+          /* FILE_APPEND_DATA makes the kernel append each call's buffer
+           * atomically, so multiple threads logging in parallel never
+           * interleave lines and there is no userspace lock to contend on
+           * FILE_SHARE_READ allows other processes to read the file.
+           */
+          _gst_debug_log_handle = CreateFileW (wname, FILE_APPEND_DATA,
+              FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+              NULL);
+          g_free (wname);
+        }
+        if (_gst_debug_log_handle == INVALID_HANDLE_VALUE) {
+          g_printerr ("Could not open log file '%s' for writing: %u\n", env,
+              (guint) GetLastError ());
+          log_file = stderr;
+        } else {
+          /* Sentinel: _gst_debug_fprintf detects this by pointer-equality and
+           * writes via the HANDLE instead of through the CRT FILE* layer. */
+          log_file = (FILE *) & _gst_debug_log_handle;
+        }
+#else
         log_file = g_fopen (name, "w");
-        g_free (name);
         if (log_file == NULL) {
           g_printerr ("Could not open log file '%s' for writing: %s\n", env,
               g_strerror (errno));
           log_file = stderr;
         }
+#endif
+        g_free (name);
       }
     } else {
       log_file = stderr;
@@ -826,7 +861,8 @@
   }
   g_rw_lock_reader_unlock (&__log_func_mutex);
 
-  g_free (message.message);
+  if (message.message != message.inline_buf)
+    g_free (message.message);
   if (message.free_object_id)
     g_free (message.object_id);
   va_end (message.arguments);
@@ -999,8 +1035,8 @@
   if (message->message == NULL) {
     int len;
 
-    len = __gst_vasprintf (&message->message, message->format,
-        message->arguments);
+    len = __gst_vasprintf_buf (&message->message, message->inline_buf,
+        sizeof (message->inline_buf), message->format, message->arguments);
 
     if (len < 0)
       message->message = NULL;
@@ -1758,11 +1794,13 @@
 _gst_debug_fprintf (FILE * file, const gchar * format, ...)
 {
   va_list args;
+  gchar inline_buf[1024];
   gchar *str = NULL;
   gint length;
 
   va_start (args, format);
-  length = gst_info_vasprintf (&str, format, args);
+  length = __gst_vasprintf_buf (&str, inline_buf, sizeof (inline_buf),
+      format, args);
   va_end (args);
 
   if (length == 0 || !str)
@@ -1777,16 +1815,18 @@
     g_printerr ("%s", str);
   } else if (file == stdout) {
     g_print ("%s", str);
+  } else if (file == (FILE *) & _gst_debug_log_handle) {
+    /* By default, we can bypass the CRT's buffering by using WriteFile */
+    DWORD written;
+    WriteFile (_gst_debug_log_handle, str, (DWORD) length, &written, NULL);
   } else {
-    /* We are writing to file. Text editors/viewers should be able to
-     * decode valid UTF-8 string regardless of codepage setting */
+    /* Cater for a custom FILE* user_data in gst_debug_add_log_function */
     fwrite (str, 1, length, file);
-
-    /* FIXME: fflush here might be redundant if setvbuf works as expected */
     fflush (file);
   }
 
-  g_free (str);
+  if (str != inline_buf)
+    g_free (str);
 }
 #endif
 
@@ -3293,6 +3333,10 @@
   }
   g_rw_lock_writer_unlock (&__log_func_mutex);
 
+#ifdef G_OS_WIN32
+  CloseHandle (_gst_debug_log_handle);
+#endif
+
 #ifdef HAVE_UNWIND
 # ifdef HAVE_DW
   if (_global_dwfl) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/gst/gstregistry.c 
new/gstreamer-1.28.4/gst/gstregistry.c
--- old/gstreamer-1.28.3/gst/gstregistry.c      2026-05-11 19:28:12.000000000 
+0200
+++ new/gstreamer-1.28.4/gst/gstregistry.c      2026-06-12 14:19:43.000000000 
+0200
@@ -1643,9 +1643,22 @@
         dir = g_path_get_dirname (real_fname);
         GST_DEBUG ("real directory location: %s", dir);
       } else {
+#ifdef __ANDROID__
+        // This is to be expected -- the path may point to inside the APK
+        // See 
https://android.googlesource.com/platform/bionic/+/main/android-changes-for-ndk-developers.md#opening-shared-libraries-directly-from-an-apk
+        dir = g_path_get_dirname (info.dli_fname);
+        if (strstr (info.dli_fname, ".apk!/") != NULL ||
+            strstr (info.dli_fname, ".zip!/") != NULL) {
+          GST_DEBUG ("libgstreamer-1.0 loaded from within container: %s", dir);
+        } else {
+          GST_LOG ("could not canonicalize path %s: %s", info.dli_fname,
+              g_strerror (errno));
+        }
+#else
         GST_ERROR ("could not canonicalize path %s: %s", info.dli_fname,
             g_strerror (errno));
         dir = g_path_get_dirname (info.dli_fname);
+#endif
       }
       g_free (real_fname);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/gst/gsttask.c 
new/gstreamer-1.28.4/gst/gsttask.c
--- old/gstreamer-1.28.3/gst/gsttask.c  2026-05-11 19:28:12.000000000 +0200
+++ new/gstreamer-1.28.4/gst/gsttask.c  2026-06-12 14:19:43.000000000 +0200
@@ -121,6 +121,9 @@
   /* remember the pool and id that is currently running. */
   gpointer id;
   GstTaskPool *pool_id;
+
+  /* we're currently inside gst_task_join() */
+  gboolean joining;
 };
 
 #ifdef _MSC_VER
@@ -258,6 +261,7 @@
   klass = GST_TASK_GET_CLASS (task);
 
   task->priv = gst_task_get_instance_private (task);
+  task->priv->joining = FALSE;
   task->running = FALSE;
   task->thread = NULL;
   task->lock = NULL;
@@ -346,6 +350,7 @@
   GRecMutex *lock;
   GThread *tself;
   GstTaskPrivate *priv;
+  gboolean joining;
 
   priv = task->priv;
 
@@ -414,18 +419,21 @@
   }
   /* now we allow messing with the lock again by setting the running flag to
    * %FALSE. Together with the SIGNAL this is the sign for the _join() to
-   * complete.
-   * Note that we still have not dropped the final ref on the task. We could
-   * check here if there is a pending join() going on and drop the last ref
-   * before releasing the lock as we can be sure that a ref is held by the
-   * caller of the join(). */
+   * complete. */
   task->running = FALSE;
   GST_TASK_SIGNAL (task);
+  /* Someone called join() while we're exiting, so they are holding
+   * a reference and we can drop ours already. This ensures that finalize() is
+   * called when the joiner's reference is dropped. */
+  joining = task->priv->joining;
+  if (joining)
+    gst_object_unref (task);
+  GST_DEBUG ("Exit task %p, thread %p", task, g_thread_self ());
   GST_OBJECT_UNLOCK (task);
 
-  GST_DEBUG ("Exit task %p, thread %p", task, g_thread_self ());
+  if (!joining)
+    gst_object_unref (task);
 
-  gst_object_unref (task);
   return;
 
 no_lock:
@@ -719,6 +727,7 @@
   /* mark task as running so that a join will wait until we schedule
    * and exit the task function. */
   task->running = TRUE;
+  task->priv->joining = FALSE;
 
   /* push on the thread pool, we remember the original pool because the user
    * could change it later on and then we join to the wrong pool. */
@@ -931,6 +940,7 @@
   GST_OBJECT_LOCK (task);
   if (G_UNLIKELY (tself == task->thread))
     goto joining_self;
+  priv->joining = TRUE;
   SET_TASK_STATE (task, GST_TASK_STOPPED);
   /* signal the state change for when it was blocked in PAUSED. */
   GST_TASK_SIGNAL (task);
@@ -946,6 +956,7 @@
   id = priv->id;
   priv->pool_id = NULL;
   priv->id = NULL;
+  priv->joining = FALSE;
   GST_OBJECT_UNLOCK (task);
 
   if (pool) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/gst/gstvalue.c 
new/gstreamer-1.28.4/gst/gstvalue.c
--- old/gstreamer-1.28.3/gst/gstvalue.c 2026-05-11 19:28:12.000000000 +0200
+++ new/gstreamer-1.28.4/gst/gstvalue.c 2026-06-12 14:19:43.000000000 +0200
@@ -427,7 +427,7 @@
   guint alen;
 
   array = src_value->data[0].v_pointer;
-  alen = array->len;
+  alen = array ? array->len : 0;
 
   /* estimate minimum string length to minimise re-allocs in GString */
   s = g_string_sized_new (2 + (10 * alen) + 2);
@@ -494,7 +494,7 @@
   guint alen;
 
   array = src_value->data[0].v_pointer;
-  alen = array->n_values;
+  alen = array ? array->n_values : 0;
 
   /* estimate minimum string length to minimise re-allocs in GString */
   s = g_string_sized_new (2 + (10 * alen) + 2);
@@ -1158,8 +1158,10 @@
   for (i = 0; i < gst_value_unique_list_get_size (value); i++) {
     p_val = gst_value_unique_list_get_value (value, i);
 
-    if (gst_value_compare (p_val, append_value) == GST_VALUE_EQUAL)
-      return;                   /* value already exist in set */
+    if (gst_value_compare (p_val, append_value) == GST_VALUE_EQUAL) {
+      g_value_unset (append_value);
+      return;
+    }
   }
 
   _gst_value_list_append_and_take_value (value, append_value);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/gst/printf/README 
new/gstreamer-1.28.4/gst/printf/README
--- old/gstreamer-1.28.3/gst/printf/README      2026-05-11 19:28:12.000000000 
+0200
+++ new/gstreamer-1.28.4/gst/printf/README      2026-06-12 14:19:43.000000000 
+0200
@@ -46,9 +46,11 @@
 This was imported from GLib's gnulib subdirectory.
 
 g-gnulib.h and _g_gnulib namespace has been changed to gst-printf.h and
-__gst_printf namespace for GStreamer. Also #define HAVE_SNPRINTF 0 has
-been changed to #undef HAVE_SNPRINTF, and HAVE_ALLOCA has been replaced
-by an #if defined(alloca) || defined(GLIB_HAVE_ALLOCA_H)
+__gst_printf namespace for GStreamer. HAVE_SNPRINTF is defined in
+gst-printf.h (snprintf is universally available on supported platforms);
+%n is never appended to the reconstructed format string passed to snprintf
+so glibc's _FORTIFY_SOURCE=2 check is satisfied. HAVE_ALLOCA has been
+replaced by an #if defined(alloca) || defined(GLIB_HAVE_ALLOCA_H)
 
 printf-extension.[ch] were added to provide support for custom pointer
 arguments (e.g. caps, events, etc.)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/gst/printf/gst-printf.h 
new/gstreamer-1.28.4/gst/printf/gst-printf.h
--- old/gstreamer-1.28.3/gst/printf/gst-printf.h        2026-05-11 
19:28:12.000000000 +0200
+++ new/gstreamer-1.28.4/gst/printf/gst-printf.h        2026-06-12 
14:19:43.000000000 +0200
@@ -36,11 +36,10 @@
 #define realloc g_realloc
 #define free    g_free
 
-/* Don't use snprintf(); we have to use sprintf instead and do our own
- * length calculations, because glibc doesn't allow passing %n in a format
- * string if the string is in writable memory (if glibc has been compiled
- * with _FORTIFY_SOURCE=2 which seems to be the case on some distros/systems) 
*/
-#undef HAVE_SNPRINTF
+/* All platforms we support have C99 snprintf. We never append %n to the
+ * reconstructed format string (see vasnprintf.c), so _FORTIFY_SOURCE=2 is
+ * fine. */
+#define HAVE_SNPRINTF 1
 
 /* based on glib's config.h.win32.in */
 #ifdef G_OS_WIN32
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/gst/printf/printf.c 
new/gstreamer-1.28.4/gst/printf/printf.c
--- old/gstreamer-1.28.3/gst/printf/printf.c    2026-05-11 19:28:12.000000000 
+0200
+++ new/gstreamer-1.28.4/gst/printf/printf.c    2026-06-12 14:19:43.000000000 
+0200
@@ -157,3 +157,19 @@
 
   return length;
 }
+
+/* Format into fixed_buf if the result fits, otherwise into a freshly malloc'd
+ * buffer. The caller must check whether *result == fixed_buf to know whether
+ * to free it. */
+int
+__gst_vasprintf_buf (char **result, char *fixed_buf, size_t fixed_buf_size,
+    char const *format, va_list args)
+{
+  size_t length = fixed_buf_size;
+
+  *result = vasnprintf (fixed_buf, &length, format, args);
+  if (*result == NULL)
+    return -1;
+
+  return length;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/gst/printf/printf.h 
new/gstreamer-1.28.4/gst/printf/printf.h
--- old/gstreamer-1.28.3/gst/printf/printf.h    2026-05-11 19:28:12.000000000 
+0200
+++ new/gstreamer-1.28.4/gst/printf/printf.h    2026-06-12 14:19:43.000000000 
+0200
@@ -60,5 +60,11 @@
                      char const *format,
                      va_list      args);
 
+int __gst_vasprintf_buf (char       **result,
+                         char        *fixed_buf,
+                         size_t       fixed_buf_size,
+                         char const  *format,
+                         va_list      args);
+
 
 #endif /* __GNULIB_PRINTF_H__ */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/gst/printf/vasnprintf.c 
new/gstreamer-1.28.4/gst/printf/vasnprintf.c
--- old/gstreamer-1.28.3/gst/printf/vasnprintf.c        2026-05-11 
19:28:12.000000000 +0200
+++ new/gstreamer-1.28.4/gst/printf/vasnprintf.c        2026-06-12 
14:19:43.000000000 +0200
@@ -668,13 +668,7 @@
               break;
           }
           *p = dp->conversion;
-#ifdef HAVE_SNPRINTF
-          p[1] = '%';
-          p[2] = 'n';
-          p[3] = '\0';
-#else
           p[1] = '\0';
-#endif
 
           /* Construct the arguments for calling snprintf or sprintf.  */
           prefix_count = 0;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/gstreamer.doap 
new/gstreamer-1.28.4/gstreamer.doap
--- old/gstreamer-1.28.3/gstreamer.doap 2026-05-11 19:28:12.000000000 +0200
+++ new/gstreamer-1.28.4/gstreamer.doap 2026-06-12 14:19:43.000000000 +0200
@@ -40,6 +40,16 @@
 
  <release>
   <Version>
+   <revision>1.28.4</revision>
+   <branch>1.28</branch>
+   <name></name>
+   <created>2026-06-12</created>
+   <file-release 
rdf:resource="https://gstreamer.freedesktop.org/src/gstreamer/gstreamer-1.28.4.tar.xz";
 />
+  </Version>
+ </release>
+
+ <release>
+  <Version>
    <revision>1.28.3</revision>
    <branch>1.28</branch>
    <name></name>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/meson.build 
new/gstreamer-1.28.4/meson.build
--- old/gstreamer-1.28.3/meson.build    2026-05-11 19:28:12.000000000 +0200
+++ new/gstreamer-1.28.4/meson.build    2026-06-12 14:19:43.000000000 +0200
@@ -1,5 +1,5 @@
 project('gstreamer', 'c',
-  version : '1.28.3',
+  version : '1.28.4',
   meson_version : '>= 1.4',
   default_options : [ 'warning_level=1',
                       'buildtype=debugoptimized',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/tests/check/gst/gstcaps.c 
new/gstreamer-1.28.4/tests/check/gst/gstcaps.c
--- old/gstreamer-1.28.3/tests/check/gst/gstcaps.c      2026-05-11 
19:28:12.000000000 +0200
+++ new/gstreamer-1.28.4/tests/check/gst/gstcaps.c      2026-06-12 
14:19:43.000000000 +0200
@@ -145,6 +145,50 @@
 
 GST_END_TEST;
 
+GST_START_TEST (test_static_caps_nested_may_be_leaked)
+{
+  /* *INDENT-OFF* */
+  static GstStaticCaps scaps = GST_STATIC_CAPS (
+      "video/x-raw,"
+        "tensors=(structure)["
+          "tensorgroups,"
+            "out=(/uniquelist){"
+              "(GstCaps)["
+                "tensor/strided,"
+                  "tensor-id=(string)out,"
+                  "type=(string)float32"
+              "]"
+            "}"
+        "]");
+  /* *INDENT-ON* */
+  GstCaps *caps;
+  const GValue *tensors, *uniquelist, *nested;
+
+  caps = gst_static_caps_get (&scaps);
+  fail_unless (caps != NULL);
+  fail_unless (GST_MINI_OBJECT_FLAG_IS_SET (caps,
+          GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED));
+
+  /* Descend tensors -> tensorgroups -> out (uniquelist) -> (GstCaps) */
+  tensors = gst_structure_get_value (gst_caps_get_structure (caps, 0),
+      "tensors");
+  fail_unless (tensors != NULL && GST_VALUE_HOLDS_STRUCTURE (tensors));
+
+  uniquelist = gst_structure_get_value (gst_value_get_structure (tensors),
+      "out");
+  fail_unless (uniquelist != NULL && GST_VALUE_HOLDS_UNIQUE_LIST (uniquelist));
+  fail_unless (gst_value_unique_list_get_size (uniquelist) == 1);
+
+  nested = gst_value_unique_list_get_value (uniquelist, 0);
+  fail_unless (nested != NULL && GST_VALUE_HOLDS_CAPS (nested));
+  fail_unless (GST_MINI_OBJECT_FLAG_IS_SET (gst_value_get_caps (nested),
+          GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED));
+
+  gst_caps_unref (caps);
+}
+
+GST_END_TEST;
+
 static const gchar non_simple_caps_string[] =
     "video/x-raw, format=(string)I420, framerate=(fraction)[ 1/100, 100 ], "
     "width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ]; video/x-raw, "
@@ -2607,6 +2651,7 @@
   tcase_add_test (tc_chain, test_double_append);
   tcase_add_test (tc_chain, test_mutability);
   tcase_add_test (tc_chain, test_static_caps);
+  tcase_add_test (tc_chain, test_static_caps_nested_may_be_leaked);
   tcase_add_test (tc_chain, test_simplify);
   tcase_add_test (tc_chain, test_truncate);
   tcase_add_test (tc_chain, test_fixate);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/tests/check/gst/gstmemory.c 
new/gstreamer-1.28.4/tests/check/gst/gstmemory.c
--- old/gstreamer-1.28.3/tests/check/gst/gstmemory.c    2026-05-11 
19:28:12.000000000 +0200
+++ new/gstreamer-1.28.4/tests/check/gst/gstmemory.c    2026-06-12 
14:19:43.000000000 +0200
@@ -25,6 +25,7 @@
 #endif
 
 #include <gst/check/gstcheck.h>
+#include <gst/glib-compat-private.h>    // g_memdup2()
 
 GST_START_TEST (test_submemory)
 {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gstreamer-1.28.3/tests/check/gst/gstvalue.c 
new/gstreamer-1.28.4/tests/check/gst/gstvalue.c
--- old/gstreamer-1.28.3/tests/check/gst/gstvalue.c     2026-05-11 
19:28:12.000000000 +0200
+++ new/gstreamer-1.28.4/tests/check/gst/gstvalue.c     2026-06-12 
14:19:43.000000000 +0200
@@ -4381,7 +4381,7 @@
 
 GST_END_TEST;
 
-GST_START_TEST (test_serialize_null_aray)
+GST_START_TEST (test_serialize_null_array)
 {
   gchar *serialized;
   GValue v = G_VALUE_INIT;
@@ -4397,6 +4397,74 @@
 
 GST_END_TEST;
 
+GST_START_TEST (test_transform_array_to_string)
+{
+  GValue v = G_VALUE_INIT, s = G_VALUE_INIT;
+  GValue iv = G_VALUE_INIT;
+
+  g_value_init (&v, GST_TYPE_ARRAY);
+  g_value_init (&iv, G_TYPE_INT);
+  g_value_set_int (&iv, 1);
+  gst_value_array_append_value (&v, &iv);
+  g_value_set_int (&iv, 2);
+  gst_value_array_append_value (&v, &iv);
+  g_value_set_int (&iv, 3);
+  gst_value_array_append_value (&v, &iv);
+  g_value_unset (&iv);
+
+  g_value_init (&s, G_TYPE_STRING);
+  fail_unless (g_value_transform (&v, &s));
+  fail_unless_equals_string (g_value_get_string (&s), "< 1, 2, 3 >");
+
+  g_value_unset (&v);
+  g_value_unset (&s);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_transform_null_array_to_string)
+{
+  GValue v = G_VALUE_INIT, s = G_VALUE_INIT;
+
+  g_value_init (&v, G_TYPE_VALUE_ARRAY);
+  g_value_set_boxed (&v, NULL);
+
+  g_value_init (&s, G_TYPE_STRING);
+
+  fail_unless (g_value_transform (&v, &s));
+  fail_unless_equals_string (g_value_get_string (&s), "<  >");
+
+  g_value_unset (&v);
+  g_value_unset (&s);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_transform_list_to_string)
+{
+  GValue v = G_VALUE_INIT, s = G_VALUE_INIT;
+  GValue iv = G_VALUE_INIT;
+
+  g_value_init (&v, GST_TYPE_LIST);
+  g_value_init (&iv, G_TYPE_INT);
+  g_value_set_int (&iv, 1);
+  gst_value_list_append_value (&v, &iv);
+  g_value_set_int (&iv, 2);
+  gst_value_list_append_value (&v, &iv);
+  g_value_set_int (&iv, 3);
+  gst_value_list_append_value (&v, &iv);
+  g_value_unset (&iv);
+
+  g_value_init (&s, G_TYPE_STRING);
+  fail_unless (g_value_transform (&v, &s));
+  fail_unless_equals_string (g_value_get_string (&s), "{ 1, 2, 3 }");
+
+  g_value_unset (&v);
+  g_value_unset (&s);
+}
+
+GST_END_TEST;
+
 GST_START_TEST (test_deserialize_array)
 {
   GValue value = { 0 };
@@ -5368,7 +5436,10 @@
   tcase_add_test (tc_chain, test_serialize_deserialize_structure);
   tcase_add_test (tc_chain, test_transform_array);
   tcase_add_test (tc_chain, test_transform_list);
-  tcase_add_test (tc_chain, test_serialize_null_aray);
+  tcase_add_test (tc_chain, test_transform_array_to_string);
+  tcase_add_test (tc_chain, test_serialize_null_array);
+  tcase_add_test (tc_chain, test_transform_null_array_to_string);
+  tcase_add_test (tc_chain, test_transform_list_to_string);
   tcase_add_test (tc_chain, test_deserialize_with_pspec);
   tcase_add_test (tc_chain, test_deserialize_serialize_nested_structures);
   tcase_add_test (tc_chain, test_serialize_deserialize_segment);

++++++ gstreamer.obsinfo ++++++
--- /var/tmp/diff_new_pack.W7AGuN/_old  2026-06-13 18:47:03.496733471 +0200
+++ /var/tmp/diff_new_pack.W7AGuN/_new  2026-06-13 18:47:03.528734800 +0200
@@ -1,5 +1,5 @@
 name: gstreamer
-version: 1.28.3
-mtime: 1778520492
-commit: 62d8936e70b11a2e21ea3c68b7672b675e142945
+version: 1.28.4
+mtime: 1781266783
+commit: b46f881eaa8126eddfd21b5ae5512f8d4ff36255
 

Reply via email to