Index: /trunk/src/VBox/Devices/Audio/DrvHostPulseAudio.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio/DrvHostPulseAudio.cpp	(revision 67950)
+++ /trunk/src/VBox/Devices/Audio/DrvHostPulseAudio.cpp	(revision 67951)
@@ -45,4 +45,7 @@
 *********************************************************************************************************************************/
 #define VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS 32 /** @todo Make this configurable thru driver options. */
+
+/* Whether to use PulseAudio's asynchronous handling or not. */
+//#define PULSEAUDIO_ASYNC /** @todo Make this configurable thru driver options. */
 
 #ifndef PA_STREAM_NOFLAGS
@@ -117,4 +120,10 @@
     size_t                 offPeekBuf;
     pa_operation          *pDrainOp;
+    /** Number of occurred audio data underflows. */
+    uint32_t               cUnderflows;
+    /** Current latency (in us). */
+    uint64_t               curLatencyUs;
+    /** Start time stamp (in us) of stream playback / recording. */
+    pa_usec_t              tsStartUs;
 } PULSEAUDIOSTREAM, *PPULSEAUDIOSTREAM;
 
@@ -177,6 +186,9 @@
 static int  paEnumerate(PDRVHOSTPULSEAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum);
 static int  paError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg);
+static void paStreamCbUnderflow(pa_stream *pStream, void *pvContext);
+#ifdef PULSEAUDIO_ASYNC
+ static void paStreamCbReqWrite(pa_stream *pStream, size_t cbLen, void *pvContext);
+#endif
 static void paStreamCbSuccess(pa_stream *pStream, int fSuccess, void *pvContext);
