mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-28 11:10:37 +00:00
a27c5c81df
Deadlock sequence: * From a streaming thread, d3d11videosink sends synchronous message to the parent window, so that internal (child) window can be constructed on the parent window's thread * App thread (parent window thread) is waiting for pipeline's state change (to GST_STATE_NULL) but streaming thread is blocked and waiting for app thread To avoid the deadlock, GstD3D11WindowWin32 should send message to the parent window asynchronously. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3570>
336 lines
10 KiB
C++
336 lines
10 KiB
C++
/*
|
|
* GStreamer
|
|
* Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "gstd3d11window_dummy.h"
|
|
#include "gstd3d11pluginutils.h"
|
|
#include <wrl.h>
|
|
|
|
/* *INDENT-OFF* */
|
|
using namespace Microsoft::WRL;
|
|
/* *INDENT-ON* */
|
|
|
|
GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug);
|
|
#define GST_CAT_DEFAULT gst_d3d11_window_debug
|
|
|
|
struct _GstD3D11WindowDummy
|
|
{
|
|
GstD3D11Window parent;
|
|
|
|
ID3D11Texture2D *fallback_texture;
|
|
ID3D11VideoProcessorOutputView *fallback_pov;
|
|
ID3D11RenderTargetView *fallback_rtv;
|
|
|
|
GstD3D11Fence *fence;
|
|
};
|
|
|
|
#define gst_d3d11_window_dummy_parent_class parent_class
|
|
G_DEFINE_TYPE (GstD3D11WindowDummy, gst_d3d11_window_dummy,
|
|
GST_TYPE_D3D11_WINDOW);
|
|
|
|
static void gst_d3d11_window_dummy_on_resize (GstD3D11Window * window,
|
|
guint width, guint height);
|
|
static GstFlowReturn gst_d3d11_window_dummy_prepare (GstD3D11Window * window,
|
|
guint display_width, guint display_height, GstCaps * caps,
|
|
GstStructure * config, DXGI_FORMAT display_format, GError ** error);
|
|
static void gst_d3d11_window_dummy_unprepare (GstD3D11Window * window);
|
|
static gboolean
|
|
gst_d3d11_window_dummy_open_shared_handle (GstD3D11Window * window,
|
|
GstD3D11WindowSharedHandleData * data);
|
|
static gboolean
|
|
gst_d3d11_window_dummy_release_shared_handle (GstD3D11Window * window,
|
|
GstD3D11WindowSharedHandleData * data);
|
|
|
|
static void
|
|
gst_d3d11_window_dummy_class_init (GstD3D11WindowDummyClass * klass)
|
|
{
|
|
GstD3D11WindowClass *window_class = GST_D3D11_WINDOW_CLASS (klass);
|
|
|
|
window_class->on_resize =
|
|
GST_DEBUG_FUNCPTR (gst_d3d11_window_dummy_on_resize);
|
|
window_class->prepare = GST_DEBUG_FUNCPTR (gst_d3d11_window_dummy_prepare);
|
|
window_class->unprepare =
|
|
GST_DEBUG_FUNCPTR (gst_d3d11_window_dummy_unprepare);
|
|
window_class->open_shared_handle =
|
|
GST_DEBUG_FUNCPTR (gst_d3d11_window_dummy_open_shared_handle);
|
|
window_class->release_shared_handle =
|
|
GST_DEBUG_FUNCPTR (gst_d3d11_window_dummy_release_shared_handle);
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_window_dummy_init (GstD3D11WindowDummy * self)
|
|
{
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_d3d11_window_dummy_prepare (GstD3D11Window * window,
|
|
guint display_width, guint display_height, GstCaps * caps,
|
|
GstStructure * config, DXGI_FORMAT display_format, GError ** error)
|
|
{
|
|
gst_clear_object (&window->compositor);
|
|
gst_clear_object (&window->converter);
|
|
|
|
/* We are supporting only RGBA, BGRA or RGB10A2_LE formats but we don't know
|
|
* which format texture will be used at this moment */
|
|
|
|
gst_video_info_from_caps (&window->info, caps);
|
|
window->render_rect.left = 0;
|
|
window->render_rect.top = 0;
|
|
window->render_rect.right = display_width;
|
|
window->render_rect.bottom = display_height;
|
|
|
|
window->input_rect.left = 0;
|
|
window->input_rect.top = 0;
|
|
window->input_rect.right = GST_VIDEO_INFO_WIDTH (&window->info);
|
|
window->input_rect.bottom = GST_VIDEO_INFO_HEIGHT (&window->info);
|
|
|
|
gst_video_info_set_format (&window->render_info,
|
|
GST_VIDEO_FORMAT_BGRA, display_width, display_height);
|
|
|
|
/* TODO: not sure which colorspace should be used, let's use BT709 since
|
|
* it's default and most common one */
|
|
window->render_info.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
|
|
window->render_info.colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
|
|
window->render_info.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
|
|
|
|
if (config) {
|
|
gst_structure_set (config, GST_D3D11_CONVERTER_OPT_BACKEND,
|
|
GST_TYPE_D3D11_CONVERTER_BACKEND, GST_D3D11_CONVERTER_BACKEND_SHADER,
|
|
nullptr);
|
|
} else {
|
|
config = gst_structure_new ("converter-config",
|
|
GST_D3D11_CONVERTER_OPT_BACKEND, GST_TYPE_D3D11_CONVERTER_BACKEND,
|
|
GST_D3D11_CONVERTER_BACKEND_SHADER, nullptr);
|
|
}
|
|
|
|
GstD3D11DeviceLockGuard lk (window->device);
|
|
window->converter = gst_d3d11_converter_new (window->device, &window->info,
|
|
&window->render_info, config);
|
|
|
|
if (!window->converter) {
|
|
GST_ERROR_OBJECT (window, "Cannot create converter");
|
|
g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
|
|
"Cannot create converter");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
window->compositor =
|
|
gst_d3d11_overlay_compositor_new (window->device, &window->render_info);
|
|
if (!window->compositor) {
|
|
GST_ERROR_OBJECT (window, "Cannot create overlay compositor");
|
|
g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
|
|
"Cannot create overlay compositor");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_window_dummy_unprepare (GstD3D11Window * window)
|
|
{
|
|
GstD3D11WindowDummy *self = GST_D3D11_WINDOW_DUMMY (window);
|
|
|
|
gst_clear_d3d11_fence (&self->fence);
|
|
}
|
|
|
|
static void
|
|
gst_d3d11_window_dummy_on_resize (GstD3D11Window * window,
|
|
guint width, guint height)
|
|
{
|
|
GstVideoRectangle src_rect, dst_rect, rst_rect;
|
|
|
|
dst_rect.x = 0;
|
|
dst_rect.y = 0;
|
|
dst_rect.w = width;
|
|
dst_rect.h = height;
|
|
|
|
if (window->force_aspect_ratio) {
|
|
src_rect.x = 0;
|
|
src_rect.y = 0;
|
|
src_rect.w = GST_VIDEO_INFO_WIDTH (&window->render_info);
|
|
src_rect.h = GST_VIDEO_INFO_HEIGHT (&window->render_info);
|
|
|
|
switch (window->method) {
|
|
case GST_VIDEO_ORIENTATION_90R:
|
|
case GST_VIDEO_ORIENTATION_90L:
|
|
case GST_VIDEO_ORIENTATION_UL_LR:
|
|
case GST_VIDEO_ORIENTATION_UR_LL:
|
|
src_rect.w = GST_VIDEO_INFO_HEIGHT (&window->render_info);
|
|
src_rect.h = GST_VIDEO_INFO_WIDTH (&window->render_info);
|
|
break;
|
|
default:
|
|
src_rect.w = GST_VIDEO_INFO_WIDTH (&window->render_info);
|
|
src_rect.h = GST_VIDEO_INFO_HEIGHT (&window->render_info);
|
|
break;
|
|
}
|
|
|
|
gst_video_sink_center_rect (src_rect, dst_rect, &rst_rect, TRUE);
|
|
} else {
|
|
rst_rect = dst_rect;
|
|
}
|
|
|
|
window->render_rect.left = rst_rect.x;
|
|
window->render_rect.top = rst_rect.y;
|
|
window->render_rect.right = rst_rect.x + rst_rect.w;
|
|
window->render_rect.bottom = rst_rect.y + rst_rect.h;
|
|
|
|
window->first_present = TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_d3d11_window_dummy_open_shared_handle (GstD3D11Window * window,
|
|
GstD3D11WindowSharedHandleData * data)
|
|
{
|
|
GstD3D11Device *device = window->device;
|
|
ID3D11Device *device_handle;
|
|
HRESULT hr;
|
|
ComPtr < ID3D11Texture2D > texture;
|
|
ComPtr < IDXGIKeyedMutex > keyed_mutex;
|
|
ID3D11RenderTargetView *rtv;
|
|
GstMemory *mem;
|
|
GstD3D11Memory *dmem;
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
gboolean use_keyed_mutex = FALSE;
|
|
|
|
device_handle = gst_d3d11_device_get_device_handle (device);
|
|
|
|
if ((data->texture_misc_flags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE) ==
|
|
D3D11_RESOURCE_MISC_SHARED_NTHANDLE) {
|
|
ComPtr < ID3D11Device1 > device1_handle;
|
|
|
|
hr = device_handle->QueryInterface (IID_PPV_ARGS (&device1_handle));
|
|
if (!gst_d3d11_result (hr, device))
|
|
return FALSE;
|
|
|
|
hr = device1_handle->OpenSharedResource1 (data->shared_handle,
|
|
IID_PPV_ARGS (&texture));
|
|
} else {
|
|
hr = device_handle->OpenSharedResource (data->shared_handle,
|
|
IID_PPV_ARGS (&texture));
|
|
}
|
|
|
|
if (!gst_d3d11_result (hr, device))
|
|
return FALSE;
|
|
|
|
texture->GetDesc (&desc);
|
|
use_keyed_mutex = (desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX) ==
|
|
D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
|
|
|
|
if (use_keyed_mutex) {
|
|
hr = texture->QueryInterface (IID_PPV_ARGS (&keyed_mutex));
|
|
if (!gst_d3d11_result (hr, device)) {
|
|
GST_ERROR_OBJECT (window, "Keyed mutex is unavailable");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
mem = gst_d3d11_allocator_alloc_wrapped (nullptr,
|
|
device, texture.Get (), desc.Width * desc.Height * 4, nullptr, nullptr);
|
|
if (!mem) {
|
|
GST_ERROR_OBJECT (window, "Couldn't allocate memory");
|
|
return FALSE;
|
|
}
|
|
|
|
dmem = GST_D3D11_MEMORY_CAST (mem);
|
|
rtv = gst_d3d11_memory_get_render_target_view (dmem, 0);
|
|
if (!rtv) {
|
|
GST_ERROR_OBJECT (window, "Render target view is unavailable");
|
|
gst_memory_unref (mem);
|
|
return FALSE;
|
|
}
|
|
|
|
if (keyed_mutex) {
|
|
hr = keyed_mutex->AcquireSync (data->acquire_key, INFINITE);
|
|
if (!gst_d3d11_result (hr, device)) {
|
|
GST_ERROR_OBJECT (window, "Couldn't acquire sync");
|
|
gst_memory_unref (mem);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Everything is prepared now */
|
|
gst_d3d11_window_dummy_on_resize (window, desc.Width, desc.Height);
|
|
|
|
/* Move owned resources */
|
|
data->render_target = gst_buffer_new ();
|
|
gst_buffer_append_memory (data->render_target, mem);
|
|
if (keyed_mutex)
|
|
data->keyed_mutex = keyed_mutex.Detach ();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_d3d11_window_dummy_release_shared_handle (GstD3D11Window * window,
|
|
GstD3D11WindowSharedHandleData * data)
|
|
{
|
|
GstD3D11WindowDummy *self = GST_D3D11_WINDOW_DUMMY (window);
|
|
GstD3D11Device *device = window->device;
|
|
HRESULT hr;
|
|
|
|
/* TODO: cache owned resource for the later reuse? */
|
|
if (data->keyed_mutex) {
|
|
hr = data->keyed_mutex->ReleaseSync (data->release_key);
|
|
gst_d3d11_result (hr, device);
|
|
|
|
GST_D3D11_CLEAR_COM (data->keyed_mutex);
|
|
} else {
|
|
/* If keyed mutex is not used, let's handle sync manually by using
|
|
* fence. Issued GPU commands might not be finished yet */
|
|
|
|
if (!self->fence)
|
|
self->fence = gst_d3d11_device_create_fence (device);
|
|
|
|
if (!self->fence) {
|
|
GST_ERROR_OBJECT (self, "Couldn't Create event query");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_d3d11_fence_signal (self->fence) ||
|
|
!gst_d3d11_fence_wait (self->fence)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't sync GPU operation");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
gst_clear_buffer (&data->render_target);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GstD3D11Window *
|
|
gst_d3d11_window_dummy_new (GstD3D11Device * device)
|
|
{
|
|
GstD3D11Window *window;
|
|
|
|
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
|
|
|
|
window = (GstD3D11Window *)
|
|
g_object_new (GST_TYPE_D3D11_WINDOW_DUMMY, "d3d11device", device, NULL);
|
|
|
|
window->initialized = TRUE;
|
|
gst_object_ref_sink (window);
|
|
|
|
return window;
|
|
}
|