1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | #include "qsgvideonode_rgb_p.h" |
40 | #include <QtQuick/qsgtexturematerial.h> |
41 | #include <QtQuick/qsgmaterial.h> |
42 | #include <QtCore/qmutex.h> |
43 | #include <QtGui/QOpenGLContext> |
44 | #include <QtGui/QOpenGLFunctions> |
45 | #include <QtGui/QOpenGLShaderProgram> |
46 | |
47 | QT_BEGIN_NAMESPACE |
48 | |
49 | QList<QVideoFrame::PixelFormat> QSGVideoNodeFactory_RGB::supportedPixelFormats( |
50 | QAbstractVideoBuffer::HandleType handleType) const |
51 | { |
52 | QList<QVideoFrame::PixelFormat> pixelFormats; |
53 | |
54 | if (handleType == QAbstractVideoBuffer::NoHandle) { |
55 | pixelFormats.append(t: QVideoFrame::Format_RGB32); |
56 | pixelFormats.append(t: QVideoFrame::Format_ARGB32); |
57 | pixelFormats.append(t: QVideoFrame::Format_BGR32); |
58 | pixelFormats.append(t: QVideoFrame::Format_BGRA32); |
59 | pixelFormats.append(t: QVideoFrame::Format_RGB565); |
60 | } |
61 | |
62 | return pixelFormats; |
63 | } |
64 | |
65 | QSGVideoNode *QSGVideoNodeFactory_RGB::createNode(const QVideoSurfaceFormat &format) |
66 | { |
67 | if (supportedPixelFormats(handleType: format.handleType()).contains(t: format.pixelFormat())) |
68 | return new QSGVideoNode_RGB(format); |
69 | |
70 | return 0; |
71 | } |
72 | |
73 | |
74 | class QSGVideoMaterialShader_RGB : public QSGMaterialShader |
75 | { |
76 | public: |
77 | QSGVideoMaterialShader_RGB() |
78 | : QSGMaterialShader(), |
79 | m_id_matrix(-1), |
80 | m_id_width(-1), |
81 | m_id_rgbTexture(-1), |
82 | m_id_opacity(-1) |
83 | { |
84 | setShaderSourceFile(type: QOpenGLShader::Vertex, QStringLiteral(":/qtmultimediaquicktools/shaders/rgbvideo_padded.vert" )); |
85 | setShaderSourceFile(type: QOpenGLShader::Fragment, QStringLiteral(":/qtmultimediaquicktools/shaders/rgbvideo.frag" )); |
86 | } |
87 | |
88 | void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override; |
89 | |
90 | char const *const *attributeNames() const override { |
91 | static const char *names[] = { |
92 | "qt_VertexPosition" , |
93 | "qt_VertexTexCoord" , |
94 | 0 |
95 | }; |
96 | return names; |
97 | } |
98 | |
99 | protected: |
100 | void initialize() override { |
101 | m_id_matrix = program()->uniformLocation(name: "qt_Matrix" ); |
102 | m_id_width = program()->uniformLocation(name: "width" ); |
103 | m_id_rgbTexture = program()->uniformLocation(name: "rgbTexture" ); |
104 | m_id_opacity = program()->uniformLocation(name: "opacity" ); |
105 | } |
106 | |
107 | int m_id_matrix; |
108 | int m_id_width; |
109 | int m_id_rgbTexture; |
110 | int m_id_opacity; |
111 | }; |
112 | |
113 | class QSGVideoMaterialShader_RGB_swizzle : public QSGVideoMaterialShader_RGB |
114 | { |
115 | public: |
116 | QSGVideoMaterialShader_RGB_swizzle(bool hasAlpha) |
117 | : m_hasAlpha(hasAlpha) |
118 | { |
119 | setShaderSourceFile(type: QOpenGLShader::Fragment, QStringLiteral(":/qtmultimediaquicktools/shaders/rgbvideo_swizzle.frag" )); |
120 | } |
121 | |
122 | protected: |
123 | void initialize() override { |
124 | QSGVideoMaterialShader_RGB::initialize(); |
125 | program()->setUniformValue(location: program()->uniformLocation(name: "hasAlpha" ), value: GLboolean(m_hasAlpha)); |
126 | } |
127 | |
128 | bool m_hasAlpha; |
129 | }; |
130 | |
131 | |
132 | class QSGVideoMaterial_RGB : public QSGMaterial |
133 | { |
134 | public: |
135 | QSGVideoMaterial_RGB(const QVideoSurfaceFormat &format) : |
136 | m_format(format), |
137 | m_textureId(0), |
138 | m_opacity(1.0), |
139 | m_width(1.0) |
140 | { |
141 | setFlag(flags: Blending, on: false); |
142 | } |
143 | |
144 | ~QSGVideoMaterial_RGB() |
145 | { |
146 | if (m_textureId) |
147 | QOpenGLContext::currentContext()->functions()->glDeleteTextures(n: 1, textures: &m_textureId); |
148 | } |
149 | |
150 | QSGMaterialType *type() const override { |
151 | static QSGMaterialType normalType, swizzleType; |
152 | return needsSwizzling() ? &swizzleType : &normalType; |
153 | } |
154 | |
155 | QSGMaterialShader *createShader() const override { |
156 | const bool hasAlpha = m_format.pixelFormat() == QVideoFrame::Format_ARGB32; |
157 | return needsSwizzling() ? new QSGVideoMaterialShader_RGB_swizzle(hasAlpha) |
158 | : new QSGVideoMaterialShader_RGB; |
159 | } |
160 | |
161 | int compare(const QSGMaterial *other) const override { |
162 | const QSGVideoMaterial_RGB *m = static_cast<const QSGVideoMaterial_RGB *>(other); |
163 | |
164 | if (!m_textureId) |
165 | return 1; |
166 | |
167 | return m_textureId - m->m_textureId; |
168 | } |
169 | |
170 | void updateBlending() { |
171 | setFlag(flags: Blending, on: qFuzzyCompare(p1: m_opacity, p2: qreal(1.0)) ? false : true); |
172 | } |
173 | |
174 | void setVideoFrame(const QVideoFrame &frame) { |
175 | QMutexLocker lock(&m_frameMutex); |
176 | m_frame = frame; |
177 | } |
178 | |
179 | void bind() |
180 | { |
181 | QOpenGLFunctions *functions = QOpenGLContext::currentContext()->functions(); |
182 | |
183 | QMutexLocker lock(&m_frameMutex); |
184 | if (m_frame.isValid()) { |
185 | if (m_frame.map(mode: QAbstractVideoBuffer::ReadOnly)) { |
186 | QSize textureSize = m_frame.size(); |
187 | |
188 | int stride = m_frame.bytesPerLine(); |
189 | switch (m_frame.pixelFormat()) { |
190 | case QVideoFrame::Format_RGB565: |
191 | stride /= 2; |
192 | break; |
193 | default: |
194 | stride /= 4; |
195 | } |
196 | |
197 | m_width = qreal(m_frame.width()) / stride; |
198 | textureSize.setWidth(stride); |
199 | |
200 | if (m_textureSize != textureSize) { |
201 | if (!m_textureSize.isEmpty()) |
202 | functions->glDeleteTextures(n: 1, textures: &m_textureId); |
203 | functions->glGenTextures(n: 1, textures: &m_textureId); |
204 | m_textureSize = textureSize; |
205 | } |
206 | |
207 | GLint dataType = GL_UNSIGNED_BYTE; |
208 | GLint dataFormat = GL_RGBA; |
209 | |
210 | if (m_frame.pixelFormat() == QVideoFrame::Format_RGB565) { |
211 | dataType = GL_UNSIGNED_SHORT_5_6_5; |
212 | dataFormat = GL_RGB; |
213 | } |
214 | |
215 | GLint previousAlignment; |
216 | functions->glGetIntegerv(GL_UNPACK_ALIGNMENT, params: &previousAlignment); |
217 | functions->glPixelStorei(GL_UNPACK_ALIGNMENT, param: 1); |
218 | |
219 | functions->glActiveTexture(GL_TEXTURE0); |
220 | functions->glBindTexture(GL_TEXTURE_2D, texture: m_textureId); |
221 | functions->glTexImage2D(GL_TEXTURE_2D, level: 0, internalformat: dataFormat, |
222 | width: m_textureSize.width(), height: m_textureSize.height(), |
223 | border: 0, format: dataFormat, type: dataType, pixels: m_frame.bits()); |
224 | |
225 | functions->glPixelStorei(GL_UNPACK_ALIGNMENT, param: previousAlignment); |
226 | |
227 | functions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
228 | functions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
229 | functions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
230 | functions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
231 | |
232 | m_frame.unmap(); |
233 | } |
234 | m_frame = QVideoFrame(); |
235 | } else { |
236 | functions->glActiveTexture(GL_TEXTURE0); |
237 | functions->glBindTexture(GL_TEXTURE_2D, texture: m_textureId); |
238 | } |
239 | } |
240 | |
241 | QVideoFrame m_frame; |
242 | QMutex m_frameMutex; |
243 | QSize m_textureSize; |
244 | QVideoSurfaceFormat m_format; |
245 | GLuint m_textureId; |
246 | qreal m_opacity; |
247 | GLfloat m_width; |
248 | |
249 | private: |
250 | bool needsSwizzling() const { |
251 | return m_format.pixelFormat() == QVideoFrame::Format_RGB32 |
252 | || m_format.pixelFormat() == QVideoFrame::Format_ARGB32; |
253 | } |
254 | }; |
255 | |
256 | |
257 | QSGVideoNode_RGB::QSGVideoNode_RGB(const QVideoSurfaceFormat &format) : |
258 | m_format(format) |
259 | { |
260 | setFlag(QSGNode::OwnsMaterial); |
261 | m_material = new QSGVideoMaterial_RGB(format); |
262 | setMaterial(m_material); |
263 | } |
264 | |
265 | QSGVideoNode_RGB::~QSGVideoNode_RGB() |
266 | { |
267 | } |
268 | |
269 | void QSGVideoNode_RGB::setCurrentFrame(const QVideoFrame &frame, FrameFlags) |
270 | { |
271 | m_material->setVideoFrame(frame); |
272 | markDirty(bits: DirtyMaterial); |
273 | } |
274 | |
275 | void QSGVideoMaterialShader_RGB::updateState(const RenderState &state, |
276 | QSGMaterial *newMaterial, |
277 | QSGMaterial *oldMaterial) |
278 | { |
279 | Q_UNUSED(oldMaterial); |
280 | QSGVideoMaterial_RGB *mat = static_cast<QSGVideoMaterial_RGB *>(newMaterial); |
281 | program()->setUniformValue(location: m_id_rgbTexture, value: 0); |
282 | |
283 | mat->bind(); |
284 | |
285 | program()->setUniformValue(location: m_id_width, value: mat->m_width); |
286 | if (state.isOpacityDirty()) { |
287 | mat->m_opacity = state.opacity(); |
288 | mat->updateBlending(); |
289 | program()->setUniformValue(location: m_id_opacity, value: GLfloat(mat->m_opacity)); |
290 | } |
291 | |
292 | if (state.isMatrixDirty()) |
293 | program()->setUniformValue(location: m_id_matrix, value: state.combinedMatrix()); |
294 | } |
295 | |
296 | QT_END_NAMESPACE |
297 | |