| /* GStreamer |
| * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> |
| * 2000 Wim Taymans <wtay@chello.be> |
| * 2005 Wim Taymans <wim@fluendo.com> |
| * |
| * gstsignalprocessor.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 <stdlib.h> |
| |
| #ifdef HAVE_CONFIG_H |
| # include "config.h" |
| #endif |
| |
| #include <gst/audio/audio.h> |
| #include "gstsignalprocessor.h" |
| |
| |
| GST_DEBUG_CATEGORY_STATIC (gst_signal_processor_debug); |
| #define GST_CAT_DEFAULT gst_signal_processor_debug |
| |
| |
| static GstStaticCaps template_caps = |
| GST_STATIC_CAPS (GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS); |
| |
| #define GST_TYPE_SIGNAL_PROCESSOR_PAD_TEMPLATE \ |
| (gst_signal_processor_pad_template_get_type ()) |
| #define GST_SIGNAL_PROCESSOR_PAD_TEMPLATE(obj) \ |
| (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SIGNAL_PROCESSOR_PAD_TEMPLATE,\ |
| GstSignalProcessorPadTemplate)) |
| typedef struct _GstSignalProcessorPadTemplate GstSignalProcessorPadTemplate; |
| typedef GstPadTemplateClass GstSignalProcessorPadTemplateClass; |
| |
| struct _GstSignalProcessorPadTemplate |
| { |
| GstPadTemplate parent; |
| |
| guint index; |
| }; |
| |
| static GType |
| gst_signal_processor_pad_template_get_type (void) |
| { |
| static GType type = 0; |
| |
| if (!type) { |
| static const GTypeInfo info = { |
| sizeof (GstSignalProcessorPadTemplateClass), NULL, NULL, NULL, NULL, |
| NULL, sizeof (GstSignalProcessorPadTemplate), 0, NULL |
| }; |
| |
| type = g_type_register_static (GST_TYPE_PAD_TEMPLATE, |
| "GstSignalProcessorPadTemplate", &info, 0); |
| } |
| return type; |
| } |
| |
| void |
| gst_signal_processor_class_add_pad_template (GstSignalProcessorClass * klass, |
| const gchar * name, GstPadDirection direction, guint index) |
| { |
| GstPadTemplate *new; |
| |
| g_return_if_fail (GST_IS_SIGNAL_PROCESSOR_CLASS (klass)); |
| g_return_if_fail (name != NULL); |
| g_return_if_fail (direction == GST_PAD_SRC || direction == GST_PAD_SINK); |
| |
| new = g_object_new (gst_signal_processor_pad_template_get_type (), |
| "name", name, NULL); |
| |
| GST_PAD_TEMPLATE_NAME_TEMPLATE (new) = g_strdup (name); |
| GST_PAD_TEMPLATE_DIRECTION (new) = direction; |
| GST_PAD_TEMPLATE_PRESENCE (new) = GST_PAD_ALWAYS; |
| GST_PAD_TEMPLATE_CAPS (new) = gst_caps_copy (gst_static_caps_get |
| (&template_caps)); |
| GST_SIGNAL_PROCESSOR_PAD_TEMPLATE (new)->index = index; |
| |
| gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass), new); |
| gst_object_unref (new); |
| } |
| |
| |
| #define GST_TYPE_SIGNAL_PROCESSOR_PAD (gst_signal_processor_pad_get_type ()) |
| #define GST_SIGNAL_PROCESSOR_PAD(obj) \ |
| (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SIGNAL_PROCESSOR_PAD,\ |
| GstSignalProcessorPad)) |
| typedef struct _GstSignalProcessorPad GstSignalProcessorPad; |
| typedef GstPadClass GstSignalProcessorPadClass; |
| |
| struct _GstSignalProcessorPad |
| { |
| GstPad parent; |
| |
| GstBuffer *pen; |
| |
| guint index; |
| |
| /* these are only used for sink pads */ |
| guint samples_avail; |
| gfloat *data; |
| }; |
| |
| static GType |
| gst_signal_processor_pad_get_type (void) |
| { |
| static GType type = 0; |
| |
| if (!type) { |
| static const GTypeInfo info = { |
| sizeof (GstSignalProcessorPadClass), NULL, NULL, NULL, NULL, |
| NULL, sizeof (GstSignalProcessorPad), 0, NULL |
| }; |
| |
| type = g_type_register_static (GST_TYPE_PAD, |
| "GstSignalProcessorPad", &info, 0); |
| } |
| return type; |
| } |
| |
| |
| GST_BOILERPLATE (GstSignalProcessor, gst_signal_processor, GstElement, |
| GST_TYPE_ELEMENT); |
| |
| |
| static void gst_signal_processor_finalize (GObject * object); |
| static void gst_signal_processor_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec); |
| static void gst_signal_processor_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec); |
| static gboolean gst_signal_processor_src_activate_pull (GstPad * pad, |
| gboolean active); |
| static gboolean gst_signal_processor_sink_activate_push (GstPad * pad, |
| gboolean active); |
| static GstStateChangeReturn gst_signal_processor_change_state (GstElement * |
| element, GstStateChange transition); |
| |
| static gboolean gst_signal_processor_event (GstPad * pad, GstEvent * event); |
| static GstFlowReturn gst_signal_processor_getrange (GstPad * pad, |
| guint64 offset, guint length, GstBuffer ** buffer); |
| static GstFlowReturn gst_signal_processor_chain (GstPad * pad, |
| GstBuffer * buffer); |
| static gboolean gst_signal_processor_setcaps (GstPad * pad, GstCaps * caps); |
| |
| |
| static void |
| gst_signal_processor_base_init (gpointer g_class) |
| { |
| GST_DEBUG_CATEGORY_INIT (gst_signal_processor_debug, "gst-dsp", 0, |
| "signalprocessor element"); |
| } |
| |
| static void |
| gst_signal_processor_class_init (GstSignalProcessorClass * klass) |
| { |
| GObjectClass *gobject_class; |
| GstElementClass *gstelement_class; |
| |
| gobject_class = G_OBJECT_CLASS (klass); |
| gstelement_class = GST_ELEMENT_CLASS (klass); |
| |
| gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_signal_processor_finalize); |
| gobject_class->set_property = |
| GST_DEBUG_FUNCPTR (gst_signal_processor_set_property); |
| gobject_class->get_property = |
| GST_DEBUG_FUNCPTR (gst_signal_processor_get_property); |
| |
| gstelement_class->change_state = |
| GST_DEBUG_FUNCPTR (gst_signal_processor_change_state); |
| } |
| |
| static void |
| gst_signal_processor_add_pad_from_template (GstSignalProcessor * self, |
| GstPadTemplate * templ) |
| { |
| GstPad *new; |
| |
| new = g_object_new (GST_TYPE_SIGNAL_PROCESSOR_PAD, |
| "name", GST_OBJECT_NAME (templ), "direction", templ->direction, |
| "template", templ, NULL); |
| GST_SIGNAL_PROCESSOR_PAD (new)->index = |
| GST_SIGNAL_PROCESSOR_PAD_TEMPLATE (templ)->index; |
| |
| gst_pad_set_setcaps_function (new, |
| GST_DEBUG_FUNCPTR (gst_signal_processor_setcaps)); |
| |
| if (templ->direction == GST_PAD_SINK) { |
| GST_DEBUG ("added new sink pad"); |
| |
| gst_pad_set_event_function (new, |
| GST_DEBUG_FUNCPTR (gst_signal_processor_event)); |
| gst_pad_set_chain_function (new, |
| GST_DEBUG_FUNCPTR (gst_signal_processor_chain)); |
| gst_pad_set_activatepush_function (new, |
| GST_DEBUG_FUNCPTR (gst_signal_processor_sink_activate_push)); |
| } else { |
| GST_DEBUG ("added new src pad"); |
| |
| gst_pad_set_getrange_function (new, |
| GST_DEBUG_FUNCPTR (gst_signal_processor_getrange)); |
| gst_pad_set_activatepull_function (new, |
| GST_DEBUG_FUNCPTR (gst_signal_processor_src_activate_pull)); |
| } |
| |
| gst_element_add_pad (GST_ELEMENT (self), new); |
| } |
| |
| static void |
| gst_signal_processor_init (GstSignalProcessor * self, |
| GstSignalProcessorClass * klass) |
| { |
| GList *templates; |
| |
| templates = |
| gst_element_class_get_pad_template_list (GST_ELEMENT_CLASS (klass)); |
| |
| while (templates) { |
| GstPadTemplate *templ = GST_PAD_TEMPLATE (templates->data); |
| |
| gst_signal_processor_add_pad_from_template (self, templ); |
| templates = templates->next; |
| } |
| |
| self->state = GST_SIGNAL_PROCESSOR_STATE_NULL; |
| |
| self->audio_in = g_new0 (gfloat *, klass->num_audio_in); |
| self->control_in = g_new0 (gfloat, klass->num_control_in); |
| self->audio_out = g_new0 (gfloat *, klass->num_audio_out); |
| self->control_out = g_new0 (gfloat, klass->num_control_out); |
| |
| /* init */ |
| self->pending_in = klass->num_audio_in; |
| self->pending_out = 0; |
| |
| self->sample_rate = 0; |
| } |
| |
| static void |
| gst_signal_processor_finalize (GObject * object) |
| { |
| GstSignalProcessor *self = GST_SIGNAL_PROCESSOR (object); |
| |
| g_free (self->audio_in); |
| self->audio_in = NULL; |
| g_free (self->control_in); |
| self->control_in = NULL; |
| g_free (self->audio_out); |
| self->audio_out = NULL; |
| g_free (self->control_out); |
| self->control_out = NULL; |
| |
| G_OBJECT_CLASS (parent_class)->finalize (object); |
| } |
| |
| static gboolean |
| gst_signal_processor_setup (GstSignalProcessor * self, guint sample_rate) |
| { |
| GstSignalProcessorClass *klass; |
| gboolean ret = TRUE; |
| |
| klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); |
| |
| GST_INFO_OBJECT (self, "setup()"); |
| |
| g_return_val_if_fail (self->state == GST_SIGNAL_PROCESSOR_STATE_NULL, FALSE); |
| |
| if (klass->setup) |
| ret = klass->setup (self, sample_rate); |
| |
| if (!ret) |
| goto setup_failed; |
| |
| self->state = GST_SIGNAL_PROCESSOR_STATE_INITIALIZED; |
| |
| return ret; |
| |
| setup_failed: |
| { |
| GST_INFO_OBJECT (self, "setup() failed at %u Hz", sample_rate); |
| return ret; |
| } |
| } |
| |
| static gboolean |
| gst_signal_processor_start (GstSignalProcessor * self) |
| { |
| GstSignalProcessorClass *klass; |
| gboolean ret = TRUE; |
| |
| klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); |
| |
| g_return_val_if_fail (self->state == GST_SIGNAL_PROCESSOR_STATE_INITIALIZED, |
| FALSE); |
| |
| GST_INFO_OBJECT (self, "start()"); |
| |
| if (klass->start) |
| ret = klass->start (self); |
| |
| if (!ret) |
| goto start_failed; |
| |
| self->state = GST_SIGNAL_PROCESSOR_STATE_RUNNING; |
| |
| return ret; |
| |
| start_failed: |
| { |
| GST_INFO_OBJECT (self, "start() failed"); |
| return ret; |
| } |
| } |
| |
| static void |
| gst_signal_processor_stop (GstSignalProcessor * self) |
| { |
| GstSignalProcessorClass *klass; |
| GstElement *elem; |
| GList *sinks; |
| |
| klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); |
| elem = GST_ELEMENT (self); |
| |
| GST_INFO_OBJECT (self, "stop()"); |
| |
| g_return_if_fail (self->state == GST_SIGNAL_PROCESSOR_STATE_RUNNING); |
| |
| if (klass->stop) |
| klass->stop (self); |
| |
| for (sinks = elem->sinkpads; sinks; sinks = sinks->next) |
| /* force set_caps when going to RUNNING, see note in set_caps */ |
| gst_pad_set_caps (GST_PAD (sinks->data), NULL); |
| |
| /* should also flush our buffers perhaps? */ |
| |
| self->state = GST_SIGNAL_PROCESSOR_STATE_INITIALIZED; |
| } |
| |
| static void |
| gst_signal_processor_cleanup (GstSignalProcessor * self) |
| { |
| GstSignalProcessorClass *klass; |
| |
| klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); |
| |
| GST_INFO_OBJECT (self, "cleanup()"); |
| |
| g_return_if_fail (self->state == GST_SIGNAL_PROCESSOR_STATE_INITIALIZED); |
| |
| if (klass->cleanup) |
| klass->cleanup (self); |
| |
| self->state = GST_SIGNAL_PROCESSOR_STATE_NULL; |
| } |
| |
| static gboolean |
| gst_signal_processor_setcaps (GstPad * pad, GstCaps * caps) |
| { |
| GstSignalProcessor *self; |
| |
| self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); |
| |
| /* the whole processor has one caps; if the sample rate changes, let subclass |
| implementations know */ |
| if (!gst_caps_is_equal (caps, self->caps)) { |
| GstStructure *s; |
| gint sample_rate; |
| |
| GST_DEBUG_OBJECT (pad, "got caps %" GST_PTR_FORMAT, caps); |
| |
| s = gst_caps_get_structure (caps, 0); |
| if (!gst_structure_get_int (s, "rate", &sample_rate)) { |
| GST_WARNING ("got no sample-rate"); |
| goto impossible; |
| } |
| |
| GST_DEBUG_OBJECT (self, "Got rate=%d", sample_rate); |
| |
| if (GST_SIGNAL_PROCESSOR_IS_RUNNING (self)) |
| gst_signal_processor_stop (self); |
| if (GST_SIGNAL_PROCESSOR_IS_INITIALIZED (self)) |
| gst_signal_processor_cleanup (self); |
| |
| if (!gst_signal_processor_setup (self, sample_rate)) |
| goto start_failed; |
| |
| self->sample_rate = sample_rate; |
| gst_caps_replace (&self->caps, caps); |
| } else { |
| GST_DEBUG_OBJECT (self, "skipping, have caps already"); |
| } |
| |
| /* we use this method to manage the processor's state, hence the caps clearing |
| in stop(). so it can be that we enter here just to manage the processor's |
| state, to take it to RUNNING from already being INITIALIZED with the right |
| sample rate (e.g., when having gone PLAYING->READY->PLAYING). make sure |
| when we leave that the processor is RUNNING. */ |
| if (!GST_SIGNAL_PROCESSOR_IS_INITIALIZED (self) |
| && !gst_signal_processor_setup (self, self->sample_rate)) |
| goto start_failed; |
| if (!GST_SIGNAL_PROCESSOR_IS_RUNNING (self) |
| && !gst_signal_processor_start (self)) |
| goto start_failed; |
| |
| gst_object_unref (self); |
| |
| return TRUE; |
| |
| start_failed: |
| { |
| gst_object_unref (self); |
| return FALSE; |
| } |
| impossible: |
| { |
| g_critical ("something impossible happened"); |
| gst_object_unref (self); |
| return FALSE; |
| } |
| } |
| |
| static gboolean |
| gst_signal_processor_event (GstPad * pad, GstEvent * event) |
| { |
| GstSignalProcessor *self; |
| GstSignalProcessorClass *bclass; |
| gboolean ret; |
| |
| self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); |
| bclass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); |
| |
| /* this probably isn't the correct interface: what about return values, what |
| about overriding event_default */ |
| if (bclass->event) |
| bclass->event (self, event); |
| |
| switch (GST_EVENT_TYPE (event)) { |
| case GST_EVENT_FLUSH_START: |
| break; |
| case GST_EVENT_FLUSH_STOP: |
| /* clear errors now */ |
| self->flow_state = GST_FLOW_OK; |
| break; |
| default: |
| break; |
| } |
| ret = gst_pad_event_default (pad, event); |
| |
| gst_object_unref (self); |
| |
| return ret; |
| } |
| |
| static guint |
| gst_signal_processor_prepare (GstSignalProcessor * self) |
| { |
| GstElement *elem = (GstElement *) self; |
| GstSignalProcessorClass *klass; |
| GList *sinks, *srcs; |
| guint samples_avail = G_MAXUINT; |
| |
| klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); |
| |
| /* first, assign audio_in pointers, and determine the number of samples that |
| * we can process */ |
| for (sinks = elem->sinkpads; sinks; sinks = sinks->next) { |
| GstSignalProcessorPad *sinkpad; |
| |
| sinkpad = (GstSignalProcessorPad *) sinks->data; |
| g_assert (sinkpad->samples_avail > 0); |
| samples_avail = MIN (samples_avail, sinkpad->samples_avail); |
| self->audio_in[sinkpad->index] = sinkpad->data; |
| } |
| |
| if (samples_avail == G_MAXUINT) { |
| /* we don't have any sink pads, just choose a size -- should fix this |
| * function to have a suggested number of samples in the case of |
| * gst_pad_pull_range */ |
| samples_avail = 256; |
| } |
| |
| /* now assign output buffers. we can avoid allocation by reusing input |
| buffers, but only if process() can work in place, and if the input buffer |
| is the exact size of the number of samples we are processing. */ |
| sinks = elem->sinkpads; |
| srcs = elem->srcpads; |
| if (GST_SIGNAL_PROCESSOR_CLASS_CAN_PROCESS_IN_PLACE (klass)) { |
| while (sinks && srcs) { |
| GstSignalProcessorPad *sinkpad, *srcpad; |
| |
| sinkpad = (GstSignalProcessorPad *) sinks->data; |
| srcpad = (GstSignalProcessorPad *) srcs->data; |
| |
| if (GST_BUFFER_SIZE (sinkpad->pen) == samples_avail * sizeof (gfloat)) { |
| /* reusable, yay */ |
| g_assert (sinkpad->samples_avail == samples_avail); |
| srcpad->pen = sinkpad->pen; |
| sinkpad->pen = NULL; |
| self->audio_out[srcpad->index] = sinkpad->data; |
| self->pending_out++; |
| |
| srcs = srcs->next; |
| } |
| |
| sinks = sinks->next; |
| } |
| } |
| |
| /* now allocate for any remaining outputs */ |
| while (srcs) { |
| GstSignalProcessorPad *srcpad; |
| GstFlowReturn ret; |
| |
| srcpad = (GstSignalProcessorPad *) srcs->data; |
| |
| ret = |
| gst_pad_alloc_buffer_and_set_caps (GST_PAD (srcpad), -1, |
| samples_avail * sizeof (gfloat), GST_PAD_CAPS (srcpad), &srcpad->pen); |
| |
| if (ret != GST_FLOW_OK) { |
| self->flow_state = ret; |
| return 0; |
| } else { |
| self->audio_out[srcpad->index] = (gfloat *) GST_BUFFER_DATA (srcpad->pen); |
| self->pending_out++; |
| } |
| |
| srcs = srcs->next; |
| } |
| |
| return samples_avail; |
| } |
| |
| static void |
| gst_signal_processor_update_inputs (GstSignalProcessor * self, guint nprocessed) |
| { |
| GstElement *elem = (GstElement *) self; |
| GList *sinks; |
| |
| for (sinks = elem->sinkpads; sinks; sinks = sinks->next) { |
| GstSignalProcessorPad *sinkpad; |
| |
| sinkpad = (GstSignalProcessorPad *) sinks->data; |
| g_assert (sinkpad->samples_avail >= nprocessed); |
| |
| if (sinkpad->pen && sinkpad->samples_avail == nprocessed) { |
| /* used up this buffer, unpen */ |
| gst_buffer_unref (sinkpad->pen); |
| sinkpad->pen = NULL; |
| } |
| |
| if (!sinkpad->pen) { |
| /* this buffer was used up */ |
| self->pending_in++; |
| sinkpad->data = NULL; |
| sinkpad->samples_avail = 0; |
| } else { |
| /* advance ->data pointers and decrement ->samples_avail, unreffing buffer |
| if no samples are left */ |
| sinkpad->samples_avail -= nprocessed; |
| sinkpad->data += nprocessed; /* gfloat* arithmetic */ |
| } |
| } |
| } |
| |
| static void |
| gst_signal_processor_process (GstSignalProcessor * self) |
| { |
| GstElement *elem; |
| GstSignalProcessorClass *klass; |
| guint nframes; |
| |
| g_return_if_fail (self->pending_in == 0); |
| g_return_if_fail (self->pending_out == 0); |
| |
| elem = GST_ELEMENT (self); |
| |
| nframes = gst_signal_processor_prepare (self); |
| if (G_UNLIKELY (nframes == 0)) |
| goto flow_error; |
| |
| klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); |
| |
| GST_LOG_OBJECT (self, "process(%u)", nframes); |
| |
| klass->process (self, nframes); |
| |
| gst_signal_processor_update_inputs (self, nframes); |
| |
| return; |
| |
| flow_error: |
| { |
| GST_WARNING ("gst_pad_alloc_buffer_and_set_caps() returned %d", |
| self->flow_state); |
| return; |
| } |
| } |
| |
| static void |
| gst_signal_processor_pen_buffer (GstSignalProcessor * self, GstPad * pad, |
| GstBuffer * buffer) |
| { |
| GstSignalProcessorPad *spad = (GstSignalProcessorPad *) pad; |
| |
| if (spad->pen) |
| goto had_buffer; |
| |
| /* keep the reference */ |
| spad->pen = buffer; |
| spad->data = (gfloat *) GST_BUFFER_DATA (buffer); |
| spad->samples_avail = GST_BUFFER_SIZE (buffer) / sizeof (float); |
| |
| g_assert (self->pending_in != 0); |
| |
| self->pending_in--; |
| |
| return; |
| |
| /* ERRORS */ |
| had_buffer: |
| { |
| GST_WARNING ("Pad %s:%s already has penned buffer", |
| GST_DEBUG_PAD_NAME (pad)); |
| gst_buffer_unref (buffer); |
| return; |
| } |
| } |
| |
| static void |
| gst_signal_processor_flush (GstSignalProcessor * self) |
| { |
| GList *pads; |
| GstSignalProcessorClass *klass; |
| |
| klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); |
| |
| GST_INFO_OBJECT (self, "flush()"); |
| |
| for (pads = GST_ELEMENT (self)->pads; pads; pads = pads->next) { |
| GstSignalProcessorPad *spad = (GstSignalProcessorPad *) pads->data; |
| |
| if (spad->pen) { |
| gst_buffer_unref (spad->pen); |
| spad->pen = NULL; |
| spad->data = NULL; |
| spad->samples_avail = 0; |
| } |
| } |
| |
| self->pending_out = 0; |
| self->pending_in = klass->num_audio_in; |
| } |
| |
| static void |
| gst_signal_processor_do_pulls (GstSignalProcessor * self, guint nframes) |
| { |
| GList *sinkpads; |
| |
| /* FIXME: not threadsafe atm */ |
| |
| sinkpads = GST_ELEMENT (self)->sinkpads; |
| |
| for (; sinkpads; sinkpads = sinkpads->next) { |
| GstSignalProcessorPad *spad = (GstSignalProcessorPad *) sinkpads->data; |
| GstFlowReturn ret = GST_FLOW_OK; |
| GstBuffer *buf; |
| |
| if (spad->pen) { |
| g_warning ("Unexpectedly full buffer pen for pad %s:%s", |
| GST_DEBUG_PAD_NAME (spad)); |
| continue; |
| } |
| |
| ret = gst_pad_pull_range (GST_PAD (spad), -1, nframes, &buf); |
| |
| if (ret != GST_FLOW_OK) { |
| gst_signal_processor_flush (self); |
| self->flow_state = ret; |
| return; |
| } else if (!buf) { |
| g_critical ("Pull failed to make a buffer!"); |
| self->flow_state = GST_FLOW_ERROR; |
| return; |
| } else { |
| gst_signal_processor_pen_buffer (self, GST_PAD (spad), buf); |
| } |
| } |
| |
| if (self->pending_in != 0) { |
| g_critical ("Something wierd happened..."); |
| self->flow_state = GST_FLOW_ERROR; |
| } else { |
| gst_signal_processor_process (self); |
| } |
| } |
| |
| static GstFlowReturn |
| gst_signal_processor_getrange (GstPad * pad, guint64 offset, |
| guint length, GstBuffer ** buffer) |
| { |
| GstSignalProcessor *self; |
| GstSignalProcessorPad *spad = (GstSignalProcessorPad *) pad; |
| GstFlowReturn ret = GST_FLOW_ERROR; |
| |
| self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); |
| |
| if (spad->pen) { |
| *buffer = spad->pen; |
| spad->pen = NULL; |
| g_assert (self->pending_out != 0); |
| self->pending_out--; |
| ret = GST_FLOW_OK; |
| } else { |
| gst_signal_processor_do_pulls (self, length); |
| if (!spad->pen) { |
| /* this is an error condition */ |
| *buffer = NULL; |
| ret = self->flow_state; |
| } else { |
| *buffer = spad->pen; |
| spad->pen = NULL; |
| self->pending_out--; |
| ret = GST_FLOW_OK; |
| } |
| } |
| |
| GST_DEBUG_OBJECT (self, "returns %s", gst_flow_get_name (ret)); |
| |
| gst_object_unref (self); |
| |
| return ret; |
| } |
| |
| static void |
| gst_signal_processor_do_pushes (GstSignalProcessor * self) |
| { |
| GList *srcpads; |
| |
| /* not threadsafe atm */ |
| |
| srcpads = GST_ELEMENT (self)->srcpads; |
| |
| for (; srcpads; srcpads = srcpads->next) { |
| GstSignalProcessorPad *spad = (GstSignalProcessorPad *) srcpads->data; |
| GstFlowReturn ret = GST_FLOW_OK; |
| GstBuffer *buffer; |
| |
| if (!spad->pen) { |
| g_warning ("Unexpectedly empty buffer pen for pad %s:%s", |
| GST_DEBUG_PAD_NAME (spad)); |
| continue; |
| } |
| |
| /* take buffer from pen */ |
| buffer = spad->pen; |
| spad->pen = NULL; |
| |
| ret = gst_pad_push (GST_PAD (spad), buffer); |
| |
| if (ret != GST_FLOW_OK) { |
| gst_signal_processor_flush (self); |
| self->flow_state = ret; |
| return; |
| } else { |
| g_assert (self->pending_out > 0); |
| self->pending_out--; |
| } |
| } |
| |
| if (self->pending_out != 0) { |
| g_critical ("Something wierd happened..."); |
| self->flow_state = GST_FLOW_ERROR; |
| } |
| } |
| |
| static GstFlowReturn |
| gst_signal_processor_chain (GstPad * pad, GstBuffer * buffer) |
| { |
| GstSignalProcessor *self; |
| |
| self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); |
| |
| gst_signal_processor_pen_buffer (self, pad, buffer); |
| |
| if (self->pending_in == 0) { |
| gst_signal_processor_process (self); |
| |
| gst_signal_processor_do_pushes (self); |
| } |
| |
| gst_object_unref (self); |
| |
| return self->flow_state; |
| } |
| |
| static void |
| gst_signal_processor_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec) |
| { |
| /* GstSignalProcessor *self = GST_SIGNAL_PROCESSOR (object); */ |
| |
| switch (prop_id) { |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| gst_signal_processor_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec) |
| { |
| /* GstSignalProcessor *self = GST_SIGNAL_PROCESSOR (object); */ |
| |
| switch (prop_id) { |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static gboolean |
| gst_signal_processor_sink_activate_push (GstPad * pad, gboolean active) |
| { |
| gboolean result = TRUE; |
| GstSignalProcessor *self; |
| GstSignalProcessorClass *bclass; |
| |
| self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); |
| bclass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); |
| |
| if (active) { |
| if (self->mode == GST_ACTIVATE_NONE) { |
| self->mode = GST_ACTIVATE_PUSH; |
| result = TRUE; |
| } else if (self->mode == GST_ACTIVATE_PUSH) { |
| result = TRUE; |
| } else { |
| g_warning ("foo"); |
| result = FALSE; |
| } |
| } else { |
| if (self->mode == GST_ACTIVATE_NONE) { |
| result = TRUE; |
| } else if (self->mode == GST_ACTIVATE_PUSH) { |
| self->mode = GST_ACTIVATE_NONE; |
| result = TRUE; |
| } else { |
| g_warning ("foo"); |
| result = FALSE; |
| } |
| } |
| |
| GST_DEBUG_OBJECT (self, "result : %d", result); |
| |
| gst_object_unref (self); |
| |
| return result; |
| } |
| |
| static gboolean |
| gst_signal_processor_src_activate_pull (GstPad * pad, gboolean active) |
| { |
| gboolean result = TRUE; |
| GstSignalProcessor *self; |
| GstSignalProcessorClass *bclass; |
| |
| self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); |
| bclass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); |
| |
| if (active) { |
| if (self->mode == GST_ACTIVATE_NONE) { |
| GList *l; |
| |
| for (l = GST_ELEMENT (self)->sinkpads; l; l = l->next) |
| result &= gst_pad_activate_pull (pad, active); |
| if (result) |
| self->mode = GST_ACTIVATE_PULL; |
| } else if (self->mode == GST_ACTIVATE_PULL) { |
| result = TRUE; |
| } else { |
| g_warning ("foo"); |
| result = FALSE; |
| } |
| } else { |
| if (self->mode == GST_ACTIVATE_NONE) { |
| result = TRUE; |
| } else if (self->mode == GST_ACTIVATE_PULL) { |
| GList *l; |
| |
| for (l = GST_ELEMENT (self)->sinkpads; l; l = l->next) |
| result &= gst_pad_activate_pull (pad, active); |
| if (result) |
| self->mode = GST_ACTIVATE_NONE; |
| result = TRUE; |
| } else { |
| g_warning ("foo"); |
| result = FALSE; |
| } |
| } |
| |
| GST_DEBUG_OBJECT (self, "result : %d", result); |
| |
| gst_object_unref (self); |
| |
| return result; |
| } |
| |
| static GstStateChangeReturn |
| gst_signal_processor_change_state (GstElement * element, |
| GstStateChange transition) |
| { |
| GstSignalProcessor *self; |
| GstStateChangeReturn result; |
| |
| self = GST_SIGNAL_PROCESSOR (element); |
| |
| switch (transition) { |
| case GST_STATE_CHANGE_NULL_TO_READY: |
| break; |
| case GST_STATE_CHANGE_READY_TO_PAUSED: |
| self->flow_state = GST_FLOW_OK; |
| break; |
| case GST_STATE_CHANGE_PAUSED_TO_PLAYING: |
| break; |
| default: |
| break; |
| } |
| |
| if ((result = |
| GST_ELEMENT_CLASS (parent_class)->change_state (element, |
| transition)) == GST_STATE_CHANGE_FAILURE) |
| goto failure; |
| |
| switch (transition) { |
| case GST_STATE_CHANGE_PLAYING_TO_PAUSED: |
| break; |
| case GST_STATE_CHANGE_PAUSED_TO_READY: |
| if (GST_SIGNAL_PROCESSOR_IS_RUNNING (self)) |
| gst_signal_processor_stop (self); |
| gst_signal_processor_flush (self); |
| break; |
| case GST_STATE_CHANGE_READY_TO_NULL: |
| if (GST_SIGNAL_PROCESSOR_IS_INITIALIZED (self)) |
| gst_signal_processor_cleanup (self); |
| break; |
| default: |
| break; |
| } |
| |
| return result; |
| |
| /* ERRORS */ |
| failure: |
| { |
| GST_DEBUG_OBJECT (element, "parent failed state change"); |
| return result; |
| } |
| } |