-
 
 
@@ -390,4 +402,60 @@
 
 
+#ifdef PULSEAUDIO_ASYNC
+static void paStreamCbReqWrite(pa_stream *pStream, size_t cbLen, void *pvContext)
+{
+    RT_NOREF(cbLen, pvContext);
+
+    PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvContext;
+    AssertPtrReturnVoid(pStrm);
+
+    pa_usec_t usec = 0;
+    int neg = 0;
+    pa_stream_get_latency(pStream, &usec, &neg);
+
+    Log2Func(("Requested %zu bytes -- Current latency is %RU64ms\n", cbLen, usec / 1000));
+}
+#endif /* PULSEAUDIO_ASYNC */
+
+
+static void paStreamCbUnderflow(pa_stream *pStream, void *pvContext)
+{
+    PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvContext;
+    AssertPtrReturnVoid(pStrm);
+
+    pStrm->cUnderflows++;
+
+    Log2Func(("Warning: Hit underflow #%RU32\n", pStrm->cUnderflows));
+
+    if (   pStrm->cUnderflows  >= 6                /** @todo Make this check configurable. */
+        && pStrm->curLatencyUs < 2000000 /* 2s */)
+    {
+        pStrm->curLatencyUs = (pStrm->curLatencyUs * 3) / 2;
+        LogFunc(("Latency increased to %RU64ms\n", pStrm->curLatencyUs / 1000));
+
+        pStrm->BufAttr.maxlength = pa_usec_to_bytes(pStrm->curLatencyUs, &pStrm->SampleSpec);
+        pStrm->BufAttr.tlength   = pa_usec_to_bytes(pStrm->curLatencyUs, &pStrm->SampleSpec);
+
+        pa_stream_set_buffer_attr(pStream, &pStrm->BufAttr, NULL, NULL);
+
+        pStrm->cUnderflows = 0;
+    }
+
+#ifdef LOG_ENABLED
+    pa_usec_t curLatencyUs = 0;
+    pa_stream_get_latency(pStream, &curLatencyUs, NULL /* Neg */);
+
+    const pa_timing_info *pTInfo = pa_stream_get_timing_info(pStream);
+    const pa_sample_spec *pSpec  = pa_stream_get_sample_spec(pStream);
+
+    pa_usec_t curPosWritesUs = pa_bytes_to_usec(pTInfo->write_index, pSpec);
+    pa_usec_t curTsUs        = pa_rtclock_now() - pStrm->tsStartUs;
+
+    Log2Func(("curPosWrite=%RU64ms, curTs=%RU64ms, curDelta=%RI64ms, curLatency=%RU64ms\n",
+              curPosWritesUs / 1000, curTsUs / 1000, (((int64_t)curPosWritesUs - (int64_t)curTsUs) / 1000), curLatencyUs / 1000));
+#endif
+}
+
+
 static void paStreamCbSuccess(pa_stream *pStream, int fSuccess, void *pvUser)
 {
@@ -406,29 +474,14 @@
 
 
-static int paStreamOpen(PDRVHOSTPULSEAUDIO pThis, bool fIn, const char *pszName,
-                        pa_sample_spec *pSampleSpec, pa_buffer_attr *pBufAttr,
-                        pa_stream **ppStream)
-{
-    AssertPtrReturn(pThis,       VERR_INVALID_POINTER);
-    AssertPtrReturn(pszName,     VERR_INVALID_POINTER);
-    AssertPtrReturn(pSampleSpec, VERR_INVALID_POINTER);
-    AssertPtrReturn(pBufAttr,    VERR_INVALID_POINTER);
-    AssertPtrReturn(ppStream,    VERR_INVALID_POINTER);
-
-    if (!pa_sample_spec_valid(pSampleSpec))
-    {
-        LogRel(("PulseAudio: Unsupported sample specification for stream '%s'\n",
-                pszName));
-        return VERR_NOT_SUPPORTED;
-    }
+static int paStreamOpen(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, bool fIn, const char *pszName)
+{
+    AssertPtrReturn(pThis,     VERR_INVALID_POINTER);
+    AssertPtrReturn(pStreamPA, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszName,   VERR_INVALID_POINTER);
 
     int rc = VINF_SUCCESS;
 
     pa_stream *pStream = NULL;
-    uint32_t   flags = PA_STREAM_NOFLAGS;
-
-    LogFunc(("Opening '%s', rate=%dHz, channels=%d, format=%s\n",
-             pszName, pSampleSpec->rate, pSampleSpec->channels,
-             pa_sample_format_to_string(pSampleSpec->format)));
+    uint32_t   flags   = PA_STREAM_NOFLAGS;
 
     pa_threaded_mainloop_lock(pThis->pMainLoop);
@@ -436,7 +489,21 @@
     do
     {
+        pa_sample_spec *pSampleSpec = &pStreamPA->SampleSpec;
+
+        LogFunc(("Opening '%s', rate=%dHz, channels=%d, format=%s\n",
+                 pszName, pSampleSpec->rate, pSampleSpec->channels,
+                 pa_sample_format_to_string(pSampleSpec->format)));
+
+        if (!pa_sample_spec_valid(pSampleSpec))
+        {
+            LogRel(("PulseAudio: Unsupported sample specification for stream '%s'\n", pszName));
+            rc = VERR_NOT_SUPPORTED;
+            break;
+        }
+
+        pa_buffer_attr *pBufAttr = &pStreamPA->BufAttr;
+
         /** @todo r=andy Use pa_stream_new_with_proplist instead. */
-        if (!(pStream = pa_stream_new(pThis->pContext, pszName, pSampleSpec,
-                                      NULL /* pa_channel_map */)))
+        if (!(pStream = pa_stream_new(pThis->pContext, pszName, pSampleSpec, NULL /* pa_channel_map */)))
         {
             LogRel(("PulseAudio: Could not create stream '%s'\n", pszName));
@@ -445,5 +512,9 @@
         }
 
-        pa_stream_set_state_callback(pStream, paStreamCbStateChanged, pThis);
+#ifdef PULSEAUDIO_ASYNC
+        pa_stream_set_write_callback(pStream,     paStreamCbReqWrite,     pStreamPA);
+#endif
+        pa_stream_set_underflow_callback(pStream, paStreamCbUnderflow,    pStreamPA);
+        pa_stream_set_state_callback(pStream,     paStreamCbStateChanged, pThis);
 
 #if PA_API_VERSION >= 12
@@ -451,9 +522,7 @@
         flags |= PA_STREAM_ADJUST_LATENCY;
 #endif
-
-#if 0
-        /* Not applicable as we don't use pa_stream_get_latency() and pa_stream_get_time(). */
+        /* For using pa_stream_get_latency() and pa_stream_get_time(). */
         flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
-#endif
+
         /* No input/output right away after the stream was started. */
         flags |= PA_STREAM_START_CORKED;
@@ -506,4 +575,6 @@
         }
 
+        pStreamPA->tsStartUs = pa_rtclock_now();
+
         if (RT_FAILURE(rc))
             break;
@@ -513,10 +584,9 @@
         memcpy(pBufAttr, pBufAttrObtained, sizeof(pa_buffer_attr));
 
-        if (fIn)
-            LogFunc(("Obtained record buffer attributes: maxlength=%RU32, fragsize=%RU32\n",
-                     pBufAttr->maxlength, pBufAttr->fragsize));
-        else
-            LogFunc(("Obtained playback buffer attributes: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d\n",
-                     pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
+        LogFunc(("Obtained %s buffer attributes: tLength=%RU32, maxLength=%RU32, minReq=%RU32, fragSize=%RU32, preBuf=%RU32\n",
+                 fIn ? "capture" : "playback",
+                 pBufAttr->tlength, pBufAttr->maxlength, pBufAttr->minreq, pBufAttr->fragsize, pBufAttr->prebuf));
+
+        pStreamPA->pStream = pStream;
 
     } while (0);
@@ -533,6 +603,4 @@
             pa_stream_unref(pStream);
     }
-    else
-        *ppStream = pStream;
 
     LogFlowFuncLeaveRC(rc);
@@ -662,15 +730,18 @@
     pStreamPA->SampleSpec.channels = pCfgReq->Props.cChannels;
 
-    /* Note that setting maxlength to -1 does not work on PulseAudio servers
-     * older than 0.9.10. So use the suggested value of 3/2 of tlength */
-    pStreamPA->BufAttr.tlength     =   (pa_bytes_per_second(&pStreamPA->SampleSpec)
-                                        * s_pulseCfg.buffer_msecs_out) / 1000;
-    pStreamPA->BufAttr.maxlength   = (pStreamPA->BufAttr.tlength * 3) / 2;
-    pStreamPA->BufAttr.prebuf      = -1; /* Same as tlength */
-    pStreamPA->BufAttr.minreq      = -1;
+    pStreamPA->curLatencyUs        = 100 * 1000; /** 100ms latency by default. @todo Make this configurable. */
+
+    const uint32_t mixsize = pa_usec_to_bytes(pStreamPA->curLatencyUs, &pStreamPA->SampleSpec);
+
+    pStreamPA->BufAttr.maxlength   = mixsize * 4;
+    pStreamPA->BufAttr.tlength     = mixsize;
+    pStreamPA->BufAttr.prebuf      = mixsize * 2;
+    pStreamPA->BufAttr.minreq      = mixsize;
+
+    LogFunc(("BufAttr tlength=%RU32, maxLength=%RU32, minReq=%RU32\n",
+             pStreamPA->BufAttr.tlength, pStreamPA->BufAttr.maxlength, pStreamPA->BufAttr.minreq));
 
     /* Note that the struct BufAttr is updated to the obtained values after this call! */
-    int rc = paStreamOpen(pThis, false /* fIn */, "PulseAudio (Out)",
-                          &pStreamPA->SampleSpec, &pStreamPA->BufAttr, &pStreamPA->pStream);
+    int rc = paStreamOpen(pThis, pStreamPA, false /* fIn */, "PulseAudio (Out)");
     if (RT_FAILURE(rc))
         return rc;
@@ -714,6 +785,5 @@
 
     /* Note: Other members of BufAttr are ignored for record streams. */
-    int rc = paStreamOpen(pThis, true /* fIn */, "PulseAudio (In)", &pStreamPA->SampleSpec, &pStreamPA->BufAttr,
-                          &pStreamPA->pStream);
+    int rc = paStreamOpen(pThis, pStreamPA, true /* fIn */, "PulseAudio (In)");
     if (RT_FAILURE(rc))
         return rc;
@@ -1439,8 +1509,13 @@
         {
             size_t cbWritable = pa_stream_writable_size(pStreamPA->pStream);
-            Log3Func(("cbWritable=%zu, cbMinReq=%RU32\n", cbWritable, pStreamPA->BufAttr.minreq));
-
-            if (cbWritable >= pStreamPA->BufAttr.minreq)
-                cbAvail = (uint32_t)cbWritable;
+
+            Log3Func(("cbWritable=%zu, maxLength=%RU32, minReq=%RU32\n",
+                      cbWritable, pStreamPA->BufAttr.maxlength, pStreamPA->BufAttr.minreq));
+
+            /* Don't report more writable than the PA server can handle. */
+            if (cbWritable > pStreamPA->BufAttr.maxlength)
+                cbWritable = pStreamPA->BufAttr.maxlength;
+
+            cbAvail = (uint32_t)cbWritable;
         }
         else
Index: /trunk/src/VBox/Devices/Audio/pulse_mangling.h
===================================================================
--- /trunk/src/VBox/Devices/Audio/pulse_mangling.h	(revision 67950)
+++ /trunk/src/VBox/Devices/Audio/pulse_mangling.h	(revision 67951)
@@ -6,5 +6,5 @@
 
 /*
- * Copyright (C) 2013-2016 Oracle Corporation
+ * Copyright (C) 2013-2017 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -30,5 +30,10 @@
 #define pa_stream_unref                         PULSE_MANGLER(pa_stream_unref)
 #define pa_stream_get_state                     PULSE_MANGLER(pa_stream_get_state)
+#define pa_stream_get_latency                   PULSE_MANGLER(pa_stream_get_latency)
+#define pa_stream_get_timing_info               PULSE_MANGLER(pa_stream_get_timing_info)
+#define pa_stream_set_buffer_attr               PULSE_MANGLER(pa_stream_set_buffer_attr)
 #define pa_stream_set_state_callback            PULSE_MANGLER(pa_stream_set_state_callback)
+#define pa_stream_set_underflow_callback        PULSE_MANGLER(pa_stream_set_underflow_callback)
+#define pa_stream_set_write_callback            PULSE_MANGLER(pa_stream_set_write_callback)
 #define pa_stream_flush                         PULSE_MANGLER(pa_stream_flush)
 #define pa_stream_drain                         PULSE_MANGLER(pa_stream_drain)
@@ -60,4 +65,7 @@
 #define pa_threaded_mainloop_lock               PULSE_MANGLER(pa_threaded_mainloop_lock)
 #define pa_bytes_per_second                     PULSE_MANGLER(pa_bytes_per_second)
+#define pa_bytes_to_usec                        PULSE_MANGLER(pa_bytes_to_usec)
+#define pa_usec_to_bytes                        PULSE_MANGLER(pa_usec_to_bytes)
+#define pa_rtclock_now                          PULSE_MANGLER(pa_rtclock_now)
 #define pa_frame_size                           PULSE_MANGLER(pa_frame_size)
 #define pa_sample_format_to_string              PULSE_MANGLER(pa_sample_format_to_string)
Index: /trunk/src/VBox/Devices/Audio/pulse_stubs.c
===================================================================
--- /trunk/src/VBox/Devices/Audio/pulse_stubs.c	(revision 67950)
+++ /trunk/src/VBox/Devices/Audio/pulse_stubs.c	(revision 67951)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2006-2016 Oracle Corporation
+ * Copyright (C) 2006-2017 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -79,6 +79,21 @@
                 (pa_stream *p),
                 (p))
+PROXY_STUB     (pa_stream_get_latency, int,
+                (pa_stream *s, pa_usec_t *r_usec, int *negative),
+                (s, r_usec, negative))
+PROXY_STUB     (pa_stream_get_timing_info, pa_timing_info*,
+                (pa_stream *s),
+                (s))
+PROXY_STUB      (pa_stream_set_buffer_attr, pa_operation *,
+                (pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata),
+                (s, attr, cb, userdata))
 PROXY_STUB_VOID(pa_stream_set_state_callback,
                 (pa_stream *s, pa_stream_notify_cb_t cb, void *userdata),
+                (s, cb, userdata))
+PROXY_STUB_VOID(pa_stream_set_underflow_callback,
+                (pa_stream *s, pa_stream_notify_cb_t cb, void *userdata),
+                (s, cb, userdata))
+PROXY_STUB_VOID(pa_stream_set_write_callback,
+                (pa_stream *s, pa_stream_request_cb_t cb, void *userdata),
                 (s, cb, userdata))
 PROXY_STUB     (pa_stream_flush, pa_operation*,
@@ -111,5 +126,6 @@
                 (p))
 PROXY_STUB     (pa_context_connect, int,
-                (pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api),
+                (pa_context *c, const char *server, pa_context_flags_t flags,
+                 const pa_spawn_api *api),
                 (c, server, flags, api))
 PROXY_STUB_VOID(pa_context_disconnect,
@@ -170,4 +186,13 @@
                 (const pa_sample_spec *spec),
                 (spec))
+PROXY_STUB     (pa_bytes_to_usec, pa_usec_t,
+                (uint64_t l, const pa_sample_spec *spec),
+                (l, spec))
+PROXY_STUB     (pa_usec_to_bytes, size_t,
+                (pa_usec_t t, const pa_sample_spec *spec),
+                (t, spec))
+PROXY_STUB     (pa_rtclock_now, pa_usec_t,
+                (void),
+                ())
 PROXY_STUB     (pa_frame_size, size_t,
                 (const pa_sample_spec *spec),
@@ -216,5 +241,10 @@
     ELEMENT(pa_stream_unref),
     ELEMENT(pa_stream_get_state),
+    ELEMENT(pa_stream_get_latency),
+    ELEMENT(pa_stream_get_timing_info),
+    ELEMENT(pa_stream_set_buffer_attr),
     ELEMENT(pa_stream_set_state_callback),
+    ELEMENT(pa_stream_set_underflow_callback),
+    ELEMENT(pa_stream_set_write_callback),
     ELEMENT(pa_stream_flush),
     ELEMENT(pa_stream_drain),
@@ -246,4 +276,7 @@
     ELEMENT(pa_threaded_mainloop_lock),
     ELEMENT(pa_bytes_per_second),
+    ELEMENT(pa_bytes_to_usec),
+    ELEMENT(pa_usec_to_bytes),
+    ELEMENT(pa_rtclock_now),
     ELEMENT(pa_frame_size),
     ELEMENT(pa_sample_format_to_string),
@@ -296,2 +329,3 @@
     return rc;
 }
+
Index: /trunk/src/VBox/Devices/Audio/pulse_stubs.h
===================================================================
--- /trunk/src/VBox/Devices/Audio/pulse_stubs.h	(revision 67950)
+++ /trunk/src/VBox/Devices/Audio/pulse_stubs.h	(revision 67951)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2006-2016 Oracle Corporation
+ * Copyright (C) 2006-2017 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
