d3d11screencapturesrc: Allow capturing screen sub-area

Adds crop-x, crop-y, crop-width, crop-height properties specifying the
screen area to capture.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1932>
This commit is contained in:
Jakub Adam 2022-03-10 17:37:26 +01:00
parent 253ee75a72
commit df55700f44
3 changed files with 127 additions and 20 deletions

View file

@ -344,7 +344,7 @@ public:
return GST_FLOW_OK; return GST_FLOW_OK;
} }
bool DrawMouse (ID3D11RenderTargetView * rtv) bool DrawMouse (ID3D11RenderTargetView * rtv, D3D11_BOX * cropBox)
{ {
GST_TRACE ("Drawing mouse"); GST_TRACE ("Drawing mouse");
@ -428,14 +428,21 @@ public:
break; break;
} }
/* Nothing draw */ /* Nothing to draw */
if (PtrWidth == 0 || PtrHeight == 0) { if (PtrWidth == 0 || PtrHeight == 0 ||
(PtrLeft + PtrWidth) < static_cast<INT>(cropBox->left) ||
PtrLeft > static_cast<INT>(cropBox->right) ||
(PtrTop + PtrHeight) < static_cast<INT>(cropBox->top) ||
PtrTop > static_cast<INT>(cropBox->bottom)) {
if (InitBuffer) if (InitBuffer)
delete[] InitBuffer; delete[] InitBuffer;
return true; return true;
} }
PtrLeft -= cropBox->left;
PtrTop -= cropBox->top;
Vertices[0].Pos.x = (PtrLeft - CenterX) / (FLOAT)CenterX; Vertices[0].Pos.x = (PtrLeft - CenterX) / (FLOAT)CenterX;
Vertices[0].Pos.y = -1 * ((PtrTop + PtrHeight) - CenterY) / (FLOAT)CenterY; Vertices[0].Pos.y = -1 * ((PtrTop + PtrHeight) - CenterY) / (FLOAT)CenterY;
Vertices[1].Pos.x = (PtrLeft - CenterX) / (FLOAT)CenterX; Vertices[1].Pos.x = (PtrLeft - CenterX) / (FLOAT)CenterX;
@ -523,13 +530,13 @@ public:
} }
void void
CopyToTexture (ID3D11Texture2D * texture) CopyToTexture (ID3D11Texture2D * texture, D3D11_BOX * cropBox)
{ {
ID3D11DeviceContext *context_handle = ID3D11DeviceContext *context_handle =
gst_d3d11_device_get_device_context_handle (device_); gst_d3d11_device_get_device_context_handle (device_);
context_handle->CopySubresourceRegion (texture, 0, 0, 0, 0, context_handle->CopySubresourceRegion (texture, 0, 0, 0, 0,
shared_texture_.Get(), 0, nullptr); shared_texture_.Get(), 0, cropBox);
} }
void void
@ -1790,10 +1797,9 @@ 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, ID3D11Texture2D * texture, ID3D11RenderTargetView * rtv,
gboolean draw_mouse) D3D11_BOX * crop_box, gboolean draw_mouse)
{ {
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
D3D11_TEXTURE2D_DESC desc;
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);
@ -1811,11 +1817,12 @@ gst_d3d11_screen_capture_do_capture (GstD3D11ScreenCapture * capture,
gst_d3d11_screen_capture_get_size (capture, &width, &height); gst_d3d11_screen_capture_get_size (capture, &width, &height);
texture->GetDesc (&desc); if (crop_box->left > width || crop_box->right > width ||
if (desc.Width != width || desc.Height != height) { crop_box->top > height || crop_box->bottom > height) {
GST_INFO_OBJECT (capture, GST_INFO_OBJECT (capture,
"Different texture size, ours: %dx%d, external: %dx%d", "Capture area (%u, %u, %u, %u) doesn't fit into screen size %ux%u",
width, height, desc.Width, desc.Height); crop_box->left, crop_box->right, crop_box->top,
crop_box->bottom, width, height);
g_rec_mutex_unlock (&capture->lock); g_rec_mutex_unlock (&capture->lock);
return GST_D3D11_SCREEN_CAPTURE_FLOW_SIZE_CHANGED; return GST_D3D11_SCREEN_CAPTURE_FLOW_SIZE_CHANGED;
@ -1843,9 +1850,9 @@ gst_d3d11_screen_capture_do_capture (GstD3D11ScreenCapture * capture,
GST_LOG_OBJECT (capture, "Capture done"); GST_LOG_OBJECT (capture, "Capture done");
capture->dupl_obj->CopyToTexture (texture); capture->dupl_obj->CopyToTexture (texture, crop_box);
if (draw_mouse) if (draw_mouse)
capture->dupl_obj->DrawMouse (rtv); 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);

View file

@ -45,7 +45,8 @@ gboolean gst_d3d11_screen_capture_get_size (GstD3D11ScreenCapture * captu
GstFlowReturn gst_d3d11_screen_capture_do_capture (GstD3D11ScreenCapture * capture, GstFlowReturn gst_d3d11_screen_capture_do_capture (GstD3D11ScreenCapture * capture,
ID3D11Texture2D * texture, ID3D11Texture2D * texture,
ID3D11RenderTargetView *rtv, ID3D11RenderTargetView * rtv,
D3D11_BOX * crop_box,
gboolean draw_mouse); gboolean draw_mouse);
HRESULT gst_d3d11_screen_capture_find_output_for_monitor (HMONITOR monitor, HRESULT gst_d3d11_screen_capture_find_output_for_monitor (HMONITOR monitor,

View file

@ -55,6 +55,10 @@ enum
PROP_MONITOR_INDEX, PROP_MONITOR_INDEX,
PROP_MONITOR_HANDLE, PROP_MONITOR_HANDLE,
PROP_SHOW_CURSOR, PROP_SHOW_CURSOR,
PROP_CROP_X,
PROP_CROP_Y,
PROP_CROP_WIDTH,
PROP_CROP_HEIGHT,
PROP_LAST, PROP_LAST,
}; };
@ -87,6 +91,12 @@ struct _GstD3D11ScreenCaptureSrc
HMONITOR monitor_handle; HMONITOR monitor_handle;
gboolean show_cursor; gboolean show_cursor;
guint crop_x;
guint crop_y;
guint crop_w;
guint crop_h;
D3D11_BOX crop_box;
gboolean flushing; gboolean flushing;
GstClockTime min_latency; GstClockTime min_latency;
GstClockTime max_latency; GstClockTime max_latency;
@ -157,6 +167,30 @@ gst_d3d11_screen_capture_src_class_init (GstD3D11ScreenCaptureSrcClass * klass)
DEFAULT_SHOW_CURSOR, DEFAULT_SHOW_CURSOR,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
properties[PROP_CROP_X] =
g_param_spec_uint ("crop-x", "Crop X",
"Horizontal coordinate of top left corner for the screen capture area",
0, G_MAXUINT, 0,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
properties[PROP_CROP_Y] =
g_param_spec_uint ("crop-y", "Crop Y",
"Vertical coordinate of top left corner for the screen capture area",
0, G_MAXUINT, 0,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
properties[PROP_CROP_WIDTH] =
g_param_spec_uint ("crop-width", "Crop Width",
"Width of screen capture area (0 = maximum)",
0, G_MAXUINT, 0,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
properties[PROP_CROP_HEIGHT] =
g_param_spec_uint ("crop-height", "Crop Height",
"Height of screen capture area (0 = maximum)",
0, G_MAXUINT, 0,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (gobject_class, PROP_LAST, properties); g_object_class_install_properties (gobject_class, PROP_LAST, properties);
element_class->set_context = element_class->set_context =
@ -232,6 +266,18 @@ gst_d3d11_screen_capture_src_set_property (GObject * object, guint prop_id,
case PROP_SHOW_CURSOR: case PROP_SHOW_CURSOR:
self->show_cursor = g_value_get_boolean (value); self->show_cursor = g_value_get_boolean (value);
break; break;
case PROP_CROP_X:
self->crop_x = g_value_get_uint (value);
break;
case PROP_CROP_Y:
self->crop_y = g_value_get_uint (value);
break;
case PROP_CROP_WIDTH:
self->crop_w = g_value_get_uint (value);
break;
case PROP_CROP_HEIGHT:
self->crop_h = g_value_get_uint (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -254,6 +300,18 @@ gst_d3d11_screen_capture_src_get_property (GObject * object, guint prop_id,
case PROP_SHOW_CURSOR: case PROP_SHOW_CURSOR:
g_value_set_boolean (value, self->show_cursor); g_value_set_boolean (value, self->show_cursor);
break; break;
case PROP_CROP_X:
g_value_set_uint (value, self->crop_x);
break;
case PROP_CROP_Y:
g_value_set_uint (value, self->crop_y);
break;
case PROP_CROP_WIDTH:
g_value_set_uint (value, self->crop_w);
break;
case PROP_CROP_HEIGHT:
g_value_set_uint (value, self->crop_h);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -272,6 +330,36 @@ gst_d3d11_screen_capture_src_set_context (GstElement * element,
GST_ELEMENT_CLASS (parent_class)->set_context (element, context); GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
} }
static D3D11_BOX
gst_d3d11_screen_capture_src_get_crop_box (GstD3D11ScreenCaptureSrc * self)
{
D3D11_BOX box;
guint screen_width, screen_height;
box.front = 0;
box.back = 1;
gst_d3d11_screen_capture_get_size (self->capture, &screen_width,
&screen_height);
if ((self->crop_x + self->crop_w) > screen_width ||
(self->crop_y + self->crop_h) > screen_height) {
GST_WARNING ("Capture region outside of the screen bounds; ignoring.");
box.left = 0;
box.top = 0;
box.right = screen_width;
box.bottom = screen_height;
} else {
box.left = self->crop_x;
box.top = self->crop_y;
box.right = self->crop_w ? (self->crop_x + self->crop_w) : screen_width;
box.bottom = self->crop_h ? (self->crop_y + self->crop_h) : screen_height;
}
return box;
}
static GstCaps * static GstCaps *
gst_d3d11_screen_capture_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter) gst_d3d11_screen_capture_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
{ {
@ -284,11 +372,9 @@ gst_d3d11_screen_capture_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
return gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (bsrc)); return gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (bsrc));
} }
if (!gst_d3d11_screen_capture_get_size (self->capture, &width, &height)) { self->crop_box = gst_d3d11_screen_capture_src_get_crop_box (self);
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, width = self->crop_box.right - self->crop_box.left;
("Cannot query supported resolution"), (NULL)); height = self->crop_box.bottom - self->crop_box.top;
return NULL;
}
caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (bsrc)); caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (bsrc));
caps = gst_caps_make_writable (caps); caps = gst_caps_make_writable (caps);
@ -691,6 +777,7 @@ gst_d3d11_screen_capture_src_create (GstBaseSrc * bsrc, guint64 offset,
gint unsupported_retry_count = 100; gint unsupported_retry_count = 100;
GstBuffer *buffer = NULL; GstBuffer *buffer = NULL;
GstBuffer *sysmem_buf = NULL; GstBuffer *sysmem_buf = NULL;
D3D11_BOX crop_box;
if (!self->capture) { if (!self->capture) {
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
@ -704,6 +791,18 @@ gst_d3d11_screen_capture_src_create (GstBaseSrc * bsrc, guint64 offset,
if (fps_n <= 0 || fps_d <= 0) if (fps_n <= 0 || fps_d <= 0)
return GST_FLOW_NOT_NEGOTIATED; return GST_FLOW_NOT_NEGOTIATED;
crop_box = gst_d3d11_screen_capture_src_get_crop_box (self);
if (crop_box.left != self->crop_box.left ||
crop_box.right != self->crop_box.right ||
crop_box.top != self->crop_box.top ||
crop_box.bottom != self->crop_box.bottom) {
GST_INFO_OBJECT (self, "Capture area changed, need negotiation");
if (!gst_base_src_negotiate (bsrc)) {
GST_ERROR_OBJECT (self, "Failed to negotiate with new capture area");
return GST_FLOW_NOT_NEGOTIATED;
}
}
again: again:
clock = gst_element_get_clock (GST_ELEMENT_CAST (self)); clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
if (!clock) { if (!clock) {
@ -820,7 +919,7 @@ again:
before_capture = gst_clock_get_time (clock); before_capture = gst_clock_get_time (clock);
ret = ret =
gst_d3d11_screen_capture_do_capture (self->capture, texture, rtv, gst_d3d11_screen_capture_do_capture (self->capture, texture, rtv,
draw_mouse); &self->crop_box, draw_mouse);
gst_memory_unmap (mem, &info); gst_memory_unmap (mem, &info);
switch (ret) { switch (ret) {