d3d11screencapturesrc: Fix crash when d3d11 device is different from owned one

GstD3D11ScreenCapture object is pipeline-independent global object
and the object can be shared by multiple src elements,
in order to overcome a limitation of DXGI Desktop Duplication API.
Note that the API allows only single capture session in a process for
a monitor.

Therefore GstD3D11ScreenCapture object must be able to handle a case
where a src element holds different GstD3D11Device object. Which can
happen when GstD3D11Device context is not shared by pipelines.

What's changed:
* Allocates capture texture with D3D11_RESOURCE_MISC_SHARED for the
  texture to be able to copied into other device's texture
* Holds additional shader objects per src element and use it when drawing
  mouse

Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1197
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2366>
This commit is contained in:
Seungha Yang 2022-05-05 02:16:54 +09:00 committed by GStreamer Marge Bot
parent b247305bfd
commit e0a9a73adf
4 changed files with 285 additions and 51 deletions

View file

@ -280,7 +280,8 @@ public:
texture_desc.BindFlags = texture_desc.BindFlags =
D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
texture_desc.CPUAccessFlags = 0; texture_desc.CPUAccessFlags = 0;
texture_desc.MiscFlags = 0; /* source element may hold different d3d11 device object */
texture_desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
hr = device_handle->CreateTexture2D (&texture_desc, hr = device_handle->CreateTexture2D (&texture_desc,
nullptr, &shared_texture_); nullptr, &shared_texture_);
@ -344,7 +345,11 @@ public:
return GST_FLOW_OK; return GST_FLOW_OK;
} }
bool DrawMouse (ID3D11RenderTargetView * rtv, D3D11_BOX * cropBox) bool
DrawMouse (GstD3D11Device * device, ID3D11RenderTargetView * rtv,
ID3D11VertexShader * vs, ID3D11PixelShader * ps,
ID3D11InputLayout * layout, ID3D11SamplerState * sampler,
ID3D11BlendState * blend, D3D11_BOX * cropBox)
{ {
GST_TRACE ("Drawing mouse"); GST_TRACE ("Drawing mouse");
@ -359,9 +364,9 @@ public:
D3D11_SUBRESOURCE_DATA InitData; D3D11_SUBRESOURCE_DATA InitData;
D3D11_TEXTURE2D_DESC Desc; D3D11_TEXTURE2D_DESC Desc;
D3D11_SHADER_RESOURCE_VIEW_DESC SDesc; D3D11_SHADER_RESOURCE_VIEW_DESC SDesc;
ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_); ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
ID3D11DeviceContext *context_handle = ID3D11DeviceContext *context_handle =
gst_d3d11_device_get_device_context_handle (device_); gst_d3d11_device_get_device_context_handle (device);
VERTEX Vertices[NUMVERTICES] = VERTEX Vertices[NUMVERTICES] =
{ {
@ -469,7 +474,7 @@ public:
// Create mouseshape as texture // Create mouseshape as texture
HRESULT hr = device_handle->CreateTexture2D(&Desc, &InitData, &MouseTex); HRESULT hr = device_handle->CreateTexture2D(&Desc, &InitData, &MouseTex);
if (!gst_d3d11_result (hr, device_)) { if (!gst_d3d11_result (hr, device)) {
GST_ERROR ("Failed to create texture for rendering mouse"); GST_ERROR ("Failed to create texture for rendering mouse");
return false; return false;
} }
@ -477,7 +482,7 @@ public:
// Create shader resource from texture // Create shader resource from texture
hr = device_handle->CreateShaderResourceView(MouseTex.Get(), &SDesc, hr = device_handle->CreateShaderResourceView(MouseTex.Get(), &SDesc,
&ShaderRes); &ShaderRes);
if (!gst_d3d11_result (hr, device_)) { if (!gst_d3d11_result (hr, device)) {
GST_ERROR ("Failed to create shader resource view for rendering mouse"); GST_ERROR ("Failed to create shader resource view for rendering mouse");
return false; return false;
} }
@ -494,7 +499,7 @@ public:
// Create vertex buffer // Create vertex buffer
hr = device_handle->CreateBuffer(&BDesc, &InitData, &VertexBufferMouse); hr = device_handle->CreateBuffer(&BDesc, &InitData, &VertexBufferMouse);
if (!gst_d3d11_result (hr, device_)) { if (!gst_d3d11_result (hr, device)) {
GST_ERROR ("Failed to create vertex buffer for rendering mouse"); GST_ERROR ("Failed to create vertex buffer for rendering mouse");
return false; return false;
} }
@ -502,19 +507,18 @@ public:
FLOAT BlendFactor[4] = {0.f, 0.f, 0.f, 0.f}; FLOAT BlendFactor[4] = {0.f, 0.f, 0.f, 0.f};
UINT Stride = sizeof(VERTEX); UINT Stride = sizeof(VERTEX);
UINT Offset = 0; UINT Offset = 0;
ID3D11SamplerState *samplers = sampler_.Get();
ID3D11ShaderResourceView *srv = ShaderRes.Get(); ID3D11ShaderResourceView *srv = ShaderRes.Get();
ID3D11Buffer *vert_buf = VertexBufferMouse.Get(); ID3D11Buffer *vert_buf = VertexBufferMouse.Get();
context_handle->IASetVertexBuffers(0, 1, &vert_buf, &Stride, &Offset); context_handle->IASetVertexBuffers(0, 1, &vert_buf, &Stride, &Offset);
context_handle->OMSetBlendState(blend_.Get(), BlendFactor, 0xFFFFFFFF); context_handle->OMSetBlendState(blend, BlendFactor, 0xFFFFFFFF);
context_handle->OMSetRenderTargets(1, &rtv, nullptr); context_handle->OMSetRenderTargets(1, &rtv, nullptr);
context_handle->VSSetShader(vs_.Get(), nullptr, 0); context_handle->VSSetShader(vs, nullptr, 0);
context_handle->PSSetShader(ps_.Get(), nullptr, 0); context_handle->PSSetShader(ps, nullptr, 0);
context_handle->PSSetShaderResources(0, 1, &srv); context_handle->PSSetShaderResources(0, 1, &srv);
context_handle->PSSetSamplers(0, 1, &samplers); context_handle->PSSetSamplers(0, 1, &sampler);
context_handle->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); context_handle->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
context_handle->IASetInputLayout(layout_.Get()); context_handle->IASetInputLayout(layout);
D3D11_VIEWPORT VP; D3D11_VIEWPORT VP;
VP.Width = static_cast<FLOAT>(FullDesc.Width); VP.Width = static_cast<FLOAT>(FullDesc.Width);
@ -538,14 +542,61 @@ public:
return true; return true;
} }
void GstFlowReturn
CopyToTexture (ID3D11Texture2D * texture, D3D11_BOX * cropBox) CopyToTexture (GstD3D11Device * device, ID3D11Texture2D * texture,
D3D11_BOX * cropBox)
{ {
ID3D11DeviceContext *context_handle = ID3D11DeviceContext *context_handle = nullptr;
gst_d3d11_device_get_device_context_handle (device_); ComPtr <ID3D11Texture2D> tex;
ComPtr < ID3D11Query > query;
HRESULT hr;
context_handle = gst_d3d11_device_get_device_context_handle (device);
if (device == device_) {
tex = shared_texture_;
} else {
ID3D11Device *device_handle = nullptr;
ComPtr < IDXGIResource > dxgi_resource;
D3D11_QUERY_DESC query_desc;
HANDLE shared_handle;
device_handle = gst_d3d11_device_get_device_handle (device);
hr = shared_texture_.As (&dxgi_resource);
if (!gst_d3d11_result (hr, device_))
return GST_FLOW_ERROR;
hr = dxgi_resource->GetSharedHandle (&shared_handle);
if (!gst_d3d11_result (hr, device_))
return GST_FLOW_ERROR;
hr = device_handle->OpenSharedResource (shared_handle,
IID_PPV_ARGS (&tex));
if (!gst_d3d11_result (hr, device))
return GST_FLOW_ERROR;
query_desc.Query = D3D11_QUERY_EVENT;
query_desc.MiscFlags = 0;
hr = device_handle->CreateQuery (&query_desc, &query);
if (!gst_d3d11_result (hr, device))
return GST_FLOW_ERROR;
}
context_handle->CopySubresourceRegion (texture, 0, 0, 0, 0, context_handle->CopySubresourceRegion (texture, 0, 0, 0, 0,
shared_texture_.Get(), 0, cropBox); tex.Get(), 0, cropBox);
if (query) {
BOOL sync_done = FALSE;
do {
hr = context_handle->GetData (query.Get (),
&sync_done, sizeof (BOOL), 0);
} while (!sync_done && (hr == S_OK || hr == S_FALSE));
}
return GST_FLOW_OK;
} }
void void
@ -635,33 +686,11 @@ private:
return false; return false;
} }
/* For blending mouse pointer texture */
D3D11_BLEND_DESC blend_desc;
blend_desc.AlphaToCoverageEnable = FALSE;
blend_desc.IndependentBlendEnable = FALSE;
blend_desc.RenderTarget[0].BlendEnable = TRUE;
blend_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
blend_desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
blend_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
blend_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
blend_desc.RenderTarget[0].RenderTargetWriteMask =
D3D11_COLOR_WRITE_ENABLE_ALL;
ComPtr<ID3D11BlendState> blend;
hr = device_handle->CreateBlendState (&blend_desc, &blend);
if (!gst_d3d11_result (hr, device)) {
GST_ERROR ("Failed to create blend state, hr 0x%x", (guint) hr);
return false;
}
/* Everything is prepared now */ /* Everything is prepared now */
vs_ = vs; vs_ = vs;
ps_ = ps; ps_ = ps;
layout_ = layout; layout_ = layout;
sampler_ = sampler; sampler_ = sampler;
blend_ = blend;
return true; return true;
} }
@ -1452,7 +1481,6 @@ private:
ComPtr<ID3D11InputLayout> layout_; ComPtr<ID3D11InputLayout> layout_;
ComPtr<ID3D11SamplerState> sampler_; ComPtr<ID3D11SamplerState> sampler_;
ComPtr<IDXGIOutputDuplication> dupl_; ComPtr<IDXGIOutputDuplication> dupl_;
ComPtr<ID3D11BlendState> blend_;
/* frame metadata */ /* frame metadata */
BYTE *metadata_buffer_; BYTE *metadata_buffer_;
@ -1486,6 +1514,7 @@ struct _GstD3D11ScreenCapture
HMONITOR monitor_handle; HMONITOR monitor_handle;
RECT desktop_coordinates; RECT desktop_coordinates;
gboolean prepared; gboolean prepared;
gint64 adapter_luid;
GRecMutex lock; GRecMutex lock;
}; };
@ -1630,6 +1659,8 @@ gst_d3d11_screen_capture_constructed (GObject * object)
self->desktop_coordinates.right, self->desktop_coordinates.bottom, self->desktop_coordinates.right, self->desktop_coordinates.bottom,
self->cached_width, self->cached_height); self->cached_width, self->cached_height);
g_object_get (self->device, "adapter-luid", &self->adapter_luid, nullptr);
ret = TRUE; ret = TRUE;
out: out:
@ -1805,15 +1836,33 @@ gst_d3d11_screen_capture_get_size (GstD3D11ScreenCapture * capture,
GstFlowReturn GstFlowReturn
gst_d3d11_screen_capture_do_capture (GstD3D11ScreenCapture * capture, gst_d3d11_screen_capture_do_capture (GstD3D11ScreenCapture * capture,
ID3D11Texture2D * texture, ID3D11RenderTargetView * rtv, GstD3D11Device * device, ID3D11Texture2D * texture,
ID3D11RenderTargetView * rtv, ID3D11VertexShader * vs,
ID3D11PixelShader * ps, ID3D11InputLayout * layout,
ID3D11SamplerState * sampler, ID3D11BlendState * blend,
D3D11_BOX * crop_box, gboolean draw_mouse) D3D11_BOX * crop_box, gboolean draw_mouse)
{ {
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
gboolean shared_device = FALSE;
guint width, height; guint width, height;
g_return_val_if_fail (GST_IS_D3D11_SCREEN_CAPTURE (capture), GST_FLOW_ERROR); g_return_val_if_fail (GST_IS_D3D11_SCREEN_CAPTURE (capture), GST_FLOW_ERROR);
g_return_val_if_fail (texture != nullptr, GST_FLOW_ERROR); g_return_val_if_fail (texture != nullptr, GST_FLOW_ERROR);
if (device != capture->device) {
gint64 luid;
g_object_get (device, "adapter-luid", &luid, nullptr);
/* source element must hold d3d11 device for the same GPU already
* by DXGI duplication API design */
if (luid != capture->adapter_luid) {
GST_ERROR_OBJECT (capture, "Trying to capture from different device");
return GST_FLOW_ERROR;
}
shared_device = TRUE;
}
g_rec_mutex_lock (&capture->lock); g_rec_mutex_lock (&capture->lock);
if (!capture->prepared) if (!capture->prepared)
ret = gst_d3d11_screen_capture_prepare (capture); ret = gst_d3d11_screen_capture_prepare (capture);
@ -1858,14 +1907,26 @@ gst_d3d11_screen_capture_do_capture (GstD3D11ScreenCapture * capture,
} }
GST_LOG_OBJECT (capture, "Capture done"); GST_LOG_OBJECT (capture, "Capture done");
if (shared_device)
gst_d3d11_device_lock (device);
ret = capture->dupl_obj->CopyToTexture (device, texture, crop_box);
if (ret != GST_FLOW_OK)
goto out;
if (draw_mouse) {
capture->dupl_obj->DrawMouse (device,
rtv, vs, ps, layout, sampler, blend, crop_box);
}
out:
if (shared_device)
gst_d3d11_device_unlock (device);
capture->dupl_obj->CopyToTexture (texture, crop_box);
if (draw_mouse)
capture->dupl_obj->DrawMouse (rtv, crop_box);
gst_d3d11_device_unlock (capture->device); gst_d3d11_device_unlock (capture->device);
g_rec_mutex_unlock (&capture->lock); g_rec_mutex_unlock (&capture->lock);
return GST_FLOW_OK; return ret;
} }
HRESULT HRESULT

