examples: Add d3d11videosink examples for shared-texture use cases
authorSeungha Yang <seungha@centricular.com>
Thu, 10 Dec 2020 20:23:20 +0000 (05:23 +0900)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Tue, 26 Jan 2021 18:14:37 +0000 (18:14 +0000)
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>

tests/examples/d3d11videosink/d3d11device.cpp [new file with mode: 0644]
tests/examples/d3d11videosink/d3d11device.h [new file with mode: 0644]
tests/examples/d3d11videosink/d3d11videosink-shared-texture-d3d9ex.cpp [new file with mode: 0644]
tests/examples/d3d11videosink/d3d11videosink-shared-texture.cpp [new file with mode: 0644]
tests/examples/d3d11videosink/meson.build

diff --git a/tests/examples/d3d11videosink/d3d11device.cpp b/tests/examples/d3d11videosink/d3d11device.cpp
new file mode 100644 (file)
index 0000000..a016edf
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+ * 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
diff --git a/tests/examples/d3d11videosink/d3d11device.h b/tests/examples/d3d11videosink/d3d11device.h
new file mode 100644 (file)
index 0000000..f99e234
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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
diff --git a/tests/examples/d3d11videosink/d3d11videosink-shared-texture-d3d9ex.cpp b/tests/examples/d3d11videosink/d3d11videosink-shared-texture-d3d9ex.cpp
new file mode 100644 (file)
index 0000000..71d871b
--- /dev/null
@@ -0,0 +1,398 @@
+/*
+ * 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 (&params, &swapchain);
+    if (FAILED (hr)) {
+      gst_printerrln ("Couldn't create swapchain");
+      exit (-1);
+    }
+  } else {
+    /* Check whether we need to re-create swapchain */
+    hr = swapchain->GetPresentParameters (&params);
+    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 (&params, &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,
+      &params, 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;
+}
diff --git a/tests/examples/d3d11videosink/d3d11videosink-shared-texture.cpp b/tests/examples/d3d11videosink/d3d11videosink-shared-texture.cpp
new file mode 100644 (file)
index 0000000..87ce1ed
--- /dev/null
@@ -0,0 +1,424 @@
+/*
+ * 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;
+}
index 9cb2897..377c751 100644 (file)
@@ -6,4 +6,34 @@ if host_system == 'windows'
     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