mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 12:11:13 +00:00
oss4sink: implement GstStreamVolume interface and add mute and volume properties
OSS4 supports per-stream volume control, so expose this using the right API, so that playbin2 and applications like totem can make use of it (instead of using a volume element for volume control). Fixes #614305.
This commit is contained in:
parent
5f25780b02
commit
8f12893c91
2 changed files with 135 additions and 2 deletions
|
@ -59,6 +59,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <gst/gst-i18n-plugin.h>
|
#include <gst/gst-i18n-plugin.h>
|
||||||
|
#include <gst/interfaces/streamvolume.h>
|
||||||
|
|
||||||
#define NO_LEGACY_MIXER
|
#define NO_LEGACY_MIXER
|
||||||
#include "oss4-audio.h"
|
#include "oss4-audio.h"
|
||||||
|
@ -93,12 +94,18 @@ static void gst_oss4_sink_reset (GstAudioSink * asink);
|
||||||
|
|
||||||
#define DEFAULT_DEVICE NULL
|
#define DEFAULT_DEVICE NULL
|
||||||
#define DEFAULT_DEVICE_NAME NULL
|
#define DEFAULT_DEVICE_NAME NULL
|
||||||
|
#define DEFAULT_MUTE FALSE
|
||||||
|
#define DEFAULT_VOLUME 1.0
|
||||||
|
#define MAX_VOLUME 10.0
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
PROP_0,
|
PROP_0,
|
||||||
PROP_DEVICE,
|
PROP_DEVICE,
|
||||||
PROP_DEVICE_NAME
|
PROP_DEVICE_NAME,
|
||||||
|
PROP_VOLUME,
|
||||||
|
PROP_MUTE,
|
||||||
|
PROP_LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
GST_BOILERPLATE_FULL (GstOss4Sink, gst_oss4_sink, GstAudioSink,
|
GST_BOILERPLATE_FULL (GstOss4Sink, gst_oss4_sink, GstAudioSink,
|
||||||
|
@ -156,6 +163,18 @@ gst_oss4_sink_class_init (GstOss4SinkClass * klass)
|
||||||
"Human-readable name of the sound device", DEFAULT_DEVICE_NAME,
|
"Human-readable name of the sound device", DEFAULT_DEVICE_NAME,
|
||||||
G_PARAM_READABLE));
|
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);
|
basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_oss4_sink_getcaps);
|
||||||
|
|
||||||
audiosink_class->open = GST_DEBUG_FUNCPTR (gst_oss4_sink_open_func);
|
audiosink_class->open = GST_DEBUG_FUNCPTR (gst_oss4_sink_open_func);
|
||||||
|
@ -180,6 +199,7 @@ gst_oss4_sink_init (GstOss4Sink * osssink, GstOss4SinkClass * klass)
|
||||||
osssink->bytes_per_sample = 0;
|
osssink->bytes_per_sample = 0;
|
||||||
osssink->probed_caps = NULL;
|
osssink->probed_caps = NULL;
|
||||||
osssink->device_name = NULL;
|
osssink->device_name = NULL;
|
||||||
|
osssink->mute_volume = 100 | (100 << 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -196,6 +216,96 @@ gst_oss4_sink_finalise (GObject * object)
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
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
|
static void
|
||||||
gst_oss4_sink_set_property (GObject * object, guint prop_id,
|
gst_oss4_sink_set_property (GObject * object, guint prop_id,
|
||||||
const GValue * value, GParamSpec * pspec)
|
const GValue * value, GParamSpec * pspec)
|
||||||
|
@ -220,6 +330,12 @@ gst_oss4_sink_set_property (GObject * object, guint prop_id,
|
||||||
}
|
}
|
||||||
GST_OBJECT_UNLOCK (oss);
|
GST_OBJECT_UNLOCK (oss);
|
||||||
break;
|
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:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
@ -258,6 +374,12 @@ gst_oss4_sink_get_property (GObject * object, guint prop_id,
|
||||||
}
|
}
|
||||||
GST_OBJECT_UNLOCK (oss);
|
GST_OBJECT_UNLOCK (oss);
|
||||||
break;
|
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:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
@ -544,10 +666,14 @@ gst_oss4_sink_delay (GstAudioSink * asink)
|
||||||
|
|
||||||
oss = GST_OSS4_SINK_CAST (asink);
|
oss = GST_OSS4_SINK_CAST (asink);
|
||||||
|
|
||||||
|
GST_OBJECT_LOCK (oss);
|
||||||
if (ioctl (oss->fd, SNDCTL_DSP_GETODELAY, &delay) < 0 || delay < 0) {
|
if (ioctl (oss->fd, SNDCTL_DSP_GETODELAY, &delay) < 0 || delay < 0) {
|
||||||
GST_LOG_OBJECT (oss, "GETODELAY failed");
|
GST_LOG_OBJECT (oss, "GETODELAY failed");
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
GST_OBJECT_UNLOCK (oss);
|
||||||
|
|
||||||
|
if (G_UNLIKELY (delay < 0)) /* error case */
|
||||||
|
return 0;
|
||||||
|
|
||||||
return delay / oss->bytes_per_sample;
|
return delay / oss->bytes_per_sample;
|
||||||
}
|
}
|
||||||
|
@ -563,5 +689,11 @@ gst_oss4_sink_reset (GstAudioSink * asink)
|
||||||
static void
|
static void
|
||||||
gst_oss4_sink_init_interfaces (GType type)
|
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);
|
gst_oss4_add_property_probe_interface (type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ struct _GstOss4Sink {
|
||||||
gchar * device_name; /* set if the device is open */
|
gchar * device_name; /* set if the device is open */
|
||||||
gint fd; /* -1 if not open */
|
gint fd; /* -1 if not open */
|
||||||
gint bytes_per_sample;
|
gint bytes_per_sample;
|
||||||
|
gint mute_volume;
|
||||||
|
|
||||||
GstCaps * probed_caps;
|
GstCaps * probed_caps;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue