1/****************************************************************************
2**
3** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
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** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include <QMutexLocker>
52#include <QtMultimedia/QVideoFrame>
53
54#include "videoplayer.h"
55
56TextureWidget::TextureWidget(QWidget *parent)
57 : QOpenGLWidget(parent)
58 , m_texture(QOpenGLTexture::Target2D)
59{
60}
61
62// Main thread
63void TextureWidget::initializeGL()
64{
65 initializeOpenGLFunctions();
66 glClearColor(red: 0.0f, green: 0.0f, blue: 0.0f, alpha: 1.0f);
67
68 if (!m_shader.addShaderFromSourceCode(type: QOpenGLShader::Vertex,
69 source: "#version 330\n"
70 "out vec2 coords;\n"
71 "const vec2 positions[6] = vec2[] ("
72 " vec2(-1.0, 1.0),"
73 " vec2(-1.0, -1.0),"
74 " vec2(1.0, 1.0),"
75 " vec2(1.0, 1.0),"
76 " vec2(-1.0, -1.0),"
77 " vec2(1.0, -1.0));\n"
78 "const vec2 texCoords[6] = vec2[] ("
79 " vec2(0.0, 0.0),"
80 " vec2(0.0, 1.0),"
81 " vec2(1.0, 0.0),"
82 " vec2(1.0, 0.0),"
83 " vec2(0.0, 1.0),"
84 " vec2(1.0, 1.0));\n"
85 "void main() {\n"
86 " coords = texCoords[gl_VertexID];\n"
87 " gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0);\n"
88 "}"))
89 qDebug() << "Failed to load vertex shader" << m_shader.log();
90 if (!m_shader.addShaderFromSourceCode(type: QOpenGLShader::Fragment,
91 source: "#version 330\n"
92 "in vec2 coords;\n"
93 "uniform sampler2D video_texture;\n"
94 "out vec4 fragColor;\n"
95 "void main() {\n"
96 " fragColor = texture(video_texture, coords);\n"
97 "}"))
98 qDebug() << "Failed to load fragment shader" << m_shader.log();
99 if (!m_shader.link())
100 qDebug() << "Failed to link shaders" << m_shader.log();
101
102 qDebug() << Q_FUNC_INFO << context()->shareContext();
103
104 m_vao.create();
105}
106
107// Main thread
108void TextureWidget::paintGL()
109{
110 glViewport(x: 0, y: 0, width: width(), height: height());
111
112 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
113
114 if (!m_texture.isCreated())
115 return;
116
117 m_shader.bind();
118
119 m_texture.bind(unit: 0);
120 m_shader.setUniformValue(name: "video_texture", value: 0);
121
122 m_vao.bind();
123 glDrawArrays(GL_TRIANGLES, first: 0, count: 6);
124
125 m_vao.release();
126 m_shader.release();
127}
128
129void TextureWidget::setVideoFrame(const QVideoFrame &frame)
130{
131 QVideoFrame f = frame;
132
133 // Map frame
134 if (!f.map(mode: QAbstractVideoBuffer::ReadOnly))
135 return;
136
137 makeCurrent();
138
139 // Create or recreate texture
140 if (m_texture.width() != f.width() || m_texture.height() != f.height()) {
141 if (m_texture.isCreated())
142 m_texture.destroy();
143
144 m_texture.setSize(width: f.width(), height: f.height());
145 m_texture.setFormat(QOpenGLTexture::RGBA32F);
146 m_texture.setWrapMode(QOpenGLTexture::ClampToBorder);
147 m_texture.setMinificationFilter(QOpenGLTexture::Nearest);
148 m_texture.setMagnificationFilter(QOpenGLTexture::Nearest);
149 m_texture.allocateStorage();
150
151 m_texture.create();
152 emit textureIdChanged(textureId: m_texture.textureId());
153 }
154
155 const QVideoFrame::PixelFormat pFormat = f.pixelFormat();
156 if (pFormat == QVideoFrame::Format_RGB32) {
157 m_texture.setData(sourceFormat: QOpenGLTexture::RGBA, sourceType: QOpenGLTexture::UInt8, data: f.bits());
158 }
159
160 doneCurrent();
161
162 // Request display udpate
163 QOpenGLWidget::update();
164
165 // Unmap
166 f.unmap();
167}
168
169QList<QVideoFrame::PixelFormat> GLVideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const
170{
171 if (type == QAbstractVideoBuffer::NoHandle)
172 return {
173 QVideoFrame::Format_RGB32,
174 QVideoFrame::Format_ARGB32,
175 QVideoFrame::Format_BGR32,
176 QVideoFrame::Format_BGRA32
177 };
178 return {};
179}
180
181// Video player thread
182bool GLVideoSurface::present(const QVideoFrame &frame)
183{
184 emit onNewFrame(frame);
185 return true;
186}
187
188VideoPlayer::VideoPlayer(TextureWidget *textureWidget)
189 : QObject(textureWidget)
190 , m_player(new QMediaPlayer(nullptr, QMediaPlayer::VideoSurface))
191 , m_surface(new GLVideoSurface())
192{
193 m_player->setMedia(media: QUrl("https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4"));
194
195 // Tell player to render on GLVideoSurface
196 m_player->setVideoOutput(m_surface.get());
197
198 // Display errors
199 QObject::connect(sender: m_player.get(), signal: QOverload<QMediaPlayer::Error>::of(ptr: &QMediaPlayer::error),
200 context: m_player.get(), slot: [this] (QMediaPlayer::Error e) {
201 qDebug() << Q_FUNC_INFO << e << m_player->errorString();
202 });
203
204 // Repeat video indefinitely
205 QObject::connect(sender: m_player.get(), signal: &QMediaPlayer::stateChanged, context: m_player.get(), slot: [this] (QMediaPlayer::State state) {
206 if (state == QMediaPlayer::StoppedState)
207 m_player->play();
208 });
209
210 QObject::connect(sender: m_surface.get(), signal: &GLVideoSurface::onNewFrame,
211 receiver: textureWidget, slot: &TextureWidget::setVideoFrame, type: Qt::DirectConnection);
212
213 // Start playing
214 m_player->play();
215}
216
217VideoPlayer::~VideoPlayer()
218{
219}
220

source code of qt3d/tests/manual/sharedtexture/videoplayer.cpp