mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 09:55:36 +00:00
An attempt at a set of audio base classes together with some design docs.
Original commit message from CVS: * docs/design-audiosinks.txt: * gst-libs/gst/audio/Makefile.am: * gst-libs/gst/audio/TODO: * gst-libs/gst/audio/gstaudiosink.c: (gst_audioringbuffer_get_type), (gst_audioringbuffer_class_init), (audioringbuffer_thread_func), (gst_audioringbuffer_init), (gst_audioringbuffer_dispose), (gst_audioringbuffer_finalize), (gst_audioringbuffer_acquire), (gst_audioringbuffer_release), (gst_audioringbuffer_play), (gst_audioringbuffer_stop), (gst_audioringbuffer_delay), (gst_audiosink_base_init), (gst_audiosink_class_init), (gst_audiosink_init), (gst_audiosink_create_ringbuffer): * gst-libs/gst/audio/gstaudiosink.h: * gst-libs/gst/audio/gstbaseaudiosink.c: (gst_baseaudiosink_base_init), (gst_baseaudiosink_class_init), (gst_baseaudiosink_init), (gst_baseaudiosink_set_property), (gst_baseaudiosink_get_property), (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_class_init), (gst_ringbuffer_init), (gst_ringbuffer_dispose), (gst_ringbuffer_finalize), (gst_ringbuffer_set_callback), (gst_ringbuffer_acquire), (gst_ringbuffer_release), (gst_ringbuffer_play_unlocked), (gst_ringbuffer_play), (gst_ringbuffer_pause), (gst_ringbuffer_resume), (gst_ringbuffer_stop), (gst_ringbuffer_callback), (gst_ringbuffer_delay), (gst_ringbuffer_played_samples), (gst_ringbuffer_commit), (gst_ringbuffer_prepare_read), (gst_ringbuffer_clear): * gst-libs/gst/audio/gstringbuffer.h: An attempt at a set of audio base classes together with some design docs.
This commit is contained in:
parent
468d6d4326
commit
5a3941c762
10 changed files with 1885 additions and 5 deletions
38
ChangeLog
38
ChangeLog
|
@ -1,3 +1,41 @@
|
||||||
|
2005-04-20 Wim Taymans <wim@fluendo.com>
|
||||||
|
|
||||||
|
* docs/design-audiosinks.txt:
|
||||||
|
* gst-libs/gst/audio/Makefile.am:
|
||||||
|
* gst-libs/gst/audio/TODO:
|
||||||
|
* gst-libs/gst/audio/gstaudiosink.c:
|
||||||
|
(gst_audioringbuffer_get_type), (gst_audioringbuffer_class_init),
|
||||||
|
(audioringbuffer_thread_func), (gst_audioringbuffer_init),
|
||||||
|
(gst_audioringbuffer_dispose), (gst_audioringbuffer_finalize),
|
||||||
|
(gst_audioringbuffer_acquire), (gst_audioringbuffer_release),
|
||||||
|
(gst_audioringbuffer_play), (gst_audioringbuffer_stop),
|
||||||
|
(gst_audioringbuffer_delay), (gst_audiosink_base_init),
|
||||||
|
(gst_audiosink_class_init), (gst_audiosink_init),
|
||||||
|
(gst_audiosink_create_ringbuffer):
|
||||||
|
* gst-libs/gst/audio/gstaudiosink.h:
|
||||||
|
* gst-libs/gst/audio/gstbaseaudiosink.c:
|
||||||
|
(gst_baseaudiosink_base_init), (gst_baseaudiosink_class_init),
|
||||||
|
(gst_baseaudiosink_init), (gst_baseaudiosink_set_property),
|
||||||
|
(gst_baseaudiosink_get_property), (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_class_init), (gst_ringbuffer_init),
|
||||||
|
(gst_ringbuffer_dispose), (gst_ringbuffer_finalize),
|
||||||
|
(gst_ringbuffer_set_callback), (gst_ringbuffer_acquire),
|
||||||
|
(gst_ringbuffer_release), (gst_ringbuffer_play_unlocked),
|
||||||
|
(gst_ringbuffer_play), (gst_ringbuffer_pause),
|
||||||
|
(gst_ringbuffer_resume), (gst_ringbuffer_stop),
|
||||||
|
(gst_ringbuffer_callback), (gst_ringbuffer_delay),
|
||||||
|
(gst_ringbuffer_played_samples), (gst_ringbuffer_commit),
|
||||||
|
(gst_ringbuffer_prepare_read), (gst_ringbuffer_clear):
|
||||||
|
* gst-libs/gst/audio/gstringbuffer.h:
|
||||||
|
An attempt at a set of audio base classes together with some
|
||||||
|
design docs.
|
||||||
|
|
||||||
2005-04-20 Wim Taymans <wim@fluendo.com>
|
2005-04-20 Wim Taymans <wim@fluendo.com>
|
||||||
|
|
||||||
* gst/audioconvert/Makefile.am:
|
* gst/audioconvert/Makefile.am:
|
||||||
|
|
137
docs/design-audiosinks.txt
Normal file
137
docs/design-audiosinks.txt
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
Audiosink design
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
|
||||||
|
- must operate chain based.
|
||||||
|
Most simple playback pipelines will push audio from the decoders
|
||||||
|
into the audio sink.
|
||||||
|
|
||||||
|
- must operate getrange based
|
||||||
|
Most professional audio applications will operate in a mode where
|
||||||
|
the audio sink pulls samples from the pipeline. This is typically
|
||||||
|
done in a callback from the audiosink requesting N samples. The
|
||||||
|
callback is either scheduled from a thread or from an interrupt
|
||||||
|
from the audio hardware device.
|
||||||
|
|
||||||
|
- Exact sample accurate clocks.
|
||||||
|
the audiosink must be able to provide a clock that is sample
|
||||||
|
accurate even if samples are dropped or when discontinuities are
|
||||||
|
found in the stream.
|
||||||
|
|
||||||
|
- Exact timing of playback.
|
||||||
|
The audiosink must be able to play samples at their exact times.
|
||||||
|
|
||||||
|
- use DMA access when possible.
|
||||||
|
When the hardware can do DMA we should use it. This should also
|
||||||
|
work over bufferpools to avoid data copying to/from kernel space.
|
||||||
|
|
||||||
|
|
||||||
|
Design:
|
||||||
|
|
||||||
|
The design is based on a set of base classes and the concept of a
|
||||||
|
ringbuffer of samples.
|
||||||
|
|
||||||
|
+-----------+ - provide preroll, rendering, timing
|
||||||
|
+ basesink + - caps nego
|
||||||
|
+-----+-----+
|
||||||
|
|
|
||||||
|
+-----V----------+ - manages ringbuffer
|
||||||
|
+ baseaudiosink + - manages scheduling (push/pull)
|
||||||
|
+-----+----------+ - manages clock/query/seek
|
||||||
|
| - manages scheduling of samples in the ringbuffer
|
||||||
|
| - manages caps parsing
|
||||||
|
|
|
||||||
|
+-----V------+ - default ringbuffer implementation with a GThread
|
||||||
|
+ audiosink + - subclasses provide open/read/close methods
|
||||||
|
+------------+
|
||||||
|
|
||||||
|
The ringbuffer is a contiguous piece of memory divided into segtotal
|
||||||
|
pieces of segments. Each segment has segsize bytes.
|
||||||
|
|
||||||
|
play position write position
|
||||||
|
v v
|
||||||
|
+---+---+---+-------------------------------------+----------+
|
||||||
|
+ 0 | 1 | 2 | .... | segtotal |
|
||||||
|
+---+---+---+-------------------------------------+----------+
|
||||||
|
<--->
|
||||||
|
segsize bytes = N samples * bytes_per_sample.
|
||||||
|
|
||||||
|
|
||||||
|
The ringbuffer has a play and write position, which is expressed in
|
||||||
|
segments. The play position is where the device is currently reading
|
||||||
|
samples and the write position is where new samples can be written
|
||||||
|
into the buffer.
|
||||||
|
|
||||||
|
The latency of the ringbuffer is the distance between the play and
|
||||||
|
write position. The lowest latency is the size of a segment, thus
|
||||||
|
smaller segment sizes allow for lower latency.
|
||||||
|
|
||||||
|
The ringbuffer can be put to the PLAYING or STOPPED state.
|
||||||
|
|
||||||
|
In the STOPPED state no samples are played to the device and the play
|
||||||
|
pointer does not advance.
|
||||||
|
|
||||||
|
In the PLAYING state samples are written to the device and the ringbuffer
|
||||||
|
should call a configurable callback after each segment is written to the
|
||||||
|
device. In this state the play pointer is advanced after each segment is
|
||||||
|
written.
|
||||||
|
|
||||||
|
A write operation to the ringbuffer will put new samples in the ringbuffer.
|
||||||
|
If there is not enough space in the ringbuffer, the write operation will
|
||||||
|
block. The playback of the buffer never stops, even if the buffer is
|
||||||
|
empty. When the buffer is empty, silence is played by the device.
|
||||||
|
|
||||||
|
The ringbuffer is implemented with lockfree atomic operations, especially
|
||||||
|
on the reading side so that low-latency operations are possible.
|
||||||
|
|
||||||
|
|
||||||
|
Scheduling:
|
||||||
|
|
||||||
|
- chain based mode:
|
||||||
|
|
||||||
|
In chain based mode, bytes are written into the ringbuffer. This operation
|
||||||
|
will eventually block when the ringbuffer is filled.
|
||||||
|
|
||||||
|
When no samples arrive in time, the ringbuffer will play silence. Each
|
||||||
|
buffer that arrives will be placed into the ringbuffer at the correct
|
||||||
|
times. This means that dropping samples or inserting silence is done
|
||||||
|
automatically and very accurate and independend of the play pointer.
|
||||||
|
|
||||||
|
In this mode, the ringbuffer is usually kept as full as possible. When
|
||||||
|
using a small buffer (small segsize and segtotal), the latency for audio
|
||||||
|
to start from the sink to when it is played can be kept low but at least
|
||||||
|
one context switch has to be made between read and write.
|
||||||
|
|
||||||
|
- getrange based mode
|
||||||
|
|
||||||
|
In getrange based mode, the baseaudiosink will use the callback function
|
||||||
|
of the ringbuffer to get a segsize samples from the peer element. These
|
||||||
|
samples will then be placed in the ringbuffer at the next play position.
|
||||||
|
It is assumed that the getrange function returns fast enough to fill the
|
||||||
|
ringbuffer before the play pointer reaches the write pointer.
|
||||||
|
|
||||||
|
In this mode, the ringbuffer is usually kept as empty as possible. There
|
||||||
|
is no context switch needed between the elements that create the samples
|
||||||
|
and the actual writing of the samples to the device.
|
||||||
|
|
||||||
|
|
||||||
|
DMA mode:
|
||||||
|
|
||||||
|
- Elements that can do DMA based access to the audio device have to subclass
|
||||||
|
from the GstBaseAudioSink class and wrap the DMA ringbuffer in a subclass
|
||||||
|
of GstRingBuffer.
|
||||||
|
|
||||||
|
The ringbuffer subclass should trigger a callback after writing or playing
|
||||||
|
each sample to the device. This callback can be triggered from a thread or
|
||||||
|
from a signal from the audio device.
|
||||||
|
|
||||||
|
|
||||||
|
Clocks:
|
||||||
|
|
||||||
|
The GstBaseAudioSink class will use the ringbuffer to act as a clock provider.
|
||||||
|
It can do this by using the play pointer and the delay to calculate the
|
||||||
|
clock time.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,10 @@ CLEANFILES = gstaudiofilterexample.c \
|
||||||
$(BUILT_SOURCES)
|
$(BUILT_SOURCES)
|
||||||
|
|
||||||
libgstaudio_la_SOURCES = audio.c audioclock.c \
|
libgstaudio_la_SOURCES = audio.c audioclock.c \
|
||||||
multichannel.c
|
multichannel.c \
|
||||||
|
gstaudiosink.c \
|
||||||
|
gstbaseaudiosink.c \
|
||||||
|
gstringbuffer.c
|
||||||
nodist_libgstaudio_la_SOURCES = $(built_sources)
|
nodist_libgstaudio_la_SOURCES = $(built_sources)
|
||||||
|
|
||||||
libgstaudioincludedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/audio
|
libgstaudioincludedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/audio
|
||||||
|
@ -25,14 +28,15 @@ libgstaudioinclude_HEADERS = \
|
||||||
audio.h \
|
audio.h \
|
||||||
audioclock.h \
|
audioclock.h \
|
||||||
gstaudiofilter.h \
|
gstaudiofilter.h \
|
||||||
multichannel.h
|
gstaudiosink.h \
|
||||||
|
gstbaseaudiosink.h \
|
||||||
nodist_libgstaudioinclude_HEADERS = \
|
gstringbuffer.h \
|
||||||
|
multichannel.h \
|
||||||
multichannel-enumtypes.h
|
multichannel-enumtypes.h
|
||||||
|
|
||||||
libgstaudio_la_LIBADD =
|
libgstaudio_la_LIBADD =
|
||||||
libgstaudio_la_CFLAGS = $(GST_CFLAGS)
|
libgstaudio_la_CFLAGS = $(GST_CFLAGS)
|
||||||
libgstaudio_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
libgstaudio_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GST_BASE_LIBS)
|
||||||
|
|
||||||
libgstaudiofilter_la_SOURCES = gstaudiofilter.c gstaudiofilter.h
|
libgstaudiofilter_la_SOURCES = gstaudiofilter.c gstaudiofilter.h
|
||||||
libgstaudiofilter_la_CFLAGS = $(GST_CFLAGS)
|
libgstaudiofilter_la_CFLAGS = $(GST_CFLAGS)
|
||||||
|
@ -45,4 +49,9 @@ libgstaudiofilterexample_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||||
gstaudiofilterexample.c: $(srcdir)/make_filter $(srcdir)/gstaudiofiltertemplate.c
|
gstaudiofilterexample.c: $(srcdir)/make_filter $(srcdir)/gstaudiofiltertemplate.c
|
||||||
$(srcdir)/make_filter AudiofilterExample $(srcdir)/gstaudiofiltertemplate.c
|
$(srcdir)/make_filter AudiofilterExample $(srcdir)/gstaudiofiltertemplate.c
|
||||||
|
|
||||||
|
noinst_PROGRAMS = testchannels
|
||||||
|
testchannels_SOURCES = testchannels.c
|
||||||
|
testchannels_CFLAGS = $(GST_CFLAGS)
|
||||||
|
testchannels_LDFLAGS = $(GST_LIBS)
|
||||||
|
|
||||||
include $(top_srcdir)/common/glib-gen.mak
|
include $(top_srcdir)/common/glib-gen.mak
|
||||||
|
|
13
gst-libs/gst/audio/TODO
Normal file
13
gst-libs/gst/audio/TODO
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
TODO
|
||||||
|
----
|
||||||
|
|
||||||
|
- audio base classes:
|
||||||
|
- GstBaseAudioSink
|
||||||
|
- parse caps into rinbuffer spec, also mase sure surround sound
|
||||||
|
is parsed correctly.
|
||||||
|
- implement seek/query/convert
|
||||||
|
- implement clocks
|
||||||
|
- implement getrange scheduling
|
||||||
|
- GstRingBuffer
|
||||||
|
- copy samples to right position in ringbuffer
|
||||||
|
|
404
gst-libs/gst/audio/gstaudiosink.c
Normal file
404
gst-libs/gst/audio/gstaudiosink.c
Normal file
|
@ -0,0 +1,404 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
|
||||||
|
* 2005 Wim Taymans <wim@fluendo.com>
|
||||||
|
*
|
||||||
|
* gstaudiosink.c: simple audio sink base class
|
||||||
|
*
|
||||||
|
* 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 "gstaudiosink.h"
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_STATIC (gst_audiosink_debug);
|
||||||
|
#define GST_CAT_DEFAULT gst_audiosink_debug
|
||||||
|
|
||||||
|
#define GST_TYPE_AUDIORINGBUFFER \
|
||||||
|
(gst_audioringbuffer_get_type())
|
||||||
|
#define GST_AUDIORINGBUFFER(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIORINGBUFFER,GstAudioRingBuffer))
|
||||||
|
#define GST_AUDIORINGBUFFER_CLASS(klass) \
|
||||||
|
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIORINGBUFFER,GstAudioRingBufferClass))
|
||||||
|
#define GST_AUDIORINGBUFFER_GET_CLASS(obj) \
|
||||||
|
(G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_AUDIORINGBUFFER, GstAudioRingBufferClass))
|
||||||
|
#define GST_IS_AUDIORINGBUFFER(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIORINGBUFFER))
|
||||||
|
#define GST_IS_AUDIORINGBUFFER_CLASS(obj)\
|
||||||
|
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIORINGBUFFER))
|
||||||
|
|
||||||
|
typedef struct _GstAudioRingBuffer GstAudioRingBuffer;
|
||||||
|
typedef struct _GstAudioRingBufferClass GstAudioRingBufferClass;
|
||||||
|
|
||||||
|
#define GST_AUDIORINGBUFFER_GET_COND(buf) (((GstAudioRingBuffer *)buf)->cond)
|
||||||
|
#define GST_AUDIORINGBUFFER_WAIT(buf) (g_cond_wait (GST_AUDIORINGBUFFER_GET_COND (buf), GST_GET_LOCK (buf)))
|
||||||
|
#define GST_AUDIORINGBUFFER_SIGNAL(buf) (g_cond_signal (GST_AUDIORINGBUFFER_GET_COND (buf)))
|
||||||
|
#define GST_AUDIORINGBUFFER_BROADCAST(buf)(g_cond_broadcast (GST_AUDIORINGBUFFER_GET_COND (buf)))
|
||||||
|
|
||||||
|
struct _GstAudioRingBuffer
|
||||||
|
{
|
||||||
|
GstRingBuffer object;
|
||||||
|
|
||||||
|
gboolean running;
|
||||||
|
gint queuedseg;
|
||||||
|
|
||||||
|
GCond *cond;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GstAudioRingBufferClass
|
||||||
|
{
|
||||||
|
GstRingBufferClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void gst_audioringbuffer_class_init (GstAudioRingBufferClass * klass);
|
||||||
|
static void gst_audioringbuffer_init (GstAudioRingBuffer * ringbuffer);
|
||||||
|
static void gst_audioringbuffer_dispose (GObject * object);
|
||||||
|
static void gst_audioringbuffer_finalize (GObject * object);
|
||||||
|
|
||||||
|
static GstRingBufferClass *ring_parent_class = NULL;
|
||||||
|
|
||||||
|
static gboolean gst_audioringbuffer_acquire (GstRingBuffer * buf,
|
||||||
|
GstRingBufferSpec * spec);
|
||||||
|
static gboolean gst_audioringbuffer_release (GstRingBuffer * buf);
|
||||||
|
static gboolean gst_audioringbuffer_play (GstRingBuffer * buf);
|
||||||
|
static gboolean gst_audioringbuffer_stop (GstRingBuffer * buf);
|
||||||
|
static guint gst_audioringbuffer_delay (GstRingBuffer * buf);
|
||||||
|
|
||||||
|
/* ringbuffer abstract base class */
|
||||||
|
GType
|
||||||
|
gst_audioringbuffer_get_type (void)
|
||||||
|
{
|
||||||
|
static GType ringbuffer_type = 0;
|
||||||
|
|
||||||
|
if (!ringbuffer_type) {
|
||||||
|
static const GTypeInfo ringbuffer_info = {
|
||||||
|
sizeof (GstAudioRingBufferClass),
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
(GClassInitFunc) gst_audioringbuffer_class_init,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
sizeof (GstAudioRingBuffer),
|
||||||
|
0,
|
||||||
|
(GInstanceInitFunc) gst_audioringbuffer_init,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
ringbuffer_type =
|
||||||
|
g_type_register_static (GST_TYPE_RINGBUFFER, "GstAudioRingBuffer",
|
||||||
|
&ringbuffer_info, 0);
|
||||||
|
}
|
||||||
|
return ringbuffer_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_audioringbuffer_class_init (GstAudioRingBufferClass * klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class;
|
||||||
|
GstObjectClass *gstobject_class;
|
||||||
|
GstRingBufferClass *gstringbuffer_class;
|
||||||
|
|
||||||
|
gobject_class = (GObjectClass *) klass;
|
||||||
|
gstobject_class = (GstObjectClass *) klass;
|
||||||
|
gstringbuffer_class = (GstRingBufferClass *) klass;
|
||||||
|
|
||||||
|
ring_parent_class = g_type_class_ref (GST_TYPE_RINGBUFFER);
|
||||||
|
|
||||||
|
gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_audioringbuffer_dispose);
|
||||||
|
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_audioringbuffer_finalize);
|
||||||
|
|
||||||
|
gstringbuffer_class->acquire =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_audioringbuffer_acquire);
|
||||||
|
gstringbuffer_class->release =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_audioringbuffer_release);
|
||||||
|
gstringbuffer_class->play = GST_DEBUG_FUNCPTR (gst_audioringbuffer_play);
|
||||||
|
gstringbuffer_class->stop = GST_DEBUG_FUNCPTR (gst_audioringbuffer_stop);
|
||||||
|
|
||||||
|
gstringbuffer_class->delay = GST_DEBUG_FUNCPTR (gst_audioringbuffer_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef guint (*WriteFunc) (GstAudioSink * sink, gpointer data, guint length);
|
||||||
|
|
||||||
|
/* this internal thread does nothing else but write samples to the audio device.
|
||||||
|
* It will write each segment in the ringbuffer and will update the play
|
||||||
|
* pointer.
|
||||||
|
* The play/stop methods control the thread.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
audioringbuffer_thread_func (GstRingBuffer * buf)
|
||||||
|
{
|
||||||
|
GstAudioSink *sink;
|
||||||
|
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);
|
||||||
|
|
||||||
|
GST_DEBUG ("enter thread");
|
||||||
|
|
||||||
|
writefunc = csink->write;
|
||||||
|
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;
|
||||||
|
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);
|
||||||
|
|
||||||
|
do {
|
||||||
|
written = writefunc (sink, readptr + written, to_write);
|
||||||
|
if (written < 0 || written > to_write) {
|
||||||
|
perror ("error writing data\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
to_write -= written;
|
||||||
|
} while (to_write > 0);
|
||||||
|
|
||||||
|
/* clear written samples */
|
||||||
|
gst_ringbuffer_clear (buf, readseg);
|
||||||
|
|
||||||
|
/* we wrote one segment */
|
||||||
|
gst_ringbuffer_callback (buf, 1);
|
||||||
|
} else {
|
||||||
|
GST_LOCK (abuf);
|
||||||
|
GST_DEBUG ("signal wait");
|
||||||
|
GST_AUDIORINGBUFFER_SIGNAL (buf);
|
||||||
|
GST_DEBUG ("wait for play");
|
||||||
|
GST_AUDIORINGBUFFER_WAIT (buf);
|
||||||
|
GST_DEBUG ("got signal");
|
||||||
|
if (!abuf->running) {
|
||||||
|
GST_UNLOCK (abuf);
|
||||||
|
GST_DEBUG ("stop running");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
GST_UNLOCK (abuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
GST_DEBUG ("exit thread");
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* ERROR */
|
||||||
|
no_function:
|
||||||
|
{
|
||||||
|
GST_DEBUG ("no write function, exit thread");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_audioringbuffer_init (GstAudioRingBuffer * ringbuffer)
|
||||||
|
{
|
||||||
|
ringbuffer->running = TRUE;
|
||||||
|
ringbuffer->queuedseg = 0;
|
||||||
|
|
||||||
|
ringbuffer->cond = g_cond_new ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_audioringbuffer_dispose (GObject * object)
|
||||||
|
{
|
||||||
|
G_OBJECT_CLASS (ring_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_audioringbuffer_finalize (GObject * object)
|
||||||
|
{
|
||||||
|
G_OBJECT_CLASS (ring_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_audioringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
|
||||||
|
{
|
||||||
|
GstAudioSink *sink;
|
||||||
|
GstAudioSinkClass *csink;
|
||||||
|
gboolean result = FALSE;
|
||||||
|
|
||||||
|
sink = GST_AUDIOSINK (GST_OBJECT_PARENT (buf));
|
||||||
|
csink = GST_AUDIOSINK_GET_CLASS (sink);
|
||||||
|
|
||||||
|
if (csink->open)
|
||||||
|
result = csink->open (sink, spec);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
goto could_not_open;
|
||||||
|
|
||||||
|
/* allocate one more segment as we need some headroom */
|
||||||
|
spec->segtotal++;
|
||||||
|
|
||||||
|
buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize);
|
||||||
|
memset (GST_BUFFER_DATA (buf), 0, GST_BUFFER_SIZE (buf));
|
||||||
|
|
||||||
|
sink->thread =
|
||||||
|
g_thread_create ((GThreadFunc) audioringbuffer_thread_func, buf, TRUE,
|
||||||
|
NULL);
|
||||||
|
GST_AUDIORINGBUFFER_WAIT (buf);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
could_not_open:
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* function is called with LOCK */
|
||||||
|
static gboolean
|
||||||
|
gst_audioringbuffer_release (GstRingBuffer * buf)
|
||||||
|
{
|
||||||
|
GstAudioSink *sink;
|
||||||
|
GstAudioSinkClass *csink;
|
||||||
|
GstAudioRingBuffer *abuf;
|
||||||
|
gboolean result = FALSE;
|
||||||
|
|
||||||
|
sink = GST_AUDIOSINK (GST_OBJECT_PARENT (buf));
|
||||||
|
csink = GST_AUDIOSINK_GET_CLASS (sink);
|
||||||
|
abuf = GST_AUDIORINGBUFFER (buf);
|
||||||
|
|
||||||
|
abuf->running = FALSE;
|
||||||
|
GST_AUDIORINGBUFFER_SIGNAL (buf);
|
||||||
|
GST_UNLOCK (buf);
|
||||||
|
|
||||||
|
/* join the thread */
|
||||||
|
g_thread_join (sink->thread);
|
||||||
|
|
||||||
|
GST_LOCK (buf);
|
||||||
|
|
||||||
|
if (csink->close)
|
||||||
|
result = csink->close (sink);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_audioringbuffer_play (GstRingBuffer * buf)
|
||||||
|
{
|
||||||
|
GstAudioSink *sink;
|
||||||
|
|
||||||
|
sink = GST_AUDIOSINK (GST_OBJECT_PARENT (buf));
|
||||||
|
|
||||||
|
GST_DEBUG ("play");
|
||||||
|
GST_AUDIORINGBUFFER_SIGNAL (buf);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_audioringbuffer_stop (GstRingBuffer * buf)
|
||||||
|
{
|
||||||
|
GstAudioSink *sink;
|
||||||
|
GstAudioSinkClass *csink;
|
||||||
|
|
||||||
|
sink = GST_AUDIOSINK (GST_OBJECT_PARENT (buf));
|
||||||
|
csink = GST_AUDIOSINK_GET_CLASS (sink);
|
||||||
|
|
||||||
|
/* unblock any pending writes to the audio device */
|
||||||
|
if (csink->reset)
|
||||||
|
csink->reset (sink);
|
||||||
|
|
||||||
|
GST_DEBUG ("stop");
|
||||||
|
GST_AUDIORINGBUFFER_WAIT (buf);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint
|
||||||
|
gst_audioringbuffer_delay (GstRingBuffer * buf)
|
||||||
|
{
|
||||||
|
GstAudioSink *sink;
|
||||||
|
GstAudioSinkClass *csink;
|
||||||
|
guint res = 0;
|
||||||
|
|
||||||
|
sink = GST_AUDIOSINK (GST_OBJECT_PARENT (buf));
|
||||||
|
csink = GST_AUDIOSINK_GET_CLASS (sink);
|
||||||
|
|
||||||
|
if (csink->delay)
|
||||||
|
res = csink->delay (sink);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AudioSink signals and args */
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
/* FILL ME */
|
||||||
|
LAST_SIGNAL
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
ARG_0,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define _do_init(bla) \
|
||||||
|
GST_DEBUG_CATEGORY_INIT (gst_audiosink_debug, "audiosink", 0, "audiosink element");
|
||||||
|
|
||||||
|
GST_BOILERPLATE_FULL (GstAudioSink, gst_audiosink, GstBaseAudioSink,
|
||||||
|
GST_TYPE_BASEAUDIOSINK, _do_init);
|
||||||
|
|
||||||
|
static GstRingBuffer *gst_audiosink_create_ringbuffer (GstBaseAudioSink * sink);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_audiosink_base_init (gpointer g_class)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_audiosink_class_init (GstAudioSinkClass * klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class;
|
||||||
|
GstElementClass *gstelement_class;
|
||||||
|
GstBaseSinkClass *gstbasesink_class;
|
||||||
|
GstBaseAudioSinkClass *gstbaseaudiosink_class;
|
||||||
|
|
||||||
|
gobject_class = (GObjectClass *) klass;
|
||||||
|
gstelement_class = (GstElementClass *) klass;
|
||||||
|
gstbasesink_class = (GstBaseSinkClass *) klass;
|
||||||
|
gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass;
|
||||||
|
|
||||||
|
gstbaseaudiosink_class->create_ringbuffer =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_audiosink_create_ringbuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_audiosink_init (GstAudioSink * audiosink)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstRingBuffer *
|
||||||
|
gst_audiosink_create_ringbuffer (GstBaseAudioSink * sink)
|
||||||
|
{
|
||||||
|
GstRingBuffer *buffer;
|
||||||
|
|
||||||
|
buffer = g_object_new (GST_TYPE_AUDIORINGBUFFER, NULL);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
85
gst-libs/gst/audio/gstaudiosink.h
Normal file
85
gst-libs/gst/audio/gstaudiosink.h
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
|
||||||
|
* 2005 Wim Taymans <wim@fluendo.com>
|
||||||
|
*
|
||||||
|
* gstaudiosink.h:
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* a base class for simple audio sinks.
|
||||||
|
*
|
||||||
|
* This base class only requires subclasses to implement a set
|
||||||
|
* of simple functions.
|
||||||
|
*
|
||||||
|
* - open: open the device with the specified caps
|
||||||
|
* - write: write the samples to the audio device
|
||||||
|
* - close: close the device
|
||||||
|
* - delay: the number of samples queued in the device
|
||||||
|
* - reset: unblock a write to the device and reset.
|
||||||
|
*
|
||||||
|
* All scheduling of samples and timestamps is done in this
|
||||||
|
* base class together with the GstBaseAudioSink using a
|
||||||
|
* default implementation of a ringbuffer that uses threads.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GST_AUDIOSINK_H__
|
||||||
|
#define __GST_AUDIOSINK_H__
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include "gstbaseaudiosink.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define GST_TYPE_AUDIOSINK (gst_audiosink_get_type())
|
||||||
|
#define GST_AUDIOSINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIOSINK,GstAudioSink))
|
||||||
|
#define GST_AUDIOSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIOSINK,GstAudioSinkClass))
|
||||||
|
#define GST_AUDIOSINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_AUDIOSINK,GstAudioSinkClass))
|
||||||
|
#define GST_IS_AUDIOSINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIOSINK))
|
||||||
|
#define GST_IS_AUDIOSINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIOSINK))
|
||||||
|
|
||||||
|
typedef struct _GstAudioSink GstAudioSink;
|
||||||
|
typedef struct _GstAudioSinkClass GstAudioSinkClass;
|
||||||
|
|
||||||
|
struct _GstAudioSink {
|
||||||
|
GstBaseAudioSink element;
|
||||||
|
|
||||||
|
/*< private >*/ /* with LOCK */
|
||||||
|
GThread *thread;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GstAudioSinkClass {
|
||||||
|
GstBaseAudioSinkClass parent_class;
|
||||||
|
|
||||||
|
/* vtable */
|
||||||
|
|
||||||
|
/* open the device with given specs */
|
||||||
|
gboolean (*open) (GstAudioSink *sink, GstRingBufferSpec *spec);
|
||||||
|
/* close the device */
|
||||||
|
gboolean (*close) (GstAudioSink *sink);
|
||||||
|
/* write samples to the device */
|
||||||
|
guint (*write) (GstAudioSink *sink, gpointer data, guint length);
|
||||||
|
/* get number of samples queued in the device */
|
||||||
|
guint (*delay) (GstAudioSink *sink);
|
||||||
|
/* reset the audio device, unblock from a write */
|
||||||
|
void (*reset) (GstAudioSink *sink);
|
||||||
|
};
|
||||||
|
|
||||||
|
GType gst_audiosink_get_type(void);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GST_AUDIOSINK_H__ */
|
267
gst-libs/gst/audio/gstbaseaudiosink.c
Normal file
267
gst-libs/gst/audio/gstbaseaudiosink.c
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
|
||||||
|
* 2005 Wim Taymans <wim@fluendo.com>
|
||||||
|
*
|
||||||
|
* gstbaseaudiosink.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 "gstbaseaudiosink.h"
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_STATIC (gst_baseaudiosink_debug);
|
||||||
|
#define GST_CAT_DEFAULT gst_baseaudiosink_debug
|
||||||
|
|
||||||
|
/* BaseAudioSink signals and args */
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
/* FILL ME */
|
||||||
|
LAST_SIGNAL
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DEFAULT_BUFFER -1
|
||||||
|
#define DEFAULT_LATENCY -1
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_0,
|
||||||
|
PROP_BUFFER,
|
||||||
|
PROP_LATENCY,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define _do_init(bla) \
|
||||||
|
GST_DEBUG_CATEGORY_INIT (gst_baseaudiosink_debug, "baseaudiosink", 0, "baseaudiosink element");
|
||||||
|
|
||||||
|
GST_BOILERPLATE_FULL (GstBaseAudioSink, gst_baseaudiosink, GstBaseSink,
|
||||||
|
GST_TYPE_BASESINK, _do_init);
|
||||||
|
|
||||||
|
static void gst_baseaudiosink_set_property (GObject * object, guint prop_id,
|
||||||
|
const GValue * value, GParamSpec * pspec);
|
||||||
|
static void gst_baseaudiosink_get_property (GObject * object, guint prop_id,
|
||||||
|
GValue * value, GParamSpec * pspec);
|
||||||
|
|
||||||
|
static GstElementStateReturn gst_baseaudiosink_change_state (GstElement *
|
||||||
|
element);
|
||||||
|
|
||||||
|
static GstFlowReturn gst_baseaudiosink_preroll (GstBaseSink * bsink,
|
||||||
|
GstBuffer * buffer);
|
||||||
|
static GstFlowReturn gst_baseaudiosink_render (GstBaseSink * bsink,
|
||||||
|
GstBuffer * buffer);
|
||||||
|
static void gst_baseaudiosink_event (GstBaseSink * bsink, GstEvent * event);
|
||||||
|
static void gst_baseaudiosink_get_times (GstBaseSink * bsink,
|
||||||
|
GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
|
||||||
|
static gboolean gst_baseaudiosink_setcaps (GstBaseSink * bsink, GstCaps * caps);
|
||||||
|
|
||||||
|
//static guint gst_baseaudiosink_signals[LAST_SIGNAL] = { 0 };
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_baseaudiosink_base_init (gpointer g_class)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_baseaudiosink_class_init (GstBaseAudioSinkClass * klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class;
|
||||||
|
GstElementClass *gstelement_class;
|
||||||
|
GstBaseSinkClass *gstbasesink_class;
|
||||||
|
|
||||||
|
gobject_class = (GObjectClass *) klass;
|
||||||
|
gstelement_class = (GstElementClass *) klass;
|
||||||
|
gstbasesink_class = (GstBaseSinkClass *) klass;
|
||||||
|
|
||||||
|
gobject_class->set_property =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_baseaudiosink_set_property);
|
||||||
|
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));
|
||||||
|
|
||||||
|
gstelement_class->change_state =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_baseaudiosink_change_state);
|
||||||
|
|
||||||
|
gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_baseaudiosink_event);
|
||||||
|
gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_baseaudiosink_preroll);
|
||||||
|
gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_baseaudiosink_render);
|
||||||
|
gstbasesink_class->get_times =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_baseaudiosink_get_times);
|
||||||
|
gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_baseaudiosink_setcaps);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_baseaudiosink_init (GstBaseAudioSink * baseaudiosink)
|
||||||
|
{
|
||||||
|
baseaudiosink->buffer = DEFAULT_BUFFER;
|
||||||
|
baseaudiosink->latency = DEFAULT_LATENCY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_baseaudiosink_set_property (GObject * object, guint prop_id,
|
||||||
|
const GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstBaseAudioSink *sink;
|
||||||
|
|
||||||
|
sink = GST_BASEAUDIOSINK (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_BUFFER:
|
||||||
|
break;
|
||||||
|
case PROP_LATENCY:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_baseaudiosink_get_property (GObject * object, guint prop_id, GValue * value,
|
||||||
|
GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstBaseAudioSink *sink;
|
||||||
|
|
||||||
|
sink = GST_BASEAUDIOSINK (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_BUFFER:
|
||||||
|
break;
|
||||||
|
case PROP_LATENCY:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_baseaudiosink_setcaps (GstBaseSink * bsink, GstCaps * caps)
|
||||||
|
{
|
||||||
|
GstBaseAudioSink *sink = GST_BASEAUDIOSINK (bsink);
|
||||||
|
GstRingBufferSpec *spec;
|
||||||
|
|
||||||
|
spec = &sink->ringbuffer->spec;
|
||||||
|
|
||||||
|
gst_caps_replace (&spec->caps, caps);
|
||||||
|
spec->buffersize = sink->buffer;
|
||||||
|
spec->latency = sink->latency;
|
||||||
|
|
||||||
|
spec->segtotal = 0x7fff;
|
||||||
|
spec->segsize = 0x2048;
|
||||||
|
|
||||||
|
gst_ringbuffer_release (sink->ringbuffer);
|
||||||
|
gst_ringbuffer_acquire (sink->ringbuffer, spec);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_baseaudiosink_get_times (GstBaseSink * bsink, GstBuffer * buffer,
|
||||||
|
GstClockTime * start, GstClockTime * end)
|
||||||
|
{
|
||||||
|
*start = GST_CLOCK_TIME_NONE;
|
||||||
|
*end = GST_CLOCK_TIME_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_baseaudiosink_event (GstBaseSink * bsink, GstEvent * event)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_baseaudiosink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
|
||||||
|
{
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_baseaudiosink_render (GstBaseSink * bsink, GstBuffer * buf)
|
||||||
|
{
|
||||||
|
GstBaseAudioSink *sink = GST_BASEAUDIOSINK (bsink);
|
||||||
|
|
||||||
|
gst_ringbuffer_commit (sink->ringbuffer, 0,
|
||||||
|
GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
|
||||||
|
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
GstRingBuffer *
|
||||||
|
gst_baseaudiosink_create_ringbuffer (GstBaseAudioSink * sink)
|
||||||
|
{
|
||||||
|
GstBaseAudioSinkClass *bclass;
|
||||||
|
GstRingBuffer *buffer = NULL;
|
||||||
|
|
||||||
|
bclass = GST_BASEAUDIOSINK_GET_CLASS (sink);
|
||||||
|
if (bclass->create_ringbuffer)
|
||||||
|
buffer = bclass->create_ringbuffer (sink);
|
||||||
|
|
||||||
|
if (buffer) {
|
||||||
|
gst_object_set_parent (GST_OBJECT (buffer), GST_OBJECT (sink));
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_baseaudiosink_callback (GstRingBuffer * rbuf, guint advance, gpointer data)
|
||||||
|
{
|
||||||
|
//GstBaseAudioSink *sink = GST_BASEAUDIOSINK (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstElementStateReturn
|
||||||
|
gst_baseaudiosink_change_state (GstElement * element)
|
||||||
|
{
|
||||||
|
GstElementStateReturn ret = GST_STATE_SUCCESS;
|
||||||
|
GstBaseAudioSink *sink = GST_BASEAUDIOSINK (element);
|
||||||
|
GstElementState transition = GST_STATE_TRANSITION (element);
|
||||||
|
|
||||||
|
switch (transition) {
|
||||||
|
case GST_STATE_NULL_TO_READY:
|
||||||
|
break;
|
||||||
|
case GST_STATE_READY_TO_PAUSED:
|
||||||
|
sink->ringbuffer = gst_baseaudiosink_create_ringbuffer (sink);
|
||||||
|
gst_ringbuffer_set_callback (sink->ringbuffer, gst_baseaudiosink_callback,
|
||||||
|
sink);
|
||||||
|
break;
|
||||||
|
case GST_STATE_PAUSED_TO_PLAYING:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
|
||||||
|
|
||||||
|
switch (transition) {
|
||||||
|
case GST_STATE_PLAYING_TO_PAUSED:
|
||||||
|
gst_ringbuffer_stop (sink->ringbuffer);
|
||||||
|
break;
|
||||||
|
case GST_STATE_PAUSED_TO_READY:
|
||||||
|
gst_ringbuffer_release (sink->ringbuffer);
|
||||||
|
gst_object_unref (GST_OBJECT (sink->ringbuffer));
|
||||||
|
break;
|
||||||
|
case GST_STATE_READY_TO_NULL:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
95
gst-libs/gst/audio/gstbaseaudiosink.h
Normal file
95
gst-libs/gst/audio/gstbaseaudiosink.h
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
|
||||||
|
* 2005 Wim Taymans <wim@fluendo.com>
|
||||||
|
*
|
||||||
|
* gstbaseaudiosink.h:
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* a base class for audio sinks.
|
||||||
|
*
|
||||||
|
* It uses a ringbuffer to schedule playback of samples. This makes
|
||||||
|
* it very easy to drop or insert samples to align incomming
|
||||||
|
* buffers to the exact playback timestamp.
|
||||||
|
*
|
||||||
|
* Subclasses must provide a ringbuffer pointing to either DMA
|
||||||
|
* memory or regular memory. A subclass should also call a callback
|
||||||
|
* function when it has played N segments in the buffer. The subclass
|
||||||
|
* is free to use a thread to signal this callback, use EIO or any
|
||||||
|
* other mechanism.
|
||||||
|
*
|
||||||
|
* The base class is able to operate in push or pull mode. The chain
|
||||||
|
* mode will queue the samples in the ringbuffer as much as possible.
|
||||||
|
* The available space is calculated in the callback function.
|
||||||
|
*
|
||||||
|
* The pull mode will pull_range() a new buffer of N samples with a
|
||||||
|
* configurable latency. This allows for high-end real time
|
||||||
|
* audio processing pipelines driven by the audiosink. The callback
|
||||||
|
* function will be used to perform a pull_range() on the sinkpad.
|
||||||
|
* The thread scheduling the callback can be a real-time thread.
|
||||||
|
*
|
||||||
|
* Subclasses must implement a GstRingBuffer in addition to overriding
|
||||||
|
* the methods in GstBaseSink and this class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GST_BASEAUDIOSINK_H__
|
||||||
|
#define __GST_BASEAUDIOSINK_H__
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <gst/base/gstbasesink.h>
|
||||||
|
#include "gstringbuffer.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define GST_TYPE_BASEAUDIOSINK (gst_baseaudiosink_get_type())
|
||||||
|
#define GST_BASEAUDIOSINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASEAUDIOSINK,GstBaseAudioSink))
|
||||||
|
#define GST_BASEAUDIOSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASEAUDIOSINK,GstBaseAudioSinkClass))
|
||||||
|
#define GST_BASEAUDIOSINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_BASEAUDIOSINK, GstBaseAudioSinkClass))
|
||||||
|
#define GST_IS_BASEAUDIOSINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASEAUDIOSINK))
|
||||||
|
#define GST_IS_BASEAUDIOSINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASEAUDIOSINK))
|
||||||
|
|
||||||
|
#define GST_BASEAUDIOSINK_CLOCK(obj) (GST_BASEAUDIOSINK (obj)->clock)
|
||||||
|
#define GST_BASEAUDIOSINK_PAD(obj) (GST_BASEAUDIOSINK (obj)->sinkpad)
|
||||||
|
|
||||||
|
typedef struct _GstBaseAudioSink GstBaseAudioSink;
|
||||||
|
typedef struct _GstBaseAudioSinkClass GstBaseAudioSinkClass;
|
||||||
|
|
||||||
|
struct _GstBaseAudioSink {
|
||||||
|
GstBaseSink element;
|
||||||
|
|
||||||
|
/* our ringbuffer */
|
||||||
|
GstRingBuffer *ringbuffer;
|
||||||
|
|
||||||
|
/* required buffer and latency */
|
||||||
|
GstClockTime buffer;
|
||||||
|
GstClockTime latency;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GstBaseAudioSinkClass {
|
||||||
|
GstBaseSinkClass parent_class;
|
||||||
|
|
||||||
|
/* subclass ringbuffer allocation */
|
||||||
|
GstRingBuffer* (*create_ringbuffer) (GstBaseAudioSink *sink);
|
||||||
|
};
|
||||||
|
|
||||||
|
GType gst_baseaudiosink_get_type(void);
|
||||||
|
|
||||||
|
GstRingBuffer *gst_baseaudiosink_create_ringbuffer (GstBaseAudioSink *sink);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GST_BASEAUDIOSINK_H__ */
|
656
gst-libs/gst/audio/gstringbuffer.c
Normal file
656
gst-libs/gst/audio/gstringbuffer.c
Normal file
|
@ -0,0 +1,656 @@
|
||||||
|
/* 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 = -1;
|
||||||
|
ringbuffer->writeseg = -1;
|
||||||
|
ringbuffer->segfilled = 0;
|
||||||
|
ringbuffer->freeseg = -1;
|
||||||
|
ringbuffer->segplayed = 0;
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_ringbuffer_set_callback:
|
||||||
|
* @buf: the #GstRingBuffer to set the callback on
|
||||||
|
* @cb: the callback to set
|
||||||
|
* @data: use 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_ringbuffer_set_callback (GstRingBuffer * buf, GstRingBufferCallback cb,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
GST_LOCK (buf);
|
||||||
|
buf->callback = cb;
|
||||||
|
buf->cb_data = data;
|
||||||
|
GST_UNLOCK (buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_ringbuffer_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_ringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
|
||||||
|
{
|
||||||
|
gboolean res = FALSE;
|
||||||
|
GstRingBufferClass *rclass;
|
||||||
|
|
||||||
|
GST_LOCK (buf);
|
||||||
|
if (buf->acquired) {
|
||||||
|
res = TRUE;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
buf->acquired = TRUE;
|
||||||
|
|
||||||
|
rclass = GST_RINGBUFFER_GET_CLASS (buf);
|
||||||
|
if (rclass->acquire)
|
||||||
|
res = rclass->acquire (buf, spec);
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
buf->acquired = FALSE;
|
||||||
|
} else {
|
||||||
|
buf->freeseg = spec->segtotal;
|
||||||
|
if (buf->spec.bytes_per_sample != 0) {
|
||||||
|
buf->samples_per_seg = buf->spec.segsize / buf->spec.bytes_per_sample;
|
||||||
|
} else {
|
||||||
|
g_warning ("invalid bytes_per_sample from acquire ringbuffer");
|
||||||
|
buf->samples_per_seg = buf->spec.segsize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
GST_UNLOCK (buf);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_ringbuffer_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_ringbuffer_release (GstRingBuffer * buf)
|
||||||
|
{
|
||||||
|
gboolean res = FALSE;
|
||||||
|
GstRingBufferClass *rclass;
|
||||||
|
|
||||||
|
gst_ringbuffer_stop (buf);
|
||||||
|
|
||||||
|
GST_LOCK (buf);
|
||||||
|
if (!buf->acquired) {
|
||||||
|
res = TRUE;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
buf->acquired = FALSE;
|
||||||
|
|
||||||
|
rclass = GST_RINGBUFFER_GET_CLASS (buf);
|
||||||
|
if (rclass->release)
|
||||||
|
res = rclass->release (buf);
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
buf->acquired = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
GST_UNLOCK (buf);
|
||||||
|
|
||||||
|
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
|
||||||
|
*
|
||||||
|
* Start playing samples from the ringbuffer.
|
||||||
|
*
|
||||||
|
* Returns: TRUE if the device could be started, FALSE on error.
|
||||||
|
*
|
||||||
|
* MT safe.
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
gst_ringbuffer_play (GstRingBuffer * buf)
|
||||||
|
{
|
||||||
|
gboolean res = FALSE;
|
||||||
|
|
||||||
|
GST_LOCK (buf);
|
||||||
|
res = gst_ringbuffer_play_unlocked (buf);
|
||||||
|
GST_UNLOCK (buf);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_ringbuffer_pause:
|
||||||
|
* @buf: the #GstRingBuffer to pause
|
||||||
|
*
|
||||||
|
* Pause playing samples from the ringbuffer.
|
||||||
|
*
|
||||||
|
* Returns: TRUE if the device could be paused, FALSE on error.
|
||||||
|
*
|
||||||
|
* MT safe.
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
gst_ringbuffer_pause (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_PLAYING, GST_RINGBUFFER_STATE_PAUSED);
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
/* was not playing */
|
||||||
|
res = TRUE;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* signal any waiters */
|
||||||
|
GST_RINGBUFFER_SIGNAL (buf);
|
||||||
|
|
||||||
|
rclass = GST_RINGBUFFER_GET_CLASS (buf);
|
||||||
|
if (rclass->pause)
|
||||||
|
res = rclass->pause (buf);
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
buf->state = GST_RINGBUFFER_STATE_PLAYING;
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
GST_UNLOCK (buf);
|
||||||
|
|
||||||
|
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
|
||||||
|
*
|
||||||
|
* Stop playing samples from the ringbuffer.
|
||||||
|
*
|
||||||
|
* Returns: TRUE if the device could be stopped, FALSE on error.
|
||||||
|
*
|
||||||
|
* MT safe.
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
gst_ringbuffer_stop (GstRingBuffer * buf)
|
||||||
|
{
|
||||||
|
gboolean res = FALSE;
|
||||||
|
GstRingBufferClass *rclass;
|
||||||
|
|
||||||
|
GST_LOCK (buf);
|
||||||
|
/* if playing, set to stopped */
|
||||||
|
res = g_atomic_int_compare_and_exchange (&buf->state,
|
||||||
|
GST_RINGBUFFER_STATE_PLAYING, GST_RINGBUFFER_STATE_STOPPED);
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
/* was not playing, must be stopped then */
|
||||||
|
res = TRUE;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* signal any waiters */
|
||||||
|
GST_RINGBUFFER_SIGNAL (buf);
|
||||||
|
|
||||||
|
rclass = GST_RINGBUFFER_GET_CLASS (buf);
|
||||||
|
if (rclass->stop)
|
||||||
|
res = rclass->stop (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;
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
GST_UNLOCK (buf);
|
||||||
|
|
||||||
|
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
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Returns: The number of samples queued in the audio device.
|
||||||
|
*
|
||||||
|
* MT safe.
|
||||||
|
*/
|
||||||
|
guint
|
||||||
|
gst_ringbuffer_delay (GstRingBuffer * buf)
|
||||||
|
{
|
||||||
|
GstRingBufferClass *rclass;
|
||||||
|
guint res = 0;
|
||||||
|
|
||||||
|
rclass = GST_RINGBUFFER_GET_CLASS (buf);
|
||||||
|
if (rclass->delay)
|
||||||
|
res = rclass->delay (buf);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_ringbuffer_played_samples:
|
||||||
|
* @buf: the #GstRingBuffer to query
|
||||||
|
*
|
||||||
|
* Get the number of samples that were played by the ringbuffer
|
||||||
|
* since it was last started.
|
||||||
|
*
|
||||||
|
* Returns: The number of samples played by the ringbuffer.
|
||||||
|
*
|
||||||
|
* MT safe.
|
||||||
|
*/
|
||||||
|
guint64
|
||||||
|
gst_ringbuffer_played_samples (GstRingBuffer * buf)
|
||||||
|
{
|
||||||
|
gint segplayed;
|
||||||
|
guint64 samples;
|
||||||
|
guint delay;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_ringbuffer_commit:
|
||||||
|
* @buf: the #GstRingBuffer to commit
|
||||||
|
* @sample: the sample position of the data
|
||||||
|
* @data: the data to commit
|
||||||
|
* @len: the length of the data to commit
|
||||||
|
*
|
||||||
|
* Commit @length samples pointed to by @data to the ringbuffer
|
||||||
|
* @buf. The first sample should be written at 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 written to the ringbuffer.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
guint8 *dest;
|
||||||
|
|
||||||
|
if (buf->data == NULL)
|
||||||
|
goto no_buffer;
|
||||||
|
|
||||||
|
dest = GST_BUFFER_DATA (buf->data);
|
||||||
|
segsize = buf->spec.segsize;
|
||||||
|
segtotal = buf->spec.segtotal;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
segfilled = buf->segfilled;
|
||||||
|
|
||||||
|
/* check for partial buffer */
|
||||||
|
if (G_LIKELY (segfilled == 0)) {
|
||||||
|
gint prevfree;
|
||||||
|
gint newseg;
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
GST_RINGBUFFER_WAIT (buf);
|
||||||
|
if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_PLAYING)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this is the available size now in the current segment */
|
||||||
|
segavail = segsize - segfilled;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
not_playing:
|
||||||
|
{
|
||||||
|
GST_UNLOCK (buf);
|
||||||
|
GST_DEBUG ("stopped playing");
|
||||||
|
return len - towrite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_ringbuffer_prepare_read:
|
||||||
|
* @buf: the #GstRingBuffer to read from
|
||||||
|
* @segment: the segment to read
|
||||||
|
*
|
||||||
|
* Returns a pointer to memory where the data from segment @segment
|
||||||
|
* can be found.
|
||||||
|
*
|
||||||
|
* MT safe.
|
||||||
|
*/
|
||||||
|
guint8 *
|
||||||
|
gst_ringbuffer_prepare_read (GstRingBuffer * buf, gint segment)
|
||||||
|
{
|
||||||
|
guint8 *data;
|
||||||
|
|
||||||
|
data = GST_BUFFER_DATA (buf->data);
|
||||||
|
|
||||||
|
return data + (segment % buf->spec.segtotal) * buf->spec.segsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_ringbuffer_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_ringbuffer_clear (GstRingBuffer * buf, gint segment)
|
||||||
|
{
|
||||||
|
guint8 *data;
|
||||||
|
|
||||||
|
data = GST_BUFFER_DATA (buf->data);
|
||||||
|
|
||||||
|
memset (data + (segment % buf->spec.segtotal) * buf->spec.segsize, 0,
|
||||||
|
buf->spec.segsize);
|
||||||
|
}
|
176
gst-libs/gst/audio/gstringbuffer.h
Normal file
176
gst-libs/gst/audio/gstringbuffer.h
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
|
||||||
|
* 2005 Wim Taymans <wim@fluendo.com>
|
||||||
|
*
|
||||||
|
* gstringbuffer.h:
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GST_RINGBUFFER_H__
|
||||||
|
#define __GST_RINGBUFFER_H__
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define GST_TYPE_RINGBUFFER (gst_ringbuffer_get_type())
|
||||||
|
#define GST_RINGBUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RINGBUFFER,GstRingBuffer))
|
||||||
|
#define GST_RINGBUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RINGBUFFER,GstRingBufferClass))
|
||||||
|
#define GST_RINGBUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RINGBUFFER, GstRingBufferClass))
|
||||||
|
#define GST_IS_RINGBUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RINGBUFFER))
|
||||||
|
#define GST_IS_RINGBUFFER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RINGBUFFER))
|
||||||
|
|
||||||
|
typedef struct _GstRingBuffer GstRingBuffer;
|
||||||
|
typedef struct _GstRingBufferClass GstRingBufferClass;
|
||||||
|
typedef struct _GstRingBufferSpec GstRingBufferSpec;
|
||||||
|
|
||||||
|
typedef void (*GstRingBufferCallback) (GstRingBuffer *rbuf, guint advance, gpointer data);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GST_RINGBUFFER_STATE_STOPPED,
|
||||||
|
GST_RINGBUFFER_STATE_PAUSED,
|
||||||
|
GST_RINGBUFFER_STATE_PLAYING,
|
||||||
|
} GstRingBufferState;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GST_SEGSTATE_INVALID,
|
||||||
|
GST_SEGSTATE_EMPTY,
|
||||||
|
GST_SEGSTATE_FILLED,
|
||||||
|
GST_SEGSTATE_PARTIAL,
|
||||||
|
} GstRingBufferSegState;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GST_U8,
|
||||||
|
GST_S8,
|
||||||
|
|
||||||
|
GST_U16_LE,
|
||||||
|
GST_S16_LE,
|
||||||
|
GST_U16_BE,
|
||||||
|
GST_S16_BE,
|
||||||
|
|
||||||
|
GST_U24_LE,
|
||||||
|
GST_S24_LE,
|
||||||
|
GST_U24_BE,
|
||||||
|
GST_S24_BE,
|
||||||
|
|
||||||
|
GST_FLOAT_LE,
|
||||||
|
GST_FLOAT_BE,
|
||||||
|
} GstBufferFormat;
|
||||||
|
|
||||||
|
struct _GstRingBufferSpec
|
||||||
|
{
|
||||||
|
/* in */
|
||||||
|
GstCaps *caps; /* the caps of the buffer */
|
||||||
|
|
||||||
|
/* in/out */
|
||||||
|
GstBufferFormat format;
|
||||||
|
gint rate;
|
||||||
|
gint channels;
|
||||||
|
|
||||||
|
GstClockTime latency; /* the required/actual latency */
|
||||||
|
GstClockTime buffersize; /* the required/actual size of the buffer */
|
||||||
|
gint segsize; /* size of one buffer segement */
|
||||||
|
gint segtotal; /* total number of segments */
|
||||||
|
|
||||||
|
/* out */
|
||||||
|
gint bytes_per_sample; /* number of bytes of one sample */
|
||||||
|
guint8 silence_sample[32]; /* bytes representing silence */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define GST_RINGBUFFER_GET_COND(buf) (((GstRingBuffer *)buf)->cond)
|
||||||
|
#define GST_RINGBUFFER_WAIT(buf) (g_cond_wait (GST_RINGBUFFER_GET_COND (buf), GST_GET_LOCK (buf)))
|
||||||
|
#define GST_RINGBUFFER_SIGNAL(buf) (g_cond_signal (GST_RINGBUFFER_GET_COND (buf)))
|
||||||
|
#define GST_RINGBUFFER_BROADCAST(buf)(g_cond_broadcast (GST_RINGBUFFER_GET_COND (buf)))
|
||||||
|
|
||||||
|
struct _GstRingBuffer {
|
||||||
|
GstObject object;
|
||||||
|
|
||||||
|
/*< public >*/ /* with LOCK */
|
||||||
|
GCond *cond;
|
||||||
|
gboolean acquired;
|
||||||
|
GstBuffer *data;
|
||||||
|
GstRingBufferSpec spec;
|
||||||
|
GstRingBufferSegState *segstate;
|
||||||
|
gint samples_per_seg; /* number of samples per segment */
|
||||||
|
|
||||||
|
/*< 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 */
|
||||||
|
|
||||||
|
/*< private >*/
|
||||||
|
GstRingBufferCallback callback;
|
||||||
|
gpointer cb_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GstRingBufferClass {
|
||||||
|
GstObjectClass parent_class;
|
||||||
|
|
||||||
|
/*< public >*/
|
||||||
|
/* allocate the resources for the ringbuffer using the given specs */
|
||||||
|
gboolean (*acquire) (GstRingBuffer *buf, GstRingBufferSpec *spec);
|
||||||
|
/* free resources of the ringbuffer */
|
||||||
|
gboolean (*release) (GstRingBuffer *buf);
|
||||||
|
|
||||||
|
/* playback control */
|
||||||
|
gboolean (*play) (GstRingBuffer *buf);
|
||||||
|
gboolean (*pause) (GstRingBuffer *buf);
|
||||||
|
gboolean (*resume) (GstRingBuffer *buf);
|
||||||
|
gboolean (*stop) (GstRingBuffer *buf);
|
||||||
|
|
||||||
|
/* number of samples queued in device */
|
||||||
|
guint (*delay) (GstRingBuffer *buf);
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* allocate resources */
|
||||||
|
gboolean gst_ringbuffer_acquire (GstRingBuffer *buf, GstRingBufferSpec *spec);
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
void gst_ringbuffer_clear (GstRingBuffer *buf, gint segment);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GST_RINGBUFFER_H__ */
|
Loading…
Reference in a new issue