/* 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; }
char *pa_source_output_list_to_string(pa_core *c) { pa_strbuf *s; pa_source_output *o; uint32_t idx = PA_IDXSET_INVALID; static const char* const state_table[] = { [PA_SOURCE_OUTPUT_RUNNING] = "RUNNING", [PA_SOURCE_OUTPUT_CORKED] = "CORKED", [PA_SOURCE_OUTPUT_UNLINKED] = "UNLINKED" }; pa_assert(c); s = pa_strbuf_new(); pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs)); for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) { char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; pa_assert(o->source); pa_strbuf_printf( s, " index: %u\n" "\tname: '%s'\n" "\tdriver: <%s>\n" "\tflags: %s%s%s%s%s%s%s\n" "\tstate: %s\n" "\tsource: <%u> '%s'\n" "\tlatency: <%0.0f usec>\n" "\tsample spec: <%s>\n" "\tchannel map: <%s>\n" "\tresample method: %s\n", o->index, o->name, o->driver, o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "", o->flags & PA_SOURCE_OUTPUT_DONT_MOVE ? "DONT_MOVE " : "", o->flags & PA_SOURCE_OUTPUT_NO_REMAP ? "NO_REMAP " : "", o->flags & PA_SOURCE_OUTPUT_NO_REMIX ? "NO_REMIX " : "", o->flags & PA_SOURCE_OUTPUT_FIX_FORMAT ? "FIX_FORMAT " : "", o->flags & PA_SOURCE_OUTPUT_FIX_RATE ? "FIX_RATE " : "", o->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "", state_table[pa_source_output_get_state(o)], o->source->index, o->source->name, (double) pa_source_output_get_latency(o), pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map), pa_resample_method_to_string(pa_source_output_get_resample_method(o))); if (o->module) pa_strbuf_printf(s, "\towner module: <%u>\n", o->module->index); if (o->client) pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", o->client->index, o->client->name); } return pa_strbuf_tostring_free(s); }
/* Called from main context */ static int source_set_state_cb(pa_source *s, pa_source_state_t state) { struct userdata *u; pa_source_assert_ref(s); pa_assert_se(u = s->userdata); if (!PA_SOURCE_IS_LINKED(state) || !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) return 0; pa_source_output_cork(u->source_output, state == PA_SOURCE_SUSPENDED); return 0; }
/* Called from main context */ static void source_get_mute_cb(pa_source *s) { struct userdata *u; pa_source_assert_ref(s); pa_assert_se(u = s->userdata); if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) || !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) return; /* FIXME, no volume control in source_output, get the info from the master */ pa_source_get_mute(u->source_output->source, TRUE); }
/* Called from output thread context */ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { struct userdata *u; pa_source_output_assert_ref(o); pa_source_output_assert_io_context(o); pa_assert_se(u = o->userdata); if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) { pa_log("push when no link?"); return; } pa_source_post(u->source, chunk); }
/* Generic source state change logic. Used by raw_source and voice_source */ int voice_source_set_state(pa_source *s, pa_source *other, pa_source_state_t state) { struct userdata *u; pa_source_assert_ref(s); pa_assert_se(u = s->userdata); if (!other) { pa_log_debug("other source not initialized or freed"); return 0; } pa_source_assert_ref(other); if (u->hw_source_output) { if (pa_source_output_get_state(u->hw_source_output) == PA_SOURCE_OUTPUT_RUNNING) { if (state == PA_SOURCE_SUSPENDED && pa_source_get_state(other) == PA_SOURCE_SUSPENDED && pa_atomic_load(&u->cmt_connection.ul_state) != CMT_UL_ACTIVE) { pa_source_output_cork(u->hw_source_output, TRUE); pa_log_debug("hw_source_output corked"); } } else if (pa_source_output_get_state(u->hw_source_output) == PA_SOURCE_OUTPUT_CORKED) { if (PA_SOURCE_IS_OPENED(state) || PA_SOURCE_IS_OPENED(pa_source_get_state(other)) || pa_atomic_load(&u->cmt_connection.ul_state) == CMT_UL_ACTIVE) { pa_source_output_cork(u->hw_source_output, FALSE); pa_log_debug("hw_source_output uncorked"); } } } if (pa_atomic_load(&u->cmt_connection.ul_state) != CMT_UL_ACTIVE && !PA_SOURCE_IS_OPENED(pa_source_get_state(u->voip_source))) { voice_aep_ear_ref_loop_reset(u); } return 0; }
static pa_hook_result_t source_output_state_changed_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) { pa_assert(c); pa_source_output_assert_ref(s); pa_assert(u); if (pa_source_output_get_state(s) == PA_SOURCE_OUTPUT_RUNNING) { struct device_info *d; if (s->source->monitor_of) d = pa_hashmap_get(u->device_infos, s->source->monitor_of); else d = pa_hashmap_get(u->device_infos, s->source); if (d) resume(d); } return PA_HOOK_OK; }
static void source_get_volume_cb(pa_source *s) { struct userdata *u; pa_source_assert_ref(s); pa_assert_se(u = s->userdata); if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) || !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) return; /* FIXME, no volume control in source_output, get the info from the master */ pa_source_get_volume(u->source_output->source, TRUE); if (pa_cvolume_equal(&s->volume,&u->source_output->source->volume)) /* no change */ return; s->volume = u->source_output->source->volume; pa_source_set_soft_volume(s, NULL); }
/* Called from input thread context */ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { struct userdata *u; pa_source_output_assert_ref(o); pa_source_output_assert_io_context(o); pa_assert_se(u = o->userdata); if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) { pa_log("push when no link?"); return; } /* PUT YOUR CODE HERE TO DO SOMETHING WITH THE SOURCE DATA */ /* if uplink sink exists, pull data from there; simplify by using same length as chunk provided by source */ if(u->sink && (pa_sink_get_state(u->sink) == PA_SINK_RUNNING)) { pa_memchunk tchunk; size_t nbytes = chunk->length; pa_mix_info streams[2]; pa_memchunk target_chunk; void *target; int ch; /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); /* get data from the sink */ while (pa_memblockq_peek(u->sink_memblockq, &tchunk) < 0) { pa_memchunk nchunk; /* make sure we get nbytes from the sink with render_full, otherwise we cannot mix with the uplink */ pa_sink_render_full(u->sink, nbytes, &nchunk); pa_memblockq_push(u->sink_memblockq, &nchunk); pa_memblock_unref(nchunk.memblock); } pa_assert(tchunk.length == chunk->length); /* move the read pointer for sink memblockq */ pa_memblockq_drop(u->sink_memblockq, tchunk.length); /* allocate target chunk */ /* this could probably be done in-place, but having chunk as both the input and output creates issues with reference counts */ target_chunk.index = 0; target_chunk.length = chunk->length; pa_assert(target_chunk.length == chunk->length); target_chunk.memblock = pa_memblock_new(o->source->core->mempool, target_chunk.length); pa_assert( target_chunk.memblock ); /* get target pointer */ target = (void*)((uint8_t*)pa_memblock_acquire(target_chunk.memblock) + target_chunk.index); /* set-up mixing structure volume was taken care of in sink and source already */ streams[0].chunk = *chunk; for(ch=0;ch<o->sample_spec.channels;ch++) streams[0].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */ streams[0].volume.channels = o->sample_spec.channels; streams[1].chunk = tchunk; for(ch=0;ch<o->sample_spec.channels;ch++) streams[1].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */ streams[1].volume.channels = o->sample_spec.channels; /* do mixing */ pa_mix(streams, /* 2 streams to be mixed */ 2, target, /* put result in target chunk */ chunk->length, /* same length as input */ (const pa_sample_spec *)&o->sample_spec, /* same sample spec for input and output */ NULL, /* no volume information */ FALSE); /* no mute */ pa_memblock_release(target_chunk.memblock); pa_memblock_unref(tchunk.memblock); /* clean-up */ /* forward the data to the virtual source */ pa_source_post(u->source, &target_chunk); pa_memblock_unref(target_chunk.memblock); /* clean-up */ } else { /* forward the data to the virtual source */ pa_source_post(u->source, chunk); } }
/* Called from main context */ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t save) { pa_resampler *new_resampler; 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 (!pa_source_output_may_move_to(o, dest)) return -1; if (o->thread_info.resampler && pa_sample_spec_equal(pa_resampler_input_sample_spec(o->thread_info.resampler), &dest->sample_spec) && pa_channel_map_equal(pa_resampler_input_channel_map(o->thread_info.resampler), &dest->channel_map)) /* Try to reuse the old resampler if possible */ new_resampler = o->thread_info.resampler; else if ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) || !pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec) || !pa_channel_map_equal(&o->channel_map, &dest->channel_map)) { /* Okey, we need a new resampler for the new source */ if (!(new_resampler = pa_resampler_new( o->core->mempool, &dest->sample_spec, &dest->channel_map, &o->sample_spec, &o->channel_map, o->requested_resample_method, ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) { pa_log_warn("Unsupported resampling operation."); return -PA_ERR_NOTSUPPORTED; } } else new_resampler = NULL; if (o->moving) o->moving(o, dest); o->source = dest; o->save_source = save; pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL); if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED) o->source->n_corked++; /* Replace resampler */ if (new_resampler != o->thread_info.resampler) { if (o->thread_info.resampler) pa_resampler_free(o->thread_info.resampler); o->thread_info.resampler = new_resampler; pa_memblockq_free(o->thread_info.delay_memblockq); o->thread_info.delay_memblockq = pa_memblockq_new( 0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&o->source->sample_spec), 0, 1, 0, &o->source->silence); o->actual_resample_method = new_resampler ? pa_resampler_get_method(new_resampler) : PA_RESAMPLER_INVALID; } pa_source_update_status(dest); pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0); pa_log_debug("Successfully moved source output %i to %s.", o->index, dest->name); /* Notify everyone */ pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], o); pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); return 0; }
char *pa_source_output_list_to_string(pa_core *c) { pa_strbuf *s; pa_source_output *o; uint32_t idx = PA_IDXSET_INVALID; static const char* const state_table[] = { [PA_SOURCE_OUTPUT_INIT] = "INIT", [PA_SOURCE_OUTPUT_RUNNING] = "RUNNING", [PA_SOURCE_OUTPUT_CORKED] = "CORKED", [PA_SOURCE_OUTPUT_UNLINKED] = "UNLINKED" }; pa_assert(c); s = pa_strbuf_new(); pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs)); for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) { char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28]; pa_usec_t cl; const char *cmn; cmn = pa_channel_map_to_pretty_name(&o->channel_map); if ((cl = pa_source_output_get_requested_latency(o)) == (pa_usec_t) -1) pa_snprintf(clt, sizeof(clt), "n/a"); else pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC); pa_assert(o->source); pa_strbuf_printf( s, " index: %u\n" "\tdriver: <%s>\n" "\tflags: %s%s%s%s%s%s%s%s%s%s%s\n" "\tstate: %s\n" "\tsource: %u <%s>\n" "\tcurrent latency: %0.2f ms\n" "\trequested latency: %s\n" "\tsample spec: %s\n" "\tchannel map: %s%s%s\n" "\tresample method: %s\n", o->index, o->driver, o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "", o->flags & PA_SOURCE_OUTPUT_DONT_MOVE ? "DONT_MOVE " : "", o->flags & PA_SOURCE_OUTPUT_START_CORKED ? "START_CORKED " : "", o->flags & PA_SOURCE_OUTPUT_NO_REMAP ? "NO_REMAP " : "", o->flags & PA_SOURCE_OUTPUT_NO_REMIX ? "NO_REMIX " : "", o->flags & PA_SOURCE_OUTPUT_FIX_FORMAT ? "FIX_FORMAT " : "", o->flags & PA_SOURCE_OUTPUT_FIX_RATE ? "FIX_RATE " : "", o->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "", o->flags & PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND ? "DONT_INHIBIT_AUTO_SUSPEND " : "", o->flags & PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND ? "NO_CREATE_ON_SUSPEND " : "", o->flags & PA_SOURCE_OUTPUT_KILL_ON_SUSPEND ? "KILL_ON_SUSPEND " : "", state_table[pa_source_output_get_state(o)], o->source->index, o->source->name, (double) pa_source_output_get_latency(o, NULL) / PA_USEC_PER_MSEC, clt, pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map), cmn ? "\n\t " : "", cmn ? cmn : "", pa_resample_method_to_string(pa_source_output_get_resample_method(o))); if (o->module) pa_strbuf_printf(s, "\towner module: %u\n", o->module->index); if (o->client) pa_strbuf_printf(s, "\tclient: %u <%s>\n", o->client->index, pa_strnull(pa_proplist_gets(o->client->proplist, PA_PROP_APPLICATION_NAME))); if (o->direct_on_input) pa_strbuf_printf(s, "\tdirect on input: %u\n", o->direct_on_input->index); t = pa_proplist_to_string_sep(o->proplist, "\n\t\t"); pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); } return pa_strbuf_tostring_free(s); }