VirtualBox

Changeset 88853 in vbox


Ignore:
Timestamp:
May 4, 2021 8:47:13 AM (4 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
144187
Message:

Audio: Reworking PDMIHOSTAUDIOPORT::pfnNotifyDevicesChanged a little, from now on it will only trigger re-enumeration (darwin still needs some adjusting). Use a timer to sligtly delay the enumeration and move it out of the re-init stream code, so that we don't redo it too frequently and that we do it even when there are active streams around. Made DrvHostAudioWasApi call pfnNotifyDevicesChanged. Reduced the DSound notification client code a little while adding pfnNotifyDeviceChanged calls to it. bugref:9890

Location:
trunk/src/VBox/Devices/Audio
Files:
5 edited

Legend:

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

    r88852 r88853  
    279279    /** Shutdown indicator. */
    280280    bool                    fTerminate;
    281 #ifdef VBOX_WITH_AUDIO_ENUM
    282     /** Flag indicating that we need to enumerate and log of the host
    283      * audio devices again. */
    284     bool                    fEnumerateDevices;
    285 #endif
    286281    /** Our audio connector interface. */
    287282    PDMIAUDIOCONNECTOR      IAudioConnector;
     
    330325    /** Unique name for the the disable-iteration timer.  */
    331326    char                    szTimerName[23];
     327
     328#ifdef VBOX_WITH_AUDIO_ENUM
     329    /** Handle to the timer for delayed re-enumeration of backend devices. */
     330    TMTIMERHANDLE           hEnumTimer;
     331    /** Unique name for the the disable-iteration timer.  */
     332    char                    szEnumTimerName[24];
     333#endif
    332334
    333335#ifdef VBOX_WITH_STATISTICS
     
    464466 * @remarks This is currently ONLY used for release logging.
    465467 */
    466 static int drvAudioDevicesEnumerateInternal(PDRVAUDIO pThis, bool fLog, PPDMAUDIOHOSTENUM pDevEnum)
     468static DECLCALLBACK(int) drvAudioDevicesEnumerateInternal(PDRVAUDIO pThis, bool fLog, PPDMAUDIOHOSTENUM pDevEnum)
    467469{
    468470    AssertReturn(!RTCritSectIsOwner(&pThis->CritSect), VERR_WRONG_ORDER);
     
    21152117    }
    21162118
    2117 #ifdef VBOX_WITH_AUDIO_ENUM
    2118 /** @todo do this elsewhere.  What if stuff changes when there are no open
    2119  *        streams to re-init? */
    2120     if (pThis->fEnumerateDevices)
    2121     {
    2122         /* Make sure to leave the driver's critical section before enumerating host stuff. */
    2123         int rc2 = RTCritSectLeave(&pThis->CritSect);
    2124         AssertRC(rc2);
    2125 
    2126         /* Re-enumerate all host devices. */
    2127         drvAudioDevicesEnumerateInternal(pThis, true /* fLog */, NULL /* pDevEnum */);
    2128 
    2129         /* Re-enter the critical section again. */
    2130         rc2 = RTCritSectEnter(&pThis->CritSect);
    2131         AssertRC(rc2);
    2132 
    2133         pThis->fEnumerateDevices = false;
    2134     }
    2135 #endif /* VBOX_WITH_AUDIO_ENUM */
    2136 
    21372119    RTCritSectLeave(&pThis->CritSect);
    21382120
     
    39963978
    39973979
     3980#ifdef VBOX_WITH_AUDIO_ENUM
     3981/**
     3982 * @callback_method_impl{FNTMTIMERDRV, Re-enumerate backend devices.}
     3983 *
     3984 * Used to do/trigger re-enumeration of backend devices with a delay after we
     3985 * got notification as there can be further notifications following shortly
     3986 * after the first one.  Also good to get it of random COM/whatever threads.
     3987 */
     3988static DECLCALLBACK(void) drvAudioEnumerateTimer(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)
     3989{
     3990    PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
     3991    RT_NOREF(hTimer, pvUser);
     3992
     3993    /* Try push the work over to the thread-pool if we've got one. */
     3994    if (pThis->hReqPool != NIL_RTREQPOOL)
     3995    {
     3996        int rc = RTReqPoolCallEx(pThis->hReqPool, 0 /*cMillies*/, NULL, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
     3997                                 (PFNRT)drvAudioDevicesEnumerateInternal,
     3998                                 3, pThis, true /*fLog*/, (PPDMAUDIOHOSTENUM)NULL /*pDevEnum*/);
     3999        LogFunc(("RTReqPoolCallEx: %Rrc\n", rc));
     4000        if (RT_SUCCESS(rc))
     4001            return;
     4002    }
     4003    LogFunc(("Calling drvAudioDevicesEnumerateInternal...\n"));
     4004    drvAudioDevicesEnumerateInternal(pThis, true /* fLog */, NULL /* pDevEnum */);
     4005}
     4006#endif /* VBOX_WITH_AUDIO_ENUM */
     4007
     4008
    39984009/**
    39994010 * @interface_method_impl{PDMIHOSTAUDIOPORT,pfnNotifyDevicesChanged}
     
    40044015    LogRel(("Audio: Device configuration of driver '%s' has changed\n", pThis->szName));
    40054016
    4006     /** @todo Remove legacy behaviour: */
    4007     if (!pThis->pHostDrvAudio->pfnStreamNotifyDeviceChanged)
    4008     {
     4017#ifdef RT_OS_DARWIN /** @todo Remove legacy behaviour: */
    40094018/** @todo r=bird: Locking?   */
    4010         /* Mark all host streams to re-initialize. */
    4011         PDRVAUDIOSTREAM pStreamEx;
    4012         RTListForEach(&pThis->lstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
    4013         {
    4014             drvAudioStreamMarkNeedReInit(pStreamEx, __PRETTY_FUNCTION__);
    4015         }
    4016     }
    4017 
    4018 # ifdef VBOX_WITH_AUDIO_ENUM
    4019     /* Re-enumerate all host devices as soon as possible. */
    4020     pThis->fEnumerateDevices = true;
    4021 # endif
     4019    /* Mark all host streams to re-initialize. */
     4020    PDRVAUDIOSTREAM pStreamEx;
     4021    RTListForEach(&pThis->lstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
     4022    {
     4023        drvAudioStreamMarkNeedReInit(pStreamEx, __PRETTY_FUNCTION__);
     4024    }
     4025#endif
     4026
     4027#ifdef VBOX_WITH_AUDIO_ENUM
     4028    /*
     4029     * Re-enumerate all host devices with a tiny delay to avoid re-doing this
     4030     * when a bunch of changes happens at once (they typically do on windows).
     4031     * We'll keep postponing it till it quiesces for a fraction of a second.
     4032     */
     4033    int rc = PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hEnumTimer, RT_MS_1SEC / 3);
     4034    AssertRC(rc);
     4035#endif
    40224036}
    40234037
     
    46804694                                0 /*fFlags*/, pThis->szTimerName, &pThis->hTimer);
    46814695    AssertRCReturn(rc, rc);
     4696
     4697#ifdef VBOX_WITH_AUDIO_ENUM
     4698    /*
     4699     * Create a timer to trigger delayed device enumeration on device changes.
     4700     */
     4701    RTStrPrintf(pThis->szEnumTimerName, sizeof(pThis->szEnumTimerName), "AudioEnum-%u", pDrvIns->iInstance);
     4702    rc = PDMDrvHlpTMTimerCreate(pDrvIns, TMCLOCK_REAL, drvAudioEnumerateTimer, NULL /*pvUser*/,
     4703                                0 /*fFlags*/, pThis->szEnumTimerName, &pThis->hEnumTimer);
     4704    AssertRCReturn(rc, rc);
     4705#endif
    46824706
    46834707    /*
  • trunk/src/VBox/Devices/Audio/DrvHostAudioDSound.cpp

    r88819 r88853  
    28112811        try
    28122812        {
    2813             pThis->m_pNotificationClient = new DrvHostAudioDSoundMMNotifClient(pIHostAudioPort);
     2813            pThis->m_pNotificationClient = new DrvHostAudioDSoundMMNotifClient(pIHostAudioPort,
     2814                                                                               pThis->Cfg.pGuidCapture == NULL,
     2815                                                                               pThis->Cfg.pGuidPlay == NULL);
    28142816        }
    28152817        catch (std::bad_alloc &)
  • trunk/src/VBox/Devices/Audio/DrvHostAudioDSoundMMNotifClient.cpp

    r88819 r88853  
    3030
    3131
    32 DrvHostAudioDSoundMMNotifClient::DrvHostAudioDSoundMMNotifClient(PPDMIHOSTAUDIOPORT pInterface)
    33     : m_fRegisteredClient(false)
     32DrvHostAudioDSoundMMNotifClient::DrvHostAudioDSoundMMNotifClient(PPDMIHOSTAUDIOPORT pInterface, bool fDefaultIn, bool fDefaultOut)
     33    : m_fDefaultIn(fDefaultIn)
     34    , m_fDefaultOut(fDefaultOut)
     35    , m_fRegisteredClient(false)
    3436    , m_cRef(1)
    3537    , m_pIAudioNotifyFromHost(pInterface)
     
    4850    HRESULT hr = m_pEnum->RegisterEndpointNotificationCallback(this);
    4951    if (SUCCEEDED(hr))
    50     {
    5152        m_fRegisteredClient = true;
    5253
    53         hr = AttachToDefaultEndpoint();
    54     }
    55 
    5654    return hr;
    5755}
     
    6260void DrvHostAudioDSoundMMNotifClient::Unregister(void)
    6361{
    64     DetachFromEndpoint();
    65 
    6662    if (m_fRegisteredClient)
    6763    {
     
    8783
    8884/**
    89  * Stub being called when attaching to the default audio endpoint.
    90  * Does nothing at the moment.
    91  */
    92 HRESULT DrvHostAudioDSoundMMNotifClient::AttachToDefaultEndpoint(void)
    93 {
    94     return S_OK;
    95 }
    96 
    97 /**
    98  * Stub being called when detaching from the default audio endpoint.
    99  * Does nothing at the moment.
    100  */
    101 void DrvHostAudioDSoundMMNotifClient::DetachFromEndpoint(void)
    102 {
    103 
    104 }
    105 
    106 /**
    107  * Helper function for invoking the audio connector callback (if any).
    108  */
    109 void DrvHostAudioDSoundMMNotifClient::doCallback(void)
    110 {
     85 * Handler implementation which is called when an audio device state
     86 * has been changed.
     87 *
     88 * @return  HRESULT
     89 * @param   pwstrDeviceId       Device ID the state is announced for.
     90 * @param   dwNewState          New state the device is now in.
     91 */
     92STDMETHODIMP DrvHostAudioDSoundMMNotifClient::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState)
     93{
     94    char *pszState = "unknown";
     95
     96    switch (dwNewState)
     97    {
     98        case DEVICE_STATE_ACTIVE:
     99            pszState = "active";
     100            break;
     101        case DEVICE_STATE_DISABLED:
     102            pszState = "disabled";
     103            break;
     104        case DEVICE_STATE_NOTPRESENT:
     105            pszState = "not present";
     106            break;
     107        case DEVICE_STATE_UNPLUGGED:
     108            pszState = "unplugged";
     109            break;
     110        default:
     111            break;
     112    }
     113
     114    LogRel(("Audio: Device '%ls' has changed state to '%s'\n", pwstrDeviceId, pszState));
     115
    111116    if (m_pIAudioNotifyFromHost)
    112117        m_pIAudioNotifyFromHost->pfnNotifyDevicesChanged(m_pIAudioNotifyFromHost);
    113 }
    114 
    115 /**
    116  * Handler implementation which is called when an audio device state
    117  * has been changed.
    118  *
    119  * @return  HRESULT
    120  * @param   pwstrDeviceId       Device ID the state is announced for.
    121  * @param   dwNewState          New state the device is now in.
    122  */
    123 STDMETHODIMP DrvHostAudioDSoundMMNotifClient::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState)
    124 {
    125     char *pszState = "unknown";
    126 
    127     switch (dwNewState)
    128     {
    129         case DEVICE_STATE_ACTIVE:
    130             pszState = "active";
    131             break;
    132         case DEVICE_STATE_DISABLED:
    133             pszState = "disabled";
    134             break;
    135         case DEVICE_STATE_NOTPRESENT:
    136             pszState = "not present";
    137             break;
    138         case DEVICE_STATE_UNPLUGGED:
    139             pszState = "unplugged";
    140             break;
    141         default:
    142             break;
    143     }
    144 
    145     LogRel(("Audio: Device '%ls' has changed state to '%s'\n", pwstrDeviceId, pszState));
    146 
    147     doCallback();
    148118
    149119    return S_OK;
     
    159129{
    160130    LogRel(("Audio: Device '%ls' has been added\n", pwstrDeviceId));
    161 
     131    /* Note! It is hard to properly support non-default devices when the backend is DSound,
     132             as DSound talks GUID where-as the pwszDeviceId string we get here is something
     133             completely different.  So, ignorining that edge case here.  The WasApi backend
     134             supports this, though. */
     135    if (m_pIAudioNotifyFromHost)
     136        m_pIAudioNotifyFromHost->pfnNotifyDevicesChanged(m_pIAudioNotifyFromHost);
    162137    return S_OK;
    163138}
     
    172147{
    173148    LogRel(("Audio: Device '%ls' has been removed\n", pwstrDeviceId));
    174 
     149    if (m_pIAudioNotifyFromHost)
     150        m_pIAudioNotifyFromHost->pfnNotifyDevicesChanged(m_pIAudioNotifyFromHost);
    175151    return S_OK;
    176152}
     
    187163STDMETHODIMP DrvHostAudioDSoundMMNotifClient::OnDefaultDeviceChanged(EDataFlow eFlow, ERole eRole, LPCWSTR pwstrDefaultDeviceId)
    188164{
    189     RT_NOREF(eRole);
    190 
    191     char *pszRole = "unknown";
    192 
    193     if (eFlow == eRender)
    194         pszRole = "output";
    195     else if (eFlow == eCapture)
    196         pszRole = "input";
    197 
    198     LogRel(("Audio: Default %s device has been changed to '%ls'\n", pszRole, pwstrDefaultDeviceId));
    199 
    200     doCallback();
    201 
     165    /* When the user triggers a default device change, we'll typically get two or
     166       three notifications. Just pick up the one for the multimedia role for now
     167       (dunno if DSound default equals eMultimedia or eConsole, and whether it make
     168       any actual difference). */
     169    if (eRole == eMultimedia)
     170    {
     171        PDMAUDIODIR enmDir  = PDMAUDIODIR_INVALID;
     172        char       *pszRole = "unknown";
     173        if (eFlow == eRender)
     174        {
     175            pszRole = "output";
     176            if (m_fDefaultOut)
     177                enmDir = PDMAUDIODIR_OUT;
     178        }
     179        else if (eFlow == eCapture)
     180        {
     181            pszRole = "input";
     182            if (m_fDefaultIn)
     183                enmDir = PDMAUDIODIR_IN;
     184        }
     185
     186        LogRel(("Audio: Default %s device has been changed to '%ls'\n", pszRole, pwstrDefaultDeviceId));
     187
     188        if (m_pIAudioNotifyFromHost)
     189        {
     190            if (enmDir != PDMAUDIODIR_INVALID)
     191                m_pIAudioNotifyFromHost->pfnNotifyDeviceChanged(m_pIAudioNotifyFromHost, enmDir, NULL);
     192            m_pIAudioNotifyFromHost->pfnNotifyDevicesChanged(m_pIAudioNotifyFromHost);
     193        }
     194    }
    202195    return S_OK;
    203196}
  • trunk/src/VBox/Devices/Audio/DrvHostAudioDSoundMMNotifClient.h

    r88819 r88853  
    4343public:
    4444
    45     DrvHostAudioDSoundMMNotifClient(PPDMIHOSTAUDIOPORT pInterface);
     45    DrvHostAudioDSoundMMNotifClient(PPDMIHOSTAUDIOPORT pInterface, bool fDefaultIn, bool fDefaultOut);
    4646    virtual ~DrvHostAudioDSoundMMNotifClient();
    4747
     
    5858private:
    5959
     60    bool                        m_fDefaultIn;
     61    bool                        m_fDefaultOut;
    6062    bool                        m_fRegisteredClient;
    6163    IMMDeviceEnumerator        *m_pEnum;
     
    6466    long                        m_cRef;
    6567
    66     PPDMIHOSTAUDIOPORT           m_pIAudioNotifyFromHost;
    67 
    68     HRESULT AttachToDefaultEndpoint();
    69     void    DetachFromEndpoint();
    70 
    71     void    doCallback(void);
     68    PPDMIHOSTAUDIOPORT          m_pIAudioNotifyFromHost;
    7269
    7370    /** @name IMMNotificationClient interface
  • trunk/src/VBox/Devices/Audio/DrvHostAudioWasApi.cpp

    r88851 r88853  
    415415        RT_NOREF(pwszDeviceId, dwNewState);
    416416        Log7Func(("pwszDeviceId=%ls dwNewState=%u (%#x)\n", pwszDeviceId, dwNewState, dwNewState));
     417
     418        /*
     419         * Just trigger device re-enumeration.
     420         */
     421        notifyDeviceChanges();
     422
     423        /** @todo do we need to check for our devices here too?  Not when using a
     424         *        default device.  But when using a specific device, we could perhaps
     425         *        re-init the stream when dwNewState indicates precense.  We might
     426         *        also take action when a devices ceases to be operating, but again
     427         *        only for non-default devices, probably... */
     428
    417429        return S_OK;
    418430    }
     
    451463                               fOutput ? "output" : "input", pwszDeviceId, hrc));
    452464            pIEnumerator->Release();
     465
     466            /*
     467             * Trigger device re-enumeration.
     468             */
     469            notifyDeviceChanges();
    453470        }
    454471        return S_OK;
     
    468485            && (   (fOutput = RTUtf16ICmp(m_pDrvWas->pwszOutputDevId, pwszDeviceId) == 0)
    469486                || RTUtf16ICmp(m_pDrvWas->pwszInputDevId, pwszDeviceId) == 0))
     487        {
     488            RTCritSectLeave(&m_CritSect);
    470489            setDevice(fOutput, NULL, pwszDeviceId, __PRETTY_FUNCTION__);
    471         RTCritSectLeave(&m_CritSect);
     490        }
     491        else
     492            RTCritSectLeave(&m_CritSect);
     493
     494        /*
     495         * Trigger device re-enumeration.
     496         */
     497        notifyDeviceChanges();
    472498        return S_OK;
    473499    }
     
    502528                               enmFlow == eRender ? "output" : "input", hrc));
    503529            pIEnumerator->Release();
    504         }
    505 
    506         RT_NOREF(enmFlow, enmRole, pwszDefaultDeviceId);
     530
     531            /*
     532             * Trigger device re-enumeration.
     533             */
     534            notifyDeviceChanges();
     535        }
     536
    507537        Log7Func(("enmFlow=%d enmRole=%d pwszDefaultDeviceId=%ls\n", enmFlow, enmRole, pwszDefaultDeviceId));
    508538        return S_OK;
     
    577607        }
    578608
     609        RTCritSectLeave(&m_CritSect);
     610    }
     611
     612    /**
     613     * Tell DrvAudio to re-enumerate devices when it get a chance.
     614     *
     615     * We exit the critsect here too before calling DrvAudio just to be on the safe
     616     * side (see setDevice()), even though the current DrvAudio code doesn't take
     617     * any critsects.
     618     */
     619    void notifyDeviceChanges(void)
     620    {
     621        RTCritSectEnter(&m_CritSect);
     622        if (m_pDrvWas)
     623        {
     624            PPDMIHOSTAUDIOPORT const pIHostAudioPort = m_pDrvWas->pIHostAudioPort;
     625            if (pIHostAudioPort)
     626            {
     627                VMSTATE const enmVmState = PDMDrvHlpVMState(m_pDrvWas->pDrvIns);
     628                if (enmVmState < VMSTATE_POWERING_OFF)
     629                {
     630                    RTCritSectLeave(&m_CritSect);
     631                    pIHostAudioPort->pfnNotifyDevicesChanged(pIHostAudioPort);
     632                    return;
     633                }
     634                LogFlowFunc(("Ignoring change: enmVmState=%d\n", enmVmState));
     635            }
     636        }
    579637        RTCritSectLeave(&m_CritSect);
    580638    }
     
    25752633    }
    25762634#endif
     2635
     2636    /*
     2637     * Deregister the notification client to reduce the risk of notifications
     2638     * comming in while we're being detatched or the VM is being destroyed.
     2639     */
     2640    if (pThis->pNotifyClient)
     2641    {
     2642        pThis->pNotifyClient->notifyDriverDestroyed();
     2643        pThis->pIEnumerator->UnregisterEndpointNotificationCallback(pThis->pNotifyClient);
     2644        pThis->pNotifyClient->Release();
     2645        pThis->pNotifyClient = NULL;
     2646    }
    25772647}
    25782648
     
    25922662        pThis->pIEnumerator->UnregisterEndpointNotificationCallback(pThis->pNotifyClient);
    25932663        pThis->pNotifyClient->Release();
     2664        pThis->pNotifyClient = NULL;
    25942665    }
    25952666
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