vlc | branch: master | Steve Lhomme <[email protected]> | Wed Mar 20 15:12:50 2019 +0100| [9930c408193f8b6a46de55e85dac9aaa6329dd4d] | committer: Steve Lhomme
doc: add a sample app to demo D3D11 callbacks > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=9930c408193f8b6a46de55e85dac9aaa6329dd4d --- doc/libvlc/d3d11_player.cpp | 473 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 473 insertions(+) diff --git a/doc/libvlc/d3d11_player.cpp b/doc/libvlc/d3d11_player.cpp new file mode 100644 index 0000000000..a32505ceee --- /dev/null +++ b/doc/libvlc/d3d11_player.cpp @@ -0,0 +1,473 @@ +/* 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 1500 +#define SCREEN_HEIGHT 900 +#define BORDER_LEFT (-0.95f) +#define BORDER_RIGHT ( 0.85f) +#define BORDER_TOP ( 0.95f) +#define BORDER_BOTTOM (-0.90f) + +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 UpdateOutput_cb( void *opaque, const libvlc_video_direct3d_cfg_t *cfg, libvlc_video_output_cfg_t *out ) +{ + struct render_context *ctx = static_cast<struct render_context *>( opaque ); + + HRESULT hr; + + DXGI_FORMAT renderFormat = DXGI_FORMAT_R8G8B8A8_UNORM; + + 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 = renderFormat; + texDesc.Height = cfg->height; + texDesc.Width = cfg->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; + + + out->surface_format = renderFormat; + out->full_range = true; + out->colorspace = libvlc_video_colorspace_BT709; + out->primaries = libvlc_video_primaries_BT709; + out->transfer = libvlc_video_transfer_func_SRGB; + + return true; +} + +static void Swap_cb( void* opaque ) +{ + struct render_context *ctx = static_cast<struct render_context *>( opaque ); + ctx->swapchain->Present( 0, 0 ); +} + +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); + + DXGI_SWAP_CHAIN_DESC scd; + ctx->swapchain->GetDesc(&scd); + RECT currentRect; + GetWindowRect(scd.OutputWindow, ¤tRect); + + D3D11_VIEWPORT viewport = { 0 }; + viewport.TopLeftX = 0; + viewport.TopLeftY = 0; + viewport.Width = SCREEN_WIDTH; //currentRect.right - currentRect.left; + viewport.Height = SCREEN_HEIGHT; //currentRect.bottom - currentRect.top; + + ctx->d3dctx->RSSetViewports(1, &viewport); + + ctx->d3dctx->DrawIndexed(ctx->quadIndexCount, 0, 0); +} + +static bool StartRendering_cb( void *opaque, bool enter ) +{ + struct render_context *ctx = static_cast<struct render_context *>( opaque ); + if ( enter ) + { + static const FLOAT blackRGBA[4] = {0.5f, 0.5f, 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->ClearRenderTargetView( ctx->textureRenderTarget, blackRGBA); + return true; + } + + EndRender( ctx ); + return true; +} + +static bool Setup_cb( void **opaque, const libvlc_video_direct3d_device_cfg_t *cfg, libvlc_video_direct3d_device_setup_t *out ) +{ + struct render_context *ctx = static_cast<struct render_context *>(*opaque); + out->device_context = ctx->d3dctx; + return true; +} + +static void Cleanup_cb( void *opaque ) +{ + // here we can release all things Direct3D11 for good (if playing only one file) +} + +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[] = + { + {BORDER_LEFT, BORDER_BOTTOM, 0.0f, 0.0f, 1.0f}, + {BORDER_RIGHT, BORDER_BOTTOM, 0.0f, 1.0f, 1.0f}, + {BORDER_RIGHT, BORDER_TOP, 0.0f, 1.0f, 0.0f}, + {BORDER_LEFT, BORDER_TOP, 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; + } + + 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; + + /* 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 ); + + 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, + CW_USEDEFAULT, CW_USEDEFAULT, + wr.right - wr.left, + wr.bottom - wr.top, + NULL, + NULL, + hInstance, + p_mp); + + ShowWindow(hWnd, nCmdShow); + + init_direct3d(&Context, hWnd); + + // DON'T use with callbacks libvlc_media_player_set_hwnd(p_mp, hWnd); + + /* Tell VLC to render into our D3D11 environment */ + libvlc_video_direct3d_set_callbacks( p_mp, libvlc_video_direct3d_engine_d3d11, + Setup_cb, Cleanup_cb, UpdateOutput_cb, Swap_cb, StartRendering_cb, + &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; +} _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
