Как я могу отображать изображения на мониторе (или телевизоре), подключенном к порту HDMI моего компьютера

Я новичок в программировании DirectX 10, и я пытался сделать следующее с моими ограниченными навыками.

Я пытаюсь отобразить изображение с одного компьютера на другое устройство вывода (другой монитор/телевизор), подключенное через HDMI. Я исследовал это и узнал, что DXGI может быть очень полезен для целей рендеринга. Более того, при дальнейшем исследовании я наткнулся на эту ссылку здесь . Он показывает, как отображать разные изображения на нескольких мониторах, подключенных к основному компьютеру, но для этого требуется расширенный дисплей.

Мое требование состоит в том, что когда я запускаю код, он должен отображать изображение на другом устройстве вывода, не превращая его в расширенный дисплей.

Подход, который я пытаюсь, заключается в том, что я перечисляю видеоадаптеры (в моем случае это 1), а затем перечисляю доступные выходы, которых должно быть два, поскольку у меня есть выход HDMI, подключенный к моему ПК. Но если я не устанавливаю расширенный дисплей, он показывает только один вывод, доступный в моем массиве перечисляемых выходов, и если я расширяю дисплей, он показывает два из них. Как только я закончу с перечислением выходов, я хочу визуализировать это изображение на желаемом выходе.

Насколько я знаю, к каждому видеоадаптеру подключен порт HDMI.

Интересно, есть ли способ программно получить доступ к этому порту и отобразить изображение, используя этот порт?

Помимо MSDN, документация или объяснение этих концепций довольно ограничены, поэтому любая помощь будет очень кстати. Вот какой код у меня есть:

// include the basic windows header files and the Direct3D header files
#include <windows.h>
#include <windowsx.h>
#include <d3d11.h>
#include <d3dx11.h>
#include <d3dx10.h>
#include<vector>
#include<iostream>
#include <sstream>
#include<dxgi.h>
using namespace std;
// include the Direct3D Library file
#pragma comment (lib, "d3d11.lib")
#pragma comment (lib, "d3dx11.lib")
#pragma comment (lib, "d3dx10.lib")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dxgi.lib")
std::vector<IDXGIAdapter*> EnumerateAdapters();

void PrintAdapterInfo(IDXGIAdapter* pAdapter);

std::vector<IDXGIOutput*> EnumerateOutputs(IDXGIAdapter* pAdapter);
void PrintOutputInfo(IDXGIOutput* output);

std::vector<DXGI_MODE_DESC*> GetDisplayModeList(IDXGIOutput* output);

void PrintDisplayModeInfo(DXGI_MODE_DESC* pModeDesc);
// global declarations
IDXGISwapChain *swapchain;             // the pointer to the swap chain interface
ID3D11Device *dev;                     // the pointer to our Direct3D device interface
ID3D11DeviceContext *devcon;           // the pointer to our Direct3D device context
ID3D11RenderTargetView *backbuffer;    // the pointer to our back buffer
IDXGIAdapter *pAdapter;
IDXGIAdapter* adapter = NULL;
IDXGIFactory1* factory = NULL;
HINSTANCE hInstance;
HINSTANCE hPrevInstance;
LPSTR lpCmdLine;
HWND hWnd;

 std::vector<IDXGIOutput*> outputArray;

int nCmdShow;
// function prototypes
void InitD3D(HWND hWnd);    // sets up and initializes Direct3D
void RenderFrame(void);     // renders a single frame
void CleanD3D(void);        // closes Direct3D and releases memory
void CreateWindowsForOutputs();
void GetAdapter();
void MultiRender();
void CreateSwapChainsAndViews();
std::vector<IDXGIAdapter*> EnumerateAdapters();

// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

struct WindowDataContainer
{
    //Direct3D 10 stuff per window data
    IDXGISwapChain* swapChain;
    ID3D11RenderTargetView* renderTargetView;
    ID3D11DepthStencilView* depthStencilView;

    // window goodies
    HWND hWnd;
    int width;
    int height;
};
std::vector<WindowDataContainer*> WindowsArray;
// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)


