static struct service *get_service(struct userdata *u, pa_object *device) { struct service *s; char *hn, *un; const char *n; pa_assert(u); pa_object_assert_ref(device); if ((s = pa_hashmap_get(u->services, device))) return s; s = pa_xnew0(struct service, 1); s->userdata = u; s->device = device; if (pa_sink_isinstance(device)) { if (!(n = pa_proplist_gets(PA_SINK(device)->proplist, PA_PROP_DEVICE_DESCRIPTION))) n = PA_SINK(device)->name; } else { if (!(n = pa_proplist_gets(PA_SOURCE(device)->proplist, PA_PROP_DEVICE_DESCRIPTION))) n = PA_SOURCE(device)->name; } hn = pa_get_host_name_malloc(); un = pa_get_user_name_malloc(); s->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s: %s", un, hn, n), kDNSServiceMaxDomainName-1); pa_xfree(un); pa_xfree(hn); pa_hashmap_put(u->services, s->device, s); return s; }
int pa__init(pa_module*m) { struct userdata *u; pa_modargs *ma = NULL; char *hn, *un; int error; if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments."); goto fail; } m->userdata = u = pa_xnew(struct userdata, 1); u->core = m->core; u->module = m; u->native = pa_native_protocol_get(u->core); u->avahi_poll = pa_avahi_poll_new(m->core->mainloop); u->services = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u); u->sink_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u); u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u); u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u); u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u); u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u); u->main_entry_group = NULL; un = pa_get_user_name_malloc(); hn = pa_get_host_name_malloc(); u->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s", un, hn), AVAHI_LABEL_MAX-1); pa_xfree(un); pa_xfree(hn); if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) { pa_log("avahi_client_new() failed: %s", avahi_strerror(error)); goto fail; } pa_modargs_free(ma); return 0; fail: pa__done(m); if (ma) pa_modargs_free(ma); return -1; }
int pa__init(pa_module*m) { struct userdata *u; pa_modargs *ma = NULL; char *hn, *un; if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments."); goto fail; } m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; u->native = pa_native_protocol_get(u->core); u->services = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u); u->sink_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u); u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u); u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u); u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u); u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u); un = pa_get_user_name_malloc(); hn = pa_get_host_name_malloc(); u->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s", un, hn), kDNSServiceMaxDomainName-1); pa_xfree(un); pa_xfree(hn); publish_all_services(u); pa_modargs_free(ma); return 0; fail: pa__done(m); if (ma) pa_modargs_free(ma); return -1; }
static void context_state_cb(pa_context *c, void *userdata) { struct userdata *u = userdata; pa_assert(u); switch (pa_context_get_state(c)) { case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: break; case PA_CONTEXT_READY: { pa_proplist *proplist; pa_buffer_attr bufferattr; pa_usec_t requested_latency; char *username = pa_get_user_name_malloc(); char *hostname = pa_get_host_name_malloc(); /* TODO: old tunnel put here the remote sink_name into stream name e.g. 'Null Output for lynxis@lazus' */ char *stream_name = pa_sprintf_malloc(_("Tunnel for %s@%s"), username, hostname); pa_xfree(hostname); pa_xfree(username); pa_log_debug("Connection successful. Creating stream."); pa_assert(!u->stream); proplist = tunnel_new_proplist(u); if(u->transcode.encoding != -1) { unsigned int n_formats = 1; pa_format_info *formats[1]; formats[0] = pa_format_info_new(); formats[0]->encoding = u->transcode.encoding; pa_format_info_set_sample_format(formats[0], u->sink->sample_spec.format); pa_format_info_set_rate(formats[0], u->sink->sample_spec.rate); pa_format_info_set_channels(formats[0], u->sink->sample_spec.channels); pa_format_info_set_channel_map(formats[0], &u->sink->channel_map); pa_transcode_set_format_info(&u->transcode, formats[0]); u->stream = pa_stream_new_extended(u->context, stream_name, formats, n_formats, proplist); } else u->stream = pa_stream_new_with_proplist(u->context, stream_name, &u->sink->sample_spec, &u->sink->channel_map, proplist); pa_proplist_free(proplist); pa_xfree(stream_name); if (!u->stream) { pa_log_error("Could not create a stream."); u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP); return; } requested_latency = pa_sink_get_requested_latency_within_thread(u->sink); if (requested_latency == (pa_usec_t) -1) requested_latency = u->sink->thread_info.max_latency; reset_bufferattr(&bufferattr); bufferattr.tlength = pa_usec_to_bytes(requested_latency, &u->sink->sample_spec); pa_stream_set_state_callback(u->stream, stream_state_cb, userdata); pa_stream_set_buffer_attr_callback(u->stream, stream_changed_buffer_attr_cb, userdata); if (pa_stream_connect_playback(u->stream, u->remote_sink_name, &bufferattr, PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_DONT_MOVE | PA_STREAM_START_CORKED | PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL) < 0) { pa_log_error("Could not connect stream."); u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP); } u->connected = true; break; } case PA_CONTEXT_FAILED: pa_log_debug("Context failed: %s.", pa_strerror(pa_context_errno(u->context))); u->connected = false; u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP); break; case PA_CONTEXT_TERMINATED: pa_log_debug("Context terminated."); u->connected = false; u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP); break; } }