ext/alsa/: Helper functions to add device probing via the GstPropertyProbe interface to a class.

Original commit message from CVS:
* ext/alsa/Makefile.am:
* ext/alsa/gstalsadeviceprobe.c:
* ext/alsa/gstalsadeviceprobe.h:
Helper functions to add device probing via the GstPropertyProbe
interface to a class.
* ext/alsa/gstalsamixer.h:
Comment out GST_ALSA_MIXER, it returns a struct that's not
used.
* ext/alsa/gstalsamixer.c: (gst_alsa_mixer_open):
Add some debug info.
* ext/alsa/gstalsamixerelement.c:
(gst_alsa_mixer_element_interface_supported),
(gst_implements_interface_init),
(gst_alsa_mixer_element_init_interfaces),
(gst_alsa_mixer_element_class_init),
(gst_alsa_mixer_element_finalize), (gst_alsa_mixer_element_init),
(gst_alsa_mixer_element_set_property),
(gst_alsa_mixer_element_get_property),
(gst_alsa_mixer_element_change_state):
* ext/alsa/gstalsamixerelement.h:
Add 'device' and 'device-name' properties. Add GstPropertyProbe
for device handling (gnome-volume-control will need that).
This commit is contained in:
Tim-Philipp Müller 2005-12-14 17:58:48 +00:00
parent 68232a2891
commit 534e0c268e
8 changed files with 532 additions and 21 deletions

View file

@ -1,5 +1,32 @@
2005-12-12 Christian Schaller <set EMAIL_ADDRESS environment variable>
2005-12-14 Tim-Philipp Müller <tim at centricular dot net>
* ext/alsa/Makefile.am:
* ext/alsa/gstalsadeviceprobe.c:
* ext/alsa/gstalsadeviceprobe.h:
Helper functions to add device probing via the GstPropertyProbe
interface to a class.
* ext/alsa/gstalsamixer.h:
Comment out GST_ALSA_MIXER, it returns a struct that's not
used.
* ext/alsa/gstalsamixer.c: (gst_alsa_mixer_open):
Add some debug info.
* ext/alsa/gstalsamixerelement.c:
(gst_alsa_mixer_element_interface_supported),
(gst_implements_interface_init),
(gst_alsa_mixer_element_init_interfaces),
(gst_alsa_mixer_element_class_init),
(gst_alsa_mixer_element_finalize), (gst_alsa_mixer_element_init),
(gst_alsa_mixer_element_set_property),
(gst_alsa_mixer_element_get_property),
(gst_alsa_mixer_element_change_state):
* ext/alsa/gstalsamixerelement.h:
Add 'device' and 'device-name' properties. Add GstPropertyProbe
for device handling (gnome-volume-control will need that).
2005-12-12 Christian Schaller <uraeus@gnome.org>
* ext/Makefile.am: fix cdparanoia entry
* gst-plugins-base.spec.in: add cdparanoia

View file

@ -1,11 +1,12 @@
plugin_LTLIBRARIES = libgstalsa.la
libgstalsa_la_SOURCES = \
gstalsaplugin.c \
gstalsadeviceprobe.c \
gstalsamixer.c \
gstalsamixerelement.c \
gstalsamixertrack.c \
gstalsamixeroptions.c \
gstalsaplugin.c \
gstalsasink.c \
gstalsasrc.c
@ -18,9 +19,10 @@ libgstalsa_la_LIBADD = \
noinst_HEADERS = \
gstalsa.h \
gstalsasink.h \
gstalsasrc.h \
gstalsadeviceprobe.h \
gstalsamixer.h \
gstalsamixerelement.h \
gstalsamixertrack.h \
gstalsamixeroptions.h
gstalsamixeroptions.h \
gstalsasrc.h \
gstalsasink.h

View file

