Index: /trunk/include/VBox/HostServices/VBoxCrOpenGLSvc.h
===================================================================
--- /trunk/include/VBox/HostServices/VBoxCrOpenGLSvc.h	(revision 36842)
+++ /trunk/include/VBox/HostServices/VBoxCrOpenGLSvc.h	(revision 36843)
@@ -42,4 +42,5 @@
 #define SHCRGL_HOST_FN_CRHGSMI_CTL (11)
 #endif
+#define SHCRGL_HOST_FN_SET_OUTPUT_REDIRECT (20)
 /* crOpenGL guest functions */
 #define SHCRGL_GUEST_FN_WRITE       (2)
@@ -65,5 +66,40 @@
 #define SHCRGL_CPARMS_WRITE_BUFFER        (4)
 #define SHCRGL_CPARMS_WRITE_READ_BUFFERED (3)
-
+#define SHCRGL_CPARMS_SET_OUTPUT_REDIRECT (1)
+
+
+/* @todo Move to H3DOR.h begin */
+
+/* Names of supported output redirect formats. */
+#define H3DOR_FMT_RGBA_TOPDOWN "H3DOR_FMT_RGBA_TOPDOWN"
+
+/* Comma separated list of output formats supported by the output redirect target. */
+#define H3DOR_PROP_FORMATS 0
+
+#pragma pack(1)
+typedef struct {
+    /* The caller's context of the redirection. */
+    const void *pvContext;
+    /* Inform caller that a new window will be redirected. */
+    DECLR3CALLBACKMEMBER(void, H3DORBegin,           (const void *pvContext, void **ppvInstance,
+                                                      const char *pszFormat));
+    /* The window dimension has been changed. */
+    DECLR3CALLBACKMEMBER(void, H3DORGeometry,        (void *pvInstance,
+                                                      int32_t x, int32_t y, uint32_t w, uint32_t h));
+    /* Update the window visible region. */
+    DECLR3CALLBACKMEMBER(void, H3DORVisibleRegion,   (void *pvInstance,
+                                                      uint32_t cRects, RTRECT *paRects));
+    /* A rendered 3D frame is ready. Format of pvData is "pszFormat" parameter of H3DORBegin. */
+    DECLR3CALLBACKMEMBER(void, H3DORFrame,           (void *pvInstance,
+                                                      void *pvData, uint32_t cbData));
+    /* The window is closed. */
+    DECLR3CALLBACKMEMBER(void, H3DOREnd,             (void *pvInstance));
+    /* Obtain caller's parameters: the list of supported formats, etc. */
+    DECLR3CALLBACKMEMBER(int,  H3DORContextProperty, (const void *pvContext, uint32_t index,
+                                                      void *pvBuffer, uint32_t cbBuffer, uint32_t *pcbOut));
+} H3DOUTPUTREDIRECT;
+#pragma pack()
+
+/* @todo Move to H3DOR.h end */
 
 #ifdef VBOX_WITH_CRHGSMI
Index: /trunk/src/VBox/GuestHost/OpenGL/include/cr_server.h
===================================================================
--- /trunk/src/VBox/GuestHost/OpenGL/include/cr_server.h	(revision 36842)
+++ /trunk/src/VBox/GuestHost/OpenGL/include/cr_server.h	(revision 36843)
@@ -35,4 +35,28 @@
 
 typedef DECLCALLBACKPTR(void, PFNCRSERVERPRESENTFBO) (void *data, int32_t screenId, int32_t x, int32_t y, uint32_t w, uint32_t h);
+
+/* Callbacks for output of the rendered frames.
+ *
+ * This allows to pass rendered frames to an external component rather than draw them on screen.
+ *
+ * An external component registers the redirection callbacks using crVBoxServerOutputRedirectSet.
+ *
+ * The list of formats supported by the caller is obtained using CRORContextProperty.
+ * The actual format choosed by the service is passed as a CRORBegin parameter.
+ */
+typedef struct {
+    const void *pvContext; /* Supplied by crVBoxServerOutputRedirectSet. */
+    DECLR3CALLBACKMEMBER(void, CRORBegin,           (const void *pvContext, void **ppvInstance,
+                                                     const char *pszFormat));
+    DECLR3CALLBACKMEMBER(void, CRORGeometry,        (void *pvInstance,
+                                                     int32_t x, int32_t y, uint32_t w, uint32_t h));
+    DECLR3CALLBACKMEMBER(void, CRORVisibleRegion,   (void *pvInstance,
+                                                     uint32_t cRects, RTRECT *paRects));
+    DECLR3CALLBACKMEMBER(void, CRORFrame,           (void *pvInstance,
+                                                     void *pvData, uint32_t cbData));
+    DECLR3CALLBACKMEMBER(void, CROREnd,             (void *pvInstance));
+    DECLR3CALLBACKMEMBER(int,  CRORContextProperty, (const void *pvContext, uint32_t index,
+                                                      void *pvBuffer, uint32_t cbBuffer, uint32_t *pcbOut));
+} CROutputRedirect;
 
 typedef struct {
@@ -71,4 +95,6 @@
     GLuint fboWidth, fboHeight;
     GLuint idPBO;
+
+    void *pvOutputRedirectInstance;
 } CRMuralInfo;
 
@@ -233,4 +259,7 @@
                                                      *using callback above to update vbox framebuffers*/
     GLboolean             bUsePBOForReadback;       /*Use PBO's for data readback*/
+
+    GLboolean             bUseOutputRedirect;       /* Whether the output redirect was set. */
+    CROutputRedirect      outputRedirect;
 } CRServer;
 
@@ -265,4 +294,6 @@
 extern DECLEXPORT(int32_t) crVBoxServerSetOffscreenRendering(GLboolean value);
 
+extern DECLEXPORT(int32_t) crVBoxServerOutputRedirectSet(const CROutputRedirect *pCallbacks);
+
 #ifdef __cplusplus
 }
Index: /trunk/src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp
===================================================================
--- /trunk/src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp	(revision 36842)
+++ /trunk/src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp	(revision 36843)
@@ -164,4 +164,5 @@
         }
 
+        // @todo use critsect only to fetch the list and update the g_SvcPresentFBO's pQueueHead and pQueueTail.
         rc = RTCritSectEnter(&g_SvcPresentFBO.hQueueLock);
         AssertRCReturn(rc, rc);
@@ -1242,5 +1243,4 @@
                     g_pConsole = pConsole;
 
-                    /*rc = crVBoxServerSetOffscreenRendering(GL_TRUE);*/
                     rc = VINF_SUCCESS;
                 }
@@ -1349,4 +1349,54 @@
 
                 rc = VINF_SUCCESS;
+            }
+            break;
+        }
+        case SHCRGL_HOST_FN_SET_OUTPUT_REDIRECT:
+        {
+            /*
+             * OutputRedirect.
+             * Note: the service calls OutputRedirect callbacks directly
+             *       and they must not block. If asynchronous processing is needed,
+             *       the callback provider must organize this.
+             */
+            Log(("svcCall: SHCRGL_HOST_FN_SET_OUTPUT_REDIRECT\n"));
+
+            /* Verify parameter count and types. */
+            if (cParms != SHCRGL_CPARMS_SET_OUTPUT_REDIRECT)
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else if (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR)
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else
+            {
+                /* Fetch parameters. */
+                H3DOUTPUTREDIRECT *pOutputRedirect = (H3DOUTPUTREDIRECT *)paParms[0].u.pointer.addr;
+                uint32_t cbData = paParms[0].u.pointer.size;
+
+                /* Verify parameters values. */
+                if (cbData != sizeof (H3DOUTPUTREDIRECT))
+                {
+                    rc = VERR_INVALID_PARAMETER;
+                }
+                else /* Execute the function. */
+                {
+                    rc = crVBoxServerSetOffscreenRendering(GL_TRUE);
+
+                    if (RT_SUCCESS(rc))
+                    {
+                        CROutputRedirect or;
+                        or.pvContext = pOutputRedirect->pvContext;
+                        or.CRORBegin = pOutputRedirect->H3DORBegin;
+                        or.CRORGeometry = pOutputRedirect->H3DORGeometry;
+                        or.CRORVisibleRegion = pOutputRedirect->H3DORVisibleRegion;
+                        or.CRORFrame = pOutputRedirect->H3DORFrame;
+                        or.CROREnd = pOutputRedirect->H3DOREnd;
+                        or.CRORContextProperty = pOutputRedirect->H3DORContextProperty;
+                        rc = crVBoxServerOutputRedirectSet(&or);
+                    }
+                }
             }
             break;
Index: /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server.h
===================================================================
--- /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server.h	(revision 36842)
+++ /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server.h	(revision 36843)
@@ -89,4 +89,5 @@
 GLuint crServerTranslateProgramID(GLuint id);
 
+void crServerSetupOutputRedirect(CRMuralInfo *mural);
 void crServerCheckMuralGeometry(CRMuralInfo *mural);
 GLboolean crServerSupportRedirMuralFBO(void);
Index: /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c
===================================================================
--- /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c	(revision 36842)
+++ /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c	(revision 36843)
@@ -58,4 +58,5 @@
     cr_server.bForceOffscreenRendering = GL_FALSE;
     cr_server.bUsePBOForReadback = GL_FALSE;
+    cr_server.bUseOutputRedirect = GL_FALSE;
 }
 
Index: /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c
===================================================================
--- /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c	(revision 36842)
+++ /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c	(revision 36843)
@@ -1260,2 +1260,21 @@
     return VINF_SUCCESS;
 }
+
+DECLEXPORT(int32_t) crVBoxServerOutputRedirectSet(const CROutputRedirect *pCallbacks)
+{
+    /* No need for a synchronization as this is single threaded. */
+    if (pCallbacks)
+    {
+        cr_server.outputRedirect = *pCallbacks;
+        cr_server.bUseOutputRedirect = true;
+    }
+    else
+    {
+        cr_server.bUseOutputRedirect = false;
+    }
+
+    // @todo dynamically intercept already existing output:
+    // crHashtableWalk(cr_server.muralTable, crVBoxServerOutputRedirectCB, NULL);
+
+    return VINF_SUCCESS;
+}
Index: /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.c
===================================================================
--- /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.c	(revision 36842)
+++ /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.c	(revision 36843)
@@ -46,4 +46,54 @@
 }
 
+/* Called when a new CRMuralInfo is created
+ * or when OutputRedirect status is changed.
+ */
+void crServerSetupOutputRedirect(CRMuralInfo *mural)
+{
+    /* Unset the previous redirect. */
+    if (mural->pvOutputRedirectInstance)
+    {
+        cr_server.outputRedirect.CROREnd(mural->pvOutputRedirectInstance);
+        mural->pvOutputRedirectInstance = NULL;
+    }
+
+    /* Setup a new redirect. */
+    if (cr_server.bUseOutputRedirect)
+    {
+        /* Query supported formats. */
+        uint32_t cbFormats = 4096;
+        char *pachFormats = (char *)crAlloc(cbFormats);
+
+        if (pachFormats)
+        {
+            int rc = cr_server.outputRedirect.CRORContextProperty(cr_server.outputRedirect.pvContext,
+                                                                  0 /* H3DOR_PROP_FORMATS */, // @todo from a header
+                                                                  pachFormats, cbFormats, &cbFormats);
+            if (RT_SUCCESS(rc))
+            {
+                if (strstr(pachFormats, "H3DOR_FMT_RGBA_TOPDOWN"))
+                {
+                    cr_server.outputRedirect.CRORBegin(cr_server.outputRedirect.pvContext,
+                                                       &mural->pvOutputRedirectInstance,
+                                                       "H3DOR_FMT_RGBA_TOPDOWN"); // @todo from a header
+                }
+            }
+
+            crFree(pachFormats);
+        }
+
+        /* If this is not NULL then there was a supported format. */
+        if (mural->pvOutputRedirectInstance)
+        {
+            cr_server.outputRedirect.CRORGeometry(mural->pvOutputRedirectInstance,
+                                                  mural->hX, mural->hY,
+                                                  mural->width, mural->height);
+            // @todo the code assumes that RTRECT == four of GLInts
+            cr_server.outputRedirect.CRORVisibleRegion(mural->pvOutputRedirectInstance,
+                                                       mural->cVisibleRects, (RTRECT *)mural->pVisibleRects);
+        }
+    }
+}
+
 void crServerCheckMuralGeometry(CRMuralInfo *mural)
 {
@@ -136,4 +186,11 @@
             cr_server.head_spu->dispatch_table.WindowPosition(mural->spuWindow, mural->hX, mural->hY);
         }
+    }
+
+    if (mural->pvOutputRedirectInstance)
+    {
+        cr_server.outputRedirect.CRORGeometry(mural->pvOutputRedirectInstance,
+                                              mural->hX, mural->hY,
+                                              mural->width, mural->height);
     }
 }
@@ -455,4 +512,12 @@
     }
 
+    if (mural->pvOutputRedirectInstance)
+    {
+        /* @todo find out why presentfbo is not called but crorframe is called. */
+        cr_server.outputRedirect.CRORFrame(mural->pvOutputRedirectInstance,
+                                           pixels,
+                                           4 * mural->fboWidth * mural->fboHeight);
+    }
+
     if (bUsePBO)
     {
Index: /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_window.c
===================================================================
--- /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_window.c	(revision 36842)
+++ /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_window.c	(revision 36843)
@@ -88,4 +88,6 @@
         mural->bReceivedRects = GL_FALSE;
 
+        mural->pvOutputRedirectInstance = NULL;
+
         /* generate ID for this new window/mural (special-case for file conns) */
         if (cr_server.curClient && cr_server.curClient->conn->type == CR_FILE)
@@ -99,4 +101,6 @@
         pCreateInfo->visualBits = visBits;
         crHashtableAdd(cr_server.pWindowCreateInfoTable, windowID, pCreateInfo);
+
+        crServerSetupOutputRedirect(mural);
     }
 
@@ -148,4 +152,10 @@
     }
 
+    if (mural->pvOutputRedirectInstance)
+    {
+        cr_server.outputRedirect.CROREnd(mural->pvOutputRedirectInstance);
+        mural->pvOutputRedirectInstance = NULL;
+    }
+
     if (cr_server.currentWindow == window)
     {
@@ -295,4 +305,11 @@
 
     cr_server.head_spu->dispatch_table.WindowVisibleRegion(mural->spuWindow, cRects, pRects);
+
+    if (mural->pvOutputRedirectInstance)
+    {
+        /* @todo the code assumes that RTRECT == four GLInts. */
+        cr_server.outputRedirect.CRORVisibleRegion(mural->pvOutputRedirectInstance,
+                                                   cRects, (RTRECT *)pRects);
+    }
 }
 
Index: /trunk/src/VBox/Main/include/ConsoleVRDPServer.h
===================================================================
--- /trunk/src/VBox/Main/include/ConsoleVRDPServer.h	(revision 36842)
+++ /trunk/src/VBox/Main/include/ConsoleVRDPServer.h	(revision 36843)
@@ -24,4 +24,6 @@
 #include <VBox/VBoxAuth.h>
 
+#include <VBox/RemoteDesktop/VRDEImage.h>
+
 #include <VBox/HostServices/VBoxClipboardExt.h>
 
@@ -151,7 +153,7 @@
     static PFNVRDECREATESERVER mpfnVRDECreateServer;
 
-    static VRDEENTRYPOINTS_3 mEntryPoints;
-    static VRDEENTRYPOINTS_3 *mpEntryPoints;
-    static VRDECALLBACKS_3 mCallbacks;
+    static VRDEENTRYPOINTS_4 mEntryPoints;
+    static VRDEENTRYPOINTS_4 *mpEntryPoints;
+    static VRDECALLBACKS_4 mCallbacks;
 
     static DECLCALLBACK(int)  VRDPCallbackQueryProperty     (void *pvCallback, uint32_t index, void *pvBuffer, uint32_t cbBuffer, uint32_t *pcbOut);
@@ -224,4 +226,33 @@
 
     uint32_t volatile mu32AudioInputClientId;
+
+    static DECLCALLBACK(void) H3DORBegin(const void *pvContext, void **ppvInstance,
+                                         const char *pszFormat);
+    static DECLCALLBACK(void) H3DORGeometry(void *pvInstance,
+                                            int32_t x, int32_t y, uint32_t w, uint32_t h);
+    static DECLCALLBACK(void) H3DORVisibleRegion(void *pvInstance,
+                                                 uint32_t cRects, RTRECT *paRects);
+    static DECLCALLBACK(void) H3DORFrame(void *pvInstance,
+                                         void *pvData, uint32_t cbData);
+    static DECLCALLBACK(void) H3DOREnd(void *pvInstance);
+    static DECLCALLBACK(int)  H3DORContextProperty(const void *pvContext, uint32_t index,
+                                                   void *pvBuffer, uint32_t cbBuffer, uint32_t *pcbOut);
+
+    void remote3DRedirect(void);
+
+    /*
+     * VRDE server optional interfaces.
+     */
+
+    /* Image update interface. */
+    bool m_fInterfaceImage;
+    VRDEIMAGECALLBACKS m_interfaceCallbacksImage;
+    VRDEIMAGEINTERFACE m_interfaceImage;
+    static DECLCALLBACK(int) VRDEImageCbNotify (void *pvContext,
+                                                void *pvUser,
+                                                HVRDEIMAGE hVideo,
+                                                uint32_t u32Id,
+                                                void *pvData,
+                                                uint32_t cbData);
 };
 
Index: /trunk/src/VBox/Main/src-client/ConsoleVRDPServer.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/ConsoleVRDPServer.cpp	(revision 36842)
+++ /trunk/src/VBox/Main/src-client/ConsoleVRDPServer.cpp	(revision 36843)
@@ -25,4 +25,5 @@
 # include "ExtPackManagerImpl.h"
 #endif
+#include "VMMDev.h"
 
 #include "Global.h"
@@ -40,4 +41,5 @@
 #include <VBox/RemoteDesktop/VRDEOrders.h>
 #include <VBox/com/listeners.h>
+#include <VBox/HostServices/VBoxCrOpenGLSvc.h>
 
 class VRDPConsoleListener
@@ -520,10 +522,10 @@
 PFNVRDECREATESERVER ConsoleVRDPServer::mpfnVRDECreateServer = NULL;
 
-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) },
+VRDEENTRYPOINTS_4 ConsoleVRDPServer::mEntryPoints; /* A copy of the server entry points. */
+VRDEENTRYPOINTS_4 *ConsoleVRDPServer::mpEntryPoints = NULL;
+
+VRDECALLBACKS_4 ConsoleVRDPServer::mCallbacks =
+{
+    { VRDE_INTERFACE_VERSION_4, sizeof(VRDECALLBACKS_4) },
     ConsoleVRDPServer::VRDPCallbackQueryProperty,
     ConsoleVRDPServer::VRDPCallbackClientLogon,
@@ -1315,4 +1317,11 @@
 
     mu32AudioInputClientId = 0;
+
+    /*
+     * Optional interfaces.
+     */
+    m_fInterfaceImage = false;
+    memset(&m_interfaceImage, 0, sizeof (m_interfaceImage));
+    memset(&m_interfaceCallbacksImage, 0, sizeof (m_interfaceCallbacksImage));
 }
 
@@ -1395,21 +1404,21 @@
         if (RT_SUCCESS(vrc))
         {
-            VRDEENTRYPOINTS_3 *pEntryPoints3;
-            vrc = mpfnVRDECreateServer(&mCallbacks.header, this, (VRDEINTERFACEHDR **)&pEntryPoints3, &mhServer);
+            VRDEENTRYPOINTS_4 *pEntryPoints4;
+            vrc = mpfnVRDECreateServer(&mCallbacks.header, this, (VRDEINTERFACEHDR **)&pEntryPoints4, &mhServer);
 
             if (RT_SUCCESS(vrc))
             {
-                mServerInterfaceVersion = 3;
-                mEntryPoints = *pEntryPoints3;
+                mServerInterfaceVersion = 4;
+                mEntryPoints = *pEntryPoints4;
                 mpEntryPoints = &mEntryPoints;
             }
             else if (vrc == VERR_VERSION_MISMATCH)
             {
-                /* An older version of VRDE is installed, try version 1. */
-                VRDEENTRYPOINTS_1 *pEntryPoints1;
-
-                static VRDECALLBACKS_1 sCallbacks =
-                {
-                    { VRDE_INTERFACE_VERSION_1, sizeof(VRDECALLBACKS_1) },
+                /* An older version of VRDE is installed, try version 3. */
+                VRDEENTRYPOINTS_3 *pEntryPoints3;
+
+                static VRDECALLBACKS_3 sCallbacks3 =
+                {
+                    { VRDE_INTERFACE_VERSION_3, sizeof(VRDECALLBACKS_3) },
                     ConsoleVRDPServer::VRDPCallbackQueryProperty,
                     ConsoleVRDPServer::VRDPCallbackClientLogon,
@@ -1423,35 +1432,109 @@
                     ConsoleVRDPServer::VRDPCallbackFramebufferUnlock,
                     ConsoleVRDPServer::VRDPCallbackInput,
-                    ConsoleVRDPServer::VRDPCallbackVideoModeHint
+                    ConsoleVRDPServer::VRDPCallbackVideoModeHint,
+                    ConsoleVRDPServer::VRDECallbackAudioIn
                 };
 
-                vrc = mpfnVRDECreateServer(&sCallbacks.header, this, (VRDEINTERFACEHDR **)&pEntryPoints1, &mhServer);
+                vrc = mpfnVRDECreateServer(&sCallbacks3.header, this, (VRDEINTERFACEHDR **)&pEntryPoints3, &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;
+                    mEntryPoints.header = pEntryPoints3->header;
+                    mEntryPoints.VRDEDestroy = pEntryPoints3->VRDEDestroy;
+                    mEntryPoints.VRDEEnableConnections = pEntryPoints3->VRDEEnableConnections;
+                    mEntryPoints.VRDEDisconnect = pEntryPoints3->VRDEDisconnect;
+                    mEntryPoints.VRDEResize = pEntryPoints3->VRDEResize;
+                    mEntryPoints.VRDEUpdate = pEntryPoints3->VRDEUpdate;
+                    mEntryPoints.VRDEColorPointer = pEntryPoints3->VRDEColorPointer;
+                    mEntryPoints.VRDEHidePointer = pEntryPoints3->VRDEHidePointer;
+                    mEntryPoints.VRDEAudioSamples = pEntryPoints3->VRDEAudioSamples;
+                    mEntryPoints.VRDEAudioVolume = pEntryPoints3->VRDEAudioVolume;
+                    mEntryPoints.VRDEUSBRequest = pEntryPoints3->VRDEUSBRequest;
+                    mEntryPoints.VRDEClipboard = pEntryPoints3->VRDEClipboard;
+                    mEntryPoints.VRDEQueryInfo = pEntryPoints3->VRDEQueryInfo;
+                    mEntryPoints.VRDERedirect = pEntryPoints3->VRDERedirect;
+                    mEntryPoints.VRDEAudioInOpen = pEntryPoints3->VRDEAudioInOpen;
+                    mEntryPoints.VRDEAudioInClose = pEntryPoints3->VRDEAudioInClose;
+                    mEntryPoints.VRDEGetInterface = NULL;
                     mpEntryPoints = &mEntryPoints;
                 }
+                else if (vrc == VERR_VERSION_MISMATCH)
+                {
+                    /* An older version of VRDE is installed, try version 1. */
+                    VRDEENTRYPOINTS_1 *pEntryPoints1;
+
+                    static VRDECALLBACKS_1 sCallbacks1 =
+                    {
+                        { 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(&sCallbacks1.header, this, (VRDEINTERFACEHDR **)&pEntryPoints1, &mhServer);
+                    if (RT_SUCCESS(vrc))
+                    {
+                        mServerInterfaceVersion = 1;
+                        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;
+                        mEntryPoints.VRDEGetInterface = NULL;
+                        mpEntryPoints = &mEntryPoints;
+                    }
+                }
             }
 
             if (RT_SUCCESS(vrc))
             {
+                LogRel(("VRDE: loaded version %d of the server.\n", mServerInterfaceVersion));
+
+                if (mServerInterfaceVersion >= 4)
+                {
+                    /* The server supports optional interfaces. */
+                    Assert(mpEntryPoints->VRDEGetInterface != NULL);
+
+                    /* Image interface. */
+                    m_interfaceImage.header.u64Version = 1;
+                    m_interfaceImage.header.u64Size = sizeof(m_interfaceImage);
+
+                    m_interfaceCallbacksImage.header.u64Version = 1;
+                    m_interfaceCallbacksImage.header.u64Size = sizeof(m_interfaceCallbacksImage);
+                    m_interfaceCallbacksImage.VRDEImageCbNotify = VRDEImageCbNotify;
+
+                    vrc = mpEntryPoints->VRDEGetInterface(mhServer,
+                                                          VRDE_IMAGE_INTERFACE_NAME,
+                                                          &m_interfaceImage.header,
+                                                          &m_interfaceCallbacksImage.header,
+                                                          this);
+                    if (RT_SUCCESS(vrc))
+                    {
+                        m_fInterfaceImage = true;
+                    }
+
+                    /* Since these interfaces are optional, it is always a success here. */
+                    vrc = VINF_SUCCESS;
+                }
 #ifdef VBOX_WITH_USB
                 remoteUSBThreadStart();
@@ -1471,4 +1554,338 @@
 }
 
+typedef struct H3DORInstance
+{
+    ConsoleVRDPServer *pThis;
+    HVRDEIMAGE hImageBitmap;
+    int32_t x;
+    int32_t y;
+    uint32_t w;
+    uint32_t h;
+    bool fCreated;
+} H3DORInstance;
+
+/* static */ DECLCALLBACK(void) ConsoleVRDPServer::H3DORBegin(const void *pvContext, void **ppvInstance,
+                                                              const char *pszFormat)
+{
+    LogFlowFunc(("ctx %p\n", pvContext));
+
+    H3DORInstance *p = (H3DORInstance *)RTMemAlloc(sizeof (H3DORInstance));
+
+    if (p)
+    {
+        p->pThis = (ConsoleVRDPServer *)pvContext;
+        p->hImageBitmap = NULL;
+        p->x = 0;
+        p->y = 0;
+        p->w = 0;
+        p->h = 0;
+        p->fCreated = false;
+
+        /* Host 3D service passes the actual format of data in this redirect instance.
+         * That is what will be in the H3DORFrame's parameters pvData and cbData.
+         */
+        if (RTStrICmp(pszFormat, H3DOR_FMT_RGBA_TOPDOWN) == 0)
+        {
+            /* Accept it. */
+        }
+        else
+        {
+            RTMemFree(p);
+            p = NULL;
+        }
+    }
+
+    /* Caller check this for NULL. */
+    *ppvInstance = p;
+}
+
+/* static */ DECLCALLBACK(void) ConsoleVRDPServer::H3DORGeometry(void *pvInstance,
+                                                                 int32_t x, int32_t y, uint32_t w, uint32_t h)
+{
+    LogFlowFunc(("ins %p %d,%d %dx%d\n", pvInstance, x, y, w, h));
+
+    H3DORInstance *p = (H3DORInstance *)pvInstance;
+    Assert(p);
+    Assert(p->pThis);
+
+    /* @todo find out what to do if size changes to 0x0 from non zero */
+    if (w == 0 || h == 0)
+    {
+        /* Do nothing. */
+        return;
+    }
+
+    RTRECT rect;
+    rect.xLeft = x;
+    rect.yTop = y;
+    rect.xRight = x + w;
+    rect.yBottom = y + h;
+
+    if (p->hImageBitmap)
+    {
+        /* An image handle has been already created,
+         * check if it has the same size as the reported geometry.
+         */
+        if (   p->x == x
+            && p->y == y
+            && p->w == w
+            && p->h == h)
+        {
+            LogFlowFunc(("geometry not changed\n"));
+            /* Do nothing. Continue using the existing handle. */
+        }
+        else
+        {
+            int rc = p->pThis->m_interfaceImage.VRDEImageGeometrySet(p->hImageBitmap, &rect);
+            if (RT_SUCCESS(rc))
+            {
+                p->x = x;
+                p->y = y;
+                p->w = w;
+                p->h = h;
+            }
+            else
+            {
+                /* The handle must be recreated. Delete existing handle here. */
+                p->pThis->m_interfaceImage.VRDEImageHandleClose(p->hImageBitmap);
+                p->hImageBitmap = NULL;
+            }
+        }
+    }
+
+    if (!p->hImageBitmap)
+    {
+        /* Create a new bitmap handle. */
+        uint32_t u32ScreenId = 0; /* @todo clip to corresponding screens.
+                                   * Clipping can be done here or in VRDP server.
+                                   * If VRDP does clipping, then uScreenId parameter
+                                   * is not necessary and coords must be global.
+                                   * (have to check which coords are used in opengl service).
+                                   * Since all VRDE API uses a ScreenId,
+                                   * the clipping must be done here in ConsoleVRDPServer
+                                   */
+        uint32_t fu32CompletionFlags = 0;
+        int rc = p->pThis->m_interfaceImage.VRDEImageHandleCreate(p->pThis->mhServer,
+                                                                  &p->hImageBitmap,
+                                                                  p,
+                                                                  u32ScreenId,
+                                                                  VRDE_IMAGE_F_CREATE_CONTENT_3D
+                                                                  | VRDE_IMAGE_F_CREATE_WINDOW,
+                                                                  &rect,
+                                                                  VRDE_IMAGE_FMT_ID_BITMAP_BGRA8,
+                                                                  NULL,
+                                                                  0,
+                                                                  &fu32CompletionFlags);
+        if (RT_SUCCESS(rc))
+        {
+            p->x = x;
+            p->y = y;
+            p->w = w;
+            p->h = h;
+
+            if ((fu32CompletionFlags & VRDE_IMAGE_F_COMPLETE_ASYNC) == 0)
+            {
+                p->fCreated = true;
+            }
+        }
+        else
+        {
+            p->hImageBitmap = NULL;
+            p->w = 0;
+            p->h = 0;
+        }
+    }
+}
+
+/* static */ DECLCALLBACK(void) ConsoleVRDPServer::H3DORVisibleRegion(void *pvInstance,
+                                                                      uint32_t cRects, RTRECT *paRects)
+{
+    LogFlowFunc(("ins %p %d\n", pvInstance, cRects));
+
+    H3DORInstance *p = (H3DORInstance *)pvInstance;
+    Assert(p);
+    Assert(p->pThis);
+
+    if (cRects == 0)
+    {
+        /* Complete image is visible. */
+        RTRECT rect;
+        rect.xLeft = p->x;
+        rect.yTop = p->y;
+        rect.xRight = p->x + p->w;
+        rect.yBottom = p->y + p->h;
+        p->pThis->m_interfaceImage.VRDEImageRegionSet (p->hImageBitmap,
+                                                       1,
+                                                       &rect);
+    }
+    else
+    {
+        p->pThis->m_interfaceImage.VRDEImageRegionSet (p->hImageBitmap,
+                                                       cRects,
+                                                       paRects);
+    }
+}
+
+/* static */ DECLCALLBACK(void) ConsoleVRDPServer::H3DORFrame(void *pvInstance,
+                                                              void *pvData, uint32_t cbData)
+{
+    LogFlowFunc(("ins %p %p %d\n", pvInstance, pvData, cbData));
+
+    H3DORInstance *p = (H3DORInstance *)pvInstance;
+    Assert(p);
+    Assert(p->pThis);
+
+    /* Currently only a topdown BGR0 bitmap format is supported. */
+    VRDEIMAGEBITMAP image;
+
+    image.cWidth = p->w;
+    image.cHeight = p->h;
+    image.pvData = pvData;
+    image.cbData = cbData;
+    image.pvScanLine0 = (uint8_t *)pvData + (p->h - 1) * p->w * 4;
+    image.iScanDelta = -4 * p->w;
+
+    p->pThis->m_interfaceImage.VRDEImageUpdate (p->hImageBitmap,
+                                                p->x,
+                                                p->y,
+                                                p->w,
+                                                p->h,
+                                                &image,
+                                                sizeof(VRDEIMAGEBITMAP));
+}
+
+/* static */ DECLCALLBACK(void) ConsoleVRDPServer::H3DOREnd(void *pvInstance)
+{
+    LogFlowFunc(("ins %p\n", pvInstance));
+
+    H3DORInstance *p = (H3DORInstance *)pvInstance;
+    Assert(p);
+    Assert(p->pThis);
+
+    p->pThis->m_interfaceImage.VRDEImageHandleClose(p->hImageBitmap);
+
+    RTMemFree(p);
+}
+
+/* static */ DECLCALLBACK(int) ConsoleVRDPServer::H3DORContextProperty(const void *pvContext, uint32_t index,
+                                                                       void *pvBuffer, uint32_t cbBuffer, uint32_t *pcbOut)
+{
+    int rc = VINF_SUCCESS;
+
+    if (index == H3DOR_PROP_FORMATS)
+    {
+        /* Return a comma separated list of supported formats. */
+        static char *pszSupportedFormats = H3DOR_FMT_RGBA_TOPDOWN;
+        uint32_t cbOut = (uint32_t)strlen(pszSupportedFormats) + 1;
+        if (cbOut <= cbBuffer)
+        {
+            memcpy(pvBuffer, pszSupportedFormats, cbOut);
+        }
+        else
+        {
+            rc = VERR_BUFFER_OVERFLOW;
+        }
+        *pcbOut = cbOut;
+    }
+    else
+    {
+        rc = VERR_NOT_SUPPORTED;
+    }
+
+    return rc;
+}
+
+void ConsoleVRDPServer::remote3DRedirect(void)
+{
+    if (!m_fInterfaceImage)
+    {
+        /* No redirect without corresponding interface. */
+        return;
+    }
+
+    /* Check if 3D redirection has been enabled. */
+    com::Bstr bstr;
+    HRESULT hrc = mConsole->getVRDEServer()->GetVRDEProperty(Bstr("H3DRedirect/Enabled").raw(), bstr.asOutParam());
+
+    if (hrc != S_OK)
+    {
+        bstr = "";
+    }
+
+    com::Utf8Str value = bstr;
+
+    bool fEnabled =    RTStrICmp(value.c_str(), "true") == 0
+                    || RTStrICmp(value.c_str(), "1") == 0;
+
+    if (!fEnabled)
+    {
+        return;
+    }
+
+    /* Tell the host 3D service to redirect output using the ConsoleVRDPServer callbacks. */
+    H3DOUTPUTREDIRECT outputRedirect =
+    {
+        this,
+        H3DORBegin,
+        H3DORGeometry,
+        H3DORVisibleRegion,
+        H3DORFrame,
+        H3DOREnd,
+        H3DORContextProperty
+    };
+
+    VBOXHGCMSVCPARM parm;
+
+    parm.type = VBOX_HGCM_SVC_PARM_PTR;
+    parm.u.pointer.addr = &outputRedirect;
+    parm.u.pointer.size = sizeof(outputRedirect);
+
+    VMMDev *pVMMDev = mConsole->getVMMDev();
+
+    if (!pVMMDev)
+    {
+        AssertMsgFailed(("remote3DRedirect no vmmdev\n"));
+        return;
+    }
+
+    int rc = pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL",
+                                   SHCRGL_HOST_FN_SET_OUTPUT_REDIRECT,
+                                   SHCRGL_CPARMS_SET_OUTPUT_REDIRECT,
+                                   &parm);
+
+    if (!RT_SUCCESS(rc))
+    {
+        AssertMsgFailed(("SHCRGL_HOST_FN_SET_CONSOLE failed with %Rrc\n", rc));
+        return;
+    }
+
+    LogRel(("VRDE: Enabled 3D redirect.\n"));
+
+    return;
+}
+
+/* static */ DECLCALLBACK(int) ConsoleVRDPServer::VRDEImageCbNotify (void *pvContext,
+                                                                     void *pvUser,
+                                                                     HVRDEIMAGE hVideo,
+                                                                     uint32_t u32Id,
+                                                                     void *pvData,
+                                                                     uint32_t cbData)
+{
+    LogFlowFunc(("pvContext %p, pvUser %p, hVideo %p, u32Id %u, pvData %p, cbData %d\n",
+                 pvContext, pvUser, hVideo, u32Id, pvData, cbData));
+
+    ConsoleVRDPServer *pServer = static_cast<ConsoleVRDPServer*>(pvContext);
+    H3DORInstance *p = (H3DORInstance *)pvUser;
+    Assert(p);
+    Assert(p->pThis);
+    Assert(p->pThis == pServer);
+
+    // @todo Process u32Id
+
+    p->fCreated = true;
+
+    return VINF_SUCCESS;
+}
+
 void ConsoleVRDPServer::EnableConnections(void)
 {
@@ -1476,4 +1893,7 @@
     {
         mpEntryPoints->VRDEEnableConnections(mhServer, true);
+
+        /* Redirect 3D output if it is enabled. */
+        remote3DRedirect();
     }
 }
