From fa05f19f14f76dfc6ed1f3a0cc1ac247104e49eb Mon Sep 17 00:00:00 2001 From: Jakub Adam Date: Fri, 14 May 2021 20:22:26 +0200 Subject: [PATCH] dxgiscreencapsrc: renegotiate caps on resolution change When desktop gets resized, recreate the textures and renegotiate the source caps with the updated video dimensions. Part-of: --- sys/winscreencap/dxgicapture.c | 49 ++++++++++++++++++++---------- sys/winscreencap/dxgicapture.h | 3 +- sys/winscreencap/gstdxgiscreencapsrc.c | 55 ++++++++++++++++++++++++---------- 3 files changed, 75 insertions(+), 32 deletions(-) diff --git a/sys/winscreencap/dxgicapture.c b/sys/winscreencap/dxgicapture.c index 5bd87db..92373bc 100644 --- a/sys/winscreencap/dxgicapture.c +++ b/sys/winscreencap/dxgicapture.c @@ -206,11 +206,12 @@ gst_dxgicap_shader_init (void) return ! !GstD3DCompileFunc; } -static gboolean +static GstFlowReturn initialize_output_duplication (DxgiCapture * self) { HDESK hdesk; HRESULT hr; + DXGI_OUTDUPL_DESC old_dupl_desc; GstDXGIScreenCapSrc *src = self->src; PTR_RELEASE (self->dxgi_dupl); @@ -235,10 +236,29 @@ initialize_output_duplication (DxgiCapture * self) GST_WARNING_OBJECT (src, "IDXGIOutput1::DuplicateOutput() failed (%x): %s", (guint) hr, msg); g_free (msg); - return FALSE; + if (hr == E_ACCESSDENIED) { + /* Happens temporarily during resolution changes. */ + return GST_FLOW_OK; + } + return GST_FLOW_ERROR; } - return TRUE; + old_dupl_desc = self->dupl_desc; + IDXGIOutputDuplication_GetDesc (self->dxgi_dupl, &self->dupl_desc); + + if (self->readable_texture && + (self->dupl_desc.ModeDesc.Width != old_dupl_desc.ModeDesc.Width || + self->dupl_desc.ModeDesc.Height != old_dupl_desc.ModeDesc.Height || + self->dupl_desc.Rotation != old_dupl_desc.Rotation)) { + PTR_RELEASE (self->readable_texture); + PTR_RELEASE (self->work_texture); + + _setup_texture (self); + + return GST_DXGICAP_FLOW_RESOLUTION_CHANGE; + } + + return GST_FLOW_OK; } DxgiCapture * @@ -314,11 +334,10 @@ dxgicap_new (HMONITOR monitor, GstDXGIScreenCapSrc * src) PTR_RELEASE (dxgi_factory1); - if (!initialize_output_duplication (self)) { + if (initialize_output_duplication (self) == GST_FLOW_ERROR) { goto new_error; } - IDXGIOutputDuplication_GetDesc (self->dxgi_dupl, &self->dupl_desc); self->pointer_buffer_capacity = INITIAL_POINTER_BUFFER_CAPACITY; self->pointer_buffer = g_malloc (self->pointer_buffer_capacity); if (NULL == self->pointer_buffer) { @@ -451,11 +470,11 @@ dxgicap_stop (DxgiCapture * self) PTR_RELEASE (self->work_texture); } -gboolean +GstFlowReturn dxgicap_acquire_next_frame (DxgiCapture * self, gboolean show_cursor, guint timeout) { - gboolean ret = FALSE; + GstFlowReturn ret = GST_FLOW_ERROR; HRESULT hr; GstDXGIScreenCapSrc *src = self->src; @@ -465,10 +484,8 @@ dxgicap_acquire_next_frame (DxgiCapture * self, gboolean show_cursor, if (!self->dxgi_dupl) { /* Desktop duplication interface became invalid due to desktop switch, * UAC prompt popping up, or similar event. Try to reinitialize. */ - if (!initialize_output_duplication (self)) { - ret = TRUE; - goto end; - } + ret = initialize_output_duplication (self); + goto end; } /* Get the latest desktop frames. */ @@ -478,13 +495,13 @@ dxgicap_acquire_next_frame (DxgiCapture * self, gboolean show_cursor, /* In case of DXGI_ERROR_WAIT_TIMEOUT, * it has not changed from the last time. */ GST_LOG_OBJECT (src, "DXGI_ERROR_WAIT_TIMEOUT"); - ret = TRUE; + ret = GST_FLOW_OK; goto end; } else if (hr == DXGI_ERROR_ACCESS_LOST) { GST_LOG_OBJECT (src, "DXGI_ERROR_ACCESS_LOST; reinitializing output " "duplication..."); PTR_RELEASE (self->dxgi_dupl); - ret = TRUE; + ret = GST_FLOW_OK; goto end; } HR_FAILED_GOTO (hr, IDXGIOutputDuplication::AcquireNextFrame, end); @@ -524,12 +541,12 @@ dxgicap_acquire_next_frame (DxgiCapture * self, gboolean show_cursor, } HR_FAILED_GOTO (hr, IDXGIOutputDuplication::GetFramePointerShape, end); self->pointer_shape_info = pointer_shape_info; - ret = TRUE; + ret = GST_FLOW_OK; } else { - ret = TRUE; + ret = GST_FLOW_OK; } } else { - ret = TRUE; + ret = GST_FLOW_OK; } end: if (self->dxgi_dupl) { diff --git a/sys/winscreencap/dxgicapture.h b/sys/winscreencap/dxgicapture.h index f2967b6..7d2ad03 100644 --- a/sys/winscreencap/dxgicapture.h +++ b/sys/winscreencap/dxgicapture.h @@ -56,6 +56,7 @@ } \ } G_STMT_END +#define GST_DXGICAP_FLOW_RESOLUTION_CHANGE GST_FLOW_CUSTOM_SUCCESS_1 typedef struct _GstDXGIScreenCapSrc GstDXGIScreenCapSrc; @@ -69,7 +70,7 @@ void dxgicap_destory (DxgiCapture * _this); gboolean dxgicap_start (DxgiCapture * _this); void dxgicap_stop (DxgiCapture * _this); -gint dxgicap_acquire_next_frame (DxgiCapture * _this, gboolean show_cursor, +GstFlowReturn dxgicap_acquire_next_frame (DxgiCapture * _this, gboolean show_cursor, guint timeout); gboolean dxgicap_copy_buffer (DxgiCapture * _this, gboolean show_cursor, LPRECT src_rect, GstVideoInfo * video_info, GstBuffer * buf); diff --git a/sys/winscreencap/gstdxgiscreencapsrc.c b/sys/winscreencap/gstdxgiscreencapsrc.c index df360b4..12c7980 100644 --- a/sys/winscreencap/gstdxgiscreencapsrc.c +++ b/sys/winscreencap/gstdxgiscreencapsrc.c @@ -126,9 +126,8 @@ static gboolean gst_dxgi_screen_cap_src_stop (GstBaseSrc * bsrc); static gboolean gst_dxgi_screen_cap_src_unlock (GstBaseSrc * bsrc); -static GstFlowReturn gst_dxgi_screen_cap_src_fill (GstPushSrc * pushsrc, - GstBuffer * buffer); - +static GstFlowReturn gst_dxgi_screen_cap_src_create (GstBaseSrc * pushsrc, + guint64 offset, guint length, GstBuffer ** buffer); static HMONITOR _get_hmonitor (GstDXGIScreenCapSrc * src); @@ -139,12 +138,10 @@ gst_dxgi_screen_cap_src_class_init (GstDXGIScreenCapSrcClass * klass) GObjectClass *go_class; GstElementClass *e_class; GstBaseSrcClass *bs_class; - GstPushSrcClass *ps_class; go_class = G_OBJECT_CLASS (klass); e_class = GST_ELEMENT_CLASS (klass); bs_class = GST_BASE_SRC_CLASS (klass); - ps_class = GST_PUSH_SRC_CLASS (klass); go_class->set_property = gst_dxgi_screen_cap_src_set_property; go_class->get_property = gst_dxgi_screen_cap_src_get_property; @@ -156,7 +153,7 @@ gst_dxgi_screen_cap_src_class_init (GstDXGIScreenCapSrcClass * klass) bs_class->stop = GST_DEBUG_FUNCPTR (gst_dxgi_screen_cap_src_stop); bs_class->unlock = GST_DEBUG_FUNCPTR (gst_dxgi_screen_cap_src_unlock); bs_class->fixate = GST_DEBUG_FUNCPTR (gst_dxgi_screen_cap_src_fixate); - ps_class->fill = GST_DEBUG_FUNCPTR (gst_dxgi_screen_cap_src_fill); + bs_class->create = GST_DEBUG_FUNCPTR (gst_dxgi_screen_cap_src_create); g_object_class_install_property (go_class, PROP_MONITOR, g_param_spec_int ("monitor", "Monitor", @@ -442,12 +439,15 @@ gst_dxgi_screen_cap_src_unlock (GstBaseSrc * bsrc) } static GstFlowReturn -gst_dxgi_screen_cap_src_fill (GstPushSrc * push_src, GstBuffer * buf) +gst_dxgi_screen_cap_src_create (GstBaseSrc * base_src, guint64 offset, + guint length, GstBuffer ** buf) { - GstDXGIScreenCapSrc *src = GST_DXGI_SCREEN_CAP_SRC (push_src); + GstDXGIScreenCapSrc *src = GST_DXGI_SCREEN_CAP_SRC (base_src); GstClock *clock; GstClockTime buf_time, buf_dur; guint64 frame_number; + GstFlowReturn ret; + GstBuffer *buffer = NULL; if (G_UNLIKELY (!src->dxgi_capture)) { GST_DEBUG_OBJECT (src, "format wasn't negotiated before create function"); @@ -534,15 +534,40 @@ gst_dxgi_screen_cap_src_fill (GstPushSrc * push_src, GstBuffer * buf) } /* Get the latest desktop frame. */ - if (dxgicap_acquire_next_frame (src->dxgi_capture, src->show_cursor, 0)) { - /* Copy the latest desktop frame to the video frame. */ - if (dxgicap_copy_buffer (src->dxgi_capture, src->show_cursor, - &src->src_rect, &src->video_info, buf)) { - GST_BUFFER_TIMESTAMP (buf) = buf_time; - GST_BUFFER_DURATION (buf) = buf_dur; - return GST_FLOW_OK; + do { + ret = dxgicap_acquire_next_frame (src->dxgi_capture, src->show_cursor, 0); + if (ret == GST_DXGICAP_FLOW_RESOLUTION_CHANGE) { + GST_DEBUG_OBJECT (src, "Resolution change detected."); + + if (!gst_base_src_negotiate (GST_BASE_SRC (src))) { + return GST_FLOW_NOT_NEGOTIATED; + } } + } while (ret == GST_DXGICAP_FLOW_RESOLUTION_CHANGE); + + if (ret != GST_FLOW_OK) { + return ret; + } + + ret = + GST_BASE_SRC_CLASS (g_type_class_peek_parent (parent_class))->alloc + (base_src, offset, length, &buffer); + if (ret != GST_FLOW_OK) { + return ret; } + + /* Copy the latest desktop frame to the video frame. */ + if (dxgicap_copy_buffer (src->dxgi_capture, src->show_cursor, + &src->src_rect, &src->video_info, buffer)) { + GST_BUFFER_TIMESTAMP (buffer) = buf_time; + GST_BUFFER_DURATION (buffer) = buf_dur; + *buf = buffer; + + return GST_FLOW_OK; + } + + gst_clear_buffer (&buffer); + return GST_FLOW_ERROR; } -- 2.7.4