@ -0,0 +1,291 @@
/* Copyright (C) 2001 CodeFactory AB
* Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se>
* Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu>
* Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
* Copyright (C) 2005 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstalsadeviceprobe.h"
#include "gst/interfaces/propertyprobe.h"
#define DATA_OFFSET_QUARK \
g_quark_from_static_string ("alsa-device-probe-data-offset-quark")
#define DEVICE_PROPID_QUARK \
g_quark_from_static_string ("alsa-device-probe-device-propid-quark")
static const GList *
gst_alsa_device_property_probe_get_properties (GstPropertyProbe * probe)
{
GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
static GList *list = NULL;
/* well, not perfect, but better than no locking at all.
* In the worst case we leak a list node, so who cares? */
GST_CLASS_LOCK (GST_OBJECT_CLASS (klass));
if (!list) {
GParamSpec *pspec;
pspec = g_object_class_find_property (klass, "device");
list = g_list_append (NULL, pspec);
}
GST_CLASS_UNLOCK (GST_OBJECT_CLASS (klass));
return list;
}
/* yes, this is evil, but hey, it works */
static gboolean
gst_alsa_device_probe_get_data (GObject * obj, GstAlsaDeviceProbeData ** p_data,
guint * p_device_propid)
{
gpointer klass;
guint devpropid;
guint offset;
GType type;
type = G_TYPE_FROM_INSTANCE (obj);
/* in case this is a derived class ... */
while (g_type_get_qdata (type, DATA_OFFSET_QUARK) == NULL) {
type = g_type_parent (type);
g_return_val_if_fail (G_TYPE_IS_FUNDAMENTAL (type) == FALSE, FALSE);
}
offset = GPOINTER_TO_UINT (g_type_get_qdata (type, DATA_OFFSET_QUARK));
devpropid = GPOINTER_TO_UINT (g_type_get_qdata (type, DEVICE_PROPID_QUARK));
g_return_val_if_fail (offset != 0, FALSE);
g_return_val_if_fail (devpropid != 0, FALSE);
klass = G_OBJECT_GET_CLASS (obj);
*p_data = (GstAlsaDeviceProbeData *) G_STRUCT_MEMBER_P (klass, offset);
*p_device_propid = devpropid;
return TRUE;
}
static void
gst_alsa_add_device_list (GstAlsaDeviceProbeData * probe_data,
snd_pcm_stream_t stream)
{
snd_ctl_t *handle;
int card, err, dev;
snd_ctl_card_info_t *info;
snd_pcm_info_t *pcminfo;
gboolean mixer = (stream == -1);
if (stream == -1)
stream = 0;
snd_ctl_card_info_alloca (&info);
snd_pcm_info_alloca (&pcminfo);
card = -1;
if (snd_card_next (&card) < 0 || card < 0) {
/* no soundcard found */
return;
}
while (card >= 0) {
gchar name[32];
g_snprintf (name, sizeof (name), "hw:%d", card);
if ((err = snd_ctl_open (&handle, name, 0)) < 0) {
goto next_card;
}
if ((err = snd_ctl_card_info (handle, info)) < 0) {
snd_ctl_close (handle);
goto next_card;
}
if (mixer) {
probe_data->devices = g_list_append (probe_data->devices,
g_strdup (name));
} else {
dev = -1;
while (1) {
gchar *gst_device;
snd_ctl_pcm_next_device (handle, &dev);
if (dev < 0)
break;
snd_pcm_info_set_device (pcminfo, dev);
snd_pcm_info_set_subdevice (pcminfo, 0);
snd_pcm_info_set_stream (pcminfo, stream);
if ((err = snd_ctl_pcm_info (handle, pcminfo)) < 0) {
continue;
}
gst_device = g_strdup_printf ("hw:%d,%d", card, dev);
probe_data->devices = g_list_append (probe_data->devices, gst_device);
}
}
snd_ctl_close (handle);
next_card:
if (snd_card_next (&card) < 0) {
break;
}
}
}
static gboolean
gst_alsa_probe_devices (GstElementClass * klass,
GstAlsaDeviceProbeData * probe_data, gboolean check)
{
static gboolean init = FALSE;
/* I'm pretty sure ALSA has a good way to do this. However, their cool
* auto-generated documentation is pretty much useless if you try to
* do function-wise look-ups. */
if (!init && !check) {
snd_pcm_stream_t mode = -1;
const GList *templates;
/* we assume one pad template at max [zero=mixer] */
templates = gst_element_class_get_pad_template_list (klass);
if (templates) {
if (GST_PAD_TEMPLATE_DIRECTION (templates->data) == GST_PAD_SRC)
mode = SND_PCM_STREAM_CAPTURE;
else
mode = SND_PCM_STREAM_PLAYBACK;
}
gst_alsa_add_device_list (probe_data, mode);
init = TRUE;
}
return init;
}
static void
gst_alsa_device_property_probe_probe_property (GstPropertyProbe * probe,
guint prop_id, const GParamSpec * pspec)
{
GstAlsaDeviceProbeData *probe_data;
guint devid;
if (!gst_alsa_device_probe_get_data (G_OBJECT (probe), &probe_data, &devid))
g_return_if_reached ();
if (prop_id == devid) {
gst_alsa_probe_devices (GST_ELEMENT_GET_CLASS (probe), probe_data, FALSE);
} else {
G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
}
}
static gboolean
gst_alsa_device_property_probe_needs_probe (GstPropertyProbe * probe,
guint prop_id, const GParamSpec * pspec)
{
GstAlsaDeviceProbeData *probe_data;
GstElementClass *klass;
guint devid;
if (!gst_alsa_device_probe_get_data (G_OBJECT (probe), &probe_data, &devid))
g_return_val_if_reached (FALSE);
if (prop_id != devid) {
G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
return FALSE;
}
klass = GST_ELEMENT_GET_CLASS (probe);
return !gst_alsa_probe_devices (klass, probe_data, TRUE);
}
static GValueArray *
gst_alsa_device_probe_list_devices (GstAlsaDeviceProbeData * probe_data)
{
GValueArray *array;
GValue value = { 0 };
GList *item;
if (!probe_data->devices)
return NULL;
array = g_value_array_new (g_list_length (probe_data->devices));
g_value_init (&value, G_TYPE_STRING);
for (item = probe_data->devices; item != NULL; item = item->next) {
g_value_set_string (&value, (const gchar *) item->data);
g_value_array_append (array, &value);
}
g_value_unset (&value);
return array;
}
static GValueArray *
gst_alsa_device_property_probe_get_values (GstPropertyProbe * probe,
guint prop_id, const GParamSpec * pspec)
{
GstAlsaDeviceProbeData *probe_data;
guint devid;
if (!gst_alsa_device_probe_get_data (G_OBJECT (probe), &probe_data, &devid))
g_return_val_if_reached (NULL);
if (prop_id != devid) {
G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
return NULL;
}
return gst_alsa_device_probe_list_devices (probe_data);
}
static void
gst_alsa_property_probe_interface_init (GstPropertyProbeInterface * iface)
{
iface->get_properties = gst_alsa_device_property_probe_get_properties;
iface->probe_property = gst_alsa_device_property_probe_probe_property;
iface->needs_probe = gst_alsa_device_property_probe_needs_probe;
iface->get_values = gst_alsa_device_property_probe_get_values;
}
void
gst_alsa_type_add_device_property_probe_interface (GType type,
guint probe_data_klass_offset, guint device_prop_id)
{
static const GInterfaceInfo probe_iface_info = {
(GInterfaceInitFunc) gst_alsa_property_probe_interface_init,
NULL,
NULL,
};
g_assert (probe_data_klass_offset != 0);
g_assert (device_prop_id != 0);
g_type_set_qdata (type, DATA_OFFSET_QUARK,
GUINT_TO_POINTER (probe_data_klass_offset));
g_type_set_qdata (type, DEVICE_PROPID_QUARK,
GUINT_TO_POINTER (device_prop_id));
g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE,
&probe_iface_info);
}

