Add two examples to demonstrate "draw-on-shared-texture" use cases.
d3d11videosink will draw application's own texture without copy
by using:
- Enable "draw-on-shared-texture" property
- make use of "begin-draw" and "draw" signals
And then, application will render the shared application's texture
to swapchain's backbuffer by using
1) Direct3D11 APIs
2) Or, Direct3D9Ex + interop APIs
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1873>
--- /dev/null
+/*
+ * GStreamer
+ * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "d3d11device.h"
+#include <d3dcompiler.h>
+#include <wrl.h>
+#include <string.h>
+
+using namespace Microsoft::WRL;
+
+HRESULT
+prepare_d3d11_device (ID3D11Device ** d3d11_device,
+ ID3D11DeviceContext ** d3d11_context, IDXGIFactory2 ** dxgi_factory)
+{
+ HRESULT hr;
+ static const D3D_FEATURE_LEVEL feature_levels[] = {
+ D3D_FEATURE_LEVEL_11_1,
+ D3D_FEATURE_LEVEL_11_0,
+ D3D_FEATURE_LEVEL_10_1,
+ D3D_FEATURE_LEVEL_10_0,
+ D3D_FEATURE_LEVEL_9_3,
+ D3D_FEATURE_LEVEL_9_2,
+ D3D_FEATURE_LEVEL_9_1
+ };
+ D3D_FEATURE_LEVEL selected_level;
+
+ ComPtr<IDXGIFactory1> factory;
+ hr = CreateDXGIFactory1 (IID_PPV_ARGS (&factory));
+ if (FAILED (hr)) {
+ gst_printerrln ("IDXGIFactory1 is unavailable, hr 0x%x", (guint) hr);
+ return hr;
+ }
+
+ ComPtr<IDXGIFactory2> factory2;
+ hr = factory.As (&factory2);
+ if (FAILED (hr)) {
+ gst_printerrln ("IDXGIFactory2 is unavailable, hr 0x%x", (guint) hr);
+ return hr;
+ }
+
+ ComPtr<IDXGIAdapter1> adapter;
+ hr = factory->EnumAdapters1 (0, &adapter);
+ if (FAILED (hr)) {
+ gst_printerrln ("IDXGIAdapter1 is unavailable, hr 0x%x", (guint) hr);
+ return hr;
+ }
+
+ ComPtr<ID3D11Device> device;
+ ComPtr<ID3D11DeviceContext> context;
+ hr = D3D11CreateDevice (adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN,
+ NULL, D3D11_CREATE_DEVICE_BGRA_SUPPORT, feature_levels,
+ G_N_ELEMENTS (feature_levels), D3D11_SDK_VERSION, &device,
+ &selected_level, &context);
+
+ /* Try again with excluding D3D_FEATURE_LEVEL_11_1 */
+ if (FAILED (hr)) {
+ hr = D3D11CreateDevice (adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN,
+ NULL, D3D11_CREATE_DEVICE_BGRA_SUPPORT, &feature_levels[1],
+ G_N_ELEMENTS (feature_levels) - 1, D3D11_SDK_VERSION, &device,
+ &selected_level, &context);
+ }
+
+ if (FAILED (hr)) {
+ gst_printerrln ("ID3D11Device is unavailable, hr 0x%x", (guint) hr);
+ return hr;
+ }
+
+ if (d3d11_device)
+ *d3d11_device = device.Detach();
+ if (d3d11_context)
+ *d3d11_context = context.Detach();
+ if (dxgi_factory)
+ *dxgi_factory = factory2.Detach();
+
+ return hr;
+}
+
+HRESULT
+prepare_shared_texture (ID3D11Device * d3d11_device, guint width,
+ guint height, DXGI_FORMAT format, UINT misc_flags,
+ ID3D11Texture2D ** texture, ID3D11ShaderResourceView ** srv,
+ IDXGIKeyedMutex ** keyed_mutex, HANDLE * shared_handle)
+{
+ D3D11_TEXTURE2D_DESC texture_desc = { 0, };
+ HRESULT hr;
+
+ /* Texture size doesn't need to be identical to that of backbuffer */
+ texture_desc.Width = width;
+ texture_desc.Height = height;
+ texture_desc.MipLevels = 1;
+ texture_desc.Format = format;
+ texture_desc.SampleDesc.Count = 1;
+ texture_desc.ArraySize = 1;
+ texture_desc.Usage = D3D11_USAGE_DEFAULT;
+ texture_desc.BindFlags =
+ D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
+ texture_desc.MiscFlags = misc_flags;
+
+ ComPtr<ID3D11Texture2D> shared_texture;
+ hr = d3d11_device->CreateTexture2D (&texture_desc, nullptr, &shared_texture);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't create ID3D11Texture2D");
+ return hr;
+ }
+
+ ComPtr<ID3D11ShaderResourceView> shader_resource_view;
+ if (srv) {
+ D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = { 0, };
+ srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+ srv_desc.Texture2D.MipLevels = 1;
+ srv_desc.Format = format;
+
+ hr = d3d11_device->CreateShaderResourceView (shared_texture.Get(), &srv_desc,
+ &shader_resource_view);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't create ID3D11ShaderResourceView");
+ return hr;
+ }
+ }
+
+ ComPtr<IDXGIKeyedMutex> keyed;
+ if ((misc_flags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX) != 0 && keyed_mutex) {
+ hr = shared_texture.As (&keyed);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't get IDXGIKeyedMutex");
+ return hr;
+ }
+ }
+
+ ComPtr<IDXGIResource> dxgi_resource;
+ hr = shared_texture.As (&dxgi_resource);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't get IDXGIResource handle");
+ return hr;
+ }
+
+ HANDLE handle;
+ if ((misc_flags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE) != 0) {
+ ComPtr<IDXGIResource1> dxgi_resource1;
+ hr = dxgi_resource.As (&dxgi_resource1);
+
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't get IDXGIResource1");
+ return hr;
+ }
+
+ hr = dxgi_resource1->CreateSharedHandle (nullptr,
+ DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr,
+ &handle);
+ } else {
+ hr = dxgi_resource->GetSharedHandle (&handle);
+ }
+
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't get shared handle from texture");
+ return hr;
+ }
+
+ *texture = shared_texture.Detach();
+ if (srv)
+ *srv = shader_resource_view.Detach();
+ *shared_handle = handle;
+ if (keyed && keyed_mutex)
+ *keyed_mutex = keyed.Detach();
+
+ return S_OK;
+}
+
+static HRESULT
+d3d_compile (const gchar * source, gboolean is_pixel_shader, ID3DBlob ** code)
+{
+ HRESULT hr;
+ const gchar *shader_target = "ps_4_0";
+
+ if (!is_pixel_shader)
+ shader_target = "vs_4_0";
+
+ ComPtr<ID3DBlob> blob;
+ ComPtr<ID3DBlob> error;
+ hr = D3DCompile (source, strlen (source), nullptr, nullptr,
+ nullptr, "main", shader_target, 0, 0, &blob, &error);
+
+ if (FAILED (hr)) {
+ const gchar *err = nullptr;
+ if (error)
+ err = (const gchar *) error->GetBufferPointer ();
+
+ gst_printerrln ("Couldn't compile pixel shader, error: %s",
+ GST_STR_NULL (err));
+ return hr;
+ }
+
+ *code = blob.Detach();
+
+ return S_OK;
+}
+
+HRESULT
+prepare_shader (ID3D11Device * d3d11_device, ID3D11DeviceContext * context,
+ ID3D11SamplerState ** sampler, ID3D11PixelShader ** ps,
+ ID3D11VertexShader ** vs, ID3D11InputLayout ** layout,
+ ID3D11Buffer ** vertex, ID3D11Buffer ** index)
+{
+ static const gchar ps_code[] =
+ "Texture2D shaderTexture;\n"
+ "SamplerState samplerState;\n"
+ "\n"
+ "struct PS_INPUT\n"
+ "{\n"
+ " float4 Position: SV_POSITION;\n"
+ " float3 Texture: TEXCOORD0;\n"
+ "};\n"
+ "\n"
+ "struct PS_OUTPUT\n"
+ "{\n"
+ " float4 Plane: SV_Target;\n"
+ "};\n"
+ "\n"
+ "PS_OUTPUT main(PS_INPUT input)\n"
+ "{\n"
+ " PS_OUTPUT output;\n"
+ " output.Plane = shaderTexture.Sample(samplerState, input.Texture);\n"
+ " return output;\n"
+ "}\n";
+
+ static const gchar vs_code[] =
+ "struct VS_INPUT\n"
+ "{\n"
+ " float4 Position : POSITION;\n"
+ " float4 Texture : TEXCOORD0;\n"
+ "};\n"
+ "\n"
+ "struct VS_OUTPUT\n"
+ "{\n"
+ " float4 Position: SV_POSITION;\n"
+ " float4 Texture: TEXCOORD0;\n"
+ "};\n"
+ "\n"
+ "VS_OUTPUT main(VS_INPUT input)\n"
+ "{\n"
+ " return input;\n"
+ "}\n";
+
+ D3D11_SAMPLER_DESC sampler_desc = { 0, };
+ sampler_desc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
+ sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
+ sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
+ sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
+ sampler_desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
+ sampler_desc.MinLOD = 0;
+ sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
+
+ ComPtr<ID3D11SamplerState> sampler_state;
+ HRESULT hr = d3d11_device->CreateSamplerState (&sampler_desc, &sampler_state);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't create ID3D11SamplerState");
+ return hr;
+ }
+
+ ComPtr<ID3DBlob> code;
+ hr = d3d_compile (ps_code, TRUE, &code);
+ if (FAILED (hr))
+ return hr;
+
+ ComPtr<ID3D11PixelShader> pixel_shader;
+ hr = d3d11_device->CreatePixelShader (code->GetBufferPointer(),
+ code->GetBufferSize(), nullptr, &pixel_shader);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't create ID3D11PixelShader");
+ return hr;
+ }
+
+ hr = d3d_compile (vs_code, FALSE, code.ReleaseAndGetAddressOf());
+ if (FAILED (hr))
+ return hr;
+
+ ComPtr<ID3D11VertexShader> vertex_shader;
+ hr = d3d11_device->CreateVertexShader (code->GetBufferPointer(),
+ code->GetBufferSize(), nullptr, &vertex_shader);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't create ID3D11VertexShader");
+ return hr;
+ }
+
+ D3D11_INPUT_ELEMENT_DESC input_desc[] = {
+ { "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 }
+ };
+
+ ComPtr<ID3D11InputLayout> input_layout;
+ hr = d3d11_device->CreateInputLayout (input_desc, G_N_ELEMENTS (input_desc),
+ code->GetBufferPointer(), code->GetBufferSize(), &input_layout);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't create ID3D11InputLayout");
+ return hr;
+ }
+
+ D3D11_BUFFER_DESC buffer_desc = { 0, };
+ buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
+ buffer_desc.ByteWidth = sizeof (VertexData) * 4;
+ buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+ buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+
+ ComPtr<ID3D11Buffer> vertex_buffer;
+ hr = d3d11_device->CreateBuffer (&buffer_desc, nullptr, &vertex_buffer);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't create ID3D11Buffer for vertex buffer");
+ return hr;
+ }
+
+ D3D11_MAPPED_SUBRESOURCE map;
+ hr = context->Map (vertex_buffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't map vertex buffer");
+ return hr;
+ }
+
+ VertexData *vertex_data = (VertexData *) map.pData;
+ vertex_data[0].position.x = -1.0f;
+ vertex_data[0].position.y = -1.0f;
+ vertex_data[0].position.z = 0.0f;
+ vertex_data[0].texture.x = 0.0f;
+ vertex_data[0].texture.y = 1.0f;
+
+ vertex_data[1].position.x = -1.0f;
+ vertex_data[1].position.y = 1.0f;
+ vertex_data[1].position.z = 0.0f;
+ vertex_data[1].texture.x = 0.0f;
+ vertex_data[1].texture.y = 0.0f;
+
+ vertex_data[2].position.x = 1.0f;
+ vertex_data[2].position.y = 1.0f;
+ vertex_data[2].position.z = 0.0f;
+ vertex_data[2].texture.x = 1.0f;
+ vertex_data[2].texture.y = 0.0f;
+
+ vertex_data[3].position.x = 1.0f;
+ vertex_data[3].position.y = -1.0f;
+ vertex_data[3].position.z = 0.0f;
+ vertex_data[3].texture.x = 1.0f;
+ vertex_data[3].texture.y = 1.0f;
+
+ context->Unmap (vertex_buffer.Get(), 0);
+
+ ComPtr<ID3D11Buffer> index_buffer;
+ buffer_desc.ByteWidth = sizeof (WORD) * 2 * 3;
+ buffer_desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
+ hr = d3d11_device->CreateBuffer (&buffer_desc, nullptr, &index_buffer);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't create ID3D11Buffer for index buffer");
+ return hr;
+ }
+
+ hr = context->Map (index_buffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't map index buffer");
+ return hr;
+ }
+
+ WORD *indices = (WORD *) map.pData;
+ indices[0] = 0;
+ indices[1] = 1;
+ indices[2] = 2;
+
+ indices[3] = 3;
+ indices[4] = 0;
+ indices[5] = 2;
+
+ context->Unmap (index_buffer.Get(), 0);
+
+ *sampler = sampler_state.Detach();
+ *ps = pixel_shader.Detach();
+ *vs = vertex_shader.Detach();
+ *layout = input_layout.Detach();
+ *vertex = vertex_buffer.Detach();
+ *index = index_buffer.Detach();
+
+ return S_OK;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * GStreamer
+ * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <gst/gst.h>
+#include <windows.h>
+#include <d3d11.h>
+#include <dxgi1_2.h>
+
+typedef struct
+{
+ struct {
+ FLOAT x;
+ FLOAT y;
+ FLOAT z;
+ } position;
+ struct {
+ FLOAT x;
+ FLOAT y;
+ } texture;
+} VertexData;
+
+HRESULT
+prepare_d3d11_device (ID3D11Device ** d3d11_device,
+ ID3D11DeviceContext ** d3d11_context,
+ IDXGIFactory2 ** dxgi_factory);
+
+HRESULT
+prepare_shared_texture (ID3D11Device * d3d11_device,
+ guint width,
+ guint height,
+ DXGI_FORMAT format,
+ UINT misc_flags,
+ ID3D11Texture2D ** texture,
+ ID3D11ShaderResourceView ** srv,
+ IDXGIKeyedMutex ** keyed_mutex,
+ HANDLE * shared_handle);
+
+HRESULT
+prepare_shader (ID3D11Device * d3d11_device,
+ ID3D11DeviceContext * context,
+ ID3D11SamplerState ** sampler,
+ ID3D11PixelShader ** ps,
+ ID3D11VertexShader ** vs,
+ ID3D11InputLayout ** layout,
+ ID3D11Buffer ** vertex,
+ ID3D11Buffer ** index);
\ No newline at end of file
--- /dev/null
+/*
+ * GStreamer
+ * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+
+#include "d3d11device.h"
+#include <wrl.h>
+#include <mutex>
+#include <d3d9.h>
+
+using namespace Microsoft::WRL;
+
+static gchar *uri = nullptr;
+
+static GMainLoop *loop = nullptr;
+static gboolean visible = FALSE;
+static HWND hwnd = nullptr;
+std::mutex lock;
+
+/* Device handles */
+ComPtr<ID3D11Device> device;
+ComPtr<ID3D11DeviceContext> context;
+ComPtr<IDXGIFactory2> factory;
+
+ComPtr<IDirect3D9Ex> d3d9_handle;
+ComPtr<IDirect3DDevice9Ex> d3d9_device;
+
+/* SwapChain resources */
+ComPtr<IDirect3DSwapChain9> swapchain;
+
+/* Shard texture resources */
+ComPtr<ID3D11Texture2D> shared_texture;
+ComPtr<IDirect3DTexture9> shared_d3d9_texture;
+ComPtr<IDirect3DSurface9> d3d9_surface;
+HANDLE shared_handle = nullptr;
+
+static void
+on_begin_draw (GstElement * videosink, gpointer user_data)
+{
+ std::lock_guard<std::mutex> lk (lock);
+ GstElement *sink = GST_ELEMENT (user_data);
+ gboolean ret = TRUE;
+ HRESULT hr;
+ ComPtr<IDirect3DSurface9> backbuffer;
+
+ /* Windows was destroyed, nothing to draw */
+ if (!hwnd)
+ return;
+
+ if (!shared_handle) {
+ gst_printerrln ("Shared handle wasn't configured");
+ exit (-1);
+ }
+
+ if (!swapchain) {
+ gst_printerrln ("SwapChain wasn't configured");
+ exit (-1);
+ }
+
+ g_signal_emit_by_name (sink,
+ "draw", shared_handle, D3D11_RESOURCE_MISC_SHARED, 0, 0, &ret);
+
+ if (!ret) {
+ gst_printerrln ("Failed to draw on shared handle");
+ if (loop)
+ g_main_loop_quit (loop);
+
+ return;
+ }
+
+ /* Get swapchain's backbuffer */
+ hr = swapchain->GetBackBuffer (0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't get backbuffer");
+ exit (-1);
+ }
+
+ /* Copy shared texture to backbuffer */
+ hr = d3d9_device->StretchRect (d3d9_surface.Get(), nullptr,
+ backbuffer.Get(), nullptr, D3DTEXF_LINEAR);
+ if (FAILED (hr)) {
+ gst_printerrln ("StretchRect failed");
+ exit (-1);
+ }
+
+ hr = d3d9_device->BeginScene ();
+ if (FAILED (hr)) {
+ gst_printerrln ("BeginScene failed");
+ exit (-1);
+ }
+
+ hr = swapchain->Present (nullptr, nullptr, nullptr, nullptr, 0);
+ if (FAILED (hr)) {
+ gst_printerrln ("Present failed");
+ exit (-1);
+ }
+
+ hr = d3d9_device->EndScene ();
+ if (FAILED (hr)) {
+ gst_printerrln ("BeginScene failed");
+ exit (-1);
+ }
+}
+
+static void
+on_resize (void)
+{
+ std::lock_guard<std::mutex> lk (lock);
+ RECT client_rect;
+ guint width, height;
+ HRESULT hr;
+
+ GetClientRect (hwnd, &client_rect);
+
+ width = MAX (1, (client_rect.right - client_rect.left));
+ height = MAX (1, (client_rect.bottom - client_rect.top));
+
+ D3DPRESENT_PARAMETERS params = { 0, };
+
+ if (!swapchain) {
+ params.Windowed = TRUE;
+ params.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ params.hDeviceWindow = hwnd;
+ /* GST_VIDEO_FORMAT_BGRA */
+ params.BackBufferFormat = D3DFMT_A8R8G8B8;
+
+ hr = d3d9_device->CreateAdditionalSwapChain (¶ms, &swapchain);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't create swapchain");
+ exit (-1);
+ }
+ } else {
+ /* Check whether we need to re-create swapchain */
+ hr = swapchain->GetPresentParameters (¶ms);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't get swapchain parameters");
+ exit (-1);
+ }
+
+ if (params.BackBufferWidth != width || params.BackBufferHeight != height) {
+ /* Backbuffer will have client area size */
+ params.BackBufferWidth = 0;
+ params.BackBufferHeight = 0;
+
+ swapchain = nullptr;
+ hr = d3d9_device->CreateAdditionalSwapChain (¶ms, &swapchain);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't re-create swapchain");
+ exit (-1);
+ }
+ }
+ }
+}
+
+static LRESULT CALLBACK
+window_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_DESTROY:
+ {
+ std::lock_guard<std::mutex> lk (lock);
+ hwnd = NULL;
+ if (loop)
+ g_main_loop_quit (loop);
+ break;
+ }
+ case WM_SIZE:
+ on_resize ();
+ break;
+ default:
+ break;
+ }
+
+ return DefWindowProc (hWnd, message, wParam, lParam);
+}
+
+static gboolean
+bus_msg (GstBus * bus, GstMessage * msg, GstElement * pipeline)
+{
+ switch (GST_MESSAGE_TYPE (msg)) {
+ case GST_MESSAGE_ASYNC_DONE:
+ /* make window visible when we have something to show */
+ if (!visible && hwnd) {
+ ShowWindow (hwnd, SW_SHOW);
+ visible = TRUE;
+ }
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ break;
+ case GST_MESSAGE_ERROR:{
+ GError *err;
+ gchar *dbg;
+
+ gst_message_parse_error (msg, &err, &dbg);
+ g_printerr ("ERROR %s \n", err->message);
+ if (dbg != NULL)
+ g_printerr ("ERROR debug information: %s\n", dbg);
+ g_clear_error (&err);
+ g_free (dbg);
+
+ g_main_loop_quit (loop);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+msg_cb (GIOChannel * source, GIOCondition condition, gpointer data)
+{
+ MSG msg;
+
+ if (!PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
+ return G_SOURCE_CONTINUE;
+
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+
+ return G_SOURCE_CONTINUE;
+}
+
+gint
+main (gint argc, gchar ** argv)
+{
+ GstElement *pipeline, *sink;
+ GstStateChangeReturn sret;
+ WNDCLASSEX wc = { 0, };
+ HINSTANCE hinstance = GetModuleHandle (NULL);
+ GIOChannel *msg_io_channel = NULL;
+ RECT wr = { 0, 0, 320, 240 };
+ HRESULT hr;
+ GOptionEntry options[] = {
+ {"uri", 0, 0, G_OPTION_ARG_STRING, &uri,
+ "URI to test (if unspecified, videotestsrc will be used)",
+ NULL},
+ {NULL}
+ };
+ GOptionContext *option_ctx;
+ gboolean ret;
+ GError *error = nullptr;
+
+ option_ctx = g_option_context_new ("d3d11videosink shard-texture with d3d9 interop example");
+ g_option_context_add_main_entries (option_ctx, options, NULL);
+ g_option_context_add_group (option_ctx, gst_init_get_option_group ());
+ ret = g_option_context_parse (option_ctx, &argc, &argv, &error);
+ g_option_context_free (option_ctx);
+
+ if (!ret) {
+ g_printerr ("option parsing failed: %s\n", error->message);
+ g_clear_error (&error);
+ exit (1);
+ }
+
+ /* 1) Prepare window */
+ wc.cbSize = sizeof (WNDCLASSEX);
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.lpfnWndProc = (WNDPROC) window_proc;
+ wc.hInstance = hinstance;
+ wc.hCursor = LoadCursor (NULL, IDC_ARROW);
+ wc.lpszClassName = TEXT ("GstD3D11VideoSinkSharedTextureD3D9ExExample");
+ RegisterClassEx (&wc);
+
+ AdjustWindowRect (&wr, WS_OVERLAPPEDWINDOW, FALSE);
+
+ hwnd = CreateWindowEx (0, wc.lpszClassName,
+ TEXT ("GstD3D11VideoSinkSharedTextureD3D9ExExample"),
+ WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ wr.right - wr.left, wr.bottom - wr.top, (HWND) NULL, (HMENU) NULL,
+ hinstance, NULL);
+
+ /* 2) Prepare D3D11 device */
+ hr = prepare_d3d11_device (&device, &context, &factory);
+ if (FAILED (hr)) {
+ gst_printerrln ("D3D11 device is unavailable");
+ return -1;
+ }
+
+ /* 3) Prepare D3D9EX device */
+ Direct3DCreate9Ex (D3D_SDK_VERSION, &d3d9_handle);
+ if (!d3d9_handle) {
+ gst_printerrln ("D3D9 handle is unavailable");
+ return -1;
+ }
+
+ D3DPRESENT_PARAMETERS params = { 0,};
+ params.Windowed = TRUE;
+ params.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ params.hDeviceWindow = hwnd;
+ params.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
+
+ hr = d3d9_handle->CreateDeviceEx (D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
+ D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
+ ¶ms, nullptr, &d3d9_device);
+ if (FAILED (hr)) {
+ gst_printerrln ("D3d9 deice is unavailable");
+ return -1;
+ }
+
+ /* 4) Create shared texture */
+ /* Texture size doesn't need to be identical to that of backbuffer */
+ hr = prepare_shared_texture (device.Get(), 1280, 720,
+ DXGI_FORMAT_B8G8R8A8_UNORM,
+ /* NOTE: D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX is incompatible with
+ * d3d9. User should use D3D11_RESOURCE_MISC_SHARED in case of d3d9 */
+ D3D11_RESOURCE_MISC_SHARED,
+ &shared_texture, nullptr, nullptr, &shared_handle);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't create texture to share with d3d11videosink");
+ return -1;
+ }
+
+ hr = d3d9_device->CreateTexture (1280, 720, 1, D3DUSAGE_RENDERTARGET,
+ D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &shared_d3d9_texture, &shared_handle);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't create shared d3d9 texture");
+ return -1;
+ }
+
+ hr = shared_d3d9_texture->GetSurfaceLevel (0, &d3d9_surface);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't get surface from shared d3d9 texture");
+ return -1;
+ }
+
+ /* Call initial resize to prepare swapchain */
+ on_resize();
+
+ loop = g_main_loop_new (NULL, FALSE);
+ msg_io_channel = g_io_channel_win32_new_messages ((gsize) hwnd);
+ g_io_add_watch (msg_io_channel, G_IO_IN, msg_cb, NULL);
+
+ /* Enable drawing on our texture and add signal handler */
+ sink = gst_element_factory_make ("d3d11videosink", NULL);
+ g_object_set (sink, "draw-on-shared-texture", TRUE, nullptr);
+ g_signal_connect (sink, "begin-draw", G_CALLBACK (on_begin_draw), sink);
+
+ if (uri) {
+ pipeline = gst_element_factory_make ("playbin", nullptr);
+ g_object_set (pipeline, "uri", uri, "video-sink", sink, nullptr);
+ } else {
+ GstElement *src = gst_element_factory_make ("videotestsrc", nullptr);
+
+ pipeline = gst_pipeline_new ("d3d11videosink-pipeline");
+ gst_bin_add_many (GST_BIN (pipeline), src, sink, NULL);
+ gst_element_link (src, sink);
+ }
+
+ gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), (GstBusFunc) bus_msg,
+ pipeline);
+
+ sret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
+ if (sret == GST_STATE_CHANGE_FAILURE) {
+ g_printerr ("Pipeline doesn't want to pause\n");
+ } else {
+ g_main_loop_run (loop);
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ }
+
+ gst_bus_remove_watch (GST_ELEMENT_BUS (pipeline));
+
+ if (hwnd)
+ DestroyWindow (hwnd);
+
+ gst_object_unref (pipeline);
+ if (msg_io_channel)
+ g_io_channel_unref (msg_io_channel);
+ g_main_loop_unref (loop);
+
+ gst_deinit ();
+ g_free (uri);
+
+ return 0;
+}
--- /dev/null
+/*
+ * GStreamer
+ * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+
+#include "d3d11device.h"
+#include <wrl.h>
+#include <mutex>
+
+using namespace Microsoft::WRL;
+
+static gboolean use_keyed_mutex = FALSE;
+static gboolean use_nt_handle = FALSE;
+static gchar *texture_foramt = nullptr;
+static gchar *uri = nullptr;
+
+static GMainLoop *loop = nullptr;
+static gboolean visible = FALSE;
+static HWND hwnd = nullptr;
+std::mutex lock;
+
+/* Device handles */
+ComPtr<ID3D11Device> device;
+ComPtr<ID3D11DeviceContext> context;
+ComPtr<IDXGIFactory2> factory;
+
+/* SwapChain resources */
+ComPtr<IDXGISwapChain1> swapchain;
+ComPtr<ID3D11RenderTargetView> rtv;
+
+/* Shard texture resources */
+ComPtr<ID3D11Texture2D> shared_texture;
+ComPtr<ID3D11ShaderResourceView> srv;
+ComPtr<IDXGIKeyedMutex> keyed_mutex;
+HANDLE shared_handle = nullptr;
+UINT misc_flags = 0;
+
+static void
+on_begin_draw (GstElement * videosink, gpointer user_data)
+{
+ std::lock_guard<std::mutex> lk (lock);
+ GstElement *sink = GST_ELEMENT (user_data);
+ gboolean ret = TRUE;
+ HRESULT hr;
+
+ /* Windows was destroyed, nothing to draw */
+ if (!hwnd)
+ return;
+
+ if (!shared_handle) {
+ gst_printerrln ("Shared handle wasn't configured");
+ exit (-1);
+ }
+
+ g_signal_emit_by_name (sink,
+ "draw", shared_handle, misc_flags, 0, 0, &ret);
+
+ if (!ret) {
+ gst_printerrln ("Failed to draw on shared handle");
+ if (loop)
+ g_main_loop_quit (loop);
+
+ return;
+ }
+
+ /* Acquire sync */
+ if (keyed_mutex) {
+ hr = keyed_mutex->AcquireSync (0, INFINITE);
+ if (FAILED (hr)) {
+ gst_printerrln ("Failed to acquire sync");
+ exit (-1);
+ }
+ }
+
+ ID3D11RenderTargetView *render_target_view = rtv.Get();
+ context->OMSetRenderTargets (1, &render_target_view, nullptr);
+
+ ID3D11ShaderResourceView *shader_resource = srv.Get();
+ context->PSSetShaderResources (0, 1, &shader_resource);
+
+ context->DrawIndexed (6, 0, 0);
+ if (keyed_mutex)
+ keyed_mutex->ReleaseSync (0);
+
+ swapchain->Present (0, 0);
+}
+
+static void
+on_resize (void)
+{
+ std::lock_guard<std::mutex> lk (lock);
+ ComPtr<ID3D11Texture2D> backbuffer;
+
+ rtv = nullptr;
+
+ HRESULT hr = swapchain->ResizeBuffers (0,
+ /* Specify zero width/height to use the size of current client area */
+ 0, 0,
+ /* Reuse configured format */
+ DXGI_FORMAT_UNKNOWN,
+ 0);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't resize swapchain");
+ exit(-1);
+ }
+
+ hr = swapchain->GetBuffer (0, IID_PPV_ARGS (&backbuffer));
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't get backbuffer from swapchain");
+ exit(-1);
+ }
+
+ hr = device->CreateRenderTargetView (backbuffer.Get(), nullptr, &rtv);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't get ID3D11RenderTargetView from backbuffer");
+ exit(-1);
+ }
+
+ D3D11_TEXTURE2D_DESC desc;
+ backbuffer->GetDesc(&desc);
+
+ D3D11_VIEWPORT viewport;
+ viewport.TopLeftX = 0;
+ viewport.TopLeftY = 0;
+ viewport.Width = desc.Width;
+ viewport.Height = desc.Height;
+ viewport.MinDepth = 0.0f;
+ viewport.MaxDepth = 1.0f;
+
+ context->RSSetViewports (1, &viewport);
+}
+
+static LRESULT CALLBACK
+window_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_DESTROY:
+ {
+ std::lock_guard<std::mutex> lk (lock);
+ hwnd = NULL;
+ if (loop)
+ g_main_loop_quit (loop);
+ break;
+ }
+ case WM_SIZE:
+ on_resize ();
+ break;
+ default:
+ break;
+ }
+
+ return DefWindowProc (hWnd, message, wParam, lParam);
+}
+
+static gboolean
+bus_msg (GstBus * bus, GstMessage * msg, GstElement * pipeline)
+{
+ switch (GST_MESSAGE_TYPE (msg)) {
+ case GST_MESSAGE_ASYNC_DONE:
+ /* make window visible when we have something to show */
+ if (!visible && hwnd) {
+ ShowWindow (hwnd, SW_SHOW);
+ visible = TRUE;
+ }
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ break;
+ case GST_MESSAGE_ERROR:{
+ GError *err;
+ gchar *dbg;
+
+ gst_message_parse_error (msg, &err, &dbg);
+ g_printerr ("ERROR %s \n", err->message);
+ if (dbg != NULL)
+ g_printerr ("ERROR debug information: %s\n", dbg);
+ g_clear_error (&err);
+ g_free (dbg);
+
+ g_main_loop_quit (loop);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+msg_cb (GIOChannel * source, GIOCondition condition, gpointer data)
+{
+ MSG msg;
+
+ if (!PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
+ return G_SOURCE_CONTINUE;
+
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+
+ return G_SOURCE_CONTINUE;
+}
+
+gint
+main (gint argc, gchar ** argv)
+{
+ GstElement *pipeline, *sink;
+ GstStateChangeReturn sret;
+ WNDCLASSEX wc = { 0, };
+ HINSTANCE hinstance = GetModuleHandle (NULL);
+ GIOChannel *msg_io_channel = NULL;
+ RECT wr = { 0, 0, 320, 240 };
+ ComPtr<ID3D11SamplerState> sampler;
+ ComPtr<ID3D11PixelShader> ps;
+ ComPtr<ID3D11VertexShader> vs;
+ ComPtr<ID3D11InputLayout> layout;
+ ComPtr<ID3D11Buffer> vertex;
+ ComPtr<ID3D11Buffer> index;
+ HRESULT hr;
+ GOptionEntry options[] = {
+ {"use-keyed-mutex", 0, 0, G_OPTION_ARG_NONE, &use_keyed_mutex,
+ "Allocate shared texture with D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX flag",
+ NULL},
+ {"use-nt-handle", 0, 0, G_OPTION_ARG_NONE, &use_nt_handle,
+ "Allocate shared texture with D3D11_RESOURCE_MISC_SHARED_NTHANDLE flag",
+ NULL},
+ {"texture-format", 0, 0, G_OPTION_ARG_STRING, &texture_foramt,
+ "texture format to test, supported arguments are { BGRA, RGBA, RGB10A2_LE }",
+ NULL},
+ {"uri", 0, 0, G_OPTION_ARG_STRING, &uri,
+ "URI to test (if unspecified, videotestsrc will be used)",
+ NULL},
+ {NULL}
+ };
+ GOptionContext *option_ctx;
+ gboolean ret;
+ GError *error = nullptr;
+ DXGI_FORMAT format = DXGI_FORMAT_B8G8R8A8_UNORM;
+
+ option_ctx = g_option_context_new ("d3d11videosink shard-texture example");
+ g_option_context_add_main_entries (option_ctx, options, NULL);
+ g_option_context_add_group (option_ctx, gst_init_get_option_group ());
+ ret = g_option_context_parse (option_ctx, &argc, &argv, &error);
+ g_option_context_free (option_ctx);
+
+ if (!ret) {
+ g_printerr ("option parsing failed: %s\n", error->message);
+ g_clear_error (&error);
+ exit (1);
+ }
+
+ if (g_strcmp0 (texture_foramt, "RGBA") == 0) {
+ gst_println ("Use DXGI_FORMAT_R8G8B8A8_UNORM (RGBA) format");
+ format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ } else if (g_strcmp0 (texture_foramt, "RGB10A2_LE") == 0) {
+ gst_println ("Use DXGI_FORMAT_R10G10B10A2_UNORM (RGB10A2_LE) format");
+ format = DXGI_FORMAT_R10G10B10A2_UNORM;
+ } else {
+ gst_println ("Use DXGI_FORMAT_B8G8R8A8_UNORM format");
+ format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ }
+
+ /* NT handle needs to be used with keyed mutex */
+ if (use_nt_handle) {
+ misc_flags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX | D3D11_RESOURCE_MISC_SHARED_NTHANDLE;
+ } else if (use_keyed_mutex) {
+ misc_flags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+ } else {
+ misc_flags = D3D11_RESOURCE_MISC_SHARED;
+ }
+
+ gst_println ("Use keyed-mutex: %d, use_nt_handle: %d", use_keyed_mutex,
+ use_nt_handle);
+
+ /* 1) Prepare window */
+ wc.cbSize = sizeof (WNDCLASSEX);
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.lpfnWndProc = (WNDPROC) window_proc;
+ wc.hInstance = hinstance;
+ wc.hCursor = LoadCursor (NULL, IDC_ARROW);
+ wc.lpszClassName = TEXT ("GstD3D11VideoSinkSharedTextureExExample");
+ RegisterClassEx (&wc);
+
+ AdjustWindowRect (&wr, WS_OVERLAPPEDWINDOW, FALSE);
+
+ hwnd = CreateWindowEx (0, wc.lpszClassName,
+ TEXT ("GstD3D11VideoSinkSharedTextureExExample"),
+ WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ wr.right - wr.left, wr.bottom - wr.top, (HWND) NULL, (HMENU) NULL,
+ hinstance, NULL);
+
+ /* 2) Prepare D3D11 device */
+ hr = prepare_d3d11_device (&device, &context, &factory);
+ if (FAILED (hr)) {
+ gst_printerrln ("D3D11 device is unavailable");
+ return -1;
+ }
+
+ hr = prepare_shader (device.Get(), context.Get(), &sampler,
+ &ps, &vs, &layout, &vertex, &index);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't setup shader");
+ return -1;
+ }
+
+ /* 3) Prepare SwapChain */
+ DXGI_SWAP_CHAIN_DESC1 desc = { 0, };
+ desc.Width = 0;
+ desc.Height = 0;
+ desc.Format = format;
+ desc.Stereo = FALSE;
+ desc.SampleDesc.Count = 1;
+ desc.SampleDesc.Quality = 0;
+ desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ desc.BufferCount = 2;
+ desc.Scaling = DXGI_SCALING_NONE;
+ desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+ desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
+
+ hr = factory->CreateSwapChainForHwnd (device.Get(), hwnd, &desc,
+ nullptr, nullptr, &swapchain);
+ if (FAILED (hr)) {
+ gst_printerrln ("IDXGISwapChain1 is unavailable");
+ return -1;
+ }
+
+ /* 4) Create shared texture */
+ /* Texture size doesn't need to be identical to that of backbuffer */
+ hr = prepare_shared_texture (device.Get(), 1280, 720,
+ format, misc_flags, &shared_texture, &srv, &keyed_mutex, &shared_handle);
+ if (FAILED (hr)) {
+ gst_printerrln ("Couldn't create texture to share with d3d11videosink");
+ return -1;
+ }
+
+ context->IASetPrimitiveTopology (D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+ context->IASetInputLayout (layout.Get());
+ ID3D11Buffer *buf = vertex.Get();
+ UINT offsets = 0;
+ UINT stride = sizeof(VertexData);
+ context->IASetVertexBuffers (0, 1, &buf, &stride, &offsets);
+ context->IASetIndexBuffer (index.Get(), DXGI_FORMAT_R16_UINT, 0);
+
+ ID3D11SamplerState *sampler_state = sampler.Get();
+ context->PSSetSamplers (0, 1, &sampler_state);
+ context->VSSetShader (vs.Get(), nullptr, 0);
+ context->PSSetShader (ps.Get(), nullptr, 0);
+
+ /* Call initial resize to prepare resources */
+ on_resize();
+
+ loop = g_main_loop_new (NULL, FALSE);
+ msg_io_channel = g_io_channel_win32_new_messages ((gsize) hwnd);
+ g_io_add_watch (msg_io_channel, G_IO_IN, msg_cb, NULL);
+
+ /* Enable drawing on our texture and add signal handler */
+ sink = gst_element_factory_make ("d3d11videosink", NULL);
+ g_object_set (sink, "draw-on-shared-texture", TRUE, nullptr);
+ g_signal_connect (sink, "begin-draw", G_CALLBACK (on_begin_draw), sink);
+
+ if (uri) {
+ pipeline = gst_element_factory_make ("playbin", nullptr);
+ g_object_set (pipeline, "uri", uri, "video-sink", sink, nullptr);
+ } else {
+ GstElement *src = gst_element_factory_make ("videotestsrc", nullptr);
+
+ pipeline = gst_pipeline_new ("d3d11videosink-pipeline");
+ gst_bin_add_many (GST_BIN (pipeline), src, sink, NULL);
+ gst_element_link (src, sink);
+ }
+
+ gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), (GstBusFunc) bus_msg,
+ pipeline);
+
+ sret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
+ if (sret == GST_STATE_CHANGE_FAILURE) {
+ g_printerr ("Pipeline doesn't want to pause\n");
+ } else {
+ g_main_loop_run (loop);
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ }
+
+ gst_bus_remove_watch (GST_ELEMENT_BUS (pipeline));
+
+ if (hwnd)
+ DestroyWindow (hwnd);
+
+ gst_object_unref (pipeline);
+ if (msg_io_channel)
+ g_io_channel_unref (msg_io_channel);
+ g_main_loop_unref (loop);
+
+ gst_deinit ();
+
+ g_free (texture_foramt);
+ g_free (uri);
+
+ /* NT handle should be explicitly closed to avoid leak */
+ if (use_nt_handle)
+ CloseHandle (shared_handle);
+
+ return 0;
+}
dependencies: [gst_dep, gstbase_dep, gstvideo_dep],
install: false,
)
+
+ d3d11_lib = cc.find_library('d3d11', required : false)
+ dxgi_lib = cc.find_library('dxgi', required : false)
+ d3dcompiler_lib = cc.find_library('d3dcompiler', required: false)
+ have_d3d11_h = cc.has_header('d3d11.h')
+ have_dxgi_h = cc.has_header('dxgi1_2.h')
+ have_d3d11compiler_h = cc.has_header('d3dcompiler.h')
+
+ d3d9_dep = cc.find_library('d3d9', required : false)
+ have_d3d9_h = cc.has_header('d3d9.h')
+
+ if d3d11_lib.found() and dxgi_lib.found() and d3dcompiler_lib.found() and have_d3d11_h and have_dxgi_h and have_d3d11compiler_h
+ executable('d3d11videosink-shared-texture',
+ ['d3d11videosink-shared-texture.cpp', 'd3d11device.cpp'],
+ c_args : gst_plugins_bad_args,
+ include_directories : [configinc, libsinc],
+ dependencies: [gst_dep, gstbase_dep, gstvideo_dep, d3d11_lib, dxgi_lib, d3dcompiler_lib],
+ install: false,
+ )
+
+ if d3d_dep.found() and have_d3d9_h
+ executable('d3d11videosink-shared-texture-d3d9ex',
+ ['d3d11videosink-shared-texture-d3d9ex.cpp', 'd3d11device.cpp'],
+ c_args : gst_plugins_bad_args,
+ include_directories : [configinc, libsinc],
+ dependencies: [gst_dep, gstbase_dep, gstvideo_dep, d3d11_lib, dxgi_lib, d3dcompiler_lib, d3d9_dep],
+ install: false,
+ )
+ endif
+ endif
endif