/* * GStreamer - SunAudio source * Copyright (C) 2005,2006 Sun Microsystems, Inc., * Brian Cameron <brian.cameron@sun.com> * * gstsunaudiosrc.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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** * SECTION:element-sunaudiosrc * * sunaudiosrc is an audio source designed to work with the Sun Audio * interface available in Solaris. * * <refsect2> * <title>Example launch line</title> * |[ * gst-launch-1.0 sunaudiosrc ! wavenc ! filesink location=audio.wav * ]| * </refsect2> */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <fcntl.h> #include <string.h> #include <unistd.h> #include <stropts.h> #include <sys/mixer.h> #include "gstsunaudiosrc.h" GST_DEBUG_CATEGORY_EXTERN (sunaudio_debug); #define GST_CAT_DEFAULT sunaudio_debug static void gst_sunaudiosrc_base_init (gpointer g_class); static void gst_sunaudiosrc_class_init (GstSunAudioSrcClass * klass); static void gst_sunaudiosrc_init (GstSunAudioSrc * sunaudiosrc, GstSunAudioSrcClass * g_class); static void gst_sunaudiosrc_dispose (GObject * object); static void gst_sunaudiosrc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_sunaudiosrc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static GstCaps *gst_sunaudiosrc_getcaps (GstBaseSrc * bsrc); static gboolean gst_sunaudiosrc_open (GstAudioSrc * asrc); static gboolean gst_sunaudiosrc_close (GstAudioSrc * asrc); static gboolean gst_sunaudiosrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec); static gboolean gst_sunaudiosrc_unprepare (GstAudioSrc * asrc); static guint gst_sunaudiosrc_read (GstAudioSrc * asrc, gpointer data, guint length); static guint gst_sunaudiosrc_delay (GstAudioSrc * asrc); static void gst_sunaudiosrc_reset (GstAudioSrc * asrc); #define DEFAULT_DEVICE "/dev/audio" enum { PROP_0, PROP_DEVICE }; GST_BOILERPLATE_WITH_INTERFACE (GstSunAudioSrc, gst_sunaudiosrc, GstAudioSrc, GST_TYPE_AUDIO_SRC, GstMixer, GST_TYPE_MIXER, gst_sunaudiosrc); GST_IMPLEMENT_SUNAUDIO_MIXER_CTRL_METHODS (GstSunAudioSrc, gst_sunaudiosrc); static GstStaticPadTemplate gst_sunaudiosrc_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-raw-int, " "endianness = (int) BYTE_ORDER, " "signed = (boolean) TRUE, " "width = (int) 16, " "depth = (int) 16, " /* [5510,48000] seems to be a Solaris limit */ "rate = (int) [ 5510, 48000 ], " "channels = (int) [ 1, 2 ]") ); static void gst_sunaudiosrc_dispose (GObject * object) { G_OBJECT_CLASS (parent_class)->dispose (object); } static void gst_sunaudiosrc_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 (&gst_sunaudiosrc_factory)); gst_element_class_set_static_metadata (element_class, "Sun Audio Source", "Source/Audio", "Audio source for Sun Audio devices", "Brian Cameron <brian.cameron@sun.com>"); } static void gst_sunaudiosrc_class_init (GstSunAudioSrcClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; GstBaseSrcClass *gstbasesrc_class; GstBaseAudioSrcClass *gstbaseaudiosrc_class; GstAudioSrcClass *gstaudiosrc_class; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; gstbasesrc_class = (GstBaseSrcClass *) klass; gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass; gstaudiosrc_class = (GstAudioSrcClass *) klass; gobject_class->dispose = gst_sunaudiosrc_dispose; gobject_class->get_property = gst_sunaudiosrc_get_property; gobject_class->set_property = gst_sunaudiosrc_set_property; gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_sunaudiosrc_getcaps); gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_sunaudiosrc_open); gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_sunaudiosrc_prepare); gstaudiosrc_class->unprepare = GST_DEBUG_FUNCPTR (gst_sunaudiosrc_unprepare); gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_sunaudiosrc_close); gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_sunaudiosrc_read); gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_sunaudiosrc_delay); gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_sunaudiosrc_reset); g_object_class_install_property (gobject_class, PROP_DEVICE, g_param_spec_string ("device", "Device", "SunAudio device (usually /dev/audio)", DEFAULT_DEVICE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void gst_sunaudiosrc_init (GstSunAudioSrc * sunaudiosrc, GstSunAudioSrcClass * g_class) { const char *audiodev; GST_DEBUG_OBJECT (sunaudiosrc, "initializing sunaudiosrc"); sunaudiosrc->fd = -1; audiodev = g_getenv ("AUDIODEV"); if (audiodev == NULL) audiodev = DEFAULT_DEVICE; sunaudiosrc->device = g_strdup (audiodev); } static void gst_sunaudiosrc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstSunAudioSrc *sunaudiosrc; sunaudiosrc = GST_SUNAUDIO_SRC (object); switch (prop_id) { case PROP_DEVICE: if (sunaudiosrc->device) g_free (sunaudiosrc->device); sunaudiosrc->device = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gst_sunaudiosrc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstSunAudioSrc *sunaudiosrc; sunaudiosrc = GST_SUNAUDIO_SRC (object); switch (prop_id) { case PROP_DEVICE: g_value_set_string (value, sunaudiosrc->device); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static GstCaps * gst_sunaudiosrc_getcaps (GstBaseSrc * bsrc) { GstPadTemplate *pad_template; GstCaps *caps = NULL; GstSunAudioSrc *sunaudiosrc = GST_SUNAUDIO_SRC (bsrc); GST_DEBUG_OBJECT (sunaudiosrc, "getcaps called"); pad_template = gst_static_pad_template_get (&gst_sunaudiosrc_factory); caps = gst_caps_copy (gst_pad_template_get_caps (pad_template)); gst_object_unref (pad_template); return caps; } static gboolean gst_sunaudiosrc_open (GstAudioSrc * asrc) { GstSunAudioSrc *sunaudiosrc = GST_SUNAUDIO_SRC (asrc); int fd, ret; fd = open (sunaudiosrc->device, O_RDONLY); if (fd == -1) { GST_ELEMENT_ERROR (sunaudiosrc, RESOURCE, OPEN_READ, (NULL), ("can't open connection to Sun Audio device %s", sunaudiosrc->device)); return FALSE; } sunaudiosrc->fd = fd; ret = ioctl (fd, AUDIO_GETDEV, &sunaudiosrc->dev); if (ret == -1) { GST_ELEMENT_ERROR (sunaudiosrc, RESOURCE, SETTINGS, (NULL), ("%s", strerror (errno))); return FALSE; } GST_DEBUG_OBJECT (sunaudiosrc, "name %s", sunaudiosrc->dev.name); GST_DEBUG_OBJECT (sunaudiosrc, "version %s", sunaudiosrc->dev.version); GST_DEBUG_OBJECT (sunaudiosrc, "config %s", sunaudiosrc->dev.config); ret = ioctl (fd, AUDIO_GETINFO, &sunaudiosrc->info); if (ret == -1) { GST_ELEMENT_ERROR (sunaudiosrc, RESOURCE, SETTINGS, (NULL), ("%s", strerror (errno))); return FALSE; } GST_DEBUG_OBJECT (sunaudiosrc, "monitor_gain %d", sunaudiosrc->info.monitor_gain); GST_DEBUG_OBJECT (sunaudiosrc, "output_muted %d", sunaudiosrc->info.output_muted); GST_DEBUG_OBJECT (sunaudiosrc, "hw_features %08x", sunaudiosrc->info.hw_features); GST_DEBUG_OBJECT (sunaudiosrc, "sw_features %08x", sunaudiosrc->info.sw_features); GST_DEBUG_OBJECT (sunaudiosrc, "sw_features_enabled %08x", sunaudiosrc->info.sw_features_enabled); if (!sunaudiosrc->mixer) { const char *audiodev; audiodev = g_getenv ("AUDIODEV"); if (audiodev == NULL) { sunaudiosrc->mixer = gst_sunaudiomixer_ctrl_new ("/dev/audioctl"); } else { gchar *device = g_strdup_printf ("%sctl", audiodev); sunaudiosrc->mixer = gst_sunaudiomixer_ctrl_new (device); g_free (device); } } return TRUE; } static gboolean gst_sunaudiosrc_close (GstAudioSrc * asrc) { GstSunAudioSrc *sunaudiosrc = GST_SUNAUDIO_SRC (asrc); close (sunaudiosrc->fd); sunaudiosrc->fd = -1; if (sunaudiosrc->mixer) { gst_sunaudiomixer_ctrl_free (sunaudiosrc->mixer); sunaudiosrc->mixer = NULL; } return TRUE; } static gboolean gst_sunaudiosrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec) { GstSunAudioSrc *sunaudiosrc = GST_SUNAUDIO_SRC (asrc); audio_info_t ainfo; int ret; GstSunAudioMixerCtrl *mixer; struct audio_info audioinfo; ret = ioctl (sunaudiosrc->fd, AUDIO_GETINFO, &ainfo); if (ret == -1) { GST_ELEMENT_ERROR (sunaudiosrc, RESOURCE, SETTINGS, (NULL), ("%s", strerror (errno))); return FALSE; } if (spec->width != 16) return FALSE; AUDIO_INITINFO (&ainfo); ainfo.record.sample_rate = spec->rate; ainfo.record.precision = spec->width; ainfo.record.channels = spec->channels; ainfo.record.encoding = AUDIO_ENCODING_LINEAR; ainfo.record.buffer_size = spec->buffer_time; mixer = sunaudiosrc->mixer; if (ioctl (mixer->mixer_fd, AUDIO_GETINFO, &audioinfo) < 0) { g_warning ("Error getting audio device volume"); } ainfo.record.port = audioinfo.record.port; ainfo.record.gain = audioinfo.record.gain; ainfo.record.balance = audioinfo.record.balance; spec->segsize = 128; spec->segtotal = spec->buffer_time / 128; spec->silence_sample[0] = 0; spec->silence_sample[1] = 0; spec->silence_sample[2] = 0; spec->silence_sample[3] = 0; ret = ioctl (sunaudiosrc->fd, AUDIO_SETINFO, &ainfo); if (ret == -1) { GST_ELEMENT_ERROR (sunaudiosrc, RESOURCE, SETTINGS, (NULL), ("%s", strerror (errno))); return FALSE; } ioctl (sunaudiosrc->fd, I_FLUSH, FLUSHR); return TRUE; } static gboolean gst_sunaudiosrc_unprepare (GstAudioSrc * asrc) { return TRUE; } static guint gst_sunaudiosrc_read (GstAudioSrc * asrc, gpointer data, guint length) { return read (GST_SUNAUDIO_SRC (asrc)->fd, data, length); } static guint gst_sunaudiosrc_delay (GstAudioSrc * asrc) { return 0; } static void gst_sunaudiosrc_reset (GstAudioSrc * asrc) { /* Get current values */ GstSunAudioSrc *sunaudiosrc = GST_SUNAUDIO_SRC (asrc); audio_info_t ainfo; int ret; ret = ioctl (sunaudiosrc->fd, AUDIO_GETINFO, &ainfo); if (ret == -1) { /* * Should never happen, but if we couldn't getinfo, then no point * trying to setinfo */ GST_ELEMENT_ERROR (sunaudiosrc, RESOURCE, SETTINGS, (NULL), ("%s", strerror (errno))); return; } /* * Pause the audio - so audio stops playing immediately rather than * waiting for the ringbuffer to empty. */ ainfo.record.pause = !NULL; ret = ioctl (sunaudiosrc->fd, AUDIO_SETINFO, &ainfo); if (ret == -1) { GST_ELEMENT_ERROR (sunaudiosrc, RESOURCE, SETTINGS, (NULL), ("%s", strerror (errno))); } /* Flush the audio */ ret = ioctl (sunaudiosrc->fd, I_FLUSH, FLUSHR); if (ret == -1) { GST_ELEMENT_ERROR (sunaudiosrc, RESOURCE, SETTINGS, (NULL), ("%s", strerror (errno))); } /* unpause the audio */ ainfo.record.pause = NULL; ret = ioctl (sunaudiosrc->fd, AUDIO_SETINFO, &ainfo); if (ret == -1) { GST_ELEMENT_ERROR (sunaudiosrc, RESOURCE, SETTINGS, (NULL), ("%s", strerror (errno))); } }