#include <future>
#include <wrl.h>
#include <gst/d3dshader/gstd3dshader.h>
+#include <gmodule.h>
#define _XM_NO_INTRINSICS_
#include <DirectXMath.h>
return GST_FLOW_ERROR;
}
+static guint
+get_sdr_white_level (PCWSTR name)
+{
+ LONG ret = ERROR_SUCCESS;
+ std::vector < DISPLAYCONFIG_PATH_INFO > path_info;
+ std::vector < DISPLAYCONFIG_MODE_INFO > mode_info;
+ gint retry_count = 0;
+ guint nits = 80;
+
+ /* QueryDisplayConfig() may return ERROR_INSUFFICIENT_BUFFER if there was
+ * configuration update between GetDisplayConfigBufferSizes() and
+ * QueryDisplayConfig() call. */
+ while (1) {
+ UINT32 n_path = 0;
+ UINT32 n_mode = 0;
+
+ ret = GetDisplayConfigBufferSizes (QDC_ONLY_ACTIVE_PATHS, &n_path, &n_mode);
+ if (ret != ERROR_SUCCESS) {
+ GST_WARNING ("GetDisplayConfigBufferSizes failed %d", (gint) ret);
+ return nits;
+ }
+
+ path_info.resize (n_path);
+ mode_info.resize (n_mode);
+
+ ret = QueryDisplayConfig (QDC_ONLY_ACTIVE_PATHS, &n_path, path_info.data (),
+ &n_mode, mode_info.data (), nullptr);
+ if (ret == ERROR_INSUFFICIENT_BUFFER) {
+ /* XXX: avoid infinite loop */
+ retry_count++;
+ if (retry_count > 100) {
+ GST_WARNING ("Too many retry, give up");
+ return nits;
+ }
+
+ GST_DEBUG ("Insufficient buffer, retrying");
+ continue;
+ } else if (ret != ERROR_SUCCESS) {
+ GST_WARNING ("QueryDisplayConfig failed %d", (gint) ret);
+ return nits;
+ }
+
+ path_info.resize (n_path);
+ mode_info.resize (n_mode);
+ break;
+ }
+
+ for (size_t i = 0; i < path_info.size (); i++) {
+ DISPLAYCONFIG_SOURCE_DEVICE_NAME src_name = { };
+ src_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
+ src_name.header.size = sizeof (DISPLAYCONFIG_SOURCE_DEVICE_NAME);
+ src_name.header.adapterId = path_info[i].sourceInfo.adapterId;
+ src_name.header.id = path_info[i].sourceInfo.id;
+
+ ret = DisplayConfigGetDeviceInfo (&src_name.header);
+ if (ret == ERROR_SUCCESS && wcscmp (name, src_name.viewGdiDeviceName) == 0) {
+ DISPLAYCONFIG_SDR_WHITE_LEVEL level;
+ level.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL;
+ level.header.size = sizeof (level);
+ level.header.adapterId = path_info[i].targetInfo.adapterId;
+ level.header.id = path_info[i].targetInfo.id;
+ ret = DisplayConfigGetDeviceInfo (&level.header);
+ if (ret != ERROR_SUCCESS) {
+ GST_WARNING ("Couldn't get SDR white level info");
+ return nits;
+ }
+
+ return (level.SDRWhiteLevel * 80) / 1000;
+ }
+ }
+
+ return nits;
+}
+
+struct DxgiCaptureVTable
+{
+ gboolean loaded;
+ DPI_AWARENESS_CONTEXT (WINAPI * SetThreadDpiAwarenessContext) (DPI_AWARENESS_CONTEXT context);
+};
+
+static DxgiCaptureVTable g_vtable = { };
+
+static gboolean
+gst_d3d12_dxgi_capture_load_library (void)
+{
+ static GModule *user32_module = nullptr;
+
+ GST_D3D12_CALL_ONCE_BEGIN {
+ g_vtable.loaded = FALSE;
+ user32_module = g_module_open ("user32.dll", G_MODULE_BIND_LAZY);
+ if (!user32_module)
+ return;
+
+ if (!g_module_symbol (user32_module, "SetThreadDpiAwarenessContext",
+ (gpointer *) &g_vtable.SetThreadDpiAwarenessContext)) {
+ return;
+ }
+
+ g_vtable.loaded = TRUE;
+ } GST_D3D12_CALL_ONCE_END;
+
+ return g_vtable.loaded;
+}
+
struct PtrInfo
{
PtrInfo ()
XMFLOAT2 TexCoord;
};
+struct PSConstBuffer
+{
+ float sdr_white_level;
+ float padding[3];
+};
+
class DesktopDupCtx
{
public:
GstFlowReturn Init (HMONITOR monitor, ID3D11Device5 * device,
ID3D11DeviceContext4 * context, ID3D11Fence * fence,
ID3D11SamplerState * sampler, ID3D11PixelShader * ps,
- ID3D11VertexShader * vs, ID3D11InputLayout * layout)
+ ID3D11PixelShader * ps_scrgb, ID3D11PixelShader * ps_scrgb_tonemap,
+ ID3D11Buffer * ps_cbuf, ID3D11VertexShader * vs,
+ ID3D11InputLayout * layout, gboolean use_reinhard)
{
ComPtr<IDXGIAdapter1> adapter;
ComPtr<IDXGIOutput> output;
ComPtr<IDXGIOutput1> output1;
+ ComPtr<IDXGIOutput6> output6;
HRESULT hr = gst_d3d12_screen_capture_find_output_for_monitor (monitor,
&adapter, &output);
return GST_FLOW_ERROR;
}
+ PSConstBuffer cbuf;
+ cbuf.sdr_white_level = 80.0;
+ gboolean is_hdr = FALSE;
+
+ if (gst_d3d12_dxgi_capture_load_library ()) {
+ hr = output.As (&output6);
+ if (SUCCEEDED (hr)) {
+ DXGI_OUTPUT_DESC1 desc1;
+ hr = output6->GetDesc1 (&desc1);
+ if (SUCCEEDED (hr) &&
+ desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
+ is_hdr = TRUE;
+
+ MONITORINFOEXW monitor_info = { };
+ monitor_info.cbSize = sizeof (MONITORINFOEXW);
+ if (GetMonitorInfoW (desc1.Monitor, (LPMONITORINFO) & monitor_info))
+ cbuf.sdr_white_level = get_sdr_white_level (monitor_info.szDevice);
+ }
+ }
+ }
+
HDESK hdesk = OpenInputDesktop (0, FALSE, GENERIC_ALL);
if (hdesk) {
if (!SetThreadDesktop (hdesk)) {
GST_WARNING ("OpenInputDesktop() failed, error %lu", GetLastError());
}
- /* FIXME: Use DuplicateOutput1 to avoid potentail color conversion */
- hr = output1->DuplicateOutput(device, &dupl_);
+ hr = E_FAIL;
+ output_format_ = DXGI_FORMAT_B8G8R8A8_UNORM;
+ if (is_hdr) {
+ DXGI_FORMAT formats[] = {
+ DXGI_FORMAT_R16G16B16A16_FLOAT,
+ DXGI_FORMAT_B8G8R8A8_UNORM,
+ };
+
+ /* XXX: DuplicateOutput1() would fail if dpi awareness is not configured */
+ auto prev_ctx = g_vtable.SetThreadDpiAwarenessContext
+ (DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
+ hr = output6->DuplicateOutput1(device, 0, 2, formats, &dupl_);
+
+ /* And restore dpi context for the current thread */
+ if (prev_ctx != nullptr)
+ g_vtable.SetThreadDpiAwarenessContext (prev_ctx);
+
+ if (FAILED (hr)) {
+ GST_WARNING ("IDXGIOutput5::DuplicateOutput1 returned 0x%x",
+ (guint) hr);
+ is_hdr = FALSE;
+ } else {
+ output_format_ = DXGI_FORMAT_R16G16B16A16_UNORM;
+ }
+ }
+
+ if (FAILED (hr))
+ hr = output1->DuplicateOutput(device, &dupl_);
+
if (FAILED (hr)) {
if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) {
GST_ERROR ("Hit the max allowed number of Desktop Duplication session");
CreateDuplicationExpectedErrors);
}
- device_ = device;
- context_ = context;
- shared_fence_ = fence;
- sampler_ = sampler;
- ps_ = ps;
- vs_ = vs;
- layout_ = layout;
-
dupl_->GetDesc (&output_desc_);
D3D11_TEXTURE2D_DESC desc = { };
desc.Height = output_desc_.ModeDesc.Height;
desc.MipLevels = 1;
desc.ArraySize = 1;
- desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ desc.Format = output_format_;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_RENDER_TARGET;
- hr = device_->CreateTexture2D (&desc, nullptr, &texture_);
+ device_ = device;
+ context_ = context;
+ shared_fence_ = fence;
+ sampler_ = sampler;
+ ps_cbuf_ = ps_cbuf;
+ vs_ = vs;
+ layout_ = layout;
+
+ if (is_hdr) {
+ GST_INFO ("HDR with SDR white level %d nits",
+ (guint) cbuf.sdr_white_level);
+ if (!use_reinhard) {
+ GST_INFO ("Use scRGB sampling");
+ ps_ = ps_scrgb;
+ } else {
+ GST_INFO ("use scRGB sampling with reinhard tonemapping");
+ ps_ = ps_scrgb_tonemap;
+ }
+ } else {
+ GST_INFO ("Monitor is SDR mode");
+ ps_ = ps;
+ }
+
+ hr = device->CreateTexture2D (&desc, nullptr, &texture_);
if (FAILED (hr)) {
GST_ERROR ("Couldn't create texture");
return GST_FLOW_ERROR;
}
- hr = device_->CreateRenderTargetView (texture_.Get (), nullptr, &rtv_);
+ hr = device->CreateRenderTargetView (texture_.Get (), nullptr, &rtv_);
if (FAILED (hr)) {
GST_ERROR ("Couldn't create render target view");
return GST_FLOW_ERROR;
}
+ D3D11_MAPPED_SUBRESOURCE map;
+ hr = context->Map (ps_cbuf_.Get (), 0, D3D11_MAP_WRITE_DISCARD, 0, &map); if (FAILED (hr)) {
+ GST_ERROR ("Couldn't map constant buffer");
+ return GST_FLOW_ERROR;
+ }
+
+ memcpy (map.pData, &cbuf, sizeof (PSConstBuffer));
+ context->Unmap (ps_cbuf_.Get (), 0);
+
viewport_.TopLeftX = 0;
viewport_.TopLeftY = 0;
viewport_.MinDepth = 0;
context_->IASetInputLayout(layout_.Get());
context_->VSSetShader(vs_.Get(), nullptr, 0);
context_->PSSetShader(ps_.Get(), nullptr, 0);
+ ID3D11Buffer *ps_cbuf[] = { ps_cbuf_.Get () };
+ context_->PSSetConstantBuffers (0, 1, ps_cbuf);
ID3D11ShaderResourceView *srv[] = { cur_srv.Get () };
context_->PSSetShaderResources(0, 1, srv);
if (hr != DXGI_ERROR_WAIT_TIMEOUT) {
if (FAILED (hr)) {
GST_WARNING ("AcquireNextFrame failed with 0x%x", (guint) hr);
+ /* XXX: HDR <-> SDR mode switching seems to be racy,
+ * and AcquireNextFrame() seems to return DXGI_ERROR_INVALID_CALL
+ * sometimes on HDR <-> SDR mode switching.
+ * Do return GST_D3D12_SCREEN_CAPTURE_FLOW_UNSUPPORTED here
+ * if AcquireNextFrame() returns DXGI_ERROR_INVALID_CALL, then
+ * source element will do retry a bit more */
+ if (hr == DXGI_ERROR_INVALID_CALL) {
+ GST_WARNING ("DXGI_ERROR_INVALID_CALL, trying again");
+ dupl_->ReleaseFrame ();
+ return GST_D3D12_SCREEN_CAPTURE_FLOW_UNSUPPORTED;
+ }
+
dupl_->ReleaseFrame ();
return flow_return_from_hr (device_.Get (), hr, FrameInfoExpectedErrors);
}
*height = output_desc_.ModeDesc.Height;
}
+ DXGI_FORMAT GetFormat ()
+ {
+ return output_format_;
+ }
+
DXGI_OUTDUPL_DESC GetDesc ()
{
return output_desc_;
ComPtr<ID3D11RenderTargetView> rtv_;
ComPtr<ID3D11SamplerState> sampler_;
ComPtr<ID3D11PixelShader> ps_;
+ ComPtr<ID3D11PixelShader> ps_scrgb_;
+ ComPtr<ID3D11PixelShader> ps_scrgb_tonemap_;
+ ComPtr<ID3D11Buffer> ps_cbuf_;
ComPtr<ID3D11VertexShader> vs_;
ComPtr<ID3D11InputLayout> layout_;
ComPtr<ID3D11Buffer> vertex_buf_;
UINT vertext_buf_size_ = 0;
D3D11_VIEWPORT viewport_ = { };
std::vector<VERTEX> dirty_vertex_;
+ DXGI_FORMAT output_format_ = DXGI_FORMAT_B8G8R8A8_UNORM;
/* frame metadata */
std::vector<BYTE> metadata_buffer_;
gst_clear_object (&fence_data_pool);
gst_clear_object (&mouse_blend);
gst_clear_object (&mouse_xor_blend);
+ gst_clear_object (&mouse_blend_scrgb);
+ gst_clear_object (&mouse_xor_blend_scrgb);
}
void WaitGPU ()
ComPtr<ID3D11Fence> shared_fence11;
ComPtr<ID3D11SamplerState> sampler;
ComPtr<ID3D11PixelShader> ps;
+ ComPtr<ID3D11PixelShader> ps_scrgb;
+ ComPtr<ID3D11PixelShader> ps_scrgb_tonemap;
ComPtr<ID3D11VertexShader> vs;
ComPtr<ID3D11InputLayout> layout;
+ ComPtr<ID3D11Buffer> const_buf;
GstBuffer *mouse_buf = nullptr;
GstBuffer *mouse_xor_buf = nullptr;
GstD3D12Converter *mouse_blend = nullptr;
GstD3D12Converter *mouse_xor_blend = nullptr;
+ GstD3D12Converter *mouse_blend_scrgb = nullptr;
+ GstD3D12Converter *mouse_xor_blend_scrgb = nullptr;
HMONITOR monitor_handle = nullptr;
RECT desktop_coordinates = { };
+ guint sdr_white_level = 80;
+ guint prepare_flags = 0;
guint cached_width = 0;
guint cached_height = 0;
static void gst_d3d12_dxgi_capture_finalize (GObject * object);
static GstFlowReturn
-gst_d3d12_dxgi_capture_prepare (GstD3D12ScreenCapture * capture);
+gst_d3d12_dxgi_capture_prepare (GstD3D12ScreenCapture * capture, guint flags);
static gboolean
gst_d3d12_dxgi_capture_get_size (GstD3D12ScreenCapture * capture,
guint * width, guint * height);
+static GstVideoFormat
+gst_d3d12_dxgi_capture_get_format (GstD3D12ScreenCapture * capture);
#define gst_d3d12_dxgi_capture_parent_class parent_class
G_DEFINE_TYPE (GstD3D12DxgiCapture, gst_d3d12_dxgi_capture,
capture_class->prepare = GST_DEBUG_FUNCPTR (gst_d3d12_dxgi_capture_prepare);
capture_class->get_size = GST_DEBUG_FUNCPTR (gst_d3d12_dxgi_capture_get_size);
+ capture_class->get_format =
+ GST_DEBUG_FUNCPTR (gst_d3d12_dxgi_capture_get_format);
}
static void
priv->monitor_handle = monitor_handle;
ComPtr < IDXGIOutput > output;
+ ComPtr < IDXGIOutput6 > output6;
ComPtr < IDXGIAdapter1 > adapter;
auto hr = gst_d3d12_screen_capture_find_output_for_monitor (monitor_handle,
&adapter, &output);
return FALSE;
}
+ priv->sdr_white_level = 80;
+ hr = output.As (&output6);
+ if (SUCCEEDED (hr)) {
+ DXGI_OUTPUT_DESC1 desc1;
+ hr = output6->GetDesc1 (&desc1);
+ if (SUCCEEDED (hr) &&
+ desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
+ priv->sdr_white_level = get_sdr_white_level (monitor_info.szDevice);
+ GST_INFO_OBJECT (self, "HDR mode detected, SDR white level in nits: %d",
+ priv->sdr_white_level);
+ }
+ }
+
priv->desktop_coordinates.left = dev_mode.dmPosition.x;
priv->desktop_coordinates.top = dev_mode.dmPosition.y;
priv->desktop_coordinates.right =
/* size will be updated later */
GstVideoInfo info;
+ GstVideoInfo scrgb_info;
gst_video_info_set_format (&info, GST_VIDEO_FORMAT_BGRA,
priv->cached_width, priv->cached_height);
+ gst_video_info_set_format (&scrgb_info, GST_VIDEO_FORMAT_RGBA64_LE,
+ priv->cached_width, priv->cached_height);
D3D12_BLEND_DESC blend_desc = CD3DX12_BLEND_DESC (D3D12_DEFAULT);
blend_desc.RenderTarget[0].BlendEnable = TRUE;
priv->mouse_blend = gst_d3d12_converter_new (self->device, nullptr, &info,
&info, &blend_desc, nullptr, nullptr);
+ priv->mouse_blend_scrgb = gst_d3d12_converter_new (self->device, nullptr,
+ &info, &scrgb_info, &blend_desc, nullptr, nullptr);
blend_desc.RenderTarget[0].SrcBlend = D3D12_BLEND_INV_DEST_COLOR;
blend_desc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_COLOR;
priv->mouse_xor_blend = gst_d3d12_converter_new (self->device, nullptr, &info,
&info, &blend_desc, nullptr, nullptr);
+ priv->mouse_xor_blend_scrgb = gst_d3d12_converter_new (self->device, nullptr,
+ &info, &scrgb_info, &blend_desc, nullptr, nullptr);
hr = device->CreateFence (0,
D3D12_FENCE_FLAG_SHARED, IID_PPV_ARGS (&priv->shared_fence));
}
GstD3DShaderByteCode vs_code;
- GstD3DShaderByteCode ps_code;
if (!gst_d3d_plugin_shader_get_vs_blob (GST_D3D_PLUGIN_VS_COORD,
GST_D3D_SM_5_0, &vs_code)) {
GST_ERROR_OBJECT (self, "Couldn't get vs bytecode");
return FALSE;
}
- if (!gst_d3d_plugin_shader_get_ps_blob (GST_D3D_PLUGIN_PS_SAMPLE,
- GST_D3D_SM_5_0, &ps_code)) {
- GST_ERROR_OBJECT (self, "Couldn't get ps bytecode");
- return FALSE;
- }
-
D3D11_INPUT_ELEMENT_DESC input_desc[2] = { };
input_desc[0].SemanticName = "POSITION";
input_desc[0].SemanticIndex = 0;
return FALSE;
}
+ GstD3DShaderByteCode ps_code;
+ if (!gst_d3d_plugin_shader_get_ps_blob (GST_D3D_PLUGIN_PS_SAMPLE,
+ GST_D3D_SM_5_0, &ps_code)) {
+ GST_ERROR_OBJECT (self, "Couldn't get ps bytecode");
+ return FALSE;
+ }
hr = priv->device11->CreatePixelShader (ps_code.byte_code,
ps_code.byte_code_len, nullptr, &priv->ps);
if (FAILED (hr)) {
return FALSE;
}
+ if (!gst_d3d_plugin_shader_get_ps_blob (GST_D3D_PLUGIN_PS_SAMPLE_SCRGB,
+ GST_D3D_SM_5_0, &ps_code)) {
+ GST_ERROR_OBJECT (self, "Couldn't get ps bytecode");
+ return FALSE;
+ }
+ hr = priv->device11->CreatePixelShader (ps_code.byte_code,
+ ps_code.byte_code_len, nullptr, &priv->ps_scrgb);
+ if (FAILED (hr)) {
+ GST_ERROR_OBJECT (self, "Couldn't create pixel shader");
+ return FALSE;
+ }
+
+ if (!gst_d3d_plugin_shader_get_ps_blob
+ (GST_D3D_PLUGIN_PS_SAMPLE_SCRGB_TONEMAP, GST_D3D_SM_5_0, &ps_code)) {
+ GST_ERROR_OBJECT (self, "Couldn't get ps bytecode");
+ return FALSE;
+ }
+ hr = priv->device11->CreatePixelShader (ps_code.byte_code,
+ ps_code.byte_code_len, nullptr, &priv->ps_scrgb_tonemap);
+ if (FAILED (hr)) {
+ GST_ERROR_OBJECT (self, "Couldn't create pixel shader");
+ return FALSE;
+ }
+
+ PSConstBuffer cbuf;
+ cbuf.sdr_white_level = (float) priv->sdr_white_level;
+
+ D3D11_BUFFER_DESC buffer_desc = { };
+ D3D11_SUBRESOURCE_DATA subresource = { };
+ buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
+ buffer_desc.ByteWidth = sizeof (PSConstBuffer);
+ buffer_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+ buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+ subresource.pSysMem = &cbuf;
+ subresource.SysMemPitch = sizeof (PSConstBuffer);
+ hr = priv->device11->CreateBuffer (&buffer_desc, &subresource,
+ &priv->const_buf);
+ if (FAILED (hr)) {
+ GST_ERROR_OBJECT (self, "Couldn't create constant buffer");
+ return FALSE;
+ }
+
D3D11_SAMPLER_DESC sampler_desc = { };
sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr);
+ gst_d3d12_dxgi_capture_load_library ();
+
/* Check if we have dup object corresponding to monitor_handle,
* and if there is already configured capture object, reuse it.
* This is because of the limitation of desktop duplication API
auto ctx = std::make_unique < DesktopDupCtx > ();
auto ret = ctx->Init (priv->monitor_handle, priv->device11.Get (),
priv->context11.Get (), priv->shared_fence11.Get (),
- priv->sampler.Get (), priv->ps.Get (), priv->vs.Get (),
- priv->layout.Get ());
+ priv->sampler.Get (), priv->ps.Get (), priv->ps_scrgb.Get (),
+ priv->ps_scrgb_tonemap.Get (), priv->const_buf.Get (),
+ priv->vs.Get (), priv->layout.Get (), priv->prepare_flags ? TRUE : FALSE);
if (ret != GST_FLOW_OK) {
GST_WARNING_OBJECT (self,
"Couldn't prepare capturing, %sexpected failure",
}
static GstFlowReturn
-gst_d3d12_dxgi_capture_prepare (GstD3D12ScreenCapture * capture)
+gst_d3d12_dxgi_capture_prepare (GstD3D12ScreenCapture * capture, guint flags)
{
auto self = GST_D3D12_DXGI_CAPTURE (capture);
auto priv = self->priv;
std::lock_guard < std::mutex > lk (priv->lock);
+ priv->prepare_flags = flags;
return gst_d3d12_dxgi_capture_prepare_unlocked (self);
}
return gst_d3d12_dxgi_capture_get_size_unlocked (self, width, height);
}
+static GstVideoFormat
+gst_d3d12_dxgi_capture_get_format (GstD3D12ScreenCapture * capture)
+{
+ auto self = GST_D3D12_DXGI_CAPTURE (capture);
+ auto priv = self->priv;
+
+ std::lock_guard < std::mutex > lk (priv->lock);
+ if (!priv->ctx)
+ return GST_VIDEO_FORMAT_BGRA;
+
+ auto format = priv->ctx->GetFormat ();
+ if (format == DXGI_FORMAT_R16G16B16A16_UNORM)
+ return GST_VIDEO_FORMAT_RGBA64_LE;
+
+ return GST_VIDEO_FORMAT_BGRA;
+}
+
static gboolean
gst_d3d12_dxgi_capture_draw_mouse (GstD3D12DxgiCapture * self,
- GstBuffer * buffer, const D3D12_BOX * crop_box)
+ GstBuffer * buffer, const D3D12_BOX * crop_box, gboolean is_hdr)
{
auto priv = self->priv;
const auto & info = priv->ctx->GetPointerInfo ();
gint ptr_w = info.width_;
gint ptr_h = info.height_;
- g_object_set (priv->mouse_blend, "src-x", 0, "src-y", 0, "src-width",
+ auto blend_conv = is_hdr ? priv->mouse_blend_scrgb : priv->mouse_blend;
+
+ g_object_set (blend_conv, "src-x", 0, "src-y", 0, "src-width",
ptr_w, "src-height", ptr_h, "dest-x", ptr_x, "dest-y", ptr_y,
"dest-width", ptr_w, "dest-height", ptr_h, nullptr);
auto cq = gst_d3d12_device_get_cmd_queue (self->device,
D3D12_COMMAND_LIST_TYPE_DIRECT);
- if (!gst_d3d12_converter_convert_buffer (priv->mouse_blend,
+ if (!gst_d3d12_converter_convert_buffer (blend_conv,
priv->mouse_buf, buffer, fence_data, cl.Get (), TRUE)) {
GST_ERROR_OBJECT (self, "Couldn't build mouse blend command");
gst_d3d12_fence_data_unref (fence_data);
}
if (priv->mouse_xor_buf) {
- g_object_set (priv->mouse_xor_blend, "src-x", 0, "src-y", 0, "src-width",
+ blend_conv = is_hdr ? priv->mouse_xor_blend_scrgb : priv->mouse_xor_blend;
+ g_object_set (blend_conv, "src-x", 0, "src-y", 0, "src-width",
ptr_w, "src-height", ptr_h, "dest-x", ptr_x, "dest-y", ptr_y,
"dest-width", ptr_w, "dest-height", ptr_h, nullptr);
- if (!gst_d3d12_converter_convert_buffer (priv->mouse_xor_blend,
+ if (!gst_d3d12_converter_convert_buffer (blend_conv,
priv->mouse_xor_buf, buffer, fence_data, cl.Get (), FALSE)) {
GST_ERROR_OBJECT (self, "Couldn't build mouse blend command");
gst_d3d12_fence_data_unref (fence_data);
return GST_FLOW_ERROR;
}
+ D3D11_TEXTURE2D_DESC tex_desc;
+ texture->GetDesc (&tex_desc);
+ if (tex_desc.Format != priv->ctx->GetFormat ()) {
+ GST_INFO_OBJECT (self, "Format mismatch");
+ return GST_D3D12_SCREEN_CAPTURE_FLOW_SIZE_CHANGED;
+ }
+
priv->fence_val++;
ret = priv->ctx->Execute (texture, (D3D11_BOX *) crop_box, priv->fence_val);
if (ret != GST_FLOW_OK) {
GST_MINI_OBJECT_FLAG_SET (dmem, GST_D3D12_MEMORY_TRANSFER_NEED_DOWNLOAD);
GST_MINI_OBJECT_FLAG_UNSET (dmem, GST_D3D12_MEMORY_TRANSFER_NEED_UPLOAD);
- if (draw_mouse && !gst_d3d12_dxgi_capture_draw_mouse (self, buffer, crop_box)) {
+ if (draw_mouse && !gst_d3d12_dxgi_capture_draw_mouse (self, buffer, crop_box,
+ tex_desc.Format == DXGI_FORMAT_R16G16B16A16_UNORM)) {
priv->WaitGPU ();
priv->ctx = nullptr;
return GST_FLOW_ERROR;
PROP_CAPTURE_API,
PROP_ADAPTER,
PROP_WINDOW_CAPTURE_MODE,
+ PROP_TONEMAP,
};
enum GstD3D12ScreenCaptureAPI
GST_D3D12_WINDOW_CAPTURE_CLIENT,
};
+enum GstD3D12ScreenCaptureTonemap
+{
+ GST_D3D12_SCREEN_CAPTURE_TONEMAP_LINEAR,
+ GST_D3D12_SCREEN_CAPTURE_TONEMAP_REINHARD,
+};
+
#ifdef HAVE_WGC
/**
* GstD3D12ScreenCaptureAPI:
}
#endif
+/**
+ * GstD3D12ScreenCaptureTonemap:
+ *
+ * Since: 1.26
+ */
+#define GST_TYPE_D3D12_SCREEN_CAPTURE_TONEMAP (gst_d3d12_screen_capture_tonemap_get_type())
+static GType
+gst_d3d12_screen_capture_tonemap_get_type (void)
+{
+ static GType type = 0;
+
+ GST_D3D12_CALL_ONCE_BEGIN {
+ static const GEnumValue modes[] = {
+ /**
+ * GstD3D12ScreenCaptureTonemap::linear:
+ *
+ * Since: 1.26
+ */
+ {GST_D3D12_SCREEN_CAPTURE_TONEMAP_LINEAR,
+ "Linear scaling", "linear"},
+
+ /**
+ * GstD3D12ScreenCaptureTonemap::reinhard:
+ *
+ * Since: 1.26
+ */
+ {GST_D3D12_SCREEN_CAPTURE_TONEMAP_REINHARD, "Reinhard tonemap",
+ "reinhard"},
+ {0, nullptr, nullptr},
+ };
+
+ type = g_enum_register_static ("GstD3D12ScreenCaptureTonemap", modes);
+ } GST_D3D12_CALL_ONCE_END;
+
+ return type;
+}
+
#define DEFAULT_MONITOR_INDEX -1
#define DEFAULT_SHOW_CURSOR FALSE
#define DEFAULT_SHOW_BORDER FALSE
#define DEFAULT_CAPTURE_API GST_D3D12_SCREEN_CAPTURE_API_DXGI
#define DEFAULT_ADAPTER -1
#define DEFAULT_WINDOW_CAPTURE_MODE GST_D3D12_WINDOW_CAPTURE_DEFAULT
+#define DEFAULT_TONEMAP GST_D3D12_SCREEN_CAPTURE_TONEMAP_LINEAR
static GstStaticPadTemplate src_template =
GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
- (GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY, "BGRA")
+ (GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY, "{ BGRA, RGBA64_LE }")
", pixel-aspect-ratio = 1/1, colorimetry = (string) sRGB; "
- GST_VIDEO_CAPS_MAKE ("BGRA") ", pixel-aspect-ratio = 1/1, "
- "colorimetry = (string) sRGB"));
+ GST_VIDEO_CAPS_MAKE ("{ BGRA, RGBA64_LE }")
+ ", pixel-aspect-ratio = 1/1, " "colorimetry = (string) sRGB"));
struct GstD3D12ScreenCaptureSrcPrivate
{
GstD3D12ScreenCaptureAPI capture_api = DEFAULT_CAPTURE_API;
GstD3D12ScreenCaptureAPI selected_capture_api = DEFAULT_CAPTURE_API;
GstD3D12WindowCaptureMode hwnd_capture_mode = DEFAULT_WINDOW_CAPTURE_MODE;
+ GstD3D12ScreenCaptureTonemap tonemap = DEFAULT_TONEMAP;
gboolean flushing = FALSE;
GstClockTime latency = GST_CLOCK_TIME_NONE;
}
#endif
+ /**
+ * GstD3D12ScreenCaptureSrc:tonemap:
+ *
+ * Tonemapping method in case of HDR capture
+ *
+ * Since: 1.26
+ */
+ g_object_class_install_property (object_class, PROP_TONEMAP,
+ g_param_spec_enum ("tonemap", "Tonemap",
+ "Tonemapping method to use when HDR capturing is enabled",
+ GST_TYPE_D3D12_SCREEN_CAPTURE_TONEMAP, DEFAULT_TONEMAP,
+ (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
+ G_PARAM_STATIC_STRINGS)));
+
+ gst_type_mark_as_plugin_api (GST_TYPE_D3D12_SCREEN_CAPTURE_TONEMAP,
+ (GstPluginAPIFlags) 0);
+
element_class->provide_clock =
GST_DEBUG_FUNCPTR (gst_d3d12_screen_capture_src_provide_clock);
element_class->set_context =
}
#endif
break;
+ case PROP_TONEMAP:
+ priv->tonemap = (GstD3D12ScreenCaptureTonemap) g_value_get_enum (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
case PROP_WINDOW_CAPTURE_MODE:
g_value_set_enum (value, priv->hwnd_capture_mode);
break;
+ case PROP_TONEMAP:
+ g_value_set_enum (value, priv->tonemap);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
auto self = GST_D3D12_SCREEN_CAPTURE_SRC (bsrc);
auto priv = self->priv;
guint width, height;
+ GstVideoFormat format = GST_VIDEO_FORMAT_BGRA;
std::lock_guard < std::recursive_mutex > lk (priv->lock);
if (!priv->capture) {
height = priv->crop_box.bottom - priv->crop_box.top;
}
+ format = gst_d3d12_screen_capture_get_format (priv->capture);
+
auto caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (bsrc));
caps = gst_caps_make_writable (caps);
- gst_caps_set_simple (caps, "width", G_TYPE_INT, width, "height",
+ gst_caps_set_simple (caps, "format", G_TYPE_STRING,
+ gst_video_format_to_string (format), "width", G_TYPE_INT, width, "height",
G_TYPE_INT, height, nullptr);
if (filter) {
}
/* Check if we can open device */
- ret = gst_d3d12_screen_capture_prepare (capture);
+ ret = gst_d3d12_screen_capture_prepare (capture,
+ priv->tonemap == GST_D3D12_SCREEN_CAPTURE_TONEMAP_REINHARD);
switch (ret) {
case GST_D3D12_SCREEN_CAPTURE_FLOW_EXPECTED_ERROR:
case GST_FLOW_OK: