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 
55 QT_BEGIN_NAMESPACE
56 
57 class QGraphicsVideoItemPrivate
58 {
59 public:
QGraphicsVideoItemPrivate()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 
clearService()94 void QGraphicsVideoItemPrivate::clearService()
95 {
96     if (rendererControl) {
97         surface->stop();
98         rendererControl->setSurface(0);
99         service->releaseControl(rendererControl);
100         rendererControl = 0;
101     }
102     if (service) {
103         QObject::disconnect(service, SIGNAL(destroyed()), q_ptr, SLOT(_q_serviceDestroyed()));
104         service = 0;
105     }
106 }
107 
updateRects()108 void 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(rect.size(), Qt::KeepAspectRatio);
122 
123         boundingRect = QRectF(0, 0, size.width(), size.height());
124         boundingRect.moveCenter(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(nativeSize, Qt::KeepAspectRatio);
132 
133         sourceRect = QRectF(
134                 0, 0, size.width() / nativeSize.width(), size.height() / nativeSize.height());
135         sourceRect.moveCenter(QPointF(0.5, 0.5));
136     }
137 }
138 
_q_present()139 void QGraphicsVideoItemPrivate::_q_present()
140 {
141     if (q_ptr->isObscured()) {
142         q_ptr->update(boundingRect);
143         surface->setReady(true);
144     } else {
145         q_ptr->update(boundingRect);
146     }
147 }
148 
_q_updateNativeSize()149 void 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(nativeSize);
157     }
158 }
159 
_q_serviceDestroyed()160 void 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 */
QGraphicsVideoItem(QGraphicsItem * parent)195 QGraphicsVideoItem::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(d_ptr->surface, SIGNAL(frameChanged()), this, SLOT(_q_present()));
205     connect(d_ptr->surface, SIGNAL(surfaceFormatChanged(QVideoSurfaceFormat)),
206             this, SLOT(_q_updateNativeSize()), Qt::QueuedConnection);
207 }
208 
209 /*!
210     Destroys a video graphics item.
211 */
~QGraphicsVideoItem()212 QGraphicsVideoItem::~QGraphicsVideoItem()
213 {
214     if (d_ptr->rendererControl) {
215         d_ptr->rendererControl->setSurface(0);
216         d_ptr->service->releaseControl(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 
mediaObject() const229 QMediaObject *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 
videoSurface() const245 QAbstractVideoSurface *QGraphicsVideoItem::videoSurface() const
246 {
247     return d_func()->surface;
248 }
249 
250 /*!
251   \internal
252 */
setMediaObject(QMediaObject * object)253 bool 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 *>(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(boundingRect());
279 
280                     connect(d->service, SIGNAL(destroyed()), 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 
aspectRatioMode() const299 Qt::AspectRatioMode QGraphicsVideoItem::aspectRatioMode() const
300 {
301     return d_func()->aspectRatioMode;
302 }
303 
setAspectRatioMode(Qt::AspectRatioMode mode)304 void 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 
offset() const320 QPointF QGraphicsVideoItem::offset() const
321 {
322     return d_func()->rect.topLeft();
323 }
324 
setOffset(const QPointF & offset)325 void QGraphicsVideoItem::setOffset(const QPointF &offset)
326 {
327     Q_D(QGraphicsVideoItem);
328 
329     d->rect.moveTo(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 
size() const341 QSizeF QGraphicsVideoItem::size() const
342 {
343     return d_func()->rect.size();
344 }
345 
setSize(const QSizeF & size)346 void 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 
nativeSize() const359 QSizeF 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 */
boundingRect() const373 QRectF QGraphicsVideoItem::boundingRect() const
374 {
375     return d_func()->boundingRect;
376 }
377 
378 /*!
379     \reimp
380 */
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)381 void 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(widget, SIGNAL(destroyed()), 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, d->boundingRect, d->sourceRect);
412         d->surface->setReady(true);
413     }
414 }
415 
416 /*!
417     \reimp
418 
419     \internal
420 */
itemChange(GraphicsItemChange change,const QVariant & value)421 QVariant QGraphicsVideoItem::itemChange(GraphicsItemChange change, const QVariant &value)
422 {
423     return QGraphicsItem::itemChange(change, value);
424 }
425 
426 /*!
427   \internal
428 */
timerEvent(QTimerEvent * event)429 void QGraphicsVideoItem::timerEvent(QTimerEvent *event)
430 {
431     QGraphicsObject::timerEvent(event);
432 }
433 
434 QT_END_NAMESPACE
435 
436 #include "moc_qgraphicsvideoitem.cpp"
437