texture_desc.BindFlags =
D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
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,
nullptr, &shared_texture_);
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");
D3D11_SUBRESOURCE_DATA InitData;
D3D11_TEXTURE2D_DESC Desc;
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 =
- gst_d3d11_device_get_device_context_handle (device_);
+ gst_d3d11_device_get_device_context_handle (device);
VERTEX Vertices[NUMVERTICES] =
{
// Create mouseshape as texture
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");
return false;
}
// Create shader resource from texture
hr = device_handle->CreateShaderResourceView(MouseTex.Get(), &SDesc,
&ShaderRes);
- if (!gst_d3d11_result (hr, device_)) {
+ if (!gst_d3d11_result (hr, device)) {
GST_ERROR ("Failed to create shader resource view for rendering mouse");
return false;
}
// Create vertex buffer
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");
return false;
}
FLOAT BlendFactor[4] = {0.f, 0.f, 0.f, 0.f};
UINT Stride = sizeof(VERTEX);
UINT Offset = 0;
- ID3D11SamplerState *samplers = sampler_.Get();
ID3D11ShaderResourceView *srv = ShaderRes.Get();
ID3D11Buffer *vert_buf = VertexBufferMouse.Get();
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->VSSetShader(vs_.Get(), nullptr, 0);
- context_handle->PSSetShader(ps_.Get(), nullptr, 0);
+ context_handle->VSSetShader(vs, nullptr, 0);
+ context_handle->PSSetShader(ps, nullptr, 0);
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->IASetInputLayout(layout_.Get());
+ context_handle->IASetInputLayout(layout);
D3D11_VIEWPORT VP;
VP.Width = static_cast<FLOAT>(FullDesc.Width);
return true;
}
- void
- CopyToTexture (ID3D11Texture2D * texture, D3D11_BOX * cropBox)
+ GstFlowReturn
+ CopyToTexture (GstD3D11Device * device, ID3D11Texture2D * texture,
+ D3D11_BOX * cropBox)
{
- ID3D11DeviceContext *context_handle =
- gst_d3d11_device_get_device_context_handle (device_);
+ ID3D11DeviceContext *context_handle = nullptr;
+ 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,
- 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
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 */
vs_ = vs;
ps_ = ps;
layout_ = layout;
sampler_ = sampler;
- blend_ = blend;
return true;
}
ComPtr<ID3D11InputLayout> layout_;
ComPtr<ID3D11SamplerState> sampler_;
ComPtr<IDXGIOutputDuplication> dupl_;
- ComPtr<ID3D11BlendState> blend_;
/* frame metadata */
BYTE *metadata_buffer_;
HMONITOR monitor_handle;
RECT desktop_coordinates;
gboolean prepared;
+ gint64 adapter_luid;
GRecMutex lock;
};
self->desktop_coordinates.right, self->desktop_coordinates.bottom,
self->cached_width, self->cached_height);
+ g_object_get (self->device, "adapter-luid", &self->adapter_luid, nullptr);
+
ret = TRUE;
out:
GstFlowReturn
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)
{
GstFlowReturn ret = GST_FLOW_OK;
+ gboolean shared_device = FALSE;
guint width, height;
g_return_val_if_fail (GST_IS_D3D11_SCREEN_CAPTURE (capture), 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);
if (!capture->prepared)
ret = gst_d3d11_screen_capture_prepare (capture);
}
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);
g_rec_mutex_unlock (&capture->lock);
- return GST_FLOW_OK;
+ return ret;
}
HRESULT
#include "gstd3d11screencapturesrc.h"
#include "gstd3d11screencapture.h"
#include "gstd3d11pluginutils.h"
+#include "gstd3d11shader.h"
#include <wrl.h>
#include <string.h>
GstClockTime max_latency;
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 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
gst_d3d11_screen_capture_src_start (GstBaseSrc * bsrc)
{
GstD3D11ScreenCaptureSrc *self = GST_D3D11_SCREEN_CAPTURE_SRC (bsrc);
goto error;
}
+ if (!gst_d3d11_screen_capture_prepare_shader (self))
+ goto error;
+
self->last_frame_no = -1;
self->min_latency = self->max_latency = GST_CLOCK_TIME_NONE;
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->device);
texture = (ID3D11Texture2D *) info.data;
before_capture = gst_clock_get_time (clock);
- ret =
- gst_d3d11_screen_capture_do_capture (self->capture, texture, rtv,
- &self->crop_box, draw_mouse);
+ ret = gst_d3d11_screen_capture_do_capture (self->capture, self->device,
+ texture, rtv, self->vs, self->ps, self->layout, self->sampler,
+ self->blend, &self->crop_box, draw_mouse);
gst_memory_unmap (mem, &info);
switch (ret) {
main (gint argc, gchar ** argv)
{
GstElement *pipeline, *src, *queue, *sink;
+ GstElement *pipeline_1 = nullptr, *src_1, *queue_1, *sink_1;
GMainLoop *loop;
gboolean ret;
gboolean show_devices = FALSE;
+ gboolean multi_pipelines = FALSE;
+ gboolean show_cursor = FALSE;
gint64 hmonitor = 0;
gint monitor_index = -1;
GError *err = nullptr;
"Address of HMONITOR handle", nullptr},
{"index", 0, 0, G_OPTION_ARG_INT, &monitor_index,
"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}
};
}
src = gst_device_create_element (device, nullptr);
- gst_object_unref (device);
if (!src) {
g_warning ("Failed to create d3d11screencapture element");
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);
pipeline = gst_pipeline_new (nullptr);
gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), (GstBusFunc) bus_msg, loop);
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);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_bus_remove_watch (GST_ELEMENT_BUS (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);
return 0;