[vlc-devel] [PATCH 8/8] doc: add a sample Win32 app to test the Direct3D11 host of libvlc

Steve Lhomme robux4 at ycbcr.xyz
Mon Jan 21 17:38:51 CET 2019


---
 doc/libvlc/d3d11_player.cpp | 472 ++++++++++++++++++++++++++++++++++++
 1 file changed, 472 insertions(+)
 create mode 100644 doc/libvlc/d3d11_player.cpp

diff --git a/doc/libvlc/d3d11_player.cpp b/doc/libvlc/d3d11_player.cpp
new file mode 100644
index 0000000000..02fbfbb67f
--- /dev/null
+++ b/doc/libvlc/d3d11_player.cpp
@@ -0,0 +1,472 @@
+/* compile: g++ d3d11_player.cpp -o d3d11_player.exe -L<path/libvlc> -lvlc -ld3d11 -ld3dcompiler_47 -luuid */
+
+#include <windows.h>
+#include <d3d11.h>
+#include <d3dcompiler.h>
+
+#include <vlc/vlc.h>
+
+#define SCREEN_WIDTH  800
+#define SCREEN_HEIGHT 600
+
+struct render_context
+{
+    /* Direct3D11 device/context */
+    ID3D11Device        *d3device;
+    ID3D11DeviceContext *d3dctx;
+
+    IDXGISwapChain         *swapchain;
+    ID3D11RenderTargetView *swapchainRenderTarget;
+
+    /* our vertex/pixel shader */
+    ID3D11VertexShader *pVS;
+    ID3D11PixelShader  *pPS;
+    ID3D11InputLayout  *pShadersInputLayout;
+
+    UINT vertexBufferStride;
+    ID3D11Buffer *pVertexBuffer;
+
+    UINT quadIndexCount;
+    ID3D11Buffer *pIndexBuffer;
+
+    ID3D11SamplerState *samplerState;
+
+    /* texture VLC renders into */
+    ID3D11Texture2D          *texture;
+    ID3D11ShaderResourceView *textureShaderInput;
+    ID3D11RenderTargetView   *textureRenderTarget;
+};
+
+static bool StartRender(struct render_context *ctx)
+{
+    static const FLOAT blackRGBA[4] = {0.0f, 0.8f, 0.0f, 1.0f};
+
+    /* force unbinding the input texture, otherwise we get:
+     * OMSetRenderTargets: Resource being set to OM RenderTarget slot 0 is still bound on input! */
+    ID3D11ShaderResourceView *reset = NULL;
+    ctx->d3dctx->PSSetShaderResources(0, 1, &reset);
+    //ctx->d3dctx->Flush();
+
+    ctx->d3dctx->OMSetRenderTargets(1, &ctx->textureRenderTarget, NULL);
+
+    ctx->d3dctx->ClearRenderTargetView( ctx->textureRenderTarget, blackRGBA);
+
+    return true;
+}
+
+static void EndRender(struct render_context *ctx)
+{
+    /* render into the swapchain */
+    static const FLOAT orangeRGBA[4] = {1.0f, 0.5f, 0.0f, 1.0f};
+
+    ctx->d3dctx->OMSetRenderTargets(1, &ctx->swapchainRenderTarget, NULL);
+    ctx->d3dctx->ClearRenderTargetView(ctx->swapchainRenderTarget, orangeRGBA);
+
+    ctx->d3dctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+
+    ctx->d3dctx->IASetInputLayout(ctx->pShadersInputLayout);
+    UINT offset = 0;
+    ctx->d3dctx->IASetVertexBuffers(0, 1, &ctx->pVertexBuffer, &ctx->vertexBufferStride, &offset);
+    ctx->d3dctx->IASetIndexBuffer(ctx->pIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
+
+    ctx->d3dctx->VSSetShader(ctx->pVS, 0, 0);
+
+    ctx->d3dctx->PSSetSamplers(0, 1, &ctx->samplerState);
+
+    ctx->d3dctx->PSSetShaderResources(0, 1, &ctx->textureShaderInput);
+
+    ctx->d3dctx->PSSetShader(ctx->pPS, 0, 0);
+
+    D3D11_VIEWPORT viewport = { 0 };
+    viewport.TopLeftX = 0;
+    viewport.TopLeftY = 0;
+    viewport.Width = SCREEN_WIDTH;
+    viewport.Height = SCREEN_HEIGHT;
+
+    ctx->d3dctx->RSSetViewports(1, &viewport);
+
+    ctx->d3dctx->DrawIndexed(ctx->quadIndexCount, 0, 0);
+
+}
+
+static bool Resize(struct render_context *ctx, unsigned width, unsigned height)
+{
+    HRESULT hr;
+
+    if (ctx->texture)
+    {
+        ctx->texture->Release();
+        ctx->texture = NULL;
+    }
+    if (ctx->textureShaderInput)
+    {
+        ctx->textureShaderInput->Release();
+        ctx->textureShaderInput = NULL;
+    }
+    if (ctx->textureRenderTarget)
+    {
+        ctx->textureRenderTarget->Release();
+        ctx->textureRenderTarget = NULL;
+    }
+
+    /* interim texture */
+    D3D11_TEXTURE2D_DESC texDesc = { 0 };
+    texDesc.MipLevels = 1;
+    texDesc.SampleDesc.Count = 1;
+    texDesc.MiscFlags = 0;
+    texDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
+    texDesc.Usage = D3D11_USAGE_DEFAULT;
+    texDesc.CPUAccessFlags = 0;
+    texDesc.ArraySize = 1;
+    texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+    texDesc.Height = height;
+    texDesc.Width  = width;
+
+    hr = ctx->d3device->CreateTexture2D( &texDesc, NULL, &ctx->texture );
+    if (FAILED(hr)) return false;
+
+    D3D11_SHADER_RESOURCE_VIEW_DESC resviewDesc;
+    ZeroMemory(&resviewDesc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
+    resviewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+    resviewDesc.Texture2D.MipLevels = 1;
+    resviewDesc.Format = texDesc.Format;
+    hr = ctx->d3device->CreateShaderResourceView(ctx->texture, &resviewDesc, &ctx->textureShaderInput );
+    if (FAILED(hr)) return false;
+
+    D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc = {
+        .Format = texDesc.Format,
+        .ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D,
+    };
+    hr = ctx->d3device->CreateRenderTargetView(ctx->texture, &renderTargetViewDesc, &ctx->textureRenderTarget);
+    if (FAILED(hr)) return false;
+
+    return true;
+}
+
+static int VideoControl(void* opaque, libvlc_video_callback_control_t control,
+                        const void *input, void **output)
+{
+    struct render_context *ctx = static_cast<struct render_context *>(opaque);
+
+    switch (control)
+    {
+    case LIBVLC_VIDEO_SETUP:
+        *output = ctx->d3dctx;
+        break;
+    case LIBVLC_VIDEO_CLEANUP:
+        // here we can release all things Direct3D11 for good (if playing only one file)
+        break;
+    case LIBVLC_VIDEO_UPDATE_OUTPUT:
+        {
+            const libvlc_video_callback_cfg_t *cfg = static_cast<const libvlc_video_callback_cfg_t *>(input);
+            if (!Resize(ctx, cfg->width, cfg->height))
+                return -1;
+        }
+        break;
+    case LIBVLC_VIDEO_SWAP:
+        ctx->swapchain->Present(0, 0);
+        break;
+    case LIBVLC_VIDEO_START_RENDER:
+        if (!StartRender(ctx))
+            return -1;
+        break;
+    case LIBVLC_VIDEO_FINISHED_RENDERING:
+        EndRender(ctx);
+        break;
+    case LIBVLC_VIDEO_MAKE_CURRENT:
+    case LIBVLC_VIDEO_GET_PROCADDRESS:
+        /* ignore OpenGL specific controls */
+        break;
+    }
+    return 0;
+}
+
+static const char *shaderStr = "\
+Texture2D shaderTexture;\n\
+SamplerState samplerState;\n\
+struct PS_INPUT\n\
+{\n\
+    float4 position     : SV_POSITION;\n\
+    float4 textureCoord : TEXCOORD0;\n\
+};\n\
+\n\
+float4 PShader(PS_INPUT In) : SV_TARGET\n\
+{\n\
+    return shaderTexture.Sample(samplerState, In.textureCoord);\n\
+}\n\
+\n\
+struct VS_INPUT\n\
+{\n\
+    float4 position     : POSITION;\n\
+    float4 textureCoord : TEXCOORD0;\n\
+};\n\
+\n\
+struct VS_OUTPUT\n\
+{\n\
+    float4 position     : SV_POSITION;\n\
+    float4 textureCoord : TEXCOORD0;\n\
+};\n\
+\n\
+VS_OUTPUT VShader(VS_INPUT In)\n\
+{\n\
+    return In;\n\
+}\n\
+";
+
+struct SHADER_INPUT {
+    struct {
+        FLOAT x;
+        FLOAT y;
+        FLOAT z;
+    } position;
+    struct {
+        FLOAT x;
+        FLOAT y;
+    } texture;
+};
+
+static void init_direct3d(struct render_context *ctx, HWND hWnd)
+{
+    HRESULT hr;
+    DXGI_SWAP_CHAIN_DESC scd = { 0 };
+
+    scd.BufferCount = 1;
+    scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+    scd.BufferDesc.Width = SCREEN_WIDTH;
+    scd.BufferDesc.Height = SCREEN_HEIGHT;
+    scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+    scd.OutputWindow = hWnd;
+    scd.SampleDesc.Count = 1;
+    scd.Windowed = TRUE;
+    scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+
+    UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT; /* needed for hardware decoding */
+#ifndef NDEBUG
+    creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
+#endif
+
+    D3D11CreateDeviceAndSwapChain(NULL,
+                                  D3D_DRIVER_TYPE_HARDWARE,
+                                  NULL,
+                                  creationFlags,
+                                  NULL,
+                                  NULL,
+                                  D3D11_SDK_VERSION,
+                                  &scd,
+                                  &ctx->swapchain,
+                                  &ctx->d3device,
+                                  NULL,
+                                  &ctx->d3dctx);
+
+    /* The ID3D11Device must have multithread protection */
+    ID3D10Multithread *pMultithread;
+    hr = ctx->d3device->QueryInterface( __uuidof(ID3D10Multithread), (void **)&pMultithread);
+    if (SUCCEEDED(hr)) {
+        pMultithread->SetMultithreadProtected(TRUE);
+        pMultithread->Release();
+    }
+
+    ID3D11Texture2D *pBackBuffer;
+    ctx->swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
+
+    ctx->d3device->CreateRenderTargetView(pBackBuffer, NULL, &ctx->swapchainRenderTarget);
+    pBackBuffer->Release();
+
+    ID3D10Blob *VS, *PS, *pErrBlob;
+    char *err;
+    hr = D3DCompile(shaderStr, strlen(shaderStr),
+                    NULL, NULL, NULL, "VShader", "vs_4_0", 0, 0, &VS, &pErrBlob);
+    err = pErrBlob ? (char*)pErrBlob->GetBufferPointer() : NULL;
+    hr = D3DCompile(shaderStr, strlen(shaderStr),
+                    NULL, NULL, NULL, "PShader", "ps_4_0", 0, 0, &PS, &pErrBlob);
+    err = pErrBlob ? (char*)pErrBlob->GetBufferPointer() : NULL;
+
+    ctx->d3device->CreateVertexShader(VS->GetBufferPointer(), VS->GetBufferSize(), NULL, &ctx->pVS);
+    ctx->d3device->CreatePixelShader(PS->GetBufferPointer(), PS->GetBufferSize(), NULL, &ctx->pPS);
+
+    D3D11_INPUT_ELEMENT_DESC ied[] =
+    {
+        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
+        { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
+    };
+
+    hr = ctx->d3device->CreateInputLayout(ied, 2, VS->GetBufferPointer(), VS->GetBufferSize(), &ctx->pShadersInputLayout);
+    SHADER_INPUT OurVertices[] =
+    {
+        {-0.95f, -0.90f, 0.0f,  0.0f, 1.0f},
+        { 0.85f, -0.90f, 0.0f,  1.0f, 1.0f},
+        { 0.85f,  0.95f, 0.0f,  1.0f, 0.0f},
+        {-0.95f,  0.95f, 0.0f,  0.0f, 0.0f},
+    };
+
+    D3D11_BUFFER_DESC bd;
+    ZeroMemory(&bd, sizeof(bd));
+
+    bd.Usage = D3D11_USAGE_DYNAMIC;
+    bd.ByteWidth = sizeof(OurVertices);
+    bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+    bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+
+    ctx->d3device->CreateBuffer(&bd, NULL, &ctx->pVertexBuffer);
+    ctx->vertexBufferStride = sizeof(OurVertices[0]);
+
+    D3D11_MAPPED_SUBRESOURCE ms;
+    ctx->d3dctx->Map(ctx->pVertexBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);
+    memcpy(ms.pData, OurVertices, sizeof(OurVertices));
+    ctx->d3dctx->Unmap(ctx->pVertexBuffer, NULL);
+
+    ctx->quadIndexCount = 6;
+    D3D11_BUFFER_DESC quadDesc = { 0 };
+    quadDesc.Usage = D3D11_USAGE_DYNAMIC;
+    quadDesc.ByteWidth = sizeof(WORD) * ctx->quadIndexCount;
+    quadDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
+    quadDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+    ctx->d3device->CreateBuffer(&quadDesc, NULL, &ctx->pIndexBuffer);
+
+    ctx->d3dctx->Map(ctx->pIndexBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);
+    WORD *triangle_pos = static_cast<WORD*>(ms.pData);
+    triangle_pos[0] = 3;
+    triangle_pos[1] = 1;
+    triangle_pos[2] = 0;
+
+    triangle_pos[3] = 2;
+    triangle_pos[4] = 1;
+    triangle_pos[5] = 3;
+    ctx->d3dctx->Unmap(ctx->pIndexBuffer, NULL);
+
+    D3D11_SAMPLER_DESC sampDesc;
+    ZeroMemory(&sampDesc, sizeof(sampDesc));
+    sampDesc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
+    sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
+    sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
+    sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
+    sampDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
+    sampDesc.MinLOD = 0;
+    sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
+
+    hr = ctx->d3device->CreateSamplerState(&sampDesc, &ctx->samplerState);
+}
+
+static void release_direct3d(struct render_context *ctx)
+{
+    ctx->samplerState->Release();
+    ctx->textureRenderTarget->Release();
+    ctx->textureShaderInput->Release();
+    ctx->texture->Release();
+    ctx->pShadersInputLayout->Release();
+    ctx->pVS->Release();
+    ctx->pPS->Release();
+    ctx->pIndexBuffer->Release();
+    ctx->pVertexBuffer->Release();
+    ctx->swapchain->Release();
+    ctx->swapchainRenderTarget->Release();
+    ctx->d3dctx->Release();
+    ctx->d3device->Release();
+}
+
+static 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);
+}
+
+int WINAPI WinMain(HINSTANCE hInstance,
+                   HINSTANCE hPrevInstance,
+                   LPSTR lpCmdLine,
+                   int nCmdShow)
+{
+    HWND hWnd;
+    WNDCLASSEX wc;
+    struct render_context Context = { 0 };
+    char *file_path;
+    libvlc_instance_t *p_libvlc;
+    libvlc_media_t *p_media;
+    libvlc_media_player_t *p_mp;
+    (void)hPrevInstance;
+
+    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.lpszClassName = "WindowClass";
+
+    RegisterClassEx(&wc);
+
+    RECT wr = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};
+    AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
+
+    hWnd = CreateWindowEx(NULL,
+                          "WindowClass",
+                          "libvlc Demo app",
+                          WS_OVERLAPPEDWINDOW,
+                          300,
+                          300,
+                          wr.right - wr.left,
+                          wr.bottom - wr.top,
+                          NULL,
+                          NULL,
+                          hInstance,
+                          NULL);
+
+    ShowWindow(hWnd, nCmdShow);
+
+    init_direct3d(&Context, hWnd);
+
+    /* remove "" around the given path */
+    if (lpCmdLine[0] == '"')
+    {
+        file_path = strdup( lpCmdLine+1 );
+        if (file_path[strlen(file_path)-1] == '"')
+            file_path[strlen(file_path)-1] = '\0';
+    }
+    else
+        file_path = strdup( lpCmdLine );
+
+    p_libvlc = libvlc_new( 0, NULL );
+    p_media = libvlc_media_new_path( p_libvlc, file_path );
+    free( file_path );
+    p_mp = libvlc_media_player_new_from_media( p_media );
+
+    // DON'T use with callbacks libvlc_media_player_set_hwnd(p_mp, hWnd);
+
+    /* Tell VLC to render into our D3D11 environment */
+    libvlc_video_set_surface_callbacks( p_mp, libvlc_video_engine_direct3d11,
+                                        VideoControl, &Context );
+
+    libvlc_media_player_play( p_mp );
+
+    MSG msg;
+
+    while(TRUE)
+    {
+        if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+        {
+            TranslateMessage(&msg);
+            DispatchMessage(&msg);
+
+            if(msg.message == WM_QUIT)
+                break;
+        }
+    }
+
+    libvlc_media_player_stop( p_mp );
+
+    libvlc_media_player_release( p_mp );
+    libvlc_media_release( p_media );
+    libvlc_release( p_libvlc );
+
+    release_direct3d(&Context);
+
+    return msg.wParam;
+}
-- 
2.17.1



More information about the vlc-devel mailing list