View file

@ -44,8 +44,14 @@ gboolean gst_d3d11_screen_capture_get_size (GstD3D11ScreenCapture * captu
guint * height); guint * height);
GstFlowReturn gst_d3d11_screen_capture_do_capture (GstD3D11ScreenCapture * capture, GstFlowReturn gst_d3d11_screen_capture_do_capture (GstD3D11ScreenCapture * capture,
GstD3D11Device * device,
ID3D11Texture2D * texture, ID3D11Texture2D * texture,
ID3D11RenderTargetView * rtv, ID3D11RenderTargetView * rtv,
ID3D11VertexShader * vs,
ID3D11PixelShader * ps,
ID3D11InputLayout * layout,
ID3D11SamplerState * sampler,
ID3D11BlendState * blend,
D3D11_BOX * crop_box, D3D11_BOX * crop_box,
gboolean draw_mouse); gboolean draw_mouse);

View file

@ -39,6 +39,7 @@
#include "gstd3d11screencapturesrc.h" #include "gstd3d11screencapturesrc.h"
#include "gstd3d11screencapture.h" #include "gstd3d11screencapture.h"
#include "gstd3d11pluginutils.h" #include "gstd3d11pluginutils.h"
#include "gstd3d11shader.h"
#include <wrl.h> #include <wrl.h>
#include <string.h> #include <string.h>
@ -102,6 +103,12 @@ struct _GstD3D11ScreenCaptureSrc
GstClockTime max_latency; GstClockTime max_latency;
gboolean downstream_supports_d3d11; gboolean downstream_supports_d3d11;
ID3D11VertexShader *vs;
ID3D11PixelShader *ps;
ID3D11InputLayout *layout;
ID3D11SamplerState *sampler;
ID3D11BlendState *blend;
}; };
static void gst_d3d11_screen_capture_src_dispose (GObject * object); static void gst_d3d11_screen_capture_src_dispose (GObject * object);
@ -599,6 +606,118 @@ error:
return FALSE; return FALSE;
} }
static gboolean
gst_d3d11_screen_capture_prepare_shader (GstD3D11ScreenCaptureSrc * self)
{
/* *INDENT-OFF* */
static const gchar vs_str[] =
"struct VS_INPUT {\n"
" float4 Position: POSITION;\n"
" float2 Texture: TEXCOORD;\n"
"};\n"
"\n"
"struct VS_OUTPUT {\n"
" float4 Position: SV_POSITION;\n"
" float2 Texture: TEXCOORD;\n"
"};\n"
"\n"
"VS_OUTPUT main (VS_INPUT input)\n"
"{\n"
" return input;\n"
"}";
static const gchar ps_str[] =
"Texture2D shaderTexture;\n"
"SamplerState samplerState;\n"
"\n"
"struct PS_INPUT {\n"
" float4 Position: SV_POSITION;\n"
" float2 Texture: TEXCOORD;\n"
"};\n"
"\n"
"struct PS_OUTPUT {\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"
"}";
/* *INDENT-ON* */
D3D11_INPUT_ELEMENT_DESC input_desc[] = {
{"POSITION",
0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD",
0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
};
ComPtr < ID3D11VertexShader > vs;
ComPtr < ID3D11InputLayout > layout;
ComPtr < ID3D11PixelShader > ps;
ComPtr < ID3D11SamplerState > sampler;
ComPtr < ID3D11BlendState > blend;
D3D11_SAMPLER_DESC sampler_desc;
D3D11_BLEND_DESC blend_desc;
ID3D11Device *device_handle;
HRESULT hr;
device_handle = gst_d3d11_device_get_device_handle (self->device);
if (!gst_d3d11_create_vertex_shader (self->device,
vs_str, input_desc, G_N_ELEMENTS (input_desc), &vs, &layout)) {
GST_ERROR_OBJECT (self, "Failed to create vertex shader");
return FALSE;
}
if (!gst_d3d11_create_pixel_shader (self->device, ps_str, &ps)) {
GST_ERROR_OBJECT (self, "Failed to create pixel shader");
return FALSE;
}
memset (&sampler_desc, 0, sizeof (D3D11_SAMPLER_DESC));
sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
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_NEVER;
sampler_desc.MinLOD = 0;
sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
hr = device_handle->CreateSamplerState (&sampler_desc, &sampler);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self,
"Failed to create sampler state, hr 0x%x", (guint) hr);
return FALSE;
}
blend_desc.AlphaToCoverageEnable = FALSE;
blend_desc.IndependentBlendEnable = FALSE;
blend_desc.RenderTarget[0].BlendEnable = TRUE;
blend_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
blend_desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
blend_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
blend_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
blend_desc.RenderTarget[0].RenderTargetWriteMask =
D3D11_COLOR_WRITE_ENABLE_ALL;
hr = device_handle->CreateBlendState (&blend_desc, &blend);
if (!gst_d3d11_result (hr, self->device)) {
GST_ERROR_OBJECT (self,
"Failed to create blend state, hr 0x%x", (guint) hr);
return FALSE;
}
self->vs = vs.Detach ();
self->ps = ps.Detach ();
self->layout = layout.Detach ();
self->sampler = sampler.Detach ();
self->blend = blend.Detach ();
return TRUE;
}
static gboolean static gboolean
gst_d3d11_screen_capture_src_start (GstBaseSrc * bsrc) gst_d3d11_screen_capture_src_start (GstBaseSrc * bsrc)
{ {
@ -655,6 +774,9 @@ gst_d3d11_screen_capture_src_start (GstBaseSrc * bsrc)
goto error; goto error;
} }
if (!gst_d3d11_screen_capture_prepare_shader (self))
goto error;
self->last_frame_no = -1; self->last_frame_no = -1;
self->min_latency = self->max_latency = GST_CLOCK_TIME_NONE; self->min_latency = self->max_latency = GST_CLOCK_TIME_NONE;
@ -690,6 +812,12 @@ gst_d3d11_screen_capture_src_stop (GstBaseSrc * bsrc)
gst_clear_object (&self->pool); gst_clear_object (&self->pool);
} }
GST_D3D11_CLEAR_COM (self->vs);
GST_D3D11_CLEAR_COM (self->ps);
GST_D3D11_CLEAR_COM (self->layout);
GST_D3D11_CLEAR_COM (self->sampler);
GST_D3D11_CLEAR_COM (self->blend);
gst_clear_object (&self->capture); gst_clear_object (&self->capture);
gst_clear_object (&self->device); gst_clear_object (&self->device);
@ -916,9 +1044,9 @@ again:
texture = (ID3D11Texture2D *) info.data; texture = (ID3D11Texture2D *) info.data;
before_capture = gst_clock_get_time (clock); before_capture = gst_clock_get_time (clock);
ret = ret = gst_d3d11_screen_capture_do_capture (self->capture, self->device,
gst_d3d11_screen_capture_do_capture (self->capture, texture, rtv, texture, rtv, self->vs, self->ps, self->layout, self->sampler,
&self->crop_box, draw_mouse); self->blend, &self->crop_box, draw_mouse);
gst_memory_unmap (mem, &info); gst_memory_unmap (mem, &info);
switch (ret) { switch (ret) {

View file

@ -143,9 +143,12 @@ gint
main (gint argc, gchar ** argv) main (gint argc, gchar ** argv)
{ {
GstElement *pipeline, *src, *queue, *sink; GstElement *pipeline, *src, *queue, *sink;
GstElement *pipeline_1 = nullptr, *src_1, *queue_1, *sink_1;
GMainLoop *loop; GMainLoop *loop;
gboolean ret; gboolean ret;
gboolean show_devices = FALSE; gboolean show_devices = FALSE;
gboolean multi_pipelines = FALSE;
gboolean show_cursor = FALSE;
gint64 hmonitor = 0; gint64 hmonitor = 0;
gint monitor_index = -1; gint monitor_index = -1;
GError *err = nullptr; GError *err = nullptr;
@ -158,6 +161,10 @@ main (gint argc, gchar ** argv)
"Address of HMONITOR handle", nullptr}, "Address of HMONITOR handle", nullptr},
{"index", 0, 0, G_OPTION_ARG_INT, &monitor_index, {"index", 0, 0, G_OPTION_ARG_INT, &monitor_index,
"Monitor index to capture (-1 for primary monitor)", nullptr}, "Monitor index to capture (-1 for primary monitor)", nullptr},
{"multi-pipelines", 0, 0, G_OPTION_ARG_NONE, &multi_pipelines,
"Run two separate pipelines for capturing a single monitor", nullptr},
{"show-cursor", 0, 0, G_OPTION_ARG_NONE, &show_cursor,
"Draw mouse cursor", nullptr},
{nullptr} {nullptr}
}; };
@ -185,12 +192,25 @@ main (gint argc, gchar ** argv)
} }
src = gst_device_create_element (device, nullptr); src = gst_device_create_element (device, nullptr);
gst_object_unref (device);
if (!src) { if (!src) {
g_warning ("Failed to create d3d11screencapture element"); g_warning ("Failed to create d3d11screencapture element");
return 1; return 1;
} }
g_object_set (src, "show-cursor", show_cursor, nullptr);
if (multi_pipelines) {
src_1 = gst_device_create_element (device, nullptr);
if (!src_1) {
g_warning ("Failed to create second d3d11screencapture element");
return 1;
}
g_object_set (src_1, "show-cursor", show_cursor, nullptr);
}
gst_object_unref (device);
loop = g_main_loop_new (nullptr, FALSE); loop = g_main_loop_new (nullptr, FALSE);
pipeline = gst_pipeline_new (nullptr); pipeline = gst_pipeline_new (nullptr);
@ -203,12 +223,31 @@ main (gint argc, gchar ** argv)
gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), (GstBusFunc) bus_msg, loop); gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), (GstBusFunc) bus_msg, loop);
gst_element_set_state (pipeline, GST_STATE_PLAYING); gst_element_set_state (pipeline, GST_STATE_PLAYING);
if (multi_pipelines) {
pipeline_1 = gst_pipeline_new (nullptr);
queue_1 = gst_element_factory_make ("queue", nullptr);
sink_1 = gst_element_factory_make ("d3d11videosink", nullptr);
gst_bin_add_many (GST_BIN (pipeline_1), src_1, queue_1, sink_1, nullptr);
gst_element_link_many (src_1, queue_1, sink_1, nullptr);
gst_bus_add_watch (GST_ELEMENT_BUS (pipeline_1), (GstBusFunc) bus_msg, loop);
gst_element_set_state (pipeline_1, GST_STATE_PLAYING);
}
g_main_loop_run (loop); g_main_loop_run (loop);
gst_element_set_state (pipeline, GST_STATE_NULL); gst_element_set_state (pipeline, GST_STATE_NULL);
gst_bus_remove_watch (GST_ELEMENT_BUS (pipeline)); gst_bus_remove_watch (GST_ELEMENT_BUS (pipeline));
gst_object_unref (pipeline); gst_object_unref (pipeline);
if (multi_pipelines) {
gst_element_set_state (pipeline_1, GST_STATE_NULL);
gst_bus_remove_watch (GST_ELEMENT_BUS (pipeline));
gst_object_unref (pipeline_1);
}
g_main_loop_unref (loop); g_main_loop_unref (loop);
return 0; return 0;