+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);
+}
+