mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-21 07:46:38 +00:00
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:
parent
f8246327ef
commit
8ea355e52c
6 changed files with 339 additions and 70 deletions
|
@ -7893,6 +7893,38 @@ MT safe.</doc>
|
|||
</instance-parameter>
|
||||
</parameters>
|
||||
</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">
|
||||
<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"/>
|
||||
|
@ -8234,6 +8266,25 @@ MT safe.</doc>
|
|||
</parameter>
|
||||
</parameters>
|
||||
</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">
|
||||
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h"/>
|
||||
<return-value transfer-ownership="none">
|
||||
|
@ -8363,8 +8414,11 @@ MT safe.</doc>
|
|||
<field name="cb_data_notify" readable="0" private="1">
|
||||
<type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
|
||||
</field>
|
||||
<field name="priv" readable="0" private="1">
|
||||
<type name="AudioRingBufferPrivate" c:type="GstAudioRingBufferPrivate*"/>
|
||||
</field>
|
||||
<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"/>
|
||||
</array>
|
||||
</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>
|
||||
</member>
|
||||
</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">
|
||||
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.h">The structure containing the format specification of the ringbuffer.
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 <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_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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue