diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/Makefile.am | 11 | ||||
-rw-r--r-- | ext/audiofile/Makefile.am | 14 | ||||
-rw-r--r-- | ext/audiofile/README | 39 | ||||
-rw-r--r-- | ext/audiofile/gstafsink.c | 508 | ||||
-rw-r--r-- | ext/audiofile/gstafsink.h | 100 | ||||
-rw-r--r-- | ext/audiofile/gstafsrc.c | 425 | ||||
-rw-r--r-- | ext/audiofile/gstafsrc.h | 106 |
7 files changed, 1201 insertions, 2 deletions
diff --git a/ext/Makefile.am b/ext/Makefile.am index c0ad3282..3d1dd792 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -1,3 +1,9 @@ +if USE_AUDIOFILE +AUDIOFILE_DIR=audiofile +else +AUDIOFILE_DIR= +endif + if USE_ESD ESD_DIR=esd else @@ -29,6 +35,7 @@ VORBIS_DIR= endif -SUBDIRS=$(ESD_DIR) $(LAME_DIR) $(MAD_DIR) $(SDL_DIR) $(VORBIS_DIR) +SUBDIRS=$(AUDIOFILE_DIR) $(ESD_DIR) $(LAME_DIR) $(MAD_DIR) \ + $(SDL_DIR) $(VORBIS_DIR) -DIST_SUBDIRS=esd lame mad sdl vorbis +DIST_SUBDIRS=audiofile esd lame mad sdl vorbis diff --git a/ext/audiofile/Makefile.am b/ext/audiofile/Makefile.am new file mode 100644 index 00000000..7e1ca5ec --- /dev/null +++ b/ext/audiofile/Makefile.am @@ -0,0 +1,14 @@ +plugindir = $(libdir)/gst + +plugin_LTLIBRARIES = libafsink.la libafsrc.la + +libafsink_la_SOURCES = gstafsink.c +libafsrc_la_SOURCES = gstafsrc.c +noinst_HEADERS = gstafsink.h gstafsrc.h + +libafsink_la_LIBADD = $(AUDIOFILE_LIBS) +libafsrc_la_LIBADD = $(AUDIOFILE_LIBS) + +libafsink_la_CFLAGS = $(GST_CFLAGS) +libafsrc_la_CFLAGS = $(GST_CFLAGS) + diff --git a/ext/audiofile/README b/ext/audiofile/README new file mode 100644 index 00000000..4c52c050 --- /dev/null +++ b/ext/audiofile/README @@ -0,0 +1,39 @@ +This plugin wraps the SGI Audiofile +(http://oss.sgi.com/projects/audiofile/) library into a src and sink +element. + +You can read from and write to the supported formats (WAVE, AIFF, AIFFC, +NEXTSND). + +What is supported : +* all the file formats +* integer sample data, both 2's complement and unsigned +* 8 or 16 bit width & depth (haven't tested others) +* sample rate +* some sort of endianness control + +What isn't supported yet : +* float data + +What you can do : +* src element only accepts location argument +* sink element accepts location, endianness and type + - location : file on the system to output + - endianness : at this time endianness is still a bit shady + you can either set 1234 or 4321; + setting it to 4321 will byteswap the buffer data + you might want to keep it at 1234 for now + - type : one of the file types + +Use gstreamer-inspect on afsink and afsrc to see all of the supported +options. + +Examples : + +* tools/gstreamer-launch afsrc location=/opt/media/wav/dark-480-16-m.wav ! afsink type=2 location=/opt/media/wav/dark-480-16-m.aiff + +Future plans : + +* add float support +* wrap up afsink and afsrc with pipe and fork to act like data convertors, + allowing arbitrary choice of sink and src element diff --git a/ext/audiofile/gstafsink.c b/ext/audiofile/gstafsink.c new file mode 100644 index 00000000..b4c584c3 --- /dev/null +++ b/ext/audiofile/gstafsink.c @@ -0,0 +1,508 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> + * 2000 Wim Taymans <wtay@chello.be> + * + * gstafsink.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 <gst/gst.h> +#include "gstafsink.h" + + +static GstElementDetails afsink_details = { + "Audiofile Sink", + "Sink", + "Audiofile sink for audio/raw", + VERSION, + "Thomas <thomas@apestaart.org>", + "(C) 2001" +}; + + +/* AFSink signals and args */ +enum { + /* FILL ME */ + SIGNAL_HANDOFF, + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_TYPE, + ARG_OUTPUT_ENDIANNESS, + ARG_LOCATION +}; + +/* added a sink factory function to force audio/raw MIME type */ +/* I think the caps can be broader, we need to change that somehow */ +GST_PADTEMPLATE_FACTORY (afsink_sink_factory, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "audiofile_sink", + "audio/raw", + "format", GST_PROPS_STRING ("int"), + "law", GST_PROPS_INT (0), + "endianness", GST_PROPS_INT (G_BYTE_ORDER), + "signed", GST_PROPS_LIST ( + GST_PROPS_BOOLEAN (TRUE), + GST_PROPS_BOOLEAN (FALSE) + ), + "width", GST_PROPS_INT_RANGE (8, 16), + "depth", GST_PROPS_INT_RANGE (8, 16), + "rate", GST_PROPS_INT_RANGE (4000, 48000), //FIXME + "channels", GST_PROPS_INT_RANGE (1, 2) + ) +); + +/* we use an enum for the output type arg */ + +#define GST_TYPE_AFSINK_TYPES (gst_afsink_types_get_type()) +/* FIXME: fix the string ints to be string-converted from the audiofile.h types */ +static GType +gst_afsink_types_get_type (void) +{ + static GType afsink_types_type = 0; + static GEnumValue afsink_types[] = { + {AF_FILE_RAWDATA, "0", "raw PCM"}, + {AF_FILE_AIFFC, "1", "AIFFC"}, + {AF_FILE_AIFF, "2", "AIFF"}, + {AF_FILE_NEXTSND, "3", "Next/SND"}, + {AF_FILE_WAVE, "4", "Wave"}, + {0, NULL, NULL}, + }; + + if (!afsink_types_type) + { + afsink_types_type = g_enum_register_static ("GstAudiosinkTypes", afsink_types); + } + return afsink_types_type; +} + +static void gst_afsink_class_init (GstAFSinkClass *klass); +static void gst_afsink_init (GstAFSink *afsink); + +static gboolean gst_afsink_open_file (GstAFSink *sink); +static void gst_afsink_close_file (GstAFSink *sink); + +static void gst_afsink_chain (GstPad *pad,GstBuffer *buf); + +static void gst_afsink_set_property (GObject *object, guint prop_id, const GValue *value, + GParamSpec *pspec); +static void gst_afsink_get_property (GObject *object, guint prop_id, GValue *value, + GParamSpec *pspec); + +static gboolean gst_afsink_handle_event (GstPad *pad, GstEvent *event); + +static GstElementStateReturn gst_afsink_change_state (GstElement *element); + +static GstElementClass *parent_class = NULL; +static guint gst_afsink_signals[LAST_SIGNAL] = { 0 }; + +GType +gst_afsink_get_type (void) +{ + static GType afsink_type = 0; + + if (!afsink_type) { + static const GTypeInfo afsink_info = { + sizeof (GstAFSinkClass), NULL, + NULL, + (GClassInitFunc) gst_afsink_class_init, + NULL, + NULL, + sizeof (GstAFSink), + 0, + (GInstanceInitFunc) gst_afsink_init, + }; + afsink_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAFSink", &afsink_info, 0); + } + return afsink_type; +} + +static void +gst_afsink_class_init (GstAFSinkClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass*)klass; + gstelement_class = (GstElementClass*)klass; + + parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + + gst_element_install_std_props ( + GST_ELEMENT_CLASS (klass), + "location", ARG_LOCATION, G_PARAM_READWRITE, + NULL); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_TYPE, + g_param_spec_enum("type","type","type", + GST_TYPE_AFSINK_TYPES,0,G_PARAM_READWRITE)); // CHECKME! + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_OUTPUT_ENDIANNESS, + g_param_spec_int("endianness","endianness","endianness", + G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); // CHECKME + + gst_afsink_signals[SIGNAL_HANDOFF] = + g_signal_new ("handoff", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstAFSinkClass, handoff), NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + + gobject_class->set_property = gst_afsink_set_property; + gobject_class->get_property = gst_afsink_get_property; + + gstelement_class->change_state = gst_afsink_change_state; +} + +static void +gst_afsink_init (GstAFSink *afsink) +{ + // GstPad *pad; this is now done in the struct + + afsink->sinkpad = gst_pad_new_from_template ( + GST_PADTEMPLATE_GET (afsink_sink_factory), "sink"); + gst_element_add_pad (GST_ELEMENT (afsink), afsink->sinkpad); + + gst_pad_set_chain_function (afsink->sinkpad, gst_afsink_chain); + gst_pad_set_event_function (afsink->sinkpad, gst_afsink_handle_event); + + afsink->filename = NULL; + afsink->file = NULL; + /* default values, should never be needed */ + afsink->channels = 2; + afsink->width = 16; + afsink->rate = 44100; + afsink->type = AF_FILE_WAVE; + afsink->endianness_data = 1234; + afsink->endianness_wanted = 1234; +} + +static void +gst_afsink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstAFSink *sink; + + /* it's not null if we got it, but it might not be ours */ + sink = GST_AFSINK (object); + + switch (prop_id) { + case ARG_LOCATION: + /* the element must be stopped or paused in order to do this */ + g_return_if_fail ((GST_STATE (sink) < GST_STATE_PLAYING) + || (GST_STATE (sink) == GST_STATE_PAUSED)); + if (sink->filename) + g_free (sink->filename); + sink->filename = g_strdup (g_value_get_string (value)); + if ( (GST_STATE (sink) == GST_STATE_PAUSED) + && (sink->filename != NULL)) + { + gst_afsink_close_file (sink); + gst_afsink_open_file (sink); + } + + break; + case ARG_TYPE: + sink->type = g_value_get_enum (value); + break; + case ARG_OUTPUT_ENDIANNESS: + { + int end = g_value_get_int (value); + if (end == 1234 || end == 4321) + sink->endianness_output = end; + } + break; + default: + break; + } +} + +static void +gst_afsink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstAFSink *sink; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_AFSINK (object)); + + sink = GST_AFSINK (object); + + switch (prop_id) { + case ARG_LOCATION: + g_value_set_string (value, sink->filename); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + factory = gst_elementfactory_new ("afsink", GST_TYPE_AFSINK, + &afsink_details); + g_return_val_if_fail (factory != NULL, FALSE); + + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (afsink_sink_factory)); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "afsink", + plugin_init +}; + +/* this is where we open the audiofile */ +static gboolean +gst_afsink_open_file (GstAFSink *sink) +{ + AFfilesetup outfilesetup; + GstCaps *caps; + int sample_format; /* audiofile's sample format, look in audiofile.h */ + int byte_order = 0; /* audiofile's byte order defines */ + + g_return_val_if_fail (!GST_FLAG_IS_SET (sink, GST_AFSINK_OPEN), FALSE); + + /* open the file */ +/* we use audiofile now + sink->file = fopen (sink->filename, "w"); + if (sink->file == NULL) { + perror ("open"); + gst_element_error (GST_ELEMENT (sink), g_strconcat("opening file \"", sink->filename, "\"", NULL)); + return FALSE; + } +*/ + + /* get the audio parameters */ + caps = NULL; + g_return_val_if_fail (GST_IS_PAD (sink->sinkpad), FALSE); + caps = GST_PAD_CAPS (sink->sinkpad); + + if (caps == NULL) + { + // FIXME : Please change this to a better warning method ! + printf ("WARNING: gstafsink chain : Could not get caps of pad !\n"); + } + else + { + sink->channels = gst_caps_get_int (caps, "channels"); + sink->width = gst_caps_get_int (caps, "width"); + sink->rate = gst_caps_get_int (caps, "rate"); + sink->is_signed = gst_caps_get_int (caps, "signed"); + sink->endianness_data = gst_caps_get_int (caps, "endianness"); + } + GST_DEBUG (GST_CAT_PLUGIN_INFO, "channels %d, width %d, rate %d, signed %s\n", + sink->channels, sink->width, sink->rate, + sink->is_signed ? "yes" : "no"); + GST_DEBUG (GST_CAT_PLUGIN_INFO, "endianness: data %d, output %d\n", + sink->endianness_data, sink->endianness_output); + /* setup the output file */ + if (sink->is_signed) + sample_format = AF_SAMPFMT_TWOSCOMP; + else + sample_format = AF_SAMPFMT_UNSIGNED; + // FIXME : this check didn't seem to work, so let the output endianness be set */ + /* + if (sink->endianness_data == sink->endianness_wanted) + byte_order = AF_BYTEORDER_LITTLEENDIAN; + else + byte_order = AF_BYTEORDER_BIGENDIAN; + */ + if (sink->endianness_output == 1234) + byte_order = AF_BYTEORDER_LITTLEENDIAN; + else + byte_order = AF_BYTEORDER_BIGENDIAN; + + outfilesetup = afNewFileSetup (); + afInitFileFormat (outfilesetup, sink->type); + afInitChannels (outfilesetup, AF_DEFAULT_TRACK, sink->channels); + afInitRate (outfilesetup, AF_DEFAULT_TRACK, sink->rate); + afInitSampleFormat (outfilesetup, AF_DEFAULT_TRACK, + sample_format, sink->width); + + /* open it */ + sink->file = afOpenFile (sink->filename, "w", outfilesetup); + if (sink->file == AF_NULL_FILEHANDLE) + { + perror ("open"); + gst_element_error (GST_ELEMENT (sink), g_strconcat("opening file \"", sink->filename, "\"", NULL)); + return FALSE; + } + + afFreeFileSetup (outfilesetup); +// afSetVirtualByteOrder (sink->file, AF_DEFAULT_TRACK, byte_order); + + GST_FLAG_SET (sink, GST_AFSINK_OPEN); + + return TRUE; +} + +static void +gst_afsink_close_file (GstAFSink *sink) +{ +// g_print ("DEBUG: closing sinkfile...\n"); + g_return_if_fail (GST_FLAG_IS_SET (sink, GST_AFSINK_OPEN)); +// g_print ("DEBUG: past flag test\n"); +// if (fclose (sink->file) != 0) + if (afCloseFile (sink->file) != 0) + { + g_print ("WARNING: afsink: oops, error closing !\n"); + perror ("close"); + gst_element_error (GST_ELEMENT (sink), g_strconcat("closing file \"", sink->filename, "\"", NULL)); + } + else { + GST_FLAG_UNSET (sink, GST_AFSINK_OPEN); + } +} + +/** + * gst_afsink_chain: + * @pad: the pad this afsink is connected to + * @buf: the buffer that has to be absorbed + * + * take the buffer from the pad and write to file if it's open + */ +static void +gst_afsink_chain (GstPad *pad, GstBuffer *buf) +{ + GstAFSink *afsink; + int ret = 0; + + g_return_if_fail (pad != NULL); + g_return_if_fail (GST_IS_PAD (pad)); + g_return_if_fail (buf != NULL); + + afsink = GST_AFSINK (gst_pad_get_parent (pad)); +/* we use audiofile now + if (GST_FLAG_IS_SET (afsink, GST_AFSINK_OPEN)) + { + bytes_written = fwrite (GST_BUFFER_DATA (buf), 1, GST_BUFFER_SIZE (buf), afsink->file); + if (bytes_written < GST_BUFFER_SIZE (buf)) + { + printf ("afsink : Warning : %d bytes should be written, only %d bytes written\n", + GST_BUFFER_SIZE (buf), bytes_written); + } + } +*/ + + if (!GST_FLAG_IS_SET (afsink, GST_AFSINK_OPEN)) + { + /* it's not open yet, open it */ + if (!gst_afsink_open_file (afsink)) + g_print ("WARNING: gstafsink: can't open file !\n"); +// return FALSE; Can't return value + } + + if (GST_FLAG_IS_SET (afsink, GST_AFSINK_OPEN)) + { + int frameCount = 0; + + frameCount = GST_BUFFER_SIZE (buf) / ((afsink->width / 8) * afsink->channels); + // g_print ("DEBUG: writing %d frames ", frameCount); + ret = afWriteFrames (afsink->file, AF_DEFAULT_TRACK, + GST_BUFFER_DATA (buf), frameCount); + if (ret == AF_BAD_WRITE || ret == AF_BAD_LSEEK) + { + printf ("afsink : Warning : afWriteFrames returned an error (%d)\n", ret); + } + } + + gst_buffer_unref (buf); + + g_signal_emit (G_OBJECT (afsink), gst_afsink_signals[SIGNAL_HANDOFF], 0); +} + +static GstElementStateReturn +gst_afsink_change_state (GstElement *element) +{ + g_return_val_if_fail (GST_IS_AFSINK (element), GST_STATE_FAILURE); + + /* if going to NULL? then close the file */ + if (GST_STATE_PENDING (element) == GST_STATE_NULL) + { +// printf ("DEBUG: afsink state change: null pending\n"); + if (GST_FLAG_IS_SET (element, GST_AFSINK_OPEN)) + { +// g_print ("DEBUG: trying to close the sink file\n"); + gst_afsink_close_file (GST_AFSINK (element)); + } + } +/* + + else +// this has been moved to the chain function, since it's only then that +// the caps are set and can be known + { +// g_print ("DEBUG: it's not going to null\n"); + if (!GST_FLAG_IS_SET (element, GST_AFSINK_OPEN)) + { +// g_print ("DEBUG: GST_AFSINK_OPEN not set\n"); + if (!gst_afsink_open_file (GST_AFSINK (element))) + { +// g_print ("DEBUG: element tries to open file\n"); + return GST_STATE_FAILURE; + } + } + } +*/ + + if (GST_ELEMENT_CLASS (parent_class)->change_state) + return GST_ELEMENT_CLASS (parent_class)->change_state (element); + + return GST_STATE_SUCCESS; +} + +/* this function was copied from sinesrc */ + +static gboolean +gst_afsink_handle_event (GstPad *pad, GstEvent *event) +{ + GstAFSink *afsink; + + afsink = GST_AFSINK (gst_pad_get_parent (pad)); + GST_DEBUG (0, "DEBUG: afsink: got event\n"); + gst_afsink_close_file (afsink); + GST_FLAG_SET (pad, GST_PAD_EOS); + + return TRUE; +} + +/* +gboolean +gst_afsink_factory_init (GstElementFactory *factory) +{ + GstPadTemplate *sink_pt; + sink_pt = afsink_sink_factory(); + gst_elementfactory_add_padtemplate (factory, sink_pt); + + return TRUE; + +} +*/ + diff --git a/ext/audiofile/gstafsink.h b/ext/audiofile/gstafsink.h new file mode 100644 index 00000000..297aed36 --- /dev/null +++ b/ext/audiofile/gstafsink.h @@ -0,0 +1,100 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> + * 2000 Wim Taymans <wtay@chello.be> + * + * gstafsink.h: + * + * 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_AFSINK_H__ +#define __GST_AFSINK_H__ + + +#include <config.h> +#include <gst/gst.h> +#include <audiofile.h> /* what else are we to do */ + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +//GstElementDetails gst_afsink_details; + + +#define GST_TYPE_AFSINK \ + (gst_afsink_get_type()) +#define GST_AFSINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AFSINK,GstAFSink)) +#define GST_AFSINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AFSINK,GstAFSinkClass)) +#define GST_IS_AFSINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AFSINK)) +#define GST_IS_AFSINK_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AFSINK)) + +typedef struct _GstAFSink GstAFSink; +typedef struct _GstAFSinkClass GstAFSinkClass; + +typedef enum { + GST_AFSINK_OPEN = GST_ELEMENT_FLAG_LAST, + + GST_AFSINK_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 2, +} GstAFSinkFlags; + +struct _GstAFSink { + GstElement element; + GstPad *sinkpad; + + gchar *filename; +// FILE *file; + +// AFfilesetup outfilesetup; + AFfilehandle file; + int format; + int channels; + int width; + unsigned int rate; + gboolean is_signed; + int type; /* type of output, compare to audiofile.h + * RAW, AIFF, AIFFC, NEXTSND, WAVE + */ + // FIXME : endianness is a little cryptic at this point + int endianness_data; /* 4321 or 1234 */ + int endianness_wanted; /* same thing, but what the output format wants */ + int endianness_output; /* what the output endianness will be */ +}; + +struct _GstAFSinkClass { + GstElementClass parent_class; + + /* signals */ + void (*handoff) (GstElement *element,GstPad *pad); +}; + +GType gst_afsink_get_type(void); +//gboolean gst_afsink_factory_init (GstElementFactory *factory); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_AFSINK_H__ */ diff --git a/ext/audiofile/gstafsrc.c b/ext/audiofile/gstafsrc.c new file mode 100644 index 00000000..ceb548f6 --- /dev/null +++ b/ext/audiofile/gstafsrc.c @@ -0,0 +1,425 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> + * 2000 Wim Taymans <wtay@chello.be> + * + * gstafsrc.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 <gst/gst.h> +#include <gst-libs/audio/gstaudio.h> +#include "gstafsrc.h" + + +static GstElementDetails afsrc_details = { + "Audiofile Src", + "Src", + "Audiofile src for audio/raw", + VERSION, + "Thomas <thomas@apestaart.org>", + "(C) 2001" +}; + + +/* AFSrc signals and args */ +enum { + /* FILL ME */ + SIGNAL_HANDOFF, + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_LOCATION +}; + +/* added a src factory function to force audio/raw MIME type */ +/* I think the caps can be broader, we need to change that somehow */ +GST_PADTEMPLATE_FACTORY (afsrc_src_factory, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "audiofile_src", + "audio/raw", + "format", GST_PROPS_STRING ("int"), + "law", GST_PROPS_INT (0), + "endianness", GST_PROPS_INT (G_BYTE_ORDER), + "signed", GST_PROPS_LIST ( + GST_PROPS_BOOLEAN (TRUE), + GST_PROPS_BOOLEAN (FALSE) + ), + "width", GST_PROPS_INT_RANGE (8, 16), + "depth", GST_PROPS_INT_RANGE (8, 16), + "rate", GST_PROPS_INT_RANGE (4000, 48000), //FIXME + "channels", GST_PROPS_INT_RANGE (1, 2) + ) +); + +/* we use an enum for the output type arg */ + +#define GST_TYPE_AFSRC_TYPES (gst_afsrc_types_get_type()) + +/* FIXME: fix the string ints to be string-converted from the audiofile.h types */ + +static GType +gst_afsrc_types_get_type (void) +{ + static GType afsrc_types_type = 0; + static GEnumValue afsrc_types[] = { + {AF_FILE_RAWDATA, "0", "raw PCM"}, + {AF_FILE_AIFFC, "1", "AIFFC"}, + {AF_FILE_AIFF, "2", "AIFF"}, + {AF_FILE_NEXTSND, "3", "Next/SND"}, + {AF_FILE_WAVE, "4", "Wave"}, + {0, NULL, NULL}, + }; + + if (!afsrc_types_type) + { + afsrc_types_type = g_enum_register_static ("GstAudiosrcTypes", afsrc_types); + } + return afsrc_types_type; +} + +static void gst_afsrc_class_init (GstAFSrcClass *klass); +static void gst_afsrc_init (GstAFSrc *afsrc); + +static gboolean gst_afsrc_open_file (GstAFSrc *src); +static void gst_afsrc_close_file (GstAFSrc *src); + +static GstBuffer* gst_afsrc_get (GstPad *pad); + +static void gst_afsrc_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec); +static void gst_afsrc_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec); + +static GstElementStateReturn gst_afsrc_change_state (GstElement *element); + +static GstElementClass *parent_class = NULL; +static guint gst_afsrc_signals[LAST_SIGNAL] = { 0 }; + +GType +gst_afsrc_get_type (void) +{ + static GType afsrc_type = 0; + + if (!afsrc_type) { + static const GTypeInfo afsrc_info = { + sizeof (GstAFSrcClass), NULL, + NULL, + (GClassInitFunc) gst_afsrc_class_init, + NULL, + NULL, + sizeof (GstAFSrc), + 0, + (GInstanceInitFunc) gst_afsrc_init, + }; + afsrc_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAFSrc", &afsrc_info, 0); + } + return afsrc_type; +} + +static void +gst_afsrc_class_init (GstAFSrcClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass*)klass; + gstelement_class = (GstElementClass*)klass; + + parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + + gst_element_install_std_props ( + GST_ELEMENT_CLASS (klass), + "location", ARG_LOCATION, G_PARAM_READWRITE, + NULL); + + gst_afsrc_signals[SIGNAL_HANDOFF] = + g_signal_new ("handoff", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstAFSrcClass, handoff), NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + + gobject_class->set_property = gst_afsrc_set_property; + gobject_class->get_property = gst_afsrc_get_property; + + gstelement_class->change_state = gst_afsrc_change_state; +} + +static void +gst_afsrc_init (GstAFSrc *afsrc) +{ + // GstPad *pad; this is now done in the struct + + /* no need for a template, caps are set based on file, right ? */ + afsrc->srcpad = gst_pad_new ("src", GST_PAD_SRC); + gst_element_add_pad (GST_ELEMENT (afsrc), afsrc->srcpad); + gst_pad_set_get_function (afsrc->srcpad, gst_afsrc_get); + + afsrc->bytes_per_read = 4096; + afsrc->curoffset = 0; + afsrc->seq = 0; + + afsrc->filename = NULL; + afsrc->file = NULL; + /* default values, should never be needed */ + afsrc->channels = 2; + afsrc->width = 16; + afsrc->rate = 44100; + afsrc->type = AF_FILE_WAVE; + afsrc->endianness_data = 1234; + afsrc->endianness_wanted = 1234; + afsrc->framestamp = 0; +} + +static GstBuffer * +gst_afsrc_get (GstPad *pad) +{ + GstAFSrc *src; + GstBuffer *buf; + + glong readbytes, readframes; + glong frameCount; + + g_return_val_if_fail (pad != NULL, NULL); + src = GST_AFSRC (gst_pad_get_parent (pad)); + + buf = gst_buffer_new (); + g_return_val_if_fail (buf, NULL); + + GST_BUFFER_DATA (buf) = (gpointer) g_malloc (src->bytes_per_read); + + /* calculate frameCount to read based on file info */ + + frameCount = src->bytes_per_read / (src->channels * src->width / 8); +// g_print ("DEBUG: gstafsrc: going to read %ld frames\n", frameCount); + readframes = afReadFrames (src->file, AF_DEFAULT_TRACK, GST_BUFFER_DATA (buf), + frameCount); + readbytes = readframes * (src->channels * src->width / 8); + if (readbytes == 0) { + gst_element_signal_eos (GST_ELEMENT (src)); + return NULL; + } + + GST_BUFFER_SIZE (buf) = readbytes; + GST_BUFFER_OFFSET (buf) = src->curoffset; + + src->curoffset += readbytes; + + src->framestamp += gst_audio_frame_length (src->srcpad, buf); + GST_BUFFER_TIMESTAMP (buf) = src->framestamp * 1E9 + / gst_audio_frame_rate (src->srcpad); + printf ("DEBUG: afsrc: timestamp set on output buffer: %f sec\n", + GST_BUFFER_TIMESTAMP (buf) / 1E9); + +// g_print("DEBUG: gstafsrc: pushed buffer of %ld bytes\n", readbytes); + return buf; +} + +static void +gst_afsrc_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstAFSrc *src; + + /* it's not null if we got it, but it might not be ours */ + src = GST_AFSRC (object); + + switch (prop_id) { + case ARG_LOCATION: + if (src->filename) + g_free (src->filename); + src->filename = g_strdup (g_value_get_string (value)); + break; + default: + break; + } +} + +static void +gst_afsrc_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstAFSrc *src; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_AFSRC (object)); + + src = GST_AFSRC (object); + + switch (prop_id) { + case ARG_LOCATION: + g_value_set_string (value, src->filename); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + factory = gst_elementfactory_new ("afsrc", GST_TYPE_AFSRC, + &afsrc_details); + g_return_val_if_fail (factory != NULL, FALSE); + + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (afsrc_src_factory)); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + /* load audio support library */ + if (!gst_library_load ("gstaudio")) + { + gst_info ("mad: could not load support library: 'gstaudio'\n"); + return FALSE; + } + + return TRUE; +} + + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "afsrc", + plugin_init +}; + +/* this is where we open the audiofile */ +static gboolean +gst_afsrc_open_file (GstAFSrc *src) +{ + g_return_val_if_fail (!GST_FLAG_IS_SET (src, GST_AFSRC_OPEN), FALSE); + + /* open the file */ + src->file = afOpenFile (src->filename, "r", AF_NULL_FILESETUP); + if (src->file == AF_NULL_FILEHANDLE) + { + g_print ("ERROR: gstafsrc: Could not open file %s for reading\n", + src->filename); + gst_element_error (GST_ELEMENT (src), g_strconcat ("opening file \"", + src->filename, "\"", NULL)); + return FALSE; + } + + /* get the audiofile audio parameters */ + { + int sampleFormat, sampleWidth; + src->channels = afGetChannels (src->file, AF_DEFAULT_TRACK); + afGetSampleFormat (src->file, AF_DEFAULT_TRACK, + &sampleFormat, &sampleWidth); + switch (sampleFormat) + { + case AF_SAMPFMT_TWOSCOMP: + src->is_signed = TRUE; + break; + case AF_SAMPFMT_UNSIGNED: + src->is_signed = FALSE; + break; + case AF_SAMPFMT_FLOAT: + case AF_SAMPFMT_DOUBLE: + GST_DEBUG (GST_CAT_PLUGIN_INFO, + "ERROR: float data not supported yet !\n"); + } + src->rate = (guint) afGetRate (src->file, AF_DEFAULT_TRACK); + src->width = sampleWidth; + GST_DEBUG (GST_CAT_PLUGIN_INFO, + "input file: %d channels, %d width, %d rate, signed %s\n", + src->channels, src->width, src->rate, + src->is_signed ? "yes" : "no"); + } + + /* set caps on src */ + //FIXME: add all the possible formats, especially float ! */ + gst_pad_set_caps (src->srcpad, gst_caps_new ( + "af_src", + "audio/raw", + gst_props_new ( + "format", GST_PROPS_STRING ("int"), + "law", GST_PROPS_INT (0), //FIXME + "endianness", GST_PROPS_INT (G_BYTE_ORDER), //FIXME + "signed", GST_PROPS_BOOLEAN (src->is_signed), + "width", GST_PROPS_INT (src->width), + "depth", GST_PROPS_INT (src->width), + "rate", GST_PROPS_INT (src->rate), + "channels", GST_PROPS_INT (src->channels), + NULL + ) + )); + + GST_FLAG_SET (src, GST_AFSRC_OPEN); + + return TRUE; +} + +static void +gst_afsrc_close_file (GstAFSrc *src) +{ +// g_print ("DEBUG: closing srcfile...\n"); + g_return_if_fail (GST_FLAG_IS_SET (src, GST_AFSRC_OPEN)); +// g_print ("DEBUG: past flag test\n"); +// if (fclose (src->file) != 0) + if (afCloseFile (src->file) != 0) + { + g_print ("WARNING: afsrc: oops, error closing !\n"); + perror ("close"); + gst_element_error (GST_ELEMENT (src), g_strconcat("closing file \"", src->filename, "\"", NULL)); + } + else { + GST_FLAG_UNSET (src, GST_AFSRC_OPEN); + } +} + +static GstElementStateReturn +gst_afsrc_change_state (GstElement *element) +{ + g_return_val_if_fail (GST_IS_AFSRC (element), GST_STATE_FAILURE); + + /* if going to NULL then close the file */ + if (GST_STATE_PENDING (element) == GST_STATE_NULL) + { +// printf ("DEBUG: afsrc state change: null pending\n"); + if (GST_FLAG_IS_SET (element, GST_AFSRC_OPEN)) + { +// g_print ("DEBUG: trying to close the src file\n"); + gst_afsrc_close_file (GST_AFSRC (element)); + } + } + else if (GST_STATE_PENDING (element) == GST_STATE_READY) + { +// g_print ("DEBUG: afsrc: ready state pending. This shouldn't happen at the *end* of a stream\n"); + if (!GST_FLAG_IS_SET (element, GST_AFSRC_OPEN)) + { +// g_print ("DEBUG: GST_AFSRC_OPEN not set\n"); + if (!gst_afsrc_open_file (GST_AFSRC (element))) + { +// g_print ("DEBUG: element tries to open file\n"); + return GST_STATE_FAILURE; + } + } + } + + if (GST_ELEMENT_CLASS (parent_class)->change_state) + return GST_ELEMENT_CLASS (parent_class)->change_state (element); + + return GST_STATE_SUCCESS; +} diff --git a/ext/audiofile/gstafsrc.h b/ext/audiofile/gstafsrc.h new file mode 100644 index 00000000..38116e4b --- /dev/null +++ b/ext/audiofile/gstafsrc.h @@ -0,0 +1,106 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> + * 2000 Wim Taymans <wtay@chello.be> + * + * gstafsrc.h: + * + * 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_AFSRC_H__ +#define __GST_AFSRC_H__ + + +#include <config.h> +#include <gst/gst.h> +#include <audiofile.h> /* what else are we to do */ + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +//GstElementDetails gst_afsrc_details; + + +#define GST_TYPE_AFSRC \ + (gst_afsrc_get_type()) +#define GST_AFSRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AFSRC,GstAFSrc)) +#define GST_AFSRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AFSRC,GstAFSrcClass)) +#define GST_IS_AFSRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AFSRC)) +#define GST_IS_AFSRC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AFSRC)) + +typedef struct _GstAFSrc GstAFSrc; +typedef struct _GstAFSrcClass GstAFSrcClass; + +typedef enum { + GST_AFSRC_OPEN = GST_ELEMENT_FLAG_LAST, + + GST_AFSRC_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 2, +} GstAFSrcFlags; + +struct _GstAFSrc { + GstElement element; + GstPad *srcpad; + + gchar *filename; +// FILE *file; + +// AFfilesetup outfilesetup; + AFfilehandle file; + int format; + int channels; + int width; + unsigned int rate; + gboolean is_signed; + int type; /* type of output, compare to audiofile.h + * RAW, AIFF, AIFFC, NEXTSND, WAVE + */ + /* blocking */ + gulong curoffset; + gulong bytes_per_read; + + gulong seq; + guint64 framestamp; + // FIXME : endianness is a little cryptic at this point + int endianness_data; /* 4321 or 1234 */ + int endianness_wanted; /* same thing, but what the output format wants */ + int endianness_output; /* what the output endianness will be */ +}; + +struct _GstAFSrcClass { + GstElementClass parent_class; + + /* signals */ + void (*handoff) (GstElement *element,GstPad *pad); +}; + +GType gst_afsrc_get_type(void); +//gboolean gst_afsrc_factory_init (GstElementFactory *factory); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_AFSRC_H__ */ |