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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6740>
This commit is contained in:
Sebastian Dröge 2024-04-26 14:41:02 +03:00 committed by GStreamer Marge Bot
parent f8246327ef
commit 8ea355e52c
6 changed files with 339 additions and 70 deletions

View file

@ -7893,6 +7893,38 @@ MT safe.</doc>
</instance-parameter> </instance-parameter>
</parameters> </parameters>
</method> </method>
<method name="get_segbase" c:identifier="gst_audio_ring_buffer_get_segbase" version="1.26">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c">Gets the current segment base number of the ringbuffer.
MT safe.</doc>
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h"/>
<return-value transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c">Current segment base number of the ringbuffer.</doc>
<type name="guint64" c:type="guint64"/>
</return-value>
<parameters>
<instance-parameter name="buf" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c">the #GstAudioRingBuffer to use</doc>
<type name="AudioRingBuffer" c:type="GstAudioRingBuffer*"/>
</instance-parameter>
</parameters>
</method>
<method name="get_segdone" c:identifier="gst_audio_ring_buffer_get_segdone" version="1.26">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c">Gets the current segment number of the ringbuffer.
MT safe.</doc>
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h"/>
<return-value transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c">Current segment number of the ringbuffer.</doc>
<type name="guint64" c:type="guint64"/>
</return-value>
<parameters>
<instance-parameter name="buf" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c">the #GstAudioRingBuffer to use</doc>
<type name="AudioRingBuffer" c:type="GstAudioRingBuffer*"/>
</instance-parameter>
</parameters>
</method>
<method name="is_acquired" c:identifier="gst_audio_ring_buffer_is_acquired"> <method name="is_acquired" c:identifier="gst_audio_ring_buffer_is_acquired">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c">Check if the ringbuffer is acquired and ready to use.</doc> <doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c">Check if the ringbuffer is acquired and ready to use.</doc>
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h"/> <source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h"/>
@ -8234,6 +8266,25 @@ MT safe.</doc>
</parameter> </parameter>
</parameters> </parameters>
</method> </method>
<method name="set_segdone" c:identifier="gst_audio_ring_buffer_set_segdone" version="1.26">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c">Sets the current segment number of the ringbuffer.
MT safe.</doc>
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h"/>
<return-value transfer-ownership="none">
<type name="none" c:type="void"/>
</return-value>
<parameters>
<instance-parameter name="buf" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c">the #GstAudioRingBuffer to use</doc>
<type name="AudioRingBuffer" c:type="GstAudioRingBuffer*"/>
</instance-parameter>
<parameter name="segdone" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c">the segment number to set</doc>
<type name="guint64" c:type="guint64"/>
</parameter>
</parameters>
</method>
<method name="set_timestamp" c:identifier="gst_audio_ring_buffer_set_timestamp"> <method name="set_timestamp" c:identifier="gst_audio_ring_buffer_set_timestamp">
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h"/> <source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h"/>
<return-value transfer-ownership="none"> <return-value transfer-ownership="none">
@ -8363,8 +8414,11 @@ MT safe.</doc>
<field name="cb_data_notify" readable="0" private="1"> <field name="cb_data_notify" readable="0" private="1">
<type name="GLib.DestroyNotify" c:type="GDestroyNotify"/> <type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
</field> </field>
<field name="priv" readable="0" private="1">
<type name="AudioRingBufferPrivate" c:type="GstAudioRingBufferPrivate*"/>
</field>
<field name="_gst_reserved" readable="0" private="1"> <field name="_gst_reserved" readable="0" private="1">
<array zero-terminated="0" fixed-size="3"> <array zero-terminated="0" fixed-size="2">
<type name="gpointer" c:type="gpointer"/> <type name="gpointer" c:type="gpointer"/>
</array> </array>
</field> </field>
@ -8687,6 +8741,9 @@ with a flush or stop.</doc>
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h">samples in DSD format (Since: 1.24)</doc> <doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h">samples in DSD format (Since: 1.24)</doc>
</member> </member>
</enumeration> </enumeration>
<record name="AudioRingBufferPrivate" c:type="GstAudioRingBufferPrivate" disguised="1">
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h"/>
</record>
<record name="AudioRingBufferSpec" c:type="GstAudioRingBufferSpec"> <record name="AudioRingBufferSpec" c:type="GstAudioRingBufferSpec">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h">The structure containing the format specification of the ringbuffer. <doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h">The structure containing the format specification of the ringbuffer.

