{
/* signals */
SIGNAL_BEGIN_DRAW,
+ SIGNAL_PRESENT,
/* actions */
SIGNAL_DRAW,
G_TYPE_BOOLEAN, 4, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT64,
G_TYPE_UINT64);
+ /**
+ * GstD3D11VideoSink::present
+ * @videosink: the #GstD3D11VideoSink
+ * @device: a #GstD3D11Device handle
+ * @render_target: a ID3D11RenderTargetView handle of swapchain's backbuffer
+ *
+ * Emitted just before presenting a texture via the IDXGISwapChain::Present.
+ * The client can perform additional rendering on the given @render_target,
+ * or can read the content already rendered on the swapchain's backbuffer.
+ *
+ * This signal will be emitted with gst_d3d11_device_lock() taken and
+ * client should perform GPU operation from the thread where this signal
+ * emitted.
+ *
+ * If a client wants to listen this signal, the client must connect this
+ * signal before the first present. Otherwise this signal will not be
+ * emitted.
+ *
+ * Since: 1.22
+ */
+ gst_d3d11_video_sink_signals[SIGNAL_PRESENT] =
+ g_signal_new ("present", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0, nullptr, nullptr, nullptr,
+ G_TYPE_NONE, 2, GST_TYPE_D3D11_DEVICE, G_TYPE_POINTER);
+
element_class->set_context =
GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_set_context);
gst_navigation_send_event_simple (GST_NAVIGATION (self), mouse_event);
}
+static void
+gst_d3d11_video_sink_present (GstD3D11Window * window, GstD3D11Device * device,
+ ID3D11RenderTargetView * rtv, GstD3D11VideoSink * self)
+{
+ g_signal_emit (self, gst_d3d11_video_sink_signals[SIGNAL_PRESENT], 0,
+ device, rtv);
+}
+
static gboolean
gst_d3d11_video_sink_start (GstBaseSink * sink)
{
gst_d3d11_video_sink_prepare_window (GstD3D11VideoSink * self)
{
GstD3D11WindowNativeType window_type = GST_D3D11_WINDOW_NATIVE_TYPE_HWND;
+ gboolean emit_present = FALSE;
if (self->window)
return TRUE;
return FALSE;
}
+ /* Emits present signal only if signal is connected for performance reason */
+ if (g_signal_has_handler_pending (self,
+ gst_d3d11_video_sink_signals[SIGNAL_PRESENT], 0, FALSE)) {
+ emit_present = TRUE;
+ }
+
g_object_set (self->window,
"force-aspect-ratio", self->force_aspect_ratio,
"fullscreen-toggle-mode", self->fullscreen_toggle_mode,
"fullscreen", self->fullscreen,
- "enable-navigation-events", self->enable_navigation_events, NULL);
+ "enable-navigation-events", self->enable_navigation_events,
+ "emit-present", emit_present, nullptr);
gst_d3d11_window_set_orientation (self->window, self->selected_method);
G_CALLBACK (gst_d3d11_video_sink_key_event), self);
g_signal_connect (self->window, "mouse-event",
G_CALLBACK (gst_d3d11_video_mouse_key_event), self);
+ g_signal_connect (self->window, "present",
+ G_CALLBACK (gst_d3d11_video_sink_present), self);
return TRUE;
}
PROP_FULLSCREEN,
PROP_WINDOW_HANDLE,
PROP_RENDER_STATS,
+ PROP_EMIT_PRESENT,
};
#define DEFAULT_ENABLE_NAVIGATION_EVENTS TRUE
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
#define DEFAULT_FULLSCREEN_TOGGLE_MODE GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE
#define DEFAULT_FULLSCREEN FALSE
-#define DEFAULT_RENDER_STATS FALSE
+#define DEFAULT_EMIT_PRESENT FALSE
enum
{
SIGNAL_KEY_EVENT,
SIGNAL_MOUSE_EVENT,
+ SIGNAL_PRESENT,
SIGNAL_LAST
};
(GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS)));
+ g_object_class_install_property (gobject_class, PROP_EMIT_PRESENT,
+ g_param_spec_boolean ("emit-present", "Emit Present",
+ "Emit present signal", DEFAULT_EMIT_PRESENT,
+ (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
d3d11_window_signals[SIGNAL_KEY_EVENT] =
g_signal_new ("key-event", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
g_signal_new ("mouse-event", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+
+ d3d11_window_signals[SIGNAL_PRESENT] =
+ g_signal_new ("present", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0, nullptr, nullptr, nullptr,
+ G_TYPE_NONE, 2, GST_TYPE_D3D11_DEVICE, G_TYPE_POINTER);
}
static void
self->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS;
self->fullscreen_toggle_mode = GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE;
self->fullscreen = DEFAULT_FULLSCREEN;
- self->render_stats = DEFAULT_RENDER_STATS;
+ self->emit_present = DEFAULT_EMIT_PRESENT;
}
static void
case PROP_WINDOW_HANDLE:
self->external_handle = (guintptr) g_value_get_pointer (value);
break;
+ case PROP_EMIT_PRESENT:
+ self->emit_present = g_value_get_boolean (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
case PROP_FULLSCREEN:
g_value_set_boolean (value, self->fullscreen);
break;
+ case PROP_EMIT_PRESENT:
+ g_value_set_boolean (value, self->emit_present);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
return GST_FLOW_ERROR;
}
+ /* We use flip mode swapchain and will not redraw borders.
+ * So backbuffer should be cleared manually in order to remove artifact of
+ * previous client's rendering on present signal */
+ if (self->emit_present) {
+ const FLOAT clear_color[] = { 0.0f, 0.0f, 0.0f, 1.0f };
+ ID3D11DeviceContext *context =
+ gst_d3d11_device_get_device_context_handle (self->device);
+
+ context->ClearRenderTargetView (rtv, clear_color);
+ }
+
crop_meta = gst_buffer_get_video_crop_meta (buffer);
if (crop_meta) {
input_rect.left = crop_meta->x;
if (self->allow_tearing && self->fullscreen)
present_flags |= DXGI_PRESENT_ALLOW_TEARING;
- if (klass->present)
+ if (klass->present) {
+ if (self->emit_present) {
+ g_signal_emit (self, d3d11_window_signals[SIGNAL_PRESENT], 0,
+ self->device, rtv, nullptr);
+ }
ret = klass->present (self, present_flags);
+ }
self->first_present = FALSE;
GstD3D11WindowFullscreenToggleMode fullscreen_toggle_mode;
gboolean requested_fullscreen;
gboolean fullscreen;
- gboolean render_stats;
+ gboolean emit_present;
GstVideoInfo info;
GstVideoInfo render_info;
IDXGISwapChain1 *swap_chain = (IDXGISwapChain1 *) window->swap_chain;
/* the first present should not specify dirty-rect */
- if (!window->first_present) {
+ if (!window->first_present && !window->emit_present) {
present_params.DirtyRectsCount = 1;
present_params.pDirtyRects = &window->render_rect;
}
IDXGISwapChain1 *swap_chain = (IDXGISwapChain1 *) window->swap_chain;
/* the first present should not specify dirty-rect */
- if (!window->first_present) {
+ if (!window->first_present && !window->emit_present) {
present_params.DirtyRectsCount = 1;
present_params.pDirtyRects = &window->render_rect;
}
GstD3D11WindowWin32OverlayState overlay_state;
HDC device_handle;
- gboolean first_present;
gboolean have_swapchain1;
/* atomic */
DXGI_PRESENT_PARAMETERS present_params = { 0, };
/* the first present should not specify dirty-rect */
- if (!window->first_present) {
+ if (!window->first_present && !window->emit_present) {
present_params.DirtyRectsCount = 1;
present_params.pDirtyRects = &window->render_rect;
}