summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog13
-rw-r--r--gst/speed/Makefile.am2
-rw-r--r--gst/speed/demo-mp3.c9
-rw-r--r--gst/speed/filter.func60
-rw-r--r--gst/speed/gstspeed.c276
-rw-r--r--gst/speed/gstspeed.h26
6 files changed, 229 insertions, 157 deletions
diff --git a/ChangeLog b/ChangeLog
index e9b2f5a9..c8013949 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2005-02-10 Tim-Philipp Müller <tim at centricular dot net>
+
+ * gst/speed/Makefile.am:
+ * gst/speed/demo-mp3.c: (main):
+ * gst/speed/filter.func:
+ * gst/speed/gstspeed.c: (speed_link), (speed_parse_caps),
+ (speed_class_init), (speed_init), (speed_chain_int16),
+ (speed_chain_float32), (speed_chain), (speed_set_property),
+ (speed_get_property), (speed_change_state):
+ * gst/speed/gstspeed.h:
+ Fix speed element and make it chain-based (fixes #156467),
+ and make it handle more than one channel.
+
2005-02-10 Jan Schmidt <thaytan@mad.scientist.com>
* ext/dts/gstdtsdec.c: (gst_dtsdec_init), (gst_dtsdec_channels),
diff --git a/gst/speed/Makefile.am b/gst/speed/Makefile.am
index 7062fa95..9f866624 100644
--- a/gst/speed/Makefile.am
+++ b/gst/speed/Makefile.am
@@ -6,7 +6,7 @@ libgstspeed_la_CFLAGS = $(GST_CFLAGS)
libgstspeed_la_LIBADD =
libgstspeed_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
-noinst_HEADERS = gstspeed.h filter.func
+noinst_HEADERS = gstspeed.h
if HAVE_GTK
noinst_PROGRAMS = demo-mp3
diff --git a/gst/speed/demo-mp3.c b/gst/speed/demo-mp3.c
index 3b551941..c61f15ef 100644
--- a/gst/speed/demo-mp3.c
+++ b/gst/speed/demo-mp3.c
@@ -37,7 +37,7 @@ int
main (int argc, char **argv)
{
GtkWidget *window, *vbox, *hscale, *button;
- GstElement *filesrc, *mad, *stereo2mono, *speed, *audiosink, *pipeline;
+ GstElement *filesrc, *mad, *audioconvert, *speed, *audiosink, *pipeline;
gst_init (&argc, &argv);
gtk_init (&argc, &argv);
@@ -65,18 +65,17 @@ main (int argc, char **argv)
filesrc = gst_element_factory_make ("filesrc", "filesrc");
mad = gst_element_factory_make ("mad", "mad");
- stereo2mono = gst_element_factory_make ("stereo2mono", "stereo2mono");
+ audioconvert = gst_element_factory_make ("audioconvert", "audioconvert0");
speed = gst_element_factory_make ("speed", "speed");
audiosink = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
- g_object_set (audiosink, "fragment", 0x00180008, NULL);
gtk_signal_connect (GTK_OBJECT (gtk_range_get_adjustment (GTK_RANGE
(hscale))), "value_changed", G_CALLBACK (set_speed), speed);
pipeline = gst_pipeline_new ("app");
- gst_bin_add_many (GST_BIN (pipeline), filesrc, mad, stereo2mono, speed,
+ gst_bin_add_many (GST_BIN (pipeline), filesrc, mad, audioconvert, speed,
audiosink, NULL);
- gst_element_link_many (filesrc, mad, stereo2mono, speed, audiosink, NULL);
+ gst_element_link_many (filesrc, mad, audioconvert, speed, audiosink, NULL);
g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
gst_element_set_state (pipeline, GST_STATE_PLAYING);
diff --git a/gst/speed/filter.func b/gst/speed/filter.func
deleted file mode 100644
index 6802b20c..00000000
--- a/gst/speed/filter.func
+++ /dev/null
@@ -1,60 +0,0 @@
-/* -*- Mode: c; c-basic-offset: 2 -*- */
- _FORMAT *in_data, *out_data;
- static gint64 offset = 0, timestamp = 0;
-
- /* get a buffer here so that we can have something to interpolate
- * against for the first few samples if speed < 0.5 */
- in_data = (_FORMAT*) GST_BUFFER_DATA(in);
- nin = GST_BUFFER_SIZE(in)/sizeof(_FORMAT);
- lower = in_data[0];
- i_float = 0.5 * (speed - 1.0);
- i = i_float + 1.0; /* ciel(i_float) for ints */
-
- do {
- speed = filter->speed; /* update this, it might have changed */
-
- out = gst_buffer_new();
- GST_BUFFER_DATA(out) = (gchar*) g_new(_FORMAT,SPEED_BUFSIZE/sizeof(_FORMAT));
- GST_BUFFER_SIZE(out) = SPEED_BUFSIZE;
- out_data = (_FORMAT*) GST_BUFFER_DATA(out);
- nout = GST_BUFFER_SIZE(out) / sizeof(_FORMAT);
- GST_BUFFER_TIMESTAMP (out) = timestamp;
- offset += nout;
- timestamp = offset * GST_SECOND / filter->rate;
-
- for (j=0; j<nout; j++) {
- /* index of upper bounds of interpolation for
- * new sample, got it by trial&error on the chalkboard */
- i_float += speed;
- i = i_float + 1.0; /* ciel(i_float) for ints */
-
- while (i >= nin) {
- i = i % nin;
- i_float = i_float - nin;
- lower = in_data[nin-1];
- gst_buffer_unref(in);
- in = GST_BUFFER (gst_pad_pull (filter->sinkpad));
-
- while (GST_IS_EVENT (in)) {
- gst_pad_event_default (filter->srcpad, GST_EVENT (in));
- in = GST_BUFFER (gst_pad_pull (filter->sinkpad));
- }
-
- in_data = (_FORMAT*) GST_BUFFER_DATA(in);
- nin = GST_BUFFER_SIZE(in) / sizeof(_FORMAT);
- }
-
- if (i>0)
- lower = in_data[i-1];
-
- interp = i_float - floor(i_float);
-
- out_data[j] = lower*(1-interp) + in_data[i]*interp;
-
- lower = in_data[i];
- }
-
- gst_pad_push(filter->srcpad, GST_DATA (out));
-
- gst_element_yield (element);
- } while (TRUE);
diff --git a/gst/speed/gstspeed.c b/gst/speed/gstspeed.c
index b685bb32..79b39f85 100644
--- a/gst/speed/gstspeed.c
+++ b/gst/speed/gstspeed.c
@@ -29,25 +29,13 @@
#include "gstspeed.h"
-/* buffer size to make if no bufferpool is available, must be divisible by
- * sizeof(gfloat) */
-#define SPEED_BUFSIZE 4096
-/* number of buffers to allocate per chunk in sink buffer pool */
-#define SPEED_NUMBUF 6
-
/* elementfactory information */
static GstElementDetails speed_details = GST_ELEMENT_DETAILS ("Speed",
"Filter/Effect/Audio",
"Set speed/pitch on audio/raw streams (resampler)",
- "Andy Wingo <apwingo@eos.ncsu.edu>");
-
+ "Andy Wingo <apwingo@eos.ncsu.edu>, "
+ "Tim-Philipp Müller <tim@centricular.net>");
-/* Filter signals and args */
-enum
-{
- /* FILL ME */
- LAST_SIGNAL
-};
enum
{
@@ -55,20 +43,35 @@ enum
ARG_SPEED
};
+/* assumption here: sizeof (gfloat) = 4 */
+#define GST_SPEED_AUDIO_CAPS \
+ "audio/x-raw-float, " \
+ "rate = (int) [ 1, MAX ], " \
+ "channels = (int) [ 1, MAX ], " \
+ "endianness = (int) BYTE_ORDER, " \
+ "width = (int) 32, " \
+ "buffer-frames = (int) 0; " \
+ \
+ "audio/x-raw-int, " \
+ "rate = (int) [ 1, MAX ], " \
+ "channels = (int) [ 1, MAX ], " \
+ "endianness = (int) BYTE_ORDER, " \
+ "width = (int) 16, " \
+ "depth = (int) 16, " \
+ "signed = (boolean) true"
+
static GstStaticPadTemplate gst_speed_sink_template =
- GST_STATIC_PAD_TEMPLATE ("sink",
+GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
- GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
- GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS)
+ GST_STATIC_CAPS (GST_SPEED_AUDIO_CAPS)
);
static GstStaticPadTemplate gst_speed_src_template =
- GST_STATIC_PAD_TEMPLATE ("src",
+GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
- GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
- GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS)
+ GST_STATIC_CAPS (GST_SPEED_AUDIO_CAPS)
);
static void speed_base_init (gpointer g_class);
@@ -82,11 +85,11 @@ static void speed_get_property (GObject * object, guint prop_id, GValue * value,
static gboolean speed_parse_caps (GstSpeed * filter, const GstCaps * caps);
-static void speed_loop (GstElement * element);
+static void speed_chain (GstPad * pad, GstData * data);
-static GstElementClass *parent_class = NULL;
+static GstElementStateReturn speed_change_state (GstElement * element);
-/*static guint gst_filter_signals[LAST_SIGNAL] = { 0 }; */
+static GstElementClass *parent_class; /* NULL */
static GstPadLinkReturn
speed_link (GstPad * pad, const GstCaps * caps)
@@ -97,7 +100,7 @@ speed_link (GstPad * pad, const GstCaps * caps)
filter = GST_SPEED (gst_pad_get_parent (pad));
g_return_val_if_fail (filter != NULL, GST_PAD_LINK_REFUSED);
g_return_val_if_fail (GST_IS_SPEED (filter), GST_PAD_LINK_REFUSED);
- otherpad = (pad == filter->srcpad ? filter->sinkpad : filter->srcpad);
+ otherpad = (pad == filter->srcpad) ? filter->sinkpad : filter->srcpad;
if (!speed_parse_caps (filter, caps))
return GST_PAD_LINK_REFUSED;
@@ -117,29 +120,31 @@ speed_parse_caps (GstSpeed * filter, const GstCaps * caps)
structure = gst_caps_get_structure (caps, 0);
+ mimetype = gst_structure_get_name (structure);
+ if (strcmp (mimetype, "audio/x-raw-float") == 0)
+ filter->format = GST_SPEED_FORMAT_FLOAT;
+ else if (strcmp (mimetype, "audio/x-raw-int") == 0)
+ filter->format = GST_SPEED_FORMAT_INT;
+ else
+ return FALSE;
+
ret = gst_structure_get_int (structure, "rate", &filter->rate);
ret &= gst_structure_get_int (structure, "channels", &filter->channels);
ret &= gst_structure_get_int (structure, "width", &filter->width);
- ret &= gst_structure_get_int (structure, "endianness", &filter->endianness);
filter->buffer_frames = 0;
gst_structure_get_int (structure, "buffer-frames", &filter->buffer_frames);
- mimetype = gst_structure_get_name (structure);
-
- if (strcmp (mimetype, "audio/x-raw-int") == 0) {
- filter->format = GST_SPEED_FORMAT_INT;
- ret &= gst_structure_get_int (structure, "depth", &filter->depth);
- ret &= gst_structure_get_boolean (structure, "signed", &filter->is_signed);
- } else if (strcmp (mimetype, "audio/x-raw-float") == 0) {
- filter->format = GST_SPEED_FORMAT_FLOAT;
+ if (filter->format == GST_SPEED_FORMAT_FLOAT) {
+ filter->sample_size = filter->channels * filter->width / 8;
} else {
- return FALSE;
+ /* our caps only allow width == depth for now */
+ filter->sample_size = filter->channels * filter->width / 8;
}
+
return ret;
}
-
GType
gst_speed_get_type (void)
{
@@ -180,11 +185,13 @@ static void
speed_class_init (GstSpeedClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
+ GstElementClass *gstelement_class = (GstElementClass *) klass;
gobject_class->set_property = speed_set_property;
gobject_class->get_property = speed_get_property;
+ gstelement_class->change_state = speed_change_state;
- parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+ parent_class = g_type_class_peek_parent (klass);
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SPEED,
g_param_spec_float ("speed", "speed", "speed",
@@ -198,73 +205,165 @@ speed_init (GstSpeed * filter)
gst_pad_new_from_template (gst_static_pad_template_get
(&gst_speed_sink_template), "sink");
gst_pad_set_link_function (filter->sinkpad, speed_link);
+ gst_pad_set_chain_function (filter->sinkpad, speed_chain);
+ gst_pad_set_getcaps_function (filter->sinkpad, gst_pad_proxy_getcaps);
gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
filter->srcpad =
gst_pad_new_from_template (gst_static_pad_template_get
(&gst_speed_src_template), "src");
gst_pad_set_link_function (filter->srcpad, speed_link);
+ gst_pad_set_getcaps_function (filter->srcpad, gst_pad_proxy_getcaps);
gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
- gst_element_set_loop_function (GST_ELEMENT (filter), speed_loop);
+ filter->offset = 0;
+ filter->timestamp = 0;
+ filter->sample_size = 0;
}
-static void
-speed_loop (GstElement * element)
+static inline guint
+speed_chain_int16 (GstSpeed * filter, GstBuffer * in_buf, GstBuffer * out_buf,
+ guint c, guint in_samples)
{
- GstSpeed *filter = GST_SPEED (element);
- GstBuffer *in, *out;
- guint i, j, nin, nout;
- gfloat interp, speed, lower, i_float;
+ gint16 *in_data, *out_data;
+ gfloat interp, lower, i_float;
+ guint i, j;
- g_return_if_fail (filter != NULL);
- g_return_if_fail (GST_IS_SPEED (filter));
+ in_data = ((gint16 *) GST_BUFFER_DATA (in_buf)) + c;
+ out_data = ((gint16 *) GST_BUFFER_DATA (out_buf)) + c;
- i = j = 0;
- speed = filter->speed;
+ lower = in_data[0];
+ i_float = 0.5 * (filter->speed - 1.0);
+ i = (guint) ceil (i_float);
+ j = 0;
- in = GST_BUFFER (gst_pad_pull (filter->sinkpad));
+ while (i < in_samples) {
+ interp = i_float - floor (i_float);
- if (GST_IS_EVENT (in)) {
- gst_pad_event_default (filter->sinkpad, GST_EVENT (in));
- return;
+ out_data[j * filter->channels] =
+ lower * (1 - interp) + in_data[i * filter->channels] * interp;
+
+ lower = in_data[i * filter->channels];
+
+ i_float += filter->speed;
+ i = (guint) ceil (i_float);
+
+ ++j;
}
- while (GST_IS_EVENT (in)) {
- gst_pad_event_default (filter->srcpad, GST_EVENT (in));
- in = GST_BUFFER (gst_pad_pull (filter->sinkpad));
+ return j;
+}
+
+static inline guint
+speed_chain_float32 (GstSpeed * filter, GstBuffer * in_buf, GstBuffer * out_buf,
+ guint c, guint in_samples)
+{
+ gfloat *in_data, *out_data;
+ gfloat interp, lower, i_float;
+ guint i, j;
+
+ in_data = ((gfloat *) GST_BUFFER_DATA (in_buf)) + c;
+ out_data = ((gfloat *) GST_BUFFER_DATA (out_buf)) + c;
+
+ lower = in_data[0];
+ i_float = 0.5 * (filter->speed - 1.0);
+ i = (guint) ceil (i_float);
+ j = 0;
+
+ while (i < in_samples) {
+ interp = i_float - floor (i_float);
+
+ out_data[j * filter->channels] =
+ lower * (1 - interp) + in_data[i * filter->channels] * interp;
+
+ lower = in_data[i * filter->channels];
+
+ i_float += filter->speed;
+ i = (guint) ceil (i_float);
+
+ ++j;
}
- /* this is a bit nasty, but hey, it's what you've got to do to keep the same
- * algorithm and multiple data types in c. */
- if (filter->format == GST_SPEED_FORMAT_FLOAT) {
-#define _FORMAT gfloat
-#include "filter.func"
-#undef _FORMAT
- } else if (filter->format == GST_SPEED_FORMAT_INT && filter->width == 16) {
-#define _FORMAT gint16
-#include "filter.func"
-#undef _FORMAT
- } else if (filter->format == GST_SPEED_FORMAT_INT && filter->width == 8) {
-#define _FORMAT gint8
-#include "filter.func"
-#undef _FORMAT
- } else {
- GST_ELEMENT_ERROR (filter, CORE, NEGOTIATION, (NULL),
- ("format wasn't negotiated before chain function"));
- gst_element_yield (element);
+ return j;
+}
+
+static void
+speed_chain (GstPad * pad, GstData * data)
+{
+ GstBuffer *in_buf, *out_buf;
+ GstSpeed *filter;
+ guint c, in_samples, out_samples, out_size;
+
+ g_return_if_fail (pad != NULL);
+ g_return_if_fail (GST_IS_PAD (pad));
+ g_return_if_fail (data != NULL);
+
+ filter = GST_SPEED (GST_OBJECT_PARENT (pad));
+ g_return_if_fail (GST_IS_SPEED (filter));
+
+ if (GST_IS_EVENT (data)) {
+ switch (GST_EVENT_TYPE (GST_EVENT (data))) {
+ case GST_EVENT_DISCONTINUOUS:
+ {
+ gint64 timestamp, offset;
+
+ if (gst_event_discont_get_value (GST_EVENT (data), GST_FORMAT_BYTES,
+ &timestamp)
+ && gst_event_discont_get_value (GST_EVENT (data), GST_FORMAT_BYTES,
+ &offset)) {
+ filter->offset = offset;
+ filter->timestamp = timestamp;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ gst_pad_event_default (pad, GST_EVENT (data));
+ return;
}
+
+ in_buf = GST_BUFFER (data);
+
+ out_size = ceil ((gfloat) GST_BUFFER_SIZE (in_buf) / filter->speed);
+ out_buf = gst_pad_alloc_buffer (filter->srcpad, -1, out_size);
+
+ in_samples = GST_BUFFER_SIZE (in_buf) / filter->sample_size;
+
+ out_samples = 0;
+
+ for (c = 0; c < filter->channels; ++c) {
+ if (filter->format == GST_SPEED_FORMAT_INT) {
+ out_samples = speed_chain_int16 (filter, in_buf, out_buf, c, in_samples);
+ } else {
+ out_samples =
+ speed_chain_float32 (filter, in_buf, out_buf, c, in_samples);
+ }
+ }
+
+ GST_BUFFER_SIZE (out_buf) = out_samples * filter->sample_size;
+
+ GST_BUFFER_OFFSET (out_buf) = filter->offset;
+ GST_BUFFER_TIMESTAMP (out_buf) = filter->timestamp;
+
+ filter->offset += GST_BUFFER_SIZE (out_buf) / filter->sample_size;
+ filter->timestamp = filter->offset * GST_SECOND / filter->rate;
+
+ GST_BUFFER_DURATION (out_buf) =
+ filter->timestamp - GST_BUFFER_TIMESTAMP (out_buf);
+
+ gst_pad_push (filter->srcpad, GST_DATA (out_buf));
+
+ gst_buffer_unref (in_buf);
}
static void
speed_set_property (GObject * object, guint prop_id, const GValue * value,
GParamSpec * pspec)
{
- GstSpeed *filter;
+ GstSpeed *filter = (GstSpeed *) object;
- /* it's not null if we got it, but it might not be ours */
g_return_if_fail (GST_IS_SPEED (object));
- filter = GST_SPEED (object);
switch (prop_id) {
case ARG_SPEED:
@@ -279,11 +378,9 @@ static void
speed_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
- GstSpeed *filter;
+ GstSpeed *filter = (GstSpeed *) object;
- /* it's not null if we got it, but it might not be ours */
g_return_if_fail (GST_IS_SPEED (object));
- filter = GST_SPEED (object);
switch (prop_id) {
case ARG_SPEED:
@@ -295,6 +392,29 @@ speed_get_property (GObject * object, guint prop_id, GValue * value,
}
}
+static GstElementStateReturn
+speed_change_state (GstElement * element)
+{
+ GstSpeed *speed = GST_SPEED (element);
+
+ switch (GST_STATE_TRANSITION (element)) {
+ case GST_STATE_PAUSED_TO_READY:
+ break;
+ case GST_STATE_READY_TO_PAUSED:
+ speed->offset = 0;
+ speed->timestamp = 0;
+ speed->sample_size = 0;
+ break;
+ default:
+ break;
+ }
+
+ if (parent_class->change_state)
+ return parent_class->change_state (element);
+
+ return GST_STATE_SUCCESS;
+}
+
static gboolean
plugin_init (GstPlugin * plugin)
{
diff --git a/gst/speed/gstspeed.h b/gst/speed/gstspeed.h
index 2bbb0e6c..7c2ba2a5 100644
--- a/gst/speed/gstspeed.h
+++ b/gst/speed/gstspeed.h
@@ -47,23 +47,23 @@ enum _GstSpeedFormat {
};
struct _GstSpeed {
- GstElement element;
+ GstElement element;
- GstPad *sinkpad, *srcpad;
+ GstPad *sinkpad;
+ GstPad *srcpad;
- gfloat speed;
+ gfloat speed;
- /* valid for both int and float */
+ gint64 offset;
+ gint64 timestamp;
+
+ guint rate;
+ guint channels;
+ guint width;
+ guint buffer_frames;
+
+ guint sample_size;
GstSpeedFormat format;
- guint rate;
- guint channels;
- guint width;
- guint endianness;
- guint buffer_frames;
-
- /* valid only for format==GST_SPEED_FORMAT_INT */
- guint depth;
- gboolean is_signed;
};
struct _GstSpeedClass {