View file

@ -1199,8 +1199,8 @@ static guint64
gst_audio_base_sink_get_offset (GstAudioBaseSink * sink) gst_audio_base_sink_get_offset (GstAudioBaseSink * sink)
{ {
guint64 sample, sps; guint64 sample, sps;
gint writeseg, segdone; guint64 writeseg, segdone;
gint diff; gint64 diff;
/* assume we can append to the previous sample */ /* assume we can append to the previous sample */
sample = sink->next_sample; sample = sink->next_sample;
@ -1215,8 +1215,8 @@ gst_audio_base_sink_get_offset (GstAudioBaseSink * sink)
writeseg = sample / sps; writeseg = sample / sps;
/* get the currently processed segment */ /* get the currently processed segment */
segdone = g_atomic_int_get (&sink->ringbuffer->segdone) segdone = gst_audio_ring_buffer_get_segdone (sink->ringbuffer)
- sink->ringbuffer->segbase; - gst_audio_ring_buffer_get_segbase (sink->ringbuffer);
/* see how far away it is from the write segment */ /* see how far away it is from the write segment */
diff = writeseg - segdone; diff = writeseg - segdone;
@ -1728,7 +1728,8 @@ gst_audio_base_sink_get_alignment (GstAudioBaseSink * sink,
gint64 align; gint64 align;
gint64 sample_diff; gint64 sample_diff;
gint64 max_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 samples_done = segdone * (gint64) ringbuf->samples_per_seg;
gint64 headroom = sample_offset - samples_done; gint64 headroom = sample_offset - samples_done;
gboolean allow_align = TRUE; gboolean allow_align = TRUE;

View file

@ -700,8 +700,9 @@ static guint64
gst_audio_base_src_get_offset (GstAudioBaseSrc * src) gst_audio_base_src_get_offset (GstAudioBaseSrc * src)
{ {
guint64 sample; guint64 sample;
gint readseg, segdone, segtotal, sps; guint64 readseg, segdone;
gint diff; gint segtotal, sps;
gint64 diff;
/* assume we can append to the previous sample */ /* assume we can append to the previous sample */
sample = src->next_sample; sample = src->next_sample;
@ -710,11 +711,12 @@ gst_audio_base_src_get_offset (GstAudioBaseSrc * src)
segtotal = src->ringbuffer->spec.segtotal; segtotal = src->ringbuffer->spec.segtotal;
/* get the currently processed segment */ /* get the currently processed segment */
segdone = g_atomic_int_get (&src->ringbuffer->segdone) segdone = gst_audio_ring_buffer_get_segdone (src->ringbuffer)
- src->ringbuffer->segbase; - gst_audio_ring_buffer_get_segbase (src->ringbuffer);
if (sample != -1) { 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); segdone, sample);
/* figure out the segment and the offset inside the segment where /* figure out the segment and the offset inside the segment where
* the sample should be read from. */ * the sample should be read from. */
@ -725,20 +727,22 @@ gst_audio_base_src_get_offset (GstAudioBaseSrc * src)
* (where we are reading). */ * (where we are reading). */
diff = segdone - readseg; diff = segdone - readseg;
if (diff >= segtotal) { 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 would be dropped, position to next playable position */
sample = ((guint64) (segdone)) * sps; sample = ((guint64) (segdone)) * sps;
} }
} else { } else {
/* no previous sample, go to the current position */ /* no previous sample, go to the current position */
GST_DEBUG_OBJECT (src, "first sample, align to current %d", segdone); GST_DEBUG_OBJECT (src, "first sample, align to current %" G_GUINT64_FORMAT,
sample = ((guint64) (segdone)) * sps; segdone);
sample = segdone * sps;
readseg = segdone; readseg = segdone;
} }
GST_DEBUG_OBJECT (src, GST_DEBUG_OBJECT (src,
"reading from %d, we are at %d, sample %" G_GUINT64_FORMAT, readseg, "reading from %" G_GUINT64_FORMAT ", we are at %" G_GUINT64_FORMAT
segdone, sample); ", sample %" G_GUINT64_FORMAT, readseg, segdone, sample);
return sample; return sample;
} }
@ -876,20 +880,22 @@ gst_audio_base_src_create (GstBaseSrc * bsrc, guint64 offset, guint length,
GstClockTime base_time; GstClockTime base_time;
GstClockTime current_time; GstClockTime current_time;
guint64 running_time_sample; guint64 running_time_sample;
gint running_time_segment; guint64 running_time_segment;
gint last_read_segment; guint64 last_read_segment;
gint segment_skew; gint64 segment_skew;
gint sps; gint sps;
gint segments_written; guint64 segments_written;
gint last_written_segment; guint64 last_written_segment;
/* get the amount of segments written from the device by now */ /* 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 /* subtract the base to segments_written to get the number of the
* last written segment in the ringbuffer * last written segment in the ringbuffer
* (one segment written = segment 0) */ * (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 */ /* samples per segment */
sps = ringbuffer->samples_per_seg; sps = ringbuffer->samples_per_seg;
@ -923,13 +929,16 @@ gst_audio_base_src_create (GstBaseSrc * bsrc, guint64 offset, guint length,
GST_TIME_FORMAT GST_TIME_FORMAT
"\n timestamp = %" "\n timestamp = %"
GST_TIME_FORMAT GST_TIME_FORMAT
"\n running_time_segment = %d" "\n running_time_segment = %"
"\n last_written_segment = %d" G_GUINT64_FORMAT
"\n segment_skew (running time segment - last_written_segment) = %d" "\n last_written_segment = %"
"\n last_read_segment = %d", G_GUINT64_FORMAT
GST_TIME_ARGS (running_time), GST_TIME_ARGS (timestamp), "\n segment_skew (running time segment - last_written_segment) = %"
running_time_segment, last_written_segment, segment_skew, G_GINT64_FORMAT
last_read_segment); "\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: /* 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) || if ((segment_skew >= ringbuffer->spec.segtotal) ||
(last_read_segment == 0) || first_sample) { (last_read_segment == 0) || first_sample) {
gint new_read_segment; guint64 new_read_segment;
gint segment_diff; guint64 segment_diff;
guint64 new_sample; guint64 new_sample;
/* the difference between running_time and the last written segment */ /* 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); gst_audio_ring_buffer_advance (ringbuffer, segment_diff);
/* we move the new read segment to the last known written segment */ /* we move the new read segment to the last known written segment */
new_read_segment = new_read_segment = gst_audio_ring_buffer_get_segdone (src->ringbuffer)
g_atomic_int_get (&ringbuffer->segdone) - ringbuffer->segbase; - gst_audio_ring_buffer_get_segbase (src->ringbuffer);
/* we calculate the new sample value */ /* 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 */ /* and get the relative time to this -> our new timestamp */
timestamp = gst_util_uint64_scale_int (new_sample, GST_SECOND, rate); 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; src->next_sample = new_sample + samples;
GST_DEBUG_OBJECT (bsrc, 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 ", " "Updating the timestamp to %" GST_TIME_FORMAT ", "
"and src->next_sample to %" G_GUINT64_FORMAT, segment_diff, "and src->next_sample to %" G_GUINT64_FORMAT, segment_diff,
GST_TIME_ARGS (timestamp), src->next_sample); GST_TIME_ARGS (timestamp), src->next_sample);

View file

@ -46,6 +46,124 @@
GST_DEBUG_CATEGORY_STATIC (gst_audio_ring_buffer_debug); GST_DEBUG_CATEGORY_STATIC (gst_audio_ring_buffer_debug);
#define GST_CAT_DEFAULT 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 <stdatomic.h>
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 <windows.h>
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_dispose (GObject * object);
static void gst_audio_ring_buffer_finalize (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); guint8 * data, gint in_samples, gint out_samples, gint * accum);
/* ringbuffer abstract base class */ /* 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); GST_TYPE_OBJECT);
static void static void
@ -80,6 +198,7 @@ gst_audio_ring_buffer_class_init (GstAudioRingBufferClass * klass)
static void static void
gst_audio_ring_buffer_init (GstAudioRingBuffer * ringbuffer) gst_audio_ring_buffer_init (GstAudioRingBuffer * ringbuffer)
{ {
ringbuffer->priv = gst_audio_ring_buffer_get_instance_private (ringbuffer);
ringbuffer->open = FALSE; ringbuffer->open = FALSE;
ringbuffer->acquired = FALSE; ringbuffer->acquired = FALSE;
g_atomic_int_set (&ringbuffer->state, GST_AUDIO_RING_BUFFER_STATE_STOPPED); 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->flushing = TRUE;
ringbuffer->segbase = 0; ringbuffer->segbase = 0;
ringbuffer->segdone = 0; ringbuffer->segdone = 0;
ringbuffer->priv->segbase = 0;
ringbuffer->priv->segdone = 0;
} }
static void static void
@ -813,7 +934,9 @@ gst_audio_ring_buffer_release (GstAudioRingBuffer * buf)
if (G_UNLIKELY (!res)) if (G_UNLIKELY (!res))
goto release_failed; goto release_failed;
gst_atomic_uint64_set (&buf->priv->segdone, 0);
g_atomic_int_set (&buf->segdone, 0); g_atomic_int_set (&buf->segdone, 0);
buf->priv->segbase = 0;
buf->segbase = 0; buf->segbase = 0;
g_free (buf->empty_seg); g_free (buf->empty_seg);
buf->empty_seg = NULL; buf->empty_seg = NULL;
@ -1348,16 +1471,16 @@ not_acquired:
guint64 guint64
gst_audio_ring_buffer_samples_done (GstAudioRingBuffer * buf) gst_audio_ring_buffer_samples_done (GstAudioRingBuffer * buf)
{ {
gint segdone; guint64 segdone;
guint64 samples; guint64 samples;
g_return_val_if_fail (GST_IS_AUDIO_RING_BUFFER (buf), 0); g_return_val_if_fail (GST_IS_AUDIO_RING_BUFFER (buf), 0);
/* get the amount of segments we processed */ /* 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 */ /* convert to samples */
samples = ((guint64) segdone) * buf->samples_per_seg; samples = segdone * buf->samples_per_seg;
return samples; return samples;
} }
@ -1379,6 +1502,8 @@ gst_audio_ring_buffer_samples_done (GstAudioRingBuffer * buf)
void void
gst_audio_ring_buffer_set_sample (GstAudioRingBuffer * buf, guint64 sample) gst_audio_ring_buffer_set_sample (GstAudioRingBuffer * buf, guint64 sample)
{ {
guint64 segdone;
g_return_if_fail (GST_IS_AUDIO_RING_BUFFER (buf)); g_return_if_fail (GST_IS_AUDIO_RING_BUFFER (buf));
if (sample == -1) 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 /* FIXME, we assume the ringbuffer can restart at a random
* position, round down to the beginning and keep track of * position, round down to the beginning and keep track of
* offset when calculating the processed samples. */ * 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_audio_ring_buffer_clear_all (buf);
GST_DEBUG_OBJECT (buf, "set sample to %" G_GUINT64_FORMAT ", segbase %d", GST_DEBUG_OBJECT (buf,
sample, buf->segbase); "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 static gboolean
wait_segment (GstAudioRingBuffer * buf) wait_segment (GstAudioRingBuffer * buf)
{ {
gint segments; guint64 segments;
gboolean wait = TRUE; gboolean wait = TRUE;
/* buffer must be started now or we deadlock since nobody is reading */ /* buffer must be started now or we deadlock since nobody is reading */
@ -1456,12 +1638,12 @@ wait_segment (GstAudioRingBuffer * buf)
goto no_start; goto no_start;
GST_DEBUG_OBJECT (buf, "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); gst_audio_ring_buffer_start (buf);
/* After starting, the writer may have wrote segments already and then we /* After starting, the writer may have wrote segments already and then we
* don't need to wait anymore */ * 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; wait = FALSE;
} }
@ -1625,10 +1807,10 @@ static guint
default_commit (GstAudioRingBuffer * buf, guint64 * sample, default_commit (GstAudioRingBuffer * buf, guint64 * sample,
guint8 * data, gint in_samples, gint out_samples, gint * accum) guint8 * data, gint in_samples, gint out_samples, gint * accum)
{ {
gint segdone; guint64 segdone;
gint segsize, segtotal, channels, bps, bpf, sps; gint segsize, segtotal, channels, bps, bpf, sps;
guint8 *dest, *data_end; guint8 *dest, *data_end;
gint writeseg, sampleoff; guint64 writeseg, sampleoff;
gint *toprocess; gint *toprocess;
gint inr, outr; gint inr, outr;
gboolean reverse; gboolean reverse;
@ -1677,17 +1859,20 @@ default_commit (GstAudioRingBuffer * buf, guint64 * sample,
gboolean skip; gboolean skip;
while (TRUE) { while (TRUE) {
gint diff; gint64 diff;
/* get the currently processed segment */ /* 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 */ /* see how far away it is from the write segment */
diff = writeseg - segdone; diff = writeseg - segdone;
GST_DEBUG_OBJECT (buf, GST_DEBUG_OBJECT (buf,
"pointer at %d, write to %d-%d, diff %d, segtotal %d, segsize %d, base %d", "pointer at %" G_GUINT64_FORMAT ", write to %" G_GUINT64_FORMAT "-%"
segdone, writeseg, sampleoff, diff, segtotal, segsize, buf->segbase); 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 */ /* segment too far ahead, writer too slow, we need to drop, hopefully UNLIKELY */
if (G_UNLIKELY (diff < 0)) { if (G_UNLIKELY (diff < 0)) {
@ -1716,7 +1901,8 @@ default_commit (GstAudioRingBuffer * buf, guint64 * sample,
d_end = d + avail; d_end = d + avail;
*sample += avail / bpf; *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); dest + ws * segsize, ws, sps, sampleoff, avail);
if (need_reorder) { if (need_reorder) {
@ -1859,8 +2045,8 @@ guint
gst_audio_ring_buffer_read (GstAudioRingBuffer * buf, guint64 sample, gst_audio_ring_buffer_read (GstAudioRingBuffer * buf, guint64 sample,
guint8 * data, guint len, GstClockTime * timestamp) guint8 * data, guint len, GstClockTime * timestamp)
{ {
gint segdone; guint64 segdone, readseg = 0;
gint segsize, segtotal, channels, bps, bpf, sps, readseg = 0; gint segsize, segtotal, channels, bps, bpf, sps;
guint8 *dest; guint8 *dest;
guint to_read; guint to_read;
gboolean need_reorder; gboolean need_reorder;
@ -1890,10 +2076,11 @@ gst_audio_ring_buffer_read (GstAudioRingBuffer * buf, guint64 sample,
sampleoff = (sample % sps); sampleoff = (sample % sps);
while (TRUE) { while (TRUE) {
gint diff; gint64 diff;
/* get the currently processed segment */ /* 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 /* see how far away it is from the read segment, normally segdone (where
* the hardware is writing) is bigger than readseg (where software is * 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; diff = segdone - readseg;
GST_DEBUG_OBJECT GST_DEBUG_OBJECT
(buf, "pointer at %d, sample %" G_GUINT64_FORMAT (buf, "pointer at %" G_GUINT64_FORMAT ", sample %" G_GUINT64_FORMAT
", read from %d-%d, to_read %d, diff %d, segtotal %d, segsize %d", ", read from %" G_GUINT64_FORMAT "-%d, to_read %d, diff %"
segdone, sample, readseg, sampleoff, to_read, diff, segtotal, G_GINT64_FORMAT ", segtotal %d, segsize %d", segdone, sample, readseg,
segsize); sampleoff, to_read, diff, segtotal, segsize);
/* segment too far ahead, reader too slow */ /* segment too far ahead, reader too slow */
if (G_UNLIKELY (diff >= segtotal)) { if (G_UNLIKELY (diff >= segtotal)) {
@ -1928,7 +2115,8 @@ gst_audio_ring_buffer_read (GstAudioRingBuffer * buf, guint64 sample,
readseg = readseg % segtotal; readseg = readseg % segtotal;
sampleslen = MIN (sps - sampleoff, to_read); 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); dest + readseg * segsize, readseg, sampleoff, sampleslen);
if (need_reorder) { if (need_reorder) {
@ -1957,7 +2145,8 @@ gst_audio_ring_buffer_read (GstAudioRingBuffer * buf, guint64 sample,
if (buf->timestamps && timestamp) { if (buf->timestamps && timestamp) {
*timestamp = buf->timestamps[readseg % segtotal]; *timestamp = buf->timestamps[readseg % segtotal];
GST_DEBUG_OBJECT (buf, "Retrieved timestamp %" GST_TIME_FORMAT 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; return len - to_read;
@ -1990,7 +2179,7 @@ gst_audio_ring_buffer_prepare_read (GstAudioRingBuffer * buf, gint * segment,
guint8 ** readptr, gint * len) guint8 ** readptr, gint * len)
{ {
guint8 *data; guint8 *data;
gint segdone; guint64 segdone;
g_return_val_if_fail (GST_IS_AUDIO_RING_BUFFER (buf), FALSE); 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; data = buf->memory;
/* get the position of the pointer */ /* 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; *segment = segdone % buf->spec.segtotal;
*len = buf->spec.segsize; *len = buf->spec.segsize;
*readptr = data + *segment * *len; *readptr = data + *segment * *len;
GST_LOG_OBJECT (buf, "prepare read from segment %d (real %d) @%p", GST_LOG_OBJECT (buf,
*segment, segdone, *readptr); "prepare read from segment %d (real %" G_GUINT64_FORMAT ") @%p", *segment,
segdone, *readptr);
/* callback to fill the memory with data, for pull based /* callback to fill the memory with data, for pull based
* scheduling. */ * scheduling. */
@ -2041,6 +2231,7 @@ gst_audio_ring_buffer_advance (GstAudioRingBuffer * buf, guint advance)
g_return_if_fail (GST_IS_AUDIO_RING_BUFFER (buf)); g_return_if_fail (GST_IS_AUDIO_RING_BUFFER (buf));
/* update counter */ /* update counter */
gst_atomic_uint64_add (&buf->priv->segdone, advance);
g_atomic_int_add (&buf->segdone, advance); g_atomic_int_add (&buf->segdone, advance);
/* the lock is already taken when the waiting flag is set, /* the lock is already taken when the waiting flag is set,

View file

@ -42,6 +42,7 @@ G_BEGIN_DECLS
typedef struct _GstAudioRingBuffer GstAudioRingBuffer; typedef struct _GstAudioRingBuffer GstAudioRingBuffer;
typedef struct _GstAudioRingBufferClass GstAudioRingBufferClass; typedef struct _GstAudioRingBufferClass GstAudioRingBufferClass;
typedef struct _GstAudioRingBufferSpec GstAudioRingBufferSpec; typedef struct _GstAudioRingBufferSpec GstAudioRingBufferSpec;
typedef struct _GstAudioRingBufferPrivate GstAudioRingBufferPrivate;
/** /**
* GstAudioRingBufferCallback: * GstAudioRingBufferCallback:
@ -241,7 +242,8 @@ struct _GstAudioRingBuffer {
GDestroyNotify cb_data_notify; GDestroyNotify cb_data_notify;
/*< private >*/ /*< 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 GST_AUDIO_API
void gst_audio_ring_buffer_may_start (GstAudioRingBuffer *buf, gboolean allowed); 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_DEFINE_AUTOPTR_CLEANUP_FUNC(GstAudioRingBuffer, gst_object_unref)
G_END_DECLS G_END_DECLS

View file

@ -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 * less. One concern here is that latency updates happen every 100ms, which
* means segdone is not updated very often, but increasing the update * means segdone is not updated very often, but increasing the update
* frequency would mean more communication overhead. */ * frequency would mean more communication overhead. */
g_atomic_int_set (&ringbuf->segdone, gst_audio_ring_buffer_set_segdone (ringbuf,
(int) gst_util_uint64_scale_ceil (info->read_index, 1, gst_util_uint64_scale_ceil (info->read_index, 1,
ringbuf->spec.segsize)); ringbuf->spec.segsize));
} }