mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-19 06:46:38 +00:00
d575347603
When iterating the formats table, we can just pass the whole entry to our helper function, which avoids iterating the table again to find the entry structure from the passed format id.
712 lines
22 KiB
C
712 lines
22 KiB
C
/* 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, "endianness", G_TYPE_INT,
|
|
fmt->endianness, "signed", G_TYPE_BOOLEAN, fmt->signedness, 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)
|