diff options
Diffstat (limited to 'gst-libs/gst/audio/gstringbuffer.c')
-rw-r--r-- | gst-libs/gst/audio/gstringbuffer.c | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/gst-libs/gst/audio/gstringbuffer.c b/gst-libs/gst/audio/gstringbuffer.c new file mode 100644 index 00000000..07889db2 --- /dev/null +++ b/gst-libs/gst/audio/gstringbuffer.c @@ -0,0 +1,282 @@ +/* GStreamer + * Copyright (C) 2005 Wim Taymans <wim@fluendo.com> + * + * gstringbuffer.c: + * + * 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. + */ + +#include <string.h> + +#include "gstringbuffer.h" + +static void gst_ringbuffer_class_init (GstRingBufferClass * klass); +static void gst_ringbuffer_init (GstRingBuffer * ringbuffer); +static void gst_ringbuffer_dispose (GObject * object); +static void gst_ringbuffer_finalize (GObject * object); + +static GstObjectClass *parent_class = NULL; + +/* ringbuffer abstract base class */ +GType +gst_ringbuffer_get_type (void) +{ + static GType ringbuffer_type = 0; + + if (!ringbuffer_type) { + static const GTypeInfo ringbuffer_info = { + sizeof (GstRingBufferClass), + NULL, + NULL, + (GClassInitFunc) gst_ringbuffer_class_init, + NULL, + NULL, + sizeof (GstRingBuffer), + 0, + (GInstanceInitFunc) gst_ringbuffer_init, + NULL + }; + + ringbuffer_type = g_type_register_static (GST_TYPE_OBJECT, "GstRingBuffer", + &ringbuffer_info, G_TYPE_FLAG_ABSTRACT); + } + return ringbuffer_type; +} + +static void +gst_ringbuffer_class_init (GstRingBufferClass * klass) +{ + GObjectClass *gobject_class; + GstObjectClass *gstobject_class; + + gobject_class = (GObjectClass *) klass; + gstobject_class = (GstObjectClass *) klass; + + parent_class = g_type_class_ref (GST_TYPE_OBJECT); + + gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_ringbuffer_dispose); + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_ringbuffer_finalize); +} + +static void +gst_ringbuffer_init (GstRingBuffer * ringbuffer) +{ + ringbuffer->acquired = FALSE; + ringbuffer->state = GST_RINGBUFFER_STATE_STOPPED; + ringbuffer->playseg = 0; + ringbuffer->writeseg = 1; + ringbuffer->segfilled = 0; + ringbuffer->waiters = FALSE; + ringbuffer->cond = g_cond_new (); +} + +static void +gst_ringbuffer_dispose (GObject * object) +{ + GstRingBuffer *ringbuffer = GST_RINGBUFFER (object); + + G_OBJECT_CLASS (parent_class)->dispose (G_OBJECT (ringbuffer)); +} + +static void +gst_ringbuffer_finalize (GObject * object) +{ + GstRingBuffer *ringbuffer = GST_RINGBUFFER (object); + + g_cond_free (ringbuffer->cond); + + G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT (ringbuffer)); +} + +void +gst_ringbuffer_set_callback (GstRingBuffer * buf, GstRingBufferCallback cb, + gpointer data) +{ + GST_LOCK (buf); + buf->callback = cb; + buf->cb_data = data; + GST_UNLOCK (buf); +} + + +gboolean +gst_ringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) +{ + gboolean res = FALSE; + GstRingBufferClass *rclass; + + GST_LOCK (buf); + if (buf->acquired) { + res = TRUE; + goto done; + } + buf->acquired = TRUE; + GST_UNLOCK (buf); + + rclass = GST_RINGBUFFER_GET_CLASS (buf); + if (rclass->acquire) + res = rclass->acquire (buf, spec); + + GST_LOCK (buf); + if (!res) { + buf->acquired = FALSE; + } +done: + GST_UNLOCK (buf); + + return res; +} + +gboolean +gst_ringbuffer_release (GstRingBuffer * buf) +{ + gboolean res = FALSE; + GstRingBufferClass *rclass; + + GST_LOCK (buf); + if (!buf->acquired) { + res = TRUE; + goto done; + } + buf->acquired = FALSE; + GST_UNLOCK (buf); + + rclass = GST_RINGBUFFER_GET_CLASS (buf); + if (rclass->release) + res = rclass->release (buf); + + GST_LOCK (buf); + if (!res) { + buf->acquired = TRUE; + } +done: + GST_UNLOCK (buf); + + return res; +} + +gboolean +gst_ringbuffer_play (GstRingBuffer * buf) +{ + gboolean res = FALSE; + GstRingBufferClass *rclass; + + GST_LOCK (buf); + if (buf->state == GST_RINGBUFFER_STATE_PLAYING) { + res = TRUE; + goto done; + } + buf->state = GST_RINGBUFFER_STATE_PLAYING; + + rclass = GST_RINGBUFFER_GET_CLASS (buf); + if (rclass->play) + res = rclass->play (buf); + + if (!res) { + buf->state = GST_RINGBUFFER_STATE_STOPPED; + } +done: + GST_UNLOCK (buf); + + return res; +} + +gboolean +gst_ringbuffer_stop (GstRingBuffer * buf) +{ + gboolean res = FALSE; + GstRingBufferClass *rclass; + + GST_LOCK (buf); + if (buf->state == GST_RINGBUFFER_STATE_STOPPED) { + res = TRUE; + goto done; + } + buf->state = GST_RINGBUFFER_STATE_STOPPED; + + rclass = GST_RINGBUFFER_GET_CLASS (buf); + if (rclass->stop) + res = rclass->stop (buf); + + if (!res) { + buf->state = GST_RINGBUFFER_STATE_PLAYING; + } +done: + GST_UNLOCK (buf); + + return res; +} + +void +gst_ringbuffer_callback (GstRingBuffer * buf, guint advance) +{ + GST_LOCK (buf); + buf->playseg = (buf->playseg + advance) % buf->spec.segtotal; + if (buf->playseg == buf->writeseg) { + g_print ("underrun!! read %d, write %d\n", buf->playseg, buf->writeseg); + buf->writeseg = (buf->playseg + 1) % buf->spec.segtotal; + buf->segfilled = 0; + } + if (buf->waiters) + GST_RINGBUFFER_SIGNAL (buf); + GST_UNLOCK (buf); + + if (buf->callback) + buf->callback (buf, advance, buf->cb_data); +} + +guint +gst_ringbuffer_write (GstRingBuffer * buf, GstClockTime time, guchar * data, + guint len) +{ + guint towrite = len; + guint written = 0; + + GST_LOCK (buf); + /* we write the complete buffer */ + while (towrite > 0) { + guint segavail; + guint segwrite; + + /* we cannot write anymore since the buffer is filled, wait for + * some space to become available */ + while (buf->writeseg == buf->playseg) { + buf->waiters = TRUE; + GST_RINGBUFFER_WAIT (buf); + buf->waiters = FALSE; + } + + /* this is the available size now in the current segment */ + segavail = buf->spec.segsize - buf->segfilled; + + /* we write up to the available space */ + segwrite = MIN (segavail, towrite); + memcpy (GST_BUFFER_DATA (buf->data) + buf->writeseg * buf->spec.segsize + + buf->segfilled, data, segwrite); + towrite -= segwrite; + data += segwrite; + buf->segfilled += segwrite; + written += segwrite; + /* we wrote a complete segment, advance the write pointer */ + if (buf->segfilled == buf->spec.segsize) { + buf->writeseg = (buf->writeseg + 1) % buf->spec.segtotal; + buf->segfilled = 0; + } + } + GST_UNLOCK (buf); + + return written; +} |