mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-23 18:21:04 +00:00
parent
bb93d06ca0
commit
e800ba112b
27 changed files with 6 additions and 8424 deletions
|
@ -47,8 +47,10 @@ CRUFT_FILES = \
|
|||
$(top_builddir)/gst/shapewipe/.libs/*.{so,dll,DLL,dylib} \
|
||||
$(top_builddir)/ext/ivorbis/.libs/*.{so,dll,DLL,dylib} \
|
||||
$(top_builddir)/gst/imagefreeze/.libs/*.{so,dll,DLL,dylib} \
|
||||
$(top_builddir)/sys/oss4/.libs/*.{so,dll,DLL,dylib} \
|
||||
$(top_builddir)/tests/check/elements/capssetter \
|
||||
$(top_builddir)/tests/check/elements/imagefreeze
|
||||
$(top_builddir)/tests/check/elements/imagefreeze \
|
||||
$(top_builddir)/tests/icles/test-oss4
|
||||
|
||||
CRUFT_DIRS = \
|
||||
$(top_srcdir)/gst/aacparse \
|
||||
|
|
11
configure.ac
11
configure.ac
|
@ -1400,15 +1400,6 @@ AG_GST_CHECK_FEATURE(DVB, [DVB Source], dvb, [
|
|||
AC_CHECK_HEADER(linux/dvb/frontend.h, [HAVE_DVB="yes"], [HAVE_DVB="no"])
|
||||
])
|
||||
|
||||
dnl *** oss4 ***
|
||||
translit(dnm, m, l) AM_CONDITIONAL(USE_OSS4, true)
|
||||
AG_GST_CHECK_FEATURE(OSS4, [Open Sound System 4], oss4, [
|
||||
AC_MSG_CHECKING([Checking if we can build the OSS4 elements])
|
||||
AC_CHECK_HEADERS([fcntl.h sys/ioctl.h sys/stat.h sys/types.h],
|
||||
[test -z "$OSS4_MISSING_HEADER" && HAVE_OSS4="yes"],
|
||||
[OSS4_MISSING_HEADER="yes";HAVE_OSS4="no"])
|
||||
])
|
||||
|
||||
dnl *** wininet ***
|
||||
translit(dnm, m, l) AM_CONDITIONAL(USE_WININET, true)
|
||||
AG_GST_CHECK_FEATURE(WININET, [Windows internet library], wininet, [
|
||||
|
@ -1540,7 +1531,6 @@ AM_CONDITIONAL(USE_SWFDEC, false)
|
|||
AM_CONDITIONAL(USE_THEORADEC, false)
|
||||
AM_CONDITIONAL(USE_XVID, false)
|
||||
AM_CONDITIONAL(USE_DVB, false)
|
||||
AM_CONDITIONAL(USE_OSS4, false)
|
||||
AM_CONDITIONAL(USE_WININET, false)
|
||||
AM_CONDITIONAL(USE_ACM, false)
|
||||
AM_CONDITIONAL(USE_VDPAU, false)
|
||||
|
@ -1679,7 +1669,6 @@ sys/dshowsrcwrapper/Makefile
|
|||
sys/dshowvideosink/Makefile
|
||||
sys/dvb/Makefile
|
||||
sys/fbdev/Makefile
|
||||
sys/oss4/Makefile
|
||||
sys/osxvideo/Makefile
|
||||
sys/qtwrapper/Makefile
|
||||
sys/vcd/Makefile
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
<plugin>
|
||||
<name>oss4</name>
|
||||
<description>Open Sound System (OSS) version 4 support for GStreamer</description>
|
||||
<filename>../../sys/oss4/.libs/libgstoss4audio.so</filename>
|
||||
<basename>libgstoss4audio.so</basename>
|
||||
<version>0.10.18.1</version>
|
||||
<license>LGPL</license>
|
||||
<source>gst-plugins-bad</source>
|
||||
<package>GStreamer Bad Plug-ins git</package>
|
||||
<origin>Unknown package origin</origin>
|
||||
<elements>
|
||||
<element>
|
||||
<name>oss4mixer</name>
|
||||
<longname>OSS v4 Audio Mixer</longname>
|
||||
<class>Generic/Audio</class>
|
||||
<description>Control sound input and output levels with OSS4</description>
|
||||
<author>Tim-Philipp Müller <tim centricular net></author>
|
||||
<pads>
|
||||
|
||||
</pads>
|
||||
</element>
|
||||
<element>
|
||||
<name>oss4sink</name>
|
||||
<longname>OSS v4 Audio Sink</longname>
|
||||
<class>Sink/Audio</class>
|
||||
<description>Output to a sound card via OSS version 4</description>
|
||||
<author>Tim-Philipp Müller <tim centricular net></author>
|
||||
<pads>
|
||||
<caps>
|
||||
<name>sink</name>
|
||||
<direction>sink</direction>
|
||||
<presence>always</presence>
|
||||
<details>audio/x-alaw, rate=(int)[ 1, 192000 ], channels=(int)[ 1, 4096 ]; audio/x-mulaw, rate=(int)[ 1, 192000 ], channels=(int)[ 1, 4096 ]; audio/x-raw-int, width=(int)32, depth=(int)32, endianness=(int)1234, signed=(boolean)true, rate=(int)[ 1, 192000 ], channels=(int)[ 1, 4096 ]; audio/x-raw-int, width=(int)32, depth=(int){ 32, 24 }, endianness=(int)4321, signed=(boolean)true, rate=(int)[ 1, 192000 ], channels=(int)[ 1, 4096 ]; audio/x-raw-int, width=(int){ 32, 24 }, depth=(int)24, endianness=(int)1234, signed=(boolean)true, rate=(int)[ 1, 192000 ], channels=(int)[ 1, 4096 ]; audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int){ 1234, 4321 }, signed=(boolean){ false, true }, rate=(int)[ 1, 192000 ], channels=(int)[ 1, 4096 ]; audio/x-raw-int, width=(int)8, depth=(int)8, endianness=(int)0, signed=(boolean){ true, false }, rate=(int)[ 1, 192000 ], channels=(int)[ 1, 4096 ]</details>
|
||||
</caps>
|
||||
</pads>
|
||||
</element>
|
||||
<element>
|
||||
<name>oss4src</name>
|
||||
<longname>OSS v4 Audio Source</longname>
|
||||
<class>Source/Audio</class>
|
||||
<description>Capture from a sound card via OSS version 4</description>
|
||||
<author>Tim-Philipp Müller <tim centricular net></author>
|
||||
<pads>
|
||||
<caps>
|
||||
<name>src</name>
|
||||
<direction>source</direction>
|
||||
<presence>always</presence>
|
||||
<details>audio/x-alaw, rate=(int)[ 1, 192000 ], channels=(int)[ 1, 4096 ]; audio/x-mulaw, rate=(int)[ 1, 192000 ], channels=(int)[ 1, 4096 ]; audio/x-raw-int, width=(int)32, depth=(int)32, endianness=(int)1234, signed=(boolean)true, rate=(int)[ 1, 192000 ], channels=(int)[ 1, 4096 ]; audio/x-raw-int, width=(int)32, depth=(int){ 32, 24 }, endianness=(int)4321, signed=(boolean)true, rate=(int)[ 1, 192000 ], channels=(int)[ 1, 4096 ]; audio/x-raw-int, width=(int){ 32, 24 }, depth=(int)24, endianness=(int)1234, signed=(boolean)true, rate=(int)[ 1, 192000 ], channels=(int)[ 1, 4096 ]; audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int){ 1234, 4321 }, signed=(boolean){ false, true }, rate=(int)[ 1, 192000 ], channels=(int)[ 1, 4096 ]; audio/x-raw-int, width=(int)8, depth=(int)8, endianness=(int)0, signed=(boolean){ true, false }, rate=(int)[ 1, 192000 ], channels=(int)[ 1, 4096 ]</details>
|
||||
</caps>
|
||||
</pads>
|
||||
</element>
|
||||
</elements>
|
||||
</plugin>
|
|
@ -97,7 +97,6 @@ rm -rf $RPM_BUILD_ROOT
|
|||
%{_libdir}/gstreamer-%{majorminor}/libgstrawparse.so
|
||||
%{_libdir}/gstreamer-%{majorminor}/libgstselector.so
|
||||
%{_libdir}/gstreamer-%{majorminor}/libgstsubenc.so
|
||||
%{_libdir}/gstreamer-%{majorminor}/libgstoss4audio.so
|
||||
%{_libdir}/gstreamer-%{majorminor}/libresindvd.so
|
||||
%{_libdir}/gstreamer-%{majorminor}/libgstaiff.so
|
||||
%{_libdir}/gstreamer-%{majorminor}/libgstdccp.so
|
||||
|
|
|
@ -4,6 +4,3 @@ ext/sndfile/gstsfsrc.c
|
|||
gst/aiff/aiffparse.c
|
||||
gst/nuvdemux/gstnuvdemux.c
|
||||
sys/dvb/gstdvbsrc.c
|
||||
sys/oss4/oss4-mixer.c
|
||||
sys/oss4/oss4-sink.c
|
||||
sys/oss4/oss4-source.c
|
||||
|
|
|
@ -46,12 +46,6 @@ else
|
|||
DVB_DIR=
|
||||
endif
|
||||
|
||||
if USE_OSS4
|
||||
OSS4_DIR=oss4
|
||||
else
|
||||
OSS4_DIR=
|
||||
endif
|
||||
|
||||
if USE_OSX_VIDEO
|
||||
OSX_VIDEO_DIR=osxvideo
|
||||
else
|
||||
|
@ -82,9 +76,9 @@ else
|
|||
VDPAU_DIR=
|
||||
endif
|
||||
|
||||
SUBDIRS = $(ACM_DIR) $(DIRECTDRAW_DIR) $(DIRECTSOUND_DIR) $(DVB_DIR) $(FBDEV_DIR) $(OSS4_DIR) $(OSX_VIDEO_DIR) $(QT_DIR) $(VCD_DIR) $(VDPAU_DIR) $(WININET_DIR)
|
||||
SUBDIRS = $(ACM_DIR) $(DIRECTDRAW_DIR) $(DIRECTSOUND_DIR) $(DVB_DIR) $(FBDEV_DIR) $(OSX_VIDEO_DIR) $(QT_DIR) $(VCD_DIR) $(VDPAU_DIR) $(WININET_DIR)
|
||||
|
||||
DIST_SUBDIRS = acmenc acmmp3dec directdraw directsound dvb fbdev dshowdecwrapper dshowsrcwrapper dshowvideosink \
|
||||
oss4 osxvideo qtwrapper vcd vdpau wasapi wininet winks winscreencap
|
||||
osxvideo qtwrapper vcd vdpau wasapi wininet winks winscreencap
|
||||
|
||||
include $(top_srcdir)/common/parallel-subdirs.mak
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
plugin_LTLIBRARIES = libgstoss4audio.la
|
||||
|
||||
libgstoss4audio_la_SOURCES = \
|
||||
oss4-audio.c \
|
||||
oss4-mixer.c \
|
||||
oss4-mixer-enum.c \
|
||||
oss4-mixer-slider.c \
|
||||
oss4-mixer-switch.c \
|
||||
oss4-property-probe.c \
|
||||
oss4-sink.c \
|
||||
oss4-source.c
|
||||
|
||||
libgstoss4audio_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
|
||||
libgstoss4audio_la_LIBADD = \
|
||||
$(GST_PLUGINS_BASE_LIBS) \
|
||||
-lgstinterfaces-$(GST_MAJORMINOR) \
|
||||
-lgstaudio-$(GST_MAJORMINOR) \
|
||||
$(GST_LIBS)
|
||||
libgstoss4audio_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||
libgstoss4audio_la_LIBTOOLFLAGS = --tag=disable-static
|
||||
|
||||
noinst_HEADERS = \
|
||||
oss4-audio.h \
|
||||
oss4-mixer.h \
|
||||
oss4-mixer-enum.h \
|
||||
oss4-mixer-slider.h \
|
||||
oss4-mixer-switch.h \
|
||||
oss4-property-probe.h \
|
||||
oss4-sink.h \
|
||||
oss4-soundcard.h \
|
||||
oss4-source.h
|
||||
|
||||
|
|
@ -1,714 +0,0 @@
|
|||
/* 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)
|
|
@ -1,43 +0,0 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef GST_OSS4_AUDIO_H
|
||||
#define GST_OSS4_AUDIO_H_
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/gstringbuffer.h>
|
||||
|
||||
/* This is the minimum version we require */
|
||||
#define GST_MIN_OSS4_VERSION 0x040003
|
||||
|
||||
int gst_oss4_audio_get_version (GstObject * obj, int fd);
|
||||
|
||||
gboolean gst_oss4_audio_check_version (GstObject * obj, int fd);
|
||||
|
||||
GstCaps * gst_oss4_audio_probe_caps (GstObject * obj, int fd);
|
||||
|
||||
gboolean gst_oss4_audio_set_format (GstObject * obj, int fd, GstRingBufferSpec * spec);
|
||||
|
||||
GstCaps * gst_oss4_audio_get_template_caps (void);
|
||||
|
||||
gchar * gst_oss4_audio_find_device (GstObject * oss);
|
||||
|
||||
#endif /* GST_OSS4_AUDIO_H */
|
||||
|
||||
|
|
@ -1,269 +0,0 @@
|
|||
/* GStreamer OSS4 mixer enumeration control
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* An 'enum' in gnome-volume-control / GstMixer is represented by a
|
||||
* GstMixerOptions object
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gst/gst-i18n-plugin.h>
|
||||
|
||||
#define NO_LEGACY_MIXER
|
||||
#include "oss4-mixer.h"
|
||||
#include "oss4-mixer-enum.h"
|
||||
#include "oss4-soundcard.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (oss4mixer_debug);
|
||||
#define GST_CAT_DEFAULT oss4mixer_debug
|
||||
|
||||
static GList *gst_oss4_mixer_enum_get_values (GstMixerOptions * options);
|
||||
|
||||
/* GstMixerTrack is a plain GObject, so let's just use the GLib macro here */
|
||||
G_DEFINE_TYPE (GstOss4MixerEnum, gst_oss4_mixer_enum, GST_TYPE_MIXER_OPTIONS);
|
||||
|
||||
static void
|
||||
gst_oss4_mixer_enum_init (GstOss4MixerEnum * e)
|
||||
{
|
||||
e->need_update = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_mixer_enum_dispose (GObject * obj)
|
||||
{
|
||||
GstMixerOptions *options = GST_MIXER_OPTIONS (obj);
|
||||
|
||||
/* our list is a flat list with constant strings, but the GstMixerOptions
|
||||
* dispose will try to g_free the contained strings, so clean up the list
|
||||
* before chaining up to GstMixerOptions */
|
||||
g_list_free (options->values);
|
||||
options->values = NULL;
|
||||
|
||||
G_OBJECT_CLASS (gst_oss4_mixer_enum_parent_class)->dispose (obj);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_mixer_enum_class_init (GstOss4MixerEnumClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstMixerOptionsClass *mixeroptions_class = (GstMixerOptionsClass *) klass;
|
||||
|
||||
gobject_class->dispose = gst_oss4_mixer_enum_dispose;
|
||||
mixeroptions_class->get_values = gst_oss4_mixer_enum_get_values;
|
||||
}
|
||||
|
||||
static GList *
|
||||
gst_oss4_mixer_enum_get_values_locked (GstMixerOptions * options)
|
||||
{
|
||||
GstOss4MixerEnum *e = GST_OSS4_MIXER_ENUM_CAST (options);
|
||||
GList *oldlist, *list = NULL;
|
||||
int i;
|
||||
|
||||
/* if current list of values is empty, update/re-check in any case */
|
||||
if (!e->need_update && options->values != NULL)
|
||||
return options->values;
|
||||
|
||||
GST_LOG_OBJECT (e, "updating available values for %s", e->mc->mixext.extname);
|
||||
|
||||
for (i = 0; i < e->mc->mixext.maxvalue; ++i) {
|
||||
const gchar *s;
|
||||
|
||||
s = g_quark_to_string (e->mc->enum_vals[i]);
|
||||
if (MIXEXT_ENUM_IS_AVAILABLE (e->mc->mixext, i)) {
|
||||
GST_LOG_OBJECT (e, "option '%s' is available", s);
|
||||
list = g_list_prepend (list, (gpointer) s);
|
||||
} else {
|
||||
GST_LOG_OBJECT (e, "option '%s' is currently not available", s);
|
||||
}
|
||||
}
|
||||
|
||||
list = g_list_reverse (list);
|
||||
|
||||
/* this is not thread-safe, but then the entire GstMixer API isn't really,
|
||||
* since we return foo->list and not a copy and don't take any locks, so
|
||||
* not much we can do here but pray; we're usually either called from _new()
|
||||
* or from within _get_values() though, so it should be okay. We could use
|
||||
* atomic ops here, but I'm not sure how much more that really buys us.*/
|
||||
oldlist = options->values; /* keep window small */
|
||||
options->values = list;
|
||||
g_list_free (oldlist);
|
||||
|
||||
e->need_update = FALSE;
|
||||
|
||||
return options->values;
|
||||
}
|
||||
|
||||
static GList *
|
||||
gst_oss4_mixer_enum_get_values (GstMixerOptions * options)
|
||||
{
|
||||
GstOss4MixerEnum *e = GST_OSS4_MIXER_ENUM (options);
|
||||
GList *list;
|
||||
|
||||
/* we take the lock here mostly to serialise ioctls with the watch thread */
|
||||
GST_OBJECT_LOCK (e->mixer);
|
||||
|
||||
list = gst_oss4_mixer_enum_get_values_locked (options);
|
||||
|
||||
GST_OBJECT_UNLOCK (e->mixer);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static const gchar *
|
||||
gst_oss4_mixer_enum_get_current_value (GstOss4MixerEnum * e)
|
||||
{
|
||||
const gchar *cur_val = NULL;
|
||||
|
||||
if (e->mc->enum_vals != NULL && e->mc->last_val < e->mc->mixext.maxvalue) {
|
||||
cur_val = g_quark_to_string (e->mc->enum_vals[e->mc->last_val]);
|
||||
}
|
||||
|
||||
return cur_val;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_oss4_mixer_enum_update_current (GstOss4MixerEnum * e)
|
||||
{
|
||||
int cur = -1;
|
||||
|
||||
if (!gst_oss4_mixer_get_control_val (e->mixer, e->mc, &cur))
|
||||
return FALSE;
|
||||
|
||||
if (cur < 0 || cur >= e->mc->mixext.maxvalue) {
|
||||
GST_WARNING_OBJECT (e, "read value %d out of bounds [0-%d]", cur,
|
||||
e->mc->mixext.maxvalue - 1);
|
||||
e->mc->last_val = 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_oss4_mixer_enum_set_option (GstOss4MixerEnum * e, const gchar * value)
|
||||
{
|
||||
GQuark q;
|
||||
int i;
|
||||
|
||||
q = g_quark_try_string (value);
|
||||
if (q == 0) {
|
||||
GST_WARNING_OBJECT (e, "unknown option '%s'", value);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (i = 0; i < e->mc->mixext.maxvalue; ++i) {
|
||||
if (q == e->mc->enum_vals[i])
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= e->mc->mixext.maxvalue) {
|
||||
GST_WARNING_OBJECT (e, "option '%s' is not valid for this control", value);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (e, "option '%s' = %d", value, i);
|
||||
|
||||
if (!MIXEXT_ENUM_IS_AVAILABLE (e->mc->mixext, i)) {
|
||||
GST_WARNING_OBJECT (e, "option '%s' is not selectable currently", value);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_oss4_mixer_set_control_val (e->mixer, e->mc, i)) {
|
||||
GST_WARNING_OBJECT (e, "could not set option '%s' (%d)", value, i);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* and re-read current value with sanity checks (or could just assign here) */
|
||||
gst_oss4_mixer_enum_update_current (e);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
const gchar *
|
||||
gst_oss4_mixer_enum_get_option (GstOss4MixerEnum * e)
|
||||
{
|
||||
const gchar *cur_str = NULL;
|
||||
|
||||
if (!gst_oss4_mixer_enum_update_current (e)) {
|
||||
GST_WARNING_OBJECT (e, "failed to read current value");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cur_str = gst_oss4_mixer_enum_get_current_value (e);
|
||||
GST_LOG_OBJECT (e, "%s (%d)", GST_STR_NULL (cur_str), e->mc->last_val);
|
||||
return cur_str;
|
||||
}
|
||||
|
||||
GstMixerTrack *
|
||||
gst_oss4_mixer_enum_new (GstOss4Mixer * mixer, GstOss4MixerControl * mc)
|
||||
{
|
||||
GstOss4MixerEnum *e;
|
||||
GstMixerTrack *track;
|
||||
|
||||
e = g_object_new (GST_TYPE_OSS4_MIXER_ENUM, "untranslated-label",
|
||||
mc->mixext.extname, NULL);
|
||||
e->mixer = mixer;
|
||||
e->mc = mc;
|
||||
|
||||
track = GST_MIXER_TRACK (e);
|
||||
|
||||
/* caller will set track->label and track->flags */
|
||||
|
||||
track->num_channels = 0;
|
||||
track->min_volume = 0;
|
||||
track->max_volume = 0;
|
||||
|
||||
(void) gst_oss4_mixer_enum_get_values_locked (GST_MIXER_OPTIONS (track));
|
||||
|
||||
if (!gst_oss4_mixer_enum_update_current (e)) {
|
||||
GST_WARNING_OBJECT (track, "failed to read current value, returning NULL");
|
||||
g_object_unref (track);
|
||||
track = NULL;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (e, "current value: %d (%s)", e->mc->last_val,
|
||||
gst_oss4_mixer_enum_get_current_value (e));
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
/* This is called from the watch thread */
|
||||
void
|
||||
gst_oss4_mixer_enum_process_change_unlocked (GstMixerTrack * track)
|
||||
{
|
||||
GstOss4MixerEnum *e = GST_OSS4_MIXER_ENUM_CAST (track);
|
||||
|
||||
gchar *cur;
|
||||
|
||||
if (!e->mc->changed && !e->mc->list_changed)
|
||||
return;
|
||||
|
||||
if (e->mc->list_changed) {
|
||||
gst_mixer_options_list_changed (GST_MIXER (e->mixer),
|
||||
GST_MIXER_OPTIONS (e));
|
||||
}
|
||||
|
||||
GST_OBJECT_LOCK (e->mixer);
|
||||
cur = (gchar *) gst_oss4_mixer_enum_get_current_value (e);
|
||||
GST_OBJECT_UNLOCK (e->mixer);
|
||||
|
||||
gst_mixer_option_changed (GST_MIXER (e->mixer), GST_MIXER_OPTIONS (e), cur);
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
/* GStreamer OSS4 mixer on/off enum control
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef GST_OSS4_MIXER_ENUM_H
|
||||
#define GST_OSS4_MIXER_ENUM_H
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/interfaces/mixer.h>
|
||||
|
||||
#include "oss4-mixer.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_OSS4_MIXER_ENUM (gst_oss4_mixer_enum_get_type())
|
||||
#define GST_OSS4_MIXER_ENUM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OSS4_MIXER_ENUM,GstOss4MixerEnum))
|
||||
#define GST_OSS4_MIXER_ENUM_CAST(obj) ((GstOss4MixerEnum *)(obj))
|
||||
#define GST_OSS4_MIXER_ENUM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSS4_MIXER_ENUM,GstOss4MixerEnumClass))
|
||||
#define GST_IS_OSS4_MIXER_ENUM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OSS4_MIXER_ENUM))
|
||||
#define GST_IS_OSS4_MIXER_ENUM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OSS4_MIXER_ENUM))
|
||||
|
||||
typedef struct _GstOss4MixerEnum GstOss4MixerEnum;
|
||||
typedef struct _GstOss4MixerEnumClass GstOss4MixerEnumClass;
|
||||
|
||||
struct _GstOss4MixerEnum {
|
||||
GstMixerOptions mixer_option;
|
||||
|
||||
GstOss4MixerControl * mc;
|
||||
GstOss4Mixer * mixer; /* the mixer we belong to (no ref taken) */
|
||||
|
||||
gboolean need_update;
|
||||
};
|
||||
|
||||
struct _GstOss4MixerEnumClass {
|
||||
GstMixerOptionsClass mixer_option_class;
|
||||
};
|
||||
|
||||
GType gst_oss4_mixer_enum_get_type (void);
|
||||
|
||||
gboolean gst_oss4_mixer_enum_set_option (GstOss4MixerEnum * e, const gchar * value);
|
||||
|
||||
const gchar * gst_oss4_mixer_enum_get_option (GstOss4MixerEnum * e);
|
||||
|
||||
GstMixerTrack * gst_oss4_mixer_enum_new (GstOss4Mixer * mixer, GstOss4MixerControl * mc);
|
||||
|
||||
void gst_oss4_mixer_enum_process_change_unlocked (GstMixerTrack * track);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* GST_OSS4_MIXER_ENUM_H */
|
||||
|
||||
|
|
@ -1,311 +0,0 @@
|
|||
/* GStreamer OSS4 mixer slider control
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* A 'slider' in gnome-volume-control / GstMixer is represented by a
|
||||
* GstMixerTrack with one or more channels.
|
||||
*
|
||||
* A slider should be either flagged as INPUT or OUTPUT (mostly because of
|
||||
* gnome-volume-control being littered with g_asserts for everything it doesn't
|
||||
* expect).
|
||||
*
|
||||
* From mixertrack.h:
|
||||
* "Input tracks can have 'recording' enabled, which means that any input will
|
||||
* be hearable into the speakers that are attached to the output. Mute is
|
||||
* obvious."
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <gst/gst-i18n-plugin.h>
|
||||
|
||||
#define NO_LEGACY_MIXER
|
||||
#include "oss4-mixer-slider.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (oss4mixer_debug);
|
||||
#define GST_CAT_DEFAULT oss4mixer_debug
|
||||
|
||||
/* GstMixerTrack is a plain GObject, so let's just use the GLib macro here */
|
||||
G_DEFINE_TYPE (GstOss4MixerSlider, gst_oss4_mixer_slider, GST_TYPE_MIXER_TRACK);
|
||||
|
||||
static void
|
||||
gst_oss4_mixer_slider_class_init (GstOss4MixerSliderClass * klass)
|
||||
{
|
||||
/* nothing to do here */
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_mixer_slider_init (GstOss4MixerSlider * s)
|
||||
{
|
||||
/* nothing to do here */
|
||||
}
|
||||
|
||||
static int
|
||||
gst_oss4_mixer_slider_pack_volume (GstOss4MixerSlider * s, const gint * volumes)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
switch (s->mc->mixext.type) {
|
||||
case MIXT_MONOSLIDER:
|
||||
case MIXT_MONOSLIDER16:
|
||||
case MIXT_SLIDER:
|
||||
val = volumes[0];
|
||||
break;
|
||||
case MIXT_STEREOSLIDER:
|
||||
val = ((volumes[1] & 0xff) << 8) | (volumes[0] & 0xff);
|
||||
break;
|
||||
case MIXT_STEREOSLIDER16:
|
||||
val = ((volumes[1] & 0xffff) << 16) | (volumes[0] & 0xffff);
|
||||
break;
|
||||
default:
|
||||
g_return_val_if_reached (0);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_mixer_slider_unpack_volume (GstOss4MixerSlider * s, int v,
|
||||
gint * volumes)
|
||||
{
|
||||
guint32 val; /* use uint so bitshifting the highest bit works right */
|
||||
|
||||
val = (guint32) v;
|
||||
switch (s->mc->mixext.type) {
|
||||
case MIXT_SLIDER:
|
||||
volumes[0] = val;
|
||||
break;
|
||||
case MIXT_MONOSLIDER:
|
||||
/* oss repeats the value in the upper bits, as if it was stereo */
|
||||
volumes[0] = val & 0x00ff;
|
||||
break;
|
||||
case MIXT_MONOSLIDER16:
|
||||
/* oss repeats the value in the upper bits, as if it was stereo */
|
||||
volumes[0] = val & 0x0000ffff;
|
||||
break;
|
||||
case MIXT_STEREOSLIDER:
|
||||
volumes[0] = (val & 0x00ff);
|
||||
volumes[1] = (val & 0xff00) >> 8;
|
||||
break;
|
||||
case MIXT_STEREOSLIDER16:
|
||||
volumes[0] = (val & 0x0000ffff);
|
||||
volumes[1] = (val & 0xffff0000) >> 16;
|
||||
break;
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_oss4_mixer_slider_get_volume (GstOss4MixerSlider * s, gint * volumes)
|
||||
{
|
||||
GstMixerTrack *track = GST_MIXER_TRACK (s);
|
||||
int v = 0;
|
||||
|
||||
/* if we're supposed to be muted, and don't have an actual mute control
|
||||
* (ie. 'simulate' the mute), then just return the volume as saved, not
|
||||
* the actually set volume which is most likely 0 */
|
||||
if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE) && !s->mc->mute) {
|
||||
volumes[0] = s->volumes[0];
|
||||
if (track->num_channels == 2)
|
||||
volumes[1] = s->volumes[1];
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!gst_oss4_mixer_get_control_val (s->mixer, s->mc, &v))
|
||||
return FALSE;
|
||||
|
||||
gst_oss4_mixer_slider_unpack_volume (s, v, volumes);
|
||||
|
||||
if (track->num_channels > 1) {
|
||||
GST_LOG_OBJECT (s, "volume: left=%d, right=%d", volumes[0], volumes[1]);
|
||||
} else {
|
||||
GST_LOG_OBJECT (s, "volume: mono=%d", volumes[0]);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_oss4_mixer_slider_set_volume (GstOss4MixerSlider * s, const gint * volumes)
|
||||
{
|
||||
GstMixerTrack *track = GST_MIXER_TRACK (s);
|
||||
int val = 0;
|
||||
|
||||
/* if we're supposed to be muted, and are 'simulating' the mute because
|
||||
* we don't have a mute control, don't actually change the volume, just
|
||||
* save it as the new desired volume for later when we get unmuted again */
|
||||
if (!GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_NO_MUTE)) {
|
||||
if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE) && !s->mc->mute)
|
||||
goto done;
|
||||
}
|
||||
|
||||
val = gst_oss4_mixer_slider_pack_volume (s, volumes);
|
||||
|
||||
if (track->num_channels > 1) {
|
||||
GST_LOG_OBJECT (s, "left=%d, right=%d", volumes[0], volumes[1]);
|
||||
} else {
|
||||
GST_LOG_OBJECT (s, "mono=%d", volumes[0]);
|
||||
}
|
||||
|
||||
if (!gst_oss4_mixer_set_control_val (s->mixer, s->mc, val))
|
||||
return FALSE;
|
||||
|
||||
done:
|
||||
|
||||
s->volumes[0] = volumes[0];
|
||||
if (track->num_channels == 2)
|
||||
s->volumes[1] = volumes[1];
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_oss4_mixer_slider_set_record (GstOss4MixerSlider * s, gboolean record)
|
||||
{
|
||||
/* There doesn't seem to be a way to do this using the OSS4 mixer API, so
|
||||
* just do nothing here for now. */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_oss4_mixer_slider_set_mute (GstOss4MixerSlider * s, gboolean mute)
|
||||
{
|
||||
GstMixerTrack *track = GST_MIXER_TRACK (s);
|
||||
gboolean ret;
|
||||
|
||||
/* if the control does not support muting, then do not do anything */
|
||||
if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_NO_MUTE)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* If we do not have a mute control, simulate mute (which is a bit broken,
|
||||
* since we can not differentiate between capture/playback volume etc., so
|
||||
* we just assume that setting the volume to 0 would be the same as muting
|
||||
* this control) */
|
||||
if (s->mc->mute == NULL) {
|
||||
int volume;
|
||||
|
||||
if (mute) {
|
||||
/* make sure the current volume values get saved. */
|
||||
gst_oss4_mixer_slider_get_volume (s, s->volumes);
|
||||
volume = 0;
|
||||
} else {
|
||||
volume = gst_oss4_mixer_slider_pack_volume (s, s->volumes);
|
||||
}
|
||||
ret = gst_oss4_mixer_set_control_val (s->mixer, s->mc, volume);
|
||||
} else {
|
||||
ret = gst_oss4_mixer_set_control_val (s->mixer, s->mc->mute, !!mute);
|
||||
}
|
||||
|
||||
if (mute) {
|
||||
track->flags |= GST_MIXER_TRACK_MUTE;
|
||||
} else {
|
||||
track->flags &= ~GST_MIXER_TRACK_MUTE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GstMixerTrack *
|
||||
gst_oss4_mixer_slider_new (GstOss4Mixer * mixer, GstOss4MixerControl * mc)
|
||||
{
|
||||
GstOss4MixerSlider *s;
|
||||
GstMixerTrack *track;
|
||||
gint volumes[2] = { 0, };
|
||||
|
||||
s = g_object_new (GST_TYPE_OSS4_MIXER_SLIDER, "untranslated-label",
|
||||
mc->mixext.extname, NULL);
|
||||
|
||||
track = GST_MIXER_TRACK (s);
|
||||
|
||||
/* caller will set track->label and track->flags */
|
||||
|
||||
s->mc = mc;
|
||||
s->mixer = mixer;
|
||||
|
||||
/* we don't do value scaling but just present a scale of 0-maxvalue */
|
||||
track->min_volume = 0;
|
||||
track->max_volume = mc->mixext.maxvalue;
|
||||
|
||||
switch (mc->mixext.type) {
|
||||
case MIXT_MONOSLIDER:
|
||||
case MIXT_MONOSLIDER16:
|
||||
case MIXT_SLIDER:
|
||||
track->num_channels = 1;
|
||||
break;
|
||||
case MIXT_STEREOSLIDER:
|
||||
case MIXT_STEREOSLIDER16:
|
||||
track->num_channels = 2;
|
||||
break;
|
||||
default:
|
||||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (track, "min=%d, max=%d, channels=%d", track->min_volume,
|
||||
track->max_volume, track->num_channels);
|
||||
|
||||
if (!gst_oss4_mixer_slider_get_volume (s, volumes)) {
|
||||
GST_WARNING_OBJECT (track, "failed to read volume, returning NULL");
|
||||
g_object_unref (track);
|
||||
track = NULL;
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
/* This is called from the watch thread */
|
||||
void
|
||||
gst_oss4_mixer_slider_process_change_unlocked (GstMixerTrack * track)
|
||||
{
|
||||
GstOss4MixerSlider *s = GST_OSS4_MIXER_SLIDER_CAST (track);
|
||||
|
||||
if (s->mc->mute != NULL && s->mc->mute->changed) {
|
||||
gst_mixer_mute_toggled (GST_MIXER (s->mixer), track,
|
||||
!!s->mc->mute->last_val);
|
||||
} else {
|
||||
/* nothing to do here, since we don't/can't easily implement the record
|
||||
* flag */
|
||||
}
|
||||
|
||||
if (s->mc->changed) {
|
||||
gint volumes[2] = { 0, 0 };
|
||||
|
||||
gst_oss4_mixer_slider_unpack_volume (s, s->mc->last_val, volumes);
|
||||
|
||||
/* if we 'simulate' the mute, update flag when the volume changes */
|
||||
if (s->mc->mute == NULL) {
|
||||
if (volumes[0] == 0 && volumes[1] == 0) {
|
||||
track->flags |= GST_MIXER_TRACK_MUTE;
|
||||
} else {
|
||||
track->flags &= ~GST_MIXER_TRACK_MUTE;
|
||||
}
|
||||
}
|
||||
|
||||
gst_mixer_volume_changed (GST_MIXER (s->mixer), track, volumes);
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/* GStreamer OSS4 mixer slider control
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef GST_OSS4_MIXER_SLIDER_H
|
||||
#define GST_OSS4_MIXER_SLIDER_H
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/interfaces/mixer.h>
|
||||
|
||||
#include "oss4-mixer.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_OSS4_MIXER_SLIDER (gst_oss4_mixer_slider_get_type())
|
||||
#define GST_OSS4_MIXER_SLIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OSS4_MIXER_SLIDER,GstOss4MixerSlider))
|
||||
#define GST_OSS4_MIXER_SLIDER_CAST(obj) ((GstOss4MixerSlider *)(obj))
|
||||
#define GST_OSS4_MIXER_SLIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSS4_MIXER_SLIDER,GstOss4MixerSliderClass))
|
||||
#define GST_IS_OSS4_MIXER_SLIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OSS4_MIXER_SLIDER))
|
||||
#define GST_IS_OSS4_MIXER_SLIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OSS4_MIXER_SLIDER))
|
||||
|
||||
typedef struct _GstOss4MixerSlider GstOss4MixerSlider;
|
||||
typedef struct _GstOss4MixerSliderClass GstOss4MixerSliderClass;
|
||||
|
||||
struct _GstOss4MixerSlider {
|
||||
GstMixerTrack mixer_track;
|
||||
|
||||
GstOss4MixerControl * mc;
|
||||
GstOss4Mixer * mixer; /* the mixer we belong to (no ref taken) */
|
||||
gint volumes[2]; /* left/mono, right */
|
||||
};
|
||||
|
||||
struct _GstOss4MixerSliderClass {
|
||||
GstMixerTrackClass mixer_track_class;
|
||||
};
|
||||
|
||||
GType gst_oss4_mixer_slider_get_type (void);
|
||||
|
||||
GstMixerTrack * gst_oss4_mixer_slider_new (GstOss4Mixer * mixer, GstOss4MixerControl * mc);
|
||||
|
||||
gboolean gst_oss4_mixer_slider_get_volume (GstOss4MixerSlider * s, gint * volumes);
|
||||
|
||||
gboolean gst_oss4_mixer_slider_set_volume (GstOss4MixerSlider * s, const gint * volumes);
|
||||
|
||||
gboolean gst_oss4_mixer_slider_set_record (GstOss4MixerSlider * s, gboolean record);
|
||||
|
||||
gboolean gst_oss4_mixer_slider_set_mute (GstOss4MixerSlider * s, gboolean mute);
|
||||
|
||||
void gst_oss4_mixer_slider_process_change_unlocked (GstMixerTrack * track);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* GST_OSS4_MIXER_SLIDER_H */
|
||||
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
/* GStreamer OSS4 mixer on/off switch control
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* A simple ON/OFF 'switch' in gnome-volume-control / GstMixer is represented
|
||||
* by a GstMixerTrack with no channels.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gst/gst-i18n-plugin.h>
|
||||
|
||||
#define NO_LEGACY_MIXER
|
||||
#include "oss4-mixer-switch.h"
|
||||
#include "oss4-soundcard.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (oss4mixer_debug);
|
||||
#define GST_CAT_DEFAULT oss4mixer_debug
|
||||
|
||||
/* GstMixerTrack is a plain GObject, so let's just use the GLib macro here */
|
||||
G_DEFINE_TYPE (GstOss4MixerSwitch, gst_oss4_mixer_switch, GST_TYPE_MIXER_TRACK);
|
||||
|
||||
static void
|
||||
gst_oss4_mixer_switch_class_init (GstOss4MixerSwitchClass * klass)
|
||||
{
|
||||
/* nothing to do here */
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_mixer_switch_init (GstOss4MixerSwitch * s)
|
||||
{
|
||||
/* nothing to do here */
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_oss4_mixer_switch_set (GstOss4MixerSwitch * s, gboolean disabled)
|
||||
{
|
||||
GstMixerTrack *track;
|
||||
int newval;
|
||||
|
||||
track = GST_MIXER_TRACK (s);
|
||||
|
||||
newval = disabled ? GST_MIXER_TRACK_MUTE : 0;
|
||||
|
||||
if (newval == (track->flags & GST_MIXER_TRACK_MUTE)) {
|
||||
GST_LOG_OBJECT (s, "switch is already %d, doing nothing", newval);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!gst_oss4_mixer_set_control_val (s->mixer, s->mc, !disabled)) {
|
||||
GST_WARNING_OBJECT (s, "could not set switch to %d", !disabled);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (disabled) {
|
||||
track->flags |= GST_MIXER_TRACK_MUTE;
|
||||
} else {
|
||||
track->flags &= ~GST_MIXER_TRACK_MUTE;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (s, "set switch to %d", newval);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_oss4_mixer_switch_get (GstOss4MixerSwitch * s, gboolean * disabled)
|
||||
{
|
||||
GstMixerTrack *track;
|
||||
int flag;
|
||||
int enabled = -1;
|
||||
|
||||
track = GST_MIXER_TRACK (s);
|
||||
|
||||
if (!gst_oss4_mixer_get_control_val (s->mixer, s->mc, &enabled)
|
||||
|| (enabled < 0)) {
|
||||
GST_WARNING_OBJECT (s, "could not get switch state");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
flag = (enabled == 0) ? GST_MIXER_TRACK_MUTE : 0;
|
||||
|
||||
if (enabled) {
|
||||
track->flags &= ~GST_MIXER_TRACK_MUTE;
|
||||
} else {
|
||||
track->flags |= GST_MIXER_TRACK_MUTE;
|
||||
}
|
||||
*disabled = (enabled == 0);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GstMixerTrack *
|
||||
gst_oss4_mixer_switch_new (GstOss4Mixer * mixer, GstOss4MixerControl * mc)
|
||||
{
|
||||
GstOss4MixerSwitch *s;
|
||||
GstMixerTrack *track;
|
||||
int cur = -1;
|
||||
|
||||
s = g_object_new (GST_TYPE_OSS4_MIXER_SWITCH, "untranslated-label",
|
||||
mc->mixext.extname, NULL);
|
||||
|
||||
s->mixer = mixer;
|
||||
s->mc = mc;
|
||||
|
||||
track = GST_MIXER_TRACK (s);
|
||||
|
||||
/* caller will set track->label and track->flags */
|
||||
|
||||
track->num_channels = 0;
|
||||
track->min_volume = 0;
|
||||
track->max_volume = 0;
|
||||
|
||||
if (!gst_oss4_mixer_get_control_val (s->mixer, s->mc, &cur) || cur < 0)
|
||||
return NULL;
|
||||
|
||||
if (cur) {
|
||||
track->flags &= ~GST_MIXER_TRACK_MUTE;
|
||||
} else {
|
||||
track->flags |= GST_MIXER_TRACK_MUTE;
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
/* This is called from the watch thread */
|
||||
void
|
||||
gst_oss4_mixer_switch_process_change_unlocked (GstMixerTrack * track)
|
||||
{
|
||||
GstOss4MixerSwitch *s = GST_OSS4_MIXER_SWITCH_CAST (track);
|
||||
|
||||
if (!s->mc->changed)
|
||||
return;
|
||||
|
||||
gst_mixer_mute_toggled (GST_MIXER (s->mixer), track, !s->mc->last_val);
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/* GStreamer OSS4 mixer on/off switch control
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef GST_OSS4_MIXER_SWITCH_H
|
||||
#define GST_OSS4_MIXER_SWITCH_H
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/interfaces/mixer.h>
|
||||
|
||||
#include "oss4-mixer.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_OSS4_MIXER_SWITCH (gst_oss4_mixer_switch_get_type())
|
||||
#define GST_OSS4_MIXER_SWITCH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OSS4_MIXER_SWITCH,GstOss4MixerSwitch))
|
||||
#define GST_OSS4_MIXER_SWITCH_CAST(obj) ((GstOss4MixerSwitch *)(obj))
|
||||
#define GST_OSS4_MIXER_SWITCH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSS4_MIXER_SWITCH,GstOss4MixerSwitchClass))
|
||||
#define GST_IS_OSS4_MIXER_SWITCH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OSS4_MIXER_SWITCH))
|
||||
#define GST_IS_OSS4_MIXER_SWITCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OSS4_MIXER_SWITCH))
|
||||
|
||||
typedef struct _GstOss4MixerSwitch GstOss4MixerSwitch;
|
||||
typedef struct _GstOss4MixerSwitchClass GstOss4MixerSwitchClass;
|
||||
|
||||
struct _GstOss4MixerSwitch {
|
||||
GstMixerTrack mixer_track;
|
||||
|
||||
GstOss4MixerControl * mc;
|
||||
GstOss4Mixer * mixer; /* the mixer we belong to (no ref taken) */
|
||||
};
|
||||
|
||||
struct _GstOss4MixerSwitchClass {
|
||||
GstMixerTrackClass mixer_track_class;
|
||||
};
|
||||
|
||||
GType gst_oss4_mixer_switch_get_type (void);
|
||||
|
||||
gboolean gst_oss4_mixer_switch_set (GstOss4MixerSwitch * s, gboolean enabled);
|
||||
|
||||
gboolean gst_oss4_mixer_switch_get (GstOss4MixerSwitch * s, gboolean * enabled);
|
||||
|
||||
GstMixerTrack * gst_oss4_mixer_switch_new (GstOss4Mixer * mixer, GstOss4MixerControl * mc);
|
||||
|
||||
void gst_oss4_mixer_switch_process_change_unlocked (GstMixerTrack * track);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* GST_OSS4_MIXER_SWITCH_H */
|
||||
|
||||
|
File diff suppressed because it is too large
Load diff
|
@ -1,128 +0,0 @@
|
|||
/* GStreamer OSS4 mixer implementation
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef OSS4_MIXER_H
|
||||
#define OSS4_MIXER_H
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "oss4-soundcard.h"
|
||||
|
||||
#define GST_OSS4_MIXER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OSS4_MIXER,GstOss4Mixer))
|
||||
#define GST_OSS4_MIXER_CAST(obj) ((GstOss4Mixer *)(obj))
|
||||
#define GST_OSS4_MIXER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSS4_MIXER,GstOss4MixerClass))
|
||||
#define GST_IS_OSS4_MIXER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OSS4_MIXER))
|
||||
#define GST_IS_OSS4_MIXER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OSS4_MIXER))
|
||||
#define GST_TYPE_OSS4_MIXER (gst_oss4_mixer_get_type())
|
||||
|
||||
#define GST_OSS4_MIXER_IS_OPEN(obj) (GST_OSS4_MIXER(obj)->fd != -1)
|
||||
|
||||
typedef struct _GstOss4Mixer GstOss4Mixer;
|
||||
typedef struct _GstOss4MixerClass GstOss4MixerClass;
|
||||
|
||||
struct _GstOss4Mixer {
|
||||
GstElement element;
|
||||
|
||||
/*< private >*/
|
||||
|
||||
/* element bits'n'bops */
|
||||
gchar * device;
|
||||
|
||||
/* mixer details */
|
||||
gint fd; /* file descriptor if open, or -1 */
|
||||
gchar * device_name; /* device description, or NULL */
|
||||
gchar * open_device; /* the device we opened */
|
||||
|
||||
GList * tracks; /* list of available tracks */
|
||||
GList * controls; /* list of available controls */
|
||||
gboolean need_update; /* re-read list of available tracks? */
|
||||
|
||||
oss_mixext last_mixext; /* we keep this around so we can
|
||||
* easily check if the mixer
|
||||
* interface has changed */
|
||||
|
||||
GThread * watch_thread; /* thread watching for value changes */
|
||||
GCond * watch_cond;
|
||||
gint watch_shutdown;
|
||||
gint modify_counter; /* from MIXERINFO */
|
||||
|
||||
/* for property probe interface */
|
||||
GList * property_probe_list;
|
||||
};
|
||||
|
||||
struct _GstOss4MixerClass {
|
||||
GstElementClass element_class;
|
||||
};
|
||||
|
||||
/* helper struct holding info about one control */
|
||||
typedef struct _GstOss4MixerControl GstOss4MixerControl;
|
||||
|
||||
struct _GstOss4MixerControl {
|
||||
oss_mixext mixext;
|
||||
GstOss4MixerControl *parent; /* NULL if root */
|
||||
GstOss4MixerControl *mute; /* sibling with mute function, or NULL */
|
||||
GList *mute_group; /* group of mute controls, or NULL */
|
||||
GList *children; /* GstOss4MixerControls (no ownership) */
|
||||
|
||||
GQuark *enum_vals; /* 0-terminated array of values or NULL */
|
||||
int enum_version; /* 0 = list won't change */
|
||||
|
||||
int last_val; /* last value seen */
|
||||
|
||||
gboolean is_virtual : 1; /* is a vmix control with dynamic label */
|
||||
gboolean is_master : 1;
|
||||
gboolean is_slider : 1; /* represent as slider */
|
||||
gboolean is_switch : 1; /* represent as switch */
|
||||
gboolean is_enum : 1; /* represent as combo/enumeration */
|
||||
gboolean no_list : 1; /* enumeration with no list available */
|
||||
gboolean is_input : 1; /* is an input-related control */
|
||||
gboolean is_output : 1; /* is an output-related control */
|
||||
gboolean used : 1; /* whether we know what to do with this */
|
||||
|
||||
gboolean changed : 1; /* transient flag used by watch thread */
|
||||
gboolean list_changed : 1; /* transient flag used by watch thread */
|
||||
};
|
||||
|
||||
/* header says parent=-1 means root, but it can also be parent=ctrl */
|
||||
#define MIXEXT_IS_ROOT(me) ((me).parent == -1 || (me).parent == (me).ctrl)
|
||||
|
||||
#define MIXEXT_IS_SLIDER(me) ((me).type == MIXT_MONOSLIDER || \
|
||||
(me).type == MIXT_STEREOSLIDER || (me).type == MIXT_MONOSLIDER16 || \
|
||||
(me).type == MIXT_STEREOSLIDER16 || (me).type == MIXT_SLIDER)
|
||||
|
||||
#define MIXEXT_HAS_DESCRIPTION(me) (((me).flags & MIXF_DESCR) != 0)
|
||||
|
||||
#define MIXEXT_ENUM_IS_AVAILABLE(me,num) \
|
||||
(((me).enum_present[num/8]) & (1 << (num % 8)))
|
||||
|
||||
|
||||
GType gst_oss4_mixer_get_type (void);
|
||||
|
||||
gboolean gst_oss4_mixer_get_control_val (GstOss4Mixer * mixer,
|
||||
GstOss4MixerControl * mc,
|
||||
int * val);
|
||||
|
||||
gboolean gst_oss4_mixer_set_control_val (GstOss4Mixer * mixer,
|
||||
GstOss4MixerControl * mc,
|
||||
int val);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* OSS4_MIXER_H */
|
||||
|
|
@ -1,414 +0,0 @@
|
|||
/* GStreamer OSS4 audio property probe interface implementation
|
||||
* 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 <gst/gst.h>
|
||||
|
||||
#define NO_LEGACY_MIXER
|
||||
#include "oss4-audio.h"
|
||||
#include "oss4-mixer.h"
|
||||
#include "oss4-sink.h"
|
||||
#include "oss4-source.h"
|
||||
#include "oss4-soundcard.h"
|
||||
#include "oss4-property-probe.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (oss4_debug);
|
||||
#define GST_CAT_DEFAULT oss4_debug
|
||||
|
||||
static const GList *
|
||||
gst_oss4_property_probe_get_properties (GstPropertyProbe * probe)
|
||||
{
|
||||
GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
|
||||
GList *list;
|
||||
|
||||
GST_OBJECT_LOCK (GST_OBJECT (probe));
|
||||
|
||||
/* we create a new list and store it in the instance struct, since apparently
|
||||
* we forgot to update the API for 0.10 (and why don't we mark probable
|
||||
* properties with a flag instead anyway?). A bit hackish, but what can you
|
||||
* do (can't really use a static variable since the pspec will be different
|
||||
* for src and sink class); this isn't particularly pretty, but the best
|
||||
* we can do given that we can't create a common base class (we could do
|
||||
* fancy things with the interface, or use g_object_set_data instead, but
|
||||
* it's not really going to make it much better) */
|
||||
if (GST_IS_AUDIO_SINK_CLASS (klass)) {
|
||||
list = GST_OSS4_SINK (probe)->property_probe_list;
|
||||
} else if (GST_IS_AUDIO_SRC_CLASS (klass)) {
|
||||
list = GST_OSS4_SOURCE (probe)->property_probe_list;
|
||||
} else if (GST_IS_OSS4_MIXER_CLASS (klass)) {
|
||||
list = GST_OSS4_MIXER (probe)->property_probe_list;
|
||||
} else {
|
||||
GST_OBJECT_UNLOCK (GST_OBJECT (probe));
|
||||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
|
||||
if (list == NULL) {
|
||||
GParamSpec *pspec;
|
||||
|
||||
pspec = g_object_class_find_property (klass, "device");
|
||||
list = g_list_prepend (NULL, pspec);
|
||||
|
||||
if (GST_IS_AUDIO_SINK_CLASS (klass)) {
|
||||
GST_OSS4_SINK (probe)->property_probe_list = list;
|
||||
} else if (GST_IS_AUDIO_SRC_CLASS (klass)) {
|
||||
GST_OSS4_SOURCE (probe)->property_probe_list = list;
|
||||
} else if (GST_IS_OSS4_MIXER_CLASS (klass)) {
|
||||
GST_OSS4_MIXER (probe)->property_probe_list = list;
|
||||
}
|
||||
}
|
||||
|
||||
GST_OBJECT_UNLOCK (GST_OBJECT (probe));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_property_probe_probe_property (GstPropertyProbe * probe,
|
||||
guint prop_id, const GParamSpec * pspec)
|
||||
{
|
||||
if (!g_str_equal (pspec->name, "device")) {
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_oss4_property_probe_needs_probe (GstPropertyProbe * probe,
|
||||
guint prop_id, const GParamSpec * pspec)
|
||||
{
|
||||
/* don't cache probed data */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gint
|
||||
oss4_mixerinfo_priority_cmp (struct oss_mixerinfo *mi1,
|
||||
struct oss_mixerinfo *mi2)
|
||||
{
|
||||
/* return negative vaue if mi1 comes before mi2 */
|
||||
if (mi1->priority != mi2->priority)
|
||||
return mi2->priority - mi1->priority;
|
||||
|
||||
return strcmp (mi1->devnode, mi2->devnode);
|
||||
}
|
||||
|
||||
/* caller must ensure LOCK is taken (e.g. if ioctls need to be serialised) */
|
||||
gboolean
|
||||
gst_oss4_property_probe_find_device_name (GstObject * obj, int fd,
|
||||
const gchar * device_handle, gchar ** device_name)
|
||||
{
|
||||
struct oss_sysinfo si = { {0,}, };
|
||||
gchar *name = NULL;
|
||||
|
||||
if (ioctl (fd, SNDCTL_SYSINFO, &si) == 0) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < si.numaudios; ++i) {
|
||||
struct oss_audioinfo ai = { 0, };
|
||||
|
||||
ai.dev = i;
|
||||
if (ioctl (fd, SNDCTL_AUDIOINFO, &ai) == -1) {
|
||||
GST_DEBUG_OBJECT (obj, "AUDIOINFO ioctl for device %d failed", i);
|
||||
continue;
|
||||
}
|
||||
if (strcmp (ai.devnode, device_handle) == 0) {
|
||||
name = g_strdup (ai.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GST_WARNING_OBJECT (obj, "SYSINFO ioctl failed: %s", g_strerror (errno));
|
||||
}
|
||||
|
||||
/* try ENGINEINFO as fallback (which is better than nothing) */
|
||||
if (name == NULL) {
|
||||
struct oss_audioinfo ai = { 0, };
|
||||
|
||||
GST_LOG_OBJECT (obj, "device %s not listed in AUDIOINFO", device_handle);
|
||||
ai.dev = -1;
|
||||
if (ioctl (fd, SNDCTL_ENGINEINFO, &ai) == 0)
|
||||
name = g_strdup (ai.name);
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (obj, "Device name: %s", GST_STR_NULL (name));
|
||||
|
||||
if (name != NULL)
|
||||
*device_name = name;
|
||||
|
||||
return (name != NULL);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_oss4_property_probe_find_device_name_nofd (GstObject * obj,
|
||||
const gchar * device_handle, gchar ** device_name)
|
||||
{
|
||||
gboolean res;
|
||||
int fd;
|
||||
|
||||
fd = open ("/dev/mixer", O_RDONLY);
|
||||
if (fd < 0)
|
||||
return FALSE;
|
||||
|
||||
res = gst_oss4_property_probe_find_device_name (obj, fd, device_handle,
|
||||
device_name);
|
||||
|
||||
close (fd);
|
||||
return res;
|
||||
}
|
||||
|
||||
static GList *
|
||||
gst_oss4_property_probe_get_mixer_devices (GstObject * obj, int fd,
|
||||
struct oss_sysinfo *si)
|
||||
{
|
||||
GList *m, *mixers = NULL;
|
||||
GList *devices = NULL;
|
||||
|
||||
int i;
|
||||
|
||||
GST_LOG_OBJECT (obj, "%d mixer devices", si->nummixers);
|
||||
|
||||
/* first, find suitable mixer devices and sort by priority */
|
||||
for (i = 0; i < si->nummixers; ++i) {
|
||||
struct oss_mixerinfo mi = { 0, };
|
||||
|
||||
mi.dev = i;
|
||||
if (ioctl (fd, SNDCTL_MIXERINFO, &mi) == -1) {
|
||||
GST_DEBUG_OBJECT (obj, "MIXERINFO ioctl for device %d failed", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
GST_INFO_OBJECT (obj, "mixer device %d:", i);
|
||||
GST_INFO_OBJECT (obj, " enabled : %s", (mi.enabled) ? "yes" :
|
||||
"no (powered off or unplugged)");
|
||||
GST_INFO_OBJECT (obj, " priority : %d", mi.priority);
|
||||
GST_INFO_OBJECT (obj, " devnode : %s", mi.devnode);
|
||||
GST_INFO_OBJECT (obj, " handle : %s", mi.handle);
|
||||
GST_INFO_OBJECT (obj, " caps : 0x%02x", mi.caps);
|
||||
GST_INFO_OBJECT (obj, " name : %s", mi.name);
|
||||
|
||||
if (!mi.enabled) {
|
||||
GST_DEBUG_OBJECT (obj, "mixer device is not usable/enabled, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* only want mixers that control hardware directly */
|
||||
if ((mi.caps & MIXER_CAP_VIRTUAL)) {
|
||||
GST_DEBUG_OBJECT (obj, "mixer device is a virtual device, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
mixers = g_list_insert_sorted (mixers, g_memdup (&mi, sizeof (mi)),
|
||||
(GCompareFunc) oss4_mixerinfo_priority_cmp);
|
||||
}
|
||||
|
||||
/* then create device list according to priority */
|
||||
for (m = mixers; m != NULL; m = m->next) {
|
||||
struct oss_mixerinfo *mi = (struct oss_mixerinfo *) m->data;
|
||||
|
||||
GST_LOG_OBJECT (obj, "mixer device: '%s'", mi->devnode);
|
||||
devices = g_list_prepend (devices, g_strdup (mi->devnode));
|
||||
g_free (m->data);
|
||||
}
|
||||
g_list_free (mixers);
|
||||
mixers = NULL;
|
||||
|
||||
return g_list_reverse (devices);
|
||||
}
|
||||
|
||||
static GList *
|
||||
gst_oss4_property_probe_get_audio_devices (GstObject * obj, int fd,
|
||||
struct oss_sysinfo *si, int cap_mask)
|
||||
{
|
||||
GList *devices = NULL;
|
||||
int i;
|
||||
|
||||
GST_LOG_OBJECT (obj, "%d audio/dsp devices", si->numaudios);
|
||||
|
||||
for (i = 0; i < si->numaudios; ++i) {
|
||||
struct oss_audioinfo ai = { 0, };
|
||||
|
||||
ai.dev = i;
|
||||
if (ioctl (fd, SNDCTL_AUDIOINFO, &ai) == -1) {
|
||||
GST_DEBUG_OBJECT (obj, "AUDIOINFO ioctl for device %d failed", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((ai.caps & cap_mask) == 0) {
|
||||
GST_DEBUG_OBJECT (obj, "audio device %d is not an %s device", i,
|
||||
(cap_mask == PCM_CAP_OUTPUT) ? "output" : "input");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ai.enabled) {
|
||||
GST_DEBUG_OBJECT (obj, "audio device %d is not usable/enabled", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (obj, "audio device %d looks ok: %s (\"%s\")", i,
|
||||
ai.devnode, ai.name);
|
||||
|
||||
devices = g_list_prepend (devices, g_strdup (ai.devnode));
|
||||
}
|
||||
|
||||
return g_list_reverse (devices);
|
||||
}
|
||||
|
||||
static GValueArray *
|
||||
gst_oss4_property_probe_get_values (GstPropertyProbe * probe,
|
||||
guint prop_id, const GParamSpec * pspec)
|
||||
{
|
||||
struct oss_sysinfo si = { {0,}, };
|
||||
GstElementClass *klass;
|
||||
GValueArray *array = NULL;
|
||||
GstObject *obj;
|
||||
GList *devices, *l;
|
||||
int cap_mask, fd = -1;
|
||||
|
||||
if (!g_str_equal (pspec->name, "device")) {
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj = GST_OBJECT (probe);
|
||||
|
||||
GST_OBJECT_LOCK (obj);
|
||||
|
||||
/* figure out whether the element is a source or sink */
|
||||
klass = GST_ELEMENT_GET_CLASS (GST_ELEMENT (probe));
|
||||
if (GST_IS_OSS4_SINK (probe)) {
|
||||
GST_DEBUG_OBJECT (probe, "probing available output devices");
|
||||
cap_mask = PCM_CAP_OUTPUT;
|
||||
fd = GST_OSS4_SINK (probe)->fd;
|
||||
} else if (GST_IS_OSS4_SOURCE (probe)) {
|
||||
GST_DEBUG_OBJECT (probe, "probing available input devices");
|
||||
cap_mask = PCM_CAP_INPUT;
|
||||
fd = GST_OSS4_SOURCE (probe)->fd;
|
||||
} else if (GST_IS_OSS4_MIXER (probe)) {
|
||||
fd = GST_OSS4_MIXER (probe)->fd;
|
||||
cap_mask = 0;
|
||||
} else {
|
||||
GST_OBJECT_UNLOCK (obj);
|
||||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
|
||||
/* copy fd if it's open, so we can just unconditionally close() later */
|
||||
if (fd != -1)
|
||||
fd = dup (fd);
|
||||
|
||||
/* this will also catch the unlikely case where the above dup() failed */
|
||||
if (fd == -1) {
|
||||
fd = open ("/dev/mixer", O_RDONLY | O_NONBLOCK, 0);
|
||||
if (fd < 0)
|
||||
goto open_failed;
|
||||
else if (!gst_oss4_audio_check_version (GST_OBJECT (probe), fd))
|
||||
goto legacy_oss;
|
||||
}
|
||||
|
||||
if (ioctl (fd, SNDCTL_SYSINFO, &si) == -1)
|
||||
goto no_sysinfo;
|
||||
|
||||
if (cap_mask != 0) {
|
||||
devices =
|
||||
gst_oss4_property_probe_get_audio_devices (obj, fd, &si, cap_mask);
|
||||
} else {
|
||||
devices = gst_oss4_property_probe_get_mixer_devices (obj, fd, &si);
|
||||
}
|
||||
|
||||
if (devices == NULL) {
|
||||
GST_DEBUG_OBJECT (obj, "No devices found");
|
||||
goto done;
|
||||
}
|
||||
|
||||
array = g_value_array_new (1);
|
||||
|
||||
for (l = devices; l != NULL; l = l->next) {
|
||||
GValue val = { 0, };
|
||||
|
||||
g_value_init (&val, G_TYPE_STRING);
|
||||
g_value_take_string (&val, (gchar *) l->data);
|
||||
l->data = NULL;
|
||||
g_value_array_append (array, &val);
|
||||
g_value_unset (&val);
|
||||
}
|
||||
|
||||
GST_OBJECT_UNLOCK (obj);
|
||||
|
||||
g_list_free (devices);
|
||||
|
||||
done:
|
||||
|
||||
close (fd);
|
||||
|
||||
return array;
|
||||
|
||||
/* ERRORS */
|
||||
open_failed:
|
||||
{
|
||||
GST_OBJECT_UNLOCK (GST_OBJECT (probe));
|
||||
GST_WARNING_OBJECT (probe, "Can't open file descriptor to probe "
|
||||
"available devices: %s", g_strerror (errno));
|
||||
return NULL;
|
||||
}
|
||||
legacy_oss:
|
||||
{
|
||||
close (fd);
|
||||
GST_OBJECT_UNLOCK (GST_OBJECT (probe));
|
||||
GST_DEBUG_OBJECT (probe, "Legacy OSS (ie. not OSSv4), not supported");
|
||||
return NULL;
|
||||
}
|
||||
no_sysinfo:
|
||||
{
|
||||
close (fd);
|
||||
GST_OBJECT_UNLOCK (GST_OBJECT (probe));
|
||||
GST_WARNING_OBJECT (probe, "Can't open file descriptor to probe "
|
||||
"available devices: %s", g_strerror (errno));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_property_probe_interface_init (GstPropertyProbeInterface * iface)
|
||||
{
|
||||
iface->get_properties = gst_oss4_property_probe_get_properties;
|
||||
iface->probe_property = gst_oss4_property_probe_probe_property;
|
||||
iface->needs_probe = gst_oss4_property_probe_needs_probe;
|
||||
iface->get_values = gst_oss4_property_probe_get_values;
|
||||
}
|
||||
|
||||
void
|
||||
gst_oss4_add_property_probe_interface (GType type)
|
||||
{
|
||||
static const GInterfaceInfo probe_iface_info = {
|
||||
(GInterfaceInitFunc) gst_oss4_property_probe_interface_init,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE,
|
||||
&probe_iface_info);
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/* GStreamer OSS4 audio property probe interface implementation
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef GST_OSS4_PROPERTY_PROBE_H
|
||||
#define GST_OSS4_PROPERTY_PROBE_H
|
||||
|
||||
#include <gst/interfaces/propertyprobe.h>
|
||||
|
||||
void gst_oss4_add_property_probe_interface (GType type);
|
||||
|
||||
gboolean gst_oss4_property_probe_find_device_name (GstObject * obj,
|
||||
int fd,
|
||||
const gchar * device_handle,
|
||||
gchar ** device_name);
|
||||
|
||||
gboolean gst_oss4_property_probe_find_device_name_nofd (GstObject * obj,
|
||||
const gchar * device_handle,
|
||||
gchar ** device_name);
|
||||
|
||||
#endif /* GST_OSS4_PROPERTY_PROBE_H */
|
||||
|
||||
|
|
@ -1,699 +0,0 @@
|
|||
/* GStreamer OSS4 audio sink
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
* SECTION:element-oss4sink
|
||||
*
|
||||
* This element lets you output sound using the Open Sound System (OSS)
|
||||
* version 4.
|
||||
*
|
||||
* Note that you should almost always use generic audio conversion elements
|
||||
* like audioconvert and audioresample in front of an audiosink to make sure
|
||||
* your pipeline works under all circumstances (those conversion elements will
|
||||
* act in passthrough-mode if no conversion is necessary).
|
||||
*
|
||||
* <refsect2>
|
||||
* <title>Example pipelines</title>
|
||||
* |[
|
||||
* gst-launch -v audiotestsrc ! audioconvert ! volume volume=0.1 ! oss4sink
|
||||
* ]| will output a sine wave (continuous beep sound) to your sound card (with
|
||||
* a very low volume as precaution).
|
||||
* |[
|
||||
* gst-launch -v filesrc location=music.ogg ! decodebin ! audioconvert ! audioresample ! oss4sink
|
||||
* ]| will play an Ogg/Vorbis audio file and output it using the Open Sound System
|
||||
* version 4.
|
||||
* </refsect2>
|
||||
*
|
||||
* Since: 0.10.7
|
||||
*/
|
||||
|
||||
/* TODO: - add "volume" property for stream volume control and intercept tags
|
||||
* to set stream title
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gst/gst-i18n-plugin.h>
|
||||
#include <gst/interfaces/streamvolume.h>
|
||||
|
||||
#define NO_LEGACY_MIXER
|
||||
#include "oss4-audio.h"
|
||||
#include "oss4-sink.h"
|
||||
#include "oss4-property-probe.h"
|
||||
#include "oss4-soundcard.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (oss4sink_debug);
|
||||
#define GST_CAT_DEFAULT oss4sink_debug
|
||||
|
||||
static void gst_oss4_sink_init_interfaces (GType type);
|
||||
static void gst_oss4_sink_dispose (GObject * object);
|
||||
static void gst_oss4_sink_finalise (GObject * object);
|
||||
|
||||
static void gst_oss4_sink_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
static void gst_oss4_sink_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
|
||||
static GstCaps *gst_oss4_sink_getcaps (GstBaseSink * bsink);
|
||||
static gboolean gst_oss4_sink_open (GstAudioSink * asink,
|
||||
gboolean silent_errors);
|
||||
static gboolean gst_oss4_sink_open_func (GstAudioSink * asink);
|
||||
static gboolean gst_oss4_sink_close (GstAudioSink * asink);
|
||||
static gboolean gst_oss4_sink_prepare (GstAudioSink * asink,
|
||||
GstRingBufferSpec * spec);
|
||||
static gboolean gst_oss4_sink_unprepare (GstAudioSink * asink);
|
||||
static guint gst_oss4_sink_write (GstAudioSink * asink, gpointer data,
|
||||
guint length);
|
||||
static guint gst_oss4_sink_delay (GstAudioSink * asink);
|
||||
static void gst_oss4_sink_reset (GstAudioSink * asink);
|
||||
|
||||
#define DEFAULT_DEVICE NULL
|
||||
#define DEFAULT_DEVICE_NAME NULL
|
||||
#define DEFAULT_MUTE FALSE
|
||||
#define DEFAULT_VOLUME 1.0
|
||||
#define MAX_VOLUME 10.0
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_DEVICE,
|
||||
PROP_DEVICE_NAME,
|
||||
PROP_VOLUME,
|
||||
PROP_MUTE,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
GST_BOILERPLATE_FULL (GstOss4Sink, gst_oss4_sink, GstAudioSink,
|
||||
GST_TYPE_AUDIO_SINK, gst_oss4_sink_init_interfaces);
|
||||
|
||||
static void
|
||||
gst_oss4_sink_dispose (GObject * object)
|
||||
{
|
||||
GstOss4Sink *osssink = GST_OSS4_SINK (object);
|
||||
|
||||
if (osssink->probed_caps) {
|
||||
gst_caps_unref (osssink->probed_caps);
|
||||
osssink->probed_caps = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_sink_base_init (gpointer g_class)
|
||||
{
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
|
||||
GstPadTemplate *templ;
|
||||
|
||||
gst_element_class_set_details_simple (element_class,
|
||||
"OSS v4 Audio Sink", "Sink/Audio",
|
||||
"Output to a sound card via OSS version 4",
|
||||
"Tim-Philipp Müller <tim centricular net>");
|
||||
|
||||
templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
|
||||
gst_oss4_audio_get_template_caps ());
|
||||
gst_element_class_add_pad_template (element_class, templ);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_sink_class_init (GstOss4SinkClass * klass)
|
||||
{
|
||||
GstAudioSinkClass *audiosink_class = (GstAudioSinkClass *) klass;
|
||||
GstBaseSinkClass *basesink_class = (GstBaseSinkClass *) klass;
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
|
||||
gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_oss4_sink_dispose);
|
||||
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_oss4_sink_finalise);
|
||||
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_oss4_sink_get_property);
|
||||
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_oss4_sink_set_property);
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_DEVICE,
|
||||
g_param_spec_string ("device", "Device",
|
||||
"OSS4 device (e.g. /dev/oss/hdaudio0/pcm0 or /dev/dspN) "
|
||||
"(NULL = use first available playback device)",
|
||||
DEFAULT_DEVICE, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
|
||||
g_param_spec_string ("device-name", "Device name",
|
||||
"Human-readable name of the sound device", DEFAULT_DEVICE_NAME,
|
||||
G_PARAM_READABLE));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_VOLUME,
|
||||
g_param_spec_double ("volume", "Volume",
|
||||
"Linear volume of this stream, 1.0=100%", 0.0, MAX_VOLUME,
|
||||
DEFAULT_VOLUME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_MUTE,
|
||||
g_param_spec_boolean ("mute", "Mute",
|
||||
"Mute state of this stream", DEFAULT_MUTE,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_oss4_sink_getcaps);
|
||||
|
||||
audiosink_class->open = GST_DEBUG_FUNCPTR (gst_oss4_sink_open_func);
|
||||
audiosink_class->close = GST_DEBUG_FUNCPTR (gst_oss4_sink_close);
|
||||
audiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_oss4_sink_prepare);
|
||||
audiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_oss4_sink_unprepare);
|
||||
audiosink_class->write = GST_DEBUG_FUNCPTR (gst_oss4_sink_write);
|
||||
audiosink_class->delay = GST_DEBUG_FUNCPTR (gst_oss4_sink_delay);
|
||||
audiosink_class->reset = GST_DEBUG_FUNCPTR (gst_oss4_sink_reset);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_sink_init (GstOss4Sink * osssink, GstOss4SinkClass * klass)
|
||||
{
|
||||
const gchar *device;
|
||||
|
||||
device = g_getenv ("AUDIODEV");
|
||||
if (device == NULL)
|
||||
device = DEFAULT_DEVICE;
|
||||
osssink->device = g_strdup (device);
|
||||
osssink->fd = -1;
|
||||
osssink->bytes_per_sample = 0;
|
||||
osssink->probed_caps = NULL;
|
||||
osssink->device_name = NULL;
|
||||
osssink->mute_volume = 100 | (100 << 8);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_sink_finalise (GObject * object)
|
||||
{
|
||||
GstOss4Sink *osssink = GST_OSS4_SINK (object);
|
||||
|
||||
g_free (osssink->device);
|
||||
osssink->device = NULL;
|
||||
|
||||
g_list_free (osssink->property_probe_list);
|
||||
osssink->property_probe_list = NULL;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_sink_set_volume (GstOss4Sink * oss, gdouble volume)
|
||||
{
|
||||
int ivol;
|
||||
|
||||
volume = volume * 100.0;
|
||||
ivol = (int) volume | ((int) volume << 8);
|
||||
GST_OBJECT_LOCK (oss);
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETPLAYVOL, &ivol) < 0) {
|
||||
GST_LOG_OBJECT (oss, "SETPLAYVOL failed");
|
||||
}
|
||||
GST_OBJECT_UNLOCK (oss);
|
||||
}
|
||||
|
||||
static gdouble
|
||||
gst_oss4_sink_get_volume (GstOss4Sink * oss)
|
||||
{
|
||||
int ivol, lvol, rvol;
|
||||
gdouble dvol = DEFAULT_VOLUME;
|
||||
|
||||
GST_OBJECT_LOCK (oss);
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_GETPLAYVOL, &ivol) < 0) {
|
||||
GST_LOG_OBJECT (oss, "GETPLAYVOL failed");
|
||||
} else {
|
||||
/* Return the higher of the two volume channels, if different */
|
||||
lvol = ivol & 0xff;
|
||||
rvol = (ivol >> 8) & 0xff;
|
||||
dvol = MAX (lvol, rvol) / 100.0;
|
||||
}
|
||||
GST_OBJECT_UNLOCK (oss);
|
||||
|
||||
return dvol;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_sink_set_mute (GstOss4Sink * oss, gboolean mute)
|
||||
{
|
||||
int ivol;
|
||||
|
||||
if (mute) {
|
||||
/*
|
||||
* OSSv4 does not have a per-channel mute, so simulate by setting
|
||||
* the value to 0. Save the volume before doing a mute so we can
|
||||
* reset the value when the user un-mutes.
|
||||
*/
|
||||
ivol = 0;
|
||||
|
||||
GST_OBJECT_LOCK (oss);
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_GETPLAYVOL, &oss->mute_volume) < 0) {
|
||||
GST_LOG_OBJECT (oss, "GETPLAYVOL failed");
|
||||
}
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETPLAYVOL, &ivol) < 0) {
|
||||
GST_LOG_OBJECT (oss, "SETPLAYVOL failed");
|
||||
}
|
||||
GST_OBJECT_UNLOCK (oss);
|
||||
} else {
|
||||
/*
|
||||
* If the saved volume is 0, then reset it to 100. Otherwise the mute
|
||||
* can get stuck. This can happen, for example, due to rounding
|
||||
* errors in converting from the float to an integer.
|
||||
*/
|
||||
if (oss->mute_volume == 0) {
|
||||
oss->mute_volume = 100 | (100 << 8);
|
||||
}
|
||||
GST_OBJECT_LOCK (oss);
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETPLAYVOL, &oss->mute_volume) < 0) {
|
||||
GST_LOG_OBJECT (oss, "SETPLAYVOL failed");
|
||||
}
|
||||
GST_OBJECT_UNLOCK (oss);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_oss4_sink_get_mute (GstOss4Sink * oss)
|
||||
{
|
||||
int ivol, lvol, rvol;
|
||||
|
||||
GST_OBJECT_LOCK (oss);
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_GETPLAYVOL, &ivol) < 0) {
|
||||
GST_LOG_OBJECT (oss, "GETPLAYVOL failed");
|
||||
lvol = rvol = 100;
|
||||
} else {
|
||||
lvol = ivol & 0xff;
|
||||
rvol = (ivol >> 8) & 0xff;
|
||||
}
|
||||
GST_OBJECT_UNLOCK (oss);
|
||||
|
||||
return (lvol == 0 && rvol == 0);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_sink_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstOss4Sink *oss = GST_OSS4_SINK (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DEVICE:
|
||||
GST_OBJECT_LOCK (oss);
|
||||
if (oss->fd == -1) {
|
||||
g_free (oss->device);
|
||||
oss->device = g_value_dup_string (value);
|
||||
if (oss->probed_caps) {
|
||||
gst_caps_unref (oss->probed_caps);
|
||||
oss->probed_caps = NULL;
|
||||
}
|
||||
g_free (oss->device_name);
|
||||
oss->device_name = NULL;
|
||||
} else {
|
||||
g_warning ("%s: can't change \"device\" property while audio sink "
|
||||
"is open", GST_OBJECT_NAME (oss));
|
||||
}
|
||||
GST_OBJECT_UNLOCK (oss);
|
||||
break;
|
||||
case PROP_VOLUME:
|
||||
gst_oss4_sink_set_volume (oss, g_value_get_double (value));
|
||||
break;
|
||||
case PROP_MUTE:
|
||||
gst_oss4_sink_set_mute (oss, g_value_get_boolean (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_sink_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstOss4Sink *oss = GST_OSS4_SINK (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DEVICE:
|
||||
GST_OBJECT_LOCK (oss);
|
||||
g_value_set_string (value, oss->device);
|
||||
GST_OBJECT_UNLOCK (oss);
|
||||
break;
|
||||
case PROP_DEVICE_NAME:
|
||||
GST_OBJECT_LOCK (oss);
|
||||
if (oss->fd == -1 && oss->device != NULL) {
|
||||
/* If device is set, try to retrieve the name even if we're not open */
|
||||
if (gst_oss4_sink_open (GST_AUDIO_SINK (oss), TRUE)) {
|
||||
g_value_set_string (value, oss->device_name);
|
||||
gst_oss4_sink_close (GST_AUDIO_SINK (oss));
|
||||
} else {
|
||||
gchar *name = NULL;
|
||||
|
||||
gst_oss4_property_probe_find_device_name_nofd (GST_OBJECT (oss),
|
||||
oss->device, &name);
|
||||
g_value_set_string (value, name);
|
||||
g_free (name);
|
||||
}
|
||||
} else {
|
||||
g_value_set_string (value, oss->device_name);
|
||||
}
|
||||
GST_OBJECT_UNLOCK (oss);
|
||||
break;
|
||||
case PROP_VOLUME:
|
||||
g_value_set_double (value, gst_oss4_sink_get_volume (oss));
|
||||
break;
|
||||
case PROP_MUTE:
|
||||
g_value_set_boolean (value, gst_oss4_sink_get_mute (oss));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_oss4_sink_getcaps (GstBaseSink * bsink)
|
||||
{
|
||||
GstOss4Sink *oss;
|
||||
GstCaps *caps;
|
||||
|
||||
oss = GST_OSS4_SINK (bsink);
|
||||
|
||||
if (oss->fd == -1) {
|
||||
caps = gst_caps_copy (gst_oss4_audio_get_template_caps ());
|
||||
} else if (oss->probed_caps) {
|
||||
caps = gst_caps_copy (oss->probed_caps);
|
||||
} else {
|
||||
caps = gst_oss4_audio_probe_caps (GST_OBJECT (oss), oss->fd);
|
||||
if (caps != NULL && !gst_caps_is_empty (caps)) {
|
||||
oss->probed_caps = gst_caps_copy (caps);
|
||||
}
|
||||
}
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
/* note: we must not take the object lock here unless we fix up get_property */
|
||||
static gboolean
|
||||
gst_oss4_sink_open (GstAudioSink * asink, gboolean silent_errors)
|
||||
{
|
||||
GstOss4Sink *oss;
|
||||
gchar *device;
|
||||
int mode;
|
||||
|
||||
oss = GST_OSS4_SINK (asink);
|
||||
|
||||
if (oss->device)
|
||||
device = g_strdup (oss->device);
|
||||
else
|
||||
device = gst_oss4_audio_find_device (GST_OBJECT_CAST (oss));
|
||||
|
||||
/* desperate times, desperate measures */
|
||||
if (device == NULL)
|
||||
device = g_strdup ("/dev/dsp0");
|
||||
|
||||
GST_INFO_OBJECT (oss, "Trying to open OSS4 device '%s'", device);
|
||||
|
||||
/* we open in non-blocking mode even if we don't really want to do writes
|
||||
* non-blocking because we can't be sure that this is really a genuine
|
||||
* OSS4 device with well-behaved drivers etc. We really don't want to
|
||||
* hang forever under any circumstances. */
|
||||
oss->fd = open (device, O_WRONLY | O_NONBLOCK, 0);
|
||||
if (oss->fd == -1) {
|
||||
switch (errno) {
|
||||
case EBUSY:
|
||||
goto busy;
|
||||
case EACCES:
|
||||
goto no_permission;
|
||||
default:
|
||||
goto open_failed;
|
||||
}
|
||||
}
|
||||
|
||||
GST_INFO_OBJECT (oss, "Opened device '%s'", device);
|
||||
|
||||
/* Make sure it's OSS4. If it's old OSS, let osssink handle it */
|
||||
if (!gst_oss4_audio_check_version (GST_OBJECT_CAST (oss), oss->fd))
|
||||
goto legacy_oss;
|
||||
|
||||
/* now remove the non-blocking flag. */
|
||||
mode = fcntl (oss->fd, F_GETFL);
|
||||
mode &= ~O_NONBLOCK;
|
||||
if (fcntl (oss->fd, F_SETFL, mode) < 0) {
|
||||
/* some drivers do no support unsetting the non-blocking flag, try to
|
||||
* close/open the device then. This is racy but we error out properly. */
|
||||
GST_WARNING_OBJECT (oss, "failed to unset O_NONBLOCK (buggy driver?), "
|
||||
"will try to re-open device now");
|
||||
gst_oss4_sink_close (asink);
|
||||
if ((oss->fd = open (device, O_WRONLY, 0)) == -1)
|
||||
goto non_block;
|
||||
}
|
||||
|
||||
oss->open_device = device;
|
||||
|
||||
/* not using ENGINEINFO here because it sometimes returns a different and
|
||||
* less useful name than AUDIOINFO for the same device */
|
||||
if (!gst_oss4_property_probe_find_device_name (GST_OBJECT (oss), oss->fd,
|
||||
oss->open_device, &oss->device_name)) {
|
||||
oss->device_name = NULL;
|
||||
}
|
||||
|
||||
/* list output routings, for informational purposes only so far */
|
||||
{
|
||||
oss_mixer_enuminfo routings = { 0, };
|
||||
guint i;
|
||||
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_GET_PLAYTGT_NAMES, &routings) != -1) {
|
||||
GST_LOG_OBJECT (oss, "%u output routings (static list: %d)",
|
||||
routings.nvalues, !!(routings.version == 0));
|
||||
for (i = 0; i < routings.nvalues; ++i) {
|
||||
GST_LOG_OBJECT (oss, " output routing %d: %s", i,
|
||||
&routings.strings[routings.strindex[i]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
busy:
|
||||
{
|
||||
if (!silent_errors) {
|
||||
GST_ELEMENT_ERROR (oss, RESOURCE, BUSY,
|
||||
(_("Could not open audio device for playback. "
|
||||
"Device is being used by another application.")), (NULL));
|
||||
}
|
||||
g_free (device);
|
||||
return FALSE;
|
||||
}
|
||||
no_permission:
|
||||
{
|
||||
if (!silent_errors) {
|
||||
GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_WRITE,
|
||||
(_("Could not open audio device for playback. "
|
||||
"You don't have permission to open the device.")),
|
||||
GST_ERROR_SYSTEM);
|
||||
}
|
||||
g_free (device);
|
||||
return FALSE;
|
||||
}
|
||||
open_failed:
|
||||
{
|
||||
if (!silent_errors) {
|
||||
GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_WRITE,
|
||||
(_("Could not open audio device for playback.")), GST_ERROR_SYSTEM);
|
||||
}
|
||||
g_free (device);
|
||||
return FALSE;
|
||||
}
|
||||
legacy_oss:
|
||||
{
|
||||
if (!silent_errors) {
|
||||
GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_WRITE,
|
||||
(_("Could not open audio device for playback. "
|
||||
"This version of the Open Sound System is not supported by this "
|
||||
"element.")), ("Try the 'osssink' element instead"));
|
||||
}
|
||||
gst_oss4_sink_close (asink);
|
||||
g_free (device);
|
||||
return FALSE;
|
||||
}
|
||||
non_block:
|
||||
{
|
||||
if (!silent_errors) {
|
||||
GST_ELEMENT_ERROR (oss, RESOURCE, SETTINGS, (NULL),
|
||||
("Unable to set device %s into non-blocking mode: %s",
|
||||
oss->device, g_strerror (errno)));
|
||||
}
|
||||
g_free (device);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_oss4_sink_open_func (GstAudioSink * asink)
|
||||
{
|
||||
return gst_oss4_sink_open (asink, FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_oss4_sink_close (GstAudioSink * asink)
|
||||
{
|
||||
GstOss4Sink *oss = GST_OSS4_SINK (asink);
|
||||
|
||||
if (oss->fd != -1) {
|
||||
GST_DEBUG_OBJECT (oss, "closing device");
|
||||
close (oss->fd);
|
||||
oss->fd = -1;
|
||||
}
|
||||
|
||||
oss->bytes_per_sample = 0;
|
||||
/* we keep the probed caps cached, at least until the device changes */
|
||||
|
||||
g_free (oss->open_device);
|
||||
oss->open_device = NULL;
|
||||
|
||||
g_free (oss->device_name);
|
||||
oss->device_name = NULL;
|
||||
|
||||
if (oss->probed_caps) {
|
||||
gst_caps_unref (oss->probed_caps);
|
||||
oss->probed_caps = NULL;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_oss4_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
|
||||
{
|
||||
GstOss4Sink *oss;
|
||||
|
||||
oss = GST_OSS4_SINK (asink);
|
||||
|
||||
if (!gst_oss4_audio_set_format (GST_OBJECT_CAST (oss), oss->fd, spec)) {
|
||||
GST_WARNING_OBJECT (oss, "Couldn't set requested format %" GST_PTR_FORMAT,
|
||||
spec->caps);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
oss->bytes_per_sample = spec->bytes_per_sample;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_oss4_sink_unprepare (GstAudioSink * asink)
|
||||
{
|
||||
/* could do a SNDCTL_DSP_HALT, but the OSS manual recommends a close/open,
|
||||
* since HALT won't properly reset some devices, apparently */
|
||||
|
||||
if (!gst_oss4_sink_close (asink))
|
||||
goto couldnt_close;
|
||||
|
||||
if (!gst_oss4_sink_open_func (asink))
|
||||
goto couldnt_reopen;
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
couldnt_close:
|
||||
{
|
||||
GST_DEBUG_OBJECT (asink, "Couldn't close the audio device");
|
||||
return FALSE;
|
||||
}
|
||||
couldnt_reopen:
|
||||
{
|
||||
GST_DEBUG_OBJECT (asink, "Couldn't reopen the audio device");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static guint
|
||||
gst_oss4_sink_write (GstAudioSink * asink, gpointer data, guint length)
|
||||
{
|
||||
GstOss4Sink *oss;
|
||||
int n;
|
||||
|
||||
oss = GST_OSS4_SINK_CAST (asink);
|
||||
|
||||
n = write (oss->fd, data, length);
|
||||
GST_LOG_OBJECT (asink, "wrote %d/%d samples, %d bytes",
|
||||
n / oss->bytes_per_sample, length / oss->bytes_per_sample, n);
|
||||
|
||||
if (G_UNLIKELY (n < 0)) {
|
||||
switch (errno) {
|
||||
case ENOTSUP:
|
||||
case EACCES:{
|
||||
/* This is the most likely cause, I think */
|
||||
GST_ELEMENT_ERROR (asink, RESOURCE, WRITE,
|
||||
(_("Playback is not supported by this audio device.")),
|
||||
("write: %s (device: %s) (maybe this is an input-only device?)",
|
||||
g_strerror (errno), oss->open_device));
|
||||
break;
|
||||
}
|
||||
default:{
|
||||
GST_ELEMENT_ERROR (asink, RESOURCE, WRITE,
|
||||
(_("Audio playback error.")),
|
||||
("write: %s (device: %s)", g_strerror (errno), oss->open_device));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static guint
|
||||
gst_oss4_sink_delay (GstAudioSink * asink)
|
||||
{
|
||||
GstOss4Sink *oss;
|
||||
gint delay = -1;
|
||||
|
||||
oss = GST_OSS4_SINK_CAST (asink);
|
||||
|
||||
GST_OBJECT_LOCK (oss);
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_GETODELAY, &delay) < 0 || delay < 0) {
|
||||
GST_LOG_OBJECT (oss, "GETODELAY failed");
|
||||
}
|
||||
GST_OBJECT_UNLOCK (oss);
|
||||
|
||||
if (G_UNLIKELY (delay < 0)) /* error case */
|
||||
return 0;
|
||||
|
||||
return delay / oss->bytes_per_sample;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_sink_reset (GstAudioSink * asink)
|
||||
{
|
||||
/* There's nothing we can do here really: OSS can't handle access to the
|
||||
* same device/fd from multiple threads and might deadlock or blow up in
|
||||
* other ways if we try an ioctl SNDCTL_DSP_HALT or similar */
|
||||
}
|
||||
|
||||
static void
|
||||
gst_oss4_sink_init_interfaces (GType type)
|
||||
{
|
||||
static const GInterfaceInfo svol_iface_info = {
|
||||
NULL, NULL, NULL
|
||||
};
|
||||
|
||||
g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_iface_info);
|
||||
|
||||
gst_oss4_add_property_probe_interface (type);
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/* GStreamer OSS4 audio sink
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef GST_OSS4_SINK_H
|
||||
#define GST_OSS4_SINK_H
|
||||
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/gstaudiosink.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_OSS4_SINK (gst_oss4_sink_get_type())
|
||||
#define GST_OSS4_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OSS4_SINK,GstOss4Sink))
|
||||
#define GST_OSS4_SINK_CAST(obj) ((GstOss4Sink *)(obj))
|
||||
#define GST_OSS4_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSS4_SINK,GstOss4SinkClass))
|
||||
#define GST_IS_OSS4_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OSS4_SINK))
|
||||
#define GST_IS_OSS4_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OSS4_SINK))
|
||||
|
||||
typedef struct _GstOss4Sink GstOss4Sink;
|
||||
typedef struct _GstOss4SinkClass GstOss4SinkClass;
|
||||
|
||||
struct _GstOss4Sink {
|
||||
GstAudioSink audio_sink;
|
||||
|
||||
gchar * device; /* NULL if none was set */
|
||||
gchar * open_device; /* the device we opened */
|
||||
gchar * device_name; /* set if the device is open */
|
||||
gint fd; /* -1 if not open */
|
||||
gint bytes_per_sample;
|
||||
gint mute_volume;
|
||||
|
||||
GstCaps * probed_caps;
|
||||
|
||||
GList * property_probe_list;
|
||||
};
|
||||
|
||||
struct _GstOss4SinkClass {
|
||||
GstAudioSinkClass audio_sink_class;
|
||||
};
|
||||
|
||||
GType gst_oss4_sink_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* GST_OSS4_SINK_H */
|
||||
|
||||
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,89 +0,0 @@
|
|||
/* GStreamer OSS4 audio source
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef GST_OSS4_SOURCE_H
|
||||
#define GST_OSS4_SOURCE_H
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/gstaudiosrc.h>
|
||||
#include <gst/interfaces/mixertrack.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_OSS4_SOURCE (gst_oss4_source_get_type())
|
||||
#define GST_OSS4_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OSS4_SOURCE,GstOss4Source))
|
||||
#define GST_OSS4_SOURCE_CAST(obj) ((GstOss4Source *)(obj))
|
||||
#define GST_OSS4_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSS4_SOURCE,GstOss4SourceClass))
|
||||
#define GST_IS_OSS4_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OSS4_SOURCE))
|
||||
#define GST_IS_OSS4_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OSS4_SOURCE))
|
||||
|
||||
typedef struct _GstOss4Source GstOss4Source;
|
||||
typedef struct _GstOss4SourceClass GstOss4SourceClass;
|
||||
|
||||
struct _GstOss4Source {
|
||||
GstAudioSrc audiosrc;
|
||||
|
||||
gchar * device; /* NULL if none was set */
|
||||
gchar * open_device; /* the device we opened */
|
||||
gchar * device_name; /* set if the device is open */
|
||||
gint fd; /* -1 if not open */
|
||||
gint bytes_per_sample;
|
||||
|
||||
GstCaps * probed_caps;
|
||||
|
||||
/* property probe interface */
|
||||
GList * property_probe_list;
|
||||
|
||||
/* mixer interface */
|
||||
GList * tracks;
|
||||
gboolean tracks_static; /* FALSE if the list of inputs may change */
|
||||
};
|
||||
|
||||
struct _GstOss4SourceClass {
|
||||
GstAudioSrcClass audiosrc_class;
|
||||
};
|
||||
|
||||
GType gst_oss4_source_get_type (void);
|
||||
|
||||
/* our mixer track for input selection */
|
||||
#define GST_TYPE_OSS4_SOURCE_INPUT (gst_oss4_source_input_get_type())
|
||||
#define GST_OSS4_SOURCE_INPUT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OSS4_SOURCE_INPUT,GstOss4SourceInput))
|
||||
#define GST_OSS4_SOURCE_INPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSS4_SOURCE_INPUT,GstOss4SourceInputClass))
|
||||
#define GST_IS_OSS4_SOURCE_INPUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OSS4_SOURCE_INPUT))
|
||||
#define GST_IS_OSS4_SOURCE_INPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OSS4_SOURCE_INPUT))
|
||||
|
||||
typedef struct _GstOss4SourceInput GstOss4SourceInput;
|
||||
typedef struct _GstOss4SourceInputClass GstOss4SourceInputClass;
|
||||
|
||||
struct _GstOss4SourceInput {
|
||||
GstMixerTrack mixer_track;
|
||||
|
||||
int route; /* number for SNDCTL_DSP_SET_RECSRC etc. */
|
||||
};
|
||||
|
||||
struct _GstOss4SourceInputClass {
|
||||
GstMixerTrackClass mixer_track_class;
|
||||
};
|
||||
|
||||
GType gst_oss4_source_input_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* GST_OSS4_SOURCE_H */
|
||||
|
1
tests/icles/.gitignore
vendored
1
tests/icles/.gitignore
vendored
|
@ -1,7 +1,6 @@
|
|||
equalizer-test
|
||||
metadata_editor
|
||||
output-selector-test
|
||||
test-oss4
|
||||
pitch-test
|
||||
cog-test
|
||||
cog-test.c
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
if USE_OSS4
|
||||
OSS4_TESTS=test-oss4
|
||||
else
|
||||
OSS4_TESTS=
|
||||
endif
|
||||
|
||||
if USE_SOUNDTOUCH
|
||||
|
||||
GST_SOUNDTOUCH_TESTS = pitch-test
|
||||
|
@ -57,18 +51,12 @@ equalizer_test_CFLAGS = $(GST_CFLAGS)
|
|||
equalizer_test_LDADD = $(GST_LIBS)
|
||||
equalizer_test_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||
|
||||
test_oss4_SOURCES = test-oss4.c
|
||||
test_oss4_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
|
||||
test_oss4_LDADD = $(GST_PLUGINS_BASE_LIBS) -lgstinterfaces-0.10 $(GST_LIBS)
|
||||
test_oss4_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||
|
||||
output_selector_test_SOURCES = output-selector-test.c
|
||||
output_selector_test_CFLAGS = $(GST_CFLAGS)
|
||||
output_selector_test_LDADD = $(GST_LIBS)
|
||||
output_selector_test_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||
|
||||
|
||||
noinst_PROGRAMS = $(GST_SOUNDTOUCH_TESTS) $(GST_METADATA_TESTS) $(OSS4_TESTS) \
|
||||
$(COG_TESTS) \
|
||||
noinst_PROGRAMS = $(GST_SOUNDTOUCH_TESTS) $(GST_METADATA_TESTS) $(COG_TESTS) \
|
||||
equalizer-test output-selector-test
|
||||
|
||||
|
|
|
@ -1,253 +0,0 @@
|
|||
/* GStreamer OSS4 audio tests
|
||||
* 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/interfaces/propertyprobe.h>
|
||||
#include <gst/interfaces/mixer.h>
|
||||
|
||||
static gboolean opt_show_mixer_messages = FALSE;
|
||||
|
||||
#define WAIT_TIME 60.0 /* in seconds */
|
||||
|
||||
static void
|
||||
show_mixer_messages (GstElement * element)
|
||||
{
|
||||
GstMessage *msg;
|
||||
GstBus *bus;
|
||||
GTimer *t;
|
||||
|
||||
t = g_timer_new ();
|
||||
|
||||
bus = gst_bus_new ();
|
||||
gst_element_set_bus (element, bus);
|
||||
|
||||
g_print ("\nShowing mixer messages for %u seconds ...\n", (guint) WAIT_TIME);
|
||||
|
||||
while (g_timer_elapsed (t, NULL) < WAIT_TIME) {
|
||||
gdouble remaining = WAIT_TIME - g_timer_elapsed (t, NULL);
|
||||
gint64 maxwait =
|
||||
GST_SECOND * gst_util_gdouble_to_guint64 (MAX (0.0, remaining));
|
||||
gchar *s = NULL;
|
||||
|
||||
msg = gst_bus_timed_pop (bus, maxwait);
|
||||
if (!msg)
|
||||
break;
|
||||
|
||||
if (msg->structure)
|
||||
s = gst_structure_to_string (msg->structure);
|
||||
g_print ("%s message: %s\n", GST_MESSAGE_TYPE_NAME (msg), s);
|
||||
gst_message_unref (msg);
|
||||
g_free (s);
|
||||
}
|
||||
|
||||
gst_element_set_bus (element, NULL);
|
||||
gst_object_unref (bus);
|
||||
g_timer_destroy (t);
|
||||
}
|
||||
|
||||
static void
|
||||
probe_mixer_tracks (GstElement * element)
|
||||
{
|
||||
const GList *tracks, *t;
|
||||
GstMixer *mixer;
|
||||
guint count;
|
||||
|
||||
if (!GST_IS_MIXER (element))
|
||||
return;
|
||||
|
||||
mixer = GST_MIXER (element);
|
||||
tracks = gst_mixer_list_tracks (mixer);
|
||||
count = g_list_length ((GList *) tracks);
|
||||
g_print (" %d mixer tracks%c\n", count, (count == 0) ? '.' : ':');
|
||||
|
||||
for (t = tracks; t != NULL; t = t->next) {
|
||||
GstMixerTrack *track;
|
||||
gchar *label = NULL;
|
||||
guint flags = 0;
|
||||
|
||||
track = GST_MIXER_TRACK (t->data);
|
||||
g_object_get (track, "label", &label, "flags", &flags, NULL);
|
||||
|
||||
if (GST_IS_MIXER_OPTIONS (track)) {
|
||||
GString *s;
|
||||
GList *vals, *v;
|
||||
|
||||
vals = gst_mixer_options_get_values (GST_MIXER_OPTIONS (track));
|
||||
s = g_string_new ("options: ");
|
||||
for (v = vals; v != NULL; v = v->next) {
|
||||
if (v->prev != NULL)
|
||||
g_string_append (s, ", ");
|
||||
g_string_append (s, (const gchar *) v->data);
|
||||
}
|
||||
|
||||
g_print (" [%s] flags=0x%08x, %s\n", label, flags, s->str);
|
||||
g_string_free (s, TRUE);
|
||||
} else if (track->num_channels == 0) {
|
||||
g_print (" [%s] flags=0x%08x, switch\n", label, flags);
|
||||
} else if (track->num_channels > 0) {
|
||||
g_print (" [%s] flags=0x%08x, slider (%d channels)\n", label, flags,
|
||||
track->num_channels);
|
||||
} else {
|
||||
g_print (" [%s] flags=0x%08x, UNKNOWN TYPE\n", label, flags);
|
||||
}
|
||||
|
||||
g_free (label);
|
||||
}
|
||||
|
||||
/* for testing the mixer watch thread / auto-notifications */
|
||||
if (strstr (GST_ELEMENT_NAME (element), "mixer") != NULL &&
|
||||
opt_show_mixer_messages) {
|
||||
show_mixer_messages (element);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
probe_pad (GstElement * element, const gchar * pad_name)
|
||||
{
|
||||
GstCaps *caps = NULL;
|
||||
GstPad *pad;
|
||||
guint i;
|
||||
|
||||
pad = gst_element_get_static_pad (element, pad_name);
|
||||
if (pad == NULL)
|
||||
return;
|
||||
|
||||
caps = gst_pad_get_caps (pad);
|
||||
g_return_if_fail (caps != NULL);
|
||||
|
||||
for (i = 0; i < gst_caps_get_size (caps); ++i) {
|
||||
gchar *s;
|
||||
|
||||
s = gst_structure_to_string (gst_caps_get_structure (caps, i));
|
||||
g_print (" %4s[%d]: %s\n", GST_PAD_NAME (pad), i, s);
|
||||
g_free (s);
|
||||
}
|
||||
gst_caps_unref (caps);
|
||||
gst_object_unref (pad);
|
||||
}
|
||||
|
||||
static void
|
||||
probe_details (GstElement * element)
|
||||
{
|
||||
GstStateChangeReturn ret;
|
||||
|
||||
ret = gst_element_set_state (element, GST_STATE_READY);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE) {
|
||||
g_print ("Could not set element %s to READY.", GST_ELEMENT_NAME (element));
|
||||
return;
|
||||
}
|
||||
|
||||
probe_pad (element, "sink");
|
||||
probe_pad (element, "src");
|
||||
|
||||
probe_mixer_tracks (element);
|
||||
|
||||
gst_element_set_state (element, GST_STATE_NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
probe_element (const gchar * name)
|
||||
{
|
||||
GstPropertyProbe *probe;
|
||||
GValueArray *arr;
|
||||
GstElement *element;
|
||||
gchar *devname = NULL;
|
||||
gint i;
|
||||
|
||||
element = gst_element_factory_make (name, name);
|
||||
|
||||
/* make sure we don't deadlock on GST_ELEMENT_ERROR or do other silly things
|
||||
* if we try to query the "device-name" property when the device isn't open */
|
||||
g_object_set (element, "device", "/dev/does/not/exist", NULL);
|
||||
g_object_get (element, "device-name", &devname, NULL);
|
||||
GST_LOG ("devname: '%s'", GST_STR_NULL (devname));
|
||||
g_assert (devname == NULL || *devname == '\0');
|
||||
|
||||
/* and now for real */
|
||||
|
||||
probe = GST_PROPERTY_PROBE (element);
|
||||
arr = gst_property_probe_probe_and_get_values_name (probe, "device");
|
||||
|
||||
for (i = 0; arr != NULL && i < arr->n_values; ++i) {
|
||||
GValue *val;
|
||||
gchar *dev_name = NULL;
|
||||
|
||||
g_print ("\n");
|
||||
/* we assume the element supports getting the device-name in NULL state */
|
||||
val = g_value_array_get_nth (arr, i);
|
||||
g_object_set (element, "device", g_value_get_string (val), NULL);
|
||||
g_object_get (element, "device-name", &dev_name, NULL);
|
||||
g_print ("%-10s device[%d] = %s (%s)\n", GST_OBJECT_NAME (element),
|
||||
i, g_value_get_string (val), dev_name);
|
||||
if (strstr (dev_name, "/usb")) {
|
||||
g_print ("\n\nWARNING: going to probe USB audio device. OSS4 USB support"
|
||||
" is still\npretty shaky, so bad things may happen (e.g. kernel "
|
||||
"lockup).\nPress Control-C NOW if you don't want to continue. "
|
||||
"(waiting 5secs)\n\n");
|
||||
g_usleep (5 * G_USEC_PER_SEC);
|
||||
}
|
||||
g_free (dev_name);
|
||||
|
||||
probe_details (element);
|
||||
}
|
||||
|
||||
if (arr) {
|
||||
g_value_array_free (arr);
|
||||
}
|
||||
|
||||
gst_object_unref (element);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
GOptionEntry options[] = {
|
||||
{"show-mixer-messages", 'm', 0, G_OPTION_ARG_NONE, &opt_show_mixer_messages,
|
||||
"For mixer elements, wait 60 seconds and show any mixer messages "
|
||||
"(for debugging auto-notifications)", NULL},
|
||||
{NULL,}
|
||||
};
|
||||
GOptionContext *ctx;
|
||||
GError *err = NULL;
|
||||
|
||||
if (!g_thread_supported ())
|
||||
g_thread_init (NULL);
|
||||
|
||||
ctx = g_option_context_new ("");
|
||||
g_option_context_add_main_entries (ctx, options, NULL);
|
||||
g_option_context_add_group (ctx, gst_init_get_option_group ());
|
||||
if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
|
||||
g_print ("Error initializing: %s\n", err->message);
|
||||
exit (1);
|
||||
}
|
||||
g_option_context_free (ctx);
|
||||
|
||||
probe_element ("oss4sink");
|
||||
probe_element ("oss4src");
|
||||
probe_element ("oss4mixer");
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue