Quantcast
Channel: VBForums - CodeBank - Visual Basic 6 and earlier
Viewing all articles
Browse latest Browse all 1449

[VB6] DirectX 11 Desktop Duplication

$
0
0
This is a work in progress of a remote control utility. This is the screen capturing part using DirectX 11 (DXGI).

Code:

Option Explicit

'--- DIB Section constants
Private Const DIB_RGB_COLORS                As Long = 0 '  color table in RGBs

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDst As Any, lpSrc As Any, ByVal ByteLength As Long)
Private Declare Function CreateDIBSection Lib "gdi32" (ByVal hDC As Long, lpBitsInfo As BITMAPINFOHEADER, ByVal wUsage As Long, lpBits As Long, ByVal Handle As Long, ByVal dw As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hDC As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hDC As Long) As Long
Private Declare Function OleCreatePictureIndirect Lib "oleaut32" (lpPictDesc As PICTDESC, riid As Any, ByVal fPictureOwnsHandle As Long, ipic As IPicture) As Long

Private Type BITMAPINFOHEADER
    biSize              As Long
    biWidth            As Long
    biHeight            As Long
    biPlanes            As Integer
    biBitCount          As Integer
    biCompression      As Long
    biSizeImage        As Long
    biXPelsPerMeter    As Long
    biYPelsPerMeter    As Long
    biClrUsed          As Long
    biClrImportant      As Long
End Type

Private Type PICTDESC
    lSize              As Long
    lType              As Long
    hBmp                As Long
    hPal                As Long
End Type

Private Type UcsDuplicationContext
    DeviceName          As String
    Width              As Long
    Height              As Long
    pContext            As ID3D11DeviceContext
    pStageTexture      As ID3D11Texture2D
    pDuplication        As IDXGIOutputDuplication
    pTexture            As ID3D11Texture2D
End Type

Private m_uCtx                  As UcsDuplicationContext

Private Sub PrintError(sFuncName As String)
    Debug.Print Err.Description & " in " & sFuncName
    If MsgBox(Err.Description, vbCritical Or vbOKCancel, sFuncName) = vbCancel Then
        Unload Me
    End If
End Sub

Private Function pvEnumOutputDeviceNames() As Collection
    Dim aGUID(0 To 3)  As Long
    Dim pFactory        As IDXGIFactory1
    Dim lIdx            As Long
    Dim lJdx            As Long
    Dim pAdapter        As IDXGIAdapter1
    Dim pOutput        As IDXGIOutput1
    Dim uAdapterDesc    As DXGI_ADAPTER_DESC
    Dim uOutputDesc    As DXGI_OUTPUT_DESC
   
    Set pvEnumOutputDeviceNames = New Collection
    Call IIDFromString(szIID_DXGIFactory1, aGUID(0))
    Set pFactory = CreateDXGIFactory1(aGUID(0))
    For lIdx = 0 To 100
        Set pAdapter = Nothing
        If pFactory.EnumAdapters1(lIdx, pAdapter) <> 0 Then
            Exit For
        End If
        pAdapter.GetDesc uAdapterDesc
        Debug.Print Replace(uAdapterDesc.Description, vbNullChar, vbNullString)
        For lJdx = 0 To 100
            Set pOutput = Nothing
            If pAdapter.EnumOutputs(lJdx, pOutput) <> 0 Then
                Exit For
            End If
            pOutput.GetDesc uOutputDesc
            pvEnumOutputDeviceNames.Add Array(Replace(uOutputDesc.DeviceName, vbNullChar, vbNullString), _
                Replace(uAdapterDesc.Description, vbNullChar, vbNullString))
        Next
    Next
End Function

Private Function pvInitCapture(uCtx As UcsDuplicationContext, sDeviceName As String) As Boolean
    Const FUNC_NAME    As String = "pvInitCapture"
    Dim aGUID(0 To 3)  As Long
    Dim pFactory        As IDXGIFactory1
    Dim lIdx            As Long
    Dim lJdx            As Long
    Dim pAdapter        As IDXGIAdapter1
    Dim pOutput        As IDXGIOutput1
    Dim uOutputDesc    As DXGI_OUTPUT_DESC
    Dim hResult        As Long
    Dim pDevice        As ID3D11Device
    Dim uTextureDesc    As D3D11_TEXTURE2D_DESC
   
    On Error GoTo EH
    With uCtx
        .DeviceName = vbNullString
        Set .pTexture = Nothing
        Set .pDuplication = Nothing
        Set .pStageTexture = Nothing
        Set .pContext = Nothing
        Call IIDFromString(szIID_DXGIFactory1, aGUID(0))
        Set pFactory = CreateDXGIFactory1(aGUID(0))
        For lIdx = 0 To 100
            Set pAdapter = Nothing
            If pFactory.EnumAdapters1(lIdx, pAdapter) <> 0 Then
                Exit For
            End If
            For lJdx = 0 To 100
                Set pOutput = Nothing
                If pAdapter.EnumOutputs(lJdx, pOutput) <> 0 Then
                    Exit For
                End If
                pOutput.GetDesc uOutputDesc
                If Replace(uOutputDesc.DeviceName, vbNullChar, vbNullString) Like sDeviceName Or LenB(sDeviceName) = 0 Then
                    lIdx = 100
                    Exit For
                End If
            Next
        Next
        If pOutput Is Nothing Then
            GoTo QH
        End If
        .DeviceName = Replace(uOutputDesc.DeviceName, vbNullChar, vbNullString)
        .Width = uOutputDesc.DesktopCoordinates.Right - uOutputDesc.DesktopCoordinates.Left
        .Height = uOutputDesc.DesktopCoordinates.Bottom - uOutputDesc.DesktopCoordinates.Top
        hResult = D3D11CreateDevice(pAdapter, D3D_DRIVER_TYPE_UNKNOWN, 0, 0, ByVal 0, 0, D3D11_SDK_VERSION, pDevice, 0, .pContext)
        If hResult < 0 Then
            Err.Raise hResult
        End If
        With uTextureDesc
            .Width = uCtx.Width
            .Height = uCtx.Height
            .MipLevels = 1
            .ArraySize = 1
            .Format = DXGI_FORMAT_B8G8R8A8_UNORM
            .SampleDesc.Count = 1
            .SampleDesc.Quality = 0
            .Usage = D3D11_USAGE_STAGING
            .BindFlags = 0
            .CPUAccessFlags = D3D11_CPU_ACCESS_READ
            .MiscFlags = 0
        End With
        Set .pStageTexture = pDevice.CreateTexture2D(uTextureDesc)
        Set .pDuplication = pOutput.DuplicateOutput(pDevice)
    End With
    '--- success
    pvInitCapture = True
QH:
    Exit Function
EH:
    PrintError FUNC_NAME
End Function

Private Function pvCaptureScreen(uCtx As UcsDuplicationContext, oPic As StdPicture) As Boolean
    Const FUNC_NAME    As String = "pvCaptureScreen"
    Dim hResult        As Long
    Dim aGUID(0 To 3)  As Long
    Dim lIdx            As Long
    Dim uFrameInfo      As DXGI_OUTDUPL_FRAME_INFO
    Dim uResource      As D3D11_MAPPED_SUBRESOURCE
    Dim hMemDC          As Long
    Dim hDib            As Long
    Dim lpBits          As Long
    Dim uDesc          As PICTDESC
    Dim uDuplDesc      As DXGI_OUTDUPL_DESC
    Dim uMapRect        As DXGI_MAPPED_RECT
   
    On Error GoTo EH
    With uCtx
        If .pDuplication Is Nothing Then
            GoTo QH
        End If
        If Not .pTexture Is Nothing Then
            .pDuplication.ReleaseFrame
            Set .pTexture = Nothing
        End If
        hResult = .pDuplication.AcquireNextFrame(100, uFrameInfo, .pTexture)
        If hResult < 0 Then
            GoTo QH
        End If
        hMemDC = CreateCompatibleDC(0)
        If hMemDC = 0 Then
            GoTo QH
        End If
        If Not pvCreateDib(hMemDC, .Width, .Height, hDib, lpBits) Then
            GoTo QH
        End If
        .pDuplication.GetDesc uDuplDesc
        If uDuplDesc.DesktopImageInSystemMemory <> 0 Then
            .pDuplication.MapDesktopSurface uMapRect
            For lIdx = 0 To .Height - 1
                Call CopyMemory(ByVal lpBits + lIdx * .Width * 4, ByVal uMapRect.pBitsPtr + lIdx * uMapRect.Pitch, .Width * 4)
            Next
        Else
            .pContext.CopyResource .pStageTexture, .pTexture
            .pContext.Map .pStageTexture, 0, D3D11_MAP_READ, 0, uResource
            For lIdx = 0 To .Height - 1
                Call CopyMemory(ByVal lpBits + lIdx * .Width * 4, ByVal uResource.pDataPtr + lIdx * uResource.RowPitch, .Width * 4)
            Next
        End If
    End With
    With uDesc
        .lSize = Len(uDesc)
        .lType = vbPicTypeBitmap
        .hBmp = hDib
    End With
    '--- IID_IPicture
    aGUID(0) = &H7BF80980
    aGUID(1) = &H101ABF32
    aGUID(2) = &HAA00BB8B
    aGUID(3) = &HAB0C3000
    If OleCreatePictureIndirect(uDesc, aGUID(0), 1, oPic) <> 0 Then
        GoTo QH
    End If
    hDib = 0
    '--- success
    pvCaptureScreen = True
QH:
    If hDib <> 0 Then
        Call DeleteObject(hDib)
        hDib = hDib
    End If
    If hMemDC <> 0 Then
        Call DeleteDC(hMemDC)
        hMemDC = 0
    End If
    If uResource.pDataPtr <> 0 Then
        uCtx.pContext.Unmap uCtx.pStageTexture, 0
    End If
    Exit Function
EH:
    PrintError FUNC_NAME
    Resume QH
End Function

Private Function pvCreateDib(ByVal hMemDC As Long, ByVal lWidth As Long, ByVal lHeight As Long, hDib As Long, Optional lpBits As Long) As Boolean
    Const FUNC_NAME    As String = "pvCreateDib"
    Dim uHdr            As BITMAPINFOHEADER
   
    On Error GoTo EH
    With uHdr
        .biSize = Len(uHdr)
        .biPlanes = 1
        .biBitCount = 32
        .biWidth = lWidth
        .biHeight = -lHeight
        .biSizeImage = 4 * lWidth * lHeight
    End With
    hDib = CreateDIBSection(hMemDC, uHdr, DIB_RGB_COLORS, lpBits, 0, 0)
    If hDib = 0 Then
        GoTo QH
    End If
    '--- success
    pvCreateDib = True
QH:
    Exit Function
EH:
    PrintError FUNC_NAME
    Resume QH
End Function

Private Sub Form_Load()
    Dim vElem          As Variant
   
    For Each vElem In pvEnumOutputDeviceNames
        Combo1.AddItem vElem(0)
    Next
    Combo1.ListIndex = 0
End Sub

Private Sub Form_Resize()
    Dim dblTop          As Double
   
    If WindowState <> vbMinimized Then
        dblTop = Combo1.Top + Combo1.Height + Combo1.Top
        Image1.Move 0, dblTop, ScaleWidth, ScaleHeight - dblTop
    End If
End Sub

Private Sub Combo1_Click()
    If Combo1.ListIndex >= 0 Then
        If Not pvInitCapture(m_uCtx, Combo1.Text) Then
            Timer1.Enabled = False
        Else
            Timer1.Enabled = True
            Timer1_Timer
        End If
    End If
End Sub

Private Sub Image1_Click()
    Timer1.Enabled = Not Timer1.Enabled
End Sub

Private Sub Timer1_Timer()
    Dim oPic            As StdPicture
   
    If pvCaptureScreen(m_uCtx, oPic) Then
        Set Image1.Picture = oPic
    ElseIf Not pvInitCapture(m_uCtx, m_uCtx.DeviceName) Then
        Timer1.Enabled = False
    End If
End Sub

There is a custom DirectX 11 type library (both .idl and .tlb in the archive) with just enough interfaces to instantiate IDXGIOutputDuplication and capture a texture which is then converted to a DIB which is then converted to a StdPicture and placed in a stretching Image control so the scale quality is poor.

The idea is for a remote screen sharing implementation to transport only screen diffs using GetFrameDirtyRects, GetFrameMoveRects and GetFramePointerShape methods (instead of current full screen capture) with some fast LZ4 compression on top and some Foreward Error Correction implementation over UDP, including UDP hole punching for serverless peer-to-peer connections when both parties happen to be behind NAT or alternative is using STUN/TURN infrastructure as currently provided by google for WebRTC.

cheers,
</wqw>
Attached Files

Viewing all articles
Browse latest Browse all 1449

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>