mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 12:11:13 +00:00
pulse: Add device monitors
https://bugzilla.gnome.org/show_bug.cgi?id=678402
This commit is contained in:
parent
fdceedb77c
commit
019a0009af
4 changed files with 776 additions and 0 deletions
|
@ -4,6 +4,7 @@ libgstpulse_la_SOURCES = \
|
||||||
plugin.c \
|
plugin.c \
|
||||||
pulsesink.c \
|
pulsesink.c \
|
||||||
pulsesrc.c \
|
pulsesrc.c \
|
||||||
|
pulsedevicemonitor.c \
|
||||||
pulseutil.c
|
pulseutil.c
|
||||||
|
|
||||||
libgstpulse_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(PULSE_CFLAGS)
|
libgstpulse_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(PULSE_CFLAGS)
|
||||||
|
@ -16,5 +17,6 @@ libgstpulse_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
pulsesink.h \
|
pulsesink.h \
|
||||||
pulsesrc.h \
|
pulsesrc.h \
|
||||||
|
pulsedevicemonitor.h \
|
||||||
pulseutil.h
|
pulseutil.h
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
#include "pulsesink.h"
|
#include "pulsesink.h"
|
||||||
#include "pulsesrc.h"
|
#include "pulsesrc.h"
|
||||||
|
#include "pulsedevicemonitor.h"
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY (pulse_debug);
|
GST_DEBUG_CATEGORY (pulse_debug);
|
||||||
|
|
||||||
|
@ -48,6 +49,10 @@ plugin_init (GstPlugin * plugin)
|
||||||
GST_TYPE_PULSESRC))
|
GST_TYPE_PULSESRC))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
if (!gst_device_monitor_register (plugin, "pulsemonitor",
|
||||||
|
GST_RANK_PRIMARY, GST_TYPE_PULSE_DEVICE_MONITOR))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_INIT (pulse_debug, "pulse", 0, "PulseAudio elements");
|
GST_DEBUG_CATEGORY_INIT (pulse_debug, "pulse", 0, "PulseAudio elements");
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
672
ext/pulse/pulsedevicemonitor.c
Normal file
672
ext/pulse/pulsedevicemonitor.c
Normal file
|
@ -0,0 +1,672 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 2012 Olivier Crete <olivier.crete@collabora.com>
|
||||||
|
*
|
||||||
|
* gstv4l2devicemonitor.c: V4l2 device probing and monitoring
|
||||||
|
*
|
||||||
|
* 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 "pulsedevicemonitor.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
|
||||||
|
#include "pulsesrc.h"
|
||||||
|
#include "pulsesink.h"
|
||||||
|
#include "pulseutil.h"
|
||||||
|
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_EXTERN (pulse_debug);
|
||||||
|
#define GST_CAT_DEFAULT pulse_debug
|
||||||
|
|
||||||
|
|
||||||
|
static GstDevice *gst_pulse_device_new (guint id,
|
||||||
|
const gchar * device_name, GstCaps * caps, const gchar * internal_name,
|
||||||
|
GstPulseDeviceType type);
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GstPulseDeviceMonitor, gst_pulse_device_monitor,
|
||||||
|
GST_TYPE_DEVICE_MONITOR);
|
||||||
|
|
||||||
|
static void gst_pulse_device_monitor_finalize (GObject * object);
|
||||||
|
static void gst_pulse_device_monitor_set_property (GObject * object,
|
||||||
|
guint prop_id, const GValue * value, GParamSpec * pspec);
|
||||||
|
static void gst_pulse_device_monitor_get_property (GObject * object,
|
||||||
|
guint prop_id, GValue * value, GParamSpec * pspec);
|
||||||
|
|
||||||
|
|
||||||
|
static GList *gst_pulse_device_monitor_probe (GstDeviceMonitor * monitor);
|
||||||
|
static gboolean gst_pulse_device_monitor_start (GstDeviceMonitor * monitor);
|
||||||
|
static void gst_pulse_device_monitor_stop (GstDeviceMonitor * monitor);
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_0,
|
||||||
|
PROP_SERVER,
|
||||||
|
PROP_CLIENT_NAME,
|
||||||
|
PROP_LAST
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_pulse_device_monitor_class_init (GstPulseDeviceMonitorClass * klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
GstDeviceMonitorClass *dm_class = GST_DEVICE_MONITOR_CLASS (klass);
|
||||||
|
gchar *client_name;
|
||||||
|
|
||||||
|
gobject_class->set_property = gst_pulse_device_monitor_set_property;
|
||||||
|
gobject_class->get_property = gst_pulse_device_monitor_get_property;
|
||||||
|
gobject_class->finalize = gst_pulse_device_monitor_finalize;
|
||||||
|
|
||||||
|
dm_class->probe = gst_pulse_device_monitor_probe;
|
||||||
|
dm_class->start = gst_pulse_device_monitor_start;
|
||||||
|
dm_class->stop = gst_pulse_device_monitor_stop;
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class,
|
||||||
|
PROP_SERVER,
|
||||||
|
g_param_spec_string ("server", "Server",
|
||||||
|
"The PulseAudio server to connect to", NULL,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
client_name = gst_pulse_client_name ();
|
||||||
|
g_object_class_install_property (gobject_class,
|
||||||
|
PROP_CLIENT_NAME,
|
||||||
|
g_param_spec_string ("client-name", "Client Name",
|
||||||
|
"The PulseAudio client_name_to_use", client_name,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
|
||||||
|
GST_PARAM_MUTABLE_READY));
|
||||||
|
g_free (client_name);
|
||||||
|
|
||||||
|
gst_device_monitor_class_set_static_metadata (dm_class,
|
||||||
|
"PulseAudio Device Monitor", "Sink/Source/Audio",
|
||||||
|
"List and monitor PulseAudio source and sink devices",
|
||||||
|
"Olivier Crete <olivier.crete@collabora.com>");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_pulse_device_monitor_init (GstPulseDeviceMonitor * self)
|
||||||
|
{
|
||||||
|
self->client_name = gst_pulse_client_name ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_pulse_device_monitor_finalize (GObject * object)
|
||||||
|
{
|
||||||
|
GstPulseDeviceMonitor *self = GST_PULSE_DEVICE_MONITOR (object);
|
||||||
|
|
||||||
|
g_free (self->client_name);
|
||||||
|
g_free (self->server);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (gst_pulse_device_monitor_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_pulse_device_monitor_set_property (GObject * object,
|
||||||
|
guint prop_id, const GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstPulseDeviceMonitor *self = GST_PULSE_DEVICE_MONITOR (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_SERVER:
|
||||||
|
g_free (self->server);
|
||||||
|
self->server = g_value_dup_string (value);
|
||||||
|
break;
|
||||||
|
case PROP_CLIENT_NAME:
|
||||||
|
g_free (self->client_name);
|
||||||
|
if (!g_value_get_string (value)) {
|
||||||
|
GST_WARNING_OBJECT (self,
|
||||||
|
"Empty PulseAudio client name not allowed. "
|
||||||
|
"Resetting to default value");
|
||||||
|
self->client_name = gst_pulse_client_name ();
|
||||||
|
} else
|
||||||
|
self->client_name = g_value_dup_string (value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_pulse_device_monitor_get_property (GObject * object,
|
||||||
|
guint prop_id, GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstPulseDeviceMonitor *self = GST_PULSE_DEVICE_MONITOR (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_SERVER:
|
||||||
|
g_value_set_string (value, self->server);
|
||||||
|
break;
|
||||||
|
case PROP_CLIENT_NAME:
|
||||||
|
g_value_set_string (value, self->client_name);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
context_state_cb (pa_context * c, void *userdata)
|
||||||
|
{
|
||||||
|
GstPulseDeviceMonitor *self = userdata;
|
||||||
|
|
||||||
|
switch (pa_context_get_state (c)) {
|
||||||
|
case PA_CONTEXT_READY:
|
||||||
|
case PA_CONTEXT_TERMINATED:
|
||||||
|
case PA_CONTEXT_FAILED:
|
||||||
|
pa_threaded_mainloop_signal (self->mainloop, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PA_CONTEXT_UNCONNECTED:
|
||||||
|
case PA_CONTEXT_CONNECTING:
|
||||||
|
case PA_CONTEXT_AUTHORIZING:
|
||||||
|
case PA_CONTEXT_SETTING_NAME:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstDevice *
|
||||||
|
new_source (const pa_source_info * info)
|
||||||
|
{
|
||||||
|
GstCaps *caps;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
caps = gst_caps_new_empty ();
|
||||||
|
|
||||||
|
for (i = 0; i < info->n_formats; i++)
|
||||||
|
gst_caps_append (caps, gst_pulse_format_info_to_caps (info->formats[i]));
|
||||||
|
|
||||||
|
return gst_pulse_device_new (info->index, info->description,
|
||||||
|
caps, info->name, GST_PULSE_DEVICE_TYPE_SOURCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstDevice *
|
||||||
|
new_sink (const pa_sink_info * info)
|
||||||
|
{
|
||||||
|
GstCaps *caps;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
caps = gst_caps_new_empty ();
|
||||||
|
|
||||||
|
for (i = 0; i < info->n_formats; i++)
|
||||||
|
gst_caps_append (caps, gst_pulse_format_info_to_caps (info->formats[i]));
|
||||||
|
|
||||||
|
return gst_pulse_device_new (info->index, info->description,
|
||||||
|
caps, info->name, GST_PULSE_DEVICE_TYPE_SINK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_source_info_cb (pa_context * context,
|
||||||
|
const pa_source_info * info, int eol, void *userdata)
|
||||||
|
{
|
||||||
|
GstPulseDeviceMonitor *self = userdata;
|
||||||
|
GstDevice *dev;
|
||||||
|
|
||||||
|
if (eol) {
|
||||||
|
pa_threaded_mainloop_signal (self->mainloop, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev = new_source (info);
|
||||||
|
|
||||||
|
if (dev)
|
||||||
|
gst_device_monitor_device_add (GST_DEVICE_MONITOR (self), dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_sink_info_cb (pa_context * context,
|
||||||
|
const pa_sink_info * info, int eol, void *userdata)
|
||||||
|
{
|
||||||
|
GstPulseDeviceMonitor *self = userdata;
|
||||||
|
GstDevice *dev;
|
||||||
|
|
||||||
|
if (eol) {
|
||||||
|
pa_threaded_mainloop_signal (self->mainloop, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev = new_sink (info);
|
||||||
|
|
||||||
|
if (dev)
|
||||||
|
gst_device_monitor_device_add (GST_DEVICE_MONITOR (self), dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
context_subscribe_cb (pa_context * context, pa_subscription_event_type_t type,
|
||||||
|
uint32_t idx, void *userdata)
|
||||||
|
{
|
||||||
|
GstPulseDeviceMonitor *self = userdata;
|
||||||
|
GstDeviceMonitor *monitor = userdata;
|
||||||
|
|
||||||
|
|
||||||
|
if (!(type & (PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_SINK)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
|
||||||
|
/* Microphone in the source output has changed */
|
||||||
|
|
||||||
|
if (type & PA_SUBSCRIPTION_EVENT_SOURCE)
|
||||||
|
pa_context_get_source_info_by_index (context, idx, get_source_info_cb,
|
||||||
|
self);
|
||||||
|
else if (type & PA_SUBSCRIPTION_EVENT_SINK)
|
||||||
|
pa_context_get_sink_info_by_index (context, idx, get_sink_info_cb, self);
|
||||||
|
} else if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
|
||||||
|
PA_SUBSCRIPTION_EVENT_REMOVE) {
|
||||||
|
GstPulseDevice *dev = NULL;
|
||||||
|
GList *item;
|
||||||
|
|
||||||
|
GST_OBJECT_LOCK (self);
|
||||||
|
for (item = monitor->devices; item; item = item->next) {
|
||||||
|
dev = item->data;
|
||||||
|
|
||||||
|
if (dev->device_index == idx) {
|
||||||
|
gst_object_ref (dev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dev = NULL;
|
||||||
|
}
|
||||||
|
GST_OBJECT_UNLOCK (self);
|
||||||
|
|
||||||
|
if (dev) {
|
||||||
|
gst_device_monitor_device_remove (GST_DEVICE_MONITOR (self),
|
||||||
|
GST_DEVICE (dev));
|
||||||
|
gst_object_unref (dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_source_info_list_cb (pa_context * context, const pa_source_info * info,
|
||||||
|
int eol, void *userdata)
|
||||||
|
{
|
||||||
|
GList **devices = userdata;
|
||||||
|
|
||||||
|
if (eol)
|
||||||
|
return;
|
||||||
|
|
||||||
|
*devices = g_list_prepend (*devices, gst_object_ref_sink (new_source (info)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_sink_info_list_cb (pa_context * context, const pa_sink_info * info,
|
||||||
|
int eol, void *userdata)
|
||||||
|
{
|
||||||
|
GList **devices = userdata;
|
||||||
|
|
||||||
|
if (eol)
|
||||||
|
return;
|
||||||
|
|
||||||
|
*devices = g_list_prepend (*devices, gst_object_ref_sink (new_sink (info)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static GList *
|
||||||
|
gst_pulse_device_monitor_probe (GstDeviceMonitor * monitor)
|
||||||
|
{
|
||||||
|
GstPulseDeviceMonitor *self = GST_PULSE_DEVICE_MONITOR (monitor);
|
||||||
|
GList *devices = NULL;
|
||||||
|
pa_mainloop *m = NULL;
|
||||||
|
pa_context *c = NULL;
|
||||||
|
pa_operation *o;
|
||||||
|
|
||||||
|
if (!(m = pa_mainloop_new ()))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!(c = pa_context_new (pa_mainloop_get_api (m), self->client_name))) {
|
||||||
|
GST_ERROR_OBJECT (self, "Failed to create context");
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pa_context_connect (c, self->server, 0, NULL) < 0) {
|
||||||
|
GST_ERROR_OBJECT (self, "Failed to connect: %s",
|
||||||
|
pa_strerror (pa_context_errno (self->context)));
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
pa_context_state_t state;
|
||||||
|
|
||||||
|
state = pa_context_get_state (c);
|
||||||
|
|
||||||
|
if (!PA_CONTEXT_IS_GOOD (state)) {
|
||||||
|
GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ("Failed to connect: %s",
|
||||||
|
pa_strerror (pa_context_errno (c))), (NULL));
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == PA_CONTEXT_READY)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Wait until the context is ready */
|
||||||
|
if (pa_mainloop_iterate (m, TRUE, NULL) < 0)
|
||||||
|
goto failed;
|
||||||
|
|
||||||
|
}
|
||||||
|
GST_DEBUG_OBJECT (self, "connected");
|
||||||
|
|
||||||
|
o = pa_context_get_sink_info_list (c, get_sink_info_list_cb, &devices);
|
||||||
|
while (pa_operation_get_state (o) == PA_OPERATION_RUNNING &&
|
||||||
|
pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
|
||||||
|
if (pa_mainloop_iterate (m, TRUE, NULL) < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pa_operation_unref (o);
|
||||||
|
|
||||||
|
o = pa_context_get_source_info_list (c, get_source_info_list_cb, &devices);
|
||||||
|
while (pa_operation_get_state (o) == PA_OPERATION_RUNNING &&
|
||||||
|
pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
|
||||||
|
if (pa_mainloop_iterate (m, TRUE, NULL) < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pa_operation_unref (o);
|
||||||
|
|
||||||
|
pa_context_disconnect (c);
|
||||||
|
pa_mainloop_free (m);
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
|
||||||
|
failed:
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_pulse_device_monitor_start (GstDeviceMonitor * monitor)
|
||||||
|
{
|
||||||
|
GstPulseDeviceMonitor *self = GST_PULSE_DEVICE_MONITOR (monitor);
|
||||||
|
pa_operation *initial_operation;
|
||||||
|
|
||||||
|
if (!(self->mainloop = pa_threaded_mainloop_new ())) {
|
||||||
|
GST_ERROR_OBJECT (self, "Could not create pulseaudio mainloop");
|
||||||
|
goto mainloop_failed;
|
||||||
|
}
|
||||||
|
if (pa_threaded_mainloop_start (self->mainloop) < 0) {
|
||||||
|
GST_ERROR_OBJECT (self, "Could not start pulseaudio mainloop");
|
||||||
|
pa_threaded_mainloop_free (self->mainloop);
|
||||||
|
self->mainloop = NULL;
|
||||||
|
goto mainloop_failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_threaded_mainloop_lock (self->mainloop);
|
||||||
|
|
||||||
|
if (!(self->context =
|
||||||
|
pa_context_new (pa_threaded_mainloop_get_api (self->mainloop),
|
||||||
|
self->client_name))) {
|
||||||
|
GST_ERROR_OBJECT (self, "Failed to create context");
|
||||||
|
goto unlock_and_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_context_set_state_callback (self->context, context_state_cb, self);
|
||||||
|
pa_context_set_subscribe_callback (self->context, context_subscribe_cb, self);
|
||||||
|
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "connect to server %s", GST_STR_NULL (self->server));
|
||||||
|
|
||||||
|
if (pa_context_connect (self->context, self->server, 0, NULL) < 0) {
|
||||||
|
GST_ERROR_OBJECT (self, "Failed to connect: %s",
|
||||||
|
pa_strerror (pa_context_errno (self->context)));
|
||||||
|
goto unlock_and_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
pa_context_state_t state;
|
||||||
|
|
||||||
|
state = pa_context_get_state (self->context);
|
||||||
|
|
||||||
|
if (!PA_CONTEXT_IS_GOOD (state)) {
|
||||||
|
GST_ERROR_OBJECT (self, "Failed to connect: %s",
|
||||||
|
pa_strerror (pa_context_errno (self->context)));
|
||||||
|
goto unlock_and_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == PA_CONTEXT_READY)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Wait until the context is ready */
|
||||||
|
pa_threaded_mainloop_wait (self->mainloop);
|
||||||
|
}
|
||||||
|
GST_DEBUG_OBJECT (self, "connected");
|
||||||
|
|
||||||
|
pa_context_subscribe (self->context,
|
||||||
|
PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SINK, NULL, NULL);
|
||||||
|
|
||||||
|
initial_operation = pa_context_get_source_info_list (self->context,
|
||||||
|
get_source_info_cb, self);
|
||||||
|
while (pa_operation_get_state (initial_operation) == PA_OPERATION_RUNNING) {
|
||||||
|
if (!PA_CONTEXT_IS_GOOD (pa_context_get_state ((self->context))))
|
||||||
|
goto cancel_and_fail;
|
||||||
|
|
||||||
|
pa_threaded_mainloop_wait (self->mainloop);
|
||||||
|
}
|
||||||
|
pa_operation_unref (initial_operation);
|
||||||
|
|
||||||
|
initial_operation = pa_context_get_sink_info_list (self->context,
|
||||||
|
get_sink_info_cb, self);
|
||||||
|
if (!initial_operation)
|
||||||
|
goto unlock_and_fail;
|
||||||
|
while (pa_operation_get_state (initial_operation) == PA_OPERATION_RUNNING) {
|
||||||
|
if (!PA_CONTEXT_IS_GOOD (pa_context_get_state ((self->context))))
|
||||||
|
goto cancel_and_fail;
|
||||||
|
|
||||||
|
pa_threaded_mainloop_wait (self->mainloop);
|
||||||
|
}
|
||||||
|
pa_operation_unref (initial_operation);
|
||||||
|
|
||||||
|
pa_threaded_mainloop_unlock (self->mainloop);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
unlock_and_fail:
|
||||||
|
pa_threaded_mainloop_unlock (self->mainloop);
|
||||||
|
gst_pulse_device_monitor_stop (monitor);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
mainloop_failed:
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
cancel_and_fail:
|
||||||
|
pa_operation_cancel (initial_operation);
|
||||||
|
pa_operation_unref (initial_operation);
|
||||||
|
goto unlock_and_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_pulse_device_monitor_stop (GstDeviceMonitor * monitor)
|
||||||
|
{
|
||||||
|
GstPulseDeviceMonitor *self = GST_PULSE_DEVICE_MONITOR (monitor);
|
||||||
|
|
||||||
|
pa_threaded_mainloop_stop (self->mainloop);
|
||||||
|
|
||||||
|
if (self->context) {
|
||||||
|
pa_context_disconnect (self->context);
|
||||||
|
|
||||||
|
/* Make sure we don't get any further callbacks */
|
||||||
|
pa_context_set_state_callback (self->context, NULL, NULL);
|
||||||
|
pa_context_set_subscribe_callback (self->context, NULL, NULL);
|
||||||
|
|
||||||
|
pa_context_unref (self->context);
|
||||||
|
self->context = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_threaded_mainloop_free (self->mainloop);
|
||||||
|
self->mainloop = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_INTERNAL_NAME = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GstPulseDevice, gst_pulse_device, GST_TYPE_DEVICE);
|
||||||
|
|
||||||
|
static void gst_pulse_device_get_property (GObject * object, guint prop_id,
|
||||||
|
GValue * value, GParamSpec * pspec);
|
||||||
|
static void gst_pulse_device_set_property (GObject * object, guint prop_id,
|
||||||
|
const GValue * value, GParamSpec * pspec);
|
||||||
|
static void gst_pulse_device_finalize (GObject * object);
|
||||||
|
static GstElement *gst_pulse_device_create_element (GstDevice * device,
|
||||||
|
const gchar * name);
|
||||||
|
static gboolean gst_pulse_device_reconfigure_element (GstDevice * device,
|
||||||
|
GstElement * element);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_pulse_device_class_init (GstPulseDeviceClass * klass)
|
||||||
|
{
|
||||||
|
GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
dev_class->create_element = gst_pulse_device_create_element;
|
||||||
|
dev_class->reconfigure_element = gst_pulse_device_reconfigure_element;
|
||||||
|
|
||||||
|
object_class->get_property = gst_pulse_device_get_property;
|
||||||
|
object_class->set_property = gst_pulse_device_set_property;
|
||||||
|
object_class->finalize = gst_pulse_device_finalize;
|
||||||
|
|
||||||
|
g_object_class_install_property (object_class, PROP_INTERNAL_NAME,
|
||||||
|
g_param_spec_string ("internal-name", "Internal PulseAudio device name",
|
||||||
|
"The internal name of the PulseAudio device", "",
|
||||||
|
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_pulse_device_init (GstPulseDevice * device)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_pulse_device_finalize (GObject * object)
|
||||||
|
{
|
||||||
|
GstPulseDevice *device = GST_PULSE_DEVICE (object);
|
||||||
|
|
||||||
|
g_free (device->internal_name);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (gst_pulse_device_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstElement *
|
||||||
|
gst_pulse_device_create_element (GstDevice * device, const gchar * name)
|
||||||
|
{
|
||||||
|
GstPulseDevice *pulse_dev = GST_PULSE_DEVICE (device);
|
||||||
|
GstElement *elem;
|
||||||
|
|
||||||
|
elem = gst_element_factory_make (pulse_dev->element, name);
|
||||||
|
g_object_set (elem, "device", pulse_dev->internal_name, NULL);
|
||||||
|
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_pulse_device_reconfigure_element (GstDevice * device, GstElement * element)
|
||||||
|
{
|
||||||
|
GstPulseDevice *pulse_dev = GST_PULSE_DEVICE (device);
|
||||||
|
|
||||||
|
if (!strcmp (pulse_dev->element, "pulsesrc")) {
|
||||||
|
if (!GST_IS_PULSESRC (element))
|
||||||
|
return FALSE;
|
||||||
|
} else if (!strcmp (pulse_dev->element, "pulsesink")) {
|
||||||
|
if (!GST_IS_PULSESINK (element))
|
||||||
|
return FALSE;
|
||||||
|
} else {
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_set (element, "device", pulse_dev->internal_name, NULL);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstDevice *
|
||||||
|
gst_pulse_device_new (guint device_index, const gchar * device_name,
|
||||||
|
GstCaps * caps, const gchar * internal_name, GstPulseDeviceType type)
|
||||||
|
{
|
||||||
|
GstPulseDevice *gstdev;
|
||||||
|
const gchar *element;
|
||||||
|
const gchar *klass;
|
||||||
|
|
||||||
|
g_return_val_if_fail (device_name, NULL);
|
||||||
|
g_return_val_if_fail (internal_name, NULL);
|
||||||
|
g_return_val_if_fail (caps, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case GST_PULSE_DEVICE_TYPE_SOURCE:
|
||||||
|
element = "pulsesrc";
|
||||||
|
klass = "Audio/Source";
|
||||||
|
break;
|
||||||
|
case GST_PULSE_DEVICE_TYPE_SINK:
|
||||||
|
element = "pulsesink";
|
||||||
|
klass = "Audio/Sink";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gstdev = g_object_new (GST_TYPE_PULSE_DEVICE,
|
||||||
|
"display-name", device_name, "caps", caps, "klass", klass,
|
||||||
|
"internal-name", internal_name, NULL);
|
||||||
|
|
||||||
|
gstdev->device_index = device_index;
|
||||||
|
gstdev->element = element;
|
||||||
|
|
||||||
|
return GST_DEVICE (gstdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_pulse_device_get_property (GObject * object, guint prop_id,
|
||||||
|
GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstPulseDevice *device;
|
||||||
|
|
||||||
|
device = GST_PULSE_DEVICE_CAST (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_INTERNAL_NAME:
|
||||||
|
g_value_set_string (value, device->internal_name);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_pulse_device_set_property (GObject * object, guint prop_id,
|
||||||
|
const GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstPulseDevice *device;
|
||||||
|
|
||||||
|
device = GST_PULSE_DEVICE_CAST (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_INTERNAL_NAME:
|
||||||
|
device->internal_name = g_value_dup_string (value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
97
ext/pulse/pulsedevicemonitor.h
Normal file
97
ext/pulse/pulsedevicemonitor.h
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 2012 Olivier Crete <olivier.crete@collabora.com>
|
||||||
|
*
|
||||||
|
* pulsedevicemonitor.h: Device probing and monitoring
|
||||||
|
*
|
||||||
|
* 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_PULSE_DEVICE_MONITOR_H__
|
||||||
|
#define __GST_PULSE_DEVICE_MONITOR_H__
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <pulse/pulseaudio.h>
|
||||||
|
#include <pulse/thread-mainloop.h>
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef struct _GstPulseDeviceMonitor GstPulseDeviceMonitor;
|
||||||
|
typedef struct _GstPulseDeviceMonitorPrivate GstPulseDeviceMonitorPrivate;
|
||||||
|
typedef struct _GstPulseDeviceMonitorClass GstPulseDeviceMonitorClass;
|
||||||
|
|
||||||
|
#define GST_TYPE_PULSE_DEVICE_MONITOR (gst_pulse_device_monitor_get_type())
|
||||||
|
#define GST_IS_PULSE_DEVICE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PULSE_DEVICE_MONITOR))
|
||||||
|
#define GST_IS_PULSE_DEVICE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PULSE_DEVICE_MONITOR))
|
||||||
|
#define GST_PULSE_DEVICE_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PULSE_DEVICE_MONITOR, GstPulseDeviceMonitorClass))
|
||||||
|
#define GST_PULSE_DEVICE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PULSE_DEVICE_MONITOR, GstPulseDeviceMonitor))
|
||||||
|
#define GST_PULSE_DEVICE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE_MONITOR, GstPulseDeviceMonitorClass))
|
||||||
|
#define GST_PULSE_DEVICE_MONITOR_CAST(obj) ((GstPulseDeviceMonitor *)(obj))
|
||||||
|
|
||||||
|
struct _GstPulseDeviceMonitor {
|
||||||
|
GstDeviceMonitor parent;
|
||||||
|
|
||||||
|
gchar *server;
|
||||||
|
gchar *client_name;
|
||||||
|
|
||||||
|
pa_threaded_mainloop *mainloop;
|
||||||
|
pa_context *context;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GST_PULSE_DEVICE_TYPE_SOURCE,
|
||||||
|
GST_PULSE_DEVICE_TYPE_SINK
|
||||||
|
} GstPulseDeviceType;
|
||||||
|
|
||||||
|
struct _GstPulseDeviceMonitorClass {
|
||||||
|
GstDeviceMonitorClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
GType gst_pulse_device_monitor_get_type (void);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _GstPulseDevice GstPulseDevice;
|
||||||
|
typedef struct _GstPulseDevicePrivate GstPulseDevicePrivate;
|
||||||
|
typedef struct _GstPulseDeviceClass GstPulseDeviceClass;
|
||||||
|
|
||||||
|
#define GST_TYPE_PULSE_DEVICE (gst_pulse_device_get_type())
|
||||||
|
#define GST_IS_PULSE_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PULSE_DEVICE))
|
||||||
|
#define GST_IS_PULSE_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PULSE_DEVICE))
|
||||||
|
#define GST_PULSE_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PULSE_DEVICE, GstPulseDeviceClass))
|
||||||
|
#define GST_PULSE_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PULSE_DEVICE, GstPulseDevice))
|
||||||
|
#define GST_PULSE_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE, GstPulseDeviceClass))
|
||||||
|
#define GST_PULSE_DEVICE_CAST(obj) ((GstPulseDevice *)(obj))
|
||||||
|
|
||||||
|
struct _GstPulseDevice {
|
||||||
|
GstDevice parent;
|
||||||
|
|
||||||
|
guint device_index;
|
||||||
|
gchar *internal_name;
|
||||||
|
const gchar *element;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GstPulseDeviceClass {
|
||||||
|
GstDeviceClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
GType gst_pulse_device_get_type (void);
|
||||||
|
|
||||||
|
#endif /* __GST_PULSE_DEVICE_MONITOR_H__ */
|
Loading…
Reference in a new issue