View file

@ -0,0 +1,43 @@
/* Copyright (C) 2001 CodeFactory AB
* Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se>
* Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu>
* Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __GST_ALSA_DEVICE_PROBE_H__
#define __GST_ALSA_DEVICE_PROBE_H__
#include "gstalsa.h"
G_BEGIN_DECLS
typedef struct _GstAlsaDeviceProbeData GstAlsaDeviceProbeData;
struct _GstAlsaDeviceProbeData {
/* autodetected devices available */
GList *devices;
};
/* offset is the offset of the device probe data struct in the klass struct */
void gst_alsa_type_add_device_property_probe_interface (GType type,
guint probe_data_offset,
guint device_prop_id);
G_END_DECLS
#endif /* __GST_ALSA_DEVICE_PROBE_H__ */

View file

@ -81,8 +81,11 @@ gst_alsa_mixer_open (GstAlsaMixer * mixer)
if (sscanf (mixer->device, "hw:%d", &devicenum) == 1) {
gchar *name;
if (!snd_card_get_name (devicenum, &name))
mixer->cardname = name;
if (!snd_card_get_name (devicenum, &name)) {
mixer->cardname = g_strdup (name);
free (name);
GST_DEBUG ("Card name = %s", GST_STR_NULL (mixer->cardname));
}
}
GST_INFO ("Successfully opened mixer for device `%s'.", mixer->device);

