summaryrefslogtreecommitdiff
path: root/sys/oss4/oss4-audio.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/oss4/oss4-audio.c')
-rw-r--r--sys/oss4/oss4-audio.c714
1 files changed, 714 insertions, 0 deletions
diff --git a/sys/oss4/oss4-audio.c b/sys/oss4/oss4-audio.c
new file mode 100644
index 0000000..09f8495
--- /dev/null
+++ b/sys/oss4/oss4-audio.c
@@ -0,0 +1,714 @@
+/* GStreamer OSS4 audio plugin
+ * Copyright (C) 2007-2008 Tim-Philipp Müller <tim centricular net>
+ *
+ * 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 <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "gst/gst-i18n-plugin.h"
+#include <gst/audio/multichannel.h>
+
+#include "oss4-audio.h"
+#include "oss4-mixer.h"
+#include "oss4-property-probe.h"
+#include "oss4-sink.h"
+#include "oss4-source.h"
+#include "oss4-soundcard.h"
+
+GST_DEBUG_CATEGORY (oss4mixer_debug);
+GST_DEBUG_CATEGORY (oss4sink_debug);
+GST_DEBUG_CATEGORY (oss4src_debug);
+GST_DEBUG_CATEGORY (oss4_debug);
+
+#define GST_CAT_DEFAULT oss4_debug
+
+typedef struct
+{
+ const GstBufferFormat gst_fmt;
+ const gint oss_fmt;
+ const gchar name[16];
+ const gint depth;
+ const gint width;
+ const gint endianness;
+ const gboolean signedness;
+} GstOss4AudioFormat;
+
+/* *INDENT-OFF* */
+static const GstOss4AudioFormat fmt_map[] = {
+ /* note: keep sorted by preference, prefered formats first */
+ {
+ GST_MU_LAW, AFMT_MU_LAW, "audio/x-mulaw", 0, 0, 0, FALSE}, {
+ GST_A_LAW, AFMT_A_LAW, "audio/x-alaw", 0, 0, 0, FALSE}, {
+ GST_S32_LE, AFMT_S32_LE, "audio/x-raw-int", 32, 32, G_LITTLE_ENDIAN, TRUE}, {
+ GST_S32_BE, AFMT_S32_BE, "audio/x-raw-int", 32, 32, G_BIG_ENDIAN, TRUE}, {
+ GST_S24_LE, AFMT_S24_LE, "audio/x-raw-int", 24, 32, G_LITTLE_ENDIAN, TRUE}, {
+ GST_S24_BE, AFMT_S24_BE, "audio/x-raw-int", 24, 32, G_BIG_ENDIAN, TRUE}, {
+ GST_S24_3LE, AFMT_S24_PACKED, "audio/x-raw-int", 24, 24, G_LITTLE_ENDIAN,
+ TRUE}, {
+ GST_S16_LE, AFMT_S16_LE, "audio/x-raw-int", 16, 16, G_LITTLE_ENDIAN, TRUE}, {
+ GST_S16_BE, AFMT_S16_BE, "audio/x-raw-int", 16, 16, G_BIG_ENDIAN, TRUE}, {
+ GST_U16_LE, AFMT_U16_LE, "audio/x-raw-int", 16, 16, G_LITTLE_ENDIAN, FALSE}, {
+ GST_U16_BE, AFMT_U16_BE, "audio/x-raw-int", 16, 16, G_BIG_ENDIAN, FALSE}, {
+ GST_S8, AFMT_S8, "audio/x-raw-int", 8, 8, 0, TRUE}, {
+ GST_U8, AFMT_U8, "audio/x-raw-int", 8, 8, 0, FALSE}
+};
+/* *INDENT-ON* */
+
+/* formats we assume the OSS4 layer can always handle and convert internally */
+#define CONVERTIBLE_FORMATS ( \
+ AFMT_MU_LAW | AFMT_A_LAW | \
+ AFMT_S32_LE | AFMT_S32_BE | \
+ AFMT_S24_LE | AFMT_S24_BE | \
+ AFMT_S24_PACKED | \
+ AFMT_S16_LE | AFMT_S16_BE | \
+ AFMT_U16_LE | AFMT_U16_BE | \
+ AFMT_S8 | AFMT_U8 )
+
+static void
+gst_oss4_append_format_to_caps (const GstOss4AudioFormat * fmt, GstCaps * caps)
+{
+ GstStructure *s;
+
+ s = gst_structure_empty_new (fmt->name);
+ if (fmt->width != 0 && fmt->depth != 0) {
+ gst_structure_set (s, "width", G_TYPE_INT, fmt->width, "depth", G_TYPE_INT,
+ fmt->depth, "signed", G_TYPE_BOOLEAN, fmt->signedness, NULL);
+ }
+ if (fmt->endianness != 0) {
+ gst_structure_set (s, "endianness", G_TYPE_INT, fmt->endianness, NULL);
+ }
+ gst_caps_append_structure (caps, s);
+}
+
+static gint
+gst_oss4_audio_get_oss_format (GstBufferFormat fmt)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (fmt_map); ++i) {
+ if (fmt_map[i].gst_fmt == fmt)
+ return fmt_map[i].oss_fmt;
+ }
+ return 0;
+}
+
+/* These are pretty random */
+#define GST_OSS4_MIN_SAMPLE_RATE 1
+#define GST_OSS4_MAX_SAMPLE_RATE 192000
+
+static gboolean
+gst_oss4_audio_detect_rates (GstObject * obj, oss_audioinfo * ai,
+ GstCaps * caps)
+{
+ GValue val = { 0, };
+ int minrate, maxrate, i;
+
+ minrate = ai->min_rate;
+ maxrate = ai->max_rate;
+
+ /* sanity check */
+ if (minrate > maxrate) {
+ GST_WARNING_OBJECT (obj, "min_rate %d > max_rate %d (buggy driver?)",
+ minrate, maxrate);
+ maxrate = ai->min_rate; /* swap */
+ minrate = ai->max_rate;
+ }
+
+ /* limit to something sensible */
+ if (minrate < GST_OSS4_MIN_SAMPLE_RATE)
+ minrate = GST_OSS4_MIN_SAMPLE_RATE;
+ if (maxrate > GST_OSS4_MAX_SAMPLE_RATE)
+ maxrate = GST_OSS4_MAX_SAMPLE_RATE;
+
+ if (maxrate < GST_OSS4_MIN_SAMPLE_RATE) {
+ GST_WARNING_OBJECT (obj, "max_rate < %d, which makes no sense",
+ GST_OSS4_MIN_SAMPLE_RATE);
+ return FALSE;
+ }
+
+ GST_LOG_OBJECT (obj, "min_rate %d, max_rate %d (originally: %d, %d)",
+ minrate, maxrate, ai->min_rate, ai->max_rate);
+
+ if ((ai->caps & PCM_CAP_FREERATE)) {
+ GST_LOG_OBJECT (obj, "device supports any sample rate between min and max");
+ if (minrate == maxrate) {
+ g_value_init (&val, G_TYPE_INT);
+ g_value_set_int (&val, maxrate);
+ } else {
+ g_value_init (&val, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range (&val, minrate, maxrate);
+ }
+ } else {
+ GST_LOG_OBJECT (obj, "%d sample rates:", ai->nrates);
+ g_value_init (&val, GST_TYPE_LIST);
+ for (i = 0; i < ai->nrates; ++i) {
+ GST_LOG_OBJECT (obj, " rate: %d", ai->rates[i]);
+
+ if (ai->rates[i] >= minrate && ai->rates[i] <= maxrate) {
+ GValue rate_val = { 0, };
+
+ g_value_init (&rate_val, G_TYPE_INT);
+ g_value_set_int (&rate_val, ai->rates[i]);
+ gst_value_list_append_value (&val, &rate_val);
+ g_value_unset (&rate_val);
+ }
+ }
+
+ if (gst_value_list_get_size (&val) == 0) {
+ g_value_unset (&val);
+ return FALSE;
+ }
+ }
+
+ for (i = 0; i < gst_caps_get_size (caps); ++i) {
+ GstStructure *s;
+
+ s = gst_caps_get_structure (caps, i);
+ gst_structure_set_value (s, "rate", &val);
+ }
+
+ g_value_unset (&val);
+
+ return TRUE;
+}
+
+static void
+gst_oss4_audio_add_channel_layout (GstObject * obj, guint64 layout,
+ guint num_channels, GstStructure * s)
+{
+ const GstAudioChannelPosition pos_map[16] = {
+ GST_AUDIO_CHANNEL_POSITION_NONE, /* 0 = dunno */
+ GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, /* 1 = left */
+ GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, /* 2 = right */
+ GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, /* 3 = center */
+ GST_AUDIO_CHANNEL_POSITION_LFE, /* 4 = lfe */
+ GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, /* 5 = left surround */
+ GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, /* 6 = right surround */
+ GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, /* 7 = left rear */
+ GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, /* 8 = right rear */
+ GST_AUDIO_CHANNEL_POSITION_NONE,
+ GST_AUDIO_CHANNEL_POSITION_NONE,
+ GST_AUDIO_CHANNEL_POSITION_NONE,
+ GST_AUDIO_CHANNEL_POSITION_NONE,
+ GST_AUDIO_CHANNEL_POSITION_NONE,
+ GST_AUDIO_CHANNEL_POSITION_NONE,
+ GST_AUDIO_CHANNEL_POSITION_NONE
+ };
+ GstAudioChannelPosition ch_layout[8] = { 0, };
+ guint speaker_pos; /* speaker position as defined by OSS */
+ guint i;
+
+ g_return_if_fail (num_channels <= G_N_ELEMENTS (ch_layout));
+
+ for (i = 0; i < num_channels; ++i) {
+ /* layout contains up to 16 speaker positions, with each taking up 4 bits */
+ speaker_pos = (guint) ((layout >> (i * 4)) & 0x0f);
+
+ /* if it's a channel position that's unknown to us, set all to NONE and
+ * bail out */
+ if (G_UNLIKELY (pos_map[speaker_pos] == GST_AUDIO_CHANNEL_POSITION_NONE))
+ goto no_layout;
+
+ ch_layout[i] = pos_map[speaker_pos];
+ }
+ gst_audio_set_channel_positions (s, ch_layout);
+ return;
+
+no_layout:
+ {
+ /* only warn if it's really unknown, position 0 is ok and represents NONE
+ * (in which case we also just set all others to NONE ignoring the other
+ * positions in the OSS-given layout, because that's what we currently
+ * require in GStreamer) */
+ if (speaker_pos != 0) {
+ GST_WARNING_OBJECT (obj, "unknown OSS channel position %x", ch_layout[i]);
+ }
+ for (i = 0; i < num_channels; ++i) {
+ ch_layout[i] = GST_AUDIO_CHANNEL_POSITION_NONE;
+ }
+ gst_audio_set_channel_positions (s, ch_layout);
+ return;
+ }
+}
+
+/* arbitrary max. limit */
+#define GST_OSS4_MIN_CHANNELS 1
+#define GST_OSS4_MAX_CHANNELS 4096
+
+/* takes ownership of the input caps */
+static GstCaps *
+gst_oss4_audio_detect_channels (GstObject * obj, int fd, oss_audioinfo * ai,
+ GstCaps * in_caps)
+{
+ const gchar *forced_layout;
+ GstStructure *s = NULL;
+ guint64 layout = 0;
+ GstCaps *chan_caps = NULL;
+ GstCaps *out_caps = NULL;
+ int minchans, maxchans;
+ int c, i, j;
+
+ /* GST_OSS4_CHANNEL_LAYOUT environment variable: may be used to force a
+ * particular channel layout (if it contains an odd number of channel
+ * positions it will also make us advertise a channel layout for that
+ * channel count, even if we'd usually skip it; this is especially useful
+ * for folks with 2.1 speakers, I guess) */
+ forced_layout = g_getenv ("GST_OSS4_CHANNEL_LAYOUT");
+
+ minchans = ai->min_channels;
+ maxchans = ai->max_channels;
+
+ /* sanity check */
+ if (minchans > maxchans) {
+ GST_WARNING_OBJECT (obj, "min_chans %d > max_chans %d (buggy driver?)",
+ minchans, maxchans);
+ maxchans = ai->min_channels; /* swap */
+ minchans = ai->max_channels;
+ }
+
+ /* limit to something sensible */
+ if (minchans < GST_OSS4_MIN_CHANNELS)
+ minchans = GST_OSS4_MIN_CHANNELS;
+ if (maxchans > GST_OSS4_MAX_CHANNELS)
+ maxchans = GST_OSS4_MAX_CHANNELS;
+
+ if (maxchans < GST_OSS4_MIN_CHANNELS) {
+ GST_WARNING_OBJECT (obj, "max_chans < %d, which makes no sense",
+ GST_OSS4_MIN_CHANNELS);
+ gst_caps_unref (in_caps);
+ return NULL;
+ }
+
+ GST_LOG_OBJECT (obj, "min_channels %d, max_channels %d (originally: %d, %d)",
+ minchans, maxchans, ai->min_channels, ai->max_channels);
+
+ chan_caps = gst_caps_new_empty ();
+
+ /* first do the simple cases: mono + stereo (channel layout implied) */
+ if (minchans == 1 && maxchans == 1)
+ s = gst_structure_new ("x", "channels", G_TYPE_INT, 1, NULL);
+ else if (minchans == 2 && maxchans >= 2)
+ s = gst_structure_new ("x", "channels", G_TYPE_INT, 2, NULL);
+ else if (minchans == 1 && maxchans >= 2)
+ s = gst_structure_new ("x", "channels", GST_TYPE_INT_RANGE, 1, 2, NULL);
+ gst_caps_append_structure (chan_caps, s);
+ s = NULL;
+
+ /* TODO: we assume all drivers use a left/right layout for stereo here */
+ if (maxchans <= 2)
+ goto done;
+
+ if (ioctl (fd, SNDCTL_DSP_GET_CHNORDER, &layout) == -1) {
+ GST_WARNING_OBJECT (obj, "couldn't query channel layout, assuming default");
+ layout = CHNORDER_NORMAL;
+ }
+ GST_DEBUG_OBJECT (obj, "channel layout: %08" G_GINT64_MODIFIER "x", layout);
+
+ /* e.g. forced 2.1 layout would be GST_OSS4_CHANNEL_LAYOUT=421 */
+ if (forced_layout != NULL && *forced_layout != '\0') {
+ guint layout_len;
+
+ layout_len = strlen (forced_layout);
+ if (layout_len >= minchans && layout_len <= maxchans) {
+ layout = g_ascii_strtoull (forced_layout, NULL, 16);
+ maxchans = layout_len;
+ GST_DEBUG_OBJECT (obj, "forced channel layout: %08" G_GINT64_MODIFIER "x"
+ " ('%s'), maxchans now %d", layout, forced_layout, maxchans);
+ } else {
+ GST_WARNING_OBJECT (obj, "ignoring forced channel layout: layout has %d "
+ "channel positions but maxchans is %d", layout_len, maxchans);
+ }
+ }
+
+ /* need to advertise channel layouts for anything >2 and <=8 channels */
+ for (c = MAX (3, minchans); c <= MIN (maxchans, 8); c++) {
+ /* "The min_channels and max_channels fields define the limits for the
+ * number of channels. However some devices don't support all channels
+ * within this range. It's possible that the odd values (3, 5, 7, 9, etc).
+ * are not supported. There is currently no way to check for this other
+ * than checking if SNDCTL_DSP_CHANNELS accepts the requested value.
+ * Another approach is trying to avoid using odd number of channels."
+ *
+ * So, we don't know for sure if these odd values are supported:
+ */
+ if ((c == 3 || c == 5 || c == 7) && (c != maxchans)) {
+ GST_LOG_OBJECT (obj, "not adding layout with %d channels", c);
+ continue;
+ }
+
+ s = gst_structure_new ("x", "channels", G_TYPE_INT, c, NULL);
+ gst_oss4_audio_add_channel_layout (obj, layout, c, s);
+ GST_LOG_OBJECT (obj, "c=%u, appending struct %" GST_PTR_FORMAT, c, s);
+ gst_caps_append_structure (chan_caps, s);
+ s = NULL;
+ }
+
+ if (maxchans <= 8)
+ goto done;
+
+ /* for everything >8 channels, CHANNEL_POSITION_NONE is implied. */
+ if (minchans == maxchans || maxchans == 9) {
+ s = gst_structure_new ("x", "channels", G_TYPE_INT, maxchans, NULL);
+ } else {
+ s = gst_structure_new ("x", "channels", GST_TYPE_INT_RANGE,
+ MAX (9, minchans), maxchans, NULL);
+ }
+ gst_caps_append_structure (chan_caps, s);
+ s = NULL;
+
+done:
+
+ GST_LOG_OBJECT (obj, "channel structures: %" GST_PTR_FORMAT, chan_caps);
+
+ out_caps = gst_caps_new_empty ();
+
+ /* combine each structure in the input caps with each channel caps struct */
+ for (i = 0; i < gst_caps_get_size (in_caps); ++i) {
+ const GstStructure *in_s;
+
+ in_s = gst_caps_get_structure (in_caps, i);
+
+ for (j = 0; j < gst_caps_get_size (chan_caps); ++j) {
+ const GstStructure *chan_s;
+ const GValue *val;
+
+ s = gst_structure_copy (in_s);
+ chan_s = gst_caps_get_structure (chan_caps, j);
+ if ((val = gst_structure_get_value (chan_s, "channels")))
+ gst_structure_set_value (s, "channels", val);
+ if ((val = gst_structure_get_value (chan_s, "channel-positions")))
+ gst_structure_set_value (s, "channel-positions", val);
+
+ gst_caps_append_structure (out_caps, s);
+ s = NULL;
+ }
+ }
+
+ gst_caps_unref (in_caps);
+ gst_caps_unref (chan_caps);
+ return out_caps;
+}
+
+GstCaps *
+gst_oss4_audio_probe_caps (GstObject * obj, int fd)
+{
+ oss_audioinfo ai = { 0, };
+ gboolean output;
+ GstCaps *caps;
+ int nonnative_formats = 0;
+ int formats, i;
+
+ output = GST_IS_OSS4_SINK (obj);
+
+ /* -1 = get info for currently open device (fd). This will fail with
+ * OSS build <= 1013 because of a bug in OSS */
+ ai.dev = -1;
+ if (ioctl (fd, SNDCTL_ENGINEINFO, &ai) == -1)
+ goto engineinfo_failed;
+
+ formats = (output) ? ai.oformats : ai.iformats;
+
+ GST_LOG_OBJECT (obj, "%s formats : 0x%08x", (output) ? "out" : "in", formats);
+
+ caps = gst_caps_new_empty ();
+
+ /* first list all the formats natively supported */
+ for (i = 0; i < G_N_ELEMENTS (fmt_map); ++i) {
+ if ((formats & fmt_map[i].oss_fmt)) {
+ gst_oss4_append_format_to_caps (&fmt_map[i], caps);
+ } else if ((fmt_map[i].oss_fmt & CONVERTIBLE_FORMATS)) {
+ nonnative_formats |= fmt_map[i].oss_fmt;
+ }
+ }
+
+ GST_LOG_OBJECT (obj, "adding non-native %s formats : 0x%08x",
+ (output) ? "out" : "in", nonnative_formats);
+
+ /* now append non-native formats for which conversion would be needed */
+ for (i = 0; i < G_N_ELEMENTS (fmt_map); ++i) {
+ if ((nonnative_formats & fmt_map[i].oss_fmt)) {
+ gst_oss4_append_format_to_caps (&fmt_map[i], caps);
+ }
+ }
+
+ gst_caps_do_simplify (caps);
+ GST_LOG_OBJECT (obj, "formats: %" GST_PTR_FORMAT, caps);
+
+ if (!gst_oss4_audio_detect_rates (obj, &ai, caps))
+ goto detect_rates_failed;
+
+ caps = gst_oss4_audio_detect_channels (obj, fd, &ai, caps);
+ if (caps == NULL)
+ goto detect_channels_failed;
+
+ GST_LOG_OBJECT (obj, "probed caps: %" GST_PTR_FORMAT, caps);
+
+ return caps;
+
+/* ERRORS */
+engineinfo_failed:
+ {
+ GST_WARNING ("ENGINEINFO supported formats probe failed: %s",
+ g_strerror (errno));
+ return NULL;
+ }
+detect_rates_failed:
+ {
+ GST_WARNING_OBJECT (obj, "failed to detect supported sample rates");
+ gst_caps_unref (caps);
+ return NULL;
+ }
+detect_channels_failed:
+ {
+ GST_WARNING_OBJECT (obj, "failed to detect supported channels");
+ gst_caps_unref (caps);
+ return NULL;
+ }
+}
+
+GstCaps *
+gst_oss4_audio_get_template_caps (void)
+{
+ GstCaps *caps;
+ gint i;
+
+ caps = gst_caps_new_empty ();
+
+ for (i = 0; i < G_N_ELEMENTS (fmt_map); ++i) {
+ gst_oss4_append_format_to_caps (&fmt_map[i], caps);
+ }
+
+ gst_caps_do_simplify (caps);
+
+ for (i = 0; i < gst_caps_get_size (caps); ++i) {
+ GstStructure *s;
+
+ s = gst_caps_get_structure (caps, i);
+ gst_structure_set (s, "rate", GST_TYPE_INT_RANGE, GST_OSS4_MIN_SAMPLE_RATE,
+ GST_OSS4_MAX_SAMPLE_RATE, "channels", GST_TYPE_INT_RANGE,
+ GST_OSS4_MIN_CHANNELS, GST_OSS4_MAX_CHANNELS, NULL);
+ }
+
+ return caps;
+}
+
+/* called by gst_oss4_sink_prepare() and gst_oss4_source_prepare() */
+gboolean
+gst_oss4_audio_set_format (GstObject * obj, int fd, GstRingBufferSpec * spec)
+{
+ struct audio_buf_info info = { 0, };
+ int fmt, chans, rate;
+
+ fmt = gst_oss4_audio_get_oss_format (spec->format);
+ if (fmt == 0)
+ goto wrong_format;
+
+ if (spec->type == GST_BUFTYPE_LINEAR && spec->width != 32 &&
+ spec->width != 24 && spec->width != 16 && spec->width != 8) {
+ goto dodgy_width;
+ }
+
+ /* format */
+ GST_LOG_OBJECT (obj, "setting format: %d", fmt);
+ if (ioctl (fd, SNDCTL_DSP_SETFMT, &fmt) == -1)
+ goto set_format_failed;
+
+ /* channels */
+ GST_LOG_OBJECT (obj, "setting channels: %d", spec->channels);
+ chans = spec->channels;
+ if (ioctl (fd, SNDCTL_DSP_CHANNELS, &chans) == -1)
+ goto set_channels_failed;
+
+ /* rate */
+ GST_LOG_OBJECT (obj, "setting rate: %d", spec->rate);
+ rate = spec->rate;
+ if (ioctl (fd, SNDCTL_DSP_SPEED, &rate) == -1)
+ goto set_rate_failed;
+
+ GST_DEBUG_OBJECT (obj, "effective format : %d", fmt);
+ GST_DEBUG_OBJECT (obj, "effective channels : %d", chans);
+ GST_DEBUG_OBJECT (obj, "effective rate : %d", rate);
+
+ /* make sure format, channels, and rate are the ones we requested */
+ if (fmt != gst_oss4_audio_get_oss_format (spec->format) ||
+ chans != spec->channels || rate != spec->rate) {
+ /* This shouldn't happen, but hey */
+ goto format_not_what_was_requested;
+ }
+
+ if (GST_IS_OSS4_SOURCE (obj)) {
+ if (ioctl (fd, SNDCTL_DSP_GETISPACE, &info) == -1)
+ goto get_ispace_failed;
+ } else {
+ if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
+ goto get_ospace_failed;
+ }
+
+ spec->segsize = info.fragsize;
+
+ /* we add some extra fragments -- this helps us account for delays due to
+ * conversion buffer, streams queueing, etc. It is important that these
+ * be taken into account because otherwise the delay counter can wind up
+ * being too large, and the buffer will wrap. */
+ spec->segtotal = info.fragstotal + 4;
+
+ spec->bytes_per_sample = (spec->width / 8) * spec->channels;
+
+ GST_DEBUG_OBJECT (obj, "got segsize: %d, segtotal: %d, value: %08x",
+ spec->segsize, spec->segtotal, info.fragsize);
+
+ return TRUE;
+
+/* ERRORS */
+wrong_format:
+ {
+ GST_ELEMENT_ERROR (obj, RESOURCE, SETTINGS, (NULL),
+ ("Unable to get format %d", spec->format));
+ return FALSE;
+ }
+dodgy_width:
+ {
+ GST_ELEMENT_ERROR (obj, RESOURCE, SETTINGS, (NULL),
+ ("unexpected width %d", spec->width));
+ return FALSE;
+ }
+set_format_failed:
+ {
+ GST_ELEMENT_ERROR (obj, RESOURCE, SETTINGS, (NULL),
+ ("DSP_SETFMT(%d) failed: %s", fmt, g_strerror (errno)));
+ return FALSE;
+ }
+set_channels_failed:
+ {
+ GST_ELEMENT_ERROR (obj, RESOURCE, SETTINGS, (NULL),
+ ("DSP_CHANNELS(%d) failed: %s", chans, g_strerror (errno)));
+ return FALSE;
+ }
+set_rate_failed:
+ {
+ GST_ELEMENT_ERROR (obj, RESOURCE, SETTINGS, (NULL),
+ ("DSP_SPEED(%d) failed: %s", rate, g_strerror (errno)));
+ return FALSE;
+ }
+get_ospace_failed:
+ {
+ GST_ELEMENT_ERROR (obj, RESOURCE, SETTINGS, (NULL),
+ ("DSP_GETOSPACE failed: %s", g_strerror (errno)));
+ return FALSE;
+ }
+get_ispace_failed:
+ {
+ GST_ELEMENT_ERROR (obj, RESOURCE, SETTINGS, (NULL),
+ ("DSP_GETISPACE failed: %s", g_strerror (errno)));
+ return FALSE;
+ }
+format_not_what_was_requested:
+ {
+ GST_ELEMENT_ERROR (obj, RESOURCE, SETTINGS, (NULL),
+ ("Format actually configured wasn't the one we requested. This is "
+ "probably either a bug in the driver or in the format probing code."));
+ return FALSE;
+ }
+}
+
+int
+gst_oss4_audio_get_version (GstObject * obj, int fd)
+{
+ gint ver = 0;
+
+ /* we use the old ioctl here on purpose instead of SNDCTL_SYSINFO */
+ if (ioctl (fd, OSS_GETVERSION, &ver) < 0) {
+ GST_LOG_OBJECT (obj, "OSS_GETVERSION failed: %s", g_strerror (errno));
+ return -1;
+ }
+ GST_LOG_OBJECT (obj, "OSS version: 0x%08x", ver);
+ return ver;
+}
+
+gboolean
+gst_oss4_audio_check_version (GstObject * obj, int fd)
+{
+ return (gst_oss4_audio_get_version (obj, fd) >= GST_MIN_OSS4_VERSION);
+}
+
+gchar *
+gst_oss4_audio_find_device (GstObject * oss)
+{
+ GValueArray *arr;
+ gchar *ret = NULL;
+
+ arr = gst_property_probe_probe_and_get_values_name (GST_PROPERTY_PROBE (oss),
+ "device");
+
+ if (arr != NULL) {
+ if (arr->n_values > 0) {
+ const GValue *val;
+
+ val = g_value_array_get_nth (arr, 0);
+ ret = g_value_dup_string (val);
+ }
+ g_value_array_free (arr);
+ }
+
+ GST_LOG_OBJECT (oss, "first device found: %s", GST_STR_NULL (ret));
+
+ return ret;
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ gint rank;
+
+ GST_DEBUG_CATEGORY_INIT (oss4sink_debug, "oss4sink", 0, "OSS4 audio sink");
+ GST_DEBUG_CATEGORY_INIT (oss4src_debug, "oss4src", 0, "OSS4 audio src");
+ GST_DEBUG_CATEGORY_INIT (oss4mixer_debug, "oss4mixer", 0, "OSS4 mixer");
+ GST_DEBUG_CATEGORY_INIT (oss4_debug, "oss4", 0, "OSS4 plugin");
+
+#ifdef ENABLE_NLS
+ GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
+ LOCALEDIR);
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+
+ /* we want a higher rank than the legacy OSS elements have now */
+ rank = GST_RANK_SECONDARY + 1;
+
+ if (!gst_element_register (plugin, "oss4sink", rank, GST_TYPE_OSS4_SINK) ||
+ !gst_element_register (plugin, "oss4src", rank, GST_TYPE_OSS4_SOURCE) ||
+ !gst_element_register (plugin, "oss4mixer", rank, GST_TYPE_OSS4_MIXER)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "oss4",
+ "Open Sound System (OSS) version 4 support for GStreamer",
+ plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)