VirtualBox

Changeset 88706 in vbox


Ignore:
Timestamp:
Apr 26, 2021 3:59:11 PM (4 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
144023
Message:

DrvHostAudioWasApi: Introduced a worker thread that processes the config hinting asynchronously at startup. bugref:9890

File:
1 edited

Legend:

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

    r88693 r88706  
    3838#include <VBox/vmm/pdmaudiohostenuminline.h>
    3939
     40#include <iprt/rand.h>
    4041#include <iprt/utf16.h>
    4142#include <iprt/uuid.h>
     
    5152/** Max GetCurrentPadding value we accept (to make sure it's safe to convert to bytes). */
    5253#define VBOX_WASAPI_MAX_PADDING         UINT32_C(0x007fffff)
     54
     55/** @name WM_DRVHOSTAUDIOWAS_XXX - Worker thread messages.
     56 * @{ */
     57/** Adds entry to the cache.
     58 * lParam points to a PDMAUDIOSTREAMCFG structure with the details. RTMemFree
     59 * when done. */
     60#define WM_DRVHOSTAUDIOWAS_HINT         (WM_APP + 2)
     61#define WM_DRVHOSTAUDIOWAS_PURGE_CACHE  (WM_APP + 1)
     62/** @} */
    5363
    5464
     
    227237    RTCRITSECT                      CritSectCache;
    228238
     239    /** The worker thread. */
     240    RTTHREAD                        hWorkerThread;
     241    /** The TID of the worker thread (for posting messages to it). */
     242    DWORD                           idWorkerThread;
     243    /** The fixed wParam value for the worker thread. */
     244    WPARAM                          uWorkerThreadFixedParam;
     245
    229246} DRVHOSTAUDIOWAS;
    230247/** Pointer to the data for a WASAPI host audio driver instance. */
     
    275292
    276293/*********************************************************************************************************************************
    277 *   Pre-activated audio device client cache.                                                                                     *
     294*   IMMNotificationClient implementation
     295*********************************************************************************************************************************/
     296/**
     297 * Multimedia notification client.
     298 *
     299 * We want to know when the default device changes so we can switch running
     300 * streams to use the new one and so we can pre-activate it in preparation
     301 * for new streams.
     302 */
     303class DrvHostAudioWasMmNotifyClient : public IMMNotificationClient
     304{
     305private:
     306    /** Reference counter. */
     307    uint32_t volatile           m_cRefs;
     308    /** The WASAPI host audio driver instance data.
     309     * @note    This can be NULL.  Only access after entering critical section. */
     310    PDRVHOSTAUDIOWAS            m_pDrvWas;
     311    /** Critical section serializing access to m_pDrvWas.  */
     312    RTCRITSECT                  m_CritSect;
     313
     314public:
     315    /**
     316     * @throws int on critical section init failure.
     317     */
     318    DrvHostAudioWasMmNotifyClient(PDRVHOSTAUDIOWAS a_pDrvWas)
     319        : m_cRefs(1)
     320        , m_pDrvWas(a_pDrvWas)
     321    {
     322        int rc = RTCritSectInit(&m_CritSect);
     323        AssertRCStmt(rc, throw(rc));
     324    }
     325
     326    virtual ~DrvHostAudioWasMmNotifyClient() RT_NOEXCEPT
     327    {
     328        RTCritSectDelete(&m_CritSect);
     329    }
     330
     331    /**
     332     * Called by drvHostAudioWasDestruct to set m_pDrvWas to NULL.
     333     */
     334    void notifyDriverDestroyed() RT_NOEXCEPT
     335    {
     336        RTCritSectEnter(&m_CritSect);
     337        m_pDrvWas = NULL;
     338        RTCritSectLeave(&m_CritSect);
     339    }
     340
     341    /**
     342     * Enters the notification critsect for getting at the IMMDevice members in
     343     * PDMHOSTAUDIOWAS.
     344     */
     345    void lockEnter() RT_NOEXCEPT
     346    {
     347        RTCritSectEnter(&m_CritSect);
     348    }
     349
     350    /**
     351     * Leaves the notification critsect.
     352     */
     353    void lockLeave() RT_NOEXCEPT
     354    {
     355        RTCritSectLeave(&m_CritSect);
     356    }
     357
     358    /** @name IUnknown interface
     359     * @{ */
     360    IFACEMETHODIMP_(ULONG)  AddRef()
     361    {
     362        uint32_t cRefs = ASMAtomicIncU32(&m_cRefs);
     363        AssertMsg(cRefs < 64, ("%#x\n", cRefs));
     364        Log6Func(("returns %u\n", cRefs));
     365        return cRefs;
     366    }
     367
     368    IFACEMETHODIMP_(ULONG)  Release()
     369    {
     370        uint32_t cRefs = ASMAtomicDecU32(&m_cRefs);
     371        AssertMsg(cRefs < 64, ("%#x\n", cRefs));
     372        if (cRefs == 0)
     373            delete this;
     374        Log6Func(("returns %u\n", cRefs));
     375        return cRefs;
     376    }
     377
     378    IFACEMETHODIMP          QueryInterface(const IID &rIID, void **ppvInterface)
     379    {
     380        if (IsEqualIID(rIID, IID_IUnknown))
     381            *ppvInterface = static_cast<IUnknown *>(this);
     382        else if (IsEqualIID(rIID, __uuidof(IMMNotificationClient)))
     383            *ppvInterface = static_cast<IMMNotificationClient *>(this);
     384        else
     385        {
     386            LogFunc(("Unknown rIID={%RTuuid}\n", &rIID));
     387            *ppvInterface = NULL;
     388            return E_NOINTERFACE;
     389        }
     390        Log6Func(("returns S_OK + %p\n", *ppvInterface));
     391        return S_OK;
     392    }
     393    /** @} */
     394
     395    /** @name IMMNotificationClient interface
     396     * @{ */
     397    IFACEMETHODIMP OnDeviceStateChanged(LPCWSTR pwszDeviceId, DWORD dwNewState)
     398    {
     399        RT_NOREF(pwszDeviceId, dwNewState);
     400        Log7Func(("pwszDeviceId=%ls dwNewState=%u (%#x)\n", pwszDeviceId, dwNewState, dwNewState));
     401        return S_OK;
     402    }
     403
     404    IFACEMETHODIMP OnDeviceAdded(LPCWSTR pwszDeviceId)
     405    {
     406        RT_NOREF(pwszDeviceId);
     407        Log7Func(("pwszDeviceId=%ls\n", pwszDeviceId));
     408
     409        /*
     410         * Is this a device we're interested in?  Grab the enumerator if it is.
     411         */
     412        bool                 fOutput      = false;
     413        IMMDeviceEnumerator *pIEnumerator = NULL;
     414        RTCritSectEnter(&m_CritSect);
     415        if (   m_pDrvWas != NULL
     416            && (   (fOutput = RTUtf16ICmp(m_pDrvWas->pwszOutputDevId, pwszDeviceId) == 0)
     417                || RTUtf16ICmp(m_pDrvWas->pwszInputDevId, pwszDeviceId) == 0))
     418        {
     419            pIEnumerator = m_pDrvWas->pIEnumerator;
     420            if (pIEnumerator /* paranoia */)
     421                pIEnumerator->AddRef();
     422        }
     423        RTCritSectLeave(&m_CritSect);
     424        if (pIEnumerator)
     425        {
     426            /*
     427             * Get the device and update it.
     428             */
     429            IMMDevice *pIDevice = NULL;
     430            HRESULT hrc = pIEnumerator->GetDevice(pwszDeviceId, &pIDevice);
     431            if (SUCCEEDED(hrc))
     432                setDevice(fOutput, pIDevice, pwszDeviceId, __PRETTY_FUNCTION__);
     433            else
     434                LogRelMax(64, ("WasAPI: Failed to get %s device '%ls' (OnDeviceAdded): %Rhrc\n",
     435                               fOutput ? "output" : "input", pwszDeviceId, hrc));
     436            pIEnumerator->Release();
     437        }
     438        return S_OK;
     439    }
     440
     441    IFACEMETHODIMP OnDeviceRemoved(LPCWSTR pwszDeviceId)
     442    {
     443        RT_NOREF(pwszDeviceId);
     444        Log7Func(("pwszDeviceId=%ls\n", pwszDeviceId));
     445
     446        /*
     447         * Is this a device we're interested in?  Then set it to NULL.
     448         */
     449        bool fOutput = false;
     450        RTCritSectEnter(&m_CritSect);
     451        if (   m_pDrvWas != NULL
     452            && (   (fOutput = RTUtf16ICmp(m_pDrvWas->pwszOutputDevId, pwszDeviceId) == 0)
     453                || RTUtf16ICmp(m_pDrvWas->pwszInputDevId, pwszDeviceId) == 0))
     454            setDevice(fOutput, NULL, pwszDeviceId, __PRETTY_FUNCTION__);
     455        RTCritSectLeave(&m_CritSect);
     456        return S_OK;
     457    }
     458
     459    IFACEMETHODIMP OnDefaultDeviceChanged(EDataFlow enmFlow, ERole enmRole, LPCWSTR pwszDefaultDeviceId)
     460    {
     461        /*
     462         * Are we interested in this device?  If so grab the enumerator.
     463         */
     464        IMMDeviceEnumerator *pIEnumerator = NULL;
     465        RTCritSectEnter(&m_CritSect);
     466        if (    m_pDrvWas != NULL
     467            && (   (enmFlow == eRender  && enmRole == eMultimedia && !m_pDrvWas->pwszOutputDevId)
     468                || (enmFlow == eCapture && enmRole == eMultimedia && !m_pDrvWas->pwszInputDevId)))
     469        {
     470            pIEnumerator = m_pDrvWas->pIEnumerator;
     471            if (pIEnumerator /* paranoia */)
     472                pIEnumerator->AddRef();
     473        }
     474        RTCritSectLeave(&m_CritSect);
     475        if (pIEnumerator)
     476        {
     477            /*
     478             * Get the device and update it.
     479             */
     480            IMMDevice *pIDevice = NULL;
     481            HRESULT hrc = pIEnumerator->GetDefaultAudioEndpoint(enmFlow, enmRole, &pIDevice);
     482            if (SUCCEEDED(hrc))
     483                setDevice(enmFlow == eRender, pIDevice, pwszDefaultDeviceId, __PRETTY_FUNCTION__);
     484            else
     485                LogRelMax(64, ("WasAPI: Failed to get default %s device (OnDefaultDeviceChange): %Rhrc\n",
     486                               enmFlow == eRender ? "output" : "input", hrc));
     487            pIEnumerator->Release();
     488        }
     489
     490        RT_NOREF(enmFlow, enmRole, pwszDefaultDeviceId);
     491        Log7Func(("enmFlow=%d enmRole=%d pwszDefaultDeviceId=%ls\n", enmFlow, enmRole, pwszDefaultDeviceId));
     492        return S_OK;
     493    }
     494
     495    IFACEMETHODIMP OnPropertyValueChanged(LPCWSTR pwszDeviceId, const PROPERTYKEY Key)
     496    {
     497        RT_NOREF(pwszDeviceId, Key);
     498        Log7Func(("pwszDeviceId=%ls Key={%RTuuid, %u (%#x)}\n", pwszDeviceId, &Key.fmtid, Key.pid, Key.pid));
     499        return S_OK;
     500    }
     501    /** @} */
     502
     503private:
     504    /**
     505     * Sets DRVHOSTAUDIOWAS::pIDeviceOutput or DRVHOSTAUDIOWAS::pIDeviceInput to @a pIDevice.
     506     */
     507    void setDevice(bool fOutput, IMMDevice *pIDevice, LPCWSTR pwszDeviceId, const char *pszCaller)
     508    {
     509        RT_NOREF(pszCaller, pwszDeviceId);
     510
     511        RTCritSectEnter(&m_CritSect);
     512        if (m_pDrvWas)
     513        {
     514            if (fOutput)
     515            {
     516                Log7((LOG_FN_FMT ": Changing output device from %p to %p (%ls)\n",
     517                      pszCaller, m_pDrvWas->pIDeviceOutput, pIDevice, pwszDeviceId));
     518                if (m_pDrvWas->pIDeviceOutput)
     519                    m_pDrvWas->pIDeviceOutput->Release();
     520                m_pDrvWas->pIDeviceOutput = pIDevice;
     521            }
     522            else
     523            {
     524                Log7((LOG_FN_FMT ": Changing input device from %p to %p (%ls)\n",
     525                      pszCaller, m_pDrvWas->pIDeviceInput, pIDevice, pwszDeviceId));
     526                if (m_pDrvWas->pIDeviceInput)
     527                    m_pDrvWas->pIDeviceInput->Release();
     528                m_pDrvWas->pIDeviceInput = pIDevice;
     529            }
     530
     531            /** @todo Invalid/update in-use streams. */
     532        }
     533        else if (pIDevice)
     534            pIDevice->Release();
     535        RTCritSectLeave(&m_CritSect);
     536    }
     537};
     538
     539
     540/*********************************************************************************************************************************
     541*   Pre-configured audio client cache.                                                                                           *
    278542*********************************************************************************************************************************/
    279543#define WAS_CACHE_MAX_ENTRIES_SAME_DEVICE   2
     
    7691033
    7701034
     1035static void drvHostWasCacheConfigHinting(PDRVHOSTAUDIOWAS pThis, PPDMAUDIOSTREAMCFG pCfgReq)
     1036{
     1037    /*
     1038     * Get the device.
     1039     */
     1040    pThis->pNotifyClient->lockEnter();
     1041    IMMDevice *pIDevice = pCfgReq->enmDir == PDMAUDIODIR_IN ? pThis->pIDeviceInput : pThis->pIDeviceOutput;
     1042    if (pIDevice)
     1043        pIDevice->AddRef();
     1044    pThis->pNotifyClient->lockLeave();
     1045    if (pIDevice)
     1046    {
     1047        /*
     1048         * Look up the config and put it back.
     1049         */
     1050        PDRVHOSTAUDIOWASCACHEDEVCFG pDevCfg = drvHostAudioWasCacheLookupOrCreate(pThis, pIDevice, pCfgReq);
     1051        LogFlowFunc(("pDevCfg=%p\n"));
     1052        if (pDevCfg)
     1053            drvHostAudioWasCachePutBack(pThis, pDevCfg);
     1054        pIDevice->Release();
     1055    }
     1056}
     1057
     1058
    7711059/**
    7721060 * Prefills the cache.
     
    8221110
    8231111/*********************************************************************************************************************************
    824 *   IMMNotificationClient implementation
     1112*   Worker thread                                                                                                                *
    8251113*********************************************************************************************************************************/
    826 /**
    827  * Multimedia notification client.
    828  *
    829  * We want to know when the default device changes so we can switch running
    830  * streams to use the new one and so we can pre-activate it in preparation
    831  * for new streams.
    832  */
    833 class DrvHostAudioWasMmNotifyClient : public IMMNotificationClient
    834 {
    835 private:
    836     /** Reference counter. */
    837     uint32_t volatile           m_cRefs;
    838     /** The WASAPI host audio driver instance data.
    839      * @note    This can be NULL.  Only access after entering critical section. */
    840     PDRVHOSTAUDIOWAS            m_pDrvWas;
    841     /** Critical section serializing access to m_pDrvWas.  */
    842     RTCRITSECT                  m_CritSect;
    843 
    844 public:
    845     /**
    846      * @throws int on critical section init failure.
    847      */
    848     DrvHostAudioWasMmNotifyClient(PDRVHOSTAUDIOWAS a_pDrvWas)
    849         : m_cRefs(1)
    850         , m_pDrvWas(a_pDrvWas)
    851     {
    852         int rc = RTCritSectInit(&m_CritSect);
    853         AssertRCStmt(rc, throw(rc));
    854     }
    855 
    856     virtual ~DrvHostAudioWasMmNotifyClient() RT_NOEXCEPT
    857     {
    858         RTCritSectDelete(&m_CritSect);
    859     }
    860 
    861     /**
    862      * Called by drvHostAudioWasDestruct to set m_pDrvWas to NULL.
    863      */
    864     void notifyDriverDestroyed() RT_NOEXCEPT
    865     {
    866         RTCritSectEnter(&m_CritSect);
    867         m_pDrvWas = NULL;
    868         RTCritSectLeave(&m_CritSect);
    869     }
    870 
    871     /**
    872      * Enters the notification critsect for getting at the IMMDevice members in
    873      * PDMHOSTAUDIOWAS.
    874      */
    875     void lockEnter() RT_NOEXCEPT
    876     {
    877         RTCritSectEnter(&m_CritSect);
    878     }
    879 
    880     /**
    881      * Leaves the notification critsect.
    882      */
    883     void lockLeave() RT_NOEXCEPT
    884     {
    885         RTCritSectLeave(&m_CritSect);
    886     }
    887 
    888     /** @name IUnknown interface
    889      * @{ */
    890     IFACEMETHODIMP_(ULONG)  AddRef()
    891     {
    892         uint32_t cRefs = ASMAtomicIncU32(&m_cRefs);
    893         AssertMsg(cRefs < 64, ("%#x\n", cRefs));
    894         Log6Func(("returns %u\n", cRefs));
    895         return cRefs;
    896     }
    897 
    898     IFACEMETHODIMP_(ULONG)  Release()
    899     {
    900         uint32_t cRefs = ASMAtomicDecU32(&m_cRefs);
    901         AssertMsg(cRefs < 64, ("%#x\n", cRefs));
    902         if (cRefs == 0)
    903             delete this;
    904         Log6Func(("returns %u\n", cRefs));
    905         return cRefs;
    906     }
    907 
    908     IFACEMETHODIMP          QueryInterface(const IID &rIID, void **ppvInterface)
    909     {
    910         if (IsEqualIID(rIID, IID_IUnknown))
    911             *ppvInterface = static_cast<IUnknown *>(this);
    912         else if (IsEqualIID(rIID, __uuidof(IMMNotificationClient)))
    913             *ppvInterface = static_cast<IMMNotificationClient *>(this);
     1114
     1115/**
     1116 * @callback_method_impl{FNRTTHREAD,
     1117 * Asynchronous thread for setting up audio client configs.}
     1118 */
     1119static DECLCALLBACK(int) drvHostWasWorkerThread(RTTHREAD hThreadSelf, void *pvUser)
     1120{
     1121    PDRVHOSTAUDIOWAS pThis = (PDRVHOSTAUDIOWAS)pvUser;
     1122
     1123    /*
     1124     * We need to set the thread ID so others can post us thread messages.
     1125     * And before we signal that we're ready, make sure we've got a message queue.
     1126     */
     1127    pThis->idWorkerThread = GetCurrentThreadId();
     1128    LogFunc(("idWorkerThread=%#x (%u)\n", pThis->idWorkerThread, pThis->idWorkerThread));
     1129
     1130    MSG Msg;
     1131    PeekMessageW(&Msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
     1132
     1133    int rc = RTThreadUserSignal(hThreadSelf);
     1134    AssertRC(rc);
     1135
     1136    /*
     1137     * Message loop.
     1138     */
     1139    BOOL fRet;
     1140    while ((fRet = GetMessageW(&Msg, NULL, 0, 0)) != FALSE)
     1141    {
     1142        if (fRet != -1)
     1143        {
     1144            TranslateMessage(&Msg);
     1145            Log9Func(("Msg: time=%u: msg=%#x l=%p w=%p for hwnd=%p\n", Msg.time, Msg.message, Msg.lParam, Msg.wParam, Msg.hwnd));
     1146            switch (Msg.message)
     1147            {
     1148                case WM_DRVHOSTAUDIOWAS_HINT:
     1149                {
     1150                    AssertMsgBreak(Msg.wParam == pThis->uWorkerThreadFixedParam, ("%p\n", Msg.wParam));
     1151                    AssertBreak(Msg.hwnd == NULL);
     1152                    PPDMAUDIOSTREAMCFG pCfgReq = (PPDMAUDIOSTREAMCFG)Msg.lParam;
     1153                    AssertPtrBreak(pCfgReq);
     1154
     1155                    drvHostWasCacheConfigHinting(pThis, pCfgReq);
     1156                    RTMemFree(pCfgReq);
     1157                    break;
     1158                }
     1159
     1160                case WM_DRVHOSTAUDIOWAS_PURGE_CACHE:
     1161                {
     1162                    AssertMsgBreak(Msg.wParam == pThis->uWorkerThreadFixedParam, ("%p\n", Msg.wParam));
     1163                    AssertBreak(Msg.hwnd == NULL);
     1164                    AssertBreak(Msg.lParam == 0);
     1165
     1166                    drvHostAudioWasCachePurge(pThis);
     1167                    break;
     1168                }
     1169
     1170                default:
     1171                    break;
     1172            }
     1173            DispatchMessageW(&Msg);
     1174        }
    9141175        else
    915         {
    916             LogFunc(("Unknown rIID={%RTuuid}\n", &rIID));
    917             *ppvInterface = NULL;
    918             return E_NOINTERFACE;
    919         }
    920         Log6Func(("returns S_OK + %p\n", *ppvInterface));
    921         return S_OK;
    922     }
    923     /** @} */
    924 
    925     /** @name IMMNotificationClient interface
    926      * @{ */
    927     IFACEMETHODIMP OnDeviceStateChanged(LPCWSTR pwszDeviceId, DWORD dwNewState)
    928     {
    929         RT_NOREF(pwszDeviceId, dwNewState);
    930         Log7Func(("pwszDeviceId=%ls dwNewState=%u (%#x)\n", pwszDeviceId, dwNewState, dwNewState));
    931         return S_OK;
    932     }
    933 
    934     IFACEMETHODIMP OnDeviceAdded(LPCWSTR pwszDeviceId)
    935     {
    936         RT_NOREF(pwszDeviceId);
    937         Log7Func(("pwszDeviceId=%ls\n", pwszDeviceId));
    938 
    939         /*
    940          * Is this a device we're interested in?  Grab the enumerator if it is.
    941          */
    942         bool                 fOutput      = false;
    943         IMMDeviceEnumerator *pIEnumerator = NULL;
    944         RTCritSectEnter(&m_CritSect);
    945         if (   m_pDrvWas != NULL
    946             && (   (fOutput = RTUtf16ICmp(m_pDrvWas->pwszOutputDevId, pwszDeviceId) == 0)
    947                 || RTUtf16ICmp(m_pDrvWas->pwszInputDevId, pwszDeviceId) == 0))
    948         {
    949             pIEnumerator = m_pDrvWas->pIEnumerator;
    950             if (pIEnumerator /* paranoia */)
    951                 pIEnumerator->AddRef();
    952         }
    953         RTCritSectLeave(&m_CritSect);
    954         if (pIEnumerator)
    955         {
    956             /*
    957              * Get the device and update it.
    958              */
    959             IMMDevice *pIDevice = NULL;
    960             HRESULT hrc = pIEnumerator->GetDevice(pwszDeviceId, &pIDevice);
    961             if (SUCCEEDED(hrc))
    962                 setDevice(fOutput, pIDevice, pwszDeviceId, __PRETTY_FUNCTION__);
    963             else
    964                 LogRelMax(64, ("WasAPI: Failed to get %s device '%ls' (OnDeviceAdded): %Rhrc\n",
    965                                fOutput ? "output" : "input", pwszDeviceId, hrc));
    966             pIEnumerator->Release();
    967         }
    968         return S_OK;
    969     }
    970 
    971     IFACEMETHODIMP OnDeviceRemoved(LPCWSTR pwszDeviceId)
    972     {
    973         RT_NOREF(pwszDeviceId);
    974         Log7Func(("pwszDeviceId=%ls\n", pwszDeviceId));
    975 
    976         /*
    977          * Is this a device we're interested in?  Then set it to NULL.
    978          */
    979         bool fOutput = false;
    980         RTCritSectEnter(&m_CritSect);
    981         if (   m_pDrvWas != NULL
    982             && (   (fOutput = RTUtf16ICmp(m_pDrvWas->pwszOutputDevId, pwszDeviceId) == 0)
    983                 || RTUtf16ICmp(m_pDrvWas->pwszInputDevId, pwszDeviceId) == 0))
    984             setDevice(fOutput, NULL, pwszDeviceId, __PRETTY_FUNCTION__);
    985         RTCritSectLeave(&m_CritSect);
    986         return S_OK;
    987     }
    988 
    989     IFACEMETHODIMP OnDefaultDeviceChanged(EDataFlow enmFlow, ERole enmRole, LPCWSTR pwszDefaultDeviceId)
    990     {
    991         /*
    992          * Are we interested in this device?  If so grab the enumerator.
    993          */
    994         IMMDeviceEnumerator *pIEnumerator = NULL;
    995         RTCritSectEnter(&m_CritSect);
    996         if (    m_pDrvWas != NULL
    997             && (   (enmFlow == eRender  && enmRole == eMultimedia && !m_pDrvWas->pwszOutputDevId)
    998                 || (enmFlow == eCapture && enmRole == eMultimedia && !m_pDrvWas->pwszInputDevId)))
    999         {
    1000             pIEnumerator = m_pDrvWas->pIEnumerator;
    1001             if (pIEnumerator /* paranoia */)
    1002                 pIEnumerator->AddRef();
    1003         }
    1004         RTCritSectLeave(&m_CritSect);
    1005         if (pIEnumerator)
    1006         {
    1007             /*
    1008              * Get the device and update it.
    1009              */
    1010             IMMDevice *pIDevice = NULL;
    1011             HRESULT hrc = pIEnumerator->GetDefaultAudioEndpoint(enmFlow, enmRole, &pIDevice);
    1012             if (SUCCEEDED(hrc))
    1013                 setDevice(enmFlow == eRender, pIDevice, pwszDefaultDeviceId, __PRETTY_FUNCTION__);
    1014             else
    1015                 LogRelMax(64, ("WasAPI: Failed to get default %s device (OnDefaultDeviceChange): %Rhrc\n",
    1016                                enmFlow == eRender ? "output" : "input", hrc));
    1017             pIEnumerator->Release();
    1018         }
    1019 
    1020         RT_NOREF(enmFlow, enmRole, pwszDefaultDeviceId);
    1021         Log7Func(("enmFlow=%d enmRole=%d pwszDefaultDeviceId=%ls\n", enmFlow, enmRole, pwszDefaultDeviceId));
    1022         return S_OK;
    1023     }
    1024 
    1025     IFACEMETHODIMP OnPropertyValueChanged(LPCWSTR pwszDeviceId, const PROPERTYKEY Key)
    1026     {
    1027         RT_NOREF(pwszDeviceId, Key);
    1028         Log7Func(("pwszDeviceId=%ls Key={%RTuuid, %u (%#x)}\n", pwszDeviceId, &Key.fmtid, Key.pid, Key.pid));
    1029         return S_OK;
    1030     }
    1031     /** @} */
    1032 
    1033 private:
    1034     /**
    1035      * Sets DRVHOSTAUDIOWAS::pIDeviceOutput or DRVHOSTAUDIOWAS::pIDeviceInput to @a pIDevice.
    1036      */
    1037     void setDevice(bool fOutput, IMMDevice *pIDevice, LPCWSTR pwszDeviceId, const char *pszCaller)
    1038     {
    1039         RT_NOREF(pszCaller, pwszDeviceId);
    1040 
    1041         RTCritSectEnter(&m_CritSect);
    1042         if (m_pDrvWas)
    1043         {
    1044             if (fOutput)
    1045             {
    1046                 Log7((LOG_FN_FMT ": Changing output device from %p to %p (%ls)\n",
    1047                       pszCaller, m_pDrvWas->pIDeviceOutput, pIDevice, pwszDeviceId));
    1048                 if (m_pDrvWas->pIDeviceOutput)
    1049                     m_pDrvWas->pIDeviceOutput->Release();
    1050                 m_pDrvWas->pIDeviceOutput = pIDevice;
    1051             }
    1052             else
    1053             {
    1054                 Log7((LOG_FN_FMT ": Changing input device from %p to %p (%ls)\n",
    1055                       pszCaller, m_pDrvWas->pIDeviceInput, pIDevice, pwszDeviceId));
    1056                 if (m_pDrvWas->pIDeviceInput)
    1057                     m_pDrvWas->pIDeviceInput->Release();
    1058                 m_pDrvWas->pIDeviceInput = pIDevice;
    1059             }
    1060 
    1061             /** @todo Invalid/update in-use streams. */
    1062         }
    1063         else if (pIDevice)
    1064             pIDevice->Release();
    1065         RTCritSectLeave(&m_CritSect);
    1066     }
    1067 };
     1176            AssertMsgFailed(("GetLastError()=%u\n", GetLastError()));
     1177    }
     1178
     1179    LogFlowFunc(("Pre-quit cache purge...\n"));
     1180    drvHostAudioWasCachePurge(pThis);
     1181
     1182    LogFunc(("Quits\n"));
     1183    return VINF_SUCCESS;
     1184}
    10681185
    10691186
     
    12891406    LogFlowFunc(("pCfg=%p\n", pCfg));
    12901407
    1291     /*
    1292      * Get the device.
    1293      */
    1294     pThis->pNotifyClient->lockEnter();
    1295     IMMDevice *pIDevice = pCfg->enmDir == PDMAUDIODIR_IN ? pThis->pIDeviceInput : pThis->pIDeviceOutput;
    1296     if (pIDevice)
    1297         pIDevice->AddRef();
    1298     pThis->pNotifyClient->lockLeave();
    1299     if (pIDevice)
    1300     {
    1301         /*
    1302          * Look up the config and put it back.
    1303          */
    1304         PDRVHOSTAUDIOWASCACHEDEVCFG pDevCfg = drvHostAudioWasCacheLookupOrCreate(pThis, pIDevice, pCfg);
    1305         LogFlowFunc(("pDevCfg=%p\n"));
    1306         if (pDevCfg)
    1307             drvHostAudioWasCachePutBack(pThis, pDevCfg);
    1308         pIDevice->Release();
    1309     }
     1408    if (pThis->hWorkerThread != NIL_RTTHREAD)
     1409    {
     1410        PPDMAUDIOSTREAMCFG pCfgCopy = PDMAudioStrmCfgDup(pCfg);
     1411        if (pCfgCopy)
     1412        {
     1413            if (PostThreadMessageW(pThis->idWorkerThread, WM_DRVHOSTAUDIOWAS_HINT,
     1414                                   pThis->uWorkerThreadFixedParam, (LPARAM)pCfgCopy))
     1415                LogFlowFunc(("Posted %p to worker thread\n", pCfgCopy));
     1416            else
     1417            {
     1418                LogRelMax(64, ("WasAPI: PostThreadMessageW failed: %u\n", GetLastError()));
     1419                PDMAudioStrmCfgFree(pCfgCopy);
     1420            }
     1421        }
     1422    }
     1423    else
     1424        drvHostWasCacheConfigHinting(pThis, pCfg);
    13101425}
    13111426
     
    22972412 * @callback_method_impl{FNPDMDRVDESTRUCT, pfnDestruct}
    22982413 */
     2414static DECLCALLBACK(void) drvHostAudioWasPowerOff(PPDMDRVINS pDrvIns)
     2415{
     2416    PDRVHOSTAUDIOWAS pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTAUDIOWAS);
     2417
     2418    if (pThis->hWorkerThread != NIL_RTTHREAD)
     2419    {
     2420        BOOL fRc = PostThreadMessageW(pThis->idWorkerThread, WM_DRVHOSTAUDIOWAS_PURGE_CACHE, pThis->uWorkerThreadFixedParam, 0);
     2421        LogFlowFunc(("Posted WM_DRVHOSTAUDIOWAS_PURGE_CACHE: %d\n", fRc));
     2422        Assert(fRc); RT_NOREF(fRc);
     2423    }
     2424}
     2425
     2426
     2427/**
     2428 * @callback_method_impl{FNPDMDRVDESTRUCT, pfnDestruct}
     2429 */
    22992430static DECLCALLBACK(void) drvHostAudioWasDestruct(PPDMDRVINS pDrvIns)
    23002431{
     
    23102441    }
    23112442
     2443    if (pThis->hWorkerThread != NIL_RTTHREAD)
     2444    {
     2445        BOOL fRc = PostThreadMessageW(pThis->idWorkerThread, WM_QUIT, 0, 0);
     2446        Assert(fRc); RT_NOREF(fRc);
     2447
     2448        int rc = RTThreadWait(pThis->hWorkerThread, RT_MS_15SEC, NULL);
     2449        AssertRC(rc);
     2450    }
     2451
    23122452    if (RTCritSectIsInitialized(&pThis->CritSectCache))
    23132453    {
     
    23332473        pThis->pIDeviceInput = NULL;
    23342474    }
     2475
    23352476
    23362477    if (RTCritSectRwIsInitialized(&pThis->CritSectStreamList))
     
    23552496    pThis->pDrvIns                          = pDrvIns;
    23562497    pThis->hDrainTimer                      = NIL_TMTIMERHANDLE;
     2498    pThis->hWorkerThread                    = NIL_RTTHREAD;
     2499    pThis->idWorkerThread                   = 0;
    23572500    RTListInit(&pThis->StreamHead);
    23582501    RTListInit(&pThis->CacheHead);
     
    23842527    /** @todo make it possible to override the default device selection. */
    23852528
     2529    AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
     2530                    ("Configuration error: Not possible to attach anything to this driver!\n"),
     2531                    VERR_PDM_DRVINS_NO_ATTACH);
     2532
    23862533    /*
    23872534     * Initialize the critical sections early.
     
    24942641                                "WasAPI drain", &pThis->hDrainTimer);
    24952642    AssertRCReturn(rc, rc);
     2643
     2644    /*
     2645     * Create the worker thread.  This thread has a message loop and will be
     2646     * signalled by DrvHostAudioWasMmNotifyClient while the VM is paused/whatever,
     2647     * so better make it a regular thread rather than PDM thread.
     2648     */
     2649    pThis->uWorkerThreadFixedParam = (WPARAM)RTRandU64();
     2650    rc = RTThreadCreateF(&pThis->hWorkerThread, drvHostWasWorkerThread, pThis, 0 /*cbStack*/, RTTHREADTYPE_DEFAULT,
     2651                         RTTHREADFLAGS_WAITABLE | RTTHREADFLAGS_COM_MTA, "WasWork%u", pDrvIns->iInstance);
     2652    AssertRCReturn(rc, rc);
     2653
     2654    rc = RTThreadUserWait(pThis->hWorkerThread, RT_MS_10SEC);
     2655    AssertRC(rc);
    24962656
    24972657    /*
     
    25482708    NULL,
    25492709    /* pfnPowerOff */
    2550     NULL,
     2710    drvHostAudioWasPowerOff,
    25512711    /* pfnSoftReset */
    25522712    NULL,
Note: See TracChangeset for help on using the changeset viewer.

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