QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
gstqsg6glnode.cc
Go to the documentation of this file.
1/*
2 * GStreamer
3 * Copyright (C) 2022 Matthew Waters <matthew@centricular.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#include "gstqsg6glnode.h"
22
23#include <QtQuick/QSGTextureProvider>
24#include <QtQuick/QSGSimpleTextureNode>
25#include <QtQuick/QQuickWindow>
26#include <QtQuick/QSGTexture>
27
28#define GST_CAT_DEFAULT gst_qsg_texture_debug
30
32{
33 static gsize _debug;
34
35 if (g_once_init_enter (&_debug)) {
36 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "qtqsgtexture", 0,
37 "Qt Scenegraph Texture");
38 g_once_init_leave (&_debug, 1);
39 }
40
41 gst_video_info_init (&this->v_info);
42
43 this->buffer_ = NULL;
44 this->sync_buffer_ = gst_buffer_new ();
45 this->dummy_tex_ = nullptr;
46 // TODO; handle windowChanged?
47 this->window_ = item->window();
48}
49
51{
52 gst_buffer_replace (&this->buffer_, NULL);
53 gst_buffer_replace (&this->sync_buffer_, NULL);
54 this->buffer_was_bound = FALSE;
55 delete this->dummy_tex_;
56 this->dummy_tex_ = nullptr;
57}
58
59QSGTexture *
61{
62 return QSGSimpleTextureNode::texture();
63}
64
65/* only called from the streaming thread with scene graph thread blocked */
66void
68{
69 GST_LOG ("%p setCaps %" GST_PTR_FORMAT, this, caps);
70
71 if (caps)
72 gst_video_info_from_caps (&this->v_info, caps);
73 else
74 gst_video_info_init (&this->v_info);
75}
76
77/* only called from the streaming thread with scene graph thread blocked */
78GstBuffer *
80{
81 GstBuffer *buffer = NULL;
82
83 if (this->buffer_)
84 buffer = gst_buffer_ref (this->buffer_);
85
86 return buffer;
87}
88
89/* only called from the streaming thread with scene graph thread blocked */
90void
91GstQSG6OpenGLNode::setBuffer (GstBuffer * buffer)
92{
93 GstGLContext *qt_context = NULL;
94 gboolean buffer_changed;
95
96 GST_LOG ("%p setBuffer %" GST_PTR_FORMAT, this, buffer);
97 /* FIXME: update more state here */
98 buffer_changed = gst_buffer_replace (&this->buffer_, buffer);
99
100 if (buffer_changed) {
101 GstGLContext *context;
102 GstGLSyncMeta *sync_meta;
103 GstMemory *mem;
104 guint tex_id;
105 QQuickWindow::CreateTextureOptions options = QQuickWindow::TextureHasAlphaChannel;
106 QSGTexture *texture = nullptr;
107 QSize texSize;
108
109 qt_context = gst_gl_context_get_current();
110 if (!qt_context)
111 goto use_dummy_tex;
112
113 if (!this->buffer_)
114 goto use_dummy_tex;
115 if (GST_VIDEO_INFO_FORMAT (&this->v_info) == GST_VIDEO_FORMAT_UNKNOWN)
116 goto use_dummy_tex;
117
118 this->mem_ = gst_buffer_peek_memory (this->buffer_, 0);
119 if (!this->mem_)
120 goto use_dummy_tex;
121
122 /* FIXME: should really lock the memory to prevent write access */
123 if (!gst_video_frame_map (&this->v_frame, &this->v_info, this->buffer_,
124 (GstMapFlags) (GST_MAP_READ | GST_MAP_GL))) {
125 // TODO(zdanek) - this happens when format of the video changes
126 //g_assert_not_reached ();
127 GST_ERROR ("Failed to map video frame");
128 goto use_dummy_tex;
129 }
130
131 mem = gst_buffer_peek_memory (this->buffer_, 0);
132 g_assert (gst_is_gl_memory (mem));
133
134 context = ((GstGLBaseMemory *)mem)->context;
135
136 sync_meta = gst_buffer_get_gl_sync_meta (this->sync_buffer_);
137 if (!sync_meta)
138 sync_meta = gst_buffer_add_gl_sync_meta (context, this->sync_buffer_);
139
140 gst_gl_sync_meta_set_sync_point (sync_meta, context);
141
142 gst_gl_sync_meta_wait (sync_meta, qt_context);
143
144 tex_id = *(guint *) this->v_frame.data[0];
145 GST_LOG ("%p binding Qt texture %u", this, tex_id);
146
147 texSize = QSize(GST_VIDEO_FRAME_WIDTH (&this->v_frame), GST_VIDEO_FRAME_HEIGHT (&this->v_frame));
148 // XXX: ideally, we would like to subclass the relevant texture object
149 // ourselves but this is good enough for now
150 texture = QNativeInterface::QSGOpenGLTexture::fromNative(tex_id, this->window_, texSize, options);
151
152 setTexture(texture);
153 setOwnsTexture(true);
154 markDirty(QSGNode::DirtyMaterial);
155
156 gst_video_frame_unmap (&this->v_frame);
157
158 /* Texture was successfully bound, so we do not need
159 * to use the dummy texture */
160 }
161
162 if (!texture()) {
163use_dummy_tex:
164 /* Create dummy texture if not already present. */
165 if (this->dummy_tex_ == nullptr) {
166 /* Make this a black 64x64 pixel RGBA texture.
167 * This size and format is supported pretty much everywhere, so these
168 * are a safe pick. (64 pixel sidelength must be supported according
169 * to the GLES2 spec, table 6.18.)
170 * Set min/mag filters to GL_LINEAR to make sure no mipmapping is used. */
171 const int tex_sidelength = 64;
172 QImage image(tex_sidelength, tex_sidelength, QImage::Format_ARGB32);
173 image.fill(QColor(0, 0, 0, 255));
174
175 this->dummy_tex_ = this->window_->createTextureFromImage(image);
176 }
177
178 g_assert (this->dummy_tex_ != nullptr);
179
180 if (texture() != this->dummy_tex_) {
181 setTexture(this->dummy_tex_);
182 setOwnsTexture(false);
183 markDirty(QSGNode::DirtyMaterial);
184 }
185
186 GST_LOG ("%p binding fallback dummy Qt texture %p", this, this->dummy_tex_);
187 }
188}
QSGTexture * texture() const override
void setBuffer(GstBuffer *buffer)
GstBuffer * getBuffer()
void setCaps(GstCaps *caps)
GstQSG6OpenGLNode(QQuickItem *item)
#define GST_CAT_DEFAULT
GST_DEBUG_CATEGORY_STATIC(gst_qsg_texture_debug)
#define GST_CAT_DEFAULT