{
     std::cout << "Hello \nworld!"; // Will show a popup
    std::vector<IDXGIAdapter*> adapters = EnumerateAdapters();
    for(std::vector<IDXGIAdapter*>::iterator itor = adapters.begin(); itor != adapters.end(); ++itor)
    {
        //PrintAdapterInfo(*itor);

        // Get Output info
        std::vector<IDXGIOutput*> outputArray = EnumerateOutputs(*itor);
        for(std::vector<IDXGIOutput*>::iterator outputItor = outputArray.begin(); outputItor != outputArray.end(); ++outputItor)
        {
        //  PrintOutputInfo(*outputItor);

            // Get display mode list
            std::vector<DXGI_MODE_DESC*> modeList = GetDisplayModeList(*outputItor);
            for(std::vector<DXGI_MODE_DESC*>::iterator modeItor = modeList.begin(); modeItor != modeList.end(); ++modeItor)
            {
            //  PrintDisplayModeInfo(*modeItor);
            }
        }
CreateWindowsForOutputs();
MSG msg;

    while(TRUE)
    {
        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);

            if(msg.message == WM_QUIT)
                break;
        }

     MultiRender();
    }

//std::getchar();
     //   return 0;
    }

//std:getchar();
}
std::vector<IDXGIAdapter*> EnumerateAdapters()
{
    // Create DXGI factory
    IDXGIFactory1* pFactory = NULL;
    HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)(&pFactory));
    if (FAILED(hr))
    {
        MessageBox(NULL, L"Create DXGI factory failed", L"Error", 0);
    }

    // Enumerate devices
    IDXGIAdapter* pAdapter = NULL;
    std::vector<IDXGIAdapter*> vAdapters;

    for (UINT i = 0; pFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND; ++i)
    {
        vAdapters.push_back(pAdapter);
    }

    if (pFactory)
    {
        pFactory->Release();
        pFactory = NULL;
    }

    return vAdapters;
}

// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
    case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        } break;
    }

    return DefWindowProc (hWnd, message, wParam, lParam);
}



// this function initializes and prepares Direct3D for use
void InitD3D(HWND hWnd)
{

    // create a struct to hold information about the swap chain
    DXGI_SWAP_CHAIN_DESC scd;

    // clear out the struct for use
    ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));

    // fill the swap chain description struct
    scd.BufferCount = 1;                                    // one back buffer
    scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;     // use 32-bit color
    scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;      // how swap chain is to be used
    scd.OutputWindow = hWnd;                                // the window to be used
    scd.SampleDesc.Count = 1;                               // how many multisamples
    scd.SampleDesc.Quality = 0;                             // multisample quality level
    scd.Windowed = TRUE;                                    // windowed/full-screen mode

    // create a device, device context and swap chain using the information in the scd struct
    D3D11CreateDeviceAndSwapChain(   NULL,
        D3D_DRIVER_TYPE_HARDWARE,
        NULL,
        NULL,
        NULL,
        NULL,
        D3D11_SDK_VERSION,
        &scd,
        &swapchain,
        &dev,
        NULL,
        &devcon);


    // get the address of the back buffer
    ID3D11Texture2D *pBackBuffer;
    swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);

    // use the back buffer address to create the render target
    dev->CreateRenderTargetView(pBackBuffer, NULL, &backbuffer);
    pBackBuffer->Release();

    // set the render target as the back buffer
    devcon->OMSetRenderTargets(1, &backbuffer, NULL);


    // Set the viewport
    D3D11_VIEWPORT viewport;
    ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));

    viewport.TopLeftX = 0;
    viewport.TopLeftY = 0;
    viewport.Width = 800;
    viewport.Height = 600;

    devcon->RSSetViewports(1, &viewport);
}


// this is the function used to render a single frame
void RenderFrame(void)
{
    // clear the back buffer to a deep blue
    devcon->ClearRenderTargetView(backbuffer, D3DXCOLOR(0.0f, 0.2f, 0.4f, 1.0f));

    // do 3D rendering on the back buffer here

    // switch the back buffer and the front buffer
    swapchain->Present(0, 0);
}


// this is the function that cleans up Direct3D and COM
void CleanD3D(void)
{
    // close and release all existing COM objects
    swapchain->Release();
    backbuffer->Release();
    dev->Release();
    devcon->Release();
}

//Acquiring the outputs on our adapter
std::vector<IDXGIOutput*> EnumerateOutputs(IDXGIAdapter* pAdapter)
{
    //std::vector<IDXGIOutput*> outputs;
    IDXGIOutput* pOutput = NULL;
    for (UINT i = 0; pAdapter->EnumOutputs(i, &pOutput) != DXGI_ERROR_NOT_FOUND; ++i)
    {
        outputArray.push_back(pOutput);
    }

    return outputArray;
}





void CreateWindowsForOutputs()
{ //  std::vector<IDXGIOutput*> outputArray;
for( int i = 0; i < outputArray.size(); ++i )
{

    IDXGIOutput* output = outputArray.at(i);
    DXGI_OUTPUT_DESC outputDesc;
    output->GetDesc( &outputDesc );
    int x = outputDesc.DesktopCoordinates.left;
    int y = outputDesc.DesktopCoordinates.top;
    int width = outputDesc.DesktopCoordinates.right - x;
    int height = outputDesc.DesktopCoordinates.bottom - y;
     WNDCLASSEX wc;
       ZeroMemory(&wc, sizeof(WNDCLASSEX));

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.lpszClassName = L"WindowClass";

    RegisterClassEx(&wc);

    RECT wr = {0, 0, 800, 600};
    AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);

    // Don't forget to clean this up. And all D3D COM objects.
    WindowDataContainer* window = new WindowDataContainer;

    window->hWnd = CreateWindowEx(NULL,
                          L"WindowClass",
                          L"Our First Direct3D Program",
                          WS_OVERLAPPEDWINDOW,
                          300,
                          300,
                          wr.right - wr.left,
                          wr.bottom - wr.top,
                          NULL,
                          NULL,
                          hInstance,
                          NULL);

    // show the window
    ShowWindow( window->hWnd, SW_SHOWDEFAULT );
    // InitD3D(hWnd);
        CreateSwapChainsAndViews();

    // set width and height
    window->width = width;
    window->height = height;
    std::vector<WindowDataContainer*> windowsArray;
    // shove it in the std::vector
    windowsArray.push_back(window);

    //if first window, associate it with DXGI so it can jump in
    // when there is something of interest in the message queue
    // think fullscreen mode switches etc. MSDN for more info.
//  if(i == 0)

    //  factory->MakeWindowAssociation( window->hWnd, 0 );

}
}

