Commit bd55fe88 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

Add service discovery for PulseAudio input

parent 933ddbeb
......@@ -257,6 +257,7 @@ $Id$
* ps: input module for MPEG PS decapsulation
* psychedelic: Psychedelic video filter
* pulse: PulseAudio audio output
* pulselist: PulseAudio audio input services discovery
* pulsesrc: PulseAudio audio input
* puzzle: puzzle video filter
* pva: PVA demuxer
......
......@@ -5,6 +5,16 @@ SOURCES_podcast = podcast.c
SOURCES_mtp = mtp.c
SOURCES_mediadirs = mediadirs.c
libpulselist_plugin_la_SOURCES = \
../audio_output/vlcpulse.c ../audio_output/vlcpulse.h \
pulse.c
libpulselist_plugin_la_CFLAGS = $(AM_CFLAGS) $(PULSE_CFLAGS)
libpulselist_plugin_la_LIBADD = $(AM_LIBADD) $(PULSE_LIBS)
lubpulselist_plugin_la_DEPENDENCIES =
if HAVE_PULSE
libvlc_LTLIBRARIES += libpulselist_plugin.la
endif
libudev_plugin_la_SOURCES = udev.c
libudev_plugin_la_CFLAGS = $(AM_CFLAGS) $(UDEV_CFLAGS)
libudev_plugin_la_LIBADD = $(AM_LIBADD) $(UDEV_LIBS)
......
/**
* @file pulse.c
* @brief List of PulseAudio sources for VLC media player
*/
/*****************************************************************************
* Copyright © 2011 Rémi Denis-Courmont
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <search.h>
#include <assert.h>
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_services_discovery.h>
#include <pulse/pulseaudio.h>
#include "../audio_output/vlcpulse.h"
static int Open (vlc_object_t *);
static void Close (vlc_object_t *);
VLC_SD_PROBE_HELPER("pulse", "Audio capture", SD_CAT_DEVICES);
vlc_module_begin ()
set_shortname (N_("Video capture"))
set_description (N_("Video capture (Video4Linux)"))
set_category (CAT_PLAYLIST)
set_subcategory (SUBCAT_PLAYLIST_SD)
set_capability ("services_discovery", 0)
set_callbacks (Open, Close)
add_shortcut ("pulse", "pa", "pulseaudio", "audio")
VLC_SD_PROBE_SUBMODULE
vlc_module_end ()
struct services_discovery_sys_t
{
void *root;
pa_context *context;
pa_threaded_mainloop *mainloop;
};
static void SourceCallback(pa_context *, const pa_source_info *, int, void *);
static void ContextCallback(pa_context *, pa_subscription_event_type_t,
uint32_t, void *);
static int Open (vlc_object_t *obj)
{
services_discovery_t *sd = (services_discovery_t *)obj;
pa_operation *op;
pa_context *ctx;
services_discovery_sys_t *sys = malloc (sizeof (*sys));
if (unlikely(sys == NULL))
return VLC_ENOMEM;
ctx = vlc_pa_connect (obj, &sys->mainloop);
if (ctx == NULL)
{
free (sys);
return VLC_EGENERIC;
}
sd->p_sys = sys;
sys->context = ctx;
sys->root = NULL;
/* Subscribe for source events */
const pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_SOURCE;
pa_threaded_mainloop_lock (sys->mainloop);
pa_context_set_subscribe_callback (ctx, ContextCallback, sd);
op = pa_context_subscribe (ctx, mask, NULL, NULL);
if (likely(op != NULL))
pa_operation_unref (op);
/* Enumerate existing sources */
op = pa_context_get_source_info_list (ctx, SourceCallback, sd);
if (likely(op != NULL))
{
//while (pa_operation_get_state (op) == PA_OPERATION_RUNNING)
// pa_threaded_mainloop_wait (sys->mainloop);
pa_operation_unref (op);
}
pa_threaded_mainloop_unlock (sys->mainloop);
return VLC_SUCCESS;
/*
error:
pa_threaded_mainloop_unlock (sys->mainloop);
vlc_pa_disconnect (obj, ctx, sys->mainloop);
free (sys);
return VLC_EGENERIC;*/
}
struct device
{
uint32_t index;
input_item_t *item;
services_discovery_t *sd;
};
static void DestroySource (void *data)
{
struct device *d = data;
if (d->sd)
services_discovery_RemoveItem (d->sd, d->item);
vlc_gc_decref (d->item);
free (d);
}
/**
* Compares two devices (to support binary search).
*/
static int cmpsrc (const void *a, const void *b)
{
const uint32_t *pa = a, *pb = b;
uint32_t idxa = *pa, idxb = *pb;
return (idxa != idxb) ? ((idxa < idxb) ? -1 : +1) : 0;
}
/**
* Adds a source.
*/
static int AddSource (services_discovery_t *sd, const pa_source_info *info)
{
services_discovery_sys_t *sys = sd->p_sys;
msg_Dbg (sd, "adding %s (%s)", info->name, info->description);
char *mrl;
if (unlikely(asprintf (&mrl, "pulse://%s", info->name) == -1))
return -1;
input_item_t *item = input_item_NewWithType (mrl, info->description,
0, NULL, 0, -1,
ITEM_TYPE_CARD);
free (mrl);
if (unlikely(item == NULL))
return -1;
struct device *d = malloc (sizeof (*d));
if (unlikely(d == NULL))
{
vlc_gc_decref (item);
return -1;
}
d->index = info->index;
d->item = item;
d->sd = NULL;
struct device **dp = tsearch (d, &sys->root, cmpsrc);
if (dp == NULL) /* Out-of-memory */
{
DestroySource (d);
return -1;
}
if (*dp != d) /* Replace existing source */
{
DestroySource (*dp);
*dp = d;
}
char *card;
if (info->card == PA_INVALID_INDEX
|| unlikely(asprintf (&card, N_("Card %"PRIu32), info->card) == -1))
card = NULL;
services_discovery_AddItem (sd, item,
(card != NULL) ? card : N_("Generic"));
d->sd = sd;
free (card);
return 0;
}
static void SourceCallback(pa_context *ctx, const pa_source_info *i, int eol,
void *userdata)
{
services_discovery_t *sd = userdata;
if (eol)
return;
AddSource (sd, i);
(void) ctx;
}
/**
* Removes a source (if present) by index.
*/
static void RemoveSource (services_discovery_t *sd, uint32_t idx)
{
services_discovery_sys_t *sys = sd->p_sys;
struct device **dp = tfind (&idx, &sys->root, cmpsrc);
if (dp == NULL)
return;
struct device *d = *dp;
tdelete (d, &sys->root, cmpsrc);
DestroySource (d);
}
static void ContextCallback(pa_context *ctx, pa_subscription_event_type_t type,
uint32_t idx, void *userdata)
{
services_discovery_t *sd = userdata;
pa_operation *op;
assert ((type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)
== PA_SUBSCRIPTION_EVENT_SOURCE);
switch (type & PA_SUBSCRIPTION_EVENT_TYPE_MASK)
{
case PA_SUBSCRIPTION_EVENT_NEW:
case PA_SUBSCRIPTION_EVENT_CHANGE:
op = pa_context_get_source_info_by_index(ctx, idx, SourceCallback, sd);
if (likely(op != NULL))
pa_operation_unref(op);
break;
case PA_SUBSCRIPTION_EVENT_REMOVE:
RemoveSource (sd, idx);
break;
}
}
static void Close (vlc_object_t *obj)
{
services_discovery_t *sd = (services_discovery_t *)obj;
services_discovery_sys_t *sys = sd->p_sys;
vlc_pa_disconnect (obj, sys->context, sys->mainloop);
tdestroy (sys->root, DestroySource);
free (sys);
}
......@@ -971,6 +971,7 @@ modules/services_discovery/bonjour.c
modules/services_discovery/mediadirs.c
modules/services_discovery/mtp.c
modules/services_discovery/podcast.c
modules/services_discovery/pulse.c
modules/services_discovery/sap.c
modules/services_discovery/udev.c
modules/services_discovery/upnp.cpp
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment