Index: trunk/src/VBox/Devices/Audio/audio.c
===================================================================
--- trunk/src/VBox/Devices/Audio/audio.c	(revision 34905)
+++ trunk/src/VBox/Devices/Audio/audio.c	(revision 34906)
@@ -1551,4 +1551,6 @@
 
     if (s->drv_opaque) {
+        /* Filter must be installed before initializing voices. */
+        drv = filteraudio_install(drv, s->drv_opaque);
         audio_init_nb_voices_out (s, drv);
         audio_init_nb_voices_in (s, drv);
Index: trunk/src/VBox/Devices/Audio/audio_int.h
===================================================================
--- trunk/src/VBox/Devices/Audio/audio_int.h	(revision 34905)
+++ trunk/src/VBox/Devices/Audio/audio_int.h	(revision 34906)
@@ -314,3 +314,19 @@
                                     unsigned cSamples);
 
+/*
+ * Filter interface.
+ */
+typedef DECLCALLBACK(int) FNAUDIOINPUTCALLBACK(void* pvCtx, uint32_t cbSamples, const void *pvSamples);
+typedef FNAUDIOINPUTCALLBACK *PFNAUDIOINPUTCALLBACK;
+
+int filter_output_intercepted(void);
+int filter_output_begin(void **ppvOutputCtx, struct audio_pcm_info *pinfo, int samples);
+void filter_output_end(void *pvOutputCtx);
+
+int filter_input_intercepted(void);
+int filter_input_begin(void **ppvInputCtx, PFNAUDIOINPUTCALLBACK pfnCallback, void *pvCallback, HWVoiceIn *phw, int samples);
+void filter_input_end(void *pvInputCtx);
+
+struct audio_driver *filteraudio_install(struct audio_driver *pDrv, void *pDrvOpaque);
+
 #endif /* audio_int.h */
Index: trunk/src/VBox/Devices/Audio/audiosniffer.c
===================================================================
--- trunk/src/VBox/Devices/Audio/audiosniffer.c	(revision 34905)
+++ trunk/src/VBox/Devices/Audio/audiosniffer.c	(revision 34906)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2006-2007 Oracle Corporation
+ * Copyright (C) 2006-2010 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -22,4 +22,5 @@
 
 #include <VBox/log.h>
+#include <iprt/asm.h>
 #include <iprt/assert.h>
 #include <iprt/uuid.h>
@@ -40,4 +41,7 @@
     /** Whether audio should reach the host driver too. */
     bool fKeepHostAudio;
+
+    /** Whether audio input operations should be forwarded to the connector. */
+    bool fInterceptAudioInput;
 
     /** Pointer to device instance. */
@@ -100,6 +104,337 @@
 
 /*
- * Audio Sniffer PDM device.
- */
+ * Filter interface.
+ */
+
+/* Internal audio input context, which makes sure that:
+ *   - the filter audio input callback is not called after the filter has issued filter_input_end;
+ *   - maintains internal information and state of the audio stream.
+ */
+typedef struct SnifferInputCtx
+{
+    /* Whether the context is still in use by the filter or I'll check. */
+    int32_t volatile cRefs;
+
+    /* The filter callback for incoming audio data. */
+    PFNAUDIOINPUTCALLBACK pfnFilterCallback;
+    void *pvFilterCallback;
+
+    /* Whether the stream has been ended by the filter. */
+    bool fEndedByFilter;
+
+    /* Context pointer returned by pfnAudioInputBegin. */
+    void *pvUserCtx;
+
+    /* Audio format used for recording. */
+    HWVoiceIn *phw;
+
+    /* Number of bytes per frame (bitsPerSample * channels) of the actual input format. */
+    uint32_t cBytesPerFrame;
+
+    /* Frequency of the actual audio format. */
+    int iFreq;
+
+    /* Convertion from the actual input format to st_sample_t. */
+    t_sample *conv;
+
+    /* If the actual format frequence differs from the requested format, this is not NULL. */
+    void *rate;
+
+} SnifferInputCtx;
+
+/*
+ * Filter audio output.
+ */
+
+/* Whether the filter should intercept audio output. */
+int filter_output_intercepted(void)
+{
+    return 0; /* @todo Not implemented yet.*/
+}
+
+/* Filter informs that an audio output is starting. */
+int filter_output_begin(void **ppvOutputCtx, struct audio_pcm_info *pinfo, int samples)
+{
+    return VERR_NOT_SUPPORTED; /* @todo Not implemented yet.*/
+}
+
+/* Filter informs that the audio output has been stopped. */
+void filter_output_end(void *pvOutputCtx)
+{
+    return; /* @todo Not implemented yet.*/
+}
+
+/*
+ * Filter audio input.
+ */
+
+/* Whether the filter should intercept audio input. */
+int filter_input_intercepted(void)
+{
+    if (!g_pData || !g_pData->pDrv)
+    {
+        return 0;
+    }
+
+    return g_pData->fInterceptAudioInput;
+}
+
+/* Filter informs that an audio input is starting. */
+int filter_input_begin (void **ppvInputCtx, PFNAUDIOINPUTCALLBACK pfnCallback, void *pvCallback, HWVoiceIn *phw, int cSamples)
+{
+    int rc = VINF_SUCCESS;
+
+    SnifferInputCtx *pCtx = NULL;
+
+    if (!g_pData || !g_pData->pDrv)
+    {
+        return VERR_NOT_SUPPORTED;
+    }
+
+    pCtx = (SnifferInputCtx *)RTMemAlloc(sizeof(SnifferInputCtx));
+
+    if (!pCtx)
+    {
+        return VERR_NO_MEMORY;
+    }
+
+    pCtx->cRefs = 2; /* Context is used by both the filter and the user. */
+    pCtx->pfnFilterCallback = pfnCallback;
+    pCtx->pvFilterCallback = pvCallback;
+    pCtx->fEndedByFilter = false;
+    pCtx->pvUserCtx = NULL;
+    pCtx->phw = phw;
+    pCtx->cBytesPerFrame = 1;
+    pCtx->iFreq = 0;
+    pCtx->conv = NULL;
+    pCtx->rate = NULL;
+
+    rc = g_pData->pDrv->pfnAudioInputBegin (g_pData->pDrv,
+                                            &pCtx->pvUserCtx,      /* Returned by the pDrv. */
+                                            pCtx,
+                                            cSamples,              /* How many samples in one block is preferred. */
+                                            phw->info.freq,        /* Required frequency. */
+                                            phw->info.nchannels,   /* Number of audio channels. */
+                                            phw->info.bits);       /* A sample size in one channel, samples are signed. */
+
+    if (RT_SUCCESS(rc))
+    {
+        *ppvInputCtx = pCtx;
+    }
+    else
+    {
+        RTMemFree(pCtx);
+    }
+
+    Log(("input_begin rc = %Rrc\n", rc));
+
+    return rc;
+}
+
+/* Filter informs that the audio input must be stopped. */
+void filter_input_end(void *pvCtx)
+{
+    int32_t c;
+
+    SnifferInputCtx *pCtx = (SnifferInputCtx *)pvCtx;
+
+    void *pvUserCtx = pCtx->pvUserCtx;
+
+    pCtx->fEndedByFilter = true;
+
+    c = ASMAtomicDecU32(&pCtx->cRefs);
+
+    if (c == 0)
+    {
+        /* The caller will not use this context anymore. */
+        if (pCtx->rate)
+        {
+            st_rate_stop (pCtx->rate);
+        }
+        RTMemFree(pCtx);
+        pCtx = NULL;
+    }
+
+    if (!g_pData || !g_pData->pDrv)
+    {
+        AssertFailed();
+        return;
+    }
+
+    g_pData->pDrv->pfnAudioInputEnd (g_pData->pDrv,
+                                     pvUserCtx);
+
+    Log(("input_end\n"));
+}
+
+
+/*
+ * Audio PDM device.
+ */
+static DECLCALLBACK(int) iface_AudioInputIntercept (PPDMIAUDIOSNIFFERPORT pInterface, bool fIntercept)
+{
+    AUDIOSNIFFERSTATE *pThis = RT_FROM_MEMBER(pInterface, AUDIOSNIFFERSTATE, IPort);
+
+    Assert(g_pData == pThis);
+
+    pThis->fInterceptAudioInput = fIntercept;
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) iface_AudioInputEventBegin (PPDMIAUDIOSNIFFERPORT pInterface,
+                                                     void *pvContext,
+                                                     int iSampleHz,
+                                                     int cChannels,
+                                                     int cBits,
+                                                     bool fUnsigned)
+{
+    int bitIdx;
+
+    AUDIOSNIFFERSTATE *pThis = RT_FROM_MEMBER(pInterface, AUDIOSNIFFERSTATE, IPort);
+
+    int rc = VINF_SUCCESS;
+
+    SnifferInputCtx *pCtx = (SnifferInputCtx *)pvContext;
+
+    Log(("FilterAudio: AudioInputEventBegin: %dHz,%dch,%dbits,%d ended %d\n",
+         iSampleHz, cChannels, cBits, fUnsigned, pCtx->fEndedByFilter));
+
+    Assert(g_pData == pThis);
+
+    /* Prepare a format convertion for the actually used format. */
+    pCtx->cBytesPerFrame = ((cBits + 7) / 8) * cChannels;
+
+    if (cBits == 16)
+    {
+        bitIdx = 1;
+    }
+    else if (cBits == 32)
+    {
+        bitIdx = 2;
+    }
+    else
+    {
+        bitIdx = 0;
+    }
+
+    pCtx->conv = mixeng_conv[(cChannels == 2)? 1: 0] /* stereo */
+                            [!fUnsigned]             /* sign */
+                            [0]                      /* big endian */
+                            [bitIdx];                /* bits */
+
+    if (iSampleHz && iSampleHz != pCtx->phw->info.freq)
+    {
+        pCtx->rate = st_rate_start (iSampleHz, pCtx->phw->info.freq);
+        pCtx->iFreq = iSampleHz;
+    }
+
+    return rc;
+}
+
+static DECLCALLBACK(int) iface_AudioInputEventData (PPDMIAUDIOSNIFFERPORT pInterface,
+                                                    void *pvContext,
+                                                    const void *pvData,
+                                                    uint32_t cbData)
+{
+    AUDIOSNIFFERSTATE *pThis = RT_FROM_MEMBER(pInterface, AUDIOSNIFFERSTATE, IPort);
+
+    int rc = VINF_SUCCESS;
+
+    SnifferInputCtx *pCtx = (SnifferInputCtx *)pvContext;
+
+    Log(("FilterAudio: AudioInputEventData: pvData %p. cbData %d, ended %d\n", pvData, cbData, pCtx->fEndedByFilter));
+
+    Assert(g_pData == pThis);
+
+    if (   !pCtx->fEndedByFilter
+        && pCtx->conv)
+    {
+        /* Convert PCM samples to st_sample_t.
+         * And then apply rate convertion if necessary.
+         */
+        /* @todo Optimization: allocate ps buffer once per context and reallocate if cbData changes. */
+        uint32_t cs = cbData / pCtx->cBytesPerFrame;
+        st_sample_t *ps = (st_sample_t *)RTMemAlloc(cs * sizeof(st_sample_t));
+        if (ps)
+        {
+            void *pvSamplesRateDst = NULL;
+
+            void *pvSamples = NULL;
+            uint32_t cbSamples = 0;
+
+            pCtx->conv(ps, pvData, cs, &nominal_volume);
+
+            if (pCtx->rate)
+            {
+                uint32_t csConverted = (cs * pCtx->phw->info.freq) / pCtx->iFreq;
+                pvSamplesRateDst = RTMemAlloc(csConverted * sizeof(st_sample_t));
+
+                if (pvSamplesRateDst)
+                {
+                    int csSrc = cs;
+                    int csDst = csConverted;
+
+                    st_rate_flow (pCtx->rate,
+                                  ps, (st_sample_t *)pvSamplesRateDst,
+                                  &csSrc, &csDst);
+
+                    pvSamples = pvSamplesRateDst;
+                    cbSamples = csDst * sizeof(st_sample_t);
+                }
+                else
+                {
+                    rc = VERR_NO_MEMORY;
+                }
+            }
+            else
+            {
+                pvSamples = ps;
+                cbSamples = cs * sizeof(st_sample_t);
+            }
+
+            if (cbSamples)
+            {
+                rc = pCtx->pfnFilterCallback(pCtx->pvFilterCallback, cbSamples, pvSamples);
+            }
+
+            RTMemFree(pvSamplesRateDst);
+            RTMemFree(ps);
+        }
+        else
+        {
+            rc = VERR_NO_MEMORY;
+        }
+    }
+
+    return rc;
+}
+
+static DECLCALLBACK(void) iface_AudioInputEventEnd (PPDMIAUDIOSNIFFERPORT pInterface,
+                                                    void *pvContext)
+{
+    int32_t c;
+
+    AUDIOSNIFFERSTATE *pThis = RT_FROM_MEMBER(pInterface, AUDIOSNIFFERSTATE, IPort);
+
+    SnifferInputCtx *pCtx = (SnifferInputCtx *)pvContext;
+
+    Log(("FilterAudio: AudioInputEventEnd: ended %d\n", pCtx->fEndedByFilter));
+
+    Assert(g_pData == pThis);
+
+    c = ASMAtomicDecU32(&pCtx->cRefs);
+
+    if (c == 0)
+    {
+        /* The caller will not use this context anymore. */
+        if (pCtx->rate)
+        {
+            st_rate_stop (pCtx->rate);
+        }
+        RTMemFree(pCtx);
+    }
+}
+
 
 static DECLCALLBACK(int) iface_Setup (PPDMIAUDIOSNIFFERPORT pInterface, bool fEnable, bool fKeepHostAudio)
@@ -159,5 +494,5 @@
      * Validate configuration.
      */
-    if (!CFGMR3AreValuesValid(pCfgHandle, "\0"))
+    if (!CFGMR3AreValuesValid(pCfgHandle, "InterceptAudioInput\0"))
     {
         return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
@@ -170,4 +505,8 @@
     pThis->fKeepHostAudio = true;
     pThis->pDrv = NULL;
+    rc = CFGMR3QueryBoolDef(pCfgHandle, "InterceptAudioInput", &pThis->fInterceptAudioInput, false);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("Configuration error: Failed to get the \"YieldOnLSRRead\" value"));
 
     /*
@@ -179,4 +518,8 @@
     /* Audio Sniffer port */
     pThis->IPort.pfnSetup = iface_Setup;
+    pThis->IPort.pfnAudioInputIntercept = iface_AudioInputIntercept;
+    pThis->IPort.pfnAudioInputEventBegin = iface_AudioInputEventBegin;
+    pThis->IPort.pfnAudioInputEventData = iface_AudioInputEventData;
+    pThis->IPort.pfnAudioInputEventEnd = iface_AudioInputEventEnd;
 
     /*
Index: trunk/src/VBox/Devices/Makefile.kmk
===================================================================
--- trunk/src/VBox/Devices/Makefile.kmk	(revision 34905)
+++ trunk/src/VBox/Devices/Makefile.kmk	(revision 34906)
@@ -794,4 +794,5 @@
  	Audio/mixeng.c \
  	Audio/noaudio.c \
+ 	Audio/filteraudio.c \
  	Input/DrvKeyboardQueue.cpp \
  	Input/DrvMouseQueue.cpp \
Index: trunk/src/VBox/Main/AudioSnifferInterface.cpp
===================================================================
--- trunk/src/VBox/Main/AudioSnifferInterface.cpp	(revision 34905)
+++ trunk/src/VBox/Main/AudioSnifferInterface.cpp	(revision 34906)
@@ -113,4 +113,30 @@
 }
 
+DECLCALLBACK(int) iface_AudioInputBegin (PPDMIAUDIOSNIFFERCONNECTOR pInterface,
+                                         void **ppvUserCtx,
+                                         void *pvContext,
+                                         uint32_t cSamples,
+                                         uint32_t iSampleHz,
+                                         uint32_t cChannels,
+                                         uint32_t cBits)
+{
+    PDRVAUDIOSNIFFER pDrv = PDMIAUDIOSNIFFERCONNECTOR_2_MAINAUDIOSNIFFER(pInterface);
+
+    return pDrv->pAudioSniffer->getParent()->consoleVRDPServer()->SendAudioInputBegin(ppvUserCtx,
+                                                                                      pvContext,
+                                                                                      cSamples,
+                                                                                      iSampleHz,
+                                                                                      cChannels,
+                                                                                      cBits);
+}
+
+DECLCALLBACK(void) iface_AudioInputEnd (PPDMIAUDIOSNIFFERCONNECTOR pInterface,
+                                        void *pvUserCtx)
+{
+    PDRVAUDIOSNIFFER pDrv = PDMIAUDIOSNIFFERCONNECTOR_2_MAINAUDIOSNIFFER(pInterface);
+
+    pDrv->pAudioSniffer->getParent()->consoleVRDPServer()->SendAudioInputEnd(pvUserCtx);
+}
+
 
 /**
@@ -175,4 +201,6 @@
     pThis->Connector.pfnAudioSamplesOut         = iface_AudioSamplesOut;
     pThis->Connector.pfnAudioVolumeOut          = iface_AudioVolumeOut;
+    pThis->Connector.pfnAudioInputBegin         = iface_AudioInputBegin;
+    pThis->Connector.pfnAudioInputEnd           = iface_AudioInputEnd;
 
     /*
Index: trunk/src/VBox/Main/ConsoleVRDPServer.cpp
===================================================================
--- trunk/src/VBox/Main/ConsoleVRDPServer.cpp	(revision 34905)
+++ trunk/src/VBox/Main/ConsoleVRDPServer.cpp	(revision 34906)
@@ -21,4 +21,5 @@
 #include "KeyboardImpl.h"
 #include "MouseImpl.h"
+#include "AudioSnifferInterface.h"
 #ifdef VBOX_WITH_EXTPACK
 # include "ExtPackManagerImpl.h"
@@ -510,9 +511,10 @@
 PFNVRDECREATESERVER ConsoleVRDPServer::mpfnVRDECreateServer = NULL;
 
-VRDEENTRYPOINTS_1 *ConsoleVRDPServer::mpEntryPoints = NULL;
-
-VRDECALLBACKS_1 ConsoleVRDPServer::mCallbacks =
-{
-    { VRDE_INTERFACE_VERSION_1, sizeof(VRDECALLBACKS_1) },
+VRDEENTRYPOINTS_3 ConsoleVRDPServer::mEntryPoints; /* A copy of the server entry points. */
+VRDEENTRYPOINTS_3 *ConsoleVRDPServer::mpEntryPoints = NULL;
+
+VRDECALLBACKS_3 ConsoleVRDPServer::mCallbacks =
+{
+    { VRDE_INTERFACE_VERSION_3, sizeof(VRDECALLBACKS_3) },
     ConsoleVRDPServer::VRDPCallbackQueryProperty,
     ConsoleVRDPServer::VRDPCallbackClientLogon,
@@ -526,5 +528,6 @@
     ConsoleVRDPServer::VRDPCallbackFramebufferUnlock,
     ConsoleVRDPServer::VRDPCallbackInput,
-    ConsoleVRDPServer::VRDPCallbackVideoModeHint
+    ConsoleVRDPServer::VRDPCallbackVideoModeHint,
+    ConsoleVRDPServer::VRDECallbackAudioIn
 };
 
@@ -851,4 +854,20 @@
 
     server->mConsole->VRDPClientDisconnect(u32ClientId, fu32Intercepted);
+
+    if (ASMAtomicReadU32(&server->mu32AudioInputClientId) == u32ClientId)
+    {
+        Log(("AUDIOIN: disconnected client %u\n", u32ClientId));
+        ASMAtomicWriteU32(&server->mu32AudioInputClientId, 0);
+
+        PPDMIAUDIOSNIFFERPORT pPort = server->mConsole->getAudioSniffer()->getAudioSnifferPort();
+        if (pPort)
+        {
+             // @todo dynamic filter attach does not yet work pPort->pfnAudioInputIntercept(pPort, false);
+        }
+        else
+        {
+            AssertFailed();
+        }
+    }
 }
 
@@ -887,4 +906,36 @@
             }
             rc = VINF_SUCCESS;
+        } break;
+
+        case VRDE_CLIENT_INTERCEPT_AUDIO_INPUT:
+        {
+            /* This request is processed internally by the ConsoleVRDPServer.
+             * Only one client is allowed to intercept audio input.
+             */
+            if (ASMAtomicCmpXchgU32(&server->mu32AudioInputClientId, u32ClientId, 0) == true)
+            {
+                Log(("AUDIOIN: connected client %u\n", u32ClientId));
+
+                PPDMIAUDIOSNIFFERPORT pPort = server->mConsole->getAudioSniffer()->getAudioSnifferPort();
+                if (pPort)
+                {
+                     // @todo dynamic filter attach does not yet work pPort->pfnAudioInputIntercept(pPort, true);
+                     if (ppvIntercept)
+                     {
+                         *ppvIntercept = server;
+                     }
+                }
+                else
+                {
+                    AssertFailed();
+                    ASMAtomicWriteU32(&server->mu32AudioInputClientId, 0);
+                    rc = VERR_NOT_SUPPORTED;
+                }
+            }
+            else
+            {
+                Log(("AUDIOIN: ignored client %u, active client %u\n", u32ClientId, server->mu32AudioInputClientId));
+                rc = VERR_NOT_SUPPORTED;
+            }
         } break;
 
@@ -1139,4 +1190,45 @@
 }
 
+DECLCALLBACK(void) ConsoleVRDPServer::VRDECallbackAudioIn(void *pvCallback,
+                                                          void *pvCtx,
+                                                          uint32_t u32ClientId,
+                                                          uint32_t u32Event,
+                                                          const void *pvData,
+                                                          uint32_t cbData)
+{
+    ConsoleVRDPServer *server = static_cast<ConsoleVRDPServer*>(pvCallback);
+
+    PPDMIAUDIOSNIFFERPORT pPort = server->mConsole->getAudioSniffer()->getAudioSnifferPort();
+
+    switch (u32Event)
+    {
+        case VRDE_AUDIOIN_BEGIN:
+        {
+            const VRDEAUDIOINBEGIN *pParms = (const VRDEAUDIOINBEGIN *)pvData;
+
+            pPort->pfnAudioInputEventBegin (pPort, pvCtx,
+                                            VRDE_AUDIO_FMT_SAMPLE_FREQ(pParms->fmt),
+                                            VRDE_AUDIO_FMT_CHANNELS(pParms->fmt),
+                                            VRDE_AUDIO_FMT_BITS_PER_SAMPLE(pParms->fmt),
+                                            VRDE_AUDIO_FMT_SIGNED(pParms->fmt)
+                                           );
+        } break;
+
+        case VRDE_AUDIOIN_DATA:
+        {
+            pPort->pfnAudioInputEventData (pPort, pvCtx, pvData, cbData);
+        } break;
+
+        case VRDE_AUDIOIN_END:
+        {
+            pPort->pfnAudioInputEventEnd (pPort, pvCtx);
+        } break;
+
+        default:
+            return;
+    }
+}
+
+
 ConsoleVRDPServer::ConsoleVRDPServer(Console *console)
 {
@@ -1159,4 +1251,5 @@
 
     mhServer = 0;
+    mServerInterfaceVersion = 0;
 
     m_fGuestWantsAbsolute = false;
@@ -1191,4 +1284,6 @@
 
     mAuthLibrary = 0;
+
+    mu32AudioInputClientId = 0;
 }
 
@@ -1272,5 +1367,61 @@
         if (RT_SUCCESS(vrc))
         {
-            vrc = mpfnVRDECreateServer(&mCallbacks.header, this, (VRDEINTERFACEHDR **)&mpEntryPoints, &mhServer);
+            VRDEENTRYPOINTS_3 *pEntryPoints3;
+            vrc = mpfnVRDECreateServer(&mCallbacks.header, this, (VRDEINTERFACEHDR **)&pEntryPoints3, &mhServer);
+
+            if (RT_SUCCESS(vrc))
+            {
+                mServerInterfaceVersion = 3;
+                mEntryPoints = *pEntryPoints3;
+                mpEntryPoints = &mEntryPoints;
+            }
+            else if (vrc == VERR_VERSION_MISMATCH)
+            {
+                /* An older version of VRDE is installed, try version 1. */
+                VRDEENTRYPOINTS_1 *pEntryPoints1;
+
+                VRDECALLBACKS_1 callbacks =
+                {
+                    { VRDE_INTERFACE_VERSION_1, sizeof(VRDECALLBACKS_1) },
+                    ConsoleVRDPServer::VRDPCallbackQueryProperty,
+                    ConsoleVRDPServer::VRDPCallbackClientLogon,
+                    ConsoleVRDPServer::VRDPCallbackClientConnect,
+                    ConsoleVRDPServer::VRDPCallbackClientDisconnect,
+                    ConsoleVRDPServer::VRDPCallbackIntercept,
+                    ConsoleVRDPServer::VRDPCallbackUSB,
+                    ConsoleVRDPServer::VRDPCallbackClipboard,
+                    ConsoleVRDPServer::VRDPCallbackFramebufferQuery,
+                    ConsoleVRDPServer::VRDPCallbackFramebufferLock,
+                    ConsoleVRDPServer::VRDPCallbackFramebufferUnlock,
+                    ConsoleVRDPServer::VRDPCallbackInput,
+                    ConsoleVRDPServer::VRDPCallbackVideoModeHint
+                };
+
+                vrc = mpfnVRDECreateServer(&callbacks.header, this, (VRDEINTERFACEHDR **)&pEntryPoints1, &mhServer);
+                if (RT_SUCCESS(vrc))
+                {
+                    LogRel(("VRDE: loaded an older version of the server.\n"));
+
+                    mServerInterfaceVersion = 3;
+                    mEntryPoints.header = pEntryPoints1->header;
+                    mEntryPoints.VRDEDestroy = pEntryPoints1->VRDEDestroy;
+                    mEntryPoints.VRDEEnableConnections = pEntryPoints1->VRDEEnableConnections;
+                    mEntryPoints.VRDEDisconnect = pEntryPoints1->VRDEDisconnect;
+                    mEntryPoints.VRDEResize = pEntryPoints1->VRDEResize;
+                    mEntryPoints.VRDEUpdate = pEntryPoints1->VRDEUpdate;
+                    mEntryPoints.VRDEColorPointer = pEntryPoints1->VRDEColorPointer;
+                    mEntryPoints.VRDEHidePointer = pEntryPoints1->VRDEHidePointer;
+                    mEntryPoints.VRDEAudioSamples = pEntryPoints1->VRDEAudioSamples;
+                    mEntryPoints.VRDEAudioVolume = pEntryPoints1->VRDEAudioVolume;
+                    mEntryPoints.VRDEUSBRequest = pEntryPoints1->VRDEUSBRequest;
+                    mEntryPoints.VRDEClipboard = pEntryPoints1->VRDEClipboard;
+                    mEntryPoints.VRDEQueryInfo = pEntryPoints1->VRDEQueryInfo;
+                    mEntryPoints.VRDERedirect = NULL;
+                    mEntryPoints.VRDEAudioInOpen = NULL;
+                    mEntryPoints.VRDEAudioInClose = NULL;
+                    mpEntryPoints = &mEntryPoints;
+                }
+            }
+
             if (RT_SUCCESS(vrc))
             {
@@ -1282,5 +1433,5 @@
             {
                 if (vrc != VERR_NET_ADDRESS_IN_USE)
-                    LogRel(("VRDE: Could not start VRDP server rc = %Rrc\n", vrc));
+                    LogRel(("VRDE: Could not start the server rc = %Rrc\n", vrc));
                 /* Don't unload the lib, because it prevents us trying again or
                    because there may be other users? */
@@ -2098,4 +2249,48 @@
     }
 }
+
+/* @todo rc not needed? */
+int ConsoleVRDPServer::SendAudioInputBegin(void **ppvUserCtx,
+                                           void *pvContext,
+                                           uint32_t cSamples,
+                                           uint32_t iSampleHz,
+                                           uint32_t cChannels,
+                                           uint32_t cBits)
+{
+    if (mpEntryPoints && mhServer && mpEntryPoints->VRDEAudioInOpen)
+    {
+        uint32_t u32ClientId = ASMAtomicReadU32(&mu32AudioInputClientId);
+        if (u32ClientId != 0) /* 0 would mean broadcast to all clients. */
+        {
+            VRDEAUDIOFORMAT audioFormat = VRDE_AUDIO_FMT_MAKE(iSampleHz, cChannels, cBits, 0);
+            mpEntryPoints->VRDEAudioInOpen (mhServer,
+                                            pvContext,
+                                            u32ClientId,
+                                            audioFormat,
+                                            cSamples);
+            *ppvUserCtx = NULL; /* This is the ConsoleVRDP server context.
+                                 * Currently not used because only one client is allowed to
+                                 * do audio input and the client id is saved by the ConsoleVRDPServer.
+                                 */
+
+            return VINF_SUCCESS;
+        }
+    }
+    return VERR_NOT_SUPPORTED;
+}
+
+void ConsoleVRDPServer::SendAudioInputEnd(void *pvUserCtx)
+{
+    if (mpEntryPoints && mhServer && mpEntryPoints->VRDEAudioInClose)
+    {
+        uint32_t u32ClientId = ASMAtomicReadU32(&mu32AudioInputClientId);
+        if (u32ClientId != 0) /* 0 would mean broadcast to all clients. */
+        {
+            mpEntryPoints->VRDEAudioInClose(mhServer, u32ClientId);
+        }
+    }
+}
+
+
 
 void ConsoleVRDPServer::QueryInfo(uint32_t index, void *pvBuffer, uint32_t cbBuffer, uint32_t *pcbOut) const
Index: trunk/src/VBox/Main/include/ConsoleImpl.h
===================================================================
--- trunk/src/VBox/Main/include/ConsoleImpl.h	(revision 34905)
+++ trunk/src/VBox/Main/include/ConsoleImpl.h	(revision 34906)
@@ -168,4 +168,5 @@
     Display *getDisplay() const { return mDisplay; }
     MachineDebugger *getMachineDebugger() const { return mDebugger; }
+    AudioSniffer *getAudioSniffer() const { return mAudioSniffer; }
 
     const ComPtr<IMachine> &machine() const { return mMachine; }
Index: trunk/src/VBox/Main/include/ConsoleVRDPServer.h
===================================================================
--- trunk/src/VBox/Main/include/ConsoleVRDPServer.h	(revision 34905)
+++ trunk/src/VBox/Main/include/ConsoleVRDPServer.h	(revision 34906)
@@ -128,4 +128,13 @@
     void QueryInfo (uint32_t index, void *pvBuffer, uint32_t cbBuffer, uint32_t *pcbOut) const;
 
+    int SendAudioInputBegin(void **ppvUserCtx,
+                            void *pvContext,
+                            uint32_t cSamples,
+                            uint32_t iSampleHz,
+                            uint32_t cChannels,
+                            uint32_t cBits);
+
+    void SendAudioInputEnd(void *pvUserCtx);
+
 private:
     /* Note: This is not a ComObjPtr here, because the ConsoleVRDPServer object
@@ -135,4 +144,5 @@
 
     HVRDESERVER mhServer;
+    int mServerInterfaceVersion;
 
     static int loadVRDPLibrary (const char *pszLibraryName);
@@ -143,6 +153,7 @@
     static PFNVRDECREATESERVER mpfnVRDECreateServer;
 
-    static VRDEENTRYPOINTS_1 *mpEntryPoints;
-    static VRDECALLBACKS_1 mCallbacks;
+    static VRDEENTRYPOINTS_3 mEntryPoints;
+    static VRDEENTRYPOINTS_3 *mpEntryPoints;
+    static VRDECALLBACKS_3 mCallbacks;
 
     static DECLCALLBACK(int)  VRDPCallbackQueryProperty     (void *pvCallback, uint32_t index, void *pvBuffer, uint32_t cbBuffer, uint32_t *pcbOut);
@@ -158,4 +169,5 @@
     static DECLCALLBACK(void) VRDPCallbackInput             (void *pvCallback, int type, const void *pvInput, unsigned cbInput);
     static DECLCALLBACK(void) VRDPCallbackVideoModeHint     (void *pvCallback, unsigned cWidth, unsigned cHeight,  unsigned cBitsPerPixel, unsigned uScreenId);
+    static DECLCALLBACK(void) VRDECallbackAudioIn           (void *pvCallback, void *pvCtx, uint32_t u32ClientId, uint32_t u32Event, const void *pvData, uint32_t cbData);
 
     bool m_fGuestWantsAbsolute;
@@ -212,4 +224,6 @@
     PAUTHENTRY2 mpfnAuthEntry2;
     PAUTHENTRY3 mpfnAuthEntry3;
+
+    uint32_t volatile mu32AudioInputClientId;
 };
 
