d3d12: Add d3d12swapchainsink element

Adding a new videosink element for Windows composition API based
applications. Unlike d3d12videosink, this element will create only
DXGI swapchain by using IDXGIFactory2::CreateSwapChainForComposition()
without actual window handle, so that video scene can be composed
via Windows native composition API, such as DirectComposition.
Note that this videosink does not support GstVideoOverlay interface
because of the design.

The swapchain created by this element can be used with
* DirectComposition's IDCompositionVisual in Win32 app
* WinRT and WinUI3's UI.Composition in Win32/UWP app
* UWP and WinUI3 XAML's SwapChainPanel

See also examples in this commit which show usage of the videosink

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7287>
This commit is contained in:
Seungha Yang 2024-07-30 21:59:53 +09:00
parent 337fee8388
commit 4bb3854772
7 changed files with 1947 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,33 @@
/* GStreamer
* Copyright (C) 2024 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.
*/
#pragma once
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/d3d12/gstd3d12.h>
G_BEGIN_DECLS
#define GST_TYPE_D3D12_SWAPCHAIN_SINK (gst_d3d12_swapchain_sink_get_type())
G_DECLARE_FINAL_TYPE (GstD3D12SwapChainSink, gst_d3d12_swapchain_sink,
GST, D3D12_SWAPCHAIN_SINK, GstVideoSink)
G_END_DECLS

View file

@ -23,6 +23,7 @@ d3d12_sources = [
'gstd3d12screencapture.cpp',
'gstd3d12screencapturedevice.cpp',
'gstd3d12screencapturesrc.cpp',
'gstd3d12swapchainsink.cpp',
'gstd3d12testsrc.cpp',
'gstd3d12videosink.cpp',
'gstd3d12vp8dec.cpp',

View file

@ -48,6 +48,7 @@
#include "gstd3d12ipcclient.h"
#include "gstd3d12ipcsrc.h"
#include "gstd3d12ipcsink.h"
#include "gstd3d12swapchainsink.h"
#include <windows.h>
#include <versionhelpers.h>
#include <wrl.h>
@ -178,6 +179,8 @@ plugin_init (GstPlugin * plugin)
"d3d12ipcsrc", GST_RANK_NONE, GST_TYPE_D3D12_IPC_SRC);
gst_element_register (plugin,
"d3d12ipcsink", GST_RANK_NONE, GST_TYPE_D3D12_IPC_SINK);
gst_element_register (plugin,
"d3d12swapchainsink", GST_RANK_NONE, GST_TYPE_D3D12_SWAPCHAIN_SINK);
g_object_set_data_full (G_OBJECT (plugin),
"plugin-d3d12-shutdown", (gpointer) "shutdown-data",

View file

@ -0,0 +1,448 @@
/* GStreamer
* Copyright (C) 2024 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 <windows.h>
#include <dcomp.h>
#include <d3d11.h>
#include <dxgi.h>
#include <wrl.h>
#include <memory>
using namespace Microsoft::WRL;
static GMainLoop *loop_ = nullptr;
static HWND hwnd_ = nullptr;
struct GpuResource
{
ComPtr<IDCompositionDesktopDevice> dcomp_device;
ComPtr<IDCompositionTarget> target;
ComPtr<IDCompositionVisual2> visual;
ComPtr<IDCompositionVirtualSurface> bg_surface;
ComPtr<IDCompositionVisual2> swapchain_visual;
ComPtr<ID3D11Device> device11;
ComPtr<ID3D11DeviceContext> context11;
};
struct AppData
{
GstElement *pipeline = nullptr;
std::shared_ptr<GpuResource> resource;
};
#define APP_DATA_PROP_NAME L"EXAMPLE-APP-DATA"
static LRESULT CALLBACK
window_proc (HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
switch (message) {
case WM_NCCREATE:
{
LPCREATESTRUCTW lpcs = (LPCREATESTRUCTW) lparam;
auto data = (AppData *) lpcs->lpCreateParams;
SetPropW (hwnd, APP_DATA_PROP_NAME, data);
break;
}
case WM_DESTROY:
gst_println ("Destroy window");
if (loop_)
g_main_loop_quit (loop_);
break;
case WM_SIZE:
{
auto data = (AppData *) GetPropW (hwnd, APP_DATA_PROP_NAME);
if (!data)
break;
auto resource = data->resource;
if (!resource)
break;
RECT rect = { };
GetClientRect (hwnd, &rect);
gint width = (rect.right - rect.left);
gint height = (rect.bottom - rect.top);
if (width > 0 && height > 0) {
POINT offset;
ComPtr<ID3D11Texture2D> texture;
ComPtr<ID3D11RenderTargetView> rtv;
auto hr = resource->bg_surface->Resize (width, height);
if (SUCCEEDED (hr)) {
hr = resource->bg_surface->BeginDraw (nullptr,
IID_PPV_ARGS (&texture), &offset);
}
if (SUCCEEDED (hr)) {
hr = resource->device11->CreateRenderTargetView (texture.Get (), nullptr,
&rtv);
}
if (SUCCEEDED (hr)) {
FLOAT bg_color[] = { 0.5, 0.5, 0.5, 0.5 };
resource->context11->ClearRenderTargetView (rtv.Get (), bg_color);
hr = resource->bg_surface->EndDraw ();
}
if (SUCCEEDED (hr)) {
if (width > 320) {
FLOAT offset_x = ((FLOAT) (width - 320)) / 2.0;
resource->swapchain_visual->SetOffsetX (offset_x);
} else {
resource->swapchain_visual->SetOffsetX (0.0);
}
if (height > 240) {
FLOAT offset_y = ((FLOAT) (height - 240)) / 2.0;
resource->swapchain_visual->SetOffsetY (offset_y);
} else {
resource->swapchain_visual->SetOffsetY (0.0);
}
resource->dcomp_device->Commit ();
}
}
break;
}
default:
break;
}
return DefWindowProcW (hwnd, message, wparam, lparam);
}
static gboolean
bus_msg (GstBus * bus, GstMessage * msg, AppData * data)
{
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_ERROR:
{
GError *err;
gchar *dbg;
gst_message_parse_error (msg, &err, &dbg);
gst_printerrln ("ERROR %s", err->message);
if (dbg != nullptr)
gst_printerrln ("ERROR debug information: %s", dbg);
g_clear_error (&err);
g_free (dbg);
g_main_loop_quit (loop_);
break;
}
case GST_MESSAGE_EOS:
{
gst_println ("Got EOS");
g_main_loop_quit (loop_);
break;
}
default:
break;
}
return TRUE;
}
static gboolean
msg_cb (GIOChannel * source, GIOCondition condition, gpointer data)
{
MSG msg;
if (!PeekMessageW (&msg, nullptr, 0, 0, PM_REMOVE))
return G_SOURCE_CONTINUE;
TranslateMessage (&msg);
DispatchMessage (&msg);
return G_SOURCE_CONTINUE;
}
int
main (int argc, char ** argv)
{
GIOChannel *msg_io_channel = nullptr;
AppData app_data = { };
HRESULT hr;
gchar *uri = nullptr;
GOptionEntry options[] = {
{"uri", 0, 0, G_OPTION_ARG_STRING, &uri, "URI to play"},
{nullptr}
};
auto opt_ctx = g_option_context_new ("D3D12 swapchainsink");
g_option_context_add_main_entries (opt_ctx, options, nullptr);
g_option_context_set_help_enabled (opt_ctx, TRUE);
g_option_context_add_group (opt_ctx, gst_init_get_option_group ());
if (!g_option_context_parse (opt_ctx, &argc, &argv, nullptr)) {
gst_printerrln ("option parsing failed");
return 1;
}
loop_ = g_main_loop_new (nullptr, FALSE);
/* Creates pipeline */
GstElement *sink;
if (uri) {
app_data.pipeline = gst_element_factory_make ("playbin3", nullptr);
if (!app_data.pipeline) {
gst_printerrln ("Couldn't create pipeline");
return 1;
}
sink = gst_element_factory_make ("d3d12swapchainsink", nullptr);
if (!sink) {
gst_printerrln ("Couldn't create sink");
return 1;
}
g_object_set (app_data.pipeline, "video-sink", sink, "uri", uri, nullptr);
} else {
app_data.pipeline = gst_parse_launch ("d3d12testsrc ! "
"video/x-raw(memory:D3D12Memory),format=RGBA,width=240,height=240 ! "
"dwritetimeoverlay font-size=50 ! queue ! d3d12swapchainsink name=sink",
nullptr);
if (!app_data.pipeline) {
gst_printerrln ("Couldn't create pipeline");
return 1;
}
sink = gst_bin_get_by_name (GST_BIN (app_data.pipeline), "sink");
g_assert (sink);
}
gst_bus_add_watch (GST_ELEMENT_BUS (app_data.pipeline), (GstBusFunc) bus_msg,
&app_data);
/* Set swapchain resolution and border color */
g_signal_emit_by_name (sink, "resize", 320, 240);
guint64 border_color = 0;
/* alpha */
border_color |= ((guint64) (G_MAXUINT16 / 2)) << 48;
/* red */
border_color |= ((guint64) (G_MAXUINT16 / 2)) << 32;
g_object_set (sink, "border-color", border_color, nullptr);
/* Gets swapchain handle. This swapchain will be bound to a dcomp visual node */
IUnknown *swapchain = nullptr;
g_object_get (sink, "swapchain", &swapchain, nullptr);
if (!swapchain) {
gst_printerrln ("Couldn't get swapchain");
return 1;
}
/* playbin will take floating refcount */
if (!uri)
gst_object_unref (sink);
/* Creates d3d11 device to initialize dcomp device.
* Note that d3d11 (or d2d) device will not be required if swapchain is
* the only visual node (i.e., root node) which needs to be composed.
* In that case, an application can pass nullptr device to
* DCompositionCreateDevice2() */
auto resource = std::make_shared<GpuResource> ();
ComPtr<IDXGIFactory1> factory;
ComPtr<IDXGIAdapter> adapter;
hr = CreateDXGIFactory1 (IID_PPV_ARGS (&factory));
if (FAILED (hr)) {
gst_printerrln ("CreateDXGIFactory1 failed");
return 1;
}
hr = factory->EnumAdapters (0, &adapter);
if (FAILED (hr)) {
gst_printerrln ("EnumAdapters failed");
return 1;
}
static const D3D_FEATURE_LEVEL feature_levels[] = {
D3D_FEATURE_LEVEL_11_1,
};
hr = D3D11CreateDevice (adapter.Get (), D3D_DRIVER_TYPE_UNKNOWN, nullptr,
D3D11_CREATE_DEVICE_BGRA_SUPPORT, feature_levels, 1, D3D11_SDK_VERSION,
&resource->device11, nullptr, &resource->context11);
if (FAILED (hr)) {
gst_printerrln ("D3D11CreateDevice failed");
return 1;
}
/* Prepare main window */
WNDCLASSEXW wc = { };
RECT wr = { 0, 0, 640, 480 };
HINSTANCE hinstance = GetModuleHandle (nullptr);
wc.cbSize = sizeof (WNDCLASSEXW);
wc.lpfnWndProc = (WNDPROC) window_proc;
wc.hInstance = hinstance;
wc.hIcon = LoadIcon (nullptr, IDI_WINLOGO);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wc.hCursor = LoadCursor (nullptr, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
wc.lpszClassName = L"GstD3D12SwapChainSinkExample";
RegisterClassExW (&wc);
AdjustWindowRect (&wr, WS_OVERLAPPEDWINDOW, FALSE);
hwnd_ = CreateWindowExW (WS_EX_NOREDIRECTIONBITMAP, wc.lpszClassName,
L"D3D12SwapChainSink Example - Win32",
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
wr.right - wr.left, wr.bottom - wr.top, (HWND) nullptr, (HMENU) nullptr,
hinstance, &app_data);
msg_io_channel = g_io_channel_win32_new_messages (0);
g_io_add_watch (msg_io_channel, G_IO_IN, msg_cb, nullptr);
/* Create DComp resources */
hr = DCompositionCreateDevice2 (resource->device11.Get (),
IID_PPV_ARGS (&resource->dcomp_device));
if (FAILED (hr)) {
gst_printerrln ("Couldn't create composition device");
return 1;
}
hr = resource->dcomp_device->CreateTargetForHwnd (hwnd_, TRUE,
&resource->target);
if (FAILED (hr)) {
gst_printerrln ("CreateTargetForHwnd failed");
return 1;
}
hr = resource->dcomp_device->CreateVisual (&resource->visual);
if (FAILED (hr)) {
gst_printerrln ("CreateVisual failed");
return 1;
}
hr = resource->target->SetRoot (resource->visual.Get ());
if (FAILED (hr)) {
gst_printerrln ("SetRoot failed");
return 1;
}
/* Create background visual, and clear color using d3d11 API */
hr = resource->dcomp_device->CreateVirtualSurface (640, 480,
DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ALPHA_MODE_PREMULTIPLIED,
&resource->bg_surface);
if (FAILED (hr)) {
gst_printerrln ("CreateVirtualSurface failed");
return 1;
}
hr = resource->visual->SetContent (resource->bg_surface.Get ());
if (FAILED (hr)) {
gst_printerrln ("SetContent failed");
return 1;
}
{
POINT offset;
ComPtr<ID3D11Texture2D> texture;
ComPtr<ID3D11RenderTargetView> rtv;
hr = resource->bg_surface->BeginDraw (nullptr, IID_PPV_ARGS (&texture),
&offset);
if (FAILED (hr)) {
gst_printerrln ("BeginDraw failed");
return 1;
}
hr = resource->device11->CreateRenderTargetView (texture.Get (),
nullptr, &rtv);
if (FAILED (hr)) {
gst_printerrln ("CreateRenderTargetView failed");
return 1;
}
/* Draw semi-transparent background */
FLOAT bg_color[] = { 0.5, 0.5, 0.5, 0.5 };
resource->context11->ClearRenderTargetView (rtv.Get (), bg_color);
hr = resource->bg_surface->EndDraw ();
if (FAILED (hr)) {
gst_printerrln ("EndDraw failed");
return 1;
}
}
hr = resource->dcomp_device->CreateVisual (&resource->swapchain_visual);
if (FAILED (hr)) {
gst_printerrln ("CreateVisual failed");
return 1;
}
hr = resource->visual->AddVisual (resource->swapchain_visual.Get (), TRUE, nullptr);
if (FAILED (hr)) {
gst_printerrln ("AddVisual failed");
return 1;
}
hr = resource->swapchain_visual->SetOffsetX (160.0);
if (FAILED (hr)) {
gst_printerrln ("SetOffsetX failed");
return 1;
}
hr = resource->swapchain_visual->SetOffsetY (120.0);
if (FAILED (hr)) {
gst_printerrln ("SetOffsetY failed");
return 1;
}
hr = resource->swapchain_visual->SetContent (swapchain);
if (FAILED (hr)) {
gst_printerrln ("SetContent failed");
return 1;
}
hr = resource->dcomp_device->Commit ();
if (FAILED (hr)) {
gst_printerrln ("Commit failed");
return 1;
}
app_data.resource = std::move (resource);
gst_element_set_state (app_data.pipeline, GST_STATE_PLAYING);
g_main_loop_run (loop_);
gst_element_set_state (app_data.pipeline, GST_STATE_NULL);
gst_bus_remove_watch (GST_ELEMENT_BUS (app_data.pipeline));
app_data.resource = nullptr;
gst_object_unref (app_data.pipeline);
if (hwnd_)
DestroyWindow (hwnd_);
g_io_channel_unref (msg_io_channel);
g_main_loop_unref (loop_);
g_free (uri);
gst_deinit ();
return 0;
}

View file

@ -0,0 +1,333 @@
/* GStreamer
* Copyright (C) 2024 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 <windows.h>
#include <wrl.h>
#include <string.h>
#include <winstring.h>
#include <roapi.h>
#include <dispatcherqueue.h>
#include <windows.system.h>
#include <windows.ui.composition.h>
#include <windows.ui.composition.interop.h>
#include <windows.ui.composition.desktop.h>
using namespace Microsoft::WRL;
using namespace ABI::Windows::System;
using namespace ABI::Windows::UI::Composition;
using namespace ABI::Windows::UI::Composition::Desktop;
using namespace ABI::Windows::Foundation;
static LRESULT CALLBACK
window_proc (HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
switch (message) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
break;
}
return DefWindowProcW (hwnd, message, wparam, lparam);
}
static void
app_main (void)
{
auto pipeline = gst_parse_launch ("d3d12testsrc ! "
"video/x-raw(memory:D3D12Memory),format=RGBA,width=240,height=240 ! "
"dwritetimeoverlay font-size=50 ! queue ! d3d12swapchainsink name=sink",
nullptr);
if (!pipeline) {
gst_printerrln ("Couldn't create pipeline");
return;
}
auto sink = gst_bin_get_by_name (GST_BIN (pipeline), "sink");
g_assert (sink);
/* Set swapchain resolution and border color */
g_signal_emit_by_name (sink, "resize", 320, 240);
guint64 border_color = 0;
/* alpha */
border_color |= ((guint64) (G_MAXUINT16 / 2)) << 48;
/* red */
border_color |= ((guint64) (G_MAXUINT16 / 2)) << 32;
g_object_set (sink, "border-color", border_color, nullptr);
IUnknown *swapchain = nullptr;
g_object_get (sink, "swapchain", &swapchain, nullptr);
if (!swapchain) {
gst_printerrln ("Couldn't get swapchain");
return;
}
gst_object_unref (sink);
/* Prepare main window */
WNDCLASSEXW wc = { };
RECT wr = { 0, 0, 640, 480 };
HINSTANCE hinstance = GetModuleHandle (nullptr);
wc.cbSize = sizeof (WNDCLASSEXW);
wc.lpfnWndProc = (WNDPROC) window_proc;
wc.hInstance = hinstance;
wc.hIcon = LoadIcon (nullptr, IDI_WINLOGO);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wc.hCursor = LoadCursor (nullptr, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
wc.lpszClassName = L"GstD3D12SwapChainSinkExample";
RegisterClassExW (&wc);
AdjustWindowRect (&wr, WS_OVERLAPPEDWINDOW, FALSE);
HWND hwnd = CreateWindowExW (WS_EX_NOREDIRECTIONBITMAP, wc.lpszClassName,
L"D3D12SwapChainSink Example - WinRT",
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
wr.right - wr.left, wr.bottom - wr.top, (HWND) nullptr, (HMENU) nullptr,
hinstance, nullptr);
/* compositor requires dispatcher queue. Creates one for the current main thread */
DispatcherQueueOptions queue_opt = { };
queue_opt.dwSize = sizeof (DispatcherQueueOptions);
queue_opt.threadType = DQTYPE_THREAD_CURRENT;
queue_opt.apartmentType = DQTAT_COM_NONE;
ComPtr < IDispatcherQueueController > queue_ctrl;
HRESULT hr = CreateDispatcherQueueController (queue_opt, &queue_ctrl);
g_assert (SUCCEEDED (hr));
ComPtr<IDispatcherQueue> dqueue;
hr = queue_ctrl->get_DispatcherQueue (&dqueue);
g_assert (SUCCEEDED (hr));
/* Creates compositor */
ComPtr<IInspectable> insp;
HSTRING class_id_hstring;
WindowsCreateString (RuntimeClass_Windows_UI_Composition_Compositor,
wcslen (RuntimeClass_Windows_UI_Composition_Compositor), &class_id_hstring);
hr = RoActivateInstance (class_id_hstring, &insp);
WindowsDeleteString (class_id_hstring);
g_assert (SUCCEEDED (hr));
ComPtr<ICompositor> compositor;
hr = insp.As (&compositor);
g_assert (SUCCEEDED (hr));
ComPtr<ICompositorDesktopInterop> compositor_desktop_interop;
hr = compositor.As (&compositor_desktop_interop);
g_assert (SUCCEEDED (hr));
ComPtr<ICompositorInterop> compositor_interop;
hr = compositor.As (&compositor_interop);
g_assert (SUCCEEDED (hr));
/* Creates compositor target for the main HWND */
ComPtr<IDesktopWindowTarget> desktop_target;
hr = compositor_desktop_interop->CreateDesktopWindowTarget (hwnd,
TRUE, &desktop_target);
g_assert (SUCCEEDED (hr));
ComPtr<ICompositionTarget> target;
hr = desktop_target.As (&target);
g_assert (SUCCEEDED (hr));
/* Creates container visual and put background static color visual */
ComPtr<IContainerVisual> root;
hr = compositor->CreateContainerVisual (&root);
g_assert (SUCCEEDED (hr));
ComPtr<IVisual> root_visual;
hr = root.As (&root_visual);
g_assert (SUCCEEDED (hr));
ComPtr<IVisual2> root_visual2;
hr = root.As (&root_visual2);
g_assert (SUCCEEDED (hr));
Numerics::Vector2 vec2 = { 1.0, 1.0 };
hr = root_visual2->put_RelativeSizeAdjustment (vec2);
g_assert (SUCCEEDED (hr));
hr = target->put_Root (root_visual.Get ());
g_assert (SUCCEEDED (hr));
ABI::Windows::UI::Color bg_color = { };
bg_color.R = 128;
bg_color.G = 128;
bg_color.B = 128;
bg_color.A = 128;
ComPtr<ICompositionColorBrush> bg_color_brush;
hr = compositor->CreateColorBrushWithColor (bg_color, &bg_color_brush);
g_assert (SUCCEEDED (hr));
ComPtr<ICompositionBrush> bg_brush;
hr = bg_color_brush.As (&bg_brush);
g_assert (SUCCEEDED (hr));
ComPtr<ISpriteVisual> bg_sprite_visual;
hr = compositor->CreateSpriteVisual (&bg_sprite_visual);
g_assert (SUCCEEDED (hr));
hr = bg_sprite_visual->put_Brush (bg_brush.Get ());
g_assert (SUCCEEDED (hr));
ComPtr<IVisual> bg_visual;
hr = bg_sprite_visual.As (&bg_visual);
g_assert (SUCCEEDED (hr));
ComPtr<IVisual2> bg_visual2;
hr = bg_sprite_visual.As (&bg_visual2);
g_assert (SUCCEEDED (hr));
hr = bg_visual2->put_RelativeSizeAdjustment (vec2);
g_assert (SUCCEEDED (hr));
ComPtr<IVisualCollection> children;
hr = root->get_Children (&children);
g_assert (SUCCEEDED (hr));
hr = children->InsertAtBottom (bg_visual.Get ());
g_assert (SUCCEEDED (hr));
/* Creates swapchain visual */
ComPtr<ICompositionSurface> swapchain_surface;
hr = compositor_interop->CreateCompositionSurfaceForSwapChain (swapchain,
&swapchain_surface);
g_assert (SUCCEEDED (hr));
ComPtr<ICompositionSurfaceBrush> swapchain_surface_brush;
hr = compositor->CreateSurfaceBrushWithSurface (swapchain_surface.Get (),
&swapchain_surface_brush);
g_assert (SUCCEEDED (hr));
ComPtr<ICompositionBrush> swapchain_brush;
hr = swapchain_surface_brush.As (&swapchain_brush);
g_assert (SUCCEEDED (hr));
/* Place swapchain visual at center */
hr = swapchain_surface_brush->put_HorizontalAlignmentRatio (0.5);
g_assert (SUCCEEDED (hr));
hr = swapchain_surface_brush->put_VerticalAlignmentRatio (0.5);
g_assert (SUCCEEDED (hr));
/* Scale swapchain visual with aspect-ratio preserved */
hr = swapchain_surface_brush->put_Stretch (CompositionStretch_Uniform);
g_assert (SUCCEEDED (hr));
ComPtr<ISpriteVisual> swapchain_sprite_visual;
hr = compositor->CreateSpriteVisual (&swapchain_sprite_visual);
g_assert (SUCCEEDED (hr));
ComPtr<IVisual> swapchain_visual;
hr = swapchain_sprite_visual.As (&swapchain_visual);
g_assert (SUCCEEDED (hr));
vec2.X = 0.5;
vec2.Y = 0.5;
hr = swapchain_visual->put_AnchorPoint (vec2);
g_assert (SUCCEEDED (hr));
ComPtr<IVisual2> swapchain_visual2;
hr = swapchain_sprite_visual.As (&swapchain_visual2);
g_assert (SUCCEEDED (hr));
hr = swapchain_visual2->put_RelativeSizeAdjustment (vec2);
g_assert (SUCCEEDED (hr));
Numerics::Vector3 vec3 = { 0.5, 0.5, 0.0 };
hr = swapchain_visual2->put_RelativeOffsetAdjustment (vec3);
g_assert (SUCCEEDED (hr));
hr = swapchain_sprite_visual->put_Brush (swapchain_brush.Get ());
g_assert (SUCCEEDED (hr));
hr = children->InsertAtTop (swapchain_visual.Get ());
g_assert (SUCCEEDED (hr));
/* Compositor and visual tree are configured, run pipeline */
gst_element_set_state (pipeline, GST_STATE_PLAYING);
auto bus = gst_element_get_bus (pipeline);
MSG msg = { };
while (msg.message != WM_QUIT) {
if (PeekMessage (&msg, nullptr, 0, 0, PM_REMOVE)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
auto gst_msg = gst_bus_pop (bus);
if (gst_msg) {
switch (GST_MESSAGE_TYPE (gst_msg)) {
case GST_MESSAGE_ERROR:
{
GError *err;
gchar *dbg;
gst_message_parse_error (gst_msg, &err, &dbg);
gst_printerrln ("ERROR %s", err->message);
if (dbg != nullptr)
gst_printerrln ("ERROR debug information: %s", dbg);
g_clear_error (&err);
g_free (dbg);
PostQuitMessage (0);
break;
}
case GST_MESSAGE_EOS:
{
gst_println ("Got EOS");
PostQuitMessage (0);
break;
}
default:
break;
}
gst_message_unref (gst_msg);
}
}
gst_object_unref (bus);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
}
int
main (int argc, char ** argv)
{
gst_init (nullptr, nullptr);
RoInitialize (RO_INIT_SINGLETHREADED);
app_main ();
RoUninitialize ();
gst_deinit ();
return 0;
}

View file

@ -5,7 +5,15 @@ endif
have_d2d_h = cc.has_header('d2d1_3.h')
have_dwrite_h = cc.has_header('dwrite.h')
have_d3d12video_h = cc.has_header('d3d12video.h')
have_dcomp_h = cc.has_header('dcomp.h')
have_d3d11_h = cc.has_header('d3d11.h')
have_dxgi_h = cc.has_header('dxgi.h')
dwrite_dep = cc.find_library('dwrite', required: false)
dcomp_dep = cc.find_library('dcomp', required: false)
d3d11_dep = cc.find_library('d3d11', required: false)
dxgi_dep = cc.find_library('dxgi', required: false)
runtimeobject_dep = cc.find_library('runtimeobject', required: false)
coremessaging_lib = cc.find_library('coremessaging', required: false)
executable('d3d12enc-dynamic-reconfigure',
['d3d12enc-dynamic-reconfigure.c', '../key-handler.c'],
@ -34,3 +42,42 @@ if gstd3d12_dep.found()
)
endif
endif
if cc.get_id() == 'msvc' and have_dcomp_h and dcomp_dep.found() and \
have_d3d11_h and d3d11_dep.found() and have_dxgi_h and dxgi_dep.found()
executable('d3d12swapchainsink-win32', 'd3d12swapchainsink-win32.cpp',
c_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'],
cpp_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'],
include_directories : [configinc, libsinc],
dependencies: [gst_dep, gstvideo_dep, dcomp_dep, d3d11_dep, dxgi_dep],
install: false,
)
endif
have_winrt_comp_headers = true
winrt_comp_headers = [
'winstring.h',
'roapi.h',
'dispatcherqueue.h',
'windows.system.h',
'windows.ui.composition.h',
'windows.ui.composition.interop.h',
'windows.ui.composition.desktop.h',
]
foreach h: winrt_comp_headers
if not cc.has_header(h)
have_winrt_comp_headers = false
endif
endforeach
if cc.get_id() == 'msvc' and have_winrt_comp_headers and \
runtimeobject_dep.found() and coremessaging_lib.found()
executable('d3d12swapchainsink-winrt', 'd3d12swapchainsink-winrt.cpp',
c_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'],
cpp_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'],
include_directories : [configinc, libsinc],
dependencies: [gst_dep, gstvideo_dep, runtimeobject_dep, coremessaging_lib],
install: false,
)
endif