From 909da9fb0489c3744547e6386531461c072da5fc Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tim-Philipp=20M=C3=BCller?= Date: Thu, 10 Feb 2005 15:06:13 +0000 Subject: [PATCH] and make it handle more than one channel. Original commit message from CVS: Fix speed element and make it chain-based (fixes #156467), and make it handle more than one channel. --- ChangeLog | 13 +++ gst/speed/Makefile.am | 2 +- gst/speed/demo-mp3.c | 9 +- gst/speed/filter.func | 60 ----------- gst/speed/gstspeed.c | 276 ++++++++++++++++++++++++++++++++++++-------------- gst/speed/gstspeed.h | 26 ++--- 6 files changed, 229 insertions(+), 157 deletions(-) delete mode 100644 gst/speed/filter.func diff --git a/ChangeLog b/ChangeLog index e9b2f5a..c801394 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2005-02-10 Tim-Philipp Müller + + * 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 * ext/dts/gstdtsdec.c: (gst_dtsdec_init), (gst_dtsdec_channels), diff --git a/gst/speed/Makefile.am b/gst/speed/Makefile.am index 7062fa9..9f86662 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 3b55194..c61f15e 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 6802b20..0000000 --- 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= 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 b685bb3..79b39f8 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 "); - + "Andy Wingo , " + "Tim-Philipp Müller "); -/* 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, + ×tamp) + && 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 2bbb0e6..7c2ba2a 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 { -- 2.7.4