mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-22 01:31:03 +00:00
qt6d3d11: Add Direct3D11 Qt6 QML sink
Adding Direct3D11 backend Qt6 QML videosink element, qml6d3d11sink. Implementation details are similar to the qt6 plugin in -good but there are a few notable differences. * qml6d3d11sink accepts all GstD3D11 supported video formats (e.g., NV12). * Scene graph (owned by qml6d3d11sink) will hold dedicated and sharable RGBA texture which belongs to Qt6's Direct3D11 device, instead of sharing GStreamer's own texture with Qt6. * All rendering operations will be done by using GStreamer's Direct3D11 device. Specifically, upstream texture will be copied (in case of RGBA) or converted to the above mentioned Qt6's sharable texture. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3707>
This commit is contained in:
parent
9825cd8951
commit
7b4e1fd602
21 changed files with 2338 additions and 0 deletions
|
@ -1,3 +1,4 @@
|
|||
subprojects/gst-plugins-bad/ext/qt6d3d11
|
||||
subprojects/gst-plugins-bad/gst-libs/gst/cuda
|
||||
subprojects/gst-plugins-bad/gst-libs/gst/d3d11
|
||||
subprojects/gst-plugins-bad/gst-libs/gst/va
|
||||
|
|
|
@ -48,6 +48,7 @@ subdir('openmpt')
|
|||
subdir('openni2')
|
||||
subdir('opus')
|
||||
subdir('qroverlay')
|
||||
subdir('qt6d3d11')
|
||||
subdir('resindvd')
|
||||
subdir('rsvg')
|
||||
subdir('rtmp')
|
||||
|
|
664
subprojects/gst-plugins-bad/ext/qt6d3d11/gstqml6d3d11sink.cpp
Normal file
664
subprojects/gst-plugins-bad/ext/qt6d3d11/gstqml6d3d11sink.cpp
Normal file
|
@ -0,0 +1,664 @@
|
|||
/* GStreamer
|
||||
* 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 <gst/d3d11/gstd3d11.h>
|
||||
#include <gst/d3d11/gstd3d11-private.h>
|
||||
#include "gstqml6d3d11sink.h"
|
||||
#include "gstqt6d3d11videoitem.h"
|
||||
#include <mutex>
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_qml6_d3d11_sink_debug);
|
||||
#define GST_CAT_DEFAULT gst_qml6_d3d11_sink_debug
|
||||
|
||||
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
|
||||
(GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, GST_D3D11_SINK_FORMATS) "; "
|
||||
GST_VIDEO_CAPS_MAKE (GST_D3D11_SINK_FORMATS)));
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_WIDGET,
|
||||
PROP_FORCE_ASPECT_RATIO,
|
||||
};
|
||||
|
||||
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
|
||||
|
||||
struct GstQml6D3D11SinkPrivate
|
||||
{
|
||||
QSharedPointer < GstQt6D3D11VideoItemProxy > widget;
|
||||
|
||||
std::recursive_mutex lock;
|
||||
|
||||
GstVideoInfo info;
|
||||
|
||||
GstD3D11Device *device = nullptr;
|
||||
gint64 adapter_luid = 0;
|
||||
GstBuffer *prepared_buffer = nullptr;
|
||||
GstBufferPool *pool = nullptr;
|
||||
|
||||
gboolean force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
|
||||
};
|
||||
|
||||
struct _GstQml6D3D11Sink
|
||||
{
|
||||
GstVideoSink parent;
|
||||
|
||||
GstQml6D3D11SinkPrivate *priv;
|
||||
};
|
||||
|
||||
static void
|
||||
gst_qml6_d3d11_sink_navigation_init (GstNavigationInterface * iface);
|
||||
static void gst_qml6_d3d11_sink_dispose (GObject * object);
|
||||
static void gst_qml6_d3d11_sink_finalize (GObject * object);
|
||||
static void gst_qml6_d3d11_sink_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec);
|
||||
static void gst_qml6_d3d11_sink_get_property (GObject * object,
|
||||
guint prop_id, GValue * value, GParamSpec * pspec);
|
||||
static void gst_qml6_d3d11_sink_set_context (GstElement * element,
|
||||
GstContext * context);
|
||||
static gboolean gst_qml6_d3d11_sink_set_caps (GstBaseSink * sink,
|
||||
GstCaps * caps);
|
||||
static gboolean gst_qml6_d3d11_sink_start (GstBaseSink * sink);
|
||||
static gboolean gst_qml6_d3d11_sink_stop (GstBaseSink * sink);
|
||||
static gboolean gst_qml6_d3d11_sink_propose_allocation (GstBaseSink * sink,
|
||||
GstQuery * query);
|
||||
static gboolean gst_qml6_d3d11_sink_query (GstBaseSink * sink,
|
||||
GstQuery * query);
|
||||
static GstFlowReturn gst_qml6_d3d11_sink_prepare (GstBaseSink * sink,
|
||||
GstBuffer * buf);
|
||||
static GstFlowReturn gst_qml6_d3d11_sink_show_frame (GstVideoSink * sink,
|
||||
GstBuffer * buf);
|
||||
|
||||
#define gst_qml6_d3d11_sink_parent_class parent_class
|
||||
G_DEFINE_TYPE_WITH_CODE (GstQml6D3D11Sink, gst_qml6_d3d11_sink,
|
||||
GST_TYPE_VIDEO_SINK, GST_DEBUG_CATEGORY_INIT (gst_qml6_d3d11_sink_debug,
|
||||
"qml6d3d11sink", 0, "qml6d3d11sink");
|
||||
G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
|
||||
gst_qml6_d3d11_sink_navigation_init);
|
||||
gst_qt6_d3d11_video_item_init_once ());
|
||||
|
||||
static void
|
||||
gst_qml6_d3d11_sink_class_init (GstQml6D3D11SinkClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||
GstBaseSinkClass *sink_class = GST_BASE_SINK_CLASS (klass);
|
||||
GstVideoSinkClass *videosink_class = GST_VIDEO_SINK_CLASS (klass);
|
||||
|
||||
object_class->dispose = gst_qml6_d3d11_sink_dispose;
|
||||
object_class->finalize = gst_qml6_d3d11_sink_finalize;
|
||||
object_class->set_property = gst_qml6_d3d11_sink_set_property;
|
||||
object_class->get_property = gst_qml6_d3d11_sink_get_property;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_WIDGET,
|
||||
g_param_spec_pointer ("widget", "QQuickItem",
|
||||
"The QQuickItem to place in the object hierarchy",
|
||||
(GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_FORCE_ASPECT_RATIO,
|
||||
g_param_spec_boolean ("force-aspect-ratio",
|
||||
"Force aspect ratio",
|
||||
"When enabled, scaling will respect original aspect ratio",
|
||||
DEFAULT_FORCE_ASPECT_RATIO,
|
||||
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
gst_element_class_set_static_metadata (element_class,
|
||||
"Qt6 Direct3D11 Video Sink", "Sink/Video",
|
||||
"A video sink that renders to a QQuickItem for Qt6 using Direct3D11",
|
||||
"Seungha Yang <seungha@centricular.com>");
|
||||
|
||||
gst_element_class_add_static_pad_template (element_class, &sink_template);
|
||||
|
||||
element_class->set_context =
|
||||
GST_DEBUG_FUNCPTR (gst_qml6_d3d11_sink_set_context);
|
||||
|
||||
sink_class->set_caps = GST_DEBUG_FUNCPTR (gst_qml6_d3d11_sink_set_caps);
|
||||
sink_class->start = GST_DEBUG_FUNCPTR (gst_qml6_d3d11_sink_start);
|
||||
sink_class->stop = GST_DEBUG_FUNCPTR (gst_qml6_d3d11_sink_stop);
|
||||
sink_class->propose_allocation =
|
||||
GST_DEBUG_FUNCPTR (gst_qml6_d3d11_sink_propose_allocation);
|
||||
sink_class->query = GST_DEBUG_FUNCPTR (gst_qml6_d3d11_sink_query);
|
||||
sink_class->prepare = GST_DEBUG_FUNCPTR (gst_qml6_d3d11_sink_prepare);
|
||||
|
||||
videosink_class->show_frame =
|
||||
GST_DEBUG_FUNCPTR (gst_qml6_d3d11_sink_show_frame);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qml6_d3d11_sink_init (GstQml6D3D11Sink * self)
|
||||
{
|
||||
self->priv = new GstQml6D3D11SinkPrivate ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qml6_d3d11_sink_dispose (GObject * object)
|
||||
{
|
||||
GstQml6D3D11Sink *self = GST_QML6_D3D11_SINK (object);
|
||||
GstQml6D3D11SinkPrivate *priv = self->priv;
|
||||
|
||||
gst_clear_object (&priv->device);
|
||||
self->priv->widget.clear ();
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qml6_d3d11_sink_finalize (GObject * object)
|
||||
{
|
||||
GstQml6D3D11Sink *self = GST_QML6_D3D11_SINK (object);
|
||||
|
||||
delete self->priv;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qml6_d3d11_sink_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstQml6D3D11Sink *self = GST_QML6_D3D11_SINK (object);
|
||||
GstQml6D3D11SinkPrivate *priv = self->priv;
|
||||
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_FORCE_ASPECT_RATIO:
|
||||
priv->force_aspect_ratio = g_value_get_boolean (value);
|
||||
if (priv->widget)
|
||||
priv->widget->SetForceAspectRatio (priv->force_aspect_ratio);
|
||||
break;
|
||||
case PROP_WIDGET:
|
||||
{
|
||||
GstQt6D3D11VideoItem *item =
|
||||
(GstQt6D3D11VideoItem *) g_value_get_pointer (value);
|
||||
if (item) {
|
||||
priv->widget = item->Proxy ();
|
||||
if (priv->widget) {
|
||||
priv->widget->SetSink (GST_ELEMENT_CAST (self));
|
||||
priv->widget->SetForceAspectRatio (priv->force_aspect_ratio);
|
||||
}
|
||||
} else {
|
||||
priv->widget.clear ();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qml6_d3d11_sink_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstQml6D3D11Sink *self = GST_QML6_D3D11_SINK (object);
|
||||
GstQml6D3D11SinkPrivate *priv = self->priv;
|
||||
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_FORCE_ASPECT_RATIO:
|
||||
g_value_set_boolean (value, priv->force_aspect_ratio);
|
||||
break;
|
||||
case PROP_WIDGET:
|
||||
if (priv->widget) {
|
||||
g_value_set_pointer (value, priv->widget->Item ());
|
||||
} else {
|
||||
g_value_set_pointer (value, nullptr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qml6_d3d11_sink_set_context (GstElement * element, GstContext * context)
|
||||
{
|
||||
GstQml6D3D11Sink *self = GST_QML6_D3D11_SINK (element);
|
||||
GstQml6D3D11SinkPrivate *priv = self->priv;
|
||||
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
||||
|
||||
if (priv->widget) {
|
||||
gint64 adapter_luid = priv->widget->AdapterLuid ();
|
||||
gst_d3d11_handle_set_context_for_adapter_luid (element,
|
||||
context, adapter_luid, &priv->device);
|
||||
}
|
||||
|
||||
GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_qml6_d3d11_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
|
||||
{
|
||||
GstQml6D3D11Sink *self = GST_QML6_D3D11_SINK (sink);
|
||||
GstQml6D3D11SinkPrivate *priv = self->priv;
|
||||
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
||||
GstBufferPool *pool;
|
||||
GstStructure *config;
|
||||
GstD3D11AllocationParams *params;
|
||||
|
||||
if (!priv->widget) {
|
||||
GST_ERROR_OBJECT (self, "Widget is not configured");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_video_info_from_caps (&priv->info, caps)) {
|
||||
GST_ERROR_OBJECT (self, "Invalid caps");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (priv->pool) {
|
||||
gst_buffer_pool_set_active (priv->pool, FALSE);
|
||||
gst_clear_object (&priv->pool);
|
||||
}
|
||||
|
||||
priv->widget->SetCaps (caps);
|
||||
|
||||
pool = gst_d3d11_buffer_pool_new (priv->device);
|
||||
config = gst_buffer_pool_get_config (pool);
|
||||
params = gst_d3d11_allocation_params_new (priv->device, &priv->info,
|
||||
GST_D3D11_ALLOCATION_FLAG_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0);
|
||||
gst_buffer_pool_config_set_d3d11_allocation_params (config, params);
|
||||
gst_d3d11_allocation_params_free (params);
|
||||
|
||||
gst_buffer_pool_config_set_params (config, caps, priv->info.size, 0, 0);
|
||||
|
||||
if (!gst_buffer_pool_set_config (pool, config)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't set pool config");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!gst_buffer_pool_set_active (pool, TRUE)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't set active");
|
||||
goto error;
|
||||
}
|
||||
|
||||
priv->pool = pool;
|
||||
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
gst_clear_object (&pool);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_qml6_d3d11_sink_start (GstBaseSink * sink)
|
||||
{
|
||||
GstQml6D3D11Sink *self = GST_QML6_D3D11_SINK (sink);
|
||||
GstQml6D3D11SinkPrivate *priv = self->priv;
|
||||
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
||||
gint64 adapter_luid;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Start");
|
||||
|
||||
if (!priv->widget) {
|
||||
GST_ERROR_OBJECT (self, "Widget is not configured");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
adapter_luid = priv->widget->AdapterLuid ();
|
||||
if (!gst_d3d11_ensure_element_data_for_adapter_luid (GST_ELEMENT_CAST (self),
|
||||
adapter_luid, &priv->device)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't create d3d11 device for luid %"
|
||||
G_GINT64_FORMAT, adapter_luid);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
priv->adapter_luid = adapter_luid;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_qml6_d3d11_sink_stop (GstBaseSink * sink)
|
||||
{
|
||||
GstQml6D3D11Sink *self = GST_QML6_D3D11_SINK (sink);
|
||||
GstQml6D3D11SinkPrivate *priv = self->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Stop");
|
||||
|
||||
if (priv->pool) {
|
||||
gst_buffer_pool_set_active (priv->pool, FALSE);
|
||||
gst_clear_object (&priv->pool);
|
||||
}
|
||||
|
||||
gst_clear_buffer (&priv->prepared_buffer);
|
||||
gst_clear_object (&priv->device);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_qml6_d3d11_sink_propose_allocation (GstBaseSink * sink, GstQuery * query)
|
||||
{
|
||||
GstQml6D3D11Sink *self = GST_QML6_D3D11_SINK (sink);
|
||||
GstQml6D3D11SinkPrivate *priv = self->priv;
|
||||
GstCaps *caps;
|
||||
GstBufferPool *pool = nullptr;
|
||||
GstVideoInfo info;
|
||||
guint size;
|
||||
gboolean need_pool;
|
||||
|
||||
if (!priv->device) {
|
||||
GST_ERROR_OBJECT (self, "No d3d11 device configured");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gst_query_parse_allocation (query, &caps, &need_pool);
|
||||
if (!caps) {
|
||||
GST_WARNING_OBJECT (self, "No caps specified");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_video_info_from_caps (&info, caps)) {
|
||||
GST_WARNING_OBJECT (self, "Invalid caps");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* the normal size of a frame */
|
||||
size = info.size;
|
||||
|
||||
if (need_pool) {
|
||||
GstCapsFeatures *features;
|
||||
GstStructure *config;
|
||||
gboolean is_d3d11 = FALSE;
|
||||
|
||||
features = gst_caps_get_features (caps, 0);
|
||||
if (features
|
||||
&& gst_caps_features_contains (features,
|
||||
GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
|
||||
GST_DEBUG_OBJECT (self, "upstream support d3d11 memory");
|
||||
pool = gst_d3d11_buffer_pool_new (priv->device);
|
||||
is_d3d11 = TRUE;
|
||||
} else {
|
||||
pool = gst_video_buffer_pool_new ();
|
||||
}
|
||||
|
||||
config = gst_buffer_pool_get_config (pool);
|
||||
gst_buffer_pool_config_add_option (config,
|
||||
GST_BUFFER_POOL_OPTION_VIDEO_META);
|
||||
if (!is_d3d11) {
|
||||
gst_buffer_pool_config_add_option (config,
|
||||
GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
|
||||
}
|
||||
|
||||
size = GST_VIDEO_INFO_SIZE (&info);
|
||||
if (is_d3d11) {
|
||||
GstD3D11AllocationParams *d3d11_params =
|
||||
gst_d3d11_allocation_params_new (priv->device, &info,
|
||||
GST_D3D11_ALLOCATION_FLAG_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0);
|
||||
|
||||
gst_buffer_pool_config_set_d3d11_allocation_params (config, d3d11_params);
|
||||
gst_d3d11_allocation_params_free (d3d11_params);
|
||||
}
|
||||
|
||||
gst_buffer_pool_config_set_params (config, caps, (guint) size, 2, 0);
|
||||
|
||||
if (!gst_buffer_pool_set_config (pool, config)) {
|
||||
GST_ERROR_OBJECT (pool, "Couldn't set config");
|
||||
gst_object_unref (pool);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* d3d11 buffer pool will update buffer size based on allocated texture,
|
||||
* get size from config again */
|
||||
config = gst_buffer_pool_get_config (pool);
|
||||
gst_buffer_pool_config_get_params (config, nullptr, &size, nullptr,
|
||||
nullptr);
|
||||
gst_structure_free (config);
|
||||
}
|
||||
|
||||
gst_query_add_allocation_pool (query, pool, size, 2, 0);
|
||||
if (pool)
|
||||
gst_object_unref (pool);
|
||||
|
||||
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_qml6_d3d11_sink_query (GstBaseSink * sink, GstQuery * query)
|
||||
{
|
||||
GstQml6D3D11Sink *self = GST_QML6_D3D11_SINK (sink);
|
||||
GstQml6D3D11SinkPrivate *priv = self->priv;
|
||||
|
||||
switch (GST_QUERY_TYPE (query)) {
|
||||
case GST_QUERY_CONTEXT:
|
||||
if (gst_d3d11_handle_context_query (GST_ELEMENT (self), query,
|
||||
priv->device)) {
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return GST_BASE_SINK_CLASS (parent_class)->query (sink, query);
|
||||
}
|
||||
|
||||
static GstBuffer *
|
||||
gst_qml6_d3d11_sink_do_system_copy (GstQml6D3D11Sink * self, GstBuffer * in_buf)
|
||||
{
|
||||
GstQml6D3D11SinkPrivate *priv = self->priv;
|
||||
GstBuffer *out_buf;
|
||||
GstVideoFrame in_frame, out_frame;
|
||||
|
||||
if (!gst_video_frame_map (&in_frame, &priv->info, in_buf, GST_MAP_READ)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't map input buffer");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (gst_buffer_pool_acquire_buffer (priv->pool, &out_buf, nullptr)
|
||||
!= GST_FLOW_OK) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't acquire buffer");
|
||||
gst_video_frame_unmap (&in_frame);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!gst_video_frame_map (&out_frame, &priv->info, out_buf, GST_MAP_WRITE)) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't map output buffer");
|
||||
gst_video_frame_unmap (&in_frame);
|
||||
gst_buffer_unref (out_buf);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gst_video_frame_copy (&out_frame, &in_frame);
|
||||
gst_video_frame_unmap (&in_frame);
|
||||
gst_video_frame_unmap (&out_frame);
|
||||
|
||||
/* Do upload staging to default texture */
|
||||
for (guint i = 0; i < gst_buffer_n_memory (out_buf); i++) {
|
||||
GstMemory *mem = gst_buffer_peek_memory (out_buf, i);
|
||||
GstMapInfo info;
|
||||
|
||||
gst_memory_map (mem, &info, (GstMapFlags) (GST_MAP_READ | GST_MAP_D3D11));
|
||||
gst_memory_unmap (mem, &info);
|
||||
}
|
||||
|
||||
return out_buf;
|
||||
}
|
||||
|
||||
static GstBuffer *
|
||||
gst_qml6_d3d11_sink_do_gpu_copy (GstQml6D3D11Sink * self, GstBuffer * in_buf)
|
||||
{
|
||||
GstQml6D3D11SinkPrivate *priv = self->priv;
|
||||
GstBuffer *out_buf;
|
||||
ID3D11DeviceContext *context;
|
||||
|
||||
if (gst_buffer_pool_acquire_buffer (priv->pool, &out_buf, nullptr)
|
||||
!= GST_FLOW_OK) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't acquire buffer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GstD3D11DeviceLockGuard lk (priv->device);
|
||||
context = gst_d3d11_device_get_device_context_handle (priv->device);
|
||||
|
||||
for (guint i = 0; i < gst_buffer_n_memory (in_buf); i++) {
|
||||
GstMapInfo src_info, dst_info;
|
||||
GstMemory *src_mem, *dst_mem;
|
||||
D3D11_TEXTURE2D_DESC src_desc, dst_desc;
|
||||
D3D11_BOX src_box;
|
||||
guint subresource_idx;
|
||||
ID3D11Texture2D *src_tex, *dst_tex;
|
||||
|
||||
src_mem = gst_buffer_peek_memory (in_buf, i);
|
||||
dst_mem = gst_buffer_peek_memory (out_buf, i);
|
||||
|
||||
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);
|
||||
|
||||
subresource_idx = GPOINTER_TO_UINT (src_info.user_data[0]);
|
||||
|
||||
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, subresource_idx, &src_box);
|
||||
gst_memory_unmap (src_mem, &src_info);
|
||||
gst_memory_unmap (dst_mem, &dst_info);
|
||||
}
|
||||
|
||||
return out_buf;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_qml6_d3d11_sink_prepare (GstBaseSink * sink, GstBuffer * buf)
|
||||
{
|
||||
GstQml6D3D11Sink *self = GST_QML6_D3D11_SINK (sink);
|
||||
GstQml6D3D11SinkPrivate *priv = self->priv;
|
||||
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
||||
GstBuffer *render_buf = nullptr;
|
||||
GstMemory *mem;
|
||||
GstD3D11Device *other_device = nullptr;
|
||||
|
||||
if (!priv->widget) {
|
||||
GST_ERROR_OBJECT (self, "Widget is not configured");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
gst_clear_buffer (&priv->prepared_buffer);
|
||||
mem = gst_buffer_peek_memory (buf, 0);
|
||||
if (!gst_is_d3d11_memory (mem)) {
|
||||
render_buf = gst_qml6_d3d11_sink_do_system_copy (self, buf);
|
||||
} else {
|
||||
GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (mem);
|
||||
|
||||
other_device = dmem->device;
|
||||
if (dmem->device != priv->device) {
|
||||
gint64 luid;
|
||||
|
||||
/* different device, check if it's the same GPU */
|
||||
g_object_get (dmem->device, "adapter-luid", &luid, nullptr);
|
||||
if (luid != priv->adapter_luid) {
|
||||
/* From other GPU, system copy */
|
||||
render_buf = gst_qml6_d3d11_sink_do_system_copy (self, buf);
|
||||
} else {
|
||||
/* same GPU and sharable, increase ref */
|
||||
render_buf = gst_buffer_ref (buf);
|
||||
}
|
||||
} else {
|
||||
render_buf = gst_buffer_ref (buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (!render_buf) {
|
||||
GST_ERROR_OBJECT (self, "Couldn't prepare output buffer");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
priv->prepared_buffer = render_buf;
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_qml6_d3d11_sink_show_frame (GstVideoSink * sink, GstBuffer * buf)
|
||||
{
|
||||
GstQml6D3D11Sink *self = GST_QML6_D3D11_SINK (sink);
|
||||
GstQml6D3D11SinkPrivate *priv = self->priv;
|
||||
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
||||
|
||||
if (!priv->widget) {
|
||||
GST_ERROR_OBJECT (self, "Widget is not configured");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
if (!priv->prepared_buffer) {
|
||||
GST_ERROR_OBJECT (self, "No prepared sample");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
priv->widget->SetBuffer (priv->prepared_buffer);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qml6_d3d11_sink_navigation_send_event (GstNavigation * navigation,
|
||||
GstEvent * event)
|
||||
{
|
||||
GstQml6D3D11Sink *self = GST_QML6_D3D11_SINK (navigation);
|
||||
|
||||
if (event) {
|
||||
gboolean handled;
|
||||
|
||||
gst_event_ref (event);
|
||||
handled = gst_pad_push_event (GST_VIDEO_SINK_PAD (self), event);
|
||||
if (!handled) {
|
||||
gst_element_post_message (GST_ELEMENT_CAST (self),
|
||||
gst_navigation_message_new_event (GST_OBJECT_CAST (self), event));
|
||||
}
|
||||
|
||||
gst_event_unref (event);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qml6_d3d11_sink_navigation_init (GstNavigationInterface * iface)
|
||||
{
|
||||
iface->send_event_simple = gst_qml6_d3d11_sink_navigation_send_event;
|
||||
}
|
31
subprojects/gst-plugins-bad/ext/qt6d3d11/gstqml6d3d11sink.h
Normal file
31
subprojects/gst-plugins-bad/ext/qt6d3d11/gstqml6d3d11sink.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* GStreamer
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_QML6_D3D11_SINK (gst_qml6_d3d11_sink_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstQml6D3D11Sink, gst_qml6_d3d11_sink,
|
||||
GST, QML6_D3D11_SINK, GstVideoSink);
|
||||
|
||||
G_END_DECLS
|
257
subprojects/gst-plugins-bad/ext/qt6d3d11/gstqsg6d3d11node.cpp
Normal file
257
subprojects/gst-plugins-bad/ext/qt6d3d11/gstqsg6d3d11node.cpp
Normal file
|
@ -0,0 +1,257 @@
|
|||
/* 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 ();
|
||||
}
|
62
subprojects/gst-plugins-bad/ext/qt6d3d11/gstqsg6d3d11node.h
Normal file
62
subprojects/gst-plugins-bad/ext/qt6d3d11/gstqsg6d3d11node.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
#include <gst/d3d11/gstd3d11.h>
|
||||
#include <QtQuick/QQuickItem>
|
||||
#include <QtQuick/QSGTexture>
|
||||
#include <QtQuick/QSGTextureProvider>
|
||||
#include <QtQuick/QSGSimpleTextureNode>
|
||||
|
||||
class GstQSG6D3D11Node : public QSGTextureProvider, public QSGSimpleTextureNode
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GstQSG6D3D11Node (QQuickItem * item, GstD3D11Device * device);
|
||||
~GstQSG6D3D11Node ();
|
||||
|
||||
QSGTexture *texture() const override;
|
||||
|
||||
void SetCaps (GstCaps * caps);
|
||||
void SetBuffer (GstBuffer * buffer);
|
||||
|
||||
private:
|
||||
void resize (guint width, guint height);
|
||||
void mapTexture ();
|
||||
void unmapTexture ();
|
||||
|
||||
private:
|
||||
QQuickWindow *window_;
|
||||
GstD3D11Device *qt_device_;
|
||||
GstD3D11Device *device_ = nullptr;
|
||||
GstBuffer *buffer_ = nullptr;
|
||||
GstCaps *caps_ = nullptr;
|
||||
GstBuffer *sharable_ = nullptr;
|
||||
GstBuffer *backbuffer_ = nullptr;
|
||||
GstVideoFrame frame_;
|
||||
GstVideoInfo info_;
|
||||
GstVideoInfo render_info_;
|
||||
GstBufferPool *pool_ = nullptr;
|
||||
GstD3D11Converter *conv_ = nullptr;
|
||||
};
|
|
@ -0,0 +1,630 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2015 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 <gst/gst.h>
|
||||
#include <gst/d3d11/gstd3d11.h>
|
||||
#include <gst/d3d11/gstd3d11-private.h>
|
||||
#include "gstqt6d3d11videoitem.h"
|
||||
#include "gstqsg6d3d11node.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (gst_qt6_d3d11_debug);
|
||||
#define GST_CAT_DEFAULT gst_qt6_d3d11_debug
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItemProxy::InvalidateRef (void)
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (lock);
|
||||
item = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItemProxy::SetSink (GstElement * sink)
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (lock);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
item->setSink (sink);
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItemProxy::SetCaps (GstCaps * caps)
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (lock);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
item->setCaps (caps);
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItemProxy::SetBuffer (GstBuffer * buffer)
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (lock);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
item->setBuffer (buffer);
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItemProxy::SetForceAspectRatio (bool force)
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (lock);
|
||||
if (!item)
|
||||
return item->setForceAspectRatio (force);
|
||||
}
|
||||
|
||||
gint64
|
||||
GstQt6D3D11VideoItemProxy::AdapterLuid ()
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (lock);
|
||||
if (!item)
|
||||
return 0;
|
||||
|
||||
return item->AdapterLuid ();
|
||||
}
|
||||
|
||||
GstQt6D3D11VideoItem *
|
||||
GstQt6D3D11VideoItemProxy::Item (void)
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (lock);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
GstQt6D3D11VideoItem::GstQt6D3D11VideoItem ()
|
||||
{
|
||||
g_weak_ref_init (&sink_, nullptr);
|
||||
|
||||
connect (this, &QQuickItem::windowChanged, this,
|
||||
&GstQt6D3D11VideoItem::handleWindowChanged);
|
||||
|
||||
proxy_ = QSharedPointer < GstQt6D3D11VideoItemProxy >
|
||||
(new GstQt6D3D11VideoItemProxy (this));
|
||||
|
||||
setFlag (ItemHasContents, true);
|
||||
setAcceptedMouseButtons (Qt::AllButtons);
|
||||
setAcceptHoverEvents (true);
|
||||
setAcceptTouchEvents (true);
|
||||
|
||||
gst_video_info_init (&info_);
|
||||
|
||||
GST_DEBUG ("%p New Qt6 Video Item", this);
|
||||
}
|
||||
|
||||
GstQt6D3D11VideoItem::~GstQt6D3D11VideoItem ()
|
||||
{
|
||||
GST_DEBUG ("%p Destroying Qt6 Video Item", this);
|
||||
|
||||
proxy_->InvalidateRef ();
|
||||
proxy_.clear ();
|
||||
|
||||
gst_clear_buffer (&buffer_);
|
||||
gst_clear_caps (&caps_);
|
||||
|
||||
g_weak_ref_clear (&sink_);
|
||||
}
|
||||
|
||||
QSharedPointer < GstQt6D3D11VideoItemProxy > GstQt6D3D11VideoItem::Proxy (void)
|
||||
{
|
||||
return proxy_;
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::setSink (GstElement * sink)
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (lock_);
|
||||
g_weak_ref_set (&sink_, sink);
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::setCaps (GstCaps * caps)
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (lock_);
|
||||
guint num, den;
|
||||
|
||||
gst_caps_replace (&caps_, caps);
|
||||
gst_video_info_from_caps (&info_, caps);
|
||||
|
||||
gst_video_calculate_display_ratio (&num, &den, info_.width, info_.height,
|
||||
info_.par_n, info_.par_d, 1, 1);
|
||||
|
||||
display_width_ = info_.width;
|
||||
display_height_ = info_.height;
|
||||
|
||||
if (display_width_ % num == 0) {
|
||||
display_height_ = gst_util_uint64_scale_int (display_width_, den, num);
|
||||
} else {
|
||||
display_width_ = gst_util_uint64_scale_int (display_height_, num, den);
|
||||
}
|
||||
|
||||
update_par_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::setBuffer (GstBuffer * buffer)
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (lock_);
|
||||
|
||||
gst_buffer_replace (&buffer_, buffer);
|
||||
|
||||
if (this->window_)
|
||||
QMetaObject::invokeMethod (this->window_, "update", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::setForceAspectRatio (bool force)
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (lock_);
|
||||
|
||||
if (force != force_aspect_ratio_)
|
||||
update_par_ = true;
|
||||
|
||||
force_aspect_ratio_ = force;
|
||||
}
|
||||
|
||||
gint64
|
||||
GstQt6D3D11VideoItem::AdapterLuid (void)
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (lock_);
|
||||
|
||||
if (!qt_device_)
|
||||
return 0;
|
||||
|
||||
return luid_;
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::handleWindowChanged (QQuickWindow * window)
|
||||
{
|
||||
if (window) {
|
||||
connect (window, &QQuickWindow::beforeSynchronizing, this,
|
||||
&GstQt6D3D11VideoItem::onBeforeSynchronizing, Qt::DirectConnection);
|
||||
connect (window, &QQuickWindow::sceneGraphInvalidated, this,
|
||||
&GstQt6D3D11VideoItem::onSceneGraphInvalidated, Qt::DirectConnection);
|
||||
} else {
|
||||
gst_clear_object (&qt_device_);
|
||||
window_ = nullptr;
|
||||
}
|
||||
node_ = nullptr;
|
||||
}
|
||||
|
||||
QSGNode *
|
||||
GstQt6D3D11VideoItem::updatePaintNode (QSGNode * old_node,
|
||||
UpdatePaintNodeData * data)
|
||||
{
|
||||
if (!qt_device_)
|
||||
return old_node;
|
||||
|
||||
GstQSG6D3D11Node *new_node = static_cast < GstQSG6D3D11Node * >(old_node);
|
||||
GstVideoRectangle src, dst, result;
|
||||
|
||||
std::lock_guard < std::mutex > lk (lock_);
|
||||
|
||||
GST_TRACE ("%p updatePaintNode", this);
|
||||
|
||||
if (!new_node)
|
||||
new_node = new GstQSG6D3D11Node (this, qt_device_);
|
||||
|
||||
if (force_aspect_ratio_ && caps_) {
|
||||
src.w = display_width_;
|
||||
src.h = display_height_;
|
||||
|
||||
dst.x = boundingRect ().x ();
|
||||
dst.y = boundingRect ().y ();
|
||||
dst.w = boundingRect ().width ();
|
||||
dst.h = boundingRect ().height ();
|
||||
|
||||
gst_video_sink_center_rect (src, dst, &result, TRUE);
|
||||
} else {
|
||||
result.x = boundingRect ().x ();
|
||||
result.y = boundingRect ().y ();
|
||||
result.w = boundingRect ().width ();
|
||||
result.h = boundingRect ().height ();
|
||||
}
|
||||
|
||||
new_node->setRect (QRectF (result.x, result.y, result.w, result.h));
|
||||
node_ = new_node;
|
||||
if (caps_ || !force_aspect_ratio_)
|
||||
update_par_ = false;
|
||||
else
|
||||
update_par_ = true;
|
||||
|
||||
return new_node;
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::onBeforeSynchronizing (void)
|
||||
{
|
||||
QSGRendererInterface *iface;
|
||||
QQuickWindow *window;
|
||||
ID3D11Device *device;
|
||||
std::unique_lock < std::mutex > lk (lock_);
|
||||
|
||||
if (window_)
|
||||
return;
|
||||
|
||||
GST_DEBUG ("%p Scene graph initialized", this);
|
||||
|
||||
window = this->window ();
|
||||
|
||||
if (!window) {
|
||||
GST_WARNING ("Initialized scene graph has no associated window");
|
||||
return;
|
||||
}
|
||||
|
||||
iface = window->rendererInterface ();
|
||||
if (!iface) {
|
||||
GST_WARNING ("Couldn't get renderer interface");
|
||||
return;
|
||||
}
|
||||
|
||||
if (iface->graphicsApi () != QSGRendererInterface::Direct3D11) {
|
||||
GST_WARNING ("Not a d3d11 api");
|
||||
return;
|
||||
}
|
||||
|
||||
device = reinterpret_cast < ID3D11Device * >(iface->getResource (window,
|
||||
QSGRendererInterface::DeviceResource));
|
||||
if (!device) {
|
||||
GST_WARNING ("Couldn't get d3d11 device");
|
||||
return;
|
||||
}
|
||||
|
||||
qt_device_ = gst_d3d11_device_new_wrapped (device);
|
||||
g_object_get (qt_device_, "adapter-luid", &luid_, nullptr);
|
||||
window_ = window;
|
||||
|
||||
lk.unlock ();
|
||||
|
||||
connect (window, &QQuickWindow::beforeRendering, this,
|
||||
&GstQt6D3D11VideoItem::onBeforeRendering, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::onSceneGraphInvalidated (void)
|
||||
{
|
||||
std::unique_lock < std::mutex > lk (lock_);
|
||||
|
||||
GST_DEBUG ("%p Scene graph invalidated", this);
|
||||
|
||||
gst_clear_object (&qt_device_);
|
||||
window_ = nullptr;
|
||||
node_ = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::onBeforeRendering (void)
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (lock_);
|
||||
|
||||
if (update_par_ && caps_ && node_) {
|
||||
GstVideoRectangle src, dst, result;
|
||||
|
||||
GST_DEBUG ("Updating node rect");
|
||||
|
||||
if (force_aspect_ratio_ && caps_) {
|
||||
src.w = display_width_;
|
||||
src.h = display_height_;
|
||||
|
||||
dst.x = boundingRect ().x ();
|
||||
dst.y = boundingRect ().y ();
|
||||
dst.w = boundingRect ().width ();
|
||||
dst.h = boundingRect ().height ();
|
||||
|
||||
gst_video_sink_center_rect (src, dst, &result, TRUE);
|
||||
} else {
|
||||
result.x = boundingRect ().x ();
|
||||
result.y = boundingRect ().y ();
|
||||
result.w = boundingRect ().width ();
|
||||
result.h = boundingRect ().height ();
|
||||
}
|
||||
|
||||
node_->setRect (QRectF (result.x, result.y, result.w, result.h));
|
||||
update_par_ = false;
|
||||
}
|
||||
|
||||
if (node_) {
|
||||
node_->SetCaps (caps_);
|
||||
node_->SetBuffer (buffer_);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::hoverEnterEvent (QHoverEvent * event)
|
||||
{
|
||||
mouse_hovering_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::hoverLeaveEvent (QHoverEvent * event)
|
||||
{
|
||||
mouse_hovering_ = false;
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::fitStreamToAllocatedSize (GstVideoRectangle * result)
|
||||
{
|
||||
if (force_aspect_ratio_) {
|
||||
GstVideoRectangle src, dst;
|
||||
|
||||
src.x = 0;
|
||||
src.y = 0;
|
||||
src.w = display_width_;
|
||||
src.h = display_height_;
|
||||
|
||||
dst.x = 0;
|
||||
dst.y = 0;
|
||||
dst.w = width ();
|
||||
dst.h = height ();
|
||||
|
||||
gst_video_sink_center_rect (src, dst, result, TRUE);
|
||||
} else {
|
||||
result->x = 0;
|
||||
result->y = 0;
|
||||
result->w = width ();
|
||||
result->h = height ();
|
||||
}
|
||||
}
|
||||
|
||||
QPointF
|
||||
GstQt6D3D11VideoItem::mapPointToStreamSize (QPointF pos)
|
||||
{
|
||||
gdouble stream_width, stream_height;
|
||||
GstVideoRectangle result;
|
||||
double stream_x, stream_y;
|
||||
double x, y;
|
||||
|
||||
fitStreamToAllocatedSize (&result);
|
||||
|
||||
stream_width = (gdouble) GST_VIDEO_INFO_WIDTH (&info_);
|
||||
stream_height = (gdouble) GST_VIDEO_INFO_HEIGHT (&info_);
|
||||
x = pos.x ();
|
||||
y = pos.y ();
|
||||
|
||||
/* from display coordinates to stream coordinates */
|
||||
if (result.w > 0)
|
||||
stream_x = (x - result.x) / result.w * stream_width;
|
||||
else
|
||||
stream_x = 0.;
|
||||
|
||||
/* clip to stream size */
|
||||
stream_x = CLAMP (stream_x, 0., stream_width);
|
||||
|
||||
/* same for y-axis */
|
||||
if (result.h > 0)
|
||||
stream_y = (y - result.y) / result.h * stream_height;
|
||||
else
|
||||
stream_y = 0.;
|
||||
|
||||
stream_y = CLAMP (stream_y, 0., stream_height);
|
||||
|
||||
return QPointF (stream_x, stream_y);
|
||||
}
|
||||
|
||||
static GstNavigationModifierType
|
||||
translateModifiers (Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
return (GstNavigationModifierType) (
|
||||
((modifiers & Qt::KeyboardModifier::ShiftModifier) ?
|
||||
GST_NAVIGATION_MODIFIER_SHIFT_MASK : 0) | ((modifiers &
|
||||
Qt::KeyboardModifier::ControlModifier) ?
|
||||
GST_NAVIGATION_MODIFIER_CONTROL_MASK : 0) | ((modifiers &
|
||||
Qt::
|
||||
KeyboardModifier::AltModifier) ? GST_NAVIGATION_MODIFIER_MOD1_MASK
|
||||
: 0) | ((modifiers & Qt::
|
||||
KeyboardModifier::MetaModifier) ?
|
||||
GST_NAVIGATION_MODIFIER_META_MASK : 0));
|
||||
}
|
||||
|
||||
static GstNavigationModifierType
|
||||
translateMouseButtons (Qt::MouseButtons buttons)
|
||||
{
|
||||
return (GstNavigationModifierType) (
|
||||
((buttons & Qt::LeftButton) ? GST_NAVIGATION_MODIFIER_BUTTON1_MASK : 0) |
|
||||
((buttons & Qt::RightButton) ? GST_NAVIGATION_MODIFIER_BUTTON2_MASK : 0) |
|
||||
((buttons & Qt::MiddleButton) ? GST_NAVIGATION_MODIFIER_BUTTON3_MASK : 0)
|
||||
| ((buttons & Qt::BackButton) ? GST_NAVIGATION_MODIFIER_BUTTON4_MASK : 0)
|
||||
| ((buttons & Qt::ForwardButton) ? GST_NAVIGATION_MODIFIER_BUTTON5_MASK :
|
||||
0));
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::hoverMoveEvent (QHoverEvent * event)
|
||||
{
|
||||
if (!mouse_hovering_)
|
||||
return;
|
||||
|
||||
if (event->position () == event->oldPos ())
|
||||
return;
|
||||
|
||||
std::lock_guard < std::mutex > lk (lock_);
|
||||
if (GST_VIDEO_INFO_FORMAT (&info_) == GST_VIDEO_FORMAT_UNKNOWN)
|
||||
return;
|
||||
|
||||
GstElement *sink = (GstElement *) g_weak_ref_get (&sink_);
|
||||
if (!sink)
|
||||
return;
|
||||
|
||||
auto pos = mapPointToStreamSize (event->position ());
|
||||
gst_navigation_send_event_simple (GST_NAVIGATION (sink),
|
||||
gst_navigation_event_new_mouse_move (pos.x (), pos.y (),
|
||||
translateModifiers (event->modifiers ())));
|
||||
|
||||
gst_object_unref (sink);
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::touchEvent (QTouchEvent * event)
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (lock_);
|
||||
if (GST_VIDEO_INFO_FORMAT (&info_) == GST_VIDEO_FORMAT_UNKNOWN)
|
||||
return;
|
||||
|
||||
GstElement *sink = (GstElement *) g_weak_ref_get (&sink_);
|
||||
if (!sink)
|
||||
return;
|
||||
|
||||
if (event->type () == QEvent::TouchCancel) {
|
||||
gst_navigation_send_event_simple (GST_NAVIGATION (sink),
|
||||
gst_navigation_event_new_touch_cancel (translateModifiers
|
||||
(event->modifiers ())));
|
||||
} else {
|
||||
const QList < QTouchEvent::TouchPoint > points = event->points ();
|
||||
gboolean sent_event = FALSE;
|
||||
|
||||
for (int i = 0; i < points.count (); i++) {
|
||||
GstEvent *nav_event;
|
||||
QPointF pos = mapPointToStreamSize (points[i].position ());
|
||||
|
||||
switch (points[i].state ()) {
|
||||
case QEventPoint::Pressed:
|
||||
nav_event =
|
||||
gst_navigation_event_new_touch_down ((guint) points[i].id (),
|
||||
pos.x (), pos.y (), (gdouble) points[i].pressure (),
|
||||
translateModifiers (event->modifiers ()));
|
||||
break;
|
||||
case QEventPoint::Updated:
|
||||
nav_event =
|
||||
gst_navigation_event_new_touch_motion ((guint) points[i].id (),
|
||||
pos.x (), pos.y (), (gdouble) points[i].pressure (),
|
||||
translateModifiers (event->modifiers ()));
|
||||
break;
|
||||
case QEventPoint::Released:
|
||||
nav_event =
|
||||
gst_navigation_event_new_touch_up ((guint) points[i].id (),
|
||||
pos.x (), pos.y (), translateModifiers (event->modifiers ()));
|
||||
break;
|
||||
/* Don't send an event if the point did not change */
|
||||
default:
|
||||
nav_event = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (nav_event) {
|
||||
gst_navigation_send_event_simple (GST_NAVIGATION (sink), nav_event);
|
||||
sent_event = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Group simultaneous touch events with a frame event */
|
||||
if (sent_event) {
|
||||
gst_navigation_send_event_simple (GST_NAVIGATION (sink),
|
||||
gst_navigation_event_new_touch_frame (translateModifiers
|
||||
(event->modifiers ())));
|
||||
}
|
||||
}
|
||||
|
||||
gst_object_unref (sink);
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::sendMouseEvent (QMouseEvent * event, gboolean is_press)
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (lock_);
|
||||
if (GST_VIDEO_INFO_FORMAT (&info_) == GST_VIDEO_FORMAT_UNKNOWN)
|
||||
return;
|
||||
|
||||
GstElement *sink = (GstElement *) g_weak_ref_get (&sink_);
|
||||
if (!sink)
|
||||
return;
|
||||
|
||||
gint button = 0;
|
||||
switch (event->button ()) {
|
||||
case Qt::LeftButton:
|
||||
button = 1;
|
||||
break;
|
||||
case Qt::RightButton:
|
||||
button = 2;
|
||||
break;
|
||||
case Qt::MiddleButton:
|
||||
button = 3;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
auto pos = mapPointToStreamSize (event->pos ());
|
||||
GstEvent *navi;
|
||||
GstNavigationModifierType type = (GstNavigationModifierType)
|
||||
(translateModifiers (event->modifiers ()) |
|
||||
translateMouseButtons (event->buttons ()));
|
||||
if (is_press) {
|
||||
navi = gst_navigation_event_new_mouse_button_press (button,
|
||||
pos.x (), pos.y (), type);
|
||||
} else {
|
||||
navi = gst_navigation_event_new_mouse_button_release (button,
|
||||
pos.x (), pos.y (), type);
|
||||
}
|
||||
|
||||
gst_navigation_send_event_simple (GST_NAVIGATION (sink), navi);
|
||||
gst_object_unref (sink);
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::mousePressEvent (QMouseEvent * event)
|
||||
{
|
||||
sendMouseEvent (event, TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::mouseReleaseEvent (QMouseEvent * event)
|
||||
{
|
||||
sendMouseEvent (event, FALSE);
|
||||
}
|
||||
|
||||
void
|
||||
GstQt6D3D11VideoItem::wheelEvent (QWheelEvent * event)
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (lock_);
|
||||
if (GST_VIDEO_INFO_FORMAT (&info_) == GST_VIDEO_FORMAT_UNKNOWN)
|
||||
return;
|
||||
|
||||
GstElement *sink = (GstElement *) g_weak_ref_get (&sink_);
|
||||
if (!sink)
|
||||
return;
|
||||
|
||||
auto pos = mapPointToStreamSize (event->position ());
|
||||
auto delta = event->angleDelta ();
|
||||
GstNavigationModifierType type = (GstNavigationModifierType)
|
||||
(translateModifiers (event->modifiers ()) |
|
||||
translateMouseButtons (event->buttons ()));
|
||||
|
||||
gst_navigation_send_event_simple (GST_NAVIGATION (sink),
|
||||
gst_navigation_event_new_mouse_scroll (pos.x (), pos.y (),
|
||||
delta.x (), delta.y (), type));
|
||||
gst_object_unref (sink);
|
||||
}
|
||||
|
||||
void
|
||||
gst_qt6_d3d11_video_item_init_once (void)
|
||||
{
|
||||
GST_D3D11_CALL_ONCE_BEGIN {
|
||||
qmlRegisterType < GstQt6D3D11VideoItem >
|
||||
("org.freedesktop.gstreamer.Qt6D3D11VideoItem",
|
||||
1, 0, "GstD3D11Qt6VideoItem");
|
||||
} GST_D3D11_CALL_ONCE_END;
|
||||
}
|
119
subprojects/gst-plugins-bad/ext/qt6d3d11/gstqt6d3d11videoitem.h
Normal file
119
subprojects/gst-plugins-bad/ext/qt6d3d11/gstqt6d3d11videoitem.h
Normal file
|
@ -0,0 +1,119 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
#include <gst/d3d11/gstd3d11.h>
|
||||
#include <QQuickItem>
|
||||
#include <QQuickWindow>
|
||||
#include <mutex>
|
||||
#include "gstqsg6d3d11node.h"
|
||||
|
||||
class GstQt6D3D11VideoItem;
|
||||
|
||||
class GstQt6D3D11VideoItemProxy : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
public:
|
||||
GstQt6D3D11VideoItemProxy (GstQt6D3D11VideoItem * videoItem)
|
||||
: item (videoItem)
|
||||
{
|
||||
}
|
||||
|
||||
void InvalidateRef (void);
|
||||
|
||||
void SetSink (GstElement * sink);
|
||||
void SetCaps (GstCaps * caps);
|
||||
void SetBuffer (GstBuffer * buffer);
|
||||
void SetForceAspectRatio (bool force);
|
||||
|
||||
gint64 AdapterLuid (void);
|
||||
GstQt6D3D11VideoItem * Item (void);
|
||||
|
||||
private:
|
||||
GstQt6D3D11VideoItem *item;
|
||||
std::mutex lock;
|
||||
};
|
||||
|
||||
class GstQt6D3D11VideoItem : public QQuickItem
|
||||
{
|
||||
friend class GstQt6D3D11VideoItemProxy;
|
||||
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
public:
|
||||
GstQt6D3D11VideoItem ();
|
||||
~GstQt6D3D11VideoItem ();
|
||||
|
||||
QSharedPointer<GstQt6D3D11VideoItemProxy> Proxy (void);
|
||||
|
||||
protected:
|
||||
QSGNode * updatePaintNode (QSGNode * oldNode, UpdatePaintNodeData * data);
|
||||
void hoverEnterEvent (QHoverEvent * event);
|
||||
void hoverLeaveEvent (QHoverEvent * event);
|
||||
void hoverMoveEvent (QHoverEvent * event);
|
||||
void touchEvent (QTouchEvent * event);
|
||||
void mousePressEvent (QMouseEvent * event);
|
||||
void mouseReleaseEvent (QMouseEvent * event);
|
||||
void wheelEvent (QWheelEvent * event);
|
||||
|
||||
private:
|
||||
void setSink (GstElement * sink);
|
||||
void setCaps (GstCaps * caps);
|
||||
void setBuffer (GstBuffer * buffer);
|
||||
void setForceAspectRatio (bool force);
|
||||
gint64 AdapterLuid ();
|
||||
void fitStreamToAllocatedSize (GstVideoRectangle * result);
|
||||
QPointF mapPointToStreamSize (QPointF point);
|
||||
void sendMouseEvent (QMouseEvent * event, gboolean is_press);
|
||||
void clearResource ();
|
||||
void clearFrameResource ();
|
||||
bool setupShader ();
|
||||
|
||||
private slots:
|
||||
void handleWindowChanged (QQuickWindow * window);
|
||||
void onBeforeSynchronizing (void);
|
||||
void onSceneGraphInvalidated (void);
|
||||
void onBeforeRendering (void);
|
||||
|
||||
private:
|
||||
QSharedPointer<GstQt6D3D11VideoItemProxy> proxy_;
|
||||
QQuickWindow *window_ = nullptr;
|
||||
GstCaps *caps_ = nullptr;
|
||||
GstBuffer *buffer_ = nullptr;
|
||||
bool force_aspect_ratio_ = true;
|
||||
gint64 luid_ = 0;
|
||||
GstVideoInfo info_;
|
||||
int display_width_ = 0;
|
||||
int display_height_ = 0;
|
||||
GWeakRef sink_;
|
||||
std::mutex lock_;
|
||||
bool mouse_hovering_ = false;
|
||||
bool update_par_ = false;
|
||||
GstD3D11Device *qt_device_ = nullptr;
|
||||
GstQSG6D3D11Node *node_ = nullptr;
|
||||
};
|
||||
|
||||
void gst_qt6_d3d11_video_item_init_once (void);
|
53
subprojects/gst-plugins-bad/ext/qt6d3d11/meson.build
Normal file
53
subprojects/gst-plugins-bad/ext/qt6d3d11/meson.build
Normal file
|
@ -0,0 +1,53 @@
|
|||
gstqt6d3d11_sources = [
|
||||
'gstqml6d3d11sink.cpp',
|
||||
'gstqsg6d3d11node.cpp',
|
||||
'gstqt6d3d11videoitem.cpp',
|
||||
'plugin.cpp',
|
||||
]
|
||||
|
||||
moc_headers = [
|
||||
'gstqsg6d3d11node.h',
|
||||
'gstqt6d3d11videoitem.h'
|
||||
]
|
||||
|
||||
have_qt6d3d11 = false
|
||||
|
||||
qt6_option = get_option('qt6d3d11')
|
||||
if qt6_option.disabled()
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
if not gstd3d11_dep.found()
|
||||
if qt6_option.enabled()
|
||||
error('qt6 plugin was enabled explicitly, but required d3d11 dep was not found')
|
||||
else
|
||||
subdir_done()
|
||||
endif
|
||||
endif
|
||||
|
||||
qt6_mod = import('qt6')
|
||||
if not qt6_mod.has_tools()
|
||||
if qt6_option.enabled()
|
||||
error('qt6 plugin was enabled, but qt specific tools were not found')
|
||||
endif
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
qt6qml_dep = dependency('qt6', modules : ['Core', 'Gui', 'Qml', 'Quick'],
|
||||
required: qt6_option)
|
||||
if not qt6qml_dep.found()
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
have_qt6d3d11 = true
|
||||
moc_files = qt6_mod.preprocess(moc_headers : moc_headers)
|
||||
gstqt6d3d11 = library('gstqt6d3d11', gstqt6d3d11_sources, moc_files,
|
||||
c_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'],
|
||||
cpp_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'],
|
||||
include_directories: [configinc, libsinc],
|
||||
dependencies : [gst_dep, gstvideo_dep, gstd3d11_dep, qt6qml_dep],
|
||||
override_options : ['cpp_std=c++17'],
|
||||
install: true,
|
||||
install_dir : plugins_install_dir
|
||||
)
|
||||
plugins += [gstqt6d3d11]
|
44
subprojects/gst-plugins-bad/ext/qt6d3d11/plugin.cpp
Normal file
44
subprojects/gst-plugins-bad/ext/qt6d3d11/plugin.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
/* GStreamer
|
||||
* 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 <gst/gst.h>
|
||||
#include <gstqml6d3d11sink.h>
|
||||
|
||||
GST_DEBUG_CATEGORY (gst_qt6_d3d11_debug);
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
GST_DEBUG_CATEGORY_INIT (gst_qt6_d3d11_debug, "qt6d3d11", 0, "qt6d3d11");
|
||||
|
||||
gst_element_register (plugin, "qml6d3d11sink", GST_RANK_NONE,
|
||||
GST_TYPE_QML6_D3D11_SINK);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
qt6d3d11,
|
||||
"Qt6 Direct3D11 plugin",
|
||||
plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
|
|
@ -149,6 +149,7 @@ option('opensles', type : 'feature', value : 'auto', description : 'OpenSL ES au
|
|||
option('opus', type : 'feature', value : 'auto', description : 'OPUS audio parser plugin')
|
||||
option('qroverlay', type : 'feature', value : 'auto', description : 'Element to set random data on a qroverlay')
|
||||
option('qsv', type : 'feature', value : 'auto', description : 'Intel Quick Sync Video plugin')
|
||||
option('qt6d3d11', type : 'feature', value : 'auto', description : 'Qt6 Direct3D11 plugin')
|
||||
option('resindvd', type : 'feature', value : 'auto', description : 'Resin DVD playback plugin (GPL - only built if gpl option is also enabled!)')
|
||||
option('rsvg', type : 'feature', value : 'auto', description : 'SVG overlayer and image decoder plugin')
|
||||
option('rtmp', type : 'feature', value : 'auto', description : 'RTMP video network source and sink plugin')
|
||||
|
|
|
@ -14,6 +14,7 @@ subdir('mxf')
|
|||
subdir('nvcodec')
|
||||
subdir('opencv', if_found: opencv_dep)
|
||||
subdir('qsv')
|
||||
subdir('qt6d3d11')
|
||||
subdir('uvch264')
|
||||
subdir('va')
|
||||
subdir('waylandsink')
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
if not have_qt6d3d11
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
qt6_example_deps = dependency('qt6',
|
||||
modules : ['Core', 'Gui', 'Widgets', 'Qml', 'Quick'],
|
||||
required: get_option('examples'))
|
||||
|
||||
if not qt6_example_deps.found()
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
subdir('qml6d3d11sink')
|
||||
subdir('qml6d3d11sink-dyn-add')
|
|
@ -0,0 +1,8 @@
|
|||
qt_preprocessed = qt6_mod.preprocess(qresources : 'qml6d3d11sink-dyn-add.qrc')
|
||||
executable('qml6d3d11sink-dyn-add', 'qml6d3d11sink-dyn-add.cpp', qt_preprocessed,
|
||||
dependencies : [gst_dep, qt6_example_deps],
|
||||
override_options : ['cpp_std=c++17'],
|
||||
c_args : gst_plugins_bad_args,
|
||||
cpp_args : gst_plugins_bad_args,
|
||||
include_directories : [configinc],
|
||||
install: false)
|
|
@ -0,0 +1,101 @@
|
|||
/* GStreamer
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <QApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQuickWindow>
|
||||
#include <QQuickItem>
|
||||
#include <QTimer>
|
||||
#include <gst/gst.h>
|
||||
|
||||
static int
|
||||
run_application (int argc, char ** argv, GstElement * pipeline)
|
||||
{
|
||||
QGuiApplication app(argc, argv);
|
||||
int ret;
|
||||
|
||||
/* NOTE: Default API on Windows is D3D11 already */
|
||||
QQuickWindow::setGraphicsApi(QSGRendererInterface::Direct3D11);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
engine.load (QUrl (QStringLiteral ("qrc:/qml6d3d11sink-dyn-add.qml")));
|
||||
|
||||
QQuickWindow *rootObject;
|
||||
|
||||
rootObject = static_cast<QQuickWindow *> (engine.rootObjects().first());
|
||||
|
||||
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
|
||||
GstElement *tee = gst_bin_get_by_name (GST_BIN (pipeline), "t");
|
||||
/* Add the qml6d3d11sink element on timer callback */
|
||||
QTimer::singleShot(5000, [&]()
|
||||
{
|
||||
GstElement *queue, *sink;
|
||||
QQuickItem *videoItem = rootObject->findChild<QQuickItem *> ("videoItem");
|
||||
|
||||
queue = gst_element_factory_make ("queue", nullptr);
|
||||
sink = gst_element_factory_make ("qml6d3d11sink", nullptr);
|
||||
|
||||
g_object_set (sink, "widget", videoItem, nullptr);
|
||||
|
||||
gst_println ("Adding new qml6d3d11sink to pipeline");
|
||||
|
||||
gst_bin_add_many (GST_BIN (pipeline), queue, sink, nullptr);
|
||||
|
||||
gst_element_sync_state_with_parent (sink);
|
||||
gst_element_sync_state_with_parent (queue);
|
||||
|
||||
gst_element_link_many (tee, queue, sink, nullptr);
|
||||
|
||||
gst_object_unref (tee);
|
||||
});
|
||||
|
||||
ret = app.exec();
|
||||
|
||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
GstElement *pipeline = nullptr;
|
||||
GstElement *sink = nullptr;
|
||||
int exit_code;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
pipeline = gst_parse_launch ("d3d11testsrc ! "
|
||||
"video/x-raw(memory:D3D11Memory),format=RGBA ! "
|
||||
"tee name=t allow-not-linked=true ! queue ! fakesink sync=true", nullptr);
|
||||
|
||||
/* the plugin must be loaded before loading the qml file to register the
|
||||
* GstD3D11Qt6VideoItem */
|
||||
sink = gst_element_factory_make ("qml6d3d11sink", nullptr);
|
||||
gst_object_unref (sink);
|
||||
|
||||
exit_code = run_application (argc, argv, pipeline);
|
||||
|
||||
gst_object_unref (pipeline);
|
||||
|
||||
gst_deinit ();
|
||||
|
||||
return exit_code;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
import QtQuick 6.0
|
||||
import QtQuick.Controls 6.0
|
||||
import QtQuick.Dialogs 6.0
|
||||
import QtQuick.Window 6.0
|
||||
|
||||
import org.freedesktop.gstreamer.Qt6D3D11VideoItem 1.0
|
||||
|
||||
ApplicationWindow {
|
||||
id: window
|
||||
visible: true
|
||||
width: 640
|
||||
height: 480
|
||||
x: 30
|
||||
y: 30
|
||||
color: "black"
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
GstD3D11Qt6VideoItem {
|
||||
id: video
|
||||
objectName: "videoItem"
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: Qt.rgba(1, 1, 1, 0.3)
|
||||
border.width: 1
|
||||
border.color: "white"
|
||||
anchors.bottom: video.bottom
|
||||
anchors.bottomMargin: 15
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width : parent.width - 30
|
||||
height: parent.height - 30
|
||||
radius: 8
|
||||
|
||||
MouseArea {
|
||||
id: mousearea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
parent.opacity = 1.0
|
||||
hidetimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: hidetimer
|
||||
interval: 5000
|
||||
onTriggered: {
|
||||
parent.opacity = 0.0
|
||||
stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>qml6d3d11sink-dyn-add.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -0,0 +1,8 @@
|
|||
qt_preprocessed = qt6_mod.preprocess(qresources : 'qml6d3d11sink.qrc')
|
||||
executable('qml6d3d11sink', 'qml6d3d11sink.cpp', qt_preprocessed,
|
||||
dependencies : [gst_dep, qt6_example_deps],
|
||||
override_options : ['cpp_std=c++17'],
|
||||
c_args : gst_plugins_bad_args,
|
||||
cpp_args : gst_plugins_bad_args,
|
||||
include_directories : [configinc],
|
||||
install: false)
|
|
@ -0,0 +1,215 @@
|
|||
/* GStreamer
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <QApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQuickWindow>
|
||||
#include <QQuickItem>
|
||||
#include <QRunnable>
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
class PipelineRunner : public QRunnable
|
||||
{
|
||||
public:
|
||||
PipelineRunner(GstElement * p);
|
||||
~PipelineRunner();
|
||||
|
||||
void run ();
|
||||
|
||||
private:
|
||||
GstElement *pipeline;
|
||||
};
|
||||
|
||||
PipelineRunner::PipelineRunner (GstElement * p)
|
||||
{
|
||||
pipeline = (GstElement *) gst_object_ref (p);
|
||||
}
|
||||
|
||||
PipelineRunner::~PipelineRunner ()
|
||||
{
|
||||
gst_object_unref (this->pipeline);
|
||||
}
|
||||
|
||||
void
|
||||
PipelineRunner::run ()
|
||||
{
|
||||
gst_element_set_state (this->pipeline, GST_STATE_PLAYING);
|
||||
}
|
||||
|
||||
struct AppData
|
||||
{
|
||||
QGuiApplication *app;
|
||||
GstElement *pipeline;
|
||||
GMainLoop *loop;
|
||||
GMainContext *context;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
message_cb (GstBus * bus, GstMessage * msg, AppData * data)
|
||||
{
|
||||
gboolean do_exit = FALSE;
|
||||
|
||||
switch (GST_MESSAGE_TYPE (msg)) {
|
||||
case GST_MESSAGE_ERROR:
|
||||
gst_println ("Got pipeline error");
|
||||
do_exit = TRUE;
|
||||
break;
|
||||
case GST_MESSAGE_EOS:
|
||||
gst_println ("Got pipeline EOS");
|
||||
do_exit = TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (do_exit) {
|
||||
g_main_loop_quit (data->loop);
|
||||
data->app->quit ();
|
||||
}
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
pipeline_watch_thread (AppData * data)
|
||||
{
|
||||
GstBus *bus;
|
||||
|
||||
g_main_context_push_thread_default (data->context);
|
||||
|
||||
bus = gst_pipeline_get_bus (GST_PIPELINE (data->pipeline));
|
||||
gst_bus_add_watch (bus, (GstBusFunc) message_cb, data);
|
||||
|
||||
g_main_loop_run (data->loop);
|
||||
|
||||
gst_bus_remove_watch (bus);
|
||||
gst_object_unref (bus);
|
||||
g_main_context_pop_thread_default (data->context);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int
|
||||
run_application (int argc, char ** argv, GstElement * pipeline,
|
||||
GstElement * sink)
|
||||
{
|
||||
QGuiApplication app (argc, argv);
|
||||
GThread *bus_thread;
|
||||
int ret;
|
||||
|
||||
/* NOTE: Default API on Windows is D3D11 already */
|
||||
QQuickWindow::setGraphicsApi (QSGRendererInterface::Direct3D11);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
engine.load (QUrl (QStringLiteral ("qrc:/qml6d3d11sink.qml")));
|
||||
|
||||
QQuickItem *videoItem;
|
||||
QQuickWindow *rootObject;
|
||||
AppData app_data;
|
||||
|
||||
app_data.app = &app;
|
||||
app_data.pipeline = pipeline;
|
||||
app_data.context = g_main_context_new ();
|
||||
app_data.loop = g_main_loop_new (app_data.context, FALSE);
|
||||
|
||||
/* find and set the videoItem on the sink */
|
||||
rootObject = static_cast<QQuickWindow *> (engine.rootObjects().first());
|
||||
videoItem = rootObject->findChild<QQuickItem *> ("videoItem");
|
||||
g_assert (videoItem);
|
||||
g_object_set (sink, "widget", videoItem, nullptr);
|
||||
|
||||
rootObject->scheduleRenderJob (new PipelineRunner (pipeline),
|
||||
QQuickWindow::BeforeSynchronizingStage);
|
||||
|
||||
bus_thread = g_thread_new ("pipeline-watch-thread",
|
||||
(GThreadFunc) pipeline_watch_thread, &app_data);
|
||||
|
||||
ret = app.exec();
|
||||
|
||||
g_main_loop_quit (app_data.loop);
|
||||
g_thread_join (bus_thread);
|
||||
g_main_loop_unref (app_data.loop);
|
||||
g_main_context_unref (app_data.context);
|
||||
|
||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
GOptionContext *option_ctx;
|
||||
gchar *uri = nullptr;
|
||||
GError *err = nullptr;
|
||||
gboolean ret;
|
||||
GOptionEntry options[] = {
|
||||
{"uri", 0, 0, G_OPTION_ARG_STRING, &uri, "URI to play", nullptr},
|
||||
{nullptr, }
|
||||
};
|
||||
GstElement *pipeline = nullptr;
|
||||
GstElement *sink = nullptr;
|
||||
int exit_code;
|
||||
|
||||
option_ctx = g_option_context_new ("Qt6 Direct3D11 QML render");
|
||||
g_option_context_add_main_entries (option_ctx, options, nullptr);
|
||||
g_option_context_add_group (option_ctx, gst_init_get_option_group ());
|
||||
ret = g_option_context_parse (option_ctx, &argc, &argv, &err);
|
||||
g_option_context_free (option_ctx);
|
||||
|
||||
if (!ret) {
|
||||
gst_printerrln ("option parsing failed: %s", err->message);
|
||||
g_clear_error (&err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* the plugin must be loaded before loading the qml file to register the
|
||||
* GstD3D11Qt6VideoItem */
|
||||
sink = gst_element_factory_make ("qml6d3d11sink", nullptr);
|
||||
|
||||
if (!uri) {
|
||||
pipeline = gst_pipeline_new (nullptr);
|
||||
|
||||
GstElement *src = gst_element_factory_make ("videotestsrc", nullptr);
|
||||
GstElement *upload = gst_element_factory_make ("d3d11upload", nullptr);
|
||||
GstElement *capsfilter = gst_element_factory_make ("capsfilter", nullptr);
|
||||
GstCaps *caps;
|
||||
|
||||
caps = gst_caps_from_string ("video/x-raw(memory:D3D11Memory),format=RGBA");
|
||||
g_object_set (capsfilter, "caps", caps, nullptr);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
gst_bin_add_many (GST_BIN (pipeline),
|
||||
src, upload, capsfilter, sink, nullptr);
|
||||
gst_element_link_many (src, upload, capsfilter, sink, nullptr);
|
||||
} else {
|
||||
pipeline = gst_element_factory_make ("playbin", nullptr);
|
||||
|
||||
g_object_set (pipeline, "uri", uri, "video-sink", sink, nullptr);
|
||||
}
|
||||
|
||||
exit_code = run_application (argc, argv, pipeline, sink);
|
||||
|
||||
gst_object_unref (pipeline);
|
||||
|
||||
gst_deinit ();
|
||||
|
||||
return exit_code;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
import QtQuick 6.0
|
||||
import QtQuick.Controls 6.0
|
||||
import QtQuick.Dialogs 6.0
|
||||
import QtQuick.Window 6.0
|
||||
|
||||
import org.freedesktop.gstreamer.Qt6D3D11VideoItem 1.0
|
||||
|
||||
ApplicationWindow {
|
||||
id: window
|
||||
visible: true
|
||||
width: 640
|
||||
height: 480
|
||||
x: 30
|
||||
y: 30
|
||||
color: "black"
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
GstD3D11Qt6VideoItem {
|
||||
id: video
|
||||
objectName: "videoItem"
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: Qt.rgba(1, 1, 1, 0.3)
|
||||
border.width: 1
|
||||
border.color: "white"
|
||||
anchors.bottom: video.bottom
|
||||
anchors.bottomMargin: 15
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width : parent.width - 30
|
||||
height: parent.height - 30
|
||||
radius: 8
|
||||
|
||||
MouseArea {
|
||||
id: mousearea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
parent.opacity = 1.0
|
||||
hidetimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: hidetimer
|
||||
interval: 5000
|
||||
onTriggered: {
|
||||
parent.opacity = 0.0
|
||||
stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>qml6d3d11sink.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
Loading…
Reference in a new issue