| /* GStreamer | |
| * Copyright (C) 2022 Matthew Waters <matthew@centricular.com> | |
| * Copyright (C) 2023 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 "gstqsg6d3d11node.h" | |
| #include <wrl.h> | |
| GST_DEBUG_CATEGORY_EXTERN (gst_qt6_d3d11_debug); | |
| #define GST_CAT_DEFAULT gst_qt6_d3d11_debug | |
| /* *INDENT-OFF* */ | |
| using namespace Microsoft::WRL; | |
| /* *INDENT-ON* */ | |
| GstQSG6D3D11Node::GstQSG6D3D11Node (QQuickItem * item, GstD3D11Device * device) | |
| { | |
| window_ = item->window (); | |
| qt_device_ = (GstD3D11Device *) gst_object_ref (device); | |
| frame_.buffer = nullptr; | |
| gst_video_info_init (&render_info_); | |
| gst_video_info_init (&info_); | |
| setOwnsTexture (true); | |
| resize (8, 8); | |
| mapTexture (); | |
| } | |
| GstQSG6D3D11Node::~GstQSG6D3D11Node () | |
| { | |
| GST_DEBUG ("Destroying %p", this); | |
| unmapTexture (); | |
| gst_clear_buffer (&sharable_); | |
| gst_clear_buffer (&backbuffer_); | |
| gst_clear_buffer (&buffer_); | |
| gst_clear_caps (&caps_); | |
| if (pool_) { | |
| gst_buffer_pool_set_active (pool_, FALSE); | |
| gst_object_unref (pool_); | |
| } | |
| gst_clear_object (&conv_); | |
| gst_clear_object (&device_); | |
| gst_clear_object (&qt_device_); | |
| } | |
| QSGTexture * | |
| GstQSG6D3D11Node::texture () const | |
| { | |
| return QSGSimpleTextureNode::texture (); | |
| } | |
| void | |
| GstQSG6D3D11Node::mapTexture () | |
| { | |
| if (backbuffer_) { | |
| gst_video_frame_map (&frame_, &render_info_, backbuffer_, | |
| (GstMapFlags) (GST_MAP_READ | GST_MAP_D3D11)); | |
| } | |
| } | |
| void | |
| GstQSG6D3D11Node::unmapTexture () | |
| { | |
| if (frame_.buffer) | |
| gst_video_frame_unmap (&frame_); | |
| frame_.buffer = nullptr; | |
| } | |
| void | |
| GstQSG6D3D11Node::resize (guint width, guint height) | |
| { | |
| GstStructure *config; | |
| GstD3D11AllocationParams *params; | |
| GstCaps *caps; | |
| GstD3D11Memory *dmem; | |
| ID3D11Texture2D *tex; | |
| if (pool_ && width == render_info_.width && height == render_info_.height) | |
| return; | |
| gst_clear_buffer (&sharable_); | |
| gst_clear_buffer (&backbuffer_); | |
| if (pool_) { | |
| gst_buffer_pool_set_active (pool_, FALSE); | |
| gst_clear_object (&pool_); | |
| } | |
| pool_ = gst_d3d11_buffer_pool_new (qt_device_); | |
| gst_video_info_set_format (&render_info_, GST_VIDEO_FORMAT_RGBA, | |
| width, height); | |
| render_info_.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709; | |
| caps = gst_video_info_to_caps (&render_info_); | |
| params = gst_d3d11_allocation_params_new (qt_device_, &render_info_, | |
| GST_D3D11_ALLOCATION_FLAG_DEFAULT, | |
| D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET, | |
| D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX); | |
| config = gst_buffer_pool_get_config (pool_); | |
| gst_buffer_pool_config_set_d3d11_allocation_params (config, params); | |
| gst_d3d11_allocation_params_free (params); | |
| gst_buffer_pool_config_set_params (config, caps, render_info_.size, 0, 0); | |
| gst_caps_unref (caps); | |
| gst_buffer_pool_set_config (pool_, config); | |
| gst_buffer_pool_set_active (pool_, TRUE); | |
| gst_buffer_pool_acquire_buffer (pool_, &backbuffer_, nullptr); | |
| dmem = (GstD3D11Memory *) gst_buffer_peek_memory (backbuffer_, 0); | |
| tex = (ID3D11Texture2D *) gst_d3d11_memory_get_resource_handle (dmem); | |
| auto qt_texture = QNativeInterface::QSGD3D11Texture::fromNative (tex, window_, | |
| QSize (render_info_.width, render_info_.height), | |
| QQuickWindow::TextureHasAlphaChannel); | |
| setTexture (qt_texture); | |
| } | |
| /* only called from QT rendering thread */ | |
| void | |
| GstQSG6D3D11Node::SetCaps (GstCaps * caps) | |
| { | |
| if (caps_ && caps && gst_caps_is_equal (caps_, caps)) | |
| return; | |
| gst_clear_object (&conv_); | |
| gst_clear_buffer (&sharable_); | |
| gst_clear_buffer (&buffer_); | |
| if (caps) | |
| gst_video_info_from_caps (&info_, caps); | |
| else | |
| gst_video_info_init (&info_); | |
| gst_caps_replace (&caps_, caps); | |
| } | |
| /* only called from QT rendering thread */ | |
| void | |
| GstQSG6D3D11Node::SetBuffer (GstBuffer * buffer) | |
| { | |
| gboolean buffer_changed; | |
| buffer_changed = gst_buffer_replace (&buffer_, buffer); | |
| unmapTexture (); | |
| if (buffer_ && buffer_changed) { | |
| GstD3D11Memory *dmem; | |
| resize (info_.width, info_.height); | |
| dmem = (GstD3D11Memory *) gst_buffer_peek_memory (buffer, 0); | |
| if (dmem->device != device_) { | |
| gst_clear_object (&conv_); | |
| gst_clear_buffer (&sharable_); | |
| gst_clear_object (&device_); | |
| device_ = (GstD3D11Device *) gst_object_ref (dmem->device); | |
| } | |
| gst_d3d11_device_lock (device_); | |
| if (!conv_) | |
| conv_ = gst_d3d11_converter_new (device_, &info_, &render_info_, nullptr); | |
| if (!sharable_) { | |
| GstMemory *mem_wrapped; | |
| ID3D11Resource *resource; | |
| ComPtr < IDXGIResource > dxgi_resource; | |
| ComPtr < ID3D11Texture2D > shared_texture; | |
| ID3D11Device *device_handle; | |
| HANDLE handle; | |
| device_handle = gst_d3d11_device_get_device_handle (dmem->device); | |
| dmem = (GstD3D11Memory *) gst_buffer_peek_memory (backbuffer_, 0); | |
| resource = gst_d3d11_memory_get_resource_handle (dmem); | |
| resource->QueryInterface (IID_PPV_ARGS (&dxgi_resource)); | |
| dxgi_resource->GetSharedHandle (&handle); | |
| device_handle->OpenSharedResource (handle, | |
| IID_PPV_ARGS (&shared_texture)); | |
| mem_wrapped = gst_d3d11_allocator_alloc_wrapped (nullptr, device_, | |
| shared_texture.Get (), render_info_.size, nullptr, nullptr); | |
| sharable_ = gst_buffer_new (); | |
| gst_buffer_append_memory (sharable_, mem_wrapped); | |
| } | |
| if (GST_VIDEO_INFO_FORMAT (&info_) == GST_VIDEO_FORMAT_RGBA) { | |
| GstMapInfo src_info, dst_info; | |
| GstMemory *src_mem, *dst_mem; | |
| D3D11_TEXTURE2D_DESC src_desc, dst_desc; | |
| D3D11_BOX src_box; | |
| ID3D11Texture2D *src_tex, *dst_tex; | |
| ID3D11DeviceContext *context = | |
| gst_d3d11_device_get_device_context_handle (device_); | |
| src_mem = gst_buffer_peek_memory (buffer_, 0); | |
| dst_mem = gst_buffer_peek_memory (sharable_, 0); | |
| gst_memory_map (src_mem, &src_info, | |
| (GstMapFlags) (GST_MAP_READ | GST_MAP_D3D11)); | |
| gst_memory_map (dst_mem, | |
| &dst_info, (GstMapFlags) (GST_MAP_WRITE | GST_MAP_D3D11)); | |
| src_tex = (ID3D11Texture2D *) src_info.data; | |
| dst_tex = (ID3D11Texture2D *) dst_info.data; | |
| src_tex->GetDesc (&src_desc); | |
| dst_tex->GetDesc (&dst_desc); | |
| src_box.left = 0; | |
| src_box.top = 0; | |
| src_box.front = 0; | |
| src_box.back = 1; | |
| src_box.right = MIN (src_desc.Width, dst_desc.Width); | |
| src_box.bottom = MIN (src_desc.Height, dst_desc.Height); | |
| context->CopySubresourceRegion (dst_tex, 0, | |
| 0, 0, 0, src_tex, 0, &src_box); | |
| gst_memory_unmap (src_mem, &src_info); | |
| gst_memory_unmap (dst_mem, &dst_info); | |
| } else { | |
| gst_d3d11_converter_convert_buffer_unlocked (conv_, buffer_, sharable_); | |
| } | |
| gst_d3d11_device_unlock (device_); | |
| markDirty (QSGNode::DirtyMaterial); | |
| } | |
| mapTexture (); | |
| } |