3 * @brief List of PulseAudio sources for VLC media player
5 /*****************************************************************************
6 * Copyright © 2011 Rémi Denis-Courmont
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_services_discovery.h>
33 #include <pulse/pulseaudio.h>
34 #include "audio_output/vlcpulse.h"
36 static int Open (vlc_object_t
*);
37 static void Close (vlc_object_t
*);
39 VLC_SD_PROBE_HELPER("pulse", N_("Audio capture"), SD_CAT_DEVICES
);
42 set_shortname (N_("Audio capture"))
43 set_description (N_("Audio capture (PulseAudio)"))
44 set_category (CAT_PLAYLIST
)
45 set_subcategory (SUBCAT_PLAYLIST_SD
)
46 set_capability ("services_discovery", 0)
47 set_callbacks (Open
, Close
)
48 add_shortcut ("pulse", "pa", "pulseaudio", "audio")
50 VLC_SD_PROBE_SUBMODULE
53 struct services_discovery_sys_t
57 pa_threaded_mainloop
*mainloop
;
60 static void SourceCallback(pa_context
*, const pa_source_info
*, int, void *);
61 static void ContextCallback(pa_context
*, pa_subscription_event_type_t
,
64 static int Open (vlc_object_t
*obj
)
66 services_discovery_t
*sd
= (services_discovery_t
*)obj
;
70 services_discovery_sys_t
*sys
= malloc (sizeof (*sys
));
71 if (unlikely(sys
== NULL
))
74 ctx
= vlc_pa_connect (obj
, &sys
->mainloop
);
82 sd
->description
= _("Audio capture");
86 /* Subscribe for source events */
87 const pa_subscription_mask_t mask
= PA_SUBSCRIPTION_MASK_SOURCE
;
88 pa_threaded_mainloop_lock (sys
->mainloop
);
89 pa_context_set_subscribe_callback (ctx
, ContextCallback
, sd
);
90 op
= pa_context_subscribe (ctx
, mask
, NULL
, NULL
);
91 if (likely(op
!= NULL
))
92 pa_operation_unref (op
);
94 /* Enumerate existing sources */
95 op
= pa_context_get_source_info_list (ctx
, SourceCallback
, sd
);
96 if (likely(op
!= NULL
))
98 //while (pa_operation_get_state (op) == PA_OPERATION_RUNNING)
99 // pa_threaded_mainloop_wait (sys->mainloop);
100 pa_operation_unref (op
);
102 pa_threaded_mainloop_unlock (sys
->mainloop
);
106 pa_threaded_mainloop_unlock (sys->mainloop);
107 vlc_pa_disconnect (obj, ctx, sys->mainloop);
109 return VLC_EGENERIC;*/
116 services_discovery_t
*sd
;
119 static void DestroySource (void *data
)
121 struct device
*d
= data
;
123 services_discovery_RemoveItem (d
->sd
, d
->item
);
124 input_item_Release (d
->item
);
129 * Compares two devices (to support binary search).
131 static int cmpsrc (const void *a
, const void *b
)
133 const uint32_t *pa
= a
, *pb
= b
;
134 uint32_t idxa
= *pa
, idxb
= *pb
;
136 return (idxa
!= idxb
) ? ((idxa
< idxb
) ? -1 : +1) : 0;
142 static int AddSource (services_discovery_t
*sd
, const pa_source_info
*info
)
144 services_discovery_sys_t
*sys
= sd
->p_sys
;
146 msg_Dbg (sd
, "adding %s (%s)", info
->name
, info
->description
);
149 if (unlikely(asprintf (&mrl
, "pulse://%s", info
->name
) == -1))
152 input_item_t
*item
= input_item_NewCard (mrl
, info
->description
);
154 if (unlikely(item
== NULL
))
157 struct device
*d
= malloc (sizeof (*d
));
158 if (unlikely(d
== NULL
))
160 input_item_Release (item
);
163 d
->index
= info
->index
;
166 struct device
**dp
= tsearch (d
, &sys
->root
, cmpsrc
);
167 if (dp
== NULL
) /* Out-of-memory */
170 input_item_Release (item
);
173 if (*dp
!= d
) /* Update existing source */
177 input_item_SetURI (d
->item
, item
->psz_uri
);
178 input_item_SetName (d
->item
, item
->psz_name
);
179 input_item_Release (item
);
183 const char *card
= pa_proplist_gets(info
->proplist
, "device.product.name");
184 services_discovery_AddItemCat(sd
, item
,
185 (card
!= NULL
) ? card
: N_("Generic"));
190 static void SourceCallback(pa_context
*ctx
, const pa_source_info
*i
, int eol
,
193 services_discovery_t
*sd
= userdata
;
202 * Removes a source (if present) by index.
204 static void RemoveSource (services_discovery_t
*sd
, uint32_t idx
)
206 services_discovery_sys_t
*sys
= sd
->p_sys
;
208 struct device
**dp
= tfind (&idx
, &sys
->root
, cmpsrc
);
212 struct device
*d
= *dp
;
213 tdelete (d
, &sys
->root
, cmpsrc
);
217 static void ContextCallback(pa_context
*ctx
, pa_subscription_event_type_t type
,
218 uint32_t idx
, void *userdata
)
220 services_discovery_t
*sd
= userdata
;
223 assert ((type
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
)
224 == PA_SUBSCRIPTION_EVENT_SOURCE
);
225 switch (type
& PA_SUBSCRIPTION_EVENT_TYPE_MASK
)
227 case PA_SUBSCRIPTION_EVENT_NEW
:
228 case PA_SUBSCRIPTION_EVENT_CHANGE
:
229 op
= pa_context_get_source_info_by_index(ctx
, idx
, SourceCallback
, sd
);
230 if (likely(op
!= NULL
))
231 pa_operation_unref(op
);
234 case PA_SUBSCRIPTION_EVENT_REMOVE
:
235 RemoveSource (sd
, idx
);
240 static void Close (vlc_object_t
*obj
)
242 services_discovery_t
*sd
= (services_discovery_t
*)obj
;
243 services_discovery_sys_t
*sys
= sd
->p_sys
;
245 vlc_pa_disconnect (obj
, sys
->context
, sys
->mainloop
);
246 tdestroy (sys
->root
, DestroySource
);