d3d12videosink: Add overlay signal to support d3d12/d3d11/d2d overlay

Conceptually identical to the present signal of d3d11videosink.
This signal will be emitted with current render target
(i.e., swapchain backbuffer) and command queue. Signal handler
can record GPU commands for an overlay image or to blend
an image to the render target.

In addition to d3d12 resources, videosink will send
d3d11 and d2d resources depending on "overlay-mode"
property, so that signal handler can render by using
preferred/required DirectX API.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6838>
This commit is contained in:
Seungha Yang 2024-05-10 22:59:15 +09:00 committed by GStreamer Marge Bot
parent 4d779d7de8
commit 84aecab150
6 changed files with 955 additions and 54 deletions

View file

@ -28,6 +28,61 @@
#include <atomic>
#include <string>
/**
* GstD3D12VideoSinkDisplayFormat:
*
* Swapchain display format
*
* Since: 1.26
*/
#define GST_TYPE_D3D12_VIDEO_SINK_DISPLAY_FORMAT (gst_d3d12_video_sink_display_format_type())
static GType
gst_d3d12_video_sink_display_format_type (void)
{
static GType format_type = 0;
GST_D3D12_CALL_ONCE_BEGIN {
static const GEnumValue format_types[] = {
/**
* GstD3D12VideoSinkDisplayFormat::unknown:
*
* Since: 1.26
*/
{DXGI_FORMAT_UNKNOWN, "DXGI_FORMAT_UNKNOWN", "unknown"},
/**
* GstD3D12VideoSinkDisplayFormat::r10g10b10a2-unorm:
*
* Since: 1.26
*/
{DXGI_FORMAT_R10G10B10A2_UNORM,
"DXGI_FORMAT_R10G10B10A2_UNORM", "r10g10b10a2-unorm"},
/**
* GstD3D12VideoSinkDisplayFormat::r8g8b8a8-unorm:
*
* Since: 1.26
*/
{DXGI_FORMAT_R8G8B8A8_UNORM,
"DXGI_FORMAT_R8G8B8A8_UNORM", "r8g8b8a8-unorm"},
/**
* GstD3D12VideoSinkDisplayFormat::b8g8r8a8-unorm:
*
* Since: 1.26
*/
{DXGI_FORMAT_B8G8R8A8_UNORM,
"DXGI_FORMAT_B8G8R8A8_UNORM", "b8g8r8a8-unorm"},
{0, nullptr, nullptr},
};
format_type = g_enum_register_static ("GstD3D12VideoSinkDisplayFormat",
format_types);
} GST_D3D12_CALL_ONCE_END;
return format_type;
}
enum
{
PROP_0,
@ -49,6 +104,8 @@ enum
PROP_SAMPLING_METHOD,
PROP_GAMMA_MODE,
PROP_PRIMARIES_MODE,
PROP_OVERLAY_MODE,
PROP_DISPLAY_FORMAT,
};
#define DEFAULT_ADAPTER -1
@ -66,6 +123,16 @@ enum
#define DEFAULT_SAMPLING_METHOD GST_D3D12_SAMPLING_METHOD_BILINEAR
#define DEFAULT_GAMMA_MODE GST_VIDEO_GAMMA_MODE_NONE
#define DEFAULT_PRIMARIES_MODE GST_VIDEO_PRIMARIES_MODE_NONE
#define DEFAULT_OVERLAY_MODE GST_D3D12_WINDOW_OVERLAY_NONE
#define DEFAULT_DISPLAY_FORMAT DXGI_FORMAT_UNKNOWN
enum
{
SIGNAL_OVERLAY,
SIGNAL_LAST
};
static guint d3d12_video_sink_signals[SIGNAL_LAST] = { 0, };
static GstStaticPadTemplate sink_template =
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
@ -135,6 +202,8 @@ struct GstD3D12VideoSinkPrivate
GstVideoGammaMode gamma_mode = DEFAULT_GAMMA_MODE;
GstVideoPrimariesMode primaries_mode = DEFAULT_PRIMARIES_MODE;
GstD3D12SamplingMethod sampling_method = DEFAULT_SAMPLING_METHOD;
GstD3D12WindowOverlayMode overlay_mode = DEFAULT_OVERLAY_MODE;
DXGI_FORMAT display_format = DEFAULT_DISPLAY_FORMAT;
};
/* *INDENT-ON* */
@ -147,9 +216,9 @@ struct _GstD3D12VideoSink
GstD3D12VideoSinkPrivate *priv;
};
static void gst_d3d12_videosink_set_property (GObject * object, guint prop_id,
static void gst_d3d12_video_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_d3d12_videosink_get_property (GObject * object, guint prop_id,
static void gst_d3d12_video_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_d3d12_video_sink_dispose (GObject * object);
static void gst_d3d12_video_sink_finalize (GObject * object);
@ -180,6 +249,10 @@ static void gst_d3d12_video_sink_mouse_event (GstD3D12Window * window,
GstD3D12VideoSink * self);
static void gst_d3d12_video_sink_on_fullscreen (GstD3D12Window * window,
gboolean is_fullscreen, GstD3D12VideoSink * self);
static void gst_d3d12_video_sink_on_overlay (GstD3D12Window * window,
gpointer command_queue, gpointer resource12, gpointer device11on12,
gpointer resource11, gpointer context2d, gpointer viewport,
GstD3D12VideoSink * self);
static void
gst_d3d12_video_sink_video_overlay_init (GstVideoOverlayInterface * iface);
@ -204,8 +277,8 @@ gst_d3d12_video_sink_class_init (GstD3D12VideoSinkClass * klass)
auto basesink_class = GST_BASE_SINK_CLASS (klass);
auto videosink_class = GST_VIDEO_SINK_CLASS (klass);
object_class->set_property = gst_d3d12_videosink_set_property;
object_class->get_property = gst_d3d12_videosink_get_property;
object_class->set_property = gst_d3d12_video_sink_set_property;
object_class->get_property = gst_d3d12_video_sink_get_property;
object_class->finalize = gst_d3d12_video_sink_dispose;
object_class->finalize = gst_d3d12_video_sink_finalize;
@ -322,6 +395,87 @@ gst_d3d12_video_sink_class_init (GstD3D12VideoSinkClass * klass)
DEFAULT_SAMPLING_METHOD,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
/**
* GstD3D12VideoSink:overlay-mode:
*
* Overly signal type
*
* Since: 1.26
*/
g_object_class_install_property (object_class, PROP_OVERLAY_MODE,
g_param_spec_flags ("overlay-mode", "Overlay Mode",
"Overlay signal type to use", GST_TYPE_D3D12_WINDOW_OVERLAY_MODE,
DEFAULT_OVERLAY_MODE,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
/**
* GstD3D12VideoSink:display-format:
*
* Swapchain display format
*
* Since: 1.26
*/
g_object_class_install_property (object_class, PROP_DISPLAY_FORMAT,
g_param_spec_enum ("display-format", "Display Format",
"Swapchain display format", GST_TYPE_D3D12_VIDEO_SINK_DISPLAY_FORMAT,
DEFAULT_DISPLAY_FORMAT, (GParamFlags) (G_PARAM_READWRITE |
GST_PARAM_MUTABLE_READY | G_PARAM_STATIC_STRINGS)));
/**
* GstD3D12VideoSink::overlay:
* @d3d12videosink: the d3d12videosink element that emitted the signal
* @command_queue: ID3D12CommandQueue
* @resource12: ID3D12Resource
* @device11on12: (nullable): ID3D11On12Device
* @resource11: (nullable): ID3D11Texture2D
* @context2d: (nullable): ID2D1DeviceContext2
* @viewport: (nullable): D3D12_RECT, d3d12videosink's current viewport
*
* Signal emitted with Direct3D12, Direct3D11 and Direct2D resources
* associated with swapchain backbuffer.
*
* This signal is emitted from the streaming thread if "overlay-mode" property
* includes GST_D3D12_WINDOW_OVERLAY_D3D12. The @resource12 is a render target
* backbuffer of the swapchain. The Resource state of @resource12 when this
* signal is emitted will be always D3D12_RESOURCE_STATE_RENDER_TARGET and
* signal handler should make sure the state is
* D3D12_RESOURCE_STATE_RENDER_TARGET when signal handler is returned,
* so that state change to final D3D12_RESOURCE_STATE_PRESENT can be
* processed by videosink.
*
* In addition to the d2d12 resources, if "overlay-mode" includes
* GST_D3D12_WINDOW_OVERLAY_D3D11 flag and d3d11on12 API is supported by
* system, @device11on12 and @resource11 will be valid handles.
* Singla handler should not assume the @device11on12 and @resource11
* are always valid handle since d3d11on12 API may not be supported.
* The @resource11 is wrapped resource created via
* ID3D11On12Device::CreateWrappedResource(). Thus, signal handler must follow
* required steps for d3d11on12 device, for example,
* ID3D11On12Device::AcquireWrappedResources() must be called before recoding
* GPU commands. Once GPU commands are recoded via d3d11 or d2d APIs,
* the resource should be released via
* ID3D11On12Device::ReleaseWrappedResources(), and then
* ID3D11DeviceContext::Flush() must be called in the signal handler.
*
* If "overlay-mode" is GST_D3D12_WINDOW_OVERLAY_D2D and d2d device is
* available, @context2d will be valid handle. When this signal is emitted,
* @context2d has configured render target already. The D2D render target
* is also a resource derived from @resource11 and it's swapchain's backbuffer.
* The same step for d3d11 resource (i.e., acquire, release, and flush)
* is required for d2d as well.
*
* Since the resource is swapchain's backbuffer, signal handler must not hold
* any derived resources such as ID3D11RenderTargetView, so that videosink
* can clear swapchain resources and resize anytime it's needed.
*
* Since: 1.26
*/
d3d12_video_sink_signals[SIGNAL_OVERLAY] =
g_signal_new_class_handler ("overlay", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, nullptr, nullptr, nullptr, nullptr,
G_TYPE_NONE, 6, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER,
G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER);
element_class->set_context =
GST_DEBUG_FUNCPTR (gst_d3d12_video_sink_set_context);
@ -363,6 +517,8 @@ gst_d3d12_video_sink_init (GstD3D12VideoSink * self)
G_CALLBACK (gst_d3d12_video_sink_mouse_event), self);
g_signal_connect (priv->window, "fullscreen",
G_CALLBACK (gst_d3d12_video_sink_on_fullscreen), self);
g_signal_connect (priv->window, "overlay",
G_CALLBACK (gst_d3d12_video_sink_on_overlay), self);
}
static void
@ -388,7 +544,7 @@ gst_d3d12_video_sink_finalize (GObject * object)
}
static void
gst_d3d12_videosink_set_property (GObject * object, guint prop_id,
gst_d3d12_video_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
auto self = GST_D3D12_VIDEO_SINK (object);
@ -485,6 +641,14 @@ gst_d3d12_videosink_set_property (GObject * object, guint prop_id,
}
break;
}
case PROP_OVERLAY_MODE:
priv->overlay_mode =
(GstD3D12WindowOverlayMode) g_value_get_flags (value);
gst_d3d12_window_set_overlay_mode (priv->window, priv->overlay_mode);
break;
case PROP_DISPLAY_FORMAT:
priv->display_format = (DXGI_FORMAT) g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -492,7 +656,7 @@ gst_d3d12_videosink_set_property (GObject * object, guint prop_id,
}
static void
gst_d3d12_videosink_get_property (GObject * object, guint prop_id,
gst_d3d12_video_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
auto self = GST_D3D12_VIDEO_SINK (object);
@ -554,6 +718,12 @@ gst_d3d12_videosink_get_property (GObject * object, guint prop_id,
case PROP_SAMPLING_METHOD:
g_value_set_enum (value, priv->sampling_method);
break;
case PROP_OVERLAY_MODE:
g_value_set_flags (value, priv->overlay_mode);
break;
case PROP_DISPLAY_FORMAT:
g_value_set_enum (value, priv->display_format);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -829,7 +999,7 @@ gst_d3d12_video_sink_update_window (GstD3D12VideoSink * self)
auto ret = gst_d3d12_window_prepare (priv->window, self->device,
window_handle, GST_VIDEO_SINK_WIDTH (self),
GST_VIDEO_SINK_HEIGHT (self), priv->caps, config);
GST_VIDEO_SINK_HEIGHT (self), priv->caps, config, priv->display_format);
if (ret != GST_FLOW_OK) {
if (ret == GST_FLOW_FLUSHING) {
GST_WARNING_OBJECT (self, "We are flushing");
@ -1247,3 +1417,13 @@ gst_d3d12_video_sink_navigation_init (GstNavigationInterface * iface)
{
iface->send_event_simple = gst_d3d12_video_sink_navigation_send_event;
}
static void
gst_d3d12_video_sink_on_overlay (GstD3D12Window * window,
gpointer command_queue, gpointer resource12, gpointer device11on12,
gpointer resource11, gpointer context2d, gpointer viewport,
GstD3D12VideoSink * self)
{
g_signal_emit (self, d3d12_video_sink_signals[SIGNAL_OVERLAY], 0,
command_queue, resource12, device11on12, resource11, context2d, viewport);
}

View file

@ -24,6 +24,8 @@
#include "gstd3d12window.h"
#include "gstd3d12overlaycompositor.h"
#include <directx/d3dx12.h>
#include <d3d11on12.h>
#include <d2d1_3.h>
#include <mutex>
#include <condition_variable>
#include <vector>
@ -65,24 +67,57 @@ enum
SIGNAL_KEY_EVENT,
SIGNAL_MOUSE_EVENT,
SIGNAL_FULLSCREEN,
SIGNAL_OVERLAY,
SIGNAL_LAST
};
static guint d3d12_window_signals[SIGNAL_LAST] = { 0, };
GType
gst_d3d12_window_overlay_mode_get_type (void)
{
static GType mode_type = 0;
GST_D3D12_CALL_ONCE_BEGIN {
static const GFlagsValue mode_types[] = {
{GST_D3D12_WINDOW_OVERLAY_NONE, "None", "none"},
{GST_D3D12_WINDOW_OVERLAY_D3D12,
"Emits present signal with Direct3D12 resources", "d3d12"},
{GST_D3D12_WINDOW_OVERLAY_D3D11,
"Emits present signal with Direct3D12/11 resources", "d3d11"},
{GST_D3D12_WINDOW_OVERLAY_D2D,
"Emit present signal with Direct3D12/11 and Direct2D resources",
"d2d"},
{0, nullptr, nullptr},
};
mode_type = g_flags_register_static ("GstD3D12WindowOverlayMode",
mode_types);
} GST_D3D12_CALL_ONCE_END;
return mode_type;
}
/* *INDENT-OFF* */
struct SwapBuffer
{
SwapBuffer (GstBuffer * buf)
SwapBuffer (GstBuffer * buf, ID3D12Resource * res)
{
backbuf = buf;
resource = res;
}
~SwapBuffer ()
{
d2d_target = nullptr;
wrapped_resource = nullptr;
resource = nullptr;
gst_clear_buffer (&backbuf);
}
ComPtr<ID2D1Bitmap1> d2d_target;
ComPtr<ID3D11Texture2D> wrapped_resource;
ComPtr<ID3D12Resource> resource;
GstBuffer *backbuf = nullptr;
gboolean first = TRUE;
};
@ -94,21 +129,145 @@ struct DeviceContext
{
event_handle = CreateEventEx (nullptr, nullptr, 0, EVENT_ALL_ACCESS);
device = (GstD3D12Device *) gst_object_ref (dev);
auto device_handle = gst_d3d12_device_get_device_handle (device);
ca_pool = gst_d3d12_command_allocator_pool_new (device_handle,
D3D12_COMMAND_LIST_TYPE_DIRECT);
if (!ca_pool) {
GST_ERROR_OBJECT (device, "Couldn't create command allocator pool");
return;
}
bool EnsureD3D11 ()
{
if (device11on12)
return true;
auto unknown = gst_d3d12_device_get_11on12_handle (device);
if (!unknown)
return false;
unknown->QueryInterface (IID_PPV_ARGS (&device11on12));
device11on12.As (&device11);
device11->GetImmediateContext (&context11);
return true;
}
bool EnsureD2D ()
{
if (context2d)
return true;
if (!EnsureD3D11 ())
return false;
HRESULT hr;
if (!factory2d) {
hr = D2D1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED,
IID_PPV_ARGS (&factory2d));
if (FAILED (hr))
return false;
}
initialized = true;
GstD3D12Device11on12LockGuard lk (device);
if (!device2d) {
ComPtr<IDXGIDevice> device_dxgi;
hr = device11.As (&device_dxgi);
if (FAILED (hr))
return false;
hr = factory2d->CreateDevice (device_dxgi.Get (), &device2d);
if (FAILED (hr))
return false;
}
if (!context2d) {
hr = device2d->CreateDeviceContext (D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
&context2d);
if (FAILED (hr))
return false;
}
return true;
}
bool EnsureD3D11RenderTarget (std::shared_ptr<SwapBuffer> swapbuf)
{
if (swapbuf->wrapped_resource)
return true;
if (!EnsureD3D11 ())
return false;
D3D11_RESOURCE_FLAGS d3d11_flags = { };
d3d11_flags.BindFlags = D3D11_BIND_RENDER_TARGET;
auto hr = device11on12->CreateWrappedResource (swapbuf->resource.Get (),
&d3d11_flags, D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_RENDER_TARGET,
IID_PPV_ARGS (&swapbuf->wrapped_resource));
if (FAILED (hr))
return false;
return true;
}
bool EnsureD2DRenderTarget (std::shared_ptr<SwapBuffer> swapbuf)
{
if (swapbuf->d2d_target)
return true;
if (!EnsureD2D ())
return false;
if (!EnsureD3D11RenderTarget (swapbuf))
return false;
GstD3D12Device11on12LockGuard lk (device);
ComPtr<IDXGISurface> surface;
auto hr = swapbuf->wrapped_resource.As (&surface);
if (FAILED (hr))
return false;
D2D1_BITMAP_PROPERTIES1 props = D2D1::BitmapProperties1(
D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED));
hr = context2d->CreateBitmapFromDxgiSurface (surface.Get (), &props,
&swapbuf->d2d_target);
if (FAILED (hr))
return false;
return true;
}
void WaitGpu ()
{
gst_d3d12_device_fence_wait (device, D3D12_COMMAND_LIST_TYPE_DIRECT,
G_MAXUINT64, event_handle);
prev_fence_val = { };
}
void ReleaseSizeDependentResources ()
{
if (fence_val != 0)
WaitGpu ();
if (context11)
gst_d3d12_device_11on12_lock (device);
swap_buffers.clear ();
gst_clear_buffer (&msaa_buf);
if (context2d)
context2d->SetTarget (nullptr);
if (context11) {
context11->ClearState ();
context11->Flush ();
gst_d3d12_device_11on12_unlock (device);
}
}
~DeviceContext ()
{
WaitGpu ();
ReleaseSizeDependentResources ();
CloseHandle (event_handle);
@ -116,20 +275,9 @@ struct DeviceContext
gst_clear_buffer (&cached_buf);
gst_clear_object (&conv);
gst_clear_object (&comp);
gst_clear_buffer (&msaa_buf);
gst_clear_object (&device);
}
void WaitGpu ()
{
if (!device)
return;
gst_d3d12_device_fence_wait (device, D3D12_COMMAND_LIST_TYPE_DIRECT,
G_MAXUINT64, event_handle);
prev_fence_val = { };
}
gboolean BeforeRendering ()
{
UINT64 fence_val_to_wait = 0;
@ -167,10 +315,15 @@ struct DeviceContext
GstBuffer *cached_buf = nullptr;
GstD3D12Device *device = nullptr;
GstD3D12CommandAllocatorPool *ca_pool = nullptr;
ComPtr<ID3D11On12Device> device11on12;
ComPtr<ID3D11Device> device11;
ComPtr<ID3D11DeviceContext> context11;
ComPtr<ID2D1Factory3> factory2d;
ComPtr<ID2D1Device2> device2d;
ComPtr<ID2D1DeviceContext2> context2d;
UINT64 fence_val = 0;
std::queue<UINT64> prev_fence_val;
HANDLE event_handle;
bool initialized = false;
HANDLE event_handle = nullptr;
};
struct GstD3D12WindowPrivate
@ -239,6 +392,7 @@ struct GstD3D12WindowPrivate
gboolean update_title = FALSE;
GstD3D12MSAAMode msaa = GST_D3D12_MSAA_DISABLED;
GstD3D12WindowOverlayMode overlay_mode = GST_D3D12_WINDOW_OVERLAY_NONE;
/* Win32 window handles */
std::mutex hwnd_lock;
@ -280,21 +434,27 @@ gst_d3d12_window_class_init (GstD3D12WindowClass * klass)
object_class->finalize = gst_d3d12_window_finalize;
d3d12_window_signals[SIGNAL_KEY_EVENT] =
g_signal_new ("key-event", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, nullptr, nullptr, nullptr,
g_signal_new_class_handler ("key-event", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, nullptr, nullptr, nullptr, nullptr,
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
d3d12_window_signals[SIGNAL_MOUSE_EVENT] =
g_signal_new ("mouse-event", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, nullptr, nullptr, nullptr,
g_signal_new_class_handler ("mouse-event", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, nullptr, nullptr, nullptr, nullptr,
G_TYPE_NONE, 5, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE,
G_TYPE_UINT);
d3d12_window_signals[SIGNAL_FULLSCREEN] =
g_signal_new ("fullscreen", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, nullptr, nullptr, nullptr,
g_signal_new_class_handler ("fullscreen", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, nullptr, nullptr, nullptr, nullptr,
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
d3d12_window_signals[SIGNAL_OVERLAY] =
g_signal_new_class_handler ("overlay", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, nullptr, nullptr, nullptr, nullptr,
G_TYPE_NONE, 6, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER,
G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER);
GST_DEBUG_CATEGORY_INIT (gst_d3d12_window_debug,
"d3d12window", 0, "d3d12window");
}
@ -1153,10 +1313,7 @@ gst_d3d12_window_on_resize (GstD3D12Window * self)
if (!priv->ctx)
return GST_FLOW_OK;
if (priv->ctx->fence_val != 0)
priv->ctx->WaitGpu ();
priv->ctx->swap_buffers.clear ();
gst_clear_buffer (&priv->ctx->msaa_buf);
priv->ctx->ReleaseSizeDependentResources ();
DXGI_SWAP_CHAIN_DESC desc = { };
priv->ctx->swapchain->GetDesc (&desc);
@ -1182,7 +1339,8 @@ gst_d3d12_window_on_resize (GstD3D12Window * self)
backbuf.Get (), 0, nullptr, nullptr);
auto buf = gst_buffer_new ();
gst_buffer_append_memory (buf, mem);
priv->ctx->swap_buffers.push_back (std::make_shared < SwapBuffer > (buf));
auto swapbuf = std::make_shared < SwapBuffer > (buf, backbuf.Get ());
priv->ctx->swap_buffers.push_back (swapbuf);
}
guint sample_count = 1;
@ -1263,7 +1421,7 @@ gst_d3d12_window_on_resize (GstD3D12Window * self)
GstFlowReturn
gst_d3d12_window_prepare (GstD3D12Window * window, GstD3D12Device * device,
guintptr window_handle, guint display_width, guint display_height,
GstCaps * caps, GstStructure * config)
GstCaps * caps, GstStructure * config, DXGI_FORMAT display_format)
{
auto priv = window->priv;
GstVideoInfo in_info;
@ -1271,7 +1429,10 @@ gst_d3d12_window_prepare (GstD3D12Window * window, GstD3D12Device * device,
priv->display_format = DXGI_FORMAT_R8G8B8A8_UNORM;
gst_video_info_from_caps (&in_info, caps);
if (GST_VIDEO_INFO_COMP_DEPTH (&in_info, 0) > 8) {
if (display_format != DXGI_FORMAT_UNKNOWN) {
priv->display_format = display_format;
format = gst_d3d12_dxgi_format_to_gst (display_format);
} else if (GST_VIDEO_INFO_COMP_DEPTH (&in_info, 0) > 8) {
auto device_handle = gst_d3d12_device_get_device_handle (device);
D3D12_FEATURE_DATA_FORMAT_SUPPORT format_support = { };
const D3D12_FORMAT_SUPPORT1 support_flags =
@ -1313,13 +1474,6 @@ gst_d3d12_window_prepare (GstD3D12Window * window, GstD3D12Device * device,
if (!priv->ctx) {
auto ctx = std::make_unique < DeviceContext > (device);
if (!ctx->initialized) {
GST_ERROR_OBJECT (window, "Couldn't initialize device context");
if (config)
gst_structure_free (config);
return GST_FLOW_ERROR;
}
DXGI_SWAP_CHAIN_DESC1 desc = { };
desc.Format = priv->display_format;
@ -1514,9 +1668,6 @@ gst_d3d12_window_set_buffer (GstD3D12Window * window, GstBuffer * buffer)
&priv->output_rect);
}
g_object_set (priv->ctx->conv, "fill-border", swapbuf->first, nullptr);
swapbuf->first = FALSE;
guint64 overlay_fence_val = 0;
gst_d3d12_overlay_compositor_upload (priv->ctx->comp, priv->ctx->cached_buf,
&overlay_fence_val);
@ -1578,6 +1729,15 @@ gst_d3d12_window_set_buffer (GstD3D12Window * window, GstBuffer * buffer)
cl->ResourceBarrier (1, &barrier);
}
if (swapbuf->first || priv->overlay_mode != GST_D3D12_WINDOW_OVERLAY_NONE) {
FLOAT clear_color[4] = { 0, 0, 0, 1 };
auto rtv_heap = gst_d3d12_memory_get_render_target_view_heap (mem);
auto cpu_handle = GetCPUDescriptorHandleForHeapStart (rtv_heap);
cl->ClearRenderTargetView (cpu_handle, clear_color, 0, nullptr);
}
swapbuf->first = FALSE;
auto cq = gst_d3d12_device_get_command_queue (priv->ctx->device,
D3D12_COMMAND_LIST_TYPE_DIRECT);
auto cq_handle = gst_d3d12_command_queue_get_handle (cq);
@ -1597,6 +1757,32 @@ gst_d3d12_window_set_buffer (GstD3D12Window * window, GstBuffer * buffer)
return GST_FLOW_ERROR;
}
D3D12_RESOURCE_STATES state_after = D3D12_RESOURCE_STATE_COMMON;
GstD3D12WindowOverlayMode selected_overlay_mode =
GST_D3D12_WINDOW_OVERLAY_NONE;
bool signal_with_lock = false;
bool set_d2d_target = false;
if ((priv->overlay_mode & GST_D3D12_WINDOW_OVERLAY_D3D12) != 0) {
selected_overlay_mode |= GST_D3D12_WINDOW_OVERLAY_D3D12;
state_after = D3D12_RESOURCE_STATE_RENDER_TARGET;
}
if ((priv->overlay_mode & GST_D3D12_WINDOW_OVERLAY_D3D11) ==
GST_D3D12_WINDOW_OVERLAY_D3D11 &&
priv->ctx->EnsureD3D11RenderTarget (swapbuf)) {
selected_overlay_mode |= GST_D3D12_WINDOW_OVERLAY_D3D11;
signal_with_lock = true;
}
if ((priv->overlay_mode & GST_D3D12_WINDOW_OVERLAY_D2D) ==
GST_D3D12_WINDOW_OVERLAY_D2D &&
(priv->display_format == DXGI_FORMAT_R8G8B8A8_UNORM ||
priv->display_format == DXGI_FORMAT_B8G8R8A8_UNORM) &&
priv->ctx->EnsureD2DRenderTarget (swapbuf)) {
selected_overlay_mode |= GST_D3D12_WINDOW_OVERLAY_D2D;
set_d2d_target = true;
}
if (msaa_resource) {
std::vector < D3D12_RESOURCE_BARRIER > barriers;
barriers.push_back (CD3DX12_RESOURCE_BARRIER::Transition (msaa_resource,
@ -1614,9 +1800,9 @@ gst_d3d12_window_set_buffer (GstD3D12Window * window, GstBuffer * buffer)
D3D12_RESOURCE_STATE_RESOLVE_SOURCE,
D3D12_RESOURCE_STATE_RENDER_TARGET));
barriers.push_back (CD3DX12_RESOURCE_BARRIER::Transition (backbuf_texture,
D3D12_RESOURCE_STATE_RESOLVE_DEST, D3D12_RESOURCE_STATE_COMMON));
D3D12_RESOURCE_STATE_RESOLVE_DEST, state_after));
cl->ResourceBarrier (barriers.size (), barriers.data ());
} else {
} else if (state_after == D3D12_RESOURCE_STATE_COMMON) {
D3D12_RESOURCE_BARRIER barrier =
CD3DX12_RESOURCE_BARRIER::Transition (backbuf_texture,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COMMON);
@ -1650,6 +1836,71 @@ gst_d3d12_window_set_buffer (GstD3D12Window * window, GstBuffer * buffer)
fence_data, (GDestroyNotify) gst_d3d12_fence_data_unref);
priv->backbuf_rendered = TRUE;
if (selected_overlay_mode != GST_D3D12_WINDOW_OVERLAY_NONE) {
D3D12_RECT viewport = priv->dirty_rect;
if (signal_with_lock)
gst_d3d12_device_11on12_lock (window->device);
if (set_d2d_target)
priv->ctx->context2d->SetTarget (swapbuf->d2d_target.Get ());
g_signal_emit (window, d3d12_window_signals[SIGNAL_OVERLAY], 0,
cq_handle, backbuf_texture, priv->ctx->device11on12.Get (),
swapbuf->wrapped_resource.Get (), priv->ctx->context2d.Get (),
&viewport);
if (signal_with_lock)
gst_d3d12_device_11on12_unlock (window->device);
}
if (state_after != D3D12_RESOURCE_STATE_COMMON) {
if (!gst_d3d12_command_allocator_pool_acquire (priv->ctx->ca_pool, &gst_ca)) {
GST_ERROR_OBJECT (window, "Couldn't acquire command allocator");
return GST_FLOW_ERROR;
}
ca = gst_d3d12_command_allocator_get_handle (gst_ca);
hr = ca->Reset ();
if (!gst_d3d12_result (hr, priv->ctx->device)) {
GST_ERROR_OBJECT (window, "Couldn't reset command allocator");
gst_d3d12_command_allocator_unref (gst_ca);
return GST_FLOW_ERROR;
}
hr = cl->Reset (ca, nullptr);
if (!gst_d3d12_result (hr, priv->ctx->device)) {
GST_ERROR_OBJECT (window, "Couldn't reset command list");
gst_d3d12_command_allocator_unref (gst_ca);
return GST_FLOW_ERROR;
}
gst_d3d12_fence_data_pool_acquire (priv->fence_data_pool, &fence_data);
gst_d3d12_fence_data_add_notify_mini_object (fence_data, gst_ca);
D3D12_RESOURCE_BARRIER barrier =
CD3DX12_RESOURCE_BARRIER::Transition (backbuf_texture,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COMMON);
cl->ResourceBarrier (1, &barrier);
hr = cl->Close ();
if (!gst_d3d12_result (hr, priv->ctx->device)) {
GST_ERROR_OBJECT (window, "Couldn't close command list");
gst_d3d12_fence_data_unref (fence_data);
return GST_FLOW_ERROR;
}
hr = gst_d3d12_command_queue_execute_command_lists (cq,
1, cmd_list, &priv->ctx->fence_val);
if (!gst_d3d12_result (hr, priv->ctx->device)) {
GST_ERROR_OBJECT (window, "Signal failed");
gst_d3d12_fence_data_unref (fence_data);
return GST_FLOW_ERROR;
}
gst_d3d12_command_queue_set_notify (cq, priv->ctx->fence_val,
fence_data, (GDestroyNotify) gst_d3d12_fence_data_unref);
}
priv->ctx->AfterRendering ();
return GST_FLOW_OK;
@ -1839,3 +2090,12 @@ gst_d3d12_window_set_msaa (GstD3D12Window * window, GstD3D12MSAAMode msaa)
gst_d3d12_window_on_resize (window);
}
}
void
gst_d3d12_window_set_overlay_mode (GstD3D12Window * window,
GstD3D12WindowOverlayMode mode)
{
auto priv = window->priv;
std::lock_guard < std::recursive_mutex > lk (priv->lock);
priv->overlay_mode = mode;
}

View file

@ -39,6 +39,19 @@ enum GstD3D12WindowState
GST_D3D12_WINDOW_STATE_CLOSED,
};
enum GstD3D12WindowOverlayMode
{
GST_D3D12_WINDOW_OVERLAY_NONE = 0,
GST_D3D12_WINDOW_OVERLAY_D3D12 = 0x1,
GST_D3D12_WINDOW_OVERLAY_D3D11 = 0x3,
GST_D3D12_WINDOW_OVERLAY_D2D = 0x7,
};
DEFINE_ENUM_FLAG_OPERATORS (GstD3D12WindowOverlayMode);
#define GST_TYPE_D3D12_WINDOW_OVERLAY_MODE (gst_d3d12_window_overlay_mode_get_type())
GType gst_d3d12_window_overlay_mode_get_type (void);
GstD3D12Window * gst_d3d12_window_new (void);
guintptr gst_d3d12_window_get_window_handle (GstD3D12Window * window);
@ -51,7 +64,8 @@ GstFlowReturn gst_d3d12_window_prepare (GstD3D12Window * window,
guint display_width,
guint display_height,
GstCaps * caps,
GstStructure * config);
GstStructure * config,
DXGI_FORMAT display_format);
void gst_d3d12_window_unprepare (GstD3D12Window * window);
@ -97,5 +111,8 @@ void gst_d3d12_window_set_fullscreen (GstD3D12Window * window,
void gst_d3d12_window_set_msaa (GstD3D12Window * window,
GstD3D12MSAAMode msaa);
void gst_d3d12_window_set_overlay_mode (GstD3D12Window * window,
GstD3D12WindowOverlayMode mode);
G_END_DECLS

View file

@ -127,7 +127,7 @@ endif
d3d12_headers = [
'd3d11.h',
'd2d1.h',
'd2d1_3.h',
]
have_d3d12_headers = true

View file

@ -0,0 +1,427 @@
/* 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 <gst/d3d12/gstd3d12.h>
#include <d3d11.h>
#include <d2d1_3.h>
#include <d3d11on12.h>
#include <dwrite.h>
#include <wrl.h>
#include <string>
#include <locale>
#include <codecvt>
/* *INDENT-OFF* */
using namespace Microsoft::WRL;
struct OverlayContext
{
GstElement *pipeline = nullptr;
ComPtr<IDWriteFactory> dwrite_factory;
ComPtr<IDWriteTextFormat> format;
ComPtr<IDWriteTextLayout> layout;
std::wstring text;
gint width = 0;
gint height = 0;
FLOAT origin_y = 0;
gint text_width = 0;
gint last_position = 0;
gboolean draw_on_viewport;
GMainLoop *loop = nullptr;
};
/* *INDENT-ON* */
static gboolean
bus_msg (GstBus * bus, GstMessage * msg, OverlayContext * context)
{
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)
gst_printerrln ("ERROR debug information: %s", dbg);
g_clear_error (&err);
g_free (dbg);
g_main_loop_quit (context->loop);
break;
}
case GST_MESSAGE_EOS:
gst_println ("Got EOS");
g_main_loop_quit (context->loop);
break;
default:
break;
}
return TRUE;
}
/* Calculate text size again per resize event */
static void
calculate_size (OverlayContext * context, gint width, gint height)
{
if (width == context->width && height == context->height)
return;
HRESULT hr;
DWRITE_TEXT_RANGE range;
range.startPosition = 0;
range.length = context->text.length ();
FLOAT text_height = height / 10.0f;
bool was_decreased = false;
ComPtr < IDWriteTextLayout > text_layout;
hr = context->dwrite_factory->CreateTextLayout (context->text.c_str (),
context->text.length (), context->format.Get (), G_MAXFLOAT, G_MAXFLOAT,
&text_layout);
g_assert (SUCCEEDED (hr));
DWRITE_TEXT_METRICS metrics;
FLOAT font_size;
do {
hr = text_layout->GetMetrics (&metrics);
g_assert (SUCCEEDED (hr));
text_layout->GetFontSize (0, &font_size);
if (metrics.height >= text_height) {
if (font_size > 1.0f) {
font_size -= 0.5f;
was_decreased = true;
hr = text_layout->SetFontSize (font_size, range);
g_assert (SUCCEEDED (hr));
continue;
}
break;
}
if (was_decreased)
break;
if (metrics.height < text_height) {
if (metrics.height >= text_height * 0.9)
break;
font_size += 0.5f;
hr = text_layout->SetFontSize (font_size, range);
g_assert (SUCCEEDED (hr));
continue;
}
} while (true);
text_layout->SetMaxWidth (metrics.widthIncludingTrailingWhitespace + 10);
text_layout->SetMaxHeight (metrics.height);
context->layout = nullptr;
context->layout = text_layout;
context->origin_y = height - text_height;
context->text_width = metrics.widthIncludingTrailingWhitespace + 10;
context->width = width;
context->height = height;
context->last_position = 0;
}
static void
on_overlay_2d (GstElement * sink, ID3D12CommandQueue * queue,
ID3D12Resource * resource12, ID3D11On12Device * device11on12,
ID3D11Texture2D * resource11, ID2D1DeviceContext2 * context2d,
D3D12_RECT * viewport, OverlayContext * context)
{
ComPtr < ID2D1SolidColorBrush > text_brush;
ComPtr < ID2D1SolidColorBrush > bg_brush;
ComPtr < ID3D11Device > device11;
ComPtr < ID3D11DeviceContext > context11;
HRESULT hr;
FLOAT position;
UINT width, height;
if (context->draw_on_viewport) {
width = viewport->right - viewport->left;
height = viewport->bottom - viewport->top;
} else {
D3D11_TEXTURE2D_DESC desc;
resource11->GetDesc (&desc);
width = desc.Width;
height = desc.Height;
}
calculate_size (context, width, height);
/* We need d3d11 immediate context to call Flush() */
hr = device11on12->QueryInterface (IID_PPV_ARGS (&device11));
g_assert (SUCCEEDED (hr));
device11->GetImmediateContext (&context11);
/* Acquire wrapped resource to build GPU command by using d2d/d3d11 for
* the wrapped d3d12 resrouce */
ID3D11Resource *resources[] = { resource11 };
device11on12->AcquireWrappedResources (resources, 1);
context2d->BeginDraw ();
hr = context2d->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF::Black),
&bg_brush);
g_assert (SUCCEEDED (hr));
hr = context2d->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF::White),
&text_brush);
g_assert (SUCCEEDED (hr));
ComPtr < ID2D1Layer > layer;
hr = context2d->CreateLayer (&layer);
g_assert (SUCCEEDED (hr));
D2D1_RECT_F bg_rect;
if (context->draw_on_viewport) {
bg_rect = D2D1::RectF (viewport->left, context->origin_y + viewport->top,
viewport->left + context->width, viewport->top + context->height);
} else {
bg_rect = D2D1::RectF (0, context->origin_y, context->width,
context->height);
}
/* Draw background */
context2d->FillRectangle (bg_rect, bg_brush.Get ());
/* Create layer to restrict drawing area to specific rect */
D2D1_LAYER_PARAMETERS params = { };
params.contentBounds = bg_rect;
params.maskAntialiasMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
params.maskTransform = D2D1::IdentityMatrix ();
params.opacity = 1;
context2d->PushLayer (&params, layer.Get ());
/* Draw text */
position = -context->last_position;
do {
context2d->DrawTextLayout (D2D1::Point2F (position, bg_rect.top),
context->layout.Get (), text_brush.Get (),
static_cast < D2D1_DRAW_TEXT_OPTIONS >
(D2D1_DRAW_TEXT_OPTIONS_CLIP |
D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT));
position = position + context->text_width;
} while (position < context->width);
context2d->PopLayer ();
context2d->EndDraw ();
/* After recording GPU command, should release wrapped resource first,
* then Flush() must be called for the recorded GPU command to be
* executed via d3d12 command queue */
device11on12->ReleaseWrappedResources (resources, 1);
context11->Flush ();
/* Update text position for the next rendering */
context->last_position += 2;
if (context->last_position >= context->text_width) {
context->last_position %= context->text_width;
}
}
gint
main (gint argc, gchar ** argv)
{
GOptionContext *option_ctx;
GError *error = nullptr;
gboolean ret;
gchar *text = nullptr;
gint width = 1280;
gint height = 720;
gboolean draw_on_viewport = FALSE;
GOptionEntry options[] = {
{"text", 0, 0, G_OPTION_ARG_STRING, &text, "Text to render", nullptr},
{"width", 0, 0, G_OPTION_ARG_INT, &width, "Width of video stream", nullptr},
{"height", 0, 0, G_OPTION_ARG_INT, &height, "Height of video stream",
nullptr},
{"draw-on-viewport", 0, 0, G_OPTION_ARG_NONE, &draw_on_viewport,
"Draw image only on viewport area", nullptr},
{nullptr}
};
OverlayContext context;
GstStateChangeReturn sret;
HRESULT hr;
std::wstring text_wide;
std::string pipeline_str;
DWRITE_TEXT_METRICS metrics;
FLOAT font_size;
bool was_decreased = false;
DWRITE_TEXT_RANGE range;
FLOAT text_height;
gchar **win32_argv;
ComPtr < IDWriteTextFormat > text_format;
ComPtr < IDWriteTextLayout > text_layout;
win32_argv = g_win32_get_command_line ();
option_ctx = g_option_context_new ("d3d12videosink present-on-11 example");
g_option_context_add_main_entries (option_ctx, options, nullptr);
g_option_context_add_group (option_ctx, gst_init_get_option_group ());
ret = g_option_context_parse_strv (option_ctx, &win32_argv, &error);
g_option_context_free (option_ctx);
g_strfreev (win32_argv);
if (!ret) {
gst_printerrln ("option parsing failed: %s", error->message);
g_clear_error (&error);
return 1;
}
/* Prepare device independent D2D objects */
hr = DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED,
__uuidof (context.dwrite_factory.Get ()),
reinterpret_cast < IUnknown ** >(context.dwrite_factory.GetAddressOf ()));
if (FAILED (hr)) {
gst_printerrln ("Couldn't create DirectWrite factory");
return 1;
}
hr = context.dwrite_factory->CreateTextFormat (L"Arial", nullptr,
DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL, 12.0f, L"en-us", &text_format);
if (FAILED (hr)) {
gst_printerrln ("Couldn't create IDWriteTextFormat");
return 1;
}
text_format->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_LEADING);
text_format->SetParagraphAlignment (DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
text_format->SetWordWrapping (DWRITE_WORD_WRAPPING_NO_WRAP);
if (text && text[0] != '\0') {
std::wstring_convert < std::codecvt_utf8_utf16 < wchar_t >>converter;
text_wide = converter.from_bytes (text);
} else {
/* *INDENT-OFF* */
text_wide =
std::wstring (L"Hello GStreamer! 😊 안녕하세요 GStreamer! 😉 ") +
std::wstring (L"नमस्ते GStreamer! ❤️ Bonjour GStreamer! 😁 ") +
std::wstring (L"Hallo GStreamer! 😎 Hola GStreamer! 😍 ") +
std::wstring (L"こんにちは GStreamer! ✌️ 你好 GStreamer! 👍");
/* *INDENT-ON* */
}
text_height = (FLOAT) height / 10.0f;
hr = context.dwrite_factory->CreateTextLayout (text_wide.c_str (),
text_wide.length (), text_format.Get (), G_MAXFLOAT, G_MAXFLOAT,
&text_layout);
if (FAILED (hr)) {
gst_printerrln ("Couldn't create IDWriteTextLayout");
return 1;
}
/* Calculate best font size */
range.startPosition = 0;
range.length = text_wide.length ();
do {
hr = text_layout->GetMetrics (&metrics);
g_assert (SUCCEEDED (hr));
text_layout->GetFontSize (0, &font_size);
if (metrics.height >= (FLOAT) text_height) {
if (font_size > 1.0f) {
font_size -= 0.5f;
was_decreased = true;
hr = text_layout->SetFontSize (font_size, range);
g_assert (SUCCEEDED (hr));
continue;
}
break;
}
if (was_decreased)
break;
if (metrics.height < text_height) {
if (metrics.height >= text_height * 0.9)
break;
font_size += 0.5f;
hr = text_layout->SetFontSize (font_size, range);
g_assert (SUCCEEDED (hr));
continue;
}
} while (true);
gst_println ("Calculated font size %lf", font_size);
/* 10 pixels padding per loop */
text_layout->SetMaxWidth (metrics.widthIncludingTrailingWhitespace + 10);
text_layout->SetMaxHeight (metrics.height);
context.text = text_wide;
context.origin_y = (FLOAT) height - text_height;
context.width = width;
context.height = height;
context.text_width = metrics.widthIncludingTrailingWhitespace + 10;
context.layout = text_layout;
context.format = text_format;
context.draw_on_viewport = draw_on_viewport;
context.loop = g_main_loop_new (nullptr, FALSE);
pipeline_str =
"d3d12testsrc ! video/x-raw(memory:D3D12Memory),format=RGBA,width=" +
std::to_string (width) + ",height=" + std::to_string (height) +
",framerate=60/1 ! queue ! d3d12videosink name=sink overlay-mode=d2d "
"display-format=r8g8b8a8-unorm";
context.pipeline = gst_parse_launch (pipeline_str.c_str (), nullptr);
g_assert (context.pipeline);
auto sink = gst_bin_get_by_name (GST_BIN (context.pipeline), "sink");
g_assert (sink);
g_signal_connect (sink, "overlay", G_CALLBACK (on_overlay_2d), &context);
gst_object_unref (sink);
gst_bus_add_watch (GST_ELEMENT_BUS (context.pipeline),
(GstBusFunc) bus_msg, &context);
sret = gst_element_set_state (context.pipeline, GST_STATE_PLAYING);
g_assert (sret != GST_STATE_CHANGE_FAILURE);
g_main_loop_run (context.loop);
gst_element_set_state (context.pipeline, GST_STATE_NULL);
gst_bus_remove_watch (GST_ELEMENT_BUS (context.pipeline));
gst_object_unref (context.pipeline);
g_main_loop_unref (context.loop);
g_free (text);
return 0;
}

View file

@ -2,9 +2,26 @@ if host_system != 'windows'
subdir_done()
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')
dwrite_dep = cc.find_library('dwrite', required: false)
executable('d3d12enc-dynamic-reconfigure',
['d3d12enc-dynamic-reconfigure.c', '../key-handler.c'],
include_directories : [configinc],
dependencies: [gst_dep, gstbase_dep, gstvideo_dep],
c_args : gst_plugins_bad_args,
install: false)
if gstd3d12_dep.found()
if have_d2d_h and have_dwrite_h and have_d3d12video_h and dwrite_dep.found()
executable('d3d12videosink-overlay', ['d3d12videosink-overlay.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, gstd3d12_dep, dwrite_dep],
install: false,
)
endif
endif