core, alsa: Better drain reporting

Previously, a drain request was acknowledged up to two hw buffers
too late, causing unnecessary delays.

This implements a new chain of events called process_underrun
which triggers exactly when the sink input has finished playing,
so the drain can be acknowledged quicker.

It could later be improved to give better underrun reporting to
clients too.

Tested-by: Dmitri Paduchikh <dpaduchikh@gmail.com>
Signed-off-by: David Henningsson <david.henningsson@canonical.com>
This commit is contained in:
David Henningsson 2013-03-01 11:51:54 +01:00
parent ada92732f0
commit e87eb85474
6 changed files with 139 additions and 33 deletions

View file

@ -231,6 +231,7 @@ enum {
CONNECTION_MESSAGE_REVOKE
};
static bool sink_input_process_underrun_cb(pa_sink_input *i);
static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
static void sink_input_kill_cb(pa_sink_input *i);
static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend);
@ -1171,6 +1172,7 @@ static playback_stream* playback_stream_new(
s->sink_input->parent.process_msg = sink_input_process_msg;
s->sink_input->pop = sink_input_pop_cb;
s->sink_input->process_underrun = sink_input_process_underrun_cb;
s->sink_input->process_rewind = sink_input_process_rewind_cb;
s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
s->sink_input->update_max_request = sink_input_update_max_request_cb;
@ -1573,6 +1575,44 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
}
static bool handle_input_underrun(playback_stream *s, bool force)
{
bool send_drain;
if (pa_memblockq_is_readable(s->memblockq))
return false;
if (!s->is_underrun)
pa_log_debug("%s %s of '%s'", force ? "Actual" : "Implicit",
s->drain_request ? "drain" : "underrun", pa_strnull(pa_proplist_gets(s->sink_input->proplist, PA_PROP_MEDIA_NAME)));
send_drain = s->drain_request && (force || pa_sink_input_safe_to_remove(s->sink_input));
if (send_drain) {
s->drain_request = false;
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL);
pa_log_debug("Drain acknowledged of '%s'", pa_strnull(pa_proplist_gets(s->sink_input->proplist, PA_PROP_MEDIA_NAME)));
} else if (!s->is_underrun) {
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, pa_memblockq_get_read_index(s->memblockq), NULL, NULL);
}
s->is_underrun = true;
playback_stream_request_bytes(s);
return true;
}
/* Called from thread context */
static bool sink_input_process_underrun_cb(pa_sink_input *i) {
playback_stream *s;
pa_sink_input_assert_ref(i);
s = PLAYBACK_STREAM(i->userdata);
playback_stream_assert_ref(s);
return handle_input_underrun(s, true);
}
/* Called from thread context */
static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
playback_stream *s;
@ -1586,25 +1626,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
pa_log("%s, pop(): %lu", pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME), (unsigned long) pa_memblockq_get_length(s->memblockq));
#endif
if (pa_memblockq_is_readable(s->memblockq))
s->is_underrun = FALSE;
else {
if (!s->is_underrun)
pa_log_debug("Underrun on '%s', %lu bytes in queue.", pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)), (unsigned long) pa_memblockq_get_length(s->memblockq));
if (s->drain_request && pa_sink_input_safe_to_remove(i)) {
s->drain_request = FALSE;
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL);
} else if (!s->is_underrun)
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, pa_memblockq_get_read_index(s->memblockq), NULL, NULL);
s->is_underrun = TRUE;
playback_stream_request_bytes(s);
}
if (!handle_input_underrun(s, false))
s->is_underrun = false;
/* This call will not fail with prebuf=0, hence we check for
underrun explicitly above */
underrun explicitly in handle_input_underrun */
if (pa_memblockq_peek(s->memblockq, chunk) < 0)
return -1;