diff options
Diffstat (limited to 'gst-libs/gst/audio/gstringbuffer.c')
-rw-r--r-- | gst-libs/gst/audio/gstringbuffer.c | 2077 |
1 files changed, 0 insertions, 2077 deletions
diff --git a/gst-libs/gst/audio/gstringbuffer.c b/gst-libs/gst/audio/gstringbuffer.c deleted file mode 100644 index de74bcd9..00000000 --- a/gst-libs/gst/audio/gstringbuffer.c +++ /dev/null @@ -1,2077 +0,0 @@ -/* GStreamer - * Copyright (C) 2005 Wim Taymans <wim@fluendo.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/** - * SECTION:gstringbuffer - * @short_description: Base class for audio ringbuffer implementations - * @see_also: #GstBaseAudioSink, #GstAudioSink - * - * <refsect2> - * <para> - * This object is the base class for audio ringbuffers used by the base - * audio source and sink classes. - * </para> - * <para> - * The ringbuffer abstracts a circular buffer of data. One reader and - * one writer can operate on the data from different threads in a lockfree - * manner. The base class is sufficiently flexible to be used as an - * abstraction for DMA based ringbuffers as well as a pure software - * implementations. - * </para> - * </refsect2> - * - * Last reviewed on 2006-02-02 (0.10.4) - */ - -#include <string.h> - -#include "gstringbuffer.h" - -GST_DEBUG_CATEGORY_STATIC (gst_ring_buffer_debug); -#define GST_CAT_DEFAULT gst_ring_buffer_debug - -static void gst_ring_buffer_class_init (GstRingBufferClass * klass); -static void gst_ring_buffer_init (GstRingBuffer * ringbuffer); -static void gst_ring_buffer_dispose (GObject * object); -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; - -/* ringbuffer abstract base class */ -GType -gst_ring_buffer_get_type (void) -{ - static GType ringbuffer_type = 0; - - if (G_UNLIKELY (!ringbuffer_type)) { - static const GTypeInfo ringbuffer_info = { - sizeof (GstRingBufferClass), - NULL, - NULL, - (GClassInitFunc) gst_ring_buffer_class_init, - NULL, - NULL, - sizeof (GstRingBuffer), - 0, - (GInstanceInitFunc) gst_ring_buffer_init, - NULL - }; - - ringbuffer_type = g_type_register_static (GST_TYPE_OBJECT, "GstRingBuffer", - &ringbuffer_info, G_TYPE_FLAG_ABSTRACT); - - GST_DEBUG_CATEGORY_INIT (gst_ring_buffer_debug, "ringbuffer", 0, - "ringbuffer class"); - } - return ringbuffer_type; -} - -static void -gst_ring_buffer_class_init (GstRingBufferClass * klass) -{ - GObjectClass *gobject_class; - GstRingBufferClass *gstringbuffer_class; - - gobject_class = (GObjectClass *) klass; - gstringbuffer_class = (GstRingBufferClass *) klass; - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->dispose = gst_ring_buffer_dispose; - gobject_class->finalize = gst_ring_buffer_finalize; - - gstringbuffer_class->clear_all = GST_DEBUG_FUNCPTR (default_clear_all); - gstringbuffer_class->commit = GST_DEBUG_FUNCPTR (default_commit); -} - -static void -gst_ring_buffer_init (GstRingBuffer * ringbuffer) -{ - ringbuffer->open = FALSE; - ringbuffer->acquired = FALSE; - ringbuffer->state = GST_RING_BUFFER_STATE_STOPPED; - ringbuffer->cond = g_cond_new (); - ringbuffer->waiting = 0; - ringbuffer->empty_seg = NULL; - ringbuffer->abidata.ABI.flushing = TRUE; -} - -static void -gst_ring_buffer_dispose (GObject * object) -{ - GstRingBuffer *ringbuffer = GST_RING_BUFFER (object); - - gst_caps_replace (&ringbuffer->spec.caps, NULL); - - G_OBJECT_CLASS (parent_class)->dispose (G_OBJECT (ringbuffer)); -} - -static void -gst_ring_buffer_finalize (GObject * object) -{ - GstRingBuffer *ringbuffer = GST_RING_BUFFER (object); - - g_cond_free (ringbuffer->cond); - g_free (ringbuffer->empty_seg); - - G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT (ringbuffer)); -} - -typedef struct -{ - const GstBufferFormat format; - const guint8 silence[4]; -} FormatDef; - -static const FormatDef linear_defs[4 * 2 * 2] = { - {GST_S8, {0x00, 0x00, 0x00, 0x00}}, - {GST_S8, {0x00, 0x00, 0x00, 0x00}}, - {GST_U8, {0x80, 0x80, 0x80, 0x80}}, - {GST_U8, {0x80, 0x80, 0x80, 0x80}}, - {GST_S16_LE, {0x00, 0x00, 0x00, 0x00}}, - {GST_S16_BE, {0x00, 0x00, 0x00, 0x00}}, - {GST_U16_LE, {0x00, 0x80, 0x00, 0x80}}, - {GST_U16_BE, {0x80, 0x00, 0x80, 0x00}}, - {GST_S24_LE, {0x00, 0x00, 0x00, 0x00}}, - {GST_S24_BE, {0x00, 0x00, 0x00, 0x00}}, - {GST_U24_LE, {0x00, 0x00, 0x80, 0x00}}, - {GST_U24_BE, {0x80, 0x00, 0x00, 0x00}}, - {GST_S32_LE, {0x00, 0x00, 0x00, 0x00}}, - {GST_S32_BE, {0x00, 0x00, 0x00, 0x00}}, - {GST_U32_LE, {0x00, 0x00, 0x00, 0x80}}, - {GST_U32_BE, {0x80, 0x00, 0x00, 0x00}} -}; - -static const FormatDef linear24_defs[3 * 2 * 2] = { - {GST_S24_3LE, {0x00, 0x00, 0x00, 0x00}}, - {GST_S24_3BE, {0x00, 0x00, 0x00, 0x00}}, - {GST_U24_3LE, {0x00, 0x00, 0x80, 0x00}}, - {GST_U24_3BE, {0x80, 0x00, 0x00, 0x00}}, - {GST_S20_3LE, {0x00, 0x00, 0x00, 0x00}}, - {GST_S20_3BE, {0x00, 0x00, 0x00, 0x00}}, - {GST_U20_3LE, {0x00, 0x00, 0x08, 0x00}}, - {GST_U20_3BE, {0x08, 0x00, 0x00, 0x00}}, - {GST_S18_3LE, {0x00, 0x00, 0x00, 0x00}}, - {GST_S18_3BE, {0x00, 0x00, 0x00, 0x00}}, - {GST_U18_3LE, {0x00, 0x00, 0x02, 0x00}}, - {GST_U18_3BE, {0x02, 0x00, 0x00, 0x00}} -}; - -static const FormatDef * -build_linear_format (int depth, int width, int unsignd, int big_endian) -{ - const FormatDef *formats; - - if (width == 24) { - switch (depth) { - case 24: - formats = &linear24_defs[0]; - break; - case 20: - formats = &linear24_defs[4]; - break; - case 18: - formats = &linear24_defs[8]; - break; - default: - return NULL; - } - } else { - switch (depth) { - case 8: - formats = &linear_defs[0]; - break; - case 16: - formats = &linear_defs[4]; - break; - case 24: - formats = &linear_defs[8]; - break; - case 32: - formats = &linear_defs[12]; - break; - default: - return NULL; - } - } - if (unsignd) - formats += 2; - if (big_endian) - formats += 1; - - return formats; -} - -#ifndef GST_DISABLE_GST_DEBUG -static const gchar *format_type_names[] = { - "linear", - "float", - "mu law", - "a law", - "ima adpcm", - "mpeg", - "gsm", - "iec958", - "ac3", - "eac3", - "dts" -}; - -static const gchar *format_names[] = { - "unknown", - "s8", - "u8", - "s16_le", - "s16_be", - "u16_le", - "u16_be", - "s24_le", - "s24_be", - "u24_le", - "u24_be", - "s32_le", - "s32_be", - "u32_le", - "u32_be", - "s24_3le", - "s24_3be", - "u24_3le", - "u24_3be", - "s20_3le", - "s20_3be", - "u20_3le", - "u20_3be", - "s18_3le", - "s18_3be", - "u18_3le", - "u18_3be", - "float32_le", - "float32_be", - "float64_le", - "float64_be", - "mu_law", - "a_law", - "ima_adpcm", - "mpeg", - "gsm", - "iec958", - "ac3", - "eac3", - "dts" -}; -#endif - -/** - * gst_ring_buffer_debug_spec_caps: - * @spec: the spec to debug - * - * Print debug info about the parsed caps in @spec to the debug log. - */ -void -gst_ring_buffer_debug_spec_caps (GstRingBufferSpec * spec) -{ - gint i, bytes; - - GST_DEBUG ("spec caps: %p %" GST_PTR_FORMAT, spec->caps, spec->caps); - GST_DEBUG ("parsed caps: type: %d, '%s'", spec->type, - format_type_names[spec->type]); - GST_DEBUG ("parsed caps: format: %d, '%s'", spec->format, - format_names[spec->format]); - GST_DEBUG ("parsed caps: width: %d", spec->width); - GST_DEBUG ("parsed caps: depth: %d", spec->depth); - GST_DEBUG ("parsed caps: sign: %d", spec->sign); - GST_DEBUG ("parsed caps: bigend: %d", spec->bigend); - GST_DEBUG ("parsed caps: rate: %d", spec->rate); - GST_DEBUG ("parsed caps: channels: %d", spec->channels); - GST_DEBUG ("parsed caps: sample bytes: %d", spec->bytes_per_sample); - bytes = (spec->width >> 3) * spec->channels; - for (i = 0; i < bytes; i++) { - GST_DEBUG ("silence byte %d: %02x", i, spec->silence_sample[i]); - } -} - -/** - * gst_ring_buffer_debug_spec_buff: - * @spec: the spec to debug - * - * Print debug info about the buffer sized in @spec to the debug log. - */ -void -gst_ring_buffer_debug_spec_buff (GstRingBufferSpec * spec) -{ - GST_DEBUG ("acquire ringbuffer: buffer time: %" G_GINT64_FORMAT " usec", - spec->buffer_time); - 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", - spec->segsize * spec->segtotal, - spec->segsize * spec->segtotal / spec->bytes_per_sample); -} - -/** - * gst_ring_buffer_parse_caps: - * @spec: a spec - * @caps: a #GstCaps - * - * Parse @caps into @spec. - * - * Returns: TRUE if the caps could be parsed. - */ -gboolean -gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps) -{ - const gchar *mimetype; - GstStructure *structure; - gint i; - - structure = gst_caps_get_structure (caps, 0); - - /* we have to differentiate between int and float formats */ - mimetype = gst_structure_get_name (structure); - - if (!strncmp (mimetype, "audio/x-raw-int", 15)) { - gint endianness; - const FormatDef *def; - gint j, bytes; - - spec->type = GST_BUFTYPE_LINEAR; - - /* extract the needed information from the cap */ - if (!(gst_structure_get_int (structure, "rate", &spec->rate) && - gst_structure_get_int (structure, "channels", &spec->channels) && - gst_structure_get_int (structure, "width", &spec->width) && - gst_structure_get_int (structure, "depth", &spec->depth) && - gst_structure_get_boolean (structure, "signed", &spec->sign))) - goto parse_error; - - /* extract endianness if needed */ - if (spec->width > 8) { - if (!gst_structure_get_int (structure, "endianness", &endianness)) - goto parse_error; - } else { - endianness = G_BYTE_ORDER; - } - - spec->bigend = endianness == G_LITTLE_ENDIAN ? FALSE : TRUE; - - def = build_linear_format (spec->depth, spec->width, spec->sign ? 0 : 1, - spec->bigend ? 1 : 0); - - if (def == NULL) - goto parse_error; - - spec->format = def->format; - - bytes = spec->width >> 3; - - for (i = 0; i < spec->channels; i++) { - for (j = 0; j < bytes; j++) { - spec->silence_sample[i * bytes + j] = def->silence[j]; - } - } - } else if (!strncmp (mimetype, "audio/x-raw-float", 17)) { - - spec->type = GST_BUFTYPE_FLOAT; - - /* extract the needed information from the cap */ - if (!(gst_structure_get_int (structure, "rate", &spec->rate) && - gst_structure_get_int (structure, "channels", &spec->channels) && - gst_structure_get_int (structure, "width", &spec->width))) - goto parse_error; - - /* match layout to format wrt to endianness */ - switch (spec->width) { - case 32: - spec->format = - G_BYTE_ORDER == G_LITTLE_ENDIAN ? GST_FLOAT32_LE : GST_FLOAT32_BE; - break; - case 64: - spec->format = - G_BYTE_ORDER == G_LITTLE_ENDIAN ? GST_FLOAT64_LE : GST_FLOAT64_BE; - break; - default: - goto parse_error; - } - /* float silence is all zeros.. */ - memset (spec->silence_sample, 0, 32); - } else if (!strncmp (mimetype, "audio/x-alaw", 12)) { - /* extract the needed information from the cap */ - if (!(gst_structure_get_int (structure, "rate", &spec->rate) && - gst_structure_get_int (structure, "channels", &spec->channels))) - goto parse_error; - - spec->type = GST_BUFTYPE_A_LAW; - spec->format = GST_A_LAW; - spec->width = 8; - spec->depth = 8; - for (i = 0; i < spec->channels; i++) - spec->silence_sample[i] = 0xd5; - } else if (!strncmp (mimetype, "audio/x-mulaw", 13)) { - /* extract the needed information from the cap */ - if (!(gst_structure_get_int (structure, "rate", &spec->rate) && - gst_structure_get_int (structure, "channels", &spec->channels))) - goto parse_error; - - spec->type = GST_BUFTYPE_MU_LAW; - spec->format = GST_MU_LAW; - spec->width = 8; - spec->depth = 8; - for (i = 0; i < spec->channels; i++) - spec->silence_sample[i] = 0xff; - } else if (!strncmp (mimetype, "audio/x-iec958", 14)) { - /* extract the needed information from the cap */ - if (!(gst_structure_get_int (structure, "rate", &spec->rate))) - goto parse_error; - - spec->type = GST_BUFTYPE_IEC958; - spec->format = GST_IEC958; - spec->width = 16; - spec->depth = 16; - spec->channels = 2; - } else if (!strncmp (mimetype, "audio/x-ac3", 11)) { - /* extract the needed information from the cap */ - if (!(gst_structure_get_int (structure, "rate", &spec->rate))) - goto parse_error; - - spec->type = GST_BUFTYPE_AC3; - spec->format = GST_AC3; - spec->width = 16; - spec->depth = 16; - spec->channels = 2; - } else { - goto parse_error; - } - - spec->bytes_per_sample = (spec->width >> 3) * spec->channels; - - gst_caps_replace (&spec->caps, caps); - - g_return_val_if_fail (spec->latency_time != 0, FALSE); - - /* calculate suggested segsize and segtotal. segsize should be one unit - * of 'latency_time' samples, scaling for the fact that latency_time is - * currently stored in microseconds (FIXME: in 0.11) */ - spec->segsize = gst_util_uint64_scale (spec->rate * spec->bytes_per_sample, - spec->latency_time, GST_SECOND / GST_USECOND); - /* Round to an integer number of samples */ - 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); - - return TRUE; - - /* ERRORS */ -parse_error: - { - GST_DEBUG ("could not parse caps"); - return FALSE; - } -} - -/** - * 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. - */ -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 - * @user_data: user data passed to the callback - * - * Sets the given callback function on the buffer. This function - * will be called every time a segment has been written to a device. - * - * MT safe. - */ -void -gst_ring_buffer_set_callback (GstRingBuffer * buf, GstRingBufferCallback cb, - gpointer user_data) -{ - g_return_if_fail (GST_IS_RING_BUFFER (buf)); - - GST_OBJECT_LOCK (buf); - buf->callback = cb; - buf->cb_data = user_data; - GST_OBJECT_UNLOCK (buf); -} - - -/** - * gst_ring_buffer_open_device: - * @buf: the #GstRingBuffer - * - * Open the audio device associated with the ring buffer. Does not perform any - * setup on the device. You must open the device before acquiring the ring - * buffer. - * - * Returns: TRUE if the device could be opened, FALSE on error. - * - * MT safe. - */ -gboolean -gst_ring_buffer_open_device (GstRingBuffer * buf) -{ - gboolean res = TRUE; - GstRingBufferClass *rclass; - - g_return_val_if_fail (GST_IS_RING_BUFFER (buf), FALSE); - - GST_DEBUG_OBJECT (buf, "opening device"); - - GST_OBJECT_LOCK (buf); - if (G_UNLIKELY (buf->open)) - goto was_opened; - - buf->open = TRUE; - - /* if this fails, something is wrong in this file */ - g_assert (!buf->acquired); - - rclass = GST_RING_BUFFER_GET_CLASS (buf); - if (G_LIKELY (rclass->open_device)) - res = rclass->open_device (buf); - - if (G_UNLIKELY (!res)) - goto open_failed; - - GST_DEBUG_OBJECT (buf, "opened device"); - -done: - GST_OBJECT_UNLOCK (buf); - - return res; - - /* ERRORS */ -was_opened: - { - GST_DEBUG_OBJECT (buf, "Device for ring buffer already open"); - g_warning ("Device for ring buffer %p already open, fix your code", buf); - res = TRUE; - goto done; - } -open_failed: - { - buf->open = FALSE; - GST_DEBUG_OBJECT (buf, "failed opening device"); - goto done; - } -} - -/** - * gst_ring_buffer_close_device: - * @buf: the #GstRingBuffer - * - * Close the audio device associated with the ring buffer. The ring buffer - * should already have been released via gst_ring_buffer_release(). - * - * Returns: TRUE if the device could be closed, FALSE on error. - * - * MT safe. - */ -gboolean -gst_ring_buffer_close_device (GstRingBuffer * buf) -{ - gboolean res = TRUE; - GstRingBufferClass *rclass; - - g_return_val_if_fail (GST_IS_RING_BUFFER (buf), FALSE); - - GST_DEBUG_OBJECT (buf, "closing device"); - - GST_OBJECT_LOCK (buf); - if (G_UNLIKELY (!buf->open)) - goto was_closed; - - if (G_UNLIKELY (buf->acquired)) - goto was_acquired; - - buf->open = FALSE; - - rclass = GST_RING_BUFFER_GET_CLASS (buf); - if (G_LIKELY (rclass->close_device)) - res = rclass->close_device (buf); - - if (G_UNLIKELY (!res)) - goto close_error; - - GST_DEBUG_OBJECT (buf, "closed device"); - -done: - GST_OBJECT_UNLOCK (buf); - - return res; - - /* ERRORS */ -was_closed: - { - GST_DEBUG_OBJECT (buf, "Device for ring buffer already closed"); - g_warning ("Device for ring buffer %p already closed, fix your code", buf); - res = TRUE; - goto done; - } -was_acquired: - { - GST_DEBUG_OBJECT (buf, "Resources for ring buffer still acquired"); - g_critical ("Resources for ring buffer %p still acquired", buf); - res = FALSE; - goto done; - } -close_error: - { - buf->open = TRUE; - GST_DEBUG_OBJECT (buf, "error closing device"); - goto done; - } -} - -/** - * gst_ring_buffer_device_is_open: - * @buf: the #GstRingBuffer - * - * Checks the status of the device associated with the ring buffer. - * - * Returns: TRUE if the device was open, FALSE if it was closed. - * - * MT safe. - */ -gboolean -gst_ring_buffer_device_is_open (GstRingBuffer * buf) -{ - gboolean res = TRUE; - - g_return_val_if_fail (GST_IS_RING_BUFFER (buf), FALSE); - - GST_OBJECT_LOCK (buf); - res = buf->open; - GST_OBJECT_UNLOCK (buf); - - return res; -} - -/** - * gst_ring_buffer_acquire: - * @buf: the #GstRingBuffer to acquire - * @spec: the specs of the buffer - * - * Allocate the resources for the ringbuffer. This function fills - * in the data pointer of the ring buffer with a valid #GstBuffer - * to which samples can be written. - * - * Returns: TRUE if the device could be acquired, FALSE on error. - * - * MT safe. - */ -gboolean -gst_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) -{ - gboolean res = FALSE; - GstRingBufferClass *rclass; - gint i, j; - gint segsize, bps; - - g_return_val_if_fail (GST_IS_RING_BUFFER (buf), FALSE); - - GST_DEBUG_OBJECT (buf, "acquiring device"); - - GST_OBJECT_LOCK (buf); - if (G_UNLIKELY (!buf->open)) - goto not_opened; - - if (G_UNLIKELY (buf->acquired)) - goto was_acquired; - - buf->acquired = TRUE; - - rclass = GST_RING_BUFFER_GET_CLASS (buf); - if (G_LIKELY (rclass->acquire)) - res = rclass->acquire (buf, spec); - - if (G_UNLIKELY (!res)) - goto acquire_failed; - - 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; - - /* create an empty segment */ - g_free (buf->empty_seg); - buf->empty_seg = g_malloc (segsize); - - /* FIXME, we only have 32 silence samples, which might not be enough to - * represent silence in all channels */ - bps = MIN (bps, 32); - for (i = 0, j = 0; i < segsize; i++) { - buf->empty_seg[i] = buf->spec.silence_sample[j]; - j = (j + 1) % bps; - } - GST_DEBUG_OBJECT (buf, "acquired device"); - -done: - GST_OBJECT_UNLOCK (buf); - - return res; - - /* ERRORS */ -not_opened: - { - GST_DEBUG_OBJECT (buf, "device not opened"); - g_critical ("Device for %p not opened", buf); - res = FALSE; - goto done; - } -was_acquired: - { - res = TRUE; - GST_DEBUG_OBJECT (buf, "device was acquired"); - goto done; - } -acquire_failed: - { - buf->acquired = FALSE; - GST_DEBUG_OBJECT (buf, "failed to acquire device"); - goto done; - } -invalid_bps: - { - g_warning - ("invalid bytes_per_sample from acquire ringbuffer, fix the element"); - buf->acquired = FALSE; - res = FALSE; - goto done; - } -} - -/** - * gst_ring_buffer_release: - * @buf: the #GstRingBuffer to release - * - * Free the resources of the ringbuffer. - * - * Returns: TRUE if the device could be released, FALSE on error. - * - * MT safe. - */ -gboolean -gst_ring_buffer_release (GstRingBuffer * buf) -{ - gboolean res = FALSE; - GstRingBufferClass *rclass; - - g_return_val_if_fail (GST_IS_RING_BUFFER (buf), FALSE); - - GST_DEBUG_OBJECT (buf, "releasing device"); - - gst_ring_buffer_stop (buf); - - GST_OBJECT_LOCK (buf); - if (G_UNLIKELY (!buf->acquired)) - goto was_released; - - buf->acquired = FALSE; - - /* if this fails, something is wrong in this file */ - g_assert (buf->open == TRUE); - - rclass = GST_RING_BUFFER_GET_CLASS (buf); - if (G_LIKELY (rclass->release)) - res = rclass->release (buf); - - /* signal any waiters */ - GST_DEBUG_OBJECT (buf, "signal waiter"); - GST_RING_BUFFER_SIGNAL (buf); - - if (G_UNLIKELY (!res)) - goto release_failed; - - g_free (buf->empty_seg); - buf->empty_seg = NULL; - GST_DEBUG_OBJECT (buf, "released device"); - -done: - GST_OBJECT_UNLOCK (buf); - - return res; - - /* ERRORS */ -was_released: - { - res = TRUE; - GST_DEBUG_OBJECT (buf, "device was released"); - goto done; - } -release_failed: - { - buf->acquired = TRUE; - GST_DEBUG_OBJECT (buf, "failed to release device"); - goto done; - } -} - -/** - * gst_ring_buffer_is_acquired: - * @buf: the #GstRingBuffer to check - * - * Check if the ringbuffer is acquired and ready to use. - * - * Returns: TRUE if the ringbuffer is acquired, FALSE on error. - * - * MT safe. - */ -gboolean -gst_ring_buffer_is_acquired (GstRingBuffer * buf) -{ - gboolean res; - - g_return_val_if_fail (GST_IS_RING_BUFFER (buf), FALSE); - - GST_OBJECT_LOCK (buf); - res = buf->acquired; - GST_OBJECT_UNLOCK (buf); - - return res; -} - -/** - * 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. - */ -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. - */ -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 - * - * Set the ringbuffer to flushing mode or normal mode. - * - * MT safe. - */ -void -gst_ring_buffer_set_flushing (GstRingBuffer * buf, gboolean flushing) -{ - g_return_if_fail (GST_IS_RING_BUFFER (buf)); - - GST_OBJECT_LOCK (buf); - buf->abidata.ABI.flushing = flushing; - - if (flushing) { - gst_ring_buffer_pause_unlocked (buf); - } else { - gst_ring_buffer_clear_all (buf); - } - GST_OBJECT_UNLOCK (buf); -} - -/** - * gst_ring_buffer_start: - * @buf: the #GstRingBuffer to start - * - * Start processing samples from the ringbuffer. - * - * Returns: TRUE if the device could be started, FALSE on error. - * - * MT safe. - */ -gboolean -gst_ring_buffer_start (GstRingBuffer * buf) -{ - gboolean res = FALSE; - GstRingBufferClass *rclass; - gboolean resume = FALSE; - - g_return_val_if_fail (GST_IS_RING_BUFFER (buf), FALSE); - - GST_DEBUG_OBJECT (buf, "starting ringbuffer"); - - GST_OBJECT_LOCK (buf); - if (G_UNLIKELY (buf->abidata.ABI.flushing)) - goto flushing; - - if (G_UNLIKELY (!buf->acquired)) - goto not_acquired; - - /* if stopped, set to started */ - res = g_atomic_int_compare_and_exchange (&buf->state, - 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 not paused, must have been started"); - goto done; - } - resume = TRUE; - GST_DEBUG_OBJECT (buf, "resuming"); - } - - rclass = GST_RING_BUFFER_GET_CLASS (buf); - if (resume) { - if (G_LIKELY (rclass->resume)) - res = rclass->resume (buf); - } else { - if (G_LIKELY (rclass->start)) - res = rclass->start (buf); - } - - if (G_UNLIKELY (!res)) { - buf->state = GST_RING_BUFFER_STATE_PAUSED; - GST_DEBUG_OBJECT (buf, "failed to start"); - } else { - GST_DEBUG_OBJECT (buf, "started"); - } - -done: - GST_OBJECT_UNLOCK (buf); - - return res; - -flushing: - { - GST_DEBUG_OBJECT (buf, "we are flushing"); - GST_OBJECT_UNLOCK (buf); - return FALSE; - } -not_acquired: - { - GST_DEBUG_OBJECT (buf, "we are not acquired"); - GST_OBJECT_UNLOCK (buf); - return FALSE; - } -} - -static gboolean -gst_ring_buffer_pause_unlocked (GstRingBuffer * buf) -{ - gboolean res = FALSE; - GstRingBufferClass *rclass; - - GST_DEBUG_OBJECT (buf, "pausing ringbuffer"); - - /* if started, set to paused */ - res = g_atomic_int_compare_and_exchange (&buf->state, - GST_RING_BUFFER_STATE_STARTED, GST_RING_BUFFER_STATE_PAUSED); - - if (!res) - goto not_started; - - /* signal any waiters */ - GST_DEBUG_OBJECT (buf, "signal waiter"); - GST_RING_BUFFER_SIGNAL (buf); - - rclass = GST_RING_BUFFER_GET_CLASS (buf); - if (G_LIKELY (rclass->pause)) - res = rclass->pause (buf); - - if (G_UNLIKELY (!res)) { - buf->state = GST_RING_BUFFER_STATE_STARTED; - GST_DEBUG_OBJECT (buf, "failed to pause"); - } else { - GST_DEBUG_OBJECT (buf, "paused"); - } - - return res; - -not_started: - { - /* was not started */ - GST_DEBUG_OBJECT (buf, "was not started"); - return TRUE; - } -} - -/** - * gst_ring_buffer_pause: - * @buf: the #GstRingBuffer to pause - * - * Pause processing samples from the ringbuffer. - * - * Returns: TRUE if the device could be paused, FALSE on error. - * - * MT safe. - */ -gboolean -gst_ring_buffer_pause (GstRingBuffer * buf) -{ - gboolean res = FALSE; - - g_return_val_if_fail (GST_IS_RING_BUFFER (buf), FALSE); - - GST_OBJECT_LOCK (buf); - if (G_UNLIKELY (buf->abidata.ABI.flushing)) - goto flushing; - - if (G_UNLIKELY (!buf->acquired)) - goto not_acquired; - - res = gst_ring_buffer_pause_unlocked (buf); - GST_OBJECT_UNLOCK (buf); - - return res; - - /* ERRORS */ -flushing: - { - GST_DEBUG_OBJECT (buf, "we are flushing"); - GST_OBJECT_UNLOCK (buf); - return FALSE; - } -not_acquired: - { - GST_DEBUG_OBJECT (buf, "not acquired"); - GST_OBJECT_UNLOCK (buf); - return FALSE; - } -} - -/** - * gst_ring_buffer_stop: - * @buf: the #GstRingBuffer to stop - * - * Stop processing samples from the ringbuffer. - * - * Returns: TRUE if the device could be stopped, FALSE on error. - * - * MT safe. - */ -gboolean -gst_ring_buffer_stop (GstRingBuffer * buf) -{ - gboolean res = FALSE; - GstRingBufferClass *rclass; - - g_return_val_if_fail (GST_IS_RING_BUFFER (buf), FALSE); - - GST_DEBUG_OBJECT (buf, "stopping"); - - GST_OBJECT_LOCK (buf); - - /* if started, set to stopped */ - res = g_atomic_int_compare_and_exchange (&buf->state, - GST_RING_BUFFER_STATE_STARTED, GST_RING_BUFFER_STATE_STOPPED); - - if (!res) { - 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 */ - GST_DEBUG_OBJECT (buf, "signal waiter"); - GST_RING_BUFFER_SIGNAL (buf); - - rclass = GST_RING_BUFFER_GET_CLASS (buf); - if (G_LIKELY (rclass->stop)) - res = rclass->stop (buf); - - if (G_UNLIKELY (!res)) { - buf->state = GST_RING_BUFFER_STATE_STARTED; - GST_DEBUG_OBJECT (buf, "failed to stop"); - } else { - GST_DEBUG_OBJECT (buf, "stopped"); - } -done: - GST_OBJECT_UNLOCK (buf); - - return res; -} - -/** - * gst_ring_buffer_delay: - * @buf: the #GstRingBuffer to query - * - * Get the number of samples queued in the audio device. This is - * usually less than the segment size but can be bigger when the - * implementation uses another internal buffer between the audio - * device. - * - * For playback ringbuffers this is the amount of samples transfered from the - * ringbuffer to the device but still not played. - * - * For capture ringbuffers this is the amount of samples in the device that are - * not yet transfered to the ringbuffer. - * - * Returns: The number of samples queued in the audio device. - * - * MT safe. - */ -guint -gst_ring_buffer_delay (GstRingBuffer * buf) -{ - GstRingBufferClass *rclass; - guint res; - - g_return_val_if_fail (GST_IS_RING_BUFFER (buf), 0); - - /* buffer must be acquired */ - if (G_UNLIKELY (!gst_ring_buffer_is_acquired (buf))) - goto not_acquired; - - rclass = GST_RING_BUFFER_GET_CLASS (buf); - if (G_LIKELY (rclass->delay)) - res = rclass->delay (buf); - else - res = 0; - - return res; - -not_acquired: - { - GST_DEBUG_OBJECT (buf, "not acquired"); - return 0; - } -} - -/** - * gst_ring_buffer_samples_done: - * @buf: the #GstRingBuffer to query - * - * Get the number of samples that were processed by the ringbuffer - * since it was last started. This does not include the number of samples not - * yet processed (see gst_ring_buffer_delay()). - * - * Returns: The number of samples processed by the ringbuffer. - * - * MT safe. - */ -guint64 -gst_ring_buffer_samples_done (GstRingBuffer * buf) -{ - gint segdone; - guint64 samples; - - g_return_val_if_fail (GST_IS_RING_BUFFER (buf), 0); - - /* get the amount of segments we processed */ - segdone = g_atomic_int_get (&buf->segdone); - - /* convert to samples */ - samples = ((guint64) segdone) * buf->samples_per_seg; - - return samples; -} - -/** - * gst_ring_buffer_set_sample: - * @buf: the #GstRingBuffer to use - * @sample: the sample number to set - * - * Make sure that the next sample written to the device is - * accounted for as being the @sample sample written to the - * device. This value will be used in reporting the current - * sample position of the ringbuffer. - * - * This function will also clear the buffer with silence. - * - * MT safe. - */ -void -gst_ring_buffer_set_sample (GstRingBuffer * buf, guint64 sample) -{ - g_return_if_fail (GST_IS_RING_BUFFER (buf)); - - if (sample == -1) - sample = 0; - - if (G_UNLIKELY (buf->samples_per_seg == 0)) - return; - - /* 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; - - gst_ring_buffer_clear_all (buf); - - GST_DEBUG_OBJECT (buf, "set sample to %" G_GUINT64_FORMAT ", segbase %d", - sample, 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 - * - * Fill the ringbuffer with silence. - * - * MT safe. - */ -void -gst_ring_buffer_clear_all (GstRingBuffer * buf) -{ - GstRingBufferClass *rclass; - - g_return_if_fail (GST_IS_RING_BUFFER (buf)); - - rclass = GST_RING_BUFFER_GET_CLASS (buf); - - if (G_LIKELY (rclass->clear_all)) - rclass->clear_all (buf); -} - - -static gboolean -wait_segment (GstRingBuffer * buf) -{ - gint segments; - gboolean wait = TRUE; - - /* buffer must be started now or we deadlock since nobody is reading */ - if (G_UNLIKELY (g_atomic_int_get (&buf->state) != - GST_RING_BUFFER_STATE_STARTED)) { - /* see if we are allowed to start it */ - if (G_UNLIKELY (g_atomic_int_get (&buf->abidata.ABI.may_start) == FALSE)) - goto no_start; - - GST_DEBUG_OBJECT (buf, "start!"); - segments = g_atomic_int_get (&buf->segdone); - gst_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)) - wait = FALSE; - } - - /* take lock first, then update our waiting flag */ - GST_OBJECT_LOCK (buf); - if (G_UNLIKELY (buf->abidata.ABI.flushing)) - goto flushing; - - if (G_UNLIKELY (g_atomic_int_get (&buf->state) != - GST_RING_BUFFER_STATE_STARTED)) - goto not_started; - - if (G_LIKELY (wait)) { - if (g_atomic_int_compare_and_exchange (&buf->waiting, 0, 1)) { - GST_DEBUG_OBJECT (buf, "waiting.."); - GST_RING_BUFFER_WAIT (buf); - - if (G_UNLIKELY (buf->abidata.ABI.flushing)) - goto flushing; - - if (G_UNLIKELY (g_atomic_int_get (&buf->state) != - GST_RING_BUFFER_STATE_STARTED)) - goto not_started; - } - } - GST_OBJECT_UNLOCK (buf); - - return TRUE; - - /* ERROR */ -not_started: - { - g_atomic_int_compare_and_exchange (&buf->waiting, 1, 0); - GST_DEBUG_OBJECT (buf, "stopped processing"); - GST_OBJECT_UNLOCK (buf); - return FALSE; - } -flushing: - { - g_atomic_int_compare_and_exchange (&buf->waiting, 1, 0); - GST_DEBUG_OBJECT (buf, "flushing"); - GST_OBJECT_UNLOCK (buf); - return FALSE; - } -no_start: - { - GST_DEBUG_OBJECT (buf, "not allowed to start"); - return FALSE; - } -} - -#define FWD_SAMPLES(s,se,d,de) \ -G_STMT_START { \ - /* no rate conversion */ \ - guint towrite = MIN (se + bps - s, de - d); \ - /* simple copy */ \ - if (!skip) \ - memcpy (d, s, towrite); \ - in_samples -= towrite / bps; \ - out_samples -= towrite / bps; \ - s += towrite; \ - GST_DEBUG ("copy %u bytes", towrite); \ -} G_STMT_END - -/* in_samples >= out_samples, rate > 1.0 */ -#define FWD_UP_SAMPLES(s,se,d,de) \ -G_STMT_START { \ - guint8 *sb = s, *db = d; \ - while (s <= se && d < de) { \ - if (!skip) \ - memcpy (d, s, bps); \ - s += bps; \ - *accum += outr; \ - if ((*accum << 1) >= inr) { \ - *accum -= inr; \ - d += bps; \ - } \ - } \ - in_samples -= (s - sb)/bps; \ - out_samples -= (d - db)/bps; \ - GST_DEBUG ("fwd_up end %d/%d",*accum,*toprocess); \ -} G_STMT_END - -/* out_samples > in_samples, for rates smaller than 1.0 */ -#define FWD_DOWN_SAMPLES(s,se,d,de) \ -G_STMT_START { \ - guint8 *sb = s, *db = d; \ - while (s <= se && d < de) { \ - if (!skip) \ - memcpy (d, s, bps); \ - d += bps; \ - *accum += inr; \ - if ((*accum << 1) >= outr) { \ - *accum -= outr; \ - s += bps; \ - } \ - } \ - in_samples -= (s - sb)/bps; \ - out_samples -= (d - db)/bps; \ - GST_DEBUG ("fwd_down end %d/%d",*accum,*toprocess); \ -} G_STMT_END - -#define REV_UP_SAMPLES(s,se,d,de) \ -G_STMT_START { \ - guint8 *sb = se, *db = d; \ - while (s <= se && d < de) { \ - if (!skip) \ - memcpy (d, se, bps); \ - se -= bps; \ - *accum += outr; \ - while (d < de && (*accum << 1) >= inr) { \ - *accum -= inr; \ - d += bps; \ - } \ - } \ - in_samples -= (sb - se)/bps; \ - out_samples -= (d - db)/bps; \ - GST_DEBUG ("rev_up end %d/%d",*accum,*toprocess); \ -} G_STMT_END - -#define REV_DOWN_SAMPLES(s,se,d,de) \ -G_STMT_START { \ - guint8 *sb = se, *db = d; \ - while (s <= se && d < de) { \ - if (!skip) \ - memcpy (d, se, bps); \ - d += bps; \ - *accum += inr; \ - while (s <= se && (*accum << 1) >= outr) { \ - *accum -= outr; \ - se -= bps; \ - } \ - } \ - in_samples -= (sb - se)/bps; \ - out_samples -= (d - db)/bps; \ - GST_DEBUG ("rev_down end %d/%d",*accum,*toprocess); \ -} G_STMT_END - -static guint -default_commit (GstRingBuffer * buf, guint64 * sample, - guchar * data, gint in_samples, gint out_samples, gint * accum) -{ - gint segdone; - gint segsize, segtotal, bps, sps; - guint8 *dest, *data_end; - gint writeseg, sampleoff; - gint *toprocess; - gint inr, outr; - gboolean reverse; - - g_return_val_if_fail (buf->data != NULL, -1); - g_return_val_if_fail (data != NULL, -1); - - dest = GST_BUFFER_DATA (buf->data); - segsize = buf->spec.segsize; - segtotal = buf->spec.segtotal; - bps = buf->spec.bytes_per_sample; - sps = buf->samples_per_seg; - - reverse = out_samples < 0; - out_samples = ABS (out_samples); - - if (in_samples >= out_samples) - toprocess = &in_samples; - else - toprocess = &out_samples; - - inr = in_samples - 1; - outr = out_samples - 1; - - /* data_end points to the last sample we have to write, not past it. This is - * needed to properly handle reverse playback: it points to the last sample. */ - data_end = data + (bps * inr); - - /* figure out the segment and the offset inside the segment where - * the first sample should be written. */ - writeseg = *sample / sps; - sampleoff = (*sample % sps) * bps; - - /* write out all samples */ - while (*toprocess > 0) { - gint avail; - guint8 *d, *d_end; - gint ws; - gboolean skip; - - while (TRUE) { - gint diff; - - /* get the currently processed segment */ - segdone = g_atomic_int_get (&buf->segdone) - buf->segbase; - - /* see how far away it is from the write segment */ - diff = writeseg - segdone; - - GST_DEBUG - ("pointer at %d, write to %d-%d, diff %d, segtotal %d, segsize %d, base %d", - segdone, writeseg, sampleoff, diff, segtotal, segsize, buf->segbase); - - /* segment too far ahead, writer too slow, we need to drop, hopefully UNLIKELY */ - if (G_UNLIKELY (diff < 0)) { - /* we need to drop one segment at a time, pretend we wrote a - * segment. */ - skip = TRUE; - break; - } - - /* write segment is within writable range, we can break the loop and - * start writing the data. */ - if (diff < segtotal) { - skip = FALSE; - break; - } - - /* else we need to wait for the segment to become writable. */ - if (!wait_segment (buf)) - goto not_started; - } - - /* we can write now */ - ws = writeseg % segtotal; - avail = MIN (segsize - sampleoff, bps * out_samples); - - d = dest + (ws * segsize) + sampleoff; - d_end = d + avail; - *sample += avail / bps; - - GST_DEBUG_OBJECT (buf, "write @%p seg %d, sps %d, off %d, avail %d", - dest + ws * segsize, ws, sps, sampleoff, avail); - - if (G_LIKELY (inr == outr && !reverse)) { - /* no rate conversion, simply copy samples */ - FWD_SAMPLES (data, data_end, d, d_end); - } else if (!reverse) { - if (inr >= outr) - /* forward speed up */ - FWD_UP_SAMPLES (data, data_end, d, d_end); - else - /* forward slow down */ - FWD_DOWN_SAMPLES (data, data_end, d, d_end); - } else { - if (inr >= outr) - /* reverse speed up */ - REV_UP_SAMPLES (data, data_end, d, d_end); - else - /* reverse slow down */ - REV_DOWN_SAMPLES (data, data_end, d, d_end); - } - - /* for the next iteration we write to the next segment at the beginning. */ - writeseg++; - sampleoff = 0; - } - /* we consumed all samples here */ - data = data_end + bps; - -done: - return inr - ((data_end - data) / bps); - - /* ERRORS */ -not_started: - { - GST_DEBUG_OBJECT (buf, "stopped processing"); - goto done; - } -} - -/** - * 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. - */ -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 - * @data: the data to commit - * @len: the number of samples in the data to commit - * - * Same as gst_ring_buffer_commit_full() but with a in_samples and out_samples - * equal to @len, ignoring accum. - * - * Returns: The number of samples written to the ringbuffer or -1 on - * error. - * - * MT safe. - */ -guint -gst_ring_buffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data, - guint len) -{ - guint res; - guint64 samplep = sample; - - res = gst_ring_buffer_commit_full (buf, &samplep, data, len, len, NULL); - - return res; -} - -/** - * gst_ring_buffer_read: - * @buf: the #GstRingBuffer to read from - * @sample: the sample position of the data - * @data: where the data should be read - * @len: the number of samples in data to read - * - * Read @len samples from the ringbuffer into the memory pointed - * to by @data. - * The first sample should be read from position @sample in - * the ringbuffer. - * - * @len should not be a multiple of the segment size of the ringbuffer - * although it is recommended. - * - * Returns: The number of samples read from the ringbuffer or -1 on - * error. - * - * MT safe. - */ -guint -gst_ring_buffer_read (GstRingBuffer * buf, guint64 sample, guchar * data, - guint len) -{ - gint segdone; - gint segsize, segtotal, bps, sps; - guint8 *dest; - guint to_read; - - 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); - - dest = GST_BUFFER_DATA (buf->data); - segsize = buf->spec.segsize; - segtotal = buf->spec.segtotal; - bps = buf->spec.bytes_per_sample; - sps = buf->samples_per_seg; - - to_read = len; - /* read enough samples */ - while (to_read > 0) { - gint sampleslen; - gint readseg, sampleoff; - - /* figure out the segment and the offset inside the segment where - * the sample should be read from. */ - readseg = sample / sps; - sampleoff = (sample % sps); - - while (TRUE) { - gint diff; - - /* get the currently processed segment */ - segdone = g_atomic_int_get (&buf->segdone) - buf->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 - * reading) */ - diff = segdone - readseg; - - GST_DEBUG - ("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); - - /* segment too far ahead, reader too slow */ - if (G_UNLIKELY (diff >= segtotal)) { - /* pretend we read an empty segment. */ - sampleslen = MIN (sps, to_read); - memcpy (data, buf->empty_seg, sampleslen * bps); - goto next; - } - - /* read segment is within readable range, we can break the loop and - * start reading the data. */ - if (diff > 0) - break; - - /* else we need to wait for the segment to become readable. */ - if (!wait_segment (buf)) - goto not_started; - } - - /* we can read now */ - readseg = readseg % segtotal; - sampleslen = MIN (sps - sampleoff, to_read); - - GST_DEBUG_OBJECT (buf, "read @%p seg %d, off %d, sampleslen %d", - dest + readseg * segsize, readseg, sampleoff, sampleslen); - - memcpy (data, dest + (readseg * segsize) + (sampleoff * bps), - (sampleslen * bps)); - - next: - to_read -= sampleslen; - sample += sampleslen; - data += sampleslen * bps; - } - - return len - to_read; - - /* ERRORS */ -not_started: - { - GST_DEBUG_OBJECT (buf, "stopped processing"); - return len - to_read; - } -} - -/** - * gst_ring_buffer_prepare_read: - * @buf: the #GstRingBuffer to read from - * @segment: the segment to read - * @readptr: the pointer to the memory where samples can be read - * @len: the number of bytes to read - * - * Returns a pointer to memory where the data from segment @segment - * can be found. This function is mostly used by subclasses. - * - * Returns: FALSE if the buffer is not started. - * - * MT safe. - */ -gboolean -gst_ring_buffer_prepare_read (GstRingBuffer * buf, gint * segment, - guint8 ** readptr, gint * len) -{ - guint8 *data; - gint segdone; - - g_return_val_if_fail (GST_IS_RING_BUFFER (buf), 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); - g_return_val_if_fail (len != NULL, FALSE); - - 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); - - *segment = segdone % buf->spec.segtotal; - *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); - - return TRUE; -} - -/** - * gst_ring_buffer_advance: - * @buf: the #GstRingBuffer to advance - * @advance: the number of segments written - * - * Subclasses should call this function to notify the fact that - * @advance segments are now processed by the device. - * - * MT safe. - */ -void -gst_ring_buffer_advance (GstRingBuffer * buf, guint advance) -{ - g_return_if_fail (GST_IS_RING_BUFFER (buf)); - - /* update counter */ - g_atomic_int_add (&buf->segdone, advance); - - /* the lock is already taken when the waiting flag is set, - * we grab the lock as well to make sure the waiter is actually - * waiting for the signal */ - if (g_atomic_int_compare_and_exchange (&buf->waiting, 1, 0)) { - GST_OBJECT_LOCK (buf); - GST_DEBUG_OBJECT (buf, "signal waiter"); - GST_RING_BUFFER_SIGNAL (buf); - GST_OBJECT_UNLOCK (buf); - } -} - -/** - * gst_ring_buffer_clear: - * @buf: the #GstRingBuffer to clear - * @segment: the segment to clear - * - * Clear the given segment of the buffer with silence samples. - * This function is used by subclasses. - * - * MT safe. - */ -void -gst_ring_buffer_clear (GstRingBuffer * buf, gint segment) -{ - guint8 *data; - - g_return_if_fail (GST_IS_RING_BUFFER (buf)); - - /* no data means it's already cleared */ - if (G_UNLIKELY (buf->data == NULL)) - return; - - /* no empty_seg means it's not opened */ - if (G_UNLIKELY (buf->empty_seg == NULL)) - return; - - segment %= buf->spec.segtotal; - - data = GST_BUFFER_DATA (buf->data); - data += segment * buf->spec.segsize; - - GST_LOG ("clear segment %d @%p", segment, data); - - memcpy (data, buf->empty_seg, buf->spec.segsize); -} - -/** - * gst_ring_buffer_may_start: - * @buf: the #GstRingBuffer - * @allowed: the new value - * - * Tell the ringbuffer that it is allowed to start playback when - * the ringbuffer is filled with samples. - * - * MT safe. - * - * Since: 0.10.6 - */ -void -gst_ring_buffer_may_start (GstRingBuffer * buf, gboolean allowed) -{ - g_return_if_fail (GST_IS_RING_BUFFER (buf)); - - GST_LOG_OBJECT (buf, "may start: %d", allowed); - g_atomic_int_set (&buf->abidata.ABI.may_start, allowed); -} |