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-07-12 16:20:36 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/gstreamer (Old) and /work/SRC/openSUSE:Factory/.gstreamer.new.1991 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "gstreamer" Sun Jul 12 16:20:36 2026 rev:124 rq:1364875 version:1.28.5 Changes: -------- --- /work/SRC/openSUSE:Factory/gstreamer/gstreamer.changes 2026-06-13 18:46:56.120427022 +0200 +++ /work/SRC/openSUSE:Factory/.gstreamer.new.1991/gstreamer.changes 2026-07-12 16:21:07.543938060 +0200 @@ -1,0 +2,44 @@ +Thu Jul 9 17:11:31 UTC 2026 - Bjørn Lie <[email protected]> + +- Update to version 1.28.5: + + Highlighted bugfixes: + - Various security fixes and playback fixes + - Fix subtitles cause green flickering with VA decoders on AMD + GPUs + - core: Fix sticky event raciness when pads are linked mid-push + - appsrc: Uniformly handle EOS events being pushed + - audio-resampler-neon fails to build for targets with neon but + without thumb + - avtp: Correct ptime generation from avtp timestamp + - fmp4mux: Various fixes for splitting at fragment boundaries + - gldownload: fix wrong DRM format negotiation causing R/B + channel swap + - gdkpixbufdec: Drop rank to NONE and handle resolution/format + changes + - gopbuffer: add support for H.266/VVC + - h265decoder: Fix HEVC with alpha decoding + - mp4mux: fix AC-3 template caps so muxer can accept input from + ac3parse + - mpegtsmux: Always assign PTS to output buffers in CBR mode + - mpegtsmux: Output buffers with PCR-only bitrate-padding + packets have wrong PTS + - rtcpbuffer: Fix parsing of SR+SDES compound packets + (regression from security fix) + - rtspsrc2: Allow disabling SRTP/SRTCP encryption and SRTP + authentication + - tsdemux: Improve PTS rollover handling in ignore-pcr mode, + fixing intermittent corruption with YouTube HLS streams + - textaccumulate: output joined single buffer, add list as meta + - threadshare: add ts-clocksync + - webrtcsink: negotiation fixes and improvements + - Various bug fixes, build fixes, memory leak fixes, and other + stability and reliability improvements + + gstreamer: + - caps: fix field name leak in gst_caps_structure_simplify + - info: Use a more optimized version of GST_TIME_ARGS + - pads: Fix sticky event raciness when linked mid-push + - typefindhelper: Drop unnecessary check for factories without + a function + - Use named pipes when spawning gst-ptp-helper on Windows + +------------------------------------------------------------------- Old: ---- gstreamer-1.28.4.obscpio New: ---- gstreamer-1.28.5.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ gstreamer.spec ++++++ --- /var/tmp/diff_new_pack.gDyL00/_old 2026-07-12 16:21:08.339965287 +0200 +++ /var/tmp/diff_new_pack.gDyL00/_new 2026-07-12 16:21:08.339965287 +0200 @@ -19,7 +19,7 @@ %define gst_branch 1.0 Name: gstreamer -Version: 1.28.4 +Version: 1.28.5 Release: 0 Summary: Streaming-Media Framework Runtime License: LGPL-2.1-or-later ++++++ _service ++++++ --- /var/tmp/diff_new_pack.gDyL00/_old 2026-07-12 16:21:08.367966245 +0200 +++ /var/tmp/diff_new_pack.gDyL00/_new 2026-07-12 16:21:08.371966381 +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.4</param> + <param name="revision">1.28.5</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.4.obscpio -> gstreamer-1.28.5.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gstreamer-1.28.4/gst/gstcaps.c new/gstreamer-1.28.5/gst/gstcaps.c --- old/gstreamer-1.28.4/gst/gstcaps.c 2026-06-12 14:19:43.000000000 +0200 +++ new/gstreamer-1.28.5/gst/gstcaps.c 2026-07-08 23:42:39.000000000 +0200 @@ -2547,9 +2547,11 @@ g_free (one); g_free (two); } + gst_id_str_clear (&field.name); return ret; } + gst_id_str_clear (&field.name); return FALSE; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gstreamer-1.28.4/gst/gstinfo.c new/gstreamer-1.28.5/gst/gstinfo.c --- old/gstreamer-1.28.4/gst/gstinfo.c 2026-06-12 14:19:43.000000000 +0200 +++ new/gstreamer-1.28.5/gst/gstinfo.c 2026-07-08 23:42:39.000000000 +0200 @@ -146,6 +146,105 @@ #define GST_ENABLE_FORMAT_NONLITERAL_WARNING #endif +#undef GST_TIME_ARGS +#undef GST_STIME_ARGS +#undef GST_TIME_FORMAT +#undef GST_STIME_FORMAT + +static inline int +_u32_to_dec (char *p, guint32 v) +{ + if (v == 0) { + *p = '0'; + return 1; + } + char tmp[10]; /* UINT32_MAX = 4294967295, 10 digits */ + int n = 0; + while (v) { + tmp[n++] = '0' + (v % 10); + v /= 10; + } + for (int i = 0; i < n; ++i) + p[i] = tmp[n - 1 - i]; + return n; +} + +/* Internal: writes "[H...]H:MM:SS.nnnnnnnnn" at p, returns the end pointer. + Caller guarantees ns < GST_SECOND. */ +static inline char * +_gst_write_hms_ns (char *p, guint64 sec, guint ns) +{ + guint h = (guint) (sec / 3600); /* fits in 32 bits: + max ≈ 5.12e6 (unsigned) + max ≈ 2.56e6 (signed) */ + guint r = (guint) (sec - (guint64) h * 3600); + guint m = r / 60; + guint s = r - m * 60; + + p += _u32_to_dec (p, h); + *p++ = ':'; + *p++ = '0' + (m / 10); + *p++ = '0' + (m % 10); + *p++ = ':'; + *p++ = '0' + (s / 10); + *p++ = '0' + (s % 10); + *p++ = '.'; + for (int i = 8; i >= 0; --i) { + p[i] = '0' + (ns % 10); + ns /= 10; + } + return p + 9; +} + +static inline const char * +_gst_t_str (GstClockTime t, char *buf) +{ + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (t))) { + memcpy (buf, "99:99:99.999999999", 19); /* incl. NUL */ + return buf; + } + guint64 sec = t / GST_SECOND; + guint ns = (guint) (t - sec * GST_SECOND); + *_gst_write_hms_ns (buf, sec, ns) = '\0'; + return buf; +} + +static inline const char * +_gst_st_str (GstClockTimeDiff t, char *buf) +{ + if (G_UNLIKELY (!GST_CLOCK_STIME_IS_VALID (t))) { + memcpy (buf, "+99:99:99.999999999", 20); /* incl. NUL */ + return buf; + } + char *p = buf; + guint64 abs_t; + if (t < 0) { + *p++ = '-'; + /* Unsigned negation is defined modulo 2^64 and yields the correct + magnitude even at t == G_MININT64, where -(gint64)t would be UB. + For valid inputs (t != G_MININT64) the magnitude fits in gint64 + anyway; this idiom just avoids a special case. */ + abs_t = -(guint64) t; + } else { + *p++ = '+'; + abs_t = (guint64) t; + } + guint64 sec = abs_t / GST_SECOND; + guint ns = (guint) (abs_t - sec * GST_SECOND); + *_gst_write_hms_ns (p, sec, ns) = '\0'; + return buf; +} + +/* + * The use of compound literals here is fine because the scope is the enclosing + * block, which is more than enough. The memory is only used for log formatting + * and isn't used outside of the formatting function call. + */ +#define GST_TIME_FORMAT "s" +#define GST_TIME_ARGS(t) _gst_t_str((t), (char[32]){0}) + +#define GST_STIME_FORMAT "s" +#define GST_STIME_ARGS(t) _gst_st_str((t), (char[32]){0}) #ifdef G_OS_WIN32 # define WIN32_LEAN_AND_MEAN /* prevents from including too many things */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gstreamer-1.28.4/gst/gstpad.c new/gstreamer-1.28.5/gst/gstpad.c --- old/gstreamer-1.28.4/gst/gstpad.c 2026-06-12 14:19:43.000000000 +0200 +++ new/gstreamer-1.28.5/gst/gstpad.c 2026-07-08 23:42:39.000000000 +0200 @@ -122,6 +122,12 @@ #define _PAD_PROBE_TYPE_ALL_BOTH_AND_FLUSH (GST_PAD_PROBE_TYPE_ALL_BOTH | GST_PAD_PROBE_TYPE_EVENT_FLUSH) +/* Some internal aliasing on flow return values to ease reading */ +#define GST_FLOW_PROBE_DROPPED GST_FLOW_CUSTOM_SUCCESS +#define GST_FLOW_PROBE_HANDLED GST_FLOW_CUSTOM_SUCCESS_1 +#define GST_FLOW_PROBE_UNSENT GST_FLOW_CUSTOM_SUCCESS_2 +#define GST_FLOW_PROBE_INTERRUPTED (GST_FLOW_CUSTOM_SUCCESS_2 + 1) + /* we have a pending and an active event on the pad. On source pads only the * active event is used. On sinkpads, events are copied to the pending entry and * moved to the active event when the eventfunc returned %TRUE. */ @@ -136,11 +142,14 @@ { guint events_cookie; GArray *events; - guint last_cookie; + guint last_events_cookie; gint using; guint probe_list_cookie; + /* Incremented when the peer changes */ + guint peer_cookie; + /* counter of how many idle probes are running directly from the add_probe * call. Used to block any data flowing in the pad while the idle callback * Doesn't finish its work */ @@ -425,7 +434,8 @@ pad->priv->events = g_array_sized_new (FALSE, TRUE, sizeof (PadEvent), 16); pad->priv->events_cookie = 0; - pad->priv->last_cookie = -1; + pad->priv->last_events_cookie = -1; + pad->priv->peer_cookie = 0; g_cond_init (&pad->priv->activation_cond); pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING; @@ -2579,6 +2589,10 @@ GST_PAD_PEER (srcpad) = sinkpad; GST_PAD_PEER (sinkpad) = srcpad; + /* Increment the srcpad's link cookie that peer might have changed + * to abort in-progress sticky event replay */ + srcpad->priv->peer_cookie++; + /* check events, when something is different, mark pending */ schedule_events (srcpad, sinkpad); @@ -3973,7 +3987,7 @@ GST_DEBUG_OBJECT (pad, "data is dropped"); if (data.called_probes_size > N_STACK_ALLOCATE_PROBES) g_free (data.called_probes); - return GST_FLOW_CUSTOM_SUCCESS; + return GST_FLOW_PROBE_DROPPED; } passed: { @@ -3988,7 +4002,7 @@ GST_DEBUG_OBJECT (pad, "data was handled"); if (data.called_probes_size > N_STACK_ALLOCATE_PROBES) g_free (data.called_probes); - return GST_FLOW_CUSTOM_SUCCESS_1; + return GST_FLOW_PROBE_HANDLED; } } @@ -4078,6 +4092,11 @@ * next. Don't forward sticky events * that would come after that */ GstEvent *event; + + /* The peer cookie when this push_sticky cycle + * started, to detect the case where the pad + * gets re-linked mid-way */ + guint peer_cookie; } PushStickyData; /* Push the sticky event in the #PadEvent pointed by @ev. @@ -4111,7 +4130,7 @@ if (data->event && GST_EVENT_IS_STICKY (data->event) && data_sticky_order <= _to_sticky_order (GST_EVENT_SEGMENT) && data_sticky_order < ev->sticky_order) { - data->ret = GST_FLOW_CUSTOM_SUCCESS_1; + data->ret = GST_FLOW_PROBE_UNSENT; } else { GST_OBJECT_UNLOCK (pad); /* The event argument remains valid during the unlock as the caller must @@ -4132,8 +4151,9 @@ GST_TRACER_PAD_PUSH_EVENT_POST (pad, data->ret >= GST_FLOW_OK); GST_OBJECT_LOCK (pad); - if (data->ret == GST_FLOW_CUSTOM_SUCCESS_1) + if (data->ret == GST_FLOW_PROBE_HANDLED) data->ret = GST_FLOW_OK; + } switch (data->ret) { @@ -4142,14 +4162,14 @@ GST_DEBUG_OBJECT (pad, "event %s marked received", GST_EVENT_TYPE_NAME (event)); break; - case GST_FLOW_CUSTOM_SUCCESS: + case GST_FLOW_PROBE_DROPPED: /* we can't assume the event is received when it was dropped */ GST_DEBUG_OBJECT (pad, "event %s was dropped, mark pending", GST_EVENT_TYPE_NAME (event)); GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS); data->ret = GST_FLOW_OK; break; - case GST_FLOW_CUSTOM_SUCCESS_1: + case GST_FLOW_PROBE_UNSENT: /* event was ignored and should be sent later */ GST_DEBUG_OBJECT (pad, "event %s was ignored, mark pending", GST_EVENT_TYPE_NAME (event)); @@ -4158,7 +4178,8 @@ break; case GST_FLOW_NOT_LINKED: /* not linked is not a problem, we are sticky so the event will be - * rescheduled to be sent later on re-link, but only for non-EOS events */ + * rescheduled to be sent later on re-link, except for EOS events + * which must be received */ GST_DEBUG_OBJECT (pad, "pad was not linked, mark pending"); if (GST_EVENT_TYPE (event) != GST_EVENT_EOS) { data->ret = GST_FLOW_OK; @@ -4175,6 +4196,14 @@ if (data->ret != GST_FLOW_OK && GST_EVENT_TYPE (event) == GST_EVENT_EOS) data->was_eos = TRUE; + /* We unlocked above an in pushing the event, so the pad might have been + * relinked. In that case, we abort and start sending sticky events again */ + if (data->peer_cookie != pad->priv->peer_cookie + && GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_FLAG_PENDING_EVENTS)) { + GST_DEBUG_OBJECT (pad, "Pad peer changed during sticky event pushing"); + data->ret = GST_FLOW_PROBE_INTERRUPTED; + } + return data->ret == GST_FLOW_OK; } @@ -4182,13 +4211,17 @@ * have not been yet received by downstream and that aren't priority-ordered * after the optional @event passed as argument. * - * Must be called with pad LOCK. */ + * Must be called with pad LOCK, but can drop the lock while it pushes events */ static inline GstFlowReturn check_sticky (GstPad * pad, GstEvent * event) { - PushStickyData data = { GST_FLOW_OK, FALSE, event }; + do { + PushStickyData data = { GST_FLOW_OK, FALSE, event, pad->priv->peer_cookie }; + + if (G_LIKELY (!GST_PAD_HAS_PENDING_EVENTS (pad))) { + return GST_FLOW_OK; + } - if (G_UNLIKELY (GST_PAD_HAS_PENDING_EVENTS (pad))) { GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS); GST_DEBUG_OBJECT (pad, "pushing all sticky events"); @@ -4222,12 +4255,24 @@ /* the event could have been dropped. Because this can only * happen if the user asked for it, it's not an error */ - if (data.ret == GST_FLOW_CUSTOM_SUCCESS) + if (data.ret == GST_FLOW_PROBE_DROPPED) data.ret = GST_FLOW_OK; } } - } - return data.ret; + + if (data.ret == GST_FLOW_PROBE_INTERRUPTED) { + GST_DEBUG_OBJECT (pad, + "Pad was re-linked during sticky event sending. Restarting"); + + GstPad *sinkpad = GST_PAD_PEER (pad); + if (sinkpad != NULL) { + schedule_events (pad, sinkpad); + } + /* Fall through and loop */ + } else { + return data.ret; + } + } while (TRUE); } @@ -4360,7 +4405,7 @@ /* if a probe dropped without handling, we don't sent it further but assume * that the probe did not answer the query and return FALSE */ - if (ret != GST_FLOW_CUSTOM_SUCCESS_1) + if (ret != GST_FLOW_PROBE_HANDLED) res = FALSE; else res = TRUE; @@ -4477,7 +4522,7 @@ /* if a probe dropped without handling, we don't sent it further but * assume that the probe did not answer the query and return FALSE */ - if (ret != GST_FLOW_CUSTOM_SUCCESS_1) + if (ret != GST_FLOW_PROBE_HANDLED) res = FALSE; else res = TRUE; @@ -4519,7 +4564,7 @@ goto wrong_mode; #ifdef GST_ENABLE_EXTRA_CHECKS - if (G_UNLIKELY (pad->priv->last_cookie != pad->priv->events_cookie)) { + if (G_UNLIKELY (pad->priv->last_events_cookie != pad->priv->events_cookie)) { if (!find_event_by_type (pad, GST_EVENT_STREAM_START, 0)) { g_warning (G_STRLOC ":%s:<%s:%s> Got data flow before stream-start event", @@ -4530,7 +4575,7 @@ ":%s:<%s:%s> Got data flow before segment event", G_STRFUNC, GST_DEBUG_PAD_NAME (pad)); } - pad->priv->last_cookie = pad->priv->events_cookie; + pad->priv->last_events_cookie = pad->priv->events_cookie; } #endif @@ -4632,13 +4677,13 @@ /* PASSTHROUGH */ probe_stopped: { - /* We unref the buffer, except if the probe handled it (CUSTOM_SUCCESS_1) */ + /* We unref the buffer, except if the probe handled it (GST_FLOW_PROBE_HANDLED) */ if (data && !handled) gst_mini_object_unref (GST_MINI_OBJECT_CAST (data)); switch (ret) { - case GST_FLOW_CUSTOM_SUCCESS: - case GST_FLOW_CUSTOM_SUCCESS_1: + case GST_FLOW_PROBE_DROPPED: + case GST_FLOW_PROBE_HANDLED: GST_DEBUG_OBJECT (pad, "dropped or handled buffer"); ret = GST_FLOW_OK; break; @@ -4809,7 +4854,7 @@ goto wrong_mode; #ifdef GST_ENABLE_EXTRA_CHECKS - if (G_UNLIKELY (pad->priv->last_cookie != pad->priv->events_cookie)) { + if (G_UNLIKELY (pad->priv->last_events_cookie != pad->priv->events_cookie)) { if (!find_event_by_type (pad, GST_EVENT_STREAM_START, 0)) { g_warning (G_STRLOC ":%s:<%s:%s> Got data flow before stream-start event", @@ -4820,7 +4865,7 @@ ":%s:<%s:%s> Got data flow before segment event", G_STRFUNC, GST_DEBUG_PAD_NAME (pad)); } - pad->priv->last_cookie = pad->priv->events_cookie; + pad->priv->last_events_cookie = pad->priv->events_cookie; } #endif @@ -4914,8 +4959,8 @@ gst_mini_object_unref (GST_MINI_OBJECT_CAST (data)); switch (ret) { - case GST_FLOW_CUSTOM_SUCCESS: - case GST_FLOW_CUSTOM_SUCCESS_1: + case GST_FLOW_PROBE_DROPPED: + case GST_FLOW_PROBE_HANDLED: GST_DEBUG_OBJECT (pad, "dropped or handled buffer"); ret = GST_FLOW_OK; break; @@ -5131,7 +5176,7 @@ { GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "probe returned %s", gst_flow_get_name (ret)); - if (ret == GST_FLOW_CUSTOM_SUCCESS) { + if (ret == GST_FLOW_PROBE_DROPPED) { if (res_buf) { /* the probe filled the buffer and asks us to not call the getrange * anymore, we continue with the post probes then. */ @@ -5155,7 +5200,7 @@ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "probe returned %s", gst_flow_get_name (ret)); /* if we drop here, it signals EOS */ - if (ret == GST_FLOW_CUSTOM_SUCCESS) + if (ret == GST_FLOW_PROBE_DROPPED) ret = GST_FLOW_EOS; pad->ABI.abi.last_flowret = ret; GST_OBJECT_UNLOCK (pad); @@ -5356,7 +5401,7 @@ { GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pre probe returned %s", gst_flow_get_name (ret)); - if (ret == GST_FLOW_CUSTOM_SUCCESS) { + if (ret == GST_FLOW_PROBE_DROPPED) { if (res_buf) { /* the probe filled the buffer and asks us to not forward to the peer * anymore, we continue with the post probes then */ @@ -5397,7 +5442,7 @@ "post probe returned %s", gst_flow_get_name (ret)); /* if we drop here, it signals EOS */ - if (ret == GST_FLOW_CUSTOM_SUCCESS) + if (ret == GST_FLOW_PROBE_DROPPED) ret = GST_FLOW_EOS; pad->ABI.abi.last_flowret = ret; @@ -5584,10 +5629,29 @@ /* Forward all sticky events before our current one that are pending */ if (ev->event != data->event - && ev->sticky_order < _to_sticky_order (GST_EVENT_TYPE (data->event))) + && ev->sticky_order < _to_sticky_order (GST_EVENT_TYPE (data->event))) { return push_sticky (pad, ev, data); + } - return TRUE; + return FALSE; +} + +/* Helper to push all changed sticky events that precede the passed event + * in sticky-order. Called with the OBJECT_LOCK held, but may release it + * while pushing an event. Loops in the case that the pad is re-linked + * during the event pushing */ +static void +push_changed_sticky_events_before (GstPad * pad, GstEvent * event) +{ + PushStickyData data = { GST_FLOW_OK, FALSE, event, pad->priv->peer_cookie }; + GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS); + + /* Push all sticky events before our current one + * that have changed */ + do { + events_foreach (pad, sticky_changed, &data); + } while (data.ret == GST_FLOW_PROBE_INTERRUPTED + && GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_FLAG_PENDING_EVENTS)); } /* should be called with pad LOCK */ @@ -5656,12 +5720,7 @@ /* recheck sticky events because the probe might have cause a relink */ if (GST_PAD_HAS_PENDING_EVENTS (pad) && GST_PAD_IS_SRC (pad) && (GST_EVENT_IS_SERIALIZED (event))) { - PushStickyData data = { GST_FLOW_OK, FALSE, event }; - GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS); - - /* Push all sticky events before our current one - * that have changed */ - events_foreach (pad, sticky_changed, &data); + push_changed_sticky_events_before (pad, event); } break; } @@ -5677,12 +5736,7 @@ if (GST_PAD_HAS_PENDING_EVENTS (pad) && GST_PAD_IS_SRC (pad) && (GST_EVENT_IS_SERIALIZED (event)) && GST_EVENT_TYPE (event) != GST_EVENT_FLUSH_STOP) { - PushStickyData data = { GST_FLOW_OK, FALSE, event }; - GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS); - - /* Push all sticky events before our current one - * that have changed */ - events_foreach (pad, sticky_changed, &data); + push_changed_sticky_events_before (pad, event); } /* the pad offset might've been changed by any of the probes above. It @@ -5742,14 +5796,14 @@ probe_stopped: { GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_PENDING_EVENTS); - if (event && ret != GST_FLOW_CUSTOM_SUCCESS_1) + if (event && ret != GST_FLOW_PROBE_HANDLED) gst_event_unref (event); switch (ret) { - case GST_FLOW_CUSTOM_SUCCESS_1: + case GST_FLOW_PROBE_HANDLED: GST_DEBUG_OBJECT (pad, "handled event"); break; - case GST_FLOW_CUSTOM_SUCCESS: + case GST_FLOW_PROBE_DROPPED: GST_DEBUG_OBJECT (pad, "dropped event"); break; default: @@ -5855,8 +5909,8 @@ /* non-serialized and non-sticky events are pushed right away. */ ret = gst_pad_push_event_unchecked (pad, event, type); /* dropped events by a probe are not an error */ - res = (ret == GST_FLOW_OK || ret == GST_FLOW_CUSTOM_SUCCESS - || ret == GST_FLOW_CUSTOM_SUCCESS_1); + res = (ret == GST_FLOW_OK || ret == GST_FLOW_PROBE_DROPPED + || ret == GST_FLOW_PROBE_HANDLED); } else { /* Errors in sticky event pushing are no problem and ignored here * as they will cause more meaningful errors during data flow. @@ -6192,12 +6246,12 @@ if (need_unlock) GST_PAD_STREAM_UNLOCK (pad); /* Only unref if unhandled */ - if (event && ret != GST_FLOW_CUSTOM_SUCCESS_1) + if (event && ret != GST_FLOW_PROBE_HANDLED) gst_event_unref (event); switch (ret) { - case GST_FLOW_CUSTOM_SUCCESS_1: - case GST_FLOW_CUSTOM_SUCCESS: + case GST_FLOW_PROBE_HANDLED: + case GST_FLOW_PROBE_DROPPED: GST_DEBUG_OBJECT (pad, "dropped or handled event"); ret = GST_FLOW_OK; break; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gstreamer-1.28.4/gstreamer.doap new/gstreamer-1.28.5/gstreamer.doap --- old/gstreamer-1.28.4/gstreamer.doap 2026-06-12 14:19:43.000000000 +0200 +++ new/gstreamer-1.28.5/gstreamer.doap 2026-07-08 23:42:39.000000000 +0200 @@ -40,6 +40,16 @@ <release> <Version> + <revision>1.28.5</revision> + <branch>1.28</branch> + <name></name> + <created>2026-07-08</created> + <file-release rdf:resource="https://gstreamer.freedesktop.org/src/gstreamer/gstreamer-1.28.5.tar.xz" /> + </Version> + </release> + + <release> + <Version> <revision>1.28.4</revision> <branch>1.28</branch> <name></name> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gstreamer-1.28.4/libs/gst/base/gsttypefindhelper.c new/gstreamer-1.28.5/libs/gst/base/gsttypefindhelper.c --- old/gstreamer-1.28.4/libs/gst/base/gsttypefindhelper.c 2026-06-12 14:19:43.000000000 +0200 +++ new/gstreamer-1.28.5/libs/gst/base/gsttypefindhelper.c 2026-07-08 23:42:39.000000000 +0200 @@ -964,10 +964,6 @@ factory = GST_TYPE_FIND_FACTORY (l->data); - /* we only want to check those factories without a function */ - if (gst_type_find_factory_has_function (factory)) - continue; - /* get the extension that this typefind factory can handle */ ext = gst_type_find_factory_get_extensions (factory); if (ext == NULL) @@ -1030,10 +1026,6 @@ factory = GST_TYPE_FIND_FACTORY (l->data); - /* We only want to check those factories without a function */ - if (gst_type_find_factory_has_function (factory)) - continue; - /* Get the caps that this typefind factory can handle */ factory_caps = gst_type_find_factory_get_caps (factory); if (!factory_caps) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gstreamer-1.28.4/libs/gst/helpers/ptp/args.rs new/gstreamer-1.28.5/libs/gst/helpers/ptp/args.rs --- old/gstreamer-1.28.4/libs/gst/helpers/ptp/args.rs 2026-06-12 14:19:43.000000000 +0200 +++ new/gstreamer-1.28.5/libs/gst/helpers/ptp/args.rs 2026-07-08 23:42:39.000000000 +0200 @@ -17,11 +17,18 @@ /// Parsed command-line arguments. #[derive(Debug)] +#[allow(dead_code)] pub struct Args { pub interfaces: Vec<String>, pub verbose: bool, pub clock_id: u64, pub ttl: u32, + /// Named pipe path for stdin replacement (Windows named pipe IPC). + pub pipe_in: Option<String>, + /// Named pipe path for stdout replacement (Windows named pipe IPC). + pub pipe_out: Option<String>, + /// Named pipe path for stderr replacement (Windows named pipe IPC). + pub pipe_err: Option<String>, } /// Parse the command-line arguments. @@ -30,6 +37,9 @@ let mut verbose = false; let mut clock_id = 0; let mut ttl = 1; + let mut pipe_in = None; + let mut pipe_out = None; + let mut pipe_err = None; let mut args = env::args(); // Skip executable name @@ -56,6 +66,15 @@ let ttl_arg = args.next().context("No TTL following --ttl")?; ttl = ttl_arg.parse::<u32>().context("Invalid TTL value")?; } + "--pipe-in" => { + pipe_in = Some(args.next().context("No pipe path following --pipe-in")?); + } + "--pipe-out" => { + pipe_out = Some(args.next().context("No pipe path following --pipe-out")?); + } + "--pipe-err" => { + pipe_err = Some(args.next().context("No pipe path following --pipe-err")?); + } arg => { bail!("Unknown command-line argument {}", arg); @@ -68,6 +87,9 @@ verbose, clock_id, ttl, + pipe_in, + pipe_out, + pipe_err, }; info!("Running with arguments {:#?}", args); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gstreamer-1.28.4/libs/gst/helpers/ptp/ffi.rs new/gstreamer-1.28.5/libs/gst/helpers/ptp/ffi.rs --- old/gstreamer-1.28.4/libs/gst/helpers/ptp/ffi.rs 2026-06-12 14:19:43.000000000 +0200 +++ new/gstreamer-1.28.5/libs/gst/helpers/ptp/ffi.rs 2026-07-08 23:42:39.000000000 +0200 @@ -868,8 +868,23 @@ pub fn QueryPerformanceFrequency(lpfrequence: *mut i64) -> i32; pub fn QueryPerformanceCounter(lpperformancecount: *mut i64) -> i32; + + pub fn CreateFileA( + lpfilename: *const u8, + dwdesiredaccess: u32, + dwsharemode: u32, + lpsecurityattributes: *const c_void, + dwcreationdisposition: u32, + dwflagsandattributes: u32, + htemplatefile: HANDLE, + ) -> HANDLE; } + pub const GENERIC_READ: u32 = 0x80000000; + pub const GENERIC_WRITE: u32 = 0x40000000; + pub const OPEN_EXISTING: u32 = 3; + pub const FILE_ATTRIBUTE_NORMAL: u32 = 0x80; + pub const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002; #[link(name = "bcrypt")] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gstreamer-1.28.4/libs/gst/helpers/ptp/io.rs new/gstreamer-1.28.5/libs/gst/helpers/ptp/io.rs --- old/gstreamer-1.28.4/libs/gst/helpers/ptp/io.rs 2026-06-12 14:19:43.000000000 +0200 +++ new/gstreamer-1.28.5/libs/gst/helpers/ptp/io.rs 2026-07-08 23:42:39.000000000 +0200 @@ -469,7 +469,7 @@ net::UdpSocket, os::windows::{io::AsRawSocket, raw::HANDLE}, ptr, - sync::{Arc, Condvar, Mutex}, + sync::{Arc, Condvar, Mutex, OnceLock}, thread, }; @@ -688,6 +688,24 @@ Self::new_internal(sockets, stdin, stdout) } + /// Create a new `Poll` instance using named pipes for stdin/stdout. + /// + /// This is used on Windows to communicate with the parent process via named + /// pipes instead of inherited anonymous pipes, which is required for running + /// in Session 0 (Windows services). + pub fn new_with_named_pipes( + sockets: Vec<(UdpSocket, UdpSocket)>, + pipe_in: &str, + pipe_out: &str, + ) -> Result<Self, Error> { + let stdin = + Stdin::open_named_pipe(pipe_in).context("Failed opening named pipe for stdin")?; + let stdout = Stdout::open_named_pipe(pipe_out) + .context("Failed opening named pipe for stdout")?; + + Self::new_internal(sockets, stdin, stdout) + } + #[cfg(test)] /// Create a new `Poll` instance for testing purposes. /// @@ -1017,6 +1035,38 @@ } } + /// Open a named pipe for reading (as a stdin replacement). + /// + /// This is used on Windows to communicate with the parent process via named + /// pipes instead of inherited anonymous pipes, which is required for running + /// in Session 0 (Windows services). + pub fn open_named_pipe(path: &str) -> Result<Self, Error> { + let path_cstr = + std::ffi::CString::new(path).context("Invalid named pipe path for stdin")?; + // SAFETY: CreateFileA with a valid null-terminated path returns a handle or + // INVALID_HANDLE_VALUE on error. + let handle = unsafe { + let h = CreateFileA( + path_cstr.as_ptr() as *const u8, + GENERIC_READ, + 0, + ptr::null(), + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + ptr::null_mut(), + ); + if h == INVALID_HANDLE_VALUE { + bail!( + source: io::Error::last_os_error(), + "Failed to open named pipe for reading: {}", + path + ); + } + h + }; + Self::from_handle(handle) + } + /// Thread function to signal readiness of stdin. /// /// This thread tries to read a single byte and buffers it, then signals an event @@ -1178,6 +1228,38 @@ Ok(Stdout(handle)) } + + /// Open a named pipe for writing (as a stdout replacement). + /// + /// This is used on Windows to communicate with the parent process via named + /// pipes instead of inherited anonymous pipes, which is required for running + /// in Session 0 (Windows services). + pub fn open_named_pipe(path: &str) -> Result<Self, Error> { + let path_cstr = + std::ffi::CString::new(path).context("Invalid named pipe path for stdout")?; + // SAFETY: CreateFileA with a valid null-terminated path returns a handle or + // INVALID_HANDLE_VALUE on error. + let handle = unsafe { + let h = CreateFileA( + path_cstr.as_ptr() as *const u8, + GENERIC_WRITE, + 0, + ptr::null(), + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + ptr::null_mut(), + ); + if h == INVALID_HANDLE_VALUE { + bail!( + source: io::Error::last_os_error(), + "Failed to open named pipe for writing: {}", + path + ); + } + h + }; + Self::from_handle(handle) + } } impl Write for Stdout { @@ -1227,29 +1309,60 @@ /// cause interleaved output. pub struct Stderr(HANDLE); + struct SyncHandle(HANDLE); + // SAFETY: This is a single-threaded application and even otherwise writing from + // multiple threads at once to a pipe is safe and will only cause interleaved output. + unsafe impl Send for SyncHandle {} + unsafe impl Sync for SyncHandle {} + + /// Lazily-initialized stderr handle. Set once, either by `init_named_pipe` (before any + /// logging) or on first `acquire()` call via the inherited standard error handle. + static STDERR_HANDLE: OnceLock<SyncHandle> = OnceLock::new(); + impl Stderr { + /// Open a named pipe for writing and use it as the stderr handle. + /// + /// This must be called before any logging so that the `OnceLock` is + /// populated with the named-pipe handle rather than the inherited one. + pub fn init_named_pipe(path: &str) -> Result<(), Error> { + let path_cstr = + std::ffi::CString::new(path).context("Invalid named pipe path for stderr")?; + // SAFETY: CreateFileA with a valid null-terminated path returns a handle or + // INVALID_HANDLE_VALUE on error. + let handle = unsafe { + let h = CreateFileA( + path_cstr.as_ptr() as *const u8, + GENERIC_WRITE, + 0, + ptr::null(), + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + ptr::null_mut(), + ); + if h == INVALID_HANDLE_VALUE { + bail!( + source: io::Error::last_os_error(), + "Failed to open named pipe for stderr: {}", + path + ); + } + h + }; + let _ = STDERR_HANDLE.set(SyncHandle(handle)); + Ok(()) + } + #[cfg(not(test))] pub fn acquire() -> Self { - use std::sync::Once; - - struct SyncHandle(HANDLE); - // SAFETY: This is a single-threaded application and even otherwise writing from - // multiple threads at once to a pipe is safe and will only cause interleaved output. - unsafe impl Send for SyncHandle {} - unsafe impl Sync for SyncHandle {} - - static mut STDERR: SyncHandle = SyncHandle(INVALID_HANDLE_VALUE); - static STDERR_ONCE: Once = Once::new(); - - STDERR_ONCE.call_once(|| { + let sync_handle = STDERR_HANDLE.get_or_init(|| { // SAFETY: GetStdHandle returns a borrowed handle, or 0 if none is set or -1 if an // error has happened. let handle = unsafe { let handle = GetStdHandle(STD_ERROR_HANDLE); if handle.is_null() { - return; + return SyncHandle(INVALID_HANDLE_VALUE); } else if handle == INVALID_HANDLE_VALUE { - return; + return SyncHandle(INVALID_HANDLE_VALUE); } handle @@ -1268,19 +1381,13 @@ let _ = SetConsoleMode(handle, 0); } } else if type_ != FILE_TYPE_PIPE { - return; + return SyncHandle(INVALID_HANDLE_VALUE); } - // SAFETY: Only accessed in this function and multiple mutable accesses are - // prevented by the `Once`. - unsafe { - STDERR.0 = handle; - } + SyncHandle(handle) }); - // SAFETY: Only accesses immutably here and all mutable accesses are serialized above - // by the `Once`. - Stderr(unsafe { STDERR.0 }) + Stderr(sync_handle.0) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gstreamer-1.28.4/libs/gst/helpers/ptp/main.rs new/gstreamer-1.28.5/libs/gst/helpers/ptp/main.rs --- old/gstreamer-1.28.4/libs/gst/helpers/ptp/main.rs 2026-06-12 14:19:43.000000000 +0200 +++ new/gstreamer-1.28.5/libs/gst/helpers/ptp/main.rs 2026-07-08 23:42:39.000000000 +0200 @@ -119,6 +119,11 @@ fn run() -> Result<(), Error> { let args = args::parse_args().context("Failed parsing commandline parameters")?; + #[cfg(windows)] + if let Some(ref pipe_err) = args.pipe_err { + io::Stderr::init_named_pipe(pipe_err).context("Failed initializing stderr named pipe")?; + } + let ifaces = list_interfaces(&args).context("Failed listing interfaces")?; let mut sockets = vec![]; @@ -158,6 +163,15 @@ privileges::drop().context("Failed dropping privileges")?; + #[cfg(windows)] + let mut poll = if let (Some(ref pipe_in), Some(ref pipe_out)) = (&args.pipe_in, &args.pipe_out) + { + io::Poll::new_with_named_pipes(sockets, pipe_in, pipe_out) + .context("Failed creating poller with named pipes")? + } else { + io::Poll::new(sockets).context("Failed creating poller")? + }; + #[cfg(not(windows))] let mut poll = io::Poll::new(sockets).context("Failed creating poller")?; // Write clock ID first diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gstreamer-1.28.4/libs/gst/net/gstptpclock.c new/gstreamer-1.28.5/libs/gst/net/gstptpclock.c --- old/gstreamer-1.28.4/libs/gst/net/gstptpclock.c 2026-06-12 14:19:43.000000000 +0200 +++ new/gstreamer-1.28.5/libs/gst/net/gstptpclock.c 2026-07-08 23:42:39.000000000 +0200 @@ -66,6 +66,8 @@ #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <processthreadsapi.h> /* GetCurrentProcessId */ +#include <gio/gwin32inputstream.h> +#include <gio/gwin32outputstream.h> #endif #ifdef HAVE_UNISTD_H @@ -2477,10 +2479,12 @@ STDIO_MESSAGE_HEADER_SIZE, G_PRIORITY_DEFAULT, NULL, (GAsyncReadyCallback) have_stdout_header, NULL); - memset (&stderr_header, 0, STDERR_MESSAGE_HEADER_SIZE); - g_input_stream_read_all_async (stderr_pipe, stderr_header, - STDERR_MESSAGE_HEADER_SIZE, G_PRIORITY_DEFAULT, NULL, - (GAsyncReadyCallback) have_stderr_header, NULL); + if (stderr_pipe) { + memset (&stderr_header, 0, STDERR_MESSAGE_HEADER_SIZE); + g_input_stream_read_all_async (stderr_pipe, stderr_header, + STDERR_MESSAGE_HEADER_SIZE, G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback) have_stderr_header, NULL); + } /* Check every 1 seconds, if we have to cleanup ANNOUNCE or pending syncs message */ cleanup_source = g_timeout_source_new_seconds (1); @@ -2769,6 +2773,11 @@ gst_structure_get_uint (config, "ttl", &ttl); argc += 2; +#ifdef G_OS_WIN32 + /* --pipe-in <name> --pipe-out <name> --pipe-err <name> for named pipe IPC */ + argc += 6; +#endif + // 3 for: executable, -v and NULL argv = g_new0 (gchar *, argc + 3); argc_c = 0; @@ -2858,6 +2867,252 @@ argv[argc_c++] = g_strdup ("-v"); } +#ifdef G_OS_WIN32 + /* On Windows, use named pipes instead of inherited anonymous pipes for + * stdin/stdout communication with the helper process. This avoids depending + * on handle inheritance via CreateProcess(bInheritHandles=TRUE) which is + * unreliable in Session 0 (Windows services). */ + { + HANDLE h_stdin_wr = INVALID_HANDLE_VALUE; + HANDLE h_stdout_rd = INVALID_HANDLE_VALUE; + HANDLE h_stderr_rd = INVALID_HANDLE_VALUE; + gchar *pipe_name_in = NULL; + gchar *pipe_name_out = NULL; + gchar *pipe_name_err = NULL; + guint32 pipe_id; + /* Timeout for the helper to connect to the named pipes */ + const DWORD connect_timeout_ms = 30000; + + pipe_id = g_random_int (); + + /* Create named pipe for stdin (parent writes, child reads). + * Use FILE_FLAG_OVERLAPPED so ConnectNamedPipe can be awaited + * with a timeout instead of blocking indefinitely. */ + pipe_name_in = g_strdup_printf ("\\\\.\\pipe\\gst-ptp-%lu-%08x-in", + (gulong) GetCurrentProcessId (), pipe_id); + h_stdin_wr = CreateNamedPipeA (pipe_name_in, + PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + 1, 4096, 4096, 5000, NULL); + if (h_stdin_wr == INVALID_HANDLE_VALUE) { + guint last_err = GetLastError (); + gchar *msg = g_win32_error_message (last_err); + GST_ERROR ("Failed to create named pipe for stdin: 0x%x (%s)", + last_err, msg); + g_free (msg); + g_free (pipe_name_in); + ret = FALSE; + supported = FALSE; + goto done; + } + + /* Create named pipe for stdout (child writes, parent reads) */ + pipe_name_out = g_strdup_printf ("\\\\.\\pipe\\gst-ptp-%lu-%08x-out", + (gulong) GetCurrentProcessId (), pipe_id); + h_stdout_rd = CreateNamedPipeA (pipe_name_out, + PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + 1, 4096, 4096, 5000, NULL); + if (h_stdout_rd == INVALID_HANDLE_VALUE) { + guint last_err = GetLastError (); + gchar *msg = g_win32_error_message (last_err); + GST_ERROR ("Failed to create named pipe for stdout: 0x%x (%s)", + last_err, msg); + g_free (msg); + g_free (pipe_name_out); + g_free (pipe_name_in); + CloseHandle (h_stdin_wr); + ret = FALSE; + supported = FALSE; + goto done; + } + + /* Create named pipe for stderr (child writes, parent reads) */ + pipe_name_err = g_strdup_printf ("\\\\.\\pipe\\gst-ptp-%lu-%08x-err", + (gulong) GetCurrentProcessId (), pipe_id); + h_stderr_rd = CreateNamedPipeA (pipe_name_err, + PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + 1, 4096, 4096, 5000, NULL); + if (h_stderr_rd == INVALID_HANDLE_VALUE) { + guint last_err = GetLastError (); + gchar *msg = g_win32_error_message (last_err); + GST_ERROR ("Failed to create named pipe for stderr: 0x%x (%s)", + last_err, msg); + g_free (msg); + g_free (pipe_name_err); + g_free (pipe_name_out); + g_free (pipe_name_in); + CloseHandle (h_stdout_rd); + CloseHandle (h_stdin_wr); + ret = FALSE; + supported = FALSE; + goto done; + } + + argv[argc_c++] = g_strdup ("--pipe-in"); + argv[argc_c++] = pipe_name_in; /* ownership transferred to argv */ + argv[argc_c++] = g_strdup ("--pipe-out"); + argv[argc_c++] = pipe_name_out; /* ownership transferred to argv */ + argv[argc_c++] = g_strdup ("--pipe-err"); + argv[argc_c++] = pipe_name_err; /* ownership transferred to argv */ + + ptp_helper_process = + g_subprocess_newv ((const gchar * const *) argv, + G_SUBPROCESS_FLAGS_NONE, &err); + if (!ptp_helper_process) { + GST_ERROR ("Failed to start ptp helper process: %s", err->message); + g_clear_error (&err); + CloseHandle (h_stdin_wr); + CloseHandle (h_stdout_rd); + CloseHandle (h_stderr_rd); + ret = FALSE; + supported = FALSE; + goto done; + } + + /* Wait for the child to connect to the named pipes using overlapped I/O + * with a timeout, so we don't block indefinitely if the helper never + * connects. */ + { + OVERLAPPED ov_in = { 0, }; + OVERLAPPED ov_out = { 0, }; + OVERLAPPED ov_err = { 0, }; + gboolean stdin_connected = FALSE; + gboolean stdout_connected = FALSE; + gboolean stderr_connected = FALSE; + DWORD wait_ret; + + ov_in.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + ov_out.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + ov_err.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + + /* Start async connect for stdin pipe */ + if (ConnectNamedPipe (h_stdin_wr, &ov_in)) { + stdin_connected = TRUE; + } else { + guint last_err = GetLastError (); + if (last_err == ERROR_PIPE_CONNECTED) { + stdin_connected = TRUE; + } else if (last_err != ERROR_IO_PENDING) { + gchar *msg = g_win32_error_message (last_err); + GST_ERROR ("ConnectNamedPipe (stdin) failed: 0x%x (%s)", + last_err, msg); + g_free (msg); + CloseHandle (ov_in.hEvent); + CloseHandle (ov_out.hEvent); + CloseHandle (ov_err.hEvent); + CloseHandle (h_stdin_wr); + CloseHandle (h_stdout_rd); + CloseHandle (h_stderr_rd); + ret = FALSE; + supported = FALSE; + goto done; + } + } + + /* Start async connect for stdout pipe */ + if (ConnectNamedPipe (h_stdout_rd, &ov_out)) { + stdout_connected = TRUE; + } else { + guint last_err = GetLastError (); + if (last_err == ERROR_PIPE_CONNECTED) { + stdout_connected = TRUE; + } else if (last_err != ERROR_IO_PENDING) { + gchar *msg = g_win32_error_message (last_err); + GST_ERROR ("ConnectNamedPipe (stdout) failed: 0x%x (%s)", + last_err, msg); + g_free (msg); + if (!stdin_connected) + CancelIo (h_stdin_wr); + CloseHandle (ov_in.hEvent); + CloseHandle (ov_out.hEvent); + CloseHandle (ov_err.hEvent); + CloseHandle (h_stdin_wr); + CloseHandle (h_stdout_rd); + CloseHandle (h_stderr_rd); + ret = FALSE; + supported = FALSE; + goto done; + } + } + + /* Start async connect for stderr pipe */ + if (ConnectNamedPipe (h_stderr_rd, &ov_err)) { + stderr_connected = TRUE; + } else { + guint last_err = GetLastError (); + if (last_err == ERROR_PIPE_CONNECTED) { + stderr_connected = TRUE; + } else if (last_err != ERROR_IO_PENDING) { + gchar *msg = g_win32_error_message (last_err); + GST_ERROR ("ConnectNamedPipe (stderr) failed: 0x%x (%s)", + last_err, msg); + g_free (msg); + if (!stdin_connected) + CancelIo (h_stdin_wr); + if (!stdout_connected) + CancelIo (h_stdout_rd); + CloseHandle (ov_in.hEvent); + CloseHandle (ov_out.hEvent); + CloseHandle (ov_err.hEvent); + CloseHandle (h_stdin_wr); + CloseHandle (h_stdout_rd); + CloseHandle (h_stderr_rd); + ret = FALSE; + supported = FALSE; + goto done; + } + } + + /* Wait for all pending connects */ + { + HANDLE wait_events[3]; + DWORD wait_count = 0; + + if (!stdin_connected) + wait_events[wait_count++] = ov_in.hEvent; + if (!stdout_connected) + wait_events[wait_count++] = ov_out.hEvent; + if (!stderr_connected) + wait_events[wait_count++] = ov_err.hEvent; + + if (wait_count > 0) { + wait_ret = WaitForMultipleObjects (wait_count, wait_events, + TRUE, connect_timeout_ms); + if (wait_ret == WAIT_TIMEOUT || wait_ret == WAIT_FAILED) { + GST_ERROR ("Timed out waiting for helper to connect to pipes"); + if (!stdin_connected) + CancelIo (h_stdin_wr); + if (!stdout_connected) + CancelIo (h_stdout_rd); + if (!stderr_connected) + CancelIo (h_stderr_rd); + CloseHandle (ov_in.hEvent); + CloseHandle (ov_out.hEvent); + CloseHandle (ov_err.hEvent); + CloseHandle (h_stdin_wr); + CloseHandle (h_stdout_rd); + CloseHandle (h_stderr_rd); + ret = FALSE; + supported = FALSE; + goto done; + } + } + } + + CloseHandle (ov_in.hEvent); + CloseHandle (ov_out.hEvent); + CloseHandle (ov_err.hEvent); + } + + /* Create GIO streams from the named pipe handles. + * The streams take ownership of the handles (close_handle=TRUE). */ + stdin_pipe = G_OUTPUT_STREAM (g_win32_output_stream_new (h_stdin_wr, TRUE)); + stdout_pipe = G_INPUT_STREAM (g_win32_input_stream_new (h_stdout_rd, TRUE)); + stderr_pipe = G_INPUT_STREAM (g_win32_input_stream_new (h_stderr_rd, TRUE)); + } +#else ptp_helper_process = g_subprocess_newv ((const gchar * const *) argv, G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE | @@ -2885,6 +3140,7 @@ supported = FALSE; goto done; } +#endif delay_req_rand = g_rand_new (); observation_system_clock = diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gstreamer-1.28.4/meson.build new/gstreamer-1.28.5/meson.build --- old/gstreamer-1.28.4/meson.build 2026-06-12 14:19:43.000000000 +0200 +++ new/gstreamer-1.28.5/meson.build 2026-07-08 23:42:39.000000000 +0200 @@ -1,5 +1,5 @@ project('gstreamer', 'c', - version : '1.28.4', + version : '1.28.5', meson_version : '>= 1.4', default_options : [ 'warning_level=1', 'buildtype=debugoptimized', @@ -569,7 +569,8 @@ gobject_dep = dependency('gobject-2.0') gmodule_dep = dependency('gmodule-no-export-2.0') if host_system == 'windows' - gio_dep = dependency('gio-2.0') + gio_dep = [dependency('gio-2.0'), + dependency('gio-windows-2.0')] else gio_dep = [dependency('gio-2.0'), dependency('gio-unix-2.0')] ++++++ gstreamer.obsinfo ++++++ --- /var/tmp/diff_new_pack.gDyL00/_old 2026-07-12 16:21:09.464003734 +0200 +++ /var/tmp/diff_new_pack.gDyL00/_new 2026-07-12 16:21:09.472004007 +0200 @@ -1,5 +1,5 @@ name: gstreamer -version: 1.28.4 -mtime: 1781266783 -commit: b46f881eaa8126eddfd21b5ae5512f8d4ff36255 +version: 1.28.5 +mtime: 1783546959 +commit: 727ceb91886862d200f423baf36cde2bb7ce5b4d
