diff options
author | Andy Wingo <wingo@pobox.com> | 2007-02-02 13:28:05 +0000 |
---|---|---|
committer | Andy Wingo <wingo@pobox.com> | 2007-02-02 13:28:05 +0000 |
commit | 91e91243a9f7be56df0be83f39a004f04b733294 (patch) | |
tree | a7642d36ecb5c06f136f8dcd5f39f9260d733545 | |
parent | 144b320afec7e17795a855fd2468dc9f23196a14 (diff) | |
download | gst-plugins-bad-91e91243a9f7be56df0be83f39a004f04b733294.tar.gz gst-plugins-bad-91e91243a9f7be56df0be83f39a004f04b733294.tar.bz2 gst-plugins-bad-91e91243a9f7be56df0be83f39a004f04b733294.zip |
configure.ac: ext/Makefile.am
Original commit message from CVS:
2007-02-02 Andy Wingo <wingo@pobox.com>
* configure.ac:
* ext/Makefile.am
* ext/sndfile/Makefile.am:
* ext/sndfile/gstsf.c:
* ext/sndfile/gstsf.h:
* ext/sndfile/gstsfsink.c:
* ext/sndfile/gstsfsink.h: Port sfsink to 0.10. Works in pull or
push mode with interleaved float or int data.
-rw-r--r-- | ChangeLog | 11 | ||||
-rw-r--r-- | configure.ac | 10 | ||||
-rw-r--r-- | ext/Makefile.am | 9 | ||||
-rw-r--r-- | ext/sndfile/Makefile.am | 8 | ||||
-rw-r--r-- | ext/sndfile/gstsf.c | 774 | ||||
-rw-r--r-- | ext/sndfile/gstsf.h | 83 | ||||
-rw-r--r-- | ext/sndfile/gstsfsink.c | 528 | ||||
-rw-r--r-- | ext/sndfile/gstsfsink.h | 73 |
8 files changed, 644 insertions, 852 deletions
@@ -1,3 +1,14 @@ +2007-02-02 Andy Wingo <wingo@pobox.com> + + * configure.ac: + * ext/Makefile.am + * ext/sndfile/Makefile.am: + * ext/sndfile/gstsf.c: + * ext/sndfile/gstsf.h: + * ext/sndfile/gstsfsink.c: + * ext/sndfile/gstsfsink.h: Port sfsink to 0.10. Works in pull or + push mode with interleaved float or int data. + 2007-02-02 Tim-Philipp Müller <tim at centricular dot net> * ext/alsaspdif/alsaspdifsink.c: (plugin_init): diff --git a/configure.ac b/configure.ac index ec10ae01..da5a78c0 100644 --- a/configure.ac +++ b/configure.ac @@ -750,6 +750,14 @@ GST_CHECK_FEATURE(SDL, [SDL plug-in], sdlvideosink sdlaudiosink, [ AM_PATH_SDL(, HAVE_SDL=yes, HAVE_SDL=no) ]) +dnl *** sndfile *** +translit(dnm, m, l) AM_CONDITIONAL(USE_SNDFILE, true) +GST_CHECK_FEATURE(SNDFILE, [sndfile plug-in], sfsrc sfsink, [ + PKG_CHECK_MODULES(SNDFILE, sndfile >= 1.0.0, HAVE_SNDFILE="yes", HAVE_SNDFILE="no") + AC_SUBST(SNDFILE_CFLAGS) + AC_SUBST(SNDFILE_LIBS) +]) + dnl *** soundtouch *** translit(dnm, m, l) AM_CONDITIONAL(USE_SOUNDTOUCH, true) GST_CHECK_FEATURE(SOUNDTOUCH, [soundtouch plug-in], soundtouch, [ @@ -889,6 +897,7 @@ AM_CONDITIONAL(USE_MUSICBRAINZ, false) AM_CONDITIONAL(USE_MYTHTV, false) AM_CONDITIONAL(USE_NEON, false) AM_CONDITIONAL(USE_SDL, false) +AM_CONDITIONAL(USE_SNDFILE, false) AM_CONDITIONAL(USE_SOUNDTOUCH, false) AM_CONDITIONAL(USE_SPC, false) AM_CONDITIONAL(USE_SWFDEC, false) @@ -1005,6 +1014,7 @@ ext/musicbrainz/Makefile ext/mythtv/Makefile ext/neon/Makefile ext/sdl/Makefile +ext/sndfile/Makefile ext/soundtouch/Makefile ext/spc/Makefile ext/swfdec/Makefile diff --git a/ext/Makefile.am b/ext/Makefile.am index 75b5323b..4e960c8e 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -202,11 +202,11 @@ SHOUT_DIR= SMOOTHWAVE_DIR= # endif -# if USE_SNDFILE -# SNDFILE_DIR=sndfile -# else +if USE_SNDFILE +SNDFILE_DIR=sndfile +else SNDFILE_DIR= -# endif +endif if USE_SOUNDTOUCH SOUNDTOUCH_DIR=soundtouch @@ -316,6 +316,7 @@ DIST_SUBDIRS= \ mythtv \ neon \ sdl \ + sndfile \ soundtouch \ spc \ swfdec \ diff --git a/ext/sndfile/Makefile.am b/ext/sndfile/Makefile.am index 23a86d71..bd051153 100644 --- a/ext/sndfile/Makefile.am +++ b/ext/sndfile/Makefile.am @@ -1,9 +1,9 @@ plugin_LTLIBRARIES = libgstsndfile.la -libgstsndfile_la_SOURCES = gstsf.c -libgstsndfile_la_CFLAGS = $(GST_CFLAGS) $(SNDFILE_CFLAGS) -libgstsndfile_la_LIBADD = $(SNDFILE_LIBS) +libgstsndfile_la_SOURCES = gstsf.c gstsfsink.c +libgstsndfile_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(SNDFILE_CFLAGS) +libgstsndfile_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(SNDFILE_LIBS) libgstsndfile_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -noinst_HEADERS = gstsf.h +noinst_HEADERS = gstsf.h gstsfsink.h diff --git a/ext/sndfile/gstsf.c b/ext/sndfile/gstsf.c index e6313ed5..084e69ac 100644 --- a/ext/sndfile/gstsf.c +++ b/ext/sndfile/gstsf.c @@ -22,51 +22,12 @@ #include "config.h" #endif -#include "gst/gst-i18n-plugin.h" #include <string.h> -#include <gst/gst.h> - -#include <gst/audio/audio.h> #include "gstsf.h" -static const GstElementDetails sfsrc_details = -GST_ELEMENT_DETAILS ("Sndfile source", - "Source/Audio", - "Read audio streams from disk using libsndfile", - "Andy Wingo <wingo at pobox dot com>"); - -static const GstElementDetails sfsink_details = -GST_ELEMENT_DETAILS ("Sndfile sink", - "Sink/Audio", - "Write audio streams to disk using libsndfile", - "Andy Wingo <wingo at pobox dot com>"); - -enum -{ - ARG_0, - ARG_LOCATION, - ARG_MAJOR_TYPE, - ARG_MINOR_TYPE, - ARG_LOOP, - ARG_CREATE_PADS -}; - -static GstStaticPadTemplate sf_src_factory = GST_STATIC_PAD_TEMPLATE ("src%d", - GST_PAD_SRC, - GST_PAD_REQUEST, - GST_STATIC_CAPS (GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS) - ); - -static GstStaticPadTemplate sf_sink_factory = GST_STATIC_PAD_TEMPLATE ("sink%d", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS (GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS) - ); - -#define GST_TYPE_SF_MAJOR_TYPES (gst_sf_major_types_get_type()) -static GType +GType gst_sf_major_types_get_type (void) { static GType sf_major_types_type = 0; @@ -94,11 +55,11 @@ gst_sf_major_types_get_type (void) if (k > 0 && strcmp (sf_major_types[k].value_nick, sf_major_types[k - 1].value_nick) == 0) { - g_free (sf_major_types[k].value_nick); + g_free ((gchar *) sf_major_types[k].value_nick); sf_major_types[k].value_nick = g_strconcat (sf_major_types[k - 1].value_nick, "-", sf_major_types[k].value_name, NULL); - g_strcanon (sf_major_types[k].value_nick, + g_strcanon ((gchar *) sf_major_types[k].value_nick, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-'); } } @@ -109,8 +70,7 @@ gst_sf_major_types_get_type (void) return sf_major_types_type; } -#define GST_TYPE_SF_MINOR_TYPES (gst_sf_minor_types_get_type()) -static GType +GType gst_sf_minor_types_get_type (void) { static GType sf_minor_types_type = 0; @@ -131,8 +91,8 @@ gst_sf_minor_types_get_type (void) sf_minor_types[k].value = format_info.format; sf_minor_types[k].value_name = g_strdup (format_info.name); sf_minor_types[k].value_nick = g_ascii_strdown (format_info.name, -1); - g_strcanon (sf_minor_types[k].value_nick, G_CSET_a_2_z G_CSET_DIGITS "-", - '-'); + g_strcanon ((gchar *) sf_minor_types[k].value_nick, + G_CSET_a_2_z G_CSET_DIGITS "-", '-'); } sf_minor_types_type = @@ -141,731 +101,13 @@ gst_sf_minor_types_get_type (void) return sf_minor_types_type; } -static void gst_sfsrc_base_init (gpointer g_class); -static void gst_sfsink_base_init (gpointer g_class); -static void gst_sf_class_init (GstSFClass * klass); -static void gst_sf_init (GstSF * this); -static void gst_sf_dispose (GObject * object); -static void gst_sf_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_sf_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static GstClock *gst_sf_get_clock (GstElement * element); -static void gst_sf_set_clock (GstElement * element, GstClock * clock); -static GstPad *gst_sf_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * unused); -static void gst_sf_release_request_pad (GstElement * element, GstPad * pad); -static GstStateChangeReturn gst_sf_change_state (GstElement * element, - GstStateChange transition); - -static GstPadLinkReturn gst_sf_link (GstPad * pad, const GstCaps * caps); - -static void gst_sf_loop (GstElement * element); - -static GstClockTime gst_sf_get_time (GstClock * clock, gpointer data); - -static gboolean gst_sf_open_file (GstSF * this); -static void gst_sf_close_file (GstSF * this); - -static GstElementClass *parent_class = NULL; - -GST_DEBUG_CATEGORY_STATIC (gstsf_debug); -#define INFO(...) \ - GST_CAT_LEVEL_LOG (gstsf_debug, GST_LEVEL_INFO, NULL, __VA_ARGS__) -#define INFO_OBJ(obj,...) \ - GST_CAT_LEVEL_LOG (gstsf_debug, GST_LEVEL_INFO, obj, __VA_ARGS__) - -GType -gst_sf_get_type (void) -{ - static GType sf_type = 0; - - if (!sf_type) { - static const GTypeInfo sf_info = { - sizeof (GstSFClass), NULL, - NULL, - (GClassInitFunc) NULL, /* don't even initialize the class */ - NULL, - NULL, - sizeof (GstSF), - 0, - (GInstanceInitFunc) NULL /* abstract base class */ - }; - - sf_type = g_type_register_static (GST_TYPE_ELEMENT, "GstSF", &sf_info, 0); - } - return sf_type; -} - -GType -gst_sfsrc_get_type (void) -{ - static GType sfsrc_type = 0; - - if (!sfsrc_type) { - static const GTypeInfo sfsrc_info = { - sizeof (GstSFClass), - gst_sfsrc_base_init, - NULL, - (GClassInitFunc) gst_sf_class_init, - NULL, - NULL, - sizeof (GstSF), - 0, - (GInstanceInitFunc) gst_sf_init, - }; - - sfsrc_type = - g_type_register_static (GST_TYPE_SF, "GstSFSrc", &sfsrc_info, 0); - } - return sfsrc_type; -} - -GType -gst_sfsink_get_type (void) -{ - static GType sfsink_type = 0; - - if (!sfsink_type) { - static const GTypeInfo sfsink_info = { - sizeof (GstSFClass), - gst_sfsink_base_init, - NULL, - (GClassInitFunc) gst_sf_class_init, - NULL, - NULL, - sizeof (GstSF), - 0, - (GInstanceInitFunc) gst_sf_init, - }; - - sfsink_type = - g_type_register_static (GST_TYPE_SF, "GstSFSink", &sfsink_info, 0); - } - return sfsink_type; -} - -static void -gst_sfsrc_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sf_src_factory)); - gst_element_class_set_details (element_class, &sfsrc_details); -} - -static void -gst_sfsink_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sf_sink_factory)); - gst_element_class_set_details (element_class, &sfsink_details); -} - -static void -gst_sf_class_init (GstSFClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - GParamSpec *pspec; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - - parent_class = g_type_class_peek_parent (klass); - - gst_element_class_install_std_props (gstelement_class, "location", - ARG_LOCATION, G_PARAM_READWRITE, NULL); - pspec = g_param_spec_enum - ("major-type", "Major type", "Major output type", GST_TYPE_SF_MAJOR_TYPES, - SF_FORMAT_WAV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (gobject_class, ARG_MAJOR_TYPE, pspec); - pspec = g_param_spec_enum - ("minor-type", "Minor type", "Minor output type", GST_TYPE_SF_MINOR_TYPES, - SF_FORMAT_FLOAT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (gobject_class, ARG_MINOR_TYPE, pspec); - - if (G_TYPE_FROM_CLASS (klass) == GST_TYPE_SFSRC) { - pspec = g_param_spec_boolean ("loop", "Loop?", "Loop the output?", - FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (gobject_class, ARG_LOOP, pspec); - pspec = - g_param_spec_boolean ("create-pads", "Create pads?", - "Create one pad for each channel in the sound file?", TRUE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (gobject_class, ARG_CREATE_PADS, pspec); - } - - gobject_class->dispose = gst_sf_dispose; - gobject_class->set_property = gst_sf_set_property; - gobject_class->get_property = gst_sf_get_property; - - gstelement_class->get_clock = gst_sf_get_clock; - gstelement_class->set_clock = gst_sf_set_clock; - gstelement_class->change_state = gst_sf_change_state; - gstelement_class->request_new_pad = gst_sf_request_new_pad; - gstelement_class->release_pad = gst_sf_release_request_pad; -} - -static void -gst_sf_init (GstSF * this) -{ - gst_element_set_loop_function (GST_ELEMENT (this), gst_sf_loop); - this->provided_clock = gst_audio_clock_new ("sfclock", gst_sf_get_time, this); - gst_object_set_parent (GST_OBJECT (this->provided_clock), GST_OBJECT (this)); -} - -static void -gst_sf_dispose (GObject * object) -{ - GstSF *this = (GstSF *) object; - - if (this->provided_clock) { - gst_object_unparent (GST_OBJECT (this->provided_clock)); - this->provided_clock = NULL; - } - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -gst_sf_set_property (GObject * object, guint prop_id, const GValue * value, - GParamSpec * pspec) -{ - GstSF *this = GST_SF (object); - - switch (prop_id) { - case ARG_LOCATION: - if (GST_OBJECT_FLAG_IS_SET (object, GST_SF_OPEN)) - gst_sf_close_file (this); - if (this->filename) - g_free (this->filename); - - if (g_value_get_string (value)) - this->filename = g_strdup (g_value_get_string (value)); - else - this->filename = NULL; - - if (this->filename) - gst_sf_open_file (this); - break; - - case ARG_MAJOR_TYPE: - this->format_major = g_value_get_enum (value); - break; - - case ARG_MINOR_TYPE: - this->format_subtype = g_value_get_enum (value); - break; - - case ARG_LOOP: - this->loop = g_value_get_boolean (value); - break; - - case ARG_CREATE_PADS: - this->create_pads = g_value_get_boolean (value); - if (this->file && this->create_pads) { - int i; - - for (i = g_list_length (this->channels); i < this->numchannels; i++) - gst_element_get_request_pad ((GstElement *) this, "src%d"); - } - break; - - default: - break; - } -} - -static void -gst_sf_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstSF *this = GST_SF (object); - - switch (prop_id) { - case ARG_LOCATION: - g_value_set_string (value, this->filename); - break; - - case ARG_MAJOR_TYPE: - g_value_set_enum (value, this->format_major); - break; - - case ARG_MINOR_TYPE: - g_value_set_enum (value, this->format_subtype); - break; - - case ARG_LOOP: - g_value_set_boolean (value, this->loop); - break; - - case ARG_CREATE_PADS: - g_value_set_boolean (value, this->create_pads); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static GstClock * -gst_sf_get_clock (GstElement * element) -{ - GstSF *this = GST_SF (element); - - return this->provided_clock; -} - -static void -gst_sf_set_clock (GstElement * element, GstClock * clock) -{ - GstSF *this = GST_SF (element); - - this->clock = clock; -} - -static GstClockTime -gst_sf_get_time (GstClock * clock, gpointer data) -{ - GstSF *this = GST_SF (data); - - return this->time; -} - -static GstStateChangeReturn -gst_sf_change_state (GstElement * element, GstStateChange transition) -{ - GstSF *this = GST_SF (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - gst_audio_clock_set_active (GST_AUDIO_CLOCK (this->provided_clock), TRUE); - break; - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - gst_audio_clock_set_active (GST_AUDIO_CLOCK (this->provided_clock), - FALSE); - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_NULL: - if (GST_OBJECT_FLAG_IS_SET (this, GST_SF_OPEN)) - gst_sf_close_file (this); - break; - } - - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - return GST_STATE_CHANGE_SUCCESS; -} - -static GstPad * -gst_sf_request_new_pad (GstElement * element, GstPadTemplate * templ, - const gchar * unused) -{ - gchar *name; - GstSF *this; - GstSFChannel *channel; - - this = GST_SF (element); - channel = g_new0 (GstSFChannel, 1); - - if (templ->direction == GST_PAD_SINK) { - /* we have an SFSink */ - name = g_strdup_printf ("sink%d", this->channelcount); - this->numchannels++; - if (this->file) { - gst_sf_close_file (this); - gst_sf_open_file (this); - } - } else { - /* we have an SFSrc */ - name = g_strdup_printf ("src%d", this->channelcount); - } - - channel->pad = gst_pad_new_from_template (templ, name); - gst_element_add_pad (GST_ELEMENT (this), channel->pad); - gst_pad_set_link_function (channel->pad, gst_sf_link); - - this->channels = g_list_append (this->channels, channel); - this->channelcount++; - - INFO_OBJ (element, "added pad %s\n", name); - - g_free (name); - return channel->pad; -} - -static void -gst_sf_release_request_pad (GstElement * element, GstPad * pad) -{ - GstSF *this; - GstSFChannel *channel = NULL; - GList *l; - - this = GST_SF (element); - - if (GST_STATE (element) == GST_STATE_PLAYING) { - g_warning - ("You can't release a request pad if the element is PLAYING, sorry."); - return; - } - - for (l = this->channels; l; l = l->next) { - if (GST_SF_CHANNEL (l)->pad == pad) { - channel = GST_SF_CHANNEL (l); - break; - } - } - - g_return_if_fail (channel != NULL); - - INFO_OBJ (element, "Releasing request pad %s", GST_PAD_NAME (channel->pad)); - - if (GST_OBJECT_FLAG_IS_SET (element, GST_SF_OPEN)) - gst_sf_close_file (this); - - gst_element_remove_pad (element, channel->pad); - this->channels = g_list_remove (this->channels, channel); - this->numchannels--; - g_free (channel); -} - -static GstPadLinkReturn -gst_sf_link (GstPad * pad, const GstCaps * caps) -{ - GstSF *this = (GstSF *) GST_OBJECT_PARENT (pad); - GstStructure *structure; - - structure = gst_caps_get_structure (caps, 0); - - gst_structure_get_int (structure, "rate", &this->rate); - gst_structure_get_int (structure, "buffer-frames", &this->buffer_frames); - - INFO_OBJ (this, "linked pad %s:%s with fixed caps, rate=%d, frames=%d", - GST_DEBUG_PAD_NAME (pad), this->rate, this->buffer_frames); - - if (this->numchannels) { - /* we can go ahead and allocate our buffer */ - if (this->buffer) - g_free (this->buffer); - this->buffer = - g_malloc (this->numchannels * this->buffer_frames * sizeof (float)); - memset (this->buffer, 0, - this->numchannels * this->buffer_frames * sizeof (float)); - } - return GST_PAD_LINK_OK; -} - -static gboolean -gst_sf_open_file (GstSF * this) -{ - int mode; - SF_INFO info; - - g_return_val_if_fail (!GST_OBJECT_FLAG_IS_SET (this, GST_SF_OPEN), FALSE); - - this->time = 0; - - if (!this->filename) { - GST_ELEMENT_ERROR (this, RESOURCE, NOT_FOUND, - (_("No filename specified.")), (NULL)); - return FALSE; - } - - if (GST_IS_SFSRC (this)) { - mode = SFM_READ; - info.format = 0; - } else { - if (!this->rate) { - INFO_OBJ (this, "Not opening %s yet because caps are not set", - this->filename); - return FALSE; - } else if (!this->numchannels) { - INFO_OBJ (this, "Not opening %s yet because we have no input channels", - this->filename); - return FALSE; - } - - mode = SFM_WRITE; - this->format = this->format_major | this->format_subtype; - info.samplerate = this->rate; - info.channels = this->numchannels; - info.format = this->format; - - INFO_OBJ (this, "Opening %s with rate %d, %d channels, format 0x%x", - this->filename, info.samplerate, info.channels, info.format); - - if (!sf_format_check (&info)) { - GST_ELEMENT_ERROR (this, STREAM, ENCODE, (NULL), - ("Input parameters (rate:%d, channels:%d, format:0x%x) invalid", - info.samplerate, info.channels, info.format)); - return FALSE; - } - } - - this->file = sf_open (this->filename, mode, &info); - - if (!this->file) { - GST_ELEMENT_ERROR (this, RESOURCE, OPEN_WRITE, - (_("Could not open file \"%s\" for writing."), this->filename), - ("soundfile error: %s", sf_strerror (NULL))); - return FALSE; - } - - if (GST_IS_SFSRC (this)) { - GList *l = NULL; - - /* the number of channels in the file can be different than the number of - * pads */ - this->numchannels = info.channels; - this->rate = info.samplerate; - - if (this->create_pads) { - int i; - - for (i = g_list_length (this->channels); i < this->numchannels; i++) - gst_element_get_request_pad ((GstElement *) this, "src%d"); - } - - for (l = this->channels; l; l = l->next) - /* queue the need to set caps */ - GST_SF_CHANNEL (l)->caps_set = FALSE; - } - - GST_OBJECT_FLAG_SET (this, GST_SF_OPEN); - - return TRUE; -} - -static void -gst_sf_close_file (GstSF * this) -{ - int err = 0; - - g_return_if_fail (GST_OBJECT_FLAG_IS_SET (this, GST_SF_OPEN)); - - INFO_OBJ (this, "Closing file %s", this->filename); - - if ((err = sf_close (this->file))) - GST_ELEMENT_ERROR (this, RESOURCE, CLOSE, - ("Could not close file file \"%s\".", this->filename), - ("soundfile error: %s", strerror (err))); - else - GST_OBJECT_FLAG_UNSET (this, GST_SF_OPEN); - - this->file = NULL; - if (this->buffer) - g_free (this->buffer); - this->buffer = NULL; -} - -static void -gst_sf_loop (GstElement * element) -{ - GstSF *this; - GList *l = NULL; - - this = (GstSF *) element; - - if (this->channels == NULL) { - GST_ELEMENT_ERROR (element, CORE, PAD, (NULL), - ("You must connect at least one pad to sndfile elements.")); - return; - } - - if (GST_IS_SFSRC (this)) { - sf_count_t read; - gint i, j; - int eos = 0; - int buffer_frames = this->buffer_frames; - int nchannels = this->numchannels; - GstSFChannel *channel = NULL; - gfloat *data; - gfloat *buf = this->buffer; - GstBuffer *out; - - if (!GST_OBJECT_FLAG_IS_SET (this, GST_SF_OPEN)) - if (!gst_sf_open_file (this)) - return; /* we've already set gst_element_error */ - - if (buffer_frames == 0) { - /* we have to set the caps later */ - buffer_frames = this->buffer_frames = 1024; - } - if (buf == NULL) { - buf = this->buffer = - g_malloc (this->numchannels * this->buffer_frames * sizeof (float)); - memset (this->buffer, 0, - this->numchannels * this->buffer_frames * sizeof (float)); - } - - read = sf_readf_float (this->file, buf, buffer_frames); - if (read < buffer_frames) - eos = 1; - - if (read) - for (i = 0, l = this->channels; l; l = l->next, i++) { - channel = GST_SF_CHANNEL (l); - - /* don't push on disconnected pads -- useful for ::create-pads=TRUE */ - if (!GST_PAD_PEER (channel->pad)) - continue; - - if (!channel->caps_set) { - GstCaps *caps = - gst_caps_copy (GST_PAD_CAPS (GST_SF_CHANNEL (l)->pad)); - if (!caps) - caps = gst_caps_copy - (GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (GST_SF_CHANNEL - (l)->pad))); - gst_caps_set_simple (caps, "rate", G_TYPE_INT, this->rate, - "buffer-frames", G_TYPE_INT, this->buffer_frames, NULL); - if (!gst_pad_try_set_caps (GST_SF_CHANNEL (l)->pad, caps)) { - GST_ELEMENT_ERROR (this, CORE, NEGOTIATION, (NULL), - ("Opened file with sample rate %d, but could not set caps", - this->rate)); - gst_sf_close_file (this); - return; - } - channel->caps_set = TRUE; - } - - out = gst_buffer_new_and_alloc (read * sizeof (float)); - data = (gfloat *) GST_BUFFER_DATA (out); - for (j = 0; j < read; j++) - data[j] = buf[j * nchannels + i % nchannels]; - gst_pad_push (channel->pad, GST_DATA (out)); - } - - this->time += read * (GST_SECOND / this->rate); - gst_audio_clock_update_time ((GstAudioClock *) this->provided_clock, - this->time); - - if (eos) { - if (this->loop) { - sf_seek (this->file, (sf_count_t) 0, SEEK_SET); - eos = 0; - } else { - for (l = this->channels; l; l = l->next) - gst_pad_push (GST_SF_CHANNEL (l)->pad, - GST_DATA (gst_event_new (GST_EVENT_EOS))); - gst_element_set_eos (element); - } - } - } else { - sf_count_t written, num_to_write; - gint i, j; - int buffer_frames = this->buffer_frames; - int nchannels = this->numchannels; - GstSFChannel *channel = NULL; - gfloat *data; - gfloat *buf = this->buffer; - GstBuffer *in; - - /* the problem: we can't allocate a buffer for pulled data before caps is - * set, and we can't open the file without the sample rate from the - * caps... */ - - num_to_write = buffer_frames; - - INFO_OBJ (this, "looping, buffer_frames=%d, nchannels=%d", buffer_frames, - nchannels); - - for (i = 0, l = this->channels; l; l = l->next, i++) { - channel = GST_SF_CHANNEL (l); - - pull_again: - in = GST_BUFFER (gst_pad_pull (channel->pad)); - - if (buffer_frames == 0) { - /* pulling a buffer from the pad should have caused capsnego to occur, - which then would set this->buffer_frames to a new value */ - buffer_frames = this->buffer_frames; - if (buffer_frames == 0) { - GST_ELEMENT_ERROR (element, CORE, NEGOTIATION, (NULL), - ("format wasn't negotiated before chain function")); - return; - } - buf = this->buffer; - num_to_write = buffer_frames; - } - - if (!GST_OBJECT_FLAG_IS_SET (this, GST_SF_OPEN)) - if (!gst_sf_open_file (this)) - return; /* we've already set gst_element_error */ - - if (GST_IS_EVENT (in)) { - switch (GST_EVENT_TYPE (in)) { - case GST_EVENT_EOS: - case GST_EVENT_INTERRUPT: - num_to_write = 0; - break; - default: - goto pull_again; - break; - } - } - - if (num_to_write) { - data = (gfloat *) GST_BUFFER_DATA (in); - num_to_write = - MIN (num_to_write, GST_BUFFER_SIZE (in) / sizeof (gfloat)); - for (j = 0; j < num_to_write; j++) - buf[j * nchannels + i % nchannels] = data[j]; - } - - gst_data_unref ((GstData *) in); - } - - if (num_to_write) { - written = sf_writef_float (this->file, buf, num_to_write); - if (written != num_to_write) - GST_ELEMENT_ERROR (element, RESOURCE, WRITE, - (_("Could not write to file \"%s\"."), this->filename), - ("soundfile error: %s", sf_strerror (this->file))); - } - - this->time += num_to_write * (GST_SECOND / this->rate); - gst_audio_clock_update_time ((GstAudioClock *) this->provided_clock, - this->time); - - if (num_to_write != buffer_frames) - gst_element_set_eos (element); - } -} - static gboolean plugin_init (GstPlugin * plugin) { - if (!gst_library_load ("gstaudio")) + if (!gst_element_register (plugin, "sfsink", GST_RANK_NONE, + gst_sf_sink_get_type ())) return FALSE; - GST_DEBUG_CATEGORY_INIT (gstsf_debug, "sf", - GST_DEBUG_FG_WHITE | GST_DEBUG_BG_GREEN | GST_DEBUG_BOLD, - "libsndfile plugin"); - - if (!gst_element_register (plugin, "sfsrc", GST_RANK_NONE, GST_TYPE_SFSRC)) - return FALSE; - - if (!gst_element_register (plugin, "sfsink", GST_RANK_NONE, GST_TYPE_SFSINK)) - return FALSE; - -#ifdef ENABLE_NLS - setlocale (LC_ALL, ""); - bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); -#endif /* ENABLE_NLS */ - return TRUE; } diff --git a/ext/sndfile/gstsf.h b/ext/sndfile/gstsf.h index 8af46a7e..129ddd93 100644 --- a/ext/sndfile/gstsf.h +++ b/ext/sndfile/gstsf.h @@ -29,86 +29,13 @@ G_BEGIN_DECLS -#define GST_TYPE_SF \ - (gst_sf_get_type()) -#define GST_SF(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SF,GstSF)) -#define GST_SF_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SF,GstSFClass)) -#define GST_IS_SF(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SF)) -#define GST_IS_SF_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SF)) +#define GST_TYPE_SF_MAJOR_TYPES (gst_sf_major_types_get_type()) +#define GST_TYPE_SF_MINOR_TYPES (gst_sf_minor_types_get_type()) -#define GST_TYPE_SFSRC \ - (gst_sfsrc_get_type()) -#define GST_SFSRC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SFSRC,GstSF)) -#define GST_SFSRC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SFSRC,GstSFClass)) -#define GST_IS_SFSRC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SFSRC)) -#define GST_IS_SFSRC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SFSRC)) +GType gst_sf_major_types_get_type (void); +GType gst_sf_minor_types_get_type (void); -#define GST_TYPE_SFSINK \ - (gst_sfsink_get_type()) -#define GST_SFSINK(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SFSINK,GstSF)) -#define GST_SFSINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SFSINK,GstSFClass)) -#define GST_IS_SFSINK(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SFSINK)) -#define GST_IS_SFSINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SFSINK)) - -typedef struct _GstSF GstSF; -typedef struct _GstSFClass GstSFClass; - -typedef enum { - GST_SF_OPEN = (GST_ELEMENT_FLAG_LAST << 0), - GST_SF_FLAG_LAST = (GST_ELEMENT_FLAG_LAST << 2), -} GstSFlags; - -typedef struct { - GstPad *pad; - gint num; - gboolean caps_set; -} GstSFChannel; - -#define GST_SF_CHANNEL(l) ((GstSFChannel*)l->data) - -struct _GstSF { - GstElement element; - GList *channels; - - GstClock *clock, *provided_clock; - - gchar *filename; - SNDFILE *file; - void *buffer; - - gboolean loop; - gboolean create_pads; - gint channelcount; - gint numchannels; - gint format_major; - gint format_subtype; - gint format; - - gint rate; - gint buffer_frames; - - guint64 time; -}; - -struct _GstSFClass { - GstElementClass parent_class; -}; - -GType gst_sf_get_type (void); -GType gst_sfsrc_get_type (void); -GType gst_sfsink_get_type (void); +GType gst_sf_sink_get_type (void); G_END_DECLS diff --git a/ext/sndfile/gstsfsink.c b/ext/sndfile/gstsfsink.c new file mode 100644 index 00000000..4039a792 --- /dev/null +++ b/ext/sndfile/gstsfsink.c @@ -0,0 +1,528 @@ +/* GStreamer libsndfile plugin + * Copyright (C) 2007 Andy Wingo <wingo at pobox dot com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/audio/audio.h> + +#include <gst/gst-i18n-plugin.h> + +#include "gstsfsink.h" + + +static const GstElementDetails sfsink_details = +GST_ELEMENT_DETAILS ("Sndfile sink", + "Sink/Audio", + "Write audio streams to disk using libsndfile", + "Andy Wingo <wingo at pobox dot com>"); + +enum +{ + PROP_0, + PROP_LOCATION, + PROP_MAJOR_TYPE, + PROP_MINOR_TYPE, + PROP_BUFFER_FRAMES +}; + +#define DEFAULT_BUFFER_FRAMES (256) + +static GstStaticPadTemplate sf_sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-float, " + "rate = (int) [ 1, MAX ], " + "channels = (int) [ 1, MAX ], " + "endianness = (int) BYTE_ORDER, " + "width = (int) 32; " + "audio/x-raw-int, " + "rate = (int) [ 1, MAX ], " + "channels = (int) [ 1, MAX ], " + "endianness = (int) BYTE_ORDER, " + "width = (int) {16, 32}, " + "depth = (int) {16, 32}, " "signed = (boolean) true") + ); + +GST_BOILERPLATE (GstSFSink, gst_sf_sink, GstBaseSink, GST_TYPE_BASE_SINK); + +static void gst_sf_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_sf_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_sf_sink_start (GstBaseSink * bsink); +static gboolean gst_sf_sink_stop (GstBaseSink * bsink); +static void gst_sf_sink_fixate (GstBaseSink * bsink, GstCaps * caps); +static gboolean gst_sf_sink_set_caps (GstBaseSink * bsink, GstCaps * caps); +static gboolean gst_sf_sink_activate_pull (GstBaseSink * bsink, + gboolean active); +static GstFlowReturn gst_sf_sink_render (GstBaseSink * bsink, + GstBuffer * buffer); +static gboolean gst_sf_sink_event (GstBaseSink * bsink, GstEvent * event); + +static gboolean gst_sf_sink_open_file (GstSFSink * this); +static void gst_sf_sink_close_file (GstSFSink * this); + +GST_DEBUG_CATEGORY_STATIC (gst_sf_debug); +#define GST_CAT_DEFAULT gst_sf_debug + +static void +gst_sf_sink_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + GST_DEBUG_CATEGORY_INIT (gst_sf_debug, "sfsink", 0, "sfsink element"); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sf_sink_factory)); + gst_element_class_set_details (element_class, &sfsink_details); +} + +static void +gst_sf_sink_class_init (GstSFSinkClass * klass) +{ + GObjectClass *gobject_class; + GstBaseSinkClass *basesink_class; + GParamSpec *pspec; + + gobject_class = (GObjectClass *) klass; + basesink_class = (GstBaseSinkClass *) klass; + + gobject_class->set_property = gst_sf_sink_set_property; + gobject_class->get_property = gst_sf_sink_get_property; + + g_object_class_install_property (gobject_class, PROP_LOCATION, + g_param_spec_string ("location", "File Location", + "Location of the file to write", NULL, G_PARAM_READWRITE)); + pspec = g_param_spec_enum + ("major-type", "Major type", "Major output type", GST_TYPE_SF_MAJOR_TYPES, + SF_FORMAT_WAV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + g_object_class_install_property (gobject_class, PROP_MAJOR_TYPE, pspec); + pspec = g_param_spec_enum + ("minor-type", "Minor type", "Minor output type", GST_TYPE_SF_MINOR_TYPES, + SF_FORMAT_FLOAT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + g_object_class_install_property (gobject_class, PROP_MINOR_TYPE, pspec); + pspec = g_param_spec_int + ("buffer-frames", "Buffer frames", + "Number of frames per buffer, in pull mode", 1, G_MAXINT, + DEFAULT_BUFFER_FRAMES, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + g_object_class_install_property (gobject_class, PROP_BUFFER_FRAMES, pspec); + + basesink_class->get_times = NULL; + basesink_class->start = GST_DEBUG_FUNCPTR (gst_sf_sink_start); + basesink_class->stop = GST_DEBUG_FUNCPTR (gst_sf_sink_stop); + basesink_class->fixate = GST_DEBUG_FUNCPTR (gst_sf_sink_fixate); + basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_sf_sink_set_caps); + basesink_class->activate_pull = GST_DEBUG_FUNCPTR (gst_sf_sink_activate_pull); + basesink_class->render = GST_DEBUG_FUNCPTR (gst_sf_sink_render); + basesink_class->event = GST_DEBUG_FUNCPTR (gst_sf_sink_event); +} + +static void +gst_sf_sink_init (GstSFSink * this, GstSFSinkClass * klass) +{ + GST_BASE_SINK (this)->can_activate_pull = TRUE; +} + +static void +gst_sf_sink_set_location (GstSFSink * this, const gchar * location) +{ + if (this->file) + goto was_open; + + if (this->location) + g_free (this->location); + + this->location = location ? g_strdup (location) : NULL; + + return; + +was_open: + { + g_warning ("Changing the `location' property on sfsink when " + "a file is open not supported."); + return; + } +} + + +static void +gst_sf_sink_set_property (GObject * object, guint prop_id, const GValue * value, + GParamSpec * pspec) +{ + GstSFSink *this = GST_SF_SINK (object); + + switch (prop_id) { + case PROP_LOCATION: + gst_sf_sink_set_location (this, g_value_get_string (value)); + break; + + case PROP_MAJOR_TYPE: + this->format_major = g_value_get_enum (value); + break; + + case PROP_MINOR_TYPE: + this->format_subtype = g_value_get_enum (value); + break; + + case PROP_BUFFER_FRAMES: + this->buffer_frames = g_value_get_int (value); + break; + + default: + break; + } +} + +static void +gst_sf_sink_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstSFSink *this = GST_SF_SINK (object); + + switch (prop_id) { + case PROP_LOCATION: + g_value_set_string (value, this->location); + break; + + case PROP_MAJOR_TYPE: + g_value_set_enum (value, this->format_major); + break; + + case PROP_MINOR_TYPE: + g_value_set_enum (value, this->format_subtype); + break; + + case PROP_BUFFER_FRAMES: + g_value_set_int (value, this->buffer_frames); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_sf_sink_start (GstBaseSink * bsink) +{ + /* pass */ + return TRUE; +} + +static gboolean +gst_sf_sink_stop (GstBaseSink * bsink) +{ + GstSFSink *this = GST_SF_SINK (bsink); + + if (this->file) + gst_sf_sink_close_file (this); + + return TRUE; +} + +static gboolean +gst_sf_sink_open_file (GstSFSink * this) +{ + int mode; + SF_INFO info; + + g_return_val_if_fail (this->file == NULL, FALSE); + g_return_val_if_fail (this->rate > 0, FALSE); + g_return_val_if_fail (this->channels > 0, FALSE); + + if (!this->location) + goto no_filename; + + mode = SFM_WRITE; + this->format = this->format_major | this->format_subtype; + info.samplerate = this->rate; + info.channels = this->channels; + info.format = this->format; + + GST_INFO_OBJECT (this, "Opening %s with rate %d, %d channels, format 0x%x", + this->location, info.samplerate, info.channels, info.format); + + if (!sf_format_check (&info)) + goto bad_format; + + this->file = sf_open (this->location, mode, &info); + + if (!this->file) + goto open_failed; + + return TRUE; + +no_filename: + { + GST_ELEMENT_ERROR (this, RESOURCE, NOT_FOUND, + (_("No file name specified for writing.")), (NULL)); + return FALSE; + } +bad_format: + { + GST_ELEMENT_ERROR (this, STREAM, ENCODE, (NULL), + ("Input parameters (rate:%d, channels:%d, format:0x%x) invalid", + info.samplerate, info.channels, info.format)); + return FALSE; + } +open_failed: + { + GST_ELEMENT_ERROR (this, RESOURCE, OPEN_WRITE, + (_("Could not open file \"%s\" for writing."), this->location), + ("soundfile error: %s", sf_strerror (NULL))); + return FALSE; + } +} + +static void +gst_sf_sink_close_file (GstSFSink * this) +{ + int err = 0; + + g_return_if_fail (this->file != NULL); + + GST_INFO_OBJECT (this, "Closing file %s", this->location); + + if ((err = sf_close (this->file))) + goto close_failed; + + this->file = NULL; + + return; + +close_failed: + { + GST_ELEMENT_ERROR (this, RESOURCE, CLOSE, + ("Could not close file file \"%s\".", this->location), + ("soundfile error: %s", sf_error_number (err))); + return; + } +} + +static void +gst_sf_sink_fixate (GstBaseSink * bsink, GstCaps * caps) +{ + GstStructure *s; + gint width, depth; + + s = gst_caps_get_structure (caps, 0); + + /* fields for all formats */ + gst_structure_fixate_field_nearest_int (s, "rate", 44100); + gst_structure_fixate_field_nearest_int (s, "channels", 2); + gst_structure_fixate_field_nearest_int (s, "width", 16); + + /* fields for int */ + if (gst_structure_has_field (s, "depth")) { + gst_structure_get_int (s, "width", &width); + /* round width to nearest multiple of 8 for the depth */ + depth = GST_ROUND_UP_8 (width); + gst_structure_fixate_field_nearest_int (s, "depth", depth); + } + if (gst_structure_has_field (s, "signed")) + gst_structure_fixate_field_boolean (s, "signed", TRUE); + if (gst_structure_has_field (s, "endianness")) + gst_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER); +} + +static gboolean +gst_sf_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) +{ + GstSFSink *this = (GstSFSink *) bsink; + GstStructure *structure; + gint width, channels, rate; + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "width", &width) + || !gst_structure_get_int (structure, "channels", &channels) + || !gst_structure_get_int (structure, "rate", &rate)) + goto impossible; + + if (gst_structure_has_name (structure, "audio/x-raw-int")) { + switch (width) { + case 16: + this->writer = (GstSFWriter) sf_writef_short; + break; + case 32: + this->writer = (GstSFWriter) sf_writef_int; + break; + default: + goto impossible; + } + } else { + switch (width) { + case 32: + this->writer = (GstSFWriter) sf_writef_float; + break; + default: + goto impossible; + } + } + + this->bytes_per_frame = width * channels / 8; + this->rate = rate; + this->channels = channels; + + return gst_sf_sink_open_file (this); + +impossible: + { + g_warning ("something impossible happened"); + return FALSE; + } +} + +/* with STREAM_LOCK + */ +static void +gst_sf_sink_loop (GstPad * pad) +{ + GstSFSink *this; + GstBaseSink *basesink; + GstBuffer *buf = NULL; + GstFlowReturn result; + + this = GST_SF_SINK (gst_pad_get_parent (pad)); + basesink = GST_BASE_SINK (this); + + result = gst_pad_pull_range (pad, basesink->offset, + this->buffer_frames * this->bytes_per_frame, &buf); + if (G_UNLIKELY (result != GST_FLOW_OK)) + goto paused; + + if (G_UNLIKELY (buf == NULL)) + goto no_buffer; + + basesink->offset += GST_BUFFER_SIZE (buf); + + GST_PAD_PREROLL_LOCK (pad); + result = gst_sf_sink_render (basesink, buf); + GST_PAD_PREROLL_UNLOCK (pad); + if (G_UNLIKELY (result != GST_FLOW_OK)) + goto paused; + + gst_object_unref (this); + + return; + + /* ERRORS */ +paused: + { + GST_INFO_OBJECT (basesink, "pausing task, reason %s", + gst_flow_get_name (result)); + gst_pad_pause_task (pad); + /* fatal errors and NOT_LINKED cause EOS */ + if (GST_FLOW_IS_FATAL (result) || result == GST_FLOW_NOT_LINKED) { + gst_pad_send_event (pad, gst_event_new_eos ()); + /* EOS does not cause an ERROR message */ + if (result != GST_FLOW_UNEXPECTED) { + GST_ELEMENT_ERROR (basesink, STREAM, FAILED, + (_("Internal data stream error.")), + ("stream stopped, reason %s", gst_flow_get_name (result))); + } + } + gst_object_unref (this); + return; + } +no_buffer: + { + GST_INFO_OBJECT (this, "no buffer, pausing"); + result = GST_FLOW_ERROR; + goto paused; + } +} + +static gboolean +gst_sf_sink_activate_pull (GstBaseSink * basesink, gboolean active) +{ + gboolean result; + + if (active) { + /* start task */ + result = gst_pad_start_task (basesink->sinkpad, + (GstTaskFunction) gst_sf_sink_loop, basesink->sinkpad); + } else { + /* step 2, make sure streaming finishes */ + result = gst_pad_stop_task (basesink->sinkpad); + } + + return result; +} + +static GstFlowReturn +gst_sf_sink_render (GstBaseSink * bsink, GstBuffer * buffer) +{ + GstSFSink *this; + sf_count_t written, num_to_write; + + this = (GstSFSink *) bsink; + + if (GST_BUFFER_SIZE (buffer) % this->bytes_per_frame) + goto bad_length; + + num_to_write = GST_BUFFER_SIZE (buffer) / this->bytes_per_frame; + + written = this->writer (this->file, GST_BUFFER_DATA (buffer), num_to_write); + if (written != num_to_write) + goto short_write; + + return GST_FLOW_OK; + +bad_length: + { + GST_ELEMENT_ERROR (this, RESOURCE, WRITE, + (_("Could not write to file \"%s\"."), this->location), + ("bad buffer size: %u %% %d != 0", GST_BUFFER_SIZE (buffer), + this->bytes_per_frame)); + return GST_FLOW_ERROR; + } +short_write: + { + GST_ELEMENT_ERROR (this, RESOURCE, WRITE, + (_("Could not write to file \"%s\"."), this->location), + ("soundfile error: %s", sf_strerror (this->file))); + return GST_FLOW_ERROR; + } +} + +static gboolean +gst_sf_sink_event (GstBaseSink * bsink, GstEvent * event) +{ + GstSFSink *this; + GstEventType type; + + this = (GstSFSink *) bsink; + + type = GST_EVENT_TYPE (event); + + switch (type) { + case GST_EVENT_EOS: + if (this->file) + sf_write_sync (this->file); + break; + default: + break; + } + + return TRUE; +} diff --git a/ext/sndfile/gstsfsink.h b/ext/sndfile/gstsfsink.h new file mode 100644 index 00000000..bf6c6956 --- /dev/null +++ b/ext/sndfile/gstsfsink.h @@ -0,0 +1,73 @@ +/* GStreamer libsndfile plugin + * Copyright (C) 2003,2007 Andy Wingo <wingo at pobox dot com> + * + * 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_SF_SINK_H__ +#define __GST_SF_SINK_H__ + + +#include "gstsf.h" +#include <gst/base/gstbasesink.h> + + +G_BEGIN_DECLS + + +#define GST_TYPE_SF_SINK \ + (gst_sf_sink_get_type()) +#define GST_SF_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SF_SINK,GstSFSink)) +#define GST_SF_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SF_SINK,GstSFSinkClass)) +#define GST_IS_SF_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SF_SINK)) +#define GST_IS_SF_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SF_SINK)) + +typedef struct _GstSFSink GstSFSink; +typedef struct _GstSFSinkClass GstSFSinkClass; + +typedef sf_count_t (*GstSFWriter)(SNDFILE *f, void *data, sf_count_t nframes); + +struct _GstSFSink { + GstBaseSink parent; + + gchar *location; + SNDFILE *file; + GstSFWriter writer; + gint bytes_per_frame; + + gint channels; + gint rate; + + gint format_major; + gint format_subtype; + gint format; + gint buffer_frames; +}; + +struct _GstSFSinkClass { + GstBaseSinkClass parent_class; +}; + + +G_END_DECLS + + +#endif /* __GST_SF_SINK_H__ */ |