VirtualBox

Changeset 89526 in vbox for trunk/src/VBox/Devices


Ignore:
Timestamp:
Jun 6, 2021 10:39:46 PM (4 years ago)
Author:
vboxsync
Message:

DrvHostAudioPulseAudio: More buffer config changes and better comments. bugref:9890

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Audio/DrvHostAudioPulseAudio.cpp

    r89522 r89526  
    742742static void drvHstAudPaStreamUnderflowDebugCallback(pa_stream *pStream, void *pvContext)
    743743{
    744     PDRVHSTAUDPASTREAM pStrm = (PDRVHSTAUDPASTREAM)pvContext;
    745     AssertPtrReturnVoid(pStrm);
    746 
    747     pStrm->cUnderflows++;
    748 
    749     LogRel2(("PulseAudio: Warning: Hit underflow #%RU32\n", pStrm->cUnderflows));
    750 
    751 # if 0
    752     if (   pStrm->cUnderflows >= 6                /** @todo Make this check configurable. */
    753         && pStrm->cUsLatency  < 2U*RT_US_1SEC)
    754     {
    755         pStrm->cUsLatency = pStrm->cUsLatency * 3 / 2;
    756         LogRel2(("PulseAudio: Increasing output latency to %'RU64 us\n", pStrm->cUsLatency));
    757 
    758         pStrm->BufAttr.maxlength = pa_usec_to_bytes(pStrm->cUsLatency, &pStrm->SampleSpec);
    759         pStrm->BufAttr.tlength   = pa_usec_to_bytes(pStrm->cUsLatency, &pStrm->SampleSpec);
    760         pa_operation *pOperation = pa_stream_set_buffer_attr(pStream, &pStrm->BufAttr, NULL, NULL);
     744    PDRVHSTAUDPASTREAM pStreamPA = (PDRVHSTAUDPASTREAM)pvContext;
     745    AssertPtrReturnVoid(pStreamPA);
     746
     747    pStreamPA->cUnderflows++;
     748
     749    LogRel2(("PulseAudio: Warning: Hit underflow #%RU32%s%s\n", pStreamPA->cUnderflows,
     750             pStreamPA->pDrainOp && pa_operation_get_state(pStreamPA->pDrainOp) == PA_OPERATION_RUNNING ? " (draining)" : "",
     751             pStreamPA->pCorkOp  && pa_operation_get_state(pStreamPA->pCorkOp)  == PA_OPERATION_RUNNING ? " (corking)" : "" ));
     752
     753# if 0 /* bird: It's certifiably insane to make buffer changes here and make DEBUG build behave differently from RELEASE builds. */
     754    if (   pStreamPA->cUnderflows >= 6                /** @todo Make this check configurable. */
     755        && pStreamPA->cUsLatency  < 2U*RT_US_1SEC)
     756    {
     757        pStreamPA->cUsLatency = pStreamPA->cUsLatency * 3 / 2;
     758        LogRel2(("PulseAudio: Increasing output latency to %'RU64 us\n", pStreamPA->cUsLatency));
     759
     760        pStreamPA->BufAttr.maxlength = pa_usec_to_bytes(pStreamPA->cUsLatency, &pStreamPA->SampleSpec);
     761        pStreamPA->BufAttr.tlength   = pa_usec_to_bytes(pStreamPA->cUsLatency, &pStreamPA->SampleSpec);
     762        pa_operation *pOperation = pa_stream_set_buffer_attr(pStream, &pStreamPA->BufAttr, NULL, NULL);
    761763        if (pOperation)
    762764            pa_operation_unref(pOperation);
     
    764766            LogRel2(("pa_stream_set_buffer_attr failed!\n"));
    765767
    766         pStrm->cUnderflows = 0;
     768        pStreamPA->cUnderflows = 0;
    767769    }
    768770# endif
     
    782784        Log2Func(("writepos=%'RU64 us, readpost=%'RU64 us, age=%'RU64 us, latency=%'RU64 us (%RU32Hz %RU8ch)\n",
    783785                  pa_bytes_to_usec(pTInfo->write_index, pSpec), pa_bytes_to_usec(pTInfo->read_index, pSpec),
    784                   pa_rtclock_now() - pStrm->tsStartUs, cUsLatency, pSpec->rate, pSpec->channels));
     786                  pa_rtclock_now() - pStreamPA->tsStartUs, cUsLatency, pSpec->rate, pSpec->channels));
    785787    }
    786788# endif
     
    884886    return VINF_SUCCESS;
    885887}
     888
     889
     890#if 0 /* experiment */
     891/**
     892 * Completion callback used with pa_stream_set_buffer_attr().
     893 */
     894static void drvHstAudPaStreamSetBufferAttrCompletionCallback(pa_stream *pStream, int fSuccess, void *pvUser)
     895{
     896    PDRVHSTAUDPA pThis = (PDRVHSTAUDPA)pvUser;
     897    LogFlowFunc(("fSuccess=%d\n", fSuccess));
     898    pa_threaded_mainloop_signal(pThis->pMainLoop, 0 /*fWaitForAccept*/);
     899    RT_NOREF(pStream);
     900}
     901#endif
    886902
    887903
     
    968984             */
    969985            const pa_buffer_attr *pBufAttribs = pa_stream_get_buffer_attr(pStream);
     986#if 0 /* Experiment for getting tlength closer to what we requested (ADJUST_LATENCY effect).
     987         Will slow down stream creation, so not pursued any further at present. */
     988            if (   pCfgAcq->enmDir == PDMAUDIODIR_OUT
     989                && pBufAttribs
     990                && pBufAttribs->tlength < pStreamPA->BufAttr.tlength)
     991            {
     992                pStreamPA->BufAttr.maxlength += (pStreamPA->BufAttr.tlength - pBufAttribs->tlength) * 2;
     993                pStreamPA->BufAttr.tlength   += (pStreamPA->BufAttr.tlength - pBufAttribs->tlength) * 2;
     994                LogRel(("Before pa_stream_set_buffer_attr: tlength=%#x (trying =%#x)\n", pBufAttribs->tlength, pStreamPA->BufAttr.tlength));
     995                drvHstAudPaWaitFor(pThis, pa_stream_set_buffer_attr(pStream, &pStreamPA->BufAttr,
     996                                                                    drvHstAudPaStreamSetBufferAttrCompletionCallback, pThis));
     997                pBufAttribs = pa_stream_get_buffer_attr(pStream);
     998                LogRel(("After  pa_stream_set_buffer_attr: tlength=%#x\n", pBufAttribs->tlength));
     999            }
     1000#endif
    9701001            AssertPtr(pBufAttribs);
    9711002            if (pBufAttribs)
     
    11511182    {
    11521183        /*
     1184         * Convert the requested buffer parameters to PA bytes.
     1185         */
     1186        uint32_t const cbBuffer    = pa_usec_to_bytes(PDMAudioPropsFramesToMicro(&pCfgAcq->Props,
     1187                                                                                 pCfgReq->Backend.cFramesBufferSize),
     1188                                                      &pStreamPA->SampleSpec);
     1189        uint32_t const cbPreBuffer = pa_usec_to_bytes(PDMAudioPropsFramesToMicro(&pCfgAcq->Props,
     1190                                                                                 pCfgReq->Backend.cFramesPreBuffering),
     1191                                                      &pStreamPA->SampleSpec);
     1192        uint32_t const cbSchedHint = pa_usec_to_bytes(pCfgReq->Device.cMsSchedulingHint * RT_US_1MS, &pStreamPA->SampleSpec);
     1193        RT_NOREF(cbBuffer, cbSchedHint, cbPreBuffer);
     1194
     1195        /*
    11531196         * Set up buffer attributes according to the stream type.
    1154          *
    1155          * For output streams we configure pre-buffering as requested, since
    1156          * there is little point in using a different size than DrvAudio. This
    1157          * assumes that a 'drain' request will override the prebuf size.
    11581197         */
    1159         pStreamPA->BufAttr.maxlength = UINT32_MAX; /* Let the PulseAudio server choose the biggest size it can handle. */
    11601198        if (pCfgReq->enmDir == PDMAUDIODIR_IN)
    11611199        {
    1162             pStreamPA->BufAttr.fragsize  = PDMAudioPropsFramesToBytes(&pCfgAcq->Props, pCfgReq->Backend.cFramesPeriod);
    1163             LogFunc(("Requesting: BufAttr: fragsize=%RU32\n", pStreamPA->BufAttr.fragsize));
    1164             /* (rlength, minreq and prebuf are playback only) */
     1200            /* Set maxlength to the requested buffer size. */
     1201            pStreamPA->BufAttr.maxlength = cbBuffer;
     1202
     1203            /* Set the fragment size according to the scheduling hint (forget
     1204               cFramesPeriod, it's generally rubbish on input). */
     1205            pStreamPA->BufAttr.fragsize  = cbSchedHint;
     1206
     1207            /* (tlength, minreq and prebuf are playback only) */
     1208            LogFunc(("Requesting: BufAttr: fragsize=%#RX32 maxLength=%#RX32\n",
     1209                     pStreamPA->BufAttr.fragsize, pStreamPA->BufAttr.maxlength));
    11651210        }
    11661211        else
    11671212        {
    1168             pStreamPA->cUsLatency        = PDMAudioPropsFramesToMicro(&pCfgAcq->Props, pCfgReq->Backend.cFramesBufferSize);
    1169             pStreamPA->BufAttr.tlength   = pa_usec_to_bytes(pStreamPA->cUsLatency, &pStreamPA->SampleSpec);
    1170 #if 0 /* bird: Bad bad idea. Messes up output via a "Intel Corporation 200 Series PCH HD Audio"
    1171                device here on fedora-32. Just use the default instead. */
    1172             pStreamPA->BufAttr.minreq    = PDMAudioPropsFramesToBytes(&pCfgAcq->Props, pCfgReq->Backend.cFramesPeriod);
    1173 #else
    1174             pStreamPA->BufAttr.minreq    = -1;
    1175 #endif
    1176             pStreamPA->BufAttr.prebuf    = pa_usec_to_bytes(PDMAudioPropsFramesToMicro(&pCfgAcq->Props,
    1177                                                                                        pCfgReq->Backend.cFramesPreBuffering),
    1178                                                             &pStreamPA->SampleSpec);
     1213            /* Set tlength to the desired buffer size as PA doesn't have any way
     1214               of telling us if anything beyond tlength is writable or not (see
     1215               drvHstAudPaStreamGetWritableLocked for more).  Because of the
     1216               ADJUST_LATENCY flag, this value will be adjusted down, so we'll
     1217               end up with less buffer than what we requested, however it should
     1218               probably reflect the actual latency a bit closer.  Probably not
     1219               worth trying to adjust this via pa_stream_set_buffer_attr. */
     1220            pStreamPA->BufAttr.tlength = cbBuffer;
     1221
     1222            /* Set maxlength to the same as tlength as we won't ever write more
     1223               than tlength. */
     1224            pStreamPA->BufAttr.maxlength = pStreamPA->BufAttr.tlength;
     1225
     1226            /* According to vlc, pulseaudio goes berserk if the minreq is not
     1227               significantly smaller than half of tlength.  They use a 1:3 ratio
     1228               between minreq and tlength.  Traditionally, we've used to just
     1229               pass the period value here, however the quality of the incoming
     1230               cFramesPeriod value is so variable that just ignore it.  This
     1231               minreq value is mainly about updating the pa_stream_writable_size
     1232               return value, so it makes sense that it need to be well below
     1233               half of the buffer length, otherwise we will think the buffer
     1234               is full for too long when it isn't.
     1235
     1236               The DMA scheduling hint is often a much better indicator. Just
     1237               to avoid generating too much IPC, limit this to 10 ms. */
     1238            uint32_t const cbMinUpdate = pa_usec_to_bytes(RT_US_10MS, &pStreamPA->SampleSpec);
     1239            pStreamPA->BufAttr.minreq = RT_MIN(RT_MAX(cbSchedHint, cbMinUpdate),
     1240                                               pStreamPA->BufAttr.tlength / 4);
     1241
     1242            /* Just pass along the requested pre-buffering size.  This seems
     1243               typically to be unaltered by pa_stream_connect_playback.  Not
     1244               sure if tlength is perhaps adjusted relative to it...  Ratio
     1245               seen here is prebuf=93.75% of tlength.  This isn't entirely
     1246               optimal as we use 50% by default (see DrvAudio) so that there
     1247               is equal room for the guest to run too fast and too slow.  Not
     1248               much we can do about it w/o slowing down stream creation. */
     1249            pStreamPA->BufAttr.prebuf = cbPreBuffer;
     1250
    11791251            /* (fragsize is capture only) */
     1252            LogRel2(("PulseAudio: Requesting: BufAttr: tlength=%#RX32 minReq=%#RX32 prebuf=%#RX32 maxLength=%#RX32\n",
     1253                     pStreamPA->BufAttr.tlength, pStreamPA->BufAttr.minreq, pStreamPA->BufAttr.prebuf, pStreamPA->BufAttr.maxlength));
     1254
     1255            /* This (cUsLatency) isn't used for anything. */
     1256            pStreamPA->cUsLatency = PDMAudioPropsFramesToMicro(&pCfgAcq->Props, pCfgReq->Backend.cFramesBufferSize);
    11801257            LogRel2(("PulseAudio: Initial output latency is %RU64 us (%RU32 bytes)\n",
    11811258                     pStreamPA->cUsLatency, pStreamPA->BufAttr.tlength));
    1182             LogFunc(("Requesting: BufAttr: tlength=%RU32 maxLength=%RU32 minReq=%RU32 maxlength=-1\n",
    1183                      pStreamPA->BufAttr.tlength, pStreamPA->BufAttr.maxlength, pStreamPA->BufAttr.minreq));
    11841259        }
    11851260
     
    11951270             * Set the acquired stream config according to the actual buffer
    11961271             * attributes we got and the stream type.
     1272             *
     1273             * Note! We use maxlength for input buffer and tlength for the
     1274             *       output buffer size.  See above for why.
    11971275             */
    11981276            if (pCfgReq->enmDir == PDMAUDIODIR_IN)
    11991277            {
     1278                LogRel2(("PulseAudio: Got:        BufAttr: fragsize=%#RX32 maxLength=%#RX32\n",
     1279                         pStreamPA->BufAttr.fragsize, pStreamPA->BufAttr.maxlength));
    12001280                pCfgAcq->Backend.cFramesPeriod       = PDMAudioPropsBytesToFrames(&pCfgAcq->Props, pStreamPA->BufAttr.fragsize);
    12011281                pCfgAcq->Backend.cFramesBufferSize   = pStreamPA->BufAttr.maxlength != UINT32_MAX /* paranoia */
    12021282                                                     ? PDMAudioPropsBytesToFrames(&pCfgAcq->Props, pStreamPA->BufAttr.maxlength)
    1203                                                      : pCfgAcq->Backend.cFramesPeriod * 2 /* whatever */;
    1204                 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod;
     1283                                                     : pCfgAcq->Backend.cFramesPeriod * 3 /* whatever */;
     1284                pCfgAcq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesPreBuffering * pCfgAcq->Backend.cFramesBufferSize
     1285                                                     / RT_MAX(pCfgReq->Backend.cFramesBufferSize, 1);
    12051286            }
    12061287            else
    12071288            {
    1208                 pCfgAcq->Backend.cFramesPeriod        = PDMAudioPropsBytesToFrames(&pCfgAcq->Props, pStreamPA->BufAttr.minreq);
    1209                 pCfgAcq->Backend.cFramesBufferSize    = PDMAudioPropsBytesToFrames(&pCfgAcq->Props, pStreamPA->BufAttr.tlength);
    1210                 pCfgAcq->Backend.cFramesPreBuffering  = pCfgReq->Backend.cFramesPreBuffering
    1211                                                       * pCfgAcq->Backend.cFramesBufferSize
    1212                                                       / RT_MAX(pCfgReq->Backend.cFramesBufferSize, 1);
     1289                LogRel2(("PulseAudio: Got:        BufAttr: tlength=%#RX32 minReq=%#RX32 prebuf=%#RX32 maxLength=%#RX32\n",
     1290                         pStreamPA->BufAttr.tlength, pStreamPA->BufAttr.minreq, pStreamPA->BufAttr.prebuf, pStreamPA->BufAttr.maxlength));
     1291                pCfgAcq->Backend.cFramesPeriod       = PDMAudioPropsBytesToFrames(&pCfgAcq->Props, pStreamPA->BufAttr.minreq);
     1292                pCfgAcq->Backend.cFramesBufferSize   = PDMAudioPropsBytesToFrames(&pCfgAcq->Props, pStreamPA->BufAttr.tlength);
     1293                pCfgAcq->Backend.cFramesPreBuffering = PDMAudioPropsBytesToFrames(&pCfgAcq->Props, pStreamPA->BufAttr.prebuf);
    12131294            }
    12141295
     
    16091690
    16101691/**
     1692 * Gets the number of bytes that can safely be written to a stream.
     1693 *
     1694 * @returns Number of writable bytes, ~(size_t)0 on error.
     1695 * @param   pStreamPA       The stream.
     1696 */
     1697DECLINLINE(uint32_t) drvHstAudPaStreamGetWritableLocked(PDRVHSTAUDPASTREAM pStreamPA)
     1698{
     1699    /* pa_stream_writable_size() returns the amount requested currently by the
     1700       server, we could write more than this if we liked.  The documentation says
     1701       up to maxlength, whoever I'm not sure how that limitation is enforced or
     1702       what would happen if we exceed it.  There seems to be no (simple) way to
     1703       figure out how much buffer we have left between what pa_stream_writable_size
     1704       returns and what maxlength indicates.
     1705
     1706       An alternative would be to guess the difference using the read and write
     1707       positions in the timing info, however the read position is only updated
     1708       when starting and stopping.  In the auto update mode it's updated at a
     1709       sharply decreasing rate starting at 10ms and ending at 1500ms.  So, not
     1710       all that helpful.  (As long as pa_stream_writable_size returns a non-zero
     1711       value, though, we could just add the maxlength-tlength difference. But
     1712       the problem is after that.)
     1713
     1714       So, for now we just use tlength = maxlength for output streams and
     1715       problem solved. */
     1716    size_t const cbWritablePa = pa_stream_writable_size(pStreamPA->pStream);
     1717#if 1
     1718    return cbWritablePa;
     1719#else
     1720    if (cbWritablePa > 0 && cbWritablePa != (size_t)-1)
     1721        return cbWritablePa + (pStreamPA->BufAttr.maxlength - pStreamPA->BufAttr.tlength);
     1722    //const pa_timing_info * const pTimingInfo  = pa_stream_get_timing_info(pStreamPA->pStream);
     1723    return 0;
     1724#endif
     1725}
     1726
     1727
     1728/**
    16111729 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
    16121730 */
     
    16231741        if (PA_STREAM_IS_GOOD(enmState))
    16241742        {
    1625             size_t cbWritablePa = pa_stream_writable_size(pStreamPA->pStream);
     1743            size_t cbWritablePa = drvHstAudPaStreamGetWritableLocked(pStreamPA);
    16261744            if (cbWritablePa != (size_t)-1)
    16271745                cbWritable = cbWritablePa <= UINT32_MAX ? (uint32_t)cbWritablePa : UINT32_MAX;
     
    16691787
    16701788    /*
    1671      * Using a loop here so we can take maxlength into account when writing.
     1789     * Using a loop here so we can stuff the buffer as full as it gets.
    16721790     */
    16731791    int      rc             = VINF_SUCCESS;
     
    16761794    for (iLoop = 0; ; iLoop++)
    16771795    {
    1678         size_t const cbWriteable = pa_stream_writable_size(pStreamPA->pStream);
     1796        size_t const cbWriteable = drvHstAudPaStreamGetWritableLocked(pStreamPA);
    16791797        if (   cbWriteable != (size_t)-1
    16801798            && cbWriteable >= PDMAudioPropsFrameSize(&pStreamPA->Cfg.Props))
    16811799        {
    1682             uint32_t cbToWrite = (uint32_t)RT_MIN(RT_MIN(cbWriteable, pStreamPA->BufAttr.maxlength), cbBuf);
     1800            uint32_t cbToWrite = (uint32_t)RT_MIN(cbWriteable, cbBuf);
    16831801            cbToWrite = PDMAudioPropsFloorBytesToFrame(&pStreamPA->Cfg.Props, cbToWrite);
    16841802            if (pa_stream_write(pStreamPA->pStream, pvBuf, cbToWrite, NULL /*pfnFree*/, 0 /*offset*/, PA_SEEK_RELATIVE) >= 0)
     
    18842002        else
    18852003        {
    1886             if (cbAvail != (size_t)-1)
     2004            if (cbAvail == (size_t)-1)
    18872005                rc = drvHstAudPaError(pStreamPA->pDrv, "pa_stream_readable_size failed on '%s'", pStreamPA->Cfg.szName);
    18882006            break;
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette