/* Called from output thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; pa_sink_input_assert_ref(i); pa_sink_input_assert_io_context(i); pa_assert_se(u = i->userdata); pa_assert(chunk); u->in_pop = TRUE; while (pa_asyncmsgq_process_one(u->asyncmsgq) > 0) ; u->in_pop = FALSE; if (pa_memblockq_peek(u->memblockq, chunk) < 0) { pa_log_info("Could not peek into queue"); return -1; } chunk->length = PA_MIN(chunk->length, nbytes); pa_memblockq_drop(u->memblockq, chunk->length); update_min_memblockq_length(u); return 0; }
/* Called from output thread context */ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; pa_sink_input_assert_ref(i); pa_sink_input_assert_io_context(i); pa_assert_se(u = i->userdata); pa_memblockq_set_maxrewind(u->memblockq, nbytes); }
/* Called from output thread context */ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; pa_sink_input_assert_ref(i); pa_sink_input_assert_io_context(i); pa_assert_se(u = i->userdata); pa_memblockq_set_prebuf(u->memblockq, nbytes*2); pa_log_info("Max request changed"); }
/* Called from output thread context */ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; pa_sink_input_assert_ref(i); pa_sink_input_assert_io_context(i); pa_assert_se(u = i->userdata); pa_memblockq_set_prebuf(u->memblockq, nbytes*2); pa_log_info("Max request changed"); pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED, NULL, 0, NULL, NULL); }
/* Called from output thread context */ static void sink_input_detach_cb(pa_sink_input *i) { struct userdata *u; pa_sink_input_assert_ref(i); pa_sink_input_assert_io_context(i); pa_assert_se(u = i->userdata); if (u->rtpoll_item_read) { pa_rtpoll_item_free(u->rtpoll_item_read); u->rtpoll_item_read = NULL; } }
/* Called from output thread context */ static void update_min_memblockq_length(struct userdata *u) { size_t length; pa_assert(u); pa_sink_input_assert_io_context(u->sink_input); length = pa_memblockq_get_length(u->memblockq); if (u->min_memblockq_length == (size_t) -1 || length < u->min_memblockq_length) u->min_memblockq_length = length; }
/* Called from output thread context */ static void sink_input_attach_cb(pa_sink_input *i) { struct userdata *u; pa_sink_input_assert_ref(i); pa_sink_input_assert_io_context(i); pa_assert_se(u = i->userdata); u->rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read( i->sink->thread_info.rtpoll, PA_RTPOLL_LATE, u->asyncmsgq); pa_memblockq_set_prebuf(u->memblockq, pa_sink_input_get_max_request(i)*2); pa_memblockq_set_maxrewind(u->memblockq, pa_sink_input_get_max_rewind(i)); }
/* Called from output thread context */ static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SINK_INPUT(obj)->userdata; switch (code) { case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { pa_usec_t *r = data; pa_sink_input_assert_io_context(u->sink_input); *r = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->sink_input->sample_spec); /* Fall through, the default handler will add in the extra * latency added by the resampler */ break; } case SINK_INPUT_MESSAGE_POST: pa_sink_input_assert_io_context(u->sink_input); if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state)) pa_memblockq_push_align(u->memblockq, chunk); else pa_memblockq_flush_write(u->memblockq, TRUE); update_min_memblockq_length(u); /* Is this the end of an underrun? Then let's start things * right-away */ if (!u->in_pop && u->sink_input->thread_info.underrun_for > 0 && pa_memblockq_is_readable(u->memblockq)) { pa_log_debug("Requesting rewind due to end of underrun."); pa_sink_input_request_rewind(u->sink_input, (size_t) (u->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : u->sink_input->thread_info.underrun_for), FALSE, TRUE, FALSE); } u->recv_counter += (int64_t) chunk->length; return 0; case SINK_INPUT_MESSAGE_REWIND: pa_sink_input_assert_io_context(u->sink_input); if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state)) pa_memblockq_seek(u->memblockq, -offset, PA_SEEK_RELATIVE, TRUE); else pa_memblockq_flush_write(u->memblockq, TRUE); u->recv_counter -= offset; update_min_memblockq_length(u); return 0; case SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT: { size_t length; update_min_memblockq_length(u); length = pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq); u->latency_snapshot.recv_counter = u->recv_counter; u->latency_snapshot.sink_input_buffer = pa_memblockq_get_length(u->memblockq) + (u->sink_input->thread_info.resampler ? pa_resampler_request(u->sink_input->thread_info.resampler, length) : length); u->latency_snapshot.sink_latency = pa_sink_get_latency_within_thread(u->sink_input->sink); u->latency_snapshot.max_request = pa_sink_input_get_max_request(u->sink_input); u->latency_snapshot.min_memblockq_length = u->min_memblockq_length; u->min_memblockq_length = (size_t) -1; return 0; } case SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED: { /* This message is sent from the IO thread to the main * thread! So don't be confused. All the user cases above * are executed in thread context, but this one is not! */ pa_assert_ctl_context(); if (u->time_event) adjust_rates(u); return 0; } } return pa_sink_input_process_msg(obj, code, data, offset, chunk); }
/* Called from output thread context */ static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SINK_INPUT(obj)->userdata; switch (code) { case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { pa_usec_t *r = data; pa_sink_input_assert_io_context(u->sink_input); *r = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->sink_input->sample_spec); /* Fall through, the default handler will add in the extra * latency added by the resampler */ break; } case SINK_INPUT_MESSAGE_POST: pa_sink_input_assert_io_context(u->sink_input); if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state)) pa_memblockq_push_align(u->memblockq, chunk); else pa_memblockq_flush_write(u->memblockq, true); /* Is this the end of an underrun? Then let's start things * right-away */ if (!u->in_pop && u->sink_input->thread_info.underrun_for > 0 && pa_memblockq_is_readable(u->memblockq)) { pa_log_debug("Requesting rewind due to end of underrun."); pa_sink_input_request_rewind(u->sink_input, (size_t) (u->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : u->sink_input->thread_info.underrun_for), false, true, false); } u->recv_counter += (int64_t) chunk->length; return 0; case SINK_INPUT_MESSAGE_REWIND: pa_sink_input_assert_io_context(u->sink_input); if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state)) pa_memblockq_seek(u->memblockq, -offset, PA_SEEK_RELATIVE, true); else pa_memblockq_flush_write(u->memblockq, true); u->recv_counter -= offset; return 0; case SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT: { size_t length; length = pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq); u->latency_snapshot.recv_counter = u->recv_counter; u->latency_snapshot.sink_input_buffer = pa_memblockq_get_length(u->memblockq); /* Add content of render memblockq to sink latency */ u->latency_snapshot.sink_latency = pa_sink_get_latency_within_thread(u->sink_input->sink) + pa_bytes_to_usec(length, &u->sink_input->sink->sample_spec); u->latency_snapshot.sink_timestamp = pa_rtclock_now(); return 0; } } return pa_sink_input_process_msg(obj, code, data, offset, chunk); }