mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-25 01:30:38 +00:00
Fixes: #152864
Original commit message from CVS: * docs/libs/gst-plugins-base-libs-sections.txt: * ext/alsa/gstalsamixer.c: * ext/alsa/gstalsamixer.h: * ext/alsa/gstalsamixerelement.c: * ext/alsa/gstalsamixertrack.c: * gst-libs/gst/interfaces/mixer.c: * gst-libs/gst/interfaces/mixer.h: * gst-libs/gst/interfaces/mixeroptions.c: * gst-libs/gst/interfaces/mixeroptions.h: * gst-libs/gst/interfaces/mixertrack.c: * gst-libs/gst/interfaces/mixertrack.h: * tests/check/Makefile.am: * tests/check/libs/mixer.c: Patch By: Marc-Andre Lureau <marcandre.lureau@gmail.com> Fixes: #152864 Add support for notifying mixer changes on the message bus, and implement it in alsamixer. API: gst_mixer_get_mixer_flags API: gst_mixer_message_parse_mute_toggled API: gst_mixer_message_parse_record_toggled API: gst_mixer_message_parse_volume_changed API: gst_mixer_message_parse_option_changed API: GstMixerMessageType API: GstMixerFlags
This commit is contained in:
parent
11cf0dcd6b
commit
c161e29307
14 changed files with 1057 additions and 52 deletions
30
ChangeLog
30
ChangeLog
|
@ -1,3 +1,33 @@
|
|||
2007-07-20 Jan Schmidt <thaytan@noraisin.net>
|
||||
|
||||
* docs/libs/gst-plugins-base-libs-sections.txt:
|
||||
* ext/alsa/gstalsamixer.c:
|
||||
* ext/alsa/gstalsamixer.h:
|
||||
* ext/alsa/gstalsamixerelement.c:
|
||||
* ext/alsa/gstalsamixertrack.c:
|
||||
* gst-libs/gst/interfaces/mixer.c:
|
||||
* gst-libs/gst/interfaces/mixer.h:
|
||||
* gst-libs/gst/interfaces/mixeroptions.c:
|
||||
* gst-libs/gst/interfaces/mixeroptions.h:
|
||||
* gst-libs/gst/interfaces/mixertrack.c:
|
||||
* gst-libs/gst/interfaces/mixertrack.h:
|
||||
* tests/check/Makefile.am:
|
||||
* tests/check/libs/mixer.c:
|
||||
|
||||
Patch By: Marc-Andre Lureau <marcandre.lureau@gmail.com>
|
||||
Fixes: #152864
|
||||
|
||||
Add support for notifying mixer changes on the message bus, and
|
||||
implement it in alsamixer.
|
||||
|
||||
API: gst_mixer_get_mixer_flags
|
||||
API: gst_mixer_message_parse_mute_toggled
|
||||
API: gst_mixer_message_parse_record_toggled
|
||||
API: gst_mixer_message_parse_volume_changed
|
||||
API: gst_mixer_message_parse_option_changed
|
||||
API: GstMixerMessageType
|
||||
API: GstMixerFlags
|
||||
|
||||
2007-07-20 Michael Smith <msmith@fluendo.com>
|
||||
|
||||
* sys/xvimage/xvimagesink.c: (gst_xvimagesink_xvimage_new),
|
||||
|
|
|
@ -288,6 +288,8 @@ gst_color_balance_channel_get_type
|
|||
<INCLUDE>gst/interfaces/mixer.h</INCLUDE>
|
||||
GstMixer
|
||||
GstMixerType
|
||||
GstMixerFlags
|
||||
GstMixerMessageType
|
||||
|
||||
gst_mixer_list_tracks
|
||||
gst_mixer_get_volume
|
||||
|
@ -303,6 +305,14 @@ gst_mixer_volume_changed
|
|||
gst_mixer_option_changed
|
||||
gst_mixer_get_option
|
||||
|
||||
gst_mixer_get_mixer_flags
|
||||
|
||||
gst_mixer_message_get_type
|
||||
gst_mixer_message_parse_mute_toggled
|
||||
gst_mixer_message_parse_option_changed
|
||||
gst_mixer_message_parse_record_toggled
|
||||
gst_mixer_message_parse_volume_changed
|
||||
|
||||
<SUBSECTION Standard>
|
||||
GstMixerClass
|
||||
GST_TYPE_MIXER
|
||||
|
@ -315,6 +325,8 @@ GST_IS_MIXER
|
|||
GST_IS_MIXER_CLASS
|
||||
gst_mixer_get_type
|
||||
gst_mixer_type_get_type
|
||||
gst_mixer_flags_get_type
|
||||
gst_mixer_message_type_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
|
|
|
@ -42,6 +42,12 @@
|
|||
|
||||
#include "gstalsamixer.h"
|
||||
|
||||
static void gst_alsa_mixer_update_option (GstAlsaMixer * mixer,
|
||||
GstAlsaMixerOptions * alsa_opts);
|
||||
static void gst_alsa_mixer_update_track (GstAlsaMixer * mixer,
|
||||
GstAlsaMixerTrack * alsa_track);
|
||||
static int gst_alsa_mixer_handle_callback (snd_mixer_t * handle,
|
||||
unsigned int mask, snd_mixer_elem_t * elem);
|
||||
|
||||
/* First some utils, then the mixer implementation */
|
||||
static gboolean
|
||||
|
@ -74,6 +80,8 @@ gst_alsa_mixer_open (GstAlsaMixer * mixer)
|
|||
goto error;
|
||||
}
|
||||
|
||||
snd_mixer_set_callback_private (mixer->handle, mixer);
|
||||
snd_mixer_set_callback (mixer->handle, gst_alsa_mixer_handle_callback);
|
||||
|
||||
/* now get the device name, any of this is not fatal */
|
||||
g_free (mixer->cardname);
|
||||
|
@ -169,17 +177,80 @@ gst_alsa_mixer_find_master_mixer (GstAlsaMixer * mixer, snd_mixer_t * handle)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_alsa_mixer_update (GstAlsaMixer * mixer, snd_mixer_elem_t * elem)
|
||||
{
|
||||
GList *item;
|
||||
|
||||
g_return_if_fail (mixer != NULL);
|
||||
|
||||
g_static_rec_mutex_lock (mixer->rec_mutex);
|
||||
|
||||
for (item = mixer->tracklist; item != NULL; item = item->next) {
|
||||
if (GST_IS_ALSA_MIXER_TRACK (item->data)) {
|
||||
if (elem && (GST_ALSA_MIXER_TRACK (item->data)->element != elem))
|
||||
continue;
|
||||
|
||||
gst_alsa_mixer_update_track (mixer, GST_ALSA_MIXER_TRACK (item->data));
|
||||
} else if (GST_IS_ALSA_MIXER_OPTIONS (item->data)) {
|
||||
if (elem && (GST_ALSA_MIXER_OPTIONS (item->data)->element != elem))
|
||||
continue;
|
||||
|
||||
gst_alsa_mixer_update_option (mixer, GST_ALSA_MIXER_OPTIONS (item->data));
|
||||
}
|
||||
}
|
||||
|
||||
g_static_rec_mutex_unlock (mixer->rec_mutex);
|
||||
}
|
||||
|
||||
static int
|
||||
gst_alsa_mixer_elem_handle_callback (snd_mixer_elem_t * elem, unsigned int mask)
|
||||
{
|
||||
GstAlsaMixer *mixer =
|
||||
(GstAlsaMixer *) snd_mixer_elem_get_callback_private (elem);
|
||||
|
||||
GST_LOG ("ALSA elem cb");
|
||||
|
||||
g_return_val_if_fail (mixer != NULL, 1);
|
||||
|
||||
gst_alsa_mixer_update (mixer, elem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gst_alsa_mixer_handle_callback (snd_mixer_t * handle, unsigned int mask,
|
||||
snd_mixer_elem_t * elem)
|
||||
{
|
||||
GstAlsaMixer *mixer =
|
||||
(GstAlsaMixer *) snd_mixer_get_callback_private (handle);
|
||||
|
||||
GST_LOG ("ALSA cb");
|
||||
|
||||
g_return_val_if_fail (mixer != NULL, 1);
|
||||
|
||||
/* Hopefully won't be call recursively and will handle pending elem events */
|
||||
snd_mixer_handle_events (mixer->handle);
|
||||
|
||||
gst_alsa_mixer_update (mixer, elem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_alsa_mixer_ensure_track_list (GstAlsaMixer * mixer)
|
||||
{
|
||||
gint i, count;
|
||||
snd_mixer_elem_t *element, *master;
|
||||
GList *item;
|
||||
|
||||
g_return_if_fail (mixer->handle != NULL);
|
||||
|
||||
if (mixer->tracklist)
|
||||
return;
|
||||
|
||||
g_static_rec_mutex_lock (mixer->rec_mutex);
|
||||
|
||||
master = gst_alsa_mixer_find_master_mixer (mixer, mixer->handle);
|
||||
|
||||
count = snd_mixer_get_count (mixer->handle);
|
||||
|
@ -287,8 +358,62 @@ gst_alsa_mixer_ensure_track_list (GstAlsaMixer * mixer)
|
|||
|
||||
element = snd_mixer_elem_next (element);
|
||||
}
|
||||
|
||||
for (item = mixer->tracklist; item != NULL; item = item->next) {
|
||||
snd_mixer_elem_t *temp;
|
||||
|
||||
if (GST_IS_ALSA_MIXER_OPTIONS (item->data))
|
||||
temp = GST_ALSA_MIXER_OPTIONS (item->data)->element;
|
||||
else
|
||||
temp = GST_ALSA_MIXER_TRACK (item->data)->element;
|
||||
|
||||
snd_mixer_elem_set_callback (temp, gst_alsa_mixer_elem_handle_callback);
|
||||
snd_mixer_elem_set_callback_private (temp, mixer);
|
||||
}
|
||||
|
||||
g_static_rec_mutex_unlock (mixer->rec_mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
task_monitor_alsa (gpointer data)
|
||||
{
|
||||
struct pollfd *pfds;
|
||||
unsigned int nfds, rnfds;
|
||||
unsigned short revents;
|
||||
GstAlsaMixer *mixer = (GstAlsaMixer *) data;
|
||||
|
||||
g_static_rec_mutex_lock (mixer->rec_mutex);
|
||||
|
||||
nfds = snd_mixer_poll_descriptors_count (mixer->handle);
|
||||
if (nfds <= 0) {
|
||||
GST_ERROR ("snd_mixer_poll_descriptors_count <= 0: %d", nfds);
|
||||
/* FIXME: sleep ? stop monitoring ? */
|
||||
return;
|
||||
}
|
||||
|
||||
pfds = g_newa (struct pollfd, nfds + 1);
|
||||
rnfds = snd_mixer_poll_descriptors (mixer->handle, pfds, nfds);
|
||||
g_assert (rnfds <= nfds);
|
||||
|
||||
pfds[rnfds].fd = mixer->pfd[0];
|
||||
pfds[rnfds].events = POLLIN | POLLPRI | POLLHUP | POLLERR;
|
||||
pfds[rnfds].revents = 0;
|
||||
|
||||
g_static_rec_mutex_unlock (mixer->rec_mutex);
|
||||
|
||||
GST_LOG ("task loop");
|
||||
poll (pfds, rnfds + 1, -1);
|
||||
|
||||
g_static_rec_mutex_lock (mixer->rec_mutex);
|
||||
|
||||
snd_mixer_poll_descriptors_revents (mixer->handle, pfds, nfds, &revents);
|
||||
if (revents & POLLIN || revents & POLLPRI) {
|
||||
GST_DEBUG ("Handling events");
|
||||
snd_mixer_handle_events (mixer->handle);
|
||||
}
|
||||
|
||||
g_static_rec_mutex_unlock (mixer->rec_mutex);
|
||||
}
|
||||
|
||||
/* API */
|
||||
|
||||
|
@ -301,12 +426,28 @@ gst_alsa_mixer_new (const char *device, GstAlsaMixerDirection dir)
|
|||
|
||||
ret = g_new0 (GstAlsaMixer, 1);
|
||||
|
||||
if (pipe (ret->pfd) == -1)
|
||||
goto error;
|
||||
|
||||
ret->rec_mutex = g_new (GStaticRecMutex, 1);
|
||||
g_static_rec_mutex_init (ret->rec_mutex);
|
||||
|
||||
ret->task_mutex = g_new (GStaticRecMutex, 1);
|
||||
g_static_rec_mutex_init (ret->task_mutex);
|
||||
|
||||
ret->task = gst_task_create (task_monitor_alsa, ret);
|
||||
gst_task_set_lock (ret->task, ret->task_mutex);
|
||||
|
||||
ret->device = g_strdup (device);
|
||||
ret->dir = dir;
|
||||
|
||||
if (!gst_alsa_mixer_open (ret))
|
||||
goto error;
|
||||
|
||||
if (gst_task_start (ret->task) == FALSE) {
|
||||
GST_WARNING ("Could not start alsamixer task");
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
/* ERRORS */
|
||||
|
@ -322,6 +463,40 @@ gst_alsa_mixer_free (GstAlsaMixer * mixer)
|
|||
{
|
||||
g_return_if_fail (mixer != NULL);
|
||||
|
||||
if (mixer->task) {
|
||||
if (write (mixer->pfd[1], "stop", 5) <= 0) {
|
||||
GST_ERROR ("Cannot send " "stop" " to alsamixer task");
|
||||
close (mixer->pfd[1]);
|
||||
mixer->pfd[1] = -1;
|
||||
}
|
||||
|
||||
if (gst_task_join (mixer->task) == FALSE) {
|
||||
GST_ERROR ("Cannot join alsamixer task");
|
||||
}
|
||||
|
||||
gst_object_unref (mixer->task);
|
||||
mixer->task = NULL;
|
||||
}
|
||||
|
||||
g_static_rec_mutex_free (mixer->task_mutex);
|
||||
g_free (mixer->task_mutex);
|
||||
mixer->task_mutex = NULL;
|
||||
|
||||
if (mixer->pfd[0] > 0) {
|
||||
close (mixer->pfd[0]);
|
||||
mixer->pfd[0] = -1;
|
||||
}
|
||||
|
||||
if (mixer->pfd[1] > 0) {
|
||||
close (mixer->pfd[1]);
|
||||
mixer->pfd[1] = -1;
|
||||
}
|
||||
|
||||
if (mixer->interface) {
|
||||
g_object_unref (G_OBJECT (mixer->interface));
|
||||
mixer->interface = NULL;
|
||||
}
|
||||
|
||||
if (mixer->device) {
|
||||
g_free (mixer->device);
|
||||
mixer->device = NULL;
|
||||
|
@ -343,6 +518,10 @@ gst_alsa_mixer_free (GstAlsaMixer * mixer)
|
|||
mixer->handle = NULL;
|
||||
}
|
||||
|
||||
g_static_rec_mutex_free (mixer->rec_mutex);
|
||||
g_free (mixer->rec_mutex);
|
||||
mixer->rec_mutex = NULL;
|
||||
|
||||
g_free (mixer);
|
||||
}
|
||||
|
||||
|
@ -356,16 +535,6 @@ gst_alsa_mixer_list_tracks (GstAlsaMixer * mixer)
|
|||
return (const GList *) mixer->tracklist;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_alsa_mixer_update (GstAlsaMixer * mixer, GstAlsaMixerTrack * alsa_track)
|
||||
{
|
||||
snd_mixer_handle_events (mixer->handle);
|
||||
|
||||
if (alsa_track)
|
||||
gst_alsa_mixer_track_update (alsa_track);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gst_alsa_mixer_get_volume (GstAlsaMixer * mixer, GstMixerTrack * track,
|
||||
gint * volumes)
|
||||
|
@ -375,7 +544,7 @@ gst_alsa_mixer_get_volume (GstAlsaMixer * mixer, GstMixerTrack * track,
|
|||
|
||||
g_return_if_fail (mixer->handle != NULL);
|
||||
|
||||
gst_alsa_mixer_update (mixer, alsa_track);
|
||||
gst_alsa_mixer_track_update (alsa_track);
|
||||
|
||||
if (track->flags & GST_MIXER_TRACK_OUTPUT) { /* return playback volume */
|
||||
|
||||
|
@ -420,7 +589,7 @@ gst_alsa_mixer_set_volume (GstAlsaMixer * mixer, GstMixerTrack * track,
|
|||
|
||||
g_return_if_fail (mixer->handle != NULL);
|
||||
|
||||
gst_alsa_mixer_update (mixer, alsa_track);
|
||||
gst_alsa_mixer_track_update (alsa_track);
|
||||
|
||||
if (track->flags & GST_MIXER_TRACK_OUTPUT) {
|
||||
|
||||
|
@ -461,7 +630,7 @@ gst_alsa_mixer_set_mute (GstAlsaMixer * mixer, GstMixerTrack * track,
|
|||
|
||||
g_return_if_fail (mixer->handle != NULL);
|
||||
|
||||
gst_alsa_mixer_update (mixer, alsa_track);
|
||||
gst_alsa_mixer_track_update (alsa_track);
|
||||
|
||||
if (!!(mute) == !!(track->flags & GST_MIXER_TRACK_MUTE))
|
||||
return;
|
||||
|
@ -509,7 +678,7 @@ gst_alsa_mixer_set_record (GstAlsaMixer * mixer,
|
|||
|
||||
g_return_if_fail (mixer->handle != NULL);
|
||||
|
||||
gst_alsa_mixer_update (mixer, alsa_track);
|
||||
gst_alsa_mixer_track_update (alsa_track);
|
||||
|
||||
if (!!(record) == !!(track->flags & GST_MIXER_TRACK_RECORD))
|
||||
return;
|
||||
|
@ -536,7 +705,7 @@ gst_alsa_mixer_set_record (GstAlsaMixer * mixer,
|
|||
|
||||
if (item_alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH_EXCL &&
|
||||
item_alsa_track->capture_group == alsa_track->capture_group) {
|
||||
gst_alsa_mixer_update (mixer, item_alsa_track);
|
||||
gst_alsa_mixer_track_update (item_alsa_track);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -562,8 +731,6 @@ gst_alsa_mixer_set_option (GstAlsaMixer * mixer,
|
|||
|
||||
g_return_if_fail (mixer->handle != NULL);
|
||||
|
||||
gst_alsa_mixer_update (mixer, NULL);
|
||||
|
||||
for (item = opts->values; item != NULL; item = item->next, n++) {
|
||||
if (!strcmp (item->data, value)) {
|
||||
idx = n;
|
||||
|
@ -585,11 +752,94 @@ gst_alsa_mixer_get_option (GstAlsaMixer * mixer, GstMixerOptions * opts)
|
|||
|
||||
g_return_val_if_fail (mixer->handle != NULL, NULL);
|
||||
|
||||
gst_alsa_mixer_update (mixer, NULL);
|
||||
|
||||
ret = snd_mixer_selem_get_enum_item (alsa_opts->element, 0, &idx);
|
||||
if (ret == 0)
|
||||
return g_list_nth_data (opts->values, idx);
|
||||
else
|
||||
return snd_strerror (ret); /* feeble attempt at error handling */
|
||||
}
|
||||
|
||||
GstMixerFlags
|
||||
gst_alsa_mixer_get_mixer_flags (GstAlsaMixer * mixer)
|
||||
{
|
||||
g_return_val_if_fail (mixer != NULL, GST_MIXER_FLAG_NONE);
|
||||
|
||||
return GST_MIXER_FLAG_AUTO_NOTIFICATIONS;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_alsa_mixer_update_option (GstAlsaMixer * mixer,
|
||||
GstAlsaMixerOptions * alsa_opts)
|
||||
{
|
||||
gint ret;
|
||||
guint idx;
|
||||
/* const */ gchar *option;
|
||||
|
||||
if (mixer->interface == NULL) {
|
||||
GST_WARNING ("Cannot send update notifications, no GstMixer * given");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = snd_mixer_selem_get_enum_item (alsa_opts->element, 0, &idx);
|
||||
if (ret == 0) {
|
||||
option = g_list_nth_data (GST_MIXER_OPTIONS (alsa_opts)->values, idx);
|
||||
gst_mixer_option_changed (mixer->interface, GST_MIXER_OPTIONS (alsa_opts),
|
||||
option);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_alsa_mixer_update_track (GstAlsaMixer * mixer,
|
||||
GstAlsaMixerTrack * alsa_track)
|
||||
{
|
||||
GstMixerTrack *track = (GstMixerTrack *) alsa_track;
|
||||
gboolean old_mute;
|
||||
gboolean old_record;
|
||||
gint i, n_channels;
|
||||
gint *old_volumes;
|
||||
|
||||
GST_DEBUG ("Updating track %" GST_PTR_FORMAT, alsa_track);
|
||||
|
||||
if (mixer->interface == NULL) {
|
||||
GST_WARNING ("Cannot send update notifications, no GstMixer * given");
|
||||
return;
|
||||
}
|
||||
|
||||
old_mute = !!(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE));
|
||||
old_record = !!(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD));
|
||||
old_volumes = g_new (gint, track->num_channels);
|
||||
n_channels = track->num_channels;
|
||||
memcpy (old_volumes, alsa_track->volumes,
|
||||
sizeof (gint) * track->num_channels);
|
||||
|
||||
gst_alsa_mixer_track_update (alsa_track);
|
||||
|
||||
if (old_record !=
|
||||
!!(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD))) {
|
||||
gst_mixer_record_toggled (mixer->interface, track,
|
||||
!!GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD));
|
||||
}
|
||||
if (old_mute != !!(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE))) {
|
||||
gst_mixer_mute_toggled (mixer->interface, track,
|
||||
!!GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE));
|
||||
}
|
||||
|
||||
n_channels = MIN (n_channels, track->num_channels);
|
||||
for (i = 0; i < n_channels; i++) {
|
||||
if (old_volumes[i] != alsa_track->volumes[i]) {
|
||||
gst_mixer_volume_changed (mixer->interface, track, alsa_track->volumes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_free (old_volumes);
|
||||
}
|
||||
|
||||
/* utility function for gstalsamixerelement to set the interface */
|
||||
void
|
||||
_gst_alsa_mixer_set_interface (GstAlsaMixer * mixer, GstMixer * interface)
|
||||
{
|
||||
g_return_if_fail (mixer != NULL && mixer->interface == NULL);
|
||||
g_return_if_fail (interface != NULL);
|
||||
|
||||
mixer->interface = g_object_ref (G_OBJECT (interface));
|
||||
}
|
||||
|
|
|
@ -52,6 +52,13 @@ struct _GstAlsaMixer
|
|||
|
||||
snd_mixer_t * handle;
|
||||
|
||||
GstTask * task;
|
||||
GStaticRecMutex * task_mutex;
|
||||
GStaticRecMutex * rec_mutex;
|
||||
|
||||
int pfd[2];
|
||||
|
||||
GstMixer * interface;
|
||||
gchar * device;
|
||||
gchar * cardname;
|
||||
|
||||
|
@ -81,7 +88,9 @@ void gst_alsa_mixer_set_option (GstAlsaMixer * mixer,
|
|||
gchar * value);
|
||||
const gchar* gst_alsa_mixer_get_option (GstAlsaMixer * mixer,
|
||||
GstMixerOptions * opts);
|
||||
|
||||
void _gst_alsa_mixer_set_interface (GstAlsaMixer * mixer,
|
||||
GstMixer * interface);
|
||||
GstMixerFlags gst_alsa_mixer_get_mixer_flags (GstAlsaMixer *mixer);
|
||||
|
||||
#define GST_IMPLEMENT_ALSA_MIXER_METHODS(Type, interface_as_function) \
|
||||
static gboolean \
|
||||
|
@ -174,6 +183,17 @@ interface_as_function ## _get_option (GstMixer * mixer, GstMixerOptions * opts)
|
|||
return gst_alsa_mixer_get_option (this->mixer, opts); \
|
||||
} \
|
||||
\
|
||||
static GstMixerFlags \
|
||||
interface_as_function ## _get_mixer_flags (GstMixer * mixer) \
|
||||
{ \
|
||||
Type *this = (Type*) mixer; \
|
||||
\
|
||||
g_return_val_if_fail (this != NULL, GST_MIXER_FLAG_NONE); \
|
||||
g_return_val_if_fail (this->mixer != NULL, GST_MIXER_FLAG_NONE); \
|
||||
\
|
||||
return gst_alsa_mixer_get_mixer_flags (this->mixer); \
|
||||
} \
|
||||
\
|
||||
static void \
|
||||
interface_as_function ## _interface_init (GstMixerClass * klass) \
|
||||
{ \
|
||||
|
@ -187,6 +207,7 @@ interface_as_function ## _interface_init (GstMixerClass * klass)
|
|||
klass->set_record = interface_as_function ## _set_record; \
|
||||
klass->set_option = interface_as_function ## _set_option; \
|
||||
klass->get_option = interface_as_function ## _get_option; \
|
||||
klass->get_mixer_flags = interface_as_function ## _get_mixer_flags; \
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -44,22 +44,22 @@ GST_ELEMENT_DETAILS ("Alsa mixer",
|
|||
static void gst_alsa_mixer_element_init_interfaces (GType type);
|
||||
|
||||
GST_BOILERPLATE_FULL (GstAlsaMixerElement, gst_alsa_mixer_element,
|
||||
GstElement, GST_TYPE_ELEMENT, gst_alsa_mixer_element_init_interfaces)
|
||||
GstElement, GST_TYPE_ELEMENT, gst_alsa_mixer_element_init_interfaces);
|
||||
|
||||
/* massive macro that takes care of all the GstMixer stuff */
|
||||
GST_IMPLEMENT_ALSA_MIXER_METHODS (GstAlsaMixerElement, gst_alsa_mixer_element);
|
||||
GST_IMPLEMENT_ALSA_MIXER_METHODS (GstAlsaMixerElement, gst_alsa_mixer_element);
|
||||
|
||||
static void gst_alsa_mixer_element_get_property (GObject * object,
|
||||
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,
|
||||
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 void gst_alsa_mixer_element_finalize (GObject * object);
|
||||
|
||||
static GstStateChangeReturn gst_alsa_mixer_element_change_state (GstElement
|
||||
static GstStateChangeReturn gst_alsa_mixer_element_change_state (GstElement
|
||||
* element, GstStateChange transition);
|
||||
|
||||
static gboolean
|
||||
gst_alsa_mixer_element_interface_supported (GstAlsaMixerElement * this,
|
||||
static gboolean
|
||||
gst_alsa_mixer_element_interface_supported (GstAlsaMixerElement * this,
|
||||
GType interface_type)
|
||||
{
|
||||
if (interface_type == GST_TYPE_MIXER) {
|
||||
|
@ -214,6 +214,7 @@ gst_alsa_mixer_element_change_state (GstElement * element,
|
|||
this->mixer = gst_alsa_mixer_new (this->device, GST_ALSA_MIXER_ALL);
|
||||
if (!this->mixer)
|
||||
goto open_failed;
|
||||
_gst_alsa_mixer_set_interface (this->mixer, GST_MIXER (element));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -47,6 +47,7 @@ gst_alsa_mixer_track_get_type (void)
|
|||
sizeof (GstAlsaMixerTrack),
|
||||
0,
|
||||
(GInstanceInitFunc) gst_alsa_mixer_track_init,
|
||||
0
|
||||
};
|
||||
|
||||
track_type =
|
||||
|
@ -239,6 +240,26 @@ gst_alsa_mixer_track_update (GstAlsaMixerTrack * alsa_track)
|
|||
gint i;
|
||||
gint audible = !(track->flags & GST_MIXER_TRACK_MUTE);
|
||||
|
||||
if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_PVOLUME)) {
|
||||
/* update playback volume */
|
||||
for (i = 0; i < track->num_channels; i++) {
|
||||
long vol = 0;
|
||||
|
||||
snd_mixer_selem_get_playback_volume (alsa_track->element, i, &vol);
|
||||
alsa_track->volumes[i] = (gint) vol;
|
||||
}
|
||||
}
|
||||
|
||||
if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_CVOLUME)) {
|
||||
/* update capture volume */
|
||||
for (i = 0; i < track->num_channels; i++) {
|
||||
long vol = 0;
|
||||
|
||||
snd_mixer_selem_get_capture_volume (alsa_track->element, i, &vol);
|
||||
alsa_track->volumes[i] = (gint) vol;
|
||||
}
|
||||
}
|
||||
|
||||
/* Any updates in flags? */
|
||||
if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_PSWITCH)) {
|
||||
int v = 0;
|
||||
|
|
|
@ -26,11 +26,14 @@
|
|||
#include "mixer.h"
|
||||
#include "interfaces-marshal.h"
|
||||
|
||||
#define GST_MIXER_MESSAGE_NAME "gst-mixer-message"
|
||||
|
||||
/**
|
||||
* SECTION:gstmixer
|
||||
* @short_description: Interface for elements that provide mixer operations
|
||||
*/
|
||||
|
||||
#ifndef GST_DISABLE_DEPRECATED
|
||||
enum
|
||||
{
|
||||
SIGNAL_MUTE_TOGGLED,
|
||||
|
@ -40,10 +43,12 @@ enum
|
|||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static void gst_mixer_class_init (GstMixerClass * klass);
|
||||
|
||||
static guint gst_mixer_signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
#endif
|
||||
|
||||
static void gst_mixer_class_init (GstMixerClass * klass);
|
||||
|
||||
GType
|
||||
gst_mixer_get_type (void)
|
||||
{
|
||||
|
@ -74,8 +79,10 @@ gst_mixer_get_type (void)
|
|||
static void
|
||||
gst_mixer_class_init (GstMixerClass * klass)
|
||||
{
|
||||
#ifndef GST_DISABLE_DEPRECATED
|
||||
static gboolean initialized = FALSE;
|
||||
|
||||
/* signals (deprecated) */
|
||||
if (!initialized) {
|
||||
gst_mixer_signals[SIGNAL_RECORD_TOGGLED] =
|
||||
g_signal_new ("record-toggled",
|
||||
|
@ -108,6 +115,7 @@ gst_mixer_class_init (GstMixerClass * klass)
|
|||
|
||||
initialized = TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
klass->mixer_type = GST_MIXER_SOFTWARE;
|
||||
|
||||
|
@ -313,53 +321,411 @@ gst_mixer_get_option (GstMixer * mixer, GstMixerOptions * opts)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_mixer_get_mixer_flags:
|
||||
* @mixer: The #GstMixer implementation
|
||||
*
|
||||
* Get the set of supported flags for this mixer implementation.
|
||||
*
|
||||
* Returns: A set of or-ed GstMixerFlags for supported features.
|
||||
*/
|
||||
GstMixerFlags
|
||||
gst_mixer_get_mixer_flags (GstMixer * mixer)
|
||||
{
|
||||
GstMixerClass *klass;
|
||||
|
||||
g_return_val_if_fail (mixer != NULL, FALSE);
|
||||
klass = GST_MIXER_GET_CLASS (mixer);
|
||||
|
||||
if (klass->get_mixer_flags) {
|
||||
return klass->get_mixer_flags (mixer);
|
||||
}
|
||||
return GST_MIXER_FLAG_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_mixer_mute_toggled:
|
||||
*
|
||||
* @mixer: the #GstMixer (a #GstElement) that owns the track
|
||||
* @track: the GstMixerTrack that has change mute state.
|
||||
* @mute: the new state of the mute flag on the track
|
||||
*
|
||||
* This function is called by the mixer implementation to produce
|
||||
* a notification message on the bus indicating that the given track
|
||||
* has changed mute state.
|
||||
*
|
||||
* This function only works for GstElements that are implementing the
|
||||
* GstMixer interface, and the element needs to have been provided a bus.
|
||||
*/
|
||||
void
|
||||
gst_mixer_mute_toggled (GstMixer * mixer, GstMixerTrack * track, gboolean mute)
|
||||
{
|
||||
GstStructure *s;
|
||||
GstMessage *m;
|
||||
|
||||
g_return_if_fail (mixer != NULL);
|
||||
g_return_if_fail (GST_IS_ELEMENT (mixer));
|
||||
g_return_if_fail (track != NULL);
|
||||
|
||||
g_signal_emit (G_OBJECT (mixer),
|
||||
gst_mixer_signals[SIGNAL_MUTE_TOGGLED], 0, track, mute);
|
||||
s = gst_structure_new (GST_MIXER_MESSAGE_NAME,
|
||||
"type", G_TYPE_STRING, "mute-toggled",
|
||||
"track", GST_TYPE_MIXER_TRACK, track, "mute", G_TYPE_BOOLEAN, mute, NULL);
|
||||
|
||||
g_signal_emit_by_name (G_OBJECT (track), "mute_toggled", mute);
|
||||
m = gst_message_new_element (GST_OBJECT (mixer), s);
|
||||
if (gst_element_post_message (GST_ELEMENT (mixer), m) == FALSE) {
|
||||
GST_WARNING ("This element has no bus, therefore no message sent!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_mixer_record_toggled:
|
||||
*
|
||||
* @mixer: the #GstMixer (a #GstElement) that owns the track
|
||||
* @track: the GstMixerTrack that has changed recording state.
|
||||
* @record: the new state of the record flag on the track
|
||||
*
|
||||
* This function is called by the mixer implementation to produce
|
||||
* a notification message on the bus indicating that the given track
|
||||
* has changed recording state.
|
||||
*
|
||||
* This function only works for GstElements that are implementing the
|
||||
* GstMixer interface, and the element needs to have been provided a bus.
|
||||
*/
|
||||
void
|
||||
gst_mixer_record_toggled (GstMixer * mixer,
|
||||
GstMixerTrack * track, gboolean record)
|
||||
{
|
||||
GstStructure *s;
|
||||
GstMessage *m;
|
||||
|
||||
g_return_if_fail (mixer != NULL);
|
||||
g_return_if_fail (GST_IS_ELEMENT (mixer));
|
||||
g_return_if_fail (track != NULL);
|
||||
|
||||
g_signal_emit (G_OBJECT (mixer),
|
||||
gst_mixer_signals[SIGNAL_RECORD_TOGGLED], 0, track, record);
|
||||
s = gst_structure_new (GST_MIXER_MESSAGE_NAME,
|
||||
"type", G_TYPE_STRING, "record-toggled",
|
||||
"track", GST_TYPE_MIXER_TRACK, track,
|
||||
"record", G_TYPE_BOOLEAN, record, NULL);
|
||||
|
||||
g_signal_emit_by_name (G_OBJECT (track), "record_toggled", record);
|
||||
m = gst_message_new_element (GST_OBJECT (mixer), s);
|
||||
if (gst_element_post_message (GST_ELEMENT (mixer), m) == FALSE) {
|
||||
GST_WARNING ("This element has no bus, therefore no message sent!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_mixer_volume_changed:
|
||||
*
|
||||
* @mixer: the #GstMixer (a #GstElement) that owns the track
|
||||
* @track: the GstMixerTrack that has changed.
|
||||
* @volumes: Array of volume values, one per channel on the mixer track.
|
||||
*
|
||||
* This function is called by the mixer implementation to produce
|
||||
* a notification message on the bus indicating that the volume(s) for the
|
||||
* given track have changed.
|
||||
*
|
||||
* This function only works for GstElements that are implementing the
|
||||
* GstMixer interface, and the element needs to have been provided a bus.
|
||||
*/
|
||||
void
|
||||
gst_mixer_volume_changed (GstMixer * mixer,
|
||||
GstMixerTrack * track, gint * volumes)
|
||||
{
|
||||
GstStructure *s;
|
||||
GstMessage *m;
|
||||
GValue l = { 0, };
|
||||
GValue v = { 0, };
|
||||
gint i;
|
||||
|
||||
g_return_if_fail (mixer != NULL);
|
||||
g_return_if_fail (GST_IS_ELEMENT (mixer));
|
||||
g_return_if_fail (track != NULL);
|
||||
|
||||
g_signal_emit (G_OBJECT (mixer),
|
||||
gst_mixer_signals[SIGNAL_VOLUME_CHANGED], 0, track, volumes);
|
||||
s = gst_structure_new (GST_MIXER_MESSAGE_NAME,
|
||||
"type", G_TYPE_STRING, "volume-changed",
|
||||
"track", GST_TYPE_MIXER_TRACK, track, NULL);
|
||||
|
||||
g_signal_emit_by_name (G_OBJECT (track), "volume_changed", volumes);
|
||||
g_value_init (&l, GST_TYPE_ARRAY);
|
||||
|
||||
g_value_init (&v, G_TYPE_INT);
|
||||
|
||||
/* FIXME 0.11: pass track->num_channels to the function */
|
||||
for (i = 0; i < track->num_channels; ++i) {
|
||||
g_value_set_int (&v, volumes[i]);
|
||||
gst_value_array_append_value (&l, &v);
|
||||
}
|
||||
g_value_unset (&v);
|
||||
|
||||
gst_structure_set_value (s, "volumes", &l);
|
||||
g_value_unset (&l);
|
||||
|
||||
m = gst_message_new_element (GST_OBJECT (mixer), s);
|
||||
if (gst_element_post_message (GST_ELEMENT (mixer), m) == FALSE) {
|
||||
GST_WARNING ("This element has no bus, therefore no message sent!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_mixer_option_changed:
|
||||
*
|
||||
* @mixer: the #GstMixer (a #GstElement) that owns the options
|
||||
* @opts: the GstMixerOptions that has changed value.
|
||||
* @value: the new value of the GstMixerOptions.
|
||||
*
|
||||
* This function is called by the mixer implementation to produce
|
||||
* a notification message on the bus indicating that the given options
|
||||
* object has changed state.
|
||||
*
|
||||
* This function only works for GstElements that are implementing the
|
||||
* GstMixer interface, and the element needs to have been provided a bus.
|
||||
*/
|
||||
void
|
||||
gst_mixer_option_changed (GstMixer * mixer,
|
||||
GstMixerOptions * opts, gchar * value)
|
||||
{
|
||||
GstStructure *s;
|
||||
GstMessage *m;
|
||||
|
||||
g_return_if_fail (mixer != NULL);
|
||||
g_return_if_fail (GST_IS_ELEMENT (mixer));
|
||||
g_return_if_fail (opts != NULL);
|
||||
|
||||
g_signal_emit (G_OBJECT (mixer),
|
||||
gst_mixer_signals[SIGNAL_OPTION_CHANGED], 0, opts, value);
|
||||
s = gst_structure_new (GST_MIXER_MESSAGE_NAME,
|
||||
"type", G_TYPE_STRING, "option-changed",
|
||||
"options", GST_TYPE_MIXER_OPTIONS, opts,
|
||||
"value", G_TYPE_STRING, value, NULL);
|
||||
|
||||
g_signal_emit_by_name (G_OBJECT (opts), "value_changed", value);
|
||||
m = gst_message_new_element (GST_OBJECT (mixer), s);
|
||||
if (gst_element_post_message (GST_ELEMENT (mixer), m) == FALSE) {
|
||||
GST_WARNING ("This element has no bus, therefore no message sent!");
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_mixer_message_is_mixer_message (GstMessage * message)
|
||||
{
|
||||
const GstStructure *s;
|
||||
|
||||
if (message == NULL)
|
||||
return FALSE;
|
||||
if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
|
||||
return FALSE;
|
||||
|
||||
s = gst_message_get_structure (message);
|
||||
return gst_structure_has_name (s, GST_MIXER_MESSAGE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_mixer_message_get_type:
|
||||
* @message: A GstMessage to inspect.
|
||||
*
|
||||
* Check a bus message to see if it is a GstMixer notification
|
||||
* message and return the GstMixerMessageType identifying which
|
||||
* type of notification it is.
|
||||
*
|
||||
* Returns: The type of the GstMixerMessage, or GST_MIXER_MESSAGE_NONE
|
||||
* if the message is not a GstMixer notification.
|
||||
*
|
||||
* Since: 0.10.14
|
||||
*/
|
||||
GstMixerMessageType
|
||||
gst_mixer_message_get_type (GstMessage * message)
|
||||
{
|
||||
const GstStructure *s;
|
||||
const gchar *m_type;
|
||||
|
||||
if (!gst_mixer_message_is_mixer_message (message))
|
||||
return GST_MIXER_MESSAGE_INVALID;
|
||||
|
||||
s = gst_message_get_structure (message);
|
||||
m_type = gst_structure_get_string (s, "type");
|
||||
g_return_val_if_fail (m_type != NULL, GST_MIXER_MESSAGE_INVALID);
|
||||
|
||||
if (g_str_equal (m_type, "mute-toggled"))
|
||||
return GST_MIXER_MESSAGE_MUTE_TOGGLED;
|
||||
else if (g_str_equal (m_type, "record-toggled"))
|
||||
return GST_MIXER_MESSAGE_RECORD_TOGGLED;
|
||||
else if (g_str_equal (m_type, "volume-changed"))
|
||||
return GST_MIXER_MESSAGE_VOLUME_CHANGED;
|
||||
else if (g_str_equal (m_type, "option-changed"))
|
||||
return GST_MIXER_MESSAGE_OPTION_CHANGED;
|
||||
|
||||
return GST_MIXER_MESSAGE_INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_mixer_message_parse_mute_toggled:
|
||||
* @message: A mute-toggled change notification message.
|
||||
* @track: Pointer to hold a GstMixerTrack object, or NULL.
|
||||
* @mute: A pointer to a gboolean variable, or NULL.
|
||||
*
|
||||
* Extracts the contents of a mute-toggled bus message. Reads
|
||||
* the GstMixerTrack that has changed, and the new value of the mute
|
||||
* flag.
|
||||
*
|
||||
* The GstMixerTrack remains valid until the message is freed.
|
||||
*
|
||||
* Since: 0.10.14
|
||||
*/
|
||||
void
|
||||
gst_mixer_message_parse_mute_toggled (GstMessage * message,
|
||||
GstMixerTrack ** track, gboolean * mute)
|
||||
{
|
||||
const GstStructure *s;
|
||||
const gchar *m_type;
|
||||
|
||||
g_return_if_fail (gst_mixer_message_is_mixer_message (message));
|
||||
s = gst_message_get_structure (message);
|
||||
|
||||
m_type = gst_structure_get_string (s, "type");
|
||||
g_return_if_fail (m_type == NULL || g_str_equal (m_type, "mute-toggled"));
|
||||
|
||||
if (track) {
|
||||
const GValue *v = gst_structure_get_value (s, "track");
|
||||
|
||||
g_return_if_fail (v != NULL);
|
||||
*track = (GstMixerTrack *) g_value_get_object (v);
|
||||
g_return_if_fail (GST_IS_MIXER_TRACK (*track));
|
||||
}
|
||||
|
||||
if (mute)
|
||||
g_return_if_fail (gst_structure_get_boolean (s, "mute", mute));
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_mixer_message_parse_record_toggled:
|
||||
* @message: A record-toggled change notification message.
|
||||
* @track: Pointer to hold a GstMixerTrack object, or NULL.
|
||||
* @record: A pointer to a gboolean variable, or NULL.
|
||||
*
|
||||
* Extracts the contents of a record-toggled bus message. Reads
|
||||
* the GstMixerTrack that has changed, and the new value of the
|
||||
* recording flag.
|
||||
*
|
||||
* The GstMixerTrack remains valid until the message is freed.
|
||||
*
|
||||
* Since: 0.10.14
|
||||
*/
|
||||
void
|
||||
gst_mixer_message_parse_record_toggled (GstMessage * message,
|
||||
GstMixerTrack ** track, gboolean * record)
|
||||
{
|
||||
const GstStructure *s;
|
||||
const gchar *m_type;
|
||||
|
||||
g_return_if_fail (gst_mixer_message_is_mixer_message (message));
|
||||
s = gst_message_get_structure (message);
|
||||
|
||||
m_type = gst_structure_get_string (s, "type");
|
||||
g_return_if_fail (m_type == NULL || g_str_equal (m_type, "record-toggled"));
|
||||
|
||||
if (track) {
|
||||
const GValue *v = gst_structure_get_value (s, "track");
|
||||
|
||||
g_return_if_fail (v != NULL);
|
||||
*track = (GstMixerTrack *) g_value_get_object (v);
|
||||
g_return_if_fail (GST_IS_MIXER_TRACK (*track));
|
||||
}
|
||||
|
||||
if (record)
|
||||
g_return_if_fail (gst_structure_get_boolean (s, "record", record));
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_mixer_message_parse_volume_changed:
|
||||
* @message: A volume-changed change notification message.
|
||||
* @track: Pointer to hold a GstMixerTrack object, or NULL.
|
||||
* @volumes: A pointer to receive an array of gint values, or NULL.
|
||||
* @num_channels: Result location to receive the number of channels, or NULL.
|
||||
*
|
||||
* Parses a volume-changed notification message and extracts the track object
|
||||
* it refers to, as well as an array of volumes and the size of the volumes array.
|
||||
*
|
||||
* The track object remains valid until the message is freed.
|
||||
*
|
||||
* The caller must free the array returned in the volumes parameter using g_free
|
||||
* when they are done with it.
|
||||
*
|
||||
* Since: 0.10.14
|
||||
*/
|
||||
void
|
||||
gst_mixer_message_parse_volume_changed (GstMessage * message,
|
||||
GstMixerTrack ** track, gint ** volumes, gint * num_channels)
|
||||
{
|
||||
const GstStructure *s;
|
||||
const gchar *m_type;
|
||||
|
||||
g_return_if_fail (gst_mixer_message_is_mixer_message (message));
|
||||
s = gst_message_get_structure (message);
|
||||
|
||||
m_type = gst_structure_get_string (s, "type");
|
||||
g_return_if_fail (m_type == NULL || g_str_equal (m_type, "volume-changed"));
|
||||
|
||||
if (track) {
|
||||
const GValue *v = gst_structure_get_value (s, "track");
|
||||
|
||||
g_return_if_fail (v != NULL);
|
||||
*track = (GstMixerTrack *) g_value_get_object (v);
|
||||
g_return_if_fail (GST_IS_MIXER_TRACK (*track));
|
||||
}
|
||||
|
||||
if (volumes || num_channels) {
|
||||
gint n_chans, i;
|
||||
const GValue *v = gst_structure_get_value (s, "volumes");
|
||||
|
||||
g_return_if_fail (v != NULL);
|
||||
g_return_if_fail (GST_VALUE_HOLDS_ARRAY (v));
|
||||
|
||||
n_chans = gst_value_array_get_size (v);
|
||||
if (num_channels)
|
||||
*num_channels = n_chans;
|
||||
|
||||
if (volumes) {
|
||||
*volumes = g_new (gint, n_chans);
|
||||
for (i = 0; i < n_chans; i++) {
|
||||
const GValue *e = gst_value_array_get_value (v, i);
|
||||
|
||||
g_return_if_fail (e != NULL && G_VALUE_HOLDS_INT (e));
|
||||
(*volumes)[i] = g_value_get_int (e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_mixer_message_parse_option_changed:
|
||||
* @message: A volume-changed change notification message.
|
||||
* @options: Pointer to hold a GstMixerOptions object, or NULL.
|
||||
* @value: Result location to receive the new options value, or NULL.
|
||||
*
|
||||
* Extracts the GstMixerOptions and new value from a option-changed bus notification
|
||||
* message.
|
||||
*
|
||||
* The options and value returned remain valid until the message is freed.
|
||||
*
|
||||
* Since: 0.10.14
|
||||
*/
|
||||
void
|
||||
gst_mixer_message_parse_option_changed (GstMessage * message,
|
||||
GstMixerOptions ** options, const gchar ** value)
|
||||
{
|
||||
const GstStructure *s;
|
||||
const gchar *m_type;
|
||||
|
||||
g_return_if_fail (gst_mixer_message_is_mixer_message (message));
|
||||
s = gst_message_get_structure (message);
|
||||
|
||||
m_type = gst_structure_get_string (s, "type");
|
||||
g_return_if_fail (m_type == NULL || g_str_equal (m_type, "option-changed"));
|
||||
|
||||
if (options) {
|
||||
const GValue *v = gst_structure_get_value (s, "options");
|
||||
|
||||
g_return_if_fail (v != NULL);
|
||||
*options = (GstMixerOptions *) g_value_get_object (v);
|
||||
g_return_if_fail (GST_IS_MIXER_OPTIONS (*options));
|
||||
}
|
||||
|
||||
if (value)
|
||||
*value = gst_structure_get_string (s, "value");
|
||||
}
|
||||
|
|
|
@ -53,6 +53,44 @@ typedef enum
|
|||
GST_MIXER_SOFTWARE
|
||||
} GstMixerType;
|
||||
|
||||
/**
|
||||
* GstMixerMessageType:
|
||||
* @GST_MIXER_MESSAGE_INVALID: Not a GstMixer message
|
||||
* @GST_MIXER_MESSAGE_MUTE_TOGGLED: A mute-toggled GstMixer message
|
||||
* @GST_MIXER_MESSAGE_RECORD_TOGGLED: A record-toggled GstMixer message
|
||||
* @GST_MIXER_MESSAGE_VOLUME_CHANGED: A volume-changed GstMixer message
|
||||
* @GST_MIXER_MESSAGE_OPTION_CHANGED: An option-changed GstMixer message
|
||||
*
|
||||
* An enumeration for the type of a GstMixer message received on the bus
|
||||
*
|
||||
* Since: 0.10.14
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GST_MIXER_MESSAGE_INVALID,
|
||||
GST_MIXER_MESSAGE_MUTE_TOGGLED,
|
||||
GST_MIXER_MESSAGE_RECORD_TOGGLED,
|
||||
GST_MIXER_MESSAGE_VOLUME_CHANGED,
|
||||
GST_MIXER_MESSAGE_OPTION_CHANGED
|
||||
} GstMixerMessageType;
|
||||
|
||||
/**
|
||||
* GstMixerFlags:
|
||||
* @GST_MIXER_FLAG_NONE: No flags
|
||||
* @GST_MIXER_AUTO_NOTIFICATIONS: The mixer implementation automatically sends
|
||||
* notification messages.
|
||||
*
|
||||
* Flags for supported features. Whether the element automatically sends
|
||||
* notifications on the bus is the only one for now.
|
||||
*
|
||||
* Since: 0.10.14
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GST_MIXER_FLAG_NONE = 0,
|
||||
GST_MIXER_FLAG_AUTO_NOTIFICATIONS = (1<<0)
|
||||
} GstMixerFlags;
|
||||
|
||||
struct _GstMixerClass {
|
||||
GTypeInterface klass;
|
||||
|
||||
|
@ -74,8 +112,8 @@ struct _GstMixerClass {
|
|||
void (* set_record) (GstMixer *mixer,
|
||||
GstMixerTrack *track,
|
||||
gboolean record);
|
||||
|
||||
/* signals */
|
||||
#ifndef GST_DISABLE_DEPRECATED
|
||||
/* signals (deprecated) */
|
||||
void (* mute_toggled) (GstMixer *mixer,
|
||||
GstMixerTrack *channel,
|
||||
gboolean mute);
|
||||
|
@ -85,6 +123,7 @@ struct _GstMixerClass {
|
|||
void (* volume_changed) (GstMixer *mixer,
|
||||
GstMixerTrack *channel,
|
||||
gint *volumes);
|
||||
#endif /* not GST_DISABLE_DEPRECATED */
|
||||
|
||||
void (* set_option) (GstMixer *mixer,
|
||||
GstMixerOptions *opts,
|
||||
|
@ -92,12 +131,16 @@ struct _GstMixerClass {
|
|||
const gchar * (* get_option) (GstMixer *mixer,
|
||||
GstMixerOptions *opts);
|
||||
|
||||
#ifndef GST_DISABLE_DEPRECATED
|
||||
void (* option_changed) (GstMixer *mixer,
|
||||
GstMixerOptions *opts,
|
||||
gchar *option);
|
||||
#endif /* not GST_DISABLE_DEPRECATED */
|
||||
|
||||
GstMixerFlags (* get_mixer_flags) (GstMixer *mixer);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
gpointer _gst_reserved[GST_PADDING-1];
|
||||
};
|
||||
|
||||
GType gst_mixer_get_type (void);
|
||||
|
@ -122,7 +165,7 @@ void gst_mixer_set_option (GstMixer *mixer,
|
|||
const gchar * gst_mixer_get_option (GstMixer *mixer,
|
||||
GstMixerOptions *opts);
|
||||
|
||||
/* trigger signals */
|
||||
/* trigger bus messages */
|
||||
void gst_mixer_mute_toggled (GstMixer *mixer,
|
||||
GstMixerTrack *track,
|
||||
gboolean mute);
|
||||
|
@ -136,6 +179,24 @@ void gst_mixer_option_changed (GstMixer *mixer,
|
|||
GstMixerOptions *opts,
|
||||
gchar *value);
|
||||
|
||||
GstMixerFlags gst_mixer_get_mixer_flags (GstMixer *mixer);
|
||||
|
||||
/* Functions for recognising and parsing GstMixerMessages on the bus */
|
||||
GstMixerMessageType gst_mixer_message_get_type (GstMessage *message);
|
||||
void gst_mixer_message_parse_mute_toggled (GstMessage *message,
|
||||
GstMixerTrack **track,
|
||||
gboolean *mute);
|
||||
void gst_mixer_message_parse_record_toggled (GstMessage *message,
|
||||
GstMixerTrack **track,
|
||||
gboolean *record);
|
||||
void gst_mixer_message_parse_volume_changed (GstMessage *message,
|
||||
GstMixerTrack **track,
|
||||
gint **volumes,
|
||||
gint *num_channels);
|
||||
void gst_mixer_message_parse_option_changed (GstMessage *message,
|
||||
GstMixerOptions **options,
|
||||
const gchar **value);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_MIXER_H__ */
|
||||
|
|
|
@ -25,19 +25,21 @@
|
|||
|
||||
#include "mixeroptions.h"
|
||||
|
||||
#ifndef GST_DISABLE_DEPRECATED
|
||||
enum
|
||||
{
|
||||
/* FILL ME */
|
||||
SIGNAL_OPTION_CHANGED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
#endif
|
||||
|
||||
static void gst_mixer_options_class_init (GstMixerOptionsClass * klass);
|
||||
static void gst_mixer_options_init (GstMixerOptions * mixer);
|
||||
static void gst_mixer_options_dispose (GObject * object);
|
||||
|
||||
static GObjectClass *parent_class = NULL;
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
GType
|
||||
gst_mixer_options_get_type (void)
|
||||
|
@ -72,13 +74,14 @@ gst_mixer_options_class_init (GstMixerOptionsClass * klass)
|
|||
GObjectClass *object_klass = (GObjectClass *) klass;
|
||||
|
||||
parent_class = g_type_class_peek_parent (klass);
|
||||
|
||||
#ifndef GST_DISABLE_DEPRECATED
|
||||
signals[SIGNAL_OPTION_CHANGED] =
|
||||
g_signal_new ("option_changed", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GstMixerOptionsClass, option_changed),
|
||||
NULL, NULL, g_cclosure_marshal_VOID__STRING,
|
||||
G_TYPE_NONE, 1, G_TYPE_STRING);
|
||||
#endif
|
||||
|
||||
object_klass->dispose = gst_mixer_options_dispose;
|
||||
}
|
||||
|
|
|
@ -57,9 +57,11 @@ struct _GstMixerOptions {
|
|||
struct _GstMixerOptionsClass {
|
||||
GstMixerTrackClass parent;
|
||||
|
||||
#ifndef GST_DISABLE_DEPRECATED
|
||||
/* signals */
|
||||
void (* option_changed) (GstMixerOptions *opts,
|
||||
gchar *value);
|
||||
#endif /* not GST_DISABLE_DEPRECATED */
|
||||
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#endif
|
||||
|
||||
#include "mixertrack.h"
|
||||
|
||||
#ifndef GST_DISABLE_DEPRECATED
|
||||
enum
|
||||
{
|
||||
/* FILL ME */
|
||||
|
@ -33,6 +33,8 @@ enum
|
|||
SIGNAL_MUTE_TOGGLED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -55,7 +57,6 @@ static void gst_mixer_track_set_property (GObject * object, guint prop_id,
|
|||
const GValue * value, GParamSpec * pspec);
|
||||
|
||||
static GObjectClass *parent_class = NULL;
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
GType
|
||||
gst_mixer_track_get_type (void)
|
||||
|
@ -135,6 +136,7 @@ gst_mixer_track_class_init (GstMixerTrackClass * klass)
|
|||
"The number of channels contained within the track",
|
||||
0, G_MAXINT, 0, G_PARAM_READABLE));
|
||||
|
||||
#ifndef GST_DISABLE_DEPRECATED
|
||||
signals[SIGNAL_RECORD_TOGGLED] =
|
||||
g_signal_new ("record_toggled", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
|
@ -156,6 +158,7 @@ gst_mixer_track_class_init (GstMixerTrackClass * klass)
|
|||
volume_changed),
|
||||
NULL, NULL, g_cclosure_marshal_VOID__POINTER,
|
||||
G_TYPE_NONE, 1, G_TYPE_POINTER);
|
||||
#endif
|
||||
|
||||
object_klass->dispose = gst_mixer_track_dispose;
|
||||
}
|
||||
|
|
|
@ -88,13 +88,15 @@ struct _GstMixerTrack {
|
|||
struct _GstMixerTrackClass {
|
||||
GObjectClass parent;
|
||||
|
||||
/* signals */
|
||||
#ifndef GST_DISABLE_DEPRECATED
|
||||
/* signals (deprecated) */
|
||||
void (* mute_toggled) (GstMixerTrack *channel,
|
||||
gboolean mute);
|
||||
void (* record_toggled) (GstMixerTrack *channel,
|
||||
gboolean record);
|
||||
void (* volume_changed) (GstMixerTrack *channel,
|
||||
gint *volumes);
|
||||
#endif /* not GST_DISABLE_DEPRECATED */
|
||||
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
|
|
@ -89,6 +89,7 @@ check_PROGRAMS = \
|
|||
gst/typefindfunctions \
|
||||
libs/audio \
|
||||
libs/cddabasesrc \
|
||||
libs/mixer \
|
||||
libs/netbuffer \
|
||||
libs/pbutils \
|
||||
libs/tag \
|
||||
|
@ -138,6 +139,16 @@ libs_cddabasesrc_LDADD = \
|
|||
$(GST_BASE_LIBS) \
|
||||
$(LDADD)
|
||||
|
||||
libs_mixer_CFLAGS = \
|
||||
$(GST_PLUGINS_BASE_CFLAGS) \
|
||||
$(GST_BASE_CFLAGS) \
|
||||
$(AM_CFLAGS)
|
||||
|
||||
libs_mixer_LDADD = \
|
||||
$(top_builddir)/gst-libs/gst/interfaces/libgstinterfaces-@GST_MAJORMINOR@.la \
|
||||
$(GST_BASE_LIBS) \
|
||||
$(LDADD)
|
||||
|
||||
libs_netbuffer_CFLAGS = \
|
||||
$(GST_PLUGINS_BASE_CFLAGS) \
|
||||
$(AM_CFLAGS)
|
||||
|
|
222
tests/check/libs/mixer.c
Normal file
222
tests/check/libs/mixer.c
Normal file
|
@ -0,0 +1,222 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* unit tests for audio support library
|
||||
*
|
||||
* Copyright (C) 2007 Jan Schmidt <thaytan@noraisin.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/check/gstcheck.h>
|
||||
|
||||
#include <gst/interfaces/mixer.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define TEST_ELEMENT_TYPE (test_element_get_type())
|
||||
|
||||
typedef struct TestElement TestElement;
|
||||
typedef struct TestElementClass TestElementClass;
|
||||
|
||||
struct TestElement
|
||||
{
|
||||
GstElement parent;
|
||||
};
|
||||
|
||||
struct TestElementClass
|
||||
{
|
||||
GstElementClass parent_class;
|
||||
};
|
||||
|
||||
static void init_interface (GType type);
|
||||
static void gst_implements_interface_init (GstImplementsInterfaceClass * klass);
|
||||
|
||||
GST_BOILERPLATE_FULL (TestElement, test_element, GstElement, GST_TYPE_ELEMENT,
|
||||
init_interface);
|
||||
|
||||
static void
|
||||
test_element_mixer_interface_init (GstMixerClass * klass)
|
||||
{
|
||||
/* Not actually implementing any interfaces for this test atm */
|
||||
}
|
||||
|
||||
static void
|
||||
init_interface (GType type)
|
||||
{
|
||||
static const GInterfaceInfo mixer_iface_info = {
|
||||
(GInterfaceInitFunc) test_element_mixer_interface_init,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
static const GInterfaceInfo implements_iface_info = {
|
||||
(GInterfaceInitFunc) gst_implements_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);
|
||||
}
|
||||
|
||||
static void
|
||||
test_element_base_init (gpointer klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
test_element_class_init (TestElementClass * klass)
|
||||
{
|
||||
}
|
||||
|
||||
static gboolean
|
||||
test_element_interface_supported (TestElement * this, GType interface_type)
|
||||
{
|
||||
if (interface_type == GST_TYPE_MIXER)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_implements_interface_init (GstImplementsInterfaceClass * klass)
|
||||
{
|
||||
klass->supported = (gpointer) test_element_interface_supported;
|
||||
}
|
||||
|
||||
static void
|
||||
test_element_init (TestElement * this, TestElementClass * klass)
|
||||
{
|
||||
}
|
||||
|
||||
GST_START_TEST (test_messages)
|
||||
{
|
||||
/* Create an empty GstElement that has a GstMixer interface and then
|
||||
* send some notifications and validate them */
|
||||
GstElement *test_element =
|
||||
(GstElement *) g_object_new (TEST_ELEMENT_TYPE, NULL);
|
||||
GstBus *bus = gst_bus_new ();
|
||||
GstMixerTrack *mtrack = g_object_new (GST_TYPE_MIXER_TRACK, NULL);
|
||||
GstMixerOptions *mopts = g_object_new (GST_TYPE_MIXER_OPTIONS, NULL);
|
||||
GstMixerTrack *t;
|
||||
GstMixerOptions *o;
|
||||
gint vols_in[2] = { 50, 75 };
|
||||
gboolean mute, record;
|
||||
gint *vols_out;
|
||||
gint n_chans, i;
|
||||
const gchar *val;
|
||||
GstMessage *message;
|
||||
|
||||
mtrack->num_channels = 2;
|
||||
mtrack->flags = GST_MIXER_TRACK_MUTE | GST_MIXER_TRACK_RECORD;
|
||||
|
||||
gst_element_set_bus (test_element, bus);
|
||||
|
||||
/* Test mute-toggled */
|
||||
gst_mixer_mute_toggled (GST_MIXER (test_element), mtrack, TRUE);
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, GST_CLOCK_TIME_NONE);
|
||||
fail_if (message == NULL);
|
||||
fail_unless (gst_mixer_message_get_type (message) ==
|
||||
GST_MIXER_MESSAGE_MUTE_TOGGLED);
|
||||
/* Test that we can pass NULL args */
|
||||
gst_mixer_message_parse_mute_toggled (message, NULL, NULL);
|
||||
/* Test the parsing */
|
||||
gst_mixer_message_parse_mute_toggled (message, &t, &mute);
|
||||
fail_unless (t == mtrack);
|
||||
fail_unless (mute == TRUE);
|
||||
gst_message_unref (message);
|
||||
|
||||
/* Test record-toggled */
|
||||
gst_mixer_record_toggled (GST_MIXER (test_element), mtrack, TRUE);
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, GST_CLOCK_TIME_NONE);
|
||||
fail_if (message == NULL);
|
||||
fail_unless (gst_mixer_message_get_type (message) ==
|
||||
GST_MIXER_MESSAGE_RECORD_TOGGLED);
|
||||
gst_mixer_message_parse_record_toggled (message, NULL, NULL);
|
||||
gst_mixer_message_parse_record_toggled (message, &t, &record);
|
||||
fail_unless (t == mtrack);
|
||||
fail_unless (record == TRUE);
|
||||
gst_message_unref (message);
|
||||
|
||||
/* Test volume-changed */
|
||||
gst_mixer_volume_changed (GST_MIXER (test_element), mtrack, vols_in);
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, GST_CLOCK_TIME_NONE);
|
||||
fail_if (message == NULL);
|
||||
fail_unless (gst_mixer_message_get_type (message) ==
|
||||
GST_MIXER_MESSAGE_VOLUME_CHANGED);
|
||||
gst_mixer_message_parse_volume_changed (message, NULL, NULL, NULL);
|
||||
gst_mixer_message_parse_volume_changed (message, NULL, NULL, &n_chans);
|
||||
fail_unless (n_chans == 2);
|
||||
gst_mixer_message_parse_volume_changed (message, &t, &vols_out, &n_chans);
|
||||
fail_unless (mtrack == t);
|
||||
for (i = 0; i < n_chans; i++)
|
||||
fail_unless (vols_out[i] == vols_in[i]);
|
||||
|
||||
gst_message_unref (message);
|
||||
g_free (vols_out);
|
||||
|
||||
/* Test options-changed */
|
||||
gst_mixer_option_changed (GST_MIXER (test_element), mopts, "TESTING");
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, GST_CLOCK_TIME_NONE);
|
||||
fail_if (message == NULL);
|
||||
fail_unless (gst_mixer_message_get_type (message) ==
|
||||
GST_MIXER_MESSAGE_OPTION_CHANGED);
|
||||
gst_mixer_message_parse_option_changed (message, NULL, NULL);
|
||||
gst_mixer_message_parse_option_changed (message, &o, &val);
|
||||
fail_unless (o == mopts);
|
||||
fail_unless (g_str_equal (val, "TESTING"));
|
||||
gst_message_unref (message);
|
||||
|
||||
gst_object_unref (mtrack);
|
||||
gst_object_unref (mopts);
|
||||
gst_object_unref (bus);
|
||||
gst_object_unref (test_element);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
mixer_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("mixer interface");
|
||||
TCase *tc_chain = tcase_create ("notifications");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
tcase_add_test (tc_chain, test_messages);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int nf;
|
||||
|
||||
Suite *s = mixer_suite ();
|
||||
SRunner *sr = srunner_create (s);
|
||||
|
||||
gst_check_init (&argc, &argv);
|
||||
|
||||
srunner_run_all (sr, CK_NORMAL);
|
||||
nf = srunner_ntests_failed (sr);
|
||||
srunner_free (sr);
|
||||
|
||||
return nf;
|
||||
}
|
Loading…
Reference in a new issue