View file

@ -30,8 +30,8 @@
G_BEGIN_DECLS
#define GST_ALSA_MIXER(obj) ((GstAlsaMixer*)(obj))
/* This does not get you what you think it does, use obj->mixer */
/* #define GST_ALSA_MIXER(obj) ((GstAlsaMixer*)(obj)) */
typedef enum {

View file

@ -23,6 +23,13 @@
#endif
#include "gstalsamixerelement.h"
#include "gstalsadeviceprobe.h"
enum
{
PROP_DEVICE = 1,
PROP_DEVICE_NAME
};
static GstElementDetails gst_alsa_mixer_element_details =
@ -31,15 +38,62 @@ GST_ELEMENT_DETAILS ("Alsa Mixer",
"Control sound input and output levels with ALSA",
"Leif Johnson <leif@ambient.2y.net>");
static void gst_alsa_mixer_element_init_interfaces (GType type);
GST_BOILERPLATE_WITH_INTERFACE (GstAlsaMixerElement, gst_alsa_mixer_element,
GstElement, GST_TYPE_ELEMENT, GstMixer, GST_TYPE_MIXER,
gst_alsa_mixer_element);
GST_BOILERPLATE_FULL (GstAlsaMixerElement, gst_alsa_mixer_element,
GstElement, GST_TYPE_ELEMENT, gst_alsa_mixer_element_init_interfaces)
GST_IMPLEMENT_ALSA_MIXER_METHODS (GstAlsaMixerElement, gst_alsa_mixer_element);
/* massive macro that takes care of all the GstMixer stuff */
GST_IMPLEMENT_ALSA_MIXER_METHODS (GstAlsaMixerElement, gst_alsa_mixer_element);
static GstStateChangeReturn gst_alsa_mixer_element_change_state (GstElement *
element, GstStateChange transition);
static void gst_alsa_mixer_element_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static void gst_alsa_mixer_element_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_alsa_mixer_element_finalize (GObject * object);
static GstStateChangeReturn gst_alsa_mixer_element_change_state (GstElement
* element, GstStateChange transition);
static gboolean
gst_alsa_mixer_element_interface_supported (GstAlsaMixerElement * this,
GType interface_type)
{
if (interface_type == GST_TYPE_MIXER) {
return gst_alsa_mixer_element_supported (this, interface_type);
}
g_return_val_if_reached (FALSE);
}
static void
gst_implements_interface_init (GstImplementsInterfaceClass * klass)
{
klass->supported = (gpointer) gst_alsa_mixer_element_interface_supported;
}
static void
gst_alsa_mixer_element_init_interfaces (GType type)
{
static const GInterfaceInfo implements_iface_info = {
(GInterfaceInitFunc) gst_implements_interface_init,
NULL,
NULL,
};
static const GInterfaceInfo mixer_iface_info = {
(GInterfaceInitFunc) gst_alsa_mixer_element_interface_init,
NULL,
NULL,
};
g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE,
&implements_iface_info);
g_type_add_interface_static (type, GST_TYPE_MIXER, &mixer_iface_info);
gst_alsa_type_add_device_property_probe_interface (type,
G_STRUCT_OFFSET (GstAlsaMixerElementClass, device_probe_data),
PROP_DEVICE);
}
static void
gst_alsa_mixer_element_base_init (gpointer klass)
@ -52,10 +106,36 @@ static void
gst_alsa_mixer_element_class_init (GstAlsaMixerElementClass * klass)
{
GstElementClass *element_class;
GObjectClass *gobject_class;
element_class = (GstElementClass *) klass;
gobject_class = (GObjectClass *) klass;
element_class->change_state = gst_alsa_mixer_element_change_state;
gobject_class->finalize = gst_alsa_mixer_element_finalize;
gobject_class->get_property = gst_alsa_mixer_element_get_property;
gobject_class->set_property = gst_alsa_mixer_element_set_property;
g_object_class_install_property (gobject_class, PROP_DEVICE,
g_param_spec_string ("device", "Device",
"ALSA device, as defined in an asound configuration file",
"default", 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", "", G_PARAM_READABLE));
element_class->change_state =
GST_DEBUG_FUNCPTR (gst_alsa_mixer_element_change_state);
}
static void
gst_alsa_mixer_element_finalize (GObject * obj)
{
GstAlsaMixerElement *this = GST_ALSA_MIXER_ELEMENT (obj);
g_free (this->device);
G_OBJECT_CLASS (parent_class)->finalize (obj);
}
static void
@ -63,20 +143,84 @@ gst_alsa_mixer_element_init (GstAlsaMixerElement * this,
GstAlsaMixerElementClass * klass)
{
this->mixer = NULL;
this->device = g_strdup ("default");
}
static void
gst_alsa_mixer_element_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstAlsaMixerElement *this = GST_ALSA_MIXER_ELEMENT (object);
switch (prop_id) {
case PROP_DEVICE:{
GST_OBJECT_LOCK (this);
g_free (this->device);
this->device = g_value_dup_string (value);
GST_OBJECT_UNLOCK (this);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_alsa_mixer_element_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstAlsaMixerElement *this = GST_ALSA_MIXER_ELEMENT (object);
switch (prop_id) {
case PROP_DEVICE:{
GST_OBJECT_LOCK (this);
g_value_set_string (value, this->device);
GST_OBJECT_UNLOCK (this);
break;
}
case PROP_DEVICE_NAME:{
GST_OBJECT_LOCK (this);
if (this->mixer) {
g_value_set_string (value, this->mixer->cardname);
} else {
g_value_set_string (value, NULL);
}
GST_OBJECT_UNLOCK (this);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static GstStateChangeReturn
gst_alsa_mixer_element_change_state (GstElement * element,
GstStateChange transition)
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GstAlsaMixerElement *this = GST_ALSA_MIXER_ELEMENT (element);
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
if (!this->mixer) {
this->mixer = gst_alsa_mixer_new ("hw:0", GST_ALSA_MIXER_ALL);
if (!this->device) {
this->mixer = gst_alsa_mixer_new ("default", GST_ALSA_MIXER_ALL);
} else {
this->mixer = gst_alsa_mixer_new (this->device, GST_ALSA_MIXER_ALL);
}
}
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
switch (transition) {
case GST_STATE_CHANGE_READY_TO_NULL:
if (this->mixer) {
gst_alsa_mixer_free (this->mixer);
@ -87,8 +231,5 @@ gst_alsa_mixer_element_change_state (GstElement * element,
break;
}
if (GST_ELEMENT_CLASS (parent_class)->change_state)
return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
return GST_STATE_CHANGE_SUCCESS;
return ret;
}

View file

@ -23,6 +23,7 @@
#include "gstalsa.h"
#include "gstalsamixer.h"
#include "gstalsadeviceprobe.h"
G_BEGIN_DECLS
@ -43,10 +44,13 @@ struct _GstAlsaMixerElement {
GstElement parent;
GstAlsaMixer *mixer;
gchar *device;
};
struct _GstAlsaMixerElementClass {
GstElementClass parent;
GstAlsaDeviceProbeData device_probe_data;
};