From 5f89bee7497749812e73be7e4e91adf74690f59a Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Tue, 17 Dec 2013 17:56:32 +0100 Subject: [PATCH] sndfile: rewrite sndfile for 1.0 Add a sfdec for a start. Instead of a source plugin, this is a demuxer/decoder combination. This makes it work with auto-plugging. --- configure.ac | 4 +- ext/sndfile/Makefile.am | 7 +- ext/sndfile/gstsf.c | 147 ++++++------- ext/sndfile/gstsf.h | 5 +- ext/sndfile/gstsfdec.c | 448 ++++++++++++++++++++++++++++++++++++++++ ext/sndfile/gstsfdec.h | 73 +++++++ 6 files changed, 603 insertions(+), 81 deletions(-) create mode 100644 ext/sndfile/gstsfdec.c create mode 100644 ext/sndfile/gstsfdec.h diff --git a/configure.ac b/configure.ac index 29dfb38a0a..5f96a748c0 100644 --- a/configure.ac +++ b/configure.ac @@ -346,7 +346,7 @@ GST_PLUGINS_NONPORTED=" cdxaparse \ linsys vcd \ apexsink dc1394 \ gsettings \ - musepack nas sdl sndfile timidity \ + musepack nas sdl timidity \ directdraw acm wininet \ xvid lv2 teletextdec sndio osx_video quicktime" AC_SUBST(GST_PLUGINS_NONPORTED) @@ -1952,7 +1952,7 @@ AG_GST_CHECK_FEATURE(SMOOTHSTREAMING, [Smooth Streaming plug-in], smoothstreamin dnl *** sndfile *** translit(dnm, m, l) AM_CONDITIONAL(USE_SNDFILE, true) -AG_GST_CHECK_FEATURE(SNDFILE, [sndfile plug-in], sfsrc sfsink, [ +AG_GST_CHECK_FEATURE(SNDFILE, [sndfile plug-in], sfdec sfenc, [ PKG_CHECK_MODULES(SNDFILE, sndfile >= 1.0.16, HAVE_SNDFILE="yes", HAVE_SNDFILE="no") AC_SUBST(SNDFILE_CFLAGS) AC_SUBST(SNDFILE_LIBS) diff --git a/ext/sndfile/Makefile.am b/ext/sndfile/Makefile.am index f7dde8a7fa..c0135cc3af 100644 --- a/ext/sndfile/Makefile.am +++ b/ext/sndfile/Makefile.am @@ -1,10 +1,9 @@ - plugin_LTLIBRARIES = libgstsndfile.la -libgstsndfile_la_SOURCES = gstsf.c gstsfsrc.c gstsfsink.c +libgstsndfile_la_SOURCES = gstsf.c gstsfdec.c libgstsndfile_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(SNDFILE_CFLAGS) -libgstsndfile_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(SNDFILE_LIBS) +libgstsndfile_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(GST_LIBS) $(SNDFILE_LIBS) libgstsndfile_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstsndfile_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) -noinst_HEADERS = gstsf.h gstsfsrc.h gstsfsink.h +noinst_HEADERS = gstsf.h gstsfdec.h diff --git a/ext/sndfile/gstsf.c b/ext/sndfile/gstsf.c index e71b188847..a8f1c61bce 100644 --- a/ext/sndfile/gstsf.c +++ b/ext/sndfile/gstsf.c @@ -27,79 +27,86 @@ #include "gstsf.h" +/* sf formats */ -GType -gst_sf_major_types_get_type (void) +GstCaps * +gst_sf_create_audio_template_caps (void) { - static GType sf_major_types_type = 0; - static GEnumValue *sf_major_types = NULL; + GstCaps *caps = gst_caps_new_empty (); + SF_FORMAT_INFO format_info; + const gchar *fmt; + gint k, count; - if (!sf_major_types_type) { - SF_FORMAT_INFO format_info; - int k, count; + sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (gint)); - sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (int)); + for (k = 0; k < count; k++) { + format_info.format = k; + sf_command (NULL, SFC_GET_FORMAT_MAJOR, &format_info, sizeof (format_info)); - sf_major_types = g_new0 (GEnumValue, count + 1); - - for (k = 0; k < count; k++) { - format_info.format = k; - sf_command (NULL, SFC_GET_FORMAT_MAJOR, &format_info, - sizeof (format_info)); - sf_major_types[k].value = format_info.format; - sf_major_types[k].value_name = g_strdup (format_info.name); - sf_major_types[k].value_nick = g_strdup (format_info.extension); - - /* Irritatingly enough, there exist major_types with the same extension. Let's - just hope that sndfile gives us the list in alphabetical order, as it - currently does. */ - if (k > 0 - && strcmp (sf_major_types[k].value_nick, - sf_major_types[k - 1].value_nick) == 0) { - 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 ((gchar *) sf_major_types[k].value_nick, - G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-'); - } + switch (format_info.format) { + case SF_FORMAT_AIFF: /* Apple/SGI AIFF format */ + fmt = "audio/x-aiff"; + break; + case SF_FORMAT_AU: /* Sun/NeXT AU format */ + fmt = "audio/x-au"; + break; + case SF_FORMAT_FLAC: /* FLAC lossless file format */ + fmt = "audio/x-flac"; + break; + case SF_FORMAT_IRCAM: /* Berkeley/IRCAM/CARL */ + fmt = "audio/x-ircam"; + break; + case SF_FORMAT_NIST: /* Sphere NIST format. */ + fmt = "audio/x-nist"; + break; + case SF_FORMAT_OGG: /* Xiph OGG container */ + fmt = "audio/ogg"; + break; + case SF_FORMAT_PAF: /* Ensoniq PARIS file format. */ + fmt = "audio/x-paris"; + break; + case SF_FORMAT_RAW: /* RAW PCM data. */ + fmt = "audio/x-raw"; + break; + case SF_FORMAT_SDS: /* Midi Sample Dump Standard */ + fmt = "audio/x-sds"; + break; + case SF_FORMAT_SVX: /* Amiga IFF / SVX8 / SV16 format. */ + fmt = "audio/x-svx"; + break; + case SF_FORMAT_VOC: /* VOC files. */ + fmt = "audio/x-voc"; + break; + case SF_FORMAT_WAV: /* Microsoft WAV format */ + case SF_FORMAT_WAVEX: /* MS WAVE with WAVEFORMATEX */ + fmt = "audio/x-wav"; + break; + case SF_FORMAT_W64: /* Sonic Foundry's 64 bit RIFF/WAV */ + fmt = "audio/x-w64"; + break; + case SF_FORMAT_XI: /* Fasttracker 2 Extended Instrument */ + fmt = "audio/x-xi"; + break; + case SF_FORMAT_RF64: /* RF64 WAV file */ + case SF_FORMAT_MAT4: /* Matlab (tm) V4.2 / GNU Octave 2.0 */ + case SF_FORMAT_MAT5: /* Matlab (tm) V5.0 / GNU Octave 2.1 */ + case SF_FORMAT_PVF: /* Portable Voice Format */ + case SF_FORMAT_HTK: /* HMM Tool Kit format */ + case SF_FORMAT_AVR: /* Audio Visual Research */ + case SF_FORMAT_SD2: /* Sound Designer 2 */ + case SF_FORMAT_CAF: /* Core Audio File format */ + case SF_FORMAT_WVE: /* Psion WVE format */ + case SF_FORMAT_MPC2K: /* Akai MPC 2000 sampler */ + default: + fmt = NULL; + GST_WARNING ("format 0x%x: '%s' is not mapped", format_info.format, + format_info.name); } - - sf_major_types_type = - g_enum_register_static ("GstSndfileMajorTypes", sf_major_types); - } - return sf_major_types_type; -} - -GType -gst_sf_minor_types_get_type (void) -{ - static GType sf_minor_types_type = 0; - static GEnumValue *sf_minor_types = NULL; - - if (!sf_minor_types_type) { - SF_FORMAT_INFO format_info; - int k, count; - - sf_command (NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof (int)); - - sf_minor_types = g_new0 (GEnumValue, count + 1); - - for (k = 0; k < count; k++) { - format_info.format = k; - sf_command (NULL, SFC_GET_FORMAT_SUBTYPE, &format_info, - sizeof (format_info)); - 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 ((gchar *) sf_minor_types[k].value_nick, - G_CSET_a_2_z G_CSET_DIGITS "-", '-'); + if (fmt != NULL) { + gst_caps_append_structure (caps, gst_structure_new_empty (fmt)); } - - sf_minor_types_type = - g_enum_register_static ("GstSndfileMinorTypes", sf_minor_types); } - return sf_minor_types_type; + return gst_caps_simplify (caps); } static gboolean @@ -112,12 +119,8 @@ plugin_init (GstPlugin * plugin) bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); #endif /* ENABLE_NLS */ - if (!gst_element_register (plugin, "sfsink", GST_RANK_NONE, - gst_sf_sink_get_type ())) - return FALSE; - - if (!gst_element_register (plugin, "sfsrc", GST_RANK_NONE, - gst_sf_src_get_type ())) + if (!gst_element_register (plugin, "sfdec", GST_RANK_MARGINAL, + gst_sf_dec_get_type ())) return FALSE; return TRUE; @@ -126,5 +129,5 @@ plugin_init (GstPlugin * plugin) GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, sndfile, - "use libsndfile to read and write audio from and to files", + "use libsndfile to read and write various audio formats", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/sndfile/gstsf.h b/ext/sndfile/gstsf.h index 0b12f656a2..71584569f1 100644 --- a/ext/sndfile/gstsf.h +++ b/ext/sndfile/gstsf.h @@ -28,6 +28,7 @@ G_BEGIN_DECLS +GstCaps *gst_sf_create_audio_template_caps (void); #define GST_TYPE_SF_MAJOR_TYPES (gst_sf_major_types_get_type()) #define GST_TYPE_SF_MINOR_TYPES (gst_sf_minor_types_get_type()) @@ -35,9 +36,7 @@ G_BEGIN_DECLS GType gst_sf_major_types_get_type (void); GType gst_sf_minor_types_get_type (void); -GType gst_sf_sink_get_type (void); -GType gst_sf_src_get_type (void); - +GType gst_sf_dec_get_type (void); G_END_DECLS diff --git a/ext/sndfile/gstsfdec.c b/ext/sndfile/gstsfdec.c new file mode 100644 index 0000000000..95e48f996e --- /dev/null +++ b/ext/sndfile/gstsfdec.c @@ -0,0 +1,448 @@ +/* GStreamer libsndfile plugin + * Copyright (C) 2013 Stefan Sauer + * + * 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 self library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "gstsfdec.h" +#include + +enum +{ + PROP_0, + PROP_LOCATION +}; + +#define FORMATS \ + "{ "GST_AUDIO_NE (F32)", "GST_AUDIO_NE (S32)", "GST_AUDIO_NE (S16)" }" + +static GstStaticPadTemplate sf_dec_src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) " FORMATS ", " + "layout = (string) interleaved, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]")); + +GST_DEBUG_CATEGORY_STATIC (gst_sf_dec_debug); +#define GST_CAT_DEFAULT gst_sf_dec_debug + +#define DEFAULT_BUFFER_FRAMES (256) + +static GstStateChangeReturn gst_sf_dec_change_state (GstElement * element, + GstStateChange transition); + +static gboolean gst_sf_dec_sink_activate (GstPad * pad, GstObject * parent); +static gboolean gst_sf_dec_sink_activate_mode (GstPad * sinkpad, + GstObject * parent, GstPadMode mode, gboolean active); +static void gst_sf_dec_loop (GstPad * pad); + +static gboolean gst_sf_dec_start (GstSFDec * bsrc); +static gboolean gst_sf_dec_stop (GstSFDec * bsrc); + +#define _do_init \ + GST_DEBUG_CATEGORY_INIT (gst_sf_dec_debug, "sfdec", 0, "sfdec element"); +#define gst_sf_dec_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstSFDec, gst_sf_dec, GST_TYPE_ELEMENT, _do_init); + +/* sf virtual io */ + +static sf_count_t +gst_sf_vio_get_filelen (void *user_data) +{ + GstElement *element = GST_ELEMENT (user_data); + gint64 dur; + + if (gst_element_query_duration (element, GST_FORMAT_BYTES, &dur)) { + return (sf_count_t) dur; + } + GST_WARNING_OBJECT (element, "query_duration failed"); + return -1; +} + +static sf_count_t +gst_sf_vio_tell (void *user_data) +{ + GstSFDec *self = GST_SF_DEC (user_data); + return self->pos; +} + +static sf_count_t +gst_sf_vio_seek (sf_count_t offset, int whence, void *user_data) +{ + GstSFDec *self = GST_SF_DEC (user_data); + + switch (whence) { + case SEEK_CUR: + self->pos += offset; + break; + case SEEK_SET: + self->pos = offset; + break; + case SEEK_END: + self->pos = gst_sf_vio_get_filelen (user_data) - offset; + break; + } + return (sf_count_t) self->pos; +} + +static sf_count_t +gst_sf_vio_read (void *ptr, sf_count_t count, void *user_data) +{ + GstSFDec *self = GST_SF_DEC (user_data); + GstBuffer *buffer = gst_buffer_new_wrapped_full (0, ptr, count, 0, count, + ptr, NULL); + + if (gst_pad_pull_range (self->sinkpad, self->pos, count, &buffer) == + GST_FLOW_OK) { + GST_DEBUG_OBJECT (self, "read %d bytes @ pos %" G_GUINT64_FORMAT, + (gint) count, self->pos); + self->pos += count; + return count; + } + GST_WARNING_OBJECT (self, "read failed"); + return 0; +} + +static sf_count_t +gst_sf_vio_write (const void *ptr, sf_count_t count, void *user_data) +{ + GstSFDec *self = GST_SF_DEC (user_data); + GstBuffer *buffer = gst_buffer_new_wrapped (g_memdup (ptr, count), count); + + if (gst_pad_push (self->srcpad, buffer) == GST_FLOW_OK) { + return count; + } + GST_WARNING_OBJECT (self, "write failed"); + return 0; +} + +SF_VIRTUAL_IO gst_sf_vio = { + &gst_sf_vio_get_filelen, + &gst_sf_vio_seek, + &gst_sf_vio_read, + &gst_sf_vio_write, + &gst_sf_vio_tell, +}; + + +static void +gst_sf_dec_class_init (GstSFDecClass * klass) +{ + GstElementClass *gstelement_class; + + gstelement_class = GST_ELEMENT_CLASS (klass); + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_sf_dec_change_state); + + gst_element_class_set_static_metadata (gstelement_class, "Sndfile decoder", + "Demuxer/Audio", + "Read audio streams using libsndfile", + "Stefan Sauer "); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&sf_dec_src_factory)); + + gst_element_class_add_pad_template (gstelement_class, + gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + gst_sf_create_audio_template_caps ())); + +} + +static void +gst_sf_dec_init (GstSFDec * self) +{ + self->sinkpad = gst_pad_new_from_template (gst_element_class_get_pad_template + (GST_ELEMENT_GET_CLASS (self), "sink"), "sink"); + gst_pad_set_activate_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_sf_dec_sink_activate)); + gst_pad_set_activatemode_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_sf_dec_sink_activate_mode)); + gst_element_add_pad (GST_ELEMENT (self), self->sinkpad); + + self->srcpad = gst_pad_new_from_static_template (&sf_dec_src_factory, "src"); + /* TODO(ensonic): event + query functions */ + gst_element_add_pad (GST_ELEMENT (self), self->srcpad); +} + +static GstStateChangeReturn +gst_sf_dec_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + GstSFDec *self = GST_SF_DEC (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_sf_dec_start (self); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_sf_dec_stop (self); + break; + default: + break; + } + return ret; +} + +static gboolean +gst_sf_dec_start (GstSFDec * self) +{ + self->file = NULL; + + return TRUE; +} + +static gboolean +gst_sf_dec_stop (GstSFDec * self) +{ + int err = 0; + + g_return_val_if_fail (self->file != NULL, FALSE); + + GST_INFO_OBJECT (self, "Closing sndfile stream"); + + if ((err = sf_close (self->file))) + goto close_failed; + + self->file = NULL; + self->offset = 0; + self->channels = 0; + self->rate = 0; + + return TRUE; + +close_failed: + { + GST_ELEMENT_ERROR (self, RESOURCE, CLOSE, + ("Could not close sndfile stream."), + ("soundfile error: %s", sf_error_number (err))); + return FALSE; + } +} + +static gboolean +gst_sf_dec_sink_activate (GstPad * sinkpad, GstObject * parent) +{ + GstQuery *query; + gboolean pull_mode; + + query = gst_query_new_scheduling (); + + if (!gst_pad_peer_query (sinkpad, query)) { + gst_query_unref (query); + goto activate_push; + } + + pull_mode = gst_query_has_scheduling_mode_with_flags (query, + GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE); + gst_query_unref (query); + + if (!pull_mode) + goto activate_push; + + GST_DEBUG_OBJECT (sinkpad, "activating pull"); + return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE); + +activate_push: + { + GST_DEBUG_OBJECT (sinkpad, "activating push"); + return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE); + } +} + +static gboolean +gst_sf_dec_sink_activate_mode (GstPad * sinkpad, GstObject * parent, + GstPadMode mode, gboolean active) +{ + gboolean res; + + switch (mode) { + case GST_PAD_MODE_PUSH: + res = FALSE; /* no push support */ + break; + case GST_PAD_MODE_PULL: + if (active) { + /* if we have a scheduler we can start the task */ + res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_sf_dec_loop, + sinkpad, NULL); + } else { + res = gst_pad_stop_task (sinkpad); + } + break; + default: + res = FALSE; + break; + } + return res; +} + +static gboolean +gst_sf_dec_open_file (GstSFDec * self) +{ + SF_INFO info; + GstCaps *caps; + GstStructure *s; + GstSegment seg; + gint width; + const gchar *format; + gchar *stream_id; + + GST_DEBUG_OBJECT (self, "opening the stream"); + info.format = 0; + self->file = sf_open_virtual (&gst_sf_vio, SFM_READ, &info, self); + if (!self->file) + goto open_failed; + + stream_id = + gst_pad_create_stream_id (self->srcpad, GST_ELEMENT_CAST (self), NULL); + gst_pad_push_event (self->srcpad, gst_event_new_stream_start (stream_id)); + g_free (stream_id); + + self->channels = info.channels; + self->rate = info.samplerate; + /* TODO(ensonic): do something with info.seekable? */ + /* TODO(ensonic): calculate duration info.frames */ + + /* negotiate srcpad caps */ + if ((caps = gst_pad_get_allowed_caps (self->srcpad)) == NULL) { + caps = gst_pad_get_pad_template_caps (self->srcpad); + } + caps = gst_caps_make_writable (caps); + GST_DEBUG_OBJECT (self, "allowed caps %" GST_PTR_FORMAT, caps); + + s = gst_caps_get_structure (caps, 0); + gst_structure_set (s, + "channels", G_TYPE_INT, self->channels, + "rate", G_TYPE_INT, self->rate, NULL); + + if (!gst_structure_fixate_field_string (s, "format", GST_AUDIO_NE (S16))) + GST_WARNING_OBJECT (self, "Failed to fixate format to S16NE"); + + caps = gst_caps_fixate (caps); + + GST_DEBUG_OBJECT (self, "fixated caps %" GST_PTR_FORMAT, caps); + + /* configure to output the negotiated format */ + s = gst_caps_get_structure (caps, 0); + format = gst_structure_get_string (s, "format"); + if (g_str_equal (format, GST_AUDIO_NE (S32))) { + self->reader = (GstSFReader) sf_readf_int; + width = 32; + } else if (g_str_equal (format, GST_AUDIO_NE (S16))) { + self->reader = (GstSFReader) sf_readf_short; + width = 16; + } else { + self->reader = (GstSFReader) sf_readf_float; + width = 32; + } + self->bytes_per_frame = width * self->channels / 8; + + gst_pad_set_caps (self->srcpad, caps); + gst_caps_unref (caps); + + gst_segment_init (&seg, GST_FORMAT_TIME); + /* TODO: calculate seg.stop = song_length; */ + gst_pad_push_event (self->srcpad, gst_event_new_segment (&seg)); + + return TRUE; + +open_failed: + { + GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, + (_("Could not open sndfile stream for reading.")), + ("soundfile error: %s", sf_strerror (NULL))); + return FALSE; + } +} + +static void +gst_sf_dec_loop (GstPad * pad) +{ + GstSFDec *self = GST_SF_DEC (GST_PAD_PARENT (pad)); + GstBuffer *buf; + GstMapInfo map; + GstFlowReturn flow; + sf_count_t bytes_read; + guint num_frames = 1024; /* arbitrary */ + + if (G_UNLIKELY (!self->file)) { + /* not started yet */ + if (!gst_sf_dec_open_file (self)) + goto pause; + } + + buf = gst_buffer_new_and_alloc (self->bytes_per_frame * num_frames); + gst_buffer_map (buf, &map, GST_MAP_WRITE); + bytes_read = self->reader (self->file, map.data, num_frames); + GST_DEBUG_OBJECT (self, "read %d / %d bytes = %d frames of audio", + (gint) bytes_read, (gint) map.size, num_frames); + gst_buffer_unmap (buf, &map); + + if (G_UNLIKELY (bytes_read < 0)) + goto could_not_read; + + if (G_UNLIKELY (bytes_read == 0)) + goto eos; + + num_frames = bytes_read / self->bytes_per_frame; + + GST_BUFFER_OFFSET (buf) = self->offset; + GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (self->offset, + GST_SECOND, self->rate); + self->offset += num_frames; + GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int (self->offset, + GST_SECOND, self->rate) - GST_BUFFER_TIMESTAMP (buf); + + flow = gst_pad_push (self->srcpad, buf); + if (flow != GST_FLOW_OK) { + GST_LOG_OBJECT (self, "pad push flow: %s", gst_flow_get_name (flow)); + goto pause; + } + + return; + + /* ERROR */ +could_not_read: + { + GST_ELEMENT_ERROR (self, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM); + gst_buffer_unref (buf); + goto pause; + } +eos: + { + GST_DEBUG_OBJECT (self, "EOS"); + gst_buffer_unref (buf); + gst_pad_push_event (self->srcpad, gst_event_new_eos ()); + goto pause; + } +pause: + { + GST_INFO_OBJECT (self, "Pausing"); + gst_pad_pause_task (self->sinkpad); + } +} diff --git a/ext/sndfile/gstsfdec.h b/ext/sndfile/gstsfdec.h new file mode 100644 index 0000000000..a29e23a024 --- /dev/null +++ b/ext/sndfile/gstsfdec.h @@ -0,0 +1,73 @@ +/* GStreamer libsndfile plugin + * Copyright (C) 2013 Stefan Sauer + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef __GST_SF_DEC_H__ +#define __GST_SF_DEC_H__ + + +#include "gstsf.h" +#include + + +G_BEGIN_DECLS + + +#define GST_TYPE_SF_DEC \ + (gst_sf_dec_get_type()) +#define GST_SF_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SF_DEC,GstSFDec)) +#define GST_SF_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SF_DEC,GstSFDecClass)) +#define GST_IS_SF_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SF_DEC)) +#define GST_IS_SF_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SF_DEC)) + +typedef struct _GstSFDec GstSFDec; +typedef struct _GstSFDecClass GstSFDecClass; + +typedef sf_count_t (*GstSFReader)(SNDFILE *f, void *data, sf_count_t nframes); + +struct _GstSFDec { + GstElement parent; + + GstPad *sinkpad; + GstPad *srcpad; + + guint64 pos; + + SNDFILE *file; + sf_count_t offset; + GstSFReader reader; + gint bytes_per_frame; + + gint channels; + gint rate; +}; + +struct _GstSFDecClass { + GstElementClass parent_class; +}; + + +G_END_DECLS + + +#endif /* __GST_SF_DEC_H__ */