From 8ea355e52c6625939ba103e0a80a1ff9ff4728f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 26 Apr 2024 14:41:02 +0300 Subject: [PATCH] audioringbuffer: Avoid overflows of segment done counter This counter is incremented once for every segment, meaning it would e.g. overflow after 24 days when using 1ms segments. Once that happens, completely wrong positions are reported and invalid memory is handed out for writing/reading the next segments. As the affected variables are unfortunately part of the public API of the struct, a second set of variables is added together with accessor functions and both variables are kept in sync for backwards compatibility. All existing users of the two variables are moved to the new ones but external code might still run into the overflow. This also slightly breaks API as external code updating the variables will have no effect anymore but the only known user of this is pulsesink. Part-of: --- girs/GstAudio-1.0.gir | 59 +++- .../gst-libs/gst/audio/gstaudiobasesink.c | 11 +- .../gst-libs/gst/audio/gstaudiobasesrc.c | 69 ++--- .../gst-libs/gst/audio/gstaudioringbuffer.c | 253 +++++++++++++++--- .../gst-libs/gst/audio/gstaudioringbuffer.h | 13 +- .../gst-plugins-good/ext/pulse/pulsesink.c | 4 +- 6 files changed, 339 insertions(+), 70 deletions(-) diff --git a/girs/GstAudio-1.0.gir b/girs/GstAudio-1.0.gir index 7189f47d1c..0fbffd648c 100644 --- a/girs/GstAudio-1.0.gir +++ b/girs/GstAudio-1.0.gir @@ -7893,6 +7893,38 @@ MT safe. + + Gets the current segment base number of the ringbuffer. + +MT safe. + + + Current segment base number of the ringbuffer. + + + + + the #GstAudioRingBuffer to use + + + + + + Gets the current segment number of the ringbuffer. + +MT safe. + + + Current segment number of the ringbuffer. + + + + + the #GstAudioRingBuffer to use + + + + Check if the ringbuffer is acquired and ready to use. @@ -8234,6 +8266,25 @@ MT safe. + + Sets the current segment number of the ringbuffer. + +MT safe. + + + + + + + the #GstAudioRingBuffer to use + + + + the segment number to set + + + + @@ -8363,8 +8414,11 @@ MT safe. + + + - + @@ -8687,6 +8741,9 @@ with a flush or stop. samples in DSD format (Since: 1.24) + + + The structure containing the format specification of the ringbuffer. diff --git a/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudiobasesink.c b/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudiobasesink.c index 537425da1e..ccb6c15b42 100644 --- a/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudiobasesink.c +++ b/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudiobasesink.c @@ -1199,8 +1199,8 @@ static guint64 gst_audio_base_sink_get_offset (GstAudioBaseSink * sink) { guint64 sample, sps; - gint writeseg, segdone; - gint diff; + guint64 writeseg, segdone; + gint64 diff; /* assume we can append to the previous sample */ sample = sink->next_sample; @@ -1215,8 +1215,8 @@ gst_audio_base_sink_get_offset (GstAudioBaseSink * sink) writeseg = sample / sps; /* get the currently processed segment */ - segdone = g_atomic_int_get (&sink->ringbuffer->segdone) - - sink->ringbuffer->segbase; + segdone = gst_audio_ring_buffer_get_segdone (sink->ringbuffer) + - gst_audio_ring_buffer_get_segbase (sink->ringbuffer); /* see how far away it is from the write segment */ diff = writeseg - segdone; @@ -1728,7 +1728,8 @@ gst_audio_base_sink_get_alignment (GstAudioBaseSink * sink, gint64 align; gint64 sample_diff; gint64 max_sample_diff; - gint segdone = g_atomic_int_get (&ringbuf->segdone) - ringbuf->segbase; + guint64 segdone = gst_audio_ring_buffer_get_segdone (sink->ringbuffer) + - gst_audio_ring_buffer_get_segbase (sink->ringbuffer); gint64 samples_done = segdone * (gint64) ringbuf->samples_per_seg; gint64 headroom = sample_offset - samples_done; gboolean allow_align = TRUE; diff --git a/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudiobasesrc.c b/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudiobasesrc.c index 04916f36fd..4e4d68f54f 100644 --- a/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudiobasesrc.c +++ b/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudiobasesrc.c @@ -700,8 +700,9 @@ static guint64 gst_audio_base_src_get_offset (GstAudioBaseSrc * src) { guint64 sample; - gint readseg, segdone, segtotal, sps; - gint diff; + guint64 readseg, segdone; + gint segtotal, sps; + gint64 diff; /* assume we can append to the previous sample */ sample = src->next_sample; @@ -710,11 +711,12 @@ gst_audio_base_src_get_offset (GstAudioBaseSrc * src) segtotal = src->ringbuffer->spec.segtotal; /* get the currently processed segment */ - segdone = g_atomic_int_get (&src->ringbuffer->segdone) - - src->ringbuffer->segbase; + segdone = gst_audio_ring_buffer_get_segdone (src->ringbuffer) + - gst_audio_ring_buffer_get_segbase (src->ringbuffer); if (sample != -1) { - GST_DEBUG_OBJECT (src, "at segment %d and sample %" G_GUINT64_FORMAT, + GST_DEBUG_OBJECT (src, + "at segment %" G_GUINT64_FORMAT " and sample %" G_GUINT64_FORMAT, segdone, sample); /* figure out the segment and the offset inside the segment where * the sample should be read from. */ @@ -725,20 +727,22 @@ gst_audio_base_src_get_offset (GstAudioBaseSrc * src) * (where we are reading). */ diff = segdone - readseg; if (diff >= segtotal) { - GST_DEBUG_OBJECT (src, "dropped, align to segment %d", segdone); + GST_DEBUG_OBJECT (src, "dropped, align to segment %" G_GUINT64_FORMAT, + segdone); /* sample would be dropped, position to next playable position */ sample = ((guint64) (segdone)) * sps; } } else { /* no previous sample, go to the current position */ - GST_DEBUG_OBJECT (src, "first sample, align to current %d", segdone); - sample = ((guint64) (segdone)) * sps; + GST_DEBUG_OBJECT (src, "first sample, align to current %" G_GUINT64_FORMAT, + segdone); + sample = segdone * sps; readseg = segdone; } GST_DEBUG_OBJECT (src, - "reading from %d, we are at %d, sample %" G_GUINT64_FORMAT, readseg, - segdone, sample); + "reading from %" G_GUINT64_FORMAT ", we are at %" G_GUINT64_FORMAT + ", sample %" G_GUINT64_FORMAT, readseg, segdone, sample); return sample; } @@ -876,20 +880,22 @@ gst_audio_base_src_create (GstBaseSrc * bsrc, guint64 offset, guint length, GstClockTime base_time; GstClockTime current_time; guint64 running_time_sample; - gint running_time_segment; - gint last_read_segment; - gint segment_skew; + guint64 running_time_segment; + guint64 last_read_segment; + gint64 segment_skew; gint sps; - gint segments_written; - gint last_written_segment; + guint64 segments_written; + guint64 last_written_segment; /* get the amount of segments written from the device by now */ - segments_written = g_atomic_int_get (&ringbuffer->segdone); + segments_written = gst_audio_ring_buffer_get_segdone (src->ringbuffer); /* subtract the base to segments_written to get the number of the * last written segment in the ringbuffer * (one segment written = segment 0) */ - last_written_segment = segments_written - ringbuffer->segbase - 1; + last_written_segment = + segments_written - gst_audio_ring_buffer_get_segbase (ringbuffer) - + 1; /* samples per segment */ sps = ringbuffer->samples_per_seg; @@ -923,13 +929,16 @@ gst_audio_base_src_create (GstBaseSrc * bsrc, guint64 offset, guint length, GST_TIME_FORMAT "\n timestamp = %" GST_TIME_FORMAT - "\n running_time_segment = %d" - "\n last_written_segment = %d" - "\n segment_skew (running time segment - last_written_segment) = %d" - "\n last_read_segment = %d", - GST_TIME_ARGS (running_time), GST_TIME_ARGS (timestamp), - running_time_segment, last_written_segment, segment_skew, - last_read_segment); + "\n running_time_segment = %" + G_GUINT64_FORMAT + "\n last_written_segment = %" + G_GUINT64_FORMAT + "\n segment_skew (running time segment - last_written_segment) = %" + G_GINT64_FORMAT + "\n last_read_segment = %" + G_GUINT64_FORMAT, GST_TIME_ARGS (running_time), + GST_TIME_ARGS (timestamp), running_time_segment, + last_written_segment, segment_skew, last_read_segment); /* Resync the ringbuffer if: * @@ -943,8 +952,8 @@ gst_audio_base_src_create (GstBaseSrc * bsrc, guint64 offset, guint length, */ if ((segment_skew >= ringbuffer->spec.segtotal) || (last_read_segment == 0) || first_sample) { - gint new_read_segment; - gint segment_diff; + guint64 new_read_segment; + guint64 segment_diff; guint64 new_sample; /* the difference between running_time and the last written segment */ @@ -954,11 +963,11 @@ gst_audio_base_src_create (GstBaseSrc * bsrc, guint64 offset, guint length, gst_audio_ring_buffer_advance (ringbuffer, segment_diff); /* we move the new read segment to the last known written segment */ - new_read_segment = - g_atomic_int_get (&ringbuffer->segdone) - ringbuffer->segbase; + new_read_segment = gst_audio_ring_buffer_get_segdone (src->ringbuffer) + - gst_audio_ring_buffer_get_segbase (src->ringbuffer); /* we calculate the new sample value */ - new_sample = ((guint64) new_read_segment) * sps; + new_sample = new_read_segment * sps; /* and get the relative time to this -> our new timestamp */ timestamp = gst_util_uint64_scale_int (new_sample, GST_SECOND, rate); @@ -967,7 +976,7 @@ gst_audio_base_src_create (GstBaseSrc * bsrc, guint64 offset, guint length, src->next_sample = new_sample + samples; GST_DEBUG_OBJECT (bsrc, - "Timeshifted the ringbuffer with %d segments: " + "Timeshifted the ringbuffer with %" G_GINT64_FORMAT " segments: " "Updating the timestamp to %" GST_TIME_FORMAT ", " "and src->next_sample to %" G_GUINT64_FORMAT, segment_diff, GST_TIME_ARGS (timestamp), src->next_sample); diff --git a/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c b/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c index bd08a24738..9781ef28bb 100644 --- a/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c +++ b/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c @@ -46,6 +46,124 @@ GST_DEBUG_CATEGORY_STATIC (gst_audio_ring_buffer_debug); #define GST_CAT_DEFAULT gst_audio_ring_buffer_debug +/* TODO: use GLib's once https://gitlab.gnome.org/GNOME/glib/issues/1076 lands, or + * use C11 atomics once MS arrives in this century. + * + * We also assume that signed overflow just wraps around because unfortunately + * there are no unsigned versions in MSVC. */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) +#include + +static inline guint64 +gst_atomic_uint64_add (guint64 * atomic, guint64 n) +{ + return atomic_fetch_add ((_Atomic guint64 *) atomic, n); +} + +static inline void +gst_atomic_uint64_set (guint64 * atomic, guint64 n) +{ + atomic_store ((_Atomic guint64 *) atomic, n); +} + +static inline guint64 +gst_atomic_uint64_get (guint64 * atomic) +{ + gint64 ret = atomic_load ((_Atomic guint64 *) atomic); + return ret; +} +#elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) +static inline guint64 +gst_atomic_uint64_add (guint64 * atomic, guint64 n) +{ + return __sync_fetch_and_add (atomic, n); +} + +static inline void +gst_atomic_uint64_set (guint64 * atomic, guint64 n) +{ + __sync_synchronize (); + __asm__ __volatile__ ("":::"memory"); + *atomic = n; +} + +static inline guint64 +gst_atomic_uint64_get (guint64 * atomic) +{ + gint64 ret = *atomic; + __sync_synchronize (); + __asm__ __volatile__ ("":::"memory"); + return ret; +} +#elif defined (G_PLATFORM_WIN32) +#include +static inline guint64 +gst_atomic_uint64_add (guint64 * atomic, guint64 n) +{ + return InterlockedExchangeAdd64 ((gint64 *) atomic, (gint64) n); +} + +static inline void +gst_atomic_uint64_set (guint64 * atomic, guint64 n) +{ + *atomic = n; + MemoryBarrier (); +} + +static inline guint64 +gst_atomic_uint64_get (guint64 * atomic) +{ + MemoryBarrier (); + return *atomic; +} +#else +#define STR_TOKEN(s) #s +#define STR(s) STR_TOKEN(s) +#pragma message "No 64-bit atomic int defined for this " STR(TARGET_CPU) " platform/toolchain!" + +#define NO_64BIT_ATOMIC_INT_FOR_PLATFORM +G_LOCK_DEFINE_STATIC (atomic_lock); +static inline guint64 +gst_atomic_uint64_add (guint64 * atomic, guint64 n) +{ + guint64 ret; + + G_LOCK (atomic_lock); + *atomic += n; + ret = *atomic; + G_UNLOCK (atomic_lock); + + return ret; +} + +static inline void +gst_atomic_uint64_set (guint64 * atomic, guint64 n) +{ + G_LOCK (atomic_lock); + *atomic = n; + G_UNLOCK (atomic_lock); +} + +static inline guint64 +gst_atomic_uint64_get (gint64 * atomic) +{ + guint64 ret; + + G_LOCK (atomic_lock); + ret = *atomic; + G_UNLOCK (atomic_lock); + + return ret; +} +#endif + +struct _GstAudioRingBufferPrivate +{ + /* ATOMIC */ + guint64 segdone; + guint64 segbase; +}; + static void gst_audio_ring_buffer_dispose (GObject * object); static void gst_audio_ring_buffer_finalize (GObject * object); @@ -55,7 +173,7 @@ static guint default_commit (GstAudioRingBuffer * buf, guint64 * sample, guint8 * data, gint in_samples, gint out_samples, gint * accum); /* ringbuffer abstract base class */ -G_DEFINE_ABSTRACT_TYPE (GstAudioRingBuffer, gst_audio_ring_buffer, +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstAudioRingBuffer, gst_audio_ring_buffer, GST_TYPE_OBJECT); static void @@ -80,6 +198,7 @@ gst_audio_ring_buffer_class_init (GstAudioRingBufferClass * klass) static void gst_audio_ring_buffer_init (GstAudioRingBuffer * ringbuffer) { + ringbuffer->priv = gst_audio_ring_buffer_get_instance_private (ringbuffer); ringbuffer->open = FALSE; ringbuffer->acquired = FALSE; g_atomic_int_set (&ringbuffer->state, GST_AUDIO_RING_BUFFER_STATE_STOPPED); @@ -89,6 +208,8 @@ gst_audio_ring_buffer_init (GstAudioRingBuffer * ringbuffer) ringbuffer->flushing = TRUE; ringbuffer->segbase = 0; ringbuffer->segdone = 0; + ringbuffer->priv->segbase = 0; + ringbuffer->priv->segdone = 0; } static void @@ -813,7 +934,9 @@ gst_audio_ring_buffer_release (GstAudioRingBuffer * buf) if (G_UNLIKELY (!res)) goto release_failed; + gst_atomic_uint64_set (&buf->priv->segdone, 0); g_atomic_int_set (&buf->segdone, 0); + buf->priv->segbase = 0; buf->segbase = 0; g_free (buf->empty_seg); buf->empty_seg = NULL; @@ -1348,16 +1471,16 @@ not_acquired: guint64 gst_audio_ring_buffer_samples_done (GstAudioRingBuffer * buf) { - gint segdone; + guint64 segdone; guint64 samples; g_return_val_if_fail (GST_IS_AUDIO_RING_BUFFER (buf), 0); /* get the amount of segments we processed */ - segdone = g_atomic_int_get (&buf->segdone); + segdone = gst_atomic_uint64_get (&buf->priv->segdone); /* convert to samples */ - samples = ((guint64) segdone) * buf->samples_per_seg; + samples = segdone * buf->samples_per_seg; return samples; } @@ -1379,6 +1502,8 @@ gst_audio_ring_buffer_samples_done (GstAudioRingBuffer * buf) void gst_audio_ring_buffer_set_sample (GstAudioRingBuffer * buf, guint64 sample) { + guint64 segdone; + g_return_if_fail (GST_IS_AUDIO_RING_BUFFER (buf)); if (sample == -1) @@ -1390,12 +1515,69 @@ gst_audio_ring_buffer_set_sample (GstAudioRingBuffer * buf, guint64 sample) /* FIXME, we assume the ringbuffer can restart at a random * position, round down to the beginning and keep track of * offset when calculating the processed samples. */ - buf->segbase = buf->segdone - sample / buf->samples_per_seg; + segdone = gst_atomic_uint64_get (&buf->priv->segdone); + buf->priv->segbase = segdone - sample / buf->samples_per_seg; + buf->segbase = buf->priv->segbase; gst_audio_ring_buffer_clear_all (buf); - GST_DEBUG_OBJECT (buf, "set sample to %" G_GUINT64_FORMAT ", segbase %d", - sample, buf->segbase); + GST_DEBUG_OBJECT (buf, + "set sample to %" G_GUINT64_FORMAT ", segbase %" G_GUINT64_FORMAT, sample, + buf->priv->segbase); +} + +/** + * gst_audio_ring_buffer_set_segdone: + * @buf: the #GstAudioRingBuffer to use + * @segdone: the segment number to set + * + * Sets the current segment number of the ringbuffer. + * + * MT safe. + * + * Since: 1.26 + */ +void +gst_audio_ring_buffer_set_segdone (GstAudioRingBuffer * buf, guint64 segdone) +{ + gst_atomic_uint64_set (&buf->priv->segdone, segdone); + g_atomic_int_set (&buf->segdone, segdone); +} + +/** + * gst_audio_ring_buffer_get_segdone: + * @buf: the #GstAudioRingBuffer to use + * + * Gets the current segment number of the ringbuffer. + * + * MT safe. + * + * Returns: Current segment number of the ringbuffer. + * + * Since: 1.26 + */ +guint64 +gst_audio_ring_buffer_get_segdone (GstAudioRingBuffer * buf) +{ + return gst_atomic_uint64_get (&buf->priv->segdone); +} + +/** + * gst_audio_ring_buffer_get_segbase: + * @buf: the #GstAudioRingBuffer to use + * + * Gets the current segment base number of the ringbuffer. + * + * MT safe. + * + * Returns: Current segment base number of the ringbuffer. + * + * Since: 1.26 + */ +guint64 +gst_audio_ring_buffer_get_segbase (GstAudioRingBuffer * buf) +{ + return gst_atomic_uint64_get (&buf->priv->segbase); } /** @@ -1445,7 +1627,7 @@ gst_audio_ring_buffer_clear_all (GstAudioRingBuffer * buf) static gboolean wait_segment (GstAudioRingBuffer * buf) { - gint segments; + guint64 segments; gboolean wait = TRUE; /* buffer must be started now or we deadlock since nobody is reading */ @@ -1456,12 +1638,12 @@ wait_segment (GstAudioRingBuffer * buf) goto no_start; GST_DEBUG_OBJECT (buf, "start!"); - segments = g_atomic_int_get (&buf->segdone); + segments = gst_atomic_uint64_get (&buf->priv->segdone); gst_audio_ring_buffer_start (buf); /* After starting, the writer may have wrote segments already and then we * don't need to wait anymore */ - if (G_LIKELY (g_atomic_int_get (&buf->segdone) != segments)) + if (G_LIKELY (gst_atomic_uint64_get (&buf->priv->segdone) != segments)) wait = FALSE; } @@ -1625,10 +1807,10 @@ static guint default_commit (GstAudioRingBuffer * buf, guint64 * sample, guint8 * data, gint in_samples, gint out_samples, gint * accum) { - gint segdone; + guint64 segdone; gint segsize, segtotal, channels, bps, bpf, sps; guint8 *dest, *data_end; - gint writeseg, sampleoff; + guint64 writeseg, sampleoff; gint *toprocess; gint inr, outr; gboolean reverse; @@ -1677,17 +1859,20 @@ default_commit (GstAudioRingBuffer * buf, guint64 * sample, gboolean skip; while (TRUE) { - gint diff; + gint64 diff; /* get the currently processed segment */ - segdone = g_atomic_int_get (&buf->segdone) - buf->segbase; + segdone = + gst_atomic_uint64_get (&buf->priv->segdone) - buf->priv->segbase; /* see how far away it is from the write segment */ diff = writeseg - segdone; GST_DEBUG_OBJECT (buf, - "pointer at %d, write to %d-%d, diff %d, segtotal %d, segsize %d, base %d", - segdone, writeseg, sampleoff, diff, segtotal, segsize, buf->segbase); + "pointer at %" G_GUINT64_FORMAT ", write to %" G_GUINT64_FORMAT "-%" + G_GUINT64_FORMAT ", diff %" G_GINT64_FORMAT + ", segtotal %d, segsize %d, base %" G_GUINT64_FORMAT, segdone, + writeseg, sampleoff, diff, segtotal, segsize, buf->priv->segbase); /* segment too far ahead, writer too slow, we need to drop, hopefully UNLIKELY */ if (G_UNLIKELY (diff < 0)) { @@ -1716,7 +1901,8 @@ default_commit (GstAudioRingBuffer * buf, guint64 * sample, d_end = d + avail; *sample += avail / bpf; - GST_DEBUG_OBJECT (buf, "write @%p seg %d, sps %d, off %d, avail %d", + GST_DEBUG_OBJECT (buf, + "write @%p seg %d, sps %d, off %" G_GUINT64_FORMAT ", avail %d", dest + ws * segsize, ws, sps, sampleoff, avail); if (need_reorder) { @@ -1859,8 +2045,8 @@ guint gst_audio_ring_buffer_read (GstAudioRingBuffer * buf, guint64 sample, guint8 * data, guint len, GstClockTime * timestamp) { - gint segdone; - gint segsize, segtotal, channels, bps, bpf, sps, readseg = 0; + guint64 segdone, readseg = 0; + gint segsize, segtotal, channels, bps, bpf, sps; guint8 *dest; guint to_read; gboolean need_reorder; @@ -1890,10 +2076,11 @@ gst_audio_ring_buffer_read (GstAudioRingBuffer * buf, guint64 sample, sampleoff = (sample % sps); while (TRUE) { - gint diff; + gint64 diff; /* get the currently processed segment */ - segdone = g_atomic_int_get (&buf->segdone) - buf->segbase; + segdone = + gst_atomic_uint64_get (&buf->priv->segdone) - buf->priv->segbase; /* see how far away it is from the read segment, normally segdone (where * the hardware is writing) is bigger than readseg (where software is @@ -1901,10 +2088,10 @@ gst_audio_ring_buffer_read (GstAudioRingBuffer * buf, guint64 sample, diff = segdone - readseg; GST_DEBUG_OBJECT - (buf, "pointer at %d, sample %" G_GUINT64_FORMAT - ", read from %d-%d, to_read %d, diff %d, segtotal %d, segsize %d", - segdone, sample, readseg, sampleoff, to_read, diff, segtotal, - segsize); + (buf, "pointer at %" G_GUINT64_FORMAT ", sample %" G_GUINT64_FORMAT + ", read from %" G_GUINT64_FORMAT "-%d, to_read %d, diff %" + G_GINT64_FORMAT ", segtotal %d, segsize %d", segdone, sample, readseg, + sampleoff, to_read, diff, segtotal, segsize); /* segment too far ahead, reader too slow */ if (G_UNLIKELY (diff >= segtotal)) { @@ -1928,7 +2115,8 @@ gst_audio_ring_buffer_read (GstAudioRingBuffer * buf, guint64 sample, readseg = readseg % segtotal; sampleslen = MIN (sps - sampleoff, to_read); - GST_DEBUG_OBJECT (buf, "read @%p seg %d, off %d, sampleslen %d", + GST_DEBUG_OBJECT (buf, + "read @%p seg %" G_GUINT64_FORMAT ", off %d, sampleslen %d", dest + readseg * segsize, readseg, sampleoff, sampleslen); if (need_reorder) { @@ -1957,7 +2145,8 @@ gst_audio_ring_buffer_read (GstAudioRingBuffer * buf, guint64 sample, if (buf->timestamps && timestamp) { *timestamp = buf->timestamps[readseg % segtotal]; GST_DEBUG_OBJECT (buf, "Retrieved timestamp %" GST_TIME_FORMAT - " @ %d", GST_TIME_ARGS (*timestamp), readseg % segtotal); + " @ %" G_GUINT64_FORMAT, GST_TIME_ARGS (*timestamp), + readseg % segtotal); } return len - to_read; @@ -1990,7 +2179,7 @@ gst_audio_ring_buffer_prepare_read (GstAudioRingBuffer * buf, gint * segment, guint8 ** readptr, gint * len) { guint8 *data; - gint segdone; + guint64 segdone; g_return_val_if_fail (GST_IS_AUDIO_RING_BUFFER (buf), FALSE); @@ -2008,14 +2197,15 @@ gst_audio_ring_buffer_prepare_read (GstAudioRingBuffer * buf, gint * segment, data = buf->memory; /* get the position of the pointer */ - segdone = g_atomic_int_get (&buf->segdone); + segdone = gst_atomic_uint64_get (&buf->priv->segdone); *segment = segdone % buf->spec.segtotal; *len = buf->spec.segsize; *readptr = data + *segment * *len; - GST_LOG_OBJECT (buf, "prepare read from segment %d (real %d) @%p", - *segment, segdone, *readptr); + GST_LOG_OBJECT (buf, + "prepare read from segment %d (real %" G_GUINT64_FORMAT ") @%p", *segment, + segdone, *readptr); /* callback to fill the memory with data, for pull based * scheduling. */ @@ -2041,6 +2231,7 @@ gst_audio_ring_buffer_advance (GstAudioRingBuffer * buf, guint advance) g_return_if_fail (GST_IS_AUDIO_RING_BUFFER (buf)); /* update counter */ + gst_atomic_uint64_add (&buf->priv->segdone, advance); g_atomic_int_add (&buf->segdone, advance); /* the lock is already taken when the waiting flag is set, diff --git a/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h b/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h index e188636145..3f9db5b84a 100644 --- a/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h +++ b/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h @@ -42,6 +42,7 @@ G_BEGIN_DECLS typedef struct _GstAudioRingBuffer GstAudioRingBuffer; typedef struct _GstAudioRingBufferClass GstAudioRingBufferClass; typedef struct _GstAudioRingBufferSpec GstAudioRingBufferSpec; +typedef struct _GstAudioRingBufferPrivate GstAudioRingBufferPrivate; /** * GstAudioRingBufferCallback: @@ -241,7 +242,8 @@ struct _GstAudioRingBuffer { GDestroyNotify cb_data_notify; /*< private >*/ - gpointer _gst_reserved[GST_PADDING - 1]; + GstAudioRingBufferPrivate *priv; + gpointer _gst_reserved[GST_PADDING - 2]; }; /** @@ -435,6 +437,15 @@ void gst_audio_ring_buffer_advance (GstAudioRingBuffer *buf, GST_AUDIO_API void gst_audio_ring_buffer_may_start (GstAudioRingBuffer *buf, gboolean allowed); +GST_AUDIO_API +void gst_audio_ring_buffer_set_segdone (GstAudioRingBuffer *buf, guint64 segdone); + +GST_AUDIO_API +guint64 gst_audio_ring_buffer_get_segdone (GstAudioRingBuffer *buf); + +GST_AUDIO_API +guint64 gst_audio_ring_buffer_get_segbase (GstAudioRingBuffer *buf); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstAudioRingBuffer, gst_object_unref) G_END_DECLS diff --git a/subprojects/gst-plugins-good/ext/pulse/pulsesink.c b/subprojects/gst-plugins-good/ext/pulse/pulsesink.c index a4b86de489..a141e8018a 100644 --- a/subprojects/gst-plugins-good/ext/pulse/pulsesink.c +++ b/subprojects/gst-plugins-good/ext/pulse/pulsesink.c @@ -744,8 +744,8 @@ gst_pulsering_stream_latency_cb (pa_stream * s, void *userdata) * less. One concern here is that latency updates happen every 100ms, which * means segdone is not updated very often, but increasing the update * frequency would mean more communication overhead. */ - g_atomic_int_set (&ringbuf->segdone, - (int) gst_util_uint64_scale_ceil (info->read_index, 1, + gst_audio_ring_buffer_set_segdone (ringbuf, + gst_util_uint64_scale_ceil (info->read_index, 1, ringbuf->spec.segsize)); }