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
