/* Called from main context */ static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) { struct userdata *u; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); if (!PA_SINK_IS_LINKED(state)) { return 0; } if (state == PA_SINK_RUNNING) { /* need to wake-up source if it was suspended */ pa_source_suspend(u->source, FALSE, PA_SUSPEND_ALL); /* FIXME: if there's no client connected, the source will suspend and playback will be stuck. You'd want to prevent the source from sleeping when the uplink sink is active; even if the audio is discarded at least the app isn't stuck */ } else { /* nothing to do, if the sink becomes idle or suspended let module-suspend-idle handle the sources later */ } return 0; }
static void sink_update_requested_latency_cb(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); /* FIXME: there's no latency support */ }
/* Called from main context */ static int raw_sink_set_state(pa_sink *s, pa_sink_state_t state) { struct userdata *u; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); int ret = voice_sink_set_state(s, u->voip_sink, state); pa_log_debug("(%p) called with %d", (void *)s, state); return ret; }
/* Called from I/O thread context */ static void voip_sink_request_rewind(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); /* Just hand this one over to the master sink */ if (u->hw_sink_input && s->thread_info.rewind_nbytes > 0) pa_sink_input_request_rewind(u->hw_sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE, FALSE); }
/* Called from I/O thread context */ static void sink_request_rewind_cb(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); /* Do nothing */ pa_sink_process_rewind(u->sink, 0); }
/* Called from main context */ static void sink_set_volume_cb(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) || !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) return; pa_sink_input_set_volume(u->sink_input, &s->real_volume, s->save_volume, TRUE); }
/* Called from main context */ static void sink_set_mute_cb(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) || !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) return; pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted); }
/* Called from I/O thread context */ static void sink_request_rewind(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) return; pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE, FALSE); }
/* Called from main context */ static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) { struct userdata *u; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); if (!PA_SINK_IS_LINKED(state) || !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) return 0; pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); return 0; }
/* Called from I/O thread context */ static void sink_request_rewind_cb(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) return; /* Just hand this one over to the master sink */ pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE, FALSE); }
/* Called from I/O thread context */ static void raw_sink_request_rewind(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); /* Just hand this one over to the master sink */ if (u->hw_sink_input && s->thread_info.rewind_nbytes > 0) { size_t nbytes = voice_convert_nbytes(s->thread_info.rewind_nbytes, &s->sample_spec, &u->hw_sink_input->sample_spec); pa_sink_input_request_rewind(u->hw_sink_input, nbytes, TRUE, FALSE, FALSE); } }
/* Called from I/O thread context */ static void sink_update_requested_latency_cb(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) return; /* Just hand this one over to the master sink */ pa_sink_input_set_requested_latency_within_thread( u->sink_input, pa_sink_get_requested_latency_within_thread(s)); }
static void sink_update_requested_latency_cb(pa_sink *s) { struct userdata *u; size_t nbytes; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); u->block_usec = pa_sink_get_requested_latency_within_thread(s); if (u->block_usec == (pa_usec_t) -1) u->block_usec = s->thread_info.max_latency; nbytes = pa_usec_to_bytes(u->block_usec, &s->sample_spec); pa_sink_set_max_rewind_within_thread(s, nbytes); pa_sink_set_max_request_within_thread(s, nbytes); }
/* Called from I/O thread context */ static void raw_sink_update_requested_latency(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); /* For example a2dp playback if sink is destroyed this function * is called with hw_sink_input->sink = NULL, while moving sink_input * elsewhere, which causes * pa_sink_input_set_requested_latency_within_thread() segfault. */ if (!u->hw_sink_input->sink) { pa_log_debug("%s() hw_sink_input->sink = NULL, won't propagate to master sink", __FUNCTION__); return; } /* Just hand this one over to the master sink */ pa_sink_input_set_requested_latency_within_thread( u->hw_sink_input, voice_sink_get_requested_latency(s, u->voip_sink)); }
static pa_bool_t shall_cork(pa_sink *s, pa_sink_input *ignore) { pa_sink_input *j; uint32_t idx; pa_sink_assert_ref(s); for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { const char *role; if (j == ignore) continue; if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE))) continue; if (pa_streq(role, "phone")) return TRUE; } return FALSE; }
/* Called from main context */ static int voip_sink_set_state(pa_sink *s, pa_sink_state_t state) { struct userdata *u; int ret = 0; ENTER(); pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); ret = voice_sink_set_state(s, u->raw_sink, state); /* TODO: Check if we still need to fiddle with PROP_MIXER_TUNING_MODE */ if (s->state != PA_SINK_RUNNING && state == PA_SINK_RUNNING) { voice_aep_ear_ref_loop_reset(u); meego_algorithm_hook_fire(u->hooks[HOOK_CALL_BEGIN], s); } else if (s->state == PA_SINK_RUNNING && state != PA_SINK_RUNNING) meego_algorithm_hook_fire(u->hooks[HOOK_CALL_END], s); pa_log_debug("(%p): called with %d", (void *)s, state); return ret; }
static void sink_update_requested_latency_cb(pa_sink *s) { struct userdata *u; size_t nbytes; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); u->block_usec = BLOCK_USEC; //u->block_usec = pa_sink_get_requested_latency_within_thread(s); pa_log("1 block_usec %d", u->block_usec); u->got_max_latency = 0; if (u->block_usec == (pa_usec_t) -1) { u->block_usec = s->thread_info.max_latency; pa_log_debug("2 block_usec %d", u->block_usec); u->got_max_latency = 1; } nbytes = pa_usec_to_bytes(u->block_usec, &s->sample_spec); pa_sink_set_max_rewind_within_thread(s, nbytes); pa_sink_set_max_request_within_thread(s, nbytes); }
static void apply_cork(struct userdata *u, pa_sink *s, pa_sink_input *ignore, pa_bool_t cork) { pa_sink_input *j; uint32_t idx; pa_assert(u); pa_sink_assert_ref(s); for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { pa_bool_t corked; const char *role; if (j == ignore) continue; if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE))) continue; if (!pa_streq(role, "video") && !pa_streq(role, "music")) continue; corked = !!pa_hashmap_get(u->cork_state, j); if (cork && !corked) { pa_hashmap_put(u->cork_state, j, PA_INT_TO_PTR(1)); pa_sink_input_set_mute(j, TRUE, FALSE); pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_CORK, NULL); } else if (!cork) { pa_hashmap_remove(u->cork_state, j); if (corked) { pa_sink_input_set_mute(j, FALSE, FALSE); pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_UNCORK, NULL); } } } }
static void sink_update_requested_latency_cb(pa_sink *s) { struct userdata *u; pa_operation *operation; size_t nbytes; pa_usec_t block_usec; pa_buffer_attr bufferattr; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); block_usec = pa_sink_get_requested_latency_within_thread(s); if (block_usec == (pa_usec_t) -1) block_usec = s->thread_info.max_latency; nbytes = pa_usec_to_bytes(block_usec, &s->sample_spec); pa_sink_set_max_request_within_thread(s, nbytes); if (u->stream) { switch (pa_stream_get_state(u->stream)) { case PA_STREAM_READY: if (pa_stream_get_buffer_attr(u->stream)->tlength == nbytes) break; reset_bufferattr(&bufferattr); bufferattr.tlength = nbytes; if ((operation = pa_stream_set_buffer_attr(u->stream, &bufferattr, stream_set_buffer_attr_cb, u))) pa_operation_unref(operation); break; case PA_STREAM_CREATING: /* we have to delay our request until stream is ready */ u->update_stream_bufferattr_after_connect = true; break; default: break; } } }
/* Generic sink state change logic. Used by raw_sink and voip_sink */ int voice_sink_set_state(pa_sink *s, pa_sink *other, pa_sink_state_t state) { struct userdata *u; pa_sink *om_sink; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); if (!other) { pa_log_debug("other sink not initialized or freed"); return 0; } pa_sink_assert_ref(other); om_sink = u->master_sink; if (u->hw_sink_input && PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->hw_sink_input))) { if (pa_sink_input_get_state(u->hw_sink_input) == PA_SINK_INPUT_CORKED) { if (PA_SINK_IS_OPENED(state) || PA_SINK_IS_OPENED(pa_sink_get_state(other)) || pa_atomic_load(&u->cmt_connection.dl_state) == CMT_DL_ACTIVE) { pa_sink_input_cork(u->hw_sink_input, FALSE); pa_log_debug("hw_sink_input uncorked"); } } else { if (state == PA_SINK_SUSPENDED && pa_sink_get_state(other) == PA_SINK_SUSPENDED && pa_atomic_load(&u->cmt_connection.dl_state) != CMT_DL_ACTIVE) { pa_sink_input_cork(u->hw_sink_input, TRUE); pa_log_debug("hw_sink_input corked"); } } } if (om_sink == NULL) { pa_log_info("No master sink, assuming primary mixer tuning.\n"); pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_PRI); } else if (pa_atomic_load(&u->cmt_connection.dl_state) == CMT_DL_ACTIVE || (pa_sink_get_state(u->voip_sink) <= PA_SINK_SUSPENDED && voice_voip_sink_used_by(u))) { if (pa_atomic_load(&u->mixer_state) == PROP_MIXER_TUNING_PRI) { pa_proplist *p = pa_proplist_new(); pa_assert(p); pa_proplist_sets(p, PROP_MIXER_TUNING_MODE, PROP_MIXER_TUNING_ALT_S); pa_sink_update_proplist(om_sink, PA_UPDATE_REPLACE, p); pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_ALT); pa_proplist_free(p); if (u->sidetone_enable) voice_enable_sidetone(u,1); } } else { if (pa_atomic_load(&u->mixer_state) == PROP_MIXER_TUNING_ALT) { pa_proplist *p = pa_proplist_new(); pa_assert(p); pa_proplist_sets(p, PROP_MIXER_TUNING_MODE, PROP_MIXER_TUNING_PRI_S); pa_sink_update_proplist(om_sink, PA_UPDATE_REPLACE, p); pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_PRI); pa_proplist_free(p); voice_enable_sidetone(u,0); } } return 0; }