mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-23 10:11:08 +00:00
Make ringbuffer faster and more simple by removing the locks in the playback thread.
Original commit message from CVS: Make ringbuffer faster and more simple by removing the locks in the playback thread. Add sample accurate playback based on buffer sample offsets. Make the baseaudiosink provide a clock. Parse caps in the base class. Correctly handle seeking, flushing and state changes.
This commit is contained in:
parent
37822dc3fb
commit
235ea5989c
11 changed files with 863 additions and 511 deletions
44
ChangeLog
44
ChangeLog
|
@ -1,3 +1,47 @@
|
|||
2005-04-28 Wim Taymans <wim@fluendo.com>
|
||||
|
||||
* gst-libs/gst/audio/Makefile.am:
|
||||
* gst-libs/gst/audio/audio.h:
|
||||
* gst-libs/gst/audio/audioclock.c:
|
||||
* gst-libs/gst/audio/audioclock.h:
|
||||
* gst-libs/gst/audio/gstaudioclock.c: (gst_audio_clock_get_type),
|
||||
(gst_audio_clock_class_init), (gst_audio_clock_init),
|
||||
(gst_audio_clock_new), (gst_audio_clock_get_internal_time):
|
||||
* gst-libs/gst/audio/gstaudioclock.h:
|
||||
* gst-libs/gst/audio/gstaudiosink.c:
|
||||
(gst_audioringbuffer_get_type), (gst_audioringbuffer_class_init),
|
||||
(audioringbuffer_thread_func), (gst_audioringbuffer_init),
|
||||
(gst_audioringbuffer_acquire), (gst_audioringbuffer_release),
|
||||
(gst_audioringbuffer_play), (gst_audioringbuffer_stop),
|
||||
(gst_audioringbuffer_delay), (gst_audiosink_class_init),
|
||||
(gst_audiosink_create_ringbuffer):
|
||||
* gst-libs/gst/audio/gstbaseaudiosink.c:
|
||||
(gst_baseaudiosink_class_init), (gst_baseaudiosink_init),
|
||||
(gst_baseaudiosink_get_clock), (gst_baseaudiosink_get_time),
|
||||
(gst_baseaudiosink_set_property), (gst_baseaudiosink_get_property),
|
||||
(build_linear_format), (debug_spec_caps), (debug_spec_buffer),
|
||||
(gst_baseaudiosink_setcaps), (gst_baseaudiosink_get_times),
|
||||
(gst_baseaudiosink_event), (gst_baseaudiosink_preroll),
|
||||
(gst_baseaudiosink_render), (gst_baseaudiosink_create_ringbuffer),
|
||||
(gst_baseaudiosink_callback), (gst_baseaudiosink_change_state):
|
||||
* gst-libs/gst/audio/gstbaseaudiosink.h:
|
||||
* gst-libs/gst/audio/gstringbuffer.c: (gst_ringbuffer_get_type),
|
||||
(gst_ringbuffer_init), (gst_ringbuffer_finalize),
|
||||
(gst_ringbuffer_set_callback), (gst_ringbuffer_acquire),
|
||||
(gst_ringbuffer_release), (gst_ringbuffer_play),
|
||||
(gst_ringbuffer_pause), (gst_ringbuffer_stop),
|
||||
(gst_ringbuffer_delay), (gst_ringbuffer_played_samples),
|
||||
(gst_ringbuffer_set_sample), (wait_segment),
|
||||
(gst_ringbuffer_commit), (gst_ringbuffer_prepare_read),
|
||||
(gst_ringbuffer_advance), (gst_ringbuffer_clear):
|
||||
* gst-libs/gst/audio/gstringbuffer.h:
|
||||
Make ringbuffer faster and more simple by removing the locks
|
||||
in the playback thread.
|
||||
Add sample accurate playback based on buffer sample offsets.
|
||||
Make the baseaudiosink provide a clock.
|
||||
Parse caps in the base class.
|
||||
Correctly handle seeking, flushing and state changes.
|
||||
|
||||
2005-04-25 Thomas Vander Stichele <thomas at apestaart dot org>
|
||||
|
||||
* configure.ac:
|
||||
|
|
|
@ -14,7 +14,7 @@ EXTRA_DIST = gstaudiofiltertemplate.c make_filter
|
|||
CLEANFILES = gstaudiofilterexample.c \
|
||||
$(BUILT_SOURCES)
|
||||
|
||||
libgstaudio_@GST_MAJORMINOR@_la_SOURCES = audio.c audioclock.c \
|
||||
libgstaudio_@GST_MAJORMINOR@_la_SOURCES = audio.c gstaudioclock.c \
|
||||
multichannel.c \
|
||||
gstaudiosink.c \
|
||||
gstbaseaudiosink.c \
|
||||
|
@ -24,7 +24,7 @@ nodist_libgstaudio_@GST_MAJORMINOR@_la_SOURCES = $(built_sources) $(built_header
|
|||
libgstaudio_@GST_MAJORMINOR@includedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/audio
|
||||
libgstaudio_@GST_MAJORMINOR@include_HEADERS = \
|
||||
audio.h \
|
||||
audioclock.h \
|
||||
gstaudioclock.h \
|
||||
gstaudiofilter.h \
|
||||
gstaudiosink.h \
|
||||
gstbaseaudiosink.h \
|
||||
|
|
|
@ -20,8 +20,6 @@
|
|||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/audio/audioclock.h>
|
||||
|
||||
#ifndef __GST_AUDIO_AUDIO_H__
|
||||
#define __GST_AUDIO_AUDIO_H__
|
||||
|
||||
|
|
|
@ -1,205 +0,0 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
|
||||
* 2000 Wim Taymans <wtay@chello.be>
|
||||
*
|
||||
* audioclock.c: Clock for use by audio plugins
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "audioclock.h"
|
||||
|
||||
static void gst_audio_clock_class_init (GstAudioClockClass * klass);
|
||||
static void gst_audio_clock_init (GstAudioClock * clock);
|
||||
|
||||
static GstClockTime gst_audio_clock_get_internal_time (GstClock * clock);
|
||||
static GstClockReturn gst_audio_clock_id_wait_async (GstClock * clock,
|
||||
GstClockEntry * entry);
|
||||
static void gst_audio_clock_id_unschedule (GstClock * clock,
|
||||
GstClockEntry * entry);
|
||||
|
||||
static GstSystemClockClass *parent_class = NULL;
|
||||
|
||||
/* static guint gst_audio_clock_signals[LAST_SIGNAL] = { 0 }; */
|
||||
|
||||
GType
|
||||
gst_audio_clock_get_type (void)
|
||||
{
|
||||
static GType clock_type = 0;
|
||||
|
||||
if (!clock_type) {
|
||||
static const GTypeInfo clock_info = {
|
||||
sizeof (GstAudioClockClass),
|
||||
NULL,
|
||||
NULL,
|
||||
(GClassInitFunc) gst_audio_clock_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof (GstAudioClock),
|
||||
4,
|
||||
(GInstanceInitFunc) gst_audio_clock_init,
|
||||
NULL
|
||||
};
|
||||
|
||||
clock_type = g_type_register_static (GST_TYPE_SYSTEM_CLOCK, "GstAudioClock",
|
||||
&clock_info, 0);
|
||||
}
|
||||
return clock_type;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gst_audio_clock_class_init (GstAudioClockClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstObjectClass *gstobject_class;
|
||||
GstClockClass *gstclock_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
gstobject_class = (GstObjectClass *) klass;
|
||||
gstclock_class = (GstClockClass *) klass;
|
||||
|
||||
parent_class = g_type_class_ref (GST_TYPE_SYSTEM_CLOCK);
|
||||
|
||||
gstclock_class->get_internal_time = gst_audio_clock_get_internal_time;
|
||||
gstclock_class->wait_async = gst_audio_clock_id_wait_async;
|
||||
gstclock_class->unschedule = gst_audio_clock_id_unschedule;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_audio_clock_init (GstAudioClock * clock)
|
||||
{
|
||||
gst_object_set_name (GST_OBJECT (clock), "GstAudioClock");
|
||||
|
||||
clock->prev1 = 0;
|
||||
clock->prev2 = 0;
|
||||
}
|
||||
|
||||
GstClock *
|
||||
gst_audio_clock_new (gchar * name, GstAudioClockGetTimeFunc func,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstAudioClock *aclock =
|
||||
GST_AUDIO_CLOCK (g_object_new (GST_TYPE_AUDIO_CLOCK, NULL));
|
||||
|
||||
aclock->func = func;
|
||||
aclock->user_data = user_data;
|
||||
aclock->adjust = 0;
|
||||
|
||||
return (GstClock *) aclock;
|
||||
}
|
||||
|
||||
void
|
||||
gst_audio_clock_set_active (GstAudioClock * aclock, gboolean active)
|
||||
{
|
||||
GstClockTime audio_time, system_time;
|
||||
GstClock *clock;
|
||||
GTimeVal timeval;
|
||||
|
||||
g_return_if_fail (GST_IS_AUDIO_CLOCK (aclock));
|
||||
clock = GST_CLOCK (aclock);
|
||||
|
||||
if (active == aclock->active) {
|
||||
/* Nothing to do. */
|
||||
return;
|
||||
}
|
||||
|
||||
audio_time = aclock->func (clock, aclock->user_data);
|
||||
|
||||
g_get_current_time (&timeval);
|
||||
system_time = GST_TIMEVAL_TO_TIME (timeval);
|
||||
|
||||
/* Set the new adjust value in such a way that there's no abrupt
|
||||
discontinuity, i.e. if gst_audio_clock_get_internal_time is
|
||||
invoked right before and right after (de)activating the clock,
|
||||
the values returned will be close to each other, and the second
|
||||
value will be greater than or equal than the first. */
|
||||
if (active) {
|
||||
aclock->adjust = aclock->adjust + system_time - audio_time;
|
||||
} else {
|
||||
aclock->adjust = aclock->adjust + audio_time - system_time;
|
||||
}
|
||||
|
||||
aclock->active = active;
|
||||
}
|
||||
|
||||
static GstClockTime
|
||||
gst_audio_clock_get_internal_time (GstClock * clock)
|
||||
{
|
||||
GstAudioClock *aclock = GST_AUDIO_CLOCK (clock);
|
||||
|
||||
if (aclock->active) {
|
||||
return aclock->func (clock, aclock->user_data) + aclock->adjust;
|
||||
} else {
|
||||
GTimeVal timeval;
|
||||
|
||||
g_get_current_time (&timeval);
|
||||
return GST_TIMEVAL_TO_TIME (timeval) + aclock->adjust;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gst_audio_clock_update_time (GstAudioClock * aclock, GstClockTime time)
|
||||
{
|
||||
/* I don't know of a purpose in updating these; perhaps they can be removed */
|
||||
aclock->prev2 = aclock->prev1;
|
||||
aclock->prev1 = time;
|
||||
|
||||
/* FIXME: the wait_async subsystem should be made threadsafe, but I don't want
|
||||
* to lock and unlock a mutex on every iteration... */
|
||||
while (aclock->async_entries) {
|
||||
GstClockEntry *entry = (GstClockEntry *) aclock->async_entries->data;
|
||||
|
||||
if (entry->time > time)
|
||||
break;
|
||||
|
||||
entry->func ((GstClock *) aclock, time, entry, entry->user_data);
|
||||
|
||||
aclock->async_entries = g_slist_delete_link (aclock->async_entries,
|
||||
aclock->async_entries);
|
||||
/* do I need to free the entry? */
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
compare_clock_entries (GstClockEntry * entry1, GstClockEntry * entry2)
|
||||
{
|
||||
return entry1->time - entry2->time;
|
||||
}
|
||||
|
||||
static GstClockReturn
|
||||
gst_audio_clock_id_wait_async (GstClock * clock, GstClockEntry * entry)
|
||||
{
|
||||
GstAudioClock *aclock = (GstAudioClock *) clock;
|
||||
|
||||
aclock->async_entries = g_slist_insert_sorted (aclock->async_entries,
|
||||
entry, (GCompareFunc) compare_clock_entries);
|
||||
|
||||
/* is this the proper return val? */
|
||||
return GST_CLOCK_EARLY;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_audio_clock_id_unschedule (GstClock * clock, GstClockEntry * entry)
|
||||
{
|
||||
GstAudioClock *aclock = (GstAudioClock *) clock;
|
||||
|
||||
aclock->async_entries = g_slist_remove (aclock->async_entries, entry);
|
||||
}
|
105
gst-libs/gst/audio/gstaudioclock.c
Normal file
105
gst-libs/gst/audio/gstaudioclock.c
Normal file
|
@ -0,0 +1,105 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
|
||||
* 2000 Wim Taymans <wtay@chello.be>
|
||||
*
|
||||
* audioclock.c: Clock for use by audio plugins
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstaudioclock.h"
|
||||
|
||||
static void gst_audio_clock_class_init (GstAudioClockClass * klass);
|
||||
static void gst_audio_clock_init (GstAudioClock * clock);
|
||||
|
||||
static GstClockTime gst_audio_clock_get_internal_time (GstClock * clock);
|
||||
|
||||
static GstSystemClockClass *parent_class = NULL;
|
||||
|
||||
/* static guint gst_audio_clock_signals[LAST_SIGNAL] = { 0 }; */
|
||||
|
||||
GType
|
||||
gst_audio_clock_get_type (void)
|
||||
{
|
||||
static GType clock_type = 0;
|
||||
|
||||
if (!clock_type) {
|
||||
static const GTypeInfo clock_info = {
|
||||
sizeof (GstAudioClockClass),
|
||||
NULL,
|
||||
NULL,
|
||||
(GClassInitFunc) gst_audio_clock_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof (GstAudioClock),
|
||||
4,
|
||||
(GInstanceInitFunc) gst_audio_clock_init,
|
||||
NULL
|
||||
};
|
||||
|
||||
clock_type = g_type_register_static (GST_TYPE_SYSTEM_CLOCK, "GstAudioClock",
|
||||
&clock_info, 0);
|
||||
}
|
||||
return clock_type;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gst_audio_clock_class_init (GstAudioClockClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstObjectClass *gstobject_class;
|
||||
GstClockClass *gstclock_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
gstobject_class = (GstObjectClass *) klass;
|
||||
gstclock_class = (GstClockClass *) klass;
|
||||
|
||||
parent_class = g_type_class_ref (GST_TYPE_SYSTEM_CLOCK);
|
||||
|
||||
gstclock_class->get_internal_time = gst_audio_clock_get_internal_time;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_audio_clock_init (GstAudioClock * clock)
|
||||
{
|
||||
gst_object_set_name (GST_OBJECT (clock), "GstAudioClock");
|
||||
}
|
||||
|
||||
GstClock *
|
||||
gst_audio_clock_new (gchar * name, GstAudioClockGetTimeFunc func,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstAudioClock *aclock =
|
||||
GST_AUDIO_CLOCK (g_object_new (GST_TYPE_AUDIO_CLOCK, NULL));
|
||||
|
||||
aclock->func = func;
|
||||
aclock->user_data = user_data;
|
||||
|
||||
return (GstClock *) aclock;
|
||||
}
|
||||
|
||||
static GstClockTime
|
||||
gst_audio_clock_get_internal_time (GstClock * clock)
|
||||
{
|
||||
GstAudioClock *aclock = GST_AUDIO_CLOCK (clock);
|
||||
|
||||
return aclock->func (clock, aclock->user_data);
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
|
||||
* 2000 Wim Taymans <wtay@chello.be>
|
||||
* 2005 Wim Taymans <wim@fluendo.com>
|
||||
*
|
||||
* audioclock.h: Clock for use by audio plugins
|
||||
* gstaudioclock.h: Clock for use by audio plugins
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
|
@ -44,22 +44,13 @@ typedef struct _GstAudioClockClass GstAudioClockClass;
|
|||
|
||||
typedef GstClockTime (*GstAudioClockGetTimeFunc) (GstClock *clock, gpointer user_data);
|
||||
|
||||
|
||||
struct _GstAudioClock {
|
||||
GstSystemClock clock;
|
||||
|
||||
GstClockTime prev1, prev2;
|
||||
|
||||
/* --- protected --- */
|
||||
GstAudioClockGetTimeFunc func;
|
||||
gpointer user_data;
|
||||
|
||||
GstClockTimeDiff adjust;
|
||||
|
||||
GSList *async_entries;
|
||||
|
||||
gboolean active;
|
||||
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
|
@ -72,9 +63,6 @@ struct _GstAudioClockClass {
|
|||
GType gst_audio_clock_get_type (void);
|
||||
GstClock* gst_audio_clock_new (gchar *name, GstAudioClockGetTimeFunc func,
|
||||
gpointer user_data);
|
||||
void gst_audio_clock_set_active (GstAudioClock *aclock, gboolean active);
|
||||
|
||||
void gst_audio_clock_update_time (GstAudioClock *aclock, GstClockTime time);
|
||||
|
||||
G_END_DECLS
|
||||
|
|
@ -125,6 +125,7 @@ gst_audioringbuffer_class_init (GstAudioRingBufferClass * klass)
|
|||
gstringbuffer_class->release =
|
||||
GST_DEBUG_FUNCPTR (gst_audioringbuffer_release);
|
||||
gstringbuffer_class->play = GST_DEBUG_FUNCPTR (gst_audioringbuffer_play);
|
||||
gstringbuffer_class->resume = GST_DEBUG_FUNCPTR (gst_audioringbuffer_play);
|
||||
gstringbuffer_class->stop = GST_DEBUG_FUNCPTR (gst_audioringbuffer_stop);
|
||||
|
||||
gstringbuffer_class->delay = GST_DEBUG_FUNCPTR (gst_audioringbuffer_delay);
|
||||
|
@ -144,7 +145,6 @@ audioringbuffer_thread_func (GstRingBuffer * buf)
|
|||
GstAudioSinkClass *csink;
|
||||
GstAudioRingBuffer *abuf = GST_AUDIORINGBUFFER (buf);
|
||||
WriteFunc writefunc;
|
||||
gint segsize, segtotal;
|
||||
|
||||
sink = GST_AUDIOSINK (GST_OBJECT_PARENT (buf));
|
||||
csink = GST_AUDIOSINK_GET_CLASS (sink);
|
||||
|
@ -155,53 +155,48 @@ audioringbuffer_thread_func (GstRingBuffer * buf)
|
|||
if (writefunc == NULL)
|
||||
goto no_function;
|
||||
|
||||
segsize = buf->spec.segsize;
|
||||
segtotal = buf->spec.segtotal;
|
||||
|
||||
while (TRUE) {
|
||||
if (g_atomic_int_get (&buf->state) == GST_RINGBUFFER_STATE_PLAYING) {
|
||||
gint to_write, written;
|
||||
gint left, len;
|
||||
guint8 *readptr;
|
||||
gint readseg;
|
||||
|
||||
/* we write one segment */
|
||||
to_write = segsize;
|
||||
written = 0;
|
||||
/* need to read and write the next segment */
|
||||
readseg = (buf->playseg + 1) % segtotal;
|
||||
/* get a pointer in the buffer to this segment */
|
||||
readptr = gst_ringbuffer_prepare_read (buf, readseg);
|
||||
if (gst_ringbuffer_prepare_read (buf, &readseg, &readptr, &len)) {
|
||||
gint written = 0;
|
||||
|
||||
left = len;
|
||||
do {
|
||||
written = writefunc (sink, readptr + written, to_write);
|
||||
if (written < 0 || written > to_write) {
|
||||
perror ("error writing data\n");
|
||||
GST_DEBUG ("transfer %d bytes from segment %d", left, readseg);
|
||||
written = writefunc (sink, readptr + written, left);
|
||||
GST_DEBUG ("transfered %d bytes", written);
|
||||
if (written < 0 || written > left) {
|
||||
GST_WARNING ("error writing data (reason: %s), skipping segment\n",
|
||||
strerror (errno));
|
||||
break;
|
||||
}
|
||||
to_write -= written;
|
||||
} while (to_write > 0);
|
||||
left -= written;
|
||||
} while (left > 0);
|
||||
|
||||
/* clear written samples */
|
||||
gst_ringbuffer_clear (buf, readseg);
|
||||
|
||||
/* we wrote one segment */
|
||||
gst_ringbuffer_callback (buf, 1);
|
||||
gst_ringbuffer_advance (buf, 1);
|
||||
} else {
|
||||
GST_LOCK (abuf);
|
||||
GST_DEBUG ("signal wait");
|
||||
GST_AUDIORINGBUFFER_SIGNAL (buf);
|
||||
GST_DEBUG ("wait for play");
|
||||
GST_DEBUG ("wait for action");
|
||||
GST_AUDIORINGBUFFER_WAIT (buf);
|
||||
GST_DEBUG ("got signal");
|
||||
if (!abuf->running) {
|
||||
GST_UNLOCK (abuf);
|
||||
GST_DEBUG ("stop running");
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
GST_DEBUG ("continue running");
|
||||
GST_UNLOCK (abuf);
|
||||
}
|
||||
}
|
||||
done:
|
||||
GST_DEBUG ("exit thread");
|
||||
|
||||
return;
|
||||
|
@ -305,7 +300,7 @@ gst_audioringbuffer_play (GstRingBuffer * buf)
|
|||
|
||||
sink = GST_AUDIOSINK (GST_OBJECT_PARENT (buf));
|
||||
|
||||
GST_DEBUG ("play");
|
||||
GST_DEBUG ("play, sending signal");
|
||||
GST_AUDIORINGBUFFER_SIGNAL (buf);
|
||||
|
||||
return TRUE;
|
||||
|
@ -321,11 +316,15 @@ gst_audioringbuffer_stop (GstRingBuffer * buf)
|
|||
csink = GST_AUDIOSINK_GET_CLASS (sink);
|
||||
|
||||
/* unblock any pending writes to the audio device */
|
||||
if (csink->reset)
|
||||
if (csink->reset) {
|
||||
GST_DEBUG ("reset...");
|
||||
csink->reset (sink);
|
||||
GST_DEBUG ("reset done");
|
||||
}
|
||||
|
||||
GST_DEBUG ("stop");
|
||||
GST_DEBUG ("stop, waiting...");
|
||||
GST_AUDIORINGBUFFER_WAIT (buf);
|
||||
GST_DEBUG ("stoped");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -398,7 +397,9 @@ gst_audiosink_create_ringbuffer (GstBaseAudioSink * sink)
|
|||
{
|
||||
GstRingBuffer *buffer;
|
||||
|
||||
GST_DEBUG ("creating ringbuffer");
|
||||
buffer = g_object_new (GST_TYPE_AUDIORINGBUFFER, NULL);
|
||||
GST_DEBUG ("created ringbuffer @%p", buffer);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "gstbaseaudiosink.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_baseaudiosink_debug);
|
||||
|
@ -32,13 +34,13 @@ enum
|
|||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
#define DEFAULT_BUFFER -1
|
||||
#define DEFAULT_LATENCY -1
|
||||
#define DEFAULT_BUFFER_TIME 500 * GST_USECOND
|
||||
#define DEFAULT_LATENCY_TIME 10 * GST_USECOND
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_BUFFER,
|
||||
PROP_LATENCY,
|
||||
PROP_BUFFER_TIME,
|
||||
PROP_LATENCY_TIME,
|
||||
};
|
||||
|
||||
#define _do_init(bla) \
|
||||
|
@ -55,6 +57,10 @@ static void gst_baseaudiosink_get_property (GObject * object, guint prop_id,
|
|||
static GstElementStateReturn gst_baseaudiosink_change_state (GstElement *
|
||||
element);
|
||||
|
||||
static GstClock *gst_baseaudiosink_get_clock (GstElement * elem);
|
||||
static GstClockTime gst_baseaudiosink_get_time (GstClock * clock,
|
||||
GstBaseAudioSink * sink);
|
||||
|
||||
static GstFlowReturn gst_baseaudiosink_preroll (GstBaseSink * bsink,
|
||||
GstBuffer * buffer);
|
||||
static GstFlowReturn gst_baseaudiosink_render (GstBaseSink * bsink,
|
||||
|
@ -87,17 +93,18 @@ gst_baseaudiosink_class_init (GstBaseAudioSinkClass * klass)
|
|||
gobject_class->get_property =
|
||||
GST_DEBUG_FUNCPTR (gst_baseaudiosink_get_property);
|
||||
|
||||
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER,
|
||||
g_param_spec_uint64 ("buffer", "Buffer",
|
||||
"Size of audio buffer in nanoseconds (-1 = default)",
|
||||
0, G_MAXUINT64, DEFAULT_BUFFER, G_PARAM_READWRITE));
|
||||
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LATENCY,
|
||||
g_param_spec_uint64 ("latency", "Latency",
|
||||
"Audio latency in nanoseconds (-1 = default)",
|
||||
0, G_MAXUINT64, DEFAULT_LATENCY, G_PARAM_READWRITE));
|
||||
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_TIME,
|
||||
g_param_spec_int64 ("buffer-time", "Buffer Time",
|
||||
"Size of audio buffer in milliseconds (-1 = default)",
|
||||
-1, G_MAXINT64, DEFAULT_BUFFER_TIME, G_PARAM_READWRITE));
|
||||
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LATENCY_TIME,
|
||||
g_param_spec_int64 ("latency-time", "Latency Time",
|
||||
"Audio latency in milliseconds (-1 = default)",
|
||||
-1, G_MAXINT64, DEFAULT_LATENCY_TIME, G_PARAM_READWRITE));
|
||||
|
||||
gstelement_class->change_state =
|
||||
GST_DEBUG_FUNCPTR (gst_baseaudiosink_change_state);
|
||||
gstelement_class->get_clock = GST_DEBUG_FUNCPTR (gst_baseaudiosink_get_clock);
|
||||
|
||||
gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_baseaudiosink_event);
|
||||
gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_baseaudiosink_preroll);
|
||||
|
@ -110,8 +117,38 @@ gst_baseaudiosink_class_init (GstBaseAudioSinkClass * klass)
|
|||
static void
|
||||
gst_baseaudiosink_init (GstBaseAudioSink * baseaudiosink)
|
||||
{
|
||||
baseaudiosink->buffer = DEFAULT_BUFFER;
|
||||
baseaudiosink->latency = DEFAULT_LATENCY;
|
||||
baseaudiosink->buffer_time = DEFAULT_BUFFER_TIME;
|
||||
baseaudiosink->latency_time = DEFAULT_LATENCY_TIME;
|
||||
|
||||
baseaudiosink->clock = gst_audio_clock_new ("clock",
|
||||
(GstAudioClockGetTimeFunc) gst_baseaudiosink_get_time, baseaudiosink);
|
||||
}
|
||||
|
||||
static GstClock *
|
||||
gst_baseaudiosink_get_clock (GstElement * elem)
|
||||
{
|
||||
GstBaseAudioSink *sink;
|
||||
|
||||
sink = GST_BASEAUDIOSINK (elem);
|
||||
|
||||
return GST_CLOCK (gst_object_ref (GST_OBJECT (sink->clock)));
|
||||
}
|
||||
|
||||
static GstClockTime
|
||||
gst_baseaudiosink_get_time (GstClock * clock, GstBaseAudioSink * sink)
|
||||
{
|
||||
guint64 samples;
|
||||
GstClockTime result;
|
||||
|
||||
if (sink->ringbuffer == NULL || sink->ringbuffer->spec.rate == 0)
|
||||
return 0;
|
||||
|
||||
samples = gst_ringbuffer_played_samples (sink->ringbuffer);
|
||||
|
||||
result = samples * GST_SECOND / sink->ringbuffer->spec.rate;
|
||||
result += GST_ELEMENT (sink)->base_time;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -123,9 +160,11 @@ gst_baseaudiosink_set_property (GObject * object, guint prop_id,
|
|||
sink = GST_BASEAUDIOSINK (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_BUFFER:
|
||||
case PROP_BUFFER_TIME:
|
||||
sink->buffer_time = g_value_get_int64 (value);
|
||||
break;
|
||||
case PROP_LATENCY:
|
||||
case PROP_LATENCY_TIME:
|
||||
sink->latency_time = g_value_get_int64 (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
|
@ -142,9 +181,11 @@ gst_baseaudiosink_get_property (GObject * object, guint prop_id, GValue * value,
|
|||
sink = GST_BASEAUDIOSINK (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_BUFFER:
|
||||
case PROP_BUFFER_TIME:
|
||||
g_value_set_int64 (value, sink->buffer_time);
|
||||
break;
|
||||
case PROP_LATENCY:
|
||||
case PROP_LATENCY_TIME:
|
||||
g_value_set_int64 (value, sink->latency_time);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
|
@ -152,25 +193,228 @@ gst_baseaudiosink_get_property (GObject * object, guint prop_id, GValue * value,
|
|||
}
|
||||
}
|
||||
|
||||
static int linear_formats[4 * 2 * 2] = {
|
||||
GST_S8,
|
||||
GST_S8,
|
||||
GST_U8,
|
||||
GST_U8,
|
||||
GST_S16_LE,
|
||||
GST_S16_BE,
|
||||
GST_U16_LE,
|
||||
GST_U16_BE,
|
||||
GST_S24_LE,
|
||||
GST_S24_BE,
|
||||
GST_U24_LE,
|
||||
GST_U24_BE,
|
||||
GST_S32_LE,
|
||||
GST_S32_BE,
|
||||
GST_U32_LE,
|
||||
GST_U32_BE
|
||||
};
|
||||
|
||||
static int linear24_formats[3 * 2 * 2] = {
|
||||
GST_S24_3LE,
|
||||
GST_S24_3BE,
|
||||
GST_U24_3LE,
|
||||
GST_U24_3BE,
|
||||
GST_S20_3LE,
|
||||
GST_S20_3BE,
|
||||
GST_U20_3LE,
|
||||
GST_U20_3BE,
|
||||
GST_S18_3LE,
|
||||
GST_S18_3BE,
|
||||
GST_U18_3LE,
|
||||
GST_U18_3BE,
|
||||
};
|
||||
|
||||
static GstBufferFormat
|
||||
build_linear_format (int depth, int width, int unsignd, int big_endian)
|
||||
{
|
||||
if (width == 24) {
|
||||
switch (depth) {
|
||||
case 24:
|
||||
depth = 0;
|
||||
break;
|
||||
case 20:
|
||||
depth = 1;
|
||||
break;
|
||||
case 18:
|
||||
depth = 2;
|
||||
break;
|
||||
default:
|
||||
return GST_UNKNOWN;
|
||||
}
|
||||
return ((int (*)[2][2]) linear24_formats)[depth][!!unsignd][!!big_endian];
|
||||
} else {
|
||||
switch (depth) {
|
||||
case 8:
|
||||
depth = 0;
|
||||
break;
|
||||
case 16:
|
||||
depth = 1;
|
||||
break;
|
||||
case 24:
|
||||
depth = 2;
|
||||
break;
|
||||
case 32:
|
||||
depth = 3;
|
||||
break;
|
||||
default:
|
||||
return GST_UNKNOWN;
|
||||
}
|
||||
}
|
||||
return ((int (*)[2][2]) linear_formats)[depth][!!unsignd][!!big_endian];
|
||||
}
|
||||
|
||||
static void
|
||||
debug_spec_caps (GstBaseAudioSink * sink, GstRingBufferSpec * spec)
|
||||
{
|
||||
GST_DEBUG ("spec caps: %p %" GST_PTR_FORMAT, spec->caps, spec->caps);
|
||||
GST_DEBUG ("parsed caps: type: %d", spec->type);
|
||||
GST_DEBUG ("parsed caps: format: %d", 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);
|
||||
}
|
||||
|
||||
static void
|
||||
debug_spec_buffer (GstBaseAudioSink * sink, 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: 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);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_baseaudiosink_setcaps (GstBaseSink * bsink, GstCaps * caps)
|
||||
{
|
||||
GstBaseAudioSink *sink = GST_BASEAUDIOSINK (bsink);
|
||||
GstRingBufferSpec *spec;
|
||||
const gchar *mimetype;
|
||||
GstStructure *structure;
|
||||
|
||||
spec = &sink->ringbuffer->spec;
|
||||
|
||||
gst_caps_replace (&spec->caps, caps);
|
||||
spec->buffersize = sink->buffer;
|
||||
spec->latency = sink->latency;
|
||||
structure = gst_caps_get_structure (caps, 0);
|
||||
|
||||
spec->segtotal = 0x7fff;
|
||||
spec->segsize = 0x2048;
|
||||
/* 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;
|
||||
|
||||
spec->type = GST_BUFTYPE_LINEAR;
|
||||
|
||||
/* extract the needed information from the cap */
|
||||
if (!(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;
|
||||
|
||||
spec->format =
|
||||
build_linear_format (spec->depth, spec->width, spec->sign ? 0 : 1,
|
||||
spec->bigend ? 1 : 0);
|
||||
|
||||
} else if (!strncmp (mimetype, "audio/x-raw-float", 17)) {
|
||||
|
||||
spec->type = GST_BUFTYPE_FLOAT;
|
||||
|
||||
/* get layout */
|
||||
if (!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;
|
||||
}
|
||||
} else if (!strncmp (mimetype, "audio/x-alaw", 12)) {
|
||||
spec->type = GST_BUFTYPE_A_LAW;
|
||||
spec->format = GST_A_LAW;
|
||||
} else if (!strncmp (mimetype, "audio/x-mulaw", 13)) {
|
||||
spec->type = GST_BUFTYPE_MU_LAW;
|
||||
spec->format = GST_MU_LAW;
|
||||
} else {
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
/* get rate and channels */
|
||||
if (!(gst_structure_get_int (structure, "rate", &spec->rate) &&
|
||||
gst_structure_get_int (structure, "channels", &spec->channels)))
|
||||
goto parse_error;
|
||||
|
||||
spec->bytes_per_sample = (spec->width >> 3) * spec->channels;
|
||||
|
||||
gst_caps_replace (&spec->caps, caps);
|
||||
|
||||
debug_spec_caps (sink, spec);
|
||||
|
||||
spec->buffer_time = sink->buffer_time;
|
||||
spec->latency_time = sink->latency_time;
|
||||
|
||||
/* calculate suggested segsize and segtotal */
|
||||
spec->segsize =
|
||||
spec->rate * spec->bytes_per_sample * spec->latency_time / GST_MSECOND;
|
||||
spec->segtotal = spec->buffer_time / spec->latency_time;
|
||||
|
||||
GST_DEBUG ("release old ringbuffer");
|
||||
|
||||
gst_ringbuffer_release (sink->ringbuffer);
|
||||
gst_ringbuffer_acquire (sink->ringbuffer, spec);
|
||||
|
||||
debug_spec_buffer (sink, spec);
|
||||
|
||||
if (!gst_ringbuffer_acquire (sink->ringbuffer, spec))
|
||||
goto acquire_error;
|
||||
|
||||
/* calculate actual latency and buffer times */
|
||||
spec->latency_time =
|
||||
spec->segsize * GST_MSECOND / (spec->rate * spec->bytes_per_sample);
|
||||
spec->buffer_time =
|
||||
spec->segtotal * spec->segsize * GST_MSECOND / (spec->rate *
|
||||
spec->bytes_per_sample);
|
||||
|
||||
debug_spec_buffer (sink, spec);
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
parse_error:
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
acquire_error:
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -184,6 +428,36 @@ gst_baseaudiosink_get_times (GstBaseSink * bsink, GstBuffer * buffer,
|
|||
static void
|
||||
gst_baseaudiosink_event (GstBaseSink * bsink, GstEvent * event)
|
||||
{
|
||||
GstBaseAudioSink *sink = GST_BASEAUDIOSINK (bsink);
|
||||
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_FLUSH:
|
||||
if (GST_EVENT_FLUSH_DONE (event)) {
|
||||
} else {
|
||||
gst_ringbuffer_pause (sink->ringbuffer);
|
||||
}
|
||||
break;
|
||||
case GST_EVENT_DISCONTINUOUS:
|
||||
{
|
||||
guint64 time, sample;
|
||||
|
||||
if (gst_event_discont_get_value (event, GST_FORMAT_DEFAULT, &sample,
|
||||
NULL))
|
||||
goto have_value;
|
||||
if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &time, NULL)) {
|
||||
sample = time * sink->ringbuffer->spec.rate / GST_SECOND;
|
||||
goto have_value;
|
||||
}
|
||||
g_warning ("discont without valid timestamp");
|
||||
sample = 0;
|
||||
|
||||
have_value:
|
||||
gst_ringbuffer_set_sample (sink->ringbuffer, sample);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
|
@ -195,9 +469,14 @@ gst_baseaudiosink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
|
|||
static GstFlowReturn
|
||||
gst_baseaudiosink_render (GstBaseSink * bsink, GstBuffer * buf)
|
||||
{
|
||||
guint64 offset;
|
||||
GstBaseAudioSink *sink = GST_BASEAUDIOSINK (bsink);
|
||||
|
||||
gst_ringbuffer_commit (sink->ringbuffer, 0,
|
||||
offset = GST_BUFFER_OFFSET (buf);
|
||||
|
||||
GST_DEBUG ("in offset %llu, time %lld", offset, GST_BUFFER_TIMESTAMP (buf));
|
||||
|
||||
gst_ringbuffer_commit (sink->ringbuffer, offset,
|
||||
GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
|
||||
|
||||
return GST_FLOW_OK;
|
||||
|
@ -221,7 +500,8 @@ gst_baseaudiosink_create_ringbuffer (GstBaseAudioSink * sink)
|
|||
}
|
||||
|
||||
void
|
||||
gst_baseaudiosink_callback (GstRingBuffer * rbuf, guint advance, gpointer data)
|
||||
gst_baseaudiosink_callback (GstRingBuffer * rbuf, guint8 * data, guint len,
|
||||
gpointer user_data)
|
||||
{
|
||||
//GstBaseAudioSink *sink = GST_BASEAUDIOSINK (data);
|
||||
}
|
||||
|
@ -251,9 +531,14 @@ gst_baseaudiosink_change_state (GstElement * element)
|
|||
|
||||
switch (transition) {
|
||||
case GST_STATE_PLAYING_TO_PAUSED:
|
||||
gst_ringbuffer_stop (sink->ringbuffer);
|
||||
gst_ringbuffer_pause (sink->ringbuffer);
|
||||
/*
|
||||
while (gst_ringbuffer_delay (sink->ringbuffer) > 0)
|
||||
g_usleep (100);
|
||||
*/
|
||||
break;
|
||||
case GST_STATE_PAUSED_TO_READY:
|
||||
gst_ringbuffer_stop (sink->ringbuffer);
|
||||
gst_ringbuffer_release (sink->ringbuffer);
|
||||
gst_object_unref (GST_OBJECT (sink->ringbuffer));
|
||||
break;
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include <gst/gst.h>
|
||||
#include <gst/base/gstbasesink.h>
|
||||
#include "gstringbuffer.h"
|
||||
#include "gstaudioclock.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
@ -75,8 +76,11 @@ struct _GstBaseAudioSink {
|
|||
GstRingBuffer *ringbuffer;
|
||||
|
||||
/* required buffer and latency */
|
||||
GstClockTime buffer;
|
||||
GstClockTime latency;
|
||||
GstClockTime buffer_time;
|
||||
GstClockTime latency_time;
|
||||
|
||||
/* clock */
|
||||
GstClock *clock;
|
||||
};
|
||||
|
||||
struct _GstBaseAudioSinkClass {
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
|
||||
#include "gstringbuffer.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_ringbuffer_debug);
|
||||
#define GST_CAT_DEFAULT gst_ringbuffer_debug
|
||||
|
||||
static void gst_ringbuffer_class_init (GstRingBufferClass * klass);
|
||||
static void gst_ringbuffer_init (GstRingBuffer * ringbuffer);
|
||||
static void gst_ringbuffer_dispose (GObject * object);
|
||||
|
@ -52,6 +55,9 @@ gst_ringbuffer_get_type (void)
|
|||
|
||||
ringbuffer_type = g_type_register_static (GST_TYPE_OBJECT, "GstRingBuffer",
|
||||
&ringbuffer_info, G_TYPE_FLAG_ABSTRACT);
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_ringbuffer_debug, "ringbuffer", 0,
|
||||
"ringbuffer class");
|
||||
}
|
||||
return ringbuffer_type;
|
||||
}
|
||||
|
@ -76,12 +82,9 @@ gst_ringbuffer_init (GstRingBuffer * ringbuffer)
|
|||
{
|
||||
ringbuffer->acquired = FALSE;
|
||||
ringbuffer->state = GST_RINGBUFFER_STATE_STOPPED;
|
||||
ringbuffer->playseg = -1;
|
||||
ringbuffer->writeseg = -1;
|
||||
ringbuffer->segfilled = 0;
|
||||
ringbuffer->freeseg = -1;
|
||||
ringbuffer->segplayed = 0;
|
||||
ringbuffer->cond = g_cond_new ();
|
||||
ringbuffer->waiting = 0;
|
||||
ringbuffer->empty_seg = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -98,6 +101,7 @@ gst_ringbuffer_finalize (GObject * object)
|
|||
GstRingBuffer *ringbuffer = GST_RINGBUFFER (object);
|
||||
|
||||
g_cond_free (ringbuffer->cond);
|
||||
g_free (ringbuffer->empty_seg);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT (ringbuffer));
|
||||
}
|
||||
|
@ -117,6 +121,8 @@ void
|
|||
gst_ringbuffer_set_callback (GstRingBuffer * buf, GstRingBufferCallback cb,
|
||||
gpointer data)
|
||||
{
|
||||
g_return_if_fail (buf != NULL);
|
||||
|
||||
GST_LOCK (buf);
|
||||
buf->callback = cb;
|
||||
buf->cb_data = data;
|
||||
|
@ -142,6 +148,8 @@ gst_ringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
|
|||
gboolean res = FALSE;
|
||||
GstRingBufferClass *rclass;
|
||||
|
||||
g_return_val_if_fail (buf != NULL, FALSE);
|
||||
|
||||
GST_LOCK (buf);
|
||||
if (buf->acquired) {
|
||||
res = TRUE;
|
||||
|
@ -156,12 +164,25 @@ gst_ringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
|
|||
if (!res) {
|
||||
buf->acquired = FALSE;
|
||||
} else {
|
||||
buf->freeseg = spec->segtotal;
|
||||
if (buf->spec.bytes_per_sample != 0) {
|
||||
gint i, j;
|
||||
|
||||
buf->samples_per_seg = buf->spec.segsize / buf->spec.bytes_per_sample;
|
||||
|
||||
/* create an empty segment */
|
||||
g_free (buf->empty_seg);
|
||||
buf->empty_seg = g_malloc (buf->spec.segsize);
|
||||
for (i = 0, j = 0; i < buf->spec.segsize; i++) {
|
||||
buf->empty_seg[i] = buf->spec.silence_sample[j];
|
||||
j = (j + 1) % buf->spec.bytes_per_sample;
|
||||
}
|
||||
/* set sample position to 0 */
|
||||
gst_ringbuffer_set_sample (buf, 0);
|
||||
} else {
|
||||
g_warning ("invalid bytes_per_sample from acquire ringbuffer");
|
||||
buf->samples_per_seg = buf->spec.segsize;
|
||||
g_warning
|
||||
("invalid bytes_per_sample from acquire ringbuffer, fix the element");
|
||||
buf->acquired = FALSE;
|
||||
res = FALSE;
|
||||
}
|
||||
}
|
||||
done:
|
||||
|
@ -186,6 +207,8 @@ gst_ringbuffer_release (GstRingBuffer * buf)
|
|||
gboolean res = FALSE;
|
||||
GstRingBufferClass *rclass;
|
||||
|
||||
g_return_val_if_fail (buf != NULL, FALSE);
|
||||
|
||||
gst_ringbuffer_stop (buf);
|
||||
|
||||
GST_LOCK (buf);
|
||||
|
@ -201,6 +224,9 @@ gst_ringbuffer_release (GstRingBuffer * buf)
|
|||
|
||||
if (!res) {
|
||||
buf->acquired = TRUE;
|
||||
} else {
|
||||
g_free (buf->empty_seg);
|
||||
buf->empty_seg = NULL;
|
||||
}
|
||||
|
||||
done:
|
||||
|
@ -209,33 +235,6 @@ done:
|
|||
return res;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_ringbuffer_play_unlocked (GstRingBuffer * buf)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstRingBufferClass *rclass;
|
||||
|
||||
/* if paused, set to playing */
|
||||
res = g_atomic_int_compare_and_exchange (&buf->state,
|
||||
GST_RINGBUFFER_STATE_STOPPED, GST_RINGBUFFER_STATE_PLAYING);
|
||||
|
||||
if (!res) {
|
||||
/* was not stopped */
|
||||
res = TRUE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
rclass = GST_RINGBUFFER_GET_CLASS (buf);
|
||||
if (rclass->play)
|
||||
res = rclass->play (buf);
|
||||
|
||||
if (!res) {
|
||||
buf->state = GST_RINGBUFFER_STATE_PAUSED;
|
||||
}
|
||||
done:
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_ringbuffer_play:
|
||||
* @buf: the #GstRingBuffer to play
|
||||
|
@ -250,9 +249,42 @@ gboolean
|
|||
gst_ringbuffer_play (GstRingBuffer * buf)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstRingBufferClass *rclass;
|
||||
gboolean resume = FALSE;
|
||||
|
||||
g_return_val_if_fail (buf != NULL, FALSE);
|
||||
|
||||
GST_LOCK (buf);
|
||||
res = gst_ringbuffer_play_unlocked (buf);
|
||||
/* if paused, set to playing */
|
||||
res = g_atomic_int_compare_and_exchange (&buf->state,
|
||||
GST_RINGBUFFER_STATE_STOPPED, GST_RINGBUFFER_STATE_PLAYING);
|
||||
|
||||
if (!res) {
|
||||
/* was not stopped, try from paused */
|
||||
res = g_atomic_int_compare_and_exchange (&buf->state,
|
||||
GST_RINGBUFFER_STATE_PAUSED, GST_RINGBUFFER_STATE_PLAYING);
|
||||
if (!res) {
|
||||
/* was not paused either, must be playing then */
|
||||
res = TRUE;
|
||||
goto done;
|
||||
}
|
||||
resume = TRUE;
|
||||
}
|
||||
|
||||
rclass = GST_RINGBUFFER_GET_CLASS (buf);
|
||||
if (resume) {
|
||||
if (rclass->resume)
|
||||
res = rclass->resume (buf);
|
||||
} else {
|
||||
if (rclass->play)
|
||||
res = rclass->play (buf);
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
buf->state = GST_RINGBUFFER_STATE_PAUSED;
|
||||
}
|
||||
|
||||
done:
|
||||
GST_UNLOCK (buf);
|
||||
|
||||
return res;
|
||||
|
@ -274,6 +306,8 @@ gst_ringbuffer_pause (GstRingBuffer * buf)
|
|||
gboolean res = FALSE;
|
||||
GstRingBufferClass *rclass;
|
||||
|
||||
g_return_val_if_fail (buf != NULL, FALSE);
|
||||
|
||||
GST_LOCK (buf);
|
||||
/* if playing, set to paused */
|
||||
res = g_atomic_int_compare_and_exchange (&buf->state,
|
||||
|
@ -301,50 +335,6 @@ done:
|
|||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_ringbuffer_resume:
|
||||
* @buf: the #GstRingBuffer to resume
|
||||
*
|
||||
* Resume playing samples from the ringbuffer in the paused state.
|
||||
*
|
||||
* Returns: TRUE if the device could be paused, FALSE on error.
|
||||
*
|
||||
* MT safe.
|
||||
*/
|
||||
gboolean
|
||||
gst_ringbuffer_resume (GstRingBuffer * buf)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstRingBufferClass *rclass;
|
||||
|
||||
GST_LOCK (buf);
|
||||
/* if playing, set to paused */
|
||||
res = g_atomic_int_compare_and_exchange (&buf->state,
|
||||
GST_RINGBUFFER_STATE_PAUSED, GST_RINGBUFFER_STATE_PLAYING);
|
||||
|
||||
if (!res) {
|
||||
/* was not paused */
|
||||
res = TRUE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* signal any waiters */
|
||||
GST_RINGBUFFER_SIGNAL (buf);
|
||||
|
||||
rclass = GST_RINGBUFFER_GET_CLASS (buf);
|
||||
if (rclass->resume)
|
||||
res = rclass->resume (buf);
|
||||
|
||||
if (!res) {
|
||||
buf->state = GST_RINGBUFFER_STATE_PAUSED;
|
||||
}
|
||||
done:
|
||||
GST_UNLOCK (buf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gst_ringbuffer_stop:
|
||||
* @buf: the #GstRingBuffer to stop
|
||||
|
@ -361,6 +351,8 @@ gst_ringbuffer_stop (GstRingBuffer * buf)
|
|||
gboolean res = FALSE;
|
||||
GstRingBufferClass *rclass;
|
||||
|
||||
g_return_val_if_fail (buf != NULL, FALSE);
|
||||
|
||||
GST_LOCK (buf);
|
||||
/* if playing, set to stopped */
|
||||
res = g_atomic_int_compare_and_exchange (&buf->state,
|
||||
|
@ -382,11 +374,7 @@ gst_ringbuffer_stop (GstRingBuffer * buf)
|
|||
if (!res) {
|
||||
buf->state = GST_RINGBUFFER_STATE_PLAYING;
|
||||
} else {
|
||||
buf->segfilled = 0;
|
||||
buf->playseg = -1;
|
||||
buf->writeseg = -1;
|
||||
buf->freeseg = buf->spec.segtotal;
|
||||
buf->segplayed = 0;
|
||||
gst_ringbuffer_set_sample (buf, 0);
|
||||
}
|
||||
done:
|
||||
GST_UNLOCK (buf);
|
||||
|
@ -394,55 +382,6 @@ done:
|
|||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_ringbuffer_callback:
|
||||
* @buf: the #GstRingBuffer to callback
|
||||
* @advance: the number of segments written
|
||||
*
|
||||
* Subclasses should call this function to notify the fact that
|
||||
* @advance segments are now played by the device.
|
||||
*
|
||||
* MT safe.
|
||||
*/
|
||||
void
|
||||
gst_ringbuffer_callback (GstRingBuffer * buf, guint advance)
|
||||
{
|
||||
gint prevfree;
|
||||
gint segtotal;
|
||||
|
||||
if (advance == 0)
|
||||
return;
|
||||
|
||||
segtotal = buf->spec.segtotal;
|
||||
|
||||
/* update counter */
|
||||
g_atomic_int_add (&buf->segplayed, advance);
|
||||
|
||||
/* update free segments counter */
|
||||
prevfree = g_atomic_int_exchange_and_add (&buf->freeseg, advance);
|
||||
if (prevfree + advance > segtotal) {
|
||||
g_warning ("underrun!! read %d, write %d, advance %d, free %d, prevfree %d",
|
||||
buf->playseg, buf->writeseg, advance, buf->freeseg, prevfree);
|
||||
buf->freeseg = segtotal;
|
||||
buf->writeseg = buf->playseg;
|
||||
/* make sure to signal */
|
||||
prevfree = -1;
|
||||
}
|
||||
|
||||
buf->playseg = (buf->playseg + advance) % segtotal;
|
||||
|
||||
if (prevfree == -1) {
|
||||
/* we need to take the lock to make sure the other thread is
|
||||
* blocking in the wait */
|
||||
GST_LOCK (buf);
|
||||
GST_RINGBUFFER_SIGNAL (buf);
|
||||
GST_UNLOCK (buf);
|
||||
}
|
||||
|
||||
if (buf->callback)
|
||||
buf->callback (buf, advance, buf->cb_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_ringbuffer_delay:
|
||||
* @buf: the #GstRingBuffer to query
|
||||
|
@ -462,6 +401,8 @@ gst_ringbuffer_delay (GstRingBuffer * buf)
|
|||
GstRingBufferClass *rclass;
|
||||
guint res = 0;
|
||||
|
||||
g_return_val_if_fail (buf != NULL, 0);
|
||||
|
||||
rclass = GST_RINGBUFFER_GET_CLASS (buf);
|
||||
if (rclass->delay)
|
||||
res = rclass->delay (buf);
|
||||
|
@ -484,19 +425,100 @@ guint64
|
|||
gst_ringbuffer_played_samples (GstRingBuffer * buf)
|
||||
{
|
||||
gint segplayed;
|
||||
guint64 samples;
|
||||
guint64 raw, samples;
|
||||
guint delay;
|
||||
|
||||
g_return_val_if_fail (buf != NULL, 0);
|
||||
|
||||
/* get the amount of segments we played */
|
||||
segplayed = g_atomic_int_get (&buf->segplayed);
|
||||
|
||||
/* and the number of samples not yet played */
|
||||
delay = gst_ringbuffer_delay (buf);
|
||||
|
||||
samples = (segplayed * buf->samples_per_seg) - delay;
|
||||
samples = (segplayed * buf->samples_per_seg);
|
||||
raw = samples;
|
||||
|
||||
if (samples >= delay)
|
||||
samples -= delay;
|
||||
|
||||
GST_DEBUG ("played samples: raw %llu, delay %u, real %llu", raw, delay,
|
||||
samples);
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_ringbuffer_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_ringbuffer_set_sample (GstRingBuffer * buf, guint64 sample)
|
||||
{
|
||||
gint i;
|
||||
|
||||
g_return_if_fail (buf != NULL);
|
||||
|
||||
if (sample == -1)
|
||||
sample = 0;
|
||||
|
||||
/* FIXME, we assume the ringbuffer can restart at a random
|
||||
* position, round down to the beginning and keep track of
|
||||
* offset when calculating the played samples. */
|
||||
buf->segplayed = sample / buf->samples_per_seg;
|
||||
buf->next_sample = sample;
|
||||
|
||||
for (i = 0; i < buf->spec.segtotal; i++) {
|
||||
gst_ringbuffer_clear (buf, i);
|
||||
}
|
||||
|
||||
GST_DEBUG ("setting sample to %llu, segplayed %d", sample, buf->segplayed);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wait_segment (GstRingBuffer * buf)
|
||||
{
|
||||
/* buffer must be playing now or we deadlock since nobody is reading */
|
||||
if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_PLAYING) {
|
||||
GST_DEBUG ("play!");
|
||||
gst_ringbuffer_play (buf);
|
||||
}
|
||||
|
||||
/* take lock first, then update our waiting flag */
|
||||
GST_LOCK (buf);
|
||||
if (g_atomic_int_compare_and_exchange (&buf->waiting, 0, 1)) {
|
||||
GST_DEBUG ("waiting..");
|
||||
if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_PLAYING)
|
||||
goto not_playing;
|
||||
|
||||
GST_RINGBUFFER_WAIT (buf);
|
||||
|
||||
if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_PLAYING)
|
||||
goto not_playing;
|
||||
}
|
||||
GST_UNLOCK (buf);
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERROR */
|
||||
not_playing:
|
||||
{
|
||||
GST_UNLOCK (buf);
|
||||
GST_DEBUG ("stopped playing");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_ringbuffer_commit:
|
||||
* @buf: the #GstRingBuffer to commit
|
||||
|
@ -511,106 +533,106 @@ gst_ringbuffer_played_samples (GstRingBuffer * buf)
|
|||
* @len should not be a multiple of the segment size of the ringbuffer
|
||||
* although it is recommended.
|
||||
*
|
||||
* Returns: The number of samples written to the ringbuffer.
|
||||
* Returns: The number of samples written to the ringbuffer or -1 on
|
||||
* error.
|
||||
*
|
||||
* MT safe.
|
||||
*/
|
||||
/* FIXME, write the samples into the right position in the ringbuffer based
|
||||
* on the sample position argument
|
||||
*/
|
||||
guint
|
||||
gst_ringbuffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data,
|
||||
guint len)
|
||||
{
|
||||
guint towrite = len;
|
||||
gint segsize, segtotal;
|
||||
gint segplayed;
|
||||
gint segsize, segtotal, bps, sps;
|
||||
guint8 *dest;
|
||||
|
||||
if (buf->data == NULL)
|
||||
goto no_buffer;
|
||||
g_return_val_if_fail (buf != NULL, -1);
|
||||
g_return_val_if_fail (buf->data != NULL, -1);
|
||||
g_return_val_if_fail (data != NULL, -1);
|
||||
|
||||
if (sample == -1) {
|
||||
/* play aligned with last sample */
|
||||
sample = buf->next_sample;
|
||||
} else {
|
||||
if (sample != buf->next_sample) {
|
||||
GST_WARNING ("discontinuity found got %" G_GUINT64_FORMAT
|
||||
", expected %" G_GUINT64_FORMAT, sample, buf->next_sample);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* we write the complete buffer in chunks of segsize so that we can check for
|
||||
* a filled buffer after each segment. */
|
||||
while (towrite > 0) {
|
||||
gint segavail;
|
||||
gint segwrite;
|
||||
gint writeseg;
|
||||
gint segfilled;
|
||||
/* we assume the complete buffer will be consumed and the next sample
|
||||
* should be written after this */
|
||||
buf->next_sample = sample + len / bps;
|
||||
|
||||
segfilled = buf->segfilled;
|
||||
/* write out all bytes */
|
||||
while (len > 0) {
|
||||
gint writelen;
|
||||
gint writeseg, writeoff;
|
||||
|
||||
/* check for partial buffer */
|
||||
if (G_LIKELY (segfilled == 0)) {
|
||||
gint prevfree;
|
||||
gint newseg;
|
||||
/* figure out the segment and the offset inside the segment where
|
||||
* the sample should be written. */
|
||||
writeseg = sample / sps;
|
||||
writeoff = (sample % sps) * bps;
|
||||
|
||||
/* no partial buffer to fill up, allocate a new one */
|
||||
prevfree = g_atomic_int_exchange_and_add (&buf->freeseg, -1);
|
||||
if (prevfree == 0) {
|
||||
/* nothing was free */
|
||||
GST_DEBUG ("filled %d %d", buf->writeseg, buf->playseg);
|
||||
while (TRUE) {
|
||||
gint diff;
|
||||
|
||||
GST_LOCK (buf);
|
||||
/* buffer must be playing now or we deadlock since nobody is reading */
|
||||
if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_PLAYING)
|
||||
gst_ringbuffer_play_unlocked (buf);
|
||||
/* get the currently playing segment */
|
||||
segplayed = g_atomic_int_get (&buf->segplayed);
|
||||
|
||||
GST_RINGBUFFER_WAIT (buf);
|
||||
if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_PLAYING)
|
||||
/* see how far away it is from the write segment */
|
||||
diff = writeseg - segplayed;
|
||||
|
||||
GST_DEBUG
|
||||
("pointer at %d, sample %llu, write to %d-%d, len %d, diff %d, segtotal %d, segsize %d",
|
||||
segplayed, sample, writeseg, writeoff, len, diff, segtotal, segsize);
|
||||
|
||||
/* play segment too far ahead, we need to drop */
|
||||
if (diff < 0) {
|
||||
/* we need to drop one segment at a time, pretend we wrote a
|
||||
* segment. */
|
||||
writelen = MIN (segsize, len);
|
||||
goto next;
|
||||
}
|
||||
|
||||
/* write segment is within writable range, we can break the loop and
|
||||
* start writing the data. */
|
||||
if (diff < segtotal)
|
||||
break;
|
||||
|
||||
/* else we need to wait for the segment to become writable. */
|
||||
if (!wait_segment (buf))
|
||||
goto not_playing;
|
||||
GST_UNLOCK (buf);
|
||||
}
|
||||
|
||||
/* need to do this atomic as the reader updates the write pointer on
|
||||
* overruns */
|
||||
do {
|
||||
writeseg = g_atomic_int_get (&buf->writeseg);
|
||||
newseg = (writeseg + 1) % segtotal;
|
||||
} while (!g_atomic_int_compare_and_exchange (&buf->writeseg, writeseg,
|
||||
newseg));
|
||||
writeseg = newseg;
|
||||
} else {
|
||||
/* this is the segment we should write to */
|
||||
writeseg = g_atomic_int_get (&buf->writeseg);
|
||||
}
|
||||
if (writeseg < 0 || writeseg > segtotal) {
|
||||
g_warning ("invalid segment %d", writeseg);
|
||||
writeseg = 0;
|
||||
/* we can write now */
|
||||
writeseg = writeseg % segtotal;
|
||||
writelen = MIN (segsize - writeoff, len);
|
||||
|
||||
GST_DEBUG ("write @%p seg %d, off %d, len %d",
|
||||
dest + writeseg * segsize, writeseg, writeoff, writelen);
|
||||
|
||||
memcpy (dest + writeseg * segsize + writeoff, data, writelen);
|
||||
|
||||
next:
|
||||
len -= writelen;
|
||||
data += writelen;
|
||||
sample += writelen / bps;
|
||||
}
|
||||
|
||||
/* this is the available size now in the current segment */
|
||||
segavail = segsize - segfilled;
|
||||
return len;
|
||||
|
||||
/* we write up to the available space */
|
||||
segwrite = MIN (segavail, towrite);
|
||||
|
||||
memcpy (dest + writeseg * segsize + segfilled, data, segwrite);
|
||||
|
||||
towrite -= segwrite;
|
||||
data += segwrite;
|
||||
|
||||
if (segfilled + segwrite == segsize) {
|
||||
buf->segfilled = 0;
|
||||
} else {
|
||||
buf->segfilled = segfilled + segwrite;
|
||||
}
|
||||
}
|
||||
return len - towrite;
|
||||
|
||||
no_buffer:
|
||||
{
|
||||
GST_DEBUG ("no buffer");
|
||||
return -1;
|
||||
}
|
||||
/* ERRORS */
|
||||
not_playing:
|
||||
{
|
||||
GST_UNLOCK (buf);
|
||||
GST_DEBUG ("stopped playing");
|
||||
return len - towrite;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -618,20 +640,78 @@ not_playing:
|
|||
* gst_ringbuffer_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.
|
||||
* can be found. This function is used by subclasses.
|
||||
*
|
||||
* Returns: FALSE if the buffer is not playing.
|
||||
*
|
||||
* MT safe.
|
||||
*/
|
||||
guint8 *
|
||||
gst_ringbuffer_prepare_read (GstRingBuffer * buf, gint segment)
|
||||
gboolean
|
||||
gst_ringbuffer_prepare_read (GstRingBuffer * buf, gint * segment,
|
||||
guint8 ** readptr, gint * len)
|
||||
{
|
||||
guint8 *data;
|
||||
gint segplayed;
|
||||
|
||||
/* buffer must be playing */
|
||||
if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_PLAYING)
|
||||
return FALSE;
|
||||
|
||||
g_return_val_if_fail (buf != NULL, FALSE);
|
||||
g_return_val_if_fail (buf->data != NULL, FALSE);
|
||||
g_return_val_if_fail (readptr != NULL, FALSE);
|
||||
g_return_val_if_fail (len != NULL, FALSE);
|
||||
|
||||
data = GST_BUFFER_DATA (buf->data);
|
||||
|
||||
return data + (segment % buf->spec.segtotal) * buf->spec.segsize;
|
||||
/* get the position of the play pointer */
|
||||
segplayed = g_atomic_int_get (&buf->segplayed);
|
||||
|
||||
*segment = segplayed % buf->spec.segtotal;
|
||||
*len = buf->spec.segsize;
|
||||
*readptr = data + *segment * *len;
|
||||
|
||||
/* callback to fill the memory with data */
|
||||
if (buf->callback)
|
||||
buf->callback (buf, *readptr, *len, buf->cb_data);
|
||||
|
||||
GST_DEBUG ("prepare read from segment %d (real %d) @%p",
|
||||
*segment, segplayed, *readptr);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_ringbuffer_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 played by the device.
|
||||
*
|
||||
* MT safe.
|
||||
*/
|
||||
void
|
||||
gst_ringbuffer_advance (GstRingBuffer * buf, guint advance)
|
||||
{
|
||||
g_return_if_fail (buf != NULL);
|
||||
|
||||
/* update counter */
|
||||
g_atomic_int_add (&buf->segplayed, 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_LOCK (buf);
|
||||
GST_DEBUG ("signal waiter");
|
||||
GST_RINGBUFFER_SIGNAL (buf);
|
||||
GST_UNLOCK (buf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -649,8 +729,13 @@ gst_ringbuffer_clear (GstRingBuffer * buf, gint segment)
|
|||
{
|
||||
guint8 *data;
|
||||
|
||||
data = GST_BUFFER_DATA (buf->data);
|
||||
g_return_if_fail (buf != NULL);
|
||||
g_return_if_fail (buf->data != NULL);
|
||||
g_return_if_fail (buf->empty_seg != NULL);
|
||||
|
||||
memset (data + (segment % buf->spec.segtotal) * buf->spec.segsize, 0,
|
||||
buf->spec.segsize);
|
||||
data = GST_BUFFER_DATA (buf->data);
|
||||
data += (segment % buf->spec.segtotal) * buf->spec.segsize,
|
||||
GST_DEBUG ("clear segment %d @%p", segment, data);
|
||||
|
||||
memcpy (data, buf->empty_seg, buf->spec.segsize);
|
||||
}
|
||||
|
|
|
@ -38,7 +38,8 @@ typedef struct _GstRingBuffer GstRingBuffer;
|
|||
typedef struct _GstRingBufferClass GstRingBufferClass;
|
||||
typedef struct _GstRingBufferSpec GstRingBufferSpec;
|
||||
|
||||
typedef void (*GstRingBufferCallback) (GstRingBuffer *rbuf, guint advance, gpointer data);
|
||||
/* called to fill data with len bytes of samples */
|
||||
typedef void (*GstRingBufferCallback) (GstRingBuffer *rbuf, guint8* data, guint len, gpointer user_data);
|
||||
|
||||
typedef enum {
|
||||
GST_RINGBUFFER_STATE_STOPPED,
|
||||
|
@ -55,21 +56,64 @@ typedef enum {
|
|||
|
||||
typedef enum
|
||||
{
|
||||
GST_U8,
|
||||
GST_BUFTYPE_LINEAR,
|
||||
GST_BUFTYPE_FLOAT,
|
||||
GST_BUFTYPE_MU_LAW,
|
||||
GST_BUFTYPE_A_LAW,
|
||||
GST_BUFTYPE_IMA_ADPCM,
|
||||
GST_BUFTYPE_MPEG,
|
||||
GST_BUFTYPE_GSM,
|
||||
} GstBufferFormatType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_UNKNOWN,
|
||||
|
||||
GST_S8,
|
||||
GST_U8,
|
||||
|
||||
GST_U16_LE,
|
||||
GST_S16_LE,
|
||||
GST_U16_BE,
|
||||
GST_S16_BE,
|
||||
GST_U16_LE,
|
||||
GST_U16_BE,
|
||||
|
||||
GST_U24_LE,
|
||||
GST_S24_LE,
|
||||
GST_U24_BE,
|
||||
GST_S24_BE,
|
||||
GST_U24_LE,
|
||||
GST_U24_BE,
|
||||
|
||||
GST_S32_LE,
|
||||
GST_S32_BE,
|
||||
GST_U32_LE,
|
||||
GST_U32_BE,
|
||||
|
||||
GST_S24_3LE,
|
||||
GST_S24_3BE,
|
||||
GST_U24_3LE,
|
||||
GST_U24_3BE,
|
||||
GST_S20_3LE,
|
||||
GST_S20_3BE,
|
||||
GST_U20_3LE,
|
||||
GST_U20_3BE,
|
||||
GST_S18_3LE,
|
||||
GST_S18_3BE,
|
||||
GST_U18_3LE,
|
||||
GST_U18_3BE,
|
||||
|
||||
GST_FLOAT32_LE,
|
||||
GST_FLOAT32_BE,
|
||||
|
||||
GST_FLOAT64_LE,
|
||||
GST_FLOAT64_BE,
|
||||
|
||||
GST_MU_LAW,
|
||||
GST_A_LAW,
|
||||
GST_IMA_ADPCM,
|
||||
GST_MPEG,
|
||||
GST_GSM,
|
||||
|
||||
/* fill me */
|
||||
|
||||
GST_FLOAT_LE,
|
||||
GST_FLOAT_BE,
|
||||
} GstBufferFormat;
|
||||
|
||||
struct _GstRingBufferSpec
|
||||
|
@ -78,12 +122,17 @@ struct _GstRingBufferSpec
|
|||
GstCaps *caps; /* the caps of the buffer */
|
||||
|
||||
/* in/out */
|
||||
GstBufferFormatType type;
|
||||
GstBufferFormat format;
|
||||
gboolean sign;
|
||||
gboolean bigend;
|
||||
gint width;
|
||||
gint depth;
|
||||
gint rate;
|
||||
gint channels;
|
||||
|
||||
GstClockTime latency; /* the required/actual latency */
|
||||
GstClockTime buffersize; /* the required/actual size of the buffer */
|
||||
GstClockTime latency_time; /* the required/actual latency time */
|
||||
GstClockTime buffer_time; /* the required/actual time of the buffer */
|
||||
gint segsize; /* size of one buffer segement */
|
||||
gint segtotal; /* total number of segments */
|
||||
|
||||
|
@ -107,18 +156,15 @@ struct _GstRingBuffer {
|
|||
GstRingBufferSpec spec;
|
||||
GstRingBufferSegState *segstate;
|
||||
gint samples_per_seg; /* number of samples per segment */
|
||||
guint8 *empty_seg;
|
||||
|
||||
/*< public >*/ /* ATOMIC */
|
||||
gint state; /* state of the buffer */
|
||||
gint freeseg; /* number of free segments */
|
||||
gint segplayed; /* number of segments played since last start */
|
||||
|
||||
/*< protected >*/
|
||||
gint playseg; /* segment currently playing */
|
||||
gint writeseg; /* segment currently written */
|
||||
gint segfilled; /* bytes used in current write segment */
|
||||
gint waiting; /* when waiting for a segment to be freed */
|
||||
|
||||
/*< private >*/
|
||||
guint64 next_sample; /* the next sample we need to write */
|
||||
GstRingBufferCallback callback;
|
||||
gpointer cb_data;
|
||||
};
|
||||
|
@ -146,8 +192,7 @@ GType gst_ringbuffer_get_type(void);
|
|||
|
||||
/* callback stuff */
|
||||
void gst_ringbuffer_set_callback (GstRingBuffer *buf, GstRingBufferCallback cb,
|
||||
gpointer data);
|
||||
void gst_ringbuffer_callback (GstRingBuffer *buf, guint advance);
|
||||
gpointer user_data);
|
||||
|
||||
/* allocate resources */
|
||||
gboolean gst_ringbuffer_acquire (GstRingBuffer *buf, GstRingBufferSpec *spec);
|
||||
|
@ -156,20 +201,22 @@ gboolean gst_ringbuffer_release (GstRingBuffer *buf);
|
|||
/* playback/pause */
|
||||
gboolean gst_ringbuffer_play (GstRingBuffer *buf);
|
||||
gboolean gst_ringbuffer_pause (GstRingBuffer *buf);
|
||||
gboolean gst_ringbuffer_resume (GstRingBuffer *buf);
|
||||
gboolean gst_ringbuffer_stop (GstRingBuffer *buf);
|
||||
|
||||
/* get status */
|
||||
guint gst_ringbuffer_delay (GstRingBuffer *buf);
|
||||
guint64 gst_ringbuffer_played_samples (GstRingBuffer *buf);
|
||||
|
||||
void gst_ringbuffer_set_sample (GstRingBuffer *buf, guint64 sample);
|
||||
|
||||
/* commit samples */
|
||||
guint gst_ringbuffer_commit (GstRingBuffer *buf, guint64 sample,
|
||||
guchar *data, guint len);
|
||||
|
||||
/* mostly protected */
|
||||
guint8* gst_ringbuffer_prepare_read (GstRingBuffer *buf, gint segment);
|
||||
gboolean gst_ringbuffer_prepare_read (GstRingBuffer *buf, gint *segment, guint8 **readptr, gint *len);
|
||||
void gst_ringbuffer_clear (GstRingBuffer *buf, gint segment);
|
||||
void gst_ringbuffer_advance (GstRingBuffer *buf, guint advance);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
|
Loading…
Reference in a new issue