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
40#include "qgraphicsvideoitem.h"
41#include "qpaintervideosurface_p.h"
42
43#include <qmediaobject.h>
44#include <qmediaservice.h>
45#include <qvideorenderercontrol.h>
46#include <qvideosurfaceformat.h>
47
48#include <QtCore/qcoreevent.h>
49#include <QtCore/qpointer.h>
50
51#if QT_CONFIG(opengl)
52#include <QOpenGLContext>
53#endif
54
55QT_BEGIN_NAMESPACE
56
57class QGraphicsVideoItemPrivate
58{
59public:
60 QGraphicsVideoItemPrivate()
61 : q_ptr(0)
62 , surface(0)
63 , mediaObject(0)
64 , service(0)
65 , rendererControl(0)
66 , aspectRatioMode(Qt::KeepAspectRatio)
67 , updatePaintDevice(true)
68 , rect(0.0, 0.0, 320, 240)
69 {
70 }
71
72 QGraphicsVideoItem *q_ptr;
73
74 QPainterVideoSurface *surface;
75 QPointer<QMediaObject> mediaObject;
76 QMediaService *service;
77 QVideoRendererControl *rendererControl;
78 Qt::AspectRatioMode aspectRatioMode;
79 bool updatePaintDevice;
80 QRectF rect;
81 QRectF boundingRect;
82 QRectF sourceRect;
83 QSizeF nativeSize;
84
85 void clearService();
86 void updateRects();
87
88 void _q_present();
89 void _q_formatChanged(const QVideoSurfaceFormat &format);
90 void _q_updateNativeSize();
91 void _q_serviceDestroyed();
92};
93
94void QGraphicsVideoItemPrivate::clearService()
95{
96 if (rendererControl) {
97 surface->stop();
98 rendererControl->setSurface(0);
99 service->releaseControl(control: rendererControl);
100 rendererControl = 0;
101 }
102 if (service) {
103 QObject::disconnect(sender: service, SIGNAL(destroyed()), receiver: q_ptr, SLOT(_q_serviceDestroyed()));
104 service = 0;
105 }
106}
107
108void QGraphicsVideoItemPrivate::updateRects()
109{
110 q_ptr->prepareGeometryChange();
111
112 if (nativeSize.isEmpty()) {
113 //this is necessary for item to receive the
114 //first paint event and configure video surface.
115 boundingRect = rect;
116 } else if (aspectRatioMode == Qt::IgnoreAspectRatio) {
117 boundingRect = rect;
118 sourceRect = QRectF(0, 0, 1, 1);
119 } else if (aspectRatioMode == Qt::KeepAspectRatio) {
120 QSizeF size = nativeSize;
121 size.scale(s: rect.size(), mode: Qt::KeepAspectRatio);
122
123 boundingRect = QRectF(0, 0, size.width(), size.height());
124 boundingRect.moveCenter(p: rect.center());
125
126 sourceRect = QRectF(0, 0, 1, 1);
127 } else if (aspectRatioMode == Qt::KeepAspectRatioByExpanding) {
128 boundingRect = rect;
129
130 QSizeF size = rect.size();
131 size.scale(s: nativeSize, mode: Qt::KeepAspectRatio);
132
133 sourceRect = QRectF(
134 0, 0, size.width() / nativeSize.width(), size.height() / nativeSize.height());
135 sourceRect.moveCenter(p: QPointF(0.5, 0.5));
136 }
137}
138
139void QGraphicsVideoItemPrivate::_q_present()
140{
141 if (q_ptr->isObscured()) {
142 q_ptr->update(rect: boundingRect);
143 surface->setReady(true);
144 } else {
145 q_ptr->update(rect: boundingRect);
146 }
147}
148
149void QGraphicsVideoItemPrivate::_q_updateNativeSize()
150{
151 const QSize &size = surface->surfaceFormat().sizeHint();
152 if (nativeSize != size) {
153 nativeSize = size;
154
155 updateRects();
156 emit q_ptr->nativeSizeChanged(size: nativeSize);
157 }
158}
159
160void QGraphicsVideoItemPrivate::_q_serviceDestroyed()
161{
162 rendererControl = 0;
163 service = 0;
164
165 surface->stop();
166}
167
168
169/*!
170 \class QGraphicsVideoItem
171
172 \brief The QGraphicsVideoItem class provides a graphics item which display video produced by a QMediaObject.
173
174 \inmodule QtMultimediaWidgets
175 \ingroup multimedia
176
177 Attaching a QGraphicsVideoItem to a QMediaObject allows it to display
178 the video or image output of that media object. A QGraphicsVideoItem
179 is attached to a media object by passing a pointer to the QMediaObject
180 to the setMediaObject() function.
181
182 \snippet multimedia-snippets/video.cpp Video graphics item
183
184 \b {Note}: Only a single display output can be attached to a media
185 object at one time.
186
187 \sa QMediaObject, QMediaPlayer, QVideoWidget
188*/
189
190/*!
191 Constructs a graphics item that displays video.
192
193 The \a parent is passed to QGraphicsItem.
194*/
195QGraphicsVideoItem::QGraphicsVideoItem(QGraphicsItem *parent)
196 : QGraphicsObject(parent)
197 , d_ptr(new QGraphicsVideoItemPrivate)
198{
199 d_ptr->q_ptr = this;
200 d_ptr->surface = new QPainterVideoSurface;
201
202 qRegisterMetaType<QVideoSurfaceFormat>();
203
204 connect(sender: d_ptr->surface, SIGNAL(frameChanged()), receiver: this, SLOT(_q_present()));
205 connect(sender: d_ptr->surface, SIGNAL(surfaceFormatChanged(QVideoSurfaceFormat)),
206 receiver: this, SLOT(_q_updateNativeSize()), Qt::QueuedConnection);
207}
208
209/*!
210 Destroys a video graphics item.
211*/
212QGraphicsVideoItem::~QGraphicsVideoItem()
213{
214 if (d_ptr->rendererControl) {
215 d_ptr->rendererControl->setSurface(0);
216 d_ptr->service->releaseControl(control: d_ptr->rendererControl);
217 }
218
219 delete d_ptr->surface;
220 delete d_ptr;
221}
222
223/*!
224 \property QGraphicsVideoItem::mediaObject
225 \brief the media object which provides the video displayed by a graphics
226 item.
227*/
228
229QMediaObject *QGraphicsVideoItem::mediaObject() const
230{
231 return d_func()->mediaObject;
232}
233
234/*!
235 \since 5.15
236 \property QGraphicsVideoItem::videoSurface
237 \brief Returns the underlying video surface that can render video frames
238 to the current item.
239 This property is never \c nullptr.
240 Example of how to render video frames to QGraphicsVideoItem:
241 \snippet multimedia-snippets/video.cpp GraphicsVideoItem Surface
242 \sa QMediaPlayer::setVideoOutput
243*/
244
245QAbstractVideoSurface *QGraphicsVideoItem::videoSurface() const
246{
247 return d_func()->surface;
248}
249
250/*!
251 \internal
252*/
253bool QGraphicsVideoItem::setMediaObject(QMediaObject *object)
254{
255 Q_D(QGraphicsVideoItem);
256
257 if (object == d->mediaObject)
258 return true;
259
260 d->clearService();
261
262 d->mediaObject = object;
263
264 if (d->mediaObject) {
265 d->service = d->mediaObject->service();
266
267 if (d->service) {
268 QMediaControl *control = d->service->requestControl(QVideoRendererControl_iid);
269 if (control) {
270 d->rendererControl = qobject_cast<QVideoRendererControl *>(object: control);
271
272 if (d->rendererControl) {
273 //don't set the surface until the item is painted
274 //at least once and the surface is configured
275 if (!d->updatePaintDevice)
276 d->rendererControl->setSurface(d->surface);
277 else
278 update(rect: boundingRect());
279
280 connect(sender: d->service, SIGNAL(destroyed()), receiver: this, SLOT(_q_serviceDestroyed()));
281
282 return true;
283 }
284 if (control)
285 d->service->releaseControl(control);
286 }
287 }
288 }
289
290 d->mediaObject = 0;
291 return false;
292}
293
294/*!
295 \property QGraphicsVideoItem::aspectRatioMode
296 \brief how a video is scaled to fit the graphics item's size.
297*/
298
299Qt::AspectRatioMode QGraphicsVideoItem::aspectRatioMode() const
300{
301 return d_func()->aspectRatioMode;
302}
303
304void QGraphicsVideoItem::setAspectRatioMode(Qt::AspectRatioMode mode)
305{
306 Q_D(QGraphicsVideoItem);
307
308 d->aspectRatioMode = mode;
309 d->updateRects();
310}
311
312/*!
313 \property QGraphicsVideoItem::offset
314 \brief the video item's offset.
315
316 QGraphicsVideoItem will draw video using the offset for its top left
317 corner.
318*/
319
320QPointF QGraphicsVideoItem::offset() const
321{
322 return d_func()->rect.topLeft();
323}
324
325void QGraphicsVideoItem::setOffset(const QPointF &offset)
326{
327 Q_D(QGraphicsVideoItem);
328
329 d->rect.moveTo(p: offset);
330 d->updateRects();
331}
332
333/*!
334 \property QGraphicsVideoItem::size
335 \brief the video item's size.
336
337 QGraphicsVideoItem will draw video scaled to fit size according to its
338 fillMode.
339*/
340
341QSizeF QGraphicsVideoItem::size() const
342{
343 return d_func()->rect.size();
344}
345
346void QGraphicsVideoItem::setSize(const QSizeF &size)
347{
348 Q_D(QGraphicsVideoItem);
349
350 d->rect.setSize(size.isValid() ? size : QSizeF(0, 0));
351 d->updateRects();
352}
353
354/*!
355 \property QGraphicsVideoItem::nativeSize
356 \brief the native size of the video.
357*/
358
359QSizeF QGraphicsVideoItem::nativeSize() const
360{
361 return d_func()->nativeSize;
362}
363
364/*!
365 \fn QGraphicsVideoItem::nativeSizeChanged(const QSizeF &size)
366
367 Signals that the native \a size of the video has changed.
368*/
369
370/*!
371 \reimp
372*/
373QRectF QGraphicsVideoItem::boundingRect() const
374{
375 return d_func()->boundingRect;
376}
377
378/*!
379 \reimp
380*/
381void QGraphicsVideoItem::paint(
382 QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
383{
384 Q_D(QGraphicsVideoItem);
385
386 Q_UNUSED(option);
387 Q_UNUSED(widget);
388
389 if (d->surface && d->updatePaintDevice) {
390 d->updatePaintDevice = false;
391#if QT_CONFIG(opengl)
392 if (widget)
393 connect(sender: widget, SIGNAL(destroyed()), receiver: d->surface, SLOT(viewportDestroyed()));
394
395 if (painter->paintEngine()->type() == QPaintEngine::OpenGL
396 || painter->paintEngine()->type() == QPaintEngine::OpenGL2)
397 {
398 d->surface->updateGLContext();
399 if (d->surface->supportedShaderTypes() & QPainterVideoSurface::GlslShader) {
400 d->surface->setShaderType(QPainterVideoSurface::GlslShader);
401 } else {
402 d->surface->setShaderType(QPainterVideoSurface::FragmentProgramShader);
403 }
404 }
405#endif
406 if (d->rendererControl && d->rendererControl->surface() != d->surface)
407 d->rendererControl->setSurface(d->surface);
408 }
409
410 if (d->surface && d->surface->isActive()) {
411 d->surface->paint(painter, target: d->boundingRect, source: d->sourceRect);
412 d->surface->setReady(true);
413 }
414}
415
416/*!
417 \reimp
418
419 \internal
420*/
421QVariant QGraphicsVideoItem::itemChange(GraphicsItemChange change, const QVariant &value)
422{
423 return QGraphicsItem::itemChange(change, value);
424}
425
426/*!
427 \internal
428*/
429void QGraphicsVideoItem::timerEvent(QTimerEvent *event)
430{
431 QGraphicsObject::timerEvent(event);
432}
433
434QT_END_NAMESPACE
435
436#include "moc_qgraphicsvideoitem.cpp"
437

source code of qtmultimedia/src/multimediawidgets/qgraphicsvideoitem.cpp