std::vector<DXGI_MODE_DESC*> GetDisplayModeList(IDXGIOutput* output)
{
    UINT num = 0;
    DXGI_FORMAT format = DXGI_FORMAT_R32G32B32A32_TYPELESS;
    UINT flags = DXGI_ENUM_MODES_INTERLACED | DXGI_ENUM_MODES_SCALING;

    // Get number of display modes
    output->GetDisplayModeList(format, flags, &num, 0);

    // Get display mode list
    DXGI_MODE_DESC * pDescs = new DXGI_MODE_DESC[num];
    output->GetDisplayModeList(format, flags, &num, pDescs);

    std::vector<DXGI_MODE_DESC*> displayList;
    for(int i = 0; i < num; ++i)
    {
        displayList.push_back(&pDescs[i]);
    }

    return displayList;
}
void CreateSwapChainsAndViews()
{
    std::vector<WindowDataContainer*> windowsArray;

    for( int i = 0; i < windowsArray.size(); i++ )
    {

        WindowDataContainer* window = windowsArray.at(i);

        // get the dxgi device
        IDXGIDevice* DXGIDevice = NULL;
        dev->QueryInterface( IID_IDXGIDevice, ( void** )&DXGIDevice ); // COM stuff, hopefully you are familiar

        // create a swap chain
        DXGI_SWAP_CHAIN_DESC swapChainDesc;

        // fill it in
           WindowDataContainer *p_Window = new WindowDataContainer;
        HRESULT hr = factory->CreateSwapChain( DXGIDevice, &swapChainDesc, &p_Window->swapChain );
        DXGIDevice->Release();
        DXGIDevice = NULL;

        // get the backbuffer
        ID3D11Texture2D* backBuffer = NULL;
        hr = window->swapChain->GetBuffer( 0, IID_ID3D11Texture2D, ( void** )&backBuffer );

        // get the backbuffer desc
        D3D11_TEXTURE2D_DESC backBufferDesc;
        backBuffer->GetDesc( &backBufferDesc );

        // create the render target view
        D3D11_RENDER_TARGET_VIEW_DESC RTVDesc;

        // fill it in

        dev->CreateRenderTargetView( backBuffer, &RTVDesc, &window->renderTargetView );
        backBuffer->Release();
        backBuffer = NULL;

        // Create depth stencil texture
        ID3D11Texture2D* depthStencil = NULL;
        D3D11_TEXTURE2D_DESC descDepth;

        // fill it in


        dev->CreateTexture2D( &descDepth, NULL, &depthStencil );

        // Create the depth stencil view
        D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;

        // fill it in

        dev->CreateDepthStencilView( depthStencil, &descDSV, &window->depthStencilView );

    }

}
void MultiRender( )
{
    std::vector<WindowDataContainer*> windowsArray;
    // Clear them all
    for( int i = 0; i < windowsArray.size(); i++ )
    {
        WindowDataContainer* window = windowsArray.at(i);

        // There is the answer to your second question:
        devcon->OMSetRenderTargets( 1, &window->renderTargetView, NULL );

        // Don't forget to adjust the viewport, in fullscreen it's not important...
        D3D11_VIEWPORT Viewport;
        Viewport.TopLeftX = 0;
        Viewport.TopLeftY = 0;
        Viewport.Width = window->width;
        Viewport.Height = window->height;
        Viewport.MinDepth = 0.0f;
        Viewport.MaxDepth = 1.0f;
        devcon->RSSetViewports( 1, &Viewport );

        // TO DO: AMAZING STUFF PER WINDOW
    }
}
//std::vector<IDXGIOutput*> outputArray; // contains outputs per adapter
void EnumOutputsOnAdapter()
{
    IDXGIOutput* output = NULL;
    for(int i = 0; DXGI_ERROR_NOT_FOUND != adapter->EnumOutputs(i, &output); ++i)
    {

        // get the description
        DXGI_OUTPUT_DESC outputDesc;
        HRESULT hr = output->GetDesc( &outputDesc );

        outputArray.push_back( output );
    }

}
// applicable for multiple ones with little effort
void GetAdapter() 
{
    // remember, we assume there's only one adapter (example purposes)
    for( int i = 0; DXGI_ERROR_NOT_FOUND != factory->EnumAdapters( i, &adapter ); ++i );
    {

        // get the description of the adapter, assuming no failure
        DXGI_ADAPTER_DESC adapterDesc;
        HRESULT hr = adapter->GetDesc( &adapterDesc );

        // Getting the outputs active on our adapter
        EnumOutputsOnAdapter();

    }
}

person G droid    schedule 30.04.2015    source источник
comment
Когда все выделено жирным, ничего не выделено жирным   -  person Thomas Ayoub    schedule 30.04.2015
comment
@Thomas: Теперь все в порядке?? :)   -  person G droid    schedule 30.04.2015
comment
При подключении внешнего экрана доступны следующие варианты: Клонировать || Расширенный (снимаю случаи только с одним экраном). Итак, вы хотите, чтобы ваш внешний дисплей был в каком режиме? (хорошее редактирование кстати ;))   -  person Thomas Ayoub    schedule 30.04.2015
comment
@Thomas: я не хочу использовать какой-либо режим .. На самом деле я хочу отображать изображение на мониторе (или телевизоре), подключенном к порту HDMI, без расширения дисплея.. Есть ли способ получить доступ к порту HDMI видео? адаптер программно??   -  person G droid    schedule 30.04.2015
comment
Я (думаю) я понял это. Но чтобы что-то отобразить на телевизоре, компьютер должен это знать и назначить ему режим, не так ли?   -  person Thomas Ayoub    schedule 30.04.2015
comment
@Thomas: вы правы.. Возможно ли, что я показываю изображение только на телевизоре, которое хочу отобразить, без клонирования или расширения всего рабочего стола.. могу ли я просто отобразить изображение на телевизоре с собственным фоном телевизора??   -  person G droid    schedule 30.04.2015
comment
Давайте продолжим обсуждение в чате.   -  person G droid    schedule 30.04.2015
comment
@Steve: На самом деле мы в команде.. я уберу это как можно скорее..   -  person G droid    schedule 01.05.2015
comment
@Gdroid, тебе удалось найти решение? Я столкнулся с аналогичной проблемой.   -  person Elad Maimoni    schedule 11.07.2018