diff -r 9b2c3c7a1a9c -r 567bb019e3e3 gst_plugins_base/gst-libs/gst/audio/gstringbuffer.c --- a/gst_plugins_base/gst-libs/gst/audio/gstringbuffer.c Wed Mar 31 22:03:18 2010 +0300 +++ b/gst_plugins_base/gst-libs/gst/audio/gstringbuffer.c Tue Aug 31 15:30:33 2010 +0300 @@ -43,10 +43,6 @@ #include "gstringbuffer.h" -#ifdef __SYMBIAN32__ -#include -#endif - GST_DEBUG_CATEGORY_STATIC (gst_ring_buffer_debug); #define GST_CAT_DEFAULT gst_ring_buffer_debug @@ -56,6 +52,9 @@ static void gst_ring_buffer_finalize (GObject * object); static gboolean gst_ring_buffer_pause_unlocked (GstRingBuffer * buf); +static void default_clear_all (GstRingBuffer * buf); +static guint default_commit (GstRingBuffer * buf, guint64 * sample, + guchar * data, gint in_samples, gint out_samples, gint * accum); static GstObjectClass *parent_class = NULL; @@ -97,14 +96,19 @@ { GObjectClass *gobject_class; GstObjectClass *gstobject_class; + GstRingBufferClass *gstringbuffer_class; gobject_class = (GObjectClass *) klass; gstobject_class = (GstObjectClass *) klass; + gstringbuffer_class = (GstRingBufferClass *) klass; parent_class = g_type_class_peek_parent (klass); gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_ring_buffer_dispose); gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_ring_buffer_finalize); + + gstringbuffer_class->clear_all = GST_DEBUG_FUNCPTR (default_clear_all); + gstringbuffer_class->commit = GST_DEBUG_FUNCPTR (default_commit); } static void @@ -274,6 +278,7 @@ GST_DEBUG ("acquire ringbuffer: latency time: %" G_GINT64_FORMAT " usec", spec->latency_time); GST_DEBUG ("acquire ringbuffer: total segments: %d", spec->segtotal); + GST_DEBUG ("acquire ringbuffer: latency segments: %d", spec->seglatency); GST_DEBUG ("acquire ringbuffer: segment size: %d bytes = %d samples", spec->segsize, spec->segsize / spec->bytes_per_sample); GST_DEBUG ("acquire ringbuffer: buffer size: %d bytes = %d samples", @@ -434,6 +439,9 @@ spec->segsize -= spec->segsize % spec->bytes_per_sample; spec->segtotal = spec->buffer_time / spec->latency_time; + /* leave the latency undefined now, implementations can change it but if it's + * not changed, we assume the same value as segtotal */ + spec->seglatency = -1; gst_ring_buffer_debug_spec_caps (spec); gst_ring_buffer_debug_spec_buff (spec); @@ -449,6 +457,105 @@ } /** + * gst_ring_buffer_convert: + * @buf: the #GstRingBuffer + * @src_fmt: the source format + * @src_val: the source value + * @dest_fmt: the destination format + * @dest_val: a location to store the converted value + * + * Convert @src_val in @src_fmt to the equivalent value in @dest_fmt. The result + * will be put in @dest_val. + * + * Returns: TRUE if the conversion succeeded. + * + * Since: 0.10.22. + */ +#ifdef __SYMBIAN32__ +EXPORT_C +#endif + +gboolean +gst_ring_buffer_convert (GstRingBuffer * buf, + GstFormat src_fmt, gint64 src_val, GstFormat dest_fmt, gint64 * dest_val) +{ + gboolean res = TRUE; + gint bps, rate; + + GST_DEBUG ("converting value %" G_GINT64_FORMAT " from %s (%d) to %s (%d)", + src_val, gst_format_get_name (src_fmt), src_fmt, + gst_format_get_name (dest_fmt), dest_fmt); + + if (src_fmt == dest_fmt || src_val == -1) { + *dest_val = src_val; + goto done; + } + + /* get important info */ + GST_OBJECT_LOCK (buf); + bps = buf->spec.bytes_per_sample; + rate = buf->spec.rate; + GST_OBJECT_UNLOCK (buf); + + if (bps == 0 || rate == 0) { + GST_DEBUG ("no rate or bps configured"); + res = FALSE; + goto done; + } + + switch (src_fmt) { + case GST_FORMAT_BYTES: + switch (dest_fmt) { + case GST_FORMAT_TIME: + *dest_val = gst_util_uint64_scale_int (src_val / bps, GST_SECOND, + rate); + break; + case GST_FORMAT_DEFAULT: + *dest_val = src_val / bps; + break; + default: + res = FALSE; + break; + } + break; + case GST_FORMAT_DEFAULT: + switch (dest_fmt) { + case GST_FORMAT_TIME: + *dest_val = gst_util_uint64_scale_int (src_val, GST_SECOND, rate); + break; + case GST_FORMAT_BYTES: + *dest_val = src_val * bps; + break; + default: + res = FALSE; + break; + } + break; + case GST_FORMAT_TIME: + switch (dest_fmt) { + case GST_FORMAT_DEFAULT: + *dest_val = gst_util_uint64_scale_int (src_val, rate, GST_SECOND); + break; + case GST_FORMAT_BYTES: + *dest_val = gst_util_uint64_scale_int (src_val, rate, GST_SECOND); + *dest_val *= bps; + break; + default: + res = FALSE; + break; + } + break; + default: + res = FALSE; + break; + } +done: + GST_DEBUG ("ret=%d result %" G_GINT64_FORMAT, res, *dest_val); + + return res; +} + +/** * gst_ring_buffer_set_callback: * @buf: the #GstRingBuffer to set the callback on * @cb: the callback to set @@ -640,7 +747,6 @@ return res; } - /** * gst_ring_buffer_acquire: * @buf: the #GstRingBuffer to acquire @@ -689,6 +795,11 @@ if (G_UNLIKELY ((bps = buf->spec.bytes_per_sample) == 0)) goto invalid_bps; + /* if the seglatency was overwritten with something else than -1, use it, else + * assume segtotal as the latency */ + if (buf->spec.seglatency == -1) + buf->spec.seglatency = buf->spec.segtotal; + segsize = buf->spec.segsize; buf->samples_per_seg = segsize / bps; @@ -840,6 +951,111 @@ } /** + * gst_ring_buffer_activate: + * @buf: the #GstRingBuffer to activate + * @active: the new mode + * + * Activate @buf to start or stop pulling data. + * + * MT safe. + * + * Returns: TRUE if the device could be activated in the requested mode, + * FALSE on error. + * + * Since: 0.10.22. + */ +#ifdef __SYMBIAN32__ +EXPORT_C +#endif + +gboolean +gst_ring_buffer_activate (GstRingBuffer * buf, gboolean active) +{ + gboolean res = FALSE; + GstRingBufferClass *rclass; + + g_return_val_if_fail (GST_IS_RING_BUFFER (buf), FALSE); + + GST_DEBUG_OBJECT (buf, "activate device"); + + GST_OBJECT_LOCK (buf); + if (G_UNLIKELY (active && !buf->acquired)) + goto not_acquired; + + if (G_UNLIKELY (buf->abidata.ABI.active == active)) + goto was_active; + + rclass = GST_RING_BUFFER_GET_CLASS (buf); + /* if there is no activate function we assume it was started/released + * in the acquire method */ + if (G_LIKELY (rclass->activate)) + res = rclass->activate (buf, active); + else + res = TRUE; + + if (G_UNLIKELY (!res)) + goto activate_failed; + + buf->abidata.ABI.active = active; + +done: + GST_OBJECT_UNLOCK (buf); + + return res; + + /* ERRORS */ +not_acquired: + { + GST_DEBUG_OBJECT (buf, "device not acquired"); + g_critical ("Device for %p not acquired", buf); + res = FALSE; + goto done; + } +was_active: + { + res = TRUE; + GST_DEBUG_OBJECT (buf, "device was active in mode %d", active); + goto done; + } +activate_failed: + { + GST_DEBUG_OBJECT (buf, "failed to activate device"); + goto done; + } +} + +/** + * gst_ring_buffer_is_active: + * @buf: the #GstRingBuffer + * + * Check if @buf is activated. + * + * MT safe. + * + * Returns: TRUE if the device is active. + * + * Since: 0.10.22. + */ +#ifdef __SYMBIAN32__ +EXPORT_C +#endif + +gboolean +gst_ring_buffer_is_active (GstRingBuffer * buf) +{ + gboolean res; + + g_return_val_if_fail (GST_IS_RING_BUFFER (buf), FALSE); + + GST_OBJECT_LOCK (buf); + res = buf->abidata.ABI.active; + GST_OBJECT_UNLOCK (buf); + + return res; +} + + +/** * gst_ring_buffer_set_flushing: * @buf: the #GstRingBuffer to flush * @flushing: the new mode @@ -860,9 +1076,10 @@ GST_OBJECT_LOCK (buf); buf->abidata.ABI.flushing = flushing; - gst_ring_buffer_clear_all (buf); if (flushing) { gst_ring_buffer_pause_unlocked (buf); + } else { + gst_ring_buffer_clear_all (buf); } GST_OBJECT_UNLOCK (buf); } @@ -904,13 +1121,14 @@ GST_RING_BUFFER_STATE_STOPPED, GST_RING_BUFFER_STATE_STARTED); if (!res) { + GST_DEBUG_OBJECT (buf, "was not stopped, try paused"); /* was not stopped, try from paused */ res = g_atomic_int_compare_and_exchange (&buf->state, GST_RING_BUFFER_STATE_PAUSED, GST_RING_BUFFER_STATE_STARTED); if (!res) { /* was not paused either, must be started then */ res = TRUE; - GST_DEBUG_OBJECT (buf, "was started"); + GST_DEBUG_OBJECT (buf, "was not paused, must have been started"); goto done; } resume = TRUE; @@ -1071,10 +1289,16 @@ GST_RING_BUFFER_STATE_STARTED, GST_RING_BUFFER_STATE_STOPPED); if (!res) { - /* was not started, must be stopped then */ - GST_DEBUG_OBJECT (buf, "was not started"); - res = TRUE; - goto done; + GST_DEBUG_OBJECT (buf, "was not started, try paused"); + /* was not started, try from paused */ + res = g_atomic_int_compare_and_exchange (&buf->state, + GST_RING_BUFFER_STATE_PAUSED, GST_RING_BUFFER_STATE_STOPPED); + if (!res) { + /* was not paused either, must have been stopped then */ + res = TRUE; + GST_DEBUG_OBJECT (buf, "was not paused, must have been stopped"); + goto done; + } } /* signal any waiters */ @@ -1220,6 +1444,22 @@ buf->segbase); } +static void +default_clear_all (GstRingBuffer * buf) +{ + gint i; + + /* not fatal, we just are not negotiated yet */ + if (G_UNLIKELY (buf->spec.segtotal <= 0)) + return; + + GST_DEBUG_OBJECT (buf, "clear all segments"); + + for (i = 0; i < buf->spec.segtotal; i++) { + gst_ring_buffer_clear (buf, i); + } +} + /** * gst_ring_buffer_clear_all: * @buf: the #GstRingBuffer to clear @@ -1235,19 +1475,14 @@ void gst_ring_buffer_clear_all (GstRingBuffer * buf) { - gint i; + GstRingBufferClass *rclass; g_return_if_fail (GST_IS_RING_BUFFER (buf)); - /* not fatal, we just are not negotiated yet */ - if (G_UNLIKELY (buf->spec.segtotal <= 0)) - return; + rclass = GST_RING_BUFFER_GET_CLASS (buf); - GST_DEBUG_OBJECT (buf, "clear all segments"); - - for (i = 0; i < buf->spec.segtotal; i++) { - gst_ring_buffer_clear (buf, i); - } + if (G_LIKELY (rclass->clear_all)) + rclass->clear_all (buf); } @@ -1370,7 +1605,7 @@ memcpy (d, se, bps); \ se -= bps; \ *accum += outr; \ - while ((*accum << 1) >= inr) { \ + while (d < de && (*accum << 1) >= inr) { \ *accum -= inr; \ d += bps; \ } \ @@ -1388,7 +1623,7 @@ memcpy (d, se, bps); \ d += bps; \ *accum += inr; \ - while ((*accum << 1) >= outr) { \ + while (s <= se && (*accum << 1) >= outr) { \ *accum -= outr; \ se -= bps; \ } \ @@ -1398,47 +1633,8 @@ GST_DEBUG ("rev_down end %d/%d",*accum,*toprocess); \ } G_STMT_END -/** - * gst_ring_buffer_commit_full: - * @buf: the #GstRingBuffer to commit - * @sample: the sample position of the data - * @data: the data to commit - * @in_samples: the number of samples in the data to commit - * @out_samples: the number of samples to write to the ringbuffer - * @accum: accumulator for rate conversion. - * - * Commit @in_samples samples pointed to by @data to the ringbuffer @buf. - * - * @in_samples and @out_samples define the rate conversion to perform on the the - * samples in @data. For negative rates, @out_samples must be negative and - * @in_samples positive. - * - * When @out_samples is positive, the first sample will be written at position @sample - * in the ringbuffer. When @out_samples is negative, the last sample will be written to - * @sample in reverse order. - * - * @out_samples does not need to be a multiple of the segment size of the ringbuffer - * although it is recommended for optimal performance. - * - * @accum will hold a temporary accumulator used in rate conversion and should be - * set to 0 when this function is first called. In case the commit operation is - * interrupted, one can resume the processing by passing the previously returned - * @accum value back to this function. - * - * Returns: The number of samples written to the ringbuffer or -1 on error. The - * number of samples written can be less than @out_samples when @buf was interrupted - * with a flush or stop. - * - * Since: 0.10.11. - * - * MT safe. - */ -#ifdef __SYMBIAN32__ -EXPORT_C -#endif - -guint -gst_ring_buffer_commit_full (GstRingBuffer * buf, guint64 * sample, +static guint +default_commit (GstRingBuffer * buf, guint64 * sample, guchar * data, gint in_samples, gint out_samples, gint * accum) { gint segdone; @@ -1449,10 +1645,6 @@ gint inr, outr; gboolean reverse; - if (G_UNLIKELY (in_samples == 0 || out_samples == 0)) - return in_samples; - - g_return_val_if_fail (GST_IS_RING_BUFFER (buf), -1); g_return_val_if_fail (buf->data != NULL, -1); g_return_val_if_fail (data != NULL, -1); @@ -1571,6 +1763,65 @@ } /** + * gst_ring_buffer_commit_full: + * @buf: the #GstRingBuffer to commit + * @sample: the sample position of the data + * @data: the data to commit + * @in_samples: the number of samples in the data to commit + * @out_samples: the number of samples to write to the ringbuffer + * @accum: accumulator for rate conversion. + * + * Commit @in_samples samples pointed to by @data to the ringbuffer @buf. + * + * @in_samples and @out_samples define the rate conversion to perform on the the + * samples in @data. For negative rates, @out_samples must be negative and + * @in_samples positive. + * + * When @out_samples is positive, the first sample will be written at position @sample + * in the ringbuffer. When @out_samples is negative, the last sample will be written to + * @sample in reverse order. + * + * @out_samples does not need to be a multiple of the segment size of the ringbuffer + * although it is recommended for optimal performance. + * + * @accum will hold a temporary accumulator used in rate conversion and should be + * set to 0 when this function is first called. In case the commit operation is + * interrupted, one can resume the processing by passing the previously returned + * @accum value back to this function. + * + * MT safe. + * + * Returns: The number of samples written to the ringbuffer or -1 on error. The + * number of samples written can be less than @out_samples when @buf was interrupted + * with a flush or stop. + * + * Since: 0.10.11. + */ +#ifdef __SYMBIAN32__ +EXPORT_C +#endif + +guint +gst_ring_buffer_commit_full (GstRingBuffer * buf, guint64 * sample, + guchar * data, gint in_samples, gint out_samples, gint * accum) +{ + GstRingBufferClass *rclass; + guint res = -1; + + g_return_val_if_fail (GST_IS_RING_BUFFER (buf), -1); + + if (G_UNLIKELY (in_samples == 0 || out_samples == 0)) + return in_samples; + + rclass = GST_RING_BUFFER_GET_CLASS (buf); + + if (G_LIKELY (rclass->commit)) + res = rclass->commit (buf, sample, data, in_samples, out_samples, accum); + + return res; +} + +/** * gst_ring_buffer_commit: * @buf: the #GstRingBuffer to commit * @sample: the sample position of the data @@ -1742,10 +1993,6 @@ g_return_val_if_fail (GST_IS_RING_BUFFER (buf), FALSE); - /* buffer must be started */ - if (g_atomic_int_get (&buf->state) != GST_RING_BUFFER_STATE_STARTED) - return FALSE; - g_return_val_if_fail (buf->data != NULL, FALSE); g_return_val_if_fail (segment != NULL, FALSE); g_return_val_if_fail (readptr != NULL, FALSE); @@ -1753,6 +2000,12 @@ data = GST_BUFFER_DATA (buf->data); + if (buf->callback == NULL) { + /* push mode, fail when nothing is started */ + if (g_atomic_int_get (&buf->state) != GST_RING_BUFFER_STATE_STARTED) + return FALSE; + } + /* get the position of the pointer */ segdone = g_atomic_int_get (&buf->segdone); @@ -1760,14 +2013,14 @@ *len = buf->spec.segsize; *readptr = data + *segment * *len; + GST_LOG ("prepare read from segment %d (real %d) @%p", + *segment, segdone, *readptr); + /* callback to fill the memory with data, for pull based * scheduling. */ if (buf->callback) buf->callback (buf, *readptr, *len, buf->cb_data); - GST_LOG ("prepare read from segment %d (real %d) @%p", - *segment, segdone, *readptr); - return TRUE; } @@ -1851,9 +2104,9 @@ * Tell the ringbuffer that it is allowed to start playback when * the ringbuffer is filled with samples. * - * Since: 0.10.6 + * MT safe. * - * MT safe. + * Since: 0.10.6 */ #ifdef __SYMBIAN32__ EXPORT_C @@ -1865,5 +2118,5 @@ g_return_if_fail (GST_IS_RING_BUFFER (buf)); GST_LOG_OBJECT (buf, "may start: %d", allowed); - gst_atomic_int_set (&buf->abidata.ABI.may_start, allowed); + g_atomic_int_set (&buf->abidata.ABI.may_start, allowed); }