/* 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; }