|
@ -82,6 +82,7 @@ PA_MODULE_USAGE( |
|
|
"rate=<sample rate> " |
|
|
"rate=<sample rate> " |
|
|
"channels=<number of channels> " |
|
|
"channels=<number of channels> " |
|
|
"channel_map=<channel map> " |
|
|
"channel_map=<channel map> " |
|
|
|
|
|
"description=<description for the sink> " |
|
|
"xrdp_socket_path=<path to XRDP sockets> " |
|
|
"xrdp_socket_path=<path to XRDP sockets> " |
|
|
"xrdp_pulse_sink_socket=<name of sink socket>"); |
|
|
"xrdp_pulse_sink_socket=<name of sink socket>"); |
|
|
|
|
|
|
|
@ -101,6 +102,8 @@ struct userdata { |
|
|
pa_core *core; |
|
|
pa_core *core; |
|
|
pa_module *module; |
|
|
pa_module *module; |
|
|
pa_sink *sink; |
|
|
pa_sink *sink; |
|
|
|
|
|
pa_device_port *port; |
|
|
|
|
|
pa_card *card; |
|
|
|
|
|
|
|
|
pa_thread *thread; |
|
|
pa_thread *thread; |
|
|
pa_thread_mq thread_mq; |
|
|
pa_thread_mq thread_mq; |
|
@ -126,11 +129,81 @@ static const char* const valid_modargs[] = { |
|
|
"channel_map", |
|
|
"channel_map", |
|
|
"xrdp_socket_path", |
|
|
"xrdp_socket_path", |
|
|
"xrdp_pulse_sink_socket", |
|
|
"xrdp_pulse_sink_socket", |
|
|
|
|
|
"description", |
|
|
NULL |
|
|
NULL |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
static int close_send(struct userdata *u); |
|
|
static int close_send(struct userdata *u); |
|
|
|
|
|
|
|
|
|
|
|
static pa_device_port *xrdp_create_port(struct userdata *u) { |
|
|
|
|
|
pa_device_port_new_data data; |
|
|
|
|
|
pa_device_port *port; |
|
|
|
|
|
|
|
|
|
|
|
pa_device_port_new_data_init(&data); |
|
|
|
|
|
|
|
|
|
|
|
pa_device_port_new_data_set_name(&data, "xrdp-output"); |
|
|
|
|
|
pa_device_port_new_data_set_description(&data, "xrdp output"); |
|
|
|
|
|
pa_device_port_new_data_set_direction(&data, PA_DIRECTION_OUTPUT); |
|
|
|
|
|
pa_device_port_new_data_set_available(&data, PA_AVAILABLE_YES); |
|
|
|
|
|
#if defined(PA_CHECK_VERSION) && PA_CHECK_VERSION(14, 0, 0) |
|
|
|
|
|
pa_device_port_new_data_set_type(&data, PA_DEVICE_PORT_TYPE_NETWORK); |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
port = pa_device_port_new(u->core, &data, 0); |
|
|
|
|
|
|
|
|
|
|
|
pa_device_port_new_data_done(&data); |
|
|
|
|
|
|
|
|
|
|
|
if (port == NULL) |
|
|
|
|
|
{ |
|
|
|
|
|
return NULL; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pa_device_port_ref(port); |
|
|
|
|
|
|
|
|
|
|
|
return port; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static pa_card_profile *xrdp_create_profile() { |
|
|
|
|
|
pa_card_profile *profile; |
|
|
|
|
|
|
|
|
|
|
|
profile = pa_card_profile_new("output:xrdp", "xrdp audio output", 0); |
|
|
|
|
|
profile->priority = 10; |
|
|
|
|
|
profile->n_sinks = 1; |
|
|
|
|
|
profile->n_sources = 0; |
|
|
|
|
|
profile->max_sink_channels = 2; |
|
|
|
|
|
profile->max_source_channels = 0; |
|
|
|
|
|
|
|
|
|
|
|
return profile; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static pa_card *xrdp_create_card(pa_module *m, pa_device_port *port, pa_card_profile *profile) { |
|
|
|
|
|
pa_card_new_data data; |
|
|
|
|
|
pa_card *card; |
|
|
|
|
|
|
|
|
|
|
|
pa_card_new_data_init(&data); |
|
|
|
|
|
data.driver = __FILE__; |
|
|
|
|
|
|
|
|
|
|
|
pa_card_new_data_set_name(&data, "xrdp.sink"); |
|
|
|
|
|
|
|
|
|
|
|
pa_hashmap_put(data.ports, port->name, port); |
|
|
|
|
|
pa_hashmap_put(data.profiles, profile->name, profile); |
|
|
|
|
|
|
|
|
|
|
|
card = pa_card_new(m->core, &data); |
|
|
|
|
|
|
|
|
|
|
|
pa_card_new_data_done(&data); |
|
|
|
|
|
|
|
|
|
|
|
if (card == NULL) |
|
|
|
|
|
{ |
|
|
|
|
|
return NULL; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pa_card_choose_initial_profile(card); |
|
|
|
|
|
|
|
|
|
|
|
pa_card_put(card); |
|
|
|
|
|
|
|
|
|
|
|
return card; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
static int sink_process_msg(pa_msgobject *o, int code, void *data, |
|
|
static int sink_process_msg(pa_msgobject *o, int code, void *data, |
|
|
int64_t offset, pa_memchunk *chunk) { |
|
|
int64_t offset, pa_memchunk *chunk) { |
|
|
|
|
|
|
|
@ -513,6 +586,8 @@ static void set_sink_socket(pa_modargs *ma, struct userdata *u) { |
|
|
|
|
|
|
|
|
int pa__init(pa_module*m) { |
|
|
int pa__init(pa_module*m) { |
|
|
struct userdata *u = NULL; |
|
|
struct userdata *u = NULL; |
|
|
|
|
|
pa_device_port *port; |
|
|
|
|
|
pa_card_profile *profile; |
|
|
pa_sample_spec ss; |
|
|
pa_sample_spec ss; |
|
|
pa_channel_map map; |
|
|
pa_channel_map map; |
|
|
pa_modargs *ma = NULL; |
|
|
pa_modargs *ma = NULL; |
|
@ -558,8 +633,11 @@ int pa__init(pa_module*m) { |
|
|
pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); |
|
|
pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); |
|
|
pa_sink_new_data_set_sample_spec(&data, &ss); |
|
|
pa_sink_new_data_set_sample_spec(&data, &ss); |
|
|
pa_sink_new_data_set_channel_map(&data, &map); |
|
|
pa_sink_new_data_set_channel_map(&data, &map); |
|
|
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "xrdp sink"); |
|
|
|
|
|
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract"); |
|
|
|
|
|
|
|
|
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "remote audio output")); |
|
|
|
|
|
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound"); |
|
|
|
|
|
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, "computer"); |
|
|
|
|
|
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PRODUCT_NAME, "xrdp"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, |
|
|
if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, |
|
|
PA_UPDATE_REPLACE) < 0) { |
|
|
PA_UPDATE_REPLACE) < 0) { |
|
@ -567,9 +645,27 @@ int pa__init(pa_module*m) { |
|
|
pa_sink_new_data_done(&data); |
|
|
pa_sink_new_data_done(&data); |
|
|
goto fail; |
|
|
goto fail; |
|
|
} |
|
|
} |
|
|
|
|
|
port = xrdp_create_port(u); |
|
|
|
|
|
if (port == NULL) { |
|
|
|
|
|
pa_log("Failed to create port object"); |
|
|
|
|
|
goto fail; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
profile = xrdp_create_profile(); |
|
|
|
|
|
|
|
|
|
|
|
pa_hashmap_put(port->profiles, profile->name, profile); |
|
|
|
|
|
|
|
|
|
|
|
u->card = xrdp_create_card(m, port, profile); |
|
|
|
|
|
if (u->card == NULL) { |
|
|
|
|
|
pa_log("Failed to create card object"); |
|
|
|
|
|
goto fail; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
data.card = u->card; |
|
|
|
|
|
pa_hashmap_put(data.ports, port->name, port); |
|
|
|
|
|
|
|
|
u->sink = pa_sink_new(m->core, &data, |
|
|
u->sink = pa_sink_new(m->core, &data, |
|
|
PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY); |
|
|
|
|
|
|
|
|
PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY | PA_SINK_NETWORK | PA_SINK_HARDWARE); |
|
|
pa_sink_new_data_done(&data); |
|
|
pa_sink_new_data_done(&data); |
|
|
|
|
|
|
|
|
if (!u->sink) { |
|
|
if (!u->sink) { |
|
@ -666,6 +762,11 @@ void pa__done(pa_module*m) { |
|
|
pa_rtpoll_free(u->rtpoll); |
|
|
pa_rtpoll_free(u->rtpoll); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (u->card) |
|
|
|
|
|
{ |
|
|
|
|
|
pa_card_free(u->card); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (u->fd >= 0) |
|
|
if (u->fd >= 0) |
|
|
{ |
|
|
{ |
|
|
close(u->fd); |
|
|
close(u->fd); |
|
|