VirtualBox

Changeset 88566 in vbox


Ignore:
Timestamp:
Apr 16, 2021 1:55:05 PM (3 years ago)
Author:
vboxsync
Message:

DrvHostAudioDSound: Added a timer hack for making sure draining streams doesn't end up looping forever (DSound race?). bugref:9890

File:
1 edited

Legend:

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

    r88564 r88566  
    108108typedef struct DSOUNDSTREAM
    109109{
     110    /** Entry in DRVHOSTDSOUND::HeadStreams. */
     111    RTLISTNODE          ListEntry;
    110112    /** The stream's acquired configuration. */
    111113    PDMAUDIOSTREAMCFG   Cfg;
     
    155157             *  to the DirectSound buffer was. */
    156158            uint32_t                    cbLastTransferred;
     159            /** The RTTimeMilliTS() deadline for the draining of this stream. */
     160            uint64_t                    msDrainDeadline;
    157161        } Out;
    158162    };
     
    165169    char                szStatus[127];
    166170    /** Fixed zero terminator. */
    167     char const         chStateZero;
     171    char const          chStateZero;
    168172} DSOUNDSTREAM, *PDSOUNDSTREAM;
    169173
     
    213217    /** The Direct Sound capturing interface. */
    214218    LPDIRECTSOUNDCAPTURE8       pDSC;
     219    /** A drain stop timer that makes sure a draining stream will be
     220     *  properly stopped (seem the non-loop mode is a bit buggy). */
     221    TMTIMERHANDLE               hDrainTimer;
     222    /** List of streams (DSOUNDSTREAM).
     223     * Requires CritSect ownership.  */
     224    RTLISTANCHOR                HeadStreams;
     225
    215226#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
    216227    DrvHostAudioDSoundMMNotifClient *m_pNotificationClient;
     
    16411652                 pCfgReq->enmDir == PDMAUDIODIR_IN ? PDMAudioRecSrcGetName(pCfgReq->u.enmSrc)
    16421653                 : PDMAudioPlaybackDstGetName(pCfgReq->u.enmDst), pCfgReq->szName));
     1654    RTListInit(&pStreamDS->ListEntry); /* paranoia */
    16431655
    16441656    /* For whatever reason: */
     
    16931705        PDMAudioStrmCfgCopy(&pStreamDS->Cfg, pCfgAcq);
    16941706        drvHostDSoundStreamReset(pThis, pStreamDS);
     1707
     1708        RTCritSectEnter(&pThis->CritSect);
     1709        RTListAppend(&pThis->HeadStreams, &pStreamDS->ListEntry);
     1710        RTCritSectLeave(&pThis->CritSect);
     1711
    16951712        rc = VINF_SUCCESS;
    16961713    }
     
    17121729    AssertPtrReturn(pStreamDS, VERR_INVALID_POINTER);
    17131730    LogFlowFunc(("Stream '%s' {%s}\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS)));
     1731
     1732    RTCritSectEnter(&pThis->CritSect);
     1733    RTListNodeRemove(&pStreamDS->ListEntry);
     1734    RTCritSectLeave(&pThis->CritSect);
    17141735
    17151736    if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
     
    20692090
    20702091/**
     2092 * This is used by the timer function as well as when arming the timer.
     2093 *
     2094 * @param   pThis       The DSound host audio driver instance data.
     2095 * @param   msNow       A current RTTimeMilliTS() value.
     2096 */
     2097static void drvHostDSoundDrainTimerWorker(PDRVHOSTDSOUND pThis, uint64_t msNow)
     2098{
     2099    /*
     2100     * Go thru the stream list and look at draining streams.
     2101     */
     2102    uint64_t        msNext = UINT64_MAX;
     2103    RTCritSectEnter(&pThis->CritSect);
     2104    PDSOUNDSTREAM   pCur;
     2105    RTListForEach(&pThis->HeadStreams, pCur, DSOUNDSTREAM, ListEntry)
     2106    {
     2107        if (   pCur->Cfg.enmDir == PDMAUDIODIR_OUT
     2108            && pCur->Out.fDrain)
     2109        {
     2110            uint64_t const msCurDeadline = pCur->Out.msDrainDeadline;
     2111            if (msCurDeadline > 0 && msCurDeadline < msNext)
     2112            {
     2113                if (msCurDeadline > msNow)
     2114                    msNext = pCur->Out.msDrainDeadline;
     2115                else
     2116                {
     2117                    LogRel2(("DSound: Stopping draining of '%s' {%s} ...\n",
     2118                             pCur->Cfg.szName, drvHostDSoundStreamStatusString(pCur)));
     2119                    if (pCur->Out.pDSB)
     2120                    {
     2121                        HRESULT hrc = IDirectSoundBuffer8_Stop(pCur->Out.pDSB);
     2122                        if (FAILED(hrc))
     2123                            LogRelMax(64, ("DSound: Failed to stop draining stream '%s': %Rhrc\n", pCur->Cfg.szName, hrc));
     2124                    }
     2125                    pCur->Out.fDrain = false;
     2126                }
     2127            }
     2128        }
     2129    }
     2130
     2131    /*
     2132     * Re-arm the timer if necessary.
     2133     */
     2134    if (msNext != UINT64_MAX)
     2135        PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hDrainTimer, msNext - msNow);
     2136    RTCritSectLeave(&pThis->CritSect);
     2137}
     2138
     2139
     2140/**
     2141 * @callback_method_impl{FNTMTIMERDRV,
     2142 * This is a hack to ensure that draining streams stop playing.}
     2143 */
     2144static DECLCALLBACK(void) drvHostDSoundDrainStopHackTimer(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)
     2145{
     2146    PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
     2147    RT_NOREF(hTimer, pvUser);
     2148    drvHostDSoundDrainTimerWorker(pThis, RTTimeMilliTS());
     2149}
     2150
     2151
     2152/**
    20712153 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
    20722154 */
     
    20922174            hrc = IDirectSoundBuffer8_Play(pStreamDS->Out.pDSB, 0, 0, 0);
    20932175            if (SUCCEEDED(hrc))
    2094                 pStreamDS->Out.fDrain = true;
     2176            {
     2177                uint64_t const msNow = RTTimeMilliTS();
     2178                pStreamDS->Out.msDrainDeadline = PDMAudioPropsBytesToMilli(&pStreamDS->Cfg.Props,  pStreamDS->cbBufSize) + msNow;
     2179                pStreamDS->Out.fDrain          = true;
     2180                drvHostDSoundDrainTimerWorker(pThis, msNow);
     2181            }
    20952182            else
    20962183                LogRelMax(64, ("DSound: Failed to restart '%s' in drain mode: %Rhrc\n", pStreamDS->Cfg.szName, hrc));
     
    21152202    return rc;
    21162203}
     2204
    21172205
    21182206/**
     
    26652753     * Init basic data members and interfaces.
    26662754     */
     2755    RTListInit(&pThis->HeadStreams);
    26672756    pThis->pDrvIns                   = pDrvIns;
     2757    pThis->hDrainTimer               = NIL_TMTIMERHANDLE;
    26682758    /* IBase */
    26692759    pDrvIns->IBase.pfnQueryInterface = drvHostDSoundQueryInterface;
     
    27542844        LogRel2(("DSound: Notification client is disabled (ver %#RX64)\n", RTSystemGetNtVersion()));
    27552845#endif
     2846
     2847    /*
     2848     * Create a timer that helps making sure draining streams actually stop playing.
     2849     * (Seem dsound doesn't stop by itself in many cases.)
     2850     */
     2851    int rc = PDMDrvHlpTMTimerCreate(pDrvIns, TMCLOCK_REAL, drvHostDSoundDrainStopHackTimer, NULL /*pvUser*/, 0 /*fFlags*/,
     2852                                    "DSound drain", &pThis->hDrainTimer);
     2853    AssertRCReturn(rc, rc);
    27562854
    27572855    /*
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