/* Called from main thread */ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { struct userdata *u; pa_proplist *p; const char *n; if (!dest) return; pa_sink_input_assert_ref(i); pa_assert_ctl_context(); pa_assert_se(u = i->userdata); p = pa_proplist_new(); pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback to %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION))); if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME))) pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n); pa_source_output_update_proplist(u->source_output, PA_UPDATE_REPLACE, p); pa_proplist_free(p); if (pa_sink_get_state(dest) == PA_SINK_SUSPENDED) pa_source_output_cork(u->source_output, true); else pa_source_output_cork(u->source_output, false); update_adjust_timer(u); }
/* Called from main context */ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save) { int r; pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_assert(o->source); pa_source_assert_ref(dest); if (dest == o->source) return 0; if (!pa_source_output_may_move_to(o, dest)) return -PA_ERR_NOTSUPPORTED; pa_source_output_ref(o); if ((r = pa_source_output_start_move(o)) < 0) { pa_source_output_unref(o); return r; } if ((r = pa_source_output_finish_move(o, dest, save)) < 0) { pa_source_output_fail_move(o); pa_source_output_unref(o); return r; } pa_source_output_unref(o); return 0; }
/* Called from main context */ void pa_source_output_send_event(pa_source_output *o, const char *event, pa_proplist *data) { pa_proplist *pl = NULL; pa_source_output_send_event_hook_data hook_data; pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert(event); if (!o->send_event) return; if (!data) data = pl = pa_proplist_new(); hook_data.source_output = o; hook_data.data = data; hook_data.event = event; if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT], &hook_data) < 0) goto finish; o->send_event(o, event, data); finish: if (pl) pa_proplist_free(pl); }
/* Called from main context */ int pa_source_output_start_move(pa_source_output *o) { pa_source *origin; int r; pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_assert(o->source); if (!pa_source_output_may_move(o)) return -PA_ERR_NOTSUPPORTED; if ((r = pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], o)) < 0) return r; origin = o->source; pa_idxset_remove_by_data(o->source->outputs, o, NULL); if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED) pa_assert_se(origin->n_corked-- >= 1); pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0); pa_source_update_status(o->source); o->source = NULL; pa_source_output_unref(o); return 0; }
/* Called from main thread */ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { struct userdata *u; char *output_description; const char *n; if (!dest) return; pa_sink_input_assert_ref(i); pa_assert_ctl_context(); pa_assert_se(u = i->userdata); output_description = pa_sprintf_malloc("Loopback to %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION))); pa_source_output_set_property(u->source_output, PA_PROP_MEDIA_NAME, output_description); pa_xfree(output_description); if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME))) pa_source_output_set_property(u->source_output, PA_PROP_MEDIA_ICON_NAME, n); if (pa_sink_get_state(dest) == PA_SINK_SUSPENDED) pa_source_output_cork(u->source_output, true); else pa_source_output_cork(u->source_output, false); update_adjust_timer(u); }
/* Called from main context */ void pa_source_output_cork(pa_source_output *o, pa_bool_t b) { pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING); }
/* Called from main thread */ static void source_output_moving_cb(pa_source_output *o, pa_source *dest) { struct userdata *u; pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert_se(u = o->userdata); if (dest) { pa_source_set_asyncmsgq(u->source, dest->asyncmsgq); pa_source_update_flags(u->source, PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY, dest->flags); } else pa_source_set_asyncmsgq(u->source, NULL); if (u->auto_desc && dest) { const char *k; pa_proplist *pl; pl = pa_proplist_new(); k = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION); pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : dest->name); pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, pl); pa_proplist_free(pl); } }
/* Called from main context */ static void teardown(struct userdata *u) { pa_assert(u); pa_assert_ctl_context(); u->adjust_time = 0; enable_adjust_timer(u, false); /* Handling the asyncmsgq between the source output and the sink input * requires some care. When the source output is unlinked, nothing needs * to be done for the asyncmsgq, because the source output is the sending * end. But when the sink input is unlinked, we should ensure that the * asyncmsgq is emptied, because the messages in the queue hold references * to the sink input. Also, we need to ensure that new messages won't be * written to the queue after we have emptied it. * * Emptying the queue can be done in the state_changed() callback of the * sink input, when the new state is "unlinked". * * Preventing new messages from being written to the queue can be achieved * by unlinking the source output before unlinking the sink input. There * are no other writers for that queue, so this is sufficient. */ if (u->source_output) { pa_source_output_unlink(u->source_output); pa_source_output_unref(u->source_output); u->source_output = NULL; } if (u->sink_input) { pa_sink_input_unlink(u->sink_input); pa_sink_input_unref(u->sink_input); u->sink_input = NULL; } }
/* Called from main context */ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) { pa_source_output_assert_ref(o); pa_assert_ctl_context(); if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) { pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); return usec; } /* If this source output is not realized yet or is being moved, we * have to touch the thread info data directly */ if (o->source) { if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY)) usec = pa_source_get_fixed_latency(o->source); if (usec != (pa_usec_t) -1) { pa_usec_t min_latency, max_latency; pa_source_get_latency_range(o->source, &min_latency, &max_latency); usec = PA_CLAMP(usec, min_latency, max_latency); } } o->thread_info.requested_source_latency = usec; return usec; }
/* Called from main context */ void pa_source_output_kill(pa_source_output*o) { pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); o->kill(o); }
/* Called from main context */ static void adjust_rates(struct userdata *u) { size_t buffer, fs; uint32_t old_rate, base_rate, new_rate; pa_usec_t buffer_latency; pa_assert(u); pa_assert_ctl_context(); pa_asyncmsgq_send(u->source_output->source->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL); pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL); buffer = u->latency_snapshot.sink_input_buffer + u->latency_snapshot.source_output_buffer; if (u->latency_snapshot.recv_counter <= u->latency_snapshot.send_counter) buffer += (size_t) (u->latency_snapshot.send_counter - u->latency_snapshot.recv_counter); else buffer += PA_CLIP_SUB(buffer, (size_t) (u->latency_snapshot.recv_counter - u->latency_snapshot.send_counter)); buffer_latency = pa_bytes_to_usec(buffer, &u->sink_input->sample_spec); pa_log_debug("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms", (double) u->latency_snapshot.sink_latency / PA_USEC_PER_MSEC, (double) buffer_latency / PA_USEC_PER_MSEC, (double) u->latency_snapshot.source_latency / PA_USEC_PER_MSEC, ((double) u->latency_snapshot.sink_latency + buffer_latency + u->latency_snapshot.source_latency) / PA_USEC_PER_MSEC); pa_log_debug("Should buffer %zu bytes, buffered at minimum %zu bytes", u->latency_snapshot.max_request*2, u->latency_snapshot.min_memblockq_length); fs = pa_frame_size(&u->sink_input->sample_spec); old_rate = u->sink_input->sample_spec.rate; base_rate = u->source_output->sample_spec.rate; if (u->latency_snapshot.min_memblockq_length < u->latency_snapshot.max_request*2) new_rate = base_rate - (((u->latency_snapshot.max_request*2 - u->latency_snapshot.min_memblockq_length) / fs) *PA_USEC_PER_SEC)/u->adjust_time; else new_rate = base_rate + (((u->latency_snapshot.min_memblockq_length - u->latency_snapshot.max_request*2) / fs) *PA_USEC_PER_SEC)/u->adjust_time; if (new_rate < (uint32_t) (base_rate*0.8) || new_rate > (uint32_t) (base_rate*1.25)) { pa_log_warn("Sample rates too different, not adjusting (%u vs. %u).", base_rate, new_rate); new_rate = base_rate; } else { if (base_rate < new_rate + 20 && new_rate < base_rate + 20) new_rate = base_rate; /* Do the adjustment in small steps; 2‰ can be considered inaudible */ if (new_rate < (uint32_t) (old_rate*0.998) || new_rate > (uint32_t) (old_rate*1.002)) { pa_log_info("New rate of %u Hz not within 2‰ of %u Hz, forcing smaller adjustment", new_rate, old_rate); new_rate = PA_CLAMP(new_rate, (uint32_t) (old_rate*0.998), (uint32_t) (old_rate*1.002)); } } pa_sink_input_set_rate(u->sink_input, new_rate); pa_log_debug("[%s] Updated sampling rate to %lu Hz.", u->sink_input->sink->name, (unsigned long) new_rate); pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time); }
/* Called from main thread */ static pa_bool_t source_output_may_move_to_cb(pa_source_output *o, pa_source *dest) { struct userdata *u; pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert_se(u = o->userdata); return dest != u->sink_input->sink->monitor_source; }
/* Called from main thread */ static void source_output_kill_cb(pa_source_output *o) { struct userdata *u; pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert_se(u = o->userdata); teardown(u); pa_module_unload_request(u->module, TRUE); }
/* Called from main thread */ static void sink_input_kill_cb(pa_sink_input *i) { struct userdata *u; pa_sink_input_assert_ref(i); pa_assert_ctl_context(); pa_assert_se(u = i->userdata); teardown(u); pa_module_unload_request(u->module, TRUE); }
/* Called from main thread */ static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspended) { struct userdata *u; pa_sink_input_assert_ref(i); pa_assert_ctl_context(); pa_assert_se(u = i->userdata); pa_source_output_cork(u->source_output, suspended); update_adjust_timer(u); }
/* Called from main thread */ void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) { pa_source_output_assert_ref(o); pa_assert_ctl_context(); if (p) pa_proplist_update(o->proplist, mode, p); if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) { pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o); pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); } }
/* Called from main context */ static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) { pa_assert(o); pa_assert_ctl_context(); if (!o->source) return; if (o->state == PA_SOURCE_OUTPUT_CORKED && state != PA_SOURCE_OUTPUT_CORKED) pa_assert_se(o->source->n_corked -- >= 1); else if (o->state != PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_CORKED) o->source->n_corked++; }
/* Called from main thread */ static bool sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { struct userdata *u; pa_sink_input_assert_ref(i); pa_assert_ctl_context(); pa_assert_se(u = i->userdata); if (!u->source_output || !u->source_output->source) return true; return dest != u->source_output->source->monitor_of; }
/* Called from main context */ pa_bool_t pa_source_output_may_move(pa_source_output *o) { pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE) return FALSE; if (o->direct_on_input) return FALSE; return TRUE; }
/* Called from main context */ void pa_source_output_unlink(pa_source_output*o) { pa_bool_t linked; pa_assert(o); pa_assert_ctl_context(); /* See pa_sink_unlink() for a couple of comments how this function * works */ pa_source_output_ref(o); linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state); if (linked) pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o); if (o->direct_on_input) pa_idxset_remove_by_data(o->direct_on_input->direct_outputs, o, NULL); pa_idxset_remove_by_data(o->core->source_outputs, o, NULL); if (o->source) if (pa_idxset_remove_by_data(o->source->outputs, o, NULL)) pa_source_output_unref(o); if (o->client) pa_idxset_remove_by_data(o->client->source_outputs, o, NULL); update_n_corked(o, PA_SOURCE_OUTPUT_UNLINKED); o->state = PA_SOURCE_OUTPUT_UNLINKED; if (linked && o->source) if (o->source->asyncmsgq) pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0); reset_callbacks(o); if (linked) { pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index); pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o); } if (o->source) { pa_source_update_status(o->source); o->source = NULL; } pa_core_maybe_vacuum(o->core); pa_source_output_unref(o); }
/* Called from main context */ pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) { pa_source_output_assert_ref(o); pa_assert_ctl_context(); if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) { pa_usec_t usec = 0; pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); return usec; } /* If this source output is not realized yet or is being moved, we * have to touch the thread info data directly */ return o->thread_info.requested_source_latency; }
/* Called from main context */ static void adjust_rates(struct userdata *u) { size_t buffer; uint32_t old_rate, base_rate, new_rate; int32_t latency_difference; pa_usec_t current_buffer_latency, snapshot_delay, current_source_sink_latency, current_latency, latency_at_optimum_rate; pa_usec_t final_latency; pa_assert(u); pa_assert_ctl_context(); /* Rates and latencies*/ old_rate = u->sink_input->sample_spec.rate; base_rate = u->source_output->sample_spec.rate; buffer = u->latency_snapshot.sink_input_buffer; if (u->latency_snapshot.recv_counter <= u->latency_snapshot.send_counter) buffer += (size_t) (u->latency_snapshot.send_counter - u->latency_snapshot.recv_counter); else buffer = PA_CLIP_SUB(buffer, (size_t) (u->latency_snapshot.recv_counter - u->latency_snapshot.send_counter)); current_buffer_latency = pa_bytes_to_usec(buffer, &u->sink_input->sample_spec); snapshot_delay = u->latency_snapshot.source_timestamp - u->latency_snapshot.sink_timestamp; current_source_sink_latency = u->latency_snapshot.sink_latency + u->latency_snapshot.source_latency - snapshot_delay; /* Current latency */ current_latency = current_source_sink_latency + current_buffer_latency; /* Latency at base rate */ latency_at_optimum_rate = current_source_sink_latency + current_buffer_latency * old_rate / base_rate; final_latency = u->latency; latency_difference = (int32_t)((int64_t)latency_at_optimum_rate - final_latency); pa_log_debug("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms", (double) u->latency_snapshot.sink_latency / PA_USEC_PER_MSEC, (double) current_buffer_latency / PA_USEC_PER_MSEC, (double) u->latency_snapshot.source_latency / PA_USEC_PER_MSEC, (double) current_latency / PA_USEC_PER_MSEC); pa_log_debug("Loopback latency at base rate is %0.2f ms", (double)latency_at_optimum_rate / PA_USEC_PER_MSEC); /* Calculate new rate */ new_rate = rate_controller(base_rate, u->adjust_time, latency_difference); /* Set rate */ pa_sink_input_set_rate(u->sink_input, new_rate); pa_log_debug("[%s] Updated sampling rate to %lu Hz.", u->sink_input->sink->name, (unsigned long) new_rate); }
/* Called from main context */ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) { pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_return_val_if_fail(o->thread_info.resampler, -PA_ERR_BADSTATE); if (o->sample_spec.rate == rate) return 0; o->sample_spec.rate = rate; pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL); pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); return 0; }
/* Called from main context */ void pa_source_output_fail_move(pa_source_output *o) { pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_assert(!o->source); /* Check if someone wants this source output? */ if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], o) == PA_HOOK_STOP) return; if (o->moving) o->moving(o, NULL); pa_source_output_kill(o); }
/* Called from main context */ static void adjust_rates(struct userdata *u) { size_t buffer, fs; uint32_t old_rate, base_rate, new_rate; pa_usec_t buffer_latency; pa_assert(u); pa_assert_ctl_context(); pa_asyncmsgq_send(u->source_output->source->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL); pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL); buffer = u->latency_snapshot.sink_input_buffer + u->latency_snapshot.source_output_buffer; if (u->latency_snapshot.recv_counter <= u->latency_snapshot.send_counter) buffer += (size_t) (u->latency_snapshot.send_counter - u->latency_snapshot.recv_counter); else buffer += PA_CLIP_SUB(buffer, (size_t) (u->latency_snapshot.recv_counter - u->latency_snapshot.send_counter)); buffer_latency = pa_bytes_to_usec(buffer, &u->sink_input->sample_spec); pa_log_info("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms", (double) u->latency_snapshot.sink_latency / PA_USEC_PER_MSEC, (double) buffer_latency / PA_USEC_PER_MSEC, (double) u->latency_snapshot.source_latency / PA_USEC_PER_MSEC, ((double) u->latency_snapshot.sink_latency + buffer_latency + u->latency_snapshot.source_latency) / PA_USEC_PER_MSEC); pa_log_info("Should buffer %zu bytes, buffered at minimum %zu bytes", u->latency_snapshot.max_request*2, u->latency_snapshot.min_memblockq_length); fs = pa_frame_size(&u->sink_input->sample_spec); old_rate = u->sink_input->sample_spec.rate; base_rate = u->source_output->sample_spec.rate; if (u->latency_snapshot.min_memblockq_length < u->latency_snapshot.max_request*2) new_rate = base_rate - (((u->latency_snapshot.max_request*2 - u->latency_snapshot.min_memblockq_length) / fs) *PA_USEC_PER_SEC)/u->adjust_time; else new_rate = base_rate + (((u->latency_snapshot.min_memblockq_length - u->latency_snapshot.max_request*2) / fs) *PA_USEC_PER_SEC)/u->adjust_time; pa_log_info("Old rate %lu Hz, new rate %lu Hz", (unsigned long) old_rate, (unsigned long) new_rate); pa_sink_input_set_rate(u->sink_input, new_rate); pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time); }
/* Called from main context */ pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency) { pa_usec_t r[2] = { 0, 0 }; pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0); if (o->get_latency) r[0] += o->get_latency(o); if (source_latency) *source_latency = r[1]; return r[0]; }
/* Called from main thread */ static void source_output_moving_cb(pa_source_output *o, pa_source *dest) { pa_proplist *p; const char *n; struct userdata *u; pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert_se(u = o->userdata); p = pa_proplist_new(); pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback of %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION))); if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME))) pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n); pa_sink_input_update_proplist(u->sink_input, PA_UPDATE_REPLACE, p); pa_proplist_free(p); }
/* Called from main context */ static void teardown(struct userdata *u) { pa_assert(u); pa_assert_ctl_context(); if (u->sink_input) pa_sink_input_unlink(u->sink_input); if (u->source_output) pa_source_output_unlink(u->source_output); if (u->sink_input) { pa_sink_input_unref(u->sink_input); u->sink_input = NULL; } if (u->source_output) { pa_source_output_unref(u->source_output); u->source_output = NULL; } }
/* Called from main thread */ static void source_output_kill_cb(pa_source_output *o) { struct userdata *u; pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert_se(u = o->userdata); /* The order here matters! We first kill the source output, followed * by the source. That means the source callbacks must be protected * against an unconnected source output! */ pa_source_output_unlink(u->source_output); pa_source_unlink(u->source); pa_source_output_unref(u->source_output); u->source_output = NULL; pa_source_unref(u->source); u->source = NULL; pa_module_unload_request(u->module, true); }
/* Called from main context */ static void source_output_set_state(pa_source_output *o, pa_source_output_state_t state) { pa_assert(o); pa_assert_ctl_context(); if (o->state == state) return; pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0); update_n_corked(o, state); o->state = state; if (state != PA_SOURCE_OUTPUT_UNLINKED) { pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o); if (PA_SOURCE_OUTPUT_IS_LINKED(state)) pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); } pa_source_update_status(o->source); }